xref: /PHP-5.3/ext/ftp/ftp.c (revision a2045ff3)
1 /*
2    +----------------------------------------------------------------------+
3    | PHP Version 5                                                        |
4    +----------------------------------------------------------------------+
5    | Copyright (c) 1997-2013 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), atleast 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->fd != -1) {
182 #if HAVE_OPENSSL_EXT
183 		if (ftp->ssl_active) {
184 			SSL_shutdown(ftp->ssl_handle);
185 		}
186 #endif
187 		closesocket(ftp->fd);
188 	}
189 	ftp_gc(ftp);
190 	efree(ftp);
191 	return NULL;
192 }
193 /* }}} */
194 
195 /* {{{ ftp_gc
196  */
197 void
ftp_gc(ftpbuf_t * ftp)198 ftp_gc(ftpbuf_t *ftp)
199 {
200 	if (ftp == NULL) {
201 		return;
202 	}
203 	if (ftp->pwd) {
204 		efree(ftp->pwd);
205 		ftp->pwd = NULL;
206 	}
207 	if (ftp->syst) {
208 		efree(ftp->syst);
209 		ftp->syst = NULL;
210 	}
211 }
212 /* }}} */
213 
214 /* {{{ ftp_quit
215  */
216 int
ftp_quit(ftpbuf_t * ftp)217 ftp_quit(ftpbuf_t *ftp)
218 {
219 	if (ftp == NULL) {
220 		return 0;
221 	}
222 
223 	if (!ftp_putcmd(ftp, "QUIT", NULL)) {
224 		return 0;
225 	}
226 	if (!ftp_getresp(ftp) || ftp->resp != 221) {
227 		return 0;
228 	}
229 
230 	if (ftp->pwd) {
231 		efree(ftp->pwd);
232 		ftp->pwd = NULL;
233 	}
234 
235 	return 1;
236 }
237 /* }}} */
238 
239 /* {{{ ftp_login
240  */
241 int
ftp_login(ftpbuf_t * ftp,const char * user,const char * pass TSRMLS_DC)242 ftp_login(ftpbuf_t *ftp, const char *user, const char *pass TSRMLS_DC)
243 {
244 #if HAVE_OPENSSL_EXT
245 	SSL_CTX	*ctx = NULL;
246 #endif
247 	if (ftp == NULL) {
248 		return 0;
249 	}
250 
251 #if HAVE_OPENSSL_EXT
252 	if (ftp->use_ssl && !ftp->ssl_active) {
253 		if (!ftp_putcmd(ftp, "AUTH", "TLS")) {
254 			return 0;
255 		}
256 		if (!ftp_getresp(ftp)) {
257 			return 0;
258 		}
259 
260 		if (ftp->resp != 234) {
261 			if (!ftp_putcmd(ftp, "AUTH", "SSL")) {
262 				return 0;
263 			}
264 			if (!ftp_getresp(ftp)) {
265 				return 0;
266 			}
267 
268 			if (ftp->resp != 334) {
269 				return 0;
270 			} else {
271 				ftp->old_ssl = 1;
272 				ftp->use_ssl_for_data = 1;
273 			}
274 		}
275 
276 		ctx = SSL_CTX_new(SSLv23_client_method());
277 		if (ctx == NULL) {
278 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create the SSL context");
279 			return 0;
280 		}
281 
282 		SSL_CTX_set_options(ctx, SSL_OP_ALL);
283 
284 		ftp->ssl_handle = SSL_new(ctx);
285 		if (ftp->ssl_handle == NULL) {
286 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "failed to create the SSL handle");
287 			SSL_CTX_free(ctx);
288 			return 0;
289 		}
290 
291 		SSL_set_fd(ftp->ssl_handle, ftp->fd);
292 
293 		if (SSL_connect(ftp->ssl_handle) <= 0) {
294 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "SSL/TLS handshake failed");
295 			SSL_shutdown(ftp->ssl_handle);
296 			return 0;
297 		}
298 
299 		ftp->ssl_active = 1;
300 
301 		if (!ftp->old_ssl) {
302 
303 			/* set protection buffersize to zero */
304 			if (!ftp_putcmd(ftp, "PBSZ", "0")) {
305 				return 0;
306 			}
307 			if (!ftp_getresp(ftp)) {
308 				return 0;
309 			}
310 
311 			/* enable data conn encryption */
312 			if (!ftp_putcmd(ftp, "PROT", "P")) {
313 				return 0;
314 			}
315 			if (!ftp_getresp(ftp)) {
316 				return 0;
317 			}
318 
319 			ftp->use_ssl_for_data = (ftp->resp >= 200 && ftp->resp <=299);
320 		}
321 	}
322 #endif
323 
324 	if (!ftp_putcmd(ftp, "USER", user)) {
325 		return 0;
326 	}
327 	if (!ftp_getresp(ftp)) {
328 		return 0;
329 	}
330 	if (ftp->resp == 230) {
331 		return 1;
332 	}
333 	if (ftp->resp != 331) {
334 		return 0;
335 	}
336 	if (!ftp_putcmd(ftp, "PASS", pass)) {
337 		return 0;
338 	}
339 	if (!ftp_getresp(ftp)) {
340 		return 0;
341 	}
342 	return (ftp->resp == 230);
343 }
344 /* }}} */
345 
346 /* {{{ ftp_reinit
347  */
348 int
ftp_reinit(ftpbuf_t * ftp)349 ftp_reinit(ftpbuf_t *ftp)
350 {
351 	if (ftp == NULL) {
352 		return 0;
353 	}
354 
355 	ftp_gc(ftp);
356 
357 	ftp->nb = 0;
358 
359 	if (!ftp_putcmd(ftp, "REIN", NULL)) {
360 		return 0;
361 	}
362 	if (!ftp_getresp(ftp) || ftp->resp != 220) {
363 		return 0;
364 	}
365 
366 	return 1;
367 }
368 /* }}} */
369 
370 /* {{{ ftp_syst
371  */
372 const char*
ftp_syst(ftpbuf_t * ftp)373 ftp_syst(ftpbuf_t *ftp)
374 {
375 	char *syst, *end;
376 
377 	if (ftp == NULL) {
378 		return NULL;
379 	}
380 
381 	/* default to cached value */
382 	if (ftp->syst) {
383 		return ftp->syst;
384 	}
385 	if (!ftp_putcmd(ftp, "SYST", NULL)) {
386 		return NULL;
387 	}
388 	if (!ftp_getresp(ftp) || ftp->resp != 215) {
389 		return NULL;
390 	}
391 	syst = ftp->inbuf;
392 	while (*syst == ' ') {
393 		syst++;
394 	}
395 	if ((end = strchr(syst, ' '))) {
396 		*end = 0;
397 	}
398 	ftp->syst = estrdup(syst);
399 	if (end) {
400 		*end = ' ';
401 	}
402 	return ftp->syst;
403 }
404 /* }}} */
405 
406 /* {{{ ftp_pwd
407  */
408 const char*
ftp_pwd(ftpbuf_t * ftp)409 ftp_pwd(ftpbuf_t *ftp)
410 {
411 	char *pwd, *end;
412 
413 	if (ftp == NULL) {
414 		return NULL;
415 	}
416 
417 	/* default to cached value */
418 	if (ftp->pwd) {
419 		return ftp->pwd;
420 	}
421 	if (!ftp_putcmd(ftp, "PWD", NULL)) {
422 		return NULL;
423 	}
424 	if (!ftp_getresp(ftp) || ftp->resp != 257) {
425 		return NULL;
426 	}
427 	/* copy out the pwd from response */
428 	if ((pwd = strchr(ftp->inbuf, '"')) == NULL) {
429 		return NULL;
430 	}
431 	if ((end = strrchr(++pwd, '"')) == NULL) {
432 		return NULL;
433 	}
434 	ftp->pwd = estrndup(pwd, end - pwd);
435 
436 	return ftp->pwd;
437 }
438 /* }}} */
439 
440 /* {{{ ftp_exec
441  */
442 int
ftp_exec(ftpbuf_t * ftp,const char * cmd)443 ftp_exec(ftpbuf_t *ftp, const char *cmd)
444 {
445 	if (ftp == NULL) {
446 		return 0;
447 	}
448 	if (!ftp_putcmd(ftp, "SITE EXEC", cmd)) {
449 		return 0;
450 	}
451 	if (!ftp_getresp(ftp) || ftp->resp != 200) {
452 		return 0;
453 	}
454 
455 	return 1;
456 }
457 /* }}} */
458 
459 /* {{{ ftp_raw
460  */
461 void
ftp_raw(ftpbuf_t * ftp,const char * cmd,zval * return_value)462 ftp_raw(ftpbuf_t *ftp, const char *cmd, zval *return_value)
463 {
464 	if (ftp == NULL || cmd == NULL) {
465 		RETURN_NULL();
466 	}
467 	if (!ftp_putcmd(ftp, cmd, NULL)) {
468 		RETURN_NULL();
469 	}
470 	array_init(return_value);
471 	while (ftp_readline(ftp)) {
472 		add_next_index_string(return_value, ftp->inbuf, 1);
473 		if (isdigit(ftp->inbuf[0]) && isdigit(ftp->inbuf[1]) && isdigit(ftp->inbuf[2]) && ftp->inbuf[3] == ' ') {
474 			return;
475 		}
476 	}
477 }
478 /* }}} */
479 
480 /* {{{ ftp_chdir
481  */
482 int
ftp_chdir(ftpbuf_t * ftp,const char * dir)483 ftp_chdir(ftpbuf_t *ftp, const char *dir)
484 {
485 	if (ftp == NULL) {
486 		return 0;
487 	}
488 
489 	if (ftp->pwd) {
490 		efree(ftp->pwd);
491 		ftp->pwd = NULL;
492 	}
493 
494 	if (!ftp_putcmd(ftp, "CWD", dir)) {
495 		return 0;
496 	}
497 	if (!ftp_getresp(ftp) || ftp->resp != 250) {
498 		return 0;
499 	}
500 	return 1;
501 }
502 /* }}} */
503 
504 /* {{{ ftp_cdup
505  */
506 int
ftp_cdup(ftpbuf_t * ftp)507 ftp_cdup(ftpbuf_t *ftp)
508 {
509 	if (ftp == NULL) {
510 		return 0;
511 	}
512 
513 	if (ftp->pwd) {
514 		efree(ftp->pwd);
515 		ftp->pwd = NULL;
516 	}
517 
518 	if (!ftp_putcmd(ftp, "CDUP", NULL)) {
519 		return 0;
520 	}
521 	if (!ftp_getresp(ftp) || ftp->resp != 250) {
522 		return 0;
523 	}
524 	return 1;
525 }
526 /* }}} */
527 
528 /* {{{ ftp_mkdir
529  */
530 char*
ftp_mkdir(ftpbuf_t * ftp,const char * dir)531 ftp_mkdir(ftpbuf_t *ftp, const char *dir)
532 {
533 	char *mkd, *end;
534 
535 	if (ftp == NULL) {
536 		return NULL;
537 	}
538 	if (!ftp_putcmd(ftp, "MKD", dir)) {
539 		return NULL;
540 	}
541 	if (!ftp_getresp(ftp) || ftp->resp != 257) {
542 		return NULL;
543 	}
544 	/* copy out the dir from response */
545 	if ((mkd = strchr(ftp->inbuf, '"')) == NULL) {
546 		mkd = estrdup(dir);
547 		return mkd;
548 	}
549 	if ((end = strrchr(++mkd, '"')) == NULL) {
550 		return NULL;
551 	}
552 	*end = 0;
553 	mkd = estrdup(mkd);
554 	*end = '"';
555 
556 	return mkd;
557 }
558 /* }}} */
559 
560 /* {{{ ftp_rmdir
561  */
562 int
ftp_rmdir(ftpbuf_t * ftp,const char * dir)563 ftp_rmdir(ftpbuf_t *ftp, const char *dir)
564 {
565 	if (ftp == NULL) {
566 		return 0;
567 	}
568 	if (!ftp_putcmd(ftp, "RMD", dir)) {
569 		return 0;
570 	}
571 	if (!ftp_getresp(ftp) || ftp->resp != 250) {
572 		return 0;
573 	}
574 	return 1;
575 }
576 /* }}} */
577 
578 /* {{{ ftp_chmod
579  */
580 int
ftp_chmod(ftpbuf_t * ftp,const int mode,const char * filename,const int filename_len)581 ftp_chmod(ftpbuf_t *ftp, const int mode, const char *filename, const int filename_len)
582 {
583 	char *buffer;
584 
585 	if (ftp == NULL || filename_len <= 0) {
586 		return 0;
587 	}
588 
589 	spprintf(&buffer, 0, "CHMOD %o %s", mode, filename);
590 
591 	if (!ftp_putcmd(ftp, "SITE", buffer)) {
592 		efree(buffer);
593 		return 0;
594 	}
595 
596 	efree(buffer);
597 
598 	if (!ftp_getresp(ftp) || ftp->resp != 200) {
599 		return 0;
600 	}
601 
602 	return 1;
603 }
604 /* }}} */
605 
606 /* {{{ ftp_alloc
607  */
608 int
ftp_alloc(ftpbuf_t * ftp,const int size,char ** response)609 ftp_alloc(ftpbuf_t *ftp, const int size, char **response)
610 {
611 	char buffer[64];
612 
613 	if (ftp == NULL || size <= 0) {
614 		return 0;
615 	}
616 
617 	snprintf(buffer, sizeof(buffer) - 1, "%d", size);
618 
619 	if (!ftp_putcmd(ftp, "ALLO", buffer)) {
620 		return 0;
621 	}
622 
623 	if (!ftp_getresp(ftp)) {
624 		return 0;
625 	}
626 
627 	if (response && ftp->inbuf) {
628 		*response = estrdup(ftp->inbuf);
629 	}
630 
631 	if (ftp->resp < 200 || ftp->resp >= 300) {
632 		return 0;
633 	}
634 
635 	return 1;
636 }
637 /* }}} */
638 
639 /* {{{ ftp_nlist
640  */
641 char**
ftp_nlist(ftpbuf_t * ftp,const char * path TSRMLS_DC)642 ftp_nlist(ftpbuf_t *ftp, const char *path TSRMLS_DC)
643 {
644 	return ftp_genlist(ftp, "NLST", path TSRMLS_CC);
645 }
646 /* }}} */
647 
648 /* {{{ ftp_list
649  */
650 char**
ftp_list(ftpbuf_t * ftp,const char * path,int recursive TSRMLS_DC)651 ftp_list(ftpbuf_t *ftp, const char *path, int recursive TSRMLS_DC)
652 {
653 	return ftp_genlist(ftp, ((recursive) ? "LIST -R" : "LIST"), path TSRMLS_CC);
654 }
655 /* }}} */
656 
657 /* {{{ ftp_type
658  */
659 int
ftp_type(ftpbuf_t * ftp,ftptype_t type)660 ftp_type(ftpbuf_t *ftp, ftptype_t type)
661 {
662 	char typechar[2] = "?";
663 
664 	if (ftp == NULL) {
665 		return 0;
666 	}
667 	if (type == ftp->type) {
668 		return 1;
669 	}
670 	if (type == FTPTYPE_ASCII) {
671 		typechar[0] = 'A';
672 	} else if (type == FTPTYPE_IMAGE) {
673 		typechar[0] = 'I';
674 	} else {
675 		return 0;
676 	}
677 	if (!ftp_putcmd(ftp, "TYPE", typechar)) {
678 		return 0;
679 	}
680 	if (!ftp_getresp(ftp) || ftp->resp != 200) {
681 		return 0;
682 	}
683 	ftp->type = type;
684 
685 	return 1;
686 }
687 /* }}} */
688 
689 /* {{{ ftp_pasv
690  */
691 int
ftp_pasv(ftpbuf_t * ftp,int pasv)692 ftp_pasv(ftpbuf_t *ftp, int pasv)
693 {
694 	char			*ptr;
695 	union ipbox		ipbox;
696 	unsigned long		b[6];
697 	socklen_t			n;
698 	struct sockaddr *sa;
699 	struct sockaddr_in *sin;
700 
701 	if (ftp == NULL) {
702 		return 0;
703 	}
704 	if (pasv && ftp->pasv == 2) {
705 		return 1;
706 	}
707 	ftp->pasv = 0;
708 	if (!pasv) {
709 		return 1;
710 	}
711 	n = sizeof(ftp->pasvaddr);
712 	memset(&ftp->pasvaddr, 0, n);
713 	sa = (struct sockaddr *) &ftp->pasvaddr;
714 
715 #if HAVE_IPV6
716 	if (getpeername(ftp->fd, sa, &n) < 0) {
717 		return 0;
718 	}
719 	if (sa->sa_family == AF_INET6) {
720 		struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *) sa;
721 		char *endptr, delimiter;
722 
723 		/* try EPSV first */
724 		if (!ftp_putcmd(ftp, "EPSV", NULL)) {
725 			return 0;
726 		}
727 		if (!ftp_getresp(ftp)) {
728 			return 0;
729 		}
730 		if (ftp->resp == 229) {
731 			/* parse out the port */
732 			for (ptr = ftp->inbuf; *ptr && *ptr != '('; ptr++);
733 			if (!*ptr) {
734 				return 0;
735 			}
736 			delimiter = *++ptr;
737 			for (n = 0; *ptr && n < 3; ptr++) {
738 				if (*ptr == delimiter) {
739 					n++;
740 				}
741 			}
742 
743 			sin6->sin6_port = htons((unsigned short) strtoul(ptr, &endptr, 10));
744 			if (ptr == endptr || *endptr != delimiter) {
745 				return 0;
746 			}
747 			ftp->pasv = 2;
748 			return 1;
749 		}
750 	}
751 
752 	/* fall back to PASV */
753 #endif
754 
755 	if (!ftp_putcmd(ftp, "PASV", NULL)) {
756 		return 0;
757 	}
758 	if (!ftp_getresp(ftp) || ftp->resp != 227) {
759 		return 0;
760 	}
761 	/* parse out the IP and port */
762 	for (ptr = ftp->inbuf; *ptr && !isdigit(*ptr); ptr++);
763 	n = sscanf(ptr, "%lu,%lu,%lu,%lu,%lu,%lu", &b[0], &b[1], &b[2], &b[3], &b[4], &b[5]);
764 	if (n != 6) {
765 		return 0;
766 	}
767 	for (n = 0; n < 6; n++) {
768 		ipbox.c[n] = (unsigned char) b[n];
769 	}
770 	sin = (struct sockaddr_in *) sa;
771 	sin->sin_family = AF_INET;
772 	sin->sin_addr = ipbox.ia[0];
773 	sin->sin_port = ipbox.s[2];
774 
775 	ftp->pasv = 2;
776 
777 	return 1;
778 }
779 /* }}} */
780 
781 /* {{{ ftp_get
782  */
783 int
ftp_get(ftpbuf_t * ftp,php_stream * outstream,const char * path,ftptype_t type,int resumepos TSRMLS_DC)784 ftp_get(ftpbuf_t *ftp, php_stream *outstream, const char *path, ftptype_t type, int resumepos TSRMLS_DC)
785 {
786 	databuf_t		*data = NULL;
787 	int			lastch;
788 	size_t			rcvd;
789 	char			arg[11];
790 
791 	if (ftp == NULL) {
792 		return 0;
793 	}
794 	if (!ftp_type(ftp, type)) {
795 		goto bail;
796 	}
797 
798 	if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) {
799 		goto bail;
800 	}
801 
802 	ftp->data = data;
803 
804 	if (resumepos > 0) {
805 		if (resumepos > 2147483647) {
806 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "PHP cannot handle files greater than 2147483647 bytes.");
807 			goto bail;
808 		}
809 		snprintf(arg, sizeof(arg), "%u", resumepos);
810 		if (!ftp_putcmd(ftp, "REST", arg)) {
811 			goto bail;
812 		}
813 		if (!ftp_getresp(ftp) || (ftp->resp != 350)) {
814 			goto bail;
815 		}
816 	}
817 
818 	if (!ftp_putcmd(ftp, "RETR", path)) {
819 		goto bail;
820 	}
821 	if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
822 		goto bail;
823 	}
824 
825 	if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) {
826 		goto bail;
827 	}
828 
829 	lastch = 0;
830 	while ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) {
831 		if (rcvd == -1) {
832 			goto bail;
833 		}
834 
835 		if (type == FTPTYPE_ASCII) {
836 #ifndef PHP_WIN32
837 			char *s;
838 #endif
839 			char *ptr = data->buf;
840 			char *e = ptr + rcvd;
841 			/* logic depends on the OS EOL
842 			 * Win32 -> \r\n
843 			 * Everything Else \n
844 			 */
845 #ifdef PHP_WIN32
846 			php_stream_write(outstream, ptr, (e - ptr));
847 			ptr = e;
848 #else
849 			while (e > ptr && (s = memchr(ptr, '\r', (e - ptr)))) {
850 				php_stream_write(outstream, ptr, (s - ptr));
851 				if (*(s + 1) == '\n') {
852 					s++;
853 					php_stream_putc(outstream, '\n');
854 				}
855 				ptr = s + 1;
856 			}
857 #endif
858 			if (ptr < e) {
859 				php_stream_write(outstream, ptr, (e - ptr));
860 			}
861 		} else if (rcvd != php_stream_write(outstream, data->buf, rcvd)) {
862 			goto bail;
863 		}
864 	}
865 
866 	ftp->data = data = data_close(ftp, data);
867 
868 	if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
869 		goto bail;
870 	}
871 
872 	return 1;
873 bail:
874 	ftp->data = data_close(ftp, data);
875 	return 0;
876 }
877 /* }}} */
878 
879 /* {{{ ftp_put
880  */
881 int
ftp_put(ftpbuf_t * ftp,const char * path,php_stream * instream,ftptype_t type,int startpos TSRMLS_DC)882 ftp_put(ftpbuf_t *ftp, const char *path, php_stream *instream, ftptype_t type, int startpos TSRMLS_DC)
883 {
884 	databuf_t		*data = NULL;
885 	int			size;
886 	char			*ptr;
887 	int			ch;
888 	char			arg[11];
889 
890 	if (ftp == NULL) {
891 		return 0;
892 	}
893 	if (!ftp_type(ftp, type)) {
894 		goto bail;
895 	}
896 	if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) {
897 		goto bail;
898 	}
899 	ftp->data = data;
900 
901 	if (startpos > 0) {
902 		if (startpos > 2147483647) {
903 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "PHP cannot handle files with a size greater than 2147483647 bytes.");
904 			goto bail;
905 		}
906 		snprintf(arg, sizeof(arg), "%u", 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 int
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 atoi(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 	int		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 	char *buf;
1193 
1194 	if (ftp == NULL) {
1195 		return 0;
1196 	}
1197 	buf = ftp->inbuf;
1198 	ftp->resp = 0;
1199 
1200 	while (1) {
1201 
1202 		if (!ftp_readline(ftp)) {
1203 			return 0;
1204 		}
1205 
1206 		/* Break out when the end-tag is found */
1207 		if (isdigit(ftp->inbuf[0]) && isdigit(ftp->inbuf[1]) && isdigit(ftp->inbuf[2]) && ftp->inbuf[3] == ' ') {
1208 			break;
1209 		}
1210 	}
1211 
1212 	/* translate the tag */
1213 	if (!isdigit(ftp->inbuf[0]) || !isdigit(ftp->inbuf[1]) || !isdigit(ftp->inbuf[2])) {
1214 		return 0;
1215 	}
1216 
1217 	ftp->resp = 100 * (ftp->inbuf[0] - '0') + 10 * (ftp->inbuf[1] - '0') + (ftp->inbuf[2] - '0');
1218 
1219 	memmove(ftp->inbuf, ftp->inbuf + 4, FTP_BUFSIZE - 4);
1220 
1221 	if (ftp->extra) {
1222 		ftp->extra -= 4;
1223 	}
1224 	return 1;
1225 }
1226 /* }}} */
1227 
1228 /* {{{ my_send
1229  */
1230 int
my_send(ftpbuf_t * ftp,php_socket_t s,void * buf,size_t len)1231 my_send(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len)
1232 {
1233 	int		n, size, sent;
1234 
1235 	size = len;
1236 	while (size) {
1237 		n = php_pollfd_for_ms(s, POLLOUT, ftp->timeout_sec * 1000);
1238 
1239 		if (n < 1) {
1240 
1241 #if !defined(PHP_WIN32) && !(defined(NETWARE) && defined(USE_WINSOCK))
1242 			if (n == 0) {
1243 				errno = ETIMEDOUT;
1244 			}
1245 #endif
1246 			return -1;
1247 		}
1248 
1249 #if HAVE_OPENSSL_EXT
1250 		if (ftp->use_ssl && ftp->fd == s && ftp->ssl_active) {
1251 			sent = SSL_write(ftp->ssl_handle, buf, size);
1252 		} else if (ftp->use_ssl && ftp->fd != s && ftp->use_ssl_for_data && ftp->data->ssl_active) {
1253 			sent = SSL_write(ftp->data->ssl_handle, buf, size);
1254 		} else {
1255 #endif
1256 			sent = send(s, buf, size, 0);
1257 #if HAVE_OPENSSL_EXT
1258 		}
1259 #endif
1260 		if (sent == -1) {
1261 			return -1;
1262 		}
1263 
1264 		buf = (char*) buf + sent;
1265 		size -= sent;
1266 	}
1267 
1268 	return len;
1269 }
1270 /* }}} */
1271 
1272 /* {{{ my_recv
1273  */
1274 int
my_recv(ftpbuf_t * ftp,php_socket_t s,void * buf,size_t len)1275 my_recv(ftpbuf_t *ftp, php_socket_t s, void *buf, size_t len)
1276 {
1277 	int		n, nr_bytes;
1278 
1279 	n = php_pollfd_for_ms(s, PHP_POLLREADABLE, ftp->timeout_sec * 1000);
1280 	if (n < 1) {
1281 #if !defined(PHP_WIN32) && !(defined(NETWARE) && defined(USE_WINSOCK))
1282 		if (n == 0) {
1283 			errno = ETIMEDOUT;
1284 		}
1285 #endif
1286 		return -1;
1287 	}
1288 
1289 #if HAVE_OPENSSL_EXT
1290 	if (ftp->use_ssl && ftp->fd == s && ftp->ssl_active) {
1291 		nr_bytes = SSL_read(ftp->ssl_handle, buf, len);
1292 	} else if (ftp->use_ssl && ftp->fd != s && ftp->use_ssl_for_data && ftp->data->ssl_active) {
1293 		nr_bytes = SSL_read(ftp->data->ssl_handle, buf, len);
1294 	} else {
1295 #endif
1296 		nr_bytes = recv(s, buf, len, 0);
1297 #if HAVE_OPENSSL_EXT
1298 	}
1299 #endif
1300 	return (nr_bytes);
1301 }
1302 /* }}} */
1303 
1304 /* {{{ data_available
1305  */
1306 int
data_available(ftpbuf_t * ftp,php_socket_t s)1307 data_available(ftpbuf_t *ftp, php_socket_t s)
1308 {
1309 	int		n;
1310 
1311 	n = php_pollfd_for_ms(s, PHP_POLLREADABLE, 1000);
1312 	if (n < 1) {
1313 #if !defined(PHP_WIN32) && !(defined(NETWARE) && defined(USE_WINSOCK))
1314 		if (n == 0) {
1315 			errno = ETIMEDOUT;
1316 		}
1317 #endif
1318 		return 0;
1319 	}
1320 
1321 	return 1;
1322 }
1323 /* }}} */
1324 /* {{{ data_writeable
1325  */
1326 int
data_writeable(ftpbuf_t * ftp,php_socket_t s)1327 data_writeable(ftpbuf_t *ftp, php_socket_t s)
1328 {
1329 	int		n;
1330 
1331 	n = php_pollfd_for_ms(s, POLLOUT, 1000);
1332 	if (n < 1) {
1333 #ifndef PHP_WIN32
1334 		if (n == 0) {
1335 			errno = ETIMEDOUT;
1336 		}
1337 #endif
1338 		return 0;
1339 	}
1340 
1341 	return 1;
1342 }
1343 /* }}} */
1344 
1345 /* {{{ my_accept
1346  */
1347 int
my_accept(ftpbuf_t * ftp,php_socket_t s,struct sockaddr * addr,socklen_t * addrlen)1348 my_accept(ftpbuf_t *ftp, php_socket_t s, struct sockaddr *addr, socklen_t *addrlen)
1349 {
1350 	int		n;
1351 
1352 	n = php_pollfd_for_ms(s, PHP_POLLREADABLE, ftp->timeout_sec * 1000);
1353 	if (n < 1) {
1354 #if !defined(PHP_WIN32) && !(defined(NETWARE) && defined(USE_WINSOCK))
1355 		if (n == 0) {
1356 			errno = ETIMEDOUT;
1357 		}
1358 #endif
1359 		return -1;
1360 	}
1361 
1362 	return accept(s, addr, addrlen);
1363 }
1364 /* }}} */
1365 
1366 /* {{{ ftp_getdata
1367  */
1368 databuf_t*
ftp_getdata(ftpbuf_t * ftp TSRMLS_DC)1369 ftp_getdata(ftpbuf_t *ftp TSRMLS_DC)
1370 {
1371 	int			fd = -1;
1372 	databuf_t		*data;
1373 	php_sockaddr_storage addr;
1374 	struct sockaddr *sa;
1375 	socklen_t		size;
1376 	union ipbox		ipbox;
1377 	char			arg[sizeof("255, 255, 255, 255, 255, 255")];
1378 	struct timeval	tv;
1379 
1380 
1381 	/* ask for a passive connection if we need one */
1382 	if (ftp->pasv && !ftp_pasv(ftp, 1)) {
1383 		return NULL;
1384 	}
1385 	/* alloc the data structure */
1386 	data = ecalloc(1, sizeof(*data));
1387 	data->listener = -1;
1388 	data->fd = -1;
1389 	data->type = ftp->type;
1390 
1391 	sa = (struct sockaddr *) &ftp->localaddr;
1392 	/* bind/listen */
1393 	if ((fd = socket(sa->sa_family, SOCK_STREAM, 0)) == SOCK_ERR) {
1394 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "socket() failed: %s (%d)", strerror(errno), errno);
1395 		goto bail;
1396 	}
1397 
1398 	/* passive connection handler */
1399 	if (ftp->pasv) {
1400 		/* clear the ready status */
1401 		ftp->pasv = 1;
1402 
1403 		/* connect */
1404 		/* Win 95/98 seems not to like size > sizeof(sockaddr_in) */
1405 		size = php_sockaddr_size(&ftp->pasvaddr);
1406 		tv.tv_sec = ftp->timeout_sec;
1407 		tv.tv_usec = 0;
1408 		if (php_connect_nonb(fd, (struct sockaddr*) &ftp->pasvaddr, size, &tv) == -1) {
1409 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "php_connect_nonb() failed: %s (%d)", strerror(errno), errno);
1410 			goto bail;
1411 		}
1412 
1413 		data->fd = fd;
1414 
1415 		ftp->data = data;
1416 		return data;
1417 	}
1418 
1419 
1420 	/* active (normal) connection */
1421 
1422 	/* bind to a local address */
1423 	php_any_addr(sa->sa_family, &addr, 0);
1424 	size = php_sockaddr_size(&addr);
1425 
1426 	if (bind(fd, (struct sockaddr*) &addr, size) != 0) {
1427 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "bind() failed: %s (%d)", strerror(errno), errno);
1428 		goto bail;
1429 	}
1430 
1431 	if (getsockname(fd, (struct sockaddr*) &addr, &size) != 0) {
1432 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "getsockname() failed: %s (%d)", strerror(errno), errno);
1433 		goto bail;
1434 	}
1435 
1436 	if (listen(fd, 5) != 0) {
1437 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "listen() failed: %s (%d)", strerror(errno), errno);
1438 		goto bail;
1439 	}
1440 
1441 	data->listener = fd;
1442 
1443 #if HAVE_IPV6 && HAVE_INET_NTOP
1444 	if (sa->sa_family == AF_INET6) {
1445 		/* need to use EPRT */
1446 		char eprtarg[INET6_ADDRSTRLEN + sizeof("|x||xxxxx|")];
1447 		char out[INET6_ADDRSTRLEN];
1448 		inet_ntop(AF_INET6, &((struct sockaddr_in6*) sa)->sin6_addr, out, sizeof(out));
1449 		snprintf(eprtarg, sizeof(eprtarg), "|2|%s|%hu|", out, ntohs(((struct sockaddr_in6 *) &addr)->sin6_port));
1450 
1451 		if (!ftp_putcmd(ftp, "EPRT", eprtarg)) {
1452 			goto bail;
1453 		}
1454 
1455 		if (!ftp_getresp(ftp) || ftp->resp != 200) {
1456 			goto bail;
1457 		}
1458 
1459 		ftp->data = data;
1460 		return data;
1461 	}
1462 #endif
1463 
1464 	/* send the PORT */
1465 	ipbox.ia[0] = ((struct sockaddr_in*) sa)->sin_addr;
1466 	ipbox.s[2] = ((struct sockaddr_in*) &addr)->sin_port;
1467 	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]);
1468 
1469 	if (!ftp_putcmd(ftp, "PORT", arg)) {
1470 		goto bail;
1471 	}
1472 	if (!ftp_getresp(ftp) || ftp->resp != 200) {
1473 		goto bail;
1474 	}
1475 
1476 	ftp->data = data;
1477 	return data;
1478 
1479 bail:
1480 	if (fd != -1) {
1481 		closesocket(fd);
1482 	}
1483 	efree(data);
1484 	return NULL;
1485 }
1486 /* }}} */
1487 
1488 /* {{{ data_accept
1489  */
1490 databuf_t*
data_accept(databuf_t * data,ftpbuf_t * ftp TSRMLS_DC)1491 data_accept(databuf_t *data, ftpbuf_t *ftp TSRMLS_DC)
1492 {
1493 	php_sockaddr_storage addr;
1494 	socklen_t			size;
1495 
1496 #if HAVE_OPENSSL_EXT
1497 	SSL_CTX		*ctx;
1498 #endif
1499 
1500 	if (data->fd != -1) {
1501 		goto data_accepted;
1502 	}
1503 	size = sizeof(addr);
1504 	data->fd = my_accept(ftp, data->listener, (struct sockaddr*) &addr, &size);
1505 	closesocket(data->listener);
1506 	data->listener = -1;
1507 
1508 	if (data->fd == -1) {
1509 		efree(data);
1510 		return NULL;
1511 	}
1512 
1513 data_accepted:
1514 #if HAVE_OPENSSL_EXT
1515 
1516 	/* now enable ssl if we need to */
1517 	if (ftp->use_ssl && ftp->use_ssl_for_data) {
1518 		ctx = SSL_CTX_new(SSLv23_client_method());
1519 		if (ctx == NULL) {
1520 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_accept: failed to create the SSL context");
1521 			return 0;
1522 		}
1523 
1524 		SSL_CTX_set_options(ctx, SSL_OP_ALL);
1525 
1526 		data->ssl_handle = SSL_new(ctx);
1527 		if (data->ssl_handle == NULL) {
1528 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_accept: failed to create the SSL handle");
1529 			SSL_CTX_free(ctx);
1530 			return 0;
1531 		}
1532 
1533 
1534 		SSL_set_fd(data->ssl_handle, data->fd);
1535 
1536 		if (ftp->old_ssl) {
1537 			SSL_copy_session_id(data->ssl_handle, ftp->ssl_handle);
1538 		}
1539 
1540 		if (SSL_connect(data->ssl_handle) <= 0) {
1541 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "data_accept: SSL/TLS handshake failed");
1542 			SSL_shutdown(data->ssl_handle);
1543 			return 0;
1544 		}
1545 
1546 		data->ssl_active = 1;
1547 	}
1548 
1549 #endif
1550 
1551 	return data;
1552 }
1553 /* }}} */
1554 
1555 /* {{{ data_close
1556  */
1557 databuf_t*
data_close(ftpbuf_t * ftp,databuf_t * data)1558 data_close(ftpbuf_t *ftp, databuf_t *data)
1559 {
1560 	if (data == NULL) {
1561 		return NULL;
1562 	}
1563 	if (data->listener != -1) {
1564 #if HAVE_OPENSSL_EXT
1565 		if (data->ssl_active) {
1566 			SSL_shutdown(data->ssl_handle);
1567 			data->ssl_active = 0;
1568 		}
1569 #endif
1570 		closesocket(data->listener);
1571 	}
1572 	if (data->fd != -1) {
1573 #if HAVE_OPENSSL_EXT
1574 		if (data->ssl_active) {
1575 			SSL_shutdown(data->ssl_handle);
1576 			data->ssl_active = 0;
1577 		}
1578 #endif
1579 		closesocket(data->fd);
1580 	}
1581 	if (ftp) {
1582 		ftp->data = NULL;
1583 	}
1584 	efree(data);
1585 	return NULL;
1586 }
1587 /* }}} */
1588 
1589 /* {{{ ftp_genlist
1590  */
1591 char**
ftp_genlist(ftpbuf_t * ftp,const char * cmd,const char * path TSRMLS_DC)1592 ftp_genlist(ftpbuf_t *ftp, const char *cmd, const char *path TSRMLS_DC)
1593 {
1594 	php_stream	*tmpstream = NULL;
1595 	databuf_t	*data = NULL;
1596 	char		*ptr;
1597 	int		ch, lastch;
1598 	int		size, rcvd;
1599 	int		lines;
1600 	char		**ret = NULL;
1601 	char		**entry;
1602 	char		*text;
1603 
1604 
1605 	if ((tmpstream = php_stream_fopen_tmpfile()) == NULL) {
1606 		php_error_docref(NULL TSRMLS_CC, E_WARNING, "Unable to create temporary file.  Check permissions in temporary files directory.");
1607 		return NULL;
1608 	}
1609 
1610 	if (!ftp_type(ftp, FTPTYPE_ASCII)) {
1611 		goto bail;
1612 	}
1613 
1614 	if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) {
1615 		goto bail;
1616 	}
1617 	ftp->data = data;
1618 
1619 	if (!ftp_putcmd(ftp, cmd, path)) {
1620 		goto bail;
1621 	}
1622 	if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125 && ftp->resp != 226)) {
1623 		goto bail;
1624 	}
1625 
1626 	/* some servers don't open a ftp-data connection if the directory is empty */
1627 	if (ftp->resp == 226) {
1628 		ftp->data = data_close(ftp, data);
1629 		php_stream_close(tmpstream);
1630 		return ecalloc(1, sizeof(char**));
1631 	}
1632 
1633 	/* pull data buffer into tmpfile */
1634 	if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) {
1635 		goto bail;
1636 	}
1637 	size = 0;
1638 	lines = 0;
1639 	lastch = 0;
1640 	while ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) {
1641 		if (rcvd == -1) {
1642 			goto bail;
1643 		}
1644 
1645 		php_stream_write(tmpstream, data->buf, rcvd);
1646 
1647 		size += rcvd;
1648 		for (ptr = data->buf; rcvd; rcvd--, ptr++) {
1649 			if (*ptr == '\n' && lastch == '\r') {
1650 				lines++;
1651 			} else {
1652 				size++;
1653 			}
1654 			lastch = *ptr;
1655 		}
1656 	}
1657 
1658 	ftp->data = data = data_close(ftp, data);
1659 
1660 	php_stream_rewind(tmpstream);
1661 
1662 	ret = safe_emalloc((lines + 1), sizeof(char**), size * sizeof(char*));
1663 
1664 	entry = ret;
1665 	text = (char*) (ret + lines + 1);
1666 	*entry = text;
1667 	lastch = 0;
1668 	while ((ch = php_stream_getc(tmpstream)) != EOF) {
1669 		if (ch == '\n' && lastch == '\r') {
1670 			*(text - 1) = 0;
1671 			*++entry = text;
1672 		} else {
1673 			*text++ = ch;
1674 		}
1675 		lastch = ch;
1676 	}
1677 	*entry = NULL;
1678 
1679 	php_stream_close(tmpstream);
1680 
1681 	if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
1682 		efree(ret);
1683 		return NULL;
1684 	}
1685 
1686 	return ret;
1687 bail:
1688 	ftp->data = data_close(ftp, data);
1689 	php_stream_close(tmpstream);
1690 	if (ret)
1691 		efree(ret);
1692 	return NULL;
1693 }
1694 /* }}} */
1695 
1696 /* {{{ ftp_nb_get
1697  */
1698 int
ftp_nb_get(ftpbuf_t * ftp,php_stream * outstream,const char * path,ftptype_t type,int resumepos TSRMLS_DC)1699 ftp_nb_get(ftpbuf_t *ftp, php_stream *outstream, const char *path, ftptype_t type, int resumepos TSRMLS_DC)
1700 {
1701 	databuf_t		*data = NULL;
1702 	char			arg[11];
1703 
1704 	if (ftp == NULL) {
1705 		return PHP_FTP_FAILED;
1706 	}
1707 
1708 	if (!ftp_type(ftp, type)) {
1709 		goto bail;
1710 	}
1711 
1712 	if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) {
1713 		goto bail;
1714 	}
1715 
1716 	if (resumepos>0) {
1717 		/* We are working on an architecture that supports 64-bit integers
1718 		 * since php is 32 bit by design, we bail out with warning
1719 		 */
1720 		if (resumepos > 2147483647) {
1721 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "PHP cannot handle files greater than 2147483648 bytes.");
1722 			goto bail;
1723 		}
1724 		snprintf(arg, sizeof(arg), "%u", resumepos);
1725 		if (!ftp_putcmd(ftp, "REST", arg)) {
1726 			goto bail;
1727 		}
1728 		if (!ftp_getresp(ftp) || (ftp->resp != 350)) {
1729 			goto bail;
1730 		}
1731 	}
1732 
1733 	if (!ftp_putcmd(ftp, "RETR", path)) {
1734 		goto bail;
1735 	}
1736 	if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
1737 		goto bail;
1738 	}
1739 
1740 	if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) {
1741 		goto bail;
1742 	}
1743 
1744 	ftp->data = data;
1745 	ftp->stream = outstream;
1746 	ftp->lastch = 0;
1747 	ftp->nb = 1;
1748 
1749 	return (ftp_nb_continue_read(ftp TSRMLS_CC));
1750 
1751 bail:
1752 	ftp->data = data_close(ftp, data);
1753 	return PHP_FTP_FAILED;
1754 }
1755 /* }}} */
1756 
1757 /* {{{ ftp_nb_continue_read
1758  */
1759 int
ftp_nb_continue_read(ftpbuf_t * ftp TSRMLS_DC)1760 ftp_nb_continue_read(ftpbuf_t *ftp TSRMLS_DC)
1761 {
1762 	databuf_t	*data = NULL;
1763 	char		*ptr;
1764 	int		lastch;
1765 	size_t		rcvd;
1766 	ftptype_t	type;
1767 
1768 	data = ftp->data;
1769 
1770 	/* check if there is already more data */
1771 	if (!data_available(ftp, data->fd)) {
1772 		return PHP_FTP_MOREDATA;
1773 	}
1774 
1775 	type = ftp->type;
1776 
1777 	lastch = ftp->lastch;
1778 	if ((rcvd = my_recv(ftp, data->fd, data->buf, FTP_BUFSIZE))) {
1779 		if (rcvd == -1) {
1780 			goto bail;
1781 		}
1782 
1783 		if (type == FTPTYPE_ASCII) {
1784 			for (ptr = data->buf; rcvd; rcvd--, ptr++) {
1785 				if (lastch == '\r' && *ptr != '\n') {
1786 					php_stream_putc(ftp->stream, '\r');
1787 				}
1788 				if (*ptr != '\r') {
1789 					php_stream_putc(ftp->stream, *ptr);
1790 				}
1791 				lastch = *ptr;
1792 			}
1793 		} else if (rcvd != php_stream_write(ftp->stream, data->buf, rcvd)) {
1794 			goto bail;
1795 		}
1796 
1797 		ftp->lastch = lastch;
1798 		return PHP_FTP_MOREDATA;
1799 	}
1800 
1801 	if (type == FTPTYPE_ASCII && lastch == '\r') {
1802 		php_stream_putc(ftp->stream, '\r');
1803 	}
1804 
1805 	ftp->data = data = data_close(ftp, data);
1806 
1807 	if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
1808 		goto bail;
1809 	}
1810 
1811 	ftp->nb = 0;
1812 	return PHP_FTP_FINISHED;
1813 bail:
1814 	ftp->nb = 0;
1815 	ftp->data = data_close(ftp, data);
1816 	return PHP_FTP_FAILED;
1817 }
1818 /* }}} */
1819 
1820 /* {{{ ftp_nb_put
1821  */
1822 int
ftp_nb_put(ftpbuf_t * ftp,const char * path,php_stream * instream,ftptype_t type,int startpos TSRMLS_DC)1823 ftp_nb_put(ftpbuf_t *ftp, const char *path, php_stream *instream, ftptype_t type, int startpos TSRMLS_DC)
1824 {
1825 	databuf_t		*data = NULL;
1826 	char			arg[11];
1827 
1828 	if (ftp == NULL) {
1829 		return 0;
1830 	}
1831 	if (!ftp_type(ftp, type)) {
1832 		goto bail;
1833 	}
1834 	if ((data = ftp_getdata(ftp TSRMLS_CC)) == NULL) {
1835 		goto bail;
1836 	}
1837 	if (startpos > 0) {
1838 		if (startpos > 2147483647) {
1839 			php_error_docref(NULL TSRMLS_CC, E_WARNING, "PHP cannot handle files with a size greater than 2147483647 bytes.");
1840 			goto bail;
1841 		}
1842 		snprintf(arg, sizeof(arg), "%u", startpos);
1843 		if (!ftp_putcmd(ftp, "REST", arg)) {
1844 			goto bail;
1845 		}
1846 		if (!ftp_getresp(ftp) || (ftp->resp != 350)) {
1847 			goto bail;
1848 		}
1849 	}
1850 
1851 	if (!ftp_putcmd(ftp, "STOR", path)) {
1852 		goto bail;
1853 	}
1854 	if (!ftp_getresp(ftp) || (ftp->resp != 150 && ftp->resp != 125)) {
1855 		goto bail;
1856 	}
1857 	if ((data = data_accept(data, ftp TSRMLS_CC)) == NULL) {
1858 		goto bail;
1859 	}
1860 	ftp->data = data;
1861 	ftp->stream = instream;
1862 	ftp->lastch = 0;
1863 	ftp->nb = 1;
1864 
1865 	return (ftp_nb_continue_write(ftp TSRMLS_CC));
1866 
1867 bail:
1868 	ftp->data = data_close(ftp, data);
1869 	return PHP_FTP_FAILED;
1870 }
1871 /* }}} */
1872 
1873 
1874 /* {{{ ftp_nb_continue_write
1875  */
1876 int
ftp_nb_continue_write(ftpbuf_t * ftp TSRMLS_DC)1877 ftp_nb_continue_write(ftpbuf_t *ftp TSRMLS_DC)
1878 {
1879 	int			size;
1880 	char			*ptr;
1881 	int 			ch;
1882 
1883 	/* check if we can write more data */
1884 	if (!data_writeable(ftp, ftp->data->fd)) {
1885 		return PHP_FTP_MOREDATA;
1886 	}
1887 
1888 	size = 0;
1889 	ptr = ftp->data->buf;
1890 	while (!php_stream_eof(ftp->stream) && (ch = php_stream_getc(ftp->stream)) != EOF) {
1891 
1892 		if (ch == '\n' && ftp->type == FTPTYPE_ASCII) {
1893 			*ptr++ = '\r';
1894 			size++;
1895 		}
1896 
1897 		*ptr++ = ch;
1898 		size++;
1899 
1900 		/* flush if necessary */
1901 		if (FTP_BUFSIZE - size < 2) {
1902 			if (my_send(ftp, ftp->data->fd, ftp->data->buf, size) != size) {
1903 				goto bail;
1904 			}
1905 			return PHP_FTP_MOREDATA;
1906 		}
1907 	}
1908 
1909 	if (size && my_send(ftp, ftp->data->fd, ftp->data->buf, size) != size) {
1910 		goto bail;
1911 	}
1912 	ftp->data = data_close(ftp, ftp->data);
1913 
1914 	if (!ftp_getresp(ftp) || (ftp->resp != 226 && ftp->resp != 250)) {
1915 		goto bail;
1916 	}
1917 	ftp->nb = 0;
1918 	return PHP_FTP_FINISHED;
1919 bail:
1920 	ftp->data = data_close(ftp, ftp->data);
1921 	ftp->nb = 0;
1922 	return PHP_FTP_FAILED;
1923 }
1924 /* }}} */
1925 
1926 #endif /* HAVE_FTP */
1927 
1928 /*
1929  * Local variables:
1930  * tab-width: 4
1931  * c-basic-offset: 4
1932  * End:
1933  * vim600: sw=4 ts=4 fdm=marker
1934  * vim<600: sw=4 ts=4
1935  */
1936