Friendica Communications Platform (please note that this is a clone of the repository at github, issues are handled there) https://friendi.ca
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2953 lines
79 KiB

11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
11 years ago
10 years ago
11 years ago
10 years ago
11 years ago
11 years ago
10 years ago
11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
10 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
  1. <?php
  2. set_time_limit(0);
  3. ini_set('pcre.backtrack_limit', 250000);
  4. define ( 'FRIENDIKA_VERSION', '2.2.1044' );
  5. define ( 'DFRN_PROTOCOL_VERSION', '2.21' );
  6. define ( 'DB_UPDATE_VERSION', 1075 );
  7. define ( 'EOL', "<br />\r\n" );
  8. define ( 'ATOM_TIME', 'Y-m-d\TH:i:s\Z' );
  9. define ( 'DOWN_ARROW', '&#x21e9;' );
  10. /**
  11. *
  12. * Image storage quality. Lower numbers save space at cost of image detail.
  13. * For ease of upgrade, please do not change here. Change jpeg quality with
  14. * set_config('system','jpeg_quality',n) in .htconfig.php
  15. * where n is netween 1 and 100, and with very poor results below about 50
  16. *
  17. */
  18. define ( 'JPEG_QUALITY', 100 );
  19. /**
  20. * SSL redirection policies
  21. */
  22. define ( 'SSL_POLICY_NONE', 0 );
  23. define ( 'SSL_POLICY_FULL', 1 );
  24. define ( 'SSL_POLICY_SELFSIGN', 2 );
  25. /**
  26. * log levels
  27. */
  28. define ( 'LOGGER_NORMAL', 0 );
  29. define ( 'LOGGER_TRACE', 1 );
  30. define ( 'LOGGER_DEBUG', 2 );
  31. define ( 'LOGGER_DATA', 3 );
  32. define ( 'LOGGER_ALL', 4 );
  33. /**
  34. * registration policies
  35. */
  36. define ( 'REGISTER_CLOSED', 0 );
  37. define ( 'REGISTER_APPROVE', 1 );
  38. define ( 'REGISTER_OPEN', 2 );
  39. /**
  40. * relationship types
  41. * When used in contact records, this indicates that 'uid' has
  42. * this relationship with contact['name']
  43. */
  44. define ( 'REL_VIP', 1); // other person is 'following' us
  45. define ( 'REL_FAN', 2); // we are 'following' other person
  46. define ( 'REL_BUD', 3); // mutual relationship
  47. /**
  48. * Hook array order
  49. */
  50. define ( 'HOOK_HOOK', 0);
  51. define ( 'HOOK_FILE', 1);
  52. define ( 'HOOK_FUNCTION', 2);
  53. /**
  54. *
  55. * page/profile types
  56. *
  57. * PAGE_NORMAL is a typical personal profile account
  58. * PAGE_SOAPBOX automatically approves all friend requests as REL_FAN, (readonly)
  59. * PAGE_COMMUNITY automatically approves all friend requests as REL_FAN, but with
  60. * write access to wall and comments (no email and not included in page owner's ACL lists)
  61. * PAGE_FREELOVE automatically approves all friend requests as full friends (REL_BUD).
  62. *
  63. */
  64. define ( 'PAGE_NORMAL', 0 );
  65. define ( 'PAGE_SOAPBOX', 1 );
  66. define ( 'PAGE_COMMUNITY', 2 );
  67. define ( 'PAGE_FREELOVE', 3 );
  68. /**
  69. * Network and protocol family types
  70. */
  71. define ( 'NETWORK_ZOT', 'zot!'); // Zot!
  72. define ( 'NETWORK_DFRN', 'dfrn'); // Friendika, Mistpark, other DFRN implementations
  73. define ( 'NETWORK_OSTATUS', 'stat'); // status.net, identi.ca, GNU-social, other OStatus implementations
  74. define ( 'NETWORK_FEED', 'feed'); // RSS/Atom feeds with no known "post/notify" protocol
  75. define ( 'NETWORK_DIASPORA', 'dspr'); // Diaspora
  76. define ( 'NETWORK_MAIL', 'mail'); // IMAP/POP
  77. define ( 'NETWORK_FACEBOOK', 'face'); // Facebook API
  78. /**
  79. * Maximum number of "people who like (or don't like) this" that we will list by name
  80. */
  81. define ( 'MAX_LIKERS', 75);
  82. /**
  83. * Communication timeout
  84. */
  85. define ( 'ZCURL_TIMEOUT' , (-1));
  86. /**
  87. * email notification options
  88. */
  89. define ( 'NOTIFY_INTRO', 0x0001 );
  90. define ( 'NOTIFY_CONFIRM', 0x0002 );
  91. define ( 'NOTIFY_WALL', 0x0004 );
  92. define ( 'NOTIFY_COMMENT', 0x0008 );
  93. define ( 'NOTIFY_MAIL', 0x0010 );
  94. /**
  95. * various namespaces we may need to parse
  96. */
  97. define ( 'NAMESPACE_ZOT', 'http://purl.org/macgirvin/zot' );
  98. define ( 'NAMESPACE_DFRN' , 'http://purl.org/macgirvin/dfrn/1.0' );
  99. define ( 'NAMESPACE_THREAD' , 'http://purl.org/syndication/thread/1.0' );
  100. define ( 'NAMESPACE_TOMB' , 'http://purl.org/atompub/tombstones/1.0' );
  101. define ( 'NAMESPACE_ACTIVITY', 'http://activitystrea.ms/spec/1.0/' );
  102. define ( 'NAMESPACE_ACTIVITY_SCHEMA', 'http://activitystrea.ms/schema/1.0/' );
  103. define ( 'NAMESPACE_MEDIA', 'http://purl.org/syndication/atommedia' );
  104. define ( 'NAMESPACE_SALMON_ME', 'http://salmon-protocol.org/ns/magic-env' );
  105. define ( 'NAMESPACE_OSTATUSSUB', 'http://ostatus.org/schema/1.0/subscribe' );
  106. define ( 'NAMESPACE_GEORSS', 'http://www.georss.org/georss' );
  107. define ( 'NAMESPACE_POCO', 'http://portablecontacts.net/spec/1.0' );
  108. define ( 'NAMESPACE_FEED', 'http://schemas.google.com/g/2010#updates-from' );
  109. define ( 'NAMESPACE_OSTATUS', 'http://ostatus.org/schema/1.0' );
  110. define ( 'NAMESPACE_STATUSNET', 'http://status.net/schema/api/1/' );
  111. /**
  112. * activity stream defines
  113. */
  114. define ( 'ACTIVITY_LIKE', NAMESPACE_ACTIVITY_SCHEMA . 'like' );
  115. define ( 'ACTIVITY_DISLIKE', NAMESPACE_DFRN . '/dislike' );
  116. define ( 'ACTIVITY_OBJ_HEART', NAMESPACE_DFRN . '/heart' );
  117. define ( 'ACTIVITY_FRIEND', NAMESPACE_ACTIVITY_SCHEMA . 'make-friend' );
  118. define ( 'ACTIVITY_FOLLOW', NAMESPACE_ACTIVITY_SCHEMA . 'follow' );
  119. define ( 'ACTIVITY_UNFOLLOW', NAMESPACE_ACTIVITY_SCHEMA . 'stop-following' );
  120. define ( 'ACTIVITY_POST', NAMESPACE_ACTIVITY_SCHEMA . 'post' );
  121. define ( 'ACTIVITY_UPDATE', NAMESPACE_ACTIVITY_SCHEMA . 'update' );
  122. define ( 'ACTIVITY_TAG', NAMESPACE_ACTIVITY_SCHEMA . 'tag' );
  123. define ( 'ACTIVITY_OBJ_COMMENT', NAMESPACE_ACTIVITY_SCHEMA . 'comment' );
  124. define ( 'ACTIVITY_OBJ_NOTE', NAMESPACE_ACTIVITY_SCHEMA . 'note' );
  125. define ( 'ACTIVITY_OBJ_PERSON', NAMESPACE_ACTIVITY_SCHEMA . 'person' );
  126. define ( 'ACTIVITY_OBJ_PHOTO', NAMESPACE_ACTIVITY_SCHEMA . 'photo' );
  127. define ( 'ACTIVITY_OBJ_P_PHOTO', NAMESPACE_ACTIVITY_SCHEMA . 'profile-photo' );
  128. define ( 'ACTIVITY_OBJ_ALBUM', NAMESPACE_ACTIVITY_SCHEMA . 'photo-album' );
  129. define ( 'ACTIVITY_OBJ_EVENT', NAMESPACE_ACTIVITY_SCHEMA . 'event' );
  130. /**
  131. * item weight for query ordering
  132. */
  133. define ( 'GRAVITY_PARENT', 0);
  134. define ( 'GRAVITY_LIKE', 3);
  135. define ( 'GRAVITY_COMMENT', 6);
  136. /**
  137. *
  138. * Reverse the effect of magic_quotes_gpc if it is enabled.
  139. * Please disable magic_quotes_gpc so we don't have to do this.
  140. * See http://php.net/manual/en/security.magicquotes.disabling.php
  141. *
  142. */
  143. if (get_magic_quotes_gpc()) {
  144. $process = array(&$_GET, &$_POST, &$_COOKIE, &$_REQUEST);
  145. while (list($key, $val) = each($process)) {
  146. foreach ($val as $k => $v) {
  147. unset($process[$key][$k]);
  148. if (is_array($v)) {
  149. $process[$key][stripslashes($k)] = $v;
  150. $process[] = &$process[$key][stripslashes($k)];
  151. } else {
  152. $process[$key][stripslashes($k)] = stripslashes($v);
  153. }
  154. }
  155. }
  156. unset($process);
  157. }
  158. /*
  159. * translation system
  160. */
  161. require_once("include/pgettext.php");
  162. /**
  163. *
  164. * class: App
  165. *
  166. * Our main application structure for the life of this page
  167. * Primarily deals with the URL that got us here
  168. * and tries to make some sense of it, and
  169. * stores our page contents and config storage
  170. * and anything else that might need to be passed around
  171. * before we spit the page out.
  172. *
  173. */
  174. if(! class_exists('App')) {
  175. class App {
  176. public $module_loaded = false;
  177. public $query_string;
  178. public $config;
  179. public $page;
  180. public $profile;
  181. public $user;
  182. public $cid;
  183. public $contact;
  184. public $contacts;
  185. public $page_contact;
  186. public $content;
  187. public $data;
  188. public $error = false;
  189. public $cmd;
  190. public $argv;
  191. public $argc;
  192. public $module;
  193. public $pager;
  194. public $strings;
  195. public $path;
  196. public $hooks;
  197. public $timezone;
  198. public $interactive = true;
  199. public $plugins;
  200. public $apps;
  201. public $identities;
  202. private $scheme;
  203. private $hostname;
  204. private $baseurl;
  205. private $db;
  206. private $curl_code;
  207. private $curl_headers;
  208. function __construct() {
  209. $this->config = array();
  210. $this->page = array();
  211. $this->pager= array();
  212. $this->query_string = '';
  213. $this->scheme = ((isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'])) ? 'https' : 'http' );
  214. if(x($_SERVER,'SERVER_NAME')) {
  215. $this->hostname = $_SERVER['SERVER_NAME'];
  216. /**
  217. * Figure out if we are running at the top of a domain
  218. * or in a sub-directory and adjust accordingly
  219. */
  220. $path = trim(dirname($_SERVER['SCRIPT_NAME']),'/\\');
  221. if(isset($path) && strlen($path) && ($path != $this->path))
  222. $this->path = $path;
  223. }
  224. set_include_path("include/$this->hostname" . PATH_SEPARATOR . 'include' . PATH_SEPARATOR . '.' );
  225. if((x($_SERVER,'QUERY_STRING')) && substr($_SERVER['QUERY_STRING'],0,2) === "q=")
  226. $this->query_string = substr($_SERVER['QUERY_STRING'],2);
  227. if(x($_GET,'q'))
  228. $this->cmd = trim($_GET['q'],'/\\');
  229. /**
  230. *
  231. * Break the URL path into C style argc/argv style arguments for our
  232. * modules. Given "http://example.com/module/arg1/arg2", $this->argc
  233. * will be 3 (integer) and $this->argv will contain:
  234. * [0] => 'module'
  235. * [1] => 'arg1'
  236. * [2] => 'arg2'
  237. *
  238. *
  239. * There will always be one argument. If provided a naked domain
  240. * URL, $this->argv[0] is set to "home".
  241. *
  242. */
  243. $this->argv = explode('/',$this->cmd);
  244. $this->argc = count($this->argv);
  245. if((array_key_exists('0',$this->argv)) && strlen($this->argv[0])) {
  246. $this->module = str_replace(".", "_", $this->argv[0]);
  247. }
  248. else {
  249. $this->argc = 1;
  250. $this->argv = array('home');
  251. $this->module = 'home';
  252. }
  253. /**
  254. * Special handling for the webfinger/lrdd host XRD file
  255. * Just spit out the contents and exit.
  256. */
  257. if($this->cmd === '.well-known/host-meta') {
  258. require_once('include/hostxrd.php');
  259. hostxrd($this->get_baseurl());
  260. // NOTREACHED
  261. }
  262. /**
  263. * See if there is any page number information, and initialise
  264. * pagination
  265. */
  266. $this->pager['page'] = ((x($_GET,'page')) ? $_GET['page'] : 1);
  267. $this->pager['itemspage'] = 50;
  268. $this->pager['start'] = ($this->pager['page'] * $this->pager['itemspage']) - $this->pager['itemspage'];
  269. $this->pager['total'] = 0;
  270. }
  271. function get_baseurl($ssl = false) {
  272. $scheme = $this->scheme;
  273. if(x($this->config,'ssl_policy')) {
  274. if(($ssl) || ($this->config['ssl_policy'] == SSL_POLICY_FULL))
  275. $scheme = 'https';
  276. if(($this->config['ssl_policy'] == SSL_POLICY_SELFSIGN) && (local_user() || x($_POST,'auth-params')))
  277. $scheme = 'https';
  278. }
  279. $this->baseurl = $scheme . "://" . $this->hostname . ((isset($this->path) && strlen($this->path)) ? '/' . $this->path : '' );
  280. return $this->baseurl;
  281. }
  282. function set_baseurl($url) {
  283. $parsed = @parse_url($url);
  284. $this->baseurl = $url;
  285. if($parsed) {
  286. $this->scheme = $parsed['scheme'];
  287. $this->hostname = $parsed['host'];
  288. if(x($parsed,'port'))
  289. $this->hostname .= ':' . $parsed['port'];
  290. if(x($parsed,'path'))
  291. $this->path = trim($parsed['path'],'\\/');
  292. }
  293. }
  294. function get_hostname() {
  295. return $this->hostname;
  296. }
  297. function set_hostname($h) {
  298. $this->hostname = $h;
  299. }
  300. function set_path($p) {
  301. $this->path = trim(trim($p),'/');
  302. }
  303. function get_path() {
  304. return $this->path;
  305. }
  306. function set_pager_total($n) {
  307. $this->pager['total'] = intval($n);
  308. }
  309. function set_pager_itemspage($n) {
  310. $this->pager['itemspage'] = intval($n);
  311. $this->pager['start'] = ($this->pager['page'] * $this->pager['itemspage']) - $this->pager['itemspage'];
  312. }
  313. function init_pagehead() {
  314. $this->page['title'] = $this->config['sitename'];
  315. $tpl = file_get_contents('view/head.tpl');
  316. $this->page['htmlhead'] = replace_macros($tpl,array(
  317. '$baseurl' => $this->get_baseurl(),
  318. '$generator' => 'Friendika' . ' ' . FRIENDIKA_VERSION,
  319. '$delitem' => t('Delete this item?'),
  320. '$comment' => t('Comment')
  321. ));
  322. }
  323. function set_curl_code($code) {
  324. $this->curl_code = $code;
  325. }
  326. function get_curl_code() {
  327. return $this->curl_code;
  328. }
  329. function set_curl_headers($headers) {
  330. $this->curl_headers = $headers;
  331. }
  332. function get_curl_headers() {
  333. return $this->curl_headers;
  334. }
  335. }}
  336. // retrieve the App structure
  337. // useful in functions which require it but don't get it passed to them
  338. if(! function_exists('get_app')) {
  339. function get_app() {
  340. global $a;
  341. return $a;
  342. }};
  343. // Multi-purpose function to check variable state.
  344. // Usage: x($var) or $x($array,'key')
  345. // returns false if variable/key is not set
  346. // if variable is set, returns 1 if has 'non-zero' value, otherwise returns 0.
  347. // e.g. x('') or x(0) returns 0;
  348. if(! function_exists('x')) {
  349. function x($s,$k = NULL) {
  350. if($k != NULL) {
  351. if((is_array($s)) && (array_key_exists($k,$s))) {
  352. if($s[$k])
  353. return (int) 1;
  354. return (int) 0;
  355. }
  356. return false;
  357. }
  358. else {
  359. if(isset($s)) {
  360. if($s) {
  361. return (int) 1;
  362. }
  363. return (int) 0;
  364. }
  365. return false;
  366. }
  367. }}
  368. // called from db initialisation if db is dead.
  369. if(! function_exists('system_unavailable')) {
  370. function system_unavailable() {
  371. include('system_unavailable.php');
  372. system_down();
  373. killme();
  374. }}
  375. // install and uninstall plugin
  376. if (! function_exists('uninstall_plugin')){
  377. function uninstall_plugin($plugin){
  378. logger("Addons: uninstalling " . $plugin);
  379. q("DELETE FROM `addon` WHERE `name` = '%s' LIMIT 1",
  380. dbesc($plugin)
  381. );
  382. @include_once('addon/' . $plugin . '/' . $plugin . '.php');
  383. if(function_exists($plugin . '_uninstall')) {
  384. $func = $plugin . '_uninstall';
  385. $func();
  386. }
  387. }}
  388. if (! function_exists('install_plugin')){
  389. function install_plugin($plugin){
  390. logger("Addons: installing " . $plugin);
  391. $t = filemtime('addon/' . $plugin . '/' . $plugin . '.php');
  392. @include_once('addon/' . $plugin . '/' . $plugin . '.php');
  393. if(function_exists($plugin . '_install')) {
  394. $func = $plugin . '_install';
  395. $func();
  396. $plugin_admin = (function_exists($plugin."_plugin_admin")?1:0);
  397. $r = q("INSERT INTO `addon` (`name`, `installed`, `timestamp`, `plugin_admin`) VALUES ( '%s', 1, %d , %d ) ",
  398. dbesc($plugin),
  399. intval($t),
  400. $plugin_admin
  401. );
  402. }
  403. }}
  404. // Primarily involved with database upgrade, but also sets the
  405. // base url for use in cmdline programs which don't have
  406. // $_SERVER variables, and synchronising the state of installed plugins.
  407. if(! function_exists('check_config')) {
  408. function check_config(&$a) {
  409. $build = get_config('system','build');
  410. if(! x($build))
  411. $build = set_config('system','build',DB_UPDATE_VERSION);
  412. $url = get_config('system','url');
  413. // if the url isn't set or the stored url is radically different
  414. // than the currently visited url, store the current value accordingly.
  415. // "Radically different" ignores common variations such as http vs https
  416. // and www.example.com vs example.com.
  417. if((! x($url)) || (! link_compare($url,$a->get_baseurl())))
  418. $url = set_config('system','url',$a->get_baseurl());
  419. if($build != DB_UPDATE_VERSION) {
  420. $stored = intval($build);
  421. $current = intval(DB_UPDATE_VERSION);
  422. if(($stored < $current) && file_exists('update.php')) {
  423. // We're reporting a different version than what is currently installed.
  424. // Run any existing update scripts to bring the database up to current.
  425. require_once('update.php');
  426. // make sure that boot.php and update.php are the same release, we might be
  427. // updating right this very second and the correct version of the update.php
  428. // file may not be here yet. This can happen on a very busy site.
  429. if(DB_UPDATE_VERSION == UPDATE_VERSION) {
  430. for($x = $stored; $x < $current; $x ++) {
  431. if(function_exists('update_' . $x)) {
  432. $func = 'update_' . $x;
  433. $func($a);
  434. }
  435. }
  436. set_config('system','build', DB_UPDATE_VERSION);
  437. }
  438. }
  439. }
  440. /**
  441. *
  442. * Synchronise plugins:
  443. *
  444. * $a->config['system']['addon'] contains a comma-separated list of names
  445. * of plugins/addons which are used on this system.
  446. * Go through the database list of already installed addons, and if we have
  447. * an entry, but it isn't in the config list, call the uninstall procedure
  448. * and mark it uninstalled in the database (for now we'll remove it).
  449. * Then go through the config list and if we have a plugin that isn't installed,
  450. * call the install procedure and add it to the database.
  451. *
  452. */
  453. $r = q("SELECT * FROM `addon` WHERE `installed` = 1");
  454. if(count($r))
  455. $installed = $r;
  456. else
  457. $installed = array();
  458. $plugins = get_config('system','addon');
  459. $plugins_arr = array();
  460. if($plugins)
  461. $plugins_arr = explode(',',str_replace(' ', '',$plugins));
  462. $a->plugins = $plugins_arr;
  463. $installed_arr = array();
  464. if(count($installed)) {
  465. foreach($installed as $i) {
  466. if(! in_array($i['name'],$plugins_arr)) {
  467. uninstall_plugin($i['name']);
  468. }
  469. else
  470. $installed_arr[] = $i['name'];
  471. }
  472. }
  473. if(count($plugins_arr)) {
  474. foreach($plugins_arr as $p) {
  475. if(! in_array($p,$installed_arr)) {
  476. install_plugin($p);
  477. }
  478. }
  479. }
  480. load_hooks();
  481. return;
  482. }}
  483. // reload all updated plugins
  484. if(! function_exists('reload_plugins')) {
  485. function reload_plugins() {
  486. $plugins = get_config('system','addon');
  487. if(strlen($plugins)) {
  488. $r = q("SELECT * FROM `addon` WHERE `installed` = 1");
  489. if(count($r))
  490. $installed = $r;
  491. else
  492. $installed = array();
  493. $parr = explode(',',$plugins);
  494. if(count($parr)) {
  495. foreach($parr as $pl) {
  496. $pl = trim($pl);
  497. $t = filemtime('addon/' . $pl . '/' . $pl . '.php');
  498. foreach($installed as $i) {
  499. if(($i['name'] == $pl) && ($i['timestamp'] != $t)) {
  500. logger('Reloading plugin: ' . $i['name']);
  501. @include_once('addon/' . $pl . '/' . $pl . '.php');
  502. if(function_exists($pl . '_uninstall')) {
  503. $func = $pl . '_uninstall';
  504. $func();
  505. }
  506. if(function_exists($pl . '_install')) {
  507. $func = $pl . '_install';
  508. $func();
  509. }
  510. q("UPDATE `addon` SET `timestamp` = %d WHERE `id` = %d LIMIT 1",
  511. intval($t),
  512. intval($i['id'])
  513. );
  514. }
  515. }
  516. }
  517. }
  518. }
  519. }}
  520. // This is our template processor.
  521. // $s is the string requiring macro substitution.
  522. // $r is an array of key value pairs (search => replace)
  523. // returns substituted string.
  524. // WARNING: this is pretty basic, and doesn't properly handle search strings that are substrings of each other.
  525. // For instance if 'test' => "foo" and 'testing' => "bar", testing could become either bar or fooing,
  526. // depending on the order in which they were declared in the array.
  527. require_once("include/template_processor.php");
  528. if(! function_exists('replace_macros')) {
  529. function replace_macros($s,$r) {
  530. global $t;
  531. return $t->replace($s,$r);
  532. }}
  533. // curl wrapper. If binary flag is true, return binary
  534. // results.
  535. if(! function_exists('fetch_url')) {
  536. function fetch_url($url,$binary = false, &$redirects = 0) {
  537. $a = get_app();
  538. $ch = curl_init($url);
  539. if(($redirects > 8) || (! $ch))
  540. return false;
  541. curl_setopt($ch, CURLOPT_HEADER, true);
  542. curl_setopt($ch, CURLOPT_RETURNTRANSFER,true);
  543. curl_setopt($ch, CURLOPT_USERAGENT, "Friendika");
  544. $curl_time = intval(get_config('system','curl_timeout'));
  545. curl_setopt($ch, CURLOPT_TIMEOUT, (($curl_time !== false) ? $curl_time : 60));
  546. // by default we will allow self-signed certs
  547. // but you can override this
  548. $check_cert = get_config('system','verifyssl');
  549. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, (($check_cert) ? true : false));
  550. $prx = get_config('system','proxy');
  551. if(strlen($prx)) {
  552. curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1);
  553. curl_setopt($ch, CURLOPT_PROXY, $prx);
  554. $prxusr = get_config('system','proxyuser');
  555. if(strlen($prxusr))
  556. curl_setopt($ch, CURLOPT_PROXYUSERPWD, $prxusr);
  557. }
  558. if($binary)
  559. curl_setopt($ch, CURLOPT_BINARYTRANSFER,1);
  560. $a->set_curl_code(0);
  561. // don't let curl abort the entire application
  562. // if it throws any errors.
  563. $s = @curl_exec($ch);
  564. $base = $s;
  565. $curl_info = curl_getinfo($ch);
  566. $http_code = $curl_info['http_code'];
  567. $header = '';
  568. // Pull out multiple headers, e.g. proxy and continuation headers
  569. // allow for HTTP/2.x without fixing code
  570. while(preg_match('/^HTTP\/[1-2].+? [1-5][0-9][0-9]/',$base)) {
  571. $chunk = substr($base,0,strpos($base,"\r\n\r\n")+4);
  572. $header .= $chunk;
  573. $base = substr($base,strlen($chunk));
  574. }
  575. if($http_code == 301 || $http_code == 302 || $http_code == 303 || $http_code == 307) {
  576. $matches = array();
  577. preg_match('/(Location:|URI:)(.*?)\n/', $header, $matches);
  578. $url = trim(array_pop($matches));
  579. $url_parsed = @parse_url($url);
  580. if (isset($url_parsed)) {
  581. $redirects++;
  582. return fetch_url($url,$binary,$redirects);
  583. }
  584. }
  585. $a->set_curl_code($http_code);
  586. $body = substr($s,strlen($header));
  587. $a->set_curl_headers($header);
  588. curl_close($ch);
  589. return($body);
  590. }}
  591. // post request to $url. $params is an array of post variables.
  592. if(! function_exists('post_url')) {
  593. function post_url($url,$params, $headers = null, &$redirects = 0) {
  594. $a = get_app();
  595. $ch = curl_init($url);
  596. if(($redirects > 8) || (! $ch))
  597. return false;
  598. curl_setopt($ch, CURLOPT_HEADER, true);
  599. curl_setopt($ch, CURLOPT_RETURNTRANSFER,true);
  600. curl_setopt($ch, CURLOPT_POST,1);
  601. curl_setopt($ch, CURLOPT_POSTFIELDS,$params);
  602. curl_setopt($ch, CURLOPT_USERAGENT, "Friendika");
  603. $curl_time = intval(get_config('system','curl_timeout'));
  604. curl_setopt($ch, CURLOPT_TIMEOUT, (($curl_time !== false) ? $curl_time : 60));
  605. if(defined('LIGHTTPD')) {
  606. if(!is_array($headers)) {
  607. $headers = array('Expect:');
  608. } else {
  609. if(!in_array('Expect:', $headers)) {
  610. array_push($headers, 'Expect:');
  611. }
  612. }
  613. }
  614. if($headers)
  615. curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
  616. $check_cert = get_config('system','verifyssl');
  617. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, (($check_cert) ? true : false));
  618. $prx = get_config('system','proxy');
  619. if(strlen($prx)) {
  620. curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1);
  621. curl_setopt($ch, CURLOPT_PROXY, $prx);
  622. $prxusr = get_config('system','proxyuser');
  623. if(strlen($prxusr))
  624. curl_setopt($ch, CURLOPT_PROXYUSERPWD, $prxusr);
  625. }
  626. $a->set_curl_code(0);
  627. // don't let curl abort the entire application
  628. // if it throws any errors.
  629. $s = @curl_exec($ch);
  630. $base = $s;
  631. $curl_info = curl_getinfo($ch);
  632. $http_code = $curl_info['http_code'];
  633. $header = '';
  634. // Pull out multiple headers, e.g. proxy and continuation headers
  635. // allow for HTTP/2.x without fixing code
  636. while(preg_match('/^HTTP\/[1-2].+? [1-5][0-9][0-9]/',$base)) {
  637. $chunk = substr($base,0,strpos($base,"\r\n\r\n")+4);
  638. $header .= $chunk;
  639. $base = substr($base,strlen($chunk));
  640. }
  641. if($http_code == 301 || $http_code == 302 || $http_code == 303) {
  642. $matches = array();
  643. preg_match('/(Location:|URI:)(.*?)\n/', $header, $matches);
  644. $url = trim(array_pop($matches));
  645. $url_parsed = @parse_url($url);
  646. if (isset($url_parsed)) {
  647. $redirects++;
  648. return post_url($url,$binary,$headers,$redirects);
  649. }
  650. }
  651. $a->set_curl_code($http_code);
  652. $body = substr($s,strlen($header));
  653. $a->set_curl_headers($header);
  654. curl_close($ch);
  655. return($body);
  656. }}
  657. // random hash, 64 chars
  658. if(! function_exists('random_string')) {
  659. function random_string() {
  660. return(hash('sha256',uniqid(rand(),true)));
  661. }}
  662. /**
  663. * This is our primary input filter.
  664. *
  665. * The high bit hack only involved some old IE browser, forget which (IE5/Mac?)
  666. * that had an XSS attack vector due to stripping the high-bit on an 8-bit character
  667. * after cleansing, and angle chars with the high bit set could get through as markup.
  668. *
  669. * This is now disabled because it was interfering with some legitimate unicode sequences
  670. * and hopefully there aren't a lot of those browsers left.
  671. *
  672. * Use this on any text input where angle chars are not valid or permitted
  673. * They will be replaced with safer brackets. This may be filtered further
  674. * if these are not allowed either.
  675. *
  676. */
  677. if(! function_exists('notags')) {
  678. function notags($string) {
  679. return(str_replace(array("<",">"), array('[',']'), $string));
  680. // High-bit filter no longer used
  681. // return(str_replace(array("<",">","\xBA","\xBC","\xBE"), array('[',']','','',''), $string));
  682. }}
  683. // use this on "body" or "content" input where angle chars shouldn't be removed,
  684. // and allow them to be safely displayed.
  685. if(! function_exists('escape_tags')) {
  686. function escape_tags($string) {
  687. return(htmlspecialchars($string));
  688. }}
  689. // wrapper for adding a login box. If $register == true provide a registration
  690. // link. This will most always depend on the value of $a->config['register_policy'].
  691. // returns the complete html for inserting into the page
  692. if(! function_exists('login')) {
  693. function login($register = false) {
  694. $o = "";
  695. $register_tpl = (($register) ? get_markup_template("register-link.tpl") : "");
  696. $register_html = replace_macros($register_tpl,array(
  697. '$title' => t('Create a New Account'),
  698. '$desc' => t('Register')
  699. ));
  700. $noid = get_config('system','no_openid');
  701. if($noid) {
  702. $classname = 'no-openid';
  703. $namelabel = t('Nickname or Email address: ');
  704. $passlabel = t('Password: ');
  705. $login = t('Login');
  706. }
  707. else {
  708. $classname = 'openid';
  709. $namelabel = t('Nickname/Email/OpenID: ');
  710. $passlabel = t("Password \x28if not OpenID\x29: ");
  711. $login = t('Login');
  712. }
  713. $lostpass = t('Forgot your password?');
  714. $lostlink = t('Password Reset');
  715. if(local_user()) {
  716. $tpl = get_markup_template("logout.tpl");
  717. }
  718. else {
  719. $tpl = get_markup_template("login.tpl");
  720. }
  721. $o = '<script type="text/javascript"> $(document).ready(function() { $("#login-name").focus();} );</script>';
  722. $o .= replace_macros($tpl,array(
  723. '$logout' => t('Logout'),
  724. '$register_html' => $register_html,
  725. '$classname' => $classname,
  726. '$namelabel' => $namelabel,
  727. '$passlabel' => $passlabel,
  728. '$login' => $login,
  729. '$lostpass' => $lostpass,
  730. '$lostlink' => $lostlink
  731. ));
  732. return $o;
  733. }}
  734. // generate a string that's random, but usually pronounceable.
  735. // used to generate initial passwords
  736. if(! function_exists('autoname')) {
  737. function autoname($len) {
  738. $vowels = array('a','a','ai','au','e','e','e','ee','ea','i','ie','o','ou','u');
  739. if(mt_rand(0,5) == 4)
  740. $vowels[] = 'y';
  741. $cons = array(
  742. 'b','bl','br',
  743. 'c','ch','cl','cr',
  744. 'd','dr',
  745. 'f','fl','fr',
  746. 'g','gh','gl','gr',
  747. 'h',
  748. 'j',
  749. 'k','kh','kl','kr',
  750. 'l',
  751. 'm',
  752. 'n',
  753. 'p','ph','pl','pr',
  754. 'qu',
  755. 'r','rh',
  756. 's','sc','sh','sm','sp','st',
  757. 't','th','tr',
  758. 'v',
  759. 'w','wh',
  760. 'x',
  761. 'z','zh'
  762. );
  763. $midcons = array('ck','ct','gn','ld','lf','lm','lt','mb','mm', 'mn','mp',
  764. 'nd','ng','nk','nt','rn','rp','rt');
  765. $noend = array('bl', 'br', 'cl','cr','dr','fl','fr','gl','gr',
  766. 'kh', 'kl','kr','mn','pl','pr','rh','tr','qu','wh');
  767. $start = mt_rand(0,2);
  768. if($start == 0)
  769. $table = $vowels;
  770. else
  771. $table = $cons;
  772. $word = '';
  773. for ($x = 0; $x < $len; $x ++) {
  774. $r = mt_rand(0,count($table) - 1);
  775. $word .= $table[$r];
  776. if($table == $vowels)
  777. $table = array_merge($cons,$midcons);
  778. else
  779. $table = $vowels;
  780. }
  781. $word = substr($word,0,$len);
  782. foreach($noend as $noe) {
  783. if((strlen($word) > 2) && (substr($word,-2) == $noe)) {
  784. $word = substr($word,0,-1);
  785. break;
  786. }
  787. }
  788. if(substr($word,-1) == 'q')
  789. $word = substr($word,0,-1);
  790. return $word;
  791. }}
  792. // Used to end the current process, after saving session state.
  793. if(! function_exists('killme')) {
  794. function killme() {
  795. session_write_close();
  796. exit;
  797. }}
  798. // redirect to another URL and terminate this process.
  799. if(! function_exists('goaway')) {
  800. function goaway($s) {
  801. header("Location: $s");
  802. killme();
  803. }}
  804. // Generic XML return
  805. // Outputs a basic dfrn XML status structure to STDOUT, with a <status> variable
  806. // of $st and an optional text <message> of $message and terminates the current process.
  807. if(! function_exists('xml_status')) {
  808. function xml_status($st, $message = '') {
  809. $xml_message = ((strlen($message)) ? "\t<message>" . xmlify($message) . "</message>\r\n" : '');
  810. if($st)
  811. logger('xml_status returning non_zero: ' . $st . " message=" . $message);
  812. header( "Content-type: text/xml" );
  813. echo '<?xml version="1.0" encoding="UTF-8"?>'."\r\n";
  814. echo "<result>\r\n\t<status>$st</status>\r\n$xml_message</result>\r\n";
  815. killme();
  816. }}
  817. // Returns the uid of locally logged in user or false.
  818. if(! function_exists('local_user')) {
  819. function local_user() {
  820. if((x($_SESSION,'authenticated')) && (x($_SESSION,'uid')))
  821. return intval($_SESSION['uid']);
  822. return false;
  823. }}
  824. // Returns contact id of authenticated site visitor or false
  825. if(! function_exists('remote_user')) {
  826. function remote_user() {
  827. if((x($_SESSION,'authenticated')) && (x($_SESSION,'visitor_id')))
  828. return intval($_SESSION['visitor_id']);
  829. return false;
  830. }}
  831. // contents of $s are displayed prominently on the page the next time
  832. // a page is loaded. Usually used for errors or alerts.
  833. if(! function_exists('notice')) {
  834. function notice($s) {
  835. $a = get_app();
  836. if($a->interactive)
  837. $_SESSION['sysmsg'] .= $s;
  838. }}
  839. if(! function_exists('info')) {
  840. function info($s) {
  841. $a = get_app();
  842. if($a->interactive)
  843. $_SESSION['sysmsg_info'] .= $s;
  844. }}
  845. // wrapper around config to limit the text length of an incoming message
  846. if(! function_exists('get_max_import_size')) {
  847. function get_max_import_size() {
  848. global $a;
  849. return ((x($a->config,'max_import_size')) ? $a->config['max_import_size'] : 0 );
  850. }}
  851. // escape text ($str) for XML transport
  852. // returns escaped text.
  853. if(! function_exists('xmlify')) {
  854. function xmlify($str) {
  855. $buffer = '';
  856. for($x = 0; $x < strlen($str); $x ++) {
  857. $char = $str[$x];
  858. switch( $char ) {
  859. case "\r" :
  860. break;
  861. case "&" :
  862. $buffer .= '&amp;';
  863. break;
  864. case "'" :
  865. $buffer .= '&apos;';
  866. break;
  867. case "\"" :
  868. $buffer .= '&quot;';
  869. break;
  870. case '<' :
  871. $buffer .= '&lt;';
  872. break;
  873. case '>' :
  874. $buffer .= '&gt;';
  875. break;
  876. case "\n" :
  877. $buffer .= "\n";
  878. break;
  879. default :
  880. $buffer .= $char;
  881. break;
  882. }
  883. }
  884. $buffer = trim($buffer);
  885. return($buffer);
  886. }}
  887. // undo an xmlify
  888. // pass xml escaped text ($s), returns unescaped text
  889. if(! function_exists('unxmlify')) {
  890. function unxmlify($s) {
  891. $ret = str_replace('&amp;','&', $s);
  892. $ret = str_replace(array('&lt;','&gt;','&quot;','&apos;'),array('<','>','"',"'"),$ret);
  893. return $ret;
  894. }}
  895. // convenience wrapper, reverse the operation "bin2hex"
  896. if(! function_exists('hex2bin')) {
  897. function hex2bin($s) {
  898. if(! ctype_xdigit($s)) {
  899. logger('hex2bin: illegal input: ' . print_r(debug_backtrace(), true));
  900. return($s);
  901. }
  902. return(pack("H*",$s));
  903. }}
  904. // Automatic pagination.
  905. // To use, get the count of total items.
  906. // Then call $a->set_pager_total($number_items);
  907. // Optionally call $a->set_pager_itemspage($n) to the number of items to display on each page
  908. // Then call paginate($a) after the end of the display loop to insert the pager block on the page
  909. // (assuming there are enough items to paginate).
  910. // When using with SQL, the setting LIMIT %d, %d => $a->pager['start'],$a->pager['itemspage']
  911. // will limit the results to the correct items for the current page.
  912. // The actual page handling is then accomplished at the application layer.
  913. if(! function_exists('paginate')) {
  914. function paginate(&$a) {
  915. $o = '';
  916. $stripped = preg_replace('/(&page=[0-9]*)/','',$a->query_string);
  917. $stripped = str_replace('q=','',$stripped);
  918. $stripped = trim($stripped,'/');
  919. $pagenum = $a->pager['page'];
  920. $url = $a->get_baseurl() . '/' . $stripped;
  921. if($a->pager['total'] > $a->pager['itemspage']) {
  922. $o .= '<div class="pager">';
  923. if($a->pager['page'] != 1)
  924. $o .= '<span class="pager_prev">'."<a href=\"$url".'&page='.($a->pager['page'] - 1).'">' . t('prev') . '</a></span> ';
  925. $o .= "<span class=\"pager_first\"><a href=\"$url"."&page=1\">" . t('first') . "</a></span> ";
  926. $numpages = $a->pager['total'] / $a->pager['itemspage'];
  927. $numstart = 1;
  928. $numstop = $numpages;
  929. if($numpages > 14) {
  930. $numstart = (($pagenum > 7) ? ($pagenum - 7) : 1);
  931. $numstop = (($pagenum > ($numpages - 7)) ? $numpages : ($numstart + 14));
  932. }
  933. for($i = $numstart; $i <= $numstop; $i++){
  934. if($i == $a->pager['page'])
  935. $o .= '<span class="pager_current">'.(($i < 10) ? '&nbsp;'.$i : $i);
  936. else
  937. $o .= "<span class=\"pager_n\"><a href=\"$url"."&page=$i\">".(($i < 10) ? '&nbsp;'.$i : $i)."</a>";
  938. $o .= '</span> ';
  939. }
  940. if(($a->pager['total'] % $a->pager['itemspage']) != 0) {
  941. if($i == $a->pager['page'])
  942. $o .= '<span class="pager_current">'.(($i < 10) ? '&nbsp;'.$i : $i);
  943. else
  944. $o .= "<span class=\"pager_n\"><a href=\"$url"."&page=$i\">".(($i < 10) ? '&nbsp;'.$i : $i)."</a>";
  945. $o .= '</span> ';
  946. }
  947. $lastpage = (($numpages > intval($numpages)) ? intval($numpages)+1 : $numpages);
  948. $o .= "<span class=\"pager_last\"><a href=\"$url"."&page=$lastpage\">" . t('last') . "</a></span> ";
  949. if(($a->pager['total'] - ($a->pager['itemspage'] * $a->pager['page'])) > 0)
  950. $o .= '<span class="pager_next">'."<a href=\"$url"."&page=".($a->pager['page'] + 1).'">' . t('next') . '</a></span>';
  951. $o .= '</div>'."\r\n";
  952. }
  953. return $o;
  954. }}
  955. // Turn user/group ACLs stored as angle bracketed text into arrays
  956. if(! function_exists('expand_acl')) {
  957. function expand_acl($s) {
  958. // turn string array of angle-bracketed elements into numeric array
  959. // e.g. "<1><2><3>" => array(1,2,3);
  960. $ret = array();
  961. if(strlen($s)) {
  962. $t = str_replace('<','',$s);
  963. $a = explode('>',$t);
  964. foreach($a as $aa) {
  965. if(intval($aa))
  966. $ret[] = intval($aa);
  967. }
  968. }
  969. return $ret;
  970. }}
  971. // Used to wrap ACL elements in angle brackets for storage
  972. if(! function_exists('sanitise_acl')) {
  973. function sanitise_acl(&$item) {
  974. if(intval($item))
  975. $item = '<' . intval(notags(trim($item))) . '>';
  976. else
  977. unset($item);
  978. }}
  979. // retrieve a "family" of config variables from database to cached storage
  980. if(! function_exists('load_config')) {
  981. function load_config($family) {
  982. global $a;
  983. $r = q("SELECT * FROM `config` WHERE `cat` = '%s'",
  984. dbesc($family)
  985. );
  986. if(count($r)) {
  987. foreach($r as $rr) {
  988. $k = $rr['k'];
  989. if ($rr['cat'] === 'config') {
  990. $a->config[$k] = $rr['v'];
  991. } else {
  992. $a->config[$family][$k] = $rr['v'];
  993. }
  994. }
  995. }
  996. }}
  997. // get a particular config variable given the family name
  998. // and key. Returns false if not set.
  999. // $instore is only used by the set_config function
  1000. // to determine if the key already exists in the DB
  1001. // If a key is found in the DB but doesn't exist in
  1002. // local config cache, pull it into the cache so we don't have
  1003. // to hit the DB again for this item.
  1004. if(! function_exists('get_config')) {
  1005. function get_config($family, $key, $instore = false) {
  1006. global $a;
  1007. if(! $instore) {
  1008. if(isset($a->config[$family][$key])) {
  1009. if($a->config[$family][$key] === '!<unset>!') {
  1010. return false;
  1011. }
  1012. return $a->config[$family][$key];
  1013. }
  1014. }
  1015. $ret = q("SELECT `v` FROM `config` WHERE `cat` = '%s' AND `k` = '%s' LIMIT 1",
  1016. dbesc($family),
  1017. dbesc($key)
  1018. );
  1019. if(count($ret)) {
  1020. // manage array value
  1021. $val = (preg_match("|^a:[0-9]+:{.*}$|", $ret[0]['v'])?unserialize( $ret[0]['v']):$ret[0]['v']);
  1022. $a->config[$family][$key] = $val;
  1023. return $val;
  1024. }
  1025. else {
  1026. $a->config[$family][$key] = '!<unset>!';
  1027. }
  1028. return false;
  1029. }}
  1030. // Store a config value ($value) in the category ($family)
  1031. // under the key ($key)
  1032. // Return the value, or false if the database update failed
  1033. if(! function_exists('set_config')) {
  1034. function set_config($family,$key,$value) {
  1035. global $a;
  1036. // manage array value
  1037. $dbvalue = (is_array($value)?serialize($value):$value);
  1038. if(get_config($family,$key,true) === false) {
  1039. $a->config[$family][$key] = $value;
  1040. $ret = q("INSERT INTO `config` ( `cat`, `k`, `v` ) VALUES ( '%s', '%s', '%s' ) ",
  1041. dbesc($family),
  1042. dbesc($key),
  1043. dbesc($dbvalue)
  1044. );
  1045. if($ret)
  1046. return $value;
  1047. return $ret;
  1048. }
  1049. $ret = q("UPDATE `config` SET `v` = '%s' WHERE `cat` = '%s' AND `k` = '%s' LIMIT 1",
  1050. dbesc($dbvalue),
  1051. dbesc($family),
  1052. dbesc(