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