[EXPERIMENTAL] Added functions for asynchronous FTP transfers

This commit is contained in:
Stefan Esser 2002-07-26 22:00:25 +00:00
parent 8b853592b1
commit 80e9724ccc
4 changed files with 601 additions and 8 deletions

View File

@ -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 */
/*

View File

@ -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

View File

@ -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)

View File

@ -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