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