/** * call a web service * * This function supports following protocols, as specified in last parameter: * - 'XML-RPC' - request and response are XML snippets * - 'JSON-RPC' - request and response are JSON snippets * * Minimum example: * [php] * $result = Call::invoke($url, $method); * if(!$result[0]) * echo $result[1]; // error message * else * ... // use call result from $result[1] * [/php] * * Note that in a previous version data was encoded. * Unfortunately, most servers do not handle encoding, despite it's a useful web standard. * * @param string the url to use * @param string the service to call * @param array the parameters to transmit * @param string the protocol to use * @return an array of which the first value indicates call success or failure * * @see control/scan.php * @see links/links.php * @see servers/servers.php * @see services/blog_test.php */ public static function invoke($url, $service, $parameters = NULL, $variant = 'XML-RPC') { global $context; // submit a raw request $raw_request = FALSE; if ($variant == 'raw') { $raw_request = TRUE; $variant = 'XML-RPC'; } // select a codec handler include_once $context['path_to_root'] . 'services/codec.php'; $codec = Codec::initialize($variant); if (!is_object($codec)) { return array(FALSE, 'Do not know how to cope with API ' . $variant); } // adjust content type if ($variant == 'JSON-RPC') { $headers = 'Content-Type: application/json' . CRLF; } else { $headers = 'Content-Type: text/xml' . CRLF; } // build the request if ($raw_request) { $result = array(TRUE, $parameters); } else { $result = $codec->export_request($service, $parameters); } if (!$result[0]) { return array(FALSE, $result[1]); } $headers .= 'Content-Length: ' . strlen($result[1]) . CRLF; // parse the target URL $items = @parse_url($url); // no host, assume it's us if (!isset($items['host']) || !($host = $items['host'])) { $host = $context['host_name']; } // no port, assume the standard if (!isset($items['port']) || !($port = $items['port'])) { $port = 80; } // outbound web is not authorized if (isset($context['without_outbound_http']) && $context['without_outbound_http'] == 'Y') { return array(FALSE, 'Outbound HTTP is not authorized.'); } // connect to the server if (!($handle = Safe::fsockopen($host, $port, $errno, $errstr, 30))) { return array(FALSE, sprintf('Impossible to connect to %s.', $host . ':' . $port)); } // ensure enough execution time Safe::set_time_limit(30); // build the path, including any query $path = $items['path']; if (!$path) { $path = '/'; } if (isset($items['query']) && $items['query']) { $path .= '?' . $items['query']; } // build an HTTP request $request = "POST " . $path . " HTTP/1.0" . CRLF . 'Host: ' . $host . CRLF . "Accept-Encoding: gzip" . CRLF . "User-Agent: YACS (www.yacs.fr)" . CRLF . "Connection: close" . CRLF . $headers . CRLF . $result[1]; // save the request if debug mode if (isset($context['debug_call']) && $context['debug_call'] == 'Y') { Logger::remember('services/call.php: Call::invoke() request', str_replace("\r\n", "\n", $request), 'debug'); } // submit the request fputs($handle, $request); // get everything by Ethernet-sized chunks $response = ''; while (!feof($handle) && strlen($response) < 5242880) { $response .= fread($handle, 1500); } fclose($handle); // ensure we have a valid HTTP status line if (preg_match('/^HTTP/', $response) && !preg_match('/^HTTP\\/[0-9\\.]+ 200 /', $response)) { $lines = explode("\n", $response, 2); return array(FALSE, 'Unexpected HTTP status "' . $lines[0] . '"'); } // separate headers from body list($headers, $content) = explode(CRLF . CRLF, $response, 2); // uncompress payload if necessary if (preg_match('/Content-Encoding: \\s*gzip/i', $headers)) { $content = gzinflate(substr($content, 10)); } // save the response if debug mode if (isset($context['debug_call']) && $context['debug_call'] == 'Y') { Logger::remember('services/call.php: Call::invoke() response', str_replace("\r\n", "\n", $headers . "\n\n" . $content), 'debug'); } // decode the result return $codec->import_response($content, $headers, $parameters); }