View source
<?php
define('FEEDS_ERROR_PARSE_ERROR', -1001);
define('FEEDS_ERROR_NO_SCHEME', -1002);
define('FEEDS_ERROR_INVALID_SCHEME', -1003);
define('HTTP_REQUEST_PCRE_LINK_TAG', '/<link((?:[\\x09\\x0A\\x0B\\x0C\\x0D\\x20]+[^\\x09\\x0A\\x0B\\x0C\\x0D\\x20\\x2F\\x3E][^\\x09\\x0A\\x0B\\x0C\\x0D\\x20\\x2F\\x3D\\x3E]*(?:[\\x09\\x0A\\x0B\\x0C\\x0D\\x20]*=[\\x09\\x0A\\x0B\\x0C\\x0D\\x20]*(?:"(?:[^"]*)"|\'(?:[^\']*)\'|(?:[^\\x09\\x0A\\x0B\\x0C\\x0D\\x20\\x22\\x27\\x3E][^\\x09\\x0A\\x0B\\x0C\\x0D\\x20\\x3E]*)?))?)*)[\\x09\\x0A\\x0B\\x0C\\x0D\\x20]*(>(.*)<\\/link>|(\\/)?>)/si');
define('HTTP_REQUEST_PCRE_TAG_ATTRIBUTES', '/[\\x09\\x0A\\x0B\\x0C\\x0D\\x20]+([^\\x09\\x0A\\x0B\\x0C\\x0D\\x20\\x2F\\x3E][^\\x09\\x0A\\x0B\\x0C\\x0D\\x20\\x2F\\x3D\\x3E]*)(?:[\\x09\\x0A\\x0B\\x0C\\x0D\\x20]*=[\\x09\\x0A\\x0B\\x0C\\x0D\\x20]*(?:"([^"]*)"|\'([^\']*)\'|([^\\x09\\x0A\\x0B\\x0C\\x0D\\x20\\x22\\x27\\x3E][^\\x09\\x0A\\x0B\\x0C\\x0D\\x20\\x3E]*)?))?/');
class HRCurlException extends Exception {
}
class FeedsHTTPRequestException extends Exception {
}
function http_request_get_common_syndication($url, $options = array()) {
$download = feeds_http_request($url, $options);
if ($download->code != 200) {
return FALSE;
}
$downloaded_string = $download->data;
if (isset($download->headers['content-type']) && http_request_is_feed($download->headers['content-type'], $downloaded_string)) {
return $url;
}
$discovered_feeds = http_request_find_feeds($downloaded_string);
foreach ($discovered_feeds as $feed_url) {
$absolute = http_request_create_absolute_url($feed_url, $url);
if (!empty($absolute)) {
return $absolute;
}
}
}
function http_request_get($url, $username = NULL, $password = NULL, $accept_invalid_cert = FALSE, $timeout = NULL) {
return feeds_http_request($url, array(
'username' => $username,
'password' => $password,
'accept_invalid_cert' => $accept_invalid_cert,
'timeout' => $timeout,
));
}
function feeds_http_request($url, array $options = array()) {
$options += array(
'username' => NULL,
'password' => NULL,
'accept_invalid_cert' => FALSE,
'cache_http_result' => TRUE,
);
if (empty($options['timeout'])) {
$options['timeout'] = variable_get('http_request_timeout', 30);
}
$cached_urls =& drupal_static(__FUNCTION__, array());
if (!empty($cached_urls[$url])) {
$cache = http_request_get_cache($url);
if ($cache->data) {
return $cache->data;
}
}
if (!$options['username'] && valid_url($url, TRUE)) {
$url_parts = parse_url($url);
if (!empty($url_parts['user'])) {
$options['password'] = urldecode($url_parts['pass']);
$options['username'] = urldecode($url_parts['user']);
}
}
$curl = http_request_use_curl();
$headers = array();
if ($options['cache_http_result'] && ($cache = http_request_get_cache($url))) {
$last_result = $cache->data;
$last_headers = array_change_key_case($last_result->headers);
if (!empty($last_headers['etag'])) {
if ($curl) {
$headers[] = 'If-None-Match: ' . $last_headers['etag'];
}
else {
$headers['If-None-Match'] = $last_headers['etag'];
}
}
if (!empty($last_headers['last-modified'])) {
if ($curl) {
$headers[] = 'If-Modified-Since: ' . $last_headers['last-modified'];
}
else {
$headers['If-Modified-Since'] = $last_headers['last-modified'];
}
}
if (!empty($options['username']) && !$curl) {
$headers['Authorization'] = 'Basic ' . base64_encode($options['username'] . ':' . $options['password']);
}
}
$url = strtr($url, array(
'feed://' => 'http://',
'webcal://' => 'http://',
));
if ($curl) {
$headers[] = 'User-Agent: Drupal (+http://drupal.org/)';
$result = new stdClass();
$result->headers = array();
$uri = parse_url($url);
if ($uri === FALSE) {
$result->error = 'unable to parse URL';
$result->code = FEEDS_ERROR_PARSE_ERROR;
}
elseif (!isset($uri['scheme'])) {
$result->error = 'missing schema';
$result->code = FEEDS_ERROR_NO_SCHEME;
}
else {
switch ($uri['scheme']) {
case 'http':
case 'https':
break;
default:
$result->error = 'invalid schema ' . $uri['scheme'];
$result->code = FEEDS_ERROR_INVALID_SCHEME;
break;
}
}
if (empty($result->error)) {
$download = curl_init($url);
curl_setopt($download, CURLOPT_FOLLOWLOCATION, TRUE);
if (!empty($options['username'])) {
curl_setopt($download, CURLOPT_USERPWD, $options['username'] . ':' . $options['password']);
curl_setopt($download, CURLOPT_HTTPAUTH, CURLAUTH_ANY);
}
curl_setopt($download, CURLOPT_HTTPHEADER, $headers);
curl_setopt($download, CURLOPT_HEADER, TRUE);
curl_setopt($download, CURLOPT_RETURNTRANSFER, TRUE);
curl_setopt($download, CURLOPT_ENCODING, '');
curl_setopt($download, CURLOPT_TIMEOUT, $options['timeout']);
$proxy_server = variable_get('proxy_server');
if ($proxy_server && _drupal_http_use_proxy($uri['host'])) {
curl_setopt($download, CURLOPT_PROXY, $proxy_server);
curl_setopt($download, CURLOPT_PROXYPORT, variable_get('proxy_port', 8080));
if ($proxy_username = variable_get('proxy_username')) {
$username_password = $proxy_username . ':' . variable_get('proxy_password', '');
curl_setopt($download, CURLOPT_PROXYUSERPWD, $username_password);
curl_setopt($download, CURLOPT_PROXYAUTH, variable_get('proxy_auth_method', CURLAUTH_BASIC));
}
}
if ($options['accept_invalid_cert']) {
curl_setopt($download, CURLOPT_SSL_VERIFYPEER, 0);
curl_setopt($download, CURLOPT_SSL_VERIFYHOST, 0);
}
$header = '';
$result->data = curl_exec($download);
if (curl_error($download)) {
throw new HRCurlException(t('cURL error (@code) @error for @url', array(
'@code' => curl_errno($download),
'@error' => curl_error($download),
'@url' => $url,
)), curl_errno($download));
}
$curl_ver = curl_version();
if ($proxy_server && $curl_ver['version_number'] < 0x71e00 && _drupal_http_use_proxy($uri['host'])) {
$http_header_break = "\r\n\r\n";
$response = explode($http_header_break, $result->data);
if (count($response) > 2) {
$result->data = substr($result->data, strlen($response[0] . $http_header_break), strlen($result->data));
}
}
$header_size = curl_getinfo($download, CURLINFO_HEADER_SIZE);
$header = substr($result->data, 0, $header_size - 1);
$result->data = substr($result->data, $header_size);
$headers = preg_split("/(\r\n){2}/", $header);
$header_lines = preg_split("/\r\n|\n|\r/", end($headers));
array_shift($header_lines);
while ($line = trim(array_shift($header_lines))) {
list($header, $value) = explode(':', $line, 2);
$header = strtolower($header);
if (isset($result->headers[$header]) && $header == 'set-cookie') {
$result->headers[$header] .= ',' . trim($value);
}
else {
$result->headers[$header] = trim($value);
}
}
$result->code = curl_getinfo($download, CURLINFO_HTTP_CODE);
curl_close($download);
}
}
else {
$result = drupal_http_request($url, array(
'headers' => $headers,
'timeout' => $options['timeout'],
));
$result->headers = isset($result->headers) ? $result->headers : array();
}
$result->code = isset($result->code) ? $result->code : 200;
if ($result->code == 304) {
if (isset($last_result->data)) {
$last_result->from_cache = TRUE;
return $last_result;
}
else {
http_request_clear_cache($url);
return feeds_http_request($url, $options);
}
}
if ($options['cache_http_result']) {
http_request_set_cache($url, $result);
$cached_urls[$url] = TRUE;
}
return $result;
}
function http_request_check_result($url, $result) {
if (!in_array($result->code, array(
200,
201,
202,
203,
204,
205,
206,
))) {
$vars = array(
'@url' => $url,
'@code' => $result->code,
'@error' => isset($result->error) ? $result->error : 'Unknown error',
);
switch ($result->code) {
case FEEDS_ERROR_PARSE_ERROR:
$message = t('Download of @url failed because it could not be parsed.', $vars);
break;
case FEEDS_ERROR_NO_SCHEME:
$message = t("Download of @url failed because its scheme could not be determined. The URL is expected to start with something like '@example'.", $vars + array(
'@example' => 'http://',
));
break;
case FEEDS_ERROR_INVALID_SCHEME:
$message = t('Download of @url failed because its scheme is not supported: @error. Examples of supported schemes are: @supported.', $vars + array(
'@supported' => implode(', ', array(
'http',
'https',
)),
));
break;
default:
if (isset($result->error)) {
$message = t('Download of @url failed with code @code and the following error: @error.', $vars);
}
else {
$message = t('Download of @url failed with code @code.', $vars);
}
break;
}
throw new FeedsHTTPRequestException($message);
}
}
function http_request_use_curl() {
if (variable_get('feeds_never_use_curl', FALSE)) {
return FALSE;
}
if (!extension_loaded('curl')) {
return FALSE;
}
if (version_compare(PHP_VERSION, '5.6.0', '<')) {
return !ini_get('safe_mode') && !ini_get('open_basedir');
}
return TRUE;
}
function http_request_clear_cache($url) {
cache_clear_all(hash('sha256', $url), 'cache_feeds_http');
}
function http_request_get_cache($url) {
return cache_get(hash('sha256', $url), 'cache_feeds_http');
}
function http_request_set_cache($url, $result) {
$item = $result instanceof FeedsHTTPCacheItem ? $result : new FeedsHTTPCacheItem(hash('sha256', $url), $result);
$item
->cacheSet();
}
function http_request_is_feed($content_type, $data) {
$pos = strpos($content_type, ';');
if ($pos !== FALSE) {
$content_type = substr($content_type, 0, $pos);
}
$content_type = strtolower($content_type);
if (strpos($content_type, 'xml') !== FALSE) {
return TRUE;
}
return FALSE;
}
function http_request_find_feeds($html) {
$matches = array();
preg_match_all(HTTP_REQUEST_PCRE_LINK_TAG, $html, $matches);
$links = $matches[1];
$valid_links = array();
foreach ($links as $link_tag) {
$attributes = array();
$candidate = array();
preg_match_all(HTTP_REQUEST_PCRE_TAG_ATTRIBUTES, $link_tag, $attributes, PREG_SET_ORDER);
foreach ($attributes as $attribute) {
if (empty($attribute[2])) {
$attribute[2] = $attribute[3];
}
if (!empty($attribute[1]) && !empty($attribute[2])) {
$candidate[drupal_strtolower($attribute[1])] = drupal_strtolower(decode_entities($attribute[2]));
}
}
if (isset($candidate['rel']) && $candidate['rel'] == 'alternate') {
if (isset($candidate['href']) && isset($candidate['type']) && strpos($candidate['type'], 'xml') !== FALSE) {
$valid_links[] = $candidate['href'];
}
}
}
return $valid_links;
}
function http_request_create_absolute_url($url, $base_url) {
$url = trim($url);
if (valid_url($url, TRUE)) {
return $url;
}
if (valid_url($url, FALSE)) {
$parsed_url = parse_url($base_url);
if ($parsed_url === FALSE) {
return FALSE;
}
$path = isset($parsed_url['path']) ? $parsed_url['path'] : '';
if (strlen($path) > 0 && substr($path, -1) != '/') {
$path = dirname($path);
}
$cparts = array();
if ($url[0] == '/') {
$cparts = array_filter(explode("/", $url));
}
else {
$path_cparts = array_filter(explode("/", $path));
$url_cparts = array_filter(explode("/", $url));
$cparts = array_merge($path_cparts, $url_cparts);
}
$remove_parts = 0;
$reverse_cparts = array_reverse($cparts);
foreach ($reverse_cparts as $i => &$part) {
if ($part == '.') {
$part = NULL;
}
elseif ($part == '..') {
$part = NULL;
$remove_parts++;
}
elseif ($remove_parts > 0) {
$part = NULL;
$remove_parts--;
}
}
$cparts = array_filter(array_reverse($reverse_cparts));
$path = implode("/", $cparts);
$absolute_url = '';
if (isset($parsed_url['scheme'])) {
$absolute_url = $parsed_url['scheme'] . '://';
}
if (isset($parsed_url['user'])) {
$absolute_url .= $parsed_url['user'];
if (isset($pass)) {
$absolute_url .= ':' . $parsed_url['pass'];
}
$absolute_url .= '@';
}
if (isset($parsed_url['host'])) {
$absolute_url .= $parsed_url['host'] . '/';
}
$absolute_url .= $path;
if (valid_url($absolute_url, TRUE)) {
return $absolute_url;
}
}
return FALSE;
}