<?php
/**
* GET an HTTP URL to retrieve its content
- * Uses the cURL library or a fallback method
+ * Uses the cURL library or a fallback method
*
- * @param string $url URL to get (http://...)
- * @param int $timeout network timeout (in seconds)
- * @param int $maxBytes maximum downloaded bytes (default: 4 MiB)
+ * @param string $url URL to get (http://...)
+ * @param int $timeout network timeout (in seconds)
+ * @param int $maxBytes maximum downloaded bytes (default: 4 MiB)
+ * @param callable|string $curlWriteFunction Optional callback called during the download (cURL CURLOPT_WRITEFUNCTION).
+ * Can be used to add download conditions on the headers (response code, content type, etc.).
*
* @return array HTTP response headers, downloaded content
*
* @see http://stackoverflow.com/q/9183178
* @see http://stackoverflow.com/q/1462720
*/
-function get_http_response($url, $timeout = 30, $maxBytes = 4194304)
+function get_http_response($url, $timeout = 30, $maxBytes = 4194304, $curlWriteFunction = null)
{
$urlObj = new Url($url);
$cleanUrl = $urlObj->idnToAscii();
curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
curl_setopt($ch, CURLOPT_USERAGENT, $userAgent);
+ if (is_callable($curlWriteFunction)) {
+ curl_setopt($ch, CURLOPT_WRITEFUNCTION, $curlWriteFunction);
+ }
+
// Max download size management
- curl_setopt($ch, CURLOPT_BUFFERSIZE, 1024);
+ curl_setopt($ch, CURLOPT_BUFFERSIZE, 1024*16);
curl_setopt($ch, CURLOPT_NOPROGRESS, false);
curl_setopt($ch, CURLOPT_PROGRESSFUNCTION,
function($arg0, $arg1, $arg2, $arg3, $arg4 = 0) use ($maxBytes)
$content = substr($response, $headSize);
$headers = array();
foreach (preg_split('~[\r\n]+~', $rawHeadersLastRedir) as $line) {
- if (empty($line) or ctype_space($line)) {
+ if (empty($line) || ctype_space($line)) {
continue;
}
$splitLine = explode(': ', $line, 2);
// Keep forwarded port
if (strpos($server['HTTP_X_FORWARDED_PORT'], ',') !== false) {
$ports = explode(',', $server['HTTP_X_FORWARDED_PORT']);
- $port = ':' . trim($ports[0]);
+ $port = trim($ports[0]);
+ } else {
+ $port = $server['HTTP_X_FORWARDED_PORT'];
+ }
+
+ // This is a workaround for proxies that don't forward the scheme properly.
+ // Connecting over port 443 has to be in HTTPS.
+ // See https://github.com/shaarli/Shaarli/issues/1022
+ if ($port == '443') {
+ $scheme = 'https';
+ }
+
+ if (($scheme == 'http' && $port != '80')
+ || ($scheme == 'https' && $port != '443')
+ ) {
+ $port = ':' . $port;
} else {
- $port = ':' . $server['HTTP_X_FORWARDED_PORT'];
+ $port = '';
}
}
- return $scheme.'://'.$server['SERVER_NAME'].$port;
+ if (isset($server['HTTP_X_FORWARDED_HOST'])) {
+ // Keep forwarded host
+ if (strpos($server['HTTP_X_FORWARDED_HOST'], ',') !== false) {
+ $hosts = explode(',', $server['HTTP_X_FORWARDED_HOST']);
+ $host = trim($hosts[0]);
+ } else {
+ $host = $server['HTTP_X_FORWARDED_HOST'];
+ }
+ } else {
+ $host = $server['SERVER_NAME'];
+ }
+
+ return $scheme.'://'.$host.$port;
}
// SSL detection
}
return index_url($server);
}
+
+/**
+ * Retrieve the initial IP forwarded by the reverse proxy.
+ *
+ * Inspired from: https://github.com/zendframework/zend-http/blob/master/src/PhpEnvironment/RemoteAddress.php
+ *
+ * @param array $server $_SERVER array which contains HTTP headers.
+ * @param array $trustedIps List of trusted IP from the configuration.
+ *
+ * @return string|bool The forwarded IP, or false if none could be extracted.
+ */
+function getIpAddressFromProxy($server, $trustedIps)
+{
+ $forwardedIpHeader = 'HTTP_X_FORWARDED_FOR';
+ if (empty($server[$forwardedIpHeader])) {
+ return false;
+ }
+
+ $ips = preg_split('/\s*,\s*/', $server[$forwardedIpHeader]);
+ $ips = array_diff($ips, $trustedIps);
+ if (empty($ips)) {
+ return false;
+ }
+
+ return array_pop($ips);
+}
+
+
+/**
+ * Return an identifier based on the advertised client IP address(es)
+ *
+ * This aims at preventing session hijacking from users behind the same proxy
+ * by relying on HTTP headers.
+ *
+ * See:
+ * - https://secure.php.net/manual/en/reserved.variables.server.php
+ * - https://stackoverflow.com/questions/3003145/how-to-get-the-client-ip-address-in-php
+ * - https://stackoverflow.com/questions/12233406/preventing-session-hijacking
+ * - https://stackoverflow.com/questions/21354859/trusting-x-forwarded-for-to-identify-a-visitor
+ *
+ * @param array $server The $_SERVER array
+ *
+ * @return string An identifier based on client IP address information
+ */
+function client_ip_id($server)
+{
+ $ip = $server['REMOTE_ADDR'];
+
+ if (isset($server['HTTP_X_FORWARDED_FOR'])) {
+ $ip = $ip . '_' . $server['HTTP_X_FORWARDED_FOR'];
+ }
+ if (isset($server['HTTP_CLIENT_IP'])) {
+ $ip = $ip . '_' . $server['HTTP_CLIENT_IP'];
+ }
+ return $ip;
+}
+
+
+/**
+ * Returns true if Shaarli's currently browsed in HTTPS.
+ * Supports reverse proxies (if the headers are correctly set).
+ *
+ * @param array $server $_SERVER.
+ *
+ * @return bool true if HTTPS, false otherwise.
+ */
+function is_https($server)
+{
+
+ if (isset($server['HTTP_X_FORWARDED_PORT'])) {
+ // Keep forwarded port
+ if (strpos($server['HTTP_X_FORWARDED_PORT'], ',') !== false) {
+ $ports = explode(',', $server['HTTP_X_FORWARDED_PORT']);
+ $port = trim($ports[0]);
+ } else {
+ $port = $server['HTTP_X_FORWARDED_PORT'];
+ }
+
+ if ($port == '443') {
+ return true;
+ }
+ }
+
+ return ! empty($server['HTTPS']);
+}