2017-11-19 19:59:55 +01:00
< ? php
/**
2023-01-01 16:15:16 +01:00
* @ copyright Copyright ( C ) 2010 - 2023 , the Friendica project
2020-02-09 16:18:46 +01:00
*
* @ license GNU AGPL version 3 or any later version
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation , either version 3 of the
* License , or ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU Affero General Public License for more details .
*
* You should have received a copy of the GNU Affero General Public License
* along with this program . If not , see < https :// www . gnu . org / licenses />.
*
2017-11-19 19:59:55 +01:00
*/
2020-02-09 16:18:46 +01:00
2017-11-19 19:59:55 +01:00
namespace Friendica\Worker ;
2018-10-21 07:15:02 +02:00
use Friendica\Core\Hook ;
2018-10-29 22:20:46 +01:00
use Friendica\Core\Logger ;
2018-08-11 22:40:44 +02:00
use Friendica\Core\Protocol ;
2017-11-19 19:59:55 +01:00
use Friendica\Core\Worker ;
2018-07-20 14:19:26 +02:00
use Friendica\Database\DBA ;
2019-12-15 22:34:11 +01:00
use Friendica\DI ;
2017-12-07 15:04:24 +01:00
use Friendica\Model\Contact ;
2018-10-21 07:15:02 +02:00
use Friendica\Model\Conversation ;
2017-12-09 19:45:17 +01:00
use Friendica\Model\Group ;
2022-12-19 10:30:56 +01:00
use Friendica\Model\GServer ;
2018-06-17 23:35:33 +02:00
use Friendica\Model\Item ;
2020-05-02 21:34:02 +02:00
use Friendica\Model\Post ;
2018-05-18 01:30:49 +02:00
use Friendica\Model\PushSubscriber ;
2020-05-05 07:11:59 +02:00
use Friendica\Model\Tag ;
2018-07-20 04:15:21 +02:00
use Friendica\Model\User ;
2020-08-09 20:42:25 +02:00
use Friendica\Protocol\Activity ;
2018-09-17 23:13:08 +02:00
use Friendica\Protocol\ActivityPub ;
2017-11-19 19:59:55 +01:00
use Friendica\Protocol\Diaspora ;
2022-12-30 22:20:28 +01:00
use Friendica\Protocol\Delivery ;
2017-11-19 19:59:55 +01:00
use Friendica\Protocol\OStatus ;
2017-12-02 15:32:45 +01:00
use Friendica\Protocol\Salmon ;
2023-01-02 00:37:17 +01:00
use Friendica\Util\LDSignature ;
2021-05-26 11:24:37 +02:00
use Friendica\Util\Network ;
2021-06-06 21:28:47 +02:00
use Friendica\Util\Strings ;
2017-11-19 19:59:55 +01:00
/*
* The notifier is typically called with :
*
* Worker :: add ( PRIORITY_HIGH , " Notifier " , COMMAND , ITEM_ID );
*
2019-06-10 16:19:24 +02:00
* where COMMAND is one of the constants that are defined in Worker / Delivery . php
2017-11-19 19:59:55 +01:00
* and ITEM_ID is the id of the item in the database that needs to be sent to others .
*/
2018-07-10 04:39:59 +02:00
class Notifier
{
2021-02-14 15:24:48 +01:00
public static function execute ( string $cmd , int $post_uriid , int $sender_uid = 0 )
2018-07-10 04:39:59 +02:00
{
2019-12-15 22:34:11 +01:00
$a = DI :: app ();
2017-11-19 19:59:55 +01:00
2021-02-14 15:24:48 +01:00
Logger :: info ( 'Invoked' , [ 'cmd' => $cmd , 'target' => $post_uriid , 'sender_uid' => $sender_uid ]);
2021-02-15 08:44:51 +01:00
$target_id = $post_uriid ;
2017-11-19 19:59:55 +01:00
$top_level = false ;
2018-01-15 14:05:12 +01:00
$recipients = [];
$url_recipients = [];
2017-11-19 19:59:55 +01:00
2018-12-05 06:35:52 +01:00
$delivery_contacts_stmt = null ;
2018-12-05 05:49:48 +01:00
$target_item = [];
2019-11-22 09:01:23 +01:00
$parent = [];
$thr_parent = [];
2018-12-05 05:49:48 +01:00
$items = [];
2018-12-07 06:52:14 +01:00
$delivery_queue_count = 0 ;
2021-01-10 16:08:40 +01:00
$ap_contacts = [];
2017-11-19 19:59:55 +01:00
2018-05-01 08:37:12 +02:00
if ( $cmd == Delivery :: MAIL ) {
2018-12-05 05:49:48 +01:00
$message = DBA :: selectFirst ( 'mail' , [ 'uid' , 'contact-id' ], [ 'id' => $target_id ]);
2018-07-21 14:46:04 +02:00
if ( ! DBA :: isResult ( $message )) {
2017-11-19 19:59:55 +01:00
return ;
}
2018-05-01 08:37:12 +02:00
$uid = $message [ 'uid' ];
$recipients [] = $message [ 'contact-id' ];
2019-05-14 19:50:45 +02:00
2022-06-20 21:21:32 +02:00
$mail = ActivityPub\Transmitter :: getItemArrayFromMail ( $target_id );
2019-05-18 11:09:13 +02:00
$inboxes = ActivityPub\Transmitter :: fetchTargetInboxes ( $mail , $uid , true );
2020-12-15 05:33:14 +01:00
foreach ( $inboxes as $inbox => $receivers ) {
2021-01-10 16:08:40 +01:00
$ap_contacts = array_merge ( $ap_contacts , $receivers );
2019-10-06 23:59:23 +02:00
Logger :: info ( 'Delivery via ActivityPub' , [ 'cmd' => $cmd , 'target' => $target_id , 'inbox' => $inbox ]);
2022-10-17 07:49:55 +02:00
Worker :: add ([ 'priority' => Worker :: PRIORITY_HIGH , 'created' => $a -> getQueueValue ( 'created' ), 'dont_fork' => true ],
2021-02-14 15:24:48 +01:00
'APDelivery' , $cmd , $target_id , $inbox , $uid , $receivers , $post_uriid );
2019-05-14 19:50:45 +02:00
}
2018-05-01 08:37:12 +02:00
} elseif ( $cmd == Delivery :: SUGGESTION ) {
2021-10-21 22:57:13 +02:00
$suggest = DI :: fsuggest () -> selectOneById ( $target_id );
2020-01-31 23:50:46 +01:00
$uid = $suggest -> uid ;
$recipients [] = $suggest -> cid ;
2018-05-01 08:37:12 +02:00
} elseif ( $cmd == Delivery :: REMOVAL ) {
2021-07-25 00:08:33 +02:00
return self :: notifySelfRemoval ( $target_id , $a -> getQueueValue ( 'priority' ), $a -> getQueueValue ( 'created' ));
2018-05-01 08:37:12 +02:00
} elseif ( $cmd == Delivery :: RELOCATION ) {
2018-12-05 05:49:48 +01:00
$uid = $target_id ;
2017-11-19 19:59:55 +01:00
2018-12-05 06:35:52 +01:00
$condition = [ 'uid' => $target_id , 'self' => false , 'network' => [ Protocol :: DFRN , Protocol :: DIASPORA ]];
2022-12-19 20:41:04 +01:00
$delivery_contacts_stmt = DBA :: select ( 'contact' , [ 'id' , 'url' , 'addr' , 'network' , 'protocol' , 'baseurl' , 'gsid' , 'batch' ], $condition );
2017-11-19 19:59:55 +01:00
} else {
2021-02-19 07:30:38 +01:00
$post = Post :: selectFirst ([ 'id' ], [ 'uri-id' => $post_uriid , 'uid' => $sender_uid ]);
if ( ! DBA :: isResult ( $post )) {
Logger :: warning ( 'Post not found' , [ 'uri-id' => $post_uriid , 'uid' => $sender_uid ]);
return ;
}
$target_id = $post [ 'id' ];
2017-11-19 19:59:55 +01:00
// find ancestors
2021-02-13 20:56:03 +01:00
$condition = [ 'id' => $target_id , 'visible' => true ];
$target_item = Post :: selectFirst ( Item :: DELIVER_FIELDLIST , $condition );
2017-11-19 19:59:55 +01:00
2018-07-21 14:46:04 +02:00
if ( ! DBA :: isResult ( $target_item ) || ! intval ( $target_item [ 'parent' ])) {
2019-10-06 23:59:23 +02:00
Logger :: info ( 'No target item' , [ 'cmd' => $cmd , 'target' => $target_id ]);
2017-11-19 19:59:55 +01:00
return ;
}
2019-01-18 00:06:27 +01:00
if ( ! empty ( $target_item [ 'contact-uid' ])) {
$uid = $target_item [ 'contact-uid' ];
} elseif ( ! empty ( $target_item [ 'uid' ])) {
$uid = $target_item [ 'uid' ];
} else {
2019-10-06 23:59:23 +02:00
Logger :: info ( 'Only public users, quitting' , [ 'target' => $target_id ]);
2019-01-18 00:06:27 +01:00
return ;
}
2021-02-13 20:56:03 +01:00
$condition = [ 'parent' => $target_item [ 'parent' ], 'visible' => true ];
2018-06-17 23:35:33 +02:00
$params = [ 'order' => [ 'id' ]];
2021-02-13 20:56:03 +01:00
$items_stmt = Post :: select ( Item :: DELIVER_FIELDLIST , $condition , $params );
2018-12-05 05:49:48 +01:00
if ( ! DBA :: isResult ( $items_stmt )) {
2019-10-06 23:59:23 +02:00
Logger :: info ( 'No item found' , [ 'cmd' => $cmd , 'target' => $target_id ]);
2017-11-19 19:59:55 +01:00
return ;
}
2021-01-16 23:37:27 +01:00
$items = Post :: toArray ( $items_stmt );
2018-06-17 23:35:33 +02:00
2017-11-19 19:59:55 +01:00
// avoid race condition with deleting entries
if ( $items [ 0 ][ 'deleted' ]) {
foreach ( $items as $item ) {
$item [ 'deleted' ] = 1 ;
}
}
2022-09-12 23:12:11 +02:00
$top_level = $target_item [ 'gravity' ] == Item :: GRAVITY_PARENT ;
2017-11-19 19:59:55 +01:00
}
2017-12-20 00:12:37 +01:00
$owner = User :: getOwnerDataById ( $uid );
if ( ! $owner ) {
2019-10-06 23:59:23 +02:00
Logger :: info ( 'Owner not found' , [ 'cmd' => $cmd , 'target' => $target_id ]);
2017-11-19 19:59:55 +01:00
return ;
}
// Should the post be transmitted to Diaspora?
2022-02-15 08:08:02 +01:00
$diaspora_delivery = ( $owner [ 'account-type' ] != User :: ACCOUNT_TYPE_COMMUNITY );
2017-11-19 19:59:55 +01:00
// If this is a public conversation, notify the feed hub
$public_message = true ;
2020-03-02 08:57:23 +01:00
$unlisted = false ;
2017-11-19 19:59:55 +01:00
// Do a PuSH
$push_notify = false ;
// Deliver directly to a forum, don't PuSH
$direct_forum_delivery = false ;
2018-02-14 06:05:00 +01:00
$followup = false ;
$recipients_followup = [];
2017-11-19 19:59:55 +01:00
2018-12-05 05:49:48 +01:00
if ( ! empty ( $target_item ) && ! empty ( $items )) {
2017-11-19 19:59:55 +01:00
$parent = $items [ 0 ];
2020-06-27 17:10:06 +02:00
$fields = [ 'network' , 'author-id' , 'author-link' , 'author-network' , 'owner-id' ];
2022-09-15 01:23:38 +02:00
$condition = [ 'uri' => $target_item [ 'thr-parent' ], 'uid' => $target_item [ 'uid' ]];
2021-01-16 05:14:58 +01:00
$thr_parent = Post :: selectFirst ( $fields , $condition );
2019-10-30 07:50:20 +01:00
if ( empty ( $thr_parent )) {
$thr_parent = $parent ;
}
2017-11-19 19:59:55 +01:00
2021-09-04 06:51:20 +02:00
Logger :: info ( 'Got post' , [ 'guid' => $target_item [ 'guid' ], 'uri-id' => $target_item [ 'uri-id' ], 'network' => $target_item [ 'network' ], 'parent-network' => $parent [ 'network' ], 'thread-parent-network' => $thr_parent [ 'network' ]]);
2017-11-19 19:59:55 +01:00
2019-10-06 23:59:23 +02:00
if ( ! self :: isRemovalActivity ( $cmd , $owner , Protocol :: ACTIVITYPUB )) {
2021-07-25 00:08:33 +02:00
$apdelivery = self :: activityPubDelivery ( $cmd , $target_item , $parent , $thr_parent , $a -> getQueueValue ( 'priority' ), $a -> getQueueValue ( 'created' ), $owner );
2021-01-10 16:08:40 +01:00
$ap_contacts = $apdelivery [ 'contacts' ];
$delivery_queue_count += $apdelivery [ 'count' ];
2019-10-06 23:59:23 +02:00
}
2019-07-28 21:13:17 +02:00
// Only deliver threaded replies (comment to a comment) to Diaspora
// when the original comment author does support the Diaspora protocol.
2020-09-18 04:47:37 +02:00
if ( $thr_parent [ 'author-link' ] && $target_item [ 'parent-uri' ] != $target_item [ 'thr-parent' ]) {
2019-07-28 21:13:17 +02:00
$diaspora_delivery = Diaspora :: isSupportedByContactUrl ( $thr_parent [ 'author-link' ]);
2022-12-10 13:08:55 +01:00
if ( $diaspora_delivery && empty ( $target_item [ 'signed_text' ])) {
2022-10-03 12:40:16 +02:00
Logger :: debug ( 'Post has got no Diaspora signature, so there will be no Diaspora delivery' , [ 'guid' => $target_item [ 'guid' ], 'uri-id' => $target_item [ 'uri-id' ]]);
$diaspora_delivery = false ;
}
2019-07-28 21:13:17 +02:00
Logger :: info ( 'Threaded comment' , [ 'diaspora_delivery' => ( int ) $diaspora_delivery ]);
}
2020-03-02 08:57:23 +01:00
$unlisted = $target_item [ 'private' ] == Item :: UNLISTED ;
2017-11-19 19:59:55 +01:00
// This is IMPORTANT!!!!
// We will only send a "notify owner to relay" or followup message if the referenced post
// originated on our system by virtue of having our hostname somewhere
// in the URI, AND it was a comment (not top_level) AND the parent originated elsewhere.
// if $parent['wall'] == 1 we will already have the parent message in our array
// and we will relay the whole lot.
2023-02-18 20:57:30 +01:00
$localhost = str_replace ( 'www.' , '' , DI :: baseUrl () -> getHost ());
2017-11-19 19:59:55 +01:00
if ( strpos ( $localhost , ':' )) {
$localhost = substr ( $localhost , 0 , strpos ( $localhost , ':' ));
}
/**
*
* Be VERY CAREFUL if you make any changes to the following several lines . Seemingly innocuous changes
* have been known to cause runaway conditions which affected several servers , along with
* permissions issues .
*
*/
$relay_to_owner = false ;
2018-01-18 00:22:01 +01:00
if ( ! $top_level && ( $parent [ 'wall' ] == 0 ) && ( stristr ( $target_item [ 'uri' ], $localhost ))) {
2017-11-19 19:59:55 +01:00
$relay_to_owner = true ;
}
// until the 'origin' flag has been in use for several months
// we will just use it as a fallback test
// later we will be able to use it as the primary test of whether or not to relay.
2017-12-20 00:12:37 +01:00
if ( ! $target_item [ 'origin' ]) {
2017-11-19 19:59:55 +01:00
$relay_to_owner = false ;
}
if ( $parent [ 'origin' ]) {
$relay_to_owner = false ;
}
// Special treatment for forum posts
2022-02-16 23:56:55 +01:00
if ( Item :: isForumPost ( $target_item [ 'uri-id' ])) {
2018-09-01 05:23:12 +02:00
$relay_to_owner = true ;
$direct_forum_delivery = true ;
}
2017-11-19 19:59:55 +01:00
2018-09-01 05:23:12 +02:00
// Avoid that comments in a forum thread are sent to OStatus
2022-02-16 23:56:55 +01:00
if ( Item :: isForumPost ( $parent [ 'uri-id' ])) {
2018-09-01 05:23:12 +02:00
$direct_forum_delivery = true ;
2017-11-19 19:59:55 +01:00
}
2018-09-01 05:23:12 +02:00
2021-06-06 21:28:47 +02:00
$exclusive_delivery = false ;
$exclusive_targets = Tag :: getByURIId ( $parent [ 'uri-id' ], [ Tag :: EXCLUSIVE_MENTION ]);
if ( ! empty ( $exclusive_targets )) {
$exclusive_delivery = true ;
Logger :: info ( 'Possible Exclusively delivering' , [ 'uid' => $target_item [ 'uid' ], 'guid' => $target_item [ 'guid' ], 'uri-id' => $target_item [ 'uri-id' ]]);
foreach ( $exclusive_targets as $target ) {
if ( Strings :: compareLink ( $owner [ 'url' ], $target [ 'url' ])) {
$exclusive_delivery = false ;
Logger :: info ( 'False Exclusively delivering' , [ 'uid' => $target_item [ 'uid' ], 'guid' => $target_item [ 'guid' ], 'uri-id' => $target_item [ 'uri-id' ], 'url' => $target [ 'url' ]]);
}
}
}
2017-11-19 19:59:55 +01:00
if ( $relay_to_owner ) {
// local followup to remote post
$followup = true ;
$public_message = false ; // not public
2018-01-15 14:05:12 +01:00
$recipients = [ $parent [ 'contact-id' ]];
$recipients_followup = [ $parent [ 'contact-id' ]];
2017-11-19 19:59:55 +01:00
2019-10-06 23:59:23 +02:00
Logger :: info ( 'Followup' , [ 'target' => $target_id , 'guid' => $target_item [ 'guid' ], 'to' => $parent [ 'contact-id' ]]);
2017-12-27 22:51:16 +01:00
2020-03-02 08:57:23 +01:00
if (( $target_item [ 'private' ] != Item :: PRIVATE ) &&
2017-11-19 19:59:55 +01:00
( strlen ( $target_item [ 'allow_cid' ] . $target_item [ 'allow_gid' ] .
$target_item [ 'deny_cid' ] . $target_item [ 'deny_gid' ]) == 0 ))
$push_notify = true ;
2018-08-11 22:40:44 +02:00
if (( $thr_parent && ( $thr_parent [ 'network' ] == Protocol :: OSTATUS )) || ( $parent [ 'network' ] == Protocol :: OSTATUS )) {
2017-11-19 19:59:55 +01:00
$push_notify = true ;
2018-08-11 22:40:44 +02:00
if ( $parent [ " network " ] == Protocol :: OSTATUS ) {
2017-11-19 19:59:55 +01:00
// Distribute the message to the DFRN contacts as if this wasn't a followup since OStatus can't relay comments
// Currently it is work at progress
2018-12-05 06:35:52 +01:00
$condition = [ 'uid' => $uid , 'network' => Protocol :: DFRN , 'blocked' => false , 'pending' => false , 'archive' => false ];
$followup_contacts_stmt = DBA :: select ( 'contact' , [ 'id' ], $condition );
while ( $followup_contact = DBA :: fetch ( $followup_contacts_stmt )) {
$recipients_followup [] = $followup_contact [ 'id' ];
2017-11-19 19:59:55 +01:00
}
2018-12-05 06:35:52 +01:00
DBA :: close ( $followup_contacts_stmt );
2017-11-19 19:59:55 +01:00
}
}
if ( $direct_forum_delivery ) {
$push_notify = false ;
}
2021-11-04 00:19:24 +01:00
Logger :: info ( 'Notify ' . $target_item [ " guid " ] . ' via PuSH: ' . ( $push_notify ? " Yes " : " No " ));
2021-06-06 21:28:47 +02:00
} elseif ( $exclusive_delivery ) {
2021-06-05 20:38:21 +02:00
$followup = true ;
2021-06-06 21:28:47 +02:00
foreach ( $exclusive_targets as $target ) {
2021-06-05 20:38:21 +02:00
$cid = Contact :: getIdForURL ( $target [ 'url' ], $uid , false );
if ( $cid ) {
$recipients_followup [] = $cid ;
2021-06-06 21:28:47 +02:00
Logger :: info ( 'Exclusively delivering' , [ 'uid' => $target_item [ 'uid' ], 'guid' => $target_item [ 'guid' ], 'uri-id' => $target_item [ 'uri-id' ], 'url' => $target [ 'url' ]]);
2021-06-05 20:38:21 +02:00
}
}
2017-11-19 19:59:55 +01:00
} else {
$followup = false ;
2019-10-06 23:59:23 +02:00
Logger :: info ( 'Distributing directly' , [ 'target' => $target_id , 'guid' => $target_item [ 'guid' ]]);
2017-11-19 19:59:55 +01:00
// don't send deletions onward for other people's stuff
2017-12-20 00:12:37 +01:00
if ( $target_item [ 'deleted' ] && ! intval ( $target_item [ 'wall' ])) {
2021-11-04 00:19:24 +01:00
Logger :: notice ( 'Ignoring delete notification for non-wall item' );
2017-11-19 19:59:55 +01:00
return ;
}
2017-12-20 00:12:37 +01:00
if ( strlen ( $parent [ 'allow_cid' ])
|| strlen ( $parent [ 'allow_gid' ])
|| strlen ( $parent [ 'deny_cid' ])
|| strlen ( $parent [ 'deny_gid' ])) {
2017-11-19 19:59:55 +01:00
$public_message = false ; // private recipients, not public
}
2019-12-15 23:28:01 +01:00
$aclFormatter = DI :: aclFormatter ();
2019-10-23 00:40:14 +02:00
2019-11-01 14:13:29 +01:00
$allow_people = $aclFormatter -> expand ( $parent [ 'allow_cid' ]);
$allow_groups = Group :: expand ( $uid , $aclFormatter -> expand ( $parent [ 'allow_gid' ]), true );
$deny_people = $aclFormatter -> expand ( $parent [ 'deny_cid' ]);
$deny_groups = Group :: expand ( $uid , $aclFormatter -> expand ( $parent [ 'deny_gid' ]));
2017-11-19 19:59:55 +01:00
foreach ( $items as $item ) {
$recipients [] = $item [ 'contact-id' ];
// pull out additional tagged people to notify (if public message)
2022-11-20 01:10:02 +01:00
if ( $public_message && $item [ 'inform' ]) {
2017-11-19 19:59:55 +01:00
$people = explode ( ',' , $item [ 'inform' ]);
foreach ( $people as $person ) {
if ( substr ( $person , 0 , 4 ) === 'cid:' ) {
$recipients [] = intval ( substr ( $person , 4 ));
} else {
$url_recipients [] = substr ( $person , 4 );
}
}
}
}
2017-12-20 00:12:37 +01:00
if ( count ( $url_recipients )) {
2019-10-06 23:59:23 +02:00
Logger :: notice ( 'Deliver' , [ 'target' => $target_id , 'guid' => $target_item [ 'guid' ], 'recipients' => $url_recipients ]);
2017-12-20 00:12:37 +01:00
}
2017-11-19 19:59:55 +01:00
2018-11-05 00:17:41 +01:00
$recipients = array_unique ( array_merge ( $recipients , $allow_people , $allow_groups ));
$deny = array_unique ( array_merge ( $deny_people , $deny_groups ));
$recipients = array_diff ( $recipients , $deny );
2018-12-06 04:23:24 +01:00
// If this is a public message and pubmail is set on the parent, include all your email contacts
if (
function_exists ( 'imap_open' )
2020-01-19 21:21:13 +01:00
&& ! DI :: config () -> get ( 'system' , 'imap_disabled' )
2018-12-06 04:23:24 +01:00
&& $public_message
&& intval ( $target_item [ 'pubmail' ])
) {
$mail_contacts_stmt = DBA :: select ( 'contact' , [ 'id' ], [ 'uid' => $uid , 'network' => Protocol :: MAIL ]);
while ( $mail_contact = DBA :: fetch ( $mail_contacts_stmt )) {
$recipients [] = $mail_contact [ 'id' ];
}
DBA :: close ( $mail_contacts_stmt );
}
2017-11-19 19:59:55 +01:00
}
// If the thread parent is OStatus then do some magic to distribute the messages.
// We have not only to look at the parent, since it could be a Friendica thread.
2018-08-11 22:40:44 +02:00
if (( $thr_parent && ( $thr_parent [ 'network' ] == Protocol :: OSTATUS )) || ( $parent [ 'network' ] == Protocol :: OSTATUS )) {
2017-11-19 19:59:55 +01:00
$diaspora_delivery = false ;
2022-09-15 01:23:38 +02:00
Logger :: info ( 'Some parent is OStatus for ' . $target_item [ 'guid' ] . ' - Author: ' . $thr_parent [ 'author-id' ] . ' - Owner: ' . $thr_parent [ 'owner-id' ]);
2017-11-19 19:59:55 +01:00
// Send a salmon to the parent author
2018-07-20 14:19:26 +02:00
$probed_contact = DBA :: selectFirst ( 'contact' , [ 'url' , 'notify' ], [ 'id' => $thr_parent [ 'author-id' ]]);
2022-09-15 01:23:38 +02:00
if ( DBA :: isResult ( $probed_contact ) && ! empty ( $probed_contact [ 'notify' ])) {
Logger :: notice ( 'Notify parent author' , [ 'url' => $probed_contact [ 'url' ], 'notify' => $probed_contact [ 'notify' ]]);
$url_recipients [ $probed_contact [ 'notify' ]] = $probed_contact [ 'notify' ];
2017-11-19 19:59:55 +01:00
}
// Send a salmon to the parent owner
2018-07-20 14:19:26 +02:00
$probed_contact = DBA :: selectFirst ( 'contact' , [ 'url' , 'notify' ], [ 'id' => $thr_parent [ 'owner-id' ]]);
2022-09-15 01:23:38 +02:00
if ( DBA :: isResult ( $probed_contact ) && ! empty ( $probed_contact [ 'notify' ])) {
Logger :: notice ( 'Notify parent owner' , [ 'url' => $probed_contact [ 'url' ], 'notify' => $probed_contact [ 'notify' ]]);
$url_recipients [ $probed_contact [ 'notify' ]] = $probed_contact [ 'notify' ];
2017-11-19 19:59:55 +01:00
}
// Send a salmon notification to every person we mentioned in the post
2020-05-05 07:11:59 +02:00
foreach ( Tag :: getByURIId ( $target_item [ 'uri-id' ], [ Tag :: MENTION , Tag :: EXCLUSIVE_MENTION , Tag :: IMPLICIT_MENTION ]) as $tag ) {
2020-07-16 12:22:14 +02:00
$probed_contact = Contact :: getByURL ( $tag [ 'url' ]);
if ( ! empty ( $probed_contact [ 'notify' ])) {
2022-09-15 01:23:38 +02:00
Logger :: notice ( 'Notify mentioned user' , [ 'url' => $probed_contact [ 'url' ], 'notify' => $probed_contact [ 'notify' ]]);
2020-07-16 12:22:14 +02:00
$url_recipients [ $probed_contact [ 'notify' ]] = $probed_contact [ 'notify' ];
2017-11-19 19:59:55 +01:00
}
}
// It only makes sense to distribute answers to OStatus messages to Friendica and OStatus - but not Diaspora
2019-07-28 21:13:17 +02:00
$networks = [ Protocol :: DFRN ];
} elseif ( $diaspora_delivery ) {
$networks = [ Protocol :: DFRN , Protocol :: DIASPORA , Protocol :: MAIL ];
2019-10-06 23:59:23 +02:00
if (( $parent [ 'network' ] == Protocol :: DIASPORA ) || ( $thr_parent [ 'network' ] == Protocol :: DIASPORA )) {
Logger :: info ( 'Add AP contacts' , [ 'target' => $target_id , 'guid' => $target_item [ 'guid' ]]);
$networks [] = Protocol :: ACTIVITYPUB ;
}
2017-11-19 19:59:55 +01:00
} else {
2019-07-28 21:13:17 +02:00
$networks = [ Protocol :: DFRN , Protocol :: MAIL ];
2017-11-19 19:59:55 +01:00
}
} else {
$public_message = false ;
}
2018-12-05 06:35:52 +01:00
if ( empty ( $delivery_contacts_stmt )) {
2018-05-01 08:37:12 +02:00
if ( $followup ) {
$recipients = $recipients_followup ;
}
2020-08-09 20:42:25 +02:00
$condition = [ 'id' => $recipients , 'self' => false , 'uid' => [ 0 , $uid ],
2018-05-01 08:37:12 +02:00
'blocked' => false , 'pending' => false , 'archive' => false ];
if ( ! empty ( $networks )) {
$condition [ 'network' ] = $networks ;
}
2022-12-19 20:41:04 +01:00
$delivery_contacts_stmt = DBA :: select ( 'contact' , [ 'id' , 'addr' , 'url' , 'network' , 'protocol' , 'baseurl' , 'gsid' , 'batch' ], $condition );
2017-11-19 19:59:55 +01:00
}
2018-11-05 00:17:41 +01:00
$conversants = [];
$batch_delivery = false ;
2017-11-19 19:59:55 +01:00
2018-11-05 00:17:41 +01:00
if ( $public_message && ! in_array ( $cmd , [ Delivery :: MAIL , Delivery :: SUGGESTION ]) && ! $followup ) {
2021-06-13 13:15:04 +02:00
$participants = [];
2017-11-19 19:59:55 +01:00
2020-03-02 08:57:23 +01:00
if ( $diaspora_delivery && ! $unlisted ) {
2018-11-05 00:17:41 +01:00
$batch_delivery = true ;
2022-12-19 20:41:04 +01:00
$participants = DBA :: selectToArray ( 'contact' , [ 'batch' , 'network' , 'protocol' , 'baseurl' , 'gsid' , 'id' , 'url' , 'name' ],
2022-01-18 07:35:18 +01:00
[ " `network` = ? AND `batch` != '' AND `uid` = ? AND `rel` != ? AND NOT `blocked` AND NOT `pending` AND NOT `archive` " , Protocol :: DIASPORA , $owner [ 'uid' ], Contact :: SHARING ],
[ 'group_by' => [ 'batch' , 'network' , 'protocol' ]]);
2018-01-12 21:52:43 +01:00
// Fetch the participation list
// The function will ensure that there are no duplicates
2021-06-13 13:15:04 +02:00
$participants = Diaspora :: participantsForThread ( $target_item , $participants );
2017-11-19 19:59:55 +01:00
}
2018-09-17 23:13:08 +02:00
$condition = [ 'network' => Protocol :: DFRN , 'uid' => $owner [ 'uid' ], 'blocked' => false ,
2018-07-25 04:53:46 +02:00
'pending' => false , 'archive' => false , 'rel' => [ Contact :: FOLLOWER , Contact :: FRIEND ]];
2022-12-19 20:41:04 +01:00
$contacts = DBA :: selectToArray ( 'contact' , [ 'id' , 'url' , 'addr' , 'name' , 'network' , 'protocol' , 'baseurl' , 'gsid' ], $condition );
2018-01-12 21:52:43 +01:00
2021-06-13 13:15:04 +02:00
$conversants = array_merge ( $contacts , $participants );
2017-11-19 19:59:55 +01:00
2021-02-14 15:24:48 +01:00
$delivery_queue_count += self :: delivery ( $cmd , $post_uriid , $sender_uid , $target_item , $thr_parent , $owner , $batch_delivery , true , $conversants , $ap_contacts , []);
2019-10-06 23:59:23 +02:00
2021-01-10 16:08:40 +01:00
$push_notify = true ;
}
2019-02-11 21:30:08 +01:00
2021-01-10 16:08:40 +01:00
$contacts = DBA :: toArray ( $delivery_contacts_stmt );
2021-02-14 15:24:48 +01:00
$delivery_queue_count += self :: delivery ( $cmd , $post_uriid , $sender_uid , $target_item , $thr_parent , $owner , $batch_delivery , false , $contacts , $ap_contacts , $conversants );
2019-01-30 20:33:08 +01:00
2021-01-10 16:08:40 +01:00
$delivery_queue_count += self :: deliverOStatus ( $target_id , $target_item , $owner , $url_recipients , $public_message , $push_notify );
2020-06-27 14:18:36 +02:00
2021-01-10 16:08:40 +01:00
if ( ! empty ( $target_item )) {
2021-11-04 00:19:24 +01:00
Logger :: info ( 'Calling hooks for ' . $cmd . ' ' . $target_id );
2018-12-07 06:52:14 +01:00
2021-07-25 00:08:33 +02:00
Hook :: fork ( $a -> getQueueValue ( 'priority' ), 'notifier_normal' , $target_item );
2019-01-19 13:30:16 +01:00
2021-01-10 16:08:40 +01:00
Hook :: callAll ( 'notifier_end' , $target_item );
2019-09-02 05:25:05 +02:00
2021-01-10 16:08:40 +01:00
// Workaround for pure connector posts
2022-08-09 19:21:42 +02:00
if ( $cmd == Delivery :: POST ) {
2021-01-10 16:08:40 +01:00
if ( $delivery_queue_count == 0 ) {
Post\DeliveryData :: incrementQueueDone ( $target_item [ 'uri-id' ]);
$delivery_queue_count = 1 ;
2018-11-05 00:17:41 +01:00
}
2017-11-19 19:59:55 +01:00
2021-01-10 16:08:40 +01:00
Post\DeliveryData :: incrementQueueCount ( $target_item [ 'uri-id' ], $delivery_queue_count );
}
2018-11-05 00:17:41 +01:00
}
2017-11-19 19:59:55 +01:00
2021-01-10 16:08:40 +01:00
return ;
}
/**
* Deliver the message to the contacts
*
2021-05-26 11:24:37 +02:00
* @ param string $cmd
* @ param int $post_uriid
2021-02-14 15:24:48 +01:00
* @ param int $sender_uid
2021-05-26 11:24:37 +02:00
* @ param array $target_item
* @ param array $thr_parent
* @ param array $owner
* @ param bool $batch_delivery
* @ param array $contacts
* @ param array $ap_contacts
* @ param array $conversants
2022-09-15 01:23:38 +02:00
*
* @ return int Count of delivery queue
2021-05-26 11:24:37 +02:00
* @ throws InternalServerErrorException
* @ throws Exception
2021-01-10 16:08:40 +01:00
*/
2022-09-15 01:23:38 +02:00
private static function delivery ( string $cmd , int $post_uriid , int $sender_uid , array $target_item , array $thr_parent , array $owner , bool $batch_delivery , bool $in_batch , array $contacts , array $ap_contacts , array $conversants = []) : int
2021-01-10 16:08:40 +01:00
{
2021-05-26 11:24:37 +02:00
$a = DI :: app ();
2021-01-10 16:08:40 +01:00
$delivery_queue_count = 0 ;
2022-09-16 07:00:06 +02:00
if ( ! empty ( $target_item [ 'verb' ]) && ( $target_item [ 'verb' ] == Activity :: ANNOUNCE )) {
2022-09-03 15:32:41 +02:00
Logger :: notice ( 'Announces are only delivery via ActivityPub' , [ 'cmd' => $cmd , 'id' => $target_item [ 'id' ], 'guid' => $target_item [ 'guid' ], 'uri-id' => $target_item [ 'uri-id' ], 'uri' => $target_item [ 'uri' ]]);
return 0 ;
}
2021-01-10 16:08:40 +01:00
foreach ( $contacts as $contact ) {
2021-05-26 11:24:37 +02:00
// Direct delivery of local contacts
2023-01-27 06:55:45 +01:00
if ( ! in_array ( $cmd , [ Delivery :: RELOCATION , Delivery :: SUGGESTION , Delivery :: MAIL ]) && $target_uid = User :: getIdForURL ( $contact [ 'url' ])) {
if ( $cmd == Delivery :: DELETION ) {
Logger :: info ( 'No need to deliver deletions internally' , [ 'uid' => $target_uid , 'guid' => $target_item [ 'guid' ], 'uri-id' => $target_item [ 'uri-id' ], 'uri' => $target_item [ 'uri' ]]);
continue ;
}
2022-08-04 21:32:36 +02:00
if ( $target_item [ 'origin' ] || ( $target_item [ 'network' ] != Protocol :: ACTIVITYPUB )) {
if ( $target_uid != $target_item [ 'uid' ]) {
$fields = [ 'protocol' => Conversation :: PARCEL_LOCAL_DFRN , 'direction' => Conversation :: PUSH , 'post-reason' => Item :: PR_DIRECT ];
Item :: storeForUserByUriId ( $target_item [ 'uri-id' ], $target_uid , $fields , $target_item [ 'uid' ]);
Logger :: info ( 'Delivered locally' , [ 'cmd' => $cmd , 'id' => $target_item [ 'id' ], 'target' => $target_uid ]);
} else {
Logger :: info ( 'No need to deliver to myself' , [ 'uid' => $target_uid , 'guid' => $target_item [ 'guid' ], 'uri-id' => $target_item [ 'uri-id' ], 'uri' => $target_item [ 'uri' ]]);
}
} else {
Logger :: info ( 'Remote item does not need to be delivered locally' , [ 'guid' => $target_item [ 'guid' ], 'uri-id' => $target_item [ 'uri-id' ], 'uri' => $target_item [ 'uri' ]]);
}
2021-05-26 11:24:37 +02:00
continue ;
2020-01-11 19:25:48 +01:00
}
2021-03-26 21:09:23 +01:00
// Deletions are always sent via DFRN as well.
// This is done until we can perform deletions of foreign comments on our own threads via AP.
if (( $cmd != Delivery :: DELETION ) && in_array ( $contact [ 'id' ], $ap_contacts )) {
2021-02-14 15:24:48 +01:00
Logger :: info ( 'Contact is already delivered via AP, so skip delivery via legacy DFRN/Diaspora' , [ 'target' => $post_uriid , 'uid' => $sender_uid , 'contact' => $contact [ 'url' ]]);
2019-10-06 23:59:23 +02:00
continue ;
}
2019-08-27 21:01:11 +02:00
if ( ! empty ( $contact [ 'id' ]) && Contact :: isArchived ( $contact [ 'id' ])) {
2021-02-14 15:24:48 +01:00
Logger :: info ( 'Contact is archived, so skip delivery' , [ 'target' => $post_uriid , 'uid' => $sender_uid , 'contact' => $contact [ 'url' ]]);
2019-08-27 21:01:11 +02:00
continue ;
}
2019-02-11 21:30:08 +01:00
if ( self :: isRemovalActivity ( $cmd , $owner , $contact [ 'network' ])) {
2021-02-14 15:24:48 +01:00
Logger :: info ( 'Contact does no supports account removal commands, so skip delivery' , [ 'target' => $post_uriid , 'uid' => $sender_uid , 'contact' => $contact [ 'url' ]]);
2019-02-11 21:30:08 +01:00
continue ;
}
2020-06-27 14:18:36 +02:00
if ( self :: skipActivityPubForDiaspora ( $contact , $target_item , $thr_parent )) {
2021-02-14 15:24:48 +01:00
Logger :: info ( 'Contact is from Diaspora, but the replied author is from ActivityPub, so skip delivery via Diaspora' , [ 'id' => $post_uriid , 'uid' => $sender_uid , 'url' => $contact [ 'url' ]]);
2020-06-27 14:18:36 +02:00
continue ;
}
2018-12-05 06:35:52 +01:00
// Don't deliver to Diaspora if it already had been done as batch delivery
2021-01-11 21:31:52 +01:00
if ( ! $in_batch && $batch_delivery && ( $contact [ 'network' ] == Protocol :: DIASPORA )) {
2021-02-14 15:24:48 +01:00
Logger :: info ( 'Diaspora contact is already delivered via batch' , [ 'id' => $post_uriid , 'uid' => $sender_uid , 'contact' => $contact ]);
2018-12-05 06:35:52 +01:00
continue ;
}
2018-11-05 00:17:41 +01:00
2018-12-05 06:35:52 +01:00
// Don't deliver to folks who have already been delivered to
if ( in_array ( $contact [ 'id' ], $conversants )) {
2021-02-14 15:24:48 +01:00
Logger :: info ( 'Already delivery' , [ 'id' => $post_uriid , 'uid' => $sender_uid , 'contact' => $contact ]);
2018-12-05 06:35:52 +01:00
continue ;
}
2018-11-05 00:17:41 +01:00
2022-12-31 13:19:34 +01:00
if ( empty ( $contact [ 'gsid' ])) {
2023-01-01 20:52:08 +01:00
$reachable = GServer :: reachable ( $contact );
2022-12-31 13:19:34 +01:00
} elseif ( ! DI :: config () -> get ( 'system' , 'bulk_delivery' )) {
2023-01-01 20:52:08 +01:00
$reachable = GServer :: isReachableById ( $contact [ 'gsid' ]);
2022-12-31 13:19:34 +01:00
} else {
2022-12-31 17:20:18 +01:00
$reachable = ! GServer :: isDefunctById ( $contact [ 'gsid' ]);
2022-12-31 13:19:34 +01:00
}
if ( ! $reachable ) {
2022-12-19 10:30:56 +01:00
Logger :: info ( 'Server is not reachable' , [ 'id' => $post_uriid , 'uid' => $sender_uid , 'contact' => $contact ]);
continue ;
}
2021-02-14 15:24:48 +01:00
Logger :: info ( 'Delivery' , [ 'batch' => $in_batch , 'target' => $post_uriid , 'uid' => $sender_uid , 'guid' => $target_item [ 'guid' ] ? ? '' , 'to' => $contact ]);
2019-01-19 13:30:16 +01:00
2018-12-05 06:35:52 +01:00
// Ensure that posts with our own protocol arrives before Diaspora posts arrive.
// Situation is that sometimes Friendica servers receive Friendica posts over the Diaspora protocol first.
// The conversion in Markdown reduces the formatting, so these posts should arrive after the Friendica posts.
2021-01-10 16:08:40 +01:00
// This is only important for high and medium priority tasks and not for Low priority jobs like deletions.
2022-10-17 07:49:55 +02:00
if (( $contact [ 'network' ] == Protocol :: DIASPORA ) && in_array ( $a -> getQueueValue ( 'priority' ), [ Worker :: PRIORITY_HIGH , Worker :: PRIORITY_MEDIUM ])) {
2021-07-25 00:08:33 +02:00
$deliver_options = [ 'priority' => $a -> getQueueValue ( 'priority' ), 'dont_fork' => true ];
2018-12-05 06:35:52 +01:00
} else {
2021-07-25 00:08:33 +02:00
$deliver_options = [ 'priority' => $a -> getQueueValue ( 'priority' ), 'created' => $a -> getQueueValue ( 'created' ), 'dont_fork' => true ];
2017-11-19 19:59:55 +01:00
}
2019-09-02 05:25:05 +02:00
2022-12-31 13:19:34 +01:00
if ( ! empty ( $contact [ 'gsid' ]) && DI :: config () -> get ( 'system' , 'bulk_delivery' )) {
2019-09-02 05:25:05 +02:00
$delivery_queue_count ++ ;
2022-12-31 20:36:47 +01:00
$deliveryQueueItem = DI :: deliveryQueueItemFactory () -> createFromDelivery ( $cmd , $post_uriid , new \DateTimeImmutable ( $target_item [ 'created' ]), $contact [ 'id' ], $contact [ 'gsid' ], $sender_uid );
DI :: deliveryQueueItemRepo () -> save ( $deliveryQueueItem );
2022-12-31 13:19:34 +01:00
Worker :: add ([ 'priority' => Worker :: PRIORITY_HIGH , 'dont_fork' => true ], 'BulkDelivery' , $contact [ 'gsid' ]);
} else {
if ( Worker :: add ( $deliver_options , 'Delivery' , $cmd , $post_uriid , ( int ) $contact [ 'id' ], $sender_uid )) {
$delivery_queue_count ++ ;
}
2019-09-02 05:25:05 +02:00
}
2022-12-31 13:19:34 +01:00
2022-09-21 21:03:07 +02:00
Worker :: coolDown ();
2018-11-05 00:17:41 +01:00
}
2021-01-10 16:08:40 +01:00
return $delivery_queue_count ;
}
/**
* Deliver the message via OStatus
*
2021-05-26 11:24:37 +02:00
* @ param int $target_id
* @ param array $target_item
* @ param array $owner
* @ param array $url_recipients
* @ param bool $public_message
* @ param bool $push_notify
2022-09-15 01:23:38 +02:00
*
* @ return int Count of sent Salmon notifications
2021-05-26 11:24:37 +02:00
* @ throws InternalServerErrorException
* @ throws Exception
2021-01-10 16:08:40 +01:00
*/
2022-09-15 01:23:38 +02:00
private static function deliverOStatus ( int $target_id , array $target_item , array $owner , array $url_recipients , bool $public_message , bool $push_notify ) : int
2021-01-10 16:08:40 +01:00
{
2021-05-26 11:24:37 +02:00
$a = DI :: app ();
2021-01-10 16:08:40 +01:00
$delivery_queue_count = 0 ;
2017-11-19 19:59:55 +01:00
2018-12-06 04:23:24 +01:00
$url_recipients = array_filter ( $url_recipients );
2018-11-05 00:17:41 +01:00
// send salmon slaps to mentioned remote tags (@foo@example.com) in OStatus posts
// They are especially used for notifications to OStatus users that don't follow us.
2021-08-30 21:46:10 +02:00
if ( count ( $url_recipients ) && ( $public_message || $push_notify ) && ! empty ( $target_item )) {
2018-11-05 00:17:41 +01:00
$slap = OStatus :: salmon ( $target_item , $owner );
foreach ( $url_recipients as $url ) {
2021-01-10 16:08:40 +01:00
Logger :: info ( 'Salmon delivery' , [ 'item' => $target_id , 'to' => $url ]);
2019-09-02 05:25:05 +02:00
$delivery_queue_count ++ ;
2018-12-06 04:23:24 +01:00
Salmon :: slapper ( $owner , $url , $slap );
2020-05-02 21:34:02 +02:00
Post\DeliveryData :: incrementQueueDone ( $target_item [ 'uri-id' ], Post\DeliveryData :: OSTATUS );
2018-11-05 00:17:41 +01:00
}
2017-11-19 19:59:55 +01:00
}
// Notify PuSH subscribers (Used for OStatus distribution of regular posts)
if ( $push_notify ) {
2022-09-15 01:23:38 +02:00
Logger :: info ( 'Activating internal PuSH' , [ 'uid' => $owner [ 'uid' ]]);
2017-11-19 19:59:55 +01:00
// Handling the pubsubhubbub requests
2021-07-25 00:08:33 +02:00
PushSubscriber :: publishFeed ( $owner [ 'uid' ], $a -> getQueueValue ( 'priority' ));
2017-11-19 19:59:55 +01:00
}
2021-01-10 16:08:40 +01:00
return $delivery_queue_count ;
2017-11-19 19:59:55 +01:00
}
2018-09-01 05:23:12 +02:00
2020-06-27 14:18:36 +02:00
/**
* Checks if the current delivery shouldn ' t be transported to Diaspora .
* This is done for posts from AP authors or posts that are comments to AP authors .
*
* @ param array $contact Receiver of the post
* @ param array $item The post
* @ param array $thr_parent The thread parent
2022-09-15 01:23:38 +02:00
*
2020-06-27 14:18:36 +02:00
* @ return bool
*/
2022-09-15 01:23:38 +02:00
private static function skipActivityPubForDiaspora ( array $contact , array $item , array $thr_parent ) : bool
2020-06-27 14:18:36 +02:00
{
// No skipping needs to be done when delivery isn't done to Diaspora
if ( $contact [ 'network' ] != Protocol :: DIASPORA ) {
return false ;
}
// Skip the delivery to Diaspora if the item is from an ActivityPub author
2020-09-29 22:12:19 +02:00
if ( ! empty ( $item [ 'author-network' ]) && ( $item [ 'author-network' ] == Protocol :: ACTIVITYPUB )) {
2020-06-27 14:18:36 +02:00
return true ;
}
2020-09-22 22:14:37 +02:00
2020-06-27 14:18:36 +02:00
// Skip the delivery to Diaspora if the thread parent is from an ActivityPub author
2020-09-29 22:12:19 +02:00
if ( ! empty ( $thr_parent [ 'author-network' ]) && ( $thr_parent [ 'author-network' ] == Protocol :: ACTIVITYPUB )) {
2020-06-27 14:18:36 +02:00
return true ;
}
return false ;
}
2019-02-11 21:30:08 +01:00
/**
* Checks if the current action is a deletion command of a account removal activity
* For Diaspora and ActivityPub we don ' t need to send single item deletion calls .
* These protocols do have a dedicated command for deleting a whole account .
*
* @ param string $cmd Notifier command
* @ param array $owner Sender of the post
* @ param string $network Receiver network
2022-09-15 01:23:38 +02:00
*
2019-02-11 21:30:08 +01:00
* @ return bool
* @ throws \Friendica\Network\HTTPException\InternalServerErrorException
* @ throws \ImagickException
*/
2022-09-15 01:23:38 +02:00
private static function isRemovalActivity ( string $cmd , array $owner , string $network ) : bool
2019-02-11 21:30:08 +01:00
{
2019-03-23 05:05:47 +01:00
return ( $cmd == Delivery :: DELETION ) && $owner [ 'account_removed' ] && in_array ( $network , [ Protocol :: ACTIVITYPUB , Protocol :: DIASPORA ]);
2019-02-11 21:30:08 +01:00
}
2018-12-05 06:06:48 +01:00
/**
* @ param int $self_user_id
2019-01-06 22:06:53 +01:00
* @ param int $priority The priority the Notifier queue item was created with
* @ param string $created The date the Notifier queue item was created on
2022-09-15 01:23:38 +02:00
*
2018-12-05 06:06:48 +01:00
* @ return bool
2019-01-06 22:06:53 +01:00
* @ throws \Friendica\Network\HTTPException\InternalServerErrorException
* @ throws \ImagickException
2018-12-05 06:06:48 +01:00
*/
2022-09-15 01:23:38 +02:00
private static function notifySelfRemoval ( int $self_user_id , int $priority , string $created ) : bool
2018-12-05 06:06:48 +01:00
{
$owner = User :: getOwnerDataById ( $self_user_id );
2021-10-30 01:21:07 +02:00
if ( empty ( $self_user_id ) || empty ( $owner )) {
2018-12-05 06:06:48 +01:00
return false ;
}
$contacts_stmt = DBA :: select ( 'contact' , [], [ 'self' => false , 'uid' => $self_user_id ]);
if ( ! DBA :: isResult ( $contacts_stmt )) {
return false ;
}
while ( $contact = DBA :: fetch ( $contacts_stmt )) {
2021-10-17 03:24:34 +02:00
Contact :: terminateFriendship ( $contact );
2018-12-05 06:06:48 +01:00
}
2018-12-05 06:35:52 +01:00
DBA :: close ( $contacts_stmt );
2018-12-05 06:06:48 +01:00
2022-05-02 16:35:57 +02:00
$inboxes = ActivityPub\Transmitter :: fetchTargetInboxesforUser ( $self_user_id );
2020-12-15 05:33:14 +01:00
foreach ( $inboxes as $inbox => $receivers ) {
2019-05-14 19:50:45 +02:00
Logger :: info ( 'Account removal via ActivityPub' , [ 'uid' => $self_user_id , 'inbox' => $inbox ]);
2022-10-17 07:49:55 +02:00
Worker :: add ([ 'priority' => Worker :: PRIORITY_NEGLIGIBLE , 'created' => $created , 'dont_fork' => true ],
2020-12-15 05:33:14 +01:00
'APDelivery' , Delivery :: REMOVAL , 0 , $inbox , $self_user_id , $receivers );
2022-09-21 21:03:07 +02:00
Worker :: coolDown ();
2018-12-05 06:06:48 +01:00
}
return true ;
}
2018-12-05 05:49:48 +01:00
/**
* @ param string $cmd
* @ param array $target_item
* @ param array $parent
2019-10-06 23:59:23 +02:00
* @ param array $thr_parent
2019-01-06 22:06:53 +01:00
* @ param int $priority The priority the Notifier queue item was created with
* @ param string $created The date the Notifier queue item was created on
2022-09-15 01:23:38 +02:00
*
2021-01-10 16:08:40 +01:00
* @ return array 'count' => The number of delivery tasks created , 'contacts' => their contact ids
2019-01-06 22:06:53 +01:00
* @ throws \Friendica\Network\HTTPException\InternalServerErrorException
* @ throws \ImagickException
2022-09-15 01:23:38 +02:00
* @ todo Unused parameter $owner
2018-12-05 05:49:48 +01:00
*/
2022-09-15 01:23:38 +02:00
private static function activityPubDelivery ( $cmd , array $target_item , array $parent , array $thr_parent , int $priority , string $created , $owner ) : array
2018-10-06 05:16:38 +02:00
{
2020-12-28 07:49:23 +01:00
// Don't deliver via AP when the starting post isn't from a federated network
if ( ! in_array ( $parent [ 'network' ], Protocol :: FEDERATED )) {
2021-09-04 06:51:20 +02:00
Logger :: info ( 'Parent network is no federated network, so no AP delivery' , [ 'network' => $parent [ 'network' ]]);
2021-01-10 16:08:40 +01:00
return [ 'count' => 0 , 'contacts' => []];
2020-12-28 07:49:23 +01:00
}
2019-10-06 23:59:23 +02:00
// Don't deliver via AP when the starting post is delivered via Diaspora
if ( $parent [ 'network' ] == Protocol :: DIASPORA ) {
2021-09-04 06:51:20 +02:00
Logger :: info ( 'Parent network is Diaspora, so no AP delivery' );
2021-01-10 16:08:40 +01:00
return [ 'count' => 0 , 'contacts' => []];
2019-10-06 23:59:23 +02:00
}
2020-12-11 07:35:38 +01:00
// Also don't deliver when the direct thread parent was delivered via Diaspora
2019-10-06 23:59:23 +02:00
if ( $thr_parent [ 'network' ] == Protocol :: DIASPORA ) {
2021-09-12 03:52:10 +02:00
Logger :: info ( 'Thread parent network is Diaspora, so no AP delivery' );
2021-01-10 16:08:40 +01:00
return [ 'count' => 0 , 'contacts' => []];
2019-10-06 23:59:23 +02:00
}
2020-12-11 07:35:38 +01:00
// Posts from Diaspora contacts are transmitted via Diaspora
if ( $target_item [ 'network' ] == Protocol :: DIASPORA ) {
2021-09-04 06:51:20 +02:00
Logger :: info ( 'Post network is Diaspora, so no AP delivery' );
2021-01-10 16:08:40 +01:00
return [ 'count' => 0 , 'contacts' => []];
2020-12-11 07:35:38 +01:00
}
2018-10-06 05:16:38 +02:00
$inboxes = [];
2020-09-22 22:14:37 +02:00
$relay_inboxes = [];
2018-10-06 05:16:38 +02:00
2019-01-22 14:31:39 +01:00
$uid = $target_item [ 'contact-uid' ] ? : $target_item [ 'uid' ];
2022-02-16 23:56:55 +01:00
// Update the locally stored follower list when we deliver to a forum
2022-02-18 10:12:33 +01:00
foreach ( Tag :: getByURIId ( $target_item [ 'uri-id' ], [ Tag :: MENTION , Tag :: EXCLUSIVE_MENTION ]) as $tag ) {
2022-02-16 23:56:55 +01:00
$target_contact = Contact :: getByURL ( Strings :: normaliseLink ( $tag [ 'url' ]), null , [], $uid );
2022-02-19 02:25:18 +01:00
if ( $target_contact && $target_contact [ 'contact-type' ] == Contact :: TYPE_COMMUNITY && $target_contact [ 'manually-approve' ]) {
2022-02-17 22:51:47 +01:00
Group :: updateMembersForForum ( $target_contact [ 'id' ]);
2022-02-16 23:56:55 +01:00
}
}
2018-10-06 05:16:38 +02:00
if ( $target_item [ 'origin' ]) {
2019-01-22 14:31:39 +01:00
$inboxes = ActivityPub\Transmitter :: fetchTargetInboxes ( $target_item , $uid );
2020-09-15 19:45:19 +02:00
if ( in_array ( $target_item [ 'private' ], [ Item :: PUBLIC ])) {
2020-11-16 00:28:05 +01:00
$inboxes = ActivityPub\Transmitter :: addRelayServerInboxesForItem ( $target_item [ 'id' ], $inboxes );
2020-09-23 17:20:16 +02:00
$relay_inboxes = ActivityPub\Transmitter :: addRelayServerInboxes ();
2020-09-15 19:45:19 +02:00
}
2022-11-03 05:23:04 +01:00
Logger :: info ( 'Origin item will be distributed' , [ 'id' => $target_item [ 'id' ], 'url' => $target_item [ 'uri' ], 'verb' => $target_item [ 'verb' ]]);
2023-01-02 00:37:17 +01:00
$check_signature = false ;
2022-07-27 19:39:00 +02:00
} elseif ( ! Post\Activity :: exists ( $target_item [ 'uri-id' ])) {
2022-11-03 05:23:04 +01:00
Logger :: info ( 'Remote item is no AP post. It will not be distributed.' , [ 'id' => $target_item [ 'id' ], 'url' => $target_item [ 'uri' ], 'verb' => $target_item [ 'verb' ]]);
2021-01-10 16:08:40 +01:00
return [ 'count' => 0 , 'contacts' => []];
2022-11-03 05:03:39 +01:00
} elseif ( $parent [ 'origin' ] && (( $target_item [ 'gravity' ] != Item :: GRAVITY_ACTIVITY ) || DI :: config () -> get ( 'system' , 'redistribute_activities' ))) {
2022-05-02 16:35:57 +02:00
$inboxes = ActivityPub\Transmitter :: fetchTargetInboxes ( $parent , $uid , false , $target_item [ 'id' ]);
2020-09-22 22:14:37 +02:00
if ( in_array ( $target_item [ 'private' ], [ Item :: PUBLIC ])) {
2020-11-16 00:28:05 +01:00
$inboxes = ActivityPub\Transmitter :: addRelayServerInboxesForItem ( $parent [ 'id' ], $inboxes );
2020-09-22 22:14:37 +02:00
}
2022-11-03 05:23:04 +01:00
Logger :: info ( 'Remote item will be distributed' , [ 'id' => $target_item [ 'id' ], 'url' => $target_item [ 'uri' ], 'verb' => $target_item [ 'verb' ]]);
2023-01-02 00:37:17 +01:00
$check_signature = ( $target_item [ 'gravity' ] == Item :: GRAVITY_ACTIVITY );
2022-11-03 05:27:30 +01:00
} else {
2022-11-03 05:23:04 +01:00
Logger :: info ( 'Remote activity will not be distributed' , [ 'id' => $target_item [ 'id' ], 'url' => $target_item [ 'uri' ], 'verb' => $target_item [ 'verb' ]]);
return [ 'count' => 0 , 'contacts' => []];
2018-10-06 05:16:38 +02:00
}
2020-09-22 22:14:37 +02:00
if ( empty ( $inboxes ) && empty ( $relay_inboxes )) {
2021-11-04 00:19:24 +01:00
Logger :: info ( 'No inboxes found for item ' . $target_item [ 'id' ] . ' with URL ' . $target_item [ 'uri' ] . '. It will not be distributed.' );
2021-01-10 16:08:40 +01:00
return [ 'count' => 0 , 'contacts' => []];
2018-10-06 15:16:52 +02:00
}
2018-10-06 05:16:38 +02:00
// Fill the item cache
2023-01-02 00:37:17 +01:00
$activity = ActivityPub\Transmitter :: createCachedActivityFromItem ( $target_item [ 'id' ], true );
if ( empty ( $activity )) {
2023-01-01 16:14:45 +01:00
Logger :: info ( 'Item cache was not created. The post will not be distributed.' , [ 'id' => $target_item [ 'id' ], 'url' => $target_item [ 'uri' ], 'verb' => $target_item [ 'verb' ]]);
return [ 'count' => 0 , 'contacts' => []];
2023-01-02 00:37:17 +01:00
}
if ( $check_signature && ! LDSignature :: isSigned ( $activity )) {
Logger :: info ( 'Unsigned remote activity will not be distributed' , [ 'id' => $target_item [ 'id' ], 'url' => $target_item [ 'uri' ], 'verb' => $target_item [ 'verb' ]]);
return [ 'count' => 0 , 'contacts' => []];
2023-01-01 16:14:45 +01:00
}
2018-10-06 05:16:38 +02:00
2019-09-02 05:25:05 +02:00
$delivery_queue_count = 0 ;
2021-01-10 16:08:40 +01:00
$contacts = [];
2019-09-02 05:25:05 +02:00
2020-12-12 17:45:23 +01:00
foreach ( $inboxes as $inbox => $receivers ) {
2021-01-10 16:08:40 +01:00
$contacts = array_merge ( $contacts , $receivers );
2021-05-26 11:24:37 +02:00
if (( count ( $receivers ) == 1 ) && Network :: isLocalLink ( $inbox )) {
$contact = Contact :: getById ( $receivers [ 0 ], [ 'url' ]);
2023-01-27 06:55:45 +01:00
if ( ! in_array ( $cmd , [ Delivery :: RELOCATION , Delivery :: SUGGESTION , Delivery :: MAIL ]) && ( $target_uid = User :: getIdForURL ( $contact [ 'url' ]))) {
if ( $cmd == Delivery :: DELETION ) {
Logger :: info ( 'No need to deliver deletions internally' , [ 'uid' => $target_uid , 'guid' => $target_item [ 'guid' ], 'uri-id' => $target_item [ 'uri-id' ], 'uri' => $target_item [ 'uri' ]]);
continue ;
}
2022-08-04 21:32:36 +02:00
if ( $target_item [ 'origin' ] || ( $target_item [ 'network' ] != Protocol :: ACTIVITYPUB )) {
if ( $target_uid != $target_item [ 'uid' ]) {
$fields = [ 'protocol' => Conversation :: PARCEL_LOCAL_DFRN , 'direction' => Conversation :: PUSH , 'post-reason' => Item :: PR_BCC ];
Item :: storeForUserByUriId ( $target_item [ 'uri-id' ], $target_uid , $fields , $target_item [ 'uid' ]);
Logger :: info ( 'Delivered locally' , [ 'cmd' => $cmd , 'id' => $target_item [ 'id' ], 'inbox' => $inbox ]);
} else {
Logger :: info ( 'No need to deliver to myself' , [ 'uid' => $target_uid , 'guid' => $target_item [ 'guid' ], 'uri-id' => $target_item [ 'uri-id' ], 'uri' => $target_item [ 'uri' ]]);
}
} else {
Logger :: info ( 'Remote item does not need to be delivered locally' , [ 'guid' => $target_item [ 'guid' ], 'uri-id' => $target_item [ 'uri-id' ], 'uri' => $target_item [ 'uri' ]]);
}
2021-05-26 11:24:37 +02:00
continue ;
}
2022-08-04 21:32:36 +02:00
} elseif (( count ( $receivers ) >= 1 ) && Network :: isLocalLink ( $inbox )) {
Logger :: info ( 'Is this a thing?' , [ 'guid' => $target_item [ 'guid' ], 'uri-id' => $target_item [ 'uri-id' ], 'uri' => $target_item [ 'uri' ]]);
2021-05-26 11:24:37 +02:00
}
2019-05-14 19:50:45 +02:00
Logger :: info ( 'Delivery via ActivityPub' , [ 'cmd' => $cmd , 'id' => $target_item [ 'id' ], 'inbox' => $inbox ]);
2018-10-06 05:16:38 +02:00
2022-05-02 19:34:40 +02:00
if ( DI :: config () -> get ( 'system' , 'bulk_delivery' )) {
2022-05-05 08:12:16 +02:00
$delivery_queue_count ++ ;
2022-05-13 07:52:05 +02:00
Post\Delivery :: add ( $target_item [ 'uri-id' ], $uid , $inbox , $target_item [ 'created' ], $cmd , $receivers );
2022-12-31 13:19:34 +01:00
Worker :: add ([ Worker :: PRIORITY_HIGH , 'dont_fork' => true ], 'APDelivery' , '' , 0 , $inbox , 0 );
2022-05-02 19:34:40 +02:00
} else {
if ( Worker :: add ([ 'priority' => $priority , 'created' => $created , 'dont_fork' => true ],
'APDelivery' , $cmd , $target_item [ 'id' ], $inbox , $uid , $receivers , $target_item [ 'uri-id' ])) {
$delivery_queue_count ++ ;
}
2019-09-02 05:25:05 +02:00
}
2022-09-21 21:03:07 +02:00
Worker :: coolDown ();
2018-10-06 05:16:38 +02:00
}
2018-12-07 06:52:14 +01:00
2020-09-22 22:14:37 +02:00
// We deliver posts to relay servers slightly delayed to priorize the direct delivery
2020-12-15 21:42:16 +01:00
foreach ( $relay_inboxes as $inbox ) {
2020-09-22 22:14:37 +02:00
Logger :: info ( 'Delivery to relay servers via ActivityPub' , [ 'cmd' => $cmd , 'id' => $target_item [ 'id' ], 'inbox' => $inbox ]);
2022-05-02 19:34:40 +02:00
if ( DI :: config () -> get ( 'system' , 'bulk_delivery' )) {
2022-05-05 08:12:16 +02:00
$delivery_queue_count ++ ;
2022-05-13 07:52:05 +02:00
Post\Delivery :: add ( $target_item [ 'uri-id' ], $uid , $inbox , $target_item [ 'created' ], $cmd , []);
2022-12-31 13:19:34 +01:00
Worker :: add ([ Worker :: PRIORITY_MEDIUM , 'dont_fork' => true ], 'APDelivery' , '' , 0 , $inbox , 0 );
2022-05-02 19:34:40 +02:00
} else {
if ( Worker :: add ([ 'priority' => $priority , 'dont_fork' => true ], 'APDelivery' , $cmd , $target_item [ 'id' ], $inbox , $uid , [], $target_item [ 'uri-id' ])) {
$delivery_queue_count ++ ;
}
2020-09-22 22:14:37 +02:00
}
2022-09-21 21:03:07 +02:00
Worker :: coolDown ();
2020-09-22 22:14:37 +02:00
}
2021-01-10 16:08:40 +01:00
return [ 'count' => $delivery_queue_count , 'contacts' => $contacts ];
2018-10-06 05:16:38 +02:00
}
2017-11-19 19:59:55 +01:00
}