Passed
Pull Request — master (#1373)
by Michael
10:46
created

Upgrade_2511::apply_zapsmarty()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 34
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 18
c 1
b 0
f 0
nc 5
nop 0
dl 0
loc 34
rs 9.3554
1
<?php
2
3
use Xmf\Database\Tables;
4
5
/**
6
 * Upgrade from 2.5.10 to 2.5.11
7
 *
8
 * @copyright    (c) 2000-2022 XOOPS Project (https://xoops.org)
9
 * @license          GNU GPL 2 (https://www.gnu.org/licenses/gpl-2.0.html)
10
 * @package          Upgrade
11
 * @since            2.5.11
12
 * @author           XOOPS Team
13
 */
14
15
class Upgrade_2511 extends XoopsUpgrade
16
{
17
    /**
18
     * __construct
19
     */
20
    public function __construct()
21
    {
22
        parent::__construct(basename(__DIR__));
23
        $this->tasks = array(
24
            'bannerintsize',
25
            'captchadata',
26
            'modulesvarchar',
27
            'qmail',
28
            'rmindexhtml',
29
            'textsanitizer',
30
            'xoopsconfig',
31
            'templates',
32
            'zapsmarty',
33
        );
34
        $this->usedFiles = array();
35
        $this->pathsToCheck = array(
36
            XOOPS_ROOT_PATH . '/cache',
37
            XOOPS_ROOT_PATH . '/class',
38
            XOOPS_ROOT_PATH . '/Frameworks',
39
            XOOPS_ROOT_PATH . '/images',
40
            XOOPS_ROOT_PATH . '/include',
41
            XOOPS_ROOT_PATH . '/kernel',
42
            XOOPS_ROOT_PATH . '/language',
43
            XOOPS_ROOT_PATH . '/media',
44
            XOOPS_ROOT_PATH . '/modules/pm',
45
            XOOPS_ROOT_PATH . '/modules/profile',
46
            XOOPS_ROOT_PATH . '/modules/protector',
47
            XOOPS_ROOT_PATH . '/modules/system',
48
            XOOPS_ROOT_PATH . '/templates_c',
49
            XOOPS_ROOT_PATH . '/themes/default',
50
            XOOPS_ROOT_PATH . '/themes/xbootstrap',
51
            XOOPS_ROOT_PATH . '/themes/xswatch',
52
            XOOPS_ROOT_PATH . '/themes/xswatch4',
53
            XOOPS_ROOT_PATH . '/uploads',
54
            XOOPS_VAR_PATH,
55
            XOOPS_PATH,
56
        );
57
    }
58
59
60
    /**
61
     * Determine if columns are declared mediumint, and if
62
     * so, queue ddl to alter to int.
63
     *
64
     * @param Tables   $migrate
65
     * @param string   $bannerTableName
66
     * @param string[] $bannerColumnNames array of columns to check
67
     *
68
     * @return integer count of queue items added
69
     */
70
    protected function fromMediumToInt(Tables $migrate, $bannerTableName, $bannerColumnNames)
71
    {
72
        $migrate->useTable($bannerTableName);
73
        $count = 0;
74
        foreach ($bannerColumnNames as $column) {
75
            $attributes = $migrate->getColumnAttributes($bannerTableName, $column);
76
            if (0 === strpos(trim($attributes), 'mediumint')) {
0 ignored issues
show
Bug introduced by
It seems like $attributes can also be of type false; however, parameter $string of trim() does only seem to accept string, 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

76
            if (0 === strpos(trim(/** @scrutinizer ignore-type */ $attributes), 'mediumint')) {
Loading history...
77
                $count++;
78
                $migrate->alterColumn($bannerTableName, $column, 'int(10) UNSIGNED NOT NULL DEFAULT \'0\'');
79
            }
80
        }
81
        return $count;
82
    }
83
84
    private $bannerTableName = 'banner';
85
    private $bannerColumnNames = array('impmade', 'clicks');
86
87
    /**
88
     * Increase count columns from mediumint to int
89
     *
90
     * @return bool true if patch IS applied, false if NOT applied
91
     */
92
    public function check_bannerintsize()
93
    {
94
        $migrate = new Tables();
95
        $count = $this->fromMediumToInt($migrate, $this->bannerTableName, $this->bannerColumnNames);
96
97
        return $count==0;
98
    }
99
100
    /**
101
     * Increase count columns from mediumint to int (Think BIG!)
102
     *
103
     * @return bool true if applied, false if failed
104
     */
105
    public function apply_bannerintsize()
106
    {
107
        $migrate = new Tables();
108
109
        $count = $this->fromMediumToInt($migrate, $this->bannerTableName, $this->bannerColumnNames);
110
111
        $result = $migrate->executeQueue(true);
112
        if (false === $result) {
113
            $this->logs[] = sprintf(
114
                'Migration of %s table failed. Error: %s - %s' .
115
                $this->bannerTableName,
116
                $migrate->getLastErrNo(),
117
                $migrate->getLastError()
118
            );
119
            return false;
120
        }
121
122
        return $count!==0;
123
    }
124
125
    /**
126
     * Add qmail as valid mailmethod
127
     *
128
     * @return bool
129
     */
130
    public function check_qmail()
131
    {
132
        /* @var XoopsMySQLDatabase $db */
133
        $db = XoopsDatabaseFactory::getDatabaseConnection();
134
135
        $table = $db->prefix('configoption');
136
137
        $sql = sprintf(
138
            'SELECT count(*) FROM `%s` '
139
            . "WHERE `conf_id` = 64 AND `confop_name` = 'qmail'",
140
            $db->escape($table)
141
        );
142
143
        /** @var mysqli_result $result */
144
        $result = $db->query($sql);
145
        if ($db->isResultSet($result)) {
146
            $row = $db->fetchRow($result);
147
            if ($row) {
148
                $count = $row[0];
149
                return (0 === (int) $count) ? false : true;
150
            }
151
        }
152
        return false;
153
    }
154
155
    /**
156
     * Add qmail as valid mailmethod
157
     *
158
     * phpMailer has qmail support, similar to but slightly different than sendmail
159
     * This will allow webmasters to utilize qmail if it is provisioned on server.
160
     *
161
     * @return bool
162
     */
163
    public function apply_qmail()
164
    {
165
        $migrate = new Tables();
166
        $migrate->useTable('configoption');
167
        $migrate->insert(
168
            'configoption',
169
            array('confop_name' => 'qmail', 'confop_value' => 'qmail', 'conf_id' => 64)
170
        );
171
        return $migrate->executeQueue(true);
172
    }
173
174
    /**
175
     * Do we need to move captcha writable data?
176
     *
177
     * @return bool true if patch IS applied, false if NOT applied
178
     */
179
    public function check_captchadata()
180
    {
181
        $captchaConfigFile = XOOPS_VAR_PATH . '/configs/captcha/config.php';
182
        $oldCaptchaConfigFile = XOOPS_ROOT_PATH . '/class/captcha/config.php';
183
        if (!file_exists($oldCaptchaConfigFile)) { // nothing to copy
184
            return true;
185
        }
186
        return file_exists($captchaConfigFile);
187
    }
188
189
    /**
190
     * Attempt to make the supplied path
191
     *
192
     * @param string $newPath
193
     *
194
     * @return bool
195
     */
196
    private function makeDirectory($newPath)
197
    {
198
        if (!mkdir($newPath) && !is_dir($newPath)) {
199
            $this->logs[] = sprintf('Captcha config directory %s was not created', $newPath);
200
            return false;
201
        }
202
        return true;
203
    }
204
205
    /**
206
     * Copy file $source to $destination
207
     *
208
     * @param string $source
209
     * @param string $destination
210
     *
211
     * @return bool true if successful, false on error
212
     */
213
    private function copyFile($source, $destination)
214
    {
215
        if (!file_exists($destination)) { // don't overwrite anything
216
            $result = copy($source, $destination);
217
            if (false === $result) {
218
                $this->logs[] = sprintf('Captcha config file copy %s failed', basename($source));
219
                return false;
220
            }
221
        }
222
        return true;
223
    }
224
225
    /**
226
     * Move captcha configs to xoops_data to segregate writable data
227
     *
228
     * @return bool
229
     */
230
    public function apply_captchadata()
231
    {
232
        $returnResult = false;
233
        $sourcePath = XOOPS_ROOT_PATH . '/class/captcha/';
234
        $destinationPath = XOOPS_VAR_PATH . '/configs/captcha/';
235
236
        if (!file_exists($destinationPath)) {
237
            $this->makeDirectory($destinationPath);
238
        }
239
        $directory = dir($sourcePath);
240
        if (false === $directory) {
241
            $this->logs[] = sprintf('Failed to read source %s', $sourcePath);
242
            return false;
243
        }
244
        while (false !== ($entry = $directory->read())) {
245
            if (false === strpos($entry, '.dist.')
246
                && strpos($entry, 'config.') === 0 && '.php' === substr($entry, -4)) {
247
                $src = $sourcePath . $entry;
248
                $dest = $destinationPath . $entry;
249
                $status = $this->copyFile($src, $dest);
250
                if (false === $status) {
251
                    $returnResult = false;
252
                }
253
            }
254
        }
255
        $directory->close();
256
257
        return $returnResult;
258
    }
259
260
    /**
261
     * Do we need to create a xoops_data/configs/xoopsconfig.php?
262
     *
263
     * @return bool true if patch IS applied, false if NOT applied
264
     */
265
    public function check_xoopsconfig()
266
    {
267
        $xoopsConfigFile = XOOPS_VAR_PATH . '/configs/xoopsconfig.php';
268
        return file_exists($xoopsConfigFile);
269
    }
270
271
    /**
272
     * Create xoops_data/configs/xoopsconfig.php from xoopsconfig.dist.php
273
     *
274
     * @return bool true if applied, false if failed
275
     */
276
    public function apply_xoopsconfig()
277
    {
278
        $source = XOOPS_VAR_PATH . '/configs/xoopsconfig.dist.php';
279
        $destination = XOOPS_VAR_PATH . '/configs/xoopsconfig.php';
280
        if (!file_exists($destination)) { // don't overwrite anything
281
            $result = copy($source, $destination);
282
            if (false === $result) {
283
                $this->logs[] = 'xoopsconfig.php file copy failed';
284
                return false;
285
            }
286
        }
287
        return true;
288
    }
289
290
    /**
291
     * This is a default list based on extensions as supplied by XOOPS.
292
     * If possible, we will build a list based on contents of class/textsanitizer/
293
     * key is file path relative to XOOPS_ROOT_PATH . '/class/textsanitizer/
294
     * value is file path relative to XOOPS_VAR_PATH . '/configs/textsanitizer/'
295
     *
296
     * @var string[]
297
     */
298
    protected $textsanitizerConfigFiles = array(
299
        'config.php' => 'config.php',
300
        'censor/config.php' => 'config.censor.php',
301
        'flash/config.php' => 'config.flash.php',
302
        'image/config.php' => 'config.image.php',
303
        'mms/config.php' => 'config.mms.php',
304
        'rtsp/config.php' => 'config.rtsp.php',
305
        'syntaxhighlight/config.php' => 'config.syntaxhighlight.php',
306
        'textfilter/config.php' => 'config.textfilter.php',
307
        'wiki/config.php' => 'config.wiki.php',
308
        'wmp/config.php' => 'config.wmp.php',
309
    );
310
311
    /**
312
     * Build a list of config files using the existing textsanitizer/config.php
313
     * each as source name => destination name in $this->textsanitizerConfigFiles
314
     *
315
     * This should prevent some issues with customized systems.
316
     *
317
     * @return void
318
     */
319
    protected function buildListTSConfigs()
320
    {
321
        if (file_exists(XOOPS_ROOT_PATH . '/class/textsanitizer/config.php')) {
322
            $config = include XOOPS_ROOT_PATH . '/class/textsanitizer/config.php';
323
            if (is_array($config) && array_key_exists('extentions', $config)) {
324
                $this->textsanitizerConfigFiles = array(
325
                    'config.php' => 'config.php',
326
                );
327
                foreach ($config['extentions'] as $module => $enabled) {
328
                    $source = "{$module}/config.php";
329
                    if (file_exists(XOOPS_ROOT_PATH . '/class/textsanitizer/' . $source)) {
330
                        $destination = "{$module}/config.{$module}.php";
331
                        $this->textsanitizerConfigFiles[$source] = $destination;
332
                    }
333
                }
334
            }
335
        }
336
        return;
337
    }
338
339
    /**
340
     * Do we need to move any existing files to xoops_data/configs/textsanitizer/ ?
341
     *
342
     * @return bool true if patch IS applied, false if NOT applied
343
     */
344
    public function check_textsanitizer()
345
    {
346
        $this->buildListTSConfigs();
347
        foreach ($this->textsanitizerConfigFiles as $source => $destination) {
348
            $src  = XOOPS_ROOT_PATH . '/class/textsanitizer/' . $source;
349
            $dest = XOOPS_VAR_PATH . '/configs/textsanitizer/' . $destination;
350
            if (!file_exists($dest) && file_exists($src)) {
351
                return false;
352
            }
353
        }
354
        return true;
355
    }
356
357
    /**
358
     * Copy and rename any existing class/textsanitizer/ config files to xoops_data/configs/textsanitizer/
359
     *
360
     * @return bool true if applied, false if failed
361
     */
362
    public function apply_textsanitizer()
363
    {
364
        $this->buildListTSConfigs();
365
        $return = true;
366
        foreach ($this->textsanitizerConfigFiles as $source => $destination) {
367
            $src  = XOOPS_ROOT_PATH . '/class/textsanitizer/' . $source;
368
            $dest = XOOPS_VAR_PATH . '/configs/textsanitizer/' . $destination;
369
            if (!file_exists($dest) && file_exists($src)) {
370
                $result = copy($src, $dest);
371
                if (false === $result) {
372
                    $this->logs[] = sprintf('textsanitizer file copy to %s failed', $destination);
373
                    $return = false;
374
                }
375
            }
376
        }
377
        return $return;
378
    }
379
380
    /**
381
     * Attempt to remove index.html files replaced by index.php
382
     */
383
    /**
384
     * List of directories supplied by XOOPS. This is used to try and keep us out
385
     * of things added to the system locally. (Set in __construct() for php BC.)
386
     *
387
     * @var string[]
388
     */
389
    private $pathsToCheck;
390
391
    /**
392
     * Do we need to remove any index.html files that were replaced by index.php files?
393
     *
394
     * @return bool true if patch IS applied, false if NOT applied
395
     */
396
    public function check_rmindexhtml()
397
    {
398
        /**
399
         * If we find an index.html that is writable, we know there is work to do
400
         *
401
         * @param string $name file name to check
402
         *
403
         * @return bool  true to continue, false to stop scan
404
         */
405
        $stopIfFound = function ($name) {
406
            $ok = is_writable($name);
407
            return !($ok);
408
        };
409
410
        clearstatcache();
411
412
        return $this->dirWalker($stopIfFound);
413
    }
414
415
    /**
416
     * Unlink any index.html files that have been replaced by index.php files
417
     *
418
     * @return bool true if patch applied, false if failed
419
     */
420
    public function apply_rmindexhtml()
421
    {
422
        /**
423
         * Do unlink() on file
424
         * Always return true so we process each writable index.html
425
         *
426
         * @param string $name file name to unlink
427
         *
428
         * @return true always report true, even if we can't delete -- best effort only
429
         */
430
        $unlinkByName = function ($name) {
431
            if (is_writable($name)) {
432
                $result = unlink($name);
0 ignored issues
show
Unused Code introduced by
The assignment to $result is dead and can be removed.
Loading history...
433
            }
434
            return true;
435
        };
436
437
438
        return $this->dirWalker($unlinkByName);
439
    }
440
441
    /**
442
     * Walk list of directories in $pathsToCheck
443
     *
444
     * @param \Closure $onFound
445
     *
446
     * @return bool
447
     */
448
    private function dirWalker(\Closure $onFound)
449
    {
450
        $check = true;
451
        foreach ($this->pathsToCheck as $path) {
452
            $check = $this->checkDirForIndexHtml($path, $onFound);
453
            if (false === $check) {
454
                break;
455
            }
456
        }
457
        if (false !== $check) {
458
            $check = true;
459
        }
460
        return $check;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $check also could return the type integer which is incompatible with the documented return type boolean.
Loading history...
461
    }
462
463
    /**
464
     * Recursively check for index.html files that have a corresponding index.php file
465
     * in the supplied path.
466
     *
467
     * @param string   $startingPath
468
     * @param \Closure $onFound
469
     *
470
     * @return false|int false if onFound returned false (don't continue) else count of matches
471
     */
472
    private function checkDirForIndexHtml($startingPath, \Closure $onFound)
473
    {
474
        if (!is_dir($startingPath)) {
475
            return 0;
476
        }
477
        $i = 0;
478
        $rdi = new \RecursiveDirectoryIterator($startingPath);
479
        $rii = new \RecursiveIteratorIterator($rdi);
480
        /** @var \SplFileInfo $fileinfo */
481
        foreach ($rii as $fileinfo) {
482
            if ($fileinfo->isFile() && 'index.html' === $fileinfo->getFilename() && 60 > $fileinfo->getSize()) {
483
                $path = $fileinfo->getPath();
484
                $testFilename = $path . '/index.php';
485
                if (file_exists($testFilename)) {
486
                    $unlinkName = $path . '/' . $fileinfo->getFilename();
487
                    ++$i;
488
                    $continue = $onFound($unlinkName);
489
                    if (false === $continue) {
490
                        return $continue;
491
                    }
492
                }
493
            }
494
        }
495
        return $i;
496
    }
497
498
    /**
499
     * Determine if columns are declared smallint, and if
500
     * so, queue ddl to alter to varchar.
501
     *
502
     * @param Tables   $migrate
503
     * @param string   $modulesTableName
504
     * @param string[] $modulesColumnNames  array of columns to check
505
     *
506
     * @return integer count of queue items added
507
     */
508
    protected function fromSmallintToVarchar(Tables $migrate, $modulesTableName, $modulesColumnNames)
509
    {
510
        $migrate->useTable($modulesTableName);
511
        $count = 0;
512
        foreach ($modulesColumnNames as $column) {
513
            $attributes = $migrate->getColumnAttributes($modulesTableName, $column);
514
            if (is_string($attributes) && 0 === strpos(trim($attributes), 'smallint')) {
515
                $count++;
516
                $migrate->alterColumn($modulesTableName, $column, 'varchar(32) NOT NULL DEFAULT \'\'');
517
            }
518
        }
519
        return $count;
520
    }
521
522
    private $modulesTableName = 'modules';
523
    private $modulesColumnNames = array('version');
524
525
    /**
526
     * Increase version columns from smallint to varchar
527
     *
528
     * @return bool true if patch IS applied, false if NOT applied
529
     */
530
    public function check_modulesvarchar()
531
    {
532
        $migrate = new Tables();
533
        $count = $this->fromSmallintToVarchar($migrate, $this->modulesTableName, $this->modulesColumnNames);
534
        return $count == 0;
535
    }
536
537
    /**
538
     * Increase version columns from smallint to varchar
539
     *
540
     * @return bool true if applied, false if failed
541
     */
542
    public function apply_modulesvarchar()
543
    {
544
        $migrate = new Tables();
545
546
        $count = $this->fromSmallintToVarchar($migrate, $this->modulesTableName, $this->modulesColumnNames);
0 ignored issues
show
Unused Code introduced by
The assignment to $count is dead and can be removed.
Loading history...
547
548
        $result = $migrate->executeQueue(true);
549
        if (false === $result) {
550
            $this->logs[] = sprintf(
551
                'Migration of %s table failed. Error: %s - %s' .
552
                $this->modulesTableName,
553
                $migrate->getLastErrNo(),
554
                $migrate->getLastError()
555
            );
556
            return false;
557
        }
558
559
        return true;
560
    }
561
562
    /**
563
     * @return bool
564
     */
565
    public function check_templates()
566
    {
567
        $sql = 'SELECT COUNT(*) FROM `' . $GLOBALS['xoopsDB']->prefix('tplfile') . "` WHERE `tpl_file` IN ('system_confirm.tpl') AND `tpl_type` = 'module'";
568
        $result = $GLOBALS['xoopsDB']->queryF($sql);
569
        if (!$GLOBALS['xoopsDB']->isResultSet($result)) {
570
            return false;
571
        }
572
        list($count) = $GLOBALS['xoopsDB']->fetchRow($result);
573
574
        return ($count != 0);
575
    }
576
577
578
    /**
579
     * @return bool
580
     */
581
    public function apply_templates()
582
    {
583
        $modversion = array();
584
        include_once XOOPS_ROOT_PATH . '/modules/system/xoops_version.php';
585
586
        $dbm = new Db_manager();
587
        $time = time();
588
        foreach ($modversion['templates'] as $tplfile) {
589
            if ((isset($tplfile['type']) && $tplfile['type'] === 'module') || !isset($tplfile['type'])) {
590
591
                $filePath = XOOPS_ROOT_PATH . '/modules/system/templates/' . $tplfile['file'];
592
                if ($fp = fopen($filePath, 'r')) {
593
                    $newtplid = $dbm->insert('tplfile', " VALUES (0, 1, 'system', 'default', '" . addslashes($tplfile['file']) . "', '" . addslashes($tplfile['description']) . "', " . $time . ', ' . $time . ", 'module')");
594
                    $tplsource = fread($fp, filesize($filePath));
595
                    fclose($fp);
596
                    $dbm->insert('tplsource', ' (tpl_id, tpl_source) VALUES (' . $newtplid . ", '" . addslashes($tplsource) . "')");
597
                }
598
            }
599
        }
600
601
        return true;
602
    }
603
604
605
    //modules/system/themes/legacy/legacy.php
606
    /**
607
     * Do we need to delete obsolete Smarty files?
608
     *
609
     * @return bool
610
     */
611
    public function check_zapsmarty()
612
    {
613
        return !file_exists('../class/smarty/smarty.class.php');
614
    }
615
616
    /**
617
     * Delete obsolete Smarty files
618
     *
619
     * @return bool
620
     */
621
    public function apply_zapsmarty()
622
    {
623
        // Define the base directory
624
        $baseDir = '../class/smarty/';
625
626
        // List of sub-folders and files to delete
627
        $itemsToDelete = array(
628
            'configs',
629
            'internals',
630
            'xoops_plugins',
631
            'Config_File.class.php',
632
            'debug.tpl',
633
            'Smarty.class.php',
634
            'Smarty_Compiler.class.php'
635
        );
636
637
        // Loop through each item and delete it
638
        foreach ($itemsToDelete as $item) {
639
            $path = $baseDir . $item;
640
641
            // Check if it's a directory or a file
642
            if (is_dir($path)) {
643
                // Delete directory and its contents
644
                array_map('unlink', glob("$path/*.*"));
645
                rmdir($path);
646
            } elseif (is_file($path)) {
647
                // Delete file
648
                if (is_writable($path)) {
649
                    unlink($path);
650
                }
651
            }
652
        }
653
654
        return true;
655
    }
656
657
658
}
659
660
return new Upgrade_2511();
661