Version 4.1.19

This commit is contained in:
Neo committed 2025-12-19 05:38:56 -08:00
1 parent 28bd025b35
commit 2bd5025018
2674 files changed
+233518 -77766

No files matched your search

+159 -29
View File
@@ -2,9 +2,9 @@
/**
* @brief HTTP Request 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 Feb 2013
* @version SVN_VERSION_NUMBER
*/
@@ -41,8 +41,16 @@ class _Request extends \IPS\Patterns\Singleton
*/
public function __construct()
{
$this->parseIncomingRecursively( $_GET );
$this->parseIncomingRecursively( $_POST );
if ( isset( $_SERVER['REQUEST_METHOD'] ) AND $_SERVER['REQUEST_METHOD'] == 'PUT' )
{
parse_str( file_get_contents('php://input'), $params );
$this->parseIncomingRecursively( $params );
}
else
{
$this->parseIncomingRecursively( $_GET );
$this->parseIncomingRecursively( $_POST );
}
array_walk_recursive( $_COOKIE, array( $this, 'clean' ) );
@@ -164,7 +172,7 @@ class _Request extends \IPS\Patterns\Singleton
*/
public function isSecure()
{
if( !empty( $_SERVER['HTTPS'] ) AND mb_strtolower( $_SERVER['HTTPS'] ) == 'on' )
if( !empty( $_SERVER['HTTPS'] ) AND ( mb_strtolower( $_SERVER['HTTPS'] ) == 'on' or $_SERVER['HTTPS'] === '1' ) )
{
return TRUE;
}
@@ -172,6 +180,10 @@ class _Request extends \IPS\Patterns\Singleton
{
return TRUE;
}
else if( !empty( $_SERVER['HTTP_CLOUDFRONT_FORWARDED_PROTO'] ) AND mb_strtolower( $_SERVER['HTTP_CLOUDFRONT_FORWARDED_PROTO'] ) == 'https' )
{
return TRUE;
}
else if ( !empty( $_SERVER['HTTP_X_FORWARDED_HTTPS'] ) AND mb_strtolower( $_SERVER['HTTP_X_FORWARDED_HTTPS'] ) == 'https' )
{
return TRUE;
@@ -202,13 +214,34 @@ class _Request extends \IPS\Patterns\Singleton
public function url()
{
if( $this->_url === NULL )
{
/* Work out the query string. We need to urldecode the friendly url slug because browsers will send the value percent-encoded, which we don't want as it turns, for example, à into a percent-encoded character
but we don't want to urlencode the query string, because that will already be urlencoded */
$path = urldecode( ( $_SERVER['QUERY_STRING'] AND mb_strpos( $_SERVER['REQUEST_URI'], $_SERVER['QUERY_STRING'] ) !== FALSE ) ? mb_substr( $_SERVER['REQUEST_URI'], 0, -mb_strlen( $_SERVER['QUERY_STRING'] ) ) : $_SERVER['REQUEST_URI'] ) . $_SERVER['QUERY_STRING'];
{
$url = $this->isSecure() ? 'https' : 'http';
$url .= '://';
/* Return */
$this->_url = new \IPS\Http\Url( ( ( $this->isSecure() ) ? 'https://' : 'http://' ) . ( !empty( $_SERVER['HTTP_HOST'] ) ? $_SERVER['HTTP_HOST'] : $_SERVER['SERVER_NAME'] ) . $path, TRUE );
if ( !empty( $_SERVER['HTTP_X_FORWARDED_HOST'] ) )
{
$url .= $_SERVER['HTTP_X_FORWARDED_HOST'];
}
elseif ( !empty( $_SERVER['HTTP_HOST'] ) )
{
$url .= $_SERVER['HTTP_HOST'];
}
else
{
$url .= $_SERVER['SERVER_NAME'];
}
if ( $_SERVER['QUERY_STRING'] AND mb_strpos( $_SERVER['REQUEST_URI'], $_SERVER['QUERY_STRING'] ) !== FALSE )
{
$url .= mb_substr( $_SERVER['REQUEST_URI'], 0, -mb_strlen( $_SERVER['QUERY_STRING'] ) );
}
else
{
$url .= $_SERVER['REQUEST_URI'];
}
$url .= $_SERVER['QUERY_STRING'];
return $this->_url = \IPS\Http\Url::createFromString( $url, TRUE, TRUE );
}
return $this->_url;
@@ -238,6 +271,11 @@ class _Request extends \IPS\Patterns\Singleton
{
$addrs[] = $_SERVER['HTTP_CLIENT_IP'];
}
if ( isset( $_SERVER['HTTP_X_CLIENT_IP'] ) )
{
$addrs[] = $_SERVER['HTTP_X_CLIENT_IP'];
}
if( isset( $_SERVER['HTTP_X_CLUSTER_CLIENT_IP'] ) )
{
@@ -292,6 +330,24 @@ class _Request extends \IPS\Patterns\Singleton
return FALSE;
}
/**
* Returns the cookie path
*
* @return string|void
*/
public static function getCookiePath()
{
if( \IPS\COOKIE_PATH !== NULL )
{
return \IPS\COOKIE_PATH;
}
$path = mb_substr( \IPS\Settings::i()->base_url, mb_strpos( \IPS\Settings::i()->base_url, ( !empty( $_SERVER['SERVER_NAME'] ) ) ? $_SERVER['SERVER_NAME'] : $_SERVER['HTTP_HOST'] ) + mb_strlen( ( !empty( $_SERVER['SERVER_NAME'] ) ) ? $_SERVER['SERVER_NAME'] : $_SERVER['HTTP_HOST'] ) );
$path = mb_substr( $path, mb_strpos( $path, '/' ) );
return $path;
}
/**
* Set a cookie
@@ -308,22 +364,11 @@ class _Request extends \IPS\Patterns\Singleton
{
/* Work out the path and if cookies should be SSL only */
$sslOnly = FALSE;
if( \IPS\COOKIE_PATH !== NULL AND $path === NULL )
{
$path = \IPS\COOKIE_PATH;
}
if ( $path === NULL )
{
$path = mb_substr( \IPS\Settings::i()->base_url, mb_strpos( \IPS\Settings::i()->base_url, ( !empty( $_SERVER['SERVER_NAME'] ) ) ? $_SERVER['SERVER_NAME'] : $_SERVER['HTTP_HOST'] ) + mb_strlen( ( !empty( $_SERVER['SERVER_NAME'] ) ) ? $_SERVER['SERVER_NAME'] : $_SERVER['HTTP_HOST'] ) );
$path = mb_substr( $path, mb_strpos( $path, '/' ) );
}
if( mb_substr( \IPS\Settings::i()->base_url, 0, 5 ) == 'https' AND !\IPS\COOKIE_BYPASS_SSLONLY )
if( mb_substr( \IPS\Settings::i()->base_url, 0, 5 ) == 'https' AND \IPS\COOKIE_BYPASS_SSLONLY !== TRUE )
{
$sslOnly = TRUE;
}
$path = $path ?: static::getCookiePath();
/* Are we forcing a cookie domain? */
if( \IPS\COOKIE_DOMAIN !== NULL AND $domain === NULL )
@@ -349,6 +394,23 @@ class _Request extends \IPS\Patterns\Singleton
return FALSE;
}
/**
* @brief Editor autosave keys to be cleared
*/
protected $clearAutoSaveCookie = array();
/**
* Set cookie to clear autosave content from editor
*
* @param $autoSaveKey string The editor's autosave key
* @return void
*/
public function setClearAutosaveCookie( $autoSaveKey )
{
$this->clearAutoSaveCookie[] = $autoSaveKey;
\IPS\Request::i()->setCookie( 'clearAutosave', implode( ',', $this->clearAutoSaveCookie ), NULL, FALSE );
}
/**
* Returns the request method
*
@@ -366,18 +428,86 @@ class _Request extends \IPS\Patterns\Singleton
*/
public static function floodCheck()
{
/* Flood control */
if( \IPS\Member::loggedIn()->group['g_search_flood'] )
$groupFloodSeconds = \IPS\Member::loggedIn()->group['g_search_flood'];
if ( \IPS\Session::i()->userAgent->spider )
{
if( isset( $_SESSION['lastSearch'] ) and ( time() - $_SESSION['lastSearch'] ) < \IPS\Member::loggedIn()->group['g_search_flood'] )
/* Force a 30 second flood control so if guests have it switched off, or set very low, you do not get flooded by known bots */
$groupFloodSeconds = \IPS\BOT_SEARCH_FLOOD_SECONDS;
}
/* Flood control */
if( $groupFloodSeconds )
{
$time = ( isset( \IPS\Request::i()->cookie['lastSearch'] ) ) ? \IPS\Request::i()->cookie['lastSearch'] : 0;
if ( isset( $_SESSION['lastSearch'] ) and $_SESSION['lastSearch'] > $time )
{
$secondsToWait = \IPS\Member::loggedIn()->group['g_search_flood'] - ( time() - $_SESSION['lastSearch'] );
\IPS\Output::i()->error( \IPS\Member::loggedIn()->language()->addToStack( 'search_flood_error', FALSE, array( 'sprintf' => array( $secondsToWait ) ) ), '1C205/3', 429, \IPS\Member::loggedIn()->language()->addToStack( 'search_flood_error_admin', FALSE, array( 'sprintf' => array( $secondsToWait ) ) ), array( 'Retry-After' => \IPS\DateTime::create()->add( new \DateInterval( 'PT' . $secondsToWait . 'S' ) )->format('r') ) );
$time = $_SESSION['lastSearch'];
}
if( $time and ( time() - $time ) < $groupFloodSeconds )
{
$secondsToWait = $groupFloodSeconds - ( time() - $time );
\IPS\Output::i()->error( \IPS\Member::loggedIn()->language()->addToStack( 'search_flood_error', FALSE, array( 'pluralize' => array( $secondsToWait ) ) ), '1C205/3', 429, \IPS\Member::loggedIn()->language()->addToStack( 'search_flood_error_admin', FALSE, array( 'pluralize' => array( $secondsToWait ) ) ), array( 'Retry-After' => \IPS\DateTime::create()->add( new \DateInterval( 'PT' . $secondsToWait . 'S' ) )->format('r') ) );
}
$_SESSION['lastSearch'] = time();
$expire = new \IPS\DateTime;
\IPS\Request::i()->setCookie( 'lastSearch', time(), $expire->add( new \DateInterval( 'PT' . intval( $groupFloodSeconds ) . 'S' ) ) );
}
}
/**
* Is PHP running as CGI?
*
* @note Possible values: cgi, cgi-fcgi, fpm-fcgi
* @return boolean
*/
public function isCgi()
{
if ( \substr( PHP_SAPI, 0, 3 ) == 'cgi' OR \substr( PHP_SAPI, -3 ) == 'cgi' )
{
return true;
}
return false;
}
/**
* Confirmation check
*
* @param string $title Lang string key for title
* @param string $message Lang string key for confirm message
* @param string $submit Lang string key for submit button
* @return void
*/
public function confirmedDelete( $title = 'delete_confirm', $message = 'delete_confirm_detail', $submit = 'delete' )
{
/* The confirmation dialogs will send form_submitted=1, as will displaying a form, so we check for this.
If the admin (or user) simply visited a delete URL directly, this would not be included in the request. */
if ( ! isset( \IPS\Request::i()->wasConfirmed ) )
{
$form = new \IPS\Helpers\Form( 'form', $submit );
$form->hiddenValues['wasConfirmed'] = 1;
$form->addMessage( $message, 'ipsMessage ipsMessage_warning' );
/* We call sendOutput() to show the form now */
\IPS\Output::i()->output = $form;
\IPS\Output::i()->title = \IPS\Member::loggedIn()->language()->addToStack( $title );
if ( \IPS\Request::i()->isAjax() )
{
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core', 'front' )->genericBlock( $form, \IPS\Output::i()->title ), 200, 'text/html', \IPS\Output::i()->httpHeaders );
}
else
{
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core' )->globalTemplate( \IPS\Output::i()->title, \IPS\Output::i()->output, array( 'app' => \IPS\Dispatcher::i()->application->directory, 'module' => \IPS\Dispatcher::i()->module->key, 'controller' => \IPS\Dispatcher::i()->controller ) ), 200, 'text/html', \IPS\Output::i()->httpHeaders );
}
}
/* If we are here, we're all good! */
return TRUE;
}
/**
* Old IPB escape-on-input routine