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