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