<?php
/**
- * @copyright Copyright (C) 2010-2022, the Friendica project
+ * @copyright Copyright (C) 2010-2023, the Friendica project
*
* @license GNU AGPL version 3 or any later version
*
use Friendica\Model\Post;
use Friendica\Protocol\Activity;
use Friendica\Protocol\ActivityPub;
+use Friendica\Protocol\Delivery;
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;
/**
* ActivityPub Processor Protocol class
*/
public static function normalizeMentionLinks(string $body): string
{
- return preg_replace('%\[url=([^\[\]]*)]([#@!])(.*?)\[/url]%ism', '$2[url=$1]$3[/url]', $body);
+ $body = preg_replace('%\[url=([^\[\]]*)]([#@!])(.*?)\[/url]%ism', '$2[url=$1]$3[/url]', $body);
+ $body = preg_replace('%([#@!])\[zrl=([^\[\]]*)](.*?)\[/zrl]%ism', '$1[url=$2]$3[/url]', $body);
+ return $body;
}
/**
$item['changed'] = DateTimeFormat::utcNow();
$item['edited'] = DateTimeFormat::utc($activity['updated']);
+ Post\Media::deleteByURIId($item['uri-id'], [Post\Media::AUDIO, Post\Media::VIDEO, Post\Media::IMAGE, Post\Media::HTML]);
$item = self::processContent($activity, $item);
if (empty($item)) {
Queue::remove($activity);
$recursion_depth = $activity['recursion-depth'] ?? 0;
if (!$in_background && ($recursion_depth < DI::config()->get('system', 'max_recursion_depth'))) {
- Logger::notice('Parent not found. Try to refetch it.', ['parent' => $activity['reply-to-id'], 'recursion-depth' => $recursion_depth]);
+ Logger::info('Parent not found. Try to refetch it.', ['parent' => $activity['reply-to-id'], 'recursion-depth' => $recursion_depth]);
$result = self::fetchMissingActivity($activity['reply-to-id'], $activity, '', Receiver::COMPLETION_AUTO);
if (empty($result) && self::isActivityGone($activity['reply-to-id'])) {
Logger::notice('The activity is gone, the queue entry will be deleted', ['parent' => $activity['reply-to-id']]);
} elseif (!empty($result)) {
$exists = Post::exists(['uri' => [$result, $activity['reply-to-id']]]);
if ($exists) {
- Logger::notice('The activity has been fetched and created.', ['parent' => $result]);
+ Logger::info('The activity has been fetched and created.', ['parent' => $result]);
return $result;
} elseif (DI::config()->get('system', 'fetch_by_worker') || DI::config()->get('system', 'decoupled_receiver')) {
- Logger::notice('The activity has been fetched and will hopefully be created later.', ['parent' => $result]);
+ Logger::info('The activity has been fetched and will hopefully be created later.', ['parent' => $result]);
} else {
Logger::notice('The activity exists but has not been created, the queue entry will be deleted.', ['parent' => $result]);
if (!empty($activity['entry-id'])) {
*/
public static function isActivityGone(string $url): bool
{
- $curlResult = HTTPSignature::fetchRaw($url, 0);
+ try {
+ $curlResult = HTTPSignature::fetchRaw($url, 0);
+ } catch (\Exception $exception) {
+ Logger::notice('Error fetching url', ['url' => $url, 'exception' => $exception]);
+ return true;
+ }
if (Network::isUrlBlocked($url)) {
return true;
if ($id) {
$shared_item = Post::selectFirst(['uri-id'], ['id' => $id]);
$item['quote-uri-id'] = $shared_item['uri-id'];
+ } elseif ($uri_id = ItemURI::getIdByURI($activity['quote-url'], false)) {
+ Logger::info('Quote was not fetched but the uri-id existed', ['guid' => $item['guid'], 'uri-id' => $item['uri-id'], 'quote' => $activity['quote-url'], 'uri-id' => $uri_id]);
+ $item['quote-uri-id'] = $uri_id;
} else {
Logger::info('Quote was not fetched', ['guid' => $item['guid'], 'uri-id' => $item['uri-id'], 'quote' => $activity['quote-url']]);
}
Logger::warning('Unknown parent item.', ['uri' => $parent_uri]);
return false;
}
- if (!empty($activity['type']) && in_array($activity['type'], Receiver::CONTENT_TYPES) && ($item['private'] == Item::PRIVATE) && ($parent['private'] != Item::PRIVATE)) {
- Logger::warning('Item is private but the parent is not. Dropping.', ['item-uri' => $item['uri'], 'thr-parent' => $item['thr-parent']]);
- return false;
- }
-
$content = self::removeImplicitMentionsFromBody($content, $parent);
}
$item['content-warning'] = HTML::toBBCode($activity['summary'] ?? '');
return true;
}
- if (in_array($activity['completion-mode'] ?? Receiver::COMPLETION_NONE, [Receiver::COMPLETION_MANUAL, Receiver::COMPLETION_ANNOUCE])) {
+ if (in_array($activity['completion-mode'] ?? Receiver::COMPLETION_NONE, [Receiver::COMPLETION_MANUAL, Receiver::COMPLETION_ANNOUNCE])) {
// Manual completions and completions caused by reshares are allowed without any further checks.
Logger::debug('Message is in completion mode - accepted', ['mode' => $activity['completion-mode'], 'uri-id' => $item['uri-id'], 'guid' => $item['guid'], 'url' => $item['uri']]);
return true;
case Receiver::TARGET_BCC:
$item['post-reason'] = Item::PR_BCC;
break;
+ case Receiver::TARGET_AUDIENCE:
+ $item['post-reason'] = Item::PR_AUDIENCE;
+ break;
case Receiver::TARGET_FOLLOWER:
$item['post-reason'] = Item::PR_FOLLOWER;
break;
continue;
}
- if (($receiver != 0) && ($item['gravity'] == Item::GRAVITY_PARENT) && !in_array($item['post-reason'], [Item::PR_FOLLOWER, Item::PR_TAG, item::PR_TO, Item::PR_CC])) {
+ if (($receiver != 0) && ($item['gravity'] == Item::GRAVITY_PARENT) && !in_array($item['post-reason'], [Item::PR_FOLLOWER, Item::PR_TAG, item::PR_TO, Item::PR_CC, Item::PR_AUDIENCE])) {
if (!($item['isForum'] ?? false)) {
if ($item['post-reason'] == Item::PR_BCC) {
Logger::info('Top level post via BCC from a non sharer, ignoring', ['uid' => $receiver, 'contact' => $item['contact-id'], 'url' => $item['uri']]);
public static function storeReceivers(int $uriid, array $receivers)
{
- foreach (['as:to' => Tag::TO, 'as:cc' => Tag::CC, 'as:bto' => Tag::BTO, 'as:bcc' => Tag::BCC] as $element => $type) {
+ foreach (['as:to' => Tag::TO, 'as:cc' => Tag::CC, 'as:bto' => Tag::BTO, 'as:bcc' => Tag::BCC, 'as:audience' => Tag::AUDIENCE, 'as:attributedTo' => Tag::ATTRIBUTED] as $element => $type) {
if (!empty($receivers[$element])) {
foreach ($receivers[$element] as $receiver) {
if ($receiver == ActivityPub::PUBLIC_COLLECTION) {
$name = Receiver::PUBLIC_COLLECTION;
} elseif ($path = parse_url($receiver, PHP_URL_PATH)) {
$name = trim($path, '/');
+ } elseif ($host = parse_url($receiver, PHP_URL_HOST)) {
+ $name = $host;
} else {
- Logger::warning('Unable to coerce name from receiver', ['receiver' => $receiver]);
+ Logger::warning('Unable to coerce name from receiver', ['element' => $element, 'type' => $type, 'receiver' => $receiver]);
$name = '';
}
* @param array $child activity array with the child of this message
* @param string $relay_actor Relay actor
* @param int $completion Completion mode, see Receiver::COMPLETION_*
+ * @param int $uid User id that is used to fetch the activity
* @return string fetched message URL
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
* @throws \ImagickException
*/
- public static function fetchMissingActivity(string $url, array $child = [], string $relay_actor = '', int $completion = Receiver::COMPLETION_MANUAL): string
+ public static function fetchMissingActivity(string $url, array $child = [], string $relay_actor = '', int $completion = Receiver::COMPLETION_MANUAL, int $uid = 0): string
{
- $object = self::fetchCachedActivity($url, 0);
+ $object = self::fetchCachedActivity($url, $uid);
if (empty($object)) {
return '';
}
$ldactivity['recursion-depth'] = !empty($child['recursion-depth']) ? $child['recursion-depth'] + 1 : 0;
- if (!empty($relay_actor)) {
- $ldactivity['thread-completion'] = $ldactivity['from-relay'] = Contact::getIdForURL($relay_actor);
- $ldactivity['completion-mode'] = Receiver::COMPLETION_RELAY;
- } elseif (!empty($child['thread-completion'])) {
+ if ($object_actor != $actor) {
+ Contact::updateByUrlIfNeeded($object_actor);
+ }
+
+ Contact::updateByUrlIfNeeded($actor);
+
+ if (!empty($child['thread-completion'])) {
$ldactivity['thread-completion'] = $child['thread-completion'];
$ldactivity['completion-mode'] = $child['completion-mode'] ?? Receiver::COMPLETION_NONE;
} else {
- $ldactivity['thread-completion'] = Contact::getIdForURL($actor);
+ $ldactivity['thread-completion'] = Contact::getIdForURL($relay_actor ?: $actor);
$ldactivity['completion-mode'] = $completion;
}
+ if ($completion == Receiver::COMPLETION_RELAY) {
+ $ldactivity['from-relay'] = $ldactivity['thread-completion'];
+ if (!self::acceptIncomingMessage($ldactivity, $object['id'])) {
+ return '';
+ }
+ }
+
if (!empty($child['thread-children-type'])) {
$ldactivity['thread-children-type'] = $child['thread-children-type'];
} elseif (!empty($child['type'])) {
$ldactivity['thread-children-type'] = 'as:Create';
}
- if (!empty($relay_actor) && !self::acceptIncomingMessage($ldactivity, $object['id'])) {
- return '';
- }
-
if (($completion == Receiver::COMPLETION_RELAY) && Queue::exists($url, 'as:Create')) {
- Logger::notice('Activity has already been queued.', ['url' => $url, 'object' => $activity['id']]);
- } elseif (ActivityPub\Receiver::processActivity($ldactivity, json_encode($activity), 0, true, false, $signer, '', $completion)) {
- Logger::notice('Activity had been fetched and processed.', ['url' => $url, 'entry' => $child['entry-id'] ?? 0, 'completion' => $completion, 'object' => $activity['id']]);
+ Logger::info('Activity has already been queued.', ['url' => $url, 'object' => $activity['id']]);
+ } elseif (ActivityPub\Receiver::processActivity($ldactivity, json_encode($activity), $uid, true, false, $signer, '', $completion)) {
+ Logger::info('Activity had been fetched and processed.', ['url' => $url, 'entry' => $child['entry-id'] ?? 0, 'completion' => $completion, 'object' => $activity['id']]);
} else {
- Logger::notice('Activity had been fetched and will be processed later.', ['url' => $url, 'entry' => $child['entry-id'] ?? 0, 'completion' => $completion, 'object' => $activity['id']]);
+ Logger::info('Activity had been fetched and will be processed later.', ['url' => $url, 'entry' => $child['entry-id'] ?? 0, 'completion' => $completion, 'object' => $activity['id']]);
}
return $activity['id'];