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