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