]> git.mxchange.org Git - quix0rs-gnu-social.git/blob - plugins/StoreRemoteMedia/StoreRemoteMediaPlugin.php
f38ca4713ecd4b9d7503835d3dac49972ca45ae0
[quix0rs-gnu-social.git] / plugins / StoreRemoteMedia / StoreRemoteMediaPlugin.php
1 <?php
2
3 if (!defined('GNUSOCIAL')) { exit(1); }
4
5 // FIXME: To support remote video/whatever files, this plugin needs reworking.
6
7 class StoreRemoteMediaPlugin extends Plugin
8 {
9     // settings which can be set in config.php with addPlugin('Oembed', array('param'=>'value', ...));
10     // WARNING, these are _regexps_ (slashes added later). Always escape your dots and end your strings
11     public $domain_whitelist = array(       // hostname => service provider
12                                     '^i\d*\.ytimg\.com$' => 'YouTube',
13                                     '^i\d*\.vimeocdn\.com$' => 'Vimeo',
14                                     );
15     public $append_whitelist = array(); // fill this array as domain_whitelist to add more trusted sources
16     public $check_whitelist  = false;    // security/abuse precaution
17
18     public $domain_blacklist = array();
19     public $check_blacklist = false;
20
21     protected $imgData = array();
22
23     // these should be declared protected everywhere
24     public function initialize()
25     {
26         parent::initialize();
27
28         $this->domain_whitelist = array_merge($this->domain_whitelist, $this->append_whitelist);
29     }
30
31     /**
32      * Save embedding information for a File, if applicable.
33      *
34      * Normally this event is called through File::saveNew()
35      *
36      * @param File   $file       The abount-to-be-inserted File object.
37      *
38      * @return boolean success
39      */
40     public function onStartFileSaveNew(File &$file)
41     {
42         // save given URL as title if it's a media file this plugin understands
43         // which will make it shown in the AttachmentList widgets
44
45         if (isset($file->title) && strlen($file->title)>0) {
46             // Title is already set
47             return true;
48         }
49         if (!isset($file->mimetype)) {
50             // Unknown mimetype, it's not our job to figure out what it is.
51             return true;
52         }
53         switch (common_get_mime_media($file->mimetype)) {
54         case 'image':
55             // Just to set something for now at least...
56             $file->title = $file->mimetype;
57             break;
58         }
59         
60         return true;
61     }
62
63     public function onCreateFileImageThumbnailSource(File $file, &$imgPath, $media=null)
64     {
65         // If we are on a private node, we won't do any remote calls (just as a precaution until
66         // we can configure this from config.php for the private nodes)
67         if (common_config('site', 'private')) {
68             return true;
69         }
70
71         if ($media !== 'image') {
72             return true;
73         }
74
75         // If there is a local filename, it is either a local file already or has already been downloaded.
76         if (!empty($file->filename)) {
77             return true;
78         }
79
80         if (!$this->checkWhiteList($file->getUrl()) ||
81             !$this->checkBlackList($file->getUrl())) {
82                     return true;
83         }
84
85         // First we download the file to memory and test whether it's actually an image file
86         common_debug(sprintf('Downloading remote file id==%u with URL: %s', $file->getID(), _ve($file->getUrl())));
87         try {
88             $imgData = HTTPClient::quickGet($file->getUrl());
89         } catch (HTTP_Request2_ConnectionException $e) {
90             common_log(LOG_ERR, __CLASS__.': quickGet on URL: '._ve($file->getUrl()).' threw exception: '.$e->getMessage());
91             return true;
92         }
93         $info = @getimagesizefromstring($imgData);
94         if ($info === false) {
95             throw new UnsupportedMediaException(_('Remote file format was not identified as an image.'), $file->getUrl());
96         } elseif (!$info[0] || !$info[1]) {
97             throw new UnsupportedMediaException(_('Image file had impossible geometry (0 width or height)'));
98         }
99
100         $filehash = hash(File::FILEHASH_ALG, $imgData);
101         try {
102             // Exception will be thrown before $file is set to anything, so old $file value will be kept
103             $file = File::getByHash($filehash);
104
105             //FIXME: Add some code so we don't have to store duplicate File rows for same hash files.
106         } catch (NoResultException $e) {
107             $filename = $filehash . '.' . common_supported_mime_to_ext($info['mime']);
108             $fullpath = File::path($filename);
109
110             // Write the file to disk if it doesn't exist yet. Throw Exception on failure.
111             if (!file_exists($fullpath) && file_put_contents($fullpath, $imgData) === false) {
112                 throw new ServerException(_('Could not write downloaded file to disk.'));
113             }
114
115             // Updated our database for the file record
116             $orig = clone($file);
117             $file->filehash = $filehash;
118             $file->filename = $filename;
119             $file->width = $info[0];    // array indexes documented on php.net:
120             $file->height = $info[1];   // https://php.net/manual/en/function.getimagesize.php
121             // Throws exception on failure.
122             $file->updateWithKeys($orig);
123         }
124         // Get rid of the file from memory
125         unset($imgData);
126
127         $imgPath = $file->getPath();
128
129         return false;
130     }
131
132     /**
133      * @return boolean          true if given url passes blacklist check
134      */
135     protected function checkBlackList($url)
136     {
137         if (!$this->check_blacklist) {
138             return true;
139         }
140         $host = parse_url($url, PHP_URL_HOST);
141         foreach ($this->domain_blacklist as $regex => $provider) {
142             if (preg_match("/$regex/", $host)) {
143                 return false;
144             }
145         }
146
147         return true;
148     }
149
150     /***
151      * @return boolean          true if given url passes whitelist check
152      */
153     protected function checkWhiteList($url)
154     {
155         if (!$this->check_whitelist) {
156             return true;
157         }
158         $host = parse_url($url, PHP_URL_HOST);
159         foreach ($this->domain_whitelist as $regex => $provider) {
160             if (preg_match("/$regex/", $host)) {
161                 return true;
162             }
163         }
164
165         return false;
166     }
167
168     public function onPluginVersion(array &$versions)
169     {
170         $versions[] = array('name' => 'StoreRemoteMedia',
171                             'version' => GNUSOCIAL_VERSION,
172                             'author' => 'Mikael Nordfeldth',
173                             'homepage' => 'https://gnu.io/',
174                             'description' =>
175                             // TRANS: Plugin description.
176                             _m('Plugin for downloading remotely attached files to local server.'));
177         return true;
178     }
179 }