diff options
-rw-r--r-- | ext/ftp/ftp.c | 10 | ||||
-rw-r--r-- | ext/ftp/ftp.h | 7 | ||||
-rw-r--r-- | ext/ftp/php_ftp.c | 36 | ||||
-rw-r--r-- | ext/ftp/php_ftp.h | 1 | ||||
-rw-r--r-- | ext/ftp/tests/006.phpt | 4 | ||||
-rw-r--r-- | ext/ftp/tests/ftp_mlsd_empty_directory.phpt | 34 | ||||
-rw-r--r-- | ext/ftp/tests/ftp_mlsd_missing_directory.phpt | 20 | ||||
-rw-r--r-- | ext/ftp/tests/server.inc | 38 |
8 files changed, 150 insertions, 0 deletions
diff --git a/ext/ftp/ftp.c b/ext/ftp/ftp.c index a7793f5279..4f682e709d 100644 --- a/ext/ftp/ftp.c +++ b/ext/ftp/ftp.c @@ -698,6 +698,16 @@ ftp_list(ftpbuf_t *ftp, const char *path, const size_t path_len, int recursive) } /* }}} */ +/* {{{ ftp_mlsd + */ +char** +ftp_mlsd(ftpbuf_t *ftp, const char *path, const size_t path_len) +{ + return ftp_genlist(ftp, "MLSD", sizeof("MLSD")-1, path, path_len); +} +/* }}} */ + + /* {{{ ftp_type */ int diff --git a/ext/ftp/ftp.h b/ext/ftp/ftp.h index 077dc78943..494c527ca1 100644 --- a/ext/ftp/ftp.h +++ b/ext/ftp/ftp.h @@ -163,6 +163,13 @@ char** ftp_nlist(ftpbuf_t *ftp, const char *path, const size_t path_len); */ char** ftp_list(ftpbuf_t *ftp, const char *path, const size_t path_len, int recursive); +/* returns a NULL-terminated array of lines returned by the ftp + * MLSD command for the given path or NULL on error. the return + * array must be freed (but don't + * free the array elements) + */ +char** ftp_mlsd(ftpbuf_t *ftp, const char *path, const size_t path_len); + /* switches passive mode on or off * returns true on success, false on error */ diff --git a/ext/ftp/php_ftp.c b/ext/ftp/php_ftp.c index 631eb69744..dc7351ff82 100644 --- a/ext/ftp/php_ftp.c +++ b/ext/ftp/php_ftp.c @@ -117,6 +117,11 @@ ZEND_BEGIN_ARG_INFO_EX(arginfo_ftp_rawlist, 0, 0, 2) ZEND_ARG_INFO(0, recursive) ZEND_END_ARG_INFO() +ZEND_BEGIN_ARG_INFO(arginfo_ftp_mlsd, 0) + ZEND_ARG_INFO(0, ftp) + ZEND_ARG_INFO(0, directory) +ZEND_END_ARG_INFO() + ZEND_BEGIN_ARG_INFO(arginfo_ftp_systype, 0) ZEND_ARG_INFO(0, ftp) ZEND_END_ARG_INFO() @@ -254,6 +259,7 @@ const zend_function_entry php_ftp_functions[] = { PHP_FE(ftp_alloc, arginfo_ftp_alloc) PHP_FE(ftp_nlist, arginfo_ftp_nlist) PHP_FE(ftp_rawlist, arginfo_ftp_rawlist) + PHP_FE(ftp_mlsd, arginfo_ftp_mlsd) PHP_FE(ftp_systype, arginfo_ftp_systype) PHP_FE(ftp_pasv, arginfo_ftp_pasv) PHP_FE(ftp_get, arginfo_ftp_get) @@ -748,6 +754,36 @@ PHP_FUNCTION(ftp_rawlist) } /* }}} */ +/* {{{ proto array ftp_mlsd(resource stream, string directory) + Returns a detailed listing of a directory as an array of output lines */ +PHP_FUNCTION(ftp_mlsd) +{ + zval *z_ftp; + ftpbuf_t *ftp; + char **llist, **ptr, *dir; + size_t dir_len; + + if (zend_parse_parameters(ZEND_NUM_ARGS(), "rs", &z_ftp, &dir, &dir_len) == FAILURE) { + return; + } + + if ((ftp = (ftpbuf_t *)zend_fetch_resource(Z_RES_P(z_ftp), le_ftpbuf_name, le_ftpbuf)) == NULL) { + RETURN_FALSE; + } + + /* get raw directory listing */ + if (NULL == (llist = ftp_mlsd(ftp, dir, dir_len))) { + RETURN_FALSE; + } + + array_init(return_value); + for (ptr = llist; *ptr; ptr++) { + add_next_index_string(return_value, *ptr); + } + efree(llist); +} +/* }}} */ + /* {{{ proto string ftp_systype(resource stream) Returns the system type identifier */ PHP_FUNCTION(ftp_systype) diff --git a/ext/ftp/php_ftp.h b/ext/ftp/php_ftp.h index d8d987bfd1..37b75f6b9b 100644 --- a/ext/ftp/php_ftp.h +++ b/ext/ftp/php_ftp.h @@ -54,6 +54,7 @@ PHP_FUNCTION(ftp_chmod); PHP_FUNCTION(ftp_alloc); PHP_FUNCTION(ftp_nlist); PHP_FUNCTION(ftp_rawlist); +PHP_FUNCTION(ftp_mlsd); PHP_FUNCTION(ftp_systype); PHP_FUNCTION(ftp_pasv); PHP_FUNCTION(ftp_get); diff --git a/ext/ftp/tests/006.phpt b/ext/ftp/tests/006.phpt index 4d31be78c0..899ecbbec3 100644 --- a/ext/ftp/tests/006.phpt +++ b/ext/ftp/tests/006.phpt @@ -30,6 +30,7 @@ var_dump(ftp_rename($ftp)); var_dump(ftp_site($ftp)); var_dump(ftp_set_option($ftp)); var_dump(ftp_get_option($ftp)); +var_dump(ftp_mlsd($ftp)); ?> --EXPECTF-- @@ -98,3 +99,6 @@ NULL Warning: ftp_get_option() expects exactly 2 parameters, 1 given in %s006.php on line 25 NULL + +Warning: ftp_mlsd() expects exactly 2 parameters, 1 given in %s006.php on line 26 +NULL diff --git a/ext/ftp/tests/ftp_mlsd_empty_directory.phpt b/ext/ftp/tests/ftp_mlsd_empty_directory.phpt new file mode 100644 index 0000000000..c9c278a11e --- /dev/null +++ b/ext/ftp/tests/ftp_mlsd_empty_directory.phpt @@ -0,0 +1,34 @@ +--TEST-- +ftp_mlsd() must not return false on empty directories +--SKIPIF-- +<?php +require 'skipif.inc'; +?> +--FILE-- +<?php +require 'server.inc'; + +$ftp = ftp_connect('127.0.0.1', $port); +if (!$ftp) die("Couldn't connect to the server"); + +var_dump(ftp_login($ftp, 'user', 'pass')); + +var_dump(ftp_mlsd($ftp, '')); +var_dump(ftp_mlsd($ftp, 'emptydir')); +var_dump(ftp_mlsd($ftp, 'bogusdir')); + +ftp_close($ftp); +?> +--EXPECT-- +bool(true) +array(3) { + [0]=> + string(109) "modify=20170127230002;perm=flcdmpe;type=cdir;unique=811U4340002;UNIX.group=33;UNIX.mode=0755;UNIX.owner=33; ." + [1]=> + string(110) "modify=20170127230002;perm=flcdmpe;type=pdir;unique=811U4340002;UNIX.group=33;UNIX.mode=0755;UNIX.owner=33; .." + [2]=> + string(122) "modify=20170126121225;perm=adfrw;size=4729;type=file;unique=811U4340CB9;UNIX.group=33;UNIX.mode=0644;UNIX.owner=33; foobar" +} +array(0) { +} +bool(false) diff --git a/ext/ftp/tests/ftp_mlsd_missing_directory.phpt b/ext/ftp/tests/ftp_mlsd_missing_directory.phpt new file mode 100644 index 0000000000..3656ef92f7 --- /dev/null +++ b/ext/ftp/tests/ftp_mlsd_missing_directory.phpt @@ -0,0 +1,20 @@ +--TEST-- +Testing ftp_mlsd returns false on server error +--CREDITS-- +Andreas Treichel <gmblar+github [at] gmail [dot] com> +--SKIPIF-- +<?php +require 'skipif.inc'; +?> +--FILE-- +<?php +require 'server.inc'; + +$ftp = ftp_connect('127.0.0.1', $port); +ftp_login($ftp, 'user', 'pass'); +if (!$ftp) die("Couldn't connect to the server"); + +var_dump(ftp_mlsd($ftp, 'no_exists/')); +?> +--EXPECT-- +bool(false) diff --git a/ext/ftp/tests/server.inc b/ext/ftp/tests/server.inc index ffef025074..1ae52f05b6 100644 --- a/ext/ftp/tests/server.inc +++ b/ext/ftp/tests/server.inc @@ -441,6 +441,44 @@ if ($pid) { fputs($s, "350 OK\r\n"); }elseif (preg_match('/^SIZE largefile/', $buf)) { fputs($s, "213 5368709120\r\n"); + }elseif (preg_match('/^MLSD no_exists\//', $buf, $matches)) { + fputs($s, "425 Error establishing connection\r\n"); + }elseif (preg_match("~^MLSD(?: ([A-Za-z./]+))?\r\n$~", $buf, $m)) { + + if(isset($m[1]) && (($m[1] === 'bogusdir') || ($m[1] === '/bogusdir'))) { + fputs($s, "250 $m[1]: No such file or directory\r\n"); + continue; + } + + // there are some servers that don't open the ftp-data socket if there's nothing to send + if(isset($bug39458) && isset($m[1]) && $m[1] === 'emptydir') { + fputs($s, "226 Transfer complete.\r\n"); + continue; + } + + if(empty($pasv)) { + fputs($s, "150 File status okay; about to open data connection\r\n"); + if(!$fs = stream_socket_client("tcp://$host:$port")) { + fputs($s, "425 Can't open data connection\r\n"); + continue; + } + } else { + fputs($s, "125 Data connection already open; transfer starting.\r\n"); + $fs = $pasvs; + } + + if((!empty($ssl)) && (!stream_socket_enable_crypto($pasvs, TRUE, STREAM_CRYPTO_METHOD_SSLv23_SERVER))) { + die("SSLv23 handshake failed.\n"); + } + + if(empty($m[1]) || $m[1] !== 'emptydir') { + fputs($fs, "modify=20170127230002;perm=flcdmpe;type=cdir;unique=811U4340002;UNIX.group=33;UNIX.mode=0755;UNIX.owner=33; .\r\n"); + fputs($fs, "modify=20170127230002;perm=flcdmpe;type=pdir;unique=811U4340002;UNIX.group=33;UNIX.mode=0755;UNIX.owner=33; ..\r\n"); + fputs($fs, "modify=20170126121225;perm=adfrw;size=4729;type=file;unique=811U4340CB9;UNIX.group=33;UNIX.mode=0644;UNIX.owner=33; foobar\r\n"); + } + + fputs($s, "226 Closing data Connection.\r\n"); + fclose($fs); }else { fputs($s, "500 Syntax error, command unrecognized.\r\n"); dump_and_exit($buf); |