php-src/ext/ftp/ftp.c
Kalle Sommer Nielsen 2104bea5d7 Remove Netware support
If this does not break the Unix system somehow, I'll be amazed. This should get most of it out, apologies for any errors this may cause on non-Windows ends which I cannot test atm.
2016-11-12 11:20:01 +01:00

2106 lines
38 KiB
C

/*
+----------------------------------------------------------------------+
| PHP Version 7 |
+----------------------------------------------------------------------+
| Copyright (c) 1997-2016 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>
#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
#ifdef HAVE_FTP_SSL
#include <openssl/ssl.h>
#endif
#include "ftp.h"
#include "ext/standard/fsock.h"
/* 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);
/* accepts the data connection, returns updated data buffer */
static databuf_t* data_accept(databuf_t *data, ftpbuf_t *ftp);
/* 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);
/* 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, zend_long timeout_sec)
{
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, STREAM_SOCKOP_NONE);
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, 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->stream && ftp->closestream) {
php_stream_close(ftp->stream);
}
if (ftp->fd != -1) {
#ifdef HAVE_FTP_SSL
if (ftp->ssl_active) {
SSL_shutdown(ftp->ssl_handle);
SSL_free(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)
{
#ifdef HAVE_FTP_SSL
SSL_CTX *ctx = NULL;
long ssl_ctx_options = SSL_OP_ALL;
int err, res;
zend_bool retry;
#endif
if (ftp == NULL) {
return 0;
}
#ifdef HAVE_FTP_SSL
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, 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);
/* allow SSL to re-use sessions */
SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_BOTH);
ftp->ssl_handle = SSL_new(ctx);
if (ftp->ssl_handle == NULL) {
php_error_docref(NULL, E_WARNING, "failed to create the SSL handle");
SSL_CTX_free(ctx);
return 0;
}
SSL_set_fd(ftp->ssl_handle, ftp->fd);
do {
res = SSL_connect(ftp->ssl_handle);
err = SSL_get_error(ftp->ssl_handle, res);
/* TODO check if handling other error codes would make sense */
switch (err) {
case SSL_ERROR_NONE:
retry = 0;
break;
case SSL_ERROR_ZERO_RETURN:
retry = 0;
SSL_shutdown(ftp->ssl_handle);
break;
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE: {
php_pollfd p;
int i;
p.fd = ftp->fd;
p.events = (err == SSL_ERROR_WANT_READ) ? (POLLIN|POLLPRI) : POLLOUT;
p.revents = 0;
i = php_poll2(&p, 1, 300);
retry = i > 0;
}
break;
default:
php_error_docref(NULL, E_WARNING, "SSL/TLS handshake failed");
SSL_shutdown(ftp->ssl_handle);
SSL_free(ftp->ssl_handle);
return 0;
}
} while (retry);
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);
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
*/
zend_string*
ftp_mkdir(ftpbuf_t *ftp, const char *dir)
{
char *mkd, *end;
zend_string *ret;
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) {
return zend_string_init(dir, strlen(dir), 0);
}
if ((end = strrchr(++mkd, '"')) == NULL) {
return NULL;
}
*end = 0;
ret = zend_string_init(mkd, end - mkd, 0);
*end = '"';
return ret;
}
/* }}} */
/* {{{ 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 zend_long size, zend_string **response)
{
char buffer[64];
if (ftp == NULL || size <= 0) {
return 0;
}
snprintf(buffer, sizeof(buffer) - 1, ZEND_LONG_FMT, size);
if (!ftp_putcmd(ftp, "ALLO", buffer)) {
return 0;
}
if (!ftp_getresp(ftp)) {
return 0;
}
if (response) {
*response = zend_string_init(ftp->inbuf, strlen(ftp->inbuf), 0);
}
if (ftp->resp < 200 || ftp->resp >= 300) {
return 0;
}
return 1;
}
/* }}} */
/* {{{ ftp_nlist
*/
char**
ftp_nlist(ftpbuf_t *ftp, const char *path)
{
return ftp_genlist(ftp, "NLST", path);
}
/* }}} */
/* {{{ ftp_list
*/
char**
ftp_list(ftpbuf_t *ftp, const char *path, int recursive)
{
return ftp_genlist(ftp, ((recursive) ? "LIST -R" : "LIST"), path);
}
/* }}} */
/* {{{ 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 (getpeername(ftp->fd, sa, &n) < 0) {
return 0;
}
#if HAVE_IPV6
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;
if (ftp->usepasvaddress) {
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, zend_long resumepos)
{
databuf_t *data = NULL;
size_t rcvd;
char arg[11];
if (ftp == NULL) {
return 0;
}
if (!ftp_type(ftp, type)) {
goto bail;
}
if ((data = ftp_getdata(ftp)) == NULL) {
goto bail;
}
ftp->data = data;
if (resumepos > 0) {
snprintf(arg, sizeof(arg), ZEND_LONG_FMT, 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;
}
while ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) {
if (rcvd == (size_t)-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, zend_long startpos)
{
databuf_t *data = NULL;
zend_long 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)) == NULL) {
goto bail;
}
ftp->data = data;
if (startpos > 0) {
snprintf(arg, sizeof(arg), ZEND_LONG_FMT, 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;
}
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
*/
zend_long
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 atol(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)
{
long 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)
{
if (ftp == NULL) {
return 0;
}
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)
{
zend_long size, sent;
int n;
#ifdef HAVE_FTP_SSL
int err;
zend_bool retry = 0;
SSL *handle = NULL;
php_socket_t fd;
#endif
size = len;
while (size) {
n = php_pollfd_for_ms(s, POLLOUT, ftp->timeout_sec * 1000);
if (n < 1) {
#ifdef PHP_WIN32
if (n == 0) {
_set_errno(ETIMEDOUT);
}
#else
if (n == 0) {
errno = ETIMEDOUT;
}
#endif
return -1;
}
#ifdef HAVE_FTP_SSL
if (ftp->use_ssl && ftp->fd == s && ftp->ssl_active) {
handle = ftp->ssl_handle;
fd = ftp->fd;
} else if (ftp->use_ssl && ftp->fd != s && ftp->use_ssl_for_data && ftp->data->ssl_active) {
handle = ftp->data->ssl_handle;
fd = ftp->data->fd;
}
if (handle) {
do {
sent = SSL_write(handle, buf, size);
err = SSL_get_error(handle, sent);
switch (err) {
case SSL_ERROR_NONE:
retry = 0;
break;
case SSL_ERROR_ZERO_RETURN:
retry = 0;
SSL_shutdown(handle);
break;
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_CONNECT: {
php_pollfd p;
int i;
p.fd = fd;
p.events = POLLOUT;
p.revents = 0;
i = php_poll2(&p, 1, 300);
retry = i > 0;
}
break;
default:
php_error_docref(NULL, E_WARNING, "SSL write failed");
return -1;
}
} while (retry);
} else {
#endif
sent = send(s, buf, size, 0);
#ifdef HAVE_FTP_SSL
}
#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;
#ifdef HAVE_FTP_SSL
int err;
zend_bool retry = 0;
SSL *handle = NULL;
php_socket_t fd;
#endif
n = php_pollfd_for_ms(s, PHP_POLLREADABLE, ftp->timeout_sec * 1000);
if (n < 1) {
#ifdef PHP_WIN32
if (n == 0) {
_set_errno(ETIMEDOUT);
}
#else
if (n == 0) {
errno = ETIMEDOUT;
}
#endif
return -1;
}
#ifdef HAVE_FTP_SSL
if (ftp->use_ssl && ftp->fd == s && ftp->ssl_active) {
handle = ftp->ssl_handle;
fd = ftp->fd;
} else if (ftp->use_ssl && ftp->fd != s && ftp->use_ssl_for_data && ftp->data->ssl_active) {
handle = ftp->data->ssl_handle;
fd = ftp->data->fd;
}
if (handle) {
do {
nr_bytes = SSL_read(handle, buf, len);
err = SSL_get_error(handle, nr_bytes);
switch (err) {
case SSL_ERROR_NONE:
retry = 0;
break;
case SSL_ERROR_ZERO_RETURN:
retry = 0;
SSL_shutdown(handle);
break;
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_CONNECT: {
php_pollfd p;
int i;
p.fd = fd;
p.events = POLLIN|POLLPRI;
p.revents = 0;
i = php_poll2(&p, 1, 300);
retry = i > 0;
}
break;
default:
php_error_docref(NULL, E_WARNING, "SSL read failed");
return -1;
}
} while (retry);
} else {
#endif
nr_bytes = recv(s, buf, len, 0);
#ifdef HAVE_FTP_SSL
}
#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) {
#ifdef PHP_WIN32
if (n == 0) {
_set_errno(ETIMEDOUT);
}
#else
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) {
#ifdef PHP_WIN32
if (n == 0) {
_set_errno(ETIMEDOUT);
}
#else
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) {
#ifdef PHP_WIN32
if (n == 0) {
_set_errno(ETIMEDOUT);
}
#else
if (n == 0) {
errno = ETIMEDOUT;
}
#endif
return -1;
}
return accept(s, addr, addrlen);
}
/* }}} */
/* {{{ ftp_getdata
*/
databuf_t*
ftp_getdata(ftpbuf_t *ftp)
{
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, 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, 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, E_WARNING, "bind() failed: %s (%d)", strerror(errno), errno);
goto bail;
}
if (getsockname(fd, (struct sockaddr*) &addr, &size) != 0) {
php_error_docref(NULL, E_WARNING, "getsockname() failed: %s (%d)", strerror(errno), errno);
goto bail;
}
if (listen(fd, 5) != 0) {
php_error_docref(NULL, 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)
{
php_sockaddr_storage addr;
socklen_t size;
#ifdef HAVE_FTP_SSL
SSL_CTX *ctx;
SSL_SESSION *session;
int err, res;
zend_bool retry;
#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:
#ifdef HAVE_FTP_SSL
/* now enable ssl if we need to */
if (ftp->use_ssl && ftp->use_ssl_for_data) {
ctx = SSL_get_SSL_CTX(ftp->ssl_handle);
if (ctx == NULL) {
php_error_docref(NULL, E_WARNING, "data_accept: failed to retreive the existing SSL context");
return 0;
}
data->ssl_handle = SSL_new(ctx);
if (data->ssl_handle == NULL) {
php_error_docref(NULL, E_WARNING, "data_accept: failed to create the SSL handle");
return 0;
}
SSL_set_fd(data->ssl_handle, data->fd);
if (ftp->old_ssl) {
SSL_copy_session_id(data->ssl_handle, ftp->ssl_handle);
}
/* get the session from the control connection so we can re-use it */
session = SSL_get_session(ftp->ssl_handle);
if (session == NULL) {
php_error_docref(NULL, E_WARNING, "data_accept: failed to retreive the existing SSL session");
SSL_free(data->ssl_handle);
return 0;
}
/* and set it on the data connection */
res = SSL_set_session(data->ssl_handle, session);
if (res == 0) {
php_error_docref(NULL, E_WARNING, "data_accept: failed to set the existing SSL session");
SSL_free(data->ssl_handle);
return 0;
}
do {
res = SSL_connect(data->ssl_handle);
err = SSL_get_error(data->ssl_handle, res);
switch (err) {
case SSL_ERROR_NONE:
retry = 0;
break;
case SSL_ERROR_ZERO_RETURN:
retry = 0;
SSL_shutdown(data->ssl_handle);
break;
case SSL_ERROR_WANT_READ:
case SSL_ERROR_WANT_WRITE: {
php_pollfd p;
int i;
p.fd = ftp->fd;
p.events = (err == SSL_ERROR_WANT_READ) ? (POLLIN|POLLPRI) : POLLOUT;
p.revents = 0;
i = php_poll2(&p, 1, 300);
retry = i > 0;
}
break;
default:
php_error_docref(NULL, E_WARNING, "data_accept: SSL/TLS handshake failed");
SSL_shutdown(data->ssl_handle);
SSL_free(data->ssl_handle);
return 0;
}
} while (retry);
data->ssl_active = 1;
}
#endif
return data;
}
/* }}} */
/* {{{ data_close
*/
databuf_t*
data_close(ftpbuf_t *ftp, databuf_t *data)
{
#ifdef HAVE_FTP_SSL
SSL_CTX *ctx;
#endif
if (data == NULL) {
return NULL;
}
if (data->listener != -1) {
#ifdef HAVE_FTP_SSL
if (data->ssl_active) {
/* don't free the data context, it's the same as the control */
SSL_shutdown(data->ssl_handle);
SSL_free(data->ssl_handle);
data->ssl_active = 0;
}
#endif
closesocket(data->listener);
}
if (data->fd != -1) {
#ifdef HAVE_FTP_SSL
if (data->ssl_active) {
/* don't free the data context, it's the same as the control */
SSL_shutdown(data->ssl_handle);
SSL_free(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)
{
php_stream *tmpstream = NULL;
databuf_t *data = NULL;
char *ptr;
int ch, lastch;
size_t size, rcvd;
size_t lines;
char **ret = NULL;
char **entry;
char *text;
if ((tmpstream = php_stream_fopen_tmpfile()) == NULL) {
php_error_docref(NULL, 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)) == 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)) == NULL) {
goto bail;
}
size = 0;
lines = 0;
lastch = 0;
while ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) {
if (rcvd == (size_t)-1 || rcvd > ((size_t)(-1))-size) {
goto bail;
}
php_stream_write(tmpstream, data->buf, rcvd);
size += rcvd;
for (ptr = data->buf; rcvd; rcvd--, ptr++) {
if (*ptr == '\n' && lastch == '\r') {
lines++;
}
lastch = *ptr;
}
}
ftp->data = data_close(ftp, data);
php_stream_rewind(tmpstream);
ret = safe_emalloc((lines + 1), sizeof(char*), size);
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, zend_long resumepos)
{
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)) == NULL) {
goto bail;
}
if (resumepos>0) {
snprintf(arg, sizeof(arg), ZEND_LONG_FMT, 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->nb = 1;
return (ftp_nb_continue_read(ftp));
bail:
ftp->data = data_close(ftp, data);
return PHP_FTP_FAILED;
}
/* }}} */
/* {{{ ftp_nb_continue_read
*/
int
ftp_nb_continue_read(ftpbuf_t *ftp)
{
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 == (size_t)-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, zend_long startpos)
{
databuf_t *data = NULL;
char arg[11];
if (ftp == NULL) {
return 0;
}
if (!ftp_type(ftp, type)) {
goto bail;
}
if ((data = ftp_getdata(ftp)) == NULL) {
goto bail;
}
if (startpos > 0) {
snprintf(arg, sizeof(arg), ZEND_LONG_FMT, 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->nb = 1;
return (ftp_nb_continue_write(ftp));
bail:
ftp->data = data_close(ftp, data);
return PHP_FTP_FAILED;
}
/* }}} */
/* {{{ ftp_nb_continue_write
*/
int
ftp_nb_continue_write(ftpbuf_t *ftp)
{
long 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
*/