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