VirtualBox

source: vbox/trunk/src/libs/curl-7.87.0/lib/ftp.c@ 98334

Last change on this file since 98334 was 98326, checked in by vboxsync, 2 years ago

curl-7.87.0: Applied and adjusted our curl changes to 7.83.1. bugref:10356

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

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