diff --git a/database.sql b/database.sql index b37036762e..3ca42e3377 100644 --- a/database.sql +++ b/database.sql @@ -1,6 +1,6 @@ -- ------------------------------------------ -- Friendica 2020.12-dev (Red Hot Poker) --- DB_UPDATE_VERSION 1378 +-- DB_UPDATE_VERSION 1379 -- ------------------------------------------ @@ -332,6 +332,7 @@ CREATE TABLE IF NOT EXISTS `apcontact` ( INDEX `alias` (`alias`(190)), INDEX `followers` (`followers`(190)), INDEX `baseurl` (`baseurl`(190)), + INDEX `sharedinbox` (`sharedinbox`(190)), INDEX `gsid` (`gsid`), FOREIGN KEY (`gsid`) REFERENCES `gserver` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='ActivityPub compatible contacts - used in the ActivityPub implementation'; @@ -1057,7 +1058,7 @@ CREATE TABLE IF NOT EXISTS `photo` ( INDEX `uid_album_scale_created` (`uid`,`album`(32),`scale`,`created`), INDEX `uid_album_resource-id_created` (`uid`,`album`(32),`resource-id`,`created`), INDEX `resource-id` (`resource-id`), - FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE CASCADE, + FOREIGN KEY (`uid`) REFERENCES `user` (`uid`) ON UPDATE RESTRICT ON DELETE RESTRICT, FOREIGN KEY (`contact-id`) REFERENCES `contact` (`id`) ON UPDATE RESTRICT ON DELETE RESTRICT ) DEFAULT COLLATE utf8mb4_general_ci COMMENT='photo storage'; diff --git a/src/Model/APContact.php b/src/Model/APContact.php index 6a8e5b3ae1..37faf240ce 100644 --- a/src/Model/APContact.php +++ b/src/Model/APContact.php @@ -32,6 +32,7 @@ use Friendica\Protocol\ActivityNamespace; use Friendica\Protocol\ActivityPub; use Friendica\Util\Crypto; use Friendica\Util\DateTimeFormat; +use Friendica\Util\HTTPSignature; use Friendica\Util\JsonLD; use Friendica\Util\Network; @@ -148,6 +149,7 @@ class APContact $data = ActivityPub::fetchContent($url); if (empty($data)) { + self::markForArchival($fetched_contact ?: []); return $fetched_contact; } @@ -350,10 +352,54 @@ class APContact return $apcontact; } + /** + * Mark the given AP Contact as "to archive" + * + * @param array $apcontact + * @return void + */ + public static function markForArchival(array $apcontact) + { + if (!empty($apcontact['inbox'])) { + Logger::info('Set inbox status to failure', ['inbox' => $apcontact['inbox']]); + HTTPSignature::setInboxStatus($apcontact['inbox'], false); + } + + if (!empty($apcontact['sharedinbox'])) { + // Check if there are any available inboxes + $available = DBA::exists('apcontact', ["`sharedinbox` = ? AnD `inbox` IN (SELECT `url` FROM `inbox-status` WHERE `success` > `failure`)", + $apcontact['sharedinbox']]); + if (!$available) { + // If all known personal inboxes are failing then set their shared inbox to failure as well + Logger::info('Set shared inbox status to failure', ['sharedinbox' => $apcontact['sharedinbox']]); + HTTPSignature::setInboxStatus($apcontact['sharedinbox'], false, true); + } + } + } + + /** + * Unmark the given AP Contact as "to archive" + * + * @param array $apcontact + * @return void + */ + public static function unmarkForArchival(array $apcontact) + { + if (!empty($apcontact['inbox'])) { + Logger::info('Set inbox status to success', ['inbox' => $apcontact['inbox']]); + HTTPSignature::setInboxStatus($apcontact['inbox'], true); + } + if (!empty($apcontact['sharedinbox'])) { + Logger::info('Set shared inbox status to success', ['sharedinbox' => $apcontact['sharedinbox']]); + HTTPSignature::setInboxStatus($apcontact['sharedinbox'], true, true); + } + } + /** * Unarchive inboxes * - * @param string $url inbox url + * @param string $url inbox url + * @param boolean $shared Shared Inbox */ private static function unarchiveInbox($url, $shared) { @@ -361,15 +407,6 @@ class APContact return; } - $now = DateTimeFormat::utcNow(); - - $fields = ['archive' => false, 'success' => $now, 'shared' => $shared]; - - if (!DBA::exists('inbox-status', ['url' => $url])) { - $fields = array_merge($fields, ['url' => $url, 'created' => $now]); - DBA::replace('inbox-status', $fields); - } else { - DBA::update('inbox-status', $fields, ['url' => $url]); - } + HTTPSignature::setInboxStatus($url, true, $shared); } } diff --git a/src/Protocol/ActivityPub/Receiver.php b/src/Protocol/ActivityPub/Receiver.php index 3c0f4862e6..d1426ff5ae 100644 --- a/src/Protocol/ActivityPub/Receiver.php +++ b/src/Protocol/ActivityPub/Receiver.php @@ -104,6 +104,8 @@ class Receiver return; } + APContact::unMarkForArchival($apcontact); + $http_signer = HTTPSignature::getSigner($body, $header); if (empty($http_signer)) { Logger::warning('Invalid HTTP signature, message will be discarded.'); @@ -233,6 +235,7 @@ class Receiver $profile = APContact::getByURL($object_id); if (!empty($profile['type'])) { + APContact::unMarkForArchival($profile); return 'as:' . $profile['type']; } diff --git a/src/Protocol/ActivityPub/Transmitter.php b/src/Protocol/ActivityPub/Transmitter.php index 4c2977ec7f..7f5f51c38a 100644 --- a/src/Protocol/ActivityPub/Transmitter.php +++ b/src/Protocol/ActivityPub/Transmitter.php @@ -670,7 +670,7 @@ class Transmitter * * @return boolean "true" if inbox is archived */ - private static function archivedInbox($url) + public static function archivedInbox($url) { return DBA::exists('inbox-status', ['url' => $url, 'archive' => true]); } diff --git a/src/Util/HTTPSignature.php b/src/Util/HTTPSignature.php index c5507adf3f..5b7bb02a3e 100644 --- a/src/Util/HTTPSignature.php +++ b/src/Util/HTTPSignature.php @@ -314,14 +314,15 @@ class HTTPSignature * * @param string $url The URL of the inbox * @param boolean $success Transmission status + * @param boolean $shared The inbox is a shared inbox */ - static private function setInboxStatus($url, $success) + static public function setInboxStatus($url, $success, $shared = false) { $now = DateTimeFormat::utcNow(); $status = DBA::selectFirst('inbox-status', [], ['url' => $url]); if (!DBA::isResult($status)) { - DBA::insert('inbox-status', ['url' => $url, 'created' => $now]); + DBA::insert('inbox-status', ['url' => $url, 'created' => $now, 'shared' => $shared]); $status = DBA::selectFirst('inbox-status', [], ['url' => $url]); } diff --git a/src/Worker/APDelivery.php b/src/Worker/APDelivery.php index 92767a1058..a25977372a 100644 --- a/src/Worker/APDelivery.php +++ b/src/Worker/APDelivery.php @@ -42,7 +42,16 @@ class APDelivery */ public static function execute($cmd, $target_id, $inbox, $uid) { - Logger::log('Invoked: ' . $cmd . ': ' . $target_id . ' to ' . $inbox, Logger::DEBUG); + if (ActivityPub\Transmitter::archivedInbox($inbox)) { + Logger::info('Inbox is archived', ['cmd' => $cmd, 'inbox' => $inbox, 'id' => $target_id, 'uid' => $uid]); + if (in_array($cmd, [Delivery::POST])) { + $item = Item::selectFirst(['uri-id'], ['id' => $target_id]); + Post\DeliveryData::incrementQueueFailed($item['uri-id'] ?? 0); + } + return; + } + + Logger::info('Invoked', ['cmd' => $cmd, 'inbox' => $inbox, 'id' => $target_id, 'uid' => $uid]); $success = true; diff --git a/src/Worker/Delivery.php b/src/Worker/Delivery.php index 44041957fc..8e49ee4715 100644 --- a/src/Worker/Delivery.php +++ b/src/Worker/Delivery.php @@ -193,9 +193,9 @@ class Delivery return; } - // We don't deliver our items to blocked or pending contacts, and not to ourselves either + // We don't deliver our items to blocked, archived or pending contacts, and not to ourselves either $contact = DBA::selectFirst('contact', [], - ['id' => $contact_id, 'blocked' => false, 'pending' => false, 'self' => false] + ['id' => $contact_id, 'archive' => false, 'blocked' => false, 'pending' => false, 'self' => false] ); if (!DBA::isResult($contact)) { self::setFailedQueue($cmd, $target_item); diff --git a/static/dbstructure.config.php b/static/dbstructure.config.php index caec82625f..3239c8e7ca 100644 --- a/static/dbstructure.config.php +++ b/static/dbstructure.config.php @@ -55,7 +55,7 @@ use Friendica\Database\DBA; if (!defined('DB_UPDATE_VERSION')) { - define('DB_UPDATE_VERSION', 1378); + define('DB_UPDATE_VERSION', 1379); } return [ @@ -395,6 +395,7 @@ return [ "alias" => ["alias(190)"], "followers" => ["followers(190)"], "baseurl" => ["baseurl(190)"], + "sharedinbox" => ["sharedinbox(190)"], "gsid" => ["gsid"] ] ], @@ -1081,7 +1082,7 @@ return [ "comment" => "photo storage", "fields" => [ "id" => ["type" => "int unsigned", "not null" => "1", "extra" => "auto_increment", "primary" => "1", "comment" => "sequential ID"], - "uid" => ["type" => "mediumint unsigned", "not null" => "1", "default" => "0", "foreign" => ["user" => "uid"], "comment" => "Owner User id"], + "uid" => ["type" => "mediumint unsigned", "not null" => "1", "default" => "0", "foreign" => ["user" => "uid", "on delete" => "restrict"], "comment" => "Owner User id"], "contact-id" => ["type" => "int unsigned", "not null" => "1", "default" => "0", "foreign" => ["contact" => "id", "on delete" => "restrict"], "comment" => "contact.id"], "guid" => ["type" => "char(16)", "not null" => "1", "default" => "", "comment" => "A unique identifier for this photo"], "resource-id" => ["type" => "char(32)", "not null" => "1", "default" => "", "comment" => ""],