diff options
-rw-r--r-- | ext/ftp/ftp.c | 289 | ||||
-rw-r--r-- | ext/ftp/ftp.h | 45 | ||||
-rw-r--r-- | ext/ftp/php_ftp.c | 270 | ||||
-rw-r--r-- | ext/ftp/php_ftp.h | 5 |
4 files changed, 601 insertions, 8 deletions
diff --git a/ext/ftp/ftp.c b/ext/ftp/ftp.c index 3effa5fcc4..573b47902f 100644 --- a/ext/ftp/ftp.c +++ b/ext/ftp/ftp.c @@ -126,6 +126,7 @@ ftp_open(const char *host, short port, long timeout_sec) /* Default Settings */ ftp->timeout_sec = timeout_sec; + ftp->async = 0; size = sizeof(ftp->localaddr); memset(&ftp->localaddr, 0, size); @@ -232,6 +233,8 @@ ftp_reinit(ftpbuf_t *ftp) ftp_gc(ftp); + ftp->async = 0; + if (!ftp_putcmd(ftp, "REIN", NULL)) return 0; if (!ftp_getresp(ftp) || ftp->resp != 220) @@ -1018,6 +1021,59 @@ my_recv(ftpbuf_t *ftp, int s, void *buf, size_t len) } /* }}} */ +/* {{{ data_available + */ +int +data_available(ftpbuf_t *ftp, int s) +{ + fd_set read_set; + struct timeval tv; + int n; + + tv.tv_sec = 0; + tv.tv_usec = 1; + + FD_ZERO(&read_set); + FD_SET(s, &read_set); + n = select(s + 1, &read_set, NULL, NULL, &tv); + if (n < 1) { +#ifndef PHP_WIN32 + if (n == 0) + errno = ETIMEDOUT; +#endif + return 0; + } + + return 1; +} +/* }}} */ +/* {{{ data_writeable + */ +int +data_writeable(ftpbuf_t *ftp, int s) +{ + fd_set write_set; + struct timeval tv; + int n; + + tv.tv_sec = 0; + tv.tv_usec = 1; + + FD_ZERO(&write_set); + FD_SET(s, &write_set); + n = select(s + 1, NULL, &write_set, NULL, &tv); + if (n < 1) { +#ifndef PHP_WIN32 + if (n == 0) + errno = ETIMEDOUT; +#endif + return 0; + } + + return 1; +} +/* }}} */ + /* {{{ my_accept */ int @@ -1309,6 +1365,239 @@ bail: } /* }}} */ +/* {{{ ftp_async_get + */ +int +ftp_async_get(ftpbuf_t *ftp, php_stream *outstream, const char *path, ftptype_t type, int resumepos) +{ + databuf_t *data = NULL; + char *ptr; + int lastch; + int rcvd; + char arg[11]; + TSRMLS_FETCH(); + + if (ftp == NULL) + goto bail; + + if (!ftp_type(ftp, type)) { + goto bail; + } + + if ((data = ftp_getdata(ftp)) == NULL) { + goto bail; + } + + if (resumepos>0) { + sprintf(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)) == NULL) { + goto bail; + } + + ftp->data = data; + ftp->stream = outstream; + ftp->lastch = 0; + ftp->async = 1; + + return (ftp_async_continue_read(ftp)); + +bail: + data_close(data); + return PHP_FTP_FAILED; +} +/* }}} */ + +/* {{{ ftp_aget + */ +int +ftp_async_continue_read(ftpbuf_t *ftp) +{ + databuf_t *data = NULL; + char *ptr; + int lastch; + int rcvd; + ftptype_t type; + TSRMLS_FETCH(); + + 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 { + php_stream_write(ftp->stream, data->buf, rcvd); + } + + ftp->lastch = lastch; + return PHP_FTP_MOREDATA; + } + + if (type == FTPTYPE_ASCII && lastch == '\r') + php_stream_putc(ftp->stream, '\r'); + + data = data_close(data); + + if (php_stream_error(ftp->stream)) { + goto bail; + } + + if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) { + goto bail; + } + + ftp->async = 0; + return PHP_FTP_FINISHED; +bail: + ftp->async = 0; + data_close(data); + return PHP_FTP_FAILED; +} +/* }}} */ + +/* {{{ ftp_async_put + */ +int +ftp_async_put(ftpbuf_t *ftp, const char *path, php_stream *instream, ftptype_t type, int startpos) +{ + databuf_t *data = NULL; + int size; + char *ptr; + int ch; + char arg[11]; + TSRMLS_FETCH(); + + if (ftp == NULL) + return 0; + + if (!ftp_type(ftp, type)) + goto bail; + + if ((data = ftp_getdata(ftp)) == NULL) + goto bail; + + if (startpos>0) { + sprintf(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)) == NULL) + goto bail; + + ftp->data = data; + ftp->stream = instream; + ftp->lastch = 0; + ftp->async = 1; + + return (ftp_async_continue_write(ftp)); + +bail: + data_close(data); + return PHP_FTP_FAILED; + +} +/* }}} */ + + +/* {{{ ftp_async_continue_write + */ +int +ftp_async_continue_write(ftpbuf_t *ftp) +{ + 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 ((ch = php_stream_getc(ftp->stream))!=EOF && !php_stream_eof(ftp->stream)) { + + 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; + + if (php_stream_error(ftp->stream)) + goto bail; + + ftp->data = data_close(ftp->data); + + if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) + goto bail; + + ftp->async = 0; + return PHP_FTP_FINISHED; +bail: + data_close(ftp->data); + ftp->async = 0; + return PHP_FTP_FAILED; +} +/* }}} */ + + #endif /* HAVE_FTP */ /* diff --git a/ext/ftp/ftp.h b/ext/ftp/ftp.h index 0dfb304810..a770dfc4e2 100644 --- a/ext/ftp/ftp.h +++ b/ext/ftp/ftp.h @@ -31,6 +31,9 @@ #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 @@ -40,6 +43,14 @@ typedef enum ftptype { FTPTYPE_IMAGE } ftptype_t; +typedef struct databuf +{ + int listener; /* listener socket */ + int fd; /* data connection */ + ftptype_t type; /* transfer type */ + char buf[FTP_BUFSIZE]; /* data buffer */ +} databuf_t; + typedef struct ftpbuf { int fd; /* control connection */ @@ -56,15 +67,15 @@ typedef struct ftpbuf php_sockaddr_storage pasvaddr; /* passive mode address */ long timeout_sec; /* User configureable timeout (seconds) */ int autoseek; /* User configureable autoseek flag */ + + int async; /* asyncronous transfer in progress */ + databuf_t *data; /* Data connection for asyncrounous transfers */ + php_stream *stream; /* output stream for asyncrounous transfers */ + int lastch; /* last char of previous call */ + int direction; /* recv = 0 / send = 1 */ + int closestream;/* close or not close stream */ } ftpbuf_t; -typedef struct databuf -{ - int listener; /* listener socket */ - int fd; /* data connection */ - ftptype_t type; /* transfer type */ - char buf[FTP_BUFSIZE]; /* data buffer */ -} databuf_t; /* open a FTP connection, returns ftpbuf (NULL on error) @@ -156,4 +167,24 @@ 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_async_get(ftpbuf_t *ftp, php_stream *outstream, const char *path, + ftptype_t type, int resumepos); + +/* 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_async_put(ftpbuf_t *ftp, const char *path, php_stream *instream, ftptype_t type, int startpos); + +/* continues a previous async_(f)get command + */ +int ftp_async_continue_read(ftpbuf_t *ftp); + +/* continues a previous async_(f)put command + */ +int ftp_async_continue_write(ftpbuf_t *ftp); + + #endif diff --git a/ext/ftp/php_ftp.c b/ext/ftp/php_ftp.c index cc51a84ffe..1dac9e3443 100644 --- a/ext/ftp/php_ftp.c +++ b/ext/ftp/php_ftp.c @@ -61,6 +61,11 @@ function_entry php_ftp_functions[] = { PHP_FE(ftp_close, NULL) PHP_FE(ftp_set_option, NULL) PHP_FE(ftp_get_option, NULL) + PHP_FE(ftp_async_fget, NULL) + PHP_FE(ftp_async_get, NULL) + PHP_FE(ftp_async_continue, NULL) + PHP_FE(ftp_async_put, NULL) + PHP_FE(ftp_async_fput, NULL) PHP_FALIAS(ftp_quit, ftp_close, NULL) {NULL, NULL, NULL} }; @@ -99,6 +104,9 @@ PHP_MINIT_FUNCTION(ftp) 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; } @@ -448,6 +456,53 @@ PHP_FUNCTION(ftp_fget) } /* }}} */ +/* {{{ proto bool ftp_async_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_async_fget) +{ + zval *z_ftp, *z_file; + ftpbuf_t *ftp; + ftptype_t xtype; + php_stream *stream; + char *file; + int file_len, mode, resumepos=0, ret; + + 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); + ZEND_FETCH_RESOURCE(stream, php_stream*, &z_file, -1, "File-Handle", php_file_le_stream()); + 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_async_get(ftp, stream, file, xtype, resumepos)) == PHP_FTP_FAILED || php_stream_error(stream)) { + php_error(E_WARNING, "%s(): %s", get_active_function_name(TSRMLS_C), 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) @@ -526,6 +581,107 @@ PHP_FUNCTION(ftp_get) } /* }}} */ +/* {{{ proto inf ftp_async_get(resource stream, string local_file, string remote_file, int mode[, int resume_pos]) + Retrieves a file from the FTP server asynchronly and writes it to a local file */ +PHP_FUNCTION(ftp_async_get) +{ + zval *z_ftp; + ftpbuf_t *ftp; + ftptype_t xtype; + php_stream *outstream; + char *local, *remote; + int local_len, remote_len, mode, resumepos=0, ret; + + 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; + } + + if (ftp->autoseek && resumepos) { + outstream = php_stream_fopen(local, "rb+", NULL); + if (outstream == NULL) { + outstream = php_stream_fopen(local, "wb", 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_fopen(local, "wb", NULL); + } + + if (outstream == NULL) { + php_error(E_WARNING, "%s(): Error opening %s", get_active_function_name(TSRMLS_C), local); + RETURN_FALSE; + } + + /* configuration */ + ftp->direction = 0; /* recv */ + ftp->closestream = 1; /* do close */ + + if ((ret = ftp_async_get(ftp, outstream, remote, xtype, resumepos)) == PHP_FTP_FAILED || php_stream_error(outstream)) { + php_stream_close(outstream); + php_error(E_WARNING, "%s(): %s", get_active_function_name(TSRMLS_C), ftp->inbuf); + RETURN_LONG(PHP_FTP_FAILED); + } + + if (ret == PHP_FTP_FINISHED) { + php_stream_close(outstream); + } + + RETURN_LONG(ret); +} +/* }}} */ + +/* {{{ proto int ftp_async_continue(resource stream) + Continues retrieving/sending a file asyncronously */ +PHP_FUNCTION(ftp_async_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->async) { + php_error(E_WARNING, "%s(): no asyncronous transfer to continue.", get_active_function_name(TSRMLS_C)); + RETURN_LONG(PHP_FTP_FAILED); + } + + if (ftp->direction) { + ret=ftp_async_continue_write(ftp); + } else { + ret=ftp_async_continue_read(ftp); + } + + if (ret != PHP_FTP_MOREDATA && ftp->closestream) { + php_stream_close(ftp->stream); + } + + if (ret == PHP_FTP_FAILED || php_stream_error(ftp->stream)) { + php_error(E_WARNING, "%s(): %s", get_active_function_name(TSRMLS_C), 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) @@ -572,6 +728,57 @@ PHP_FUNCTION(ftp_fput) } /* }}} */ +/* {{{ proto bool ftp_async_fput(resource stream, string remote_file, resource fp, int mode[, int startpos]) + Stores a file from an open file to the FTP server asyncronly */ +PHP_FUNCTION(ftp_async_fput) +{ + zval *z_ftp, *z_file; + ftpbuf_t *ftp; + ftptype_t xtype; + int mode, remote_len, startpos=0, ret; + 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); + ZEND_FETCH_RESOURCE(stream, php_stream*, &z_file, -1, "File-Handle", php_file_le_stream()); + 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_async_put(ftp, remote, stream, xtype, startpos)) == PHP_FTP_FAILED) || php_stream_error(stream)) { + php_error(E_WARNING, "%s(): %s", get_active_function_name(TSRMLS_C), 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) @@ -580,7 +787,7 @@ PHP_FUNCTION(ftp_put) ftpbuf_t *ftp; ftptype_t xtype; char *remote, *local; - int remote_len, local_len, mode, startpos=0; + int remote_len, local_len, mode, startpos=0, ret; php_stream * instream; if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rssl|l", &z_ftp, &remote, &remote_len, &local, &local_len, &mode, &startpos) == FAILURE) { @@ -625,6 +832,67 @@ PHP_FUNCTION(ftp_put) } /* }}} */ + +/* {{{ proto bool ftp_async_put(resource stream, string remote_file, string local_file, int mode[, int startpos]) + Stores a file on the FTP server */ +PHP_FUNCTION(ftp_async_put) +{ + zval *z_ftp; + ftpbuf_t *ftp; + ftptype_t xtype; + char *remote, *local; + int remote_len, local_len, mode, startpos=0, ret; + php_stream * instream; + + if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "rssl|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); + + instream = php_stream_fopen(local, "rb", NULL); + + if (instream == 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_async_put(ftp, remote, instream, xtype, startpos); + + if (ret != PHP_FTP_MOREDATA) { + php_stream_close(instream); + } + + if (ret == PHP_FTP_FAILED || php_stream_error(instream)) { + php_error(E_WARNING, "%s(): %s", get_active_function_name(TSRMLS_C), 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) diff --git a/ext/ftp/php_ftp.h b/ext/ftp/php_ftp.h index fdbb13b3e9..f22697779c 100644 --- a/ext/ftp/php_ftp.h +++ b/ext/ftp/php_ftp.h @@ -58,6 +58,11 @@ PHP_FUNCTION(ftp_site); PHP_FUNCTION(ftp_close); PHP_FUNCTION(ftp_set_option); PHP_FUNCTION(ftp_get_option); +PHP_FUNCTION(ftp_async_get); +PHP_FUNCTION(ftp_async_fget); +PHP_FUNCTION(ftp_async_put); +PHP_FUNCTION(ftp_async_fput); +PHP_FUNCTION(ftp_async_continue); #define phpext_ftp_ptr php_ftp_module_ptr |