This project does not seem to handle request data directly as such no vulnerable execution paths were found.
include
, or for example
via PHP's auto-loading mechanism.
These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more
1 | <?php |
||
2 | |||
3 | /** |
||
4 | * @package Installer |
||
5 | * @author Iurii Makukh <[email protected]> |
||
6 | * @copyright Copyright (c) 2015, Iurii Makukh |
||
7 | * @license https://www.gnu.org/licenses/gpl.html GNU/GPLv3 |
||
8 | */ |
||
9 | |||
10 | namespace gplcart\modules\installer\models; |
||
11 | |||
12 | use Exception; |
||
13 | use gplcart\core\Config; |
||
14 | use gplcart\core\helpers\Url; |
||
15 | use gplcart\core\helpers\Zip; |
||
16 | use gplcart\core\models\Job; |
||
17 | use gplcart\core\models\Module as ModuleModel; |
||
18 | use gplcart\core\models\Translation; |
||
19 | use gplcart\core\Module; |
||
20 | |||
21 | /** |
||
22 | * Manages basic behaviors and data related to Installer module |
||
23 | */ |
||
24 | class Install |
||
25 | { |
||
26 | |||
27 | /** |
||
28 | * Database class instance |
||
29 | * @var \gplcart\core\Database $db |
||
30 | */ |
||
31 | protected $db; |
||
32 | |||
33 | /** |
||
34 | * Config class instance |
||
35 | * @var \gplcart\core\Config $config |
||
36 | */ |
||
37 | protected $config; |
||
38 | |||
39 | /** |
||
40 | * Module model instance |
||
41 | * @var \gplcart\core\Module $module |
||
42 | */ |
||
43 | protected $module; |
||
44 | |||
45 | /** |
||
46 | * Zip helper class instance |
||
47 | * @var \gplcart\core\helpers\Zip $zip |
||
48 | */ |
||
49 | protected $zip; |
||
50 | |||
51 | /** |
||
52 | * Url helper class instance |
||
53 | * @var \gplcart\core\helpers\Url $url |
||
54 | */ |
||
55 | protected $url; |
||
56 | |||
57 | /** |
||
58 | * Translation UI model instance |
||
59 | * @var \gplcart\core\models\Translation $translation |
||
60 | */ |
||
61 | protected $translation; |
||
62 | |||
63 | /** |
||
64 | * Job model instance |
||
65 | * @var \gplcart\core\models\Job $job |
||
66 | */ |
||
67 | protected $job; |
||
68 | |||
69 | /** |
||
70 | * Module model instance |
||
71 | * @var \gplcart\core\models\Module $module_model |
||
72 | */ |
||
73 | protected $module_model; |
||
74 | |||
75 | /** |
||
76 | * The latest validation error |
||
77 | * @var string |
||
78 | */ |
||
79 | protected $error; |
||
80 | |||
81 | /** |
||
82 | * The temporary renamed module directory |
||
83 | * @var string |
||
84 | */ |
||
85 | protected $tempname; |
||
86 | |||
87 | /** |
||
88 | * The module directory |
||
89 | * @var string |
||
90 | */ |
||
91 | protected $destination; |
||
92 | |||
93 | /** |
||
94 | * The module ID |
||
95 | * @var string |
||
96 | */ |
||
97 | protected $module_id; |
||
98 | |||
99 | /** |
||
100 | * An array of module data |
||
101 | * @var array |
||
102 | */ |
||
103 | protected $data; |
||
104 | |||
105 | /** |
||
106 | * Whether an original module has been temporary renamed |
||
107 | * @var boolean |
||
108 | */ |
||
109 | protected $renamed; |
||
110 | |||
111 | /** |
||
112 | * Install constructor. |
||
113 | * @param Config $config |
||
114 | * @param Module $module |
||
115 | * @param ModuleModel $module_model |
||
116 | * @param Translation $translation |
||
117 | * @param Job $job |
||
118 | * @param Zip $zip |
||
119 | * @param Url $url |
||
120 | */ |
||
121 | public function __construct(Config $config, Module $module, ModuleModel $module_model, |
||
122 | Translation $translation, Job $job, Zip $zip, Url $url) |
||
123 | { |
||
124 | $this->config = $config; |
||
125 | $this->module = $module; |
||
126 | $this->db = $this->config->getDb(); |
||
127 | |||
128 | $this->zip = $zip; |
||
129 | $this->url = $url; |
||
130 | $this->job = $job; |
||
131 | $this->translation = $translation; |
||
132 | $this->module_model = $module_model; |
||
133 | } |
||
134 | |||
135 | /** |
||
136 | * Installs a module from a ZIP file |
||
137 | * @param string $zip |
||
138 | * @return string|bool |
||
139 | */ |
||
140 | public function fromZip($zip) |
||
141 | { |
||
142 | $this->data = null; |
||
143 | $this->error = null; |
||
144 | $this->renamed = false; |
||
145 | $this->tempname = null; |
||
146 | $this->module_id = null; |
||
147 | $this->destination = null; |
||
148 | |||
149 | if (!$this->setModuleId($zip)) { |
||
150 | return $this->error; |
||
151 | } |
||
152 | |||
153 | if (!$this->extract()) { |
||
154 | $this->rollback(); |
||
155 | return $this->error; |
||
156 | } |
||
157 | |||
158 | if (!$this->validate()) { |
||
159 | $this->rollback(); |
||
160 | return $this->error; |
||
161 | } |
||
162 | |||
163 | if (!$this->backup()) { |
||
164 | return $this->error; |
||
165 | } |
||
166 | |||
167 | return true; |
||
168 | } |
||
169 | |||
170 | /** |
||
171 | * Install modules from multiple URLs |
||
172 | * @param array $sources |
||
173 | */ |
||
174 | public function fromUrl(array $sources) |
||
175 | { |
||
176 | $total = count($sources); |
||
177 | $finish_message = $this->translation->text('New modules: %inserted, updated: %updated'); |
||
178 | $vars = array('@url' => $this->url->get('', array('download_errors' => true))); |
||
179 | $errors_message = $this->translation->text('New modules: %inserted, updated: %updated, errors: %errors. <a href="@url">See error log</a>', $vars); |
||
180 | |||
181 | $data = array( |
||
182 | 'total' => $total, |
||
183 | 'data' => array('sources' => $sources), |
||
184 | 'id' => 'installer_download_module', |
||
185 | 'log' => array('errors' => $this->getErrorLogFile()), |
||
186 | 'redirect_message' => array('finish' => $finish_message, 'errors' => $errors_message) |
||
187 | ); |
||
188 | |||
189 | $this->job->submit($data); |
||
190 | } |
||
191 | |||
192 | /** |
||
193 | * Returns path to error log file |
||
194 | * @return string |
||
195 | */ |
||
196 | public function getErrorLogFile() |
||
197 | { |
||
198 | return gplcart_file_private_temp('installer-download-errors.csv'); |
||
199 | } |
||
200 | |||
201 | /** |
||
202 | * Backup the previous version of the updated module |
||
203 | */ |
||
204 | protected function backup() |
||
205 | { |
||
206 | if (empty($this->tempname)) { |
||
207 | return true; |
||
208 | } |
||
209 | |||
210 | $module = $this->data; |
||
211 | |||
212 | $module += array( |
||
213 | 'directory' => $this->tempname, |
||
214 | 'module_id' => $this->module_id |
||
215 | ); |
||
216 | |||
217 | $result = $this->getBackupModule()->backup('module', $module); |
||
218 | |||
219 | if ($result === true) { |
||
220 | gplcart_file_delete_recursive($this->tempname); |
||
221 | return true; |
||
222 | } |
||
223 | |||
224 | $this->error = $this->translation->text('Failed to backup module @id', array('@id' => $this->module_id)); |
||
225 | return false; |
||
226 | } |
||
227 | |||
228 | /** |
||
229 | * Returns Backup module instance |
||
230 | * @return \gplcart\modules\backup\Main |
||
231 | */ |
||
232 | protected function getBackupModule() |
||
233 | { |
||
234 | /** @var \gplcart\modules\backup\Main $instance */ |
||
235 | $instance = $this->module->getInstance('backup'); |
||
236 | return $instance; |
||
237 | } |
||
238 | |||
239 | /** |
||
240 | * Extracts module files to the system directory |
||
241 | * @return boolean |
||
242 | */ |
||
243 | protected function extract() |
||
244 | { |
||
245 | $this->destination = GC_DIR_MODULE . "/{$this->module_id}"; |
||
246 | |||
247 | if (file_exists($this->destination)) { |
||
248 | |||
249 | $this->tempname = gplcart_file_unique($this->destination . '~'); |
||
250 | |||
251 | if (!rename($this->destination, $this->tempname)) { |
||
252 | $this->error = $this->translation->text('Failed to rename @old to @new', array( |
||
253 | '@old' => $this->destination, '@new' => $this->tempname)); |
||
254 | return false; |
||
255 | } |
||
256 | |||
257 | $this->renamed = true; |
||
258 | } |
||
259 | |||
260 | if ($this->zip->extract(GC_DIR_MODULE)) { |
||
261 | return true; |
||
262 | } |
||
263 | |||
264 | $this->error = $this->translation->text('Failed to extract to @name', array('@name' => $this->destination)); |
||
265 | return false; |
||
266 | } |
||
267 | |||
268 | /** |
||
269 | * Restore the original module files |
||
270 | */ |
||
271 | protected function rollback() |
||
272 | { |
||
273 | if (!$this->isUpdate() || ($this->isUpdate() && $this->renamed)) { |
||
274 | gplcart_file_delete_recursive($this->destination); |
||
275 | } |
||
276 | |||
277 | if (isset($this->tempname)) { |
||
278 | rename($this->tempname, $this->destination); |
||
279 | } |
||
280 | } |
||
281 | |||
282 | /** |
||
283 | * Validates a module data |
||
284 | * @return boolean |
||
285 | */ |
||
286 | protected function validate() |
||
287 | { |
||
288 | $this->data = $this->module->getInfo($this->module_id); |
||
289 | |||
290 | if (empty($this->data)) { |
||
291 | $this->error = $this->translation->text('Failed to read module @id', array('@id' => $this->module_id)); |
||
292 | return false; |
||
293 | } |
||
294 | |||
295 | try { |
||
296 | $this->module_model->checkCore($this->data); |
||
297 | $this->module_model->checkPhpVersion($this->data); |
||
298 | return true; |
||
299 | } catch (Exception $ex) { |
||
300 | $this->error = $ex->getMessage(); |
||
301 | return false; |
||
302 | } |
||
303 | } |
||
304 | |||
305 | /** |
||
306 | * Returns an array of files from a ZIP file |
||
307 | * @param string $file |
||
308 | * @return array |
||
309 | */ |
||
310 | public function getFilesFromZip($file) |
||
311 | { |
||
312 | try { |
||
313 | $files = $this->zip->set($file)->getList(); |
||
314 | } catch (Exception $e) { |
||
315 | return array(); |
||
316 | } |
||
317 | |||
318 | return count($files) < 2 ? array() : $files; |
||
319 | } |
||
320 | |||
321 | /** |
||
322 | * Set a module id |
||
323 | * @param string $file |
||
324 | * @return boolean |
||
325 | */ |
||
326 | protected function setModuleId($file) |
||
327 | { |
||
328 | $module_id = $this->getModuleIdFromZip($file); |
||
329 | |||
330 | try { |
||
331 | $this->module_model->checkModuleId($module_id); |
||
332 | } catch (Exception $ex) { |
||
333 | $this->error = $ex->getMessage(); |
||
334 | return false; |
||
335 | } |
||
336 | |||
337 | // Do not deal with enabled modules as it may cause fatal results |
||
338 | // Check if the module ID actually has enabled status in the database |
||
339 | // Alternative system methods are based on the scanned module folders so may return incorrect results |
||
340 | if ($this->isEnabledModule($module_id)) { |
||
0 ignored issues
–
show
|
|||
341 | $this->error = $this->translation->text('Module @id is enabled and cannot be updated', array('@id' => $module_id)); |
||
342 | return false; |
||
343 | } |
||
344 | |||
345 | $this->module_id = $module_id; |
||
346 | return true; |
||
347 | } |
||
348 | |||
349 | /** |
||
350 | * Check if a module ID has enabled status in the database |
||
351 | * @param string $module_id |
||
352 | * @return bool |
||
353 | */ |
||
354 | protected function isEnabledModule($module_id) |
||
355 | { |
||
356 | $sql = 'SELECT module_id FROM module WHERE module_id=? AND status > 0'; |
||
357 | $result = $this->db->fetchColumn($sql, array($module_id)); |
||
358 | return !empty($result); |
||
359 | } |
||
360 | |||
361 | /** |
||
362 | * Returns a module id from a zip file or false on error |
||
363 | * @param string $file |
||
364 | * @return boolean|string |
||
365 | */ |
||
366 | public function getModuleIdFromZip($file) |
||
367 | { |
||
368 | $list = $this->getFilesFromZip($file); |
||
369 | |||
370 | if (empty($list)) { |
||
371 | return false; |
||
372 | } |
||
373 | |||
374 | $folder = reset($list); |
||
375 | |||
376 | if (strrchr($folder, '/') !== '/') { |
||
377 | return false; |
||
378 | } |
||
379 | |||
380 | $nested = 0; |
||
381 | foreach ($list as $item) { |
||
382 | if (strpos($item, $folder) === 0) { |
||
383 | $nested++; |
||
384 | } |
||
385 | } |
||
386 | |||
387 | if (count($list) != $nested) { |
||
388 | return false; |
||
389 | } |
||
390 | |||
391 | return rtrim($folder, '/'); |
||
392 | } |
||
393 | |||
394 | /** |
||
395 | * Whether the module files have been updated |
||
396 | * @return bool |
||
397 | */ |
||
398 | public function isUpdate() |
||
399 | { |
||
400 | return isset($this->tempname); |
||
401 | } |
||
402 | |||
403 | } |
||
404 |
This check looks for type mismatches where the missing type is
false
. This is usually indicative of an error condtion.Consider the follow example
This function either returns a new
DateTime
object or false, if there was an error. This is a typical pattern in PHP programming to show that an error has occurred without raising an exception. The calling code should check for this returnedfalse
before passing on the value to another function or method that may not be able to handle afalse
.