Fix formatting and PHPDoc in ActivityPub\Processor
[friendica.git/.git] / src / Protocol / ActivityPub / Processor.php
index 3bb0639..1ac29d9 100644 (file)
@@ -5,6 +5,7 @@
 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\Logger;
@@ -15,6 +16,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 +64,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 +91,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 +106,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"])) {
@@ -167,7 +173,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 +198,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 +313,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 +361,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;
                }
@@ -351,7 +393,7 @@ 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;
 
@@ -363,6 +405,11 @@ class Processor
                                $item['contact-id'] = Contact::getIdForURL($activity['author'], 0, true);
                        }
 
+                       if (!empty($activity['directmessage'])) {
+                               self::postMail($activity, $item);
+                               continue;
+                       }
+
                        if ($activity['object_type'] == 'as:Event') {
                                self::createEvent($activity, $item);
                        }
@@ -390,6 +437,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
         *
@@ -457,7 +567,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 +578,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;