]> git.mxchange.org Git - friendica.git/blobdiff - src/Protocol/OStatus.php
Avoid local network communication / invalid url requests
[friendica.git] / src / Protocol / OStatus.php
index ddc55a2c5bd4dcf0e10d50e023ecc99635dafffe..2a160724b4d0ea16cdbd55de357c6c4860875371 100644 (file)
@@ -1,6 +1,6 @@
 <?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
  *
@@ -24,6 +24,7 @@ namespace Friendica\Protocol;
 use DOMDocument;
 use DOMElement;
 use DOMXPath;
+use Friendica\App;
 use Friendica\Content\Text\BBCode;
 use Friendica\Content\Text\HTML;
 use Friendica\Core\Cache\Enum\Duration;
@@ -102,9 +103,9 @@ class OStatus
 */
                if ($aliaslink != '') {
                        $contact = DBA::selectFirst('contact', [], [
-                               "`uid` = ? AND `alias` = ? AND `network` != ? AND `rel` IN (?, ?)",
+                               "`uid` = ? AND `alias` = ? AND `rel` IN (?, ?)",
                                $importer['uid'],
-                               $aliaslink, Protocol::STATUSNET,
+                               $aliaslink,
                                Contact::SHARING, Contact::FRIEND,
                        ]);
                }
@@ -115,11 +116,10 @@ class OStatus
                        }
 
                        $contact = DBA::selectFirst('contact', [], [
-                               "`uid` = ? AND `nurl` IN (?, ?) AND `network` != ? AND `rel` IN (?, ?)",
+                               "`uid` = ? AND `nurl` IN (?, ?) AND `rel` IN (?, ?)",
                                $importer['uid'],
                                Strings::normaliseLink($author['author-link']),
                                Strings::normaliseLink($aliaslink),
-                               Protocol::STATUSNET,
                                Contact::SHARING,
                                Contact::FRIEND,
                        ]);
@@ -127,10 +127,9 @@ class OStatus
 
                if (!DBA::isResult($contact) && ($addr != '')) {
                        $contact = DBA::selectFirst('contact', [], [
-                               "`uid` = ? AND `addr` = ? AND `network` != ? AND `rel` IN (?, ?)",
+                               "`uid` = ? AND `addr` = ? AND `rel` IN (?, ?)",
                                $importer['uid'],
                                $addr,
-                               Protocol::STATUSNET,
                                Contact::SHARING,
                                Contact::FRIEND,
                        ]);
@@ -391,7 +390,7 @@ class OStatus
                $header['network'] = Protocol::OSTATUS;
                $header['wall'] = 0;
                $header['origin'] = 0;
-               $header['gravity'] = GRAVITY_COMMENT;
+               $header['gravity'] = Item::GRAVITY_COMMENT;
 
                if (!is_object($doc->firstChild) || empty($doc->firstChild->tagName)) {
                        return false;
@@ -499,7 +498,7 @@ class OStatus
 
                                $item['verb'] = Activity::LIKE;
                                $item['thr-parent'] = $orig_uri;
-                               $item['gravity'] = GRAVITY_ACTIVITY;
+                               $item['gravity'] = Item::GRAVITY_ACTIVITY;
                                $item['object-type'] = Activity\ObjectType::NOTE;
                        }
 
@@ -616,16 +615,16 @@ class OStatus
 
                $item['created'] = XML::getFirstNodeValue($xpath, 'atom:published/text()', $entry);
                $item['edited'] = XML::getFirstNodeValue($xpath, 'atom:updated/text()', $entry);
-               $item['conversation-uri'] = XML::getFirstNodeValue($xpath, 'ostatus:conversation/text()', $entry);
+               $item['conversation'] = XML::getFirstNodeValue($xpath, 'ostatus:conversation/text()', $entry);
 
                $conv = $xpath->query('ostatus:conversation', $entry);
                if (is_object($conv->item(0))) {
                        foreach ($conv->item(0)->attributes as $attributes) {
                                if ($attributes->name == 'ref') {
-                                       $item['conversation-uri'] = $attributes->textContent;
+                                       $item['conversation'] = $attributes->textContent;
                                }
                                if ($attributes->name == 'href') {
-                                       $item['conversation-href'] = $attributes->textContent;
+                                       $item['conversation'] = $attributes->textContent;
                                }
                        }
                }
@@ -704,14 +703,6 @@ class OStatus
                        }
                }
 
