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