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