diff --git a/src/Model/Post/User.php b/src/Model/Post/User.php index 9da9718068..7bd5706997 100644 --- a/src/Model/Post/User.php +++ b/src/Model/Post/User.php @@ -24,7 +24,6 @@ namespace Friendica\Model\Post; use Friendica\Database\DBA; use \BadMethodCallException; use Friendica\Database\Database; -use Friendica\Database\DBStructure; use Friendica\DI; class User @@ -44,10 +43,6 @@ class User throw new BadMethodCallException('Empty URI_id'); } - if (DBA::exists('post-user', ['uri-id' => $uri_id, 'uid' => $uid])) { - return false; - } - $fields = DI::dbaDefinition()->truncateFieldsForTable('post-user', $data); // Additionally assign the key fields @@ -59,6 +54,33 @@ class User $fields['unseen'] = false; } + // Does the entry already exist? + if (DBA::exists('post-user', ['uri-id' => $uri_id, 'uid' => $uid])) { + $postuser = DBA::selectFirst('post-user', [], ['uri-id' => $uri_id, 'uid' => $uid]); + + // We quit here, when there are obvious differences + foreach (['created', 'owner-id', 'author-id', 'vid', 'network', 'private', 'wall', 'origin'] as $key) { + if ($fields[$key] != $postuser[$key]) { + return 0; + } + } + + $update = []; + foreach (['gravity', 'parent-uri-id', 'thr-parent-id'] as $key) { + if ($fields[$key] != $postuser[$key]) { + $update[$key] = $fields[$key]; + } + } + + // When the parents changed, we apply these changes to the existing entry + if (!empty($update)) { + DBA::update('post-user', $update, ['id' => $postuser['id']]); + return $postuser['id']; + } else { + return 0; + } + } + if (!DBA::insert('post-user', $fields, Database::INSERT_IGNORE)) { return 0; } diff --git a/src/Protocol/ActivityPub/Processor.php b/src/Protocol/ActivityPub/Processor.php index 9d5f3f2f6e..d1c3994e2a 100644 --- a/src/Protocol/ActivityPub/Processor.php +++ b/src/Protocol/ActivityPub/Processor.php @@ -45,7 +45,9 @@ use Friendica\Protocol\Activity; use Friendica\Protocol\ActivityPub; use Friendica\Protocol\Relay; use Friendica\Util\DateTimeFormat; +use Friendica\Util\HTTPSignature; use Friendica\Util\JsonLD; +use Friendica\Util\Network; use Friendica\Util\Strings; use Friendica\Worker\Delivery; @@ -301,6 +303,11 @@ class Processor Logger::notice('Parent not found. Try to refetch it.', ['parent' => $activity['reply-to-id'], 'recursion-depth' => $recursion_depth]); if ($recursion_depth < 10) { $result = self::fetchMissingActivity($activity['reply-to-id'], $activity, '', Receiver::COMPLETION_AUTO); + if (empty($result) && self::ActivityIsGone($activity['reply-to-id'])) { + // Recursively delete this and all depending entries + Queue::deleteById($activity['entry-id']); + return []; + } $fetch_by_worker = empty($result); } else { Logger::notice('Recursion level is too high.', ['parent' => $activity['reply-to-id'], 'recursion-depth' => $recursion_depth]); @@ -452,6 +459,24 @@ class Processor return $item; } + /** + * Check if a given activity is no longer available + * + * @param string $url + * + * @return boolean + */ + private static function ActivityIsGone(string $url): bool + { + $curlResult = HTTPSignature::fetchRaw($url, 0); + + if (Network::isUrlBlocked($url)) { + return true; + } + + // @todo To ensure that the remote system is working correctly, we can check if the "Content-Type" contains JSON + return in_array($curlResult->getReturnCode(), [404]); + } /** * Delete items * @@ -933,6 +958,9 @@ class Processor $success = true; } else { Logger::notice('Item insertion aborted', ['uri' => $item['uri'], 'uid' => $item['uid']]); + if (Item::isTooOld($item) || !Item::isValid($item)) { + Queue::remove($activity); + } } if ($item['uid'] == 0) { diff --git a/src/Protocol/ActivityPub/Queue.php b/src/Protocol/ActivityPub/Queue.php index 3d40d71638..7b91609bdb 100644 --- a/src/Protocol/ActivityPub/Queue.php +++ b/src/Protocol/ActivityPub/Queue.php @@ -116,7 +116,7 @@ class Queue * @param integer $id * @return void */ - private static function deleteById(int $id) + public static function deleteById(int $id) { $entry = DBA::selectFirst('inbox-entry', ['id', 'object-id'], ['id' => $id]); if (empty($entry)) { @@ -206,7 +206,7 @@ class Queue $entries = DBA::select('inbox-entry', ['id', 'type', 'object-type', 'object-id', 'in-reply-to-id'], ["`wid` IS NULL"], ['order' => ['id' => true]]); while ($entry = DBA::fetch($entries)) { // We don't need to process entries that depend on already existing entries. - if (!empty($entry['in-reply-to-id']) && DBA::exists('inbox-entry', ['object-id' => $entry['in-reply-to-id']])) { + if (!empty($entry['in-reply-to-id']) && DBA::exists('inbox-entry', ["`id` != ? AND `object-id` = ?", $entry['id'], $entry['in-reply-to-id']])) { continue; } Logger::debug('Process leftover entry', $entry); diff --git a/src/Worker/Cron.php b/src/Worker/Cron.php index c2109e66a3..2ace97d830 100644 --- a/src/Worker/Cron.php +++ b/src/Worker/Cron.php @@ -92,6 +92,9 @@ class Cron // Remove old pending posts from the queue Queue::clear(); + // Process all unprocessed entries + Queue::processAll(); + // Search for new contacts in the directory if (DI::config()->get('system', 'synchronize_directory')) { Worker::add(PRIORITY_LOW, 'PullDirectory'); @@ -129,9 +132,6 @@ class Cron Worker::add(PRIORITY_LOW, 'OptimizeTables'); } - // Process all unprocessed entries - Queue::processAll(); - // Resubscribe to relay servers Relay::reSubscribe();