Version 4.5.0

This commit is contained in:
Neo committed 2025-12-19 05:51:58 -08:00
1 parent 1a0c7fd3c2
commit f79dcf067a
3791 files changed
+248032 -76372

No files matched your search

+183 -36
View File
@@ -11,7 +11,7 @@
namespace IPS;
/* 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;
@@ -87,7 +87,7 @@ class _Request extends \IPS\Patterns\Singleton
{
foreach( $data as $k => $v )
{
if ( is_array( $v ) )
if ( \is_array( $v ) )
{
array_walk_recursive( $v, array( $this, 'clean' ) );
}
@@ -112,18 +112,12 @@ class _Request extends \IPS\Patterns\Singleton
{
/* Remove NULL bytes and the RTL control byte */
$v = str_replace( array( "\0", "\u202E" ), '', $v );
/* Undo magic quote madness */
if ( get_magic_quotes_gpc() === 1 )
{
$v = stripslashes( $v );
}
}
/**
* Get value from array
*
* @param string Key with square brackets (e.g. "foo[bar]")
* @param string $key Key with square brackets (e.g. "foo[bar]")
* @return mixed Value
*/
public function valueFromArray( $key )
@@ -151,6 +145,25 @@ class _Request extends \IPS\Patterns\Singleton
return $array[ $key ];
}
/**
* Get an object that can be cast to a string to get the value for a given input
*
* This can be used in place of passing strings as arguments to functions where it is
* desirable to avoid the value being included in a backtrace if an error occurs.
*
* @return object
*/
public function protect( $k )
{
return eval('return new class
{
public function __toString()
{
return \IPS\Request::i()->' . $k . ' ?? \'\';
}
};' );
}
/**
* Is this an AJAX request?
*
@@ -161,6 +174,16 @@ class _Request extends \IPS\Patterns\Singleton
return ( isset( $_SERVER['HTTP_X_REQUESTED_WITH'] ) and $_SERVER['HTTP_X_REQUESTED_WITH'] == 'XMLHttpRequest' );
}
/**
* Is this request from the mobile app?
*
* @return bool
*/
public function isApp()
{
return isset( $_SERVER['HTTP_X_IPS_APP'] );
}
/**
* Is this an SSL/Secure request?
*
@@ -206,7 +229,6 @@ class _Request extends \IPS\Patterns\Singleton
* Get current URL
*
* @return \IPS\Http\Url
* @see init.php
*/
public function url()
{
@@ -412,6 +434,10 @@ class _Request extends \IPS\Patterns\Singleton
{
$this->setCookie( 'member_id', NULL );
$this->setCookie( 'login_key', NULL );
$this->setCookie( 'loggedIn', NULL );
$this->setCookie( 'guestTime', NULL );
$this->setCookie( 'noCache', NULL );
foreach( $this->cookie as $name => $value )
{
if( mb_strpos( $name, "ipbforumpass_" ) !== FALSE )
@@ -435,7 +461,12 @@ class _Request extends \IPS\Patterns\Singleton
public function setClearAutosaveCookie( $autoSaveKey )
{
$this->clearAutoSaveCookie[] = $autoSaveKey;
\IPS\Request::i()->setCookie( 'clearAutosave', implode( ',', $this->clearAutoSaveCookie ), NULL, FALSE );
/* It is possible for this method to be called from tasks executing during __destruct() after the output has been sent. We want to avoid sending a cookie in that case, as it can cause an error to display. */
if ( ! headers_sent() )
{
\IPS\Request::i()->setCookie( 'clearAutosave', implode( ',', $this->clearAutoSaveCookie ), NULL, FALSE );
}
}
/**
@@ -467,10 +498,6 @@ class _Request extends \IPS\Patterns\Singleton
if( $groupFloodSeconds )
{
$time = ( isset( \IPS\Request::i()->cookie['lastSearch'] ) ) ? \IPS\Request::i()->cookie['lastSearch'] : 0;
if ( isset( $_SESSION['lastSearch'] ) and $_SESSION['lastSearch'] > $time )
{
$time = $_SESSION['lastSearch'];
}
if( $time and ( time() - $time ) < $groupFloodSeconds )
{
@@ -478,9 +505,8 @@ class _Request extends \IPS\Patterns\Singleton
\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' ) ) );
\IPS\Request::i()->setCookie( 'lastSearch', time(), $expire->add( new \DateInterval( 'PT' . \intval( $groupFloodSeconds ) . 'S' ) ) );
}
}
@@ -512,7 +538,7 @@ class _Request extends \IPS\Patterns\Singleton
{
/* 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 ) )
if ( !isset( \IPS\Request::i()->wasConfirmed ) )
{
$form = new \IPS\Helpers\Form( 'form', $submit );
$form->hiddenValues['wasConfirmed'] = 1;
@@ -524,39 +550,160 @@ class _Request extends \IPS\Patterns\Singleton
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 );
\IPS\Output::i()->sendOutput( \IPS\Theme::i()->getTemplate( 'global', 'core', 'front' )->genericBlock( $form, \IPS\Output::i()->title ), 200, 'text/html' );
}
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 );
\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' );
}
}
/* If we are here, we're all good! */
/* If we are here, just check the csrf key */
\IPS\Session::i()->csrfCheck();
return TRUE;
}
/**
* Old IPB escape-on-input routine
*
* @param string $val The unescaped text
* @param string|object $val The unescaped text (can be a string or an object that can be cast to a string)
* @return string The IPB3-style escaped text
*/
public static function legacyEscape( $val )
{
$val = str_replace( "&" , "&amp;" , $val );
$val = str_replace( "<!--" , "&#60;&#33;--" , $val );
$val = str_replace( "-->" , "--&#62;" , $val );
$val = str_ireplace( "<script" , "&#60;script" , $val );
$val = str_replace( ">" , "&gt;" , $val );
$val = str_replace( "<" , "&lt;" , $val );
$val = str_replace( '"' , "&quot;" , $val );
$val = str_replace( "\n" , "<br />" , $val );
$val = str_replace( "$" , "&#036;" , $val );
$val = str_replace( "!" , "&#33;" , $val );
$val = str_replace( "'" , "&#39;" , $val );
$val = str_replace( "\\" , "&#092;" , $val );
return $val;
$val = (string) $val;
$val = str_replace( "&" , "&amp;" , $val );
$val = str_replace( "<!--" , "&#60;&#33;--" , $val );
$val = str_replace( "-->" , "--&#62;" , $val );
$val = str_ireplace( "<script" , "&#60;script" , $val );
$val = str_replace( ">" , "&gt;" , $val );
$val = str_replace( "<" , "&lt;" , $val );
$val = str_replace( '"' , "&quot;" , $val );
$val = str_replace( "\n" , "<br />" , $val );
$val = str_replace( "$" , "&#036;" , $val );
$val = str_replace( "!" , "&#33;" , $val );
$val = str_replace( "'" , "&#39;" , $val );
$val = str_replace( "\\" , "&#092;" , $val );
return $val;
}
/**
* Get our referrer, looking for a specific request variable, then falling back to the header
*
* @param bool $allowExternal If set to TRUE, external URL's will be allowed and returned.
* @param bool $onlyRequest If set to TRUE, will only look for the "ref" request parameter. Useful if you need to look for HTTP_REFERER at a specific point in time.
* @param string|NULL $base If set, will only return URL's with this base.
* @return \IPS\Http\Url|NULL
*/
public function referrer( bool $allowExternal=FALSE, bool $onlyRequest=FALSE, ?string $base = NULL ): ?\IPS\Http\Url
{
/* Do we have a _ref request parameter? */
$ref = NULL;
if ( isset( $this->ref ) )
{
$ref = @base64_decode( $this->ref );
}
/* Maybe not - check HTTP_REFERER */
if ( !$ref AND !$onlyRequest AND !empty( $_SERVER['HTTP_REFERER'] ) )
{
$ref = $_SERVER['HTTP_REFERER'];
}
/* Did that work? */
if ( $ref )
{
try
{
$ref = \IPS\Http\Url::createFromString( $ref );
}
catch( \IPS\Http\Url\Exception $e )
{
/* Failed to create? Nope. */
return NULL;
}
/* Return if URL is internal and not an open redirect, or if we're allowing external referrer references */
if ( ( ( $ref instanceof \IPS\Http\Url\Internal ) AND !$ref->openRedirect() ) OR $allowExternal )
{
if ( $base !== NULL AND ( $ref instanceof \IPS\Http\Url\Internal ) )
{
if ( $ref->base === $base )
{
return $ref;
}
else
{
return NULL;
}
}
else
{
return $ref;
}
}
else
{
return NULL;
}
}
/* Still here? Nothing worked */
return NULL;
}
/**
* Get any Authorization header (or otherwise passed API key or OAuth access token) in the request
* Note: doesn't do any kind of decoding, value will be something like "Basic xxxxx" or "Bearer xxxxx"
*
* @return string|null
*/
public function authorizationHeader()
{
/* Check if an API Key or Access Token has been passed as a parameter in the query string. Because of the
obvious security issues with this, we do not recommend it, but sometimes it is the only choice */
if ( isset( $this->key ) )
{
return 'Basic ' . base64_encode( $this->key . ':' );
}
if ( isset( $this->access_token ) and ( !\IPS\OAUTH_REQUIRES_HTTPS or $this->isSecure() ) )
{
return 'Bearer ' . $this->access_token;
}
/* Look for an API key in an automatically decoded HTTP Basic header */
if ( isset( $_SERVER['PHP_AUTH_USER'] ) )
{
return 'Basic ' . base64_encode( $_SERVER['PHP_AUTH_USER'] . ':' );
}
/* If we're still here, try to find an Authorization header - start with $_SERVER... */
$authorizationHeader = NULL;
foreach ( $_SERVER as $k => $v )
{
if ( mb_substr( $k, -18 ) == 'HTTP_AUTHORIZATION' or mb_substr( $k, -20 ) == 'HTTP_X_AUTHORIZATION' )
{
return $v;
}
}
/* ...if we didn't find anything there, try apache_request_headers() */
if ( \function_exists('apache_request_headers') )
{
$headers = @apache_request_headers();
if ( isset( $headers['Authorization'] ) )
{
return $headers['Authorization'];
}
elseif ( isset( $headers['X-Authorization'] ) )
{
return $headers['X-Authorization'];
}
}
/* Still here? We got nothing */
return NULL;
}
}