xref: /PHP-5.6/ext/ftp/ftp.c (revision dfadc5a4)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 5                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2016 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$ */
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*
ftp_open(const char * host,short port,long timeout_sec TSRMLS_DC)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*
ftp_close(ftpbuf_t * ftp)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
ftp_gc(ftpbuf_t * ftp)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
ftp_quit(ftpbuf_t * ftp)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
ftp_login(ftpbuf_t * ftp,const char * user,const char * pass TSRMLS_DC)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 		/* allow SSL to re-use sessions */
294 		SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_BOTH);
295 
296 		ftp->ssl_handle = SSL_new(ctx);
297 		if (ftp->ssl_handle == NULL) {
298 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create the SSL handle");
299 			SSL_CTX_free(ctx);
300 			return 0;
301 		}
302 
303 		SSL_set_fd(ftp->ssl_handle, ftp->fd);
304 
305 		if (SSL_connect(ftp->ssl_handle) <= 0) {
306 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL/TLS handshake failed");
307 			SSL_shutdown(ftp->ssl_handle);
308 			SSL_free(ftp->ssl_handle);
309 			return 0;
310 		}
311 
312 		ftp->ssl_active = 1;
313 
314 		if (!ftp->old_ssl) {
315 
316 			/* set protection buffersize to zero */
317 			if (!ftp_putcmd(ftp, "PBSZ", "0")) {
318 				return 0;
319 			}
320 			if (!ftp_getresp(ftp)) {
321 				return 0;
322 			}
323 
324 			/* enable data conn encryption */
325 			if (!ftp_putcmd(ftp, "PROT", "P")) {
326 				return 0;
327 			}
328 			if (!ftp_getresp(ftp)) {
329 				return 0;
330 			}
331 
332 			ftp->use_ssl_for_data = (ftp->resp >= 200 && ftp->resp <=299);
333 		}
334 	}
335 #endif
336 
337 	if (!ftp_putcmd(ftp, "USER", user)) {
338 		return 0;
339 	}
340 	if (!ftp_getresp(ftp)) {
341 		return 0;
342 	}
343 	if (ftp->resp == 230) {
344 		return 1;
345 	}
346 	if (ftp->resp != 331) {
347 		return 0;
348 	}
349 	if (!ftp_putcmd(ftp, "PASS", pass)) {
350 		return 0;
351 	}
352 	if (!ftp_getresp(ftp)) {
353 		return 0;
354 	}
355 	return (ftp->resp == 230);
356 }
357 /* }}} */
358 
359 /* {{{ ftp_reinit
360  */
361 int
ftp_reinit(ftpbuf_t * ftp)362 ftp_reinit(ftpbuf_t *ftp)
363 {
364 	if (ftp == NULL) {
365 		return 0;
366 	}
367 
368 	ftp_gc(ftp);
369 
370 	ftp->nb = 0;
371 
372 	if (!ftp_putcmd(ftp, "REIN", NULL)) {
373 		return 0;
374 	}
375 	if (!ftp_getresp(ftp) || ftp->resp != 220) {
376 		return 0;
377 	}
378 
379 	return 1;
380 }
381 /* }}} */
382 
383 /* {{{ ftp_syst
384  */
385 const char*
ftp_syst(ftpbuf_t * ftp)386 ftp_syst(ftpbuf_t *ftp)
387 {
388 	char *syst, *end;
389 
390 	if (ftp == NULL) {
391 		return NULL;
392 	}
393 
394 	/* default to cached value */
395 	if (ftp->syst) {
396 		return ftp->syst;
397 	}
398 	if (!ftp_putcmd(ftp, "SYST", NULL)) {
399 		return NULL;
400 	}
401 	if (!ftp_getresp(ftp) || ftp->resp != 215) {
402 		return NULL;
403 	}
404 	syst = ftp->inbuf;
405 	while (*syst == ' ') {
406 		syst++;
407 	}
408 	if ((end = strchr(syst, ' '))) {
409 		*end = 0;
410 	}
411 	ftp->syst = estrdup(syst);
412 	if (end) {
413 		*end = ' ';
414 	}
415 	return ftp->syst;
416 }
417 /* }}} */
418 
419 /* {{{ ftp_pwd
420  */
421 const char*
ftp_pwd(ftpbuf_t * ftp)422 ftp_pwd(ftpbuf_t *ftp)
423 {
424 	char *pwd, *end;
425 
426 	if (ftp == NULL) {
427 		return NULL;
428 	}
429 
430 	/* default to cached value */
431 	if (ftp->pwd) {
432 		return ftp->pwd;
433 	}
434 	if (!ftp_putcmd(ftp, "PWD", NULL)) {
435 		return NULL;
436 	}
437 	if (!ftp_getresp(ftp) || ftp->resp != 257) {
438 		return NULL;
439 	}
440 	/* copy out the pwd from response */
441 	if ((pwd = strchr(ftp->inbuf, '"')) == NULL) {
442 		return NULL;
443 	}
444 	if ((end = strrchr(++pwd, '"')) == NULL) {
445 		return NULL;
446 	}
447 	ftp->pwd = estrndup(pwd, end - pwd);
448 
449 	return ftp->pwd;
450 }
451 /* }}} */
452 
453 /* {{{ ftp_exec
454  */
455 int
ftp_exec(ftpbuf_t * ftp,const char * cmd)456 ftp_exec(ftpbuf_t *ftp, const char *cmd)
457 {
458 	if (ftp == NULL) {
459 		return 0;
460 	}
461 	if (!ftp_putcmd(ftp, "SITE EXEC", cmd)) {
462 		return 0;
463 	}
464 	if (!ftp_getresp(ftp) || ftp->resp != 200) {
465 		return 0;
466 	}
467 
468 	return 1;
469 }
470 /* }}} */
471 
472 /* {{{ ftp_raw
473  */
474 void
ftp_raw(ftpbuf_t * ftp,const char * cmd,zval * return_value)475 ftp_raw(ftpbuf_t *ftp, const char *cmd, zval *return_value)
476 {
477 	if (ftp == NULL || cmd == NULL) {
478 		RETURN_NULL();
479 	}
480 	if (!ftp_putcmd(ftp, cmd, NULL)) {
481 		RETURN_NULL();
482 	}
483 	array_init(return_value);
484 	while (ftp_readline(ftp)) {
485 		add_next_index_string(return_value, ftp->inbuf, 1);
486 		if (isdigit(ftp->inbuf[0]) && isdigit(ftp->inbuf[1]) && isdigit(ftp->inbuf[2]) && ftp->inbuf[3] == ' ') {
487 			return;
488 		}
489 	}
490 }
491 /* }}} */
492 
493 /* {{{ ftp_chdir
494  */
495 int
ftp_chdir(ftpbuf_t * ftp,const char * dir)496 ftp_chdir(ftpbuf_t *ftp, const char *dir)
497 {
498 	if (ftp == NULL) {
499 		return 0;
500 	}
501 
502 	if (ftp->pwd) {
503 		efree(ftp->pwd);
504 		ftp->pwd = NULL;
505 	}
506 
507 	if (!ftp_putcmd(ftp, "CWD", dir)) {
508 		return 0;
509 	}
510 	if (!ftp_getresp(ftp) || ftp->resp != 250) {
511 		return 0;
512 	}
513 	return 1;
514 }
515 /* }}} */
516 
517 /* {{{ ftp_cdup
518  */
519 int
ftp_cdup(ftpbuf_t * ftp)520 ftp_cdup(ftpbuf_t *ftp)
521 {
522 	if (ftp == NULL) {
523 		return 0;
524 	}
525 
526 	if (ftp->pwd) {
527 		efree(ftp->pwd);
528 		ftp->pwd = NULL;
529 	}
530 
531 	if (!ftp_putcmd(ftp, "CDUP", NULL)) {
532 		return 0;
533 	}
534 	if (!ftp_getresp(ftp) || ftp->resp != 250) {
535 		return 0;
536 	}
537 	return 1;
538 }
539 /* }}} */
540 
541 /* {{{ ftp_mkdir
542  */
543 char*
ftp_mkdir(ftpbuf_t * ftp,const char * dir)544 ftp_mkdir(ftpbuf_t *ftp, const char *dir)
545 {
546 	char *mkd, *end;
547 
548 	if (ftp == NULL) {
549 		return NULL;
550 	}
551 	if (!ftp_putcmd(ftp, "MKD", dir)) {
552 		return NULL;
553 	}
554 	if (!ftp_getresp(ftp) || ftp->resp != 257) {
555 		return NULL;
556 	}
557 	/* copy out the dir from response */
558 	if ((mkd = strchr(ftp->inbuf, '"')) == NULL) {
559 		mkd = estrdup(dir);
560 		return mkd;
561 	}
562 	if ((end = strrchr(++mkd, '"')) == NULL) {
563 		return NULL;
564 	}
565 	*end = 0;
566 	mkd = estrdup(mkd);
567 	*end = '"';
568 
569 	return mkd;
570 }
571 /* }}} */
572 
573 /* {{{ ftp_rmdir
574  */
575 int
ftp_rmdir(ftpbuf_t * ftp,const char * dir)576 ftp_rmdir(ftpbuf_t *ftp, const char *dir)
577 {
578 	if (ftp == NULL) {
579 		return 0;
580 	}
581 	if (!ftp_putcmd(ftp, "RMD", dir)) {
582 		return 0;
583 	}
584 	if (!ftp_getresp(ftp) || ftp->resp != 250) {
585 		return 0;
586 	}
587 	return 1;
588 }
589 /* }}} */
590 
591 /* {{{ ftp_chmod
592  */
593 int
ftp_chmod(ftpbuf_t * ftp,const int mode,const char * filename,const int filename_len)594 ftp_chmod(ftpbuf_t *ftp, const int mode, const char *filename, const int filename_len)
595 {
596 	char *buffer;
597 
598 	if (ftp == NULL || filename_len <= 0) {
599 		return 0;
600 	}
601 
602 	spprintf(&buffer, 0, "CHMOD %o %s", mode, filename);
603 
604 	if (!ftp_putcmd(ftp, "SITE", buffer)) {
605 		efree(buffer);
606 		return 0;
607 	}
608 
609 	efree(buffer);
610 
611 	if (!ftp_getresp(ftp) || ftp->resp != 200) {
612 		return 0;
613 	}
614 
615 	return 1;
616 }
617 /* }}} */
618 
619 /* {{{ ftp_alloc
620  */
621 int
ftp_alloc(ftpbuf_t * ftp,const long size,char ** response)622 ftp_alloc(ftpbuf_t *ftp, const long size, char **response)
623 {
624 	char buffer[64];
625 
626 	if (ftp == NULL || size <= 0) {
627 		return 0;
628 	}
629 
630 	snprintf(buffer, sizeof(buffer) - 1, "%ld", size);
631 
632 	if (!ftp_putcmd(ftp, "ALLO", buffer)) {
633 		return 0;
634 	}
635 
636 	if (!ftp_getresp(ftp)) {
637 		return 0;
638 	}
639 
640 	if (response) {
641 		*response = estrdup(ftp->inbuf);
642 	}
643 
644 	if (ftp->resp < 200 || ftp->resp >= 300) {
645 		return 0;
646 	}
647 
648 	return 1;
649 }
650 /* }}} */
651 
652 /* {{{ ftp_nlist
653  */
654 char**
ftp_nlist(ftpbuf_t * ftp,const char * path TSRMLS_DC)655 ftp_nlist(ftpbuf_t *ftp, const char *path TSRMLS_DC)
656 {
657 	return ftp_genlist(ftp, "NLST", path TSRMLS_CC);
658 }
659 /* }}} */
660 
661 /* {{{ ftp_list
662  */
663 char**
ftp_list(ftpbuf_t * ftp,const char * path,int recursive TSRMLS_DC)664 ftp_list(ftpbuf_t *ftp, const char *path, int recursive TSRMLS_DC)
665 {
666 	return ftp_genlist(ftp, ((recursive) ? "LIST -R" : "LIST"), path TSRMLS_CC);
667 }
668 /* }}} */
669 
670 /* {{{ ftp_type
671  */
672 int
ftp_type(ftpbuf_t * ftp,ftptype_t type)673 ftp_type(ftpbuf_t *ftp, ftptype_t type)
674 {
675 	char typechar[2] = "?";
676 
677 	if (ftp == NULL) {
678 		return 0;
679 	}
680 	if (type == ftp->type) {
681 		return 1;
682 	}
683 	if (type == FTPTYPE_ASCII) {
684 		typechar[0] = 'A';
685 	} else if (type == FTPTYPE_IMAGE) {
686 		typechar[0] = 'I';
687 	} else {
688 		return 0;
689 	}
690 	if (!ftp_putcmd(ftp, "TYPE", typechar)) {
691 		return 0;
692 	}
693 	if (!ftp_getresp(ftp) || ftp->resp != 200) {
694 		return 0;
695 	}
696 	ftp->type = type;
697 
698 	return 1;
699 }
700 /* }}} */
701 
702 /* {{{ ftp_pasv
703  */
704 int
ftp_pasv(ftpbuf_t * ftp,int pasv)705 ftp_pasv(ftpbuf_t *ftp, int pasv)
706 {
707 	char			*ptr;
708 	union ipbox		ipbox;
709 	unsigned long		b[6];
710 	socklen_t			n;
711 	struct sockaddr *sa;
712 	struct sockaddr_in *sin;
713 
714 	if (ftp == NULL) {
715 		return 0;
716 	}
717 	if (pasv && ftp->pasv == 2) {
718 		return 1;
719 	}
720 	ftp->pasv = 0;
721 	if (!pasv) {
722 		return 1;
723 	}
724 	n = sizeof(ftp->pasvaddr);
725 	memset(&ftp->pasvaddr, 0, n);
726 	sa = (struct sockaddr *) &ftp->pasvaddr;
727 
728 	if (getpeername(ftp->fd, sa, &n) < 0) {
729 		return 0;
730 	}
731 
732 #if HAVE_IPV6
733 	if (sa->sa_family == AF_INET6) {
734 		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sa;
735 		char *endptr, delimiter;
736 
737 		/* try EPSV first */
738 		if (!ftp_putcmd(ftp, "EPSV", NULL)) {
739 			return 0;
740 		}
741 		if (!ftp_getresp(ftp)) {
742 			return 0;
743 		}
744 		if (ftp->resp == 229) {
745 			/* parse out the port */
746 			for (ptr = ftp->inbuf; *ptr && *ptr != '('; ptr++);
747 			if (!*ptr) {
748 				return 0;
749 			}
750 			delimiter = *++ptr;
751 			for (n = 0; *ptr && n < 3; ptr++) {
752 				if (*ptr == delimiter) {
753 					n++;
754 				}
755 			}
756 
757 			sin6->sin6_port = htons((unsigned short) strtoul(ptr, &endptr, 10));
758 			if (ptr == endptr || *endptr != delimiter) {
759 				return 0;
760 			}
761 			ftp->pasv = 2;
762 			return 1;
763 		}
764 	}
765 
766 	/* fall back to PASV */
767 #endif
768 
769 	if (!ftp_putcmd(ftp, "PASV", NULL)) {
770 		return 0;
771 	}
772 	if (!ftp_getresp(ftp) || ftp->resp != 227) {
773 		return 0;
774 	}
775 	/* parse out the IP and port */
776 	for (ptr = ftp->inbuf; *ptr && !isdigit(*ptr); ptr++);
777 	n = sscanf(ptr, "%lu,%lu,%lu,%lu,%lu,%lu", &b[0], &b[1], &b[2], &b[3], &b[4], &b[5]);
778 	if (n != 6) {
779 		return 0;
780 	}
781 	for (n = 0; n < 6; n++) {
782 		ipbox.c[n] = (unsigned char) b[n];
783 	}
784 	sin = (struct sockaddr_in *) sa;
785 	if (ftp->usepasvaddress) {
786 		sin->sin_addr = ipbox.ia[0];
787 	}
788 	sin->sin_port = ipbox.s[2];
789 
790 	ftp->pasv = 2;
791 
792 	return 1;
793 }
794 /* }}} */
795 
796 /* {{{ ftp_get
797  */
798 int
ftp_get(ftpbuf_t * ftp,php_stream * outstream,const char * path,ftptype_t type,long resumepos TSRMLS_DC)799 ftp_get(ftpbuf_t *ftp, php_stream *outstream, const char *path, ftptype_t type, long resumepos TSRMLS_DC)
800 {
801 	databuf_t		*data = NULL;
802 	size_t			rcvd;
803 	char			arg[11];
804 
805 	if (ftp == NULL) {
806 		return 0;
807 	}
808 	if (!ftp_type(ftp, type)) {
809 		goto bail;
810 	}
811 
812 	if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) {
813 		goto bail;
814 	}
815 
816 	ftp->data = data;
817 
818 	if (resumepos > 0) {
819 		snprintf(arg, sizeof(arg), "%ld", resumepos);
820 		if (!ftp_putcmd(ftp, "REST", arg)) {
821 			goto bail;
822 		}
823 		if (!ftp_getresp(ftp) || (ftp->resp != 350)) {
824 			goto bail;
825 		}
826 	}
827 
828 	if (!ftp_putcmd(ftp, "RETR", path)) {
829 		goto bail;
830 	}
831 	if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
832 		goto bail;
833 	}
834 
835 	if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) {
836 		goto bail;
837 	}
838 
839 	while ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) {
840 		if (rcvd == -1) {
841 			goto bail;
842 		}
843 
844 		if (type == FTPTYPE_ASCII) {
845 #ifndef PHP_WIN32
846 			char *s;
847 #endif
848 			char *ptr = data->buf;
849 			char *e = ptr + rcvd;
850 			/* logic depends on the OS EOL
851 			 * Win32 -> \r\n
852 			 * Everything Else \n
853 			 */
854 #ifdef PHP_WIN32
855 			php_stream_write(outstream, ptr, (e - ptr));
856 			ptr = e;
857 #else
858 			while (e > ptr && (s = memchr(ptr, '\r', (e - ptr)))) {
859 				php_stream_write(outstream, ptr, (s - ptr));
860 				if (*(s + 1) == '\n') {
861 					s++;
862 					php_stream_putc(outstream, '\n');
863 				}
864 				ptr = s + 1;
865 			}
866 #endif
867 			if (ptr < e) {
868 				php_stream_write(outstream, ptr, (e - ptr));
869 			}
870 		} else if (rcvd != php_stream_write(outstream, data->buf, rcvd)) {
871 			goto bail;
872 		}
873 	}
874 
875 	ftp->data = data = data_close(ftp, data);
876 
877 	if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
878 		goto bail;
879 	}
880 
881 	return 1;
882 bail:
883 	ftp->data = data_close(ftp, data);
884 	return 0;
885 }
886 /* }}} */
887 
888 /* {{{ ftp_put
889  */
890 int
ftp_put(ftpbuf_t * ftp,const char * path,php_stream * instream,ftptype_t type,long startpos TSRMLS_DC)891 ftp_put(ftpbuf_t *ftp, const char *path, php_stream *instream, ftptype_t type, long startpos TSRMLS_DC)
892 {
893 	databuf_t		*data = NULL;
894 	long			size;
895 	char			*ptr;
896 	int			ch;
897 	char			arg[11];
898 
899 	if (ftp == NULL) {
900 		return 0;
901 	}
902 	if (!ftp_type(ftp, type)) {
903 		goto bail;
904 	}
905 	if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) {
906 		goto bail;
907 	}
908 	ftp->data = data;
909 
910 	if (startpos > 0) {
911 		snprintf(arg, sizeof(arg), "%ld", startpos);
912 		if (!ftp_putcmd(ftp, "REST", arg)) {
913 			goto bail;
914 		}
915 		if (!ftp_getresp(ftp) || (ftp->resp != 350)) {
916 			goto bail;
917 		}
918 	}
919 
920 	if (!ftp_putcmd(ftp, "STOR", path)) {
921 		goto bail;
922 	}
923 	if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
924 		goto bail;
925 	}
926 	if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) {
927 		goto bail;
928 	}
929 
930 	size = 0;
931 	ptr = data->buf;
932 	while (!php_stream_eof(instream) && (ch = php_stream_getc(instream))!=EOF) {
933 		/* flush if necessary */
934 		if (FTP_BUFSIZE - size < 2) {
935 			if (my_send(ftp, data->fd, data->buf, size) != size) {
936 				goto bail;
937 			}
938 			ptr = data->buf;
939 			size = 0;
940 		}
941 
942 		if (ch == '\n' && type == FTPTYPE_ASCII) {
943 			*ptr++ = '\r';
944 			size++;
945 		}
946 
947 		*ptr++ = ch;
948 		size++;
949 	}
950 
951 	if (size && my_send(ftp, data->fd, data->buf, size) != size) {
952 		goto bail;
953 	}
954 	ftp->data = data = data_close(ftp, data);
955 
956 	if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250 && ftp->resp != 200)) {
957 		goto bail;
958 	}
959 	return 1;
960 bail:
961 	ftp->data = data_close(ftp, data);
962 	return 0;
963 }
964 /* }}} */
965 
966 /* {{{ ftp_size
967  */
968 long
ftp_size(ftpbuf_t * ftp,const char * path)969 ftp_size(ftpbuf_t *ftp, const char *path)
970 {
971 	if (ftp == NULL) {
972 		return -1;
973 	}
974 	if (!ftp_type(ftp, FTPTYPE_IMAGE)) {
975 		return -1;
976 	}
977 	if (!ftp_putcmd(ftp, "SIZE", path)) {
978 		return -1;
979 	}
980 	if (!ftp_getresp(ftp) || ftp->resp != 213) {
981 		return -1;
982 	}
983 	return atol(ftp->inbuf);
984 }
985 /* }}} */
986 
987 /* {{{ ftp_mdtm
988  */
989 time_t
ftp_mdtm(ftpbuf_t * ftp,const char * path)990 ftp_mdtm(ftpbuf_t *ftp, const char *path)
991 {
992 	time_t		stamp;
993 	struct tm	*gmt, tmbuf;
994 	struct tm	tm;
995 	char		*ptr;
996 	int		n;
997 
998 	if (ftp == NULL) {
999 		return -1;
1000 	}
1001 	if (!ftp_putcmd(ftp, "MDTM", path)) {
1002 		return -1;
1003 	}
1004 	if (!ftp_getresp(ftp) || ftp->resp != 213) {
1005 		return -1;
1006 	}
1007 	/* parse out the timestamp */
1008 	for (ptr = ftp->inbuf; *ptr && !isdigit(*ptr); ptr++);
1009 	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);
1010 	if (n != 6) {
1011 		return -1;
1012 	}
1013 	tm.tm_year -= 1900;
1014 	tm.tm_mon--;
1015 	tm.tm_isdst = -1;
1016 
1017 	/* figure out the GMT offset */
1018 	stamp = time(NULL);
1019 	gmt = php_gmtime_r(&stamp, &tmbuf);
1020 	if (!gmt) {
1021 		return -1;
1022 	}
1023 	gmt->tm_isdst = -1;
1024 
1025 	/* apply the GMT offset */
1026 	tm.tm_sec += stamp - mktime(gmt);
1027 	tm.tm_isdst = gmt->tm_isdst;
1028 
1029 	stamp = mktime(&tm);
1030 
1031 	return stamp;
1032 }
1033 /* }}} */
1034 
1035 /* {{{ ftp_delete
1036  */
1037 int
ftp_delete(ftpbuf_t * ftp,const char * path)1038 ftp_delete(ftpbuf_t *ftp, const char *path)
1039 {
1040 	if (ftp == NULL) {
1041 		return 0;
1042 	}
1043 	if (!ftp_putcmd(ftp, "DELE", path)) {
1044 		return 0;
1045 	}
1046 	if (!ftp_getresp(ftp) || ftp->resp != 250) {
1047 		return 0;
1048 	}
1049 
1050 	return 1;
1051 }
1052 /* }}} */
1053 
1054 /* {{{ ftp_rename
1055  */
1056 int
ftp_rename(ftpbuf_t * ftp,const char * src,const char * dest)1057 ftp_rename(ftpbuf_t *ftp, const char *src, const char *dest)
1058 {
1059 	if (ftp == NULL) {
1060 		return 0;
1061 	}
1062 	if (!ftp_putcmd(ftp, "RNFR", src)) {
1063 		return 0;
1064 	}
1065 	if (!ftp_getresp(ftp) || ftp->resp != 350) {
1066 		return 0;
1067 	}
1068 	if (!ftp_putcmd(ftp, "RNTO", dest)) {
1069 		return 0;
1070 	}
1071 	if (!ftp_getresp(ftp) || ftp->resp != 250) {
1072 		return 0;
1073 	}
1074 	return 1;
1075 }
1076 /* }}} */
1077 
1078 /* {{{ ftp_site
1079  */
1080 int
ftp_site(ftpbuf_t * ftp,const char * cmd)1081 ftp_site(ftpbuf_t *ftp, const char *cmd)
1082 {
1083 	if (ftp == NULL) {
1084 		return 0;
1085 	}
1086 	if (!ftp_putcmd(ftp, "SITE", cmd)) {
1087 		return 0;
1088 	}
1089 	if (!ftp_getresp(ftp) || ftp->resp < 200 || ftp->resp >= 300) {
1090 		return 0;
1091 	}
1092 
1093 	return 1;
1094 }
1095 /* }}} */
1096 
1097 /* static functions */
1098 
1099 /* {{{ ftp_putcmd
1100  */
1101 int
ftp_putcmd(ftpbuf_t * ftp,const char * cmd,const char * args)1102 ftp_putcmd(ftpbuf_t *ftp, const char *cmd, const char *args)
1103 {
1104 	int		size;
1105 	char		*data;
1106 
1107 	if (strpbrk(cmd, "\r\n")) {
1108 		return 0;
1109 	}
1110 	/* build the output buffer */
1111 	if (args && args[0]) {
1112 		/* "cmd args\r\n\0" */
1113 		if (strlen(cmd) + strlen(args) + 4 > FTP_BUFSIZE) {
1114 			return 0;
1115 		}
1116 		if (strpbrk(args, "\r\n")) {
1117 			return 0;
1118 		}
1119 		size = slprintf(ftp->outbuf, sizeof(ftp->outbuf), "%s %s\r\n", cmd, args);
1120 	} else {
1121 		/* "cmd\r\n\0" */
1122 		if (strlen(cmd) + 3 > FTP_BUFSIZE) {
1123 			return 0;
1124 		}
1125 		size = slprintf(ftp->outbuf, sizeof(ftp->outbuf), "%s\r\n", cmd);
1126 	}
1127 
1128 	data = ftp->outbuf;
1129 
1130 	/* Clear the extra-lines buffer */
1131 	ftp->extra = NULL;
1132 
1133 	if (my_send(ftp, ftp->fd, data, size) != size) {
1134 		return 0;
1135 	}
1136 	return 1;
1137 }
1138 /* }}} */
1139 
1140 /* {{{ ftp_readline
1141  */
1142 int
ftp_readline(ftpbuf_t * ftp)1143 ftp_readline(ftpbuf_t *ftp)
1144 {
1145 	long		size, rcvd;
1146 	char		*data, *eol;
1147 
1148 	/* shift the extra to the front */
1149 	size = FTP_BUFSIZE;
1150 	rcvd = 0;
1151 	if (ftp->extra) {
1152 		memmove(ftp->inbuf, ftp->extra, ftp->extralen);
1153 		rcvd = ftp->extralen;
1154 	}
1155 
1156 	data = ftp->inbuf;
1157 
1158 	do {
1159 		size -= rcvd;
1160 		for (eol = data; rcvd; rcvd--, eol++) {
1161 			if (*eol == '\r') {
1162 				*eol = 0;
1163 				ftp->extra = eol + 1;
1164 				if (rcvd > 1 && *(eol + 1) == '\n') {
1165 					ftp->extra++;
1166 					rcvd--;
1167 				}
1168 				if ((ftp->extralen = --rcvd) == 0) {
1169 					ftp->extra = NULL;
1170 				}
1171 				return 1;
1172 			} else if (*eol == '\n') {
1173 				*eol = 0;
1174 				ftp->extra = eol + 1;
1175 				if ((ftp->extralen = --rcvd) == 0) {
1176 					ftp->extra = NULL;
1177 				}
1178 				return 1;
1179 			}
1180 		}
1181 
1182 		data = eol;
1183 		if ((rcvd = my_recv(ftp, ftp->fd, data, size)) < 1) {
1184 			return 0;
1185 		}
1186 	} while (size);
1187 
1188 	return 0;
1189 }
1190 /* }}} */
1191 
1192 /* {{{ ftp_getresp
1193  */
1194 int
ftp_getresp(ftpbuf_t * ftp)1195 ftp_getresp(ftpbuf_t *ftp)
1196 {
1197 	if (ftp == NULL) {
1198 		return 0;
1199 	}
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
my_send(ftpbuf_t * ftp,php_socket_t s,void * buf,size_t len)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
my_recv(ftpbuf_t * ftp,php_socket_t s,void * buf,size_t len)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
data_available(ftpbuf_t * ftp,php_socket_t s)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
data_writeable(ftpbuf_t * ftp,php_socket_t s)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
my_accept(ftpbuf_t * ftp,php_socket_t s,struct sockaddr * addr,socklen_t * addrlen)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*
ftp_getdata(ftpbuf_t * ftp TSRMLS_DC)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*
data_accept(databuf_t * data,ftpbuf_t * ftp TSRMLS_DC)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 	SSL_SESSION *session;
1502 	int result;
1503 #endif
1504 
1505 	if (data->fd != -1) {
1506 		goto data_accepted;
1507 	}
1508 	size = sizeof(addr);
1509 	data->fd = my_accept(ftp, data->listener, (struct sockaddr*) &addr, &size);
1510 	closesocket(data->listener);
1511 	data->listener = -1;
1512 
1513 	if (data->fd == -1) {
1514 		efree(data);
1515 		return NULL;
1516 	}
1517 
1518 data_accepted:
1519 #if HAVE_OPENSSL_EXT
1520 
1521 	/* now enable ssl if we need to */
1522 	if (ftp->use_ssl && ftp->use_ssl_for_data) {
1523 		ctx = SSL_get_SSL_CTX(ftp->ssl_handle);
1524 		if (ctx == NULL) {
1525 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_accept: failed to retreive the existing SSL context");
1526 			return 0;
1527 		}
1528 
1529 		data->ssl_handle = SSL_new(ctx);
1530 		if (data->ssl_handle == NULL) {
1531 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_accept: failed to create the SSL handle");
1532 			return 0;
1533 		}
1534 
1535 		SSL_set_fd(data->ssl_handle, data->fd);
1536 
1537 		if (ftp->old_ssl) {
1538 			SSL_copy_session_id(data->ssl_handle, ftp->ssl_handle);
1539 		}
1540 
1541 		/* get the session from the control connection so we can re-use it */
1542 		session = SSL_get_session(ftp->ssl_handle);
1543 		if (session == NULL) {
1544 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_accept: failed to retreive the existing SSL session");
1545 			SSL_free(data->ssl_handle);
1546 			return 0;
1547 		}
1548 
1549 		/* and set it on the data connection */
1550 		result = SSL_set_session(data->ssl_handle, session);
1551 		if (result == 0) {
1552 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_accept: failed to set the existing SSL session");
1553 			SSL_free(data->ssl_handle);
1554 			return 0;
1555 		}
1556 
1557 		if (SSL_connect(data->ssl_handle) <= 0) {
1558 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_accept: SSL/TLS handshake failed");
1559 			SSL_shutdown(data->ssl_handle);
1560 			SSL_free(data->ssl_handle);
1561 			return 0;
1562 		}
1563 
1564 		data->ssl_active = 1;
1565 	}
1566 
1567 #endif
1568 
1569 	return data;
1570 }
1571 /* }}} */
1572 
1573 /* {{{ data_close
1574  */
1575 databuf_t*
data_close(ftpbuf_t * ftp,databuf_t * data)1576 data_close(ftpbuf_t *ftp, databuf_t *data)
1577 {
1578 #if HAVE_OPENSSL_EXT
1579 	SSL_CTX		*ctx;
1580 #endif
1581 	if (data == NULL) {
1582 		return NULL;
1583 	}
1584 	if (data->listener != -1) {
1585 #if HAVE_OPENSSL_EXT
1586 		if (data->ssl_active) {
1587 			/* don't free the data context, it's the same as the control */
1588 			SSL_shutdown(data->ssl_handle);
1589 			SSL_free(data->ssl_handle);
1590 			data->ssl_active = 0;
1591 		}
1592 #endif
1593 		closesocket(data->listener);
1594 	}
1595 	if (data->fd != -1) {
1596 #if HAVE_OPENSSL_EXT
1597 		if (data->ssl_active) {
1598 			/* don't free the data context, it's the same as the control */
1599 			SSL_shutdown(data->ssl_handle);
1600 			SSL_free(data->ssl_handle);
1601 			data->ssl_active = 0;
1602 		}
1603 #endif
1604 		closesocket(data->fd);
1605 	}
1606 	if (ftp) {
1607 		ftp->data = NULL;
1608 	}
1609 	efree(data);
1610 	return NULL;
1611 }
1612 /* }}} */
1613 
1614 /* {{{ ftp_genlist
1615  */
1616 char**
ftp_genlist(ftpbuf_t * ftp,const char * cmd,const char * path TSRMLS_DC)1617 ftp_genlist(ftpbuf_t *ftp, const char *cmd, const char *path TSRMLS_DC)
1618 {
1619 	php_stream	*tmpstream = NULL;
1620 	databuf_t	*data = NULL;
1621 	char		*ptr;
1622 	int		ch, lastch;
1623 	size_t		size, rcvd;
1624 	size_t		lines;
1625 	char		**ret = NULL;
1626 	char		**entry;
1627 	char		*text;
1628 
1629 
1630 	if ((tmpstream = php_stream_fopen_tmpfile()) == NULL) {
1631 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to create temporary file.  Check permissions in temporary files directory.");
1632 		return NULL;
1633 	}
1634 
1635 	if (!ftp_type(ftp, FTPTYPE_ASCII)) {
1636 		goto bail;
1637 	}
1638 
1639 	if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) {
1640 		goto bail;
1641 	}
1642 	ftp->data = data;
1643 
1644 	if (!ftp_putcmd(ftp, cmd, path)) {
1645 		goto bail;
1646 	}
1647 	if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125 && ftp->resp != 226)) {
1648 		goto bail;
1649 	}
1650 
1651 	/* some servers don't open a ftp-data connection if the directory is empty */
1652 	if (ftp->resp == 226) {
1653 		ftp->data = data_close(ftp, data);
1654 		php_stream_close(tmpstream);
1655 		return ecalloc(1, sizeof(char*));
1656 	}
1657 
1658 	/* pull data buffer into tmpfile */
1659 	if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) {
1660 		goto bail;
1661 	}
1662 	size = 0;
1663 	lines = 0;
1664 	lastch = 0;
1665 	while ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) {
1666 		if (rcvd == -1 || rcvd > ((size_t)(-1))-size) {
1667 			goto bail;
1668 		}
1669 
1670 		php_stream_write(tmpstream, data->buf, rcvd);
1671 
1672 		size += rcvd;
1673 		for (ptr = data->buf; rcvd; rcvd--, ptr++) {
1674 			if (*ptr == '\n' && lastch == '\r') {
1675 				lines++;
1676 			}
1677 			lastch = *ptr;
1678 		}
1679 	}
1680 
1681 	ftp->data = data_close(ftp, data);
1682 
1683 	php_stream_rewind(tmpstream);
1684 
1685 	ret = safe_emalloc((lines + 1), sizeof(char*), size);
1686 
1687 	entry = ret;
1688 	text = (char*) (ret + lines + 1);
1689 	*entry = text;
1690 	lastch = 0;
1691 	while ((ch = php_stream_getc(tmpstream)) != EOF) {
1692 		if (ch == '\n' && lastch == '\r') {
1693 			*(text - 1) = 0;
1694 			*++entry = text;
1695 		} else {
1696 			*text++ = ch;
1697 		}
1698 		lastch = ch;
1699 	}
1700 	*entry = NULL;
1701 
1702 	php_stream_close(tmpstream);
1703 
1704 	if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
1705 		efree(ret);
1706 		return NULL;
1707 	}
1708 
1709 	return ret;
1710 bail:
1711 	ftp->data = data_close(ftp, data);
1712 	php_stream_close(tmpstream);
1713 	if (ret)
1714 		efree(ret);
1715 	return NULL;
1716 }
1717 /* }}} */
1718 
1719 /* {{{ ftp_nb_get
1720  */
1721 int
ftp_nb_get(ftpbuf_t * ftp,php_stream * outstream,const char * path,ftptype_t type,long resumepos TSRMLS_DC)1722 ftp_nb_get(ftpbuf_t *ftp, php_stream *outstream, const char *path, ftptype_t type, long resumepos TSRMLS_DC)
1723 {
1724 	databuf_t		*data = NULL;
1725 	char			arg[11];
1726 
1727 	if (ftp == NULL) {
1728 		return PHP_FTP_FAILED;
1729 	}
1730 
1731 	if (!ftp_type(ftp, type)) {
1732 		goto bail;
1733 	}
1734 
1735 	if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) {
1736 		goto bail;
1737 	}
1738 
1739 	if (resumepos>0) {
1740 		snprintf(arg, sizeof(arg), "%ld", resumepos);
1741 		if (!ftp_putcmd(ftp, "REST", arg)) {
1742 			goto bail;
1743 		}
1744 		if (!ftp_getresp(ftp) || (ftp->resp != 350)) {
1745 			goto bail;
1746 		}
1747 	}
1748 
1749 	if (!ftp_putcmd(ftp, "RETR", path)) {
1750 		goto bail;
1751 	}
1752 	if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
1753 		goto bail;
1754 	}
1755 
1756 	if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) {
1757 		goto bail;
1758 	}
1759 
1760 	ftp->data = data;
1761 	ftp->stream = outstream;
1762 	ftp->lastch = 0;
1763 	ftp->nb = 1;
1764 
1765 	return (ftp_nb_continue_read(ftp TSRMLS_CC));
1766 
1767 bail:
1768 	ftp->data = data_close(ftp, data);
1769 	return PHP_FTP_FAILED;
1770 }
1771 /* }}} */
1772 
1773 /* {{{ ftp_nb_continue_read
1774  */
1775 int
ftp_nb_continue_read(ftpbuf_t * ftp TSRMLS_DC)1776 ftp_nb_continue_read(ftpbuf_t *ftp TSRMLS_DC)
1777 {
1778 	databuf_t	*data = NULL;
1779 	char		*ptr;
1780 	int		lastch;
1781 	size_t		rcvd;
1782 	ftptype_t	type;
1783 
1784 	data = ftp->data;
1785 
1786 	/* check if there is already more data */
1787 	if (!data_available(ftp, data->fd)) {
1788 		return PHP_FTP_MOREDATA;
1789 	}
1790 
1791 	type = ftp->type;
1792 
1793 	lastch = ftp->lastch;
1794 	if ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) {
1795 		if (rcvd == -1) {
1796 			goto bail;
1797 		}
1798 
1799 		if (type == FTPTYPE_ASCII) {
1800 			for (ptr = data->buf; rcvd; rcvd--, ptr++) {
1801 				if (lastch == '\r' && *ptr != '\n') {
1802 					php_stream_putc(ftp->stream, '\r');
1803 				}
1804 				if (*ptr != '\r') {
1805 					php_stream_putc(ftp->stream, *ptr);
1806 				}
1807 				lastch = *ptr;
1808 			}
1809 		} else if (rcvd != php_stream_write(ftp->stream, data->buf, rcvd)) {
1810 			goto bail;
1811 		}
1812 
1813 		ftp->lastch = lastch;
1814 		return PHP_FTP_MOREDATA;
1815 	}
1816 
1817 	if (type == FTPTYPE_ASCII && lastch == '\r') {
1818 		php_stream_putc(ftp->stream, '\r');
1819 	}
1820 
1821 	ftp->data = data = data_close(ftp, data);
1822 
1823 	if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
1824 		goto bail;
1825 	}
1826 
1827 	ftp->nb = 0;
1828 	return PHP_FTP_FINISHED;
1829 bail:
1830 	ftp->nb = 0;
1831 	ftp->data = data_close(ftp, data);
1832 	return PHP_FTP_FAILED;
1833 }
1834 /* }}} */
1835 
1836 /* {{{ ftp_nb_put
1837  */
1838 int
ftp_nb_put(ftpbuf_t * ftp,const char * path,php_stream * instream,ftptype_t type,long startpos TSRMLS_DC)1839 ftp_nb_put(ftpbuf_t *ftp, const char *path, php_stream *instream, ftptype_t type, long startpos TSRMLS_DC)
1840 {
1841 	databuf_t		*data = NULL;
1842 	char			arg[11];
1843 
1844 	if (ftp == NULL) {
1845 		return 0;
1846 	}
1847 	if (!ftp_type(ftp, type)) {
1848 		goto bail;
1849 	}
1850 	if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) {
1851 		goto bail;
1852 	}
1853 	if (startpos > 0) {
1854 		snprintf(arg, sizeof(arg), "%ld", startpos);
1855 		if (!ftp_putcmd(ftp, "REST", arg)) {
1856 			goto bail;
1857 		}
1858 		if (!ftp_getresp(ftp) || (ftp->resp != 350)) {
1859 			goto bail;
1860 		}
1861 	}
1862 
1863 	if (!ftp_putcmd(ftp, "STOR", path)) {
1864 		goto bail;
1865 	}
1866 	if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
1867 		goto bail;
1868 	}
1869 	if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) {
1870 		goto bail;
1871 	}
1872 	ftp->data = data;
1873 	ftp->stream = instream;
1874 	ftp->lastch = 0;
1875 	ftp->nb = 1;
1876 
1877 	return (ftp_nb_continue_write(ftp TSRMLS_CC));
1878 
1879 bail:
1880 	ftp->data = data_close(ftp, data);
1881 	return PHP_FTP_FAILED;
1882 }
1883 /* }}} */
1884 
1885 
1886 /* {{{ ftp_nb_continue_write
1887  */
1888 int
ftp_nb_continue_write(ftpbuf_t * ftp TSRMLS_DC)1889 ftp_nb_continue_write(ftpbuf_t *ftp TSRMLS_DC)
1890 {
1891 	long			size;
1892 	char			*ptr;
1893 	int 			ch;
1894 
1895 	/* check if we can write more data */
1896 	if (!data_writeable(ftp, ftp->data->fd)) {
1897 		return PHP_FTP_MOREDATA;
1898 	}
1899 
1900 	size = 0;
1901 	ptr = ftp->data->buf;
1902 	while (!php_stream_eof(ftp->stream) && (ch = php_stream_getc(ftp->stream)) != EOF) {
1903 
1904 		if (ch == '\n' && ftp->type == FTPTYPE_ASCII) {
1905 			*ptr++ = '\r';
1906 			size++;
1907 		}
1908 
1909 		*ptr++ = ch;
1910 		size++;
1911 
1912 		/* flush if necessary */
1913 		if (FTP_BUFSIZE - size < 2) {
1914 			if (my_send(ftp, ftp->data->fd, ftp->data->buf, size) != size) {
1915 				goto bail;
1916 			}
1917 			return PHP_FTP_MOREDATA;
1918 		}
1919 	}
1920 
1921 	if (size && my_send(ftp, ftp->data->fd, ftp->data->buf, size) != size) {
1922 		goto bail;
1923 	}
1924 	ftp->data = data_close(ftp, ftp->data);
1925 
1926 	if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
1927 		goto bail;
1928 	}
1929 	ftp->nb = 0;
1930 	return PHP_FTP_FINISHED;
1931 bail:
1932 	ftp->data = data_close(ftp, ftp->data);
1933 	ftp->nb = 0;
1934 	return PHP_FTP_FAILED;
1935 }
1936 /* }}} */
1937 
1938 #endif /* HAVE_FTP */
1939 
1940 /*
1941  * Local variables:
1942  * tab-width: 4
1943  * c-basic-offset: 4
1944  * End:
1945  * vim600: sw=4 ts=4 fdm=marker
1946  * vim<600: sw=4 ts=4
1947  */
1948