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