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