Linux ip-172-26-2-223 5.4.0-1018-aws #18-Ubuntu SMP Wed Jun 24 01:15:00 UTC 2020 x86_64
Apache
: 172.26.2.223 | : 3.144.134.101
Cant Read [ /etc/named.conf ]
8.1.13
www
www.github.com/MadExploits
Terminal
AUTO ROOT
Adminer
Backdoor Destroyer
Linux Exploit
Lock Shell
Lock File
Create User
CREATE RDP
PHP Mailer
BACKCONNECT
UNLOCK SHELL
HASH IDENTIFIER
CPANEL RESET
CREATE WP USER
BLACK DEFEND!
README
+ Create Folder
+ Create File
/
www /
server /
php /
81 /
src /
ext /
ftp /
[ HOME SHELL ]
Name
Size
Permission
Action
tests
[ DIR ]
drwxr-xr-x
CREDITS
33
B
-rw-r--r--
config.m4
808
B
-rw-r--r--
config.w32
347
B
-rw-r--r--
ftp.c
45.07
KB
-rw-r--r--
ftp.h
8.64
KB
-rw-r--r--
ftp.stub.php
3.9
KB
-rw-r--r--
ftp_arginfo.h
10.87
KB
-rw-r--r--
php_ftp.c
31.07
KB
-rw-r--r--
php_ftp.h
1.43
KB
-rw-r--r--
Delete
Unzip
Zip
${this.title}
Close
Code Editor : ftp.c
/* +----------------------------------------------------------------------+ | Copyright (c) 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: | | https://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) | +----------------------------------------------------------------------+ */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include "php.h" #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> #else #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> #ifdef 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> #include <openssl/err.h> #endif #include "ftp.h" #include "ext/standard/fsock.h" #ifdef PHP_WIN32 # undef ETIMEDOUT # define ETIMEDOUT WSAETIMEDOUT #endif /* 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 size_t cmd_len, const char *args, const size_t args_len); /* 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 size_t cmd_len, const char *path, const size_t path_len); #ifdef HAVE_FTP_SSL /* shuts down a TLS/SSL connection */ static void ftp_ssl_shutdown(ftpbuf_t *ftp, php_socket_t fd, SSL *ssl_handle); #endif /* 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) { ftp_ssl_shutdown(ftp, ftp->fd, 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", sizeof("QUIT")-1, NULL, (size_t) 0)) { 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 size_t user_len, const char *pass, const size_t pass_len) { #ifdef HAVE_FTP_SSL SSL_CTX *ctx = NULL; long ssl_ctx_options = SSL_OP_ALL; int err, res; bool retry; #endif if (ftp == NULL) { return 0; } #ifdef HAVE_FTP_SSL if (ftp->use_ssl && !ftp->ssl_active) { if (!ftp_putcmd(ftp, "AUTH", sizeof("AUTH")-1, "TLS", sizeof("TLS")-1)) { return 0; } if (!ftp_getresp(ftp)) { return 0; } if (ftp->resp != 234) { if (!ftp_putcmd(ftp, "AUTH", sizeof("AUTH")-1, "SSL", sizeof("SSL")-1)) { 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); SSL_CTX_free(ctx); if (ftp->ssl_handle == NULL) { php_error_docref(NULL, E_WARNING, "Failed to create the SSL handle"); 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", sizeof("PBSZ")-1, "0", sizeof("0")-1)) { return 0; } if (!ftp_getresp(ftp)) { return 0; } /* enable data conn encryption */ if (!ftp_putcmd(ftp, "PROT", sizeof("PROT")-1, "P", sizeof("P")-1)) { 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", sizeof("USER")-1, user, user_len)) { 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", sizeof("PASS")-1, pass, pass_len)) { 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", sizeof("REIN")-1, NULL, (size_t) 0)) { 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", sizeof("SYST")-1, NULL, (size_t) 0)) { 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", sizeof("PWD")-1, NULL, (size_t) 0)) { 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, const size_t cmd_len) { if (ftp == NULL) { return 0; } if (!ftp_putcmd(ftp, "SITE EXEC", sizeof("SITE EXEC")-1, cmd, cmd_len)) { return 0; } if (!ftp_getresp(ftp) || ftp->resp != 200) { return 0; } return 1; } /* }}} */ /* {{{ ftp_raw */ void ftp_raw(ftpbuf_t *ftp, const char *cmd, const size_t cmd_len, zval *return_value) { if (ftp == NULL || cmd == NULL) { RETURN_NULL(); } if (!ftp_putcmd(ftp, cmd, cmd_len, NULL, (size_t) 0)) { 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, const size_t dir_len) { if (ftp == NULL) { return 0; } if (ftp->pwd) { efree(ftp->pwd); ftp->pwd = NULL; } if (!ftp_putcmd(ftp, "CWD", sizeof("CWD")-1, dir, dir_len)) { 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", sizeof("CDUP")-1, NULL, (size_t) 0)) { 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, const size_t dir_len) { char *mkd, *end; zend_string *ret; if (ftp == NULL) { return NULL; } if (!ftp_putcmd(ftp, "MKD", sizeof("MKD")-1, dir, dir_len)) { 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, dir_len, 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, const size_t dir_len) { if (ftp == NULL) { return 0; } if (!ftp_putcmd(ftp, "RMD", sizeof("RMD")-1, dir, dir_len)) { 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; size_t buffer_len; if (ftp == NULL || filename_len <= 0) { return 0; } buffer_len = spprintf(&buffer, 0, "CHMOD %o %s", mode, filename); if (!buffer) { return 0; } if (!ftp_putcmd(ftp, "SITE", sizeof("SITE")-1, buffer, buffer_len)) { 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]; int buffer_len; if (ftp == NULL || size <= 0) { return 0; } buffer_len = snprintf(buffer, sizeof(buffer) - 1, ZEND_LONG_FMT, size); if (buffer_len < 0) { return 0; } if (!ftp_putcmd(ftp, "ALLO", sizeof("ALLO")-1, buffer, buffer_len)) { 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, const size_t path_len) { return ftp_genlist(ftp, "NLST", sizeof("NLST")-1, path, path_len); } /* }}} */ /* {{{ ftp_list */ char** ftp_list(ftpbuf_t *ftp, const char *path, const size_t path_len, int recursive) { return ftp_genlist(ftp, ((recursive) ? "LIST -R" : "LIST"), ((recursive) ? sizeof("LIST -R")-1 : sizeof("LIST")-1), path, path_len); } /* }}} */ /* {{{ ftp_mlsd */ char** ftp_mlsd(ftpbuf_t *ftp, const char *path, const size_t path_len) { return ftp_genlist(ftp, "MLSD", sizeof("MLSD")-1, path, path_len); } /* }}} */ /* {{{ ftp_mlsd_parse_line */ int ftp_mlsd_parse_line(HashTable *ht, const char *input) { zval zstr; const char *end = input + strlen(input); const char *sp = memchr(input, ' ', end - input); if (!sp) { php_error_docref(NULL, E_WARNING, "Missing pathname in MLSD response"); return FAILURE; } /* Extract pathname */ ZVAL_STRINGL(&zstr, sp + 1, end - sp - 1); zend_hash_str_update(ht, "name", sizeof("name")-1, &zstr); end = sp; while (input < end) { const char *semi, *eq; /* Find end of fact */ semi = memchr(input, ';', end - input); if (!semi) { php_error_docref(NULL, E_WARNING, "Malformed fact in MLSD response"); return FAILURE; } /* Separate fact key and value */ eq = memchr(input, '=', semi - input); if (!eq) { php_error_docref(NULL, E_WARNING, "Malformed fact in MLSD response"); return FAILURE; } ZVAL_STRINGL(&zstr, eq + 1, semi - eq - 1); zend_hash_str_update(ht, input, eq - input, &zstr); input = semi + 1; } return SUCCESS; } /* }}} */ /* {{{ ftp_type */ int ftp_type(ftpbuf_t *ftp, ftptype_t type) { const char *typechar; if (ftp == NULL) { return 0; } if (type == ftp->type) { return 1; } if (type == FTPTYPE_ASCII) { typechar = "A"; } else if (type == FTPTYPE_IMAGE) { typechar = "I"; } else { return 0; } if (!ftp_putcmd(ftp, "TYPE", sizeof("TYPE")-1, typechar, 1)) { 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; } #ifdef 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", sizeof("EPSV")-1, NULL, (size_t) 0)) { 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", sizeof("PASV")-1, NULL, (size_t) 0)) { 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, const size_t path_len, 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) { int arg_len = snprintf(arg, sizeof(arg), ZEND_LONG_FMT, resumepos); if (arg_len < 0) { goto bail; } if (!ftp_putcmd(ftp, "REST", sizeof("REST")-1, arg, arg_len)) { goto bail; } if (!ftp_getresp(ftp) || (ftp->resp != 350)) { goto bail; } } if (!ftp_putcmd(ftp, "RETR", sizeof("RETR")-1, path, path_len)) { 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, const size_t path_len, 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) { int arg_len = snprintf(arg, sizeof(arg), ZEND_LONG_FMT, startpos); if (arg_len < 0) { goto bail; } if (!ftp_putcmd(ftp, "REST", sizeof("REST")-1, arg, arg_len)) { goto bail; } if (!ftp_getresp(ftp) || (ftp->resp != 350)) { goto bail; } } if (!ftp_putcmd(ftp, "STOR", sizeof("STOR")-1, path, path_len)) { 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_append */ int ftp_append(ftpbuf_t *ftp, const char *path, const size_t path_len, php_stream *instream, ftptype_t type) { databuf_t *data = NULL; zend_long size; char *ptr; int ch; if (ftp == NULL) { return 0; } if (!ftp_type(ftp, type)) { goto bail; } if ((data = ftp_getdata(ftp)) == NULL) { goto bail; } ftp->data = data; if (!ftp_putcmd(ftp, "APPE", sizeof("APPE")-1, path, path_len)) { 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, const size_t path_len) { if (ftp == NULL) { return -1; } if (!ftp_type(ftp, FTPTYPE_IMAGE)) { return -1; } if (!ftp_putcmd(ftp, "SIZE", sizeof("SIZE")-1, path, path_len)) { return -1; } if (!ftp_getresp(ftp) || ftp->resp != 213) { return -1; } return ZEND_ATOL(ftp->inbuf); } /* }}} */ /* {{{ ftp_mdtm */ time_t ftp_mdtm(ftpbuf_t *ftp, const char *path, const size_t path_len) { time_t stamp; struct tm *gmt, tmbuf; struct tm tm; char *ptr; int n; if (ftp == NULL) { return -1; } if (!ftp_putcmd(ftp, "MDTM", sizeof("MDTM")-1, path, path_len)) { 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, const size_t path_len) { if (ftp == NULL) { return 0; } if (!ftp_putcmd(ftp, "DELE", sizeof("DELE")-1, path, path_len)) { 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 size_t src_len, const char *dest, const size_t dest_len) { if (ftp == NULL) { return 0; } if (!ftp_putcmd(ftp, "RNFR", sizeof("RNFR")-1, src, src_len)) { return 0; } if (!ftp_getresp(ftp) || ftp->resp != 350) { return 0; } if (!ftp_putcmd(ftp, "RNTO", sizeof("RNTO")-1, dest, dest_len)) { return 0; } if (!ftp_getresp(ftp) || ftp->resp != 250) { return 0; } return 1; } /* }}} */ /* {{{ ftp_site */ int ftp_site(ftpbuf_t *ftp, const char *cmd, const size_t cmd_len) { if (ftp == NULL) { return 0; } if (!ftp_putcmd(ftp, "SITE", sizeof("SITE")-1, cmd, cmd_len)) { 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 size_t cmd_len, const char *args, const size_t args_len) { 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 (cmd_len + args_len + 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 (cmd_len + 3 > FTP_BUFSIZE) { return 0; } size = slprintf(ftp->outbuf, sizeof(ftp->outbuf), "%s\r\n", cmd); } data = ftp->outbuf; /* Clear the inbuf and extra-lines buffer */ ftp->inbuf[0] = '\0'; 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) { *data = 0; return 0; } } while (size); *data = 0; 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; } /* }}} */ int single_send(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t size) { #ifdef HAVE_FTP_SSL int err; bool retry = 0; SSL *handle = NULL; php_socket_t fd; size_t sent; 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; } else { return send(s, buf, size, 0); } 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); return sent; #else return send(s, buf, size, 0); #endif } /* {{{ my_send */ int my_send(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len) { zend_long size, sent; int n; size = len; while (size) { n = php_pollfd_for_ms(s, POLLOUT, ftp->timeout_sec * 1000); if (n < 1) { char buf[256]; if (n == 0) { #ifdef PHP_WIN32 _set_errno(ETIMEDOUT); #else errno = ETIMEDOUT; #endif } php_error_docref(NULL, E_WARNING, "%s", php_socket_strerror(errno, buf, sizeof buf)); return -1; } sent = single_send(ftp, s, buf, size); 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; 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) { char buf[256]; if (n == 0) { #ifdef PHP_WIN32 _set_errno(ETIMEDOUT); #else errno = ETIMEDOUT; #endif } php_error_docref(NULL, E_WARNING, "%s", php_socket_strerror(errno, buf, sizeof buf)); 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) { char buf[256]; if (n == 0) { #ifdef PHP_WIN32 _set_errno(ETIMEDOUT); #else errno = ETIMEDOUT; #endif } php_error_docref(NULL, E_WARNING, "%s", php_socket_strerror(errno, buf, sizeof buf)); 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) { char buf[256]; if (n == 0) { #ifdef PHP_WIN32 _set_errno(ETIMEDOUT); #else errno = ETIMEDOUT; #endif } php_error_docref(NULL, E_WARNING, "%s", php_socket_strerror(errno, buf, sizeof buf)); 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) { char buf[256]; if (n == 0) { #ifdef PHP_WIN32 _set_errno(ETIMEDOUT); #else errno = ETIMEDOUT; #endif } php_error_docref(NULL, E_WARNING, "%s", php_socket_strerror(errno, buf, sizeof buf)); 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; int arg_len; /* 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 defined(HAVE_IPV6) && defined(HAVE_INET_NTOP) if (sa->sa_family == AF_INET6) { /* need to use EPRT */ char eprtarg[INET6_ADDRSTRLEN + sizeof("|x||xxxxx|")]; char out[INET6_ADDRSTRLEN]; int eprtarg_len; inet_ntop(AF_INET6, &((struct sockaddr_in6*) sa)->sin6_addr, out, sizeof(out)); eprtarg_len = snprintf(eprtarg, sizeof(eprtarg), "|2|%s|%hu|", out, ntohs(((struct sockaddr_in6 *) &addr)->sin6_port)); if (eprtarg_len < 0) { goto bail; } if (!ftp_putcmd(ftp, "EPRT", sizeof("EPRT")-1, eprtarg, eprtarg_len)) { 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; arg_len = 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 (arg_len < 0) { goto bail; } if (!ftp_putcmd(ftp, "PORT", sizeof("PORT")-1, arg, arg_len)) { 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; 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 retrieve 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 retrieve 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 = data->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; } /* }}} */ /* {{{ ftp_ssl_shutdown */ #ifdef HAVE_FTP_SSL static void ftp_ssl_shutdown(ftpbuf_t *ftp, php_socket_t fd, SSL *ssl_handle) { /* In TLS 1.3 it's common to receive session tickets after the handshake has completed. We need to train the socket (read the tickets until EOF/close_notify alert) before closing the socket. Otherwise the server might get an ECONNRESET which might lead to data truncation on server side. */ char buf[256]; /* We will use this for the OpenSSL error buffer, so it has to be at least 256 bytes long.*/ int done = 1, err, nread; unsigned long sslerror; err = SSL_shutdown(ssl_handle); if (err < 0) { php_error_docref(NULL, E_WARNING, "SSL_shutdown failed"); } else if (err == 0) { /* The shutdown is not yet finished. Call SSL_read() to do a bidirectional shutdown. */ done = 0; } while (!done && data_available(ftp, fd)) { ERR_clear_error(); nread = SSL_read(ssl_handle, buf, sizeof(buf)); if (nread <= 0) { err = SSL_get_error(ssl_handle, nread); switch (err) { case SSL_ERROR_NONE: /* this is not an error */ case SSL_ERROR_ZERO_RETURN: /* no more data */ /* This is the expected response. There was no data but only the close notify alert */ done = 1; break; case SSL_ERROR_WANT_READ: /* there's data pending, re-invoke SSL_read() */ break; case SSL_ERROR_WANT_WRITE: /* SSL wants a write. Really odd. Let's bail out. */ done = 1; break; case SSL_ERROR_SYSCALL: /* most likely the peer closed the connection without sending a close_notify shutdown alert; bail out to avoid raising a spurious warning */ done = 1; break; default: if ((sslerror = ERR_get_error())) { ERR_error_string_n(sslerror, buf, sizeof(buf)); php_error_docref(NULL, E_WARNING, "SSL_read on shutdown: %s", buf); } else if (errno) { php_error_docref(NULL, E_WARNING, "SSL_read on shutdown: %s (%d)", strerror(errno), errno); } done = 1; break; } } } (void)SSL_free(ssl_handle); } #endif /* }}} */ /* {{{ data_close */ databuf_t* data_close(ftpbuf_t *ftp, databuf_t *data) { 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 */ ftp_ssl_shutdown(ftp, data->listener, 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 */ ftp_ssl_shutdown(ftp, data->fd, 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 size_t cmd_len, const char *path, const size_t path_len) { 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, cmd_len, path, path_len)) { 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, const size_t path_len, 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) { int arg_len = snprintf(arg, sizeof(arg), ZEND_LONG_FMT, resumepos); if (arg_len < 0) { goto bail; } if (!ftp_putcmd(ftp, "REST", sizeof("REST")-1, arg, arg_len)) { goto bail; } if (!ftp_getresp(ftp) || (ftp->resp != 350)) { goto bail; } } if (!ftp_putcmd(ftp, "RETR", sizeof("RETR")-1, path, path_len)) { 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, const size_t path_len, 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) { int arg_len = snprintf(arg, sizeof(arg), ZEND_LONG_FMT, startpos); if (arg_len < 0) { goto bail; } if (!ftp_putcmd(ftp, "REST", sizeof("REST")-1, arg, arg_len)) { goto bail; } if (!ftp_getresp(ftp) || (ftp->resp != 350)) { goto bail; } } if (!ftp_putcmd(ftp, "STOR", sizeof("STOR")-1, path, path_len)) { 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; } /* }}} */
Close