[Embed] Refactoring and bug fixing

This commit is contained in:
Miguel Dantas 2019-07-14 23:35:11 +01:00 committed by Diogo Cordeiro
parent 462ea26303
commit 0c20d35206
4 changed files with 94 additions and 136 deletions

View 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
}

View 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;
}

View 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;
}

View 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;