diff --git a/ext/ftp/ftp.c b/ext/ftp/ftp.c index 17a3e10e0d61..4b171fd07f18 100644 --- a/ext/ftp/ftp.c +++ b/ext/ftp/ftp.c @@ -892,6 +892,11 @@ ftp_get(ftpbuf_t *ftp, php_stream *outstream, const char *path, const size_t pat if (ftp == NULL) { return 0; } + if (ftp->in_use) { + php_error_docref(NULL, E_WARNING, "FTP\\Connection is already in use"); + return 0; + } + ftp->in_use = true; if (!ftp_type(ftp, type)) { goto bail; } @@ -967,9 +972,11 @@ ftp_get(ftpbuf_t *ftp, php_stream *outstream, const char *path, const size_t pat goto bail; } + ftp->in_use = false; return 1; bail: data_close(ftp); + ftp->in_use = false; return 0; } /* }}} */ @@ -1057,6 +1064,11 @@ ftp_put(ftpbuf_t *ftp, const char *path, const size_t path_len, php_stream *inst if (ftp == NULL) { return 0; } + if (ftp->in_use) { + php_error_docref(NULL, E_WARNING, "FTP\\Connection is already in use"); + return 0; + } + ftp->in_use = true; if (!ftp_type(ftp, type)) { goto bail; } @@ -1097,9 +1109,11 @@ ftp_put(ftpbuf_t *ftp, const char *path, const size_t path_len, php_stream *inst if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250 && ftp->resp != 200)) { goto bail; } + ftp->in_use = false; return 1; bail: data_close(ftp); + ftp->in_use = false; return 0; } /* }}} */ @@ -1114,6 +1128,11 @@ ftp_append(ftpbuf_t *ftp, const char *path, const size_t path_len, php_stream *i if (ftp == NULL) { return 0; } + if (ftp->in_use) { + php_error_docref(NULL, E_WARNING, "FTP\\Connection is already in use"); + return 0; + } + ftp->in_use = true; if (!ftp_type(ftp, type)) { goto bail; } @@ -1141,9 +1160,11 @@ ftp_append(ftpbuf_t *ftp, const char *path, const size_t path_len, php_stream *i if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250 && ftp->resp != 200)) { goto bail; } + ftp->in_use = false; return 1; bail: data_close(ftp); + ftp->in_use = false; return 0; } /* }}} */ @@ -2223,11 +2244,17 @@ ftp_nb_continue_read(ftpbuf_t *ftp) data = ftp->data; + if (ftp->in_use) { + php_error_docref(NULL, E_WARNING, "FTP\\Connection is already in use"); + return PHP_FTP_FAILED; + } + /* check if there is already more data */ if (!data_available(ftp, data->fd, false)) { return PHP_FTP_MOREDATA; } + ftp->in_use = true; type = ftp->type; lastch = ftp->lastch; @@ -2251,6 +2278,7 @@ ftp_nb_continue_read(ftpbuf_t *ftp) } ftp->lastch = lastch; + ftp->in_use = false; return PHP_FTP_MOREDATA; } @@ -2265,9 +2293,11 @@ ftp_nb_continue_read(ftpbuf_t *ftp) } ftp->nb = 0; + ftp->in_use = false; return PHP_FTP_FINISHED; bail: ftp->nb = 0; + ftp->in_use = false; data_close(ftp); return PHP_FTP_FAILED; } @@ -2330,16 +2360,24 @@ ftp_nb_put(ftpbuf_t *ftp, const char *path, const size_t path_len, php_stream *i int ftp_nb_continue_write(ftpbuf_t *ftp) { + if (ftp->in_use) { + php_error_docref(NULL, E_WARNING, "FTP\\Connection is already in use"); + return PHP_FTP_FAILED; + } + /* check if we can write more data */ if (!data_writeable(ftp, ftp->data->fd)) { return PHP_FTP_MOREDATA; } + ftp->in_use = true; + if (ftp_send_stream_to_data_socket(ftp, ftp->data, ftp->stream, ftp->type, true) != SUCCESS) { goto bail; } if (!php_stream_eof(ftp->stream)) { + ftp->in_use = false; return PHP_FTP_MOREDATA; } @@ -2349,10 +2387,12 @@ ftp_nb_continue_write(ftpbuf_t *ftp) goto bail; } ftp->nb = 0; + ftp->in_use = false; return PHP_FTP_FINISHED; bail: data_close(ftp); ftp->nb = 0; + ftp->in_use = false; return PHP_FTP_FAILED; } /* }}} */ diff --git a/ext/ftp/ftp.h b/ext/ftp/ftp.h index 94abc588ca85..91aafad02b79 100644 --- a/ext/ftp/ftp.h +++ b/ext/ftp/ftp.h @@ -73,6 +73,7 @@ typedef struct ftpbuf databuf_t *data; /* Data connection for "nonblocking" transfers */ php_stream *stream; /* output stream for "nonblocking" transfers */ bool nb; /* "nonblocking" transfer in progress */ + bool in_use; /* engine transfer in progress; blocks re-entrant ftp_close */ char lastch; /* last char of previous call */ bool direction; /* recv = 0 / send = 1 */ bool closestream;/* close or not close stream */ diff --git a/ext/ftp/php_ftp.c b/ext/ftp/php_ftp.c index 8fa7675c6e9d..f985e5821561 100644 --- a/ext/ftp/php_ftp.c +++ b/ext/ftp/php_ftp.c @@ -1220,6 +1220,10 @@ PHP_FUNCTION(ftp_close) obj = ftp_object_from_zend_object(Z_OBJ_P(z_ftp)); if (obj->ftp) { + if (obj->ftp->in_use) { + zend_throw_error(NULL, "Cannot close FTP\\Connection while a transfer is in progress"); + RETURN_THROWS(); + } success = ftp_quit(obj->ftp); ftp_close(obj->ftp); obj->ftp = NULL; diff --git a/ext/ftp/tests/ftp_close_during_transfer.phpt b/ext/ftp/tests/ftp_close_during_transfer.phpt new file mode 100644 index 000000000000..72e410603921 --- /dev/null +++ b/ext/ftp/tests/ftp_close_during_transfer.phpt @@ -0,0 +1,44 @@ +--TEST-- +ftp_close() from a stream wrapper during a transfer throws instead of freeing the connection +--EXTENSIONS-- +ftp +pcntl +--FILE-- +getMessage(), "\n"; +} + +ftp_close($ftp); +echo "closed\n"; +?> +--EXPECT-- +bool(true) +Cannot close FTP\Connection while a transfer is in progress +closed diff --git a/ext/ftp/tests/ftp_nb_close_during_transfer.phpt b/ext/ftp/tests/ftp_nb_close_during_transfer.phpt new file mode 100644 index 000000000000..702648f69592 --- /dev/null +++ b/ext/ftp/tests/ftp_nb_close_during_transfer.phpt @@ -0,0 +1,44 @@ +--TEST-- +ftp_close() from a stream wrapper during a non-blocking transfer throws instead of freeing the connection +--EXTENSIONS-- +ftp +pcntl +--FILE-- +getMessage(), "\n"; +} + +ftp_close($ftp); +echo "closed\n"; +?> +--EXPECT-- +bool(true) +Cannot close FTP\Connection while a transfer is in progress +closed