Friendica Communications Platform (please note that this is a clone of the repository at github, issues are handled there) https://friendi.ca
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2674 lines
72 KiB

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