Version 4.1.19
This commit is contained in:
1 parent
28bd025b35
commit
2bd5025018
2674 files changed
+233518
-77766
No files matched your search
@@ -1,10 +1,10 @@
|
||||
<?php
|
||||
/**
|
||||
* @brief Initiates IPS Social Suite constants, autoloader and exception handler
|
||||
* @brief Initiates IPS Community Suite constants, autoloader and exception handler
|
||||
* @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
|
||||
*/
|
||||
@@ -12,7 +12,7 @@
|
||||
namespace IPS;
|
||||
|
||||
/**
|
||||
* Class to contain IPS Social Suite autoloader and exception handler
|
||||
* Class to contain IPS Community Suite autoloader and exception handler
|
||||
*/
|
||||
class IPS
|
||||
{
|
||||
@@ -49,6 +49,8 @@ class IPS
|
||||
public static function defaultConstants()
|
||||
{
|
||||
return array(
|
||||
'IPB_USE_SEO_TRANSLIT' => TRUE,
|
||||
'CIC' => isset( $_SERVER['IPS_CIC'] ),
|
||||
'CP_DIRECTORY' => 'admin',
|
||||
'IN_DEV' => FALSE,
|
||||
'DEV_USE_WHOOPS' => TRUE,
|
||||
@@ -57,24 +59,25 @@ class IPS
|
||||
'DEBUG_JS' => FALSE,
|
||||
'DEV_DEBUG_JS' => TRUE,
|
||||
'DEV_DEBUG_CSS' => FALSE,
|
||||
'DEV_DEBUG_TEMPLATES' => FALSE,
|
||||
'DEBUG_TEMPLATES' => FALSE,
|
||||
'IPS_FOLDER_PERMISSION' => 0777,
|
||||
'FOLDER_PERMISSION_NO_WRITE' => 0755,
|
||||
'IPS_FILE_PERMISSION' => 0666,
|
||||
'FILE_PERMISSION_NO_WRITE' => 0644,
|
||||
'ROOT_PATH' => __DIR__,
|
||||
'NO_WRITES' => FALSE,
|
||||
'LOG_METHOD' => '{}',
|
||||
'STORE_METHOD' => 'FileSystem',
|
||||
'NO_WRITES' => ( isset( $_SERVER['IPS_CIC'] ) and !isset( $_SERVER['IPS_CIC_ACP'] ) ),
|
||||
'DEBUG_LOG' => FALSE,
|
||||
'LOG_FALLBACK_DIR' => '{root}/uploads/logs',
|
||||
'STORE_METHOD' => ( isset( $_SERVER['IPS_CIC'] ) ? 'Database' : 'FileSystem' ),
|
||||
'STORE_CONFIG' => '{"path":"{root}/datastore"}',
|
||||
'CACHE_METHOD' => 'None',
|
||||
'CACHE_CONFIG' => '{}',
|
||||
'CACHE_PAGE_TIMEOUT' => 30,
|
||||
'CACHE_PAGE_TIMEOUT' => ( isset( $_SERVER['IPS_CIC'] ) ? 300 : 30 ),
|
||||
'TEST_CACHING' => FALSE,
|
||||
'EMAIL_DEBUG_PATH' => NULL,
|
||||
'BULK_MAILS_PER_CYCLE' => 0,
|
||||
'BULK_MAILS_PER_CYCLE' => 50,
|
||||
'JAVA_PATH' => "",
|
||||
'ERROR_PAGE' => 'error.html',
|
||||
'ERROR_PAGE' => 'error.php',
|
||||
'UPGRADING_PAGE' => 'upgrading.html',
|
||||
'QUERY_LOG' => FALSE,
|
||||
'CACHING_LOG' => FALSE,
|
||||
@@ -94,17 +97,29 @@ class IPS
|
||||
'NEXUS_LKEY_API_ALLOW_IP_OVERRIDE' => FALSE,
|
||||
'UPGRADE_MANUAL_THRESHOLD' => 250000,
|
||||
'HTMLENTITIES' => ( version_compare( phpversion(), '5.4.0', '<' ) ) ? ENT_IGNORE : ENT_DISALLOWED,
|
||||
'SUITE_UNIQUE_KEY' => mb_substr( md5( '12bde_mafia' . '$Rev: 3023$'), 10, 10 ),
|
||||
'SUITE_UNIQUE_KEY' => mb_substr( md5( '13_mafia' . '$Rev: 3023$'), 10, 10 ),
|
||||
'CACHEBUST_KEY' => mb_substr( md5( '13_mafia' . '$Rev: 3023$'), 10, 10 ), // This looks unnecessary but SUITE_UNIQUE_KEY can be set to a constant constant in constants.php whereas we need a version specific constant for cache busting.
|
||||
'TEXT_ENCRYPTION_KEY' => NULL,
|
||||
'CONNECT_MASTER_KEY' => NULL,
|
||||
'USE_DEVELOPMENT_BUILDS' => FALSE,
|
||||
'DEV_WHOOPS_EDITOR' => NULL,
|
||||
'DEFAULT_REQUEST_TIMEOUT' => 5, // In seconds
|
||||
'DEFAULT_REQUEST_TIMEOUT' => 10, // In seconds - default for most external connections
|
||||
'LONG_REQUEST_TIMEOUT' => 30, // In seconds - used for specific API-based calls where we expect a slightly longer response time
|
||||
'TEMP_DIRECTORY' => sys_get_temp_dir(),
|
||||
'MONTH_DAY_FORMAT' => '%B %d',
|
||||
'TEST_DELTA_ZIP' => '',
|
||||
'DELTA_FORCE_FTP' => FALSE,
|
||||
'BYPASS_ACP_IP_CHECK' => FALSE,
|
||||
'RECOVERY_MODE' => FALSE,
|
||||
'DEV_FORCE_MFA' => FALSE,
|
||||
'DISABLE_MFA' => FALSE,
|
||||
'BOT_SEARCH_FLOOD_SECONDS' => 30,
|
||||
'READ_WRITE_SEPARATION' => TRUE,
|
||||
'USE_MYSQL_SEARCH_BASIC_MODE_THRESHOLD' => 1000000
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initiate IPS Social Suite constants, autoloader and exception handler
|
||||
* Initiate IPS Community Suite constants, autoloader and exception handler
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
@@ -160,15 +175,12 @@ class IPS
|
||||
{
|
||||
self::$PSR0Namespaces['Whoops'] = ROOT_PATH . '/dev/Whoops';
|
||||
$whoops = new \Whoops\Run;
|
||||
$ipsHandler = new \Whoops\Handler\IPSHandler;
|
||||
$handler = new \Whoops\Handler\PrettyPageHandler;
|
||||
if ( \IPS\DEV_WHOOPS_EDITOR )
|
||||
{
|
||||
$ipsHandler->setEditor( \IPS\DEV_WHOOPS_EDITOR );
|
||||
$handler->setEditor( \IPS\DEV_WHOOPS_EDITOR );
|
||||
}
|
||||
$whoops->pushHandler( $ipsHandler );
|
||||
$jsonHandler = new \Whoops\Handler\JsonResponseHandler;
|
||||
$jsonHandler->onlyForAjaxRequests( true );
|
||||
$whoops->pushHandler( $jsonHandler );
|
||||
$whoops->pushHandler( $handler );
|
||||
$whoops->register();
|
||||
}
|
||||
else
|
||||
@@ -275,6 +287,12 @@ class IPS
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Is it a trait? The function_exists call is just so PHP 5.3 doesn't throw a fatal error (even though it's required, we want it to fail gracefully) */
|
||||
if ( function_exists('trait_exists') and trait_exists( "{$namespace}\\{$class}", FALSE ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Doesn't exist? */
|
||||
if( !class_exists( "{$namespace}\\_{$class}", FALSE ) )
|
||||
@@ -285,116 +303,122 @@ class IPS
|
||||
/* Stuff for developer mode */
|
||||
if( IN_DEV )
|
||||
{
|
||||
try
|
||||
$reflection = new \ReflectionClass( "{$namespace}\\_{$class}" );
|
||||
|
||||
/* Import our code to override forbidden functions */
|
||||
if( !in_array( \strtolower( $namespace ), self::$inDevCodeImportedTo ) )
|
||||
{
|
||||
$reflection = new \ReflectionClass( "{$namespace}\\_{$class}" );
|
||||
|
||||
/* Import our code to override forbidden functions */
|
||||
if( !in_array( \strtolower( $namespace ), self::$inDevCodeImportedTo ) )
|
||||
$inDevCode = self::$inDevCode;
|
||||
self::$inDevCodeImportedTo[] = \strtolower( $namespace );
|
||||
}
|
||||
|
||||
/* Any classes which extend a core PHP class are exempt from our rules */
|
||||
$extendsCorePhpClass = FALSE;
|
||||
for ( $workingClass = $reflection; $parent = $workingClass->getParentClass(); $workingClass = $parent )
|
||||
{
|
||||
if ( \substr( $parent->getNamespaceName(), 0, 3 ) !== 'IPS' )
|
||||
{
|
||||
$inDevCode = self::$inDevCode;
|
||||
self::$inDevCodeImportedTo[] = \strtolower( $namespace );
|
||||
$extendsCorePhpClass = TRUE;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Any classes which extend a core PHP class are exempt from our rules */
|
||||
$parent = $reflection->getParentClass();
|
||||
if( $parent === false or \substr( $parent->getNamespaceName(), 0, 3 ) === 'IPS' )
|
||||
}
|
||||
if ( !$extendsCorePhpClass )
|
||||
{
|
||||
/* Make sure it's name follows our standards */
|
||||
if( !preg_match( '/^_[A-Z0-9]+$/i', $reflection->getShortName() ) )
|
||||
{
|
||||
/* Make sure it's name follows our standards */
|
||||
if( !preg_match( '/^_[A-Z]+$/i', $reflection->getShortName() ) )
|
||||
trigger_error( "{$classname} does not follow our naming conventions. Please rename using only alphabetic characters and PascalCase. (PHP Coding Standards: Classes.5)", E_USER_ERROR );
|
||||
}
|
||||
|
||||
/* Loop methods */
|
||||
$hasNonAbstract = FALSE;
|
||||
$hasNonStatic = FALSE;
|
||||
foreach ( $reflection->getMethods() as $method )
|
||||
{
|
||||
if ( \substr( $method->getDeclaringClass()->getName(), 0, 3 ) === 'IPS' )
|
||||
{
|
||||
trigger_error( "{$classname} does not follow our naming conventions. Please rename using only alphabetic characters and PascalCase. (PHP Coding Standards: Classes.5)", E_USER_ERROR );
|
||||
}
|
||||
/* Make sure it's not private */
|
||||
if( $method->isPrivate() )
|
||||
{
|
||||
trigger_error( "{$classname}::{$method->name} is declared as private. In order to ensure that hooks are able to work freely, please use protected instead. (PHP Coding Standards: Functions and Methods.4)", E_USER_ERROR );
|
||||
}
|
||||
|
||||
/* Loop methods */
|
||||
$hasNonAbstract = FALSE;
|
||||
$hasNonStatic = FALSE;
|
||||
foreach ( $reflection->getMethods() as $method )
|
||||
{
|
||||
if ( \substr( $method->getDeclaringClass()->getName(), 0, 3 ) === 'IPS' )
|
||||
/* We need to know for later if we have non-abstract methods */
|
||||
if( !$method->isAbstract() )
|
||||
{
|
||||
/* Make sure it's not private */
|
||||
if( $method->isPrivate() )
|
||||
{
|
||||
trigger_error( "{$classname}::{$method->name} is declared as private. In order to ensure that hooks are able to work freely, please use protected instead. (PHP Coding Standards: Functions and Methods.4)", E_USER_ERROR );
|
||||
}
|
||||
|
||||
/* We need to know for later if we have non-abstract methods */
|
||||
if( !$method->isAbstract() )
|
||||
{
|
||||
$hasNonAbstract = TRUE;
|
||||
}
|
||||
|
||||
/* We need to know for later if we have non-static methods */
|
||||
if( !$method->isStatic() )
|
||||
{
|
||||
$hasNonStatic = TRUE;
|
||||
}
|
||||
|
||||
/* Make sure the name follows our conventions */
|
||||
if(
|
||||
!preg_match( '/^_?[a-z][A-Za-z0-9]*$/', $method->name ) // Normal pattern most methods should match
|
||||
and
|
||||
!preg_match( '/^get_/i', $method->name ) // get_* is allowed
|
||||
and
|
||||
!preg_match( '/^set_/i', $method->name ) // set_* is allowed
|
||||
and
|
||||
!preg_match( '/^parse_/i', $method->name ) // parse_* is allowed
|
||||
and
|
||||
!preg_match( '/^setBitwise_/i', $method->name ) // set_Bitiwse_* is allowed
|
||||
and
|
||||
!in_array( $method->name, array( // PHP's magic methods are allowed (except __sleep and __wakeup as we don't allow serializing)
|
||||
'__construct',
|
||||
'__destruct',
|
||||
'__call',
|
||||
'__callStatic',
|
||||
'__get',
|
||||
'__set',
|
||||
'__isset',
|
||||
'__unset',
|
||||
'__toString',
|
||||
'__invoke',
|
||||
'__set_state',
|
||||
'__clone',
|
||||
) )
|
||||
) {
|
||||
trigger_error( "{$classname}::{$method->name} does not follow our naming conventions. Please rename using only alphabetic characters and camelCase. (PHP Coding Standards: Functions and Methods.1-3)", E_USER_ERROR );
|
||||
}
|
||||
$hasNonAbstract = TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
/* Loop properties */
|
||||
foreach ( $reflection->getProperties() as $property )
|
||||
{
|
||||
$hasNonAbstract = TRUE;
|
||||
|
||||
/* Make sure it's not private */
|
||||
if( $property->isPrivate() )
|
||||
/* We need to know for later if we have non-static methods */
|
||||
if( !$method->isStatic() )
|
||||
{
|
||||
trigger_error( "{$classname}::\${$property->name} is declared as private. In order to ensure that hooks are able to work freely, please use protected instead. (PHP Coding Standards: Properties and Variables.3)", E_USER_ERROR );
|
||||
$hasNonStatic = TRUE;
|
||||
}
|
||||
|
||||
|
||||
/* Make sure the name follows our conventions */
|
||||
if( !preg_match( '/^_?[a-z][A-Za-z]*$/', $property->name ) )
|
||||
{
|
||||
trigger_error( "{$classname}::\${$property->name} does not follow our naming conventions. Please rename using only alphabetic characters and camelCase. (PHP Coding Standards: Properties and Variables.1-2)", E_USER_ERROR );
|
||||
if(
|
||||
!preg_match( '/^_?[a-z][A-Za-z0-9]*$/', $method->name ) // Normal pattern most methods should match
|
||||
and
|
||||
!preg_match( '/^get_/i', $method->name ) // get_* is allowed
|
||||
and
|
||||
!preg_match( '/^set_/i', $method->name ) // set_* is allowed
|
||||
and
|
||||
!preg_match( '/^parse_/i', $method->name ) // parse_* is allowed
|
||||
and
|
||||
!preg_match( '/^setBitwise_/i', $method->name ) // set_Bitiwse_* is allowed
|
||||
and
|
||||
!preg_match( '/^(GET|POST|PUT|DELETE)[a-zA-Z_]+$/', $method->name ) // API methods have a specific naming format
|
||||
and
|
||||
!in_array( $method->name, array( // PHP's magic methods are allowed (except __sleep and __wakeup as we don't allow serializing)
|
||||
'__construct',
|
||||
'__destruct',
|
||||
'__call',
|
||||
'__callStatic',
|
||||
'__get',
|
||||
'__set',
|
||||
'__isset',
|
||||
'__unset',
|
||||
'__toString',
|
||||
'__invoke',
|
||||
'__set_state',
|
||||
'__clone',
|
||||
) )
|
||||
) {
|
||||
trigger_error( "{$classname}::{$method->name} does not follow our naming conventions. Please rename using only alphabetic characters and camelCase. (PHP Coding Standards: Functions and Methods.1-3)", E_USER_ERROR );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Loop properties */
|
||||
foreach ( $reflection->getProperties() as $property )
|
||||
{
|
||||
$hasNonAbstract = TRUE;
|
||||
|
||||
/* Check an interface wouldn't be more appropriate */
|
||||
if( !$hasNonAbstract )
|
||||
/* Make sure it's not private */
|
||||
if( $property->isPrivate() )
|
||||
{
|
||||
trigger_error( "You do not have any non-abstract methods in {$classname}. Please use an interface instead. (PHP Coding Standards: Classes.7)", E_USER_ERROR );
|
||||
trigger_error( "{$classname}::\${$property->name} is declared as private. In order to ensure that hooks are able to work freely, please use protected instead. (PHP Coding Standards: Properties and Variables.3)", E_USER_ERROR );
|
||||
}
|
||||
|
||||
/* Check we have at least one non-static method (unless this class is abstract or has a parent) */
|
||||
elseif( !$reflection->isAbstract() and $reflection->getParentClass() === FALSE and !$hasNonStatic and $reflection->getNamespacename() !== 'IPS\Output\Plugin' and !in_array( 'extensions', $bits ) and !in_array( 'templateplugins', $bits ) )
|
||||
|
||||
/* Make sure the name follows our conventions */
|
||||
if( !preg_match( '/^_?[a-z][A-Za-z]*$/', $property->name ) )
|
||||
{
|
||||
trigger_error( "You do not have any methods in {$classname} which are not static. Please refactor. (PHP Coding Standards: Functions and Methods.6)", E_USER_ERROR );
|
||||
trigger_error( "{$classname}::\${$property->name} does not follow our naming conventions. Please rename using only alphabetic characters and camelCase. (PHP Coding Standards: Properties and Variables.1-2)", E_USER_ERROR );
|
||||
}
|
||||
}
|
||||
|
||||
/* Check an interface wouldn't be more appropriate */
|
||||
if( !$hasNonAbstract )
|
||||
{
|
||||
trigger_error( "You do not have any non-abstract methods in {$classname}. Please use an interface instead. (PHP Coding Standards: Classes.7)", E_USER_ERROR );
|
||||
}
|
||||
|
||||
/* Check we have at least one non-static method (unless this class is abstract or has a parent) */
|
||||
elseif( !$reflection->isAbstract() and $reflection->getParentClass() === FALSE and !$hasNonStatic and $reflection->getNamespacename() !== 'IPS\Output\Plugin' and !in_array( 'extensions', $bits ) and !in_array( 'templateplugins', $bits ) )
|
||||
{
|
||||
trigger_error( "You do not have any methods in {$classname} which are not static. Please refactor. (PHP Coding Standards: Functions and Methods.6)", E_USER_ERROR );
|
||||
}
|
||||
}
|
||||
catch ( \Exception $e ) {}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -413,17 +437,21 @@ class IPS
|
||||
public static function monkeyPatch( $namespace, $finalClass, $extraCode = '' )
|
||||
{
|
||||
$realClass = "_{$finalClass}";
|
||||
if( isset( self::$hooks[ "\\{$namespace}\\{$finalClass}" ] ) )
|
||||
if( isset( self::$hooks[ "\\{$namespace}\\{$finalClass}" ] ) AND \IPS\RECOVERY_MODE === FALSE )
|
||||
{
|
||||
foreach ( self::$hooks[ "\\{$namespace}\\{$finalClass}" ] as $id => $data )
|
||||
{
|
||||
if ( file_exists( ROOT_PATH . '/' . $data['file'] ) )
|
||||
{
|
||||
$contents = "namespace {$namespace}; ". str_replace( '_HOOK_CLASS_', $realClass, file_get_contents( ROOT_PATH . '/' . $data['file'] ) );
|
||||
if( @eval( $contents ) !== FALSE )
|
||||
try
|
||||
{
|
||||
$realClass = $data['class'];
|
||||
if( @eval( $contents ) !== FALSE )
|
||||
{
|
||||
$realClass = $data['class'];
|
||||
}
|
||||
}
|
||||
catch ( \ParseError $e ) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -434,12 +462,12 @@ class IPS
|
||||
trigger_error( "There was an error initiating the class {$namespace}\\{$finalClass}.", E_USER_ERROR );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Error Handler
|
||||
*
|
||||
* @param int $errno Error number
|
||||
* @param errstr $errstr Error message
|
||||
* @param string $errstr Error message
|
||||
* @param string $errfile File
|
||||
* @param int $errline Line
|
||||
* @param array $trace Backtrace
|
||||
@@ -448,7 +476,7 @@ class IPS
|
||||
public static function errorHandler( $errno, $errstr, $errfile, $errline, $trace=NULL )
|
||||
{
|
||||
/* We don't care about these in production */
|
||||
if ( in_array( $errno, array( E_NOTICE, E_DEPRECATED ) ) )
|
||||
if ( in_array( $errno, array( E_WARNING, E_NOTICE, E_STRICT, E_DEPRECATED ) ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -465,35 +493,93 @@ class IPS
|
||||
/**
|
||||
* Exception Handler
|
||||
*
|
||||
* @param \Exception $exception Exception class
|
||||
* @param \Throwable $exception Exception class
|
||||
* @return void
|
||||
*/
|
||||
public static function exceptionHandler( $exception )
|
||||
{
|
||||
try
|
||||
{
|
||||
\IPS\Log::i( \LOG_CRIT )->write( get_class( $exception ) . "\n" . $exception->getCode() . ": " . $exception->getMessage() . "\n" . $exception->getTraceAsString() );
|
||||
}
|
||||
catch ( \Exception $e ){}
|
||||
/* Are we trying to access the ACP? */
|
||||
$acp = ( \IPS\Dispatcher::hasInstance() AND \IPS\Dispatcher::i()->controllerLocation == 'admin' );
|
||||
|
||||
/* Work out what we'll log - exception classes can provide extra data */
|
||||
$log = '';
|
||||
if ( method_exists( $exception, 'extraLogData' ) )
|
||||
{
|
||||
$log .= $exception->extraLogData() . "\n";
|
||||
}
|
||||
$log .= get_class( $exception ) . ": " . $exception->getMessage() . " (" . $exception->getCode() . ")\n" . $exception->getTraceAsString();
|
||||
|
||||
/* Log it */
|
||||
\IPS\Log::log( $log, 'uncaught_exception' );
|
||||
|
||||
/* Report it */
|
||||
try
|
||||
{
|
||||
$message = 'generic_error';
|
||||
if ( !\IPS\IN_DEV and \IPS\Settings::i()->diagnostics_reporting and !\IPS\Settings::i()->theme_designers_mode and !self::exceptionWasThrownByThirdParty( $exception ) )
|
||||
{
|
||||
self::reportExceptionToIPS( $exception );
|
||||
}
|
||||
}
|
||||
catch ( \Exception $e ) { }
|
||||
|
||||
/* Try to display a friendly error page */
|
||||
try
|
||||
{
|
||||
/* If we couldn't connect to the database, don't bother trying to show the friendly page because nope */
|
||||
if( $exception instanceof \IPS\Db\Exception AND $exception->getCode() === 0 )
|
||||
{
|
||||
throw new \RuntimeException;
|
||||
}
|
||||
|
||||
/* If we're in the installer/upgrader, show the raw message */
|
||||
$message = 'generic_error';
|
||||
if( \IPS\Dispatcher::hasInstance() AND \IPS\Dispatcher::i()->controllerLocation == 'setup' )
|
||||
{
|
||||
$message = $exception->getMessage();
|
||||
}
|
||||
|
||||
\IPS\Output::i()->error( $message, "EX{$exception->getCode()}", 500 );
|
||||
$faultyAppOrHookId = static::exceptionWasThrownByThirdParty( $exception );
|
||||
|
||||
/* Output */
|
||||
\IPS\Output::i()->error( $message, "EX{$exception->getCode()}", 500, NULL, array(), $log, $faultyAppOrHookId );
|
||||
}
|
||||
/* And if *that* fails, show our generic page */
|
||||
catch ( \Exception $e )
|
||||
{
|
||||
static::genericExceptionPage();
|
||||
static::genericExceptionPage( $acp ? $exception->getMessage() : NULL );
|
||||
}
|
||||
catch ( \Throwable $e )
|
||||
{
|
||||
static::genericExceptionPage( $acp ? $exception->getMessage() : NULL );
|
||||
}
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should a given exception be reported to IPS? Filter out 3rd party etc.
|
||||
*
|
||||
* @param \Throwable $exception The exception
|
||||
* @return void
|
||||
*/
|
||||
final public static function reportExceptionToIPS( $exception )
|
||||
{
|
||||
$response = \IPS\Http\Url::external('https://invisionpowerdiagnostics.com')->request()->post( array(
|
||||
'version' => \IPS\Application::getAvailableVersion('core'),
|
||||
'class' => get_class( $exception ),
|
||||
'message' => $exception->getMessage(),
|
||||
'code' => $exception->getCode(),
|
||||
'file' => str_replace( \IPS\ROOT_PATH, '', $exception->getFile() ),
|
||||
'line' => $exception->getLine(),
|
||||
'backtrace' => str_replace( \IPS\ROOT_PATH, '', $exception->getTraceAsString() )
|
||||
) );
|
||||
|
||||
if ( $response->httpResponseCode == 410 )
|
||||
{
|
||||
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => 0 ), array( 'conf_key=?', 'diagnostics_reporting' ) );
|
||||
unset( \IPS\Data\Store::i()->settings );
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic exception page
|
||||
@@ -502,7 +588,7 @@ class IPS
|
||||
* @return void
|
||||
* @note Abstracted so Theme can call this if templates are in the process of building
|
||||
*/
|
||||
public static function genericExceptionPage()
|
||||
public static function genericExceptionPage( $message = NULL )
|
||||
{
|
||||
if( isset( $_SERVER['SERVER_PROTOCOL'] ) and \strstr( $_SERVER['SERVER_PROTOCOL'], '/1.0' ) !== false )
|
||||
{
|
||||
@@ -520,73 +606,66 @@ class IPS
|
||||
/**
|
||||
* Get license key data
|
||||
*
|
||||
* @param bool $forceRefresh If TRUE, will get data from server
|
||||
* @return array|NULL
|
||||
*/
|
||||
public static function licenseKey()
|
||||
public static function licenseKey( $forceRefresh = FALSE )
|
||||
{
|
||||
/* Get the cached value */
|
||||
/* We haven't license key saved in settings? Saving... */
|
||||
if ( !\IPS\Settings::i()->ipb_reg_number ) {
|
||||
\IPS\Db::i()->update( 'core_sys_conf_settings', array( 'conf_value' => 'LICENSE KEY GOES HERE!-123456789' ), array( 'conf_key=?', 'ipb_reg_number' ) );
|
||||
\IPS\Settings::i()->ipb_reg_number = 'LICENSE KEY GOES HERE!-123456789';
|
||||
}
|
||||
|
||||
$response = array(
|
||||
'key' => \IPS\Settings::i()->ipb_reg_number, //IPS Key
|
||||
'active' => \IPS\Settings::i()->ipb_license_active, //License Active?
|
||||
'cloud' => \IPS\Settings::i()->ipb_license_cloud, //We are "cloud" clients?
|
||||
'url' => \IPS\Settings::i()->ipb_license_url, //Forum URL
|
||||
'test_url' => \IPS\Settings::i()->ipb_license_test_url, //Test URL
|
||||
'expires' => \IPS\Settings::i()->ipb_license_expires, //When our license will expire?
|
||||
'products' => array( //Array of components. Can we use...
|
||||
'forums' => \IPS\Settings::i()->ipb_license_product_forums, //...IP.Board // Forums?
|
||||
'calendar' => \IPS\Settings::i()->ipb_license_product_calendar, //...IP.Calendar // Calendar?
|
||||
'blog' => \IPS\Settings::i()->ipb_license_product_blog, //...IP.Blogs // Blogs?
|
||||
'gallery' => \IPS\Settings::i()->ipb_license_product_gallery, //...IP.Gallery // Gallery?
|
||||
'downloads' => \IPS\Settings::i()->ipb_license_product_downloads, //...IP.Downloads // Downloads?
|
||||
'cms' => \IPS\Settings::i()->ipb_license_product_cms, //...IP.Content // Pages?
|
||||
'nexus' => \IPS\Settings::i()->ipb_license_product_nexus, //...IP.Nexus // Commerce?
|
||||
'spam' => FALSE, //...IPS Spam Service? No! Hardcoded to prevent requests to IPS servers.
|
||||
'copyright' => \IPS\Settings::i()->ipb_license_product_copyright, //...remove copyright function?
|
||||
),
|
||||
'chat_limit' => \IPS\Settings::i()->ipb_license_chat_limit, //How many users can use IP.Chat?
|
||||
'support' => \IPS\Settings::i()->ipb_license_support, //Can we use Support?
|
||||
);
|
||||
|
||||
$cached = NULL;
|
||||
$setFetched = FALSE;
|
||||
if ( isset( \IPS\Data\Store::i()->license_data ) )
|
||||
if ( isset( \IPS\Data\Store::i()->license_data ) ) //License data exists in cache?
|
||||
{
|
||||
$cached = \IPS\Data\Store::i()->license_data;
|
||||
|
||||
/* If it's younger than 21 days, just use that */
|
||||
if ( $cached['fetched'] > ( time() - 1814400 ) )
|
||||
/* Keep license data updated in cache store */
|
||||
if ( $cached['fetched'] < ( time() - 1814400 ) )
|
||||
{
|
||||
/* If the license is not expired, return the data */
|
||||
if( !$cached['data']['expires'] OR strtotime( $cached['data']['expires'] ) > time() )
|
||||
{
|
||||
return $cached['data'];
|
||||
}
|
||||
/* Otherwise if the license is expired but we've automatically refetched, return the data */
|
||||
else if( $cached['data']['expires'] AND strtotime( $cached['data']['expires'] ) < time() AND isset( $cached['data']['refetched'] ) )
|
||||
{
|
||||
return $cached['data'];
|
||||
}
|
||||
/* Otherwise remember to set the 'refetched' flag */
|
||||
else
|
||||
{
|
||||
$setFetched = TRUE;
|
||||
}
|
||||
}
|
||||
/* Data older, than 21 days. Updating... */
|
||||
unset( \IPS\Data\Store::i()->license_data );
|
||||
\IPS\Data\Store::i()->license_data = array( //Add information to cache...
|
||||
'fetched' => time(),
|
||||
'data' => $response,
|
||||
);
|
||||
return $response;
|
||||
} else {
|
||||
return $cached['data'];
|
||||
}
|
||||
}
|
||||
|
||||
/* Call the main server */
|
||||
try
|
||||
else
|
||||
{
|
||||
$response = \IPS\Http\Url::ips( 'license/' . \IPS\Settings::i()->ipb_reg_number )->request()->get();
|
||||
if ( $response->httpResponseCode == 404 )
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
$response = $response->decodeJson();
|
||||
/* Cached license data is missing? Creating... */
|
||||
\IPS\Data\Store::i()->license_data = array( //Add information to cache...
|
||||
'fetched' => time(),
|
||||
'data' => $response,
|
||||
);
|
||||
return $response;
|
||||
}
|
||||
catch ( \Exception $e )
|
||||
{
|
||||
/* If we can't access the license server right now, store something in cache to prevent a request on every page load. We
|
||||
set fetched to 20 days ago so that this cache is only good for 1 day instead of 21 days however. */
|
||||
if( $cached === NULL )
|
||||
{
|
||||
\IPS\Data\Store::i()->license_data = array( 'fetched' => time() - 1728000, 'data' => NULL );
|
||||
}
|
||||
|
||||
/* If the server is offline right now, use the cached value from above */
|
||||
return $cached;
|
||||
}
|
||||
|
||||
/* Update the license info in the store */
|
||||
$licenseData = array( 'fetched' => time(), 'data' => $response );
|
||||
|
||||
if( $setFetched )
|
||||
{
|
||||
$licenseData['refetched'] = 1;
|
||||
}
|
||||
|
||||
\IPS\Data\Store::i()->license_data = $licenseData;
|
||||
|
||||
/* Return */
|
||||
return $response;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -599,7 +678,121 @@ class IPS
|
||||
*/
|
||||
public static function checkLicenseKey( $val, $url )
|
||||
{
|
||||
return $false;
|
||||
//Do Nothing
|
||||
}
|
||||
|
||||
/**
|
||||
* Was the exception thrown by a third party app/plugin?
|
||||
*
|
||||
* @param \Throwable $exception The exception
|
||||
* @return string|int|NULL string = application directory, int = hook id, null means, that it was probably caused by an IPS4 application
|
||||
*/
|
||||
public static function exceptionWasThrownByThirdParty( $exception )
|
||||
{
|
||||
/* Did it happen in a hook? */
|
||||
if ( preg_match( '/init\.php\(\d*\) : eval\(\)\'d code$/', $exception->getFile() ) )
|
||||
{
|
||||
$trace = $exception->getTraceAsString();
|
||||
|
||||
/* Did it happen inside an applications hook? */
|
||||
if ( preg_match( '/([a-zA-Z]+)_hook/', $trace, $matches ) )
|
||||
{
|
||||
if ( !in_array( $matches[1], \IPS\Application::$ipsApps ) )
|
||||
{
|
||||
return $matches[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
/* Did it happen inside a plugin hook? */
|
||||
else if ( preg_match( '/hook(\d)/', $trace, $matches ) )
|
||||
{
|
||||
/* Return the hook id, the error method will fetch the plugin name */
|
||||
return $matches[1];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
/* Exception was thrown by 'normal code', check if it's from a third-party app */
|
||||
else
|
||||
{
|
||||
if ( preg_match( '/' . preg_quote( DIRECTORY_SEPARATOR, '/' ) . 'applications' . preg_quote( DIRECTORY_SEPARATOR, '/' ) . '([a-zA-Z]+)/', str_replace( \IPS\ROOT_PATH, '', $exception->getFile() ), $matches ) )
|
||||
{
|
||||
if ( !in_array( $matches[1], \IPS\Application::$ipsApps ) )
|
||||
{
|
||||
return $matches[1];
|
||||
}
|
||||
else
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Still here? Probably system */
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Resync IPS Cloud File System
|
||||
* Must be called when writing any files to disk on IPS Community in the Cloud
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public static function resyncIPSCloud()
|
||||
{
|
||||
if ( \IPS\CIC )
|
||||
{
|
||||
if ( preg_match( '/^\/var\/www\/html\/(.+?)(?:\/|$)/i', \IPS\ROOT_PATH, $matches ) )
|
||||
{
|
||||
try
|
||||
{
|
||||
\IPS\Http\Url::external('http://ips-cic-fileupdate.invisioncic.com/')
|
||||
->setQueryString( 'user', $matches[1] )
|
||||
->request()
|
||||
->post();
|
||||
}
|
||||
catch ( \Exception $e ) { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get CiC Site Tracking ID
|
||||
*
|
||||
* @return int|null
|
||||
*/
|
||||
public static function cicTrackingId()
|
||||
{
|
||||
if ( \IPS\CIC and preg_match( '/^\/var\/www\/html\/(.+?)(?:\/|$)/i', \IPS\ROOT_PATH, $matches ) )
|
||||
{
|
||||
$id = NULL;
|
||||
if ( isset( \IPS\Data\Store::i()->cicTrackingId ) )
|
||||
{
|
||||
$decoded = json_decode( \IPS\Data\Store::i()->cicTrackingId, TRUE );
|
||||
$id = $decoded['value'];
|
||||
if ( $decoded['time'] > ( time() - 86400 ) )
|
||||
{
|
||||
return $id;
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
$id = intval( (string) \IPS\Http\Url::external("https://invisioncloudstats.com/IPS/")->setQueryString( 'user', $matches[1] )->request()->get() );
|
||||
}
|
||||
catch ( \Exception $e )
|
||||
{
|
||||
return $id;
|
||||
}
|
||||
|
||||
\IPS\Data\Store::i()->cicTrackingId = json_encode( array( 'time' => time(), 'value' => $id ) );
|
||||
|
||||
return $id;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user