File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / php / ext / ftp / ftp.c
Revision 1.1.1.5 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Sun Jun 15 20:03:48 2014 UTC (10 years, 1 month ago) by misho
Branches: php, MAIN
CVS tags: v5_4_29, HEAD
php 5.4.29

    1: /*
    2:    +----------------------------------------------------------------------+
    3:    | PHP Version 5                                                        |
    4:    +----------------------------------------------------------------------+
    5:    | Copyright (c) 1997-2014 The PHP Group                                |
    6:    +----------------------------------------------------------------------+
    7:    | This source file is subject to version 3.01 of the PHP license,      |
    8:    | that is bundled with this package in the file LICENSE, and is        |
    9:    | available through the world-wide-web at the following url:           |
   10:    | http://www.php.net/license/3_01.txt                                  |
   11:    | If you did not receive a copy of the PHP license and are unable to   |
   12:    | obtain it through the world-wide-web, please send a note to          |
   13:    | license@php.net so we can mail you a copy immediately.               |
   14:    +----------------------------------------------------------------------+
   15:    | Authors: Andrew Skalski <askalski@chek.com>                          |
   16:    |          Stefan Esser <sesser@php.net> (resume functions)            |
   17:    +----------------------------------------------------------------------+
   18:  */
   19: 
   20: /* $Id: ftp.c,v 1.1.1.5 2014/06/15 20:03:48 misho Exp $ */
   21: 
   22: #ifdef HAVE_CONFIG_H
   23: #include "config.h"
   24: #endif
   25: 
   26: #include "php.h"
   27: 
   28: #if HAVE_FTP
   29: 
   30: #include <stdio.h>
   31: #include <ctype.h>
   32: #include <stdlib.h>
   33: #ifdef HAVE_UNISTD_H
   34: #include <unistd.h>
   35: #endif
   36: #include <fcntl.h>
   37: #include <string.h>
   38: #include <time.h>
   39: #ifdef PHP_WIN32
   40: #include <winsock2.h>
   41: #elif defined(NETWARE)
   42: #ifdef USE_WINSOCK    /* Modified to use Winsock (NOVSOCK2.H), at least for now */
   43: #include <novsock2.h>
   44: #else
   45: #include <sys/socket.h>
   46: #include <netinet/in.h>
   47: #include <netdb.h>
   48: #endif
   49: #else
   50: #ifdef HAVE_SYS_TYPES_H
   51: #include <sys/types.h>
   52: #endif
   53: #include <sys/socket.h>
   54: #include <netinet/in.h>
   55: #include <arpa/inet.h>
   56: #include <netdb.h>
   57: #endif
   58: #include <errno.h>
   59: 
   60: #if HAVE_SYS_TIME_H
   61: #include <sys/time.h>
   62: #endif
   63: 
   64: #ifdef HAVE_SYS_SELECT_H
   65: #include <sys/select.h>
   66: #endif
   67: 
   68: #if HAVE_OPENSSL_EXT
   69: #include <openssl/ssl.h>
   70: #endif
   71: 
   72: #include "ftp.h"
   73: #include "ext/standard/fsock.h"
   74: 
   75: /* Additional headers for NetWare */
   76: #if defined(NETWARE) && !defined(USE_WINSOCK)
   77: #include <sys/select.h>
   78: #endif
   79: 
   80: /* sends an ftp command, returns true on success, false on error.
   81:  * it sends the string "cmd args\r\n" if args is non-null, or
   82:  * "cmd\r\n" if args is null
   83:  */
   84: static int		ftp_putcmd(	ftpbuf_t *ftp,
   85: 					const char *cmd,
   86: 					const char *args);
   87: 
   88: /* wrapper around send/recv to handle timeouts */
   89: static int		my_send(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len);
   90: static int		my_recv(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len);
   91: static int		my_accept(ftpbuf_t *ftp, php_socket_t s, struct sockaddr *addr, socklen_t *addrlen);
   92: 
   93: /* reads a line the socket , returns true on success, false on error */
   94: static int		ftp_readline(ftpbuf_t *ftp);
   95: 
   96: /* reads an ftp response, returns true on success, false on error */
   97: static int		ftp_getresp(ftpbuf_t *ftp);
   98: 
   99: /* sets the ftp transfer type */
  100: static int		ftp_type(ftpbuf_t *ftp, ftptype_t type);
  101: 
  102: /* opens up a data stream */
  103: static databuf_t*	ftp_getdata(ftpbuf_t *ftp TSRMLS_DC);
  104: 
  105: /* accepts the data connection, returns updated data buffer */
  106: static databuf_t*	data_accept(databuf_t *data, ftpbuf_t *ftp TSRMLS_DC);
  107: 
  108: /* closes the data connection, returns NULL */
  109: static databuf_t*	data_close(ftpbuf_t *ftp, databuf_t *data);
  110: 
  111: /* generic file lister */
  112: static char**		ftp_genlist(ftpbuf_t *ftp, const char *cmd, const char *path TSRMLS_DC);
  113: 
  114: /* IP and port conversion box */
  115: union ipbox {
  116: 	struct in_addr	ia[2];
  117: 	unsigned short	s[4];
  118: 	unsigned char	c[8];
  119: };
  120: 
  121: /* {{{ ftp_open
  122:  */
  123: ftpbuf_t*
  124: ftp_open(const char *host, short port, long timeout_sec TSRMLS_DC)
  125: {
  126: 	ftpbuf_t		*ftp;
  127: 	socklen_t		 size;
  128: 	struct timeval tv;
  129: 
  130: 
  131: 	/* alloc the ftp structure */
  132: 	ftp = ecalloc(1, sizeof(*ftp));
  133: 
  134: 	tv.tv_sec = timeout_sec;
  135: 	tv.tv_usec = 0;
  136: 
  137: 	ftp->fd = php_network_connect_socket_to_host(host,
  138: 			(unsigned short) (port ? port : 21), SOCK_STREAM,
  139: 			0, &tv, NULL, NULL, NULL, 0 TSRMLS_CC);
  140: 	if (ftp->fd == -1) {
  141: 		goto bail;
  142: 	}
  143: 
  144: 	/* Default Settings */
  145: 	ftp->timeout_sec = timeout_sec;
  146: 	ftp->nb = 0;
  147: 
  148: 	size = sizeof(ftp->localaddr);
  149: 	memset(&ftp->localaddr, 0, size);
  150: 	if (getsockname(ftp->fd, (struct sockaddr*) &ftp->localaddr, &size) != 0) {
  151: 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "getsockname failed: %s (%d)", strerror(errno), errno);
  152: 		goto bail;
  153: 	}
  154: 
  155: 	if (!ftp_getresp(ftp) || ftp->resp != 220) {
  156: 		goto bail;
  157: 	}
  158: 
  159: 	return ftp;
  160: 
  161: bail:
  162: 	if (ftp->fd != -1) {
  163: 		closesocket(ftp->fd);
  164: 	}
  165: 	efree(ftp);
  166: 	return NULL;
  167: }
  168: /* }}} */
  169: 
  170: /* {{{ ftp_close
  171:  */
  172: ftpbuf_t*
  173: ftp_close(ftpbuf_t *ftp)
  174: {
  175: 	if (ftp == NULL) {
  176: 		return NULL;
  177: 	}
  178: 	if (ftp->data) {
  179: 		data_close(ftp, ftp->data);
  180: 	}
  181: 	if (ftp->stream && ftp->closestream) {
  182: 		TSRMLS_FETCH();
  183: 		php_stream_close(ftp->stream);
  184: 	}
  185: 	if (ftp->fd != -1) {
  186: #if HAVE_OPENSSL_EXT
  187: 		if (ftp->ssl_active) {
  188: 			SSL_shutdown(ftp->ssl_handle);
  189: 			SSL_free(ftp->ssl_handle);
  190: 		}
  191: #endif		
  192: 		closesocket(ftp->fd);
  193: 	}	
  194: 	ftp_gc(ftp);
  195: 	efree(ftp);
  196: 	return NULL;
  197: }
  198: /* }}} */
  199: 
  200: /* {{{ ftp_gc
  201:  */
  202: void
  203: ftp_gc(ftpbuf_t *ftp)
  204: {
  205: 	if (ftp == NULL) {
  206: 		return;
  207: 	}
  208: 	if (ftp->pwd) {
  209: 		efree(ftp->pwd);
  210: 		ftp->pwd = NULL;
  211: 	}
  212: 	if (ftp->syst) {
  213: 		efree(ftp->syst);
  214: 		ftp->syst = NULL;
  215: 	}
  216: }
  217: /* }}} */
  218: 
  219: /* {{{ ftp_quit
  220:  */
  221: int
  222: ftp_quit(ftpbuf_t *ftp)
  223: {
  224: 	if (ftp == NULL) {
  225: 		return 0;
  226: 	}
  227: 
  228: 	if (!ftp_putcmd(ftp, "QUIT", NULL)) {
  229: 		return 0;
  230: 	}
  231: 	if (!ftp_getresp(ftp) || ftp->resp != 221) {
  232: 		return 0;
  233: 	}
  234: 
  235: 	if (ftp->pwd) {
  236: 		efree(ftp->pwd);
  237: 		ftp->pwd = NULL;
  238: 	}
  239: 
  240: 	return 1;
  241: }
  242: /* }}} */
  243: 
  244: /* {{{ ftp_login
  245:  */
  246: int
  247: ftp_login(ftpbuf_t *ftp, const char *user, const char *pass TSRMLS_DC)
  248: {
  249: #if HAVE_OPENSSL_EXT
  250: 	SSL_CTX	*ctx = NULL;
  251: 	long ssl_ctx_options = SSL_OP_ALL;
  252: #endif
  253: 	if (ftp == NULL) {
  254: 		return 0;
  255: 	}
  256: 
  257: #if HAVE_OPENSSL_EXT
  258: 	if (ftp->use_ssl && !ftp->ssl_active) {
  259: 		if (!ftp_putcmd(ftp, "AUTH", "TLS")) {
  260: 			return 0;
  261: 		}
  262: 		if (!ftp_getresp(ftp)) {
  263: 			return 0;
  264: 		}
  265: 			
  266: 		if (ftp->resp != 234) {
  267: 			if (!ftp_putcmd(ftp, "AUTH", "SSL")) {
  268: 				return 0;
  269: 			}
  270: 			if (!ftp_getresp(ftp)) {
  271: 				return 0;
  272: 			}
  273: 				
  274: 			if (ftp->resp != 334) {
  275: 				return 0;
  276: 			} else {
  277: 				ftp->old_ssl = 1;
  278: 				ftp->use_ssl_for_data = 1;
  279: 			}
  280: 		}
  281: 		
  282: 		ctx = SSL_CTX_new(SSLv23_client_method());
  283: 		if (ctx == NULL) {
  284: 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create the SSL context");
  285: 			return 0;
  286: 		}
  287: 
  288: #if OPENSSL_VERSION_NUMBER >= 0x0090605fL
  289: 		ssl_ctx_options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
  290: #endif
  291: 		SSL_CTX_set_options(ctx, ssl_ctx_options);
  292: 
  293: 		ftp->ssl_handle = SSL_new(ctx);
  294: 		if (ftp->ssl_handle == NULL) {
  295: 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create the SSL handle");
  296: 			SSL_CTX_free(ctx);
  297: 			return 0;
  298: 		}
  299: 
  300: 		SSL_set_fd(ftp->ssl_handle, ftp->fd);
  301: 
  302: 		if (SSL_connect(ftp->ssl_handle) <= 0) {
  303: 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL/TLS handshake failed");
  304: 			SSL_shutdown(ftp->ssl_handle);
  305: 			SSL_free(ftp->ssl_handle);
  306: 			return 0;
  307: 		}
  308: 
  309: 		ftp->ssl_active = 1;
  310: 
  311: 		if (!ftp->old_ssl) {
  312: 
  313: 			/* set protection buffersize to zero */
  314: 			if (!ftp_putcmd(ftp, "PBSZ", "0")) {
  315: 				return 0;
  316: 			}
  317: 			if (!ftp_getresp(ftp)) {
  318: 				return 0;
  319: 			}
  320: 
  321: 			/* enable data conn encryption */
  322: 			if (!ftp_putcmd(ftp, "PROT", "P")) {
  323: 				return 0;
  324: 			}
  325: 			if (!ftp_getresp(ftp)) {
  326: 				return 0;
  327: 			}
  328: 			
  329: 			ftp->use_ssl_for_data = (ftp->resp >= 200 && ftp->resp <=299);		
  330: 		}
  331: 	}
  332: #endif
  333: 
  334: 	if (!ftp_putcmd(ftp, "USER", user)) {
  335: 		return 0;
  336: 	}
  337: 	if (!ftp_getresp(ftp)) {
  338: 		return 0;
  339: 	}
  340: 	if (ftp->resp == 230) {
  341: 		return 1;
  342: 	}
  343: 	if (ftp->resp != 331) {
  344: 		return 0;
  345: 	}
  346: 	if (!ftp_putcmd(ftp, "PASS", pass)) {
  347: 		return 0;
  348: 	}
  349: 	if (!ftp_getresp(ftp)) {
  350: 		return 0;
  351: 	}
  352: 	return (ftp->resp == 230);
  353: }
  354: /* }}} */
  355: 
  356: /* {{{ ftp_reinit
  357:  */
  358: int
  359: ftp_reinit(ftpbuf_t *ftp)
  360: {
  361: 	if (ftp == NULL) {
  362: 		return 0;
  363: 	}	
  364: 
  365: 	ftp_gc(ftp);
  366: 
  367: 	ftp->nb = 0;
  368: 
  369: 	if (!ftp_putcmd(ftp, "REIN", NULL)) {
  370: 		return 0;
  371: 	}
  372: 	if (!ftp_getresp(ftp) || ftp->resp != 220) {
  373: 		return 0;
  374: 	}
  375: 
  376: 	return 1;
  377: }
  378: /* }}} */
  379: 
  380: /* {{{ ftp_syst
  381:  */
  382: const char*
  383: ftp_syst(ftpbuf_t *ftp)
  384: {
  385: 	char *syst, *end;
  386: 
  387: 	if (ftp == NULL) {
  388: 		return NULL;
  389: 	}
  390: 
  391: 	/* default to cached value */
  392: 	if (ftp->syst) {
  393: 		return ftp->syst;
  394: 	}
  395: 	if (!ftp_putcmd(ftp, "SYST", NULL)) {
  396: 		return NULL;
  397: 	}
  398: 	if (!ftp_getresp(ftp) || ftp->resp != 215) { 
  399: 		return NULL;
  400: 	}
  401: 	syst = ftp->inbuf;
  402: 	while (*syst == ' ') {
  403: 		syst++;
  404: 	}
  405: 	if ((end = strchr(syst, ' '))) {
  406: 		*end = 0;
  407: 	}
  408: 	ftp->syst = estrdup(syst);
  409: 	if (end) {
  410: 		*end = ' ';
  411: 	}
  412: 	return ftp->syst;
  413: }
  414: /* }}} */
  415: 
  416: /* {{{ ftp_pwd
  417:  */
  418: const char*
  419: ftp_pwd(ftpbuf_t *ftp)
  420: {
  421: 	char *pwd, *end;
  422: 
  423: 	if (ftp == NULL) {
  424: 		return NULL;
  425: 	}
  426: 
  427: 	/* default to cached value */
  428: 	if (ftp->pwd) {
  429: 		return ftp->pwd;
  430: 	}
  431: 	if (!ftp_putcmd(ftp, "PWD", NULL)) {
  432: 		return NULL;
  433: 	}
  434: 	if (!ftp_getresp(ftp) || ftp->resp != 257) { 
  435: 		return NULL;
  436: 	}
  437: 	/* copy out the pwd from response */
  438: 	if ((pwd = strchr(ftp->inbuf, '"')) == NULL) { 
  439: 		return NULL;
  440: 	}
  441: 	if ((end = strrchr(++pwd, '"')) == NULL) { 
  442: 		return NULL;
  443: 	}
  444: 	ftp->pwd = estrndup(pwd, end - pwd);
  445: 
  446: 	return ftp->pwd;
  447: }
  448: /* }}} */
  449: 
  450: /* {{{ ftp_exec
  451:  */
  452: int
  453: ftp_exec(ftpbuf_t *ftp, const char *cmd)
  454: {
  455: 	if (ftp == NULL) {
  456: 		return 0;
  457: 	}
  458: 	if (!ftp_putcmd(ftp, "SITE EXEC", cmd)) {
  459: 		return 0;
  460: 	}
  461: 	if (!ftp_getresp(ftp) || ftp->resp != 200) {
  462: 		return 0;
  463: 	}
  464: 
  465: 	return 1;
  466: }
  467: /* }}} */
  468: 
  469: /* {{{ ftp_raw
  470:  */
  471: void
  472: ftp_raw(ftpbuf_t *ftp, const char *cmd, zval *return_value)
  473: {
  474: 	if (ftp == NULL || cmd == NULL) {
  475: 		RETURN_NULL();
  476: 	}
  477: 	if (!ftp_putcmd(ftp, cmd, NULL)) {
  478: 		RETURN_NULL();
  479: 	}
  480: 	array_init(return_value);
  481: 	while (ftp_readline(ftp)) {
  482: 		add_next_index_string(return_value, ftp->inbuf, 1);
  483: 		if (isdigit(ftp->inbuf[0]) && isdigit(ftp->inbuf[1]) && isdigit(ftp->inbuf[2]) && ftp->inbuf[3] == ' ') {
  484: 			return;
  485: 		}
  486: 	}
  487: }
  488: /* }}} */
  489: 
  490: /* {{{ ftp_chdir
  491:  */
  492: int
  493: ftp_chdir(ftpbuf_t *ftp, const char *dir)
  494: {
  495: 	if (ftp == NULL) {
  496: 		return 0;
  497: 	}
  498: 
  499: 	if (ftp->pwd) {
  500: 		efree(ftp->pwd);
  501: 		ftp->pwd = NULL;
  502: 	}
  503: 
  504: 	if (!ftp_putcmd(ftp, "CWD", dir)) {
  505: 		return 0;
  506: 	}
  507: 	if (!ftp_getresp(ftp) || ftp->resp != 250) {
  508: 		return 0;
  509: 	}
  510: 	return 1;
  511: }
  512: /* }}} */
  513: 
  514: /* {{{ ftp_cdup
  515:  */
  516: int
  517: ftp_cdup(ftpbuf_t *ftp)
  518: {
  519: 	if (ftp == NULL) {
  520: 		return 0;
  521: 	}
  522: 
  523: 	if (ftp->pwd) {
  524: 		efree(ftp->pwd);
  525: 		ftp->pwd = NULL;
  526: 	}
  527: 
  528: 	if (!ftp_putcmd(ftp, "CDUP", NULL)) {
  529: 		return 0;
  530: 	}
  531: 	if (!ftp_getresp(ftp) || ftp->resp != 250) {
  532: 		return 0;
  533: 	}
  534: 	return 1;
  535: }
  536: /* }}} */
  537: 
  538: /* {{{ ftp_mkdir
  539:  */
  540: char*
  541: ftp_mkdir(ftpbuf_t *ftp, const char *dir)
  542: {
  543: 	char *mkd, *end;
  544: 
  545: 	if (ftp == NULL) {
  546: 		return NULL;
  547: 	}
  548: 	if (!ftp_putcmd(ftp, "MKD", dir)) {
  549: 		return NULL;
  550: 	}
  551: 	if (!ftp_getresp(ftp) || ftp->resp != 257) {
  552: 		return NULL;
  553: 	}
  554: 	/* copy out the dir from response */
  555: 	if ((mkd = strchr(ftp->inbuf, '"')) == NULL) {
  556: 		mkd = estrdup(dir);
  557: 		return mkd;
  558: 	}
  559: 	if ((end = strrchr(++mkd, '"')) == NULL) {
  560: 		return NULL;
  561: 	}
  562: 	*end = 0;
  563: 	mkd = estrdup(mkd);
  564: 	*end = '"';
  565: 
  566: 	return mkd;
  567: }
  568: /* }}} */
  569: 
  570: /* {{{ ftp_rmdir
  571:  */
  572: int
  573: ftp_rmdir(ftpbuf_t *ftp, const char *dir)
  574: {
  575: 	if (ftp == NULL) {
  576: 		return 0;
  577: 	}
  578: 	if (!ftp_putcmd(ftp, "RMD", dir)) {
  579: 		return 0;
  580: 	}
  581: 	if (!ftp_getresp(ftp) || ftp->resp != 250) {
  582: 		return 0;
  583: 	}
  584: 	return 1;
  585: }
  586: /* }}} */
  587: 
  588: /* {{{ ftp_chmod
  589:  */
  590: int
  591: ftp_chmod(ftpbuf_t *ftp, const int mode, const char *filename, const int filename_len)
  592: {
  593: 	char *buffer;
  594: 
  595: 	if (ftp == NULL || filename_len <= 0) {
  596: 		return 0;
  597: 	}
  598: 
  599: 	spprintf(&buffer, 0, "CHMOD %o %s", mode, filename);
  600: 
  601: 	if (!ftp_putcmd(ftp, "SITE", buffer)) {
  602: 		efree(buffer);
  603: 		return 0;
  604: 	}
  605: 
  606: 	efree(buffer);
  607: 
  608: 	if (!ftp_getresp(ftp) || ftp->resp != 200) {
  609: 		return 0;
  610: 	}
  611: 	
  612: 	return 1;
  613: }
  614: /* }}} */
  615: 
  616: /* {{{ ftp_alloc
  617:  */
  618: int
  619: ftp_alloc(ftpbuf_t *ftp, const long size, char **response)
  620: {
  621: 	char buffer[64];
  622: 
  623: 	if (ftp == NULL || size <= 0) {
  624: 		return 0;
  625: 	}
  626: 
  627: 	snprintf(buffer, sizeof(buffer) - 1, "%ld", size);
  628:     
  629: 	if (!ftp_putcmd(ftp, "ALLO", buffer)) {
  630: 		return 0;
  631: 	}
  632: 
  633: 	if (!ftp_getresp(ftp)) {
  634: 		return 0;
  635: 	}
  636: 
  637: 	if (response) {
  638: 		*response = estrdup(ftp->inbuf);
  639: 	}
  640: 
  641: 	if (ftp->resp < 200 || ftp->resp >= 300) {
  642: 		return 0;
  643: 	}
  644: 
  645: 	return 1;	
  646: }
  647: /* }}} */
  648: 
  649: /* {{{ ftp_nlist
  650:  */
  651: char**
  652: ftp_nlist(ftpbuf_t *ftp, const char *path TSRMLS_DC)
  653: {
  654: 	return ftp_genlist(ftp, "NLST", path TSRMLS_CC);
  655: }
  656: /* }}} */
  657: 
  658: /* {{{ ftp_list
  659:  */
  660: char**
  661: ftp_list(ftpbuf_t *ftp, const char *path, int recursive TSRMLS_DC)
  662: {
  663: 	return ftp_genlist(ftp, ((recursive) ? "LIST -R" : "LIST"), path TSRMLS_CC);
  664: }
  665: /* }}} */
  666: 
  667: /* {{{ ftp_type
  668:  */
  669: int
  670: ftp_type(ftpbuf_t *ftp, ftptype_t type)
  671: {
  672: 	char typechar[2] = "?";
  673: 
  674: 	if (ftp == NULL) {
  675: 		return 0;
  676: 	}
  677: 	if (type == ftp->type) { 
  678: 		return 1;
  679: 	}
  680: 	if (type == FTPTYPE_ASCII) {
  681: 		typechar[0] = 'A';
  682: 	} else if (type == FTPTYPE_IMAGE) {
  683: 		typechar[0] = 'I';
  684: 	} else {
  685: 		return 0;
  686: 	}
  687: 	if (!ftp_putcmd(ftp, "TYPE", typechar)) {
  688: 		return 0;
  689: 	}
  690: 	if (!ftp_getresp(ftp) || ftp->resp != 200) {
  691: 		return 0;
  692: 	}
  693: 	ftp->type = type;
  694: 
  695: 	return 1;
  696: }
  697: /* }}} */
  698: 
  699: /* {{{ ftp_pasv
  700:  */
  701: int
  702: ftp_pasv(ftpbuf_t *ftp, int pasv)
  703: {
  704: 	char			*ptr;
  705: 	union ipbox		ipbox;
  706: 	unsigned long		b[6];
  707: 	socklen_t			n;
  708: 	struct sockaddr *sa;
  709: 	struct sockaddr_in *sin;
  710: 
  711: 	if (ftp == NULL) {
  712: 		return 0;
  713: 	}
  714: 	if (pasv && ftp->pasv == 2) {
  715: 		return 1;
  716: 	}
  717: 	ftp->pasv = 0;
  718: 	if (!pasv) {
  719: 		return 1;
  720: 	}
  721: 	n = sizeof(ftp->pasvaddr);
  722: 	memset(&ftp->pasvaddr, 0, n);
  723: 	sa = (struct sockaddr *) &ftp->pasvaddr;
  724: 
  725: #if HAVE_IPV6
  726: 	if (getpeername(ftp->fd, sa, &n) < 0) {
  727: 		return 0;
  728: 	}
  729: 	if (sa->sa_family == AF_INET6) {
  730: 		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sa;
  731: 		char *endptr, delimiter;
  732: 
  733: 		/* try EPSV first */
  734: 		if (!ftp_putcmd(ftp, "EPSV", NULL)) {
  735: 			return 0;
  736: 		}
  737: 		if (!ftp_getresp(ftp)) {
  738: 			return 0;
  739: 		}
  740: 		if (ftp->resp == 229) {
  741: 			/* parse out the port */
  742: 			for (ptr = ftp->inbuf; *ptr && *ptr != '('; ptr++);
  743: 			if (!*ptr) {
  744: 				return 0;
  745: 			}
  746: 			delimiter = *++ptr;
  747: 			for (n = 0; *ptr && n < 3; ptr++) {
  748: 				if (*ptr == delimiter) {
  749: 					n++;
  750: 				}
  751: 			}
  752: 
  753: 			sin6->sin6_port = htons((unsigned short) strtoul(ptr, &endptr, 10));
  754: 			if (ptr == endptr || *endptr != delimiter) {
  755: 				return 0;
  756: 			}
  757: 			ftp->pasv = 2;
  758: 			return 1;
  759: 		}
  760: 	}
  761: 
  762: 	/* fall back to PASV */
  763: #endif
  764: 
  765: 	if (!ftp_putcmd(ftp, "PASV", NULL)) {
  766: 		return 0;
  767: 	}
  768: 	if (!ftp_getresp(ftp) || ftp->resp != 227) { 
  769: 		return 0;
  770: 	}
  771: 	/* parse out the IP and port */
  772: 	for (ptr = ftp->inbuf; *ptr && !isdigit(*ptr); ptr++);
  773: 	n = sscanf(ptr, "%lu,%lu,%lu,%lu,%lu,%lu", &b[0], &b[1], &b[2], &b[3], &b[4], &b[5]);
  774: 	if (n != 6) {
  775: 		return 0;
  776: 	}
  777: 	for (n = 0; n < 6; n++) {
  778: 		ipbox.c[n] = (unsigned char) b[n];
  779: 	}
  780: 	sin = (struct sockaddr_in *) sa;
  781: 	sin->sin_family = AF_INET;
  782: 	sin->sin_addr = ipbox.ia[0];
  783: 	sin->sin_port = ipbox.s[2];
  784: 
  785: 	ftp->pasv = 2;
  786: 
  787: 	return 1;
  788: }
  789: /* }}} */
  790: 
  791: /* {{{ ftp_get
  792:  */
  793: int
  794: ftp_get(ftpbuf_t *ftp, php_stream *outstream, const char *path, ftptype_t type, long resumepos TSRMLS_DC)
  795: {
  796: 	databuf_t		*data = NULL;
  797: 	int			lastch;
  798: 	size_t			rcvd;
  799: 	char			arg[11];
  800: 
  801: 	if (ftp == NULL) {
  802: 		return 0;
  803: 	}
  804: 	if (!ftp_type(ftp, type)) {
  805: 		goto bail;
  806: 	}
  807: 
  808: 	if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) {
  809: 		goto bail;
  810: 	}
  811: 	
  812: 	ftp->data = data;
  813: 
  814: 	if (resumepos > 0) {
  815: 		snprintf(arg, sizeof(arg), "%ld", resumepos);
  816: 		if (!ftp_putcmd(ftp, "REST", arg)) {
  817: 			goto bail;
  818: 		}
  819: 		if (!ftp_getresp(ftp) || (ftp->resp != 350)) {
  820: 			goto bail;
  821: 		}
  822: 	}
  823: 
  824: 	if (!ftp_putcmd(ftp, "RETR", path)) {
  825: 		goto bail;
  826: 	}
  827: 	if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
  828: 		goto bail;
  829: 	}
  830: 
  831: 	if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) {
  832: 		goto bail;
  833: 	}
  834: 
  835: 	lastch = 0;
  836: 	while ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) {
  837: 		if (rcvd == -1) {
  838: 			goto bail;
  839: 		}
  840: 
  841: 		if (type == FTPTYPE_ASCII) {
  842: #ifndef PHP_WIN32
  843: 			char *s;
  844: #endif
  845: 			char *ptr = data->buf;
  846: 			char *e = ptr + rcvd;
  847: 			/* logic depends on the OS EOL
  848: 			 * Win32 -> \r\n
  849: 			 * Everything Else \n
  850: 			 */
  851: #ifdef PHP_WIN32
  852: 			php_stream_write(outstream, ptr, (e - ptr));
  853: 			ptr = e;
  854: #else
  855: 			while (e > ptr && (s = memchr(ptr, '\r', (e - ptr)))) {
  856: 				php_stream_write(outstream, ptr, (s - ptr));
  857: 				if (*(s + 1) == '\n') {
  858: 					s++;
  859: 					php_stream_putc(outstream, '\n');
  860: 				}
  861: 				ptr = s + 1;
  862: 			}
  863: #endif
  864: 			if (ptr < e) {
  865: 				php_stream_write(outstream, ptr, (e - ptr));
  866: 			}
  867: 		} else if (rcvd != php_stream_write(outstream, data->buf, rcvd)) {
  868: 			goto bail;
  869: 		}
  870: 	}
  871: 
  872: 	ftp->data = data = data_close(ftp, data);
  873: 
  874: 	if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
  875: 		goto bail;
  876: 	}
  877: 
  878: 	return 1;
  879: bail:
  880: 	ftp->data = data_close(ftp, data);
  881: 	return 0;
  882: }
  883: /* }}} */
  884: 
  885: /* {{{ ftp_put
  886:  */
  887: int
  888: ftp_put(ftpbuf_t *ftp, const char *path, php_stream *instream, ftptype_t type, long startpos TSRMLS_DC)
  889: {
  890: 	databuf_t		*data = NULL;
  891: 	long			size;
  892: 	char			*ptr;
  893: 	int			ch;
  894: 	char			arg[11];
  895: 
  896: 	if (ftp == NULL) {
  897: 		return 0;
  898: 	}
  899: 	if (!ftp_type(ftp, type)) {
  900: 		goto bail;
  901: 	}
  902: 	if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) {
  903: 		goto bail;
  904: 	}
  905: 	ftp->data = data;	
  906: 
  907: 	if (startpos > 0) {
  908: 		snprintf(arg, sizeof(arg), "%ld", startpos);
  909: 		if (!ftp_putcmd(ftp, "REST", arg)) {
  910: 			goto bail;
  911: 		}
  912: 		if (!ftp_getresp(ftp) || (ftp->resp != 350)) {
  913: 			goto bail;
  914: 		}
  915: 	}
  916: 
  917: 	if (!ftp_putcmd(ftp, "STOR", path)) {
  918: 		goto bail;
  919: 	}
  920: 	if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
  921: 		goto bail;
  922: 	}
  923: 	if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) {
  924: 		goto bail;
  925: 	}
  926: 
  927: 	size = 0;
  928: 	ptr = data->buf;
  929: 	while (!php_stream_eof(instream) && (ch = php_stream_getc(instream))!=EOF) {
  930: 		/* flush if necessary */
  931: 		if (FTP_BUFSIZE - size < 2) {
  932: 			if (my_send(ftp, data->fd, data->buf, size) != size) {
  933: 				goto bail;
  934: 			}
  935: 			ptr = data->buf;
  936: 			size = 0;
  937: 		}
  938: 
  939: 		if (ch == '\n' && type == FTPTYPE_ASCII) {
  940: 			*ptr++ = '\r';
  941: 			size++;
  942: 		}
  943: 
  944: 		*ptr++ = ch;
  945: 		size++;
  946: 	}
  947: 
  948: 	if (size && my_send(ftp, data->fd, data->buf, size) != size) {
  949: 		goto bail;
  950: 	}
  951: 	ftp->data = data = data_close(ftp, data);
  952: 
  953: 	if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250 && ftp->resp != 200)) {
  954: 		goto bail;
  955: 	}
  956: 	return 1;
  957: bail:
  958: 	ftp->data = data_close(ftp, data);
  959: 	return 0;
  960: }
  961: /* }}} */
  962: 
  963: /* {{{ ftp_size
  964:  */
  965: long
  966: ftp_size(ftpbuf_t *ftp, const char *path)
  967: {
  968: 	if (ftp == NULL) {
  969: 		return -1;
  970: 	}
  971: 	if (!ftp_type(ftp, FTPTYPE_IMAGE)) {
  972: 		return -1;
  973: 	}
  974: 	if (!ftp_putcmd(ftp, "SIZE", path)) {
  975: 		return -1;
  976: 	}
  977: 	if (!ftp_getresp(ftp) || ftp->resp != 213) {
  978: 		return -1;
  979: 	}
  980: 	return atol(ftp->inbuf);
  981: }
  982: /* }}} */
  983: 
  984: /* {{{ ftp_mdtm
  985:  */
  986: time_t
  987: ftp_mdtm(ftpbuf_t *ftp, const char *path)
  988: {
  989: 	time_t		stamp;
  990: 	struct tm	*gmt, tmbuf;
  991: 	struct tm	tm;
  992: 	char		*ptr;
  993: 	int		n;
  994: 
  995: 	if (ftp == NULL) {
  996: 		return -1;
  997: 	}
  998: 	if (!ftp_putcmd(ftp, "MDTM", path)) {
  999: 		return -1;
 1000: 	}
 1001: 	if (!ftp_getresp(ftp) || ftp->resp != 213) {
 1002: 		return -1;
 1003: 	}
 1004: 	/* parse out the timestamp */
 1005: 	for (ptr = ftp->inbuf; *ptr && !isdigit(*ptr); ptr++);
 1006: 	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);
 1007: 	if (n != 6) {
 1008: 		return -1;
 1009: 	}
 1010: 	tm.tm_year -= 1900;
 1011: 	tm.tm_mon--;
 1012: 	tm.tm_isdst = -1;
 1013: 
 1014: 	/* figure out the GMT offset */
 1015: 	stamp = time(NULL);
 1016: 	gmt = php_gmtime_r(&stamp, &tmbuf);
 1017: 	if (!gmt) {
 1018: 		return -1;
 1019: 	}
 1020: 	gmt->tm_isdst = -1;
 1021: 
 1022: 	/* apply the GMT offset */
 1023: 	tm.tm_sec += stamp - mktime(gmt);
 1024: 	tm.tm_isdst = gmt->tm_isdst;
 1025: 
 1026: 	stamp = mktime(&tm);
 1027: 
 1028: 	return stamp;
 1029: }
 1030: /* }}} */
 1031: 
 1032: /* {{{ ftp_delete
 1033:  */
 1034: int
 1035: ftp_delete(ftpbuf_t *ftp, const char *path)
 1036: {
 1037: 	if (ftp == NULL) {
 1038: 		return 0;
 1039: 	}
 1040: 	if (!ftp_putcmd(ftp, "DELE", path)) {
 1041: 		return 0;
 1042: 	}
 1043: 	if (!ftp_getresp(ftp) || ftp->resp != 250) {
 1044: 		return 0;
 1045: 	}
 1046: 
 1047: 	return 1;
 1048: }
 1049: /* }}} */
 1050: 
 1051: /* {{{ ftp_rename
 1052:  */
 1053: int
 1054: ftp_rename(ftpbuf_t *ftp, const char *src, const char *dest)
 1055: {
 1056: 	if (ftp == NULL) {
 1057: 		return 0;
 1058: 	}
 1059: 	if (!ftp_putcmd(ftp, "RNFR", src)) {
 1060: 		return 0;
 1061: 	}
 1062: 	if (!ftp_getresp(ftp) || ftp->resp != 350) {
 1063: 		return 0;
 1064: 	}
 1065: 	if (!ftp_putcmd(ftp, "RNTO", dest)) {
 1066: 		return 0;
 1067: 	}
 1068: 	if (!ftp_getresp(ftp) || ftp->resp != 250) {
 1069: 		return 0;
 1070: 	}
 1071: 	return 1;
 1072: }
 1073: /* }}} */
 1074: 
 1075: /* {{{ ftp_site
 1076:  */
 1077: int
 1078: ftp_site(ftpbuf_t *ftp, const char *cmd)
 1079: {
 1080: 	if (ftp == NULL) {
 1081: 		return 0;
 1082: 	}
 1083: 	if (!ftp_putcmd(ftp, "SITE", cmd)) {
 1084: 		return 0;
 1085: 	}
 1086: 	if (!ftp_getresp(ftp) || ftp->resp < 200 || ftp->resp >= 300) {
 1087: 		return 0;
 1088: 	}
 1089: 
 1090: 	return 1;
 1091: }
 1092: /* }}} */
 1093: 
 1094: /* static functions */
 1095: 
 1096: /* {{{ ftp_putcmd
 1097:  */
 1098: int
 1099: ftp_putcmd(ftpbuf_t *ftp, const char *cmd, const char *args)
 1100: {
 1101: 	int		size;
 1102: 	char		*data;
 1103: 
 1104: 	if (strpbrk(cmd, "\r\n")) {
 1105: 		return 0;
 1106: 	} 
 1107: 	/* build the output buffer */
 1108: 	if (args && args[0]) {
 1109: 		/* "cmd args\r\n\0" */
 1110: 		if (strlen(cmd) + strlen(args) + 4 > FTP_BUFSIZE) {
 1111: 			return 0;
 1112: 		}
 1113: 		if (strpbrk(args, "\r\n")) {
 1114: 			return 0;
 1115: 		}
 1116: 		size = slprintf(ftp->outbuf, sizeof(ftp->outbuf), "%s %s\r\n", cmd, args);
 1117: 	} else {
 1118: 		/* "cmd\r\n\0" */
 1119: 		if (strlen(cmd) + 3 > FTP_BUFSIZE) {
 1120: 			return 0;
 1121: 		}
 1122: 		size = slprintf(ftp->outbuf, sizeof(ftp->outbuf), "%s\r\n", cmd);
 1123: 	}
 1124: 
 1125: 	data = ftp->outbuf;
 1126: 
 1127: 	/* Clear the extra-lines buffer */
 1128: 	ftp->extra = NULL;
 1129: 
 1130: 	if (my_send(ftp, ftp->fd, data, size) != size) {
 1131: 		return 0;
 1132: 	}
 1133: 	return 1;
 1134: }
 1135: /* }}} */
 1136: 
 1137: /* {{{ ftp_readline
 1138:  */
 1139: int
 1140: ftp_readline(ftpbuf_t *ftp)
 1141: {
 1142: 	long		size, rcvd;
 1143: 	char		*data, *eol;
 1144: 
 1145: 	/* shift the extra to the front */
 1146: 	size = FTP_BUFSIZE;
 1147: 	rcvd = 0;
 1148: 	if (ftp->extra) {
 1149: 		memmove(ftp->inbuf, ftp->extra, ftp->extralen);
 1150: 		rcvd = ftp->extralen;
 1151: 	}
 1152: 
 1153: 	data = ftp->inbuf;
 1154: 
 1155: 	do {
 1156: 		size -= rcvd;
 1157: 		for (eol = data; rcvd; rcvd--, eol++) {
 1158: 			if (*eol == '\r') {
 1159: 				*eol = 0;
 1160: 				ftp->extra = eol + 1;
 1161: 				if (rcvd > 1 && *(eol + 1) == '\n') {
 1162: 					ftp->extra++;
 1163: 					rcvd--;
 1164: 				}
 1165: 				if ((ftp->extralen = --rcvd) == 0) {
 1166: 					ftp->extra = NULL;
 1167: 				}
 1168: 				return 1;
 1169: 			} else if (*eol == '\n') {
 1170: 				*eol = 0;
 1171: 				ftp->extra = eol + 1;
 1172: 				if ((ftp->extralen = --rcvd) == 0) {
 1173: 					ftp->extra = NULL;
 1174: 				}
 1175: 				return 1;
 1176: 			}
 1177: 		}
 1178: 
 1179: 		data = eol;
 1180: 		if ((rcvd = my_recv(ftp, ftp->fd, data, size)) < 1) {
 1181: 			return 0;
 1182: 		}
 1183: 	} while (size);
 1184: 
 1185: 	return 0;
 1186: }
 1187: /* }}} */
 1188: 
 1189: /* {{{ ftp_getresp
 1190:  */
 1191: int
 1192: ftp_getresp(ftpbuf_t *ftp)
 1193: {
 1194: 	char *buf;
 1195: 
 1196: 	if (ftp == NULL) {
 1197: 		return 0;
 1198: 	}
 1199: 	buf = ftp->inbuf;
 1200: 	ftp->resp = 0;
 1201: 
 1202: 	while (1) {
 1203: 
 1204: 		if (!ftp_readline(ftp)) {
 1205: 			return 0;
 1206: 		}
 1207: 
 1208: 		/* Break out when the end-tag is found */
 1209: 		if (isdigit(ftp->inbuf[0]) && isdigit(ftp->inbuf[1]) && isdigit(ftp->inbuf[2]) && ftp->inbuf[3] == ' ') {
 1210: 			break;
 1211: 		}
 1212: 	}
 1213: 
 1214: 	/* translate the tag */
 1215: 	if (!isdigit(ftp->inbuf[0]) || !isdigit(ftp->inbuf[1]) || !isdigit(ftp->inbuf[2])) {
 1216: 		return 0;
 1217: 	}
 1218: 
 1219: 	ftp->resp = 100 * (ftp->inbuf[0] - '0') + 10 * (ftp->inbuf[1] - '0') + (ftp->inbuf[2] - '0');
 1220: 
 1221: 	memmove(ftp->inbuf, ftp->inbuf + 4, FTP_BUFSIZE - 4);
 1222: 
 1223: 	if (ftp->extra) {
 1224: 		ftp->extra -= 4;
 1225: 	}
 1226: 	return 1;
 1227: }
 1228: /* }}} */
 1229: 
 1230: /* {{{ my_send
 1231:  */
 1232: int
 1233: my_send(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len)
 1234: {
 1235: 	long		size, sent;
 1236:     int         n;
 1237: 
 1238: 	size = len;
 1239: 	while (size) {
 1240: 		n = php_pollfd_for_ms(s, POLLOUT, ftp->timeout_sec * 1000);
 1241: 
 1242: 		if (n < 1) {
 1243: 
 1244: #if !defined(PHP_WIN32) && !(defined(NETWARE) && defined(USE_WINSOCK))
 1245: 			if (n == 0) {
 1246: 				errno = ETIMEDOUT;
 1247: 			}
 1248: #endif
 1249: 			return -1;
 1250: 		}
 1251: 
 1252: #if HAVE_OPENSSL_EXT
 1253: 		if (ftp->use_ssl && ftp->fd == s && ftp->ssl_active) {
 1254: 			sent = SSL_write(ftp->ssl_handle, buf, size);
 1255: 		} else if (ftp->use_ssl && ftp->fd != s && ftp->use_ssl_for_data && ftp->data->ssl_active) {	
 1256: 			sent = SSL_write(ftp->data->ssl_handle, buf, size);
 1257: 		} else {
 1258: #endif
 1259: 			sent = send(s, buf, size, 0);
 1260: #if HAVE_OPENSSL_EXT
 1261: 		}
 1262: #endif
 1263: 		if (sent == -1) {
 1264: 			return -1;
 1265: 		}
 1266: 
 1267: 		buf = (char*) buf + sent;
 1268: 		size -= sent;
 1269: 	}
 1270: 
 1271: 	return len;
 1272: }
 1273: /* }}} */
 1274: 
 1275: /* {{{ my_recv
 1276:  */
 1277: int
 1278: my_recv(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len)
 1279: {
 1280: 	int		n, nr_bytes;
 1281: 
 1282: 	n = php_pollfd_for_ms(s, PHP_POLLREADABLE, ftp->timeout_sec * 1000);
 1283: 	if (n < 1) {
 1284: #if !defined(PHP_WIN32) && !(defined(NETWARE) && defined(USE_WINSOCK))
 1285: 		if (n == 0) {
 1286: 			errno = ETIMEDOUT;
 1287: 		}
 1288: #endif
 1289: 		return -1;
 1290: 	}
 1291: 
 1292: #if HAVE_OPENSSL_EXT
 1293: 	if (ftp->use_ssl && ftp->fd == s && ftp->ssl_active) {
 1294: 		nr_bytes = SSL_read(ftp->ssl_handle, buf, len);
 1295: 	} else if (ftp->use_ssl && ftp->fd != s && ftp->use_ssl_for_data && ftp->data->ssl_active) {	
 1296: 		nr_bytes = SSL_read(ftp->data->ssl_handle, buf, len);
 1297: 	} else {
 1298: #endif
 1299: 		nr_bytes = recv(s, buf, len, 0);
 1300: #if HAVE_OPENSSL_EXT
 1301: 	}
 1302: #endif	
 1303: 	return (nr_bytes);
 1304: }
 1305: /* }}} */
 1306: 
 1307: /* {{{ data_available
 1308:  */
 1309: int
 1310: data_available(ftpbuf_t *ftp, php_socket_t s)
 1311: {
 1312: 	int		n;
 1313: 
 1314: 	n = php_pollfd_for_ms(s, PHP_POLLREADABLE, 1000);
 1315: 	if (n < 1) {
 1316: #if !defined(PHP_WIN32) && !(defined(NETWARE) && defined(USE_WINSOCK))
 1317: 		if (n == 0) {
 1318: 			errno = ETIMEDOUT;
 1319: 		}
 1320: #endif
 1321: 		return 0;
 1322: 	}
 1323: 
 1324: 	return 1;
 1325: }
 1326: /* }}} */
 1327: /* {{{ data_writeable
 1328:  */
 1329: int
 1330: data_writeable(ftpbuf_t *ftp, php_socket_t s)
 1331: {
 1332: 	int		n;
 1333: 
 1334: 	n = php_pollfd_for_ms(s, POLLOUT, 1000);
 1335: 	if (n < 1) {
 1336: #ifndef PHP_WIN32
 1337: 		if (n == 0) {
 1338: 			errno = ETIMEDOUT;
 1339: 		}
 1340: #endif
 1341: 		return 0;
 1342: 	}
 1343: 
 1344: 	return 1;
 1345: }
 1346: /* }}} */
 1347: 
 1348: /* {{{ my_accept
 1349:  */
 1350: int
 1351: my_accept(ftpbuf_t *ftp, php_socket_t s, struct sockaddr *addr, socklen_t *addrlen)
 1352: {
 1353: 	int		n;
 1354: 
 1355: 	n = php_pollfd_for_ms(s, PHP_POLLREADABLE, ftp->timeout_sec * 1000);
 1356: 	if (n < 1) {
 1357: #if !defined(PHP_WIN32) && !(defined(NETWARE) && defined(USE_WINSOCK))
 1358: 		if (n == 0) {
 1359: 			errno = ETIMEDOUT;
 1360: 		}
 1361: #endif
 1362: 		return -1;
 1363: 	}
 1364: 
 1365: 	return accept(s, addr, addrlen);
 1366: }
 1367: /* }}} */
 1368: 
 1369: /* {{{ ftp_getdata
 1370:  */
 1371: databuf_t*
 1372: ftp_getdata(ftpbuf_t *ftp TSRMLS_DC)
 1373: {
 1374: 	int			fd = -1;
 1375: 	databuf_t		*data;
 1376: 	php_sockaddr_storage addr;
 1377: 	struct sockaddr *sa;
 1378: 	socklen_t		size;
 1379: 	union ipbox		ipbox;
 1380: 	char			arg[sizeof("255, 255, 255, 255, 255, 255")];
 1381: 	struct timeval	tv;
 1382: 
 1383: 
 1384: 	/* ask for a passive connection if we need one */
 1385: 	if (ftp->pasv && !ftp_pasv(ftp, 1)) {
 1386: 		return NULL;
 1387: 	}
 1388: 	/* alloc the data structure */
 1389: 	data = ecalloc(1, sizeof(*data));
 1390: 	data->listener = -1;
 1391: 	data->fd = -1;
 1392: 	data->type = ftp->type;
 1393: 
 1394: 	sa = (struct sockaddr *) &ftp->localaddr;
 1395: 	/* bind/listen */
 1396: 	if ((fd = socket(sa->sa_family, SOCK_STREAM, 0)) == SOCK_ERR) {
 1397: 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "socket() failed: %s (%d)", strerror(errno), errno);
 1398: 		goto bail;
 1399: 	}
 1400: 
 1401: 	/* passive connection handler */
 1402: 	if (ftp->pasv) {
 1403: 		/* clear the ready status */
 1404: 		ftp->pasv = 1;
 1405: 
 1406: 		/* connect */
 1407: 		/* Win 95/98 seems not to like size > sizeof(sockaddr_in) */
 1408: 		size = php_sockaddr_size(&ftp->pasvaddr);
 1409: 		tv.tv_sec = ftp->timeout_sec;
 1410: 		tv.tv_usec = 0;
 1411: 		if (php_connect_nonb(fd, (struct sockaddr*) &ftp->pasvaddr, size, &tv) == -1) {
 1412: 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_connect_nonb() failed: %s (%d)", strerror(errno), errno);
 1413: 			goto bail;
 1414: 		}
 1415: 
 1416: 		data->fd = fd;
 1417: 
 1418: 		ftp->data = data;
 1419: 		return data;
 1420: 	}
 1421: 
 1422: 
 1423: 	/* active (normal) connection */
 1424: 
 1425: 	/* bind to a local address */
 1426: 	php_any_addr(sa->sa_family, &addr, 0);
 1427: 	size = php_sockaddr_size(&addr);
 1428: 
 1429: 	if (bind(fd, (struct sockaddr*) &addr, size) != 0) {
 1430: 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "bind() failed: %s (%d)", strerror(errno), errno);
 1431: 		goto bail;
 1432: 	}
 1433: 
 1434: 	if (getsockname(fd, (struct sockaddr*) &addr, &size) != 0) {
 1435: 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "getsockname() failed: %s (%d)", strerror(errno), errno);
 1436: 		goto bail;
 1437: 	}
 1438: 
 1439: 	if (listen(fd, 5) != 0) {
 1440: 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "listen() failed: %s (%d)", strerror(errno), errno);
 1441: 		goto bail;
 1442: 	}
 1443: 
 1444: 	data->listener = fd;
 1445: 
 1446: #if HAVE_IPV6 && HAVE_INET_NTOP
 1447: 	if (sa->sa_family == AF_INET6) {
 1448: 		/* need to use EPRT */
 1449: 		char eprtarg[INET6_ADDRSTRLEN + sizeof("|x||xxxxx|")];
 1450: 		char out[INET6_ADDRSTRLEN];
 1451: 		inet_ntop(AF_INET6, &((struct sockaddr_in6*) sa)->sin6_addr, out, sizeof(out));
 1452: 		snprintf(eprtarg, sizeof(eprtarg), "|2|%s|%hu|", out, ntohs(((struct sockaddr_in6 *) &addr)->sin6_port));
 1453: 
 1454: 		if (!ftp_putcmd(ftp, "EPRT", eprtarg)) {
 1455: 			goto bail;
 1456: 		}
 1457: 
 1458: 		if (!ftp_getresp(ftp) || ftp->resp != 200) {
 1459: 			goto bail;
 1460: 		}
 1461: 
 1462: 		ftp->data = data;
 1463: 		return data;
 1464: 	}
 1465: #endif
 1466: 
 1467: 	/* send the PORT */
 1468: 	ipbox.ia[0] = ((struct sockaddr_in*) sa)->sin_addr;
 1469: 	ipbox.s[2] = ((struct sockaddr_in*) &addr)->sin_port;
 1470: 	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]);
 1471: 
 1472: 	if (!ftp_putcmd(ftp, "PORT", arg)) {
 1473: 		goto bail;
 1474: 	}
 1475: 	if (!ftp_getresp(ftp) || ftp->resp != 200) {
 1476: 		goto bail;
 1477: 	}
 1478: 
 1479: 	ftp->data = data;
 1480: 	return data;
 1481: 
 1482: bail:
 1483: 	if (fd != -1) {
 1484: 		closesocket(fd);
 1485: 	}
 1486: 	efree(data);
 1487: 	return NULL;
 1488: }
 1489: /* }}} */
 1490: 
 1491: /* {{{ data_accept
 1492:  */
 1493: databuf_t*
 1494: data_accept(databuf_t *data, ftpbuf_t *ftp TSRMLS_DC)
 1495: {
 1496: 	php_sockaddr_storage addr;
 1497: 	socklen_t			size;
 1498: 
 1499: #if HAVE_OPENSSL_EXT
 1500: 	SSL_CTX		*ctx;
 1501: 	long ssl_ctx_options = SSL_OP_ALL;
 1502: #endif
 1503: 
 1504: 	if (data->fd != -1) {
 1505: 		goto data_accepted;
 1506: 	}
 1507: 	size = sizeof(addr);
 1508: 	data->fd = my_accept(ftp, data->listener, (struct sockaddr*) &addr, &size);
 1509: 	closesocket(data->listener);
 1510: 	data->listener = -1;
 1511: 
 1512: 	if (data->fd == -1) {
 1513: 		efree(data);
 1514: 		return NULL;
 1515: 	}
 1516: 
 1517: data_accepted:
 1518: #if HAVE_OPENSSL_EXT
 1519: 	
 1520: 	/* now enable ssl if we need to */
 1521: 	if (ftp->use_ssl && ftp->use_ssl_for_data) {
 1522: 		ctx = SSL_CTX_new(SSLv23_client_method());
 1523: 		if (ctx == NULL) {
 1524: 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_accept: failed to create the SSL context");
 1525: 			return 0;
 1526: 		}
 1527: 
 1528: #if OPENSSL_VERSION_NUMBER >= 0x0090605fL
 1529: 		ssl_ctx_options &= ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
 1530: #endif
 1531: 		SSL_CTX_set_options(ctx, ssl_ctx_options);
 1532: 
 1533: 		data->ssl_handle = SSL_new(ctx);
 1534: 		if (data->ssl_handle == NULL) {
 1535: 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_accept: failed to create the SSL handle");
 1536: 			SSL_CTX_free(ctx);
 1537: 			return 0;
 1538: 		}
 1539: 			
 1540: 		
 1541: 		SSL_set_fd(data->ssl_handle, data->fd);
 1542: 
 1543: 		if (ftp->old_ssl) {
 1544: 			SSL_copy_session_id(data->ssl_handle, ftp->ssl_handle);
 1545: 		}
 1546: 			
 1547: 		if (SSL_connect(data->ssl_handle) <= 0) {
 1548: 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_accept: SSL/TLS handshake failed");
 1549: 			SSL_shutdown(data->ssl_handle);
 1550: 			SSL_free(data->ssl_handle);
 1551: 			return 0;
 1552: 		}
 1553: 			
 1554: 		data->ssl_active = 1;
 1555: 	}	
 1556: 
 1557: #endif
 1558: 
 1559: 	return data;
 1560: }
 1561: /* }}} */
 1562: 
 1563: /* {{{ data_close
 1564:  */
 1565: databuf_t*
 1566: data_close(ftpbuf_t *ftp, databuf_t *data)
 1567: {
 1568: #if HAVE_OPENSSL_EXT
 1569: 	SSL_CTX		*ctx;
 1570: #endif				
 1571: 	if (data == NULL) {
 1572: 		return NULL;
 1573: 	}
 1574: 	if (data->listener != -1) {
 1575: #if HAVE_OPENSSL_EXT
 1576: 		if (data->ssl_active) {
 1577: 		
 1578: 			ctx = SSL_get_SSL_CTX(data->ssl_handle);
 1579: 			SSL_CTX_free(ctx);
 1580: 
 1581: 			SSL_shutdown(data->ssl_handle);
 1582: 			SSL_free(data->ssl_handle);
 1583: 			data->ssl_active = 0;
 1584: 		}
 1585: #endif				
 1586: 		closesocket(data->listener);
 1587: 	}	
 1588: 	if (data->fd != -1) {
 1589: #if HAVE_OPENSSL_EXT
 1590: 		if (data->ssl_active) {
 1591: 			ctx = SSL_get_SSL_CTX(data->ssl_handle);
 1592: 			SSL_CTX_free(ctx);
 1593: 
 1594: 			SSL_shutdown(data->ssl_handle);
 1595: 			SSL_free(data->ssl_handle);
 1596: 			data->ssl_active = 0;
 1597: 		}
 1598: #endif				
 1599: 		closesocket(data->fd);
 1600: 	}	
 1601: 	if (ftp) {
 1602: 		ftp->data = NULL;
 1603: 	}
 1604: 	efree(data);
 1605: 	return NULL;
 1606: }
 1607: /* }}} */
 1608: 
 1609: /* {{{ ftp_genlist
 1610:  */
 1611: char**
 1612: ftp_genlist(ftpbuf_t *ftp, const char *cmd, const char *path TSRMLS_DC)
 1613: {
 1614: 	php_stream	*tmpstream = NULL;
 1615: 	databuf_t	*data = NULL;
 1616: 	char		*ptr;
 1617: 	int		ch, lastch;
 1618: 	int		size, rcvd;
 1619: 	int		lines;
 1620: 	char		**ret = NULL;
 1621: 	char		**entry;
 1622: 	char		*text;
 1623: 
 1624: 
 1625: 	if ((tmpstream = php_stream_fopen_tmpfile()) == NULL) {
 1626: 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to create temporary file.  Check permissions in temporary files directory.");
 1627: 		return NULL;
 1628: 	}
 1629: 
 1630: 	if (!ftp_type(ftp, FTPTYPE_ASCII)) {
 1631: 		goto bail;
 1632: 	}
 1633: 
 1634: 	if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) {
 1635: 		goto bail;
 1636: 	}
 1637: 	ftp->data = data;	
 1638: 
 1639: 	if (!ftp_putcmd(ftp, cmd, path)) {
 1640: 		goto bail;
 1641: 	}
 1642: 	if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125 && ftp->resp != 226)) {
 1643: 		goto bail;
 1644: 	}
 1645: 
 1646: 	/* some servers don't open a ftp-data connection if the directory is empty */
 1647: 	if (ftp->resp == 226) {
 1648: 		ftp->data = data_close(ftp, data);
 1649: 		php_stream_close(tmpstream);
 1650: 		return ecalloc(1, sizeof(char*));
 1651: 	}
 1652: 
 1653: 	/* pull data buffer into tmpfile */
 1654: 	if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) {
 1655: 		goto bail;
 1656: 	}
 1657: 	size = 0;
 1658: 	lines = 0;
 1659: 	lastch = 0;
 1660: 	while ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) {
 1661: 		if (rcvd == -1) {
 1662: 			goto bail;
 1663: 		}
 1664: 
 1665: 		php_stream_write(tmpstream, data->buf, rcvd);
 1666: 
 1667: 		size += rcvd;
 1668: 		for (ptr = data->buf; rcvd; rcvd--, ptr++) {
 1669: 			if (*ptr == '\n' && lastch == '\r') {
 1670: 				lines++;
 1671: 			} else {
 1672: 				size++;
 1673: 			}
 1674: 			lastch = *ptr;
 1675: 		}
 1676: 	}
 1677: 
 1678: 	ftp->data = data_close(ftp, data);
 1679: 
 1680: 	php_stream_rewind(tmpstream);
 1681: 
 1682: 	ret = safe_emalloc((lines + 1), sizeof(char*), size);
 1683: 
 1684: 	entry = ret;
 1685: 	text = (char*) (ret + lines + 1);
 1686: 	*entry = text;
 1687: 	lastch = 0;
 1688: 	while ((ch = php_stream_getc(tmpstream)) != EOF) {
 1689: 		if (ch == '\n' && lastch == '\r') {
 1690: 			*(text - 1) = 0;
 1691: 			*++entry = text;
 1692: 		} else {
 1693: 			*text++ = ch;
 1694: 		}
 1695: 		lastch = ch;
 1696: 	}
 1697: 	*entry = NULL;
 1698: 
 1699: 	php_stream_close(tmpstream);
 1700: 
 1701: 	if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
 1702: 		efree(ret);
 1703: 		return NULL;
 1704: 	}
 1705: 
 1706: 	return ret;
 1707: bail:
 1708: 	ftp->data = data_close(ftp, data);
 1709: 	php_stream_close(tmpstream);
 1710: 	if (ret)
 1711: 		efree(ret);
 1712: 	return NULL;
 1713: }
 1714: /* }}} */
 1715: 
 1716: /* {{{ ftp_nb_get
 1717:  */
 1718: int
 1719: ftp_nb_get(ftpbuf_t *ftp, php_stream *outstream, const char *path, ftptype_t type, long resumepos TSRMLS_DC)
 1720: {
 1721: 	databuf_t		*data = NULL;
 1722: 	char			arg[11];
 1723: 
 1724: 	if (ftp == NULL) {
 1725: 		return PHP_FTP_FAILED;
 1726: 	}
 1727: 
 1728: 	if (!ftp_type(ftp, type)) {
 1729: 		goto bail;
 1730: 	}
 1731: 
 1732: 	if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) {
 1733: 		goto bail;
 1734: 	}
 1735: 
 1736: 	if (resumepos>0) {
 1737: 		snprintf(arg, sizeof(arg), "%ld", resumepos);
 1738: 		if (!ftp_putcmd(ftp, "REST", arg)) {
 1739: 			goto bail;
 1740: 		}
 1741: 		if (!ftp_getresp(ftp) || (ftp->resp != 350)) {
 1742: 			goto bail;
 1743: 		}
 1744: 	}
 1745: 
 1746: 	if (!ftp_putcmd(ftp, "RETR", path)) {
 1747: 		goto bail;
 1748: 	}
 1749: 	if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
 1750: 		goto bail;
 1751: 	}
 1752: 
 1753: 	if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) {
 1754: 		goto bail;
 1755: 	}
 1756: 
 1757: 	ftp->data = data;
 1758: 	ftp->stream = outstream;
 1759: 	ftp->lastch = 0;
 1760: 	ftp->nb = 1;
 1761: 
 1762: 	return (ftp_nb_continue_read(ftp TSRMLS_CC));
 1763: 
 1764: bail:
 1765: 	ftp->data = data_close(ftp, data);
 1766: 	return PHP_FTP_FAILED;
 1767: }
 1768: /* }}} */
 1769: 
 1770: /* {{{ ftp_nb_continue_read
 1771:  */
 1772: int
 1773: ftp_nb_continue_read(ftpbuf_t *ftp TSRMLS_DC)
 1774: {
 1775: 	databuf_t	*data = NULL;
 1776: 	char		*ptr;
 1777: 	int		lastch;
 1778: 	size_t		rcvd;
 1779: 	ftptype_t	type;
 1780: 
 1781: 	data = ftp->data;
 1782: 
 1783: 	/* check if there is already more data */
 1784: 	if (!data_available(ftp, data->fd)) {
 1785: 		return PHP_FTP_MOREDATA;
 1786: 	}
 1787: 
 1788: 	type = ftp->type;
 1789: 
 1790: 	lastch = ftp->lastch;
 1791: 	if ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) {
 1792: 		if (rcvd == -1) {
 1793: 			goto bail;
 1794: 		}
 1795: 
 1796: 		if (type == FTPTYPE_ASCII) {
 1797: 			for (ptr = data->buf; rcvd; rcvd--, ptr++) {
 1798: 				if (lastch == '\r' && *ptr != '\n') {
 1799: 					php_stream_putc(ftp->stream, '\r');
 1800: 				}
 1801: 				if (*ptr != '\r') {
 1802: 					php_stream_putc(ftp->stream, *ptr);
 1803: 				}
 1804: 				lastch = *ptr;
 1805: 			}
 1806: 		} else if (rcvd != php_stream_write(ftp->stream, data->buf, rcvd)) {
 1807: 			goto bail;
 1808: 		}
 1809: 
 1810: 		ftp->lastch = lastch;
 1811: 		return PHP_FTP_MOREDATA;
 1812: 	}
 1813: 
 1814: 	if (type == FTPTYPE_ASCII && lastch == '\r') {
 1815: 		php_stream_putc(ftp->stream, '\r');
 1816: 	}
 1817: 
 1818: 	ftp->data = data = data_close(ftp, data);
 1819: 
 1820: 	if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
 1821: 		goto bail;
 1822: 	}
 1823: 
 1824: 	ftp->nb = 0;
 1825: 	return PHP_FTP_FINISHED;
 1826: bail:
 1827: 	ftp->nb = 0;
 1828: 	ftp->data = data_close(ftp, data);
 1829: 	return PHP_FTP_FAILED;
 1830: }
 1831: /* }}} */
 1832: 
 1833: /* {{{ ftp_nb_put
 1834:  */
 1835: int
 1836: ftp_nb_put(ftpbuf_t *ftp, const char *path, php_stream *instream, ftptype_t type, long startpos TSRMLS_DC)
 1837: {
 1838: 	databuf_t		*data = NULL;
 1839: 	char			arg[11];
 1840: 
 1841: 	if (ftp == NULL) {
 1842: 		return 0;
 1843: 	}
 1844: 	if (!ftp_type(ftp, type)) {
 1845: 		goto bail;
 1846: 	}
 1847: 	if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) {
 1848: 		goto bail;
 1849: 	}
 1850: 	if (startpos > 0) {
 1851: 		snprintf(arg, sizeof(arg), "%ld", startpos);
 1852: 		if (!ftp_putcmd(ftp, "REST", arg)) {
 1853: 			goto bail;
 1854: 		}
 1855: 		if (!ftp_getresp(ftp) || (ftp->resp != 350)) {
 1856: 			goto bail;
 1857: 		}
 1858: 	}
 1859: 
 1860: 	if (!ftp_putcmd(ftp, "STOR", path)) {
 1861: 		goto bail;
 1862: 	}
 1863: 	if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
 1864: 		goto bail;
 1865: 	}
 1866: 	if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) { 
 1867: 		goto bail;
 1868: 	}
 1869: 	ftp->data = data;
 1870: 	ftp->stream = instream;
 1871: 	ftp->lastch = 0;
 1872: 	ftp->nb = 1;
 1873: 
 1874: 	return (ftp_nb_continue_write(ftp TSRMLS_CC));
 1875: 
 1876: bail:
 1877: 	ftp->data = data_close(ftp, data);
 1878: 	return PHP_FTP_FAILED;
 1879: }
 1880: /* }}} */
 1881: 
 1882: 
 1883: /* {{{ ftp_nb_continue_write
 1884:  */
 1885: int
 1886: ftp_nb_continue_write(ftpbuf_t *ftp TSRMLS_DC)
 1887: {
 1888: 	long			size;
 1889: 	char			*ptr;
 1890: 	int 			ch;
 1891: 
 1892: 	/* check if we can write more data */
 1893: 	if (!data_writeable(ftp, ftp->data->fd)) {
 1894: 		return PHP_FTP_MOREDATA;
 1895: 	}
 1896: 
 1897: 	size = 0;
 1898: 	ptr = ftp->data->buf;
 1899: 	while (!php_stream_eof(ftp->stream) && (ch = php_stream_getc(ftp->stream)) != EOF) {
 1900: 
 1901: 		if (ch == '\n' && ftp->type == FTPTYPE_ASCII) {
 1902: 			*ptr++ = '\r';
 1903: 			size++;
 1904: 		}
 1905: 
 1906: 		*ptr++ = ch;
 1907: 		size++;
 1908: 
 1909: 		/* flush if necessary */
 1910: 		if (FTP_BUFSIZE - size < 2) {
 1911: 			if (my_send(ftp, ftp->data->fd, ftp->data->buf, size) != size) {
 1912: 				goto bail;
 1913: 			}
 1914: 			return PHP_FTP_MOREDATA;
 1915: 		}
 1916: 	}
 1917: 
 1918: 	if (size && my_send(ftp, ftp->data->fd, ftp->data->buf, size) != size) {
 1919: 		goto bail;
 1920: 	}
 1921: 	ftp->data = data_close(ftp, ftp->data);
 1922:  
 1923: 	if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
 1924: 		goto bail;
 1925: 	}
 1926: 	ftp->nb = 0;
 1927: 	return PHP_FTP_FINISHED;
 1928: bail:
 1929: 	ftp->data = data_close(ftp, ftp->data);
 1930: 	ftp->nb = 0;
 1931: 	return PHP_FTP_FAILED;
 1932: }
 1933: /* }}} */
 1934: 
 1935: #endif /* HAVE_FTP */
 1936: 
 1937: /*
 1938:  * Local variables:
 1939:  * tab-width: 4
 1940:  * c-basic-offset: 4
 1941:  * End:
 1942:  * vim600: sw=4 ts=4 fdm=marker
 1943:  * vim<600: sw=4 ts=4
 1944:  */

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>