1 | <?php |
||
2 | |||
3 | /** |
||
4 | * This deals with changing of file and directory permission either with PHP or FTP |
||
5 | * |
||
6 | * @package ElkArte Forum |
||
7 | * @copyright ElkArte Forum contributors |
||
8 | * @license BSD http://opensource.org/licenses/BSD-3-Clause (see accompanying LICENSE.txt file) |
||
9 | * |
||
10 | * This file contains code covered by: |
||
11 | * copyright: 2011 Simple Machines (http://www.simplemachines.org) |
||
12 | * |
||
13 | * @version 2.0 dev |
||
14 | * |
||
15 | */ |
||
16 | |||
17 | namespace ElkArte\Packages; |
||
18 | |||
19 | use ElkArte\AbstractModel; |
||
20 | use ElkArte\Helper\FileFunctions; |
||
21 | use ElkArte\Http\FtpConnection; |
||
22 | |||
23 | /** |
||
24 | * Class that handles teh chmod of files/directories via PHP or FTP |
||
25 | */ |
||
26 | class PackageChmod extends AbstractModel |
||
27 | { |
||
28 | /** @var FileFunctions */ |
||
29 | protected $fileFunc; |
||
30 | |||
31 | /** |
||
32 | * Basic constructor |
||
33 | */ |
||
34 | public function __construct() |
||
35 | { |
||
36 | $this->fileFunc = FileFunctions::instance(); |
||
37 | parent::__construct(); |
||
38 | } |
||
39 | |||
40 | /** |
||
41 | * Create a chmod control for, you guessed it, chmod-ing files / directories. |
||
42 | * |
||
43 | * @param string[] $chmodFiles |
||
44 | * @param array $chmodOptions -- force_find_error, crash_on_error, destination_url |
||
45 | * @param bool $restore_write_status |
||
46 | * @return array|bool |
||
47 | * @package Packages |
||
48 | */ |
||
49 | public function createChmodControl($chmodFiles = array(), $chmodOptions = array(), $restore_write_status = false) |
||
50 | { |
||
51 | global $context, $package_ftp, $txt; |
||
52 | |||
53 | // If we're restoring the status of existing files prepare the data. |
||
54 | if ($restore_write_status && !empty($_SESSION['ftp_connection']['original_perms'])) |
||
55 | { |
||
56 | $this->showList($restore_write_status, $chmodOptions); |
||
57 | } |
||
58 | // Otherwise, it's entirely irrelevant? |
||
59 | elseif ($restore_write_status) |
||
60 | { |
||
61 | return true; |
||
62 | } |
||
63 | |||
64 | // This is where we report what we got up to. |
||
65 | $return_data = [ |
||
66 | 'files' => [ |
||
67 | 'writable' => [], |
||
68 | 'notwritable' => [], |
||
69 | ], |
||
70 | ]; |
||
71 | |||
72 | // If we have some FTP information already, then let's assume it was required |
||
73 | // and try to get ourselves reconnected. |
||
74 | if (!empty($_SESSION['ftp_connection']['connected'])) |
||
75 | { |
||
76 | $package_ftp = new FtpConnection($_SESSION['ftp_connection']['server'], $_SESSION['ftp_connection']['port'], $_SESSION['ftp_connection']['username'], $this->packageCrypt($_SESSION['ftp_connection']['password'])); |
||
77 | |||
78 | // Check for a valid connection |
||
79 | if ($package_ftp->error !== false) |
||
80 | { |
||
81 | unset($package_ftp, $_SESSION['ftp_connection']); |
||
82 | } |
||
83 | } |
||
84 | |||
85 | // Just got a submission, did we? |
||
86 | if (isset($this->_req->post->ftp_username, $this->_req->post->ftp_password) |
||
87 | && (empty($package_ftp) || ($package_ftp->error !== false))) |
||
88 | { |
||
89 | $ftp = $this->getFTPControl(); |
||
90 | } |
||
91 | |||
92 | // Now try to simply make the files writable, with whatever we might have. |
||
93 | if (!empty($chmodFiles)) |
||
94 | { |
||
95 | foreach ($chmodFiles as $k => $file) |
||
96 | { |
||
97 | // Sometimes this can somehow happen maybe? |
||
98 | if (empty($file)) |
||
99 | { |
||
100 | unset($chmodFiles[$k]); |
||
101 | } |
||
102 | // Already writable? |
||
103 | elseif ($this->fileFunc->isWritable($file)) |
||
104 | { |
||
105 | $return_data['files']['writable'][] = $file; |
||
106 | } |
||
107 | else |
||
108 | { |
||
109 | // Now try to change that. |
||
110 | $return_data['files'][$this->pkgChmod($file, true) ? 'writable' : 'notwritable'][] = $file; |
||
111 | } |
||
112 | } |
||
113 | } |
||
114 | |||
115 | // Have we still got nasty files which ain't writable? Dear me we need more FTP good sir. |
||
116 | if (empty($package_ftp) |
||
117 | && (!empty($return_data['files']['notwritable']) || !empty($chmodOptions['force_find_error']))) |
||
118 | { |
||
119 | $this->reportUnWritable($ftp ?? null, $chmodOptions, $return_data); |
||
120 | |||
121 | // Sent here to die? |
||
122 | if (!empty($chmodOptions['crash_on_error'])) |
||
123 | { |
||
124 | $context['page_title'] = $txt['package_ftp_necessary']; |
||
125 | $context['sub_template'] = 'ftp_required'; |
||
126 | obExit(); |
||
127 | } |
||
128 | } |
||
129 | |||
130 | return $return_data; |
||
131 | } |
||
132 | |||
133 | /** |
||
134 | * If file permissions were changed, provide the option to reset them |
||
135 | * |
||
136 | * @param bool $restore_write_status |
||
137 | * @param array $chmodOptions |
||
138 | * @return bool|void |
||
139 | */ |
||
140 | public function showList($restore_write_status, $chmodOptions) |
||
141 | { |
||
142 | global $context, $txt, $scripturl; |
||
143 | |||
144 | // If we're restoring the status of existing files prepare the data. |
||
145 | if ($restore_write_status && !empty($_SESSION['ftp_connection']['original_perms'])) |
||
146 | { |
||
147 | $listOptions = array( |
||
148 | 'id' => 'restore_file_permissions', |
||
149 | 'title' => $txt['package_restore_permissions'], |
||
150 | 'get_items' => array( |
||
151 | 'function' => 'list_restoreFiles', |
||
152 | 'params' => array( |
||
153 | !empty($this->_req->getPost('restore_perms')), |
||
154 | ), |
||
155 | ), |
||
156 | 'columns' => array( |
||
157 | 'path' => array( |
||
158 | 'header' => array( |
||
159 | 'value' => $txt['package_restore_permissions_filename'], |
||
160 | ), |
||
161 | 'data' => array( |
||
162 | 'db' => 'path', |
||
163 | 'class' => 'smalltext', |
||
164 | ), |
||
165 | ), |
||
166 | 'old_perms' => array( |
||
167 | 'header' => array( |
||
168 | 'value' => $txt['package_restore_permissions_orig_status'], |
||
169 | ), |
||
170 | 'data' => array( |
||
171 | 'db' => 'old_perms', |
||
172 | 'class' => 'smalltext', |
||
173 | ), |
||
174 | ), |
||
175 | 'cur_perms' => array( |
||
176 | 'header' => array( |
||
177 | 'value' => $txt['package_restore_permissions_cur_status'], |
||
178 | ), |
||
179 | 'data' => array( |
||
180 | 'function' => static function ($rowData) { |
||
181 | global $txt; |
||
182 | $formatTxt = $rowData['result'] === '' || $rowData['result'] === 'skipped' ? $txt['package_restore_permissions_pre_change'] : $txt['package_restore_permissions_post_change']; |
||
183 | return sprintf($formatTxt, $rowData['cur_perms'], $rowData['new_perms'], $rowData['writable_message']); |
||
184 | }, |
||
185 | 'class' => 'smalltext', |
||
186 | ), |
||
187 | ), |
||
188 | 'check' => array( |
||
189 | 'header' => array( |
||
190 | 'value' => '<input type="checkbox" onclick="invertAll(this, this.form);" class="input_check" />', |
||
191 | 'class' => 'centertext', |
||
192 | ), |
||
193 | 'data' => array( |
||
194 | 'sprintf' => array( |
||
195 | 'format' => '<input type="checkbox" name="restore_files[]" value="%1$s" class="input_check" />', |
||
196 | 'params' => array( |
||
197 | 'path' => false, |
||
198 | ), |
||
199 | ), |
||
200 | 'class' => 'centertext', |
||
201 | ), |
||
202 | ), |
||
203 | 'result' => array( |
||
204 | 'header' => array( |
||
205 | 'value' => $txt['package_restore_permissions_result'], |
||
206 | ), |
||
207 | 'data' => array( |
||
208 | 'function' => static function ($rowData) { |
||
209 | global $txt; |
||
210 | return $txt['package_restore_permissions_action_' . $rowData['result']]; |
||
211 | }, |
||
212 | 'class' => 'smalltext', |
||
213 | ), |
||
214 | ), |
||
215 | ), |
||
216 | 'form' => array( |
||
217 | 'href' => empty($chmodOptions['destination_url']) ? $scripturl . '?action=admin;area=packages;sa=perms;restore;' . $context['session_var'] . '=' . $context['session_id'] : $chmodOptions['destination_url'], |
||
218 | ), |
||
219 | 'additional_rows' => array( |
||
220 | array( |
||
221 | 'position' => 'below_table_data', |
||
222 | 'value' => '<input type="submit" name="restore_perms" value="' . $txt['package_restore_permissions_restore'] . '" class="right_submit" />', |
||
223 | 'class' => 'category_header', |
||
224 | ), |
||
225 | array( |
||
226 | 'position' => 'after_title', |
||
227 | 'value' => '<span class="smalltext">' . $txt['package_restore_permissions_desc'] . '</span>', |
||
228 | ), |
||
229 | ), |
||
230 | ); |
||
231 | |||
232 | // Work out what columns and the like to show. |
||
233 | if (!empty($this->_req->getPost('restore_perms'))) |
||
234 | { |
||
235 | $listOptions['additional_rows'][1]['value'] = sprintf($txt['package_restore_permissions_action_done'], $scripturl . '?action=admin;area=packages;sa=perms;' . $context['session_var'] . '=' . $context['session_id']); |
||
236 | unset($listOptions['columns']['check'], $listOptions['form'], $listOptions['additional_rows'][0]); |
||
237 | |||
238 | $context['sub_template'] = 'show_list'; |
||
239 | $context['default_list'] = 'restore_file_permissions'; |
||
240 | } |
||
241 | else |
||
242 | { |
||
243 | unset($listOptions['columns']['result']); |
||
244 | } |
||
245 | |||
246 | // Create the list for display. |
||
247 | createList($listOptions); |
||
248 | |||
249 | // If we just restored permissions then wherever we are, we are now done and dusted. |
||
250 | if (!empty($this->_req->getPost('restore_perms'))) |
||
251 | { |
||
252 | obExit(); |
||
253 | } |
||
254 | } |
||
255 | // Otherwise, it's entirely irrelevant? |
||
256 | elseif ($restore_write_status) |
||
257 | { |
||
258 | return true; |
||
259 | } |
||
260 | } |
||
261 | |||
262 | /** |
||
263 | * Prepares $context['package_ftp'] with whatever information we may have available. |
||
264 | * |
||
265 | * @param FtpConnection|null $ftp |
||
266 | * @param array $chmodOptions |
||
267 | * @param array $return_data |
||
268 | */ |
||
269 | public function reportUnWritable($ftp, $chmodOptions, $return_data) |
||
270 | { |
||
271 | global $context; |
||
272 | |||
273 | $ftp_server = $this->_req->getPost('ftp_server', 'trim'); |
||
274 | $ftp_port = $this->_req->getPost('ftp_port', 'intval'); |
||
275 | $ftp_username = $this->_req->getPost('ftp_username', 'trim'); |
||
276 | $ftp_path = $this->_req->getPost('ftp_path', 'trim'); |
||
277 | $ftp_error = $_SESSION['ftp_connection']['error'] ?? null; |
||
278 | |||
279 | if (!isset($ftp) || $ftp->error !== false) |
||
280 | { |
||
281 | if (!isset($ftp)) |
||
282 | { |
||
283 | $ftp = new FtpConnection(null); |
||
284 | } |
||
285 | elseif ($ftp->error !== false && !isset($ftp_error)) |
||
286 | { |
||
287 | $ftp_error = $ftp->last_message ?? ''; |
||
288 | } |
||
289 | |||
290 | [$username, $detect_path, $found_path] = $ftp->detect_path(BOARDDIR); |
||
291 | |||
292 | if ($found_path) |
||
293 | { |
||
294 | $ftp_path = $detect_path; |
||
295 | } |
||
296 | elseif (!isset($ftp_path)) |
||
297 | { |
||
298 | $ftp_path = $this->_modSettings['package_path'] ?? $detect_path; |
||
299 | } |
||
300 | } |
||
301 | |||
302 | // Place some hopefully useful information in the form |
||
303 | $context['package_ftp'] = array( |
||
304 | 'server' => $ftp_server ?? ($this->_modSettings['package_server'] ?? 'localhost'), |
||
305 | 'port' => $ftp_port ?? ($this->_modSettings['package_port'] ?? '21'), |
||
306 | 'username' => $ftp_username ?? ($this->_modSettings['package_username'] ?? $username ?? ''), |
||
307 | 'path' => $ftp_path ?? ($this->_modSettings['package_path'] ?? ''), |
||
308 | 'error' => empty($ftp_error) ? null : $ftp_error, |
||
309 | 'destination' => empty($chmodOptions['destination_url']) ? '' : $chmodOptions['destination_url'], |
||
310 | ); |
||
311 | |||
312 | // Which files failed? |
||
313 | $context['notwritable_files'] = $context['notwritable_files'] ?? []; |
||
314 | $context['notwritable_files'] = array_merge($context['notwritable_files'], $return_data['files']['notwritable']); |
||
315 | } |
||
316 | |||
317 | /** |
||
318 | * Using the user supplied FTP information, attempts to create a connection. If |
||
319 | * successful will save the supplied data in session for use in other steps. |
||
320 | * |
||
321 | * @return FtpConnection |
||
322 | */ |
||
323 | public function getFTPControl() |
||
324 | { |
||
325 | global $package_ftp; |
||
326 | |||
327 | // CLean up what was sent |
||
328 | $server = $this->_req->getPost('ftp_server', 'trim', ''); |
||
329 | $port = $this->_req->getPost('ftp_port', 'intval', 21); |
||
330 | $username = $this->_req->getPost('ftp_username', 'trim', ''); |
||
331 | $password = $this->_req->getPost('ftp_password', 'trim', ''); |
||
332 | $path = $this->_req->getPost('ftp_path', 'trim', ''); |
||
333 | |||
334 | $ftp = new FtpConnection($server, $port, $username, $password); |
||
335 | |||
336 | // We're connected, jolly good! |
||
337 | if ($ftp->error === false) |
||
338 | { |
||
339 | // Common mistake, so let's try to remedy it... |
||
340 | if (!$ftp->chdir($path)) |
||
341 | { |
||
342 | $ftp_error = $ftp->last_message; |
||
343 | |||
344 | if ($ftp->chdir(preg_replace('~^/home[2]?/[^/]+~', '', $path))) |
||
345 | { |
||
346 | $path = preg_replace('~^/home[2]?/[^/]+~', '', $path); |
||
347 | $ftp_error = $ftp->last_message; |
||
348 | } |
||
349 | } |
||
350 | |||
351 | // A valid path was entered |
||
352 | if (!in_array($path, array('', '/'), true) && empty($ftp_error)) |
||
353 | { |
||
354 | $ftp_root = substr(BOARDDIR, 0, -strlen($path)); |
||
355 | |||
356 | // Avoid double//slash entries |
||
357 | if (substr($ftp_root, -1) === '/' && (substr($path, 0, 1) === '/')) |
||
358 | { |
||
359 | $ftp_root = substr($ftp_root, 0, -1); |
||
360 | } |
||
361 | } |
||
362 | else |
||
363 | { |
||
364 | $ftp_root = BOARDDIR; |
||
365 | } |
||
366 | |||
367 | $_SESSION['ftp_connection'] = array( |
||
368 | 'server' => $server, |
||
369 | 'port' => $port, |
||
370 | 'username' => $username, |
||
371 | 'password' => $this->packageCrypt($password), |
||
372 | 'path' => $path, |
||
373 | 'root' => rtrim($ftp_root, '\/'), |
||
374 | 'connected' => true, |
||
375 | 'error' => empty($ftp_error) ? null : $ftp_error, |
||
376 | ); |
||
377 | |||
378 | if (!isset($this->_modSettings['package_path']) || $this->_modSettings['package_path'] !== $path) |
||
379 | { |
||
380 | updateSettings(['package_path' => $path]); |
||
381 | } |
||
382 | |||
383 | // This is now the primary connection. |
||
384 | $package_ftp = $ftp; |
||
385 | } |
||
386 | |||
387 | return $ftp; |
||
388 | } |
||
389 | |||
390 | /** |
||
391 | * Try to make a file writable using PHP and/or FTP if available |
||
392 | * |
||
393 | * @param string $filename |
||
394 | * @param bool $track_change = false |
||
395 | * |
||
396 | * @return bool True if it worked, false if it didn't |
||
397 | * @package Packages |
||
398 | */ |
||
399 | public function pkgChmod($filename, $track_change = false) |
||
400 | { |
||
401 | global $package_ftp; |
||
402 | |||
403 | // File is already writable, easy |
||
404 | if ($this->fileFunc->isWritable($filename)) |
||
405 | { |
||
406 | return true; |
||
407 | } |
||
408 | |||
409 | // If we don't have FTP, see if we can get this done |
||
410 | if (!isset($package_ftp) || $package_ftp === false) |
||
411 | { |
||
412 | return $this->chmodNoFTP($filename, $track_change); |
||
413 | } |
||
414 | |||
415 | // If we have FTP, then we take it for a spin |
||
416 | if (!empty($_SESSION['ftp_connection'])) |
||
417 | { |
||
418 | return $this->chmodWithFTP($filename, $track_change); |
||
419 | } |
||
420 | |||
421 | // Oh dear, we failed if we get here. |
||
422 | return false; |
||
423 | } |
||
424 | |||
425 | /** |
||
426 | * Try to make a file writable using built in PHP SplFileInfo() functions |
||
427 | * |
||
428 | * @param string $filename |
||
429 | * @param bool $track_change = false |
||
430 | * @return bool True if it worked, false if it didn't |
||
431 | */ |
||
432 | public function chmodNoFTP($filename, $track_change) |
||
433 | { |
||
434 | $chmod_file = $filename; |
||
435 | |||
436 | for ($i = 0; $i < 2; $i++) |
||
437 | { |
||
438 | // Start off with a less aggressive test. |
||
439 | if ($i === 0) |
||
440 | { |
||
441 | // If this file doesn't exist, then we actually want to look at whatever parent directory does. |
||
442 | $subTraverseLimit = 2; |
||
443 | while (!$this->fileFunc->fileExists($chmod_file) && $subTraverseLimit) |
||
444 | { |
||
445 | $chmod_file = dirname($chmod_file); |
||
446 | $subTraverseLimit--; |
||
447 | } |
||
448 | |||
449 | // Keep track of the writable status here. |
||
450 | $file_permissions = $this->fileFunc->filePerms($chmod_file); |
||
451 | } |
||
452 | elseif (!$this->fileFunc->fileExists($chmod_file)) |
||
453 | { |
||
454 | // This looks odd, but it's an attempt to work around PHP suExec. |
||
455 | $file_permissions = $this->fileFunc->filePerms(dirname($chmod_file)); |
||
456 | mktree(dirname($chmod_file)); |
||
457 | @touch($chmod_file); |
||
0 ignored issues
–
show
|
|||
458 | $this->fileFunc->elk_chmod($chmod_file, 0755); |
||
459 | } |
||
460 | else |
||
461 | { |
||
462 | $file_permissions = $this->fileFunc->filePerms($chmod_file); |
||
463 | } |
||
464 | |||
465 | // Let chmod make this file or directory writable |
||
466 | $this->fileFunc->chmod($chmod_file); |
||
467 | |||
468 | // The ultimate writable test. |
||
469 | if ($this->testAccess($chmod_file)) |
||
470 | { |
||
471 | // It worked! |
||
472 | if ($track_change) |
||
473 | { |
||
474 | $_SESSION['ftp_connection']['original_perms'][$chmod_file] = $file_permissions; |
||
475 | } |
||
476 | |||
477 | return true; |
||
478 | } |
||
479 | |||
480 | if (isset($_SESSION['ftp_connection']['original_perms'][$chmod_file])) |
||
481 | { |
||
482 | unset($_SESSION['ftp_connection']['original_perms'][$chmod_file]); |
||
483 | } |
||
484 | } |
||
485 | |||
486 | // If we're here we're a failure. |
||
487 | return false; |
||
488 | } |
||
489 | |||
490 | /** |
||
491 | * Try to make a file writable using FTP functions |
||
492 | * |
||
493 | * @param string $filename |
||
494 | * @param bool $track_change = false |
||
495 | * @return bool True if it worked, false if it didn't |
||
496 | */ |
||
497 | public function chmodWithFTP($filename, $track_change) |
||
498 | { |
||
499 | /** @var $package_ftp FtpConnection */ |
||
500 | global $package_ftp; |
||
501 | |||
502 | $ftp_file = setFtpName($filename); |
||
503 | |||
504 | // If the file does not yet exist, make sure its directory is at least writable |
||
505 | if (!$this->fileFunc->fileExists($filename) && !$this->fileFunc->isDir($filename)) |
||
506 | { |
||
507 | $file_permissions = $this->fileFunc->filePerms(dirname($filename)); |
||
508 | |||
509 | // Make sure the directory exits and is writable |
||
510 | mktree(dirname($filename)); |
||
511 | |||
512 | $package_ftp->create_file($ftp_file); |
||
513 | $package_ftp->chmod($ftp_file, 0755); |
||
514 | } |
||
515 | else |
||
516 | { |
||
517 | $file_permissions = $this->fileFunc->filePerms($filename); |
||
518 | } |
||
519 | |||
520 | // Directories |
||
521 | if (!$this->fileFunc->isWritable(dirname($filename))) |
||
522 | { |
||
523 | $package_ftp->ftp_chmod(dirname($ftp_file), [0775, 0777]); |
||
524 | } |
||
525 | |||
526 | if ($this->fileFunc->isDir($filename) && !$this->fileFunc->isWritable($filename)) |
||
527 | { |
||
528 | $package_ftp->ftp_chmod($ftp_file, [0775, 0777]); |
||
529 | } |
||
530 | |||
531 | // File |
||
532 | if (!$this->fileFunc->isDir($filename) && !$this->fileFunc->isWritable($filename)) |
||
533 | { |
||
534 | $package_ftp->ftp_chmod($ftp_file, [0664, 0666]); |
||
535 | } |
||
536 | |||
537 | if ($this->fileFunc->isWritable($filename)) |
||
538 | { |
||
539 | if ($track_change) |
||
540 | { |
||
541 | $_SESSION['ftp_connection']['original_perms'][$filename] = $file_permissions; |
||
542 | } |
||
543 | |||
544 | return true; |
||
545 | } |
||
546 | |||
547 | return false; |
||
548 | } |
||
549 | |||
550 | /** |
||
551 | * The ultimate writable test. |
||
552 | * |
||
553 | * Mind you, I'm not sure why this is needed if PHP says it is writable, but |
||
554 | * sometimes you have to be a lemming. Plus windows ACL is not handled well. |
||
555 | * |
||
556 | * @param $item |
||
557 | * @return bool |
||
558 | */ |
||
559 | public function testAccess($item) |
||
560 | { |
||
561 | $fp = $this->fileFunc->isDir($item) ? @opendir($item) : @fopen($item, 'rb'); |
||
562 | if ($this->fileFunc->isWritable($item) && $fp !== false) |
||
563 | { |
||
564 | if (!$this->fileFunc->isDir($item)) |
||
565 | { |
||
566 | fclose($fp); |
||
567 | } |
||
568 | else |
||
569 | { |
||
570 | closedir($fp); |
||
571 | } |
||
572 | |||
573 | return true; |
||
574 | } |
||
575 | |||
576 | return false; |
||
577 | } |
||
578 | |||
579 | /** |
||
580 | * Used to crypt the supplied ftp password in this session |
||
581 | * |
||
582 | * - Don't be fooled by the name, this is a reversing hash function. |
||
583 | * It will hash a password, and if supplied that hash will return the |
||
584 | * original password. Uses the session_id as salt |
||
585 | * |
||
586 | * @param string $pass |
||
587 | * @return string The encrypted password |
||
588 | * @package Packages |
||
589 | */ |
||
590 | public function packageCrypt($pass) |
||
591 | { |
||
592 | $n = strlen($pass); |
||
593 | |||
594 | $salt = session_id(); |
||
595 | while (strlen($salt) < $n) |
||
596 | { |
||
597 | $salt .= session_id(); |
||
598 | } |
||
599 | |||
600 | for ($i = 0; $i < $n; $i++) |
||
601 | { |
||
602 | $pass[$i] = chr(ord($pass[$i]) ^ (ord($salt[$i]) - 32)); |
||
603 | } |
||
604 | |||
605 | return $pass; |
||
606 | } |
||
607 | } |
If you suppress an error, we recommend checking for the error condition explicitly: