Passed
Pull Request — master (#1375)
by Richard
08:11
created

Upgrade_2511::check_cleancache()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 7
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 4
c 0
b 0
f 0
nc 2
nop 0
dl 0
loc 7
rs 10
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-2023 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
class Upgrade_2511 extends XoopsUpgrade
15
{
16
    /**
17
     * __construct
18
     */
19
    public function __construct()
20
    {
21
        parent::__construct(basename(__DIR__));
22
        $this->tasks = array(
23
            'cleancache',
24
            'bannerintsize',
25
            'captchadata',
26
            'configkey',
27
            'modulesvarchar',
28
            'qmail',
29
            'rmindexhtml',
30
            'textsanitizer',
31
            'xoopsconfig',
32
        );
33
        $this->usedFiles = array();
34
        $this->pathsToCheck = array(
35
            XOOPS_ROOT_PATH . '/cache',
36
            XOOPS_ROOT_PATH . '/class',
37
            XOOPS_ROOT_PATH . '/Frameworks',
38
            XOOPS_ROOT_PATH . '/images',
39
            XOOPS_ROOT_PATH . '/include',
40
            XOOPS_ROOT_PATH . '/kernel',
41
            XOOPS_ROOT_PATH . '/language',
42
            XOOPS_ROOT_PATH . '/media',
43
            XOOPS_ROOT_PATH . '/modules/pm',
44
            XOOPS_ROOT_PATH . '/modules/profile',
45
            XOOPS_ROOT_PATH . '/modules/protector',
46
            XOOPS_ROOT_PATH . '/modules/system',
47
            XOOPS_ROOT_PATH . '/templates_c',
48
            XOOPS_ROOT_PATH . '/themes/default',
49
            XOOPS_ROOT_PATH . '/themes/xbootstrap',
50
            XOOPS_ROOT_PATH . '/themes/xswatch',
51
            XOOPS_ROOT_PATH . '/themes/xswatch4',
52
            XOOPS_ROOT_PATH . '/uploads',
53
            XOOPS_VAR_PATH,
54
            XOOPS_PATH,
55
        );
56
    }
57
58
    protected $cleanCacheKey = 'cache-cleaned';
59
60
    /**
61
     * We must remove stale template caches and compiles
62
     *
63
     * @return bool true if patch IS applied, false if NOT applied
64
     */
65
    public function check_cleancache()
66
    {
67
        if (!array_key_exists($this->cleanCacheKey, $_SESSION)
68
            || $_SESSION[$this->cleanCacheKey]===false) {
69
            return false;
70
        }
71
        return true;
72
    }
73
74
    /**
75
     * Remove  all caches and compiles
76
     *
77
     * @return bool true if applied, false if failed
78
     */
79
    public function apply_cleancache()
80
    {
81
        require_once XOOPS_ROOT_PATH . '/modules/system/class/maintenance.php';
82
        $maintenance = new SystemMaintenance();
83
        $result  = $maintenance->CleanCache(array(1,2,3));
84
        if ($result===true) {
85
            $_SESSION[$this->cleanCacheKey] = true;
86
        }
87
        return $result;
88
    }
89
90
    /**
91
     * Determine if columns are declared mediumint, and if
92
     * so, queue ddl to alter to int.
93
     *
94
     * @param Tables   $migrate
95
     * @param string   $bannerTableName
96
     * @param string[] $bannerColumnNames array of columns to check
97
     *
98
     * @return integer count of queue items added
99
     */
100
    protected function fromMediumToInt(Tables $migrate, $bannerTableName, $bannerColumnNames)
101
    {
102
        $migrate->useTable($bannerTableName);
103
        $count = 0;
104
        foreach ($bannerColumnNames as $column) {
105
            $attributes = $migrate->getColumnAttributes($bannerTableName, $column);
106
            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

106
            if (0 === strpos(trim(/** @scrutinizer ignore-type */ $attributes), 'mediumint')) {
Loading history...
107
                $count++;
108
                $migrate->alterColumn($bannerTableName, $column, 'int(10) UNSIGNED NOT NULL DEFAULT \'0\'');
109
            }
110
        }
111
        return $count;
112
    }
113
114
    private $bannerTableName = 'banner';
115
    private $bannerColumnNames = array('impmade', 'clicks');
116
117
    /**
118
     * Increase count columns from mediumint to int
119
     *
120
     * @return bool true if patch IS applied, false if NOT applied
121
     */
122
    public function check_bannerintsize()
123
    {
124
        $migrate = new Tables();
125
        $count = $this->fromMediumToInt($migrate, $this->bannerTableName, $this->bannerColumnNames);
126
127
        return $count==0;
128
    }
129
130
    /**
131
     * Increase count columns from mediumint to int (Think BIG!)
132
     *
133
     * @return bool true if applied, false if failed
134
     */
135
    public function apply_bannerintsize()
136
    {
137
        $migrate = new Tables();
138
139
        $count = $this->fromMediumToInt($migrate, $this->bannerTableName, $this->bannerColumnNames);
140
141
        $result = $migrate->executeQueue(true);
142
        if (false === $result) {
143
            $this->logs[] = sprintf(
144
                'Migration of %s table failed. Error: %s - %s' .
145
                $this->bannerTableName,
146
                $migrate->getLastErrNo(),
147
                $migrate->getLastError()
148
            );
149
            return false;
150
        }
151
152
        return $count!==0;
153
    }
154
155
    /**
156
     * Add qmail as valid mailmethod
157
     *
158
     * @return bool
159
     */
160
    public function check_qmail()
161
    {
162
        /* @var XoopsMySQLDatabase $db */
163
        $db = XoopsDatabaseFactory::getDatabaseConnection();
164
165
        $table = $db->prefix('configoption');
166
167
        $sql = sprintf(
168
            'SELECT count(*) FROM `%s` '
169
            . "WHERE `conf_id` = 64 AND `confop_name` = 'qmail'",
170
            $db->escape($table)
171
        );
172
173
        /** @var mysqli_result $result */
174
        $result = $db->query($sql);
175
        if ($db->isResultSet($result)) {
176
            $row = $db->fetchRow($result);
177
            if ($row) {
178
                $count = $row[0];
179
                return (0 === (int) $count) ? false : true;
180
            }
181
        }
182
        return false;
183
    }
184
185
    /**
186
     * Add qmail as valid mailmethod
187
     *
188
     * phpMailer has qmail support, similar to but slightly different than sendmail
189
     * This will allow webmasters to utilize qmail if it is provisioned on server.
190
     *
191
     * @return bool
192
     */
193
    public function apply_qmail()
194
    {
195
        $migrate = new Tables();
196
        $migrate->useTable('configoption');
197
        $migrate->insert(
198
            'configoption',
199
            array('confop_name' => 'qmail', 'confop_value' => 'qmail', 'conf_id' => 64)
200
        );
201
        return $migrate->executeQueue(true);
202
    }
203
204
    /**
205
     * Do we need to move captcha writable data?
206
     *
207
     * @return bool true if patch IS applied, false if NOT applied
208
     */
209
    public function check_captchadata()
210
    {
211
        $captchaConfigFile = XOOPS_VAR_PATH . '/configs/captcha/config.php';
212
        $oldCaptchaConfigFile = XOOPS_ROOT_PATH . '/class/captcha/config.php';
213
        if (!file_exists($oldCaptchaConfigFile)) { // nothing to copy
214
            return true;
215
        }
216
        return file_exists($captchaConfigFile);
217
    }
218
219
    /**
220
     * Attempt to make the supplied path
221
     *
222
     * @param string $newPath
223
     *
224
     * @return bool
225
     */
226
    private function makeDirectory($newPath)
227
    {
228
        if (!mkdir($newPath) && !is_dir($newPath)) {
229
            $this->logs[] = sprintf('Captcha config directory %s was not created', $newPath);
230
            return false;
231
        }
232
        return true;
233
    }
234
235
    /**
236
     * Copy file $source to $destination
237
     *
238
     * @param string $source
239
     * @param string $destination
240
     *
241
     * @return bool true if successful, false on error
242
     */
243
    private function copyFile($source, $destination)
244
    {
245
        if (!file_exists($destination)) { // don't overwrite anything
246
            $result = copy($source, $destination);
247
            if (false === $result) {
248
                $this->logs[] = sprintf('Captcha config file copy %s failed', basename($source));
249
                return false;
250
            }
251
        }
252
        return true;
253
    }
254
255
    /**
256
     * Move captcha configs to xoops_data to segregate writable data
257
     *
258
     * @return bool
259
     */
260
    public function apply_captchadata()
261
    {
262
        $returnResult = false;
263
        $sourcePath = XOOPS_ROOT_PATH . '/class/captcha/';
264
        $destinationPath = XOOPS_VAR_PATH . '/configs/captcha/';
265
266
        if (!file_exists($destinationPath)) {
267
            $this->makeDirectory($destinationPath);
268
        }
269
        $directory = dir($sourcePath);
270
        if (false === $directory) {
271
            $this->logs[] = sprintf('Failed to read source %s', $sourcePath);
272
            return false;
273
        }
274
        while (false !== ($entry = $directory->read())) {
275
            if (false === strpos($entry, '.dist.')
276
                && strpos($entry, 'config.') === 0 && '.php' === substr($entry, -4)) {
277
                $src = $sourcePath . $entry;
278
                $dest = $destinationPath . $entry;
279
                $status = $this->copyFile($src, $dest);
280
                if (false === $status) {
281
                    $returnResult = false;
282
                }
283
            }
284
        }
285
        $directory->close();
286
287
        return $returnResult;
288
    }
289
290
    //config
291
    /**
292
     * Increase primary key columns from smallint to int
293
     *
294
     * @return bool true if patch IS applied, false if NOT applied
295
     */
296
    public function check_configkey()
297
    {
298
        $tableName = 'config';
299
        $columnName = 'conf_id';
300
301
        $migrate = new Tables();
302
        $migrate->useTable($tableName);
303
        $count = 0;
304
        $attributes = $migrate->getColumnAttributes($tableName, $columnName);
305
        if (0 === strpos(trim($attributes), 'smallint')) {
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

305
        if (0 === strpos(trim(/** @scrutinizer ignore-type */ $attributes), 'smallint')) {
Loading history...
306
            $count++;
307
            $migrate->alterColumn($tableName, $columnName, 'int(10) UNSIGNED NOT NULL');
308
        }
309
310
        return $count==0;
311
    }
312
313
    /**
314
     * Increase primary key columns from smallint to int
315
     *
316
     * @return bool true if applied, false if failed
317
     */
318
    public function apply_configkey()
319
    {
320
        $tableName = 'config';
321
        $columnName = 'conf_id';
322
323
        $migrate = new Tables();
324
        $migrate->useTable($tableName);
325
        $count = 0;
326
        $attributes = $migrate->getColumnAttributes($tableName, $columnName);
327
        if (0 === strpos(trim($attributes), 'smallint')) {
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

327
        if (0 === strpos(trim(/** @scrutinizer ignore-type */ $attributes), 'smallint')) {
Loading history...
328
            $count++;
329
            $migrate->alterColumn($tableName, $columnName, 'int(10) UNSIGNED NOT NULL');
330
        }
331
332
        $result = $migrate->executeQueue(true);
333
        if (false === $result) {
334
            $this->logs[] = sprintf(
335
                'Migration of %s table failed. Error: %s - %s' .
336
                $tableName,
337
                $migrate->getLastErrNo(),
338
                $migrate->getLastError()
339
            );
340
            return false;
341
        }
342
343
        return $count!==0;
344
    }
345
    //configend
346
347
    /**
348
     * Do we need to create a xoops_data/configs/xoopsconfig.php?
349
     *
350
     * @return bool true if patch IS applied, false if NOT applied
351
     */
352
    public function check_xoopsconfig()
353
    {
354
        $xoopsConfigFile = XOOPS_VAR_PATH . '/configs/xoopsconfig.php';
355
        return file_exists($xoopsConfigFile);
356
    }
357
358
    /**
359
     * Create xoops_data/configs/xoopsconfig.php from xoopsconfig.dist.php
360
     *
361
     * @return bool true if applied, false if failed
362
     */
363
    public function apply_xoopsconfig()
364
    {
365
        $source = XOOPS_VAR_PATH . '/configs/xoopsconfig.dist.php';
366
        $destination = XOOPS_VAR_PATH . '/configs/xoopsconfig.php';
367
        if (!file_exists($destination)) { // don't overwrite anything
368
            $result = copy($source, $destination);
369
            if (false === $result) {
370
                $this->logs[] = 'xoopsconfig.php file copy failed';
371
                return false;
372
            }
373
        }
374
        return true;
375
    }
376
377
    /**
378
     * This is a default list based on extensions as supplied by XOOPS.
379
     * If possible, we will build a list based on contents of class/textsanitizer/
380
     * key is file path relative to XOOPS_ROOT_PATH . '/class/textsanitizer/
381
     * value is file path relative to XOOPS_VAR_PATH . '/configs/textsanitizer/'
382
     *
383
     * @var string[]
384
     */
385
    protected $textsanitizerConfigFiles = array(
386
        'config.php' => 'config.php',
387
        'censor/config.php' => 'config.censor.php',
388
        'flash/config.php' => 'config.flash.php',
389
        'image/config.php' => 'config.image.php',
390
        'mms/config.php' => 'config.mms.php',
391
        'rtsp/config.php' => 'config.rtsp.php',
392
        'syntaxhighlight/config.php' => 'config.syntaxhighlight.php',
393
        'textfilter/config.php' => 'config.textfilter.php',
394
        'wiki/config.php' => 'config.wiki.php',
395
        'wmp/config.php' => 'config.wmp.php',
396
    );
397
398
    /**
399
     * Build a list of config files using the existing textsanitizer/config.php
400
     * each as source name => destination name in $this->textsanitizerConfigFiles
401
     *
402
     * This should prevent some issues with customized systems.
403
     *
404
     * @return void
405
     */
406
    protected function buildListTSConfigs()
407
    {
408
        if (file_exists(XOOPS_ROOT_PATH . '/class/textsanitizer/config.php')) {
409
            $config = include XOOPS_ROOT_PATH . '/class/textsanitizer/config.php';
410
            if (is_array($config) && array_key_exists('extentions', $config)) {
411
                $this->textsanitizerConfigFiles = array(
412
                    'config.php' => 'config.php',
413
                );
414
                foreach ($config['extentions'] as $module => $enabled) {
415
                    $source = "{$module}/config.php";
416
                    if (file_exists(XOOPS_ROOT_PATH . '/class/textsanitizer/' . $source)) {
417
                        $destination = "{$module}/config.{$module}.php";
418
                        $this->textsanitizerConfigFiles[$source] = $destination;
419
                    }
420
                }
421
            }
422
        }
423
        return;
424
    }
425
426
    /**
427
     * Do we need to move any existing files to xoops_data/configs/textsanitizer/ ?
428
     *
429
     * @return bool true if patch IS applied, false if NOT applied
430
     */
431
    public function check_textsanitizer()
432
    {
433
        $this->buildListTSConfigs();
434
        foreach ($this->textsanitizerConfigFiles as $source => $destination) {
435
            $src  = XOOPS_ROOT_PATH . '/class/textsanitizer/' . $source;
436
            $dest = XOOPS_VAR_PATH . '/configs/textsanitizer/' . $destination;
437
            if (!file_exists($dest) && file_exists($src)) {
438
                return false;
439
            }
440
        }
441
        return true;
442
    }
443
444
    /**
445
     * Copy and rename any existing class/textsanitizer/ config files to xoops_data/configs/textsanitizer/
446
     *
447
     * @return bool true if applied, false if failed
448
     */
449
    public function apply_textsanitizer()
450
    {
451
        $this->buildListTSConfigs();
452
        $return = true;
453
        foreach ($this->textsanitizerConfigFiles as $source => $destination) {
454
            $src  = XOOPS_ROOT_PATH . '/class/textsanitizer/' . $source;
455
            $dest = XOOPS_VAR_PATH . '/configs/textsanitizer/' . $destination;
456
            if (!file_exists($dest) && file_exists($src)) {
457
                $result = copy($src, $dest);
458
                if (false === $result) {
459
                    $this->logs[] = sprintf('textsanitizer file copy to %s failed', $destination);
460
                    $return = false;
461
                }
462
            }
463
        }
464
        return $return;
465
    }
466
467
    /**
468
     * Attempt to remove index.html files replaced by index.php
469
     */
470
    /**
471
     * List of directories supplied by XOOPS. This is used to try and keep us out
472
     * of things added to the system locally. (Set in __construct() for php BC.)
473
     *
474
     * @var string[]
475
     */
476
    private $pathsToCheck;
477
478
    /**
479
     * Do we need to remove any index.html files that were replaced by index.php files?
480
     *
481
     * @return bool true if patch IS applied, false if NOT applied
482
     */
483
    public function check_rmindexhtml()
484
    {
485
        /**
486
         * If we find an index.html that is writable, we know there is work to do
487
         *
488
         * @param string $name file name to check
489
         *
490
         * @return bool  true to continue, false to stop scan
491
         */
492
        $stopIfFound = function ($name) {
493
            $ok = is_writable($name);
494
            return !($ok);
495
        };
496
497
        clearstatcache();
498
499
        return $this->dirWalker($stopIfFound);
500
    }
501
502
    /**
503
     * Unlink any index.html files that have been replaced by index.php files
504
     *
505
     * @return bool true if patch applied, false if failed
506
     */
507
    public function apply_rmindexhtml()
508
    {
509
        /**
510
         * Do unlink() on file
511
         * Always return true so we process each writable index.html
512
         *
513
         * @param string $name file name to unlink
514
         *
515
         * @return true always report true, even if we can't delete -- best effort only
516
         */
517
        $unlinkByName = function ($name) {
518
            if (is_writable($name)) {
519
                $result = unlink($name);
0 ignored issues
show
Unused Code introduced by
The assignment to $result is dead and can be removed.
Loading history...
520
            }
521
            return true;
522
        };
523
524
525
        return $this->dirWalker($unlinkByName);
526
    }
527
528
    /**
529
     * Walk list of directories in $pathsToCheck
530
     *
531
     * @param \Closure $onFound
532
     *
533
     * @return bool
534
     */
535
    private function dirWalker(\Closure $onFound)
536
    {
537
        $check = true;
538
        foreach ($this->pathsToCheck as $path) {
539
            $check = $this->checkDirForIndexHtml($path, $onFound);
540
            if (false === $check) {
541
                break;
542
            }
543
        }
544
        if (false !== $check) {
545
            $check = true;
546
        }
547
        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...
548
    }
549
550
    /**
551
     * Recursively check for index.html files that have a corresponding index.php file
552
     * in the supplied path.
553
     *
554
     * @param string   $startingPath
555
     * @param \Closure $onFound
556
     *
557
     * @return false|int false if onFound returned false (don't continue) else count of matches
558
     */
559
    private function checkDirForIndexHtml($startingPath, \Closure $onFound)
560
    {
561
        if (!is_dir($startingPath)) {
562
            return 0;
563
        }
564
        $i = 0;
565
        $rdi = new \RecursiveDirectoryIterator($startingPath);
566
        $rii = new \RecursiveIteratorIterator($rdi);
567
        /** @var \SplFileInfo $fileinfo */
568
        foreach ($rii as $fileinfo) {
569
            if ($fileinfo->isFile() && 'index.html' === $fileinfo->getFilename() && 60 > $fileinfo->getSize()) {
570
                $path = $fileinfo->getPath();
571
                $testFilename = $path . '/index.php';
572
                if (file_exists($testFilename)) {
573
                    $unlinkName = $path . '/' . $fileinfo->getFilename();
574
                    ++$i;
575
                    $continue = $onFound($unlinkName);
576
                    if (false === $continue) {
577
                        return $continue;
578
                    }
579
                }
580
            }
581
        }
582
        return $i;
583
    }
584
585
    /**
586
     * Determine if columns are declared smallint, and if
587
     * so, queue ddl to alter to varchar.
588
     *
589
     * @param Tables   $migrate
590
     * @param string   $modulesTableName
591
     * @param string[] $modulesColumnNames  array of columns to check
592
     *
593
     * @return integer count of queue items added
594
     */
595
    protected function fromSmallintToVarchar(Tables $migrate, $modulesTableName, $modulesColumnNames)
596
    {
597
        $migrate->useTable($modulesTableName);
598
        $count = 0;
599
        foreach ($modulesColumnNames as $column) {
600
            $attributes = $migrate->getColumnAttributes($modulesTableName, $column);
601
            if (is_string($attributes) && 0 === strpos(trim($attributes), 'smallint')) {
602
                $count++;
603
                $migrate->alterColumn($modulesTableName, $column, 'varchar(32) NOT NULL DEFAULT \'\'');
604
            }
605
        }
606
        return $count;
607
    }
608
609
    private $modulesTableName = 'modules';
610
    private $modulesColumnNames = array('version');
611
612
    /**
613
     * Increase version columns from smallint to varchar
614
     *
615
     * @return bool true if patch IS applied, false if NOT applied
616
     */
617
    public function check_modulesvarchar()
618
    {
619
        $migrate = new Tables();
620
        $count = $this->fromSmallintToVarchar($migrate, $this->modulesTableName, $this->modulesColumnNames);
621
        return $count == 0;
622
    }
623
624
    /**
625
     * Increase version columns from smallint to varchar
626
     *
627
     * @return bool true if applied, false if failed
628
     */
629
    public function apply_modulesvarchar()
630
    {
631
        $migrate = new Tables();
632
633
        $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...
634
635
        $result = $migrate->executeQueue(true);
636
        if (false === $result) {
637
            $this->logs[] = sprintf(
638
                'Migration of %s table failed. Error: %s - %s' .
639
                $this->modulesTableName,
640
                $migrate->getLastErrNo(),
641
                $migrate->getLastError()
642
            );
643
            return false;
644
        }
645
646
        return true;
647
    }
648
}
649
650
return new Upgrade_2511();
651