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