]> git.mxchange.org Git - friendica.git/blob - boot.php
bd6d6cc867702ac2bee2ddc1b7738edb119b48c9
[friendica.git] / boot.php
1 <?php
2
3 set_time_limit(0);
4
5 define ( 'BUILD_ID' , 1007 );
6
7 define ( 'EOL', "<br />\r\n");
8 define ( 'ATOM_TIME',  'Y-m-d\TH:i:s\Z' );
9
10 define ( 'REGISTER_CLOSED',  0);
11 define ( 'REGISTER_APPROVE', 1);
12 define ( 'REGISTER_OPEN',    2);
13
14 // relationship types
15
16 define ( 'REL_VIP',        1);
17 define ( 'REL_FAN',        2);
18 define ( 'REL_BUD',        3);
19
20 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                         // 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 dfrn XML status structure to STDOUT, with a <status> variable 
503 // of $st and an optional text <message> of $message and terminates the current process. 
504
505 if(! function_exists('xml_status')) {
506 function xml_status($st, $message = '') {
507
508         if(strlen($message))
509                 $xml_message = "\t<message>" . xmlify($message) . "</message>\r\n";
510
511         header( "Content-type: text/xml" );
512         echo '<?xml version="1.0" encoding="UTF-8"?>'."\r\n";
513         echo "<result>\r\n\t<status>$st</status>\r\n$xml_message</result>\r\n";
514         killme();
515 }}
516
517 // Returns the uid of locally logged on user or false.
518
519 if(! function_exists('local_user')) {
520 function local_user() {
521         if((x($_SESSION,'authenticated')) && (x($_SESSION,'uid')))
522                 return $_SESSION['uid'];
523         return false;
524 }}
525
526 // Returns contact id of authenticated site visitor or false
527
528 if(! function_exists('remote_user')) {
529 function remote_user() {
530         if((x($_SESSION,'authenticated')) && (x($_SESSION,'visitor_id')))
531                 return $_SESSION['visitor_id'];
532         return false;
533 }}
534
535 // contents of $s are displayed prominently on the page the next time
536 // a page is loaded. Usually used for errors or alerts.
537
538 if(! function_exists('notice')) {
539 function notice($s) {
540
541         $_SESSION['sysmsg'] .= $s;
542
543 }}
544
545 // wrapper around config to limit the text length of an incoming message
546
547 if(! function_exists('get_max_import_size')) {
548 function get_max_import_size() {
549         global $a;
550         return ((x($a->config,'max_import_size')) ? $a->config['max_import_size'] : 0 );
551 }}
552
553
554 // escape text ($str) for XML transport
555 // returns escaped text.
556
557 if(! function_exists('xmlify')) {
558 function xmlify($str) {
559         $buffer = '';
560         
561         for($x = 0; $x < strlen($str); $x ++) {
562                 $char = $str[$x];
563         
564                 switch( $char ) {
565
566                         case "\r" :
567                                 break;
568                         case "&" :
569                                 $buffer .= '&amp;';
570                                 break;
571                         case "'" :
572                                 $buffer .= '&apos;';
573                                 break;
574
575                         case "\"" :
576                                 $buffer .= '&quot;';
577                                 break;
578                         case '<' :
579                                 $buffer .= '&lt;';
580                                 break;
581                         case '>' :
582                                 $buffer .= '&gt;';
583                                 break;
584                         case "\n" :
585                                 $buffer .= ' ';
586                                 break;
587                         default :
588                                 $buffer .= $char;
589                                 break;
590                 }       
591         }
592         $buffer = trim($buffer);
593         return($buffer);
594 }}
595
596 // undo an xmlify
597 // pass xml escaped text ($s), returns unescaped text
598
599 if(! function_exists('unxmlify')) {
600 function unxmlify($s) {
601         $ret = str_replace('&amp;','&', $s);
602         $ret = str_replace(array('&lt;','&gt;','&quot;','&apos;'),array('<','>','"',"'"),$ret);
603         return $ret;    
604 }}
605
606 // convenience wrapper, reverse the operation "bin2hex"
607
608 if(! function_exists('hex2bin')) {
609 function hex2bin($s) {
610         return(pack("H*",$s));
611 }}
612
613 // Automatic pagination.
614 // To use, get the count of total items.
615 // Then call $a->set_pager_total($number_items);
616 // Optionally call $a->set_pager_itemspage($n) to the number of items to display on each page
617 // Then call paginate($a) after the end of the display loop to insert the pager block on the page
618 // (assuming there are enough items to paginate).
619 // When using with SQL, the setting LIMIT %d, %d => $a->pager['start'],$a->pager['itemspage']
620 // will limit the results to the correct items for the current page. 
621 // The actual page handling is then accomplished at the application layer. 
622
623 if(! function_exists('paginate')) {
624 function paginate(&$a) {
625         $o = '';
626         $stripped = ereg_replace("(&page=[0-9]*)","",$_SERVER['QUERY_STRING']);
627         $stripped = str_replace('q=','',$stripped);
628         $stripped = trim($stripped,'/');
629         $url = $a->get_baseurl() . '/' . $stripped;
630
631
632           if($a->pager['total'] > $a->pager['itemspage']) {
633                 $o .= '<div class="pager">';
634                 if($a->pager['page'] != 1)
635                         $o .= '<span class="pager_prev">'."<a href=\"$url".'&page='.($a->pager['page'] - 1).'">' . t('prev') . '</a></span> ';
636
637                 $o .=  "<span class=\"pager_first\"><a href=\"$url"."&page=1\">" . t('first') . "</a></span> ";
638
639                 $numpages = $a->pager['total'] / $a->pager['itemspage'];
640
641                 $numstart = 1;
642                 $numstop = $numpages;
643
644                 if($numpages > 14) {
645                         $numstart = (($pagenum > 7) ? ($pagenum - 7) : 1);
646                         $numstop = (($pagenum > ($numpages - 7)) ? $numpages : ($numstart + 14));
647                 }
648    
649                 for($i = $numstart; $i <= $numstop; $i++){
650                         if($i == $a->pager['page'])
651                                 $o .= '<span class="pager_current">'.(($i < 10) ? '&nbsp;'.$i : $i);
652                         else
653                                 $o .= "<span class=\"pager_n\"><a href=\"$url"."&page=$i\">".(($i < 10) ? '&nbsp;'.$i : $i)."</a>";
654                         $o .= '</span> ';
655                 }
656
657                 if(($a->pager['total'] % $a->pager['itemspage']) != 0) {
658                         if($i == $a->pager['page'])
659                                 $o .= '<span class="pager_current">'.(($i < 10) ? '&nbsp;'.$i : $i);
660                         else
661                                 $o .= "<span class=\"pager_n\"><a href=\"$url"."&page=$i\">".(($i < 10) ? '&nbsp;'.$i : $i)."</a>";
662                         $o .= '</span> ';
663                 }
664
665                 $lastpage = (($numpages > intval($numpages)) ? intval($numpages)+1 : $numpages);
666                 $o .= "<span class=\"pager_last\"><a href=\"$url"."&page=$lastpage\">" . t('last') . "</a></span> ";
667
668                 if(($a->pager['total'] - ($a->pager['itemspage'] * $a->pager['page'])) > 0)
669                         $o .= '<span class="pager_next">'."<a href=\"$url"."&page=".($a->pager['page'] + 1).'">' . t('next') . '</a></span>';
670                 $o .= '</div>'."\r\n";
671         }
672         return $o;
673 }}
674
675 // Turn user/group ACLs stored as angle bracketed text into arrays
676
677 if(! function_exists('expand_acl')) {
678 function expand_acl($s) {
679         // turn string array of angle-bracketed elements into numeric array
680         // e.g. "<1><2><3>" => array(1,2,3);
681         $ret = array();
682
683         if(strlen($s)) {
684                 $t = str_replace('<','',$s);
685                 $a = explode('>',$t);
686                 foreach($a as $aa) {
687                         if(intval($aa))
688                                 $ret[] = intval($aa);
689                 }
690         }
691         return $ret;
692 }}              
693
694 // Used to wrap ACL elements in angle brackets for storage 
695
696 if(! function_exists('sanitise_acl')) {
697 function sanitise_acl(&$item) {
698         if(intval($item))
699                 $item = '<' . intval(notags(trim($item))) . '>';
700         else
701                 unset($item);
702 }}
703
704 // retrieve a "family" of config variables from database to cached storage
705
706 if(! function_exists('load_config')) {
707 function load_config($family) {
708         global $a;
709         $r = q("SELECT * FROM `config` WHERE `cat` = '%s'",
710                 dbesc($family)
711         );
712         if(count($r)) {
713                 foreach($r as $rr) {
714                         $k = $rr['k'];
715                         $a->config[$family][$k] = $rr['v'];
716                 }
717         }
718 }}
719
720 // get a particular config variable given the family name
721 // and key. Returns false if not set.
722 // $instore is only used by the set_config function
723 // to determine if the key already exists in the DB
724 // If a key is found in the DB but doesn't exist in
725 // local config cache, pull it into the cache so we don't have
726 // to hit the DB again for this item.
727
728 if(! function_exists('get_config')) {
729 function get_config($family, $key, $instore = false) {
730
731         global $a;
732
733         if(! $instore) {
734                 if(isset($a->config[$family][$key])) {
735                         if($a->config[$family][$key] === '!<unset>!') {
736                                 return false;
737                         }
738                         return $a->config[$family][$key];
739                 }
740         }
741         $ret = q("SELECT `v` FROM `config` WHERE `cat` = '%s' AND `k` = '%s' LIMIT 1",
742                 dbesc($family),
743                 dbesc($key)
744         );
745         if(count($ret)) {
746                 $a->config[$family][$key] = $ret[0]['v'];
747                 return $ret[0]['v'];
748         }
749         else {
750                 $a->config[$family][$key] = '!<unset>!';
751         }
752         return false;
753 }}
754
755 // Store a config value ($value) in the category ($family)
756 // under the key ($key)
757 // Return the value, or false if the database update failed
758
759 if(! function_exists('set_config')) {
760 function set_config($family,$key,$value) {
761
762         global $a;
763         $a->config[$family][$key] = $value;
764
765         if(get_config($family,$key,true) === false) {
766                 $ret = q("INSERT INTO `config` ( `cat`, `k`, `v` ) VALUES ( '%s', '%s', '%s' ) ",
767                         dbesc($family),
768                         dbesc($key),
769                         dbesc($value)
770                 );
771                 if($ret) 
772                         return $value;
773                 return $ret;
774         }
775         $ret = q("UPDATE `config` SET `v` = '%s' WHERE `cat` = '%s' AND `k` = '%s' LIMIT 1",
776                 dbesc($value),
777                 dbesc($family),
778                 dbesc($key)
779         );
780         if($ret)
781                 return $value;
782         return $ret;
783 }}
784
785 // convert an XML document to a normalised, case-corrected array
786 // used by webfinger
787
788 if(! function_exists('convert_xml_element_to_array')) {
789 function convert_xml_element_to_array($xml_element, &$recursion_depth=0) {
790
791         // If we're getting too deep, bail out
792         if ($recursion_depth > 512) {
793                 return(null);
794         }
795
796         if (!is_string($xml_element) &&
797         !is_array($xml_element) &&
798         (get_class($xml_element) == 'SimpleXMLElement')) {
799                 $xml_element_copy = $xml_element;
800                 $xml_element = get_object_vars($xml_element);
801         }
802
803         if (is_array($xml_element)) {
804                 $result_array = array();
805                 if (count($xml_element) <= 0) {
806                         return (trim(strval($xml_element_copy)));
807                 }
808
809                 foreach($xml_element as $key=>$value) {
810
811                         $recursion_depth++;
812                         $result_array[strtolower($key)] =
813                 convert_xml_element_to_array($value, $recursion_depth);
814                         $recursion_depth--;
815                 }
816                 if ($recursion_depth == 0) {
817                         $temp_array = $result_array;
818                         $result_array = array(
819                                 strtolower($xml_element_copy->getName()) => $temp_array,
820                         );
821                 }
822
823                 return ($result_array);
824
825         } else {
826                 return (trim(strval($xml_element)));
827         }
828 }}
829
830 // Given an email style address, perform webfinger lookup and 
831 // return the resulting DFRN profile URL.
832 // If this isn't an email style address just return $s.
833 // Return an empty string if email-style addresses but webfinger fails,
834 // or if the resultant personal XRD doesn't contain a DFRN profile.
835
836 if(! function_exists('webfinger_dfrn')) {
837 function webfinger_dfrn($s) {
838         if(! strstr($s,'@')) {
839                 return $s;
840         }
841         $links = webfinger($s);
842         if(count($links)) {
843                 foreach($links as $link)
844                         if($link['@attributes']['rel'] == NAMESPACE_DFRN)
845                                 return $link['@attributes']['href'];
846         }
847         return '';
848 }}
849
850 // Given an email style address, perform webfinger lookup and 
851 // return the array of link attributes from the personal XRD file.
852 // On error/failure return an empty array.
853
854
855 if(! function_exists('webfinger')) {
856 function webfinger($s) {
857         $host = '';
858         if(strstr($s,'@')) {
859                 $host = substr($s,strpos($s,'@') + 1);
860         }
861         if(strlen($host)) {
862                 $tpl = fetch_lrdd_template($host);
863                 if(strlen($tpl)) {
864                         $pxrd = str_replace('{uri}', urlencode('acct://'.$s), $tpl);
865                         $links = fetch_xrd_links($pxrd);
866                         if(! count($links)) {
867                                 // try without the double slashes
868                                 $pxrd = str_replace('{uri}', urlencode('acct:'.$s), $tpl);
869                                 $links = fetch_xrd_links($pxrd);
870                         }
871                         return $links;
872                 }
873         }
874         return array();
875 }}
876
877 // Given a host name, locate the LRDD template from that
878 // host. Returns the LRDD template or an empty string on
879 // error/failure.
880
881 if(! function_exists('fetch_lrdd_template')) {
882 function fetch_lrdd_template($host) {
883         $tpl = '';
884         $url = 'http://' . $host . '/.well-known/host-meta' ;
885         $links = fetch_xrd_links($url);
886         if(count($links)) {
887                 foreach($links as $link)
888                         if($link['@attributes']['rel'] && $link['@attributes']['rel'] === 'lrdd')
889                                 $tpl = $link['@attributes']['template'];
890         }
891         if(! strpos($tpl,'{uri}'))
892                 $tpl = '';
893         return $tpl;
894 }}
895
896 // Given a URL, retrieve the page as an XRD document.
897 // Return an array of links.
898 // on error/failure return empty array.
899
900 if(! function_exists('fetch_xrd_links')) {
901 function fetch_xrd_links($url) {
902
903         $xml = fetch_url($url);
904         if (! $xml)
905                 return array();
906         $h = simplexml_load_string($xml);
907         $arr = convert_xml_element_to_array($h);
908
909         if(isset($arr['xrd']['link'])) {
910                 $link = $arr['xrd']['link'];
911                 if(! isset($link[0]))
912                         $links = array($link);
913                 else
914                         $links = $link;
915                 return $links;
916         }
917         return array();
918 }}
919
920 // Convert an ACL array to a storable string
921
922 if(! function_exists('perms2str')) {
923 function perms2str($p) {
924         $ret = '';
925         $tmp = $p;
926         if(is_array($tmp)) {
927                 array_walk($tmp,'sanitise_acl');
928                 $ret = implode('',$tmp);
929         }
930         return $ret;
931 }}
932
933 // generate a guaranteed unique (for this domain) item ID for ATOM
934 // safe from birthday paradox
935
936 if(! function_exists('item_new_uri')) {
937 function item_new_uri($hostname,$uid) {
938
939         do {
940                 $dups = false;
941                 $hash = random_string();
942
943                 $uri = "urn:X-dfrn:" . $hostname . ':' . $uid . ':' . $hash;
944
945                 $r = q("SELECT `id` FROM `item` WHERE `uri` = '%s' LIMIT 1",
946                         dbesc($uri));
947                 if(count($r))
948                         $dups = true;
949         } while($dups == true);
950         return $uri;
951 }}
952
953 // Generate a guaranteed unique photo ID.
954 // safe from birthday paradox
955
956 if(! function_exists('photo_new_resource')) {
957 function photo_new_resource() {
958
959         do {
960                 $found = false;
961                 $resource = hash('md5',uniqid(mt_rand(),true));
962                 $r = q("SELECT `id` FROM `photo` WHERE `resource-id` = '%s' LIMIT 1",
963                         dbesc($resource)
964                 );
965                 if(count($r))
966                         $found = true;
967         } while($found == true);
968         return $resource;
969 }}
970
971
972 // Returns logged in user ID
973
974 if(! function_exists('get_uid')) {
975 function get_uid() {
976         return ((x($_SESSION,'uid')) ? intval($_SESSION['uid']) : 0) ;
977 }}
978
979 // Take a URL from the wild, prepend http:// if necessary
980 // and check DNS to see if it's real
981 // return true if it's OK, false if something is wrong with it
982
983 if(! function_exists('validate_url')) {
984 function validate_url(&$url) {
985         if(substr($url,0,4) != 'http')
986                 $url = 'http://' . $url;
987         $h = parse_url($url);
988
989         if(! $h) {
990                 return false;
991         }
992         if(! checkdnsrr($h['host'], 'ANY')) {
993                 return false;
994         }
995         return true;
996 }}
997
998 // Check $url against our list of allowed sites,
999 // wildcards allowed. If allowed_sites is unset return true;
1000 // If url is allowed, return true.
1001 // otherwise, return false
1002
1003 if(! function_exists('allowed_url')) {
1004 function allowed_url($url) {
1005
1006         $h = parse_url($url);
1007
1008         if(! $h) {
1009                 return false;
1010         }
1011
1012         $str_allowed = get_config('system','allowed_sites');
1013         if(! $str_allowed)
1014                 return true;
1015
1016         $found = false;
1017
1018         $host = strtolower($h['host']);
1019
1020         // always allow our own site
1021
1022         if($host == strtolower($_SERVER['SERVER_NAME']))
1023                 return true;
1024
1025         $fnmatch = function_exists('fnmatch');
1026         $allowed = explode(',',$str_allowed);
1027
1028         if(count($allowed)) {
1029                 foreach($allowed as $a) {
1030                         $pat = strtolower(trim($a));
1031                         if(($fnmatch && fnmatch($pat,$host)) || ($pat == $host)) {
1032                                 $found = true; 
1033                                 break;
1034                         }
1035                 }
1036         }
1037         return $found;
1038 }}
1039
1040 // check if email address is allowed to register here.
1041 // Compare against our list (wildcards allowed).
1042 // Returns false if not allowed, true if allowed or if
1043 // allowed list is not configured.
1044
1045 if(! function_exists('allowed_email')) {
1046 function allowed_email($email) {
1047
1048
1049         $domain = strtolower(substr($email,strpos($email,'@') + 1));
1050         if(! $domain)
1051                 return false;
1052
1053         $str_allowed = get_config('system','allowed_email');
1054         if(! $str_allowed)
1055                 return true;
1056
1057         $found = false;
1058
1059         $fnmatch = function_exists('fnmatch');
1060         $allowed = explode(',',$str_allowed);
1061
1062         if(count($allowed)) {
1063                 foreach($allowed as $a) {
1064                         $pat = strtolower(trim($a));
1065                         if(($fnmatch && fnmatch($pat,$host)) || ($pat == $host)) {
1066                                 $found = true; 
1067                                 break;
1068                         }
1069                 }
1070         }
1071         return $found;
1072 }}
1073
1074 // Format the like/dislike text for a profile item
1075 // $cnt = number of people who like/dislike the item
1076 // $arr = array of pre-linked names of likers/dislikers
1077 // $type = one of 'like, 'dislike'
1078 // $id  = item id
1079 // returns formatted text
1080
1081 if(! function_exists('format_like')) {
1082 function format_like($cnt,$arr,$type,$id) {
1083         if($cnt == 1)
1084                 $o .= $arr[0] . (($type === 'like') ? t(' likes this.') : t(' doesn\'t like this.')) . EOL ;
1085         else {
1086                 $o .= '<span class="fakelink" onclick="openClose(\'' . $type . 'list-' . $id . '\');" >' 
1087                         . $cnt . ' ' . t('people') . '</span> ' . (($type === 'like') ? t('like this.') : t('don\'t like this.')) . EOL ;
1088                 $total = count($arr);
1089                 if($total >= 75)
1090                         $arr = array_slice($arr,0,74);
1091                 if($total < 75)
1092                         $arr[count($arr)-1] = t('and') . ' ' . $arr[count($arr)-1];
1093                 $str = implode(', ', $arr);
1094                 if($total >= 75)
1095                         $str .= t(', and ') . $total - 75 . t(' other people');
1096                 $str .= (($type === 'like') ? t(' like this.') : t(' don\'t like this.'));
1097                 $o .= '<div id="' . $type . 'list-' . $id . '" style="display: none;" >' . $str . '</div>';
1098         }
1099         return $o;
1100 }}
1101
1102
1103 // wrapper to load a view template, checking for alternate
1104 // languages before falling back to the default
1105
1106 if(! function_exists('load_view_file')) {
1107 function load_view_file($s) {
1108         $b = basename($s);
1109         $d = dirname($s);
1110         $lang = get_config('system','language');
1111         if($lang && file_exists("$d/$lang/$b"))
1112                 return file_get_contents("$d/$lang/$b");
1113         return file_get_contents($s);
1114 }}