diff options
Diffstat (limited to 'ext/ftp')
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'); +?> |