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