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