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