Passed
Push — master ( 9ee7b0...25f9e6 )
by Michael
06:20 queued 22s
created

Upgrade_2511::fromSmallintToVarchar()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

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

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

504
            if (0 === strpos(trim(/** @scrutinizer ignore-type */ $attributes), 'smallint')) {
Loading history...
505
                $count++;
506
                $migrate->alterColumn($modulesTableName, $column, 'varchar(12) NOT NULL DEFAULT \'\'');
507
            }
508
        }
509
        return $count;
510
    }
511
512
    private $modulesTableName = 'modules';
513
    private $modulesColumnNames = array('version');
514
515
    /**
516
     * Increase count columns from smallint to varchar
517
     *
518
     * @return bool true if patch IS applied, false if NOT applied
519
     */
520
    public function check_modulesvarchar()
521
    {
522
        $migrate = new Tables();
523
        $count = $this->fromSmallintToVarchar($migrate, $this->modulesTableName, $this->modulesColumnNames);
524
		echo 'count: ' .$count;
525
        return $count==0;
526
    }
527
528
    /**
529
     * Increase count columns from smallint to varchar
530
     *
531
     * @return bool true if applied, false if failed
532
     */
533
    public function apply_modulesvarchar()
534
    {
535
        $migrate = new \Xmf\Database\Tables();
536
537
        $count = $this->fromSmallintToVarchar($migrate, $this->modulesTableName, $this->modulesColumnNames);
538
539
        $result = $migrate->executeQueue(true);
540
        if (false === $result) {
541
            $this->logs[] = sprintf('Migration of %s table failed. Error: %s - %s' .
542
                $this->modulesTableName,
543
                $migrate->getLastErrNo(),
544
                $migrate->getLastError()
545
            );
546
            return false;
547
        }
548
549
        return $count!==0;
550
    }
551
552
}
553
554
return new Upgrade_2511();
555