Annotation of embedaddon/curl/lib/ftp.c, revision 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>