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