-               if (($self != '') && empty($item['protocol'])) {
-                       self::fetchSelf($self, $item);
-               }
-
-               if (!empty($item['conversation-href'])) {
-                       self::fetchConversation($item['conversation-href'], $item['conversation-uri']);
-               }
-
                if (isset($item['thr-parent'])) {
                        if (!Post::exists(['uid' => $importer['uid'], 'uri' => $item['thr-parent']])) {
                                if ($related != '') {
@@ -722,197 +713,12 @@ class OStatus
                        }
                } else {
                        $item['thr-parent'] = $item['uri'];
-                       $item['gravity'] = GRAVITY_PARENT;
-               }
-
-               if (($item['author-link'] != '') && !empty($item['protocol'])) {
-                       $item = Conversation::insert($item);
+                       $item['gravity'] = Item::GRAVITY_PARENT;
                }
 
                self::$itemlist[] = $item;
        }
 
-       /**
-        * Fetch the conversation for posts
-        *
-        * @param string $conversation     The link to the conversation
-        * @param string $conversation_uri The conversation in "uri" format
-        * @return void
-        * @throws \Friendica\Network\HTTPException\InternalServerErrorException
-        */
-       private static function fetchConversation(string $conversation, string $conversation_uri)
-       {
-               // Ensure that we only store a conversation once in a process
-               if (isset(self::$conv_list[$conversation])) {
-                       return;
-               }
-
-               self::$conv_list[$conversation] = true;
-
-               $curlResult = DI::httpClient()->get($conversation, HttpClientAccept::ATOM_XML);
-
-               if (!$curlResult->isSuccess() || empty($curlResult->getBody())) {
-                       return;
-               }
-
-               $xml = '';
-
-               if ($curlResult->inHeader('Content-Type') &&
-                       in_array('application/atom+xml', $curlResult->getHeader('Content-Type'))) {
-                       $xml = $curlResult->getBody();
-               }
-
-               if ($xml == '') {
-                       $doc = new DOMDocument();
-                       if (!@$doc->loadHTML($curlResult->getBody())) {
-                               return;
-                       }
-                       $xpath = new DOMXPath($doc);
-
-                       $links = $xpath->query('//link');
-                       if ($links) {
-                               $file = '';
-                               foreach ($links as $link) {
-                                       $attribute = self::readAttributes($link);
-                                       if (($attribute['rel'] == 'alternate') && ($attribute['type'] == 'application/atom+xml')) {
-                                               $file = $attribute['href'];
-                                       }
-                               }
-                               if ($file != '') {
-                                       $conversation_atom = DI::httpClient()->get($attribute['href'], HttpClientAccept::ATOM_XML);
-
-                                       if ($conversation_atom->isSuccess()) {
-                                               $xml = $conversation_atom->getBody();
-                                       }
-                               }
-                       }
-               }
-
-               if ($xml == '') {
-                       return;
-               }
-
-               self::storeConversation($xml, $conversation, $conversation_uri);
-       }
-
-       /**
-        * Store a feed in several conversation entries
-        *
-        * @param string $xml              The feed
-        * @param string $conversation     conversation
-        * @param string $conversation_uri conversation uri
-        * @return void
-        * @throws \Exception
-        */
-       private static function storeConversation(string $xml, string $conversation = '', string $conversation_uri = '')
-       {
-               $doc = new DOMDocument();
-               @$doc->loadXML($xml);
-
-               $xpath = new DOMXPath($doc);
-               $xpath->registerNamespace('atom', ActivityNamespace::ATOM1);
-               $xpath->registerNamespace('thr', ActivityNamespace::THREAD);
-               $xpath->registerNamespace('ostatus', ActivityNamespace::OSTATUS);
-
-               $entries = $xpath->query('/atom:feed/atom:entry');
-
-               // Now store the entries
-               foreach ($entries as $entry) {
-                       $doc2 = new DOMDocument();
-                       $doc2->preserveWhiteSpace = false;
-                       $doc2->formatOutput = true;
-
-                       $conv_data = [];
-
-                       $conv_data['protocol'] = Conversation::PARCEL_SPLIT_CONVERSATION;
-                       $conv_data['direction'] = Conversation::PULL;
-                       $conv_data['network'] = Protocol::OSTATUS;
-                       $conv_data['uri'] = XML::getFirstNodeValue($xpath, 'atom:id/text()', $entry);
-
-                       $inreplyto = $xpath->query('thr:in-reply-to', $entry);
-                       if (is_object($inreplyto->item(0))) {
-                               foreach ($inreplyto->item(0)->attributes as $attributes) {
-                                       if ($attributes->name == 'ref') {
-                                               $conv_data['reply-to-uri'] = $attributes->textContent;
-                                       }
-                               }
-                       }
-
-                       $conv_data['conversation-uri'] = XML::getFirstNodeValue($xpath, 'ostatus:conversation/text()', $entry);
-
-                       $conv = $xpath->query('ostatus:conversation', $entry);
-                       if (is_object($conv->item(0))) {
-                               foreach ($conv->item(0)->attributes as $attributes) {
-                                       if ($attributes->name == 'ref') {
-                                               $conv_data['conversation-uri'] = $attributes->textContent;
-                                       }
-                                       if ($attributes->name == 'href') {
-                                               $conv_data['conversation-href'] = $attributes->textContent;
-                                       }
-                               }
-                       }
-
-                       if ($conversation != '') {
-                               $conv_data['conversation-uri'] = $conversation;
-                       }
-
-                       if ($conversation_uri != '') {
-                               $conv_data['conversation-uri'] = $conversation_uri;
-                       }
-
-                       $entry = $doc2->importNode($entry, true);
-
-                       $doc2->appendChild($entry);
-
-                       $conv_data['source'] = $doc2->saveXML();
-
-                       Logger::info('Store conversation data for uri '.$conv_data['uri']);
-                       Conversation::insert($conv_data);
-               }
-       }
-
-       /**
-        * Fetch the own post so that it can be stored later
-        *
-        * We want to store the original data for later processing.
-        * This function is meant for cases where we process a feed with multiple entries.
-        * In that case we need to fetch the single posts here.
-        *
-        * @param string $self The link to the self item
-        * @param array  $item The item array
-        * @return void
-        * @throws \Friendica\Network\HTTPException\InternalServerErrorException
-        */
-       private static function fetchSelf(string $self, array &$item)
-       {
-               $condition = ['item-uri' => $self, 'protocol' => [Conversation::PARCEL_DFRN,
-                       Conversation::PARCEL_DIASPORA_DFRN, Conversation::PARCEL_LOCAL_DFRN,
-                       Conversation::PARCEL_DIRECT, Conversation::PARCEL_SALMON]];
-               if (DBA::exists('conversation', $condition)) {
-                       Logger::info('Conversation '.$item['uri'].' is already stored.');
-                       return;
-               }
-
-               $curlResult = DI::httpClient()->get($self, HttpClientAccept::ATOM_XML);
-
-               if (!$curlResult->isSuccess()) {
-                       return;
-               }
-
-               // We reformat the XML to make it better readable
-               $doc = new DOMDocument();
-               $doc->loadXML($curlResult->getBody());
-               $doc->preserveWhiteSpace = false;
-               $doc->formatOutput = true;
-               $xml = $doc->saveXML();
-
-               $item['protocol'] = Conversation::PARCEL_SALMON;
-               $item['source'] = $xml;
-               $item['direction'] = Conversation::PULL;
-
-               Logger::info('Conversation '.$item['uri'].' is now fetched.');
-       }
-
        /**
         * Fetch related posts and processes them
         *
@@ -925,30 +731,6 @@ class OStatus
         */
        private static function fetchRelated(string $related, string $related_uri, array $importer)
        {
-               $condition = [
-                       'item-uri' => $related_uri,
-                       'protocol' => [
-                               Conversation::PARCEL_DFRN,
-                               Conversation::PARCEL_DIASPORA_DFRN,
-                               Conversation::PARCEL_LOCAL_DFRN,
-                               Conversation::PARCEL_DIRECT,
-                               Conversation::PARCEL_SALMON,
-                       ],
-               ];
-               $conversation = DBA::selectFirst('conversation', ['source', 'protocol'], $condition);
-               if (DBA::isResult($conversation)) {
-                       $stored = true;
-                       $xml = $conversation['source'];
-                       if (self::process($xml, $importer, $contact, $hub, $stored, false, Conversation::PULL)) {
-                               Logger::info('Got valid cached XML for URI '.$related_uri);
-                               return;
-                       }
-                       if ($conversation['protocol'] == Conversation::PARCEL_SALMON) {
-                               Logger::info('Delete invalid cached XML for URI '.$related_uri);
-                               DBA::delete('conversation', ['item-uri' => $related_uri]);
-                       }
-               }
-
                $stored = false;
                $curlResult = DI::httpClient()->get($related, HttpClientAccept::ATOM_XML);
 
@@ -1013,18 +795,8 @@ class OStatus
                        }
                }
 
