[CORE][StoreRemoteMedia] Fixed bug where sometimes images were written outside the site root

This commit is contained in:
Miguel Dantas 2019-07-11 23:49:16 +01:00 committed by Diogo Cordeiro
parent a38f25f7cd
commit 7070a14480
7 changed files with 53 additions and 39 deletions

View File

@ -208,7 +208,8 @@ class AttachmentAction extends ManagedAction
*/ */
static function sendFile(string $filepath, $filesize) { static function sendFile(string $filepath, $filesize) {
if (is_string(common_config('site', 'x-static-delivery'))) { if (is_string(common_config('site', 'x-static-delivery'))) {
$relative_path = end(explode(INSTALLDIR, $filepath)); $tmp = explode(INSTALLDIR, $filepath);
$relative_path = end($tmp);
common_debug("Using Static Delivery with header: '" . common_debug("Using Static Delivery with header: '" .
common_config('site', 'x-static-delivery') . ": {$relative_path}'"); common_config('site', 'x-static-delivery') . ": {$relative_path}'");
header(common_config('site', 'x-static-delivery') . ": {$relative_path}"); header(common_config('site', 'x-static-delivery') . ": {$relative_path}");

View File

@ -617,7 +617,7 @@ class File extends Managed_DataObject
} }
} else { } else {
try { try {
return File_thumbnail::byFile($this)->getPath(); return File_thumbnail::byFile($this, true)->getPath();
} catch (NoResultException $e) { } catch (NoResultException $e) {
// File not stored locally // File not stored locally
throw new FileNotStoredLocallyException($this); throw new FileNotStoredLocallyException($this);
@ -637,10 +637,10 @@ class File extends Managed_DataObject
{ {
if (!empty($thumbnail)) { if (!empty($thumbnail)) {
$filepath = $thumbnail->getPath(); $filepath = $thumbnail->getPath();
} elseif (empty($this->filename)) { } elseif (!empty($this->filename)) {
$filepath = File_thumbnail::byFile($this)->getPath();
} else {
return $this->mimetype; return $this->mimetype;
} else {
$filepath = File_thumbnail::byFile($this, true)->getPath();
} }
$info = @getimagesize($filepath); $info = @getimagesize($filepath);

View File

@ -244,11 +244,9 @@ class File_thumbnail extends Managed_DataObject
*/ */
public function getHtmlAttrs(array $orig=array(), $overwrite=true) public function getHtmlAttrs(array $orig=array(), $overwrite=true)
{ {
$attrs = [ $attrs = [ 'height' => $this->getHeight(),
'height' => $this->getHeight(), 'width' => $this->getWidth(),
'width' => $this->getWidth(), 'src' => $this->getUrl() ];
'src' => $this->getUrl(),
];
return $overwrite ? array_merge($orig, $attrs) : array_merge($attrs, $orig); return $overwrite ? array_merge($orig, $attrs) : array_merge($attrs, $orig);
} }

View File

@ -402,4 +402,19 @@ class HTTPClient extends HTTP_Request2
} while ($maxRedirs); } while ($maxRedirs);
return new GNUsocial_HTTPResponse($response, $this->getUrl(), $redirs); return new GNUsocial_HTTPResponse($response, $this->getUrl(), $redirs);
} }
public static function get_filename(string $url, array $headers = null) : string {
if ($headers === null) {
$head = (new HTTPClient())->head($url);
$headers = $head->getHeader();
$headers = array_change_key_case($headers, CASE_LOWER);
}
if (array_key_exists('content-disposition', $headers) &&
preg_match('/^.+; filename="(.+?)"$/', $headers['content-disposition'], $matches) === 1) {
return $matches[1];
} else {
common_log(LOG_INFO, "Couldn't determine filename for url: {$url}");
return _('Untitled attachment');
}
}
} }

View File

