2000-10-13 08:09:31 +08:00
|
|
|
/*
|
|
|
|
+----------------------------------------------------------------------+
|
2001-12-11 23:32:16 +08:00
|
|
|
| PHP Version 4 |
|
2000-10-13 08:09:31 +08:00
|
|
|
+----------------------------------------------------------------------+
|
2003-01-01 00:08:15 +08:00
|
|
|
| Copyright (c) 1997-2003 The PHP Group |
|
2000-10-13 08:09:31 +08:00
|
|
|
+----------------------------------------------------------------------+
|
|
|
|
| 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. |
|
|
|
|
+----------------------------------------------------------------------+
|
2002-02-28 16:29:35 +08:00
|
|
|
| Authors: Rasmus Lerdorf <rasmus@php.net> |
|
2000-10-13 08:09:31 +08:00
|
|
|
| Jim Winstead <jimw@php.net> |
|
|
|
|
| Hartmut Holzgraefe <hholzgra@php.net> |
|
|
|
|
+----------------------------------------------------------------------+
|
|
|
|
*/
|
|
|
|
/* $Id$ */
|
|
|
|
|
|
|
|
#include "php.h"
|
|
|
|
#include "php_globals.h"
|
2000-10-13 17:13:01 +08:00
|
|
|
#include "php_network.h"
|
2000-10-13 08:09:31 +08:00
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <errno.h>
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
|
|
|
|
#ifdef PHP_WIN32
|
2003-02-16 11:48:49 +08:00
|
|
|
#include <winsock2.h>
|
2000-10-13 08:09:31 +08:00
|
|
|
#define O_RDONLY _O_RDONLY
|
|
|
|
#include "win32/param.h"
|
2002-09-05 22:25:07 +08:00
|
|
|
#elif defined(NETWARE)
|
|
|
|
/*#include <ws2nlm.h>*/
|
|
|
|
/*#include <sys/socket.h>*/
|
|
|
|
#ifdef NEW_LIBC
|
|
|
|
#include <sys/param.h>
|
|
|
|
#else
|
|
|
|
#include "netware/param.h"
|
|
|
|
#endif
|
2000-10-13 08:09:31 +08:00
|
|
|
#else
|
|
|
|
#include <sys/param.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "php_standard.h"
|
|
|
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
#if HAVE_SYS_SOCKET_H
|
|
|
|
#include <sys/socket.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef PHP_WIN32
|
2003-02-16 11:48:49 +08:00
|
|
|
#include <winsock2.h>
|
2002-09-05 22:25:07 +08:00
|
|
|
#elif defined(NETWARE) && defined(USE_WINSOCK)
|
|
|
|
/*#include <ws2nlm.h>*/
|
|
|
|
#include <novsock2.h>
|
2000-10-13 08:09:31 +08:00
|
|
|
#else
|
|
|
|
#include <netinet/in.h>
|
|
|
|
#include <netdb.h>
|
2001-01-22 01:29:15 +08:00
|
|
|
#if HAVE_ARPA_INET_H
|
2000-10-13 08:09:31 +08:00
|
|
|
#include <arpa/inet.h>
|
|
|
|
#endif
|
2001-01-22 01:29:15 +08:00
|
|
|
#endif
|
2000-10-13 08:09:31 +08:00
|
|
|
|
2002-09-05 22:25:07 +08:00
|
|
|
#if defined(PHP_WIN32) || defined(__riscos__) || defined(NETWARE)
|
2000-10-13 08:09:31 +08:00
|
|
|
#undef AF_UNIX
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if defined(AF_UNIX)
|
|
|
|
#include <sys/un.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "php_fopen_wrappers.h"
|
|
|
|
|
|
|
|
|
2002-04-11 06:42:32 +08:00
|
|
|
static inline int get_ftp_result(php_stream *stream, char *buffer, size_t buffer_size TSRMLS_DC)
|
|
|
|
{
|
|
|
|
while (php_stream_gets(stream, buffer, buffer_size-1) &&
|
|
|
|
!(isdigit((int) buffer[0]) && isdigit((int) buffer[1]) &&
|
|
|
|
isdigit((int) buffer[2]) && buffer[3] == ' '));
|
|
|
|
return strtol(buffer, NULL, 10);
|
2000-10-13 08:09:31 +08:00
|
|
|
}
|
2002-04-11 06:42:32 +08:00
|
|
|
#define GET_FTP_RESULT(stream) get_ftp_result((stream), tmp_line, sizeof(tmp_line) TSRMLS_CC)
|
2000-10-13 08:09:31 +08:00
|
|
|
|
2002-09-09 00:45:32 +08:00
|
|
|
#define FTPS_ENCRYPT_DATA 1
|
|
|
|
|
2002-03-28 08:49:00 +08:00
|
|
|
static int php_stream_ftp_stream_stat(php_stream_wrapper *wrapper,
|
|
|
|
php_stream *stream,
|
|
|
|
php_stream_statbuf *ssb
|
|
|
|
TSRMLS_DC)
|
|
|
|
{
|
|
|
|
/* For now, we return with a failure code to prevent the underlying
|
|
|
|
* file's details from being used instead. */
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-09-09 03:11:07 +08:00
|
|
|
static int php_stream_ftp_stream_close(php_stream_wrapper *wrapper,
|
|
|
|
php_stream *stream
|
|
|
|
TSRMLS_DC)
|
|
|
|
{
|
|
|
|
php_stream *controlstream = (php_stream *)stream->wrapperdata;
|
|
|
|
|
|
|
|
if (controlstream) {
|
|
|
|
php_stream_write_string(controlstream, "QUIT\r\n");
|
|
|
|
php_stream_close(controlstream);
|
|
|
|
stream->wrapperdata = NULL;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2003-04-25 04:32:14 +08:00
|
|
|
/* {{{ php_ftp_fopen_connect
|
2001-06-06 21:06:12 +08:00
|
|
|
*/
|
2003-04-25 04:32:14 +08:00
|
|
|
static php_stream *php_ftp_fopen_connect(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context,
|
|
|
|
php_stream **preuseid, php_url **presource, int *puse_ssl, int *puse_ssl_on_data
|
|
|
|
STREAMS_DC TSRMLS_DC)
|
2000-10-13 08:09:31 +08:00
|
|
|
{
|
2003-04-25 04:32:14 +08:00
|
|
|
php_stream *stream = NULL, *reuseid = NULL;
|
|
|
|
php_url *resource = NULL;
|
|
|
|
int result, use_ssl, use_ssl_on_data = 0;
|
2000-10-13 08:09:31 +08:00
|
|
|
char *scratch;
|
2003-04-25 04:32:14 +08:00
|
|
|
char tmp_line[512];
|
2002-11-26 06:53:57 +08:00
|
|
|
|
2001-06-11 23:14:04 +08:00
|
|
|
resource = php_url_parse((char *) path);
|
2002-03-16 05:03:08 +08:00
|
|
|
if (resource == NULL || resource->path == NULL)
|
2000-10-13 08:09:31 +08:00
|
|
|
return NULL;
|
2002-03-16 05:03:08 +08:00
|
|
|
|
2002-09-09 00:45:32 +08:00
|
|
|
use_ssl = resource->scheme && (strlen(resource->scheme) > 3) && resource->scheme[3] == 's';
|
|
|
|
|
2000-10-13 08:09:31 +08:00
|
|
|
/* use port 21 if one wasn't specified */
|
|
|
|
if (resource->port == 0)
|
|
|
|
resource->port = 21;
|
|
|
|
|
2002-07-23 02:46:26 +08:00
|
|
|
stream = php_stream_sock_open_host(resource->host, resource->port, SOCK_STREAM, NULL, 0);
|
2002-11-05 19:17:45 +08:00
|
|
|
if (stream == NULL) {
|
|
|
|
result = 0; /* silence */
|
2003-04-25 04:32:14 +08:00
|
|
|
goto connect_errexit;
|
2002-11-05 19:17:45 +08:00
|
|
|
}
|
2002-03-16 05:03:08 +08:00
|
|
|
|
2002-04-11 06:42:32 +08:00
|
|
|
php_stream_context_set(stream, context);
|
|
|
|
php_stream_notify_info(context, PHP_STREAM_NOTIFY_CONNECT, NULL, 0);
|
|
|
|
|
2000-10-13 08:09:31 +08:00
|
|
|
/* Start talking to ftp server */
|
2002-04-11 06:42:32 +08:00
|
|
|
result = GET_FTP_RESULT(stream);
|
|
|
|
if (result > 299 || result < 200) {
|
|
|
|
php_stream_notify_error(context, PHP_STREAM_NOTIFY_FAILURE, tmp_line, result);
|
2003-04-25 04:32:14 +08:00
|
|
|
goto connect_errexit;
|
2002-04-11 06:42:32 +08:00
|
|
|
}
|
2000-10-13 08:09:31 +08:00
|
|
|
|
2002-09-09 00:45:32 +08:00
|
|
|
if (use_ssl) {
|
|
|
|
|
|
|
|
/* send the AUTH TLS request name */
|
|
|
|
php_stream_write_string(stream, "AUTH TLS\r\n");
|
|
|
|
|
|
|
|
/* get the response */
|
|
|
|
result = GET_FTP_RESULT(stream);
|
|
|
|
if (result != 234) {
|
|
|
|
/* AUTH TLS not supported try AUTH SSL */
|
|
|
|
php_stream_write_string(stream, "AUTH SSL\r\n");
|
|
|
|
|
|
|
|
/* get the response */
|
|
|
|
result = GET_FTP_RESULT(stream);
|
|
|
|
if (result != 334) {
|
|
|
|
use_ssl = 0;
|
2002-09-09 06:26:11 +08:00
|
|
|
} else {
|
|
|
|
/* we must reuse the old SSL session id */
|
|
|
|
/* if we talk to an old ftpd-ssl */
|
|
|
|
reuseid = stream;
|
2002-09-09 00:45:32 +08:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* encrypt data etc */
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (use_ssl) {
|
2003-02-28 01:43:38 +08:00
|
|
|
if (php_stream_xport_crypto_setup(stream,
|
|
|
|
STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL TSRMLS_CC) < 0
|
|
|
|
|| php_stream_xport_crypto_enable(stream, 1 TSRMLS_CC) < 0) {
|
2002-09-09 00:45:32 +08:00
|
|
|
php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "Unable to activate SSL mode");
|
|
|
|
php_stream_close(stream);
|
|
|
|
stream = NULL;
|
2003-04-25 04:32:14 +08:00
|
|
|
goto connect_errexit;
|
2002-09-09 00:45:32 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/* set PBSZ to 0 */
|
|
|
|
php_stream_write_string(stream, "PBSZ 0\r\n");
|
|
|
|
|
|
|
|
/* ignore the response */
|
|
|
|
result = GET_FTP_RESULT(stream);
|
|
|
|
|
|
|
|
/* set data connection protection level */
|
|
|
|
#if FTPS_ENCRYPT_DATA
|
|
|
|
php_stream_write_string(stream, "PROT P\r\n");
|
|
|
|
|
|
|
|
/* get the response */
|
|
|
|
result = GET_FTP_RESULT(stream);
|
2002-09-09 06:26:11 +08:00
|
|
|
use_ssl_on_data = (result >= 200 && result<=299) || reuseid;
|
2002-09-09 00:45:32 +08:00
|
|
|
#else
|
|
|
|
php_stream_write_string(stream, "PROT C\r\n");
|
|
|
|
|
|
|
|
/* get the response */
|
|
|
|
result = GET_FTP_RESULT(stream);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2000-10-13 08:09:31 +08:00
|
|
|
/* send the user name */
|
2002-03-16 05:03:08 +08:00
|
|
|
php_stream_write_string(stream, "USER ");
|
2000-10-13 08:09:31 +08:00
|
|
|
if (resource->user != NULL) {
|
|
|
|
php_raw_url_decode(resource->user, strlen(resource->user));
|
2002-03-16 05:03:08 +08:00
|
|
|
php_stream_write_string(stream, resource->user);
|
2000-10-13 08:09:31 +08:00
|
|
|
} else {
|
2002-03-16 05:03:08 +08:00
|
|
|
php_stream_write_string(stream, "anonymous");
|
2000-10-13 08:09:31 +08:00
|
|
|
}
|
2002-03-16 05:03:08 +08:00
|
|
|
php_stream_write_string(stream, "\r\n");
|
2000-10-13 08:09:31 +08:00
|
|
|
|
|
|
|
/* get the response */
|
2002-04-11 06:42:32 +08:00
|
|
|
result = GET_FTP_RESULT(stream);
|
2000-10-13 08:09:31 +08:00
|
|
|
|
|
|
|
/* if a password is required, send it */
|
|
|
|
if (result >= 300 && result <= 399) {
|
2002-04-11 06:42:32 +08:00
|
|
|
php_stream_notify_info(context, PHP_STREAM_NOTIFY_AUTH_REQUIRED, tmp_line, 0);
|
|
|
|
|
2002-03-16 05:03:08 +08:00
|
|
|
php_stream_write_string(stream, "PASS ");
|
2000-10-13 08:09:31 +08:00
|
|
|
if (resource->pass != NULL) {
|
|
|
|
php_raw_url_decode(resource->pass, strlen(resource->pass));
|
2002-03-16 05:03:08 +08:00
|
|
|
php_stream_write_string(stream, resource->pass);
|
2000-10-13 08:09:31 +08:00
|
|
|
} else {
|
|
|
|
/* if the user has configured who they are,
|
|
|
|
send that as the password */
|
|
|
|
if (cfg_get_string("from", &scratch) == SUCCESS) {
|
2002-03-16 05:03:08 +08:00
|
|
|
php_stream_write_string(stream, scratch);
|
2000-10-13 08:09:31 +08:00
|
|
|
} else {
|
2002-03-16 05:03:08 +08:00
|
|
|
php_stream_write_string(stream, "anonymous");
|
2000-10-13 08:09:31 +08:00
|
|
|
}
|
|
|
|
}
|
2002-03-16 05:03:08 +08:00
|
|
|
php_stream_write_string(stream, "\r\n");
|
2000-10-13 08:09:31 +08:00
|
|
|
|
|
|
|
/* read the response */
|
2002-04-11 06:42:32 +08:00
|
|
|
result = GET_FTP_RESULT(stream);
|
|
|
|
|
|
|
|
if (result > 299 || result < 200) {
|
|
|
|
php_stream_notify_error(context, PHP_STREAM_NOTIFY_AUTH_RESULT, tmp_line, result);
|
|
|
|
} else {
|
|
|
|
php_stream_notify_info(context, PHP_STREAM_NOTIFY_AUTH_RESULT, tmp_line, result);
|
|
|
|
}
|
2000-10-13 08:09:31 +08:00
|
|
|
}
|
2003-04-25 04:32:14 +08:00
|
|
|
if (result > 299 || result < 200) {
|
|
|
|
goto connect_errexit;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (puse_ssl) {
|
|
|
|
*puse_ssl = use_ssl;
|
|
|
|
}
|
|
|
|
if (puse_ssl_on_data) {
|
|
|
|
*puse_ssl_on_data = use_ssl_on_data;
|
|
|
|
}
|
|
|
|
if (preuseid) {
|
|
|
|
*preuseid = reuseid;
|
|
|
|
}
|
|
|
|
if (presource) {
|
|
|
|
*presource = resource;
|
|
|
|
}
|
|
|
|
|
|
|
|
return stream;
|
|
|
|
|
|
|
|
connect_errexit:
|
|
|
|
if (stream) {
|
|
|
|
php_stream_close(stream);
|
|
|
|
}
|
|
|
|
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
|
|
|
/* {{{ php_fopen_do_pasv
|
|
|
|
*/
|
|
|
|
static unsigned short php_fopen_do_pasv(php_stream *stream, char *ip, int ip_size, char **phoststart STREAMS_DC TSRMLS_DC)
|
|
|
|
{
|
|
|
|
char tmp_line[512];
|
|
|
|
int result, i;
|
|
|
|
unsigned short portno;
|
|
|
|
char *tpath, *ttpath, *hoststart=NULL;
|
|
|
|
|
|
|
|
/* We try EPSV first, needed for IPv6 and works on some IPv4 servers */
|
|
|
|
php_stream_write_string(stream, "EPSV\r\n");
|
|
|
|
result = GET_FTP_RESULT(stream);
|
|
|
|
|
|
|
|
/* check if we got a 229 response */
|
|
|
|
if (result != 229) {
|
|
|
|
/* EPSV failed, let's try PASV */
|
|
|
|
php_stream_write_string(stream, "PASV\r\n");
|
|
|
|
result = GET_FTP_RESULT(stream);
|
|
|
|
|
|
|
|
/* make sure we got a 227 response */
|
|
|
|
if (result != 227) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* parse pasv command (129, 80, 95, 25, 13, 221) */
|
|
|
|
tpath = tmp_line;
|
|
|
|
/* skip over the "227 Some message " part */
|
|
|
|
for (tpath += 4; *tpath && !isdigit((int) *tpath); tpath++);
|
|
|
|
if (!*tpath) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/* skip over the host ip, to get the port */
|
|
|
|
hoststart = tpath;
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
|
|
for (; isdigit((int) *tpath); tpath++);
|
|
|
|
if (*tpath != ',') {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
*tpath='.';
|
|
|
|
tpath++;
|
|
|
|
}
|
|
|
|
tpath[-1] = '\0';
|
|
|
|
memcpy(ip, hoststart, ip_size);
|
|
|
|
ip[ip_size-1] = '\0';
|
|
|
|
hoststart = ip;
|
|
|
|
|
|
|
|
/* pull out the MSB of the port */
|
|
|
|
portno = (unsigned short) strtoul(tpath, &ttpath, 10) * 256;
|
|
|
|
if (ttpath == NULL) {
|
|
|
|
/* didn't get correct response from PASV */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
tpath = ttpath;
|
|
|
|
if (*tpath != ',') {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
tpath++;
|
|
|
|
/* pull out the LSB of the port */
|
|
|
|
portno += (unsigned short) strtoul(tpath, &ttpath, 10);
|
|
|
|
} else {
|
|
|
|
/* parse epsv command (|||6446|) */
|
|
|
|
for (i = 0, tpath = tmp_line + 4; *tpath; tpath++) {
|
|
|
|
if (*tpath == '|') {
|
|
|
|
i++;
|
|
|
|
if (i == 3)
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (i < 3) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/* pull out the port */
|
|
|
|
portno = (unsigned short) strtoul(tpath + 1, &ttpath, 10);
|
|
|
|
}
|
2000-10-13 08:09:31 +08:00
|
|
|
|
2003-04-25 04:32:14 +08:00
|
|
|
if (ttpath == NULL) {
|
|
|
|
/* didn't get correct response from EPSV/PASV */
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (phoststart) {
|
|
|
|
*phoststart = hoststart;
|
|
|
|
}
|
|
|
|
|
|
|
|
return portno;
|
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
|
|
|
/* {{{ php_fopen_url_wrap_ftp
|
|
|
|
*/
|
|
|
|
php_stream * php_stream_url_wrap_ftp(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC)
|
|
|
|
{
|
|
|
|
php_stream *stream = NULL, *datastream = NULL;
|
|
|
|
php_url *resource = NULL;
|
|
|
|
char tmp_line[512];
|
|
|
|
char ip[sizeof("123.123.123.123")];
|
|
|
|
unsigned short portno;
|
|
|
|
char *scratch, *hoststart = NULL;
|
|
|
|
int result, use_ssl, use_ssl_on_data=0;
|
|
|
|
php_stream *reuseid=NULL;
|
|
|
|
size_t file_size = 0;
|
|
|
|
zval **tmpzval;
|
|
|
|
int allow_overwrite = 0;
|
2003-04-25 05:21:41 +08:00
|
|
|
int read_write = 0;
|
2003-04-25 04:32:14 +08:00
|
|
|
|
|
|
|
tmp_line[0] = '\0';
|
|
|
|
|
2003-04-25 05:21:41 +08:00
|
|
|
if (strpbrk(mode, "r+")) {
|
|
|
|
read_write = 1; /* Open for reading */
|
|
|
|
}
|
|
|
|
if (strpbrk(mode, "wa+")) {
|
|
|
|
if (read_write) {
|
|
|
|
php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "FTP does not support simultaneous read/write connections.");
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
if (strchr(mode, 'a')) {
|
|
|
|
read_write = 3; /* Open for Appending */
|
|
|
|
} else {
|
|
|
|
read_write = 2; /* Open for writting */
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!read_write) {
|
|
|
|
/* No mode specified? */
|
|
|
|
php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "Unknown file open mode.");
|
2003-04-25 04:32:14 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
stream = php_ftp_fopen_connect(wrapper, path, mode, options, opened_path, context, &reuseid, &resource, &use_ssl, &use_ssl_on_data STREAMS_CC TSRMLS_CC);
|
2003-04-25 05:21:41 +08:00
|
|
|
if (!stream) {
|
|
|
|
goto errext;
|
|
|
|
}
|
2003-04-25 04:32:14 +08:00
|
|
|
|
2000-10-13 08:09:31 +08:00
|
|
|
/* set the connection to be binary */
|
2002-03-16 05:03:08 +08:00
|
|
|
php_stream_write_string(stream, "TYPE I\r\n");
|
2002-04-11 06:42:32 +08:00
|
|
|
result = GET_FTP_RESULT(stream);
|
2000-10-13 08:09:31 +08:00
|
|
|
if (result > 299 || result < 200)
|
|
|
|
goto errexit;
|
|
|
|
|
|
|
|
/* find out the size of the file (verifying it exists) */
|
2002-03-16 05:03:08 +08:00
|
|
|
php_stream_write_string(stream, "SIZE ");
|
|
|
|
php_stream_write_string(stream, resource->path);
|
|
|
|
php_stream_write_string(stream, "\r\n");
|
2000-10-13 08:09:31 +08:00
|
|
|
|
|
|
|
/* read the response */
|
2002-04-11 06:42:32 +08:00
|
|
|
result = GET_FTP_RESULT(stream);
|
2003-04-25 05:21:41 +08:00
|
|
|
if (read_write == 1) {
|
|
|
|
/* Read Mode */
|
2002-04-11 06:42:32 +08:00
|
|
|
char *sizestr;
|
|
|
|
|
2000-10-13 08:09:31 +08:00
|
|
|
/* when reading file, it must exist */
|
|
|
|
if (result > 299 || result < 200) {
|
|
|
|
errno = ENOENT;
|
2002-03-16 05:03:08 +08:00
|
|
|
goto errexit;
|
2000-10-13 08:09:31 +08:00
|
|
|
}
|
2002-04-11 06:42:32 +08:00
|
|
|
|
|
|
|
sizestr = strchr(tmp_line, ' ');
|
|
|
|
if (sizestr) {
|
|
|
|
sizestr++;
|
|
|
|
file_size = atoi(sizestr);
|
|
|
|
php_stream_notify_file_size(context, file_size, tmp_line, result);
|
|
|
|
}
|
2003-04-25 05:21:41 +08:00
|
|
|
} else if (read_write == 2) {
|
|
|
|
/* when writing file (but not appending), it must NOT exist, unless a context option exists which allows it */
|
2003-04-22 12:13:09 +08:00
|
|
|
if (context && php_stream_context_get_option(context, "ftp", "overwrite", &tmpzval) == SUCCESS) {
|
|
|
|
allow_overwrite = Z_LVAL_PP(tmpzval);
|
|
|
|
}
|
2000-10-13 08:09:31 +08:00
|
|
|
if (result <= 299 && result >= 200) {
|
2003-04-22 12:13:09 +08:00
|
|
|
if (allow_overwrite) {
|
|
|
|
/* Context permits overwritting file,
|
|
|
|
so we just delete whatever's there in preparation */
|
|
|
|
php_stream_write_string(stream, "DELE ");
|
|
|
|
php_stream_write_string(stream, resource->path);
|
|
|
|
php_stream_write_string(stream, "\r\n");
|
|
|
|
result = GET_FTP_RESULT(stream);
|
|
|
|
if (result >= 300 || result <= 199) {
|
|
|
|
goto errexit;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
errno = EEXIST;
|
|
|
|
goto errexit;
|
|
|
|
}
|
2000-10-13 08:09:31 +08:00
|
|
|
}
|
|
|
|
}
|
2002-04-11 06:42:32 +08:00
|
|
|
|
2000-10-13 08:09:31 +08:00
|
|
|
/* set up the passive connection */
|
2003-04-25 04:32:14 +08:00
|
|
|
portno = php_fopen_do_pasv(stream, ip, sizeof(ip), &hoststart STREAMS_CC TSRMLS_CC);
|
2000-10-13 08:09:31 +08:00
|
|
|
|
2003-04-25 04:32:14 +08:00
|
|
|
if (!portno) {
|
2000-10-13 08:09:31 +08:00
|
|
|
goto errexit;
|
|
|
|
}
|
2003-04-25 04:32:14 +08:00
|
|
|
|
|
|
|
/* Send RETR/STOR command */
|
2003-04-25 05:21:41 +08:00
|
|
|
if (read_write == 1) {
|
2000-10-13 08:09:31 +08:00
|
|
|
/* retrieve file */
|
2002-03-16 05:03:08 +08:00
|
|
|
php_stream_write_string(stream, "RETR ");
|
2003-04-25 05:21:41 +08:00
|
|
|
} else if (read_write == 2) {
|
|
|
|
/* Write new file */
|
2002-03-16 05:03:08 +08:00
|
|
|
php_stream_write_string(stream, "STOR ");
|
2003-04-25 05:21:41 +08:00
|
|
|
} else {
|
|
|
|
/* Append */
|
|
|
|
php_stream_write_string(stream, "APPE ");
|
2000-10-13 08:09:31 +08:00
|
|
|
}
|
|
|
|
if (resource->path != NULL) {
|
2002-03-16 05:03:08 +08:00
|
|
|
php_stream_write_string(stream, resource->path);
|
2000-10-13 08:09:31 +08:00
|
|
|
} else {
|
2002-03-16 05:03:08 +08:00
|
|
|
php_stream_write_string(stream, "/");
|
2000-10-13 08:09:31 +08:00
|
|
|
}
|
2002-09-09 00:45:32 +08:00
|
|
|
php_stream_write_string(stream, "\r\n");
|
2002-11-26 06:00:52 +08:00
|
|
|
|
|
|
|
/* open the data channel */
|
|
|
|
if (hoststart == NULL) {
|
|
|
|
hoststart = resource->host;
|
|
|
|
}
|
|
|
|
datastream = php_stream_sock_open_host(hoststart, portno, SOCK_STREAM, 0, 0);
|
|
|
|
if (datastream == NULL) {
|
|
|
|
goto errexit;
|
|
|
|
}
|
2002-09-09 00:45:32 +08:00
|
|
|
|
2002-11-26 06:00:52 +08:00
|
|
|
result = GET_FTP_RESULT(stream);
|
2003-02-25 12:20:22 +08:00
|
|
|
if (result != 150 && result != 125) {
|
2002-11-26 06:00:52 +08:00
|
|
|
/* Could not retrieve or send the file
|
|
|
|
* this data will only be sent to us after connection on the data port was initiated.
|
|
|
|
*/
|
|
|
|
php_stream_close(datastream);
|
|
|
|
datastream = NULL;
|
|
|
|
goto errexit;
|
|
|
|
}
|
|
|
|
|
2002-09-09 00:45:32 +08:00
|
|
|
/* close control connection if not in ssl mode */
|
|
|
|
if (!use_ssl) {
|
|
|
|
php_stream_write_string(stream, "QUIT\r\n");
|
|
|
|
php_stream_close(stream);
|
2002-09-09 03:11:07 +08:00
|
|
|
stream = NULL;
|
2002-09-09 00:45:32 +08:00
|
|
|
}
|
2002-09-09 03:11:07 +08:00
|
|
|
|
2002-09-09 00:45:32 +08:00
|
|
|
php_stream_context_set(datastream, context);
|
2002-04-11 06:42:32 +08:00
|
|
|
php_stream_notify_progress_init(context, 0, file_size);
|
|
|
|
|
2003-02-28 01:43:38 +08:00
|
|
|
if (use_ssl_on_data && (php_stream_xport_crypto_setup(stream,
|
|
|
|
STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL TSRMLS_CC) < 0 ||
|
|
|
|
php_stream_xport_crypto_enable(stream, 1 TSRMLS_CC) < 0)) {
|
|
|
|
|
2002-09-09 00:45:32 +08:00
|
|
|
php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "Unable to activate SSL mode");
|
|
|
|
php_stream_close(datastream);
|
|
|
|
datastream = NULL;
|
|
|
|
goto errexit;
|
|
|
|
}
|
|
|
|
|
2002-09-09 06:26:11 +08:00
|
|
|
/* remember control stream */
|
|
|
|
datastream->wrapperdata = (zval *)stream;
|
|
|
|
|
2001-06-11 23:14:04 +08:00
|
|
|
php_url_free(resource);
|
2002-09-09 00:45:32 +08:00
|
|
|
return datastream;
|
2000-10-13 08:09:31 +08:00
|
|
|
|
|
|
|
errexit:
|
2001-06-11 23:14:04 +08:00
|
|
|
php_url_free(resource);
|
2002-04-11 06:42:32 +08:00
|
|
|
if (stream) {
|
|
|
|
php_stream_notify_error(context, PHP_STREAM_NOTIFY_FAILURE, tmp_line, result);
|
2002-03-16 05:03:08 +08:00
|
|
|
php_stream_close(stream);
|
2002-04-11 06:42:32 +08:00
|
|
|
}
|
|
|
|
if (tmp_line[0] != '\0')
|
2002-08-11 22:29:01 +08:00
|
|
|
php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "FTP server reports %s", tmp_line);
|
2000-10-13 08:09:31 +08:00
|
|
|
return NULL;
|
|
|
|
}
|
2001-06-06 21:06:12 +08:00
|
|
|
/* }}} */
|
2001-06-05 21:12:10 +08:00
|
|
|
|
2003-04-25 04:32:14 +08:00
|
|
|
/* {{{ php_ftp_dirsteam_read
|
|
|
|
*/
|
|
|
|
static size_t php_ftp_dirstream_read(php_stream *stream, char *buf, size_t count TSRMLS_DC)
|
|
|
|
{
|
|
|
|
php_stream_dirent *ent = (php_stream_dirent *)buf;
|
|
|
|
php_stream *innerstream = (php_stream *)stream->abstract;
|
|
|
|
size_t tmp_len;
|
|
|
|
char *basename;
|
|
|
|
|
|
|
|
if (count != sizeof(php_stream_dirent)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (php_stream_eof(innerstream)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!php_stream_get_line(innerstream, ent->d_name, sizeof(ent->d_name), &tmp_len)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
if (!(basename = php_basename(ent->d_name, tmp_len, NULL, 0))) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strlen(basename) == 0) {
|
|
|
|
efree(basename);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
strcpy(ent->d_name, basename);
|
|
|
|
efree(basename);
|
|
|
|
|
|
|
|
return sizeof(php_stream_dirent);
|
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
|
|
|
/* {{{ php_ftp_dirstream_close
|
|
|
|
*/
|
|
|
|
static int php_ftp_dirstream_close(php_stream *stream, int close_handle TSRMLS_DC)
|
|
|
|
{
|
|
|
|
php_stream *innerstream = (php_stream *)stream->abstract;
|
|
|
|
|
|
|
|
if (innerstream->wrapperdata) {
|
|
|
|
php_stream_close((php_stream *)innerstream->wrapperdata);
|
|
|
|
innerstream->wrapperdata = NULL;
|
|
|
|
}
|
|
|
|
php_stream_close((php_stream *)stream->abstract);
|
|
|
|
stream->abstract = NULL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
|
|
|
/* ftp dirstreams only need to support read and close operations,
|
|
|
|
They can't be rewound because the underlying ftp stream can't be rewound. */
|
|
|
|
static php_stream_ops php_ftp_dirstream_ops = {
|
|
|
|
NULL, /* write */
|
|
|
|
php_ftp_dirstream_read, /* read */
|
|
|
|
php_ftp_dirstream_close, /* close */
|
|
|
|
NULL, /* flush */
|
|
|
|
"ftpdir",
|
|
|
|
NULL, /* rewind */
|
|
|
|
NULL, /* cast */
|
|
|
|
NULL, /* stat */
|
|
|
|
NULL /* set option */
|
|
|
|
};
|
|
|
|
|
|
|
|
/* {{{ php_stream_ftp_opendir
|
|
|
|
*/
|
|
|
|
php_stream * php_stream_ftp_opendir(php_stream_wrapper *wrapper, char *path, char *mode, int options, char **opened_path, php_stream_context *context STREAMS_DC TSRMLS_DC)
|
|
|
|
{
|
|
|
|
php_stream *stream, *reuseid, *datastream = NULL;
|
|
|
|
php_url *resource;
|
|
|
|
int result, use_ssl, use_ssl_on_data = 0;
|
|
|
|
char *hoststart = NULL, tmp_line[512];
|
|
|
|
char ip[sizeof("123.123.123.123")];
|
|
|
|
unsigned short portno;
|
|
|
|
|
|
|
|
stream = php_ftp_fopen_connect(wrapper, path, mode, options, opened_path, context, &reuseid, &resource, &use_ssl, &use_ssl_on_data STREAMS_CC TSRMLS_CC);
|
|
|
|
|
|
|
|
/* set the connection to be ascii */
|
|
|
|
php_stream_write_string(stream, "TYPE A\r\n");
|
|
|
|
result = GET_FTP_RESULT(stream);
|
|
|
|
if (result > 299 || result < 200)
|
|
|
|
goto opendir_errexit;
|
|
|
|
|
|
|
|
/* set up the passive connection */
|
|
|
|
portno = php_fopen_do_pasv(stream, ip, sizeof(ip), &hoststart STREAMS_CC TSRMLS_CC);
|
|
|
|
|
|
|
|
if (!portno) {
|
|
|
|
goto opendir_errexit;
|
|
|
|
}
|
|
|
|
|
|
|
|
php_stream_write_string(stream, "NLST ");
|
|
|
|
if (resource->path != NULL) {
|
|
|
|
php_stream_write_string(stream, resource->path);
|
|
|
|
} else {
|
|
|
|
php_stream_write_string(stream, "/");
|
|
|
|
}
|
|
|
|
php_stream_write_string(stream, "\r\n");
|
|
|
|
|
|
|
|
/* open the data channel */
|
|
|
|
if (hoststart == NULL) {
|
|
|
|
hoststart = resource->host;
|
|
|
|
}
|
|
|
|
datastream = php_stream_sock_open_host(hoststart, portno, SOCK_STREAM, 0, 0);
|
|
|
|
if (datastream == NULL) {
|
|
|
|
goto opendir_errexit;
|
|
|
|
}
|
|
|
|
|
|
|
|
result = GET_FTP_RESULT(stream);
|
|
|
|
if (result != 150 && result != 125) {
|
|
|
|
/* Could not retrieve or send the file
|
|
|
|
* this data will only be sent to us after connection on the data port was initiated.
|
|
|
|
*/
|
|
|
|
php_stream_close(datastream);
|
|
|
|
datastream = NULL;
|
|
|
|
goto opendir_errexit;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* close control connection if not in ssl mode */
|
|
|
|
if (!use_ssl) {
|
|
|
|
php_stream_write_string(stream, "QUIT\r\n");
|
|
|
|
php_stream_close(stream);
|
|
|
|
stream = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
php_stream_context_set(datastream, context);
|
|
|
|
|
|
|
|
if (use_ssl_on_data && (php_stream_xport_crypto_setup(stream,
|
|
|
|
STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL TSRMLS_CC) < 0 ||
|
|
|
|
php_stream_xport_crypto_enable(stream, 1 TSRMLS_CC) < 0)) {
|
|
|
|
|
|
|
|
php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "Unable to activate SSL mode");
|
|
|
|
php_stream_close(datastream);
|
|
|
|
datastream = NULL;
|
|
|
|
goto opendir_errexit;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* remember control stream */
|
|
|
|
datastream->wrapperdata = (zval *)stream;
|
|
|
|
|
|
|
|
php_url_free(resource);
|
|
|
|
return php_stream_alloc(&php_ftp_dirstream_ops, datastream, 0, mode);
|
|
|
|
|
|
|
|
opendir_errexit:
|
|
|
|
php_url_free(resource);
|
|
|
|
if (stream) {
|
|
|
|
php_stream_notify_error(context, PHP_STREAM_NOTIFY_FAILURE, tmp_line, result);
|
|
|
|
php_stream_close(stream);
|
|
|
|
}
|
|
|
|
if (tmp_line[0] != '\0')
|
|
|
|
php_stream_wrapper_log_error(wrapper, options TSRMLS_CC, "FTP server reports %s", tmp_line);
|
|
|
|
return NULL;
|
|
|
|
}
|
|
|
|
/* }}} */
|
|
|
|
|
|
|
|
static php_stream_wrapper_ops ftp_stream_wops = {
|
|
|
|
php_stream_url_wrap_ftp,
|
|
|
|
php_stream_ftp_stream_close, /* stream_close */
|
|
|
|
php_stream_ftp_stream_stat,
|
|
|
|
NULL, /* stat_url */
|
|
|
|
php_stream_ftp_opendir, /* opendir */
|
|
|
|
"FTP"
|
|
|
|
};
|
|
|
|
|
|
|
|
PHPAPI php_stream_wrapper php_stream_ftp_wrapper = {
|
|
|
|
&ftp_stream_wops,
|
|
|
|
NULL,
|
|
|
|
1 /* is_url */
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2001-06-05 21:12:10 +08:00
|
|
|
/*
|
|
|
|
* Local variables:
|
|
|
|
* tab-width: 4
|
|
|
|
* c-basic-offset: 4
|
|
|
|
* End:
|
2001-09-09 21:29:31 +08:00
|
|
|
* vim600: sw=4 ts=4 fdm=marker
|
|
|
|
* vim<600: sw=4 ts=4
|
2001-06-05 21:12:10 +08:00
|
|
|
*/
|