-               // Finally we take the data that we fetched from "ostatus:conversation"
-               if ($xml == '') {
-                       $condition = ['item-uri' => $related_uri, 'protocol' => Conversation::PARCEL_SPLIT_CONVERSATION];
-                       $conversation = DBA::selectFirst('conversation', ['source'], $condition);
-                       if (DBA::isResult($conversation)) {
-                               $stored = true;
-                               Logger::info('Got cached XML from conversation for URI ' . $related_uri);
-                               $xml = $conversation['source'];
-                       }
-               }
-
                if ($xml != '') {
+                       $hub = '';
                        self::process($xml, $importer, $contact, $hub, $stored, false, Conversation::PULL);
                } else {
                        Logger::info('XML could not be fetched for URI: ' . $related_uri . ' - href: ' . $related);
@@ -1130,10 +902,7 @@ class OStatus
 
                                        case 'ostatus:conversation':
                                                $link_data['conversation'] = $attribute['href'];
-                                               $item['conversation-href'] = $link_data['conversation'];
-                                               if (!isset($item['conversation-uri'])) {
-                                                       $item['conversation-uri'] = $item['conversation-href'];
-                                               }
+                                               $item['conversation'] = $link_data['conversation'];
                                                break;
 
                                        case 'enclosure':
@@ -1168,7 +937,7 @@ class OStatus
                                                break;
 
                                        default:
-                                               Logger::warning('Unsupported rel=' . $attribute['rel'] . ', href=' . $attribute['href'] . ', object-type=' . $attribute['object-type']);
+                                               Logger::notice('Unsupported rel=' . $attribute['rel'] . ', href=' . $attribute['href'] . ', object-type=' . $item['object-type']);
                                }
                        }
                }
