VirtualBox

source: vbox/trunk/src/libs/curl-8.7.1/lib/ftp.c@ 105284

Last change on this file since 105284 was 104083, checked in by vboxsync, 11 months ago

curl-8.7.1: Applied and adjusted our curl changes to 8.4.0. bugref:10639

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

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette