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