@ -125,12 +125,10 @@ class ImageFile extends MediaFile
} }
// And we'll only consider it an image if it has such a media type // And we'll only consider it an image if it has such a media type
switch ($media) { if($media !== 'image') {
case 'image':
$imgPath = $file->getPath();
break;
default:
throw new UnsupportedMediaException(_m('Unsupported media format.'), $file->getPath()); throw new UnsupportedMediaException(_m('Unsupported media format.'), $file->getPath());
} else if (!empty($file->filename)) {
$imgPath = $file->getPath();
} }
} }
@ -376,7 +374,11 @@ class ImageFile extends MediaFile
$type = $this->preferredType(); $type = $this->preferredType();
$ext = image_type_to_extension($type, true); $ext = image_type_to_extension($type, true);
$outpath = preg_replace("/\.[^\.]+$/", $ext, $outpath); // Decoding returns null if the file is in the old format
$filename = MediaFile::decodeFilename(basename($outpath));
// Encoding null makes the file use 'untitled', and also replaces the extension
$outfilename = MediaFile::encodeFilename($filename, $this->filehash, $ext);
$outpath = dirname($outpath) . DIRECTORY_SEPARATOR . $outfilename;
switch ($type) { switch ($type) {
case IMAGETYPE_GIF: case IMAGETYPE_GIF:
@ -573,9 +575,8 @@ class ImageFile extends MediaFile
return $thumb; return $thumb;
} }
$filename = basename($this->filepath);
$extension = File::guessMimeExtension($this->mimetype); $extension = File::guessMimeExtension($this->mimetype);
$outname = "thumb-{$this->fileRecord->getID()}-{$width}x{$height}-{$filename}"; $outname = "thumb-{$this->fileRecord->getID()}-{$width}x{$height}-{$this->filename}";
$outpath = File_thumbnail::path($outname); $outpath = File_thumbnail::path($outname);
// The boundary box for our resizing // The boundary box for our resizing
@ -601,16 +602,8 @@ class ImageFile extends MediaFile
)); ));
// Perform resize and store into file // Perform resize and store into file
$this->resizeTo($outpath, $box); $outpath = $this->resizeTo($outpath, $box);
$outname = basename($outpath);
try {
// Avoid deleting the original
if (!in_array($this->getPath(), [File::path($this->filename), File_thumbnail::path($this->filename)])) {
$this->unlink();
}
} catch (FileNotFoundException $e) {
// $this->getPath() says the file doesn't exist anyway, so no point in trying to delete it!
}
return File_thumbnail::saveThumbnail( return File_thumbnail::saveThumbnail(
$this->fileRecord->getID(), $this->fileRecord->getID(),

View File

@ -241,7 +241,7 @@ class MediaFile
* having an extension in the file, removing trust in extensions, while keeping the original name * having an extension in the file, removing trust in extensions, while keeping the original name
* @throws ClientException * @throws ClientException
*/ */
public static function encodeFilename(string $original_name, string $filehash, string $ext = null) : string public static function encodeFilename($original_name, string $filehash, $ext = null) : string
{ {
if (empty($original_name)) { if (empty($original_name)) {
$original_name = _('Untitled attachment'); $original_name = _('Untitled attachment');
@ -274,7 +274,8 @@ class MediaFile
*/ */
public static function decodeFilename(string $encoded_filename) public static function decodeFilename(string $encoded_filename)
{ {
$ret = preg_match('/^([^-]+?)-[^-]+$/', $encoded_filename, $matches); // The x is because it is using in thumbnails
$ret = preg_match('/^([^-x]+?)-[^-]+$/', $encoded_filename, $matches);
if ($ret === false) { if ($ret === false) {
return false; return false;
} elseif ($ret === 0) { } elseif ($ret === 0) {
@ -650,7 +651,7 @@ class MediaFile
} elseif ($filename === null) { } elseif ($filename === null) {
// The old file name format was "{hash}.{ext}" so we didn't have a name // The old file name format was "{hash}.{ext}" so we didn't have a name
// This extracts the extension // This extracts the extension
$ret = preg_match('/^.+?\.(.+)$/', $file->filename, $matches); $ret = preg_match('/^.+?\.+?(.+)$/', $file->filename, $matches);
if ($ret !== 1) { if ($ret !== 1) {
common_log(LOG_ERR, $log_error_msg); common_log(LOG_ERR, $log_error_msg);
return _('Untitled attachment'); return _('Untitled attachment');

View File

@ -75,8 +75,15 @@ class StoreRemoteMediaPlugin extends Plugin
'unnecessarily downloading too large files. URL: %s', 'unnecessarily downloading too large files. URL: %s',
$file->getID(), $remoteUrl)); $file->getID(), $remoteUrl));
$url = $remoteUrl;
$head = $http->head($remoteUrl); $head = $http->head($remoteUrl);
$remoteUrl = $head->getEffectiveUrl(); // to avoid going through redirects again $remoteUrl = $head->getEffectiveUrl(); // to avoid going through redirects again
if (empty($remoteUrl)) {
common_log(LOG_ERR, "URL after redirects is somehow empty, for URL {$url}");
return true;
}
if (!$this->checkBlackList($remoteUrl)) { if (!$this->checkBlackList($remoteUrl)) {
common_log(LOG_WARN, sprintf('%s: Non-blacklisted URL %s redirected to blacklisted URL %s', common_log(LOG_WARN, sprintf('%s: Non-blacklisted URL %s redirected to blacklisted URL %s',
__CLASS__, $file->getUrl(), $remoteUrl)); __CLASS__, $file->getUrl(), $remoteUrl));
@ -84,6 +91,7 @@ class StoreRemoteMediaPlugin extends Plugin
} }
$headers = $head->getHeader(); $headers = $head->getHeader();
$headers = array_change_key_case($headers, CASE_LOWER);
$filesize = isset($headers['content-length']) ?: $file->getSize(); $filesize = isset($headers['content-length']) ?: $file->getSize();
if (empty($filesize)) { if (empty($filesize)) {
@ -133,18 +141,16 @@ class StoreRemoteMediaPlugin extends Plugin
//FIXME: Add some code so we don't have to store duplicate File rows for same hash files. //FIXME: Add some code so we don't have to store duplicate File rows for same hash files.
} catch (NoResultException $e) { } catch (NoResultException $e) {
if (preg_match('/^.+; filename="(.+?)"$/', $headers['content-disposition'], $matches) === 1) { $original_name = HTTPClient::get_filename($remoteUrl, $headers);
$filename = MediaFile::encodeFilename($matches[1], $filehash); $filename = MediaFile::encodeFilename($original_name, $filehash);
} else {
common_log(LOG_ERR, "Couldn't determine filename for url: {$remoteUrl}");
// throw new ServerError(_("Couldn't determine filename for url: {$remoteUrl}"));
}
$fullpath = File::path($filename); $fullpath = File::path($filename);
common_debug("StoreRemoteMedia retrieved file with id={$file->id} and will store in {$filename}"); common_debug("StoreRemoteMedia retrieved url {$remoteUrl} for file with id={$file->id} " .
"and will store in {$fullpath}");
// Write the file to disk if it doesn't exist yet. Throw Exception on failure. // Write the file to disk if it doesn't exist yet. Throw Exception on failure.
if (!file_exists($fullpath) && file_put_contents($fullpath, $imgData) === false) { if ((!file_exists($fullpath) || substr($fullpath, 0, strlen(INSTALLDIR)) != INSTALLDIR) &&
file_put_contents($fullpath, $imgData) === false) {
throw new ServerException(_('Could not write downloaded file to disk.')); throw new ServerException(_('Could not write downloaded file to disk.'));
} }