1 /***************************************************************************
3 * Project ___| | | | _ \| |
5 * | (__| |_| | _ <| |___
6 * \___|\___/|_| \_\_____|
8 * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
10 * This software is licensed as described in the file COPYING, which
11 * you should have received as part of this distribution. The terms
12 * are also available at http://curl.haxx.se/docs/copyright.html.
14 * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15 * copies of the Software, and permit persons to whom the Software is
16 * furnished to do so, under the terms of the COPYING file.
18 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19 * KIND, either express or implied.
22 ***************************************************************************/
26 #ifndef CURL_DISABLE_FTP
38 #if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
40 #else /* probably some kind of unix */
41 #ifdef HAVE_SYS_SOCKET_H
42 #include <sys/socket.h>
44 #include <sys/types.h>
45 #ifdef HAVE_NETINET_IN_H
46 #include <netinet/in.h>
48 #ifdef HAVE_ARPA_INET_H
49 #include <arpa/inet.h>
52 #include <sys/utsname.h>
63 #if defined(WIN32) && defined(__GNUC__) || defined(__MINGW32__)
67 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
69 #define in_addr_t unsigned long
72 #include <curl/curl.h>
81 #include "http.h" /* for HTTP proxy tunnel stuff */
89 #include "strtoofft.h"
95 #include "inet_ntop.h"
98 #if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
99 #include "inet_ntoa_r.h"
102 #define _MPRINTF_REPLACE /* use our functions only */
103 #include <curl/mprintf.h>
105 /* The last #include file should be: */
107 #include "memdebug.h"
110 #ifdef HAVE_NI_WITHSCOPEID
111 #define NIFLAGS NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID
113 #define NIFLAGS NI_NUMERICHOST | NI_NUMERICSERV
116 /* Local API functions */
117 static CURLcode ftp_sendquote(struct connectdata *conn,
118 struct curl_slist *quote);
119 static CURLcode ftp_cwd(struct connectdata *conn, char *path);
120 static CURLcode ftp_mkd(struct connectdata *conn, char *path);
121 static CURLcode ftp_cwd_and_mkd(struct connectdata *conn, char *path);
122 static CURLcode ftp_quit(struct connectdata *conn);
123 static CURLcode ftp_3rdparty_pretransfer(struct connectdata *conn);
124 static CURLcode ftp_3rdparty_transfer(struct connectdata *conn);
125 static CURLcode ftp_parse_url_path(struct connectdata *conn);
126 static CURLcode ftp_cwd_and_create_path(struct connectdata *conn);
127 static CURLcode ftp_regular_transfer(struct connectdata *conn);
128 static CURLcode ftp_3rdparty(struct connectdata *conn);
130 /* easy-to-use macro: */
131 #define FTPSENDF(x,y,z) if((result = Curl_ftpsendf(x,y,z))) return result
133 static void freedirs(struct FTP *ftp)
137 for (i=0; i < ftp->dirdepth; i++){
152 /***********************************************************************
154 * AllowServerConnect()
156 * When we've issue the PORT command, we have told the server to connect
157 * to us. This function will sit and wait here until the server has
161 static CURLcode AllowServerConnect(struct connectdata *conn)
164 struct SessionHandle *data = conn->data;
165 curl_socket_t sock = conn->sock[SECONDARYSOCKET];
166 struct timeval now = Curl_tvnow();
167 long timespent = Curl_tvdiff(Curl_tvnow(), now)/1000;
168 long timeout = data->set.connecttimeout?data->set.connecttimeout:
169 (data->set.timeout?data->set.timeout: 0);
172 timeout -= timespent;
174 failf(data, "Timed out before server could connect to us");
175 return CURLE_OPERATION_TIMEDOUT;
179 /* We allow the server 60 seconds to connect to us, or a custom timeout.
180 Note the typecast here. */
181 timeout_ms = (timeout?(int)timeout:60) * 1000;
183 switch (Curl_select(sock, CURL_SOCKET_BAD, timeout_ms)) {
186 failf(data, "Error while waiting for server connect");
187 return CURLE_FTP_PORT_FAILED;
188 case 0: /* timeout */
190 failf(data, "Timeout while waiting for server connect");
191 return CURLE_FTP_PORT_FAILED;
193 /* we have received data here */
196 size_t size = sizeof(struct sockaddr_in);
197 struct sockaddr_in add;
199 getsockname(sock, (struct sockaddr *) &add, (socklen_t *)&size);
200 s=accept(sock, (struct sockaddr *) &add, (socklen_t *)&size);
202 sclose(sock); /* close the first socket */
204 if (CURL_SOCKET_BAD == s) {
206 failf(data, "Error accept()ing server connect");
207 return CURLE_FTP_PORT_FAILED;
209 infof(data, "Connection accepted from server\n");
211 conn->sock[SECONDARYSOCKET] = s;
212 Curl_nonblock(s, TRUE); /* enable non-blocking */
221 /* --- parse FTP server responses --- */
224 * Curl_GetFTPResponse() is supposed to be invoked after each command sent to
225 * a remote FTP server. This function will wait and read all lines of the
226 * response and extract the relevant return code for the invoking function.
229 CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */
230 struct connectdata *conn,
231 int *ftpcode) /* return the ftp-code */
233 /* Brand new implementation.
234 * We cannot read just one byte per read() and then go back to select()
235 * as it seems that the OpenSSL read() stuff doesn't grok that properly.
237 * Alas, read as much as possible, split up into lines, use the ending
238 * line in a response or continue reading. */
240 curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
241 int perline; /* count bytes per line */
245 long timeout; /* timeout in seconds */
247 struct SessionHandle *data = conn->data;
249 int code=0; /* default ftp "error code" to return */
250 char *buf = data->state.buffer;
251 CURLcode result = CURLE_OK;
252 struct FTP *ftp = conn->proto.ftp;
253 struct timeval now = Curl_tvnow();
256 *ftpcode = 0; /* 0 for errors */
265 while((*nreadp<BUFSIZE) && (keepon && !result)) {
266 /* check and reset timeout value every lap */
267 if(data->set.ftp_response_timeout )
268 /* if CURLOPT_FTP_RESPONSE_TIMEOUT is set, use that to determine
269 remaining time. Also, use "now" as opposed to "conn->now"
270 because ftp_response_timeout is only supposed to govern
271 the response for any given ftp response, not for the time
272 from connect to the given ftp response. */
273 timeout = data->set.ftp_response_timeout - /* timeout time */
274 Curl_tvdiff(Curl_tvnow(), now)/1000; /* spent time */
275 else if(data->set.timeout)
276 /* if timeout is requested, find out how much remaining time we have */
277 timeout = data->set.timeout - /* timeout time */
278 Curl_tvdiff(Curl_tvnow(), conn->now)/1000; /* spent time */
280 /* Even without a requested timeout, we only wait response_time
281 seconds for the full response to arrive before we bail out */
282 timeout = ftp->response_time -
283 Curl_tvdiff(Curl_tvnow(), now)/1000; /* spent time */
286 failf(data, "FTP response timeout");
287 return CURLE_OPERATION_TIMEDOUT; /* already too little time */
291 interval_ms = 1 * 1000; /* use 1 second timeout intervals */
293 switch (Curl_select(sockfd, CURL_SOCKET_BAD, interval_ms)) {
294 case -1: /* select() error, stop reading */
295 result = CURLE_RECV_ERROR;
296 failf(data, "FTP response aborted due to select() error: %d", errno);
298 case 0: /* timeout */
299 if(Curl_pgrsUpdate(conn))
300 return CURLE_ABORTED_BY_CALLBACK;
301 continue; /* just continue in our loop for the timeout duration */
307 if(CURLE_OK == result) {
309 * This code previously didn't use the kerberos sec_read() code
310 * to read, but when we use Curl_read() it may do so. Do confirm
311 * that this is still ok and then remove this comment!
314 /* we had data in the "cache", copy that instead of doing an actual
317 * Dave Meyer, December 2003:
318 * ftp->cache_size is cast to int here. This should be safe,
319 * because it would have been populated with something of size
320 * int to begin with, even though its datatype may be larger
323 memcpy(ptr, ftp->cache, (int)ftp->cache_size);
324 gotbytes = (int)ftp->cache_size;
325 free(ftp->cache); /* free the cache */
326 ftp->cache = NULL; /* clear the pointer */
327 ftp->cache_size = 0; /* zero the size just in case */
330 int res = Curl_read(conn, sockfd, ptr, BUFSIZE-*nreadp, &gotbytes);
333 continue; /* go looping again */
341 else if(gotbytes <= 0) {
343 result = CURLE_RECV_ERROR;
344 failf(data, "FTP response reading failed");
347 /* we got a whole chunk of data, which can be anything from one
348 * byte to a set of lines and possible just a piece of the last
352 conn->headerbytecount += gotbytes;
355 for(i = 0; i < gotbytes; ptr++, i++) {
358 /* a newline is CRLF in ftp-talk, so the CR is ignored as
359 the line isn't really terminated until the LF comes */
361 /* output debug output if that is requested */
362 if(data->set.verbose)
363 Curl_debug(data, CURLINFO_HEADER_IN, line_start, perline, conn->host.dispname);
366 * We pass all response-lines to the callback function registered
367 * for "headers". The response lines can be seen as a kind of
370 result = Curl_client_write(data, CLIENTWRITE_HEADER,
371 line_start, perline);
375 #define lastline(line) (isdigit((int)line[0]) && isdigit((int)line[1]) && \
376 isdigit((int)line[2]) && (' ' == line[3]))
378 if(perline>3 && lastline(line_start)) {
379 /* This is the end of the last line, copy the last
380 * line to the start of the buffer and zero terminate,
381 * for old times sake (and krb4)! */
384 for(meow=line_start, n=0; meow<ptr; meow++, n++)
386 *meow=0; /* zero terminate */
388 line_start = ptr+1; /* advance pointer */
389 i++; /* skip this before getting out */
392 perline=0; /* line starts over here */
396 if(!keepon && (i != gotbytes)) {
397 /* We found the end of the response lines, but we didn't parse the
398 full chunk of data we have read from the server. We therefore
399 need to store the rest of the data to be checked on the next
400 invoke as it may actually contain another end of response
401 already! Cleverly figured out by Eric Lavigne in December
403 ftp->cache_size = gotbytes - i;
404 ftp->cache = (char *)malloc((int)ftp->cache_size);
406 memcpy(ftp->cache, line_start, (int)ftp->cache_size);
408 return CURLE_OUT_OF_MEMORY; /**BANG**/
410 } /* there was data */
412 } /* while there's buffer left and loop is requested */
418 /* handle the security-oriented responses 6xx ***/
419 /* FIXME: some errorchecking perhaps... ***/
422 Curl_sec_read_msg(conn, buf, prot_safe);
425 Curl_sec_read_msg(conn, buf, prot_private);
428 Curl_sec_read_msg(conn, buf, prot_confidential);
431 /* normal ftp stuff we pass through! */
437 *ftpcode=code; /* return the initial number like this */
439 /* store the latest code for later retrieval */
440 conn->data->info.httpcode=code;
445 static const char *ftpauth[]= {
450 * Curl_ftp_connect() should do everything that is to be considered a part of
451 * the connection phase.
453 CURLcode Curl_ftp_connect(struct connectdata *conn)
455 /* this is FTP and no proxy */
457 struct SessionHandle *data=conn->data;
458 char *buf = data->state.buffer; /* this is our buffer */
463 ftp = (struct FTP *)malloc(sizeof(struct FTP));
465 return CURLE_OUT_OF_MEMORY;
467 memset(ftp, 0, sizeof(struct FTP));
468 conn->proto.ftp = ftp;
470 /* We always support persistant connections on ftp */
471 conn->bits.close = FALSE;
473 /* get some initial data into the ftp struct */
474 ftp->bytecountp = &conn->bytecount;
476 /* no need to duplicate them, this connectdata struct won't change */
477 ftp->user = conn->user;
478 ftp->passwd = conn->passwd;
479 ftp->response_time = 3600; /* set default response time-out */
481 #ifndef CURL_DISABLE_HTTP
482 if (conn->bits.tunnel_proxy) {
483 /* We want "seamless" FTP operations through HTTP proxy tunnel */
484 result = Curl_ConnectHTTPProxyTunnel(conn, FIRSTSOCKET,
485 conn->host.name, conn->remote_port);
486 if(CURLE_OK != result)
489 #endif /* CURL_DISABLE_HTTP */
491 if(conn->protocol & PROT_FTPS) {
492 /* FTPS is simply ftp with SSL for the control channel */
493 /* now, perform the SSL initialization for this socket */
494 result = Curl_SSLConnect(conn, FIRSTSOCKET);
499 /* The first thing we do is wait for the "220*" line: */
500 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
505 failf(data, "This doesn't seem like a nice ftp-server response");
506 return CURLE_FTP_WEIRD_SERVER_REPLY;
510 /* if not anonymous login, try a secure login */
513 /* request data protection level (default is 'clear') */
514 Curl_sec_request_prot(conn, "private");
516 /* We set private first as default, in case the line below fails to
518 Curl_sec_request_prot(conn, data->set.krb4_level);
520 if(Curl_sec_login(conn) != 0)
521 infof(data, "Logging in with password in cleartext!\n");
523 infof(data, "Authentication successful\n");
527 if(data->set.ftp_ssl && !conn->ssl[FIRSTSOCKET].use) {
528 /* we don't have a SSL/TLS connection, try a FTPS connection now */
533 switch(data->set.ftpsslauth) {
534 case CURLFTPAUTH_DEFAULT:
535 case CURLFTPAUTH_SSL:
539 case CURLFTPAUTH_TLS:
544 failf(data, "unsupported parameter to CURLOPT_FTPSSLAUTH: %d\n",
545 data->set.ftpsslauth);
546 return CURLE_FAILED_INIT; /* we don't know what to do */
549 for (trynum = start; ftpauth[count]; trynum=trynext, count++) {
551 FTPSENDF(conn, "AUTH %s", ftpauth[trynum]);
553 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
558 /* RFC2228 (page 5) says:
560 * If the server is willing to accept the named security mechanism, and
561 * does not require any security data, it must respond with reply code
565 if((ftpcode == 234) || (ftpcode == 334)) {
566 result = Curl_SSLConnect(conn, FIRSTSOCKET);
569 conn->protocol |= PROT_FTPS;
570 conn->ssl[SECONDARYSOCKET].use = FALSE; /* clear-text data */
577 FTPSENDF(conn, "USER %s", ftp->user?ftp->user:"");
579 /* wait for feedback */
580 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
585 /* 530 User ... access denied
586 (the server denies to log the specified user) */
587 failf(data, "Access denied: %s", &buf[4]);
588 return CURLE_FTP_ACCESS_DENIED;
590 else if(ftpcode == 331) {
591 /* 331 Password required for ...
592 (the server requires to send the user's password too) */
593 FTPSENDF(conn, "PASS %s", ftp->passwd?ftp->passwd:"");
594 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
599 /* 530 Login incorrect.
600 (the username and/or the password are incorrect)
602 530 Sorry, the maximum number of allowed users are already connected
604 failf(data, "not logged in: %s", &buf[4]);
605 return CURLE_FTP_USER_PASSWORD_INCORRECT;
607 else if(ftpcode/100 == 2) {
608 /* 230 User ... logged in.
609 (user successfully logged in)
611 Apparently, proftpd with SSL returns 232 here at times. */
613 infof(data, "We have successfully logged in\n");
616 failf(data, "Odd return code after PASS");
617 return CURLE_FTP_WEIRD_PASS_REPLY;
620 else if(buf[0] == '2') {
621 /* 230 User ... logged in.
622 (the user logged in without password) */
623 infof(data, "We have successfully logged in\n");
624 if (conn->ssl[FIRSTSOCKET].use) {
626 /* We are logged in with Kerberos, now set the requested protection
629 if(conn->sec_complete)
630 Curl_sec_set_protection_level(conn);
632 /* We may need to issue a KAUTH here to have access to the files
633 * do it if user supplied a password
635 if(conn->passwd && *conn->passwd) {
636 result = Curl_krb_kauth(conn);
644 failf(data, "Odd return code after USER");
645 return CURLE_FTP_WEIRD_USER_REPLY;
648 if(conn->ssl[FIRSTSOCKET].use) {
649 /* PBSZ = PROTECTION BUFFER SIZE.
651 The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says:
653 Specifically, the PROT command MUST be preceded by a PBSZ command
654 and a PBSZ command MUST be preceded by a successful security data
655 exchange (the TLS negotiation in this case)
659 Thus the PBSZ command must still be issued, but must have a parameter
660 of '0' to indicate that no buffering is taking place and the data
661 connection should not be encapsulated.
663 FTPSENDF(conn, "PBSZ %d", 0);
664 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
668 /* For TLS, the data connection can have one of two security levels.
670 1)Clear (requested by 'PROT C')
672 2)Private (requested by 'PROT P')
674 if(!conn->ssl[SECONDARYSOCKET].use) {
675 FTPSENDF(conn, "PROT %c", 'P');
676 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
681 /* We have enabled SSL for the data connection! */
682 conn->ssl[SECONDARYSOCKET].use = TRUE;
683 /* FTP servers typically responds with 500 if they decide to reject
685 else if(data->set.ftp_ssl> CURLFTPSSL_CONTROL)
686 /* we failed and bails out */
687 return CURLE_FTP_SSL_FAILED;
691 /* send PWD to discover our entry point */
692 FTPSENDF(conn, "PWD", NULL);
694 /* wait for feedback */
695 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
700 char *dir = (char *)malloc(nread+1);
702 char *ptr=&buf[4]; /* start on the first letter */
705 return CURLE_OUT_OF_MEMORY;
707 /* Reply format is like
708 257<space>"<directory-name>"<space><commentary> and the RFC959 says
710 The directory name can contain any character; embedded double-quotes
711 should be escaped by double-quotes (the "quote-doubling" convention).
714 /* it started good */
719 /* "quote-doubling" */
725 *store = '\0'; /* zero terminate */
726 break; /* get out of this loop */
734 ftp->entrypath =dir; /* remember this */
735 infof(data, "Entry path is '%s'\n", ftp->entrypath);
738 /* couldn't get the path */
740 infof(data, "Failed to figure out path\n");
745 /* We couldn't read the PWD response! */
751 /***********************************************************************
755 * The DONE function. This does what needs to be done after a single DO has
758 * Input argument is already checked for validity.
760 CURLcode Curl_ftp_done(struct connectdata *conn, CURLcode status)
762 struct SessionHandle *data = conn->data;
763 struct FTP *ftp = conn->proto.ftp;
766 CURLcode result=CURLE_OK;
768 bool was_ctl_valid = ftp->ctl_valid;
770 /* now store a copy of the directory we are in */
774 size_t flen = ftp->file?strlen(ftp->file):0;
775 size_t dlen = conn->path?strlen(conn->path)-flen:0;
777 ftp->prevpath = malloc(dlen + 1);
779 return CURLE_OUT_OF_MEMORY;
780 memcpy(ftp->prevpath, conn->path, dlen);
781 ftp->prevpath[dlen]=0; /* terminate */
782 infof(data, "Remembering we are in dir %s\n", ftp->prevpath);
785 ftp->prevpath = NULL; /* no path */
788 /* free the dir tree and file parts */
791 ftp->ctl_valid = FALSE;
793 if(data->set.upload) {
794 if((-1 != data->set.infilesize) &&
795 (data->set.infilesize != *ftp->bytecountp) &&
797 failf(data, "Uploaded unaligned file size (%" FORMAT_OFF_T
798 " out of %" FORMAT_OFF_T " bytes)",
799 *ftp->bytecountp, data->set.infilesize);
800 conn->bits.close = TRUE; /* close this connection since we don't
801 know what state this error leaves us in */
802 return CURLE_PARTIAL_FILE;
806 if((-1 != conn->size) && (conn->size != *ftp->bytecountp) &&
807 (conn->maxdownload != *ftp->bytecountp)) {
808 failf(data, "Received only partial file: %" FORMAT_OFF_T " bytes",
810 conn->bits.close = TRUE; /* close this connection since we don't
811 know what state this error leaves us in */
812 return CURLE_PARTIAL_FILE;
814 else if(!ftp->dont_check &&
817 /* We consider this an error, but there's no true FTP error received
818 why we need to continue to "read out" the server response too.
819 We don't want to leave a "waiting" server reply if we'll get told
820 to make a second request on this same connection! */
821 failf(data, "No data was received!");
822 result = CURLE_FTP_COULDNT_RETR_FILE;
827 case CURLE_BAD_DOWNLOAD_RESUME:
828 case CURLE_FTP_WEIRD_PASV_REPLY:
829 case CURLE_FTP_PORT_FAILED:
830 case CURLE_FTP_COULDNT_SET_BINARY:
831 case CURLE_FTP_COULDNT_RETR_FILE:
832 case CURLE_FTP_ACCESS_DENIED:
833 /* the connection stays alive fine even though this happened */
835 case CURLE_OK: /* doesn't affect the control connection's status */
836 ftp->ctl_valid = was_ctl_valid;
838 default: /* by default, an error means the control connection is
839 wedged and should not be used anymore */
840 ftp->ctl_valid = FALSE;
845 Curl_sec_fflush_fd(conn, conn->sock[SECONDARYSOCKET]);
848 /* shut down the socket to inform the server we're done */
851 shutdown(conn->sock[SECONDARYSOCKET],2); /* SD_BOTH */
854 sclose(conn->sock[SECONDARYSOCKET]);
856 conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
858 if(!ftp->no_transfer && !status) {
859 /* Let's see what the server says about the transfer we just performed,
860 * but lower the timeout as sometimes this connection has died while the
861 * data has been transfered. This happens when doing through NATs etc that
862 * abandon old silent connections.
864 ftp->response_time = 60; /* give it only a minute for now */
866 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
868 ftp->response_time = 3600; /* set this back to one hour waits */
870 if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) {
871 failf(data, "control connection looks dead");
878 if(!ftp->dont_check) {
879 /* 226 Transfer complete, 250 Requested file action okay, completed. */
880 if((ftpcode != 226) && (ftpcode != 250)) {
881 failf(data, "server did not report OK, got %d", ftpcode);
882 return CURLE_FTP_WRITE_ERROR;
887 /* clear these for next connection */
888 ftp->no_transfer = FALSE;
889 ftp->dont_check = FALSE;
891 if (!result && conn->sec_conn) { /* 3rd party transfer */
892 /* "done" with the secondary connection */
893 result = Curl_ftp_done(conn->sec_conn, status);
896 /* Send any post-transfer QUOTE strings? */
897 if(!status && !result && data->set.postquote)
898 result = ftp_sendquote(conn, data->set.postquote);
903 /***********************************************************************
907 * Where a 'quote' means a list of custom commands to send to the server.
908 * The quote list is passed as an argument.
912 CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote)
914 struct curl_slist *item;
922 FTPSENDF(conn, "%s", item->data);
924 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
928 if (ftpcode >= 400) {
929 failf(conn->data, "QUOT string not accepted: %s", item->data);
930 return CURLE_FTP_QUOTE_ERROR;
940 /***********************************************************************
944 * Get the timestamp of the given file.
947 CURLcode ftp_getfiletime(struct connectdata *conn, char *file)
949 CURLcode result=CURLE_OK;
950 int ftpcode; /* for ftp status */
952 char *buf = conn->data->state.buffer;
954 /* we have requested to get the modified-time of the file, this is yet
955 again a grey area as the MDTM is not kosher RFC959 */
956 FTPSENDF(conn, "MDTM %s", file);
958 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
965 /* we got a time. Format should be: "YYYYMMDDHHMMSS[.sss]" where the
966 last .sss part is optional and means fractions of a second */
967 int year, month, day, hour, minute, second;
968 if(6 == sscanf(buf+4, "%04d%02d%02d%02d%02d%02d",
969 &year, &month, &day, &hour, &minute, &second)) {
970 /* we have a time, reformat it */
971 time_t secs=time(NULL);
972 /* using the good old yacc/bison yuck */
973 snprintf(buf, sizeof(conn->data->state.buffer),
974 "%04d%02d%02d %02d:%02d:%02d GMT",
975 year, month, day, hour, minute, second);
976 /* now, convert this into a time() value: */
977 conn->data->info.filetime = curl_getdate(buf, &secs);
982 infof(conn->data, "unsupported MDTM reply format\n");
984 case 550: /* "No such file or directory" */
985 failf(conn->data, "Given file does not exist");
986 result = CURLE_FTP_COULDNT_RETR_FILE;
992 /***********************************************************************
996 * Set transfer type. We only deal with ASCII or BINARY so this function
999 static CURLcode ftp_transfertype(struct connectdata *conn,
1002 struct SessionHandle *data = conn->data;
1007 FTPSENDF(conn, "TYPE %s", ascii?"A":"I");
1009 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1013 if(ftpcode != 200) {
1014 failf(data, "Couldn't set %s mode",
1015 ascii?"ASCII":"binary");
1016 return ascii? CURLE_FTP_COULDNT_SET_ASCII:CURLE_FTP_COULDNT_SET_BINARY;
1022 /***********************************************************************
1026 * Returns the file size (in bytes) of the given remote file.
1030 CURLcode ftp_getsize(struct connectdata *conn, char *file,
1033 struct SessionHandle *data = conn->data;
1036 char *buf=data->state.buffer;
1039 FTPSENDF(conn, "SIZE %s", file);
1040 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1044 if(ftpcode == 213) {
1045 /* get the size from the ascii string: */
1046 *size = curlx_strtoofft(buf+4, NULL, 0);
1049 return CURLE_FTP_COULDNT_GET_SIZE;
1054 /***************************************************************************
1056 * ftp_pasv_verbose()
1058 * This function only outputs some informationals about this second connection
1059 * when we've issued a PASV command before and thus we have connected to a
1060 * possibly new IP address.
1064 ftp_pasv_verbose(struct connectdata *conn,
1066 char *newhost, /* ascii version */
1070 Curl_printable_address(ai, buf, sizeof(buf));
1071 infof(conn->data, "Connecting to %s (%s) port %d\n", newhost, buf, port);
1074 /***********************************************************************
1078 * Send the proper PORT command. PORT is the ftp client's way of telling the
1079 * server that *WE* open a port that we listen on an awaits the server to
1080 * connect to. This is the opposite of PASV.
1084 CURLcode ftp_use_port(struct connectdata *conn)
1086 struct SessionHandle *data=conn->data;
1087 curl_socket_t portsock= CURL_SOCKET_BAD;
1089 int ftpcode; /* receive FTP response codes in this */
1093 /******************************************************************
1095 * Here's a piece of IPv6-specific code coming up
1099 struct addrinfo hints, *res, *ai;
1100 struct sockaddr_storage ss;
1102 char hbuf[NI_MAXHOST];
1104 struct sockaddr *sa=(struct sockaddr *)&ss;
1107 char portmsgbuf[1024], tmp[1024];
1109 enum ftpcommand { EPRT, LPRT, PORT, DONE } fcmd;
1110 const char *mode[] = { "EPRT", "LPRT", "PORT", NULL };
1115 * we should use Curl_if2ip? given pickiness of recent ftpd,
1116 * I believe we should use the same address as the control connection.
1119 rc = getsockname(conn->sock[FIRSTSOCKET], (struct sockaddr *)&ss, &sslen);
1121 failf(data, "getsockname() returned %d\n", rc);
1122 return CURLE_FTP_PORT_FAILED;
1125 rc = getnameinfo((struct sockaddr *)&ss, sslen, hbuf, sizeof(hbuf), NULL, 0,
1128 failf(data, "getnameinfo() returned %d\n", rc);
1129 return CURLE_FTP_PORT_FAILED;
1132 memset(&hints, 0, sizeof(hints));
1133 hints.ai_family = sa->sa_family;
1134 /*hints.ai_family = ss.ss_family;
1135 this way can be used if sockaddr_storage is properly defined, as glibc
1137 hints.ai_socktype = SOCK_STREAM;
1138 hints.ai_flags = AI_PASSIVE;
1140 rc = getaddrinfo(hbuf, NULL, &hints, &res);
1142 failf(data, "getaddrinfo() returned %d\n", rc);
1143 return CURLE_FTP_PORT_FAILED;
1146 portsock = CURL_SOCKET_BAD;
1148 for (ai = res; ai; ai = ai->ai_next) {
1150 * Workaround for AIX5 getaddrinfo() problem (it doesn't set ai_socktype):
1152 if (ai->ai_socktype == 0)
1153 ai->ai_socktype = hints.ai_socktype;
1155 portsock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
1156 if (portsock == CURL_SOCKET_BAD) {
1157 error = Curl_ourerrno();
1161 if (bind(portsock, ai->ai_addr, ai->ai_addrlen) < 0) {
1162 error = Curl_ourerrno();
1164 portsock = CURL_SOCKET_BAD;
1168 if (listen(portsock, 1) < 0) {
1169 error = Curl_ourerrno();
1171 portsock = CURL_SOCKET_BAD;
1178 if (portsock == CURL_SOCKET_BAD) {
1179 failf(data, "%s", Curl_strerror(conn,error));
1180 return CURLE_FTP_PORT_FAILED;
1184 if (getsockname(portsock, sa, &sslen) < 0) {
1185 failf(data, "%s", Curl_strerror(conn,Curl_ourerrno()));
1186 return CURLE_FTP_PORT_FAILED;
1190 if(!conn->bits.ftp_use_eprt &&
1191 (conn->ip_addr->ai_family == PF_INET6)) {
1192 /* EPRT is disabled but we are connected to a IPv6 host, so we ignore the
1194 conn->bits.ftp_use_eprt = TRUE;
1199 for (fcmd = EPRT; fcmd != DONE; fcmd++) {
1203 if(!conn->bits.ftp_use_eprt && (EPRT == fcmd))
1204 /* if disabled, goto next */
1207 if(!conn->bits.ftp_use_lprt && (LPRT == fcmd))
1208 /* if disabled, goto next */
1211 switch (sa->sa_family) {
1213 ap = (unsigned char *)&((struct sockaddr_in *)&ss)->sin_addr;
1214 alen = sizeof(((struct sockaddr_in *)&ss)->sin_addr);
1215 pp = (unsigned char *)&((struct sockaddr_in *)&ss)->sin_port;
1216 plen = sizeof(((struct sockaddr_in *)&ss)->sin_port);
1221 ap = (unsigned char *)&((struct sockaddr_in6 *)&ss)->sin6_addr;
1222 alen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_addr);
1223 pp = (unsigned char *)&((struct sockaddr_in6 *)&ss)->sin6_port;
1224 plen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_port);
1230 lprtaf = eprtaf = -1;
1237 if (getnameinfo((struct sockaddr *)&ss, sslen,
1238 portmsgbuf, sizeof(portmsgbuf), tmp, sizeof(tmp),
1242 /* do not transmit IPv6 scope identifier to the wire */
1243 if (sa->sa_family == AF_INET6) {
1244 char *q = strchr(portmsgbuf, '%');
1249 result = Curl_ftpsendf(conn, "%s |%d|%s|%s|", mode[fcmd], eprtaf,
1254 else if ((LPRT == fcmd) || (PORT == fcmd)) {
1257 if ((LPRT == fcmd) && lprtaf < 0)
1259 if ((PORT == fcmd) && sa->sa_family != AF_INET)
1262 portmsgbuf[0] = '\0';
1264 snprintf(tmp, sizeof(tmp), "%d,%d", lprtaf, alen);
1265 if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >=
1266 sizeof(portmsgbuf)) {
1271 for (i = 0; i < alen; i++) {
1273 snprintf(tmp, sizeof(tmp), ",%u", ap[i]);
1275 snprintf(tmp, sizeof(tmp), "%u", ap[i]);
1277 if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >=
1278 sizeof(portmsgbuf)) {
1284 snprintf(tmp, sizeof(tmp), ",%d", plen);
1286 if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= sizeof(portmsgbuf))
1290 for (i = 0; i < plen; i++) {
1291 snprintf(tmp, sizeof(tmp), ",%u", pp[i]);
1293 if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >=
1294 sizeof(portmsgbuf)) {
1299 result = Curl_ftpsendf(conn, "%s %s", mode[fcmd], portmsgbuf);
1304 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1308 if (ftpcode != 200) {
1310 infof(data, "disabling EPRT usage\n");
1311 conn->bits.ftp_use_eprt = FALSE;
1313 else if (LPRT == fcmd) {
1314 infof(data, "disabling LPRT usage\n");
1315 conn->bits.ftp_use_lprt = FALSE;
1325 failf(data, "PORT command attempts failed");
1326 return CURLE_FTP_PORT_FAILED;
1328 /* we set the secondary socket variable to this for now, it
1329 is only so that the cleanup function will close it in case
1330 we fail before the true secondary stuff is made */
1331 conn->sock[SECONDARYSOCKET] = portsock;
1334 /******************************************************************
1336 * Here's a piece of IPv4-specific code coming up
1339 struct sockaddr_in sa;
1340 unsigned short porttouse;
1341 char myhost[256] = "";
1342 bool sa_filled_in = FALSE;
1343 Curl_addrinfo *addr = NULL;
1344 unsigned short ip[4];
1346 if(data->set.ftpport) {
1349 /* First check if the given name is an IP address */
1350 in=inet_addr(data->set.ftpport);
1352 if(in != CURL_INADDR_NONE)
1353 /* this is an IPv4 address */
1354 addr = Curl_ip2addr(in, data->set.ftpport, 0);
1356 if(Curl_if2ip(data->set.ftpport, myhost, sizeof(myhost))) {
1357 /* The interface to IP conversion provided a dotted address */
1358 in=inet_addr(myhost);
1359 addr = Curl_ip2addr(in, myhost, 0);
1361 else if(strlen(data->set.ftpport)> 1) {
1362 /* might be a host name! */
1363 struct Curl_dns_entry *h=NULL;
1364 int rc = Curl_resolv(conn, myhost, 0, &h);
1365 if(rc == CURLRESOLV_PENDING)
1366 rc = Curl_wait_for_resolv(conn, &h);
1369 /* when we return from this function, we can forget about this entry
1370 to we can unlock it now already */
1371 Curl_resolv_unlock(data, h);
1374 } /* CURL_INADDR_NONE */
1375 } /* data->set.ftpport */
1378 /* pick a suitable default here */
1383 if (getsockname(conn->sock[FIRSTSOCKET],
1384 (struct sockaddr *)&sa, &sslen) < 0) {
1385 failf(data, "getsockname() failed");
1386 return CURLE_FTP_PORT_FAILED;
1389 sa_filled_in = TRUE; /* the sa struct is filled in */
1392 if (addr || sa_filled_in) {
1393 portsock = socket(AF_INET, SOCK_STREAM, 0);
1394 if(CURL_SOCKET_BAD != portsock) {
1397 /* we set the secondary socket variable to this for now, it
1398 is only so that the cleanup function will close it in case
1399 we fail before the true secondary stuff is made */
1400 conn->sock[SECONDARYSOCKET] = portsock;
1403 memcpy(&sa, addr->ai_addr, sizeof(sa));
1404 sa.sin_addr.s_addr = INADDR_ANY;
1410 if(bind(portsock, (struct sockaddr *)&sa, size) >= 0) {
1411 /* we succeeded to bind */
1412 struct sockaddr_in add;
1413 socklen_t socksize = sizeof(add);
1415 if(getsockname(portsock, (struct sockaddr *) &add,
1417 failf(data, "getsockname() failed");
1418 return CURLE_FTP_PORT_FAILED;
1420 porttouse = ntohs(add.sin_port);
1422 if ( listen(portsock, 1) < 0 ) {
1423 failf(data, "listen(2) failed on socket");
1424 return CURLE_FTP_PORT_FAILED;
1428 failf(data, "bind(2) failed on socket");
1429 return CURLE_FTP_PORT_FAILED;
1433 failf(data, "socket(2) failed (%s)");
1434 return CURLE_FTP_PORT_FAILED;
1438 failf(data, "could't find IP address to use");
1439 return CURLE_FTP_PORT_FAILED;
1443 Curl_inet_ntop(AF_INET, &((struct sockaddr_in *)&sa)->sin_addr,
1444 myhost, sizeof(myhost));
1446 Curl_printable_address(addr, myhost, sizeof(myhost));
1448 if(4 == sscanf(myhost, "%hu.%hu.%hu.%hu",
1449 &ip[0], &ip[1], &ip[2], &ip[3])) {
1451 infof(data, "Telling server to connect to %d.%d.%d.%d:%d\n",
1452 ip[0], ip[1], ip[2], ip[3], porttouse);
1454 result=Curl_ftpsendf(conn, "PORT %d,%d,%d,%d,%d,%d",
1455 ip[0], ip[1], ip[2], ip[3],
1463 return CURLE_FTP_PORT_FAILED;
1465 Curl_freeaddrinfo(addr);
1467 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1471 if(ftpcode != 200) {
1472 failf(data, "Server does not grok PORT, try without it!");
1473 return CURLE_FTP_PORT_FAILED;
1475 #endif /* end of ipv4-specific code */
1480 /***********************************************************************
1484 * Send the PASV command. PASV is the ftp client's way of asking the server to
1485 * open a second port that we can connect to (for the data transfer). This is
1486 * the opposite of PORT.
1490 CURLcode ftp_use_pasv(struct connectdata *conn,
1493 struct SessionHandle *data = conn->data;
1495 char *buf = data->state.buffer; /* this is our buffer */
1496 int ftpcode; /* receive FTP response codes in this */
1498 struct Curl_dns_entry *addr=NULL;
1499 Curl_addrinfo *conninfo;
1503 Here's the excecutive summary on what to do:
1505 PASV is RFC959, expect:
1506 227 Entering Passive Mode (a1,a2,a3,a4,p1,p2)
1508 LPSV is RFC1639, expect:
1509 228 Entering Long Passive Mode (4,4,a1,a2,a3,a4,2,p1,p2)
1511 EPSV is RFC2428, expect:
1512 229 Entering Extended Passive Mode (|||port|)
1516 const char *mode[] = { "EPSV", "PASV", NULL };
1517 int results[] = { 229, 227, 0 };
1519 unsigned short connectport; /* the local port connect() should use! */
1520 unsigned short newport=0; /* remote port, not necessary the local one */
1522 /* newhost must be able to hold a full IP-style address in ASCII, which
1523 in the IPv6 case means 5*8-1 = 39 letters */
1524 #define NEWHOST_BUFSIZE 48
1525 char newhost[NEWHOST_BUFSIZE];
1528 if(!conn->bits.ftp_use_epsv &&
1529 (conn->ip_addr->ai_family == PF_INET6)) {
1530 /* EPSV is disabled but we are connected to a IPv6 host, so we ignore the
1532 conn->bits.ftp_use_epsv = TRUE;
1536 for (modeoff = (conn->bits.ftp_use_epsv?0:1);
1537 mode[modeoff]; modeoff++) {
1538 result = Curl_ftpsendf(conn, "%s", mode[modeoff]);
1541 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1544 if (ftpcode == results[modeoff])
1548 /* EPSV is not supported, disable it for next transfer */
1549 conn->bits.ftp_use_epsv = FALSE;
1550 infof(data, "disabling EPSV usage\n");
1554 if (!mode[modeoff]) {
1555 failf(data, "Odd return code after PASV");
1556 return CURLE_FTP_WEIRD_PASV_REPLY;
1558 else if (227 == results[modeoff]) {
1564 * New 227-parser June 3rd 1999.
1565 * It now scans for a sequence of six comma-separated numbers and
1566 * will take them as IP+port indicators.
1568 * Found reply-strings include:
1569 * "227 Entering Passive Mode (127,0,0,1,4,51)"
1570 * "227 Data transfer will passively listen to 127,0,0,1,4,51"
1571 * "227 Entering passive mode. 127,0,0,1,4,51"
1575 if (6 == sscanf(str, "%d,%d,%d,%d,%d,%d",
1576 &ip[0], &ip[1], &ip[2], &ip[3],
1577 &port[0], &port[1]))
1583 failf(data, "Couldn't interpret this 227-reply: %s", buf);
1584 return CURLE_FTP_WEIRD_227_FORMAT;
1587 snprintf(newhost, sizeof(newhost),
1588 "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
1589 newport = (port[0]<<8) + port[1];
1591 else if (229 == results[modeoff]) {
1592 char *ptr = strchr(buf, '(');
1597 if(5 == sscanf(ptr, "%c%c%c%u%c",
1603 char sep1 = separator[0];
1606 /* The four separators should be identical, or else this is an oddly
1607 formatted reply and we bail out immediately. */
1608 for(i=1; i<4; i++) {
1609 if(separator[i] != sep1) {
1610 ptr=NULL; /* set to NULL to signal error */
1617 /* We must use the same IP we are already connected to */
1618 Curl_printable_address(conn->ip_addr, newhost, NEWHOST_BUFSIZE);
1625 failf(data, "Weirdly formatted EPSV reply");
1626 return CURLE_FTP_WEIRD_PASV_REPLY;
1630 return CURLE_FTP_CANT_RECONNECT;
1632 if(data->change.proxy && *data->change.proxy) {
1634 * This is a tunnel through a http proxy and we need to connect to the
1637 * We don't want to rely on a former host lookup that might've expired
1638 * now, instead we remake the lookup here and now!
1640 rc = Curl_resolv(conn, conn->proxy.name, (int)conn->port, &addr);
1641 if(rc == CURLRESOLV_PENDING)
1642 rc = Curl_wait_for_resolv(conn, &addr);
1645 (unsigned short)conn->port; /* we connect to the proxy's port */
1649 /* normal, direct, ftp connection */
1650 rc = Curl_resolv(conn, newhost, newport, &addr);
1651 if(rc == CURLRESOLV_PENDING)
1652 rc = Curl_wait_for_resolv(conn, &addr);
1655 failf(data, "Can't resolve new host %s:%d", newhost, newport);
1656 return CURLE_FTP_CANT_GET_HOST;
1658 connectport = newport; /* we connect to the remote port */
1661 result = Curl_connecthost(conn,
1663 &conn->sock[SECONDARYSOCKET],
1667 Curl_resolv_unlock(data, addr); /* we're done using this address */
1673 * When this is used from the multi interface, this might've returned with
1674 * the 'connected' set to FALSE and thus we are now awaiting a non-blocking
1675 * connect to connect and we should not be "hanging" here waiting.
1678 if(data->set.verbose)
1679 /* this just dumps information about this second connection */
1680 ftp_pasv_verbose(conn, conninfo, newhost, connectport);
1682 #ifndef CURL_DISABLE_HTTP
1683 if(conn->bits.tunnel_proxy) {
1684 /* We want "seamless" FTP operations through HTTP proxy tunnel */
1685 result = Curl_ConnectHTTPProxyTunnel(conn, SECONDARYSOCKET,
1687 if(CURLE_OK != result)
1690 #endif /* CURL_DISABLE_HTTP */
1696 * Curl_ftp_nextconnect()
1698 * This function shall be called when the second FTP connection has been
1699 * established and is confirmed connected.
1702 CURLcode Curl_ftp_nextconnect(struct connectdata *conn)
1704 struct SessionHandle *data=conn->data;
1705 char *buf = data->state.buffer; /* this is our buffer */
1708 int ftpcode; /* for ftp status */
1710 /* the ftp struct is already inited in Curl_ftp_connect() */
1711 struct FTP *ftp = conn->proto.ftp;
1712 curl_off_t *bytecountp = ftp->bytecountp;
1714 if(data->set.upload) {
1716 /* Set type to binary (unless specified ASCII) */
1717 result = ftp_transfertype(conn, data->set.ftp_ascii);
1721 /* Send any PREQUOTE strings after transfer type is set? (Wesley Laxton)*/
1722 if(data->set.prequote) {
1723 if ((result = ftp_sendquote(conn, data->set.prequote)) != CURLE_OK)
1727 if(conn->resume_from) {
1728 /* we're about to continue the uploading of a file */
1729 /* 1. get already existing file's size. We use the SIZE
1730 command for this which may not exist in the server!
1731 The SIZE command is not in RFC959. */
1733 /* 2. This used to set REST. But since we can do append, we
1734 don't another ftp command. We just skip the source file
1735 offset and then we APPEND the rest on the file instead */
1737 /* 3. pass file-size number of bytes in the source file */
1738 /* 4. lower the infilesize counter */
1739 /* => transfer as usual */
1741 if(conn->resume_from < 0 ) {
1742 /* we could've got a specified offset from the command line,
1743 but now we know we didn't */
1744 curl_off_t gottensize;
1746 if(CURLE_OK != ftp_getsize(conn, ftp->file, &gottensize)) {
1747 failf(data, "Couldn't get remote file size");
1748 return CURLE_FTP_COULDNT_GET_SIZE;
1750 conn->resume_from = gottensize;
1753 if(conn->resume_from) {
1754 /* do we still game? */
1755 curl_off_t passed=0;
1756 /* enable append instead */
1757 data->set.ftp_append = 1;
1759 /* Now, let's read off the proper amount of bytes from the
1760 input. If we knew it was a proper file we could've just
1761 fseek()ed but we only have a stream here */
1763 curl_off_t readthisamountnow = (conn->resume_from - passed);
1764 curl_off_t actuallyread;
1766 if(readthisamountnow > BUFSIZE)
1767 readthisamountnow = BUFSIZE;
1769 actuallyread = (curl_off_t)
1770 conn->fread(data->state.buffer, 1, (size_t)readthisamountnow,
1773 passed += actuallyread;
1774 if(actuallyread != readthisamountnow) {
1775 failf(data, "Could only read %" FORMAT_OFF_T
1776 " bytes from the input", passed);
1777 return CURLE_FTP_COULDNT_USE_REST;
1780 while(passed != conn->resume_from);
1782 /* now, decrease the size of the read */
1783 if(data->set.infilesize>0) {
1784 data->set.infilesize -= conn->resume_from;
1786 if(data->set.infilesize <= 0) {
1787 infof(data, "File already completely uploaded\n");
1789 /* no data to transfer */
1790 result=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1792 /* Set no_transfer so that we won't get any error in
1793 * Curl_ftp_done() because we didn't transfer anything! */
1794 ftp->no_transfer = TRUE;
1799 /* we've passed, proceed as normal */
1803 /* Send everything on data->state.in to the socket */
1804 if(data->set.ftp_append) {
1805 /* we append onto the file instead of rewriting it */
1806 FTPSENDF(conn, "APPE %s", ftp->file);
1809 FTPSENDF(conn, "STOR %s", ftp->file);
1812 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1817 failf(data, "Failed FTP upload:%s", buf+3);
1818 /* oops, we never close the sockets! */
1819 return CURLE_FTP_COULDNT_STOR_FILE;
1822 if(data->set.ftp_use_port) {
1823 /* PORT means we are now awaiting the server to connect to us. */
1824 result = AllowServerConnect(conn);
1829 if(conn->ssl[SECONDARYSOCKET].use) {
1830 /* since we only have a plaintext TCP connection here, we must now
1832 infof(data, "Doing the SSL/TLS handshake on the data stream\n");
1833 result = Curl_SSLConnect(conn, SECONDARYSOCKET);
1840 /* When we know we're uploading a specified file, we can get the file
1841 size prior to the actual upload. */
1843 Curl_pgrsSetUploadSize(data, data->set.infilesize);
1845 result = Curl_Transfer(conn, -1, -1, FALSE, NULL, /* no download */
1846 SECONDARYSOCKET, bytecountp);
1851 else if(!conn->bits.no_body) {
1852 /* Retrieve file or directory */
1854 curl_off_t downloadsize=-1;
1856 if(conn->bits.use_range && conn->range) {
1857 curl_off_t from, to;
1858 curl_off_t totalsize=-1;
1862 from=curlx_strtoofft(conn->range, &ptr, 0);
1863 while(ptr && *ptr && (isspace((int)*ptr) || (*ptr=='-')))
1865 to=curlx_strtoofft(ptr, &ptr2, 0);
1867 /* we didn't get any digit */
1870 if((-1 == to) && (from>=0)) {
1872 conn->resume_from = from;
1873 infof(data, "FTP RANGE %" FORMAT_OFF_T " to end of file\n", from);
1878 conn->maxdownload = -from;
1879 conn->resume_from = from;
1880 infof(data, "FTP RANGE the last %" FORMAT_OFF_T " bytes\n", totalsize);
1884 totalsize = to-from;
1885 conn->maxdownload = totalsize+1; /* include the last mentioned byte */
1886 conn->resume_from = from;
1887 infof(data, "FTP RANGE from %" FORMAT_OFF_T
1888 " getting %" FORMAT_OFF_T " bytes\n", from, conn->maxdownload);
1890 infof(data, "range-download from %" FORMAT_OFF_T
1891 " to %" FORMAT_OFF_T ", totally %" FORMAT_OFF_T " bytes\n",
1892 from, to, conn->maxdownload);
1893 ftp->dont_check = TRUE; /* dont check for successful transfer */
1896 if((data->set.ftp_list_only) || !ftp->file) {
1897 /* The specified path ends with a slash, and therefore we think this
1898 is a directory that is requested, use LIST. But before that we
1899 need to set ASCII transfer mode. */
1902 /* Set type to ASCII */
1903 result = ftp_transfertype(conn, TRUE /* ASCII enforced */);
1907 /* if this output is to be machine-parsed, the NLST command will be
1908 better used since the LIST command output is not specified or
1909 standard in any way */
1911 FTPSENDF(conn, "%s",
1912 data->set.customrequest?data->set.customrequest:
1913 (data->set.ftp_list_only?"NLST":"LIST"));
1916 curl_off_t foundsize;
1918 /* Set type to binary (unless specified ASCII) */
1919 result = ftp_transfertype(conn, data->set.ftp_ascii);
1923 /* Send any PREQUOTE strings after transfer type is set? */
1924 if(data->set.prequote) {
1925 if ((result = ftp_sendquote(conn, data->set.prequote)) != CURLE_OK)
1929 /* Attempt to get the size, it'll be useful in some cases: for resumed
1930 downloads and when talking to servers that don't give away the size
1931 in the RETR response line. */
1932 result = ftp_getsize(conn, ftp->file, &foundsize);
1933 if(CURLE_OK == result) {
1934 if (data->set.max_filesize && foundsize > data->set.max_filesize) {
1935 failf(data, "Maximum file size exceeded");
1936 return CURLE_FILESIZE_EXCEEDED;
1938 downloadsize = foundsize;
1941 if(conn->resume_from) {
1943 /* Daniel: (August 4, 1999)
1945 * We start with trying to use the SIZE command to figure out the size
1946 * of the file we're gonna get. If we can get the size, this is by far
1947 * the best way to know if we're trying to resume beyond the EOF.
1949 * Daniel, November 28, 2001. We *always* get the size on downloads
1950 * now, so it is done before this even when not doing resumes. I saved
1951 * the comment above for nostalgical reasons! ;-)
1953 if(CURLE_OK != result) {
1954 infof(data, "ftp server doesn't support SIZE\n");
1955 /* We couldn't get the size and therefore we can't know if there
1956 really is a part of the file left to get, although the server
1957 will just close the connection when we start the connection so it
1958 won't cause us any harm, just not make us exit as nicely. */
1961 /* We got a file size report, so we check that there actually is a
1962 part of the file left to get, or else we go home. */
1963 if(conn->resume_from< 0) {
1964 /* We're supposed to download the last abs(from) bytes */
1965 if(foundsize < -conn->resume_from) {
1966 failf(data, "Offset (%" FORMAT_OFF_T
1967 ") was beyond file size (%" FORMAT_OFF_T ")",
1968 conn->resume_from, foundsize);
1969 return CURLE_FTP_BAD_DOWNLOAD_RESUME;
1971 /* convert to size to download */
1972 downloadsize = -conn->resume_from;
1973 /* download from where? */
1974 conn->resume_from = foundsize - downloadsize;
1977 if(foundsize < conn->resume_from) {
1978 failf(data, "Offset (%" FORMAT_OFF_T
1979 ") was beyond file size (%" FORMAT_OFF_T ")",
1980 conn->resume_from, foundsize);
1981 return CURLE_FTP_BAD_DOWNLOAD_RESUME;
1983 /* Now store the number of bytes we are expected to download */
1984 downloadsize = foundsize-conn->resume_from;
1988 if (downloadsize == 0) {
1989 /* no data to transfer */
1990 result=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1991 infof(data, "File already completely downloaded\n");
1993 /* Set no_transfer so that we won't get any error in Curl_ftp_done()
1994 * because we didn't transfer the any file */
1995 ftp->no_transfer = TRUE;
1999 /* Set resume file transfer offset */
2000 infof(data, "Instructs server to resume from offset %" FORMAT_OFF_T
2004 FTPSENDF(conn, "REST %" FORMAT_OFF_T, conn->resume_from);
2006 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2010 if(ftpcode != 350) {
2011 failf(data, "Couldn't use REST: %s", buf+4);
2012 return CURLE_FTP_COULDNT_USE_REST;
2016 FTPSENDF(conn, "RETR %s", ftp->file);
2019 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2023 if((ftpcode == 150) || (ftpcode == 125)) {
2027 150 Opening BINARY mode data connection for /etc/passwd (2241
2028 bytes). (ok, the file is being transfered)
2031 150 Opening ASCII mode data connection for /bin/ls
2034 150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes).
2037 150 Opening ASCII mode data connection for /linux/fisk/kpanelrc (0.0.0.0,0) (545 bytes).
2040 125 Data connection already open; Transfer starting. */
2042 curl_off_t size=-1; /* default unknown size */
2046 * It appears that there are FTP-servers that return size 0 for files
2047 * when SIZE is used on the file while being in BINARY mode. To work
2048 * around that (stupid) behavior, we attempt to parse the RETR response
2049 * even if the SIZE returned size zero.
2051 * Debugging help from Salvatore Sorrentino on February 26, 2003.
2055 !data->set.ftp_ascii &&
2056 (downloadsize < 1)) {
2058 * It seems directory listings either don't show the size or very
2059 * often uses size 0 anyway. ASCII transfers may very well turn out
2060 * that the transfered amount of data is not the same as this line
2061 * tells, why using this number in those cases only confuses us.
2063 * Example D above makes this parsing a little tricky */
2065 bytes=strstr(buf, " bytes");
2068 /* this is a hint there is size information in there! ;-) */
2070 /* scan for the parenthesis and break there */
2073 /* if only skip digits, or else we're in deep trouble */
2074 if(!isdigit((int)*bytes)) {
2078 /* one more estep backwards */
2081 /* only if we have nothing but digits: */
2083 /* get the number! */
2084 size = curlx_strtoofft(bytes, NULL, 0);
2089 else if(downloadsize > -1)
2090 size = downloadsize;
2092 if(data->set.ftp_use_port) {
2093 result = AllowServerConnect(conn);
2098 if(conn->ssl[SECONDARYSOCKET].use) {
2099 /* since we only have a plaintext TCP connection here, we must now
2101 infof(data, "Doing the SSL/TLS handshake on the data stream\n");
2102 result = Curl_SSLConnect(conn, SECONDARYSOCKET);
2107 if(size > conn->maxdownload && conn->maxdownload > 0)
2108 size = conn->size = conn->maxdownload;
2110 infof(data, "Getting file with size: %" FORMAT_OFF_T "\n", size);
2113 result=Curl_Transfer(conn, SECONDARYSOCKET, size, FALSE,
2115 -1, NULL); /* no upload here */
2120 if(dirlist && (ftpcode == 450)) {
2121 /* simply no matching files */
2122 ftp->no_transfer = TRUE; /* don't think we should download anything */
2125 failf(data, "%s", buf+4);
2126 return CURLE_FTP_COULDNT_RETR_FILE;
2131 /* end of transfer */
2136 /***********************************************************************
2140 * This is the actual DO function for FTP. Get a file/directory according to
2141 * the options previously setup.
2145 CURLcode ftp_perform(struct connectdata *conn,
2146 bool *connected) /* for the TCP connect status after
2149 /* this is FTP and no proxy */
2150 CURLcode result=CURLE_OK;
2151 struct SessionHandle *data=conn->data;
2152 char *buf = data->state.buffer; /* this is our buffer */
2154 /* the ftp struct is already inited in Curl_ftp_connect() */
2155 struct FTP *ftp = conn->proto.ftp;
2157 /* Send any QUOTE strings? */
2158 if(data->set.quote) {
2159 if ((result = ftp_sendquote(conn, data->set.quote)) != CURLE_OK)
2163 result = ftp_cwd_and_create_path(conn);
2167 /* Requested time of file or time-depended transfer? */
2168 if((data->set.get_filetime || data->set.timecondition) &&
2170 result = ftp_getfiletime(conn, ftp->file);
2173 case CURLE_FTP_COULDNT_RETR_FILE:
2175 if(data->set.timecondition) {
2176 if((data->info.filetime > 0) && (data->set.timevalue > 0)) {
2177 switch(data->set.timecondition) {
2178 case CURL_TIMECOND_IFMODSINCE:
2180 if(data->info.filetime < data->set.timevalue) {
2181 infof(data, "The requested document is not new enough\n");
2182 ftp->no_transfer = TRUE; /* mark this to not transfer data */
2186 case CURL_TIMECOND_IFUNMODSINCE:
2187 if(data->info.filetime > data->set.timevalue) {
2188 infof(data, "The requested document is not old enough\n");
2189 ftp->no_transfer = TRUE; /* mark this to not transfer data */
2196 infof(data, "Skipping time comparison\n");
2205 /* If we have selected NOBODY and HEADER, it means that we only want file
2206 information. Which in FTP can't be much more than the file size and
2208 if(conn->bits.no_body && data->set.include_header && ftp->file) {
2209 /* The SIZE command is _not_ RFC 959 specified, and therefor many servers
2210 may not support it! It is however the only way we have to get a file's
2212 curl_off_t filesize;
2216 ftp->no_transfer = TRUE; /* this means no actual transfer is made */
2218 /* Some servers return different sizes for different modes, and thus we
2219 must set the proper type before we check the size */
2220 result = ftp_transfertype(conn, data->set.ftp_ascii);
2224 /* failing to get size is not a serious error */
2225 result = ftp_getsize(conn, ftp->file, &filesize);
2227 if(CURLE_OK == result) {
2228 snprintf(buf, sizeof(data->state.buffer),
2229 "Content-Length: %" FORMAT_OFF_T "\r\n", filesize);
2230 result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
2235 /* Determine if server can respond to REST command and therefore
2236 whether it can do a range */
2237 FTPSENDF(conn, "REST 0", NULL);
2238 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2240 if ((CURLE_OK == result) && (ftpcode == 350)) {
2241 result = Curl_client_write(data, CLIENTWRITE_BOTH,
2242 (char *)"Accept-ranges: bytes\r\n", 0);
2247 /* If we asked for a time of the file and we actually got one as
2248 well, we "emulate" a HTTP-style header in our output. */
2250 #ifdef HAVE_STRFTIME
2251 if(data->set.get_filetime && (data->info.filetime>=0) ) {
2253 time_t clock = (time_t)data->info.filetime;
2254 #ifdef HAVE_GMTIME_R
2256 tm = (struct tm *)gmtime_r(&clock, &buffer);
2258 tm = gmtime(&clock);
2260 /* format: "Tue, 15 Nov 1994 12:45:26" */
2261 strftime(buf, BUFSIZE-1, "Last-Modified: %a, %d %b %Y %H:%M:%S GMT\r\n",
2263 result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
2272 if(conn->bits.no_body)
2273 /* doesn't really transfer any data */
2274 ftp->no_transfer = TRUE;
2275 /* Get us a second connection up and connected */
2276 else if(data->set.ftp_use_port) {
2277 /* We have chosen to use the PORT command */
2278 result = ftp_use_port(conn);
2279 if(CURLE_OK == result) {
2280 /* we have the data connection ready */
2281 infof(data, "Ordered connect of the data stream with PORT!\n");
2282 *connected = TRUE; /* mark us "still connected" */
2286 /* We have chosen (this is default) to use the PASV command */
2287 result = ftp_use_pasv(conn, connected);
2288 if(CURLE_OK == result && *connected)
2289 infof(data, "Connected the data stream with PASV!\n");
2295 /***********************************************************************
2299 * This function is registered as 'curl_do' function. It decodes the path
2300 * parts etc as a wrapper to the actual DO function (ftp_perform).
2302 * The input argument is already checked for validity.
2304 CURLcode Curl_ftp(struct connectdata *conn)
2306 CURLcode retcode = CURLE_OK;
2308 retcode = ftp_parse_url_path(conn);
2312 if (conn->sec_conn) /* 3rd party transfer */
2313 retcode = ftp_3rdparty(conn);
2315 retcode = ftp_regular_transfer(conn);
2320 /***********************************************************************
2324 * Sends the formated string as a ftp command to a ftp server
2326 * NOTE: we build the command in a fixed-length buffer, which sets length
2327 * restrictions on the command!
2329 CURLcode Curl_ftpsendf(struct connectdata *conn,
2330 const char *fmt, ...)
2332 ssize_t bytes_written;
2336 CURLcode res = CURLE_OK;
2340 vsnprintf(s, 250, fmt, ap);
2343 strcat(s, "\r\n"); /* append a trailing CRLF */
2346 write_len = strlen(s);
2349 res = Curl_write(conn, conn->sock[FIRSTSOCKET], sptr, write_len,
2355 if(conn->data->set.verbose)
2356 Curl_debug(conn->data, CURLINFO_HEADER_OUT, sptr, bytes_written, conn->host.dispname);
2358 if(bytes_written != (ssize_t)write_len) {
2359 write_len -= bytes_written;
2360 sptr += bytes_written;
2369 /***********************************************************************
2373 * This should be called before calling sclose() on an ftp control connection
2374 * (not data connections). We should then wait for the response from the
2375 * server before returning. The calling code should then try to close the
2379 static CURLcode ftp_quit(struct connectdata *conn)
2383 CURLcode ret = CURLE_OK;
2385 if(conn->proto.ftp->ctl_valid) {
2386 ret = Curl_ftpsendf(conn, "%s", "QUIT");
2388 ret = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2394 /***********************************************************************
2396 * Curl_ftp_disconnect()
2398 * Disconnect from an FTP server. Cleanup protocol-specific per-connection
2401 CURLcode Curl_ftp_disconnect(struct connectdata *conn)
2403 struct FTP *ftp= conn->proto.ftp;
2405 /* We cannot send quit unconditionally. If this connection is stale or
2406 bad in any way, sending quit and waiting around here will make the
2407 disconnect wait in vain and cause more problems than we need to.
2409 ftp_quit() will check the state of ftp->ctl_valid. If it's ok it
2410 will try to send the QUIT command, otherwise it will just return.
2413 /* The FTP session may or may not have been allocated/setup at this point! */
2415 (void)ftp_quit(conn); /* ignore errors on the QUIT */
2418 free(ftp->entrypath);
2425 free(ftp->prevpath);
2430 /***********************************************************************
2434 * Makes a directory on the FTP server.
2438 static CURLcode ftp_mkd(struct connectdata *conn, char *path)
2440 CURLcode result=CURLE_OK;
2441 int ftpcode; /* for ftp status */
2444 /* Create a directory on the remote server */
2445 FTPSENDF(conn, "MKD %s", path);
2447 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2454 infof( conn->data , "Created remote directory %s\n" , path );
2457 failf(conn->data, "Permission denied to make directory %s", path);
2458 result = CURLE_FTP_ACCESS_DENIED;
2461 failf(conn->data, "unrecognized MKD response: %d", ftpcode );
2462 result = CURLE_FTP_ACCESS_DENIED;
2468 /***********************************************************************
2472 * Send 'CWD' to the remote server to Change Working Directory. It is the ftp
2473 * version of the unix 'cd' command. This function is only called from the
2474 * ftp_cwd_and_mkd() function these days.
2476 * This function does NOT call failf().
2479 CURLcode ftp_cwd(struct connectdata *conn, char *path)
2485 FTPSENDF(conn, "CWD %s", path);
2486 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2488 /* According to RFC959, CWD is supposed to return 250 on success, but
2489 there seem to be non-compliant FTP servers out there that return 200,
2490 so we accept any '2xy' code here. */
2491 if (ftpcode/100 != 2)
2492 result = CURLE_FTP_ACCESS_DENIED;
2498 /***********************************************************************
2502 * Change to the given directory. If the directory is not present, and we
2503 * have been told to allow it, then create the directory and cd to it.
2506 static CURLcode ftp_cwd_and_mkd(struct connectdata *conn, char *path)
2510 result = ftp_cwd(conn, path);
2512 if(conn->data->set.ftp_create_missing_dirs) {
2513 result = ftp_mkd(conn, path);
2515 /* ftp_mkd() calls failf() itself */
2517 result = ftp_cwd(conn, path);
2520 failf(conn->data, "Couldn't CWD to %s", path);
2527 /***********************************************************************
2529 * ftp_3rdparty_pretransfer()
2531 * Preparation for 3rd party transfer.
2534 static CURLcode ftp_3rdparty_pretransfer(struct connectdata *conn)
2536 CURLcode result = CURLE_OK;
2537 struct SessionHandle *data = conn->data;
2538 struct connectdata *sec_conn = conn->sec_conn;
2540 /* sets transfer type */
2541 result = ftp_transfertype(conn, data->set.ftp_ascii);
2545 result = ftp_transfertype(sec_conn, data->set.ftp_ascii);
2549 /* Send any PREQUOTE strings after transfer type is set? */
2550 if (data->set.source_prequote) {
2551 /* sends command(s) to source server before file transfer */
2552 result = ftp_sendquote(sec_conn, data->set.source_prequote);
2554 if (!result && data->set.prequote)
2555 result = ftp_sendquote(conn, data->set.prequote);
2562 /***********************************************************************
2564 * ftp_3rdparty_transfer()
2566 * Performs 3rd party transfer.
2569 static CURLcode ftp_3rdparty_transfer(struct connectdata *conn)
2571 CURLcode result = CURLE_OK;
2573 int ftpcode, ip[4], port[2];
2574 struct SessionHandle *data = conn->data;
2575 struct connectdata *sec_conn = conn->sec_conn;
2576 char *buf = data->state.buffer; /* this is our buffer */
2579 const char *stor_cmd;
2580 struct connectdata *pasv_conn;
2581 struct connectdata *port_conn;
2583 if (data->set.pasvHost == CURL_TARGET_PASV) {
2585 port_conn = sec_conn;
2588 pasv_conn = sec_conn;
2592 result = ftp_cwd_and_create_path(conn);
2596 /* sets the passive mode */
2597 FTPSENDF(pasv_conn, "%s", "PASV");
2598 result = Curl_GetFTPResponse(&nread, pasv_conn, &ftpcode);
2599 if (result) return result;
2600 if (ftpcode != 227) {
2601 failf(data, "Odd return code after PASV:%s", buf + 3);
2602 return CURLE_FTP_WEIRD_PASV_REPLY;
2606 if (6 == sscanf(str, "%d,%d,%d,%d,%d,%d",
2607 &ip[0], &ip[1], &ip[2], &ip[3], &port[0], &port[1]))
2613 failf(pasv_conn->data, "Couldn't interpret this 227-reply: %s", buf);
2614 return CURLE_FTP_WEIRD_227_FORMAT;
2617 snprintf(pasv_port, sizeof(pasv_port), "%d,%d,%d,%d,%d,%d", ip[0], ip[1],
2618 ip[2], ip[3], port[0], port[1]);
2620 /* sets data connection between remote hosts */
2621 FTPSENDF(port_conn, "PORT %s", pasv_port);
2622 result = Curl_GetFTPResponse(&nread, port_conn, &ftpcode);
2626 if (ftpcode != 200) {
2627 failf(data, "PORT command attempts failed:%s", buf + 3);
2628 return CURLE_FTP_PORT_FAILED;
2631 /* we might append onto the file instead of overwriting it */
2632 stor_cmd = data->set.ftp_append?"APPE":"STOR";
2634 /* transfers file between remote hosts */
2635 FTPSENDF(sec_conn, "RETR %s", data->set.source_path);
2637 if(data->set.pasvHost == CURL_TARGET_PASV) {
2639 result = Curl_GetFTPResponse(&nread, sec_conn, &ftpcode);
2643 if (ftpcode != 150) {
2644 failf(data, "Failed RETR: %s", buf + 4);
2645 return CURLE_FTP_COULDNT_RETR_FILE;
2648 result = Curl_ftpsendf(conn, "%s %s", stor_cmd, conn->path);
2649 if(CURLE_OK == result)
2650 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2654 if (ftpcode != 150) {
2655 failf(data, "Failed FTP upload: %s", buf + 4);
2656 return CURLE_FTP_COULDNT_STOR_FILE;
2662 result = Curl_ftpsendf(conn, "%s %s", stor_cmd, conn->path);
2663 if(CURLE_OK == result)
2664 result = Curl_GetFTPResponse(&nread, sec_conn, &ftpcode);
2668 if (ftpcode != 150) {
2669 failf(data, "Failed FTP upload: %s", buf + 4);
2670 return CURLE_FTP_COULDNT_STOR_FILE;
2673 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2677 if (ftpcode != 150) {
2678 failf(data, "Failed FTP upload: %s", buf + 4);
2679 return CURLE_FTP_COULDNT_STOR_FILE;
2688 /***********************************************************************
2690 * ftp_parse_url_path()
2692 * Parse the URL path into separate path components.
2696 CURLcode ftp_parse_url_path(struct connectdata *conn)
2698 CURLcode retcode = CURLE_OK;
2699 struct SessionHandle *data = conn->data;
2702 char *slash_pos; /* position of the first '/' char in curpos */
2703 char *cur_pos = conn->path; /* current position in path. point at the begin
2704 of next path component */
2706 /* the ftp struct is already inited in ftp_connect() */
2707 ftp = conn->proto.ftp;
2708 ftp->ctl_valid = FALSE;
2711 ftp->diralloc = 5; /* default dir depth to allocate */
2712 ftp->dirs = (char **)malloc(ftp->diralloc * sizeof(ftp->dirs[0]));
2714 return CURLE_OUT_OF_MEMORY;
2715 ftp->dirs[0] = NULL; /* to start with */
2717 /* parse the URL path into separate path components */
2718 while((slash_pos=strchr(cur_pos, '/'))) {
2719 /* 1 or 0 to indicate absolute directory */
2720 bool absolute_dir = (cur_pos - conn->path > 0) && (ftp->dirdepth == 0);
2722 /* seek out the next path component */
2723 if (slash_pos-cur_pos) {
2724 /* we skip empty path components, like "x//y" since the FTP command CWD
2725 requires a parameter and a non-existant parameter a) doesn't work on
2726 many servers and b) has no effect on the others. */
2727 int len = (int)(slash_pos - cur_pos + absolute_dir);
2728 ftp->dirs[ftp->dirdepth] = curl_unescape(cur_pos - absolute_dir, len);
2730 if (!ftp->dirs[ftp->dirdepth]) { /* run out of memory ... */
2731 failf(data, "no memory");
2733 return CURLE_OUT_OF_MEMORY;
2737 cur_pos = slash_pos + 1; /* jump to the rest of the string */
2742 cur_pos = slash_pos + 1; /* jump to the rest of the string */
2743 if(++ftp->dirdepth >= ftp->diralloc) {
2746 ftp->diralloc *= 2; /* double the size each time */
2747 bigger = realloc(ftp->dirs, ftp->diralloc * sizeof(ftp->dirs[0]));
2750 return CURLE_OUT_OF_MEMORY;
2752 ftp->dirs = (char **)bigger;
2757 ftp->file = cur_pos; /* the rest is the file name */
2760 ftp->file = curl_unescape(ftp->file, 0);
2761 if(NULL == ftp->file) {
2763 failf(data, "no memory");
2764 return CURLE_OUT_OF_MEMORY;
2768 ftp->file=NULL; /* instead of point to a zero byte, we make it a NULL
2771 ftp->cwddone = FALSE; /* default to not done */
2773 size_t dlen = conn->path?strlen(conn->path):0;
2774 if(dlen && ftp->prevpath) {
2775 dlen -= ftp->file?strlen(ftp->file):0;
2776 if((dlen == strlen(ftp->prevpath)) &&
2777 curl_strnequal(conn->path, ftp->prevpath, dlen)) {
2778 infof(data, "Request has same path as previous transfer\n");
2779 ftp->cwddone = TRUE;
2789 /***********************************************************************
2791 * ftp_cwd_and_create_path()
2793 * Creates full path on remote target host.
2797 CURLcode ftp_cwd_and_create_path(struct connectdata *conn)
2799 CURLcode result = CURLE_OK;
2800 /* the ftp struct is already inited in Curl_ftp_connect() */
2801 struct FTP *ftp = conn->proto.ftp;
2805 /* already done and fine */
2808 /* This is a re-used connection. Since we change directory to where the
2809 transfer is taking place, we must now get back to the original dir
2810 where we ended up after login: */
2811 if (conn->bits.reuse && ftp->entrypath) {
2812 if ((result = ftp_cwd_and_mkd(conn, ftp->entrypath)) != CURLE_OK)
2816 for (i=0; i < ftp->dirdepth; i++) {
2817 /* RFC 1738 says empty components should be respected too, but
2818 that is plain stupid since CWD can't be used with an empty argument */
2819 if ((result = ftp_cwd_and_mkd(conn, ftp->dirs[i])) != CURLE_OK)
2827 /***********************************************************************
2829 * ftp_regular_transfer()
2831 * The input argument is already checked for validity.
2832 * Performs a regular transfer between local and remote hosts.
2834 * ftp->ctl_valid starts out as FALSE, and gets set to TRUE if we reach the
2835 * Curl_ftp_done() function without finding any major problem.
2838 CURLcode ftp_regular_transfer(struct connectdata *conn)
2840 CURLcode retcode=CURLE_OK;
2842 struct SessionHandle *data = conn->data;
2845 /* the ftp struct is already inited in ftp_connect() */
2846 ftp = conn->proto.ftp;
2847 conn->size = -1; /* make sure this is unknown at this point */
2849 Curl_pgrsSetUploadCounter(data, 0);
2850 Curl_pgrsSetDownloadCounter(data, 0);
2851 Curl_pgrsSetUploadSize(data, 0);
2852 Curl_pgrsSetDownloadSize(data, 0);
2854 retcode = ftp_perform(conn, &connected);
2856 if(CURLE_OK == retcode) {
2858 retcode = Curl_ftp_nextconnect(conn);
2860 if(retcode && (conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD)) {
2861 /* Failure detected, close the second socket if it was created already */
2862 sclose(conn->sock[SECONDARYSOCKET]);
2863 conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
2866 if(ftp->no_transfer)
2867 /* no data to transfer */
2868 retcode=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
2870 /* since we didn't connect now, we want do_more to get called */
2871 conn->bits.do_more = TRUE;
2876 ftp->ctl_valid = TRUE; /* seems good */
2883 /***********************************************************************
2887 * The input argument is already checked for validity.
2888 * Performs a 3rd party transfer between two remote hosts.
2890 static CURLcode ftp_3rdparty(struct connectdata *conn)
2892 CURLcode retcode = CURLE_OK;
2894 conn->proto.ftp->ctl_valid = conn->sec_conn->proto.ftp->ctl_valid = TRUE;
2895 conn->size = conn->sec_conn->size = -1;
2897 retcode = ftp_3rdparty_pretransfer(conn);
2899 retcode = ftp_3rdparty_transfer(conn);
2904 #endif /* CURL_DISABLE_FTP */