]> git.mxchange.org Git - quix0rs-gnu-social.git/commitdiff
[Embed] Refactoring and bug fixing
authorMiguel Dantas <biodantasgs@gmail.com>
Sun, 14 Jul 2019 22:35:11 +0000 (23:35 +0100)
committerDiogo Cordeiro <diogo@fc.up.pt>
Sat, 3 Aug 2019 16:49:09 +0000 (17:49 +0100)
lib/schema.php
plugins/Embed/EmbedPlugin.php
plugins/Embed/classes/File_embed.php
public/plugins/Embed/css/embed.css

index bba6d1781c325757af650486f8f45293614aa81b..1b0a38ea8b1038c4d967308ce72023f4be896136 100644 (file)
@@ -1073,7 +1073,7 @@ class Schema
             try {
                 $this->getTableDef($new_name);
                 // New table exists, can't work
-                throw new ServerException("Both table {$old_name} and {$new_name} exist. You're on your own");
+                throw new ServerException("Both table {$old_name} and {$new_name} exist. You're on your own.");
             } catch(SchemaTableMissingException $e) {
                 // New table doesn't exist, carry on
             }
index b129e08ed1b6391a45d233e4b96ef466c7876618..cfd9c0005dce4778277b59c05ff5f6b84e3dcf1d 100644 (file)
@@ -170,10 +170,10 @@ class EmbedPlugin extends Plugin
             // sometimes sites serve the path, not the full URL, for images
             // let's "be liberal in what you accept from others"!
             // add protocol and host if the thumbnail_url starts with /
