com php-src: FTP: implement MLSD for structured listing of directories, decribed at https://tools.ietf.org/html/rfc3659: ext/ftp/ftp.c ext/ftp/ftp.h
ext/ftp/php_ftp.c ext/ftp/php_ftp.h e xt/ftp/tests/006.phpt ext/ftp/tests/ftp_mlsd_empt y_directory.phpt ext/ftp/tests/ftp_mlsd_missing_dir
ectory.phpt ext/ftp/tests/server.inc

From: Date: Sun, 29 Jan 2017 07:28:22 +0000
Subject: com php-src: FTP: implement MLSD for structured listing of directories, decribed at https://tools.ietf.org/html/rfc3659: ext/ftp/ftp.c ext/ftp/ftp.h
ext/ftp/php_ftp.c ext/ftp/php_ftp.h e xt/ftp/tests/006.phpt ext/ftp/tests/ftp_mlsd_empt y_directory.phpt ext/ftp/tests/ftp_mlsd_missing_dir
ectory.phpt ext/ftp/tests/server.inc
Groups: php.cvs 
Request: Send a blank email to php-cvs+get-96897@lists.php.net to get a copy of this message
Commit: 0103d1e3bc7b3861a2bff81de204eceb3f4c212a Author: Andreas Treichel <gmblar+github@gmail.com> Sat, 28 Jan 2017 04:20:40 +0100 Committer: Joe Watkins <krakjoe@php.net> Sun, 29 Jan 2017 07:28:22 +0000 Parents: 3de7b2ab523947de3cf85e230a242b1af884641a Branches: master Link: http://git.php.net/?p=php-src.git;a=commitdiff;h=0103d1e3bc7b3861a2bff81de204eceb3f4c212a Log: FTP: implement MLSD for structured listing of directories, decribed at https://tools.ietf.org/html/rfc3659 Changed paths: M ext/ftp/ftp.c M ext/ftp/ftp.h M ext/ftp/php_ftp.c M ext/ftp/php_ftp.h M ext/ftp/tests/006.phpt A ext/ftp/tests/ftp_mlsd_empty_directory.phpt A ext/ftp/tests/ftp_mlsd_missing_directory.phpt M ext/ftp/tests/server.inc Diff: diff --git a/ext/ftp/ftp.c b/ext/ftp/ftp.c index a7793f5..4f682e7 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 077dc78..494c527 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 631eb69..dc7351f 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 d8d987b..37b75f6 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 4d31be7...899ecbb 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 0000000..c9c278a --- /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 0000000..3656ef9 --- /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 ffef025..1ae52f0 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);

« previous php.cvs (#96897) next »