From: jerome Date: Wed, 11 Feb 2009 06:57:30 +0000 (+0000) Subject: web serviceMDL-12886 refactor servers into object + add Zend_soap and Zend_xmlrpc... X-Git-Url: http://git.mjollnir.org/gw?a=commitdiff_plain;h=06e7fadc4307c5ec76fcc98e7c3cbf714ea359e8;p=moodle.git web serviceMDL-12886 refactor servers into object + add Zend_soap and Zend_xmlrpc server --- diff --git a/lib/moodleexternal.php b/lib/moodleexternal.php index 72fa171b60..a9a6925e65 100644 --- a/lib/moodleexternal.php +++ b/lib/moodleexternal.php @@ -23,7 +23,8 @@ abstract class moodle_external { /** * - * @param $functionname + * @param string $functionname + * @return array */ public function get_function_webservice_description($functionname) { if (key_exists($functionname, $this->descriptions)) { @@ -36,7 +37,7 @@ abstract class moodle_external { /** * - * @return + * @return array */ public function get_descriptions() { return $this->descriptions; diff --git a/lib/zend/Zend/Http/Client.php b/lib/zend/Zend/Http/Client.php index d0df9429a7..621ccbe905 100644 --- a/lib/zend/Zend/Http/Client.php +++ b/lib/zend/Zend/Http/Client.php @@ -306,6 +306,11 @@ class Zend_Http_Client foreach ($config as $k => $v) $this->config[strtolower($k)] = $v; + // Pass configuration options to the adapter if it exists + if ($this->adapter instanceof Zend_Http_Client_Adapter_Interface) { + $this->adapter->setConfig($config); + } + return $this; } @@ -932,11 +937,11 @@ class Zend_Http_Client // Set the Accept-encoding header if not set - depending on whether // zlib is available or not. if (! isset($this->headers['accept-encoding'])) { - if (function_exists('gzinflate')) { - $headers[] = 'Accept-encoding: gzip, deflate'; - } else { - $headers[] = 'Accept-encoding: identity'; - } + if (function_exists('gzinflate')) { + $headers[] = 'Accept-encoding: gzip, deflate'; + } else { + $headers[] = 'Accept-encoding: identity'; + } } // Set the Content-Type header @@ -967,7 +972,7 @@ class Zend_Http_Client // Add all other user defined headers foreach ($this->headers as $header) { - list($name, $value) = $header; + list($name, $value) = $header; if (is_array($value)) $value = implode(', ', $value); diff --git a/lib/zend/Zend/Http/Client/Adapter/Exception.php b/lib/zend/Zend/Http/Client/Adapter/Exception.php new file mode 100644 index 0000000000..89eb95b0a9 --- /dev/null +++ b/lib/zend/Zend/Http/Client/Adapter/Exception.php @@ -0,0 +1,33 @@ + 'ssl', + 'proxy_host' => '', + 'proxy_port' => 8080, + 'proxy_user' => '', + 'proxy_pass' => '', + 'proxy_auth' => Zend_Http_Client::AUTH_BASIC, + 'persistent' => false + ); + + /** + * Whether HTTPS CONNECT was already negotiated with the proxy or not + * + * @var boolean + */ + protected $negotiated = false; + + /** + * Connect to the remote server + * + * Will try to connect to the proxy server. If no proxy was set, will + * fall back to the target server (behave like regular Socket adapter) + * + * @param string $host + * @param int $port + * @param boolean $secure + * @param int $timeout + */ + public function connect($host, $port = 80, $secure = false) + { + // If no proxy is set, fall back to Socket adapter + if (! $this->config['proxy_host']) return parent::connect($host, $port, $secure); + + // Go through a proxy - the connection is actually to the proxy server + $host = $this->config['proxy_host']; + $port = $this->config['proxy_port']; + + // If we are connected to the wrong proxy, disconnect first + if (($this->connected_to[0] != $host || $this->connected_to[1] != $port)) { + if (is_resource($this->socket)) $this->close(); + } + + // Now, if we are not connected, connect + if (! is_resource($this->socket) || ! $this->config['keepalive']) { + $this->socket = @fsockopen($host, $port, $errno, $errstr, (int) $this->config['timeout']); + if (! $this->socket) { + $this->close(); + require_once 'Zend/Http/Client/Adapter/Exception.php'; + throw new Zend_Http_Client_Adapter_Exception( + 'Unable to Connect to proxy server ' . $host . ':' . $port . '. Error #' . $errno . ': ' . $errstr); + } + + // Set the stream timeout + if (!stream_set_timeout($this->socket, (int) $this->config['timeout'])) { + require_once 'Zend/Http/Client/Adapter/Exception.php'; + throw new Zend_Http_Client_Adapter_Exception('Unable to set the connection timeout'); + } + + // Update connected_to + $this->connected_to = array($host, $port); + } + } + + /** + * Send request to the proxy server + * + * @param string $method + * @param Zend_Uri_Http $uri + * @param string $http_ver + * @param array $headers + * @param string $body + * @return string Request as string + */ + public function write($method, $uri, $http_ver = '1.1', $headers = array(), $body = '') + { + // If no proxy is set, fall back to default Socket adapter + if (! $this->config['proxy_host']) return parent::write($method, $uri, $http_ver, $headers, $body); + + // Make sure we're properly connected + if (! $this->socket) { + require_once 'Zend/Http/Client/Adapter/Exception.php'; + throw new Zend_Http_Client_Adapter_Exception("Trying to write but we are not connected"); + } + + $host = $this->config['proxy_host']; + $port = $this->config['proxy_port']; + + if ($this->connected_to[0] != $host || $this->connected_to[1] != $port) { + require_once 'Zend/Http/Client/Adapter/Exception.php'; + throw new Zend_Http_Client_Adapter_Exception("Trying to write but we are connected to the wrong proxy server"); + } + + // Add Proxy-Authorization header + if ($this->config['proxy_user'] && ! isset($headers['proxy-authorization'])) { + $headers['proxy-authorization'] = Zend_Http_Client::encodeAuthHeader( + $this->config['proxy_user'], $this->config['proxy_pass'], $this->config['proxy_auth'] + ); + } + + // if we are proxying HTTPS, preform CONNECT handshake with the proxy + if ($uri->getScheme() == 'https' && (! $this->negotiated)) { + $this->connectHandshake($uri->getHost(), $uri->getPort(), $http_ver, $headers); + $this->negotiated = true; + } + + // Save request method for later + $this->method = $method; + + // Build request headers + $request = "{$method} {$uri->__toString()} HTTP/{$http_ver}\r\n"; + + // Add all headers to the request string + foreach ($headers as $k => $v) { + if (is_string($k)) $v = "$k: $v"; + $request .= "$v\r\n"; + } + + // Add the request body + $request .= "\r\n" . $body; + + // Send the request + if (! @fwrite($this->socket, $request)) { + require_once 'Zend/Http/Client/Adapter/Exception.php'; + throw new Zend_Http_Client_Adapter_Exception("Error writing request to proxy server"); + } + + return $request; + } + + /** + * Preform handshaking with HTTPS proxy using CONNECT method + * + * @param string $host + * @param integer $port + * @param string $http_ver + * @param array $headers + */ + protected function connectHandshake($host, $port = 443, $http_ver = '1.1', array &$headers = array()) + { + $request = "CONNECT $host:$port HTTP/$http_ver\r\n" . + "Host: " . $this->config['proxy_host'] . "\r\n"; + + // Add the user-agent header + if (isset($this->config['useragent'])) { + $request .= "User-agent: " . $this->config['useragent'] . "\r\n"; + } + + // If the proxy-authorization header is set, send it to proxy but remove + // it from headers sent to target host + if (isset($headers['proxy-authorization'])) { + $request .= "Proxy-authorization: " . $headers['proxy-authorization'] . "\r\n"; + unset($headers['proxy-authorization']); + } + + $request .= "\r\n"; + + // Send the request + if (! @fwrite($this->socket, $request)) { + require_once 'Zend/Http/Client/Adapter/Exception.php'; + throw new Zend_Http_Client_Adapter_Exception("Error writing request to proxy server"); + } + + // Read response headers only + $response = ''; + $gotStatus = false; + while ($line = @fgets($this->socket)) { + $gotStatus = $gotStatus || (strpos($line, 'HTTP') !== false); + if ($gotStatus) { + $response .= $line; + if (!chop($line)) break; + } + } + + // Check that the response from the proxy is 200 + if (Zend_Http_Response::extractCode($response) != 200) { + require_once 'Zend/Http/Client/Adapter/Exception.php'; + throw new Zend_Http_Client_Adapter_Exception("Unable to connect to HTTPS proxy. Server response: " . $response); + } + + // If all is good, switch socket to secure mode. We have to fall back + // through the different modes + $modes = array( + STREAM_CRYPTO_METHOD_TLS_CLIENT, + STREAM_CRYPTO_METHOD_SSLv3_CLIENT, + STREAM_CRYPTO_METHOD_SSLv23_CLIENT, + STREAM_CRYPTO_METHOD_SSLv2_CLIENT + ); + + $success = false; + foreach($modes as $mode) { + $success = stream_socket_enable_crypto($this->socket, true, $mode); + if ($success) break; + } + + if (! $success) { + require_once 'Zend/Http/Client/Adapter/Exception.php'; + throw new Zend_Http_Client_Adapter_Exception("Unable to connect to" . + " HTTPS server through proxy: could not negotiate secure connection."); + } + } + + /** + * Close the connection to the server + * + */ + public function close() + { + parent::close(); + $this->negotiated = false; + } + + /** + * Destructor: make sure the socket is disconnected + * + */ + public function __destruct() + { + if ($this->socket) $this->close(); + } +} diff --git a/lib/zend/Zend/Http/Client/Adapter/Test.php b/lib/zend/Zend/Http/Client/Adapter/Test.php new file mode 100644 index 0000000000..aad25f203f --- /dev/null +++ b/lib/zend/Zend/Http/Client/Adapter/Test.php @@ -0,0 +1,193 @@ + $v) { + $this->config[strtolower($k)] = $v; + } + } + + /** + * Connect to the remote server + * + * @param string $host + * @param int $port + * @param boolean $secure + * @param int $timeout + */ + public function connect($host, $port = 80, $secure = false) + { } + + /** + * Send request to the remote server + * + * @param string $method + * @param Zend_Uri_Http $uri + * @param string $http_ver + * @param array $headers + * @param string $body + * @return string Request as string + */ + public function write($method, $uri, $http_ver = '1.1', $headers = array(), $body = '') + { + $host = $uri->getHost(); + $host = (strtolower($uri->getScheme()) == 'https' ? 'sslv2://' . $host : $host); + + // Build request headers + $path = $uri->getPath(); + if ($uri->getQuery()) $path .= '?' . $uri->getQuery(); + $request = "{$method} {$path} HTTP/{$http_ver}\r\n"; + foreach ($headers as $k => $v) { + if (is_string($k)) $v = ucfirst($k) . ": $v"; + $request .= "$v\r\n"; + } + + // Add the request body + $request .= "\r\n" . $body; + + // Do nothing - just return the request as string + + return $request; + } + + /** + * Return the response set in $this->setResponse() + * + * @return string + */ + public function read() + { + if ($this->responseIndex >= count($this->responses)) { + $this->responseIndex = 0; + } + return $this->responses[$this->responseIndex++]; + } + + /** + * Close the connection (dummy) + * + */ + public function close() + { } + + /** + * Set the HTTP response(s) to be returned by this adapter + * + * @param Zend_Http_Response|array|string $response + */ + public function setResponse($response) + { + if ($response instanceof Zend_Http_Response) { + $response = $response->asString(); + } + + $this->responses = (array)$response; + $this->responseIndex = 0; + } + + /** + * Add another response to the response buffer. + * + * @param string $response + */ + public function addResponse($response) + { + $this->responses[] = $response; + } + + /** + * Sets the position of the response buffer. Selects which + * response will be returned on the next call to read(). + * + * @param integer $index + */ + public function setResponseIndex($index) + { + if ($index < 0 || $index >= count($this->responses)) { + require_once 'Zend/Http/Client/Adapter/Exception.php'; + throw new Zend_Http_Client_Adapter_Exception( + 'Index out of range of response buffer size'); + } + $this->responseIndex = $index; + } +} diff --git a/lib/zend/Zend/Http/Cookie.php b/lib/zend/Zend/Http/Cookie.php new file mode 100644 index 0000000000..cb91ddea5e --- /dev/null +++ b/lib/zend/Zend/Http/Cookie.php @@ -0,0 +1,327 @@ +name = (string) $name) { + require_once 'Zend/Http/Exception.php'; + throw new Zend_Http_Exception('Cookies must have a name'); + } + + if (! $this->domain = (string) $domain) { + require_once 'Zend/Http/Exception.php'; + throw new Zend_Http_Exception('Cookies must have a domain'); + } + + $this->value = (string) $value; + $this->expires = ($expires === null ? null : (int) $expires); + $this->path = ($path ? $path : '/'); + $this->secure = $secure; + } + + /** + * Get Cookie name + * + * @return string + */ + public function getName() + { + return $this->name; + } + + /** + * Get cookie value + * + * @return string + */ + public function getValue() + { + return $this->value; + } + + /** + * Get cookie domain + * + * @return string + */ + public function getDomain() + { + return $this->domain; + } + + /** + * Get the cookie path + * + * @return string + */ + public function getPath() + { + return $this->path; + } + + /** + * Get the expiry time of the cookie, or null if no expiry time is set + * + * @return int|null + */ + public function getExpiryTime() + { + return $this->expires; + } + + /** + * Check whether the cookie should only be sent over secure connections + * + * @return boolean + */ + public function isSecure() + { + return $this->secure; + } + + /** + * Check whether the cookie has expired + * + * Always returns false if the cookie is a session cookie (has no expiry time) + * + * @param int $now Timestamp to consider as "now" + * @return boolean + */ + public function isExpired($now = null) + { + if ($now === null) $now = time(); + if (is_int($this->expires) && $this->expires < $now) { + return true; + } else { + return false; + } + } + + /** + * Check whether the cookie is a session cookie (has no expiry time set) + * + * @return boolean + */ + public function isSessionCookie() + { + return ($this->expires === null); + } + + /** + * Checks whether the cookie should be sent or not in a specific scenario + * + * @param string|Zend_Uri_Http $uri URI to check against (secure, domain, path) + * @param boolean $matchSessionCookies Whether to send session cookies + * @param int $now Override the current time when checking for expiry time + * @return boolean + */ + public function match($uri, $matchSessionCookies = true, $now = null) + { + if (is_string ($uri)) { + $uri = Zend_Uri_Http::factory($uri); + } + + // Make sure we have a valid Zend_Uri_Http object + if (! ($uri->valid() && ($uri->getScheme() == 'http' || $uri->getScheme() =='https'))) { + require_once 'Zend/Http/Exception.php'; + throw new Zend_Http_Exception('Passed URI is not a valid HTTP or HTTPS URI'); + } + + // Check that the cookie is secure (if required) and not expired + if ($this->secure && $uri->getScheme() != 'https') return false; + if ($this->isExpired($now)) return false; + if ($this->isSessionCookie() && ! $matchSessionCookies) return false; + + // Validate domain and path + // Domain is validated using tail match, while path is validated using head match + $domain_preg = preg_quote($this->getDomain(), "/"); + if (! preg_match("/{$domain_preg}$/", $uri->getHost())) return false; + $path_preg = preg_quote($this->getPath(), "/"); + if (! preg_match("/^{$path_preg}/", $uri->getPath())) return false; + + // If we didn't die until now, return true. + return true; + } + + /** + * Get the cookie as a string, suitable for sending as a "Cookie" header in an + * HTTP request + * + * @return string + */ + public function __toString() + { + return $this->name . '=' . urlencode($this->value) . ';'; + } + + /** + * Generate a new Cookie object from a cookie string + * (for example the value of the Set-Cookie HTTP header) + * + * @param string $cookieStr + * @param Zend_Uri_Http|string $ref_uri Reference URI for default values (domain, path) + * @return Zend_Http_Cookie A new Zend_Http_Cookie object or false on failure. + */ + public static function fromString($cookieStr, $ref_uri = null) + { + // Set default values + if (is_string($ref_uri)) { + $ref_uri = Zend_Uri_Http::factory($ref_uri); + } + + $name = ''; + $value = ''; + $domain = ''; + $path = ''; + $expires = null; + $secure = false; + $parts = explode(';', $cookieStr); + + // If first part does not include '=', fail + if (strpos($parts[0], '=') === false) return false; + + // Get the name and value of the cookie + list($name, $value) = explode('=', trim(array_shift($parts)), 2); + $name = trim($name); + $value = urldecode(trim($value)); + + // Set default domain and path + if ($ref_uri instanceof Zend_Uri_Http) { + $domain = $ref_uri->getHost(); + $path = $ref_uri->getPath(); + $path = substr($path, 0, strrpos($path, '/')); + } + + // Set other cookie parameters + foreach ($parts as $part) { + $part = trim($part); + if (strtolower($part) == 'secure') { + $secure = true; + continue; + } + + $keyValue = explode('=', $part, 2); + if (count($keyValue) == 2) { + list($k, $v) = $keyValue; + switch (strtolower($k)) { + case 'expires': + $expires = strtotime($v); + break; + case 'path': + $path = $v; + break; + case 'domain': + $domain = $v; + break; + default: + break; + } + } + } + + if ($name !== '') { + return new Zend_Http_Cookie($name, $value, $domain, $expires, $path, $secure); + } else { + return false; + } + } +} diff --git a/lib/zend/Zend/Http/CookieJar.php b/lib/zend/Zend/Http/CookieJar.php new file mode 100644 index 0000000000..0a135ca375 --- /dev/null +++ b/lib/zend/Zend/Http/CookieJar.php @@ -0,0 +1,350 @@ +getDomain(); + $path = $cookie->getPath(); + if (! isset($this->cookies[$domain])) $this->cookies[$domain] = array(); + if (! isset($this->cookies[$domain][$path])) $this->cookies[$domain][$path] = array(); + $this->cookies[$domain][$path][$cookie->getName()] = $cookie; + } else { + require_once 'Zend/Http/Exception.php'; + throw new Zend_Http_Exception('Supplient argument is not a valid cookie string or object'); + } + } + + /** + * Parse an HTTP response, adding all the cookies set in that response + * to the cookie jar. + * + * @param Zend_Http_Response $response + * @param Zend_Uri_Http|string $ref_uri Requested URI + */ + public function addCookiesFromResponse($response, $ref_uri) + { + if (! $response instanceof Zend_Http_Response) { + require_once 'Zend/Http/Exception.php'; + throw new Zend_Http_Exception('$response is expected to be a Response object, ' . + gettype($response) . ' was passed'); + } + + $cookie_hdrs = $response->getHeader('Set-Cookie'); + + if (is_array($cookie_hdrs)) { + foreach ($cookie_hdrs as $cookie) { + $this->addCookie($cookie, $ref_uri); + } + } elseif (is_string($cookie_hdrs)) { + $this->addCookie($cookie_hdrs, $ref_uri); + } + } + + /** + * Get all cookies in the cookie jar as an array + * + * @param int $ret_as Whether to return cookies as objects of Zend_Http_Cookie or as strings + * @return array|string + */ + public function getAllCookies($ret_as = self::COOKIE_OBJECT) + { + $cookies = $this->_flattenCookiesArray($this->cookies, $ret_as); + return $cookies; + } + + /** + * Return an array of all cookies matching a specific request according to the request URI, + * whether session cookies should be sent or not, and the time to consider as "now" when + * checking cookie expiry time. + * + * @param string|Zend_Uri_Http $uri URI to check against (secure, domain, path) + * @param boolean $matchSessionCookies Whether to send session cookies + * @param int $ret_as Whether to return cookies as objects of Zend_Http_Cookie or as strings + * @param int $now Override the current time when checking for expiry time + * @return array|string + */ + public function getMatchingCookies($uri, $matchSessionCookies = true, + $ret_as = self::COOKIE_OBJECT, $now = null) + { + if (is_string($uri)) $uri = Zend_Uri::factory($uri); + if (! $uri instanceof Zend_Uri_Http) { + require_once 'Zend/Http/Exception.php'; + throw new Zend_Http_Exception("Invalid URI string or object passed"); + } + + // Set path + $path = $uri->getPath(); + $path = substr($path, 0, strrpos($path, '/')); + if (! $path) $path = '/'; + + // First, reduce the array of cookies to only those matching domain and path + $cookies = $this->_matchDomain($uri->getHost()); + $cookies = $this->_matchPath($cookies, $path); + $cookies = $this->_flattenCookiesArray($cookies, self::COOKIE_OBJECT); + + // Next, run Cookie->match on all cookies to check secure, time and session mathcing + $ret = array(); + foreach ($cookies as $cookie) + if ($cookie->match($uri, $matchSessionCookies, $now)) + $ret[] = $cookie; + + // Now, use self::_flattenCookiesArray again - only to convert to the return format ;) + $ret = $this->_flattenCookiesArray($ret, $ret_as); + + return $ret; + } + + /** + * Get a specific cookie according to a URI and name + * + * @param Zend_Uri_Http|string $uri The uri (domain and path) to match + * @param string $cookie_name The cookie's name + * @param int $ret_as Whether to return cookies as objects of Zend_Http_Cookie or as strings + * @return Zend_Http_Cookie|string + */ + public function getCookie($uri, $cookie_name, $ret_as = self::COOKIE_OBJECT) + { + if (is_string($uri)) { + $uri = Zend_Uri::factory($uri); + } + + if (! $uri instanceof Zend_Uri_Http) { + require_once 'Zend/Http/Exception.php'; + throw new Zend_Http_Exception('Invalid URI specified'); + } + + // Get correct cookie path + $path = $uri->getPath(); + $path = substr($path, 0, strrpos($path, '/')); + if (! $path) $path = '/'; + + if (isset($this->cookies[$uri->getHost()][$path][$cookie_name])) { + $cookie = $this->cookies[$uri->getHost()][$path][$cookie_name]; + + switch ($ret_as) { + case self::COOKIE_OBJECT: + return $cookie; + break; + + case self::COOKIE_STRING_ARRAY: + case self::COOKIE_STRING_CONCAT: + return $cookie->__toString(); + break; + + default: + require_once 'Zend/Http/Exception.php'; + throw new Zend_Http_Exception("Invalid value passed for \$ret_as: {$ret_as}"); + break; + } + } else { + return false; + } + } + + /** + * Helper function to recursivly flatten an array. Shoud be used when exporting the + * cookies array (or parts of it) + * + * @param Zend_Http_Cookie|array $ptr + * @param int $ret_as What value to return + * @return array|string + */ + protected function _flattenCookiesArray($ptr, $ret_as = self::COOKIE_OBJECT) { + if (is_array($ptr)) { + $ret = ($ret_as == self::COOKIE_STRING_CONCAT ? '' : array()); + foreach ($ptr as $item) { + if ($ret_as == self::COOKIE_STRING_CONCAT) { + $ret .= $this->_flattenCookiesArray($item, $ret_as); + } else { + $ret = array_merge($ret, $this->_flattenCookiesArray($item, $ret_as)); + } + } + return $ret; + } elseif ($ptr instanceof Zend_Http_Cookie) { + switch ($ret_as) { + case self::COOKIE_STRING_ARRAY: + return array($ptr->__toString()); + break; + + case self::COOKIE_STRING_CONCAT: + return $ptr->__toString(); + break; + + case self::COOKIE_OBJECT: + default: + return array($ptr); + break; + } + } + + return null; + } + + /** + * Return a subset of the cookies array matching a specific domain + * + * Returned array is actually an array of pointers to items in the $this->cookies array. + * + * @param string $domain + * @return array + */ + protected function _matchDomain($domain) { + $ret = array(); + + foreach (array_keys($this->cookies) as $cdom) { + $regex = "/" . preg_quote($cdom, "/") . "$/i"; + if (preg_match($regex, $domain)) $ret[$cdom] = &$this->cookies[$cdom]; + } + + return $ret; + } + + /** + * Return a subset of a domain-matching cookies that also match a specified path + * + * Returned array is actually an array of pointers to items in the $passed array. + * + * @param array $dom_array + * @param string $path + * @return array + */ + protected function _matchPath($domains, $path) { + $ret = array(); + if (substr($path, -1) != '/') $path .= '/'; + + foreach ($domains as $dom => $paths_array) { + foreach (array_keys($paths_array) as $cpath) { + $regex = "|^" . preg_quote($cpath, "|") . "|i"; + if (preg_match($regex, $path)) { + if (! isset($ret[$dom])) $ret[$dom] = array(); + $ret[$dom][$cpath] = &$paths_array[$cpath]; + } + } + } + + return $ret; + } + + /** + * Create a new CookieJar object and automatically load into it all the + * cookies set in an Http_Response object. If $uri is set, it will be + * considered as the requested URI for setting default domain and path + * of the cookie. + * + * @param Zend_Http_Response $response HTTP Response object + * @param Zend_Uri_Http|string $uri The requested URI + * @return Zend_Http_CookieJar + * @todo Add the $uri functionality. + */ + public static function fromResponse(Zend_Http_Response $response, $ref_uri) + { + $jar = new self(); + $jar->addCookiesFromResponse($response, $ref_uri); + return $jar; + } +} diff --git a/lib/zend/Zend/Http/Response.php b/lib/zend/Zend/Http/Response.php index 17c0261d15..776f907421 100644 --- a/lib/zend/Zend/Http/Response.php +++ b/lib/zend/Zend/Http/Response.php @@ -162,7 +162,7 @@ class Zend_Http_Response if (! is_array($headers)) { require_once 'Zend/Http/Exception.php'; throw new Zend_Http_Exception('No valid headers were passed'); - } + } foreach ($headers as $name => $value) { if (is_int($name)) @@ -536,10 +536,9 @@ class Zend_Http_Response { $parts = preg_split('|(?:\r?\n){2}|m', $response_str, 2); if (isset($parts[1])) { - return $parts[1]; - } else { - return ''; + return $parts[1]; } + return ''; } /** @@ -603,7 +602,7 @@ class Zend_Http_Response 'body: perhaps the zlib extension is not loaded?'); } - return gzuncompress($body); + return gzuncompress($body); } /** diff --git a/lib/zend/Zend/Soap/AutoDiscover.php b/lib/zend/Zend/Soap/AutoDiscover.php new file mode 100644 index 0000000000..7ecfb2298f --- /dev/null +++ b/lib/zend/Zend/Soap/AutoDiscover.php @@ -0,0 +1,397 @@ +_reflection = new Zend_Server_Reflection(); + $this->setComplexTypeStrategy($strategy); + + if($uri !== null) { + $this->setUri($uri); + } + } + + /** + * Set the location at which the WSDL file will be availabe. + * + * @see Zend_Soap_Exception + * @throws Zend_Soap_AutoDiscover_Exception + * @param Zend_Uri|string $uri + * @return Zend_Soap_AutoDiscover + */ + public function setUri($uri) + { + if(is_string($uri)) { + $uri = Zend_Uri::factory($uri); + } else if(!($uri instanceof Zend_Uri)) { + require_once "Zend/Soap/AutoDiscover/Exception.php"; + throw new Zend_Soap_AutoDiscover_Exception("No uri given to Zend_Soap_AutoDiscover::setUri as string or Zend_Uri instance."); + } + $this->_uri = $uri; + + // change uri in WSDL file also if existant + if($this->_wsdl instanceof Zend_Soap_Wsdl) { + $this->_wsdl->setUri($uri); + } + + return $this; + } + + /** + * Return the current Uri that the SOAP WSDL Service will be located at. + * + * @return Zend_Uri + */ + public function getUri() + { + if($this->_uri instanceof Zend_Uri) { + $uri = $this->_uri; + } else { + $schema = $this->getSchema(); + $host = $this->getHostName(); + $scriptName = $this->getRequestUriWithoutParameters(); + $uri = Zend_Uri::factory($schema . '://' . $host . $scriptName); + $this->setUri($uri); + } + return $uri; + } + + /** + * Detect and returns the current HTTP/HTTPS Schema + * + * @return string + */ + protected function getSchema() + { + $schema = "http"; + if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') { + $schema = 'https'; + } + return $schema; + } + + /** + * Detect and return the current hostname + * + * @return string + */ + protected function getHostName() + { + if(isset($_SERVER['HTTP_HOST'])) { + $host = $_SERVER['HTTP_HOST']; + } else { + $host = $_SERVER['SERVER_NAME']; + } + return $host; + } + + /** + * Detect and return the current script name without parameters + * + * @return string + */ + protected function getRequestUriWithoutParameters() + { + if (isset($_SERVER['HTTP_X_REWRITE_URL'])) { // check this first so IIS will catch + $requestUri = $_SERVER['HTTP_X_REWRITE_URL']; + } elseif (isset($_SERVER['REQUEST_URI'])) { + $requestUri = $_SERVER['REQUEST_URI']; + } elseif (isset($_SERVER['ORIG_PATH_INFO'])) { // IIS 5.0, PHP as CGI + $requestUri = $_SERVER['ORIG_PATH_INFO']; + } else { + $requestUri = $_SERVER['SCRIPT_NAME']; + } + if( ($pos = strpos($requestUri, "?")) !== false) { + $requestUri = substr($requestUri, 0, $pos); + } + + return $requestUri; + } + + /** + * Set the strategy that handles functions and classes that are added AFTER this call. + * + * @param boolean|string|Zend_Soap_Wsdl_Strategy_Interface $strategy + * @return Zend_Soap_AutoDiscover + */ + public function setComplexTypeStrategy($strategy) + { + $this->_strategy = $strategy; + if($this->_wsdl instanceof Zend_Soap_Wsdl) { + $this->_wsdl->setComplexTypeStrategy($strategy); + } + + return $this; + } + + /** + * Set the Class the SOAP server will use + * + * @param string $class Class Name + * @param string $namespace Class Namspace - Not Used + * @param array $argv Arguments to instantiate the class - Not Used + */ + public function setClass($class, $namespace = '', $argv = null) + { + $uri = $this->getUri(); + $wsdl = new Zend_Soap_Wsdl($class, $uri, $this->_strategy); + $port = $wsdl->addPortType($class . 'Port'); + $binding = $wsdl->addBinding($class . 'Binding', 'tns:' .$class. 'Port'); + + $wsdl->addSoapBinding($binding, 'rpc'); + $wsdl->addService($class . 'Service', $class . 'Port', 'tns:' . $class . 'Binding', $uri); + + foreach ($this->_reflection->reflectClass($class)->getMethods() as $method) { + /* 's */ + $portOperation = $wsdl->addPortOperation($port, $method->getName(), 'tns:' .$method->getName(). 'Request', 'tns:' .$method->getName(). 'Response'); + $desc = $method->getDescription(); + if (strlen($desc) > 0) { + /** @todo check, what should be done for portoperation documentation */ + //$wsdl->addDocumentation($portOperation, $desc); + } + /* 's */ + + $this->_functions[] = $method->getName(); + + $selectedPrototype = null; + $maxNumArgumentsOfPrototype = -1; + foreach ($method->getPrototypes() as $prototype) { + $numParams = count($prototype->getParameters()); + if($numParams > $maxNumArgumentsOfPrototype) { + $maxNumArgumentsOfPrototype = $numParams; + $selectedPrototype = $prototype; + } + } + + if($selectedPrototype != null) { + $prototype = $selectedPrototype; + $args = array(); + foreach($prototype->getParameters() as $param) { + $args[$param->getName()] = $wsdl->getType($param->getType()); + } + $message = $wsdl->addMessage($method->getName() . 'Request', $args); + if (strlen($desc) > 0) { + //$wsdl->addDocumentation($message, $desc); + } + if ($prototype->getReturnType() != "void") { + $message = $wsdl->addMessage($method->getName() . 'Response', array($method->getName() . 'Return' => $wsdl->getType($prototype->getReturnType()))); + } + + /* 's */ + $operation = $wsdl->addBindingOperation($binding, $method->getName(), array('use' => 'encoded', 'encodingStyle' => "http://schemas.xmlsoap.org/soap/encoding/"), array('use' => 'encoded', 'encodingStyle' => "http://schemas.xmlsoap.org/soap/encoding/")); + $wsdl->addSoapOperation($operation, $uri->getUri() . '#' .$method->getName()); + /* 's */ + } + } + $this->_wsdl = $wsdl; + } + + /** + * Add a Single or Multiple Functions to the WSDL + * + * @param string $function Function Name + * @param string $namespace Function namespace - Not Used + */ + public function addFunction($function, $namespace = '') + { + static $port; + static $operation; + static $binding; + + if (!is_array($function)) { + $function = (array) $function; + } + + $uri = $this->getUri(); + + if (!($this->_wsdl instanceof Zend_Soap_Wsdl)) { + $parts = explode('.', basename($_SERVER['SCRIPT_NAME'])); + $name = $parts[0]; + $wsdl = new Zend_Soap_Wsdl($name, $uri, $this->_strategy); + + $port = $wsdl->addPortType($name . 'Port'); + $binding = $wsdl->addBinding($name . 'Binding', 'tns:' .$name. 'Port'); + + $wsdl->addSoapBinding($binding, 'rpc'); + $wsdl->addService($name . 'Service', $name . 'Port', 'tns:' . $name . 'Binding', $uri); + } else { + $wsdl = $this->_wsdl; + } + + foreach ($function as $func) { + $method = $this->_reflection->reflectFunction($func); + foreach ($method->getPrototypes() as $prototype) { + $args = array(); + foreach ($prototype->getParameters() as $param) { + $args[$param->getName()] = $wsdl->getType($param->getType()); + } + $message = $wsdl->addMessage($method->getName() . 'Request', $args); + $desc = $method->getDescription(); + if (strlen($desc) > 0) { + //$wsdl->addDocumentation($message, $desc); + } + if ($prototype->getReturnType() != "void") { + $message = $wsdl->addMessage($method->getName() . 'Response', array($method->getName() . 'Return' => $wsdl->getType($prototype->getReturnType()))); + } + /* 's */ + $portOperation = $wsdl->addPortOperation($port, $method->getName(), 'tns:' .$method->getName(). 'Request', 'tns:' .$method->getName(). 'Response'); + if (strlen($desc) > 0) { + //$wsdl->addDocumentation($portOperation, $desc); + } + /* 's */ + + /* 's */ + $operation = $wsdl->addBindingOperation($binding, $method->getName(), array('use' => 'encoded', 'encodingStyle' => "http://schemas.xmlsoap.org/soap/encoding/"), array('use' => 'encoded', 'encodingStyle' => "http://schemas.xmlsoap.org/soap/encoding/")); + $wsdl->addSoapOperation($operation, $uri->getUri() . '#' .$method->getName()); + /* 's */ + + $this->_functions[] = $method->getName(); + + // We will only add one prototype + break; + } + } + $this->_wsdl = $wsdl; + } + + /** + * Action to take when an error occurs + * + * @param string $fault + * @param string|int $code + */ + public function fault($fault = null, $code = null) + { + require_once "Zend/Soap/AutoDiscover/Exception.php"; + throw new Zend_Soap_AutoDiscover_Exception("Function has no use in AutoDiscover."); + } + + /** + * Handle the Request + * + * @param string $request A non-standard request - Not Used + */ + public function handle($request = false) + { + if (!headers_sent()) { + header('Content-Type: text/xml'); + } + $this->_wsdl->dump(); + } + + /** + * Return an array of functions in the WSDL + * + * @return array + */ + public function getFunctions() + { + return $this->_functions; + } + + /** + * Load Functions + * + * @param unknown_type $definition + */ + public function loadFunctions($definition) + { + require_once "Zend/Soap/AutoDiscover/Exception.php"; + throw new Zend_Soap_AutoDiscover_Exception("Function has no use in AutoDiscover."); + } + + /** + * Set Persistance + * + * @param int $mode + */ + public function setPersistence($mode) + { + require_once "Zend/Soap/AutoDiscover/Exception.php"; + throw new Zend_Soap_AutoDiscover_Exception("Function has no use in AutoDiscover."); + } + + /** + * Returns an XSD Type for the given PHP type + * + * @param string $type PHP Type to get the XSD type for + * @return string + */ + public function getType($type) + { + if (!($this->_wsdl instanceof Zend_Soap_Wsdl)) { + /** @todo Exception throwing may be more correct */ + + // WSDL is not defined yet, so we can't recognize type in context of current service + return ''; + } else { + return $this->_wsdl->getType($type); + } + } +} + diff --git a/lib/zend/Zend/Soap/AutoDiscover/Exception.php b/lib/zend/Zend/Soap/AutoDiscover/Exception.php new file mode 100644 index 0000000000..5c7966a394 --- /dev/null +++ b/lib/zend/Zend/Soap/AutoDiscover/Exception.php @@ -0,0 +1,23 @@ + PHP class pairings for handling return/incoming values + * @var array + */ + protected $_classmap = null; + + /** + * Registered fault exceptions + * @var array + */ + protected $_faultExceptions = array(); + + /** + * SOAP version to use; SOAP_1_2 by default, to allow processing of headers + * @var int + */ + protected $_soapVersion = SOAP_1_2; + + /** Set of other SoapClient options */ + protected $_uri = null; + protected $_location = null; + protected $_style = null; + protected $_use = null; + protected $_login = null; + protected $_password = null; + protected $_proxy_host = null; + protected $_proxy_port = null; + protected $_proxy_login = null; + protected $_proxy_password = null; + protected $_local_cert = null; + protected $_passphrase = null; + protected $_compression = null; + protected $_connection_timeout = null; + + /** + * WSDL used to access server + * It also defines Zend_Soap_Client working mode (WSDL vs non-WSDL) + * + * @var string + */ + protected $_wsdl = null; + + /** + * SoapClient object + * + * @var SoapClient + */ + protected $_soapClient; + + /** + * Last invoked method + * + * @var string + */ + protected $_lastMethod = ''; + + /** + * Constructor + * + * @param string $wsdl + * @param array $options + */ + public function __construct($wsdl = null, $options = null) + { + if (!extension_loaded('soap')) { + throw new Zend_Soap_Client_Exception('SOAP extension is not loaded.'); + } + + if ($wsdl !== null) { + $this->setWsdl($wsdl); + } + if ($options !== null) { + $this->setOptions($options); + } + } + + /** + * Set wsdl + * + * @param string $wsdl + * @return Zend_Soap_Client + */ + public function setWsdl($wsdl) + { + $this->_wsdl = $wsdl; + $this->_soapClient = null; + + return $this; + } + + /** + * Get wsdl + * + * @return string + */ + public function getWsdl() + { + return $this->_wsdl; + } + + /** + * Set Options + * + * Allows setting options as an associative array of option => value pairs. + * + * @param array $options + * @return Zend_Soap_Client + * @throws Zend_SoapClient_Exception + */ + public function setOptions(array $options) + { + foreach ($options as $key => $value) { + switch ($key) { + case 'classmap': + case 'classMap': + $this->setClassmap($value); + break; + case 'encoding': + $this->setEncoding($value); + break; + case 'soapVersion': + case 'soap_version': + $this->setSoapVersion($value); + break; + case 'wsdl': + $this->setWsdl($value); + break; + case 'uri': + $this->setUri($value); + break; + case 'location': + $this->setLocation($value); + break; + case 'style': + $this->setStyle($value); + break; + case 'use': + $this->setEncodingMethod($value); + break; + case 'login': + $this->setHttpLogin($value); + break; + case 'password': + $this->setHttpPassword($value); + break; + case 'proxy_host': + $this->setProxyHost($value); + break; + case 'proxy_port': + $this->setProxyPort($value); + break; + case 'proxy_login': + $this->setProxyLogin($value); + break; + case 'proxy_password': + $this->setProxyPassword($value); + break; + case 'local_cert': + $this->setHttpsCertificate($value); + break; + case 'passphrase': + $this->setHttpsCertPassphrase($value); + break; + case 'compression': + $this->setCompressionOptions($value); + break; + + // Not used now + // case 'connection_timeout': + // $this->_connection_timeout = $value; + // break; + + default: + throw new Zend_Soap_Client_Exception('Unknown SOAP client option'); + break; + } + } + + return $this; + } + + /** + * Return array of options suitable for using with SoapClient constructor + * + * @return array + */ + public function getOptions() + { + $options = array(); + + $options['classmap'] = $this->getClassmap(); + $options['encoding'] = $this->getEncoding(); + $options['soap_version'] = $this->getSoapVersion(); + $options['wsdl'] = $this->getWsdl(); + $options['uri'] = $this->getUri(); + $options['location'] = $this->getLocation(); + $options['style'] = $this->getStyle(); + $options['use'] = $this->getEncodingMethod(); + $options['login'] = $this->getHttpLogin(); + $options['password'] = $this->getHttpPassword(); + $options['proxy_host'] = $this->getProxyHost(); + $options['proxy_port'] = $this->getProxyPort(); + $options['proxy_login'] = $this->getProxyLogin(); + $options['proxy_password'] = $this->getProxyPassword(); + $options['local_cert'] = $this->getHttpsCertificate(); + $options['passphrase'] = $this->getHttpsCertPassphrase(); + $options['compression'] = $this->getCompressionOptions(); +// $options['connection_timeout'] = $this->_connection_timeout; + + foreach ($options as $key => $value) { + if ($value == null) { + unset($options[$key]); + } + } + + return $options; + } + + /** + * Set SOAP version + * + * @param int $version One of the SOAP_1_1 or SOAP_1_2 constants + * @return Zend_Soap_Client + * @throws Zend_Soap_Client_Exception with invalid soap version argument + */ + public function setSoapVersion($version) + { + if (!in_array($version, array(SOAP_1_1, SOAP_1_2))) { + throw new Zend_Soap_Client_Exception('Invalid soap version specified. Use SOAP_1_1 or SOAP_1_2 constants.'); + } + $this->_soapVersion = $version; + + $this->_soapClient = null; + + return $this; + } + + /** + * Get SOAP version + * + * @return int + */ + public function getSoapVersion() + { + return $this->_soapVersion; + } + + /** + * Set classmap + * + * @param array $classmap + * @return Zend_Soap_Client + * @throws Zend_Soap_Client_Exception for any invalid class in the class map + */ + public function setClassmap(array $classmap) + { + foreach ($classmap as $type => $class) { + if (!class_exists($class)) { + throw new Zend_Soap_Client_Exception('Invalid class in class map'); + } + } + + $this->_classmap = $classmap; + + $this->_soapClient = null; + + return $this; + } + + /** + * Retrieve classmap + * + * @return mixed + */ + public function getClassmap() + { + return $this->_classmap; + } + + /** + * Set encoding + * + * @param string $encoding + * @return Zend_Soap_Client + * @throws Zend_Soap_Client_Exception with invalid encoding argument + */ + public function setEncoding($encoding) + { + if (!is_string($encoding)) { + throw new Zend_Soap_Client_Exception('Invalid encoding specified'); + } + + $this->_encoding = $encoding; + + $this->_soapClient = null; + + return $this; + } + + /** + * Get encoding + * + * @return string + */ + public function getEncoding() + { + return $this->_encoding; + } + + /** + * Check for valid URN + * + * @param string $urn + * @return true + * @throws Zend_Soap_Client_Exception on invalid URN + */ + public function validateUrn($urn) + { + $segs = parse_url($urn); + if (isset($segs['scheme'])) { + return true; + } + + throw new Zend_Soap_Client_Exception('Invalid URN'); + } + + /** + * Set URI + * + * URI in Web Service the target namespace + * + * @param string $uri + * @return Zend_Soap_Client + * @throws Zend_Soap_Client_Exception with invalid uri argument + */ + public function setUri($uri) + { + $this->validateUrn($uri); + $this->_uri = $uri; + + $this->_soapClient = null; + + return $this; + } + + /** + * Retrieve URI + * + * @return string + */ + public function getUri() + { + return $this->_uri; + } + + /** + * Set Location + * + * URI in Web Service the target namespace + * + * @param string $location + * @return Zend_Soap_Client + * @throws Zend_Soap_Client_Exception with invalid uri argument + */ + public function setLocation($location) + { + $this->validateUrn($location); + $this->_location = $location; + + $this->_soapClient = null; + + return $this; + } + + /** + * Retrieve URI + * + * @return string + */ + public function getLocation() + { + return $this->_location; + } + + /** + * Set request style + * + * @param int $style One of the SOAP_RPC or SOAP_DOCUMENT constants + * @return Zend_Soap_Client + * @throws Zend_Soap_Client_Exception with invalid style argument + */ + public function setStyle($style) + { + if (!in_array($style, array(SOAP_RPC, SOAP_DOCUMENT))) { + throw new Zend_Soap_Client_Exception('Invalid request style specified. Use SOAP_RPC or SOAP_DOCUMENT constants.'); + } + + $this->_style = $style; + + $this->_soapClient = null; + + return $this; + } + + /** + * Get request style + * + * @return int + */ + public function getStyle() + { + return $this->_style; + } + + /** + * Set message encoding method + * + * @param int $use One of the SOAP_ENCODED or SOAP_LITERAL constants + * @return Zend_Soap_Client + * @throws Zend_Soap_Client_Exception with invalid message encoding method argument + */ + public function setEncodingMethod($use) + { + if (!in_array($use, array(SOAP_ENCODED, SOAP_LITERAL))) { + throw new Zend_Soap_Client_Exception('Invalid message encoding method. Use SOAP_ENCODED or SOAP_LITERAL constants.'); + } + + $this->_use = $use; + + $this->_soapClient = null; + + return $this; + } + + /** + * Get message encoding method + * + * @return int + */ + public function getEncodingMethod() + { + return $this->_use; + } + + /** + * Set HTTP login + * + * @param string $login + * @return Zend_Soap_Client + */ + public function setHttpLogin($login) + { + $this->_login = $login; + + $this->_soapClient = null; + + return $this; + } + + /** + * Retrieve HTTP Login + * + * @return string + */ + public function getHttpLogin() + { + return $this->_login; + } + + /** + * Set HTTP password + * + * @param string $password + * @return Zend_Soap_Client + */ + public function setHttpPassword($password) + { + $this->_password = $password; + + $this->_soapClient = null; + + return $this; + } + + /** + * Retrieve HTTP Password + * + * @return string + */ + public function getHttpPassword() + { + return $this->_password; + } + + /** + * Set proxy host + * + * @param string $proxyHost + * @return Zend_Soap_Client + */ + public function setProxyHost($proxyHost) + { + $this->_proxy_host = $proxyHost; + + $this->_soapClient = null; + + return $this; + } + + /** + * Retrieve proxy host + * + * @return string + */ + public function getProxyHost() + { + return $this->_proxy_host; + } + + /** + * Set proxy port + * + * @param int $proxyPort + * @return Zend_Soap_Client + */ + public function setProxyPort($proxyPort) + { + $this->_proxy_port = (int)$proxyPort; + + $this->_soapClient = null; + + return $this; + } + + /** + * Retrieve proxy port + * + * @return int + */ + public function getProxyPort() + { + return $this->_proxy_port; + } + + /** + * Set proxy login + * + * @param string $proxyLogin + * @return Zend_Soap_Client + */ + public function setProxyLogin($proxyLogin) + { + $this->_proxy_login = $proxyLogin; + + $this->_soapClient = null; + + return $this; + } + + /** + * Retrieve proxy login + * + * @return string + */ + public function getProxyLogin() + { + return $this->_proxy_login; + } + + /** + * Set proxy password + * + * @param string $proxyLogin + * @return Zend_Soap_Client + */ + public function setProxyPassword($proxyPassword) + { + $this->_proxy_password = $proxyPassword; + + $this->_soapClient = null; + + return $this; + } + + /** + * Set HTTPS client certificate path + * + * @param string $localCert local certificate path + * @return Zend_Soap_Client + * @throws Zend_Soap_Client_Exception with invalid local certificate path argument + */ + public function setHttpsCertificate($localCert) + { + if (!is_readable($localCert)) { + throw new Zend_Soap_Client_Exception('Invalid HTTPS client certificate path.'); + } + + $this->_local_cert = $localCert; + + $this->_soapClient = null; + + return $this; + } + + /** + * Get HTTPS client certificate path + * + * @return string + */ + public function getHttpsCertificate() + { + return $this->_local_cert; + } + + /** + * Set HTTPS client certificate passphrase + * + * @param string $passphrase + * @return Zend_Soap_Client + */ + public function setHttpsCertPassphrase($passphrase) + { + $this->_passphrase = $passphrase; + + $this->_soapClient = null; + + return $this; + } + + /** + * Get HTTPS client certificate passphrase + * + * @return string + */ + public function getHttpsCertPassphrase() + { + return $this->_passphrase; + } + + /** + * Set compression options + * + * @param int $compressionOptions + * @return Zend_Soap_Client + */ + public function setCompressionOptions($compressionOptions) + { + $this->_compression = $compressionOptions; + + $this->_soapClient = null; + + return $this; + } + + /** + * Get Compression options + * + * @return int + */ + public function getCompressionOptions() + { + return $this->_compression; + } + + /** + * Retrieve proxy password + * + * @return string + */ + public function getProxyPassword() + { + return $this->_proxy_password; + } + + /** + * Retrieve request XML + * + * @return string + */ + public function getLastRequest() + { + if ($this->_soapClient !== null) { + return $this->_soapClient->__getLastRequest(); + } + + return ''; + } + + /** + * Get response XML + * + * @return string + */ + public function getLastResponse() + { + if ($this->_soapClient !== null) { + return $this->_soapClient->__getLastResponse(); + } + + return ''; + } + + /** + * Retrieve request headers + * + * @return string + */ + public function getLastRequestHeaders() + { + if ($this->_soapClient !== null) { + return $this->_soapClient->__getLastRequestHeaders(); + } + + return ''; + } + + /** + * Retrieve response headers + * + * @return string + */ + public function getLastResponseHeaders() + { + if ($this->_soapClient !== null) { + return $this->_soapClient->__getLastResponseHeaders(); + } + + return ''; + } + + /** + * Retrieve last invoked method + * + * @return string + */ + public function getLastMethod() + { + return $this->_lastMethod; + } + + /** + * Do request proxy method. + * + * May be overridden in subclasses + * + * @internal + * @param Zend_Soap_Client_Common $client + * @param string $request + * @param string $location + * @param string $action + * @param int $version + * @param int $one_way + * @return mixed + */ + public function _doRequest(Zend_Soap_Client_Common $client, $request, $location, $action, $version, $one_way = null) + { + // Perform request as is + if ($one_way == null) { + return call_user_func(array($client,'SoapClient::__doRequest'), $request, $location, $action, $version); + } else { + return call_user_func(array($client,'SoapClient::__doRequest'), $request, $location, $action, $version, $one_way); + } + } + + /** + * Initialize SOAP Client object + * + * @throws Zend_Soap_Client_Exception + */ + protected function _initSoapClientObject() + { + $wsdl = $this->getWsdl(); + $options = array_merge($this->getOptions(), array('trace' => true)); + + + if ($wsdl == null) { + if (!isset($options['location'])) { + throw new Zend_Soap_Client_Exception('\'location\' parameter is required in non-WSDL mode.'); + } + if (!isset($options['uri'])) { + throw new Zend_Soap_Client_Exception('\'uri\' parameter is required in non-WSDL mode.'); + } + } else { + if (isset($options['use'])) { + throw new Zend_Soap_Client_Exception('\'use\' parameter only works in non-WSDL mode.'); + } + if (isset($options['style'])) { + throw new Zend_Soap_Client_Exception('\'style\' parameter only works in non-WSDL mode.'); + } + } + unset($options['wsdl']); + + $this->_soapClient = new Zend_Soap_Client_Common(array($this, '_doRequest'), $wsdl, $options); + } + + + /** + * Perform arguments pre-processing + * + * My be overridden in descendant classes + * + * @param array $arguments + */ + protected function _preProcessArguments($arguments) + { + // Do nothing + return $arguments; + } + + /** + * Perform result pre-processing + * + * My be overridden in descendant classes + * + * @param array $arguments + */ + protected function _preProcessResult($result) + { + // Do nothing + return $result; + } + + /** + * Perform a SOAP call + * + * @param string $name + * @param array $arguments + * @return mixed + */ + public function __call($name, $arguments) + { + if ($this->_soapClient == null) { + $this->_initSoapClientObject(); + } + + $this->_lastMethod = $name; + + $result = call_user_func_array(array($this->_soapClient, $name), $this->_preProcessArguments($arguments)); + + return $this->_preProcessResult($result); + } + + + /** + * Return a list of available functions + * + * @return array + * @throws Zend_Soap_Client_Exception + */ + public function getFunctions() + { + if ($this->getWsdl() == null) { + throw new Zend_Soap_Client_Exception('\'getFunctions\' method is available only in WSDL mode.'); + } + + if ($this->_soapClient == null) { + $this->_initSoapClientObject(); + } + + return $this->_soapClient->__getFunctions(); + } + + + /** + * Get used types. + * + * @return array + */ + + /** + * Return a list of SOAP types + * + * @return array + * @throws Zend_Soap_Client_Exception + */ + public function getTypes() + { + if ($this->getWsdl() == null) { + throw new Zend_Soap_Client_Exception('\'getTypes\' method is available only in WSDL mode.'); + } + + if ($this->_soapClient == null) { + $this->_initSoapClientObject(); + } + + return $this->_soapClient->__getTypes(); + } +} diff --git a/lib/zend/Zend/Soap/Client/Common.php b/lib/zend/Zend/Soap/Client/Common.php new file mode 100644 index 0000000000..7a057a829a --- /dev/null +++ b/lib/zend/Zend/Soap/Client/Common.php @@ -0,0 +1,73 @@ +_doRequestCallback = $doRequestCallback; + + parent::__construct($wsdl, $options); + } + + /** + * Performs SOAP request over HTTP. + * Overridden to implement different transport layers, perform additional XML processing or other purpose. + * + * @param string $request + * @param string $location + * @param string $action + * @param int $version + * @param int $one_way + * @return mixed + */ + function __doRequest($request, $location, $action, $version, $one_way = null) + { + if ($one_way === null) { + return call_user_func($this->_doRequestCallback, $this, $request, $location, $action, $version); + } else { + return call_user_func($this->_doRequestCallback, $this, $request, $location, $action, $version, $one_way); + } + } + +} + +} // end if (extension_loaded('soap') diff --git a/lib/zend/Zend/Soap/Client/DotNet.php b/lib/zend/Zend/Soap/Client/DotNet.php new file mode 100644 index 0000000000..d00a39de14 --- /dev/null +++ b/lib/zend/Zend/Soap/Client/DotNet.php @@ -0,0 +1,88 @@ +setSoapVersion(SOAP_1_1); + + parent::__construct($wsdl, $options); + } + + + /** + * Perform arguments pre-processing + * + * My be overridden in descendant classes + * + * @param array $arguments + */ + protected function _preProcessArguments($arguments) + { + // Do nothing + return array($arguments); + } + + /** + * Perform result pre-processing + * + * My be overridden in descendant classes + * + * @param array $arguments + */ + protected function _preProcessResult($result) + { + $resultProperty = $this->getLastMethod() . 'Result'; + + return $result->$resultProperty; + } + +} + +} // end if (extension_loaded('soap') diff --git a/lib/zend/Zend/Soap/Client/Exception.php b/lib/zend/Zend/Soap/Client/Exception.php new file mode 100644 index 0000000000..84e302faac --- /dev/null +++ b/lib/zend/Zend/Soap/Client/Exception.php @@ -0,0 +1,33 @@ +_server = $server; + + // Use Server specified SOAP version as default + $this->setSoapVersion($server->getSoapVersion()); + + parent::__construct($wsdl, $options); + } + + /** + * Actual "do request" method. + * + * @internal + * @param Zend_Soap_Client_Common $client + * @param string $request + * @param string $location + * @param string $action + * @param int $version + * @param int $one_way + * @return mixed + */ + public function _doRequest(Zend_Soap_Client_Common $client, $request, $location, $action, $version, $one_way = null) + { + // Perform request as is + ob_start(); + $this->_server->handle($request); + $response = ob_get_contents(); + ob_end_clean(); + + return $response; + } +} + +} // end if (extension_loaded('soap') diff --git a/lib/zend/Zend/Soap/Server.php b/lib/zend/Zend/Soap/Server.php new file mode 100644 index 0000000000..2a7b2fb541 --- /dev/null +++ b/lib/zend/Zend/Soap/Server.php @@ -0,0 +1,862 @@ + PHP class pairings for handling return/incoming values + * @var array + */ + protected $_classmap; + + /** + * Encoding + * @var string + */ + protected $_encoding; + + /** + * Registered fault exceptions + * @var array + */ + protected $_faultExceptions = array(); + + /** + * Functions registered with this server; may be either an array or the SOAP_FUNCTIONS_ALL + * constant + * @var array|int + */ + protected $_functions = array(); + + /** + * Persistence mode; should be one of the SOAP persistence constants + * @var int + */ + protected $_persistence; + + /** + * Request XML + * @var string + */ + protected $_request; + + /** + * Response XML + * @var string + */ + protected $_response; + + /** + * Flag: whether or not {@link handle()} should return a response instead + * of automatically emitting it. + * @var boolean + */ + protected $_returnResponse = false; + + /** + * SOAP version to use; SOAP_1_2 by default, to allow processing of headers + * @var int + */ + protected $_soapVersion = SOAP_1_2; + + /** + * URI or path to WSDL + * @var string + */ + protected $_wsdl; + + /** + * URI namespace for SOAP server + * @var string URI + */ + protected $_uri; + + /** + * Constructor + * + * Sets display_errors INI setting to off (prevent client errors due to bad + * XML in response). Registers {@link handlePhpErrors()} as error handler + * for E_USER_ERROR. + * + * If $wsdl is provided, it is passed on to {@link setWsdl()}; if any + * options are specified, they are passed on to {@link setOptions()}. + * + * @param string $wsdl + * @param array $options + * @return void + */ + public function __construct($wsdl = null, array $options = null) + { + if (!extension_loaded('soap')) { + throw new Zend_Soap_Server_Exception('SOAP extension is not loaded.'); + } + + if (null !== $wsdl) { + $this->setWsdl($wsdl); + } + + if (null !== $options) { + $this->setOptions($options); + } + } + + /** + * Set Options + * + * Allows setting options as an associative array of option => value pairs. + * + * @param array $options + * @return Zend_Soap_Server + */ + public function setOptions(array $options) + { + foreach ($options as $key => $value) { + switch ($key) { + case 'actor': + $this->setActor($value); + break; + case 'classmap': + case 'classMap': + $this->setClassmap($value); + break; + case 'encoding': + $this->setEncoding($value); + break; + case 'soapVersion': + case 'soap_version': + $this->setSoapVersion($value); + break; + case 'uri': + $this->setUri($value); + break; + case 'wsdl': + $this->setWsdl($value); + break; + default: + break; + } + } + + return $this; + } + + /** + * Return array of options suitable for using with SoapServer constructor + * + * @return array + */ + public function getOptions() + { + $options = array(); + if (null !== $this->_actor) { + $options['actor'] = $this->_actor; + } + + if (null !== $this->_classmap) { + $options['classmap'] = $this->_classmap; + } + + if (null !== $this->_encoding) { + $options['encoding'] = $this->_encoding; + } + + if (null !== $this->_soapVersion) { + $options['soap_version'] = $this->_soapVersion; + } + + if (null !== $this->_uri) { + $options['uri'] = $this->_uri; + } + + return $options; + } + + /** + * Set encoding + * + * @param string $encoding + * @return Zend_Soap_Server + * @throws Zend_Soap_Server_Exception with invalid encoding argument + */ + public function setEncoding($encoding) + { + if (!is_string($encoding)) { + throw new Zend_Soap_Server_Exception('Invalid encoding specified'); + } + + $this->_encoding = $encoding; + return $this; + } + + /** + * Get encoding + * + * @return string + */ + public function getEncoding() + { + return $this->_encoding; + } + + /** + * Set SOAP version + * + * @param int $version One of the SOAP_1_1 or SOAP_1_2 constants + * @return Zend_Soap_Server + * @throws Zend_Soap_Server_Exception with invalid soap version argument + */ + public function setSoapVersion($version) + { + if (!in_array($version, array(SOAP_1_1, SOAP_1_2))) { + throw new Zend_Soap_Server_Exception('Invalid soap version specified'); + } + + $this->_soapVersion = $version; + return $this; + } + + /** + * Get SOAP version + * + * @return int + */ + public function getSoapVersion() + { + return $this->_soapVersion; + } + + /** + * Check for valid URN + * + * @param string $urn + * @return true + * @throws Zend_Soap_Server_Exception on invalid URN + */ + public function validateUrn($urn) + { + $segs = parse_url($urn); + if (isset($segs['scheme'])) { + return true; + } + + throw new Zend_Soap_Server_Exception('Invalid URN'); + } + + /** + * Set actor + * + * Actor is the actor URI for the server. + * + * @param string $actor + * @return Zend_Soap_Server + */ + public function setActor($actor) + { + $this->validateUrn($actor); + $this->_actor = $actor; + return $this; + } + + /** + * Retrieve actor + * + * @return string + */ + public function getActor() + { + return $this->_actor; + } + + /** + * Set URI + * + * URI in SoapServer is actually the target namespace, not a URI; $uri must begin with 'urn:'. + * + * @param string $uri + * @return Zend_Soap_Server + * @throws Zend_Soap_Server_Exception with invalid uri argument + */ + public function setUri($uri) + { + $this->validateUrn($uri); + $this->_uri = $uri; + return $this; + } + + /** + * Retrieve URI + * + * @return string + */ + public function getUri() + { + return $this->_uri; + } + + /** + * Set classmap + * + * @param array $classmap + * @return Zend_Soap_Server + * @throws Zend_Soap_Server_Exception for any invalid class in the class map + */ + public function setClassmap($classmap) + { + if (!is_array($classmap)) { + /** + * @see Zend_Soap_Server_Exception + */ + require_once 'Zend/Soap/Server/Exception.php'; + throw new Zend_Soap_Server_Exception('Classmap must be an array'); + } + foreach ($classmap as $type => $class) { + if (!class_exists($class)) { + /** + * @see Zend_Soap_Server_Exception + */ + require_once 'Zend/Soap/Server/Exception.php'; + throw new Zend_Soap_Server_Exception('Invalid class in class map'); + } + } + + $this->_classmap = $classmap; + return $this; + } + + /** + * Retrieve classmap + * + * @return mixed + */ + public function getClassmap() + { + return $this->_classmap; + } + + /** + * Set wsdl + * + * @param string $wsdl URI or path to a WSDL + * @return Zend_Soap_Server + */ + public function setWsdl($wsdl) + { + $this->_wsdl = $wsdl; + return $this; + } + + /** + * Retrieve wsdl + * + * @return string + */ + public function getWsdl() + { + return $this->_wsdl; + } + + /** + * Attach a function as a server method + * + * @param array|string $function Function name, array of function names to attach, + * or SOAP_FUNCTIONS_ALL to attach all functions + * @param string $namespace Ignored + * @return Zend_Soap_Server + * @throws Zend_Soap_Server_Exception on invalid functions + */ + public function addFunction($function, $namespace = '') + { + // Bail early if set to SOAP_FUNCTIONS_ALL + if ($this->_functions == SOAP_FUNCTIONS_ALL) { + return $this; + } + + if (is_array($function)) { + foreach ($function as $func) { + if (is_string($func) && function_exists($func)) { + $this->_functions[] = $func; + } else { + throw new Zend_Soap_Server_Exception('One or more invalid functions specified in array'); + } + } + $this->_functions = array_merge($this->_functions, $function); + } elseif (is_string($function) && function_exists($function)) { + $this->_functions[] = $function; + } elseif ($function == SOAP_FUNCTIONS_ALL) { + $this->_functions = SOAP_FUNCTIONS_ALL; + } else { + throw new Zend_Soap_Server_Exception('Invalid function specified'); + } + + if (is_array($this->_functions)) { + $this->_functions = array_unique($this->_functions); + } + + return $this; + } + + /** + * Attach a class to a server + * + * Accepts a class name to use when handling requests. Any additional + * arguments will be passed to that class' constructor when instantiated. + * + * @param mixed $class Class name or object instance to examine and attach + * to the server. + * @param mixed $arg1 Optional argument to pass to class constructor + * @param mixed $arg2 Optional second argument to pass to class constructor + * dispatch. + * @return Zend_Soap_Server + * @throws Zend_Soap_Server_Exception if called more than once, or if class + * does not exist + */ + public function setClass($class, $arg1 = null, $arg2 = null) + { + if (isset($this->_class)) { + throw new Zend_Soap_Server_Exception('A class has already been registered with this soap server instance'); + } + + if (!is_string($class)) { + throw new Zend_Soap_Server_Exception('Invalid class argument (' . gettype($class) . ')'); + } + + if (!class_exists($class)) { + throw new Zend_Soap_Server_Exception('Class "' . $class . '" does not exist'); + } + + $this->_class = $class; + if (1 < func_num_args()) { + $argv = func_get_args(); + array_shift($argv); + $this->_classArgs = $argv; + } + + return $this; + } + + /** + * Attach an object to a server + * + * Accepts an instanciated object to use when handling requests. + * + * @param object $object + * @return Zend_Soap_Server + */ + public function setObject($object) + { + if(!is_object($object)) { + throw new Zend_Soap_Server_Exception('Invalid object argument ('.gettype($object).')'); + } + + if(isset($this->_object)) { + throw new Zend_Soap_Server_Exception('An object has already been registered with this soap server instance'); + } + + $this->_object = $object; + + return $this; + } + + /** + * Return a server definition array + * + * Returns a list of all functions registered with {@link addFunction()}, + * merged with all public methods of the class set with {@link setClass()} + * (if any). + * + * @access public + * @return array + */ + public function getFunctions() + { + $functions = array(); + if (null !== $this->_class) { + $functions = get_class_methods($this->_class); + } elseif (null !== $this->_object) { + $functions = get_class_methods($this->_object); + } + + return array_merge((array) $this->_functions, $functions); + } + + /** + * Unimplemented: Load server definition + * + * @param array $array + * @return void + * @throws Zend_Soap_Server_Exception Unimplemented + */ + public function loadFunctions($definition) + { + throw new Zend_Soap_Server_Exception('Unimplemented'); + } + + /** + * Set server persistence + * + * @param int $mode + * @return Zend_Soap_Server + */ + public function setPersistence($mode) + { + if (!in_array($mode, array(SOAP_PERSISTENCE_SESSION, SOAP_PERSISTENCE_REQUEST))) { + throw new Zend_Soap_Server_Exception('Invalid persistence mode specified'); + } + + $this->_persistence = $mode; + return $this; + } + + /** + * Get server persistence + * + * @return Zend_Soap_Server + */ + public function getPersistence() + { + return $this->_persistence; + } + + /** + * Set request + * + * $request may be any of: + * - DOMDocument; if so, then cast to XML + * - DOMNode; if so, then grab owner document and cast to XML + * - SimpleXMLElement; if so, then cast to XML + * - stdClass; if so, calls __toString() and verifies XML + * - string; if so, verifies XML + * + * @param DOMDocument|DOMNode|SimpleXMLElement|stdClass|string $request + * @return Zend_Soap_Server + */ + private function _setRequest($request) + { + if ($request instanceof DOMDocument) { + $xml = $request->saveXML(); + } elseif ($request instanceof DOMNode) { + $xml = $request->ownerDocument->saveXML(); + } elseif ($request instanceof SimpleXMLElement) { + $xml = $request->asXML(); + } elseif (is_object($request) || is_string($request)) { + if (is_object($request)) { + $xml = $request->__toString(); + } else { + $xml = $request; + } + + $dom = new DOMDocument(); + if(strlen($xml) == 0 || !$dom->loadXML($xml)) { + throw new Zend_Soap_Server_Exception('Invalid XML'); + } + } + $this->_request = $xml; + return $this; + } + + /** + * Retrieve request XML + * + * @return string + */ + public function getLastRequest() + { + return $this->_request; + } + + /** + * Set return response flag + * + * If true, {@link handle()} will return the response instead of + * automatically sending it back to the requesting client. + * + * The response is always available via {@link getResponse()}. + * + * @param boolean $flag + * @return Zend_Soap_Server + */ + public function setReturnResponse($flag) + { + $this->_returnResponse = ($flag) ? true : false; + return $this; + } + + /** + * Retrieve return response flag + * + * @return boolean + */ + public function getReturnResponse() + { + return $this->_returnResponse; + } + + /** + * Get response XML + * + * @return string + */ + public function getLastResponse() + { + return $this->_response; + } + + /** + * Get SoapServer object + * + * Uses {@link $_wsdl} and return value of {@link getOptions()} to instantiate + * SoapServer object, and then registers any functions or class with it, as + * well as peristence. + * + * @return SoapServer + */ + protected function _getSoap() + { + $options = $this->getOptions(); + $server = new SoapServer($this->_wsdl, $options); + + if (!empty($this->_functions)) { + $server->addFunction($this->_functions); + } + + if (!empty($this->_class)) { + $args = $this->_classArgs; + array_unshift($args, $this->_class); + call_user_func_array(array($server, 'setClass'), $args); + } + + if (!empty($this->_object)) { + $server->setObject($this->_object); + } + + if (null !== $this->_persistence) { + $server->setPersistence($this->_persistence); + } + + return $server; + } + + /** + * Handle a request + * + * Instantiates SoapServer object with options set in object, and + * dispatches its handle() method. + * + * $request may be any of: + * - DOMDocument; if so, then cast to XML + * - DOMNode; if so, then grab owner document and cast to XML + * - SimpleXMLElement; if so, then cast to XML + * - stdClass; if so, calls __toString() and verifies XML + * - string; if so, verifies XML + * + * If no request is passed, pulls request using php:://input (for + * cross-platform compatability purposes). + * + * @param DOMDocument|DOMNode|SimpleXMLElement|stdClass|string $request Optional request + * @return void|string + */ + public function handle($request = null) + { + if (null === $request) { + $request = file_get_contents('php://input'); + } + + // Set Zend_Soap_Server error handler + $displayErrorsOriginalState = ini_get('display_errors'); + ini_set('display_errors', false); + set_error_handler(array($this, 'handlePhpErrors'), E_USER_ERROR); + + $setRequestException = null; + /** + * @see Zend_Soap_Server_Exception + */ + require_once 'Zend/Soap/Server/Exception.php'; + try { + $this->_setRequest($request); + } catch (Zend_Soap_Server_Exception $e) { + $setRequestException = $e; + } + + $soap = $this->_getSoap(); + + ob_start(); + if($setRequestException instanceof Exception) { + // Send SOAP fault message if we've catched exception + $soap->fault("Sender", $setRequestException->getMessage()); + } else { + try { + $soap->handle($request); + } catch (Exception $e) { + $fault = $this->fault($e); + $soap->fault($fault->faultcode, $fault->faultstring); + } + } + $this->_response = ob_get_clean(); + + // Restore original error handler + restore_error_handler(); + ini_set('display_errors', $displayErrorsOriginalState); + + if (!$this->_returnResponse) { + echo $this->_response; + return; + } + + return $this->_response; + } + + /** + * Register a valid fault exception + * + * @param string|array $class Exception class or array of exception classes + * @return Zend_Soap_Server + */ + public function registerFaultException($class) + { + $this->_faultExceptions = array_merge($this->_faultExceptions, (array) $class); + return $this; + } + + /** + * Deregister a fault exception from the fault exception stack + * + * @param string $class + * @return boolean + */ + public function deregisterFaultException($class) + { + if (in_array($class, $this->_faultExceptions, true)) { + $index = array_search($class, $this->_faultExceptions); + unset($this->_faultExceptions[$index]); + return true; + } + + return false; + } + + /** + * Return fault exceptions list + * + * @return array + */ + public function getFaultExceptions() + { + return $this->_faultExceptions; + } + + /** + * Generate a server fault + * + * Note that the arguments are reverse to those of SoapFault. + * + * If an exception is passed as the first argument, its message and code + * will be used to create the fault object if it has been registered via + * {@Link registerFaultException()}. + * + * @link http://www.w3.org/TR/soap12-part1/#faultcodes + * @param string|Exception $fault + * @param string $code SOAP Fault Codes + * @return SoapFault + */ + public function fault($fault = null, $code = "Receiver") + { + if ($fault instanceof Exception) { + $class = get_class($fault); + if (in_array($class, $this->_faultExceptions)) { + $message = $fault->getMessage(); + $eCode = $fault->getCode(); + $code = empty($eCode) ? $code : $eCode; + } else { + $message = 'Unknown error'; + } + } elseif(is_string($fault)) { + $message = $fault; + } else { + $message = 'Unknown error'; + } + + $allowedFaultModes = array( + 'VersionMismatch', 'MustUnderstand', 'DataEncodingUnknown', + 'Sender', 'Receiver', 'Server' + ); + if(!in_array($code, $allowedFaultModes)) { + $code = "Receiver"; + } + + return new SoapFault($code, $message); + } + + /** + * Throw PHP errors as SoapFaults + * + * @param int $errno + * @param string $errstr + * @param string $errfile + * @param int $errline + * @param array $errcontext + * @return void + * @throws SoapFault + */ + public function handlePhpErrors($errno, $errstr, $errfile = null, $errline = null, array $errcontext = null) + { + throw $this->fault($errstr, "Receiver"); + } +} diff --git a/lib/zend/Zend/Soap/Server/Exception.php b/lib/zend/Zend/Soap/Server/Exception.php new file mode 100644 index 0000000000..d1a2050b35 --- /dev/null +++ b/lib/zend/Zend/Soap/Server/Exception.php @@ -0,0 +1,35 @@ +getUri(); + } + $this->_uri = $uri; + /* + if( ($pos = strpos($uri, "?")) !== false) { + $uri = substr($uri, 0, $pos); + } + * + */ + /** + * @todo change DomDocument object creation from cparsing to construxting using API + * It also should authomatically escape $name and $uri values if necessary + */ + $wsdl = " + "; + $this->_dom = new DOMDocument(); + if (!$this->_dom->loadXML($wsdl)) { + throw new Zend_Server_Exception('Unable to create DomDocument'); + } else { + $this->_wsdl = $this->_dom->documentElement; + } + + $this->setComplexTypeStrategy($strategy); + } + + /** + * Set a new uri for this WSDL + * + * @param string|Zend_Uri_Http $uri + * @return Zend_Server_Wsdl + */ + public function setUri($uri) + { + if ($uri instanceof Zend_Uri_Http) { + $uri = $uri->getUri(); + } + $oldUri = $this->_uri; + $this->_uri = $uri; + + if($this->_dom !== null) { + // @todo: This is the worst hack ever, but its needed due to design and non BC issues of WSDL generation + $xml = $this->_dom->saveXML(); + $xml = str_replace($oldUri, $uri, $xml); + $this->_dom = new DOMDocument(); + $this->_dom->loadXML($xml); + } + + return $this; + } + + /** + * Set a strategy for complex type detection and handling + * + * @todo Boolean is for backwards compability with extractComplexType object var. Remove it in later versions. + * @param boolean|string|Zend_Soap_Wsdl_Strategy_Interface $strategy + * @return Zend_Soap_Wsdl + */ + public function setComplexTypeStrategy($strategy) + { + if($strategy === true) { + require_once "Zend/Soap/Wsdl/Strategy/DefaultComplexType.php"; + $strategy = new Zend_Soap_Wsdl_Strategy_DefaultComplexType(); + } else if($strategy === false) { + require_once "Zend/Soap/Wsdl/Strategy/AnyType.php"; + $strategy = new Zend_Soap_Wsdl_Strategy_AnyType(); + } else if(is_string($strategy)) { + if(class_exists($strategy)) { + $strategy = new $strategy(); + } else { + require_once "Zend/Soap/Wsdl/Exception.php"; + throw new Zend_Soap_Wsdl_Exception( + sprintf("Strategy with name '%s does not exist.", $strategy + )); + } + } + + if(!($strategy instanceof Zend_Soap_Wsdl_Strategy_Interface)) { + require_once "Zend/Soap/Wsdl/Exception.php"; + throw new Zend_Soap_Wsdl_Exception("Set a strategy that is not of type 'Zend_Soap_Wsdl_Strategy_Interface'"); + } + $this->_strategy = $strategy; + return $this; + } + + /** + * Get the current complex type strategy + * + * @return Zend_Soap_Wsdl_Strategy_Interface + */ + public function getComplexTypeStrategy() + { + return $this->_strategy; + } + + /** + * Add a {@link http://www.w3.org/TR/wsdl#_messages message} element to the WSDL + * + * @param string $name Name for the {@link http://www.w3.org/TR/wsdl#_messages message} + * @param array $parts An array of {@link http://www.w3.org/TR/wsdl#_message parts} + * The array is constructed like: 'name of part' => 'part xml schema data type' + * @return object The new message's XML_Tree_Node for use in {@link function addDocumentation} + */ + public function addMessage($name, $parts) + { + $message = $this->_dom->createElement('message'); + + $message->setAttribute('name', $name); + + if (sizeof($parts) > 0) { + foreach ($parts as $name => $type) { + $part = $this->_dom->createElement('part'); + $part->setAttribute('name', $name); + $part->setAttribute('type', $type); + $message->appendChild($part); + } + } + + $this->_wsdl->appendChild($message); + + return $message; + } + + /** + * Add a {@link http://www.w3.org/TR/wsdl#_porttypes portType} element to the WSDL + * + * @param string $name portType element's name + * @return object The new portType's XML_Tree_Node for use in {@link function addPortOperation} and {@link function addDocumentation} + */ + public function addPortType($name) + { + $portType = $this->_dom->createElement('portType'); + $portType->setAttribute('name', $name); + $this->_wsdl->appendChild($portType); + + return $portType; + } + + /** + * Add an {@link http://www.w3.org/TR/wsdl#_request-response operation} element to a portType element + * + * @param object $portType a portType XML_Tree_Node, from {@link function addPortType} + * @param string $name Operation name + * @param string $input Input Message + * @param string $output Output Message + * @param string $fault Fault Message + * @return object The new operation's XML_Tree_Node for use in {@link function addDocumentation} + */ + public function addPortOperation($portType, $name, $input = false, $output = false, $fault = false) + { + $operation = $this->_dom->createElement('operation'); + $operation->setAttribute('name', $name); + + if (is_string($input) && (strlen(trim($input)) >= 1)) { + $node = $this->_dom->createElement('input'); + $node->setAttribute('message', $input); + $operation->appendChild($node); + } + if (is_string($output) && (strlen(trim($output)) >= 1)) { + $node= $this->_dom->createElement('output'); + $node->setAttribute('message', $output); + $operation->appendChild($node); + } + if (is_string($fault) && (strlen(trim($fault)) >= 1)) { + $node = $this->_dom->createElement('fault'); + $node->setAttribute('message', $fault); + $operation->appendChild($node); + } + + $portType->appendChild($operation); + + return $operation; + } + + /** + * Add a {@link http://www.w3.org/TR/wsdl#_bindings binding} element to WSDL + * + * @param string $name Name of the Binding + * @param string $type name of the portType to bind + * @return object The new binding's XML_Tree_Node for use with {@link function addBindingOperation} and {@link function addDocumentation} + */ + public function addBinding($name, $portType) + { + $binding = $this->_dom->createElement('binding'); + $binding->setAttribute('name', $name); + $binding->setAttribute('type', $portType); + + $this->_wsdl->appendChild($binding); + + return $binding; + } + + /** + * Add an operation to a binding element + * + * @param object $binding A binding XML_Tree_Node returned by {@link function addBinding} + * @param array $input An array of attributes for the input element, allowed keys are: 'use', 'namespace', 'encodingStyle'. {@link http://www.w3.org/TR/wsdl#_soap:body More Information} + * @param array $output An array of attributes for the output element, allowed keys are: 'use', 'namespace', 'encodingStyle'. {@link http://www.w3.org/TR/wsdl#_soap:body More Information} + * @param array $fault An array of attributes for the fault element, allowed keys are: 'name', 'use', 'namespace', 'encodingStyle'. {@link http://www.w3.org/TR/wsdl#_soap:body More Information} + * @return object The new Operation's XML_Tree_Node for use with {@link function addSoapOperation} and {@link function addDocumentation} + */ + public function addBindingOperation($binding, $name, $input = false, $output = false, $fault = false) + { + $operation = $this->_dom->createElement('operation'); + $operation->setAttribute('name', $name); + + if (is_array($input)) { + $node = $this->_dom->createElement('input'); + $soap_node = $this->_dom->createElement('soap:body'); + foreach ($input as $name => $value) { + $soap_node->setAttribute($name, $value); + } + $node->appendChild($soap_node); + $operation->appendChild($node); + } + + if (is_array($output)) { + $node = $this->_dom->createElement('output'); + $soap_node = $this->_dom->createElement('soap:body'); + foreach ($output as $name => $value) { + $soap_node->setAttribute($name, $value); + } + $node->appendChild($soap_node); + $operation->appendChild($node); + } + + if (is_array($fault)) { + $node = $this->_dom->createElement('fault'); + if (isset($fault['name'])) { + $node->setAttribute('name', $fault['name']); + } + $soap_node = $this->_dom->createElement('soap:body'); + foreach ($output as $name => $value) { + $soap_node->setAttribute($name, $value); + } + $node->appendChild($soap_node); + $operation->appendChild($node); + } + + $binding->appendChild($operation); + + return $operation; + } + + /** + * Add a {@link http://www.w3.org/TR/wsdl#_soap:binding SOAP binding} element to a Binding element + * + * @param object $binding A binding XML_Tree_Node returned by {@link function addBinding} + * @param string $style binding style, possible values are "rpc" (the default) and "document" + * @param string $transport Transport method (defaults to HTTP) + * @return boolean + */ + public function addSoapBinding($binding, $style = 'document', $transport = 'http://schemas.xmlsoap.org/soap/http') + { + $soap_binding = $this->_dom->createElement('soap:binding'); + $soap_binding->setAttribute('style', $style); + $soap_binding->setAttribute('transport', $transport); + + $binding->appendChild($soap_binding); + + return $soap_binding; + } + + /** + * Add a {@link http://www.w3.org/TR/wsdl#_soap:operation SOAP operation} to an operation element + * + * @param object $operation An operation XML_Tree_Node returned by {@link function addBindingOperation} + * @param string $soap_action SOAP Action + * @return boolean + */ + public function addSoapOperation($binding, $soap_action) + { + if ($soap_action instanceof Zend_Uri_Http) { + $soap_action = $soap_action->getUri(); + } + $soap_operation = $this->_dom->createElement('soap:operation'); + $soap_operation->setAttribute('soapAction', $soap_action); + + $binding->insertBefore($soap_operation, $binding->firstChild); + + return $soap_operation; + } + + /** + * Add a {@link http://www.w3.org/TR/wsdl#_services service} element to the WSDL + * + * @param string $name Service Name + * @param string $port_name Name of the port for the service + * @param string $binding Binding for the port + * @param string $location SOAP Address for the service + * @return object The new service's XML_Tree_Node for use with {@link function addDocumentation} + */ + public function addService($name, $port_name, $binding, $location) + { + if ($location instanceof Zend_Uri_Http) { + $location = $location->getUri(); + } + $service = $this->_dom->createElement('service'); + $service->setAttribute('name', $name); + + $port = $this->_dom->createElement('port'); + $port->setAttribute('name', $port_name); + $port->setAttribute('binding', $binding); + + $soap_address = $this->_dom->createElement('soap:address'); + $soap_address->setAttribute('location', $location); + + $port->appendChild($soap_address); + $service->appendChild($port); + + $this->_wsdl->appendChild($service); + + return $service; + } + + /** + * Add a {@link http://www.w3.org/TR/wsdl#_documentation document} element to any element in the WSDL + * + * @param object $input_node An XML_Tree_Node returned by another method to add the document to + * @param string $document Human readable documentation for the node + * @return boolean + */ + public function addDocumentation($input_node, $documentation) + { + if ($input_node === $this) { + $node = $this->_dom->documentElement; + } else { + $node = $input_node; + } + + /** @todo Check if 'documentation' is a correct name for the element (WSDL spec uses 'document') */ + $doc = $this->_dom->createElement('documentation'); + $doc_cdata = $this->_dom->createTextNode($documentation); + $doc->appendChild($doc_cdata); + $node->appendChild($doc); + + return $doc; + } + + /** + * Add WSDL Types element + * + * @param object $types A DomDocument|DomNode|DomElement|DomDocumentFragment with all the XML Schema types defined in it + */ + public function addTypes($types) + { + if ($types instanceof DomDocument) { + $dom = $this->_dom->importNode($types->documentElement); + $this->_wsdl->appendChild($types->documentElement); + } elseif ($types instanceof DomNode || $types instanceof DomElement || $types instanceof DomDocumentFragment ) { + $dom = $this->_dom->importNode($types); + $this->_wsdl->appendChild($dom); + } + } + + /** + * Add a complex type name that is part of this WSDL and can be used in signatures. + * + * @param string $type + * @return Zend_Soap_Wsdl + */ + public function addType($type) + { + if(!in_array($type, $this->_includedTypes)) { + $this->_includedTypes[] = $type; + } + return $this; + } + + /** + * Return an array of all currently included complex types + * + * @return array + */ + public function getTypes() + { + return $this->_includedTypes; + } + + /** + * Return the Schema node of the WSDL + * + * @return DOMElement + */ + public function getSchema() + { + return $this->_schema; + } + + /** + * Return the WSDL as XML + * + * @return string WSDL as XML + */ + public function toXML() + { + return $this->_dom->saveXML(); + } + + /** + * Return DOM Document + * + * @return object DomDocum ent + */ + public function toDomDocument() + { + return $this->_dom; + } + + /** + * Echo the WSDL as XML + * + * @return boolean + */ + public function dump($filename = false) + { + if (!$filename) { + echo $this->toXML(); + return true; + } else { + return file_put_contents($filename, $this->toXML()); + } + } + + /** + * Returns an XSD Type for the given PHP type + * + * @param string $type PHP Type to get the XSD type for + * @return string + */ + public function getType($type) + { + switch (strtolower($type)) { + case 'string': + case 'str': + return 'xsd:string'; + break; + case 'int': + case 'integer': + return 'xsd:int'; + break; + case 'float': + case 'double': + return 'xsd:float'; + break; + case 'boolean': + case 'bool': + return 'xsd:boolean'; + break; + case 'array': + return 'soap-enc:Array'; + break; + case 'object': + return 'xsd:struct'; + break; + case 'mixed': + return 'xsd:anyType'; + break; + case 'void': + return ''; + default: + // delegate retrieval of complex type to current strategy + return $this->addComplexType($type); + } + } + + /** + * This function makes sure a complex types section and schema additions are set. + * + * @return Zend_Soap_Wsdl + */ + public function addSchemaTypeSection() + { + if ($this->_schema === null) { + $this->_schema = $this->_dom->createElement('xsd:schema'); + $this->_schema->setAttribute('targetNamespace', $this->_uri); + $types = $this->_dom->createElement('types'); + $types->appendChild($this->_schema); + $this->_wsdl->appendChild($types); + } + return $this; + } + + /** + * Add a {@link http://www.w3.org/TR/wsdl#_types types} data type definition + * + * @param string $type Name of the class to be specified + * @return string XSD Type for the given PHP type + */ + public function addComplexType($type) + { + if (in_array($type, $this->getTypes())) { + return "tns:$type"; + } + $this->addSchemaTypeSection(); + + $strategy = $this->getComplexTypeStrategy(); + $strategy->setContext($this); + // delegates the detection of a complex type to the current strategy + return $strategy->addComplexType($type); + } +} diff --git a/lib/zend/Zend/Soap/Wsdl/CodeGenerator.php b/lib/zend/Zend/Soap/Wsdl/CodeGenerator.php new file mode 100644 index 0000000000..5d22e6755e --- /dev/null +++ b/lib/zend/Zend/Soap/Wsdl/CodeGenerator.php @@ -0,0 +1,127 @@ +documentation)) { + $docs = self::$wsdl->documentation; + $docs = explode("\n", $docs); + $php_code .= "/**\n"; + foreach ($docs as $line) { + $php_code .= ' * ' .trim($line). PHP_EOL; + } + $php_code .= " */\n\n"; + } + if (!isset(self::$wsdl->name)) { + $classname = 'SoapService'; + } else { + $classname = self::$wsdl->name; + } + + $php_code .= "class {$classname} {\n"; + + foreach (self::$wsdl->operations as $name => $io) { + if (isset($io['documentation'])) { + $php_code .= "\n\t/**\n"; + $docs = $io['documentation']; + $docs = explode("\n", $docs); + foreach ($docs as $line) { + $php_code .= "\t * " .trim($line). PHP_EOL; + } + $php_code .= "\t */\n"; + } + $php_code .= "\n\tpublic function {$name} ("; + if (isset($io['input'])) { + $arg_names = array(); + foreach ($io['input'] as $arg) { + $arg_names[] = $arg['name']; + } + $php_code .= '$' .implode(', $', $arg_names); + } + $php_code .= ')'; + $php_code .= "\n\t{"; + $php_code .= "\n\t\t\n"; + if (isset($io['output'])) { + $php_code .= "\t\treturn \${$io['output']['name']};\n"; + } + $php_code .= "\t}\n"; + } + + $php_code .= "\n}"; + + $php_code .= PHP_EOL. "\$server = new SoapServer;" .PHP_EOL; + $php_code .= "\$server->setClass($classname);"; + $php_code .= "\n?>"; + return $php_code; + } +} + diff --git a/lib/zend/Zend/Soap/Wsdl/Exception.php b/lib/zend/Zend/Soap/Wsdl/Exception.php new file mode 100644 index 0000000000..a0dbe211d8 --- /dev/null +++ b/lib/zend/Zend/Soap/Wsdl/Exception.php @@ -0,0 +1,25 @@ +asXML()); + + if (isset(self::$xml->documentation)) { + $wsdl_result->documentation = trim(self::$xml->documentation); + } + if (!isset(self::$xml['name'])) { + $wsdl_result->name = null; + } else { + $wsdl_result->name = (string) self::$xml['name']; + } + + foreach (self::$xml->binding->operation as $operation) { + $name = (string) $operation['name']; + $wsdl_result->operations[$name] = array(); + $wsdl_result->operations[$name]['input'] = self::getOperationInputs($name); + $wsdl_result->operations[$name]['output'] = self::getOperationOutput($name); + $wsdl_result->operations[$name]['documentation'] = self::getDocs($name); + } + + $wsdl_result->portType = (string) self::$xml->portType['name']; + $wsdl_result->binding = (string) self::$xml->binding['name']; + $wsdl_result->service['name'] = (string) self::$xml->service['name']; + $wsdl_result->service['address'] = (string) self::$xml->service->port->children('http://schemas.xmlsoap.org/wsdl/soap/')->attributes(); + $wsdl_result->targetNamespace = (string) self::$xml['targetNamespace']; + + return $wsdl_result; + } + + /** + * Get Function arguments + * + * @param string $operation_name Name of the element to find + * @return string + */ + private static function getOperationInputs($operation_name) + { + $operation = self::$xml->xpath('/zfwsdl:definitions[1]/zfwsdl:portType/zfwsdl:operation[@name="' .$operation_name. '"]'); + + if ($operation == null) { + return ''; + } + + if (isset($operation[0]->input)) { + $input_message_name = $operation[0]->input['message']; + $input_message_name = explode(':', $input_message_name); + $input_message_name = $input_message_name[1]; + $input_message = self::$xml->xpath('/zfwsdl:definitions[1]/zfwsdl:message[@name="' .$input_message_name. '"]'); + } + + if ($input_message != null) { + foreach ($input_message[0]->part as $part) { + $args[] = array( + 'name' => (string) $part['name'], + 'type' => (string) $part['type'], + ); + } + + if (isset($args) && is_array($args)) { + return $args; + } else { + return null; + } + } else { + return null; + } + } + + /** + * Get Function return variable + * + * @param string $operation_name Name of the element to find + * @return string|false Returns variable name if found, or false + */ + private static function getOperationOutput($operation_name) + { + $operation = self::$xml->xpath('/zfwsdl:definitions[1]/zfwsdl:portType/zfwsdl:operation[@name="' .$operation_name. '"]'); + + + if (isset($operation[0]->output)) { + $output_message_name = $operation[0]->output['message']; + $output_message_name = explode(':', $output_message_name); + $output_message_name = $output_message_name[1]; + $output_message = self::$xml->xpath('/zfwsdl:definitions[1]/zfwsdl:message[@name="' .$output_message_name. '"]'); + } + + if ($output_message != null) { + return array( + 'name' => (string) $output_message[0]->part['name'], + 'type' => (string) $output_message[0]->part['type'] + ); + } else { + return null; + } + } + + /** + * Get Function Documentation + * + * @param string $operation_name Name of the element to find + * @return string + */ + private static function getDocs($operation_name) + { + + $portType = self::$xml->xpath('//zfwsdl:operation[@name="' .$operation_name. '"]/zfwsdl:documentation'); + if (isset($portType) && is_array($portType) && (sizeof($portType) >= 1)) { + return trim((string) $portType[0]); + } else { + return null; + } + } +} + + diff --git a/lib/zend/Zend/Soap/Wsdl/Parser/Result.php b/lib/zend/Zend/Soap/Wsdl/Parser/Result.php new file mode 100644 index 0000000000..b0cfacfd49 --- /dev/null +++ b/lib/zend/Zend/Soap/Wsdl/Parser/Result.php @@ -0,0 +1,52 @@ +wsdl_file = $wsdl; + } +} + + diff --git a/lib/zend/Zend/Soap/Wsdl/Strategy/Abstract.php b/lib/zend/Zend/Soap/Wsdl/Strategy/Abstract.php new file mode 100644 index 0000000000..3aaaff9721 --- /dev/null +++ b/lib/zend/Zend/Soap/Wsdl/Strategy/Abstract.php @@ -0,0 +1,27 @@ +_context = $context; + } + + /** + * Return the current Zend_Soap_Wsdl context object + * + * @return Zend_Soap_Wsdl + */ + public function getContext() + { + return $this->_context; + } +} diff --git a/lib/zend/Zend/Soap/Wsdl/Strategy/AnyType.php b/lib/zend/Zend/Soap/Wsdl/Strategy/AnyType.php new file mode 100644 index 0000000000..fe6062bf40 --- /dev/null +++ b/lib/zend/Zend/Soap/Wsdl/Strategy/AnyType.php @@ -0,0 +1,45 @@ +_getNestedCount($type); + + if($nestingLevel > 1) { + require_once "Zend/Soap/Wsdl/Exception.php"; + throw new Zend_Soap_Wsdl_Exception( + "ArrayOfTypeComplex cannot return nested ArrayOfObject deeper than ". + "one level. Use array object properties to return deep nested data. + "); + } + + $singularType = $this->_getSingularPhpType($type); + + if(!class_exists($singularType)) { + require_once "Zend/Soap/Wsdl/Exception.php"; + throw new Zend_Soap_Wsdl_Exception(sprintf( + "Cannot add a complex type %s that is not an object or where ". + "class could not be found in 'DefaultComplexType' strategy.", $type + )); + } + + if($nestingLevel == 1) { + // The following blocks define the Array of Object structure + $xsdComplexTypeName = $this->_addArrayOfComplexType($singularType, $type); + } else { + $xsdComplexTypeName = $singularType; + } + + // The array for the objects has been created, now build the object definition: + if(!in_array($singularType, $this->getContext()->getTypes())) { + parent::addComplexType($singularType); + } + + return "tns:".$xsdComplexTypeName; + } + + protected function _addArrayOfComplexType($singularType, $type) + { + $dom = $this->getContext()->toDomDocument(); + + $xsdComplexTypeName = $this->_getXsdComplexTypeName($singularType); + + if(!in_array($xsdComplexTypeName, $this->getContext()->getTypes())) { + $complexType = $dom->createElement('xsd:complexType'); + $complexType->setAttribute('name', $xsdComplexTypeName); + + $complexContent = $dom->createElement("xsd:complexContent"); + $complexType->appendChild($complexContent); + + $xsdRestriction = $dom->createElement("xsd:restriction"); + $xsdRestriction->setAttribute('base', 'soap-enc:Array'); + $complexContent->appendChild($xsdRestriction); + + $xsdAttribute = $dom->createElement("xsd:attribute"); + $xsdAttribute->setAttribute("ref", "soap-enc:arrayType"); + $xsdAttribute->setAttribute("wsdl:arrayType", sprintf("tns:%s[]", $singularType)); + $xsdRestriction->appendChild($xsdAttribute); + + $this->getContext()->getSchema()->appendChild($complexType); + $this->getContext()->addType($xsdComplexTypeName); + } + + return $xsdComplexTypeName; + } + + protected function _getXsdComplexTypeName($type) + { + return sprintf('ArrayOf%s', $type); + } + + /** + * From a nested defintion with type[], get the singular PHP Type + * + * @param string $type + * @return string + */ + protected function _getSingularPhpType($type) + { + return str_replace("[]", "", $type); + } + + /** + * Return the array nesting level based on the type name + * + * @param string $type + * @return integer + */ + protected function _getNestedCount($type) + { + return substr_count($type, "[]"); + } +} \ No newline at end of file diff --git a/lib/zend/Zend/Soap/Wsdl/Strategy/ArrayOfTypeSequence.php b/lib/zend/Zend/Soap/Wsdl/Strategy/ArrayOfTypeSequence.php new file mode 100644 index 0000000000..e05e3b9bdb --- /dev/null +++ b/lib/zend/Zend/Soap/Wsdl/Strategy/ArrayOfTypeSequence.php @@ -0,0 +1,148 @@ +_getNestedCount($type); + + if($nestedCounter > 0) { + $singularType = $this->_getSingularType($type); + + for($i = 1; $i <= $nestedCounter; $i++) { + $complexTypeName = $this->_getTypeNameBasedOnNestingLevel($singularType, $i); + $childTypeName = $this->_getTypeNameBasedOnNestingLevel($singularType, $i-1); + + $this->_addElementFromWsdlAndChildTypes($complexTypeName, $childTypeName); + } + // adding the PHP type which is resolved to a nested XSD type. therefore add only once. + $this->getContext()->addType($complexTypeName); + + return "tns:$complexTypeName"; + } else { + require_once "Zend/Soap/Wsdl/Exception.php"; + throw new Zend_Soap_Wsdl_Exception(sprintf( + 'ArrayOfTypeSequence Strategy does not allow for complex types that are not in @return type[] syntax. "%s" type was specified.', $type + )); + } + } + + /** + * Return the ArrayOf or simple type name based on the singular xsdtype and the nesting level + * + * @param string $singularType + * @param int $level + * @return string + */ + protected function _getTypeNameBasedOnNestingLevel($singularType, $level) + { + if($level == 0) { + // This is not an Array anymore, return the xsd simple type + return $singularType; + } else { + $prefix = str_repeat("ArrayOf", $level); + $xsdType = $this->_getStrippedXsdType($singularType); + $arrayType = $prefix.$xsdType; + return $arrayType; + } + } + + /** + * Strip the xsd: from a singularType and Format it nice for ArrayOf naming + * + * @param string $singularType + * @return string + */ + protected function _getStrippedXsdType($singularType) + { + return ucfirst(substr(strtolower($singularType), 4)); + } + + /** + * From a nested defintion with type[], get the singular xsd:type + * + * @throws Zend_Soap_Wsdl_Exception When no xsd:simpletype can be detected. + * @param string $type + * @return string + */ + protected function _getSingularType($type) + { + $singulartype = $this->getContext()->getType(str_replace("[]", "", $type)); + + if(substr($singulartype, 0, 4) != "xsd:") { + require_once "Zend/Soap/Wsdl/Exception.php"; + throw new Zend_Soap_Wsdl_Exception(sprintf( + 'ArrayOfTypeSequence Strategy works only with arrays of simple types like int, string, boolean, not with "%s".'. + 'You may use Zend_Soap_Wsdl_Strategy_ArrayOfTypeComplex for more complex types.', $type + )); + } + return $singulartype; + } + + /** + * Return the array nesting level based on the type name + * + * @param string $type + * @return integer + */ + protected function _getNestedCount($type) + { + return substr_count($type, "[]"); + } + + /** + * Append the complex type definition to the WSDL via the context access + * + * @param string $arrayType + * @param string $childTypeName + * @return void + */ + protected function _addElementFromWsdlAndChildTypes($arrayType, $childTypeName) + { + if (!in_array($arrayType, $this->getContext()->getTypes())) { + $dom = $this->getContext()->toDomDocument(); + + $complexType = $dom->createElement('xsd:complexType'); + $complexType->setAttribute('name', $arrayType); + + $sequence = $dom->createElement('xsd:sequence'); + + $element = $dom->createElement('xsd:element'); + $element->setAttribute('name', 'item'); + $element->setAttribute('type', $childTypeName); + $element->setAttribute('minOccurs', 0); + $element->setAttribute('maxOccurs', 'unbounded'); + $sequence->appendChild($element); + + $complexType->appendChild($sequence); + + $this->getContext()->getSchema()->appendChild($complexType); + $this->getContext()->addType($arrayType); + } + } +} \ No newline at end of file diff --git a/lib/zend/Zend/Soap/Wsdl/Strategy/DefaultComplexType.php b/lib/zend/Zend/Soap/Wsdl/Strategy/DefaultComplexType.php new file mode 100644 index 0000000000..3bf086c68a --- /dev/null +++ b/lib/zend/Zend/Soap/Wsdl/Strategy/DefaultComplexType.php @@ -0,0 +1,69 @@ +getContext()->toDomDocument(); + $class = new ReflectionClass($type); + + $complexType = $dom->createElement('xsd:complexType'); + $complexType->setAttribute('name', $type); + + $all = $dom->createElement('xsd:all'); + + foreach ($class->getProperties() as $property) { + if (preg_match_all('/@var\s+([^\s]+)/m', $property->getDocComment(), $matches)) { + + /** + * @todo check if 'xsd:element' must be used here (it may not be compatible with using 'complexType' + * node for describing other classes used as attribute types for current class + */ + $element = $dom->createElement('xsd:element'); + $element->setAttribute('name', $property->getName()); + $element->setAttribute('type', $this->getContext()->getType(trim($matches[1][0]))); + $all->appendChild($element); + } + } + + $complexType->appendChild($all); + $this->getContext()->getSchema()->appendChild($complexType); + $this->getContext()->addType($type); + + return "tns:$type"; + } +} diff --git a/lib/zend/Zend/Soap/Wsdl/Strategy/Interface.php b/lib/zend/Zend/Soap/Wsdl/Strategy/Interface.php new file mode 100644 index 0000000000..d2bf917bb4 --- /dev/null +++ b/lib/zend/Zend/Soap/Wsdl/Strategy/Interface.php @@ -0,0 +1,35 @@ +_httpClient = new Zend_Http_Client(); + } else { + $this->_httpClient = $httpClient; + } + + $this->_introspector = new Zend_XmlRpc_Client_ServerIntrospection($this); + $this->_serverAddress = $server; + } + + + /** + * Sets the HTTP client object to use for connecting the XML-RPC server. + * + * @param Zend_Http_Client $httpClient + * @return Zend_Http_Client + */ + public function setHttpClient(Zend_Http_Client $httpClient) + { + return $this->_httpClient = $httpClient; + } + + + /** + * Gets the HTTP client object. + * + * @return Zend_Http_Client + */ + public function getHttpClient() + { + return $this->_httpClient; + } + + + /** + * Sets the object used to introspect remote servers + * + * @param Zend_XmlRpc_Client_ServerIntrospection + * @return Zend_XmlRpc_Client_ServerIntrospection + */ + public function setIntrospector(Zend_XmlRpc_Client_ServerIntrospection $introspector) + { + return $this->_introspector = $introspector; + } + + + /** + * Gets the introspection object. + * + * @return Zend_XmlRpc_Client_ServerIntrospection + */ + public function getIntrospector() + { + return $this->_introspector; + } + + + /** + * The request of the last method call + * + * @return Zend_XmlRpc_Request + */ + public function getLastRequest() + { + return $this->_lastRequest; + } + + + /** + * The response received from the last method call + * + * @return Zend_XmlRpc_Response + */ + public function getLastResponse() + { + return $this->_lastResponse; + } + + + /** + * Returns a proxy object for more convenient method calls + * + * @param $namespace Namespace to proxy or empty string for none + * @return Zend_XmlRpc_Client_ServerProxy + */ + public function getProxy($namespace = '') + { + if (empty($this->_proxyCache[$namespace])) { + $proxy = new Zend_XmlRpc_Client_ServerProxy($this, $namespace); + $this->_proxyCache[$namespace] = $proxy; + } + return $this->_proxyCache[$namespace]; + } + + /** + * Set skip system lookup flag + * + * @param bool $flag + * @return Zend_XmlRpc_Client + */ + public function setSkipSystemLookup($flag = true) + { + $this->_skipSystemLookup = (bool) $flag; + return $this; + } + + /** + * Skip system lookup when determining if parameter should be array or struct? + * + * @return bool + */ + public function skipSystemLookup() + { + return $this->_skipSystemLookup; + } + + /** + * Perform an XML-RPC request and return a response. + * + * @param Zend_XmlRpc_Request $request + * @param null|Zend_XmlRpc_Response $response + * @return void + * @throws Zend_XmlRpc_Client_HttpException + */ + public function doRequest($request, $response = null) + { + $this->_lastRequest = $request; + + iconv_set_encoding('input_encoding', 'UTF-8'); + iconv_set_encoding('output_encoding', 'UTF-8'); + iconv_set_encoding('internal_encoding', 'UTF-8'); + + $http = $this->getHttpClient(); + if($http->getUri() === null) { + $http->setUri($this->_serverAddress); + } + + $http->setHeaders(array( + 'Content-Type: text/xml; charset=utf-8', + 'User-Agent: Zend_XmlRpc_Client', + 'Accept: text/xml', + )); + + $xml = $this->_lastRequest->__toString(); + $http->setRawData($xml); + $httpResponse = $http->request(Zend_Http_Client::POST); + + if (! $httpResponse->isSuccessful()) { + /** + * Exception thrown when an HTTP error occurs + * @see Zend_XmlRpc_Client_HttpException + */ + require_once 'Zend/XmlRpc/Client/HttpException.php'; + throw new Zend_XmlRpc_Client_HttpException( + $httpResponse->getMessage(), + $httpResponse->getStatus()); + } + + if ($response === null) { + $response = new Zend_XmlRpc_Response(); + } + $this->_lastResponse = $response; + $this->_lastResponse->loadXml($httpResponse->getBody()); + } + + /** + * Send an XML-RPC request to the service (for a specific method) + * + * @param string $method Name of the method we want to call + * @param array $params Array of parameters for the method + * @throws Zend_XmlRpc_Client_FaultException + */ + public function call($method, $params=array()) + { + if (!$this->skipSystemLookup() && ('system.' != substr($method, 0, 7))) { + // Ensure empty array/struct params are cast correctly + // If system.* methods are not available, bypass. (ZF-2978) + $success = true; + try { + $signatures = $this->getIntrospector()->getMethodSignature($method); + } catch (Zend_XmlRpc_Exception $e) { + $success = false; + } + if ($success) { + foreach ($params as $key => $param) { + if (is_array($param) && empty($param)) { + $type = 'array'; + foreach ($signatures as $signature) { + if (!is_array($signature)) { + continue; + } + if (array_key_exists($key + 1, $signature)) { + $type = $signature[$key + 1]; + $type = (in_array($type, array('array', 'struct'))) ? $type : 'array'; + break; + } + } + $params[$key] = array( + 'type' => $type, + 'value' => $param + ); + } + } + } + } + + $request = new Zend_XmlRpc_Request($method, $params); + + $this->doRequest($request); + + if ($this->_lastResponse->isFault()) { + $fault = $this->_lastResponse->getFault(); + /** + * Exception thrown when an XML-RPC fault is returned + * @see Zend_XmlRpc_Client_FaultException + */ + require_once 'Zend/XmlRpc/Client/FaultException.php'; + throw new Zend_XmlRpc_Client_FaultException($fault->getMessage(), + $fault->getCode()); + } + + return $this->_lastResponse->getReturnValue(); + } +} diff --git a/lib/zend/Zend/XmlRpc/Client/Exception.php b/lib/zend/Zend/XmlRpc/Client/Exception.php new file mode 100644 index 0000000000..266fe4da5d --- /dev/null +++ b/lib/zend/Zend/XmlRpc/Client/Exception.php @@ -0,0 +1,39 @@ +_system = $client->getProxy('system'); + } + + /** + * Returns the signature for each method on the server, + * autodetecting whether system.multicall() is supported and + * using it if so. + * + * @return array + */ + public function getSignatureForEachMethod() + { + $methods = $this->listMethods(); + + try { + $signatures = $this->getSignatureForEachMethodByMulticall($methods); + } catch (Zend_XmlRpc_Client_FaultException $e) { + // degrade to looping + } + + if (empty($signatures)) { + $signatures = $this->getSignatureForEachMethodByLooping($methods); + } + + return $signatures; + } + + /** + * Attempt to get the method signatures in one request via system.multicall(). + * This is a boxcar feature of XML-RPC and is found on fewer servers. However, + * can significantly improve performance if present. + * + * @param array $methods + * @return array array(array(return, param, param, param...)) + */ + public function getSignatureForEachMethodByMulticall($methods = null) + { + if ($methods === null) { + $methods = $this->listMethods(); + } + + $multicallParams = array(); + foreach ($methods as $method) { + $multicallParams[] = array('methodName' => 'system.methodSignature', + 'params' => array($method)); + } + + $serverSignatures = $this->_system->multicall($multicallParams); + + if (! is_array($serverSignatures)) { + $type = gettype($serverSignatures); + $error = "Multicall return is malformed. Expected array, got $type"; + throw new Zend_XmlRpc_Client_IntrospectException($error); + } + + if (count($serverSignatures) != count($methods)) { + $error = 'Bad number of signatures received from multicall'; + throw new Zend_XmlRpc_Client_IntrospectException($error); + } + + // Create a new signatures array with the methods name as keys and the signature as value + $signatures = array(); + foreach ($serverSignatures as $i => $signature) { + $signatures[$methods[$i]] = $signature; + } + + return $signatures; + } + + /** + * Get the method signatures for every method by + * successively calling system.methodSignature + * + * @param array $methods + * @return array + */ + public function getSignatureForEachMethodByLooping($methods = null) + { + if ($methods === null) { + $methods = $this->listMethods(); + } + + $signatures = array(); + foreach ($methods as $method) { + $signatures[$method] = $this->getMethodSignature($method); + } + + return $signatures; + } + + /** + * Call system.methodSignature() for the given method + * + * @param array $method + * @return array array(array(return, param, param, param...)) + */ + public function getMethodSignature($method) + { + $signature = $this->_system->methodSignature($method); + return $signature; + } + + /** + * Call system.listMethods() + * + * @param array $method + * @return array array(method, method, method...) + */ + public function listMethods() + { + return $this->_system->listMethods(); + } + +} diff --git a/lib/zend/Zend/XmlRpc/Client/ServerProxy.php b/lib/zend/Zend/XmlRpc/Client/ServerProxy.php new file mode 100644 index 0000000000..521c1aa609 --- /dev/null +++ b/lib/zend/Zend/XmlRpc/Client/ServerProxy.php @@ -0,0 +1,94 @@ +foo->bar->baz()". + * + * @category Zend + * @package Zend_XmlRpc + * @subpackage Client + * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +class Zend_XmlRpc_Client_ServerProxy +{ + /** + * @var Zend_XmlRpc_Client + */ + private $_client = null; + + /** + * @var string + */ + private $_namespace = ''; + + + /** + * @var array of Zend_XmlRpc_Client_ServerProxy + */ + private $_cache = array(); + + + /** + * Class constructor + * + * @param string $namespace + * @param Zend_XmlRpc_Client $client + */ + public function __construct($client, $namespace = '') + { + $this->_namespace = $namespace; + $this->_client = $client; + } + + + /** + * Get the next successive namespace + * + * @param string $name + * @return Zend_XmlRpc_Client_ServerProxy + */ + public function __get($namespace) + { + $namespace = ltrim("$this->_namespace.$namespace", '.'); + if (!isset($this->_cache[$namespace])) { + $this->_cache[$namespace] = new $this($this->_client, $namespace); + } + return $this->_cache[$namespace]; + } + + + /** + * Call a method in this namespace. + * + * @param string $methodN + * @param array $args + * @return mixed + */ + public function __call($method, $args) + { + $method = ltrim("$this->_namespace.$method", '.'); + return $this->_client->call($method, $args); + } +} diff --git a/lib/zend/Zend/XmlRpc/Exception.php b/lib/zend/Zend/XmlRpc/Exception.php new file mode 100644 index 0000000000..48031f51a7 --- /dev/null +++ b/lib/zend/Zend/XmlRpc/Exception.php @@ -0,0 +1,36 @@ + messages + * @var array + */ + protected $_internal = array( + 404 => 'Unknown Error', + + // 610 - 619 reflection errors + 610 => 'Invalid method class', + 611 => 'Unable to attach function or callback; not callable', + 612 => 'Unable to load array; not an array', + 613 => 'One or more method records are corrupt or otherwise unusable', + + // 620 - 629 dispatch errors + 620 => 'Method does not exist', + 621 => 'Error instantiating class to invoke method', + 622 => 'Method missing implementation', + 623 => 'Calling parameters do not match signature', + + // 630 - 639 request errors + 630 => 'Unable to read request', + 631 => 'Failed to parse request', + 632 => 'Invalid request, no method passed; request must contain a \'methodName\' tag', + 633 => 'Param must contain a value', + 634 => 'Invalid method name', + 635 => 'Invalid XML provided to request', + 636 => 'Error creating xmlrpc value', + + // 640 - 649 system.* errors + 640 => 'Method does not exist', + + // 650 - 659 response errors + 650 => 'Invalid XML provided for response', + 651 => 'Failed to parse response', + 652 => 'Invalid response', + 653 => 'Invalid XMLRPC value in response', + ); + + /** + * Constructor + * + * @return Zend_XmlRpc_Fault + */ + public function __construct($code = 404, $message = '') + { + $this->setCode($code); + $code = $this->getCode(); + + if (empty($message) && isset($this->_internal[$code])) { + $message = $this->_internal[$code]; + } elseif (empty($message)) { + $message = 'Unknown error'; + } + $this->setMessage($message); + } + + /** + * Set the fault code + * + * @param int $code + * @return Zend_XmlRpc_Fault + */ + public function setCode($code) + { + $this->_code = (int) $code; + return $this; + } + + /** + * Return fault code + * + * @return int + */ + public function getCode() + { + return $this->_code; + } + + /** + * Retrieve fault message + * + * @param string + * @return Zend_XmlRpc_Fault + */ + public function setMessage($message) + { + $this->_message = (string) $message; + return $this; + } + + /** + * Retrieve fault message + * + * @return string + */ + public function getMessage() + { + return $this->_message; + } + + /** + * Set encoding to use in fault response + * + * @param string $encoding + * @return Zend_XmlRpc_Fault + */ + public function setEncoding($encoding) + { + $this->_encoding = $encoding; + return $this; + } + + /** + * Retrieve current fault encoding + * + * @return string + */ + public function getEncoding() + { + return $this->_encoding; + } + + /** + * Load an XMLRPC fault from XML + * + * @param string $fault + * @return boolean Returns true if successfully loaded fault response, false + * if response was not a fault response + * @throws Zend_XmlRpc_Exception if no or faulty XML provided, or if fault + * response does not contain either code or message + */ + public function loadXml($fault) + { + if (!is_string($fault)) { + throw new Zend_XmlRpc_Exception('Invalid XML provided to fault'); + } + + try { + $xml = @new SimpleXMLElement($fault); + } catch (Exception $e) { + // Not valid XML + throw new Zend_XmlRpc_Exception('Failed to parse XML fault: ' . $e->getMessage(), 500); + } + + // Check for fault + if (!$xml->fault) { + // Not a fault + return false; + } + + if (!$xml->fault->value->struct) { + // not a proper fault + throw new Zend_XmlRpc_Exception('Invalid fault structure', 500); + } + + $structXml = $xml->fault->value->asXML(); + $structXml = preg_replace('/<\?xml version=.*?\?>/i', '', $structXml); + $struct = Zend_XmlRpc_Value::getXmlRpcValue(trim($structXml), Zend_XmlRpc_Value::XML_STRING); + $struct = $struct->getValue(); + + if (isset($struct['faultCode'])) { + $code = $struct['faultCode']; + } + if (isset($struct['faultString'])) { + $message = $struct['faultString']; + } + + if (empty($code) && empty($message)) { + throw new Zend_XmlRpc_Exception('Fault code and string required'); + } + + if (empty($code)) { + $code = '404'; + } + + if (empty($message)) { + if (isset($this->_internal[$code])) { + $message = $this->_internal[$code]; + } else { + $message = 'Unknown Error'; + } + } + + $this->setCode($code); + $this->setMessage($message); + + return true; + } + + /** + * Determine if an XML response is an XMLRPC fault + * + * @param string $xml + * @return boolean + */ + public static function isFault($xml) + { + $fault = new self(); + try { + $isFault = $fault->loadXml($xml); + } catch (Zend_XmlRpc_Exception $e) { + $isFault = false; + } + + return $isFault; + } + + /** + * Serialize fault to XML + * + * @return string + */ + public function saveXML() + { + // Create fault value + $faultStruct = array( + 'faultCode' => $this->getCode(), + 'faultString' => $this->getMessage() + ); + $value = Zend_XmlRpc_Value::getXmlRpcValue($faultStruct); + $valueDOM = new DOMDocument('1.0', $this->getEncoding()); + $valueDOM->loadXML($value->saveXML()); + + // Build response XML + $dom = new DOMDocument('1.0', $this->getEncoding()); + $r = $dom->appendChild($dom->createElement('methodResponse')); + $f = $r->appendChild($dom->createElement('fault')); + $f->appendChild($dom->importNode($valueDOM->documentElement, 1)); + + return $dom->saveXML(); + } + + /** + * Return XML fault response + * + * @return string + */ + public function __toString() + { + return $this->saveXML(); + } +} diff --git a/lib/zend/Zend/XmlRpc/Request.php b/lib/zend/Zend/XmlRpc/Request.php new file mode 100644 index 0000000000..dbb62f5a6b --- /dev/null +++ b/lib/zend/Zend/XmlRpc/Request.php @@ -0,0 +1,443 @@ +setMethod($method); + } + + if ($params !== null) { + $this->setParams($params); + } + } + + + /** + * Set encoding to use in request + * + * @param string $encoding + * @return Zend_XmlRpc_Request + */ + public function setEncoding($encoding) + { + $this->_encoding = $encoding; + return $this; + } + + /** + * Retrieve current request encoding + * + * @return string + */ + public function getEncoding() + { + return $this->_encoding; + } + + /** + * Set method to call + * + * @param string $method + * @return boolean Returns true on success, false if method name is invalid + */ + public function setMethod($method) + { + if (!is_string($method) || !preg_match('/^[a-z0-9_.:\/]+$/i', $method)) { + $this->_fault = new Zend_XmlRpc_Fault(634, 'Invalid method name ("' . $method . '")'); + $this->_fault->setEncoding($this->getEncoding()); + return false; + } + + $this->_method = $method; + return true; + } + + /** + * Retrieve call method + * + * @return string + */ + public function getMethod() + { + return $this->_method; + } + + /** + * Add a parameter to the parameter stack + * + * Adds a parameter to the parameter stack, associating it with the type + * $type if provided + * + * @param mixed $value + * @param string $type Optional; type hinting + * @return void + */ + public function addParam($value, $type = null) + { + $this->_params[] = $value; + if (null === $type) { + // Detect type if not provided explicitly + if ($value instanceof Zend_XmlRpc_Value) { + $type = $value->getType(); + } else { + $xmlRpcValue = Zend_XmlRpc_Value::getXmlRpcValue($value); + $type = $xmlRpcValue->getType(); + } + } + $this->_types[] = $type; + $this->_xmlRpcParams[] = array('value' => $value, 'type' => $type); + } + + /** + * Set the parameters array + * + * If called with a single, array value, that array is used to set the + * parameters stack. If called with multiple values or a single non-array + * value, the arguments are used to set the parameters stack. + * + * Best is to call with array of the format, in order to allow type hinting + * when creating the XMLRPC values for each parameter: + * + * $array = array( + * array( + * 'value' => $value, + * 'type' => $type + * )[, ... ] + * ); + * + * + * @access public + * @return void + */ + public function setParams() + { + $argc = func_num_args(); + $argv = func_get_args(); + if (0 == $argc) { + return; + } + + if ((1 == $argc) && is_array($argv[0])) { + $params = array(); + $types = array(); + $wellFormed = true; + foreach ($argv[0] as $arg) { + if (!is_array($arg) || !isset($arg['value'])) { + $wellFormed = false; + break; + } + $params[] = $arg['value']; + + if (!isset($arg['type'])) { + $xmlRpcValue = Zend_XmlRpc_Value::getXmlRpcValue($arg['value']); + $arg['type'] = $xmlRpcValue->getType(); + } + $types[] = $arg['type']; + } + if ($wellFormed) { + $this->_xmlRpcParams = $argv[0]; + $this->_params = $params; + $this->_types = $types; + } else { + $this->_params = $argv[0]; + $this->_types = array(); + $xmlRpcParams = array(); + foreach ($argv[0] as $arg) { + if ($arg instanceof Zend_XmlRpc_Value) { + $type = $arg->getType(); + } else { + $xmlRpcValue = Zend_XmlRpc_Value::getXmlRpcValue($arg); + $type = $xmlRpcValue->getType(); + } + $xmlRpcParams[] = array('value' => $arg, 'type' => $type); + $this->_types[] = $type; + } + $this->_xmlRpcParams = $xmlRpcParams; + } + return; + } + + $this->_params = $argv; + $this->_types = array(); + $xmlRpcParams = array(); + foreach ($argv as $arg) { + if ($arg instanceof Zend_XmlRpc_Value) { + $type = $arg->getType(); + } else { + $xmlRpcValue = Zend_XmlRpc_Value::getXmlRpcValue($arg); + $type = $xmlRpcValue->getType(); + } + $xmlRpcParams[] = array('value' => $arg, 'type' => $type); + $this->_types[] = $type; + } + $this->_xmlRpcParams = $xmlRpcParams; + } + + /** + * Retrieve the array of parameters + * + * @return array + */ + public function getParams() + { + return $this->_params; + } + + /** + * Return parameter types + * + * @return array + */ + public function getTypes() + { + return $this->_types; + } + + /** + * Load XML and parse into request components + * + * @param string $request + * @return boolean True on success, false if an error occurred. + */ + public function loadXml($request) + { + if (!is_string($request)) { + $this->_fault = new Zend_XmlRpc_Fault(635); + $this->_fault->setEncoding($this->getEncoding()); + return false; + } + + try { + $xml = @new SimpleXMLElement($request); + } catch (Exception $e) { + // Not valid XML + $this->_fault = new Zend_XmlRpc_Fault(631); + $this->_fault->setEncoding($this->getEncoding()); + return false; + } + + // Check for method name + if (empty($xml->methodName)) { + // Missing method name + $this->_fault = new Zend_XmlRpc_Fault(632); + $this->_fault->setEncoding($this->getEncoding()); + return false; + } + + $this->_method = (string) $xml->methodName; + + // Check for parameters + if (!empty($xml->params)) { + $types = array(); + $argv = array(); + foreach ($xml->params->children() as $param) { + if (! $param->value instanceof SimpleXMLElement) { + $this->_fault = new Zend_XmlRpc_Fault(633); + $this->_fault->setEncoding($this->getEncoding()); + return false; + } + + try { + $param = Zend_XmlRpc_Value::getXmlRpcValue($param->value, Zend_XmlRpc_Value::XML_STRING); + $types[] = $param->getType(); + $argv[] = $param->getValue(); + } catch (Exception $e) { + $this->_fault = new Zend_XmlRpc_Fault(636); + $this->_fault->setEncoding($this->getEncoding()); + return false; + } + } + + $this->_types = $types; + $this->_params = $argv; + } + + $this->_xml = $request; + + return true; + } + + /** + * Does the current request contain errors and should it return a fault + * response? + * + * @return boolean + */ + public function isFault() + { + return $this->_fault instanceof Zend_XmlRpc_Fault; + } + + /** + * Retrieve the fault response, if any + * + * @return null|Zend_XmlRpc_Fault + */ + public function getFault() + { + return $this->_fault; + } + + /** + * Retrieve method parameters as XMLRPC values + * + * @return array + */ + protected function _getXmlRpcParams() + { + $params = array(); + if (is_array($this->_xmlRpcParams)) { + foreach ($this->_xmlRpcParams as $param) { + $value = $param['value']; + $type = isset($param['type']) ? $param['type'] : Zend_XmlRpc_Value::AUTO_DETECT_TYPE; + + if (!$value instanceof Zend_XmlRpc_Value) { + $value = Zend_XmlRpc_Value::getXmlRpcValue($value, $type); + } + $params[] = $value; + } + } + + return $params; + } + + /** + * Create XML request + * + * @return string + */ + public function saveXML() + { + $args = $this->_getXmlRpcParams(); + $method = $this->getMethod(); + + $dom = new DOMDocument('1.0', $this->getEncoding()); + $mCall = $dom->appendChild($dom->createElement('methodCall')); + $mName = $mCall->appendChild($dom->createElement('methodName', $method)); + + if (is_array($args) && count($args)) { + $params = $mCall->appendChild($dom->createElement('params')); + + foreach ($args as $arg) { + /* @var $arg Zend_XmlRpc_Value */ + $argDOM = new DOMDocument('1.0', $this->getEncoding()); + $argDOM->loadXML($arg->saveXML()); + + $param = $params->appendChild($dom->createElement('param')); + $param->appendChild($dom->importNode($argDOM->documentElement, 1)); + } + } + + return $dom->saveXML(); + } + + /** + * Return XML request + * + * @return string + */ + public function __toString() + { + return $this->saveXML(); + } +} diff --git a/lib/zend/Zend/XmlRpc/Request/Http.php b/lib/zend/Zend/XmlRpc/Request/Http.php new file mode 100644 index 0000000000..d77fe5503b --- /dev/null +++ b/lib/zend/Zend/XmlRpc/Request/Http.php @@ -0,0 +1,129 @@ +_fault = new Zend_XmlRpc_Server_Exception(630); + return; + } + + $xml = ''; + while (!feof($fh)) { + $xml .= fgets($fh); + } + fclose($fh); + + $this->_xml = $xml; + + $this->loadXml($xml); + } + + /** + * Retrieve the raw XML request + * + * @return string + */ + public function getRawRequest() + { + return $this->_xml; + } + + /** + * Get headers + * + * Gets all headers as key => value pairs and returns them. + * + * @return array + */ + public function getHeaders() + { + if (null === $this->_headers) { + $this->_headers = array(); + foreach ($_SERVER as $key => $value) { + if ('HTTP_' == substr($key, 0, 5)) { + $header = str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($key, 5))))); + $this->_headers[$header] = $value; + } + } + } + + return $this->_headers; + } + + /** + * Retrieve the full HTTP request, including headers and XML + * + * @return string + */ + public function getFullRequest() + { + $request = ''; + foreach ($this->getHeaders() as $key => $value) { + $request .= $key . ': ' . $value . "\n"; + } + + $request .= $this->_xml; + + return $request; + } +} diff --git a/lib/zend/Zend/XmlRpc/Request/Stdin.php b/lib/zend/Zend/XmlRpc/Request/Stdin.php new file mode 100644 index 0000000000..e152b3b965 --- /dev/null +++ b/lib/zend/Zend/XmlRpc/Request/Stdin.php @@ -0,0 +1,84 @@ +_fault = new Zend_XmlRpc_Server_Exception(630); + return; + } + + $xml = ''; + while (!feof($fh)) { + $xml .= fgets($fh); + } + fclose($fh); + + $this->_xml = $xml; + + $this->loadXml($xml); + } + + /** + * Retrieve the raw XML request + * + * @return string + */ + public function getRawRequest() + { + return $this->_xml; + } +} diff --git a/lib/zend/Zend/XmlRpc/Response.php b/lib/zend/Zend/XmlRpc/Response.php new file mode 100644 index 0000000000..c6b59a3e8c --- /dev/null +++ b/lib/zend/Zend/XmlRpc/Response.php @@ -0,0 +1,249 @@ +setReturnValue($return, $type); + } + + /** + * Set encoding to use in response + * + * @param string $encoding + * @return Zend_XmlRpc_Response + */ + public function setEncoding($encoding) + { + $this->_encoding = $encoding; + return $this; + } + + /** + * Retrieve current response encoding + * + * @return string + */ + public function getEncoding() + { + return $this->_encoding; + } + + /** + * Set the return value + * + * Sets the return value, with optional type hinting if provided. + * + * @param mixed $value + * @param string $type + * @return void + */ + public function setReturnValue($value, $type = null) + { + $this->_return = $value; + $this->_type = (string) $type; + } + + /** + * Retrieve the return value + * + * @return mixed + */ + public function getReturnValue() + { + return $this->_return; + } + + /** + * Retrieve the XMLRPC value for the return value + * + * @return Zend_XmlRpc_Value + */ + protected function _getXmlRpcReturn() + { + return Zend_XmlRpc_Value::getXmlRpcValue($this->_return); + } + + /** + * Is the response a fault response? + * + * @return boolean + */ + public function isFault() + { + return $this->_fault instanceof Zend_XmlRpc_Fault; + } + + /** + * Returns the fault, if any. + * + * @return null|Zend_XmlRpc_Fault + */ + public function getFault() + { + return $this->_fault; + } + + /** + * Load a response from an XML response + * + * Attempts to load a response from an XMLRPC response, autodetecting if it + * is a fault response. + * + * @param string $response + * @return boolean True if a valid XMLRPC response, false if a fault + * response or invalid input + */ + public function loadXml($response) + { + if (!is_string($response)) { + $this->_fault = new Zend_XmlRpc_Fault(650); + $this->_fault->setEncoding($this->getEncoding()); + return false; + } + + try { + $xml = @new SimpleXMLElement($response); + } catch (Exception $e) { + // Not valid XML + $this->_fault = new Zend_XmlRpc_Fault(651); + $this->_fault->setEncoding($this->getEncoding()); + return false; + } + + if (!empty($xml->fault)) { + // fault response + $this->_fault = new Zend_XmlRpc_Fault(); + $this->_fault->setEncoding($this->getEncoding()); + $this->_fault->loadXml($response); + return false; + } + + if (empty($xml->params)) { + // Invalid response + $this->_fault = new Zend_XmlRpc_Fault(652); + $this->_fault->setEncoding($this->getEncoding()); + return false; + } + + try { + if (!isset($xml->params) || !isset($xml->params->param) || !isset($xml->params->param->value)) { + throw new Zend_XmlRpc_Value_Exception('Missing XML-RPC value in XML'); + } + $valueXml = $xml->params->param->value->asXML(); + $valueXml = preg_replace('/<\?xml version=.*?\?>/i', '', $valueXml); + $value = Zend_XmlRpc_Value::getXmlRpcValue(trim($valueXml), Zend_XmlRpc_Value::XML_STRING); + } catch (Zend_XmlRpc_Value_Exception $e) { + $this->_fault = new Zend_XmlRpc_Fault(653); + $this->_fault->setEncoding($this->getEncoding()); + return false; + } + + $this->setReturnValue($value->getValue()); + return true; + } + + /** + * Return response as XML + * + * @return string + */ + public function saveXML() + { + $value = $this->_getXmlRpcReturn(); + $valueDOM = new DOMDocument('1.0', $this->getEncoding()); + $valueDOM->loadXML($value->saveXML()); + + $dom = new DOMDocument('1.0', $this->getEncoding()); + $response = $dom->appendChild($dom->createElement('methodResponse')); + $params = $response->appendChild($dom->createElement('params')); + $param = $params->appendChild($dom->createElement('param')); + + $param->appendChild($dom->importNode($valueDOM->documentElement, true)); + + return $dom->saveXML(); + } + + /** + * Return XML response + * + * @return string + */ + public function __toString() + { + return $this->saveXML(); + } +} diff --git a/lib/zend/Zend/XmlRpc/Response/Http.php b/lib/zend/Zend/XmlRpc/Response/Http.php new file mode 100644 index 0000000000..76938320c6 --- /dev/null +++ b/lib/zend/Zend/XmlRpc/Response/Http.php @@ -0,0 +1,51 @@ +getEncoding())); + } + + return parent::__toString(); + } +} diff --git a/lib/zend/Zend/XmlRpc/Server.php b/lib/zend/Zend/XmlRpc/Server.php new file mode 100644 index 0000000000..4c4dae8b08 --- /dev/null +++ b/lib/zend/Zend/XmlRpc/Server.php @@ -0,0 +1,581 @@ + + * require_once 'Zend/XmlRpc/Server.php'; + * require_once 'Zend/XmlRpc/Server/Cache.php'; + * require_once 'Zend/XmlRpc/Server/Fault.php'; + * require_once 'My/Exception.php'; + * require_once 'My/Fault/Observer.php'; + * + * // Instantiate server + * $server = new Zend_XmlRpc_Server(); + * + * // Allow some exceptions to report as fault responses: + * Zend_XmlRpc_Server_Fault::attachFaultException('My_Exception'); + * Zend_XmlRpc_Server_Fault::attachObserver('My_Fault_Observer'); + * + * // Get or build dispatch table: + * if (!Zend_XmlRpc_Server_Cache::get($filename, $server)) { + * require_once 'Some/Service/Class.php'; + * require_once 'Another/Service/Class.php'; + * + * // Attach Some_Service_Class in 'some' namespace + * $server->setClass('Some_Service_Class', 'some'); + * + * // Attach Another_Service_Class in 'another' namespace + * $server->setClass('Another_Service_Class', 'another'); + * + * // Create dispatch table cache file + * Zend_XmlRpc_Server_Cache::save($filename, $server); + * } + * + * $response = $server->handle(); + * echo $response; + * + * + * @category Zend + * @package Zend_XmlRpc + * @subpackage Server + * @copyright Copyright (c) 2005-2008 Zend Technologies USA Inc. (http://www.zend.com) + * @license http://framework.zend.com/license/new-bsd New BSD License + */ +class Zend_XmlRpc_Server extends Zend_Server_Abstract +{ + /** + * Character encoding + * @var string + */ + protected $_encoding = 'UTF-8'; + + /** + * Request processed + * @var null|Zend_XmlRpc_Request + */ + protected $_request = null; + + /** + * Class to use for responses; defaults to {@link Zend_XmlRpc_Response_Http} + * @var string + */ + protected $_responseClass = 'Zend_XmlRpc_Response_Http'; + + /** + * Dispatch table of name => method pairs + * @var Zend_XmlRpc_Server_ServerDefinition + */ + protected $_table; + + /** + * PHP types => XML-RPC types + * @var array + */ + protected $_typeMap = array( + 'i4' => 'i4', + 'int' => 'int', + 'integer' => 'int', + 'double' => 'double', + 'float' => 'double', + 'real' => 'double', + 'boolean' => 'boolean', + 'bool' => 'boolean', + 'true' => 'boolean', + 'false' => 'boolean', + 'string' => 'string', + 'str' => 'string', + 'base64' => 'base64', + 'dateTime.iso8601' => 'dateTime.iso8601', + 'date' => 'dateTime.iso8601', + 'time' => 'dateTime.iso8601', + 'time' => 'dateTime.iso8601', + 'array' => 'array', + 'struct' => 'struct', + 'null' => 'nil', + 'nil' => 'nil', + 'void' => 'void', + 'mixed' => 'struct' + ); + + /** + * Constructor + * + * Creates system.* methods. + * + * @return void + */ + public function __construct() + { + $this->_table = new Zend_Server_Definition(); + $this->_registerSystemMethods(); + } + + /** + * Proxy calls to system object + * + * @param string $method + * @param array $params + * @return mixed + * @throws Zend_XmlRpc_Server_Exception + */ + public function __call($method, $params) + { + $system = $this->getSystem(); + if (!method_exists($system, $method)) { + throw new Zend_XmlRpc_Server_Exception('Unknown instance method called on server: ' . $method); + } + return call_user_func_array(array($system, $method), $params); + } + + /** + * Attach a callback as an XMLRPC method + * + * Attaches a callback as an XMLRPC method, prefixing the XMLRPC method name + * with $namespace, if provided. Reflection is done on the callback's + * docblock to create the methodHelp for the XMLRPC method. + * + * Additional arguments to pass to the function at dispatch may be passed; + * any arguments following the namespace will be aggregated and passed at + * dispatch time. + * + * @param string|array $function Valid callback + * @param string $namespace Optional namespace prefix + * @return void + * @throws Zend_XmlRpc_Server_Exception + */ + public function addFunction($function, $namespace = '') + { + if (!is_string($function) && !is_array($function)) { + throw new Zend_XmlRpc_Server_Exception('Unable to attach function; invalid', 611); + } + + $argv = null; + if (2 < func_num_args()) { + $argv = func_get_args(); + $argv = array_slice($argv, 2); + } + + $function = (array) $function; + foreach ($function as $func) { + if (!is_string($func) || !function_exists($func)) { + throw new Zend_XmlRpc_Server_Exception('Unable to attach function; invalid', 611); + } + $reflection = Zend_Server_Reflection::reflectFunction($func, $argv, $namespace); + $this->_buildSignature($reflection); + } + } + + /** + * Attach class methods as XMLRPC method handlers + * + * $class may be either a class name or an object. Reflection is done on the + * class or object to determine the available public methods, and each is + * attached to the server as an available method; if a $namespace has been + * provided, that namespace is used to prefix the XMLRPC method names. + * + * Any additional arguments beyond $namespace will be passed to a method at + * invocation. + * + * @param string|object $class + * @param string $namespace Optional + * @param mixed $argv Optional arguments to pass to methods + * @return void + * @throws Zend_XmlRpc_Server_Exception on invalid input + */ + public function setClass($class, $namespace = '', $argv = null) + { + if (is_string($class) && !class_exists($class)) { + if (!class_exists($class)) { + throw new Zend_XmlRpc_Server_Exception('Invalid method class', 610); + } + } + + $argv = null; + if (3 < func_num_args()) { + $argv = func_get_args(); + $argv = array_slice($argv, 3); + } + + $dispatchable = Zend_Server_Reflection::reflectClass($class, $argv, $namespace); + foreach ($dispatchable->getMethods() as $reflection) { + $this->_buildSignature($reflection, $class); + } + } + + /** + * Raise an xmlrpc server fault + * + * @param string|Exception $fault + * @param int $code + * @return Zend_XmlRpc_Server_Fault + */ + public function fault($fault = null, $code = 404) + { + if (!$fault instanceof Exception) { + $fault = (string) $fault; + if (empty($fault)) { + $fault = 'Unknown error'; + } + $fault = new Zend_XmlRpc_Server_Exception($fault, $code); + } + + return Zend_XmlRpc_Server_Fault::getInstance($fault); + } + + /** + * Handle an xmlrpc call + * + * @param Zend_XmlRpc_Request $request Optional + * @return Zend_XmlRpc_Response|Zend_XmlRpc_Fault + */ + public function handle($request = false) + { + // Get request + if ((!$request || !$request instanceof Zend_XmlRpc_Request) + && (null === ($request = $this->getRequest())) + ) { + require_once 'Zend/XmlRpc/Request/Http.php'; + $request = new Zend_XmlRpc_Request_Http(); + $request->setEncoding($this->getEncoding()); + } + + $this->setRequest($request); + + if ($request->isFault()) { + $response = $request->getFault(); + } else { + try { + $response = $this->_handle($request); + } catch (Exception $e) { + $response = $this->fault($e); + } + } + + // Set output encoding + $response->setEncoding($this->getEncoding()); + + return $response; + } + + /** + * Load methods as returned from {@link getFunctions} + * + * Typically, you will not use this method; it will be called using the + * results pulled from {@link Zend_XmlRpc_Server_Cache::get()}. + * + * @param array|Zend_Server_Definition $definition + * @return void + * @throws Zend_XmlRpc_Server_Exception on invalid input + */ + public function loadFunctions($definition) + { + if (!is_array($definition) && (!$definition instanceof Zend_Server_Definition)) { + if (is_object($definition)) { + $type = get_class($definition); + } else { + $type = gettype($definition); + } + throw new Zend_XmlRpc_Server_Exception('Unable to load server definition; must be an array or Zend_Server_Definition, received ' . $type, 612); + } + + $this->_table->clearMethods(); + $this->_registerSystemMethods(); + + if ($definition instanceof Zend_Server_Definition) { + $definition = $definition->getMethods(); + } + + foreach ($definition as $key => $method) { + if ('system.' == substr($key, 0, 7)) { + continue; + } + $this->_table->addMethod($method, $key); + } + } + + /** + * Set encoding + * + * @param string $encoding + * @return Zend_XmlRpc_Server + */ + public function setEncoding($encoding) + { + $this->_encoding = $encoding; + return $this; + } + + /** + * Retrieve current encoding + * + * @return string + */ + public function getEncoding() + { + return $this->_encoding; + } + + /** + * Do nothing; persistence is handled via {@link Zend_XmlRpc_Server_Cache} + * + * @param mixed $mode + * @return void + */ + public function setPersistence($mode) + { + } + + /** + * Set the request object + * + * @param string|Zend_XmlRpc_Request $request + * @return Zend_XmlRpc_Server + * @throws Zend_XmlRpc_Server_Exception on invalid request class or object + */ + public function setRequest($request) + { + if (is_string($request) && class_exists($request)) { + $request = new $request(); + if (!$request instanceof Zend_XmlRpc_Request) { + throw new Zend_XmlRpc_Server_Exception('Invalid request class'); + } + $request->setEncoding($this->getEncoding()); + } elseif (!$request instanceof Zend_XmlRpc_Request) { + throw new Zend_XmlRpc_Server_Exception('Invalid request object'); + } + + $this->_request = $request; + return $this; + } + + /** + * Return currently registered request object + * + * @return null|Zend_XmlRpc_Request + */ + public function getRequest() + { + return $this->_request; + } + + /** + * Set the class to use for the response + * + * @param string $class + * @return boolean True if class was set, false if not + */ + public function setResponseClass($class) + { + if (class_exists($class)) { + $reflection = new ReflectionClass($class); + if ($reflection->isSubclassOf(new ReflectionClass('Zend_XmlRpc_Response'))) { + $this->_responseClass = $class; + return true; + } + } + + return false; + } + + /** + * Retrieve current response class + * + * @return string + */ + public function getResponseClass() + { + return $this->_responseClass; + } + + /** + * Retrieve dispatch table + * + * @return array + */ + public function getDispatchTable() + { + return $this->_table; + } + + /** + * Returns a list of registered methods + * + * Returns an array of dispatchables (Zend_Server_Reflection_Function, + * _Method, and _Class items). + * + * @return array + */ + public function getFunctions() + { + return $this->_table->toArray(); + } + + /** + * Retrieve system object + * + * @return Zend_XmlRpc_Server_System + */ + public function getSystem() + { + return $this->_system; + } + + /** + * Map PHP type to XML-RPC type + * + * @param string $type + * @return string + */ + protected function _fixType($type) + { + if (isset($this->_typeMap[$type])) { + return $this->_typeMap[$type]; + } + return 'void'; + } + + /** + * Handle an xmlrpc call (actual work) + * + * @param Zend_XmlRpc_Request $request + * @return Zend_XmlRpc_Response + * @throws Zend_XmlRpcServer_Exception|Exception + * Zend_XmlRpcServer_Exceptions are thrown for internal errors; otherwise, + * any other exception may be thrown by the callback + */ + protected function _handle(Zend_XmlRpc_Request $request) + { + $method = $request->getMethod(); + + // Check for valid method + if (!$this->_table->hasMethod($method)) { + throw new Zend_XmlRpc_Server_Exception('Method "' . $method . '" does not exist', 620); + } + + $info = $this->_table->getMethod($method); + $params = $request->getParams(); + $argv = $info->getInvokeArguments(); + if (0 < count($argv)) { + $params = array_merge($params, $argv); + } + + // Check calling parameters against signatures + $matched = false; + $sigCalled = $request->getTypes(); + + $sigLength = count($sigCalled); + $paramsLen = count($params); + if ($sigLength < $paramsLen) { + for ($i = $sigLength; $i < $paramsLen; ++$i) { + $xmlRpcValue = Zend_XmlRpc_Value::getXmlRpcValue($params[$i]); + $sigCalled[] = $xmlRpcValue->getType(); + } + } + + $signatures = $info->getPrototypes(); + foreach ($signatures as $signature) { + $sigParams = $signature->getParameters(); + if ($sigCalled === $sigParams) { + $matched = true; + break; + } + } + if (!$matched) { + throw new Zend_XmlRpc_Server_Exception('Calling parameters do not match signature', 623); + } + + $return = $this->_dispatch($info, $params); + $responseClass = $this->getResponseClass(); + return new $responseClass($return); + } + + /** + * Register system methods with the server + * + * @return void + */ + protected function _registerSystemMethods() + { + $system = new Zend_XmlRpc_Server_System($this); + $this->_system = $system; + $this->setClass($system, 'system'); + } +} diff --git a/lib/zend/Zend/XmlRpc/Server/Cache.php b/lib/zend/Zend/XmlRpc/Server/Cache.php new file mode 100644 index 0000000000..59c3e469f5 --- /dev/null +++ b/lib/zend/Zend/XmlRpc/Server/Cache.php @@ -0,0 +1,46 @@ + true); + + /** + * @var array Array of fault observers + */ + protected static $_observers = array(); + + /** + * Constructor + * + * @param Exception $e + * @return Zend_XmlRpc_Server_Fault + */ + public function __construct(Exception $e) + { + $this->_exception = $e; + $code = 404; + $message = 'Unknown error'; + $exceptionClass = get_class($e); + + foreach (array_keys(self::$_faultExceptionClasses) as $class) { + if ($e instanceof $class) { + $code = $e->getCode(); + $message = $e->getMessage(); + break; + } + } + + parent::__construct($code, $message); + + // Notify exception observers, if present + if (!empty(self::$_observers)) { + foreach (array_keys(self::$_observers) as $observer) { + call_user_func(array($observer, 'observe'), $this); + } + } + } + + /** + * Return Zend_XmlRpc_Server_Fault instance + * + * @param Exception $e + * @return Zend_XmlRpc_Server_Fault + */ + public static function getInstance(Exception $e) + { + return new self($e); + } + + /** + * Attach valid exceptions that can be used to define xmlrpc faults + * + * @param string|array $classes Class name or array of class names + * @return void + */ + public static function attachFaultException($classes) + { + if (!is_array($classes)) { + $classes = (array) $classes; + } + + foreach ($classes as $class) { + if (is_string($class) && class_exists($class)) { + self::$_faultExceptionClasses[$class] = true; + } + } + } + + /** + * Detach fault exception classes + * + * @param string|array $classes Class name or array of class names + * @return void + */ + public static function detachFaultException($classes) + { + if (!is_array($classes)) { + $classes = (array) $classes; + } + + foreach ($classes as $class) { + if (is_string($class) && isset(self::$_faultExceptionClasses[$class])) { + unset(self::$_faultExceptionClasses[$class]); + } + } + } + + /** + * Attach an observer class + * + * Allows observation of xmlrpc server faults, thus allowing logging or mail + * notification of fault responses on the xmlrpc server. + * + * Expects a valid class name; that class must have a public static method + * 'observe' that accepts an exception as its sole argument. + * + * @param string $class + * @return boolean + */ + public static function attachObserver($class) + { + if (!is_string($class) + || !class_exists($class) + || !is_callable(array($class, 'observe'))) + { + return false; + } + + if (!isset(self::$_observers[$class])) { + self::$_observers[$class] = true; + } + + return true; + } + + /** + * Detach an observer + * + * @param string $class + * @return boolean + */ + public static function detachObserver($class) + { + if (!isset(self::$_observers[$class])) { + return false; + } + + unset(self::$_observers[$class]); + return true; + } + + /** + * Retrieve the exception + * + * @access public + * @return Exception + */ + public function getException() + { + return $this->_exception; + } +} diff --git a/lib/zend/Zend/XmlRpc/Server/System.php b/lib/zend/Zend/XmlRpc/Server/System.php new file mode 100644 index 0000000000..b1e7cb91ad --- /dev/null +++ b/lib/zend/Zend/XmlRpc/Server/System.php @@ -0,0 +1,149 @@ +_server = $server; + } + + /** + * List all available XMLRPC methods + * + * Returns an array of methods. + * + * @return array + */ + public function listMethods() + { + $table = $this->_server->getDispatchTable()->getMethods(); + return array_keys($table); + } + + /** + * Display help message for an XMLRPC method + * + * @param string $method + * @return string + */ + public function methodHelp($method) + { + $table = $this->_server->getDispatchTable(); + if (!$table->hasMethod($method)) { + throw new Zend_Server_Exception('Method "' . $method . '"does not exist', 640); + } + + return $table->getMethod($method)->getMethodHelp(); + } + + /** + * Return a method signature + * + * @param string $method + * @return array + */ + public function methodSignature($method) + { + $table = $this->_server->getDispatchTable(); + if (!$table->hasMethod($method)) { + throw new Zend_Server_Exception('Method "' . $method . '"does not exist', 640); + } + $method = $table->getMethod($method)->toArray(); + return $method['prototypes']; + } + + /** + * Multicall - boxcar feature of XML-RPC for calling multiple methods + * in a single request. + * + * Expects a an array of structs representing method calls, each element + * having the keys: + * - methodName + * - params + * + * Returns an array of responses, one for each method called, with the value + * returned by the method. If an error occurs for a given method, returns a + * struct with a fault response. + * + * @see http://www.xmlrpc.com/discuss/msgReader$1208 + * @param array $methods + * @return array + */ + public function multicall($methods) + { + $responses = array(); + foreach ($methods as $method) { + $fault = false; + if (!is_array($method)) { + $fault = $this->_server->fault('system.multicall expects each method to be a struct', 601); + } elseif (!isset($method['methodName'])) { + $fault = $this->_server->fault('Missing methodName: ' . var_export($methods, 1), 602); + } elseif (!isset($method['params'])) { + $fault = $this->_server->fault('Missing params', 603); + } elseif (!is_array($method['params'])) { + $fault = $this->_server->fault('Params must be an array', 604); + } else { + if ('system.multicall' == $method['methodName']) { + // don't allow recursive calls to multicall + $fault = $this->_server->fault('Recursive system.multicall forbidden', 605); + } + } + + if (!$fault) { + try { + $request = new Zend_XmlRpc_Request(); + $request->setMethod($method['methodName']); + $request->setParams($method['params']); + $response = $this->_server->handle($request); + $responses[] = $response->getReturnValue(); + } catch (Exception $e) { + $fault = $this->_server->fault($e); + } + } + + if ($fault) { + $responses[] = array( + 'faultCode' => $fault->getCode(), + 'faultString' => $fault->getMessage() + ); + } + } + + return $responses; + } +} diff --git a/lib/zend/Zend/XmlRpc/Value.php b/lib/zend/Zend/XmlRpc/Value.php new file mode 100644 index 0000000000..6001d08eb6 --- /dev/null +++ b/lib/zend/Zend/XmlRpc/Value.php @@ -0,0 +1,401 @@ +_type; + } + + + /** + * Return the value of this object, convert the XML-RPC native value into a PHP variable + * + * @return mixed + */ + abstract public function getValue(); + + + /** + * Return the XML code that represent a native MXL-RPC value + * + * @return string + */ + abstract public function saveXML(); + + /** + * Return DOMElement representation of object + * + * @return DOMElement + */ + public function getAsDOM() + { + if (!$this->_as_dom) { + $doc = new DOMDocument('1.0'); + $doc->loadXML($this->saveXML()); + $this->_as_dom = $doc->documentElement; + } + + return $this->_as_dom; + } + + protected function _stripXmlDeclaration(DOMDocument $dom) + { + return preg_replace('/<\?xml version="1.0"( encoding="[^\"]*")?\?>\n/u', '', $dom->saveXML()); + } + + /** + * Creates a Zend_XmlRpc_Value* object, representing a native XML-RPC value + * A XmlRpcValue object can be created in 3 ways: + * 1. Autodetecting the native type out of a PHP variable + * (if $type is not set or equal to Zend_XmlRpc_Value::AUTO_DETECT_TYPE) + * 2. By specifing the native type ($type is one of the Zend_XmlRpc_Value::XMLRPC_TYPE_* constants) + * 3. From a XML string ($type is set to Zend_XmlRpc_Value::XML_STRING) + * + * By default the value type is autodetected according to it's PHP type + * + * @param mixed $value + * @param Zend_XmlRpc_Value::constant $type + * + * @return Zend_XmlRpc_Value + * @static + */ + public static function getXmlRpcValue($value, $type = self::AUTO_DETECT_TYPE) + { + switch ($type) { + case self::AUTO_DETECT_TYPE: + // Auto detect the XML-RPC native type from the PHP type of $value + return self::_phpVarToNativeXmlRpc($value); + + case self::XML_STRING: + // Parse the XML string given in $value and get the XML-RPC value in it + return self::_xmlStringToNativeXmlRpc($value); + + case self::XMLRPC_TYPE_I4: + // fall through to the next case + case self::XMLRPC_TYPE_INTEGER: + return new Zend_XmlRpc_Value_Integer($value); + + case self::XMLRPC_TYPE_DOUBLE: + return new Zend_XmlRpc_Value_Double($value); + + case self::XMLRPC_TYPE_BOOLEAN: + return new Zend_XmlRpc_Value_Boolean($value); + + case self::XMLRPC_TYPE_STRING: + return new Zend_XmlRpc_Value_String($value); + + case self::XMLRPC_TYPE_BASE64: + return new Zend_XmlRpc_Value_Base64($value); + + case self::XMLRPC_TYPE_NIL: + return new Zend_XmlRpc_Value_Nil(); + + case self::XMLRPC_TYPE_DATETIME: + return new Zend_XmlRpc_Value_DateTime($value); + + case self::XMLRPC_TYPE_ARRAY: + return new Zend_XmlRpc_Value_Array($value); + + case self::XMLRPC_TYPE_STRUCT: + return new Zend_XmlRpc_Value_Struct($value); + + default: + throw new Zend_XmlRpc_Value_Exception('Given type is not a '. __CLASS__ .' constant'); + } + } + + + /** + * Transform a PHP native variable into a XML-RPC native value + * + * @param mixed $value The PHP variable for convertion + * + * @return Zend_XmlRpc_Value + * @static + */ + private static function _phpVarToNativeXmlRpc($value) + { + switch (gettype($value)) { + case 'object': + // Check to see if it's an XmlRpc value + if ($value instanceof Zend_XmlRpc_Value) { + return $value; + } + + // Otherwise, we convert the object into a struct + $value = get_object_vars($value); + // Break intentionally omitted + case 'array': + // Default native type for a PHP array (a simple numeric array) is 'array' + $obj = 'Zend_XmlRpc_Value_Array'; + + // Determine if this is an associative array + if (!empty($value) && is_array($value) && (array_keys($value) !== range(0, count($value) - 1))) { + $obj = 'Zend_XmlRpc_Value_Struct'; + } + return new $obj($value); + + case 'integer': + return new Zend_XmlRpc_Value_Integer($value); + + case 'double': + return new Zend_XmlRpc_Value_Double($value); + + case 'boolean': + return new Zend_XmlRpc_Value_Boolean($value); + + case 'NULL': + case 'null': + return new Zend_XmlRpc_Value_Nil(); + + case 'string': + // Fall through to the next case + default: + // If type isn't identified (or identified as string), it treated as string + return new Zend_XmlRpc_Value_String($value); + } + } + + + /** + * Transform an XML string into a XML-RPC native value + * + * @param string|SimpleXMLElement $simple_xml A SimpleXMLElement object represent the XML string + * It can be also a valid XML string for convertion + * + * @return Zend_XmlRpc_Value + * @static + */ + private static function _xmlStringToNativeXmlRpc($simple_xml) + { + if (!$simple_xml instanceof SimpleXMLElement) { + try { + $simple_xml = @new SimpleXMLElement($simple_xml); + } catch (Exception $e) { + // The given string is not a valid XML + throw new Zend_XmlRpc_Value_Exception('Failed to create XML-RPC value from XML string: '.$e->getMessage(),$e->getCode()); + } + } + + // Get the key (tag name) and value from the simple xml object and convert the value to an XML-RPC native value + list($type, $value) = each($simple_xml); + if (!$type) { // If no type was specified, the default is string + $type = self::XMLRPC_TYPE_STRING; + } + + switch ($type) { + // All valid and known XML-RPC native values + case self::XMLRPC_TYPE_I4: + // Fall through to the next case + case self::XMLRPC_TYPE_INTEGER: + $xmlrpc_val = new Zend_XmlRpc_Value_Integer($value); + break; + case self::XMLRPC_TYPE_DOUBLE: + $xmlrpc_val = new Zend_XmlRpc_Value_Double($value); + break; + case self::XMLRPC_TYPE_BOOLEAN: + $xmlrpc_val = new Zend_XmlRpc_Value_Boolean($value); + break; + case self::XMLRPC_TYPE_STRING: + $xmlrpc_val = new Zend_XmlRpc_Value_String($value); + break; + case self::XMLRPC_TYPE_DATETIME: // The value should already be in a iso8601 format + $xmlrpc_val = new Zend_XmlRpc_Value_DateTime($value); + break; + case self::XMLRPC_TYPE_BASE64: // The value should already be base64 encoded + $xmlrpc_val = new Zend_XmlRpc_Value_Base64($value ,true); + break; + case self::XMLRPC_TYPE_NIL: // The value should always be NULL + $xmlrpc_val = new Zend_XmlRpc_Value_Nil(); + break; + case self::XMLRPC_TYPE_ARRAY: + // If the XML is valid, $value must be an SimpleXML element and contain the tag + if (!$value instanceof SimpleXMLElement) { + throw new Zend_XmlRpc_Value_Exception('XML string is invalid for XML-RPC native '. self::XMLRPC_TYPE_ARRAY .' type'); + } + + // PHP 5.2.4 introduced a regression in how empty($xml->value) + // returns; need to look for the item specifically + $data = null; + foreach ($value->children() as $key => $value) { + if ('data' == $key) { + $data = $value; + break; + } + } + + if (null === $data) { + throw new Zend_XmlRpc_Value_Exception('Invalid XML for XML-RPC native '. self::XMLRPC_TYPE_ARRAY .' type: ARRAY tag must contain DATA tag'); + } + $values = array(); + // Parse all the elements of the array from the XML string + // (simple xml element) to Zend_XmlRpc_Value objects + foreach ($data->value as $element) { + $values[] = self::_xmlStringToNativeXmlRpc($element); + } + $xmlrpc_val = new Zend_XmlRpc_Value_Array($values); + break; + case self::XMLRPC_TYPE_STRUCT: + // If the XML is valid, $value must be an SimpleXML + if ((!$value instanceof SimpleXMLElement)) { + throw new Zend_XmlRpc_Value_Exception('XML string is invalid for XML-RPC native '. self::XMLRPC_TYPE_STRUCT .' type'); + } + $values = array(); + // Parse all the memebers of the struct from the XML string + // (simple xml element) to Zend_XmlRpc_Value objects + foreach ($value->member as $member) { + // @todo? If a member doesn't have a tag, we don't add it to the struct + // Maybe we want to throw an exception here ? + if ((!$member->value instanceof SimpleXMLElement)) { + continue; + //throw new Zend_XmlRpc_Value_Exception('Member of the '. self::XMLRPC_TYPE_STRUCT .' XML-RPC native type must contain a VALUE tag'); + } + $values[(string)$member->name] = self::_xmlStringToNativeXmlRpc($member->value); + } + $xmlrpc_val = new Zend_XmlRpc_Value_Struct($values); + break; + default: + throw new Zend_XmlRpc_Value_Exception('Value type \''. $type .'\' parsed from the XML string is not a known XML-RPC native type'); + break; + } + $xmlrpc_val->_setXML($simple_xml->asXML()); + + return $xmlrpc_val; + } + + + private function _setXML($xml) + { + $this->_as_xml = $xml; + } + +} + + diff --git a/lib/zend/Zend/XmlRpc/Value/Array.php b/lib/zend/Zend/XmlRpc/Value/Array.php new file mode 100644 index 0000000000..c6d8bc6e7b --- /dev/null +++ b/lib/zend/Zend/XmlRpc/Value/Array.php @@ -0,0 +1,78 @@ +_type = self::XMLRPC_TYPE_ARRAY; + parent::__construct($value); + } + + + /** + * Return the XML code that represent an array native MXL-RPC value + * + * @return string + */ + public function saveXML() + { + if (!$this->_as_xml) { // The XML code was not calculated yet + $dom = new DOMDocument('1.0'); + $value = $dom->appendChild($dom->createElement('value')); + $array = $value->appendChild($dom->createElement('array')); + $data = $array->appendChild($dom->createElement('data')); + + if (is_array($this->_value)) { + foreach ($this->_value as $val) { + /* @var $val Zend_XmlRpc_Value */ + $data->appendChild($dom->importNode($val->getAsDOM(), true)); + } + } + + $this->_as_dom = $value; + $this->_as_xml = $this->_stripXmlDeclaration($dom); + } + + return $this->_as_xml; + } +} + diff --git a/lib/zend/Zend/XmlRpc/Value/Base64.php b/lib/zend/Zend/XmlRpc/Value/Base64.php new file mode 100644 index 0000000000..88bc6b10f0 --- /dev/null +++ b/lib/zend/Zend/XmlRpc/Value/Base64.php @@ -0,0 +1,89 @@ +_type = self::XMLRPC_TYPE_BASE64; + + $value = (string)$value; // Make sure this value is string + if (!$already_encoded) { + $value = base64_encode($value); // We encode it in base64 + } + $this->_value = $value; + } + + /** + * Return the value of this object, convert the XML-RPC native base64 value into a PHP string + * We return this value decoded (a normal string) + * + * @return string + */ + public function getValue() + { + return base64_decode($this->_value); + } + + /** + * Return the XML code representing the base64-encoded value + * + * @return string + */ + public function saveXML() + { + if (! $this->_as_xml) { // The XML was not generated yet + $dom = new DOMDocument('1.0', 'UTF-8'); + $value = $dom->appendChild($dom->createElement('value')); + $type = $value->appendChild($dom->createElement($this->_type)); + $type->appendChild($dom->createTextNode($this->_value)); + + $this->_as_dom = $value; + $this->_as_xml = $this->_stripXmlDeclaration($dom); + } + + return $this->_as_xml; + } +} + diff --git a/lib/zend/Zend/XmlRpc/Value/Boolean.php b/lib/zend/Zend/XmlRpc/Value/Boolean.php new file mode 100644 index 0000000000..76258c7b54 --- /dev/null +++ b/lib/zend/Zend/XmlRpc/Value/Boolean.php @@ -0,0 +1,84 @@ +_type = self::XMLRPC_TYPE_BOOLEAN; + // Make sure the value is boolean and then convert it into a integer + // The double convertion is because a bug in the ZendOptimizer in PHP version 5.0.4 + $this->_value = (int)(bool)$value; + } + + /** + * Return the value of this object, convert the XML-RPC native boolean value into a PHP boolean + * + * @return bool + */ + public function getValue() + { + return (bool)$this->_value; + } + + /** + * Return the XML-RPC serialization of the boolean value + * + * @return string + */ + public function saveXML() + { + if (! $this->_as_xml) { // The XML was not generated yet + $dom = new DOMDocument('1.0', 'UTF-8'); + $value = $dom->appendChild($dom->createElement('value')); + $type = $value->appendChild($dom->createElement($this->_type)); + $type->appendChild($dom->createTextNode($this->_value)); + + $this->_as_dom = $value; + $this->_as_xml = $this->_stripXmlDeclaration($dom); + } + + return $this->_as_xml; + } +} + diff --git a/lib/zend/Zend/XmlRpc/Value/Collection.php b/lib/zend/Zend/XmlRpc/Value/Collection.php new file mode 100644 index 0000000000..03cce31b0f --- /dev/null +++ b/lib/zend/Zend/XmlRpc/Value/Collection.php @@ -0,0 +1,79 @@ + $value) { + // If the elements of the given array are not Zend_XmlRpc_Value objects, + // we need to convert them as such (using auto-detection from PHP value) + if (!$value instanceof parent) { + $value = self::getXmlRpcValue($value, self::AUTO_DETECT_TYPE); + } + $this->_value[$key] = $value; + } + } + + + /** + * Return the value of this object, convert the XML-RPC native collection values into a PHP array + * + * @return arary + */ + public function getValue() + { + $values = (array)$this->_value; + foreach ($values as $key => $value) { + /* @var $value Zend_XmlRpc_Value */ + + if (!$value instanceof parent) { + throw new Zend_XmlRpc_Value_Exception('Values of '. get_class($this) .' type must be Zend_XmlRpc_Value objects'); + } + $values[$key] = $value->getValue(); + } + return $values; + } + +} + diff --git a/lib/zend/Zend/XmlRpc/Value/DateTime.php b/lib/zend/Zend/XmlRpc/Value/DateTime.php new file mode 100644 index 0000000000..b94eaf5bb4 --- /dev/null +++ b/lib/zend/Zend/XmlRpc/Value/DateTime.php @@ -0,0 +1,81 @@ +_type = self::XMLRPC_TYPE_DATETIME; + + // If the value is not numeric, we try to convert it to a timestamp (using the strtotime function) + if (is_numeric($value)) { // The value is numeric, we make sure it is an integer + $value = (int)$value; + } else { + $value = strtotime($value); + if ($value === false || $value == -1) { // cannot convert the value to a timestamp + throw new Zend_XmlRpc_Value_Exception('Cannot convert given value \''. $value .'\' to a timestamp'); + } + } + $value = date('c', $value); // Convert the timestamp to iso8601 format + + // Strip out TZ information and dashes + $value = preg_replace('/(\+|-)\d{2}:\d{2}$/', '', $value); + $value = str_replace('-', '', $value); + + $this->_value = $value; + } + + /** + * Return the value of this object as iso8601 dateTime value + * + * @return int As a Unix timestamp + */ + public function getValue() + { + return $this->_value; + } + +} + diff --git a/lib/zend/Zend/XmlRpc/Value/Double.php b/lib/zend/Zend/XmlRpc/Value/Double.php new file mode 100644 index 0000000000..48eaf5a0ce --- /dev/null +++ b/lib/zend/Zend/XmlRpc/Value/Double.php @@ -0,0 +1,62 @@ +_type = self::XMLRPC_TYPE_DOUBLE; + $this->_value = sprintf('%f',(float)$value); // Make sure this value is float (double) and without the scientific notation + } + + /** + * Return the value of this object, convert the XML-RPC native double value into a PHP float + * + * @return float + */ + public function getValue() + { + return (float)$this->_value; + } + +} + diff --git a/lib/zend/Zend/XmlRpc/Value/Exception.php b/lib/zend/Zend/XmlRpc/Value/Exception.php new file mode 100644 index 0000000000..6cc016b49e --- /dev/null +++ b/lib/zend/Zend/XmlRpc/Value/Exception.php @@ -0,0 +1,39 @@ +_type = self::XMLRPC_TYPE_INTEGER; + $this->_value = (int)$value; // Make sure this value is integer + } + + /** + * Return the value of this object, convert the XML-RPC native integer value into a PHP integer + * + * @return int + */ + public function getValue() + { + return $this->_value; + } + +} + diff --git a/lib/zend/Zend/XmlRpc/Value/Nil.php b/lib/zend/Zend/XmlRpc/Value/Nil.php new file mode 100644 index 0000000000..4edad72f0f --- /dev/null +++ b/lib/zend/Zend/XmlRpc/Value/Nil.php @@ -0,0 +1,79 @@ +_type = self::XMLRPC_TYPE_NIL; + $this->_value = null; + } + + /** + * Return the value of this object, convert the XML-RPC native nill value into a PHP NULL + * + * @return null + */ + public function getValue() + { + return null; + } + + /** + * Return the XML code representing the nil + * + * @return string + */ + public function saveXML() + { + if (! $this->_as_xml) { // The XML was not generated yet + $dom = new DOMDocument('1.0', 'UTF-8'); + $value = $dom->appendChild($dom->createElement('value')); + $type = $value->appendChild($dom->createElement($this->_type)); + + $this->_as_dom = $value; + $this->_as_xml = $this->_stripXmlDeclaration($dom); + } + + return $this->_as_xml; + } +} + diff --git a/lib/zend/Zend/XmlRpc/Value/Scalar.php b/lib/zend/Zend/XmlRpc/Value/Scalar.php new file mode 100644 index 0000000000..95eccdf786 --- /dev/null +++ b/lib/zend/Zend/XmlRpc/Value/Scalar.php @@ -0,0 +1,60 @@ +_as_xml) { // The XML code was not calculated yet + $dom = new DOMDocument('1.0'); + $value = $dom->appendChild($dom->createElement('value')); + $type = $value->appendChild($dom->createElement($this->_type)); + $type->appendChild($dom->createTextNode($this->getValue())); + + $this->_as_dom = $value; + $this->_as_xml = $this->_stripXmlDeclaration($dom); + } + + return $this->_as_xml; + } +} + diff --git a/lib/zend/Zend/XmlRpc/Value/String.php b/lib/zend/Zend/XmlRpc/Value/String.php new file mode 100644 index 0000000000..576c127666 --- /dev/null +++ b/lib/zend/Zend/XmlRpc/Value/String.php @@ -0,0 +1,74 @@ +_type = self::XMLRPC_TYPE_STRING; + + // Make sure this value is string and all XML characters are encoded + $this->_value = $this->_xml_entities($value); + } + + /** + * Return the value of this object, convert the XML-RPC native string value into a PHP string + * Decode all encoded risky XML entities back to normal characters + * + * @return string + */ + public function getValue() + { + return html_entity_decode($this->_value, ENT_QUOTES, 'UTF-8'); + } + + /** + * Make sure a string will be safe for XML, convert risky characters to HTML entities + * + * @param string $str + * @return string + */ + private function _xml_entities($str) + { + return htmlentities($str, ENT_QUOTES, 'UTF-8'); + } + +} + diff --git a/lib/zend/Zend/XmlRpc/Value/Struct.php b/lib/zend/Zend/XmlRpc/Value/Struct.php new file mode 100644 index 0000000000..865691a0f7 --- /dev/null +++ b/lib/zend/Zend/XmlRpc/Value/Struct.php @@ -0,0 +1,79 @@ +_type = self::XMLRPC_TYPE_STRUCT; + parent::__construct($value); + } + + + /** + * Return the XML code that represent struct native MXL-RPC value + * + * @return string + */ + public function saveXML() + { + if (!$this->_as_xml) { // The XML code was not calculated yet + $dom = new DOMDocument('1.0'); + $value = $dom->appendChild($dom->createElement('value')); + $struct = $value->appendChild($dom->createElement('struct')); + + if (is_array($this->_value)) { + foreach ($this->_value as $name => $val) { + /* @var $val Zend_XmlRpc_Value */ + $member = $struct->appendChild($dom->createElement('member')); + $member->appendChild($dom->createElement('name', $name)); + $member->appendChild($dom->importNode($val->getAsDOM(), 1)); + } + } + + $this->_as_dom = $value; + $this->_as_xml = $this->_stripXmlDeclaration($dom); + } + + return $this->_as_xml; + } +} + diff --git a/user/external.php b/user/external.php index f1e7739065..e8bf4bc9f3 100644 --- a/user/external.php +++ b/user/external.php @@ -42,19 +42,51 @@ final class user_external extends moodle_external { $this->descriptions['tmp_update_user'] = array( 'params' => array('username'=> PARAM_ALPHANUM, 'mnethostid'=> PARAM_NUMBER), 'optionalparams' => array( 'newusername' => PARAM_ALPHANUM, 'firstname' => PARAM_ALPHANUM), 'return' => array('result' => PARAM_BOOL)); + + $this->descriptions['tmp_do_multiple_user_searches'] = array( 'params' => array(array('search'=> PARAM_RAW)), + 'optionalparams' => array( ), + 'return' => array('user' => array('id' => PARAM_RAW, 'auth' => PARAM_RAW, 'confirmed' => PARAM_RAW, 'username' => PARAM_RAW, 'idnumber' => PARAM_RAW, + 'firstname' => PARAM_RAW, 'lastname' => PARAM_RAW, 'email' => PARAM_RAW, 'emailstop' => PARAM_RAW, + 'lang' => PARAM_RAW, 'theme' => PARAM_RAW, 'timezone' => PARAM_RAW, 'mailformat' => PARAM_RAW))); + + } + /** + * + * @global object $USER + * @param array|struct $params + * @return array + */ + + static function tmp_do_multiple_user_searches($params) { + global $USER; + if (has_capability('moodle/user:viewdetails', get_context_instance(CONTEXT_SYSTEM))) { + $users = array(); + foreach($params as $searchparams) { + $searchusers = get_users(true, $searchparams['search'], false, null, 'firstname ASC','', '', '', 1000, 'id, auth, confirmed, username, idnumber, firstname, lastname, email, emailstop, lang, theme, timezone, mailformat'); + foreach ($searchusers as $user) { + $users[] = $user; + } + } + return $users; + } + else { + throw new moodle_exception('wscouldnotvieweuser'); + } + } + /** * Retrieve all user - * @param array $params + * @param array|struct $params - need to be define as struct for XMLRPC * ->search string * @return object user */ static function tmp_get_users($params) { global $USER; if (has_capability('moodle/user:viewdetails', get_context_instance(CONTEXT_SYSTEM))) { - return get_users(true, $params['search'], false, null, 'firstname ASC','', '', '', '', 'id, auth, confirmed, username, idnumber, firstname, lastname, email, emailstop, lang, theme, timezone, mailformat'); - + // return "toto"; + return get_users(true, $params['search'], false, null, 'firstname ASC','', '', '', 1000, 'id, auth, confirmed, username, idnumber, firstname, lastname, email, emailstop, lang, theme, timezone, mailformat'); } else { throw new moodle_exception('wscouldnotvieweuser'); @@ -63,7 +95,7 @@ final class user_external extends moodle_external { /** * Create a user - * @param array $params + * @param array|struct $params - need to be define as struct for XMLRPC * ->firstname string * ->lastname string * ->email string @@ -89,7 +121,7 @@ final class user_external extends moodle_external { /** * Delete a user * @global object $DB - * @param array $params + * @param array|struct $params - need to be define as struct for XMLRPC * ->username string * ->mnethostid integer * @return boolean true if success @@ -109,12 +141,12 @@ final class user_external extends moodle_external { /** * Update some user information * @global object $DB - * @param array $params + * @param array|struct $params - need to be define as struct for XMLRPC * ->username string * ->mnethostid integer * ->newusername string * ->firstname string - * @return bool true if success + * @return string true if success */ static function tmp_update_user($params) { global $DB,$USER; diff --git a/webservice/amf/lib.php b/webservice/amf/lib.php new file mode 100644 index 0000000000..5451095390 --- /dev/null +++ b/webservice/amf/lib.php @@ -0,0 +1,61 @@ +set_protocolname("Amf"); + } + + /** + * Run the AMF server + */ + public function run() { + include "Zend/Loader.php"; + Zend_Loader::registerAutoload(); + + //retrieve the api name + $classpath = optional_param(classpath,'user',PARAM_ALPHA); + require_once(dirname(__FILE__) . '/../../'.$classpath.'/external.php'); + + /// run the Zend AMF server + $server = new Zend_Amf_Server(); + $server->setClass($classpath."_external"); + $response = $server->handle(); + echo $response; + } +} + + + +?> diff --git a/webservice/amf/server.php b/webservice/amf/server.php index 620e60c5fb..eeb9cf9f97 100644 --- a/webservice/amf/server.php +++ b/webservice/amf/server.php @@ -1,32 +1,39 @@ enablewebservices)) { die; } -/* - * FULL SERVER - * - */ -//retrieve the api name -$classpath = optional_param(classpath,'user',PARAM_ALPHA); -require_once(dirname(__FILE__) . '/../../'.$classpath.'/external.php'); - -/// run the server -$server = new Zend_Amf_Server(); -$server->setClass($classpath."_external"); -$response = $server->handle(); -echo $response; - +$server = new amf_server(); +$server->run(); ?> \ No newline at end of file diff --git a/webservice/lib.php b/webservice/lib.php new file mode 100644 index 0000000000..6615730a68 --- /dev/null +++ b/webservice/lib.php @@ -0,0 +1,145 @@ + $CFG + * @return + */ + public static function get_list_protocols() { + global $CFG; + $protocols = array(); + $directorypath = $CFG->dirroot . "/webservice"; + if( $dh = opendir($directorypath)) { + while( false !== ($file = readdir($dh))) + { + if( $file == '.' || $file == '..' || $file == 'CVS') { // Skip '.' and '..' + continue; + } + $path = $directorypath . '/' . $file; + ///browse the subfolder + if( is_dir($path) ) { + $protocols[] = $file; + } + ///retrieve api.php file + else { + continue; + } + } + closedir($dh); + } + return $protocols; + } + + /** + * Temporary Authentication method to be modified/removed + * @global $DB + * @param $token + * @return + */ + public static function mock_check_token($token) { + //fake test + if ($token == 465465465468468464) { + ///retrieve the user + global $DB; + $user = $DB->get_record('user', array('username'=>'wsuser', 'mnethostid'=>1)); + + if (empty($user)) { + return false; + } + + return $user; + } else { + return false; + } + } + +} + +/** + * Web Service server base class + */ +abstract class webservice_server { + + /** + * Web Service Protocol name (eg. SOAP, REST, XML-RPC,...) + * @var String + */ + private $protocolname; + + /** + * if set to false the server cannot be run + * @var String + */ + private $enable; + + public function __construct() { + } + + abstract public function run(); + + public function get_protocolname() { + return $this->protocolname; + } + + public function set_protocolname($protocolname) { + $this->protocolname = $protocolname; + } + + public function get_enable() { + return $this->enable; + } + + public function set_enable($enable) { + $this->enable = $enable; + } + +} + +/** + * Temporary authentication class to be removed/modified + */ +class ws_authentication { + /** + * + * @param array|struct $params + * @return integer + */ + function tmp_get_token($params) { + if ($params['username'] == 'wsuser' && $params['password'] == 'wspassword') { + return '465465465468468464'; + } else { + throw new moodle_exception('wrongusernamepassword'); + } + } +} + +?> diff --git a/webservice/rest/lib.php b/webservice/rest/lib.php new file mode 100644 index 0000000000..50ef328ffc --- /dev/null +++ b/webservice/rest/lib.php @@ -0,0 +1,89 @@ +set_protocolname("Rest"); + } + + /** + * Run REST server + */ + public function run() { + require_once('locallib.php'); + //retrieve path and function name from the URL + $rest_arguments = get_file_argument('server.php'); + header ("Content-type: text/xml"); + echo call_moodle_function($rest_arguments); + } + + /** + * Run Zend REST server + * @global object $USER . + */ + public function zend_run() { + include "Zend/Loader.php"; + Zend_Loader::registerAutoload(); + + // retrieve the token from the url + // if the token doesn't exist, set a class containing only get_token() + $token = optional_param('token',null,PARAM_ALPHANUM); + if (empty($token)) { + $server = new Zend_Rest_Server(); + $server->setClass("ws_authentication"); + $server->handle(); + } else { // if token exist, do the authentication here + /// TODO: following function will need to be modified + $user = webservice_lib::mock_check_token($token); + if (empty($user)) { + throw new moodle_exception('wrongidentification'); + } else { + global $USER; + $USER = $user; + } + + //retrieve the api name + $classpath = optional_param(classpath,null,PARAM_ALPHA); + require_once(dirname(__FILE__) . '/../../'.$classpath.'/external.php'); + + /// run the server + $server = new Zend_Rest_Server(); + $server->setClass($classpath."_external"); + $server->handle(); + } + } + +} + + + +?> diff --git a/webservice/rest/locallib.php b/webservice/rest/locallib.php index 295ec274d4..2294de35b9 100644 --- a/webservice/rest/locallib.php +++ b/webservice/rest/locallib.php @@ -1,8 +1,31 @@ $token - * @return + * @param int $token + * @return object|boolean */ function mock_check_token($token) { //fake test @@ -102,8 +125,8 @@ function mock_check_token($token) { /** * * @author Jerome Mouneyrac - * @param $description - * @return + * @param array $description + * @return array */ function retrieve_params ($description) { $params = array(); diff --git a/webservice/rest/server.php b/webservice/rest/server.php index a07085464d..e4a5a0a49d 100644 --- a/webservice/rest/server.php +++ b/webservice/rest/server.php @@ -1,7 +1,29 @@ enablewebservices)) { die; } -//retrieve path and function name from the URL -$rest_arguments = get_file_argument('server.php'); -header ("Content-type: text/xml"); -//TODO implement authentication (probably in the locallib.php) -echo call_moodle_function($rest_arguments); +$server = new rest_server(); +$server->run(); + ?> \ No newline at end of file diff --git a/webservice/rest/testclient/getusers.php b/webservice/rest/testclient/getusers.php index 0f1567ac06..f736b8d40e 100644 --- a/webservice/rest/testclient/getusers.php +++ b/webservice/rest/testclient/getusers.php @@ -54,7 +54,6 @@ if ($search) { $out = curl_exec($ch); $res = basicxml_xml_to_object($out); - show_object($res->user,2,'auth'); show_xml ($out); diff --git a/webservice/rest/testclient/zend_rest_client.php b/webservice/rest/testclient/zend_rest_client.php index a21c195c66..681dc0b52a 100644 --- a/webservice/rest/testclient/zend_rest_client.php +++ b/webservice/rest/testclient/zend_rest_client.php @@ -1,15 +1,30 @@ - * @version 1.0 - * @package webservices + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * http://www.gnu.org/copyleft/gpl.html + * + * @category Moodle + * @package webservice + * @copyright Copyright (c) 1999 onwards Martin Dougiamas http://dougiamas.com + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL License */ /* - * Zend Rest sclient + * Moodle Zend Rest test client */ require_once('../../../config.php'); include "Zend/Loader.php"; @@ -18,33 +33,22 @@ Zend_Loader::registerAutoload(); //1. authentication $client = new Zend_Rest_Client($CFG->wwwroot."/webservice/rest/zend_rest_server.php"); - - $token = $client->tmp_get_token(array('username' => "wsuser", 'password' => "wspassword"))->get(); echo $token->response(); $token = $token->response(); -printLastRequestResponse($client); - +print "
\n
"; //2. test functions $client = new Zend_Rest_Client($CFG->wwwroot."/webservice/rest/zend_rest_server.php/?classpath=user&token=".$token); - var_dump($client->tmp_get_users(array('search' => "admin"))->get()); -printLastRequestResponse($client); +print "
\n
"; var_dump($client->tmp_create_user(array('username' => "mockuser66",'firstname' => "firstname6",'lastname' => "lastname6",'email' => "mockuser6@mockuser6.com",'password' => "password6"))->get()); -printLastRequestResponse($client); +print "
\n
"; var_dump($client->tmp_update_user(array('username' => "mockuser66",'mnethostid' => 1,'newusername' => "mockuser6b",'firstname' => "firstname6b"))->get()); -printLastRequestResponse($client); +print "
\n
"; var_dump($client->tmp_delete_user(array('username' => "mockuser6b",'mnethostid' => 1))->get()); -printLastRequestResponse($client); - - - -function printLastRequestResponse($client) { - print "
\n";
-    //  print "Request :\n".htmlspecialchars($client->__getLastRequest()) ."\n";
-    // print "Response:\n".htmlspecialchars($client->__getLastResponse())."\n";
-    print "
"; -} +print "
\n
"; +var_dump($client->tmp_do_multiple_user_searches(array(array('search' => "admin"),array('search' => 'mock')))->get()); +print "
\n
"; ?> \ No newline at end of file diff --git a/webservice/rest/zend_rest_server.php b/webservice/rest/zend_rest_server.php index 8db5e0d490..a37c8e7625 100644 --- a/webservice/rest/zend_rest_server.php +++ b/webservice/rest/zend_rest_server.php @@ -1,80 +1,41 @@ - * @version 1.0 - * @package webservices + * LICENSE + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * http://www.gnu.org/copyleft/gpl.html + * + * @category Moodle + * @package webservice + * @copyright Copyright (c) 1999 onwards Martin Dougiamas http://dougiamas.com + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL License */ -/* - * Zend Rest server - */ -require_once(dirname(__FILE__) . '/../../config.php'); -include "Zend/Loader.php"; -Zend_Loader::registerAutoload(); -if (empty($CFG->enablewebservices)) { - die; -} -// retrieve the token from the url -// if the token doesn't exist, set a class containing only get_token() -$token = optional_param('token',null,PARAM_ALPHANUM); -if (empty($token)) { - $server = new Zend_Rest_Server(); - $server->setClass("soap_authentication"); - $server->handle(); -} else { // if token exist, do the authentication here - /// TODO: following function will need to be modified - $user = mock_check_token($token); - if (empty($user)) { - throw new moodle_exception('wrongidentification'); - } else { - /// TODO: probably change this - global $USER; - $USER = $user; - } +/** + * Zend REST Moodle server. + */ - //retrieve the api name - $classpath = optional_param(classpath,null,PARAM_ALPHA); - require_once(dirname(__FILE__) . '/../../'.$classpath.'/external.php'); +require_once(dirname(dirname(dirname(__FILE__))) . '/config.php'); +require_once('lib.php'); - /// run the server - $server = new Zend_Rest_Server(); //TODO: need to call the wsdl generation on the fly - $server->setClass($classpath."_external"); //TODO: pass $user as parameter - $server->handle(); +if (empty($CFG->enablewebservices)) { + die; } +$server = new rest_server(); +$server->zend_run(); -function mock_check_token($token) { - //fake test - if ($token == 465465465468468464) { - ///retrieve the user - global $DB; - $user = $DB->get_record('user', array('username'=>'wsuser', 'mnethostid'=>1)); - - if (empty($user)) { - return false; - } - - return $user; - } else { - return false; - } -} - -class soap_authentication { - /** - * - * @param array $params - * @return integer - */ - function tmp_get_token($params) { - if ($params['username'] == 'wsuser' && $params['password'] == 'wspassword') { - return '465465465468468464'; - } else { - throw new moodle_exception('wrongusernamepassword'); - } - } -} ?> \ No newline at end of file diff --git a/webservice/soap/generatewsdl.php b/webservice/soap/generatewsdl.php index 466575d66c..0756c51692 100644 --- a/webservice/soap/generatewsdl.php +++ b/webservice/soap/generatewsdl.php @@ -1,7 +1,32 @@ generate_wsdl($token); echo $wsdl; /** - * WORK IN PROGRESS - Generator not working yet + * WORK IN PROGRESS */ class wsdl_generator { @@ -323,6 +348,12 @@ EOF; case PARAM_NUMBER: return "integer"; break; + case PARAM_INT: + return "integer"; + break; + case PARAM_BOOL: + return "boolean"; + break; case PARAM_ALPHANUM: return "string"; break; diff --git a/webservice/soap/lib.php b/webservice/soap/lib.php new file mode 100644 index 0000000000..68373dc0f7 --- /dev/null +++ b/webservice/soap/lib.php @@ -0,0 +1,148 @@ +set_protocolname("Soap"); + } + + /** + * Run SOAP server + * @global $CFG + * @global $USER + */ + public function run() { + global $CFG; + // retrieve the token from the url + // if the token doesn't exist, set a class containing only get_token() + $token = optional_param('token',null,PARAM_ALPHANUM); + if (empty($token)) { + $server = new SoapServer($CFG->wwwroot."/webservice/soap/generatewsdl.php"); + $server->setClass("ws_authentication"); + $server->handle(); + } else { // if token exist, do the authentication here + /// TODO: following function will need to be modified + $user = webservice_lib::mock_check_token($token); + if (empty($user)) { + throw new moodle_exception('wrongidentification'); + } else { + /// TODO: probably change this + global $USER; + $USER = $user; + } + + //retrieve the api name + $classpath = optional_param(classpath,null,PARAM_ALPHA); + require_once(dirname(__FILE__) . '/../../'.$classpath.'/external.php'); + + /// run the server + $server = new SoapServer($CFG->wwwroot."/webservice/soap/generatewsdl.php?token=".$token); + $server->setClass($classpath."_external"); //TODO: pass $user as parameter + $server->handle(); + } + } + + /** + * Run Zend SOAP server + * @global $CFG + * @global $USER + */ + public function zend_run() { + global $CFG; + include "Zend/Loader.php"; + Zend_Loader::registerAutoload(); + + // retrieve the token from the url + // if the token doesn't exist, set a class containing only get_token() + $token = optional_param('token',null,PARAM_ALPHANUM); + + + ///this is a hack, because there is a bug in Zend framework (http://framework.zend.com/issues/browse/ZF-5736) + if (empty($token)) { + $relativepath = get_file_argument(); + $args = explode('/', trim($relativepath, '/')); + if (count($args) == 2) { + $token = (integer)$args[0]; + $classpath = $args[1]; + } + } + + if (empty($token)) { + + if(isset($_GET['wsdl'])) { + $autodiscover = new Zend_Soap_AutoDiscover(); + $autodiscover->setClass('ws_authentication'); + $autodiscover->handle(); + } else { + + $soap = new Zend_Soap_Server($CFG->wwwroot."/webservice/soap/zend_soap_server.php?wsdl"); // this current file here + $soap->setClass('ws_authentication'); + $soap->handle(); + } + } else { // if token exist, do the authentication here + /// TODO: following function will need to be modified + $user = webservice_lib::mock_check_token($token); + if (empty($user)) { + throw new moodle_exception('wrongidentification'); + } else { + /// TODO: probably change this + global $USER; + $USER = $user; + } + //retrieve the api name + if (empty($classpath)) { + $classpath = optional_param('classpath',null,PARAM_ALPHANUM); + } + require_once(dirname(__FILE__) . '/../../'.$classpath.'/external.php'); + + /// run the server + if(isset($_GET['wsdl'])) { + $autodiscover = new Zend_Soap_AutoDiscover(); + + //this is a hack, because there is a bug in Zend framework (http://framework.zend.com/issues/browse/ZF-5736) + $autodiscover->setUri($CFG->wwwroot."/webservice/soap/zend_soap_server.php/".$token."/".$classpath); + $autodiscover->setClass($classpath."_external"); + $autodiscover->handle(); + } else { + $soap = new Zend_Soap_Server($CFG->wwwroot."/webservice/soap/zend_soap_server.php?token=".$token."&classpath=".$classpath."&wsdl"); // this current file here + $soap->setClass($classpath."_external"); + $soap->handle(); + } + } + } + +} + + +?> diff --git a/webservice/soap/server.php b/webservice/soap/server.php index c8942d2794..caee02c6c1 100644 --- a/webservice/soap/server.php +++ b/webservice/soap/server.php @@ -1,74 +1,40 @@ - * @version 1.0 - * @package webservices + * LICENSE + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details: + * + * http://www.gnu.org/copyleft/gpl.html + * + * @category Moodle + * @package webservice + * @copyright Copyright (c) 1999 onwards Martin Dougiamas http://dougiamas.com + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL License */ + /* * SOAP server */ require_once(dirname(__FILE__) . '/../../config.php'); +require_once('lib.php'); if (empty($CFG->enablewebservices)) { die; } -// retrieve the token from the url -// if the token doesn't exist, set a class containing only get_token() -$token = optional_param('token',null,PARAM_ALPHANUM); -if (empty($token)) { - $server = new SoapServer($CFG->wwwroot."/webservice/soap/generatewsdl.php"); - $server->setClass("soap_authentication"); - $server->handle(); -} else { // if token exist, do the authentication here - /// TODO: following function will need to be modified - $user = mock_check_token($token); - if (empty($user)) { - throw new moodle_exception('wrongidentification'); - } else { - /// TODO: probably change this - global $USER; - $USER = $user; - } - - //retrieve the api name - $classpath = optional_param(classpath,null,PARAM_ALPHA); - require_once(dirname(__FILE__) . '/../../'.$classpath.'/external.php'); - - /// run the server - $server = new SoapServer($CFG->wwwroot."/webservice/soap/generatewsdl.php?token=".$token); //TODO: need to call the wsdl generation on the fly - $server->setClass($classpath."_external"); //TODO: pass $user as parameter - $server->handle(); -} - - -function mock_check_token($token) { - //fake test - if ($token == 465465465468468464) { - ///retrieve the user - global $DB; - $user = $DB->get_record('user', array('username'=>'wsuser', 'mnethostid'=>1)); - - if (empty($user)) { - return false; - } +$server = new soap_server(); +$server->run(); - return $user; - } else { - return false; - } -} - -class soap_authentication { - function tmp_get_token($params) { - if ($params['username'] == 'wsuser' && $params['password'] == 'wspassword') { - return '465465465468468464'; - } else { - throw new moodle_exception('wrongusernamepassword'); - } - } -} ?> \ No newline at end of file diff --git a/webservice/soap/testclient/php_soap_client.php b/webservice/soap/testclient/php_soap_client.php index 77366a9022..1b62a64089 100644 --- a/webservice/soap/testclient/php_soap_client.php +++ b/webservice/soap/testclient/php_soap_client.php @@ -1,15 +1,30 @@ - * @version 1.0 - * @package webservices + * @category Moodle + * @package webservice + * @copyright Copyright (c) 1999 onwards Martin Dougiamas http://dougiamas.com + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL License */ /* - * SOAP client + * SOAP test client */ require_once(dirname(__FILE__) . '/../../../config.php'); diff --git a/webservice/soap/testclient/zend_soap_client.php b/webservice/soap/testclient/zend_soap_client.php new file mode 100644 index 0000000000..063aa9f625 --- /dev/null +++ b/webservice/soap/testclient/zend_soap_client.php @@ -0,0 +1,66 @@ +wwwroot."/webservice/soap/zend_soap_server.php?wsdl"); +try { + $token = $client->tmp_get_token(array('username' => "wsuser", 'password' => "wspassword")); + printLastRequestResponse($client); +} catch (moodle_exception $exception) { + echo $exception; +} +echo $CFG->wwwroot."/webservice/soap/zend_soap_server.php?token=".$token."&classpath=user&wsdl"; + +//2. test functions +$client = new Zend_Soap_Client($CFG->wwwroot."/webservice/soap/zend_soap_server.php?token=".$token."&classpath=user&wsdl"); +var_dump($client->tmp_get_users(array('search' => "admin"))); +printLastRequestResponse($client); +var_dump($client->tmp_create_user(array('username' => "mockuser66",'firstname' => "firstname6",'lastname' => "lastname6",'email' => "mockuser6@mockuser6.com",'password' => "password6"))); +printLastRequestResponse($client); +var_dump($client->tmp_update_user(array('username' => "mockuser66",'mnethostid' => 1,'newusername' => "mockuser6b",'firstname' => "firstname6b"))); +printLastRequestResponse($client); +var_dump($client->tmp_delete_user(array('username' => "mockuser6b",'mnethostid' => 1))); +printLastRequestResponse($client); +var_dump($client->tmp_do_multiple_user_searches(array(array('search' => "jerome"),array('search' => "mock")))); +printLastRequestResponse($client); + +function printLastRequestResponse($client) { + print "
\n";
+    print "Request :\n".htmlspecialchars($client->__getLastRequest()) ."\n";
+    print "Response:\n".htmlspecialchars($client->__getLastResponse())."\n";
+    print "
"; +} + +?> \ No newline at end of file diff --git a/webservice/soap/zend_soap_server.php b/webservice/soap/zend_soap_server.php new file mode 100644 index 0000000000..29094c7e57 --- /dev/null +++ b/webservice/soap/zend_soap_server.php @@ -0,0 +1,41 @@ +enablewebservices)) { + die; +} + +$server = new soap_server(); +$server->zend_run(); + +?> \ No newline at end of file diff --git a/webservice/xmlrpc/lib.php b/webservice/xmlrpc/lib.php new file mode 100644 index 0000000000..7335656657 --- /dev/null +++ b/webservice/xmlrpc/lib.php @@ -0,0 +1,77 @@ +set_protocolname("XML-RPC"); + } + + public function run() { + include "Zend/Loader.php"; + Zend_Loader::registerAutoload(); + + Zend_XmlRpc_Server_Fault::attachFaultException('moodle_exception'); + + // retrieve the token from the url + // if the token doesn't exist, set a class containing only get_token() + $token = optional_param('token',null,PARAM_ALPHANUM); + if (empty($token)) { + $server = new Zend_XmlRpc_Server(); + $server->setClass("ws_authentication", "authentication"); + echo $server->handle(); + } else { // if token exist, do the authentication here + /// TODO: following function will need to be modified + $user = webservice_lib::mock_check_token($token); + if (empty($user)) { + throw new moodle_exception('wrongidentification'); + } else { + /// TODO: probably change this + global $USER; + $USER = $user; + } + + //retrieve the api name + $classpath = optional_param(classpath,null,PARAM_ALPHA); + require_once(dirname(__FILE__) . '/../../'.$classpath.'/external.php'); + + /// run the server + $server = new Zend_XmlRpc_Server(); + $server->setClass($classpath."_external", $classpath); + echo $server->handle(); + } + } + +} + + +?> diff --git a/webservice/xmlrpc/testclient/zend_xmlrpc_client.php b/webservice/xmlrpc/testclient/zend_xmlrpc_client.php new file mode 100644 index 0000000000..8c88f06914 --- /dev/null +++ b/webservice/xmlrpc/testclient/zend_xmlrpc_client.php @@ -0,0 +1,53 @@ +wwwroot."/webservice/xmlrpc/zend_xmlrpc_server.php"); +$token = $client->call('authentication.tmp_get_token', array(array('username' => "wsuser", 'password' => "wspassword"))); +var_dump($token); + +//2. test functions +$client = new Zend_XmlRpc_Client($CFG->wwwroot."/webservice/xmlrpc/zend_xmlrpc_server.php?classpath=user&token=".$token); +var_dump($users = $client->call('user.tmp_get_users', array(array('search' => "admin")))); +print "

