use DOMDocument;
use DOMXPath;
use Exception;
+use FastRoute\RouteCollector;
+use Friendica\Core\Config\Cache\IConfigCache;
+use Friendica\Core\Config\Configuration;
+use Friendica\Core\Hook;
+use Friendica\Core\Theme;
use Friendica\Database\DBA;
+use Friendica\Model\Profile;
use Friendica\Network\HTTPException\InternalServerErrorException;
+use Friendica\Util\Config\ConfigFileLoader;
+use Friendica\Util\HTTPSignature;
+use Friendica\Util\Profiler;
+use Friendica\Util\Strings;
+use Psr\Log\LoggerInterface;
/**
*
*/
class App
{
- public $module_loaded = false;
public $module_class = null;
public $query_string = '';
- public $config = [];
public $page = [];
public $profile;
public $profile_uid;
public $identities;
public $is_mobile = false;
public $is_tablet = false;
- public $performance = [];
- public $callstack = [];
public $theme_info = [];
public $category;
// Allow themes to control internal parameters
private $mode;
/**
- * @var string The App base path
+ * @var App\Router
*/
- private $basePath;
+ private $router;
/**
* @var string The App URL path
*/
public $mobileDetect;
+ /**
+ * @var Configuration The config
+ */
+ private $config;
+
+ /**
+ * @var LoggerInterface The logger
+ */
+ private $logger;
+
+ /**
+ * @var Profiler The profiler of this app
+ */
+ private $profiler;
+
+ /**
+ * Returns the current config cache of this node
+ *
+ * @return IConfigCache
+ */
+ public function getConfigCache()
+ {
+ return $this->config->getCache();
+ }
+
+ /**
+ * The basepath of this app
+ *
+ * @return string
+ */
+ public function getBasePath()
+ {
+ return $this->config->get('system', 'basepath');
+ }
+
+ /**
+ * The Logger of this app
+ *
+ * @return LoggerInterface
+ */
+ public function getLogger()
+ {
+ return $this->logger;
+ }
+
+ /**
+ * The profiler of this app
+ *
+ * @return Profiler
+ */
+ public function getProfiler()
+ {
+ return $this->profiler;
+ }
+
+ /**
+ * Returns the Mode of the Application
+ *
+ * @return App\Mode The Application Mode
+ */
+ public function getMode()
+ {
+ return $this->mode;
+ }
+
+ public function getRouter()
+ {
+ return $this->router;
+ }
+
/**
* Register a stylesheet file path to be included in the <head> tag of every page.
* Inclusion is done in App->initHead().
* @see initHead()
*
* @param string $path
+ * @throws InternalServerErrorException
*/
public function registerStylesheet($path)
{
- $url = str_replace($this->getBasePath() . DIRECTORY_SEPARATOR, '', $path);
+ if (mb_strpos($path, $this->getBasePath() . DIRECTORY_SEPARATOR) === 0) {
+ $path = mb_substr($path, mb_strlen($this->getBasePath() . DIRECTORY_SEPARATOR));
+ }
- $this->stylesheets[] = trim($url, '/');
+ $this->stylesheets[] = trim($path, '/');
}
/**
* @see initFooter()
*
* @param string $path
+ * @throws InternalServerErrorException
*/
public function registerFooterScript($path)
{
$this->footerScripts[] = trim($url, '/');
}
- public $process_id;
public $queue;
private $scheme;
private $hostname;
/**
* @brief App constructor.
*
- * @param string $basePath Path to the app base folder
- * @param bool $isBackend Whether it is used for backend or frontend (Default true=backend)
+ * @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
+ * @param LoggerInterface $logger The current app logger
+ * @param Profiler $profiler The profiler of this application
+ * @param bool $isBackend Whether it is used for backend or frontend (Default true=backend)
*
* @throws Exception if the Basepath is not usable
*/
- public function __construct($basePath, $isBackend = true)
+ public function __construct(Configuration $config, App\Mode $mode, App\Router $router, LoggerInterface $logger, Profiler $profiler, $isBackend = true)
{
- if (!static::isDirectoryUsable($basePath, false)) {
- throw new Exception('Basepath ' . $basePath . ' isn\'t usable.');
- }
-
BaseObject::setApp($this);
- $this->basePath = rtrim($basePath, DIRECTORY_SEPARATOR);
+ $this->config = $config;
+ $this->mode = $mode;
+ $this->router = $router;
+ $this->profiler = $profiler;
+ $this->logger = $logger;
+
$this->checkBackend($isBackend);
$this->checkFriendicaApp();
- $this->performance['start'] = microtime(true);
- $this->performance['database'] = 0;
- $this->performance['database_write'] = 0;
- $this->performance['cache'] = 0;
- $this->performance['cache_write'] = 0;
- $this->performance['network'] = 0;
- $this->performance['file'] = 0;
- $this->performance['rendering'] = 0;
- $this->performance['parser'] = 0;
- $this->performance['marktime'] = 0;
- $this->performance['markstart'] = microtime(true);
-
- $this->callstack['database'] = [];
- $this->callstack['database_write'] = [];
- $this->callstack['cache'] = [];
- $this->callstack['cache_write'] = [];
- $this->callstack['network'] = [];
- $this->callstack['file'] = [];
- $this->callstack['rendering'] = [];
- $this->callstack['parser'] = [];
-
- $this->mode = new App\Mode($basePath);
+ $this->profiler->reset();
$this->reload();
Core\Renderer::registerTemplateEngine('Friendica\Render\FriendicaSmartyEngine');
}
- /**
- * Returns the Mode of the Application
- *
- * @return App\Mode The Application Mode
- *
- * @throws InternalServerErrorException when the mode isn't created
- */
- public function getMode()
- {
- if (empty($this->mode)) {
- throw new InternalServerErrorException('Mode of the Application is not defined');
- }
-
- return $this->mode;
- }
-
/**
* Reloads the whole app instance
*/
public function reload()
{
- // The order of the following calls is important to ensure proper initialization
- $this->loadConfigFiles();
-
- $this->loadDatabase();
+ $this->determineURLPath();
$this->getMode()->determine($this->getBasePath());
- $this->determineURLPath();
+ if ($this->getMode()->has(App\Mode::DBAVAILABLE)) {
+ $loader = new ConfigFileLoader($this->getBasePath(), $this->getMode());
+ $this->config->getCache()->load($loader->loadCoreConfig('addon'), true);
- Core\Config::load();
+ $this->profiler->update(
+ $this->config->get('system', 'profiler', false),
+ $this->config->get('rendertime', 'callstack', false));
- if ($this->getMode()->has(App\Mode::DBAVAILABLE)) {
Core\Hook::loadHooks();
-
- $this->loadAddonConfig();
+ $loader = new ConfigFileLoader($this->getBasePath(), $this->mode);
+ Core\Hook::callAll('load_config', $loader);
}
$this->loadDefaultTimezone();
Core\L10n::init();
-
- $this->process_id = Core\System::processID('log');
- }
-
- /**
- * Load the configuration files
- *
- * First loads the default value for all the configuration keys, then the legacy configuration files, then the
- * expected local.config.php
- */
- private function loadConfigFiles()
- {
- $this->loadConfigFile($this->getBasePath() . '/config/defaults.config.php');
- $this->loadConfigFile($this->getBasePath() . '/config/settings.config.php');
-
- // Legacy .htconfig.php support
- if (file_exists($this->getBasePath() . '/.htpreconfig.php')) {
- $a = $this;
- include $this->getBasePath() . '/.htpreconfig.php';
- }
-
- // Legacy .htconfig.php support
- if (file_exists($this->getBasePath() . '/.htconfig.php')) {
- $a = $this;
-
- include $this->getBasePath() . '/.htconfig.php';
-
- $this->setConfigValue('database', 'hostname', $db_host);
- $this->setConfigValue('database', 'username', $db_user);
- $this->setConfigValue('database', 'password', $db_pass);
- $this->setConfigValue('database', 'database', $db_data);
- if (isset($a->config['system']['db_charset'])) {
- $this->setConfigValue('database', 'charset', $a->config['system']['db_charset']);
- }
-
- unset($db_host, $db_user, $db_pass, $db_data);
-
- if (isset($default_timezone)) {
- $this->setConfigValue('system', 'default_timezone', $default_timezone);
- unset($default_timezone);
- }
-
- if (isset($pidfile)) {
- $this->setConfigValue('system', 'pidfile', $pidfile);
- unset($pidfile);
- }
-
- if (isset($lang)) {
- $this->setConfigValue('system', 'language', $lang);
- unset($lang);
- }
- }
-
- if (file_exists($this->getBasePath() . '/config/local.config.php')) {
- $this->loadConfigFile($this->getBasePath() . '/config/local.config.php', true);
- } elseif (file_exists($this->getBasePath() . '/config/local.ini.php')) {
- $this->loadINIConfigFile($this->getBasePath() . '/config/local.ini.php', true);
- }
- }
-
- /**
- * Tries to load the specified legacy configuration file into the App->config array.
- * Doesn't overwrite previously set values by default to prevent default config files to supersede DB Config.
- *
- * @deprecated since version 2018.12
- * @param string $filepath
- * @param bool $overwrite Force value overwrite if the config key already exists
- * @throws Exception
- */
- public function loadINIConfigFile($filepath, $overwrite = false)
- {
- if (!file_exists($filepath)) {
- throw new Exception('Error parsing non-existent INI config file ' . $filepath);
- }
-
- $contents = include($filepath);
-
- $config = parse_ini_string($contents, true, INI_SCANNER_TYPED);
-
- if ($config === false) {
- throw new Exception('Error parsing INI config file ' . $filepath);
- }
-
- $this->loadConfigArray($config, $overwrite);
- }
-
- /**
- * Tries to load the specified configuration file into the App->config array.
- * Doesn't overwrite previously set values by default to prevent default config files to supersede DB Config.
- *
- * The config format is PHP array and the template for configuration files is the following:
- *
- * <?php return [
- * 'section' => [
- * 'key' => 'value',
- * ],
- * ];
- *
- * @param string $filepath
- * @param bool $overwrite Force value overwrite if the config key already exists
- * @throws Exception
- */
- public function loadConfigFile($filepath, $overwrite = false)
- {
- if (!file_exists($filepath)) {
- throw new Exception('Error loading non-existent config file ' . $filepath);
- }
-
- $config = include($filepath);
-
- if (!is_array($config)) {
- throw new Exception('Error loading config file ' . $filepath);
- }
-
- $this->loadConfigArray($config, $overwrite);
- }
-
- /**
- * Loads addons configuration files
- *
- * First loads all activated addons default configuration through the load_config hook, then load the local.config.php
- * again to overwrite potential local addon configuration.
- */
- private function loadAddonConfig()
- {
- // Loads addons default config
- Core\Hook::callAll('load_config');
-
- // Load the local addon config file to overwritten default addon config values
- if (file_exists($this->getBasePath() . '/config/addon.config.php')) {
- $this->loadConfigFile($this->getBasePath() . '/config/addon.config.php', true);
- } elseif (file_exists($this->getBasePath() . '/config/addon.ini.php')) {
- $this->loadINIConfigFile($this->getBasePath() . '/config/addon.ini.php', true);
- }
- }
-
- /**
- * Tries to load the specified configuration array into the App->config array.
- * Doesn't overwrite previously set values by default to prevent default config files to supersede DB Config.
- *
- * @param array $config
- * @param bool $overwrite Force value overwrite if the config key already exists
- */
- private function loadConfigArray(array $config, $overwrite = false)
- {
- foreach ($config as $category => $values) {
- foreach ($values as $key => $value) {
- if ($overwrite) {
- $this->setConfigValue($category, $key, $value);
- } else {
- $this->setDefaultConfigValue($category, $key, $value);
- }
- }
- }
}
/**
*/
private function loadDefaultTimezone()
{
- if ($this->getConfigValue('system', 'default_timezone')) {
- $this->timezone = $this->getConfigValue('system', 'default_timezone');
+ if ($this->config->get('system', 'default_timezone')) {
+ $this->timezone = $this->config->get('system', 'default_timezone');
} else {
global $default_timezone;
$this->timezone = !empty($default_timezone) ? $default_timezone : 'UTC';
*/
private function determineURLPath()
{
+ /*
+ * The automatic path detection in this function is currently deactivated,
+ * see issue https://github.com/friendica/friendica/issues/6679
+ *
+ * The problem is that the function seems to be confused with some url.
+ * These then confuses the detection which changes the url path.
+ */
+
/* Relative script path to the web server root
* Not all of those $_SERVER properties can be present, so we do by inverse priority order
*/
+/*
$relative_script_path = '';
$relative_script_path = defaults($_SERVER, 'REDIRECT_URL' , $relative_script_path);
$relative_script_path = defaults($_SERVER, 'REDIRECT_URI' , $relative_script_path);
$relative_script_path = defaults($_SERVER, 'REDIRECT_SCRIPT_URL', $relative_script_path);
$relative_script_path = defaults($_SERVER, 'SCRIPT_URL' , $relative_script_path);
$relative_script_path = defaults($_SERVER, 'REQUEST_URI' , $relative_script_path);
-
- $this->urlPath = $this->getConfigValue('system', 'urlpath');
+*/
+ $this->urlPath = $this->config->get('system', 'urlpath');
/* $relative_script_path gives /relative/path/to/friendica/module/parameter
* QUERY_STRING gives pagename=module/parameter
*
* To get /relative/path/to/friendica we perform dirname() for as many levels as there are slashes in the QUERY_STRING
*/
+/*
if (!empty($relative_script_path)) {
// Module
if (!empty($_SERVER['QUERY_STRING'])) {
$this->urlPath = $path;
}
}
- }
-
- public function loadDatabase()
- {
- if (DBA::connected()) {
- return;
- }
-
- $db_host = $this->getConfigValue('database', 'hostname');
- $db_user = $this->getConfigValue('database', 'username');
- $db_pass = $this->getConfigValue('database', 'password');
- $db_data = $this->getConfigValue('database', 'database');
- $charset = $this->getConfigValue('database', 'charset');
-
- // Use environment variables for mysql if they are set beforehand
- if (!empty(getenv('MYSQL_HOST'))
- && !empty(getenv('MYSQL_USERNAME') || !empty(getenv('MYSQL_USER')))
- && getenv('MYSQL_PASSWORD') !== false
- && !empty(getenv('MYSQL_DATABASE')))
- {
- $db_host = getenv('MYSQL_HOST');
- if (!empty(getenv('MYSQL_PORT'))) {
- $db_host .= ':' . getenv('MYSQL_PORT');
- }
- if (!empty(getenv('MYSQL_USERNAME'))) {
- $db_user = getenv('MYSQL_USERNAME');
- } else {
- $db_user = getenv('MYSQL_USER');
- }
- $db_pass = (string) getenv('MYSQL_PASSWORD');
- $db_data = getenv('MYSQL_DATABASE');
- }
-
- $stamp1 = microtime(true);
-
- if (DBA::connect($db_host, $db_user, $db_pass, $db_data, $charset)) {
- // Loads DB_UPDATE_VERSION constant
- Database\DBStructure::definition(false);
- }
-
- unset($db_host, $db_user, $db_pass, $db_data, $charset);
-
- $this->saveTimestamp($stamp1, 'network');
- }
-
- /**
- * @brief Returns the base filesystem path of the App
- *
- * It first checks for the internal variable, then for DOCUMENT_ROOT and
- * finally for PWD
- *
- * @return string
- */
- public function getBasePath()
- {
- $basepath = $this->basePath;
-
- if (!$basepath) {
- $basepath = Core\Config::get('system', 'basepath');
- }
-
- if (!$basepath && !empty($_SERVER['DOCUMENT_ROOT'])) {
- $basepath = $_SERVER['DOCUMENT_ROOT'];
- }
-
- if (!$basepath && !empty($_SERVER['PWD'])) {
- $basepath = $_SERVER['PWD'];
- }
-
- return self::getRealPath($basepath);
- }
-
- /**
- * @brief Returns a normalized file path
- *
- * This is a wrapper for the "realpath" function.
- * That function cannot detect the real path when some folders aren't readable.
- * Since this could happen with some hosters we need to handle this.
- *
- * @param string $path The path that is about to be normalized
- * @return string normalized path - when possible
- */
- public static function getRealPath($path)
- {
- $normalized = realpath($path);
-
- if (!is_bool($normalized)) {
- return $normalized;
- } else {
- return $path;
- }
+*/
}
public function getScheme()
*
* @param bool $ssl Whether to append http or https under SSL_POLICY_SELFSIGN
* @return string Friendica server base URL
+ * @throws InternalServerErrorException
*/
public function getBaseURL($ssl = false)
{
$scheme = $this->scheme;
- if (Core\Config::get('system', 'ssl_policy') == SSL_POLICY_FULL) {
+ if ($this->config->get('system', 'ssl_policy') == SSL_POLICY_FULL) {
$scheme = 'https';
}
// Basically, we have $ssl = true on any links which can only be seen by a logged in user
// (and also the login link). Anything seen by an outsider will have it turned off.
- if (Core\Config::get('system', 'ssl_policy') == SSL_POLICY_SELFSIGN) {
+ if ($this->config->get('system', 'ssl_policy') == SSL_POLICY_SELFSIGN) {
if ($ssl) {
$scheme = 'https';
} else {
}
}
- if (Core\Config::get('config', 'hostname') != '') {
- $this->hostname = Core\Config::get('config', 'hostname');
+ if ($this->config->get('config', 'hostname') != '') {
+ $this->hostname = $this->config->get('config', 'hostname');
}
- return $scheme . '://' . $this->hostname . !empty($this->getURLPath() ? '/' . $this->getURLPath() : '' );
+ return $scheme . '://' . $this->hostname . (!empty($this->getURLPath()) ? '/' . $this->getURLPath() : '' );
}
/**
* Clears the baseurl cache to prevent inconsistencies
*
* @param string $url
+ * @throws InternalServerErrorException
*/
public function setBaseURL($url)
{
include $this->getBasePath() . '/.htpreconfig.php';
}
- if (Core\Config::get('config', 'hostname') != '') {
- $this->hostname = Core\Config::get('config', 'hostname');
+ if ($this->config->get('config', 'hostname') != '') {
+ $this->hostname = $this->config->get('config', 'hostname');
}
if (!isset($this->hostname) || ($this->hostname == '')) {
public function getHostName()
{
- if (Core\Config::get('config', 'hostname') != '') {
- $this->hostname = Core\Config::get('config', 'hostname');
+ if ($this->config->get('config', 'hostname') != '') {
+ $this->hostname = $this->config->get('config', 'hostname');
}
return $this->hostname;
$interval = 40000;
}
- // compose the page title from the sitename and the
- // current module called
- if (!$this->module == '') {
- $this->page['title'] = $this->config['sitename'] . ' (' . $this->module . ')';
- } else {
- $this->page['title'] = $this->config['sitename'];
+ // Default title: current module called
+ if (empty($this->page['title']) && $this->module) {
+ $this->page['title'] = ucfirst($this->module);
}
+ // Prepend the sitename to the page title
+ $this->page['title'] = $this->config->get('config', 'sitename', '') . (!empty($this->page['title']) ? ' | ' . $this->page['title'] : '');
+
if (!empty(Core\Renderer::$theme['stylesheet'])) {
$stylesheet = Core\Renderer::$theme['stylesheet'];
} else {
$this->registerStylesheet($stylesheet);
- $shortcut_icon = Core\Config::get('system', 'shortcut_icon');
+ $shortcut_icon = $this->config->get('system', 'shortcut_icon');
if ($shortcut_icon == '') {
$shortcut_icon = 'images/friendica-32.png';
}
- $touch_icon = Core\Config::get('system', 'touch_icon');
+ $touch_icon = $this->config->get('system', 'touch_icon');
if ($touch_icon == '') {
$touch_icon = 'images/friendica-128.png';
}
- Core\Addon::callHooks('head', $this->page['htmlhead']);
+ Core\Hook::callAll('head', $this->page['htmlhead']);
$tpl = Core\Renderer::getMarkupTemplate('head.tpl');
/* put the head template at the beginning of page['htmlhead']
'$local_user' => local_user(),
'$generator' => 'Friendica' . ' ' . FRIENDICA_VERSION,
'$delitem' => Core\L10n::t('Delete this item?'),
- '$showmore' => Core\L10n::t('show more'),
- '$showfewer' => Core\L10n::t('show fewer'),
'$update_interval' => $interval,
'$shortcut_icon' => $shortcut_icon,
'$touch_icon' => $touch_icon,
- '$block_public' => intval(Core\Config::get('system', 'block_public')),
+ '$block_public' => intval($this->config->get('system', 'block_public')),
'$stylesheets' => $this->stylesheets,
]) . $this->page['htmlhead'];
}
]);
}
- Core\Addon::callHooks('footer', $this->page['footer']);
+ Core\Hook::callAll('footer', $this->page['footer']);
$tpl = Core\Renderer::getMarkupTemplate('footer.tpl');
$this->page['footer'] = Core\Renderer::replaceMacros($tpl, [
* @param string $origURL
*
* @return string The cleaned url
+ * @throws InternalServerErrorException
*/
public function removeBaseURL($origURL)
{
}
}
- /**
- * Saves a timestamp for a value - f.e. a call
- * Necessary for profiling Friendica
- *
- * @param int $timestamp the Timestamp
- * @param string $value A value to profile
- */
- public function saveTimestamp($timestamp, $value)
- {
- if (!isset($this->config['system']['profiler']) || !$this->config['system']['profiler']) {
- return;
- }
-
- $duration = (float) (microtime(true) - $timestamp);
-
- if (!isset($this->performance[$value])) {
- // Prevent ugly E_NOTICE
- $this->performance[$value] = 0;
- }
-
- $this->performance[$value] += (float) $duration;
- $this->performance['marktime'] += (float) $duration;
-
- $callstack = Core\System::callstack();
-
- if (!isset($this->callstack[$value][$callstack])) {
- // Prevent ugly E_NOTICE
- $this->callstack[$value][$callstack] = 0;
- }
-
- $this->callstack[$value][$callstack] += (float) $duration;
- }
-
/**
* Returns the current UserAgent as a String
*
* @return string the UserAgent as a String
+ * @throws InternalServerErrorException
*/
public function getUserAgent()
{
'fetch',
'hcard',
'hostxrd',
+ 'manifest',
'nodeinfo',
'noscrape',
'p',
*
if ($this->is_backend()) {
$process = 'backend';
- $max_processes = Core\Config::get('system', 'max_processes_backend');
+ $max_processes = $this->config->get('system', 'max_processes_backend');
if (intval($max_processes) == 0) {
$max_processes = 5;
}
} else {
$process = 'frontend';
- $max_processes = Core\Config::get('system', 'max_processes_frontend');
+ $max_processes = $this->config->get('system', 'max_processes_frontend');
if (intval($max_processes) == 0) {
$max_processes = 20;
}
* @brief Checks if the minimal memory is reached
*
* @return bool Is the memory limit reached?
+ * @throws InternalServerErrorException
*/
public function isMinMemoryReached()
{
- $min_memory = Core\Config::get('system', 'min_memory', 0);
+ $min_memory = $this->config->get('system', 'min_memory', 0);
if ($min_memory == 0) {
return false;
}
* @brief Checks if the maximum load is reached
*
* @return bool Is the load reached?
+ * @throws InternalServerErrorException
*/
public function isMaxLoadReached()
{
if ($this->isBackend()) {
$process = 'backend';
- $maxsysload = intval(Core\Config::get('system', 'maxloadavg'));
+ $maxsysload = intval($this->config->get('system', 'maxloadavg'));
if ($maxsysload < 1) {
$maxsysload = 50;
}
} else {
$process = 'frontend';
- $maxsysload = intval(Core\Config::get('system', 'maxloadavg_frontend'));
+ $maxsysload = intval($this->config->get('system', 'maxloadavg_frontend'));
if ($maxsysload < 1) {
$maxsysload = 50;
}
*
* @param string $command The command to execute
* @param array $args Arguments to pass to the command ( [ 'key' => value, 'key2' => value2, ... ]
+ * @throws InternalServerErrorException
*/
public function proc_run($command, $args)
{
return;
}
- $cmdline = $this->getConfigValue('config', 'php_path', 'php') . ' ' . escapeshellarg($command);
+ $cmdline = $this->config->get('config', 'php_path', 'php') . ' ' . escapeshellarg($command);
foreach ($args as $key => $value) {
if (!is_null($value) && is_bool($value) && !$value) {
proc_close($resource);
}
- /**
- * @brief Returns the system user that is executing the script
- *
- * This mostly returns something like "www-data".
- *
- * @return string system username
- */
- private static function getSystemUser()
- {
- if (!function_exists('posix_getpwuid') || !function_exists('posix_geteuid')) {
- return '';
- }
-
- $processUser = posix_getpwuid(posix_geteuid());
- return $processUser['name'];
- }
-
- /**
- * @brief Checks if a given directory is usable for the system
- *
- * @return boolean the directory is usable
- */
- public static function isDirectoryUsable($directory, $check_writable = true)
- {
- if ($directory == '') {
- Core\Logger::log('Directory is empty. This shouldn\'t happen.', Core\Logger::DEBUG);
- return false;
- }
-
- if (!file_exists($directory)) {
- Core\Logger::log('Path "' . $directory . '" does not exist for user ' . self::getSystemUser(), Core\Logger::DEBUG);
- return false;
- }
-
- if (is_file($directory)) {
- Core\Logger::log('Path "' . $directory . '" is a file for user ' . self::getSystemUser(), Core\Logger::DEBUG);
- return false;
- }
-
- if (!is_dir($directory)) {
- Core\Logger::log('Path "' . $directory . '" is not a directory for user ' . self::getSystemUser(), Core\Logger::DEBUG);
- return false;
- }
-
- if ($check_writable && !is_writable($directory)) {
- Core\Logger::log('Path "' . $directory . '" is not writable for user ' . self::getSystemUser(), Core\Logger::DEBUG);
- return false;
- }
-
- return true;
- }
-
- /**
- * @param string $cat Config category
- * @param string $k Config key
- * @param mixed $default Default value if it isn't set
- *
- * @return string Returns the value of the Config entry
- */
- public function getConfigValue($cat, $k, $default = null)
- {
- $return = $default;
-
- if ($cat === 'config') {
- if (isset($this->config[$k])) {
- $return = $this->config[$k];
- }
- } else {
- if (isset($this->config[$cat][$k])) {
- $return = $this->config[$cat][$k];
- }
- }
-
- return $return;
- }
-
- /**
- * Sets a default value in the config cache. Ignores already existing keys.
- *
- * @param string $cat Config category
- * @param string $k Config key
- * @param mixed $v Default value to set
- */
- private function setDefaultConfigValue($cat, $k, $v)
- {
- if (!isset($this->config[$cat][$k])) {
- $this->setConfigValue($cat, $k, $v);
- }
- }
-
- /**
- * Sets a value in the config cache. Accepts raw output from the config table
- *
- * @param string $cat Config category
- * @param string $k Config key
- * @param mixed $v Value to set
- */
- public function setConfigValue($cat, $k, $v)
- {
- // Only arrays are serialized in database, so we have to unserialize sparingly
- $value = is_string($v) && preg_match("|^a:[0-9]+:{.*}$|s", $v) ? unserialize($v) : $v;
-
- if ($cat === 'config') {
- $this->config[$k] = $value;
- } else {
- if (!isset($this->config[$cat])) {
- $this->config[$cat] = [];
- }
-
- $this->config[$cat][$k] = $value;
- }
- }
-
- /**
- * Deletes a value from the config cache
- *
- * @param string $cat Config category
- * @param string $k Config key
- */
- public function deleteConfigValue($cat, $k)
- {
- if ($cat === 'config') {
- if (isset($this->config[$k])) {
- unset($this->config[$k]);
- }
- } else {
- if (isset($this->config[$cat][$k])) {
- unset($this->config[$cat][$k]);
- }
- }
- }
-
-
- /**
- * Retrieves a value from the user config cache
- *
- * @param int $uid User Id
- * @param string $cat Config category
- * @param string $k Config key
- * @param mixed $default Default value if key isn't set
- *
- * @return string The value of the config entry
- */
- public function getPConfigValue($uid, $cat, $k, $default = null)
- {
- $return = $default;
-
- if (isset($this->config[$uid][$cat][$k])) {
- $return = $this->config[$uid][$cat][$k];
- }
-
- return $return;
- }
-
- /**
- * Sets a value in the user config cache
- *
- * Accepts raw output from the pconfig table
- *
- * @param int $uid User Id
- * @param string $cat Config category
- * @param string $k Config key
- * @param mixed $v Value to set
- */
- public function setPConfigValue($uid, $cat, $k, $v)
- {
- // Only arrays are serialized in database, so we have to unserialize sparingly
- $value = is_string($v) && preg_match("|^a:[0-9]+:{.*}$|s", $v) ? unserialize($v) : $v;
-
- if (!isset($this->config[$uid]) || !is_array($this->config[$uid])) {
- $this->config[$uid] = [];
- }
-
- if (!isset($this->config[$uid][$cat]) || !is_array($this->config[$uid][$cat])) {
- $this->config[$uid][$cat] = [];
- }
-
- $this->config[$uid][$cat][$k] = $value;
- }
-
- /**
- * Deletes a value from the user config cache
- *
- * @param int $uid User Id
- * @param string $cat Config category
- * @param string $k Config key
- */
- public function deletePConfigValue($uid, $cat, $k)
- {
- if (isset($this->config[$uid][$cat][$k])) {
- unset($this->config[$uid][$cat][$k]);
- }
- }
-
/**
* Generates the site's default sender email address
*
* @return string
+ * @throws InternalServerErrorException
*/
public function getSenderEmailAddress()
{
- $sender_email = Core\Config::get('config', 'sender_email');
+ $sender_email = $this->config->get('config', 'sender_email');
if (empty($sender_email)) {
$hostname = $this->getHostName();
if (strpos($hostname, ':')) {
* Returns the current theme name.
*
* @return string the name of the current theme
+ * @throws InternalServerErrorException
*/
public function getCurrentTheme()
{
*/
private function computeCurrentTheme()
{
- $system_theme = Core\Config::get('system', 'theme');
+ $system_theme = $this->config->get('system', 'theme');
if (!$system_theme) {
throw new Exception(Core\L10n::t('No system theme config value set.'));
}
// Sane default
$this->currentTheme = $system_theme;
- $allowed_themes = explode(',', Core\Config::get('system', 'allowed_themes', $system_theme));
-
$page_theme = null;
// Find the theme that belongs to the user whose stuff we are looking at
if ($this->profile_uid && ($this->profile_uid != local_user())) {
// Specific mobile theme override
if (($this->is_mobile || $this->is_tablet) && Core\Session::get('show-mobile', true)) {
- $system_mobile_theme = Core\Config::get('system', 'mobile-theme');
+ $system_mobile_theme = $this->config->get('system', 'mobile-theme');
$user_mobile_theme = Core\Session::get('mobile-theme', $system_mobile_theme);
// --- means same mobile theme as desktop
$theme_name = $user_theme;
}
+ $theme_name = Strings::sanitizeFilePathItem($theme_name);
if ($theme_name
- && in_array($theme_name, $allowed_themes)
+ && in_array($theme_name, Theme::getAllowedList())
&& (file_exists('view/theme/' . $theme_name . '/style.css')
|| file_exists('view/theme/' . $theme_name . '/style.php'))
) {
* Provide a sane default if nothing is chosen or the specified theme does not exist.
*
* @return string
+ * @throws InternalServerErrorException
*/
public function getCurrentThemeStylesheetPath()
{
*/
public function checkURL()
{
- $url = Core\Config::get('system', 'url');
+ $url = $this->config->get('system', 'url');
// if the url isn't set or the stored url is radically different
// than the currently visited url, store the current value accordingly.
// We will only change the url to an ip address if there is no existing setting
if (empty($url) || (!Util\Strings::compareLink($url, $this->getBaseURL())) && (!preg_match("/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/", $this->getHostName()))) {
- Core\Config::set('system', 'url', $this->getBaseURL());
+ $this->config->set('system', 'url', $this->getBaseURL());
}
}
if (!$this->getMode()->isInstall()) {
// Force SSL redirection
- if (Core\Config::get('system', 'force_ssl') && ($this->getScheme() == "http")
- && intval(Core\Config::get('system', 'ssl_policy')) == SSL_POLICY_FULL
+ if ($this->config->get('system', 'force_ssl') && ($this->getScheme() == "http")
+ && intval($this->config->get('system', 'ssl_policy')) == SSL_POLICY_FULL
&& strpos($this->getBaseURL(), 'https://') === 0
&& $_SERVER['REQUEST_METHOD'] == 'GET') {
header('HTTP/1.1 302 Moved Temporarily');
}
Core\Session::init();
- Core\Addon::callHooks('init_1');
+ Core\Hook::callAll('init_1');
}
// Exclude the backend processes from the session management
if (!$this->isBackend()) {
$stamp1 = microtime(true);
session_start();
- $this->saveTimestamp($stamp1, 'parser');
+ $this->profiler->saveTimestamp($stamp1, 'parser', Core\System::callstack());
Core\L10n::setSessionVariable();
Core\L10n::setLangFromSession();
} else {
Core\Worker::executeIfIdle();
}
+ if ($this->getMode()->isNormal()) {
+ $requester = HTTPSignature::getSigner('', $_SERVER);
+ if (!empty($requester)) {
+ Profile::addVisitorCookieForHandle($requester);
+ }
+ }
+
// ZRL
if (!empty($_GET['zrl']) && $this->getMode()->isNormal()) {
$this->query_string = Model\Profile::stripZrls($this->query_string);
$this->module = 'maintenance';
} else {
$this->checkURL();
- Core\Update::check(false);
+ Core\Update::check($this->getBasePath(), false, $this->getMode());
Core\Addon::loadAddons();
Core\Hook::loadHooks();
}
$this->module = "login";
}
- $privateapps = Core\Config::get('config', 'private_addons', false);
- if (Core\Addon::isEnabled($this->module) && file_exists("addon/{$this->module}/{$this->module}.php")) {
+ /*
+ * 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();
+
+ Hook::callAll('route_collection', $this->router->getRouteCollector());
+
+ $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 {
if (function_exists($this->module . '_module')) {
LegacyModule::setModuleFile("addon/{$this->module}/{$this->module}.php");
$this->module_class = 'Friendica\\LegacyModule';
- $this->module_loaded = true;
}
}
}
- // Controller class routing
- if (! $this->module_loaded && class_exists('Friendica\\Module\\' . ucfirst($this->module))) {
+ // 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);
- $this->module_loaded = true;
}
- /* If not, next look for a 'standard' program module in the 'mod' directory
+ /* Finally, we look for a 'standard' program module in the 'mod' directory
* We emulate a Module class through the LegacyModule class
*/
- if (! $this->module_loaded && file_exists("mod/{$this->module}.php")) {
+ if (!$this->module_class && file_exists("mod/{$this->module}.php")) {
LegacyModule::setModuleFile("mod/{$this->module}.php");
$this->module_class = 'Friendica\\LegacyModule';
- $this->module_loaded = true;
}
/* The URL provided does not resolve to a valid module.
*
* Otherwise we are going to emit a 404 not found.
*/
- if (! $this->module_loaded) {
+ 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();
$content = '';
// Initialize module that can set the current theme in the init() method, either directly or via App->profile_uid
- if ($this->module_loaded) {
+ if ($this->module_class) {
$this->page['page_title'] = $this->module;
$placeholder = '';
- Core\Addon::callHooks($this->module . '_mod_init', $placeholder);
+ Core\Hook::callAll($this->module . '_mod_init', $placeholder);
call_user_func([$this->module_class, 'init']);
$func($this);
}
- if ($this->module_loaded) {
+ if ($this->module_class) {
if (! $this->error && $_SERVER['REQUEST_METHOD'] === 'POST') {
- Core\Addon::callHooks($this->module . '_mod_post', $_POST);
+ Core\Hook::callAll($this->module . '_mod_post', $_POST);
call_user_func([$this->module_class, 'post']);
}
if (! $this->error) {
- Core\Addon::callHooks($this->module . '_mod_afterpost', $placeholder);
+ Core\Hook::callAll($this->module . '_mod_afterpost', $placeholder);
call_user_func([$this->module_class, 'afterpost']);
}
if (! $this->error) {
$arr = ['content' => $content];
- Core\Addon::callHooks($this->module . '_mod_content', $arr);
+ Core\Hook::callAll($this->module . '_mod_content', $arr);
$content = $arr['content'];
$arr = ['content' => call_user_func([$this->module_class, 'content'])];
- Core\Addon::callHooks($this->module . '_mod_aftercontent', $arr);
+ Core\Hook::callAll($this->module . '_mod_aftercontent', $arr);
$content .= $arr['content'];
}
}
// initialise content region
if ($this->getMode()->isNormal()) {
- Core\Addon::callHooks('page_content_top', $this->page['content']);
+ Core\Hook::callAll('page_content_top', $this->page['content']);
}
$this->page['content'] .= $content;
}
// Report anything which needs to be communicated in the notification area (before the main body)
- Core\Addon::callHooks('page_end', $this->page['content']);
+ Core\Hook::callAll('page_end', $this->page['content']);
// Add the navigation (menu) template
if ($this->module != 'install' && $this->module != 'maintenance') {
// And then append it to the target
$target->documentElement->appendChild($item);
}
- }
- if (isset($_GET["mode"]) && ($_GET["mode"] == "raw")) {
- header("Content-type: text/html; charset=utf-8");
+ if ($_GET["mode"] == "raw") {
+ header("Content-type: text/html; charset=utf-8");
- echo substr($target->saveHTML(), 6, -8);
+ echo substr($target->saveHTML(), 6, -8);
- exit();
+ exit();
+ }
}
$page = $this->page;
header("X-Friendica-Version: " . FRIENDICA_VERSION);
header("Content-type: text/html; charset=utf-8");
- if (Core\Config::get('system', 'hsts') && (Core\Config::get('system', 'ssl_policy') == SSL_POLICY_FULL)) {
+ if ($this->config->get('system', 'hsts') && ($this->config->get('system', 'ssl_policy') == SSL_POLICY_FULL)) {
header("Strict-Transport-Security: max-age=31536000");
}
* 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
*/
public function redirect($toUrl)
{