Issues (2407)

administration/controller/extension/installer.php (8 issues)

1
<?php
2
3
/* 	Divine CMS - Open source CMS for widespread use.
4
    Copyright (c) 2019 Mykola Burakov ([email protected])
5
6
    See SOURCE.txt for other and additional information.
7
8
    This file is part of Divine CMS.
9
10
    This program is free software: you can redistribute it and/or modify
11
    it under the terms of the GNU General Public License as published by
12
    the Free Software Foundation, either version 3 of the License, or
13
    (at your option) any later version.
14
15
    This program is distributed in the hope that it will be useful,
16
    but WITHOUT ANY WARRANTY; without even the implied warranty of
17
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18
    GNU General Public License for more details.
19
20
    You should have received a copy of the GNU General Public License
21
    along with this program. If not, see <http://www.gnu.org/licenses/>. */
22
23
class ControllerExtensionInstaller extends \Divine\Engine\Core\Controller
0 ignored issues
show
Coding Style Compatibility introduced by
PSR1 recommends that each class must be in a namespace of at least one level to avoid collisions.

You can fix this by adding a namespace to your class:

namespace YourVendor;

class YourClass { }

When choosing a vendor namespace, try to pick something that is not too generic to avoid conflicts with other libraries.

Loading history...
24
{
25
    public function index()
0 ignored issues
show
Expected 2 blank lines before function; 0 found
Loading history...
26
    {
27
        $this->load->language('extension/installer');
28
29
        $this->document->setTitle($this->language->get('heading_title'));
30
31
        $data['breadcrumbs'] = array();
0 ignored issues
show
Comprehensibility Best Practice introduced by
$data was never initialized. Although not strictly required by PHP, it is generally a good practice to add $data = array(); before regardless.
Loading history...
32
33
        $data['breadcrumbs'][] = array(
34
            'text' => $this->language->get('text_home'),
35
            'href' => $this->url->link('common/dashboard', 'token=' . $this->session->data['token'], true)
36
        );
37
38
        $data['breadcrumbs'][] = array(
39
            'text' => $this->language->get('heading_title'),
40
            'href' => $this->url->link('extension/installer', 'token=' . $this->session->data['token'], true)
41
        );
42
43
44
45
46
47
        $data['heading_title'] = $this->language->get('heading_title');
48
49
        $data['text_upload'] = $this->language->get('text_upload');
50
        $data['text_loading'] = $this->language->get('text_loading');
51
52
        $data['entry_upload'] = $this->language->get('entry_upload');
53
        $data['entry_overwrite'] = $this->language->get('entry_overwrite');
54
        $data['entry_progress'] = $this->language->get('entry_progress');
55
56
        $data['help_upload'] = $this->language->get('help_upload');
57
58
        $data['button_upload'] = $this->language->get('button_upload');
59
        $data['button_clear'] = $this->language->get('button_clear');
60
        $data['button_continue'] = $this->language->get('button_continue');
61
62
63
64
65
        $data['token'] = $this->session->data['token'];
66
67
        $directories = glob($_SERVER['DOCUMENT_ROOT'] . '/storage/upload/' . 'temp-*', GLOB_ONLYDIR);
68
69
        if ($directories) {
70
            $data['error_warning'] = $this->language->get('error_temporary');
71
        } else {
72
            $data['error_warning'] = '';
73
        }
74
75
76
77
        $data['header'] = $this->load->controller('common/header');
78
        $data['column'] = $this->load->controller('common/column_left');
79
        $data['footer'] = $this->load->controller('common/footer');
80
81
        $this->response->setOutput($this->load->view('extension/installer', $data));
82
    }
83
84
    public function upload()
85
    {
86
        $this->load->language('extension/installer');
87
88
        $json = array();
89
90
        // Check user has permission
91
        if (!$this->user->hasPermission('modify', 'extension/installer')) {
92
            $json['error'] = $this->language->get('error_permission');
93
        }
94
95
        if (!$json) {
96
            if (!empty($this->request->files['file']['name'])) {
97
                if (substr($this->request->files['file']['name'], -10) != '.srmod.zip' && substr($this->request->files['file']['name'], -10) != '.srmod.xml') {
98
                    $json['error'] = $this->language->get('error_filetype');
99
                }
100
101
                if ($this->request->files['file']['error'] != UPLOAD_ERR_OK) {
102
                    $json['error'] = $this->language->get('error_upload_' . $this->request->files['file']['error']);
103
                }
104
            } else {
105
                $json['error'] = $this->language->get('error_upload');
106
            }
107
        }
108
109
        if (!$json) {
110
            // If no temp directory exists create it
111
            $path = 'temp-' . (new \Tokenly\TokenGenerator\TokenGenerator())->generateToken(32, 'SR');
112
113
            if (!is_dir($_SERVER['DOCUMENT_ROOT'] . '/storage/upload/' . $path)) {
114
                mkdir($_SERVER['DOCUMENT_ROOT'] . '/storage/upload/' . $path, 0777);
115
            }
116
117
            // Set the steps required for installation
118
            $json['step'] = array();
119
            $json['overwrite'] = array();
120
121
            if (strrchr($this->request->files['file']['name'], '.') == '.xml') {
122
                $file = $_SERVER['DOCUMENT_ROOT'] . '/storage/upload/' . $path . '/install.xml';
123
124
                // If xml file copy it to the temporary directory
125
                move_uploaded_file($this->request->files['file']['tmp_name'], $file);
126
127
                if (file_exists($file)) {
128
                    $json['step'][] = array(
129
                        'text' => $this->language->get('text_xml'),
130
                        'url'  => str_replace('&amp;', '&', $this->url->link('extension/installer/xml', 'token=' . $this->session->data['token'], true)),
131
                        'path' => $path
132
                    );
133
134
                    // Clear temporary files
135
                    $json['step'][] = array(
136
                        'text' => $this->language->get('text_remove'),
137
                        'url'  => str_replace('&amp;', '&', $this->url->link('extension/installer/remove', 'token=' . $this->session->data['token'], true)),
138
                        'path' => $path
139
                    );
140
                } else {
141
                    $json['error'] = $this->language->get('error_file');
142
                }
143
            }
144
145
            // If zip file copy it to the temp directory
146
            if (strrchr($this->request->files['file']['name'], '.') == '.zip') {
147
                $file = $_SERVER['DOCUMENT_ROOT'] . '/storage/upload/' . $path . '/upload.zip';
148
149
                move_uploaded_file($this->request->files['file']['tmp_name'], $file);
150
151
                if (file_exists($file)) {
152
                    $zip = zip_open($file);
153
154
                    if ($zip) {
0 ignored issues
show
$zip is of type resource, thus it always evaluated to false.
Loading history...
155
                        // Zip
156
                        $json['step'][] = array(
157
                            'text' => $this->language->get('text_unzip'),
158
                            'url'  => str_replace('&amp;', '&', $this->url->link('extension/installer/unzip', 'token=' . $this->session->data['token'], true)),
159
                            'path' => $path
160
                        );
161
162
                        // FTP
163
                        $json['step'][] = array(
164
                            'text' => $this->language->get('text_ftp'),
165
                            'url'  => str_replace('&amp;', '&', $this->url->link('extension/installer/ftp', 'token=' . $this->session->data['token'], true)),
166
                            'path' => $path
167
                        );
168
169
                        // Send make and array of actions to carry out
170
                        while ($entry = zip_read($zip)) {
171
                            $zip_name = zip_entry_name($entry);
172
173
                            // SQL
174
                            if (substr($zip_name, 0, 11) == 'install.sql') {
175
                                $json['step'][] = array(
176
                                    'text' => $this->language->get('text_sql'),
177
                                    'url'  => str_replace('&amp;', '&', $this->url->link('extension/installer/sql', 'token=' . $this->session->data['token'], true)),
178
                                    'path' => $path
179
                                );
180
                            }
181
182
                            // XML
183
                            if (substr($zip_name, 0, 11) == 'install.xml') {
184
                                $json['step'][] = array(
185
                                    'text' => $this->language->get('text_xml'),
186
                                    'url'  => str_replace('&amp;', '&', $this->url->link('extension/installer/xml', 'token=' . $this->session->data['token'], true)),
187
                                    'path' => $path
188
                                );
189
                            }
190
191
                            // PHP
192
                            if (substr($zip_name, 0, 11) == 'install.php') {
193
                                $json['step'][] = array(
194
                                    'text' => $this->language->get('text_php'),
195
                                    'url'  => str_replace('&amp;', '&', $this->url->link('extension/installer/php', 'token=' . $this->session->data['token'], true)),
196
                                    'path' => $path
197
                                );
198
                            }
199
200
                            // Compare admin files
201
                            $file = SR_APPLICATION . substr($zip_name, 13);
202
203
                            if (is_file($file) && substr($zip_name, 0, 13) == 'upload/administration/') {
204
                                $json['overwrite'][] = substr($zip_name, 7);
205
                            }
206
207
                            // Compare catalog files
208
                            $file = $_SERVER['DOCUMENT_ROOT'] . '/application/' . substr($zip_name, 15);
209
210
                            if (is_file($file) && substr($zip_name, 0, 15) == 'upload/application/') {
211
                                $json['overwrite'][] = substr($zip_name, 7);
212
                            }
213
214
                            // Compare image files
215
                            $file = $_SERVER['DOCUMENT_ROOT'] . '/public_html/assets/images/' . substr($zip_name, 13);
216
217
                            if (is_file($file) && substr($zip_name, 0, 13) == 'upload/image/') {
218
                                $json['overwrite'][] = substr($zip_name, 7);
219
                            }
220
221
                            // Compare system files
222
                            $file = $_SERVER['DOCUMENT_ROOT'] . '/engine/' . substr($zip_name, 14);
223
224
                            if (is_file($file) && substr($zip_name, 0, 14) == 'upload/engine/') {
225
                                $json['overwrite'][] = substr($zip_name, 7);
226
                            }
227
                        }
228
229
                        // Clear temporary files
230
                        $json['step'][] = array(
231
                            'text' => $this->language->get('text_remove'),
232
                            'url'  => str_replace('&amp;', '&', $this->url->link('extension/installer/remove', 'token=' . $this->session->data['token'], true)),
233
                            'path' => $path
234
                        );
235
236
                        zip_close($zip);
237
                    } else {
238
                        $json['error'] = $this->language->get('error_unzip');
239
                    }
240
                } else {
241
                    $json['error'] = $this->language->get('error_file');
242
                }
243
            }
244
        }
245
246
        $this->response->addHeader('Content-Type: application/json');
247
        $this->response->setOutput(json_encode($json));
248
    }
249
250
    public function unzip()
251
    {
252
        $this->load->language('extension/installer');
253
254
        $json = array();
255
256
        if (!$this->user->hasPermission('modify', 'extension/installer')) {
257
            $json['error'] = $this->language->get('error_permission');
258
        }
259
260
        // Sanitize the filename
261
        $file = $_SERVER['DOCUMENT_ROOT'] . '/storage/upload/' . $this->request->post['path'] . '/upload.zip';
262
263
        if (!is_file($file) || substr(str_replace('\\', '/', realpath($file)), 0, strlen($_SERVER['DOCUMENT_ROOT'] . '/storage/upload/')) != $_SERVER['DOCUMENT_ROOT'] . '/storage/upload/') {
264
            $json['error'] = $this->language->get('error_file');
265
        }
266
267
        if (!$json) {
268
            // Unzip the files
269
            $zip = new ZipArchive();
270
271
            if ($zip->open($file)) {
272
                $zip->extractTo($_SERVER['DOCUMENT_ROOT'] . '/storage/upload/' . $this->request->post['path']);
273
                $zip->close();
274
            } else {
275
                $json['error'] = $this->language->get('error_unzip');
276
            }
277
278
            // Remove Zip
279
            unlink($file);
280
        }
281
282
        $this->response->addHeader('Content-Type: application/json');
283
        $this->response->setOutput(json_encode($json));
284
    }
285
286
    public function sql()
287
    {
288
        $this->load->language('extension/installer');
289
290
        $json = array();
291
292
        if (!$this->user->hasPermission('modify', 'extension/installer')) {
293
            $json['error'] = $this->language->get('error_permission');
294
        }
295
296
        $file = $_SERVER['DOCUMENT_ROOT'] . '/storage/upload/' . $this->request->post['path'] . '/install.sql';
297
298
        if (!is_file($file) || substr(str_replace('\\', '/', realpath($file)), 0, strlen($_SERVER['DOCUMENT_ROOT'] . '/storage/upload/')) != $_SERVER['DOCUMENT_ROOT'] . '/storage/upload/') {
299
            $json['error'] = $this->language->get('error_file');
300
        }
301
302
        if (!$json) {
303
            $lines = file($file);
304
305
            if ($lines) {
306
                try {
307
                    $sql = '';
308
309
                    foreach ($lines as $line) {
310
                        if ($line && (substr($line, 0, 2) != '--') && (substr($line, 0, 1) != '#')) {
311
                            $sql .= $line;
312
313
                            if (preg_match('/;\s*$/', $line)) {
314
                                // $sql = str_replace(" `oc_", " `" . DB_PREFIX, $sql);
315
316
                                $this->db->query($sql);
317
318
                                $sql = '';
319
                            }
320
                        }
321
                    }
322
                } catch (Exception $exception) {
323
                    $json['error'] = sprintf($this->language->get('error_exception'), $exception->getCode(), $exception->getMessage(), $exception->getFile(), $exception->getLine());
324
                }
325
            }
326
        }
327
328
        $this->response->addHeader('Content-Type: application/json');
329
        $this->response->setOutput(json_encode($json));
330
    }
331
332
    public function xml()
333
    {
334
        $this->load->language('extension/installer');
335
336
        $json = array();
337
338
        if (!$this->user->hasPermission('modify', 'extension/installer')) {
339
            $json['error'] = $this->language->get('error_permission');
340
        }
341
342
        $file = $_SERVER['DOCUMENT_ROOT'] . '/storage/upload/' . $this->request->post['path'] . '/install.xml';
343
344
        if (!is_file($file) || substr(str_replace('\\', '/', realpath($file)), 0, strlen($_SERVER['DOCUMENT_ROOT'] . '/storage/upload/')) != $_SERVER['DOCUMENT_ROOT'] . '/storage/upload/') {
345
            $json['error'] = $this->language->get('error_file');
346
        }
347
348
        if (!$json) {
0 ignored issues
show
Blank line found at start of control structure
Loading history...
349
350
            // If xml file just put it straight into the DB
351
            $xml = file_get_contents($file);
352
353
            if ($xml) {
354
                try {
355
                    $dom = new \DOMDocument('1.0', 'UTF-8');
356
                    $dom->loadXml($xml);
357
358
                    $name = $dom->getElementsByTagName('name')->item(0);
359
360
                    if ($name) {
361
                        $name = $name->nodeValue;
362
                    } else {
363
                        $name = '';
364
                    }
365
366
                    $code = $dom->getElementsByTagName('code')->item(0);
367
368
                    $author = $dom->getElementsByTagName('author')->item(0);
369
370
                    if ($author) {
371
                        $author = $author->nodeValue;
372
                    } else {
373
                        $author = '';
374
                    }
375
376
                    $version = $dom->getElementsByTagName('version')->item(0);
377
378
                    if ($version) {
379
                        $version = $version->nodeValue;
380
                    } else {
381
                        $version = '';
382
                    }
383
384
                    $link = $dom->getElementsByTagName('link')->item(0);
385
386
                    if ($link) {
387
                        $link = $link->nodeValue;
388
                    } else {
389
                        $link = '';
390
                    }
391
392
                    $modification_data = array(
0 ignored issues
show
The assignment to $modification_data is dead and can be removed.
Loading history...
393
                        'name'    => $name,
394
                        'code'    => $code,
395
                        'author'  => $author,
396
                        'version' => $version,
397
                        'link'    => $link,
398
                        'xml'     => $xml,
399
                        'status'  => 1
400
                    );
401
                } catch (Exception $exception) {
402
                    $json['error'] = sprintf($this->language->get('error_exception'), $exception->getCode(), $exception->getMessage(), $exception->getFile(), $exception->getLine());
403
                }
404
            }
405
        }
406
407
        $this->response->addHeader('Content-Type: application/json');
408
        $this->response->setOutput(json_encode($json));
409
    }
410
411
    public function php()
412
    {
413
        $this->load->language('extension/installer');
414
415
        $json = array();
416
417
        if (!$this->user->hasPermission('modify', 'extension/installer')) {
418
            $json['error'] = $this->language->get('error_permission');
419
        }
420
421
        $file = $_SERVER['DOCUMENT_ROOT'] . '/storage/upload/' . $this->request->post['path'] . '/install.php';
422
423
        if (!is_file($file) || substr(str_replace('\\', '/', realpath($file)), 0, strlen($_SERVER['DOCUMENT_ROOT'] . '/storage/upload/')) != $_SERVER['DOCUMENT_ROOT'] . '/storage/upload/') {
424
            $json['error'] = $this->language->get('error_file');
425
        }
426
427
        if (!$json) {
428
            try {
429
                include($file);
430
            } catch (Exception $exception) {
431
                $json['error'] = sprintf($this->language->get('error_exception'), $exception->getCode(), $exception->getMessage(), $exception->getFile(), $exception->getLine());
432
            }
433
        }
434
435
        $this->response->addHeader('Content-Type: application/json');
436
        $this->response->setOutput(json_encode($json));
437
    }
438
439
    public function remove()
440
    {
441
        $this->load->language('extension/installer');
442
443
        $json = array();
444
445
        if (!$this->user->hasPermission('modify', 'extension/installer')) {
446
            $json['error'] = $this->language->get('error_permission');
447
        }
448
449
        $directory = $_SERVER['DOCUMENT_ROOT'] . '/storage/upload/' . $this->request->post['path'];
450
451
        if (!is_dir($directory) || substr(str_replace('\\', '/', realpath($directory)), 0, strlen($_SERVER['DOCUMENT_ROOT'] . '/storage/upload/')) != $_SERVER['DOCUMENT_ROOT'] . '/storage/upload/') {
452
            $json['error'] = $this->language->get('error_directory');
453
        }
454
455
        if (!$json) {
456
            // Get a list of files ready to upload
457
            $files = array();
458
459
            $path = array($directory);
460
461
            while (count($path) != 0) {
462
                $next = array_shift($path);
463
464
                // We have to use scandir function because glob will not pick up dot files.
465
                foreach (array_diff(scandir($next), array('.', '..')) as $file) {
0 ignored issues
show
It seems like scandir($next) can also be of type false; however, parameter $array1 of array_diff() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

465
                foreach (array_diff(/** @scrutinizer ignore-type */ scandir($next), array('.', '..')) as $file) {
Loading history...
466
                    $file = $next . '/' . $file;
467
468
                    if (is_dir($file)) {
469
                        $path[] = $file;
470
                    }
471
472
                    $files[] = $file;
473
                }
474
            }
475
476
            rsort($files);
477
478
            foreach ($files as $file) {
479
                if (is_file($file)) {
480
                    unlink($file);
481
                } elseif (is_dir($file)) {
482
                    rmdir($file);
483
                }
484
            }
485
486
            if (file_exists($directory)) {
487
                rmdir($directory);
488
            }
489
490
            $json['success'] = $this->language->get('text_success');
491
        }
492
493
        $this->response->addHeader('Content-Type: application/json');
494
        $this->response->setOutput(json_encode($json));
495
    }
496
497
    public function clear()
498
    {
499
        $this->load->language('extension/installer');
500
501
        $json = array();
502
503
        if (!$this->user->hasPermission('modify', 'extension/installer')) {
504
            $json['error'] = $this->language->get('error_permission');
505
        }
506
507
        if (!$json) {
508
            $directories = glob($_SERVER['DOCUMENT_ROOT'] . '/storage/upload/' . 'temp-*', GLOB_ONLYDIR);
509
510
            if ($directories) {
511
                foreach ($directories as $directory) {
512
                    // Get a list of files ready to upload
513
                    $files = array();
514
515
                    $path = array($directory);
516
517
                    while (count($path) != 0) {
518
                        $next = array_shift($path);
519
520
                        // We have to use scandir function because glob will not pick up dot files.
521
                        foreach (array_diff(scandir($next), array('.', '..')) as $file) {
0 ignored issues
show
It seems like scandir($next) can also be of type false; however, parameter $array1 of array_diff() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

521
                        foreach (array_diff(/** @scrutinizer ignore-type */ scandir($next), array('.', '..')) as $file) {
Loading history...
522
                            $file = $next . '/' . $file;
523
524
                            if (is_dir($file)) {
525
                                $path[] = $file;
526
                            }
527
528
                            $files[] = $file;
529
                        }
530
                    }
531
532
                    rsort($files);
533
534
                    foreach ($files as $file) {
535
                        if (is_file($file)) {
536
                            unlink($file);
537
                        } elseif (is_dir($file)) {
538
                            rmdir($file);
539
                        }
540
                    }
541
542
                    if (file_exists($directory)) {
543
                        rmdir($directory);
544
                    }
545
                }
546
            }
547
548
            $json['success'] = $this->language->get('text_clear');
549
        }
550
551
        $this->response->addHeader('Content-Type: application/json');
552
        $this->response->setOutput(json_encode($json));
553
    }
554
}
555