summaryrefslogtreecommitdiff
path: root/ext/ftp
diff options
context:
space:
mode:
Diffstat (limited to 'ext/ftp')
-rw-r--r--ext/ftp/CREDITS2
-rw-r--r--ext/ftp/config.m422
-rw-r--r--ext/ftp/config.w329
-rw-r--r--ext/ftp/ftp.c1943
-rw-r--r--ext/ftp/ftp.h213
-rw-r--r--ext/ftp/package.xml56
-rw-r--r--ext/ftp/php_ftp.c1443
-rw-r--r--ext/ftp/php_ftp.h79
-rw-r--r--ext/ftp/tests/001.phpt36
-rw-r--r--ext/ftp/tests/002.phpt38
-rw-r--r--ext/ftp/tests/003.phpt43
-rw-r--r--ext/ftp/tests/004.phpt79
-rw-r--r--ext/ftp/tests/005.phpt86
-rw-r--r--ext/ftp/tests/006.phpt100
-rw-r--r--ext/ftp/tests/bug27809.phpt21
-rw-r--r--ext/ftp/tests/bug37799.phpt22
-rw-r--r--ext/ftp/tests/bug39458-2.phpt36
-rw-r--r--ext/ftp/tests/bug39458.phpt35
-rw-r--r--ext/ftp/tests/bug39583-2.phpt34
-rw-r--r--ext/ftp/tests/bug39583.phpt35
-rw-r--r--ext/ftp/tests/bug7216-2.phpt21
-rw-r--r--ext/ftp/tests/bug7216.phpt21
-rw-r--r--ext/ftp/tests/cert.pem48
-rw-r--r--ext/ftp/tests/ftp_alloc_basic1.phpt21
-rw-r--r--ext/ftp/tests/ftp_alloc_basic2.phpt23
-rw-r--r--ext/ftp/tests/ftp_chmod_basic.phpt21
-rw-r--r--ext/ftp/tests/ftp_exec_basic.phpt21
-rw-r--r--ext/ftp/tests/ftp_fget_basic.phpt43
-rw-r--r--ext/ftp/tests/ftp_fget_basic1.phpt32
-rw-r--r--ext/ftp/tests/ftp_fget_basic2.phpt32
-rw-r--r--ext/ftp/tests/ftp_fget_basic3.phpt32
-rw-r--r--ext/ftp/tests/ftp_get_basic.phpt40
-rw-r--r--ext/ftp/tests/ftp_mdtm_basic.phpt48
-rw-r--r--ext/ftp/tests/ftp_nb_fget_basic1.phpt32
-rw-r--r--ext/ftp/tests/ftp_nb_fget_basic2.phpt32
-rw-r--r--ext/ftp/tests/ftp_nb_fget_basic3.phpt32
-rw-r--r--ext/ftp/tests/ftp_rawlist_basic2.phpt21
-rw-r--r--ext/ftp/tests/ftp_rmdir_basic.phpt21
-rw-r--r--ext/ftp/tests/server.inc410
-rw-r--r--ext/ftp/tests/skipif.inc5
40 files changed, 5288 insertions, 0 deletions
diff --git a/ext/ftp/CREDITS b/ext/ftp/CREDITS
new file mode 100644
index 0000000..bf0920a
--- /dev/null
+++ b/ext/ftp/CREDITS
@@ -0,0 +1,2 @@
+FTP
+Stefan Esser, Andrew Skalski
diff --git a/ext/ftp/config.m4 b/ext/ftp/config.m4
new file mode 100644
index 0000000..2df7f7b
--- /dev/null
+++ b/ext/ftp/config.m4
@@ -0,0 +1,22 @@
+dnl
+dnl $Id$
+dnl
+
+PHP_ARG_ENABLE(ftp,whether to enable FTP support,
+[ --enable-ftp Enable FTP support])
+
+PHP_ARG_WITH(openssl-dir,OpenSSL dir for FTP,
+[ --with-openssl-dir[=DIR] FTP: openssl install prefix], no, no)
+
+if test "$PHP_FTP" = "yes"; then
+ AC_DEFINE(HAVE_FTP,1,[Whether you want FTP support])
+ PHP_NEW_EXTENSION(ftp, php_ftp.c ftp.c, $ext_shared)
+
+ dnl Empty variable means 'no'
+ test -z "$PHP_OPENSSL" && PHP_OPENSSL=no
+
+ if test "$PHP_OPENSSL" != "no" || test "$PHP_OPENSSL_DIR" != "no"; then
+ PHP_SETUP_OPENSSL(FTP_SHARED_LIBADD)
+ PHP_SUBST(FTP_SHARED_LIBADD)
+ fi
+fi
diff --git a/ext/ftp/config.w32 b/ext/ftp/config.w32
new file mode 100644
index 0000000..c91e350
--- /dev/null
+++ b/ext/ftp/config.w32
@@ -0,0 +1,9 @@
+// $Id$
+// vim:ft=javascript
+
+ARG_ENABLE("ftp", "ftp support", "yes");
+
+if (PHP_FTP == "yes") {
+ EXTENSION("ftp", "php_ftp.c ftp.c");
+ AC_DEFINE('HAVE_FTP', 1, 'Have FTP support');
+}
diff --git a/ext/ftp/ftp.c b/ext/ftp/ftp.c
new file mode 100644
index 0000000..4c8a94f
--- /dev/null
+++ b/ext/ftp/ftp.c
@@ -0,0 +1,1943 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andrew Skalski <askalski@chek.com> |
+ | Stefan Esser <sesser@php.net> (resume functions) |
+ +----------------------------------------------------------------------+
+ */
+
+/* $Id$ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php.h"
+
+#if HAVE_FTP
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#include <fcntl.h>
+#include <string.h>
+#include <time.h>
+#ifdef PHP_WIN32
+#include <winsock2.h>
+#elif defined(NETWARE)
+#ifdef USE_WINSOCK /* Modified to use Winsock (NOVSOCK2.H), atleast for now */
+#include <novsock2.h>
+#else
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+#endif
+#else
+#ifdef HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+#endif
+#include <errno.h>
+
+#if HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+
+#if HAVE_OPENSSL_EXT
+#include <openssl/ssl.h>
+#endif
+
+#include "ftp.h"
+#include "ext/standard/fsock.h"
+
+/* Additional headers for NetWare */
+#if defined(NETWARE) && !defined(USE_WINSOCK)
+#include <sys/select.h>
+#endif
+
+/* sends an ftp command, returns true on success, false on error.
+ * it sends the string "cmd args\r\n" if args is non-null, or
+ * "cmd\r\n" if args is null
+ */
+static int ftp_putcmd( ftpbuf_t *ftp,
+ const char *cmd,
+ const char *args);
+
+/* wrapper around send/recv to handle timeouts */
+static int my_send(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len);
+static int my_recv(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len);
+static int my_accept(ftpbuf_t *ftp, php_socket_t s, struct sockaddr *addr, socklen_t *addrlen);
+
+/* reads a line the socket , returns true on success, false on error */
+static int ftp_readline(ftpbuf_t *ftp);
+
+/* reads an ftp response, returns true on success, false on error */
+static int ftp_getresp(ftpbuf_t *ftp);
+
+/* sets the ftp transfer type */
+static int ftp_type(ftpbuf_t *ftp, ftptype_t type);
+
+/* opens up a data stream */
+static databuf_t* ftp_getdata(ftpbuf_t *ftp TSRMLS_DC);
+
+/* accepts the data connection, returns updated data buffer */
+static databuf_t* data_accept(databuf_t *data, ftpbuf_t *ftp TSRMLS_DC);
+
+/* closes the data connection, returns NULL */
+static databuf_t* data_close(ftpbuf_t *ftp, databuf_t *data);
+
+/* generic file lister */
+static char** ftp_genlist(ftpbuf_t *ftp, const char *cmd, const char *path TSRMLS_DC);
+
+/* IP and port conversion box */
+union ipbox {
+ struct in_addr ia[2];
+ unsigned short s[4];
+ unsigned char c[8];
+};
+
+/* {{{ ftp_open
+ */
+ftpbuf_t*
+ftp_open(const char *host, short port, long timeout_sec TSRMLS_DC)
+{
+ ftpbuf_t *ftp;
+ socklen_t size;
+ struct timeval tv;
+
+
+ /* alloc the ftp structure */
+ ftp = ecalloc(1, sizeof(*ftp));
+
+ tv.tv_sec = timeout_sec;
+ tv.tv_usec = 0;
+
+ ftp->fd = php_network_connect_socket_to_host(host,
+ (unsigned short) (port ? port : 21), SOCK_STREAM,
+ 0, &tv, NULL, NULL, NULL, 0 TSRMLS_CC);
+ if (ftp->fd == -1) {
+ goto bail;
+ }
+
+ /* Default Settings */
+ ftp->timeout_sec = timeout_sec;
+ ftp->nb = 0;
+
+ size = sizeof(ftp->localaddr);
+ memset(&ftp->localaddr, 0, size);
+ if (getsockname(ftp->fd, (struct sockaddr*) &ftp->localaddr, &size) != 0) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "getsockname failed: %s (%d)", strerror(errno), errno);
+ goto bail;
+ }
+
+ if (!ftp_getresp(ftp) || ftp->resp != 220) {
+ goto bail;
+ }
+
+ return ftp;
+
+bail:
+ if (ftp->fd != -1) {
+ closesocket(ftp->fd);
+ }
+ efree(ftp);
+ return NULL;
+}
+/* }}} */
+
+/* {{{ ftp_close
+ */
+ftpbuf_t*
+ftp_close(ftpbuf_t *ftp)
+{
+ if (ftp == NULL) {
+ return NULL;
+ }
+ if (ftp->data) {
+ data_close(ftp, ftp->data);
+ }
+ if (ftp->fd != -1) {
+#if HAVE_OPENSSL_EXT
+ if (ftp->ssl_active) {
+ SSL_shutdown(ftp->ssl_handle);
+ }
+#endif
+ closesocket(ftp->fd);
+ }
+ ftp_gc(ftp);
+ efree(ftp);
+ return NULL;
+}
+/* }}} */
+
+/* {{{ ftp_gc
+ */
+void
+ftp_gc(ftpbuf_t *ftp)
+{
+ if (ftp == NULL) {
+ return;
+ }
+ if (ftp->pwd) {
+ efree(ftp->pwd);
+ ftp->pwd = NULL;
+ }
+ if (ftp->syst) {
+ efree(ftp->syst);
+ ftp->syst = NULL;
+ }
+}
+/* }}} */
+
+/* {{{ ftp_quit
+ */
+int
+ftp_quit(ftpbuf_t *ftp)
+{
+ if (ftp == NULL) {
+ return 0;
+ }
+
+ if (!ftp_putcmd(ftp, "QUIT", NULL)) {
+ return 0;
+ }
+ if (!ftp_getresp(ftp) || ftp->resp != 221) {
+ return 0;
+ }
+
+ if (ftp->pwd) {
+ efree(ftp->pwd);
+ ftp->pwd = NULL;
+ }
+
+ return 1;
+}
+/* }}} */
+
+/* {{{ ftp_login
+ */
+int
+ftp_login(ftpbuf_t *ftp, const char *user, const char *pass TSRMLS_DC)
+{
+#if HAVE_OPENSSL_EXT
+ SSL_CTX *ctx = NULL;
+ long ssl_ctx_options = SSL_OP_ALL;
+#endif
+ if (ftp == NULL) {
+ return 0;
+ }
+
+#if HAVE_OPENSSL_EXT
+ if (ftp->use_ssl && !ftp->ssl_active) {
+ if (!ftp_putcmd(ftp, "AUTH", "TLS")) {
+ return 0;
+ }
+ if (!ftp_getresp(ftp)) {
+ return 0;
+ }
+
+ if (ftp->resp != 234) {
+ if (!ftp_putcmd(ftp, "AUTH", "SSL")) {
+ return 0;
+ }
+ if (!ftp_getresp(ftp)) {
+ return 0;
+ }
+
+ if (ftp->resp != 334) {
+ return 0;
+ } else {
+ ftp->old_ssl = 1;
+ ftp->use_ssl_for_data = 1;
+ }
+ }
+
+ ctx = SSL_CTX_new(SSLv23_client_method());
+ if (ctx == NULL) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create the SSL context");
+ return 0;
+ }
+
+#if OPENSSL_VERSION_NUMBER >= 0x0090605fL
+ ssl_ctx_options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
+#endif
+ SSL_CTX_set_options(ctx, ssl_ctx_options);
+
+ ftp->ssl_handle = SSL_new(ctx);
+ if (ftp->ssl_handle == NULL) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create the SSL handle");
+ SSL_CTX_free(ctx);
+ return 0;
+ }
+
+ SSL_set_fd(ftp->ssl_handle, ftp->fd);
+
+ if (SSL_connect(ftp->ssl_handle) <= 0) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL/TLS handshake failed");
+ SSL_shutdown(ftp->ssl_handle);
+ return 0;
+ }
+
+ ftp->ssl_active = 1;
+
+ if (!ftp->old_ssl) {
+
+ /* set protection buffersize to zero */
+ if (!ftp_putcmd(ftp, "PBSZ", "0")) {
+ return 0;
+ }
+ if (!ftp_getresp(ftp)) {
+ return 0;
+ }
+
+ /* enable data conn encryption */
+ if (!ftp_putcmd(ftp, "PROT", "P")) {
+ return 0;
+ }
+ if (!ftp_getresp(ftp)) {
+ return 0;
+ }
+
+ ftp->use_ssl_for_data = (ftp->resp >= 200 && ftp->resp <=299);
+ }
+ }
+#endif
+
+ if (!ftp_putcmd(ftp, "USER", user)) {
+ return 0;
+ }
+ if (!ftp_getresp(ftp)) {
+ return 0;
+ }
+ if (ftp->resp == 230) {
+ return 1;
+ }
+ if (ftp->resp != 331) {
+ return 0;
+ }
+ if (!ftp_putcmd(ftp, "PASS", pass)) {
+ return 0;
+ }
+ if (!ftp_getresp(ftp)) {
+ return 0;
+ }
+ return (ftp->resp == 230);
+}
+/* }}} */
+
+/* {{{ ftp_reinit
+ */
+int
+ftp_reinit(ftpbuf_t *ftp)
+{
+ if (ftp == NULL) {
+ return 0;
+ }
+
+ ftp_gc(ftp);
+
+ ftp->nb = 0;
+
+ if (!ftp_putcmd(ftp, "REIN", NULL)) {
+ return 0;
+ }
+ if (!ftp_getresp(ftp) || ftp->resp != 220) {
+ return 0;
+ }
+
+ return 1;
+}
+/* }}} */
+
+/* {{{ ftp_syst
+ */
+const char*
+ftp_syst(ftpbuf_t *ftp)
+{
+ char *syst, *end;
+
+ if (ftp == NULL) {
+ return NULL;
+ }
+
+ /* default to cached value */
+ if (ftp->syst) {
+ return ftp->syst;
+ }
+ if (!ftp_putcmd(ftp, "SYST", NULL)) {
+ return NULL;
+ }
+ if (!ftp_getresp(ftp) || ftp->resp != 215) {
+ return NULL;
+ }
+ syst = ftp->inbuf;
+ while (*syst == ' ') {
+ syst++;
+ }
+ if ((end = strchr(syst, ' '))) {
+ *end = 0;
+ }
+ ftp->syst = estrdup(syst);
+ if (end) {
+ *end = ' ';
+ }
+ return ftp->syst;
+}
+/* }}} */
+
+/* {{{ ftp_pwd
+ */
+const char*
+ftp_pwd(ftpbuf_t *ftp)
+{
+ char *pwd, *end;
+
+ if (ftp == NULL) {
+ return NULL;
+ }
+
+ /* default to cached value */
+ if (ftp->pwd) {
+ return ftp->pwd;
+ }
+ if (!ftp_putcmd(ftp, "PWD", NULL)) {
+ return NULL;
+ }
+ if (!ftp_getresp(ftp) || ftp->resp != 257) {
+ return NULL;
+ }
+ /* copy out the pwd from response */
+ if ((pwd = strchr(ftp->inbuf, '"')) == NULL) {
+ return NULL;
+ }
+ if ((end = strrchr(++pwd, '"')) == NULL) {
+ return NULL;
+ }
+ ftp->pwd = estrndup(pwd, end - pwd);
+
+ return ftp->pwd;
+}
+/* }}} */
+
+/* {{{ ftp_exec
+ */
+int
+ftp_exec(ftpbuf_t *ftp, const char *cmd)
+{
+ if (ftp == NULL) {
+ return 0;
+ }
+ if (!ftp_putcmd(ftp, "SITE EXEC", cmd)) {
+ return 0;
+ }
+ if (!ftp_getresp(ftp) || ftp->resp != 200) {
+ return 0;
+ }
+
+ return 1;
+}
+/* }}} */
+
+/* {{{ ftp_raw
+ */
+void
+ftp_raw(ftpbuf_t *ftp, const char *cmd, zval *return_value)
+{
+ if (ftp == NULL || cmd == NULL) {
+ RETURN_NULL();
+ }
+ if (!ftp_putcmd(ftp, cmd, NULL)) {
+ RETURN_NULL();
+ }
+ array_init(return_value);
+ while (ftp_readline(ftp)) {
+ add_next_index_string(return_value, ftp->inbuf, 1);
+ if (isdigit(ftp->inbuf[0]) && isdigit(ftp->inbuf[1]) && isdigit(ftp->inbuf[2]) && ftp->inbuf[3] == ' ') {
+ return;
+ }
+ }
+}
+/* }}} */
+
+/* {{{ ftp_chdir
+ */
+int
+ftp_chdir(ftpbuf_t *ftp, const char *dir)
+{
+ if (ftp == NULL) {
+ return 0;
+ }
+
+ if (ftp->pwd) {
+ efree(ftp->pwd);
+ ftp->pwd = NULL;
+ }
+
+ if (!ftp_putcmd(ftp, "CWD", dir)) {
+ return 0;
+ }
+ if (!ftp_getresp(ftp) || ftp->resp != 250) {
+ return 0;
+ }
+ return 1;
+}
+/* }}} */
+
+/* {{{ ftp_cdup
+ */
+int
+ftp_cdup(ftpbuf_t *ftp)
+{
+ if (ftp == NULL) {
+ return 0;
+ }
+
+ if (ftp->pwd) {
+ efree(ftp->pwd);
+ ftp->pwd = NULL;
+ }
+
+ if (!ftp_putcmd(ftp, "CDUP", NULL)) {
+ return 0;
+ }
+ if (!ftp_getresp(ftp) || ftp->resp != 250) {
+ return 0;
+ }
+ return 1;
+}
+/* }}} */
+
+/* {{{ ftp_mkdir
+ */
+char*
+ftp_mkdir(ftpbuf_t *ftp, const char *dir)
+{
+ char *mkd, *end;
+
+ if (ftp == NULL) {
+ return NULL;
+ }
+ if (!ftp_putcmd(ftp, "MKD", dir)) {
+ return NULL;
+ }
+ if (!ftp_getresp(ftp) || ftp->resp != 257) {
+ return NULL;
+ }
+ /* copy out the dir from response */
+ if ((mkd = strchr(ftp->inbuf, '"')) == NULL) {
+ mkd = estrdup(dir);
+ return mkd;
+ }
+ if ((end = strrchr(++mkd, '"')) == NULL) {
+ return NULL;
+ }
+ *end = 0;
+ mkd = estrdup(mkd);
+ *end = '"';
+
+ return mkd;
+}
+/* }}} */
+
+/* {{{ ftp_rmdir
+ */
+int
+ftp_rmdir(ftpbuf_t *ftp, const char *dir)
+{
+ if (ftp == NULL) {
+ return 0;
+ }
+ if (!ftp_putcmd(ftp, "RMD", dir)) {
+ return 0;
+ }
+ if (!ftp_getresp(ftp) || ftp->resp != 250) {
+ return 0;
+ }
+ return 1;
+}
+/* }}} */
+
+/* {{{ ftp_chmod
+ */
+int
+ftp_chmod(ftpbuf_t *ftp, const int mode, const char *filename, const int filename_len)
+{
+ char *buffer;
+
+ if (ftp == NULL || filename_len <= 0) {
+ return 0;
+ }
+
+ spprintf(&buffer, 0, "CHMOD %o %s", mode, filename);
+
+ if (!ftp_putcmd(ftp, "SITE", buffer)) {
+ efree(buffer);
+ return 0;
+ }
+
+ efree(buffer);
+
+ if (!ftp_getresp(ftp) || ftp->resp != 200) {
+ return 0;
+ }
+
+ return 1;
+}
+/* }}} */
+
+/* {{{ ftp_alloc
+ */
+int
+ftp_alloc(ftpbuf_t *ftp, const int size, char **response)
+{
+ char buffer[64];
+
+ if (ftp == NULL || size <= 0) {
+ return 0;
+ }
+
+ snprintf(buffer, sizeof(buffer) - 1, "%d", size);
+
+ if (!ftp_putcmd(ftp, "ALLO", buffer)) {
+ return 0;
+ }
+
+ if (!ftp_getresp(ftp)) {
+ return 0;
+ }
+
+ if (response && ftp->inbuf) {
+ *response = estrdup(ftp->inbuf);
+ }
+
+ if (ftp->resp < 200 || ftp->resp >= 300) {
+ return 0;
+ }
+
+ return 1;
+}
+/* }}} */
+
+/* {{{ ftp_nlist
+ */
+char**
+ftp_nlist(ftpbuf_t *ftp, const char *path TSRMLS_DC)
+{
+ return ftp_genlist(ftp, "NLST", path TSRMLS_CC);
+}
+/* }}} */
+
+/* {{{ ftp_list
+ */
+char**
+ftp_list(ftpbuf_t *ftp, const char *path, int recursive TSRMLS_DC)
+{
+ return ftp_genlist(ftp, ((recursive) ? "LIST -R" : "LIST"), path TSRMLS_CC);
+}
+/* }}} */
+
+/* {{{ ftp_type
+ */
+int
+ftp_type(ftpbuf_t *ftp, ftptype_t type)
+{
+ char typechar[2] = "?";
+
+ if (ftp == NULL) {
+ return 0;
+ }
+ if (type == ftp->type) {
+ return 1;
+ }
+ if (type == FTPTYPE_ASCII) {
+ typechar[0] = 'A';
+ } else if (type == FTPTYPE_IMAGE) {
+ typechar[0] = 'I';
+ } else {
+ return 0;
+ }
+ if (!ftp_putcmd(ftp, "TYPE", typechar)) {
+ return 0;
+ }
+ if (!ftp_getresp(ftp) || ftp->resp != 200) {
+ return 0;
+ }
+ ftp->type = type;
+
+ return 1;
+}
+/* }}} */
+
+/* {{{ ftp_pasv
+ */
+int
+ftp_pasv(ftpbuf_t *ftp, int pasv)
+{
+ char *ptr;
+ union ipbox ipbox;
+ unsigned long b[6];
+ socklen_t n;
+ struct sockaddr *sa;
+ struct sockaddr_in *sin;
+
+ if (ftp == NULL) {
+ return 0;
+ }
+ if (pasv && ftp->pasv == 2) {
+ return 1;
+ }
+ ftp->pasv = 0;
+ if (!pasv) {
+ return 1;
+ }
+ n = sizeof(ftp->pasvaddr);
+ memset(&ftp->pasvaddr, 0, n);
+ sa = (struct sockaddr *) &ftp->pasvaddr;
+
+#if HAVE_IPV6
+ if (getpeername(ftp->fd, sa, &n) < 0) {
+ return 0;
+ }
+ if (sa->sa_family == AF_INET6) {
+ struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sa;
+ char *endptr, delimiter;
+
+ /* try EPSV first */
+ if (!ftp_putcmd(ftp, "EPSV", NULL)) {
+ return 0;
+ }
+ if (!ftp_getresp(ftp)) {
+ return 0;
+ }
+ if (ftp->resp == 229) {
+ /* parse out the port */
+ for (ptr = ftp->inbuf; *ptr && *ptr != '('; ptr++);
+ if (!*ptr) {
+ return 0;
+ }
+ delimiter = *++ptr;
+ for (n = 0; *ptr && n < 3; ptr++) {
+ if (*ptr == delimiter) {
+ n++;
+ }
+ }
+
+ sin6->sin6_port = htons((unsigned short) strtoul(ptr, &endptr, 10));
+ if (ptr == endptr || *endptr != delimiter) {
+ return 0;
+ }
+ ftp->pasv = 2;
+ return 1;
+ }
+ }
+
+ /* fall back to PASV */
+#endif
+
+ if (!ftp_putcmd(ftp, "PASV", NULL)) {
+ return 0;
+ }
+ if (!ftp_getresp(ftp) || ftp->resp != 227) {
+ return 0;
+ }
+ /* parse out the IP and port */
+ for (ptr = ftp->inbuf; *ptr && !isdigit(*ptr); ptr++);
+ n = sscanf(ptr, "%lu,%lu,%lu,%lu,%lu,%lu", &b[0], &b[1], &b[2], &b[3], &b[4], &b[5]);
+ if (n != 6) {
+ return 0;
+ }
+ for (n = 0; n < 6; n++) {
+ ipbox.c[n] = (unsigned char) b[n];
+ }
+ sin = (struct sockaddr_in *) sa;
+ sin->sin_family = AF_INET;
+ sin->sin_addr = ipbox.ia[0];
+ sin->sin_port = ipbox.s[2];
+
+ ftp->pasv = 2;
+
+ return 1;
+}
+/* }}} */
+
+/* {{{ ftp_get
+ */
+int
+ftp_get(ftpbuf_t *ftp, php_stream *outstream, const char *path, ftptype_t type, int resumepos TSRMLS_DC)
+{
+ databuf_t *data = NULL;
+ int lastch;
+ size_t rcvd;
+ char arg[11];
+
+ if (ftp == NULL) {
+ return 0;
+ }
+ if (!ftp_type(ftp, type)) {
+ goto bail;
+ }
+
+ if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) {
+ goto bail;
+ }
+
+ ftp->data = data;
+
+ if (resumepos > 0) {
+ if (resumepos > 2147483647) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "PHP cannot handle files greater than 2147483647 bytes.");
+ goto bail;
+ }
+ snprintf(arg, sizeof(arg), "%u", resumepos);
+ if (!ftp_putcmd(ftp, "REST", arg)) {
+ goto bail;
+ }
+ if (!ftp_getresp(ftp) || (ftp->resp != 350)) {
+ goto bail;
+ }
+ }
+
+ if (!ftp_putcmd(ftp, "RETR", path)) {
+ goto bail;
+ }
+ if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
+ goto bail;
+ }
+
+ if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) {
+ goto bail;
+ }
+
+ lastch = 0;
+ while ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) {
+ if (rcvd == -1) {
+ goto bail;
+ }
+
+ if (type == FTPTYPE_ASCII) {
+#ifndef PHP_WIN32
+ char *s;
+#endif
+ char *ptr = data->buf;
+ char *e = ptr + rcvd;
+ /* logic depends on the OS EOL
+ * Win32 -> \r\n
+ * Everything Else \n
+ */
+#ifdef PHP_WIN32
+ php_stream_write(outstream, ptr, (e - ptr));
+ ptr = e;
+#else
+ while (e > ptr && (s = memchr(ptr, '\r', (e - ptr)))) {
+ php_stream_write(outstream, ptr, (s - ptr));
+ if (*(s + 1) == '\n') {
+ s++;
+ php_stream_putc(outstream, '\n');
+ }
+ ptr = s + 1;
+ }
+#endif
+ if (ptr < e) {
+ php_stream_write(outstream, ptr, (e - ptr));
+ }
+ } else if (rcvd != php_stream_write(outstream, data->buf, rcvd)) {
+ goto bail;
+ }
+ }
+
+ ftp->data = data = data_close(ftp, data);
+
+ if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
+ goto bail;
+ }
+
+ return 1;
+bail:
+ ftp->data = data_close(ftp, data);
+ return 0;
+}
+/* }}} */
+
+/* {{{ ftp_put
+ */
+int
+ftp_put(ftpbuf_t *ftp, const char *path, php_stream *instream, ftptype_t type, int startpos TSRMLS_DC)
+{
+ databuf_t *data = NULL;
+ int size;
+ char *ptr;
+ int ch;
+ char arg[11];
+
+ if (ftp == NULL) {
+ return 0;
+ }
+ if (!ftp_type(ftp, type)) {
+ goto bail;
+ }
+ if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) {
+ goto bail;
+ }
+ ftp->data = data;
+
+ if (startpos > 0) {
+ if (startpos > 2147483647) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "PHP cannot handle files with a size greater than 2147483647 bytes.");
+ goto bail;
+ }
+ snprintf(arg, sizeof(arg), "%u", startpos);
+ if (!ftp_putcmd(ftp, "REST", arg)) {
+ goto bail;
+ }
+ if (!ftp_getresp(ftp) || (ftp->resp != 350)) {
+ goto bail;
+ }
+ }
+
+ if (!ftp_putcmd(ftp, "STOR", path)) {
+ goto bail;
+ }
+ if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
+ goto bail;
+ }
+ if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) {
+ goto bail;
+ }
+
+ size = 0;
+ ptr = data->buf;
+ while (!php_stream_eof(instream) && (ch = php_stream_getc(instream))!=EOF) {
+ /* flush if necessary */
+ if (FTP_BUFSIZE - size < 2) {
+ if (my_send(ftp, data->fd, data->buf, size) != size) {
+ goto bail;
+ }
+ ptr = data->buf;
+ size = 0;
+ }
+
+ if (ch == '\n' && type == FTPTYPE_ASCII) {
+ *ptr++ = '\r';
+ size++;
+ }
+
+ *ptr++ = ch;
+ size++;
+ }
+
+ if (size && my_send(ftp, data->fd, data->buf, size) != size) {
+ goto bail;
+ }
+ ftp->data = data = data_close(ftp, data);
+
+ if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250 && ftp->resp != 200)) {
+ goto bail;
+ }
+ return 1;
+bail:
+ ftp->data = data_close(ftp, data);
+ return 0;
+}
+/* }}} */
+
+/* {{{ ftp_size
+ */
+int
+ftp_size(ftpbuf_t *ftp, const char *path)
+{
+ if (ftp == NULL) {
+ return -1;
+ }
+ if (!ftp_type(ftp, FTPTYPE_IMAGE)) {
+ return -1;
+ }
+ if (!ftp_putcmd(ftp, "SIZE", path)) {
+ return -1;
+ }
+ if (!ftp_getresp(ftp) || ftp->resp != 213) {
+ return -1;
+ }
+ return atoi(ftp->inbuf);
+}
+/* }}} */
+
+/* {{{ ftp_mdtm
+ */
+time_t
+ftp_mdtm(ftpbuf_t *ftp, const char *path)
+{
+ time_t stamp;
+ struct tm *gmt, tmbuf;
+ struct tm tm;
+ char *ptr;
+ int n;
+
+ if (ftp == NULL) {
+ return -1;
+ }
+ if (!ftp_putcmd(ftp, "MDTM", path)) {
+ return -1;
+ }
+ if (!ftp_getresp(ftp) || ftp->resp != 213) {
+ return -1;
+ }
+ /* parse out the timestamp */
+ for (ptr = ftp->inbuf; *ptr && !isdigit(*ptr); ptr++);
+ n = sscanf(ptr, "%4u%2u%2u%2u%2u%2u", &tm.tm_year, &tm.tm_mon, &tm.tm_mday, &tm.tm_hour, &tm.tm_min, &tm.tm_sec);
+ if (n != 6) {
+ return -1;
+ }
+ tm.tm_year -= 1900;
+ tm.tm_mon--;
+ tm.tm_isdst = -1;
+
+ /* figure out the GMT offset */
+ stamp = time(NULL);
+ gmt = php_gmtime_r(&stamp, &tmbuf);
+ if (!gmt) {
+ return -1;
+ }
+ gmt->tm_isdst = -1;
+
+ /* apply the GMT offset */
+ tm.tm_sec += stamp - mktime(gmt);
+ tm.tm_isdst = gmt->tm_isdst;
+
+ stamp = mktime(&tm);
+
+ return stamp;
+}
+/* }}} */
+
+/* {{{ ftp_delete
+ */
+int
+ftp_delete(ftpbuf_t *ftp, const char *path)
+{
+ if (ftp == NULL) {
+ return 0;
+ }
+ if (!ftp_putcmd(ftp, "DELE", path)) {
+ return 0;
+ }
+ if (!ftp_getresp(ftp) || ftp->resp != 250) {
+ return 0;
+ }
+
+ return 1;
+}
+/* }}} */
+
+/* {{{ ftp_rename
+ */
+int
+ftp_rename(ftpbuf_t *ftp, const char *src, const char *dest)
+{
+ if (ftp == NULL) {
+ return 0;
+ }
+ if (!ftp_putcmd(ftp, "RNFR", src)) {
+ return 0;
+ }
+ if (!ftp_getresp(ftp) || ftp->resp != 350) {
+ return 0;
+ }
+ if (!ftp_putcmd(ftp, "RNTO", dest)) {
+ return 0;
+ }
+ if (!ftp_getresp(ftp) || ftp->resp != 250) {
+ return 0;
+ }
+ return 1;
+}
+/* }}} */
+
+/* {{{ ftp_site
+ */
+int
+ftp_site(ftpbuf_t *ftp, const char *cmd)
+{
+ if (ftp == NULL) {
+ return 0;
+ }
+ if (!ftp_putcmd(ftp, "SITE", cmd)) {
+ return 0;
+ }
+ if (!ftp_getresp(ftp) || ftp->resp < 200 || ftp->resp >= 300) {
+ return 0;
+ }
+
+ return 1;
+}
+/* }}} */
+
+/* static functions */
+
+/* {{{ ftp_putcmd
+ */
+int
+ftp_putcmd(ftpbuf_t *ftp, const char *cmd, const char *args)
+{
+ int size;
+ char *data;
+
+ if (strpbrk(cmd, "\r\n")) {
+ return 0;
+ }
+ /* build the output buffer */
+ if (args && args[0]) {
+ /* "cmd args\r\n\0" */
+ if (strlen(cmd) + strlen(args) + 4 > FTP_BUFSIZE) {
+ return 0;
+ }
+ if (strpbrk(args, "\r\n")) {
+ return 0;
+ }
+ size = slprintf(ftp->outbuf, sizeof(ftp->outbuf), "%s %s\r\n", cmd, args);
+ } else {
+ /* "cmd\r\n\0" */
+ if (strlen(cmd) + 3 > FTP_BUFSIZE) {
+ return 0;
+ }
+ size = slprintf(ftp->outbuf, sizeof(ftp->outbuf), "%s\r\n", cmd);
+ }
+
+ data = ftp->outbuf;
+
+ /* Clear the extra-lines buffer */
+ ftp->extra = NULL;
+
+ if (my_send(ftp, ftp->fd, data, size) != size) {
+ return 0;
+ }
+ return 1;
+}
+/* }}} */
+
+/* {{{ ftp_readline
+ */
+int
+ftp_readline(ftpbuf_t *ftp)
+{
+ int size, rcvd;
+ char *data, *eol;
+
+ /* shift the extra to the front */
+ size = FTP_BUFSIZE;
+ rcvd = 0;
+ if (ftp->extra) {
+ memmove(ftp->inbuf, ftp->extra, ftp->extralen);
+ rcvd = ftp->extralen;
+ }
+
+ data = ftp->inbuf;
+
+ do {
+ size -= rcvd;
+ for (eol = data; rcvd; rcvd--, eol++) {
+ if (*eol == '\r') {
+ *eol = 0;
+ ftp->extra = eol + 1;
+ if (rcvd > 1 && *(eol + 1) == '\n') {
+ ftp->extra++;
+ rcvd--;
+ }
+ if ((ftp->extralen = --rcvd) == 0) {
+ ftp->extra = NULL;
+ }
+ return 1;
+ } else if (*eol == '\n') {
+ *eol = 0;
+ ftp->extra = eol + 1;
+ if ((ftp->extralen = --rcvd) == 0) {
+ ftp->extra = NULL;
+ }
+ return 1;
+ }
+ }
+
+ data = eol;
+ if ((rcvd = my_recv(ftp, ftp->fd, data, size)) < 1) {
+ return 0;
+ }
+ } while (size);
+
+ return 0;
+}
+/* }}} */
+
+/* {{{ ftp_getresp
+ */
+int
+ftp_getresp(ftpbuf_t *ftp)
+{
+ char *buf;
+
+ if (ftp == NULL) {
+ return 0;
+ }
+ buf = ftp->inbuf;
+ ftp->resp = 0;
+
+ while (1) {
+
+ if (!ftp_readline(ftp)) {
+ return 0;
+ }
+
+ /* Break out when the end-tag is found */
+ if (isdigit(ftp->inbuf[0]) && isdigit(ftp->inbuf[1]) && isdigit(ftp->inbuf[2]) && ftp->inbuf[3] == ' ') {
+ break;
+ }
+ }
+
+ /* translate the tag */
+ if (!isdigit(ftp->inbuf[0]) || !isdigit(ftp->inbuf[1]) || !isdigit(ftp->inbuf[2])) {
+ return 0;
+ }
+
+ ftp->resp = 100 * (ftp->inbuf[0] - '0') + 10 * (ftp->inbuf[1] - '0') + (ftp->inbuf[2] - '0');
+
+ memmove(ftp->inbuf, ftp->inbuf + 4, FTP_BUFSIZE - 4);
+
+ if (ftp->extra) {
+ ftp->extra -= 4;
+ }
+ return 1;
+}
+/* }}} */
+
+/* {{{ my_send
+ */
+int
+my_send(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len)
+{
+ int n, size, sent;
+
+ size = len;
+ while (size) {
+ n = php_pollfd_for_ms(s, POLLOUT, ftp->timeout_sec * 1000);
+
+ if (n < 1) {
+
+#if !defined(PHP_WIN32) && !(defined(NETWARE) && defined(USE_WINSOCK))
+ if (n == 0) {
+ errno = ETIMEDOUT;
+ }
+#endif
+ return -1;
+ }
+
+#if HAVE_OPENSSL_EXT
+ if (ftp->use_ssl && ftp->fd == s && ftp->ssl_active) {
+ sent = SSL_write(ftp->ssl_handle, buf, size);
+ } else if (ftp->use_ssl && ftp->fd != s && ftp->use_ssl_for_data && ftp->data->ssl_active) {
+ sent = SSL_write(ftp->data->ssl_handle, buf, size);
+ } else {
+#endif
+ sent = send(s, buf, size, 0);
+#if HAVE_OPENSSL_EXT
+ }
+#endif
+ if (sent == -1) {
+ return -1;
+ }
+
+ buf = (char*) buf + sent;
+ size -= sent;
+ }
+
+ return len;
+}
+/* }}} */
+
+/* {{{ my_recv
+ */
+int
+my_recv(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len)
+{
+ int n, nr_bytes;
+
+ n = php_pollfd_for_ms(s, PHP_POLLREADABLE, ftp->timeout_sec * 1000);
+ if (n < 1) {
+#if !defined(PHP_WIN32) && !(defined(NETWARE) && defined(USE_WINSOCK))
+ if (n == 0) {
+ errno = ETIMEDOUT;
+ }
+#endif
+ return -1;
+ }
+
+#if HAVE_OPENSSL_EXT
+ if (ftp->use_ssl && ftp->fd == s && ftp->ssl_active) {
+ nr_bytes = SSL_read(ftp->ssl_handle, buf, len);
+ } else if (ftp->use_ssl && ftp->fd != s && ftp->use_ssl_for_data && ftp->data->ssl_active) {
+ nr_bytes = SSL_read(ftp->data->ssl_handle, buf, len);
+ } else {
+#endif
+ nr_bytes = recv(s, buf, len, 0);
+#if HAVE_OPENSSL_EXT
+ }
+#endif
+ return (nr_bytes);
+}
+/* }}} */
+
+/* {{{ data_available
+ */
+int
+data_available(ftpbuf_t *ftp, php_socket_t s)
+{
+ int n;
+
+ n = php_pollfd_for_ms(s, PHP_POLLREADABLE, 1000);
+ if (n < 1) {
+#if !defined(PHP_WIN32) && !(defined(NETWARE) && defined(USE_WINSOCK))
+ if (n == 0) {
+ errno = ETIMEDOUT;
+ }
+#endif
+ return 0;
+ }
+
+ return 1;
+}
+/* }}} */
+/* {{{ data_writeable
+ */
+int
+data_writeable(ftpbuf_t *ftp, php_socket_t s)
+{
+ int n;
+
+ n = php_pollfd_for_ms(s, POLLOUT, 1000);
+ if (n < 1) {
+#ifndef PHP_WIN32
+ if (n == 0) {
+ errno = ETIMEDOUT;
+ }
+#endif
+ return 0;
+ }
+
+ return 1;
+}
+/* }}} */
+
+/* {{{ my_accept
+ */
+int
+my_accept(ftpbuf_t *ftp, php_socket_t s, struct sockaddr *addr, socklen_t *addrlen)
+{
+ int n;
+
+ n = php_pollfd_for_ms(s, PHP_POLLREADABLE, ftp->timeout_sec * 1000);
+ if (n < 1) {
+#if !defined(PHP_WIN32) && !(defined(NETWARE) && defined(USE_WINSOCK))
+ if (n == 0) {
+ errno = ETIMEDOUT;
+ }
+#endif
+ return -1;
+ }
+
+ return accept(s, addr, addrlen);
+}
+/* }}} */
+
+/* {{{ ftp_getdata
+ */
+databuf_t*
+ftp_getdata(ftpbuf_t *ftp TSRMLS_DC)
+{
+ int fd = -1;
+ databuf_t *data;
+ php_sockaddr_storage addr;
+ struct sockaddr *sa;
+ socklen_t size;
+ union ipbox ipbox;
+ char arg[sizeof("255, 255, 255, 255, 255, 255")];
+ struct timeval tv;
+
+
+ /* ask for a passive connection if we need one */
+ if (ftp->pasv && !ftp_pasv(ftp, 1)) {
+ return NULL;
+ }
+ /* alloc the data structure */
+ data = ecalloc(1, sizeof(*data));
+ data->listener = -1;
+ data->fd = -1;
+ data->type = ftp->type;
+
+ sa = (struct sockaddr *) &ftp->localaddr;
+ /* bind/listen */
+ if ((fd = socket(sa->sa_family, SOCK_STREAM, 0)) == SOCK_ERR) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "socket() failed: %s (%d)", strerror(errno), errno);
+ goto bail;
+ }
+
+ /* passive connection handler */
+ if (ftp->pasv) {
+ /* clear the ready status */
+ ftp->pasv = 1;
+
+ /* connect */
+ /* Win 95/98 seems not to like size > sizeof(sockaddr_in) */
+ size = php_sockaddr_size(&ftp->pasvaddr);
+ tv.tv_sec = ftp->timeout_sec;
+ tv.tv_usec = 0;
+ if (php_connect_nonb(fd, (struct sockaddr*) &ftp->pasvaddr, size, &tv) == -1) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_connect_nonb() failed: %s (%d)", strerror(errno), errno);
+ goto bail;
+ }
+
+ data->fd = fd;
+
+ ftp->data = data;
+ return data;
+ }
+
+
+ /* active (normal) connection */
+
+ /* bind to a local address */
+ php_any_addr(sa->sa_family, &addr, 0);
+ size = php_sockaddr_size(&addr);
+
+ if (bind(fd, (struct sockaddr*) &addr, size) != 0) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "bind() failed: %s (%d)", strerror(errno), errno);
+ goto bail;
+ }
+
+ if (getsockname(fd, (struct sockaddr*) &addr, &size) != 0) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "getsockname() failed: %s (%d)", strerror(errno), errno);
+ goto bail;
+ }
+
+ if (listen(fd, 5) != 0) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "listen() failed: %s (%d)", strerror(errno), errno);
+ goto bail;
+ }
+
+ data->listener = fd;
+
+#if HAVE_IPV6 && HAVE_INET_NTOP
+ if (sa->sa_family == AF_INET6) {
+ /* need to use EPRT */
+ char eprtarg[INET6_ADDRSTRLEN + sizeof("|x||xxxxx|")];
+ char out[INET6_ADDRSTRLEN];
+ inet_ntop(AF_INET6, &((struct sockaddr_in6*) sa)->sin6_addr, out, sizeof(out));
+ snprintf(eprtarg, sizeof(eprtarg), "|2|%s|%hu|", out, ntohs(((struct sockaddr_in6 *) &addr)->sin6_port));
+
+ if (!ftp_putcmd(ftp, "EPRT", eprtarg)) {
+ goto bail;
+ }
+
+ if (!ftp_getresp(ftp) || ftp->resp != 200) {
+ goto bail;
+ }
+
+ ftp->data = data;
+ return data;
+ }
+#endif
+
+ /* send the PORT */
+ ipbox.ia[0] = ((struct sockaddr_in*) sa)->sin_addr;
+ ipbox.s[2] = ((struct sockaddr_in*) &addr)->sin_port;
+ snprintf(arg, sizeof(arg), "%u,%u,%u,%u,%u,%u", ipbox.c[0], ipbox.c[1], ipbox.c[2], ipbox.c[3], ipbox.c[4], ipbox.c[5]);
+
+ if (!ftp_putcmd(ftp, "PORT", arg)) {
+ goto bail;
+ }
+ if (!ftp_getresp(ftp) || ftp->resp != 200) {
+ goto bail;
+ }
+
+ ftp->data = data;
+ return data;
+
+bail:
+ if (fd != -1) {
+ closesocket(fd);
+ }
+ efree(data);
+ return NULL;
+}
+/* }}} */
+
+/* {{{ data_accept
+ */
+databuf_t*
+data_accept(databuf_t *data, ftpbuf_t *ftp TSRMLS_DC)
+{
+ php_sockaddr_storage addr;
+ socklen_t size;
+
+#if HAVE_OPENSSL_EXT
+ SSL_CTX *ctx;
+ long ssl_ctx_options = SSL_OP_ALL;
+#endif
+
+ if (data->fd != -1) {
+ goto data_accepted;
+ }
+ size = sizeof(addr);
+ data->fd = my_accept(ftp, data->listener, (struct sockaddr*) &addr, &size);
+ closesocket(data->listener);
+ data->listener = -1;
+
+ if (data->fd == -1) {
+ efree(data);
+ return NULL;
+ }
+
+data_accepted:
+#if HAVE_OPENSSL_EXT
+
+ /* now enable ssl if we need to */
+ if (ftp->use_ssl && ftp->use_ssl_for_data) {
+ ctx = SSL_CTX_new(SSLv23_client_method());
+ if (ctx == NULL) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_accept: failed to create the SSL context");
+ return 0;
+ }
+
+#if OPENSSL_VERSION_NUMBER >= 0x0090605fL
+ ssl_ctx_options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
+#endif
+ SSL_CTX_set_options(ctx, ssl_ctx_options);
+
+ data->ssl_handle = SSL_new(ctx);
+ if (data->ssl_handle == NULL) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_accept: failed to create the SSL handle");
+ SSL_CTX_free(ctx);
+ return 0;
+ }
+
+
+ SSL_set_fd(data->ssl_handle, data->fd);
+
+ if (ftp->old_ssl) {
+ SSL_copy_session_id(data->ssl_handle, ftp->ssl_handle);
+ }
+
+ if (SSL_connect(data->ssl_handle) <= 0) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_accept: SSL/TLS handshake failed");
+ SSL_shutdown(data->ssl_handle);
+ return 0;
+ }
+
+ data->ssl_active = 1;
+ }
+
+#endif
+
+ return data;
+}
+/* }}} */
+
+/* {{{ data_close
+ */
+databuf_t*
+data_close(ftpbuf_t *ftp, databuf_t *data)
+{
+ if (data == NULL) {
+ return NULL;
+ }
+ if (data->listener != -1) {
+#if HAVE_OPENSSL_EXT
+ if (data->ssl_active) {
+ SSL_shutdown(data->ssl_handle);
+ data->ssl_active = 0;
+ }
+#endif
+ closesocket(data->listener);
+ }
+ if (data->fd != -1) {
+#if HAVE_OPENSSL_EXT
+ if (data->ssl_active) {
+ SSL_shutdown(data->ssl_handle);
+ data->ssl_active = 0;
+ }
+#endif
+ closesocket(data->fd);
+ }
+ if (ftp) {
+ ftp->data = NULL;
+ }
+ efree(data);
+ return NULL;
+}
+/* }}} */
+
+/* {{{ ftp_genlist
+ */
+char**
+ftp_genlist(ftpbuf_t *ftp, const char *cmd, const char *path TSRMLS_DC)
+{
+ php_stream *tmpstream = NULL;
+ databuf_t *data = NULL;
+ char *ptr;
+ int ch, lastch;
+ int size, rcvd;
+ int lines;
+ char **ret = NULL;
+ char **entry;
+ char *text;
+
+
+ if ((tmpstream = php_stream_fopen_tmpfile()) == NULL) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to create temporary file. Check permissions in temporary files directory.");
+ return NULL;
+ }
+
+ if (!ftp_type(ftp, FTPTYPE_ASCII)) {
+ goto bail;
+ }
+
+ if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) {
+ goto bail;
+ }
+ ftp->data = data;
+
+ if (!ftp_putcmd(ftp, cmd, path)) {
+ goto bail;
+ }
+ if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125 && ftp->resp != 226)) {
+ goto bail;
+ }
+
+ /* some servers don't open a ftp-data connection if the directory is empty */
+ if (ftp->resp == 226) {
+ ftp->data = data_close(ftp, data);
+ php_stream_close(tmpstream);
+ return ecalloc(1, sizeof(char**));
+ }
+
+ /* pull data buffer into tmpfile */
+ if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) {
+ goto bail;
+ }
+ size = 0;
+ lines = 0;
+ lastch = 0;
+ while ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) {
+ if (rcvd == -1) {
+ goto bail;
+ }
+
+ php_stream_write(tmpstream, data->buf, rcvd);
+
+ size += rcvd;
+ for (ptr = data->buf; rcvd; rcvd--, ptr++) {
+ if (*ptr == '\n' && lastch == '\r') {
+ lines++;
+ } else {
+ size++;
+ }
+ lastch = *ptr;
+ }
+ }
+
+ ftp->data = data = data_close(ftp, data);
+
+ php_stream_rewind(tmpstream);
+
+ ret = safe_emalloc((lines + 1), sizeof(char**), size * sizeof(char*));
+
+ entry = ret;
+ text = (char*) (ret + lines + 1);
+ *entry = text;
+ lastch = 0;
+ while ((ch = php_stream_getc(tmpstream)) != EOF) {
+ if (ch == '\n' && lastch == '\r') {
+ *(text - 1) = 0;
+ *++entry = text;
+ } else {
+ *text++ = ch;
+ }
+ lastch = ch;
+ }
+ *entry = NULL;
+
+ php_stream_close(tmpstream);
+
+ if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
+ efree(ret);
+ return NULL;
+ }
+
+ return ret;
+bail:
+ ftp->data = data_close(ftp, data);
+ php_stream_close(tmpstream);
+ if (ret)
+ efree(ret);
+ return NULL;
+}
+/* }}} */
+
+/* {{{ ftp_nb_get
+ */
+int
+ftp_nb_get(ftpbuf_t *ftp, php_stream *outstream, const char *path, ftptype_t type, int resumepos TSRMLS_DC)
+{
+ databuf_t *data = NULL;
+ char arg[11];
+
+ if (ftp == NULL) {
+ return PHP_FTP_FAILED;
+ }
+
+ if (!ftp_type(ftp, type)) {
+ goto bail;
+ }
+
+ if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) {
+ goto bail;
+ }
+
+ if (resumepos>0) {
+ /* We are working on an architecture that supports 64-bit integers
+ * since php is 32 bit by design, we bail out with warning
+ */
+ if (resumepos > 2147483647) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "PHP cannot handle files greater than 2147483648 bytes.");
+ goto bail;
+ }
+ snprintf(arg, sizeof(arg), "%u", resumepos);
+ if (!ftp_putcmd(ftp, "REST", arg)) {
+ goto bail;
+ }
+ if (!ftp_getresp(ftp) || (ftp->resp != 350)) {
+ goto bail;
+ }
+ }
+
+ if (!ftp_putcmd(ftp, "RETR", path)) {
+ goto bail;
+ }
+ if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
+ goto bail;
+ }
+
+ if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) {
+ goto bail;
+ }
+
+ ftp->data = data;
+ ftp->stream = outstream;
+ ftp->lastch = 0;
+ ftp->nb = 1;
+
+ return (ftp_nb_continue_read(ftp TSRMLS_CC));
+
+bail:
+ ftp->data = data_close(ftp, data);
+ return PHP_FTP_FAILED;
+}
+/* }}} */
+
+/* {{{ ftp_nb_continue_read
+ */
+int
+ftp_nb_continue_read(ftpbuf_t *ftp TSRMLS_DC)
+{
+ databuf_t *data = NULL;
+ char *ptr;
+ int lastch;
+ size_t rcvd;
+ ftptype_t type;
+
+ data = ftp->data;
+
+ /* check if there is already more data */
+ if (!data_available(ftp, data->fd)) {
+ return PHP_FTP_MOREDATA;
+ }
+
+ type = ftp->type;
+
+ lastch = ftp->lastch;
+ if ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) {
+ if (rcvd == -1) {
+ goto bail;
+ }
+
+ if (type == FTPTYPE_ASCII) {
+ for (ptr = data->buf; rcvd; rcvd--, ptr++) {
+ if (lastch == '\r' && *ptr != '\n') {
+ php_stream_putc(ftp->stream, '\r');
+ }
+ if (*ptr != '\r') {
+ php_stream_putc(ftp->stream, *ptr);
+ }
+ lastch = *ptr;
+ }
+ } else if (rcvd != php_stream_write(ftp->stream, data->buf, rcvd)) {
+ goto bail;
+ }
+
+ ftp->lastch = lastch;
+ return PHP_FTP_MOREDATA;
+ }
+
+ if (type == FTPTYPE_ASCII && lastch == '\r') {
+ php_stream_putc(ftp->stream, '\r');
+ }
+
+ ftp->data = data = data_close(ftp, data);
+
+ if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
+ goto bail;
+ }
+
+ ftp->nb = 0;
+ return PHP_FTP_FINISHED;
+bail:
+ ftp->nb = 0;
+ ftp->data = data_close(ftp, data);
+ return PHP_FTP_FAILED;
+}
+/* }}} */
+
+/* {{{ ftp_nb_put
+ */
+int
+ftp_nb_put(ftpbuf_t *ftp, const char *path, php_stream *instream, ftptype_t type, int startpos TSRMLS_DC)
+{
+ databuf_t *data = NULL;
+ char arg[11];
+
+ if (ftp == NULL) {
+ return 0;
+ }
+ if (!ftp_type(ftp, type)) {
+ goto bail;
+ }
+ if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) {
+ goto bail;
+ }
+ if (startpos > 0) {
+ if (startpos > 2147483647) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "PHP cannot handle files with a size greater than 2147483647 bytes.");
+ goto bail;
+ }
+ snprintf(arg, sizeof(arg), "%u", startpos);
+ if (!ftp_putcmd(ftp, "REST", arg)) {
+ goto bail;
+ }
+ if (!ftp_getresp(ftp) || (ftp->resp != 350)) {
+ goto bail;
+ }
+ }
+
+ if (!ftp_putcmd(ftp, "STOR", path)) {
+ goto bail;
+ }
+ if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
+ goto bail;
+ }
+ if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) {
+ goto bail;
+ }
+ ftp->data = data;
+ ftp->stream = instream;
+ ftp->lastch = 0;
+ ftp->nb = 1;
+
+ return (ftp_nb_continue_write(ftp TSRMLS_CC));
+
+bail:
+ ftp->data = data_close(ftp, data);
+ return PHP_FTP_FAILED;
+}
+/* }}} */
+
+
+/* {{{ ftp_nb_continue_write
+ */
+int
+ftp_nb_continue_write(ftpbuf_t *ftp TSRMLS_DC)
+{
+ int size;
+ char *ptr;
+ int ch;
+
+ /* check if we can write more data */
+ if (!data_writeable(ftp, ftp->data->fd)) {
+ return PHP_FTP_MOREDATA;
+ }
+
+ size = 0;
+ ptr = ftp->data->buf;
+ while (!php_stream_eof(ftp->stream) && (ch = php_stream_getc(ftp->stream)) != EOF) {
+
+ if (ch == '\n' && ftp->type == FTPTYPE_ASCII) {
+ *ptr++ = '\r';
+ size++;
+ }
+
+ *ptr++ = ch;
+ size++;
+
+ /* flush if necessary */
+ if (FTP_BUFSIZE - size < 2) {
+ if (my_send(ftp, ftp->data->fd, ftp->data->buf, size) != size) {
+ goto bail;
+ }
+ return PHP_FTP_MOREDATA;
+ }
+ }
+
+ if (size && my_send(ftp, ftp->data->fd, ftp->data->buf, size) != size) {
+ goto bail;
+ }
+ ftp->data = data_close(ftp, ftp->data);
+
+ if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
+ goto bail;
+ }
+ ftp->nb = 0;
+ return PHP_FTP_FINISHED;
+bail:
+ ftp->data = data_close(ftp, ftp->data);
+ ftp->nb = 0;
+ return PHP_FTP_FAILED;
+}
+/* }}} */
+
+#endif /* HAVE_FTP */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * End:
+ * vim600: sw=4 ts=4 fdm=marker
+ * vim<600: sw=4 ts=4
+ */
diff --git a/ext/ftp/ftp.h b/ext/ftp/ftp.h
new file mode 100644
index 0000000..c7db457
--- /dev/null
+++ b/ext/ftp/ftp.h
@@ -0,0 +1,213 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andrew Skalski <askalski@chek.com> |
+ | Stefan Esser <sesser@php.net> (resume functions) |
+ +----------------------------------------------------------------------+
+ */
+
+/* $Id$ */
+
+#ifndef FTP_H
+#define FTP_H
+
+#include "php_network.h"
+
+#include <stdio.h>
+#ifdef HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+
+#define FTP_DEFAULT_TIMEOUT 90
+#define FTP_DEFAULT_AUTOSEEK 1
+#define PHP_FTP_FAILED 0
+#define PHP_FTP_FINISHED 1
+#define PHP_FTP_MOREDATA 2
+
+/* XXX this should be configurable at runtime XXX */
+#define FTP_BUFSIZE 4096
+
+typedef enum ftptype {
+ FTPTYPE_ASCII=1,
+ FTPTYPE_IMAGE
+} ftptype_t;
+
+typedef struct databuf
+{
+ int listener; /* listener socket */
+ php_socket_t fd; /* data connection */
+ ftptype_t type; /* transfer type */
+ char buf[FTP_BUFSIZE]; /* data buffer */
+#if HAVE_OPENSSL_EXT
+ SSL *ssl_handle; /* ssl handle */
+ int ssl_active; /* flag if ssl is active or not */
+#endif
+} databuf_t;
+
+typedef struct ftpbuf
+{
+ php_socket_t fd; /* control connection */
+ php_sockaddr_storage localaddr; /* local address */
+ int resp; /* last response code */
+ char inbuf[FTP_BUFSIZE]; /* last response text */
+ char *extra; /* extra characters */
+ int extralen; /* number of extra chars */
+ char outbuf[FTP_BUFSIZE]; /* command output buffer */
+ char *pwd; /* cached pwd */
+ char *syst; /* cached system type */
+ ftptype_t type; /* current transfer type */
+ int pasv; /* 0=off; 1=pasv; 2=ready */
+ php_sockaddr_storage pasvaddr; /* passive mode address */
+ long timeout_sec; /* User configureable timeout (seconds) */
+ int autoseek; /* User configureable autoseek flag */
+
+ int nb; /* "nonblocking" transfer in progress */
+ databuf_t *data; /* Data connection for "nonblocking" transfers */
+ php_stream *stream; /* output stream for "nonblocking" transfers */
+ int lastch; /* last char of previous call */
+ int direction; /* recv = 0 / send = 1 */
+ int closestream;/* close or not close stream */
+#if HAVE_OPENSSL_EXT
+ int use_ssl; /* enable(1) or disable(0) ssl */
+ int use_ssl_for_data; /* en/disable ssl for the dataconnection */
+ int old_ssl; /* old mode = forced data encryption */
+ SSL *ssl_handle; /* handle for control connection */
+ int ssl_active; /* ssl active on control conn */
+#endif
+
+} ftpbuf_t;
+
+
+
+/* open a FTP connection, returns ftpbuf (NULL on error)
+ * port is the ftp port in network byte order, or 0 for the default
+ */
+ftpbuf_t* ftp_open(const char *host, short port, long timeout_sec TSRMLS_DC);
+
+/* quits from the ftp session (it still needs to be closed)
+ * return true on success, false on error
+ */
+int ftp_quit(ftpbuf_t *ftp);
+
+/* frees up any cached data held in the ftp buffer */
+void ftp_gc(ftpbuf_t *ftp);
+
+/* close the FTP connection and return NULL */
+ftpbuf_t* ftp_close(ftpbuf_t *ftp);
+
+/* logs into the FTP server, returns true on success, false on error */
+int ftp_login(ftpbuf_t *ftp, const char *user, const char *pass TSRMLS_DC);
+
+/* reinitializes the connection, returns true on success, false on error */
+int ftp_reinit(ftpbuf_t *ftp);
+
+/* returns the remote system type (NULL on error) */
+const char* ftp_syst(ftpbuf_t *ftp);
+
+/* returns the present working directory (NULL on error) */
+const char* ftp_pwd(ftpbuf_t *ftp);
+
+/* exec a command [special features], return true on success, false on error */
+int ftp_exec(ftpbuf_t *ftp, const char *cmd);
+
+/* send a raw ftp command, return response as a hashtable, NULL on error */
+void ftp_raw(ftpbuf_t *ftp, const char *cmd, zval *return_value);
+
+/* changes directories, return true on success, false on error */
+int ftp_chdir(ftpbuf_t *ftp, const char *dir);
+
+/* changes to parent directory, return true on success, false on error */
+int ftp_cdup(ftpbuf_t *ftp);
+
+/* creates a directory, return the directory name on success, NULL on error.
+ * the return value must be freed
+ */
+char* ftp_mkdir(ftpbuf_t *ftp, const char *dir);
+
+/* removes a directory, return true on success, false on error */
+int ftp_rmdir(ftpbuf_t *ftp, const char *dir);
+
+/* Set permissions on a file */
+int ftp_chmod(ftpbuf_t *ftp, const int mode, const char *filename, const int filename_len);
+
+/* Allocate space on remote server with ALLO command
+ * Many servers will respond with 202 Allocation not necessary,
+ * however some servers will not accept STOR or APPE until ALLO is confirmed.
+ * If response is passed, it is estrdup()ed from ftp->inbuf and must be freed
+ * or assigned to a zval returned to the user */
+int ftp_alloc(ftpbuf_t *ftp, const int size, char **response);
+
+/* returns a NULL-terminated array of filenames in the given path
+ * or NULL on error. the return array must be freed (but don't
+ * free the array elements)
+ */
+char** ftp_nlist(ftpbuf_t *ftp, const char *path TSRMLS_DC);
+
+/* returns a NULL-terminated array of lines returned by the ftp
+ * LIST command for the given path or NULL on error. the return
+ * array must be freed (but don't
+ * free the array elements)
+ */
+char** ftp_list(ftpbuf_t *ftp, const char *path, int recursive TSRMLS_DC);
+
+/* switches passive mode on or off
+ * returns true on success, false on error
+ */
+int ftp_pasv(ftpbuf_t *ftp, int pasv);
+
+/* retrieves a file and saves its contents to outfp
+ * returns true on success, false on error
+ */
+int ftp_get(ftpbuf_t *ftp, php_stream *outstream, const char *path, ftptype_t type, int resumepos TSRMLS_DC);
+
+/* stores the data from a file, socket, or process as a file on the remote server
+ * returns true on success, false on error
+ */
+int ftp_put(ftpbuf_t *ftp, const char *path, php_stream *instream, ftptype_t type, int startpos TSRMLS_DC);
+
+/* returns the size of the given file, or -1 on error */
+int ftp_size(ftpbuf_t *ftp, const char *path);
+
+/* returns the last modified time of the given file, or -1 on error */
+time_t ftp_mdtm(ftpbuf_t *ftp, const char *path);
+
+/* renames a file on the server */
+int ftp_rename(ftpbuf_t *ftp, const char *src, const char *dest);
+
+/* deletes the file from the server */
+int ftp_delete(ftpbuf_t *ftp, const char *path);
+
+/* sends a SITE command to the server */
+int ftp_site(ftpbuf_t *ftp, const char *cmd);
+
+/* retrieves part of a file and saves its contents to outfp
+ * returns true on success, false on error
+ */
+int ftp_nb_get(ftpbuf_t *ftp, php_stream *outstream, const char *path, ftptype_t type, int resumepos TSRMLS_DC);
+
+/* stores the data from a file, socket, or process as a file on the remote server
+ * returns true on success, false on error
+ */
+int ftp_nb_put(ftpbuf_t *ftp, const char *path, php_stream *instream, ftptype_t type, int startpos TSRMLS_DC);
+
+/* continues a previous nb_(f)get command
+ */
+int ftp_nb_continue_read(ftpbuf_t *ftp TSRMLS_DC);
+
+/* continues a previous nb_(f)put command
+ */
+int ftp_nb_continue_write(ftpbuf_t *ftp TSRMLS_DC);
+
+
+#endif
diff --git a/ext/ftp/package.xml b/ext/ftp/package.xml
new file mode 100644
index 0000000..ecb2637
--- /dev/null
+++ b/ext/ftp/package.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="ISO-8859-1" ?>
+<!DOCTYPE package SYSTEM "../pear/package.dtd">
+<package>
+ <name>ftp</name>
+ <summary>File Transfer Protocol functions</summary>
+ <maintainers>
+ <maintainer>
+ <user>???</user>
+ <name>Andrew Skalski</name>
+ <email>askalski@chek.com</email>
+ <role>lead</role>
+ </maintainer>
+ <maintainer>
+ <user>sesser</user>
+ <name>Stefan Esser</name>
+ <email>sesser@php.net</email>
+ <role>lead</role>
+ </maintainer>
+ </maintainers>
+ <description>
+The functions in this extension implement client access to file
+servers speaking the File Transfer Protocol (FTP) as defined in
+http://www.faqs.org/rfcs/rfc959. This extension is meant for
+detailed access to an FTP server providing a wide range of
+control to the executing script. If you only wish to read from
+or write to a file on an FTP server, consider using the ftp://
+wrapper with the filesystem functions which provide a simpler
+and more intuitive interface.
+ </description>
+ <license>PHP</license>
+ <release>
+ <state>beta</state>
+ <version>5.0.0rc1</version>
+ <date>2004-03-19</date>
+ <notes>
+package.xml added to support installation using pear installer
+ </notes>
+ <filelist>
+ <file role="doc" name="CREDITS"/>
+ <file role="doc" name="README"/>
+ <file role="src" name="config.m4"/>
+ <file role="src" name="config.w32"/>
+ <file role="src" name="ctype.dsp"/>
+ <file role="src" name="ftp.c"/>
+ <file role="src" name="ftp.h"/>
+ <file role="src" name="php_ftp.c"/>
+ <file role="src" name="php_ftp.h"/>
+ </filelist>
+ <deps>
+ <dep type="php" rel="ge" version="5" />
+ </deps>
+ </release>
+</package>
+<!--
+vim:et:ts=1:sw=1
+-->
diff --git a/ext/ftp/php_ftp.c b/ext/ftp/php_ftp.c
new file mode 100644
index 0000000..7307193
--- /dev/null
+++ b/ext/ftp/php_ftp.c
@@ -0,0 +1,1443 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andrew Skalski <askalski@chek.com> |
+ | Stefan Esser <sesser@php.net> (resume functions) |
+ +----------------------------------------------------------------------+
+ */
+
+/* $Id$ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "php.h"
+
+#if defined(NETWARE) && defined(USE_WINSOCK)
+#include <novsock2.h>
+#endif
+
+#if HAVE_OPENSSL_EXT
+# include <openssl/ssl.h>
+#endif
+
+#if HAVE_FTP
+
+#include "ext/standard/info.h"
+#include "ext/standard/file.h"
+
+#include "php_ftp.h"
+#include "ftp.h"
+
+static int le_ftpbuf;
+#define le_ftpbuf_name "FTP Buffer"
+
+/* {{{ arginfo */
+ZEND_BEGIN_ARG_INFO_EX(arginfo_ftp_connect, 0, 0, 1)
+ ZEND_ARG_INFO(0, host)
+ ZEND_ARG_INFO(0, port)
+ ZEND_ARG_INFO(0, timeout)
+ZEND_END_ARG_INFO()
+
+#if HAVE_OPENSSL_EXT
+ZEND_BEGIN_ARG_INFO_EX(arginfo_ftp_ssl_connect, 0, 0, 1)
+ ZEND_ARG_INFO(0, host)
+ ZEND_ARG_INFO(0, port)
+ ZEND_ARG_INFO(0, timeout)
+ZEND_END_ARG_INFO()
+#endif
+
+ZEND_BEGIN_ARG_INFO(arginfo_ftp_login, 0)
+ ZEND_ARG_INFO(0, ftp)
+ ZEND_ARG_INFO(0, username)
+ ZEND_ARG_INFO(0, password)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_ftp_pwd, 0)
+ ZEND_ARG_INFO(0, ftp)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_ftp_cdup, 0)
+ ZEND_ARG_INFO(0, ftp)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_ftp_chdir, 0)
+ ZEND_ARG_INFO(0, ftp)
+ ZEND_ARG_INFO(0, directory)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_ftp_exec, 0)
+ ZEND_ARG_INFO(0, ftp)
+ ZEND_ARG_INFO(0, command)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_ftp_raw, 0)
+ ZEND_ARG_INFO(0, ftp)
+ ZEND_ARG_INFO(0, command)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_ftp_mkdir, 0)
+ ZEND_ARG_INFO(0, ftp)
+ ZEND_ARG_INFO(0, directory)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_ftp_rmdir, 0)
+ ZEND_ARG_INFO(0, ftp)
+ ZEND_ARG_INFO(0, directory)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_ftp_chmod, 0)
+ ZEND_ARG_INFO(0, ftp)
+ ZEND_ARG_INFO(0, mode)
+ ZEND_ARG_INFO(0, filename)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_ftp_alloc, 0, 0, 2)
+ ZEND_ARG_INFO(0, ftp)
+ ZEND_ARG_INFO(0, size)
+ ZEND_ARG_INFO(1, response)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_ftp_nlist, 0)
+ ZEND_ARG_INFO(0, ftp)
+ ZEND_ARG_INFO(0, directory)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_ftp_rawlist, 0, 0, 2)
+ ZEND_ARG_INFO(0, ftp)
+ ZEND_ARG_INFO(0, directory)
+ ZEND_ARG_INFO(0, recursive)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_ftp_systype, 0)
+ ZEND_ARG_INFO(0, ftp)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_ftp_fget, 0, 0, 4)
+ ZEND_ARG_INFO(0, ftp)
+ ZEND_ARG_INFO(0, fp)
+ ZEND_ARG_INFO(0, remote_file)
+ ZEND_ARG_INFO(0, mode)
+ ZEND_ARG_INFO(0, resumepos)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_ftp_nb_fget, 0, 0, 4)
+ ZEND_ARG_INFO(0, ftp)
+ ZEND_ARG_INFO(0, fp)
+ ZEND_ARG_INFO(0, remote_file)
+ ZEND_ARG_INFO(0, mode)
+ ZEND_ARG_INFO(0, resumepos)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_ftp_pasv, 0)
+ ZEND_ARG_INFO(0, ftp)
+ ZEND_ARG_INFO(0, pasv)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_ftp_get, 0, 0, 4)
+ ZEND_ARG_INFO(0, ftp)
+ ZEND_ARG_INFO(0, local_file)
+ ZEND_ARG_INFO(0, remote_file)
+ ZEND_ARG_INFO(0, mode)
+ ZEND_ARG_INFO(0, resume_pos)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_ftp_nb_get, 0, 0, 4)
+ ZEND_ARG_INFO(0, ftp)
+ ZEND_ARG_INFO(0, local_file)
+ ZEND_ARG_INFO(0, remote_file)
+ ZEND_ARG_INFO(0, mode)
+ ZEND_ARG_INFO(0, resume_pos)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_ftp_nb_continue, 0)
+ ZEND_ARG_INFO(0, ftp)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_ftp_fput, 0, 0, 4)
+ ZEND_ARG_INFO(0, ftp)
+ ZEND_ARG_INFO(0, remote_file)
+ ZEND_ARG_INFO(0, fp)
+ ZEND_ARG_INFO(0, mode)
+ ZEND_ARG_INFO(0, startpos)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_ftp_nb_fput, 0, 0, 4)
+ ZEND_ARG_INFO(0, ftp)
+ ZEND_ARG_INFO(0, remote_file)
+ ZEND_ARG_INFO(0, fp)
+ ZEND_ARG_INFO(0, mode)
+ ZEND_ARG_INFO(0, startpos)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_ftp_put, 0, 0, 4)
+ ZEND_ARG_INFO(0, ftp)
+ ZEND_ARG_INFO(0, remote_file)
+ ZEND_ARG_INFO(0, local_file)
+ ZEND_ARG_INFO(0, mode)
+ ZEND_ARG_INFO(0, startpos)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO_EX(arginfo_ftp_nb_put, 0, 0, 4)
+ ZEND_ARG_INFO(0, ftp)
+ ZEND_ARG_INFO(0, remote_file)
+ ZEND_ARG_INFO(0, local_file)
+ ZEND_ARG_INFO(0, mode)
+ ZEND_ARG_INFO(0, startpos)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_ftp_size, 0)
+ ZEND_ARG_INFO(0, ftp)
+ ZEND_ARG_INFO(0, filename)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_ftp_mdtm, 0)
+ ZEND_ARG_INFO(0, ftp)
+ ZEND_ARG_INFO(0, filename)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_ftp_rename, 0)
+ ZEND_ARG_INFO(0, ftp)
+ ZEND_ARG_INFO(0, src)
+ ZEND_ARG_INFO(0, dest)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_ftp_delete, 0)
+ ZEND_ARG_INFO(0, ftp)
+ ZEND_ARG_INFO(0, file)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_ftp_site, 0)
+ ZEND_ARG_INFO(0, ftp)
+ ZEND_ARG_INFO(0, cmd)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_ftp_close, 0)
+ ZEND_ARG_INFO(0, ftp)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_ftp_set_option, 0)
+ ZEND_ARG_INFO(0, ftp)
+ ZEND_ARG_INFO(0, option)
+ ZEND_ARG_INFO(0, value)
+ZEND_END_ARG_INFO()
+
+ZEND_BEGIN_ARG_INFO(arginfo_ftp_get_option, 0)
+ ZEND_ARG_INFO(0, ftp)
+ ZEND_ARG_INFO(0, option)
+ZEND_END_ARG_INFO()
+
+/* }}} */
+
+const zend_function_entry php_ftp_functions[] = {
+ PHP_FE(ftp_connect, arginfo_ftp_connect)
+#if HAVE_OPENSSL_EXT
+ PHP_FE(ftp_ssl_connect, arginfo_ftp_ssl_connect)
+#endif
+ PHP_FE(ftp_login, arginfo_ftp_login)
+ PHP_FE(ftp_pwd, arginfo_ftp_pwd)
+ PHP_FE(ftp_cdup, arginfo_ftp_cdup)
+ PHP_FE(ftp_chdir, arginfo_ftp_chdir)
+ PHP_FE(ftp_exec, arginfo_ftp_exec)
+ PHP_FE(ftp_raw, arginfo_ftp_raw)
+ PHP_FE(ftp_mkdir, arginfo_ftp_mkdir)
+ PHP_FE(ftp_rmdir, arginfo_ftp_rmdir)
+ PHP_FE(ftp_chmod, arginfo_ftp_chmod)
+ PHP_FE(ftp_alloc, arginfo_ftp_alloc)
+ PHP_FE(ftp_nlist, arginfo_ftp_nlist)
+ PHP_FE(ftp_rawlist, arginfo_ftp_rawlist)
+ PHP_FE(ftp_systype, arginfo_ftp_systype)
+ PHP_FE(ftp_pasv, arginfo_ftp_pasv)
+ PHP_FE(ftp_get, arginfo_ftp_get)
+ PHP_FE(ftp_fget, arginfo_ftp_fget)
+ PHP_FE(ftp_put, arginfo_ftp_put)
+ PHP_FE(ftp_fput, arginfo_ftp_fput)
+ PHP_FE(ftp_size, arginfo_ftp_size)
+ PHP_FE(ftp_mdtm, arginfo_ftp_mdtm)
+ PHP_FE(ftp_rename, arginfo_ftp_rename)
+ PHP_FE(ftp_delete, arginfo_ftp_delete)
+ PHP_FE(ftp_site, arginfo_ftp_site)
+ PHP_FE(ftp_close, arginfo_ftp_close)
+ PHP_FE(ftp_set_option, arginfo_ftp_set_option)
+ PHP_FE(ftp_get_option, arginfo_ftp_get_option)
+ PHP_FE(ftp_nb_fget, arginfo_ftp_nb_fget)
+ PHP_FE(ftp_nb_get, arginfo_ftp_nb_get)
+ PHP_FE(ftp_nb_continue, arginfo_ftp_nb_continue)
+ PHP_FE(ftp_nb_put, arginfo_ftp_nb_put)
+ PHP_FE(ftp_nb_fput, arginfo_ftp_nb_fput)
+ PHP_FALIAS(ftp_quit, ftp_close, arginfo_ftp_close)
+ PHP_FE_END
+};
+
+zend_module_entry php_ftp_module_entry = {
+ STANDARD_MODULE_HEADER,
+ "ftp",
+ php_ftp_functions,
+ PHP_MINIT(ftp),
+ NULL,
+ NULL,
+ NULL,
+ PHP_MINFO(ftp),
+ NO_VERSION_YET,
+ STANDARD_MODULE_PROPERTIES
+};
+
+#if COMPILE_DL_FTP
+ZEND_GET_MODULE(php_ftp)
+#endif
+
+static void ftp_destructor_ftpbuf(zend_rsrc_list_entry *rsrc TSRMLS_DC)
+{
+ ftpbuf_t *ftp = (ftpbuf_t *)rsrc->ptr;
+
+ ftp_close(ftp);
+}
+
+PHP_MINIT_FUNCTION(ftp)
+{
+ le_ftpbuf = zend_register_list_destructors_ex(ftp_destructor_ftpbuf, NULL, le_ftpbuf_name, module_number);
+ REGISTER_LONG_CONSTANT("FTP_ASCII", FTPTYPE_ASCII, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("FTP_TEXT", FTPTYPE_ASCII, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("FTP_BINARY", FTPTYPE_IMAGE, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("FTP_IMAGE", FTPTYPE_IMAGE, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("FTP_AUTORESUME", PHP_FTP_AUTORESUME, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("FTP_TIMEOUT_SEC", PHP_FTP_OPT_TIMEOUT_SEC, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("FTP_AUTOSEEK", PHP_FTP_OPT_AUTOSEEK, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("FTP_FAILED", PHP_FTP_FAILED, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("FTP_FINISHED", PHP_FTP_FINISHED, CONST_PERSISTENT | CONST_CS);
+ REGISTER_LONG_CONSTANT("FTP_MOREDATA", PHP_FTP_MOREDATA, CONST_PERSISTENT | CONST_CS);
+ return SUCCESS;
+}
+
+PHP_MINFO_FUNCTION(ftp)
+{
+ php_info_print_table_start();
+ php_info_print_table_row(2, "FTP support", "enabled");
+ php_info_print_table_end();
+}
+
+#define XTYPE(xtype, mode) { \
+ if (mode != FTPTYPE_ASCII && mode != FTPTYPE_IMAGE) { \
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Mode must be FTP_ASCII or FTP_BINARY"); \
+ RETURN_FALSE; \
+ } \
+ xtype = mode; \
+ }
+
+
+/* {{{ proto resource ftp_connect(string host [, int port [, int timeout]])
+ Opens a FTP stream */
+PHP_FUNCTION(ftp_connect)
+{
+ ftpbuf_t *ftp;
+ char *host;
+ int host_len;
+ long port = 0;
+ long timeout_sec = FTP_DEFAULT_TIMEOUT;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ll", &host, &host_len, &port, &timeout_sec) == FAILURE) {
+ return;
+ }
+
+ if (timeout_sec <= 0) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Timeout has to be greater than 0");
+ RETURN_FALSE;
+ }
+
+ /* connect */
+ if (!(ftp = ftp_open(host, (short)port, timeout_sec TSRMLS_CC))) {
+ RETURN_FALSE;
+ }
+
+ /* autoseek for resuming */
+ ftp->autoseek = FTP_DEFAULT_AUTOSEEK;
+#if HAVE_OPENSSL_EXT
+ /* disable ssl */
+ ftp->use_ssl = 0;
+#endif
+
+ ZEND_REGISTER_RESOURCE(return_value, ftp, le_ftpbuf);
+}
+/* }}} */
+
+#if HAVE_OPENSSL_EXT
+/* {{{ proto resource ftp_ssl_connect(string host [, int port [, int timeout]])
+ Opens a FTP-SSL stream */
+PHP_FUNCTION(ftp_ssl_connect)
+{
+ ftpbuf_t *ftp;
+ char *host;
+ int host_len;
+ long port = 0;
+ long timeout_sec = FTP_DEFAULT_TIMEOUT;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|ll", &host, &host_len, &port, &timeout_sec) == FAILURE) {
+ return;
+ }
+
+ if (timeout_sec <= 0) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Timeout has to be greater than 0");
+ RETURN_FALSE;
+ }
+
+ /* connect */
+ if (!(ftp = ftp_open(host, (short)port, timeout_sec TSRMLS_CC))) {
+ RETURN_FALSE;
+ }
+
+ /* autoseek for resuming */
+ ftp->autoseek = FTP_DEFAULT_AUTOSEEK;
+ /* enable ssl */
+ ftp->use_ssl = 1;
+
+ ZEND_REGISTER_RESOURCE(return_value, ftp, le_ftpbuf);
+}
+/* }}} */
+#endif
+
+/* {{{ proto bool ftp_login(resource stream, string username, string password)
+ Logs into the FTP server */
+PHP_FUNCTION(ftp_login)
+{
+ zval *z_ftp;
+ ftpbuf_t *ftp;
+ char *user, *pass;
+ int user_len, pass_len;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rss", &z_ftp, &user, &user_len, &pass, &pass_len) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(ftp, ftpbuf_t*, &z_ftp, -1, le_ftpbuf_name, le_ftpbuf);
+
+ /* log in */
+ if (!ftp_login(ftp, user, pass TSRMLS_CC)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", ftp->inbuf);
+ RETURN_FALSE;
+ }
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto string ftp_pwd(resource stream)
+ Returns the present working directory */
+PHP_FUNCTION(ftp_pwd)
+{
+ zval *z_ftp;
+ ftpbuf_t *ftp;
+ const char *pwd;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &z_ftp) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(ftp, ftpbuf_t*, &z_ftp, -1, le_ftpbuf_name, le_ftpbuf);
+
+ if (!(pwd = ftp_pwd(ftp))) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", ftp->inbuf);
+ RETURN_FALSE;
+ }
+
+ RETURN_STRING((char*) pwd, 1);
+}
+/* }}} */
+
+/* {{{ proto bool ftp_cdup(resource stream)
+ Changes to the parent directory */
+PHP_FUNCTION(ftp_cdup)
+{
+ zval *z_ftp;
+ ftpbuf_t *ftp;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &z_ftp) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(ftp, ftpbuf_t*, &z_ftp, -1, le_ftpbuf_name, le_ftpbuf);
+
+ if (!ftp_cdup(ftp)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", ftp->inbuf);
+ RETURN_FALSE;
+ }
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto bool ftp_chdir(resource stream, string directory)
+ Changes directories */
+PHP_FUNCTION(ftp_chdir)
+{
+ zval *z_ftp;
+ ftpbuf_t *ftp;
+ char *dir;
+ int dir_len;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &z_ftp, &dir, &dir_len) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(ftp, ftpbuf_t*, &z_ftp, -1, le_ftpbuf_name, le_ftpbuf);
+
+ /* change directories */
+ if (!ftp_chdir(ftp, dir)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", ftp->inbuf);
+ RETURN_FALSE;
+ }
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto bool ftp_exec(resource stream, string command)
+ Requests execution of a program on the FTP server */
+PHP_FUNCTION(ftp_exec)
+{
+ zval *z_ftp;
+ ftpbuf_t *ftp;
+ char *cmd;
+ int cmd_len;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &z_ftp, &cmd, &cmd_len) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(ftp, ftpbuf_t*, &z_ftp, -1, le_ftpbuf_name, le_ftpbuf);
+
+ /* execute serverside command */
+ if (!ftp_exec(ftp, cmd)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", ftp->inbuf);
+ RETURN_FALSE;
+ }
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto array ftp_raw(resource stream, string command)
+ Sends a literal command to the FTP server */
+PHP_FUNCTION(ftp_raw)
+{
+ zval *z_ftp;
+ ftpbuf_t *ftp;
+ char *cmd;
+ int cmd_len;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &z_ftp, &cmd, &cmd_len) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(ftp, ftpbuf_t*, &z_ftp, -1, le_ftpbuf_name, le_ftpbuf);
+
+ /* execute arbitrary ftp command */
+ ftp_raw(ftp, cmd, return_value);
+}
+/* }}} */
+
+/* {{{ proto string ftp_mkdir(resource stream, string directory)
+ Creates a directory and returns the absolute path for the new directory or false on error */
+PHP_FUNCTION(ftp_mkdir)
+{
+ zval *z_ftp;
+ ftpbuf_t *ftp;
+ char *dir, *tmp;
+ int dir_len;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &z_ftp, &dir, &dir_len) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(ftp, ftpbuf_t*, &z_ftp, -1, le_ftpbuf_name, le_ftpbuf);
+
+ /* create directorie */
+ if (NULL == (tmp = ftp_mkdir(ftp, dir))) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", ftp->inbuf);
+ RETURN_FALSE;
+ }
+
+ RETURN_STRING(tmp, 0);
+}
+/* }}} */
+
+/* {{{ proto bool ftp_rmdir(resource stream, string directory)
+ Removes a directory */
+PHP_FUNCTION(ftp_rmdir)
+{
+ zval *z_ftp;
+ ftpbuf_t *ftp;
+ char *dir;
+ int dir_len;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &z_ftp, &dir, &dir_len) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(ftp, ftpbuf_t*, &z_ftp, -1, le_ftpbuf_name, le_ftpbuf);
+
+ /* remove directorie */
+ if (!ftp_rmdir(ftp, dir)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", ftp->inbuf);
+ RETURN_FALSE;
+ }
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto int ftp_chmod(resource stream, int mode, string filename)
+ Sets permissions on a file */
+PHP_FUNCTION(ftp_chmod)
+{
+ zval *z_ftp;
+ ftpbuf_t *ftp;
+ char *filename;
+ int filename_len;
+ long mode;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rlp", &z_ftp, &mode, &filename, &filename_len) == FAILURE) {
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE(ftp, ftpbuf_t*, &z_ftp, -1, le_ftpbuf_name, le_ftpbuf);
+
+ if (!ftp_chmod(ftp, mode, filename, filename_len)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", ftp->inbuf);
+ RETURN_FALSE;
+ }
+
+ RETURN_LONG(mode);
+}
+/* }}} */
+
+/* {{{ proto bool ftp_alloc(resource stream, int size[, &response])
+ Attempt to allocate space on the remote FTP server */
+PHP_FUNCTION(ftp_alloc)
+{
+ zval *z_ftp, *zresponse = NULL;
+ ftpbuf_t *ftp;
+ long size, ret;
+ char *response = NULL;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl|z", &z_ftp, &size, &zresponse) == FAILURE) {
+ RETURN_FALSE;
+ }
+
+ ZEND_FETCH_RESOURCE(ftp, ftpbuf_t*, &z_ftp, -1, le_ftpbuf_name, le_ftpbuf);
+
+ ret = ftp_alloc(ftp, size, zresponse ? &response : NULL);
+ if (response) {
+ zval_dtor(zresponse);
+ ZVAL_STRING(zresponse, response, 0);
+ }
+
+ if (!ret) {
+ RETURN_FALSE;
+ }
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto array ftp_nlist(resource stream, string directory)
+ Returns an array of filenames in the given directory */
+PHP_FUNCTION(ftp_nlist)
+{
+ zval *z_ftp;
+ ftpbuf_t *ftp;
+ char **nlist, **ptr, *dir;
+ int dir_len;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rp", &z_ftp, &dir, &dir_len) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(ftp, ftpbuf_t*, &z_ftp, -1, le_ftpbuf_name, le_ftpbuf);
+
+ /* get list of files */
+ if (NULL == (nlist = ftp_nlist(ftp, dir TSRMLS_CC))) {
+ RETURN_FALSE;
+ }
+
+ array_init(return_value);
+ for (ptr = nlist; *ptr; ptr++) {
+ add_next_index_string(return_value, *ptr, 1);
+ }
+ efree(nlist);
+}
+/* }}} */
+
+/* {{{ proto array ftp_rawlist(resource stream, string directory [, bool recursive])
+ Returns a detailed listing of a directory as an array of output lines */
+PHP_FUNCTION(ftp_rawlist)
+{
+ zval *z_ftp;
+ ftpbuf_t *ftp;
+ char **llist, **ptr, *dir;
+ int dir_len;
+ zend_bool recursive = 0;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs|b", &z_ftp, &dir, &dir_len, &recursive) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(ftp, ftpbuf_t*, &z_ftp, -1, le_ftpbuf_name, le_ftpbuf);
+
+ /* get raw directory listing */
+ if (NULL == (llist = ftp_list(ftp, dir, recursive TSRMLS_CC))) {
+ RETURN_FALSE;
+ }
+
+ array_init(return_value);
+ for (ptr = llist; *ptr; ptr++) {
+ add_next_index_string(return_value, *ptr, 1);
+ }
+ efree(llist);
+}
+/* }}} */
+
+/* {{{ proto string ftp_systype(resource stream)
+ Returns the system type identifier */
+PHP_FUNCTION(ftp_systype)
+{
+ zval *z_ftp;
+ ftpbuf_t *ftp;
+ const char *syst;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &z_ftp) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(ftp, ftpbuf_t*, &z_ftp, -1, le_ftpbuf_name, le_ftpbuf);
+
+ if (NULL == (syst = ftp_syst(ftp))) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", ftp->inbuf);
+ RETURN_FALSE;
+ }
+
+ RETURN_STRING((char*) syst, 1);
+}
+/* }}} */
+
+/* {{{ proto bool ftp_fget(resource stream, resource fp, string remote_file, int mode[, int resumepos])
+ Retrieves a file from the FTP server and writes it to an open file */
+PHP_FUNCTION(ftp_fget)
+{
+ zval *z_ftp, *z_file;
+ ftpbuf_t *ftp;
+ ftptype_t xtype;
+ php_stream *stream;
+ char *file;
+ int file_len;
+ long mode, resumepos=0;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rrsl|l", &z_ftp, &z_file, &file, &file_len, &mode, &resumepos) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(ftp, ftpbuf_t*, &z_ftp, -1, le_ftpbuf_name, le_ftpbuf);
+ php_stream_from_zval(stream, &z_file);
+ XTYPE(xtype, mode);
+
+ /* ignore autoresume if autoseek is switched off */
+ if (!ftp->autoseek && resumepos == PHP_FTP_AUTORESUME) {
+ resumepos = 0;
+ }
+
+ if (ftp->autoseek && resumepos) {
+ /* if autoresume is wanted seek to end */
+ if (resumepos == PHP_FTP_AUTORESUME) {
+ php_stream_seek(stream, 0, SEEK_END);
+ resumepos = php_stream_tell(stream);
+ } else {
+ php_stream_seek(stream, resumepos, SEEK_SET);
+ }
+ }
+
+ if (!ftp_get(ftp, stream, file, xtype, resumepos TSRMLS_CC)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", ftp->inbuf);
+ RETURN_FALSE;
+ }
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto int ftp_nb_fget(resource stream, resource fp, string remote_file, int mode[, int resumepos])
+ Retrieves a file from the FTP server asynchronly and writes it to an open file */
+PHP_FUNCTION(ftp_nb_fget)
+{
+ zval *z_ftp, *z_file;
+ ftpbuf_t *ftp;
+ ftptype_t xtype;
+ php_stream *stream;
+ char *file;
+ int file_len, ret;
+ long mode, resumepos=0;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rrsl|l", &z_ftp, &z_file, &file, &file_len, &mode, &resumepos) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(ftp, ftpbuf_t*, &z_ftp, -1, le_ftpbuf_name, le_ftpbuf);
+ php_stream_from_zval(stream, &z_file);
+ XTYPE(xtype, mode);
+
+ /* ignore autoresume if autoseek is switched off */
+ if (!ftp->autoseek && resumepos == PHP_FTP_AUTORESUME) {
+ resumepos = 0;
+ }
+
+ if (ftp->autoseek && resumepos) {
+ /* if autoresume is wanted seek to end */
+ if (resumepos == PHP_FTP_AUTORESUME) {
+ php_stream_seek(stream, 0, SEEK_END);
+ resumepos = php_stream_tell(stream);
+ } else {
+ php_stream_seek(stream, resumepos, SEEK_SET);
+ }
+ }
+
+ /* configuration */
+ ftp->direction = 0; /* recv */
+ ftp->closestream = 0; /* do not close */
+
+ if ((ret = ftp_nb_get(ftp, stream, file, xtype, resumepos TSRMLS_CC)) == PHP_FTP_FAILED) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", ftp->inbuf);
+ RETURN_LONG(ret);
+ }
+
+ RETURN_LONG(ret);
+}
+/* }}} */
+
+/* {{{ proto bool ftp_pasv(resource stream, bool pasv)
+ Turns passive mode on or off */
+PHP_FUNCTION(ftp_pasv)
+{
+ zval *z_ftp;
+ ftpbuf_t *ftp;
+ zend_bool pasv;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rb", &z_ftp, &pasv) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(ftp, ftpbuf_t*, &z_ftp, -1, le_ftpbuf_name, le_ftpbuf);
+
+ if (!ftp_pasv(ftp, pasv ? 1 : 0)) {
+ RETURN_FALSE;
+ }
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto bool ftp_get(resource stream, string local_file, string remote_file, int mode[, int resume_pos])
+ Retrieves a file from the FTP server and writes it to a local file */
+PHP_FUNCTION(ftp_get)
+{
+ zval *z_ftp;
+ ftpbuf_t *ftp;
+ ftptype_t xtype;
+ php_stream *outstream;
+ char *local, *remote;
+ int local_len, remote_len;
+ long mode, resumepos=0;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rppl|l", &z_ftp, &local, &local_len, &remote, &remote_len, &mode, &resumepos) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(ftp, ftpbuf_t*, &z_ftp, -1, le_ftpbuf_name, le_ftpbuf);
+ XTYPE(xtype, mode);
+
+ /* ignore autoresume if autoseek is switched off */
+ if (!ftp->autoseek && resumepos == PHP_FTP_AUTORESUME) {
+ resumepos = 0;
+ }
+
+#ifdef PHP_WIN32
+ mode = FTPTYPE_IMAGE;
+#endif
+
+ if (ftp->autoseek && resumepos) {
+ outstream = php_stream_open_wrapper(local, mode == FTPTYPE_ASCII ? "rt+" : "rb+", REPORT_ERRORS, NULL);
+ if (outstream == NULL) {
+ outstream = php_stream_open_wrapper(local, mode == FTPTYPE_ASCII ? "wt" : "wb", REPORT_ERRORS, NULL);
+ }
+ if (outstream != NULL) {
+ /* if autoresume is wanted seek to end */
+ if (resumepos == PHP_FTP_AUTORESUME) {
+ php_stream_seek(outstream, 0, SEEK_END);
+ resumepos = php_stream_tell(outstream);
+ } else {
+ php_stream_seek(outstream, resumepos, SEEK_SET);
+ }
+ }
+ } else {
+ outstream = php_stream_open_wrapper(local, mode == FTPTYPE_ASCII ? "wt" : "wb", REPORT_ERRORS, NULL);
+ }
+
+ if (outstream == NULL) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error opening %s", local);
+ RETURN_FALSE;
+ }
+
+ if (!ftp_get(ftp, outstream, remote, xtype, resumepos TSRMLS_CC)) {
+ php_stream_close(outstream);
+ VCWD_UNLINK(local);
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", ftp->inbuf);
+ RETURN_FALSE;
+ }
+
+ php_stream_close(outstream);
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto int ftp_nb_get(resource stream, string local_file, string remote_file, int mode[, int resume_pos])
+ Retrieves a file from the FTP server nbhronly and writes it to a local file */
+PHP_FUNCTION(ftp_nb_get)
+{
+ zval *z_ftp;
+ ftpbuf_t *ftp;
+ ftptype_t xtype;
+ php_stream *outstream;
+ char *local, *remote;
+ int local_len, remote_len, ret;
+ long mode, resumepos=0;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rssl|l", &z_ftp, &local, &local_len, &remote, &remote_len, &mode, &resumepos) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(ftp, ftpbuf_t*, &z_ftp, -1, le_ftpbuf_name, le_ftpbuf);
+ XTYPE(xtype, mode);
+
+ /* ignore autoresume if autoseek is switched off */
+ if (!ftp->autoseek && resumepos == PHP_FTP_AUTORESUME) {
+ resumepos = 0;
+ }
+#ifdef PHP_WIN32
+ mode = FTPTYPE_IMAGE;
+#endif
+ if (ftp->autoseek && resumepos) {
+ outstream = php_stream_open_wrapper(local, mode == FTPTYPE_ASCII ? "rt+" : "rb+", REPORT_ERRORS, NULL);
+ if (outstream == NULL) {
+ outstream = php_stream_open_wrapper(local, mode == FTPTYPE_ASCII ? "wt" : "wb", REPORT_ERRORS, NULL);
+ }
+ if (outstream != NULL) {
+ /* if autoresume is wanted seek to end */
+ if (resumepos == PHP_FTP_AUTORESUME) {
+ php_stream_seek(outstream, 0, SEEK_END);
+ resumepos = php_stream_tell(outstream);
+ } else {
+ php_stream_seek(outstream, resumepos, SEEK_SET);
+ }
+ }
+ } else {
+ outstream = php_stream_open_wrapper(local, mode == FTPTYPE_ASCII ? "wt" : "wb", REPORT_ERRORS, NULL);
+ }
+
+ if (outstream == NULL) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Error opening %s", local);
+ RETURN_FALSE;
+ }
+
+ /* configuration */
+ ftp->direction = 0; /* recv */
+ ftp->closestream = 1; /* do close */
+
+ if ((ret = ftp_nb_get(ftp, outstream, remote, xtype, resumepos TSRMLS_CC)) == PHP_FTP_FAILED) {
+ php_stream_close(outstream);
+ VCWD_UNLINK(local);
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", ftp->inbuf);
+ RETURN_LONG(PHP_FTP_FAILED);
+ }
+
+ if (ret == PHP_FTP_FINISHED) {
+ php_stream_close(outstream);
+ }
+
+ RETURN_LONG(ret);
+}
+/* }}} */
+
+/* {{{ proto int ftp_nb_continue(resource stream)
+ Continues retrieving/sending a file nbronously */
+PHP_FUNCTION(ftp_nb_continue)
+{
+ zval *z_ftp;
+ ftpbuf_t *ftp;
+ int ret;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &z_ftp) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(ftp, ftpbuf_t*, &z_ftp, -1, le_ftpbuf_name, le_ftpbuf);
+
+ if (!ftp->nb) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "no nbronous transfer to continue.");
+ RETURN_LONG(PHP_FTP_FAILED);
+ }
+
+ if (ftp->direction) {
+ ret=ftp_nb_continue_write(ftp TSRMLS_CC);
+ } else {
+ ret=ftp_nb_continue_read(ftp TSRMLS_CC);
+ }
+
+ if (ret != PHP_FTP_MOREDATA && ftp->closestream) {
+ php_stream_close(ftp->stream);
+ }
+
+ if (ret == PHP_FTP_FAILED) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", ftp->inbuf);
+ }
+
+ RETURN_LONG(ret);
+}
+/* }}} */
+
+/* {{{ proto bool ftp_fput(resource stream, string remote_file, resource fp, int mode[, int startpos])
+ Stores a file from an open file to the FTP server */
+PHP_FUNCTION(ftp_fput)
+{
+ zval *z_ftp, *z_file;
+ ftpbuf_t *ftp;
+ ftptype_t xtype;
+ int remote_len;
+ long mode, startpos=0;
+ php_stream *stream;
+ char *remote;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsrl|l", &z_ftp, &remote, &remote_len, &z_file, &mode, &startpos) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(ftp, ftpbuf_t*, &z_ftp, -1, le_ftpbuf_name, le_ftpbuf);
+ php_stream_from_zval(stream, &z_file);
+ XTYPE(xtype, mode);
+
+ /* ignore autoresume if autoseek is switched off */
+ if (!ftp->autoseek && startpos == PHP_FTP_AUTORESUME) {
+ startpos = 0;
+ }
+
+ if (ftp->autoseek && startpos) {
+ /* if autoresume is wanted ask for remote size */
+ if (startpos == PHP_FTP_AUTORESUME) {
+ startpos = ftp_size(ftp, remote);
+ if (startpos < 0) {
+ startpos = 0;
+ }
+ }
+ if (startpos) {
+ php_stream_seek(stream, startpos, SEEK_SET);
+ }
+ }
+
+ if (!ftp_put(ftp, remote, stream, xtype, startpos TSRMLS_CC)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", ftp->inbuf);
+ RETURN_FALSE;
+ }
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto int ftp_nb_fput(resource stream, string remote_file, resource fp, int mode[, int startpos])
+ Stores a file from an open file to the FTP server nbronly */
+PHP_FUNCTION(ftp_nb_fput)
+{
+ zval *z_ftp, *z_file;
+ ftpbuf_t *ftp;
+ ftptype_t xtype;
+ int remote_len, ret;
+ long mode, startpos=0;
+ php_stream *stream;
+ char *remote;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rsrl|l", &z_ftp, &remote, &remote_len, &z_file, &mode, &startpos) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(ftp, ftpbuf_t*, &z_ftp, -1, le_ftpbuf_name, le_ftpbuf);
+ php_stream_from_zval(stream, &z_file);
+ XTYPE(xtype, mode);
+
+ /* ignore autoresume if autoseek is switched off */
+ if (!ftp->autoseek && startpos == PHP_FTP_AUTORESUME) {
+ startpos = 0;
+ }
+
+ if (ftp->autoseek && startpos) {
+ /* if autoresume is wanted ask for remote size */
+ if (startpos == PHP_FTP_AUTORESUME) {
+ startpos = ftp_size(ftp, remote);
+ if (startpos < 0) {
+ startpos = 0;
+ }
+ }
+ if (startpos) {
+ php_stream_seek(stream, startpos, SEEK_SET);
+ }
+ }
+
+ /* configuration */
+ ftp->direction = 1; /* send */
+ ftp->closestream = 0; /* do not close */
+
+ if (((ret = ftp_nb_put(ftp, remote, stream, xtype, startpos TSRMLS_CC)) == PHP_FTP_FAILED)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", ftp->inbuf);
+ RETURN_LONG(ret);
+ }
+
+ RETURN_LONG(ret);
+}
+/* }}} */
+
+
+/* {{{ proto bool ftp_put(resource stream, string remote_file, string local_file, int mode[, int startpos])
+ Stores a file on the FTP server */
+PHP_FUNCTION(ftp_put)
+{
+ zval *z_ftp;
+ ftpbuf_t *ftp;
+ ftptype_t xtype;
+ char *remote, *local;
+ int remote_len, local_len;
+ long mode, startpos=0;
+ php_stream *instream;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rppl|l", &z_ftp, &remote, &remote_len, &local, &local_len, &mode, &startpos) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(ftp, ftpbuf_t*, &z_ftp, -1, le_ftpbuf_name, le_ftpbuf);
+ XTYPE(xtype, mode);
+
+ if (!(instream = php_stream_open_wrapper(local, mode == FTPTYPE_ASCII ? "rt" : "rb", REPORT_ERRORS, NULL))) {
+ RETURN_FALSE;
+ }
+
+ /* ignore autoresume if autoseek is switched off */
+ if (!ftp->autoseek && startpos == PHP_FTP_AUTORESUME) {
+ startpos = 0;
+ }
+
+ if (ftp->autoseek && startpos) {
+ /* if autoresume is wanted ask for remote size */
+ if (startpos == PHP_FTP_AUTORESUME) {
+ startpos = ftp_size(ftp, remote);
+ if (startpos < 0) {
+ startpos = 0;
+ }
+ }
+ if (startpos) {
+ php_stream_seek(instream, startpos, SEEK_SET);
+ }
+ }
+
+ if (!ftp_put(ftp, remote, instream, xtype, startpos TSRMLS_CC)) {
+ php_stream_close(instream);
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", ftp->inbuf);
+ RETURN_FALSE;
+ }
+ php_stream_close(instream);
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+
+/* {{{ proto int ftp_nb_put(resource stream, string remote_file, string local_file, int mode[, int startpos])
+ Stores a file on the FTP server */
+PHP_FUNCTION(ftp_nb_put)
+{
+ zval *z_ftp;
+ ftpbuf_t *ftp;
+ ftptype_t xtype;
+ char *remote, *local;
+ int remote_len, local_len, ret;
+ long mode, startpos=0;
+ php_stream *instream;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rppl|l", &z_ftp, &remote, &remote_len, &local, &local_len, &mode, &startpos) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(ftp, ftpbuf_t*, &z_ftp, -1, le_ftpbuf_name, le_ftpbuf);
+ XTYPE(xtype, mode);
+
+ if (!(instream = php_stream_open_wrapper(local, mode == FTPTYPE_ASCII ? "rt" : "rb", REPORT_ERRORS, NULL))) {
+ RETURN_FALSE;
+ }
+
+ /* ignore autoresume if autoseek is switched off */
+ if (!ftp->autoseek && startpos == PHP_FTP_AUTORESUME) {
+ startpos = 0;
+ }
+
+ if (ftp->autoseek && startpos) {
+ /* if autoresume is wanted ask for remote size */
+ if (startpos == PHP_FTP_AUTORESUME) {
+ startpos = ftp_size(ftp, remote);
+ if (startpos < 0) {
+ startpos = 0;
+ }
+ }
+ if (startpos) {
+ php_stream_seek(instream, startpos, SEEK_SET);
+ }
+ }
+
+ /* configuration */
+ ftp->direction = 1; /* send */
+ ftp->closestream = 1; /* do close */
+
+ ret = ftp_nb_put(ftp, remote, instream, xtype, startpos TSRMLS_CC);
+
+ if (ret != PHP_FTP_MOREDATA) {
+ php_stream_close(instream);
+ }
+
+ if (ret == PHP_FTP_FAILED) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", ftp->inbuf);
+ }
+
+ RETURN_LONG(ret);
+}
+/* }}} */
+
+/* {{{ proto int ftp_size(resource stream, string filename)
+ Returns the size of the file, or -1 on error */
+PHP_FUNCTION(ftp_size)
+{
+ zval *z_ftp;
+ ftpbuf_t *ftp;
+ char *file;
+ int file_len;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rp", &z_ftp, &file, &file_len) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(ftp, ftpbuf_t*, &z_ftp, -1, le_ftpbuf_name, le_ftpbuf);
+
+ /* get file size */
+ RETURN_LONG(ftp_size(ftp, file));
+}
+/* }}} */
+
+/* {{{ proto int ftp_mdtm(resource stream, string filename)
+ Returns the last modification time of the file, or -1 on error */
+PHP_FUNCTION(ftp_mdtm)
+{
+ zval *z_ftp;
+ ftpbuf_t *ftp;
+ char *file;
+ int file_len;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rp", &z_ftp, &file, &file_len) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(ftp, ftpbuf_t*, &z_ftp, -1, le_ftpbuf_name, le_ftpbuf);
+
+ /* get file mod time */
+ RETURN_LONG(ftp_mdtm(ftp, file));
+}
+/* }}} */
+
+/* {{{ proto bool ftp_rename(resource stream, string src, string dest)
+ Renames the given file to a new path */
+PHP_FUNCTION(ftp_rename)
+{
+ zval *z_ftp;
+ ftpbuf_t *ftp;
+ char *src, *dest;
+ int src_len, dest_len;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rss", &z_ftp, &src, &src_len, &dest, &dest_len) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(ftp, ftpbuf_t*, &z_ftp, -1, le_ftpbuf_name, le_ftpbuf);
+
+ /* rename the file */
+ if (!ftp_rename(ftp, src, dest)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", ftp->inbuf);
+ RETURN_FALSE;
+ }
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto bool ftp_delete(resource stream, string file)
+ Deletes a file */
+PHP_FUNCTION(ftp_delete)
+{
+ zval *z_ftp;
+ ftpbuf_t *ftp;
+ char *file;
+ int file_len;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &z_ftp, &file, &file_len) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(ftp, ftpbuf_t*, &z_ftp, -1, le_ftpbuf_name, le_ftpbuf);
+
+ /* delete the file */
+ if (!ftp_delete(ftp, file)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", ftp->inbuf);
+ RETURN_FALSE;
+ }
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto bool ftp_site(resource stream, string cmd)
+ Sends a SITE command to the server */
+PHP_FUNCTION(ftp_site)
+{
+ zval *z_ftp;
+ ftpbuf_t *ftp;
+ char *cmd;
+ int cmd_len;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rs", &z_ftp, &cmd, &cmd_len) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(ftp, ftpbuf_t*, &z_ftp, -1, le_ftpbuf_name, le_ftpbuf);
+
+ /* send the site command */
+ if (!ftp_site(ftp, cmd)) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "%s", ftp->inbuf);
+ RETURN_FALSE;
+ }
+
+ RETURN_TRUE;
+}
+/* }}} */
+
+/* {{{ proto bool ftp_close(resource stream)
+ Closes the FTP stream */
+PHP_FUNCTION(ftp_close)
+{
+ zval *z_ftp;
+ ftpbuf_t *ftp;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "r", &z_ftp) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(ftp, ftpbuf_t*, &z_ftp, -1, le_ftpbuf_name, le_ftpbuf);
+
+ ftp_quit(ftp);
+
+ RETURN_BOOL(zend_list_delete(Z_LVAL_P(z_ftp)) == SUCCESS);
+}
+/* }}} */
+
+/* {{{ proto bool ftp_set_option(resource stream, int option, mixed value)
+ Sets an FTP option */
+PHP_FUNCTION(ftp_set_option)
+{
+ zval *z_ftp, *z_value;
+ long option;
+ ftpbuf_t *ftp;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rlz", &z_ftp, &option, &z_value) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(ftp, ftpbuf_t*, &z_ftp, -1, le_ftpbuf_name, le_ftpbuf);
+
+ switch (option) {
+ case PHP_FTP_OPT_TIMEOUT_SEC:
+ if (Z_TYPE_P(z_value) != IS_LONG) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Option TIMEOUT_SEC expects value of type long, %s given",
+ zend_zval_type_name(z_value));
+ RETURN_FALSE;
+ }
+ if (Z_LVAL_P(z_value) <= 0) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Timeout has to be greater than 0");
+ RETURN_FALSE;
+ }
+ ftp->timeout_sec = Z_LVAL_P(z_value);
+ RETURN_TRUE;
+ break;
+ case PHP_FTP_OPT_AUTOSEEK:
+ if (Z_TYPE_P(z_value) != IS_BOOL) {
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Option AUTOSEEK expects value of type boolean, %s given",
+ zend_zval_type_name(z_value));
+ RETURN_FALSE;
+ }
+ ftp->autoseek = Z_LVAL_P(z_value);
+ RETURN_TRUE;
+ break;
+ default:
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown option '%ld'", option);
+ RETURN_FALSE;
+ break;
+ }
+}
+/* }}} */
+
+/* {{{ proto mixed ftp_get_option(resource stream, int option)
+ Gets an FTP option */
+PHP_FUNCTION(ftp_get_option)
+{
+ zval *z_ftp;
+ long option;
+ ftpbuf_t *ftp;
+
+ if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rl", &z_ftp, &option) == FAILURE) {
+ return;
+ }
+
+ ZEND_FETCH_RESOURCE(ftp, ftpbuf_t*, &z_ftp, -1, le_ftpbuf_name, le_ftpbuf);
+
+ switch (option) {
+ case PHP_FTP_OPT_TIMEOUT_SEC:
+ RETURN_LONG(ftp->timeout_sec);
+ break;
+ case PHP_FTP_OPT_AUTOSEEK:
+ RETURN_BOOL(ftp->autoseek);
+ break;
+ default:
+ php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unknown option '%ld'", option);
+ RETURN_FALSE;
+ break;
+ }
+}
+/* }}} */
+
+#endif /* HAVE_FTP */
+
+/*
+ * Local variables:
+ * tab-width: 4
+ * c-basic-offset: 4
+ * indent-tabs-mode: t
+ * End:
+ */
diff --git a/ext/ftp/php_ftp.h b/ext/ftp/php_ftp.h
new file mode 100644
index 0000000..41f65f9
--- /dev/null
+++ b/ext/ftp/php_ftp.h
@@ -0,0 +1,79 @@
+/*
+ +----------------------------------------------------------------------+
+ | PHP Version 5 |
+ +----------------------------------------------------------------------+
+ | Copyright (c) 1997-2013 The PHP Group |
+ +----------------------------------------------------------------------+
+ | This source file is subject to version 3.01 of the PHP license, |
+ | that is bundled with this package in the file LICENSE, and is |
+ | available through the world-wide-web at the following url: |
+ | http://www.php.net/license/3_01.txt |
+ | If you did not receive a copy of the PHP license and are unable to |
+ | obtain it through the world-wide-web, please send a note to |
+ | license@php.net so we can mail you a copy immediately. |
+ +----------------------------------------------------------------------+
+ | Authors: Andrew Skalski <askalski@chek.com> |
+ | Stefan Esser <sesser@php.net> (resume functions) |
+ +----------------------------------------------------------------------+
+ */
+
+/* $Id$ */
+
+#ifndef _INCLUDED_FTP_H
+#define _INCLUDED_FTP_H
+
+#if HAVE_FTP
+
+extern zend_module_entry php_ftp_module_entry;
+#define php_ftp_module_ptr &php_ftp_module_entry
+
+#define PHP_FTP_OPT_TIMEOUT_SEC 0
+#define PHP_FTP_OPT_AUTOSEEK 1
+#define PHP_FTP_AUTORESUME -1
+
+PHP_MINIT_FUNCTION(ftp);
+PHP_MINFO_FUNCTION(ftp);
+
+PHP_FUNCTION(ftp_connect);
+#ifdef HAVE_OPENSSL_EXT
+PHP_FUNCTION(ftp_ssl_connect);
+#endif
+PHP_FUNCTION(ftp_login);
+PHP_FUNCTION(ftp_pwd);
+PHP_FUNCTION(ftp_cdup);
+PHP_FUNCTION(ftp_chdir);
+PHP_FUNCTION(ftp_exec);
+PHP_FUNCTION(ftp_raw);
+PHP_FUNCTION(ftp_mkdir);
+PHP_FUNCTION(ftp_rmdir);
+PHP_FUNCTION(ftp_chmod);
+PHP_FUNCTION(ftp_alloc);
+PHP_FUNCTION(ftp_nlist);
+PHP_FUNCTION(ftp_rawlist);
+PHP_FUNCTION(ftp_systype);
+PHP_FUNCTION(ftp_pasv);
+PHP_FUNCTION(ftp_get);
+PHP_FUNCTION(ftp_fget);
+PHP_FUNCTION(ftp_put);
+PHP_FUNCTION(ftp_fput);
+PHP_FUNCTION(ftp_size);
+PHP_FUNCTION(ftp_mdtm);
+PHP_FUNCTION(ftp_rename);
+PHP_FUNCTION(ftp_delete);
+PHP_FUNCTION(ftp_site);
+PHP_FUNCTION(ftp_close);
+PHP_FUNCTION(ftp_set_option);
+PHP_FUNCTION(ftp_get_option);
+PHP_FUNCTION(ftp_nb_get);
+PHP_FUNCTION(ftp_nb_fget);
+PHP_FUNCTION(ftp_nb_put);
+PHP_FUNCTION(ftp_nb_fput);
+PHP_FUNCTION(ftp_nb_continue);
+
+#define phpext_ftp_ptr php_ftp_module_ptr
+
+#else
+#define php_ftp_module_ptr NULL
+#endif /* HAVE_FTP */
+
+#endif
diff --git a/ext/ftp/tests/001.phpt b/ext/ftp/tests/001.phpt
new file mode 100644
index 0000000..38475d3
--- /dev/null
+++ b/ext/ftp/tests/001.phpt
@@ -0,0 +1,36 @@
+--TEST--
+FTP login
+--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_raw($ftp, 'HELP'));
+var_dump(ftp_raw($ftp, 'HELP HELP'));
+
+var_dump(ftp_close($ftp));
+?>
+--EXPECT--
+bool(true)
+array(4) {
+ [0]=>
+ string(55) "214-There is help available for the following commands:"
+ [1]=>
+ string(5) " USER"
+ [2]=>
+ string(5) " HELP"
+ [3]=>
+ string(15) "214 end of list"
+}
+array(1) {
+ [0]=>
+ string(39) "214 Syntax: HELP [<SP> <string>] <CRLF>"
+}
+bool(true)
diff --git a/ext/ftp/tests/002.phpt b/ext/ftp/tests/002.phpt
new file mode 100644
index 0000000..e27113f
--- /dev/null
+++ b/ext/ftp/tests/002.phpt
@@ -0,0 +1,38 @@
+--TEST--
+FTP login (SSL)
+--SKIPIF--
+<?php
+$ssl = 1;
+require 'skipif.inc';
+?>
+--FILE--
+<?php
+$ssl = 1;
+require 'server.inc';
+
+$ftp = ftp_ssl_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_raw($ftp, 'HELP'));
+var_dump(ftp_raw($ftp, 'HELP HELP'));
+
+var_dump(ftp_close($ftp));
+?>
+--EXPECT--
+bool(true)
+array(4) {
+ [0]=>
+ string(55) "214-There is help available for the following commands:"
+ [1]=>
+ string(5) " USER"
+ [2]=>
+ string(5) " HELP"
+ [3]=>
+ string(15) "214 end of list"
+}
+array(1) {
+ [0]=>
+ string(39) "214 Syntax: HELP [<SP> <string>] <CRLF>"
+}
+bool(true)
diff --git a/ext/ftp/tests/003.phpt b/ext/ftp/tests/003.phpt
new file mode 100644
index 0000000..db57ed5
--- /dev/null
+++ b/ext/ftp/tests/003.phpt
@@ -0,0 +1,43 @@
+--TEST--
+FTP cwd
+--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_pwd($ftp));
+
+var_dump(ftp_chdir($ftp, 'mydir'));
+var_dump(ftp_pwd($ftp));
+
+var_dump(ftp_chdir($ftp, '/xpto/mydir'));
+var_dump(ftp_pwd($ftp));
+
+var_dump(ftp_cdup($ftp));
+var_dump(ftp_pwd($ftp));
+
+var_dump(ftp_chdir($ftp, '..'));
+var_dump(ftp_pwd($ftp));
+
+var_dump(ftp_close($ftp));
+?>
+--EXPECT--
+bool(true)
+string(1) "/"
+bool(true)
+string(6) "/mydir"
+bool(true)
+string(11) "/xpto/mydir"
+bool(true)
+string(5) "/xpto"
+bool(true)
+string(1) "/"
+bool(true)
diff --git a/ext/ftp/tests/004.phpt b/ext/ftp/tests/004.phpt
new file mode 100644
index 0000000..df6951b
--- /dev/null
+++ b/ext/ftp/tests/004.phpt
@@ -0,0 +1,79 @@
+--TEST--
+FTP with bogus parameters
+--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_systype($ftp));
+
+/* some bogus usage */
+var_dump(ftp_alloc($ftp, array()));
+var_dump(ftp_cdup($ftp, 0));
+var_dump(ftp_chdir($ftp, array()));
+var_dump(ftp_chmod($ftp, 0666));
+var_dump(ftp_get($ftp, 1234,12));
+var_dump(ftp_close());
+var_dump(ftp_connect('sfjkfjaksfjkasjf'));
+var_dump(ftp_delete($ftp, array()));
+var_dump(ftp_exec($ftp, array()));
+
+var_dump(ftp_systype($ftp, 0));
+var_dump(ftp_pwd($ftp, array()));
+
+var_dump(ftp_login($ftp));
+var_dump(ftp_login($ftp, 'user', 'bogus'));
+
+var_dump(ftp_quit($ftp));
+?>
+--EXPECTF--
+bool(true)
+string(4) "UNIX"
+
+Warning: ftp_alloc() expects parameter 2 to be long, array given in %s004.php on line 12
+bool(false)
+
+Warning: ftp_cdup() expects exactly 1 parameter, 2 given in %s004.php on line 13
+NULL
+
+Warning: ftp_chdir() expects parameter 2 to be string, array given in %s004.php on line 14
+NULL
+
+Warning: ftp_chmod() expects exactly 3 parameters, 2 given in %s on line %d
+bool(false)
+
+Warning: ftp_get() expects at least 4 parameters, 3 given in %s on line %d
+NULL
+
+Warning: ftp_close() expects exactly 1 parameter, 0 given in %s004.php on line 17
+NULL
+
+Warning: ftp_connect(): php_network_getaddresses: getaddrinfo failed: %s in %s004.php on line 18
+bool(false)
+
+Warning: ftp_delete() expects parameter 2 to be string, array given in %s004.php on line 19
+NULL
+
+Warning: ftp_exec() expects parameter 2 to be string, array given in %s004.php on line 20
+NULL
+
+Warning: ftp_systype() expects exactly 1 parameter, 2 given in %s004.php on line 22
+NULL
+
+Warning: ftp_pwd() expects exactly 1 parameter, 2 given in %s004.php on line 23
+NULL
+
+Warning: ftp_login() expects exactly 3 parameters, 1 given in %s004.php on line 25
+NULL
+
+Warning: ftp_login(): Not logged in. in %s004.php on line 26
+bool(false)
+bool(true)
diff --git a/ext/ftp/tests/005.phpt b/ext/ftp/tests/005.phpt
new file mode 100644
index 0000000..bbc11e8
--- /dev/null
+++ b/ext/ftp/tests/005.phpt
@@ -0,0 +1,86 @@
+--TEST--
+FTP with bogus server responses
+--SKIPIF--
+<?php
+require 'skipif.inc';
+?>
+--FILE--
+<?php
+$bogus = 1;
+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, 'anonymous', 'mail@example.com'));
+
+var_dump(ftp_alloc($ftp, 400));
+var_dump(ftp_cdup($ftp));
+var_dump(ftp_chdir($ftp, '~'));
+var_dump(ftp_chmod($ftp, 0666, 'x'));
+var_dump(ftp_delete($ftp, 'x'));
+var_dump(ftp_exec($ftp, 'x'));
+var_dump(ftp_fget($ftp, STDOUT, 'x', 0));
+var_dump(ftp_fput($ftp, 'x', fopen(__FILE__, 'r'), 0));
+var_dump(ftp_get($ftp, 'x', 'y', 0));
+var_dump(ftp_mdtm($ftp, 'x'));
+var_dump(ftp_mkdir($ftp, 'x'));
+var_dump(ftp_nb_continue($ftp));
+var_dump(ftp_nb_fget($ftp, STDOUT, 'x', 0));
+var_dump(ftp_nb_fput($ftp, 'x', fopen(__FILE__, 'r'), 0));
+var_dump(ftp_systype($ftp));
+var_dump(ftp_pwd($ftp));
+var_dump(ftp_size($ftp, ''));
+var_dump(ftp_rmdir($ftp, ''));
+
+?>
+--EXPECTF--
+bool(true)
+bool(false)
+
+Warning: ftp_cdup(): Command not implemented (1). in %s005.php on line 11
+bool(false)
+
+Warning: ftp_chdir(): Command not implemented (2). in %s005.php on line 12
+bool(false)
+
+Warning: ftp_chmod(): Command not implemented (3). in %s005.php on line 13
+bool(false)
+
+Warning: ftp_delete(): Command not implemented (4). in %s005.php on line 14
+bool(false)
+
+Warning: ftp_exec(): Command not implemented (5). in %s005.php on line 15
+bool(false)
+
+Warning: ftp_fget(): Mode must be FTP_ASCII or FTP_BINARY in %s005.php on line 16
+bool(false)
+
+Warning: ftp_fput(): Mode must be FTP_ASCII or FTP_BINARY in %s005.php on line 17
+bool(false)
+
+Warning: ftp_get(): Mode must be FTP_ASCII or FTP_BINARY in %s005.php on line 18
+bool(false)
+int(-1)
+
+Warning: ftp_mkdir(): Command not implemented (7). in %s005.php on line 20
+bool(false)
+
+Warning: ftp_nb_continue(): no nbronous transfer to continue. in %s005.php on line 21
+int(0)
+
+Warning: ftp_nb_fget(): Mode must be FTP_ASCII or FTP_BINARY in %s005.php on line 22
+bool(false)
+
+Warning: ftp_nb_fput(): Mode must be FTP_ASCII or FTP_BINARY in %s005.php on line 23
+bool(false)
+
+Warning: ftp_systype(): Command not implemented (8). in %s005.php on line 24
+bool(false)
+
+Warning: ftp_pwd(): Command not implemented (9). in %s005.php on line 25
+bool(false)
+int(-1)
+
+Warning: ftp_rmdir(): Command not implemented (11). in %s005.php on line 27
+bool(false)
diff --git a/ext/ftp/tests/006.phpt b/ext/ftp/tests/006.phpt
new file mode 100644
index 0000000..4d31be7
--- /dev/null
+++ b/ext/ftp/tests/006.phpt
@@ -0,0 +1,100 @@
+--TEST--
+FTP with bogus parameters
+--SKIPIF--
+<?php
+require 'skipif.inc';
+?>
+--FILE--
+<?php
+$ftp=null;
+
+var_dump(ftp_connect(array()));
+var_dump(ftp_connect('127.0.0.1', 0, -3));
+var_dump(ftp_raw($ftp));
+var_dump(ftp_mkdir($ftp));
+var_dump(ftp_rmdir($ftp));
+var_dump(ftp_nlist($ftp));
+var_dump(ftp_rawlist($ftp));
+var_dump(ftp_fget($ftp));
+var_dump(ftp_nb_fget($ftp));
+var_dump(ftp_nb_get($ftp));
+var_dump(ftp_pasv($ftp));
+var_dump(ftp_nb_continue());
+var_dump(ftp_fput());
+var_dump(ftp_nb_fput($ftp));
+var_dump(ftp_put($ftp));
+var_dump(ftp_nb_put($ftp));
+var_dump(ftp_size($ftp));
+var_dump(ftp_mdtm($ftp));
+var_dump(ftp_rename($ftp));
+var_dump(ftp_site($ftp));
+var_dump(ftp_set_option($ftp));
+var_dump(ftp_get_option($ftp));
+
+?>
+--EXPECTF--
+Warning: ftp_connect() expects parameter 1 to be string, array given in %s006.php on line 4
+NULL
+
+Warning: ftp_connect(): Timeout has to be greater than 0 in %s006.php on line 5
+bool(false)
+
+Warning: ftp_raw() expects exactly 2 parameters, 1 given in %s006.php on line 6
+NULL
+
+Warning: ftp_mkdir() expects exactly 2 parameters, 1 given in %s006.php on line 7
+NULL
+
+Warning: ftp_rmdir() expects exactly 2 parameters, 1 given in %s006.php on line 8
+NULL
+
+Warning: ftp_nlist() expects exactly 2 parameters, 1 given in %s006.php on line 9
+NULL
+
+Warning: ftp_rawlist() expects at least 2 parameters, 1 given in %s006.php on line 10
+NULL
+
+Warning: ftp_fget() expects at least 4 parameters, 1 given in %s006.php on line 11
+NULL
+
+Warning: ftp_nb_fget() expects at least 4 parameters, 1 given in %s006.php on line 12
+NULL
+
+Warning: ftp_nb_get() expects at least 4 parameters, 1 given in %s006.php on line 13
+NULL
+
+Warning: ftp_pasv() expects exactly 2 parameters, 1 given in %s006.php on line 14
+NULL
+
+Warning: ftp_nb_continue() expects exactly 1 parameter, 0 given in %s006.php on line 15
+NULL
+
+Warning: ftp_fput() expects at least 4 parameters, 0 given in %s006.php on line 16
+NULL
+
+Warning: ftp_nb_fput() expects at least 4 parameters, 1 given in %s006.php on line 17
+NULL
+
+Warning: ftp_put() expects at least 4 parameters, 1 given in %s006.php on line 18
+NULL
+
+Warning: ftp_nb_put() expects at least 4 parameters, 1 given in %s006.php on line 19
+NULL
+
+Warning: ftp_size() expects exactly 2 parameters, 1 given in %s006.php on line 20
+NULL
+
+Warning: ftp_mdtm() expects exactly 2 parameters, 1 given in %s006.php on line 21
+NULL
+
+Warning: ftp_rename() expects exactly 3 parameters, 1 given in %s006.php on line 22
+NULL
+
+Warning: ftp_site() expects exactly 2 parameters, 1 given in %s006.php on line 23
+NULL
+
+Warning: ftp_set_option() expects exactly 3 parameters, 1 given in %s006.php on line 24
+NULL
+
+Warning: ftp_get_option() expects exactly 2 parameters, 1 given in %s006.php on line 25
+NULL
diff --git a/ext/ftp/tests/bug27809.phpt b/ext/ftp/tests/bug27809.phpt
new file mode 100644
index 0000000..ff9765c
--- /dev/null
+++ b/ext/ftp/tests/bug27809.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Bug #27809 (ftp_systype returns null)
+--SKIPIF--
+<?php
+require 'skipif.inc';
+?>
+--FILE--
+<?php
+$bug27809=true;
+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, 'anonymous', 'IEUser@'));
+var_dump(ftp_systype($ftp));
+
+?>
+--EXPECT--
+bool(true)
+string(6) "OS/400"
diff --git a/ext/ftp/tests/bug37799.phpt b/ext/ftp/tests/bug37799.phpt
new file mode 100644
index 0000000..bc9ce00
--- /dev/null
+++ b/ext/ftp/tests/bug37799.phpt
@@ -0,0 +1,22 @@
+--TEST--
+Bug #37799 (ftp_ssl_connect() falls back to non-ssl connection)
+--SKIPIF--
+<?php
+$ssl = 1;
+require 'skipif.inc';
+?>
+--FILE--
+<?php
+$bug37799=$ssl=1;
+require 'server.inc';
+
+$ftp = ftp_ssl_connect('127.0.0.1', $port);
+if (!$ftp) die("Couldn't connect to the server");
+
+var_dump(ftp_login($ftp, 'user', 'pass'));
+
+ftp_close($ftp);
+?>
+--EXPECTF--
+Warning: ftp_login(): bogus msg in %sbug37799.php on line 8
+bool(false)
diff --git a/ext/ftp/tests/bug39458-2.phpt b/ext/ftp/tests/bug39458-2.phpt
new file mode 100644
index 0000000..9e4be5d
--- /dev/null
+++ b/ext/ftp/tests/bug39458-2.phpt
@@ -0,0 +1,36 @@
+--TEST--
+Bug #39458 (ftp_nlist() returns false on empty directories (other server behaviour))
+--SKIPIF--
+<?php
+require 'skipif.inc';
+?>
+--FILE--
+<?php
+$bug39458=1;
+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_nlist($ftp, ''));
+var_dump(ftp_nlist($ftp, 'emptydir'));
+var_dump(ftp_nlist($ftp, 'bogusdir'));
+
+ftp_close($ftp);
+?>
+--EXPECT--
+bool(true)
+array(3) {
+ [0]=>
+ string(5) "file1"
+ [1]=>
+ string(5) "file1"
+ [2]=>
+ string(9) "file
+b0rk"
+}
+array(0) {
+}
+bool(false)
diff --git a/ext/ftp/tests/bug39458.phpt b/ext/ftp/tests/bug39458.phpt
new file mode 100644
index 0000000..8286649
--- /dev/null
+++ b/ext/ftp/tests/bug39458.phpt
@@ -0,0 +1,35 @@
+--TEST--
+Bug #39458 (ftp_nlist() returns 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_nlist($ftp, ''));
+var_dump(ftp_nlist($ftp, 'emptydir'));
+var_dump(ftp_nlist($ftp, 'bogusdir'));
+
+ftp_close($ftp);
+?>
+--EXPECT--
+bool(true)
+array(3) {
+ [0]=>
+ string(5) "file1"
+ [1]=>
+ string(5) "file1"
+ [2]=>
+ string(9) "file
+b0rk"
+}
+array(0) {
+}
+bool(false)
diff --git a/ext/ftp/tests/bug39583-2.phpt b/ext/ftp/tests/bug39583-2.phpt
new file mode 100644
index 0000000..0a8423d
--- /dev/null
+++ b/ext/ftp/tests/bug39583-2.phpt
@@ -0,0 +1,34 @@
+--TEST--
+Bug #39583 (FTP always transfers in binary mode)
+--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'));
+
+$source_file = __FILE__;
+$destination_file = basename(__FILE__);
+
+// upload the file
+$upload = ftp_put($ftp, $destination_file, $source_file, FTP_BINARY);
+
+// check upload status
+if (!$upload) {
+ echo "FTP upload has failed!";
+ } else {
+ echo "Uploaded $source_file as $destination_file";
+ }
+
+// close the FTP stream
+ftp_close($ftp);
+?>
+--EXPECTF--
+bool(true)
+Uploaded %sbug39583-2.php as bug39583-2.php
diff --git a/ext/ftp/tests/bug39583.phpt b/ext/ftp/tests/bug39583.phpt
new file mode 100644
index 0000000..b3af56e
--- /dev/null
+++ b/ext/ftp/tests/bug39583.phpt
@@ -0,0 +1,35 @@
+--TEST--
+Bug #39583 (FTP always transfers in binary mode)
+--SKIPIF--
+<?php
+require 'skipif.inc';
+?>
+--FILE--
+<?php
+$bug39583=1;
+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'));
+
+$source_file = __FILE__;
+$destination_file = basename(__FILE__);
+
+// upload the file
+$upload = ftp_put($ftp, $destination_file, $source_file, FTP_ASCII);
+
+// check upload status
+if (!$upload) {
+ echo "FTP upload has failed!";
+ } else {
+ echo "Uploaded $source_file as $destination_file";
+ }
+
+// close the FTP stream
+ftp_close($ftp);
+?>
+--EXPECTF--
+bool(true)
+Uploaded %sbug39583.php as bug39583.php
diff --git a/ext/ftp/tests/bug7216-2.phpt b/ext/ftp/tests/bug7216-2.phpt
new file mode 100644
index 0000000..23ab851
--- /dev/null
+++ b/ext/ftp/tests/bug7216-2.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Bug #7216 (ftp_mkdir returns nothing (2))
+--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, 'anonymous', 'IEUser@'));
+// test for the correct behavior this time
+var_dump(ftp_mkdir($ftp, 'CVS'));
+
+?>
+--EXPECT--
+bool(true)
+string(20) "/path/to/ftproot/CVS"
diff --git a/ext/ftp/tests/bug7216.phpt b/ext/ftp/tests/bug7216.phpt
new file mode 100644
index 0000000..000bb1d
--- /dev/null
+++ b/ext/ftp/tests/bug7216.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Bug #7216 (ftp_mkdir returns nothing)
+--SKIPIF--
+<?php
+require 'skipif.inc';
+?>
+--FILE--
+<?php
+$bug7216=true;
+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, 'anonymous', 'IEUser@'));
+var_dump(ftp_mkdir($ftp, 'CVS'));
+
+?>
+--EXPECT--
+bool(true)
+string(3) "CVS"
diff --git a/ext/ftp/tests/cert.pem b/ext/ftp/tests/cert.pem
new file mode 100644
index 0000000..94c61ff
--- /dev/null
+++ b/ext/ftp/tests/cert.pem
@@ -0,0 +1,48 @@
+-----BEGIN CERTIFICATE REQUEST-----
+MIIBmzCCAQQCAQAwWzELMAkGA1UEBhMCUFQxCzAJBgNVBAgTAkx4MQswCQYDVQQH
+EwJMeDEcMBoGA1UEChMTQSBtaW5oYSBlbXByZXNhLCBTQTEUMBIGA1UECxMLUEhQ
+IFFBIFRlYW0wgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAM9mfEOSYwXf58ch
+4NyO1QOU1XMfquz8OVpvMUITABLAevZpeQn6vZPHNyXHFQC0QC8scydK1rAYd2U+
+9K2aPub6ioMjYyjPpAE07l9EAAPUEBlqqsziB/wT8QjWkByyJEkYu+o0Wyjokhfn
+BMPvm52wLWUx9nvUeNDCftnKg1wxAgMBAAGgADANBgkqhkiG9w0BAQQFAAOBgQDD
+s1FeqPxnF2bWj8/dG8MyPaRfOAMVz1UsCZUciXIVG5LSIvR2qnMC3iEYt3s13sEq
+z8VJlNHa8nniE+VFNv093yIu+PlWXMEvb5y5EFqP2AYq3RAT+SJsSxGqIdzPZiKY
+INaktLCZmQ/E1v7/4hFzVRq9ydJI82DVS1nv282Whw==
+-----END CERTIFICATE REQUEST-----
+-----BEGIN CERTIFICATE-----
+MIIC4zCCAkygAwIBAgIBADANBgkqhkiG9w0BAQQFADBbMQswCQYDVQQGEwJQVDEL
+MAkGA1UECBMCTHgxCzAJBgNVBAcTAkx4MRwwGgYDVQQKExNBIG1pbmhhIGVtcHJl
+c2EsIFNBMRQwEgYDVQQLEwtQSFAgUUEgVGVhbTAeFw0wNjExMTkxODIzNTNaFw0w
+NzExMTkxODIzNTNaMFsxCzAJBgNVBAYTAlBUMQswCQYDVQQIEwJMeDELMAkGA1UE
+BxMCTHgxHDAaBgNVBAoTE0EgbWluaGEgZW1wcmVzYSwgU0ExFDASBgNVBAsTC1BI
+UCBRQSBUZWFtMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDPZnxDkmMF3+fH
+IeDcjtUDlNVzH6rs/DlabzFCEwASwHr2aXkJ+r2TxzclxxUAtEAvLHMnStawGHdl
+PvStmj7m+oqDI2Moz6QBNO5fRAAD1BAZaqrM4gf8E/EI1pAcsiRJGLvqNFso6JIX
+5wTD75udsC1lMfZ71HjQwn7ZyoNcMQIDAQABo4G2MIGzMB0GA1UdDgQWBBTIga5L
+q+Ub1SWXgNZRYCpq3c8Z+jCBgwYDVR0jBHwweoAUyIGuS6vlG9Ull4DWUWAqat3P
+GfqhX6RdMFsxCzAJBgNVBAYTAlBUMQswCQYDVQQIEwJMeDELMAkGA1UEBxMCTHgx
+HDAaBgNVBAoTE0EgbWluaGEgZW1wcmVzYSwgU0ExFDASBgNVBAsTC1BIUCBRQSBU
+ZWFtggEAMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEEBQADgYEAe6AA8aC3KDI8
+smd+7XWjaTSp1Q0uMkEZ2PEBzif2I1aPPqw1CQykJ1iDdC/8PJ1yEIezloP2XQoZ
+NjTaCO+uubay03ncoPTZvDUwExN9BYFAYgc2z3tLMHYbA7kM2sIbKys7ZQegLibr
+TSKYQOBeYA/FB9GHECJGU3zBRvYi+Og=
+-----END CERTIFICATE-----
+-----BEGIN RSA PRIVATE KEY-----
+Proc-Type: 4,ENCRYPTED
+DEK-Info: DES-EDE3-CBC,928762DB6DE222AD
+
+oOxNUBX0wrqmRqb3IEZMogc1bnVm6JoW6YFjGfHNcIz0jS7UPDhUFDR26y0dYujL
+LEgxOcYo8ItvGcXSRbs+3W7lISbosgkB0DOaKx5jVmOGwUVRergUUSY8rbf93FtP
+27CEvAfsU6do5HmlJ34mYZW1k+onCznlJXJkupQ5jmiily3GwEdr/5mMIVOmXQ6p
+xWkxHySDKyVbR0v4JY3SJLRBuhgofYNG5155PiqZ7KwYY4Aw60eVgINsvJCF9/8b
+kEj+lecHbBdAf7N82320Ga+F+VeFnUl0gWFjoIF9UFCO80+7ZvIGdGlyPkr4zMvt
+TsC1snJQdHg+IlT3sGayYrQANpTG6GPYhn3KEvK5aqq+bPEe5lija0gw34jbPCo+
+TjHR76lToxzubGZODyyF/rjl5KwUbqTCNuv1PX1jTx7n7sCbu+KHpqXMhTHLKtby
++Wh7WAfsVrbIW+P85/mkfhPbPZ2621f9cyStdFGgWU4dHdD00HIGOgAJvUSbC2Au
+oVUoKf2818t1s9aA4ptog04sNi+Ixu+z+3yYNLZj51j4ZX3KuXxLIiQvlvFQ8LQi
+RHGQk3u2W3iNtDKKUQjMPaB2FlVtC7FmtHBCpRmos6ld240DDyucqMdIDTMaqV0+
+sL4X+LIeBM/hP/IquRTuQBHBmgjkN4845ihTUJOanyKx605ANq/roHzXrbIxhR5p
+pcJLCBMLMWgdOCJMZRavSq04iXeNfP6Mk/joVpHS62Ljdfc94BBLfsOKOErA20Nq
+lfvbZqy2tI5IIDoq05S8FU0DYNqq/hyrv9Udo8IAo+WkBOABm0x/WA==
+-----END RSA PRIVATE KEY-----
+ \ No newline at end of file
diff --git a/ext/ftp/tests/ftp_alloc_basic1.phpt b/ext/ftp/tests/ftp_alloc_basic1.phpt
new file mode 100644
index 0000000..b2bdf74
--- /dev/null
+++ b/ext/ftp/tests/ftp_alloc_basic1.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Testing ftp_alloc returns true
+--CREDITS--
+Rodrigo Moyle <eu [at] rodrigorm [dot] com [dot] br>
+#testfest PHPSP on 2009-06-20
+--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");
+ftp_login($ftp, 'user', 'pass');
+
+var_dump(ftp_alloc($ftp, 1024));
+?>
+--EXPECT--
+bool(true) \ No newline at end of file
diff --git a/ext/ftp/tests/ftp_alloc_basic2.phpt b/ext/ftp/tests/ftp_alloc_basic2.phpt
new file mode 100644
index 0000000..b9e4253
--- /dev/null
+++ b/ext/ftp/tests/ftp_alloc_basic2.phpt
@@ -0,0 +1,23 @@
+--TEST--
+Testing ftp_alloc returns true
+--CREDITS--
+Rodrigo Moyle <eu [at] rodrigorm [dot] com [dot] br>
+#testfest PHPSP on 2009-06-20
+--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");
+ftp_login($ftp, 'user', 'pass');
+
+var_dump(ftp_alloc($ftp, 1024, $result));
+var_dump($result);
+?>
+--EXPECT--
+bool(true)
+string(20) "1024 bytes allocated" \ No newline at end of file
diff --git a/ext/ftp/tests/ftp_chmod_basic.phpt b/ext/ftp/tests/ftp_chmod_basic.phpt
new file mode 100644
index 0000000..baaa25a
--- /dev/null
+++ b/ext/ftp/tests/ftp_chmod_basic.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Testing ftp_chmod returns file mode
+--CREDITS--
+Rodrigo Moyle <eu [at] rodrigorm [dot] com [dot] br>
+#testfest PHPSP on 2009-06-20
+--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");
+ftp_login($ftp, 'user', 'pass');
+
+var_dump(ftp_chmod($ftp, 0644, 'test.txt'));
+?>
+--EXPECT--
+int(420)
diff --git a/ext/ftp/tests/ftp_exec_basic.phpt b/ext/ftp/tests/ftp_exec_basic.phpt
new file mode 100644
index 0000000..4a1f63e
--- /dev/null
+++ b/ext/ftp/tests/ftp_exec_basic.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Testing ftp_exec returns true
+--CREDITS--
+Rodrigo Moyle <eu [at] rodrigorm [dot] com [dot] br>
+#testfest PHPSP on 2009-06-20
+--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_exec($ftp, 'ls -al'));
+?>
+--EXPECT--
+bool(true) \ No newline at end of file
diff --git a/ext/ftp/tests/ftp_fget_basic.phpt b/ext/ftp/tests/ftp_fget_basic.phpt
new file mode 100644
index 0000000..d736f8c
--- /dev/null
+++ b/ext/ftp/tests/ftp_fget_basic.phpt
@@ -0,0 +1,43 @@
+--TEST--
+FTP ftp_fget file for both binary and ASCII transfer modes
+--CREDITS--
+Nathaniel McHugh
+--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'));
+
+//test simple text transfer
+$fp = tmpfile();
+var_dump(ftp_fget($ftp, $fp ,'a story.txt', FTP_ASCII));
+fseek($fp, 0);
+echo fgets($fp);
+
+$postition = ftell($fp);
+//test binary data transfer
+var_dump(ftp_fget($ftp, $fp, 'binary data.bin', FTP_BINARY));
+fseek($fp, $postition);
+var_dump(urlencode(fgets($fp)));
+
+//test non-existant file request
+ftp_fget($ftp, $fp ,'a warning.txt', FTP_ASCII);
+
+//remove file
+fclose($fp);
+?>
+--EXPECTF--
+bool(true)
+bool(true)
+For sale: baby shoes, never worn.
+bool(true)
+string(21) "BINARYFoo%00Bar%0D%0A"
+
+Warning: ftp_fget(): a warning: No such file or directory in %sftp_fget_basic.php on line %d
diff --git a/ext/ftp/tests/ftp_fget_basic1.phpt b/ext/ftp/tests/ftp_fget_basic1.phpt
new file mode 100644
index 0000000..475f718
--- /dev/null
+++ b/ext/ftp/tests/ftp_fget_basic1.phpt
@@ -0,0 +1,32 @@
+--TEST--
+Testing ftp_fget ignore autoresume if autoseek is switched off
+--CREDITS--
+Rodrigo Moyle <eu [at] rodrigorm [dot] com [dot] br>
+#testfest PHPSP on 2009-06-20
+--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");
+ftp_set_option($ftp, FTP_AUTOSEEK, false);
+
+$local_file = dirname(__FILE__) . DIRECTORY_SEPARATOR . "localfile.txt";
+$handle = fopen($local_file, 'w');
+
+var_dump(ftp_fget($ftp, $handle, 'fget.txt', FTP_ASCII, FTP_AUTORESUME));
+var_dump(file_get_contents($local_file));
+?>
+--CLEAN--
+<?php
+@unlink(dirname(__FILE__) . DIRECTORY_SEPARATOR . "localfile.txt");
+?>
+--EXPECT--
+bool(true)
+string(12) "ASCIIFooBar
+"
diff --git a/ext/ftp/tests/ftp_fget_basic2.phpt b/ext/ftp/tests/ftp_fget_basic2.phpt
new file mode 100644
index 0000000..00a2675
--- /dev/null
+++ b/ext/ftp/tests/ftp_fget_basic2.phpt
@@ -0,0 +1,32 @@
+--TEST--
+Testing ftp_fget autoresume
+--CREDITS--
+Rodrigo Moyle <eu [at] rodrigorm [dot] com [dot] br>
+#testfest PHPSP on 2009-06-20
+--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");
+
+$local_file = dirname(__FILE__) . DIRECTORY_SEPARATOR . "localfile.txt";
+file_put_contents($local_file, 'ASCIIFoo');
+$handle = fopen($local_file, 'a');
+
+var_dump(ftp_fget($ftp, $handle, 'fgetresume.txt', FTP_ASCII, FTP_AUTORESUME));
+var_dump(file_get_contents($local_file));
+?>
+--CLEAN--
+<?php
+@unlink(dirname(__FILE__) . DIRECTORY_SEPARATOR . "localfile.txt");
+?>
+--EXPECT--
+bool(true)
+string(12) "ASCIIFooBar
+"
diff --git a/ext/ftp/tests/ftp_fget_basic3.phpt b/ext/ftp/tests/ftp_fget_basic3.phpt
new file mode 100644
index 0000000..b709870
--- /dev/null
+++ b/ext/ftp/tests/ftp_fget_basic3.phpt
@@ -0,0 +1,32 @@
+--TEST--
+Testing ftp_fget resume parameter
+--CREDITS--
+Rodrigo Moyle <eu [at] rodrigorm [dot] com [dot] br>
+#testfest PHPSP on 2009-06-20
+--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");
+
+$local_file = dirname(__FILE__) . DIRECTORY_SEPARATOR . "localfile.txt";
+file_put_contents($local_file, 'ASCIIFoo');
+$handle = fopen($local_file, 'a');
+
+var_dump(ftp_fget($ftp, $handle, 'fgetresume.txt', FTP_ASCII, 8));
+var_dump(file_get_contents($local_file));
+?>
+--CLEAN--
+<?php
+@unlink(dirname(__FILE__) . DIRECTORY_SEPARATOR . "localfile.txt");
+?>
+--EXPECT--
+bool(true)
+string(12) "ASCIIFooBar
+"
diff --git a/ext/ftp/tests/ftp_get_basic.phpt b/ext/ftp/tests/ftp_get_basic.phpt
new file mode 100644
index 0000000..23fd8d0
--- /dev/null
+++ b/ext/ftp/tests/ftp_get_basic.phpt
@@ -0,0 +1,40 @@
+--TEST--
+FTP ftp_get file for both binary and ASCII transfer modes
+--CREDITS--
+Nathaniel McHugh
+--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'));
+
+//test simple text transfer
+$tmpfname = tempnam(dirname(__FILE__), "ftp_test");
+var_dump(ftp_get($ftp, $tmpfname ,'a story.txt', FTP_ASCII));
+echo file_get_contents($tmpfname);
+unlink($tmpfname);
+
+//test binary data transfer
+$tmpfname = tempnam(dirname(__FILE__), "ftp_test");
+var_dump(ftp_get($ftp, $tmpfname, 'binary data.bin', FTP_BINARY));
+var_dump(urlencode(file_get_contents($tmpfname)));
+unlink($tmpfname);
+
+//test non-existant file request
+ftp_get($ftp, $tmpfname ,'a warning.txt', FTP_ASCII);
+?>
+--EXPECTF--
+bool(true)
+bool(true)
+For sale: baby shoes, never worn.
+bool(true)
+string(21) "BINARYFoo%00Bar%0D%0A"
+
+Warning: ftp_get(): a warning: No such file or directory in %sftp_get_basic.php on line %d
diff --git a/ext/ftp/tests/ftp_mdtm_basic.phpt b/ext/ftp/tests/ftp_mdtm_basic.phpt
new file mode 100644
index 0000000..39aeb76
--- /dev/null
+++ b/ext/ftp/tests/ftp_mdtm_basic.phpt
@@ -0,0 +1,48 @@
+--TEST--
+Test the File Modification Time as described in http://tools.ietf.org/html/rfc3659#section-3.1
+--CREDITS--
+Nathaniel McHugh
+--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'));
+
+
+date_default_timezone_set('UTC');
+
+$time = ftp_mdtm($ftp, "A");
+echo date("F d Y H:i:s u",$time), PHP_EOL;
+
+$time = ftp_mdtm($ftp, "B");
+echo date("F d Y H:i:s u",$time), PHP_EOL;
+
+$time = ftp_mdtm($ftp, "C");
+echo date("F d Y H:i:s u",$time), PHP_EOL;
+
+$time = ftp_mdtm($ftp, "D");
+var_dump($time);
+
+$time = ftp_mdtm($ftp, "19990929043300 File6");
+echo date("F d Y H:i:s u",$time), PHP_EOL;
+
+$time = ftp_mdtm($ftp, "MdTm 19990929043300 file6");
+var_dump($time);
+
+?>
+--EXPECTF--
+bool(true)
+June 15 1998 10:00:45 000000
+June 15 1998 10:00:45 000000
+July 05 1998 13:23:16 000000
+int(-1)
+October 05 1999 21:31:02 000000
+int(-1)
diff --git a/ext/ftp/tests/ftp_nb_fget_basic1.phpt b/ext/ftp/tests/ftp_nb_fget_basic1.phpt
new file mode 100644
index 0000000..cac4eec
--- /dev/null
+++ b/ext/ftp/tests/ftp_nb_fget_basic1.phpt
@@ -0,0 +1,32 @@
+--TEST--
+Testing ftp_nb_fget ignore autoresume if autoseek is switched off
+--CREDITS--
+Rodrigo Moyle <eu [at] rodrigorm [dot] com [dot] br>
+#testfest PHPSP on 2009-06-20
+--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");
+ftp_set_option($ftp, FTP_AUTOSEEK, false);
+
+$local_file = dirname(__FILE__) . DIRECTORY_SEPARATOR . "localfile.txt";
+$handle = fopen($local_file, 'w');
+
+var_dump(ftp_nb_fget($ftp, $handle, 'fget.txt', FTP_ASCII, FTP_AUTORESUME));
+var_dump(file_get_contents($local_file));
+?>
+--CLEAN--
+<?php
+@unlink(dirname(__FILE__) . DIRECTORY_SEPARATOR . "localfile.txt");
+?>
+--EXPECT--
+int(2)
+string(12) "ASCIIFooBar
+"
diff --git a/ext/ftp/tests/ftp_nb_fget_basic2.phpt b/ext/ftp/tests/ftp_nb_fget_basic2.phpt
new file mode 100644
index 0000000..dc92f4e
--- /dev/null
+++ b/ext/ftp/tests/ftp_nb_fget_basic2.phpt
@@ -0,0 +1,32 @@
+--TEST--
+Testing ftp_nb_fget autoresume
+--CREDITS--
+Rodrigo Moyle <eu [at] rodrigorm [dot] com [dot] br>
+#testfest PHPSP on 2009-06-20
+--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");
+
+$local_file = dirname(__FILE__) . DIRECTORY_SEPARATOR . "localfile.txt";
+file_put_contents($local_file, 'ASCIIFoo');
+$handle = fopen($local_file, 'a');
+
+var_dump(ftp_nb_fget($ftp, $handle, 'fgetresume.txt', FTP_ASCII, FTP_AUTORESUME));
+var_dump(file_get_contents($local_file));
+?>
+--CLEAN--
+<?php
+@unlink(dirname(__FILE__) . DIRECTORY_SEPARATOR . "localfile.txt");
+?>
+--EXPECT--
+int(2)
+string(12) "ASCIIFooBar
+"
diff --git a/ext/ftp/tests/ftp_nb_fget_basic3.phpt b/ext/ftp/tests/ftp_nb_fget_basic3.phpt
new file mode 100644
index 0000000..d1a87c4
--- /dev/null
+++ b/ext/ftp/tests/ftp_nb_fget_basic3.phpt
@@ -0,0 +1,32 @@
+--TEST--
+Testing ftp_nb_fget resume parameter
+--CREDITS--
+Rodrigo Moyle <eu [at] rodrigorm [dot] com [dot] br>
+#testfest PHPSP on 2009-06-20
+--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");
+
+$local_file = dirname(__FILE__) . DIRECTORY_SEPARATOR . "localfile.txt";
+file_put_contents($local_file, 'ASCIIFoo');
+$handle = fopen($local_file, 'a');
+
+var_dump(ftp_nb_fget($ftp, $handle, 'fgetresume.txt', FTP_ASCII, 8));
+var_dump(file_get_contents($local_file));
+?>
+--CLEAN--
+<?php
+@unlink(dirname(__FILE__) . DIRECTORY_SEPARATOR . "localfile.txt");
+?>
+--EXPECT--
+int(2)
+string(12) "ASCIIFooBar
+"
diff --git a/ext/ftp/tests/ftp_rawlist_basic2.phpt b/ext/ftp/tests/ftp_rawlist_basic2.phpt
new file mode 100644
index 0000000..1d93643
--- /dev/null
+++ b/ext/ftp/tests/ftp_rawlist_basic2.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Testing ftp_rawlist returns false on server error
+--CREDITS--
+Rodrigo Moyle <eu [at] rodrigorm [dot] com [dot] br>
+#testfest PHPSP on 2009-06-20
+--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_rawlist($ftp, 'no_exists/'));
+?>
+--EXPECT--
+bool(false) \ No newline at end of file
diff --git a/ext/ftp/tests/ftp_rmdir_basic.phpt b/ext/ftp/tests/ftp_rmdir_basic.phpt
new file mode 100644
index 0000000..6626cb4
--- /dev/null
+++ b/ext/ftp/tests/ftp_rmdir_basic.phpt
@@ -0,0 +1,21 @@
+--TEST--
+Testing ftp_rmdir returns true
+--CREDITS--
+Rodrigo Moyle <eu [at] rodrigorm [dot] com [dot] br>
+#testfest PHPSP on 2009-06-20
+--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");
+ftp_login($ftp, 'user', 'pass');
+
+var_dump(ftp_rmdir($ftp, 'www/'));
+?>
+--EXPECT--
+bool(true) \ No newline at end of file
diff --git a/ext/ftp/tests/server.inc b/ext/ftp/tests/server.inc
new file mode 100644
index 0000000..8617880
--- /dev/null
+++ b/ext/ftp/tests/server.inc
@@ -0,0 +1,410 @@
+<?php
+
+$socket = null;
+$errno = 0;
+$context = stream_context_create(array('ssl' => array('local_cert' => dirname(__FILE__).'/cert.pem', 'passphrase' => 'pass')));
+
+for ($i=0; $i<10 && !$socket; ++$i) {
+ $port = rand(50000, 65535);
+
+ $socket = stream_socket_server("tcp://127.0.0.1:$port", $errno, $errstr, STREAM_SERVER_BIND|STREAM_SERVER_LISTEN, $context);
+}
+//set anther random port that is not the same as $port
+do{
+ $pasv_port = rand(50000, 65535);
+}while($pasv_port == $port);
+
+if (!$socket) {
+ die("could not start/bind the ftp server\n");
+}
+
+
+
+
+$pid = pcntl_fork();
+
+
+
+function pasv_listen($action){
+ global $pasv_port, $tmp_file;
+ $tmp_file = 'nm2.php';
+ $pid = pcntl_fork();
+ if($pid === 0){
+ $soc = stream_socket_server("tcp://127.0.0.1:$pasv_port");
+ $fs = stream_socket_accept($soc, 3);
+ switch ($action) {
+ case 'fget':
+ case 'get':
+ //listen for 3 seconds 3 seconds
+ fputs($fs, "I am passive.\r\n");
+ break;
+ case 'put':
+ file_put_contents($tmp_file, stream_get_contents($fs));
+ break;
+ case 'list':
+ fputs($fs, "drwxr-x--- 3 owner group 4096 Jul 12 12:16 .\r\n");
+ fputs($fs, "drwxr-x--- 3 owner group 4096 Jul 12 12:16 ..\r\n");
+ fputs($fs, "drwxr-x--- 3 owner group 4096 Jul 12 12:16 public_ftp\r\n");
+ break;
+ case 'list_null':
+ fputs($fs, "\r\n");
+ break;
+ }
+ fclose($fs);
+ exit;
+ }
+}
+
+
+
+if ($pid) {
+
+ function dump_and_exit($buf)
+ {
+ var_dump($buf);
+ fclose($GLOBALS['s']);
+ exit;
+ }
+
+ function anonymous()
+ {
+ return $GLOBALS['user'] === 'anonymous';
+ }
+
+ /* quick&dirty realpath() like function */
+ function change_dir($dir)
+ {
+ global $cwd;
+
+ if ($dir[0] == '/') {
+ $cwd = $dir;
+ return;
+ }
+
+ $cwd = "$cwd/$dir";
+
+ do {
+ $old = $cwd;
+ $cwd = preg_replace('@/?[^/]+/\.\.@', '', $cwd);
+ } while ($old != $cwd);
+
+ $cwd = strtr($cwd, array('//' => '/'));
+ if (!$cwd) $cwd = '/';
+ }
+
+ $s = stream_socket_accept($socket);
+
+ if (!$s) die("Error accepting a new connection\n");
+
+ fputs($s, "220----- PHP FTP server 0.3 -----\r\n220 Service ready\r\n");
+ $buf = fread($s, 2048);
+
+
+ function user_auth($buf) {
+ global $user, $s, $ssl, $bug37799;
+
+ if (!empty($ssl)) {
+ if ($buf !== "AUTH TLS\r\n") {
+ fputs($s, "500 Syntax error, command unrecognized.\r\n");
+ dump_and_exit($buf);
+ }
+
+ if (empty($bug37799)) {
+ fputs($s, "234 auth type accepted\r\n");
+ } else {
+ fputs($s, "666 dummy\r\n");
+ fputs($s, "666 bogus msg\r\n");
+ exit;
+ }
+
+ if (!stream_socket_enable_crypto($s, true, STREAM_CRYPTO_METHOD_SSLv23_SERVER)) {
+ die("SSLv23 handshake failed.\n");
+ }
+
+ if (!preg_match('/^PBSZ \d+\r\n$/', $buf = fread($s, 2048))) {
+ fputs($s, "501 bogus data\r\n");
+ dump_and_exit($buf);
+ }
+
+ fputs($s, "200 OK\r\n");
+ $buf = fread($s, 2048);
+
+ if ($buf !== "PROT P\r\n") {
+ fputs($s, "504 Wrong protection.\r\n");
+ dump_and_exit($buf);
+ }
+
+ fputs($s, "200 OK\r\n");
+
+ $buf = fread($s, 2048);
+ }
+
+ if (!preg_match('/^USER (\w+)\r\n$/', $buf, $m)) {
+ fputs($s, "500 Syntax error, command unrecognized.\r\n");
+ dump_and_exit($buf);
+ }
+ $user = $m[1];
+ if ($user !== 'user' && $user !== 'anonymous') {
+ fputs($s, "530 Not logged in.\r\n");
+ fclose($s);
+ exit;
+ }
+
+ if (anonymous()) {
+ fputs($s, "230 Anonymous user logged in\r\n");
+
+ } else {
+ fputs($s, "331 User name ok, need password\r\n");
+
+ if (!preg_match('/^PASS (\w+)\r\n$/', $buf = fread($s, 100), $m)) {
+ fputs($s, "500 Syntax error, command unrecognized.\r\n");
+ dump_and_exit($buf);
+ }
+
+ $pass = $m[1];
+ if ($pass === 'pass') {
+ fputs($s, "230 User logged in\r\n");
+ } else {
+ fputs($s, "530 Not logged in.\r\n");
+ fclose($s);
+ exit;
+ }
+ }
+ }
+
+ user_auth($buf);
+
+ $cwd = '/';
+ $num_bogus_cmds = 0;
+
+ while($buf = fread($s, 4098)) {
+ if (!empty($bogus)) {
+ fputs($s, "502 Command not implemented (".$num_bogus_cmds++.").\r\n");
+
+ } else if ($buf === "HELP\r\n") {
+ fputs($s, "214-There is help available for the following commands:\r\n");
+ fputs($s, " USER\r\n");
+ fputs($s, " HELP\r\n");
+ fputs($s, "214 end of list\r\n");
+
+ } elseif ($buf === "HELP HELP\r\n") {
+ fputs($s, "214 Syntax: HELP [<SP> <string>] <CRLF>\r\n");
+
+ } elseif ($buf === "PWD\r\n") {
+ fputs($s, "257 \"$cwd\" is current directory.\r\n");
+
+ } elseif ($buf === "CDUP\r\n") {
+ change_dir('..');
+ fputs($s, "250 CDUP command successful.\r\n");
+
+ } elseif ($buf === "SYST\r\n") {
+ if (isset($bug27809)) {
+ fputs($s, "215 OS/400 is the remote operating system. The TCP/IP version is \"V5R2M0\"\r\n");
+ } else {
+ fputs($s, "215 UNIX Type: L8.\r\n");
+ }
+
+ } elseif ($buf === "TYPE A\r\n") {
+ $ascii = true;
+ fputs($s, "200 OK\r\n");
+
+ } elseif ($buf === "TYPE I\r\n") {
+ $ascii = false;
+ fputs($s, "200 OK\r\n");
+
+ } elseif ($buf === "QUIT\r\n") {
+ break;
+
+ } elseif (preg_match("~^PORT (\d+),(\d+),(\d+),(\d+),(\d+),(\d+)\r\n$~", $buf, $m)) {
+ $host = "$m[1].$m[2].$m[3].$m[4]";
+ $port = ((int)$m[5] << 8) + (int)$m[6];
+ fputs($s, "200 OK.\r\n");
+
+ } elseif (preg_match("~^STOR ([\w/.-]+)\r\n$~", $buf, $m)) {
+ fputs($s, "150 File status okay; about to open data connection\r\n");
+
+ if(empty($pasv))
+ {
+ if (!$fs = stream_socket_client("tcp://$host:$port")) {
+ fputs($s, "425 Can't open data connection\r\n");
+ continue;
+ }
+
+ $data = stream_get_contents($fs);
+ $orig = file_get_contents(dirname(__FILE__).'/'.$m[1]);
+
+
+ if (isset($ascii) && !$ascii && $orig === $data) {
+ fputs($s, "226 Closing data Connection.\r\n");
+
+ } elseif ((!empty($ascii) || isset($bug39583)) && $data === strtr($orig, array("\r\n" => "\n", "\r" => "\n", "\n" => "\r\n"))) {
+ fputs($s, "226 Closing data Connection.\r\n");
+
+ } else {
+ var_dump($data);
+ var_dump($orig);
+ fputs($s, "552 Requested file action aborted.\r\n");
+ }
+ fclose($fs);
+ }else{
+ $data = file_get_contents('nm2.php');
+ $orig = file_get_contents(dirname(__FILE__).'/'.$m[1]);
+ if ( $orig === $data) {
+ fputs($s, "226 Closing data Connection.\r\n");
+
+ } else {
+ var_dump($data);
+ var_dump($orig);
+ fputs($s, "552 Requested file action aborted.\r\n");
+ }
+ }
+
+ } elseif (preg_match("~^CWD ([A-Za-z./]+)\r\n$~", $buf, $m)) {
+ change_dir($m[1]);
+ fputs($s, "250 CWD command successful.\r\n");
+
+ } elseif (preg_match("~^NLST(?: ([A-Za-z./]+))?\r\n$~", $buf, $m)) {
+
+ if (isset($m[1]) && $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;
+ }
+
+ 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;
+ }
+
+ if (empty($m[1]) || $m[1] !== 'emptydir') {
+ fputs($fs, "file1\r\nfile1\r\nfile\nb0rk\r\n");
+ }
+
+ fputs($s, "226 Closing data Connection.\r\n");
+ fclose($fs);
+
+ } elseif (preg_match("~^MKD ([A-Za-z./]+)\r\n$~", $buf, $m)) {
+ if (isset($bug7216)) {
+ fputs($s, "257 OK.\r\n");
+ } else {
+ fputs($s, "257 \"/path/to/ftproot$cwd$m[1]\" created.\r\n");
+ }
+
+ } elseif (preg_match('/^USER /', $buf)) {
+ user_auth($buf);
+
+ } elseif (preg_match('/^MDTM ([\w\h]+)/', $buf, $matches)) {
+ switch ($matches [1]){
+ case "A":
+ fputs($s, "213 19980615100045.014\r\n");
+ break;
+ case "B":
+ fputs($s, "213 19980615100045.014\r\n");
+ break;
+ case "C":
+ fputs($s, "213 19980705132316\r\n");
+ break;
+ case "19990929043300 File6":
+ fputs($s, "213 19991005213102\r\n");
+ break;
+ default :
+ fputs($s, "550 No file named \"{$matches [1]}\"\r\n");
+ break;
+ }
+ }elseif (preg_match('/^RETR ([\w\h]+)/', $buf, $matches)) {
+ if(!empty($pasv)){
+ ;
+ }
+ else if (!$fs = stream_socket_client("tcp://$host:$port")) {
+ fputs($s, "425 Can't open data connection\r\n");
+ continue;
+ }
+
+ switch($matches[1]){
+
+ case "pasv":
+ fputs($s, "150 File status okay; about to open data connection.\r\n");
+ //the data connection is handled in another forked process
+ // called from outside this while loop
+ fputs($s, "226 Closing data Connection.\r\n");
+ break;
+ case "a story":
+ fputs($s, "150 File status okay; about to open data connection.\r\n");
+ fputs($fs, "For sale: baby shoes, never worn.\r\n");
+ fputs($s, "226 Closing data Connection.\r\n");
+ break;
+ case "binary data":
+ fputs($s, "150 File status okay; about to open data connection.\r\n");
+ $transfer_type = $ascii? 'ASCII' : 'BINARY' ;
+ fputs($fs, $transfer_type."Foo\0Bar\r\n");
+ fputs($s, "226 Closing data Connection.\r\n");
+ break;
+ case "fget":
+ fputs($s, "150 File status okay; about to open data connection.\r\n");
+ $transfer_type = $ascii? 'ASCII' : 'BINARY' ;
+ fputs($fs, $transfer_type."FooBar\r\n");
+ fputs($s, "226 Closing data Connection.\r\n");
+ break;
+ case "fgetresume":
+ fputs($s, "150 File status okay; about to open data connection.\r\n");
+ $transfer_type = $ascii? 'ASCII' : 'BINARY' ;
+ fputs($fs, "Bar\r\n");
+ fputs($s, "226 Closing data Connection.\r\n");
+ break;
+ default:
+ fputs($s, "550 {$matches[1]}: No such file or directory \r\n");
+ break;
+ }
+ if(isset($fs))
+ fclose($fs);
+
+
+ }elseif (preg_match('/^PASV/', $buf, $matches)) {
+ $port = $pasv_port;
+ $p2 = $port % ((int) 1 << 8);
+ $p1 = ($port-$p2)/((int) 1 << 8);
+ $host = "127.0.0.1";
+ fputs($s, "227 Entering Passive Mode. (127,0,0,1,{$p1},{$p2})\r\n");
+
+
+ } elseif (preg_match('/^SITE EXEC/', $buf, $matches)) {
+ fputs($s, "200 OK\r\n");
+
+ } elseif (preg_match('/^RMD/', $buf, $matches)) {
+ fputs($s, "250 OK\r\n");
+
+ } elseif (preg_match('/^SITE CHMOD/', $buf, $matches)) {
+ fputs($s, "200 OK\r\n");
+
+ } elseif (preg_match('/^ALLO (\d+)/', $buf, $matches)) {
+ fputs($s, "200 " . $matches[1] . " bytes allocated\r\n");
+
+ }elseif (preg_match('/^LIST www\//', $buf, $matches)) {
+ fputs($s, "150 Opening ASCII mode data connection for file list\r\n");
+ fputs($s, "226 Transfer complete\r\n");
+
+ }elseif (preg_match('/^LIST no_exists\//', $buf, $matches)) {
+ fputs($s, "425 Error establishing connection\r\n");
+
+ }elseif (preg_match('/^REST \d+/', $buf, $matches)) {
+ fputs($s, "350 OK\r\n");
+ }
+
+ else {
+ fputs($s, "500 Syntax error, command unrecognized.\r\n");
+ dump_and_exit($buf);
+ }
+ }
+ fclose($s);
+ exit;
+}
+
+fclose($socket);
+?> \ No newline at end of file
diff --git a/ext/ftp/tests/skipif.inc b/ext/ftp/tests/skipif.inc
new file mode 100644
index 0000000..9ab6ad9
--- /dev/null
+++ b/ext/ftp/tests/skipif.inc
@@ -0,0 +1,5 @@
+<?php
+if (!extension_loaded('ftp')) die('skip ftp extension not loaded');
+if (!function_exists('pcntl_fork')) die('skip fork not available');
+if (!empty($ssl) && !extension_loaded('openssl')) die('skip openssl extension not loaded');
+?>