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