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