VirtualBox

source: vbox/trunk/src/libs/curl-8.0.1/lib/ftp.c@ 99371

Last change on this file since 99371 was 99344, checked in by vboxsync, 2 years ago

curl-8.0.1: Applied and adjusted our curl changes to 7.87.0 bugref:10417

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

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