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