\n"; +var_dump($users = $client->call('user.tmp_create_user', array(array('username' => "mockuser66",'firstname' => "firstname6",'lastname' => "lastname6",'email' => "mockuser6@mockuser6.com",'password' => "password6")))); +print "

\n"; +var_dump($users = $client->call('user.tmp_update_user', array(array('username' => "mockuser66",'mnethostid' => 1,'newusername' => "mockuser6b",'firstname' => "firstname6b")))); +print "

\n"; +var_dump($users = $client->call('user.tmp_delete_user', array(array('username' => "mockuser6b",'mnethostid' => 1)))); +print "

\n"; +var_dump($users = $client->call('user.tmp_do_multiple_user_searches', array(array(array('search' => "jerome"),array('search' => "admin"))))); +print "

\n"; + +?> \ No newline at end of file diff --git a/webservice/xmlrpc/zend_xmlrpc_server.php b/webservice/xmlrpc/zend_xmlrpc_server.php new file mode 100644 index 0000000000..427f8b523d --- /dev/null +++ b/webservice/xmlrpc/zend_xmlrpc_server.php @@ -0,0 +1,46 @@ + + * @version 1.0 + * @package webservices + */ + +/* + * Zend XML-RPC server + */ +require_once(dirname(__FILE__) . '/../../config.php'); +require_once('lib.php'); + +if (empty($CFG->enablewebservices)) { + die; +} + +$server = new xmlrpc_server(); +$server->run(); + +?> \ No newline at end of file