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