예제 #1
0
/* {{{ php_ftp_fopen_connect
 */
static php_stream *php_ftp_fopen_connect(php_stream_wrapper *wrapper, const char *path, const 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)
{
	php_stream *stream = NULL, *reuseid = NULL;
	php_url *resource = NULL;
	int result, use_ssl, use_ssl_on_data = 0, tmp_len;
	char tmp_line[512];
	char *transport;
	int transport_len;

	resource = php_url_parse(path);
	if (resource == NULL || resource->path == NULL) {
		if (resource && presource) {
			*presource = resource;
		}
		return NULL;
	}

	use_ssl = resource->scheme && (strlen(resource->scheme) > 3) && resource->scheme[3] == 's';

	/* use port 21 if one wasn't specified */
	if (resource->port == 0)
		resource->port = 21;
	
	transport_len = (int)spprintf(&transport, 0, "tcp://%s:%d", resource->host, resource->port);
	stream = php_stream_xport_create(transport, transport_len, REPORT_ERRORS, STREAM_XPORT_CLIENT | STREAM_XPORT_CONNECT, NULL, NULL, context, NULL, NULL);
	efree(transport);
	if (stream == NULL) {
		result = 0; /* silence */
		goto connect_errexit;
	}

	php_stream_context_set(stream, context);
	php_stream_notify_info(context, PHP_STREAM_NOTIFY_CONNECT, NULL, 0);

	/* Start talking to ftp server */
	result = GET_FTP_RESULT(stream);
	if (result > 299 || result < 200) {
		php_stream_notify_error(context, PHP_STREAM_NOTIFY_FAILURE, tmp_line, result);
		goto connect_errexit;
	}

	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;
			} else {
				/* we must reuse the old SSL session id */
				/* if we talk to an old ftpd-ssl */
				reuseid = stream;
			}
		} else {
			/* encrypt data etc */


		}

	}
	
	if (use_ssl) {
		if (php_stream_xport_crypto_setup(stream,
				STREAM_CRYPTO_METHOD_SSLv23_CLIENT, NULL) < 0
				|| php_stream_xport_crypto_enable(stream, 1) < 0) {
			php_stream_wrapper_log_error(wrapper, options, "Unable to activate SSL mode");
			php_stream_close(stream);
			stream = NULL;
			goto connect_errexit;
		}
	
		/* 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);
		use_ssl_on_data = (result >= 200 && result<=299) || reuseid;
#else
		php_stream_write_string(stream, "PROT C\r\n");

		/* get the response */
		result = GET_FTP_RESULT(stream);
#endif
	}

