X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FApp.php;h=8b2d50512b80b695f3b770db337e5e395e7e5791;hb=e9511b4f34189e3839f9c54f98835dc1374fc316;hp=fddfb0a6dadefcaeccd21dc9151c10994c527031;hpb=34748e8ffd6f2138556ea0df93d02231a8cdf4fa;p=friendica.git diff --git a/src/App.php b/src/App.php index fddfb0a6da..8b2d50512b 100644 --- a/src/App.php +++ b/src/App.php @@ -8,13 +8,14 @@ use Detection\MobileDetect; use DOMDocument; use DOMXPath; use Exception; -use Friendica\Core\Config\Cache\IConfigCache; +use Friendica\Core\Config\Cache\ConfigCache; use Friendica\Core\Config\Configuration; use Friendica\Core\Hook; use Friendica\Core\Theme; +use Friendica\Database\Database; use Friendica\Database\DBA; use Friendica\Model\Profile; -use Friendica\Network\HTTPException\InternalServerErrorException; +use Friendica\Network\HTTPException; use Friendica\Util\BaseURL; use Friendica\Util\Config\ConfigFileLoader; use Friendica\Util\HTTPSignature; @@ -49,7 +50,6 @@ class App public $page_contact; public $content; public $data = []; - public $error = false; public $cmd = ''; public $argv; public $argc; @@ -88,11 +88,6 @@ class App */ private $baseURL; - /** - * @var bool true, if the call is from the Friendica APP, otherwise false - */ - private $isFriendicaApp; - /** * @var bool true, if the call is from an backend node (f.e. worker) */ @@ -128,10 +123,15 @@ class App */ private $profiler; + /** + * @var Database The Friendica database connection + */ + private $database; + /** * Returns the current config cache of this node * - * @return IConfigCache + * @return ConfigCache */ public function getConfigCache() { @@ -155,7 +155,8 @@ class App */ public function getBasePath() { - return $this->config->get('system', 'basepath'); + // Don't use the basepath of the config table for basepath (it should always be the config-file one) + return $this->config->getCache()->get('system', 'basepath'); } /** @@ -198,6 +199,14 @@ class App return $this->router; } + /** + * @return Database + */ + public function getDatabase() + { + return $this->database; + } + /** * Register a stylesheet file path to be included in the tag of every page. * Inclusion is done in App->initHead(). @@ -237,6 +246,7 @@ class App /** * @brief App constructor. * + * @param Database $database The Friendica Database * @param Configuration $config The Configuration * @param App\Mode $mode The mode of this Friendica app * @param App\Router $router The router of this Friendica app @@ -247,10 +257,11 @@ class App * * @throws Exception if the Basepath is not usable */ - public function __construct(Configuration $config, App\Mode $mode, App\Router $router, BaseURL $baseURL, LoggerInterface $logger, Profiler $profiler, $isBackend = true) + public function __construct(Database $database, Configuration $config, App\Mode $mode, App\Router $router, BaseURL $baseURL, LoggerInterface $logger, Profiler $profiler, $isBackend = true) { BaseObject::setApp($this); + $this->database = $database; $this->config = $config; $this->mode = $mode; $this->router = $router; @@ -258,8 +269,6 @@ class App $this->profiler = $profiler; $this->logger = $logger; - $this->checkFriendicaApp(); - $this->profiler->reset(); $this->reload(); @@ -406,8 +415,6 @@ class App * @param bool $ssl Whether to append http or https under BaseURL::SSL_POLICY_SELFSIGN * * @return string Friendica server base URL - * - * @deprecated 2019.06 - use BaseURL->get($ssl) instead */ public function getBaseURL($ssl = false) { @@ -509,7 +516,6 @@ class App * being first */ $this->page['htmlhead'] = Core\Renderer::replaceMacros($tpl, [ - '$baseurl' => $this->getBaseURL(), '$local_user' => local_user(), '$generator' => 'Friendica' . ' ' . FRIENDICA_VERSION, '$delitem' => Core\L10n::t('Delete this item?'), @@ -562,7 +568,6 @@ class App $tpl = Core\Renderer::getMarkupTemplate('footer.tpl'); $this->page['footer'] = Core\Renderer::replaceMacros($tpl, [ - '$baseurl' => $this->getBaseURL(), '$footerScripts' => $this->footerScripts, ]) . $this->page['footer']; } @@ -573,7 +578,7 @@ class App * @param string $origURL * * @return string The cleaned url - * @throws InternalServerErrorException + * @throws HTTPException\InternalServerErrorException */ public function removeBaseURL($origURL) { @@ -594,7 +599,7 @@ class App * Returns the current UserAgent as a String * * @return string the UserAgent as a String - * @throws InternalServerErrorException + * @throws HTTPException\InternalServerErrorException */ public function getUserAgent() { @@ -606,28 +611,6 @@ class App $this->getBaseURL(); } - /** - * Checks, if the call is from the Friendica App - * - * Reason: - * The friendica client has problems with the GUID in the notify. this is some workaround - */ - private function checkFriendicaApp() - { - // Friendica-Client - $this->isFriendicaApp = isset($_SERVER['HTTP_USER_AGENT']) && $_SERVER['HTTP_USER_AGENT'] == 'Apache-HttpClient/UNAVAILABLE (java 1.4)'; - } - - /** - * Is the call via the Friendica app? (not a "normale" call) - * - * @return bool true if it's from the Friendica app - */ - public function isFriendicaApp() - { - return $this->isFriendicaApp; - } - /** * @brief Checks if the site is called via a backend process * @@ -642,13 +625,18 @@ class App '_well_known', 'api', 'dfrn_notify', + 'feed', 'fetch', + 'followers', + 'following', 'hcard', 'hostxrd', + 'inbox', 'manifest', 'nodeinfo', 'noscrape', - 'p', + 'objects', + 'outbox', 'poco', 'post', 'proxy', @@ -719,7 +707,7 @@ class App * @brief Checks if the minimal memory is reached * * @return bool Is the memory limit reached? - * @throws InternalServerErrorException + * @throws HTTPException\InternalServerErrorException */ public function isMinMemoryReached() { @@ -764,7 +752,7 @@ class App * @brief Checks if the maximum load is reached * * @return bool Is the load reached? - * @throws InternalServerErrorException + * @throws HTTPException\InternalServerErrorException */ public function isMaxLoadReached() { @@ -797,7 +785,7 @@ class App * * @param string $command The command to execute * @param array $args Arguments to pass to the command ( [ 'key' => value, 'key2' => value2, ... ] - * @throws InternalServerErrorException + * @throws HTTPException\InternalServerErrorException */ public function proc_run($command, $args) { @@ -838,7 +826,7 @@ class App * Generates the site's default sender email address * * @return string - * @throws InternalServerErrorException + * @throws HTTPException\InternalServerErrorException */ public function getSenderEmailAddress() { @@ -859,7 +847,7 @@ class App * Returns the current theme name. * * @return string the name of the current theme - * @throws InternalServerErrorException + * @throws HTTPException\InternalServerErrorException */ public function getCurrentTheme() { @@ -940,7 +928,7 @@ class App * Provide a sane default if nothing is chosen or the specified theme does not exist. * * @return string - * @throws InternalServerErrorException + * @throws HTTPException\InternalServerErrorException */ public function getCurrentThemeStylesheetPath() { @@ -1006,7 +994,9 @@ class App { // Missing DB connection: ERROR if ($this->getMode()->has(App\Mode::LOCALCONFIGPRESENT) && !$this->getMode()->has(App\Mode::DBAVAILABLE)) { - Core\System::httpExit(500, ['title' => 'Error 500 - Internal Server Error', 'description' => 'Apologies but the website is unavailable at the moment.']); + Module\Special\HTTPException::rawContent( + new HTTPException\InternalServerErrorException('Apologies but the website is unavailable at the moment.') + ); } // Max Load Average reached: ERROR @@ -1014,11 +1004,9 @@ class App header('Retry-After: 120'); header('Refresh: 120; url=' . $this->getBaseURL() . "/" . $this->query_string); - Core\System::httpExit(503, ['title' => 'Error 503 - Service Temporarily Unavailable', 'description' => 'Core\System is currently overloaded. Please try again later.']); - } - - if (strstr($this->query_string, '.well-known/host-meta') && ($this->query_string != '.well-known/host-meta')) { - Core\System::httpExit(404); + Module\Special\HTTPException::rawContent( + new HTTPException\ServiceUnavailableException('The node is currently overloaded. Please try again later.') + ); } if (!$this->getMode()->isInstall()) { @@ -1060,16 +1048,19 @@ class App // Valid profile links contain a path with "/profile/" and no query parameters if ((parse_url($_GET['zrl'], PHP_URL_QUERY) == "") && strstr(parse_url($_GET['zrl'], PHP_URL_PATH), "/profile/")) { - if (defaults($_SESSION, "visitor_home", "") != $_GET["zrl"]) { - $_SESSION['my_url'] = $_GET['zrl']; - $_SESSION['authenticated'] = 0; + if (Core\Session::get('visitor_home') != $_GET["zrl"]) { + Core\Session::set('my_url', $_GET['zrl']); + Core\Session::set('authenticated', 0); } + Model\Profile::zrlInit($this); } else { // Someone came with an invalid parameter, maybe as a DDoS attempt // We simply stop processing here Core\Logger::log("Invalid ZRL parameter " . $_GET['zrl'], Core\Logger::DEBUG); - Core\System::httpExit(403, ['title' => '403 Forbidden']); + Module\Special\HTTPException::rawContent( + new HTTPException\ForbiddenException() + ); } } } @@ -1086,9 +1077,9 @@ class App header('X-Account-Management-Status: none'); } - $_SESSION['sysmsg'] = defaults($_SESSION, 'sysmsg' , []); - $_SESSION['sysmsg_info'] = defaults($_SESSION, 'sysmsg_info' , []); - $_SESSION['last_updated'] = defaults($_SESSION, 'last_updated', []); + $_SESSION['sysmsg'] = Core\Session::get('sysmsg', []); + $_SESSION['sysmsg_info'] = Core\Session::get('sysmsg_info', []); + $_SESSION['last_updated'] = Core\Session::get('last_updated', []); /* * check_config() is responsible for running update scripts. These automatically @@ -1098,10 +1089,10 @@ class App // in install mode, any url loads install module // but we need "view" module for stylesheet - if ($this->getMode()->isInstall() && $this->module != 'view') { - $this->module = 'install'; - } elseif (!$this->getMode()->has(App\Mode::MAINTENANCEDISABLED) && $this->module != 'view') { - $this->module = 'maintenance'; + if ($this->getMode()->isInstall() && $this->module !== 'install') { + $this->internalRedirect('install'); + } elseif (!$this->getMode()->isInstall() && !$this->getMode()->has(App\Mode::MAINTENANCEDISABLED) && $this->module !== 'maintenance') { + $this->internalRedirect('maintenance'); } else { $this->checkURL(); Core\Update::check($this->getBasePath(), false, $this->getMode()); @@ -1122,123 +1113,110 @@ class App 'title' => '' ]; - if (strlen($this->module)) { - // Compatibility with the Android Diaspora client - if ($this->module == 'stream') { - $this->internalRedirect('network?f=&order=post'); - } + // Compatibility with the Android Diaspora client + if ($this->module == 'stream') { + $this->internalRedirect('network?order=post'); + } - if ($this->module == 'conversations') { - $this->internalRedirect('message'); - } + if ($this->module == 'conversations') { + $this->internalRedirect('message'); + } - if ($this->module == 'commented') { - $this->internalRedirect('network?f=&order=comment'); - } + if ($this->module == 'commented') { + $this->internalRedirect('network?order=comment'); + } - if ($this->module == 'liked') { - $this->internalRedirect('network?f=&order=comment'); - } + if ($this->module == 'liked') { + $this->internalRedirect('network?order=comment'); + } - if ($this->module == 'activity') { - $this->internalRedirect('network/?f=&conv=1'); - } + if ($this->module == 'activity') { + $this->internalRedirect('network?conv=1'); + } - if (($this->module == 'status_messages') && ($this->cmd == 'status_messages/new')) { - $this->internalRedirect('bookmarklet'); - } + if (($this->module == 'status_messages') && ($this->cmd == 'status_messages/new')) { + $this->internalRedirect('bookmarklet'); + } - if (($this->module == 'user') && ($this->cmd == 'user/edit')) { - $this->internalRedirect('settings'); - } + if (($this->module == 'user') && ($this->cmd == 'user/edit')) { + $this->internalRedirect('settings'); + } - if (($this->module == 'tag_followings') && ($this->cmd == 'tag_followings/manage')) { - $this->internalRedirect('search'); - } + if (($this->module == 'tag_followings') && ($this->cmd == 'tag_followings/manage')) { + $this->internalRedirect('search'); + } - // Compatibility with the Firefox App - if (($this->module == "users") && ($this->cmd == "users/sign_in")) { - $this->module = "login"; - } + // Compatibility with the Firefox App + if (($this->module == "users") && ($this->cmd == "users/sign_in")) { + $this->module = "login"; + } - /* - * ROUTING - * - * From the request URL, routing consists of obtaining the name of a BaseModule-extending class of which the - * post() and/or content() static methods can be respectively called to produce a data change or an output. - */ + /* + * ROUTING + * + * From the request URL, routing consists of obtaining the name of a BaseModule-extending class of which the + * post() and/or content() static methods can be respectively called to produce a data change or an output. + */ - // First we try explicit routes defined in App\Router - $this->router->collectRoutes(); + // First we try explicit routes defined in App\Router + $this->router->collectRoutes(); - $data = $this->router->getRouteCollector(); - Hook::callAll('route_collection', $data); + $data = $this->router->getRouteCollector(); + Hook::callAll('route_collection', $data); - $this->module_class = $this->router->getModuleClass($this->cmd); + $this->module_class = $this->router->getModuleClass($this->cmd); - // Then we try addon-provided modules that we wrap in the LegacyModule class - if (!$this->module_class && Core\Addon::isEnabled($this->module) && file_exists("addon/{$this->module}/{$this->module}.php")) { - //Check if module is an app and if public access to apps is allowed or not - $privateapps = $this->config->get('config', 'private_addons', false); - if ((!local_user()) && Core\Hook::isAddonApp($this->module) && $privateapps) { - info(Core\L10n::t("You must be logged in to use addons. ")); - } else { - include_once "addon/{$this->module}/{$this->module}.php"; - if (function_exists($this->module . '_module')) { - LegacyModule::setModuleFile("addon/{$this->module}/{$this->module}.php"); - $this->module_class = 'Friendica\\LegacyModule'; - } + // Then we try addon-provided modules that we wrap in the LegacyModule class + if (!$this->module_class && Core\Addon::isEnabled($this->module) && file_exists("addon/{$this->module}/{$this->module}.php")) { + //Check if module is an app and if public access to apps is allowed or not + $privateapps = $this->config->get('config', 'private_addons', false); + if ((!local_user()) && Core\Hook::isAddonApp($this->module) && $privateapps) { + info(Core\L10n::t("You must be logged in to use addons. ")); + } else { + include_once "addon/{$this->module}/{$this->module}.php"; + if (function_exists($this->module . '_module')) { + LegacyModule::setModuleFile("addon/{$this->module}/{$this->module}.php"); + $this->module_class = LegacyModule::class; } } + } - // Then we try name-matching a Friendica\Module class - if (!$this->module_class && class_exists('Friendica\\Module\\' . ucfirst($this->module))) { - $this->module_class = 'Friendica\\Module\\' . ucfirst($this->module); - } + /* Finally, we look for a 'standard' program module in the 'mod' directory + * We emulate a Module class through the LegacyModule class + */ + if (!$this->module_class && file_exists("mod/{$this->module}.php")) { + LegacyModule::setModuleFile("mod/{$this->module}.php"); + $this->module_class = LegacyModule::class; + } - /* Finally, we look for a 'standard' program module in the 'mod' directory - * We emulate a Module class through the LegacyModule class - */ - if (!$this->module_class && file_exists("mod/{$this->module}.php")) { - LegacyModule::setModuleFile("mod/{$this->module}.php"); - $this->module_class = 'Friendica\\LegacyModule'; + /* The URL provided does not resolve to a valid module. + * + * On Dreamhost sites, quite often things go wrong for no apparent reason and they send us to '/internal_error.html'. + * We don't like doing this, but as it occasionally accounts for 10-20% or more of all site traffic - + * we are going to trap this and redirect back to the requested page. As long as you don't have a critical error on your page + * this will often succeed and eventually do the right thing. + * + * Otherwise we are going to emit a 404 not found. + */ + if (!$this->module_class) { + // Stupid browser tried to pre-fetch our Javascript img template. Don't log the event or return anything - just quietly exit. + if (!empty($_SERVER['QUERY_STRING']) && preg_match('/{[0-9]}/', $_SERVER['QUERY_STRING']) !== 0) { + exit(); } - /* The URL provided does not resolve to a valid module. - * - * On Dreamhost sites, quite often things go wrong for no apparent reason and they send us to '/internal_error.html'. - * We don't like doing this, but as it occasionally accounts for 10-20% or more of all site traffic - - * we are going to trap this and redirect back to the requested page. As long as you don't have a critical error on your page - * this will often succeed and eventually do the right thing. - * - * Otherwise we are going to emit a 404 not found. - */ - if (!$this->module_class) { - // Stupid browser tried to pre-fetch our Javascript img template. Don't log the event or return anything - just quietly exit. - if (!empty($_SERVER['QUERY_STRING']) && preg_match('/{[0-9]}/', $_SERVER['QUERY_STRING']) !== 0) { - exit(); - } - - if (!empty($_SERVER['QUERY_STRING']) && ($_SERVER['QUERY_STRING'] === 'q=internal_error.html') && isset($dreamhost_error_hack)) { - Core\Logger::log('index.php: dreamhost_error_hack invoked. Original URI =' . $_SERVER['REQUEST_URI']); - $this->internalRedirect($_SERVER['REQUEST_URI']); - } + if (!empty($_SERVER['QUERY_STRING']) && ($_SERVER['QUERY_STRING'] === 'q=internal_error.html') && isset($dreamhost_error_hack)) { + Core\Logger::log('index.php: dreamhost_error_hack invoked. Original URI =' . $_SERVER['REQUEST_URI']); + $this->internalRedirect($_SERVER['REQUEST_URI']); + } - Core\Logger::log('index.php: page not found: ' . $_SERVER['REQUEST_URI'] . ' ADDRESS: ' . $_SERVER['REMOTE_ADDR'] . ' QUERY: ' . $_SERVER['QUERY_STRING'], Core\Logger::DEBUG); + Core\Logger::log('index.php: page not found: ' . $_SERVER['REQUEST_URI'] . ' ADDRESS: ' . $_SERVER['REMOTE_ADDR'] . ' QUERY: ' . $_SERVER['QUERY_STRING'], Core\Logger::DEBUG); - header($_SERVER["SERVER_PROTOCOL"] . ' 404 ' . Core\L10n::t('Not Found')); - $tpl = Core\Renderer::getMarkupTemplate("404.tpl"); - $this->page['content'] = Core\Renderer::replaceMacros($tpl, [ - '$message' => Core\L10n::t('Page not found.') - ]); - } + $this->module_class = Module\PageNotFound::class; } - $content = ''; - // Initialize module that can set the current theme in the init() method, either directly or via App->profile_uid - if ($this->module_class) { - $this->page['page_title'] = $this->module; + $this->page['page_title'] = $this->module; + try { $placeholder = ''; Core\Hook::callAll($this->module . '_mod_init', $placeholder); @@ -1247,41 +1225,41 @@ class App // "rawContent" is especially meant for technical endpoints. // This endpoint doesn't need any theme initialization or other comparable stuff. - if (!$this->error) { - call_user_func([$this->module_class, 'rawContent']); - } - } + call_user_func([$this->module_class, 'rawContent']); - // Load current theme info after module has been initialized as theme could have been set in module - $theme_info_file = 'view/theme/' . $this->getCurrentTheme() . '/theme.php'; - if (file_exists($theme_info_file)) { - require_once $theme_info_file; - } + // Load current theme info after module has been initialized as theme could have been set in module + $theme_info_file = 'view/theme/' . $this->getCurrentTheme() . '/theme.php'; + if (file_exists($theme_info_file)) { + require_once $theme_info_file; + } - if (function_exists(str_replace('-', '_', $this->getCurrentTheme()) . '_init')) { - $func = str_replace('-', '_', $this->getCurrentTheme()) . '_init'; - $func($this); - } + if (function_exists(str_replace('-', '_', $this->getCurrentTheme()) . '_init')) { + $func = str_replace('-', '_', $this->getCurrentTheme()) . '_init'; + $func($this); + } - if ($this->module_class) { - if (! $this->error && $_SERVER['REQUEST_METHOD'] === 'POST') { + if ($_SERVER['REQUEST_METHOD'] === 'POST') { Core\Hook::callAll($this->module . '_mod_post', $_POST); call_user_func([$this->module_class, 'post']); } - if (! $this->error) { - Core\Hook::callAll($this->module . '_mod_afterpost', $placeholder); - call_user_func([$this->module_class, 'afterpost']); - } + Core\Hook::callAll($this->module . '_mod_afterpost', $placeholder); + call_user_func([$this->module_class, 'afterpost']); + } catch(HTTPException $e) { + Module\Special\HTTPException::rawContent($e); + } - if (! $this->error) { - $arr = ['content' => $content]; - Core\Hook::callAll($this->module . '_mod_content', $arr); - $content = $arr['content']; - $arr = ['content' => call_user_func([$this->module_class, 'content'])]; - Core\Hook::callAll($this->module . '_mod_aftercontent', $arr); - $content .= $arr['content']; - } + $content = ''; + + try { + $arr = ['content' => $content]; + Core\Hook::callAll($this->module . '_mod_content', $arr); + $content = $arr['content']; + $arr = ['content' => call_user_func([$this->module_class, 'content'])]; + Core\Hook::callAll($this->module . '_mod_aftercontent', $arr); + $content .= $arr['content']; + } catch(HTTPException $e) { + $content = Module\Special\HTTPException::content($e); } // initialise content region @@ -1305,13 +1283,6 @@ class App */ $this->initFooter(); - /* now that we've been through the module content, see if the page reported - * a permission problem and if so, a 403 response would seem to be in order. - */ - if (stristr(implode("", $_SESSION['sysmsg']), Core\L10n::t('Permission denied'))) { - header($_SERVER["SERVER_PROTOCOL"] . ' 403 ' . Core\L10n::t('Permission denied.')); - } - if (!$this->isAjax()) { Core\Hook::callAll('page_end', $this->page['content']); } @@ -1403,12 +1374,12 @@ class App * @param string $toUrl The destination URL (Default is empty, which is the default page of the Friendica node) * @param bool $ssl if true, base URL will try to get called with https:// (works just for relative paths) * - * @throws InternalServerErrorException In Case the given URL is not relative to the Friendica node + * @throws HTTPException\InternalServerErrorException In Case the given URL is not relative to the Friendica node */ public function internalRedirect($toUrl = '', $ssl = false) { if (!empty(parse_url($toUrl, PHP_URL_SCHEME))) { - throw new InternalServerErrorException("'$toUrl is not a relative path, please use System::externalRedirectTo"); + throw new HTTPException\InternalServerErrorException("'$toUrl is not a relative path, please use System::externalRedirectTo"); } $redirectTo = $this->getBaseURL($ssl) . '/' . ltrim($toUrl, '/'); @@ -1420,7 +1391,7 @@ class App * Should only be used if it isn't clear if the URL is either internal or external * * @param string $toUrl The target URL - * @throws InternalServerErrorException + * @throws HTTPException\InternalServerErrorException */ public function redirect($toUrl) {