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.

3055 lines
99 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
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
10 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
9 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
9 years ago
9 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
11 years ago
10 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
11 years ago
10 years ago
11 years ago
10 years ago
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
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
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
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`.`moderated` = 0 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(FRIENDICA_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. if($res['plink'])
  238. $base_url = implode('/', array_slice(explode('/',$res['plink']),0,3));
  239. else
  240. $base_url = '';
  241. // look for a photo. We should check media size and find the best one,
  242. // but for now let's just find any author photo
  243. $rawauthor = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'author');
  244. if($rawauthor && $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) {
  245. $base = $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'];
  246. foreach($base as $link) {
  247. if(!x($res, 'author-avatar') || !$res['author-avatar']) {
  248. if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar')
  249. $res['author-avatar'] = unxmlify($link['attribs']['']['href']);
  250. }
  251. }
  252. }
  253. $rawactor = $item->get_item_tags(NAMESPACE_ACTIVITY, 'actor');
  254. if($rawactor && activity_match($rawactor[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data'],ACTIVITY_OBJ_PERSON)) {
  255. $base = $rawactor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'];
  256. if($base && count($base)) {
  257. foreach($base as $link) {
  258. if($link['attribs']['']['rel'] === 'alternate' && (! $res['author-link']))
  259. $res['author-link'] = unxmlify($link['attribs']['']['href']);
  260. if(!x($res, 'author-avatar') || !$res['author-avatar']) {
  261. if($link['attribs']['']['rel'] === 'avatar' || $link['attribs']['']['rel'] === 'photo')
  262. $res['author-avatar'] = unxmlify($link['attribs']['']['href']);
  263. }
  264. }
  265. }
  266. }
  267. // No photo/profile-link on the item - look at the feed level
  268. if((! (x($res,'author-link'))) || (! (x($res,'author-avatar')))) {
  269. $rawauthor = $feed->get_feed_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'author');
  270. if($rawauthor && $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) {
  271. $base = $rawauthor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'];
  272. foreach($base as $link) {
  273. if($link['attribs']['']['rel'] === 'alternate' && (! $res['author-link']))
  274. $res['author-link'] = unxmlify($link['attribs']['']['href']);
  275. if(! $res['author-avatar']) {
  276. if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar')
  277. $res['author-avatar'] = unxmlify($link['attribs']['']['href']);
  278. }
  279. }
  280. }
  281. $rawactor = $feed->get_feed_tags(NAMESPACE_ACTIVITY, 'subject');
  282. if($rawactor && activity_match($rawactor[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data'],ACTIVITY_OBJ_PERSON)) {
  283. $base = $rawactor[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'];
  284. if($base && count($base)) {
  285. foreach($base as $link) {
  286. if($link['attribs']['']['rel'] === 'alternate' && (! $res['author-link']))
  287. $res['author-link'] = unxmlify($link['attribs']['']['href']);
  288. if(! (x($res,'author-avatar'))) {
  289. if($link['attribs']['']['rel'] === 'avatar' || $link['attribs']['']['rel'] === 'photo')
  290. $res['author-avatar'] = unxmlify($link['attribs']['']['href']);
  291. }
  292. }
  293. }
  294. }
  295. }
  296. $apps = $item->get_item_tags(NAMESPACE_STATUSNET,'notice_info');
  297. if($apps && $apps[0]['attribs']['']['source']) {
  298. $res['app'] = strip_tags(unxmlify($apps[0]['attribs']['']['source']));
  299. if($res['app'] === 'web')
  300. $res['app'] = 'OStatus';
  301. }
  302. // base64 encoded json structure representing Diaspora signature
  303. $dsig = $item->get_item_tags(NAMESPACE_DFRN,'diaspora_signature');
  304. if($dsig) {
  305. $res['dsprsig'] = unxmlify($dsig[0]['data']);
  306. }
  307. $dguid = $item->get_item_tags(NAMESPACE_DFRN,'diaspora_guid');
  308. if($dguid)
  309. $res['guid'] = unxmlify($dguid[0]['data']);
  310. $bm = $item->get_item_tags(NAMESPACE_DFRN,'bookmark');
  311. if($bm)
  312. $res['bookmark'] = ((unxmlify($bm[0]['data']) === 'true') ? 1 : 0);
  313. /**
  314. * If there's a copy of the body content which is guaranteed to have survived mangling in transit, use it.
  315. */
  316. $have_real_body = false;
  317. $rawenv = $item->get_item_tags(NAMESPACE_DFRN, 'env');
  318. if($rawenv) {
  319. $have_real_body = true;
  320. $res['body'] = $rawenv[0]['data'];
  321. $res['body'] = str_replace(array(' ',"\t","\r","\n"), array('','','',''),$res['body']);
  322. // make sure nobody is trying to sneak some html tags by us
  323. $res['body'] = notags(base64url_decode($res['body']));
  324. }
  325. $maxlen = get_max_import_size();
  326. if($maxlen && (strlen($res['body']) > $maxlen))
  327. $res['body'] = substr($res['body'],0, $maxlen);
  328. // It isn't certain at this point whether our content is plaintext or html and we'd be foolish to trust
  329. // the content type. Our own network only emits text normally, though it might have been converted to
  330. // html if we used a pubsubhubbub transport. But if we see even one html tag in our text, we will
  331. // have to assume it is all html and needs to be purified.
  332. // It doesn't matter all that much security wise - because before this content is used anywhere, we are
  333. // going to escape any tags we find regardless, but this lets us import a limited subset of html from
  334. // the wild, by sanitising it and converting supported tags to bbcode before we rip out any remaining
  335. // html.
  336. if((strpos($res['body'],'<') !== false) || (strpos($res['body'],'>') !== false)) {
  337. $res['body'] = reltoabs($res['body'],$base_url);
  338. $res['body'] = html2bb_video($res['body']);
  339. $res['body'] = oembed_html2bbcode($res['body']);
  340. $config = HTMLPurifier_Config::createDefault();
  341. $config->set('Cache.DefinitionImpl', null);
  342. // we shouldn't need a whitelist, because the bbcode converter
  343. // will strip out any unsupported tags.
  344. // $config->set('HTML.Allowed', 'p,b,a[href],i');
  345. $purifier = new HTMLPurifier($config);
  346. $res['body'] = $purifier->purify($res['body']);
  347. $res['body'] = html2bbcode($res['body']);
  348. }
  349. $allow = $item->get_item_tags(NAMESPACE_DFRN,'comment-allow');
  350. if($allow && $allow[0]['data'] == 1)
  351. $res['last-child'] = 1;
  352. else
  353. $res['last-child'] = 0;
  354. $private = $item->get_item_tags(NAMESPACE_DFRN,'private');
  355. if($private && $private[0]['data'] == 1)
  356. $res['private'] = 1;
  357. else
  358. $res['private'] = 0;
  359. $extid = $item->get_item_tags(NAMESPACE_DFRN,'extid');
  360. if($extid && $extid[0]['data'])
  361. $res['extid'] = $extid[0]['data'];
  362. $rawlocation = $item->get_item_tags(NAMESPACE_DFRN, 'location');
  363. if($rawlocation)
  364. $res['location'] = unxmlify($rawlocation[0]['data']);
  365. $rawcreated = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'published');
  366. if($rawcreated)
  367. $res['created'] = unxmlify($rawcreated[0]['data']);
  368. $rawedited = $item->get_item_tags(SIMPLEPIE_NAMESPACE_ATOM_10,'updated');
  369. if($rawedited)
  370. $res['edited'] = unxmlify($rawedited[0]['data']);
  371. if((x($res,'edited')) && (! (x($res,'created'))))
  372. $res['created'] = $res['edited'];
  373. if(! $res['created'])
  374. $res['created'] = $item->get_date('c');
  375. if(! $res['edited'])
  376. $res['edited'] = $item->get_date('c');
  377. // Disallow time travelling posts
  378. $d1 = strtotime($res['created']);
  379. $d2 = strtotime($res['edited']);
  380. $d3 = strtotime('now');
  381. if($d1 > $d3)
  382. $res['created'] = datetime_convert();
  383. if($d2 > $d3)
  384. $res['edited'] = datetime_convert();
  385. $rawowner = $item->get_item_tags(NAMESPACE_DFRN, 'owner');
  386. if($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data'])
  387. $res['owner-name'] = unxmlify($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['name'][0]['data']);
  388. elseif($rawowner[0]['child'][NAMESPACE_DFRN]['name'][0]['data'])
  389. $res['owner-name'] = unxmlify($rawowner[0]['child'][NAMESPACE_DFRN]['name'][0]['data']);
  390. if($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data'])
  391. $res['owner-link'] = unxmlify($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['uri'][0]['data']);
  392. elseif($rawowner[0]['child'][NAMESPACE_DFRN]['uri'][0]['data'])
  393. $res['owner-link'] = unxmlify($rawowner[0]['child'][NAMESPACE_DFRN]['uri'][0]['data']);
  394. if($rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) {
  395. $base = $rawowner[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'];
  396. foreach($base as $link) {
  397. if(!x($res, 'owner-avatar') || !$res['owner-avatar']) {
  398. if($link['attribs']['']['rel'] === 'photo' || $link['attribs']['']['rel'] === 'avatar')
  399. $res['owner-avatar'] = unxmlify($link['attribs']['']['href']);
  400. }
  401. }
  402. }
  403. $rawgeo = $item->get_item_tags(NAMESPACE_GEORSS,'point');
  404. if($rawgeo)
  405. $res['coord'] = unxmlify($rawgeo[0]['data']);
  406. $rawverb = $item->get_item_tags(NAMESPACE_ACTIVITY, 'verb');
  407. // select between supported verbs
  408. if($rawverb) {
  409. $res['verb'] = unxmlify($rawverb[0]['data']);
  410. }
  411. // translate OStatus unfollow to activity streams if it happened to get selected
  412. if((x($res,'verb')) && ($res['verb'] === 'http://ostatus.org/schema/1.0/unfollow'))
  413. $res['verb'] = ACTIVITY_UNFOLLOW;
  414. $cats = $item->get_categories();
  415. if($cats) {
  416. $tag_arr = array();
  417. foreach($cats as $cat) {
  418. $term = $cat->get_term();
  419. if(! $term)
  420. $term = $cat->get_label();
  421. $scheme = $cat->get_scheme();
  422. if($scheme && $term && stristr($scheme,'X-DFRN:'))
  423. $tag_arr[] = substr($scheme,7,1) . '[url=' . unxmlify(substr($scheme,9)) . ']' . unxmlify($term) . '[/url]';
  424. elseif($term)
  425. $tag_arr[] = notags(trim($term));
  426. }
  427. $res['tag'] = implode(',', $tag_arr);
  428. }
  429. $attach = $item->get_enclosures();
  430. if($attach) {
  431. $att_arr = array();
  432. foreach($attach as $att) {
  433. $len = intval($att->get_length());
  434. $link = str_replace(array(',','"'),array('%2D','%22'),notags(trim(unxmlify($att->get_link()))));
  435. $title = str_replace(array(',','"'),array('%2D','%22'),notags(trim(unxmlify($att->get_title()))));
  436. $type = str_replace(array(',','"'),array('%2D','%22'),notags(trim(unxmlify($att->get_type()))));
  437. if(strpos($type,';'))
  438. $type = substr($type,0,strpos($type,';'));
  439. if((! $link) || (strpos($link,'http') !== 0))
  440. continue;
  441. if(! $title)
  442. $title = ' ';
  443. if(! $type)
  444. $type = 'application/octet-stream';
  445. $att_arr[] = '[attach]href="' . $link . '" length="' . $len . '" type="' . $type . '" title="' . $title . '"[/attach]';
  446. }
  447. $res['attach'] = implode(',', $att_arr);
  448. }
  449. $rawobj = $item->get_item_tags(NAMESPACE_ACTIVITY, 'object');
  450. if($rawobj) {
  451. $res['object'] = '<object>' . "\n";
  452. if($rawobj[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data']) {
  453. $res['object-type'] = $rawobj[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data'];
  454. $res['object'] .= '<type>' . $rawobj[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data'] . '</type>' . "\n";
  455. }
  456. if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data'])
  457. $res['object'] .= '<id>' . $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data'] . '</id>' . "\n";
  458. if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'])
  459. $res['object'] .= '<link>' . encode_rel_links($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) . '</link>' . "\n";
  460. if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data'])
  461. $res['object'] .= '<title>' . $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data'] . '</title>' . "\n";
  462. if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data']) {
  463. $body = $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data'];
  464. if(! $body)
  465. $body = $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['summary'][0]['data'];
  466. // preserve a copy of the original body content in case we later need to parse out any microformat information, e.g. events
  467. $res['object'] .= '<orig>' . xmlify($body) . '</orig>' . "\n";
  468. if((strpos($body,'<') !== false) || (strpos($body,'>') !== false)) {
  469. $body = html2bb_video($body);
  470. $config = HTMLPurifier_Config::createDefault();
  471. $config->set('Cache.DefinitionImpl', null);
  472. $purifier = new HTMLPurifier($config);
  473. $body = $purifier->purify($body);
  474. $body = html2bbcode($body);
  475. }
  476. $res['object'] .= '<content>' . $body . '</content>' . "\n";
  477. }
  478. $res['object'] .= '</object>' . "\n";
  479. }
  480. $rawobj = $item->get_item_tags(NAMESPACE_ACTIVITY, 'target');
  481. if($rawobj) {
  482. $res['target'] = '<target>' . "\n";
  483. if($rawobj[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data']) {
  484. $res['target'] .= '<type>' . $rawobj[0]['child'][NAMESPACE_ACTIVITY]['object-type'][0]['data'] . '</type>' . "\n";
  485. }
  486. if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data'])
  487. $res['target'] .= '<id>' . $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['id'][0]['data'] . '</id>' . "\n";
  488. if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link'])
  489. $res['target'] .= '<link>' . encode_rel_links($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['link']) . '</link>' . "\n";
  490. if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data'])
  491. $res['target'] .= '<title>' . $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['title'][0]['data'] . '</title>' . "\n";
  492. if($rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data']) {
  493. $body = $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['content'][0]['data'];
  494. if(! $body)
  495. $body = $rawobj[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10]['summary'][0]['data'];
  496. // preserve a copy of the original body content in case we later need to parse out any microformat information, e.g. events
  497. $res['target'] .= '<orig>' . xmlify($body) . '</orig>' . "\n";
  498. if((strpos($body,'<') !== false) || (strpos($body,'>') !== false)) {
  499. $body = html2bb_video($body);
  500. $config = HTMLPurifier_Config::createDefault();
  501. $config->set('Cache.DefinitionImpl', null);
  502. $purifier = new HTMLPurifier($config);
  503. $body = $purifier->purify($body);
  504. $body = html2bbcode($body);
  505. }
  506. $res['target'] .= '<content>' . $body . '</content>' . "\n";
  507. }
  508. $res['target'] .= '</target>' . "\n";
  509. }
  510. $arr = array('feed' => $feed, 'item' => $item, 'result' => $res);
  511. call_hooks('parse_atom', $arr);
  512. return $res;
  513. }
  514. function encode_rel_links($links) {
  515. $o = '';
  516. if(! ((is_array($links)) && (count($links))))
  517. return $o;
  518. foreach($links as $link) {
  519. $o .= '<link ';
  520. if($link['attribs']['']['rel'])
  521. $o .= 'rel="' . $link['attribs']['']['rel'] . '" ';
  522. if($link['attribs']['']['type'])
  523. $o .= 'type="' . $link['attribs']['']['type'] . '" ';
  524. if($link['attribs']['']['href'])
  525. $o .= 'href="' . $link['attribs']['']['href'] . '" ';
  526. if( (x($link['attribs'],NAMESPACE_MEDIA)) && $link['attribs'][NAMESPACE_MEDIA]['width'])
  527. $o .= 'media:width="' . $link['attribs'][NAMESPACE_MEDIA]['width'] . '" ';
  528. if( (x($link['attribs'],NAMESPACE_MEDIA)) && $link['attribs'][NAMESPACE_MEDIA]['height'])
  529. $o .= 'media:height="' . $link['attribs'][NAMESPACE_MEDIA]['height'] . '" ';
  530. $o .= ' />' . "\n" ;
  531. }
  532. return xmlify($o);
  533. }
  534. function item_store($arr,$force_parent = false) {
  535. // If a Diaspora signature structure was passed in, pull it out of the
  536. // item array and set it aside for later storage.
  537. $dsprsig = null;
  538. if(x($arr,'dsprsig')) {
  539. $dsprsig = json_decode(base64_decode($arr['dsprsig']));
  540. unset($arr['dsprsig']);
  541. }
  542. if(x($arr, 'gravity'))
  543. $arr['gravity'] = intval($arr['gravity']);
  544. elseif($arr['parent-uri'] === $arr['uri'])
  545. $arr['gravity'] = 0;
  546. elseif(activity_match($arr['verb'],ACTIVITY_POST))
  547. $arr['gravity'] = 6;
  548. else
  549. $arr['gravity'] = 6; // extensible catchall
  550. if(! x($arr,'type'))
  551. $arr['type'] = 'remote';
  552. // Shouldn't happen but we want to make absolutely sure it doesn't leak from a plugin.
  553. if((strpos($arr['body'],'<') !== false) || (strpos($arr['body'],'>') !== false))
  554. $arr['body'] = strip_tags($arr['body']);
  555. $arr['wall'] = ((x($arr,'wall')) ? intval($arr['wall']) : 0);
  556. $arr['uri'] = ((x($arr,'uri')) ? notags(trim($arr['uri'])) : random_string());
  557. $arr['extid'] = ((x($arr,'extid')) ? notags(trim($arr['extid'])) : '');
  558. $arr['author-name'] = ((x($arr,'author-name')) ? notags(trim($arr['author-name'])) : '');
  559. $arr['author-link'] = ((x($arr,'author-link')) ? notags(trim($arr['author-link'])) : '');
  560. $arr['author-avatar'] = ((x($arr,'author-avatar')) ? notags(trim($arr['author-avatar'])) : '');
  561. $arr['owner-name'] = ((x($arr,'owner-name')) ? notags(trim($arr['owner-name'])) : '');
  562. $arr['owner-link'] = ((x($arr,'owner-link')) ? notags(trim($arr['owner-link'])) : '');
  563. $arr['owner-avatar'] = ((x($arr,'owner-avatar')) ? notags(trim($arr['owner-avatar'])) : '');
  564. $arr['created'] = ((x($arr,'created') !== false) ? datetime_convert('UTC','UTC',$arr['created']) : datetime_convert());
  565. $arr['edited'] = ((x($arr,'edited') !== false) ? datetime_convert('UTC','UTC',$arr['edited']) : datetime_convert());
  566. $arr['commented'] = datetime_convert();
  567. $arr['received'] = datetime_convert();
  568. $arr['changed'] = datetime_convert();
  569. $arr['title'] = ((x($arr,'title')) ? notags(trim($arr['title'])) : '');
  570. $arr['location'] = ((x($arr,'location')) ? notags(trim($arr['location'])) : '');
  571. $arr['coord'] = ((x($arr,'coord')) ? notags(trim($arr['coord'])) : '');
  572. $arr['last-child'] = ((x($arr,'last-child')) ? intval($arr['last-child']) : 0 );
  573. $arr['visible'] = ((x($arr,'visible') !== false) ? intval($arr['visible']) : 1 );
  574. $arr['deleted'] = 0;
  575. $arr['parent-uri'] = ((x($arr,'parent-uri')) ? notags(trim($arr['parent-uri'])) : '');
  576. $arr['verb'] = ((x($arr,'verb')) ? notags(trim($arr['verb'])) : '');
  577. $arr['object-type'] = ((x($arr,'object-type')) ? notags(trim($arr['object-type'])) : '');
  578. $arr['object'] = ((x($arr,'object')) ? trim($arr['object']) : '');
  579. $arr['target-type'] = ((x($arr,'target-type')) ? notags(trim($arr['target-type'])) : '');
  580. $arr['target'] = ((x($arr,'target')) ? trim($arr['target']) : '');
  581. $arr['plink'] = ((x($arr,'plink')) ? notags(trim($arr['plink'])) : '');
  582. $arr['allow_cid'] = ((x($arr,'allow_cid')) ? trim($arr['allow_cid']) : '');
  583. $arr['allow_gid'] = ((x($arr,'allow_gid')) ? trim($arr['allow_gid']) : '');
  584. $arr['deny_cid'] = ((x($arr,'deny_cid')) ? trim($arr['deny_cid']) : '');
  585. $arr['deny_gid'] = ((x($arr,'deny_gid')) ? trim($arr['deny_gid']) : '');
  586. $arr['private'] = ((x($arr,'private')) ? intval($arr['private']) : 0 );
  587. $arr['bookmark'] = ((x($arr,'bookmark')) ? intval($arr['bookmark']) : 0 );
  588. $arr['body'] = ((x($arr,'body')) ? trim($arr['body']) : '');
  589. $arr['tag'] = ((x($arr,'tag')) ? notags(trim($arr['tag'])) : '');
  590. $arr['attach'] = ((x($arr,'attach')) ? notags(trim($arr['attach'])) : '');
  591. $arr['app'] = ((x($arr,'app')) ? notags(trim($arr['app'])) : '');
  592. $arr['origin'] = ((x($arr,'origin')) ? intval($arr['origin']) : 0 );
  593. $arr['guid'] = ((x($arr,'guid')) ? notags(trim($arr['guid'])) : get_guid());
  594. if($arr['parent-uri'] === $arr['uri']) {
  595. $parent_id = 0;
  596. $parent_deleted = 0;
  597. $allow_cid = $arr['allow_cid'];
  598. $allow_gid = $arr['allow_gid'];
  599. $deny_cid = $arr['deny_cid'];
  600. $deny_gid = $arr['deny_gid'];
  601. }
  602. else {
  603. // find the parent and snarf the item id and ACL's
  604. // and anything else we need to inherit
  605. $r = q("SELECT * FROM `item` WHERE `uri` = '%s' AND `uid` = %d ORDER BY `id` ASC LIMIT 1",
  606. dbesc($arr['parent-uri']),
  607. intval($arr['uid'])
  608. );
  609. if(count($r)) {
  610. // is the new message multi-level threaded?
  611. // even though we don't support it now, preserve the info
  612. // and re-attach to the conversation parent.
  613. if($r[0]['uri'] != $r[0]['parent-uri']) {
  614. $arr['thr-parent'] = $arr['parent-uri'];
  615. $arr['parent-uri'] = $r[0]['parent-uri'];
  616. $z = q("SELECT `id` FROM `item` WHERE `uri` = '%s' AND `parent-uri` = '%s' AND `uid` = %d
  617. ORDER BY `id` ASC LIMIT 1",
  618. dbesc($r[0]['parent-uri']),
  619. dbesc($r[0]['parent-uri']),
  620. intval($arr['uid'])
  621. );
  622. if($z && count($z))
  623. $r = $z;
  624. }
  625. $parent_id = $r[0]['id'];
  626. $parent_deleted = $r[0]['deleted'];
  627. $allow_cid = $r[0]['allow_cid'];
  628. $allow_gid = $r[0]['allow_gid'];
  629. $deny_cid = $r[0]['deny_cid'];
  630. $deny_gid = $r[0]['deny_gid'];
  631. $arr['wall'] = $r[0]['wall'];
  632. }
  633. else {
  634. // Allow one to see reply tweets from status.net even when
  635. // we don't have or can't see the original post.
  636. if($force_parent) {
  637. logger('item_store: $force_parent=true, reply converted to top-level post.');
  638. $parent_id = 0;
  639. $arr['thr-parent'] = $arr['parent-uri'];
  640. $arr['parent-uri'] = $arr['uri'];
  641. $arr['gravity'] = 0;
  642. }
  643. else {
  644. logger('item_store: item parent was not found - ignoring item');
  645. return 0;
  646. }
  647. $parent_deleted = 0;
  648. }
  649. }
  650. $r = q("SELECT `id` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
  651. dbesc($arr['uri']),
  652. intval($arr['uid'])
  653. );
  654. if($r && count($r)) {
  655. logger('item-store: duplicate item ignored. ' . print_r($arr,true));
  656. return 0;
  657. }
  658. call_hooks('post_remote',$arr);
  659. if(x($arr,'cancel')) {
  660. logger('item_store: post cancelled by plugin.');
  661. return 0;
  662. }
  663. dbesc_array($arr);
  664. logger('item_store: ' . print_r($arr,true), LOGGER_DATA);
  665. $r = dbq("INSERT INTO `item` (`"
  666. . implode("`, `", array_keys($arr))
  667. . "`) VALUES ('"
  668. . implode("', '", array_values($arr))
  669. . "')" );
  670. // find the item we just created
  671. $r = q("SELECT `id` FROM `item` WHERE `uri` = '%s' AND `uid` = %d ORDER BY `id` ASC ",
  672. $arr['uri'], // already dbesc'd
  673. intval($arr['uid'])
  674. );
  675. if(count($r)) {
  676. $current_post = $r[0]['id'];
  677. logger('item_store: created item ' . $current_post);
  678. }
  679. else {
  680. logger('item_store: could not locate created item');
  681. return 0;
  682. }
  683. if(count($r) > 1) {
  684. logger('item_store: duplicated post occurred. Removing duplicates.');
  685. q("DELETE FROM `item` WHERE `uri` = '%s' AND `uid` = %d AND `id` != %d ",
  686. $arr['uri'],
  687. intval($arr['uid']),
  688. intval($current_post)
  689. );
  690. }
  691. if((! $parent_id) || ($arr['parent-uri'] === $arr['uri']))
  692. $parent_id = $current_post;
  693. if(strlen($allow_cid) || strlen($allow_gid) || strlen($deny_cid) || strlen($deny_gid))
  694. $private = 1;
  695. else
  696. $private = $arr['private'];
  697. // Set parent id - and also make sure to inherit the parent's ACL's.
  698. $r = q("UPDATE `item` SET `parent` = %d, `allow_cid` = '%s', `allow_gid` = '%s',
  699. `deny_cid` = '%s', `deny_gid` = '%s', `private` = %d, `deleted` = %d WHERE `id` = %d LIMIT 1",
  700. intval($parent_id),
  701. dbesc($allow_cid),
  702. dbesc($allow_gid),
  703. dbesc($deny_cid),
  704. dbesc($deny_gid),
  705. intval($private),
  706. intval($parent_deleted),
  707. intval($current_post)
  708. );
  709. // update the commented timestamp on the parent
  710. q("UPDATE `item` set `commented` = '%s', `changed` = '%s' WHERE `id` = %d LIMIT 1",
  711. dbesc(datetime_convert()),
  712. dbesc(datetime_convert()),
  713. intval($parent_id)
  714. );
  715. if($dsprsig) {
  716. q("insert into sign (`iid`,`signed_text`,`signature`,`signer`) values (%d,'%s','%s','%s') ",
  717. intval($current_post),
  718. dbesc($dsprsig->signed_text),
  719. dbesc($dsprsig->signature),
  720. dbesc($dsprsig->signer)
  721. );
  722. }
  723. /**
  724. * If this is now the last-child, force all _other_ children of this parent to *not* be last-child
  725. */
  726. if($arr['last-child']) {
  727. $r = q("UPDATE `item` SET `last-child` = 0 WHERE `parent-uri` = '%s' AND `uid` = %d AND `id` != %d",
  728. dbesc($arr['uri']),
  729. intval($arr['uid']),
  730. intval($current_post)
  731. );
  732. }
  733. tag_deliver($arr['uid'],$current_post);
  734. return $current_post;
  735. }
  736. function get_item_contact($item,$contacts) {
  737. if(! count($contacts) || (! is_array($item)))
  738. return false;
  739. foreach($contacts as $contact) {
  740. if($contact['id'] == $item['contact-id']) {
  741. return $contact;
  742. break; // NOTREACHED
  743. }
  744. }
  745. return false;
  746. }
  747. function tag_deliver($uid,$item_id) {
  748. // look for mention tags and setup a second delivery chain for forum/community posts if appropriate
  749. $a = get_app();
  750. $mention = false;
  751. $u = q("select uid, nickname, language, username, email, `page-flags`, `notify-flags` from user where uid = %d limit 1",
  752. intval($uid)
  753. );
  754. if(! count($u))
  755. return;
  756. $community_page = (($u[0]['page-flags'] == PAGE_COMMUNITY) ? true : false);
  757. $i = q("select * from item where id = %d and uid = %d limit 1",
  758. intval($item_id),
  759. intval($uid)
  760. );
  761. if(! count($i))
  762. return;
  763. $item = $i[0];
  764. $link = normalise_link($a->get_baseurl() . '/profile/' . $u[0]['nickname']);
  765. // Diaspora uses their own hardwired link URL in @-tags
  766. // instead of the one we supply with webfinger
  767. $dlink = normalise_link($a->get_baseurl() . '/u/' . $u[0]['nickname']);
  768. $cnt = preg_match_all('/[\@\!]\[url\=(.*?)\](.*?)\[\/url\]/ism',$item['body'],$matches,PREG_SET_ORDER);
  769. if($cnt) {
  770. foreach($matches as $mtch) {
  771. if(link_compare($link,$mtch[1]) || link_compare($dlink,$mtch[1])) {
  772. $mention = true;
  773. logger('tag_deliver: mention found: ' . $mtch[2]);
  774. }
  775. }
  776. }
  777. if(! $mention)
  778. return;
  779. // send a notification
  780. require_once('include/enotify.php');
  781. notification(array(
  782. 'type' => NOTIFY_TAGSELF,
  783. 'notify_flags' => $u[0]['notify-flags'],
  784. 'language' => $u[0]['language'],
  785. 'to_name' => $u[0]['username'],
  786. 'to_email' => $u[0]['email'],
  787. 'uid' => $u[0]['uid'],
  788. 'item' => $item,
  789. 'link' => $a->get_baseurl() . '/display/' . $u[0]['nickname'] . '/' . $item['id'],
  790. 'source_name' => $item['author-name'],
  791. 'source_link' => $item['author-link'],
  792. 'source_photo' => $item['author-avatar'],
  793. 'verb' => ACTIVITY_TAG,
  794. 'otype' => 'item'
  795. ));
  796. if(! $community_page)
  797. return;
  798. // tgroup delivery - setup a second delivery chain
  799. // prevent delivery looping - only proceed
  800. // if the message originated elsewhere and is a top-level post
  801. if(($item['wall']) || ($item['origin']) || ($item['id'] != $item['parent']))
  802. return;
  803. // now change this copy of the post to a forum head message and deliver to all the tgroup members
  804. $c = q("select name, url, thumb from contact where self = 1 and uid = %d limit 1",
  805. intval($u[0]['uid'])
  806. );
  807. if(! count($c))
  808. return;
  809. q("update item set wall = 1, origin = 1, forum_mode = 1, `owner-name` = '%s', `owner-link` = '%s', `owner-avatar` = '%s' where id = %d limit 1",
  810. dbesc($c[0]['name']),
  811. dbesc($c[0]['url']),
  812. dbesc($c[0]['thumb']),
  813. intval($item_id)
  814. );
  815. proc_run('php','include/notifier.php','tgroup',$item_id);
  816. }
  817. function dfrn_deliver($owner,$contact,$atom, $dissolve = false) {
  818. $a = get_app();
  819. // if((! strlen($contact['issued-id'])) && (! $contact['duplex']) && (! ($owner['page-flags'] == PAGE_COMMUNITY)))
  820. // return 3;
  821. $idtosend = $orig_id = (($contact['dfrn-id']) ? $contact['dfrn-id'] : $contact['issued-id']);
  822. if($contact['duplex'] && $contact['dfrn-id'])
  823. $idtosend = '0:' . $orig_id;
  824. if($contact['duplex'] && $contact['issued-id'])
  825. $idtosend = '1:' . $orig_id;
  826. $rino = ((function_exists('mcrypt_encrypt')) ? 1 : 0);
  827. $rino_enable = get_config('system','rino_encrypt');
  828. if(! $rino_enable)
  829. $rino = 0;
  830. $ssl_val = intval(get_config('system','ssl_policy'));
  831. $ssl_policy = '';
  832. switch($ssl_val){
  833. case SSL_POLICY_FULL:
  834. $ssl_policy = 'full';
  835. break;
  836. case SSL_POLICY_SELFSIGN:
  837. $ssl_policy = 'self';
  838. break;
  839. case SSL_POLICY_NONE:
  840. default:
  841. $ssl_policy = 'none';
  842. break;
  843. }
  844. $url = $contact['notify'] . '&dfrn_id=' . $idtosend . '&dfrn_version=' . DFRN_PROTOCOL_VERSION . (($rino) ? '&rino=1' : '');
  845. logger('dfrn_deliver: ' . $url);
  846. $xml = fetch_url($url);
  847. $curl_stat = $a->get_curl_code();
  848. if(! $curl_stat)
  849. return(-1); // timed out
  850. logger('dfrn_deliver: ' . $xml, LOGGER_DATA);
  851. if(! $xml)
  852. return 3;
  853. if(strpos($xml,'<?xml') === false) {
  854. logger('dfrn_deliver: no valid XML returned');
  855. logger('dfrn_deliver: returned XML: ' . $xml, LOGGER_DATA);
  856. return 3;
  857. }
  858. $res = parse_xml_string($xml);
  859. if((intval($res->status) != 0) || (! strlen($res->challenge)) || (! strlen($res->dfrn_id)))
  860. return (($res->status) ? $res->status : 3);
  861. $postvars = array();
  862. $sent_dfrn_id = hex2bin((string) $res->dfrn_id);
  863. $challenge = hex2bin((string) $res->challenge);
  864. $dfrn_version = (float) (($res->dfrn_version) ? $res->dfrn_version : 2.0);
  865. $rino_allowed = ((intval($res->rino) === 1) ? 1 : 0);
  866. $final_dfrn_id = '';
  867. if(($contact['duplex'] && strlen($contact['pubkey']))
  868. || ($owner['page-flags'] == PAGE_COMMUNITY && strlen($contact['pubkey']))
  869. || ($contact['rel'] == CONTACT_IS_SHARING && strlen($contact['pubkey']))) {
  870. openssl_public_decrypt($sent_dfrn_id,$final_dfrn_id,$contact['pubkey']);
  871. openssl_public_decrypt($challenge,$postvars['challenge'],$contact['pubkey']);
  872. }
  873. else {
  874. openssl_private_decrypt($sent_dfrn_id,$final_dfrn_id,$contact['prvkey']);
  875. openssl_private_decrypt($challenge,$postvars['challenge'],$contact['prvkey']);
  876. }
  877. $final_dfrn_id = substr($final_dfrn_id, 0, strpos($final_dfrn_id, '.'));
  878. if(strpos($final_dfrn_id,':') == 1)
  879. $final_dfrn_id = substr($final_dfrn_id,2);
  880. if($final_dfrn_id != $orig_id) {
  881. logger('dfrn_deliver: wrong dfrn_id.');
  882. // did not decode properly - cannot trust this site
  883. return 3;
  884. }
  885. $postvars['dfrn_id'] = $idtosend;
  886. $postvars['dfrn_version'] = DFRN_PROTOCOL_VERSION;
  887. if($dissolve)
  888. $postvars['dissolve'] = '1';
  889. if((($contact['rel']) && ($contact['rel'] != CONTACT_IS_SHARING) && (! $contact['blocked'])) || ($owner['page-flags'] == PAGE_COMMUNITY)) {
  890. $postvars['data'] = $atom;
  891. $postvars['perm'] = 'rw';
  892. }
  893. else {
  894. $postvars['data'] = str_replace('<dfrn:comment-allow>1','<dfrn:comment-allow>0',$atom);
  895. $postvars['perm'] = 'r';
  896. }
  897. $postvars['ssl_policy'] = $ssl_policy;
  898. if($rino && $rino_allowed && (! $dissolve)) {
  899. $key = substr(random_string(),0,16);
  900. $data = bin2hex(aes_encrypt($postvars['data'],$key));
  901. $postvars['data'] = $data;
  902. logger('rino: sent key = ' . $key, LOGGER_DEBUG);
  903. if($dfrn_version >= 2.1) {
  904. if(($contact['duplex'] && strlen($contact['pubkey']))
  905. || ($owner['page-flags'] == PAGE_COMMUNITY && strlen($contact['pubkey']))
  906. || ($contact['rel'] == CONTACT_IS_SHARING && strlen($contact['pubkey']))) {
  907. openssl_public_encrypt($key,$postvars['key'],$contact['pubkey']);
  908. }
  909. else {
  910. openssl_private_encrypt($key,$postvars['key'],$contact['prvkey']);
  911. }
  912. }
  913. else {
  914. if(($contact['duplex'] && strlen($contact['prvkey'])) || ($owner['page-flags'] == PAGE_COMMUNITY)) {
  915. openssl_private_encrypt($key,$postvars['key'],$contact['prvkey']);
  916. }
  917. else {
  918. openssl_public_encrypt($key,$postvars['key'],$contact['pubkey']);
  919. }
  920. }
  921. logger('md5 rawkey ' . md5($postvars['key']));
  922. $postvars['key'] = bin2hex($postvars['key']);
  923. }
  924. logger('dfrn_deliver: ' . "SENDING: " . print_r($postvars,true), LOGGER_DATA);
  925. $xml = post_url($contact['notify'],$postvars);
  926. logger('dfrn_deliver: ' . "RECEIVED: " . $xml, LOGGER_DATA);
  927. $curl_stat = $a->get_curl_code();
  928. if((! $curl_stat) || (! strlen($xml)))
  929. return(-1); // timed out
  930. if(($curl_stat == 503) && (stristr($a->get_curl_headers(),'retry-after')))
  931. return(-1);
  932. if(strpos($xml,'<?xml') === false) {
  933. logger('dfrn_deliver: phase 2: no valid XML returned');
  934. logger('dfrn_deliver: phase 2: returned XML: ' . $xml, LOGGER_DATA);
  935. return 3;
  936. }
  937. $res = parse_xml_string($xml);
  938. return $res->status;
  939. }
  940. /**
  941. *
  942. * consume_feed - process atom feed and update anything/everything we might need to update
  943. *
  944. * $xml = the (atom) feed to consume - RSS isn't as fully supported but may work for simple feeds.
  945. *
  946. * $importer = the contact_record (joined to user_record) of the local user who owns this relationship.
  947. * It is this person's stuff that is going to be updated.
  948. * $contact = the person who is sending us stuff. If not set, we MAY be processing a "follow" activity
  949. * from an external network and MAY create an appropriate contact record. Otherwise, we MUST
  950. * have a contact record.
  951. * $hub = should we find a hub declation in the feed, pass it back to our calling process, who might (or
  952. * might not) try and subscribe to it.
  953. * $datedir sorts in reverse order
  954. * $pass - by default ($pass = 0) we cannot guarantee that a parent item has been
  955. * imported prior to its children being seen in the stream unless we are certain
  956. * of how the feed is arranged/ordered.
  957. * With $pass = 1, we only pull parent items out of the stream.
  958. * With $pass = 2, we only pull children (comments/likes).
  959. *
  960. * So running this twice, first with pass 1 and then with pass 2 will do the right
  961. * thing regardless of feed ordering. This won't be adequate in a fully-threaded
  962. * model where comments can have sub-threads. That would require some massive sorting
  963. * to get all the feed items into a mostly linear ordering, and might still require
  964. * recursion.
  965. */
  966. function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0, $pass = 0) {
  967. require_once('library/simplepie/simplepie.inc');
  968. if(! strlen($xml)) {
  969. logger('consume_feed: empty input');
  970. return;
  971. }
  972. $feed = new SimplePie();
  973. $feed->set_raw_data($xml);
  974. if($datedir)
  975. $feed->enable_order_by_date(true);
  976. else
  977. $feed->enable_order_by_date(false);
  978. $feed->init();
  979. if($feed->error())
  980. logger('consume_feed: Error parsing XML: ' . $feed->error());
  981. $permalink = $feed->get_permalink();
  982. // Check at the feed level for updated contact name and/or photo
  983. $name_updated = '';
  984. $new_name = '';
  985. $photo_timestamp = '';
  986. $photo_url = '';
  987. $birthday = '';
  988. $hubs = $feed->get_links('hub');
  989. if(count($hubs))
  990. $hub = implode(',', $hubs);
  991. $rawtags = $feed->get_feed_tags( NAMESPACE_DFRN, 'owner');
  992. if(! $rawtags)
  993. $rawtags = $feed->get_feed_tags( SIMPLEPIE_NAMESPACE_ATOM_10, 'author');
  994. if($rawtags) {
  995. $elems = $rawtags[0]['child'][SIMPLEPIE_NAMESPACE_ATOM_10];
  996. if($elems['name'][0]['attribs'][NAMESPACE_DFRN]['updated']) {
  997. $name_updated = $elems['name'][0]['attribs'][NAMESPACE_DFRN]['updated'];
  998. $new_name = $elems['name'][0]['data'];
  999. }
  1000. if((x($elems,'link')) && ($elems['link'][0]['attribs']['']['rel'] === 'photo') && ($elems['link'][0]['attribs'][NAMESPACE_DFRN]['updated'])) {
  1001. $photo_timestamp = datetime_convert('UTC','UTC',$elems['link'][0]['attribs'][NAMESPACE_DFRN]['updated']);
  1002. $photo_url = $elems['link'][0]['attribs']['']['href'];
  1003. }
  1004. if((x($rawtags[0]['child'], NAMESPACE_DFRN))