From fea48fa85e3707f41f4b1d4469ffb5a5938d6c21 Mon Sep 17 00:00:00 2001
From: Michael <heluecht@pirati.ca>
Date: Sat, 20 Oct 2018 07:53:45 +0000
Subject: [PATCH] AP: Transmitting and receiving with non AP contacts

---
 boot.php                                 |   2 +-
 config/dbstructure.json                  |   1 +
 src/Protocol/ActivityPub/Receiver.php    |   3 +-
 src/Protocol/ActivityPub/Transmitter.php | 102 +++++++++++++----------
 src/Worker/Notifier.php                  |   6 +-
 5 files changed, 68 insertions(+), 46 deletions(-)

diff --git a/boot.php b/boot.php
index acbc737658..5d4b7c147e 100644
--- a/boot.php
+++ b/boot.php
@@ -41,7 +41,7 @@ define('FRIENDICA_PLATFORM',     'Friendica');
 define('FRIENDICA_CODENAME',     'The Tazmans Flax-lily');
 define('FRIENDICA_VERSION',      '2018.12-dev');
 define('DFRN_PROTOCOL_VERSION',  '2.23');
-define('DB_UPDATE_VERSION',      1288);
+define('DB_UPDATE_VERSION',      1289);
 define('NEW_UPDATE_ROUTINE_VERSION', 1170);
 
 /**
diff --git a/config/dbstructure.json b/config/dbstructure.json
index da1da7a66b..43fce1d2f9 100644
--- a/config/dbstructure.json
+++ b/config/dbstructure.json
@@ -40,6 +40,7 @@
 		"indexes": {
 			"PRIMARY": ["url"],
 			"addr": ["addr(32)"],
+			"alias": ["alias(190)"],
 			"url": ["followers(190)"]
 		}
 	},
diff --git a/src/Protocol/ActivityPub/Receiver.php b/src/Protocol/ActivityPub/Receiver.php
index a5ba0763db..4341c14a3f 100644
--- a/src/Protocol/ActivityPub/Receiver.php
+++ b/src/Protocol/ActivityPub/Receiver.php
@@ -419,8 +419,9 @@ class Receiver
 				}
 
 				if (in_array($receiver, [$followers, self::PUBLIC_COLLECTION]) && !empty($actor)) {
+					$networks = [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::DIASPORA, Protocol::OSTATUS];
 					$condition = ['nurl' => normalise_link($actor), 'rel' => [Contact::SHARING, Contact::FRIEND],
-						'network' => Protocol::ACTIVITYPUB, 'archive' => false, 'pending' => false];
+						'network' => $networks, 'archive' => false, 'pending' => false];
 					$contacts = DBA::select('contact', ['uid'], $condition);
 					while ($contact = DBA::fetch($contacts)) {
 						if ($contact['uid'] != 0) {
diff --git a/src/Protocol/ActivityPub/Transmitter.php b/src/Protocol/ActivityPub/Transmitter.php
index 98422f3ad0..a94b7f59fd 100644
--- a/src/Protocol/ActivityPub/Transmitter.php
+++ b/src/Protocol/ActivityPub/Transmitter.php
@@ -308,6 +308,12 @@ class Transmitter
 	 */
 	private static function createPermissionBlockForItem($item)
 	{
+		// Will be activated in a later step
+		// $networks = [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::DIASPORA, Protocol::OSTATUS];
+
+		// For now only send to these contacts:
+		$networks = [Protocol::ACTIVITYPUB, Protocol::OSTATUS];
+
 		$data = ['to' => [], 'cc' => []];
 
 		$data = array_merge($data, self::fetchPermissionBlockFromConversation($item));
@@ -316,8 +322,6 @@ class Transmitter
 
 		$terms = Term::tagArrayFromItemId($item['id'], TERM_MENTION);
 
-		$contacts[$item['author-link']] = $item['author-link'];
-
 		if (!$item['private']) {
 			$data['to'][] = ActivityPub::PUBLIC_COLLECTION;
 			if (!empty($actor_profile['followers'])) {
@@ -326,13 +330,8 @@ class Transmitter
 
 			foreach ($terms as $term) {
 				$profile = APContact::getByURL($term['url'], false);
-				if (!empty($profile) && empty($contacts[$profile['url']])) {
+				if (!empty($profile)) {
 					$data['to'][] = $profile['url'];
-					$contacts[$profile['url']] = $profile['url'];
-
-					if (($key = array_search($profile['url'], $data['cc'])) !== false) {
-						unset($data['cc'][$key]);
-					}
 				}
 			}
 		} else {
@@ -343,21 +342,17 @@ class Transmitter
 			foreach ($terms as $term) {
 				$cid = Contact::getIdForURL($term['url'], $item['uid']);
 				if (!empty($cid) && in_array($cid, $receiver_list)) {
-					$contact = DBA::selectFirst('contact', ['url'], ['id' => $cid, 'network' => Protocol::ACTIVITYPUB]);
-					$data['to'][] = $contact['url'];
-					$contacts[$contact['url']] = $contact['url'];
-
-					if (($key = array_search($profile['url'], $data['cc'])) !== false) {
-						unset($data['cc'][$key]);
+					$contact = DBA::selectFirst('contact', ['url'], ['id' => $cid, 'network' => $networks]);
+					if (DBA::isResult($contact) && !empty($profile = APContact::getByURL($contact['url'], false))) {
+						$data['to'][] = $profile['url'];
 					}
 				}
 			}
 
 			foreach ($receiver_list as $receiver) {
-				$contact = DBA::selectFirst('contact', ['url'], ['id' => $receiver, 'network' => Protocol::ACTIVITYPUB]);
-				if (DBA::isResult($contact) && empty($contacts[$contact['url']])) {
-					$data['cc'][] = $contact['url'];
-					$contacts[$contact['url']] = $contact['url'];
+				$contact = DBA::selectFirst('contact', ['url'], ['id' => $receiver, 'network' => $networks]);
+				if (DBA::isResult($contact) && !empty($profile = APContact::getByURL($contact['url'], false))) {
+					$data['cc'][] = $profile['url'];
 				}
 			}
 		}
@@ -370,32 +365,28 @@ class Transmitter
 			}
 
 			$profile = APContact::getByURL($parent['author-link'], false);
-			if (!empty($profile) && ($parent['uri'] == $item['thr-parent'])) {
-				$data['to'][] = $profile['url'];
-				$contacts[$profile['url']] = $profile['url'];
-
-				if (($key = array_search($profile['url'], $data['cc'])) !== false) {
-					unset($data['cc'][$key]);
+			if (!empty($profile)) {
+				if ($parent['uri'] == $item['thr-parent']) {
+					$data['to'][] = $profile['url'];
+				} else {
+					$data['cc'][] = $profile['url'];
 				}
 			}
 
-			if (!empty($profile) && empty($contacts[$profile['url']])) {
-				$data['cc'][] = $profile['url'];
-				$contacts[$profile['url']] = $profile['url'];
-			}
-
 			if ($item['gravity'] != GRAVITY_PARENT) {
 				continue;
 			}
 
 			$profile = APContact::getByURL($parent['owner-link'], false);
-			if (!empty($profile) && empty($contacts[$profile['url']])) {
+			if (!empty($profile)) {
 				$data['cc'][] = $profile['url'];
-				$contacts[$profile['url']] = $profile['url'];
 			}
 		}
 		DBA::close($parents);
 
+		$data['to'] = array_unique($data['to']);
+		$data['cc'] = array_unique($data['cc']);
+
 		if (($key = array_search($item['author-link'], $data['to'])) !== false) {
 			unset($data['to'][$key]);
 		}
@@ -404,30 +395,50 @@ class Transmitter
 			unset($data['cc'][$key]);
 		}
 
-		return ['to' => array_values(array_unique($data['to'])), 'cc' => array_values(array_unique($data['cc']))];
+		foreach ($data['to'] as $to) {
+			if (($key = array_search($to, $data['cc'])) !== false) {
+				unset($data['cc'][$key]);
+			}
+		}
+
+		return ['to' => array_values($data['to']), 'cc' => array_values($data['cc'])];
 	}
 
 	/**
 	 * Fetches a list of inboxes of followers of a given user
 	 *
 	 * @param integer $uid User ID
+	 * @param boolean $personal fetch personal inboxes
 	 *
 	 * @return array of follower inboxes
 	 */
-	public static function fetchTargetInboxesforUser($uid)
+	public static function fetchTargetInboxesforUser($uid, $personal = false)
 	{
 		$inboxes = [];
 
-		$condition = ['uid' => $uid, 'network' => Protocol::ACTIVITYPUB, 'archive' => false, 'pending' => false];
+		// Will be activated in a later step
+		// $networks = [Protocol::ACTIVITYPUB, Protocol::DFRN, Protocol::DIASPORA, Protocol::OSTATUS];
+
+		// For now only send to these contacts:
+		$networks = [Protocol::ACTIVITYPUB, Protocol::OSTATUS];
+
+		$condition = ['uid' => $uid, 'network' => $networks, 'archive' => false, 'pending' => false];
 
 		if (!empty($uid)) {
 			$condition['rel'] = [Contact::FOLLOWER, Contact::FRIEND];
 		}
 
-		$contacts = DBA::select('contact', ['notify', 'batch'], $condition);
+		$contacts = DBA::select('contact', ['url'], $condition);
 		while ($contact = DBA::fetch($contacts)) {
-			$contact = defaults($contact, 'batch', $contact['notify']);
-			$inboxes[$contact] = $contact;
+			$profile = APContact::getByURL($contact['url'], false);
+			if (!empty($profile)) {
+				if (empty($profile['sharedinbox']) || $personal) {
+					$target = $profile['inbox'];
+				} else {
+					$target = $profile['sharedinbox'];
+				}
+				$inboxes[$target] = $target;
+			}
 		}
 		DBA::close($contacts);
 
@@ -439,10 +450,11 @@ class Transmitter
 	 *
 	 * @param array $item
 	 * @param integer $uid User ID
+	 * @param boolean $personal fetch personal inboxes
 	 *
 	 * @return array with inboxes
 	 */
-	public static function fetchTargetInboxes($item, $uid)
+	public static function fetchTargetInboxes($item, $uid, $personal = false)
 	{
 		$permissions = self::createPermissionBlockForItem($item);
 		if (empty($permissions)) {
@@ -452,9 +464,9 @@ class Transmitter
 		$inboxes = [];
 
 		if ($item['gravity'] == GRAVITY_ACTIVITY) {
-			$item_profile = APContact::getByURL($item['author-link']);
+			$item_profile = APContact::getByURL($item['author-link'], false);
 		} else {
-			$item_profile = APContact::getByURL($item['owner-link']);
+			$item_profile = APContact::getByURL($item['owner-link'], false);
 		}
 
 		foreach (['to', 'cc', 'bto', 'bcc'] as $element) {
@@ -464,11 +476,15 @@ class Transmitter
 
 			foreach ($permissions[$element] as $receiver) {
 				if ($receiver == $item_profile['followers']) {
-					$inboxes = self::fetchTargetInboxesforUser($uid);
+					$inboxes = self::fetchTargetInboxesforUser($uid, $personal);
 				} else {
-					$profile = APContact::getByURL($receiver);
+					$profile = APContact::getByURL($receiver, false);
 					if (!empty($profile)) {
-						$target = defaults($profile, 'sharedinbox', $profile['inbox']);
+						if (empty($profile['sharedinbox']) || $personal) {
+							$target = $profile['inbox'];
+						} else {
+							$target = $profile['sharedinbox'];
+						}
 						$inboxes[$target] = $target;
 					}
 				}
diff --git a/src/Worker/Notifier.php b/src/Worker/Notifier.php
index ad33c3245d..3edc15c3b8 100644
--- a/src/Worker/Notifier.php
+++ b/src/Worker/Notifier.php
@@ -512,6 +512,7 @@ class Notifier
 	private static function activityPubDelivery($a, $cmd, $item_id, $uid, $target_item, $parent)
 	{
 		$inboxes = [];
+		$personal = false;
 
 		if ($target_item['origin']) {
 			$inboxes = ActivityPub\Transmitter::fetchTargetInboxes($target_item, $uid);
@@ -520,11 +521,14 @@ class Notifier
 			logger('Remote item ' . $item_id . ' with URL ' . $target_item['uri'] . ' is no AP post. It will not be distributed.', LOGGER_DEBUG);
 			return;
 		} else {
+			// Remote items are transmitted via the personal inboxes.
+			// Doing so ensures that the dedicated receiver will get the message.
+			$personal = true;
 			logger('Remote item ' . $item_id . ' with URL ' . $target_item['uri'] . ' will be distributed.', LOGGER_DEBUG);
 		}
 
 		if ($parent['origin']) {
-			$parent_inboxes = ActivityPub\Transmitter::fetchTargetInboxes($parent, $uid);
+			$parent_inboxes = ActivityPub\Transmitter::fetchTargetInboxes($parent, $uid, $personal);
 			$inboxes = array_merge($inboxes, $parent_inboxes);
 		}