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;
446 * Curl_ftp_connect() should do everything that is to be considered a part of
447 * the connection phase.
449 CURLcode Curl_ftp_connect(struct connectdata *conn)
451 /* this is FTP and no proxy */
453 struct SessionHandle *data=conn->data;
454 char *buf = data->state.buffer; /* this is our buffer */
458 static const char * const ftpauth[] = {
462 ftp = (struct FTP *)malloc(sizeof(struct FTP));
464 return CURLE_OUT_OF_MEMORY;
466 memset(ftp, 0, sizeof(struct FTP));
467 conn->proto.ftp = ftp;
469 /* We always support persistant connections on ftp */
470 conn->bits.close = FALSE;
472 /* get some initial data into the ftp struct */
473 ftp->bytecountp = &conn->bytecount;
475 /* no need to duplicate them, this connectdata struct won't change */
476 ftp->user = conn->user;
477 ftp->passwd = conn->passwd;
478 ftp->response_time = 3600; /* set default response time-out */
480 #ifndef CURL_DISABLE_HTTP
481 if (conn->bits.tunnel_proxy) {
482 /* We want "seamless" FTP operations through HTTP proxy tunnel */
483 result = Curl_ConnectHTTPProxyTunnel(conn, FIRSTSOCKET,
484 conn->host.name, conn->remote_port);
485 if(CURLE_OK != result)
488 #endif /* CURL_DISABLE_HTTP */
490 if(conn->protocol & PROT_FTPS) {
491 /* FTPS is simply ftp with SSL for the control channel */
492 /* now, perform the SSL initialization for this socket */
493 result = Curl_SSLConnect(conn, FIRSTSOCKET);
498 /* The first thing we do is wait for the "220*" line: */
499 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
504 failf(data, "This doesn't seem like a nice ftp-server response");
505 return CURLE_FTP_WEIRD_SERVER_REPLY;
509 /* if not anonymous login, try a secure login */
512 /* request data protection level (default is 'clear') */
513 Curl_sec_request_prot(conn, "private");
515 /* We set private first as default, in case the line below fails to
517 Curl_sec_request_prot(conn, data->set.krb4_level);
519 if(Curl_sec_login(conn) != 0)
520 infof(data, "Logging in with password in cleartext!\n");
522 infof(data, "Authentication successful\n");
526 if(data->set.ftp_ssl && !conn->ssl[FIRSTSOCKET].use) {
527 /* we don't have a SSL/TLS connection, try a FTPS connection now */
532 switch(data->set.ftpsslauth) {
533 case CURLFTPAUTH_DEFAULT:
534 case CURLFTPAUTH_SSL:
538 case CURLFTPAUTH_TLS:
543 failf(data, "unsupported parameter to CURLOPT_FTPSSLAUTH: %d\n",
544 data->set.ftpsslauth);
545 return CURLE_FAILED_INIT; /* we don't know what to do */
548 for (trynum = start; ftpauth[count]; trynum=trynext, count++) {
550 FTPSENDF(conn, "AUTH %s", ftpauth[trynum]);
552 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
557 /* RFC2228 (page 5) says:
559 * If the server is willing to accept the named security mechanism, and
560 * does not require any security data, it must respond with reply code
564 if((ftpcode == 234) || (ftpcode == 334)) {
565 result = Curl_SSLConnect(conn, FIRSTSOCKET);
568 conn->protocol |= PROT_FTPS;
569 conn->ssl[SECONDARYSOCKET].use = FALSE; /* clear-text data */
576 FTPSENDF(conn, "USER %s", ftp->user?ftp->user:"");
578 /* wait for feedback */
579 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
584 /* 530 User ... access denied
585 (the server denies to log the specified user) */
586 failf(data, "Access denied: %s", &buf[4]);
587 return CURLE_FTP_ACCESS_DENIED;
589 else if(ftpcode == 331) {
590 /* 331 Password required for ...
591 (the server requires to send the user's password too) */
592 FTPSENDF(conn, "PASS %s", ftp->passwd?ftp->passwd:"");
593 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
598 /* 530 Login incorrect.
599 (the username and/or the password are incorrect)
601 530 Sorry, the maximum number of allowed users are already connected
603 failf(data, "not logged in: %s", &buf[4]);
604 return CURLE_FTP_USER_PASSWORD_INCORRECT;
606 else if(ftpcode/100 == 2) {
607 /* 230 User ... logged in.
608 (user successfully logged in)
610 Apparently, proftpd with SSL returns 232 here at times. */
612 infof(data, "We have successfully logged in\n");
615 failf(data, "Odd return code after PASS");
616 return CURLE_FTP_WEIRD_PASS_REPLY;
619 else if(buf[0] == '2') {
620 /* 230 User ... logged in.
621 (the user logged in without password) */
622 infof(data, "We have successfully logged in\n");
623 if (conn->ssl[FIRSTSOCKET].use) {
625 /* We are logged in with Kerberos, now set the requested protection
628 if(conn->sec_complete)
629 Curl_sec_set_protection_level(conn);
631 /* We may need to issue a KAUTH here to have access to the files
632 * do it if user supplied a password
634 if(conn->passwd && *conn->passwd) {
635 result = Curl_krb_kauth(conn);
643 failf(data, "Odd return code after USER");
644 return CURLE_FTP_WEIRD_USER_REPLY;
647 if(conn->ssl[FIRSTSOCKET].use) {
648 /* PBSZ = PROTECTION BUFFER SIZE.
650 The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says:
652 Specifically, the PROT command MUST be preceded by a PBSZ command
653 and a PBSZ command MUST be preceded by a successful security data
654 exchange (the TLS negotiation in this case)
658 Thus the PBSZ command must still be issued, but must have a parameter
659 of '0' to indicate that no buffering is taking place and the data
660 connection should not be encapsulated.
662 FTPSENDF(conn, "PBSZ %d", 0);
663 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
667 /* For TLS, the data connection can have one of two security levels.
669 1)Clear (requested by 'PROT C')
671 2)Private (requested by 'PROT P')
673 if(!conn->ssl[SECONDARYSOCKET].use) {
674 FTPSENDF(conn, "PROT %c", 'P');
675 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
680 /* We have enabled SSL for the data connection! */
681 conn->ssl[SECONDARYSOCKET].use = TRUE;
682 /* FTP servers typically responds with 500 if they decide to reject
684 else if(data->set.ftp_ssl> CURLFTPSSL_CONTROL)
685 /* we failed and bails out */
686 return CURLE_FTP_SSL_FAILED;
690 /* send PWD to discover our entry point */
691 FTPSENDF(conn, "PWD", NULL);
693 /* wait for feedback */
694 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
699 char *dir = (char *)malloc(nread+1);
701 char *ptr=&buf[4]; /* start on the first letter */
704 return CURLE_OUT_OF_MEMORY;
706 /* Reply format is like
707 257<space>"<directory-name>"<space><commentary> and the RFC959 says
709 The directory name can contain any character; embedded double-quotes
710 should be escaped by double-quotes (the "quote-doubling" convention).
713 /* it started good */
718 /* "quote-doubling" */
724 *store = '\0'; /* zero terminate */
725 break; /* get out of this loop */
733 ftp->entrypath =dir; /* remember this */
734 infof(data, "Entry path is '%s'\n", ftp->entrypath);
737 /* couldn't get the path */
739 infof(data, "Failed to figure out path\n");
744 /* We couldn't read the PWD response! */
750 /***********************************************************************
754 * The DONE function. This does what needs to be done after a single DO has
757 * Input argument is already checked for validity.
759 CURLcode Curl_ftp_done(struct connectdata *conn, CURLcode status)
761 struct SessionHandle *data = conn->data;
762 struct FTP *ftp = conn->proto.ftp;
765 CURLcode result=CURLE_OK;
767 bool was_ctl_valid = ftp->ctl_valid;
769 /* now store a copy of the directory we are in */
773 size_t flen = ftp->file?strlen(ftp->file):0;
774 size_t dlen = conn->path?strlen(conn->path)-flen:0;
776 ftp->prevpath = malloc(dlen + 1);
778 return CURLE_OUT_OF_MEMORY;
779 memcpy(ftp->prevpath, conn->path, dlen);
780 ftp->prevpath[dlen]=0; /* terminate */
781 infof(data, "Remembering we are in dir %s\n", ftp->prevpath);
784 ftp->prevpath = NULL; /* no path */
787 /* free the dir tree and file parts */
790 ftp->ctl_valid = FALSE;
792 if(data->set.upload) {
793 if((-1 != data->set.infilesize) &&
794 (data->set.infilesize != *ftp->bytecountp) &&
796 failf(data, "Uploaded unaligned file size (%" FORMAT_OFF_T
797 " out of %" FORMAT_OFF_T " bytes)",
798 *ftp->bytecountp, data->set.infilesize);
799 conn->bits.close = TRUE; /* close this connection since we don't
800 know what state this error leaves us in */
801 return CURLE_PARTIAL_FILE;
805 if((-1 != conn->size) && (conn->size != *ftp->bytecountp) &&
806 (conn->maxdownload != *ftp->bytecountp)) {
807 failf(data, "Received only partial file: %" FORMAT_OFF_T " bytes",
809 conn->bits.close = TRUE; /* close this connection since we don't
810 know what state this error leaves us in */
811 return CURLE_PARTIAL_FILE;
813 else if(!ftp->dont_check &&
816 /* We consider this an error, but there's no true FTP error received
817 why we need to continue to "read out" the server response too.
818 We don't want to leave a "waiting" server reply if we'll get told
819 to make a second request on this same connection! */
820 failf(data, "No data was received!");
821 result = CURLE_FTP_COULDNT_RETR_FILE;
826 case CURLE_BAD_DOWNLOAD_RESUME:
827 case CURLE_FTP_WEIRD_PASV_REPLY:
828 case CURLE_FTP_PORT_FAILED:
829 case CURLE_FTP_COULDNT_SET_BINARY:
830 case CURLE_FTP_COULDNT_RETR_FILE:
831 case CURLE_FTP_ACCESS_DENIED:
832 /* the connection stays alive fine even though this happened */
834 case CURLE_OK: /* doesn't affect the control connection's status */
835 ftp->ctl_valid = was_ctl_valid;
837 default: /* by default, an error means the control connection is
838 wedged and should not be used anymore */
839 ftp->ctl_valid = FALSE;
844 Curl_sec_fflush_fd(conn, conn->sock[SECONDARYSOCKET]);
847 /* shut down the socket to inform the server we're done */
850 shutdown(conn->sock[SECONDARYSOCKET],2); /* SD_BOTH */
853 sclose(conn->sock[SECONDARYSOCKET]);
855 conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
857 if(!ftp->no_transfer && !status) {
858 /* Let's see what the server says about the transfer we just performed,
859 * but lower the timeout as sometimes this connection has died while the
860 * data has been transfered. This happens when doing through NATs etc that
861 * abandon old silent connections.
863 ftp->response_time = 60; /* give it only a minute for now */
865 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
867 ftp->response_time = 3600; /* set this back to one hour waits */
869 if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) {
870 failf(data, "control connection looks dead");
877 if(!ftp->dont_check) {
878 /* 226 Transfer complete, 250 Requested file action okay, completed. */
879 if((ftpcode != 226) && (ftpcode != 250)) {
880 failf(data, "server did not report OK, got %d", ftpcode);
881 return CURLE_FTP_WRITE_ERROR;
886 /* clear these for next connection */
887 ftp->no_transfer = FALSE;
888 ftp->dont_check = FALSE;
890 if (!result && conn->sec_conn) { /* 3rd party transfer */
891 /* "done" with the secondary connection */
892 result = Curl_ftp_done(conn->sec_conn, status);
895 /* Send any post-transfer QUOTE strings? */
896 if(!status && !result && data->set.postquote)
897 result = ftp_sendquote(conn, data->set.postquote);
902 /***********************************************************************
906 * Where a 'quote' means a list of custom commands to send to the server.
907 * The quote list is passed as an argument.
911 CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote)
913 struct curl_slist *item;
921 FTPSENDF(conn, "%s", item->data);
923 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
927 if (ftpcode >= 400) {
928 failf(conn->data, "QUOT string not accepted: %s", item->data);
929 return CURLE_FTP_QUOTE_ERROR;
939 /***********************************************************************
943 * Get the timestamp of the given file.
946 CURLcode ftp_getfiletime(struct connectdata *conn, char *file)
948 CURLcode result=CURLE_OK;
949 int ftpcode; /* for ftp status */
951 char *buf = conn->data->state.buffer;
953 /* we have requested to get the modified-time of the file, this is yet
954 again a grey area as the MDTM is not kosher RFC959 */
955 FTPSENDF(conn, "MDTM %s", file);
957 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
964 /* we got a time. Format should be: "YYYYMMDDHHMMSS[.sss]" where the
965 last .sss part is optional and means fractions of a second */
966 int year, month, day, hour, minute, second;
967 if(6 == sscanf(buf+4, "%04d%02d%02d%02d%02d%02d",
968 &year, &month, &day, &hour, &minute, &second)) {
969 /* we have a time, reformat it */
970 time_t secs=time(NULL);
971 /* using the good old yacc/bison yuck */
972 snprintf(buf, sizeof(conn->data->state.buffer),
973 "%04d%02d%02d %02d:%02d:%02d GMT",
974 year, month, day, hour, minute, second);
975 /* now, convert this into a time() value: */
976 conn->data->info.filetime = curl_getdate(buf, &secs);
981 infof(conn->data, "unsupported MDTM reply format\n");
983 case 550: /* "No such file or directory" */
984 failf(conn->data, "Given file does not exist");
985 result = CURLE_FTP_COULDNT_RETR_FILE;
991 /***********************************************************************
995 * Set transfer type. We only deal with ASCII or BINARY so this function
998 static CURLcode ftp_transfertype(struct connectdata *conn,
1001 struct SessionHandle *data = conn->data;
1006 FTPSENDF(conn, "TYPE %s", ascii?"A":"I");
1008 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1012 if(ftpcode != 200) {
1013 failf(data, "Couldn't set %s mode",
1014 ascii?"ASCII":"binary");
1015 return ascii? CURLE_FTP_COULDNT_SET_ASCII:CURLE_FTP_COULDNT_SET_BINARY;
1021 /***********************************************************************
1025 * Returns the file size (in bytes) of the given remote file.
1029 CURLcode ftp_getsize(struct connectdata *conn, char *file,
1032 struct SessionHandle *data = conn->data;
1035 char *buf=data->state.buffer;
1038 FTPSENDF(conn, "SIZE %s", file);
1039 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1043 if(ftpcode == 213) {
1044 /* get the size from the ascii string: */
1045 *size = curlx_strtoofft(buf+4, NULL, 0);
1048 return CURLE_FTP_COULDNT_GET_SIZE;
1053 /***************************************************************************
1055 * ftp_pasv_verbose()
1057 * This function only outputs some informationals about this second connection
1058 * when we've issued a PASV command before and thus we have connected to a
1059 * possibly new IP address.
1063 ftp_pasv_verbose(struct connectdata *conn,
1065 char *newhost, /* ascii version */
1069 Curl_printable_address(ai, buf, sizeof(buf));
1070 infof(conn->data, "Connecting to %s (%s) port %d\n", newhost, buf, port);
1073 /***********************************************************************
1077 * Send the proper PORT command. PORT is the ftp client's way of telling the
1078 * server that *WE* open a port that we listen on an awaits the server to
1079 * connect to. This is the opposite of PASV.
1083 CURLcode ftp_use_port(struct connectdata *conn)
1085 struct SessionHandle *data=conn->data;
1086 curl_socket_t portsock= CURL_SOCKET_BAD;
1088 int ftpcode; /* receive FTP response codes in this */
1092 /******************************************************************
1094 * Here's a piece of IPv6-specific code coming up
1098 struct addrinfo hints, *res, *ai;
1099 struct sockaddr_storage ss;
1101 char hbuf[NI_MAXHOST];
1103 struct sockaddr *sa=(struct sockaddr *)&ss;
1106 char portmsgbuf[1024], tmp[1024];
1108 enum ftpcommand { EPRT, LPRT, PORT, DONE } fcmd;
1109 const char *mode[] = { "EPRT", "LPRT", "PORT", NULL };
1114 * we should use Curl_if2ip? given pickiness of recent ftpd,
1115 * I believe we should use the same address as the control connection.
1118 rc = getsockname(conn->sock[FIRSTSOCKET], (struct sockaddr *)&ss, &sslen);
1120 failf(data, "getsockname() returned %d\n", rc);
1121 return CURLE_FTP_PORT_FAILED;
1124 rc = getnameinfo((struct sockaddr *)&ss, sslen, hbuf, sizeof(hbuf), NULL, 0,
1127 failf(data, "getnameinfo() returned %d\n", rc);
1128 return CURLE_FTP_PORT_FAILED;
1131 memset(&hints, 0, sizeof(hints));
1132 hints.ai_family = sa->sa_family;
1133 /*hints.ai_family = ss.ss_family;
1134 this way can be used if sockaddr_storage is properly defined, as glibc
1136 hints.ai_socktype = SOCK_STREAM;
1137 hints.ai_flags = AI_PASSIVE;
1139 rc = getaddrinfo(hbuf, NULL, &hints, &res);
1141 failf(data, "getaddrinfo() returned %d\n", rc);
1142 return CURLE_FTP_PORT_FAILED;
1145 portsock = CURL_SOCKET_BAD;
1147 for (ai = res; ai; ai = ai->ai_next) {
1149 * Workaround for AIX5 getaddrinfo() problem (it doesn't set ai_socktype):
1151 if (ai->ai_socktype == 0)
1152 ai->ai_socktype = hints.ai_socktype;
1154 portsock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
1155 if (portsock == CURL_SOCKET_BAD) {
1156 error = Curl_ourerrno();
1160 if (bind(portsock, ai->ai_addr, ai->ai_addrlen) < 0) {
1161 error = Curl_ourerrno();
1163 portsock = CURL_SOCKET_BAD;
1167 if (listen(portsock, 1) < 0) {
1168 error = Curl_ourerrno();
1170 portsock = CURL_SOCKET_BAD;
1177 if (portsock == CURL_SOCKET_BAD) {
1178 failf(data, "%s", Curl_strerror(conn,error));
1179 return CURLE_FTP_PORT_FAILED;
1183 if (getsockname(portsock, sa, &sslen) < 0) {
1184 failf(data, "%s", Curl_strerror(conn,Curl_ourerrno()));
1185 return CURLE_FTP_PORT_FAILED;
1189 if(!conn->bits.ftp_use_eprt &&
1190 (conn->ip_addr->ai_family == PF_INET6)) {
1191 /* EPRT is disabled but we are connected to a IPv6 host, so we ignore the
1193 conn->bits.ftp_use_eprt = TRUE;
1198 for (fcmd = EPRT; fcmd != DONE; fcmd++) {
1202 if(!conn->bits.ftp_use_eprt && (EPRT == fcmd))
1203 /* if disabled, goto next */
1206 if(!conn->bits.ftp_use_lprt && (LPRT == fcmd))
1207 /* if disabled, goto next */
1210 switch (sa->sa_family) {
1212 ap = (unsigned char *)&((struct sockaddr_in *)&ss)->sin_addr;
1213 alen = sizeof(((struct sockaddr_in *)&ss)->sin_addr);
1214 pp = (unsigned char *)&((struct sockaddr_in *)&ss)->sin_port;
1215 plen = sizeof(((struct sockaddr_in *)&ss)->sin_port);
1220 ap = (unsigned char *)&((struct sockaddr_in6 *)&ss)->sin6_addr;
1221 alen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_addr);
1222 pp = (unsigned char *)&((struct sockaddr_in6 *)&ss)->sin6_port;
1223 plen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_port);
1229 lprtaf = eprtaf = -1;
1236 if (getnameinfo((struct sockaddr *)&ss, sslen,
1237 portmsgbuf, sizeof(portmsgbuf), tmp, sizeof(tmp),
1241 /* do not transmit IPv6 scope identifier to the wire */
1242 if (sa->sa_family == AF_INET6) {
1243 char *q = strchr(portmsgbuf, '%');
1248 result = Curl_ftpsendf(conn, "%s |%d|%s|%s|", mode[fcmd], eprtaf,
1253 else if ((LPRT == fcmd) || (PORT == fcmd)) {
1256 if ((LPRT == fcmd) && lprtaf < 0)
1258 if ((PORT == fcmd) && sa->sa_family != AF_INET)
1261 portmsgbuf[0] = '\0';
1263 snprintf(tmp, sizeof(tmp), "%d,%d", lprtaf, alen);
1264 if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >=
1265 sizeof(portmsgbuf)) {
1270 for (i = 0; i < alen; i++) {
1272 snprintf(tmp, sizeof(tmp), ",%u", ap[i]);
1274 snprintf(tmp, sizeof(tmp), "%u", ap[i]);
1276 if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >=
1277 sizeof(portmsgbuf)) {
1283 snprintf(tmp, sizeof(tmp), ",%d", plen);
1285 if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= sizeof(portmsgbuf))
1289 for (i = 0; i < plen; i++) {
1290 snprintf(tmp, sizeof(tmp), ",%u", pp[i]);
1292 if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >=
1293 sizeof(portmsgbuf)) {
1298 result = Curl_ftpsendf(conn, "%s %s", mode[fcmd], portmsgbuf);
1303 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1307 if (ftpcode != 200) {
1309 infof(data, "disabling EPRT usage\n");
1310 conn->bits.ftp_use_eprt = FALSE;
1312 else if (LPRT == fcmd) {
1313 infof(data, "disabling LPRT usage\n");
1314 conn->bits.ftp_use_lprt = FALSE;
1324 failf(data, "PORT command attempts failed");
1325 return CURLE_FTP_PORT_FAILED;
1327 /* we set the secondary socket variable to this for now, it
1328 is only so that the cleanup function will close it in case
1329 we fail before the true secondary stuff is made */
1330 conn->sock[SECONDARYSOCKET] = portsock;
1333 /******************************************************************
1335 * Here's a piece of IPv4-specific code coming up
1338 struct sockaddr_in sa;
1339 unsigned short porttouse;
1340 char myhost[256] = "";
1341 bool sa_filled_in = FALSE;
1342 Curl_addrinfo *addr = NULL;
1343 unsigned short ip[4];
1345 if(data->set.ftpport) {
1348 /* First check if the given name is an IP address */
1349 in=inet_addr(data->set.ftpport);
1351 if(in != CURL_INADDR_NONE)
1352 /* this is an IPv4 address */
1353 addr = Curl_ip2addr(in, data->set.ftpport, 0);
1355 if(Curl_if2ip(data->set.ftpport, myhost, sizeof(myhost))) {
1356 /* The interface to IP conversion provided a dotted address */
1357 in=inet_addr(myhost);
1358 addr = Curl_ip2addr(in, myhost, 0);
1360 else if(strlen(data->set.ftpport)> 1) {
1361 /* might be a host name! */
1362 struct Curl_dns_entry *h=NULL;
1363 int rc = Curl_resolv(conn, myhost, 0, &h);
1364 if(rc == CURLRESOLV_PENDING)
1365 rc = Curl_wait_for_resolv(conn, &h);
1368 /* when we return from this function, we can forget about this entry
1369 to we can unlock it now already */
1370 Curl_resolv_unlock(data, h);
1373 } /* CURL_INADDR_NONE */
1374 } /* data->set.ftpport */
1377 /* pick a suitable default here */
1382 if (getsockname(conn->sock[FIRSTSOCKET],
1383 (struct sockaddr *)&sa, &sslen) < 0) {
1384 failf(data, "getsockname() failed");
1385 return CURLE_FTP_PORT_FAILED;
1388 sa_filled_in = TRUE; /* the sa struct is filled in */
1391 if (addr || sa_filled_in) {
1392 portsock = socket(AF_INET, SOCK_STREAM, 0);
1393 if(CURL_SOCKET_BAD != portsock) {
1396 /* we set the secondary socket variable to this for now, it
1397 is only so that the cleanup function will close it in case
1398 we fail before the true secondary stuff is made */
1399 conn->sock[SECONDARYSOCKET] = portsock;
1402 memcpy(&sa, addr->ai_addr, sizeof(sa));
1403 sa.sin_addr.s_addr = INADDR_ANY;
1409 if(bind(portsock, (struct sockaddr *)&sa, size) >= 0) {
1410 /* we succeeded to bind */
1411 struct sockaddr_in add;
1412 socklen_t socksize = sizeof(add);
1414 if(getsockname(portsock, (struct sockaddr *) &add,
1416 failf(data, "getsockname() failed");
1417 return CURLE_FTP_PORT_FAILED;
1419 porttouse = ntohs(add.sin_port);
1421 if ( listen(portsock, 1) < 0 ) {
1422 failf(data, "listen(2) failed on socket");
1423 return CURLE_FTP_PORT_FAILED;
1427 failf(data, "bind(2) failed on socket");
1428 return CURLE_FTP_PORT_FAILED;
1432 failf(data, "socket(2) failed (%s)");
1433 return CURLE_FTP_PORT_FAILED;
1437 failf(data, "could't find IP address to use");
1438 return CURLE_FTP_PORT_FAILED;
1442 Curl_inet_ntop(AF_INET, &((struct sockaddr_in *)&sa)->sin_addr,
1443 myhost, sizeof(myhost));
1445 Curl_printable_address(addr, myhost, sizeof(myhost));
1447 if(4 == sscanf(myhost, "%hu.%hu.%hu.%hu",
1448 &ip[0], &ip[1], &ip[2], &ip[3])) {
1450 infof(data, "Telling server to connect to %d.%d.%d.%d:%d\n",
1451 ip[0], ip[1], ip[2], ip[3], porttouse);
1453 result=Curl_ftpsendf(conn, "PORT %d,%d,%d,%d,%d,%d",
1454 ip[0], ip[1], ip[2], ip[3],
1462 return CURLE_FTP_PORT_FAILED;
1464 Curl_freeaddrinfo(addr);
1466 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1470 if(ftpcode != 200) {
1471 failf(data, "Server does not grok PORT, try without it!");
1472 return CURLE_FTP_PORT_FAILED;
1474 #endif /* end of ipv4-specific code */
1479 /***********************************************************************
1483 * Send the PASV command. PASV is the ftp client's way of asking the server to
1484 * open a second port that we can connect to (for the data transfer). This is
1485 * the opposite of PORT.
1489 CURLcode ftp_use_pasv(struct connectdata *conn,
1492 struct SessionHandle *data = conn->data;
1494 char *buf = data->state.buffer; /* this is our buffer */
1495 int ftpcode; /* receive FTP response codes in this */
1497 struct Curl_dns_entry *addr=NULL;
1498 Curl_addrinfo *conninfo;
1502 Here's the excecutive summary on what to do:
1504 PASV is RFC959, expect:
1505 227 Entering Passive Mode (a1,a2,a3,a4,p1,p2)
1507 LPSV is RFC1639, expect:
1508 228 Entering Long Passive Mode (4,4,a1,a2,a3,a4,2,p1,p2)
1510 EPSV is RFC2428, expect:
1511 229 Entering Extended Passive Mode (|||port|)
1515 const char *mode[] = { "EPSV", "PASV", NULL };
1516 int results[] = { 229, 227, 0 };
1518 unsigned short connectport; /* the local port connect() should use! */
1519 unsigned short newport=0; /* remote port, not necessary the local one */
1521 /* newhost must be able to hold a full IP-style address in ASCII, which
1522 in the IPv6 case means 5*8-1 = 39 letters */
1523 #define NEWHOST_BUFSIZE 48
1524 char newhost[NEWHOST_BUFSIZE];
1527 if(!conn->bits.ftp_use_epsv &&
1528 (conn->ip_addr->ai_family == PF_INET6)) {
1529 /* EPSV is disabled but we are connected to a IPv6 host, so we ignore the
1531 conn->bits.ftp_use_epsv = TRUE;
1535 for (modeoff = (conn->bits.ftp_use_epsv?0:1);
1536 mode[modeoff]; modeoff++) {
1537 result = Curl_ftpsendf(conn, "%s", mode[modeoff]);
1540 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1543 if (ftpcode == results[modeoff])
1547 /* EPSV is not supported, disable it for next transfer */
1548 conn->bits.ftp_use_epsv = FALSE;
1549 infof(data, "disabling EPSV usage\n");
1553 if (!mode[modeoff]) {
1554 failf(data, "Odd return code after PASV");
1555 return CURLE_FTP_WEIRD_PASV_REPLY;
1557 else if (227 == results[modeoff]) {
1563 * New 227-parser June 3rd 1999.
1564 * It now scans for a sequence of six comma-separated numbers and
1565 * will take them as IP+port indicators.
1567 * Found reply-strings include:
1568 * "227 Entering Passive Mode (127,0,0,1,4,51)"
1569 * "227 Data transfer will passively listen to 127,0,0,1,4,51"
1570 * "227 Entering passive mode. 127,0,0,1,4,51"
1574 if (6 == sscanf(str, "%d,%d,%d,%d,%d,%d",
1575 &ip[0], &ip[1], &ip[2], &ip[3],
1576 &port[0], &port[1]))
1582 failf(data, "Couldn't interpret this 227-reply: %s", buf);
1583 return CURLE_FTP_WEIRD_227_FORMAT;
1586 snprintf(newhost, sizeof(newhost),
1587 "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
1588 newport = (port[0]<<8) + port[1];
1590 else if (229 == results[modeoff]) {
1591 char *ptr = strchr(buf, '(');
1596 if(5 == sscanf(ptr, "%c%c%c%u%c",
1602 char sep1 = separator[0];
1605 /* The four separators should be identical, or else this is an oddly
1606 formatted reply and we bail out immediately. */
1607 for(i=1; i<4; i++) {
1608 if(separator[i] != sep1) {
1609 ptr=NULL; /* set to NULL to signal error */
1616 /* We must use the same IP we are already connected to */
1617 Curl_printable_address(conn->ip_addr, newhost, NEWHOST_BUFSIZE);
1624 failf(data, "Weirdly formatted EPSV reply");
1625 return CURLE_FTP_WEIRD_PASV_REPLY;
1629 return CURLE_FTP_CANT_RECONNECT;
1631 if(data->change.proxy && *data->change.proxy) {
1633 * This is a tunnel through a http proxy and we need to connect to the
1636 * We don't want to rely on a former host lookup that might've expired
1637 * now, instead we remake the lookup here and now!
1639 rc = Curl_resolv(conn, conn->proxy.name, (int)conn->port, &addr);
1640 if(rc == CURLRESOLV_PENDING)
1641 rc = Curl_wait_for_resolv(conn, &addr);
1644 (unsigned short)conn->port; /* we connect to the proxy's port */
1648 /* normal, direct, ftp connection */
1649 rc = Curl_resolv(conn, newhost, newport, &addr);
1650 if(rc == CURLRESOLV_PENDING)
1651 rc = Curl_wait_for_resolv(conn, &addr);
1654 failf(data, "Can't resolve new host %s:%d", newhost, newport);
1655 return CURLE_FTP_CANT_GET_HOST;
1657 connectport = newport; /* we connect to the remote port */
1660 result = Curl_connecthost(conn,
1662 &conn->sock[SECONDARYSOCKET],
1666 Curl_resolv_unlock(data, addr); /* we're done using this address */
1672 * When this is used from the multi interface, this might've returned with
1673 * the 'connected' set to FALSE and thus we are now awaiting a non-blocking
1674 * connect to connect and we should not be "hanging" here waiting.
1677 if(data->set.verbose)
1678 /* this just dumps information about this second connection */
1679 ftp_pasv_verbose(conn, conninfo, newhost, connectport);
1681 #ifndef CURL_DISABLE_HTTP
1682 if(conn->bits.tunnel_proxy) {
1683 /* We want "seamless" FTP operations through HTTP proxy tunnel */
1684 result = Curl_ConnectHTTPProxyTunnel(conn, SECONDARYSOCKET,
1686 if(CURLE_OK != result)
1689 #endif /* CURL_DISABLE_HTTP */
1695 * Curl_ftp_nextconnect()
1697 * This function shall be called when the second FTP connection has been
1698 * established and is confirmed connected.
1701 CURLcode Curl_ftp_nextconnect(struct connectdata *conn)
1703 struct SessionHandle *data=conn->data;
1704 char *buf = data->state.buffer; /* this is our buffer */
1707 int ftpcode; /* for ftp status */
1709 /* the ftp struct is already inited in Curl_ftp_connect() */
1710 struct FTP *ftp = conn->proto.ftp;
1711 curl_off_t *bytecountp = ftp->bytecountp;
1713 if(data->set.upload) {
1715 /* Set type to binary (unless specified ASCII) */
1716 result = ftp_transfertype(conn, data->set.ftp_ascii);
1720 /* Send any PREQUOTE strings after transfer type is set? (Wesley Laxton)*/
1721 if(data->set.prequote) {
1722 if ((result = ftp_sendquote(conn, data->set.prequote)) != CURLE_OK)
1726 if(conn->resume_from) {
1727 /* we're about to continue the uploading of a file */
1728 /* 1. get already existing file's size. We use the SIZE
1729 command for this which may not exist in the server!
1730 The SIZE command is not in RFC959. */
1732 /* 2. This used to set REST. But since we can do append, we
1733 don't another ftp command. We just skip the source file
1734 offset and then we APPEND the rest on the file instead */
1736 /* 3. pass file-size number of bytes in the source file */
1737 /* 4. lower the infilesize counter */
1738 /* => transfer as usual */
1740 if(conn->resume_from < 0 ) {
1741 /* we could've got a specified offset from the command line,
1742 but now we know we didn't */
1743 curl_off_t gottensize;
1745 if(CURLE_OK != ftp_getsize(conn, ftp->file, &gottensize)) {
1746 failf(data, "Couldn't get remote file size");
1747 return CURLE_FTP_COULDNT_GET_SIZE;
1749 conn->resume_from = gottensize;
1752 if(conn->resume_from) {
1753 /* do we still game? */
1754 curl_off_t passed=0;
1755 /* enable append instead */
1756 data->set.ftp_append = 1;
1758 /* Now, let's read off the proper amount of bytes from the
1759 input. If we knew it was a proper file we could've just
1760 fseek()ed but we only have a stream here */
1762 curl_off_t readthisamountnow = (conn->resume_from - passed);
1763 curl_off_t actuallyread;
1765 if(readthisamountnow > BUFSIZE)
1766 readthisamountnow = BUFSIZE;
1768 actuallyread = (curl_off_t)
1769 conn->fread(data->state.buffer, 1, (size_t)readthisamountnow,
1772 passed += actuallyread;
1773 if(actuallyread != readthisamountnow) {
1774 failf(data, "Could only read %" FORMAT_OFF_T
1775 " bytes from the input", passed);
1776 return CURLE_FTP_COULDNT_USE_REST;
1779 while(passed != conn->resume_from);
1781 /* now, decrease the size of the read */
1782 if(data->set.infilesize>0) {
1783 data->set.infilesize -= conn->resume_from;
1785 if(data->set.infilesize <= 0) {
1786 infof(data, "File already completely uploaded\n");
1788 /* no data to transfer */
1789 result=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1791 /* Set no_transfer so that we won't get any error in
1792 * Curl_ftp_done() because we didn't transfer anything! */
1793 ftp->no_transfer = TRUE;
1798 /* we've passed, proceed as normal */
1802 /* Send everything on data->state.in to the socket */
1803 if(data->set.ftp_append) {
1804 /* we append onto the file instead of rewriting it */
1805 FTPSENDF(conn, "APPE %s", ftp->file);
1808 FTPSENDF(conn, "STOR %s", ftp->file);
1811 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1816 failf(data, "Failed FTP upload:%s", buf+3);
1817 /* oops, we never close the sockets! */
1818 return CURLE_FTP_COULDNT_STOR_FILE;
1821 if(data->set.ftp_use_port) {
1822 /* PORT means we are now awaiting the server to connect to us. */
1823 result = AllowServerConnect(conn);
1828 if(conn->ssl[SECONDARYSOCKET].use) {
1829 /* since we only have a plaintext TCP connection here, we must now
1831 infof(data, "Doing the SSL/TLS handshake on the data stream\n");
1832 result = Curl_SSLConnect(conn, SECONDARYSOCKET);
1839 /* When we know we're uploading a specified file, we can get the file
1840 size prior to the actual upload. */
1842 Curl_pgrsSetUploadSize(data, data->set.infilesize);
1844 result = Curl_Transfer(conn, -1, -1, FALSE, NULL, /* no download */
1845 SECONDARYSOCKET, bytecountp);
1850 else if(!conn->bits.no_body) {
1851 /* Retrieve file or directory */
1853 curl_off_t downloadsize=-1;
1855 if(conn->bits.use_range && conn->range) {
1856 curl_off_t from, to;
1857 curl_off_t totalsize=-1;
1861 from=curlx_strtoofft(conn->range, &ptr, 0);
1862 while(ptr && *ptr && (isspace((int)*ptr) || (*ptr=='-')))
1864 to=curlx_strtoofft(ptr, &ptr2, 0);
1866 /* we didn't get any digit */
1869 if((-1 == to) && (from>=0)) {
1871 conn->resume_from = from;
1872 infof(data, "FTP RANGE %" FORMAT_OFF_T " to end of file\n", from);
1877 conn->maxdownload = -from;
1878 conn->resume_from = from;
1879 infof(data, "FTP RANGE the last %" FORMAT_OFF_T " bytes\n", totalsize);
1883 totalsize = to-from;
1884 conn->maxdownload = totalsize+1; /* include the last mentioned byte */
1885 conn->resume_from = from;
1886 infof(data, "FTP RANGE from %" FORMAT_OFF_T
1887 " getting %" FORMAT_OFF_T " bytes\n", from, conn->maxdownload);
1889 infof(data, "range-download from %" FORMAT_OFF_T
1890 " to %" FORMAT_OFF_T ", totally %" FORMAT_OFF_T " bytes\n",
1891 from, to, conn->maxdownload);
1892 ftp->dont_check = TRUE; /* dont check for successful transfer */
1895 if((data->set.ftp_list_only) || !ftp->file) {
1896 /* The specified path ends with a slash, and therefore we think this
1897 is a directory that is requested, use LIST. But before that we
1898 need to set ASCII transfer mode. */
1901 /* Set type to ASCII */
1902 result = ftp_transfertype(conn, TRUE /* ASCII enforced */);
1906 /* if this output is to be machine-parsed, the NLST command will be
1907 better used since the LIST command output is not specified or
1908 standard in any way */
1910 FTPSENDF(conn, "%s",
1911 data->set.customrequest?data->set.customrequest:
1912 (data->set.ftp_list_only?"NLST":"LIST"));
1915 curl_off_t foundsize;
1917 /* Set type to binary (unless specified ASCII) */
1918 result = ftp_transfertype(conn, data->set.ftp_ascii);
1922 /* Send any PREQUOTE strings after transfer type is set? */
1923 if(data->set.prequote) {
1924 if ((result = ftp_sendquote(conn, data->set.prequote)) != CURLE_OK)
1928 /* Attempt to get the size, it'll be useful in some cases: for resumed
1929 downloads and when talking to servers that don't give away the size
1930 in the RETR response line. */
1931 result = ftp_getsize(conn, ftp->file, &foundsize);
1932 if(CURLE_OK == result) {
1933 if (data->set.max_filesize && foundsize > data->set.max_filesize) {
1934 failf(data, "Maximum file size exceeded");
1935 return CURLE_FILESIZE_EXCEEDED;
1937 downloadsize = foundsize;
1940 if(conn->resume_from) {
1942 /* Daniel: (August 4, 1999)
1944 * We start with trying to use the SIZE command to figure out the size
1945 * of the file we're gonna get. If we can get the size, this is by far
1946 * the best way to know if we're trying to resume beyond the EOF.
1948 * Daniel, November 28, 2001. We *always* get the size on downloads
1949 * now, so it is done before this even when not doing resumes. I saved
1950 * the comment above for nostalgical reasons! ;-)
1952 if(CURLE_OK != result) {
1953 infof(data, "ftp server doesn't support SIZE\n");
1954 /* We couldn't get the size and therefore we can't know if there
1955 really is a part of the file left to get, although the server
1956 will just close the connection when we start the connection so it
1957 won't cause us any harm, just not make us exit as nicely. */
1960 /* We got a file size report, so we check that there actually is a
1961 part of the file left to get, or else we go home. */
1962 if(conn->resume_from< 0) {
1963 /* We're supposed to download the last abs(from) bytes */
1964 if(foundsize < -conn->resume_from) {
1965 failf(data, "Offset (%" FORMAT_OFF_T
1966 ") was beyond file size (%" FORMAT_OFF_T ")",
1967 conn->resume_from, foundsize);
1968 return CURLE_BAD_DOWNLOAD_RESUME;
1970 /* convert to size to download */
1971 downloadsize = -conn->resume_from;
1972 /* download from where? */
1973 conn->resume_from = foundsize - downloadsize;
1976 if(foundsize < conn->resume_from) {
1977 failf(data, "Offset (%" FORMAT_OFF_T
1978 ") was beyond file size (%" FORMAT_OFF_T ")",
1979 conn->resume_from, foundsize);
1980 return CURLE_BAD_DOWNLOAD_RESUME;
1982 /* Now store the number of bytes we are expected to download */
1983 downloadsize = foundsize-conn->resume_from;
1987 if (downloadsize == 0) {
1988 /* no data to transfer */
1989 result=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1990 infof(data, "File already completely downloaded\n");
1992 /* Set no_transfer so that we won't get any error in Curl_ftp_done()
1993 * because we didn't transfer the any file */
1994 ftp->no_transfer = TRUE;
1998 /* Set resume file transfer offset */
1999 infof(data, "Instructs server to resume from offset %" FORMAT_OFF_T
2003 FTPSENDF(conn, "REST %" FORMAT_OFF_T, conn->resume_from);
2005 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2009 if(ftpcode != 350) {
2010 failf(data, "Couldn't use REST: %s", buf+4);
2011 return CURLE_FTP_COULDNT_USE_REST;
2015 FTPSENDF(conn, "RETR %s", ftp->file);
2018 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2022 if((ftpcode == 150) || (ftpcode == 125)) {
2026 150 Opening BINARY mode data connection for /etc/passwd (2241
2027 bytes). (ok, the file is being transfered)
2030 150 Opening ASCII mode data connection for /bin/ls
2033 150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes).
2036 150 Opening ASCII mode data connection for /linux/fisk/kpanelrc (0.0.0.0,0) (545 bytes).
2039 125 Data connection already open; Transfer starting. */
2041 curl_off_t size=-1; /* default unknown size */
2045 * It appears that there are FTP-servers that return size 0 for files
2046 * when SIZE is used on the file while being in BINARY mode. To work
2047 * around that (stupid) behavior, we attempt to parse the RETR response
2048 * even if the SIZE returned size zero.
2050 * Debugging help from Salvatore Sorrentino on February 26, 2003.
2054 !data->set.ftp_ascii &&
2055 (downloadsize < 1)) {
2057 * It seems directory listings either don't show the size or very
2058 * often uses size 0 anyway. ASCII transfers may very well turn out
2059 * that the transfered amount of data is not the same as this line
2060 * tells, why using this number in those cases only confuses us.
2062 * Example D above makes this parsing a little tricky */
2064 bytes=strstr(buf, " bytes");
2067 /* this is a hint there is size information in there! ;-) */
2069 /* scan for the parenthesis and break there */
2072 /* if only skip digits, or else we're in deep trouble */
2073 if(!isdigit((int)*bytes)) {
2077 /* one more estep backwards */
2080 /* only if we have nothing but digits: */
2082 /* get the number! */
2083 size = curlx_strtoofft(bytes, NULL, 0);
2088 else if(downloadsize > -1)
2089 size = downloadsize;
2091 if(data->set.ftp_use_port) {
2092 result = AllowServerConnect(conn);
2097 if(conn->ssl[SECONDARYSOCKET].use) {
2098 /* since we only have a plaintext TCP connection here, we must now
2100 infof(data, "Doing the SSL/TLS handshake on the data stream\n");
2101 result = Curl_SSLConnect(conn, SECONDARYSOCKET);
2106 if(size > conn->maxdownload && conn->maxdownload > 0)
2107 size = conn->size = conn->maxdownload;
2109 infof(data, "Getting file with size: %" FORMAT_OFF_T "\n", size);
2112 result=Curl_Transfer(conn, SECONDARYSOCKET, size, FALSE,
2114 -1, NULL); /* no upload here */
2119 if(dirlist && (ftpcode == 450)) {
2120 /* simply no matching files */
2121 ftp->no_transfer = TRUE; /* don't think we should download anything */
2124 failf(data, "%s", buf+4);
2125 return CURLE_FTP_COULDNT_RETR_FILE;
2130 /* end of transfer */
2135 /***********************************************************************
2139 * This is the actual DO function for FTP. Get a file/directory according to
2140 * the options previously setup.
2144 CURLcode ftp_perform(struct connectdata *conn,
2145 bool *connected) /* for the TCP connect status after
2148 /* this is FTP and no proxy */
2149 CURLcode result=CURLE_OK;
2150 struct SessionHandle *data=conn->data;
2151 char *buf = data->state.buffer; /* this is our buffer */
2153 /* the ftp struct is already inited in Curl_ftp_connect() */
2154 struct FTP *ftp = conn->proto.ftp;
2156 /* Send any QUOTE strings? */
2157 if(data->set.quote) {
2158 if ((result = ftp_sendquote(conn, data->set.quote)) != CURLE_OK)
2162 result = ftp_cwd_and_create_path(conn);
2166 /* Requested time of file or time-depended transfer? */
2167 if((data->set.get_filetime || data->set.timecondition) &&
2169 result = ftp_getfiletime(conn, ftp->file);
2172 case CURLE_FTP_COULDNT_RETR_FILE:
2174 if(data->set.timecondition) {
2175 if((data->info.filetime > 0) && (data->set.timevalue > 0)) {
2176 switch(data->set.timecondition) {
2177 case CURL_TIMECOND_IFMODSINCE:
2179 if(data->info.filetime < data->set.timevalue) {
2180 infof(data, "The requested document is not new enough\n");
2181 ftp->no_transfer = TRUE; /* mark this to not transfer data */
2185 case CURL_TIMECOND_IFUNMODSINCE:
2186 if(data->info.filetime > data->set.timevalue) {
2187 infof(data, "The requested document is not old enough\n");
2188 ftp->no_transfer = TRUE; /* mark this to not transfer data */
2195 infof(data, "Skipping time comparison\n");
2204 /* If we have selected NOBODY and HEADER, it means that we only want file
2205 information. Which in FTP can't be much more than the file size and
2207 if(conn->bits.no_body && data->set.include_header && ftp->file) {
2208 /* The SIZE command is _not_ RFC 959 specified, and therefor many servers
2209 may not support it! It is however the only way we have to get a file's
2211 curl_off_t filesize;
2215 ftp->no_transfer = TRUE; /* this means no actual transfer is made */
2217 /* Some servers return different sizes for different modes, and thus we
2218 must set the proper type before we check the size */
2219 result = ftp_transfertype(conn, data->set.ftp_ascii);
2223 /* failing to get size is not a serious error */
2224 result = ftp_getsize(conn, ftp->file, &filesize);
2226 if(CURLE_OK == result) {
2227 snprintf(buf, sizeof(data->state.buffer),
2228 "Content-Length: %" FORMAT_OFF_T "\r\n", filesize);
2229 result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
2234 /* Determine if server can respond to REST command and therefore
2235 whether it can do a range */
2236 FTPSENDF(conn, "REST 0", NULL);
2237 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2239 if ((CURLE_OK == result) && (ftpcode == 350)) {
2240 result = Curl_client_write(data, CLIENTWRITE_BOTH,
2241 (char *)"Accept-ranges: bytes\r\n", 0);
2246 /* If we asked for a time of the file and we actually got one as
2247 well, we "emulate" a HTTP-style header in our output. */
2249 #ifdef HAVE_STRFTIME
2250 if(data->set.get_filetime && (data->info.filetime>=0) ) {
2252 time_t clock = (time_t)data->info.filetime;
2253 #ifdef HAVE_GMTIME_R
2255 tm = (struct tm *)gmtime_r(&clock, &buffer);
2257 tm = gmtime(&clock);
2259 /* format: "Tue, 15 Nov 1994 12:45:26" */
2260 strftime(buf, BUFSIZE-1, "Last-Modified: %a, %d %b %Y %H:%M:%S GMT\r\n",
2262 result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
2271 if(conn->bits.no_body)
2272 /* doesn't really transfer any data */
2273 ftp->no_transfer = TRUE;
2274 /* Get us a second connection up and connected */
2275 else if(data->set.ftp_use_port) {
2276 /* We have chosen to use the PORT command */
2277 result = ftp_use_port(conn);
2278 if(CURLE_OK == result) {
2279 /* we have the data connection ready */
2280 infof(data, "Ordered connect of the data stream with PORT!\n");
2281 *connected = TRUE; /* mark us "still connected" */
2285 /* We have chosen (this is default) to use the PASV command */
2286 result = ftp_use_pasv(conn, connected);
2287 if(CURLE_OK == result && *connected)
2288 infof(data, "Connected the data stream with PASV!\n");
2294 /***********************************************************************
2298 * This function is registered as 'curl_do' function. It decodes the path
2299 * parts etc as a wrapper to the actual DO function (ftp_perform).
2301 * The input argument is already checked for validity.
2303 CURLcode Curl_ftp(struct connectdata *conn)
2305 CURLcode retcode = CURLE_OK;
2307 retcode = ftp_parse_url_path(conn);
2311 if (conn->sec_conn) /* 3rd party transfer */
2312 retcode = ftp_3rdparty(conn);
2314 retcode = ftp_regular_transfer(conn);
2319 /***********************************************************************
2323 * Sends the formated string as a ftp command to a ftp server
2325 * NOTE: we build the command in a fixed-length buffer, which sets length
2326 * restrictions on the command!
2328 CURLcode Curl_ftpsendf(struct connectdata *conn,
2329 const char *fmt, ...)
2331 ssize_t bytes_written;
2335 CURLcode res = CURLE_OK;
2339 vsnprintf(s, 250, fmt, ap);
2342 strcat(s, "\r\n"); /* append a trailing CRLF */
2345 write_len = strlen(s);
2348 res = Curl_write(conn, conn->sock[FIRSTSOCKET], sptr, write_len,
2354 if(conn->data->set.verbose)
2355 Curl_debug(conn->data, CURLINFO_HEADER_OUT, sptr, bytes_written, conn->host.dispname);
2357 if(bytes_written != (ssize_t)write_len) {
2358 write_len -= bytes_written;
2359 sptr += bytes_written;
2368 /***********************************************************************
2372 * This should be called before calling sclose() on an ftp control connection
2373 * (not data connections). We should then wait for the response from the
2374 * server before returning. The calling code should then try to close the
2378 static CURLcode ftp_quit(struct connectdata *conn)
2382 CURLcode ret = CURLE_OK;
2384 if(conn->proto.ftp->ctl_valid) {
2385 ret = Curl_ftpsendf(conn, "%s", "QUIT");
2387 ret = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2393 /***********************************************************************
2395 * Curl_ftp_disconnect()
2397 * Disconnect from an FTP server. Cleanup protocol-specific per-connection
2400 CURLcode Curl_ftp_disconnect(struct connectdata *conn)
2402 struct FTP *ftp= conn->proto.ftp;
2404 /* We cannot send quit unconditionally. If this connection is stale or
2405 bad in any way, sending quit and waiting around here will make the
2406 disconnect wait in vain and cause more problems than we need to.
2408 ftp_quit() will check the state of ftp->ctl_valid. If it's ok it
2409 will try to send the QUIT command, otherwise it will just return.
2412 /* The FTP session may or may not have been allocated/setup at this point! */
2414 (void)ftp_quit(conn); /* ignore errors on the QUIT */
2417 free(ftp->entrypath);
2424 free(ftp->prevpath);
2429 /***********************************************************************
2433 * Makes a directory on the FTP server.
2437 static CURLcode ftp_mkd(struct connectdata *conn, char *path)
2439 CURLcode result=CURLE_OK;
2440 int ftpcode; /* for ftp status */
2443 /* Create a directory on the remote server */
2444 FTPSENDF(conn, "MKD %s", path);
2446 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2453 infof( conn->data , "Created remote directory %s\n" , path );
2456 failf(conn->data, "Permission denied to make directory %s", path);
2457 result = CURLE_FTP_ACCESS_DENIED;
2460 failf(conn->data, "unrecognized MKD response: %d", ftpcode );
2461 result = CURLE_FTP_ACCESS_DENIED;
2467 /***********************************************************************
2471 * Send 'CWD' to the remote server to Change Working Directory. It is the ftp
2472 * version of the unix 'cd' command. This function is only called from the
2473 * ftp_cwd_and_mkd() function these days.
2475 * This function does NOT call failf().
2478 CURLcode ftp_cwd(struct connectdata *conn, char *path)
2484 FTPSENDF(conn, "CWD %s", path);
2485 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2487 /* According to RFC959, CWD is supposed to return 250 on success, but
2488 there seem to be non-compliant FTP servers out there that return 200,
2489 so we accept any '2xy' code here. */
2490 if (ftpcode/100 != 2)
2491 result = CURLE_FTP_ACCESS_DENIED;
2497 /***********************************************************************
2501 * Change to the given directory. If the directory is not present, and we
2502 * have been told to allow it, then create the directory and cd to it.
2505 static CURLcode ftp_cwd_and_mkd(struct connectdata *conn, char *path)
2509 result = ftp_cwd(conn, path);
2511 if(conn->data->set.ftp_create_missing_dirs) {
2512 result = ftp_mkd(conn, path);
2514 /* ftp_mkd() calls failf() itself */
2516 result = ftp_cwd(conn, path);
2519 failf(conn->data, "Couldn't CWD to %s", path);
2526 /***********************************************************************
2528 * ftp_3rdparty_pretransfer()
2530 * Preparation for 3rd party transfer.
2533 static CURLcode ftp_3rdparty_pretransfer(struct connectdata *conn)
2535 CURLcode result = CURLE_OK;
2536 struct SessionHandle *data = conn->data;
2537 struct connectdata *sec_conn = conn->sec_conn;
2539 /* sets transfer type */
2540 result = ftp_transfertype(conn, data->set.ftp_ascii);
2544 result = ftp_transfertype(sec_conn, data->set.ftp_ascii);
2548 /* Send any PREQUOTE strings after transfer type is set? */
2549 if (data->set.source_prequote) {
2550 /* sends command(s) to source server before file transfer */
2551 result = ftp_sendquote(sec_conn, data->set.source_prequote);
2553 if (!result && data->set.prequote)
2554 result = ftp_sendquote(conn, data->set.prequote);
2561 /***********************************************************************
2563 * ftp_3rdparty_transfer()
2565 * Performs 3rd party transfer.
2568 static CURLcode ftp_3rdparty_transfer(struct connectdata *conn)
2570 CURLcode result = CURLE_OK;
2572 int ftpcode, ip[4], port[2];
2573 struct SessionHandle *data = conn->data;
2574 struct connectdata *sec_conn = conn->sec_conn;
2575 char *buf = data->state.buffer; /* this is our buffer */
2578 const char *stor_cmd;
2579 struct connectdata *pasv_conn;
2580 struct connectdata *port_conn;
2582 if (data->set.pasvHost == CURL_TARGET_PASV) {
2584 port_conn = sec_conn;
2587 pasv_conn = sec_conn;
2591 result = ftp_cwd_and_create_path(conn);
2595 /* sets the passive mode */
2596 FTPSENDF(pasv_conn, "%s", "PASV");
2597 result = Curl_GetFTPResponse(&nread, pasv_conn, &ftpcode);
2598 if (result) return result;
2599 if (ftpcode != 227) {
2600 failf(data, "Odd return code after PASV:%s", buf + 3);
2601 return CURLE_FTP_WEIRD_PASV_REPLY;
2605 if (6 == sscanf(str, "%d,%d,%d,%d,%d,%d",
2606 &ip[0], &ip[1], &ip[2], &ip[3], &port[0], &port[1]))
2612 failf(pasv_conn->data, "Couldn't interpret this 227-reply: %s", buf);
2613 return CURLE_FTP_WEIRD_227_FORMAT;
2616 snprintf(pasv_port, sizeof(pasv_port), "%d,%d,%d,%d,%d,%d", ip[0], ip[1],
2617 ip[2], ip[3], port[0], port[1]);
2619 /* sets data connection between remote hosts */
2620 FTPSENDF(port_conn, "PORT %s", pasv_port);
2621 result = Curl_GetFTPResponse(&nread, port_conn, &ftpcode);
2625 if (ftpcode != 200) {
2626 failf(data, "PORT command attempts failed:%s", buf + 3);
2627 return CURLE_FTP_PORT_FAILED;
2630 /* we might append onto the file instead of overwriting it */
2631 stor_cmd = data->set.ftp_append?"APPE":"STOR";
2633 /* transfers file between remote hosts */
2634 FTPSENDF(sec_conn, "RETR %s", data->set.source_path);
2636 if(data->set.pasvHost == CURL_TARGET_PASV) {
2638 result = Curl_GetFTPResponse(&nread, sec_conn, &ftpcode);
2642 if (ftpcode != 150) {
2643 failf(data, "Failed RETR: %s", buf + 4);
2644 return CURLE_FTP_COULDNT_RETR_FILE;
2647 result = Curl_ftpsendf(conn, "%s %s", stor_cmd, conn->path);
2648 if(CURLE_OK == result)
2649 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2653 if (ftpcode != 150) {
2654 failf(data, "Failed FTP upload: %s", buf + 4);
2655 return CURLE_FTP_COULDNT_STOR_FILE;
2661 result = Curl_ftpsendf(conn, "%s %s", stor_cmd, conn->path);
2662 if(CURLE_OK == result)
2663 result = Curl_GetFTPResponse(&nread, sec_conn, &ftpcode);
2667 if (ftpcode != 150) {
2668 failf(data, "Failed FTP upload: %s", buf + 4);
2669 return CURLE_FTP_COULDNT_STOR_FILE;
2672 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2676 if (ftpcode != 150) {
2677 failf(data, "Failed FTP upload: %s", buf + 4);
2678 return CURLE_FTP_COULDNT_STOR_FILE;
2687 /***********************************************************************
2689 * ftp_parse_url_path()
2691 * Parse the URL path into separate path components.
2695 CURLcode ftp_parse_url_path(struct connectdata *conn)
2697 CURLcode retcode = CURLE_OK;
2698 struct SessionHandle *data = conn->data;
2701 char *slash_pos; /* position of the first '/' char in curpos */
2702 char *cur_pos = conn->path; /* current position in path. point at the begin
2703 of next path component */
2705 /* the ftp struct is already inited in ftp_connect() */
2706 ftp = conn->proto.ftp;
2707 ftp->ctl_valid = FALSE;
2710 ftp->diralloc = 5; /* default dir depth to allocate */
2711 ftp->dirs = (char **)malloc(ftp->diralloc * sizeof(ftp->dirs[0]));
2713 return CURLE_OUT_OF_MEMORY;
2714 ftp->dirs[0] = NULL; /* to start with */
2716 /* parse the URL path into separate path components */
2717 while((slash_pos=strchr(cur_pos, '/'))) {
2718 /* 1 or 0 to indicate absolute directory */
2719 bool absolute_dir = (cur_pos - conn->path > 0) && (ftp->dirdepth == 0);
2721 /* seek out the next path component */
2722 if (slash_pos-cur_pos) {
2723 /* we skip empty path components, like "x//y" since the FTP command CWD
2724 requires a parameter and a non-existant parameter a) doesn't work on
2725 many servers and b) has no effect on the others. */
2726 int len = (int)(slash_pos - cur_pos + absolute_dir);
2727 ftp->dirs[ftp->dirdepth] = curl_unescape(cur_pos - absolute_dir, len);
2729 if (!ftp->dirs[ftp->dirdepth]) { /* run out of memory ... */
2730 failf(data, "no memory");
2732 return CURLE_OUT_OF_MEMORY;
2736 cur_pos = slash_pos + 1; /* jump to the rest of the string */
2741 cur_pos = slash_pos + 1; /* jump to the rest of the string */
2742 if(++ftp->dirdepth >= ftp->diralloc) {
2745 ftp->diralloc *= 2; /* double the size each time */
2746 bigger = realloc(ftp->dirs, ftp->diralloc * sizeof(ftp->dirs[0]));
2749 return CURLE_OUT_OF_MEMORY;
2751 ftp->dirs = (char **)bigger;
2756 ftp->file = cur_pos; /* the rest is the file name */
2759 ftp->file = curl_unescape(ftp->file, 0);
2760 if(NULL == ftp->file) {
2762 failf(data, "no memory");
2763 return CURLE_OUT_OF_MEMORY;
2767 ftp->file=NULL; /* instead of point to a zero byte, we make it a NULL
2770 ftp->cwddone = FALSE; /* default to not done */
2772 size_t dlen = conn->path?strlen(conn->path):0;
2773 if(dlen && ftp->prevpath) {
2774 dlen -= ftp->file?strlen(ftp->file):0;
2775 if((dlen == strlen(ftp->prevpath)) &&
2776 curl_strnequal(conn->path, ftp->prevpath, dlen)) {
2777 infof(data, "Request has same path as previous transfer\n");
2778 ftp->cwddone = TRUE;
2788 /***********************************************************************
2790 * ftp_cwd_and_create_path()
2792 * Creates full path on remote target host.
2796 CURLcode ftp_cwd_and_create_path(struct connectdata *conn)
2798 CURLcode result = CURLE_OK;
2799 /* the ftp struct is already inited in Curl_ftp_connect() */
2800 struct FTP *ftp = conn->proto.ftp;
2804 /* already done and fine */
2807 /* This is a re-used connection. Since we change directory to where the
2808 transfer is taking place, we must now get back to the original dir
2809 where we ended up after login: */
2810 if (conn->bits.reuse && ftp->entrypath) {
2811 if ((result = ftp_cwd_and_mkd(conn, ftp->entrypath)) != CURLE_OK)
2815 for (i=0; i < ftp->dirdepth; i++) {
2816 /* RFC 1738 says empty components should be respected too, but
2817 that is plain stupid since CWD can't be used with an empty argument */
2818 if ((result = ftp_cwd_and_mkd(conn, ftp->dirs[i])) != CURLE_OK)
2826 /***********************************************************************
2828 * ftp_regular_transfer()
2830 * The input argument is already checked for validity.
2831 * Performs a regular transfer between local and remote hosts.
2833 * ftp->ctl_valid starts out as FALSE, and gets set to TRUE if we reach the
2834 * Curl_ftp_done() function without finding any major problem.
2837 CURLcode ftp_regular_transfer(struct connectdata *conn)
2839 CURLcode retcode=CURLE_OK;
2841 struct SessionHandle *data = conn->data;
2844 /* the ftp struct is already inited in ftp_connect() */
2845 ftp = conn->proto.ftp;
2846 conn->size = -1; /* make sure this is unknown at this point */
2848 Curl_pgrsSetUploadCounter(data, 0);
2849 Curl_pgrsSetDownloadCounter(data, 0);
2850 Curl_pgrsSetUploadSize(data, 0);
2851 Curl_pgrsSetDownloadSize(data, 0);
2853 retcode = ftp_perform(conn, &connected);
2855 if(CURLE_OK == retcode) {
2857 retcode = Curl_ftp_nextconnect(conn);
2859 if(retcode && (conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD)) {
2860 /* Failure detected, close the second socket if it was created already */
2861 sclose(conn->sock[SECONDARYSOCKET]);
2862 conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
2865 if(ftp->no_transfer)
2866 /* no data to transfer */
2867 retcode=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
2869 /* since we didn't connect now, we want do_more to get called */
2870 conn->bits.do_more = TRUE;
2875 ftp->ctl_valid = TRUE; /* seems good */
2882 /***********************************************************************
2886 * The input argument is already checked for validity.
2887 * Performs a 3rd party transfer between two remote hosts.
2889 static CURLcode ftp_3rdparty(struct connectdata *conn)
2891 CURLcode retcode = CURLE_OK;
2893 conn->proto.ftp->ctl_valid = conn->sec_conn->proto.ftp->ctl_valid = TRUE;
2894 conn->size = conn->sec_conn->size = -1;
2896 retcode = ftp_3rdparty_pretransfer(conn);
2898 retcode = ftp_3rdparty_transfer(conn);
2903 #endif /* CURL_DISABLE_FTP */