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