]> git.mxchange.org Git - quix0rs-gnu-social.git/blobdiff - plugins/OStatus/classes/Ostatus_profile.php
Merge commit 'refs/merge-requests/49' of https://gitorious.org/social/mainline into...
[quix0rs-gnu-social.git] / plugins / OStatus / classes / Ostatus_profile.php
index 72a0c34b92128b074da408530df2f87debe7b20d..77b1e9bef5ae179a67d8ef24bdf132c11b2de088 100644 (file)
@@ -51,12 +51,12 @@ class Ostatus_profile extends Managed_DataObject
     {
         return array(
             'fields' => array(
-                'uri' => array('type' => 'varchar', 'length' => 255, 'not null' => true),
+                'uri' => array('type' => 'varchar', 'length' => 191, 'not null' => true),
                 'profile_id' => array('type' => 'integer'),
                 'group_id' => array('type' => 'integer'),
                 'peopletag_id' => array('type' => 'integer'),
-                'feeduri' => array('type' => 'varchar', 'length' => 255),
-                'salmonuri' => array('type' => 'varchar', 'length' => 255),
+                'feeduri' => array('type' => 'varchar', 'length' => 191),
+                'salmonuri' => array('type' => 'varchar', 'length' => 191),
                 'avatar' => array('type' => 'text'),
                 'created' => array('type' => 'datetime', 'not null' => true),
                 'modified' => array('type' => 'datetime', 'not null' => true),
@@ -81,6 +81,14 @@ class Ostatus_profile extends Managed_DataObject
         return $this->uri;
     }
 
+    public function fromProfile(Profile $profile)
+    {
+        $oprofile = Ostatus_profile::getKV('profile_id', $profile->id);
+        if (!$oprofile instanceof Ostatus_profile) {
+            throw new Exception('No Ostatus_profile for Profile ID: '.$profile->id);
+        }
+    }
+
     /**
      * Fetch the locally stored profile for this feed
      * @return Profile
@@ -88,11 +96,15 @@ class Ostatus_profile extends Managed_DataObject
      */
     public function localProfile()
     {
+        if ($this->isGroup()) {
+            return $this->localGroup()->getProfile();
+        }
+
         $profile = Profile::getKV('id', $this->profile_id);
-        if ($profile instanceof Profile) {
-            return $profile;
+        if (!$profile instanceof Profile) {
+            throw new NoProfileException($this->profile_id);
         }
-        throw new NoProfileException($this->profile_id);
+        return $profile;
     }
 
     /**
@@ -101,10 +113,13 @@ class Ostatus_profile extends Managed_DataObject
      */
     public function localGroup()
     {
-        if ($this->group_id) {
-            return User_group::getKV('id', $this->group_id);
+        $group = User_group::getKV('id', $this->group_id);
+
+        if (!$group instanceof User_group) {
+            throw new NoSuchGroupException(array('id'=>$this->group_id));
         }
-        return null;
+
+        return $group;
     }
 
     /**
@@ -606,7 +621,7 @@ class Ostatus_profile extends Managed_DataObject
 
         // Get (safe!) HTML and text versions of the content
 
-        $rendered = $this->purify($sourceContent);
+        $rendered = common_purify($sourceContent);
         $content = common_strip_html($rendered);
 
         $shortened = common_shorten_links($content);
@@ -731,7 +746,7 @@ class Ostatus_profile extends Managed_DataObject
     {
         $notice = null;
 
-        $profile = $this->checkAuthorship($activity, $this->localProfile());
+        $profile = ActivityUtils::checkAuthorship($activity, $this->localProfile());
 
         // It's not always an ActivityObject::NOTE, but... let's just say it is.
 
@@ -773,7 +788,7 @@ class Ostatus_profile extends Managed_DataObject
 
         // Get (safe!) HTML and text versions of the content
 
-        $rendered = $this->purify($sourceContent);
+        $rendered = common_purify($sourceContent);
         $content = common_strip_html($rendered);
 
         $shortened = common_shorten_links($content);
@@ -844,6 +859,10 @@ class Ostatus_profile extends Managed_DataObject
                     $options['reply_to'] = $orig->id;
                 }
             }
+            if (!empty($activity->context->conversation)) {
+                // we store the URI here, Notice class can look it up later
+                $options['conversation'] = $activity->context->conversation;
+            }
 
             $location = $activity->context->location;
             if ($location) {
@@ -895,17 +914,6 @@ class Ostatus_profile extends Managed_DataObject
         return $saved;
     }
 
-    /**
-     * Clean up HTML
-     */
-    protected function purify($html)
-    {
-        require_once INSTALLDIR.'/extlib/htmLawed/htmLawed.php';
-        $config = array('safe' => 1,
-                        'deny_attribute' => 'id,style,on*');
-        return htmLawed($html, $config);
-    }
-
     /**
      * Filters a list of recipient ID URIs to just those for local delivery.
      * @param Profile local profile of sender
@@ -977,7 +985,7 @@ class Ostatus_profile extends Managed_DataObject
      * @throws Exception on various error conditions
      * @throws OStatusShadowException if this reference would obscure a local user/group
      */
-    public static function ensureProfileURL($profile_url, $hints=array())
+    public static function ensureProfileURL($profile_url, array $hints=array())
     {
         $oprofile = self::getFromProfileURL($profile_url);
 
@@ -1110,8 +1118,13 @@ class Ostatus_profile extends Managed_DataObject
      * @return Ostatus_profile
      * @throws Exception
      */
-    public static function ensureFeedURL($feed_url, $hints=array())
+    public static function ensureFeedURL($feed_url, array $hints=array())
     {
+        $oprofile = Ostatus_profile::getKV('feeduri', $feed_url);
+        if ($oprofile instanceof Ostatus_profile) {
+            return $oprofile;
+        }
+
         $discover = new FeedDiscovery();
 
         $feeduri = $discover->discoverFromFeedURL($feed_url);
@@ -1125,8 +1138,9 @@ class Ostatus_profile extends Managed_DataObject
                         ?: $discover->getAtomLink(Salmon::NS_REPLIES);
         $hints['salmon'] = $salmonuri;
 
-        if (!$huburi && !common_config('feedsub', 'fallback_hub')) {
+        if (!$huburi && !common_config('feedsub', 'fallback_hub') && !common_config('feedsub', 'nohub')) {
             // We can only deal with folks with a PuSH hub
+            // unless we have something similar available locally.
             throw new FeedSubNoHubException();
         }
 
@@ -1153,7 +1167,7 @@ class Ostatus_profile extends Managed_DataObject
      * @return Ostatus_profile
      * @throws Exception
      */
-    public static function ensureAtomFeed($feedEl, $hints)
+    public static function ensureAtomFeed(DOMElement $feedEl, array $hints)
     {
         $author = ActivityUtils::getFeedAuthor($feedEl);
 
@@ -1179,7 +1193,7 @@ class Ostatus_profile extends Managed_DataObject
      * @return Ostatus_profile
      * @throws Exception
      */
-    public static function ensureRssChannel($feedEl, $hints)
+    public static function ensureRssChannel(DOMElement $feedEl, array $hints)
     {
         // Special-case for Posterous. They have some nice metadata in their
         // posterous:author elements. We should use them instead of the channel.
@@ -1214,41 +1228,40 @@ class Ostatus_profile extends Managed_DataObject
      * Download and update given avatar image
      *
      * @param string $url
+     * @return Avatar    The Avatar we have on disk. (seldom used)
      * @throws Exception in various failure cases
      */
-    protected function updateAvatar($url)
+    public function updateAvatar($url, $force=false)
     {
-        if ($url == $this->avatar) {
-            // We've already got this one.
-            return;
+        try {
+            // If avatar URL differs: update. If URLs were identical but we're forced: update.
+            if ($url == $this->avatar && !$force) {
+                // If there's no locally stored avatar, throw an exception and continue fetching below.
+                $avatar = Avatar::getUploaded($this->localProfile()) instanceof Avatar;
+                return $avatar;
+            }
+        } catch (NoAvatarException $e) {
+            // No avatar available, let's fetch it.
         }
+
         if (!common_valid_http_url($url)) {
             // TRANS: Server exception. %s is a URL.
             throw new ServerException(sprintf(_m('Invalid avatar URL %s.'), $url));
         }
 
-        if ($this->isGroup()) {
-            // FIXME: throw exception for localGroup
-            $self = $this->localGroup();
-        } else {
-            // this throws an exception already
-            $self = $this->localProfile();
-        }
-        if (!$self) {
-            throw new ServerException(sprintf(
-                // TRANS: Server exception. %s is a URI.
-                _m('Tried to update avatar for unsaved remote profile %s.'),
-                $this->getUri()));
-        }
+        $self = $this->localProfile();
 
         // @todo FIXME: This should be better encapsulated
         // ripped from oauthstore.php (for old OMB client)
         $temp_filename = tempnam(sys_get_temp_dir(), 'listener_avatar');
         try {
-            if (!copy($url, $temp_filename)) {
-                // TRANS: Server exception. %s is a URL.
-                throw new ServerException(sprintf(_m('Unable to fetch avatar from %s.'), $url));
+            $imgData = HTTPClient::quickGet($url);
+            // Make sure it's at least an image file. ImageFile can do the rest.
+            if (false === getimagesizefromstring($imgData)) {
+                throw new UnsupportedMediaException(_('Downloaded group avatar was not an image.'));
             }
+            file_put_contents($temp_filename, $imgData);
+            unset($imgData);    // No need to carry this in memory.
 
             if ($this->isGroup()) {
                 $id = $this->group_id;
@@ -1279,6 +1292,8 @@ class Ostatus_profile extends Managed_DataObject
         $orig = clone($this);
         $this->avatar = $url;
         $this->update($orig);
+
+        return Avatar::getUploaded($self);
     }
 
     /**
@@ -1288,7 +1303,7 @@ class Ostatus_profile extends Managed_DataObject
      * @param array $hints
      * @return mixed URL string or false
      */
-    public static function getActivityObjectAvatar($object, $hints=array())
+    public static function getActivityObjectAvatar(ActivityObject $object, array $hints=array())
     {
         if ($object->avatarLinks) {
             $best = false;
@@ -1317,7 +1332,7 @@ class Ostatus_profile extends Managed_DataObject
      * @param DOMElement $feed
      * @return string
      */
-    protected static function getAvatar($actor, $feed)
+    protected static function getAvatar(ActivityObject $actor, DOMElement $feed)
     {
         $url = '';
         $icon = '';
@@ -1368,7 +1383,7 @@ class Ostatus_profile extends Managed_DataObject
      * @return Ostatus_profile
      * @throws Exception
      */
-    public static function ensureActorProfile($activity, $hints=array())
+    public static function ensureActorProfile(Activity $activity, array $hints=array())
     {
         return self::ensureActivityObjectProfile($activity->actor, $hints);
     }
@@ -1384,7 +1399,7 @@ class Ostatus_profile extends Managed_DataObject
      * @return Ostatus_profile
      * @throws Exception
      */
-    public static function ensureActivityObjectProfile($object, $hints=array())
+    public static function ensureActivityObjectProfile(ActivityObject $object, array $hints=array())
     {
         $profile = self::getActivityObjectProfile($object);
         if ($profile instanceof Ostatus_profile) {
@@ -1400,7 +1415,7 @@ class Ostatus_profile extends Managed_DataObject
      * @return mixed matching Ostatus_profile or false if none known
      * @throws ServerException if feed info invalid
      */
-    public static function getActorProfile($activity)
+    public static function getActorProfile(Activity $activity)
     {
         return self::getActivityObjectProfile($activity->actor);
     }
@@ -1410,7 +1425,7 @@ class Ostatus_profile extends Managed_DataObject
      * @return mixed matching Ostatus_profile or false if none known
      * @throws ServerException if feed info invalid
      */
-    protected static function getActivityObjectProfile($object)
+    protected static function getActivityObjectProfile(ActivityObject $object)
     {
         $uri = self::getActivityObjectProfileURI($object);
         return Ostatus_profile::getKV('uri', $uri);
@@ -1425,7 +1440,7 @@ class Ostatus_profile extends Managed_DataObject
      * @return string
      * @throws ServerException if feed info invalid
      */
-    protected static function getActivityObjectProfileURI($object)
+    protected static function getActivityObjectProfileURI(ActivityObject $object)
     {
         if ($object->id) {
             if (ActivityUtils::validateUri($object->id)) {
@@ -1458,7 +1473,7 @@ class Ostatus_profile extends Managed_DataObject
      *
      * @return Ostatus_profile
      */
-    protected static function createActivityObjectProfile($object, $hints=array())
+    protected static function createActivityObjectProfile(ActivityObject $object, array $hints=array())
     {
         $homeuri = $object->id;
         $discover = false;
@@ -1518,7 +1533,7 @@ class Ostatus_profile extends Managed_DataObject
             $huburi = $discover->getHubLink();
         }
 
-        if (!$huburi && !common_config('feedsub', 'fallback_hub')) {
+        if (!$huburi && !common_config('feedsub', 'fallback_hub') && !common_config('feedsub', 'nohub')) {
             // We can only deal with folks with a PuSH hub
             throw new FeedSubNoHubException();
         }
@@ -1623,7 +1638,7 @@ class Ostatus_profile extends Managed_DataObject
      * @param ActivityObject $object
      * @param array $hints
      */
-    public function updateFromActivityObject($object, $hints=array())
+    public function updateFromActivityObject(ActivityObject $object, array $hints=array())
     {
         if ($this->isGroup()) {
             $group = $this->localGroup();
@@ -1646,7 +1661,7 @@ class Ostatus_profile extends Managed_DataObject
         }
     }
 
-    public static function updateProfile($profile, $object, $hints=array())
+    public static function updateProfile(Profile $profile, ActivityObject $object, array $hints=array())
     {
         $orig = clone($profile);
 
@@ -1711,7 +1726,7 @@ class Ostatus_profile extends Managed_DataObject
         }
     }
 
-    protected static function updateGroup(User_group $group, $object, $hints=array())
+    protected static function updateGroup(User_group $group, ActivityObject $object, array $hints=array())
     {
         $orig = clone($group);
 
@@ -1735,7 +1750,7 @@ class Ostatus_profile extends Managed_DataObject
         }
     }
 
-    protected static function updatePeopletag($tag, $object, $hints=array()) {
+    protected static function updatePeopletag($tag, ActivityObject $object, array $hints=array()) {
         $orig = clone($tag);
 
         $tag->tag = $object->title;
@@ -1756,7 +1771,7 @@ class Ostatus_profile extends Managed_DataObject
         }
     }
 
-    protected static function getActivityObjectHomepage($object, $hints=array())
+    protected static function getActivityObjectHomepage(ActivityObject $object, array $hints=array())
     {
         $homepage = null;
         $poco     = $object->poco;
@@ -1773,7 +1788,7 @@ class Ostatus_profile extends Managed_DataObject
         return $homepage;
     }
 
-    protected static function getActivityObjectLocation($object, $hints=array())
+    protected static function getActivityObjectLocation(ActivityObject $object, array $hints=array())
     {
         $location = null;
 
@@ -1785,8 +1800,8 @@ class Ostatus_profile extends Managed_DataObject
         }
 
         if (!empty($location)) {
-            if (mb_strlen($location) > 255) {
-                $location = mb_substr($note, 0, 255 - 3) . ' … ';
+            if (mb_strlen($location) > 191) {   // not 255 because utf8mb4 takes more space
+                $location = mb_substr($note, 0, 191 - 3) . ' … ';
             }
         }
 
@@ -1795,7 +1810,7 @@ class Ostatus_profile extends Managed_DataObject
         return $location;
     }
 
-    protected static function getActivityObjectBio($object, $hints=array())
+    protected static function getActivityObjectBio(ActivityObject $object, array $hints=array())
     {
         $bio  = null;
 
@@ -1819,7 +1834,7 @@ class Ostatus_profile extends Managed_DataObject
         return $bio;
     }
 
-    public static function getActivityObjectNickname($object, $hints=array())
+    public static function getActivityObjectNickname(ActivityObject $object, array $hints=array())
     {
         if ($object->poco) {
             if (!empty($object->poco->preferredUsername)) {
@@ -1922,7 +1937,7 @@ class Ostatus_profile extends Managed_DataObject
         }
 
         // Try looking it up
-        $oprofile = Ostatus_profile::getKV('uri', 'acct:'.$addr);
+        $oprofile = Ostatus_profile::getKV('uri', Discovery::normalize($addr));
 
         if ($oprofile instanceof Ostatus_profile) {
             self::cacheSet(sprintf('ostatus_profile:webfinger:%s', $addr), $oprofile->getUri());
@@ -1959,7 +1974,9 @@ class Ostatus_profile extends Managed_DataObject
         }
 
         // If we got a feed URL, try that
+        $feedUrl = null;
         if (array_key_exists('feedurl', $hints)) {
+            $feedUrl = $hints['feedurl'];
             try {
                 common_log(LOG_INFO, "Discovery on acct:$addr with feed URL " . $hints['feedurl']);
                 $oprofile = self::ensureFeedURL($hints['feedurl'], $hints);
@@ -1972,7 +1989,9 @@ class Ostatus_profile extends Managed_DataObject
         }
 
         // If we got a profile page, try that!
+        $profileUrl = null;
         if (array_key_exists('profileurl', $hints)) {
+            $profileUrl = $hints['profileurl'];
             try {
                 common_log(LOG_INFO, "Discovery on acct:$addr with profile URL $profileUrl");
                 $oprofile = self::ensureProfileURL($hints['profileurl'], $hints);
@@ -2009,7 +2028,7 @@ class Ostatus_profile extends Managed_DataObject
             $profile->nickname = self::nicknameFromUri($uri);
             $profile->created  = common_sql_now();
 
-            if (isset($profileUrl)) {
+            if (!is_null($profileUrl)) {
                 $profile->profileurl = $profileUrl;
             }
 
@@ -2028,13 +2047,14 @@ class Ostatus_profile extends Managed_DataObject
             $oprofile->profile_id = $profile_id;
             $oprofile->created    = common_sql_now();
 
-            if (isset($feedUrl)) {
-                $profile->feeduri = $feedUrl;
+            if (!is_null($feedUrl)) {
+                $oprofile->feeduri = $feedUrl;
             }
 
             $result = $oprofile->insert();
 
             if ($result === false) {
+                $profile->delete();
                 common_log_db_error($oprofile, 'INSERT', __FILE__);
                 // TRANS: Exception. %s is a webfinger address.
                 throw new Exception(sprintf(_m('Could not save OStatus profile for "%s".'),$addr));
@@ -2072,13 +2092,15 @@ class Ostatus_profile extends Managed_DataObject
                                    'text/html');
 
         $filepath = File::path($filename);
+        $fileurl = File::url($filename);
 
         file_put_contents($filepath, $final);
 
         $file = new File;
 
         $file->filename = $filename;
-        $file->url      = File::url($filename);
+        $file->urlhash  = File::hashurl($fileurl);
+        $file->url      = $fileurl;
         $file->size     = filesize($filepath);
         $file->date     = time();
         $file->mimetype = 'text/html';
@@ -2145,7 +2167,7 @@ class Ostatus_profile extends Managed_DataObject
                 common_log(LOG_WARNING,
                     "OStatus: skipping post with group listed ".
                     "as author: " . $oprofile->getUri() . " in feed from " . $this->getUri());
-                return false;
+                throw new ServerException('Activity author is a non-actor');
             }
         } else {
             $actor = $activity->actor;