VirtualBox

source: vbox/trunk/src/libs/curl-8.3.0/lib/ftp.c@ 101127

Last change on this file since 101127 was 101127, checked in by vboxsync, 19 months ago

curl-8.3.0: Applied and adjusted our curl changes to 8.0.1. bugref:10526

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