X-Git-Url: https://git.mxchange.org/?a=blobdiff_plain;f=src%2FProtocol%2FActivityPub%2FProcessor.php;h=c1ab6bc1af58cdd5381f0e1584d33ebf57493951;hb=15535811752e5b3cb548dcbe1abe5d9c98c276b0;hp=3bb0639ab05cd349931475695d2115b03818cad0;hpb=8af0ae171e7a3f9f47441043cec17db66b2088e5;p=friendica.git diff --git a/src/Protocol/ActivityPub/Processor.php b/src/Protocol/ActivityPub/Processor.php index 3bb0639ab0..c1ab6bc1af 100644 --- a/src/Protocol/ActivityPub/Processor.php +++ b/src/Protocol/ActivityPub/Processor.php @@ -5,8 +5,10 @@ namespace Friendica\Protocol\ActivityPub; use Friendica\Database\DBA; +use Friendica\Content\Text\BBCode; use Friendica\Content\Text\HTML; use Friendica\Core\Config; +use Friendica\Core\PConfig; use Friendica\Core\Logger; use Friendica\Core\Protocol; use Friendica\Model\Contact; @@ -15,6 +17,7 @@ use Friendica\Model\Item; use Friendica\Model\Event; use Friendica\Model\Term; use Friendica\Model\User; +use Friendica\Model\Mail; use Friendica\Protocol\ActivityPub; use Friendica\Util\DateTimeFormat; use Friendica\Util\JsonLD; @@ -62,10 +65,9 @@ class Processor * * @param array $tags * @param boolean $sensitive - * @param array $implicit_mentions List of profile URLs to skip * @return string with tags */ - private static function constructTagString(array $tags, $sensitive) + private static function constructTagString(array $tags = null, $sensitive = false) { if (empty($tags)) { return ''; @@ -90,12 +92,13 @@ class Processor /** * Add attachment data to the item array * - * @param array $attachments - * @param array $item + * @param array $attachments + * @param array $item + * @param boolean $no_images * * @return array array */ - private static function constructAttachList($attachments, $item) + private static function constructAttachList($attachments, $item, $no_images) { if (empty($attachments)) { return $item; @@ -104,6 +107,10 @@ class Processor foreach ($attachments as $attach) { $filetype = strtolower(substr($attach['mediaType'], 0, strpos($attach['mediaType'], '/'))); if ($filetype == 'image') { + if ($no_images) { + continue; + } + $item['body'] .= "\n[img]" . $attach['url'] . '[/img]'; } else { if (!empty($item["attach"])) { @@ -136,7 +143,7 @@ class Processor } $item['changed'] = DateTimeFormat::utcNow(); - $item['edited'] = $activity['updated']; + $item['edited'] = DateTimeFormat::utc($activity['updated']); $item = self::processContent($activity, $item); if (empty($item)) { @@ -167,7 +174,7 @@ class Processor $item['object-type'] = ACTIVITY_OBJ_COMMENT; } - if (($activity['id'] != $activity['reply-to-id']) && !Item::exists(['uri' => $activity['reply-to-id']])) { + if (empty($activity['directmessage']) && ($activity['id'] != $activity['reply-to-id']) && !Item::exists(['uri' => $activity['reply-to-id']])) { Logger::log('Parent ' . $activity['reply-to-id'] . ' not found. Try to refetch it.'); self::fetchMissingActivity($activity['reply-to-id'], $activity); } @@ -192,6 +199,43 @@ class Processor Item::delete(['uri' => $activity['object_id'], 'owner-id' => $owner]); } + /** + * Prepare the item array for an activity + * + * @param array $activity Activity array + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + * @throws \ImagickException + */ + public static function addTag($activity) + { + if (empty($activity['object_content']) || empty($activity['object_id'])) { + return; + } + + foreach ($activity['receiver'] as $receiver) { + $item = Item::selectFirst(['id', 'tag', 'origin', 'author-link'], ['uri' => $activity['target_id'], 'uid' => $receiver]); + if (!DBA::isResult($item)) { + // We don't fetch missing content for this purpose + continue; + } + + if (($item['author-link'] != $activity['actor']) && !$item['origin']) { + Logger::info('Not origin, not from the author, skipping update', ['id' => $item['id'], 'author' => $item['author-link'], 'actor' => $activity['actor']]); + continue; + } + + // To-Do: + // - Check if "blocktag" is set + // - Check if actor is a contact + + if (!stristr($item['tag'], trim($activity['object_content']))) { + $tag = $item['tag'] . (strlen($item['tag']) ? ',' : '') . '#[url=' . $activity['object_id'] . ']'. $activity['object_content'] . '[/url]'; + Item::update(['tag' => $tag], ['id' => $item['id']]); + Logger::info('Tagged item', ['id' => $item['id'], 'tag' => $activity['object_content'], 'uri' => $activity['target_id'], 'actor' => $activity['actor']]); + } + } + } + /** * Prepare the item array for an activity * @@ -270,7 +314,7 @@ class Processor $content = self::convertMentions($content); - if (($item['thr-parent'] != $item['uri']) && ($item['gravity'] == GRAVITY_COMMENT)) { + if (empty($activity['directmessage']) && ($item['thr-parent'] != $item['uri']) && ($item['gravity'] == GRAVITY_COMMENT)) { $item_private = !in_array(0, $activity['item_receiver']); $parent = Item::selectFirst(['id', 'private', 'author-link', 'alias'], ['uri' => $item['thr-parent']]); if (!DBA::isResult($parent)) { @@ -318,8 +362,7 @@ class Processor private static function postItem($activity, $item) { /// @todo What to do with $activity['context']? - - if (($item['gravity'] != GRAVITY_PARENT) && !Item::exists(['uri' => $item['thr-parent']])) { + if (empty($activity['directmessage']) && ($item['gravity'] != GRAVITY_PARENT) && !Item::exists(['uri' => $item['thr-parent']])) { Logger::info('Parent not found, message will be discarded.', ['thr-parent' => $item['thr-parent']]); return; } @@ -328,20 +371,28 @@ class Processor $item['private'] = !in_array(0, $activity['receiver']); $item['author-link'] = $activity['author']; $item['author-id'] = Contact::getIdForURL($activity['author'], 0, true); + $item['owner-link'] = $activity['actor']; + $item['owner-id'] = Contact::getIdForURL($activity['actor'], 0, true); - if (empty($activity['thread-completion'])) { - $item['owner-link'] = $activity['actor']; - $item['owner-id'] = Contact::getIdForURL($activity['actor'], 0, true); - } else { - Logger::info('Ignoring actor because of thread completion.'); + $isForum = false; + + if (!empty($activity['thread-completion'])) { + // Store the original actor in the "causer" fields to enable the check for ignored or blocked contacts + $item['causer-link'] = $item['owner-link']; + $item['causer-id'] = $item['owner-id']; + + Logger::info('Ignoring actor because of thread completion.', ['actor' => $item['owner-link']]); $item['owner-link'] = $item['author-link']; $item['owner-id'] = $item['author-id']; + } else { + $actor = APContact::getByURL($item['owner-link'], false); + $isForum = ($actor['type'] == 'Group'); } $item['uri'] = $activity['id']; - $item['created'] = $activity['published']; - $item['edited'] = $activity['updated']; + $item['created'] = DateTimeFormat::utc($activity['published']); + $item['edited'] = DateTimeFormat::utc($activity['updated']); $item['guid'] = $activity['diaspora:guid']; $item = self::processContent($activity, $item); @@ -351,18 +402,45 @@ class Processor $item['plink'] = defaults($activity, 'alternate-url', $item['uri']); - $item = self::constructAttachList($activity['attachments'], $item); + $item = self::constructAttachList($activity['attachments'], $item, !empty($activity['source'])); $stored = false; foreach ($activity['receiver'] as $receiver) { $item['uid'] = $receiver; - $item['contact-id'] = Contact::getIdForURL($activity['author'], $receiver, true); + + if ($isForum) { + $item['contact-id'] = Contact::getIdForURL($activity['actor'], $receiver, true); + } + + if (empty($item['contact-id'])) { + $item['contact-id'] = Contact::getIdForURL($activity['author'], $receiver, true); + } if (($receiver != 0) && empty($item['contact-id'])) { $item['contact-id'] = Contact::getIdForURL($activity['author'], 0, true); } + if (!empty($activity['directmessage'])) { + self::postMail($activity, $item); + continue; + } + + if (PConfig::get($receiver, 'system', 'accept_only_sharer', false) && ($receiver != 0) && ($item['gravity'] == GRAVITY_PARENT)) { + $skip = !Contact::isSharingByURL($activity['author'], $receiver); + + if ($skip && (($activity['type'] == 'as:Announce') || $isForum)) { + $skip = !Contact::isSharingByURL($activity['actor'], $receiver); + } + + if ($skip) { + Logger::info('Skipping post', ['uid' => $receiver, 'url' => $item['uri']]); + continue; + } + + Logger::info('Accepting post', ['uid' => $receiver, 'url' => $item['uri']]); + } + if ($activity['object_type'] == 'as:Event') { self::createEvent($activity, $item); } @@ -390,6 +468,69 @@ class Processor } } + /** + * Creates an mail post + * + * @param array $activity Activity data + * @param array $item item array + * @return int|bool New mail table row id or false on error + * @throws \Friendica\Network\HTTPException\InternalServerErrorException + */ + private static function postMail($activity, $item) + { + if (($item['gravity'] != GRAVITY_PARENT) && !DBA::exists('mail', ['uri' => $item['thr-parent'], 'uid' => $item['uid']])) { + Logger::info('Parent not found, mail will be discarded.', ['uid' => $item['uid'], 'uri' => $item['thr-parent']]); + return false; + } + + Logger::info('Direct Message', $item); + + $msg = []; + $msg['uid'] = $item['uid']; + + $msg['contact-id'] = $item['contact-id']; + + $contact = Contact::getById($item['contact-id'], ['name', 'url', 'photo']); + $msg['from-name'] = $contact['name']; + $msg['from-url'] = $contact['url']; + $msg['from-photo'] = $contact['photo']; + + $msg['uri'] = $item['uri']; + $msg['created'] = $item['created']; + + $parent = DBA::selectFirst('mail', ['parent-uri', 'title'], ['uri' => $item['thr-parent']]); + if (DBA::isResult($parent)) { + $msg['parent-uri'] = $parent['parent-uri']; + $msg['title'] = $parent['title']; + } else { + $msg['parent-uri'] = $item['thr-parent']; + + if (!empty($item['title'])) { + $msg['title'] = $item['title']; + } elseif (!empty($item['content-warning'])) { + $msg['title'] = $item['content-warning']; + } else { + // Trying to generate a title out of the body + $title = $item['body']; + + while (preg_match('#^(@\[url=([^\]]+)].*?\[\/url]\s)(.*)#is', $title, $matches)) { + $title = $matches[3]; + } + + $title = trim(HTML::toPlaintext(BBCode::convert($title, false, 2, true), 0)); + + if (strlen($title) > 20) { + $title = substr($title, 0, 20) . '...'; + } + + $msg['title'] = $title; + } + } + $msg['body'] = $item['body']; + + return Mail::insert($msg); + } + /** * Fetches missing posts * @@ -397,14 +538,14 @@ class Processor * @param $child * @throws \Friendica\Network\HTTPException\InternalServerErrorException */ - private static function fetchMissingActivity($url, $child) + public static function fetchMissingActivity($url, $child = []) { - if (Config::get('system', 'ostatus_full_threads')) { - return; + if (!empty($child['receiver'])) { + $uid = ActivityPub\Receiver::getFirstUserFromReceivers($child['receiver']); + } else { + $uid = 0; } - $uid = ActivityPub\Receiver::getFirstUserFromReceivers($child['receiver']); - $object = ActivityPub::fetchContent($url, $uid); if (empty($object)) { Logger::log('Activity ' . $url . ' was not fetchable, aborting.'); @@ -416,15 +557,34 @@ class Processor return; } + if (!empty($child['author'])) { + $actor = $child['author']; + } elseif (!empty($object['actor'])) { + $actor = $object['actor']; + } elseif (!empty($object['attributedTo'])) { + $actor = $object['attributedTo']; + } else { + // Shouldn't happen + $actor = ''; + } + + if (!empty($object['published'])) { + $published = $object['published']; + } elseif (!empty($child['published'])) { + $published = $child['published']; + } else { + $published = DateTimeFormat::utcNow(); + } + $activity = []; $activity['@context'] = $object['@context']; unset($object['@context']); $activity['id'] = $object['id']; $activity['to'] = defaults($object, 'to', []); $activity['cc'] = defaults($object, 'cc', []); - $activity['actor'] = $child['author']; + $activity['actor'] = $actor; $activity['object'] = $object; - $activity['published'] = defaults($object, 'published', $child['published']); + $activity['published'] = $published; $activity['type'] = 'Create'; $ldactivity = JsonLD::compact($activity); @@ -457,7 +617,7 @@ class Processor DBA::update('contact', ['hub-verify' => $activity['id'], 'protocol' => Protocol::ACTIVITYPUB], ['id' => $cid]); $contact = DBA::selectFirst('contact', [], ['id' => $cid, 'network' => Protocol::NATIVE_SUPPORT]); } else { - $contact = false; + $contact = []; } $item = ['author-id' => Contact::getIdForURL($activity['actor']), @@ -468,7 +628,11 @@ class Processor // Ensure that the contact has got the right network type self::switchContact($item['author-id']); - Contact::addRelationship($owner, $contact, $item, '', false, $note); + $result = Contact::addRelationship($owner, $contact, $item, false, $note); + if ($result === true) { + ActivityPub\Transmitter::sendContactAccept($item['author-link'], $item['author-id'], $owner['uid']); + } + $cid = Contact::getIdForURL($activity['actor'], $uid); if (empty($cid)) { return; @@ -494,7 +658,7 @@ class Processor } Logger::log('Updating profile for ' . $activity['object_id'], Logger::DEBUG); - APContact::getByURL($activity['object_id'], true); + Contact::updateFromProbeByURL($activity['object_id'], true); } /**