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