Initial commit.
[CMakeLuaTailorHgBridge.git] / CMakeLua / Utilities / cmcurl / ftp.c
blob70ad32c63e739d6ec88369f5c4cee9af326b58ef
1 /***************************************************************************
2 * _ _ ____ _
3 * Project ___| | | | _ \| |
4 * / __| | | | |_) | |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2007, Daniel Stenberg, <daniel@haxx.se>, et al.
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 http://curl.haxx.se/docs/copyright.html.
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.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
21 * $Id: ftp.c,v 1.3 2007/11/05 19:34:36 king Exp $
22 ***************************************************************************/
24 #include "setup.h"
26 #ifndef CURL_DISABLE_FTP
27 #include <stdio.h>
28 #include <string.h>
29 #include <stdlib.h>
30 #include <stdarg.h>
31 #include <ctype.h>
33 #ifdef HAVE_UNISTD_H
34 #include <unistd.h>
35 #endif
37 #ifdef WIN32
39 #else /* probably some kind of unix */
40 #ifdef HAVE_SYS_SOCKET_H
41 #include <sys/socket.h>
42 #endif
43 #include <sys/types.h>
44 #ifdef HAVE_NETINET_IN_H
45 #include <netinet/in.h>
46 #endif
47 #ifdef HAVE_ARPA_INET_H
48 #include <arpa/inet.h>
49 #endif
50 #ifdef HAVE_UTSNAME_H
51 #include <sys/utsname.h>
52 #endif
53 #ifdef HAVE_NETDB_H
54 #include <netdb.h>
55 #endif
56 #ifdef VMS
57 #include <in.h>
58 #include <inet.h>
59 #endif
60 #endif
62 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
63 #undef in_addr_t
64 #define in_addr_t unsigned long
65 #endif
67 #include <curl/curl.h>
68 #include "urldata.h"
69 #include "sendf.h"
70 #include "easyif.h" /* for Curl_convert_... prototypes */
72 #include "if2ip.h"
73 #include "hostip.h"
74 #include "progress.h"
75 #include "transfer.h"
76 #include "escape.h"
77 #include "http.h" /* for HTTP proxy tunnel stuff */
78 #include "ftp.h"
80 #ifdef HAVE_KRB4
81 #include "krb4.h"
82 #endif
84 #include "strtoofft.h"
85 #include "strequal.h"
86 #include "sslgen.h"
87 #include "connect.h"
88 #include "strerror.h"
89 #include "memory.h"
90 #include "inet_ntop.h"
91 #include "select.h"
92 #include "parsedate.h" /* for the week day and month names */
93 #include "sockaddr.h" /* required for Curl_sockaddr_storage */
94 #include "multiif.h"
96 #if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
97 #include "inet_ntoa_r.h"
98 #endif
100 #define _MPRINTF_REPLACE /* use our functions only */
101 #include <curl/mprintf.h>
103 /* The last #include file should be: */
104 #ifdef CURLDEBUG
105 #include "memdebug.h"
106 #endif
108 #ifdef HAVE_NI_WITHSCOPEID
109 #define NIFLAGS NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID
110 #else
111 #define NIFLAGS NI_NUMERICHOST | NI_NUMERICSERV
112 #endif
114 /* Local API functions */
115 static CURLcode ftp_sendquote(struct connectdata *conn,
116 struct curl_slist *quote);
117 static CURLcode ftp_quit(struct connectdata *conn);
118 static CURLcode ftp_parse_url_path(struct connectdata *conn);
119 static CURLcode ftp_regular_transfer(struct connectdata *conn, bool *done);
120 static void ftp_pasv_verbose(struct connectdata *conn,
121 Curl_addrinfo *ai,
122 char *newhost, /* ascii version */
123 int port);
124 static CURLcode ftp_state_post_rest(struct connectdata *conn);
125 static CURLcode ftp_state_post_cwd(struct connectdata *conn);
126 static CURLcode ftp_state_quote(struct connectdata *conn,
127 bool init, ftpstate instate);
128 static CURLcode ftp_nb_type(struct connectdata *conn,
129 bool ascii, ftpstate state);
130 static int ftp_need_type(struct connectdata *conn,
131 bool ascii);
133 /* easy-to-use macro: */
134 #define FTPSENDF(x,y,z) if ((result = Curl_ftpsendf(x,y,z)) != CURLE_OK) \
135 return result
136 #define NBFTPSENDF(x,y,z) if ((result = Curl_nbftpsendf(x,y,z)) != CURLE_OK) \
137 return result
139 static void freedirs(struct connectdata *conn)
141 struct ftp_conn *ftpc = &conn->proto.ftpc;
142 struct FTP *ftp = conn->data->reqdata.proto.ftp;
144 int i;
145 if(ftpc->dirs) {
146 for (i=0; i < ftpc->dirdepth; i++){
147 if(ftpc->dirs[i]) {
148 free(ftpc->dirs[i]);
149 ftpc->dirs[i]=NULL;
152 free(ftpc->dirs);
153 ftpc->dirs = NULL;
155 if(ftp->file) {
156 free(ftp->file);
157 ftp->file = NULL;
161 /* Returns non-zero if the given string contains CR (\r) or LF (\n),
162 which are not allowed within RFC 959 <string>.
163 Note: The input string is in the client's encoding which might
164 not be ASCII, so escape sequences \r & \n must be used instead
165 of hex values 0x0d & 0x0a.
167 static bool isBadFtpString(const char *string)
169 return (bool)((NULL != strchr(string, '\r')) || (NULL != strchr(string, '\n')));
172 /***********************************************************************
174 * AllowServerConnect()
176 * When we've issue the PORT command, we have told the server to connect
177 * to us. This function will sit and wait here until the server has
178 * connected.
181 static CURLcode AllowServerConnect(struct connectdata *conn)
183 int timeout_ms;
184 struct SessionHandle *data = conn->data;
185 curl_socket_t sock = conn->sock[SECONDARYSOCKET];
186 struct timeval now = Curl_tvnow();
187 long timespent = Curl_tvdiff(Curl_tvnow(), now)/1000;
188 long timeout = data->set.connecttimeout?data->set.connecttimeout:
189 (data->set.timeout?data->set.timeout: 0);
191 if(timeout) {
192 timeout -= timespent;
193 if(timeout<=0) {
194 failf(data, "Timed out before server could connect to us");
195 return CURLE_OPERATION_TIMEDOUT;
199 /* We allow the server 60 seconds to connect to us, or a custom timeout.
200 Note the typecast here. */
201 timeout_ms = (timeout?(int)timeout:60) * 1000;
203 switch (Curl_select(sock, CURL_SOCKET_BAD, timeout_ms)) {
204 case -1: /* error */
205 /* let's die here */
206 failf(data, "Error while waiting for server connect");
207 return CURLE_FTP_PORT_FAILED;
208 case 0: /* timeout */
209 /* let's die here */
210 failf(data, "Timeout while waiting for server connect");
211 return CURLE_FTP_PORT_FAILED;
212 default:
213 /* we have received data here */
215 curl_socket_t s = CURL_SOCKET_BAD;
216 #ifdef ENABLE_IPV6
217 struct Curl_sockaddr_storage add;
218 #else
219 struct sockaddr_in add;
220 #endif
221 socklen_t size = (socklen_t) sizeof(add);
223 if(0 == getsockname(sock, (struct sockaddr *) &add, &size)) {
224 size = sizeof(add);
226 s=accept(sock, (struct sockaddr *) &add, &size);
228 sclose(sock); /* close the first socket */
230 if (CURL_SOCKET_BAD == s) {
231 /* DIE! */
232 failf(data, "Error accept()ing server connect");
233 return CURLE_FTP_PORT_FAILED;
235 infof(data, "Connection accepted from server\n");
237 conn->sock[SECONDARYSOCKET] = s;
238 Curl_nonblock(s, TRUE); /* enable non-blocking */
240 break;
243 return CURLE_OK;
246 /* initialize stuff to prepare for reading a fresh new response */
247 static void ftp_respinit(struct connectdata *conn)
249 struct ftp_conn *ftpc = &conn->proto.ftpc;
250 ftpc->nread_resp = 0;
251 ftpc->linestart_resp = conn->data->state.buffer;
254 /* macro to check for the last line in an FTP server response */
255 #define lastline(line) (ISDIGIT(line[0]) && ISDIGIT(line[1]) && \
256 ISDIGIT(line[2]) && (' ' == line[3]))
258 static CURLcode ftp_readresp(curl_socket_t sockfd,
259 struct connectdata *conn,
260 int *ftpcode, /* return the ftp-code if done */
261 size_t *size) /* size of the response */
263 int perline; /* count bytes per line */
264 bool keepon=TRUE;
265 ssize_t gotbytes;
266 char *ptr;
267 struct SessionHandle *data = conn->data;
268 char *buf = data->state.buffer;
269 CURLcode result = CURLE_OK;
270 struct ftp_conn *ftpc = &conn->proto.ftpc;
271 int code = 0;
273 if (ftpcode)
274 *ftpcode = 0; /* 0 for errors or not done */
276 ptr=buf + ftpc->nread_resp;
278 perline= (int)(ptr-ftpc->linestart_resp); /* number of bytes in the current
279 line, so far */
280 keepon=TRUE;
282 while((ftpc->nread_resp<BUFSIZE) && (keepon && !result)) {
284 if(ftpc->cache) {
285 /* we had data in the "cache", copy that instead of doing an actual
286 * read
288 * ftp->cache_size is cast to int here. This should be safe,
289 * because it would have been populated with something of size
290 * int to begin with, even though its datatype may be larger
291 * than an int.
293 memcpy(ptr, ftpc->cache, (int)ftpc->cache_size);
294 gotbytes = (int)ftpc->cache_size;
295 free(ftpc->cache); /* free the cache */
296 ftpc->cache = NULL; /* clear the pointer */
297 ftpc->cache_size = 0; /* zero the size just in case */
299 else {
300 int res = Curl_read(conn, sockfd, ptr, BUFSIZE-ftpc->nread_resp,
301 &gotbytes);
302 if(res < 0)
303 /* EWOULDBLOCK */
304 return CURLE_OK; /* return */
306 #ifdef CURL_DOES_CONVERSIONS
307 if((res == CURLE_OK) && (gotbytes > 0)) {
308 /* convert from the network encoding */
309 result = res = Curl_convert_from_network(data, ptr, gotbytes);
310 /* Curl_convert_from_network calls failf if unsuccessful */
312 #endif /* CURL_DOES_CONVERSIONS */
314 if(CURLE_OK != res)
315 keepon = FALSE;
318 if(!keepon)
320 else if(gotbytes <= 0) {
321 keepon = FALSE;
322 result = CURLE_RECV_ERROR;
323 failf(data, "FTP response reading failed");
325 else {
326 /* we got a whole chunk of data, which can be anything from one
327 * byte to a set of lines and possible just a piece of the last
328 * line */
329 int i;
331 conn->headerbytecount += gotbytes;
333 ftpc->nread_resp += gotbytes;
334 for(i = 0; i < gotbytes; ptr++, i++) {
335 perline++;
336 if(*ptr=='\n') {
337 /* a newline is CRLF in ftp-talk, so the CR is ignored as
338 the line isn't really terminated until the LF comes */
340 /* output debug output if that is requested */
341 if(data->set.verbose)
342 Curl_debug(data, CURLINFO_HEADER_IN,
343 ftpc->linestart_resp, (size_t)perline, conn);
346 * We pass all response-lines to the callback function registered
347 * for "headers". The response lines can be seen as a kind of
348 * headers.
350 result = Curl_client_write(conn, CLIENTWRITE_HEADER,
351 ftpc->linestart_resp, perline);
352 if(result)
353 return result;
355 if(perline>3 && lastline(ftpc->linestart_resp)) {
356 /* This is the end of the last line, copy the last line to the
357 start of the buffer and zero terminate, for old times sake (and
358 krb4)! */
359 char *meow;
360 int n;
361 for(meow=ftpc->linestart_resp, n=0; meow<ptr; meow++, n++)
362 buf[n] = *meow;
363 *meow=0; /* zero terminate */
364 keepon=FALSE;
365 ftpc->linestart_resp = ptr+1; /* advance pointer */
366 i++; /* skip this before getting out */
368 *size = ftpc->nread_resp; /* size of the response */
369 ftpc->nread_resp = 0; /* restart */
370 break;
372 perline=0; /* line starts over here */
373 ftpc->linestart_resp = ptr+1;
376 if(!keepon && (i != gotbytes)) {
377 /* We found the end of the response lines, but we didn't parse the
378 full chunk of data we have read from the server. We therefore need
379 to store the rest of the data to be checked on the next invoke as
380 it may actually contain another end of response already! */
381 ftpc->cache_size = gotbytes - i;
382 ftpc->cache = (char *)malloc((int)ftpc->cache_size);
383 if(ftpc->cache)
384 memcpy(ftpc->cache, ftpc->linestart_resp, (int)ftpc->cache_size);
385 else
386 return CURLE_OUT_OF_MEMORY; /**BANG**/
388 } /* there was data */
390 } /* while there's buffer left and loop is requested */
392 if(!result)
393 code = atoi(buf);
395 #ifdef HAVE_KRB4
396 /* handle the security-oriented responses 6xx ***/
397 /* FIXME: some errorchecking perhaps... ***/
398 switch(code) {
399 case 631:
400 Curl_sec_read_msg(conn, buf, prot_safe);
401 break;
402 case 632:
403 Curl_sec_read_msg(conn, buf, prot_private);
404 break;
405 case 633:
406 Curl_sec_read_msg(conn, buf, prot_confidential);
407 break;
408 default:
409 /* normal ftp stuff we pass through! */
410 break;
412 #endif
414 *ftpcode=code; /* return the initial number like this */
417 /* store the latest code for later retrieval */
418 conn->data->info.httpcode=code;
420 return result;
423 /* --- parse FTP server responses --- */
426 * Curl_GetFTPResponse() is supposed to be invoked after each command sent to
427 * a remote FTP server. This function will wait and read all lines of the
428 * response and extract the relevant return code for the invoking function.
431 CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */
432 struct connectdata *conn,
433 int *ftpcode) /* return the ftp-code */
436 * We cannot read just one byte per read() and then go back to select() as
437 * the OpenSSL read() doesn't grok that properly.
439 * Alas, read as much as possible, split up into lines, use the ending
440 * line in a response or continue reading. */
442 curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
443 int perline; /* count bytes per line */
444 bool keepon=TRUE;
445 ssize_t gotbytes;
446 char *ptr;
447 long timeout; /* timeout in seconds */
448 int interval_ms;
449 struct SessionHandle *data = conn->data;
450 char *line_start;
451 int code=0; /* default ftp "error code" to return */
452 char *buf = data->state.buffer;
453 CURLcode result = CURLE_OK;
454 struct ftp_conn *ftpc = &conn->proto.ftpc;
455 struct timeval now = Curl_tvnow();
457 if (ftpcode)
458 *ftpcode = 0; /* 0 for errors */
460 ptr=buf;
461 line_start = buf;
463 *nreadp=0;
464 perline=0;
465 keepon=TRUE;
467 while((*nreadp<BUFSIZE) && (keepon && !result)) {
468 /* check and reset timeout value every lap */
469 if(data->set.ftp_response_timeout )
470 /* if CURLOPT_FTP_RESPONSE_TIMEOUT is set, use that to determine
471 remaining time. Also, use "now" as opposed to "conn->now"
472 because ftp_response_timeout is only supposed to govern
473 the response for any given ftp response, not for the time
474 from connect to the given ftp response. */
475 timeout = data->set.ftp_response_timeout - /* timeout time */
476 Curl_tvdiff(Curl_tvnow(), now)/1000; /* spent time */
477 else if(data->set.timeout)
478 /* if timeout is requested, find out how much remaining time we have */
479 timeout = data->set.timeout - /* timeout time */
480 Curl_tvdiff(Curl_tvnow(), conn->now)/1000; /* spent time */
481 else
482 /* Even without a requested timeout, we only wait response_time
483 seconds for the full response to arrive before we bail out */
484 timeout = ftpc->response_time -
485 Curl_tvdiff(Curl_tvnow(), now)/1000; /* spent time */
487 if(timeout <=0 ) {
488 failf(data, "FTP response timeout");
489 return CURLE_OPERATION_TIMEDOUT; /* already too little time */
492 if(!ftpc->cache) {
493 interval_ms = 1 * 1000; /* use 1 second timeout intervals */
495 switch (Curl_select(sockfd, CURL_SOCKET_BAD, interval_ms)) {
496 case -1: /* select() error, stop reading */
497 result = CURLE_RECV_ERROR;
498 failf(data, "FTP response aborted due to select() error: %d",
499 Curl_sockerrno());
500 break;
501 case 0: /* timeout */
502 if(Curl_pgrsUpdate(conn))
503 return CURLE_ABORTED_BY_CALLBACK;
504 continue; /* just continue in our loop for the timeout duration */
506 default:
507 break;
510 if(CURLE_OK == result) {
512 * This code previously didn't use the kerberos sec_read() code
513 * to read, but when we use Curl_read() it may do so. Do confirm
514 * that this is still ok and then remove this comment!
516 if(ftpc->cache) {
517 /* we had data in the "cache", copy that instead of doing an actual
518 * read
520 * Dave Meyer, December 2003:
521 * ftp->cache_size is cast to int here. This should be safe,
522 * because it would have been populated with something of size
523 * int to begin with, even though its datatype may be larger
524 * than an int.
526 memcpy(ptr, ftpc->cache, (int)ftpc->cache_size);
527 gotbytes = (int)ftpc->cache_size;
528 free(ftpc->cache); /* free the cache */
529 ftpc->cache = NULL; /* clear the pointer */
530 ftpc->cache_size = 0; /* zero the size just in case */
532 else {
533 int res = Curl_read(conn, sockfd, ptr, BUFSIZE-*nreadp, &gotbytes);
534 if(res < 0)
535 /* EWOULDBLOCK */
536 continue; /* go looping again */
538 #ifdef CURL_DOES_CONVERSIONS
539 if((res == CURLE_OK) && (gotbytes > 0)) {
540 /* convert from the network encoding */
541 result = res = Curl_convert_from_network(data, ptr, gotbytes);
542 /* Curl_convert_from_network calls failf if unsuccessful */
544 #endif /* CURL_DOES_CONVERSIONS */
546 if(CURLE_OK != res)
547 keepon = FALSE;
550 if(!keepon)
552 else if(gotbytes <= 0) {
553 keepon = FALSE;
554 result = CURLE_RECV_ERROR;
555 failf(data, "FTP response reading failed");
557 else {
558 /* we got a whole chunk of data, which can be anything from one
559 * byte to a set of lines and possible just a piece of the last
560 * line */
561 int i;
563 conn->headerbytecount += gotbytes;
565 *nreadp += gotbytes;
566 for(i = 0; i < gotbytes; ptr++, i++) {
567 perline++;
568 if(*ptr=='\n') {
569 /* a newline is CRLF in ftp-talk, so the CR is ignored as
570 the line isn't really terminated until the LF comes */
572 /* output debug output if that is requested */
573 if(data->set.verbose)
574 Curl_debug(data, CURLINFO_HEADER_IN,
575 line_start, (size_t)perline, conn);
578 * We pass all response-lines to the callback function registered
579 * for "headers". The response lines can be seen as a kind of
580 * headers.
582 result = Curl_client_write(conn, CLIENTWRITE_HEADER,
583 line_start, perline);
584 if(result)
585 return result;
587 if(perline>3 && lastline(line_start)) {
588 /* This is the end of the last line, copy the last
589 * line to the start of the buffer and zero terminate,
590 * for old times sake (and krb4)! */
591 char *meow;
592 int n;
593 for(meow=line_start, n=0; meow<ptr; meow++, n++)
594 buf[n] = *meow;
595 *meow=0; /* zero terminate */
596 keepon=FALSE;
597 line_start = ptr+1; /* advance pointer */
598 i++; /* skip this before getting out */
599 break;
601 perline=0; /* line starts over here */
602 line_start = ptr+1;
605 if(!keepon && (i != gotbytes)) {
606 /* We found the end of the response lines, but we didn't parse the
607 full chunk of data we have read from the server. We therefore
608 need to store the rest of the data to be checked on the next
609 invoke as it may actually contain another end of response
610 already! Cleverly figured out by Eric Lavigne in December
611 2001. */
612 ftpc->cache_size = gotbytes - i;
613 ftpc->cache = (char *)malloc((int)ftpc->cache_size);
614 if(ftpc->cache)
615 memcpy(ftpc->cache, line_start, (int)ftpc->cache_size);
616 else
617 return CURLE_OUT_OF_MEMORY; /**BANG**/
619 } /* there was data */
620 } /* if(no error) */
621 } /* while there's buffer left and loop is requested */
623 if(!result)
624 code = atoi(buf);
626 #ifdef HAVE_KRB4
627 /* handle the security-oriented responses 6xx ***/
628 /* FIXME: some errorchecking perhaps... ***/
629 switch(code) {
630 case 631:
631 Curl_sec_read_msg(conn, buf, prot_safe);
632 break;
633 case 632:
634 Curl_sec_read_msg(conn, buf, prot_private);
635 break;
636 case 633:
637 Curl_sec_read_msg(conn, buf, prot_confidential);
638 break;
639 default:
640 /* normal ftp stuff we pass through! */
641 break;
643 #endif
645 if(ftpcode)
646 *ftpcode=code; /* return the initial number like this */
648 /* store the latest code for later retrieval */
649 conn->data->info.httpcode=code;
651 return result;
654 /* This is the ONLY way to change FTP state! */
655 static void state(struct connectdata *conn,
656 ftpstate state)
658 #ifdef CURLDEBUG
659 /* for debug purposes */
660 const char *names[]={
661 "STOP",
662 "WAIT220",
663 "AUTH",
664 "USER",
665 "PASS",
666 "ACCT",
667 "PBSZ",
668 "PROT",
669 "CCC",
670 "PWD",
671 "QUOTE",
672 "RETR_PREQUOTE",
673 "STOR_PREQUOTE",
674 "POSTQUOTE",
675 "CWD",
676 "MKD",
677 "MDTM",
678 "TYPE",
679 "LIST_TYPE",
680 "RETR_TYPE",
681 "STOR_TYPE",
682 "SIZE",
683 "RETR_SIZE",
684 "STOR_SIZE",
685 "REST",
686 "RETR_REST",
687 "PORT",
688 "PASV",
689 "LIST",
690 "RETR",
691 "STOR",
692 "QUIT"
694 #endif
695 struct ftp_conn *ftpc = &conn->proto.ftpc;
696 #ifdef CURLDEBUG
697 if(ftpc->state != state)
698 infof(conn->data, "FTP %p state change from %s to %s\n",
699 ftpc, names[ftpc->state], names[state]);
700 #endif
701 ftpc->state = state;
704 static CURLcode ftp_state_user(struct connectdata *conn)
706 CURLcode result;
707 struct FTP *ftp = conn->data->reqdata.proto.ftp;
708 /* send USER */
709 NBFTPSENDF(conn, "USER %s", ftp->user?ftp->user:"");
711 state(conn, FTP_USER);
712 conn->data->state.ftp_trying_alternative = FALSE;
714 return CURLE_OK;
717 static CURLcode ftp_state_pwd(struct connectdata *conn)
719 CURLcode result;
721 /* send PWD to discover our entry point */
722 NBFTPSENDF(conn, "PWD", NULL);
723 state(conn, FTP_PWD);
725 return CURLE_OK;
728 /* For the FTP "protocol connect" and "doing" phases only */
729 int Curl_ftp_getsock(struct connectdata *conn,
730 curl_socket_t *socks,
731 int numsocks)
733 struct ftp_conn *ftpc = &conn->proto.ftpc;
735 if(!numsocks)
736 return GETSOCK_BLANK;
738 socks[0] = conn->sock[FIRSTSOCKET];
740 if(ftpc->sendleft) {
741 /* write mode */
742 return GETSOCK_WRITESOCK(0);
745 /* read mode */
746 return GETSOCK_READSOCK(0);
749 /* This is called after the FTP_QUOTE state is passed.
751 ftp_state_cwd() sends the range of PWD commands to the server to change to
752 the correct directory. It may also need to send MKD commands to create
753 missing ones, if that option is enabled.
755 static CURLcode ftp_state_cwd(struct connectdata *conn)
757 CURLcode result = CURLE_OK;
758 struct ftp_conn *ftpc = &conn->proto.ftpc;
760 if(ftpc->cwddone)
761 /* already done and fine */
762 result = ftp_state_post_cwd(conn);
763 else {
764 ftpc->count2 = 0;
765 if (conn->bits.reuse && ftpc->entrypath) {
766 /* This is a re-used connection. Since we change directory to where the
767 transfer is taking place, we must first get back to the original dir
768 where we ended up after login: */
769 ftpc->count1 = 0; /* we count this as the first path, then we add one
770 for all upcoming ones in the ftp->dirs[] array */
771 NBFTPSENDF(conn, "CWD %s", ftpc->entrypath);
772 state(conn, FTP_CWD);
774 else {
775 if(ftpc->dirdepth) {
776 ftpc->count1 = 1;
777 /* issue the first CWD, the rest is sent when the CWD responses are
778 received... */
779 NBFTPSENDF(conn, "CWD %s", ftpc->dirs[ftpc->count1 -1]);
780 state(conn, FTP_CWD);
782 else {
783 /* No CWD necessary */
784 result = ftp_state_post_cwd(conn);
788 return result;
791 typedef enum {
792 EPRT,
793 PORT,
794 DONE
795 } ftpport;
797 static CURLcode ftp_state_use_port(struct connectdata *conn,
798 ftpport fcmd) /* start with this */
801 CURLcode result = CURLE_OK;
802 struct ftp_conn *ftpc = &conn->proto.ftpc;
803 struct SessionHandle *data=conn->data;
804 curl_socket_t portsock= CURL_SOCKET_BAD;
805 char myhost[256] = "";
807 #ifdef ENABLE_IPV6
808 /******************************************************************
809 * IPv6-specific section
811 struct Curl_sockaddr_storage ss;
812 struct addrinfo *res, *ai;
813 socklen_t sslen;
814 char hbuf[NI_MAXHOST];
815 struct sockaddr *sa=(struct sockaddr *)&ss;
816 char tmp[1024];
817 const char *mode[] = { "EPRT", "PORT", NULL };
818 int rc;
819 int error;
820 char *host=NULL;
821 struct Curl_dns_entry *h=NULL;
822 unsigned short port = 0;
824 /* Step 1, figure out what address that is requested */
826 if(data->set.ftpport && (strlen(data->set.ftpport) > 1)) {
827 /* attempt to get the address of the given interface name */
828 if(!Curl_if2ip(data->set.ftpport, hbuf, sizeof(hbuf)))
829 /* not an interface, use the given string as host name instead */
830 host = data->set.ftpport;
831 else
832 host = hbuf; /* use the hbuf for host name */
833 } /* data->set.ftpport */
835 if(!host) {
836 /* not an interface and not a host name, get default by extracting
837 the IP from the control connection */
839 sslen = sizeof(ss);
840 if (getsockname(conn->sock[FIRSTSOCKET], (struct sockaddr *)&ss, &sslen)) {
841 failf(data, "getsockname() failed: %s",
842 Curl_strerror(conn, Curl_sockerrno()) );
843 return CURLE_FTP_PORT_FAILED;
846 if (sslen > (socklen_t)sizeof(ss))
847 sslen = sizeof(ss);
848 rc = getnameinfo((struct sockaddr *)&ss, sslen, hbuf, sizeof(hbuf), NULL,
849 0, NIFLAGS);
850 if(rc) {
851 failf(data, "getnameinfo() returned %d \n", rc);
852 return CURLE_FTP_PORT_FAILED;
854 host = hbuf; /* use this host name */
857 rc = Curl_resolv(conn, host, 0, &h);
858 if(rc == CURLRESOLV_PENDING)
859 rc = Curl_wait_for_resolv(conn, &h);
860 if(h) {
861 res = h->addr;
862 /* when we return from this function, we can forget about this entry
863 to we can unlock it now already */
864 Curl_resolv_unlock(data, h);
865 } /* (h) */
866 else
867 res = NULL; /* failure! */
870 /* step 2, create a socket for the requested address */
872 portsock = CURL_SOCKET_BAD;
873 error = 0;
874 for (ai = res; ai; ai = ai->ai_next) {
876 * Workaround for AIX5 getaddrinfo() problem (it doesn't set ai_socktype):
878 if (ai->ai_socktype == 0)
879 ai->ai_socktype = conn->socktype;
881 portsock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
882 if (portsock == CURL_SOCKET_BAD) {
883 error = Curl_sockerrno();
884 continue;
886 break;
888 if(!ai) {
889 failf(data, "socket failure: %s", Curl_strerror(conn, error));
890 return CURLE_FTP_PORT_FAILED;
893 /* step 3, bind to a suitable local address */
895 /* Try binding the given address. */
896 if (bind(portsock, ai->ai_addr, ai->ai_addrlen)) {
898 /* It failed. Bind the address used for the control connection instead */
899 sslen = sizeof(ss);
900 if (getsockname(conn->sock[FIRSTSOCKET],
901 (struct sockaddr *)sa, &sslen)) {
902 failf(data, "getsockname() failed: %s",
903 Curl_strerror(conn, Curl_sockerrno()) );
904 sclose(portsock);
905 return CURLE_FTP_PORT_FAILED;
908 /* set port number to zero to make bind() pick "any" */
909 if(((struct sockaddr *)sa)->sa_family == AF_INET)
910 ((struct sockaddr_in *)sa)->sin_port=0;
911 else
912 ((struct sockaddr_in6 *)sa)->sin6_port =0;
914 if (sslen > (socklen_t)sizeof(ss))
915 sslen = sizeof(ss);
917 if(bind(portsock, (struct sockaddr *)sa, sslen)) {
918 failf(data, "bind failed: %s", Curl_strerror(conn, Curl_sockerrno()));
919 sclose(portsock);
920 return CURLE_FTP_PORT_FAILED;
924 /* get the name again after the bind() so that we can extract the
925 port number it uses now */
926 sslen = sizeof(ss);
927 if(getsockname(portsock, (struct sockaddr *)sa, &sslen)) {
928 failf(data, "getsockname() failed: %s",
929 Curl_strerror(conn, Curl_sockerrno()) );
930 sclose(portsock);
931 return CURLE_FTP_PORT_FAILED;
934 /* step 4, listen on the socket */
936 if (listen(portsock, 1)) {
937 failf(data, "socket failure: %s", Curl_strerror(conn, Curl_sockerrno()));
938 sclose(portsock);
939 return CURLE_FTP_PORT_FAILED;
942 /* step 5, send the proper FTP command */
944 /* get a plain printable version of the numerical address to work with
945 below */
946 Curl_printable_address(ai, myhost, sizeof(myhost));
948 #ifdef PF_INET6
949 if(!conn->bits.ftp_use_eprt && conn->bits.ipv6)
950 /* EPRT is disabled but we are connected to a IPv6 host, so we ignore the
951 request and enable EPRT again! */
952 conn->bits.ftp_use_eprt = TRUE;
953 #endif
955 for (; fcmd != DONE; fcmd++) {
957 if(!conn->bits.ftp_use_eprt && (EPRT == fcmd))
958 /* if disabled, goto next */
959 continue;
961 switch (sa->sa_family) {
962 case AF_INET:
963 port = ntohs(((struct sockaddr_in *)sa)->sin_port);
964 break;
965 case AF_INET6:
966 port = ntohs(((struct sockaddr_in6 *)sa)->sin6_port);
967 break;
968 default:
969 break;
972 if (EPRT == fcmd) {
974 * Two fine examples from RFC2428;
976 * EPRT |1|132.235.1.2|6275|
978 * EPRT |2|1080::8:800:200C:417A|5282|
981 result = Curl_nbftpsendf(conn, "%s |%d|%s|%d|", mode[fcmd],
982 ai->ai_family == AF_INET?1:2,
983 myhost, port);
984 if(result)
985 return result;
986 break;
988 else if (PORT == fcmd) {
989 char *source = myhost;
990 char *dest = tmp;
992 if ((PORT == fcmd) && ai->ai_family != AF_INET)
993 continue;
995 /* translate x.x.x.x to x,x,x,x */
996 while(source && *source) {
997 if(*source == '.')
998 *dest=',';
999 else
1000 *dest = *source;
1001 dest++;
1002 source++;
1004 *dest = 0;
1005 snprintf(dest, 20, ",%d,%d", port>>8, port&0xff);
1007 result = Curl_nbftpsendf(conn, "%s %s", mode[fcmd], tmp);
1008 if(result)
1009 return result;
1010 break;
1014 /* store which command was sent */
1015 ftpc->count1 = fcmd;
1017 /* we set the secondary socket variable to this for now, it is only so that
1018 the cleanup function will close it in case we fail before the true
1019 secondary stuff is made */
1020 if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET])
1021 sclose(conn->sock[SECONDARYSOCKET]);
1022 conn->sock[SECONDARYSOCKET] = portsock;
1024 #else
1025 /******************************************************************
1026 * IPv4-specific section
1028 struct sockaddr_in sa;
1029 unsigned short porttouse;
1030 bool sa_filled_in = FALSE;
1031 Curl_addrinfo *addr = NULL;
1032 unsigned short ip[4];
1033 bool freeaddr = TRUE;
1034 socklen_t sslen = sizeof(sa);
1036 (void)fcmd; /* not used in the IPv4 code */
1037 if(data->set.ftpport) {
1038 in_addr_t in;
1040 /* First check if the given name is an IP address */
1041 in=inet_addr(data->set.ftpport);
1043 if(in != CURL_INADDR_NONE)
1044 /* this is an IPv4 address */
1045 addr = Curl_ip2addr(in, data->set.ftpport, 0);
1046 else {
1047 if(Curl_if2ip(data->set.ftpport, myhost, sizeof(myhost))) {
1048 /* The interface to IP conversion provided a dotted address */
1049 in=inet_addr(myhost);
1050 addr = Curl_ip2addr(in, myhost, 0);
1052 else if(strlen(data->set.ftpport)> 1) {
1053 /* might be a host name! */
1054 struct Curl_dns_entry *h=NULL;
1055 int rc = Curl_resolv(conn, data->set.ftpport, 0, &h);
1056 if(rc == CURLRESOLV_PENDING)
1057 /* BLOCKING */
1058 rc = Curl_wait_for_resolv(conn, &h);
1059 if(h) {
1060 addr = h->addr;
1061 /* when we return from this function, we can forget about this entry
1062 so we can unlock it now already */
1063 Curl_resolv_unlock(data, h);
1065 freeaddr = FALSE; /* make sure we don't free 'addr' in this function
1066 since it points to a DNS cache entry! */
1067 } /* (h) */
1068 else {
1069 infof(data, "Failed to resolve host name %s\n", data->set.ftpport);
1071 } /* strlen */
1072 } /* CURL_INADDR_NONE */
1073 } /* data->set.ftpport */
1075 if(!addr) {
1076 /* pick a suitable default here */
1078 if (getsockname(conn->sock[FIRSTSOCKET],
1079 (struct sockaddr *)&sa, &sslen)) {
1080 failf(data, "getsockname() failed: %s",
1081 Curl_strerror(conn, Curl_sockerrno()) );
1082 return CURLE_FTP_PORT_FAILED;
1084 if (sslen > (socklen_t)sizeof(sa))
1085 sslen = sizeof(sa);
1087 sa_filled_in = TRUE; /* the sa struct is filled in */
1090 if (addr || sa_filled_in) {
1091 portsock = socket(AF_INET, SOCK_STREAM, 0);
1092 if(CURL_SOCKET_BAD != portsock) {
1094 /* we set the secondary socket variable to this for now, it
1095 is only so that the cleanup function will close it in case
1096 we fail before the true secondary stuff is made */
1097 if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET])
1098 sclose(conn->sock[SECONDARYSOCKET]);
1099 conn->sock[SECONDARYSOCKET] = portsock;
1101 if(!sa_filled_in) {
1102 memcpy(&sa, addr->ai_addr, sslen);
1103 sa.sin_addr.s_addr = INADDR_ANY;
1106 sa.sin_port = 0;
1107 sslen = sizeof(sa);
1109 if(bind(portsock, (struct sockaddr *)&sa, sslen) == 0) {
1110 /* we succeeded to bind */
1111 struct sockaddr_in add;
1112 socklen_t socksize = sizeof(add);
1114 if(getsockname(portsock, (struct sockaddr *) &add,
1115 &socksize)) {
1116 failf(data, "getsockname() failed: %s",
1117 Curl_strerror(conn, Curl_sockerrno()) );
1118 return CURLE_FTP_PORT_FAILED;
1120 porttouse = ntohs(add.sin_port);
1122 if ( listen(portsock, 1) < 0 ) {
1123 failf(data, "listen(2) failed on socket");
1124 return CURLE_FTP_PORT_FAILED;
1127 else {
1128 failf(data, "bind(2) failed on socket");
1129 return CURLE_FTP_PORT_FAILED;
1132 else {
1133 failf(data, "socket(2) failed (%s)");
1134 return CURLE_FTP_PORT_FAILED;
1137 else {
1138 failf(data, "couldn't find IP address to use");
1139 return CURLE_FTP_PORT_FAILED;
1142 if(sa_filled_in)
1143 Curl_inet_ntop(AF_INET, &((struct sockaddr_in *)&sa)->sin_addr,
1144 myhost, sizeof(myhost));
1145 else
1146 Curl_printable_address(addr, myhost, sizeof(myhost));
1148 if(4 == sscanf(myhost, "%hu.%hu.%hu.%hu",
1149 &ip[0], &ip[1], &ip[2], &ip[3])) {
1151 infof(data, "Telling server to connect to %d.%d.%d.%d:%d\n",
1152 ip[0], ip[1], ip[2], ip[3], porttouse);
1154 result=Curl_nbftpsendf(conn, "PORT %d,%d,%d,%d,%d,%d",
1155 ip[0], ip[1], ip[2], ip[3],
1156 porttouse >> 8, porttouse & 255);
1157 if(result)
1158 return result;
1160 else
1161 return CURLE_FTP_PORT_FAILED;
1163 if(freeaddr)
1164 Curl_freeaddrinfo(addr);
1166 ftpc->count1 = PORT;
1168 #endif /* end of ipv4-specific code */
1170 /* this tcpconnect assignment below is a hackish work-around to make the
1171 multi interface with active FTP work - as it will not wait for a
1172 (passive) connect in Curl_is_connected().
1174 The *proper* fix is to make sure that the active connection from the
1175 server is done in a non-blocking way. Currently, it is still BLOCKING.
1177 conn->bits.tcpconnect = TRUE;
1179 state(conn, FTP_PORT);
1180 return result;
1183 static CURLcode ftp_state_use_pasv(struct connectdata *conn)
1185 struct ftp_conn *ftpc = &conn->proto.ftpc;
1186 CURLcode result = CURLE_OK;
1188 Here's the excecutive summary on what to do:
1190 PASV is RFC959, expect:
1191 227 Entering Passive Mode (a1,a2,a3,a4,p1,p2)
1193 LPSV is RFC1639, expect:
1194 228 Entering Long Passive Mode (4,4,a1,a2,a3,a4,2,p1,p2)
1196 EPSV is RFC2428, expect:
1197 229 Entering Extended Passive Mode (|||port|)
1201 const char *mode[] = { "EPSV", "PASV", NULL };
1202 int modeoff;
1204 #ifdef PF_INET6
1205 if(!conn->bits.ftp_use_epsv && conn->bits.ipv6)
1206 /* EPSV is disabled but we are connected to a IPv6 host, so we ignore the
1207 request and enable EPSV again! */
1208 conn->bits.ftp_use_epsv = TRUE;
1209 #endif
1211 modeoff = conn->bits.ftp_use_epsv?0:1;
1213 result = Curl_nbftpsendf(conn, "%s", mode[modeoff]);
1214 if(result)
1215 return result;
1217 ftpc->count1 = modeoff;
1218 state(conn, FTP_PASV);
1219 infof(conn->data, "Connect data stream passively\n");
1221 return result;
1224 /* REST is the last command in the chain of commands when a "head"-like
1225 request is made. Thus, if an actual transfer is to be made this is where
1226 we take off for real. */
1227 static CURLcode ftp_state_post_rest(struct connectdata *conn)
1229 CURLcode result = CURLE_OK;
1230 struct FTP *ftp = conn->data->reqdata.proto.ftp;
1231 struct SessionHandle *data = conn->data;
1233 if(ftp->no_transfer || conn->bits.no_body) {
1234 /* doesn't transfer any data */
1235 ftp->no_transfer = TRUE;
1237 /* still possibly do PRE QUOTE jobs */
1238 state(conn, FTP_RETR_PREQUOTE);
1239 result = ftp_state_quote(conn, TRUE, FTP_RETR_PREQUOTE);
1241 else if(data->set.ftp_use_port) {
1242 /* We have chosen to use the PORT (or similar) command */
1243 result = ftp_state_use_port(conn, EPRT);
1245 else {
1246 /* We have chosen (this is default) to use the PASV (or similar) command */
1247 result = ftp_state_use_pasv(conn);
1249 return result;
1252 static CURLcode ftp_state_post_size(struct connectdata *conn)
1254 CURLcode result = CURLE_OK;
1255 struct FTP *ftp = conn->data->reqdata.proto.ftp;
1257 if(ftp->no_transfer) {
1258 /* if a "head"-like request is being made */
1260 /* Determine if server can respond to REST command and therefore
1261 whether it supports range */
1262 NBFTPSENDF(conn, "REST %d", 0);
1264 state(conn, FTP_REST);
1266 else
1267 result = ftp_state_post_rest(conn);
1269 return result;
1272 static CURLcode ftp_state_post_type(struct connectdata *conn)
1274 CURLcode result = CURLE_OK;
1275 struct FTP *ftp = conn->data->reqdata.proto.ftp;
1277 if(ftp->no_transfer) {
1278 /* if a "head"-like request is being made */
1280 /* we know ftp->file is a valid pointer to a file name */
1281 NBFTPSENDF(conn, "SIZE %s", ftp->file);
1283 state(conn, FTP_SIZE);
1285 else
1286 result = ftp_state_post_size(conn);
1288 return result;
1291 static CURLcode ftp_state_post_listtype(struct connectdata *conn)
1293 CURLcode result = CURLE_OK;
1294 struct SessionHandle *data = conn->data;
1296 /* If this output is to be machine-parsed, the NLST command might be better
1297 to use, since the LIST command output is not specified or standard in any
1298 way. It has turned out that the NLST list output is not the same on all
1299 servers either... */
1301 NBFTPSENDF(conn, "%s",
1302 data->set.customrequest?data->set.customrequest:
1303 (data->set.ftp_list_only?"NLST":"LIST"));
1305 state(conn, FTP_LIST);
1307 return result;
1310 static CURLcode ftp_state_post_retrtype(struct connectdata *conn)
1312 CURLcode result = CURLE_OK;
1314 /* We've sent the TYPE, now we must send the list of prequote strings */
1316 result = ftp_state_quote(conn, TRUE, FTP_RETR_PREQUOTE);
1318 return result;
1321 static CURLcode ftp_state_post_stortype(struct connectdata *conn)
1323 CURLcode result = CURLE_OK;
1325 /* We've sent the TYPE, now we must send the list of prequote strings */
1327 result = ftp_state_quote(conn, TRUE, FTP_STOR_PREQUOTE);
1329 return result;
1332 static CURLcode ftp_state_post_mdtm(struct connectdata *conn)
1334 CURLcode result = CURLE_OK;
1335 struct FTP *ftp = conn->data->reqdata.proto.ftp;
1336 struct SessionHandle *data = conn->data;
1338 /* If we have selected NOBODY and HEADER, it means that we only want file
1339 information. Which in FTP can't be much more than the file size and
1340 date. */
1341 if(conn->bits.no_body && data->set.include_header && ftp->file &&
1342 ftp_need_type(conn, data->set.prefer_ascii)) {
1343 /* The SIZE command is _not_ RFC 959 specified, and therefor many servers
1344 may not support it! It is however the only way we have to get a file's
1345 size! */
1347 ftp->no_transfer = TRUE; /* this means no actual transfer will be made */
1349 /* Some servers return different sizes for different modes, and thus we
1350 must set the proper type before we check the size */
1351 result = ftp_nb_type(conn, data->set.prefer_ascii, FTP_TYPE);
1352 if (result)
1353 return result;
1355 else
1356 result = ftp_state_post_type(conn);
1358 return result;
1361 /* This is called after the CWD commands have been done in the beginning of
1362 the DO phase */
1363 static CURLcode ftp_state_post_cwd(struct connectdata *conn)
1365 CURLcode result = CURLE_OK;
1366 struct FTP *ftp = conn->data->reqdata.proto.ftp;
1367 struct SessionHandle *data = conn->data;
1369 /* Requested time of file or time-depended transfer? */
1370 if((data->set.get_filetime || data->set.timecondition) && ftp->file) {
1372 /* we have requested to get the modified-time of the file, this is a white
1373 spot as the MDTM is not mentioned in RFC959 */
1374 NBFTPSENDF(conn, "MDTM %s", ftp->file);
1376 state(conn, FTP_MDTM);
1378 else
1379 result = ftp_state_post_mdtm(conn);
1381 return result;
1385 /* This is called after the TYPE and possible quote commands have been sent */
1386 static CURLcode ftp_state_ul_setup(struct connectdata *conn,
1387 bool sizechecked)
1389 CURLcode result = CURLE_OK;
1390 struct FTP *ftp = conn->data->reqdata.proto.ftp;
1391 struct SessionHandle *data = conn->data;
1392 curl_off_t passed=0;
1394 if((data->reqdata.resume_from && !sizechecked) ||
1395 ((data->reqdata.resume_from > 0) && sizechecked)) {
1396 /* we're about to continue the uploading of a file */
1397 /* 1. get already existing file's size. We use the SIZE command for this
1398 which may not exist in the server! The SIZE command is not in
1399 RFC959. */
1401 /* 2. This used to set REST. But since we can do append, we
1402 don't another ftp command. We just skip the source file
1403 offset and then we APPEND the rest on the file instead */
1405 /* 3. pass file-size number of bytes in the source file */
1406 /* 4. lower the infilesize counter */
1407 /* => transfer as usual */
1409 if(data->reqdata.resume_from < 0 ) {
1410 /* Got no given size to start from, figure it out */
1411 NBFTPSENDF(conn, "SIZE %s", ftp->file);
1412 state(conn, FTP_STOR_SIZE);
1413 return result;
1416 /* enable append */
1417 data->set.ftp_append = TRUE;
1419 /* Let's read off the proper amount of bytes from the input. If we knew it
1420 was a proper file we could've just fseek()ed but we only have a stream
1421 here */
1423 /* TODO: allow the ioctlfunction to provide a fast forward function that
1424 can be used here and use this method only as a fallback! */
1425 do {
1426 curl_off_t readthisamountnow = (data->reqdata.resume_from - passed);
1427 curl_off_t actuallyread;
1429 if(readthisamountnow > BUFSIZE)
1430 readthisamountnow = BUFSIZE;
1432 actuallyread = (curl_off_t)
1433 conn->fread(data->state.buffer, 1, (size_t)readthisamountnow,
1434 conn->fread_in);
1436 passed += actuallyread;
1437 if(actuallyread != readthisamountnow) {
1438 failf(data, "Could only read %" FORMAT_OFF_T
1439 " bytes from the input", passed);
1440 return CURLE_FTP_COULDNT_USE_REST;
1442 } while(passed != data->reqdata.resume_from);
1444 /* now, decrease the size of the read */
1445 if(data->set.infilesize>0) {
1446 data->set.infilesize -= data->reqdata.resume_from;
1448 if(data->set.infilesize <= 0) {
1449 infof(data, "File already completely uploaded\n");
1451 /* no data to transfer */
1452 result=Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1454 /* Set no_transfer so that we won't get any error in
1455 * Curl_ftp_done() because we didn't transfer anything! */
1456 ftp->no_transfer = TRUE;
1458 state(conn, FTP_STOP);
1459 return CURLE_OK;
1462 /* we've passed, proceed as normal */
1463 } /* resume_from */
1465 NBFTPSENDF(conn, data->set.ftp_append?"APPE %s":"STOR %s",
1466 ftp->file);
1468 state(conn, FTP_STOR);
1470 return result;
1473 static CURLcode ftp_state_quote(struct connectdata *conn,
1474 bool init,
1475 ftpstate instate)
1477 CURLcode result = CURLE_OK;
1478 struct SessionHandle *data = conn->data;
1479 struct FTP *ftp = data->reqdata.proto.ftp;
1480 struct ftp_conn *ftpc = &conn->proto.ftpc;
1481 bool quote=FALSE;
1482 struct curl_slist *item;
1484 switch(instate) {
1485 case FTP_QUOTE:
1486 default:
1487 item = data->set.quote;
1488 break;
1489 case FTP_RETR_PREQUOTE:
1490 case FTP_STOR_PREQUOTE:
1491 item = data->set.prequote;
1492 break;
1493 case FTP_POSTQUOTE:
1494 item = data->set.postquote;
1495 break;
1498 if(init)
1499 ftpc->count1 = 0;
1500 else
1501 ftpc->count1++;
1503 if(item) {
1504 int i = 0;
1506 /* Skip count1 items in the linked list */
1507 while((i< ftpc->count1) && item) {
1508 item = item->next;
1509 i++;
1511 if(item) {
1512 NBFTPSENDF(conn, "%s", item->data);
1513 state(conn, instate);
1514 quote = TRUE;
1518 if(!quote) {
1519 /* No more quote to send, continue to ... */
1520 switch(instate) {
1521 case FTP_QUOTE:
1522 default:
1523 result = ftp_state_cwd(conn);
1524 break;
1525 case FTP_RETR_PREQUOTE:
1526 if (ftp->no_transfer)
1527 state(conn, FTP_STOP);
1528 else {
1529 NBFTPSENDF(conn, "SIZE %s", ftp->file);
1530 state(conn, FTP_RETR_SIZE);
1532 break;
1533 case FTP_STOR_PREQUOTE:
1534 result = ftp_state_ul_setup(conn, FALSE);
1535 break;
1536 case FTP_POSTQUOTE:
1537 break;
1541 return result;
1544 static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
1545 int ftpcode)
1547 struct ftp_conn *ftpc = &conn->proto.ftpc;
1548 CURLcode result;
1549 struct SessionHandle *data=conn->data;
1550 Curl_addrinfo *conninfo;
1551 struct Curl_dns_entry *addr=NULL;
1552 int rc;
1553 unsigned short connectport; /* the local port connect() should use! */
1554 unsigned short newport=0; /* remote port */
1555 bool connected;
1557 /* newhost must be able to hold a full IP-style address in ASCII, which
1558 in the IPv6 case means 5*8-1 = 39 letters */
1559 #define NEWHOST_BUFSIZE 48
1560 char newhost[NEWHOST_BUFSIZE];
1561 char *str=&data->state.buffer[4]; /* start on the first letter */
1563 if((ftpc->count1 == 0) &&
1564 (ftpcode == 229)) {
1565 /* positive EPSV response */
1566 char *ptr = strchr(str, '(');
1567 if(ptr) {
1568 unsigned int num;
1569 char separator[4];
1570 ptr++;
1571 if(5 == sscanf(ptr, "%c%c%c%u%c",
1572 &separator[0],
1573 &separator[1],
1574 &separator[2],
1575 &num,
1576 &separator[3])) {
1577 const char sep1 = separator[0];
1578 int i;
1580 /* The four separators should be identical, or else this is an oddly
1581 formatted reply and we bail out immediately. */
1582 for(i=1; i<4; i++) {
1583 if(separator[i] != sep1) {
1584 ptr=NULL; /* set to NULL to signal error */
1585 break;
1588 if(ptr) {
1589 newport = num;
1591 if (conn->bits.tunnel_proxy)
1592 /* proxy tunnel -> use other host info because ip_addr_str is the
1593 proxy address not the ftp host */
1594 snprintf(newhost, sizeof(newhost), "%s", conn->host.name);
1595 else
1596 /* use the same IP we are already connected to */
1597 snprintf(newhost, NEWHOST_BUFSIZE, "%s", conn->ip_addr_str);
1600 else
1601 ptr=NULL;
1603 if(!ptr) {
1604 failf(data, "Weirdly formatted EPSV reply");
1605 return CURLE_FTP_WEIRD_PASV_REPLY;
1608 else if((ftpc->count1 == 1) &&
1609 (ftpcode == 227)) {
1610 /* positive PASV response */
1611 int ip[4];
1612 int port[2];
1615 * Scan for a sequence of six comma-separated numbers and use them as
1616 * IP+port indicators.
1618 * Found reply-strings include:
1619 * "227 Entering Passive Mode (127,0,0,1,4,51)"
1620 * "227 Data transfer will passively listen to 127,0,0,1,4,51"
1621 * "227 Entering passive mode. 127,0,0,1,4,51"
1623 while(*str) {
1624 if (6 == sscanf(str, "%d,%d,%d,%d,%d,%d",
1625 &ip[0], &ip[1], &ip[2], &ip[3],
1626 &port[0], &port[1]))
1627 break;
1628 str++;
1631 if(!*str) {
1632 failf(data, "Couldn't interpret the 227-response");
1633 return CURLE_FTP_WEIRD_227_FORMAT;
1636 /* we got OK from server */
1637 if(data->set.ftp_skip_ip) {
1638 /* told to ignore the remotely given IP but instead use the one we used
1639 for the control connection */
1640 infof(data, "Skips %d.%d.%d.%d for data connection, uses %s instead\n",
1641 ip[0], ip[1], ip[2], ip[3],
1642 conn->ip_addr_str);
1643 if (conn->bits.tunnel_proxy)
1644 /* proxy tunnel -> use other host info because ip_addr_str is the
1645 proxy address not the ftp host */
1646 snprintf(newhost, sizeof(newhost), "%s", conn->host.name);
1647 else
1648 snprintf(newhost, sizeof(newhost), "%s", conn->ip_addr_str);
1650 else
1651 snprintf(newhost, sizeof(newhost),
1652 "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
1653 newport = (port[0]<<8) + port[1];
1655 else if(ftpc->count1 == 0) {
1656 /* EPSV failed, move on to PASV */
1658 /* disable it for next transfer */
1659 conn->bits.ftp_use_epsv = FALSE;
1660 infof(data, "disabling EPSV usage\n");
1662 NBFTPSENDF(conn, "PASV", NULL);
1663 ftpc->count1++;
1664 /* remain in the FTP_PASV state */
1665 return result;
1667 else {
1668 failf(data, "Bad PASV/EPSV response: %03d", ftpcode);
1669 return CURLE_FTP_WEIRD_PASV_REPLY;
1672 if(data->set.proxy && *data->set.proxy) {
1674 * This is a tunnel through a http proxy and we need to connect to the
1675 * proxy again here.
1677 * We don't want to rely on a former host lookup that might've expired
1678 * now, instead we remake the lookup here and now!
1680 rc = Curl_resolv(conn, conn->proxy.name, (int)conn->port, &addr);
1681 if(rc == CURLRESOLV_PENDING)
1682 /* BLOCKING */
1683 rc = Curl_wait_for_resolv(conn, &addr);
1685 connectport =
1686 (unsigned short)conn->port; /* we connect to the proxy's port */
1689 else {
1690 /* normal, direct, ftp connection */
1691 rc = Curl_resolv(conn, newhost, newport, &addr);
1692 if(rc == CURLRESOLV_PENDING)
1693 /* BLOCKING */
1694 rc = Curl_wait_for_resolv(conn, &addr);
1696 if(!addr) {
1697 failf(data, "Can't resolve new host %s:%d", newhost, newport);
1698 return CURLE_FTP_CANT_GET_HOST;
1700 connectport = newport; /* we connect to the remote port */
1703 result = Curl_connecthost(conn,
1704 addr,
1705 &conn->sock[SECONDARYSOCKET],
1706 &conninfo,
1707 &connected);
1709 Curl_resolv_unlock(data, addr); /* we're done using this address */
1711 if (result && ftpc->count1 == 0 && ftpcode == 229) {
1712 infof(data, "got positive EPSV response, but can't connect. "
1713 "Disabling EPSV\n");
1714 /* disable it for next transfer */
1715 conn->bits.ftp_use_epsv = FALSE;
1716 data->state.errorbuf = FALSE; /* allow error message to get rewritten */
1717 NBFTPSENDF(conn, "PASV", NULL);
1718 ftpc->count1++;
1719 /* remain in the FTP_PASV state */
1720 return result;
1723 if(result)
1724 return result;
1726 conn->bits.tcpconnect = connected; /* simply TRUE or FALSE */
1729 * When this is used from the multi interface, this might've returned with
1730 * the 'connected' set to FALSE and thus we are now awaiting a non-blocking
1731 * connect to connect and we should not be "hanging" here waiting.
1734 if(data->set.verbose)
1735 /* this just dumps information about this second connection */
1736 ftp_pasv_verbose(conn, conninfo, newhost, connectport);
1738 #ifndef CURL_DISABLE_HTTP
1739 if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
1740 /* FIX: this MUST wait for a proper connect first if 'connected' is
1741 * FALSE */
1743 /* BLOCKING */
1744 /* We want "seamless" FTP operations through HTTP proxy tunnel */
1746 /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the member
1747 * conn->proto.http; we want FTP through HTTP and we have to change the
1748 * member temporarily for connecting to the HTTP proxy. After
1749 * Curl_proxyCONNECT we have to set back the member to the original struct
1750 * FTP pointer
1752 struct HTTP http_proxy;
1753 struct FTP *ftp_save = data->reqdata.proto.ftp;
1754 memset(&http_proxy, 0, sizeof(http_proxy));
1755 data->reqdata.proto.http = &http_proxy;
1757 result = Curl_proxyCONNECT(conn, SECONDARYSOCKET, newhost, newport);
1759 data->reqdata.proto.ftp = ftp_save;
1761 if(CURLE_OK != result)
1762 return result;
1764 #endif /* CURL_DISABLE_HTTP */
1766 state(conn, FTP_STOP); /* this phase is completed */
1768 return result;
1771 static CURLcode ftp_state_port_resp(struct connectdata *conn,
1772 int ftpcode)
1774 struct SessionHandle *data = conn->data;
1775 struct ftp_conn *ftpc = &conn->proto.ftpc;
1776 ftpport fcmd = (ftpport)ftpc->count1;
1777 CURLcode result = CURLE_OK;
1779 if(ftpcode != 200) {
1780 /* the command failed */
1782 if (EPRT == fcmd) {
1783 infof(data, "disabling EPRT usage\n");
1784 conn->bits.ftp_use_eprt = FALSE;
1786 fcmd++;
1788 if(fcmd == DONE) {
1789 failf(data, "Failed to do PORT");
1790 result = CURLE_FTP_PORT_FAILED;
1792 else
1793 /* try next */
1794 result = ftp_state_use_port(conn, fcmd);
1796 else {
1797 infof(data, "Connect data stream actively\n");
1798 state(conn, FTP_STOP); /* end of DO phase */
1801 return result;
1804 static CURLcode ftp_state_mdtm_resp(struct connectdata *conn,
1805 int ftpcode)
1807 CURLcode result = CURLE_OK;
1808 struct SessionHandle *data=conn->data;
1809 struct FTP *ftp = data->reqdata.proto.ftp;
1811 switch(ftpcode) {
1812 case 213:
1814 /* we got a time. Format should be: "YYYYMMDDHHMMSS[.sss]" where the
1815 last .sss part is optional and means fractions of a second */
1816 int year, month, day, hour, minute, second;
1817 char *buf = data->state.buffer;
1818 if(6 == sscanf(buf+4, "%04d%02d%02d%02d%02d%02d",
1819 &year, &month, &day, &hour, &minute, &second)) {
1820 /* we have a time, reformat it */
1821 time_t secs=time(NULL);
1822 /* using the good old yacc/bison yuck */
1823 snprintf(buf, sizeof(conn->data->state.buffer),
1824 "%04d%02d%02d %02d:%02d:%02d GMT",
1825 year, month, day, hour, minute, second);
1826 /* now, convert this into a time() value: */
1827 data->info.filetime = (long)curl_getdate(buf, &secs);
1830 /* If we asked for a time of the file and we actually got one as well,
1831 we "emulate" a HTTP-style header in our output. */
1833 if(conn->bits.no_body &&
1834 data->set.include_header &&
1835 ftp->file &&
1836 data->set.get_filetime &&
1837 (data->info.filetime>=0) ) {
1838 struct tm *tm;
1839 time_t clock = (time_t)data->info.filetime;
1840 #ifdef HAVE_GMTIME_R
1841 struct tm buffer;
1842 tm = (struct tm *)gmtime_r(&clock, &buffer);
1843 #else
1844 tm = gmtime(&clock);
1845 #endif
1846 /* format: "Tue, 15 Nov 1994 12:45:26" */
1847 snprintf(buf, BUFSIZE-1,
1848 "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
1849 Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
1850 tm->tm_mday,
1851 Curl_month[tm->tm_mon],
1852 tm->tm_year + 1900,
1853 tm->tm_hour,
1854 tm->tm_min,
1855 tm->tm_sec);
1856 result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
1857 if(result)
1858 return result;
1859 } /* end of a ridiculous amount of conditionals */
1861 break;
1862 default:
1863 infof(data, "unsupported MDTM reply format\n");
1864 break;
1865 case 550: /* "No such file or directory" */
1866 failf(data, "Given file does not exist");
1867 result = CURLE_FTP_COULDNT_RETR_FILE;
1868 break;
1871 if(data->set.timecondition) {
1872 if((data->info.filetime > 0) && (data->set.timevalue > 0)) {
1873 switch(data->set.timecondition) {
1874 case CURL_TIMECOND_IFMODSINCE:
1875 default:
1876 if(data->info.filetime <= data->set.timevalue) {
1877 infof(data, "The requested document is not new enough\n");
1878 ftp->no_transfer = TRUE; /* mark this to not transfer data */
1879 state(conn, FTP_STOP);
1880 return CURLE_OK;
1882 break;
1883 case CURL_TIMECOND_IFUNMODSINCE:
1884 if(data->info.filetime > data->set.timevalue) {
1885 infof(data, "The requested document is not old enough\n");
1886 ftp->no_transfer = TRUE; /* mark this to not transfer data */
1887 state(conn, FTP_STOP);
1888 return CURLE_OK;
1890 break;
1891 } /* switch */
1893 else {
1894 infof(data, "Skipping time comparison\n");
1898 if(!result)
1899 result = ftp_state_post_mdtm(conn);
1901 return result;
1904 static CURLcode ftp_state_type_resp(struct connectdata *conn,
1905 int ftpcode,
1906 ftpstate instate)
1908 CURLcode result = CURLE_OK;
1909 struct SessionHandle *data=conn->data;
1911 if(ftpcode/100 != 2) {
1912 /* "sasserftpd" and "(u)r(x)bot ftpd" both responds with 226 after a
1913 successful 'TYPE I'. While that is not as RFC959 says, it is still a
1914 positive response code and we allow that. */
1915 failf(data, "Couldn't set desired mode");
1916 return CURLE_FTP_COULDNT_SET_BINARY; /* FIX */
1918 if(ftpcode != 200)
1919 infof(data, "Got a %03d response code instead of the assumed 200\n",
1920 ftpcode);
1922 if(instate == FTP_TYPE)
1923 result = ftp_state_post_type(conn);
1924 else if(instate == FTP_LIST_TYPE)
1925 result = ftp_state_post_listtype(conn);
1926 else if(instate == FTP_RETR_TYPE)
1927 result = ftp_state_post_retrtype(conn);
1928 else if(instate == FTP_STOR_TYPE)
1929 result = ftp_state_post_stortype(conn);
1931 return result;
1934 static CURLcode ftp_state_post_retr_size(struct connectdata *conn,
1935 curl_off_t filesize)
1937 CURLcode result = CURLE_OK;
1938 struct SessionHandle *data=conn->data;
1939 struct FTP *ftp = data->reqdata.proto.ftp;
1941 if (data->set.max_filesize && (filesize > data->set.max_filesize)) {
1942 failf(data, "Maximum file size exceeded");
1943 return CURLE_FILESIZE_EXCEEDED;
1945 ftp->downloadsize = filesize;
1947 if(data->reqdata.resume_from) {
1948 /* We always (attempt to) get the size of downloads, so it is done before
1949 this even when not doing resumes. */
1950 if(filesize == -1) {
1951 infof(data, "ftp server doesn't support SIZE\n");
1952 /* We couldn't get the size and therefore we can't know if there really
1953 is a part of the file left to get, although the server will just
1954 close the connection when we start the connection so it won't cause
1955 us any harm, just not make us exit as nicely. */
1957 else {
1958 /* We got a file size report, so we check that there actually is a
1959 part of the file left to get, or else we go home. */
1960 if(data->reqdata.resume_from< 0) {
1961 /* We're supposed to download the last abs(from) bytes */
1962 if(filesize < -data->reqdata.resume_from) {
1963 failf(data, "Offset (%" FORMAT_OFF_T
1964 ") was beyond file size (%" FORMAT_OFF_T ")",
1965 data->reqdata.resume_from, filesize);
1966 return CURLE_BAD_DOWNLOAD_RESUME;
1968 /* convert to size to download */
1969 ftp->downloadsize = -data->reqdata.resume_from;
1970 /* download from where? */
1971 data->reqdata.resume_from = filesize - ftp->downloadsize;
1973 else {
1974 if(filesize < data->reqdata.resume_from) {
1975 failf(data, "Offset (%" FORMAT_OFF_T
1976 ") was beyond file size (%" FORMAT_OFF_T ")",
1977 data->reqdata.resume_from, filesize);
1978 return CURLE_BAD_DOWNLOAD_RESUME;
1980 /* Now store the number of bytes we are expected to download */
1981 ftp->downloadsize = filesize-data->reqdata.resume_from;
1985 if(ftp->downloadsize == 0) {
1986 /* no data to transfer */
1987 result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1988 infof(data, "File already completely downloaded\n");
1990 /* Set no_transfer so that we won't get any error in Curl_ftp_done()
1991 * because we didn't transfer the any file */
1992 ftp->no_transfer = TRUE;
1993 state(conn, FTP_STOP);
1994 return CURLE_OK;
1997 /* Set resume file transfer offset */
1998 infof(data, "Instructs server to resume from offset %" FORMAT_OFF_T
1999 "\n", data->reqdata.resume_from);
2001 NBFTPSENDF(conn, "REST %" FORMAT_OFF_T, data->reqdata.resume_from);
2003 state(conn, FTP_RETR_REST);
2006 else {
2007 /* no resume */
2008 NBFTPSENDF(conn, "RETR %s", ftp->file);
2009 state(conn, FTP_RETR);
2012 return result;
2015 static CURLcode ftp_state_size_resp(struct connectdata *conn,
2016 int ftpcode,
2017 ftpstate instate)
2019 CURLcode result = CURLE_OK;
2020 struct SessionHandle *data=conn->data;
2021 curl_off_t filesize;
2022 char *buf = data->state.buffer;
2024 /* get the size from the ascii string: */
2025 filesize = (ftpcode == 213)?curlx_strtoofft(buf+4, NULL, 0):-1;
2027 if(instate == FTP_SIZE) {
2028 if(-1 != filesize) {
2029 snprintf(buf, sizeof(data->state.buffer),
2030 "Content-Length: %" FORMAT_OFF_T "\r\n", filesize);
2031 result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
2032 if(result)
2033 return result;
2035 result = ftp_state_post_size(conn);
2037 else if(instate == FTP_RETR_SIZE)
2038 result = ftp_state_post_retr_size(conn, filesize);
2039 else if(instate == FTP_STOR_SIZE) {
2040 data->reqdata.resume_from = filesize;
2041 result = ftp_state_ul_setup(conn, TRUE);
2044 return result;
2047 static CURLcode ftp_state_rest_resp(struct connectdata *conn,
2048 int ftpcode,
2049 ftpstate instate)
2051 CURLcode result = CURLE_OK;
2052 struct FTP *ftp = conn->data->reqdata.proto.ftp;
2054 switch(instate) {
2055 case FTP_REST:
2056 default:
2057 if (ftpcode == 350) {
2058 result = Curl_client_write(conn, CLIENTWRITE_BOTH,
2059 (char *)"Accept-ranges: bytes\r\n", 0);
2060 if(result)
2061 return result;
2064 result = ftp_state_post_rest(conn);
2065 break;
2067 case FTP_RETR_REST:
2068 if (ftpcode != 350) {
2069 failf(conn->data, "Couldn't use REST");
2070 result = CURLE_FTP_COULDNT_USE_REST;
2072 else {
2073 NBFTPSENDF(conn, "RETR %s", ftp->file);
2074 state(conn, FTP_RETR);
2076 break;
2079 return result;
2082 static CURLcode ftp_state_stor_resp(struct connectdata *conn,
2083 int ftpcode)
2085 CURLcode result = CURLE_OK;
2086 struct SessionHandle *data = conn->data;
2087 struct FTP *ftp = data->reqdata.proto.ftp;
2089 if(ftpcode>=400) {
2090 failf(data, "Failed FTP upload: %0d", ftpcode);
2091 /* oops, we never close the sockets! */
2092 return CURLE_FTP_COULDNT_STOR_FILE;
2095 if(data->set.ftp_use_port) {
2096 /* BLOCKING */
2097 /* PORT means we are now awaiting the server to connect to us. */
2098 result = AllowServerConnect(conn);
2099 if( result )
2100 return result;
2103 if(conn->ssl[SECONDARYSOCKET].use) {
2104 /* since we only have a plaintext TCP connection here, we must now
2105 do the TLS stuff */
2106 infof(data, "Doing the SSL/TLS handshake on the data stream\n");
2107 /* BLOCKING */
2108 result = Curl_ssl_connect(conn, SECONDARYSOCKET);
2109 if(result)
2110 return result;
2113 *(ftp->bytecountp)=0;
2115 /* When we know we're uploading a specified file, we can get the file
2116 size prior to the actual upload. */
2118 Curl_pgrsSetUploadSize(data, data->set.infilesize);
2120 result = Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */
2121 SECONDARYSOCKET, ftp->bytecountp);
2122 state(conn, FTP_STOP);
2124 return result;
2127 /* for LIST and RETR responses */
2128 static CURLcode ftp_state_get_resp(struct connectdata *conn,
2129 int ftpcode,
2130 ftpstate instate)
2132 CURLcode result = CURLE_OK;
2133 struct SessionHandle *data = conn->data;
2134 struct FTP *ftp = data->reqdata.proto.ftp;
2135 char *buf = data->state.buffer;
2137 if((ftpcode == 150) || (ftpcode == 125)) {
2141 150 Opening BINARY mode data connection for /etc/passwd (2241
2142 bytes). (ok, the file is being transfered)
2145 150 Opening ASCII mode data connection for /bin/ls
2148 150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes).
2151 150 Opening ASCII mode data connection for /linux/fisk/kpanelrc (0.0.0.0,0) (545 bytes).
2154 125 Data connection already open; Transfer starting. */
2156 curl_off_t size=-1; /* default unknown size */
2160 * It appears that there are FTP-servers that return size 0 for files when
2161 * SIZE is used on the file while being in BINARY mode. To work around
2162 * that (stupid) behavior, we attempt to parse the RETR response even if
2163 * the SIZE returned size zero.
2165 * Debugging help from Salvatore Sorrentino on February 26, 2003.
2168 if((instate != FTP_LIST) &&
2169 !data->set.prefer_ascii &&
2170 (ftp->downloadsize < 1)) {
2172 * It seems directory listings either don't show the size or very
2173 * often uses size 0 anyway. ASCII transfers may very well turn out
2174 * that the transfered amount of data is not the same as this line
2175 * tells, why using this number in those cases only confuses us.
2177 * Example D above makes this parsing a little tricky */
2178 char *bytes;
2179 bytes=strstr(buf, " bytes");
2180 if(bytes--) {
2181 long in=(long)(bytes-buf);
2182 /* this is a hint there is size information in there! ;-) */
2183 while(--in) {
2184 /* scan for the left parenthesis and break there */
2185 if('(' == *bytes)
2186 break;
2187 /* skip only digits */
2188 if(!ISDIGIT(*bytes)) {
2189 bytes=NULL;
2190 break;
2192 /* one more estep backwards */
2193 bytes--;
2195 /* if we have nothing but digits: */
2196 if(bytes++) {
2197 /* get the number! */
2198 size = curlx_strtoofft(bytes, NULL, 0);
2202 else if(ftp->downloadsize > -1)
2203 size = ftp->downloadsize;
2205 if(data->set.ftp_use_port) {
2206 /* BLOCKING */
2207 result = AllowServerConnect(conn);
2208 if( result )
2209 return result;
2212 if(conn->ssl[SECONDARYSOCKET].use) {
2213 /* since we only have a plaintext TCP connection here, we must now
2214 do the TLS stuff */
2215 infof(data, "Doing the SSL/TLS handshake on the data stream\n");
2216 result = Curl_ssl_connect(conn, SECONDARYSOCKET);
2217 if(result)
2218 return result;
2221 if(size > data->reqdata.maxdownload && data->reqdata.maxdownload > 0)
2222 size = data->reqdata.size = data->reqdata.maxdownload;
2224 infof(data, "Maxdownload = %" FORMAT_OFF_T "\n", data->reqdata.maxdownload);
2226 if(instate != FTP_LIST)
2227 infof(data, "Getting file with size: %" FORMAT_OFF_T "\n", size);
2229 /* FTP download: */
2230 result=Curl_setup_transfer(conn, SECONDARYSOCKET, size, FALSE,
2231 ftp->bytecountp,
2232 -1, NULL); /* no upload here */
2233 if(result)
2234 return result;
2236 state(conn, FTP_STOP);
2238 else {
2239 if((instate == FTP_LIST) && (ftpcode == 450)) {
2240 /* simply no matching files in the dir listing */
2241 ftp->no_transfer = TRUE; /* don't download anything */
2242 state(conn, FTP_STOP); /* this phase is over */
2244 else {
2245 failf(data, "RETR response: %03d", ftpcode);
2246 return CURLE_FTP_COULDNT_RETR_FILE;
2250 return result;
2253 /* after USER, PASS and ACCT */
2254 static CURLcode ftp_state_loggedin(struct connectdata *conn)
2256 CURLcode result = CURLE_OK;
2258 #ifdef HAVE_KRB4
2259 if(conn->data->set.krb4) {
2260 /* We are logged in, asked to use Kerberos. Set the requested
2261 * protection level
2263 if(conn->sec_complete)
2264 /* BLOCKING */
2265 Curl_sec_set_protection_level(conn);
2267 /* We may need to issue a KAUTH here to have access to the files
2268 * do it if user supplied a password
2270 if(conn->passwd && *conn->passwd) {
2271 /* BLOCKING */
2272 result = Curl_krb_kauth(conn);
2273 if(result)
2274 return result;
2277 #endif
2278 if(conn->ssl[FIRSTSOCKET].use) {
2279 /* PBSZ = PROTECTION BUFFER SIZE.
2281 The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says:
2283 Specifically, the PROT command MUST be preceded by a PBSZ
2284 command and a PBSZ command MUST be preceded by a successful
2285 security data exchange (the TLS negotiation in this case)
2287 ... (and on page 8):
2289 Thus the PBSZ command must still be issued, but must have a
2290 parameter of '0' to indicate that no buffering is taking place
2291 and the data connection should not be encapsulated.
2293 NBFTPSENDF(conn, "PBSZ %d", 0);
2294 state(conn, FTP_PBSZ);
2296 else {
2297 result = ftp_state_pwd(conn);
2299 return result;
2302 /* for USER and PASS responses */
2303 static CURLcode ftp_state_user_resp(struct connectdata *conn,
2304 int ftpcode,
2305 ftpstate instate)
2307 CURLcode result = CURLE_OK;
2308 struct SessionHandle *data = conn->data;
2309 struct FTP *ftp = data->reqdata.proto.ftp;
2310 struct ftp_conn *ftpc = &conn->proto.ftpc;
2311 (void)instate; /* no use for this yet */
2313 if((ftpcode == 331) && (ftpc->state == FTP_USER)) {
2314 /* 331 Password required for ...
2315 (the server requires to send the user's password too) */
2316 NBFTPSENDF(conn, "PASS %s", ftp->passwd?ftp->passwd:"");
2317 state(conn, FTP_PASS);
2319 else if(ftpcode/100 == 2) {
2320 /* 230 User ... logged in.
2321 (the user logged in with or without password) */
2322 result = ftp_state_loggedin(conn);
2324 else if(ftpcode == 332) {
2325 if(data->set.ftp_account) {
2326 NBFTPSENDF(conn, "ACCT %s", data->set.ftp_account);
2327 state(conn, FTP_ACCT);
2329 else {
2330 failf(data, "ACCT requested but none available");
2331 result = CURLE_LOGIN_DENIED;
2334 else {
2335 /* All other response codes, like:
2337 530 User ... access denied
2338 (the server denies to log the specified user) */
2340 if (conn->data->set.ftp_alternative_to_user &&
2341 !conn->data->state.ftp_trying_alternative) {
2342 /* Ok, USER failed. Let's try the supplied command. */
2343 NBFTPSENDF(conn, "%s", conn->data->set.ftp_alternative_to_user);
2344 conn->data->state.ftp_trying_alternative = TRUE;
2345 state(conn, FTP_USER);
2346 result = CURLE_OK;
2348 else {
2349 failf(data, "Access denied: %03d", ftpcode);
2350 result = CURLE_LOGIN_DENIED;
2353 return result;
2356 /* for ACCT response */
2357 static CURLcode ftp_state_acct_resp(struct connectdata *conn,
2358 int ftpcode)
2360 CURLcode result = CURLE_OK;
2361 struct SessionHandle *data = conn->data;
2362 if(ftpcode != 230) {
2363 failf(data, "ACCT rejected by server: %03d", ftpcode);
2364 result = CURLE_FTP_WEIRD_PASS_REPLY; /* FIX */
2366 else
2367 result = ftp_state_loggedin(conn);
2369 return result;
2373 static CURLcode ftp_statemach_act(struct connectdata *conn)
2375 CURLcode result;
2376 curl_socket_t sock = conn->sock[FIRSTSOCKET];
2377 struct SessionHandle *data=conn->data;
2378 int ftpcode;
2379 struct ftp_conn *ftpc = &conn->proto.ftpc;
2380 static const char * const ftpauth[] = {
2381 "SSL", "TLS"
2383 size_t nread = 0;
2385 if(ftpc->sendleft) {
2386 /* we have a piece of a command still left to send */
2387 ssize_t written;
2388 result = Curl_write(conn, sock, ftpc->sendthis + ftpc->sendsize -
2389 ftpc->sendleft, ftpc->sendleft, &written);
2390 if(result)
2391 return result;
2393 if(written != (ssize_t)ftpc->sendleft) {
2394 /* only a fraction was sent */
2395 ftpc->sendleft -= written;
2397 else {
2398 free(ftpc->sendthis);
2399 ftpc->sendthis=NULL;
2400 ftpc->sendleft = ftpc->sendsize = 0;
2401 ftpc->response = Curl_tvnow();
2403 return CURLE_OK;
2406 /* we read a piece of response */
2407 result = ftp_readresp(sock, conn, &ftpcode, &nread);
2408 if(result)
2409 return result;
2411 if(ftpcode) {
2412 /* we have now received a full FTP server response */
2413 switch(ftpc->state) {
2414 case FTP_WAIT220:
2415 if(ftpcode != 220) {
2416 failf(data, "This doesn't seem like a nice ftp-server response");
2417 return CURLE_FTP_WEIRD_SERVER_REPLY;
2420 /* We have received a 220 response fine, now we proceed. */
2421 #ifdef HAVE_KRB4
2422 if(data->set.krb4) {
2423 /* If not anonymous login, try a secure login. Note that this
2424 procedure is still BLOCKING. */
2426 Curl_sec_request_prot(conn, "private");
2427 /* We set private first as default, in case the line below fails to
2428 set a valid level */
2429 Curl_sec_request_prot(conn, data->set.krb4_level);
2431 if(Curl_sec_login(conn) != 0)
2432 infof(data, "Logging in with password in cleartext!\n");
2433 else
2434 infof(data, "Authentication successful\n");
2436 #endif
2438 if(data->set.ftp_ssl && !conn->ssl[FIRSTSOCKET].use) {
2439 /* We don't have a SSL/TLS connection yet, but FTPS is
2440 requested. Try a FTPS connection now */
2442 ftpc->count3=0;
2443 switch(data->set.ftpsslauth) {
2444 case CURLFTPAUTH_DEFAULT:
2445 case CURLFTPAUTH_SSL:
2446 ftpc->count2 = 1; /* add one to get next */
2447 ftpc->count1 = 0;
2448 break;
2449 case CURLFTPAUTH_TLS:
2450 ftpc->count2 = -1; /* subtract one to get next */
2451 ftpc->count1 = 1;
2452 break;
2453 default:
2454 failf(data, "unsupported parameter to CURLOPT_FTPSSLAUTH: %d\n",
2455 data->set.ftpsslauth);
2456 return CURLE_FAILED_INIT; /* we don't know what to do */
2458 NBFTPSENDF(conn, "AUTH %s", ftpauth[ftpc->count1]);
2459 state(conn, FTP_AUTH);
2461 else {
2462 result = ftp_state_user(conn);
2463 if(result)
2464 return result;
2467 break;
2469 case FTP_AUTH:
2470 /* we have gotten the response to a previous AUTH command */
2472 /* RFC2228 (page 5) says:
2474 * If the server is willing to accept the named security mechanism,
2475 * and does not require any security data, it must respond with
2476 * reply code 234/334.
2479 if((ftpcode == 234) || (ftpcode == 334)) {
2480 /* Curl_ssl_connect is BLOCKING */
2481 result = Curl_ssl_connect(conn, FIRSTSOCKET);
2482 if(CURLE_OK == result) {
2483 conn->protocol |= PROT_FTPS;
2484 conn->ssl[SECONDARYSOCKET].use = FALSE; /* clear-text data */
2485 result = ftp_state_user(conn);
2488 else if(ftpc->count3 < 1) {
2489 ftpc->count3++;
2490 ftpc->count1 += ftpc->count2; /* get next attempt */
2491 result = Curl_nbftpsendf(conn, "AUTH %s", ftpauth[ftpc->count1]);
2492 /* remain in this same state */
2494 else {
2495 if(data->set.ftp_ssl > CURLFTPSSL_TRY)
2496 /* we failed and CURLFTPSSL_CONTROL or CURLFTPSSL_ALL is set */
2497 result = CURLE_FTP_SSL_FAILED;
2498 else
2499 /* ignore the failure and continue */
2500 result = ftp_state_user(conn);
2503 if(result)
2504 return result;
2505 break;
2507 case FTP_USER:
2508 case FTP_PASS:
2509 result = ftp_state_user_resp(conn, ftpcode, ftpc->state);
2510 break;
2512 case FTP_ACCT:
2513 result = ftp_state_acct_resp(conn, ftpcode);
2514 break;
2516 case FTP_PBSZ:
2517 /* FIX: check response code */
2519 /* For TLS, the data connection can have one of two security levels.
2521 1) Clear (requested by 'PROT C')
2523 2)Private (requested by 'PROT P')
2525 if(!conn->ssl[SECONDARYSOCKET].use) {
2526 NBFTPSENDF(conn, "PROT %c",
2527 data->set.ftp_ssl == CURLFTPSSL_CONTROL ? 'C' : 'P');
2528 state(conn, FTP_PROT);
2530 else {
2531 result = ftp_state_pwd(conn);
2532 if(result)
2533 return result;
2536 break;
2538 case FTP_PROT:
2539 if(ftpcode/100 == 2)
2540 /* We have enabled SSL for the data connection! */
2541 conn->ssl[SECONDARYSOCKET].use =
2542 (bool)(data->set.ftp_ssl != CURLFTPSSL_CONTROL);
2543 /* FTP servers typically responds with 500 if they decide to reject
2544 our 'P' request */
2545 else if(data->set.ftp_ssl> CURLFTPSSL_CONTROL)
2546 /* we failed and bails out */
2547 return CURLE_FTP_SSL_FAILED;
2549 if(data->set.ftp_use_ccc) {
2550 /* CCC - Clear Command Channel
2552 NBFTPSENDF(conn, "CCC", NULL);
2553 state(conn, FTP_CCC);
2555 else {
2556 result = ftp_state_pwd(conn);
2557 if(result)
2558 return result;
2560 break;
2562 case FTP_CCC:
2563 if (ftpcode < 500) {
2564 /* First shut down the SSL layer (note: this call will block) */
2565 result = Curl_ssl_shutdown(conn, FIRSTSOCKET);
2567 if(result) {
2568 failf(conn->data, "Failed to clear the command channel (CCC)");
2569 return result;
2573 /* Then continue as normal */
2574 result = ftp_state_pwd(conn);
2575 if(result)
2576 return result;
2577 break;
2579 case FTP_PWD:
2580 if(ftpcode == 257) {
2581 char *dir = (char *)malloc(nread+1);
2582 char *store=dir;
2583 char *ptr=&data->state.buffer[4]; /* start on the first letter */
2585 if(!dir)
2586 return CURLE_OUT_OF_MEMORY;
2588 /* Reply format is like
2589 257<space>"<directory-name>"<space><commentary> and the RFC959
2590 says
2592 The directory name can contain any character; embedded
2593 double-quotes should be escaped by double-quotes (the
2594 "quote-doubling" convention).
2596 if('\"' == *ptr) {
2597 /* it started good */
2598 ptr++;
2599 while(ptr && *ptr) {
2600 if('\"' == *ptr) {
2601 if('\"' == ptr[1]) {
2602 /* "quote-doubling" */
2603 *store = ptr[1];
2604 ptr++;
2606 else {
2607 /* end of path */
2608 *store = '\0'; /* zero terminate */
2609 break; /* get out of this loop */
2612 else
2613 *store = *ptr;
2614 store++;
2615 ptr++;
2617 ftpc->entrypath =dir; /* remember this */
2618 infof(data, "Entry path is '%s'\n", ftpc->entrypath);
2619 /* also save it where getinfo can access it: */
2620 data->state.most_recent_ftp_entrypath = ftpc->entrypath;
2622 else {
2623 /* couldn't get the path */
2624 free(dir);
2625 infof(data, "Failed to figure out path\n");
2628 state(conn, FTP_STOP); /* we are done with the CONNECT phase! */
2629 DEBUGF(infof(data, "protocol connect phase DONE\n"));
2630 break;
2632 case FTP_QUOTE:
2633 case FTP_POSTQUOTE:
2634 case FTP_RETR_PREQUOTE:
2635 case FTP_STOR_PREQUOTE:
2636 if(ftpcode >= 400) {
2637 failf(conn->data, "QUOT command failed with %03d", ftpcode);
2638 return CURLE_FTP_QUOTE_ERROR;
2640 result = ftp_state_quote(conn, FALSE, ftpc->state);
2641 if(result)
2642 return result;
2644 break;
2646 case FTP_CWD:
2647 if(ftpcode/100 != 2) {
2648 /* failure to CWD there */
2649 if(conn->data->set.ftp_create_missing_dirs &&
2650 ftpc->count1 && !ftpc->count2) {
2651 /* try making it */
2652 ftpc->count2++; /* counter to prevent CWD-MKD loops */
2653 NBFTPSENDF(conn, "MKD %s", ftpc->dirs[ftpc->count1 - 1]);
2654 state(conn, FTP_MKD);
2656 else {
2657 /* return failure */
2658 failf(data, "Server denied you to change to the given directory");
2659 ftpc->cwdfail = TRUE; /* don't remember this path as we failed
2660 to enter it */
2661 return CURLE_FTP_ACCESS_DENIED;
2664 else {
2665 /* success */
2666 ftpc->count2=0;
2667 if(++ftpc->count1 <= ftpc->dirdepth) {
2668 /* send next CWD */
2669 NBFTPSENDF(conn, "CWD %s", ftpc->dirs[ftpc->count1 - 1]);
2671 else {
2672 result = ftp_state_post_cwd(conn);
2673 if(result)
2674 return result;
2677 break;
2679 case FTP_MKD:
2680 if(ftpcode/100 != 2) {
2681 /* failure to MKD the dir */
2682 failf(data, "Failed to MKD dir: %03d", ftpcode);
2683 return CURLE_FTP_ACCESS_DENIED;
2685 state(conn, FTP_CWD);
2686 /* send CWD */
2687 NBFTPSENDF(conn, "CWD %s", ftpc->dirs[ftpc->count1 - 1]);
2688 break;
2690 case FTP_MDTM:
2691 result = ftp_state_mdtm_resp(conn, ftpcode);
2692 break;
2694 case FTP_TYPE:
2695 case FTP_LIST_TYPE:
2696 case FTP_RETR_TYPE:
2697 case FTP_STOR_TYPE:
2698 result = ftp_state_type_resp(conn, ftpcode, ftpc->state);
2699 break;
2701 case FTP_SIZE:
2702 case FTP_RETR_SIZE:
2703 case FTP_STOR_SIZE:
2704 result = ftp_state_size_resp(conn, ftpcode, ftpc->state);
2705 break;
2707 case FTP_REST:
2708 case FTP_RETR_REST:
2709 result = ftp_state_rest_resp(conn, ftpcode, ftpc->state);
2710 break;
2712 case FTP_PASV:
2713 result = ftp_state_pasv_resp(conn, ftpcode);
2714 break;
2716 case FTP_PORT:
2717 result = ftp_state_port_resp(conn, ftpcode);
2718 break;
2720 case FTP_LIST:
2721 case FTP_RETR:
2722 result = ftp_state_get_resp(conn, ftpcode, ftpc->state);
2723 break;
2725 case FTP_STOR:
2726 result = ftp_state_stor_resp(conn, ftpcode);
2727 break;
2729 case FTP_QUIT:
2730 /* fallthrough, just stop! */
2731 default:
2732 /* internal error */
2733 state(conn, FTP_STOP);
2734 break;
2736 } /* if(ftpcode) */
2738 return result;
2741 /* Returns timeout in ms. 0 or negative number means the timeout has already
2742 triggered */
2743 static long ftp_state_timeout(struct connectdata *conn)
2745 struct SessionHandle *data=conn->data;
2746 struct ftp_conn *ftpc = &conn->proto.ftpc;
2747 long timeout_ms=360000; /* in milliseconds */
2749 if(data->set.ftp_response_timeout )
2750 /* if CURLOPT_FTP_RESPONSE_TIMEOUT is set, use that to determine remaining
2751 time. Also, use ftp->response because FTP_RESPONSE_TIMEOUT is supposed
2752 to govern the response for any given ftp response, not for the time
2753 from connect to the given ftp response. */
2754 timeout_ms = data->set.ftp_response_timeout*1000 - /* timeout time */
2755 Curl_tvdiff(Curl_tvnow(), ftpc->response); /* spent time */
2756 else if(data->set.timeout)
2757 /* if timeout is requested, find out how much remaining time we have */
2758 timeout_ms = data->set.timeout*1000 - /* timeout time */
2759 Curl_tvdiff(Curl_tvnow(), conn->now); /* spent time */
2760 else
2761 /* Without a requested timeout, we only wait 'response_time' seconds for
2762 the full response to arrive before we bail out */
2763 timeout_ms = ftpc->response_time*1000 -
2764 Curl_tvdiff(Curl_tvnow(), ftpc->response); /* spent time */
2766 return timeout_ms;
2770 /* called repeatedly until done from multi.c */
2771 CURLcode Curl_ftp_multi_statemach(struct connectdata *conn,
2772 bool *done)
2774 curl_socket_t sock = conn->sock[FIRSTSOCKET];
2775 int rc;
2776 struct SessionHandle *data=conn->data;
2777 struct ftp_conn *ftpc = &conn->proto.ftpc;
2778 CURLcode result = CURLE_OK;
2779 long timeout_ms = ftp_state_timeout(conn);
2781 *done = FALSE; /* default to not done yet */
2783 if(timeout_ms <= 0) {
2784 failf(data, "FTP response timeout");
2785 return CURLE_OPERATION_TIMEDOUT;
2788 rc = Curl_select(ftpc->sendleft?CURL_SOCKET_BAD:sock, /* reading */
2789 ftpc->sendleft?sock:CURL_SOCKET_BAD, /* writing */
2792 if(rc == -1) {
2793 failf(data, "select error");
2794 return CURLE_OUT_OF_MEMORY;
2796 else if(rc != 0) {
2797 result = ftp_statemach_act(conn);
2798 *done = (bool)(ftpc->state == FTP_STOP);
2800 /* if rc == 0, then select() timed out */
2802 return result;
2805 static CURLcode ftp_easy_statemach(struct connectdata *conn)
2807 curl_socket_t sock = conn->sock[FIRSTSOCKET];
2808 int rc;
2809 struct SessionHandle *data=conn->data;
2810 struct ftp_conn *ftpc = &conn->proto.ftpc;
2811 CURLcode result = CURLE_OK;
2813 while(ftpc->state != FTP_STOP) {
2814 long timeout_ms = ftp_state_timeout(conn);
2816 if(timeout_ms <=0 ) {
2817 failf(data, "FTP response timeout");
2818 return CURLE_OPERATION_TIMEDOUT; /* already too little time */
2821 rc = Curl_select(ftpc->sendleft?CURL_SOCKET_BAD:sock, /* reading */
2822 ftpc->sendleft?sock:CURL_SOCKET_BAD, /* writing */
2823 (int)timeout_ms);
2825 if(rc == -1) {
2826 failf(data, "select error");
2827 return CURLE_OUT_OF_MEMORY;
2829 else if(rc == 0) {
2830 result = CURLE_OPERATION_TIMEDOUT;
2831 break;
2833 else {
2834 result = ftp_statemach_act(conn);
2835 if(result)
2836 break;
2840 return result;
2844 * Allocate and initialize the struct FTP for the current SessionHandle. If
2845 * need be.
2847 static CURLcode ftp_init(struct connectdata *conn)
2849 struct SessionHandle *data = conn->data;
2850 struct FTP *ftp;
2851 if(data->reqdata.proto.ftp)
2852 return CURLE_OK;
2854 ftp = (struct FTP *)calloc(sizeof(struct FTP), 1);
2855 if(!ftp)
2856 return CURLE_OUT_OF_MEMORY;
2858 data->reqdata.proto.ftp = ftp;
2860 /* get some initial data into the ftp struct */
2861 ftp->bytecountp = &data->reqdata.keep.bytecount;
2863 /* no need to duplicate them, this connectdata struct won't change */
2864 ftp->user = conn->user;
2865 ftp->passwd = conn->passwd;
2866 if (isBadFtpString(ftp->user) || isBadFtpString(ftp->passwd))
2867 return CURLE_URL_MALFORMAT;
2869 return CURLE_OK;
2873 * Curl_ftp_connect() should do everything that is to be considered a part of
2874 * the connection phase.
2876 * The variable 'done' points to will be TRUE if the protocol-layer connect
2877 * phase is done when this function returns, or FALSE is not. When called as
2878 * a part of the easy interface, it will always be TRUE.
2880 CURLcode Curl_ftp_connect(struct connectdata *conn,
2881 bool *done) /* see description above */
2883 CURLcode result;
2884 #ifndef CURL_DISABLE_HTTP
2885 /* for FTP over HTTP proxy */
2886 struct HTTP http_proxy;
2887 struct FTP *ftp_save;
2888 #endif /* CURL_DISABLE_HTTP */
2889 struct ftp_conn *ftpc = &conn->proto.ftpc;
2890 struct SessionHandle *data=conn->data;
2892 *done = FALSE; /* default to not done yet */
2894 if (data->reqdata.proto.ftp) {
2895 Curl_ftp_disconnect(conn);
2896 free(data->reqdata.proto.ftp);
2897 data->reqdata.proto.ftp = NULL;
2900 result = ftp_init(conn);
2901 if(result)
2902 return result;
2904 /* We always support persistant connections on ftp */
2905 conn->bits.close = FALSE;
2907 ftpc->response_time = 3600; /* set default response time-out */
2909 #ifndef CURL_DISABLE_HTTP
2910 if (conn->bits.tunnel_proxy && conn->bits.httpproxy) {
2911 /* BLOCKING */
2912 /* We want "seamless" FTP operations through HTTP proxy tunnel */
2914 /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the member
2915 * conn->proto.http; we want FTP through HTTP and we have to change the
2916 * member temporarily for connecting to the HTTP proxy. After
2917 * Curl_proxyCONNECT we have to set back the member to the original struct
2918 * FTP pointer
2920 ftp_save = data->reqdata.proto.ftp;
2921 memset(&http_proxy, 0, sizeof(http_proxy));
2922 data->reqdata.proto.http = &http_proxy;
2924 result = Curl_proxyCONNECT(conn, FIRSTSOCKET,
2925 conn->host.name, conn->remote_port);
2927 data->reqdata.proto.ftp = ftp_save;
2929 if(CURLE_OK != result)
2930 return result;
2932 #endif /* CURL_DISABLE_HTTP */
2934 if(conn->protocol & PROT_FTPS) {
2935 /* BLOCKING */
2936 /* FTPS is simply ftp with SSL for the control channel */
2937 /* now, perform the SSL initialization for this socket */
2938 result = Curl_ssl_connect(conn, FIRSTSOCKET);
2939 if(result)
2940 return result;
2943 /* When we connect, we start in the state where we await the 220
2944 response */
2945 ftp_respinit(conn); /* init the response reader stuff */
2946 state(conn, FTP_WAIT220);
2947 ftpc->response = Curl_tvnow(); /* start response time-out now! */
2949 if(data->state.used_interface == Curl_if_multi)
2950 result = Curl_ftp_multi_statemach(conn, done);
2951 else {
2952 result = ftp_easy_statemach(conn);
2953 if(!result)
2954 *done = TRUE;
2957 return result;
2960 /***********************************************************************
2962 * Curl_ftp_done()
2964 * The DONE function. This does what needs to be done after a single DO has
2965 * performed.
2967 * Input argument is already checked for validity.
2969 CURLcode Curl_ftp_done(struct connectdata *conn, CURLcode status, bool premature)
2971 struct SessionHandle *data = conn->data;
2972 struct FTP *ftp = data->reqdata.proto.ftp;
2973 struct ftp_conn *ftpc = &conn->proto.ftpc;
2974 ssize_t nread;
2975 int ftpcode;
2976 CURLcode result=CURLE_OK;
2977 bool was_ctl_valid = ftpc->ctl_valid;
2978 size_t flen;
2979 size_t dlen;
2980 char *path;
2981 char *path_to_use = data->reqdata.path;
2982 struct Curl_transfer_keeper *k = &data->reqdata.keep;
2984 if(!ftp)
2985 /* When the easy handle is removed from the multi while libcurl is still
2986 * trying to resolve the host name, it seems that the ftp struct is not
2987 * yet initialized, but the removal action calls Curl_done() which calls
2988 * this function. So we simply return success if no ftp pointer is set.
2990 return CURLE_OK;
2992 switch(status) {
2993 case CURLE_BAD_DOWNLOAD_RESUME:
2994 case CURLE_FTP_WEIRD_PASV_REPLY:
2995 case CURLE_FTP_PORT_FAILED:
2996 case CURLE_FTP_COULDNT_SET_BINARY:
2997 case CURLE_FTP_COULDNT_RETR_FILE:
2998 case CURLE_FTP_COULDNT_STOR_FILE:
2999 case CURLE_FTP_ACCESS_DENIED:
3000 /* the connection stays alive fine even though this happened */
3001 /* fall-through */
3002 case CURLE_OK: /* doesn't affect the control connection's status */
3003 if (!premature) {
3004 ftpc->ctl_valid = was_ctl_valid;
3005 break;
3007 /* until we cope better with prematurely ended requests, let them
3008 * fallback as if in complete failure */
3009 default: /* by default, an error means the control connection is
3010 wedged and should not be used anymore */
3011 ftpc->ctl_valid = FALSE;
3012 ftpc->cwdfail = TRUE; /* set this TRUE to prevent us to remember the
3013 current path, as this connection is going */
3014 conn->bits.close = TRUE; /* marked for closure */
3015 break;
3018 /* now store a copy of the directory we are in */
3019 if(ftpc->prevpath)
3020 free(ftpc->prevpath);
3022 /* get the "raw" path */
3023 path = curl_easy_unescape(data, path_to_use, 0, NULL);
3024 if(!path)
3025 return CURLE_OUT_OF_MEMORY;
3027 flen = ftp->file?strlen(ftp->file):0; /* file is "raw" already */
3028 dlen = strlen(path)-flen;
3029 if(dlen && !ftpc->cwdfail) {
3030 ftpc->prevpath = path;
3031 if(flen)
3032 /* if 'path' is not the whole string */
3033 ftpc->prevpath[dlen]=0; /* terminate */
3034 infof(data, "Remembering we are in dir %s\n", ftpc->prevpath);
3036 else {
3037 ftpc->prevpath = NULL; /* no path */
3038 free(path);
3040 /* free the dir tree and file parts */
3041 freedirs(conn);
3043 #ifdef HAVE_KRB4
3044 Curl_sec_fflush_fd(conn, conn->sock[SECONDARYSOCKET]);
3045 #endif
3047 /* shut down the socket to inform the server we're done */
3049 #ifdef _WIN32_WCE
3050 shutdown(conn->sock[SECONDARYSOCKET],2); /* SD_BOTH */
3051 #endif
3053 sclose(conn->sock[SECONDARYSOCKET]);
3055 conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
3057 if(!ftp->no_transfer && !status && !premature) {
3059 * Let's see what the server says about the transfer we just performed,
3060 * but lower the timeout as sometimes this connection has died while the
3061 * data has been transfered. This happens when doing through NATs etc that
3062 * abandon old silent connections.
3064 long old_time = ftpc->response_time;
3066 ftpc->response_time = 60; /* give it only a minute for now */
3068 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
3070 ftpc->response_time = old_time; /* set this back to previous value */
3072 if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) {
3073 failf(data, "control connection looks dead");
3074 ftpc->ctl_valid = FALSE; /* mark control connection as bad */
3075 return result;
3078 if(result)
3079 return result;
3081 if(!ftpc->dont_check) {
3082 /* 226 Transfer complete, 250 Requested file action okay, completed. */
3083 if((ftpcode != 226) && (ftpcode != 250)) {
3084 failf(data, "server did not report OK, got %d", ftpcode);
3085 result = CURLE_PARTIAL_FILE;
3090 if(result || premature)
3091 /* the response code from the transfer showed an error already so no
3092 use checking further */
3094 else if(data->set.upload) {
3095 if((-1 != data->set.infilesize) &&
3096 (data->set.infilesize != *ftp->bytecountp) &&
3097 !data->set.crlf &&
3098 !ftp->no_transfer) {
3099 failf(data, "Uploaded unaligned file size (%" FORMAT_OFF_T
3100 " out of %" FORMAT_OFF_T " bytes)",
3101 *ftp->bytecountp, data->set.infilesize);
3102 result = CURLE_PARTIAL_FILE;
3105 else {
3106 if((-1 != k->size) && (k->size != *ftp->bytecountp) &&
3107 #ifdef CURL_DO_LINEEND_CONV
3108 /* Most FTP servers don't adjust their file SIZE response for CRLFs, so
3109 * we'll check to see if the discrepancy can be explained by the number
3110 * of CRLFs we've changed to LFs.
3112 ((k->size + data->state.crlf_conversions) != *ftp->bytecountp) &&
3113 #endif /* CURL_DO_LINEEND_CONV */
3114 (k->maxdownload != *ftp->bytecountp)) {
3115 failf(data, "Received only partial file: %" FORMAT_OFF_T " bytes",
3116 *ftp->bytecountp);
3117 result = CURLE_PARTIAL_FILE;
3119 else if(!ftpc->dont_check &&
3120 !*ftp->bytecountp &&
3121 (k->size>0)) {
3122 failf(data, "No data was received!");
3123 result = CURLE_FTP_COULDNT_RETR_FILE;
3127 /* clear these for next connection */
3128 ftp->no_transfer = FALSE;
3129 ftpc->dont_check = FALSE;
3131 /* Send any post-transfer QUOTE strings? */
3132 if(!status && !result && !premature && data->set.postquote)
3133 result = ftp_sendquote(conn, data->set.postquote);
3135 return result;
3138 /***********************************************************************
3140 * ftp_sendquote()
3142 * Where a 'quote' means a list of custom commands to send to the server.
3143 * The quote list is passed as an argument.
3146 static
3147 CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote)
3149 struct curl_slist *item;
3150 ssize_t nread;
3151 int ftpcode;
3152 CURLcode result;
3154 item = quote;
3155 while (item) {
3156 if (item->data) {
3157 FTPSENDF(conn, "%s", item->data);
3159 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
3160 if (result)
3161 return result;
3163 if (ftpcode >= 400) {
3164 failf(conn->data, "QUOT string not accepted: %s", item->data);
3165 return CURLE_FTP_QUOTE_ERROR;
3169 item = item->next;
3172 return CURLE_OK;
3175 /***********************************************************************
3177 * ftp_need_type()
3179 * Returns TRUE if we in the current situation should send TYPE
3181 static int ftp_need_type(struct connectdata *conn,
3182 bool ascii_wanted)
3184 return conn->proto.ftpc.transfertype != (ascii_wanted?'A':'I');
3187 /***********************************************************************
3189 * ftp_nb_type()
3191 * Set TYPE. We only deal with ASCII or BINARY so this function
3192 * sets one of them.
3193 * If the transfer type is not sent, simulate on OK response in newstate
3195 static CURLcode ftp_nb_type(struct connectdata *conn,
3196 bool ascii, ftpstate newstate)
3198 struct ftp_conn *ftpc = &conn->proto.ftpc;
3199 CURLcode result;
3200 int want = ascii?'A':'I';
3202 if (ftpc->transfertype == want) {
3203 state(conn, newstate);
3204 return ftp_state_type_resp(conn, 200, newstate);
3207 NBFTPSENDF(conn, "TYPE %c", want);
3208 state(conn, newstate);
3210 /* keep track of our current transfer type */
3211 ftpc->transfertype = want;
3212 return CURLE_OK;
3215 /***************************************************************************
3217 * ftp_pasv_verbose()
3219 * This function only outputs some informationals about this second connection
3220 * when we've issued a PASV command before and thus we have connected to a
3221 * possibly new IP address.
3224 static void
3225 ftp_pasv_verbose(struct connectdata *conn,
3226 Curl_addrinfo *ai,
3227 char *newhost, /* ascii version */
3228 int port)
3230 char buf[256];
3231 Curl_printable_address(ai, buf, sizeof(buf));
3232 infof(conn->data, "Connecting to %s (%s) port %d\n", newhost, buf, port);
3236 Check if this is a range download, and if so, set the internal variables
3237 properly.
3240 static CURLcode ftp_range(struct connectdata *conn)
3242 curl_off_t from, to;
3243 curl_off_t totalsize=-1;
3244 char *ptr;
3245 char *ptr2;
3246 struct SessionHandle *data = conn->data;
3247 struct ftp_conn *ftpc = &conn->proto.ftpc;
3249 if(data->reqdata.use_range && data->reqdata.range) {
3250 from=curlx_strtoofft(data->reqdata.range, &ptr, 0);
3251 while(ptr && *ptr && (ISSPACE(*ptr) || (*ptr=='-')))
3252 ptr++;
3253 to=curlx_strtoofft(ptr, &ptr2, 0);
3254 if(ptr == ptr2) {
3255 /* we didn't get any digit */
3256 to=-1;
3258 if((-1 == to) && (from>=0)) {
3259 /* X - */
3260 data->reqdata.resume_from = from;
3261 DEBUGF(infof(conn->data, "FTP RANGE %" FORMAT_OFF_T " to end of file\n",
3262 from));
3264 else if(from < 0) {
3265 /* -Y */
3266 totalsize = -from;
3267 data->reqdata.maxdownload = -from;
3268 data->reqdata.resume_from = from;
3269 DEBUGF(infof(conn->data, "FTP RANGE the last %" FORMAT_OFF_T " bytes\n",
3270 totalsize));
3272 else {
3273 /* X-Y */
3274 totalsize = to-from;
3275 data->reqdata.maxdownload = totalsize+1; /* include last byte */
3276 data->reqdata.resume_from = from;
3277 DEBUGF(infof(conn->data, "FTP RANGE from %" FORMAT_OFF_T
3278 " getting %" FORMAT_OFF_T " bytes\n",
3279 from, data->reqdata.maxdownload));
3281 DEBUGF(infof(conn->data, "range-download from %" FORMAT_OFF_T
3282 " to %" FORMAT_OFF_T ", totally %" FORMAT_OFF_T " bytes\n",
3283 from, to, data->reqdata.maxdownload));
3284 ftpc->dont_check = TRUE; /* dont check for successful transfer */
3286 return CURLE_OK;
3291 * Curl_ftp_nextconnect()
3293 * This function shall be called when the second FTP (data) connection is
3294 * connected.
3297 CURLcode Curl_ftp_nextconnect(struct connectdata *conn)
3299 struct SessionHandle *data=conn->data;
3300 CURLcode result = CURLE_OK;
3302 /* the ftp struct is inited in Curl_ftp_connect() */
3303 struct FTP *ftp = data->reqdata.proto.ftp;
3305 DEBUGF(infof(data, "DO-MORE phase starts\n"));
3307 if(!ftp->no_transfer && !conn->bits.no_body) {
3308 /* a transfer is about to take place */
3310 if(data->set.upload) {
3311 result = ftp_nb_type(conn, data->set.prefer_ascii,
3312 FTP_STOR_TYPE);
3313 if (result)
3314 return result;
3316 else {
3317 /* download */
3318 ftp->downloadsize = -1; /* unknown as of yet */
3320 result = ftp_range(conn);
3321 if(result)
3323 else if((data->set.ftp_list_only) || !ftp->file) {
3324 /* The specified path ends with a slash, and therefore we think this
3325 is a directory that is requested, use LIST. But before that we
3326 need to set ASCII transfer mode. */
3327 result = ftp_nb_type(conn, 1, FTP_LIST_TYPE);
3328 if (result)
3329 return result;
3331 else {
3332 result = ftp_nb_type(conn, data->set.prefer_ascii, FTP_RETR_TYPE);
3333 if (result)
3334 return result;
3337 result = ftp_easy_statemach(conn);
3340 if(ftp->no_transfer)
3341 /* no data to transfer. FIX: it feels like a kludge to have this here
3342 too! */
3343 result=Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
3345 /* end of transfer */
3346 DEBUGF(infof(data, "DO-MORE phase ends with %d\n", result));
3348 return result;
3353 /***********************************************************************
3355 * ftp_perform()
3357 * This is the actual DO function for FTP. Get a file/directory according to
3358 * the options previously setup.
3361 static
3362 CURLcode ftp_perform(struct connectdata *conn,
3363 bool *connected, /* connect status after PASV / PORT */
3364 bool *dophase_done)
3366 /* this is FTP and no proxy */
3367 CURLcode result=CURLE_OK;
3369 DEBUGF(infof(conn->data, "DO phase starts\n"));
3371 *dophase_done = FALSE; /* not done yet */
3373 /* start the first command in the DO phase */
3374 result = ftp_state_quote(conn, TRUE, FTP_QUOTE);
3375 if(result)
3376 return result;
3378 /* run the state-machine */
3379 if(conn->data->state.used_interface == Curl_if_multi)
3380 result = Curl_ftp_multi_statemach(conn, dophase_done);
3381 else {
3382 result = ftp_easy_statemach(conn);
3383 *dophase_done = TRUE; /* with the easy interface we are done here */
3385 *connected = conn->bits.tcpconnect;
3387 if(*dophase_done) {
3388 DEBUGF(infof(conn->data, "DO phase is complete\n"));
3391 return result;
3394 /***********************************************************************
3396 * Curl_ftp()
3398 * This function is registered as 'curl_do' function. It decodes the path
3399 * parts etc as a wrapper to the actual DO function (ftp_perform).
3401 * The input argument is already checked for validity.
3403 CURLcode Curl_ftp(struct connectdata *conn, bool *done)
3405 CURLcode retcode = CURLE_OK;
3407 *done = FALSE; /* default to false */
3410 Since connections can be re-used between SessionHandles, this might be a
3411 connection already existing but on a fresh SessionHandle struct so we must
3412 make sure we have a good 'struct FTP' to play with. For new connections,
3413 the struct FTP is allocated and setup in the Curl_ftp_connect() function.
3415 retcode = ftp_init(conn);
3416 if(retcode)
3417 return retcode;
3419 retcode = ftp_parse_url_path(conn);
3420 if (retcode)
3421 return retcode;
3423 retcode = ftp_regular_transfer(conn, done);
3425 return retcode;
3428 /***********************************************************************
3430 * Curl_(nb)ftpsendf()
3432 * Sends the formated string as a ftp command to a ftp server
3434 * NOTE: we build the command in a fixed-length buffer, which sets length
3435 * restrictions on the command!
3437 * The "nb" version is made to Never Block.
3439 CURLcode Curl_nbftpsendf(struct connectdata *conn,
3440 const char *fmt, ...)
3442 ssize_t bytes_written;
3443 char s[256];
3444 size_t write_len;
3445 char *sptr=s;
3446 CURLcode res = CURLE_OK;
3447 struct SessionHandle *data = conn->data;
3448 struct ftp_conn *ftpc = &conn->proto.ftpc;
3450 va_list ap;
3451 va_start(ap, fmt);
3452 vsnprintf(s, 250, fmt, ap);
3453 va_end(ap);
3455 strcat(s, "\r\n"); /* append a trailing CRLF */
3457 bytes_written=0;
3458 write_len = strlen(s);
3460 ftp_respinit(conn);
3462 #ifdef CURL_DOES_CONVERSIONS
3463 res = Curl_convert_to_network(data, s, write_len);
3464 /* Curl_convert_to_network calls failf if unsuccessful */
3465 if(res != CURLE_OK) {
3466 return res;
3468 #endif /* CURL_DOES_CONVERSIONS */
3470 res = Curl_write(conn, conn->sock[FIRSTSOCKET], sptr, write_len,
3471 &bytes_written);
3473 if(CURLE_OK != res)
3474 return res;
3476 if(conn->data->set.verbose)
3477 Curl_debug(conn->data, CURLINFO_HEADER_OUT,
3478 sptr, (size_t)bytes_written, conn);
3480 if(bytes_written != (ssize_t)write_len) {
3481 /* the whole chunk was not sent, store the rest of the data */
3482 write_len -= bytes_written;
3483 sptr += bytes_written;
3484 ftpc->sendthis = malloc(write_len);
3485 if(ftpc->sendthis) {
3486 memcpy(ftpc->sendthis, sptr, write_len);
3487 ftpc->sendsize = ftpc->sendleft = write_len;
3489 else {
3490 failf(data, "out of memory");
3491 res = CURLE_OUT_OF_MEMORY;
3494 else
3495 ftpc->response = Curl_tvnow();
3497 return res;
3500 CURLcode Curl_ftpsendf(struct connectdata *conn,
3501 const char *fmt, ...)
3503 ssize_t bytes_written;
3504 char s[256];
3505 size_t write_len;
3506 char *sptr=s;
3507 CURLcode res = CURLE_OK;
3509 va_list ap;
3510 va_start(ap, fmt);
3511 vsnprintf(s, 250, fmt, ap);
3512 va_end(ap);
3514 strcat(s, "\r\n"); /* append a trailing CRLF */
3516 bytes_written=0;
3517 write_len = strlen(s);
3519 #ifdef CURL_DOES_CONVERSIONS
3520 res = Curl_convert_to_network(conn->data, s, write_len);
3521 /* Curl_convert_to_network calls failf if unsuccessful */
3522 if(res != CURLE_OK) {
3523 return(res);
3525 #endif /* CURL_DOES_CONVERSIONS */
3527 while(1) {
3528 res = Curl_write(conn, conn->sock[FIRSTSOCKET], sptr, write_len,
3529 &bytes_written);
3531 if(CURLE_OK != res)
3532 break;
3534 if(conn->data->set.verbose)
3535 Curl_debug(conn->data, CURLINFO_HEADER_OUT,
3536 sptr, (size_t)bytes_written, conn);
3538 if(bytes_written != (ssize_t)write_len) {
3539 write_len -= bytes_written;
3540 sptr += bytes_written;
3542 else
3543 break;
3546 return res;
3549 /***********************************************************************
3551 * ftp_quit()
3553 * This should be called before calling sclose() on an ftp control connection
3554 * (not data connections). We should then wait for the response from the
3555 * server before returning. The calling code should then try to close the
3556 * connection.
3559 static CURLcode ftp_quit(struct connectdata *conn)
3561 CURLcode result = CURLE_OK;
3563 if(conn->proto.ftpc.ctl_valid) {
3564 NBFTPSENDF(conn, "QUIT", NULL);
3565 state(conn, FTP_QUIT);
3567 result = ftp_easy_statemach(conn);
3570 return result;
3573 /***********************************************************************
3575 * Curl_ftp_disconnect()
3577 * Disconnect from an FTP server. Cleanup protocol-specific per-connection
3578 * resources. BLOCKING.
3580 CURLcode Curl_ftp_disconnect(struct connectdata *conn)
3582 struct ftp_conn *ftpc= &conn->proto.ftpc;
3584 /* We cannot send quit unconditionally. If this connection is stale or
3585 bad in any way, sending quit and waiting around here will make the
3586 disconnect wait in vain and cause more problems than we need to.
3588 ftp_quit() will check the state of ftp->ctl_valid. If it's ok it
3589 will try to send the QUIT command, otherwise it will just return.
3592 /* The FTP session may or may not have been allocated/setup at this point! */
3593 if(conn->data->reqdata.proto.ftp) {
3594 (void)ftp_quit(conn); /* ignore errors on the QUIT */
3596 if(ftpc->entrypath) {
3597 struct SessionHandle *data = conn->data;
3598 data->state.most_recent_ftp_entrypath = NULL;
3599 free(ftpc->entrypath);
3600 ftpc->entrypath = NULL;
3602 if(ftpc->cache) {
3603 free(ftpc->cache);
3604 ftpc->cache = NULL;
3606 freedirs(conn);
3607 if(ftpc->prevpath) {
3608 free(ftpc->prevpath);
3609 ftpc->prevpath = NULL;
3612 return CURLE_OK;
3615 /***********************************************************************
3617 * ftp_parse_url_path()
3619 * Parse the URL path into separate path components.
3622 static
3623 CURLcode ftp_parse_url_path(struct connectdata *conn)
3625 CURLcode retcode = CURLE_OK;
3626 struct SessionHandle *data = conn->data;
3627 /* the ftp struct is already inited in ftp_connect() */
3628 struct FTP *ftp = data->reqdata.proto.ftp;
3629 struct ftp_conn *ftpc = &conn->proto.ftpc;
3630 size_t dlen;
3631 char *slash_pos; /* position of the first '/' char in curpos */
3632 char *path_to_use = data->reqdata.path;
3633 char *cur_pos;
3635 cur_pos = path_to_use; /* current position in path. point at the begin
3636 of next path component */
3638 ftpc->ctl_valid = FALSE;
3639 ftpc->cwdfail = FALSE;
3641 switch(data->set.ftp_filemethod) {
3642 case FTPFILE_NOCWD:
3643 /* fastest, but less standard-compliant */
3644 ftp->file = data->reqdata.path; /* this is a full file path */
3645 break;
3647 case FTPFILE_SINGLECWD:
3648 /* get the last slash */
3649 slash_pos=strrchr(cur_pos, '/');
3650 if(slash_pos || !cur_pos || !*cur_pos) {
3651 ftpc->dirdepth = 1; /* we consider it to be a single dir */
3652 ftpc->dirs = (char **)calloc(1, sizeof(ftpc->dirs[0]));
3653 if(!ftpc->dirs)
3654 return CURLE_OUT_OF_MEMORY;
3656 ftpc->dirs[0] = curl_easy_unescape(conn->data, slash_pos ? cur_pos : "/",
3657 slash_pos?(int)(slash_pos-cur_pos):1,
3658 NULL);
3659 if(!ftpc->dirs[0]) {
3660 free(ftpc->dirs);
3661 return CURLE_OUT_OF_MEMORY;
3663 ftp->file = slash_pos ? slash_pos+1 : cur_pos; /* rest is file name */
3665 else
3666 ftp->file = cur_pos; /* this is a file name only */
3667 break;
3669 default: /* allow pretty much anything */
3670 case FTPFILE_MULTICWD:
3671 ftpc->dirdepth = 0;
3672 ftpc->diralloc = 5; /* default dir depth to allocate */
3673 ftpc->dirs = (char **)calloc(ftpc->diralloc, sizeof(ftpc->dirs[0]));
3674 if(!ftpc->dirs)
3675 return CURLE_OUT_OF_MEMORY;
3677 /* parse the URL path into separate path components */
3678 while ((slash_pos = strchr(cur_pos, '/')) != NULL) {
3679 /* 1 or 0 to indicate absolute directory */
3680 bool absolute_dir = (bool)((cur_pos - data->reqdata.path > 0) &&
3681 (ftpc->dirdepth == 0));
3683 /* seek out the next path component */
3684 if (slash_pos-cur_pos) {
3685 /* we skip empty path components, like "x//y" since the FTP command
3686 CWD requires a parameter and a non-existant parameter a) doesn't
3687 work on many servers and b) has no effect on the others. */
3688 int len = (int)(slash_pos - cur_pos + absolute_dir);
3689 ftpc->dirs[ftpc->dirdepth] = curl_easy_unescape(conn->data,
3690 cur_pos - absolute_dir,
3691 len, NULL);
3692 if (!ftpc->dirs[ftpc->dirdepth]) { /* run out of memory ... */
3693 failf(data, "no memory");
3694 freedirs(conn);
3695 return CURLE_OUT_OF_MEMORY;
3697 if (isBadFtpString(ftpc->dirs[ftpc->dirdepth])) {
3698 freedirs(conn);
3699 return CURLE_URL_MALFORMAT;
3702 else {
3703 cur_pos = slash_pos + 1; /* jump to the rest of the string */
3704 continue;
3707 if(!retcode) {
3708 cur_pos = slash_pos + 1; /* jump to the rest of the string */
3709 if(++ftpc->dirdepth >= ftpc->diralloc) {
3710 /* enlarge array */
3711 char *bigger;
3712 ftpc->diralloc *= 2; /* double the size each time */
3713 bigger = realloc(ftpc->dirs, ftpc->diralloc * sizeof(ftpc->dirs[0]));
3714 if(!bigger) {
3715 ftpc->dirdepth--;
3716 freedirs(conn);
3717 return CURLE_OUT_OF_MEMORY;
3719 ftpc->dirs = (char **)bigger;
3724 ftp->file = cur_pos; /* the rest is the file name */
3727 if(*ftp->file) {
3728 ftp->file = curl_easy_unescape(conn->data, ftp->file, 0, NULL);
3729 if(NULL == ftp->file) {
3730 freedirs(conn);
3731 failf(data, "no memory");
3732 return CURLE_OUT_OF_MEMORY;
3734 if (isBadFtpString(ftp->file)) {
3735 freedirs(conn);
3736 return CURLE_URL_MALFORMAT;
3739 else
3740 ftp->file=NULL; /* instead of point to a zero byte, we make it a NULL
3741 pointer */
3743 if(data->set.upload && !ftp->file &&
3744 (!ftp->no_transfer || conn->bits.no_body)) {
3745 /* We need a file name when uploading. Return error! */
3746 failf(data, "Uploading to a URL without a file name!");
3747 return CURLE_URL_MALFORMAT;
3750 ftpc->cwddone = FALSE; /* default to not done */
3752 if(ftpc->prevpath) {
3753 /* prevpath is "raw" so we convert the input path before we compare the
3754 strings */
3755 char *path = curl_easy_unescape(conn->data, data->reqdata.path, 0, NULL);
3756 if(!path)
3757 return CURLE_OUT_OF_MEMORY;
3759 dlen = strlen(path) - (ftp->file?strlen(ftp->file):0);
3760 if((dlen == strlen(ftpc->prevpath)) &&
3761 curl_strnequal(path, ftpc->prevpath, dlen)) {
3762 infof(data, "Request has same path as previous transfer\n");
3763 ftpc->cwddone = TRUE;
3765 free(path);
3768 return retcode;
3771 /* call this when the DO phase has completed */
3772 static CURLcode ftp_dophase_done(struct connectdata *conn,
3773 bool connected)
3775 CURLcode result = CURLE_OK;
3776 struct FTP *ftp = conn->data->reqdata.proto.ftp;
3777 struct ftp_conn *ftpc = &conn->proto.ftpc;
3779 if(connected)
3780 result = Curl_ftp_nextconnect(conn);
3782 if(result && (conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD)) {
3783 /* Failure detected, close the second socket if it was created already */
3784 sclose(conn->sock[SECONDARYSOCKET]);
3785 conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
3786 return result;
3789 if(ftp->no_transfer)
3790 /* no data to transfer */
3791 result=Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
3792 else if(!connected)
3793 /* since we didn't connect now, we want do_more to get called */
3794 conn->bits.do_more = TRUE;
3796 ftpc->ctl_valid = TRUE; /* seems good */
3798 return result;
3801 /* called from multi.c while DOing */
3802 CURLcode Curl_ftp_doing(struct connectdata *conn,
3803 bool *dophase_done)
3805 CURLcode result;
3806 result = Curl_ftp_multi_statemach(conn, dophase_done);
3808 if(*dophase_done) {
3809 result = ftp_dophase_done(conn, FALSE /* not connected */);
3811 DEBUGF(infof(conn->data, "DO phase is complete\n"));
3813 return result;
3816 /***********************************************************************
3818 * ftp_regular_transfer()
3820 * The input argument is already checked for validity.
3822 * Performs all commands done before a regular transfer between a local and a
3823 * remote host.
3825 * ftp->ctl_valid starts out as FALSE, and gets set to TRUE if we reach the
3826 * Curl_ftp_done() function without finding any major problem.
3828 static
3829 CURLcode ftp_regular_transfer(struct connectdata *conn,
3830 bool *dophase_done)
3832 CURLcode result=CURLE_OK;
3833 bool connected=0;
3834 struct SessionHandle *data = conn->data;
3835 struct ftp_conn *ftpc = &conn->proto.ftpc;
3836 data->reqdata.size = -1; /* make sure this is unknown at this point */
3838 Curl_pgrsSetUploadCounter(data, 0);
3839 Curl_pgrsSetDownloadCounter(data, 0);
3840 Curl_pgrsSetUploadSize(data, 0);
3841 Curl_pgrsSetDownloadSize(data, 0);
3843 ftpc->ctl_valid = TRUE; /* starts good */
3845 result = ftp_perform(conn,
3846 &connected, /* have we connected after PASV/PORT */
3847 dophase_done); /* all commands in the DO-phase done? */
3849 if(CURLE_OK == result) {
3851 if(!*dophase_done)
3852 /* the DO phase has not completed yet */
3853 return CURLE_OK;
3855 result = ftp_dophase_done(conn, connected);
3856 if(result)
3857 return result;
3859 else
3860 freedirs(conn);
3862 return result;
3865 #endif /* CURL_DISABLE_FTP */