Version 4.5.0
This commit is contained in:
1 parent
1a0c7fd3c2
commit
f79dcf067a
3791 files changed
+248032
-76372
No files matched your search
@@ -12,7 +12,7 @@
|
||||
namespace IPS\Http;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
@@ -66,7 +66,7 @@ class _Ranges extends Response
|
||||
$ranges = explode( ",", $range );
|
||||
|
||||
/* Did we only find one range? */
|
||||
if( count($ranges) == 1 )
|
||||
if( \count($ranges) == 1 )
|
||||
{
|
||||
\IPS\Log::debug( "Only one range detected", 'ranges' );
|
||||
|
||||
@@ -165,7 +165,7 @@ class _Ranges extends Response
|
||||
}
|
||||
|
||||
/* Did we still only have one range? */
|
||||
if( count($the_responses) == 1 )
|
||||
if( \count($the_responses) == 1 )
|
||||
{
|
||||
$length = $the_responses[0][2];
|
||||
$start_range = $the_responses[0][0];
|
||||
@@ -185,7 +185,7 @@ class _Ranges extends Response
|
||||
}
|
||||
}
|
||||
/* Now we're working with something exciting */
|
||||
else if( count($the_responses) > 1 )
|
||||
else if( \count($the_responses) > 1 )
|
||||
{
|
||||
$content_length = 0;
|
||||
|
||||
@@ -229,7 +229,7 @@ class _Ranges extends Response
|
||||
}
|
||||
|
||||
/* Not sending multiple ranges */
|
||||
if( !isset( $the_responses ) OR !count( $the_responses ) )
|
||||
if( !isset( $the_responses ) OR !\count( $the_responses ) )
|
||||
{
|
||||
\IPS\Output::i()->sendHeader( 'Content-Type: ' . \IPS\File::getMimeType( $file->originalFilename ) );
|
||||
\IPS\Output::i()->sendHeader( 'Content-Disposition: ' . \IPS\Output::getContentDisposition( 'attachment', $file->originalFilename ) );
|
||||
@@ -245,7 +245,7 @@ class _Ranges extends Response
|
||||
$throttle = ( $throttle > 0 ) ? ( $throttle * 1024 ) : NULL;
|
||||
|
||||
/* Do we have multiple ranges? */
|
||||
if( isset( $the_responses ) AND count($the_responses) )
|
||||
if( isset( $the_responses ) AND \count($the_responses) )
|
||||
{
|
||||
/* Loop over each range */
|
||||
foreach( $the_responses as $part )
|
||||
@@ -285,7 +285,7 @@ class _Ranges extends Response
|
||||
$ranges = explode( ",", $range );
|
||||
|
||||
/* Did we only find one range? */
|
||||
if( count($ranges) == 1 )
|
||||
if( \count($ranges) == 1 )
|
||||
{
|
||||
/* Get the actual range start and end */
|
||||
list( $start_range, $end_range ) = explode( "-", $range );
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
namespace IPS\Http\Request;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
@@ -53,6 +53,11 @@ class _Curl
|
||||
* @brief Follow redirects?
|
||||
*/
|
||||
protected $followRedirects = TRUE;
|
||||
|
||||
/**
|
||||
* @brief Allowed protocols
|
||||
*/
|
||||
protected $allowedProtocols = array();
|
||||
|
||||
/**
|
||||
* @brief Data sent
|
||||
@@ -66,16 +71,18 @@ class _Curl
|
||||
* @param int $timeout Timeout (in seconds)
|
||||
* @param string $httpVersion HTTP Version
|
||||
* @param bool|int $followRedirects Automatically follow redirects? If a number is provided, will follow up to that number of redirects
|
||||
* @param array|null $allowedProtocols Protocols allowed (if NULL we default to array( 'http', 'https', 'ftp', 'scp', 'sftp', 'ftps' ))
|
||||
* @return void
|
||||
*/
|
||||
public function __construct( $url, $timeout=5, $httpVersion=NULL, $followRedirects=TRUE )
|
||||
public function __construct( $url, $timeout=5, $httpVersion=NULL, $followRedirects=TRUE, $allowedProtocols=NULL )
|
||||
{
|
||||
/* Init */
|
||||
$this->url = $url;
|
||||
$this->curl = curl_init();
|
||||
$this->httpVersion = $httpVersion ?: '1.1';
|
||||
$this->timeout = $timeout;
|
||||
$this->followRedirects = $followRedirects;
|
||||
$this->url = $url;
|
||||
$this->curl = curl_init();
|
||||
$this->httpVersion = $httpVersion ?: '1.1';
|
||||
$this->timeout = $timeout;
|
||||
$this->followRedirects = $followRedirects;
|
||||
$this->allowedProtocols = $allowedProtocols ?: array( 'http', 'https', 'ftp', 'scp', 'sftp', 'ftps' );
|
||||
|
||||
/* Need to adjust if this is FTP */
|
||||
$user = null;
|
||||
@@ -155,7 +162,7 @@ class _Curl
|
||||
*
|
||||
* @param string $username Username
|
||||
* @param string $password Password
|
||||
* @return \IPS\Http\Request\Curl
|
||||
* @return static
|
||||
*/
|
||||
public function login( $username, $password )
|
||||
{
|
||||
@@ -171,7 +178,7 @@ class _Curl
|
||||
* Set Headers
|
||||
*
|
||||
* @param array $headers Key/Value pair of headers
|
||||
* @return \IPS\Http\Request\Curl
|
||||
* @return static
|
||||
*/
|
||||
public function setHeaders( $headers )
|
||||
{
|
||||
@@ -220,7 +227,7 @@ class _Curl
|
||||
* Toggle SSL checks
|
||||
*
|
||||
* @param boolean $value True will enable SSL checks, false will disable them
|
||||
* @return \IPS\Http\Request\Socket
|
||||
* @return static
|
||||
*/
|
||||
public function sslCheck( $value=TRUE )
|
||||
{
|
||||
@@ -235,7 +242,7 @@ class _Curl
|
||||
/**
|
||||
* Force TLS
|
||||
*
|
||||
* @return \IPS\Http\Request\Socket
|
||||
* @return static
|
||||
*/
|
||||
public function forceTls()
|
||||
{
|
||||
@@ -244,17 +251,17 @@ class _Curl
|
||||
{
|
||||
if ( version_compare( $openSSLVersionData[1], '1.0.1', '>=' ) )
|
||||
{
|
||||
if ( !defined('CURL_SSLVERSION_TLSv1_2') ) // constant not defined in PHP < 5.5
|
||||
if ( !\defined('CURL_SSLVERSION_TLSv1_2') ) // constant not defined in PHP < 5.5
|
||||
{
|
||||
define( 'CURL_SSLVERSION_TLSv1_2', 6 );
|
||||
\define( 'CURL_SSLVERSION_TLSv1_2', 6 );
|
||||
}
|
||||
curl_setopt( $this->curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1_2 );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( !defined('CURL_SSLVERSION_TLSv1') ) // constant not defined in PHP < 5.5
|
||||
if ( !\defined('CURL_SSLVERSION_TLSv1') ) // constant not defined in PHP < 5.5
|
||||
{
|
||||
define( 'CURL_SSLVERSION_TLSv1', 1 );
|
||||
\define( 'CURL_SSLVERSION_TLSv1', 1 );
|
||||
}
|
||||
curl_setopt( $this->curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1 );
|
||||
}
|
||||
@@ -266,6 +273,7 @@ class _Curl
|
||||
/**
|
||||
* HTTP GET
|
||||
*
|
||||
* @param mixed $data Data to send with the GET request
|
||||
* @return \IPS\Http\Response
|
||||
* @throws \IPS\Http\Request\CurlException
|
||||
*/
|
||||
@@ -353,7 +361,7 @@ class _Curl
|
||||
protected function _dataToSend( $data=NULL )
|
||||
{
|
||||
$this->dataForLog = $data;
|
||||
if ( !$this->modifiedContentType and is_array( $data ) )
|
||||
if ( !$this->modifiedContentType and \is_array( $data ) )
|
||||
{
|
||||
$data = http_build_query( $data, '', '&' );
|
||||
}
|
||||
@@ -372,7 +380,7 @@ class _Curl
|
||||
$output = curl_exec( $this->curl );
|
||||
|
||||
/* Log - but because the output can be large, only do this if we explicitly have debug logging enabled */
|
||||
if ( defined('\IPS\DEBUG_LOG') and \IPS\DEBUG_LOG )
|
||||
if ( \defined('\IPS\DEBUG_LOG') and \IPS\DEBUG_LOG )
|
||||
{
|
||||
\IPS\Log::debug( "\n\n------------------------------------\ncURL REQUEST: {$this->url}\n------------------------------------\n\n" . var_export( $this->dataForLog, TRUE ) . "\n\n------------------------------------\nRESPONSE\n------------------------------------\n\n" . $output, 'request' );
|
||||
}
|
||||
@@ -380,7 +388,7 @@ class _Curl
|
||||
/* Errors? */
|
||||
if ( $output === FALSE )
|
||||
{
|
||||
throw new CurlException( curl_error( $this->curl ), curl_errno( $this->curl ) );
|
||||
throw new CurlException( $this->url . "\n" . curl_error( $this->curl ), curl_errno( $this->curl ) );
|
||||
}
|
||||
|
||||
/* If this is FTP we need to fudge the headers a little */
|
||||
@@ -407,17 +415,30 @@ class _Curl
|
||||
$response = $this->_execute();
|
||||
|
||||
/* Either return it or follow it */
|
||||
if ( $this->followRedirects and in_array( $response->httpResponseCode, array( 301, 302, 303, 307, 308 ) ) )
|
||||
if ( $this->followRedirects and \in_array( $response->httpResponseCode, array( 301, 302, 303, 307, 308 ) ) )
|
||||
{
|
||||
/* Fix missing hostname in location */
|
||||
$location = $response->httpHeaders['Location'];
|
||||
foreach( $response->httpHeaders as $k => $v )
|
||||
{
|
||||
if( mb_strtolower( $k ) == 'location' )
|
||||
{
|
||||
$location = $v;
|
||||
}
|
||||
}
|
||||
|
||||
if( parse_url( $location, PHP_URL_HOST ) === NULL )
|
||||
{
|
||||
$location = $this->url->data['scheme'] . '://' . $this->url->data['host'] . $location;
|
||||
}
|
||||
|
||||
$newRequest = \IPS\Http\Url::external( $location )->request( $this->timeout, $this->httpVersion, is_int( $this->followRedirects ) ? ( $this->followRedirects - 1 ) : $this->followRedirects );
|
||||
$newRequest = \IPS\Http\Url::external( $location );
|
||||
|
||||
if( !\in_array( $newRequest->data['scheme'], $this->allowedProtocols ) )
|
||||
{
|
||||
throw new \IPS\Http\Request\Exception( 'protocol_not_followed' );
|
||||
}
|
||||
|
||||
$newRequest = $newRequest->request( $this->timeout, $this->httpVersion, \is_int( $this->followRedirects ) ? ( $this->followRedirects - 1 ) : $this->followRedirects );
|
||||
return $newRequest->$method( $params );
|
||||
}
|
||||
return $response;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
namespace IPS\Http\Request;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
namespace IPS\Http\Request;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
@@ -51,6 +51,11 @@ class _Sockets
|
||||
* @brief Follow redirects?
|
||||
*/
|
||||
protected $followRedirects = TRUE;
|
||||
|
||||
/**
|
||||
* @brief Allowed protocols
|
||||
*/
|
||||
protected $allowedProtocols = array();
|
||||
|
||||
/**
|
||||
* Contructor
|
||||
@@ -59,15 +64,17 @@ class _Sockets
|
||||
* @param int $timeout Timeout (in seconds)
|
||||
* @param string $httpVersion HTTP Version
|
||||
* @param bool|int $followRedirects Automatically follow redirects? If a number is provided, will follow up to that number of redirects
|
||||
* @param array|null $allowedProtocols Protocols allowed (if NULL we default to array( 'http', 'https', 'ftp', 'scp', 'sftp', 'ftps' ))
|
||||
* @return void
|
||||
*/
|
||||
public function __construct( $url, $timeout, $httpVersion, $followRedirects )
|
||||
public function __construct( $url, $timeout=5, $httpVersion=NULL, $followRedirects=TRUE, $allowedProtocols=NULL )
|
||||
{
|
||||
$this->url = $url;
|
||||
$this->context = stream_context_create();
|
||||
$this->httpVersion = $httpVersion ?: '1.1';
|
||||
$this->timeout = $timeout;
|
||||
$this->followRedirects = $followRedirects;
|
||||
$this->url = $url;
|
||||
$this->context = stream_context_create();
|
||||
$this->httpVersion = $httpVersion ?: '1.1';
|
||||
$this->timeout = $timeout;
|
||||
$this->followRedirects = $followRedirects;
|
||||
$this->allowedProtocols = $allowedProtocols ?: array( 'http', 'https', 'ftp', 'scp', 'sftp', 'ftps' );
|
||||
|
||||
/* Set our basic settings */
|
||||
stream_context_set_option( $this->context, array(
|
||||
@@ -87,9 +94,9 @@ class _Sockets
|
||||
/**
|
||||
* Login
|
||||
*
|
||||
* @param string Username
|
||||
* @param string Password
|
||||
* @return \IPS\Http\Request\Socket (for daisy chaining)
|
||||
* @param string $username Username
|
||||
* @param string $password Password
|
||||
* @return static (for daisy chaining)
|
||||
*/
|
||||
public function login( $username, $password )
|
||||
{
|
||||
@@ -100,8 +107,8 @@ class _Sockets
|
||||
/**
|
||||
* Set Headers
|
||||
*
|
||||
* @param array Key/Value pair of headers
|
||||
* @return \IPS\Http\Request\Socket
|
||||
* @param array $headers Key/Value pair of headers
|
||||
* @return static
|
||||
*/
|
||||
public function setHeaders( $headers )
|
||||
{
|
||||
@@ -113,7 +120,7 @@ class _Sockets
|
||||
* Toggle SSL checks
|
||||
*
|
||||
* @param boolean $value True will enable SSL checks, false will disable them
|
||||
* @return \IPS\Http\Request\Socket
|
||||
* @return static
|
||||
*/
|
||||
public function sslCheck( $value=TRUE )
|
||||
{
|
||||
@@ -130,11 +137,11 @@ class _Sockets
|
||||
/**
|
||||
* Force TLS
|
||||
*
|
||||
* @return \IPS\Http\Request\Socket
|
||||
* @return static
|
||||
*/
|
||||
public function forceTls()
|
||||
{
|
||||
if ( defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT') )
|
||||
if ( \defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT') )
|
||||
{
|
||||
stream_context_set_option( $this->context, array(
|
||||
'ssl' => array(
|
||||
@@ -142,7 +149,7 @@ class _Sockets
|
||||
)
|
||||
) );
|
||||
}
|
||||
elseif ( defined('STREAM_CRYPTO_METHOD_TLS_CLIENT') )
|
||||
elseif ( \defined('STREAM_CRYPTO_METHOD_TLS_CLIENT') )
|
||||
{
|
||||
stream_context_set_option( $this->context, array(
|
||||
'ssl' => array(
|
||||
@@ -168,7 +175,7 @@ class _Sockets
|
||||
$method = mb_strtoupper( $method );
|
||||
|
||||
/* The data (string or array) will be the first parameter */
|
||||
if ( isset( $params[0] ) && is_array( $params[0] ) )
|
||||
if ( isset( $params[0] ) && \is_array( $params[0] ) )
|
||||
{
|
||||
$this->setHeaders( array( 'Content-Type' => 'application/x-www-form-urlencoded' ) );
|
||||
$data = http_build_query( $params[0], '', '&' );
|
||||
@@ -218,7 +225,7 @@ class _Sockets
|
||||
|
||||
/* Get the location */
|
||||
$location = $this->url->data['path'] ? \IPS\Http\Url::encodeComponent( \IPS\Http\Url::COMPONENT_PATH, $this->url->data['path'] ) : '';
|
||||
$location .= ( count( $this->url->queryString ) ) ? '?' . \IPS\Http\Url::convertQueryAsArrayToString( $this->url->queryString, true ) : '';
|
||||
$location .= ( \count( $this->url->queryString ) ) ? '?' . \IPS\Http\Url::convertQueryAsArrayToString( $this->url->queryString, true ) : '';
|
||||
$location .= $this->url->data['fragment'] ? '#' . \IPS\Http\Url::encodeComponent( \IPS\Http\Url::COMPONENT_FRAGMENT, $this->url->data['fragment'] ) : '';
|
||||
|
||||
/* Send request */
|
||||
@@ -255,7 +262,7 @@ class _Sockets
|
||||
\fclose( $resource );
|
||||
|
||||
/* Log - but because the output can be large, only do this if we explicitly have debug logging enabled */
|
||||
if ( defined('\IPS\DEBUG_LOG') and \IPS\DEBUG_LOG )
|
||||
if ( \defined('\IPS\DEBUG_LOG') and \IPS\DEBUG_LOG )
|
||||
{
|
||||
\IPS\Log::debug( "\n\n------------------------------------\nSOCKETS REQUEST: {$this->url}\n------------------------------------\n\n{$request}\n\n------------------------------------\nRESPONSE\n------------------------------------\n\n" . $response, 'request' );
|
||||
}
|
||||
@@ -264,17 +271,30 @@ class _Sockets
|
||||
$response = new \IPS\Http\Response( $response );
|
||||
|
||||
/* Either return it or follow it */
|
||||
if ( $this->followRedirects and in_array( $response->httpResponseCode, array( 301, 302, 303, 307, 308 ) ) )
|
||||
if ( $this->followRedirects and \in_array( $response->httpResponseCode, array( 301, 302, 303, 307, 308 ) ) )
|
||||
{
|
||||
/* Fix missing hostname in location */
|
||||
$location = $response->httpHeaders['Location'];
|
||||
foreach( $response->httpHeaders as $k => $v )
|
||||
{
|
||||
if( mb_strtolower( $k ) == 'location' )
|
||||
{
|
||||
$location = $v;
|
||||
}
|
||||
}
|
||||
|
||||
if( parse_url( $location, PHP_URL_HOST ) === NULL )
|
||||
{
|
||||
$location = $this->url->data['scheme'] . '://' . $this->url->data['host'] . $location;
|
||||
}
|
||||
|
||||
$newRequest = \IPS\Http\Url::external( $location )->request( $this->timeout, $this->httpVersion, is_int( $this->followRedirects ) ? ( $this->followRedirects - 1 ) : $this->followRedirects );
|
||||
$newRequest = \IPS\Http\Url::external( $location );
|
||||
|
||||
if( !\in_array( $newRequest->data['scheme'], $this->allowedProtocols ) )
|
||||
{
|
||||
throw new \IPS\Http\Request\Exception( 'protocol_not_followed' );
|
||||
}
|
||||
|
||||
$newRequest = $newRequest->request( $this->timeout, $this->httpVersion, \is_int( $this->followRedirects ) ? ( $this->followRedirects - 1 ) : $this->followRedirects );
|
||||
return $newRequest->$method( $params );
|
||||
}
|
||||
return $response;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
namespace IPS\Http;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
@@ -88,7 +88,7 @@ class _Response
|
||||
$content = trim( mb_substr( $content, $firstLineBreak ) );
|
||||
}
|
||||
/* Or a 3xx response with another response */
|
||||
elseif ( mb_substr( $this->httpResponseCode, 0, 1 ) == 3 AND mb_strpos( mb_substr( $content, $firstLineBreak ), "HTTP/" ) !== FALSE )
|
||||
elseif ( mb_substr( $this->httpResponseCode, 0, 1 ) == 3 AND preg_match( "/^HTTP\//m", mb_substr( $content, $firstLineBreak ) ) )
|
||||
{
|
||||
$_content = trim( mb_substr( $content, $firstLineBreak ) );
|
||||
if ( $_content )
|
||||
@@ -139,6 +139,12 @@ class _Response
|
||||
}
|
||||
}
|
||||
|
||||
/* Fix location */
|
||||
if( isset( $this->httpHeaders['location'] ) )
|
||||
{
|
||||
$this->httpHeaders['Location'] = $this->httpHeaders['location'];
|
||||
}
|
||||
|
||||
/* Fix chunked encoding */
|
||||
if( isset( $this->httpHeaders['transfer-encoding'] ) )
|
||||
{
|
||||
|
||||
+118
-97
@@ -11,7 +11,7 @@
|
||||
namespace IPS\Http;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
@@ -28,20 +28,74 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
*/
|
||||
class _Url
|
||||
{
|
||||
/**
|
||||
* @brief Automatically determine the protocol
|
||||
*/
|
||||
const PROTOCOL_AUTOMATIC = 0;
|
||||
|
||||
/**
|
||||
* @brief Use https://
|
||||
*/
|
||||
const PROTOCOL_HTTPS = 1;
|
||||
|
||||
/**
|
||||
* @brief Use http://
|
||||
*/
|
||||
const PROTOCOL_HTTP = 2;
|
||||
|
||||
/**
|
||||
* @brief Use // (protoool-relative)
|
||||
*/
|
||||
const PROTOCOL_RELATIVE = 3;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Scheme component
|
||||
*/
|
||||
const COMPONENT_SCHEME = 'scheme';
|
||||
|
||||
/**
|
||||
* @brief Username component
|
||||
*/
|
||||
const COMPONENT_USERNAME = 'user';
|
||||
|
||||
/**
|
||||
* @brief Password component
|
||||
*/
|
||||
const COMPONENT_PASSWORD = 'pass';
|
||||
|
||||
/**
|
||||
* @brief Host component
|
||||
*/
|
||||
const COMPONENT_HOST = 'host';
|
||||
|
||||
/**
|
||||
* @brief Port component
|
||||
*/
|
||||
const COMPONENT_PORT = 'port';
|
||||
|
||||
/**
|
||||
* @brief Path component
|
||||
*/
|
||||
const COMPONENT_PATH = 'path';
|
||||
|
||||
/**
|
||||
* @brief Querystring component
|
||||
*/
|
||||
const COMPONENT_QUERY = 'query';
|
||||
|
||||
/**
|
||||
* @brief Query string key
|
||||
*/
|
||||
const COMPONENT_QUERY_KEY = 'queryKey';
|
||||
|
||||
/**
|
||||
* @brief Query string value
|
||||
*/
|
||||
const COMPONENT_QUERY_VALUE = 'queryValue';
|
||||
|
||||
/**
|
||||
* @brief Fragment component
|
||||
*/
|
||||
const COMPONENT_FRAGMENT = 'fragment';
|
||||
|
||||
/* !Factory Methods */
|
||||
@@ -89,36 +143,14 @@ class _Url
|
||||
return \IPS\Http\Url\Internal::createInternalFromComponents( 'front', $protocol, $queryString ? 'index.php' : '', $queryString );
|
||||
}
|
||||
/* Admin */
|
||||
elseif ( $base === 'admin' )
|
||||
elseif ( $base === 'admin' or $base === 'admin_redirect' )
|
||||
{
|
||||
/* Front: Never disclose adsess in front pages */
|
||||
if ( \IPS\Dispatcher::hasInstance() and \IPS\Dispatcher::i()->controllerLocation !== 'admin' )
|
||||
{
|
||||
/* If there is a query string (like a link from an error page, pass through the redirector so we don't disclose the location */
|
||||
if ( $queryString )
|
||||
{
|
||||
return \IPS\Http\Url\Internal::createInternalFromComponents( 'front', $protocol, 'index.php', 'app=core&module=system&controller=redirect&do=admin&_data=' . base64_encode( $queryString ) );
|
||||
}
|
||||
/* Or if it's just a normal link like in the user bar, show that */
|
||||
else
|
||||
{
|
||||
return \IPS\Http\Url\Internal::createInternalFromComponents(
|
||||
'admin',
|
||||
$protocol,
|
||||
\IPS\CP_DIRECTORY . '/'
|
||||
);
|
||||
}
|
||||
}
|
||||
/* Within ACP */
|
||||
else
|
||||
{
|
||||
return \IPS\Http\Url\Internal::createInternalFromComponents(
|
||||
'admin',
|
||||
$protocol,
|
||||
\IPS\CP_DIRECTORY . '/',
|
||||
array( 'adsess' => session_id() ) + static::convertQueryAsStringToArray( $queryString )
|
||||
);
|
||||
}
|
||||
return \IPS\Http\Url\Internal::createInternalFromComponents(
|
||||
'admin',
|
||||
$protocol,
|
||||
\IPS\CP_DIRECTORY . '/',
|
||||
static::convertQueryAsStringToArray( $queryString )
|
||||
);
|
||||
}
|
||||
/* None */
|
||||
else
|
||||
@@ -145,7 +177,7 @@ class _Url
|
||||
* @param string $url
|
||||
* @return \IPS\Http\Url
|
||||
*/
|
||||
public static function ips( $url )
|
||||
final public static function ips( $url )
|
||||
{
|
||||
return new static( "https://remoteservices.invisionpower.com/{$url}/?version=" . \IPS\Application::getAvailableVersion('core') );
|
||||
}
|
||||
@@ -175,12 +207,12 @@ class _Url
|
||||
$obj->data[ static::COMPONENT_PATH ] = $path;
|
||||
$obj->data[ static::COMPONENT_FRAGMENT ] = $fragment;
|
||||
|
||||
if ( is_array( $query ) )
|
||||
if ( \is_array( $query ) )
|
||||
{
|
||||
$obj->data[ static::COMPONENT_QUERY ] = static::convertQueryAsArrayToString( $query );
|
||||
$obj->queryString = $query;
|
||||
}
|
||||
elseif ( is_string( $query ) )
|
||||
elseif ( \is_string( $query ) )
|
||||
{
|
||||
$obj->data[ static::COMPONENT_QUERY ] = $query;
|
||||
$obj->queryString = static::convertQueryAsStringToArray( $query );
|
||||
@@ -298,10 +330,10 @@ class _Url
|
||||
* @li INVALID_SCHEME The scheme was invalid
|
||||
* @li INVALID_USERNAME The username was invalid
|
||||
* @li INVALID_PASSWORD The password was invalid
|
||||
* @LI INVALID_HOST The host name was invalid
|
||||
* @LI INVALID_PATH The path was invalid
|
||||
* @LI INVALID_QUERY The query was invalid
|
||||
* @LI INVALID_FRAGMENT The fragment was invalid
|
||||
* @li INVALID_HOST The host name was invalid
|
||||
* @li INVALID_PATH The path was invalid
|
||||
* @li INVALID_QUERY The query was invalid
|
||||
* @li INVALID_FRAGMENT The fragment was invalid
|
||||
*/
|
||||
public function __construct( $url = NULL, $autoEncode = FALSE )
|
||||
{
|
||||
@@ -370,7 +402,7 @@ class _Url
|
||||
{
|
||||
$newQueryArray = $this->queryString;
|
||||
|
||||
if ( is_array( $keyOrArray ) )
|
||||
if ( \is_array( $keyOrArray ) )
|
||||
{
|
||||
foreach ( $keyOrArray as $k => $v )
|
||||
{
|
||||
@@ -440,7 +472,7 @@ class _Url
|
||||
$port = '';
|
||||
if ( $this->data[ static::COMPONENT_PORT ] )
|
||||
{
|
||||
$port = ':' . intval( $this->data[ static::COMPONENT_PORT ] );
|
||||
$port = ':' . \intval( $this->data[ static::COMPONENT_PORT ] );
|
||||
}
|
||||
|
||||
/* Path */
|
||||
@@ -485,7 +517,7 @@ class _Url
|
||||
|
||||
if( $keys !== NULL )
|
||||
{
|
||||
if( !is_array( $keys ) )
|
||||
if( !\is_array( $keys ) )
|
||||
{
|
||||
$keys = array( $keys => $keys );
|
||||
}
|
||||
@@ -510,58 +542,36 @@ class _Url
|
||||
/**
|
||||
* Make safe for ACP
|
||||
*
|
||||
* @param bool $resource If TRUE, will redirect silently
|
||||
* @deprecated No longer needed as of 4.5, ACP URLs no longer have the session ID in the URL
|
||||
* @return \IPS\Http\Url
|
||||
*/
|
||||
public function makeSafeForAcp( $resource=FALSE )
|
||||
{
|
||||
return static::internal( "app=core&module=system&controller=redirect", 'front' )->setQueryString( array(
|
||||
'url' => (string) $this,
|
||||
'key' => hash_hmac( "sha256", (string) $this, \IPS\Settings::i()->site_secret_key ),
|
||||
'resource' => $resource
|
||||
) );
|
||||
}
|
||||
return $this;
|
||||
|
||||
/**
|
||||
* Is this URL pointing to the local server?
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isLocalhost()
|
||||
{
|
||||
$baseUrlHostname = static::internal('')->data['host'];
|
||||
|
||||
return (
|
||||
isset( $this->data['host'] )
|
||||
and
|
||||
(
|
||||
(
|
||||
$this->data['host'] == 'localhost' or
|
||||
preg_match( "#\." . preg_quote( $baseUrlHostname ) . "$#", '.' . $this->data['host'] ) or
|
||||
preg_match( "#\." . preg_quote( $this->data['host'] ) . "$#", '.' . $baseUrlHostname )
|
||||
)
|
||||
or
|
||||
(
|
||||
filter_var( $this->data['host'], FILTER_VALIDATE_IP ) and
|
||||
filter_var( $this->data['host'], FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === FALSE
|
||||
)
|
||||
)
|
||||
);
|
||||
//return static::internal( "app=core&module=system&controller=redirect", 'front' )->setQueryString( array(
|
||||
// 'url' => (string) $this,
|
||||
// 'key' => hash_hmac( "sha256", (string) $this, \IPS\Settings::i()->site_secret_key . 'r' ),
|
||||
// 'resource' => $resource
|
||||
//) );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Make a HTTP Request
|
||||
*
|
||||
* @param int|null $timeout Timeout
|
||||
* @param string $httpVersion HTTP Version
|
||||
* @param bool|int $followRedirects Automatically follow redirects? If a number is provided, will follow up to that number of redirects
|
||||
* @param int|null $timeout Timeout
|
||||
* @param string $httpVersion HTTP Version
|
||||
* @param bool|int $followRedirects Automatically follow redirects? If a number is provided, will follow up to that number of redirects
|
||||
* @param array|null $allowedProtocols Protocols allowed (if NULL we default to array( 'http', 'https', 'ftp', 'scp', 'sftp', 'ftps' ))
|
||||
* @return \IPS\Http\Request
|
||||
*/
|
||||
public function request( $timeout=null, $httpVersion=null, $followRedirects=5 )
|
||||
public function request( $timeout=null, $httpVersion=null, $followRedirects=5, $allowedProtocols=NULL )
|
||||
{
|
||||
$allowedProtocols = $allowedProtocols ?: array( 'http', 'https', 'ftp', 'scp', 'sftp', 'ftps' );
|
||||
|
||||
/* Check the scheme is valid. Some areas accept user-submitted information to create a Url object. To avoid
|
||||
security issues from using file:// telnet:// etc. We reject everything not used by the suite here */
|
||||
if( isset( $this->data['scheme'] ) AND !in_array( $this->data['scheme'], array( 'http', 'https', 'ftp', 'scp', 'sftp', 'ftps' ) ) )
|
||||
if( isset( $this->data['scheme'] ) AND !\in_array( $this->data['scheme'], $allowedProtocols ) )
|
||||
{
|
||||
throw new \RuntimeException( mb_strtoupper( $this->data['scheme'] ) . '_SCHEME_NOT_PERMITTED' );
|
||||
}
|
||||
@@ -573,20 +583,20 @@ class _Url
|
||||
}
|
||||
|
||||
/* Use cURL if we can. BYPASS_CURL constant can be set to TRUE to force us to fallback to Sockets */
|
||||
if ( function_exists( 'curl_init' ) and function_exists( 'curl_exec' ) and \IPS\BYPASS_CURL === false )
|
||||
if ( \function_exists( 'curl_init' ) and \function_exists( 'curl_exec' ) and \IPS\BYPASS_CURL === false )
|
||||
{
|
||||
/* We require 7.36 or higher because older versions can't handle chunked encoding properly - FORCE_CURL constant can be set to override this and use it anyway */
|
||||
$version = curl_version();
|
||||
if( \IPS\FORCE_CURL or version_compare( $version['version'], '7.36', '>=' ) )
|
||||
{
|
||||
$requestObj = new \IPS\Http\Request\Curl( $this, $timeout, $httpVersion, $followRedirects );
|
||||
$requestObj = new \IPS\Http\Request\Curl( $this, $timeout, $httpVersion, $followRedirects, $allowedProtocols );
|
||||
}
|
||||
}
|
||||
|
||||
/* Fallback to Sockets if we can't use cURL */
|
||||
if( !isset( $requestObj ) )
|
||||
{
|
||||
$requestObj = new \IPS\Http\Request\Sockets( $this, $timeout, $httpVersion, $followRedirects );
|
||||
$requestObj = new \IPS\Http\Request\Sockets( $this, $timeout, $httpVersion, $followRedirects, $allowedProtocols );
|
||||
}
|
||||
|
||||
/* Set a default user-agent (some services, e.g. spotify, block requests without one but it's good to do so anyway) */
|
||||
@@ -671,7 +681,7 @@ class _Url
|
||||
$allowedCharacters = static::$allowedCharacters[ $component ];
|
||||
|
||||
/* These ones can also include percent-encoded characters and non-latin characters */
|
||||
if ( !in_array( $component, array( static::COMPONENT_SCHEME, static::COMPONENT_PORT ) ) )
|
||||
if ( !\in_array( $component, array( static::COMPONENT_SCHEME, static::COMPONENT_PORT ) ) )
|
||||
{
|
||||
$regex = '(' . '(%[A-Fa-z0-9]{2})|' . '[' . $allowedCharacters . ']\X*' . ')*';
|
||||
}
|
||||
@@ -701,7 +711,7 @@ class _Url
|
||||
public static function encodeComponent( $component, $value )
|
||||
{
|
||||
/* These ones cannot be percent-encoded */
|
||||
if ( in_array( $component, array( static::COMPONENT_SCHEME, static::COMPONENT_PORT ) ) )
|
||||
if ( \in_array( $component, array( static::COMPONENT_SCHEME, static::COMPONENT_PORT ) ) )
|
||||
{
|
||||
throw new \InvalidArgumentException;
|
||||
}
|
||||
@@ -720,7 +730,7 @@ class _Url
|
||||
a Google+ profile - when that hits PHP, it will convert it to a space, making the URL invalid.
|
||||
There is no downside to percent-encoding any particular character even if it doesn't technically
|
||||
need to be, so to avoid this issue, we'll just encode it */
|
||||
if ( in_array( $component, array( static::COMPONENT_QUERY, static::COMPONENT_QUERY_KEY, static::COMPONENT_QUERY_VALUE ) ) )
|
||||
if ( \in_array( $component, array( static::COMPONENT_QUERY, static::COMPONENT_QUERY_KEY, static::COMPONENT_QUERY_VALUE ) ) )
|
||||
{
|
||||
$return = str_replace( '+', '%2B', $return );
|
||||
}
|
||||
@@ -784,6 +794,17 @@ class _Url
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a referrer to the URL
|
||||
*
|
||||
* @param string $url The URL.
|
||||
* @return \IPS\Http\Url
|
||||
*/
|
||||
public function addRef( string $url ): \IPS\Http\Url
|
||||
{
|
||||
return $this->setQueryString( 'ref', base64_encode( $url ) );
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Punycode object
|
||||
*/
|
||||
@@ -800,10 +821,10 @@ class _Url
|
||||
* @li INVALID_SCHEME The scheme was invalid
|
||||
* @li INVALID_USERNAME The username was invalid
|
||||
* @li INVALID_PASSWORD The password was invalid
|
||||
* @LI INVALID_HOST The host name was invalid
|
||||
* @LI INVALID_PATH The path was invalid
|
||||
* @LI INVALID_QUERY The query was invalid
|
||||
* @LI INVALID_FRAGMENT The fragment was invalid
|
||||
* @li INVALID_HOST The host name was invalid
|
||||
* @li INVALID_PATH The path was invalid
|
||||
* @li INVALID_QUERY The query was invalid
|
||||
* @li INVALID_FRAGMENT The fragment was invalid
|
||||
*/
|
||||
protected static function componentsFromUrlString( $url, $autoEncode = FALSE )
|
||||
{
|
||||
@@ -935,7 +956,7 @@ class _Url
|
||||
/* If the authority ends in a : followed by a number, that's the port */
|
||||
if ( preg_match( '/:(\d*)$/', $authority, $matches ) )
|
||||
{
|
||||
$return[ static::COMPONENT_PORT ] = intval( $matches[1] );
|
||||
$return[ static::COMPONENT_PORT ] = \intval( $matches[1] );
|
||||
$authority = mb_substr( $authority, 0, -mb_strlen( $matches[0] ) );
|
||||
}
|
||||
|
||||
@@ -1075,7 +1096,7 @@ class _Url
|
||||
$k = static::encodeComponent( static::COMPONENT_QUERY_KEY, $k );
|
||||
}
|
||||
|
||||
if ( is_array( $v ) )
|
||||
if ( \is_array( $v ) )
|
||||
{
|
||||
$return[] = static::squashQueryStringArray( $k, $v );
|
||||
}
|
||||
@@ -1113,7 +1134,7 @@ class _Url
|
||||
$k = static::encodeComponent( static::COMPONENT_QUERY_KEY, $k );
|
||||
}
|
||||
|
||||
if ( is_array( $v ) )
|
||||
if ( \is_array( $v ) )
|
||||
{
|
||||
$return[] = static::squashQueryStringArray( "{$key}[{$k}]", $v, $encode );
|
||||
}
|
||||
@@ -1127,7 +1148,7 @@ class _Url
|
||||
$return[] = "{$key}[{$k}]={$v}";
|
||||
}
|
||||
}
|
||||
return count( $return ) ? implode( '&', $return ) : '';
|
||||
return \count( $return ) ? implode( '&', $return ) : '';
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1184,6 +1205,7 @@ class _Url
|
||||
* @param string $mainKey The main key, for example, if parsing "foo[x][y]=bar", the value will be "foo"
|
||||
* @param string $subKeyNames The sub keys in square brackets, for example, if parsing "foo[x][y]=bar", the value will be "[x][y]"
|
||||
* @param string $value The value, for example, if parsing "foo[x][y]=bar", the value will be "bar"
|
||||
* @return void
|
||||
*/
|
||||
protected static function _pushQueryStringPartIntoArray( &$return, $mainKey, $subKeyNames, $value )
|
||||
{
|
||||
@@ -1194,7 +1216,7 @@ class _Url
|
||||
$return[ $mainKey ] = array();
|
||||
}
|
||||
|
||||
if ( is_array( $return[ $mainKey ] ) )
|
||||
if ( \is_array( $return[ $mainKey ] ) )
|
||||
{
|
||||
$workingArray =& $return[ $mainKey ];
|
||||
}
|
||||
@@ -1209,7 +1231,7 @@ class _Url
|
||||
if ( $k === '' )
|
||||
{
|
||||
$workingArray[] = array();
|
||||
$workingArray =& $workingArray[ count( $workingArray ) - 1 ];
|
||||
$workingArray =& $workingArray[ \count( $workingArray ) - 1 ];
|
||||
}
|
||||
elseif ( !isset( $workingArray[ $k ] ) )
|
||||
{
|
||||
@@ -1291,7 +1313,6 @@ class _Url
|
||||
public function acpQueryString()
|
||||
{
|
||||
$queryString = $this->queryString;
|
||||
unset( $queryString['adsess'] );
|
||||
unset( $queryString['csrf'] );
|
||||
return static::convertQueryAsArrayToString( $queryString );
|
||||
}
|
||||
@@ -1375,7 +1396,7 @@ class _Url
|
||||
* Get friendly URL data
|
||||
*
|
||||
* @return array Parameters
|
||||
* @deprecared
|
||||
* @deprecated
|
||||
*/
|
||||
public function getFriendlyUrlData()
|
||||
{
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
namespace IPS\Http\Url;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
|
||||
+260
-49
@@ -11,7 +11,7 @@
|
||||
namespace IPS\Http\Url;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
@@ -50,7 +50,7 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
* @param int $protocol Protocol (one of the PROTOCOL_* constants)
|
||||
* @param string $friendlyUrlComponent The friendly URL component, which may be for the path or the query string (e.g. "topic/1-test")
|
||||
* @param array $queryString Additional query string data
|
||||
* @return \IPS\Http\Url\Friendly
|
||||
* @return static
|
||||
* @throws \IPS\Http\Url\Exception
|
||||
*/
|
||||
public static function friendlyUrlFromComponent( $protocol, $friendlyUrlComponent, $queryString )
|
||||
@@ -80,7 +80,7 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
*
|
||||
* @param array $components An array of components as returned by componentsFromUrlString()
|
||||
* @param string $potentialFurl The potential FURL slug (e.g. "topic/1-test")
|
||||
* @return \IPS\Http\Url\Friendly|NULL
|
||||
* @return static|NULL
|
||||
*/
|
||||
public static function createFriendlyUrlFromComponents( $components, $potentialFurl )
|
||||
{
|
||||
@@ -115,12 +115,14 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
* @param string $seoTemplate The key for making this a friendly URL
|
||||
* @param string|array $seoTitles The title(s) needed for the friendly URL
|
||||
* @param int $protocol Protocol (one of the PROTOCOL_* constants)
|
||||
* @return \IPS\Http\Url\Friendly
|
||||
* @return static
|
||||
* @throws \IPS\Http\Url\Exception
|
||||
* @todo Currently this will silently return a non-friendly URL if $seoTemplate is not valid, for backwards compatibility. Remove this in a major update.
|
||||
*/
|
||||
public static function friendlyUrlFromQueryString( $queryString, $seoTemplate, $seoTitles, $protocol )
|
||||
{
|
||||
$_originalQueryString = $queryString;
|
||||
|
||||
/* Get the friendly URL component */
|
||||
try
|
||||
{
|
||||
@@ -153,16 +155,14 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
{
|
||||
if ( \IPS\IN_DEV )
|
||||
{
|
||||
throw new \IPS\Http\Url\Exception( 'SEO_TEMPLATE_IS_NOT_VALID_FOR_URL' );
|
||||
throw new \IPS\Http\Url\Exception( 'SEO_TEMPLATE_IS_NOT_VALID_FOR_URL: ' . $_originalQueryString . ' - ' . $seoTemplate );
|
||||
}
|
||||
}
|
||||
|
||||
/* Return */
|
||||
return static::friendlyUrlFromComponent( $protocol, $friendlyUrlComponent, $queryString )->setFriendlyUrlData( $seoTemplate, $seoTitles, $matchedParams, $friendlyUrlComponent );
|
||||
}
|
||||
|
||||
/* !Instance */
|
||||
|
||||
|
||||
/**
|
||||
* Set friendly URL data
|
||||
*
|
||||
@@ -170,7 +170,7 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
* @param string|array $seoTitles The title(s) needed for the friendly URL
|
||||
* @param array $matchedParams The values for hidden query string properties
|
||||
* @param string $friendlyUrlComponent The friendly URL component, which may be for the path or the query string (e.g. "topic/1-test")
|
||||
* @return \IPS\Http\Url\Friendly
|
||||
* @return static
|
||||
* @throws \IPS\Http\Url\Exception
|
||||
*/
|
||||
protected function setFriendlyUrlData( $seoTemplate, $seoTitles, $matchedParams=array(), $friendlyUrlComponent )
|
||||
@@ -181,16 +181,17 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
{
|
||||
throw new \IPS\Http\Url\Exception( 'INVALID_SEO_TEMPLATE' );
|
||||
}
|
||||
|
||||
|
||||
/* Set basic properties */
|
||||
$this->seoTemplate = $seoTemplate;
|
||||
$this->seoTitles = is_string( $seoTitles ) ? array( $seoTitles ) : $seoTitles;
|
||||
$this->seoTitles = \is_string( $seoTitles ) ? array( $seoTitles ) : $seoTitles;
|
||||
$this->friendlyUrlComponent = $friendlyUrlComponent;
|
||||
|
||||
$this->seoPagination = ! empty( $furlDefinition[ $seoTemplate ]['seoPagination'] ) ? true : false;
|
||||
|
||||
/* Set hidden query string */
|
||||
parse_str( $furlDefinition[ $seoTemplate ]['real'], $hiddenQueryString );
|
||||
$this->hiddenQueryString = $hiddenQueryString + $matchedParams;
|
||||
|
||||
|
||||
/* Return */
|
||||
return $this;
|
||||
}
|
||||
@@ -198,7 +199,7 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
/**
|
||||
* Validate that the SEO title is correct
|
||||
*
|
||||
* @return mixed The correct URL if incorrect, TRUE is correct, or NULL if unknown
|
||||
* @return mixed The correct URL if incorrect, TRUE if correct, or NULL if unknown
|
||||
*/
|
||||
public function correctFriendlyUrl()
|
||||
{
|
||||
@@ -220,10 +221,9 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
if( mb_strpos( (string) $this, '/page-' ) )
|
||||
{
|
||||
preg_match( "/\/page\-(\d+)/", (string) $this, $matches );
|
||||
|
||||
if( isset( $matches[1] ) )
|
||||
{
|
||||
$correctUrl = $correctUrl->setQueryString( 'page', $matches[1] );
|
||||
$correctUrl = $correctUrl->setPage( 'page', $matches[1] );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,12 +231,10 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
if( mb_strpos( (string) $this, '/page__' ) )
|
||||
{
|
||||
preg_match( "/\/page__([^\/$]+?)(?:$|\/)/", (string) $this, $matches );
|
||||
|
||||
if( isset( $matches[1] ) )
|
||||
{
|
||||
$params = explode( '__', $matches[1] );
|
||||
$key = NULL;
|
||||
|
||||
foreach( $params as $param )
|
||||
{
|
||||
if( $key === NULL )
|
||||
@@ -251,19 +249,7 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* And it also used to do index.php?/topic/123-example/?p=456 - with the double ? */
|
||||
foreach ( $correctUrl->queryString as $k => $v )
|
||||
{
|
||||
if ( preg_match( '/^\/' . preg_quote( $correctUrl->friendlyUrlComponent, '/' ) . '\/\?(.+)$/', $k, $matches ) )
|
||||
{
|
||||
$correctUrl = $correctUrl->setQueryString( array(
|
||||
$matches[0] => NULL,
|
||||
$matches[1] => $v
|
||||
) );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if ( (string) $correctUrl->normaliseForEqualityCheck() !== (string) $this->normaliseForEqualityCheck() )
|
||||
{
|
||||
return $correctUrl;
|
||||
@@ -294,7 +280,10 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
|
||||
/* Make it schema-relative */
|
||||
$return = $return->setScheme( NULL );
|
||||
|
||||
|
||||
/* Remove pagination attributes */
|
||||
$return = $return->setPage();
|
||||
|
||||
/* Return */
|
||||
return $return;
|
||||
}
|
||||
@@ -317,9 +306,205 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
|
||||
return parent::stripQueryString( $keys );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the page parameter to the URL
|
||||
*
|
||||
* @param string $param The page key, default is 'page'
|
||||
* @param int|NULL $number The page number to use
|
||||
* @return \IPS\Http\Url
|
||||
*/
|
||||
public function setPage( $param='page', $number=1 )
|
||||
{
|
||||
if ( $param === 'page' )
|
||||
{
|
||||
$potentialFurl = NULL;
|
||||
$components = static::componentsFromUrlString( $this );
|
||||
|
||||
/* seoPagination is set usually in setFriendlyUrlData */
|
||||
if ( $this->seoPagination === NULL )
|
||||
{
|
||||
/* Fallback */
|
||||
$def = static::getFurlDefinitionFromPath( preg_replace( "/\/" . preg_quote( $param, '/' ) . "\/\d+?/", '', $this->getFriendlyComponent() ) );
|
||||
|
||||
if ( $def !== NULL and isset( $def['seoPagination'] ) )
|
||||
{
|
||||
$this->seoPagination = $def['seoPagination'];
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $this->seoPagination )
|
||||
{
|
||||
/* Ok, do the usual ?page=x param */
|
||||
return parent::setPage( $param, $number );
|
||||
}
|
||||
|
||||
/* If rewrite furls is disabled, or specifically if the request has index.php in the URL (it may be a cached URL from before the setting was toggled) */
|
||||
if ( !\IPS\Settings::i()->htaccess_mod_rewrite OR static::fixComponentPath( $components[ static::COMPONENT_PATH ] ) === 'index.php' )
|
||||
{
|
||||
if ( \is_array( $components[ static::COMPONENT_QUERY ] ) and \count( $components[ static::COMPONENT_QUERY ] ) )
|
||||
{
|
||||
$new = NULL;
|
||||
foreach( $components[ static::COMPONENT_QUERY ] as $k => $v )
|
||||
{
|
||||
if ( $new === NULL and mb_strstr( $k, '/' . $param . '/' ) )
|
||||
{
|
||||
$new = static::stripPageComponent( $k );
|
||||
$potentialFurl = $new;
|
||||
unset( $components[ static::COMPONENT_QUERY ][ $k ] );
|
||||
}
|
||||
}
|
||||
|
||||
if ( $new )
|
||||
{
|
||||
$components[ static::COMPONENT_QUERY ] = array_merge( array( '/' . ltrim( $new, '/' ) => NULL ), $components[ static::COMPONENT_QUERY ] );
|
||||
}
|
||||
}
|
||||
|
||||
if ( ! $potentialFurl )
|
||||
{
|
||||
$potentialFurl = $this->getFriendlyComponent();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$potentialFurl = static::stripPageComponent( static::fixComponentPath( $components[ static::COMPONENT_PATH ] ) );
|
||||
$components[ static::COMPONENT_PATH ] = static::stripPageComponent( $components[ static::COMPONENT_PATH ] );
|
||||
}
|
||||
|
||||
/* Add on the page itself */
|
||||
if ( \intval( $number ) > 1 )
|
||||
{
|
||||
$potentialFurl = NULL;
|
||||
if ( ! \IPS\Settings::i()->htaccess_mod_rewrite OR static::fixComponentPath( $components[ static::COMPONENT_PATH ] ) === 'index.php' )
|
||||
{
|
||||
foreach( $components[ static::COMPONENT_QUERY ] as $k => $v )
|
||||
{
|
||||
/* The FURL is always the first query string component */
|
||||
if ( $potentialFurl === NULL )
|
||||
{
|
||||
$potentialFurl = $k . $param . '/' . $number . '/';
|
||||
unset($components[ static::COMPONENT_QUERY ][ $k ] );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( $potentialFurl )
|
||||
{
|
||||
$components[ static::COMPONENT_QUERY ] = array_merge( array( $potentialFurl => NULL ), $components[ static::COMPONENT_QUERY ] );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$components[ static::COMPONENT_PATH ] .= $param . '/' . $number . '/';
|
||||
$potentialFurl = static::stripPageComponent( static::fixComponentPath( $components[ static::COMPONENT_PATH ] ) ) . $param . '/' . $number . '/';
|
||||
}
|
||||
}
|
||||
|
||||
return static::createFriendlyUrlFromComponents( $components, trim( $potentialFurl, '/' ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
return parent::setPage( $param, $number );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* !Utilities */
|
||||
|
||||
/**
|
||||
* Returns the FURL string from the current URL
|
||||
*
|
||||
* @return string|NULL The furl part of the URL, eg /topic/1-foo/
|
||||
*/
|
||||
protected function getFriendlyComponent()
|
||||
{
|
||||
/* Are we lucky? */
|
||||
if ( ! empty( $this->friendlyUrlComponent ) )
|
||||
{
|
||||
return trim( $this->friendlyUrlComponent, '/' );
|
||||
}
|
||||
|
||||
/* Of course not... */
|
||||
if ( ! \IPS\Settings::i()->htaccess_mod_rewrite )
|
||||
{
|
||||
if ( \is_array( $this->queryString ) and \count( $this->queryString ) )
|
||||
{
|
||||
foreach( $this->queryString as $k => $v )
|
||||
{
|
||||
if ( mb_strstr( $k, '/' ) )
|
||||
{
|
||||
$this->friendlyUrlComponent = trim( $k, '/' );
|
||||
return $this->friendlyUrlComponent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->friendlyUrlComponent = trim( static::fixComponentPath( $this->data[ static::COMPONENT_PATH ] ), '/' );
|
||||
return $this->friendlyUrlComponent;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the path doesn't contain any 'real' path eg `site.com</forums/>`
|
||||
*
|
||||
* @param string $path The path from ->data['path'] eg forums/topic/1-foo
|
||||
* @return string The furl part of the path, eg topic/1-foo
|
||||
*/
|
||||
public static function fixComponentPath( $path )
|
||||
{
|
||||
$baseUrl = parse_url( static::baseUrl() );
|
||||
|
||||
if ( ! empty( $baseUrl['path'] ) )
|
||||
{
|
||||
$pos = mb_stripos( $path, $baseUrl['path'] );
|
||||
if( $pos !== FALSE )
|
||||
{
|
||||
$path = substr_replace( $path, '/', $pos, mb_strlen( $baseUrl['path'] ) );
|
||||
}
|
||||
}
|
||||
|
||||
return trim( $path, '/' );
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the matching FURL definition data for the FURL component eg /topic/1-foo/
|
||||
*
|
||||
* @param string $friendlyUrlComponent The furl part of the path, eg /topic/1-foo/
|
||||
* @return array|NULL The FURL definition
|
||||
*/
|
||||
protected static function getFurlDefinitionFromPath( $friendlyUrlComponent )
|
||||
{
|
||||
foreach ( static::furlDefinition() as $seoTemplate => $furlDefinition )
|
||||
{
|
||||
foreach ( $furlDefinition['regex'] as $_regex )
|
||||
{
|
||||
if ( preg_match( '/^' . $_regex . '$/i', $friendlyUrlComponent ) )
|
||||
{
|
||||
return $furlDefinition;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes any inline page component from the FURL component eg /topic/1-foo/page/2/
|
||||
*
|
||||
* @param string $friendlyUrlComponent The furl part of the path, eg /topic/1-foo/
|
||||
* @param string $param The /page/N/ key
|
||||
* @return string The cleaned FURL eg /topic/1-foo/
|
||||
*/
|
||||
public static function stripPageComponent( $friendlyUrlComponent, $param='page' )
|
||||
{
|
||||
return preg_replace( '#/' . $param . '/(\d+?)$#', '', rtrim( $friendlyUrlComponent, '/' ) ) . '/';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get friendly URL component (e.g. "topic/1-test") from a query string and SEO template
|
||||
*
|
||||
@@ -334,7 +519,7 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
/* Get the definition */
|
||||
$furlDefinition = static::furlDefinition();
|
||||
if ( !isset( $furlDefinition[ $seoTemplate ] ) )
|
||||
{
|
||||
{
|
||||
throw new \IPS\Http\Url\Exception( 'INVALID_SEO_TEMPLATE' );
|
||||
}
|
||||
$component = $furlDefinition[ $seoTemplate ]['friendly'];
|
||||
@@ -347,15 +532,15 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
{
|
||||
if ( mb_strpos( $component, "{#{$k}}" ) !== FALSE or mb_strpos( $component, "{@{$k}}" ) !== FALSE )
|
||||
{
|
||||
$component = str_replace( "{#{$k}}", intval( $v ), $component );
|
||||
$component = str_replace( "{#{$k}}", \intval( $v ), $component );
|
||||
$component = str_replace( "{@{$k}}", $v, $component );
|
||||
unset( $queryString[ $k ] );
|
||||
}
|
||||
}
|
||||
|
||||
/* Parse out the titles */
|
||||
$seoTitles = is_null( $seoTitles ) ? array() : ( is_string( $seoTitles ) ? array( $seoTitles ) : array_values( $seoTitles ) );
|
||||
$seoTitlesCount = count( $seoTitles );
|
||||
$seoTitles = \is_null( $seoTitles ) ? array() : ( \is_string( $seoTitles ) ? array( $seoTitles ) : array_values( $seoTitles ) );
|
||||
$seoTitlesCount = \count( $seoTitles );
|
||||
for ( $i = 0; $i < $seoTitlesCount; $i++ )
|
||||
{
|
||||
if ( $i === 0 )
|
||||
@@ -420,14 +605,34 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
* @return array|NULL array( $matchedParams, $seoTitles ) if matched, NULL if the $friendlyUrlComponent does not match this $furlDefinition
|
||||
*/
|
||||
protected static function getMatchedParamsFromFriendlyUrlComponent( $furlDefinition, $friendlyUrlComponent )
|
||||
{
|
||||
{
|
||||
/* See if it matches */
|
||||
$seoTitles = array();
|
||||
$matchedParams = array();
|
||||
|
||||
foreach ( $furlDefinition['regex'] as $_regex )
|
||||
{
|
||||
if ( preg_match( '/^' . $_regex . '$/i', $friendlyUrlComponent, $matches ) )
|
||||
$regex = ( ! empty( $furlDefinition['seoPagination'] ) ) ? '#^' . $_regex . '($|/page/\d+?$)#i' : '#^' . $_regex . '$#i';
|
||||
|
||||
if ( preg_match( $regex, $friendlyUrlComponent, $matches ) )
|
||||
{
|
||||
/* Check pagination first */
|
||||
if ( ! empty( $furlDefinition['seoPagination'] ) and mb_strstr( $matches[0], '/page/' ) )
|
||||
{
|
||||
foreach( $matches as $k => $v )
|
||||
{
|
||||
if ( $k and mb_strstr( $matches[ $k ], '/page/' ) )
|
||||
{
|
||||
list( $x, $page ) = explode( '/', trim( $matches[ $k ], '/' ) );
|
||||
|
||||
if ( $page )
|
||||
{
|
||||
$matchedParams['page'] = \intval( $page );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ( $furlDefinition['params'] as $k => $param )
|
||||
{
|
||||
if ( $param )
|
||||
@@ -439,7 +644,7 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
$seoTitles[] = $matches[ $k + 1 ];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return array( $matchedParams, $seoTitles );
|
||||
}
|
||||
}
|
||||
@@ -463,10 +668,10 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
{
|
||||
if ( static::$furlDefinition === NULL or $revert )
|
||||
{
|
||||
$furlCustomizations = ( \IPS\Settings::i()->furl_configuration AND !$revert ) ? json_decode( \IPS\Settings::i()->furl_configuration, TRUE ) : array();
|
||||
$furlCustomizations = ( \IPS\Settings::i()->furl_configuration AND !$revert and ! \IPS\IN_DEV ) ? json_decode( \IPS\Settings::i()->furl_configuration, TRUE ) : array();
|
||||
$furlConfiguration = ( isset( \IPS\Data\Store::i()->furl_configuration ) AND \IPS\Data\Store::i()->furl_configuration ) ? json_decode( \IPS\Data\Store::i()->furl_configuration, TRUE ) : array();
|
||||
|
||||
if ( ( \IPS\IN_DEV and !\IPS\DEV_USE_FURL_CACHE ) or !count( $furlConfiguration ) or $revert )
|
||||
if ( ( \IPS\IN_DEV and !\IPS\DEV_USE_FURL_CACHE ) or !\count( $furlConfiguration ) or $revert )
|
||||
{
|
||||
$furlConfiguration = static::buildFurlConfiguation();
|
||||
}
|
||||
@@ -478,7 +683,7 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuild and return \IPS\Data\Store::i()->furl_configuration with default, uncustomised values
|
||||
* Rebuild and return `\IPS\Data\Store::i()->furl_configuration` with default, uncustomised values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
@@ -503,17 +708,17 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
foreach ( $applications as $app )
|
||||
{
|
||||
/* If it has a furl.json file... */
|
||||
if( file_exists( \IPS\ROOT_PATH . "/applications/{$app->directory}/data/furl.json" ) )
|
||||
if( $app->enabled and file_exists( $app->getApplicationPath() . "/data/furl.json" ) )
|
||||
{
|
||||
/* Open it up */
|
||||
$data = json_decode( preg_replace( '/\/\*.+?\*\//s', '', \file_get_contents( \IPS\ROOT_PATH . "/applications/{$app->directory}/data/furl.json" ) ), TRUE );
|
||||
$data = json_decode( preg_replace( '/\/\*.+?\*\//s', '', \file_get_contents( $app->getApplicationPath() . "/data/furl.json" ) ), TRUE );
|
||||
$topLevel = $data['topLevel'];
|
||||
|
||||
/* Process them */
|
||||
$definitions = array();
|
||||
foreach ( $data['pages'] as $k => $page )
|
||||
{
|
||||
$definitions[ $k ] = static::buildFurlDefinition( $page['friendly'], $page['real'], $topLevel, $app->default, isset( $page['alias'] ) ? $page['alias'] : NULL, FALSE, isset( $page['verify'] ) ? $page['verify'] : NULL, isset( $page['seoTitles'] ) ? $page['seoTitles'] : NULL );
|
||||
$definitions[ $k ] = static::buildFurlDefinition( $page['friendly'], $page['real'], $topLevel, $app->default, isset( $page['alias'] ) ? $page['alias'] : NULL, FALSE, isset( $page['verify'] ) ? $page['verify'] : NULL, isset( $page['seoTitles'] ) ? $page['seoTitles'] : NULL, isset( $page['seoPagination'] ) ? $page['seoPagination'] : NULL );
|
||||
}
|
||||
|
||||
/* Add it in */
|
||||
@@ -539,9 +744,10 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
* @param bool $custom Flag to indicate if this is a custom FURL definition
|
||||
* @param string $verify The name of a class that contains a loadFromUrl() and an url() method for verifying the friendly URL is correct
|
||||
* @param array $seoTitles The class, query param and property to load from to rebuild seo titles
|
||||
* @param bool|null $seoPagination Whether to use SEO-friendly pagination (e.g. /page/2/) or not
|
||||
* @return array
|
||||
*/
|
||||
public static function buildFurlDefinition( $friendly, $real, $appTopLevel = NULL, $appIsDefault = FALSE, $alias = NULL, $custom = FALSE, $verify = NULL, $seoTitles = NULL )
|
||||
public static function buildFurlDefinition( $friendly, $real, $appTopLevel = NULL, $appIsDefault = FALSE, $alias = NULL, $custom = FALSE, $verify = NULL, $seoTitles = NULL, $seoPagination = NULL )
|
||||
{
|
||||
/* Init */
|
||||
$return = array(
|
||||
@@ -561,6 +767,11 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
$return['seoTitles'] = $seoTitles;
|
||||
}
|
||||
|
||||
if ( $seoPagination )
|
||||
{
|
||||
$return['seoPagination'] = $seoPagination;
|
||||
}
|
||||
|
||||
/* If it has a top-level (e.g. "/forums") we need to store the definition either with that if it's the default app, or
|
||||
without it if it isn't, so that if the default app changes we can redirect accordingly */
|
||||
if ( $appTopLevel )
|
||||
@@ -600,21 +811,21 @@ class _Friendly extends \IPS\Http\Url\Internal
|
||||
{
|
||||
case '#':
|
||||
$return['regex'] = array_map( function( $_regex ) use ( $tag ) {
|
||||
return str_replace( '\{#' . mb_substr( $tag, 1 ) . '\}', '(\d+?)', $_regex );
|
||||
return str_replace( preg_quote( '{#' . mb_substr( $tag, 1 ) . '}', '/' ), '(\d+?)', $_regex );
|
||||
}, $return['regex'] );
|
||||
$return['params'][] = mb_substr( $tag, 1 );
|
||||
break;
|
||||
|
||||
case '@':
|
||||
$return['regex'] = array_map( function( $_regex ) use ( $tag ) {
|
||||
return str_replace( '\{@' . mb_substr( $tag, 1 ) . '\}', '(.+?)', $_regex );
|
||||
return str_replace( preg_quote( '{@' . mb_substr( $tag, 1 ) . '}', '/' ), '(.+?)', $_regex );
|
||||
}, $return['regex'] );
|
||||
$return['params'][] = mb_substr( $tag, 1 );
|
||||
break;
|
||||
|
||||
case '?':
|
||||
$return['regex'] = array_map( function( $_regex ) use ( $tag ) {
|
||||
return str_replace( '\{\?\}', '(?!&)(.+?)', $_regex );
|
||||
return preg_replace( '/\\\{\\\\\?\d*\\\}/', '(?![&\\/])(.+?)', $_regex );
|
||||
}, $return['regex'] );
|
||||
$return['params'][] = '';
|
||||
break;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
namespace IPS\Http\Url;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
@@ -34,7 +34,7 @@ class _Internal extends \IPS\Http\Url
|
||||
*/
|
||||
protected static function createInternalFromComponents( $base, $protocol, $path, $query = NULL, $fragment = NULL )
|
||||
{
|
||||
$baseUrlData = parse_url( static::baseUrl( is_numeric( $protocol ) ? $protocol : NULL ) );
|
||||
$baseUrlData = parse_url( static::baseUrl( \is_numeric( $protocol ) ? $protocol : NULL ) );
|
||||
|
||||
$pathToUse = '';
|
||||
if ( isset( $baseUrlData['path'] ) and $baseUrlData['path'] )
|
||||
@@ -52,7 +52,7 @@ class _Internal extends \IPS\Http\Url
|
||||
|
||||
$return = static::createFromComponents(
|
||||
$baseUrlData['host'],
|
||||
( is_string( $protocol ) and !is_numeric( $protocol ) ) ? $protocol : ( ( isset( $baseUrlData['scheme'] ) AND $protocol !== NULL ) ? $baseUrlData['scheme'] : NULL ),
|
||||
( \is_string( $protocol ) and !\is_numeric( $protocol ) ) ? $protocol : ( ( isset( $baseUrlData['scheme'] ) AND $protocol !== NULL ) ? $baseUrlData['scheme'] : NULL ),
|
||||
$pathToUse ? "/{$pathToUse}" : '',
|
||||
$query,
|
||||
isset( $baseUrlData['port'] ) ? $baseUrlData['port'] : NULL,
|
||||
@@ -65,6 +65,11 @@ class _Internal extends \IPS\Http\Url
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Does this URL have the SEO pagination (page/n/)?
|
||||
*/
|
||||
public $seoPagination = NULL;
|
||||
|
||||
/**
|
||||
* @brief Base
|
||||
*/
|
||||
@@ -100,7 +105,7 @@ class _Internal extends \IPS\Http\Url
|
||||
}
|
||||
|
||||
/* If this definition requires a parameter, see if we have it. If not, skip to next definition to check. */
|
||||
if( count( array_diff( $params, array_keys( $this->queryString ) ) ) )
|
||||
if( \count( array_diff( $params, array_keys( $this->queryString ) ) ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
@@ -158,7 +163,8 @@ class _Internal extends \IPS\Http\Url
|
||||
/**
|
||||
* Get the correct URL using a "verify" property from the FURL definition
|
||||
*
|
||||
* @return
|
||||
* @param object $verify Class containing `preCorrectUrlFromVerifyClass()` method to check URL against
|
||||
* @return NULL|\IPS\Http\Url
|
||||
* @throws \OutOfRangeException
|
||||
*/
|
||||
protected function correctUrlFromVerifyClass( $verify )
|
||||
@@ -198,7 +204,7 @@ class _Internal extends \IPS\Http\Url
|
||||
$paramsToSet[ $k ] = $v;
|
||||
}
|
||||
}
|
||||
if ( count( $paramsToSet ) )
|
||||
if ( \count( $paramsToSet ) )
|
||||
{
|
||||
$correctUrl = $correctUrl->setQueryString( $paramsToSet );
|
||||
}
|
||||
@@ -217,6 +223,26 @@ class _Internal extends \IPS\Http\Url
|
||||
return $this->setQueryString( 'csrfKey', \IPS\Session::i()->csrfKey );
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the page parameter to the URL
|
||||
*
|
||||
* @param string $param The page key, default is 'page'
|
||||
* @param int|NULL $number The page number to use
|
||||
* @return \IPS\Http\Url
|
||||
*/
|
||||
public function setPage( $param='page', $number=1 )
|
||||
{
|
||||
/* Remove any existing page param */
|
||||
$url = $this->stripQueryString( $param );
|
||||
|
||||
if ( \intval( $number ) > 1 )
|
||||
{
|
||||
$url = $this->setQueryString( $param, $number );
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ACP query string without adsess
|
||||
*
|
||||
@@ -225,7 +251,6 @@ class _Internal extends \IPS\Http\Url
|
||||
public function acpQueryString()
|
||||
{
|
||||
$queryString = $this->queryString;
|
||||
unset( $queryString['adsess'] );
|
||||
unset( $queryString['csrf'] );
|
||||
return static::convertQueryAsArrayToString( $queryString );
|
||||
}
|
||||
|
||||
Whitespace-only changes.
@@ -11,7 +11,7 @@
|
||||
namespace IPS\Http;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
if ( !\defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
@@ -26,11 +26,11 @@ class _Useragent
|
||||
* @brief Search engine spider?
|
||||
*/
|
||||
public $spider = FALSE;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Mobile or tablet device?
|
||||
* @brief Invision Community mobile app?
|
||||
*/
|
||||
public $mobile = FALSE;
|
||||
public $ipsApp = FALSE;
|
||||
|
||||
/**
|
||||
* @brief Browser name
|
||||
@@ -96,6 +96,24 @@ class _Useragent
|
||||
*/
|
||||
public function parseUserAgent()
|
||||
{
|
||||
/* Is it ours? */
|
||||
if ( preg_match( '/^InvisionCommunityApp\/(\d+\.\d+\.\d+) \((.+?)\) (.+?) \((.+?)\)$/', $this->useragent, $matches ) ) // Matches: 1 = App version, 2 = App name (differentiates multicommunity vs white-label) 3 = "Android" or "iOS/X.Y.Z" 4 = Semi-human-friendly name (e.g. "iPhone11,6" is iPhone XS Max)
|
||||
{
|
||||
if ( $matches[3] === 'Android' )
|
||||
{
|
||||
$this->platform = $matches[4];
|
||||
}
|
||||
else
|
||||
{
|
||||
$this->platform = preg_replace( '/^(.+?)\d+,\d+$/', '$1', $matches[4] );
|
||||
}
|
||||
|
||||
$this->browser = 'InvisionCommunityApp';
|
||||
$this->browserVersion = $matches[1];
|
||||
$this->ipsApp = $matches[2];
|
||||
return;
|
||||
}
|
||||
|
||||
/* Set basic data */
|
||||
require_once( \IPS\ROOT_PATH . '/system/3rd_party/PhpUserAgent/UserAgentParser.php' );
|
||||
$data = parse_user_agent( $this->useragent );
|
||||
@@ -106,7 +124,7 @@ class _Useragent
|
||||
/* Is this a spider? */
|
||||
foreach( $this->searchEngineUseragents as $key => $regex )
|
||||
{
|
||||
if( is_array( $regex ) )
|
||||
if( \is_array( $regex ) )
|
||||
{
|
||||
foreach( $regex as $_expression )
|
||||
{
|
||||
@@ -165,7 +183,8 @@ class _Useragent
|
||||
'dotbot' => "DotBot[ /]([0-9.]{1,10})",
|
||||
'sogou' => "Sogou web spider[ /]([0-9.]{1,10})",
|
||||
'isetallabot' => "istellabot[ /][a-z]([0-9.]{1,10})",
|
||||
'blexbot' => "BLEXBot[ /]([0-9.]{1,10})"
|
||||
'blexbot' => "BLEXBot[ /]([0-9.]{1,10})",
|
||||
'semrush' => "SemrushBot/([0-9.]{1,10})"
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -253,7 +272,7 @@ class _Useragent
|
||||
|
||||
foreach( $unpackedAddress as $char )
|
||||
{
|
||||
$ipAddress .= str_pad( decbin( ord( $char ) ), 8, '0', STR_PAD_LEFT );
|
||||
$ipAddress .= str_pad( decbin( \ord( $char ) ), 8, '0', STR_PAD_LEFT );
|
||||
}
|
||||
|
||||
return $ipAddress;
|
||||
|
||||
Reference in new issue
Block a user