Passed
Push — master ( 5079e2...30c968 )
by Michael
19:59 queued 08:12
created

PathController::checkPermissions()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 29
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 18
c 0
b 0
f 0
nc 4
nop 1
dl 0
loc 29
rs 9.6666
1
<?php
2
/*
3
 You may not change or alter any portion of this comment or credits
4
 of supporting developers from this source code or any supporting source code
5
 which is considered copyrighted (c) material of the original comment or credit authors.
6
7
 This program is distributed in the hope that it will be useful,
8
 but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
10
*/
11
12
/**
13
 * Upgrader from 2.0.18 to 2.3.0
14
 *
15
 * See the enclosed file license.txt for licensing information.
16
 * If you did not receive this file, get it at https://www.gnu.org/licenses/gpl-2.0.html
17
 *
18
 * @copyright    (c) 2000-2016 XOOPS Project (www.xoops.org)
19
 * @license          GNU GPL 2.0 or later (https://www.gnu.org/licenses/gpl-2.0.html)
20
 * @package          upgrader
21
 * @since            2.3.0
22
 * @author           Taiwen Jiang <[email protected]>
23
 */
24
class PathController
25
{
26
    public $xoopsPath   = [
27
        'lib'  => '',
28
        'data' => '',
29
    ];
30
    public $path_lookup = [
31
        'data' => 'VAR_PATH',
32
        'lib'  => 'PATH',
33
    ];
34
35
    public $validPath = [
36
        'data' => 0,
37
        'lib'  => 0,
38
    ];
39
40
    public $permErrors = [
41
        'data' => null,
42
    ];
43
44
    public function __construct()
45
    {
46
        if (isset($_SESSION['settings']['VAR_PATH'])) {
47
            foreach ($this->path_lookup as $req => $sess) {
48
                $this->xoopsPath[$req] = $_SESSION['settings'][$sess];
49
            }
50
        } else {
51
            $path = XOOPS_ROOT_PATH;
52
            if (defined('XOOPS_PATH')) {
53
                $this->xoopsPath['lib'] = XOOPS_PATH;
54
            } elseif (defined('XOOPS_TRUST_PATH')) {
55
                $this->xoopsPath['lib'] = XOOPS_TRUST_PATH;
56
            } else {
57
                $this->xoopsPath['lib'] = dirname($path) . '/xoops_lib';
58
                if (!is_dir($this->xoopsPath['lib'] . '/')) {
59
                    $this->xoopsPath['lib'] = $path . '/xoops_lib';
60
                }
61
            }
62
            if (defined('XOOPS_VAR_PATH')) {
63
                $this->xoopsPath['data'] = XOOPS_VAR_PATH;
64
            } else {
65
                $this->xoopsPath['data'] = dirname($path) . '/xoops_data';
66
                if (!is_dir($this->xoopsPath['data'] . '/')) {
67
                    $this->xoopsPath['data'] = $path . '/xoops_data';
68
                }
69
            }
70
        }
71
    }
72
73
    /**
74
     * @return bool
75
     */
76
    public function execute()
77
    {
78
        $this->readRequest();
79
        $valid = $this->validate();
80
        if ($_SERVER['REQUEST_METHOD'] === 'POST' && @$_POST['task'] === 'path') {
81
            foreach ($this->path_lookup as $req => $sess) {
82
                $_SESSION['settings'][$sess] = $this->xoopsPath[$req];
83
            }
84
            if ($valid) {
85
                return $_SESSION['settings'];
86
            } else {
87
                return false;
88
            }
89
        }
90
        return null;
91
    }
92
93
    public function readRequest()
94
    {
95
        if ($_SERVER['REQUEST_METHOD'] === 'POST' && @$_POST['task'] === 'path') {
96
            $request = $_POST;
97
            foreach ($this->path_lookup as $req => $sess) {
98
                if (isset($request[$req])) {
99
                    $request[$req] = str_replace("\\", '/', trim($request[$req]));
100
                    if (substr($request[$req], -1) === '/') {
101
                        $request[$req] = substr($request[$req], 0, -1);
102
                    }
103
                    $this->xoopsPath[$req] = $request[$req];
104
                }
105
            }
106
        }
107
    }
108
109
    /**
110
     * @return bool
111
     */
112
    public function validate()
113
    {
114
        foreach (array_keys($this->xoopsPath) as $path) {
115
            if ($this->checkPath($path)) {
116
                $this->checkPermissions($path);
117
            }
118
        }
119
        $validPaths = (array_sum(array_values($this->validPath)) == count(array_keys($this->validPath))) ? 1 : 0;
120
        $validPerms = true;
121
        foreach ($this->permErrors as $key => $errs) {
122
            if (empty($errs)) {
123
                continue;
124
            }
125
            foreach ($errs as $path => $status) {
126
                if (empty($status)) {
127
                    $validPerms = false;
128
                    break;
129
                }
130
            }
131
        }
132
133
        return ($validPaths && $validPerms);
134
    }
135
136
    /**
137
     * @param string $PATH
138
     *
139
     * @return int
140
     */
141
    public function checkPath($PATH = '')
142
    {
143
        $ret = 1;
144
        if ($PATH === 'lib' || empty($PATH)) {
145
            $path = 'lib';
146
            if (is_dir($this->xoopsPath[$path]) && is_readable($this->xoopsPath[$path])) {
147
                $this->validPath[$path] = 1;
148
            }
149
            $ret *= $this->validPath[$path];
150
        }
151
        if ($PATH === 'data' || empty($PATH)) {
152
            $path = 'data';
153
            if (is_dir($this->xoopsPath[$path]) && is_readable($this->xoopsPath[$path])) {
154
                $this->validPath[$path] = 1;
155
            }
156
            $ret *= $this->validPath[$path];
157
        }
158
159
        return $ret;
160
    }
161
162
    /**
163
     * @param $parent
164
     * @param $path
165
     * @param $error
166
     * @return null
167
     */
168
    public function setPermission($parent, $path, &$error)
169
    {
170
        if (is_array($path)) {
171
            foreach (array_keys($path) as $item) {
172
                if (is_string($item)) {
173
                    $error[$parent . '/' . $item] = $this->makeWritable($parent . '/' . $item);
174
                    if (empty($path[$item])) {
175
                        continue;
176
                    }
177
                    foreach ($path[$item] as $child) {
178
                        $this->setPermission($parent . '/' . $item, $child, $error);
179
                    }
180
                } else {
181
                    $error[$parent . '/' . $path[$item]] = $this->makeWritable($parent . '/' . $path[$item]);
182
                }
183
            }
184
        } else {
185
            $error[$parent . '/' . $path] = $this->makeWritable($parent . '/' . $path);
186
        }
187
188
        return null;
189
    }
190
191
    /**
192
     * @param string $path
193
     *
194
     * @return bool
195
     */
196
    public function checkPermissions($path = 'data')
197
    {
198
        $paths  = [
199
            'data' => [
200
                'caches' => [
201
                    'xoops_cache',
202
                    'smarty_cache',
203
                    'smarty_compile',
204
                ],
205
                'configs',
206
            ],
207
        ];
208
        $errors = [
209
            'data' => null,
210
        ];
211
        if (!isset($this->xoopsPath[$path])) {
212
            return false;
213
        }
214
        if (!isset($paths[$path])) {
215
            return true;
216
        }
217
        $this->setPermission($this->xoopsPath[$path], $paths[$path], $errors[$path]);
218
        if (in_array(false, $errors[$path])) {
0 ignored issues
show
Bug introduced by
$errors[$path] of type null is incompatible with the type array expected by parameter $haystack of in_array(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

218
        if (in_array(false, /** @scrutinizer ignore-type */ $errors[$path])) {
Loading history...
219
            $this->permErrors[$path] = $errors[$path];
220
221
            return false;
222
        }
223
224
        return true;
225
    }
226
227
    /**
228
     * Write-enable the specified file/folder
229
     *
230
     * @param string      $path
231
     * @param bool|string $group
232
     * @param bool        $create
233
     *
234
     * @internal param bool $recurse
235
     * @return false on failure, method (u-ser,g-roup,w-orld) on success
236
     */
237
    public function makeWritable($path, $group = false, $create = true)
238
    {
239
        if (!file_exists($path)) {
240
            if (!$create) {
241
                return false;
242
            } else {
243
                $perm = 6;
244
                @mkdir($path, octdec('0' . $perm . '00'));
0 ignored issues
show
Bug introduced by
It seems like octdec('0' . $perm . '00') can also be of type double; however, parameter $permissions of mkdir() does only seem to accept integer, 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

244
                @mkdir($path, /** @scrutinizer ignore-type */ octdec('0' . $perm . '00'));
Loading history...
Security Best Practice introduced by
It seems like you do not handle an error condition for mkdir(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

244
                /** @scrutinizer ignore-unhandled */ @mkdir($path, octdec('0' . $perm . '00'));

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
245
            }
246
        } else {
247
            $perm = is_dir($path) ? 6 : 7;
248
        }
249
        if (!is_writable($path)) {
250
            // First try using owner bit
251
            @chmod($path, octdec('0' . $perm . '00'));
0 ignored issues
show
Bug introduced by
It seems like octdec('0' . $perm . '00') can also be of type double; however, parameter $permissions of chmod() does only seem to accept integer, 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

251
            @chmod($path, /** @scrutinizer ignore-type */ octdec('0' . $perm . '00'));
Loading history...
Security Best Practice introduced by
It seems like you do not handle an error condition for chmod(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

251
            /** @scrutinizer ignore-unhandled */ @chmod($path, octdec('0' . $perm . '00'));

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
252
            clearstatcache();
253
            if (!is_writable($path) && $group !== false) {
254
                // If group has been specified, try using the group bit
255
                @chgrp($path, $group);
0 ignored issues
show
Bug introduced by
It seems like $group can also be of type true; however, parameter $group of chgrp() does only seem to accept integer|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

255
                @chgrp($path, /** @scrutinizer ignore-type */ $group);
Loading history...
Security Best Practice introduced by
It seems like you do not handle an error condition for chgrp(). This can introduce security issues, and is generally not recommended. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unhandled  annotation

255
                /** @scrutinizer ignore-unhandled */ @chgrp($path, $group);

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
256
                @chmod($path, octdec('0' . $perm . $perm . '0'));
257
            }
258
            clearstatcache();
259
            if (!is_writable($path)) {
260
                @chmod($path, octdec('0' . $perm . $perm . $perm));
261
            }
262
        }
263
        clearstatcache();
264
        if (is_writable($path)) {
265
            $info = stat($path);
266
            //echo $path . ' : ' . sprintf( '%o', $info['mode'] ) . '....';
267
            if ($info['mode'] & 0002) {
268
                return 'w';
0 ignored issues
show
Bug Best Practice introduced by
The expression return 'w' returns the type string which is incompatible with the documented return type false.
Loading history...
269
            } elseif ($info['mode'] & 0020) {
270
                return 'g';
0 ignored issues
show
Bug Best Practice introduced by
The expression return 'g' returns the type string which is incompatible with the documented return type false.
Loading history...
271
            }
272
273
            return 'u';
0 ignored issues
show
Bug Best Practice introduced by
The expression return 'u' returns the type string which is incompatible with the documented return type false.
Loading history...
274
        }
275
276
        return false;
277
    }
278
}
279