Version 4.1.19
This commit is contained in:
1 parent
28bd025b35
commit
2bd5025018
2674 files changed
+233518
-77766
No files matched your search
+22
-2
@@ -2,9 +2,9 @@
|
||||
/**
|
||||
* @brief HTTP Ranges Request/Response Class
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Social Suite
|
||||
* @package IPS Community Suite
|
||||
* @since 19 May 2014
|
||||
* @version SVN_VERSION_NUMBER
|
||||
* @note This class facilitates parsing of HTTP Range requests and sending the appropriate response
|
||||
@@ -53,9 +53,13 @@ class _Ranges extends Response
|
||||
$range = 0;
|
||||
$start_range = 0;
|
||||
|
||||
\IPS\Log::debug( "Range response initiated", 'ranges' );
|
||||
|
||||
/* Was this a ranged request? */
|
||||
if( isset( $_SERVER['HTTP_RANGE'] ) )
|
||||
{
|
||||
\IPS\Log::debug( "This is a range request: " . $_SERVER['HTTP_RANGE'], 'ranges' );
|
||||
|
||||
/* Get the range */
|
||||
list( $a, $range ) = explode( "=", $_SERVER['HTTP_RANGE'] );
|
||||
|
||||
@@ -65,6 +69,8 @@ class _Ranges extends Response
|
||||
/* Did we only find one range? */
|
||||
if( count($ranges) == 1 )
|
||||
{
|
||||
\IPS\Log::debug( "Only one range detected", 'ranges' );
|
||||
|
||||
/* Get the actual range start and end */
|
||||
list( $start_range, $end_range ) = explode( "-", $range );
|
||||
|
||||
@@ -90,6 +96,8 @@ class _Ranges extends Response
|
||||
/* If the range is invalid we need to send a 416 header and let the client know the valid range */
|
||||
if( $start_range > $size OR $end_range > $size )
|
||||
{
|
||||
\IPS\Log::debug( "Invalid range, responding with 416 header. Requested {$start_range} - {$end_range} out of {$size}", 'ranges' );
|
||||
|
||||
\IPS\Output::i()->sendStatusCodeHeader( 416 );
|
||||
\IPS\Output::i()->sendHeader( "Content-Range: */{$filesize}" );
|
||||
return;
|
||||
@@ -97,6 +105,8 @@ class _Ranges extends Response
|
||||
/* Otherwise send the 206 header */
|
||||
else
|
||||
{
|
||||
\IPS\Log::debug( "Sending requested range with 206 header: {$start_range} - {$size} out of {$filesize}", 'ranges' );
|
||||
|
||||
\IPS\Output::i()->sendStatusCodeHeader( 206 );
|
||||
\IPS\Output::i()->sendHeader( "Content-Range: bytes {$start_range}-{$size}/{$filesize}" );
|
||||
}
|
||||
@@ -115,6 +125,8 @@ class _Ranges extends Response
|
||||
|
||||
foreach( $ranges as $arange )
|
||||
{
|
||||
\IPS\Log::debug( "Multiple ranges: {$start_range} - {$end_range}", 'ranges' );
|
||||
|
||||
/* Get the actual range start and end */
|
||||
list( $start_range, $end_range ) = explode( "-", $arange );
|
||||
|
||||
@@ -140,6 +152,8 @@ class _Ranges extends Response
|
||||
/* If the range is invalid we need to send a 416 header and let the client know the valid range */
|
||||
if( $start_range > $size OR $end_range > $size )
|
||||
{
|
||||
\IPS\Log::debug( "This range out of multiple ranges was invalid", 'ranges' );
|
||||
|
||||
\IPS\Output::i()->sendStatusCodeHeader( 416 );
|
||||
\IPS\Output::i()->sendHeader( "Content-Range: */{$filesize}" );
|
||||
return;
|
||||
@@ -160,6 +174,8 @@ class _Ranges extends Response
|
||||
|
||||
$the_responses = array();
|
||||
|
||||
\IPS\Log::debug( "One range out of multiple ranges to respond: {$start_range} - {$size} out of {$filesize}", 'ranges' );
|
||||
|
||||
\IPS\Output::i()->sendStatusCodeHeader( 206 );
|
||||
\IPS\Output::i()->sendHeader( "Content-Range: bytes {$start_range}-{$size}/{$filesize}" );
|
||||
|
||||
@@ -174,6 +190,8 @@ class _Ranges extends Response
|
||||
{
|
||||
$content_length = 0;
|
||||
|
||||
\IPS\Log::debug( "Multiple range responses, using boundary", 'ranges' );
|
||||
|
||||
foreach( $the_responses as $part )
|
||||
{
|
||||
$content_length += \strlen( "\r\n--IPDOWNLOADSBOUNDARYMARKER\r\n" );
|
||||
@@ -197,6 +215,8 @@ class _Ranges extends Response
|
||||
/* No range */
|
||||
else
|
||||
{
|
||||
\IPS\Log::debug( "No range requested, sending entire file", 'ranges' );
|
||||
|
||||
$size = $filesize - 1;
|
||||
|
||||
\IPS\Output::i()->sendStatusCodeHeader( 200 );
|
||||
|
||||
+136
-73
@@ -2,9 +2,9 @@
|
||||
/**
|
||||
* @brief cURL REST Class
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Social Suite
|
||||
* @package IPS Community Suite
|
||||
* @since 18 Mar 2013
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
@@ -128,7 +128,7 @@ class _Curl
|
||||
}
|
||||
|
||||
$httpVersion = ( $httpVersion == '1.1' ? CURL_HTTP_VERSION_1_1 : CURL_HTTP_VERSION_1_0 );
|
||||
|
||||
|
||||
/* Set our basic settings */
|
||||
curl_setopt_array( $this->curl, array(
|
||||
CURLOPT_HEADER => TRUE, // Specifies that we want the headers
|
||||
@@ -215,62 +215,7 @@ class _Curl
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP GET
|
||||
*
|
||||
* @return \IPS\Http\Response
|
||||
* @throws \IPS\Http\Request\CurlException
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
/* Specify that this is a GET request */
|
||||
curl_setopt( $this->curl, CURLOPT_HTTPGET, TRUE );
|
||||
$this->dataForLog = NULL;
|
||||
|
||||
/* Execute */
|
||||
$response = $this->_execute();
|
||||
|
||||
/* Either return it or follow it */
|
||||
if ( $this->followRedirects and ( $response->httpResponseCode == 302 OR $response->httpResponseCode == 301 ) )
|
||||
{
|
||||
$newRequest = \IPS\Http\Url::external( $response->httpHeaders['Location'] )->request( $this->timeout, $this->httpVersion, is_int( $this->followRedirects ) ? ( $this->followRedirects - 1 ) : $this->followRedirects );
|
||||
return $newRequest->get();
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP POST
|
||||
*
|
||||
* @param mixed $data Data to post (can be array or string)
|
||||
* @return \IPS\Http\Response
|
||||
* @throws \IPS\Http\Request\CurlException
|
||||
*/
|
||||
public function post( $data=NULL )
|
||||
{
|
||||
/* Specify that this is a POST request */
|
||||
curl_setopt( $this->curl, CURLOPT_POST, TRUE );
|
||||
|
||||
/* Set the data */
|
||||
$this->dataForLog = $data;
|
||||
if ( !$this->modifiedContentType and is_array( $data ) )
|
||||
{
|
||||
$data = http_build_query( $data, '', '&' );
|
||||
}
|
||||
curl_setopt( $this->curl, CURLOPT_POSTFIELDS, $data );
|
||||
|
||||
/* Execute */
|
||||
$response = $this->_execute();
|
||||
|
||||
/* Either return it or follow it */
|
||||
if ( $this->followRedirects and ( $response->httpResponseCode == 302 OR $response->httpResponseCode == 301 ) )
|
||||
{
|
||||
$newRequest = \IPS\Http\Url::external( $response->httpHeaders['Location'] )->request( $this->timeout, $this->httpVersion, is_int( $this->followRedirects ) ? ( $this->followRedirects - 1 ) : $this->followRedirects );
|
||||
return call_user_func_array( array( $newRequest, 'post' ), $data );
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle SSL checks
|
||||
@@ -288,6 +233,89 @@ class _Curl
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Force TLS
|
||||
*
|
||||
* @return \IPS\Http\Request\Socket
|
||||
*/
|
||||
public function forceTls()
|
||||
{
|
||||
$curlVersionData = curl_version();
|
||||
if ( preg_match( '/^OpenSSL\/(\d+\.\d+\.\d+)/', $curlVersionData['ssl_version'], $openSSLVersionData ) )
|
||||
{
|
||||
if ( version_compare( $openSSLVersionData[1], '1.0.1', '>=' ) )
|
||||
{
|
||||
if ( !defined('CURL_SSLVERSION_TLSv1_2') ) // constant not defined in PHP < 5.5
|
||||
{
|
||||
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
|
||||
{
|
||||
define( 'CURL_SSLVERSION_TLSv1', 1 );
|
||||
}
|
||||
curl_setopt( $this->curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1 );
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP GET
|
||||
*
|
||||
* @return \IPS\Http\Response
|
||||
* @throws \IPS\Http\Request\CurlException
|
||||
*/
|
||||
public function get()
|
||||
{
|
||||
/* Specify that this is a GET request */
|
||||
curl_setopt( $this->curl, CURLOPT_HTTPGET, TRUE );
|
||||
$this->dataForLog = NULL;
|
||||
|
||||
return $this->_executeAndFollowRedirects( 'GET', NULL );
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP POST
|
||||
*
|
||||
* @param mixed $data Data to post (can be array or string)
|
||||
* @return \IPS\Http\Response
|
||||
* @throws \IPS\Http\Request\CurlException
|
||||
*/
|
||||
public function post( $data=NULL )
|
||||
{
|
||||
/* Specify that this is a POST request */
|
||||
curl_setopt( $this->curl, CURLOPT_POST, TRUE );
|
||||
|
||||
/* Set the data */
|
||||
curl_setopt( $this->curl, CURLOPT_POSTFIELDS, $this->_dataToSend( $data ) );
|
||||
|
||||
/* Execute */
|
||||
return $this->_executeAndFollowRedirects( 'POST', $data );
|
||||
}
|
||||
|
||||
/**
|
||||
* HTTP HEAD
|
||||
*
|
||||
* @return \IPS\Http\Response
|
||||
* @throws \IPS\Http\Request\CurlException
|
||||
*/
|
||||
public function head()
|
||||
{
|
||||
/* Specify the request method */
|
||||
curl_setopt( $this->curl, CURLOPT_CUSTOMREQUEST, 'HEAD' );
|
||||
|
||||
/* For HEAD requests, do not try to fetch the body or curl times out */
|
||||
curl_setopt( $this->curl, CURLOPT_NOBODY, true );
|
||||
|
||||
/* Execute */
|
||||
return $this->_executeAndFollowRedirects( 'HEAD', NULL );
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic Method: __call
|
||||
* Used for other HTTP methods (like PUT and DELETE)
|
||||
@@ -301,25 +329,31 @@ class _Curl
|
||||
{
|
||||
/* Specify the request method */
|
||||
curl_setopt( $this->curl, CURLOPT_CUSTOMREQUEST, mb_strtoupper( $method ) );
|
||||
|
||||
|
||||
/* If we have any data to send, set it */
|
||||
if ( isset( $params[0] ) )
|
||||
{
|
||||
curl_setopt( $this->curl, CURLOPT_POSTFIELDS, $params[0] );
|
||||
|
||||
$this->dataForLog = $params[0];
|
||||
curl_setopt( $this->curl, CURLOPT_POSTFIELDS, $this->_dataToSend( $params[0] ) );
|
||||
}
|
||||
|
||||
/* Execute */
|
||||
$response = $this->_execute();
|
||||
|
||||
/* Either return it or follow it */
|
||||
if ( $this->followRedirects and ( $response->httpResponseCode == 302 OR $response->httpResponseCode == 301 ) )
|
||||
return $this->_executeAndFollowRedirects( mb_strtoupper( $method ), $params );
|
||||
}
|
||||
|
||||
/**
|
||||
* Data to send
|
||||
*
|
||||
* @param mixed $data Data to post (can be array or string)
|
||||
* @return mixed
|
||||
*/
|
||||
protected function _dataToSend( $data=NULL )
|
||||
{
|
||||
$this->dataForLog = $data;
|
||||
if ( !$this->modifiedContentType and is_array( $data ) )
|
||||
{
|
||||
$newRequest = \IPS\Http\Url::external( $response->httpHeaders['Location'] )->request( $this->timeout, $this->httpVersion, is_int( $this->followRedirects ) ? ( $this->followRedirects - 1 ) : $this->followRedirects );
|
||||
return call_user_func_array( array( $newRequest, $method ), $params );
|
||||
$data = http_build_query( $data, '', '&' );
|
||||
}
|
||||
return $response;
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -333,12 +367,11 @@ class _Curl
|
||||
/* Execute */
|
||||
$output = curl_exec( $this->curl );
|
||||
|
||||
/* Log */
|
||||
try
|
||||
/* 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 )
|
||||
{
|
||||
\IPS\Log::i( LOG_DEBUG )->write( "\n\n------------------------------------\ncURL REQUEST: {$this->url}\n------------------------------------\n\n" . var_export( $this->dataForLog, TRUE ) . "\n\n------------------------------------\nRESPONSE\n------------------------------------\n\n" . $output, 'request' );
|
||||
\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' );
|
||||
}
|
||||
catch( \Exception $e ){}
|
||||
|
||||
/* Errors? */
|
||||
if ( $output === FALSE )
|
||||
@@ -355,6 +388,36 @@ class _Curl
|
||||
/* Return it */
|
||||
return new \IPS\Http\Response( $output );
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the request and follow redirects id necessary
|
||||
*
|
||||
* @param string $method Request method to use
|
||||
* @param NULL|array $params Parameters to send with request
|
||||
* @return \IPS\Http\Response
|
||||
* @throws \IPS\Http\Request\CurlException
|
||||
*/
|
||||
protected function _executeAndFollowRedirects( $method, $params=NULL )
|
||||
{
|
||||
/* Execute */
|
||||
$response = $this->_execute();
|
||||
|
||||
/* Either return it or follow it */
|
||||
if ( $this->followRedirects and in_array( $response->httpResponseCode, array( 301, 302, 303, 307, 308 ) ) )
|
||||
{
|
||||
/* Fix missing hostname in location */
|
||||
$location = $response->httpHeaders['Location'];
|
||||
|
||||
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 );
|
||||
return call_user_func_array( array( $newRequest, $method ), $params );
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
/**
|
||||
* @brief Request Exception Class
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Social Suite
|
||||
* @package IPS Community Suite
|
||||
* @since 26 Aug 2013
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
@@ -21,4 +21,4 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
/**
|
||||
* Request Exception Class
|
||||
*/
|
||||
abstract class _Exception extends \RuntimeException { }
|
||||
class _Exception extends \RuntimeException { }
|
||||
+121
-46
@@ -2,9 +2,9 @@
|
||||
/**
|
||||
* @brief Sockets REST Class
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Social Suite
|
||||
* @package IPS Community Suite
|
||||
* @since 18 Mar 2013
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
@@ -22,17 +22,22 @@ if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
* Sockets REST Class
|
||||
*/
|
||||
class _Sockets
|
||||
{
|
||||
{
|
||||
/**
|
||||
* @brief URL
|
||||
*/
|
||||
protected $url = NULL;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Stream context
|
||||
*/
|
||||
protected $context;
|
||||
|
||||
/**
|
||||
* @brief HTTP Version
|
||||
*/
|
||||
protected $httpVersion = '1.1';
|
||||
|
||||
|
||||
/**
|
||||
* @brief Timeout
|
||||
*/
|
||||
@@ -42,7 +47,7 @@ class _Sockets
|
||||
* @brief Headers
|
||||
*/
|
||||
protected $headers = array();
|
||||
|
||||
|
||||
/**
|
||||
* @brief Follow redirects?
|
||||
*/
|
||||
@@ -60,18 +65,31 @@ class _Sockets
|
||||
public function __construct( $url, $timeout, $httpVersion, $followRedirects )
|
||||
{
|
||||
$this->url = $url;
|
||||
$this->context = stream_context_create();
|
||||
$this->httpVersion = $httpVersion ?: '1.1';
|
||||
$this->timeout = $timeout;
|
||||
$this->followRedirects = $followRedirects;
|
||||
$this->contextOptions['http']['ignore_errors'] = TRUE;
|
||||
|
||||
/* Set our basic settings */
|
||||
stream_context_set_option( $this->context, array(
|
||||
'http' => array(
|
||||
'protocol_version' => $httpVersion,
|
||||
'follow_location' => $followRedirects,
|
||||
'timeout' => $timeout,
|
||||
'ignore_errors' => TRUE,
|
||||
),
|
||||
'ssl' => array(
|
||||
'verify_peer' => FALSE,
|
||||
)
|
||||
) );
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Login
|
||||
*
|
||||
* @param string Username
|
||||
* @param string Password
|
||||
* @retrun \IPS\Http\Request\Socket (for daisy chaining)
|
||||
* @return \IPS\Http\Request\Socket (for daisy chaining)
|
||||
*/
|
||||
public function login( $username, $password )
|
||||
{
|
||||
@@ -99,19 +117,56 @@ class _Sockets
|
||||
*/
|
||||
public function sslCheck( $value=TRUE )
|
||||
{
|
||||
/* This is here for compatibility with curl */
|
||||
stream_context_set_option( $this->context, array(
|
||||
'ssl' => array(
|
||||
'verify_peer_name' => ( $value ) ? 2 : FALSE,
|
||||
'verify_peer' => (boolean) $value,
|
||||
)
|
||||
) );
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic Method: __call
|
||||
* Force TLS
|
||||
*
|
||||
* @param string $method
|
||||
* @param array Params
|
||||
* @return \IPS\Http\Request\Socket
|
||||
*/
|
||||
public function forceTls()
|
||||
{
|
||||
if ( defined('STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT') )
|
||||
{
|
||||
stream_context_set_option( $this->context, array(
|
||||
'ssl' => array(
|
||||
'crypto_method' => STREAM_CRYPTO_METHOD_TLSv1_2_CLIENT
|
||||
)
|
||||
) );
|
||||
}
|
||||
elseif ( defined('STREAM_CRYPTO_METHOD_TLS_CLIENT') )
|
||||
{
|
||||
stream_context_set_option( $this->context, array(
|
||||
'ssl' => array(
|
||||
'crypto_method' => STREAM_CRYPTO_METHOD_TLS_CLIENT
|
||||
)
|
||||
) );
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic Method: __call
|
||||
* Used for other HTTP methods (like PUT and DELETE)
|
||||
*
|
||||
* @param string $method Method (A HTTP method)
|
||||
* @param array $params Parameters (a single parameter with data to post, which can be an array or a string)
|
||||
* @return \IPS\Http\Response
|
||||
* @throws \IPS\Http\Request\CurlException
|
||||
*/
|
||||
public function __call( $method, $params )
|
||||
{
|
||||
$method = mb_strtoupper( $method );
|
||||
|
||||
/* The data (string or array) will be the first parameter */
|
||||
if ( isset( $params[0] ) && is_array( $params[0] ) )
|
||||
{
|
||||
@@ -123,87 +178,107 @@ class _Sockets
|
||||
$data = ( isset( $params[0] ) ? $params[0] : NULL );
|
||||
}
|
||||
|
||||
/* Set the method */
|
||||
$this->contextOptions['http']['method'] = mb_strtoupper( $method );
|
||||
/* Set the method and the Content-Length header if this is a POST, PUT or PATCH request */
|
||||
stream_context_set_option( $this->context, 'http', 'method', $method );
|
||||
|
||||
/* Set the Content-Length header automatically if this is a POST */
|
||||
if( $this->contextOptions['http']['method'] == 'POST' )
|
||||
if( in_array( $method, array( 'POST', 'PUT', 'PATCH' ) ) )
|
||||
{
|
||||
$this->setHeaders( array( 'Content-Length' => \strlen( $data ) ) );
|
||||
}
|
||||
|
||||
/* Parse URL */
|
||||
|
||||
/* Parse URL */
|
||||
if ( isset( $this->url->data['user'] ) or isset( $this->url->data['pass'] ) )
|
||||
{
|
||||
$this->login( isset( $this->url->data['user'] ) ? $this->url->data['user'] : NULL, isset( $this->url->data['pass'] ) ? $this->url->data['pass'] : NULL );
|
||||
}
|
||||
|
||||
$hostname = sprintf( '%s%s:%d',
|
||||
( $this->url->data['scheme'] === 'https' ) ? 'tls://' : '',
|
||||
$this->url->data['host'],
|
||||
isset( $this->url->data['port'] )
|
||||
? $this->url->data['port']
|
||||
: ( $this->url->data['scheme'] === 'http' ? 80 : 443 )
|
||||
);
|
||||
|
||||
/* Open connection */
|
||||
$resource = @\fsockopen( ( $this->url->data['scheme'] === 'https' ? 'ssl://' : '' ) . $this->url->data['host'], isset( $this->url->data['port'] ) ? $this->url->data['port'] : ( $this->url->data['scheme'] === 'http' ? 80 : 443 ), $errno, $errstr, $this->timeout );
|
||||
try
|
||||
{
|
||||
$resource = stream_socket_client( $hostname, $errno, $errstr, $this->timeout, \STREAM_CLIENT_CONNECT, $this->context );
|
||||
}
|
||||
/* Catch issues that may arise, such as DNS failure */
|
||||
catch( \ErrorException $e )
|
||||
{
|
||||
throw new SocketsException( $e->getMessage(), $e->getCode() );
|
||||
}
|
||||
|
||||
if ( $resource === FALSE )
|
||||
{
|
||||
throw new SocketsException( $errstr, $errno );
|
||||
}
|
||||
|
||||
/* Work out location to use. Ideally use it exactly as $this->url has it as some servers (Google is one) will be sensitive about the encoding
|
||||
and we will want to use whatever was passed to \IPS\Url::__construct. If we can't figure it out, construct manually */
|
||||
$pathPos = ( isset( $this->url->data['path'] ) ) ? mb_strpos( $this->url, $this->url->data['path'] ) : FALSE;
|
||||
if ( $pathPos !== FALSE )
|
||||
{
|
||||
$location = mb_substr( $this->url, $pathPos );
|
||||
}
|
||||
else
|
||||
{
|
||||
$location = ( isset( $this->url->data['path'] ) ? $this->url->data['path'] : '' ) . ( !empty( $this->url->queryString ) ? ( '?' . http_build_query( $this->url->queryString ) ) : '' ) . ( isset( $this->url->data['fragment'] ) ? "#{$this->url->data['fragment']}" : '' );
|
||||
}
|
||||
|
||||
/* Send request */
|
||||
$request = mb_strtoupper( $method ) . ' ' . $location . " HTTP/{$this->httpVersion}\r\n";
|
||||
$request .= "Host: {$this->url->data['host']}\r\n";
|
||||
|
||||
/* Get the location */
|
||||
$location = $this->url->data['path'] ? \IPS\Http\Url::encodeComponent( \IPS\Http\Url::COMPONENT_PATH, $this->url->data['path'] ) : '';
|
||||
$location .= $this->url->data['query'] ? '?' . \IPS\Http\Url::encodeComponent( \IPS\Http\Url::COMPONENT_QUERY, $this->url->data['query'] ) : '';
|
||||
$location .= $this->url->data['fragment'] ? '#' . \IPS\Http\Url::encodeComponent( \IPS\Http\Url::COMPONENT_FRAGMENT, $this->url->data['fragment'] ) : '';
|
||||
|
||||
/* Send request */
|
||||
$request = mb_strtoupper( $method ) . ' /' . ltrim( $location, '/' ) . " HTTP/{$this->httpVersion}\r\n";
|
||||
$request .= "Host: {$this->url->data['host']}" . ( isset( $this->url->data['port'] ) ? ":{$this->url->data['port']}" : '' ) . "\r\n";
|
||||
|
||||
foreach ( $this->headers as $k => $v )
|
||||
{
|
||||
$request .= "{$k}: {$v}\r\n";
|
||||
}
|
||||
|
||||
$request .= "Connection: Close\r\n";
|
||||
$request .= "\r\n";
|
||||
|
||||
if ( $data )
|
||||
{
|
||||
$request .= $data;
|
||||
}
|
||||
|
||||
\fwrite( $resource, $request );
|
||||
|
||||
|
||||
/* Read response */
|
||||
stream_set_timeout( $resource, $this->timeout );
|
||||
$status = stream_get_meta_data( $resource );
|
||||
|
||||
$response = '';
|
||||
while( !feof($resource) and !$status['timed_out'] )
|
||||
while( !feof($resource) and !$status['timed_out'] )
|
||||
{
|
||||
$response .= \fgets( $resource, 8192 );
|
||||
$status = stream_get_meta_data( $resource );
|
||||
}
|
||||
|
||||
|
||||
/* Close connection */
|
||||
\fclose( $resource );
|
||||
|
||||
/* Log */
|
||||
try
|
||||
/* 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 )
|
||||
{
|
||||
\IPS\Log::i( LOG_DEBUG )->write( "\n\n------------------------------------\nSOCKETS REQUEST: {$this->url}\n------------------------------------\n\n{$request}\n\n------------------------------------\nRESPONSE\n------------------------------------\n\n" . $response, 'request' );
|
||||
\IPS\Log::debug( "\n\n------------------------------------\nSOCKETS REQUEST: {$this->url}\n------------------------------------\n\n{$request}\n\n------------------------------------\nRESPONSE\n------------------------------------\n\n" . $response, 'request' );
|
||||
}
|
||||
catch( \Exception $e ){}
|
||||
|
||||
/* Interpret response */
|
||||
$response = new \IPS\Http\Response( $response );
|
||||
|
||||
/* Either return it or follow it */
|
||||
if ( $this->followRedirects and ( $response->httpResponseCode == 302 OR $response->httpResponseCode == 301 ) )
|
||||
if ( $this->followRedirects and in_array( $response->httpResponseCode, array( 301, 302, 303, 307, 308 ) ) )
|
||||
{
|
||||
$newRequest = \IPS\Http\Url::external( $response->httpHeaders['Location'] )->request( $this->timeout, $this->httpVersion, is_int( $this->followRedirects ) ? ( $this->followRedirects - 1 ) : $this->followRedirects );
|
||||
/* Fix missing hostname in location */
|
||||
$location = $response->httpHeaders['Location'];
|
||||
|
||||
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 );
|
||||
return call_user_func_array( array( $newRequest, $method ), $params );
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
/**
|
||||
* @brief HTTP Response Class
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Social Suite
|
||||
* @package IPS Community Suite
|
||||
* @since 18 Mar 2013
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
@@ -42,6 +42,11 @@ class _Response
|
||||
* @brief HTTP Headers
|
||||
*/
|
||||
public $httpHeaders = NULL;
|
||||
|
||||
/**
|
||||
* @brief Cookies returned in the response
|
||||
*/
|
||||
public $cookies = array();
|
||||
|
||||
/**
|
||||
* @brief Response Content
|
||||
@@ -79,7 +84,7 @@ class _Response
|
||||
}
|
||||
|
||||
/* If we have received a 1xx response code, that means it's not the final response */
|
||||
if ( mb_substr( $this->httpResponseCode, 0, 1 ) == 1 )
|
||||
if ( mb_substr( $this->httpResponseCode, 0, 1 ) == 1 OR ( $this->httpResponseCode == 200 AND trim( $this->httpResponseText ) == 'Connection established' ) )
|
||||
{
|
||||
$content = trim( mb_substr( $content, $firstLineBreak ) );
|
||||
}
|
||||
@@ -112,6 +117,26 @@ class _Response
|
||||
if ( $line !== '' and preg_match( "/^(.+?): (.+?)$/", $line, $matches ) )
|
||||
{
|
||||
$this->httpHeaders[ $matches[1] ] = $matches[2];
|
||||
|
||||
if( mb_strtolower( $matches[1] ) == 'set-cookie' )
|
||||
{
|
||||
$cookie = array();
|
||||
$cookieDetails = explode( ';', $matches[2] );
|
||||
|
||||
$cookieInfo = array_shift( $cookieDetails );
|
||||
$cookieInfo = explode( '=', $cookieInfo );
|
||||
|
||||
$cookie[ $cookieInfo[0] ] = array( 'value' => rawurldecode( $cookieInfo[1] ) );
|
||||
|
||||
foreach( $cookieDetails as $cookieDetail )
|
||||
{
|
||||
$cookieDetail = explode( '=', $cookieDetail );
|
||||
|
||||
$cookie[ $cookieInfo[0] ][ trim( $cookieDetail[0] ) ] = ( isset( $cookieDetail[1] ) ) ? $cookieDetail[1] : true;
|
||||
}
|
||||
|
||||
$this->cookies = array_merge( $this->cookies, $cookie );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -123,7 +148,7 @@ class _Response
|
||||
|
||||
if( isset( $this->httpHeaders['Transfer-Encoding'] ) and mb_strtolower( trim( $this->httpHeaders['Transfer-Encoding'] ) ) == 'chunked' )
|
||||
{
|
||||
if ( $decoded = $this->decodeChunked( $this->content ) )
|
||||
if ( ( $decoded = $this->decodeChunked( $this->content ) ) !== '' )
|
||||
{
|
||||
$this->content = $decoded;
|
||||
}
|
||||
@@ -194,11 +219,11 @@ class _Response
|
||||
*/
|
||||
public function decodeJson( $asArray=TRUE )
|
||||
{
|
||||
if ( !( $json = json_decode( $this->content, $asArray ) ) )
|
||||
$json = json_decode( $this->content, $asArray );
|
||||
if ( $json === FALSE )
|
||||
{
|
||||
throw new \RuntimeException('BAD_JSON');
|
||||
}
|
||||
|
||||
return $json;
|
||||
}
|
||||
|
||||
|
||||
+1230
-1098
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief URL Exception
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 12 May 2015
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\Http\Url;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* URL Exception
|
||||
*/
|
||||
class _Exception extends \DomainException
|
||||
{
|
||||
|
||||
}
|
||||
@@ -0,0 +1,681 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Friendly URL
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 11 May 2015
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\Http\Url;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Friendly URL
|
||||
*/
|
||||
class _Friendly extends \IPS\Http\Url\Internal
|
||||
{
|
||||
/**
|
||||
* @brief Base
|
||||
*/
|
||||
public $base = 'front';
|
||||
|
||||
/**
|
||||
* @brief SEO Template
|
||||
*/
|
||||
public $seoTemplate;
|
||||
|
||||
/**
|
||||
* @brief SEO Titles (unencoded)
|
||||
*/
|
||||
public $seoTitles = array();
|
||||
|
||||
/**
|
||||
* @brief The friendly URL component, which may be for the path or the query string (e.g. "topic/1-test")
|
||||
*/
|
||||
public $friendlyUrlComponent;
|
||||
|
||||
/* !Factory methods */
|
||||
|
||||
/**
|
||||
* Create a friendly URL from a query string
|
||||
*
|
||||
* @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
|
||||
* @throws \IPS\Http\Url\Exception
|
||||
*/
|
||||
public static function friendlyUrlFromComponent( $protocol, $friendlyUrlComponent, $queryString )
|
||||
{
|
||||
if ( \IPS\Settings::i()->htaccess_mod_rewrite )
|
||||
{
|
||||
return static::createInternalFromComponents(
|
||||
'front',
|
||||
$protocol,
|
||||
$friendlyUrlComponent ? "{$friendlyUrlComponent}/" : '',
|
||||
$queryString
|
||||
);
|
||||
}
|
||||
else
|
||||
{
|
||||
return static::createInternalFromComponents(
|
||||
'front',
|
||||
$protocol,
|
||||
'index.php',
|
||||
( $friendlyUrlComponent ? array( "/{$friendlyUrlComponent}/" => NULL ) : array() ) + $queryString
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a friendly URL from a full URL, working out friendly URL data
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
public static function createFriendlyUrlFromComponents( $components, $potentialFurl )
|
||||
{
|
||||
/* Loop each of our friendly URL definitions */
|
||||
$matchedFurlDefinition = NULL;
|
||||
$seoTitles = array();
|
||||
foreach ( static::furlDefinition() as $seoTemplate => $furlDefinition )
|
||||
{
|
||||
if ( $returnedMatches = static::getMatchedParamsFromFriendlyUrlComponent( $furlDefinition, $potentialFurl ) )
|
||||
{
|
||||
$matchedFurlDefinition = $furlDefinition;
|
||||
list( $matchedParams, $seoTitles ) = $returnedMatches;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* If we didn't get one return NULL */
|
||||
if ( !$matchedFurlDefinition )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* If we did, create our object... */
|
||||
return static::createFromComponents( $components[ static::COMPONENT_HOST ], $components[ static::COMPONENT_SCHEME ], $components[ static::COMPONENT_PATH ], $components[ static::COMPONENT_QUERY ], $components[ static::COMPONENT_PORT ], $components[ static::COMPONENT_USERNAME ], $components[ static::COMPONENT_PASSWORD ], $components[ static::COMPONENT_FRAGMENT ] )
|
||||
->setFriendlyUrlData( $seoTemplate, $seoTitles, $matchedParams, $potentialFurl );
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a friendly URL from a query string with known friendly URL data
|
||||
*
|
||||
* @param string $queryString The query string
|
||||
* @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
|
||||
* @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 )
|
||||
{
|
||||
/* Get the friendly URL component */
|
||||
try
|
||||
{
|
||||
$friendlyUrlComponent = static::buildFriendlyUrlComponentFromData( $queryString, $seoTemplate, $seoTitles );
|
||||
}
|
||||
catch ( \IPS\Http\Url\Exception $e )
|
||||
{
|
||||
if ( $e->getMessage() === 'INVALID_SEO_TEMPLATE' and !\IPS\IN_DEV )
|
||||
{
|
||||
return static::createInternalFromComponents( 'front', $protocol, 'index.php', $queryString );
|
||||
}
|
||||
else
|
||||
{
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
/* Extract the hidden query string parameters inside it */
|
||||
$matchedParams = array();
|
||||
$furlDefinition = static::furlDefinition();
|
||||
if ( !isset( $furlDefinition[ $seoTemplate ] ) )
|
||||
{
|
||||
throw new \IPS\Http\Url\Exception( 'INVALID_SEO_TEMPLATE' );
|
||||
}
|
||||
if ( $returnedMatches = static::getMatchedParamsFromFriendlyUrlComponent( $furlDefinition[ $seoTemplate ], $friendlyUrlComponent ) )
|
||||
{
|
||||
list( $matchedParams, $_seoTitles ) = $returnedMatches;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( \IPS\IN_DEV )
|
||||
{
|
||||
throw new \IPS\Http\Url\Exception( 'SEO_TEMPLATE_IS_NOT_VALID_FOR_URL' );
|
||||
}
|
||||
}
|
||||
|
||||
/* Return */
|
||||
return static::friendlyUrlFromComponent( $protocol, $friendlyUrlComponent, $queryString )->setFriendlyUrlData( $seoTemplate, $seoTitles, $matchedParams, $friendlyUrlComponent );
|
||||
}
|
||||
|
||||
/* !Instance */
|
||||
|
||||
/**
|
||||
* Set friendly URL data
|
||||
*
|
||||
* @param string $seoTemplate The key for making this a friendly URL
|
||||
* @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
|
||||
* @throws \IPS\Http\Url\Exception
|
||||
*/
|
||||
protected function setFriendlyUrlData( $seoTemplate, $seoTitles, $matchedParams=array(), $friendlyUrlComponent )
|
||||
{
|
||||
/* Get the definition */
|
||||
$furlDefinition = static::furlDefinition();
|
||||
if ( !isset( $furlDefinition[ $seoTemplate ] ) )
|
||||
{
|
||||
throw new \IPS\Http\Url\Exception( 'INVALID_SEO_TEMPLATE' );
|
||||
}
|
||||
|
||||
/* Set basic properties */
|
||||
$this->seoTemplate = $seoTemplate;
|
||||
$this->seoTitles = is_string( $seoTitles ) ? array( $seoTitles ) : $seoTitles;
|
||||
$this->friendlyUrlComponent = $friendlyUrlComponent;
|
||||
|
||||
/* Set hidden query string */
|
||||
parse_str( $furlDefinition[ $seoTemplate ]['real'], $hiddenQueryString );
|
||||
$this->hiddenQueryString = $hiddenQueryString + $matchedParams;
|
||||
|
||||
/* Return */
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate that the SEO title is correct
|
||||
*
|
||||
* @return mixed The correct URL if incorrect, TRUE is correct, or NULL if unknown
|
||||
*/
|
||||
public function correctFriendlyUrl()
|
||||
{
|
||||
/* Get the definition */
|
||||
$furlDefinition = static::furlDefinition();
|
||||
|
||||
/* If we don't have a validate callback, we can return NULL */
|
||||
if ( !isset( $furlDefinition[ $this->seoTemplate ]['verify'] ) or !$furlDefinition[ $this->seoTemplate ]['verify'] )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Load it */
|
||||
try
|
||||
{
|
||||
if ( $correctUrl = $this->correctUrlFromVerifyClass( $furlDefinition[ $this->seoTemplate ]['verify'] ) )
|
||||
{
|
||||
/* IP.Board 3.x used /page-x in the path rather than a query string argument - we support this so as not to break past links */
|
||||
if( mb_strpos( (string) $this, '/page-' ) )
|
||||
{
|
||||
preg_match( "/\/page\-(\d+)/", (string) $this, $matches );
|
||||
|
||||
if( isset( $matches[1] ) )
|
||||
{
|
||||
$correctUrl = $correctUrl->setQueryString( 'page', $matches[1] );
|
||||
}
|
||||
}
|
||||
|
||||
/* IP.Board 3.x also used to do /page__x__y__a__b for /?x=y&a=b ... let's extract those parameters */
|
||||
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 )
|
||||
{
|
||||
$key = $param;
|
||||
}
|
||||
else
|
||||
{
|
||||
$correctUrl = $correctUrl->setQueryString( $key, $param );
|
||||
$key = NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( (string) $correctUrl->normaliseForEqualityCheck() !== (string) $this->normaliseForEqualityCheck() )
|
||||
{
|
||||
return $correctUrl;
|
||||
}
|
||||
else
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
/* It doesn't exist */
|
||||
catch ( \OutOfRangeException $e )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Normalise the URL for comparing if the "correct" URL is different to the accessed URL
|
||||
*
|
||||
* @return \IPS\Http\Url
|
||||
*/
|
||||
public function normaliseForEqualityCheck()
|
||||
{
|
||||
/* Strip the query string except the FURL component */
|
||||
$return = $this->stripQueryString();
|
||||
|
||||
/* Make it schema-relative */
|
||||
$return = $return->setScheme( NULL );
|
||||
|
||||
/* Return */
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Strip Query String
|
||||
* Overrides main method so that the FURL slug isn't lost
|
||||
*
|
||||
* @param string|array $keys The key(s) to strip - if omitted, entire query string is wiped
|
||||
* @return \IPS\Http\Url
|
||||
*/
|
||||
public function stripQueryString( $keys=NULL )
|
||||
{
|
||||
if ( $keys === NULL and !\IPS\Settings::i()->htaccess_mod_rewrite )
|
||||
{
|
||||
$keys = $this->queryString;
|
||||
unset( $keys[ "/{$this->friendlyUrlComponent}/" ] );
|
||||
$keys = array_keys( $keys );
|
||||
}
|
||||
|
||||
return parent::stripQueryString( $keys );
|
||||
}
|
||||
|
||||
/* !Utilities */
|
||||
|
||||
/**
|
||||
* Get friendly URL component (e.g. "topic/1-test") from a query string and SEO template
|
||||
*
|
||||
* @param string $queryString The query string - is passed by reference and any parts used are removed, which can be used to detect extraneous parts
|
||||
* @param string $seoTemplate The key for making this a friendly URL
|
||||
* @param string|array $seoTitles The title(s) needed for the friendly URL
|
||||
* @return string
|
||||
* @throws \IPS\Http\Url\Exception
|
||||
*/
|
||||
public static function buildFriendlyUrlComponentFromData( &$queryString, $seoTemplate, $seoTitles )
|
||||
{
|
||||
/* Get the definition */
|
||||
$furlDefinition = static::furlDefinition();
|
||||
if ( !isset( $furlDefinition[ $seoTemplate ] ) )
|
||||
{
|
||||
throw new \IPS\Http\Url\Exception( 'INVALID_SEO_TEMPLATE' );
|
||||
}
|
||||
$component = $furlDefinition[ $seoTemplate ]['friendly'];
|
||||
|
||||
/* Parse the query string into an array */
|
||||
parse_str( $queryString, $queryString );
|
||||
|
||||
/* For each query string component, replace it out */
|
||||
foreach ( $queryString as $k => $v )
|
||||
{
|
||||
if ( mb_strpos( $component, "{#{$k}}" ) !== FALSE or mb_strpos( $component, "{@{$k}}" ) !== FALSE )
|
||||
{
|
||||
$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 );
|
||||
for ( $i = 0; $i < $seoTitlesCount; $i++ )
|
||||
{
|
||||
if ( $i === 0 )
|
||||
{
|
||||
$component = str_replace( '{?}', $seoTitles[ $i ], $component );
|
||||
}
|
||||
$component = str_replace( "{?{$i}}", $seoTitles[ $i ], $component );
|
||||
}
|
||||
|
||||
/* Remove the "real" parts in the query string */
|
||||
parse_str( $furlDefinition[ $seoTemplate ]['real'], $realQueryString );
|
||||
foreach ( $realQueryString as $k => $v )
|
||||
{
|
||||
if ( isset( $queryString[ $k ] ) and $queryString[ $k ] == $v )
|
||||
{
|
||||
unset( $queryString[ $k ] );
|
||||
}
|
||||
}
|
||||
|
||||
/* Return */
|
||||
return $component;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a value into an "SEO Title" for friendly URLs
|
||||
*
|
||||
* @param string $value Value
|
||||
* @return string
|
||||
* @note Many places require an SEO title, so we always need to return something, so when no valid title is available we return a dash
|
||||
*/
|
||||
public static function seoTitle( $value )
|
||||
{
|
||||
/* Ensure there are no HTML tags */
|
||||
$value = strip_tags( $value );
|
||||
|
||||
/* Always lowercase */
|
||||
$value = mb_strtolower( $value );
|
||||
|
||||
/* Get rid of newlines/carriage returns as they're not cool in friendly URL titles */
|
||||
$value = str_replace( array( "\r\n", "\r", "\n" ), ' ', $value );
|
||||
|
||||
/* Just for readability */
|
||||
$value = str_replace( ' ', '-', $value );
|
||||
|
||||
/* Disallowed characters which browsers may try to automatically percent-encode */
|
||||
$value = str_replace( array( '!', '*', '\'', '(', ')', ';', ':', '@', '&', '=', '+', '$', ',', '/', '?', '#', '[', ']', '%', '\\', '"', '<', '>', '^', '{', '}', '|', '.', '`' ), '', $value );
|
||||
if(defined("\IPS\IPB_USE_SEO_TRANSLIT")&&\IPS\IPB_USE_SEO_TRANSLIT) $value = self::rus2translit($value);
|
||||
/* Trim */
|
||||
$value = preg_replace( '/\-+/', '-', $value );
|
||||
$value = trim( $value, '-' );
|
||||
$value = trim( $value );
|
||||
|
||||
/* Return */
|
||||
return $value ?: '-';
|
||||
}
|
||||
|
||||
static function rus2translit($string) {
|
||||
|
||||
$converter = array(
|
||||
|
||||
'а' => 'a', 'б' => 'b', 'в' => 'v',
|
||||
|
||||
'г' => 'g', 'д' => 'd', 'е' => 'e',
|
||||
|
||||
'ё' => 'e', 'ж' => 'zh', 'з' => 'z',
|
||||
|
||||
'и' => 'i', 'й' => 'y', 'к' => 'k',
|
||||
|
||||
'л' => 'l', 'м' => 'm', 'н' => 'n',
|
||||
|
||||
'о' => 'o', 'п' => 'p', 'р' => 'r',
|
||||
|
||||
'с' => 's', 'т' => 't', 'у' => 'u',
|
||||
|
||||
'ф' => 'f', 'х' => 'h', 'ц' => 'c',
|
||||
|
||||
'ч' => 'ch', 'ш' => 'sh', 'щ' => 'sch',
|
||||
|
||||
'ь' => '', 'ы' => 'y', 'ъ' => '',
|
||||
|
||||
'э' => 'e', 'ю' => 'yu', 'я' => 'ya',
|
||||
|
||||
|
||||
|
||||
'А' => 'A', 'Б' => 'B', 'В' => 'V',
|
||||
|
||||
'Г' => 'G', 'Д' => 'D', 'Е' => 'E',
|
||||
|
||||
'Ё' => 'E', 'Ж' => 'Zh', 'З' => 'Z',
|
||||
|
||||
'И' => 'I', 'Й' => 'Y', 'К' => 'K',
|
||||
|
||||
'Л' => 'L', 'М' => 'M', 'Н' => 'N',
|
||||
|
||||
'О' => 'O', 'П' => 'P', 'Р' => 'R',
|
||||
|
||||
'С' => 'S', 'Т' => 'T', 'У' => 'U',
|
||||
|
||||
'Ф' => 'F', 'Х' => 'H', 'Ц' => 'C',
|
||||
|
||||
'Ч' => 'Ch', 'Ш' => 'Sh', 'Щ' => 'Sch',
|
||||
|
||||
'Ь' => '', 'Ы' => 'Y', 'Ъ' => '',
|
||||
|
||||
'Э' => 'E', 'Ю' => 'Yu', 'Я' => 'Ya',
|
||||
|
||||
);
|
||||
|
||||
return strtr($string, $converter);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the matched parameters and SEO titles from a friendly URL component for a particular
|
||||
*
|
||||
* @param array $furlDefinition The FURL definition from the furl.json file
|
||||
* @param string $friendlyUrlComponent The friendly URL component, which may be for the path or the query string (e.g. "topic/1-test")
|
||||
* @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 ) )
|
||||
{
|
||||
foreach ( $furlDefinition['params'] as $k => $param )
|
||||
{
|
||||
if ( $param )
|
||||
{
|
||||
$matchedParams[ $param ] = $matches[ $k + 1 ];
|
||||
}
|
||||
else
|
||||
{
|
||||
$seoTitles[] = $matches[ $k + 1 ];
|
||||
}
|
||||
}
|
||||
|
||||
return array( $matchedParams, $seoTitles );
|
||||
}
|
||||
}
|
||||
|
||||
/* Still here? No match */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief FURL Definition
|
||||
*/
|
||||
protected static $furlDefinition = NULL;
|
||||
|
||||
/**
|
||||
* Get FURL Definition
|
||||
*
|
||||
* @param bool $revert If TRUE, ignores all customisations and reloads from json
|
||||
* @return array
|
||||
*/
|
||||
public static function furlDefinition( $revert=FALSE )
|
||||
{
|
||||
if ( static::$furlDefinition === NULL or $revert )
|
||||
{
|
||||
$furlCustomizations = ( \IPS\Settings::i()->furl_configuration AND !$revert ) ? 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 )
|
||||
{
|
||||
$furlConfiguration = static::buildFurlConfiguation();
|
||||
}
|
||||
|
||||
static::$furlDefinition = array_merge( $furlConfiguration, $furlCustomizations );
|
||||
}
|
||||
|
||||
return static::$furlDefinition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rebuild and return \IPS\Data\Store::i()->furl_configuration with default, uncustomised values
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected static function buildFurlConfiguation()
|
||||
{
|
||||
/* Init */
|
||||
$furlConfiguration = array();
|
||||
|
||||
/* Load apps, prioritising the default (otherwise if two apps both have "/category/..." the app which is higher in the list may steal from the default app) */
|
||||
$applications = \IPS\Application::applications();
|
||||
foreach ( $applications as $k => $app )
|
||||
{
|
||||
if ( $app->default )
|
||||
{
|
||||
unset( $applications[ $k ] );
|
||||
array_unshift( $applications, $app );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Loop each app... */
|
||||
foreach ( $applications as $app )
|
||||
{
|
||||
/* If it has a furl.json file... */
|
||||
if( file_exists( \IPS\ROOT_PATH . "/applications/{$app->directory}/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 );
|
||||
$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 );
|
||||
}
|
||||
|
||||
/* Add it in */
|
||||
$furlConfiguration = array_merge( $furlConfiguration, $definitions );
|
||||
}
|
||||
}
|
||||
|
||||
/* Store */
|
||||
\IPS\Data\Store::i()->furl_configuration = json_encode( $furlConfiguration );
|
||||
|
||||
/* Return */
|
||||
return $furlConfiguration;
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the friendly URL definition
|
||||
*
|
||||
* @param string $friendly Friendly URL pattern
|
||||
* @param string $real Non-friendly URL pattern
|
||||
* @param NULL|string $appTopLevel FURL slug if the app is not the default app
|
||||
* @param bool $appIsDefault Flag to indicate if the app is default or not
|
||||
* @param NULL|string $alias Friendly URL alias
|
||||
* @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
|
||||
* @return array
|
||||
*/
|
||||
public static function buildFurlDefinition( $friendly, $real, $appTopLevel = NULL, $appIsDefault = FALSE, $alias = NULL, $custom = FALSE, $verify = NULL )
|
||||
{
|
||||
/* Init */
|
||||
$return = array(
|
||||
'friendly' => $friendly,
|
||||
'real' => $real
|
||||
);
|
||||
if ( $verify )
|
||||
{
|
||||
$return['verify'] = $verify;
|
||||
}
|
||||
if ( $custom )
|
||||
{
|
||||
$return['custom'] = TRUE;
|
||||
}
|
||||
|
||||
/* 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 )
|
||||
{
|
||||
if ( $appIsDefault )
|
||||
{
|
||||
$return['with_top_level'] = $appTopLevel . ( $friendly ? '/' . $friendly : '' );
|
||||
}
|
||||
else
|
||||
{
|
||||
$return['without_top_level'] = $friendly;
|
||||
$return['friendly'] = $appTopLevel . ( $friendly ? '/' . $friendly : '' );
|
||||
}
|
||||
}
|
||||
|
||||
/* Figure out the regexes */
|
||||
$return['regex'] = array( preg_quote( $return['friendly'], '/' ) );
|
||||
if ( $alias )
|
||||
{
|
||||
$return['regex'][] = str_replace( '\{\!\}', '(?!&)(?:.+?)', preg_quote( $alias, '/' ) );
|
||||
}
|
||||
if ( isset( $return['without_top_level'] ) and $return['without_top_level'] )
|
||||
{
|
||||
$return['regex'][] = preg_quote( $return['without_top_level'], '/' );
|
||||
}
|
||||
elseif ( isset( $return['with_top_level'] ) )
|
||||
{
|
||||
$return['regex'][] = preg_quote( $return['with_top_level'], '/' );
|
||||
}
|
||||
|
||||
/* Parse out variables */
|
||||
$return['params'] = array();
|
||||
preg_match_all( '/{(.+?)}/', $return['friendly'], $matches );
|
||||
foreach ( $matches[1] as $tag )
|
||||
{
|
||||
switch ( mb_substr( $tag, 0, 1 ) )
|
||||
{
|
||||
case '#':
|
||||
$return['regex'] = array_map( function( $_regex ) use ( $tag ) {
|
||||
return str_replace( '\{#' . 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['regex'] );
|
||||
$return['params'][] = mb_substr( $tag, 1 );
|
||||
break;
|
||||
|
||||
case '?':
|
||||
$return['regex'] = array_map( function( $_regex ) use ( $tag ) {
|
||||
return str_replace( '\{\?\}', '(?!&)(.+?)', $_regex );
|
||||
}, $return['regex'] );
|
||||
$return['params'][] = '';
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Return */
|
||||
return $return;
|
||||
}
|
||||
|
||||
/* !Deprecated */
|
||||
|
||||
/**
|
||||
* @brief Is internal?
|
||||
* @deprecated Using this property is deprecated. Check if instance of \IPS\Http\Url\Internal
|
||||
*/
|
||||
public $isInternal = TRUE;
|
||||
|
||||
/**
|
||||
* @brief Is friendly?
|
||||
* @deprecated Using this property is deprecated. Check if instance of \IPS\Http\Url\Friendly
|
||||
*/
|
||||
public $isFriendly = TRUE;
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Internal URL
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Community Suite
|
||||
* @since 11 May 2015
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
|
||||
namespace IPS\Http\Url;
|
||||
|
||||
/* To prevent PHP errors (extending class does not exist) revealing path */
|
||||
if ( !defined( '\IPS\SUITE_UNIQUE_KEY' ) )
|
||||
{
|
||||
header( ( isset( $_SERVER['SERVER_PROTOCOL'] ) ? $_SERVER['SERVER_PROTOCOL'] : 'HTTP/1.0' ) . ' 403 Forbidden' );
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal URL
|
||||
*/
|
||||
class _Internal extends \IPS\Http\Url
|
||||
{
|
||||
/**
|
||||
* Create internal URL from components - all arguments should be UNENCODED
|
||||
*
|
||||
* @param string $base Key for the URL base
|
||||
* @param int|string|null $protocol Protocol (one of the PROTOCOL_* constants) or an actual scheme
|
||||
* @param string $path Path, relative to site's base URL
|
||||
* @param string|array|NULL $query Query
|
||||
* @param string|NULL $fragment Fragment
|
||||
* @return \IPS\Http\Url
|
||||
*/
|
||||
protected static function createInternalFromComponents( $base, $protocol, $path, $query = NULL, $fragment = NULL )
|
||||
{
|
||||
$baseUrlData = parse_url( static::baseUrl( is_numeric( $protocol ) ? $protocol : NULL ) );
|
||||
|
||||
$pathToUse = '';
|
||||
if ( isset( $baseUrlData['path'] ) and $baseUrlData['path'] )
|
||||
{
|
||||
$pathToUse .= trim( $baseUrlData['path'], '/' );
|
||||
}
|
||||
if ( $path or $query or $fragment or $pathToUse )
|
||||
{
|
||||
if ( $pathToUse )
|
||||
{
|
||||
$pathToUse .= '/';
|
||||
}
|
||||
$pathToUse .= ltrim( $path, '/' );
|
||||
}
|
||||
|
||||
$return = static::createFromComponents(
|
||||
$baseUrlData['host'],
|
||||
( 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,
|
||||
isset( $baseUrlData['user'] ) ? $baseUrlData['user'] : NULL,
|
||||
isset( $baseUrlData['pass'] ) ? $baseUrlData['pass'] : NULL,
|
||||
$fragment
|
||||
);
|
||||
|
||||
$return->base = $base;
|
||||
return $return;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Base
|
||||
*/
|
||||
public $base;
|
||||
|
||||
/**
|
||||
* Get the friendly URL for this URL if there is one
|
||||
*
|
||||
* @return mixed The friendly URL if there is one, TRUE if there isn't, or NULL if not sure
|
||||
*/
|
||||
public function correctFriendlyUrl()
|
||||
{
|
||||
foreach ( \IPS\Http\Url\Friendly::furlDefinition() as $seoTemplate => $furlDefinition )
|
||||
{
|
||||
if( mb_stripos( $this->data[ static::COMPONENT_QUERY ], $furlDefinition['real'] ) !== FALSE )
|
||||
{
|
||||
/* Figure out if this FURL definition requires extra data.
|
||||
Example: messenger_convo and messenger have the same $furlDefinition['real'] definition, but messenger_convo requires an 'id' parameter too */
|
||||
$params = array();
|
||||
preg_match_all( '/{(.+?)}/', $furlDefinition['friendly'], $matches );
|
||||
foreach ( $matches[1] as $tag )
|
||||
{
|
||||
switch ( mb_substr( $tag, 0, 1 ) )
|
||||
{
|
||||
case '#':
|
||||
$params[] = mb_substr( $tag, 1 );
|
||||
break;
|
||||
|
||||
case '@':
|
||||
$params[] = mb_substr( $tag, 1 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* 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 ) ) ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Now try to check URL */
|
||||
try
|
||||
{
|
||||
/* If there's a "verify" method, we can use that to get the correct URL */
|
||||
if( !empty( $furlDefinition['verify'] ) )
|
||||
{
|
||||
/* Load it */
|
||||
try
|
||||
{
|
||||
if ( $return = $this->correctUrlFromVerifyClass( $furlDefinition['verify'] ) )
|
||||
{
|
||||
return $return;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
/* It doesn't exist */
|
||||
catch ( \OutOfRangeException $e )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
/* If not, we have to build it ourself */
|
||||
else
|
||||
{
|
||||
$seoTitles = array();
|
||||
if ( isset( $furlDefinition['seoTitles'] ) )
|
||||
{
|
||||
foreach ( $furlDefinition['seoTitles'] as $seoTitleData )
|
||||
{
|
||||
try
|
||||
{
|
||||
$class = $seoTitleData['class'];
|
||||
$queryParam = $seoTitleData['queryParam'];
|
||||
$property = $seoTitleData['property'];
|
||||
|
||||
$seoTitles[] = $class::load( $this->queryString[ $queryParam ] )->$property;
|
||||
}
|
||||
catch ( \OutOfRangeException $e ) {}
|
||||
}
|
||||
}
|
||||
|
||||
return \IPS\Http\Url::internal( $this->data['query'], 'front', $seoTemplate, $seoTitles );
|
||||
}
|
||||
}
|
||||
catch ( \Exception $e ) {}
|
||||
}
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the correct URL using a "verify" property from the FURL definition
|
||||
*
|
||||
* @return
|
||||
* @throws \OutOfRangeException
|
||||
*/
|
||||
protected function correctUrlFromVerifyClass( $verify )
|
||||
{
|
||||
/* Load it */
|
||||
if ( method_exists( $verify, 'preCorrectUrlFromVerifyClass' ) )
|
||||
{
|
||||
$verify::preCorrectUrlFromVerifyClass( $this );
|
||||
}
|
||||
$contentObject = $verify::loadFromUrl( $this );
|
||||
|
||||
/* Check the currently logged in user can view it, as we don't want to reveal the correct
|
||||
URL to something the user has no permission to view */
|
||||
$canView = TRUE;
|
||||
if ( $contentObject instanceof \IPS\Content )
|
||||
{
|
||||
$canView = $contentObject->canView();
|
||||
}
|
||||
elseif ( $contentObject instanceof \IPS\Node\Model )
|
||||
{
|
||||
$canView = $contentObject->can('view');
|
||||
}
|
||||
if ( !$canView )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* Create it */
|
||||
$correctUrl = $contentObject->url();
|
||||
|
||||
/* Set extra stuff in our query string */
|
||||
$paramsToSet = array();
|
||||
foreach ( $this->queryString as $k => $v )
|
||||
{
|
||||
if ( !array_key_exists( $k, $correctUrl->queryString ) and !array_key_exists( $k, $correctUrl->hiddenQueryString ) )
|
||||
{
|
||||
$paramsToSet[ $k ] = $v;
|
||||
}
|
||||
}
|
||||
if ( count( $paramsToSet ) )
|
||||
{
|
||||
$correctUrl = $correctUrl->setQueryString( $paramsToSet );
|
||||
}
|
||||
|
||||
/* Return */
|
||||
return $correctUrl;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add CSRF check to query string
|
||||
*
|
||||
* @return \IPS\Http\Url
|
||||
*/
|
||||
public function csrf()
|
||||
{
|
||||
return $this->setQueryString( 'csrfKey', \IPS\Session::i()->csrfKey );
|
||||
}
|
||||
|
||||
/**
|
||||
* Get ACP query string without adsess
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function acpQueryString()
|
||||
{
|
||||
$queryString = $this->queryString;
|
||||
unset( $queryString['adsess'] );
|
||||
unset( $queryString['csrf'] );
|
||||
return static::convertQueryAsArrayToString( $queryString );
|
||||
}
|
||||
|
||||
/* !Deprecated */
|
||||
|
||||
/**
|
||||
* @brief Is internal?
|
||||
* @deprecated Using this property is deprecated. Check if instance of \IPS\Http\Url\Internal
|
||||
*/
|
||||
public $isInternal = TRUE;
|
||||
|
||||
/**
|
||||
* @brief Is friendly?
|
||||
* @deprecated Using this property is deprecated. Check if instance of \IPS\Http\Url\Friendly
|
||||
*/
|
||||
public $isFriendly = FALSE;
|
||||
}
|
||||
@@ -2,9 +2,9 @@
|
||||
/**
|
||||
* @brief User-Agent Management Class
|
||||
* @author <a href='http://www.invisionpower.com'>Invision Power Services, Inc.</a>
|
||||
* @copyright (c) 2001 - SVN_YYYY Invision Power Services, Inc.
|
||||
* @copyright (c) 2001 - 2016 Invision Power Services, Inc.
|
||||
* @license http://www.invisionpower.com/legal/standards/
|
||||
* @package IPS Social Suite
|
||||
* @package IPS Community Suite
|
||||
* @since 20 Aug 2013
|
||||
* @version SVN_VERSION_NUMBER
|
||||
*/
|
||||
@@ -224,6 +224,7 @@ class _Useragent
|
||||
* @note You may put multiple regex definitions for a single key into an array
|
||||
*/
|
||||
protected $standardEngineUseragents = array(
|
||||
'edge' => "edge/([0-9.]{1,10})",
|
||||
'amaya' => "amaya/([0-9.]{1,10})",
|
||||
'aol' => "aol[ /\-]([0-9.]{1,10})",
|
||||
'camino' => "camino/([0-9.+]{1,10})",
|
||||
@@ -249,21 +250,9 @@ class _Useragent
|
||||
/**
|
||||
* @brief List of Facebook IP addresses
|
||||
* @see <a href='https://developers.facebook.com/docs/ApplicationSecurity/#facebook_scraper'>Facebook application security</a>
|
||||
* @note List pulled via suggested whois command on Sep 26 2013
|
||||
* @note List pulled via suggested whois command on Dec 13 2016
|
||||
*/
|
||||
protected $facebookIpRange = array(
|
||||
'204.15.20.0/22', '69.63.176.0/20', '66.220.144.0/20', '66.220.144.0/21', '69.63.184.0/21', '69.63.176.0/21', '74.119.76.0/22', '69.171.255.0/24', '173.252.64.0/18',
|
||||
'69.171.224.0/19', '69.171.224.0/20', '103.4.96.0/22', '69.63.176.0/24', '173.252.64.0/19', '173.252.70.0/24', '31.13.64.0/18', '31.13.24.0/21', '66.220.152.0/21',
|
||||
'66.220.159.0/24', '69.171.239.0/24', '69.171.240.0/20', '31.13.64.0/19', '31.13.64.0/24', '31.13.65.0/24', '31.13.67.0/24', '31.13.68.0/24', '31.13.69.0/24',
|
||||
'31.13.70.0/24', '31.13.71.0/24', '31.13.72.0/24', '31.13.73.0/24', '31.13.74.0/24', '31.13.75.0/24', '31.13.76.0/24', '31.13.77.0/24', '31.13.96.0/19', '31.13.66.0/24',
|
||||
'173.252.96.0/19', '69.63.178.0/24', '31.13.78.0/24', '31.13.79.0/24', '31.13.80.0/24', '31.13.82.0/24', '31.13.83.0/24', '31.13.84.0/24', '31.13.85.0/24', '31.13.86.0/24',
|
||||
'31.13.87.0/24', '31.13.88.0/24', '31.13.89.0/24', '31.13.90.0/24', '31.13.91.0/24', '31.13.92.0/24', '31.13.93.0/24', '31.13.94.0/24', '31.13.95.0/24', '69.171.253.0/24',
|
||||
'69.63.186.0/24', '31.13.81.0/24', '69.63.184.0/21', '2620:0:1c00::/40',
|
||||
'2a03:2880::/32', '2401:DB00::/32', '2a03:2880:fffe::/48', '2a03:2880:ffff::/48', '2620:0:1cff::/48', '2a03:2880:f000::/48', '2a03:2880:f001::/48', '2a03:2880:f002::/48',
|
||||
'2a03:2880:f003::/48', '2a03:2880:f004::/48', '2a03:2880:f005::/48', '2a03:2880:f006::/48', '2a03:2880:f007::/48', '2a03:2880:f008::/48', '2a03:2880:f009::/48', '2a03:2880:f00a::/48',
|
||||
'2a03:2880:f00b::/48', '2a03:2880:f00c::/48', '2a03:2880:f00d::/48', '2a03:2880:f00e::/48', '2a03:2880:f00f::/48', '2a03:2880:f010::/48', '2a03:2880:f011::/48', '2a03:2880:f012::/48',
|
||||
'2a03:2880:f013::/48', '2a03:2880:f014::/48', '2a03:2880:f015::/48', '2a03:2880:f016::/48'
|
||||
);
|
||||
protected $facebookIpRange = array('204.15.20.0/22', '69.63.176.0/20', '66.220.144.0/20', '66.220.144.0/21', '69.63.184.0/21', '69.63.176.0/21', '74.119.76.0/22', '69.171.255.0/24', '173.252.64.0/18', '69.171.224.0/19', '69.171.224.0/20', '103.4.96.0/22', '69.63.176.0/24', '173.252.64.0/19', '173.252.70.0/24', '31.13.64.0/18', '31.13.24.0/21', '66.220.152.0/21', '66.220.159.0/24', '69.171.239.0/24', '69.171.240.0/20', '31.13.64.0/19', '31.13.64.0/24', '31.13.65.0/24', '31.13.67.0/24', '31.13.68.0/24', '31.13.69.0/24', '31.13.70.0/24', '31.13.71.0/24', '31.13.72.0/24', '31.13.73.0/24', '31.13.74.0/24', '31.13.75.0/24', '31.13.76.0/24', '31.13.77.0/24', '31.13.96.0/19', '31.13.66.0/24', '173.252.96.0/19', '69.63.178.0/24', '31.13.78.0/24', '31.13.79.0/24', '31.13.80.0/24', '31.13.82.0/24', '31.13.83.0/24', '31.13.84.0/24', '31.13.85.0/24', '31.13.86.0/24', '31.13.87.0/24', '31.13.88.0/24', '31.13.89.0/24', '31.13.90.0/24', '31.13.91.0/24', '31.13.92.0/24', '31.13.93.0/24', '31.13.94.0/24', '31.13.95.0/24', '69.171.253.0/24', '69.63.186.0/24', '31.13.81.0/24', '179.60.192.0/22', '179.60.192.0/24', '179.60.193.0/24', '179.60.194.0/24', '179.60.195.0/24', '185.60.216.0/22', '45.64.40.0/22', '185.60.216.0/24', '185.60.217.0/24', '185.60.218.0/24', '185.60.219.0/24', '129.134.0.0/16', '157.240.0.0/16', '157.240.8.0/24', '157.240.0.0/24', '157.240.1.0/24', '157.240.2.0/24', '157.240.3.0/24', '157.240.4.0/24', '157.240.5.0/24', '157.240.6.0/24', '157.240.7.0/24', '157.240.9.0/24', '157.240.10.0/24', '204.15.20.0/22', '69.63.176.0/20', '69.63.176.0/21', '69.63.184.0/21', '66.220.144.0/20', ' 69.63.176.0/20', '2620:0:1c00::/40', '2a03:2880::/32', '2a03:2880:fffe::/48', '2a03:2880:ffff::/48', '2620:0:1cff::/48', '2a03:2880:f000::/48', '2a03:2880:f001::/48', '2a03:2880:f002::/48', '2a03:2880:f003::/48', '2a03:2880:f004::/48', '2a03:2880:f005::/48', '2a03:2880:f006::/48', '2a03:2880:f007::/48', '2a03:2880:f008::/48', '2a03:2880:f009::/48', '2a03:2880:f00a::/48', '2a03:2880:f00b::/48', '2a03:2880:f00c::/48', '2a03:2880:f00d::/48', '2a03:2880:f00e::/48', '2a03:2880:f00f::/48', '2a03:2880:f010::/48', '2a03:2880:f011::/48', '2a03:2880:f012::/48', '2a03:2880:f013::/48', '2a03:2880:f014::/48', '2a03:2880:f015::/48', '2a03:2880:f016::/48', '2a03:2880:f017::/48', '2a03:2880:f018::/48', '2a03:2880:f019::/48', '2a03:2880:f01a::/48', '2a03:2880:f01b::/48', '2a03:2880:f01c::/48', '2a03:2880:f01d::/48', '2a03:2880:f01e::/48', '2a03:2880:f01f::/48', '2a03:2880:1000::/36', '2a03:2880:2000::/36', '2a03:2880:3000::/36', '2a03:2880:4000::/36', '2a03:2880:5000::/36', '2a03:2880:6000::/36', '2a03:2880:7000::/36', '2a03:2880:f020::/48', '2a03:2880:f021::/48', '2a03:2880:f022::/48', '2a03:2880:f023::/48', '2a03:2880:f024::/48', '2a03:2880:f025::/48', '2a03:2880:f026::/48', '2a03:2880:f027::/48', '2a03:2880:f028::/48', '2a03:2880:f029::/48', '2a03:2880:f02b::/48', '2a03:2880:f02c::/48', '2a03:2880:f02d::/48', '2a03:2880:f02e::/48', '2a03:2880:f02f::/48', '2a03:2880:f030::/48', '2a03:2880:f031::/48', '2a03:2880:f032::/48', '2a03:2880:f033::/48', '2a03:2880:f034::/48', '2a03:2880:f035::/48', '2a03:2880:f036::/48', '2a03:2880:f037::/48', '2a03:2880:f038::/48', '2a03:2880:f039::/48', '2a03:2880:f03a::/48', '2a03:2880:f03b::/48', '2a03:2880:f03c::/48', '2a03:2880:f03d::/48', '2a03:2880:f03e::/48', '2a03:2880:f03f::/48', '2401:db00::/32', '2a03:2880::/36', '2803:6080::/32', '2a03:2880:f100::/48', '2a03:2880:f200::/48', '2a03:2880:f101::/48', '2a03:2880:f201::/48', '2a03:2880:f102::/48', '2a03:2880:f202::/48', '2a03:2880:f103::/48', '2a03:2880:f203::/48', '2a03:2880:f104::/48', '2a03:2880:f204::/48', '2a03:2880:f107::/48', '2a03:2880:f207::/48', '2a03:2880:f108::/48', '2a03:2880:f208::/48', '2a03:2880:f109::/48', '2a03:2880:f209::/48', '2a03:2880:f10a::/48', '2a03:2880:f20a::/48', '2a03:2880:f10b::/48', '2a03:2880:f20b::/48', '2a03:2880:f10d::/48', '2a03:2880:f20d::/48', '2a03:2880:f10e::/48', '2a03:2880:f20e::/48', '2a03:2880:f10f::/48', '2a03:2880:f20f::/48', '2a03:2880:f110::/48', '2a03:2880:f210::/48', '2a03:2880:f111::/48', '2a03:2880:f211::/48', '2a03:2880:f112::/48', '2a03:2880:f212::/48', '2a03:2880:f114::/48', '2a03:2880:f214::/48', '2a03:2880:f115::/48', '2a03:2880:f215::/48', '2a03:2880:f116::/48', '2a03:2880:f216::/48', '2a03:2880:f117::/48', '2a03:2880:f217::/48', '2a03:2880:f118::/48', '2a03:2880:f218::/48', '2a03:2880:f119::/48', '2a03:2880:f219::/48', '2a03:2880:f11a::/48', '2a03:2880:f21a::/48', '2a03:2880:f11f::/48', '2a03:2880:f21f::/48', '2a03:2880:f121::/48', '2a03:2880:f221::/48', '2a03:2880:f122::/48', '2a03:2880:f222::/48', '2a03:2880:f123::/48', '2a03:2880:f223::/48', '2a03:2880:f10c::/48', '2a03:2880:f20c::/48', '2a03:2880:f126::/48', '2a03:2880:f226::/48', '2a03:2880:f105::/48', '2a03:2880:f205::/48', '2a03:2880:f125::/48', '2a03:2880:f225::/48', '2a03:2880:f106::/48', '2a03:2880:f206::/48', '2a03:2880:f11b::/48', '2a03:2880:f21b::/48', '2a03:2880:f113::/48', '2a03:2880:f213::/48', '2a03:2880:f11c::/48', '2a03:2880:f21Line truncated
|
||||
|
||||
/**
|
||||
* Verify a supplied IP address is within the Facebook range
|
||||
@@ -291,7 +280,7 @@ class _Useragent
|
||||
|
||||
$net = $this->_convertCompressedIpv6ToBits( inet_pton( $net ) );
|
||||
|
||||
if( $_checkIp == $_checkNet )
|
||||
if( $ip == $net )
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user