mirror of
https://github.com/php/php-src.git
synced 2024-10-13 12:32:38 +00:00
0a18a9d744
windows sockets. The winsock implementation will only work with sockets; our implementation works with sockets and file descriptors. By association, stream_select() will now operate correctly with files, pipes and sockets. This change required linking against the winsock2 library. In terms of compatibility, only older versions of windows 95 do not have winsock2 installed by default. It is available as a redistributable file, and is most likely installed by any OS patches (eg: Internet Explorer) applied by the user. Also, add a win32 compatible pipe test when opening a stream from a pipe. This test will only work on NT, win2k and XP platforms. Without this test, interleaved fread() and select() calls would cause the read buffer to be clobbered. I will be working on a fix for this issue for win9x.
1911 lines
35 KiB
C
1911 lines
35 KiB
C
/*
|
|
+----------------------------------------------------------------------+
|
|
| PHP Version 4 |
|
|
+----------------------------------------------------------------------+
|
|
| Copyright (c) 1997-2003 The PHP Group |
|
|
+----------------------------------------------------------------------+
|
|
| This source file is subject to version 2.02 of the PHP license, |
|
|
| that is bundled with this package in the file LICENSE, and is |
|
|
| available at through the world-wide-web at |
|
|
| http://www.php.net/license/2_02.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$ */
|
|
|
|
#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>
|
|
#elif defined(NETWARE)
|
|
#ifdef USE_WINSOCK /* Modified to use Winsock (NOVSOCK2.H), atleast for now */
|
|
#include <novsock2.h>
|
|
#else
|
|
#ifdef NEW_LIBC
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <netdb.h>
|
|
#else
|
|
#include <sys/socket.h>
|
|
#endif
|
|
#endif
|
|
#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>
|
|
|
|
#if HAVE_SYS_TIME_H
|
|
#include <sys/time.h>
|
|
#endif
|
|
|
|
#ifdef HAVE_SYS_SELECT_H
|
|
#include <sys/select.h>
|
|
#endif
|
|
|
|
#include "ftp.h"
|
|
#include "ext/standard/fsock.h"
|
|
|
|
/* Additional headers for NetWare */
|
|
#if defined(NETWARE) && defined(NEW_LIBC) && !defined(USE_WINSOCK)
|
|
#include <sys/select.h>
|
|
#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 char *args);
|
|
|
|
/* wrapper around send/recv to handle timeouts */
|
|
static int my_send(ftpbuf_t *ftp, int s, void *buf, size_t len);
|
|
static int my_recv(ftpbuf_t *ftp, int s, void *buf, size_t len);
|
|
static int my_accept(ftpbuf_t *ftp, int s, struct sockaddr *addr,
|
|
int *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 TSRMLS_DC);
|
|
|
|
/* 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 TSRMLS_DC);
|
|
|
|
/* IP and port conversion box */
|
|
union ipbox {
|
|
unsigned long l[2];
|
|
unsigned short s[4];
|
|
unsigned char c[8];
|
|
};
|
|
|
|
/* {{{ ftp_open
|
|
*/
|
|
ftpbuf_t*
|
|
ftp_open(const char *host, short port, long timeout_sec TSRMLS_DC)
|
|
{
|
|
ftpbuf_t *ftp;
|
|
int 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_hostconnect(host, (unsigned short) (port ? port : 21), SOCK_STREAM, &tv TSRMLS_CC);
|
|
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, (unsigned int*)&size) == -1) {
|
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "getsockname failed: %s (%d)\n", 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->fd != -1) {
|
|
#if HAVE_OPENSSL_EXT
|
|
if (ftp->ssl_active) {
|
|
SSL_shutdown(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);
|
|
}
|
|
if (ftp->syst) {
|
|
efree(ftp->syst);
|
|
}
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ 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);
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ ftp_login
|
|
*/
|
|
int
|
|
ftp_login(ftpbuf_t *ftp, const char *user, const char *pass TSRMLS_DC)
|
|
{
|
|
#if HAVE_OPENSSL_EXT
|
|
SSL_CTX *ctx = NULL;
|
|
#endif
|
|
if (ftp == NULL) {
|
|
return 0;
|
|
}
|
|
|
|
#if HAVE_OPENSSL_EXT
|
|
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) {
|
|
ftp->use_ssl = 0;
|
|
} else {
|
|
ftp->old_ssl = 1;
|
|
ftp->use_ssl_for_data = 1;
|
|
}
|
|
}
|
|
|
|
/* now enable ssl if we still need to */
|
|
if (ftp->use_ssl) {
|
|
ctx = SSL_CTX_new(SSLv23_client_method());
|
|
if (ctx == NULL) {
|
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create the SSL context");
|
|
return 0;
|
|
}
|
|
|
|
ftp->ssl_handle = SSL_new(ctx);
|
|
if (ftp->ssl_handle == NULL) {
|
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create the SSL handle");
|
|
SSL_CTX_free(ctx);
|
|
return 0;
|
|
}
|
|
|
|
SSL_set_fd(ftp->ssl_handle, ftp->fd);
|
|
|
|
if (SSL_connect(ftp->ssl_handle) <= 0) {
|
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL/TLS handshake failed");
|
|
SSL_shutdown(ftp->ssl_handle);
|
|
return 0;
|
|
}
|
|
|
|
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;
|
|
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;
|
|
}
|
|
*end = 0;
|
|
ftp->pwd = estrdup(pwd);
|
|
*end = '"';
|
|
|
|
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, 1);
|
|
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);
|
|
}
|
|
|
|
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);
|
|
}
|
|
|
|
if (!ftp_putcmd(ftp, "CDUP", NULL)) {
|
|
return 0;
|
|
}
|
|
if (!ftp_getresp(ftp) || ftp->resp != 250) {
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ ftp_mkdir
|
|
*/
|
|
char*
|
|
ftp_mkdir(ftpbuf_t *ftp, const char *dir)
|
|
{
|
|
char *mkd, *end;
|
|
|
|
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) {
|
|
mkd = estrdup(dir);
|
|
return mkd;
|
|
}
|
|
if ((end = strrchr(++mkd, '"')) == NULL) {
|
|
return NULL;
|
|
}
|
|
*end = 0;
|
|
mkd = estrdup(mkd);
|
|
*end = '"';
|
|
|
|
return mkd;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ 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;
|
|
}
|
|
|
|
if (!(buffer = emalloc(32 + filename_len + 1))) {
|
|
return 0;
|
|
}
|
|
|
|
sprintf(buffer, "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_nlist
|
|
*/
|
|
char**
|
|
ftp_nlist(ftpbuf_t *ftp, const char *path TSRMLS_DC)
|
|
{
|
|
return ftp_genlist(ftp, "NLST", path TSRMLS_CC);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ ftp_list
|
|
*/
|
|
char**
|
|
ftp_list(ftpbuf_t *ftp, const char *path, int recursive TSRMLS_DC)
|
|
{
|
|
return ftp_genlist(ftp, ((recursive) ? "LIST -R" : "LIST"), path TSRMLS_CC);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ 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];
|
|
int 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;
|
|
|
|
#ifdef HAVE_IPV6
|
|
if (getpeername(ftp->fd, sa, &n) < 0) {
|
|
return 0;
|
|
}
|
|
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;
|
|
sin->sin_family = AF_INET;
|
|
sin->sin_addr.s_addr = ipbox.l[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, int resumepos TSRMLS_DC)
|
|
{
|
|
databuf_t *data = NULL;
|
|
char *ptr;
|
|
int lastch;
|
|
size_t rcvd;
|
|
char arg[11];
|
|
|
|
if (ftp == NULL) {
|
|
return 0;
|
|
}
|
|
if (!ftp_type(ftp, type)) {
|
|
goto bail;
|
|
}
|
|
|
|
if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) {
|
|
goto bail;
|
|
}
|
|
|
|
ftp->data = data;
|
|
|
|
if (resumepos > 0) {
|
|
if (resumepos > 2147483647) {
|
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "PHP cannot handle files greater then 2147483647 bytes.\n");
|
|
goto bail;
|
|
}
|
|
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;
|
|
}
|
|
|
|
lastch = 0;
|
|
while ((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(outstream, '\r');
|
|
if (*ptr != '\r')
|
|
php_stream_putc(outstream, *ptr);
|
|
lastch = *ptr;
|
|
}
|
|
} else if (rcvd != php_stream_write(outstream, data->buf, rcvd)) {
|
|
goto bail;
|
|
}
|
|
}
|
|
|
|
if (type == FTPTYPE_ASCII && lastch == '\r') {
|
|
php_stream_putc(outstream, '\r');
|
|
}
|
|
|
|
data = data_close(ftp, data);
|
|
ftp->data = NULL;
|
|
|
|
if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
|
|
goto bail;
|
|
}
|
|
|
|
return 1;
|
|
bail:
|
|
data_close(ftp, data);
|
|
ftp->data = NULL;
|
|
return 0;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ ftp_put
|
|
*/
|
|
int
|
|
ftp_put(ftpbuf_t *ftp, const char *path, php_stream *instream, ftptype_t type, int startpos TSRMLS_DC)
|
|
{
|
|
databuf_t *data = NULL;
|
|
int 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 TSRMLS_CC)) == NULL) {
|
|
goto bail;
|
|
}
|
|
ftp->data = data;
|
|
|
|
if (startpos > 0) {
|
|
if (startpos > 2147483647) {
|
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "PHP cannot handle files with a size greater then 2147483647 bytes.\n");
|
|
goto bail;
|
|
}
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
data = data_close(ftp, data);
|
|
|
|
if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
|
|
goto bail;
|
|
}
|
|
return 1;
|
|
bail:
|
|
data_close(ftp, data);
|
|
return 0;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ ftp_size
|
|
*/
|
|
int
|
|
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 atoi(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);
|
|
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;
|
|
|
|
/* build the output buffer */
|
|
if (args && args[0]) {
|
|
/* "cmd args\r\n\0" */
|
|
if (strlen(cmd) + strlen(args) + 4 > FTP_BUFSIZE) {
|
|
return 0;
|
|
}
|
|
size = sprintf(ftp->outbuf, "%s %s\r\n", cmd, args);
|
|
} else {
|
|
/* "cmd\r\n\0" */
|
|
if (strlen(cmd) + 3 > FTP_BUFSIZE) {
|
|
return 0;
|
|
}
|
|
size = sprintf(ftp->outbuf, "%s\r\n", cmd);
|
|
}
|
|
|
|
data = ftp->outbuf;
|
|
|
|
if (my_send(ftp, ftp->fd, data, size) != size) {
|
|
return 0;
|
|
}
|
|
return 1;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ ftp_readline
|
|
*/
|
|
int
|
|
ftp_readline(ftpbuf_t *ftp)
|
|
{
|
|
int 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)
|
|
{
|
|
char *buf;
|
|
|
|
if (ftp == NULL) {
|
|
return 0;
|
|
}
|
|
buf = ftp->inbuf;
|
|
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, int s, void *buf, size_t len)
|
|
{
|
|
fd_set write_set;
|
|
struct timeval tv;
|
|
int n, size, sent;
|
|
|
|
size = len;
|
|
while (size) {
|
|
tv.tv_sec = ftp->timeout_sec;
|
|
tv.tv_usec = 0;
|
|
|
|
FD_ZERO(&write_set);
|
|
FD_SET(s, &write_set);
|
|
|
|
n = select(s + 1, NULL, &write_set, NULL, &tv);
|
|
|
|
if (n < 1) {
|
|
|
|
#if !defined(PHP_WIN32) && !(defined(NETWARE) && defined(USE_WINSOCK))
|
|
if (n == 0) {
|
|
errno = ETIMEDOUT;
|
|
}
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
#if HAVE_OPENSSL_EXT
|
|
if (ftp->use_ssl && ftp->fd == s && ftp->ssl_active) {
|
|
sent = SSL_write(ftp->ssl_handle, buf, size);
|
|
} else if (ftp->use_ssl && ftp->fd != s && ftp->use_ssl_for_data && ftp->data->ssl_active) {
|
|
sent = SSL_write(ftp->data->ssl_handle, buf, size);
|
|
} else {
|
|
#endif
|
|
sent = send(s, buf, size, 0);
|
|
#if HAVE_OPENSSL_EXT
|
|
}
|
|
#endif
|
|
if (sent == -1) {
|
|
return -1;
|
|
}
|
|
|
|
buf = (char*) buf + sent;
|
|
size -= sent;
|
|
}
|
|
|
|
return len;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ my_recv
|
|
*/
|
|
int
|
|
my_recv(ftpbuf_t *ftp, int s, void *buf, size_t len)
|
|
{
|
|
fd_set read_set;
|
|
struct timeval tv;
|
|
int n, nr_bytes;
|
|
|
|
tv.tv_sec = ftp->timeout_sec;
|
|
tv.tv_usec = 0;
|
|
|
|
FD_ZERO(&read_set);
|
|
FD_SET(s, &read_set);
|
|
|
|
n = select(s + 1, &read_set, NULL, NULL, &tv);
|
|
if (n < 1) {
|
|
#if !defined(PHP_WIN32) && !(defined(NETWARE) && defined(USE_WINSOCK))
|
|
if (n == 0) {
|
|
errno = ETIMEDOUT;
|
|
}
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
#if HAVE_OPENSSL_EXT
|
|
if (ftp->use_ssl && ftp->fd == s && ftp->ssl_active) {
|
|
nr_bytes = SSL_read(ftp->ssl_handle, buf, len);
|
|
} else if (ftp->use_ssl && ftp->fd != s && ftp->use_ssl_for_data && ftp->data->ssl_active) {
|
|
nr_bytes = SSL_read(ftp->data->ssl_handle, buf, len);
|
|
} else {
|
|
#endif
|
|
nr_bytes = recv(s, buf, len, 0);
|
|
#if HAVE_OPENSSL_EXT
|
|
}
|
|
#endif
|
|
return (nr_bytes);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ 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) {
|
|
#if !defined(PHP_WIN32) && !(defined(NETWARE) && defined(USE_WINSOCK))
|
|
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
|
|
my_accept(ftpbuf_t *ftp, int s, struct sockaddr *addr, int *addrlen)
|
|
{
|
|
fd_set accept_set;
|
|
struct timeval tv;
|
|
int n;
|
|
|
|
tv.tv_sec = ftp->timeout_sec;
|
|
tv.tv_usec = 0;
|
|
FD_ZERO(&accept_set);
|
|
FD_SET(s, &accept_set);
|
|
|
|
n = select(s + 1, &accept_set, NULL, NULL, &tv);
|
|
if (n < 1) {
|
|
#if !defined(PHP_WIN32) && !(defined(NETWARE) && defined(USE_WINSOCK))
|
|
if (n == 0) {
|
|
errno = ETIMEDOUT;
|
|
}
|
|
#endif
|
|
return -1;
|
|
}
|
|
|
|
return accept(s, addr, (unsigned int*)addrlen);
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ ftp_getdata
|
|
*/
|
|
databuf_t*
|
|
ftp_getdata(ftpbuf_t *ftp TSRMLS_DC)
|
|
{
|
|
int fd = -1;
|
|
databuf_t *data;
|
|
php_sockaddr_storage addr;
|
|
struct sockaddr *sa;
|
|
int 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)) == -1) {
|
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "socket() failed: %s (%d)\n", 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 TSRMLS_CC, E_WARNING, "php_connect_nonb() failed: %s (%d)\n", 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) == -1) {
|
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "bind() failed: %s (%d)\n", strerror(errno), errno);
|
|
goto bail;
|
|
}
|
|
|
|
if (getsockname(fd, (struct sockaddr*) &addr, (unsigned int*)&size) == -1) {
|
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "getsockname() failed: %s (%d)\n", strerror(errno), errno);
|
|
goto bail;
|
|
}
|
|
|
|
if (listen(fd, 5) == -1) {
|
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "listen() failed: %s (%d)\n", strerror(errno), errno);
|
|
goto bail;
|
|
}
|
|
|
|
data->listener = fd;
|
|
|
|
#ifdef HAVE_IPV6
|
|
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));
|
|
sprintf(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.l[0] = ((struct sockaddr_in*) sa)->sin_addr.s_addr;
|
|
ipbox.s[2] = ((struct sockaddr_in*) &addr)->sin_port;
|
|
sprintf(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;
|
|
int size;
|
|
|
|
#if HAVE_OPENSSL_EXT
|
|
SSL_CTX *ctx;
|
|
TSRMLS_FETCH();
|
|
#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:
|
|
#if HAVE_OPENSSL_EXT
|
|
|
|
/* now enable ssl if we need to */
|
|
if (ftp->use_ssl && ftp->use_ssl_for_data) {
|
|
ctx = SSL_CTX_new(SSLv23_client_method());
|
|
if (ctx == NULL) {
|
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_accept: failed to create the SSL context");
|
|
return 0;
|
|
}
|
|
|
|
data->ssl_handle = SSL_new(ctx);
|
|
if (data->ssl_handle == NULL) {
|
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_accept: failed to create the SSL handle");
|
|
SSL_CTX_free(ctx);
|
|
return 0;
|
|
}
|
|
|
|
|
|
SSL_set_fd(data->ssl_handle, data->fd);
|
|
|
|
if (ftp->old_ssl) {
|
|
SSL_copy_session_id(data->ssl_handle, ftp->ssl_handle);
|
|
}
|
|
|
|
if (SSL_connect(data->ssl_handle) <= 0) {
|
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_accept: SSL/TLS handshake failed");
|
|
SSL_shutdown(data->ssl_handle);
|
|
return 0;
|
|
}
|
|
|
|
data->ssl_active = 1;
|
|
}
|
|
|
|
#endif
|
|
|
|
return data;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ data_close
|
|
*/
|
|
databuf_t*
|
|
data_close(ftpbuf_t *ftp, databuf_t *data)
|
|
{
|
|
if (data == NULL) {
|
|
return NULL;
|
|
}
|
|
if (data->listener != -1) {
|
|
#if HAVE_OPENSSL_EXT
|
|
if (data->ssl_active) {
|
|
SSL_shutdown(data->ssl_handle);
|
|
data->ssl_active = 0;
|
|
}
|
|
#endif
|
|
closesocket(data->listener);
|
|
}
|
|
if (data->fd != -1) {
|
|
#if HAVE_OPENSSL_EXT
|
|
if (data->ssl_active) {
|
|
SSL_shutdown(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 TSRMLS_DC)
|
|
{
|
|
FILE *tmpfp = NULL;
|
|
databuf_t *data = NULL;
|
|
char *ptr;
|
|
int ch, lastch;
|
|
int size, rcvd;
|
|
int lines;
|
|
char **ret = NULL;
|
|
char **entry;
|
|
char *text;
|
|
|
|
|
|
if ((tmpfp = tmpfile()) == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
if (!ftp_type(ftp, FTPTYPE_ASCII)) {
|
|
goto bail;
|
|
}
|
|
|
|
if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) {
|
|
goto bail;
|
|
}
|
|
ftp->data = data;
|
|
|
|
if (!ftp_putcmd(ftp, cmd, path)) {
|
|
goto bail;
|
|
}
|
|
if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
|
|
goto bail;
|
|
}
|
|
|
|
/* 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 == -1) {
|
|
goto bail;
|
|
}
|
|
|
|
fwrite(data->buf, rcvd, 1, tmpfp);
|
|
|
|
size += rcvd;
|
|
for (ptr = data->buf; rcvd; rcvd--, ptr++) {
|
|
if (*ptr == '\n' && lastch == '\r') {
|
|
lines++;
|
|
} else {
|
|
size++;
|
|
}
|
|
lastch = *ptr;
|
|
}
|
|
}
|
|
|
|
data = data_close(ftp, data);
|
|
|
|
if (ferror(tmpfp)) {
|
|
goto bail;
|
|
}
|
|
|
|
rewind(tmpfp);
|
|
|
|
ret = emalloc((lines + 1) * sizeof(char**) + size * sizeof(char*));
|
|
|
|
entry = ret;
|
|
text = (char*) (ret + lines + 1);
|
|
*entry = text;
|
|
lastch = 0;
|
|
while ((ch = getc(tmpfp)) != EOF) {
|
|
if (ch == '\n' && lastch == '\r') {
|
|
*(text - 1) = 0;
|
|
*++entry = text;
|
|
} else {
|
|
*text++ = ch;
|
|
}
|
|
lastch = ch;
|
|
}
|
|
*entry = NULL;
|
|
|
|
if (ferror(tmpfp)) {
|
|
goto bail;
|
|
}
|
|
fclose(tmpfp);
|
|
|
|
if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
|
|
efree(ret);
|
|
return NULL;
|
|
}
|
|
|
|
return ret;
|
|
bail:
|
|
if (data)
|
|
data_close(ftp, data);
|
|
fclose(tmpfp);
|
|
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, int resumepos TSRMLS_DC)
|
|
{
|
|
databuf_t *data = NULL;
|
|
char arg[11];
|
|
|
|
if (ftp == NULL) {
|
|
goto bail;
|
|
}
|
|
|
|
if (!ftp_type(ftp, type)) {
|
|
goto bail;
|
|
}
|
|
|
|
if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) {
|
|
goto bail;
|
|
}
|
|
|
|
if (resumepos>0) {
|
|
/* We are working on an architecture that supports 64-bit integers
|
|
* since php is 32 bit by design, we bail out with warning
|
|
*/
|
|
if (resumepos > 2147483647) {
|
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "PHP cannot handle files greater then 2147483648 bytes.\n");
|
|
goto bail;
|
|
}
|
|
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->nb = 1;
|
|
|
|
return (ftp_nb_continue_read(ftp TSRMLS_CC));
|
|
|
|
bail:
|
|
data_close(ftp, data);
|
|
return PHP_FTP_FAILED;
|
|
}
|
|
/* }}} */
|
|
|
|
/* {{{ ftp_nb_continue_read
|
|
*/
|
|
int
|
|
ftp_nb_continue_read(ftpbuf_t *ftp TSRMLS_DC)
|
|
{
|
|
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 == -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');
|
|
}
|
|
|
|
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;
|
|
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, int startpos TSRMLS_DC)
|
|
{
|
|
databuf_t *data = NULL;
|
|
char arg[11];
|
|
|
|
if (ftp == NULL) {
|
|
return 0;
|
|
}
|
|
if (!ftp_type(ftp, type)) {
|
|
goto bail;
|
|
}
|
|
if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) {
|
|
goto bail;
|
|
}
|
|
if (startpos > 0) {
|
|
if (startpos > 2147483647) {
|
|
php_error_docref(NULL TSRMLS_CC, E_WARNING, "PHP cannot handle files with a size greater then 2147483647 bytes.\n");
|
|
goto bail;
|
|
}
|
|
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->nb = 1;
|
|
|
|
return (ftp_nb_continue_write(ftp TSRMLS_CC));
|
|
|
|
bail:
|
|
data_close(ftp, data);
|
|
return PHP_FTP_FAILED;
|
|
}
|
|
/* }}} */
|
|
|
|
|
|
/* {{{ ftp_nb_continue_write
|
|
*/
|
|
int
|
|
ftp_nb_continue_write(ftpbuf_t *ftp TSRMLS_DC)
|
|
{
|
|
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 (!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:
|
|
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
|
|
*/
|