]> git.mxchange.org Git - friendica.git/blob - boot.php
6530973339e27a22a92744047339fb09a446ad75
[friendica.git] / boot.php
1 <?php
2
3 set_time_limit(0);
4
5 define ( 'BUILD_ID',               1022   );
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 /**
12  * log levels
13  */
14
15 define ( 'LOGGER_NORMAL',          0 );
16 define ( 'LOGGER_TRACE',           1 );
17 define ( 'LOGGER_DEBUG',           2 );
18 define ( 'LOGGER_DATA',            3 );
19 define ( 'LOGGER_ALL',             4 );
20
21 /**
22  * registration policies
23  */
24
25 define ( 'REGISTER_CLOSED',        0 );
26 define ( 'REGISTER_APPROVE',       1 );
27 define ( 'REGISTER_OPEN',          2 );
28
29 /**
30  * relationship types
31  */
32
33 define ( 'REL_VIP',        1);
34 define ( 'REL_FAN',        2);
35 define ( 'REL_BUD',        3);
36
37
38 /**
39  *
40  * page/profile types
41  *
42  * PAGE_NORMAL is a typical personal profile account
43  * PAGE_SOAPBOX automatically approves all friend requests as REL_FAN, (readonly)
44  * PAGE_COMMUNITY automatically approves all friend requests as REL_FAN, but with 
45  *      write access to wall and comments (no email and not included in page owner's ACL lists)
46  * PAGE_FREELOVE automatically approves all friend requests as full friends (REL_BUD). 
47  *
48  */
49
50 define ( 'PAGE_NORMAL',            0 );
51 define ( 'PAGE_SOAPBOX',           1 );
52 define ( 'PAGE_COMMUNITY',         2 );
53 define ( 'PAGE_FREELOVE',          3 );
54
55 /**
56  * Maximum number of "people who like (or don't like) this"  that we will list by name
57  */
58
59 define ( 'MAX_LIKERS',    75);
60
61 /**
62  * email notification options
63  */
64
65 define ( 'NOTIFY_INTRO',   0x0001 );
66 define ( 'NOTIFY_CONFIRM', 0x0002 );
67 define ( 'NOTIFY_WALL',    0x0004 );
68 define ( 'NOTIFY_COMMENT', 0x0008 );
69 define ( 'NOTIFY_MAIL',    0x0010 );
70
71 /**
72  * various namespaces we may need to parse
73  */
74
75 define ( 'NAMESPACE_DFRN' ,           'http://purl.org/macgirvin/dfrn/1.0' ); 
76 define ( 'NAMESPACE_THREAD' ,         'http://purl.org/syndication/thread/1.0' );
77 define ( 'NAMESPACE_TOMB' ,           'http://purl.org/atompub/tombstones/1.0' );
78 define ( 'NAMESPACE_ACTIVITY',        'http://activitystrea.ms/spec/1.0/' );
79 define ( 'NAMESPACE_ACTIVITY_SCHEMA', 'http://activitystrea.ms/schema/1.0/' );
80 define ( 'NAMESPACE_MEDIA',           'http://purl.org/syndication/atommedia' );
81 define ( 'NAMESPACE_SALMON_ME',       'http://salmon-protocol.org/ns/magic-env' );
82 define ( 'NAMESPACE_OSTATUSSUB',      'http://ostatus.org/schema/1.0/subscribe' );
83 define ( 'NAMESPACE_GEORSS',          'http://www.georss.org/georss' );
84 define ( 'NAMESPACE_POCO',            'http://portablecontacts.net/spec/1.0' );
85 define ( 'NAMESPACE_FEED',            'http://schemas.google.com/g/2010#updates-from' );
86
87 /**
88  * activity stream defines
89  */
90
91 define ( 'ACTIVITY_LIKE',        NAMESPACE_ACTIVITY_SCHEMA . 'like' );
92 define ( 'ACTIVITY_DISLIKE',     NAMESPACE_DFRN            . '/dislike' );
93 define ( 'ACTIVITY_OBJ_HEART',   NAMESPACE_DFRN            . '/heart' );
94
95 define ( 'ACTIVITY_FRIEND',      NAMESPACE_ACTIVITY_SCHEMA . 'make-friend' );
96 define ( 'ACTIVITY_FOLLOW',      NAMESPACE_ACTIVITY_SCHEMA . 'follow' );
97 define ( 'ACTIVITY_UNFOLLOW',    NAMESPACE_ACTIVITY_SCHEMA . 'unfollow' );
98 define ( 'ACTIVITY_POST',        NAMESPACE_ACTIVITY_SCHEMA . 'post' );
99 define ( 'ACTIVITY_UPDATE',      NAMESPACE_ACTIVITY_SCHEMA . 'update' );
100 define ( 'ACTIVITY_TAG',         NAMESPACE_ACTIVITY_SCHEMA . 'tag' );
101
102 define ( 'ACTIVITY_OBJ_COMMENT', NAMESPACE_ACTIVITY_SCHEMA . 'comment' );
103 define ( 'ACTIVITY_OBJ_NOTE',    NAMESPACE_ACTIVITY_SCHEMA . 'note' );
104 define ( 'ACTIVITY_OBJ_PERSON',  NAMESPACE_ACTIVITY_SCHEMA . 'person' );
105 define ( 'ACTIVITY_OBJ_PHOTO',   NAMESPACE_ACTIVITY_SCHEMA . 'photo' );
106 define ( 'ACTIVITY_OBJ_P_PHOTO', NAMESPACE_ACTIVITY_SCHEMA . 'profile-photo' );
107 define ( 'ACTIVITY_OBJ_ALBUM',   NAMESPACE_ACTIVITY_SCHEMA . 'photo-album' );
108
109 /**
110  * item weight for query ordering
111  */
112
113 define ( 'GRAVITY_PARENT',       0);
114 define ( 'GRAVITY_LIKE',         3);
115 define ( 'GRAVITY_COMMENT',      6);
116
117 /**
118  *
119  * Reverse the effect of magic_quotes_gpc if it is enabled.
120  * Please disable magic_quotes_gpc so we don't have to do this.
121  * See http://php.net/manual/en/security.magicquotes.disabling.php
122  *
123  */
124
125 if (get_magic_quotes_gpc()) {
126     $process = array(&$_GET, &$_POST, &$_COOKIE, &$_REQUEST);
127     while (list($key, $val) = each($process)) {
128         foreach ($val as $k => $v) {
129             unset($process[$key][$k]);
130             if (is_array($v)) {
131                 $process[$key][stripslashes($k)] = $v;
132                 $process[] = &$process[$key][stripslashes($k)];
133             } else {
134                 $process[$key][stripslashes($k)] = stripslashes($v);
135             }
136         }
137     }
138     unset($process);
139 }
140
141
142 /**
143  *
144  * class: App
145  *
146  * Our main application structure for the life of this page
147  * Primarily deals with the URL that got us here
148  * and tries to make some sense of it, and 
149  * stores our page contents and config storage
150  * and anything else that might need to be passed around 
151  * before we spit the page out. 
152  *
153  */
154
155 if(! class_exists('App')) {
156 class App {
157
158         public  $module_loaded = false;
159         public  $config;
160         public  $page;
161         public  $profile;
162         public  $user;
163         public  $cid;
164         public  $contact;
165         public  $content;
166         public  $data;
167         public  $error = false;
168         public  $cmd;
169         public  $argv;
170         public  $argc;
171         public  $module;
172         public  $pager;
173         public  $strings;   
174         public  $path;
175         public  $interactive = true;
176
177         private $scheme;
178         private $hostname;
179         private $baseurl;
180         private $db;
181
182         private $curl_code;
183         private $curl_headers;
184
185         function __construct() {
186
187                 $this->config = array();
188                 $this->page = array();
189                 $this->pager= array();
190
191                 $this->scheme = ((isset($_SERVER['HTTPS']) && ($_SERVER['HTTPS']))      ?  'https' : 'http' );
192
193                 if(x($_SERVER,'SERVER_NAME'))
194                         $this->hostname = $_SERVER['SERVER_NAME'];
195
196                 set_include_path("include/$this->hostname" . PATH_SEPARATOR . 'include' . PATH_SEPARATOR . '.' );
197
198                 if((x($_SERVER,'QUERY_STRING')) && substr($_SERVER['QUERY_STRING'],0,2) === "q=")
199                         $_SERVER['QUERY_STRING'] = substr($_SERVER['QUERY_STRING'],2);
200                 if(x($_GET,'q'))
201                         $this->cmd = trim($_GET['q'],'/');
202
203                 $path = trim(dirname($_SERVER['SCRIPT_NAME']),'/');
204                 if(isset($path) && strlen($path) && ($path != $this->path))
205                         $this->path = $path;
206
207                 $this->argv = explode('/',$this->cmd);
208                 $this->argc = count($this->argv);
209                 if((array_key_exists('0',$this->argv)) && strlen($this->argv[0])) {
210                         $this->module = $this->argv[0];
211                 }
212                 else {
213                         $this->module = 'home';
214                 }
215
216                 if($this->cmd === '.well-known/host-meta')
217                         require_once('include/hostxrd.php');
218
219
220                 $this->pager['page'] = ((x($_GET,'page')) ? $_GET['page'] : 1);
221                 $this->pager['itemspage'] = 50;
222                 $this->pager['start'] = ($this->pager['page'] * $this->pager['itemspage']) - $this->pager['itemspage'];
223                 $this->pager['total'] = 0;
224         }
225
226         function get_baseurl($ssl = false) {
227                 if(strlen($this->baseurl))
228                         return $this->baseurl;
229
230                 $this->baseurl = (($ssl) ? 'https' : $this->scheme) . "://" . $this->hostname . ((isset($this->path) && strlen($this->path)) ? '/' . $this->path : '' );
231                 return $this->baseurl;
232         }
233
234         function set_baseurl($url) {
235                 $this->baseurl = $url;
236                 $this->hostname = basename($url);
237         }
238
239         function get_hostname() {
240                 return $this->hostname;
241         }
242
243         function set_hostname($h) {
244                 $this->hostname = $h;
245         }
246
247         function set_path($p) {
248                 $this->path = trim(trim($p),'/');
249         } 
250
251         function get_path() {
252                 return $this->path;
253         }
254
255         function set_pager_total($n) {
256                 $this->pager['total'] = intval($n);
257         }
258
259         function set_pager_itemspage($n) {
260                 $this->pager['itemspage'] = intval($n);
261                 $this->pager['start'] = ($this->pager['page'] * $this->pager['itemspage']) - $this->pager['itemspage'];
262
263         } 
264
265         function init_pagehead() {
266                 $tpl = load_view_file("view/head.tpl");
267                 $this->page['htmlhead'] = replace_macros($tpl,array(
268                         '$baseurl' => $this->get_baseurl() . '/'
269                 ));
270         }
271
272         function set_curl_code($code) {
273                 $this->curl_code = $code;
274         }
275
276         function get_curl_code() {
277                 return $this->curl_code;
278         }
279
280         function set_curl_headers($headers) {
281                 $this->curl_headers = $headers;
282         }
283
284         function get_curl_headers() {
285                 return $this->curl_headers;
286         }
287
288
289 }}
290
291 // retrieve the App structure
292 // useful in functions which require it but don't get it passed to them
293
294 if(! function_exists('get_app')) {
295 function get_app() {
296         global $a;
297         return $a;
298 }};
299
300
301 // Multi-purpose function to check variable state.
302 // Usage: x($var) or $x($array,'key')
303 // returns false if variable/key is not set
304 // if variable is set, returns 1 if has 'non-zero' value, otherwise returns 0.
305 // e.g. x('') or x(0) returns 0;
306
307 if(! function_exists('x')) {
308 function x($s,$k = NULL) {
309         if($k != NULL) {
310                 if((is_array($s)) && (array_key_exists($k,$s))) {
311                         if($s[$k])
312                                 return (int) 1;
313                         return (int) 0;
314                 }
315                 return false;
316         }
317         else {          
318                 if(isset($s)) {
319                         if($s) {
320                                 return (int) 1;
321                         }
322                         return (int) 0;
323                 }
324                 return false;
325         }
326 }}
327
328 // called from db initialisation if db is dead.
329
330 if(! function_exists('system_unavailable')) {
331 function system_unavailable() {
332         include('system_unavailable.php');
333         killme();
334 }}
335
336 // Primarily involved with database upgrade, but also sets the 
337 // base url for use in cmdline programs which don't have
338 // $_SERVER variables.
339
340 if(! function_exists('check_config')) {
341 function check_config(&$a) {
342
343         load_config('system');
344
345         $build = get_config('system','build');
346         if(! x($build))
347                 $build = set_config('system','build',BUILD_ID);
348
349         $url = get_config('system','url');
350         if(! x($url))
351                 $url = set_config('system','url',$a->get_baseurl());
352
353         if($build != BUILD_ID) {
354                 $stored = intval($build);
355                 $current = intval(BUILD_ID);
356                 if(($stored < $current) && file_exists('update.php')) {
357                         // We're reporting a different version than what is currently installed.
358                         // Run any existing update scripts to bring the database up to current.
359
360                         require_once('update.php');
361                         for($x = $stored; $x < $current; $x ++) {
362                                 if(function_exists('update_' . $x)) {
363                                         $func = 'update_' . $x;
364                                         $func($a);
365                                 }
366                         }
367                         set_config('system','build', BUILD_ID);
368                 }
369         }
370         return;
371 }}
372
373
374 // This is our template processor.
375 // $s is the string requiring macro substitution.
376 // $r is an array of key value pairs (search => replace)
377 // returns substituted string.
378 // WARNING: this is pretty basic, and doesn't properly handle search strings that are substrings of each other.
379 // For instance if 'test' => "foo" and 'testing' => "bar", testing could become either bar or fooing, 
380 // depending on the order in which they were declared in the array.   
381
382 if(! function_exists('replace_macros')) {  
383 function replace_macros($s,$r) {
384
385         $search = array();
386         $replace = array();
387
388         if(is_array($r) && count($r)) {
389                 foreach ($r as $k => $v ) {
390                         $search[] =  $k;
391                         $replace[] = $v;
392                 }
393         }
394         return str_replace($search,$replace,$s);
395 }}
396
397
398 // load string translation table for alternate language
399
400 if(! function_exists('load_translation_table')) {
401 function load_translation_table($lang) {
402         global $a;
403
404         if(file_exists("view/$lang/strings.php"))
405                 include("view/$lang/strings.php");
406 }}
407
408 // translate string if translation exists
409
410 if(! function_exists('t')) {
411 function t($s) {
412
413         $a = get_app();
414
415         if(x($a->strings,$s))
416                 return $a->strings[$s];
417         return $s;
418 }}
419
420 // curl wrapper. If binary flag is true, return binary
421 // results. 
422
423 if(! function_exists('fetch_url')) {
424 function fetch_url($url,$binary = false, &$redirects = 0) {
425
426         $a = get_app();
427
428         $ch = curl_init($url);
429         if(($redirects > 8) || (! $ch)) 
430                 return false;
431
432         curl_setopt($ch, CURLOPT_HEADER, true);
433         curl_setopt($ch, CURLOPT_RETURNTRANSFER,true);
434
435
436         $curl_time = intval(get_config('system','curl_timeout'));
437         curl_setopt($ch, CURLOPT_TIMEOUT, (($curl_time !== false) ? $curl_time : 60));
438
439         // by default we will allow self-signed certs
440         // but you can override this
441
442         $check_cert = get_config('system','verifyssl');
443         curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, (($check_cert) ? true : false));
444
445         $prx = get_config('system','proxy');
446         if(strlen($prx)) {
447                 curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1);
448                 curl_setopt($ch, CURLOPT_PROXY, $prx);
449                 $prxusr = get_config('system','proxyuser');
450                 if(strlen($prxusr))
451                         curl_setopt($ch, CURLOPT_PROXYUSERPWD, $prxusr);
452         }
453         if($binary)
454                 curl_setopt($ch, CURLOPT_BINARYTRANSFER,1);
455
456         $a->set_curl_code(0);
457
458         // don't let curl abort the entire application
459         // if it throws any errors.
460
461         $s = @curl_exec($ch);
462
463         $http_code = intval(curl_getinfo($ch, CURLINFO_HTTP_CODE));
464         $header = substr($s,0,strpos($s,"\r\n\r\n"));
465         if(stristr($header,'100') && (strlen($header) < 30)) {
466                 // 100 Continue has two headers, get the real one
467                 $s = substr($s,strlen($header)+4);
468                 $header = substr($s,0,strpos($s,"\r\n\r\n"));
469         }
470         if($http_code == 301 || $http_code == 302 || $http_code == 303) {
471         $matches = array();
472         preg_match('/(Location:|URI:)(.*?)\n/', $header, $matches);
473         $url = trim(array_pop($matches));
474         $url_parsed = parse_url($url);
475         if (isset($url_parsed)) {
476             $redirects++;
477             return fetch_url($url,$binary,$redirects);
478         }
479     }
480         $a->set_curl_code($http_code);
481         $body = substr($s,strlen($header)+4);
482         $a->set_curl_headers($header);
483
484         curl_close($ch);
485         return($body);
486 }}
487
488 // post request to $url. $params is an array of post variables.
489
490 if(! function_exists('post_url')) {
491 function post_url($url,$params, $headers = null, &$redirects = 0) {
492         $a = get_app();
493         $ch = curl_init($url);
494         if(($redirects > 8) || (! $ch)) 
495                 return false;
496
497         curl_setopt($ch, CURLOPT_HEADER, true);
498         curl_setopt($ch, CURLOPT_RETURNTRANSFER,true);
499         curl_setopt($ch, CURLOPT_POST,1);
500         curl_setopt($ch, CURLOPT_POSTFIELDS,$params);
501
502         $curl_time = intval(get_config('system','curl_timeout'));
503         curl_setopt($ch, CURLOPT_TIMEOUT, (($curl_time !== false) ? $curl_time : 60));
504
505         if(is_array($headers))
506                 curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
507
508         $check_cert = get_config('system','verifyssl');
509         curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, (($check_cert) ? true : false));
510         $prx = get_config('system','proxy');
511         if(strlen($prx)) {
512                 curl_setopt($ch, CURLOPT_HTTPPROXYTUNNEL, 1);
513                 curl_setopt($ch, CURLOPT_PROXY, $prx);
514                 $prxusr = get_config('system','proxyuser');
515                 if(strlen($prxusr))
516                         curl_setopt($ch, CURLOPT_PROXYUSERPWD, $prxusr);
517         }
518
519         $a->set_curl_code(0);
520
521         // don't let curl abort the entire application
522         // if it throws any errors.
523
524         $s = @curl_exec($ch);
525
526         $http_code = intval(curl_getinfo($ch, CURLINFO_HTTP_CODE));
527         $header = substr($s,0,strpos($s,"\r\n\r\n"));
528         if(stristr($header,'100') && (strlen($header) < 30)) {
529                 // 100 Continue has two headers, get the real one
530                 $s = substr($s,strlen($header)+4);
531                 $header = substr($s,0,strpos($s,"\r\n\r\n"));
532         }
533         if($http_code == 301 || $http_code == 302 || $http_code == 303) {
534         $matches = array();
535         preg_match('/(Location:|URI:)(.*?)\n/', $header, $matches);
536         $url = trim(array_pop($matches));
537         $url_parsed = parse_url($url);
538         if (isset($url_parsed)) {
539             $redirects++;
540             return post_url($url,$binary,$headers,$redirects);
541         }
542     }
543         $a->set_curl_code($http_code);
544         $body = substr($s,strlen($header)+4);
545         $a->set_curl_headers($header);
546
547         curl_close($ch);
548         return($body);
549 }}
550
551 // random hash, 64 chars
552
553 if(! function_exists('random_string')) {
554 function random_string() {
555         return(hash('sha256',uniqid(rand(),true)));
556 }}
557
558 /**
559  * This is our primary input filter. 
560  *
561  * The high bit hack only involved some old IE browser, forget which (IE5/Mac?)
562  * that had an XSS attack vector due to stripping the high-bit on an 8-bit character
563  * after cleansing, and angle chars with the high bit set could get through as markup.
564  * 
565  * This is now disabled because it was interfering with some legitimate unicode sequences 
566  * and hopefully there aren't a lot of those browsers left. 
567  *
568  * Use this on any text input where angle chars are not valid or permitted
569  * They will be replaced with safer brackets. This may be filtered further
570  * if these are not allowed either.   
571  *
572  */
573
574 if(! function_exists('notags')) {
575 function notags($string) {
576
577         return(str_replace(array("<",">"), array('[',']'), $string));
578
579 //  High-bit filter no longer used
580 //      return(str_replace(array("<",">","\xBA","\xBC","\xBE"), array('[',']','','',''), $string));
581 }}
582
583 // use this on "body" or "content" input where angle chars shouldn't be removed,
584 // and allow them to be safely displayed.
585
586 if(! function_exists('escape_tags')) {
587 function escape_tags($string) {
588
589         return(htmlspecialchars($string));
590 }}
591
592 // wrapper for adding a login box. If $register == true provide a registration
593 // link. This will most always depend on the value of $a->config['register_policy'].
594 // returns the complete html for inserting into the page
595
596 if(! function_exists('login')) {
597 function login($register = false) {
598         $o = "";
599         $register_html = (($register) ? load_view_file("view/register-link.tpl") : "");
600
601         $noid = get_config('system','no_openid');
602         if($noid) {
603                 $classname = 'no-openid';
604                 $namelabel = t('Nickname or Email address: ');
605                 $passlabel = t('Password: ');
606                 $login     = t('Login');
607         }
608         else {
609                 $classname = 'openid';
610                 $namelabel = t('Nickname/Email/OpenID: ');
611                 $passlabel = t("Password \x28if not OpenID\x29: ");
612                 $login     = t('Login');
613         }
614         $lostpass = t('Forgot your password?');
615         $lostlink = t('Password Reset');
616
617         if(x($_SESSION,'authenticated')) {
618                 $tpl = load_view_file("view/logout.tpl");
619         }
620         else {
621                 $tpl = load_view_file("view/login.tpl");
622
623         }
624         
625         $o = replace_macros($tpl,array(
626                 '$register_html' => $register_html, 
627                 '$classname' => $classname,
628                 '$namelabel' => $namelabel,
629                 '$passlabel' => $passlabel,
630                 '$login' => $login,
631                 '$lostpass' => $lostpass,
632                 '$lostlink' => $lostlink 
633         ));
634
635         return $o;
636 }}
637
638 // generate a string that's random, but usually pronounceable. 
639 // used to generate initial passwords
640
641 if(! function_exists('autoname')) {
642 function autoname($len) {
643
644         $vowels = array('a','a','ai','au','e','e','e','ee','ea','i','ie','o','ou','u'); 
645         if(mt_rand(0,5) == 4)
646                 $vowels[] = 'y';
647
648         $cons = array(
649                         'b','bl','br',
650                         'c','ch','cl','cr',
651                         'd','dr',
652                         'f','fl','fr',
653                         'g','gh','gl','gr',
654                         'h',
655                         'j',
656                         'k','kh','kl','kr',
657                         'l',
658                         'm',
659                         'n',
660                         'p','ph','pl','pr',
661                         'qu',
662                         'r','rh',
663                         's','sc','sh','sm','sp','st',
664                         't','th','tr',
665                         'v',
666                         'w','wh',
667                         'x',
668                         'z','zh'
669                         );
670
671         $midcons = array('ck','ct','gn','ld','lf','lm','lt','mb','mm', 'mn','mp',
672                                 'nd','ng','nk','nt','rn','rp','rt');
673
674         $noend = array('bl', 'br', 'cl','cr','dr','fl','fr','gl','gr',
675                                 'kh', 'kl','kr','mn','pl','pr','rh','tr','qu','wh');
676
677         $start = mt_rand(0,2);
678         if($start == 0)
679                 $table = $vowels;
680         else
681                 $table = $cons;
682
683         $word = '';
684
685         for ($x = 0; $x < $len; $x ++) {
686                 $r = mt_rand(0,count($table) - 1);
687                 $word .= $table[$r];
688   
689                 if($table == $vowels)
690                         $table = array_merge($cons,$midcons);
691                 else
692                         $table = $vowels;
693
694         }
695
696         $word = substr($word,0,$len);
697
698         foreach($noend as $noe) {
699                 if((strlen($word) > 2) && (substr($word,-2) == $noe)) {
700                         $word = substr($word,0,-1);
701                         break;
702                 }
703         }
704         if(substr($word,-1) == 'q')
705                 $word = substr($word,0,-1);    
706         return $word;
707 }}
708
709 // Used to end the current process, after saving session state. 
710
711 if(! function_exists('killme')) {
712 function killme() {
713         session_write_close();
714         exit;
715 }}
716
717 // redirect to another URL and terminate this process.
718
719 if(! function_exists('goaway')) {
720 function goaway($s) {
721         header("Location: $s");
722         killme();
723 }}
724
725 // Generic XML return
726 // Outputs a basic dfrn XML status structure to STDOUT, with a <status> variable 
727 // of $st and an optional text <message> of $message and terminates the current process. 
728
729 if(! function_exists('xml_status')) {
730 function xml_status($st, $message = '') {
731
732         $xml_message = ((strlen($message)) ? "\t<message>" . xmlify($message) . "</message>\r\n" : '');
733
734         if($st)
735                 logger('xml_status returning non_zero: ' . $st . " message=" . $message);
736
737         header( "Content-type: text/xml" );
738         echo '<?xml version="1.0" encoding="UTF-8"?>'."\r\n";
739         echo "<result>\r\n\t<status>$st</status>\r\n$xml_message</result>\r\n";
740         killme();
741 }}
742
743 // Returns the uid of locally logged in user or false.
744
745 if(! function_exists('local_user')) {
746 function local_user() {
747         if((x($_SESSION,'authenticated')) && (x($_SESSION,'uid')))
748                 return intval($_SESSION['uid']);
749         return false;
750 }}
751
752 // Returns contact id of authenticated site visitor or false
753
754 if(! function_exists('remote_user')) {
755 function remote_user() {
756         if((x($_SESSION,'authenticated')) && (x($_SESSION,'visitor_id')))
757                 return intval($_SESSION['visitor_id']);
758         return false;
759 }}
760
761 // contents of $s are displayed prominently on the page the next time
762 // a page is loaded. Usually used for errors or alerts.
763
764 if(! function_exists('notice')) {
765 function notice($s) {
766         $a = get_app();
767         if($a->interactive)
768                 $_SESSION['sysmsg'] .= $s;
769 }}
770
771 // wrapper around config to limit the text length of an incoming message
772
773 if(! function_exists('get_max_import_size')) {
774 function get_max_import_size() {
775         global $a;
776         return ((x($a->config,'max_import_size')) ? $a->config['max_import_size'] : 0 );
777 }}
778
779
780 // escape text ($str) for XML transport
781 // returns escaped text.
782
783 if(! function_exists('xmlify')) {
784 function xmlify($str) {
785         $buffer = '';
786         
787         for($x = 0; $x < strlen($str); $x ++) {
788                 $char = $str[$x];
789         
790                 switch( $char ) {
791
792                         case "\r" :
793                                 break;
794                         case "&" :
795                                 $buffer .= '&amp;';
796                                 break;
797                         case "'" :
798                                 $buffer .= '&apos;';
799                                 break;
800
801                         case "\"" :
802                                 $buffer .= '&quot;';
803                                 break;
804                         case '<' :
805                                 $buffer .= '&lt;';
806                                 break;
807                         case '>' :
808                                 $buffer .= '&gt;';
809                                 break;
810                         case "\n" :
811                                 $buffer .= "\n";
812                                 break;
813                         default :
814                                 $buffer .= $char;
815                                 break;
816                 }       
817         }
818         $buffer = trim($buffer);
819         return($buffer);
820 }}
821
822 // undo an xmlify
823 // pass xml escaped text ($s), returns unescaped text
824
825 if(! function_exists('unxmlify')) {
826 function unxmlify($s) {
827         $ret = str_replace('&amp;','&', $s);
828         $ret = str_replace(array('&lt;','&gt;','&quot;','&apos;'),array('<','>','"',"'"),$ret);
829         return $ret;    
830 }}
831
832 // convenience wrapper, reverse the operation "bin2hex"
833
834 if(! function_exists('hex2bin')) {
835 function hex2bin($s) {
836         return(pack("H*",$s));
837 }}
838
839 // Automatic pagination.
840 // To use, get the count of total items.
841 // Then call $a->set_pager_total($number_items);
842 // Optionally call $a->set_pager_itemspage($n) to the number of items to display on each page
843 // Then call paginate($a) after the end of the display loop to insert the pager block on the page
844 // (assuming there are enough items to paginate).
845 // When using with SQL, the setting LIMIT %d, %d => $a->pager['start'],$a->pager['itemspage']
846 // will limit the results to the correct items for the current page. 
847 // The actual page handling is then accomplished at the application layer. 
848
849 if(! function_exists('paginate')) {
850 function paginate(&$a) {
851         $o = '';
852         $stripped = preg_replace('/(&page=[0-9]*)/','',$_SERVER['QUERY_STRING']);
853         $stripped = str_replace('q=','',$stripped);
854         $stripped = trim($stripped,'/');
855         $url = $a->get_baseurl() . '/' . $stripped;
856
857
858           if($a->pager['total'] > $a->pager['itemspage']) {
859                 $o .= '<div class="pager">';
860                 if($a->pager['page'] != 1)
861                         $o .= '<span class="pager_prev">'."<a href=\"$url".'&page='.($a->pager['page'] - 1).'">' . t('prev') . '</a></span> ';
862
863                 $o .=  "<span class=\"pager_first\"><a href=\"$url"."&page=1\">" . t('first') . "</a></span> ";
864
865                 $numpages = $a->pager['total'] / $a->pager['itemspage'];
866
867                 $numstart = 1;
868                 $numstop = $numpages;
869
870                 if($numpages > 14) {
871                         $numstart = (($pagenum > 7) ? ($pagenum - 7) : 1);
872                         $numstop = (($pagenum > ($numpages - 7)) ? $numpages : ($numstart + 14));
873                 }
874    
875                 for($i = $numstart; $i <= $numstop; $i++){
876                         if($i == $a->pager['page'])
877                                 $o .= '<span class="pager_current">'.(($i < 10) ? '&nbsp;'.$i : $i);
878                         else
879                                 $o .= "<span class=\"pager_n\"><a href=\"$url"."&page=$i\">".(($i < 10) ? '&nbsp;'.$i : $i)."</a>";
880                         $o .= '</span> ';
881                 }
882
883                 if(($a->pager['total'] % $a->pager['itemspage']) != 0) {
884                         if($i == $a->pager['page'])
885                                 $o .= '<span class="pager_current">'.(($i < 10) ? '&nbsp;'.$i : $i);
886                         else
887                                 $o .= "<span class=\"pager_n\"><a href=\"$url"."&page=$i\">".(($i < 10) ? '&nbsp;'.$i : $i)."</a>";
888                         $o .= '</span> ';
889                 }
890
891                 $lastpage = (($numpages > intval($numpages)) ? intval($numpages)+1 : $numpages);
892                 $o .= "<span class=\"pager_last\"><a href=\"$url"."&page=$lastpage\">" . t('last') . "</a></span> ";
893
894                 if(($a->pager['total'] - ($a->pager['itemspage'] * $a->pager['page'])) > 0)
895                         $o .= '<span class="pager_next">'."<a href=\"$url"."&page=".($a->pager['page'] + 1).'">' . t('next') . '</a></span>';
896                 $o .= '</div>'."\r\n";
897         }
898         return $o;
899 }}
900
901 // Turn user/group ACLs stored as angle bracketed text into arrays
902
903 if(! function_exists('expand_acl')) {
904 function expand_acl($s) {
905         // turn string array of angle-bracketed elements into numeric array
906         // e.g. "<1><2><3>" => array(1,2,3);
907         $ret = array();
908
909         if(strlen($s)) {
910                 $t = str_replace('<','',$s);
911                 $a = explode('>',$t);
912                 foreach($a as $aa) {
913                         if(intval($aa))
914                                 $ret[] = intval($aa);
915                 }
916         }
917         return $ret;
918 }}              
919
920 // Used to wrap ACL elements in angle brackets for storage 
921
922 if(! function_exists('sanitise_acl')) {
923 function sanitise_acl(&$item) {
924         if(intval($item))
925                 $item = '<' . intval(notags(trim($item))) . '>';
926         else
927                 unset($item);
928 }}
929
930 // retrieve a "family" of config variables from database to cached storage
931
932 if(! function_exists('load_config')) {
933 function load_config($family) {
934         global $a;
935         $r = q("SELECT * FROM `config` WHERE `cat` = '%s'",
936                 dbesc($family)
937         );
938         if(count($r)) {
939                 foreach($r as $rr) {
940                         $k = $rr['k'];
941                         $a->config[$family][$k] = $rr['v'];
942                 }
943         }
944 }}
945
946 // get a particular config variable given the family name
947 // and key. Returns false if not set.
948 // $instore is only used by the set_config function
949 // to determine if the key already exists in the DB
950 // If a key is found in the DB but doesn't exist in
951 // local config cache, pull it into the cache so we don't have
952 // to hit the DB again for this item.
953
954 if(! function_exists('get_config')) {
955 function get_config($family, $key, $instore = false) {
956
957         global $a;
958
959         if(! $instore) {
960                 if(isset($a->config[$family][$key])) {
961                         if($a->config[$family][$key] === '!<unset>!') {
962                                 return false;
963                         }
964                         return $a->config[$family][$key];
965                 }
966         }
967         $ret = q("SELECT `v` FROM `config` WHERE `cat` = '%s' AND `k` = '%s' LIMIT 1",
968                 dbesc($family),
969                 dbesc($key)
970         );
971         if(count($ret)) {
972                 $a->config[$family][$key] = $ret[0]['v'];
973                 return $ret[0]['v'];
974         }
975         else {
976                 $a->config[$family][$key] = '!<unset>!';
977         }
978         return false;
979 }}
980
981 // Store a config value ($value) in the category ($family)
982 // under the key ($key)
983 // Return the value, or false if the database update failed
984
985 if(! function_exists('set_config')) {
986 function set_config($family,$key,$value) {
987
988         global $a;
989         $a->config[$family][$key] = $value;
990
991         if(get_config($family,$key,true) === false) {
992                 $ret = q("INSERT INTO `config` ( `cat`, `k`, `v` ) VALUES ( '%s', '%s', '%s' ) ",
993                         dbesc($family),
994                         dbesc($key),
995                         dbesc($value)
996                 );
997                 if($ret) 
998                         return $value;
999                 return $ret;
1000         }
1001         $ret = q("UPDATE `config` SET `v` = '%s' WHERE `cat` = '%s' AND `k` = '%s' LIMIT 1",
1002                 dbesc($value),
1003                 dbesc($family),
1004                 dbesc($key)
1005         );
1006         if($ret)
1007                 return $value;
1008         return $ret;
1009 }}
1010
1011 // convert an XML document to a normalised, case-corrected array
1012 // used by webfinger
1013
1014 if(! function_exists('convert_xml_element_to_array')) {
1015 function convert_xml_element_to_array($xml_element, &$recursion_depth=0) {
1016
1017         // If we're getting too deep, bail out
1018         if ($recursion_depth > 512) {
1019                 return(null);
1020         }
1021
1022         if (!is_string($xml_element) &&
1023         !is_array($xml_element) &&
1024         (get_class($xml_element) == 'SimpleXMLElement')) {
1025                 $xml_element_copy = $xml_element;
1026                 $xml_element = get_object_vars($xml_element);
1027         }
1028
1029         if (is_array($xml_element)) {
1030                 $result_array = array();
1031                 if (count($xml_element) <= 0) {
1032                         return (trim(strval($xml_element_copy)));
1033                 }
1034
1035                 foreach($xml_element as $key=>$value) {
1036
1037                         $recursion_depth++;
1038                         $result_array[strtolower($key)] =
1039                 convert_xml_element_to_array($value, $recursion_depth);
1040                         $recursion_depth--;
1041                 }
1042                 if ($recursion_depth == 0) {
1043                         $temp_array = $result_array;
1044                         $result_array = array(
1045                                 strtolower($xml_element_copy->getName()) => $temp_array,
1046                         );
1047                 }
1048
1049                 return ($result_array);
1050
1051         } else {
1052                 return (trim(strval($xml_element)));
1053         }
1054 }}
1055
1056 // Given an email style address, perform webfinger lookup and 
1057 // return the resulting DFRN profile URL, or if no DFRN profile URL
1058 // is located, returns an OStatus subscription template (prefixed 
1059 // with the string 'stat:' to identify it as on OStatus template).
1060 // If this isn't an email style address just return $s.
1061 // Return an empty string if email-style addresses but webfinger fails,
1062 // or if the resultant personal XRD doesn't contain a supported 
1063 // subscription/friend-request attribute.
1064
1065 if(! function_exists('webfinger_dfrn')) {
1066 function webfinger_dfrn($s) {
1067         if(! strstr($s,'@')) {
1068                 return $s;
1069         }
1070         $links = webfinger($s);
1071         logger('webfinger_dfrn: ' . $s . ':' . print_r($links,true), LOGGER_DATA);
1072         if(count($links)) {
1073                 foreach($links as $link)
1074                         if($link['@attributes']['rel'] === NAMESPACE_DFRN)
1075                                 return $link['@attributes']['href'];
1076                 foreach($links as $link)
1077                         if($link['@attributes']['rel'] === NAMESPACE_OSTATUSSUB)
1078                                 return 'stat:' . $link['@attributes']['template'];              
1079         }
1080         return '';
1081 }}
1082
1083 // Given an email style address, perform webfinger lookup and 
1084 // return the array of link attributes from the personal XRD file.
1085 // On error/failure return an empty array.
1086
1087
1088 if(! function_exists('webfinger')) {
1089 function webfinger($s) {
1090         $host = '';
1091         if(strstr($s,'@')) {
1092                 $host = substr($s,strpos($s,'@') + 1);
1093         }
1094         if(strlen($host)) {
1095                 $tpl = fetch_lrdd_template($host);
1096                 logger('webfinger: lrdd template: ' . $tpl);
1097                 if(strlen($tpl)) {
1098                         $pxrd = str_replace('{uri}', urlencode('acct:'.$s), $tpl);
1099                         $links = fetch_xrd_links($pxrd);
1100                         if(! count($links)) {
1101                                 // try with double slashes
1102                                 $pxrd = str_replace('{uri}', urlencode('acct://'.$s), $tpl);
1103                                 $links = fetch_xrd_links($pxrd);
1104                         }
1105                         return $links;
1106                 }
1107         }
1108         return array();
1109 }}
1110
1111 if(! function_exists('lrdd')) {
1112 function lrdd($uri) {
1113
1114         $a = get_app();
1115
1116         if(strstr($uri,'@')) {  
1117                 return(webfinger($uri));
1118         }
1119         else {
1120                 $html = fetch_url($uri);
1121                 $headers = $a->get_curl_headers();
1122                 $lines = explode("\n",$headers);
1123                 if(count($lines)) {
1124                         foreach($lines as $line) {                              
1125                                 // TODO alter the following regex to support multiple relations (space separated)
1126                                 if((stristr($line,'link:')) && preg_match('/<([^>].*)>.*rel\=[\'\"]lrdd[\'\"]/',$line,$matches)) {
1127                                         $link = $matches[1];
1128                                         break;
1129                                 }
1130                         }
1131                 }
1132                 if(! isset($link)) {
1133                         // parse the page of the supplied URL looking for rel links
1134
1135                         require_once('library/HTML5/Parser.php');
1136                         $dom = HTML5_Parser::parse($html);
1137
1138                         if($dom) {
1139                                 $items = $dom->getElementsByTagName('link');
1140
1141                                 foreach($items as $item) {
1142                                         $x = $item->getAttribute('rel');
1143                                         if($x == "lrdd") {
1144                                                 $link = $item->getAttribute('href');
1145                                                 break;
1146                                         }
1147                                 }
1148                         }
1149                 }
1150
1151                 if(isset($link))
1152                         return(fetch_xrd_links($link));
1153         }
1154         return array();
1155 }}
1156
1157
1158
1159 // Given a host name, locate the LRDD template from that
1160 // host. Returns the LRDD template or an empty string on
1161 // error/failure.
1162
1163 if(! function_exists('fetch_lrdd_template')) {
1164 function fetch_lrdd_template($host) {
1165         $tpl = '';
1166         $url = 'http://' . $host . '/.well-known/host-meta' ;
1167         $links = fetch_xrd_links($url);
1168         if(count($links)) {
1169                 foreach($links as $link)
1170                         if($link['@attributes']['rel'] && $link['@attributes']['rel'] === 'lrdd')
1171                                 $tpl = $link['@attributes']['template'];
1172         }
1173         if(! strpos($tpl,'{uri}'))
1174                 $tpl = '';
1175         return $tpl;
1176 }}
1177
1178 // Given a URL, retrieve the page as an XRD document.
1179 // Return an array of links.
1180 // on error/failure return empty array.
1181
1182 if(! function_exists('fetch_xrd_links')) {
1183 function fetch_xrd_links($url) {
1184
1185
1186         $xml = fetch_url($url);
1187         if (! $xml)
1188                 return array();
1189
1190         logger('fetch_xrd_links: ' . $xml, LOGGER_DATA);
1191         $h = simplexml_load_string($xml);
1192         $arr = convert_xml_element_to_array($h);
1193
1194         if(isset($arr['xrd']['link'])) {
1195                 $link = $arr['xrd']['link'];
1196                 if(! isset($link[0]))
1197                         $links = array($link);
1198                 else
1199                         $links = $link;
1200                 return $links;
1201         }
1202         return array();
1203 }}
1204
1205 // Convert an ACL array to a storable string
1206
1207 if(! function_exists('perms2str')) {
1208 function perms2str($p) {
1209         $ret = '';
1210         $tmp = $p;
1211         if(is_array($tmp)) {
1212                 array_walk($tmp,'sanitise_acl');
1213                 $ret = implode('',$tmp);
1214         }
1215         return $ret;
1216 }}
1217
1218 // generate a guaranteed unique (for this domain) item ID for ATOM
1219 // safe from birthday paradox
1220
1221 if(! function_exists('item_new_uri')) {
1222 function item_new_uri($hostname,$uid) {
1223
1224         do {
1225                 $dups = false;
1226                 $hash = random_string();
1227
1228                 $uri = "urn:X-dfrn:" . $hostname . ':' . $uid . ':' . $hash;
1229
1230                 $r = q("SELECT `id` FROM `item` WHERE `uri` = '%s' LIMIT 1",
1231                         dbesc($uri));
1232                 if(count($r))
1233                         $dups = true;
1234         } while($dups == true);
1235         return $uri;
1236 }}
1237
1238 // Generate a guaranteed unique photo ID.
1239 // safe from birthday paradox
1240
1241 if(! function_exists('photo_new_resource')) {
1242 function photo_new_resource() {
1243
1244         do {
1245                 $found = false;
1246                 $resource = hash('md5',uniqid(mt_rand(),true));
1247                 $r = q("SELECT `id` FROM `photo` WHERE `resource-id` = '%s' LIMIT 1",
1248                         dbesc($resource)
1249                 );
1250                 if(count($r))
1251                         $found = true;
1252         } while($found == true);
1253         return $resource;
1254 }}
1255
1256
1257 // Take a URL from the wild, prepend http:// if necessary
1258 // and check DNS to see if it's real
1259 // return true if it's OK, false if something is wrong with it
1260
1261 if(! function_exists('validate_url')) {
1262 function validate_url(&$url) {
1263         if(substr($url,0,4) != 'http')
1264                 $url = 'http://' . $url;
1265         $h = parse_url($url);
1266
1267         if(($h) && (checkdnsrr($h['host'], 'ANY'))) {
1268                 return true;
1269         }
1270         return false;
1271 }}
1272
1273 // checks that email is an actual resolvable internet address
1274
1275 if(! function_exists('validate_email')) {
1276 function validate_email($addr) {
1277
1278         if(! strpos($addr,'@'))
1279                 return false;
1280         $h = substr($addr,strpos($addr,'@') + 1);
1281
1282         if(($h) && (checkdnsrr($h, 'ANY'))) {
1283                 return true;
1284         }
1285         return false;
1286 }}
1287
1288 // Check $url against our list of allowed sites,
1289 // wildcards allowed. If allowed_sites is unset return true;
1290 // If url is allowed, return true.
1291 // otherwise, return false
1292
1293 if(! function_exists('allowed_url')) {
1294 function allowed_url($url) {
1295
1296         $h = parse_url($url);
1297
1298         if(! $h) {
1299                 return false;
1300         }
1301
1302         $str_allowed = get_config('system','allowed_sites');
1303         if(! $str_allowed)
1304                 return true;
1305
1306         $found = false;
1307
1308         $host = strtolower($h['host']);
1309
1310         // always allow our own site
1311
1312         if($host == strtolower($_SERVER['SERVER_NAME']))
1313                 return true;
1314
1315         $fnmatch = function_exists('fnmatch');
1316         $allowed = explode(',',$str_allowed);
1317
1318         if(count($allowed)) {
1319                 foreach($allowed as $a) {
1320                         $pat = strtolower(trim($a));
1321                         if(($fnmatch && fnmatch($pat,$host)) || ($pat == $host)) {
1322                                 $found = true; 
1323                                 break;
1324                         }
1325                 }
1326         }
1327         return $found;
1328 }}
1329
1330 // check if email address is allowed to register here.
1331 // Compare against our list (wildcards allowed).
1332 // Returns false if not allowed, true if allowed or if
1333 // allowed list is not configured.
1334
1335 if(! function_exists('allowed_email')) {
1336 function allowed_email($email) {
1337
1338
1339         $domain = strtolower(substr($email,strpos($email,'@') + 1));
1340         if(! $domain)
1341                 return false;
1342
1343         $str_allowed = get_config('system','allowed_email');
1344         if(! $str_allowed)
1345                 return true;
1346
1347         $found = false;
1348
1349         $fnmatch = function_exists('fnmatch');
1350         $allowed = explode(',',$str_allowed);
1351
1352         if(count($allowed)) {
1353                 foreach($allowed as $a) {
1354                         $pat = strtolower(trim($a));
1355                         if(($fnmatch && fnmatch($pat,$host)) || ($pat == $host)) {
1356                                 $found = true; 
1357                                 break;
1358                         }
1359                 }
1360         }
1361         return $found;
1362 }}
1363
1364 // Format the like/dislike text for a profile item
1365 // $cnt = number of people who like/dislike the item
1366 // $arr = array of pre-linked names of likers/dislikers
1367 // $type = one of 'like, 'dislike'
1368 // $id  = item id
1369 // returns formatted text
1370
1371 if(! function_exists('format_like')) {
1372 function format_like($cnt,$arr,$type,$id) {
1373         $o = '';
1374         if($cnt == 1)
1375                 $o .= $arr[0] . (($type === 'like') ? t(' likes this.') : t(' doesn\'t like this.')) . EOL ;
1376         else {
1377                 $o .= '<span class="fakelink" onclick="openClose(\'' . $type . 'list-' . $id . '\');" >' 
1378                         . $cnt . ' ' . t('people') . '</span> ' . (($type === 'like') ? t('like this.') : t('don\'t like this.')) . EOL ;
1379                 $total = count($arr);
1380                 if($total >= MAX_LIKERS)
1381                         $arr = array_slice($arr, 0, MAX_LIKERS - 1);
1382                 if($total < MAX_LIKERS)
1383                         $arr[count($arr)-1] = t('and') . ' ' . $arr[count($arr)-1];
1384                 $str = implode(', ', $arr);
1385                 if($total >= MAX_LIKERS)
1386                         $str .= t(', and ') . $total - MAX_LIKERS . t(' other people');
1387                 $str .= (($type === 'like') ? t(' like this.') : t(' don\'t like this.'));
1388                 $o .= "\t" . '<div id="' . $type . 'list-' . $id . '" style="display: none;" >' . $str . '</div>';
1389         }
1390         return $o;
1391 }}
1392
1393
1394 // wrapper to load a view template, checking for alternate
1395 // languages before falling back to the default
1396
1397 if(! function_exists('load_view_file')) {
1398 function load_view_file($s) {
1399         $b = basename($s);
1400         $d = dirname($s);
1401         $lang = get_config('system','language');
1402         if($lang === false)
1403                 $lang = 'en';
1404         if(file_exists("$d/$lang/$b"))
1405                 return file_get_contents("$d/$lang/$b");
1406         return file_get_contents($s);
1407 }}
1408
1409 // for html,xml parsing - let's say you've got
1410 // an attribute foobar="class1 class2 class3"
1411 // and you want to find out if it contains 'class3'.
1412 // you can't use a normal sub string search because you
1413 // might match 'notclass3' and a regex to do the job is 
1414 // possible but a bit complicated. 
1415 // pass the attribute string as $attr and the attribute you 
1416 // are looking for as $s - returns true if found, otherwise false
1417
1418 if(! function_exists('attribute_contains')) {
1419 function attribute_contains($attr,$s) {
1420         $a = explode(' ', $attr);
1421         if(count($a) && in_array($s,$a))
1422                 return true;
1423         return false;
1424 }}
1425
1426 if(! function_exists('logger')) {
1427 function logger($msg,$level = 0) {
1428
1429         $debugging = get_config('system','debugging');
1430         $loglevel  = intval(get_config('system','loglevel'));
1431         $logfile   = get_config('system','logfile');
1432
1433         if((! $debugging) || (! $logfile) || ($level > $loglevel))
1434                 return;
1435         
1436         @file_put_contents($logfile, datetime_convert() . ':' . session_id() . ' ' . $msg . "\n", FILE_APPEND);
1437         return;
1438 }}
1439
1440
1441 if(! function_exists('activity_match')) {
1442 function activity_match($haystack,$needle) {
1443         if(($haystack === $needle) || ((basename($needle) === $haystack) && strstr($needle,NAMESPACE_ACTIVITY_SCHEMA)))
1444                 return true;
1445         return false;
1446 }}
1447
1448
1449 // Pull out all #hashtags and @person tags from $s;
1450 // We also get @person@domain.com - which would make 
1451 // the regex quite complicated as tags can also
1452 // end a sentence. So we'll run through our results
1453 // and strip the period from any tags which end with one.
1454 // Returns array of tags found, or empty array.
1455
1456
1457 if(! function_exists('get_tags')) {
1458 function get_tags($s) {
1459         $ret = array();
1460         if(preg_match_all('/([@#][^ \x0D\x0A,:?]*)([ \x0D\x0A,:?]|$)/',$s,$match)) {
1461                 foreach($match[1] as $match) {
1462                         if(strstr($match,"]")) {
1463                                 // we might be inside a bbcode color tag - leave it alone
1464                                 continue;
1465                         }
1466                         if(substr($match,-1,1) === '.')
1467                                 $ret[] = substr($match,0,-1);
1468                         else
1469                                 $ret[] = $match;
1470                 }
1471         }
1472
1473         return $ret;
1474 }}
1475
1476
1477 // quick and dirty quoted_printable encoding
1478
1479 if(! function_exists('qp')) {
1480 function qp($s) {
1481 return str_replace ("%","=",rawurlencode($s));
1482 }} 
1483
1484
1485 if(! function_exists('like_puller')) {
1486 function like_puller($a,$item,&$arr,$mode) {
1487
1488         $url = '';
1489         $sparkle = '';
1490         $verb = (($mode === 'like') ? ACTIVITY_LIKE : ACTIVITY_DISLIKE);
1491
1492         if((activity_match($item['verb'],$verb)) && ($item['id'] != $item['parent'])) {
1493                 $url = $item['author-link'];
1494                 if(($item['network'] === 'dfrn') && (! $item['self']) && ($item['author-link'] == $item['url'])) {
1495                         $url = $a->get_baseurl() . '/redir/' . $item['contact-id'];
1496                         $sparkle = ' class="sparkle" ';
1497                 }
1498                 if(! ((isset($arr[$item['parent'] . '-l'])) && (is_array($arr[$item['parent'] . '-l']))))
1499                         $arr[$item['parent'] . '-l'] = array();
1500                 if(! isset($arr[$item['parent']]))
1501                         $arr[$item['parent']] = 1;
1502                 else    
1503                         $arr[$item['parent']] ++;
1504                 $arr[$item['parent'] . '-l'][] = '<a href="'. $url . '"'. $sparkle .'>' . $item['author-name'] . '</a>';
1505         }
1506         return;
1507 }}
1508
1509 if(! function_exists('get_mentions')) {
1510 function get_mentions($item) {
1511         $o = '';
1512         if(! strlen($item['tag']))
1513                 return $o;
1514
1515         $arr = explode(',',$item['tag']);
1516         foreach($arr as $x) {
1517                 $matches = null;
1518                 if(preg_match('/@\[url=([^\]]*)\]/',$x,$matches))
1519                         $o .= "\t\t" . '<link rel="mentioned" href="' . $matches[1] . '" />' . "\r\n";
1520         }
1521         return $o;
1522 }}
1523
1524 if(! function_exists('contact_block')) {
1525 function contact_block() {
1526         $o = '';
1527         $a = get_app();
1528         if((! is_array($a->profile)) || ($a->profile['hide-friends']))
1529                 return $o;
1530         $r = q("SELECT COUNT(*) AS `total` FROM `contact` WHERE `uid` = %d AND `self` = 0 AND `blocked` = 0 and `pending` = 0",
1531                         intval($a->profile['uid'])
1532         );
1533         if(count($r)) {
1534                 $total = intval($r[0]['total']);
1535         }
1536         if(! $total) {
1537                 $o .= '<h4 class="contact-h4">' . t('No contacts') . '</h4>';
1538                 return $o;
1539         }
1540         $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `self` = 0 AND `blocked` = 0 and `pending` = 0 ORDER BY RAND() LIMIT 24",
1541                         intval($a->profile['uid'])
1542         );
1543         if(count($r)) {
1544                 $o .= '<h4 class="contact-h4">' . $total . ' ' . t('Contacts') . '</h4><div id="contact-block">';
1545                 foreach($r as $rr) {
1546                         $redirect_url = $a->get_baseurl() . '/redir/' . $rr['id'];
1547                         if(local_user() && ($rr['uid'] == local_user())
1548                                 && ($rr['network'] === 'dfrn')) {
1549                                 $url = $redirect_url;
1550                                 $sparkle = ' sparkle';
1551                         }
1552                         else {
1553                                 $url = $rr['url'];
1554                                 $sparkle = '';
1555                         }
1556
1557                         $o .= '<div class="contact-block-div"><a class="contact-block-link' . $sparkle . '" href="' . $url . '" ><img class="contact-block-img' . $sparkle . '" src="' . $rr['micro'] . '" title="' . $rr['name'] . ' [' . $rr['url'] . ']" alt="' . $rr['name'] . '" /></a></div>' . "\r\n";
1558                 }
1559                 $o .= '</div><div id="contact-block-end"></div>';
1560                 $o .=  '<div id="viewcontacts"><a id="viewcontacts-link" href="viewcontacts/' . $a->profile['nickname'] . '">' . t('View Contacts') . '</a></div>';
1561                 
1562         }
1563         return $o;
1564
1565 }}
1566
1567 if(! function_exists('search')) {
1568 function search($s) {
1569         $a = get_app();
1570         $o  = '<div id="search-box">';
1571         $o .= '<form action="' . $a->get_baseurl() . '/search' . '" method="get" >';
1572         $o .= '<input type="text" name="search" id="search-text" value="' . $s .'" />';
1573         $o .= '<input type="submit" name="submit" id="search-submit" value="' . t('Search') . '" />'; 
1574         $o .= '</form></div>';
1575         return $o;
1576 }}
1577
1578 if(! function_exists('valid_email')) {
1579 function valid_email($x){
1580         if(preg_match('/^[_a-zA-Z0-9-]+(\.[_a-zA-Z0-9-]+)*@[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)+$/',$x))
1581                 return true;
1582         return false;
1583 }}
1584
1585
1586 if(! function_exists('gravatar_img')) {
1587 function gravatar_img($email) {
1588         $size = 175;
1589         $opt = 'identicon';   // psuedo-random geometric pattern if not found
1590         $rating = 'pg';
1591         $hash = md5(trim(strtolower($email)));
1592         
1593         $url = 'http://www.gravatar.com/avatar/' . $hash . '.jpg' 
1594                 . '?s=' . $size . '&d=' . $opt . '&r=' . $rating;
1595
1596         logger('gravatar: ' . $email . ' ' . $url);
1597         return $url;
1598 }}
1599
1600 if(! function_exists('aes_decrypt')) {
1601 function aes_decrypt($val,$ky)
1602 {
1603     $key="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
1604     for($a=0;$a<strlen($ky);$a++)
1605       $key[$a%16]=chr(ord($key[$a%16]) ^ ord($ky[$a]));
1606     $mode = MCRYPT_MODE_ECB;
1607     $enc = MCRYPT_RIJNDAEL_128;
1608     $dec = @mcrypt_decrypt($enc, $key, $val, $mode, @mcrypt_create_iv( @mcrypt_get_iv_size($enc, $mode), MCRYPT_DEV_URANDOM ) );
1609     return rtrim($dec,(( ord(substr($dec,strlen($dec)-1,1))>=0 and ord(substr($dec, strlen($dec)-1,1))<=16)? chr(ord( substr($dec,strlen($dec)-1,1))):null));
1610 }}
1611
1612
1613 if(! function_exists('aes_encrypt')) {
1614 function aes_encrypt($val,$ky)
1615 {
1616     $key="\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0";
1617     for($a=0;$a<strlen($ky);$a++)
1618       $key[$a%16]=chr(ord($key[$a%16]) ^ ord($ky[$a]));
1619     $mode=MCRYPT_MODE_ECB;
1620     $enc=MCRYPT_RIJNDAEL_128;
1621     $val=str_pad($val, (16*(floor(strlen($val) / 16)+(strlen($val) % 16==0?2:1))), chr(16-(strlen($val) % 16)));
1622     return mcrypt_encrypt($enc, $key, $val, $mode, mcrypt_create_iv( mcrypt_get_iv_size($enc, $mode), MCRYPT_DEV_URANDOM));
1623 }} 
1624
1625 if(! function_exists('linkify')) {
1626 function linkify($s) {
1627         $s = preg_replace("/(https?\:\/\/[a-zA-Z0-9\:\/\-\?\&\.\=\_\~\#\'\%]*)/", ' <a href="$1" >$1</a>', $s);
1628         return($s);
1629 }}
1630
1631 if(! function_exists('smilies')) {
1632 function smilies($s) {
1633         $a = get_app();
1634
1635         return str_replace(
1636         array( ':-)', ';-)', ':-(', ':(', ':-P', ':-"', ':-x', ':-X', ':-D', '8-|', '8-O'),
1637         array(
1638                 '<img src="' . $a->get_baseurl() . '/images/smiley-smile.gif" alt=":-)" />',
1639                 '<img src="' . $a->get_baseurl() . '/images/smiley-wink.gif" alt=";-)" />',
1640                 '<img src="' . $a->get_baseurl() . '/images/smiley-frown.gif" alt=":-(" />',
1641                 '<img src="' . $a->get_baseurl() . '/images/smiley-frown.gif" alt=":(" />',
1642                 '<img src="' . $a->get_baseurl() . '/images/smiley-tongue-out.gif" alt=":-P" />',
1643                 '<img src="' . $a->get_baseurl() . '/images/smiley-kiss.gif" alt=":-\"" />',
1644                 '<img src="' . $a->get_baseurl() . '/images/smiley-kiss.gif" alt=":-x" />',
1645                 '<img src="' . $a->get_baseurl() . '/images/smiley-kiss.gif" alt=":-X" />',
1646                 '<img src="' . $a->get_baseurl() . '/images/smiley-laughing.gif" alt=":-D" />',
1647                 '<img src="' . $a->get_baseurl() . '/images/smiley-surprised.gif" alt="8-|" />',
1648                 '<img src="' . $a->get_baseurl() . '/images/smiley-surprised.gif" alt="8-O" />'
1649         ), $s);
1650 }}