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