2017-05-08 06:11:38 +00:00
< ? php
2024-08-24 15:27:00 +02:00
// Copyright (C) 2010-2024, the Friendica project
// SPDX-FileCopyrightText: 2010-2024 the Friendica project
//
// SPDX-License-Identifier: AGPL-3.0-or-later
2020-02-09 16:18:46 +01:00
2017-05-08 06:11:38 +00:00
namespace Friendica ;
2024-12-18 22:16:34 +00:00
use Dice\Dice ;
2019-08-12 18:13:58 +02:00
use Friendica\App\Arguments ;
2019-08-15 17:23:00 +02:00
use Friendica\App\BaseURL ;
2024-11-06 07:21:50 +00:00
use Friendica\App\Mode ;
2024-11-10 12:02:31 +00:00
use Friendica\App\Page ;
2024-06-09 06:13:20 +00:00
use Friendica\App\Request ;
2024-11-10 12:02:31 +00:00
use Friendica\App\Router ;
2021-11-21 23:37:17 +01:00
use Friendica\Capabilities\ICanCreateResponses ;
2023-01-08 01:17:06 -05:00
use Friendica\Content\Nav ;
2021-10-26 21:44:29 +02:00
use Friendica\Core\Config\Factory\Config ;
2024-12-26 08:27:02 +00:00
use Friendica\Core\Renderer ;
2022-10-20 21:27:32 +02:00
use Friendica\Core\Session\Capability\IHandleUserSessions ;
2024-12-26 10:07:03 +00:00
use Friendica\Database\DBA ;
2022-12-16 21:59:32 +01:00
use Friendica\Database\Definition\DbaDefinition ;
use Friendica\Database\Definition\ViewDefinition ;
2024-12-26 10:07:03 +00:00
use Friendica\DI ;
2021-03-10 09:40:42 -05:00
use Friendica\Module\Maintenance ;
2020-09-30 16:53:18 +02:00
use Friendica\Security\Authentication ;
2021-10-26 21:44:29 +02:00
use Friendica\Core\Config\Capability\IManageConfigValues ;
2024-12-26 10:07:03 +00:00
use Friendica\Core\L10n ;
use Friendica\Core\Logger ;
2024-12-25 21:12:24 +00:00
use Friendica\Core\Logger\Capability\LogChannel ;
2021-10-26 21:44:29 +02:00
use Friendica\Core\PConfig\Capability\IManagePersonalConfigValues ;
2019-07-23 20:01:45 -04:00
use Friendica\Core\System ;
2024-12-26 10:07:03 +00:00
use Friendica\Core\Update ;
use Friendica\Core\Worker ;
2019-08-12 18:13:58 +02:00
use Friendica\Module\Special\HTTPException as ModuleHTTPException ;
2019-05-01 21:33:33 -04:00
use Friendica\Network\HTTPException ;
2024-03-16 12:54:17 +00:00
use Friendica\Protocol\ATProtocol\DID ;
2024-12-25 21:12:24 +00:00
use Friendica\Security\ExAuth ;
2024-05-27 04:33:28 +00:00
use Friendica\Security\OpenWebAuth ;
2021-10-03 12:38:47 -04:00
use Friendica\Util\DateTimeFormat ;
2021-11-28 13:44:42 +01:00
use Friendica\Util\HTTPInputData ;
2019-03-19 06:44:51 +00:00
use Friendica\Util\HTTPSignature ;
2019-02-16 23:11:30 +01:00
use Friendica\Util\Profiler ;
2024-12-20 10:07:59 +00:00
use Psr\Http\Message\ServerRequestInterface ;
2018-12-30 21:42:56 +01:00
use Psr\Log\LoggerInterface ;
2017-05-11 11:53:04 -04:00
2024-12-26 10:07:03 +00:00
2017-05-08 06:11:38 +00:00
/**
2020-01-19 06:05:23 +00:00
* Our main application structure for the life of this page .
2017-05-08 06:11:38 +00:00
*
* Primarily deals with the URL that got us here
* and tries to make some sense of it , and
* stores our page contents and config storage
* and anything else that might need to be passed around
* before we spit the page out .
*
*/
2024-12-18 21:30:36 +00:00
class App
2018-01-15 19:13:21 -05:00
{
2022-10-17 10:37:48 +00:00
const PLATFORM = 'Friendica' ;
2023-12-24 15:52:17 +01:00
const CODENAME = 'Yellow Archangel' ;
2024-11-16 08:39:37 +00:00
const VERSION = '2024.12-dev' ;
2022-10-17 10:37:48 +00:00
2024-12-18 22:16:34 +00:00
public static function fromDice ( Dice $dice ) : self
{
2024-12-20 12:30:05 +00:00
return new self ( $dice );
2024-12-18 22:16:34 +00:00
}
2024-12-19 20:24:24 +00:00
/**
* @ var Dice
*/
private $container ;
2018-10-06 16:27:20 +02:00
/**
2024-11-06 07:21:50 +00:00
* @ var Mode The Mode of the Application
2018-10-06 16:27:20 +02:00
*/
private $mode ;
2018-10-09 19:58:58 +02:00
/**
2019-04-08 21:12:10 +02:00
* @ var BaseURL
2018-10-09 19:58:58 +02:00
*/
2019-04-08 21:12:10 +02:00
private $baseURL ;
2018-10-09 19:58:58 +02:00
2024-06-09 06:13:20 +00:00
/** @var string */
private $requestId ;
2024-05-27 04:33:28 +00:00
/** @var Authentication */
private $auth ;
2018-12-30 21:42:56 +01:00
/**
2021-10-26 21:44:29 +02:00
* @ var IManageConfigValues The config
2019-02-03 22:22:04 +01:00
*/
private $config ;
2019-02-17 21:12:12 +01:00
/**
* @ var LoggerInterface The logger
2018-12-30 21:42:56 +01:00
*/
private $logger ;
2019-02-03 22:22:04 +01:00
/**
2019-02-16 23:11:30 +01:00
* @ var Profiler The profiler of this app
2019-02-03 22:22:04 +01:00
*/
2019-02-16 23:11:30 +01:00
private $profiler ;
2019-02-03 22:22:04 +01:00
2019-07-09 21:44:02 +02:00
/**
* @ var L10n The translator
*/
private $l10n ;
2019-08-12 18:13:58 +02:00
/**
* @ var App\Arguments
*/
private $args ;
2022-10-20 22:06:25 +02:00
/**
* @ var IHandleUserSessions
*/
2022-10-23 20:41:17 +02:00
private $session ;
2022-10-20 22:06:25 +02:00
2024-11-06 12:26:33 +00:00
/**
* @ var AppHelper $appHelper
*/
private $appHelper ;
2024-12-20 12:30:05 +00:00
private function __construct ( Dice $container )
{
2024-12-19 20:24:24 +00:00
$this -> container = $container ;
2024-12-20 13:24:25 +00:00
}
public function processRequest ( ServerRequestInterface $request , float $start_time ) : void
{
2024-12-25 21:27:08 +00:00
$this -> setupContainerForAddons ();
2024-12-26 08:10:06 +00:00
$this -> setupContainerForLogger ( LogChannel :: DEFAULT );
2024-12-25 21:27:08 +00:00
$this -> container = $this -> container -> addRule ( Mode :: class , [
'call' => [
[ 'determineRunMode' , [ false , $request -> getServerParams ()], Dice :: CHAIN_CALL ],
],
]);
$this -> setupLegacyServerLocator ();
2024-12-23 15:22:27 +00:00
2024-12-23 15:30:42 +00:00
$this -> registerErrorHandler ();
2024-12-20 12:30:05 +00:00
$this -> requestId = $this -> container -> create ( Request :: class ) -> getRequestId ();
$this -> auth = $this -> container -> create ( Authentication :: class );
$this -> config = $this -> container -> create ( IManageConfigValues :: class );
$this -> mode = $this -> container -> create ( Mode :: class );
$this -> baseURL = $this -> container -> create ( BaseURL :: class );
$this -> logger = $this -> container -> create ( LoggerInterface :: class );
$this -> profiler = $this -> container -> create ( Profiler :: class );
$this -> l10n = $this -> container -> create ( L10n :: class );
$this -> args = $this -> container -> create ( Arguments :: class );
$this -> session = $this -> container -> create ( IHandleUserSessions :: class );
$this -> appHelper = $this -> container -> create ( AppHelper :: class );
2024-11-17 19:53:17 +00:00
2024-12-26 08:45:57 +00:00
$this -> loadSetupForFrontend (
$request ,
2024-12-20 10:04:02 +00:00
$this -> container -> create ( DbaDefinition :: class ),
$this -> container -> create ( ViewDefinition :: class ),
);
2024-12-20 10:05:13 +00:00
2024-12-26 08:29:11 +00:00
$this -> registerTemplateEngine ();
2024-12-20 10:05:13 +00:00
$this -> mode -> setExecutor ( Mode :: INDEX );
2024-12-20 10:07:59 +00:00
$this -> runFrontend (
2024-12-20 13:38:38 +00:00
$this -> container -> create ( Router :: class ),
$this -> container -> create ( IManagePersonalConfigValues :: class ),
$this -> container -> create ( Page :: class ),
$this -> container -> create ( Nav :: class ),
$this -> container -> create ( ModuleHTTPException :: class ),
new HTTPInputData ( $request -> getServerParams ()),
2024-12-20 10:07:59 +00:00
$start_time ,
$request -> getServerParams ()
);
2024-12-20 10:04:02 +00:00
}
2024-12-25 21:12:24 +00:00
public function processEjabberd () : void
{
2024-12-25 21:16:00 +00:00
$this -> setupContainerForAddons ();
2024-12-26 08:10:06 +00:00
$this -> setupContainerForLogger ( LogChannel :: AUTH_JABBERED );
2024-12-25 21:12:24 +00:00
2024-12-25 21:20:08 +00:00
$this -> setupLegacyServerLocator ();
2024-12-25 21:17:02 +00:00
$this -> registerErrorHandler ();
2024-12-25 21:12:24 +00:00
// Check the database structure and possibly fixes it
2024-12-26 10:13:04 +00:00
Update :: check ( DI :: basePath (), true );
2024-12-25 21:12:24 +00:00
$appMode = $this -> container -> create ( Mode :: class );
if ( $appMode -> isNormal ()) {
/** @var ExAuth $oAuth */
$oAuth = $this -> container -> create ( ExAuth :: class );
$oAuth -> readStdin ();
}
}
2024-12-26 07:44:19 +00:00
public function processConsole ( array $argv ) : void
{
2024-12-26 08:01:30 +00:00
$this -> setupContainerForAddons ();
2024-12-26 07:44:19 +00:00
2024-12-26 08:10:06 +00:00
$this -> setupContainerForLogger ( LogChannel :: CONSOLE );
2024-12-26 08:01:30 +00:00
$this -> setupLegacyServerLocator ();
$this -> registerErrorHandler ();
2024-12-26 07:44:19 +00:00
2024-12-26 08:27:02 +00:00
$this -> registerTemplateEngine ();
2024-12-26 08:24:19 +00:00
2024-12-26 07:44:19 +00:00
( new \Friendica\Core\Console ( $this -> container , $argv )) -> execute ();
}
2024-12-26 10:27:44 +00:00
public function processDaemon ( array $argv , array $options ) : void
2024-12-26 10:07:03 +00:00
{
2024-12-26 10:16:01 +00:00
$this -> setupContainerForAddons ();
$this -> setupContainerForLogger ( LogChannel :: DAEMON );
2024-12-26 10:07:03 +00:00
2024-12-26 10:17:22 +00:00
$this -> setupLegacyServerLocator ();
2024-12-26 10:18:03 +00:00
$this -> registerErrorHandler ();
2024-12-26 10:07:03 +00:00
2024-12-26 10:30:09 +00:00
/** @var Mode */
$mode = $this -> container -> create ( Mode :: class );
if ( $mode -> isInstall ()) {
2024-12-26 10:07:03 +00:00
die ( " Friendica isn't properly installed yet. \n " );
}
2024-12-26 10:30:09 +00:00
$mode -> setExecutor ( Mode :: DAEMON );
2024-12-26 10:07:03 +00:00
2024-12-26 10:34:32 +00:00
/** @var IManageConfigValues */
$config = $this -> container -> create ( IManageConfigValues :: class );
2024-12-26 10:07:03 +00:00
2024-12-26 10:34:32 +00:00
$config -> reload ();
if ( empty ( $config -> get ( 'system' , 'pidfile' ))) {
2024-12-26 10:26:10 +00:00
die ( <<< TXT
Please set system . pidfile in config / local . config . php . For example :
2024-12-26 10:07:03 +00:00
2024-12-26 10:26:10 +00:00
'system' => [
'pidfile' => '/path/to/daemon.pid' ,
],
TXT
2024-12-26 10:07:03 +00:00
);
}
2024-12-26 10:34:32 +00:00
$pidfile = $config -> get ( 'system' , 'pidfile' );
2024-12-26 10:07:03 +00:00
2024-12-26 10:27:44 +00:00
if ( in_array ( 'start' , $argv )) {
2024-12-26 10:38:10 +00:00
$daemonMode = 'start' ;
2024-12-26 10:07:03 +00:00
}
2024-12-26 10:27:44 +00:00
if ( in_array ( 'stop' , $argv )) {
2024-12-26 10:38:10 +00:00
$daemonMode = 'stop' ;
2024-12-26 10:07:03 +00:00
}
2024-12-26 10:27:44 +00:00
if ( in_array ( 'status' , $argv )) {
2024-12-26 10:38:10 +00:00
$daemonMode = 'status' ;
2024-12-26 10:07:03 +00:00
}
$foreground = array_key_exists ( 'f' , $options ) || array_key_exists ( 'foreground' , $options );
2024-12-26 10:38:10 +00:00
if ( ! isset ( $daemonMode )) {
2024-12-26 10:07:03 +00:00
die ( " Please use either 'start', 'stop' or 'status'. \n " );
}
2024-12-26 10:27:44 +00:00
if ( empty ( $argv [ 0 ])) {
2024-12-26 10:07:03 +00:00
die ( " Unexpected script behaviour. This message should never occur. \n " );
}
$pid = null ;
if ( is_readable ( $pidfile )) {
$pid = intval ( file_get_contents ( $pidfile ));
}
2024-12-26 10:38:10 +00:00
if ( empty ( $pid ) && in_array ( $daemonMode , [ 'stop' , 'status' ])) {
2024-12-26 10:07:03 +00:00
DI :: keyValue () -> set ( 'worker_daemon_mode' , false );
die ( " Pidfile wasn't found. Is the daemon running? \n " );
}
2024-12-26 10:38:10 +00:00
if ( $daemonMode == 'status' ) {
2024-12-26 10:07:03 +00:00
if ( posix_kill ( $pid , 0 )) {
die ( " Daemon process $pid is running. \n " );
}
unlink ( $pidfile );
DI :: keyValue () -> set ( 'worker_daemon_mode' , false );
die ( " Daemon process $pid isn't running. \n " );
}
2024-12-26 10:38:10 +00:00
if ( $daemonMode == 'stop' ) {
2024-12-26 10:07:03 +00:00
posix_kill ( $pid , SIGTERM );
unlink ( $pidfile );
Logger :: notice ( 'Worker daemon process was killed' , [ 'pid' => $pid ]);
DI :: keyValue () -> set ( 'worker_daemon_mode' , false );
die ( " Worker daemon process $pid was killed. \n " );
}
if ( ! empty ( $pid ) && posix_kill ( $pid , 0 )) {
die ( " Daemon process $pid is already running. \n " );
}
Logger :: notice ( 'Starting worker daemon.' , [ 'pid' => $pid ]);
if ( ! $foreground ) {
echo " Starting worker daemon. \n " ;
DBA :: disconnect ();
// Fork a daemon process
$pid = pcntl_fork ();
if ( $pid == - 1 ) {
echo " Daemon couldn't be forked. \n " ;
Logger :: warning ( 'Could not fork daemon' );
exit ( 1 );
} elseif ( $pid ) {
// The parent process continues here
if ( ! file_put_contents ( $pidfile , $pid )) {
echo " Pid file wasn't written. \n " ;
Logger :: warning ( 'Could not store pid file' );
posix_kill ( $pid , SIGTERM );
exit ( 1 );
}
echo 'Child process started with pid ' . $pid . " . \n " ;
Logger :: notice ( 'Child process started' , [ 'pid' => $pid ]);
exit ( 0 );
}
// We now are in the child process
register_shutdown_function ( function () {
posix_kill ( posix_getpid (), SIGTERM );
posix_kill ( posix_getpid (), SIGHUP );
});
// Make the child the main process, detach it from the terminal
if ( posix_setsid () < 0 ) {
return ;
}
// Closing all existing connections with the outside
fclose ( STDIN );
// And now connect the database again
DBA :: connect ();
}
DI :: keyValue () -> set ( 'worker_daemon_mode' , true );
// Just to be sure that this script really runs endlessly
set_time_limit ( 0 );
2024-12-26 10:34:32 +00:00
$wait_interval = intval ( $config -> get ( 'system' , 'cron_interval' , 5 )) * 60 ;
2024-12-26 10:07:03 +00:00
$do_cron = true ;
$last_cron = 0 ;
// Now running as a daemon.
while ( true ) {
// Check the database structure and possibly fixes it
Update :: check ( DI :: basePath (), true );
if ( ! $do_cron && ( $last_cron + $wait_interval ) < time ()) {
Logger :: info ( 'Forcing cron worker call.' , [ 'pid' => $pid ]);
$do_cron = true ;
}
if ( $do_cron || ( ! DI :: system () -> isMaxLoadReached () && Worker :: entriesExists () && Worker :: isReady ())) {
Worker :: spawnWorker ( $do_cron );
} else {
Logger :: info ( 'Cool down for 5 seconds' , [ 'pid' => $pid ]);
sleep ( 5 );
}
if ( $do_cron ) {
// We force a reconnect of the database connection.
// This is done to ensure that the connection don't get lost over time.
DBA :: reconnect ();
$last_cron = time ();
}
$start = time ();
Logger :: info ( 'Sleeping' , [ 'pid' => $pid , 'until' => gmdate ( DateTimeFormat :: MYSQL , $start + $wait_interval )]);
do {
$seconds = ( time () - $start );
// logarithmic wait time calculation.
// Background: After jobs had been started, they often fork many workers.
// To not waste too much time, the sleep period increases.
$arg = (( $seconds + 1 ) / ( $wait_interval / 9 )) + 1 ;
$sleep = min ( 1000000 , round ( log10 ( $arg ) * 1000000 , 0 ));
usleep ( $sleep );
$pid = pcntl_waitpid ( - 1 , $status , WNOHANG );
if ( $pid > 0 ) {
Logger :: info ( 'Children quit via pcntl_waitpid' , [ 'pid' => $pid , 'status' => $status ]);
}
$timeout = ( $seconds >= $wait_interval );
} while ( ! $timeout && ! Worker\IPC :: JobsExists ());
if ( $timeout ) {
$do_cron = true ;
Logger :: info ( 'Woke up after $wait_interval seconds.' , [ 'pid' => $pid , 'sleep' => $wait_interval ]);
} else {
$do_cron = false ;
Logger :: info ( 'Worker jobs are calling to be forked.' , [ 'pid' => $pid ]);
}
}
}
2024-12-25 21:16:00 +00:00
private function setupContainerForAddons () : void
{
/** @var \Friendica\Core\Addon\Capability\ICanLoadAddons $addonLoader */
$addonLoader = $this -> container -> create ( \Friendica\Core\Addon\Capability\ICanLoadAddons :: class );
$this -> container = $this -> container -> addRules ( $addonLoader -> getActiveAddonConfig ( 'dependencies' ));
}
2024-12-26 08:10:06 +00:00
private function setupContainerForLogger ( string $logChannel ) : void
{
$this -> container = $this -> container -> addRule ( LoggerInterface :: class , [
'constructParams' => [ $logChannel ],
]);
}
2024-12-25 21:20:08 +00:00
private function setupLegacyServerLocator () : void
{
2024-12-26 10:13:04 +00:00
DI :: init ( $this -> container );
2024-12-25 21:20:08 +00:00
}
2024-12-23 15:30:42 +00:00
private function registerErrorHandler () : void
{
2024-12-23 15:28:57 +00:00
\Friendica\Core\Logger\Handler\ErrorHandler :: register ( $this -> container -> create ( LoggerInterface :: class ));
2024-12-23 15:22:27 +00:00
}
2024-12-26 08:27:02 +00:00
private function registerTemplateEngine () : void
{
Renderer :: registerTemplateEngine ( 'Friendica\Render\FriendicaSmartyEngine' );
}
2019-08-15 16:18:08 +02:00
/**
* Load the whole app instance
*/
2024-12-26 08:45:57 +00:00
private function loadSetupForFrontend ( ServerRequestInterface $request , DbaDefinition $dbaDefinition , ViewDefinition $viewDefinition )
2019-08-15 16:18:08 +02:00
{
2023-05-04 17:48:13 +02:00
if ( $this -> config -> get ( 'system' , 'ini_max_execution_time' ) !== false ) {
set_time_limit (( int ) $this -> config -> get ( 'system' , 'ini_max_execution_time' ));
}
if ( $this -> config -> get ( 'system' , 'ini_pcre_backtrack_limit' ) !== false ) {
ini_set ( 'pcre.backtrack_limit' , ( int ) $this -> config -> get ( 'system' , 'ini_pcre_backtrack_limit' ));
}
2018-07-02 07:23:47 -04:00
2022-10-18 04:35:06 +00:00
// Normally this constant is defined - but not if "pcntl" isn't installed
if ( ! defined ( 'SIGTERM' )) {
define ( 'SIGTERM' , 15 );
}
2021-10-03 12:38:47 -04:00
// Ensure that all "strtotime" operations do run timezone independent
date_default_timezone_set ( 'UTC' );
2019-08-15 16:18:08 +02:00
$this -> profiler -> reset ();
2018-06-25 20:38:41 -04:00
2024-11-06 07:21:50 +00:00
if ( $this -> mode -> has ( Mode :: DBAVAILABLE )) {
2019-02-23 01:24:08 +01:00
Core\Hook :: loadHooks ();
2024-12-26 08:45:57 +00:00
$loader = ( new Config ()) -> createConfigFileManager ( $this -> appHelper -> getBasePath (), $request -> getServerParams ());
2019-02-23 01:24:08 +01:00
Core\Hook :: callAll ( 'load_config' , $loader );
2022-12-16 21:59:32 +01:00
// Hooks are now working, reload the whole definitions with hook enabled
$dbaDefinition -> load ( true );
$viewDefinition -> load ( true );
2018-08-27 06:15:55 +02:00
}
$this -> loadDefaultTimezone ();
}
2018-06-27 23:05:38 -04:00
/**
* Loads the default timezone
*
* Include support for legacy $default_timezone
*
* @ global string $default_timezone
*/
2018-06-25 20:38:41 -04:00
private function loadDefaultTimezone ()
{
2019-02-03 22:22:04 +01:00
if ( $this -> config -> get ( 'system' , 'default_timezone' )) {
2021-10-03 12:38:47 -04:00
$timezone = $this -> config -> get ( 'system' , 'default_timezone' , 'UTC' );
2018-06-25 20:38:41 -04:00
} else {
global $default_timezone ;
2021-10-03 12:38:47 -04:00
$timezone = $default_timezone ? ? '' ? : 'UTC' ;
2018-06-25 20:38:41 -04:00
}
2024-11-06 12:26:33 +00:00
$this -> appHelper -> setTimeZone ( $timezone );
2018-06-25 20:38:41 -04:00
}
2018-10-21 22:24:47 -04:00
/**
* Frontend App script
*
* The App object behaves like a container and a dispatcher at the same time , including a representation of the
* request and a representation of the response .
*
* This probably should change to limit the size of this monster method .
2019-08-12 18:13:58 +02:00
*
2024-11-10 12:02:31 +00:00
* @ param Router $router
2021-10-26 21:44:29 +02:00
* @ param IManagePersonalConfigValues $pconfig
2024-11-10 12:02:31 +00:00
* @ param Page $page The Friendica page printing container
2022-12-26 21:17:32 +01:00
* @ param ModuleHTTPException $httpException The possible HTTP Exception container
2021-11-28 14:01:13 +01:00
* @ param HTTPInputData $httpInput A library for processing PHP input streams
* @ param float $start_time The start time of the overall script execution
2023-02-18 20:47:52 +01:00
* @ param array $server The $_SERVER array
2020-01-19 22:23:44 +01:00
*
2019-10-06 11:18:51 -04:00
* @ throws HTTPException\InternalServerErrorException
* @ throws \ImagickException
2018-10-21 22:24:47 -04:00
*/
2024-12-20 13:24:25 +00:00
private function runFrontend (
2024-11-10 12:02:31 +00:00
Router $router ,
IManagePersonalConfigValues $pconfig ,
Page $page ,
Nav $nav ,
ModuleHTTPException $httpException ,
HTTPInputData $httpInput ,
float $start_time ,
array $server
) {
2024-03-16 12:54:17 +00:00
$requeststring = ( $server [ 'REQUEST_METHOD' ] ? ? '' ) . ' ' . ( $server [ 'REQUEST_URI' ] ? ? '' ) . ' ' . ( $server [ 'SERVER_PROTOCOL' ] ? ? '' );
$this -> logger -> debug ( 'Request received' , [ 'address' => $server [ 'REMOTE_ADDR' ] ? ? '' , 'request' => $requeststring , 'referer' => $server [ 'HTTP_REFERER' ] ? ? '' , 'user-agent' => $server [ 'HTTP_USER_AGENT' ] ? ? '' ]);
2023-10-26 03:41:35 +00:00
$request_start = microtime ( true );
2024-11-19 08:40:48 +00:00
$request = $_REQUEST ;
2023-09-11 08:47:35 +00:00
2020-12-10 00:02:23 +00:00
$this -> profiler -> set ( $start_time , 'start' );
2020-12-09 22:10:27 +00:00
$this -> profiler -> set ( microtime ( true ), 'classinit' );
2021-11-19 22:47:49 +01:00
$moduleName = $this -> args -> getModuleName ();
2022-09-20 16:30:56 +02:00
$page -> setLogging ( $this -> args -> getMethod (), $this -> args -> getModuleName (), $this -> args -> getCommand ());
2019-08-12 18:13:58 +02:00
2019-07-23 20:01:45 -04:00
try {
// Missing DB connection: ERROR
2024-11-06 07:21:50 +00:00
if ( $this -> mode -> has ( Mode :: LOCALCONFIGPRESENT ) && ! $this -> mode -> has ( Mode :: DBAVAILABLE )) {
2022-01-03 22:29:26 +01:00
throw new HTTPException\InternalServerErrorException ( $this -> l10n -> t ( 'Apologies but the website is unavailable at the moment.' ));
2019-07-23 20:01:45 -04:00
}
2018-10-21 22:24:47 -04:00
2019-08-15 16:18:08 +02:00
if ( ! $this -> mode -> isInstall ()) {
2019-07-23 20:01:45 -04:00
// Force SSL redirection
2023-02-18 20:47:52 +01:00
if ( $this -> config -> get ( 'system' , 'force_ssl' ) &&
( empty ( $server [ 'HTTPS' ]) || $server [ 'HTTPS' ] === 'off' ) &&
2023-05-24 08:04:34 -04:00
( empty ( $server [ 'HTTP_X_FORWARDED_PROTO' ]) || $server [ 'HTTP_X_FORWARDED_PROTO' ] === 'http' ) &&
2023-02-18 20:47:52 +01:00
! empty ( $server [ 'REQUEST_METHOD' ]) &&
$server [ 'REQUEST_METHOD' ] === 'GET' ) {
System :: externalRedirect ( $this -> baseURL . '/' . $this -> args -> getQueryString ());
2019-07-23 20:01:45 -04:00
}
Core\Hook :: callAll ( 'init_1' );
2019-03-19 06:44:51 +00:00
}
2024-03-16 12:54:17 +00:00
DID :: routeRequest ( $this -> args -> getCommand (), $server );
2020-10-02 09:31:39 +00:00
if ( $this -> mode -> isNormal () && ! $this -> mode -> isBackend ()) {
2024-03-16 12:54:17 +00:00
$requester = HTTPSignature :: getSigner ( '' , $server );
2019-07-23 20:01:45 -04:00
if ( ! empty ( $requester )) {
2024-05-27 04:33:28 +00:00
OpenWebAuth :: addVisitorCookieForHandle ( $requester );
2018-10-21 22:24:47 -04:00
}
}
2019-07-23 20:01:45 -04:00
// ZRL
2022-10-23 20:41:17 +02:00
if ( ! empty ( $_GET [ 'zrl' ]) && $this -> mode -> isNormal () && ! $this -> mode -> isBackend () && ! $this -> session -> getLocalUserId ()) {
2022-12-07 22:24:01 -05:00
// Only continue when the given profile link seems valid.
2021-12-08 20:23:07 +00:00
// Valid profile links contain a path with "/profile/" and no query parameters
2022-06-24 03:01:13 +02:00
if (( parse_url ( $_GET [ 'zrl' ], PHP_URL_QUERY ) == '' ) &&
2022-12-07 22:24:01 -05:00
strpos ( parse_url ( $_GET [ 'zrl' ], PHP_URL_PATH ) ? ? '' , '/profile/' ) !== false ) {
2024-05-27 04:33:28 +00:00
$this -> auth -> setUnauthenticatedVisitor ( $_GET [ 'zrl' ]);
OpenWebAuth :: zrlInit ();
2021-12-08 20:23:07 +00:00
} else {
// Someone came with an invalid parameter, maybe as a DDoS attempt
// We simply stop processing here
$this -> logger -> debug ( 'Invalid ZRL parameter.' , [ 'zrl' => $_GET [ 'zrl' ]]);
throw new HTTPException\ForbiddenException ();
2019-07-23 20:01:45 -04:00
}
}
2018-10-21 22:24:47 -04:00
2019-08-15 16:18:08 +02:00
if ( ! empty ( $_GET [ 'owt' ]) && $this -> mode -> isNormal ()) {
2019-07-23 20:01:45 -04:00
$token = $_GET [ 'owt' ];
2024-05-27 04:33:28 +00:00
OpenWebAuth :: init ( $token );
2019-07-23 20:01:45 -04:00
}
2018-10-21 22:24:47 -04:00
2021-10-24 23:17:22 -04:00
if ( ! $this -> mode -> isBackend ()) {
2024-12-20 13:38:38 +00:00
$this -> auth -> withSession ();
2021-10-24 23:17:22 -04:00
}
2018-10-21 22:24:47 -04:00
2024-05-27 04:33:28 +00:00
if ( $this -> session -> isUnauthenticated ()) {
2019-07-23 20:01:45 -04:00
header ( 'X-Account-Management-Status: none' );
}
2018-10-21 22:24:47 -04:00
2019-07-23 20:01:45 -04:00
/*
* check_config () is responsible for running update scripts . These automatically
* update the DB schema whenever we push a new one out . It also checks to see if
* any addons have been added or removed and reacts accordingly .
*/
// in install mode, any url loads install module
// but we need "view" module for stylesheet
2019-08-15 16:18:08 +02:00
if ( $this -> mode -> isInstall () && $moduleName !== 'install' ) {
2019-12-16 00:28:31 +01:00
$this -> baseURL -> redirect ( 'install' );
2019-07-23 20:01:45 -04:00
} else {
2024-11-08 16:20:55 +00:00
Core\Update :: check ( $this -> appHelper -> getBasePath (), false );
2019-07-23 20:01:45 -04:00
Core\Addon :: loadAddons ();
Core\Hook :: loadHooks ();
}
2018-10-21 22:24:47 -04:00
2024-05-20 19:36:40 +00:00
// Compatibility with Hubzilla
if ( $moduleName == 'rpost' ) {
$this -> baseURL -> redirect ( 'compose' );
}
2019-07-23 20:01:45 -04:00
// Compatibility with the Android Diaspora client
2019-08-12 18:13:58 +02:00
if ( $moduleName == 'stream' ) {
2019-12-16 00:28:31 +01:00
$this -> baseURL -> redirect ( 'network?order=post' );
2019-07-23 20:01:45 -04:00
}
2018-10-21 22:24:47 -04:00
2019-08-12 18:13:58 +02:00
if ( $moduleName == 'conversations' ) {
2019-12-16 00:28:31 +01:00
$this -> baseURL -> redirect ( 'message' );
2019-07-23 20:01:45 -04:00
}
2018-10-21 22:24:47 -04:00
2019-08-12 18:13:58 +02:00
if ( $moduleName == 'commented' ) {
2019-12-16 00:28:31 +01:00
$this -> baseURL -> redirect ( 'network?order=comment' );
2019-07-23 20:01:45 -04:00
}
2018-10-21 22:24:47 -04:00
2019-08-12 18:13:58 +02:00
if ( $moduleName == 'liked' ) {
2019-12-16 00:28:31 +01:00
$this -> baseURL -> redirect ( 'network?order=comment' );
2019-07-23 20:01:45 -04:00
}
2018-10-21 22:24:47 -04:00
2019-08-12 18:13:58 +02:00
if ( $moduleName == 'activity' ) {
2019-12-16 00:28:31 +01:00
$this -> baseURL -> redirect ( 'network?conv=1' );
2019-07-23 20:01:45 -04:00
}
2018-10-21 22:24:47 -04:00
2019-08-12 18:13:58 +02:00
if (( $moduleName == 'status_messages' ) && ( $this -> args -> getCommand () == 'status_messages/new' )) {
2019-12-16 00:28:31 +01:00
$this -> baseURL -> redirect ( 'bookmarklet' );
2019-07-23 20:01:45 -04:00
}
2018-10-21 22:24:47 -04:00
2019-08-12 18:13:58 +02:00
if (( $moduleName == 'user' ) && ( $this -> args -> getCommand () == 'user/edit' )) {
2019-12-16 00:28:31 +01:00
$this -> baseURL -> redirect ( 'settings' );
2019-07-23 20:01:45 -04:00
}
2018-10-21 22:24:47 -04:00
2019-08-12 18:13:58 +02:00
if (( $moduleName == 'tag_followings' ) && ( $this -> args -> getCommand () == 'tag_followings/manage' )) {
2019-12-16 00:28:31 +01:00
$this -> baseURL -> redirect ( 'search' );
2019-07-23 20:01:45 -04:00
}
2018-10-21 22:24:47 -04:00
2021-07-25 05:08:29 +00:00
// Initialize module that can set the current theme in the init() method, either directly or via App->setProfileOwner
2019-12-30 20:02:09 +01:00
$page [ 'page_title' ] = $moduleName ;
2019-08-15 20:58:57 +02:00
2022-07-12 19:48:36 -04:00
// The "view" module is required to show the theme CSS
2024-11-06 07:21:50 +00:00
if ( ! $this -> mode -> isInstall () && ! $this -> mode -> has ( Mode :: MAINTENANCEDISABLED ) && $moduleName !== 'view' ) {
2021-11-20 15:38:03 +01:00
$module = $router -> getModule ( Maintenance :: class );
2021-03-10 09:40:42 -05:00
} else {
// determine the module class and save it to the module instance
// @todo there's an implicit dependency due SESSION::start(), so it has to be called here (yet)
2021-11-19 22:47:49 +01:00
$module = $router -> getModule ();
2021-03-10 09:40:42 -05:00
}
2018-10-21 22:24:47 -04:00
2023-07-08 21:01:48 -04:00
// Display can change depending on the requested language, so it shouldn't be cached whole
header ( 'Vary: Accept-Language' , false );
2021-11-28 13:44:42 +01:00
// Processes data from GET requests
2021-11-28 14:01:13 +01:00
$httpinput = $httpInput -> process ();
2024-11-19 08:40:48 +00:00
$input = array_merge ( $httpinput [ 'variables' ], $httpinput [ 'files' ], $request );
2021-11-28 13:44:42 +01:00
2022-12-26 21:17:32 +01:00
// Let the module run its internal process (init, get, post, ...)
2021-12-09 13:04:51 +00:00
$timestamp = microtime ( true );
2024-11-10 11:42:59 +00:00
$response = $module -> run ( $httpException , $input );
2021-12-09 13:04:51 +00:00
$this -> profiler -> set ( microtime ( true ) - $timestamp , 'content' );
2023-07-09 22:44:40 -04:00
// Wrapping HTML responses in the theme template
2021-11-22 00:07:09 +01:00
if ( $response -> getHeaderLine ( ICanCreateResponses :: X_HEADER ) === ICanCreateResponses :: TYPE_HTML ) {
2024-12-18 21:57:39 +00:00
$response = $page -> run ( $this -> appHelper , $this -> session , $this -> baseURL , $this -> args , $this -> mode , $response , $this -> l10n , $this -> profiler , $this -> config , $pconfig , $nav , $this -> session -> getLocalUserId ());
2021-11-21 21:52:36 +01:00
}
2023-07-09 22:44:40 -04:00
2024-03-16 12:54:17 +00:00
$this -> logger -> debug ( 'Request processed sucessfully' , [ 'response' => $response -> getStatusCode (), 'address' => $server [ 'REMOTE_ADDR' ] ? ? '' , 'request' => $requeststring , 'referer' => $server [ 'HTTP_REFERER' ] ? ? '' , 'user-agent' => $server [ 'HTTP_USER_AGENT' ] ? ? '' , 'duration' => number_format ( microtime ( true ) - $request_start , 3 )]);
2024-06-09 06:13:20 +00:00
$this -> logSlowCalls ( microtime ( true ) - $request_start , $response -> getStatusCode (), $requeststring , $server [ 'HTTP_USER_AGENT' ] ? ? '' );
2023-09-21 09:17:38 -04:00
System :: echoResponse ( $response );
2019-08-15 16:18:08 +02:00
} catch ( HTTPException $e ) {
2024-03-16 12:54:17 +00:00
$this -> logger -> debug ( 'Request processed with exception' , [ 'response' => $e -> getCode (), 'address' => $server [ 'REMOTE_ADDR' ] ? ? '' , 'request' => $requeststring , 'referer' => $server [ 'HTTP_REFERER' ] ? ? '' , 'user-agent' => $server [ 'HTTP_USER_AGENT' ] ? ? '' , 'duration' => number_format ( microtime ( true ) - $request_start , 3 )]);
2024-06-09 06:13:20 +00:00
$this -> logSlowCalls ( microtime ( true ) - $request_start , $e -> getCode (), $requeststring , $server [ 'HTTP_USER_AGENT' ] ? ? '' );
2022-12-26 21:17:32 +01:00
$httpException -> rawContent ( $e );
2019-05-01 21:33:33 -04:00
}
2022-06-10 18:49:03 +00:00
$page -> logRuntime ( $this -> config , 'runFrontend' );
2018-10-20 18:19:55 +02:00
}
2018-10-13 20:02:04 +02:00
2024-06-09 06:13:20 +00:00
/**
* Log slow page executions
*
* @ param float $duration
* @ param integer $code
* @ param string $request
* @ param string $agent
* @ return void
*/
private function logSlowCalls ( float $duration , int $code , string $request , string $agent )
{
$logfile = $this -> config -> get ( 'system' , 'page_execution_logfile' );
$loglimit = $this -> config -> get ( 'system' , 'page_execution_log_limit' );
if ( empty ( $logfile ) || empty ( $loglimit ) || ( $duration < $loglimit )) {
return ;
}
@ file_put_contents (
$logfile ,
DateTimeFormat :: utcNow () . " \t " . round ( $duration , 3 ) . " \t " .
$this -> requestId . " \t " . $code . " \t " .
$request . " \t " . $agent . " \n " ,
FILE_APPEND
);
}
2017-05-08 06:11:38 +00:00
}