1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
7 *
8 * Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
9 *
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at https://curl.se/docs/copyright.html.
13 *
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
17 *
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
20 *
21 * SPDX-License-Identifier: curl
22 *
23 ***************************************************************************/
24
25 #include "curl_setup.h"
26
27 #ifndef CURL_DISABLE_FTP
28
29 #ifdef HAVE_NETINET_IN_H
30 #include <netinet/in.h>
31 #endif
32 #ifdef HAVE_ARPA_INET_H
33 #include <arpa/inet.h>
34 #endif
35 #ifdef HAVE_NETDB_H
36 #include <netdb.h>
37 #endif
38 #ifdef __VMS
39 #include <in.h>
40 #include <inet.h>
41 #endif
42
43 #include <curl/curl.h>
44 #include "urldata.h"
45 #include "sendf.h"
46 #include "if2ip.h"
47 #include "hostip.h"
48 #include "progress.h"
49 #include "transfer.h"
50 #include "escape.h"
51 #include "http.h" /* for HTTP proxy tunnel stuff */
52 #include "ftp.h"
53 #include "fileinfo.h"
54 #include "ftplistparser.h"
55 #include "curl_range.h"
56 #include "curl_krb5.h"
57 #include "strtoofft.h"
58 #include "strcase.h"
59 #include "vtls/vtls.h"
60 #include "cfilters.h"
61 #include "cf-socket.h"
62 #include "connect.h"
63 #include "strerror.h"
64 #include "inet_ntop.h"
65 #include "inet_pton.h"
66 #include "select.h"
67 #include "parsedate.h" /* for the week day and month names */
68 #include "sockaddr.h" /* required for Curl_sockaddr_storage */
69 #include "multiif.h"
70 #include "url.h"
71 #include "speedcheck.h"
72 #include "warnless.h"
73 #include "http_proxy.h"
74 #include "socks.h"
75 #include "strdup.h"
76 /* The last 3 #include files should be in this order */
77 #include "curl_printf.h"
78 #include "curl_memory.h"
79 #include "memdebug.h"
80
81 #ifndef NI_MAXHOST
82 #define NI_MAXHOST 1025
83 #endif
84 #ifndef INET_ADDRSTRLEN
85 #define INET_ADDRSTRLEN 16
86 #endif
87
88 /* macro to check for a three-digit ftp status code at the start of the
89 given string */
90 #define STATUSCODE(line) (ISDIGIT(line[0]) && ISDIGIT(line[1]) && \
91 ISDIGIT(line[2]))
92
93 /* macro to check for the last line in an FTP server response */
94 #define LASTLINE(line) (STATUSCODE(line) && (' ' == line[3]))
95
96 #ifdef CURL_DISABLE_VERBOSE_STRINGS
97 #define ftp_pasv_verbose(a,b,c,d) Curl_nop_stmt
98 #endif
99
100 /* Local API functions */
101 #ifndef DEBUGBUILD
102 static void _ftp_state(struct Curl_easy *data,
103 ftpstate newstate);
104 #define ftp_state(x,y) _ftp_state(x,y)
105 #else
106 static void _ftp_state(struct Curl_easy *data,
107 ftpstate newstate,
108 int lineno);
109 #define ftp_state(x,y) _ftp_state(x,y,__LINE__)
110 #endif
111
112 static CURLcode ftp_sendquote(struct Curl_easy *data,
113 struct connectdata *conn,
114 struct curl_slist *quote);
115 static CURLcode ftp_quit(struct Curl_easy *data, struct connectdata *conn);
116 static CURLcode ftp_parse_url_path(struct Curl_easy *data);
117 static CURLcode ftp_regular_transfer(struct Curl_easy *data, bool *done);
118 #ifndef CURL_DISABLE_VERBOSE_STRINGS
119 static void ftp_pasv_verbose(struct Curl_easy *data,
120 struct Curl_addrinfo *ai,
121 char *newhost, /* ascii version */
122 int port);
123 #endif
124 static CURLcode ftp_state_prepare_transfer(struct Curl_easy *data);
125 static CURLcode ftp_state_mdtm(struct Curl_easy *data);
126 static CURLcode ftp_state_quote(struct Curl_easy *data,
127 bool init, ftpstate instate);
128 static CURLcode ftp_nb_type(struct Curl_easy *data,
129 struct connectdata *conn,
130 bool ascii, ftpstate newstate);
131 static int ftp_need_type(struct connectdata *conn,
132 bool ascii);
133 static CURLcode ftp_do(struct Curl_easy *data, bool *done);
134 static CURLcode ftp_done(struct Curl_easy *data,
135 CURLcode, bool premature);
136 static CURLcode ftp_connect(struct Curl_easy *data, bool *done);
137 static CURLcode ftp_disconnect(struct Curl_easy *data,
138 struct connectdata *conn, bool dead_connection);
139 static CURLcode ftp_do_more(struct Curl_easy *data, int *completed);
140 static CURLcode ftp_multi_statemach(struct Curl_easy *data, bool *done);
141 static int ftp_getsock(struct Curl_easy *data, struct connectdata *conn,
142 curl_socket_t *socks);
143 static int ftp_domore_getsock(struct Curl_easy *data,
144 struct connectdata *conn, curl_socket_t *socks);
145 static CURLcode ftp_doing(struct Curl_easy *data,
146 bool *dophase_done);
147 static CURLcode ftp_setup_connection(struct Curl_easy *data,
148 struct connectdata *conn);
149 static CURLcode init_wc_data(struct Curl_easy *data);
150 static CURLcode wc_statemach(struct Curl_easy *data);
151 static void wc_data_dtor(void *ptr);
152 static CURLcode ftp_state_retr(struct Curl_easy *data, curl_off_t filesize);
153 static CURLcode ftp_readresp(struct Curl_easy *data,
154 int sockindex,
155 struct pingpong *pp,
156 int *ftpcode,
157 size_t *size);
158 static CURLcode ftp_dophase_done(struct Curl_easy *data,
159 bool connected);
160
161 /*
162 * FTP protocol handler.
163 */
164
165 const struct Curl_handler Curl_handler_ftp = {
166 "FTP", /* scheme */
167 ftp_setup_connection, /* setup_connection */
168 ftp_do, /* do_it */
169 ftp_done, /* done */
170 ftp_do_more, /* do_more */
171 ftp_connect, /* connect_it */
172 ftp_multi_statemach, /* connecting */
173 ftp_doing, /* doing */
174 ftp_getsock, /* proto_getsock */
175 ftp_getsock, /* doing_getsock */
176 ftp_domore_getsock, /* domore_getsock */
177 ZERO_NULL, /* perform_getsock */
178 ftp_disconnect, /* disconnect */
179 ZERO_NULL, /* write_resp */
180 ZERO_NULL, /* write_resp_hd */
181 ZERO_NULL, /* connection_check */
182 ZERO_NULL, /* attach connection */
183 PORT_FTP, /* defport */
184 CURLPROTO_FTP, /* protocol */
185 CURLPROTO_FTP, /* family */
186 PROTOPT_DUAL | PROTOPT_CLOSEACTION | PROTOPT_NEEDSPWD |
187 PROTOPT_NOURLQUERY | PROTOPT_PROXY_AS_HTTP |
188 PROTOPT_WILDCARD /* flags */
189 };
190
191
192 #ifdef USE_SSL
193 /*
194 * FTPS protocol handler.
195 */
196
197 const struct Curl_handler Curl_handler_ftps = {
198 "FTPS", /* scheme */
199 ftp_setup_connection, /* setup_connection */
200 ftp_do, /* do_it */
201 ftp_done, /* done */
202 ftp_do_more, /* do_more */
203 ftp_connect, /* connect_it */
204 ftp_multi_statemach, /* connecting */
205 ftp_doing, /* doing */
206 ftp_getsock, /* proto_getsock */
207 ftp_getsock, /* doing_getsock */
208 ftp_domore_getsock, /* domore_getsock */
209 ZERO_NULL, /* perform_getsock */
210 ftp_disconnect, /* disconnect */
211 ZERO_NULL, /* write_resp */
212 ZERO_NULL, /* write_resp_hd */
213 ZERO_NULL, /* connection_check */
214 ZERO_NULL, /* attach connection */
215 PORT_FTPS, /* defport */
216 CURLPROTO_FTPS, /* protocol */
217 CURLPROTO_FTP, /* family */
218 PROTOPT_SSL | PROTOPT_DUAL | PROTOPT_CLOSEACTION |
219 PROTOPT_NEEDSPWD | PROTOPT_NOURLQUERY | PROTOPT_WILDCARD /* flags */
220 };
221 #endif
222
close_secondarysocket(struct Curl_easy * data,struct connectdata * conn)223 static void close_secondarysocket(struct Curl_easy *data,
224 struct connectdata *conn)
225 {
226 Curl_conn_close(data, SECONDARYSOCKET);
227 Curl_conn_cf_discard_all(data, conn, SECONDARYSOCKET);
228 }
229
230 /*
231 * NOTE: back in the old days, we added code in the FTP code that made NOBODY
232 * requests on files respond with headers passed to the client/stdout that
233 * looked like HTTP ones.
234 *
235 * This approach is not very elegant, it causes confusion and is error-prone.
236 * It is subject for removal at the next (or at least a future) soname bump.
237 * Until then you can test the effects of the removal by undefining the
238 * following define named CURL_FTP_HTTPSTYLE_HEAD.
239 */
240 #define CURL_FTP_HTTPSTYLE_HEAD 1
241
freedirs(struct ftp_conn * ftpc)242 static void freedirs(struct ftp_conn *ftpc)
243 {
244 if(ftpc->dirs) {
245 int i;
246 for(i = 0; i < ftpc->dirdepth; i++) {
247 free(ftpc->dirs[i]);
248 ftpc->dirs[i] = NULL;
249 }
250 free(ftpc->dirs);
251 ftpc->dirs = NULL;
252 ftpc->dirdepth = 0;
253 }
254 Curl_safefree(ftpc->file);
255
256 /* no longer of any use */
257 Curl_safefree(ftpc->newhost);
258 }
259
260 #ifdef CURL_DO_LINEEND_CONV
261 /***********************************************************************
262 *
263 * Lineend Conversions
264 * On ASCII transfers, e.g. directory listings, we might get lines
265 * ending in '\r\n' and we prefer just '\n'.
266 * We might also get a lonely '\r' which we convert into a '\n'.
267 */
268 struct ftp_cw_lc_ctx {
269 struct Curl_cwriter super;
270 bool newline_pending;
271 };
272
ftp_cw_lc_write(struct Curl_easy * data,struct Curl_cwriter * writer,int type,const char * buf,size_t blen)273 static CURLcode ftp_cw_lc_write(struct Curl_easy *data,
274 struct Curl_cwriter *writer, int type,
275 const char *buf, size_t blen)
276 {
277 static const char nl = '\n';
278 struct ftp_cw_lc_ctx *ctx = writer->ctx;
279
280 if(!(type & CLIENTWRITE_BODY) ||
281 data->conn->proto.ftpc.transfertype != 'A')
282 return Curl_cwriter_write(data, writer->next, type, buf, blen);
283
284 /* ASCII mode BODY data, convert lineends */
285 while(blen) {
286 /* do not pass EOS when writing parts */
287 int chunk_type = (type & ~CLIENTWRITE_EOS);
288 const char *cp;
289 size_t chunk_len;
290 CURLcode result;
291
292 if(ctx->newline_pending) {
293 if(buf[0] != '\n') {
294 /* previous chunk ended in '\r' and we do not see a '\n' in this one,
295 * need to write a newline. */
296 result = Curl_cwriter_write(data, writer->next, chunk_type, &nl, 1);
297 if(result)
298 return result;
299 }
300 /* either we just wrote the newline or it is part of the next
301 * chunk of bytes we write. */
302 data->state.crlf_conversions++;
303 ctx->newline_pending = FALSE;
304 }
305
306 cp = memchr(buf, '\r', blen);
307 if(!cp)
308 break;
309
310 /* write the bytes before the '\r', excluding the '\r' */
311 chunk_len = cp - buf;
312 if(chunk_len) {
313 result = Curl_cwriter_write(data, writer->next, chunk_type,
314 buf, chunk_len);
315 if(result)
316 return result;
317 }
318 /* skip the '\r', we now have a newline pending */
319 buf = cp + 1;
320 blen = blen - chunk_len - 1;
321 ctx->newline_pending = TRUE;
322 }
323
324 /* Any remaining data does not contain a '\r' */
325 if(blen) {
326 DEBUGASSERT(!ctx->newline_pending);
327 return Curl_cwriter_write(data, writer->next, type, buf, blen);
328 }
329 else if(type & CLIENTWRITE_EOS) {
330 /* EndOfStream, if we have a trailing cr, now is the time to write it */
331 if(ctx->newline_pending) {
332 ctx->newline_pending = FALSE;
333 data->state.crlf_conversions++;
334 return Curl_cwriter_write(data, writer->next, type, &nl, 1);
335 }
336 /* Always pass on the EOS type indicator */
337 return Curl_cwriter_write(data, writer->next, type, buf, 0);
338 }
339 return CURLE_OK;
340 }
341
342 static const struct Curl_cwtype ftp_cw_lc = {
343 "ftp-lineconv",
344 NULL,
345 Curl_cwriter_def_init,
346 ftp_cw_lc_write,
347 Curl_cwriter_def_close,
348 sizeof(struct ftp_cw_lc_ctx)
349 };
350
351 #endif /* CURL_DO_LINEEND_CONV */
352 /***********************************************************************
353 *
354 * AcceptServerConnect()
355 *
356 * After connection request is received from the server this function is
357 * called to accept the connection and close the listening socket
358 *
359 */
AcceptServerConnect(struct Curl_easy * data)360 static CURLcode AcceptServerConnect(struct Curl_easy *data)
361 {
362 struct connectdata *conn = data->conn;
363 curl_socket_t sock = conn->sock[SECONDARYSOCKET];
364 curl_socket_t s = CURL_SOCKET_BAD;
365 #ifdef USE_IPV6
366 struct Curl_sockaddr_storage add;
367 #else
368 struct sockaddr_in add;
369 #endif
370 curl_socklen_t size = (curl_socklen_t) sizeof(add);
371 CURLcode result;
372
373 if(0 == getsockname(sock, (struct sockaddr *) &add, &size)) {
374 size = sizeof(add);
375
376 s = accept(sock, (struct sockaddr *) &add, &size);
377 }
378
379 if(CURL_SOCKET_BAD == s) {
380 failf(data, "Error accept()ing server connect");
381 return CURLE_FTP_PORT_FAILED;
382 }
383 infof(data, "Connection accepted from server");
384 /* when this happens within the DO state it is important that we mark us as
385 not needing DO_MORE anymore */
386 conn->bits.do_more = FALSE;
387
388 (void)curlx_nonblock(s, TRUE); /* enable non-blocking */
389 /* Replace any filter on SECONDARY with one listening on this socket */
390 result = Curl_conn_tcp_accepted_set(data, conn, SECONDARYSOCKET, &s);
391 if(result) {
392 sclose(s);
393 return result;
394 }
395
396 if(data->set.fsockopt) {
397 int error = 0;
398
399 /* activate callback for setting socket options */
400 Curl_set_in_callback(data, true);
401 error = data->set.fsockopt(data->set.sockopt_client,
402 s,
403 CURLSOCKTYPE_ACCEPT);
404 Curl_set_in_callback(data, false);
405
406 if(error) {
407 close_secondarysocket(data, conn);
408 return CURLE_ABORTED_BY_CALLBACK;
409 }
410 }
411
412 return CURLE_OK;
413
414 }
415
416 /*
417 * ftp_timeleft_accept() returns the amount of milliseconds left allowed for
418 * waiting server to connect. If the value is negative, the timeout time has
419 * already elapsed.
420 *
421 * The start time is stored in progress.t_acceptdata - as set with
422 * Curl_pgrsTime(..., TIMER_STARTACCEPT);
423 *
424 */
ftp_timeleft_accept(struct Curl_easy * data)425 static timediff_t ftp_timeleft_accept(struct Curl_easy *data)
426 {
427 timediff_t timeout_ms = DEFAULT_ACCEPT_TIMEOUT;
428 timediff_t other;
429 struct curltime now;
430
431 if(data->set.accepttimeout > 0)
432 timeout_ms = data->set.accepttimeout;
433
434 now = Curl_now();
435
436 /* check if the generic timeout possibly is set shorter */
437 other = Curl_timeleft(data, &now, FALSE);
438 if(other && (other < timeout_ms))
439 /* note that this also works fine for when other happens to be negative
440 due to it already having elapsed */
441 timeout_ms = other;
442 else {
443 /* subtract elapsed time */
444 timeout_ms -= Curl_timediff(now, data->progress.t_acceptdata);
445 if(!timeout_ms)
446 /* avoid returning 0 as that means no timeout! */
447 return -1;
448 }
449
450 return timeout_ms;
451 }
452
453
454 /***********************************************************************
455 *
456 * ReceivedServerConnect()
457 *
458 * After allowing server to connect to us from data port, this function
459 * checks both data connection for connection establishment and ctrl
460 * connection for a negative response regarding a failure in connecting
461 *
462 */
ReceivedServerConnect(struct Curl_easy * data,bool * received)463 static CURLcode ReceivedServerConnect(struct Curl_easy *data, bool *received)
464 {
465 struct connectdata *conn = data->conn;
466 curl_socket_t ctrl_sock = conn->sock[FIRSTSOCKET];
467 curl_socket_t data_sock = conn->sock[SECONDARYSOCKET];
468 struct ftp_conn *ftpc = &conn->proto.ftpc;
469 struct pingpong *pp = &ftpc->pp;
470 int socketstate = 0;
471 timediff_t timeout_ms;
472 ssize_t nread;
473 int ftpcode;
474 bool response = FALSE;
475
476 *received = FALSE;
477
478 timeout_ms = ftp_timeleft_accept(data);
479 infof(data, "Checking for server connect");
480 if(timeout_ms < 0) {
481 /* if a timeout was already reached, bail out */
482 failf(data, "Accept timeout occurred while waiting server connect");
483 return CURLE_FTP_ACCEPT_TIMEOUT;
484 }
485
486 /* First check whether there is a cached response from server */
487 if(Curl_dyn_len(&pp->recvbuf) && (*Curl_dyn_ptr(&pp->recvbuf) > '3')) {
488 /* Data connection could not be established, let's return */
489 infof(data, "There is negative response in cache while serv connect");
490 (void)Curl_GetFTPResponse(data, &nread, &ftpcode);
491 return CURLE_FTP_ACCEPT_FAILED;
492 }
493
494 if(pp->overflow)
495 /* there is pending control data still in the buffer to read */
496 response = TRUE;
497 else
498 socketstate = Curl_socket_check(ctrl_sock, data_sock, CURL_SOCKET_BAD, 0);
499
500 /* see if the connection request is already here */
501 switch(socketstate) {
502 case -1: /* error */
503 /* let's die here */
504 failf(data, "Error while waiting for server connect");
505 return CURLE_FTP_ACCEPT_FAILED;
506 case 0: /* Server connect is not received yet */
507 break; /* loop */
508 default:
509 if(socketstate & CURL_CSELECT_IN2) {
510 infof(data, "Ready to accept data connection from server");
511 *received = TRUE;
512 }
513 else if(socketstate & CURL_CSELECT_IN)
514 response = TRUE;
515 break;
516 }
517 if(response) {
518 infof(data, "Ctrl conn has data while waiting for data conn");
519 if(pp->overflow > 3) {
520 char *r = Curl_dyn_ptr(&pp->recvbuf);
521
522 DEBUGASSERT((pp->overflow + pp->nfinal) <=
523 Curl_dyn_len(&pp->recvbuf));
524 /* move over the most recently handled response line */
525 r += pp->nfinal;
526
527 if(LASTLINE(r)) {
528 int status = curlx_sltosi(strtol(r, NULL, 10));
529 if(status == 226) {
530 /* funny timing situation where we get the final message on the
531 control connection before traffic on the data connection has been
532 noticed. Leave the 226 in there and use this as a trigger to read
533 the data socket. */
534 infof(data, "Got 226 before data activity");
535 *received = TRUE;
536 return CURLE_OK;
537 }
538 }
539 }
540
541 (void)Curl_GetFTPResponse(data, &nread, &ftpcode);
542
543 infof(data, "FTP code: %03d", ftpcode);
544
545 if(ftpcode/100 > 3)
546 return CURLE_FTP_ACCEPT_FAILED;
547
548 return CURLE_WEIRD_SERVER_REPLY;
549 }
550
551 return CURLE_OK;
552 }
553
554
555 /***********************************************************************
556 *
557 * InitiateTransfer()
558 *
559 * After connection from server is accepted this function is called to
560 * setup transfer parameters and initiate the data transfer.
561 *
562 */
InitiateTransfer(struct Curl_easy * data)563 static CURLcode InitiateTransfer(struct Curl_easy *data)
564 {
565 CURLcode result = CURLE_OK;
566 struct connectdata *conn = data->conn;
567 bool connected;
568
569 DEBUGF(infof(data, "ftp InitiateTransfer()"));
570 if(conn->bits.ftp_use_data_ssl && data->set.ftp_use_port &&
571 !Curl_conn_is_ssl(conn, SECONDARYSOCKET)) {
572 result = Curl_ssl_cfilter_add(data, conn, SECONDARYSOCKET);
573 if(result)
574 return result;
575 }
576 result = Curl_conn_connect(data, SECONDARYSOCKET, TRUE, &connected);
577 if(result || !connected)
578 return result;
579
580 if(conn->proto.ftpc.state_saved == FTP_STOR) {
581 /* When we know we're uploading a specified file, we can get the file
582 size prior to the actual upload. */
583 Curl_pgrsSetUploadSize(data, data->state.infilesize);
584
585 /* set the SO_SNDBUF for the secondary socket for those who need it */
586 Curl_sndbufset(conn->sock[SECONDARYSOCKET]);
587
588 Curl_xfer_setup(data, -1, -1, FALSE, SECONDARYSOCKET);
589 }
590 else {
591 /* FTP download: */
592 Curl_xfer_setup(data, SECONDARYSOCKET,
593 conn->proto.ftpc.retr_size_saved, FALSE, -1);
594 }
595
596 conn->proto.ftpc.pp.pending_resp = TRUE; /* expect server response */
597 ftp_state(data, FTP_STOP);
598
599 return CURLE_OK;
600 }
601
602 /***********************************************************************
603 *
604 * AllowServerConnect()
605 *
606 * When we've issue the PORT command, we have told the server to connect to
607 * us. This function checks whether data connection is established if so it is
608 * accepted.
609 *
610 */
AllowServerConnect(struct Curl_easy * data,bool * connected)611 static CURLcode AllowServerConnect(struct Curl_easy *data, bool *connected)
612 {
613 timediff_t timeout_ms;
614 CURLcode result = CURLE_OK;
615
616 *connected = FALSE;
617 infof(data, "Preparing for accepting server on data port");
618
619 /* Save the time we start accepting server connect */
620 Curl_pgrsTime(data, TIMER_STARTACCEPT);
621
622 timeout_ms = ftp_timeleft_accept(data);
623 if(timeout_ms < 0) {
624 /* if a timeout was already reached, bail out */
625 failf(data, "Accept timeout occurred while waiting server connect");
626 result = CURLE_FTP_ACCEPT_TIMEOUT;
627 goto out;
628 }
629
630 /* see if the connection request is already here */
631 result = ReceivedServerConnect(data, connected);
632 if(result)
633 goto out;
634
635 if(*connected) {
636 result = AcceptServerConnect(data);
637 if(result)
638 goto out;
639
640 result = InitiateTransfer(data);
641 if(result)
642 goto out;
643 }
644 else {
645 /* Add timeout to multi handle and break out of the loop */
646 Curl_expire(data, data->set.accepttimeout ?
647 data->set.accepttimeout: DEFAULT_ACCEPT_TIMEOUT,
648 EXPIRE_FTP_ACCEPT);
649 }
650
651 out:
652 DEBUGF(infof(data, "ftp AllowServerConnect() -> %d", result));
653 return result;
654 }
655
ftp_endofresp(struct Curl_easy * data,struct connectdata * conn,char * line,size_t len,int * code)656 static bool ftp_endofresp(struct Curl_easy *data, struct connectdata *conn,
657 char *line, size_t len, int *code)
658 {
659 (void)data;
660 (void)conn;
661
662 if((len > 3) && LASTLINE(line)) {
663 *code = curlx_sltosi(strtol(line, NULL, 10));
664 return TRUE;
665 }
666
667 return FALSE;
668 }
669
ftp_readresp(struct Curl_easy * data,int sockindex,struct pingpong * pp,int * ftpcode,size_t * size)670 static CURLcode ftp_readresp(struct Curl_easy *data,
671 int sockindex,
672 struct pingpong *pp,
673 int *ftpcode, /* return the ftp-code if done */
674 size_t *size) /* size of the response */
675 {
676 int code;
677 CURLcode result = Curl_pp_readresp(data, sockindex, pp, &code, size);
678
679 #ifdef HAVE_GSSAPI
680 {
681 struct connectdata *conn = data->conn;
682 char * const buf = Curl_dyn_ptr(&data->conn->proto.ftpc.pp.recvbuf);
683
684 /* handle the security-oriented responses 6xx ***/
685 switch(code) {
686 case 631:
687 code = Curl_sec_read_msg(data, conn, buf, PROT_SAFE);
688 break;
689 case 632:
690 code = Curl_sec_read_msg(data, conn, buf, PROT_PRIVATE);
691 break;
692 case 633:
693 code = Curl_sec_read_msg(data, conn, buf, PROT_CONFIDENTIAL);
694 break;
695 default:
696 /* normal ftp stuff we pass through! */
697 break;
698 }
699 }
700 #endif
701
702 /* store the latest code for later retrieval */
703 data->info.httpcode = code;
704
705 if(ftpcode)
706 *ftpcode = code;
707
708 if(421 == code) {
709 /* 421 means "Service not available, closing control connection." and FTP
710 * servers use it to signal that idle session timeout has been exceeded.
711 * If we ignored the response, it could end up hanging in some cases.
712 *
713 * This response code can come at any point so having it treated
714 * generically is a good idea.
715 */
716 infof(data, "We got a 421 - timeout");
717 ftp_state(data, FTP_STOP);
718 return CURLE_OPERATION_TIMEDOUT;
719 }
720
721 return result;
722 }
723
724 /* --- parse FTP server responses --- */
725
726 /*
727 * Curl_GetFTPResponse() is a BLOCKING function to read the full response
728 * from a server after a command.
729 *
730 */
731
Curl_GetFTPResponse(struct Curl_easy * data,ssize_t * nreadp,int * ftpcode)732 CURLcode Curl_GetFTPResponse(struct Curl_easy *data,
733 ssize_t *nreadp, /* return number of bytes read */
734 int *ftpcode) /* return the ftp-code */
735 {
736 /*
737 * We cannot read just one byte per read() and then go back to select() as
738 * the OpenSSL read() doesn't grok that properly.
739 *
740 * Alas, read as much as possible, split up into lines, use the ending
741 * line in a response or continue reading. */
742
743 struct connectdata *conn = data->conn;
744 curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
745 CURLcode result = CURLE_OK;
746 struct ftp_conn *ftpc = &conn->proto.ftpc;
747 struct pingpong *pp = &ftpc->pp;
748 size_t nread;
749 int cache_skip = 0;
750 int value_to_be_ignored = 0;
751
752 if(ftpcode)
753 *ftpcode = 0; /* 0 for errors */
754 else
755 /* make the pointer point to something for the rest of this function */
756 ftpcode = &value_to_be_ignored;
757
758 *nreadp = 0;
759
760 while(!*ftpcode && !result) {
761 /* check and reset timeout value every lap */
762 timediff_t timeout = Curl_pp_state_timeout(data, pp, FALSE);
763 timediff_t interval_ms;
764
765 if(timeout <= 0) {
766 failf(data, "FTP response timeout");
767 return CURLE_OPERATION_TIMEDOUT; /* already too little time */
768 }
769
770 interval_ms = 1000; /* use 1 second timeout intervals */
771 if(timeout < interval_ms)
772 interval_ms = timeout;
773
774 /*
775 * Since this function is blocking, we need to wait here for input on the
776 * connection and only then we call the response reading function. We do
777 * timeout at least every second to make the timeout check run.
778 *
779 * A caution here is that the ftp_readresp() function has a cache that may
780 * contain pieces of a response from the previous invoke and we need to
781 * make sure we don't just wait for input while there is unhandled data in
782 * that cache. But also, if the cache is there, we call ftp_readresp() and
783 * the cache wasn't good enough to continue we must not just busy-loop
784 * around this function.
785 *
786 */
787
788 if(Curl_dyn_len(&pp->recvbuf) && (cache_skip < 2)) {
789 /*
790 * There's a cache left since before. We then skipping the wait for
791 * socket action, unless this is the same cache like the previous round
792 * as then the cache was deemed not enough to act on and we then need to
793 * wait for more data anyway.
794 */
795 }
796 else if(!Curl_conn_data_pending(data, FIRSTSOCKET)) {
797 switch(SOCKET_READABLE(sockfd, interval_ms)) {
798 case -1: /* select() error, stop reading */
799 failf(data, "FTP response aborted due to select/poll error: %d",
800 SOCKERRNO);
801 return CURLE_RECV_ERROR;
802
803 case 0: /* timeout */
804 if(Curl_pgrsUpdate(data))
805 return CURLE_ABORTED_BY_CALLBACK;
806 continue; /* just continue in our loop for the timeout duration */
807
808 default: /* for clarity */
809 break;
810 }
811 }
812 result = ftp_readresp(data, FIRSTSOCKET, pp, ftpcode, &nread);
813 if(result)
814 break;
815
816 if(!nread && Curl_dyn_len(&pp->recvbuf))
817 /* bump cache skip counter as on repeated skips we must wait for more
818 data */
819 cache_skip++;
820 else
821 /* when we got data or there is no cache left, we reset the cache skip
822 counter */
823 cache_skip = 0;
824
825 *nreadp += nread;
826
827 } /* while there's buffer left and loop is requested */
828
829 pp->pending_resp = FALSE;
830
831 return result;
832 }
833
834 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
835 /* for debug purposes */
836 static const char * const ftp_state_names[]={
837 "STOP",
838 "WAIT220",
839 "AUTH",
840 "USER",
841 "PASS",
842 "ACCT",
843 "PBSZ",
844 "PROT",
845 "CCC",
846 "PWD",
847 "SYST",
848 "NAMEFMT",
849 "QUOTE",
850 "RETR_PREQUOTE",
851 "STOR_PREQUOTE",
852 "POSTQUOTE",
853 "CWD",
854 "MKD",
855 "MDTM",
856 "TYPE",
857 "LIST_TYPE",
858 "RETR_TYPE",
859 "STOR_TYPE",
860 "SIZE",
861 "RETR_SIZE",
862 "STOR_SIZE",
863 "REST",
864 "RETR_REST",
865 "PORT",
866 "PRET",
867 "PASV",
868 "LIST",
869 "RETR",
870 "STOR",
871 "QUIT"
872 };
873 #endif
874
875 /* This is the ONLY way to change FTP state! */
_ftp_state(struct Curl_easy * data,ftpstate newstate,int lineno)876 static void _ftp_state(struct Curl_easy *data,
877 ftpstate newstate
878 #ifdef DEBUGBUILD
879 , int lineno
880 #endif
881 )
882 {
883 struct connectdata *conn = data->conn;
884 struct ftp_conn *ftpc = &conn->proto.ftpc;
885
886 #if defined(DEBUGBUILD)
887
888 #if defined(CURL_DISABLE_VERBOSE_STRINGS)
889 (void) lineno;
890 #else
891 if(ftpc->state != newstate)
892 infof(data, "FTP %p (line %d) state change from %s to %s",
893 (void *)ftpc, lineno, ftp_state_names[ftpc->state],
894 ftp_state_names[newstate]);
895 #endif
896 #endif
897
898 ftpc->state = newstate;
899 }
900
ftp_state_user(struct Curl_easy * data,struct connectdata * conn)901 static CURLcode ftp_state_user(struct Curl_easy *data,
902 struct connectdata *conn)
903 {
904 CURLcode result = Curl_pp_sendf(data,
905 &conn->proto.ftpc.pp, "USER %s",
906 conn->user?conn->user:"");
907 if(!result) {
908 struct ftp_conn *ftpc = &conn->proto.ftpc;
909 ftpc->ftp_trying_alternative = FALSE;
910 ftp_state(data, FTP_USER);
911 }
912 return result;
913 }
914
ftp_state_pwd(struct Curl_easy * data,struct connectdata * conn)915 static CURLcode ftp_state_pwd(struct Curl_easy *data,
916 struct connectdata *conn)
917 {
918 CURLcode result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", "PWD");
919 if(!result)
920 ftp_state(data, FTP_PWD);
921
922 return result;
923 }
924
925 /* For the FTP "protocol connect" and "doing" phases only */
ftp_getsock(struct Curl_easy * data,struct connectdata * conn,curl_socket_t * socks)926 static int ftp_getsock(struct Curl_easy *data,
927 struct connectdata *conn,
928 curl_socket_t *socks)
929 {
930 return Curl_pp_getsock(data, &conn->proto.ftpc.pp, socks);
931 }
932
933 /* For the FTP "DO_MORE" phase only */
ftp_domore_getsock(struct Curl_easy * data,struct connectdata * conn,curl_socket_t * socks)934 static int ftp_domore_getsock(struct Curl_easy *data,
935 struct connectdata *conn, curl_socket_t *socks)
936 {
937 struct ftp_conn *ftpc = &conn->proto.ftpc;
938 (void)data;
939
940 /* When in DO_MORE state, we could be either waiting for us to connect to a
941 * remote site, or we could wait for that site to connect to us. Or just
942 * handle ordinary commands.
943 */
944 DEBUGF(infof(data, "ftp_domore_getsock()"));
945
946 if(FTP_STOP == ftpc->state) {
947 /* if stopped and still in this state, then we're also waiting for a
948 connect on the secondary connection */
949 DEBUGASSERT(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD ||
950 (conn->cfilter[SECONDARYSOCKET] &&
951 !Curl_conn_is_connected(conn, SECONDARYSOCKET)));
952 socks[0] = conn->sock[FIRSTSOCKET];
953 /* An unconnected SECONDARY will add its socket by itself
954 * via its adjust_pollset() */
955 return GETSOCK_READSOCK(0);
956 }
957 return Curl_pp_getsock(data, &conn->proto.ftpc.pp, socks);
958 }
959
960 /* This is called after the FTP_QUOTE state is passed.
961
962 ftp_state_cwd() sends the range of CWD commands to the server to change to
963 the correct directory. It may also need to send MKD commands to create
964 missing ones, if that option is enabled.
965 */
ftp_state_cwd(struct Curl_easy * data,struct connectdata * conn)966 static CURLcode ftp_state_cwd(struct Curl_easy *data,
967 struct connectdata *conn)
968 {
969 CURLcode result = CURLE_OK;
970 struct ftp_conn *ftpc = &conn->proto.ftpc;
971
972 if(ftpc->cwddone)
973 /* already done and fine */
974 result = ftp_state_mdtm(data);
975 else {
976 /* FTPFILE_NOCWD with full path: expect ftpc->cwddone! */
977 DEBUGASSERT((data->set.ftp_filemethod != FTPFILE_NOCWD) ||
978 !(ftpc->dirdepth && ftpc->dirs[0][0] == '/'));
979
980 ftpc->count2 = 0; /* count2 counts failed CWDs */
981
982 if(conn->bits.reuse && ftpc->entrypath &&
983 /* no need to go to entrypath when we have an absolute path */
984 !(ftpc->dirdepth && ftpc->dirs[0][0] == '/')) {
985 /* This is a reused connection. Since we change directory to where the
986 transfer is taking place, we must first get back to the original dir
987 where we ended up after login: */
988 ftpc->cwdcount = 0; /* we count this as the first path, then we add one
989 for all upcoming ones in the ftp->dirs[] array */
990 result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s", ftpc->entrypath);
991 if(!result)
992 ftp_state(data, FTP_CWD);
993 }
994 else {
995 if(ftpc->dirdepth) {
996 ftpc->cwdcount = 1;
997 /* issue the first CWD, the rest is sent when the CWD responses are
998 received... */
999 result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s",
1000 ftpc->dirs[ftpc->cwdcount -1]);
1001 if(!result)
1002 ftp_state(data, FTP_CWD);
1003 }
1004 else {
1005 /* No CWD necessary */
1006 result = ftp_state_mdtm(data);
1007 }
1008 }
1009 }
1010 return result;
1011 }
1012
1013 typedef enum {
1014 EPRT,
1015 PORT,
1016 DONE
1017 } ftpport;
1018
ftp_state_use_port(struct Curl_easy * data,ftpport fcmd)1019 static CURLcode ftp_state_use_port(struct Curl_easy *data,
1020 ftpport fcmd) /* start with this */
1021 {
1022 CURLcode result = CURLE_FTP_PORT_FAILED;
1023 struct connectdata *conn = data->conn;
1024 struct ftp_conn *ftpc = &conn->proto.ftpc;
1025 curl_socket_t portsock = CURL_SOCKET_BAD;
1026 char myhost[MAX_IPADR_LEN + 1] = "";
1027
1028 struct Curl_sockaddr_storage ss;
1029 struct Curl_addrinfo *res, *ai;
1030 curl_socklen_t sslen;
1031 char hbuf[NI_MAXHOST];
1032 struct sockaddr *sa = (struct sockaddr *)&ss;
1033 struct sockaddr_in * const sa4 = (void *)sa;
1034 #ifdef USE_IPV6
1035 struct sockaddr_in6 * const sa6 = (void *)sa;
1036 #endif
1037 static const char mode[][5] = { "EPRT", "PORT" };
1038 enum resolve_t rc;
1039 int error;
1040 char *host = NULL;
1041 char *string_ftpport = data->set.str[STRING_FTPPORT];
1042 struct Curl_dns_entry *h = NULL;
1043 unsigned short port_min = 0;
1044 unsigned short port_max = 0;
1045 unsigned short port;
1046 bool possibly_non_local = TRUE;
1047 char buffer[STRERROR_LEN];
1048 char *addr = NULL;
1049 size_t addrlen = 0;
1050 char ipstr[50];
1051
1052 /* Step 1, figure out what is requested,
1053 * accepted format :
1054 * (ipv4|ipv6|domain|interface)?(:port(-range)?)?
1055 */
1056
1057 if(data->set.str[STRING_FTPPORT] &&
1058 (strlen(data->set.str[STRING_FTPPORT]) > 1)) {
1059 char *ip_end = NULL;
1060
1061 #ifdef USE_IPV6
1062 if(*string_ftpport == '[') {
1063 /* [ipv6]:port(-range) */
1064 char *ip_start = string_ftpport + 1;
1065 ip_end = strchr(ip_start, ']');
1066 if(ip_end) {
1067 addrlen = ip_end - ip_start;
1068 addr = ip_start;
1069 }
1070 }
1071 else
1072 #endif
1073 if(*string_ftpport == ':') {
1074 /* :port */
1075 ip_end = string_ftpport;
1076 }
1077 else {
1078 ip_end = strchr(string_ftpport, ':');
1079 addr = string_ftpport;
1080 if(ip_end) {
1081 /* either ipv6 or (ipv4|domain|interface):port(-range) */
1082 addrlen = ip_end - string_ftpport;
1083 #ifdef USE_IPV6
1084 if(Curl_inet_pton(AF_INET6, string_ftpport, &sa6->sin6_addr) == 1) {
1085 /* ipv6 */
1086 port_min = port_max = 0;
1087 ip_end = NULL; /* this got no port ! */
1088 }
1089 #endif
1090 }
1091 else
1092 /* ipv4|interface */
1093 addrlen = strlen(string_ftpport);
1094 }
1095
1096 /* parse the port */
1097 if(ip_end) {
1098 char *port_sep = NULL;
1099 char *port_start = strchr(ip_end, ':');
1100 if(port_start) {
1101 port_min = curlx_ultous(strtoul(port_start + 1, NULL, 10));
1102 port_sep = strchr(port_start, '-');
1103 if(port_sep) {
1104 port_max = curlx_ultous(strtoul(port_sep + 1, NULL, 10));
1105 }
1106 else
1107 port_max = port_min;
1108 }
1109 }
1110
1111 /* correct errors like:
1112 * :1234-1230
1113 * :-4711, in this case port_min is (unsigned)-1,
1114 * therefore port_min > port_max for all cases
1115 * but port_max = (unsigned)-1
1116 */
1117 if(port_min > port_max)
1118 port_min = port_max = 0;
1119
1120 if(addrlen) {
1121 DEBUGASSERT(addr);
1122 if(addrlen >= sizeof(ipstr))
1123 goto out;
1124 memcpy(ipstr, addr, addrlen);
1125 ipstr[addrlen] = 0;
1126
1127 /* attempt to get the address of the given interface name */
1128 switch(Curl_if2ip(conn->remote_addr->family,
1129 #ifdef USE_IPV6
1130 Curl_ipv6_scope(&conn->remote_addr->sa_addr),
1131 conn->scope_id,
1132 #endif
1133 ipstr, hbuf, sizeof(hbuf))) {
1134 case IF2IP_NOT_FOUND:
1135 /* not an interface, use the given string as host name instead */
1136 host = ipstr;
1137 break;
1138 case IF2IP_AF_NOT_SUPPORTED:
1139 goto out;
1140 case IF2IP_FOUND:
1141 host = hbuf; /* use the hbuf for host name */
1142 break;
1143 }
1144 }
1145 else
1146 /* there was only a port(-range) given, default the host */
1147 host = NULL;
1148 } /* data->set.ftpport */
1149
1150 if(!host) {
1151 const char *r;
1152 /* not an interface and not a host name, get default by extracting
1153 the IP from the control connection */
1154 sslen = sizeof(ss);
1155 if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
1156 failf(data, "getsockname() failed: %s",
1157 Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
1158 goto out;
1159 }
1160 switch(sa->sa_family) {
1161 #ifdef USE_IPV6
1162 case AF_INET6:
1163 r = Curl_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, sizeof(hbuf));
1164 break;
1165 #endif
1166 default:
1167 r = Curl_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, sizeof(hbuf));
1168 break;
1169 }
1170 if(!r) {
1171 goto out;
1172 }
1173 host = hbuf; /* use this host name */
1174 possibly_non_local = FALSE; /* we know it is local now */
1175 }
1176
1177 /* resolv ip/host to ip */
1178 rc = Curl_resolv(data, host, 0, FALSE, &h);
1179 if(rc == CURLRESOLV_PENDING)
1180 (void)Curl_resolver_wait_resolv(data, &h);
1181 if(h) {
1182 res = h->addr;
1183 /* when we return from this function, we can forget about this entry
1184 to we can unlock it now already */
1185 Curl_resolv_unlock(data, h);
1186 } /* (h) */
1187 else
1188 res = NULL; /* failure! */
1189
1190 if(!res) {
1191 failf(data, "failed to resolve the address provided to PORT: %s", host);
1192 goto out;
1193 }
1194
1195 host = NULL;
1196
1197 /* step 2, create a socket for the requested address */
1198 error = 0;
1199 for(ai = res; ai; ai = ai->ai_next) {
1200 if(Curl_socket_open(data, ai, NULL, conn->transport, &portsock)) {
1201 error = SOCKERRNO;
1202 continue;
1203 }
1204 break;
1205 }
1206 if(!ai) {
1207 failf(data, "socket failure: %s",
1208 Curl_strerror(error, buffer, sizeof(buffer)));
1209 goto out;
1210 }
1211 DEBUGF(infof(data, "ftp_state_use_port(), opened socket"));
1212
1213 /* step 3, bind to a suitable local address */
1214
1215 memcpy(sa, ai->ai_addr, ai->ai_addrlen);
1216 sslen = ai->ai_addrlen;
1217
1218 for(port = port_min; port <= port_max;) {
1219 if(sa->sa_family == AF_INET)
1220 sa4->sin_port = htons(port);
1221 #ifdef USE_IPV6
1222 else
1223 sa6->sin6_port = htons(port);
1224 #endif
1225 /* Try binding the given address. */
1226 if(bind(portsock, sa, sslen) ) {
1227 /* It failed. */
1228 error = SOCKERRNO;
1229 if(possibly_non_local && (error == EADDRNOTAVAIL)) {
1230 /* The requested bind address is not local. Use the address used for
1231 * the control connection instead and restart the port loop
1232 */
1233 infof(data, "bind(port=%hu) on non-local address failed: %s", port,
1234 Curl_strerror(error, buffer, sizeof(buffer)));
1235
1236 sslen = sizeof(ss);
1237 if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
1238 failf(data, "getsockname() failed: %s",
1239 Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
1240 goto out;
1241 }
1242 port = port_min;
1243 possibly_non_local = FALSE; /* don't try this again */
1244 continue;
1245 }
1246 if(error != EADDRINUSE && error != EACCES) {
1247 failf(data, "bind(port=%hu) failed: %s", port,
1248 Curl_strerror(error, buffer, sizeof(buffer)));
1249 goto out;
1250 }
1251 }
1252 else
1253 break;
1254
1255 port++;
1256 }
1257
1258 /* maybe all ports were in use already */
1259 if(port > port_max) {
1260 failf(data, "bind() failed, we ran out of ports");
1261 goto out;
1262 }
1263
1264 /* get the name again after the bind() so that we can extract the
1265 port number it uses now */
1266 sslen = sizeof(ss);
1267 if(getsockname(portsock, sa, &sslen)) {
1268 failf(data, "getsockname() failed: %s",
1269 Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
1270 goto out;
1271 }
1272 DEBUGF(infof(data, "ftp_state_use_port(), socket bound to port %d", port));
1273
1274 /* step 4, listen on the socket */
1275
1276 if(listen(portsock, 1)) {
1277 failf(data, "socket failure: %s",
1278 Curl_strerror(SOCKERRNO, buffer, sizeof(buffer)));
1279 goto out;
1280 }
1281 DEBUGF(infof(data, "ftp_state_use_port(), listening on %d", port));
1282
1283 /* step 5, send the proper FTP command */
1284
1285 /* get a plain printable version of the numerical address to work with
1286 below */
1287 Curl_printable_address(ai, myhost, sizeof(myhost));
1288
1289 #ifdef USE_IPV6
1290 if(!conn->bits.ftp_use_eprt && conn->bits.ipv6)
1291 /* EPRT is disabled but we are connected to a IPv6 host, so we ignore the
1292 request and enable EPRT again! */
1293 conn->bits.ftp_use_eprt = TRUE;
1294 #endif
1295
1296 /* Replace any filter on SECONDARY with one listening on this socket */
1297 result = Curl_conn_tcp_listen_set(data, conn, SECONDARYSOCKET, &portsock);
1298 if(result)
1299 goto out;
1300 portsock = CURL_SOCKET_BAD; /* now held in filter */
1301
1302 for(; fcmd != DONE; fcmd++) {
1303
1304 if(!conn->bits.ftp_use_eprt && (EPRT == fcmd))
1305 /* if disabled, goto next */
1306 continue;
1307
1308 if((PORT == fcmd) && sa->sa_family != AF_INET)
1309 /* PORT is IPv4 only */
1310 continue;
1311
1312 switch(sa->sa_family) {
1313 case AF_INET:
1314 port = ntohs(sa4->sin_port);
1315 break;
1316 #ifdef USE_IPV6
1317 case AF_INET6:
1318 port = ntohs(sa6->sin6_port);
1319 break;
1320 #endif
1321 default:
1322 continue; /* might as well skip this */
1323 }
1324
1325 if(EPRT == fcmd) {
1326 /*
1327 * Two fine examples from RFC2428;
1328 *
1329 * EPRT |1|132.235.1.2|6275|
1330 *
1331 * EPRT |2|1080::8:800:200C:417A|5282|
1332 */
1333
1334 result = Curl_pp_sendf(data, &ftpc->pp, "%s |%d|%s|%hu|", mode[fcmd],
1335 sa->sa_family == AF_INET?1:2,
1336 myhost, port);
1337 if(result) {
1338 failf(data, "Failure sending EPRT command: %s",
1339 curl_easy_strerror(result));
1340 goto out;
1341 }
1342 break;
1343 }
1344 if(PORT == fcmd) {
1345 /* large enough for [IP address],[num],[num] */
1346 char target[sizeof(myhost) + 20];
1347 char *source = myhost;
1348 char *dest = target;
1349
1350 /* translate x.x.x.x to x,x,x,x */
1351 while(source && *source) {
1352 if(*source == '.')
1353 *dest = ',';
1354 else
1355 *dest = *source;
1356 dest++;
1357 source++;
1358 }
1359 *dest = 0;
1360 msnprintf(dest, 20, ",%d,%d", (int)(port>>8), (int)(port&0xff));
1361
1362 result = Curl_pp_sendf(data, &ftpc->pp, "%s %s", mode[fcmd], target);
1363 if(result) {
1364 failf(data, "Failure sending PORT command: %s",
1365 curl_easy_strerror(result));
1366 goto out;
1367 }
1368 break;
1369 }
1370 }
1371
1372 /* store which command was sent */
1373 ftpc->count1 = fcmd;
1374
1375 ftp_state(data, FTP_PORT);
1376
1377 out:
1378 if(result) {
1379 ftp_state(data, FTP_STOP);
1380 }
1381 if(portsock != CURL_SOCKET_BAD)
1382 Curl_socket_close(data, conn, portsock);
1383 return result;
1384 }
1385
ftp_state_use_pasv(struct Curl_easy * data,struct connectdata * conn)1386 static CURLcode ftp_state_use_pasv(struct Curl_easy *data,
1387 struct connectdata *conn)
1388 {
1389 struct ftp_conn *ftpc = &conn->proto.ftpc;
1390 CURLcode result = CURLE_OK;
1391 /*
1392 Here's the executive summary on what to do:
1393
1394 PASV is RFC959, expect:
1395 227 Entering Passive Mode (a1,a2,a3,a4,p1,p2)
1396
1397 LPSV is RFC1639, expect:
1398 228 Entering Long Passive Mode (4,4,a1,a2,a3,a4,2,p1,p2)
1399
1400 EPSV is RFC2428, expect:
1401 229 Entering Extended Passive Mode (|||port|)
1402
1403 */
1404
1405 static const char mode[][5] = { "EPSV", "PASV" };
1406 int modeoff;
1407
1408 #ifdef PF_INET6
1409 if(!conn->bits.ftp_use_epsv && conn->bits.ipv6)
1410 /* EPSV is disabled but we are connected to a IPv6 host, so we ignore the
1411 request and enable EPSV again! */
1412 conn->bits.ftp_use_epsv = TRUE;
1413 #endif
1414
1415 modeoff = conn->bits.ftp_use_epsv?0:1;
1416
1417 result = Curl_pp_sendf(data, &ftpc->pp, "%s", mode[modeoff]);
1418 if(!result) {
1419 ftpc->count1 = modeoff;
1420 ftp_state(data, FTP_PASV);
1421 infof(data, "Connect data stream passively");
1422 }
1423 return result;
1424 }
1425
1426 /*
1427 * ftp_state_prepare_transfer() starts PORT, PASV or PRET etc.
1428 *
1429 * REST is the last command in the chain of commands when a "head"-like
1430 * request is made. Thus, if an actual transfer is to be made this is where we
1431 * take off for real.
1432 */
ftp_state_prepare_transfer(struct Curl_easy * data)1433 static CURLcode ftp_state_prepare_transfer(struct Curl_easy *data)
1434 {
1435 CURLcode result = CURLE_OK;
1436 struct FTP *ftp = data->req.p.ftp;
1437 struct connectdata *conn = data->conn;
1438
1439 if(ftp->transfer != PPTRANSFER_BODY) {
1440 /* doesn't transfer any data */
1441
1442 /* still possibly do PRE QUOTE jobs */
1443 ftp_state(data, FTP_RETR_PREQUOTE);
1444 result = ftp_state_quote(data, TRUE, FTP_RETR_PREQUOTE);
1445 }
1446 else if(data->set.ftp_use_port) {
1447 /* We have chosen to use the PORT (or similar) command */
1448 result = ftp_state_use_port(data, EPRT);
1449 }
1450 else {
1451 /* We have chosen (this is default) to use the PASV (or similar) command */
1452 if(data->set.ftp_use_pret) {
1453 /* The user has requested that we send a PRET command
1454 to prepare the server for the upcoming PASV */
1455 struct ftp_conn *ftpc = &conn->proto.ftpc;
1456 if(!conn->proto.ftpc.file)
1457 result = Curl_pp_sendf(data, &ftpc->pp, "PRET %s",
1458 data->set.str[STRING_CUSTOMREQUEST]?
1459 data->set.str[STRING_CUSTOMREQUEST]:
1460 (data->state.list_only?"NLST":"LIST"));
1461 else if(data->state.upload)
1462 result = Curl_pp_sendf(data, &ftpc->pp, "PRET STOR %s",
1463 conn->proto.ftpc.file);
1464 else
1465 result = Curl_pp_sendf(data, &ftpc->pp, "PRET RETR %s",
1466 conn->proto.ftpc.file);
1467 if(!result)
1468 ftp_state(data, FTP_PRET);
1469 }
1470 else
1471 result = ftp_state_use_pasv(data, conn);
1472 }
1473 return result;
1474 }
1475
ftp_state_rest(struct Curl_easy * data,struct connectdata * conn)1476 static CURLcode ftp_state_rest(struct Curl_easy *data,
1477 struct connectdata *conn)
1478 {
1479 CURLcode result = CURLE_OK;
1480 struct FTP *ftp = data->req.p.ftp;
1481 struct ftp_conn *ftpc = &conn->proto.ftpc;
1482
1483 if((ftp->transfer != PPTRANSFER_BODY) && ftpc->file) {
1484 /* if a "head"-like request is being made (on a file) */
1485
1486 /* Determine if server can respond to REST command and therefore
1487 whether it supports range */
1488 result = Curl_pp_sendf(data, &ftpc->pp, "REST %d", 0);
1489 if(!result)
1490 ftp_state(data, FTP_REST);
1491 }
1492 else
1493 result = ftp_state_prepare_transfer(data);
1494
1495 return result;
1496 }
1497
ftp_state_size(struct Curl_easy * data,struct connectdata * conn)1498 static CURLcode ftp_state_size(struct Curl_easy *data,
1499 struct connectdata *conn)
1500 {
1501 CURLcode result = CURLE_OK;
1502 struct FTP *ftp = data->req.p.ftp;
1503 struct ftp_conn *ftpc = &conn->proto.ftpc;
1504
1505 if((ftp->transfer == PPTRANSFER_INFO) && ftpc->file) {
1506 /* if a "head"-like request is being made (on a file) */
1507
1508 /* we know ftpc->file is a valid pointer to a file name */
1509 result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file);
1510 if(!result)
1511 ftp_state(data, FTP_SIZE);
1512 }
1513 else
1514 result = ftp_state_rest(data, conn);
1515
1516 return result;
1517 }
1518
ftp_state_list(struct Curl_easy * data)1519 static CURLcode ftp_state_list(struct Curl_easy *data)
1520 {
1521 CURLcode result = CURLE_OK;
1522 struct FTP *ftp = data->req.p.ftp;
1523 struct connectdata *conn = data->conn;
1524
1525 /* If this output is to be machine-parsed, the NLST command might be better
1526 to use, since the LIST command output is not specified or standard in any
1527 way. It has turned out that the NLST list output is not the same on all
1528 servers either... */
1529
1530 /*
1531 if FTPFILE_NOCWD was specified, we should add the path
1532 as argument for the LIST / NLST / or custom command.
1533 Whether the server will support this, is uncertain.
1534
1535 The other ftp_filemethods will CWD into dir/dir/ first and
1536 then just do LIST (in that case: nothing to do here)
1537 */
1538 char *lstArg = NULL;
1539 char *cmd;
1540
1541 if((data->set.ftp_filemethod == FTPFILE_NOCWD) && ftp->path) {
1542 /* url-decode before evaluation: e.g. paths starting/ending with %2f */
1543 const char *slashPos = NULL;
1544 char *rawPath = NULL;
1545 result = Curl_urldecode(ftp->path, 0, &rawPath, NULL, REJECT_CTRL);
1546 if(result)
1547 return result;
1548
1549 slashPos = strrchr(rawPath, '/');
1550 if(slashPos) {
1551 /* chop off the file part if format is dir/file otherwise remove
1552 the trailing slash for dir/dir/ except for absolute path / */
1553 size_t n = slashPos - rawPath;
1554 if(n == 0)
1555 ++n;
1556
1557 lstArg = rawPath;
1558 lstArg[n] = '\0';
1559 }
1560 else
1561 free(rawPath);
1562 }
1563
1564 cmd = aprintf("%s%s%s",
1565 data->set.str[STRING_CUSTOMREQUEST]?
1566 data->set.str[STRING_CUSTOMREQUEST]:
1567 (data->state.list_only?"NLST":"LIST"),
1568 lstArg? " ": "",
1569 lstArg? lstArg: "");
1570 free(lstArg);
1571
1572 if(!cmd)
1573 return CURLE_OUT_OF_MEMORY;
1574
1575 result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", cmd);
1576 free(cmd);
1577
1578 if(!result)
1579 ftp_state(data, FTP_LIST);
1580
1581 return result;
1582 }
1583
ftp_state_retr_prequote(struct Curl_easy * data)1584 static CURLcode ftp_state_retr_prequote(struct Curl_easy *data)
1585 {
1586 /* We've sent the TYPE, now we must send the list of prequote strings */
1587 return ftp_state_quote(data, TRUE, FTP_RETR_PREQUOTE);
1588 }
1589
ftp_state_stor_prequote(struct Curl_easy * data)1590 static CURLcode ftp_state_stor_prequote(struct Curl_easy *data)
1591 {
1592 /* We've sent the TYPE, now we must send the list of prequote strings */
1593 return ftp_state_quote(data, TRUE, FTP_STOR_PREQUOTE);
1594 }
1595
ftp_state_type(struct Curl_easy * data)1596 static CURLcode ftp_state_type(struct Curl_easy *data)
1597 {
1598 CURLcode result = CURLE_OK;
1599 struct FTP *ftp = data->req.p.ftp;
1600 struct connectdata *conn = data->conn;
1601 struct ftp_conn *ftpc = &conn->proto.ftpc;
1602
1603 /* If we have selected NOBODY and HEADER, it means that we only want file
1604 information. Which in FTP can't be much more than the file size and
1605 date. */
1606 if(data->req.no_body && ftpc->file &&
1607 ftp_need_type(conn, data->state.prefer_ascii)) {
1608 /* The SIZE command is _not_ RFC 959 specified, and therefore many servers
1609 may not support it! It is however the only way we have to get a file's
1610 size! */
1611
1612 ftp->transfer = PPTRANSFER_INFO;
1613 /* this means no actual transfer will be made */
1614
1615 /* Some servers return different sizes for different modes, and thus we
1616 must set the proper type before we check the size */
1617 result = ftp_nb_type(data, conn, data->state.prefer_ascii, FTP_TYPE);
1618 if(result)
1619 return result;
1620 }
1621 else
1622 result = ftp_state_size(data, conn);
1623
1624 return result;
1625 }
1626
1627 /* This is called after the CWD commands have been done in the beginning of
1628 the DO phase */
ftp_state_mdtm(struct Curl_easy * data)1629 static CURLcode ftp_state_mdtm(struct Curl_easy *data)
1630 {
1631 CURLcode result = CURLE_OK;
1632 struct connectdata *conn = data->conn;
1633 struct ftp_conn *ftpc = &conn->proto.ftpc;
1634
1635 /* Requested time of file or time-depended transfer? */
1636 if((data->set.get_filetime || data->set.timecondition) && ftpc->file) {
1637
1638 /* we have requested to get the modified-time of the file, this is a white
1639 spot as the MDTM is not mentioned in RFC959 */
1640 result = Curl_pp_sendf(data, &ftpc->pp, "MDTM %s", ftpc->file);
1641
1642 if(!result)
1643 ftp_state(data, FTP_MDTM);
1644 }
1645 else
1646 result = ftp_state_type(data);
1647
1648 return result;
1649 }
1650
1651
1652 /* This is called after the TYPE and possible quote commands have been sent */
ftp_state_ul_setup(struct Curl_easy * data,bool sizechecked)1653 static CURLcode ftp_state_ul_setup(struct Curl_easy *data,
1654 bool sizechecked)
1655 {
1656 CURLcode result = CURLE_OK;
1657 struct connectdata *conn = data->conn;
1658 struct FTP *ftp = data->req.p.ftp;
1659 struct ftp_conn *ftpc = &conn->proto.ftpc;
1660 bool append = data->set.remote_append;
1661
1662 if((data->state.resume_from && !sizechecked) ||
1663 ((data->state.resume_from > 0) && sizechecked)) {
1664 /* we're about to continue the uploading of a file */
1665 /* 1. get already existing file's size. We use the SIZE command for this
1666 which may not exist in the server! The SIZE command is not in
1667 RFC959. */
1668
1669 /* 2. This used to set REST. But since we can do append, we
1670 don't another ftp command. We just skip the source file
1671 offset and then we APPEND the rest on the file instead */
1672
1673 /* 3. pass file-size number of bytes in the source file */
1674 /* 4. lower the infilesize counter */
1675 /* => transfer as usual */
1676 int seekerr = CURL_SEEKFUNC_OK;
1677
1678 if(data->state.resume_from < 0) {
1679 /* Got no given size to start from, figure it out */
1680 result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file);
1681 if(!result)
1682 ftp_state(data, FTP_STOR_SIZE);
1683 return result;
1684 }
1685
1686 /* enable append */
1687 append = TRUE;
1688
1689 /* Let's read off the proper amount of bytes from the input. */
1690 if(data->set.seek_func) {
1691 Curl_set_in_callback(data, true);
1692 seekerr = data->set.seek_func(data->set.seek_client,
1693 data->state.resume_from, SEEK_SET);
1694 Curl_set_in_callback(data, false);
1695 }
1696
1697 if(seekerr != CURL_SEEKFUNC_OK) {
1698 curl_off_t passed = 0;
1699 if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
1700 failf(data, "Could not seek stream");
1701 return CURLE_FTP_COULDNT_USE_REST;
1702 }
1703 /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
1704 do {
1705 char scratch[4*1024];
1706 size_t readthisamountnow =
1707 (data->state.resume_from - passed > (curl_off_t)sizeof(scratch)) ?
1708 sizeof(scratch) :
1709 curlx_sotouz(data->state.resume_from - passed);
1710
1711 size_t actuallyread =
1712 data->state.fread_func(scratch, 1, readthisamountnow,
1713 data->state.in);
1714
1715 passed += actuallyread;
1716 if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
1717 /* this checks for greater-than only to make sure that the
1718 CURL_READFUNC_ABORT return code still aborts */
1719 failf(data, "Failed to read data");
1720 return CURLE_FTP_COULDNT_USE_REST;
1721 }
1722 } while(passed < data->state.resume_from);
1723 }
1724 /* now, decrease the size of the read */
1725 if(data->state.infilesize>0) {
1726 data->state.infilesize -= data->state.resume_from;
1727
1728 if(data->state.infilesize <= 0) {
1729 infof(data, "File already completely uploaded");
1730
1731 /* no data to transfer */
1732 Curl_xfer_setup(data, -1, -1, FALSE, -1);
1733
1734 /* Set ->transfer so that we won't get any error in
1735 * ftp_done() because we didn't transfer anything! */
1736 ftp->transfer = PPTRANSFER_NONE;
1737
1738 ftp_state(data, FTP_STOP);
1739 return CURLE_OK;
1740 }
1741 }
1742 /* we've passed, proceed as normal */
1743 } /* resume_from */
1744
1745 result = Curl_pp_sendf(data, &ftpc->pp, append?"APPE %s":"STOR %s",
1746 ftpc->file);
1747 if(!result)
1748 ftp_state(data, FTP_STOR);
1749
1750 return result;
1751 }
1752
ftp_state_quote(struct Curl_easy * data,bool init,ftpstate instate)1753 static CURLcode ftp_state_quote(struct Curl_easy *data,
1754 bool init,
1755 ftpstate instate)
1756 {
1757 CURLcode result = CURLE_OK;
1758 struct FTP *ftp = data->req.p.ftp;
1759 struct connectdata *conn = data->conn;
1760 struct ftp_conn *ftpc = &conn->proto.ftpc;
1761 bool quote = FALSE;
1762 struct curl_slist *item;
1763
1764 switch(instate) {
1765 case FTP_QUOTE:
1766 default:
1767 item = data->set.quote;
1768 break;
1769 case FTP_RETR_PREQUOTE:
1770 case FTP_STOR_PREQUOTE:
1771 item = data->set.prequote;
1772 break;
1773 case FTP_POSTQUOTE:
1774 item = data->set.postquote;
1775 break;
1776 }
1777
1778 /*
1779 * This state uses:
1780 * 'count1' to iterate over the commands to send
1781 * 'count2' to store whether to allow commands to fail
1782 */
1783
1784 if(init)
1785 ftpc->count1 = 0;
1786 else
1787 ftpc->count1++;
1788
1789 if(item) {
1790 int i = 0;
1791
1792 /* Skip count1 items in the linked list */
1793 while((i< ftpc->count1) && item) {
1794 item = item->next;
1795 i++;
1796 }
1797 if(item) {
1798 char *cmd = item->data;
1799 if(cmd[0] == '*') {
1800 cmd++;
1801 ftpc->count2 = 1; /* the sent command is allowed to fail */
1802 }
1803 else
1804 ftpc->count2 = 0; /* failure means cancel operation */
1805
1806 result = Curl_pp_sendf(data, &ftpc->pp, "%s", cmd);
1807 if(result)
1808 return result;
1809 ftp_state(data, instate);
1810 quote = TRUE;
1811 }
1812 }
1813
1814 if(!quote) {
1815 /* No more quote to send, continue to ... */
1816 switch(instate) {
1817 case FTP_QUOTE:
1818 default:
1819 result = ftp_state_cwd(data, conn);
1820 break;
1821 case FTP_RETR_PREQUOTE:
1822 if(ftp->transfer != PPTRANSFER_BODY)
1823 ftp_state(data, FTP_STOP);
1824 else {
1825 if(ftpc->known_filesize != -1) {
1826 Curl_pgrsSetDownloadSize(data, ftpc->known_filesize);
1827 result = ftp_state_retr(data, ftpc->known_filesize);
1828 }
1829 else {
1830 if(data->set.ignorecl || data->state.prefer_ascii) {
1831 /* 'ignorecl' is used to support download of growing files. It
1832 prevents the state machine from requesting the file size from
1833 the server. With an unknown file size the download continues
1834 until the server terminates it, otherwise the client stops if
1835 the received byte count exceeds the reported file size. Set
1836 option CURLOPT_IGNORE_CONTENT_LENGTH to 1 to enable this
1837 behavior.
1838
1839 In addition: asking for the size for 'TYPE A' transfers is not
1840 constructive since servers don't report the converted size. So
1841 skip it.
1842 */
1843 result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file);
1844 if(!result)
1845 ftp_state(data, FTP_RETR);
1846 }
1847 else {
1848 result = Curl_pp_sendf(data, &ftpc->pp, "SIZE %s", ftpc->file);
1849 if(!result)
1850 ftp_state(data, FTP_RETR_SIZE);
1851 }
1852 }
1853 }
1854 break;
1855 case FTP_STOR_PREQUOTE:
1856 result = ftp_state_ul_setup(data, FALSE);
1857 break;
1858 case FTP_POSTQUOTE:
1859 break;
1860 }
1861 }
1862
1863 return result;
1864 }
1865
1866 /* called from ftp_state_pasv_resp to switch to PASV in case of EPSV
1867 problems */
ftp_epsv_disable(struct Curl_easy * data,struct connectdata * conn)1868 static CURLcode ftp_epsv_disable(struct Curl_easy *data,
1869 struct connectdata *conn)
1870 {
1871 CURLcode result = CURLE_OK;
1872
1873 if(conn->bits.ipv6
1874 #ifndef CURL_DISABLE_PROXY
1875 && !(conn->bits.tunnel_proxy || conn->bits.socksproxy)
1876 #endif
1877 ) {
1878 /* We can't disable EPSV when doing IPv6, so this is instead a fail */
1879 failf(data, "Failed EPSV attempt, exiting");
1880 return CURLE_WEIRD_SERVER_REPLY;
1881 }
1882
1883 infof(data, "Failed EPSV attempt. Disabling EPSV");
1884 /* disable it for next transfer */
1885 conn->bits.ftp_use_epsv = FALSE;
1886 Curl_conn_close(data, SECONDARYSOCKET);
1887 Curl_conn_cf_discard_all(data, conn, SECONDARYSOCKET);
1888 data->state.errorbuf = FALSE; /* allow error message to get
1889 rewritten */
1890 result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", "PASV");
1891 if(!result) {
1892 conn->proto.ftpc.count1++;
1893 /* remain in/go to the FTP_PASV state */
1894 ftp_state(data, FTP_PASV);
1895 }
1896 return result;
1897 }
1898
1899
control_address(struct connectdata * conn)1900 static char *control_address(struct connectdata *conn)
1901 {
1902 /* Returns the control connection IP address.
1903 If a proxy tunnel is used, returns the original host name instead, because
1904 the effective control connection address is the proxy address,
1905 not the ftp host. */
1906 #ifndef CURL_DISABLE_PROXY
1907 if(conn->bits.tunnel_proxy || conn->bits.socksproxy)
1908 return conn->host.name;
1909 #endif
1910 return conn->primary.remote_ip;
1911 }
1912
match_pasv_6nums(const char * p,unsigned int * array)1913 static bool match_pasv_6nums(const char *p,
1914 unsigned int *array) /* 6 numbers */
1915 {
1916 int i;
1917 for(i = 0; i < 6; i++) {
1918 unsigned long num;
1919 char *endp;
1920 if(i) {
1921 if(*p != ',')
1922 return FALSE;
1923 p++;
1924 }
1925 if(!ISDIGIT(*p))
1926 return FALSE;
1927 num = strtoul(p, &endp, 10);
1928 if(num > 255)
1929 return FALSE;
1930 array[i] = (unsigned int)num;
1931 p = endp;
1932 }
1933 return TRUE;
1934 }
1935
ftp_state_pasv_resp(struct Curl_easy * data,int ftpcode)1936 static CURLcode ftp_state_pasv_resp(struct Curl_easy *data,
1937 int ftpcode)
1938 {
1939 struct connectdata *conn = data->conn;
1940 struct ftp_conn *ftpc = &conn->proto.ftpc;
1941 CURLcode result;
1942 struct Curl_dns_entry *addr = NULL;
1943 enum resolve_t rc;
1944 unsigned short connectport; /* the local port connect() should use! */
1945 struct pingpong *pp = &ftpc->pp;
1946 char *str =
1947 Curl_dyn_ptr(&pp->recvbuf) + 4; /* start on the first letter */
1948
1949 /* if we come here again, make sure the former name is cleared */
1950 Curl_safefree(ftpc->newhost);
1951
1952 if((ftpc->count1 == 0) &&
1953 (ftpcode == 229)) {
1954 /* positive EPSV response */
1955 char *ptr = strchr(str, '(');
1956 if(ptr) {
1957 char sep;
1958 ptr++;
1959 /* |||12345| */
1960 sep = ptr[0];
1961 /* the ISDIGIT() check here is because strtoul() accepts leading minus
1962 etc */
1963 if((ptr[1] == sep) && (ptr[2] == sep) && ISDIGIT(ptr[3])) {
1964 char *endp;
1965 unsigned long num = strtoul(&ptr[3], &endp, 10);
1966 if(*endp != sep)
1967 ptr = NULL;
1968 else if(num > 0xffff) {
1969 failf(data, "Illegal port number in EPSV reply");
1970 return CURLE_FTP_WEIRD_PASV_REPLY;
1971 }
1972 if(ptr) {
1973 ftpc->newport = (unsigned short)(num & 0xffff);
1974 ftpc->newhost = strdup(control_address(conn));
1975 if(!ftpc->newhost)
1976 return CURLE_OUT_OF_MEMORY;
1977 }
1978 }
1979 else
1980 ptr = NULL;
1981 }
1982 if(!ptr) {
1983 failf(data, "Weirdly formatted EPSV reply");
1984 return CURLE_FTP_WEIRD_PASV_REPLY;
1985 }
1986 }
1987 else if((ftpc->count1 == 1) &&
1988 (ftpcode == 227)) {
1989 /* positive PASV response */
1990 unsigned int ip[6];
1991
1992 /*
1993 * Scan for a sequence of six comma-separated numbers and use them as
1994 * IP+port indicators.
1995 *
1996 * Found reply-strings include:
1997 * "227 Entering Passive Mode (127,0,0,1,4,51)"
1998 * "227 Data transfer will passively listen to 127,0,0,1,4,51"
1999 * "227 Entering passive mode. 127,0,0,1,4,51"
2000 */
2001 while(*str) {
2002 if(match_pasv_6nums(str, ip))
2003 break;
2004 str++;
2005 }
2006
2007 if(!*str) {
2008 failf(data, "Couldn't interpret the 227-response");
2009 return CURLE_FTP_WEIRD_227_FORMAT;
2010 }
2011
2012 /* we got OK from server */
2013 if(data->set.ftp_skip_ip) {
2014 /* told to ignore the remotely given IP but instead use the host we used
2015 for the control connection */
2016 infof(data, "Skip %u.%u.%u.%u for data connection, reuse %s instead",
2017 ip[0], ip[1], ip[2], ip[3],
2018 conn->host.name);
2019 ftpc->newhost = strdup(control_address(conn));
2020 }
2021 else
2022 ftpc->newhost = aprintf("%u.%u.%u.%u", ip[0], ip[1], ip[2], ip[3]);
2023
2024 if(!ftpc->newhost)
2025 return CURLE_OUT_OF_MEMORY;
2026
2027 ftpc->newport = (unsigned short)(((ip[4]<<8) + ip[5]) & 0xffff);
2028 }
2029 else if(ftpc->count1 == 0) {
2030 /* EPSV failed, move on to PASV */
2031 return ftp_epsv_disable(data, conn);
2032 }
2033 else {
2034 failf(data, "Bad PASV/EPSV response: %03d", ftpcode);
2035 return CURLE_FTP_WEIRD_PASV_REPLY;
2036 }
2037
2038 #ifndef CURL_DISABLE_PROXY
2039 if(conn->bits.proxy) {
2040 /*
2041 * This connection uses a proxy and we need to connect to the proxy again
2042 * here. We don't want to rely on a former host lookup that might've
2043 * expired now, instead we remake the lookup here and now!
2044 */
2045 const char * const host_name = conn->bits.socksproxy ?
2046 conn->socks_proxy.host.name : conn->http_proxy.host.name;
2047 rc = Curl_resolv(data, host_name, conn->primary.remote_port, FALSE, &addr);
2048 if(rc == CURLRESOLV_PENDING)
2049 /* BLOCKING, ignores the return code but 'addr' will be NULL in
2050 case of failure */
2051 (void)Curl_resolver_wait_resolv(data, &addr);
2052
2053 /* we connect to the proxy's port */
2054 connectport = (unsigned short)conn->primary.remote_port;
2055
2056 if(!addr) {
2057 failf(data, "Can't resolve proxy host %s:%hu", host_name, connectport);
2058 return CURLE_COULDNT_RESOLVE_PROXY;
2059 }
2060 }
2061 else
2062 #endif
2063 {
2064 /* normal, direct, ftp connection */
2065 DEBUGASSERT(ftpc->newhost);
2066
2067 /* postponed address resolution in case of tcp fastopen */
2068 if(conn->bits.tcp_fastopen && !conn->bits.reuse && !ftpc->newhost[0]) {
2069 Curl_conn_ev_update_info(data, conn);
2070 Curl_safefree(ftpc->newhost);
2071 ftpc->newhost = strdup(control_address(conn));
2072 if(!ftpc->newhost)
2073 return CURLE_OUT_OF_MEMORY;
2074 }
2075
2076 rc = Curl_resolv(data, ftpc->newhost, ftpc->newport, FALSE, &addr);
2077 if(rc == CURLRESOLV_PENDING)
2078 /* BLOCKING */
2079 (void)Curl_resolver_wait_resolv(data, &addr);
2080
2081 connectport = ftpc->newport; /* we connect to the remote port */
2082
2083 if(!addr) {
2084 failf(data, "Can't resolve new host %s:%hu", ftpc->newhost, connectport);
2085 return CURLE_FTP_CANT_GET_HOST;
2086 }
2087 }
2088
2089 result = Curl_conn_setup(data, conn, SECONDARYSOCKET, addr,
2090 conn->bits.ftp_use_data_ssl?
2091 CURL_CF_SSL_ENABLE : CURL_CF_SSL_DISABLE);
2092
2093 if(result) {
2094 Curl_resolv_unlock(data, addr); /* we're done using this address */
2095 if(ftpc->count1 == 0 && ftpcode == 229)
2096 return ftp_epsv_disable(data, conn);
2097
2098 return result;
2099 }
2100
2101
2102 /*
2103 * When this is used from the multi interface, this might've returned with
2104 * the 'connected' set to FALSE and thus we are now awaiting a non-blocking
2105 * connect to connect.
2106 */
2107
2108 if(data->set.verbose)
2109 /* this just dumps information about this second connection */
2110 ftp_pasv_verbose(data, addr->addr, ftpc->newhost, connectport);
2111
2112 Curl_resolv_unlock(data, addr); /* we're done using this address */
2113
2114 Curl_safefree(conn->secondaryhostname);
2115 conn->secondary_port = ftpc->newport;
2116 conn->secondaryhostname = strdup(ftpc->newhost);
2117 if(!conn->secondaryhostname)
2118 return CURLE_OUT_OF_MEMORY;
2119
2120 conn->bits.do_more = TRUE;
2121 ftp_state(data, FTP_STOP); /* this phase is completed */
2122
2123 return result;
2124 }
2125
ftp_state_port_resp(struct Curl_easy * data,int ftpcode)2126 static CURLcode ftp_state_port_resp(struct Curl_easy *data,
2127 int ftpcode)
2128 {
2129 struct connectdata *conn = data->conn;
2130 struct ftp_conn *ftpc = &conn->proto.ftpc;
2131 ftpport fcmd = (ftpport)ftpc->count1;
2132 CURLcode result = CURLE_OK;
2133
2134 /* The FTP spec tells a positive response should have code 200.
2135 Be more permissive here to tolerate deviant servers. */
2136 if(ftpcode / 100 != 2) {
2137 /* the command failed */
2138
2139 if(EPRT == fcmd) {
2140 infof(data, "disabling EPRT usage");
2141 conn->bits.ftp_use_eprt = FALSE;
2142 }
2143 fcmd++;
2144
2145 if(fcmd == DONE) {
2146 failf(data, "Failed to do PORT");
2147 result = CURLE_FTP_PORT_FAILED;
2148 }
2149 else
2150 /* try next */
2151 result = ftp_state_use_port(data, fcmd);
2152 }
2153 else {
2154 infof(data, "Connect data stream actively");
2155 ftp_state(data, FTP_STOP); /* end of DO phase */
2156 result = ftp_dophase_done(data, FALSE);
2157 }
2158
2159 return result;
2160 }
2161
twodigit(const char * p)2162 static int twodigit(const char *p)
2163 {
2164 return (p[0]-'0') * 10 + (p[1]-'0');
2165 }
2166
ftp_213_date(const char * p,int * year,int * month,int * day,int * hour,int * minute,int * second)2167 static bool ftp_213_date(const char *p, int *year, int *month, int *day,
2168 int *hour, int *minute, int *second)
2169 {
2170 size_t len = strlen(p);
2171 if(len < 14)
2172 return FALSE;
2173 *year = twodigit(&p[0]) * 100 + twodigit(&p[2]);
2174 *month = twodigit(&p[4]);
2175 *day = twodigit(&p[6]);
2176 *hour = twodigit(&p[8]);
2177 *minute = twodigit(&p[10]);
2178 *second = twodigit(&p[12]);
2179
2180 if((*month > 12) || (*day > 31) || (*hour > 23) || (*minute > 59) ||
2181 (*second > 60))
2182 return FALSE;
2183 return TRUE;
2184 }
2185
client_write_header(struct Curl_easy * data,char * buf,size_t blen)2186 static CURLcode client_write_header(struct Curl_easy *data,
2187 char *buf, size_t blen)
2188 {
2189 /* Some replies from an FTP server are written to the client
2190 * as CLIENTWRITE_HEADER, formatted as if they came from a
2191 * HTTP conversation.
2192 * In all protocols, CLIENTWRITE_HEADER data is only passed to
2193 * the body write callback when data->set.include_header is set
2194 * via CURLOPT_HEADER.
2195 * For historic reasons, FTP never played this game and expects
2196 * all its HEADERs to do that always. Set that flag during the
2197 * call to Curl_client_write() so it does the right thing.
2198 *
2199 * Notice that we cannot enable this flag for FTP in general,
2200 * as an FTP transfer might involve a HTTP proxy connection and
2201 * headers from CONNECT should not automatically be part of the
2202 * output. */
2203 CURLcode result;
2204 int save = data->set.include_header;
2205 data->set.include_header = TRUE;
2206 result = Curl_client_write(data, CLIENTWRITE_HEADER, buf, blen);
2207 data->set.include_header = save? TRUE:FALSE;
2208 return result;
2209 }
2210
ftp_state_mdtm_resp(struct Curl_easy * data,int ftpcode)2211 static CURLcode ftp_state_mdtm_resp(struct Curl_easy *data,
2212 int ftpcode)
2213 {
2214 CURLcode result = CURLE_OK;
2215 struct FTP *ftp = data->req.p.ftp;
2216 struct connectdata *conn = data->conn;
2217 struct ftp_conn *ftpc = &conn->proto.ftpc;
2218
2219 switch(ftpcode) {
2220 case 213:
2221 {
2222 /* we got a time. Format should be: "YYYYMMDDHHMMSS[.sss]" where the
2223 last .sss part is optional and means fractions of a second */
2224 int year, month, day, hour, minute, second;
2225 struct pingpong *pp = &ftpc->pp;
2226 char *resp = Curl_dyn_ptr(&pp->recvbuf) + 4;
2227 if(ftp_213_date(resp, &year, &month, &day, &hour, &minute, &second)) {
2228 /* we have a time, reformat it */
2229 char timebuf[24];
2230 msnprintf(timebuf, sizeof(timebuf),
2231 "%04d%02d%02d %02d:%02d:%02d GMT",
2232 year, month, day, hour, minute, second);
2233 /* now, convert this into a time() value: */
2234 data->info.filetime = Curl_getdate_capped(timebuf);
2235 }
2236
2237 #ifdef CURL_FTP_HTTPSTYLE_HEAD
2238 /* If we asked for a time of the file and we actually got one as well,
2239 we "emulate" an HTTP-style header in our output. */
2240
2241 if(data->req.no_body &&
2242 ftpc->file &&
2243 data->set.get_filetime &&
2244 (data->info.filetime >= 0) ) {
2245 char headerbuf[128];
2246 int headerbuflen;
2247 time_t filetime = data->info.filetime;
2248 struct tm buffer;
2249 const struct tm *tm = &buffer;
2250
2251 result = Curl_gmtime(filetime, &buffer);
2252 if(result)
2253 return result;
2254
2255 /* format: "Tue, 15 Nov 1994 12:45:26" */
2256 headerbuflen = msnprintf(headerbuf, sizeof(headerbuf),
2257 "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
2258 Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
2259 tm->tm_mday,
2260 Curl_month[tm->tm_mon],
2261 tm->tm_year + 1900,
2262 tm->tm_hour,
2263 tm->tm_min,
2264 tm->tm_sec);
2265 result = client_write_header(data, headerbuf, headerbuflen);
2266 if(result)
2267 return result;
2268 } /* end of a ridiculous amount of conditionals */
2269 #endif
2270 }
2271 break;
2272 default:
2273 infof(data, "unsupported MDTM reply format");
2274 break;
2275 case 550: /* 550 is used for several different problems, e.g.
2276 "No such file or directory" or "Permission denied".
2277 It does not mean that the file does not exist at all. */
2278 infof(data, "MDTM failed: file does not exist or permission problem,"
2279 " continuing");
2280 break;
2281 }
2282
2283 if(data->set.timecondition) {
2284 if((data->info.filetime > 0) && (data->set.timevalue > 0)) {
2285 switch(data->set.timecondition) {
2286 case CURL_TIMECOND_IFMODSINCE:
2287 default:
2288 if(data->info.filetime <= data->set.timevalue) {
2289 infof(data, "The requested document is not new enough");
2290 ftp->transfer = PPTRANSFER_NONE; /* mark to not transfer data */
2291 data->info.timecond = TRUE;
2292 ftp_state(data, FTP_STOP);
2293 return CURLE_OK;
2294 }
2295 break;
2296 case CURL_TIMECOND_IFUNMODSINCE:
2297 if(data->info.filetime > data->set.timevalue) {
2298 infof(data, "The requested document is not old enough");
2299 ftp->transfer = PPTRANSFER_NONE; /* mark to not transfer data */
2300 data->info.timecond = TRUE;
2301 ftp_state(data, FTP_STOP);
2302 return CURLE_OK;
2303 }
2304 break;
2305 } /* switch */
2306 }
2307 else {
2308 infof(data, "Skipping time comparison");
2309 }
2310 }
2311
2312 if(!result)
2313 result = ftp_state_type(data);
2314
2315 return result;
2316 }
2317
ftp_state_type_resp(struct Curl_easy * data,int ftpcode,ftpstate instate)2318 static CURLcode ftp_state_type_resp(struct Curl_easy *data,
2319 int ftpcode,
2320 ftpstate instate)
2321 {
2322 CURLcode result = CURLE_OK;
2323 struct connectdata *conn = data->conn;
2324
2325 if(ftpcode/100 != 2) {
2326 /* "sasserftpd" and "(u)r(x)bot ftpd" both responds with 226 after a
2327 successful 'TYPE I'. While that is not as RFC959 says, it is still a
2328 positive response code and we allow that. */
2329 failf(data, "Couldn't set desired mode");
2330 return CURLE_FTP_COULDNT_SET_TYPE;
2331 }
2332 if(ftpcode != 200)
2333 infof(data, "Got a %03d response code instead of the assumed 200",
2334 ftpcode);
2335
2336 if(instate == FTP_TYPE)
2337 result = ftp_state_size(data, conn);
2338 else if(instate == FTP_LIST_TYPE)
2339 result = ftp_state_list(data);
2340 else if(instate == FTP_RETR_TYPE)
2341 result = ftp_state_retr_prequote(data);
2342 else if(instate == FTP_STOR_TYPE)
2343 result = ftp_state_stor_prequote(data);
2344
2345 return result;
2346 }
2347
ftp_state_retr(struct Curl_easy * data,curl_off_t filesize)2348 static CURLcode ftp_state_retr(struct Curl_easy *data,
2349 curl_off_t filesize)
2350 {
2351 CURLcode result = CURLE_OK;
2352 struct FTP *ftp = data->req.p.ftp;
2353 struct connectdata *conn = data->conn;
2354 struct ftp_conn *ftpc = &conn->proto.ftpc;
2355
2356 DEBUGF(infof(data, "ftp_state_retr()"));
2357 if(data->set.max_filesize && (filesize > data->set.max_filesize)) {
2358 failf(data, "Maximum file size exceeded");
2359 return CURLE_FILESIZE_EXCEEDED;
2360 }
2361 ftp->downloadsize = filesize;
2362
2363 if(data->state.resume_from) {
2364 /* We always (attempt to) get the size of downloads, so it is done before
2365 this even when not doing resumes. */
2366 if(filesize == -1) {
2367 infof(data, "ftp server doesn't support SIZE");
2368 /* We couldn't get the size and therefore we can't know if there really
2369 is a part of the file left to get, although the server will just
2370 close the connection when we start the connection so it won't cause
2371 us any harm, just not make us exit as nicely. */
2372 }
2373 else {
2374 /* We got a file size report, so we check that there actually is a
2375 part of the file left to get, or else we go home. */
2376 if(data->state.resume_from< 0) {
2377 /* We're supposed to download the last abs(from) bytes */
2378 if(filesize < -data->state.resume_from) {
2379 failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T
2380 ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")",
2381 data->state.resume_from, filesize);
2382 return CURLE_BAD_DOWNLOAD_RESUME;
2383 }
2384 /* convert to size to download */
2385 ftp->downloadsize = -data->state.resume_from;
2386 /* download from where? */
2387 data->state.resume_from = filesize - ftp->downloadsize;
2388 }
2389 else {
2390 if(filesize < data->state.resume_from) {
2391 failf(data, "Offset (%" CURL_FORMAT_CURL_OFF_T
2392 ") was beyond file size (%" CURL_FORMAT_CURL_OFF_T ")",
2393 data->state.resume_from, filesize);
2394 return CURLE_BAD_DOWNLOAD_RESUME;
2395 }
2396 /* Now store the number of bytes we are expected to download */
2397 ftp->downloadsize = filesize-data->state.resume_from;
2398 }
2399 }
2400
2401 if(ftp->downloadsize == 0) {
2402 /* no data to transfer */
2403 Curl_xfer_setup(data, -1, -1, FALSE, -1);
2404 infof(data, "File already completely downloaded");
2405
2406 /* Set ->transfer so that we won't get any error in ftp_done()
2407 * because we didn't transfer the any file */
2408 ftp->transfer = PPTRANSFER_NONE;
2409 ftp_state(data, FTP_STOP);
2410 return CURLE_OK;
2411 }
2412
2413 /* Set resume file transfer offset */
2414 infof(data, "Instructs server to resume from offset %"
2415 CURL_FORMAT_CURL_OFF_T, data->state.resume_from);
2416
2417 result = Curl_pp_sendf(data, &ftpc->pp, "REST %" CURL_FORMAT_CURL_OFF_T,
2418 data->state.resume_from);
2419 if(!result)
2420 ftp_state(data, FTP_RETR_REST);
2421 }
2422 else {
2423 /* no resume */
2424 result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file);
2425 if(!result)
2426 ftp_state(data, FTP_RETR);
2427 }
2428
2429 return result;
2430 }
2431
ftp_state_size_resp(struct Curl_easy * data,int ftpcode,ftpstate instate)2432 static CURLcode ftp_state_size_resp(struct Curl_easy *data,
2433 int ftpcode,
2434 ftpstate instate)
2435 {
2436 CURLcode result = CURLE_OK;
2437 curl_off_t filesize = -1;
2438 char *buf = Curl_dyn_ptr(&data->conn->proto.ftpc.pp.recvbuf);
2439 size_t len = data->conn->proto.ftpc.pp.nfinal;
2440
2441 /* get the size from the ascii string: */
2442 if(ftpcode == 213) {
2443 /* To allow servers to prepend "rubbish" in the response string, we scan
2444 for all the digits at the end of the response and parse only those as a
2445 number. */
2446 char *start = &buf[4];
2447 char *fdigit = memchr(start, '\r', len);
2448 if(fdigit) {
2449 fdigit--;
2450 if(*fdigit == '\n')
2451 fdigit--;
2452 while(ISDIGIT(fdigit[-1]) && (fdigit > start))
2453 fdigit--;
2454 }
2455 else
2456 fdigit = start;
2457 /* ignores parsing errors, which will make the size remain unknown */
2458 (void)curlx_strtoofft(fdigit, NULL, 10, &filesize);
2459
2460 }
2461 else if(ftpcode == 550) { /* "No such file or directory" */
2462 /* allow a SIZE failure for (resumed) uploads, when probing what command
2463 to use */
2464 if(instate != FTP_STOR_SIZE) {
2465 failf(data, "The file does not exist");
2466 return CURLE_REMOTE_FILE_NOT_FOUND;
2467 }
2468 }
2469
2470 if(instate == FTP_SIZE) {
2471 #ifdef CURL_FTP_HTTPSTYLE_HEAD
2472 if(-1 != filesize) {
2473 char clbuf[128];
2474 int clbuflen = msnprintf(clbuf, sizeof(clbuf),
2475 "Content-Length: %" CURL_FORMAT_CURL_OFF_T "\r\n", filesize);
2476 result = client_write_header(data, clbuf, clbuflen);
2477 if(result)
2478 return result;
2479 }
2480 #endif
2481 Curl_pgrsSetDownloadSize(data, filesize);
2482 result = ftp_state_rest(data, data->conn);
2483 }
2484 else if(instate == FTP_RETR_SIZE) {
2485 Curl_pgrsSetDownloadSize(data, filesize);
2486 result = ftp_state_retr(data, filesize);
2487 }
2488 else if(instate == FTP_STOR_SIZE) {
2489 data->state.resume_from = filesize;
2490 result = ftp_state_ul_setup(data, TRUE);
2491 }
2492
2493 return result;
2494 }
2495
ftp_state_rest_resp(struct Curl_easy * data,struct connectdata * conn,int ftpcode,ftpstate instate)2496 static CURLcode ftp_state_rest_resp(struct Curl_easy *data,
2497 struct connectdata *conn,
2498 int ftpcode,
2499 ftpstate instate)
2500 {
2501 CURLcode result = CURLE_OK;
2502 struct ftp_conn *ftpc = &conn->proto.ftpc;
2503
2504 switch(instate) {
2505 case FTP_REST:
2506 default:
2507 #ifdef CURL_FTP_HTTPSTYLE_HEAD
2508 if(ftpcode == 350) {
2509 char buffer[24]= { "Accept-ranges: bytes\r\n" };
2510 result = client_write_header(data, buffer, strlen(buffer));
2511 if(result)
2512 return result;
2513 }
2514 #endif
2515 result = ftp_state_prepare_transfer(data);
2516 break;
2517
2518 case FTP_RETR_REST:
2519 if(ftpcode != 350) {
2520 failf(data, "Couldn't use REST");
2521 result = CURLE_FTP_COULDNT_USE_REST;
2522 }
2523 else {
2524 result = Curl_pp_sendf(data, &ftpc->pp, "RETR %s", ftpc->file);
2525 if(!result)
2526 ftp_state(data, FTP_RETR);
2527 }
2528 break;
2529 }
2530
2531 return result;
2532 }
2533
ftp_state_stor_resp(struct Curl_easy * data,int ftpcode,ftpstate instate)2534 static CURLcode ftp_state_stor_resp(struct Curl_easy *data,
2535 int ftpcode, ftpstate instate)
2536 {
2537 CURLcode result = CURLE_OK;
2538 struct connectdata *conn = data->conn;
2539
2540 if(ftpcode >= 400) {
2541 failf(data, "Failed FTP upload: %0d", ftpcode);
2542 ftp_state(data, FTP_STOP);
2543 /* oops, we never close the sockets! */
2544 return CURLE_UPLOAD_FAILED;
2545 }
2546
2547 conn->proto.ftpc.state_saved = instate;
2548
2549 /* PORT means we are now awaiting the server to connect to us. */
2550 if(data->set.ftp_use_port) {
2551 bool connected;
2552
2553 ftp_state(data, FTP_STOP); /* no longer in STOR state */
2554
2555 result = AllowServerConnect(data, &connected);
2556 if(result)
2557 return result;
2558
2559 if(!connected) {
2560 struct ftp_conn *ftpc = &conn->proto.ftpc;
2561 infof(data, "Data conn was not available immediately");
2562 ftpc->wait_data_conn = TRUE;
2563 }
2564
2565 return CURLE_OK;
2566 }
2567 return InitiateTransfer(data);
2568 }
2569
2570 /* for LIST and RETR responses */
ftp_state_get_resp(struct Curl_easy * data,int ftpcode,ftpstate instate)2571 static CURLcode ftp_state_get_resp(struct Curl_easy *data,
2572 int ftpcode,
2573 ftpstate instate)
2574 {
2575 CURLcode result = CURLE_OK;
2576 struct FTP *ftp = data->req.p.ftp;
2577 struct connectdata *conn = data->conn;
2578
2579 if((ftpcode == 150) || (ftpcode == 125)) {
2580
2581 /*
2582 A;
2583 150 Opening BINARY mode data connection for /etc/passwd (2241
2584 bytes). (ok, the file is being transferred)
2585
2586 B:
2587 150 Opening ASCII mode data connection for /bin/ls
2588
2589 C:
2590 150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes).
2591
2592 D:
2593 150 Opening ASCII mode data connection for [file] (0.0.0.0,0) (545 bytes)
2594
2595 E:
2596 125 Data connection already open; Transfer starting. */
2597
2598 curl_off_t size = -1; /* default unknown size */
2599
2600
2601 /*
2602 * It appears that there are FTP-servers that return size 0 for files when
2603 * SIZE is used on the file while being in BINARY mode. To work around
2604 * that (stupid) behavior, we attempt to parse the RETR response even if
2605 * the SIZE returned size zero.
2606 *
2607 * Debugging help from Salvatore Sorrentino on February 26, 2003.
2608 */
2609
2610 if((instate != FTP_LIST) &&
2611 !data->state.prefer_ascii &&
2612 !data->set.ignorecl &&
2613 (ftp->downloadsize < 1)) {
2614 /*
2615 * It seems directory listings either don't show the size or very
2616 * often uses size 0 anyway. ASCII transfers may very well turn out
2617 * that the transferred amount of data is not the same as this line
2618 * tells, why using this number in those cases only confuses us.
2619 *
2620 * Example D above makes this parsing a little tricky */
2621 char *bytes;
2622 char *buf = Curl_dyn_ptr(&conn->proto.ftpc.pp.recvbuf);
2623 bytes = strstr(buf, " bytes");
2624 if(bytes) {
2625 long in = (long)(--bytes-buf);
2626 /* this is a hint there is size information in there! ;-) */
2627 while(--in) {
2628 /* scan for the left parenthesis and break there */
2629 if('(' == *bytes)
2630 break;
2631 /* skip only digits */
2632 if(!ISDIGIT(*bytes)) {
2633 bytes = NULL;
2634 break;
2635 }
2636 /* one more estep backwards */
2637 bytes--;
2638 }
2639 /* if we have nothing but digits: */
2640 if(bytes) {
2641 ++bytes;
2642 /* get the number! */
2643 (void)curlx_strtoofft(bytes, NULL, 10, &size);
2644 }
2645 }
2646 }
2647 else if(ftp->downloadsize > -1)
2648 size = ftp->downloadsize;
2649
2650 if(size > data->req.maxdownload && data->req.maxdownload > 0)
2651 size = data->req.size = data->req.maxdownload;
2652 else if((instate != FTP_LIST) && (data->state.prefer_ascii))
2653 size = -1; /* kludge for servers that understate ASCII mode file size */
2654
2655 infof(data, "Maxdownload = %" CURL_FORMAT_CURL_OFF_T,
2656 data->req.maxdownload);
2657
2658 if(instate != FTP_LIST)
2659 infof(data, "Getting file with size: %" CURL_FORMAT_CURL_OFF_T,
2660 size);
2661
2662 /* FTP download: */
2663 conn->proto.ftpc.state_saved = instate;
2664 conn->proto.ftpc.retr_size_saved = size;
2665
2666 if(data->set.ftp_use_port) {
2667 bool connected;
2668
2669 result = AllowServerConnect(data, &connected);
2670 if(result)
2671 return result;
2672
2673 if(!connected) {
2674 struct ftp_conn *ftpc = &conn->proto.ftpc;
2675 infof(data, "Data conn was not available immediately");
2676 ftp_state(data, FTP_STOP);
2677 ftpc->wait_data_conn = TRUE;
2678 }
2679 }
2680 else
2681 return InitiateTransfer(data);
2682 }
2683 else {
2684 if((instate == FTP_LIST) && (ftpcode == 450)) {
2685 /* simply no matching files in the dir listing */
2686 ftp->transfer = PPTRANSFER_NONE; /* don't download anything */
2687 ftp_state(data, FTP_STOP); /* this phase is over */
2688 }
2689 else {
2690 failf(data, "RETR response: %03d", ftpcode);
2691 return instate == FTP_RETR && ftpcode == 550?
2692 CURLE_REMOTE_FILE_NOT_FOUND:
2693 CURLE_FTP_COULDNT_RETR_FILE;
2694 }
2695 }
2696
2697 return result;
2698 }
2699
2700 /* after USER, PASS and ACCT */
ftp_state_loggedin(struct Curl_easy * data)2701 static CURLcode ftp_state_loggedin(struct Curl_easy *data)
2702 {
2703 CURLcode result = CURLE_OK;
2704 struct connectdata *conn = data->conn;
2705
2706 if(conn->bits.ftp_use_control_ssl) {
2707 /* PBSZ = PROTECTION BUFFER SIZE.
2708
2709 The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says:
2710
2711 Specifically, the PROT command MUST be preceded by a PBSZ
2712 command and a PBSZ command MUST be preceded by a successful
2713 security data exchange (the TLS negotiation in this case)
2714
2715 ... (and on page 8):
2716
2717 Thus the PBSZ command must still be issued, but must have a
2718 parameter of '0' to indicate that no buffering is taking place
2719 and the data connection should not be encapsulated.
2720 */
2721 result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "PBSZ %d", 0);
2722 if(!result)
2723 ftp_state(data, FTP_PBSZ);
2724 }
2725 else {
2726 result = ftp_state_pwd(data, conn);
2727 }
2728 return result;
2729 }
2730
2731 /* for USER and PASS responses */
ftp_state_user_resp(struct Curl_easy * data,int ftpcode)2732 static CURLcode ftp_state_user_resp(struct Curl_easy *data,
2733 int ftpcode)
2734 {
2735 CURLcode result = CURLE_OK;
2736 struct connectdata *conn = data->conn;
2737 struct ftp_conn *ftpc = &conn->proto.ftpc;
2738
2739 /* some need password anyway, and others just return 2xx ignored */
2740 if((ftpcode == 331) && (ftpc->state == FTP_USER)) {
2741 /* 331 Password required for ...
2742 (the server requires to send the user's password too) */
2743 result = Curl_pp_sendf(data, &ftpc->pp, "PASS %s",
2744 conn->passwd?conn->passwd:"");
2745 if(!result)
2746 ftp_state(data, FTP_PASS);
2747 }
2748 else if(ftpcode/100 == 2) {
2749 /* 230 User ... logged in.
2750 (the user logged in with or without password) */
2751 result = ftp_state_loggedin(data);
2752 }
2753 else if(ftpcode == 332) {
2754 if(data->set.str[STRING_FTP_ACCOUNT]) {
2755 result = Curl_pp_sendf(data, &ftpc->pp, "ACCT %s",
2756 data->set.str[STRING_FTP_ACCOUNT]);
2757 if(!result)
2758 ftp_state(data, FTP_ACCT);
2759 }
2760 else {
2761 failf(data, "ACCT requested but none available");
2762 result = CURLE_LOGIN_DENIED;
2763 }
2764 }
2765 else {
2766 /* All other response codes, like:
2767
2768 530 User ... access denied
2769 (the server denies to log the specified user) */
2770
2771 if(data->set.str[STRING_FTP_ALTERNATIVE_TO_USER] &&
2772 !ftpc->ftp_trying_alternative) {
2773 /* Ok, USER failed. Let's try the supplied command. */
2774 result =
2775 Curl_pp_sendf(data, &ftpc->pp, "%s",
2776 data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]);
2777 if(!result) {
2778 ftpc->ftp_trying_alternative = TRUE;
2779 ftp_state(data, FTP_USER);
2780 }
2781 }
2782 else {
2783 failf(data, "Access denied: %03d", ftpcode);
2784 result = CURLE_LOGIN_DENIED;
2785 }
2786 }
2787 return result;
2788 }
2789
2790 /* for ACCT response */
ftp_state_acct_resp(struct Curl_easy * data,int ftpcode)2791 static CURLcode ftp_state_acct_resp(struct Curl_easy *data,
2792 int ftpcode)
2793 {
2794 CURLcode result = CURLE_OK;
2795 if(ftpcode != 230) {
2796 failf(data, "ACCT rejected by server: %03d", ftpcode);
2797 result = CURLE_FTP_WEIRD_PASS_REPLY; /* FIX */
2798 }
2799 else
2800 result = ftp_state_loggedin(data);
2801
2802 return result;
2803 }
2804
2805
ftp_statemachine(struct Curl_easy * data,struct connectdata * conn)2806 static CURLcode ftp_statemachine(struct Curl_easy *data,
2807 struct connectdata *conn)
2808 {
2809 CURLcode result;
2810 int ftpcode;
2811 struct ftp_conn *ftpc = &conn->proto.ftpc;
2812 struct pingpong *pp = &ftpc->pp;
2813 static const char * const ftpauth[] = { "SSL", "TLS" };
2814 size_t nread = 0;
2815
2816 if(pp->sendleft)
2817 return Curl_pp_flushsend(data, pp);
2818
2819 result = ftp_readresp(data, FIRSTSOCKET, pp, &ftpcode, &nread);
2820 if(result)
2821 return result;
2822
2823 if(ftpcode) {
2824 /* we have now received a full FTP server response */
2825 switch(ftpc->state) {
2826 case FTP_WAIT220:
2827 if(ftpcode == 230) {
2828 /* 230 User logged in - already! Take as 220 if TLS required. */
2829 if(data->set.use_ssl <= CURLUSESSL_TRY ||
2830 conn->bits.ftp_use_control_ssl)
2831 return ftp_state_user_resp(data, ftpcode);
2832 }
2833 else if(ftpcode != 220) {
2834 failf(data, "Got a %03d ftp-server response when 220 was expected",
2835 ftpcode);
2836 return CURLE_WEIRD_SERVER_REPLY;
2837 }
2838
2839 /* We have received a 220 response fine, now we proceed. */
2840 #ifdef HAVE_GSSAPI
2841 if(data->set.krb) {
2842 /* If not anonymous login, try a secure login. Note that this
2843 procedure is still BLOCKING. */
2844
2845 Curl_sec_request_prot(conn, "private");
2846 /* We set private first as default, in case the line below fails to
2847 set a valid level */
2848 Curl_sec_request_prot(conn, data->set.str[STRING_KRB_LEVEL]);
2849
2850 if(Curl_sec_login(data, conn)) {
2851 failf(data, "secure login failed");
2852 return CURLE_WEIRD_SERVER_REPLY;
2853 }
2854 infof(data, "Authentication successful");
2855 }
2856 #endif
2857
2858 if(data->set.use_ssl && !conn->bits.ftp_use_control_ssl) {
2859 /* We don't have a SSL/TLS control connection yet, but FTPS is
2860 requested. Try a FTPS connection now */
2861
2862 ftpc->count3 = 0;
2863 switch(data->set.ftpsslauth) {
2864 case CURLFTPAUTH_DEFAULT:
2865 case CURLFTPAUTH_SSL:
2866 ftpc->count2 = 1; /* add one to get next */
2867 ftpc->count1 = 0;
2868 break;
2869 case CURLFTPAUTH_TLS:
2870 ftpc->count2 = -1; /* subtract one to get next */
2871 ftpc->count1 = 1;
2872 break;
2873 default:
2874 failf(data, "unsupported parameter to CURLOPT_FTPSSLAUTH: %d",
2875 (int)data->set.ftpsslauth);
2876 return CURLE_UNKNOWN_OPTION; /* we don't know what to do */
2877 }
2878 result = Curl_pp_sendf(data, &ftpc->pp, "AUTH %s",
2879 ftpauth[ftpc->count1]);
2880 if(!result)
2881 ftp_state(data, FTP_AUTH);
2882 }
2883 else
2884 result = ftp_state_user(data, conn);
2885 break;
2886
2887 case FTP_AUTH:
2888 /* we have gotten the response to a previous AUTH command */
2889
2890 if(pp->overflow)
2891 return CURLE_WEIRD_SERVER_REPLY; /* Forbid pipelining in response. */
2892
2893 /* RFC2228 (page 5) says:
2894 *
2895 * If the server is willing to accept the named security mechanism,
2896 * and does not require any security data, it must respond with
2897 * reply code 234/334.
2898 */
2899
2900 if((ftpcode == 234) || (ftpcode == 334)) {
2901 /* this was BLOCKING, keep it so for now */
2902 bool done;
2903 if(!Curl_conn_is_ssl(conn, FIRSTSOCKET)) {
2904 result = Curl_ssl_cfilter_add(data, conn, FIRSTSOCKET);
2905 if(result) {
2906 /* we failed and bail out */
2907 return CURLE_USE_SSL_FAILED;
2908 }
2909 }
2910 result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, &done);
2911 if(!result) {
2912 conn->bits.ftp_use_data_ssl = FALSE; /* clear-text data */
2913 conn->bits.ftp_use_control_ssl = TRUE; /* SSL on control */
2914 result = ftp_state_user(data, conn);
2915 }
2916 }
2917 else if(ftpc->count3 < 1) {
2918 ftpc->count3++;
2919 ftpc->count1 += ftpc->count2; /* get next attempt */
2920 result = Curl_pp_sendf(data, &ftpc->pp, "AUTH %s",
2921 ftpauth[ftpc->count1]);
2922 /* remain in this same state */
2923 }
2924 else {
2925 if(data->set.use_ssl > CURLUSESSL_TRY)
2926 /* we failed and CURLUSESSL_CONTROL or CURLUSESSL_ALL is set */
2927 result = CURLE_USE_SSL_FAILED;
2928 else
2929 /* ignore the failure and continue */
2930 result = ftp_state_user(data, conn);
2931 }
2932 break;
2933
2934 case FTP_USER:
2935 case FTP_PASS:
2936 result = ftp_state_user_resp(data, ftpcode);
2937 break;
2938
2939 case FTP_ACCT:
2940 result = ftp_state_acct_resp(data, ftpcode);
2941 break;
2942
2943 case FTP_PBSZ:
2944 result =
2945 Curl_pp_sendf(data, &ftpc->pp, "PROT %c",
2946 data->set.use_ssl == CURLUSESSL_CONTROL ? 'C' : 'P');
2947 if(!result)
2948 ftp_state(data, FTP_PROT);
2949 break;
2950
2951 case FTP_PROT:
2952 if(ftpcode/100 == 2)
2953 /* We have enabled SSL for the data connection! */
2954 conn->bits.ftp_use_data_ssl =
2955 (data->set.use_ssl != CURLUSESSL_CONTROL) ? TRUE : FALSE;
2956 /* FTP servers typically responds with 500 if they decide to reject
2957 our 'P' request */
2958 else if(data->set.use_ssl > CURLUSESSL_CONTROL)
2959 /* we failed and bails out */
2960 return CURLE_USE_SSL_FAILED;
2961
2962 if(data->set.ftp_ccc) {
2963 /* CCC - Clear Command Channel
2964 */
2965 result = Curl_pp_sendf(data, &ftpc->pp, "%s", "CCC");
2966 if(!result)
2967 ftp_state(data, FTP_CCC);
2968 }
2969 else
2970 result = ftp_state_pwd(data, conn);
2971 break;
2972
2973 case FTP_CCC:
2974 if(ftpcode < 500) {
2975 /* First shut down the SSL layer (note: this call will block) */
2976 result = Curl_ssl_cfilter_remove(data, FIRSTSOCKET);
2977
2978 if(result)
2979 failf(data, "Failed to clear the command channel (CCC)");
2980 }
2981 if(!result)
2982 /* Then continue as normal */
2983 result = ftp_state_pwd(data, conn);
2984 break;
2985
2986 case FTP_PWD:
2987 if(ftpcode == 257) {
2988 char *ptr = Curl_dyn_ptr(&pp->recvbuf) + 4; /* start on the first
2989 letter */
2990 bool entry_extracted = FALSE;
2991 struct dynbuf out;
2992 Curl_dyn_init(&out, 1000);
2993
2994 /* Reply format is like
2995 257<space>[rubbish]"<directory-name>"<space><commentary> and the
2996 RFC959 says
2997
2998 The directory name can contain any character; embedded
2999 double-quotes should be escaped by double-quotes (the
3000 "quote-doubling" convention).
3001 */
3002
3003 /* scan for the first double-quote for non-standard responses */
3004 while(*ptr != '\n' && *ptr != '\0' && *ptr != '"')
3005 ptr++;
3006
3007 if('\"' == *ptr) {
3008 /* it started good */
3009 for(ptr++; *ptr; ptr++) {
3010 if('\"' == *ptr) {
3011 if('\"' == ptr[1]) {
3012 /* "quote-doubling" */
3013 result = Curl_dyn_addn(&out, &ptr[1], 1);
3014 ptr++;
3015 }
3016 else {
3017 /* end of path */
3018 if(Curl_dyn_len(&out))
3019 entry_extracted = TRUE;
3020 break; /* get out of this loop */
3021 }
3022 }
3023 else
3024 result = Curl_dyn_addn(&out, ptr, 1);
3025 if(result)
3026 return result;
3027 }
3028 }
3029 if(entry_extracted) {
3030 /* If the path name does not look like an absolute path (i.e.: it
3031 does not start with a '/'), we probably need some server-dependent
3032 adjustments. For example, this is the case when connecting to
3033 an OS400 FTP server: this server supports two name syntaxes,
3034 the default one being incompatible with standard paths. In
3035 addition, this server switches automatically to the regular path
3036 syntax when one is encountered in a command: this results in
3037 having an entrypath in the wrong syntax when later used in CWD.
3038 The method used here is to check the server OS: we do it only
3039 if the path name looks strange to minimize overhead on other
3040 systems. */
3041 char *dir = Curl_dyn_ptr(&out);
3042
3043 if(!ftpc->server_os && dir[0] != '/') {
3044 result = Curl_pp_sendf(data, &ftpc->pp, "%s", "SYST");
3045 if(result) {
3046 free(dir);
3047 return result;
3048 }
3049 Curl_safefree(ftpc->entrypath);
3050 ftpc->entrypath = dir; /* remember this */
3051 infof(data, "Entry path is '%s'", ftpc->entrypath);
3052 /* also save it where getinfo can access it: */
3053 data->state.most_recent_ftp_entrypath = ftpc->entrypath;
3054 ftp_state(data, FTP_SYST);
3055 break;
3056 }
3057
3058 Curl_safefree(ftpc->entrypath);
3059 ftpc->entrypath = dir; /* remember this */
3060 infof(data, "Entry path is '%s'", ftpc->entrypath);
3061 /* also save it where getinfo can access it: */
3062 data->state.most_recent_ftp_entrypath = ftpc->entrypath;
3063 }
3064 else {
3065 /* couldn't get the path */
3066 Curl_dyn_free(&out);
3067 infof(data, "Failed to figure out path");
3068 }
3069 }
3070 ftp_state(data, FTP_STOP); /* we are done with the CONNECT phase! */
3071 DEBUGF(infof(data, "protocol connect phase DONE"));
3072 break;
3073
3074 case FTP_SYST:
3075 if(ftpcode == 215) {
3076 char *ptr = Curl_dyn_ptr(&pp->recvbuf) + 4; /* start on the first
3077 letter */
3078 char *os;
3079 char *start;
3080
3081 /* Reply format is like
3082 215<space><OS-name><space><commentary>
3083 */
3084 while(*ptr == ' ')
3085 ptr++;
3086 for(start = ptr; *ptr && *ptr != ' '; ptr++)
3087 ;
3088 os = Curl_memdup0(start, ptr - start);
3089 if(!os)
3090 return CURLE_OUT_OF_MEMORY;
3091
3092 /* Check for special servers here. */
3093 if(strcasecompare(os, "OS/400")) {
3094 /* Force OS400 name format 1. */
3095 result = Curl_pp_sendf(data, &ftpc->pp, "%s", "SITE NAMEFMT 1");
3096 if(result) {
3097 free(os);
3098 return result;
3099 }
3100 /* remember target server OS */
3101 Curl_safefree(ftpc->server_os);
3102 ftpc->server_os = os;
3103 ftp_state(data, FTP_NAMEFMT);
3104 break;
3105 }
3106 /* Nothing special for the target server. */
3107 /* remember target server OS */
3108 Curl_safefree(ftpc->server_os);
3109 ftpc->server_os = os;
3110 }
3111 else {
3112 /* Cannot identify server OS. Continue anyway and cross fingers. */
3113 }
3114
3115 ftp_state(data, FTP_STOP); /* we are done with the CONNECT phase! */
3116 DEBUGF(infof(data, "protocol connect phase DONE"));
3117 break;
3118
3119 case FTP_NAMEFMT:
3120 if(ftpcode == 250) {
3121 /* Name format change successful: reload initial path. */
3122 ftp_state_pwd(data, conn);
3123 break;
3124 }
3125
3126 ftp_state(data, FTP_STOP); /* we are done with the CONNECT phase! */
3127 DEBUGF(infof(data, "protocol connect phase DONE"));
3128 break;
3129
3130 case FTP_QUOTE:
3131 case FTP_POSTQUOTE:
3132 case FTP_RETR_PREQUOTE:
3133 case FTP_STOR_PREQUOTE:
3134 if((ftpcode >= 400) && !ftpc->count2) {
3135 /* failure response code, and not allowed to fail */
3136 failf(data, "QUOT command failed with %03d", ftpcode);
3137 result = CURLE_QUOTE_ERROR;
3138 }
3139 else
3140 result = ftp_state_quote(data, FALSE, ftpc->state);
3141 break;
3142
3143 case FTP_CWD:
3144 if(ftpcode/100 != 2) {
3145 /* failure to CWD there */
3146 if(data->set.ftp_create_missing_dirs &&
3147 ftpc->cwdcount && !ftpc->count2) {
3148 /* try making it */
3149 ftpc->count2++; /* counter to prevent CWD-MKD loops */
3150
3151 /* count3 is set to allow MKD to fail once per dir. In the case when
3152 CWD fails and then MKD fails (due to another session raced it to
3153 create the dir) this then allows for a second try to CWD to it. */
3154 ftpc->count3 = (data->set.ftp_create_missing_dirs == 2) ? 1 : 0;
3155
3156 result = Curl_pp_sendf(data, &ftpc->pp, "MKD %s",
3157 ftpc->dirs[ftpc->cwdcount - 1]);
3158 if(!result)
3159 ftp_state(data, FTP_MKD);
3160 }
3161 else {
3162 /* return failure */
3163 failf(data, "Server denied you to change to the given directory");
3164 ftpc->cwdfail = TRUE; /* don't remember this path as we failed
3165 to enter it */
3166 result = CURLE_REMOTE_ACCESS_DENIED;
3167 }
3168 }
3169 else {
3170 /* success */
3171 ftpc->count2 = 0;
3172 if(++ftpc->cwdcount <= ftpc->dirdepth)
3173 /* send next CWD */
3174 result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s",
3175 ftpc->dirs[ftpc->cwdcount - 1]);
3176 else
3177 result = ftp_state_mdtm(data);
3178 }
3179 break;
3180
3181 case FTP_MKD:
3182 if((ftpcode/100 != 2) && !ftpc->count3--) {
3183 /* failure to MKD the dir */
3184 failf(data, "Failed to MKD dir: %03d", ftpcode);
3185 result = CURLE_REMOTE_ACCESS_DENIED;
3186 }
3187 else {
3188 ftp_state(data, FTP_CWD);
3189 /* send CWD */
3190 result = Curl_pp_sendf(data, &ftpc->pp, "CWD %s",
3191 ftpc->dirs[ftpc->cwdcount - 1]);
3192 }
3193 break;
3194
3195 case FTP_MDTM:
3196 result = ftp_state_mdtm_resp(data, ftpcode);
3197 break;
3198
3199 case FTP_TYPE:
3200 case FTP_LIST_TYPE:
3201 case FTP_RETR_TYPE:
3202 case FTP_STOR_TYPE:
3203 result = ftp_state_type_resp(data, ftpcode, ftpc->state);
3204 break;
3205
3206 case FTP_SIZE:
3207 case FTP_RETR_SIZE:
3208 case FTP_STOR_SIZE:
3209 result = ftp_state_size_resp(data, ftpcode, ftpc->state);
3210 break;
3211
3212 case FTP_REST:
3213 case FTP_RETR_REST:
3214 result = ftp_state_rest_resp(data, conn, ftpcode, ftpc->state);
3215 break;
3216
3217 case FTP_PRET:
3218 if(ftpcode != 200) {
3219 /* there only is this one standard OK return code. */
3220 failf(data, "PRET command not accepted: %03d", ftpcode);
3221 return CURLE_FTP_PRET_FAILED;
3222 }
3223 result = ftp_state_use_pasv(data, conn);
3224 break;
3225
3226 case FTP_PASV:
3227 result = ftp_state_pasv_resp(data, ftpcode);
3228 break;
3229
3230 case FTP_PORT:
3231 result = ftp_state_port_resp(data, ftpcode);
3232 break;
3233
3234 case FTP_LIST:
3235 case FTP_RETR:
3236 result = ftp_state_get_resp(data, ftpcode, ftpc->state);
3237 break;
3238
3239 case FTP_STOR:
3240 result = ftp_state_stor_resp(data, ftpcode, ftpc->state);
3241 break;
3242
3243 case FTP_QUIT:
3244 default:
3245 /* internal error */
3246 ftp_state(data, FTP_STOP);
3247 break;
3248 }
3249 } /* if(ftpcode) */
3250
3251 return result;
3252 }
3253
3254
3255 /* called repeatedly until done from multi.c */
ftp_multi_statemach(struct Curl_easy * data,bool * done)3256 static CURLcode ftp_multi_statemach(struct Curl_easy *data,
3257 bool *done)
3258 {
3259 struct connectdata *conn = data->conn;
3260 struct ftp_conn *ftpc = &conn->proto.ftpc;
3261 CURLcode result = Curl_pp_statemach(data, &ftpc->pp, FALSE, FALSE);
3262
3263 /* Check for the state outside of the Curl_socket_check() return code checks
3264 since at times we are in fact already in this state when this function
3265 gets called. */
3266 *done = (ftpc->state == FTP_STOP) ? TRUE : FALSE;
3267
3268 return result;
3269 }
3270
ftp_block_statemach(struct Curl_easy * data,struct connectdata * conn)3271 static CURLcode ftp_block_statemach(struct Curl_easy *data,
3272 struct connectdata *conn)
3273 {
3274 struct ftp_conn *ftpc = &conn->proto.ftpc;
3275 struct pingpong *pp = &ftpc->pp;
3276 CURLcode result = CURLE_OK;
3277
3278 while(ftpc->state != FTP_STOP) {
3279 result = Curl_pp_statemach(data, pp, TRUE, TRUE /* disconnecting */);
3280 if(result)
3281 break;
3282 }
3283
3284 return result;
3285 }
3286
3287 /*
3288 * ftp_connect() should do everything that is to be considered a part of
3289 * the connection phase.
3290 *
3291 * The variable 'done' points to will be TRUE if the protocol-layer connect
3292 * phase is done when this function returns, or FALSE if not.
3293 *
3294 */
ftp_connect(struct Curl_easy * data,bool * done)3295 static CURLcode ftp_connect(struct Curl_easy *data,
3296 bool *done) /* see description above */
3297 {
3298 CURLcode result;
3299 struct connectdata *conn = data->conn;
3300 struct ftp_conn *ftpc = &conn->proto.ftpc;
3301 struct pingpong *pp = &ftpc->pp;
3302
3303 *done = FALSE; /* default to not done yet */
3304
3305 /* We always support persistent connections on ftp */
3306 connkeep(conn, "FTP default");
3307
3308 PINGPONG_SETUP(pp, ftp_statemachine, ftp_endofresp);
3309
3310 if(conn->handler->flags & PROTOPT_SSL) {
3311 /* BLOCKING */
3312 result = Curl_conn_connect(data, FIRSTSOCKET, TRUE, done);
3313 if(result)
3314 return result;
3315 conn->bits.ftp_use_control_ssl = TRUE;
3316 }
3317
3318 Curl_pp_init(pp); /* once per transfer */
3319
3320 /* When we connect, we start in the state where we await the 220
3321 response */
3322 ftp_state(data, FTP_WAIT220);
3323
3324 result = ftp_multi_statemach(data, done);
3325
3326 return result;
3327 }
3328
3329 /***********************************************************************
3330 *
3331 * ftp_done()
3332 *
3333 * The DONE function. This does what needs to be done after a single DO has
3334 * performed.
3335 *
3336 * Input argument is already checked for validity.
3337 */
ftp_done(struct Curl_easy * data,CURLcode status,bool premature)3338 static CURLcode ftp_done(struct Curl_easy *data, CURLcode status,
3339 bool premature)
3340 {
3341 struct connectdata *conn = data->conn;
3342 struct FTP *ftp = data->req.p.ftp;
3343 struct ftp_conn *ftpc = &conn->proto.ftpc;
3344 struct pingpong *pp = &ftpc->pp;
3345 ssize_t nread;
3346 int ftpcode;
3347 CURLcode result = CURLE_OK;
3348 char *rawPath = NULL;
3349 size_t pathLen = 0;
3350
3351 if(!ftp)
3352 return CURLE_OK;
3353
3354 switch(status) {
3355 case CURLE_BAD_DOWNLOAD_RESUME:
3356 case CURLE_FTP_WEIRD_PASV_REPLY:
3357 case CURLE_FTP_PORT_FAILED:
3358 case CURLE_FTP_ACCEPT_FAILED:
3359 case CURLE_FTP_ACCEPT_TIMEOUT:
3360 case CURLE_FTP_COULDNT_SET_TYPE:
3361 case CURLE_FTP_COULDNT_RETR_FILE:
3362 case CURLE_PARTIAL_FILE:
3363 case CURLE_UPLOAD_FAILED:
3364 case CURLE_REMOTE_ACCESS_DENIED:
3365 case CURLE_FILESIZE_EXCEEDED:
3366 case CURLE_REMOTE_FILE_NOT_FOUND:
3367 case CURLE_WRITE_ERROR:
3368 /* the connection stays alive fine even though this happened */
3369 case CURLE_OK: /* doesn't affect the control connection's status */
3370 if(!premature)
3371 break;
3372
3373 /* until we cope better with prematurely ended requests, let them
3374 * fallback as if in complete failure */
3375 FALLTHROUGH();
3376 default: /* by default, an error means the control connection is
3377 wedged and should not be used anymore */
3378 ftpc->ctl_valid = FALSE;
3379 ftpc->cwdfail = TRUE; /* set this TRUE to prevent us to remember the
3380 current path, as this connection is going */
3381 connclose(conn, "FTP ended with bad error code");
3382 result = status; /* use the already set error code */
3383 break;
3384 }
3385
3386 if(data->state.wildcardmatch) {
3387 if(data->set.chunk_end && ftpc->file) {
3388 Curl_set_in_callback(data, true);
3389 data->set.chunk_end(data->set.wildcardptr);
3390 Curl_set_in_callback(data, false);
3391 }
3392 ftpc->known_filesize = -1;
3393 }
3394
3395 if(!result)
3396 /* get the url-decoded "raw" path */
3397 result = Curl_urldecode(ftp->path, 0, &rawPath, &pathLen,
3398 REJECT_CTRL);
3399 if(result) {
3400 /* We can limp along anyway (and should try to since we may already be in
3401 * the error path) */
3402 ftpc->ctl_valid = FALSE; /* mark control connection as bad */
3403 connclose(conn, "FTP: out of memory!"); /* mark for connection closure */
3404 free(ftpc->prevpath);
3405 ftpc->prevpath = NULL; /* no path remembering */
3406 }
3407 else { /* remember working directory for connection reuse */
3408 if((data->set.ftp_filemethod == FTPFILE_NOCWD) && (rawPath[0] == '/'))
3409 free(rawPath); /* full path => no CWDs happened => keep ftpc->prevpath */
3410 else {
3411 free(ftpc->prevpath);
3412
3413 if(!ftpc->cwdfail) {
3414 if(data->set.ftp_filemethod == FTPFILE_NOCWD)
3415 pathLen = 0; /* relative path => working directory is FTP home */
3416 else
3417 pathLen -= ftpc->file?strlen(ftpc->file):0; /* file is url-decoded */
3418
3419 rawPath[pathLen] = '\0';
3420 ftpc->prevpath = rawPath;
3421 }
3422 else {
3423 free(rawPath);
3424 ftpc->prevpath = NULL; /* no path */
3425 }
3426 }
3427
3428 if(ftpc->prevpath)
3429 infof(data, "Remembering we are in dir \"%s\"", ftpc->prevpath);
3430 }
3431
3432 /* free the dir tree and file parts */
3433 freedirs(ftpc);
3434
3435 /* shut down the socket to inform the server we're done */
3436
3437 #ifdef _WIN32_WCE
3438 shutdown(conn->sock[SECONDARYSOCKET], 2); /* SD_BOTH */
3439 #endif
3440
3441 if(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) {
3442 if(!result && ftpc->dont_check && data->req.maxdownload > 0) {
3443 /* partial download completed */
3444 result = Curl_pp_sendf(data, pp, "%s", "ABOR");
3445 if(result) {
3446 failf(data, "Failure sending ABOR command: %s",
3447 curl_easy_strerror(result));
3448 ftpc->ctl_valid = FALSE; /* mark control connection as bad */
3449 connclose(conn, "ABOR command failed"); /* connection closure */
3450 }
3451 }
3452
3453 close_secondarysocket(data, conn);
3454 }
3455
3456 if(!result && (ftp->transfer == PPTRANSFER_BODY) && ftpc->ctl_valid &&
3457 pp->pending_resp && !premature) {
3458 /*
3459 * Let's see what the server says about the transfer we just performed,
3460 * but lower the timeout as sometimes this connection has died while the
3461 * data has been transferred. This happens when doing through NATs etc that
3462 * abandon old silent connections.
3463 */
3464 timediff_t old_time = pp->response_time;
3465
3466 pp->response_time = 60*1000; /* give it only a minute for now */
3467 pp->response = Curl_now(); /* timeout relative now */
3468
3469 result = Curl_GetFTPResponse(data, &nread, &ftpcode);
3470
3471 pp->response_time = old_time; /* set this back to previous value */
3472
3473 if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) {
3474 failf(data, "control connection looks dead");
3475 ftpc->ctl_valid = FALSE; /* mark control connection as bad */
3476 connclose(conn, "Timeout or similar in FTP DONE operation"); /* close */
3477 }
3478
3479 if(result) {
3480 Curl_safefree(ftp->pathalloc);
3481 return result;
3482 }
3483
3484 if(ftpc->dont_check && data->req.maxdownload > 0) {
3485 /* we have just sent ABOR and there is no reliable way to check if it was
3486 * successful or not; we have to close the connection now */
3487 infof(data, "partial download completed, closing connection");
3488 connclose(conn, "Partial download with no ability to check");
3489 return result;
3490 }
3491
3492 if(!ftpc->dont_check) {
3493 /* 226 Transfer complete, 250 Requested file action okay, completed. */
3494 switch(ftpcode) {
3495 case 226:
3496 case 250:
3497 break;
3498 case 552:
3499 failf(data, "Exceeded storage allocation");
3500 result = CURLE_REMOTE_DISK_FULL;
3501 break;
3502 default:
3503 failf(data, "server did not report OK, got %d", ftpcode);
3504 result = CURLE_PARTIAL_FILE;
3505 break;
3506 }
3507 }
3508 }
3509
3510 if(result || premature)
3511 /* the response code from the transfer showed an error already so no
3512 use checking further */
3513 ;
3514 else if(data->state.upload) {
3515 if((-1 != data->state.infilesize) &&
3516 (data->state.infilesize != data->req.writebytecount) &&
3517 !data->set.crlf &&
3518 (ftp->transfer == PPTRANSFER_BODY)) {
3519 failf(data, "Uploaded unaligned file size (%" CURL_FORMAT_CURL_OFF_T
3520 " out of %" CURL_FORMAT_CURL_OFF_T " bytes)",
3521 data->req.writebytecount, data->state.infilesize);
3522 result = CURLE_PARTIAL_FILE;
3523 }
3524 }
3525 else {
3526 if((-1 != data->req.size) &&
3527 (data->req.size != data->req.bytecount) &&
3528 #ifdef CURL_DO_LINEEND_CONV
3529 /* Most FTP servers don't adjust their file SIZE response for CRLFs, so
3530 * we'll check to see if the discrepancy can be explained by the number
3531 * of CRLFs we've changed to LFs.
3532 */
3533 ((data->req.size + data->state.crlf_conversions) !=
3534 data->req.bytecount) &&
3535 #endif /* CURL_DO_LINEEND_CONV */
3536 (data->req.maxdownload != data->req.bytecount)) {
3537 failf(data, "Received only partial file: %" CURL_FORMAT_CURL_OFF_T
3538 " bytes", data->req.bytecount);
3539 result = CURLE_PARTIAL_FILE;
3540 }
3541 else if(!ftpc->dont_check &&
3542 !data->req.bytecount &&
3543 (data->req.size>0)) {
3544 failf(data, "No data was received");
3545 result = CURLE_FTP_COULDNT_RETR_FILE;
3546 }
3547 }
3548
3549 /* clear these for next connection */
3550 ftp->transfer = PPTRANSFER_BODY;
3551 ftpc->dont_check = FALSE;
3552
3553 /* Send any post-transfer QUOTE strings? */
3554 if(!status && !result && !premature && data->set.postquote)
3555 result = ftp_sendquote(data, conn, data->set.postquote);
3556 Curl_safefree(ftp->pathalloc);
3557 return result;
3558 }
3559
3560 /***********************************************************************
3561 *
3562 * ftp_sendquote()
3563 *
3564 * Where a 'quote' means a list of custom commands to send to the server.
3565 * The quote list is passed as an argument.
3566 *
3567 * BLOCKING
3568 */
3569
3570 static
ftp_sendquote(struct Curl_easy * data,struct connectdata * conn,struct curl_slist * quote)3571 CURLcode ftp_sendquote(struct Curl_easy *data,
3572 struct connectdata *conn, struct curl_slist *quote)
3573 {
3574 struct curl_slist *item;
3575 struct ftp_conn *ftpc = &conn->proto.ftpc;
3576 struct pingpong *pp = &ftpc->pp;
3577
3578 item = quote;
3579 while(item) {
3580 if(item->data) {
3581 ssize_t nread;
3582 char *cmd = item->data;
3583 bool acceptfail = FALSE;
3584 CURLcode result;
3585 int ftpcode = 0;
3586
3587 /* if a command starts with an asterisk, which a legal FTP command never
3588 can, the command will be allowed to fail without it causing any
3589 aborts or cancels etc. It will cause libcurl to act as if the command
3590 is successful, whatever the server responds. */
3591
3592 if(cmd[0] == '*') {
3593 cmd++;
3594 acceptfail = TRUE;
3595 }
3596
3597 result = Curl_pp_sendf(data, &ftpc->pp, "%s", cmd);
3598 if(!result) {
3599 pp->response = Curl_now(); /* timeout relative now */
3600 result = Curl_GetFTPResponse(data, &nread, &ftpcode);
3601 }
3602 if(result)
3603 return result;
3604
3605 if(!acceptfail && (ftpcode >= 400)) {
3606 failf(data, "QUOT string not accepted: %s", cmd);
3607 return CURLE_QUOTE_ERROR;
3608 }
3609 }
3610
3611 item = item->next;
3612 }
3613
3614 return CURLE_OK;
3615 }
3616
3617 /***********************************************************************
3618 *
3619 * ftp_need_type()
3620 *
3621 * Returns TRUE if we in the current situation should send TYPE
3622 */
ftp_need_type(struct connectdata * conn,bool ascii_wanted)3623 static int ftp_need_type(struct connectdata *conn,
3624 bool ascii_wanted)
3625 {
3626 return conn->proto.ftpc.transfertype != (ascii_wanted?'A':'I');
3627 }
3628
3629 /***********************************************************************
3630 *
3631 * ftp_nb_type()
3632 *
3633 * Set TYPE. We only deal with ASCII or BINARY so this function
3634 * sets one of them.
3635 * If the transfer type is not sent, simulate on OK response in newstate
3636 */
ftp_nb_type(struct Curl_easy * data,struct connectdata * conn,bool ascii,ftpstate newstate)3637 static CURLcode ftp_nb_type(struct Curl_easy *data,
3638 struct connectdata *conn,
3639 bool ascii, ftpstate newstate)
3640 {
3641 struct ftp_conn *ftpc = &conn->proto.ftpc;
3642 CURLcode result;
3643 char want = (char)(ascii?'A':'I');
3644
3645 if(ftpc->transfertype == want) {
3646 ftp_state(data, newstate);
3647 return ftp_state_type_resp(data, 200, newstate);
3648 }
3649
3650 result = Curl_pp_sendf(data, &ftpc->pp, "TYPE %c", want);
3651 if(!result) {
3652 ftp_state(data, newstate);
3653
3654 /* keep track of our current transfer type */
3655 ftpc->transfertype = want;
3656 }
3657 return result;
3658 }
3659
3660 /***************************************************************************
3661 *
3662 * ftp_pasv_verbose()
3663 *
3664 * This function only outputs some informationals about this second connection
3665 * when we've issued a PASV command before and thus we have connected to a
3666 * possibly new IP address.
3667 *
3668 */
3669 #ifndef CURL_DISABLE_VERBOSE_STRINGS
3670 static void
ftp_pasv_verbose(struct Curl_easy * data,struct Curl_addrinfo * ai,char * newhost,int port)3671 ftp_pasv_verbose(struct Curl_easy *data,
3672 struct Curl_addrinfo *ai,
3673 char *newhost, /* ascii version */
3674 int port)
3675 {
3676 char buf[256];
3677 Curl_printable_address(ai, buf, sizeof(buf));
3678 infof(data, "Connecting to %s (%s) port %d", newhost, buf, port);
3679 }
3680 #endif
3681
3682 /*
3683 * ftp_do_more()
3684 *
3685 * This function shall be called when the second FTP (data) connection is
3686 * connected.
3687 *
3688 * 'complete' can return 0 for incomplete, 1 for done and -1 for go back
3689 * (which basically is only for when PASV is being sent to retry a failed
3690 * EPSV).
3691 */
3692
ftp_do_more(struct Curl_easy * data,int * completep)3693 static CURLcode ftp_do_more(struct Curl_easy *data, int *completep)
3694 {
3695 struct connectdata *conn = data->conn;
3696 struct ftp_conn *ftpc = &conn->proto.ftpc;
3697 CURLcode result = CURLE_OK;
3698 bool connected = FALSE;
3699 bool complete = FALSE;
3700
3701 /* the ftp struct is inited in ftp_connect(). If we are connecting to an HTTP
3702 * proxy then the state will not be valid until after that connection is
3703 * complete */
3704 struct FTP *ftp = NULL;
3705
3706 /* if the second connection isn't done yet, wait for it to have
3707 * connected to the remote host. When using proxy tunneling, this
3708 * means the tunnel needs to have been establish. However, we
3709 * can not expect the remote host to talk to us in any way yet.
3710 * So, when using ftps: the SSL handshake will not start until we
3711 * tell the remote server that we are there. */
3712 if(conn->cfilter[SECONDARYSOCKET]) {
3713 result = Curl_conn_connect(data, SECONDARYSOCKET, FALSE, &connected);
3714 if(result || !Curl_conn_is_ip_connected(data, SECONDARYSOCKET)) {
3715 if(result && (ftpc->count1 == 0)) {
3716 *completep = -1; /* go back to DOING please */
3717 /* this is a EPSV connect failing, try PASV instead */
3718 return ftp_epsv_disable(data, conn);
3719 }
3720 return result;
3721 }
3722 }
3723
3724 /* Curl_proxy_connect might have moved the protocol state */
3725 ftp = data->req.p.ftp;
3726
3727 if(ftpc->state) {
3728 /* already in a state so skip the initial commands.
3729 They are only done to kickstart the do_more state */
3730 result = ftp_multi_statemach(data, &complete);
3731
3732 *completep = (int)complete;
3733
3734 /* if we got an error or if we don't wait for a data connection return
3735 immediately */
3736 if(result || !ftpc->wait_data_conn)
3737 return result;
3738
3739 /* if we reach the end of the FTP state machine here, *complete will be
3740 TRUE but so is ftpc->wait_data_conn, which says we need to wait for the
3741 data connection and therefore we're not actually complete */
3742 *completep = 0;
3743 }
3744
3745 if(ftp->transfer <= PPTRANSFER_INFO) {
3746 /* a transfer is about to take place, or if not a file name was given
3747 so we'll do a SIZE on it later and then we need the right TYPE first */
3748
3749 if(ftpc->wait_data_conn) {
3750 bool serv_conned;
3751
3752 result = ReceivedServerConnect(data, &serv_conned);
3753 if(result)
3754 return result; /* Failed to accept data connection */
3755
3756 if(serv_conned) {
3757 /* It looks data connection is established */
3758 result = AcceptServerConnect(data);
3759 ftpc->wait_data_conn = FALSE;
3760 if(!result)
3761 result = InitiateTransfer(data);
3762
3763 if(result)
3764 return result;
3765
3766 *completep = 1; /* this state is now complete when the server has
3767 connected back to us */
3768 }
3769 }
3770 else if(data->state.upload) {
3771 result = ftp_nb_type(data, conn, data->state.prefer_ascii,
3772 FTP_STOR_TYPE);
3773 if(result)
3774 return result;
3775
3776 result = ftp_multi_statemach(data, &complete);
3777 *completep = (int)complete;
3778 }
3779 else {
3780 /* download */
3781 ftp->downloadsize = -1; /* unknown as of yet */
3782
3783 result = Curl_range(data);
3784
3785 if(result == CURLE_OK && data->req.maxdownload >= 0) {
3786 /* Don't check for successful transfer */
3787 ftpc->dont_check = TRUE;
3788 }
3789
3790 if(result)
3791 ;
3792 else if(data->state.list_only || !ftpc->file) {
3793 /* The specified path ends with a slash, and therefore we think this
3794 is a directory that is requested, use LIST. But before that we
3795 need to set ASCII transfer mode. */
3796
3797 /* But only if a body transfer was requested. */
3798 if(ftp->transfer == PPTRANSFER_BODY) {
3799 result = ftp_nb_type(data, conn, TRUE, FTP_LIST_TYPE);
3800 if(result)
3801 return result;
3802 }
3803 /* otherwise just fall through */
3804 }
3805 else {
3806 result = ftp_nb_type(data, conn, data->state.prefer_ascii,
3807 FTP_RETR_TYPE);
3808 if(result)
3809 return result;
3810 }
3811
3812 result = ftp_multi_statemach(data, &complete);
3813 *completep = (int)complete;
3814 }
3815 return result;
3816 }
3817
3818 /* no data to transfer */
3819 Curl_xfer_setup(data, -1, -1, FALSE, -1);
3820
3821 if(!ftpc->wait_data_conn) {
3822 /* no waiting for the data connection so this is now complete */
3823 *completep = 1;
3824 DEBUGF(infof(data, "DO-MORE phase ends with %d", (int)result));
3825 }
3826
3827 return result;
3828 }
3829
3830
3831
3832 /***********************************************************************
3833 *
3834 * ftp_perform()
3835 *
3836 * This is the actual DO function for FTP. Get a file/directory according to
3837 * the options previously setup.
3838 */
3839
3840 static
ftp_perform(struct Curl_easy * data,bool * connected,bool * dophase_done)3841 CURLcode ftp_perform(struct Curl_easy *data,
3842 bool *connected, /* connect status after PASV / PORT */
3843 bool *dophase_done)
3844 {
3845 /* this is FTP and no proxy */
3846 CURLcode result = CURLE_OK;
3847
3848 DEBUGF(infof(data, "DO phase starts"));
3849
3850 if(data->req.no_body) {
3851 /* requested no body means no transfer... */
3852 struct FTP *ftp = data->req.p.ftp;
3853 ftp->transfer = PPTRANSFER_INFO;
3854 }
3855
3856 *dophase_done = FALSE; /* not done yet */
3857
3858 /* start the first command in the DO phase */
3859 result = ftp_state_quote(data, TRUE, FTP_QUOTE);
3860 if(result)
3861 return result;
3862
3863 /* run the state-machine */
3864 result = ftp_multi_statemach(data, dophase_done);
3865
3866 *connected = Curl_conn_is_connected(data->conn, SECONDARYSOCKET);
3867
3868 infof(data, "ftp_perform ends with SECONDARY: %d", *connected);
3869
3870 if(*dophase_done)
3871 DEBUGF(infof(data, "DO phase is complete1"));
3872
3873 return result;
3874 }
3875
wc_data_dtor(void * ptr)3876 static void wc_data_dtor(void *ptr)
3877 {
3878 struct ftp_wc *ftpwc = ptr;
3879 if(ftpwc && ftpwc->parser)
3880 Curl_ftp_parselist_data_free(&ftpwc->parser);
3881 free(ftpwc);
3882 }
3883
init_wc_data(struct Curl_easy * data)3884 static CURLcode init_wc_data(struct Curl_easy *data)
3885 {
3886 char *last_slash;
3887 struct FTP *ftp = data->req.p.ftp;
3888 char *path = ftp->path;
3889 struct WildcardData *wildcard = data->wildcard;
3890 CURLcode result = CURLE_OK;
3891 struct ftp_wc *ftpwc = NULL;
3892
3893 last_slash = strrchr(ftp->path, '/');
3894 if(last_slash) {
3895 last_slash++;
3896 if(last_slash[0] == '\0') {
3897 wildcard->state = CURLWC_CLEAN;
3898 result = ftp_parse_url_path(data);
3899 return result;
3900 }
3901 wildcard->pattern = strdup(last_slash);
3902 if(!wildcard->pattern)
3903 return CURLE_OUT_OF_MEMORY;
3904 last_slash[0] = '\0'; /* cut file from path */
3905 }
3906 else { /* there is only 'wildcard pattern' or nothing */
3907 if(path[0]) {
3908 wildcard->pattern = strdup(path);
3909 if(!wildcard->pattern)
3910 return CURLE_OUT_OF_MEMORY;
3911 path[0] = '\0';
3912 }
3913 else { /* only list */
3914 wildcard->state = CURLWC_CLEAN;
3915 result = ftp_parse_url_path(data);
3916 return result;
3917 }
3918 }
3919
3920 /* program continues only if URL is not ending with slash, allocate needed
3921 resources for wildcard transfer */
3922
3923 /* allocate ftp protocol specific wildcard data */
3924 ftpwc = calloc(1, sizeof(struct ftp_wc));
3925 if(!ftpwc) {
3926 result = CURLE_OUT_OF_MEMORY;
3927 goto fail;
3928 }
3929
3930 /* INITIALIZE parselist structure */
3931 ftpwc->parser = Curl_ftp_parselist_data_alloc();
3932 if(!ftpwc->parser) {
3933 result = CURLE_OUT_OF_MEMORY;
3934 goto fail;
3935 }
3936
3937 wildcard->ftpwc = ftpwc; /* put it to the WildcardData tmp pointer */
3938 wildcard->dtor = wc_data_dtor;
3939
3940 /* wildcard does not support NOCWD option (assert it?) */
3941 if(data->set.ftp_filemethod == FTPFILE_NOCWD)
3942 data->set.ftp_filemethod = FTPFILE_MULTICWD;
3943
3944 /* try to parse ftp url */
3945 result = ftp_parse_url_path(data);
3946 if(result) {
3947 goto fail;
3948 }
3949
3950 wildcard->path = strdup(ftp->path);
3951 if(!wildcard->path) {
3952 result = CURLE_OUT_OF_MEMORY;
3953 goto fail;
3954 }
3955
3956 /* backup old write_function */
3957 ftpwc->backup.write_function = data->set.fwrite_func;
3958 /* parsing write function */
3959 data->set.fwrite_func = Curl_ftp_parselist;
3960 /* backup old file descriptor */
3961 ftpwc->backup.file_descriptor = data->set.out;
3962 /* let the writefunc callback know the transfer */
3963 data->set.out = data;
3964
3965 infof(data, "Wildcard - Parsing started");
3966 return CURLE_OK;
3967
3968 fail:
3969 if(ftpwc) {
3970 Curl_ftp_parselist_data_free(&ftpwc->parser);
3971 free(ftpwc);
3972 }
3973 Curl_safefree(wildcard->pattern);
3974 wildcard->dtor = ZERO_NULL;
3975 wildcard->ftpwc = NULL;
3976 return result;
3977 }
3978
wc_statemach(struct Curl_easy * data)3979 static CURLcode wc_statemach(struct Curl_easy *data)
3980 {
3981 struct WildcardData * const wildcard = data->wildcard;
3982 struct connectdata *conn = data->conn;
3983 CURLcode result = CURLE_OK;
3984
3985 for(;;) {
3986 switch(wildcard->state) {
3987 case CURLWC_INIT:
3988 result = init_wc_data(data);
3989 if(wildcard->state == CURLWC_CLEAN)
3990 /* only listing! */
3991 return result;
3992 wildcard->state = result ? CURLWC_ERROR : CURLWC_MATCHING;
3993 return result;
3994
3995 case CURLWC_MATCHING: {
3996 /* In this state is LIST response successfully parsed, so lets restore
3997 previous WRITEFUNCTION callback and WRITEDATA pointer */
3998 struct ftp_wc *ftpwc = wildcard->ftpwc;
3999 data->set.fwrite_func = ftpwc->backup.write_function;
4000 data->set.out = ftpwc->backup.file_descriptor;
4001 ftpwc->backup.write_function = ZERO_NULL;
4002 ftpwc->backup.file_descriptor = NULL;
4003 wildcard->state = CURLWC_DOWNLOADING;
4004
4005 if(Curl_ftp_parselist_geterror(ftpwc->parser)) {
4006 /* error found in LIST parsing */
4007 wildcard->state = CURLWC_CLEAN;
4008 continue;
4009 }
4010 if(wildcard->filelist.size == 0) {
4011 /* no corresponding file */
4012 wildcard->state = CURLWC_CLEAN;
4013 return CURLE_REMOTE_FILE_NOT_FOUND;
4014 }
4015 continue;
4016 }
4017
4018 case CURLWC_DOWNLOADING: {
4019 /* filelist has at least one file, lets get first one */
4020 struct ftp_conn *ftpc = &conn->proto.ftpc;
4021 struct curl_fileinfo *finfo = wildcard->filelist.head->ptr;
4022 struct FTP *ftp = data->req.p.ftp;
4023
4024 char *tmp_path = aprintf("%s%s", wildcard->path, finfo->filename);
4025 if(!tmp_path)
4026 return CURLE_OUT_OF_MEMORY;
4027
4028 /* switch default ftp->path and tmp_path */
4029 free(ftp->pathalloc);
4030 ftp->pathalloc = ftp->path = tmp_path;
4031
4032 infof(data, "Wildcard - START of \"%s\"", finfo->filename);
4033 if(data->set.chunk_bgn) {
4034 long userresponse;
4035 Curl_set_in_callback(data, true);
4036 userresponse = data->set.chunk_bgn(
4037 finfo, data->set.wildcardptr, (int)wildcard->filelist.size);
4038 Curl_set_in_callback(data, false);
4039 switch(userresponse) {
4040 case CURL_CHUNK_BGN_FUNC_SKIP:
4041 infof(data, "Wildcard - \"%s\" skipped by user",
4042 finfo->filename);
4043 wildcard->state = CURLWC_SKIP;
4044 continue;
4045 case CURL_CHUNK_BGN_FUNC_FAIL:
4046 return CURLE_CHUNK_FAILED;
4047 }
4048 }
4049
4050 if(finfo->filetype != CURLFILETYPE_FILE) {
4051 wildcard->state = CURLWC_SKIP;
4052 continue;
4053 }
4054
4055 if(finfo->flags & CURLFINFOFLAG_KNOWN_SIZE)
4056 ftpc->known_filesize = finfo->size;
4057
4058 result = ftp_parse_url_path(data);
4059 if(result)
4060 return result;
4061
4062 /* we don't need the Curl_fileinfo of first file anymore */
4063 Curl_llist_remove(&wildcard->filelist, wildcard->filelist.head, NULL);
4064
4065 if(wildcard->filelist.size == 0) { /* remains only one file to down. */
4066 wildcard->state = CURLWC_CLEAN;
4067 /* after that will be ftp_do called once again and no transfer
4068 will be done because of CURLWC_CLEAN state */
4069 return CURLE_OK;
4070 }
4071 return result;
4072 }
4073
4074 case CURLWC_SKIP: {
4075 if(data->set.chunk_end) {
4076 Curl_set_in_callback(data, true);
4077 data->set.chunk_end(data->set.wildcardptr);
4078 Curl_set_in_callback(data, false);
4079 }
4080 Curl_llist_remove(&wildcard->filelist, wildcard->filelist.head, NULL);
4081 wildcard->state = (wildcard->filelist.size == 0) ?
4082 CURLWC_CLEAN : CURLWC_DOWNLOADING;
4083 continue;
4084 }
4085
4086 case CURLWC_CLEAN: {
4087 struct ftp_wc *ftpwc = wildcard->ftpwc;
4088 result = CURLE_OK;
4089 if(ftpwc)
4090 result = Curl_ftp_parselist_geterror(ftpwc->parser);
4091
4092 wildcard->state = result ? CURLWC_ERROR : CURLWC_DONE;
4093 return result;
4094 }
4095
4096 case CURLWC_DONE:
4097 case CURLWC_ERROR:
4098 case CURLWC_CLEAR:
4099 if(wildcard->dtor) {
4100 wildcard->dtor(wildcard->ftpwc);
4101 wildcard->ftpwc = NULL;
4102 }
4103 return result;
4104 }
4105 }
4106 /* UNREACHABLE */
4107 }
4108
4109 /***********************************************************************
4110 *
4111 * ftp_do()
4112 *
4113 * This function is registered as 'curl_do' function. It decodes the path
4114 * parts etc as a wrapper to the actual DO function (ftp_perform).
4115 *
4116 * The input argument is already checked for validity.
4117 */
ftp_do(struct Curl_easy * data,bool * done)4118 static CURLcode ftp_do(struct Curl_easy *data, bool *done)
4119 {
4120 CURLcode result = CURLE_OK;
4121 struct connectdata *conn = data->conn;
4122 struct ftp_conn *ftpc = &conn->proto.ftpc;
4123
4124 *done = FALSE; /* default to false */
4125 ftpc->wait_data_conn = FALSE; /* default to no such wait */
4126
4127 #ifdef CURL_DO_LINEEND_CONV
4128 {
4129 /* FTP data may need conversion. */
4130 struct Curl_cwriter *ftp_lc_writer;
4131
4132 result = Curl_cwriter_create(&ftp_lc_writer, data, &ftp_cw_lc,
4133 CURL_CW_CONTENT_DECODE);
4134 if(result)
4135 return result;
4136
4137 result = Curl_cwriter_add(data, ftp_lc_writer);
4138 if(result) {
4139 Curl_cwriter_free(data, ftp_lc_writer);
4140 return result;
4141 }
4142 }
4143 #endif /* CURL_DO_LINEEND_CONV */
4144
4145 if(data->state.wildcardmatch) {
4146 result = wc_statemach(data);
4147 if(data->wildcard->state == CURLWC_SKIP ||
4148 data->wildcard->state == CURLWC_DONE) {
4149 /* do not call ftp_regular_transfer */
4150 return CURLE_OK;
4151 }
4152 if(result) /* error, loop or skipping the file */
4153 return result;
4154 }
4155 else { /* no wildcard FSM needed */
4156 result = ftp_parse_url_path(data);
4157 if(result)
4158 return result;
4159 }
4160
4161 result = ftp_regular_transfer(data, done);
4162
4163 return result;
4164 }
4165
4166 /***********************************************************************
4167 *
4168 * ftp_quit()
4169 *
4170 * This should be called before calling sclose() on an ftp control connection
4171 * (not data connections). We should then wait for the response from the
4172 * server before returning. The calling code should then try to close the
4173 * connection.
4174 *
4175 */
ftp_quit(struct Curl_easy * data,struct connectdata * conn)4176 static CURLcode ftp_quit(struct Curl_easy *data, struct connectdata *conn)
4177 {
4178 CURLcode result = CURLE_OK;
4179
4180 if(conn->proto.ftpc.ctl_valid) {
4181 result = Curl_pp_sendf(data, &conn->proto.ftpc.pp, "%s", "QUIT");
4182 if(result) {
4183 failf(data, "Failure sending QUIT command: %s",
4184 curl_easy_strerror(result));
4185 conn->proto.ftpc.ctl_valid = FALSE; /* mark control connection as bad */
4186 connclose(conn, "QUIT command failed"); /* mark for connection closure */
4187 ftp_state(data, FTP_STOP);
4188 return result;
4189 }
4190
4191 ftp_state(data, FTP_QUIT);
4192
4193 result = ftp_block_statemach(data, conn);
4194 }
4195
4196 return result;
4197 }
4198
4199 /***********************************************************************
4200 *
4201 * ftp_disconnect()
4202 *
4203 * Disconnect from an FTP server. Cleanup protocol-specific per-connection
4204 * resources. BLOCKING.
4205 */
ftp_disconnect(struct Curl_easy * data,struct connectdata * conn,bool dead_connection)4206 static CURLcode ftp_disconnect(struct Curl_easy *data,
4207 struct connectdata *conn,
4208 bool dead_connection)
4209 {
4210 struct ftp_conn *ftpc = &conn->proto.ftpc;
4211 struct pingpong *pp = &ftpc->pp;
4212
4213 /* We cannot send quit unconditionally. If this connection is stale or
4214 bad in any way, sending quit and waiting around here will make the
4215 disconnect wait in vain and cause more problems than we need to.
4216
4217 ftp_quit() will check the state of ftp->ctl_valid. If it's ok it
4218 will try to send the QUIT command, otherwise it will just return.
4219 */
4220 if(dead_connection)
4221 ftpc->ctl_valid = FALSE;
4222
4223 /* The FTP session may or may not have been allocated/setup at this point! */
4224 (void)ftp_quit(data, conn); /* ignore errors on the QUIT */
4225
4226 if(ftpc->entrypath) {
4227 if(data->state.most_recent_ftp_entrypath == ftpc->entrypath) {
4228 data->state.most_recent_ftp_entrypath = NULL;
4229 }
4230 Curl_safefree(ftpc->entrypath);
4231 }
4232
4233 freedirs(ftpc);
4234 Curl_safefree(ftpc->account);
4235 Curl_safefree(ftpc->alternative_to_user);
4236 Curl_safefree(ftpc->prevpath);
4237 Curl_safefree(ftpc->server_os);
4238 Curl_pp_disconnect(pp);
4239 Curl_sec_end(conn);
4240 return CURLE_OK;
4241 }
4242
4243 #ifdef _MSC_VER
4244 /* warning C4706: assignment within conditional expression */
4245 #pragma warning(disable:4706)
4246 #endif
4247
4248 /***********************************************************************
4249 *
4250 * ftp_parse_url_path()
4251 *
4252 * Parse the URL path into separate path components.
4253 *
4254 */
4255 static
ftp_parse_url_path(struct Curl_easy * data)4256 CURLcode ftp_parse_url_path(struct Curl_easy *data)
4257 {
4258 /* the ftp struct is already inited in ftp_connect() */
4259 struct FTP *ftp = data->req.p.ftp;
4260 struct connectdata *conn = data->conn;
4261 struct ftp_conn *ftpc = &conn->proto.ftpc;
4262 const char *slashPos = NULL;
4263 const char *fileName = NULL;
4264 CURLcode result = CURLE_OK;
4265 char *rawPath = NULL; /* url-decoded "raw" path */
4266 size_t pathLen = 0;
4267
4268 ftpc->ctl_valid = FALSE;
4269 ftpc->cwdfail = FALSE;
4270
4271 /* url-decode ftp path before further evaluation */
4272 result = Curl_urldecode(ftp->path, 0, &rawPath, &pathLen, REJECT_CTRL);
4273 if(result) {
4274 failf(data, "path contains control characters");
4275 return result;
4276 }
4277
4278 switch(data->set.ftp_filemethod) {
4279 case FTPFILE_NOCWD: /* fastest, but less standard-compliant */
4280
4281 if((pathLen > 0) && (rawPath[pathLen - 1] != '/'))
4282 fileName = rawPath; /* this is a full file path */
4283 /*
4284 else: ftpc->file is not used anywhere other than for operations on
4285 a file. In other words, never for directory operations.
4286 So we can safely leave filename as NULL here and use it as a
4287 argument in dir/file decisions.
4288 */
4289 break;
4290
4291 case FTPFILE_SINGLECWD:
4292 slashPos = strrchr(rawPath, '/');
4293 if(slashPos) {
4294 /* get path before last slash, except for / */
4295 size_t dirlen = slashPos - rawPath;
4296 if(dirlen == 0)
4297 dirlen = 1;
4298
4299 ftpc->dirs = calloc(1, sizeof(ftpc->dirs[0]));
4300 if(!ftpc->dirs) {
4301 free(rawPath);
4302 return CURLE_OUT_OF_MEMORY;
4303 }
4304
4305 ftpc->dirs[0] = Curl_memdup0(rawPath, dirlen);
4306 if(!ftpc->dirs[0]) {
4307 free(rawPath);
4308 return CURLE_OUT_OF_MEMORY;
4309 }
4310
4311 ftpc->dirdepth = 1; /* we consider it to be a single dir */
4312 fileName = slashPos + 1; /* rest is file name */
4313 }
4314 else
4315 fileName = rawPath; /* file name only (or empty) */
4316 break;
4317
4318 default: /* allow pretty much anything */
4319 case FTPFILE_MULTICWD: {
4320 /* current position: begin of next path component */
4321 const char *curPos = rawPath;
4322
4323 /* number of entries allocated for the 'dirs' array */
4324 size_t dirAlloc = 0;
4325 const char *str = rawPath;
4326 for(; *str != 0; ++str)
4327 if(*str == '/')
4328 ++dirAlloc;
4329
4330 if(dirAlloc) {
4331 ftpc->dirs = calloc(dirAlloc, sizeof(ftpc->dirs[0]));
4332 if(!ftpc->dirs) {
4333 free(rawPath);
4334 return CURLE_OUT_OF_MEMORY;
4335 }
4336
4337 /* parse the URL path into separate path components */
4338 while((slashPos = strchr(curPos, '/'))) {
4339 size_t compLen = slashPos - curPos;
4340
4341 /* path starts with a slash: add that as a directory */
4342 if((compLen == 0) && (ftpc->dirdepth == 0))
4343 ++compLen;
4344
4345 /* we skip empty path components, like "x//y" since the FTP command
4346 CWD requires a parameter and a non-existent parameter a) doesn't
4347 work on many servers and b) has no effect on the others. */
4348 if(compLen > 0) {
4349 char *comp = Curl_memdup0(curPos, compLen);
4350 if(!comp) {
4351 free(rawPath);
4352 return CURLE_OUT_OF_MEMORY;
4353 }
4354 ftpc->dirs[ftpc->dirdepth++] = comp;
4355 }
4356 curPos = slashPos + 1;
4357 }
4358 }
4359 DEBUGASSERT((size_t)ftpc->dirdepth <= dirAlloc);
4360 fileName = curPos; /* the rest is the file name (or empty) */
4361 }
4362 break;
4363 } /* switch */
4364
4365 if(fileName && *fileName)
4366 ftpc->file = strdup(fileName);
4367 else
4368 ftpc->file = NULL; /* instead of point to a zero byte,
4369 we make it a NULL pointer */
4370
4371 if(data->state.upload && !ftpc->file && (ftp->transfer == PPTRANSFER_BODY)) {
4372 /* We need a file name when uploading. Return error! */
4373 failf(data, "Uploading to a URL without a file name");
4374 free(rawPath);
4375 return CURLE_URL_MALFORMAT;
4376 }
4377
4378 ftpc->cwddone = FALSE; /* default to not done */
4379
4380 if((data->set.ftp_filemethod == FTPFILE_NOCWD) && (rawPath[0] == '/'))
4381 ftpc->cwddone = TRUE; /* skip CWD for absolute paths */
4382 else { /* newly created FTP connections are already in entry path */
4383 const char *oldPath = conn->bits.reuse ? ftpc->prevpath : "";
4384 if(oldPath) {
4385 size_t n = pathLen;
4386 if(data->set.ftp_filemethod == FTPFILE_NOCWD)
4387 n = 0; /* CWD to entry for relative paths */
4388 else
4389 n -= ftpc->file?strlen(ftpc->file):0;
4390
4391 if((strlen(oldPath) == n) && !strncmp(rawPath, oldPath, n)) {
4392 infof(data, "Request has same path as previous transfer");
4393 ftpc->cwddone = TRUE;
4394 }
4395 }
4396 }
4397
4398 free(rawPath);
4399 return CURLE_OK;
4400 }
4401
4402 /* call this when the DO phase has completed */
ftp_dophase_done(struct Curl_easy * data,bool connected)4403 static CURLcode ftp_dophase_done(struct Curl_easy *data, bool connected)
4404 {
4405 struct connectdata *conn = data->conn;
4406 struct FTP *ftp = data->req.p.ftp;
4407 struct ftp_conn *ftpc = &conn->proto.ftpc;
4408
4409 if(connected) {
4410 int completed;
4411 CURLcode result = ftp_do_more(data, &completed);
4412
4413 if(result) {
4414 close_secondarysocket(data, conn);
4415 return result;
4416 }
4417 }
4418
4419 if(ftp->transfer != PPTRANSFER_BODY)
4420 /* no data to transfer */
4421 Curl_xfer_setup(data, -1, -1, FALSE, -1);
4422 else if(!connected)
4423 /* since we didn't connect now, we want do_more to get called */
4424 conn->bits.do_more = TRUE;
4425
4426 ftpc->ctl_valid = TRUE; /* seems good */
4427
4428 return CURLE_OK;
4429 }
4430
4431 /* called from multi.c while DOing */
ftp_doing(struct Curl_easy * data,bool * dophase_done)4432 static CURLcode ftp_doing(struct Curl_easy *data,
4433 bool *dophase_done)
4434 {
4435 CURLcode result = ftp_multi_statemach(data, dophase_done);
4436
4437 if(result)
4438 DEBUGF(infof(data, "DO phase failed"));
4439 else if(*dophase_done) {
4440 result = ftp_dophase_done(data, FALSE /* not connected */);
4441
4442 DEBUGF(infof(data, "DO phase is complete2"));
4443 }
4444 return result;
4445 }
4446
4447 /***********************************************************************
4448 *
4449 * ftp_regular_transfer()
4450 *
4451 * The input argument is already checked for validity.
4452 *
4453 * Performs all commands done before a regular transfer between a local and a
4454 * remote host.
4455 *
4456 * ftp->ctl_valid starts out as FALSE, and gets set to TRUE if we reach the
4457 * ftp_done() function without finding any major problem.
4458 */
4459 static
ftp_regular_transfer(struct Curl_easy * data,bool * dophase_done)4460 CURLcode ftp_regular_transfer(struct Curl_easy *data,
4461 bool *dophase_done)
4462 {
4463 CURLcode result = CURLE_OK;
4464 bool connected = FALSE;
4465 struct connectdata *conn = data->conn;
4466 struct ftp_conn *ftpc = &conn->proto.ftpc;
4467 data->req.size = -1; /* make sure this is unknown at this point */
4468
4469 Curl_pgrsSetUploadCounter(data, 0);
4470 Curl_pgrsSetDownloadCounter(data, 0);
4471 Curl_pgrsSetUploadSize(data, -1);
4472 Curl_pgrsSetDownloadSize(data, -1);
4473
4474 ftpc->ctl_valid = TRUE; /* starts good */
4475
4476 result = ftp_perform(data,
4477 &connected, /* have we connected after PASV/PORT */
4478 dophase_done); /* all commands in the DO-phase done? */
4479
4480 if(!result) {
4481
4482 if(!*dophase_done)
4483 /* the DO phase has not completed yet */
4484 return CURLE_OK;
4485
4486 result = ftp_dophase_done(data, connected);
4487
4488 if(result)
4489 return result;
4490 }
4491 else
4492 freedirs(ftpc);
4493
4494 return result;
4495 }
4496
ftp_setup_connection(struct Curl_easy * data,struct connectdata * conn)4497 static CURLcode ftp_setup_connection(struct Curl_easy *data,
4498 struct connectdata *conn)
4499 {
4500 char *type;
4501 struct FTP *ftp;
4502 CURLcode result = CURLE_OK;
4503 struct ftp_conn *ftpc = &conn->proto.ftpc;
4504
4505 ftp = calloc(1, sizeof(struct FTP));
4506 if(!ftp)
4507 return CURLE_OUT_OF_MEMORY;
4508
4509 /* clone connection related data that is FTP specific */
4510 if(data->set.str[STRING_FTP_ACCOUNT]) {
4511 ftpc->account = strdup(data->set.str[STRING_FTP_ACCOUNT]);
4512 if(!ftpc->account) {
4513 free(ftp);
4514 return CURLE_OUT_OF_MEMORY;
4515 }
4516 }
4517 if(data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]) {
4518 ftpc->alternative_to_user =
4519 strdup(data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]);
4520 if(!ftpc->alternative_to_user) {
4521 Curl_safefree(ftpc->account);
4522 free(ftp);
4523 return CURLE_OUT_OF_MEMORY;
4524 }
4525 }
4526 data->req.p.ftp = ftp;
4527
4528 ftp->path = &data->state.up.path[1]; /* don't include the initial slash */
4529
4530 /* FTP URLs support an extension like ";type=<typecode>" that
4531 * we'll try to get now! */
4532 type = strstr(ftp->path, ";type=");
4533
4534 if(!type)
4535 type = strstr(conn->host.rawalloc, ";type=");
4536
4537 if(type) {
4538 char command;
4539 *type = 0; /* it was in the middle of the hostname */
4540 command = Curl_raw_toupper(type[6]);
4541
4542 switch(command) {
4543 case 'A': /* ASCII mode */
4544 data->state.prefer_ascii = TRUE;
4545 break;
4546
4547 case 'D': /* directory mode */
4548 data->state.list_only = TRUE;
4549 break;
4550
4551 case 'I': /* binary mode */
4552 default:
4553 /* switch off ASCII */
4554 data->state.prefer_ascii = FALSE;
4555 break;
4556 }
4557 }
4558
4559 /* get some initial data into the ftp struct */
4560 ftp->transfer = PPTRANSFER_BODY;
4561 ftp->downloadsize = 0;
4562 ftpc->known_filesize = -1; /* unknown size for now */
4563 ftpc->use_ssl = data->set.use_ssl;
4564 ftpc->ccc = data->set.ftp_ccc;
4565
4566 return result;
4567 }
4568
4569 #endif /* CURL_DISABLE_FTP */
4570