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.

845 lines
27 KiB

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
  1. <?php
  2. require_once('bbcode.php');
  3. function get_feed_for(&$a, $dfrn_id, $owner_id, $last_update, $direction = 0) {
  4. // default permissions - anonymous user
  5. $sql_extra = "
  6. AND `allow_cid` = ''
  7. AND `allow_gid` = ''
  8. AND `deny_cid` = ''
  9. AND `deny_gid` = ''
  10. ";
  11. if(strlen($owner_id) && ! intval($owner_id)) {
  12. $r = q("SELECT `uid`, `nickname` FROM `user` WHERE `nickname` = '%s' LIMIT 1",
  13. dbesc($owner_id)
  14. );
  15. if(count($r)) {
  16. $owner_id = $r[0]['uid'];
  17. $owner_nick = $r[0]['nickname'];
  18. }
  19. }
  20. $r = q("SELECT * FROM `contact` WHERE `self` = 1 AND `uid` = %d LIMIT 1",
  21. intval($owner_id)
  22. );
  23. if(count($r))
  24. $owner = $r[0];
  25. else
  26. killme();
  27. if($dfrn_id && $dfrn_id != '*') {
  28. $sql_extra = '';
  29. switch($direction) {
  30. case (-1):
  31. $sql_extra = sprintf(" AND `issued-id` = '%s' ", dbesc($dfrn_id));
  32. $my_id = $dfrn_id;
  33. break;
  34. case 0:
  35. $sql_extra = sprintf(" AND `issued-id` = '%s' AND `duplex` = 1 ", dbesc($dfrn_id));
  36. $my_id = '1:' . $dfrn_id;
  37. break;
  38. case 1:
  39. $sql_extra = sprintf(" AND `dfrn-id` = '%s' AND `duplex` = 1 ", dbesc($dfrn_id));
  40. $my_id = '0:' . $dfrn_id;
  41. break;
  42. default:
  43. return false;
  44. break; // NOTREACHED
  45. }
  46. $r = q("SELECT * FROM `contact` WHERE `blocked` = 0 AND `pending` = 0 AND `contact`.`uid` = %d $sql_extra LIMIT 1",
  47. intval($owner_id)
  48. );
  49. if(! count($r))
  50. return false;
  51. $contact = $r[0];
  52. $groups = init_groups_visitor($contact['id']);
  53. if(count($groups)) {
  54. for($x = 0; $x < count($groups); $x ++)
  55. $groups[$x] = '<' . intval($groups[$x]) . '>' ;
  56. $gs = implode('|', $groups);
  57. }
  58. else
  59. $gs = '<<>>' ; // Impossible to match
  60. $sql_extra = sprintf("
  61. AND ( `allow_cid` = '' OR `allow_cid` REGEXP '<%d>' )
  62. AND ( `deny_cid` = '' OR NOT `deny_cid` REGEXP '<%d>' )
  63. AND ( `allow_gid` = '' OR `allow_gid` REGEXP '%s' )
  64. AND ( `deny_gid` = '' OR NOT `deny_gid` REGEXP '%s')
  65. ",
  66. intval($contact['id']),
  67. intval($contact['id']),
  68. dbesc($gs),
  69. dbesc($gs)
  70. );
  71. }
  72. if($dfrn_id === '' || $dfrn_id === '*')
  73. $sort = 'DESC';
  74. else
  75. $sort = 'ASC';
  76. if(! strlen($last_update))
  77. $last_update = 'now - 30 days';
  78. $check_date = datetime_convert('UTC','UTC',$last_update,'Y-m-d H:i:s');
  79. $r = q("SELECT `item`.*, `item`.`id` AS `item_id`,
  80. `contact`.`name`, `contact`.`photo`, `contact`.`url`,
  81. `contact`.`name-date`, `contact`.`uri-date`, `contact`.`avatar-date`,
  82. `contact`.`thumb`, `contact`.`dfrn-id`, `contact`.`self`,
  83. `contact`.`id` AS `contact-id`, `contact`.`uid` AS `contact-uid`
  84. FROM `item` LEFT JOIN `contact` ON `contact`.`id` = `item`.`contact-id`
  85. WHERE `item`.`uid` = %d AND `item`.`visible` = 1
  86. AND `item`.`wall` = 1 AND `contact`.`blocked` = 0 AND `contact`.`pending` = 0
  87. AND ( `item`.`edited` > '%s' OR `item`.`changed` > '%s' )
  88. $sql_extra
  89. ORDER BY `parent` %s, `created` ASC LIMIT 0, 300",
  90. intval($owner_id),
  91. dbesc($check_date),
  92. dbesc($check_date),
  93. dbesc($sort)
  94. );
  95. // Will check further below if this actually returned results.
  96. // We will provide an empty feed in any case.
  97. $items = $r;
  98. $feed_template = load_view_file('view/atom_feed.tpl');
  99. $tomb_template = load_view_file('view/atom_tomb.tpl');
  100. $item_template = load_view_file('view/atom_item.tpl');
  101. $cmnt_template = load_view_file('view/atom_cmnt.tpl');
  102. $atom = '';
  103. $hub = get_config('system','huburl');
  104. $hubxml = ((strlen($hub)) ? '<link rel="hub" href="' . xmlify($hub) . '" />' . "\n" : '');
  105. $atom .= replace_macros($feed_template, array(
  106. '$feed_id' => xmlify($a->get_baseurl() . '/profile/' . $owner_nick),
  107. '$feed_title' => xmlify($owner['name']),
  108. '$feed_updated' => xmlify(datetime_convert('UTC', 'UTC', $updated . '+00:00' , ATOM_TIME)) ,
  109. '$hub' => $hubxml,
  110. '$name' => xmlify($owner['name']),
  111. '$profile_page' => xmlify($owner['url']),
  112. '$photo' => xmlify($owner['photo']),
  113. '$thumb' => xmlify($owner['thumb']),
  114. '$picdate' => xmlify(datetime_convert('UTC','UTC',$owner['avatar-date'] . '+00:00' , ATOM_TIME)) ,
  115. '$uridate' => xmlify(datetime_convert('UTC','UTC',$owner['uri-date'] . '+00:00' , ATOM_TIME)) ,
  116. '$namdate' => xmlify(datetime_convert('UTC','UTC',$owner['name-date'] . '+00:00' , ATOM_TIME))
  117. ));
  118. if(! count($items)) {
  119. $atom .= '</feed>' . "\r\n";
  120. return $atom;
  121. }
  122. foreach($items as $item) {
  123. // public feeds get html, our own nodes use bbcode
  124. if($dfrn_id === '*') {
  125. $allow = (($item['last-child']) ? 1 : 0);
  126. $item['body'] = bbcode($item['body']);
  127. $type = 'html';
  128. }
  129. else {
  130. $allow = ((($item['last-child']) && ($contact['rel']) && ($contact['rel'] != REL_FAN)) ? 1 : 0);
  131. $type = 'text';
  132. }
  133. if($item['deleted']) {
  134. $atom .= replace_macros($tomb_template, array(
  135. '$id' => xmlify($item['uri']),
  136. '$updated' => xmlify(datetime_convert('UTC', 'UTC', $item['edited'] . '+00:00' , ATOM_TIME))
  137. ));
  138. }
  139. else {
  140. $verb = construct_verb($item);
  141. $actobj = construct_activity($item);
  142. if($item['parent'] == $item['id']) {
  143. $atom .= replace_macros($item_template, array(
  144. '$name' => xmlify($item['name']),
  145. '$profile_page' => xmlify($item['url']),
  146. '$thumb' => xmlify($item['thumb']),
  147. '$owner_name' => xmlify($item['owner-name']),
  148. '$owner_profile_page' => xmlify($item['owner-link']),
  149. '$owner_thumb' => xmlify($item['owner-avatar']),
  150. '$item_id' => xmlify($item['uri']),
  151. '$title' => xmlify($item['title']),
  152. '$published' => xmlify(datetime_convert('UTC', 'UTC', $item['created'] . '+00:00' , ATOM_TIME)),
  153. '$updated' => xmlify(datetime_convert('UTC', 'UTC', $item['edited'] . '+00:00' , ATOM_TIME)),
  154. '$location' => xmlify($item['location']),
  155. '$type' => $type,
  156. '$alt' => xmlify($a->get_baseurl() . '/display/' . $owner_nick . '/' . $item['id']),
  157. '$content' => xmlify($item['body']),
  158. '$verb' => xmlify($verb),
  159. '$actobj' => $actobj, // do not xmlify
  160. '$comment_allow' => $allow
  161. ));
  162. }
  163. else {
  164. $atom .= replace_macros($cmnt_template, array(
  165. '$name' => xmlify($item['name']),
  166. '$profile_page' => xmlify($item['url']),
  167. '$thumb' => xmlify($item['thumb']),
  168. '$item_id' => xmlify($item['uri']),
  169. '$title' => xmlify($item['title']),
  170. '$published' => xmlify(datetime_convert('UTC', 'UTC', $item['created'] . '+00:00' , ATOM_TIME)),
  171. '$updated' => xmlify(datetime_convert('UTC', 'UTC', $item['edited'] . '+00:00' , ATOM_TIME)),
  172. '$type' => $type,
  173. '$content' => xmlify($item['body']),
  174. '$alt' => xmlify($a->get_baseurl() . '/display/' . $owner_nick . '/' . $item['id']),
  175. '$verb' => xmlify($verb),
  176. '$actobj' => $actobj, // do not xmlify
  177. '$parent_id' => xmlify($item['parent-uri']),
  178. '$comment_allow' => $allow
  179. ));
  180. }
  181. }
  182. }
  183. $atom .= '</feed>' . "\r\n";
  184. return $atom;
  185. }
  186. function construct_verb($item) {
  187. if($item['verb'])
  188. return $item['verb'];
  189. return ACTIVITY_POST;
  190. }
  191. function construct_activity($item) {
  192. if($item['object']) {
  193. $o = '<as:object>' . "\r\n";
  194. $r = @simplexml_load_string($item['object']);
  195. if($r->type)
  196. $o .= '<as:object-type>' . $r->type . '</as:object-type>' . "\r\n";
  197. if($r->id)
  198. $o .= '<id>' . $r->id . '</id>' . "\r\n";
  199. if($r->link)
  200. $o .= '<link rel="alternate" type="text/html" href="' . $r->link . '" />' . "\r\n";
  201. if($r->title)
  202. $o .= '<title>' . $r->title . '</title>' . "\r\n";
  203. if($r->content)
  204. $o .= '<content type="html" >' . bbcode($r->content) . '</content>' . "\r\n";
  205. $o .= '</as:object>' . "\r\n";
  206. return $o;
  207. }
  208. return '';
  209. }
  210. function get_atom_elements($item) {
  211. require_once('library/HTMLPurifier.auto.php');
  212. require_once('include/html2bbcode.php');
  213. $res = array();
  214. $raw_author = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'author');
  215. if($raw_author) {
  216. if($raw_author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'][0]['attribs']['']['rel'] === 'photo')
  217. $res['author-avatar'] = unxmlify($raw_author[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'][0]['attribs']['']['href']);
  218. }
  219. $author = $item->get_author();
  220. $res['author-name'] = unxmlify($author->get_name());
  221. $res['author-link'] = unxmlify($author->get_link());
  222. if(! $res['author-avatar'])
  223. $res['author-avatar'] = unxmlify($author->get_avatar());
  224. $res['uri'] = unxmlify($item->get_id());
  225. $res['title'] = unxmlify($item->get_title());
  226. $res['body'] = unxmlify($item->get_content());
  227. $maxlen = get_max_import_size();
  228. if($maxlen && (strlen($res['body']) > $maxlen))
  229. $res['body'] = substr($res['body'],0, $maxlen);
  230. // It isn't certain at this point whether our content is plaintext or html and we'd be foolish to trust
  231. // the content type. Our own network only emits text normally, though it might have been converted to
  232. // html if we used a pubsubhubbub transport. But if we see even one html open tag in our text, we will
  233. // have to assume it is all html and needs to be purified.
  234. // It doesn't matter all that much security wise - because before this content is used anywhere, we are
  235. // going to escape any tags we find regardless, but this lets us import a limited subset of html from
  236. // the wild, by sanitising it and converting supported tags to bbcode before we rip out any remaining
  237. // html.
  238. if(strpos($res['body'],'<')) {
  239. $res['body'] = preg_replace('#<object[^>]+>.+?' . 'http://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+).+?</object>#s',
  240. '[youtube]$1[/youtube]', $res['body']);
  241. $config = HTMLPurifier_Config::createDefault();
  242. $config->set('Core.DefinitionCache', null);
  243. // we shouldn't need a whitelist, because the bbcode converter
  244. // will strip out any unsupported tags.
  245. // $config->set('HTML.Allowed', 'p,b,a[href],i');
  246. $purifier = new HTMLPurifier($config);
  247. $res['body'] = $purifier->purify($res['body']);
  248. }
  249. $res['body'] = html2bbcode($res['body']);
  250. $allow = $item->get_item_tags(NAMESPACE_DFRN,'comment-allow');
  251. if($allow && $allow[0]['data'] == 1)
  252. $res['last-child'] = 1;
  253. else
  254. $res['last-child'] = 0;
  255. $rawcreated = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'published');
  256. if($rawcreated)
  257. $res['created'] = unxmlify($rawcreated[0]['data']);
  258. $rawlocation = $item->get_item_tags(NAMESPACE_DFRN, 'location');
  259. if($rawlocation)
  260. $res['location'] = unxmlify($rawlocation[0]['data']);
  261. $rawedited = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'updated');
  262. if($rawedited)
  263. $res['edited'] = unxmlify($rawcreated[0]['data']);
  264. $rawowner = $item->get_item_tags(NAMESPACE_DFRN, 'owner');
  265. if($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])
  266. $res['owner-name'] = unxmlify($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']);
  267. elseif($rawowner[0]['child'][NAMESPACE_DFRN]['name'][0]['data'])
  268. $res['owner-name'] = unxmlify($rawowner[0]['child'][NAMESPACE_DFRN]['name'][0]['data']);
  269. if($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])
  270. $res['owner-link'] = unxmlify($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']);
  271. elseif($rawowner[0]['child'][NAMESPACE_DFRN]['uri'][0]['data'])
  272. $res['owner-link'] = unxmlify($rawowner[0]['child'][NAMESPACE_DFRN]['uri'][0]['data']);
  273. if($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'][0]['attribs']['']['rel'] === 'photo')
  274. $res['owner-avatar'] = unxmlify($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'][0]['attribs']['']['href']);
  275. elseif($rawowner[0]['child'][NAMESPACE_DFRN]['avatar'][0]['data'])
  276. $res['owner-avatar'] = unxmlify($rawowner[0]['child'][NAMESPACE_DFRN]['avatar'][0]['data']);
  277. $rawverb = $item->get_item_tags(NAMESPACE_ACTIVITY, 'verb');
  278. // select between supported verbs
  279. if($rawverb)
  280. $res['verb'] = unxmlify($rawverb[0]['data']);
  281. $rawobj = $item->get_item_tags(NAMESPACE_ACTIVITY, 'object');
  282. if($rawobj) {
  283. $res['object'] = '<object>' . "\n";
  284. if($rawobj[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data']) {
  285. $res['object-type'] = $rawobj[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data'];
  286. $res['object'] .= '<type>' . $rawobj[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data'] . '</type>' . "\n";
  287. }
  288. if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data'])
  289. $res['object'] .= '<id>' . $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data'] . '</id>' . "\n";
  290. if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'][0]['attribs']['']['rel'] === 'alternate')
  291. $res['object'] .= '<link>' . $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'][0]['attribs']['']['href'] . '</link>' . "\n";
  292. if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data'])
  293. $res['object'] .= '<title>' . $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data'] . '</title>' . "\n";
  294. if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data']) {
  295. $body = $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data'];
  296. if(! $body)
  297. $body = $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['summary'][0]['data'];
  298. if(strpos($body,'<')) {
  299. $body = preg_replace('#<object[^>]+>.+?' . 'http://www.youtube.com/((?:v|cp)/[A-Za-z0-9\-_=]+).+?</object>#s',
  300. '[youtube]$1[/youtube]', $body);
  301. $config = HTMLPurifier_Config::createDefault();
  302. $config->set('Core.DefinitionCache', null);
  303. $purifier = new HTMLPurifier($config);
  304. $body = $purifier->purify($body);
  305. }
  306. $body = html2bbcode($body);
  307. $res['object'] .= '<content>' . $body . '</content>' . "\n";
  308. }
  309. $res['object'] .= '</object>' . "\n";
  310. }
  311. return $res;
  312. }
  313. function item_store($arr) {
  314. //print_r($arr);
  315. if($arr['gravity'])
  316. $arr['gravity'] = intval($arr['gravity']);
  317. elseif($arr['parent-uri'] == $arr['uri'])
  318. $arr['gravity'] = 0;
  319. elseif($arr['verb'] == ACTIVITY_POST)
  320. $arr['gravity'] = 6;
  321. if(! x($arr,'type'))
  322. $arr['type'] = 'remote';
  323. $arr['wall'] = ((intval($arr['wall'])) ? 1 : 0);
  324. $arr['uri'] = notags(trim($arr['uri']));
  325. $arr['author-name'] = notags(trim($arr['author-name']));
  326. $arr['author-link'] = notags(trim($arr['author-link']));
  327. $arr['author-avatar'] = notags(trim($arr['author-avatar']));
  328. $arr['owner-name'] = notags(trim($arr['owner-name']));
  329. $arr['owner-link'] = notags(trim($arr['owner-link']));
  330. $arr['owner-avatar'] = notags(trim($arr['owner-avatar']));
  331. $arr['created'] = ((x($arr,'created') !== false) ? datetime_convert('UTC','UTC',$arr['created']) : datetime_convert());
  332. $arr['edited'] = ((x($arr,'edited') !== false) ? datetime_convert('UTC','UTC',$arr['edited']) : datetime_convert());
  333. $arr['changed'] = datetime_convert();
  334. $arr['title'] = notags(trim($arr['title']));
  335. $arr['location'] = notags(trim($arr['location']));
  336. $arr['body'] = escape_tags(trim($arr['body']));
  337. $arr['last-child'] = intval($arr['last-child']);
  338. $arr['visible'] = ((x($arr,'visible') !== false) ? intval($arr['visible']) : 1);
  339. $arr['deleted'] = 0;
  340. $arr['parent-uri'] = notags(trim($arr['parent-uri']));
  341. $arr['verb'] = notags(trim($arr['verb']));
  342. $arr['object-type'] = notags(trim($arr['object-type']));
  343. $arr['object'] = trim($arr['object']);
  344. $parent_id = 0;
  345. $parent_missing = false;
  346. dbesc_array($arr);
  347. $r = q("INSERT INTO `item` (`"
  348. . implode("`, `", array_keys($arr))
  349. . "`) VALUES ('"
  350. . implode("', '", array_values($arr))
  351. . "')" );
  352. // find the parent and snarf the item id and ACL's
  353. $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
  354. dbesc($arr['parent-uri']),
  355. intval($arr['uid'])
  356. );
  357. if(count($r)) {
  358. $parent_id = $r[0]['id'];
  359. $allow_cid = $r[0]['allow_cid'];
  360. $allow_gid = $r[0]['allow_gid'];
  361. $deny_cid = $r[0]['deny_cid'];
  362. $deny_gid = $r[0]['deny_gid'];
  363. }
  364. else {
  365. $parent_missing = true;
  366. }
  367. $r = q("SELECT `id` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
  368. $arr['uri'], // already dbesc'd
  369. intval($arr['uid'])
  370. );
  371. if(count($r))
  372. $current_post = $r[0]['id'];
  373. else
  374. return 0;
  375. if($parent_missing) {
  376. // perhaps the parent was deleted, but in any case, this thread is dead
  377. // and unfortunately our brand new item now has to be destroyed
  378. q("DELETE FROM `item` WHERE `id` = %d LIMIT 1",
  379. intval($current_post)
  380. );
  381. return 0;
  382. }
  383. // Set parent id - all of the parent's ACL's are also inherited by this post
  384. $r = q("UPDATE `item` SET `parent` = %d, `allow_cid` = '%s', `allow_gid` = '%s',
  385. `deny_cid` = '%s', `deny_gid` = '%s' WHERE `id` = %d LIMIT 1",
  386. intval($parent_id),
  387. dbesc($allow_cid),
  388. dbesc($allow_gid),
  389. dbesc($deny_cid),
  390. dbesc($deny_gid),
  391. intval($current_post)
  392. );
  393. return $current_post;
  394. }
  395. function get_item_contact($item,$contacts) {
  396. if(! count($contacts) || (! is_array($item)))
  397. return false;
  398. foreach($contacts as $contact) {
  399. if($contact['id'] == $item['contact-id']) {
  400. return $contact;
  401. break; // NOTREACHED
  402. }
  403. }
  404. return false;
  405. }
  406. function dfrn_deliver($contact,$atom,$debugging = false) {
  407. if((! strlen($contact['dfrn-id'])) && (! $contact['duplex']))
  408. return 3;
  409. $idtosend = $orig_id = (($contact['dfrn-id']) ? $contact['dfrn-id'] : $contact['issued-id']);
  410. if($contact['duplex'] && $contact['dfrn-id'])
  411. $idtosend = '0:' . $orig_id;
  412. if($contact['duplex'] && $contact['issued-id'])
  413. $idtosend = '1:' . $orig_id;
  414. $url = $contact['notify'] . '?dfrn_id=' . $idtosend;
  415. if($debugging)
  416. echo "URL: $url";
  417. $xml = fetch_url($url);
  418. if($debugging)
  419. echo $xml;
  420. if(! $xml)
  421. return 3;
  422. $res = simplexml_load_string($xml);
  423. if((intval($res->status) != 0) || (! strlen($res->challenge)) || (! strlen($res->dfrn_id)))
  424. return (($res->status) ? $res->status : 3);
  425. $postvars = array();
  426. $sent_dfrn_id = hex2bin($res->dfrn_id);
  427. $challenge = hex2bin($res->challenge);
  428. $final_dfrn_id = '';
  429. if($contact['duplex'] && strlen($contact['prvkey'])) {
  430. openssl_private_decrypt($sent_dfrn_id,$final_dfrn_id,$contact['prvkey']);
  431. openssl_private_decrypt($challenge,$postvars['challenge'],$contact['prvkey']);
  432. }
  433. else {
  434. openssl_public_decrypt($sent_dfrn_id,$final_dfrn_id,$contact['pubkey']);
  435. openssl_public_decrypt($challenge,$postvars['challenge'],$contact['pubkey']);
  436. }
  437. $final_dfrn_id = substr($final_dfrn_id, 0, strpos($final_dfrn_id, '.'));
  438. if(strpos($final_dfrn_id,':') == 1)
  439. $final_dfrn_id = substr($final_dfrn_id,2);
  440. if($final_dfrn_id != $orig_id) {
  441. // did not decode properly - cannot trust this site
  442. return 3;
  443. }
  444. $postvars['dfrn_id'] = $idtosend;
  445. if(($contact['rel']) && ($contact['rel'] != REL_FAN) && (! $contact['blocked']) && (! $contact['readonly'])) {
  446. $postvars['data'] = $atom;
  447. }
  448. else {
  449. $postvars['data'] = str_replace('<dfrn:comment-allow>1','<dfrn:comment-allow>0',$atom);
  450. }
  451. $xml = post_url($contact['notify'],$postvars);
  452. if($debugging)
  453. echo $xml;
  454. $res = simplexml_load_string($xml);
  455. return $res->status;
  456. }
  457. function consume_feed($xml,$importer,$contact, &$hub) {
  458. require_once('simplepie/simplepie.inc');
  459. $feed = new SimplePie();
  460. $feed->set_raw_data($xml);
  461. $feed->enable_order_by_date(false);
  462. $feed->init();
  463. // Check at the feed level for updated contact name and/or photo
  464. $name_updated = '';
  465. $new_name = '';
  466. $photo_timestamp = '';
  467. $photo_url = '';
  468. $foundhub = $feed->get_link(0,'hub');
  469. if(strlen($foundhub))
  470. $hub = $foundhub;
  471. $rawtags = $feed->get_feed_tags( SIMPLEPIE_NAMESPACE_ATOM_10, 'author');
  472. if($rawtags) {
  473. $elems = $rawtags[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10];
  474. if($elems['name'][0]['attribs'][NAMESPACE_DFRN]['updated']) {
  475. $name_updated = $elems['name'][0]['attribs'][NAMESPACE_DFRN]['updated'];
  476. $new_name = $elems['name'][0]['data'];
  477. }
  478. if(($elems['link'][0]['attribs']['']['rel'] === 'photo') && ($elems['link'][0]['attribs'][NAMESPACE_DFRN]['updated'])) {
  479. $photo_timestamp = datetime_convert('UTC','UTC',$elems['link'][0]['attribs'][NAMESPACE_DFRN]['updated']);
  480. $photo_url = $elems['link'][0]['attribs']['']['href'];
  481. }
  482. }
  483. if(! $photo_timestamp) {
  484. $photo_rawupdate = $feed->get_feed_tags(NAMESPACE_DFRN,'icon-updated');
  485. if($photo_rawupdate) {
  486. $photo_timestamp = datetime_convert('UTC','UTC',$photo_rawupdate[0]['data']);
  487. $photo_url = $feed->get_image_url();
  488. }
  489. }
  490. if(($photo_timestamp) && (strlen($photo_url)) && ($photo_timestamp > $contact['avatar-date'])) {
  491. require_once("Photo.php");
  492. $photo_failure = false;
  493. $r = q("SELECT `resource-id` FROM `photo` WHERE `contact-id` = %d AND `uid` = %d LIMIT 1",
  494. intval($contact['id']),
  495. intval($contact['uid'])
  496. );
  497. if(count($r)) {
  498. $resource_id = $r[0]['resource-id'];
  499. $img_str = fetch_url($photo_url,true);
  500. $img = new Photo($img_str);
  501. if($img->is_valid()) {
  502. q("DELETE FROM `photo` WHERE `resource-id` = '%s' AND contact-id` = %d AND `uid` = %d",
  503. dbesc($resource_id),
  504. intval($contact['id']),
  505. intval($contact['uid'])
  506. );
  507. $img->scaleImageSquare(175);
  508. $hash = $resource_id;
  509. $r = $img->store($contact['uid'], $contact['id'], $hash, basename($photo_url), t('Contact Photos') , 4);
  510. $img->scaleImage(80);
  511. $r = $img->store($contact['uid'], $contact['id'], $hash, basename($photo_url), t('Contact Photos') , 5);
  512. if($r)
  513. q("UPDATE `contact` SET `avatar-date` = '%s' WHERE `uid` = %d AND `id` = %d LIMIT 1",
  514. dbesc(datetime_convert()),
  515. intval($contact['uid']),
  516. intval($contact['id'])
  517. );
  518. }
  519. }
  520. }
  521. if(($name_updated) && (strlen($new_name)) && ($name_updated > $contact['name-date'])) {
  522. q("UPDATE `contact` SET `name` = '%s', `name-date` = '%s' WHERE `uid` = %d AND `id` = %d LIMIT 1",
  523. dbesc(notags(trim($new_name))),
  524. dbesc(datetime_convert()),
  525. intval($contact['uid']),
  526. intval($contact['id'])
  527. );
  528. }
  529. // Now process the feed
  530. if($feed->get_item_quantity()) {
  531. foreach($feed->get_items() as $item) {
  532. $deleted = false;
  533. $rawdelete = $item->get_item_tags( NAMESPACE_TOMB, 'deleted-entry');
  534. if(isset($rawdelete[0]['attribs']['']['ref'])) {
  535. $uri = $rawthread[0]['attribs']['']['ref'];
  536. $deleted = true;
  537. if(isset($rawdelete[0]['attribs']['']['when'])) {
  538. $when = $rawthread[0]['attribs']['']['when'];
  539. $when = datetime_convert('UTC','UTC', $when, 'Y-m-d H:i:s');
  540. }
  541. else
  542. $when = datetime_convert('UTC','UTC','now','Y-m-d H:i:s');
  543. }
  544. if($deleted) {
  545. $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
  546. dbesc($uri),
  547. intval($importer['uid'])
  548. );
  549. if(count($r)) {
  550. $item = $r[0];
  551. if($item['uri'] == $item['parent-uri']) {
  552. $r = q("UPDATE `item` SET `deleted` = 1, `edited` = '%s', `changed` = '%s',
  553. `body` = '', `title` = ''
  554. WHERE `parent-uri` = '%s' AND `uid` = %d",
  555. dbesc($when),
  556. dbesc(datetime_convert()),
  557. dbesc($item['uri']),
  558. intval($importer['uid'])
  559. );
  560. }
  561. else {
  562. $r = q("UPDATE `item` SET `deleted` = 1, `edited` = '%s', `changed` = '%s',
  563. `body` = '', `title` = ''
  564. WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
  565. dbesc($when),
  566. dbesc(datetime_convert()),
  567. dbesc($uri),
  568. intval($importer['uid'])
  569. );
  570. if($item['last-child']) {
  571. // ensure that last-child is set in case the comment that had it just got wiped.
  572. $q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d ",
  573. dbesc(datetime_convert()),
  574. dbesc($item['parent-uri']),
  575. intval($item['uid'])
  576. );
  577. // who is the last child now?
  578. $r = q("SELECT `id` FROM `item` WHERE `parent-uri` = '%s' AND `type` != 'activity' AND `deleted` = 0 AND `uid` = %d
  579. ORDER BY `created` DESC LIMIT 1",
  580. dbesc($item['parent-uri']),
  581. intval($importer['uid'])
  582. );
  583. if(count($r)) {
  584. q("UPDATE `item` SET `last-child` = 1 WHERE `id` = %d LIMIT 1",
  585. intval($r[0]['id'])
  586. );
  587. }
  588. }
  589. }
  590. }
  591. continue;
  592. }
  593. $is_reply = false;
  594. $item_id = $item->get_id();
  595. $rawthread = $item->get_item_tags( NAMESPACE_THREAD,'in-reply-to');
  596. if(isset($rawthread[0]['attribs']['']['ref'])) {
  597. $is_reply = true;
  598. $parent_uri = $rawthread[0]['attribs']['']['ref'];
  599. }
  600. if($is_reply) {
  601. // Have we seen it? If not, import it.
  602. $item_id = $item->get_id();
  603. $r = q("SELECT `uid`, `last-child`, `edited` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
  604. dbesc($item_id),
  605. intval($importer['uid'])
  606. );
  607. // FIXME update content if 'updated' changes
  608. if(count($r)) {
  609. $allow = $item->get_item_tags( NAMESPACE_DFRN, 'comment-allow');
  610. if($allow && $allow[0]['data'] != $r[0]['last-child']) {
  611. $r = q("UPDATE `item` SET `last-child` = 0, `changed` = '%s' WHERE `parent-uri` = '%s' AND `uid` = %d",
  612. dbesc(datetime_convert()),
  613. dbesc($parent_uri),
  614. intval($importer['uid'])
  615. );
  616. $r = q("UPDATE `item` SET `last-child` = %d , `changed` = '%s' WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
  617. intval($allow[0]['data']),
  618. dbesc(datetime_convert()),
  619. dbesc($item_id),
  620. intval($importer['uid'])
  621. );
  622. }
  623. continue;
  624. }
  625. $datarray = get_atom_elements($item);
  626. $datarray['parent-uri'] = $parent_uri;
  627. $datarray['uid'] = $importer['uid'];
  628. $datarray['contact-id'] = $contact['id'];
  629. if(($datarray['verb'] == ACTIVITY_LIKE) || ($datarray['verb'] == ACTIVITY_DISLIKE)) {
  630. $datarray['type'] = 'activity';
  631. $datarray['gravity'] = GRAVITY_LIKE;
  632. }
  633. $r = item_store($datarray);
  634. continue;
  635. }
  636. else {
  637. // Head post of a conversation. Have we seen it? If not, import it.
  638. $item_id = $item->get_id();
  639. $r = q("SELECT `uid`, `last-child`, `edited` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
  640. dbesc($item_id),
  641. intval($importer['uid'])
  642. );
  643. if(count($r)) {
  644. $allow = $item->get_item_tags( NAMESPACE_DFRN, 'comment-allow');
  645. if($allow && $allow[0]['data'] != $r[0]['last-child']) {
  646. $r = q("UPDATE `item` SET `last-child` = %d , `changed` = '%s' WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
  647. intval($allow[0]['data']),
  648. dbesc(datetime_convert()),
  649. dbesc($item_id),
  650. intval($importer['uid'])
  651. );
  652. }
  653. continue;
  654. }
  655. $datarray = get_atom_elements($item);
  656. $datarray['parent-uri'] = $item_id;
  657. $datarray['uid'] = $importer['uid'];
  658. $datarray['contact-id'] = $contact['id'];
  659. $r = item_store($datarray);
  660. continue;
  661. }
  662. }
  663. }
  664. }
  665. function subscribe_to_hub($url,$importer,$contact) {
  666. if(is_array($importer)) {
  667. $r = q("SELECT `nickname` FROM `user` WHERE `uid` = %d LIMIT 1",
  668. intval($importer['uid'])
  669. );
  670. }
  671. if(! count($r))
  672. return;
  673. $push_url = get_config('system','url') . '/pubsub/' . $r[0]['nickname'] . '/' . $contact['id'];
  674. $verify_token = random_string();
  675. $params= 'hub.mode=subscribe&hub.callback=' . urlencode($push_url) . '&hub.topic=' . urlencode($contact['poll']) . '&hub.verify=async&hub.verify_token=' . $verify_token;
  676. $r = q("UPDATE `contact` SET `hub-verify` = '%s' WHERE `id` = %d LIMIT 1",
  677. dbesc($verify_token),
  678. intval($contact['id'])
  679. );
  680. post_url($url,$params);
  681. return;
  682. }