-            if (substr($metadata->thumbnail_url, 0, 1) == '/') {
+            if ($metadata->thumbnail_url[0] == '/') {
                 $thumbnail_url_parsed = parse_url($metadata->url);
-                $metadata->thumbnail_url = $thumbnail_url_parsed['scheme']."://".
-                                         $thumbnail_url_parsed['host'].$metadata->thumbnail_url;
+                $metadata->thumbnail_url = "{$thumbnail_url_parsed['scheme']}://".
+                                         "{$thumbnail_url_parsed['host']}{$metadata->thumbnail_url}";
             }
 
             // some wordpress opengraph implementations sometimes return a white blank image
@@ -191,59 +191,33 @@ class EmbedPlugin extends Plugin
     {
         switch ($action->getActionName()) {
         case 'attachment':
-            $action->element('link', array('rel'=>'alternate',
-                'type'=>'application/json+oembed',
-                'href'=>common_local_url(
-                    'oembed',
-                    array(),
-                    array('format'=>'json', 'url'=>
-                        common_local_url(
-                            'attachment',
-                            array('attachment' => $action->attachment->getID())
-                        ))
-                ),
-                'title'=>'oEmbed'));
-            $action->element('link', array('rel'=>'alternate',
-                'type'=>'text/xml+oembed',
-                'href'=>common_local_url(
-                    'oembed',
-                    array(),
-                    array('format'=>'xml','url'=>
-                        common_local_url(
-                            'attachment',
-                            array('attachment' => $action->attachment->getID())
-                        ))
-                ),
-                'title'=>'oEmbed'));
+            $url = common_local_url('attachment', array('attachment' => $action->attachment->getID()));
             break;
         case 'shownotice':
             if (!$action->notice->isLocal()) {
-                break;
+                return true;
             }
             try {
-                $action->element('link', array('rel'=>'alternate',
-                    'type'=>'application/json+oembed',
-                    'href'=>common_local_url(
-                        'oembed',
-                        array(),
-                        array('format'=>'json','url'=>$action->notice->getUrl())
-                    ),
-                    'title'=>'oEmbed'));
-                $action->element('link', array('rel'=>'alternate',
-                    'type'=>'text/xml+oembed',
-                    'href'=>common_local_url(
-                        'oembed',
-                        array(),
-                        array('format'=>'xml','url'=>$action->notice->getUrl())
-                    ),
-                    'title'=>'oEmbed'));
+                $url = $action->notice->getUrl();
             } catch (InvalidUrlException $e) {
                 // The notice is probably a share or similar, which don't
                 // have a representational URL of their own.
+                return true;
             }
             break;
         }
 
+        if (isset($url)) {
+            foreach (['xml', 'json'] as $format) {
+                $action->element('link',
+                                 array('rel'   =>'alternate',
+                                       'type'  => "application/{$format}+oembed",
+                                       'href'  => common_local_url('oembed',
+                                                                   array(),
+                                                                   array('format' => $format, 'url' => $url)),
+                                       'title' => 'oEmbed'));
+            }
+        }
         return true;
     }
 
@@ -258,29 +232,30 @@ class EmbedPlugin extends Plugin
      *
      * Normally this event is called through File::saveNew()
      *
-     * @param File   $file       The newly inserted File object.
+     * @param File $file  The newly inserted File object.
      *
      * @return boolean success
      */
     public function onEndFileSaveNew(File $file)
     {
-        $fo = File_embed::getKV('file_id', $file->getID());
-        if ($fo instanceof File_embed) {
+        $fe = File_embed::getKV('file_id', $file->getID());
+        if ($fe instanceof File_embed) {
             common_log(LOG_WARNING, "Strangely, a File_embed object exists for new file {$file->getID()}", __FILE__);
             return true;
         }
 
         if (isset($file->mimetype)
-            && (('text/html' === substr($file->mimetype, 0, 9)
-            || 'application/xhtml+xml' === substr($file->mimetype, 0, 21)))) {
+            && (('text/html' === substr($file->mimetype, 0, 9) ||
+                 'application/xhtml+xml' === substr($file->mimetype, 0, 21)))) {
             try {
-                $embed_data = File_embed::_getEmbed($file->url);
+                $embed_data = File_embed::getEmbed($file->url);
                 if ($embed_data === false) {
-                    throw new Exception('Did not get embed data from URL');
+                    throw new Exception("Did not get Embed data from URL {$file->url}");
                 }
                 $file->setTitle($embed_data->title);
             } catch (Exception $e) {
-                common_log(LOG_WARNING, sprintf(__METHOD__.': %s thrown when getting embed data: %s', get_class($e), _ve($e->getMessage())));
+                common_log(LOG_WARNING, sprintf(__METHOD__.': %s thrown when getting embed data: %s',
+                                                get_class($e), _ve($e->getMessage())));
                 return true;
             }
 
@@ -296,30 +271,20 @@ class EmbedPlugin extends Plugin
             return true;
         }
         $out->elementStart('div', array('id'=>'oembed_info', 'class'=>'e-content'));
-        if (!empty($embed->author_name)) {
-            $out->elementStart('div', 'fn vcard author');
-            if (empty($embed->author_url)) {
-                $out->text($embed->author_name);
-            } else {
-                $out->element(
-                    'a',
-                    array('href' => $embed->author_url,
-                          'class' => 'url'),
-                    $embed->author_name
-                );
-            }
-        }
-        if (!empty($embed->provider)) {
-            $out->elementStart('div', 'fn vcard');
-            if (empty($embed->provider_url)) {
-                $out->text($embed->provider);
-            } else {
-                $out->element(
-                    'a',
-                    array('href' => $embed->provider_url,
-                          'class' => 'url'),
-                    $embed->provider
-                );
+        foreach (['author_name' => ['class' => ' author', 'url' => 'author_url'],
+                  'provider'    => ['class' => '',        'url' => 'provider_url']]
+                 as $field => $options) {
+            if (!empty($embed->{$field})) {
+                $out->elementStart('div', "fn vcard" . $options['class']);
+                if (empty($embed->{$options['url']})) {
+                    $out->text($embed->{$field});
+                } else {
+                    $out->element('a',
+                                  array('href' => $embed->{$options['url']},
+                                        'class' => 'url'),
+                                  $embed->{$field}
+                    );
+                }
             }
         }
         $out->elementEnd('div');
@@ -334,7 +299,7 @@ class EmbedPlugin extends Plugin
             return true;
         }
 
-        foreach (array('mimetype', 'url', 'title', 'modified', 'width', 'height') as $key) {
+        foreach (['mimetype', 'url', 'title', 'modified', 'width', 'height'] as $key) {
             if (isset($embed->{$key}) && !empty($embed->{$key})) {
                 $enclosure->{$key} = $embed->{$key};
             }
@@ -396,7 +361,7 @@ class EmbedPlugin extends Plugin
         }
         $out->elementEnd('div');
         $out->elementEnd('header');
-        $out->elementStart('div', ['class'=>'p-summary oembed']);
+        $out->elementStart('div', ['class'=>'p-summary embed']);
         $out->raw(common_purify($embed->html));
         $out->elementEnd('div');
         $out->elementStart('footer');
@@ -422,7 +387,8 @@ class EmbedPlugin extends Plugin
                     && (GNUsocial::isAjax() || common_config('attachments', 'show_html'))) {
                 require_once INSTALLDIR.'/extlib/HTMLPurifier/HTMLPurifier.auto.php';
                 $purifier = new HTMLPurifier();
-                // FIXME: do we allow <object> and <embed> here? we did that when we used htmLawed, but I'm not sure anymore...
+                // FIXME: do we allow <object> and <embed> here? we did that when we used htmLawed,
+                // but I'm not sure anymore...
                 $out->raw($purifier->purify($embed->html));
             }
             return false;
@@ -510,27 +476,21 @@ class EmbedPlugin extends Plugin
      *
      * @return string|bool the file size if it succeeds, false otherwise.
      */
-    private function getRemoteFileSize($url)
+    private function getRemoteFileSize($url, $headers = null)
     {
         try {
-            if (empty($url)) {
-                return false;
-            }
-            stream_context_set_default(array('http' => array('method' => 'HEAD')));
-            $head = @get_headers($url, 1);
-            if (gettype($head)=="array") {
-                $head = array_change_key_case($head);
-                $size = isset($head['content-length']) ? $head['content-length'] : 0;
-
-                if (!$size) {
+            if ($headers === null) {
+                if (!common_valid_http_url($url)) {
+                    common_log(LOG_ERR, "Invalid URL in Embed::getRemoteFileSize()");
                     return false;
                 }
-            } else {
-                return false;
+                $head = (new HTTPClient())->head($url);
+                $headers = $head->getHeader();
             }
-            return $size; // return formatted size
+            return $headers['content-length'] ?: false;
         } catch (Exception $err) {
-            common_log(LOG_ERR, __CLASS__.': getRemoteFileSize on URL : '._ve($url).' threw exception: '.$err->getMessage());
+            common_log(LOG_ERR, __CLASS__.': getRemoteFileSize on URL : '._ve($url).
+                       ' threw exception: '.$err->getMessage());
             return false;
         }
     }
@@ -541,31 +501,17 @@ class EmbedPlugin extends Plugin
      *
      * @return bool true if the remote URL is an image, or false otherwise.
      */
-    private function isRemoteImage($url)
+    private function isRemoteImage($url, $headers = null)
     {
-        if (!filter_var($url, FILTER_VALIDATE_URL)) {
-            common_log(LOG_ERR, "Invalid URL in Embed::isRemoteImage()");
-            return false;
-        }
-        if ($url==null) {
-            common_log(LOG_ERR, "URL not specified in Embed::isRemoteImage()");
-            return false;
-        }
-        try {
-            $curl = curl_init($url);
-            curl_setopt($curl, CURLOPT_RETURNTRANSFER, true);
-            curl_setopt($curl, CURLOPT_HEADER, true);
-            curl_setopt($curl, CURLOPT_NOBODY, true);
-            curl_exec($curl);
-            $type    = curl_getinfo($curl, CURLINFO_CONTENT_TYPE);
-            if (strpos($type, 'image') !== false) {
-                return true;
-            } else {
+        if (empty($headers)) {
+            if (!common_valid_http_url($url)) {
+                common_log(LOG_ERR, "Invalid URL in Embed::isRemoteImage()");
                 return false;
             }
-        } finally {
-            return false;
+            $head = (new HTTPClient())->head($url);
+            $headers = $head->getHeader();
         }
+        return !empty($headers['content-type']) && common_get_mime_media($headers['content-type']) === 'image';
     }
 
     /**
@@ -585,11 +531,14 @@ class EmbedPlugin extends Plugin
         $url = $thumbnail->getUrl();
         $this->checkWhitelist($url);
 
+        $head = (new HTTPClient())->head($url);
+        $headers = $head->getHeader();
+
         try {
-            $isImage = $this->isRemoteImage($url);
+            $isImage = $this->isRemoteImage($url, $headers);
             if ($isImage==true) {
                 $max_size  = common_get_preferred_php_upload_limit();
-                $file_size = $this->getRemoteFileSize($url);
+                $file_size = $this->getRemoteFileSize($url, $headers);
                 if (($file_size!=false) && ($file_size > $max_size)) {
                     common_debug("Went to store remote thumbnail of size " . $file_size .
                                  " but the upload limit is " . $max_size . " so we aborted.");
@@ -613,20 +562,29 @@ class EmbedPlugin extends Plugin
             throw new UnsupportedMediaException(_('Image file had impossible geometry (0 width or height)'));
         }
 
-        $ext = File::guessMimeExtension($info['mime']);
+        $filehash = hash(File::FILEHASH_ALG, $imgData);
 
         try {
-            // We'll trust sha256 (File::FILEHASH_ALG) not to have collision issues any time soon :)
-            $original_filename = bin2hex('embed.' . $ext);
-            $filehash = hash(File::FILEHASH_ALG, $imgData);
-            $filename = "{$original_filename}-{$filehash}";
+            $original_name = HTTPClient::get_filename($url, $headers);
+            $filename = MediaFile::encodeFilename($original_name, $filehash);
             $fullpath = File_thumbnail::path($filename);
             // Write the file to disk. Throw Exception on failure
-            if (!file_exists($fullpath) && file_put_contents($fullpath, $imgData) === false) {
-                throw new ServerException(_('Could not write downloaded file to disk.'));
+            if (!file_exists($fullpath)) {
+                if (strpos($fullpath, INSTALLDIR) !== 0 || file_put_contents($fullpath, $imgData) === false) {
+                    throw new ServerException(_('Could not write downloaded file to disk.'));
+                }
+
+                if (common_get_mime_media(MediaFile::getUploadedMimeType($fullpath)) !== 'image') {
+                    @unlink($fullpath);
+                    throw new UnsupportedMediaException(
+                        _('Remote file format was not identified as an image.'), $url);
+                }
+            } else {
+                throw new AlreadyFulfilledException('A thumbnail seems to already exist for remote file with id==' .
+                                                    $thumbnail->file_id);
             }
         } catch (Exception $err) {
-            common_log(LOG_ERROR, "Went to write a thumbnail to disk in EmbedPlugin::storeRemoteThumbnail " .
+            common_log(LOG_ERR, "Went to write a thumbnail to disk in EmbedPlugin::storeRemoteThumbnail " .
                        "but encountered error: {$err}");
             return $err;
         } finally {
@@ -642,7 +600,7 @@ class EmbedPlugin extends Plugin
             // Throws exception on failure.
             $thumbnail->updateWithKeys($orig);
         } catch (exception $err) {
-            common_log(LOG_ERROR, "Went to write a thumbnail entry to the database in " .
+            common_log(LOG_ERR, "Went to write a thumbnail entry to the database in " .
                        "EmbedPlugin::storeRemoteThumbnail but encountered error: ".$err);
             return $err;
         }
index c927da9f5b8132ae3e78df1d46c39bd4417d884c..0701a1b37baeab6ee6fcffba45c8229890a2087b 100644 (file)
@@ -77,12 +77,12 @@ class File_embed extends Managed_DataObject
         );
     }
 
-    public static function _getEmbed($url)
+    public static function getEmbed($url)
     {
         try {
             return EmbedHelper::getObject($url);
         } catch (Exception $e) {
-            common_log(LOG_INFO, "Error during oembed lookup for $url - " . $e->getMessage());
+            common_log(LOG_INFO, "Error during Embed lookup for {$url} - " . $e->getMessage());
             return false;
         }
     }
@@ -116,16 +116,16 @@ class File_embed extends Managed_DataObject
         $file_embed = new File_embed;
         $file_embed->file_id = $file_id;
         if (!isset($data->version)) {
-            common_debug('DEBUGGING oEmbed: data->version undefined in variable $data: '.var_export($data, true));
+            common_debug('Embed: data->version undefined in variable $data: '.var_export($data, true));
         }
         $file_embed->version = $data->version;
         $file_embed->type = $data->type;
-        if (!empty($data->provider_name)) {
-            $file_embed->provider = $data->provider_name;
-        }
         if (!empty($data->provider)) {
             $file_embed->provider = $data->provider;
         }
+        if (!empty($data->provider_name)) {
+            $file_embed->provider = $data->provider_name;
+        }
         if (!empty($data->provider_url)) {
             $file_embed->provider_url = $data->provider_url;
         }
index 9750f0277967663ecdfa5a069547287ebedd7878..06388fa9aad6407e2cce9860754abd50a5f3acb2 100644 (file)
@@ -1,14 +1,14 @@
-.u-photo.oembed {
+.u-photo.embed {
     float: left;
     margin-bottom: 1ex;
     margin-right: 1em;
 }
 
-.p-author.oembed {
+.p-author.embed {
     font-style: italic;
 }
 
-.p-summary.oembed {
+.p-summary.embed {
     line-height: 1.25em;
     max-height: 5em;
     overflow: auto;