/**
* Friendica
- *
+ *
* Friendica is a communications platform for integrated social communications
* utilising decentralised communications and linkage to several indie social
* projects - as well as popular mainstream providers.
- *
+ *
* Our mission is to free our friends and families from the clutches of
* data-harvesting corporations, and pave the way to a future where social
* communications are free and open and flow between alternate providers as
* easily as email does today.
*/
+require_once('include/autoloader.php');
+
require_once('include/config.php');
require_once('include/network.php');
require_once('include/plugin.php');
require_once('library/Mobile_Detect/Mobile_Detect.php');
require_once('include/features.php');
require_once('include/identity.php');
-
+require_once('include/pidfile.php');
require_once('update.php');
require_once('include/dbstructure.php');
define ( 'FRIENDICA_CODENAME', 'Asparagus');
define ( 'FRIENDICA_VERSION', '3.5-dev' );
define ( 'DFRN_PROTOCOL_VERSION', '2.23' );
-define ( 'DB_UPDATE_VERSION', 1191 );
+define ( 'DB_UPDATE_VERSION', 1196 );
+
+/**
+ * @brief Constant with a HTML line break.
+ *
+ * Contains a HTML line break (br) element and a real carriage return with line
+ * feed for the source.
+ * This can be used in HTML and JavaScript where needed a line break.
+ */
define ( 'EOL', "<br />\r\n" );
define ( 'ATOM_TIME', 'Y-m-d\TH:i:s\Z' );
/**
- *
- * Image storage quality. Lower numbers save space at cost of image detail.
+ * @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;
* in .htconfig.php, where n is netween 1 and 100, and with very poor results
define ( 'DEFAULT_DB_ENGINE', 'MyISAM' );
/**
+ * @name SSL Policy
+ *
* SSL redirection policies
+ * @{
*/
-
define ( 'SSL_POLICY_NONE', 0 );
define ( 'SSL_POLICY_FULL', 1 );
define ( 'SSL_POLICY_SELFSIGN', 2 );
-
+/* @}*/
/**
+ * @name Logger
+ *
* log levels
+ * @{
*/
-
define ( 'LOGGER_NORMAL', 0 );
define ( 'LOGGER_TRACE', 1 );
define ( 'LOGGER_DEBUG', 2 );
define ( 'LOGGER_DATA', 3 );
define ( 'LOGGER_ALL', 4 );
+/* @}*/
/**
- * cache levels
+ * @name Cache
+ *
+ * Cache levels
+ * @{
*/
-
define ( 'CACHE_MONTH', 0 );
define ( 'CACHE_WEEK', 1 );
define ( 'CACHE_DAY', 2 );
define ( 'CACHE_HOUR', 3 );
+/* @}*/
/**
- * registration policies
+ * @name Register
+ *
+ * Registration policies
+ * @{
*/
-
define ( 'REGISTER_CLOSED', 0 );
define ( 'REGISTER_APPROVE', 1 );
define ( 'REGISTER_OPEN', 2 );
+/** @}*/
/**
- * relationship types
+ * @name Contact_is
+ *
+ * Relationship types
+ * @{
*/
-
define ( 'CONTACT_IS_FOLLOWER', 1);
define ( 'CONTACT_IS_SHARING', 2);
define ( 'CONTACT_IS_FRIEND', 3);
-
+/** @}*/
/**
+ * @name Update
+ *
* DB update return values
+ * @{
*/
-
define ( 'UPDATE_SUCCESS', 0);
define ( 'UPDATE_FAILED', 1);
+/** @}*/
/**
- *
- * page/profile types
+ * @name page/profile types
*
* PAGE_NORMAL is a typical personal profile account
* PAGE_SOAPBOX automatically approves all friend requests as CONTACT_IS_SHARING, (readonly)
* write access to wall and comments (no email and not included in page owner's ACL lists)
* PAGE_FREELOVE automatically approves all friend requests as full friends (CONTACT_IS_FRIEND).
*
+ * @{
*/
-
define ( 'PAGE_NORMAL', 0 );
define ( 'PAGE_SOAPBOX', 1 );
define ( 'PAGE_COMMUNITY', 2 );
define ( 'PAGE_FREELOVE', 3 );
define ( 'PAGE_BLOG', 4 );
define ( 'PAGE_PRVGROUP', 5 );
+/** @}*/
-// Type of the community page
+/**
+ * @name CP
+ *
+ * Type of the community page
+ * @{
+ */
define ( 'CP_NO_COMMUNITY_PAGE', -1 );
define ( 'CP_USERS_ON_SERVER', 0 );
define ( 'CP_GLOBAL_COMMUNITY', 1 );
+/** @}*/
/**
+ * @name Network
+ *
* Network and protocol family types
+ * @{
*/
-
define ( 'NETWORK_DFRN', 'dfrn'); // Friendica, Mistpark, other DFRN implementations
define ( 'NETWORK_ZOT', 'zot!'); // Zot!
define ( 'NETWORK_OSTATUS', 'stat'); // status.net, identi.ca, GNU-social, other OStatus implementations
define ( 'NETWORK_NEWS', 'nntp'); // Network News Transfer Protocol
define ( 'NETWORK_ICALENDAR', 'ical'); // iCalendar
define ( 'NETWORK_PHANTOM', 'unkn'); // Place holder
+/** @}*/
/**
* These numbers are used in stored permissions
/**
- * email notification options
+ * @name Notify
+ *
+ * Email notification options
+ * @{
*/
-
define ( 'NOTIFY_INTRO', 0x0001 );
define ( 'NOTIFY_CONFIRM', 0x0002 );
define ( 'NOTIFY_WALL', 0x0004 );
define ( 'NOTIFY_SHARE', 0x0400 );
define ( 'NOTIFY_SYSTEM', 0x8000 );
+/* @}*/
/**
+ * @name Term
+ *
* Tag/term types
+ * @{
*/
-
define ( 'TERM_UNKNOWN', 0 );
define ( 'TERM_HASHTAG', 1 );
define ( 'TERM_MENTION', 2 );
/**
- * various namespaces we may need to parse
+ * @name Namespaces
+ *
+ * Various namespaces we may need to parse
+ * @{
*/
-
define ( 'NAMESPACE_ZOT', 'http://purl.org/zot' );
define ( 'NAMESPACE_DFRN' , 'http://purl.org/macgirvin/dfrn/1.0' );
define ( 'NAMESPACE_THREAD' , 'http://purl.org/syndication/thread/1.0' );
define ( 'NAMESPACE_OSTATUS', 'http://ostatus.org/schema/1.0' );
define ( 'NAMESPACE_STATUSNET', 'http://status.net/schema/api/1/' );
define ( 'NAMESPACE_ATOM1', 'http://www.w3.org/2005/Atom' );
+/* @}*/
+
/**
- * activity stream defines
+ * @name Activity
+ *
+ * Activity stream defines
+ * @{
*/
-
define ( 'ACTIVITY_LIKE', NAMESPACE_ACTIVITY_SCHEMA . 'like' );
define ( 'ACTIVITY_DISLIKE', NAMESPACE_DFRN . '/dislike' );
define ( 'ACTIVITY_ATTEND', NAMESPACE_ZOT . '/activity/attendyes' );
define ( 'ACTIVITY_OBJ_TAGTERM', NAMESPACE_DFRN . '/tagterm' );
define ( 'ACTIVITY_OBJ_PROFILE', NAMESPACE_DFRN . '/profile' );
define ( 'ACTIVITY_OBJ_QUESTION', 'http://activityschema.org/object/question' );
+/* @}*/
/**
- * item weight for query ordering
+ * @name Gravity
+ *
+ * Item weight for query ordering
+ * @{
*/
-
define ( 'GRAVITY_PARENT', 0);
define ( 'GRAVITY_LIKE', 3);
define ( 'GRAVITY_COMMENT', 6);
+/* @}*/
+
+
+// Normally this constant is defined - but not if "pcntl" isn't installed
+if (!defined("SIGTERM"))
+ define("SIGTERM", 15);
/**
*
public $plugins;
public $apps = array();
public $identities;
- public $is_mobile;
- public $is_tablet;
+ public $is_mobile = false;
+ public $is_tablet = false;
public $is_friendica_app;
public $performance = array();
+ public $callstack = array();
+ public $theme_info = array();
public $nav_sel;
private $cached_profile_image;
private $cached_profile_picdate;
+ private static $a;
+
/**
- * App constructor.
+ * @brief App constructor.
*/
function __construct() {
$this->performance["marktime"] = 0;
$this->performance["markstart"] = microtime(true);
+ $this->callstack["database"] = array();
+ $this->callstack["network"] = array();
+ $this->callstack["file"] = array();
+ $this->callstack["rendering"] = array();
+ $this->callstack["parser"] = array();
+
$this->config = array();
$this->page = array();
$this->pager= array();
$this->scheme = 'http';
- if(x($_SERVER,'HTTPS') && $_SERVER['HTTPS'])
- $this->scheme = 'https';
- elseif(x($_SERVER,'SERVER_PORT') && (intval($_SERVER['SERVER_PORT']) == 443))
+ if((x($_SERVER,'HTTPS') && $_SERVER['HTTPS']) ||
+ (x($_SERVER['HTTP_FORWARDED']) && preg_match("/proto=https/", $_SERVER['HTTP_FORWARDED'])) ||
+ (x($_SERVER['HTTP_X_FORWARDED_PROTO']) && $_SERVER['HTTP_X_FORWARDED_PROTO'] == 'https') ||
+ (x($_SERVER['HTTP_X_FORWARDED_SSL']) && $_SERVER['HTTP_X_FORWARDED_SSL'] == 'on') ||
+ (x($_SERVER['FRONT_END_HTTPS']) && $_SERVER['FRONT_END_HTTPS'] == 'on') ||
+ (x($_SERVER,'SERVER_PORT') && (intval($_SERVER['SERVER_PORT']) == 443)) // XXX: reasonable assumption, but isn't this hardcoding too much?
+ ) {
$this->scheme = 'https';
+ }
if(x($_SERVER,'SERVER_NAME')) {
$this->hostname = $_SERVER['SERVER_NAME'];
- // See bug 437 - this didn't work so disabling it
- //if(stristr($this->hostname,'xn--')) {
- // PHP or webserver may have converted idn to punycode, so
- // convert punycode back to utf-8
- // require_once('library/simplepie/idn/idna_convert.class.php');
- // $x = new idna_convert();
- // $this->hostname = $x->decode($_SERVER['SERVER_NAME']);
- //}
-
if(x($_SERVER,'SERVER_PORT') && $_SERVER['SERVER_PORT'] != 80 && $_SERVER['SERVER_PORT'] != 443)
$this->hostname .= ':' . $_SERVER['SERVER_PORT'];
- /**
+ /*
* Figure out if we are running at the top of a domain
* or in a sub-directory and adjust accordingly
*/
$this->cmd = 'profile/' . substr($this->cmd,2);
- /**
+ /*
*
* Break the URL path into C style argc/argv style arguments for our
* modules. Given "http://example.com/module/arg1/arg2", $this->argc
$this->module = 'home';
}
- /**
+ /*
* See if there is any page number information, and initialise
* pagination
*/
$this->pager['start'] = 0;
$this->pager['total'] = 0;
- /**
+ /*
* Detect mobile devices
*/
// Friendica-Client
$this->is_friendica_app = ($_SERVER['HTTP_USER_AGENT'] == "Apache-HttpClient/UNAVAILABLE (java 1.4)");
- /**
+ /*
* register template engines
*/
$dc = get_declared_classes();
}
}
+ self::$a = $this;
+
}
function get_basepath() {
function get_baseurl($ssl = false) {
+ // Is the function called statically?
+ if (!is_object($this))
+ return(self::$a->get_baseurl($ssl));
+
$scheme = $this->scheme;
if((x($this->config,'system')) && (x($this->config['system'],'ssl_policy'))) {
$shortcut_icon = get_config("system", "shortcut_icon");
if ($shortcut_icon == "")
- $shortcut_icon = $this->get_baseurl()."/images/friendica-32.png";
+ $shortcut_icon = "images/friendica-32.png";
$touch_icon = get_config("system", "touch_icon");
if ($touch_icon == "")
- $touch_icon = $this->get_baseurl()."/images/friendica-128.png";
+ $touch_icon = "images/friendica-128.png";
+
+ // get data wich is needed for infinite scroll on the network page
+ $invinite_scroll = infinite_scroll_data($this->module);
$tpl = get_markup_template('head.tpl');
$this->page['htmlhead'] = replace_macros($tpl,array(
'$update_interval' => $interval,
'$shortcut_icon' => $shortcut_icon,
'$touch_icon' => $touch_icon,
- '$stylesheet' => $stylesheet
+ '$stylesheet' => $stylesheet,
+ '$infinite_scroll' => $invinite_scroll,
)) . $this->page['htmlhead'];
}
}
function get_cached_avatar_image($avatar_image){
+ return $avatar_image;
+
+ // The following code is deactivated. It doesn't seem to make any sense and it slows down the system.
+ /*
if($this->cached_profile_image[$avatar_image])
return $this->cached_profile_image[$avatar_image];
}
}
return $this->cached_profile_image[$avatar_image];
+ */
}
+ /**
+ * @brief Removes the baseurl from an url. This avoids some mixed content problems.
+ *
+ * @param string $url
+ *
+ * @return string The cleaned url
+ */
+ function remove_baseurl($url){
+
+ // Is the function called statically?
+ if (!is_object($this))
+ return(self::$a->remove_baseurl($url));
+
+ $url = normalise_link($url);
+ $base = normalise_link($this->get_baseurl());
+ $url = str_replace($base."/", "", $url);
+ return $url;
+ }
+
/**
* @brief Register template engine class
*
function save_timestamp($stamp, $value) {
$duration = (float)(microtime(true)-$stamp);
+ if (!isset($this->performance[$value])) {
+ // Prevent ugly E_NOTICE
+ $this->performance[$value] = 0;
+ }
+
$this->performance[$value] += (float)$duration;
$this->performance["marktime"] += (float)$duration;
+
+ $callstack = $this->callstack();
+
+ if (!isset($this->callstack[$value][$callstack])) {
+ // Prevent ugly E_NOTICE
+ $this->callstack[$value][$callstack] = 0;
+ }
+
+ $this->callstack[$value][$callstack] += (float)$duration;
+
+ }
+
+ /**
+ * @brief Returns a string with a callstack. Can be used for logging.
+ *
+ * @return string
+ */
+ function callstack() {
+ $trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 6);
+
+ // We remove the first two items from the list since they contain data that we don't need.
+ array_shift($trace);
+ array_shift($trace);
+
+ $callstack = array();
+ foreach ($trace AS $func)
+ $callstack[] = $func["function"];
+
+ return implode(", ", $callstack);
}
function mark_timestamp($mark) {
return($this->is_friendica_app);
}
+ /**
+ * @brief Checks if the maximum load is reached
+ *
+ * @return bool Is the load reached?
+ */
+ function maxload_reached() {
+
+ $maxsysload = intval(get_config('system', 'maxloadavg'));
+ if ($maxsysload < 1)
+ $maxsysload = 50;
+
+ $load = current_load();
+ if ($load) {
+ if (intval($load) > $maxsysload) {
+ logger('system: load '.$load.' too high.');
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * @brief Checks if the process is already running
+ *
+ * @param string $taskname The name of the task that will be used for the name of the lockfile
+ * @param string $task The path and name of the php script
+ * @param int $timeout The timeout after which a task should be killed
+ *
+ * @return bool Is the process running?
+ */
+ function is_already_running($taskname, $task = "", $timeout = 540) {
+
+ $lockpath = get_lockpath();
+ if ($lockpath != '') {
+ $pidfile = new pidfile($lockpath, $taskname);
+ if ($pidfile->is_already_running()) {
+ logger("Already running");
+ if ($pidfile->running_time() > $timeout) {
+ $pidfile->kill();
+ logger("killed stale process");
+ // Calling a new instance
+ if ($task != "")
+ proc_run('php', $task);
+ }
+ return true;
+ }
+ }
+ return false;
+ }
}
/**
return true;
}
-
-function check_plugins(&$a) {
-
- /**
- *
- * Synchronise plugins:
- *
- * $a->config['system']['addon'] contains a comma-separated list of names
- * of plugins/addons which are used on this system.
- * Go through the database list of already installed addons, and if we have
- * an entry, but it isn't in the config list, call the uninstall procedure
- * 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.
- *
+/**
+ * @brief Synchronise plugins:
+ *
+ * $a->config['system']['addon'] contains a comma-separated list of names
+ * of plugins/addons which are used on this system.
+ * Go through the database list of already installed addons, and if we have
+ * an entry, but it isn't in the config list, call the uninstall procedure
+ * 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) {
$r = q("SELECT * FROM `addon` WHERE `installed` = 1");
if(count($r))
$prefix = substr($prefix, 0, $size - 22);
return(str_replace(".", "", uniqid($prefix, true)));
} else {
- $prefix = substr($prefix, 0, $size - 13);
+ $prefix = substr($prefix, 0, max($size - 13, 0));
return(uniqid($prefix));
}
}
*
* @return string
* Returns the complete html for inserting into the page
+ *
+ * @hooks 'login_hook'
+ * string $o
*/
function login($register = false, $hiddens=false) {
$a = get_app();
$noid = get_config('system','no_openid');
- $dest_url = $a->get_baseurl(true) . '/' . $a->query_string;
+ $dest_url = $a->query_string;
if(local_user()) {
$tpl = get_markup_template("logout.tpl");
$o .= replace_macros($tpl, array(
- '$dest_url' => $dest_url,
- '$logout' => t('Logout'),
- '$login' => t('Login'),
+ '$dest_url' => $dest_url,
+ '$logout' => t('Logout'),
+ '$login' => t('Login'),
- '$lname' => array('username', t('Nickname or Email address: ') , '', ''),
+ '$lname' => array('username', t('Nickname or Email: ') , '', ''),
'$lpassword' => array('password', t('Password: '), '', ''),
'$lremember' => array('remember', t('Remember me'), 0, ''),
- '$openid' => !$noid,
- '$lopenid' => array('openid_url', t('Or login using OpenID: '),'',''),
+ '$openid' => !$noid,
+ '$lopenid' => array('openid_url', t('Or login using OpenID: '),'',''),
- '$hiddens' => $hiddens,
+ '$hiddens' => $hiddens,
- '$register' => $reg,
+ '$register' => $reg,
'$lostpass' => t('Forgot your password?'),
'$lostlink' => t('Password Reset'),
* @brief Redirect to another URL and terminate this process.
*/
function goaway($s) {
+ if (!strstr(normalise_link($s), "http://"))
+ $s = App::get_baseurl()."/".$s;
+
header("Location: $s");
killme();
}
}
/**
-/* @brief Returns contact id of authenticated site visitor or false
+ * @brief Returns contact id of authenticated site visitor or false
*
* @return int|bool visitor_id or false
*/
}
/**
+ * @brief Wrap calls to proc_close(proc_open()) and call hook
+ * so plugins can take part in process :)
*
- * Wrap calls to proc_close(proc_open()) and call hook
- * so plugins can take part in process :)
- *
- * args:
- * $cmd program to run
- * next args are passed as $cmd command line
- *
+ * @param string $cmd program to run
+ *
+ * next args are passed as $cmd command line
* e.g.: proc_run("ls","-la","/tmp");
*
- * $cmd and string args are surrounded with ""
+ * @note $cmd and string args are surrounded with ""
+ *
+ * @hooks 'proc_run'
+ * array $arr
*/
function proc_run($cmd){
$opts = (($a->profile_uid) ? '?f=&puid=' . $a->profile_uid : '');
if (file_exists('view/theme/' . $t . '/style.php'))
- return($a->get_baseurl() . '/view/theme/' . $t . '/style.pcss' . $opts);
+ return('view/theme/'.$t.'/style.pcss'.$opts);
- return($a->get_baseurl() . '/view/theme/' . $t . '/style.css');
+ return('view/theme/'.$t.'/style.css');
}
function feed_birthday($uid,$tz) {
return false;
}
-
-function load_contact_links($uid) {
-
- $a = get_app();
-
- $ret = array();
-
- if(! $uid || x($a->contacts,'empty'))
- return;
-
- $r = q("SELECT `id`,`network`,`url`,`thumb`, `rel` FROM `contact` WHERE `uid` = %d AND `self` = 0 AND `blocked` = 0 AND `thumb` != ''",
- intval($uid)
- );
- if(count($r)) {
- foreach($r as $rr){
- $url = normalise_link($rr['url']);
- $ret[$url] = $rr;
- }
- } else
- $ret['empty'] = true;
-
- $a->contacts = $ret;
- return;
-}
-
/**
* @brief Returns querystring as string from a mapped array.
*
}
function set_template_engine(&$a, $engine = 'internal') {
-// This function is no longer necessary, but keep it as a wrapper to the class method
-// to avoid breaking themes again unnecessarily
+/// @note This function is no longer necessary, but keep it as a wrapper to the class method
+/// to avoid breaking themes again unnecessarily
$a->set_template_engine($engine);
}
return max($load_arr);
}
+
+/**
+ * @brief get c-style args
+ *
+ * @return int
+ */
+function argc() {
+ return get_app()->argc;
+}
+
+/**
+ * @brief Returns the value of a argv key
+ *
+ * @param int $x argv key
+ * @return string Value of the argv key
+ */
+function argv($x) {
+ if(array_key_exists($x,get_app()->argv))
+ return get_app()->argv[$x];
+
+ return '';
+}
+
+/**
+ * @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
+ * 'reload_uri' => $reload_uri The URI of the content we have to load
+ */
+function infinite_scroll_data($module) {
+
+ if (get_pconfig(local_user(),'system','infinite_scroll')
+ AND ($module == "network") AND ($_GET["mode"] != "minimal")) {
+
+ // get the page number
+ if (is_string($_GET["page"]))
+ $pageno = $_GET["page"];
+ else
+ $pageno = 1;
+
+ $reload_uri = "";
+
+ // try to get the uri from which we load the content
+ foreach ($_GET AS $param => $value)
+ if (($param != "page") AND ($param != "q"))
+ $reload_uri .= "&".$param."=".urlencode($value);
+
+ if (($a->page_offset != "") AND !strstr($reload_uri, "&offset="))
+ $reload_uri .= "&offset=".urlencode($a->page_offset);
+
+ $arr = array("pageno" => $pageno, "reload_uri" => $reload_uri);
+
+ return $arr;
+ }
+}