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