]> git.mxchange.org Git - friendica.git/blobdiff - boot.php
Merge remote-tracking branch 'upstream/develop' into more-temp-stuff
[friendica.git] / boot.php
index bbfd671955215a1e5ebb4847a98654efdbaea41b..b8e92670708836c6d5b9c376185120031e914161 100644 (file)
--- a/boot.php
+++ b/boot.php
@@ -19,6 +19,8 @@
 
 require_once('include/autoloader.php');
 
+use \Friendica\Core\Config;
+
 require_once('include/config.php');
 require_once('include/network.php');
 require_once('include/plugin.php');
@@ -36,9 +38,9 @@ require_once('include/dbstructure.php');
 
 define ( 'FRIENDICA_PLATFORM',     'Friendica');
 define ( 'FRIENDICA_CODENAME',     'Asparagus');
-define ( 'FRIENDICA_VERSION',      '3.5-dev' );
+define ( 'FRIENDICA_VERSION',      '3.5.1-dev' );
 define ( 'DFRN_PROTOCOL_VERSION',  '2.23'    );
-define ( 'DB_UPDATE_VERSION',      1200      );
+define ( 'DB_UPDATE_VERSION',      1214      );
 
 /**
  * @brief Constant with a HTML line break.
@@ -53,7 +55,7 @@ define ( 'ATOM_TIME',              'Y-m-d\TH:i:s\Z' );
 
 /**
  * @brief Image storage quality.
- * 
+ *
  * Lower numbers save space at cost of image detail.
  * For ease of upgrade, please do not change here. Change jpeg quality with
  * $a->config['system']['jpeg_quality'] = n;
@@ -95,7 +97,7 @@ define ( 'DEFAULT_DB_ENGINE',  'MyISAM'  );
 
 /**
  * @name SSL Policy
- * 
+ *
  * SSL redirection policies
  * @{
  */
@@ -106,7 +108,7 @@ define ( 'SSL_POLICY_SELFSIGN',     2 );
 
 /**
  * @name Logger
- * 
+ *
  * log levels
  * @{
  */
@@ -119,7 +121,7 @@ define ( 'LOGGER_ALL',             4 );
 
 /**
  * @name Cache
- * 
+ *
  * Cache levels
  * @{
  */
@@ -127,11 +129,15 @@ define ( 'CACHE_MONTH',            0 );
 define ( 'CACHE_WEEK',             1 );
 define ( 'CACHE_DAY',              2 );
 define ( 'CACHE_HOUR',             3 );
+define ( 'CACHE_HALF_HOUR',        4 );
+define ( 'CACHE_QUARTER_HOUR',     5 );
+define ( 'CACHE_FIVE_MINUTES',     6 );
+define ( 'CACHE_MINUTE',           7 );
 /* @}*/
 
 /**
  * @name Register
- * 
+ *
  * Registration policies
  * @{
  */
@@ -142,7 +148,7 @@ define ( 'REGISTER_OPEN',          2 );
 
 /**
  * @name Contact_is
- * 
+ *
  * Relationship types
  * @{
  */
@@ -153,7 +159,7 @@ define ( 'CONTACT_IS_FRIEND',   3);
 
 /**
  * @name Update
- * 
+ *
  * DB update return values
  * @{
  */
@@ -181,9 +187,31 @@ define ( 'PAGE_BLOG',              4 );
 define ( 'PAGE_PRVGROUP',          5 );
 /** @}*/
 
+/**
+ * @name account types
+ *
+ * ACCOUNT_TYPE_PERSON - the account belongs to a person
+ *     Associated page types: PAGE_NORMAL, PAGE_SOAPBOX, PAGE_FREELOVE
+ *
+ * ACCOUNT_TYPE_ORGANISATION - the account belongs to an organisation
+ *     Associated page type: PAGE_SOAPBOX
+ *
+ * ACCOUNT_TYPE_NEWS - the account is a news reflector
+ *     Associated page type: PAGE_SOAPBOX
+ *
+ * ACCOUNT_TYPE_COMMUNITY - the account is community forum
+ *     Associated page types: PAGE_COMMUNITY, PAGE_PRVGROUP
+ * @{
+ */
+define ( 'ACCOUNT_TYPE_PERSON',      0 );
+define ( 'ACCOUNT_TYPE_ORGANISATION',1 );
+define ( 'ACCOUNT_TYPE_NEWS',        2 );
+define ( 'ACCOUNT_TYPE_COMMUNITY',   3 );
+/** @}*/
+
 /**
  * @name CP
- * 
+ *
  * Type of the community page
  * @{
  */
@@ -194,7 +222,7 @@ define ( 'CP_GLOBAL_COMMUNITY',    1 );
 
 /**
  * @name Network
- * 
+ *
  * Network and protocol family types
  * @{
  */
@@ -217,6 +245,7 @@ define ( 'NETWORK_STATUSNET',        'stac');    // Statusnet connector
 define ( 'NETWORK_APPNET',           'apdn');    // app.net
 define ( 'NETWORK_NEWS',             'nntp');    // Network News Transfer Protocol
 define ( 'NETWORK_ICALENDAR',        'ical');    // iCalendar
+define ( 'NETWORK_PNUT',             'pnut');    // pnut.io
 define ( 'NETWORK_PHANTOM',          'unkn');    // Place holder
 /** @}*/
 
@@ -246,6 +275,7 @@ $netgroup_ids = array(
        NETWORK_APPNET    => (-17),
        NETWORK_NEWS      => (-18),
        NETWORK_ICALENDAR => (-19),
+       NETWORK_PNUT      => (-20),
 
        NETWORK_PHANTOM  => (-127),
 );
