VirtualBox

source: vbox/trunk/src/libs/curl-7.83.1/lib/ftp.c@ 97623

Last change on this file since 97623 was 95312, checked in by vboxsync, 3 years ago

libs/{curl,libxml2}: OSE export fixes, bugref:8515

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

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