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