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.

1158 lines
30 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
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
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
  1. <?php
  2. set_time_limit(0);
  3. define ( 'BUILD_ID', 1010 );
  4. define ( 'DFRN_PROTOCOL_VERSION', '2.0' );
  5. define ( 'EOL', "<br />\r\n" );
  6. define ( 'ATOM_TIME', 'Y-m-d\TH:i:s\Z' );
  7. // registration policy
  8. define ( 'REGISTER_CLOSED', 0 );
  9. define ( 'REGISTER_APPROVE', 1 );
  10. define ( 'REGISTER_OPEN', 2 );
  11. // relationship types
  12. define ( 'REL_VIP', 1);
  13. define ( 'REL_FAN', 2);
  14. define ( 'REL_BUD', 3);
  15. // page/profile types
  16. // PAGE_NORMAL is a typical personal profile account
  17. // PAGE_SOAPBOX automatically approves all friend requests as REL_FAN, (readonly)
  18. // PAGE_COMMUNITY automatically approves all friend requests as REL_FAN, but with
  19. // write access to wall and comments (no email and not included in page owner's ACL lists)
  20. // PAGE_FREELOVE automatically approves all friend requests as full friends (REL_BUD).
  21. define ( 'PAGE_NORMAL', 0 );
  22. define ( 'PAGE_SOAPBOX', 1 );
  23. define ( 'PAGE_COMMUNITY', 2 );
  24. define ( 'PAGE_FREELOVE', 3 );
  25. // Maximum number of "people who like (or don't like) this"
  26. // that we will list by name
  27. define ( 'MAX_LIKERS', 75);
  28. // email notification options
  29. define ( 'NOTIFY_INTRO', 0x0001 );
  30. define ( 'NOTIFY_CONFIRM', 0x0002 );
  31. define ( 'NOTIFY_WALL', 0x0004 );
  32. define ( 'NOTIFY_COMMENT', 0x0008 );
  33. define ( 'NOTIFY_MAIL', 0x0010 );
  34. // various namespaces we may need to parse
  35. define ( 'NAMESPACE_DFRN' , 'http://purl.org/macgirvin/dfrn/1.0' );
  36. define ( 'NAMESPACE_THREAD' , 'http://purl.org/syndication/thread/1.0' );
  37. define ( 'NAMESPACE_TOMB' , 'http://purl.org/atompub/tombstones/1.0' );
  38. define ( 'NAMESPACE_ACTIVITY', 'http://activitystrea.ms/spec/1.0/' );
  39. define ( 'NAMESPACE_ACTIVITY_SCHEMA', 'http://activitystrea.ms/schema/1.0/');
  40. define ( 'NAMESPACE_SALMON_ME', 'http://salmon-protocol.org/ns/magic-env');
  41. define ( 'NAMESPACE_OSTATUSSUB', 'http://ostatus.org/schema/1.0/subscribe');
  42. define ( 'NAMESPACE_GEORSS', 'http://www.georss.org/georss');
  43. // activity stream defines
  44. define ( 'ACTIVITY_LIKE', NAMESPACE_ACTIVITY_SCHEMA . 'like' );
  45. define ( 'ACTIVITY_DISLIKE', NAMESPACE_DFRN . '/dislike' );
  46. define ( 'ACTIVITY_OBJ_HEART', NAMESPACE_DFRN . '/heart' );
  47. define ( 'ACTIVITY_FRIEND', NAMESPACE_ACTIVITY_SCHEMA . 'make-friend' );
  48. define ( 'ACTIVITY_POST', NAMESPACE_ACTIVITY_SCHEMA . 'post' );
  49. define ( 'ACTIVITY_UPDATE', NAMESPACE_ACTIVITY_SCHEMA . 'update' );
  50. define ( 'ACTIVITY_OBJ_COMMENT', NAMESPACE_ACTIVITY_SCHEMA . 'comment' );
  51. define ( 'ACTIVITY_OBJ_NOTE', NAMESPACE_ACTIVITY_SCHEMA . 'note' );
  52. define ( 'ACTIVITY_OBJ_PERSON', NAMESPACE_ACTIVITY_SCHEMA . 'person' );
  53. define ( 'ACTIVITY_OBJ_PHOTO', NAMESPACE_ACTIVITY_SCHEMA . 'photo' );
  54. define ( 'ACTIVITY_OBJ_P_PHOTO', NAMESPACE_ACTIVITY_SCHEMA . 'profile-photo' );
  55. define ( 'ACTIVITY_OBJ_ALBUM', NAMESPACE_ACTIVITY_SCHEMA . 'photo-album' );
  56. // item weight for query ordering
  57. define ( 'GRAVITY_PARENT', 0);
  58. define ( 'GRAVITY_LIKE', 3);
  59. define ( 'GRAVITY_COMMENT', 6);
  60. // Our main application structure for the life of this page
  61. // Primarily deals with the URL that got us here
  62. // and tries to make some sense of it, and
  63. // stores our page contents and config storage
  64. // and anything else that might need to be passed around
  65. // before we spit the page out.
  66. if(! class_exists('App')) {
  67. class App {
  68. public $module_loaded = false;
  69. public $config;
  70. public $page;
  71. public $profile;
  72. public $user;
  73. public $cid;
  74. public $contact;
  75. public $content;
  76. public $data;
  77. public $error = false;
  78. public $cmd;
  79. public $argv;
  80. public $argc;
  81. public $module;
  82. public $pager;
  83. public $strings;
  84. public $path;
  85. public $interactive = true;
  86. private $scheme;
  87. private $hostname;
  88. private $baseurl;
  89. private $db;
  90. private $curl_code;
  91. function __construct() {
  92. $this->config = array();
  93. $this->page = array();
  94. $this->pager= array();
  95. $this->scheme = ((isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS'])) ? 'https' : 'http' );
  96. $this->hostname = $_SERVER['SERVER_NAME'];
  97. set_include_path("include/$this->hostname" . PATH_SEPARATOR . 'include' . PATH_SEPARATOR . '.' );
  98. if(substr($_SERVER['QUERY_STRING'],0,2) == "q=")
  99. $_SERVER['QUERY_STRING'] = substr($_SERVER['QUERY_STRING'],2);
  100. $this->cmd = trim($_GET['q'],'/');
  101. $this->argv = explode('/',$this->cmd);
  102. $this->argc = count($this->argv);
  103. if((array_key_exists('0',$this->argv)) && strlen($this->argv[0])) {
  104. $this->module = $this->argv[0];
  105. }
  106. else {
  107. $this->module = 'home';
  108. }
  109. if($this->cmd === '.well-known/host-meta')
  110. require_once('include/hostxrd.php');
  111. $this->pager['page'] = ((x($_GET,'page')) ? $_GET['page'] : 1);
  112. $this->pager['itemspage'] = 50;
  113. $this->pager['start'] = ($this->pager['page'] * $this->pager['itemspage']) - $this->pager['itemspage'];
  114. $this->pager['total'] = 0;
  115. }
  116. function get_baseurl($ssl = false) {
  117. if(strlen($this->baseurl))
  118. return $this->baseurl;
  119. $this->baseurl = (($ssl) ? 'https' : $this->scheme) . "://" . $this->hostname . ((isset($this->path) && strlen($this->path)) ? '/' . $this->path : '' );
  120. return $this->baseurl;
  121. }
  122. function set_baseurl($url) {
  123. $this->baseurl = $url;
  124. $this->hostname = basename($url);
  125. }
  126. function get_hostname() {
  127. return $this->hostname;
  128. }
  129. function set_hostname($h) {
  130. $this->hostname = $h;
  131. }
  132. function set_path($p) {
  133. $this->path = trim(trim($p),'/');
  134. }
  135. function get_path() {
  136. return $this->path;
  137. }
  138. function set_pager_total($n) {
  139. $this->pager['total'] = intval($n);
  140. }
  141. function set_pager_itemspage($n) {
  142. $this->pager['itemspage'] = intval($n);
  143. $this->pager['start'] = ($this->pager['page'] * $this->pager['itemspage']) - $this->pager['itemspage'];
  144. }
  145. function init_pagehead() {
  146. $tpl = load_view_file("view/head.tpl");
  147. $this->page['htmlhead'] = replace_macros($tpl,array(
  148. '$baseurl' => $this->get_baseurl()
  149. ));
  150. }
  151. function set_curl_code($code) {
  152. $this->curl_code = $code;
  153. }
  154. function get_curl_code() {
  155. return $this->curl_code;
  156. }
  157. }}
  158. // retrieve the App structure
  159. // useful in functions which require it but don't get it passed to them
  160. if(! function_exists('get_app')) {
  161. function get_app() {
  162. global $a;
  163. return $a;
  164. }};
  165. // Multi-purpose function to check variable state.
  166. // Usage: x($var) or $x($array,'key')
  167. // returns false if variable/key is not set
  168. // if variable is set, returns 1 if has 'non-zero' value, otherwise returns 0.
  169. // e.g. x('') or x(0) returns 0;
  170. if(! function_exists('x')) {
  171. function x($s,$k = NULL) {
  172. if($k != NULL) {
  173. if((is_array($s)) && (array_key_exists($k,$s))) {
  174. if($s[$k])
  175. return (int) 1;
  176. return (int) 0;
  177. }
  178. return false;
  179. }
  180. else {
  181. if(isset($s)) {
  182. if($s) {
  183. return (int) 1;
  184. }
  185. return (int) 0;
  186. }
  187. return false;
  188. }
  189. }}
  190. // called from db initialisation if db is dead.
  191. if(! function_exists('system_unavailable')) {
  192. function system_unavailable() {
  193. include('system_unavailable.php');
  194. killme();
  195. }}
  196. // Primarily involved with database upgrade, but also sets the
  197. // base url for use in cmdline programs which don't have
  198. // $_SERVER variables.
  199. if(! function_exists('check_config')) {
  200. function check_config(&$a) {
  201. load_config('system');
  202. $build = get_config('system','build');
  203. if(! x($build))
  204. $build = set_config('system','build',BUILD_ID);
  205. $url = get_config('system','url');
  206. if(! x($url))
  207. $url = set_config('system','url',$a->get_baseurl());
  208. if($build != BUILD_ID) {
  209. $stored = intval($build);
  210. $current = intval(BUILD_ID);
  211. if(($stored < $current) && file_exists('update.php')) {
  212. // We're reporting a different version than what is currently installed.
  213. // Run any existing update scripts to bring the database up to current.
  214. require_once('update.php');
  215. for($x = $stored; $x < $current; $x ++) {
  216. if(function_exists('update_' . $x)) {
  217. $func = 'update_' . $x;
  218. $func($a);
  219. }
  220. }
  221. set_config('system','build', BUILD_ID);
  222. }
  223. }
  224. return;
  225. }}
  226. // This is our template processor.
  227. // $s is the string requiring macro substitution.
  228. // $r is an array of key value pairs (search => replace)
  229. // returns substituted string.
  230. // WARNING: this is pretty basic, and doesn't properly handle search strings that are substrings of each other.
  231. // For instance if 'test' => "foo" and 'testing' => "bar", testing could become either bar or fooing,
  232. // depending on the order in which they were declared in the array.
  233. if(! function_exists('replace_macros')) {
  234. function replace_macros($s,$r) {
  235. $search = array();
  236. $replace = array();
  237. if(is_array($r) && count($r)) {
  238. foreach ($r as $k => $v ) {
  239. $search[] = $k;
  240. $replace[] = $v;
  241. }
  242. }
  243. return str_replace($search,$replace,$s);
  244. }}
  245. // load string translation table for alternate language
  246. if(! function_exists('load_translation_table')) {
  247. function load_translation_table($lang) {
  248. global $a;
  249. if(file_exists("view/$lang/strings.php"))
  250. include("view/$lang/strings.php");
  251. }}
  252. // translate string if translation exists
  253. if(! function_exists('t')) {
  254. function t($s) {
  255. $a = get_app();
  256. if($a->strings[$s])
  257. return $a->strings[$s];
  258. return $s;
  259. }}
  260. // curl wrapper. If binary flag is true, return binary
  261. // results.
  262. if(! function_exists('fetch_url')) {
  263. function fetch_url($url,$binary = false) {
  264. $ch = curl_init($url);
  265. if(! $ch) return false;
  266. curl_setopt($ch, CURLOPT_HEADER, 0);
  267. curl_setopt($ch, CURLOPT_FOLLOWLOCATION,true);
  268. curl_setopt($ch, CURLOPT_MAXREDIRS,8);
  269. curl_setopt($ch, CURLOPT_RETURNTRANSFER,true);
  270. // by default we will allow self-signed certs
  271. // but you can override this
  272. $check_cert = get_config('system','verifyssl');
  273. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, (($check_cert) ? true : false));
  274. $prx = get_config('system','proxy');
  275. if(strlen($prx)) {
  276. curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1);
  277. curl_setopt($ch, CURLOPT_PROXY, $prx);
  278. $prxusr = get_config('system','proxyuser');
  279. if(strlen($prxusr))
  280. curl_setopt($ch, CURLOPT_PROXYUSERPWD, $prxusr);
  281. }
  282. if($binary)
  283. curl_setopt($ch, CURLOPT_BINARYTRANSFER,1);
  284. $s = curl_exec($ch);
  285. $info = curl_getinfo($ch);
  286. $a = get_app();
  287. $a->set_curl_code($info['http_code']);
  288. curl_close($ch);
  289. return($s);
  290. }}
  291. // post request to $url. $params is an array of post variables.
  292. if(! function_exists('post_url')) {
  293. function post_url($url,$params) {
  294. $ch = curl_init($url);
  295. if(! $ch) return false;
  296. curl_setopt($ch, CURLOPT_HEADER, 0);
  297. curl_setopt($ch, CURLOPT_FOLLOWLOCATION,true);
  298. curl_setopt($ch, CURLOPT_MAXREDIRS,8);
  299. curl_setopt($ch, CURLOPT_RETURNTRANSFER,true);
  300. curl_setopt($ch, CURLOPT_POST,1);
  301. curl_setopt($ch, CURLOPT_POSTFIELDS,$params);
  302. $check_cert = get_config('system','verifyssl');
  303. curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, (($check_cert) ? true : false));
  304. $prx = get_config('system','proxy');
  305. if(strlen($prx)) {
  306. curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1);
  307. curl_setopt($ch, CURLOPT_PROXY, $prx);
  308. $prxusr = get_config('system','proxyuser');
  309. if(strlen($prxusr))
  310. curl_setopt($ch, CURLOPT_PROXYUSERPWD, $prxusr);
  311. }
  312. $s = curl_exec($ch);
  313. $info = curl_getinfo($ch);
  314. $a = get_app();
  315. $a->set_curl_code($info['http_code']);
  316. curl_close($ch);
  317. return($s);
  318. }}
  319. // random hash, 64 chars
  320. if(! function_exists('random_string')) {
  321. function random_string() {
  322. return(hash('sha256',uniqid(rand(),true)));
  323. }}
  324. // This is our primary input filter. The high bit hack only involved some old
  325. // IE browser, forget which.
  326. // Use this on any text input where angle chars are not valid or permitted
  327. // They will be replaced with safer brackets. This may be filtered further
  328. // if these are not allowed either.
  329. if(! function_exists('notags')) {
  330. function notags($string) {
  331. // protect against :<> with high-bit set
  332. return(str_replace(array("<",">","\xBA","\xBC","\xBE"), array('[',']','','',''), $string));
  333. }}
  334. // use this on "body" or "content" input where angle chars shouldn't be removed,
  335. // and allow them to be safely displayed.
  336. if(! function_exists('escape_tags')) {
  337. function escape_tags($string) {
  338. return(htmlspecialchars($string));
  339. }}
  340. // wrapper for adding a login box. If $register == true provide a registration
  341. // link. This will most always depend on the value of $a->config['register_policy'].
  342. // returns the complete html for inserting into the page
  343. if(! function_exists('login')) {
  344. function login($register = false) {
  345. $o = "";
  346. $register_html = (($register) ? load_view_file("view/register-link.tpl") : "");
  347. if(x($_SESSION,'authenticated')) {
  348. $o = load_view_file("view/logout.tpl");
  349. }
  350. else {
  351. $o = load_view_file("view/login.tpl");
  352. $o = replace_macros($o,array('$register_html' => $register_html ));
  353. }
  354. return $o;
  355. }}
  356. // generate a string that's random, but usually pronounceable.
  357. // used to generate initial passwords
  358. if(! function_exists('autoname')) {
  359. function autoname($len) {
  360. $vowels = array('a','a','ai','au','e','e','e','ee','ea','i','ie','o','ou','u');
  361. if(mt_rand(0,5) == 4)
  362. $vowels[] = 'y';
  363. $cons = array(
  364. 'b','bl','br',
  365. 'c','ch','cl','cr',
  366. 'd','dr',
  367. 'f','fl','fr',
  368. 'g','gh','gl','gr',
  369. 'h',
  370. 'j',
  371. 'k','kh','kl','kr',
  372. 'l',
  373. 'm',
  374. 'n',
  375. 'p','ph','pl','pr',
  376. 'qu',
  377. 'r','rh',
  378. 's','sc','sh','sm','sp','st',
  379. 't','th','tr',
  380. 'v',
  381. 'w','wh',
  382. 'x',
  383. 'z','zh'
  384. );
  385. $midcons = array('ck','ct','gn','ld','lf','lm','lt','mb','mm', 'mn','mp',
  386. 'nd','ng','nk','nt','rn','rp','rt');
  387. $noend = array('bl', 'br', 'cl','cr','dr','fl','fr','gl','gr',
  388. 'kh', 'kl','kr','mn','pl','pr','rh','tr','qu','wh');
  389. $start = mt_rand(0,2);
  390. if($start == 0)
  391. $table = $vowels;
  392. else
  393. $table = $cons;
  394. $word = '';
  395. for ($x = 0; $x < $len; $x ++) {
  396. $r = mt_rand(0,count($table) - 1);
  397. $word .= $table[$r];
  398. if($table == $vowels)
  399. $table = array_merge($cons,$midcons);
  400. else
  401. $table = $vowels;
  402. }
  403. $word = substr($word,0,$len);
  404. foreach($noend as $noe) {
  405. if((strlen($word) > 2) && (substr($word,-2) == $noe)) {
  406. $word = substr($word,0,-1);
  407. break;
  408. }
  409. }
  410. if(substr($word,-1) == 'q')
  411. $word = substr($word,0,-1);
  412. return $word;
  413. }}
  414. // Used to end the current process, after saving session state.
  415. if(! function_exists('killme')) {
  416. function killme() {
  417. session_write_close();
  418. exit;
  419. }}
  420. // redirect to another URL and terminate this process.
  421. if(! function_exists('goaway')) {
  422. function goaway($s) {
  423. header("Location: $s");
  424. killme();
  425. }}
  426. // Generic XML return
  427. // Outputs a basic dfrn XML status structure to STDOUT, with a <status> variable
  428. // of $st and an optional text <message> of $message and terminates the current process.
  429. if(! function_exists('xml_status')) {
  430. function xml_status($st, $message = '') {
  431. if(strlen($message))
  432. $xml_message = "\t<message>" . xmlify($message) . "</message>\r\n";
  433. header( "Content-type: text/xml" );
  434. echo '<?xml version="1.0" encoding="UTF-8"?>'."\r\n";
  435. echo "<result>\r\n\t<status>$st</status>\r\n$xml_message</result>\r\n";
  436. killme();
  437. }}
  438. // Returns the uid of locally logged in user or false.
  439. if(! function_exists('local_user')) {
  440. function local_user() {
  441. if((x($_SESSION,'authenticated')) && (x($_SESSION,'uid')))
  442. return intval($_SESSION['uid']);
  443. return false;
  444. }}
  445. // Returns contact id of authenticated site visitor or false
  446. if(! function_exists('remote_user')) {
  447. function remote_user() {
  448. if((x($_SESSION,'authenticated')) && (x($_SESSION,'visitor_id')))
  449. return intval($_SESSION['visitor_id']);
  450. return false;
  451. }}
  452. // contents of $s are displayed prominently on the page the next time
  453. // a page is loaded. Usually used for errors or alerts.
  454. if(! function_exists('notice')) {
  455. function notice($s) {
  456. $a = get_app();
  457. if($a->interactive)
  458. $_SESSION['sysmsg'] .= $s;
  459. }}
  460. // wrapper around config to limit the text length of an incoming message
  461. if(! function_exists('get_max_import_size')) {
  462. function get_max_import_size() {
  463. global $a;
  464. return ((x($a->config,'max_import_size')) ? $a->config['max_import_size'] : 0 );
  465. }}
  466. // escape text ($str) for XML transport
  467. // returns escaped text.
  468. if(! function_exists('xmlify')) {
  469. function xmlify($str) {
  470. $buffer = '';
  471. for($x = 0; $x < strlen($str); $x ++) {
  472. $char = $str[$x];
  473. switch( $char ) {
  474. case "\r" :
  475. break;
  476. case "&" :
  477. $buffer .= '&amp;';
  478. break;
  479. case "'" :
  480. $buffer .= '&apos;';
  481. break;
  482. case "\"" :
  483. $buffer .= '&quot;';
  484. break;
  485. case '<' :
  486. $buffer .= '&lt;';
  487. break;
  488. case '>' :
  489. $buffer .= '&gt;';
  490. break;
  491. case "\n" :
  492. $buffer .= ' ';
  493. break;
  494. default :
  495. $buffer .= $char;
  496. break;
  497. }
  498. }
  499. $buffer = trim($buffer);
  500. return($buffer);
  501. }}
  502. // undo an xmlify
  503. // pass xml escaped text ($s), returns unescaped text
  504. if(! function_exists('unxmlify')) {
  505. function unxmlify($s) {
  506. $ret = str_replace('&amp;','&', $s);
  507. $ret = str_replace(array('&lt;','&gt;','&quot;','&apos;'),array('<','>','"',"'"),$ret);
  508. return $ret;
  509. }}
  510. // convenience wrapper, reverse the operation "bin2hex"
  511. if(! function_exists('hex2bin')) {
  512. function hex2bin($s) {
  513. return(pack("H*",$s));
  514. }}
  515. // Automatic pagination.
  516. // To use, get the count of total items.
  517. // Then call $a->set_pager_total($number_items);
  518. // Optionally call $a->set_pager_itemspage($n) to the number of items to display on each page
  519. // Then call paginate($a) after the end of the display loop to insert the pager block on the page
  520. // (assuming there are enough items to paginate).
  521. // When using with SQL, the setting LIMIT %d, %d => $a->pager['start'],$a->pager['itemspage']
  522. // will limit the results to the correct items for the current page.
  523. // The actual page handling is then accomplished at the application layer.
  524. if(! function_exists('paginate')) {
  525. function paginate(&$a) {
  526. $o = '';
  527. $stripped = ereg_replace("(&page=[0-9]*)","",$_SERVER['QUERY_STRING']);
  528. $stripped = str_replace('q=','',$stripped);
  529. $stripped = trim($stripped,'/');
  530. $url = $a->get_baseurl() . '/' . $stripped;
  531. if($a->pager['total'] > $a->pager['itemspage']) {
  532. $o .= '<div class="pager">';
  533. if($a->pager['page'] != 1)
  534. $o .= '<span class="pager_prev">'."<a href=\"$url".'&page='.($a->pager['page'] - 1).'">' . t('prev') . '</a></span> ';
  535. $o .= "<span class=\"pager_first\"><a href=\"$url"."&page=1\">" . t('first') . "</a></span> ";
  536. $numpages = $a->pager['total'] / $a->pager['itemspage'];
  537. $numstart = 1;
  538. $numstop = $numpages;
  539. if($numpages > 14) {
  540. $numstart = (($pagenum > 7) ? ($pagenum - 7) : 1);
  541. $numstop = (($pagenum > ($numpages - 7)) ? $numpages : ($numstart + 14));
  542. }
  543. for($i = $numstart; $i <= $numstop; $i++){
  544. if($i == $a->pager['page'])
  545. $o .= '<span class="pager_current">'.(($i < 10) ? '&nbsp;'.$i : $i);
  546. else
  547. $o .= "<span class=\"pager_n\"><a href=\"$url"."&page=$i\">".(($i < 10) ? '&nbsp;'.$i : $i)."</a>";
  548. $o .= '</span> ';
  549. }
  550. if(($a->pager['total'] % $a->pager['itemspage']) != 0) {
  551. if($i == $a->pager['page'])
  552. $o .= '<span class="pager_current">'.(($i < 10) ? '&nbsp;'.$i : $i);
  553. else
  554. $o .= "<span class=\"pager_n\"><a href=\"$url"."&page=$i\">".(($i < 10) ? '&nbsp;'.$i : $i)."</a>";
  555. $o .= '</span> ';
  556. }
  557. $lastpage = (($numpages > intval($numpages)) ? intval($numpages)+1 : $numpages);
  558. $o .= "<span class=\"pager_last\"><a href=\"$url"."&page=$lastpage\">" . t('last') . "</a></span> ";
  559. if(($a->pager['total'] - ($a->pager['itemspage'] * $a->pager['page'])) > 0)
  560. $o .= '<span class="pager_next">'."<a href=\"$url"."&page=".($a->pager['page'] + 1).'">' . t('next') . '</a></span>';
  561. $o .= '</div>'."\r\n";
  562. }
  563. return $o;
  564. }}
  565. // Turn user/group ACLs stored as angle bracketed text into arrays
  566. if(! function_exists('expand_acl')) {
  567. function expand_acl($s) {
  568. // turn string array of angle-bracketed elements into numeric array
  569. // e.g. "<1><2><3>" => array(1,2,3);
  570. $ret = array();
  571. if(strlen($s)) {
  572. $t = str_replace('<','',$s);
  573. $a = explode('>',$t);
  574. foreach($a as $aa) {
  575. if(intval($aa))
  576. $ret[] = intval($aa);
  577. }
  578. }
  579. return $ret;
  580. }}
  581. // Used to wrap ACL elements in angle brackets for storage
  582. if(! function_exists('sanitise_acl')) {
  583. function sanitise_acl(&$item) {
  584. if(intval($item))
  585. $item = '<' . intval(notags(trim($item))) . '>';
  586. else
  587. unset($item);
  588. }}
  589. // retrieve a "family" of config variables from database to cached storage
  590. if(! function_exists('load_config')) {
  591. function load_config($family) {
  592. global $a;
  593. $r = q("SELECT * FROM `config` WHERE `cat` = '%s'",
  594. dbesc($family)
  595. );
  596. if(count($r)) {
  597. foreach($r as $rr) {
  598. $k = $rr['k'];
  599. $a->config[$family][$k] = $rr['v'];
  600. }
  601. }
  602. }}
  603. // get a particular config variable given the family name
  604. // and key. Returns false if not set.
  605. // $instore is only used by the set_config function
  606. // to determine if the key already exists in the DB
  607. // If a key is found in the DB but doesn't exist in
  608. // local config cache, pull it into the cache so we don't have
  609. // to hit the DB again for this item.
  610. if(! function_exists('get_config')) {
  611. function get_config($family, $key, $instore = false) {
  612. global $a;
  613. if(! $instore) {
  614. if(isset($a->config[$family][$key])) {
  615. if($a->config[$family][$key] === '!<unset>!') {
  616. return false;
  617. }
  618. return $a->config[$family][$key];
  619. }
  620. }
  621. $ret = q("SELECT `v` FROM `config` WHERE `cat` = '%s' AND `k` = '%s' LIMIT 1",
  622. dbesc($family),
  623. dbesc($key)
  624. );
  625. if(count($ret)) {
  626. $a->config[$family][$key] = $ret[0]['v'];
  627. return $ret[0]['v'];
  628. }
  629. else {
  630. $a->config[$family][$key] = '!<unset>!';
  631. }
  632. return false;
  633. }}
  634. // Store a config value ($value) in the category ($family)
  635. // under the key ($key)
  636. // Return the value, or false if the database update failed
  637. if(! function_exists('set_config')) {
  638. function set_config($family,$key,$value) {
  639. global $a;
  640. $a->config[$family][$key] = $value;
  641. if(get_config($family,$key,true) === false) {
  642. $ret = q("INSERT INTO `config` ( `cat`, `k`, `v` ) VALUES ( '%s', '%s', '%s' ) ",
  643. dbesc($family),
  644. dbesc($key),
  645. dbesc($value)
  646. );
  647. if($ret)
  648. return $value;
  649. return $ret;
  650. }
  651. $ret = q("UPDATE `config` SET `v` = '%s' WHERE `cat` = '%s' AND `k` = '%s' LIMIT 1",
  652. dbesc($value),
  653. dbesc($family),
  654. dbesc($key)
  655. );
  656. if($ret)
  657. return $value;
  658. return $ret;
  659. }}
  660. // convert an XML document to a normalised, case-corrected array
  661. // used by webfinger
  662. if(! function_exists('convert_xml_element_to_array')) {
  663. function convert_xml_element_to_array($xml_element, &$recursion_depth=0) {
  664. // If we're getting too deep, bail out
  665. if ($recursion_depth > 512) {
  666. return(null);
  667. }
  668. if (!is_string($xml_element) &&
  669. !is_array($xml_element) &&
  670. (get_class($xml_element) == 'SimpleXMLElement')) {
  671. $xml_element_copy = $xml_element;
  672. $xml_element = get_object_vars($xml_element);
  673. }
  674. if (is_array($xml_element)) {
  675. $result_array = array();
  676. if (count($xml_element) <= 0) {
  677. return (trim(strval($xml_element_copy)));
  678. }
  679. foreach($xml_element as $key=>$value) {
  680. $recursion_depth++;
  681. $result_array[strtolower($key)] =
  682. convert_xml_element_to_array($value, $recursion_depth);
  683. $recursion_depth--;
  684. }
  685. if ($recursion_depth == 0) {
  686. $temp_array = $result_array;
  687. $result_array = array(
  688. strtolower($xml_element_copy->getName()) => $temp_array,
  689. );
  690. }
  691. return ($result_array);
  692. } else {
  693. return (trim(strval($xml_element)));
  694. }
  695. }}
  696. // Given an email style address, perform webfinger lookup and
  697. // return the resulting DFRN profile URL, or if no DFRN profile URL
  698. // is located, returns an OStatus subscription template (prefixed
  699. // with the string 'stat:' to identify it as on OStatus template).
  700. // If this isn't an email style address just return $s.
  701. // Return an empty string if email-style addresses but webfinger fails,
  702. // or if the resultant personal XRD doesn't contain a supported
  703. // subscription/friend-request attribute.
  704. if(! function_exists('webfinger_dfrn')) {
  705. function webfinger_dfrn($s) {
  706. if(! strstr($s,'@')) {
  707. return $s;
  708. }
  709. $links = webfinger($s);
  710. if(count($links)) {
  711. foreach($links as $link)
  712. if($link['@attributes']['rel'] === NAMESPACE_DFRN)
  713. return $link['@attributes']['href'];
  714. foreach($links as $link)
  715. if($link['@attributes']['rel'] === NAMESPACE_OSTATUSSUB)
  716. return 'stat:' . $link['@attributes']['template'];
  717. }
  718. return '';
  719. }}
  720. // Given an email style address, perform webfinger lookup and
  721. // return the array of link attributes from the personal XRD file.
  722. // On error/failure return an empty array.
  723. if(! function_exists('webfinger')) {
  724. function webfinger($s) {
  725. $host = '';
  726. if(strstr($s,'@')) {
  727. $host = substr($s,strpos($s,'@') + 1);
  728. }
  729. if(strlen($host)) {
  730. $tpl = fetch_lrdd_template($host);
  731. if(strlen($tpl)) {
  732. $pxrd = str_replace('{uri}', urlencode('acct://'.$s), $tpl);
  733. $links = fetch_xrd_links($pxrd);
  734. if(! count($links)) {
  735. // try without the double slashes
  736. $pxrd = str_replace('{uri}', urlencode('acct:'.$s), $tpl);
  737. $links = fetch_xrd_links($pxrd);
  738. }
  739. return $links;
  740. }
  741. }
  742. return array();
  743. }}
  744. // Given a host name, locate the LRDD template from that
  745. // host. Returns the LRDD template or an empty string on
  746. // error/failure.
  747. if(! function_exists('fetch_lrdd_template')) {
  748. function fetch_lrdd_template($host) {
  749. $tpl = '';
  750. $url = 'http://' . $host . '/.well-known/host-meta' ;
  751. $links = fetch_xrd_links($url);
  752. if(count($links)) {
  753. foreach($links as $link)
  754. if($link['@attributes']['rel'] && $link['@attributes']['rel'] === 'lrdd')
  755. $tpl = $link['@attributes']['template'];
  756. }
  757. if(! strpos($tpl,'{uri}'))
  758. $tpl = '';
  759. return $tpl;
  760. }}
  761. // Given a URL, retrieve the page as an XRD document.
  762. // Return an array of links.
  763. // on error/failure return empty array.
  764. if(! function_exists('fetch_xrd_links')) {
  765. function fetch_xrd_links($url) {
  766. $xml = fetch_url($url);
  767. if (! $xml)
  768. return array();
  769. $h = simplexml_load_string($xml);
  770. $arr = convert_xml_element_to_array($h);
  771. if(isset($arr['xrd']['link'])) {
  772. $link = $arr['xrd']['link'];
  773. if(! isset($link[0]))
  774. $links = array($link);
  775. else
  776. $links = $link;
  777. return $links;
  778. }
  779. return array();
  780. }}
  781. // Convert an ACL array to a storable string
  782. if(! function_exists('perms2str')) {
  783. function perms2str($p) {
  784. $ret = '';
  785. $tmp = $p;
  786. if(is_array($tmp)) {
  787. array_walk($tmp,'sanitise_acl');
  788. $ret = implode('',$tmp);
  789. }
  790. return $ret;
  791. }}
  792. // generate a guaranteed unique (for this domain) item ID for ATOM
  793. // safe from birthday paradox
  794. if(! function_exists('item_new_uri')) {
  795. function item_new_uri($hostname,$uid) {
  796. do {
  797. $dups = false;
  798. $hash = random_string();
  799. $uri = "urn:X-dfrn:" . $hostname . ':' . $uid . ':' . $hash;
  800. $r = q("SELECT `id` FROM `item` WHERE `uri` = '%s' LIMIT 1",
  801. dbesc($uri));
  802. if(count($r))
  803. $dups = true;
  804. } while($dups == true);
  805. return $uri;
  806. }}
  807. // Generate a guaranteed unique photo ID.
  808. // safe from birthday paradox
  809. if(! function_exists('photo_new_resource')) {
  810. function photo_new_resource() {
  811. do {
  812. $found = false;
  813. $resource = hash('md5',uniqid(mt_rand(),true));
  814. $r = q("SELECT `id` FROM `photo` WHERE `resource-id` = '%s' LIMIT 1",
  815. dbesc($resource)
  816. );
  817. if(count($r))
  818. $found = true;
  819. } while($found == true);
  820. return $resource;
  821. }}
  822. // Take a URL from the wild, prepend http:// if necessary
  823. // and check DNS to see if it's real
  824. // return true if it's OK, false if something is wrong with it
  825. if(! function_exists('validate_url')) {
  826. function validate_url(&$url) {
  827. if(substr($url,0,4) != 'http')
  828. $url = 'http://' . $url;
  829. $h = parse_url($url);
  830. if(! $h) {
  831. return false;
  832. }
  833. if(! checkdnsrr($h['host'], 'ANY')) {
  834. return false;
  835. }
  836. return true;
  837. }}
  838. // Check $url against our list of allowed sites,
  839. // wildcards allowed. If allowed_sites is unset return true;
  840. // If url is allowed, return true.
  841. // otherwise, return false
  842. if(! function_exists('allowed_url')) {
  843. function allowed_url($url) {
  844. $h = parse_url($url);
  845. if(! $h) {
  846. return false;
  847. }
  848. $str_allowed = get_config('system','allowed_sites');
  849. if(! $str_allowed)
  850. return true;
  851. $found = false;
  852. $host = strtolower($h['host']);
  853. // always allow our own site
  854. if($host == strtolower($_SERVER['SERVER_NAME']))
  855. return true;
  856. $fnmatch = function_exists('fnmatch');
  857. $allowed = explode(',',$str_allowed);
  858. if(count($allowed)) {
  859. foreach($allowed as $a) {
  860. $pat = strtolower(trim($a));
  861. if(($fnmatch && fnmatch($pat,$host)) || ($pat == $host)) {
  862. $found = true;
  863. break;
  864. }
  865. }
  866. }
  867. return $found;
  868. }}
  869. // check if email address is allowed to register here.
  870. // Compare against our list (wildcards allowed).
  871. // Returns false if not allowed, true if allowed or if
  872. // allowed list is not configured.
  873. if(! function_exists('allowed_email')) {
  874. function allowed_email($email) {
  875. $domain = strtolower(substr($email,strpos($email,'@') + 1));
  876. if(! $domain)
  877. return false;
  878. $str_allowed = get_config('system','allowed_email');
  879. if(! $str_allowed)
  880. return true;
  881. $found = false;
  882. $fnmatch = function_exists('fnmatch');
  883. $allowed = explode(',',$str_allowed);
  884. if(count($allowed)) {
  885. foreach($allowed as $a) {
  886. $pat = strtolower(trim($a));
  887. if(($fnmatch && fnmatch($pat,$host)) || ($pat == $host)) {
  888. $found = true;
  889. break;
  890. }
  891. }
  892. }
  893. return $found;
  894. }}
  895. // Format the like/dislike text for a profile item
  896. // $cnt = number of people who like/dislike the item
  897. // $arr = array of pre-linked names of likers/dislikers
  898. // $type = one of 'like, 'dislike'
  899. // $id = item id
  900. // returns formatted text
  901. if(! function_exists('format_like')) {
  902. function format_like($cnt,$arr,$type,$id) {
  903. if($cnt == 1)
  904. $o .= $arr[0] . (($type === 'like') ? t(' likes this.') : t(' doesn\'t like this.')) . EOL ;
  905. else {
  906. $o .= '<span class="fakelink" onclick="openClose(\'' . $type . 'list-' . $id . '\');" >'
  907. . $cnt . ' ' . t('people') . '</span> ' . (($type === 'like') ? t('like this.') : t('don\'t like this.')) . EOL ;
  908. $total = count($arr);
  909. if($total >= MAX_LIKERS)
  910. $arr = array_slice($arr, 0, MAX_LIKERS - 1);
  911. if($total < MAX_LIKERS)
  912. $arr[count($arr)-1] = t('and') . ' ' . $arr[count($arr)-1];
  913. $str = implode(', ', $arr);
  914. if($total >= MAX_LIKERS)
  915. $str .= t(', and ') . $total - MAX_LIKERS . t(' other people');
  916. $str .= (($type === 'like') ? t(' like this.') : t(' don\'t like this.'));
  917. $o .= "\t" . '<div id="' . $type . 'list-' . $id . '" style="display: none;" >' . $str . '</div>';
  918. }
  919. return $o;
  920. }}
  921. // wrapper to load a view template, checking for alternate
  922. // languages before falling back to the default
  923. if(! function_exists('load_view_file')) {
  924. function load_view_file($s) {
  925. $b = basename($s);
  926. $d = dirname($s);
  927. $lang = get_config('system','language');
  928. if($lang && file_exists("$d/$lang/$b"))
  929. return file_get_contents("$d/$lang/$b");
  930. return file_get_contents($s);
  931. }}