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