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