Passed
Pull Request — master (#1373)
by Michael
05:20
created

Upgrade_2511::check_templates()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 1 Features 0
Metric Value
cc 2
eloc 6
c 1
b 1
f 0
nc 2
nop 0
dl 0
loc 10
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-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
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
            'bannerintsize',
24
            'captchadata',
25
            'modulesvarchar',
26
            'qmail',
27
            'rmindexhtml',
28
            'textsanitizer',
29
            'xoopsconfig',
30
            'templates',
31
        );
32
        $this->usedFiles = array();
33
        $this->pathsToCheck = array(
34
            XOOPS_ROOT_PATH . '/cache',
35
            XOOPS_ROOT_PATH . '/class',
36
            XOOPS_ROOT_PATH . '/Frameworks',
37
            XOOPS_ROOT_PATH . '/images',
38
            XOOPS_ROOT_PATH . '/include',
39
            XOOPS_ROOT_PATH . '/kernel',
40
            XOOPS_ROOT_PATH . '/language',
41
            XOOPS_ROOT_PATH . '/media',
42
            XOOPS_ROOT_PATH . '/modules/pm',
43
            XOOPS_ROOT_PATH . '/modules/profile',
44
            XOOPS_ROOT_PATH . '/modules/protector',
45
            XOOPS_ROOT_PATH . '/modules/system',
46
            XOOPS_ROOT_PATH . '/templates_c',
47
            XOOPS_ROOT_PATH . '/themes/default',
48
            XOOPS_ROOT_PATH . '/themes/xbootstrap',
49
            XOOPS_ROOT_PATH . '/themes/xswatch',
50
            XOOPS_ROOT_PATH . '/themes/xswatch4',
51
            XOOPS_ROOT_PATH . '/uploads',
52
            XOOPS_VAR_PATH,
53
            XOOPS_PATH,
54
        );
55
    }
56
57
58
    /**
59
     * Determine if columns are declared mediumint, and if
60
     * so, queue ddl to alter to int.
61
     *
62
     * @param Tables   $migrate
63
     * @param string   $bannerTableName
64
     * @param string[] $bannerColumnNames array of columns to check
65
     *
66
     * @return integer count of queue items added
67
     */
68
    protected function fromMediumToInt(Tables $migrate, $bannerTableName, $bannerColumnNames)
69
    {
70
        $migrate->useTable($bannerTableName);
71
        $count = 0;
72
        foreach ($bannerColumnNames as $column) {
73
            $attributes = $migrate->getColumnAttributes($bannerTableName, $column);
74
            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

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