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