Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 279 |
|
0.00% |
0 / 43 |
CRAP | |
0.00% |
0 / 3 |
transfer | |
0.00% |
0 / 68 |
|
0.00% |
0 / 12 |
552 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
write_file | |
0.00% |
0 / 12 |
|
0.00% |
0 / 1 |
6 | |||
overwrite_file | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
2 | |||
make_dir | |
0.00% |
0 / 16 |
|
0.00% |
0 / 1 |
20 | |||
copy_file | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
12 | |||
delete_file | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
remove_dir | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
rename | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
2 | |||
file_exists | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
20 | |||
open_session | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
close_session | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
methods | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
12 | |||
ftp | |
0.00% |
0 / 86 |
|
0.00% |
0 / 14 |
1806 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
20 | |||
data | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
2 | |||
_init | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
20 | |||
_mkdir | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
_rmdir | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
_rename | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
_chdir | |
0.00% |
0 / 6 |
|
0.00% |
0 / 1 |
30 | |||
_chmod | |
0.00% |
0 / 7 |
|
0.00% |
0 / 1 |
12 | |||
_put | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
6 | |||
_delete | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
_close | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
_cwd | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
_ls | |
0.00% |
0 / 15 |
|
0.00% |
0 / 1 |
90 | |||
_site | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
ftp_fsock | |
0.00% |
0 / 125 |
|
0.00% |
0 / 17 |
3306 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 10 |
|
0.00% |
0 / 1 |
20 | |||
data | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
2 | |||
_init | |
0.00% |
0 / 13 |
|
0.00% |
0 / 1 |
42 | |||
_mkdir | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
_rmdir | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
_rename | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
_chdir | |
0.00% |
0 / 4 |
|
0.00% |
0 / 1 |
20 | |||
_chmod | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
_put | |
0.00% |
0 / 11 |
|
0.00% |
0 / 1 |
20 | |||
_delete | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
_close | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
_cwd | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
_ls | |
0.00% |
0 / 22 |
|
0.00% |
0 / 1 |
110 | |||
_send_command | |
0.00% |
0 / 8 |
|
0.00% |
0 / 1 |
30 | |||
_open_data_connection | |
0.00% |
0 / 25 |
|
0.00% |
0 / 1 |
90 | |||
_close_data_connection | |
0.00% |
0 / 3 |
|
0.00% |
0 / 1 |
6 | |||
_check_command | |
0.00% |
0 / 9 |
|
0.00% |
0 / 1 |
20 |
1 | <?php |
2 | /** |
3 | * |
4 | * This file is part of the phpBB Forum Software package. |
5 | * |
6 | * @copyright (c) phpBB Limited <https://www.phpbb.com> |
7 | * @license GNU General Public License, version 2 (GPL-2.0) |
8 | * |
9 | * For full copyright and license information, please see |
10 | * the docs/CREDITS.txt file. |
11 | * |
12 | */ |
13 | |
14 | /** |
15 | * @ignore |
16 | */ |
17 | if (!defined('IN_PHPBB')) |
18 | { |
19 | exit; |
20 | } |
21 | |
22 | /** |
23 | * Transfer class, wrapper for ftp/sftp/ssh |
24 | */ |
25 | class transfer |
26 | { |
27 | var $connection; |
28 | var $host; |
29 | var $port; |
30 | var $username; |
31 | var $password; |
32 | var $timeout; |
33 | var $root_path; |
34 | var $tmp_path; |
35 | var $file_perms; |
36 | var $dir_perms; |
37 | |
38 | /** |
39 | * Constructor - init some basic values |
40 | */ |
41 | function __construct() |
42 | { |
43 | global $phpbb_root_path; |
44 | |
45 | $this->file_perms = 0644; |
46 | $this->dir_perms = 0777; |
47 | |
48 | // We use the store directory as temporary path to circumvent open basedir restrictions |
49 | $this->tmp_path = $phpbb_root_path . 'store/'; |
50 | } |
51 | |
52 | /** |
53 | * Write file to location |
54 | */ |
55 | function write_file($destination_file = '', $contents = '') |
56 | { |
57 | global $phpbb_root_path; |
58 | |
59 | $destination_file = $this->root_path . str_replace($phpbb_root_path, '', $destination_file); |
60 | |
61 | // need to create a temp file and then move that temp file. |
62 | // ftp functions can only move files around and can't create. |
63 | // This means that the users will need to have access to write |
64 | // temporary files or have write access on a folder within phpBB |
65 | // like the cache folder. If the user can't do either, then |
66 | // he/she needs to use the fsock ftp method |
67 | $temp_name = tempnam($this->tmp_path, 'transfer_'); |
68 | @unlink($temp_name); |
69 | |
70 | $fp = @fopen($temp_name, 'w'); |
71 | |
72 | if (!$fp) |
73 | { |
74 | trigger_error('Unable to create temporary file ' . $temp_name, E_USER_ERROR); |
75 | } |
76 | |
77 | @fwrite($fp, $contents); |
78 | @fclose($fp); |
79 | |
80 | $result = $this->overwrite_file($temp_name, $destination_file); |
81 | |
82 | // remove temporary file now |
83 | @unlink($temp_name); |
84 | |
85 | return $result; |
86 | } |
87 | |
88 | /** |
89 | * Moving file into location. If the destination file already exists it gets overwritten |
90 | */ |
91 | function overwrite_file($source_file, $destination_file) |
92 | { |
93 | /** |
94 | * @todo generally think about overwriting files in another way, by creating a temporary file and then renaming it |
95 | * @todo check for the destination file existance too |
96 | */ |
97 | $this->_delete($destination_file); |
98 | $result = $this->_put($source_file, $destination_file); |
99 | $this->_chmod($destination_file, $this->file_perms); |
100 | |
101 | return $result; |
102 | } |
103 | |
104 | /** |
105 | * Create directory structure |
106 | */ |
107 | function make_dir($dir) |
108 | { |
109 | global $phpbb_root_path; |
110 | |
111 | $dir = str_replace($phpbb_root_path, '', $dir); |
112 | $dir = explode('/', $dir); |
113 | $dirs = ''; |
114 | |
115 | for ($i = 0, $total = count($dir); $i < $total; $i++) |
116 | { |
117 | $result = true; |
118 | |
119 | if (strpos($dir[$i], '.') === 0) |
120 | { |
121 | continue; |
122 | } |
123 | $cur_dir = $dir[$i] . '/'; |
124 | |
125 | if (!file_exists($phpbb_root_path . $dirs . $cur_dir)) |
126 | { |
127 | // create the directory |
128 | $result = $this->_mkdir($dir[$i]); |
129 | $this->_chmod($dir[$i], $this->dir_perms); |
130 | } |
131 | |
132 | $this->_chdir($this->root_path . $dirs . $dir[$i]); |
133 | $dirs .= $cur_dir; |
134 | } |
135 | |
136 | $this->_chdir($this->root_path); |
137 | |
138 | /** |
139 | * @todo stack result into array to make sure every path creation has been taken care of |
140 | */ |
141 | return $result; |
142 | } |
143 | |
144 | /** |
145 | * Copy file from source location to destination location |
146 | */ |
147 | function copy_file($from_loc, $to_loc) |
148 | { |
149 | global $phpbb_root_path; |
150 | |
151 | $from_loc = ((strpos($from_loc, $phpbb_root_path) !== 0) ? $phpbb_root_path : '') . $from_loc; |
152 | $to_loc = $this->root_path . str_replace($phpbb_root_path, '', $to_loc); |
153 | |
154 | if (!file_exists($from_loc)) |
155 | { |
156 | return false; |
157 | } |
158 | |
159 | $result = $this->overwrite_file($from_loc, $to_loc); |
160 | |
161 | return $result; |
162 | } |
163 | |
164 | /** |
165 | * Remove file |
166 | */ |
167 | function delete_file($file) |
168 | { |
169 | global $phpbb_root_path; |
170 | |
171 | $file = $this->root_path . str_replace($phpbb_root_path, '', $file); |
172 | |
173 | return $this->_delete($file); |
174 | } |
175 | |
176 | /** |
177 | * Remove directory |
178 | * @todo remove child directories? |
179 | */ |
180 | function remove_dir($dir) |
181 | { |
182 | global $phpbb_root_path; |
183 | |
184 | $dir = $this->root_path . str_replace($phpbb_root_path, '', $dir); |
185 | |
186 | return $this->_rmdir($dir); |
187 | } |
188 | |
189 | /** |
190 | * Rename a file or folder |
191 | */ |
192 | function rename($old_handle, $new_handle) |
193 | { |
194 | global $phpbb_root_path; |
195 | |
196 | $old_handle = $this->root_path . str_replace($phpbb_root_path, '', $old_handle); |
197 | |
198 | return $this->_rename($old_handle, $new_handle); |
199 | } |
200 | |
201 | /** |
202 | * Check if a specified file exist... |
203 | */ |
204 | function file_exists($directory, $filename) |
205 | { |
206 | global $phpbb_root_path; |
207 | |
208 | $directory = $this->root_path . str_replace($phpbb_root_path, '', $directory); |
209 | |
210 | $this->_chdir($directory); |
211 | $result = $this->_ls(); |
212 | |
213 | if ($result !== false && is_array($result)) |
214 | { |
215 | return (in_array($filename, $result)) ? true : false; |
216 | } |
217 | |
218 | return false; |
219 | } |
220 | |
221 | /** |
222 | * Open session |
223 | */ |
224 | function open_session() |
225 | { |
226 | return $this->_init(); |
227 | } |
228 | |
229 | /** |
230 | * Close current session |
231 | */ |
232 | function close_session() |
233 | { |
234 | return $this->_close(); |
235 | } |
236 | |
237 | /** |
238 | * Determine methods able to be used |
239 | */ |
240 | public static function methods() |
241 | { |
242 | $methods = array(); |
243 | $disabled_functions = explode(',', @ini_get('disable_functions')); |
244 | |
245 | if (@extension_loaded('ftp')) |
246 | { |
247 | $methods[] = 'ftp'; |
248 | } |
249 | |
250 | if (!in_array('fsockopen', $disabled_functions)) |
251 | { |
252 | $methods[] = 'ftp_fsock'; |
253 | } |
254 | |
255 | return $methods; |
256 | } |
257 | } |
258 | |
259 | /** |
260 | * FTP transfer class |
261 | */ |
262 | class ftp extends transfer |
263 | { |
264 | /** |
265 | * Standard parameters for FTP session |
266 | */ |
267 | function __construct($host, $username, $password, $root_path, $port = 21, $timeout = 10) |
268 | { |
269 | $this->host = $host; |
270 | $this->port = $port; |
271 | $this->username = $username; |
272 | $this->password = $password; |
273 | $this->timeout = $timeout; |
274 | |
275 | // Make sure $this->root_path is layed out the same way as the $user->page['root_script_path'] value (/ at the end) |
276 | $this->root_path = str_replace('\\', '/', $this->root_path); |
277 | |
278 | if (!empty($root_path)) |
279 | { |
280 | $this->root_path = (($root_path[0] != '/' ) ? '/' : '') . $root_path . ((substr($root_path, -1, 1) == '/') ? '' : '/'); |
281 | } |
282 | |
283 | // Init some needed values |
284 | parent::__construct(); |
285 | |
286 | return; |
287 | } |
288 | |
289 | /** |
290 | * Requests data |
291 | */ |
292 | public static function data() |
293 | { |
294 | global $user; |
295 | |
296 | return array( |
297 | 'host' => 'localhost', |
298 | 'username' => 'anonymous', |
299 | 'password' => '', |
300 | 'root_path' => $user->page['root_script_path'], |
301 | 'port' => 21, |
302 | 'timeout' => 10 |
303 | ); |
304 | } |
305 | |
306 | /** |
307 | * Init FTP Session |
308 | * @access private |
309 | */ |
310 | function _init() |
311 | { |
312 | // connect to the server |
313 | $this->connection = @ftp_connect($this->host, $this->port, $this->timeout); |
314 | |
315 | if (!$this->connection) |
316 | { |
317 | return 'ERR_CONNECTING_SERVER'; |
318 | } |
319 | |
320 | // login to the server |
321 | if (!@ftp_login($this->connection, $this->username, $this->password)) |
322 | { |
323 | return 'ERR_UNABLE_TO_LOGIN'; |
324 | } |
325 | |
326 | // attempt to turn pasv mode on |
327 | @ftp_pasv($this->connection, true); |
328 | |
329 | // change to the root directory |
330 | if (!$this->_chdir($this->root_path)) |
331 | { |
332 | return 'ERR_CHANGING_DIRECTORY'; |
333 | } |
334 | |
335 | return true; |
336 | } |
337 | |
338 | /** |
339 | * Create Directory (MKDIR) |
340 | * @access private |
341 | */ |
342 | function _mkdir($dir) |
343 | { |
344 | if (!$this->connection) |
345 | { |
346 | return false; |
347 | } |
348 | |
349 | return @ftp_mkdir($this->connection, $dir); |
350 | } |
351 | |
352 | /** |
353 | * Remove directory (RMDIR) |
354 | * @access private |
355 | */ |
356 | function _rmdir($dir) |
357 | { |
358 | if (!$this->connection) |
359 | { |
360 | return false; |
361 | } |
362 | |
363 | return @ftp_rmdir($this->connection, $dir); |
364 | } |
365 | |
366 | /** |
367 | * Rename file |
368 | * @access private |
369 | */ |
370 | function _rename($old_handle, $new_handle) |
371 | { |
372 | if (!$this->connection) |
373 | { |
374 | return false; |
375 | } |
376 | |
377 | return @ftp_rename($this->connection, $old_handle, $new_handle); |
378 | } |
379 | |
380 | /** |
381 | * Change current working directory (CHDIR) |
382 | * @access private |
383 | */ |
384 | function _chdir($dir = '') |
385 | { |
386 | if (!$this->connection) |
387 | { |
388 | return false; |
389 | } |
390 | |
391 | if ($dir && $dir !== '/') |
392 | { |
393 | if (substr($dir, -1, 1) == '/') |
394 | { |
395 | $dir = substr($dir, 0, -1); |
396 | } |
397 | } |
398 | |
399 | return @ftp_chdir($this->connection, $dir); |
400 | } |
401 | |
402 | /** |
403 | * change file permissions (CHMOD) |
404 | * @access private |
405 | */ |
406 | function _chmod($file, $perms) |
407 | { |
408 | if (!$this->connection) |
409 | { |
410 | return false; |
411 | } |
412 | |
413 | if (function_exists('ftp_chmod')) |
414 | { |
415 | $err = @ftp_chmod($this->connection, $perms, $file); |
416 | } |
417 | else |
418 | { |
419 | // Unfortunatly CHMOD is not expecting an octal value... |
420 | // We need to transform the integer (which was an octal) to an octal representation (to get the int) and then pass as is. ;) |
421 | $chmod_cmd = 'CHMOD ' . base_convert($perms, 10, 8) . ' ' . $file; |
422 | $err = $this->_site($chmod_cmd); |
423 | } |
424 | |
425 | return $err; |
426 | } |
427 | |
428 | /** |
429 | * Upload file to location (PUT) |
430 | * @access private |
431 | */ |
432 | function _put($from_file, $to_file) |
433 | { |
434 | if (!$this->connection) |
435 | { |
436 | return false; |
437 | } |
438 | |
439 | // We only use the BINARY file mode to cicumvent rewrite actions from ftp server (mostly linefeeds being replaced) |
440 | $mode = FTP_BINARY; |
441 | |
442 | $to_dir = dirname($to_file); |
443 | $to_file = basename($to_file); |
444 | $this->_chdir($to_dir); |
445 | |
446 | $result = @ftp_put($this->connection, $to_file, $from_file, $mode); |
447 | $this->_chdir($this->root_path); |
448 | |
449 | return $result; |
450 | } |
451 | |
452 | /** |
453 | * Delete file (DELETE) |
454 | * @access private |
455 | */ |
456 | function _delete($file) |
457 | { |
458 | if (!$this->connection) |
459 | { |
460 | return false; |
461 | } |
462 | |
463 | return @ftp_delete($this->connection, $file); |
464 | } |
465 | |
466 | /** |
467 | * Close ftp session (CLOSE) |
468 | * @access private |
469 | */ |
470 | function _close() |
471 | { |
472 | if (!$this->connection) |
473 | { |
474 | return false; |
475 | } |
476 | |
477 | return @ftp_quit($this->connection); |
478 | } |
479 | |
480 | /** |
481 | * Return current working directory (CWD) |
482 | * At the moment not used by parent class |
483 | * @access private |
484 | */ |
485 | function _cwd() |
486 | { |
487 | if (!$this->connection) |
488 | { |
489 | return false; |
490 | } |
491 | |
492 | return @ftp_pwd($this->connection); |
493 | } |
494 | |
495 | /** |
496 | * Return list of files in a given directory (LS) |
497 | * @access private |
498 | */ |
499 | function _ls($dir = './') |
500 | { |
501 | if (!$this->connection) |
502 | { |
503 | return false; |
504 | } |
505 | |
506 | $list = @ftp_nlist($this->connection, $dir); |
507 | |
508 | // See bug #46295 - Some FTP daemons don't like './' |
509 | if ($dir === './') |
510 | { |
511 | // Let's try some alternatives |
512 | $list = (empty($list)) ? @ftp_nlist($this->connection, '.') : $list; |
513 | $list = (empty($list)) ? @ftp_nlist($this->connection, '') : $list; |
514 | } |
515 | |
516 | // Return on error |
517 | if ($list === false) |
518 | { |
519 | return false; |
520 | } |
521 | |
522 | // Remove path if prepended |
523 | foreach ($list as $key => $item) |
524 | { |
525 | // Use same separator for item and dir |
526 | $item = str_replace('\\', '/', $item); |
527 | $dir = str_replace('\\', '/', $dir); |
528 | |
529 | if (!empty($dir) && strpos($item, $dir) === 0) |
530 | { |
531 | $item = substr($item, strlen($dir)); |
532 | } |
533 | |
534 | $list[$key] = $item; |
535 | } |
536 | |
537 | return $list; |
538 | } |
539 | |
540 | /** |
541 | * FTP SITE command (ftp-only function) |
542 | * @access private |
543 | */ |
544 | function _site($command) |
545 | { |
546 | if (!$this->connection) |
547 | { |
548 | return false; |
549 | } |
550 | |
551 | return @ftp_site($this->connection, $command); |
552 | } |
553 | } |
554 | |
555 | /** |
556 | * FTP fsock transfer class |
557 | */ |
558 | class ftp_fsock extends transfer |
559 | { |
560 | var $data_connection; |
561 | |
562 | /** |
563 | * Standard parameters for FTP session |
564 | */ |
565 | function __construct($host, $username, $password, $root_path, $port = 21, $timeout = 10) |
566 | { |
567 | $this->host = $host; |
568 | $this->port = $port; |
569 | $this->username = $username; |
570 | $this->password = $password; |
571 | $this->timeout = $timeout; |
572 | |
573 | // Make sure $this->root_path is layed out the same way as the $user->page['root_script_path'] value (/ at the end) |
574 | $this->root_path = str_replace('\\', '/', $this->root_path); |
575 | |
576 | if (!empty($root_path)) |
577 | { |
578 | $this->root_path = (($root_path[0] != '/' ) ? '/' : '') . $root_path . ((substr($root_path, -1, 1) == '/') ? '' : '/'); |
579 | } |
580 | |
581 | // Init some needed values |
582 | parent::__construct(); |
583 | |
584 | return; |
585 | } |
586 | |
587 | /** |
588 | * Requests data |
589 | */ |
590 | public static function data() |
591 | { |
592 | global $user; |
593 | |
594 | return array( |
595 | 'host' => 'localhost', |
596 | 'username' => 'anonymous', |
597 | 'password' => '', |
598 | 'root_path' => $user->page['root_script_path'], |
599 | 'port' => 21, |
600 | 'timeout' => 10 |
601 | ); |
602 | } |
603 | |
604 | /** |
605 | * Init FTP Session |
606 | * @access private |
607 | */ |
608 | function _init() |
609 | { |
610 | $errno = 0; |
611 | $errstr = ''; |
612 | |
613 | // connect to the server |
614 | $this->connection = @fsockopen($this->host, $this->port, $errno, $errstr, $this->timeout); |
615 | |
616 | if (!$this->connection || !$this->_check_command()) |
617 | { |
618 | return 'ERR_CONNECTING_SERVER'; |
619 | } |
620 | |
621 | @stream_set_timeout($this->connection, $this->timeout); |
622 | |
623 | // login |
624 | if (!$this->_send_command('USER', $this->username)) |
625 | { |
626 | return 'ERR_UNABLE_TO_LOGIN'; |
627 | } |
628 | |
629 | if (!$this->_send_command('PASS', $this->password)) |
630 | { |
631 | return 'ERR_UNABLE_TO_LOGIN'; |
632 | } |
633 | |
634 | // change to the root directory |
635 | if (!$this->_chdir($this->root_path)) |
636 | { |
637 | return 'ERR_CHANGING_DIRECTORY'; |
638 | } |
639 | |
640 | return true; |
641 | } |
642 | |
643 | /** |
644 | * Create Directory (MKDIR) |
645 | * @access private |
646 | */ |
647 | function _mkdir($dir) |
648 | { |
649 | return $this->_send_command('MKD', $dir); |
650 | } |
651 | |
652 | /** |
653 | * Remove directory (RMDIR) |
654 | * @access private |
655 | */ |
656 | function _rmdir($dir) |
657 | { |
658 | return $this->_send_command('RMD', $dir); |
659 | } |
660 | |
661 | /** |
662 | * Rename File |
663 | * @access private |
664 | */ |
665 | function _rename($old_handle, $new_handle) |
666 | { |
667 | $this->_send_command('RNFR', $old_handle); |
668 | return $this->_send_command('RNTO', $new_handle); |
669 | } |
670 | |
671 | /** |
672 | * Change current working directory (CHDIR) |
673 | * @access private |
674 | */ |
675 | function _chdir($dir = '') |
676 | { |
677 | if ($dir && $dir !== '/') |
678 | { |
679 | if (substr($dir, -1, 1) == '/') |
680 | { |
681 | $dir = substr($dir, 0, -1); |
682 | } |
683 | } |
684 | |
685 | return $this->_send_command('CWD', $dir); |
686 | } |
687 | |
688 | /** |
689 | * change file permissions (CHMOD) |
690 | * @access private |
691 | */ |
692 | function _chmod($file, $perms) |
693 | { |
694 | // Unfortunatly CHMOD is not expecting an octal value... |
695 | // We need to transform the integer (which was an octal) to an octal representation (to get the int) and then pass as is. ;) |
696 | return $this->_send_command('SITE CHMOD', base_convert($perms, 10, 8) . ' ' . $file); |
697 | } |
698 | |
699 | /** |
700 | * Upload file to location (PUT) |
701 | * @access private |
702 | */ |
703 | function _put($from_file, $to_file) |
704 | { |
705 | // We only use the BINARY file mode to cicumvent rewrite actions from ftp server (mostly linefeeds being replaced) |
706 | // 'I' == BINARY |
707 | // 'A' == ASCII |
708 | if (!$this->_send_command('TYPE', 'I')) |
709 | { |
710 | return false; |
711 | } |
712 | |
713 | // open the connection to send file over |
714 | if (!$this->_open_data_connection()) |
715 | { |
716 | return false; |
717 | } |
718 | |
719 | $this->_send_command('STOR', $to_file, false); |
720 | |
721 | // send the file |
722 | $fp = @fopen($from_file, 'rb'); |
723 | while (!@feof($fp)) |
724 | { |
725 | @fwrite($this->data_connection, @fread($fp, 4096)); |
726 | } |
727 | @fclose($fp); |
728 | |
729 | // close connection |
730 | $this->_close_data_connection(); |
731 | |
732 | return $this->_check_command(); |
733 | } |
734 | |
735 | /** |
736 | * Delete file (DELETE) |
737 | * @access private |
738 | */ |
739 | function _delete($file) |
740 | { |
741 | return $this->_send_command('DELE', $file); |
742 | } |
743 | |
744 | /** |
745 | * Close ftp session (CLOSE) |
746 | * @access private |
747 | */ |
748 | function _close() |
749 | { |
750 | if (!$this->connection) |
751 | { |
752 | return false; |
753 | } |
754 | |
755 | return $this->_send_command('QUIT'); |
756 | } |
757 | |
758 | /** |
759 | * Return current working directory (CWD) |
760 | * At the moment not used by parent class |
761 | * @access private |
762 | */ |
763 | function _cwd() |
764 | { |
765 | $this->_send_command('PWD', '', false); |
766 | return preg_replace('#^[0-9]{3} "(.+)" .+\r\n#', '\\1', $this->_check_command(true)); |
767 | } |
768 | |
769 | /** |
770 | * Return list of files in a given directory (LS) |
771 | * @access private |
772 | */ |
773 | function _ls($dir = './') |
774 | { |
775 | if (!$this->_open_data_connection()) |
776 | { |
777 | return false; |
778 | } |
779 | |
780 | $this->_send_command('NLST', $dir); |
781 | |
782 | $list = array(); |
783 | while (!@feof($this->data_connection)) |
784 | { |
785 | $filename = preg_replace('#[\r\n]#', '', @fgets($this->data_connection, 512)); |
786 | |
787 | if ($filename !== '') |
788 | { |
789 | $list[] = $filename; |
790 | } |
791 | } |
792 | $this->_close_data_connection(); |
793 | |
794 | // Clear buffer |
795 | $this->_check_command(); |
796 | |
797 | // See bug #46295 - Some FTP daemons don't like './' |
798 | if ($dir === './' && empty($list)) |
799 | { |
800 | // Let's try some alternatives |
801 | $list = $this->_ls('.'); |
802 | |
803 | if (empty($list)) |
804 | { |
805 | $list = $this->_ls(''); |
806 | } |
807 | |
808 | return $list; |
809 | } |
810 | |
811 | // Remove path if prepended |
812 | foreach ($list as $key => $item) |
813 | { |
814 | // Use same separator for item and dir |
815 | $item = str_replace('\\', '/', $item); |
816 | $dir = str_replace('\\', '/', $dir); |
817 | |
818 | if (!empty($dir) && strpos($item, $dir) === 0) |
819 | { |
820 | $item = substr($item, strlen($dir)); |
821 | } |
822 | |
823 | $list[$key] = $item; |
824 | } |
825 | |
826 | return $list; |
827 | } |
828 | |
829 | /** |
830 | * Send a command to server (FTP fsock only function) |
831 | * @access private |
832 | */ |
833 | function _send_command($command, $args = '', $check = true) |
834 | { |
835 | if (!$this->connection) |
836 | { |
837 | return false; |
838 | } |
839 | |
840 | if (!empty($args)) |
841 | { |
842 | $command = "$command $args"; |
843 | } |
844 | |
845 | fwrite($this->connection, $command . "\r\n"); |
846 | |
847 | if ($check === true && !$this->_check_command()) |
848 | { |
849 | return false; |
850 | } |
851 | |
852 | return true; |
853 | } |
854 | |
855 | /** |
856 | * Opens a connection to send data (FTP fosck only function) |
857 | * @access private |
858 | */ |
859 | function _open_data_connection() |
860 | { |
861 | // Try to find out whether we have a IPv4 or IPv6 (control) connection |
862 | if (function_exists('stream_socket_get_name')) |
863 | { |
864 | $socket_name = stream_socket_get_name($this->connection, true); |
865 | $server_ip = substr($socket_name, 0, strrpos($socket_name, ':')); |
866 | } |
867 | |
868 | if (isset($server_ip) && filter_var($server_ip, FILTER_VALIDATE_IP, FILTER_FLAG_IPV4)) // ipv4 |
869 | { |
870 | // Passive mode |
871 | $this->_send_command('PASV', '', false); |
872 | |
873 | if (!$ip_port = $this->_check_command(true)) |
874 | { |
875 | return false; |
876 | } |
877 | |
878 | // open the connection to start sending the file |
879 | if (!preg_match('#[0-9]{1,3},[0-9]{1,3},[0-9]{1,3},[0-9]{1,3},[0-9]+,[0-9]+#', $ip_port, $temp)) |
880 | { |
881 | // bad ip and port |
882 | return false; |
883 | } |
884 | |
885 | $temp = explode(',', $temp[0]); |
886 | $server_ip = $temp[0] . '.' . $temp[1] . '.' . $temp[2] . '.' . $temp[3]; |
887 | $server_port = $temp[4] * 256 + $temp[5]; |
888 | } |
889 | else // ipv6 |
890 | { |
891 | // Extended Passive Mode - RFC2428 |
892 | $this->_send_command('EPSV', '', false); |
893 | |
894 | if (!$epsv_response = $this->_check_command(true)) |
895 | { |
896 | return false; |
897 | } |
898 | |
899 | // Response looks like "229 Entering Extended Passive Mode (|||12345|)" |
900 | // where 12345 is the tcp port for the data connection |
901 | if (!preg_match('#\(\|\|\|([0-9]+)\|\)#', $epsv_response, $match)) |
902 | { |
903 | return false; |
904 | } |
905 | $server_port = (int) $match[1]; |
906 | |
907 | // fsockopen expects IPv6 address in square brackets |
908 | $server_ip = "[$server_ip]"; |
909 | } |
910 | |
911 | $errno = 0; |
912 | $errstr = ''; |
913 | |
914 | if (!$this->data_connection = @fsockopen($server_ip, $server_port, $errno, $errstr, $this->timeout)) |
915 | { |
916 | return false; |
917 | } |
918 | @stream_set_timeout($this->data_connection, $this->timeout); |
919 | |
920 | return true; |
921 | } |
922 | |
923 | /** |
924 | * Closes a connection used to send data |
925 | * @access private |
926 | */ |
927 | function _close_data_connection() |
928 | { |
929 | if (!$this->connection) |
930 | { |
931 | return false; |
932 | } |
933 | |
934 | return @fclose($this->data_connection); |
935 | } |
936 | |
937 | /** |
938 | * Check to make sure command was successful (FTP fsock only function) |
939 | * @access private |
940 | */ |
941 | function _check_command($return = false) |
942 | { |
943 | if (!$this->connection) |
944 | { |
945 | return false; |
946 | } |
947 | |
948 | $response = ''; |
949 | |
950 | do |
951 | { |
952 | $result = @fgets($this->connection, 512); |
953 | $response .= $result; |
954 | } |
955 | while (substr($result, 3, 1) !== ' '); |
956 | |
957 | if (!preg_match('#^[123]#', $response)) |
958 | { |
959 | return false; |
960 | } |
961 | |
962 | return ($return) ? $response : true; |
963 | } |
964 | } |