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