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