? drupaldown ? upgrade ? modules/syndication Index: includes/common.inc =================================================================== RCS file: /cvs/drupal/drupal/includes/common.inc,v retrieving revision 1.811 diff -u -p -r1.811 common.inc --- includes/common.inc 26 Oct 2008 18:06:38 -0000 1.811 +++ includes/common.inc 29 Oct 2008 05:55:28 -0000 @@ -398,27 +398,29 @@ function drupal_access_denied() { * * @param $url * A string containing a fully qualified URI. - * @param $headers - * An array containing an HTTP header => value pair. - * @param $method - * A string defining the HTTP request to use. - * @param $data - * A string containing data to include in the request. - * @param $retry - * An integer representing how many times to retry the request in case of a - * redirect. + * @param $options + * (optional) An array which can have any or all of the following keys: + * - headers + * An array containing an HTTP header => value pair. + * - method + * A string defining the HTTP request to use. Defaults to 'GET'. + * - data + * A string defining the HTTP request to use. Defaults to NULL. + * - retry + * An integer representing how many times to retry the request in case of + * a redirect. Defaults to 3. * @return * An object containing the HTTP request headers, response code, headers, - * data and redirect status. + * data and, if redirected, the redirect status and location. */ -function drupal_http_request($url, $headers = array(), $method = 'GET', $data = NULL, $retry = 3) { - global $db_prefix; - static $self_test = FALSE; +function drupal_http_request($url, Array $options = array()) { $result = new stdClass(); + // Try to clear the drupal_http_request_fails variable if it's set. We // can't tie this call to any error because there is no surefire way to // tell whether a request has failed, so we add the check to places where // some parsing has failed. + static $self_test = FALSE; if (!$self_test && variable_get('drupal_http_request_fails', FALSE)) { $self_test = TRUE; $works = module_invoke('system', 'check_http_request'); @@ -430,106 +432,71 @@ function drupal_http_request($url, $head return $result; } } + + // Merge default options. + $default_options = array( + 'headers' => array(), + 'method' => 'GET', + 'data' => NULL, + 'retry' => 3, + ); + $options += $default_options; + + // Merge default headers. + $default_headers = array( + 'User-Agent' => 'Drupal (+http://drupal.org/)', + 'Content-Length' => strlen($options['data']), + ); + $options['headers'] += $default_headers; // Parse the URL and make sure we can handle the schema. $uri = @parse_url($url); if ($uri == FALSE) { - $result->error = 'unable to parse URL'; - return $result; - } - - if (!isset($uri['scheme'])) { - $result->error = 'missing schema'; + $result->error = t('Unable to parse URL'); return $result; } - - switch ($uri['scheme']) { - case 'http': - $port = isset($uri['port']) ? $uri['port'] : 80; - $host = $uri['host'] . ($port != 80 ? ':' . $port : ''); - $fp = @fsockopen($uri['host'], $port, $errno, $errstr, 15); - break; - case 'https': - // Note: Only works for PHP 4.3 compiled with OpenSSL. - $port = isset($uri['port']) ? $uri['port'] : 443; - $host = $uri['host'] . ($port != 443 ? ':' . $port : ''); - $fp = @fsockopen('ssl://' . $uri['host'], $port, $errno, $errstr, 20); - break; - default: - $result->error = 'invalid schema ' . $uri['scheme']; - return $result; - } - - // Make sure the socket opened properly. - if (!$fp) { - // When a network error occurs, we use a negative number so it does not - // clash with the HTTP status codes. - $result->code = -$errno; - $result->error = trim($errstr); + elseif (!isset($uri['scheme'])) { + $result->error = t('Missing schema'); return $result; } - - // Construct the path to act on. - $path = isset($uri['path']) ? $uri['path'] : '/'; - if (isset($uri['query'])) { - $path .= '?' . $uri['query']; - } - - // Create HTTP request. - $defaults = array( - // RFC 2616: "non-standard ports MUST, default ports MAY be included". - // We don't add the port to prevent from breaking rewrite rules checking the - // host that do not take into account the port number. - 'Host' => "Host: $host", - 'User-Agent' => 'User-Agent: Drupal (+http://drupal.org/)', - 'Content-Length' => 'Content-Length: ' . strlen($data) - ); - + // If the server url has a user then attempt to use basic authentication if (isset($uri['user'])) { - $defaults['Authorization'] = 'Authorization: Basic ' . base64_encode($uri['user'] . (!empty($uri['pass']) ? ":" . $uri['pass'] : '')); + $options['headers']['Authorization'] = 'Basic ' . base64_encode($uri['user'] . (!empty($uri['pass']) ? ":" . $uri['pass'] : '')); } - + // If the database prefix is being used by SimpleTest to run the tests in a copied // database then set the user-agent header to the database prefix so that any // calls to other Drupal pages will run the SimpleTest prefixed database. The // user-agent is used to ensure that multiple testing sessions running at the // same time won't interfere with each other as they would if the database // prefix were stored statically in a file or database variable. + global $db_prefix; if (preg_match("/^simpletest\d+/", $db_prefix)) { - $headers['User-Agent'] = $db_prefix; - } - - foreach ($headers as $header => $value) { - $defaults[$header] = $header . ': ' . $value; - } - - $request = $method . ' ' . $path . " HTTP/1.0\r\n"; - $request .= implode("\r\n", $defaults); - $request .= "\r\n\r\n"; - if ($data) { - $request .= $data . "\r\n"; - } - $result->request = $request; - - fwrite($fp, $request); - - // Fetch response. - $response = ''; - while (!feof($fp) && $chunk = fread($fp, 1024)) { - $response .= $chunk; + $options['headers']['User-Agent'] = $db_prefix; } - fclose($fp); + + // Format the headers. + foreach ($options['headers'] as $header => $value) { + $options['headers'][$header] = $header . ': ' . $value; + } + + // Perform request. + $module = variable_get('http_request', 'system'); + $result = module_invoke($module, 'http_request', $url, $options); // Parse response. - list($split, $result->data) = explode("\r\n\r\n", $response, 2); + list($split, $result->data) = explode("\r\n\r\n", $result->response, 2); + unset($result->response); $split = preg_split("/\r\n|\n|\r/", $split); + // Parse response code. list($protocol, $code, $text) = explode(' ', trim(array_shift($split)), 3); - $result->headers = array(); + $result->code = $code; // Parse headers. + $result->headers = array(); while ($line = trim(array_shift($split))) { list($header, $value) = explode(':', $line, 2); if (isset($result->headers[$header]) && $header == 'Set-Cookie') { @@ -541,43 +508,79 @@ function drupal_http_request($url, $head $result->headers[$header] = trim($value); } } - + $responses = array( - 100 => 'Continue', 101 => 'Switching Protocols', - 200 => 'OK', 201 => 'Created', 202 => 'Accepted', 203 => 'Non-Authoritative Information', 204 => 'No Content', 205 => 'Reset Content', 206 => 'Partial Content', - 300 => 'Multiple Choices', 301 => 'Moved Permanently', 302 => 'Found', 303 => 'See Other', 304 => 'Not Modified', 305 => 'Use Proxy', 307 => 'Temporary Redirect', - 400 => 'Bad Request', 401 => 'Unauthorized', 402 => 'Payment Required', 403 => 'Forbidden', 404 => 'Not Found', 405 => 'Method Not Allowed', 406 => 'Not Acceptable', 407 => 'Proxy Authentication Required', 408 => 'Request Time-out', 409 => 'Conflict', 410 => 'Gone', 411 => 'Length Required', 412 => 'Precondition Failed', 413 => 'Request Entity Too Large', 414 => 'Request-URI Too Large', 415 => 'Unsupported Media Type', 416 => 'Requested range not satisfiable', 417 => 'Expectation Failed', - 500 => 'Internal Server Error', 501 => 'Not Implemented', 502 => 'Bad Gateway', 503 => 'Service Unavailable', 504 => 'Gateway Time-out', 505 => 'HTTP Version not supported' + 100 => 'Continue', + 101 => 'Switching Protocols', + 200 => 'OK', + 201 => 'Created', + 202 => 'Accepted', + 203 => 'Non-Authoritative Information', + 204 => 'No Content', + 205 => 'Reset Content', + 206 => 'Partial Content', + 300 => 'Multiple Choices', + 301 => 'Moved Permanently', + 302 => 'Found', + 303 => 'See Other', + 304 => 'Not Modified', + 305 => 'Use Proxy', + 307 => 'Temporary Redirect', + 400 => 'Bad Request', + 401 => 'Unauthorized', + 402 => 'Payment Required', + 403 => 'Forbidden', + 404 => 'Not Found', + 405 => 'Method Not Allowed', + 406 => 'Not Acceptable', + 407 => 'Proxy Authentication Required', + 408 => 'Request Time-out', + 409 => 'Conflict', + 410 => 'Gone', + 411 => 'Length Required', + 412 => 'Precondition Failed', + 413 => 'Request Entity Too Large', + 414 => 'Request-URI Too Large', + 415 => 'Unsupported Media Type', + 416 => 'Requested range not satisfiable', + 417 => 'Expectation Failed', + 500 => 'Internal Server Error', + 501 => 'Not Implemented', + 502 => 'Bad Gateway', + 503 => 'Service Unavailable', + 504 => 'Gateway Time-out', + 505 => 'HTTP Version not supported', ); + // RFC 2616 states that all unknown HTTP codes must be treated the same as the // base code in their class. - if (!isset($responses[$code])) { - $code = floor($code / 100) * 100; + if (!isset($responses[$result->code])) { + $result->code = floor($result->code / 100) * 100; } - switch ($code) { + switch ($result->code) { case 200: // OK case 304: // Not modified break; case 301: // Moved permanently case 302: // Moved temporarily case 307: // Moved temporarily - $location = $result->headers['Location']; - - if ($retry) { - $result = drupal_http_request($location, $headers, $method, $data, --$retry); - $result->redirect_code = $code; + $redirect_url = $result->headers['Location']; + if ($options['retry']) { + $options['retry']--; + $redirect_code = $result->code; + $result = drupal_http_request($redirect_url, $options); + $result->redirect_code = $redirect_code; } - $result->redirect_url = $location; - + $result->redirect_url = $redirect_url; break; default: $result->error = $text; } - - $result->code = $code; + return $result; } + /** * @} End of "HTTP handling". */ Index: modules/system/system.module =================================================================== RCS file: /cvs/drupal/drupal/modules/system/system.module,v retrieving revision 1.634 diff -u -p -r1.634 system.module --- modules/system/system.module 29 Oct 2008 03:02:42 -0000 1.634 +++ modules/system/system.module 29 Oct 2008 05:55:30 -0000 @@ -2073,6 +2073,69 @@ function _system_zonelist() { } /** + * Implementation of hook_http_request(). + */ +function system_http_request($url, Array $options) { + $result = new stdClass(); + + // Parse the URL and make sure we can handle the schema. + $uri = @parse_url($url); + + switch ($uri['scheme']) { + case 'http': + $port = isset($uri['port']) ? $uri['port'] : 80; + $host = $uri['host'] . ($port != 80 ? ':' . $port : ''); + $fp = @fsockopen($uri['host'], $port, $errno, $errstr, 15); + break; + case 'https': + // Note: Only works for PHP 4.3 compiled with OpenSSL. + $port = isset($uri['port']) ? $uri['port'] : 443; + $host = $uri['host'] . ($port != 443 ? ':' . $port : ''); + $fp = @fsockopen('ssl://' . $uri['host'], $port, $errno, $errstr, 20); + break; + default: + $result->error = 'invalid schema ' . $uri['scheme']; + return $result; + } + + // Make sure the socket opened properly. + if (!$fp) { + // When a network error occurs, we use a negative number so it does not + // clash with the HTTP status codes. + $result->code = -$errno; + $result->error = trim($errstr); + return $result; + } + + // Construct the path to act on. + $path = isset($uri['path']) ? $uri['path'] : '/'; + if (isset($uri['query'])) { + $path .= '?' . $uri['query']; + } + + $options['headers']['Host'] = "Host: $host"; + + $request = $options['method'] . ' ' . $path . " HTTP/1.0\r\n"; + $request .= implode("\r\n", $options['headers']); + $request .= "\r\n\r\n"; + if ($options['data']) { + $request .= $options['data'] . "\r\n"; + } + $result->request = $request; + + fwrite($fp, $request); + + // Fetch response. + $result->response = ''; + while (!feof($fp) && $chunk = fread($fp, 1024)) { + $result->response .= $chunk; + } + fclose($fp); + + return $result; +} + +/** * Checks whether the server is capable of issuing HTTP requests. * * The function sets the drupal_http_request_fail system variable to TRUE if @@ -2084,6 +2147,13 @@ function _system_zonelist() { * and contains the same output as if called via the menu system. */ function system_check_http_request() { + // Check whether we can perform external requests first. Use the simple and + // always available test page provided by RFC 2606. + $result = drupal_http_request('http://example.com/'); + $works = isset($result->data) && $result->code == 200 && md5($result->data) == '32e8347a8caee51bd4474c4fbb7025c5'; + variable_set('drupal_http_request_fails', !$works); + return $works; + // Check whether we can do any request at all. First get the results for // a very simple page which has access TRUE set via the menu system. Then, // try to drupal_http_request() the same page and compare. @@ -2099,6 +2169,36 @@ function system_check_http_request() { } /** + * Implementation of hook_modules_enabled(). + */ +function system_modules_enabled($modules) { + // Check if modules define hook_http_request(). + // Set override variable if it requests successfully. + // TODO: Registry has not been rebuilt yet and module_hook FAILS if module does have hook_http_request(). + foreach ($modules as $module) { + if (module_hook($module, 'http_request') && module_invoke($module, 'check_http_request')) { + variable_set('http_request', $module); + break; + } + } +} + +/** + * Implementation of hook_modules_disabled(). + */ +function system_modules_disabled($modules) { + // Check if modules defined hook_http_request(). + // Reset override variableif no longer available. + foreach ($modules as $module) { + if (module_hook($module, 'http_request')) { + variable_set('http_request', 'system'); + module_invoke('system', 'check_http_request'); + break; + } + } +} + +/** * Format the Powered by Drupal text. * * @ingroup themeable