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.

1045 lines
32 KiB

8 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
9 years ago
10 years ago
10 years ago
10 years ago
10 years ago
8 years ago
8 years ago
8 years ago
8 years ago
8 years ago
10 years ago
9 years ago
9 years ago
10 years ago
9 years ago
9 years ago
8 years ago
  1. <?php
  2. require_once("boot.php");
  3. require_once('include/queue_fn.php');
  4. require_once('include/html2plain.php');
  5. /*
  6. * This file was at one time responsible for doing all deliveries, but this caused
  7. * big problems on shared hosting systems, where the process might get killed by the
  8. * hosting provider and nothing would get delivered.
  9. * It now only delivers one message under certain cases, and invokes a queued
  10. * delivery mechanism (include/deliver.php) to deliver individual contacts at
  11. * controlled intervals.
  12. * This has a much better chance of surviving random processes getting killed
  13. * by the hosting provider.
  14. * A lot of this code is duplicated in include/deliver.php until we have time to go back
  15. * and re-structure the delivery procedure based on the obstacles that have been thrown at
  16. * us by hosting providers.
  17. */
  18. /*
  19. * The notifier is typically called with:
  20. *
  21. * proc_run('php', "include/notifier.php", COMMAND, ITEM_ID);
  22. *
  23. * where COMMAND is one of the following:
  24. *
  25. * activity (in diaspora.php, dfrn_confirm.php, profiles.php)
  26. * comment-import (in diaspora.php, items.php)
  27. * comment-new (in item.php)
  28. * drop (in diaspora.php, items.php, photos.php)
  29. * edit_post (in item.php)
  30. * event (in events.php)
  31. * expire (in items.php)
  32. * like (in like.php, poke.php)
  33. * mail (in message.php)
  34. * suggest (in fsuggest.php)
  35. * tag (in photos.php, poke.php, tagger.php)
  36. * tgroup (in items.php)
  37. * wall-new (in photos.php, item.php)
  38. * removeme (in Contact.php)
  39. * relocate (in uimport.php)
  40. *
  41. * and ITEM_ID is the id of the item in the database that needs to be sent to others.
  42. */
  43. function notifier_run(&$argv, &$argc){
  44. global $a, $db;
  45. if(is_null($a)){
  46. $a = new App;
  47. }
  48. if(is_null($db)) {
  49. @include(".htconfig.php");
  50. require_once("include/dba.php");
  51. $db = new dba($db_host, $db_user, $db_pass, $db_data);
  52. unset($db_host, $db_user, $db_pass, $db_data);
  53. }
  54. require_once("include/session.php");
  55. require_once("include/datetime.php");
  56. require_once('include/items.php');
  57. require_once('include/bbcode.php');
  58. require_once('include/email.php');
  59. load_config('config');
  60. load_config('system');
  61. load_hooks();
  62. if($argc < 3)
  63. return;
  64. $a->set_baseurl(get_config('system','url'));
  65. logger('notifier: invoked: ' . print_r($argv,true), LOGGER_DEBUG);
  66. $cmd = $argv[1];
  67. switch($cmd) {
  68. case 'mail':
  69. default:
  70. $item_id = intval($argv[2]);
  71. if(! $item_id){
  72. return;
  73. }
  74. break;
  75. }
  76. $expire = false;
  77. $mail = false;
  78. $fsuggest = false;
  79. $relocate = false;
  80. $top_level = false;
  81. $recipients = array();
  82. $url_recipients = array();
  83. $normal_mode = true;
  84. if($cmd === 'mail') {
  85. $normal_mode = false;
  86. $mail = true;
  87. $message = q("SELECT * FROM `mail` WHERE `id` = %d LIMIT 1",
  88. intval($item_id)
  89. );
  90. if(! count($message)){
  91. return;
  92. }
  93. $uid = $message[0]['uid'];
  94. $recipients[] = $message[0]['contact-id'];
  95. $item = $message[0];
  96. }
  97. elseif($cmd === 'expire') {
  98. $normal_mode = false;
  99. $expire = true;
  100. $items = q("SELECT * FROM `item` WHERE `uid` = %d AND `wall` = 1
  101. AND `deleted` = 1 AND `changed` > UTC_TIMESTAMP() - INTERVAL 10 MINUTE",
  102. intval($item_id)
  103. );
  104. $uid = $item_id;
  105. $item_id = 0;
  106. if(! count($items))
  107. return;
  108. }
  109. elseif($cmd === 'suggest') {
  110. $normal_mode = false;
  111. $fsuggest = true;
  112. $suggest = q("SELECT * FROM `fsuggest` WHERE `id` = %d LIMIT 1",
  113. intval($item_id)
  114. );
  115. if(! count($suggest))
  116. return;
  117. $uid = $suggest[0]['uid'];
  118. $recipients[] = $suggest[0]['cid'];
  119. $item = $suggest[0];
  120. } elseif($cmd === 'removeme') {
  121. $r = q("SELECT * FROM `user` WHERE `uid` = %d LIMIT 1", intval($item_id));
  122. if (! $r)
  123. return;
  124. $user = $r[0];
  125. $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `self` = 1 LIMIT 1", intval($item_id));
  126. if (! $r)
  127. return;
  128. $self = $r[0];
  129. $r = q("SELECT * FROM `contact` WHERE `self` = 0 AND `uid` = %d", intval($item_id));
  130. if(! $r)
  131. return;
  132. require_once('include/Contact.php');
  133. foreach($r as $contact) {
  134. terminate_friendship($user, $self, $contact);
  135. }
  136. return;
  137. } elseif($cmd === 'relocate') {
  138. $normal_mode = false;
  139. $relocate = true;
  140. $uid = $item_id;
  141. } else {
  142. // find ancestors
  143. $r = q("SELECT * FROM `item` WHERE `id` = %d and visible = 1 and moderated = 0 LIMIT 1",
  144. intval($item_id)
  145. );
  146. if((! count($r)) || (! intval($r[0]['parent']))) {
  147. return;
  148. }
  149. $target_item = $r[0];
  150. $parent_id = intval($r[0]['parent']);
  151. $uid = $r[0]['uid'];
  152. $updated = $r[0]['edited'];
  153. // POSSIBLE CLEANUP --> The following seems superfluous. We've already checked for "if (! intval($r[0]['parent']))" a few lines up
  154. if(! $parent_id)
  155. return;
  156. $items = q("SELECT `item`.*, `sign`.`signed_text`,`sign`.`signature`,`sign`.`signer`
  157. FROM `item` LEFT JOIN `sign` ON `sign`.`iid` = `item`.`id` WHERE `parent` = %d and visible = 1 and moderated = 0 ORDER BY `id` ASC",
  158. intval($parent_id)
  159. );
  160. if(! count($items)) {
  161. return;
  162. }
  163. // avoid race condition with deleting entries
  164. if($items[0]['deleted']) {
  165. foreach($items as $item)
  166. $item['deleted'] = 1;
  167. }
  168. if((count($items) == 1) && ($items[0]['id'] === $target_item['id']) && ($items[0]['uri'] === $items[0]['parent-uri'])) {
  169. logger('notifier: top level post');
  170. $top_level = true;
  171. }
  172. }
  173. $r = q("SELECT `contact`.*, `user`.`pubkey` AS `upubkey`, `user`.`prvkey` AS `uprvkey`,
  174. `user`.`timezone`, `user`.`nickname`, `user`.`sprvkey`, `user`.`spubkey`,
  175. `user`.`page-flags`, `user`.`prvnets`
  176. FROM `contact` INNER JOIN `user` ON `user`.`uid` = `contact`.`uid`
  177. WHERE `contact`.`uid` = %d AND `contact`.`self` = 1 LIMIT 1",
  178. intval($uid)
  179. );
  180. if(! count($r))
  181. return;
  182. $owner = $r[0];
  183. $walltowall = ((($top_level) && ($owner['id'] != $items[0]['contact-id'])) ? true : false);
  184. $hub = get_config('system','huburl');
  185. // If this is a public conversation, notify the feed hub
  186. $public_message = true;
  187. // Do a PuSH
  188. $push_notify = false;
  189. // fill this in with a single salmon slap if applicable
  190. $slap = '';
  191. // List of OStatus receiptians of follow up messages
  192. $ostatus_recip_str = "";
  193. if(! ($mail || $fsuggest || $relocate)) {
  194. require_once('include/group.php');
  195. $parent = $items[0];
  196. // This is IMPORTANT!!!!
  197. // We will only send a "notify owner to relay" or followup message if the referenced post
  198. // originated on our system by virtue of having our hostname somewhere
  199. // in the URI, AND it was a comment (not top_level) AND the parent originated elsewhere.
  200. // if $parent['wall'] == 1 we will already have the parent message in our array
  201. // and we will relay the whole lot.
  202. // expire sends an entire group of expire messages and cannot be forwarded.
  203. // However the conversation owner will be a part of the conversation and will
  204. // be notified during this run.
  205. // Other DFRN conversation members will be alerted during polled updates.
  206. // Diaspora members currently are not notified of expirations, and other networks have
  207. // either limited or no ability to process deletions. We should at least fix Diaspora
  208. // by stringing togther an array of retractions and sending them onward.
  209. $localhost = str_replace('www.','',$a->get_hostname());
  210. if(strpos($localhost,':'))
  211. $localhost = substr($localhost,0,strpos($localhost,':'));
  212. /**
  213. *
  214. * Be VERY CAREFUL if you make any changes to the following several lines. Seemingly innocuous changes
  215. * have been known to cause runaway conditions which affected several servers, along with
  216. * permissions issues.
  217. *
  218. */
  219. $relay_to_owner = false;
  220. if((! $top_level) && ($parent['wall'] == 0) && (! $expire) && (stristr($target_item['uri'],$localhost))) {
  221. $relay_to_owner = true;
  222. }
  223. if(($cmd === 'uplink') && (intval($parent['forum_mode']) == 1) && (! $top_level)) {
  224. $relay_to_owner = true;
  225. }
  226. // until the 'origin' flag has been in use for several months
  227. // we will just use it as a fallback test
  228. // later we will be able to use it as the primary test of whether or not to relay.
  229. if(! $target_item['origin'])
  230. $relay_to_owner = false;
  231. if($parent['origin'])
  232. $relay_to_owner = false;
  233. if($relay_to_owner) {
  234. logger('notifier: followup', LOGGER_DEBUG);
  235. // local followup to remote post
  236. $followup = true;
  237. $public_message = false; // not public
  238. $conversant_str = dbesc($parent['contact-id']);
  239. $recipients = array($parent['contact-id']);
  240. if ($parent['network'] == NETWORK_OSTATUS) {
  241. logger('Parent is OStatus', LOGGER_DEBUG);
  242. $push_notify = true;
  243. /* $ostatus_recipients = array();
  244. $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `network` = '%s'", intval($uid), dbesc(NETWORK_OSTATUS));
  245. if(count($r)) {
  246. foreach($r as $rr)
  247. $ostatus_recipients[] = $rr['id'];
  248. $ostatus_recip_str = ", ".implode(', ', $ostatus_recipients);
  249. }
  250. */
  251. // Check if the recipient isn't in your contact list
  252. $r = q("SELECT `url` FROM `contact` WHERE `id` = %d", $parent['contact-id']);
  253. if (count($r)) {
  254. $url_recipients = array();
  255. $thrparent = q("SELECT `author-link` FROM `item` WHERE `uri` = '%s'", dbesc($target_item["thr-parent"]));
  256. if (count($thrparent) AND (normalise_link($r[0]["url"]) != normalise_link($thrparent[0]["author-link"]))) {
  257. require_once("include/Scrape.php");
  258. $probed_contact = probe_url($thrparent[0]["author-link"]);
  259. if ($probed_contact["notify"] != "") {
  260. logger('scrape data for slapper: '.print_r($probed_contact, true));
  261. $url_recipients[$probed_contact["notify"]] = $probed_contact["notify"];
  262. }
  263. }
  264. }
  265. logger("url_recipients".print_r($url_recipients,true));
  266. }
  267. } else {
  268. $followup = false;
  269. // don't send deletions onward for other people's stuff
  270. if($target_item['deleted'] && (! intval($target_item['wall']))) {
  271. logger('notifier: ignoring delete notification for non-wall item');
  272. return;
  273. }
  274. if((strlen($parent['allow_cid']))
  275. || (strlen($parent['allow_gid']))
  276. || (strlen($parent['deny_cid']))
  277. || (strlen($parent['deny_gid']))) {
  278. $public_message = false; // private recipients, not public
  279. }
  280. $allow_people = expand_acl($parent['allow_cid']);
  281. $allow_groups = expand_groups(expand_acl($parent['allow_gid']),true);
  282. $deny_people = expand_acl($parent['deny_cid']);
  283. $deny_groups = expand_groups(expand_acl($parent['deny_gid']));
  284. // if our parent is a public forum (forum_mode == 1), uplink to the origional author causing
  285. // a delivery fork. private groups (forum_mode == 2) do not uplink
  286. if((intval($parent['forum_mode']) == 1) && (! $top_level) && ($cmd !== 'uplink')) {
  287. proc_run('php','include/notifier.php','uplink',$item_id);
  288. }
  289. $conversants = array();
  290. foreach($items as $item) {
  291. $recipients[] = $item['contact-id'];
  292. $conversants[] = $item['contact-id'];
  293. // pull out additional tagged people to notify (if public message)
  294. if($public_message && strlen($item['inform'])) {
  295. $people = explode(',',$item['inform']);
  296. foreach($people as $person) {
  297. if(substr($person,0,4) === 'cid:') {
  298. $recipients[] = intval(substr($person,4));
  299. $conversants[] = intval(substr($person,4));
  300. }
  301. else {
  302. $url_recipients[] = substr($person,4);
  303. }
  304. }
  305. }
  306. }
  307. logger('notifier: url_recipients' . print_r($url_recipients,true));
  308. $conversants = array_unique($conversants);
  309. $recipients = array_unique(array_merge($recipients,$allow_people,$allow_groups));
  310. $deny = array_unique(array_merge($deny_people,$deny_groups));
  311. $recipients = array_diff($recipients,$deny);
  312. $conversant_str = dbesc(implode(', ',$conversants));
  313. }
  314. $r = q("SELECT * FROM `contact` WHERE `id` IN ( $conversant_str ) AND `blocked` = 0 AND `pending` = 0 AND `archive` = 0");
  315. if(count($r))
  316. $contacts = $r;
  317. }
  318. $feed_template = get_markup_template('atom_feed.tpl');
  319. $mail_template = get_markup_template('atom_mail.tpl');
  320. $atom = '';
  321. $slaps = array();
  322. $hubxml = feed_hublinks();
  323. $birthday = feed_birthday($owner['uid'],$owner['timezone']);
  324. if(strlen($birthday))
  325. $birthday = '<dfrn:birthday>' . xmlify($birthday) . '</dfrn:birthday>';
  326. $atom .= replace_macros($feed_template, array(
  327. '$version' => xmlify(FRIENDICA_VERSION),
  328. '$feed_id' => xmlify($a->get_baseurl() . '/profile/' . $owner['nickname'] ),
  329. '$feed_title' => xmlify($owner['name']),
  330. '$feed_updated' => xmlify(datetime_convert('UTC', 'UTC', $updated . '+00:00' , ATOM_TIME)) ,
  331. '$hub' => $hubxml,
  332. '$salmon' => '', // private feed, we don't use salmon here
  333. '$name' => xmlify($owner['name']),
  334. '$profile_page' => xmlify($owner['url']),
  335. '$photo' => xmlify($owner['photo']),
  336. '$thumb' => xmlify($owner['thumb']),
  337. '$picdate' => xmlify(datetime_convert('UTC','UTC',$owner['avatar-date'] . '+00:00' , ATOM_TIME)) ,
  338. '$uridate' => xmlify(datetime_convert('UTC','UTC',$owner['uri-date'] . '+00:00' , ATOM_TIME)) ,
  339. '$namdate' => xmlify(datetime_convert('UTC','UTC',$owner['name-date'] . '+00:00' , ATOM_TIME)) ,
  340. '$birthday' => $birthday,
  341. '$community' => (($owner['page-flags'] == PAGE_COMMUNITY) ? '<dfrn:community>1</dfrn:community>' : '')
  342. ));
  343. if($mail) {
  344. $public_message = false; // mail is not public
  345. $body = fix_private_photos($item['body'],$owner['uid'],null,$message[0]['contact-id']);
  346. $atom .= replace_macros($mail_template, array(
  347. '$name' => xmlify($owner['name']),
  348. '$profile_page' => xmlify($owner['url']),
  349. '$thumb' => xmlify($owner['thumb']),
  350. '$item_id' => xmlify($item['uri']),
  351. '$subject' => xmlify($item['title']),
  352. '$created' => xmlify(datetime_convert('UTC', 'UTC', $item['created'] . '+00:00' , ATOM_TIME)),
  353. '$content' => xmlify($body),
  354. '$parent_id' => xmlify($item['parent-uri'])
  355. ));
  356. } elseif($fsuggest) {
  357. $public_message = false; // suggestions are not public
  358. $sugg_template = get_markup_template('atom_suggest.tpl');
  359. $atom .= replace_macros($sugg_template, array(
  360. '$name' => xmlify($item['name']),
  361. '$url' => xmlify($item['url']),
  362. '$photo' => xmlify($item['photo']),
  363. '$request' => xmlify($item['request']),
  364. '$note' => xmlify($item['note'])
  365. ));
  366. // We don't need this any more
  367. q("DELETE FROM `fsuggest` WHERE `id` = %d LIMIT 1",
  368. intval($item['id'])
  369. );
  370. } elseif($relocate) {
  371. $public_message = false; // suggestions are not public
  372. $sugg_template = get_markup_template('atom_relocate.tpl');
  373. /* get site pubkey. this could be a new installation with no site keys*/
  374. $pubkey = get_config('system','site_pubkey');
  375. if(! $pubkey) {
  376. $res = new_keypair(1024);
  377. set_config('system','site_prvkey', $res['prvkey']);
  378. set_config('system','site_pubkey', $res['pubkey']);
  379. }
  380. $rp = q("SELECT `resource-id` , `scale`, type FROM `photo`
  381. WHERE `profile` = 1 AND `uid` = %d ORDER BY scale;", $uid);
  382. $photos = array();
  383. $ext = Photo::supportedTypes();
  384. foreach($rp as $p){
  385. $photos[$p['scale']] = $a->get_baseurl().'/photo/'.$p['resource-id'].'-'.$p['scale'].'.'.$ext[$p['type']];
  386. }
  387. unset($rp, $ext);
  388. $atom .= replace_macros($sugg_template, array(
  389. '$name' => xmlify($owner['name']),
  390. '$photo' => xmlify($photos[4]),
  391. '$thumb' => xmlify($photos[5]),
  392. '$micro' => xmlify($photos[6]),
  393. '$url' => xmlify($owner['url']),
  394. '$request' => xmlify($owner['request']),
  395. '$confirm' => xmlify($owner['confirm']),
  396. '$notify' => xmlify($owner['notify']),
  397. '$poll' => xmlify($owner['poll']),
  398. '$sitepubkey' => xmlify(get_config('system','site_pubkey')),
  399. //'$pubkey' => xmlify($owner['pubkey']),
  400. //'$prvkey' => xmlify($owner['prvkey']),
  401. ));
  402. $recipients_relocate = q("SELECT * FROM contact WHERE uid = %d AND self = 0 AND network = '%s'" , intval($uid), NETWORK_DFRN);
  403. unset($photos);
  404. } else {
  405. if($followup) {
  406. foreach($items as $item) { // there is only one item
  407. if(! $item['parent'])
  408. continue;
  409. if($item['id'] == $item_id) {
  410. logger('notifier: followup: item: ' . print_r($item,true), LOGGER_DATA);
  411. $slap = atom_entry($item,'html',null,$owner,false);
  412. $atom .= atom_entry($item,'text',null,$owner,false);
  413. }
  414. }
  415. } else {
  416. foreach($items as $item) {
  417. if(! $item['parent'])
  418. continue;
  419. // private emails may be in included in public conversations. Filter them.
  420. if(($public_message) && $item['private'] == 1)
  421. continue;
  422. $contact = get_item_contact($item,$contacts);
  423. if(! $contact)
  424. continue;
  425. if($normal_mode) {
  426. // we only need the current item, but include the parent because without it
  427. // older sites without a corresponding dfrn_notify change may do the wrong thing.
  428. if($item_id == $item['id'] || $item['id'] == $item['parent'])
  429. $atom .= atom_entry($item,'text',null,$owner,true);
  430. } else
  431. $atom .= atom_entry($item,'text',null,$owner,true);
  432. if(($top_level) && ($public_message) && ($item['author-link'] === $item['owner-link']) && (! $expire))
  433. $slaps[] = atom_entry($item,'html',null,$owner,true);
  434. }
  435. }
  436. }
  437. $atom .= '</feed>' . "\r\n";
  438. logger('notifier: ' . $atom, LOGGER_DATA);
  439. logger('notifier: slaps: ' . print_r($slaps,true), LOGGER_DATA);
  440. // If this is a public message and pubmail is set on the parent, include all your email contacts
  441. $mail_disabled = ((function_exists('imap_open') && (! get_config('system','imap_disabled'))) ? 0 : 1);
  442. if(! $mail_disabled) {
  443. if((! strlen($target_item['allow_cid'])) && (! strlen($target_item['allow_gid']))
  444. && (! strlen($target_item['deny_cid'])) && (! strlen($target_item['deny_gid']))
  445. && (intval($target_item['pubmail']))) {
  446. $r = q("SELECT * FROM `contact` WHERE `uid` = %d AND `network` = '%s'",
  447. intval($uid),
  448. dbesc(NETWORK_MAIL)
  449. );
  450. if(count($r)) {
  451. foreach($r as $rr)
  452. $recipients[] = $rr['id'];
  453. }
  454. }
  455. }
  456. if($followup)
  457. $recip_str = $parent['contact-id'].$ostatus_recip_str;
  458. else
  459. $recip_str = implode(', ', $recipients);
  460. if ($relocate)
  461. $r = $recipients_relocate;
  462. else
  463. $r = q("SELECT * FROM `contact` WHERE `id` IN ( %s ) AND `blocked` = 0 AND `pending` = 0 ",
  464. dbesc($recip_str)
  465. );
  466. require_once('include/salmon.php');
  467. $interval = ((get_config('system','delivery_interval') === false) ? 2 : intval(get_config('system','delivery_interval')));
  468. // delivery loop
  469. if(count($r)) {
  470. foreach($r as $contact) {
  471. if((! $mail) && (! $fsuggest) && (!$followup OR ($parent['contact-id'] != $contact['id'])) && (!$relocate) && (! $contact['self'])) {
  472. if(($contact['network'] === NETWORK_DIASPORA) && ($public_message))
  473. continue;
  474. q("insert into deliverq ( `cmd`,`item`,`contact` ) values ('%s', %d, %d )",
  475. dbesc($cmd),
  476. intval($item_id),
  477. intval($contact['id'])
  478. );
  479. }
  480. }
  481. // This controls the number of deliveries to execute with each separate delivery process.
  482. // By default we'll perform one delivery per process. Assuming a hostile shared hosting
  483. // provider, this provides the greatest chance of deliveries if processes start getting
  484. // killed. We can also space them out with the delivery_interval to also help avoid them
  485. // getting whacked.
  486. // If $deliveries_per_process > 1, we will chain this number of multiple deliveries
  487. // together into a single process. This will reduce the overall number of processes
  488. // spawned for each delivery, but they will run longer.
  489. $deliveries_per_process = intval(get_config('system','delivery_batch_count'));
  490. if($deliveries_per_process <= 0)
  491. $deliveries_per_process = 1;
  492. $this_batch = array();
  493. for($x = 0; $x < count($r); $x ++) {
  494. $contact = $r[$x];
  495. if($contact['self'])
  496. continue;
  497. logger("Deliver to ".$contact['url'], LOGGER_DEBUG);
  498. // potentially more than one recipient. Start a new process and space them out a bit.
  499. // we will deliver single recipient types of message and email recipients here.
  500. if((! $mail) && (! $fsuggest) && (!$relocate) && (!$followup OR ($parent['contact-id'] != $contact['id']))) {
  501. $this_batch[] = $contact['id'];
  502. if(count($this_batch) == $deliveries_per_process) {
  503. proc_run('php','include/delivery.php',$cmd,$item_id,$this_batch);
  504. $this_batch = array();
  505. if($interval)
  506. @time_sleep_until(microtime(true) + (float) $interval);
  507. }
  508. continue;
  509. }
  510. // be sure to pick up any stragglers
  511. if(count($this_batch))
  512. proc_run('php','include/delivery.php',$cmd,$item_id,$this_batch);
  513. $deliver_status = 0;
  514. logger("main delivery by notifier: followup=$followup mail=$mail fsuggest=$fsuggest relocate=$relocate");
  515. switch($contact['network']) {
  516. case NETWORK_DFRN:
  517. // perform local delivery if we are on the same site
  518. $basepath = implode('/', array_slice(explode('/',$contact['url']),0,3));
  519. if(link_compare($basepath,$a->get_baseurl())) {
  520. $nickname = basename($contact['url']);
  521. if($contact['issued-id'])
  522. $sql_extra = sprintf(" AND `dfrn-id` = '%s' ", dbesc($contact['issued-id']));
  523. else
  524. $sql_extra = sprintf(" AND `issued-id` = '%s' ", dbesc($contact['dfrn-id']));
  525. $x = q("SELECT `contact`.*, `contact`.`uid` AS `importer_uid`,
  526. `contact`.`pubkey` AS `cpubkey`,
  527. `contact`.`prvkey` AS `cprvkey`,
  528. `contact`.`thumb` AS `thumb`,
  529. `contact`.`url` as `url`,
  530. `contact`.`name` as `senderName`,
  531. `user`.*
  532. FROM `contact`
  533. INNER JOIN `user` ON `contact`.`uid` = `user`.`uid`
  534. WHERE `contact`.`blocked` = 0 AND `contact`.`archive` = 0
  535. AND `contact`.`pending` = 0
  536. AND `contact`.`network` = '%s' AND `user`.`nickname` = '%s'
  537. $sql_extra
  538. AND `user`.`account_expired` = 0 AND `user`.`account_removed` = 0 LIMIT 1",
  539. dbesc(NETWORK_DFRN),
  540. dbesc($nickname)
  541. );
  542. if($x && count($x)) {
  543. $write_flag = ((($x[0]['rel']) && ($x[0]['rel'] != CONTACT_IS_SHARING)) ? true : false);
  544. if((($owner['page-flags'] == PAGE_COMMUNITY) || ($write_flag)) && (! $x[0]['writable'])) {
  545. q("update contact set writable = 1 where id = %d",
  546. intval($x[0]['id'])
  547. );
  548. $x[0]['writable'] = 1;
  549. }
  550. // if contact's ssl policy changed, which we just determined
  551. // is on our own server, update our contact links
  552. $ssl_policy = get_config('system','ssl_policy');
  553. fix_contact_ssl_policy($x[0],$ssl_policy);
  554. // If we are setup as a soapbox we aren't accepting input from this person
  555. if($x[0]['page-flags'] == PAGE_SOAPBOX)
  556. break;
  557. require_once('library/simplepie/simplepie.inc');
  558. logger('mod-delivery: local delivery');
  559. local_delivery($x[0],$atom);
  560. break;
  561. }
  562. }
  563. logger('notifier: dfrndelivery: ' . $contact['name']);
  564. $deliver_status = dfrn_deliver($owner,$contact,$atom);
  565. logger('notifier: dfrn_delivery returns ' . $deliver_status);
  566. if($deliver_status == (-1)) {
  567. logger('notifier: delivery failed: queuing message');
  568. // queue message for redelivery
  569. add_to_queue($contact['id'],NETWORK_DFRN,$atom);
  570. }
  571. break;
  572. case NETWORK_OSTATUS:
  573. // Do not send to ostatus if we are not configured to send to public networks
  574. if($owner['prvnets'])
  575. break;
  576. if(get_config('system','ostatus_disabled') || get_config('system','dfrn_only'))
  577. break;
  578. if($followup && $contact['notify']) {
  579. logger('slapdelivery followup item '.$item_id.' to ' . $contact['name']);
  580. $deliver_status = slapper($owner,$contact['notify'],$slap);
  581. if($deliver_status == (-1)) {
  582. // queue message for redelivery
  583. add_to_queue($contact['id'],NETWORK_OSTATUS,$slap);
  584. }
  585. } else {
  586. // only send salmon if public - e.g. if it's ok to notify
  587. // a public hub, it's ok to send a salmon
  588. if((count($slaps)) && ($public_message) && (! $expire)) {
  589. logger('slapdelivery item '.$item_id.' to ' . $contact['name']);
  590. foreach($slaps as $slappy) {
  591. if($contact['notify']) {
  592. $deliver_status = slapper($owner,$contact['notify'],$slappy);
  593. if($deliver_status == (-1)) {
  594. // queue message for redelivery
  595. add_to_queue($contact['id'],NETWORK_OSTATUS,$slappy);
  596. }
  597. }
  598. }
  599. }
  600. }
  601. break;
  602. case NETWORK_MAIL:
  603. case NETWORK_MAIL2:
  604. if(get_config('system','dfrn_only'))
  605. break;
  606. // WARNING: does not currently convert to RFC2047 header encodings, etc.
  607. $addr = $contact['addr'];
  608. if(! strlen($addr))
  609. break;
  610. if($cmd === 'wall-new' || $cmd === 'comment-new') {
  611. $it = null;
  612. if($cmd === 'wall-new')
  613. $it = $items[0];
  614. else {
  615. $r = q("SELECT * FROM `item` WHERE `id` = %d AND `uid` = %d LIMIT 1",
  616. intval($argv[2]),
  617. intval($uid)
  618. );
  619. if(count($r))
  620. $it = $r[0];
  621. }
  622. if(! $it)
  623. break;
  624. $local_user = q("SELECT * FROM `user` WHERE `uid` = %d LIMIT 1",
  625. intval($uid)
  626. );
  627. if(! count($local_user))
  628. break;
  629. $reply_to = '';
  630. $r1 = q("SELECT * FROM `mailacct` WHERE `uid` = %d LIMIT 1",
  631. intval($uid)
  632. );
  633. if($r1 && $r1[0]['reply_to'])
  634. $reply_to = $r1[0]['reply_to'];
  635. $subject = (($it['title']) ? email_header_encode($it['title'],'UTF-8') : t("\x28no subject\x29")) ;
  636. // only expose our real email address to true friends
  637. if(($contact['rel'] == CONTACT_IS_FRIEND) && (! $contact['blocked']))
  638. if($reply_to) {
  639. $headers = 'From: ' . email_header_encode($local_user[0]['username'],'UTF-8') . ' <' . $reply_to . '>' . "\n";
  640. $headers .= 'Sender: '.$local_user[0]['email']."\n";
  641. } else
  642. $headers = 'From: ' . email_header_encode($local_user[0]['username'],'UTF-8') . ' <' . $local_user[0]['email'] . '>' . "\n";
  643. else
  644. $headers = 'From: ' . email_header_encode($local_user[0]['username'],'UTF-8') . ' <' . t('noreply') . '@' . $a->get_hostname() . '>' . "\n";
  645. //if($reply_to)
  646. // $headers .= 'Reply-to: ' . $reply_to . "\n";
  647. $headers .= 'Message-Id: <' . iri2msgid($it['uri']) . '>' . "\n";
  648. if($it['uri'] !== $it['parent-uri']) {
  649. $headers .= "References: <".iri2msgid($it["parent-uri"]).">";
  650. // If Threading is enabled, write down the correct parent
  651. if (($it["thr-parent"] != "") and ($it["thr-parent"] != $it["parent-uri"]))
  652. $headers .= " <".iri2msgid($it["thr-parent"]).">";
  653. $headers .= "\n";
  654. if(!$it['title']) {
  655. $r = q("SELECT `title` FROM `item` WHERE `uri` = '%s' AND `uid` = %d LIMIT 1",
  656. dbesc($it['parent-uri']),
  657. intval($uid));
  658. if(count($r) AND ($r[0]['title'] != ''))
  659. $subject = $r[0]['title'];
  660. else {
  661. $r = q("SELECT `title` FROM `item` WHERE `parent-uri` = '%s' AND `uid` = %d LIMIT 1",
  662. dbesc($it['parent-uri']),
  663. intval($uid));
  664. if(count($r) AND ($r[0]['title'] != ''))
  665. $subject = $r[0]['title'];
  666. }
  667. }
  668. if(strncasecmp($subject,'RE:',3))
  669. $subject = 'Re: '.$subject;
  670. }
  671. email_send($addr, $subject, $headers, $it);
  672. }
  673. break;
  674. case NETWORK_DIASPORA:
  675. require_once('include/diaspora.php');
  676. if(get_config('system','dfrn_only') || (! get_config('system','diaspora_enabled')))
  677. break;
  678. if($mail) {
  679. diaspora_send_mail($item,$owner,$contact);
  680. break;
  681. }
  682. if(! $normal_mode)
  683. break;
  684. // special handling for followup to public post
  685. // all other public posts processed as public batches further below
  686. if($public_message) {
  687. if($followup)
  688. diaspora_send_followup($target_item,$owner,$contact, true);
  689. break;
  690. }
  691. if(! $contact['pubkey'])
  692. break;
  693. if($target_item['verb'] === ACTIVITY_DISLIKE) {
  694. // unsupported
  695. break;
  696. }
  697. elseif(($target_item['deleted']) && (($target_item['uri'] === $target_item['parent-uri']) || $followup)) {
  698. // send both top-level retractions and relayable retractions for owner to relay
  699. diaspora_send_retraction($target_item,$owner,$contact);
  700. break;
  701. }
  702. elseif($followup) {
  703. // send comments and likes to owner to relay
  704. diaspora_send_followup($target_item,$owner,$contact);
  705. break;
  706. }
  707. elseif($target_item['uri'] !== $target_item['parent-uri']) {
  708. // we are the relay - send comments, likes and relayable_retractions
  709. // (of comments and likes) to our conversants
  710. diaspora_send_relay($target_item,$owner,$contact);
  711. break;
  712. }
  713. elseif(($top_level) && (! $walltowall)) {
  714. // currently no workable solution for sending walltowall
  715. diaspora_send_status($target_item,$owner,$contact);
  716. break;
  717. }
  718. break;
  719. case NETWORK_FEED:
  720. case NETWORK_FACEBOOK:
  721. if(get_config('system','dfrn_only'))
  722. break;
  723. case NETWORK_PUMPIO:
  724. if(get_config('system','dfrn_only'))
  725. break;
  726. default:
  727. break;
  728. }
  729. }
  730. }
  731. // send additional slaps to mentioned remote tags (@foo@example.com)
  732. if($slap && count($url_recipients) && ($followup || $top_level) && $public_message && (! $expire)) {
  733. if(! get_config('system','dfrn_only')) {
  734. foreach($url_recipients as $url) {
  735. if($url) {
  736. logger('notifier: urldelivery: ' . $url);
  737. $deliver_status = slapper($owner,$url,$slap);
  738. // TODO: redeliver/queue these items on failure, though there is no contact record
  739. }
  740. }
  741. }
  742. }
  743. if($public_message) {
  744. $r1 = q("SELECT DISTINCT(`batch`), `id`, `name`,`network` FROM `contact` WHERE `network` = '%s'
  745. AND `uid` = %d AND `rel` != %d group by `batch` ORDER BY rand() ",
  746. dbesc(NETWORK_DIASPORA),
  747. intval($owner['uid']),
  748. intval(CONTACT_IS_SHARING)
  749. );
  750. $r2 = q("SELECT `id`, `name`,`network` FROM `contact`
  751. WHERE `network` in ( '%s', '%s') AND `uid` = %d AND `blocked` = 0 AND `pending` = 0 AND `archive` = 0
  752. AND `rel` != %d order by rand() ",
  753. dbesc(NETWORK_DFRN),
  754. dbesc(NETWORK_MAIL2),
  755. intval($owner['uid']),
  756. intval(CONTACT_IS_SHARING)
  757. );
  758. $r = array_merge($r2,$r1);
  759. if(count($r)) {
  760. logger('pubdeliver: ' . print_r($r,true), LOGGER_DEBUG);
  761. // throw everything into the queue in case we get killed
  762. foreach($r as $rr) {
  763. if((! $mail) && (! $fsuggest) && (!$followup OR ($parent['contact-id'] != $contact['id']))) {
  764. q("insert into deliverq ( `cmd`,`item`,`contact` ) values ('%s', %d, %d )",
  765. dbesc($cmd),
  766. intval($item_id),
  767. intval($rr['id'])
  768. );
  769. }
  770. }
  771. foreach($r as $rr) {
  772. // except for Diaspora batch jobs
  773. // Don't deliver to folks who have already been delivered to
  774. if(($rr['network'] !== NETWORK_DIASPORA) && (in_array($rr['id'],$conversants))) {
  775. logger('notifier: already delivered id=' . $rr['id']);
  776. continue;
  777. }
  778. if((! $mail) && (! $fsuggest) && (! $followup)) {
  779. logger('notifier: delivery agent: ' . $rr['name'] . ' ' . $rr['id']);
  780. proc_run('php','include/delivery.php',$cmd,$item_id,$rr['id']);
  781. if($interval)
  782. @time_sleep_until(microtime(true) + (float) $interval);
  783. }
  784. }
  785. }
  786. $push_notify = true;
  787. }
  788. if($push_notify AND strlen($hub)) {
  789. $hubs = explode(',', $hub);
  790. if(count($hubs)) {
  791. foreach($hubs as $h) {
  792. $h = trim($h);
  793. if(! strlen($h))
  794. continue;
  795. if ($h === '[internal]') {
  796. // Set push flag for PuSH subscribers to this topic,
  797. // they will be notified in queue.php
  798. q("UPDATE `push_subscriber` SET `push` = 1 " .
  799. "WHERE `nickname` = '%s'", dbesc($owner['nickname']));
  800. logger('Activating internal PuSH for item '.$item_id, LOGGER_DEBUG);
  801. } else {
  802. $params = 'hub.mode=publish&hub.url=' . urlencode( $a->get_baseurl() . '/dfrn_poll/' . $owner['nickname'] );
  803. post_url($h,$params);
  804. logger('publish for item '.$item_id.' ' . $h . ' ' . $params . ' returned ' . $a->get_curl_code());
  805. }
  806. if(count($hubs) > 1)
  807. sleep(7); // try and avoid multiple hubs responding at precisely the same time
  808. }
  809. }
  810. // Handling the pubsubhubbub requests
  811. proc_run('php','include/pubsubpublish.php');
  812. }
  813. // If the item was deleted, clean up the `sign` table
  814. if($target_item['deleted']) {
  815. $r = q("DELETE FROM sign where `retract_iid` = %d",
  816. intval($target_item['id'])
  817. );
  818. }
  819. logger('notifier: calling hooks', LOGGER_DEBUG);
  820. if($normal_mode)
  821. call_hooks('notifier_normal',$target_item);
  822. call_hooks('notifier_end',$target_item);
  823. return;
  824. }
  825. if (array_search(__file__,get_included_files())===0){
  826. notifier_run($_SERVER["argv"],$_SERVER["argc"]);
  827. killme();
  828. }