$audience[] = $owner['url'];
}
- if (self::isAnnounce($item) || self::isAPPost($last_id)) {
- // Will be activated in a later step
- $networks = Protocol::FEDERATED;
- } else {
- // For now only send to these contacts:
- $networks = [Protocol::ACTIVITYPUB];
- }
-
$data = ['to' => [], 'cc' => [], 'bto' => [], 'bcc' => [], 'audience' => $audience];
if ($item['gravity'] == Item::GRAVITY_PARENT) {
$cid = Contact::getIdForURL($term['url'], $item['uid']);
if (!empty($cid) && in_array($cid, $receiver_list)) {
$contact = DBA::selectFirst('contact', ['url', 'network', 'protocol', 'gsid'], ['id' => $cid, 'network' => Protocol::FEDERATED]);
- if (!DBA::isResult($contact) || !self::isAPContact($contact, $networks)) {
+ if (!DBA::isResult($contact)) {
continue;
}
}
$contact = DBA::selectFirst('contact', ['url', 'hidden', 'network', 'protocol', 'gsid'], ['id' => $receiver, 'network' => Protocol::FEDERATED]);
- if (!DBA::isResult($contact) || !self::isAPContact($contact, $networks)) {
+ if (!DBA::isResult($contact)) {
continue;
}
return DBA::exists('inbox-status', ['url' => $url, 'archive' => true]);
}
- /**
- * Check if a given contact should be delivered via AP
- *
- * @param array $contact Contact array
- * @param array $networks Array with networks
- * @return bool Whether the used protocol matches ACTIVITYPUB
- * @throws Exception
- */
- private static function isAPContact(array $contact, array $networks): bool
- {
- if (in_array($contact['network'], $networks) || ($contact['protocol'] == Protocol::ACTIVITYPUB)) {
- return true;
- }
-
- return GServer::getProtocol($contact['gsid'] ?? 0) == Post\DeliveryData::ACTIVITYPUB;
- }
-
/**
* Fetches a list of inboxes of followers of a given user
*
* @param integer $uid User ID
- * @param boolean $all_ap Retrieve all AP enabled inboxes
* @return array of follower inboxes
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
- public static function fetchTargetInboxesforUser(int $uid, bool $all_ap = false): array
+ public static function fetchTargetInboxesforUser(int $uid): array
{
- $inboxes = [];
-
- if ($all_ap) {
- // Will be activated in a later step
- $networks = Protocol::FEDERATED;
- } else {
- // For now only send to these contacts:
- $networks = [Protocol::ACTIVITYPUB];
- }
-
$condition = [
'uid' => $uid,
'self' => false,
'pending' => false,
'blocked' => false,
'network' => Protocol::FEDERATED,
- 'contact-type' => [Contact::TYPE_UNKNOWN, Contact::TYPE_PERSON, Contact::TYPE_NEWS, Contact::TYPE_ORGANISATION],
];
if (!empty($uid)) {
$condition['rel'] = [Contact::FOLLOWER, Contact::FRIEND];
}
- $contacts = DBA::select('contact', ['id', 'url', 'network', 'protocol', 'gsid'], $condition);
- while ($contact = DBA::fetch($contacts)) {
- if (!self::isAPContact($contact, $networks)) {
- continue;
- }
+ return self::addInboxesForCondition($condition, []);
+ }
- if (Network::isUrlBlocked($contact['url'])) {
+ /**
+ * Fetch inboxes for a list of contacts
+ *
+ * @param array $recipients
+ * @param array $inboxes
+ * @return array
+ */
+ public static function addInboxesForRecipients(array $recipients, array $inboxes): array
+ {
+ return self::addInboxesForCondition(['id' => $recipients], $inboxes);
+ }
+
+ /**
+ * Get a list of inboxes for a given contact condition
+ *
+ * @param array $condition
+ * @param array $inboxes
+ * @return array
+ */
+ private static function addInboxesForCondition(array $condition, array $inboxes): array
+ {
+ $condition = DBA::mergeConditions($condition, ["(`ap-inbox` IS NOT NULL OR `ap-sharedinbox` IS NOT NULL)"]);
+
+ $accounts = DBA::select('account-user-view', ['id', 'url', 'ap-inbox', 'ap-sharedinbox'], $condition);
+ while ($account = DBA::fetch($accounts)) {
+ if (!empty($account['ap-sharedinbox']) && !Contact::isLocal($account['url'])) {
+ $target = $account['ap-sharedinbox'];
+ } elseif (!empty($account['ap-inbox'])) {
+ $target = $account['ap-inbox'];
+ } else {
continue;
}
-
- $profile = APContact::getByURL($contact['url'], false);
- if (!empty($profile)) {
- if (empty($profile['sharedinbox']) || Contact::isLocal($contact['url'])) {
- $target = $profile['inbox'];
- } else {
- $target = $profile['sharedinbox'];
- }
- if (!self::archivedInbox($target)) {
- $inboxes[$target][] = $contact['id'];
- }
+ if (!Transmitter::archivedInbox($target) && (empty($inboxes[$target]) || !in_array($account['id'], $inboxes[$target]))) {
+ $inboxes[$target][] = $account['id'];
}
}
- DBA::close($contacts);
-
return $inboxes;
}
}
if ($item_profile && ($receiver == $item_profile['followers']) && ($uid == $profile_uid)) {
- $inboxes = array_merge_recursive($inboxes, self::fetchTargetInboxesforUser($uid, true));
+ $inboxes = array_merge_recursive($inboxes, self::fetchTargetInboxesforUser($uid));
} else {
$profile = APContact::getByURL($receiver, false);
if (!empty($profile)) {
use Friendica\Model\User;
use Friendica\Protocol\Activity;
use Friendica\Protocol\ActivityPub;
+use Friendica\Protocol\ActivityPub\Transmitter;
use Friendica\Protocol\Diaspora;
use Friendica\Protocol\Delivery;
use Friendica\Util\LDSignature;
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']]);
- if (!self::isRemovalActivity($cmd, $owner, Protocol::ACTIVITYPUB)) {
- $apdelivery = self::activityPubDelivery($cmd, $target_item, $parent, $thr_parent, $a->getQueueValue('priority'), $a->getQueueValue('created'), $owner);
- $ap_contacts = $apdelivery['contacts'];
- $delivery_queue_count += $apdelivery['count'];
- // Restrict distribution to AP, when there are no permissions.
- if (($target_item['private'] == Item::PRIVATE) && empty($target_item['allow_cid']) && empty($target_item['allow_gid']) && empty($target_item['deny_cid']) && empty($target_item['deny_gid'])) {
- $only_ap_delivery = true;
- $public_message = false;
- $diaspora_delivery = false;
- }
+ // Restrict distribution to AP, when there are no permissions.
+ if (!self::isRemovalActivity($cmd, $owner, Protocol::ACTIVITYPUB) && ($target_item['private'] == Item::PRIVATE) && empty($target_item['allow_cid']) && empty($target_item['allow_gid']) && empty($target_item['deny_cid']) && empty($target_item['deny_gid'])) {
+ $only_ap_delivery = true;
+ $public_message = false;
+ $diaspora_delivery = false;
+ }
+
+ if (!$target_item['origin'] && $target_item['network'] == Protocol::ACTIVITYPUB) {
+ $only_ap_delivery = true;
+ $diaspora_delivery = false;
}
// Only deliver threaded replies (comment to a comment) to Diaspora
// if $parent['wall'] == 1 we will already have the parent message in our array
// and we will relay the whole lot.
- $localhost = str_replace('www.','', DI::baseUrl()->getHost());
- if (strpos($localhost,':')) {
- $localhost = substr($localhost,0,strpos($localhost,':'));
+ $localhost = str_replace('www.', '', DI::baseUrl()->getHost());
+ if (strpos($localhost, ':')) {
+ $localhost = substr($localhost, 0, strpos($localhost, ':'));
}
/**
*
$relay_to_owner = false;
- if (!$top_level && ($parent['wall'] == 0) && (stristr($target_item['uri'],$localhost))) {
+ if (!$top_level && ($parent['wall'] == 0) && (stristr($target_item['uri'], $localhost))) {
$relay_to_owner = true;
}
// Special treatment for group posts
if (Item::isGroupPost($target_item['uri-id'])) {
$relay_to_owner = true;
- $direct_group_delivery = true;
- }
-
- // Avoid that comments in a group thread are sent to OStatus
- if (Item::isGroupPost($parent['uri-id'])) {
- $direct_group_delivery = true;
}
$exclusive_delivery = false;
return;
}
- if (strlen($parent['allow_cid'])
+ if (
+ strlen($parent['allow_cid'])
|| strlen($parent['allow_gid'])
|| strlen($parent['deny_cid'])
- || strlen($parent['deny_gid'])) {
+ || strlen($parent['deny_gid'])
+ ) {
$public_message = false; // private recipients, not public
}
$aclFormatter = DI::aclFormatter();
$allow_people = $aclFormatter->expand($parent['allow_cid']);
- $allow_circles = Circle::expand($uid, $aclFormatter->expand($parent['allow_gid']),true);
+ $allow_circles = Circle::expand($uid, $aclFormatter->expand($parent['allow_gid']), true);
$deny_people = $aclFormatter->expand($parent['deny_cid']);
$deny_circles = Circle::expand($uid, $aclFormatter->expand($parent['deny_gid']));
$recipients[] = $item['contact-id'];
// pull out additional tagged people to notify (if public message)
if ($public_message && $item['inform']) {
- $people = explode(',',$item['inform']);
+ $people = explode(',', $item['inform']);
foreach ($people as $person) {
- if (substr($person,0,4) === 'cid:') {
- $recipients[] = intval(substr($person,4));
+ if (substr($person, 0, 4) === 'cid:') {
+ $recipients[] = intval(substr($person, 4));
}
}
}
// If this is a public message and pubmail is set on the parent, include all your email contacts
if (
function_exists('imap_open')
- && !DI::config()->get('system','imap_disabled')
+ && !DI::config()->get('system', 'imap_disabled')
&& $public_message
&& intval($target_item['pubmail'])
) {
$public_message = false;
}
+ if ($only_ap_delivery) {
+ $recipients = [];
+ } elseif ($followup) {
+ $recipients = $recipients_followup;
+ }
+
+ $apdelivery = self::activityPubDelivery($cmd, $target_item, $parent, $thr_parent, $a->getQueueValue('priority'), $a->getQueueValue('created'), $recipients);
+ $ap_contacts = $apdelivery['contacts'];
+ $delivery_queue_count += $apdelivery['count'];
+
if (empty($delivery_contacts_stmt)) {
- if ($only_ap_delivery) {
- $recipients = $ap_contacts;
- } elseif ($followup) {
- $recipients = $recipients_followup;
- }
$condition = ['id' => $recipients, 'self' => false, 'uid' => [0, $uid],
'blocked' => false, 'pending' => false, 'archive' => false];
if (!empty($networks)) {
$participants = Diaspora::participantsForThread($target_item, $participants);
}
- $condition = ['network' => Protocol::DFRN, 'uid' => $owner['uid'], 'blocked' => false,
- 'pending' => false, 'archive' => false, 'rel' => [Contact::FOLLOWER, Contact::FRIEND]];
+ $condition = [
+ 'network' => Protocol::DFRN,
+ 'uid' => $owner['uid'],
+ 'self' => false,
+ 'blocked' => false,
+ 'pending' => false,
+ 'archive' => false,
+ 'rel' => [Contact::FOLLOWER, Contact::FRIEND]
+ ];
$contacts = DBA::selectToArray('contact', ['id', 'uri-id', 'url', 'addr', 'name', 'network', 'protocol', 'baseurl', 'gsid'], $condition);
$conversants = array_merge($contacts, $participants);
- $delivery_queue_count += self::delivery($cmd, $post_uriid, $sender_uid, $target_item, $thr_parent, $owner, $batch_delivery, true, $conversants, $ap_contacts, []);
+ $delivery_queue_count += self::delivery($cmd, $post_uriid, $sender_uid, $target_item, $parent, $thr_parent, $owner, $batch_delivery, true, $conversants, $ap_contacts, []);
}
$contacts = DBA::toArray($delivery_contacts_stmt);
- $delivery_queue_count += self::delivery($cmd, $post_uriid, $sender_uid, $target_item, $thr_parent, $owner, $batch_delivery, false, $contacts, $ap_contacts, $conversants);
+ $delivery_queue_count += self::delivery($cmd, $post_uriid, $sender_uid, $target_item, $parent, $thr_parent, $owner, $batch_delivery, false, $contacts, $ap_contacts, $conversants);
if (!empty($target_item)) {
Logger::info('Calling hooks for ' . $cmd . ' ' . $target_id);
* @param int $post_uriid
* @param int $sender_uid
* @param array $target_item
+ * @param array $parent
* @param array $thr_parent
* @param array $owner
* @param bool $batch_delivery
* @throws InternalServerErrorException
* @throws Exception
*/
- 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
+ private static function delivery(string $cmd, int $post_uriid, int $sender_uid, array $target_item, array $parent, array $thr_parent, array $owner, bool $batch_delivery, bool $in_batch, array $contacts, array $ap_contacts, array $conversants = []): int
{
$a = DI::app();
$delivery_queue_count = 0;
}
foreach ($contacts as $contact) {
+ // Transmit via Diaspora if the thread had started as Diaspora post.
+ // Also transmit via Diaspora if this is a direct answer to a Diaspora comment.
+ if (($contact['network'] != Protocol::DIASPORA) && in_array(Protocol::DIASPORA, [$parent['network'] ?? '', $thr_parent['network'] ?? '', $target_item['network'] ?? ''])) {
+ Logger::info('Enforcing the Diaspora protocol', ['id' => $contact['id'], 'network' => $contact['network'], 'parent' => $parent['network'], 'thread-parent' => $thr_parent['network'], 'post' => $target_item['network']]);
+ $contact['network'] = Protocol::DIASPORA;
+ }
+
// Direct delivery of local contacts
if (!in_array($cmd, [Delivery::RELOCATION, Delivery::SUGGESTION, Delivery::MAIL]) && $target_uid = User::getIdForURL($contact['url'])) {
if ($cmd == Delivery::DELETION) {
continue;
}
- // 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)) {
- Logger::info('Contact is already delivered via AP, so skip delivery via legacy DFRN/Diaspora', ['target' => $post_uriid, 'uid' => $sender_uid, 'contact' => $contact['url']]);
+ $cdata = Contact::getPublicAndUserContactID($contact['id'], $sender_uid);
+ if (in_array($cdata['public'] ?: $contact['id'], $ap_contacts)) {
+ Logger::info('The public contact is already delivered via AP, so skip delivery via legacy DFRN/Diaspora', ['batch' => $in_batch, 'target' => $post_uriid, 'uid' => $sender_uid, 'contact' => $contact['url']]);
+ continue;
+ } elseif (in_array($cdata['user'] ?: $contact['id'], $ap_contacts)) {
+ Logger::info('The user contact is already delivered via AP, so skip delivery via legacy DFRN/Diaspora', ['batch' => $in_batch, 'target' => $post_uriid, 'uid' => $sender_uid, 'contact' => $contact['url']]);
continue;
}
if (!empty($contact['id']) && Contact::isArchived($contact['id'])) {
+ // We mark the contact here, since we could have only got here, when the "archived" value on this
+ // specific contact hadn't been set.
+ Contact::markForArchival($contact);
Logger::info('Contact is archived, so skip delivery', ['target' => $post_uriid, 'uid' => $sender_uid, 'contact' => $contact['url']]);
continue;
}
*/
private static function isRemovalActivity(string $cmd, array $owner, string $network): bool
{
- return ($cmd == Delivery::DELETION) && $owner['account_removed'] && in_array($network, [Protocol::ACTIVITYPUB, Protocol::DIASPORA]);
+ return ($cmd == Delivery::REMOVAL) && $owner['account_removed'] && in_array($network, [Protocol::ACTIVITYPUB, Protocol::DIASPORA]);
}
/**
return false;
}
- while($contact = DBA::fetch($contacts_stmt)) {
+ while ($contact = DBA::fetch($contacts_stmt)) {
Contact::terminateFriendship($contact);
}
DBA::close($contacts_stmt);
* @param array $target_item
* @param array $parent
* @param array $thr_parent
- * @param int $priority The priority the Notifier queue item was created with
- * @param string $created The date the Notifier queue item was created on
+ * @param int $priority The priority the Notifier queue item was created with
+ * @param string $created The date the Notifier queue item was created on
+ * @param array $recipients Array of receivers
*
* @return array 'count' => The number of delivery tasks created, 'contacts' => their contact ids
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
* @todo Unused parameter $owner
*/
- private static function activityPubDelivery($cmd, array $target_item, array $parent, array $thr_parent, int $priority, string $created, $owner): array
+ private static function activityPubDelivery($cmd, array $target_item, array $parent, array $thr_parent, int $priority, string $created, array $recipients): array
{
// Don't deliver via AP when the starting post isn't from a federated network
if (!in_array($parent['network'], Protocol::FEDERATED)) {
return ['count' => 0, 'contacts' => []];
}
+ if ($target_item['private'] != Item::PRIVATE) {
+ $inboxes = Transmitter::addInboxesForRecipients($recipients, $inboxes);
+ }
+
if (empty($inboxes) && empty($relay_inboxes)) {
Logger::info('No inboxes found for item ' . $target_item['id'] . ' with URL ' . $target_item['uri'] . '. It will not be distributed.');
return ['count' => 0, 'contacts' => []];