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