@@ -1207,44 +976,6 @@ class OStatus
                }
        }
 
-       /**
-        * Cleans the body of a post if it contains picture links
-        *
-        * @param string $body The body
-        * @param integer $uriid URI id
-        * @return string The cleaned body
-        * @throws \Friendica\Network\HTTPException\InternalServerErrorException
-        */
-       public static function formatPicturePost(string $body, int $uriid): string
-       {
-               $siteinfo = BBCode::getAttachedData($body);
-
-               if (($siteinfo['type'] == 'photo') && (!empty($siteinfo['preview']) || !empty($siteinfo['image']))) {
-                       if (isset($siteinfo['preview'])) {
-                               $preview = $siteinfo['preview'];
-                       } else {
-                               $preview = $siteinfo['image'];
-                       }
-
-                       // Is it a remote picture? Then make a smaller preview here
-                       $preview = Post\Link::getByLink($uriid, $preview, Proxy::SIZE_SMALL);
-
-                       // Is it a local picture? Then make it smaller here
-                       $preview = str_replace(['-0.jpg', '-0.png'], ['-2.jpg', '-2.png'], $preview);
-                       $preview = str_replace(['-1.jpg', '-1.png'], ['-2.jpg', '-2.png'], $preview);
-
-                       if (isset($siteinfo['url'])) {
-                               $url = $siteinfo['url'];
-                       } else {
-                               $url = $siteinfo['image'];
-                       }
-
-                       $body = trim($siteinfo['text']) . ' [url]' . $url . "[/url]\n[img]" . $preview . '[/img]';
-               }
-
-               return $body;
-       }
-
        /**
         * Adds the header elements to the XML document
         *
@@ -1291,9 +1022,9 @@ class OStatus
 
                $attributes = [
                        'uri' => 'https://friendi.ca',
-                       'version' => FRIENDICA_VERSION . '-' . DB_UPDATE_VERSION,
+                       'version' => App::VERSION . '-' . DB_UPDATE_VERSION,
                ];
-               XML::addElement($doc, $root, 'generator', FRIENDICA_PLATFORM, $attributes);
+               XML::addElement($doc, $root, 'generator', App::PLATFORM, $attributes);
                XML::addElement($doc, $root, 'id', DI::baseUrl() . '/profile/' . $owner['nick']);
                XML::addElement($doc, $root, 'title', $title);
                XML::addElement($doc, $root, 'subtitle', sprintf("Updates from %s on %s", $owner['name'], DI::config()->get('config', 'sitename')));
@@ -1371,55 +1102,7 @@ class OStatus
         */
        public static function getAttachment(DOMDocument $doc, DOMElement $root, array $item)
        {
-               $siteinfo = BBCode::getAttachedData($item['body']);
-
-               switch ($siteinfo['type']) {
-                       case 'photo':
-                               if (!empty($siteinfo['image'])) {
-                                       $imgdata = Images::getInfoFromURLCached($siteinfo['image']);
-                                       if ($imgdata) {
-                                               $attributes = [
-                                                       'rel' => 'enclosure',
-                                                       'href' => $siteinfo['image'],
-                                                       'type' => $imgdata['mime'],
-                                                       'length' => intval($imgdata['size']),
-                                               ];
-                                               XML::addElement($doc, $root, 'link', '', $attributes);
-                                       }
-                               }
-                               break;
-
-                       case 'video':
-                               $attributes = [
-                                       'rel' => 'enclosure',
-                                       'href' => $siteinfo['url'],
-                                       'type' => 'text/html; charset=UTF-8',
-                                       'length' => '0',
-                                       'title' => ($siteinfo['title'] ?? '') ?: $siteinfo['url'],
-                               ];
-                               XML::addElement($doc, $root, 'link', '', $attributes);
-                               break;
-
-                       default:
-                               Logger::warning('Unsupported type', ['type' => $siteinfo['type'], 'url' => $siteinfo['url']]);
-                               break;
-               }
-
-               if (!DI::config()->get('system', 'ostatus_not_attach_preview') && ($siteinfo['type'] != 'photo') && isset($siteinfo['image'])) {
-                       $imgdata = Images::getInfoFromURLCached($siteinfo['image']);
-                       if ($imgdata) {
-                               $attributes = [
-                                       'rel' => 'enclosure',
-                                       'href' => $siteinfo['image'],
-                                       'type' => $imgdata['mime'],
-                                       'length' => intval($imgdata['size']),
-                               ];
-
-                               XML::addElement($doc, $root, 'link', '', $attributes);
-                       }
-               }
-
-               foreach (Post\Media::getByURIId($item['uri-id'], [Post\Media::DOCUMENT, Post\Media::TORRENT, Post\Media::UNKNOWN]) as $attachment) {
+               foreach (Post\Media::getByURIId($item['uri-id'], [Post\Media::AUDIO, Post\Media::IMAGE, Post\Media::VIDEO, Post\Media::DOCUMENT, Post\Media::TORRENT]) as $attachment) {
                        $attributes = ['rel' => 'enclosure',
                                'href' => $attachment['url'],
                                'type' => $attachment['mimetype']];
@@ -1593,7 +1276,7 @@ class OStatus
         */
        private static function likeEntry(DOMDocument $doc, array $item, array $owner, bool $toplevel): DOMElement
        {
-               if (($item['gravity'] != GRAVITY_PARENT) && (Strings::normaliseLink($item['author-link']) != Strings::normaliseLink($owner['url']))) {
+               if (($item['gravity'] != Item::GRAVITY_PARENT) && (Strings::normaliseLink($item['author-link']) != Strings::normaliseLink($owner['url']))) {
                        Logger::info('OStatus entry is from author ' . $owner['url'] . ' - not from ' . $item['author-link'] . '. Quitting.');
                }
 
@@ -1743,7 +1426,7 @@ class OStatus
         */
        private static function noteEntry(DOMDocument $doc, array $item, array $owner, bool $toplevel): DOMElement
        {
-               if (($item['gravity'] != GRAVITY_PARENT) && (Strings::normaliseLink($item['author-link']) != Strings::normaliseLink($owner['url']))) {
+               if (($item['gravity'] != Item::GRAVITY_PARENT) && (Strings::normaliseLink($item['author-link']) != Strings::normaliseLink($owner['url']))) {
                        Logger::info('OStatus entry is from author ' . $owner['url'] . ' - not from ' . $item['author-link'] . '. Quitting.');
                }
 
@@ -1786,7 +1469,7 @@ class OStatus
 
                        if ($owner['contact-type'] == Contact::TYPE_COMMUNITY) {
                                $contact = Contact::getByURL($item['author-link']) ?: $owner;
-                               $contact['nickname'] = $contact['nickname'] ?? $contact['nick']; 
+                               $contact['nickname'] = $contact['nickname'] ?? $contact['nick'];
                                $author = self::addAuthor($doc, $contact, false);
                                $entry->appendChild($author);
                        }
@@ -1831,8 +1514,7 @@ class OStatus
                XML::addElement($doc, $entry, 'id', $item['uri']);
                XML::addElement($doc, $entry, 'title', html_entity_decode($title, ENT_QUOTES, 'UTF-8'));
 
-               $body = Post\Media::addAttachmentsToBody($item['uri-id'], $item['body']);
-               $body = self::formatPicturePost($body, $item['uri-id']);
+               $body = Post\Media::addAttachmentsToBody($item['uri-id'], DI::contentItem()->addSharedPost($item));
 
                if (!empty($item['title'])) {
                        $body = '[b]' . $item['title'] . "[/b]\n\n" . $body;
@@ -1873,7 +1555,7 @@ class OStatus
        {
                $mentioned = [];
 
-               if ($item['gravity'] != GRAVITY_PARENT) {
+               if ($item['gravity'] != Item::GRAVITY_PARENT) {
                        $parent = Post::selectFirst(['guid', 'author-link', 'owner-link'], ['id' => $item['parent']]);
 
                        $thrparent = Post::selectFirst(['guid', 'author-link', 'owner-link', 'plink'], ['uid' => $owner['uid'], 'uri' => $item['thr-parent']]);
@@ -1904,19 +1586,7 @@ class OStatus
                }
 
                if (intval($item['parent']) > 0) {
-                       $conversation_href = $conversation_uri = str_replace('/objects/', '/context/', $item['thr-parent']);
-
-                       if (isset($parent_item)) {
-                               $conversation = DBA::selectFirst('conversation', ['conversation-uri', 'conversation-href'], ['item-uri' => $parent_item]);
-                               if (DBA::isResult($conversation)) {
-                                       if ($conversation['conversation-uri'] != '') {
-                                               $conversation_uri = $conversation['conversation-uri'];
-                                       }
-                                       if ($conversation['conversation-href'] != '') {
-                                               $conversation_href = $conversation['conversation-href'];
-                                       }
-                               }
-                       }
+                       $conversation_href = $conversation_uri = $item['conversation'];
 
                        XML::addElement($doc, $entry, 'link', '', ['rel' => 'ostatus:conversation', 'href' => $conversation_href]);
 
@@ -2017,7 +1687,7 @@ class OStatus
         * cache or it is empty
         *
         * @param string  $owner_nick  Nickname of the feed owner
-        * @param string  $last_update Date of the last update
+        * @param string  $last_update Date of the last update (in "Y-m-d H:i:s" format)
         * @param integer $max_items   Number of maximum items to fetch
         * @param string  $filter      Feed items filter (activity, posts or comments)
         * @param boolean $nocache     Wether to bypass caching