update Piwik to version 2.16 (fixes #91)
This commit is contained in:
parent
296343bf3b
commit
d885a4baa9
5833 changed files with 418860 additions and 226988 deletions
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
/**
|
||||
* Piwik - Open source web analytics
|
||||
* Piwik - free/libre analytics platform
|
||||
*
|
||||
* @link http://piwik.org
|
||||
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
|
||||
|
|
@ -13,7 +13,7 @@ use Exception;
|
|||
/**
|
||||
* Contains HTTP client related helper methods that can retrieve content from remote servers
|
||||
* and optionally save to a local file.
|
||||
*
|
||||
*
|
||||
* Used to check for the latest Piwik version and download updates.
|
||||
*
|
||||
*/
|
||||
|
|
@ -21,8 +21,8 @@ class Http
|
|||
{
|
||||
/**
|
||||
* Returns the "best" available transport method for {@link sendHttpRequest()} calls.
|
||||
*
|
||||
* @return string Either `'curl'`, `'fopen'` or `'socket'`.
|
||||
*
|
||||
* @return string|null Either curl, fopen, socket or null if no method is supported.
|
||||
* @api
|
||||
*/
|
||||
public static function getTransportMethod()
|
||||
|
|
@ -47,7 +47,7 @@ class Http
|
|||
|
||||
protected static function isCurlEnabled()
|
||||
{
|
||||
return function_exists('curl_init');
|
||||
return function_exists('curl_init') && function_exists('curl_exec');
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -64,21 +64,34 @@ class Http
|
|||
* Doesn't work w/ `fopen` transport method.
|
||||
* @param bool $getExtendedInfo If true returns the status code, headers & response, if false just the response.
|
||||
* @param string $httpMethod The HTTP method to use. Defaults to `'GET'`.
|
||||
* @param string $httpUsername HTTP Auth username
|
||||
* @param string $httpPassword HTTP Auth password
|
||||
*
|
||||
* @throws Exception if the response cannot be saved to `$destinationPath`, if the HTTP response cannot be sent,
|
||||
* if there are more than 5 redirects or if the request times out.
|
||||
* @return bool|string If `$destinationPath` is not specified the HTTP response is returned on success. `false`
|
||||
* is returned on failure.
|
||||
* If `$getExtendedInfo` is `true` and `$destinationPath` is not specified an array with
|
||||
* the following information is returned on success:
|
||||
*
|
||||
*
|
||||
* - **status**: the HTTP status code
|
||||
* - **headers**: the HTTP headers
|
||||
* - **data**: the HTTP response data
|
||||
*
|
||||
*
|
||||
* `false` is still returned on failure.
|
||||
* @api
|
||||
*/
|
||||
public static function sendHttpRequest($aUrl, $timeout, $userAgent = null, $destinationPath = null, $followDepth = 0, $acceptLanguage = false, $byteRange = false, $getExtendedInfo = false, $httpMethod = 'GET')
|
||||
public static function sendHttpRequest($aUrl,
|
||||
$timeout,
|
||||
$userAgent = null,
|
||||
$destinationPath = null,
|
||||
$followDepth = 0,
|
||||
$acceptLanguage = false,
|
||||
$byteRange = false,
|
||||
$getExtendedInfo = false,
|
||||
$httpMethod = 'GET',
|
||||
$httpUsername = null,
|
||||
$httpPassword = null)
|
||||
{
|
||||
// create output file
|
||||
$file = null;
|
||||
|
|
@ -91,7 +104,7 @@ class Http
|
|||
}
|
||||
|
||||
$acceptLanguage = $acceptLanguage ? 'Accept-Language: ' . $acceptLanguage : '';
|
||||
return self::sendHttpRequestBy(self::getTransportMethod(), $aUrl, $timeout, $userAgent, $destinationPath, $file, $followDepth, $acceptLanguage, $acceptInvalidSslCertificate = false, $byteRange, $getExtendedInfo, $httpMethod);
|
||||
return self::sendHttpRequestBy(self::getTransportMethod(), $aUrl, $timeout, $userAgent, $destinationPath, $file, $followDepth, $acceptLanguage, $acceptInvalidSslCertificate = false, $byteRange, $getExtendedInfo, $httpMethod, $httpUsername, $httpPassword);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -99,7 +112,7 @@ class Http
|
|||
*
|
||||
* @param string $method
|
||||
* @param string $aUrl
|
||||
* @param int $timeout
|
||||
* @param int $timeout in seconds
|
||||
* @param string $userAgent
|
||||
* @param string $destinationPath
|
||||
* @param resource $file
|
||||
|
|
@ -110,6 +123,9 @@ class Http
|
|||
* Doesn't work w/ fopen method.
|
||||
* @param bool $getExtendedInfo True to return status code, headers & response, false if just response.
|
||||
* @param string $httpMethod The HTTP method to use. Defaults to `'GET'`.
|
||||
* @param string $httpUsername HTTP Auth username
|
||||
* @param string $httpPassword HTTP Auth password
|
||||
* @param array|string $requestBody If $httpMethod is 'POST' this may accept an array of variables or a string that needs to be posted
|
||||
*
|
||||
* @throws Exception
|
||||
* @return bool true (or string/array) on success; false on HTTP response error code (1xx or 4xx)
|
||||
|
|
@ -126,9 +142,11 @@ class Http
|
|||
$acceptInvalidSslCertificate = false,
|
||||
$byteRange = false,
|
||||
$getExtendedInfo = false,
|
||||
$httpMethod = 'GET'
|
||||
)
|
||||
{
|
||||
$httpMethod = 'GET',
|
||||
$httpUsername = null,
|
||||
$httpPassword = null,
|
||||
$requestBody = null
|
||||
) {
|
||||
if ($followDepth > 5) {
|
||||
throw new Exception('Too many redirects (' . $followDepth . ')');
|
||||
}
|
||||
|
|
@ -136,6 +154,10 @@ class Http
|
|||
$contentLength = 0;
|
||||
$fileLength = 0;
|
||||
|
||||
if (!empty($requestBody) && is_array($requestBody)) {
|
||||
$requestBody = http_build_query($requestBody);
|
||||
}
|
||||
|
||||
// Piwik services behave like a proxy, so we should act like one.
|
||||
$xff = 'X-Forwarded-For: '
|
||||
. (isset($_SERVER['HTTP_X_FORWARDED_FOR']) && !empty($_SERVER['HTTP_X_FORWARDED_FOR']) ? $_SERVER['HTTP_X_FORWARDED_FOR'] . ',' : '')
|
||||
|
|
@ -156,16 +178,17 @@ class Http
|
|||
$rangeHeader = 'Range: bytes=' . $byteRange[0] . '-' . $byteRange[1] . "\r\n";
|
||||
}
|
||||
|
||||
// proxy configuration
|
||||
$proxyHost = Config::getInstance()->proxy['host'];
|
||||
$proxyPort = Config::getInstance()->proxy['port'];
|
||||
$proxyUser = Config::getInstance()->proxy['username'];
|
||||
$proxyPassword = Config::getInstance()->proxy['password'];
|
||||
list($proxyHost, $proxyPort, $proxyUser, $proxyPassword) = self::getProxyConfiguration($aUrl);
|
||||
|
||||
|
||||
$aUrl = trim($aUrl);
|
||||
|
||||
// other result data
|
||||
$status = null;
|
||||
$status = null;
|
||||
$headers = array();
|
||||
|
||||
$httpAuthIsUsed = !empty($httpUsername) || !empty($httpPassword);
|
||||
|
||||
if ($method == 'socket') {
|
||||
if (!self::isSocketEnabled()) {
|
||||
// can be triggered in tests
|
||||
|
|
@ -177,11 +200,11 @@ class Http
|
|||
throw new Exception('Malformed URL: ' . $aUrl);
|
||||
}
|
||||
|
||||
if ($url['scheme'] != 'http') {
|
||||
if ($url['scheme'] != 'http' && $url['scheme'] != 'https') {
|
||||
throw new Exception('Invalid protocol/scheme: ' . $url['scheme']);
|
||||
}
|
||||
$host = $url['host'];
|
||||
$port = isset($url['port)']) ? $url['port'] : 80;
|
||||
$port = isset($url['port']) ? $url['port'] : 80;
|
||||
$path = isset($url['path']) ? $url['path'] : '/';
|
||||
if (isset($url['query'])) {
|
||||
$path .= '?' . $url['query'];
|
||||
|
|
@ -219,19 +242,32 @@ class Http
|
|||
throw new Exception("Error while connecting to: $host. Please try again later. $errstr");
|
||||
}
|
||||
|
||||
$httpAuth = '';
|
||||
if ($httpAuthIsUsed) {
|
||||
$httpAuth = 'Authorization: Basic ' . base64_encode($httpUsername.':'.$httpPassword) . "\r\n";
|
||||
}
|
||||
|
||||
// send HTTP request header
|
||||
$requestHeader .=
|
||||
"Host: $host" . ($port != 80 ? ':' . $port : '') . "\r\n"
|
||||
. ($httpAuth ? $httpAuth : '')
|
||||
. ($proxyAuth ? $proxyAuth : '')
|
||||
. 'User-Agent: ' . $userAgent . "\r\n"
|
||||
. ($acceptLanguage ? $acceptLanguage . "\r\n" : '')
|
||||
. $xff . "\r\n"
|
||||
. $via . "\r\n"
|
||||
. $rangeHeader
|
||||
. "Connection: close\r\n"
|
||||
. "\r\n";
|
||||
. "Connection: close\r\n";
|
||||
fwrite($fsock, $requestHeader);
|
||||
|
||||
if (strtolower($httpMethod) === 'post' && !empty($requestBody)) {
|
||||
fwrite($fsock, self::buildHeadersForPost($requestBody));
|
||||
fwrite($fsock, "\r\n");
|
||||
fwrite($fsock, $requestBody);
|
||||
} else {
|
||||
fwrite($fsock, "\r\n");
|
||||
}
|
||||
|
||||
$streamMetaData = array('timed_out' => false);
|
||||
@stream_set_blocking($fsock, true);
|
||||
|
||||
|
|
@ -313,7 +349,9 @@ class Http
|
|||
$acceptInvalidSslCertificate = false,
|
||||
$byteRange,
|
||||
$getExtendedInfo,
|
||||
$httpMethod
|
||||
$httpMethod,
|
||||
$httpUsername,
|
||||
$httpPassword
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -359,7 +397,7 @@ class Http
|
|||
|
||||
// determine success or failure
|
||||
@fclose(@$fsock);
|
||||
} else if ($method == 'fopen') {
|
||||
} elseif ($method == 'fopen') {
|
||||
$response = false;
|
||||
|
||||
// we make sure the request takes less than a few seconds to fail
|
||||
|
|
@ -368,11 +406,17 @@ class Http
|
|||
$default_socket_timeout = @ini_get('default_socket_timeout');
|
||||
@ini_set('default_socket_timeout', $timeout);
|
||||
|
||||
$httpAuth = '';
|
||||
if ($httpAuthIsUsed) {
|
||||
$httpAuth = 'Authorization: Basic ' . base64_encode($httpUsername.':'.$httpPassword) . "\r\n";
|
||||
}
|
||||
|
||||
$ctx = null;
|
||||
if (function_exists('stream_context_create')) {
|
||||
$stream_options = array(
|
||||
'http' => array(
|
||||
'header' => 'User-Agent: ' . $userAgent . "\r\n"
|
||||
. ($httpAuth ? $httpAuth : '')
|
||||
. ($acceptLanguage ? $acceptLanguage . "\r\n" : '')
|
||||
. $xff . "\r\n"
|
||||
. $via . "\r\n"
|
||||
|
|
@ -390,12 +434,22 @@ class Http
|
|||
}
|
||||
}
|
||||
|
||||
if (strtolower($httpMethod) === 'post' && !empty($requestBody)) {
|
||||
$postHeader = self::buildHeadersForPost($requestBody);
|
||||
$postHeader .= "\r\n";
|
||||
$stream_options['http']['method'] = 'POST';
|
||||
$stream_options['http']['header'] .= $postHeader;
|
||||
$stream_options['http']['content'] = $requestBody;
|
||||
}
|
||||
|
||||
$ctx = stream_context_create($stream_options);
|
||||
}
|
||||
|
||||
// save to file
|
||||
if (is_resource($file)) {
|
||||
$handle = fopen($aUrl, 'rb', false, $ctx);
|
||||
if (!($handle = fopen($aUrl, 'rb', false, $ctx))) {
|
||||
throw new Exception("Unable to open $aUrl");
|
||||
}
|
||||
while (!feof($handle)) {
|
||||
$response = fread($handle, 8192);
|
||||
$fileLength += strlen($response);
|
||||
|
|
@ -403,7 +457,17 @@ class Http
|
|||
}
|
||||
fclose($handle);
|
||||
} else {
|
||||
$response = file_get_contents($aUrl, 0, $ctx);
|
||||
$response = @file_get_contents($aUrl, 0, $ctx);
|
||||
|
||||
// try to get http status code from response headers
|
||||
if (isset($http_response_header) && preg_match('~^HTTP/(\d\.\d)\s+(\d+)(\s*.*)?~', implode("\n", $http_response_header), $m)) {
|
||||
$status = (int)$m[2];
|
||||
}
|
||||
|
||||
if (!$status && $response === false) {
|
||||
$error = error_get_last();
|
||||
throw new \Exception($error['message']);
|
||||
}
|
||||
$fileLength = strlen($response);
|
||||
}
|
||||
|
||||
|
|
@ -411,7 +475,7 @@ class Http
|
|||
if (!empty($default_socket_timeout)) {
|
||||
@ini_set('default_socket_timeout', $default_socket_timeout);
|
||||
}
|
||||
} else if ($method == 'curl') {
|
||||
} elseif ($method == 'curl') {
|
||||
if (!self::isCurlEnabled()) {
|
||||
// can be triggered in tests
|
||||
throw new Exception("CURL is not enabled in php.ini, but is being used.");
|
||||
|
|
@ -442,8 +506,9 @@ class Http
|
|||
// only get header info if not saving directly to file
|
||||
CURLOPT_HEADER => is_resource($file) ? false : true,
|
||||
CURLOPT_CONNECTTIMEOUT => $timeout,
|
||||
CURLOPT_TIMEOUT => $timeout,
|
||||
);
|
||||
// Case archive.php is triggering archiving on https:// and the certificate is not valid
|
||||
// Case core:archive command is triggering archiving on https:// and the certificate is not valid
|
||||
if ($acceptInvalidSslCertificate) {
|
||||
$curl_options += array(
|
||||
CURLOPT_SSL_VERIFYHOST => false,
|
||||
|
|
@ -455,6 +520,17 @@ class Http
|
|||
@curl_setopt($ch, CURLOPT_NOBODY, true);
|
||||
}
|
||||
|
||||
if (strtolower($httpMethod) === 'post' && !empty($requestBody)) {
|
||||
curl_setopt($ch, CURLOPT_POST, 1);
|
||||
curl_setopt($ch, CURLOPT_POSTFIELDS, $requestBody);
|
||||
}
|
||||
|
||||
if (!empty($httpUsername) && !empty($httpPassword)) {
|
||||
$curl_options += array(
|
||||
CURLOPT_USERPWD => $httpUsername . ':' . $httpPassword,
|
||||
);
|
||||
}
|
||||
|
||||
@curl_setopt_array($ch, $curl_options);
|
||||
self::configCurlCertificate($ch);
|
||||
|
||||
|
|
@ -485,10 +561,11 @@ class Http
|
|||
|
||||
if ($response === true) {
|
||||
$response = '';
|
||||
} else if ($response === false) {
|
||||
} elseif ($response === false) {
|
||||
$errstr = curl_error($ch);
|
||||
if ($errstr != '') {
|
||||
throw new Exception('curl_exec: ' . $errstr);
|
||||
throw new Exception('curl_exec: ' . $errstr
|
||||
. '. Hostname requested was: ' . UrlHelper::getHostFromUrl($aUrl));
|
||||
}
|
||||
$response = '';
|
||||
} else {
|
||||
|
|
@ -496,7 +573,14 @@ class Http
|
|||
// redirects are included in the output html, so we look for the last line that starts w/ HTTP/...
|
||||
// to split the response
|
||||
while (substr($response, 0, 5) == "HTTP/") {
|
||||
list($header, $response) = explode("\r\n\r\n", $response, 2);
|
||||
$split = explode("\r\n\r\n", $response, 2);
|
||||
|
||||
if(count($split) == 2) {
|
||||
list($header, $response) = $split;
|
||||
} else {
|
||||
$response = '';
|
||||
$header = $split;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (explode("\r\n", $header) as $line) {
|
||||
|
|
@ -538,22 +622,30 @@ class Http
|
|||
}
|
||||
}
|
||||
|
||||
private static function buildHeadersForPost($requestBody)
|
||||
{
|
||||
$postHeader = "Content-Type: application/x-www-form-urlencoded\r\n";
|
||||
$postHeader .= "Content-Length: " . strlen($requestBody) . "\r\n";
|
||||
|
||||
return $postHeader;
|
||||
}
|
||||
|
||||
/**
|
||||
* Downloads the next chunk of a specific file. The next chunk's byte range
|
||||
* is determined by the existing file's size and the expected file size, which
|
||||
* is stored in the piwik_option table before starting a download. The expected
|
||||
* file size is obtained through a `HEAD` HTTP request.
|
||||
*
|
||||
*
|
||||
* _Note: this function uses the **Range** HTTP header to accomplish downloading in
|
||||
* parts. Not every server supports this header._
|
||||
*
|
||||
*
|
||||
* The proper use of this function is to call it once per request. The browser
|
||||
* should continue to send requests to Piwik which will in turn call this method
|
||||
* until the file has completely downloaded. In this way, the user can be informed
|
||||
* of a download's progress.
|
||||
*
|
||||
*
|
||||
* **Example Usage**
|
||||
*
|
||||
*
|
||||
* ```
|
||||
* // browser JavaScript
|
||||
* var downloadFile = function (isStart) {
|
||||
|
|
@ -571,10 +663,10 @@ class Http
|
|||
* });
|
||||
* ajax.send();
|
||||
* }
|
||||
*
|
||||
*
|
||||
* downloadFile(true);
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* ```
|
||||
* // PHP controller action
|
||||
* public function myAction()
|
||||
|
|
@ -584,7 +676,7 @@ class Http
|
|||
* Http::downloadChunk("http://bigfiles.com/averybigfile.zip", $outputPath, $isStart == 1);
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
*
|
||||
* @param string $url The url to download from.
|
||||
* @param string $outputPath The path to the file to save/append to.
|
||||
* @param bool $isContinuation `true` if this is the continuation of a download,
|
||||
|
|
@ -751,4 +843,47 @@ class Http
|
|||
}
|
||||
return $str;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the If-Modified-Since HTTP header if it can be found. If it cannot be
|
||||
* found, an empty string is returned.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function getModifiedSinceHeader()
|
||||
{
|
||||
$modifiedSince = '';
|
||||
if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE'])) {
|
||||
$modifiedSince = $_SERVER['HTTP_IF_MODIFIED_SINCE'];
|
||||
|
||||
// strip any trailing data appended to header
|
||||
if (false !== ($semicolonPos = strpos($modifiedSince, ';'))) {
|
||||
$modifiedSince = substr($modifiedSince, 0, $semicolonPos);
|
||||
}
|
||||
}
|
||||
return $modifiedSince;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns Proxy to use for connecting via HTTP to given URL
|
||||
*
|
||||
* @param string $url
|
||||
* @return array
|
||||
*/
|
||||
private static function getProxyConfiguration($url)
|
||||
{
|
||||
$hostname = UrlHelper::getHostFromUrl($url);
|
||||
|
||||
if (Url::isLocalHost($hostname)) {
|
||||
return array(null, null, null, null);
|
||||
}
|
||||
|
||||
// proxy configuration
|
||||
$proxyHost = Config::getInstance()->proxy['host'];
|
||||
$proxyPort = Config::getInstance()->proxy['port'];
|
||||
$proxyUser = Config::getInstance()->proxy['username'];
|
||||
$proxyPassword = Config::getInstance()->proxy['password'];
|
||||
|
||||
return array($proxyHost, $proxyPort, $proxyUser, $proxyPassword);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue