]> git.mxchange.org Git - quix0rs-gnu-social.git/blobdiff - plugins/OStatus/classes/Ostatus_profile.php
Merge branch 'testing' into 0.9.x
[quix0rs-gnu-social.git] / plugins / OStatus / classes / Ostatus_profile.php
index 562ab3bde51f13927e44d1f9be9c72ee60049917..d2e046a602579ab18534d446198ceb746fa0c53f 100644 (file)
@@ -388,11 +388,17 @@ class Ostatus_profile extends Memcached_DataObject
     {
         $feed = $doc->documentElement;
 
-        if ($feed->localName != 'feed' || $feed->namespaceURI != Activity::ATOM) {
-            common_log(LOG_ERR, __METHOD__ . ": not an Atom feed, ignoring");
-            return;
+        if ($feed->localName == 'feed' && $feed->namespaceURI == Activity::ATOM) {
+            $this->processAtomFeed($feed, $source);
+        } else if ($feed->localName == 'rss') { // @fixme check namespace
+            $this->processRssFeed($feed, $source);
+        } else {
+            throw new Exception("Unknown feed format.");
         }
+    }
 
+    public function processAtomFeed(DOMElement $feed, $source)
+    {
         $entries = $feed->getElementsByTagNameNS(Activity::ATOM, 'entry');
         if ($entries->length == 0) {
             common_log(LOG_ERR, __METHOD__ . ": no entries in feed update, ignoring");
@@ -405,6 +411,26 @@ class Ostatus_profile extends Memcached_DataObject
         }
     }
 
+    public function processRssFeed(DOMElement $rss, $source)
+    {
+        $channels = $rss->getElementsByTagName('channel');
+
+        if ($channels->length == 0) {
+            throw new Exception("RSS feed without a channel.");
+        } else if ($channels->length > 1) {
+            common_log(LOG_WARNING, __METHOD__ . ": more than one channel in an RSS feed");
+        }
+
+        $channel = $channels->item(0);
+
+        $items = $channel->getElementsByTagName('item');
+
+        for ($i = 0; $i < $items->length; $i++) {
+            $item = $items->item($i);
+            $this->processEntry($item, $channel, $source);
+        }
+    }
+
     /**
      * Process a posted entry from this feed source.
      *
@@ -442,24 +468,27 @@ class Ostatus_profile extends Memcached_DataObject
                 return false;
             }
         } else {
-            // Individual user feeds may contain only posts from themselves.
-            // Authorship is validated against the profile URI on upper layers,
-            // through PuSH setup or Salmon signature checks.
-            $actorUri = self::getActorProfileURI($activity);
-            if ($actorUri == $this->uri) {
-                // Check if profile info has changed and update it
-                $this->updateFromActivityObject($activity->actor);
+            $actor = $activity->actor;
+
+            if (empty($actor)) {
+                // OK here! assume the default
+            } else if ($actor->id == $this->uri || $actor->link == $this->uri) {
+                $this->updateFromActivityObject($actor);
             } else {
-                common_log(LOG_WARNING, "OStatus: skipping post with bad author: got $actorUri expected $this->uri");
-                return false;
+                throw new Exception("Got an actor '{$actor->title}' ({$actor->id}) on single-user feed for {$this->uri}");
             }
+
             $oprofile = $this;
         }
 
+        // It's not always an ActivityObject::NOTE, but... let's just say it is.
+
+        $note = $activity->object;
+
         // The id URI will be used as a unique identifier for for the notice,
         // protecting against duplicate saves. It isn't required to be a URL;
         // tag: URIs for instance are found in Google Buzz feeds.
-        $sourceUri = $activity->object->id;
+        $sourceUri = $note->id;
         $dupe = Notice::staticGet('uri', $sourceUri);
         if ($dupe) {
             common_log(LOG_INFO, "OStatus: ignoring duplicate post: $sourceUri");
@@ -468,16 +497,30 @@ class Ostatus_profile extends Memcached_DataObject
 
         // We'll also want to save a web link to the original notice, if provided.
         $sourceUrl = null;
-        if ($activity->object->link) {
-            $sourceUrl = $activity->object->link;
+        if ($note->link) {
+            $sourceUrl = $note->link;
         } else if ($activity->link) {
             $sourceUrl = $activity->link;
-        } else if (preg_match('!^https?://!', $activity->object->id)) {
-            $sourceUrl = $activity->object->id;
+        } else if (preg_match('!^https?://!', $note->id)) {
+            $sourceUrl = $note->id;
+        }
+
+        // Use summary as fallback for content
+
+        if (!empty($note->content)) {
+            $sourceContent = $note->content;
+        } else if (!empty($note->summary)) {
+            $sourceContent = $note->summary;
+        } else if (!empty($note->title)) {
+            $sourceContent = $note->title;
+        } else {
+            // @fixme fetch from $sourceUrl?
+            throw new ClientException("No content for notice {$sourceUri}");
         }
 
         // Get (safe!) HTML and text versions of the content
-        $rendered = $this->purify($activity->object->content);
+
+        $rendered = $this->purify($sourceContent);
         $content = html_entity_decode(strip_tags($rendered));
 
         $shortened = common_shorten_links($content);
@@ -488,8 +531,8 @@ class Ostatus_profile extends Memcached_DataObject
         $attachment = null;
 
         if (Notice::contentTooLong($shortened)) {
-            $attachment = $this->saveHTMLFile($activity->object->title, $rendered);
-            $summary = $activity->object->summary;
+            $attachment = $this->saveHTMLFile($note->title, $rendered);
+            $summary = html_entity_decode(strip_tags($note->summary));
             if (empty($summary)) {
                 $summary = $content;
             }
@@ -795,9 +838,20 @@ class Ostatus_profile extends Memcached_DataObject
             throw new FeedSubNoHubException();
         }
 
-        // Try to get a profile from the feed activity:subject
+        $feedEl = $discover->root;
+
+        if ($feedEl->tagName == 'feed') {
+            return self::ensureAtomFeed($feedEl, $hints);
+        } else if ($feedEl->tagName == 'channel') {
+            return self::ensureRssChannel($feedEl, $hints);
+        } else {
+            throw new FeedSubBadXmlException($feeduri);
+        }
+    }
 
-        $feedEl = $discover->feed->documentElement;
+    public static function ensureAtomFeed($feedEl, $hints)
+    {
+        // Try to get a profile from the feed activity:subject
 
         $subject = ActivityUtils::child($feedEl, Activity::SUBJECT, Activity::SPEC);
 
@@ -818,7 +872,7 @@ class Ostatus_profile extends Memcached_DataObject
         // Sheesh. Not a very nice feed! Let's try fingerpoken in the
         // entries.
 
-        $entries = $discover->feed->getElementsByTagNameNS(Activity::ATOM, 'entry');
+        $entries = $feedEl->getElementsByTagNameNS(Activity::ATOM, 'entry');
 
         if (!empty($entries) && $entries->length > 0) {
 
@@ -845,6 +899,17 @@ class Ostatus_profile extends Memcached_DataObject
         throw new FeedSubException("Can't find enough profile information to make a feed.");
     }
 
+    public static function ensureRssChannel($feedEl, $hints)
+    {
+        // @fixme we should check whether this feed has elements
+        // with different <author> or <dc:creator> elements, and... I dunno.
+        // Do something about that.
+
+        $obj = ActivityObject::fromRssChannel($feedEl);
+
+        return self::ensureActivityObjectProfile($obj, $hints);
+    }
+
     /**
      * Download and update given avatar image
      *
@@ -1307,9 +1372,19 @@ class Ostatus_profile extends Memcached_DataObject
             return $hints['nickname'];
         }
 
-        // Try the definitive ID
+        // Try the profile url (like foo.example.com or example.com/user/foo)
+
+        $profileUrl = ($object->link) ? $object->link : $hints['profileurl'];
+
+        if (!empty($profileUrl)) {
+            $nickname = self::nicknameFromURI($profileUrl);
+        }
+
+        // Try the URI (may be a tag:, http:, acct:, ...
 
-        $nickname = self::nicknameFromURI($object->id);
+        if (empty($nickname)) {
+            $nickname = self::nicknameFromURI($object->id);
+        }
 
         // Try a Webfinger if one was passed (way) down