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.

508 lines
17 KiB

3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
3 years ago
Cleanups: isResult() more used, readability improved (#5608) * [diaspora]: Maybe SimpleXMLElement is the right type-hint? * Changes proposed + pre-renaming: - pre-renamed $db -> $connection - added TODOs for not allowing bad method invocations (there is a BadMethodCallException in SPL) * If no record is found, below $r[0] will fail with a E_NOTICE and the code doesn't behave as expected. * Ops, one more left ... * Continued: - added documentation for Contact::updateSslPolicy() method - added type-hint for $contact of same method - empty lines added + TODO where the bug origins that $item has no element 'body' * Added empty lines for better readability * Cleaned up: - no more x() (deprecated) usage but empty() instead - fixed mixing of space/tab indending - merged else/if block goether in elseif() (lesser nested code blocks) * Re-fixed DBM -> DBA switch * Fixes/rewrites: - use empty()/isset() instead of deprecated x() - merged 2 nested if() blocks into one - avoided nested if() block inside else block by rewriting it to elseif() - $contact_id is an integer, let's test on > 0 here - added a lot spaces and some empty lines for better readability * Rewrite: - moved all CONTACT_* constants from boot.php to Contact class * CR request: - renamed Contact::CONTACT_IS_* -> Contact::* ;-) * Rewrites: - moved PAGE_* to Friendica\Model\Profile class - fixed mixure with "Contact::* rewrite" * Ops, one still there (return is no function) * Rewrite to Proxy class: - introduced new Friendica\Network\Proxy class for in exchange of proxy_*() functions - moved also all PROXY_* constants there as Proxy::* - removed now no longer needed mod/proxy.php loading as composer's auto-load will do this for us - renamed those proxy_*() functions to better names: + proxy_init() -> Proxy::init() (public) + proxy_url() -> Proxy::proxifyUrl() (public) + proxy_parse_html() -> Proxy::proxifyHtml() (public) + proxy_is_local_image() -> Proxy::isLocalImage() (private) + proxy_parse_query() -> Proxy::parseQuery() (private) + proxy_img_cb() -> Proxy::replaceUrl() (private) * CR request: - moved all PAGE_* constants to Friendica\Model\Contact class - fixed all references of both classes * Ops, need to set $a here ... * CR request: - moved Proxy class to Friendica\Module - extended BaseModule * Ops, no need for own instance of $a when self::getApp() is around. * Proxy-rewrite: - proxy_url() and proxy_parse_html() are both non-module functions (now methods) - so they must be splitted into a seperate class - also the SIZE_* and DEFAULT_TIME constants are both not relevant to module * No instances from utility classes * Fixed error: - proxify*() is now located in `Friendica\Util\ProxyUtils` * Moved back to original place, ops? How did they move here? Well, it was not intended by me. * Removed duplicate (left-over from split) constants and static array. Thank to MrPetovan finding it. * Renamed ProxyUtils -> Proxy and aliased it back to ProxyUtils. * Rewrite: - stopped using deprecated NETWORK_* constants, now Protocol::* should be used - still left them intact for slow/lazy developers ... * Ops, was added accidentally ... * Ops, why these wrong moves? * Ops, one to much (thanks to MrPetovan) * Ops, wrong moving ... * moved back to original place ... * spaces added * empty lines add for better readability. * convertered spaces -> tab for code indenting. * CR request: Add space between if and brace. * CR requests fixed + move reverted - ops, src/Module/*.php has been moved to src/Network/ accidentally - reverted some parts in src/Database/DBA.php as pointed out by Annando - removed internal TODO items - added some spaces for better readability
3 years ago
Cleanups: isResult() more used, readability improved (#5608) * [diaspora]: Maybe SimpleXMLElement is the right type-hint? * Changes proposed + pre-renaming: - pre-renamed $db -> $connection - added TODOs for not allowing bad method invocations (there is a BadMethodCallException in SPL) * If no record is found, below $r[0] will fail with a E_NOTICE and the code doesn't behave as expected. * Ops, one more left ... * Continued: - added documentation for Contact::updateSslPolicy() method - added type-hint for $contact of same method - empty lines added + TODO where the bug origins that $item has no element 'body' * Added empty lines for better readability * Cleaned up: - no more x() (deprecated) usage but empty() instead - fixed mixing of space/tab indending - merged else/if block goether in elseif() (lesser nested code blocks) * Re-fixed DBM -> DBA switch * Fixes/rewrites: - use empty()/isset() instead of deprecated x() - merged 2 nested if() blocks into one - avoided nested if() block inside else block by rewriting it to elseif() - $contact_id is an integer, let's test on > 0 here - added a lot spaces and some empty lines for better readability * Rewrite: - moved all CONTACT_* constants from boot.php to Contact class * CR request: - renamed Contact::CONTACT_IS_* -> Contact::* ;-) * Rewrites: - moved PAGE_* to Friendica\Model\Profile class - fixed mixure with "Contact::* rewrite" * Ops, one still there (return is no function) * Rewrite to Proxy class: - introduced new Friendica\Network\Proxy class for in exchange of proxy_*() functions - moved also all PROXY_* constants there as Proxy::* - removed now no longer needed mod/proxy.php loading as composer's auto-load will do this for us - renamed those proxy_*() functions to better names: + proxy_init() -> Proxy::init() (public) + proxy_url() -> Proxy::proxifyUrl() (public) + proxy_parse_html() -> Proxy::proxifyHtml() (public) + proxy_is_local_image() -> Proxy::isLocalImage() (private) + proxy_parse_query() -> Proxy::parseQuery() (private) + proxy_img_cb() -> Proxy::replaceUrl() (private) * CR request: - moved all PAGE_* constants to Friendica\Model\Contact class - fixed all references of both classes * Ops, need to set $a here ... * CR request: - moved Proxy class to Friendica\Module - extended BaseModule * Ops, no need for own instance of $a when self::getApp() is around. * Proxy-rewrite: - proxy_url() and proxy_parse_html() are both non-module functions (now methods) - so they must be splitted into a seperate class - also the SIZE_* and DEFAULT_TIME constants are both not relevant to module * No instances from utility classes * Fixed error: - proxify*() is now located in `Friendica\Util\ProxyUtils` * Moved back to original place, ops? How did they move here? Well, it was not intended by me. * Removed duplicate (left-over from split) constants and static array. Thank to MrPetovan finding it. * Renamed ProxyUtils -> Proxy and aliased it back to ProxyUtils. * Rewrite: - stopped using deprecated NETWORK_* constants, now Protocol::* should be used - still left them intact for slow/lazy developers ... * Ops, was added accidentally ... * Ops, why these wrong moves? * Ops, one to much (thanks to MrPetovan) * Ops, wrong moving ... * moved back to original place ... * spaces added * empty lines add for better readability. * convertered spaces -> tab for code indenting. * CR request: Add space between if and brace. * CR requests fixed + move reverted - ops, src/Module/*.php has been moved to src/Network/ accidentally - reverted some parts in src/Database/DBA.php as pointed out by Annando - removed internal TODO items - added some spaces for better readability
3 years ago
Cleanups: isResult() more used, readability improved (#5608) * [diaspora]: Maybe SimpleXMLElement is the right type-hint? * Changes proposed + pre-renaming: - pre-renamed $db -> $connection - added TODOs for not allowing bad method invocations (there is a BadMethodCallException in SPL) * If no record is found, below $r[0] will fail with a E_NOTICE and the code doesn't behave as expected. * Ops, one more left ... * Continued: - added documentation for Contact::updateSslPolicy() method - added type-hint for $contact of same method - empty lines added + TODO where the bug origins that $item has no element 'body' * Added empty lines for better readability * Cleaned up: - no more x() (deprecated) usage but empty() instead - fixed mixing of space/tab indending - merged else/if block goether in elseif() (lesser nested code blocks) * Re-fixed DBM -> DBA switch * Fixes/rewrites: - use empty()/isset() instead of deprecated x() - merged 2 nested if() blocks into one - avoided nested if() block inside else block by rewriting it to elseif() - $contact_id is an integer, let's test on > 0 here - added a lot spaces and some empty lines for better readability * Rewrite: - moved all CONTACT_* constants from boot.php to Contact class * CR request: - renamed Contact::CONTACT_IS_* -> Contact::* ;-) * Rewrites: - moved PAGE_* to Friendica\Model\Profile class - fixed mixure with "Contact::* rewrite" * Ops, one still there (return is no function) * Rewrite to Proxy class: - introduced new Friendica\Network\Proxy class for in exchange of proxy_*() functions - moved also all PROXY_* constants there as Proxy::* - removed now no longer needed mod/proxy.php loading as composer's auto-load will do this for us - renamed those proxy_*() functions to better names: + proxy_init() -> Proxy::init() (public) + proxy_url() -> Proxy::proxifyUrl() (public) + proxy_parse_html() -> Proxy::proxifyHtml() (public) + proxy_is_local_image() -> Proxy::isLocalImage() (private) + proxy_parse_query() -> Proxy::parseQuery() (private) + proxy_img_cb() -> Proxy::replaceUrl() (private) * CR request: - moved all PAGE_* constants to Friendica\Model\Contact class - fixed all references of both classes * Ops, need to set $a here ... * CR request: - moved Proxy class to Friendica\Module - extended BaseModule * Ops, no need for own instance of $a when self::getApp() is around. * Proxy-rewrite: - proxy_url() and proxy_parse_html() are both non-module functions (now methods) - so they must be splitted into a seperate class - also the SIZE_* and DEFAULT_TIME constants are both not relevant to module * No instances from utility classes * Fixed error: - proxify*() is now located in `Friendica\Util\ProxyUtils` * Moved back to original place, ops? How did they move here? Well, it was not intended by me. * Removed duplicate (left-over from split) constants and static array. Thank to MrPetovan finding it. * Renamed ProxyUtils -> Proxy and aliased it back to ProxyUtils. * Rewrite: - stopped using deprecated NETWORK_* constants, now Protocol::* should be used - still left them intact for slow/lazy developers ... * Ops, was added accidentally ... * Ops, why these wrong moves? * Ops, one to much (thanks to MrPetovan) * Ops, wrong moving ... * moved back to original place ... * spaces added * empty lines add for better readability. * convertered spaces -> tab for code indenting. * CR request: Add space between if and brace. * CR requests fixed + move reverted - ops, src/Module/*.php has been moved to src/Network/ accidentally - reverted some parts in src/Database/DBA.php as pointed out by Annando - removed internal TODO items - added some spaces for better readability
3 years ago
Cleanups: isResult() more used, readability improved (#5608) * [diaspora]: Maybe SimpleXMLElement is the right type-hint? * Changes proposed + pre-renaming: - pre-renamed $db -> $connection - added TODOs for not allowing bad method invocations (there is a BadMethodCallException in SPL) * If no record is found, below $r[0] will fail with a E_NOTICE and the code doesn't behave as expected. * Ops, one more left ... * Continued: - added documentation for Contact::updateSslPolicy() method - added type-hint for $contact of same method - empty lines added + TODO where the bug origins that $item has no element 'body' * Added empty lines for better readability * Cleaned up: - no more x() (deprecated) usage but empty() instead - fixed mixing of space/tab indending - merged else/if block goether in elseif() (lesser nested code blocks) * Re-fixed DBM -> DBA switch * Fixes/rewrites: - use empty()/isset() instead of deprecated x() - merged 2 nested if() blocks into one - avoided nested if() block inside else block by rewriting it to elseif() - $contact_id is an integer, let's test on > 0 here - added a lot spaces and some empty lines for better readability * Rewrite: - moved all CONTACT_* constants from boot.php to Contact class * CR request: - renamed Contact::CONTACT_IS_* -> Contact::* ;-) * Rewrites: - moved PAGE_* to Friendica\Model\Profile class - fixed mixure with "Contact::* rewrite" * Ops, one still there (return is no function) * Rewrite to Proxy class: - introduced new Friendica\Network\Proxy class for in exchange of proxy_*() functions - moved also all PROXY_* constants there as Proxy::* - removed now no longer needed mod/proxy.php loading as composer's auto-load will do this for us - renamed those proxy_*() functions to better names: + proxy_init() -> Proxy::init() (public) + proxy_url() -> Proxy::proxifyUrl() (public) + proxy_parse_html() -> Proxy::proxifyHtml() (public) + proxy_is_local_image() -> Proxy::isLocalImage() (private) + proxy_parse_query() -> Proxy::parseQuery() (private) + proxy_img_cb() -> Proxy::replaceUrl() (private) * CR request: - moved all PAGE_* constants to Friendica\Model\Contact class - fixed all references of both classes * Ops, need to set $a here ... * CR request: - moved Proxy class to Friendica\Module - extended BaseModule * Ops, no need for own instance of $a when self::getApp() is around. * Proxy-rewrite: - proxy_url() and proxy_parse_html() are both non-module functions (now methods) - so they must be splitted into a seperate class - also the SIZE_* and DEFAULT_TIME constants are both not relevant to module * No instances from utility classes * Fixed error: - proxify*() is now located in `Friendica\Util\ProxyUtils` * Moved back to original place, ops? How did they move here? Well, it was not intended by me. * Removed duplicate (left-over from split) constants and static array. Thank to MrPetovan finding it. * Renamed ProxyUtils -> Proxy and aliased it back to ProxyUtils. * Rewrite: - stopped using deprecated NETWORK_* constants, now Protocol::* should be used - still left them intact for slow/lazy developers ... * Ops, was added accidentally ... * Ops, why these wrong moves? * Ops, one to much (thanks to MrPetovan) * Ops, wrong moving ... * moved back to original place ... * spaces added * empty lines add for better readability. * convertered spaces -> tab for code indenting. * CR request: Add space between if and brace. * CR requests fixed + move reverted - ops, src/Module/*.php has been moved to src/Network/ accidentally - reverted some parts in src/Database/DBA.php as pointed out by Annando - removed internal TODO items - added some spaces for better readability
3 years ago
Cleanups: isResult() more used, readability improved (#5608) * [diaspora]: Maybe SimpleXMLElement is the right type-hint? * Changes proposed + pre-renaming: - pre-renamed $db -> $connection - added TODOs for not allowing bad method invocations (there is a BadMethodCallException in SPL) * If no record is found, below $r[0] will fail with a E_NOTICE and the code doesn't behave as expected. * Ops, one more left ... * Continued: - added documentation for Contact::updateSslPolicy() method - added type-hint for $contact of same method - empty lines added + TODO where the bug origins that $item has no element 'body' * Added empty lines for better readability * Cleaned up: - no more x() (deprecated) usage but empty() instead - fixed mixing of space/tab indending - merged else/if block goether in elseif() (lesser nested code blocks) * Re-fixed DBM -> DBA switch * Fixes/rewrites: - use empty()/isset() instead of deprecated x() - merged 2 nested if() blocks into one - avoided nested if() block inside else block by rewriting it to elseif() - $contact_id is an integer, let's test on > 0 here - added a lot spaces and some empty lines for better readability * Rewrite: - moved all CONTACT_* constants from boot.php to Contact class * CR request: - renamed Contact::CONTACT_IS_* -> Contact::* ;-) * Rewrites: - moved PAGE_* to Friendica\Model\Profile class - fixed mixure with "Contact::* rewrite" * Ops, one still there (return is no function) * Rewrite to Proxy class: - introduced new Friendica\Network\Proxy class for in exchange of proxy_*() functions - moved also all PROXY_* constants there as Proxy::* - removed now no longer needed mod/proxy.php loading as composer's auto-load will do this for us - renamed those proxy_*() functions to better names: + proxy_init() -> Proxy::init() (public) + proxy_url() -> Proxy::proxifyUrl() (public) + proxy_parse_html() -> Proxy::proxifyHtml() (public) + proxy_is_local_image() -> Proxy::isLocalImage() (private) + proxy_parse_query() -> Proxy::parseQuery() (private) + proxy_img_cb() -> Proxy::replaceUrl() (private) * CR request: - moved all PAGE_* constants to Friendica\Model\Contact class - fixed all references of both classes * Ops, need to set $a here ... * CR request: - moved Proxy class to Friendica\Module - extended BaseModule * Ops, no need for own instance of $a when self::getApp() is around. * Proxy-rewrite: - proxy_url() and proxy_parse_html() are both non-module functions (now methods) - so they must be splitted into a seperate class - also the SIZE_* and DEFAULT_TIME constants are both not relevant to module * No instances from utility classes * Fixed error: - proxify*() is now located in `Friendica\Util\ProxyUtils` * Moved back to original place, ops? How did they move here? Well, it was not intended by me. * Removed duplicate (left-over from split) constants and static array. Thank to MrPetovan finding it. * Renamed ProxyUtils -> Proxy and aliased it back to ProxyUtils. * Rewrite: - stopped using deprecated NETWORK_* constants, now Protocol::* should be used - still left them intact for slow/lazy developers ... * Ops, was added accidentally ... * Ops, why these wrong moves? * Ops, one to much (thanks to MrPetovan) * Ops, wrong moving ... * moved back to original place ... * spaces added * empty lines add for better readability. * convertered spaces -> tab for code indenting. * CR request: Add space between if and brace. * CR requests fixed + move reverted - ops, src/Module/*.php has been moved to src/Network/ accidentally - reverted some parts in src/Database/DBA.php as pointed out by Annando - removed internal TODO items - added some spaces for better readability
3 years ago
  1. <?php
  2. /**
  3. * @file src/Worker/Delivery.php
  4. */
  5. namespace Friendica\Worker;
  6. use Friendica\BaseObject;
  7. use Friendica\Core\Config;
  8. use Friendica\Core\L10n;
  9. use Friendica\Core\Logger;
  10. use Friendica\Core\Protocol;
  11. use Friendica\Core\System;
  12. use Friendica\Database\DBA;
  13. use Friendica\Model;
  14. use Friendica\Protocol\DFRN;
  15. use Friendica\Protocol\Diaspora;
  16. use Friendica\Protocol\Email;
  17. use Friendica\Util\Strings;
  18. use Friendica\Util\Network;
  19. use Friendica\Core\Worker;
  20. class Delivery extends BaseObject
  21. {
  22. const MAIL = 'mail';
  23. const SUGGESTION = 'suggest';
  24. const RELOCATION = 'relocate';
  25. const DELETION = 'drop';
  26. const POST = 'wall-new';
  27. const POKE = 'poke';
  28. const UPLINK = 'uplink';
  29. const REMOVAL = 'removeme';
  30. const PROFILEUPDATE = 'profileupdate';
  31. public static function execute($cmd, $target_id, $contact_id)
  32. {
  33. Logger::log('Invoked: ' . $cmd . ': ' . $target_id . ' to ' . $contact_id, Logger::DEBUG);
  34. $top_level = false;
  35. $followup = false;
  36. $public_message = false;
  37. $items = [];
  38. if ($cmd == self::MAIL) {
  39. $target_item = DBA::selectFirst('mail', [], ['id' => $target_id]);
  40. if (!DBA::isResult($target_item)) {
  41. return;
  42. }
  43. $uid = $target_item['uid'];
  44. } elseif ($cmd == self::SUGGESTION) {
  45. $target_item = DBA::selectFirst('fsuggest', [], ['id' => $target_id]);
  46. if (!DBA::isResult($target_item)) {
  47. return;
  48. }
  49. $uid = $target_item['uid'];
  50. } elseif ($cmd == self::RELOCATION) {
  51. $uid = $target_id;
  52. $target_item = [];
  53. } else {
  54. $item = Model\Item::selectFirst(['parent'], ['id' => $target_id]);
  55. if (!DBA::isResult($item) || empty($item['parent'])) {
  56. return;
  57. }
  58. $parent_id = intval($item['parent']);
  59. $condition = ['id' => [$target_id, $parent_id], 'visible' => true, 'moderated' => false];
  60. $params = ['order' => ['id']];
  61. $itemdata = Model\Item::select([], $condition, $params);
  62. while ($item = Model\Item::fetch($itemdata)) {
  63. if ($item['id'] == $parent_id) {
  64. $parent = $item;
  65. }
  66. if ($item['id'] == $target_id) {
  67. $target_item = $item;
  68. }
  69. $items[] = $item;
  70. }
  71. DBA::close($itemdata);
  72. if (empty($target_item)) {
  73. Logger::log('Item ' . $target_id . "wasn't found. Quitting here.");
  74. return;
  75. }
  76. if (empty($parent)) {
  77. Logger::log('Parent ' . $parent_id . ' for item ' . $target_id . "wasn't found. Quitting here.");
  78. return;
  79. }
  80. if (!empty($target_item['contact-uid'])) {
  81. $uid = $target_item['contact-uid'];
  82. } elseif (!empty($target_item['uid'])) {
  83. $uid = $target_item['uid'];
  84. } else {
  85. Logger::log('Only public users for item ' . $target_id, Logger::DEBUG);
  86. return;
  87. }
  88. // avoid race condition with deleting entries
  89. if ($items[0]['deleted']) {
  90. foreach ($items as $item) {
  91. $item['deleted'] = 1;
  92. }
  93. }
  94. // When commenting too fast after delivery, a post wasn't recognized as top level post.
  95. // The count then showed more than one entry. The additional check should help.
  96. // The check for the "count" should be superfluous, but I'm not totally sure by now, so we keep it.
  97. if ((($parent['id'] == $target_id) || (count($items) == 1)) && ($parent['uri'] === $parent['parent-uri'])) {
  98. Logger::log('Top level post');
  99. $top_level = true;
  100. }
  101. // This is IMPORTANT!!!!
  102. // We will only send a "notify owner to relay" or followup message if the referenced post
  103. // originated on our system by virtue of having our hostname somewhere
  104. // in the URI, AND it was a comment (not top_level) AND the parent originated elsewhere.
  105. // if $parent['wall'] == 1 we will already have the parent message in our array
  106. // and we will relay the whole lot.
  107. $localhost = self::getApp()->getHostName();
  108. if (strpos($localhost, ':')) {
  109. $localhost = substr($localhost, 0, strpos($localhost, ':'));
  110. }
  111. /**
  112. *
  113. * Be VERY CAREFUL if you make any changes to the following line. Seemingly innocuous changes
  114. * have been known to cause runaway conditions which affected several servers, along with
  115. * permissions issues.
  116. *
  117. */
  118. if (!$top_level && ($parent['wall'] == 0) && stristr($target_item['uri'], $localhost)) {
  119. Logger::log('Followup ' . $target_item["guid"], Logger::DEBUG);
  120. // local followup to remote post
  121. $followup = true;
  122. }
  123. if (empty($parent['allow_cid'])
  124. && empty($parent['allow_gid'])
  125. && empty($parent['deny_cid'])
  126. && empty($parent['deny_gid'])
  127. && !$parent["private"]) {
  128. $public_message = true;
  129. }
  130. }
  131. if (empty($items)) {
  132. Logger::log('No delivery data for ' . $cmd . ' - Item ID: ' .$target_id . ' - Contact ID: ' . $contact_id);
  133. }
  134. $owner = Model\User::getOwnerDataById($uid);
  135. if (!DBA::isResult($owner)) {
  136. return;
  137. }
  138. // We don't deliver our items to blocked or pending contacts, and not to ourselves either
  139. $contact = DBA::selectFirst('contact', [],
  140. ['id' => $contact_id, 'blocked' => false, 'pending' => false, 'self' => false]
  141. );
  142. if (!DBA::isResult($contact)) {
  143. return;
  144. }
  145. if (Network::isUrlBlocked($contact['url'])) {
  146. return;
  147. }
  148. // Transmit via Diaspora if the thread had started as Diaspora post
  149. // This is done since the uri wouldn't match (Diaspora doesn't transmit it)
  150. if (isset($parent) && ($parent['network'] == Protocol::DIASPORA) && ($contact['network'] == Protocol::DFRN)) {
  151. $contact['network'] = Protocol::DIASPORA;
  152. }
  153. Logger::log("Delivering " . $cmd . " followup=$followup - via network " . $contact['network']);
  154. switch ($contact['network']) {
  155. case Protocol::DFRN:
  156. self::deliverDFRN($cmd, $contact, $owner, $items, $target_item, $public_message, $top_level, $followup);
  157. break;
  158. case Protocol::DIASPORA:
  159. self::deliverDiaspora($cmd, $contact, $owner, $items, $target_item, $public_message, $top_level, $followup);
  160. break;
  161. case Protocol::OSTATUS:
  162. // Do not send to otatus if we are not configured to send to public networks
  163. if ($owner['prvnets']) {
  164. break;
  165. }
  166. if (Config::get('system','ostatus_disabled') || Config::get('system','dfrn_only')) {
  167. break;
  168. }
  169. // There is currently no code here to distribute anything to OStatus.
  170. // This is done in "notifier.php" (See "url_recipients" and "push_notify")
  171. break;
  172. case Protocol::MAIL:
  173. self::deliverMail($cmd, $contact, $owner, $target_item);
  174. break;
  175. default:
  176. break;
  177. }
  178. return;
  179. }
  180. /**
  181. * @brief Deliver content via DFRN
  182. *
  183. * @param string $cmd Command
  184. * @param array $contact Contact record of the receiver
  185. * @param array $owner Owner record of the sender
  186. * @param array $items Item record of the content and the parent
  187. * @param array $target_item Item record of the content
  188. * @param boolean $public_message Is the content public?
  189. * @param boolean $top_level Is it a thread starter?
  190. * @param boolean $followup Is it an answer to a remote post?
  191. * @throws \Friendica\Network\HTTPException\InternalServerErrorException
  192. * @throws \ImagickException
  193. */
  194. private static function deliverDFRN($cmd, $contact, $owner, $items, $target_item, $public_message, $top_level, $followup)
  195. {
  196. Logger::log('Deliver ' . defaults($target_item, 'guid', $target_item['id']) . ' via DFRN to ' . (empty($contact['addr']) ? $contact['url'] : $contact['addr']));
  197. if ($cmd == self::MAIL) {
  198. $item = $target_item;
  199. $item['body'] = Model\Item::fixPrivatePhotos($item['body'], $owner['uid'], null, $item['contact-id']);
  200. $atom = DFRN::mail($item, $owner);
  201. } elseif ($cmd == self::SUGGESTION) {
  202. $item = $target_item;
  203. $atom = DFRN::fsuggest($item, $owner);
  204. DBA::delete('fsuggest', ['id' => $item['id']]);
  205. } elseif ($cmd == self::RELOCATION) {
  206. $atom = DFRN::relocate($owner, $owner['uid']);
  207. } elseif ($followup) {
  208. $msgitems = [$target_item];
  209. $atom = DFRN::entries($msgitems, $owner);
  210. } else {
  211. $msgitems = [];
  212. foreach ($items as $item) {
  213. // Only add the parent when we don't delete other items.
  214. if (($target_item['id'] == $item['id']) || ($cmd != self::DELETION)) {
  215. $item["entry:comment-allow"] = true;
  216. $item["entry:cid"] = ($top_level ? $contact['id'] : 0);
  217. $msgitems[] = $item;
  218. }
  219. }
  220. $atom = DFRN::entries($msgitems, $owner);
  221. }
  222. Logger::log('Notifier entry: ' . $contact["url"] . ' ' . defaults($target_item, 'guid', $target_item['id']) . ' entry: ' . $atom, Logger::DATA);
  223. $basepath = implode('/', array_slice(explode('/', $contact['url']), 0, 3));
  224. // perform local delivery if we are on the same site
  225. if (Strings::compareLink($basepath, System::baseUrl())) {
  226. $condition = ['nurl' => Strings::normaliseLink($contact['url']), 'self' => true];
  227. $target_self = DBA::selectFirst('contact', ['uid'], $condition);
  228. if (!DBA::isResult($target_self)) {
  229. return;
  230. }
  231. $target_uid = $target_self['uid'];
  232. // Check if the user has got this contact
  233. $cid = Model\Contact::getIdForURL($owner['url'], $target_uid);
  234. if (!$cid) {
  235. // Otherwise there should be a public contact
  236. $cid = Model\Contact::getIdForURL($owner['url']);
  237. if (!$cid) {
  238. return;
  239. }
  240. }
  241. $target_importer = DFRN::getImporter($cid, $target_uid);
  242. if (empty($target_importer)) {
  243. // This should never happen
  244. return;
  245. }
  246. DFRN::import($atom, $target_importer);
  247. return;
  248. }
  249. // We don't have a relationship with contacts on a public post.
  250. // Se we transmit with the new method and via Diaspora as a fallback
  251. if (!empty($items) && (($items[0]['uid'] == 0) || ($contact['uid'] == 0))) {
  252. // Transmit in public if it's a relay post
  253. $public_dfrn = ($contact['contact-type'] == Model\Contact::TYPE_RELAY);
  254. $deliver_status = DFRN::transmit($owner, $contact, $atom, $public_dfrn);
  255. // We never spool failed relay deliveries
  256. if ($public_dfrn) {
  257. Logger::log('Relay delivery to ' . $contact["url"] . ' with guid ' . $target_item["guid"] . ' returns ' . $deliver_status);
  258. return;
  259. }
  260. if (($deliver_status < 200) || ($deliver_status > 299)) {
  261. // Transmit via Diaspora if not possible via Friendica
  262. self::deliverDiaspora($cmd, $contact, $owner, $items, $target_item, $public_message, $top_level, $followup);
  263. return;
  264. }
  265. } elseif ($cmd != self::RELOCATION) {
  266. $deliver_status = DFRN::deliver($owner, $contact, $atom);
  267. } else {
  268. $deliver_status = DFRN::deliver($owner, $contact, $atom, false, true);
  269. }
  270. Logger::info('DFRN Delivery', ['cmd' => $cmd, 'url' => $contact['url'], 'guid' => defaults($target_item, 'guid', $target_item['id']), 'return' => $deliver_status]);
  271. if (($deliver_status >= 200) && ($deliver_status <= 299)) {
  272. // We successfully delivered a message, the contact is alive
  273. Model\Contact::unmarkForArchival($contact);
  274. if (in_array($cmd, [Delivery::POST, Delivery::POKE])) {
  275. Model\ItemDeliveryData::incrementQueueDone($target_item['id']);
  276. }
  277. } else {
  278. // The message could not be delivered. We mark the contact as "dead"
  279. Model\Contact::markForArchival($contact);
  280. Logger::info('Delivery failed: defer message', ['id' => defaults($target_item, 'guid', $target_item['id'])]);
  281. Worker::defer();
  282. }
  283. }
  284. /**
  285. * @brief Deliver content via Diaspora
  286. *
  287. * @param string $cmd Command
  288. * @param array $contact Contact record of the receiver
  289. * @param array $owner Owner record of the sender
  290. * @param array $items Item record of the content and the parent
  291. * @param array $target_item Item record of the content
  292. * @param boolean $public_message Is the content public?
  293. * @param boolean $top_level Is it a thread starter?
  294. * @param boolean $followup Is it an answer to a remote post?
  295. * @throws \Friendica\Network\HTTPException\InternalServerErrorException
  296. * @throws \ImagickException
  297. */
  298. private static function deliverDiaspora($cmd, $contact, $owner, $items, $target_item, $public_message, $top_level, $followup)
  299. {
  300. // We don't treat Forum posts as "wall-to-wall" to be able to post them via Diaspora
  301. $walltowall = $top_level && ($owner['id'] != $items[0]['contact-id']) & ($owner['account-type'] != Model\User::ACCOUNT_TYPE_COMMUNITY);
  302. if ($public_message) {
  303. $loc = 'public batch ' . $contact['batch'];
  304. } else {
  305. $loc = $contact['addr'];
  306. }
  307. Logger::log('Deliver ' . defaults($target_item, 'guid', $target_item['id']) . ' via Diaspora to ' . $loc);
  308. if (Config::get('system', 'dfrn_only') || !Config::get('system', 'diaspora_enabled')) {
  309. return;
  310. }
  311. if ($cmd == self::MAIL) {
  312. Diaspora::sendMail($target_item, $owner, $contact);
  313. return;
  314. }
  315. if ($cmd == self::SUGGESTION) {
  316. return;
  317. }
  318. if (!$contact['pubkey'] && !$public_message) {
  319. return;
  320. }
  321. if ($cmd == self::RELOCATION) {
  322. $deliver_status = Diaspora::sendAccountMigration($owner, $contact, $owner['uid']);
  323. } elseif ($target_item['deleted'] && (($target_item['uri'] === $target_item['parent-uri']) || $followup)) {
  324. // top-level retraction
  325. Logger::log('diaspora retract: ' . $loc);
  326. $deliver_status = Diaspora::sendRetraction($target_item, $owner, $contact, $public_message);
  327. } elseif ($followup) {
  328. // send comments and likes to owner to relay
  329. Logger::log('diaspora followup: ' . $loc);
  330. $deliver_status = Diaspora::sendFollowup($target_item, $owner, $contact, $public_message);
  331. } elseif ($target_item['uri'] !== $target_item['parent-uri']) {
  332. // we are the relay - send comments, likes and relayable_retractions to our conversants
  333. Logger::log('diaspora relay: ' . $loc);
  334. $deliver_status = Diaspora::sendRelay($target_item, $owner, $contact, $public_message);
  335. } elseif ($top_level && !$walltowall) {
  336. // currently no workable solution for sending walltowall
  337. Logger::log('diaspora status: ' . $loc);
  338. $deliver_status = Diaspora::sendStatus($target_item, $owner, $contact, $public_message);
  339. } else {
  340. Logger::log('Unknown mode ' . $cmd . ' for ' . $loc);
  341. return;
  342. }
  343. if (($deliver_status >= 200) && ($deliver_status <= 299)) {
  344. // We successfully delivered a message, the contact is alive
  345. Model\Contact::unmarkForArchival($contact);
  346. if (in_array($cmd, [Delivery::POST, Delivery::POKE])) {
  347. Model\ItemDeliveryData::incrementQueueDone($target_item['id']);
  348. }
  349. } else {
  350. // The message could not be delivered. We mark the contact as "dead"
  351. Model\Contact::markForArchival($contact);
  352. if (empty($contact['contact-type']) || ($contact['contact-type'] != Model\Contact::TYPE_RELAY)) {
  353. Logger::info('Delivery failed: defer message', ['id' => defaults($target_item, 'guid', $target_item['id'])]);
  354. // defer message for redelivery
  355. Worker::defer();
  356. } elseif (in_array($cmd, [Delivery::POST, Delivery::POKE])) {
  357. Model\ItemDeliveryData::incrementQueueDone($target_item['id']);
  358. }
  359. }
  360. }
  361. /**
  362. * @brief Deliver content via mail
  363. *
  364. * @param string $cmd Command
  365. * @param array $contact Contact record of the receiver
  366. * @param array $owner Owner record of the sender
  367. * @param array $target_item Item record of the content
  368. * @throws \Friendica\Network\HTTPException\InternalServerErrorException
  369. * @throws \ImagickException
  370. */
  371. private static function deliverMail($cmd, $contact, $owner, $target_item)
  372. {
  373. if (Config::get('system','dfrn_only')) {
  374. return;
  375. }
  376. // WARNING: does not currently convert to RFC2047 header encodings, etc.
  377. $addr = $contact['addr'];
  378. if (!strlen($addr)) {
  379. return;
  380. }
  381. if (!in_array($cmd, [self::POST, self::POKE])) {
  382. return;
  383. }
  384. $local_user = DBA::selectFirst('user', [], ['uid' => $owner['uid']]);
  385. if (!DBA::isResult($local_user)) {
  386. return;
  387. }
  388. Logger::log('Deliver ' . $target_item["guid"] . ' via mail to ' . $contact['addr']);
  389. $reply_to = '';
  390. $mailacct = DBA::selectFirst('mailacct', ['reply_to'], ['uid' => $owner['uid']]);
  391. if (DBA::isResult($mailacct) && !empty($mailacct['reply_to'])) {
  392. $reply_to = $mailacct['reply_to'];
  393. }
  394. $subject = ($target_item['title'] ? Email::encodeHeader($target_item['title'], 'UTF-8') : L10n::t("\x28no subject\x29"));
  395. // only expose our real email address to true friends
  396. if (($contact['rel'] == Model\Contact::FRIEND) && !$contact['blocked']) {
  397. if ($reply_to) {
  398. $headers = 'From: ' . Email::encodeHeader($local_user['username'],'UTF-8') . ' <' . $reply_to.'>' . "\n";
  399. $headers .= 'Sender: ' . $local_user['email'] . "\n";
  400. } else {
  401. $headers = 'From: ' . Email::encodeHeader($local_user['username'],'UTF-8').' <' . $local_user['email'] . '>' . "\n";
  402. }
  403. } else {
  404. $headers = 'From: '. Email::encodeHeader($local_user['username'], 'UTF-8') . ' <noreply@' . self::getApp()->getHostName() . '>' . "\n";
  405. }
  406. $headers .= 'Message-Id: <' . Email::iri2msgid($target_item['uri']) . '>' . "\n";
  407. if ($target_item['uri'] !== $target_item['parent-uri']) {
  408. $headers .= "References: <" . Email::iri2msgid($target_item["parent-uri"]) . ">";
  409. // If Threading is enabled, write down the correct parent
  410. if (($target_item["thr-parent"] != "") && ($target_item["thr-parent"] != $target_item["parent-uri"])) {
  411. $headers .= " <".Email::iri2msgid($target_item["thr-parent"]).">";
  412. }
  413. $headers .= "\n";
  414. if (empty($target_item['title'])) {
  415. $condition = ['uri' => $target_item['parent-uri'], 'uid' => $owner['uid']];
  416. $title = Model\Item::selectFirst(['title'], $condition);
  417. if (DBA::isResult($title) && ($title['title'] != '')) {
  418. $subject = $title['title'];
  419. } else {
  420. $condition = ['parent-uri' => $target_item['parent-uri'], 'uid' => $owner['uid']];
  421. $title = Model\Item::selectFirst(['title'], $condition);
  422. if (DBA::isResult($title) && ($title['title'] != '')) {
  423. $subject = $title['title'];
  424. }
  425. }
  426. }
  427. if (strncasecmp($subject, 'RE:', 3)) {
  428. $subject = 'Re: ' . $subject;
  429. }
  430. }
  431. Email::send($addr, $subject, $headers, $target_item);
  432. }
  433. }