#define PHP_FTP_CNTRL_CHK(val, val_len, err_msg) {	\
	unsigned char *s = val, *e = s + val_len;	\
	while (s < e) {	\
		if (iscntrl(*s)) {	\
			php_stream_wrapper_log_error(wrapper, options, err_msg, val);	\
			goto connect_errexit;	\
		}	\
		s++;	\
	}	\
} 

	/* send the user name */
	if (resource->user != NULL) {
		tmp_len = (int)php_raw_url_decode(resource->user, (int)strlen(resource->user));

		PHP_FTP_CNTRL_CHK(resource->user, tmp_len, "Invalid login %s")

		php_stream_printf(stream, "USER %s\r\n", resource->user);
	} else {
		php_stream_write_string(stream, "USER anonymous\r\n");
	}
	
	/* get the response */
	result = GET_FTP_RESULT(stream);
	
	/* if a password is required, send it */
	if (result >= 300 && result <= 399) {
		php_stream_notify_info(context, PHP_STREAM_NOTIFY_AUTH_REQUIRED, tmp_line, 0);

		if (resource->pass != NULL) {
			tmp_len = (int)php_raw_url_decode(resource->pass, (int)strlen(resource->pass));

			PHP_FTP_CNTRL_CHK(resource->pass, tmp_len, "Invalid password %s")

			php_stream_printf(stream, "PASS %s\r\n", resource->pass);
		} else {
			/* if the user has configured who they are,
			   send that as the password */
			if (FG(from_address)) {
				php_stream_printf(stream, "PASS %s\r\n", FG(from_address));
			} else {
				php_stream_write_string(stream, "PASS anonymous\r\n");
			}
		}

		/* read the response */
		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);
		}
	}
	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 (resource) {
		php_url_free(resource);	
	}

	if (stream) {
		php_stream_close(stream);
	}

	return NULL;
}
FILE *php_fopen_url_wrap_ftp(char *path, char *mode, int options, int *issock, int *socketd, char **opened_path)
{
	FILE *fp=NULL;
	php_url *resource=NULL;
	char tmp_line[512];
	unsigned short portno;
	char *scratch;
	int result;
	int i;
	char *tpath, *ttpath;
	
	resource = url_parse((char *) path);
	if (resource == NULL) {
		php_error(E_WARNING, "Invalid URL specified, %s", path);
		*issock = BAD_URL;
		return NULL;
	} else if (resource->path == NULL) {
		php_error(E_WARNING, "No file-path specified");
		free_url(resource);
		*issock = BAD_URL;
		return NULL;
	}
	/* use port 21 if one wasn't specified */
	if (resource->port == 0)
		resource->port = 21;

	*socketd = php_hostconnect(resource->host, resource->port, SOCK_STREAM, 0);
	if (*socketd == -1)
		goto errexit;
#if 0
	if ((fpc = fdopen(*socketd, "r+")) == NULL) {
		free_url(resource);
		return NULL;
	}
#ifdef HAVE_SETVBUF
	if ((setvbuf(fpc, NULL, _IONBF, 0)) != 0) {
		free_url(resource);
		fclose(fpc);
		return NULL;
	}
#endif
#endif
	
	/* Start talking to ftp server */
	result = php_get_ftp_result(*socketd);
	if (result > 299 || result < 200)
		goto errexit;

	/* send the user name */
	SOCK_WRITE("USER ", *socketd);
	if (resource->user != NULL) {
		php_raw_url_decode(resource->user, strlen(resource->user));
		SOCK_WRITE(resource->user, *socketd);
	} else {
		SOCK_WRITE("anonymous", *socketd);
	}
	SOCK_WRITE("\r\n", *socketd);
	
	/* get the response */
	result = php_get_ftp_result(*socketd);
	
	/* if a password is required, send it */
	if (result >= 300 && result <= 399) {
		SOCK_WRITE("PASS ", *socketd);
		if (resource->pass != NULL) {
			php_raw_url_decode(resource->pass, strlen(resource->pass));
			SOCK_WRITE(resource->pass, *socketd);
		} else {
			/* if the user has configured who they are,
			   send that as the password */
			if (cfg_get_string("from", &scratch) == SUCCESS) {
				SOCK_WRITE(scratch, *socketd);
			} else {
				SOCK_WRITE("anonymous", *socketd);
			}
		}
		SOCK_WRITE("\r\n", *socketd);
		
		/* read the response */
		result = php_get_ftp_result(*socketd);
	}
	if (result > 299 || result < 200)
		goto errexit;
	
	/* set the connection to be binary */
	SOCK_WRITE("TYPE I\r\n", *socketd);
	result = php_get_ftp_result(*socketd);
	if (result > 299 || result < 200)
		goto errexit;
	
	/* find out the size of the file (verifying it exists) */
	SOCK_WRITE("SIZE ", *socketd);
	SOCK_WRITE(resource->path, *socketd);
	SOCK_WRITE("\r\n", *socketd);
	
	/* read the response */
	result = php_get_ftp_result(*socketd);
	if (mode[0] == 'r') {
		/* when reading file, it must exist */
		if (result > 299 || result < 200) {
			php_error(E_WARNING, "File not found");
			free_url(resource);
			SOCK_FCLOSE(*socketd);
			*socketd = 0;
			errno = ENOENT;
			return NULL;
		}
	} else {
		/* when writing file, it must NOT exist */
		if (result <= 299 && result >= 200) {
			php_error(E_WARNING, "File already exists");
			free_url(resource);
			SOCK_FCLOSE(*socketd);
			*socketd = 0;
			errno = EEXIST;
			return NULL;
		}
	}
	
	/* set up the passive connection */

    /* We try EPSV first, needed for IPv6 and works on some IPv4 servers */
	SOCK_WRITE("EPSV\r\n", *socketd);
	while (SOCK_FGETS(tmp_line, sizeof(tmp_line)-1, *socketd) &&
		   !(isdigit((int) tmp_line[0]) && isdigit((int) tmp_line[1]) &&
			 isdigit((int) tmp_line[2]) && tmp_line[3] == ' '));

	/* check if we got a 229 response */
	if (strncmp(tmp_line, "229", 3)) {
		/* EPSV failed, let's try PASV */
		SOCK_WRITE("PASV\r\n", *socketd);
		while (SOCK_FGETS(tmp_line, sizeof(tmp_line)-1, *socketd) &&
			   !(isdigit((int) tmp_line[0]) && isdigit((int) tmp_line[1]) &&
				 isdigit((int) tmp_line[2]) && tmp_line[3] == ' '));
		/* make sure we got a 227 response */
		if (strncmp(tmp_line, "227", 3))
			goto errexit;
		/* 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)
			goto errexit;
		/* skip over the host ip, we just assume it's the same */
		for (i = 0; i < 4; i++) {
			for (; isdigit((int) *tpath); tpath++);
			if (*tpath != ',')
				goto errexit;
			tpath++;
		}
		/* pull out the MSB of the port */
		portno = (unsigned short) strtol(tpath, &ttpath, 10) * 256;
		if (ttpath == NULL) {
			/* didn't get correct response from PASV */
			goto errexit;
		}
		tpath = ttpath;
		if (*tpath != ',')
			goto errexit;
		tpath++;
		/* pull out the LSB of the port */
		portno += (unsigned short) strtol(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)
			goto errexit;
		/* pull out the port */
		portno = (unsigned short) strtol(tpath + 1, &ttpath, 10);
	}
	
	if (ttpath == NULL) {
		/* didn't get correct response from EPSV/PASV */
		goto errexit;
	}
	
	if (mode[0] == 'r') {
		/* retrieve file */
		SOCK_WRITE("RETR ", *socketd);
	} else {
		/* store file */
		SOCK_WRITE("STOR ", *socketd);
	} 
	if (resource->path != NULL) {
		SOCK_WRITE(resource->path, *socketd);
	} else {
		SOCK_WRITE("/", *socketd);
	}
	
	/* close control connection */
	SOCK_WRITE("\r\nQUIT\r\n", *socketd);
	SOCK_FCLOSE(*socketd);

	/* open the data channel */
	*socketd = php_hostconnect(resource->host, portno, SOCK_STREAM, 0);
	if (*socketd == -1)
		goto errexit;
#if 0
	if (mode[0] == 'r') {
		if ((fp = fdopen(*socketd, "r+")) == NULL) {
			free_url(resource);
			return NULL;
		}
	} else {
		if ((fp = fdopen(*socketd, "w+")) == NULL) {
			free_url(resource);
			return NULL;
		}
	}
#ifdef HAVE_SETVBUF
	if ((setvbuf(fp, NULL, _IONBF, 0)) != 0) {
		free_url(resource);
		fclose(fp);
		return NULL;
	}
#endif
#endif
	free_url(resource);
	*issock = 1;
	return (fp);

 errexit:
	free_url(resource);
	SOCK_FCLOSE(*socketd);
	*socketd = 0;
	return NULL;
}