@@ -266,7 +296,7 @@ define ( 'ZCURL_TIMEOUT' , (-1));
 
 /**
  * @name Notify
- * 
+ *
  * Email notification options
  * @{
  */
@@ -288,7 +318,7 @@ define ( 'NOTIFY_SYSTEM',   0x8000 );
 
 /**
  * @name Term
- * 
+ *
  * Tag/term types
  * @{
  */
@@ -308,7 +338,7 @@ define ( 'TERM_OBJ_PHOTO', 2 );
 
 /**
  * @name Namespaces
- * 
+ *
  * Various namespaces we may need to parse
  * @{
  */
@@ -331,7 +361,7 @@ define ( 'NAMESPACE_ATOM1',           'http://www.w3.org/2005/Atom' );
 
 /**
  * @name Activity
- * 
+ *
  * Activity stream defines
  * @{
  */
@@ -377,7 +407,7 @@ define ( 'ACTIVITY_OBJ_QUESTION', 'http://activityschema.org/object/question' );
 
 /**
  * @name Gravity
- * 
+ *
  * Item weight for query ordering
  * @{
  */
@@ -386,6 +416,31 @@ define ( 'GRAVITY_LIKE',         3);
 define ( 'GRAVITY_COMMENT',      6);
 /* @}*/
 
+/**
+ * @name Priority
+ *
+ * Process priority for the worker
+ * @{
+ */
+define('PRIORITY_UNDEFINED',  0);
+define('PRIORITY_CRITICAL',  10);
+define('PRIORITY_HIGH',      20);
+define('PRIORITY_MEDIUM',    30);
+define('PRIORITY_LOW',       40);
+define('PRIORITY_NEGLIGIBLE',50);
+/* @}*/
+
+/**
+ * @name Social Relay settings
+ *
+ * See here: https://github.com/jaywink/social-relay
+ * and here: https://wiki.diasporafoundation.org/Relay_servers_for_public_posts
+ * @{
+ */
+define('SR_SCOPE_NONE', '');
+define('SR_SCOPE_ALL',  'all');
+define('SR_SCOPE_TAGS', 'tags');
+/* @}*/
 
 // Normally this constant is defined - but not if "pcntl" isn't installed
 if (!defined("SIGTERM"))
