From 178455928a87b549cea8f1936baa5776b214b8bc Mon Sep 17 00:00:00 2001 From: Michael Date: Tue, 15 Sep 2020 17:45:19 +0000 Subject: [PATCH 1/6] We can now manage relay servers and can send content to them --- doc/tools.md | 1 + src/Console/Relay.php | 150 +++++++++++++++++++++++ src/Core/Console.php | 2 + src/Protocol/ActivityPub/Receiver.php | 2 + src/Protocol/ActivityPub/Transmitter.php | 69 ++++++++++- src/Worker/Notifier.php | 5 + 6 files changed, 226 insertions(+), 3 deletions(-) create mode 100644 src/Console/Relay.php diff --git a/doc/tools.md b/doc/tools.md index 8746e9c15..86be0b492 100644 --- a/doc/tools.md +++ b/doc/tools.md @@ -27,6 +27,7 @@ The console provides the following commands: * typo: Checks for parse errors in Friendica files * postupdate: Execute pending post update scripts (can last days) * storage: Manage storage backend +* relay: Manage ActivityPub relais Please consult *bin/console help* on the command line interface of your server for details about the commands. diff --git a/src/Console/Relay.php b/src/Console/Relay.php new file mode 100644 index 000000000..e1da56dba --- /dev/null +++ b/src/Console/Relay.php @@ -0,0 +1,150 @@ +. + * + */ + +namespace Friendica\Console; + +use Asika\SimpleConsole\CommandArgsException; +use Friendica\App; +use Friendica\Database\DBA; +use Friendica\Model\APContact; +use Friendica\Model\Contact; +use Friendica\Protocol\ActivityPub\Transmitter; + +/** + * tool to access the system config from the CLI + * + * With this script you can access the system configuration of your node from + * the CLI. You can do both, reading current values stored in the database and + * set new values to config variables. + * + * Usage: + * If you specify no parameters at the CLI, the script will list all config + * variables defined. + * + * If you specify one parameter, the script will list all config variables + * defined in this section of the configuration (e.g. "system"). + * + * If you specify two parameters, the script will show you the current value + * of the named configuration setting. (e.g. "system loglevel") + * + * If you specify three parameters, the named configuration setting will be + * set to the value of the last parameter. (e.g. "system loglevel 0" will + * disable logging) + */ +class Relay extends \Asika\SimpleConsole\Console +{ + protected $helpOptions = ['h', 'help', '?']; + + /** + * @var App\Mode + */ + private $appMode; + + protected function getHelp() + { + $help = << [-h|--help|-?] [-v] + bin/console relay remove [-h|--help|-?] [-v] + +Description + bin/console relay + Lists all active relais + + bin/console relay add + Add a relay actor + + bin/console relay remove + Remove a relay actor + +Options + -h|--help|-? Show help information + -v Show more debug information. +HELP; + return $help; + } + + public function __construct(App\Mode $appMode, array $argv = null) + { + parent::__construct($argv); + + $this->appMode = $appMode; + } + + protected function doExecute() + { + if ($this->getOption('v')) { + $this->out('Executable: ' . $this->executable); + $this->out('Class: ' . __CLASS__); + $this->out('Arguments: ' . var_export($this->args, true)); + $this->out('Options: ' . var_export($this->options, true)); + } + + if (count($this->args) > 2) { + throw new CommandArgsException('Too many arguments'); + } + + if (count($this->args) == 1) { + throw new CommandArgsException('Too few arguments'); + } + + if (count($this->args) == 0) { + $contacts = DBA::select('apcontact', ['url'], + ["`type` = ? AND `url` IN (SELECT `url` FROM `contact` WHERE `uid` = ? AND `rel` IN (?, ?))", + 'Application', 0, Contact::FOLLOWER, Contact::FRIEND]); + while ($contact = DBA::fetch($contacts)) { + $this->out($contact['url']); + } + DBA::close($contacts); + } + + if (count($this->args) == 2) { + $mode = $this->getArgument(0); + $actor = $this->getArgument(1); + + $apcontact = APContact::getByURL($actor); + if (empty($apcontact) || ($apcontact['type'] != 'Application')) { + $this->out($actor . ' is no relay actor'); + return 1; + } + + if ($mode == 'add') { + if (Transmitter::sendRelayFollow($actor)) { + $this->out('Successfully added ' . $actor); + } else { + $this->out($actor . " couldn't be added"); + } + } elseif ($mode == 'remove') { + if (Transmitter::sendRelayUndoFollow($actor)) { + $this->out('Successfully removed ' . $actor); + } else { + $this->out($actor . " couldn't be removed"); + } + } else { + throw new CommandArgsException($mode . ' is no valid command'); + } + } + + return 0; + } +} diff --git a/src/Core/Console.php b/src/Core/Console.php index e08ea7f42..575a67503 100644 --- a/src/Core/Console.php +++ b/src/Core/Console.php @@ -64,6 +64,7 @@ Commands: postupdate Execute pending post update scripts (can last days) serverblock Manage blocked servers storage Manage storage backend + relay Manage ActivityPub relais Options: -h|--help|-? Show help information @@ -92,6 +93,7 @@ HELP; 'postupdate' => Friendica\Console\PostUpdate::class, 'serverblock' => Friendica\Console\ServerBlock::class, 'storage' => Friendica\Console\Storage::class, + 'relay' => Friendica\Console\Relay::class, ]; /** diff --git a/src/Protocol/ActivityPub/Receiver.php b/src/Protocol/ActivityPub/Receiver.php index 05c3abc01..d5164b5cc 100644 --- a/src/Protocol/ActivityPub/Receiver.php +++ b/src/Protocol/ActivityPub/Receiver.php @@ -163,11 +163,13 @@ class Receiver if ($type != 'as:Announce') { Logger::info('Not an announcement', ['activity' => $activity]); + return; } $object_id = JsonLD::fetchElement($activity, 'as:object', '@id'); if (empty($object_id)) { Logger::info('No object id found', ['activity' => $activity]); + return; } Logger::info('Got relayed message id', ['id' => $object_id]); diff --git a/src/Protocol/ActivityPub/Transmitter.php b/src/Protocol/ActivityPub/Transmitter.php index 91042c06b..dae783bd7 100644 --- a/src/Protocol/ActivityPub/Transmitter.php +++ b/src/Protocol/ActivityPub/Transmitter.php @@ -61,6 +61,68 @@ require_once 'mod/share.php'; */ class Transmitter { + /** + * Add relay servers to the list of inboxes + * + * @param array $inboxes + * @return array inboxes with added relay servers + */ + public static function addRelayServerInboxes(array $inboxes) + { + $contacts = DBA::select('apcontact', ['inbox'], + ["`type` = ? AND `url` IN (SELECT `url` FROM `contact` WHERE `uid` = ? AND `rel` IN (?, ?))", + 'Application', 0, Contact::FOLLOWER, Contact::FRIEND]); + while ($contact = DBA::fetch($contacts)) { + $inboxes[] = $contact['inbox']; + } + DBA::close($contacts); + + return $inboxes; + } + + /** + * Subscribe to a relay + * + * @param string $url Subscribe actor url + * @return bool success + */ + public static function sendRelayFollow(string $url) + { + $contact_id = Contact::getIdForURL($url); + if (!$contact_id) { + return false; + } + + $activity_id = ActivityPub\Transmitter::activityIDFromContact($contact_id); + $success = ActivityPub\Transmitter::sendActivity('Follow', $url, 0, $activity_id); + if ($success) { + DBA::update('contact', ['rel' => Contact::FRIEND], ['id' => $contact_id]); + } + + return $success; + } + + /** + * Unsubscribe from a relay + * + * @param string $url Subscribe actor url + * @return bool success + */ + public static function sendRelayUndoFollow(string $url) + { + $contact_id = Contact::getIdForURL($url); + if (!$contact_id) { + return false; + } + + $success = self::sendContactUndo($url, $contact_id, 0); + if ($success) { + DBA::update('contact', ['rel' => Contact::SHARING], ['id' => $contact_id]); + } + + return $success; + } + /** * Collects a list of contacts of the given owner * @@ -1917,18 +1979,19 @@ class Transmitter * @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \ImagickException * @throws \Exception + * @return bool success */ public static function sendContactUndo($target, $cid, $uid) { $profile = APContact::getByURL($target); if (empty($profile['inbox'])) { Logger::warning('No inbox found for target', ['target' => $target, 'profile' => $profile]); - return; + return false; } $object_id = self::activityIDFromContact($cid); if (empty($object_id)) { - return; + return false; } $id = DI::baseUrl() . '/activity/' . System::createGUID(); @@ -1947,7 +2010,7 @@ class Transmitter Logger::log('Sending undo to ' . $target . ' for user ' . $uid . ' with id ' . $id, Logger::DEBUG); $signed = LDSignature::sign($data, $owner); - HTTPSignature::transmit($signed, $profile['inbox'], $uid); + return HTTPSignature::transmit($signed, $profile['inbox'], $uid); } private static function prependMentions($body, int $uriid) diff --git a/src/Worker/Notifier.php b/src/Worker/Notifier.php index 3ee75bbb0..69b684feb 100644 --- a/src/Worker/Notifier.php +++ b/src/Worker/Notifier.php @@ -786,6 +786,11 @@ class Notifier if ($target_item['origin']) { $inboxes = ActivityPub\Transmitter::fetchTargetInboxes($target_item, $uid); + + if (in_array($target_item['private'], [Item::PUBLIC])) { + $inboxes = ActivityPub\Transmitter::addRelayServerInboxes($inboxes); + } + Logger::log('Origin item ' . $target_item['id'] . ' with URL ' . $target_item['uri'] . ' will be distributed.', Logger::DEBUG); } elseif (Item::isForumPost($target_item, $owner)) { $inboxes = ActivityPub\Transmitter::fetchTargetInboxes($target_item, $uid, false, 0, true); From 6c6cfa939da5aacea0b9fc5a936f9bbee7066798 Mon Sep 17 00:00:00 2001 From: Michael Vogel Date: Wed, 16 Sep 2020 20:23:27 +0200 Subject: [PATCH 2/6] Apply suggestions from code review Co-authored-by: Hypolite Petovan --- doc/tools.md | 2 +- src/Console/Relay.php | 17 ++++++++++------- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/doc/tools.md b/doc/tools.md index 86be0b492..1c3b8e119 100644 --- a/doc/tools.md +++ b/doc/tools.md @@ -27,7 +27,7 @@ The console provides the following commands: * typo: Checks for parse errors in Friendica files * postupdate: Execute pending post update scripts (can last days) * storage: Manage storage backend -* relay: Manage ActivityPub relais +* relay: Manage ActivityPub relay servers Please consult *bin/console help* on the command line interface of your server for details about the commands. diff --git a/src/Console/Relay.php b/src/Console/Relay.php index e1da56dba..d23d871f9 100644 --- a/src/Console/Relay.php +++ b/src/Console/Relay.php @@ -69,12 +69,12 @@ Synopsis Description bin/console relay - Lists all active relais + Lists all active relay servers bin/console relay add Add a relay actor - bin/console relay remove + bin/console relay remove Remove a relay actor Options @@ -84,11 +84,14 @@ HELP; return $help; } - public function __construct(App\Mode $appMode, array $argv = null) + /** @var $dba Friendica\Database\Database */ + private $dba; + + public function __construct(Friendica\Database\Database $dba, array $argv = null) { parent::__construct($argv); - $this->appMode = $appMode; + $this->dba = $dba; } protected function doExecute() @@ -109,13 +112,13 @@ HELP; } if (count($this->args) == 0) { - $contacts = DBA::select('apcontact', ['url'], + $contacts = $this->dba->select('apcontact', ['url'], ["`type` = ? AND `url` IN (SELECT `url` FROM `contact` WHERE `uid` = ? AND `rel` IN (?, ?))", 'Application', 0, Contact::FOLLOWER, Contact::FRIEND]); - while ($contact = DBA::fetch($contacts)) { + while ($contact = $this->dba->fetch($contacts)) { $this->out($contact['url']); } - DBA::close($contacts); + $this->dba->close($contacts); } if (count($this->args) == 2) { From c8f02350b6a7a34df73b0b6911dcdb6c112ffe12 Mon Sep 17 00:00:00 2001 From: Michael Date: Wed, 16 Sep 2020 18:38:36 +0000 Subject: [PATCH 3/6] Changed class description --- src/Console/Relay.php | 33 +++++++-------------------------- 1 file changed, 7 insertions(+), 26 deletions(-) diff --git a/src/Console/Relay.php b/src/Console/Relay.php index d23d871f9..8a4c84bef 100644 --- a/src/Console/Relay.php +++ b/src/Console/Relay.php @@ -22,41 +22,25 @@ namespace Friendica\Console; use Asika\SimpleConsole\CommandArgsException; -use Friendica\App; -use Friendica\Database\DBA; use Friendica\Model\APContact; use Friendica\Model\Contact; use Friendica\Protocol\ActivityPub\Transmitter; /** - * tool to access the system config from the CLI + * tool to control the list of ActivityPub relay servers from the CLI * - * With this script you can access the system configuration of your node from - * the CLI. You can do both, reading current values stored in the database and - * set new values to config variables. - * - * Usage: - * If you specify no parameters at the CLI, the script will list all config - * variables defined. - * - * If you specify one parameter, the script will list all config variables - * defined in this section of the configuration (e.g. "system"). - * - * If you specify two parameters, the script will show you the current value - * of the named configuration setting. (e.g. "system loglevel") - * - * If you specify three parameters, the named configuration setting will be - * set to the value of the last parameter. (e.g. "system loglevel 0" will - * disable logging) + * With this script you can access the relay servers of your node from + * the CLI. */ class Relay extends \Asika\SimpleConsole\Console { protected $helpOptions = ['h', 'help', '?']; /** - * @var App\Mode + * @var $dba Friendica\Database\Database */ - private $appMode; + private $dba; + protected function getHelp() { @@ -84,10 +68,7 @@ HELP; return $help; } - /** @var $dba Friendica\Database\Database */ - private $dba; - - public function __construct(Friendica\Database\Database $dba, array $argv = null) + public function __construct(\Friendica\Database\Database $dba, array $argv = null) { parent::__construct($argv); From 5f46b0977217000d5637b1f3eff25eb36cd9a2e7 Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 17 Sep 2020 04:31:38 +0000 Subject: [PATCH 4/6] Added some logging --- src/Protocol/ActivityPub/Receiver.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/Protocol/ActivityPub/Receiver.php b/src/Protocol/ActivityPub/Receiver.php index d5164b5cc..ea5251438 100644 --- a/src/Protocol/ActivityPub/Receiver.php +++ b/src/Protocol/ActivityPub/Receiver.php @@ -181,6 +181,13 @@ class Receiver } Processor::fetchMissingActivity($object_id); + + $item_id = Item::searchByLink($object_id); + if ($item_id) { + Logger::info('Relayed message had been fetched and stored', ['id' => $object_id, 'item' => $item_id]); + } else { + Logger::notice('Relayed message had not been stored', ['id' => $object_id]); + } } /** From 0fe21463386f40d4492ae02f92b1915206de3413 Mon Sep 17 00:00:00 2001 From: Michael Vogel Date: Thu, 17 Sep 2020 06:33:31 +0200 Subject: [PATCH 5/6] Apply suggestions from code review Co-authored-by: Hypolite Petovan --- src/Core/Console.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Core/Console.php b/src/Core/Console.php index 575a67503..4a4dc13ef 100644 --- a/src/Core/Console.php +++ b/src/Core/Console.php @@ -64,7 +64,7 @@ Commands: postupdate Execute pending post update scripts (can last days) serverblock Manage blocked servers storage Manage storage backend - relay Manage ActivityPub relais + relay Manage ActivityPub relay servers Options: -h|--help|-? Show help information From 144ee09c43f1a30fad22f767471e9b2b0683a2f5 Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 17 Sep 2020 04:36:32 +0000 Subject: [PATCH 6/6] Improved description --- src/Console/Relay.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Console/Relay.php b/src/Console/Relay.php index 8a4c84bef..5d7c8b3f7 100644 --- a/src/Console/Relay.php +++ b/src/Console/Relay.php @@ -56,10 +56,10 @@ Description Lists all active relay servers bin/console relay add - Add a relay actor + Add a relay actor in the format https://relayserver.tld/actor bin/console relay remove - Remove a relay actor + Remove a relay actor in the format https://relayserver.tld/actor Options -h|--help|-? Show help information