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