@@ -430,9 +485,9 @@ function startup() {
 /**
  *
  * class: App
- * 
+ *
  * @brief Our main application structure for the life of this page.
- * 
+ *
  * 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
@@ -530,7 +585,6 @@ class App {
 
        private $scheme;
        private $hostname;
-       private $baseurl;
        private $db;
 
        private $curl_code;
@@ -560,6 +614,7 @@ class App {
 
                $this->performance["start"] = microtime(true);
                $this->performance["database"] = 0;
+               $this->performance["database_write"] = 0;
                $this->performance["network"] = 0;
                $this->performance["file"] = 0;
                $this->performance["rendering"] = 0;
@@ -568,6 +623,7 @@ class App {
                $this->performance["markstart"] = microtime(true);
 
                $this->callstack["database"] = array();
+               $this->callstack["database_write"] = array();
                $this->callstack["network"] = array();
                $this->callstack["file"] = array();
                $this->callstack["rendering"] = array();
@@ -627,22 +683,23 @@ class App {
 
                #set_include_path("include/$this->hostname" . PATH_SEPARATOR . get_include_path());
 
-               if((x($_SERVER,'QUERY_STRING')) && substr($_SERVER['QUERY_STRING'],0,9) === "pagename=") {
+               if ((x($_SERVER,'QUERY_STRING')) && substr($_SERVER['QUERY_STRING'],0,9) === "pagename=") {
                        $this->query_string = substr($_SERVER['QUERY_STRING'],9);
                        // removing trailing / - maybe a nginx problem
                        if (substr($this->query_string, 0, 1) == "/")
                                $this->query_string = substr($this->query_string, 1);
-               } elseif((x($_SERVER,'QUERY_STRING')) && substr($_SERVER['QUERY_STRING'],0,2) === "q=") {
+               } elseif ((x($_SERVER,'QUERY_STRING')) && substr($_SERVER['QUERY_STRING'],0,2) === "q=") {
                        $this->query_string = substr($_SERVER['QUERY_STRING'],2);
                        // removing trailing / - maybe a nginx problem
                        if (substr($this->query_string, 0, 1) == "/")
                                $this->query_string = substr($this->query_string, 1);
                }
 
-               if (x($_GET,'pagename'))
+               if (x($_GET,'pagename')) {
                        $this->cmd = trim($_GET['pagename'],'/\\');
-               elseif (x($_GET,'q'))
+               } elseif (x($_GET,'q')) {
                        $this->cmd = trim($_GET['q'],'/\\');
+               }
 
 
                // fix query_string
@@ -651,13 +708,15 @@ class App {
 
                // unix style "homedir"
 
-               if(substr($this->cmd,0,1) === '~')
+               if (substr($this->cmd,0,1) === '~') {
                        $this->cmd = 'profile/' . substr($this->cmd,1);
+               }
 
                // Diaspora style profile url
 
-               if(substr($this->cmd,0,2) === 'u/')
+               if (substr($this->cmd,0,2) === 'u/') {
                        $this->cmd = 'profile/' . substr($this->cmd,2);
+               }
 
 
                /*
@@ -724,7 +783,7 @@ class App {
 
        }
 
-       function get_basepath() {
+       public static function get_basepath() {
 
                $basepath = get_config("system", "basepath");
 
@@ -744,60 +803,84 @@ class App {
                return($this->scheme);
        }
 
+       /**
+        * @brief Retrieves the Friendica instance base URL
+        *
+        * This function assembles the base URL from multiple parts:
+        * - Protocol is determined either by the request or a combination of
+        * system.ssl_policy and the $ssl parameter.
+        * - Host name is determined either by system.hostname or inferred from request
+        * - Path is inferred from SCRIPT_NAME
+        *
+        * Note: $ssl parameter value doesn't directly correlate with the resulting protocol
+        *
+        * @param bool $ssl Whether to append http or https under SSL_POLICY_SELFSIGN
+        * @return string Friendica server base URL
+        */
        function get_baseurl($ssl = false) {
 
                // Is the function called statically?
-               if (!is_object($this))
-                       return(self::$a->get_baseurl($ssl));
+               if (!(isset($this) && get_class($this) == __CLASS__)) {
+                       return self::$a->get_baseurl($ssl);
+               }
 
                $scheme = $this->scheme;
 
-               if((x($this->config,'system')) && (x($this->config['system'],'ssl_policy'))) {
-                       if(intval($this->config['system']['ssl_policy']) === intval(SSL_POLICY_FULL))
-                               $scheme = 'https';
+               if (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.
+               //      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($this->config['system']['ssl_policy'] == SSL_POLICY_SELFSIGN) {
-                               if($ssl)
-                                       $scheme = 'https';
-                               else
-                                       $scheme = 'http';
+               if (Config::get('system', 'ssl_policy') == SSL_POLICY_SELFSIGN) {
+                       if ($ssl) {
+                               $scheme = 'https';
+                       } else {
+                               $scheme = 'http';
                        }
                }
 
-               if (get_config('config','hostname') != "")
-                       $this->hostname = get_config('config','hostname');
+               if (Config::get('config', 'hostname') != '') {
+                       $this->hostname = Config::get('config', 'hostname');
+               }
 
-               $this->baseurl = $scheme . "://" . $this->hostname . ((isset($this->path) && strlen($this->path)) ? '/' . $this->path : '' );
-               return $this->baseurl;
+               return $scheme . "://" . $this->hostname . ((isset($this->path) && strlen($this->path)) ? '/' . $this->path : '' );
        }
 
+       /**
+        * @brief Initializes the baseurl components
+        *
+        * Clears the baseurl cache to prevent inconstistencies
+        *
+        * @param string $url
+        */
        function set_baseurl($url) {
                $parsed = @parse_url($url);
 
-               $this->baseurl = $url;
-
                if($parsed) {
                        $this->scheme = $parsed['scheme'];
 
                        $hostname = $parsed['host'];
-                       if(x($parsed,'port'))
+                       if (x($parsed, 'port')) {
                                $hostname .= ':' . $parsed['port'];
-                       if(x($parsed,'path'))
-                               $this->path = trim($parsed['path'],'\\/');
+                       }
+                       if (x($parsed, 'path')) {
+                               $this->path = trim($parsed['path'], '\\/');
+                       }
 
-                       if (file_exists(".htpreconfig.php"))
+                       if (file_exists(".htpreconfig.php")) {
                                @include(".htpreconfig.php");
+                       }
 
-                       if (get_config('config','hostname') != "")
-                               $this->hostname = get_config('config','hostname');
+                       if (get_config('config', 'hostname') != '') {
+                               $this->hostname = get_config('config', 'hostname');
+                       }
 
-                       if (!isset($this->hostname) OR ($this->hostname == ""))
+                       if (!isset($this->hostname) OR ($this->hostname == '')) {
                                $this->hostname = $hostname;
+                       }
                }
-
        }
 
        function get_hostname() {
@@ -885,7 +968,6 @@ class App {
                        '$local_user' => local_user(),
                        '$generator' => 'Friendica' . ' ' . FRIENDICA_VERSION,
                        '$delitem' => t('Delete this item?'),
-                       '$comment' => t('Comment'),
                        '$showmore' => t('show more'),
                        '$showfewer' => t('show fewer'),
                        '$update_interval' => $interval,
@@ -945,7 +1027,7 @@ class App {
                } else {
                        $r = q("SELECT `contact`.`avatar-date` AS picdate FROM `contact` WHERE `contact`.`thumb` like '%%/%s'",
                                $common_filename);
-                       if(! dbm::is_result($r)){
+                       if (! dbm::is_result($r)) {
                                $this->cached_profile_image[$avatar_image] = $avatar_image;
                        } else {
                                $this->cached_profile_picdate[$common_filename] = "?rev=".urlencode($r[0]['picdate']);
@@ -960,27 +1042,35 @@ class App {
        /**
         * @brief Removes the baseurl from an url. This avoids some mixed content problems.
         *
-        * @param string $url
+        * @param string $orig_url
         *
         * @return string The cleaned url
         */
-       function remove_baseurl($url){
+       function remove_baseurl($orig_url){
 
                // Is the function called statically?
-               if (!is_object($this))
-                       return(self::$a->remove_baseurl($url));
+               if (!(isset($this) && get_class($this) == __CLASS__)) {
+                       return(self::$a->remove_baseurl($orig_url));
+               }
 
-               $url = normalise_link($url);
+               // Remove the hostname from the url if it is an internal link
+               $nurl = normalise_link($orig_url);
                $base = normalise_link($this->get_baseurl());
-               $url = str_replace($base."/", "", $url);
-               return $url;
+               $url = str_replace($base."/", "", $nurl);
+
+               // if it is an external link return the orignal value
+               if ($url == normalise_link($orig_url)) {
+                       return $orig_url;
+               } else {
+                       return $url;
+               }
        }
 
        /**
         * @brief Register template engine class
-        * 
+        *
         * If $name is "", is used class static property $class::$name
-        * 
+        *
         * @param string $class
         * @param string $name
         */
@@ -998,7 +1088,7 @@ class App {
 
        /**
         * @brief Return template engine instance.
-        * 
+        *
         * If $name is not defined, return engine defined by theme,
         * or default
         *
@@ -1063,6 +1153,9 @@ class App {
        }
 
        function save_timestamp($stamp, $value) {
+               if (!isset($this->config['system']['profiler']) || !$this->config['system']['profiler'])
+                       return;
+
                $duration = (float)(microtime(true)-$stamp);
 
                if (!isset($this->performance[$value])) {
@@ -1084,6 +1177,52 @@ class App {
 
        }
 
+       /**
+        * @brief Log active processes into the "process" table
+        */
+       function start_process() {
+               $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 1);
+
+               $command = basename($trace[0]["file"]);
+
+               $this->remove_inactive_processes();
+
+               q("START TRANSACTION");
+
+               $r = q("SELECT `pid` FROM `process` WHERE `pid` = %d", intval(getmypid()));
+               if (!dbm::is_result($r)) {
+                       q("INSERT INTO `process` (`pid`,`command`,`created`) VALUES (%d, '%s', '%s')",
+                               intval(getmypid()),
+                               dbesc($command),
+                               dbesc(datetime_convert()));
+               }
+               q("COMMIT");
+       }
+
+       /**
+        * @brief Remove inactive processes
+        */
+       function remove_inactive_processes() {
+               q("START TRANSACTION");
+
+               $r = q("SELECT `pid` FROM `process`");
+               if (dbm::is_result($r)) {
+                       foreach ($r AS $process) {
+                               if (!posix_kill($process["pid"], 0)) {
+                                       q("DELETE FROM `process` WHERE `pid` = %d", intval($process["pid"]));
+                               }
+                       }
+               }
+               q("COMMIT");
+       }
+
+       /**
+        * @brief Remove the active process from the "process" table
+        */
+       function end_process() {
+               q("DELETE FROM `process` WHERE `pid` = %d", intval(getmypid()));
+       }
+
        /**
         * @brief Returns a string with a callstack. Can be used for logging.
         *
@@ -1103,11 +1242,6 @@ class App {
                return implode(", ", $callstack);
        }
 
-       function mark_timestamp($mark) {
-               //$this->performance["markstart"] -= microtime(true) - $this->performance["marktime"];
-               $this->performance["markstart"] = microtime(true) - $this->performance["markstart"] - $this->performance["marktime"];
-       }
-
        function get_useragent() {
                return(FRIENDICA_PLATFORM." '".FRIENDICA_CODENAME."' ".FRIENDICA_VERSION."-".DB_UPDATE_VERSION."; ".$this->get_baseurl());
        }
@@ -1159,10 +1293,6 @@ class App {
         */
        function max_processes_reached() {
 
-               // Is the function called statically?
-               if (!is_object($this))
-                       return(self::$a->max_processes_reached());
-
                if ($this->is_backend()) {
                        $process = "backend";
                        $max_processes = get_config('system', 'max_processes_backend');
@@ -1194,10 +1324,6 @@ class App {
         */
        function maxload_reached() {
 
-               // Is the function called statically?
-               if (!is_object($this))
-                       return(self::$a->maxload_reached());
-
                if ($this->is_backend()) {
                        $process = "backend";
                        $maxsysload = intval(get_config('system', 'maxloadavg'));
@@ -1241,18 +1367,106 @@ class App {
                                        logger("killed stale process");
                                        // Calling a new instance
                                        if ($task != "")
-                                               proc_run('php', $task);
+                                               proc_run(PRIORITY_MEDIUM, $task);
                                }
                                return true;
                        }
                }
                return false;
        }
+
+       function proc_run($args) {
+
+               if (!function_exists("proc_open")) {
+                       return;
+               }
+
+               // Add the php path if it is a php call
+               if (count($args) && ($args[0] === 'php' OR !is_string($args[0]))) {
+
+                       // If the last worker fork was less than 10 seconds before then don't fork another one.
+                       // This should prevent the forking of masses of workers.
+                       if (get_config("system", "worker")) {
+                               $cachekey = "app:proc_run:started";
+                               $result = Cache::get($cachekey);
+                               if (!is_null($result)) {
+                                       if ((time() - $result) < 10) {
+                                               return;
+                                       }
+                               }
+                               // Set the timestamp of the last proc_run
+                               Cache::set($cachekey, time(), CACHE_MINUTE);
+                       }
+
+                       $args[0] = ((x($this->config,'php_path')) && (strlen($this->config['php_path'])) ? $this->config['php_path'] : 'php');
+               }
+
+               // add baseurl to args. cli scripts can't construct it
+               $args[] = $this->get_baseurl();
+
+               for($x = 0; $x < count($args); $x ++)
+                       $args[$x] = escapeshellarg($args[$x]);
+
+               $cmdline = implode($args," ");
+
+               if(get_config('system','proc_windows'))
+                       proc_close(proc_open('cmd /c start /b ' . $cmdline,array(),$foo,dirname(__FILE__)));
+               else
+                       proc_close(proc_open($cmdline." &",array(),$foo,dirname(__FILE__)));
+
+       }
+
+       /**
+        * @brief Returns the system user that is executing the script
+        *
+        * This mostly returns something like "www-data".
+        *
+        * @return string system username
+        */
+       static function systemuser() {
+               if (!function_exists('posix_getpwuid') OR !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
+        */
+       static function directory_usable($directory) {
+
+               if ($directory == '') {
+                       logger("Directory is empty. This shouldn't happen.", LOGGER_DEBUG);
+                       return false;
+               }
+
+               if (!file_exists($directory)) {
+                       logger('Path "'.$directory.'" does not exist for user '.self::systemuser(), LOGGER_DEBUG);
+                       return false;
+               }
+               if (is_file($directory)) {
+                       logger('Path "'.$directory.'" is a file for user '.self::systemuser(), LOGGER_DEBUG);
+                       return false;
+               }
+               if (!is_dir($directory)) {
+                       logger('Path "'.$directory.'" is not a directory for user '.self::systemuser(), LOGGER_DEBUG);
+                       return false;
+               }
+               if (!is_writable($directory)) {
+                       logger('Path "'.$directory.'" is not writable for user '.self::systemuser(), LOGGER_DEBUG);
+                       return false;
+               }
+               return true;
+       }
 }
 
 /**
  * @brief Retrieve the App structure
- * 
+ *
  * Useful in functions which require it but don't get it passed to them
  */
 function get_app() {
@@ -1307,17 +1521,16 @@ function system_unavailable() {
 
 
 function clean_urls() {
-       global $a;
-       //      if($a->config['system']['clean_urls'])
+       $a = get_app();
        return true;
-       //      return false;
 }
 
 function z_path() {
-       global $a;
-       $base = $a->get_baseurl();
+       $base = App::get_baseurl();
+
        if(! clean_urls())
                $base .= '/?q=';
+
        return $base;
 }
 
@@ -1327,10 +1540,10 @@ function z_path() {
  * @see App::get_baseurl()
  *
  * @return string
+ * @TODO Maybe super-flous and deprecated? Seems to only wrap App::get_baseurl()
  */
 function z_root() {
-       global $a;
-       return $a->get_baseurl();
+       return App::get_baseurl();
 }
 
 /**
@@ -1363,7 +1576,7 @@ function check_db() {
                $build = DB_UPDATE_VERSION;
        }
        if($build != DB_UPDATE_VERSION)
-               proc_run('php', 'include/dbupdate.php');
+               proc_run(PRIORITY_CRITICAL, 'include/dbupdate.php');
 
 }
 
@@ -1372,7 +1585,7 @@ function check_db() {
  * Sets the base url for use in cmdline programs which don't have
  * $_SERVER variables
  */
-function check_url(&$a) {
+function check_url(App $a) {
 
        $url = get_config('system','url');
 
@@ -1383,9 +1596,9 @@ function check_url(&$a) {
        // We will only change the url to an ip address if there is no existing setting
 
        if(! x($url))
-               $url = set_config('system','url',$a->get_baseurl());
-       if((! link_compare($url,$a->get_baseurl())) && (! preg_match("/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/",$a->get_hostname)))
-               $url = set_config('system','url',$a->get_baseurl());
+               $url = set_config('system','url',App::get_baseurl());
+       if((! link_compare($url,App::get_baseurl())) && (! preg_match("/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/",$a->get_hostname)))
+               $url = set_config('system','url',App::get_baseurl());
 
        return;
 }
@@ -1394,7 +1607,7 @@ function check_url(&$a) {
 /**
  * @brief Automatic database updates
  */
-function update_db(&$a) {
+function update_db(App $a) {
        $build = get_config('system','build');
        if(! x($build))
                $build = set_config('system','build',DB_UPDATE_VERSION);
@@ -1403,7 +1616,7 @@ function update_db(&$a) {
                $stored = intval($build);
                $current = intval(DB_UPDATE_VERSION);
                if($stored < $current) {
-                       load_config('database');
+                       Config::load('database');
 
                        // We're reporting a different version than what is currently installed.
                        // Run any existing update scripts to bring the database up to current.
@@ -1506,14 +1719,14 @@ function run_update_function($x) {
  * and mark it uninstalled in the database (for now we'll remove it).
  * Then go through the config list and if we have a plugin that isn't installed,
  * call the install procedure and add it to the database.
- * 
+ *
  * @param App $a
  *
         */
-function check_plugins(&$a) {
+function check_plugins(App $a) {
 
        $r = q("SELECT * FROM `addon` WHERE `installed` = 1");
-       if(dbm::is_result($r))
+       if (dbm::is_result($r))
                $installed = $r;
        else
                $installed = array();
@@ -1572,17 +1785,17 @@ function get_guid($size=16, $prefix = "") {
        }
 }
 
-/** 
+/**
  * @brief Wrapper for adding a login box.
- * 
+ *
  * @param bool $register
  *     If $register == true provide a registration link.
  *     This will most always depend on the value of $a->config['register_policy'].
  * @param bool $hiddens
- * 
+ *
  * @return string
  *     Returns the complete html for inserting into the page
- * 
+ *
  * @hooks 'login_hook'
  *     string $o
  */
@@ -1651,7 +1864,10 @@ function login($register = false, $hiddens=false) {
  * @brief Used to end the current process, after saving session state.
  */
 function killme() {
-       session_write_close();
+
+       if (!get_app()->is_backend())
+               session_write_close();
+
        exit;
 }
 
@@ -1669,7 +1885,7 @@ function goaway($s) {
 
 /**
  * @brief Returns the user id of locally logged in user or false.
- * 
+ *
  * @return int|bool user id or false
  */
 function local_user() {
@@ -1680,7 +1896,7 @@ function local_user() {
 
 /**
  * @brief Returns contact id of authenticated site visitor or false
- * 
+ *
  * @return int|bool visitor_id or false
  */
 function remote_user() {
@@ -1728,7 +1944,7 @@ function info($s) {
  * @return int
  */
 function get_max_import_size() {
-       global $a;
+       $a = get_app();
        return ((x($a->config,'max_import_size')) ? $a->config['max_import_size'] : 0 );
 }
 
@@ -1736,13 +1952,15 @@ function get_max_import_size() {
  * @brief Wrap calls to proc_close(proc_open()) and call hook
  *     so plugins can take part in process :)
  *
- * @param string $cmd program to run
- * 
+ * @param (string|integer|array) $cmd program to run, priority or parameter array
+ *
  * next args are passed as $cmd command line
  * e.g.: proc_run("ls","-la","/tmp");
+ * or: proc_run(PRIORITY_HIGH, "include/notifier.php", "drop", $drop_id);
+ * or: proc_run(array('priority' => PRIORITY_HIGH, 'dont_fork' => true), "include/create_shadowentry.php", $post_id);
  *
  * @note $cmd and string args are surrounded with ""
- * 
+ *
  * @hooks 'proc_run'
  *     array $arr
  */
@@ -1750,104 +1968,93 @@ function proc_run($cmd){
 
        $a = get_app();
 
-       $args = func_get_args();
+       $proc_args = func_get_args();
 
-       $newargs = array();
-       if(! count($args))
+       $args = array();
+       if (!count($proc_args)) {
                return;
+       }
 
-       // expand any arrays
+       // Preserve the first parameter
+       // It could contain a command, the priority or an parameter array
+       // If we use the parameter array we have to protect it from the following function
+       $run_parameter = array_shift($proc_args);
 
-       foreach($args as $arg) {
-               if(is_array($arg)) {
-                       foreach($arg as $n) {
-                               $newargs[] = $n;
+       // expand any arrays
+       foreach ($proc_args as $arg) {
+               if (is_array($arg)) {
+                       foreach ($arg as $n) {
+                               $args[] = $n;
                        }
+               } else {
+                       $args[] = $arg;
                }
-               else
-                       $newargs[] = $arg;
        }
 
-       $args = $newargs;
+       // Now we add the run parameters back to the array
+       array_unshift($args, $run_parameter);
 
        $arr = array('args' => $args, 'run_cmd' => true);
 
        call_hooks("proc_run", $arr);
-       if(! $arr['run_cmd'])
+       if (!$arr['run_cmd'] OR !count($args))
                return;
 
-       if(count($args) && $args[0] === 'php') {
-
-               if (get_config("system", "worker")) {
-                       $argv = $args;
-                       array_shift($argv);
-
-                       $parameters = json_encode($argv);
-                       $found = q("SELECT `id` FROM `workerqueue` WHERE `parameter` = '%s'",
-                                       dbesc($parameters));
-
-                       $funcname = str_replace(".php", "", basename($argv[0]))."_run";
-
-                       // Define the processes that have priority over any other process
-                       /// @todo Better check for priority processes
-                       $high_priority = array("delivery_run", "notifier_run", "pubsubpublish_run");
-                       $low_priority = array("queue_run", "gprobe_run", "discover_poco_run");
-
-                       if (in_array($funcname, $high_priority))
-                               $priority = 1;
-                       elseif (in_array($funcname, $low_priority))
-                               $priority = 3;
-                       else
-                               $priority = 2;
-
-                       if (!$found)
-                               //  quickfix for the delivery problems, 2106-07-28
-                               /// @todo find better solution
-                               //q("INSERT INTO `workerqueue` (`function`, `parameter`, `created`, `priority`)
-                               //                      VALUES ('%s', '%s', '%s', %d)",
-                               //      dbesc($funcname),
-                               q("INSERT INTO `workerqueue` (`parameter`, `created`, `priority`)
-                                                       VALUES ('%s', '%s', %d)",
-                                       dbesc($parameters),
-                                       dbesc(datetime_convert()),
-                                       intval($priority));
-
-                       // Should we quit and wait for the poller to be called as a cronjob?
-                       if (get_config("system", "worker_dont_fork"))
-                               return;
-
-                       // Checking number of workers
-                       $workers = q("SELECT COUNT(*) AS `workers` FROM `workerqueue` WHERE `executed` != '0000-00-00 00:00:00'");
-
-                       // Get number of allowed number of worker threads
-                       $queues = intval(get_config("system", "worker_queues"));
-
-                       if ($queues == 0)
-                               $queues = 4;
-
-                       // If there are already enough workers running, don't fork another one
-                       if ($workers[0]["workers"] >= $queues)
-                               return;
-
-                       // Now call the poller to execute the jobs that we just added to the queue
-                       $args = array("php", "include/poller.php", "no_cron");
+       if (!get_config("system", "worker") OR (is_string($run_parameter) AND ($run_parameter != 'php'))) {
+               $a->proc_run($args);
+               return;
+       }
+
+       $priority = PRIORITY_MEDIUM;
+       $dont_fork = get_config("system", "worker_dont_fork");
+
+       if (is_int($run_parameter)) {
+               $priority = $run_parameter;
+       } elseif (is_array($run_parameter)) {
+               if (isset($run_parameter['priority'])) {
+                       $priority = $run_parameter['priority'];
                }
+               if (isset($run_parameter['dont_fork'])) {
+                       $dont_fork = $run_parameter['dont_fork'];
+               }
+       }
+
+       $argv = $args;
+       array_shift($argv);
 
-               $args[0] = ((x($a->config,'php_path')) && (strlen($a->config['php_path'])) ? $a->config['php_path'] : 'php');
+       $parameters = json_encode($argv);
+       $found = q("SELECT `id` FROM `workerqueue` WHERE `parameter` = '%s'",
+               dbesc($parameters));
+
+       if (!$found)
+               q("INSERT INTO `workerqueue` (`parameter`, `created`, `priority`)
+                       VALUES ('%s', '%s', %d)",
+                       dbesc($parameters),
+                       dbesc(datetime_convert()),
+                       intval($priority));
+
+       // Should we quit and wait for the poller to be called as a cronjob?
+       if ($dont_fork) {
+               return;
        }
 
-       // add baseurl to args. cli scripts can't construct it
-       $args[] = $a->get_baseurl();
+       // Checking number of workers
+       $workers = q("SELECT COUNT(*) AS `workers` FROM `workerqueue` WHERE `executed` != '0000-00-00 00:00:00'");
 
-       for($x = 0; $x < count($args); $x ++)
-               $args[$x] = escapeshellarg($args[$x]);
+       // Get number of allowed number of worker threads
+       $queues = intval(get_config("system", "worker_queues"));
 
-       $cmdline = implode($args," ");
+       if ($queues == 0)
+               $queues = 4;
 
-       if(get_config('system','proc_windows'))
-               proc_close(proc_open('cmd /c start /b ' . $cmdline,array(),$foo,dirname(__FILE__)));
-       else
-               proc_close(proc_open($cmdline." &",array(),$foo,dirname(__FILE__)));
+       // If there are already enough workers running, don't fork another one
+       if ($workers[0]["workers"] >= $queues)
+               return;
+
+       // Now call the poller to execute the jobs that we just added to the queue
+       $args = array("php", "include/poller.php", "no_cron");
+
+       $a->proc_run($args);
 }
 
 function current_theme(){
@@ -1863,7 +2070,7 @@ function current_theme(){
                $r = q("select theme from user where uid = %d limit 1",
                        intval($a->profile_uid)
                );
-               if(dbm::is_result($r))
+               if (dbm::is_result($r))
                        $page_theme = $r[0]['theme'];
        }
 
@@ -1879,16 +2086,18 @@ function current_theme(){
 //             $is_mobile = $mobile_detect->isMobile() || $mobile_detect->isTablet();
        $is_mobile = $a->is_mobile || $a->is_tablet;
 
-       $standard_system_theme = ((isset($a->config['system']['theme'])) ? $a->config['system']['theme'] : '');
+       $standard_system_theme = Config::get('system', 'theme', '');
        $standard_theme_name = ((isset($_SESSION) && x($_SESSION,'theme')) ? $_SESSION['theme'] : $standard_system_theme);
 
-       if($is_mobile) {
-               if(isset($_SESSION['show-mobile']) && !$_SESSION['show-mobile']) {
+       if ($is_mobile) {
+               if (isset($_SESSION['show-mobile']) && !$_SESSION['show-mobile']) {
                        $system_theme = $standard_system_theme;
                        $theme_name = $standard_theme_name;
-               }
-               else {
-                       $system_theme = ((isset($a->config['system']['mobile-theme'])) ? $a->config['system']['mobile-theme'] : $standard_system_theme);
+               } else {
+                       $system_theme = Config::get('system', 'mobile-theme', '');
+                       if ($system_theme == '') {
+                               $system_theme = $standard_system_theme;
+                       }
                        $theme_name = ((isset($_SESSION) && x($_SESSION,'mobile-theme')) ? $_SESSION['mobile-theme'] : $system_theme);
 
                        if($theme_name === '---') {
@@ -1928,13 +2137,13 @@ function current_theme(){
 
 /**
  * @brief Return full URL to theme which is currently in effect.
- * 
+ *
  * Provide a sane default if nothing is chosen or the specified theme does not exist.
- * 
+ *
  * @return string
  */
 function current_theme_url() {
-       global $a;
+       $a = get_app();
 
        $t = current_theme();
 
@@ -1976,7 +2185,7 @@ function feed_birthday($uid,$tz) {
                        intval($uid)
        );
 
-       if(dbm::is_result($p)) {
+       if (dbm::is_result($p)) {
                $tmp_dob = substr($p[0]['dob'],5);
                if(intval($tmp_dob)) {
                        $y = datetime_convert($tz,$tz,'now','Y');
@@ -2157,8 +2366,9 @@ function get_itemcachepath() {
                return "";
 
        $itemcache = get_config('system','itemcache');
-       if (($itemcache != "") AND is_dir($itemcache) AND is_writable($itemcache))
-               return($itemcache);
+       if (($itemcache != "") AND App::directory_usable($itemcache)) {
+               return $itemcache;
+       }
 
        $temppath = get_temppath();
 
@@ -2168,9 +2378,9 @@ function get_itemcachepath() {
                        mkdir($itemcache);
                }
 
-               if (is_dir($itemcache) AND is_writable($itemcache)) {
+               if (App::directory_usable($itemcache)) {
                        set_config("system", "itemcache", $itemcache);
-                       return($itemcache);
+                       return $itemcache;
                }
        }
        return "";
@@ -2178,50 +2388,110 @@ function get_itemcachepath() {
 
 function get_lockpath() {
        $lockpath = get_config('system','lockpath');
-       if (($lockpath != "") AND is_dir($lockpath) AND is_writable($lockpath))
-               return($lockpath);
+       if (($lockpath != "") AND App::directory_usable($lockpath)) {
+               // We have a lock path and it is usable
+               return $lockpath;
+       }
 
+       // We don't have a working preconfigured lock path, so we take the temp path.
        $temppath = get_temppath();
 
        if ($temppath != "") {
+               // To avoid any interferences with other systems we create our own directory
                $lockpath = $temppath."/lock";
-
-               if (!is_dir($lockpath))
+               if (!is_dir($lockpath)) {
                        mkdir($lockpath);
-               elseif (!is_writable($lockpath))
-                       $lockpath = $temppath;
+               }
 
-               if (is_dir($lockpath) AND is_writable($lockpath)) {
+               if (App::directory_usable($lockpath)) {
+                       // The new path is usable, we are happy
                        set_config("system", "lockpath", $lockpath);
-                       return($lockpath);
+                       return $lockpath;
+               } else {
+                       // We can't create a subdirectory, strange.
+                       // But the directory seems to work, so we use it but don't store it.
+                       return $temppath;
                }
        }
+
+       // Reaching this point means that the operating system is configured badly.
+       return "";
+}
+
+/**
+ * @brief Returns the path where spool files are stored
+ *
+ * @return string Spool path
+ */
+function get_spoolpath() {
+       $spoolpath = get_config('system','spoolpath');
+       if (($spoolpath != "") AND App::directory_usable($spoolpath)) {
+               // We have a spool path and it is usable
+               return $spoolpath;
+       }
+
+       // We don't have a working preconfigured spool path, so we take the temp path.
+       $temppath = get_temppath();
+
+       if ($temppath != "") {
+               // To avoid any interferences with other systems we create our own directory
+               $spoolpath = $temppath."/spool";
+               if (!is_dir($spoolpath)) {
+                       mkdir($spoolpath);
+               }
+
+               if (App::directory_usable($spoolpath)) {
+                       // The new path is usable, we are happy
+                       set_config("system", "spoolpath", $spoolpath);
+                       return $spoolpath;
+               } else {
+                       // We can't create a subdirectory, strange.
+                       // But the directory seems to work, so we use it but don't store it.
+                       return $temppath;
+               }
+       }
+
+       // Reaching this point means that the operating system is configured badly.
        return "";
 }
 
 function get_temppath() {
        $a = get_app();
 
-       $temppath = get_config("system","temppath");
-       if (($temppath != "") AND is_dir($temppath) AND is_writable($temppath))
-               return($temppath);
+       $temppath = get_config("system", "temppath");
 
+       if (($temppath != "") AND App::directory_usable($temppath)) {
+               // We have a temp path and it is usable
+               return $temppath;
+       }
+
+       // We don't have a working preconfigured temp path, so we take the system path.
        $temppath = sys_get_temp_dir();
-       if (($temppath != "") AND is_dir($temppath) AND is_writable($temppath)) {
-               $temppath .= "/".$a->get_hostname();
-               if (!is_dir($temppath))
-                       mkdir($temppath);
-
-               if (is_dir($temppath) AND is_writable($temppath)) {
-                       set_config("system", "temppath", $temppath);
-                       return($temppath);
+
+       // Check if it is usable
+       if (($temppath != "") AND App::directory_usable($temppath)) {
+               // To avoid any interferences with other systems we create our own directory
+               $new_temppath .= "/".$a->get_hostname();
+               if (!is_dir($new_temppath))
+                       mkdir($new_temppath);
+
+               if (App::directory_usable($new_temppath)) {
+                       // The new path is usable, we are happy
+                       set_config("system", "temppath", $new_temppath);
+                       return $new_temppath;
+               } else {
+                       // We can't create a subdirectory, strange.
+                       // But the directory seems to work, so we use it but don't store it.
+                       return $temppath;
                }
        }
 
-       return("");
+       // Reaching this point means that the operating system is configured badly.
+       return '';
 }
 
-function set_template_engine(&$a, $engine = 'internal') {
+/// @deprecated
+function set_template_engine(App $a, $engine = 'internal') {
 /// @note This function is no longer necessary, but keep it as a wrapper to the class method
 /// to avoid breaking themes again unnecessarily
 
@@ -2272,12 +2542,12 @@ function current_load() {
        if (!is_array($load_arr))
                return false;
 
-       return max($load_arr);
+       return max($load_arr[0], $load_arr[1]);
 }
 
 /**
  * @brief get c-style args
- * 
+ *
  * @return int
  */
 function argc() {
@@ -2286,7 +2556,7 @@ function argc() {
 
 /**
  * @brief Returns the value of a argv key
- * 
+ *
  * @param int $x argv key
  * @return string Value of the argv key
  */
@@ -2299,12 +2569,12 @@ function argv($x) {
 
 /**
  * @brief Get the data which is needed for infinite scroll
- * 
+ *
  * For invinite scroll we need the page number of the actual page
  * and the the URI where the content of the next page comes from.
  * This data is needed for the js part in main.js.
  * Note: infinite scroll does only work for the network page (module)
- * 
+ *
  * @param string $module The name of the module (e.g. "network")
  * @return array Of infinite scroll data
  *     'pageno' => $pageno The number of the actual page