forked from GNUsocial/gnu-social
46f98b3142
The core plugins whose version was attached to GS's were reseted to 2.0.0. 2.0.0 was chosen as reset version for plugins because it is higher than the one that was set by inheriting GS version. Furthermore, it's a major change from prior plugin versioning system thus it also makes semantic sense. Justification for version bump: == GS == 9a4ab31f26 1.19.0c13b935201
1.18.3c13b935201
1.18.218fc39d2cf
1.18.1c083a8bcc2
1.18.0e8783d46d0
1.17.1d9a42550ff
1.17.01536d3ef29
1.16.0c03ed457a6
1.15.0d2e6519bad
1.14.2fe411e8138
1.14.1b17e0b4169
1.14.0daa5f87fd4
1.13.0d75b5d2f4a
1.11.7f6dbf66983
1.11.66cf674f8f8
1.11.57845a09b34
1.11.4e4d432295d
1.11.3339204f1ee
1.11.2a4e679a118
1.11.17967db6ff5
1.11.0bc030da320
1.10.19cc7df51d6
1.10.0bf7f17474d
1.9.28a07edec5f
1.9.10042971d74
1.9.06b5450b7e6
1.8.05dcc98d1c6
1.7.0e6667db0cd
1.6.03290227b50
1.5.0a59c439b46
1.4.0496ab8c920
1.3.10986030060b
1.3.91d529c021a
1.3.8f89c052cf8
1.3.738f2ecefac
1.3.6e473937cb9
1.3.59a39ebe66f
1.3.4ddc3cecfc0
1.3.32b43d484eb
1.3.2e8e487187e
1.3.1 == Plugins == XMPP plugine0887220b0
bump patche186ad57d0
bump patch OStatuse186ad57d0
bump patch Nodeinfoceae66a30f
bump minor586fb5a517
bump major195296846e
bump minor
215 lines
8.6 KiB
PHP
215 lines
8.6 KiB
PHP
<?php
|
|
|
|
if (!defined('GNUSOCIAL')) { exit(1); }
|
|
|
|
// FIXME: To support remote video/whatever files, this plugin needs reworking.
|
|
|
|
class StoreRemoteMediaPlugin extends Plugin
|
|
{
|
|
const PLUGIN_VERSION = '2.0.0';
|
|
|
|
// settings which can be set in config.php with addPlugin('Oembed', array('param'=>'value', ...));
|
|
// WARNING, these are _regexps_ (slashes added later). Always escape your dots and end your strings
|
|
public $domain_whitelist = array( // hostname => service provider
|
|
'^i\d*\.ytimg\.com$' => 'YouTube',
|
|
'^i\d*\.vimeocdn\.com$' => 'Vimeo',
|
|
);
|
|
public $append_whitelist = array(); // fill this array as domain_whitelist to add more trusted sources
|
|
public $check_whitelist = false; // security/abuse precaution
|
|
|
|
public $domain_blacklist = array();
|
|
public $check_blacklist = false;
|
|
|
|
public $max_image_bytes = 10485760; // 10MiB max image size by default
|
|
|
|
protected $imgData = array();
|
|
|
|
// these should be declared protected everywhere
|
|
public function initialize()
|
|
{
|
|
parent::initialize();
|
|
|
|
$this->domain_whitelist = array_merge($this->domain_whitelist, $this->append_whitelist);
|
|
}
|
|
|
|
/**
|
|
* Save embedding information for a File, if applicable.
|
|
*
|
|
* Normally this event is called through File::saveNew()
|
|
*
|
|
* @param File $file The abount-to-be-inserted File object.
|
|
*
|
|
* @return boolean success
|
|
*/
|
|
public function onStartFileSaveNew(File &$file)
|
|
{
|
|
// save given URL as title if it's a media file this plugin understands
|
|
// which will make it shown in the AttachmentList widgets
|
|
|
|
if (isset($file->title) && strlen($file->title)>0) {
|
|
// Title is already set
|
|
return true;
|
|
}
|
|
if (!isset($file->mimetype)) {
|
|
// Unknown mimetype, it's not our job to figure out what it is.
|
|
return true;
|
|
}
|
|
switch (common_get_mime_media($file->mimetype)) {
|
|
case 'image':
|
|
// Just to set something for now at least...
|
|
//$file->title = $file->mimetype;
|
|
break;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public function onCreateFileImageThumbnailSource(File $file, &$imgPath, $media=null)
|
|
{
|
|
// If we are on a private node, we won't do any remote calls (just as a precaution until
|
|
// we can configure this from config.php for the private nodes)
|
|
if (common_config('site', 'private')) {
|
|
return true;
|
|
}
|
|
|
|
if ($media !== 'image') {
|
|
return true;
|
|
}
|
|
|
|
// If there is a local filename, it is either a local file already or has already been downloaded.
|
|
if (!empty($file->filename)) {
|
|
return true;
|
|
}
|
|
|
|
$remoteUrl = $file->getUrl();
|
|
|
|
if (!$this->checkWhiteList($remoteUrl) ||
|
|
!$this->checkBlackList($remoteUrl)) {
|
|
return true;
|
|
}
|
|
|
|
try {
|
|
/*
|
|
$http = new HTTPClient();
|
|
common_debug(sprintf('Performing HEAD request for remote file id==%u to avoid unnecessarily downloading too large files. URL: %s', $file->getID(), $remoteUrl));
|
|
$head = $http->head($remoteUrl);
|
|
$remoteUrl = $head->getEffectiveUrl(); // to avoid going through redirects again
|
|
if (!$this->checkBlackList($remoteUrl)) {
|
|
common_log(LOG_WARN, sprintf('%s: Non-blacklisted URL %s redirected to blacklisted URL %s', __CLASS__, $file->getUrl(), $remoteUrl));
|
|
return true;
|
|
}
|
|
|
|
$headers = $head->getHeader();
|
|
$filesize = isset($headers['content-length']) ? $headers['content-length'] : null;
|
|
*/
|
|
$filesize = $file->getSize();
|
|
if (empty($filesize)) {
|
|
// file size not specified on remote server
|
|
common_debug(sprintf('%s: Ignoring remote media because we did not get a content length for file id==%u', __CLASS__, $file->getID()));
|
|
return true;
|
|
} elseif ($filesize > $this->max_image_bytes) {
|
|
//FIXME: When we perhaps start fetching videos etc. we'll need to differentiate max_image_bytes from that...
|
|
// file too big according to plugin configuration
|
|
common_debug(sprintf('%s: Skipping remote media because content length (%u) is larger than plugin configured max_image_bytes (%u) for file id==%u', __CLASS__, intval($filesize), $this->max_image_bytes, $file->getID()));
|
|
return true;
|
|
} elseif ($filesize > common_config('attachments', 'file_quota')) {
|
|
// file too big according to site configuration
|
|
common_debug(sprintf('%s: Skipping remote media because content length (%u) is larger than file_quota (%u) for file id==%u', __CLASS__, intval($filesize), common_config('attachments', 'file_quota'), $file->getID()));
|
|
return true;
|
|
}
|
|
|
|
// Then we download the file to memory and test whether it's actually an image file
|
|
common_debug(sprintf('Downloading remote file id==%u (should be size %u) with effective URL: %s', $file->getID(), $filesize, _ve($remoteUrl)));
|
|
$imgData = HTTPClient::quickGet($remoteUrl);
|
|
} catch (HTTP_Request2_ConnectionException $e) {
|
|
common_log(LOG_ERR, __CLASS__.': '._ve(get_class($e)).' on URL: '._ve($file->getUrl()).' threw exception: '.$e->getMessage());
|
|
return true;
|
|
}
|
|
$info = @getimagesizefromstring($imgData);
|
|
if ($info === false) {
|
|
throw new UnsupportedMediaException(_('Remote file format was not identified as an image.'), $remoteUrl);
|
|
} elseif (!$info[0] || !$info[1]) {
|
|
throw new UnsupportedMediaException(_('Image file had impossible geometry (0 width or height)'));
|
|
}
|
|
|
|
$filehash = hash(File::FILEHASH_ALG, $imgData);
|
|
try {
|
|
// Exception will be thrown before $file is set to anything, so old $file value will be kept
|
|
$file = File::getByHash($filehash);
|
|
|
|
//FIXME: Add some code so we don't have to store duplicate File rows for same hash files.
|
|
} catch (NoResultException $e) {
|
|
$filename = $filehash . '.' . common_supported_mime_to_ext($info['mime']);
|
|
$fullpath = File::path($filename);
|
|
|
|
// 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) {
|
|
throw new ServerException(_('Could not write downloaded file to disk.'));
|
|
}
|
|
|
|
// Updated our database for the file record
|
|
$orig = clone($file);
|
|
$file->filehash = $filehash;
|
|
$file->filename = $filename;
|
|
$file->width = $info[0]; // array indexes documented on php.net:
|
|
$file->height = $info[1]; // https://php.net/manual/en/function.getimagesize.php
|
|
// Throws exception on failure.
|
|
$file->updateWithKeys($orig);
|
|
}
|
|
// Get rid of the file from memory
|
|
unset($imgData);
|
|
|
|
$imgPath = $file->getPath();
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @return boolean true if given url passes blacklist check
|
|
*/
|
|
protected function checkBlackList($url)
|
|
{
|
|
if (!$this->check_blacklist) {
|
|
return true;
|
|
}
|
|
$host = parse_url($url, PHP_URL_HOST);
|
|
foreach ($this->domain_blacklist as $regex => $provider) {
|
|
if (preg_match("/$regex/", $host)) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/***
|
|
* @return boolean true if given url passes whitelist check
|
|
*/
|
|
protected function checkWhiteList($url)
|
|
{
|
|
if (!$this->check_whitelist) {
|
|
return true;
|
|
}
|
|
$host = parse_url($url, PHP_URL_HOST);
|
|
foreach ($this->domain_whitelist as $regex => $provider) {
|
|
if (preg_match("/$regex/", $host)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public function onPluginVersion(array &$versions)
|
|
{
|
|
$versions[] = array('name' => 'StoreRemoteMedia',
|
|
'version' => self::PLUGIN_VERSION,
|
|
'author' => 'Mikael Nordfeldth',
|
|
'homepage' => 'https://gnu.io/',
|
|
'description' =>
|
|
// TRANS: Plugin description.
|
|
_m('Plugin for downloading remotely attached files to local server.'));
|
|
return true;
|
|
}
|
|
}
|