We can now manage relay servers and can send content to them

This commit is contained in:
Michael 2020-09-15 17:45:19 +00:00
parent ca624ec612
commit 178455928a
6 changed files with 226 additions and 3 deletions

View file

@ -27,6 +27,7 @@ The console provides the following commands:
* typo: Checks for parse errors in Friendica files * typo: Checks for parse errors in Friendica files
* postupdate: Execute pending post update scripts (can last days) * postupdate: Execute pending post update scripts (can last days)
* storage: Manage storage backend * 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. Please consult *bin/console help* on the command line interface of your server for details about the commands.

150
src/Console/Relay.php Normal file
View file

@ -0,0 +1,150 @@
<?php
/**
* @copyright Copyright (C) 2020, Friendica
*
* @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/>.
*
*/
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 = <<<HELP
console relay - Manage ActivityPub relay configuration
Synopsis
bin/console relay [-h|--help|-?] [-v]
bin/console relay add <actor> [-h|--help|-?] [-v]
bin/console relay remove <actoor> [-h|--help|-?] [-v]
Description
bin/console relay
Lists all active relais
bin/console relay add <actor>
Add a relay actor
bin/console relay remove <actoor>
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;
}
}

View file

@ -64,6 +64,7 @@ Commands:
postupdate Execute pending post update scripts (can last days) postupdate Execute pending post update scripts (can last days)
serverblock Manage blocked servers serverblock Manage blocked servers
storage Manage storage backend storage Manage storage backend
relay Manage ActivityPub relais
Options: Options:
-h|--help|-? Show help information -h|--help|-? Show help information
@ -92,6 +93,7 @@ HELP;
'postupdate' => Friendica\Console\PostUpdate::class, 'postupdate' => Friendica\Console\PostUpdate::class,
'serverblock' => Friendica\Console\ServerBlock::class, 'serverblock' => Friendica\Console\ServerBlock::class,
'storage' => Friendica\Console\Storage::class, 'storage' => Friendica\Console\Storage::class,
'relay' => Friendica\Console\Relay::class,
]; ];
/** /**

View file

@ -163,11 +163,13 @@ class Receiver
if ($type != 'as:Announce') { if ($type != 'as:Announce') {
Logger::info('Not an announcement', ['activity' => $activity]); Logger::info('Not an announcement', ['activity' => $activity]);
return;
} }
$object_id = JsonLD::fetchElement($activity, 'as:object', '@id'); $object_id = JsonLD::fetchElement($activity, 'as:object', '@id');
if (empty($object_id)) { if (empty($object_id)) {
Logger::info('No object id found', ['activity' => $activity]); Logger::info('No object id found', ['activity' => $activity]);
return;
} }
Logger::info('Got relayed message id', ['id' => $object_id]); Logger::info('Got relayed message id', ['id' => $object_id]);

View file

@ -61,6 +61,68 @@ require_once 'mod/share.php';
*/ */
class Transmitter 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 * Collects a list of contacts of the given owner
* *
@ -1917,18 +1979,19 @@ class Transmitter
* @throws \Friendica\Network\HTTPException\InternalServerErrorException * @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException * @throws \ImagickException
* @throws \Exception * @throws \Exception
* @return bool success
*/ */
public static function sendContactUndo($target, $cid, $uid) public static function sendContactUndo($target, $cid, $uid)
{ {
$profile = APContact::getByURL($target); $profile = APContact::getByURL($target);
if (empty($profile['inbox'])) { if (empty($profile['inbox'])) {
Logger::warning('No inbox found for target', ['target' => $target, 'profile' => $profile]); Logger::warning('No inbox found for target', ['target' => $target, 'profile' => $profile]);
return; return false;
} }
$object_id = self::activityIDFromContact($cid); $object_id = self::activityIDFromContact($cid);
if (empty($object_id)) { if (empty($object_id)) {
return; return false;
} }
$id = DI::baseUrl() . '/activity/' . System::createGUID(); $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); Logger::log('Sending undo to ' . $target . ' for user ' . $uid . ' with id ' . $id, Logger::DEBUG);
$signed = LDSignature::sign($data, $owner); $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) private static function prependMentions($body, int $uriid)

View file

@ -786,6 +786,11 @@ class Notifier
if ($target_item['origin']) { if ($target_item['origin']) {
$inboxes = ActivityPub\Transmitter::fetchTargetInboxes($target_item, $uid); $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); Logger::log('Origin item ' . $target_item['id'] . ' with URL ' . $target_item['uri'] . ' will be distributed.', Logger::DEBUG);
} elseif (Item::isForumPost($target_item, $owner)) { } elseif (Item::isForumPost($target_item, $owner)) {
$inboxes = ActivityPub\Transmitter::fetchTargetInboxes($target_item, $uid, false, 0, true); $inboxes = ActivityPub\Transmitter::fetchTargetInboxes($target_item, $uid, false, 0, true);