Passed
Push — master ( c39b1b...a640df )
by Richard
04:29 queued 11s
created

Upgrade_2511::apply_rmindexhtml()   A

Complexity

Conditions 2
Paths 1

Size

Total Lines 19
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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

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