summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--ext/ftp/ftp.c289
-rw-r--r--ext/ftp/ftp.h45
-rw-r--r--ext/ftp/php_ftp.c270
-rw-r--r--ext/ftp/php_ftp.h5
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