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.

2878 lines
96 KiB

11 years ago
11 years ago
11 years ago
10 years ago
10 years ago
10 years ago
10 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
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
10 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
10 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
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
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
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
10 years ago
11 years ago
10 years ago
10 years ago
11 years ago
11 years ago
11 years ago
10 years ago
11 years ago
10 years ago
  1. <?php
  2. require_once('include/bbcode.php');
  3. require_once('include/oembed.php');
  4. require_once('include/salmon.php');
  5. require_once('include/crypto.php');
  6. function get_feed_for(&$a, $dfrn_id, $owner_nick, $last_update, $direction = 0) {
  7. // default permissions - anonymous user
  8. if(! strlen($owner_nick))
  9. killme();
  10. $public_feed = (($dfrn_id) ? false : true);
  11. $starred = false;
  12. $converse = false;
  13. if($public_feed && $a->argc > 2) {
  14. for($x = 2; $x < $a->argc; $x++) {
  15. if($a->argv[$x] == 'converse')
  16. $converse = true;
  17. if($a->argv[$x] == 'starred')
  18. $starred = true;
  19. }
  20. }
  21. $sql_extra = " AND `allow_cid` = '' AND `allow_gid` = '' AND `deny_cid` = '' AND `deny_gid` = '' ";
  22. $r = q("SELECT `contact`.*, `user`.`uid` AS `user_uid`, `user`.`nickname`, `user`.`timezone`
  23. FROM `contact` LEFT JOIN `user` ON `user`.`uid` = `contact`.`uid`
  24. WHERE `contact`.`self` = 1 AND `user`.`nickname` = '%s' LIMIT 1",
  25. dbesc($owner_nick)
  26. );
  27. if(! count($r))
  28. killme();
  29. $owner = $r[0];
  30. $owner_id = $owner['user_uid'];
  31. $owner_nick = $owner['nickname'];
  32. $birthday = feed_birthday($owner_id,$owner['timezone']);
  33. if(! $public_feed) {
  34. $sql_extra = '';
  35. switch($direction) {
  36. case (-1):
  37. $sql_extra = sprintf(" AND `issued-id` = '%s' ", dbesc($dfrn_id));
  38. $my_id = $dfrn_id;
  39. break;
  40. case 0:
  41. $sql_extra = sprintf(" AND `issued-id` = '%s' AND `duplex` = 1 ", dbesc($dfrn_id));
  42. $my_id = '1:' . $dfrn_id;
  43. break;
  44. case 1:
  45. $sql_extra = sprintf(" AND `dfrn-id` = '%s' AND `duplex` = 1 ", dbesc($dfrn_id));
  46. $my_id = '0:' . $dfrn_id;
  47. break;
  48. default:
  49. return false;
  50. break; // NOTREACHED
  51. }
  52. $r = q("SELECT * FROM `contact` WHERE `blocked` = 0 AND `pending` = 0 AND `contact`.`uid` = %d $sql_extra LIMIT 1",
  53. intval($owner_id)
  54. );
  55. if(! count($r))
  56. killme();
  57. $contact = $r[0];
  58. $groups = init_groups_visitor($contact['id']);
  59. if(count($groups)) {
  60. for($x = 0; $x < count($groups); $x ++)
  61. $groups[$x] = '<' . intval($groups[$x]) . '>' ;
  62. $gs = implode('|', $groups);
  63. }
  64. else
  65. $gs = '<<>>' ; // Impossible to match
  66. $sql_extra = sprintf("
  67. AND ( `allow_cid` = '' OR `allow_cid` REGEXP '<%d>' )
  68. AND ( `deny_cid` = '' OR NOT `deny_cid` REGEXP '<%d>' )
  69. AND ( `allow_gid` = '' OR `allow_gid` REGEXP '%s' )
  70. AND ( `deny_gid` = '' OR NOT `deny_gid` REGEXP '%s')
  71. ",
  72. intval($contact['id']),
  73. intval($contact['id']),
  74. dbesc($gs),
  75. dbesc($gs)
  76. );
  77. }
  78. if($public_feed)
  79. $sort = 'DESC';
  80. else
  81. $sort = 'ASC';
  82. if(! strlen($last_update))
  83. $last_update = 'now -30 days';
  84. if($public_feed) {
  85. if(! $converse)
  86. $sql_extra .= " AND `contact`.`self` = 1 ";
  87. }
  88. $check_date = datetime_convert('UTC','UTC',$last_update,'Y-m-d H:i:s');
  89. $r = q("SELECT `item`.*, `item`.`id` AS `item_id`,
  90. `contact`.`name`, `contact`.`photo`, `contact`.`url`,
  91. `contact`.`name-date`, `contact`.`uri-date`, `contact`.`avatar-date`,
  92. `contact`.`thumb`, `contact`.`dfrn-id`, `contact`.`self`,
  93. `contact`.`id` AS `contact-id`, `contact`.`uid` AS `contact-uid`,
  94. `sign`.`signed_text`, `sign`.`signature`, `sign`.`signer`
  95. FROM `item` LEFT JOIN `contact` ON `contact`.`id` = `item`.`contact-id`
  96. LEFT JOIN `sign` ON `sign`.`iid` = `item`.`id`
  97. WHERE `item`.`uid` = %d AND `item`.`visible` = 1 AND `item`.`parent` != 0
  98. AND `item`.`wall` = 1 AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0
  99. AND ( `item`.`edited` > '%s' OR `item`.`changed` > '%s' )
  100. $sql_extra
  101. ORDER BY `parent` %s, `created` ASC LIMIT 0, 300",
  102. intval($owner_id),
  103. dbesc($check_date),
  104. dbesc($check_date),
  105. dbesc($sort)
  106. );
  107. // Will check further below if this actually returned results.
  108. // We will provide an empty feed if that is the case.
  109. $items = $r;
  110. $feed_template = get_markup_template(($dfrn_id) ? 'atom_feed_dfrn.tpl' : 'atom_feed.tpl');
  111. $atom = '';
  112. $hubxml = feed_hublinks();
  113. $salmon = feed_salmonlinks($owner_nick);
  114. $atom .= replace_macros($feed_template, array(
  115. '$version' => xmlify(FRIENDIKA_VERSION),
  116. '$feed_id' => xmlify($a->get_baseurl() . '/profile/' . $owner_nick),
  117. '$feed_title' => xmlify($owner['name']),
  118. '$feed_updated' => xmlify(datetime_convert('UTC', 'UTC', 'now' , ATOM_TIME)) ,
  119. '$hub' => $hubxml,
  120. '$salmon' => $salmon,
  121. '$name' => xmlify($owner['name']),
  122. '$profile_page' => xmlify($owner['url']),
  123. '$photo' => xmlify($owner['photo']),
  124. '$thumb' => xmlify($owner['thumb']),
  125. '$picdate' => xmlify(datetime_convert('UTC','UTC',$owner['avatar-date'] . '+00:00' , ATOM_TIME)) ,
  126. '$uridate' => xmlify(datetime_convert('UTC','UTC',$owner['uri-date'] . '+00:00' , ATOM_TIME)) ,
  127. '$namdate' => xmlify(datetime_convert('UTC','UTC',$owner['name-date'] . '+00:00' , ATOM_TIME)) ,
  128. '$birthday' => ((strlen($birthday)) ? '<dfrn:birthday>' . xmlify($birthday) . '</dfrn:birthday>' : '')
  129. ));
  130. call_hooks('atom_feed', $atom);
  131. if(! count($items)) {
  132. call_hooks('atom_feed_end', $atom);
  133. $atom .= '</feed>' . "\r\n";
  134. return $atom;
  135. }
  136. foreach($items as $item) {
  137. // public feeds get html, our own nodes use bbcode
  138. if($public_feed) {
  139. $type = 'html';
  140. // catch any email that's in a public conversation and make sure it doesn't leak
  141. if($item['private'])
  142. continue;
  143. }
  144. else {
  145. $type = 'text';
  146. }
  147. $atom .= atom_entry($item,$type,null,$owner,true);
  148. }
  149. call_hooks('atom_feed_end', $atom);
  150. $atom .= '</feed>' . "\r\n";
  151. return $atom;
  152. }
  153. function construct_verb($item) {
  154. if($item['verb'])
  155. return $item['verb'];
  156. return ACTIVITY_POST;
  157. }
  158. function construct_activity_object($item) {
  159. if($item['object']) {
  160. $o = '<as:object>' . "\r\n";
  161. $r = parse_xml_string($item['object'],false);
  162. if(! $r)
  163. return '';
  164. if($r->type)
  165. $o .= '<as:object-type>' . xmlify($r->type) . '</as:object-type>' . "\r\n";
  166. if($r->id)
  167. $o .= '<id>' . xmlify($r->id) . '</id>' . "\r\n";
  168. if($r->title)
  169. $o .= '<title>' . xmlify($r->title) . '</title>' . "\r\n";
  170. if($r->link) {
  171. if(substr($r->link,0,1) === '<') {
  172. // patch up some facebook "like" activity objects that got stored incorrectly
  173. // for a couple of months prior to 9-Jun-2011 and generated bad XML.
  174. // we can probably remove this hack here and in the following function in a few months time.
  175. if(strstr($r->link,'&') && (! strstr($r->link,'&amp;')))
  176. $r->link = str_replace('&','&amp;', $r->link);
  177. $r->link = preg_replace('/\<link(.*?)\"\>/','<link$1"/>',$r->link);
  178. $o .= $r->link;
  179. }
  180. else
  181. $o .= '<link rel="alternate" type="text/html" href="' . xmlify($r->link) . '" />' . "\r\n";
  182. }
  183. if($r->content)
  184. $o .= '<content type="html" >' . xmlify(bbcode($r->content)) . '</content>' . "\r\n";
  185. $o .= '</as:object>' . "\r\n";
  186. return $o;
  187. }
  188. return '';
  189. }
  190. function construct_activity_target($item) {
  191. if($item['target']) {
  192. $o = '<as:target>' . "\r\n";
  193. $r = parse_xml_string($item['target'],false);
  194. if(! $r)
  195. return '';
  196. if($r->type)
  197. $o .= '<as:object-type>' . xmlify($r->type) . '</as:object-type>' . "\r\n";
  198. if($r->id)
  199. $o .= '<id>' . xmlify($r->id) . '</id>' . "\r\n";
  200. if($r->title)
  201. $o .= '<title>' . xmlify($r->title) . '</title>' . "\r\n";
  202. if($r->link) {
  203. if(substr($r->link,0,1) === '<') {
  204. if(strstr($r->link,'&') && (! strstr($r->link,'&amp;')))
  205. $r->link = str_replace('&','&amp;', $r->link);
  206. $r->link = preg_replace('/\<link(.*?)\"\>/','<link$1"/>',$r->link);
  207. $o .= $r->link;
  208. }
  209. else
  210. $o .= '<link rel="alternate" type="text/html" href="' . xmlify($r->link) . '" />' . "\r\n";
  211. }
  212. if($r->content)
  213. $o .= '<content type="html" >' . xmlify(bbcode($r->content)) . '</content>' . "\r\n";
  214. $o .= '</as:target>' . "\r\n";
  215. return $o;
  216. }
  217. return '';
  218. }
  219. function get_atom_elements($feed,$item) {
  220. require_once('library/HTMLPurifier.auto.php');
  221. require_once('include/html2bbcode.php');
  222. $best_photo = array();
  223. $res = array();
  224. $author = $item->get_author();
  225. if($author) {
  226. $res['author-name'] = unxmlify($author->get_name());
  227. $res['author-link'] = unxmlify($author->get_link());
  228. }
  229. else {
  230. $res['author-name'] = unxmlify($feed->get_title());
  231. $res['author-link'] = unxmlify($feed->get_permalink());
  232. }
  233. $res['uri'] = unxmlify($item->get_id());
  234. $res['title'] = unxmlify($item->get_title());
  235. $res['body'] = unxmlify($item->get_content());
  236. $res['plink'] = unxmlify($item->get_link(0));
  237. // look for a photo. We should check media size and find the best one,
  238. // but for now let's just find any author photo
  239. $rawauthor = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'author');
  240. if($rawauthor && $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) {
  241. $base = $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'];
  242. foreach($base as $link) {
  243. if(! $res['author-avatar']) {
  244. if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar')
  245. $res['author-avatar'] = unxmlify($link['attribs']['']['href']);
  246. }
  247. }
  248. }
  249. $rawactor = $item->get_item_tags(NAMESPACE_ACTIVITY, 'actor');
  250. if($rawactor && activity_match($rawactor[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data'],ACTIVITY_OBJ_PERSON)) {
  251. $base = $rawactor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'];
  252. if($base && count($base)) {
  253. foreach($base as $link) {
  254. if($link['attribs']['']['rel'] === 'alternate' && (! $res['author-link']))
  255. $res['author-link'] = unxmlify($link['attribs']['']['href']);
  256. if(! $res['author-avatar']) {
  257. if($link['attribs']['']['rel'] === 'avatar' || $link['attribs']['']['rel'] === 'photo')
  258. $res['author-avatar'] = unxmlify($link['attribs']['']['href']);
  259. }
  260. }
  261. }
  262. }
  263. // No photo/profile-link on the item - look at the feed level
  264. if((! (x($res,'author-link'))) || (! (x($res,'author-avatar')))) {
  265. $rawauthor = $feed->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'author');
  266. if($rawauthor && $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) {
  267. $base = $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'];
  268. foreach($base as $link) {
  269. if($link['attribs']['']['rel'] === 'alternate' && (! $res['author-link']))
  270. $res['author-link'] = unxmlify($link['attribs']['']['href']);
  271. if(! $res['author-avatar']) {
  272. if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar')
  273. $res['author-avatar'] = unxmlify($link['attribs']['']['href']);
  274. }
  275. }
  276. }
  277. $rawactor = $feed->get_feed_tags(NAMESPACE_ACTIVITY, 'subject');
  278. if($rawactor && activity_match($rawactor[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data'],ACTIVITY_OBJ_PERSON)) {
  279. $base = $rawactor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'];
  280. if($base && count($base)) {
  281. foreach($base as $link) {
  282. if($link['attribs']['']['rel'] === 'alternate' && (! $res['author-link']))
  283. $res['author-link'] = unxmlify($link['attribs']['']['href']);
  284. if(! (x($res,'author-avatar'))) {
  285. if($link['attribs']['']['rel'] === 'avatar' || $link['attribs']['']['rel'] === 'photo')
  286. $res['author-avatar'] = unxmlify($link['attribs']['']['href']);
  287. }
  288. }
  289. }
  290. }
  291. }
  292. $apps = $item->get_item_tags(NAMESPACE_STATUSNET,'notice_info');
  293. if($apps && $apps[0]['attribs']['']['source']) {
  294. $res['app'] = strip_tags(unxmlify($apps[0]['attribs']['']['source']));
  295. if($res['app'] === 'web')
  296. $res['app'] = 'OStatus';
  297. }
  298. // base64 encoded json structure representing Diaspora signature
  299. $dsig = $item->get_item_tags(NAMESPACE_DFRN,'diaspora_signature');
  300. if($dsig) {
  301. $res['dsprsig'] = unxmlify($dsig[0]['data']);
  302. }
  303. $dguid = $item->get_item_tags(NAMESPACE_DFRN,'diaspora_guid');
  304. if($dguid)
  305. $res['guid'] = unxmlify($dguid[0]['data']);
  306. $bm = $item->get_item_tags(NAMESPACE_DFRN,'bookmark');
  307. if($bm)
  308. $res['bookmark'] = ((unxmlify($bm[0]['data']) === 'true') ? 1 : 0);
  309. /**
  310. * If there's a copy of the body content which is guaranteed to have survived mangling in transit, use it.
  311. */
  312. $have_real_body = false;
  313. $rawenv = $item->get_item_tags(NAMESPACE_DFRN, 'env');
  314. if($rawenv) {
  315. $have_real_body = true;
  316. $res['body'] = $rawenv[0]['data'];
  317. $res['body'] = str_replace(array(' ',"\t","\r","\n"), array('','','',''),$res['body']);
  318. // make sure nobody is trying to sneak some html tags by us
  319. $res['body'] = notags(base64url_decode($res['body']));
  320. }
  321. $maxlen = get_max_import_size();
  322. if($maxlen && (strlen($res['body']) > $maxlen))
  323. $res['body'] = substr($res['body'],0, $maxlen);
  324. // It isn't certain at this point whether our content is plaintext or html and we'd be foolish to trust
  325. // the content type. Our own network only emits text normally, though it might have been converted to
  326. // html if we used a pubsubhubbub transport. But if we see even one html tag in our text, we will
  327. // have to assume it is all html and needs to be purified.
  328. // It doesn't matter all that much security wise - because before this content is used anywhere, we are
  329. // going to escape any tags we find regardless, but this lets us import a limited subset of html from
  330. // the wild, by sanitising it and converting supported tags to bbcode before we rip out any remaining
  331. // html.
  332. if((strpos($res['body'],'<') !== false) || (strpos($res['body'],'>') !== false)) {
  333. $res['body'] = html2bb_video($res['body']);
  334. $res['body'] = oembed_html2bbcode($res['body']);
  335. $config = HTMLPurifier_Config::createDefault();
  336. $config->set('Cache.DefinitionImpl', null);
  337. // we shouldn't need a whitelist, because the bbcode converter
  338. // will strip out any unsupported tags.
  339. // $config->set('HTML.Allowed', 'p,b,a[href],i');
  340. $purifier = new HTMLPurifier($config);
  341. $res['body'] = $purifier->purify($res['body']);
  342. $res['body'] = html2bbcode($res['body']);
  343. }
  344. $allow = $item->get_item_tags(NAMESPACE_DFRN,'comment-allow');
  345. if($allow && $allow[0]['data'] == 1)
  346. $res['last-child'] = 1;
  347. else
  348. $res['last-child'] = 0;
  349. $private = $item->get_item_tags(NAMESPACE_DFRN,'private');
  350. if($private && $private[0]['data'] == 1)
  351. $res['private'] = 1;
  352. else
  353. $res['private'] = 0;
  354. $extid = $item->get_item_tags(NAMESPACE_DFRN,'extid');
  355. if($extid && $extid[0]['data'])
  356. $res['extid'] = $extid[0]['data'];
  357. $rawlocation = $item->get_item_tags(NAMESPACE_DFRN, 'location');
  358. if($rawlocation)
  359. $res['location'] = unxmlify($rawlocation[0]['data']);
  360. $rawcreated = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'published');
  361. if($rawcreated)
  362. $res['created'] = unxmlify($rawcreated[0]['data']);
  363. $rawedited = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'updated');
  364. if($rawedited)
  365. $res['edited'] = unxmlify($rawedited[0]['data']);
  366. if((x($res,'edited')) && (! (x($res,'created'))))
  367. $res['created'] = $res['edited'];
  368. if(! $res['created'])
  369. $res['created'] = $item->get_date('c');
  370. if(! $res['edited'])
  371. $res['edited'] = $item->get_date('c');
  372. // Disallow time travelling posts
  373. $d1 = strtotime($res['created']);
  374. $d2 = strtotime($res['edited']);
  375. $d3 = strtotime('now');
  376. if($d1 > $d3)
  377. $res['created'] = datetime_convert();
  378. if($d2 > $d3)
  379. $res['edited'] = datetime_convert();
  380. $rawowner = $item->get_item_tags(NAMESPACE_DFRN, 'owner');
  381. if($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])
  382. $res['owner-name'] = unxmlify($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']);
  383. elseif($rawowner[0]['child'][NAMESPACE_DFRN]['name'][0]['data'])
  384. $res['owner-name'] = unxmlify($rawowner[0]['child'][NAMESPACE_DFRN]['name'][0]['data']);
  385. if($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])
  386. $res['owner-link'] = unxmlify($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']);
  387. elseif($rawowner[0]['child'][NAMESPACE_DFRN]['uri'][0]['data'])
  388. $res['owner-link'] = unxmlify($rawowner[0]['child'][NAMESPACE_DFRN]['uri'][0]['data']);
  389. if($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) {
  390. $base = $rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'];
  391. foreach($base as $link) {
  392. if(! $res['owner-avatar']) {
  393. if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar')
  394. $res['owner-avatar'] = unxmlify($link['attribs']['']['href']);
  395. }
  396. }
  397. }
  398. $rawgeo = $item->get_item_tags(NAMESPACE_GEORSS,'point');
  399. if($rawgeo)
  400. $res['coord'] = unxmlify($rawgeo[0]['data']);
  401. $rawverb = $item->get_item_tags(NAMESPACE_ACTIVITY, 'verb');
  402. // select between supported verbs
  403. if($rawverb) {
  404. $res['verb'] = unxmlify($rawverb[0]['data']);
  405. }
  406. // translate OStatus unfollow to activity streams if it happened to get selected
  407. if((x($res,'verb')) && ($res['verb'] === 'http://ostatus.org/schema/1.0/unfollow'))
  408. $res['verb'] = ACTIVITY_UNFOLLOW;
  409. $cats = $item->get_categories();
  410. if($cats) {
  411. $tag_arr = array();
  412. foreach($cats as $cat) {
  413. $term = $cat->get_term();
  414. if(! $term)
  415. $term = $cat->get_label();
  416. $scheme = $cat->get_scheme();
  417. if($scheme && $term && stristr($scheme,'X-DFRN:'))
  418. $tag_arr[] = substr($scheme,7,1) . '[url=' . unxmlify(substr($scheme,9)) . ']' . unxmlify($term) . '[/url]';
  419. elseif($term)
  420. $tag_arr[] = notags(trim($term));
  421. }
  422. $res['tag'] = implode(',', $tag_arr);
  423. }
  424. $attach = $item->get_enclosures();
  425. if($attach) {
  426. $att_arr = array();
  427. foreach($attach as $att) {
  428. $len = intval($att->get_length());
  429. $link = str_replace(array(',','"'),array('%2D','%22'),notags(trim(unxmlify($att->get_link()))));
  430. $title = str_replace(array(',','"'),array('%2D','%22'),notags(trim(unxmlify($att->get_title()))));
  431. $type = str_replace(array(',','"'),array('%2D','%22'),notags(trim(unxmlify($att->get_type()))));
  432. if(strpos($type,';'))
  433. $type = substr($type,0,strpos($type,';'));
  434. if((! $link) || (strpos($link,'http') !== 0))
  435. continue;
  436. if(! $title)
  437. $title = ' ';
  438. if(! $type)
  439. $type = 'application/octet-stream';
  440. $att_arr[] = '[attach]href="' . $link . '" length="' . $len . '" type="' . $type . '" title="' . $title . '"[/attach]';
  441. }
  442. $res['attach'] = implode(',', $att_arr);
  443. }
  444. $rawobj = $item->get_item_tags(NAMESPACE_ACTIVITY, 'object');
  445. if($rawobj) {
  446. $res['object'] = '<object>' . "\n";
  447. if($rawobj[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data']) {
  448. $res['object-type'] = $rawobj[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data'];
  449. $res['object'] .= '<type>' . $rawobj[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data'] . '</type>' . "\n";
  450. }
  451. if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data'])
  452. $res['object'] .= '<id>' . $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data'] . '</id>' . "\n";
  453. if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'])
  454. $res['object'] .= '<link>' . encode_rel_links($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) . '</link>' . "\n";
  455. if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data'])
  456. $res['object'] .= '<title>' . $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data'] . '</title>' . "\n";
  457. if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data']) {
  458. $body = $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data'];
  459. if(! $body)
  460. $body = $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['summary'][0]['data'];
  461. // preserve a copy of the original body content in case we later need to parse out any microformat information, e.g. events
  462. $res['object'] .= '<orig>' . xmlify($body) . '</orig>' . "\n";
  463. if((strpos($body,'<') !== false) || (strpos($body,'>') !== false)) {
  464. $body = html2bb_video($body);
  465. $config = HTMLPurifier_Config::createDefault();
  466. $config->set('Cache.DefinitionImpl', null);
  467. $purifier = new HTMLPurifier($config);
  468. $body = $purifier->purify($body);
  469. $body = html2bbcode($body);
  470. }
  471. $res['object'] .= '<content>' . $body . '</content>' . "\n";
  472. }
  473. $res['object'] .= '</object>' . "\n";
  474. }
  475. $rawobj = $item->get_item_tags(NAMESPACE_ACTIVITY, 'target');
  476. if($rawobj) {
  477. $res['target'] = '<target>' . "\n";
  478. if($rawobj[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data']) {
  479. $res['target'] .= '<type>' . $rawobj[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data'] . '</type>' . "\n";
  480. }
  481. if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data'])
  482. $res['target'] .= '<id>' . $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data'] . '</id>' . "\n";
  483. if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'])
  484. $res['target'] .= '<link>' . encode_rel_links($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) . '</link>' . "\n";
  485. if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data'])
  486. $res['target'] .= '<title>' . $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data'] . '</title>' . "\n";
  487. if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data']) {
  488. $body = $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data'];
  489. if(! $body)
  490. $body = $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['summary'][0]['data'];
  491. // preserve a copy of the original body content in case we later need to parse out any microformat information, e.g. events
  492. $res['target'] .= '<orig>' . xmlify($body) . '</orig>' . "\n";
  493. if((strpos($body,'<') !== false) || (strpos($body,'>') !== false)) {
  494. $body = html2bb_video($body);
  495. $config = HTMLPurifier_Config::createDefault();
  496. $config->set('Cache.DefinitionImpl', null);
  497. $purifier = new HTMLPurifier($config);
  498. $body = $purifier->purify($body);
  499. $body = html2bbcode($body);
  500. }
  501. $res['target'] .= '<content>' . $body . '</content>' . "\n";
  502. }
  503. $res['target'] .= '</target>' . "\n";
  504. }
  505. $arr = array('feed' => $feed, 'item' => $item, 'result' => $res);
  506. call_hooks('parse_atom', $arr);
  507. return $res;
  508. }
  509. function encode_rel_links($links) {
  510. $o = '';
  511. if(! ((is_array($links)) && (count($links))))
  512. return $o;
  513. foreach($links as $link) {
  514. $o .= '<link ';
  515. if($link['attribs']['']['rel'])
  516. $o .= 'rel="' . $link['attribs']['']['rel'] . '" ';
  517. if($link['attribs']['']['type'])
  518. $o .= 'type="' . $link['attribs']['']['type'] . '" ';
  519. if($link['attribs']['']['href'])
  520. $o .= 'href="' . $link['attribs']['']['href'] . '" ';
  521. if( (x($link['attribs'],NAMESPACE_MEDIA)) && $link['attribs'][NAMESPACE_MEDIA]['width'])
  522. $o .= 'media:width="' . $link['attribs'][NAMESPACE_MEDIA]['width'] . '" ';
  523. if( (x($link['attribs'],NAMESPACE_MEDIA)) && $link['attribs'][NAMESPACE_MEDIA]['height'])
  524. $o .= 'media:height="' . $link['attribs'][NAMESPACE_MEDIA]['height'] . '" ';
  525. $o .= ' />' . "\n" ;
  526. }
  527. return xmlify($o);
  528. }
  529. function item_store($arr,$force_parent = false) {
  530. // If a Diaspora signature structure was passed in, pull it out of the
  531. // item array and set it aside for later storage.
  532. $dsprsig = null;
  533. if(x($arr,'dsprsig')) {
  534. $dsprsig = json_decode(base64_decode($arr['dsprsig']));
  535. unset($arr['dsprsig']);
  536. }
  537. if($arr['gravity'])
  538. $arr['gravity'] = intval($arr['gravity']);
  539. elseif($arr['parent-uri'] === $arr['uri'])
  540. $arr['gravity'] = 0;
  541. elseif(activity_match($arr['verb'],ACTIVITY_POST))
  542. $arr['gravity'] = 6;
  543. else
  544. $arr['gravity'] = 6; // extensible catchall
  545. if(! x($arr,'type'))
  546. $arr['type'] = 'remote';
  547. // Shouldn't happen but we want to make absolutely sure it doesn't leak from a plugin.
  548. if((strpos($arr['body'],'<') !== false) || (strpos($arr['body'],'>') !== false))
  549. $arr['body'] = strip_tags($arr['body']);
  550. $arr['wall'] = ((x($arr,'wall')) ? intval($arr['wall']) : 0);
  551. $arr['uri'] = ((x($arr,'uri')) ? notags(trim($arr['uri'])) : random_string());
  552. $arr['extid'] = ((x($arr,'extid')) ? notags(trim($arr['extid'])) : '');
  553. $arr['author-name'] = ((x($arr,'author-name')) ? notags(trim($arr['author-name'])) : '');
  554. $arr['author-link'] = ((x($arr,'author-link')) ? notags(trim($arr['author-link'])) : '');
  555. $arr['author-avatar'] = ((x($arr,'author-avatar')) ? notags(trim($arr['author-avatar'])) : '');
  556. $arr['owner-name'] = ((x($arr,'owner-name')) ? notags(trim($arr['owner-name'])) : '');
  557. $arr['owner-link'] = ((x($arr,'owner-link')) ? notags(trim($arr['owner-link'])) : '');
  558. $arr['owner-avatar'] = ((x($arr,'owner-avatar')) ? notags(trim($arr['owner-avatar'])) : '');
  559. $arr['created'] = ((x($arr,'created') !== false) ? datetime_convert('UTC','UTC',$arr['created']) : datetime_convert());
  560. $arr['edited'] = ((x($arr,'edited') !== false) ? datetime_convert('UTC','UTC',$arr['edited']) : datetime_convert());
  561. $arr['commented'] = datetime_convert();
  562. $arr['received'] = datetime_convert();
  563. $arr['changed'] = datetime_convert();
  564. $arr['title'] = ((x($arr,'title')) ? notags(trim($arr['title'])) : '');
  565. $arr['location'] = ((x($arr,'location')) ? notags(trim($arr['location'])) : '');
  566. $arr['coord'] = ((x($arr,'coord')) ? notags(trim($arr['coord'])) : '');
  567. $arr['last-child'] = ((x($arr,'last-child')) ? intval($arr['last-child']) : 0 );
  568. $arr['visible'] = ((x($arr,'visible') !== false) ? intval($arr['visible']) : 1 );
  569. $arr['deleted'] = 0;
  570. $arr['parent-uri'] = ((x($arr,'parent-uri')) ? notags(trim($arr['parent-uri'])) : '');
  571. $arr['verb'] = ((x($arr,'verb')) ? notags(trim($arr['verb'])) : '');
  572. $arr['object-type'] = ((x($arr,'object-type')) ? notags(trim($arr['object-type'])) : '');
  573. $arr['object'] = ((x($arr,'object')) ? trim($arr['object']) : '');
  574. $arr['target-type'] = ((x($arr,'target-type')) ? notags(trim($arr['target-type'])) : '');
  575. $arr['target'] = ((x($arr,'target')) ? trim($arr['target']) : '');
  576. $arr['plink'] = ((x($arr,'plink')) ? notags(trim($arr['plink'])) : '');
  577. $arr['allow_cid'] = ((x($arr,'allow_cid')) ? trim($arr['allow_cid']) : '');
  578. $arr['allow_gid'] = ((x($arr,'allow_gid')) ? trim($arr['allow_gid']) : '');
  579. $arr['deny_cid'] = ((x($arr,'deny_cid')) ? trim($arr['deny_cid']) : '');
  580. $arr['deny_gid'] = ((x($arr,'deny_gid')) ? trim($arr['deny_gid']) : '');
  581. $arr['private'] = ((x($arr,'private')) ? intval($arr['private']) : 0 );
  582. $arr['bookmark'] = ((x($arr,'bookmark')) ? intval($arr['bookmark']) : 0 );
  583. $arr['body'] = ((x($arr,'body')) ? trim($arr['body']) : '');
  584. $arr['tag'] = ((x($arr,'tag')) ? notags(trim($arr['tag'])) : '');
  585. $arr['attach'] = ((x($arr,'attach')) ? notags(trim($arr['attach'])) : '');
  586. $arr['app'] = ((x($arr,'app')) ? notags(trim($arr['app'])) : '');
  587. $arr['origin'] = ((x($arr,'origin')) ? intval($arr['origin']) : 0 );
  588. $arr['guid'] = ((x($arr,'guid')) ? notags(trim($arr['guid'])) : get_guid());
  589. if($arr['parent-uri'] === $arr['uri']) {
  590. $parent_id = 0;
  591. $allow_cid = $arr['allow_cid'];
  592. $allow_gid = $arr['allow_gid'];
  593. $deny_cid = $arr['deny_cid'];
  594. $deny_gid = $arr['deny_gid'];
  595. }
  596. else {
  597. // find the parent and snarf the item id and ACL's
  598. // and anything else we need to inherit
  599. $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d ORDER BY `id` ASC LIMIT 1",
  600. dbesc($arr['parent-uri']),
  601. intval($arr['uid'])
  602. );
  603. if(count($r)) {
  604. // is the new message multi-level threaded?
  605. // even though we don't support it now, preserve the info
  606. // and re-attach to the conversation parent.
  607. if($r[0]['uri'] != $r[0]['parent-uri']) {
  608. $arr['thr-parent'] = $arr['parent-uri'];
  609. $arr['parent-uri'] = $r[0]['parent-uri'];
  610. $z = q("SELECT `id` FROM `item` WHERE `uri` = '%s' AND `parent-uri` = '%s' AND `uid` = %d
  611. ORDER BY `id` ASC LIMIT 1",
  612. dbesc($r[0]['parent-uri']),
  613. dbesc($r[0]['parent-uri']),
  614. intval($arr['uid'])
  615. );
  616. if($z && count($z))
  617. $r = $z;
  618. }
  619. $parent_id = $r[0]['id'];
  620. $parent_deleted = $r[0]['deleted'];
  621. $allow_cid = $r[0]['allow_cid'];
  622. $allow_gid = $r[0]['allow_gid'];
  623. $deny_cid = $r[0]['deny_cid'];
  624. $deny_gid = $r[0]['deny_gid'];
  625. $arr['wall'] = $r[0]['wall'];
  626. }
  627. else {
  628. // Allow one to see reply tweets from status.net even when
  629. // we don't have or can't see the original post.
  630. if($force_parent) {
  631. logger('item_store: $force_parent=true, reply converted to top-level post.');
  632. $parent_id = 0;
  633. $arr['thr-parent'] = $arr['parent-uri'];
  634. $arr['parent-uri'] = $arr['uri'];
  635. $arr['gravity'] = 0;
  636. }
  637. else {
  638. logger('item_store: item parent was not found - ignoring item');
  639. return 0;
  640. }
  641. }
  642. }
  643. $r = q("SELECT `id` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
  644. dbesc($arr['uri']),
  645. intval($arr['uid'])
  646. );
  647. if($r && count($r)) {
  648. logger('item-store: duplicate item ignored. ' . print_r($arr,true));
  649. return 0;
  650. }
  651. call_hooks('post_remote',$arr);
  652. dbesc_array($arr);
  653. logger('item_store: ' . print_r($arr,true), LOGGER_DATA);
  654. $r = dbq("INSERT INTO `item` (`"
  655. . implode("`, `", array_keys($arr))
  656. . "`) VALUES ('"
  657. . implode("', '", array_values($arr))
  658. . "')" );
  659. // find the item we just created
  660. $r = q("SELECT `id` FROM `item` WHERE `uri` = '%s' AND `uid` = %d ORDER BY `id` ASC ",
  661. $arr['uri'], // already dbesc'd
  662. intval($arr['uid'])
  663. );
  664. if(count($r)) {
  665. $current_post = $r[0]['id'];
  666. logger('item_store: created item ' . $current_post);
  667. }
  668. else {
  669. logger('item_store: could not locate created item');
  670. return 0;
  671. }
  672. if(count($r) > 1) {
  673. logger('item_store: duplicated post occurred. Removing duplicates.');
  674. q("DELETE FROM `item` WHERE `uri` = '%s' AND `uid` = %d AND `id` != %d ",
  675. $arr['uri'],
  676. intval($arr['uid']),
  677. intval($current_post)
  678. );
  679. }
  680. if((! $parent_id) || ($arr['parent-uri'] === $arr['uri']))
  681. $parent_id = $current_post;
  682. if(strlen($allow_cid) || strlen($allow_gid) || strlen($deny_cid) || strlen($deny_gid))
  683. $private = 1;
  684. else
  685. $private = $arr['private'];
  686. // Set parent id - and also make sure to inherit the parent's ACL's.
  687. $r = q("UPDATE `item` SET `parent` = %d, `allow_cid` = '%s', `allow_gid` = '%s',
  688. `deny_cid` = '%s', `deny_gid` = '%s', `private` = %d, `deleted` = %d WHERE `id` = %d LIMIT 1",
  689. intval($parent_id),
  690. dbesc($allow_cid),
  691. dbesc($allow_gid),
  692. dbesc($deny_cid),
  693. dbesc($deny_gid),
  694. intval($private),
  695. intval($parent_deleted),
  696. intval($current_post)
  697. );
  698. // update the commented timestamp on the parent
  699. q("UPDATE `item` set `commented` = '%s', `changed` = '%s' WHERE `id` = %d LIMIT 1",
  700. dbesc(datetime_convert()),
  701. dbesc(datetime_convert()),
  702. intval($parent_id)
  703. );
  704. if($dsprsig) {
  705. q("insert into sign (`iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ",
  706. intval($current_post),
  707. dbesc($dsprsig->signed_text),
  708. dbesc($dsprsig->signature),
  709. dbesc($dsprsig->signer)
  710. );
  711. }
  712. /**
  713. * If this is now the last-child, force all _other_ children of this parent to *not* be last-child
  714. */
  715. if($arr['last-child']) {
  716. $r = q("UPDATE `item` SET `last-child` = 0 WHERE `parent-uri` = '%s' AND `uid` = %d AND `id` != %d",
  717. dbesc($arr['uri']),
  718. intval($arr['uid']),
  719. intval($current_post)
  720. );
  721. }
  722. return $current_post;
  723. }
  724. function get_item_contact($item,$contacts) {
  725. if(! count($contacts) || (! is_array($item)))
  726. return false;
  727. foreach($contacts as $contact) {
  728. if($contact['id'] == $item['contact-id']) {
  729. return $contact;
  730. break; // NOTREACHED
  731. }
  732. }
  733. return false;
  734. }
  735. function dfrn_deliver($owner,$contact,$atom, $dissolve = false) {
  736. $a = get_app();
  737. if((! strlen($contact['issued-id'])) && (! $contact['duplex']) && (! ($owner['page-flags'] == PAGE_COMMUNITY)))
  738. return 3;
  739. $idtosend = $orig_id = (($contact['dfrn-id']) ? $contact['dfrn-id'] : $contact['issued-id']);
  740. if($contact['duplex'] && $contact['dfrn-id'])
  741. $idtosend = '0:' . $orig_id;
  742. if($contact['duplex'] && $contact['issued-id'])
  743. $idtosend = '1:' . $orig_id;
  744. $rino = ((function_exists('mcrypt_encrypt')) ? 1 : 0);
  745. $rino_enable = get_config('system','rino_encrypt');
  746. if(! $rino_enable)
  747. $rino = 0;
  748. $url = $contact['notify'] . '&dfrn_id=' . $idtosend . '&dfrn_version=' . DFRN_PROTOCOL_VERSION . (($rino) ? '&rino=1' : '');
  749. logger('dfrn_deliver: ' . $url);
  750. $xml = fetch_url($url);
  751. $curl_stat = $a->get_curl_code();
  752. if(! $curl_stat)
  753. return(-1); // timed out
  754. logger('dfrn_deliver: ' . $xml, LOGGER_DATA);
  755. if(! $xml)
  756. return 3;
  757. if(strpos($xml,'<?xml') === false) {
  758. logger('dfrn_deliver: no valid XML returned');
  759. logger('dfrn_deliver: returned XML: ' . $xml, LOGGER_DATA);
  760. return 3;
  761. }
  762. $res = parse_xml_string($xml);
  763. if((intval($res->status) != 0) || (! strlen($res->challenge)) || (! strlen($res->dfrn_id)))
  764. return (($res->status) ? $res->status : 3);
  765. $postvars = array();
  766. $sent_dfrn_id = hex2bin((string) $res->dfrn_id);
  767. $challenge = hex2bin((string) $res->challenge);
  768. $dfrn_version = (float) (($res->dfrn_version) ? $res->dfrn_version : 2.0);
  769. $rino_allowed = ((intval($res->rino) === 1) ? 1 : 0);
  770. $final_dfrn_id = '';
  771. if(($contact['duplex'] && strlen($contact['pubkey'])) || ($owner['page-flags'] == PAGE_COMMUNITY)) {
  772. openssl_public_decrypt($sent_dfrn_id,$final_dfrn_id,$contact['pubkey']);
  773. openssl_public_decrypt($challenge,$postvars['challenge'],$contact['pubkey']);
  774. }
  775. else {
  776. openssl_private_decrypt($sent_dfrn_id,$final_dfrn_id,$contact['prvkey']);
  777. openssl_private_decrypt($challenge,$postvars['challenge'],$contact['prvkey']);
  778. }
  779. $final_dfrn_id = substr($final_dfrn_id, 0, strpos($final_dfrn_id, '.'));
  780. if(strpos($final_dfrn_id,':') == 1)
  781. $final_dfrn_id = substr($final_dfrn_id,2);
  782. if($final_dfrn_id != $orig_id) {
  783. logger('dfrn_deliver: wrong dfrn_id.');
  784. // did not decode properly - cannot trust this site
  785. return 3;
  786. }
  787. $postvars['dfrn_id'] = $idtosend;
  788. $postvars['dfrn_version'] = DFRN_PROTOCOL_VERSION;
  789. if($dissolve)
  790. $postvars['dissolve'] = '1';
  791. if((($contact['rel']) && ($contact['rel'] != CONTACT_IS_SHARING) && (! $contact['blocked'])) || ($owner['page-flags'] == PAGE_COMMUNITY)) {
  792. $postvars['data'] = $atom;
  793. $postvars['perm'] = 'rw';
  794. }
  795. else {
  796. $postvars['data'] = str_replace('<dfrn:comment-allow>1','<dfrn:comment-allow>0',$atom);
  797. $postvars['perm'] = 'r';
  798. }
  799. if($rino && $rino_allowed && (! $dissolve)) {
  800. $key = substr(random_string(),0,16);
  801. $data = bin2hex(aes_encrypt($postvars['data'],$key));
  802. $postvars['data'] = $data;
  803. logger('rino: sent key = ' . $key, LOGGER_DEBUG);
  804. if($dfrn_version >= 2.1) {
  805. if(($contact['duplex'] && strlen($contact['pubkey'])) || ($owner['page-flags'] == PAGE_COMMUNITY)) {
  806. openssl_public_encrypt($key,$postvars['key'],$contact['pubkey']);
  807. }
  808. else {
  809. openssl_private_encrypt($key,$postvars['key'],$contact['prvkey']);
  810. }
  811. }
  812. else {
  813. if(($contact['duplex'] && strlen($contact['prvkey'])) || ($owner['page-flags'] == PAGE_COMMUNITY)) {
  814. openssl_private_encrypt($key,$postvars['key'],$contact['prvkey']);
  815. }
  816. else {
  817. openssl_public_encrypt($key,$postvars['key'],$contact['pubkey']);
  818. }
  819. }
  820. logger('md5 rawkey ' . md5($postvars['key']));
  821. $postvars['key'] = bin2hex($postvars['key']);
  822. }
  823. logger('dfrn_deliver: ' . "SENDING: " . print_r($postvars,true), LOGGER_DATA);
  824. $xml = post_url($contact['notify'],$postvars);
  825. logger('dfrn_deliver: ' . "RECEIVED: " . $xml, LOGGER_DATA);
  826. $curl_stat = $a->get_curl_code();
  827. if((! $curl_stat) || (! strlen($xml)))
  828. return(-1); // timed out
  829. if(($curl_stat == 503) && (stristr($a->get_curl_headers(),'retry-after')))
  830. return(-1);
  831. if(strpos($xml,'<?xml') === false) {
  832. logger('dfrn_deliver: phase 2: no valid XML returned');
  833. logger('dfrn_deliver: phase 2: returned XML: ' . $xml, LOGGER_DATA);
  834. return 3;
  835. }
  836. $res = parse_xml_string($xml);
  837. return $res->status;
  838. }
  839. /**
  840. *
  841. * consume_feed - process atom feed and update anything/everything we might need to update
  842. *
  843. * $xml = the (atom) feed to consume - RSS isn't as fully supported but may work for simple feeds.
  844. *
  845. * $importer = the contact_record (joined to user_record) of the local user who owns this relationship.
  846. * It is this person's stuff that is going to be updated.
  847. * $contact = the person who is sending us stuff. If not set, we MAY be processing a "follow" activity
  848. * from an external network and MAY create an appropriate contact record. Otherwise, we MUST
  849. * have a contact record.
  850. * $hub = should we find a hub declation in the feed, pass it back to our calling process, who might (or
  851. * might not) try and subscribe to it.
  852. * $datedir sorts in reverse order
  853. * $pass - by default ($pass = 0) we cannot guarantee that a parent item has been
  854. * imported prior to its children being seen in the stream unless we are certain
  855. * of how the feed is arranged/ordered.
  856. * With $pass = 1, we only pull parent items out of the stream.
  857. * With $pass = 2, we only pull children (comments/likes).
  858. *
  859. * So running this twice, first with pass 1 and then with pass 2 will do the right
  860. * thing regardless of feed ordering. This won't be adequate in a fully-threaded
  861. * model where comments can have sub-threads. That would require some massive sorting
  862. * to get all the feed items into a mostly linear ordering, and might still require
  863. * recursion.
  864. */
  865. function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $pass = 0) {
  866. require_once('library/simplepie/simplepie.inc');
  867. if(! strlen($xml)) {
  868. logger('consume_feed: empty input');
  869. return;
  870. }
  871. $feed = new SimplePie();
  872. $feed->set_raw_data($xml);
  873. if($datedir)
  874. $feed->enable_order_by_date(true);
  875. else
  876. $feed->enable_order_by_date(false);
  877. $feed->init();
  878. if($feed->error())
  879. logger('consume_feed: Error parsing XML: ' . $feed->error());
  880. $permalink = $feed->get_permalink();
  881. // Check at the feed level for updated contact name and/or photo
  882. $name_updated = '';
  883. $new_name = '';
  884. $photo_timestamp = '';
  885. $photo_url = '';
  886. $birthday = '';
  887. $hubs = $feed->get_links('hub');
  888. if(count($hubs))
  889. $hub = implode(',', $hubs);
  890. $rawtags = $feed->get_feed_tags( NAMESPACE_DFRN, 'owner');
  891. if(! $rawtags)
  892. $rawtags = $feed->get_feed_tags( SIMPLEPIE_NAMESPACE_ATOM_10, 'author');
  893. if($rawtags) {
  894. $elems = $rawtags[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10];
  895. if($elems['name'][0]['attribs'][NAMESPACE_DFRN]['updated']) {
  896. $name_updated = $elems['name'][0]['attribs'][NAMESPACE_DFRN]['updated'];
  897. $new_name = $elems['name'][0]['data'];
  898. }
  899. if((x($elems,'link')) && ($elems['link'][0]['attribs']['']['rel'] === 'photo') && ($elems['link'][0]['attribs'][NAMESPACE_DFRN]['updated'])) {
  900. $photo_timestamp = datetime_convert('UTC','UTC',$elems['link'][0]['attribs'][NAMESPACE_DFRN]['updated']);
  901. $photo_url = $elems['link'][0]['attribs']['']['href'];
  902. }
  903. if((x($rawtags[0]['child'], NAMESPACE_DFRN)) && (x($rawtags[0]['child'][NAMESPACE_DFRN],'birthday'))) {
  904. $birthday = datetime_convert('UTC','UTC', $rawtags[0]['child'][NAMESPACE_DFRN]['birthday'][0]['data']);
  905. }
  906. }
  907. if((is_array($contact)) && ($photo_timestamp) && (strlen($photo_url)) && ($photo_timestamp > $contact['avatar-date'])) {
  908. logger('consume_feed: Updating photo for ' . $contact['name']);
  909. require_once("Photo.php");
  910. $photo_failure = false;
  911. $have_photo = false;
  912. $r = q("SELECT `resource-id` FROM `photo` WHERE `contact-id` = %d AND `uid` = %d LIMIT 1",
  913. intval($contact['id']),
  914. intval($contact['uid'])
  915. );
  916. if(count($r)) {
  917. $resource_id = $r[0]['resource-id'];
  918. $have_photo = true;
  919. }
  920. else {
  921. $resource_id = photo_new_resource();
  922. }
  923. $img_str = fetch_url($photo_url,true);
  924. $img = new Photo($img_str);
  925. if($img->is_valid()) {
  926. if($have_photo) {
  927. q("DELETE FROM `photo` WHERE `resource-id` = '%s' AND `contact-id` = %d AND `uid` = %d",
  928. dbesc($resource_id),
  929. intval($contact['id']),
  930. intval($contact['uid'])
  931. );
  932. }
  933. $img->scaleImageSquare(175);
  934. $hash = $resource_id;
  935. $r = $img->store($contact['uid'], $contact['id'], $hash, basename($photo_url), 'Contact Photos', 4);
  936. $img->scaleImage(80);
  937. $r = $img->store($contact['uid'], $contact['id'], $hash, basename($photo_url), 'Contact Photos', 5);
  938. $img->scaleImage(48);
  939. $r = $img->store($contact['uid'], $contact['id'], $hash, basename($photo_url), 'Contact Photos', 6);
  940. $a = get_app();
  941. q("UPDATE `contact` SET `avatar-date` = '%s', `photo` = '%s', `thumb` = '%s', `micro` = '%s'
  942. WHERE `uid` = %d AND `id` = %d LIMIT 1",
  943. dbesc(datetime_convert()),
  944. dbesc($a->get_baseurl() . '/photo/' . $hash . '-4.jpg'),
  945. dbesc($a->get_baseurl() . '/photo/' . $hash . '-5.jpg'),
  946. dbesc($a->get_baseurl() . '/photo/' . $hash . '-6.jpg'),
  947. intval($contact['uid']),
  948. intval($contact['id'])
  949. );
  950. }
  951. }
  952. if((is_array($contact)) && ($name_updated) && (strlen($new_name)) && ($name_updated > $contact['name-date'])) {
  953. q("UPDATE `contact` SET `name` = '%s', `name-date` = '%s' WHERE `uid` = %d AND `id` = %d LIMIT 1",
  954. dbesc(notags(trim($new_name))),
  955. dbesc(datetime_convert()),
  956. intval($contact['uid']),
  957. intval($contact['id'])
  958. );
  959. }
  960. if(strlen($birthday)) {
  961. if(substr($birthday,0,4) != $contact['bdyear']) {
  962. logger('consume_feed: updating birthday: ' . $birthday);
  963. /**
  964. *
  965. * Add new birthday event for this person
  966. *
  967. * $bdtext is just a readable placeholder in case the event is shared
  968. * with others. We will replace it during presentation to our $importer
  969. * to contain a sparkle link and perhaps a photo.
  970. *
  971. */
  972. $bdtext = t('Birthday:') . ' [url=' . $contact['url'] . ']' . $contact['name'] . '[/url]' ;
  973. $r = q("INSERT INTO `event` (`uid`,`cid`,`created`,`edited`,`start`,`finish`,`desc`,`type`)
  974. VALUES ( %d, %d, '%s', '%s', '%s', '%s', '%s', '%s' ) ",
  975. intval($contact['uid']),
  976. intval($contact['id']),
  977. dbesc(datetime_convert()),
  978. dbesc(datetime_convert()),
  979. dbesc(datetime_convert('UTC','UTC', $birthday)),
  980. dbesc(datetime_convert('UTC','UTC', $birthday . ' + 1 day ')),
  981. dbesc($bdtext),
  982. dbesc('birthday')
  983. );
  984. // update bdyear
  985. q("UPDATE `contact` SET `bdyear` = '%s' WHERE `uid` = %d AND `id` = %d LIMIT 1",
  986. dbesc(substr($birthday,0,4)),
  987. intval($contact['uid']),
  988. intval($contact['id'])
  989. );
  990. // This function is called twice without reloading the contact
  991. // Make sure we only create one event. This is why &$contact
  992. // is a reference var in this function
  993. $contact['bdyear'] = substr($birthday,0,4);
  994. }
  995. }
  996. // process any deleted entries
  997. $del_entries = $feed->get_feed_tags(NAMESPACE_TOMB, 'deleted-entry');
  998. if(is_array($del_entries) && count($del_entries) && $pass != 2) {
  999. foreach($del_entries as $dentry) {
  1000. $deleted = false;
  1001. if(isset($dentry['attribs']['']['ref'])) {
  1002. $uri = $dentry['attribs']['']['ref'];
  1003. $deleted = true;
  1004. if(isset($dentry['attribs']['']['when'])) {
  1005. $when = $dentry['attribs']['']['when'];
  1006. $when = datetime_convert('UTC','UTC', $when, 'Y-m-d H:i:s');
  1007. }
  1008. else
  1009. $when = datetime_convert('UTC','UTC','now','Y-m-d H:i:s');
  1010. }
  1011. if($deleted && is_array($contact)) {
  1012. $r = q("SELECT `item`.*, `contact`.`self` FROM `item` left join `contact` on `item`.`contact-id` = `contact`.id`
  1013. WHERE `uri` = '%s' AND `uid` = %d AND `contact-id` = %d LIMIT 1",
  1014. dbesc($uri),
  1015. intval($importer['uid']),
  1016. intval($contact['id'])
  1017. );
  1018. if(count($r)) {
  1019. $item = $r[0];
  1020. if(! $item['deleted'])
  1021. logger('consume_feed: deleting item ' . $item['id'] . ' uri=' . $item['uri'], LOGGER_DEBUG);
  1022. if(($item['verb'] === ACTIVITY_TAG) && ($item['object-type'] === ACTVITY_OBJ_TAGTERM)) {
  1023. $xo = parse_xml_string($item['object'],false);
  1024. $xt = parse_xml_string($item['target'],false);
  1025. if($xt->type === ACTIVITY_OBJ_NOTE) {
  1026. $i = q("select * from `item` where uri = '%s' and uid = %d limit 1",
  1027. dbesc($xt->id),
  1028. intval($importer['importer_uid'])
  1029. );
  1030. if(count($i)) {
  1031. // For tags, the owner cannot remove the tag on the author's copy of the post.
  1032. $owner_remove = (($item['contact-id'] == $i[0]['contact-id']) ? true: false);
  1033. $author_remove = (($item['origin'] && $item['self']) ? true : false);
  1034. $author_copy = (($item['origin']) ? true : false);
  1035. if($owner_remove && $author_copy)
  1036. continue;
  1037. if($author_remove || $owner_remove) {
  1038. $tags = explode(',',$i[0]['tag']);
  1039. $newtags = array();
  1040. if(count($tags)) {
  1041. foreach($tags as $tag)
  1042. if(trim($tag) !== trim($xo->body))
  1043. $newtags[] = trim($tag);
  1044. }
  1045. q("update item set tag = '%s' where id = %d limit 1",
  1046. dbesc(implode(',',$newtags)),
  1047. intval($i[0]['id'])
  1048. );
  1049. }
  1050. }
  1051. }
  1052. }
  1053. if($item['uri'] == $item['parent-uri']) {
  1054. $r = q("UPDATE `item` SET `deleted` = 1, `edited` = '%s', `changed` = '%s',
  1055. `body` = '', `title` = ''
  1056. WHERE `parent-uri` = '%s' AND `uid` = %d",
  1057. dbesc($when),
  1058. dbesc(datetime_convert()),
  1059. dbesc($item['uri']),
  1060. intval($importer['uid'])
  1061. );
  1062. }
  1063. else {
  1064. $r = q("UPDATE `item` SET `deleted` = 1, `edited` = '%s', `changed` = '%s',
  1065. `body` = '', `title` = ''
  1066. WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
  1067. dbesc($when),
  1068. dbesc(datetime_convert()),
  1069. dbesc($uri),
  1070. intval($importer['uid'])
  1071. );
  1072. if($item['last-child']) {
  1073. // ensure that last-child is set in case the comment that had it just got wiped.
  1074. q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d ",
  1075. dbesc(datetime_convert()),
  1076. dbesc($item['parent-uri']),
  1077. intval($item['uid'])
  1078. );
  1079. // who is the last child now?
  1080. $r = q("SELECT `id` FROM `item` WHERE `parent-uri` = '%s' AND `type` != 'activity' AND `deleted` = 0 AND `uid` = %d
  1081. ORDER BY `created` DESC LIMIT 1",
  1082. dbesc($item['parent-uri']),
  1083. intval($importer['uid'])
  1084. );
  1085. if(count($r)) {
  1086. q("UPDATE `item` SET `last-child` = 1 WHERE `id` = %d LIMIT 1",
  1087. intval($r[0]['id'])
  1088. );
  1089. }
  1090. }
  1091. }
  1092. }
  1093. }
  1094. }
  1095. }
  1096. // Now process the feed
  1097. if($feed->get_item_quantity()) {
  1098. logger('consume_feed: feed item count = ' . $feed->get_item_quantity());
  1099. // in inverse date order
  1100. if ($datedir)
  1101. $items = array_reverse($feed->get_items());
  1102. else
  1103. $items = $feed->get_items();
  1104. foreach($items as $item) {
  1105. $is_reply = false;
  1106. $item_id = $item->get_id();
  1107. $rawthread = $item->get_item_tags( NAMESPACE_THREAD,'in-reply-to');
  1108. if(isset($rawthread[0]['attribs']['']['ref'])) {
  1109. $is_reply = true;
  1110. $parent_uri = $rawthread[0]['attribs']['']['ref'];
  1111. }
  1112. if(($is_reply) && is_array($contact)) {
  1113. if($pass == 1)
  1114. continue;
  1115. // Have we seen it? If not, import it.
  1116. $item_id = $item->get_id();
  1117. $datarray = get_atom_elements($feed,$item);
  1118. if(! x($datarray,'author-name'))
  1119. $datarray['author-name'] = $contact['name'];
  1120. if(! x($datarray,'author-link'))
  1121. $datarray['author-link'] = $contact['url'];
  1122. if(! x($datarray,'author-avatar'))
  1123. $datarray['author-avatar'] = $contact['thumb'];
  1124. $r = q("SELECT `uid`, `last-child`, `edited`, `body` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
  1125. dbesc($item_id),
  1126. intval($importer['uid'])
  1127. );
  1128. // Update content if 'updated' changes
  1129. if(count($r)) {
  1130. if((x($datarray,'edited') !== false) && (datetime_convert('UTC','UTC',$datarray['edited']) !== $r[0]['edited'])) {
  1131. $r = q("UPDATE `item` SET `body` = '%s', `edited` = '%s' WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
  1132. dbesc($datarray['body']),
  1133. dbesc(datetime_convert('UTC','UTC',$datarray['edited'])),
  1134. dbesc($item_id),
  1135. intval($importer['uid'])
  1136. );
  1137. }
  1138. // update last-child if it changes
  1139. $allow = $item->get_item_tags( NAMESPACE_DFRN, 'comment-allow');
  1140. if(($allow) && ($allow[0]['data'] != $r[0]['last-child'])) {
  1141. $r = q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d",
  1142. dbesc(datetime_convert()),
  1143. dbesc($parent_uri),
  1144. intval($importer['uid'])
  1145. );
  1146. $r = q("UPDATE `item` SET `last-child` = %d , `changed` = '%s' WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
  1147. intval($allow[0]['data']),
  1148. dbesc(datetime_convert()),
  1149. dbesc($item_id),
  1150. intval($importer['uid'])
  1151. );
  1152. }
  1153. continue;
  1154. }
  1155. $force_parent = false;
  1156. if($contact['network'] === NETWORK_OSTATUS) {
  1157. $force_parent = true;
  1158. if(strlen($datarray['title']))
  1159. unset($datarray['title']);
  1160. $r = q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d",
  1161. dbesc(datetime_convert()),
  1162. dbesc($parent_uri),
  1163. intval($importer['uid'])
  1164. );
  1165. $datarray['last-child'] = 1;
  1166. }
  1167. if(($contact['network'] === NETWORK_FEED) || (! strlen($contact['notify']))) {
  1168. // one way feed - no remote comment ability
  1169. $datarray['last-child'] = 0;
  1170. }
  1171. $datarray['parent-uri'] = $parent_uri;
  1172. $datarray['uid'] = $importer['uid'];
  1173. $datarray['contact-id'] = $contact['id'];
  1174. if((activity_match($datarray['verb'],ACTIVITY_LIKE)) || (activity_match($datarray['verb'],ACTIVITY_DISLIKE))) {
  1175. $datarray['type'] = 'activity';
  1176. $datarray['gravity'] = GRAVITY_LIKE;
  1177. }
  1178. if(($datarray['verb'] === ACTIVITY_TAG) && ($datarray['object-type'] === ACTIVITY_OBJ_TAGTERM)) {
  1179. $xo = parse_xml_string($datarray['object'],false);
  1180. $xt = parse_xml_string($datarray['target'],false);
  1181. if($xt->type == ACTIVITY_OBJ_NOTE) {
  1182. $r = q("select * from item where `uri` = '%s' AND `uid` = %d limit 1",
  1183. dbesc($xt->id),
  1184. intval($importer['importer_uid'])
  1185. );
  1186. if(! count($r))
  1187. continue;
  1188. // extract tag, if not duplicate, add to parent item
  1189. if($xo->content) {
  1190. if(! (stristr($r[0]['tag'],trim($xo->content)))) {
  1191. q("UPDATE item SET tag = '%s' WHERE id = %d LIMIT 1",
  1192. dbesc($r[0]['tag'] . (strlen($r[0]['tag']) ? ',' : '') . '#[url=' . $xo->id . ']'. $xo->content . '[/url]'),
  1193. intval($r[0]['id'])
  1194. );
  1195. }
  1196. }
  1197. }
  1198. }
  1199. $r = item_store($datarray,$force_parent);
  1200. continue;
  1201. }
  1202. else {
  1203. // Head post of a conversation. Have we seen it? If not, import it.
  1204. $item_id = $item->get_id();
  1205. $datarray = get_atom_elements($feed,$item);
  1206. if(is_array($contact)) {
  1207. if(! x($datarray,'author-name'))
  1208. $datarray['author-name'] = $contact['name'];
  1209. if(! x($datarray,'author-link'))
  1210. $datarray['author-link'] = $contact['url'];
  1211. if(! x($datarray,'author-avatar'))
  1212. $datarray['author-avatar'] = $contact['thumb'];
  1213. }
  1214. // special handling for events
  1215. if((x($datarray,'object-type')) && ($datarray['object-type'] === ACTIVITY_OBJ_EVENT)) {
  1216. $ev = bbtoevent($datarray['body']);
  1217. if(x($ev,'desc') && x($ev,'start')) {
  1218. $ev['uid'] = $importer['uid'];
  1219. $ev['uri'] = $item_id;
  1220. $ev['edited'] = $datarray['edited'];
  1221. $ev['private'] = $datarray['private'];
  1222. if(is_array($contact))
  1223. $ev['cid'] = $contact['id'];
  1224. $r = q("SELECT * FROM `event` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
  1225. dbesc($item_id),
  1226. intval($importer['uid'])
  1227. );
  1228. if(count($r))
  1229. $ev['id'] = $r[0]['id'];
  1230. $xyz = event_store($ev);
  1231. continue;
  1232. }
  1233. }
  1234. $r = q("SELECT `uid`, `last-child`, `edited`, `body` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
  1235. dbesc($item_id),
  1236. intval($importer['uid'])
  1237. );
  1238. // Update content if 'updated' changes
  1239. if(count($r)) {
  1240. if((x($datarray,'edited') !== false) && (datetime_convert('UTC','UTC',$datarray['edited']) !== $r[0]['edited'])) {
  1241. $r = q("UPDATE `item` SET `body` = '%s', `edited` = '%s' WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
  1242. dbesc($datarray['body']),
  1243. dbesc(datetime_convert('UTC','UTC',$datarray['edited'])),
  1244. dbesc($item_id),
  1245. intval($importer['uid'])
  1246. );
  1247. }
  1248. // update last-child if it changes
  1249. $allow = $item->get_item_tags( NAMESPACE_DFRN, 'comment-allow');
  1250. if($allow && $allow[0]['data'] != $r[0]['last-child']) {
  1251. $r = q("UPDATE `item` SET `last-child` = %d , `changed` = '%s' WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
  1252. intval($allow[0]['data']),
  1253. dbesc(datetime_convert()),
  1254. dbesc($item_id),
  1255. intval($importer['uid'])
  1256. );
  1257. }
  1258. continue;
  1259. }
  1260. if(activity_match($datarray['verb'],ACTIVITY_FOLLOW)) {
  1261. logger('consume-feed: New follower');
  1262. new_follower($importer,$contact,$datarray,$item);
  1263. return;
  1264. }
  1265. if(activity_match($datarray['verb'],ACTIVITY_UNFOLLOW)) {
  1266. lose_follower($importer,$contact,$datarray,$item);
  1267. return;
  1268. }
  1269. if(activity_match($datarray['verb'],ACTIVITY_REQ_FRIEND)) {
  1270. logger('consume-feed: New friend request');
  1271. new_follower($importer,$contact,$datarray,$item,true);
  1272. return;
  1273. }
  1274. if(activity_match($datarray['verb'],ACTIVITY_UNFRIEND)) {
  1275. lose_sharer($importer,$contact,$datarray,$item);
  1276. return;
  1277. }
  1278. if(! is_array($contact))
  1279. return;
  1280. if($contact['network'] === NETWORK_OSTATUS || stristr($permalink,'twitter.com')) {
  1281. if(strlen($datarray['title']))
  1282. unset($datarray['title']);
  1283. $datarray['last-child'] = 1;
  1284. }
  1285. if(($contact['network'] === NETWORK_FEED) || (! strlen($contact['notify']))) {
  1286. // one way feed - no remote comment ability
  1287. $datarray['last-child'] = 0;
  1288. }
  1289. // This is my contact on another system, but it's really me.
  1290. // Turn this into a wall post.
  1291. if($contact['remote_self'])
  1292. $datarray['wall'] = 1;
  1293. $datarray['parent-uri'] = $item_id;
  1294. $datarray['uid'] = $importer['uid'];
  1295. $datarray['contact-id'] = $contact['id'];
  1296. $r = item_store($datarray);
  1297. continue;
  1298. }
  1299. }
  1300. }
  1301. }
  1302. function local_delivery($importer,$data) {
  1303. $a = get_app();
  1304. if($importer['readonly']) {
  1305. // We aren't receiving stuff from this person. But we will quietly ignore them
  1306. // rather than a blatant "go away" message.
  1307. logger('local_delivery: ignoring');
  1308. return 0;
  1309. //NOTREACHED
  1310. }
  1311. // Consume notification feed. This may differ from consuming a public feed in several ways
  1312. // - might contain email or friend suggestions
  1313. // - might contain remote followup to our message
  1314. // - in which case we need to accept it and then notify other conversants
  1315. // - we may need to send various email notifications
  1316. $feed = new SimplePie();
  1317. $feed->set_raw_data($data);
  1318. $feed->enable_order_by_date(false);
  1319. $feed->init();
  1320. $reloc = $feed->get_feed_tags( NAMESPACE_DFRN, 'relocate' );
  1321. if(isset($reloc[0]['child'][NAMESPACE_DFRN])) {
  1322. $base = $reloc[0]['child'][NAMESPACE_DFRN];
  1323. $newloc = array();
  1324. $newloc['uid'] = $importer['importer_uid'];
  1325. $newloc['cid'] = $importer['id'];
  1326. $newloc['name'] = notags(unxmlify($base['name'][0]['data']));
  1327. $newloc['photo'] = notags(unxmlify($base['photo'][0]['data']));
  1328. $newloc['url'] = notags(unxmlify($base['url'][0]['data']));
  1329. $newloc['request'] = notags(unxmlify($base['request'][0]['data']));
  1330. $newloc['confirm'] = notags(unxmlify($base['confirm'][0]['data']));
  1331. $newloc['notify'] = notags(unxmlify($base['notify'][0]['data']));
  1332. $newloc['poll'] = notags(unxmlify($base['poll'][0]['data']));
  1333. $newloc['site-pubkey'] = notags(unxmlify($base['site-pubkey'][0]['data']));
  1334. $newloc['pubkey'] = notags(unxmlify($base['pubkey'][0]['data']));
  1335. $newloc['prvkey'] = notags(unxmlify($base['prvkey'][0]['data']));
  1336. // TODO
  1337. // merge with current record, current contents have priority
  1338. // update record, set url-updated
  1339. // update profile photos
  1340. // schedule a scan?
  1341. }
  1342. // handle friend suggestion notification
  1343. $sugg = $feed->get_feed_tags( NAMESPACE_DFRN, 'suggest' );
  1344. if(isset($sugg[0]['child'][NAMESPACE_DFRN])) {
  1345. $base = $sugg[0]['child'][NAMESPACE_DFRN];
  1346. $fsugg = array();
  1347. $fsugg['uid'] = $importer['importer_uid'];
  1348. $fsugg['cid'] = $importer['id'];
  1349. $fsugg['name'] = notags(unxmlify($base['name'][0]['data']));
  1350. $fsugg['photo'] = notags(unxmlify($base['photo'][0]['data']));
  1351. $fsugg['url'] = notags(unxmlify($base['url'][0]['data']));
  1352. $fsugg['request'] = notags(unxmlify($base['request'][0]['data']));
  1353. $fsugg['body'] = escape_tags(unxmlify($base['note'][0]['data']));
  1354. // Does our member already have a friend matching this description?
  1355. $r = q("SELECT * FROM `contact` WHERE `name` = '%s' AND `url` = '%s' AND `uid` = %d LIMIT 1",
  1356. dbesc($fsugg['name']),
  1357. dbesc($fsugg['url']),
  1358. intval($fsugg['uid'])
  1359. );
  1360. if(count($r))
  1361. return 0;
  1362. // Do we already have an fcontact record for this person?
  1363. $fid = 0;
  1364. $r = q("SELECT * FROM `fcontact` WHERE `url` = '%s' AND `name` = '%s' AND `request` = '%s' LIMIT 1",
  1365. dbesc($fsugg['url']),
  1366. dbesc($fsugg['name']),
  1367. dbesc($fsugg['request'])
  1368. );
  1369. if(count($r)) {
  1370. $fid = $r[0]['id'];
  1371. }
  1372. if(! $fid)
  1373. $r = q("INSERT INTO `fcontact` ( `name`,`url`,`photo`,`request` ) VALUES ( '%s', '%s', '%s', '%s' ) ",
  1374. dbesc($fsugg['name']),
  1375. dbesc($fsugg['url']),
  1376. dbesc($fsugg['photo']),
  1377. dbesc($fsugg['request'])
  1378. );
  1379. $r = q("SELECT * FROM `fcontact` WHERE `url` = '%s' AND `name` = '%s' AND `request` = '%s' LIMIT 1",
  1380. dbesc($fsugg['url']),
  1381. dbesc($fsugg['name']),
  1382. dbesc($fsugg['request'])
  1383. );
  1384. if(count($r)) {
  1385. $fid = $r[0]['id'];
  1386. }
  1387. // database record did not get created. Quietly give up.
  1388. else
  1389. return 0;
  1390. $hash = random_string();
  1391. $r = q("INSERT INTO `intro` ( `uid`, `fid`, `contact-id`, `note`, `hash`, `datetime`, `blocked` )
  1392. VALUES( %d, %d, %d, '%s', '%s', '%s', %d )",
  1393. intval($fsugg['uid']),
  1394. intval($fid),
  1395. intval($fsugg['cid']),
  1396. dbesc($fsugg['body']),
  1397. dbesc($hash),
  1398. dbesc(datetime_convert()),
  1399. intval(0)
  1400. );
  1401. // TODO - send email notify (which may require a new notification preference)
  1402. return 0;
  1403. }
  1404. $ismail = false;
  1405. $rawmail = $feed->get_feed_tags( NAMESPACE_DFRN, 'mail' );
  1406. if(isset($rawmail[0]['child'][NAMESPACE_DFRN])) {
  1407. logger('local_delivery: private message received');
  1408. $ismail = true;
  1409. $base = $rawmail[0]['child'][NAMESPACE_DFRN];
  1410. $msg = array();
  1411. $msg['uid'] = $importer['importer_uid'];
  1412. $msg['from-name'] = notags(unxmlify($base['sender'][0]['child'][NAMESPACE_DFRN]['name'][0]['data']));
  1413. $msg['from-photo'] = notags(unxmlify($base['sender'][0]['child'][NAMESPACE_DFRN]['avatar'][0]['data']));
  1414. $msg['from-url'] = notags(unxmlify($base['sender'][0]['child'][NAMESPACE_DFRN]['uri'][0]['data']));
  1415. $msg['contact-id'] = $importer['id'];
  1416. $msg['title'] = notags(unxmlify($base['subject'][0]['data']));
  1417. $msg['body'] = escape_tags(unxmlify($base['content'][0]['data']));
  1418. $msg['seen'] = 0;
  1419. $msg['replied'] = 0;
  1420. $msg['uri'] = notags(unxmlify($base['id'][0]['data']));
  1421. $msg['parent-uri'] = notags(unxmlify($base['in-reply-to'][0]['data']));
  1422. $msg['created'] = datetime_convert(notags(unxmlify('UTC','UTC',$base['sentdate'][0]['data'])));
  1423. dbesc_array($msg);
  1424. $r = dbq("INSERT INTO `mail` (`" . implode("`, `", array_keys($msg))
  1425. . "`) VALUES ('" . implode("', '", array_values($msg)) . "')" );
  1426. // send email notification if requested.
  1427. require_once('bbcode.php');
  1428. if($importer['notify-flags'] & NOTIFY_MAIL) {
  1429. push_lang($importer['language']);
  1430. // name of the automated email sender
  1431. $msg['notificationfromname'] = t('Administrator');
  1432. // noreply address to send from
  1433. $msg['notificationfromemail'] = t('noreply') . '@' . $a->get_hostname();
  1434. // text version
  1435. // process the message body to display properly in text mode
  1436. // 1) substitute a \n character for the "\" then "n", so it behaves properly (it doesn't come in as a \n character)
  1437. // 2) remove escape slashes
  1438. // 3) decode any bbcode from the message editor
  1439. // 4) decode any encoded html tags
  1440. // 5) remove html tags
  1441. $msg['textversion']
  1442. = strip_tags(html_entity_decode(bbcode(stripslashes(str_replace(array("\\r\\n", "\\r", "\\n"), "\n",$msg['body']))),ENT_QUOTES,'UTF-8'));
  1443. // html version
  1444. // process the message body to display properly in text mode
  1445. // 1) substitute a <br /> tag for the "\" then "n", so it behaves properly (it doesn't come in as a \n character)
  1446. // 2) remove escape slashes
  1447. // 3) decode any bbcode from the message editor
  1448. // 4) decode any encoded html tags
  1449. $msg['htmlversion']
  1450. = html_entity_decode(bbcode(stripslashes(str_replace(array("\\r\\n", "\\r","\\n\\n" ,"\\n"), "<br />\n",$msg['body']))));
  1451. // load the template for private message notifications
  1452. $tpl = get_intltext_template('mail_received_html_body_eml.tpl');
  1453. $email_html_body_tpl = replace_macros($tpl,array(
  1454. '$username' => $importer['username'],
  1455. '$siteName' => $a->config['sitename'], // name of this site
  1456. '$siteurl' => $a->get_baseurl(), // descriptive url of this site
  1457. '$thumb' => $importer['thumb'], // thumbnail url for sender icon
  1458. '$email' => $importer['email'], // email address to send to
  1459. '$url' => $importer['url'], // full url for the site
  1460. '$from' => $msg['from-name'], // name of the person sending the message
  1461. '$title' => stripslashes($msg['title']), // subject of the message
  1462. '$htmlversion' => $msg['htmlversion'], // html version of the message
  1463. '$mimeboundary' => $msg['mimeboundary'], // mime message divider
  1464. '$hostname' => $a->get_hostname() // name of this host
  1465. ));
  1466. // load the template for private message notifications
  1467. $tpl = get_intltext_template('mail_received_text_body_eml.tpl');
  1468. $email_text_body_tpl = replace_macros($tpl,array(
  1469. &