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
37 #ifdef HAVE_SYS_SELECT_H
38 #include <sys/select.h>
41 #if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
43 #else /* some kind of unix */
44 #ifdef HAVE_SYS_SOCKET_H
45 #include <sys/socket.h>
47 #include <sys/types.h>
48 #ifdef HAVE_NETINET_IN_H
49 #include <netinet/in.h>
51 #ifdef HAVE_ARPA_INET_H
52 #include <arpa/inet.h>
54 #include <sys/utsname.h>
64 #if defined(WIN32) && defined(__GNUC__) || defined(__MINGW32__)
68 #include <curl/curl.h>
77 #include "http.h" /* for HTTP proxy tunnel stuff */
85 #include "strtoofft.h"
90 #if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
91 #include "inet_ntoa_r.h"
94 #define _MPRINTF_REPLACE /* use our functions only */
95 #include <curl/mprintf.h>
97 /* The last #include file should be: */
102 /* Local API functions */
103 static CURLcode ftp_sendquote(struct connectdata *conn,
104 struct curl_slist *quote);
105 static CURLcode ftp_cwd(struct connectdata *conn, char *path);
106 static CURLcode ftp_mkd(struct connectdata *conn, char *path);
107 static CURLcode ftp_cwd_and_mkd(struct connectdata *conn, char *path);
109 /* easy-to-use macro: */
110 #define FTPSENDF(x,y,z) if((result = Curl_ftpsendf(x,y,z))) return result
112 static void freedirs(struct FTP *ftp)
115 for (i=0; ftp->dirs[i]; i++){
121 /***********************************************************************
123 * AllowServerConnect()
125 * When we've issue the PORT command, we have told the server to connect
126 * to us. This function will sit and wait here until the server has
130 static CURLcode AllowServerConnect(struct connectdata *conn)
134 struct SessionHandle *data = conn->data;
135 int sock = conn->sock[SECONDARYSOCKET];
139 FD_SET(sock, &rdset);
141 /* we give the server 10 seconds to connect to us */
145 switch (select(sock+1, &rdset, NULL, NULL, &dt)) {
148 failf(data, "Error while waiting for server connect");
149 return CURLE_FTP_PORT_FAILED;
150 case 0: /* timeout */
152 failf(data, "Timeout while waiting for server connect");
153 return CURLE_FTP_PORT_FAILED;
155 /* we have received data here */
158 size_t size = sizeof(struct sockaddr_in);
159 struct sockaddr_in add;
161 getsockname(sock, (struct sockaddr *) &add, (socklen_t *)&size);
162 s=accept(sock, (struct sockaddr *) &add, (socklen_t *)&size);
164 sclose(sock); /* close the first socket */
168 failf(data, "Error accept()ing server connect");
169 return CURLE_FTP_PORT_FAILED;
171 infof(data, "Connection accepted from server\n");
173 conn->sock[SECONDARYSOCKET] = s;
174 Curl_nonblock(s, TRUE); /* enable non-blocking */
183 /* --- parse FTP server responses --- */
186 * Curl_GetFTPResponse() is supposed to be invoked after each command sent to
187 * a remote FTP server. This function will wait and read all lines of the
188 * response and extract the relevant return code for the invoking function.
191 CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */
192 struct connectdata *conn,
193 int *ftpcode) /* return the ftp-code */
195 /* Brand new implementation.
196 * We cannot read just one byte per read() and then go back to select()
197 * as it seems that the OpenSSL read() stuff doesn't grok that properly.
199 * Alas, read as much as possible, split up into lines, use the ending
200 * line in a response or continue reading. */
202 int sockfd = conn->sock[FIRSTSOCKET];
203 int perline; /* count bytes per line */
207 int timeout; /* timeout in seconds */
208 struct timeval interval;
211 struct SessionHandle *data = conn->data;
213 int code=0; /* default ftp "error code" to return */
214 char *buf = data->state.buffer;
215 CURLcode result = CURLE_OK;
216 struct FTP *ftp = conn->proto.ftp;
217 struct timeval now = Curl_tvnow();
220 *ftpcode = 0; /* 0 for errors */
222 FD_ZERO (&readfd); /* clear it */
223 FD_SET (sockfd, &readfd); /* read socket */
225 /* get this in a backup variable to be able to restore it on each lap in the
236 while((*nreadp<BUFSIZE) && (keepon && !result)) {
237 /* check and reset timeout value every lap */
238 if(data->set.ftp_response_timeout )
239 /* if CURLOPT_FTP_RESPONSE_TIMEOUT is set, use that to determine
240 remaining time. Also, use "now" as opposed to "conn->now"
241 because ftp_response_timeout is only supposed to govern
242 the response for any given ftp response, not for the time
243 from connect to the given ftp response. */
244 timeout = data->set.ftp_response_timeout - /* timeout time */
245 Curl_tvdiff(Curl_tvnow(), now)/1000; /* spent time */
246 else if(data->set.timeout)
247 /* if timeout is requested, find out how much remaining time we have */
248 timeout = data->set.timeout - /* timeout time */
249 Curl_tvdiff(Curl_tvnow(), conn->now)/1000; /* spent time */
251 /* Even without a requested timeout, we only wait response_time
252 seconds for the full response to arrive before we bail out */
253 timeout = ftp->response_time -
254 Curl_tvdiff(Curl_tvnow(), now)/1000; /* spent time */
257 failf(data, "Transfer aborted due to timeout");
258 return CURLE_OPERATION_TIMEDOUT; /* already too little time */
262 readfd = rkeepfd; /* set every lap */
263 interval.tv_sec = 1; /* use 1 second timeout intervals */
264 interval.tv_usec = 0;
266 switch (select (sockfd+1, &readfd, NULL, NULL, &interval)) {
267 case -1: /* select() error, stop reading */
268 result = CURLE_RECV_ERROR;
269 failf(data, "Transfer aborted due to select() error: %d", errno);
271 case 0: /* timeout */
272 if(Curl_pgrsUpdate(conn))
273 return CURLE_ABORTED_BY_CALLBACK;
274 continue; /* just continue in our loop for the timeout duration */
280 if(CURLE_OK == result) {
282 * This code previously didn't use the kerberos sec_read() code
283 * to read, but when we use Curl_read() it may do so. Do confirm
284 * that this is still ok and then remove this comment!
287 /* we had data in the "cache", copy that instead of doing an actual
290 * Dave Meyer, December 2003:
291 * ftp->cache_size is cast to int here. This should be safe,
292 * because it would have been populated with something of size
293 * int to begin with, even though its datatype may be larger
296 memcpy(ptr, ftp->cache, (int)ftp->cache_size);
297 gotbytes = (int)ftp->cache_size;
298 free(ftp->cache); /* free the cache */
299 ftp->cache = NULL; /* clear the pointer */
300 ftp->cache_size = 0; /* zero the size just in case */
303 int res = Curl_read(conn, sockfd, ptr, BUFSIZE-*nreadp, &gotbytes);
306 continue; /* go looping again */
314 else if(gotbytes <= 0) {
316 result = CURLE_RECV_ERROR;
317 failf(data, "FTP response reading failed");
320 /* we got a whole chunk of data, which can be anything from one
321 * byte to a set of lines and possible just a piece of the last
326 for(i = 0; i < gotbytes; ptr++, i++) {
329 /* a newline is CRLF in ftp-talk, so the CR is ignored as
330 the line isn't really terminated until the LF comes */
332 /* output debug output if that is requested */
333 if(data->set.verbose)
334 Curl_debug(data, CURLINFO_HEADER_IN, line_start, perline);
337 * We pass all response-lines to the callback function registered
338 * for "headers". The response lines can be seen as a kind of
341 result = Curl_client_write(data, CLIENTWRITE_HEADER,
342 line_start, perline);
346 #define lastline(line) (isdigit((int)line[0]) && isdigit((int)line[1]) && \
347 isdigit((int)line[2]) && (' ' == line[3]))
349 if(perline>3 && lastline(line_start)) {
350 /* This is the end of the last line, copy the last
351 * line to the start of the buffer and zero terminate,
352 * for old times sake (and krb4)! */
355 for(meow=line_start, n=0; meow<ptr; meow++, n++)
357 *meow=0; /* zero terminate */
359 line_start = ptr+1; /* advance pointer */
360 i++; /* skip this before getting out */
363 perline=0; /* line starts over here */
367 if(!keepon && (i != gotbytes)) {
368 /* We found the end of the response lines, but we didn't parse the
369 full chunk of data we have read from the server. We therefore
370 need to store the rest of the data to be checked on the next
371 invoke as it may actually contain another end of response
372 already! Cleverly figured out by Eric Lavigne in December
374 ftp->cache_size = gotbytes - i;
375 ftp->cache = (char *)malloc((int)ftp->cache_size);
377 memcpy(ftp->cache, line_start, (int)ftp->cache_size);
379 return CURLE_OUT_OF_MEMORY; /**BANG**/
381 } /* there was data */
383 } /* while there's buffer left and loop is requested */
389 /* handle the security-oriented responses 6xx ***/
390 /* FIXME: some errorchecking perhaps... ***/
393 Curl_sec_read_msg(conn, buf, prot_safe);
396 Curl_sec_read_msg(conn, buf, prot_private);
399 Curl_sec_read_msg(conn, buf, prot_confidential);
402 /* normal ftp stuff we pass through! */
408 *ftpcode=code; /* return the initial number like this */
410 /* store the latest code for later retrieval */
411 conn->data->info.httpcode=code;
416 static const char *ftpauth[]= {
421 * Curl_ftp_connect() should do everything that is to be considered a part of
422 * the connection phase.
424 CURLcode Curl_ftp_connect(struct connectdata *conn)
426 /* this is FTP and no proxy */
428 struct SessionHandle *data=conn->data;
429 char *buf = data->state.buffer; /* this is our buffer */
434 ftp = (struct FTP *)malloc(sizeof(struct FTP));
436 return CURLE_OUT_OF_MEMORY;
438 memset(ftp, 0, sizeof(struct FTP));
439 conn->proto.ftp = ftp;
441 /* We always support persistant connections on ftp */
442 conn->bits.close = FALSE;
444 /* get some initial data into the ftp struct */
445 ftp->bytecountp = &conn->bytecount;
447 /* no need to duplicate them, this connectdata struct won't change */
448 ftp->user = conn->user;
449 ftp->passwd = conn->passwd;
450 ftp->response_time = 3600; /* set default response time-out */
452 if (data->set.tunnel_thru_httpproxy) {
453 /* We want "seamless" FTP operations through HTTP proxy tunnel */
454 result = Curl_ConnectHTTPProxyTunnel(conn, FIRSTSOCKET,
455 conn->hostname, conn->remote_port);
456 if(CURLE_OK != result)
460 if(conn->protocol & PROT_FTPS) {
461 /* FTPS is simply ftp with SSL for the control channel */
462 /* now, perform the SSL initialization for this socket */
463 result = Curl_SSLConnect(conn, FIRSTSOCKET);
468 /* The first thing we do is wait for the "220*" line: */
469 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
474 failf(data, "This doesn't seem like a nice ftp-server response");
475 return CURLE_FTP_WEIRD_SERVER_REPLY;
479 /* if not anonymous login, try a secure login */
482 /* request data protection level (default is 'clear') */
483 Curl_sec_request_prot(conn, "private");
485 /* We set private first as default, in case the line below fails to
487 Curl_sec_request_prot(conn, data->set.krb4_level);
489 if(Curl_sec_login(conn) != 0)
490 infof(data, "Logging in with password in cleartext!\n");
492 infof(data, "Authentication successful\n");
496 if(data->set.ftp_ssl && !conn->ssl[FIRSTSOCKET].use) {
497 /* we don't have a SSL/TLS connection, try a FTPS connection now */
499 for (try = 0; ftpauth[try]; try++) {
501 FTPSENDF(conn, "AUTH %s", ftpauth[try]);
503 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
508 /* RFC2228 (page 5) says:
510 * If the server is willing to accept the named security mechanism, and
511 * does not require any security data, it must respond with reply code
515 if((ftpcode == 234) || (ftpcode == 334)) {
516 result = Curl_SSLConnect(conn, FIRSTSOCKET);
519 conn->protocol |= PROT_FTPS;
520 conn->ssl[SECONDARYSOCKET].use = FALSE; /* clear-text data */
527 FTPSENDF(conn, "USER %s", ftp->user?ftp->user:"");
529 /* wait for feedback */
530 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
535 /* 530 User ... access denied
536 (the server denies to log the specified user) */
537 failf(data, "Access denied: %s", &buf[4]);
538 return CURLE_FTP_ACCESS_DENIED;
540 else if(ftpcode == 331) {
541 /* 331 Password required for ...
542 (the server requires to send the user's password too) */
543 FTPSENDF(conn, "PASS %s", ftp->passwd?ftp->passwd:"");
544 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
549 /* 530 Login incorrect.
550 (the username and/or the password are incorrect) */
551 failf(data, "the username and/or the password are incorrect");
552 return CURLE_FTP_USER_PASSWORD_INCORRECT;
554 else if(ftpcode == 230) {
555 /* 230 User ... logged in.
556 (user successfully logged in) */
558 infof(data, "We have successfully logged in\n");
561 failf(data, "Odd return code after PASS");
562 return CURLE_FTP_WEIRD_PASS_REPLY;
565 else if(buf[0] == '2') {
566 /* 230 User ... logged in.
567 (the user logged in without password) */
568 infof(data, "We have successfully logged in\n");
569 if (conn->ssl[FIRSTSOCKET].use) {
571 /* we are logged in (with Kerberos)
572 * now set the requested protection level
574 if(conn->sec_complete)
575 Curl_sec_set_protection_level(conn);
577 /* we may need to issue a KAUTH here to have access to the files
578 * do it if user supplied a password
580 if(conn->passwd && *conn->passwd) {
581 result = Curl_krb_kauth(conn);
589 failf(data, "Odd return code after USER");
590 return CURLE_FTP_WEIRD_USER_REPLY;
593 if(conn->ssl[FIRSTSOCKET].use) {
594 /* PBSZ = PROTECTION BUFFER SIZE.
596 The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says:
598 Specifically, the PROT command MUST be preceded by a PBSZ command
599 and a PBSZ command MUST be preceded by a successful security data
600 exchange (the TLS negotiation in this case)
604 Thus the PBSZ command must still be issued, but must have a parameter
605 of '0' to indicate that no buffering is taking place and the data
606 connection should not be encapsulated.
608 FTPSENDF(conn, "PBSZ %d", 0);
609 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
613 /* For TLS, the data connection can have one of two security levels.
615 1)Clear (requested by 'PROT C')
617 2)Private (requested by 'PROT P')
619 if(!conn->ssl[SECONDARYSOCKET].use) {
620 FTPSENDF(conn, "PROT %c", 'P');
621 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
626 /* We have enabled SSL for the data connection! */
627 conn->ssl[SECONDARYSOCKET].use = TRUE;
629 /* FTP servers typically responds with 500 if they decide to reject
634 /* send PWD to discover our entry point */
635 FTPSENDF(conn, "PWD", NULL);
637 /* wait for feedback */
638 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
643 char *dir = (char *)malloc(nread+1);
645 char *ptr=&buf[4]; /* start on the first letter */
648 return CURLE_OUT_OF_MEMORY;
650 /* Reply format is like
651 257<space>"<directory-name>"<space><commentary> and the RFC959 says
653 The directory name can contain any character; embedded double-quotes
654 should be escaped by double-quotes (the "quote-doubling" convention).
657 /* it started good */
662 /* "quote-doubling" */
668 *store = '\0'; /* zero terminate */
669 break; /* get out of this loop */
677 ftp->entrypath =dir; /* remember this */
678 infof(data, "Entry path is '%s'\n", ftp->entrypath);
681 /* couldn't get the path */
683 infof(data, "Failed to figure out path\n");
688 /* We couldn't read the PWD response! */
694 /***********************************************************************
698 * The DONE function. This does what needs to be done after a single DO has
701 * Input argument is already checked for validity.
703 CURLcode Curl_ftp_done(struct connectdata *conn)
705 struct SessionHandle *data = conn->data;
706 struct FTP *ftp = conn->proto.ftp;
709 CURLcode result=CURLE_OK;
711 /* free the dir tree parts */
719 if(data->set.upload) {
720 if((-1 != data->set.infilesize) &&
721 (data->set.infilesize != *ftp->bytecountp) &&
723 failf(data, "Uploaded unaligned file size (%Od out of %Od bytes)",
724 *ftp->bytecountp, data->set.infilesize);
725 conn->bits.close = TRUE; /* close this connection since we don't
726 know what state this error leaves us in */
727 return CURLE_PARTIAL_FILE;
731 if((-1 != conn->size) && (conn->size != *ftp->bytecountp) &&
732 (conn->maxdownload != *ftp->bytecountp)) {
733 failf(data, "Received only partial file: %Od bytes",
735 conn->bits.close = TRUE; /* close this connection since we don't
736 know what state this error leaves us in */
737 return CURLE_PARTIAL_FILE;
739 else if(!ftp->dont_check &&
742 /* We consider this an error, but there's no true FTP error received
743 why we need to continue to "read out" the server response too.
744 We don't want to leave a "waiting" server reply if we'll get told
745 to make a second request on this same connection! */
746 failf(data, "No data was received!");
747 result = CURLE_FTP_COULDNT_RETR_FILE;
752 Curl_sec_fflush_fd(conn, conn->sock[SECONDARYSOCKET]);
754 /* shut down the socket to inform the server we're done */
755 sclose(conn->sock[SECONDARYSOCKET]);
756 conn->sock[SECONDARYSOCKET] = -1;
758 if(!ftp->no_transfer) {
759 /* Let's see what the server says about the transfer we just performed,
760 but lower the timeout as sometimes this connection has died while
761 the data has been transfered. This happens when doing through NATs
762 etc that abandon old silent connections.
764 ftp->response_time = 60; /* give it only a minute for now */
766 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
768 ftp->response_time = 3600; /* set this back to one hour waits */
770 if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) {
771 failf(data, "control connection looks dead");
778 if(!ftp->dont_check) {
779 /* 226 Transfer complete, 250 Requested file action okay, completed. */
780 if((ftpcode != 226) && (ftpcode != 250)) {
781 failf(data, "server did not report OK, got %d", ftpcode);
782 return CURLE_FTP_WRITE_ERROR;
787 /* clear these for next connection */
788 ftp->no_transfer = FALSE;
789 ftp->dont_check = FALSE;
791 /* Send any post-transfer QUOTE strings? */
792 if(!result && data->set.postquote)
793 result = ftp_sendquote(conn, data->set.postquote);
798 /***********************************************************************
802 * Where a 'quote' means a list of custom commands to send to the server.
803 * The quote list is passed as an argument.
807 CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote)
809 struct curl_slist *item;
817 FTPSENDF(conn, "%s", item->data);
819 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
823 if (ftpcode >= 400) {
824 failf(conn->data, "QUOT string not accepted: %s", item->data);
825 return CURLE_FTP_QUOTE_ERROR;
835 /***********************************************************************
839 * Get the timestamp of the given file.
842 CURLcode ftp_getfiletime(struct connectdata *conn, char *file)
844 CURLcode result=CURLE_OK;
845 int ftpcode; /* for ftp status */
847 char *buf = conn->data->state.buffer;
849 /* we have requested to get the modified-time of the file, this is yet
850 again a grey area as the MDTM is not kosher RFC959 */
851 FTPSENDF(conn, "MDTM %s", file);
853 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
860 /* we got a time. Format should be: "YYYYMMDDHHMMSS[.sss]" where the
861 last .sss part is optional and means fractions of a second */
862 int year, month, day, hour, minute, second;
863 if(6 == sscanf(buf+4, "%04d%02d%02d%02d%02d%02d",
864 &year, &month, &day, &hour, &minute, &second)) {
865 /* we have a time, reformat it */
866 time_t secs=time(NULL);
867 sprintf(buf, "%04d%02d%02d %02d:%02d:%02d GMT",
868 year, month, day, hour, minute, second);
869 /* now, convert this into a time() value: */
870 conn->data->info.filetime = curl_getdate(buf, &secs);
875 infof(conn->data, "unsupported MDTM reply format\n");
877 case 550: /* "No such file or directory" */
878 failf(conn->data, "Given file does not exist");
879 result = CURLE_FTP_COULDNT_RETR_FILE;
885 /***********************************************************************
889 * Set transfer type. We only deal with ASCII or BINARY so this function
892 static CURLcode ftp_transfertype(struct connectdata *conn,
895 struct SessionHandle *data = conn->data;
900 FTPSENDF(conn, "TYPE %s", ascii?"A":"I");
902 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
907 failf(data, "Couldn't set %s mode",
908 ascii?"ASCII":"binary");
909 return ascii? CURLE_FTP_COULDNT_SET_ASCII:CURLE_FTP_COULDNT_SET_BINARY;
915 /***********************************************************************
919 * Returns the file size (in bytes) of the given remote file.
923 CURLcode ftp_getsize(struct connectdata *conn, char *file,
926 struct SessionHandle *data = conn->data;
929 char *buf=data->state.buffer;
932 FTPSENDF(conn, "SIZE %s", file);
933 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
938 /* get the size from the ascii string: */
939 *size = strtoofft(buf+4, NULL, 0);
942 return CURLE_FTP_COULDNT_GET_SIZE;
947 /***************************************************************************
951 * This function only outputs some informationals about this second connection
952 * when we've issued a PASV command before and thus we have connected to a
953 * possibly new IP address.
957 ftp_pasv_verbose(struct connectdata *conn,
958 Curl_ipconnect *addr,
959 char *newhost, /* ascii version */
963 /*****************************************************************
965 * IPv4-only code section
969 struct hostent * answer;
971 #ifdef HAVE_INET_NTOA_R
974 /* The array size trick below is to make this a large chunk of memory
975 suitably 8-byte aligned on 64-bit platforms. This was thoughtfully
976 suggested by Philip Gladstone. */
977 long bigbuf[9000 / sizeof(long)];
979 #if defined(HAVE_INET_ADDR)
981 # if defined(HAVE_GETHOSTBYADDR_R)
984 char *hostent_buf = (char *)bigbuf; /* get a char * to the buffer */
986 address = inet_addr(newhost);
987 # ifdef HAVE_GETHOSTBYADDR_R
989 # ifdef HAVE_GETHOSTBYADDR_R_5
990 /* AIX, Digital Unix (OSF1, Tru64) style:
991 extern int gethostbyaddr_r(char *addr, size_t len, int type,
992 struct hostent *htent, struct hostent_data *ht_data); */
994 /* Fred Noz helped me try this out, now it at least compiles! */
996 /* Bjorn Reese (November 28 2001):
997 The Tru64 man page on gethostbyaddr_r() says that
998 the hostent struct must be filled with zeroes before the call to
1001 ... as must be struct hostent_data Craig Markwardt 19 Sep 2002. */
1003 memset(hostent_buf, 0, sizeof(struct hostent)+sizeof(struct hostent_data));
1005 if(gethostbyaddr_r((char *) &address,
1006 sizeof(address), AF_INET,
1007 (struct hostent *)hostent_buf,
1008 (struct hostent_data *)(hostent_buf + sizeof(*answer))))
1011 answer=(struct hostent *)hostent_buf;
1014 # ifdef HAVE_GETHOSTBYADDR_R_7
1015 /* Solaris and IRIX */
1016 answer = gethostbyaddr_r((char *) &address, sizeof(address), AF_INET,
1017 (struct hostent *)bigbuf,
1018 hostent_buf + sizeof(*answer),
1019 sizeof(bigbuf) - sizeof(*answer),
1022 # ifdef HAVE_GETHOSTBYADDR_R_8
1024 if(gethostbyaddr_r((char *) &address, sizeof(address), AF_INET,
1025 (struct hostent *)hostent_buf,
1026 hostent_buf + sizeof(*answer),
1027 sizeof(bigbuf) - sizeof(*answer),
1030 answer=NULL; /* error */
1034 (void)hostent_buf; /* avoid compiler warning */
1035 answer = gethostbyaddr((char *) &address, sizeof(address), AF_INET);
1040 (void) memcpy(&in.s_addr, addr, sizeof (Curl_ipconnect));
1041 infof(conn->data, "Connecting to %s (%s) port %u\n",
1042 answer?answer->h_name:newhost,
1043 #if defined(HAVE_INET_NTOA_R)
1044 inet_ntoa_r(in, ntoa_buf, sizeof(ntoa_buf)),
1051 /*****************************************************************
1053 * IPv6-only code section
1055 char hbuf[NI_MAXHOST]; /* ~1KB */
1056 char nbuf[NI_MAXHOST]; /* ~1KB */
1057 char sbuf[NI_MAXSERV]; /* around 32 */
1058 #ifdef NI_WITHSCOPEID
1059 const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID;
1061 const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
1063 (void)port; /* prevent compiler warning */
1064 if (getnameinfo(addr->ai_addr, addr->ai_addrlen,
1065 nbuf, sizeof(nbuf), sbuf, sizeof(sbuf), niflags)) {
1066 snprintf(nbuf, sizeof(nbuf), "?");
1067 snprintf(sbuf, sizeof(sbuf), "?");
1070 if (getnameinfo(addr->ai_addr, addr->ai_addrlen,
1071 hbuf, sizeof(hbuf), NULL, 0, 0)) {
1072 infof(conn->data, "Connecting to %s (%s) port %s\n", nbuf, newhost, sbuf);
1075 infof(conn->data, "Connecting to %s (%s) port %s\n", hbuf, nbuf, sbuf);
1080 /***********************************************************************
1084 * Send the proper PORT command. PORT is the ftp client's way of telling the
1085 * server that *WE* open a port that we listen on an awaits the server to
1086 * connect to. This is the opposite of PASV.
1090 CURLcode ftp_use_port(struct connectdata *conn)
1092 struct SessionHandle *data=conn->data;
1095 int ftpcode; /* receive FTP response codes in this */
1099 /******************************************************************
1101 * Here's a piece of IPv6-specific code coming up
1105 struct addrinfo hints, *res, *ai;
1106 struct sockaddr_storage ss;
1108 char hbuf[NI_MAXHOST];
1110 struct sockaddr *sa=(struct sockaddr *)&ss;
1111 #ifdef NI_WITHSCOPEID
1112 const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID;
1114 const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
1118 char portmsgbuf[4096], tmp[4096];
1120 const char *mode[] = { "EPRT", "LPRT", "PORT", NULL };
1124 * we should use Curl_if2ip? given pickiness of recent ftpd,
1125 * I believe we should use the same address as the control connection.
1128 if (getsockname(conn->sock[FIRSTSOCKET], (struct sockaddr *)&ss, &sslen) < 0)
1129 return CURLE_FTP_PORT_FAILED;
1131 if (getnameinfo((struct sockaddr *)&ss, sslen, hbuf, sizeof(hbuf), NULL, 0,
1133 return CURLE_FTP_PORT_FAILED;
1135 memset(&hints, 0, sizeof(hints));
1136 hints.ai_family = sa->sa_family;
1137 /*hints.ai_family = ss.ss_family;
1138 this way can be used if sockaddr_storage is properly defined, as glibc
1140 hints.ai_socktype = SOCK_STREAM;
1141 hints.ai_flags = AI_PASSIVE;
1143 if (getaddrinfo(hbuf, (char *)"0", &hints, &res))
1144 return CURLE_FTP_PORT_FAILED;
1147 for (ai = res; ai; ai = ai->ai_next) {
1148 portsock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
1152 if (bind(portsock, ai->ai_addr, ai->ai_addrlen) < 0) {
1158 if (listen(portsock, 1) < 0) {
1168 failf(data, "%s", strerror(errno));
1169 return CURLE_FTP_PORT_FAILED;
1173 if (getsockname(portsock, sa, &sslen) < 0) {
1174 failf(data, "%s", strerror(errno));
1175 return CURLE_FTP_PORT_FAILED;
1178 for (modep = (char **)(data->set.ftp_use_eprt?&mode[0]:&mode[2]);
1179 modep && *modep; modep++) {
1183 switch (sa->sa_family) {
1185 ap = (unsigned char *)&((struct sockaddr_in *)&ss)->sin_addr;
1186 alen = sizeof(((struct sockaddr_in *)&ss)->sin_addr);
1187 pp = (unsigned char *)&((struct sockaddr_in *)&ss)->sin_port;
1188 plen = sizeof(((struct sockaddr_in *)&ss)->sin_port);
1193 ap = (unsigned char *)&((struct sockaddr_in6 *)&ss)->sin6_addr;
1194 alen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_addr);
1195 pp = (unsigned char *)&((struct sockaddr_in6 *)&ss)->sin6_port;
1196 plen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_port);
1202 lprtaf = eprtaf = -1;
1206 if (strcmp(*modep, "EPRT") == 0) {
1209 if (getnameinfo((struct sockaddr *)&ss, sslen,
1210 portmsgbuf, sizeof(portmsgbuf), tmp, sizeof(tmp), niflags))
1213 /* do not transmit IPv6 scope identifier to the wire */
1214 if (sa->sa_family == AF_INET6) {
1215 char *q = strchr(portmsgbuf, '%');
1220 result = Curl_ftpsendf(conn, "%s |%d|%s|%s|", *modep, eprtaf,
1224 } else if (strcmp(*modep, "LPRT") == 0 ||
1225 strcmp(*modep, "PORT") == 0) {
1228 if (strcmp(*modep, "LPRT") == 0 && lprtaf < 0)
1230 if (strcmp(*modep, "PORT") == 0 && sa->sa_family != AF_INET)
1233 portmsgbuf[0] = '\0';
1234 if (strcmp(*modep, "LPRT") == 0) {
1235 snprintf(tmp, sizeof(tmp), "%d,%d", lprtaf, alen);
1236 if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >=
1237 sizeof(portmsgbuf)) {
1242 for (i = 0; i < alen; i++) {
1244 snprintf(tmp, sizeof(tmp), ",%u", ap[i]);
1246 snprintf(tmp, sizeof(tmp), "%u", ap[i]);
1248 if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >=
1249 sizeof(portmsgbuf)) {
1254 if (strcmp(*modep, "LPRT") == 0) {
1255 snprintf(tmp, sizeof(tmp), ",%d", plen);
1257 if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= sizeof(portmsgbuf))
1261 for (i = 0; i < plen; i++) {
1262 snprintf(tmp, sizeof(tmp), ",%u", pp[i]);
1264 if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >=
1265 sizeof(portmsgbuf)) {
1270 result = Curl_ftpsendf(conn, "%s %s", *modep, portmsgbuf);
1275 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1279 if (ftpcode != 200) {
1288 failf(data, "PORT command attempts failed");
1289 return CURLE_FTP_PORT_FAILED;
1291 /* we set the secondary socket variable to this for now, it
1292 is only so that the cleanup function will close it in case
1293 we fail before the true secondary stuff is made */
1294 conn->sock[SECONDARYSOCKET] = portsock;
1297 /******************************************************************
1299 * Here's a piece of IPv4-specific code coming up
1302 struct sockaddr_in sa;
1303 struct Curl_dns_entry *h=NULL;
1304 unsigned short porttouse;
1305 char myhost[256] = "";
1306 bool sa_filled_in = FALSE;
1308 if(data->set.ftpport) {
1312 /* First check if the given name is an IP address */
1313 in=inet_addr(data->set.ftpport);
1315 if((in == CURL_INADDR_NONE) &&
1316 Curl_if2ip(data->set.ftpport, myhost, sizeof(myhost))) {
1317 rc = Curl_resolv(conn, myhost, 0, &h);
1319 rc = Curl_wait_for_resolv(conn, &h);
1322 int len = strlen(data->set.ftpport);
1324 rc = Curl_resolv(conn, data->set.ftpport, 0, &h);
1326 rc = Curl_wait_for_resolv(conn, &h);
1329 strcpy(myhost, data->set.ftpport); /* buffer overflow risk */
1333 /* pick a suitable default here */
1338 if (getsockname(conn->sock[FIRSTSOCKET],
1339 (struct sockaddr *)&sa, &sslen) < 0) {
1340 failf(data, "getsockname() failed");
1341 return CURLE_FTP_PORT_FAILED;
1344 sa_filled_in = TRUE; /* the sa struct is filled in */
1348 /* when we return from here, we can forget about this */
1349 Curl_resolv_unlock(data, h);
1351 if ( h || sa_filled_in) {
1352 if( (portsock = socket(AF_INET, SOCK_STREAM, 0)) >= 0 ) {
1355 /* we set the secondary socket variable to this for now, it
1356 is only so that the cleanup function will close it in case
1357 we fail before the true secondary stuff is made */
1358 conn->sock[SECONDARYSOCKET] = portsock;
1361 memset((char *)&sa, 0, sizeof(sa));
1362 memcpy((char *)&sa.sin_addr,
1365 sa.sin_family = AF_INET;
1366 sa.sin_addr.s_addr = INADDR_ANY;
1372 if(bind(portsock, (struct sockaddr *)&sa, size) >= 0) {
1373 /* we succeeded to bind */
1374 struct sockaddr_in add;
1375 socklen_t socksize = sizeof(add);
1377 if(getsockname(portsock, (struct sockaddr *) &add,
1379 failf(data, "getsockname() failed");
1380 return CURLE_FTP_PORT_FAILED;
1382 porttouse = ntohs(add.sin_port);
1384 if ( listen(portsock, 1) < 0 ) {
1385 failf(data, "listen(2) failed on socket");
1386 return CURLE_FTP_PORT_FAILED;
1390 failf(data, "bind(2) failed on socket");
1391 return CURLE_FTP_PORT_FAILED;
1395 failf(data, "socket(2) failed (%s)");
1396 return CURLE_FTP_PORT_FAILED;
1400 failf(data, "could't find my own IP address (%s)", myhost);
1401 return CURLE_FTP_PORT_FAILED;
1404 #ifdef HAVE_INET_NTOA_R
1408 unsigned short ip[5];
1409 (void) memcpy(&in.s_addr,
1410 h?*h->addr->h_addr_list:(char *)&sa.sin_addr.s_addr,
1411 sizeof (in.s_addr));
1413 #ifdef HAVE_INET_NTOA_R
1414 /* ignore the return code from inet_ntoa_r() as it is int or
1415 char * depending on system */
1416 inet_ntoa_r(in, ntoa_buf, sizeof(ntoa_buf));
1417 sscanf( ntoa_buf, "%hu.%hu.%hu.%hu",
1418 &ip[0], &ip[1], &ip[2], &ip[3]);
1420 sscanf( inet_ntoa(in), "%hu.%hu.%hu.%hu",
1421 &ip[0], &ip[1], &ip[2], &ip[3]);
1423 infof(data, "Telling server to connect to %d.%d.%d.%d:%d\n",
1424 ip[0], ip[1], ip[2], ip[3], porttouse);
1426 result=Curl_ftpsendf(conn, "PORT %d,%d,%d,%d,%d,%d",
1427 ip[0], ip[1], ip[2], ip[3],
1434 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1438 if(ftpcode != 200) {
1439 failf(data, "Server does not grok PORT, try without it!");
1440 return CURLE_FTP_PORT_FAILED;
1442 #endif /* end of ipv4-specific code */
1447 /***********************************************************************
1451 * Send the PASV command. PASV is the ftp client's way of asking the server to
1452 * open a second port that we can connect to (for the data transfer). This is
1453 * the opposite of PORT.
1457 CURLcode ftp_use_pasv(struct connectdata *conn,
1460 struct SessionHandle *data = conn->data;
1462 char *buf = data->state.buffer; /* this is our buffer */
1463 int ftpcode; /* receive FTP response codes in this */
1465 struct Curl_dns_entry *addr=NULL;
1466 Curl_ipconnect *conninfo;
1470 Here's the excecutive summary on what to do:
1472 PASV is RFC959, expect:
1473 227 Entering Passive Mode (a1,a2,a3,a4,p1,p2)
1475 LPSV is RFC1639, expect:
1476 228 Entering Long Passive Mode (4,4,a1,a2,a3,a4,2,p1,p2)
1478 EPSV is RFC2428, expect:
1479 229 Entering Extended Passive Mode (|||port|)
1483 const char *mode[] = { "EPSV", "PASV", NULL };
1484 int results[] = { 229, 227, 0 };
1486 unsigned short connectport; /* the local port connect() should use! */
1487 unsigned short newport=0; /* remote port, not necessary the local one */
1489 /* newhost must be able to hold a full IP-style address in ASCII, which
1490 in the IPv6 case means 5*8-1 = 39 letters */
1492 char *newhostp=NULL;
1494 for (modeoff = (data->set.ftp_use_epsv?0:1);
1495 mode[modeoff]; modeoff++) {
1496 result = Curl_ftpsendf(conn, "%s", mode[modeoff]);
1499 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1502 if (ftpcode == results[modeoff])
1506 if (!mode[modeoff]) {
1507 failf(data, "Odd return code after PASV");
1508 return CURLE_FTP_WEIRD_PASV_REPLY;
1510 else if (227 == results[modeoff]) {
1516 * New 227-parser June 3rd 1999.
1517 * It now scans for a sequence of six comma-separated numbers and
1518 * will take them as IP+port indicators.
1520 * Found reply-strings include:
1521 * "227 Entering Passive Mode (127,0,0,1,4,51)"
1522 * "227 Data transfer will passively listen to 127,0,0,1,4,51"
1523 * "227 Entering passive mode. 127,0,0,1,4,51"
1527 if (6 == sscanf(str, "%d,%d,%d,%d,%d,%d",
1528 &ip[0], &ip[1], &ip[2], &ip[3],
1529 &port[0], &port[1]))
1535 failf(data, "Couldn't interpret this 227-reply: %s", buf);
1536 return CURLE_FTP_WEIRD_227_FORMAT;
1539 sprintf(newhost, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
1541 newport = (port[0]<<8) + port[1];
1543 else if (229 == results[modeoff]) {
1544 char *ptr = strchr(buf, '(');
1549 if(5 == sscanf(ptr, "%c%c%c%u%c",
1555 char sep1 = separator[0];
1558 /* The four separators should be identical, or else this is an oddly
1559 formatted reply and we bail out immediately. */
1560 for(i=1; i<4; i++) {
1561 if(separator[i] != sep1) {
1562 ptr=NULL; /* set to NULL to signal error */
1569 /* we should use the same host we already are connected to */
1570 newhostp = conn->name;
1577 failf(data, "Weirdly formatted EPSV reply");
1578 return CURLE_FTP_WEIRD_PASV_REPLY;
1582 return CURLE_FTP_CANT_RECONNECT;
1584 if(data->change.proxy && *data->change.proxy) {
1586 * This is a tunnel through a http proxy and we need to connect to the
1589 * We don't want to rely on a former host lookup that might've expired
1590 * now, instead we remake the lookup here and now!
1592 rc = Curl_resolv(conn, conn->proxyhost, conn->port, &addr);
1594 rc = Curl_wait_for_resolv(conn, &addr);
1597 (unsigned short)conn->port; /* we connect to the proxy's port */
1601 /* normal, direct, ftp connection */
1602 rc = Curl_resolv(conn, newhostp, newport, &addr);
1604 rc = Curl_wait_for_resolv(conn, &addr);
1607 failf(data, "Can't resolve new host %s:%d", newhostp, newport);
1608 return CURLE_FTP_CANT_GET_HOST;
1610 connectport = newport; /* we connect to the remote port */
1613 result = Curl_connecthost(conn,
1616 &conn->sock[SECONDARYSOCKET],
1620 Curl_resolv_unlock(data, addr); /* we're done using this address */
1626 * When this is used from the multi interface, this might've returned with
1627 * the 'connected' set to FALSE and thus we are now awaiting a non-blocking
1628 * connect to connect and we should not be "hanging" here waiting.
1631 if(data->set.verbose)
1632 /* this just dumps information about this second connection */
1633 ftp_pasv_verbose(conn, conninfo, newhostp, connectport);
1635 if(data->set.tunnel_thru_httpproxy) {
1636 /* We want "seamless" FTP operations through HTTP proxy tunnel */
1637 result = Curl_ConnectHTTPProxyTunnel(conn, SECONDARYSOCKET,
1639 if(CURLE_OK != result)
1647 * Curl_ftp_nextconnect()
1649 * This function shall be called when the second FTP connection has been
1650 * established and is confirmed connected.
1653 CURLcode Curl_ftp_nextconnect(struct connectdata *conn)
1655 struct SessionHandle *data=conn->data;
1656 char *buf = data->state.buffer; /* this is our buffer */
1659 int ftpcode; /* for ftp status */
1661 /* the ftp struct is already inited in Curl_ftp_connect() */
1662 struct FTP *ftp = conn->proto.ftp;
1663 off_t *bytecountp = ftp->bytecountp;
1665 if(data->set.upload) {
1667 /* Set type to binary (unless specified ASCII) */
1668 result = ftp_transfertype(conn, data->set.ftp_ascii);
1672 /* Send any PREQUOTE strings after transfer type is set? (Wesley Laxton)*/
1673 if(data->set.prequote) {
1674 if ((result = ftp_sendquote(conn, data->set.prequote)) != CURLE_OK)
1678 if(conn->resume_from) {
1679 /* we're about to continue the uploading of a file */
1680 /* 1. get already existing file's size. We use the SIZE
1681 command for this which may not exist in the server!
1682 The SIZE command is not in RFC959. */
1684 /* 2. This used to set REST. But since we can do append, we
1685 don't another ftp command. We just skip the source file
1686 offset and then we APPEND the rest on the file instead */
1688 /* 3. pass file-size number of bytes in the source file */
1689 /* 4. lower the infilesize counter */
1690 /* => transfer as usual */
1692 if(conn->resume_from < 0 ) {
1693 /* we could've got a specified offset from the command line,
1694 but now we know we didn't */
1697 if(CURLE_OK != ftp_getsize(conn, ftp->file, &gottensize)) {
1698 failf(data, "Couldn't get remote file size");
1699 return CURLE_FTP_COULDNT_GET_SIZE;
1701 conn->resume_from = gottensize;
1704 if(conn->resume_from) {
1705 /* do we still game? */
1707 /* enable append instead */
1708 data->set.ftp_append = 1;
1710 /* Now, let's read off the proper amount of bytes from the
1711 input. If we knew it was a proper file we could've just
1712 fseek()ed but we only have a stream here */
1714 off_t readthisamountnow = (conn->resume_from - passed);
1717 if(readthisamountnow > BUFSIZE)
1718 readthisamountnow = BUFSIZE;
1721 conn->fread(data->state.buffer, 1, (size_t)readthisamountnow,
1724 passed += actuallyread;
1725 if(actuallyread != readthisamountnow) {
1726 failf(data, "Could only read %Od bytes from the input",
1728 return CURLE_FTP_COULDNT_USE_REST;
1731 while(passed != conn->resume_from);
1733 /* now, decrease the size of the read */
1734 if(data->set.infilesize>0) {
1735 data->set.infilesize -= conn->resume_from;
1737 if(data->set.infilesize <= 0) {
1738 infof(data, "File already completely uploaded\n");
1740 /* no data to transfer */
1741 result=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1743 /* Set no_transfer so that we won't get any error in
1744 * Curl_ftp_done() because we didn't transfer anything! */
1745 ftp->no_transfer = TRUE;
1750 /* we've passed, proceed as normal */
1754 /* Send everything on data->state.in to the socket */
1755 if(data->set.ftp_append) {
1756 /* we append onto the file instead of rewriting it */
1757 FTPSENDF(conn, "APPE %s", ftp->file);
1760 FTPSENDF(conn, "STOR %s", ftp->file);
1763 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1768 failf(data, "Failed FTP upload:%s", buf+3);
1769 /* oops, we never close the sockets! */
1770 return CURLE_FTP_COULDNT_STOR_FILE;
1773 if(data->set.ftp_use_port) {
1774 /* PORT means we are now awaiting the server to connect to us. */
1775 result = AllowServerConnect(conn);
1780 if(conn->ssl[SECONDARYSOCKET].use) {
1781 /* since we only have a plaintext TCP connection here, we must now
1783 infof(data, "Doing the SSL/TLS handshake on the data stream\n");
1784 result = Curl_SSLConnect(conn, SECONDARYSOCKET);
1791 /* When we know we're uploading a specified file, we can get the file
1792 size prior to the actual upload. */
1794 Curl_pgrsSetUploadSize(data, (double)data->set.infilesize);
1796 result = Curl_Transfer(conn, -1, -1, FALSE, NULL, /* no download */
1797 SECONDARYSOCKET, bytecountp);
1802 else if(!data->set.no_body) {
1803 /* Retrieve file or directory */
1805 off_t downloadsize=-1;
1807 if(conn->bits.use_range && conn->range) {
1813 from=strtoofft(conn->range, &ptr, 0);
1814 while(ptr && *ptr && (isspace((int)*ptr) || (*ptr=='-')))
1816 to=strtoofft(ptr, &ptr2, 0);
1818 /* we didn't get any digit */
1821 if((-1 == to) && (from>=0)) {
1823 conn->resume_from = from;
1824 infof(data, "FTP RANGE %Od to end of file\n", from);
1829 conn->maxdownload = -from;
1830 conn->resume_from = from;
1831 infof(data, "FTP RANGE the last %Od bytes\n", totalsize);
1835 totalsize = to-from;
1836 conn->maxdownload = totalsize+1; /* include the last mentioned byte */
1837 conn->resume_from = from;
1838 infof(data, "FTP RANGE from %Od getting %Od bytes\n", from,
1841 infof(data, "range-download from %Od to %Od, totally %Od bytes\n",
1842 from, to, conn->maxdownload);
1843 ftp->dont_check = TRUE; /* dont check for successful transfer */
1846 if((data->set.ftp_list_only) || !ftp->file) {
1847 /* The specified path ends with a slash, and therefore we think this
1848 is a directory that is requested, use LIST. But before that we
1849 need to set ASCII transfer mode. */
1852 /* Set type to ASCII */
1853 result = ftp_transfertype(conn, TRUE /* ASCII enforced */);
1857 /* if this output is to be machine-parsed, the NLST command will be
1858 better used since the LIST command output is not specified or
1859 standard in any way */
1861 FTPSENDF(conn, "%s",
1862 data->set.customrequest?data->set.customrequest:
1863 (data->set.ftp_list_only?"NLST":"LIST"));
1868 /* Set type to binary (unless specified ASCII) */
1869 result = ftp_transfertype(conn, data->set.ftp_ascii);
1873 /* Send any PREQUOTE strings after transfer type is set? */
1874 if(data->set.prequote) {
1875 if ((result = ftp_sendquote(conn, data->set.prequote)) != CURLE_OK)
1879 /* Attempt to get the size, it'll be useful in some cases: for resumed
1880 downloads and when talking to servers that don't give away the size
1881 in the RETR response line. */
1882 result = ftp_getsize(conn, ftp->file, &foundsize);
1883 if(CURLE_OK == result) {
1884 if (data->set.max_filesize && foundsize > data->set.max_filesize) {
1885 failf(data, "Maximum file size exceeded");
1886 return CURLE_FILESIZE_EXCEEDED;
1888 downloadsize = foundsize;
1891 if(conn->resume_from) {
1893 /* Daniel: (August 4, 1999)
1895 * We start with trying to use the SIZE command to figure out the size
1896 * of the file we're gonna get. If we can get the size, this is by far
1897 * the best way to know if we're trying to resume beyond the EOF.
1899 * Daniel, November 28, 2001. We *always* get the size on downloads
1900 * now, so it is done before this even when not doing resumes. I saved
1901 * the comment above for nostalgical reasons! ;-)
1903 if(CURLE_OK != result) {
1904 infof(data, "ftp server doesn't support SIZE\n");
1905 /* We couldn't get the size and therefore we can't know if there
1906 really is a part of the file left to get, although the server
1907 will just close the connection when we start the connection so it
1908 won't cause us any harm, just not make us exit as nicely. */
1911 /* We got a file size report, so we check that there actually is a
1912 part of the file left to get, or else we go home. */
1913 if(conn->resume_from< 0) {
1914 /* We're supposed to download the last abs(from) bytes */
1915 if(foundsize < -conn->resume_from) {
1916 failf(data, "Offset (%Od) was beyond file size (%Od)",
1917 conn->resume_from, foundsize);
1918 return CURLE_FTP_BAD_DOWNLOAD_RESUME;
1920 /* convert to size to download */
1921 downloadsize = -conn->resume_from;
1922 /* download from where? */
1923 conn->resume_from = foundsize - downloadsize;
1926 if(foundsize < conn->resume_from) {
1927 failf(data, "Offset (%Od) was beyond file size (%Od)",
1928 conn->resume_from, foundsize);
1929 return CURLE_FTP_BAD_DOWNLOAD_RESUME;
1931 /* Now store the number of bytes we are expected to download */
1932 downloadsize = foundsize-conn->resume_from;
1936 if (downloadsize == 0) {
1937 /* no data to transfer */
1938 result=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1939 infof(data, "File already completely downloaded\n");
1941 /* Set no_transfer so that we won't get any error in Curl_ftp_done()
1942 * because we didn't transfer the any file */
1943 ftp->no_transfer = TRUE;
1947 /* Set resume file transfer offset */
1948 infof(data, "Instructs server to resume from offset %Od\n",
1951 FTPSENDF(conn, "REST %Od", conn->resume_from);
1953 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1957 if(ftpcode != 350) {
1958 failf(data, "Couldn't use REST: %s", buf+4);
1959 return CURLE_FTP_COULDNT_USE_REST;
1963 FTPSENDF(conn, "RETR %s", ftp->file);
1966 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1970 if((ftpcode == 150) || (ftpcode == 125)) {
1974 150 Opening BINARY mode data connection for /etc/passwd (2241
1975 bytes). (ok, the file is being transfered)
1978 150 Opening ASCII mode data connection for /bin/ls
1981 150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes).
1984 150 Opening ASCII mode data connection for /linux/fisk/kpanelrc (0.0.0.0,0) (545 bytes).
1987 125 Data connection already open; Transfer starting. */
1989 off_t size=-1; /* default unknown size */
1993 * It appears that there are FTP-servers that return size 0 for files
1994 * when SIZE is used on the file while being in BINARY mode. To work
1995 * around that (stupid) behavior, we attempt to parse the RETR response
1996 * even if the SIZE returned size zero.
1998 * Debugging help from Salvatore Sorrentino on February 26, 2003.
2002 !data->set.ftp_ascii &&
2003 (downloadsize < 1)) {
2005 * It seems directory listings either don't show the size or very
2006 * often uses size 0 anyway. ASCII transfers may very well turn out
2007 * that the transfered amount of data is not the same as this line
2008 * tells, why using this number in those cases only confuses us.
2010 * Example D above makes this parsing a little tricky */
2012 bytes=strstr(buf, " bytes");
2015 /* this is a hint there is size information in there! ;-) */
2017 /* scan for the parenthesis and break there */
2020 /* if only skip digits, or else we're in deep trouble */
2021 if(!isdigit((int)*bytes)) {
2025 /* one more estep backwards */
2028 /* only if we have nothing but digits: */
2030 /* get the number! */
2031 size = strtoofft(bytes, NULL, 0);
2036 else if(downloadsize > -1)
2037 size = downloadsize;
2039 if(data->set.ftp_use_port) {
2040 result = AllowServerConnect(conn);
2045 if(conn->ssl[SECONDARYSOCKET].use) {
2046 /* since we only have a plaintext TCP connection here, we must now
2048 infof(data, "Doing the SSL/TLS handshake on the data stream\n");
2049 result = Curl_SSLConnect(conn, SECONDARYSOCKET);
2054 if(size > conn->maxdownload && conn->maxdownload > 0)
2055 size = conn->size = conn->maxdownload;
2057 infof(data, "Getting file with size: %Od\n", size);
2060 result=Curl_Transfer(conn, SECONDARYSOCKET, size, FALSE,
2062 -1, NULL); /* no upload here */
2067 if(dirlist && (ftpcode == 450)) {
2068 /* simply no matching files */
2069 ftp->no_transfer = TRUE; /* don't think we should download anything */
2072 failf(data, "%s", buf+4);
2073 return CURLE_FTP_COULDNT_RETR_FILE;
2078 /* end of transfer */
2083 /***********************************************************************
2087 * This is the actual DO function for FTP. Get a file/directory according to
2088 * the options previously setup.
2092 CURLcode ftp_perform(struct connectdata *conn,
2093 bool *connected) /* for the TCP connect status after
2096 /* this is FTP and no proxy */
2097 CURLcode result=CURLE_OK;
2098 struct SessionHandle *data=conn->data;
2099 char *buf = data->state.buffer; /* this is our buffer */
2101 /* the ftp struct is already inited in Curl_ftp_connect() */
2102 struct FTP *ftp = conn->proto.ftp;
2104 /* Send any QUOTE strings? */
2105 if(data->set.quote) {
2106 if ((result = ftp_sendquote(conn, data->set.quote)) != CURLE_OK)
2110 /* This is a re-used connection. Since we change directory to where the
2111 transfer is taking place, we must now get back to the original dir
2112 where we ended up after login: */
2113 if (conn->bits.reuse && ftp->entrypath) {
2114 if ((result = ftp_cwd_and_mkd(conn, ftp->entrypath)) != CURLE_OK)
2119 int i; /* counter for loop */
2120 for (i=0; ftp->dirs[i]; i++) {
2121 /* RFC 1738 says empty components should be respected too, but
2122 that is plain stupid since CWD can't be used with an empty argument */
2123 if ((result = ftp_cwd_and_mkd(conn, ftp->dirs[i])) != CURLE_OK)
2128 /* Requested time of file or time-depended transfer? */
2129 if((data->set.get_filetime || data->set.timecondition) &&
2131 result = ftp_getfiletime(conn, ftp->file);
2134 case CURLE_FTP_COULDNT_RETR_FILE:
2136 if(data->set.timecondition) {
2137 if((data->info.filetime > 0) && (data->set.timevalue > 0)) {
2138 switch(data->set.timecondition) {
2139 case CURL_TIMECOND_IFMODSINCE:
2141 if(data->info.filetime < data->set.timevalue) {
2142 infof(data, "The requested document is not new enough\n");
2143 ftp->no_transfer = TRUE; /* mark this to not transfer data */
2147 case CURL_TIMECOND_IFUNMODSINCE:
2148 if(data->info.filetime > data->set.timevalue) {
2149 infof(data, "The requested document is not old enough\n");
2150 ftp->no_transfer = TRUE; /* mark this to not transfer data */
2157 infof(data, "Skipping time comparison\n");
2166 /* If we have selected NOBODY and HEADER, it means that we only want file
2167 information. Which in FTP can't be much more than the file size and
2169 if(data->set.no_body && data->set.include_header && ftp->file) {
2170 /* The SIZE command is _not_ RFC 959 specified, and therefor many servers
2171 may not support it! It is however the only way we have to get a file's
2177 ftp->no_transfer = TRUE; /* this means no actual transfer is made */
2179 /* Some servers return different sizes for different modes, and thus we
2180 must set the proper type before we check the size */
2181 result = ftp_transfertype(conn, data->set.ftp_ascii);
2185 /* failing to get size is not a serious error */
2186 result = ftp_getsize(conn, ftp->file, &filesize);
2188 if(CURLE_OK == result) {
2189 sprintf(buf, "Content-Length: %Od\r\n", filesize);
2190 result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
2195 /* Determine if server can respond to REST command and therefore
2196 whether it can do a range */
2197 FTPSENDF(conn, "REST 0", NULL);
2198 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2200 if ((CURLE_OK == result) && (ftpcode == 350)) {
2201 result = Curl_client_write(data, CLIENTWRITE_BOTH,
2202 (char *)"Accept-ranges: bytes\r\n", 0);
2207 /* If we asked for a time of the file and we actually got one as
2208 well, we "emulate" a HTTP-style header in our output. */
2210 #ifdef HAVE_STRFTIME
2211 if(data->set.get_filetime && (data->info.filetime>=0) ) {
2213 #ifdef HAVE_GMTIME_R
2215 tm = (struct tm *)gmtime_r((time_t *)&data->info.filetime, &buffer);
2217 tm = gmtime((time_t *)&data->info.filetime);
2219 /* format: "Tue, 15 Nov 1994 12:45:26" */
2220 strftime(buf, BUFSIZE-1, "Last-Modified: %a, %d %b %Y %H:%M:%S GMT\r\n",
2222 result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
2231 if(data->set.no_body)
2232 /* doesn't really transfer any data */
2233 ftp->no_transfer = TRUE;
2234 /* Get us a second connection up and connected */
2235 else if(data->set.ftp_use_port) {
2236 /* We have chosen to use the PORT command */
2237 result = ftp_use_port(conn);
2238 if(CURLE_OK == result) {
2239 /* we have the data connection ready */
2240 infof(data, "Ordered connect of the data stream with PORT!\n");
2241 *connected = TRUE; /* mark us "still connected" */
2245 /* We have chosen (this is default) to use the PASV command */
2246 result = ftp_use_pasv(conn, connected);
2247 if(CURLE_OK == result && *connected)
2248 infof(data, "Connected the data stream with PASV!\n");
2254 /***********************************************************************
2258 * This function is registered as 'curl_do' function. It decodes the path
2259 * parts etc as a wrapper to the actual DO function (ftp_perform).
2261 * The input argument is already checked for validity.
2263 CURLcode Curl_ftp(struct connectdata *conn)
2265 CURLcode retcode=CURLE_OK;
2268 struct SessionHandle *data = conn->data;
2271 char *slash_pos; /* position of the first '/' char in curpos */
2272 char *cur_pos=conn->ppath; /* current position in ppath. point at the begin
2273 of next path component */
2274 int path_part=0;/* current path component */
2276 /* the ftp struct is already inited in ftp_connect() */
2277 ftp = conn->proto.ftp;
2278 conn->size = -1; /* make sure this is unknown at this point */
2280 Curl_pgrsSetUploadCounter(data, 0);
2281 Curl_pgrsSetDownloadCounter(data, 0);
2282 Curl_pgrsSetUploadSize(data, 0);
2283 Curl_pgrsSetDownloadSize(data, 0);
2285 /* fixed : initialize ftp->dirs[xxx] to NULL !
2286 is done in Curl_ftp_connect() */
2288 /* parse the URL path into separate path components */
2289 while((slash_pos=strchr(cur_pos, '/'))) {
2290 /* 1 or 0 to indicate absolute directory */
2291 bool absolute_dir = (cur_pos - conn->ppath > 0) && (path_part == 0);
2293 /* seek out the next path component */
2294 if (slash_pos-cur_pos) {
2295 /* we skip empty path components, like "x//y" since the FTP command CWD
2296 requires a parameter and a non-existant parameter a) doesn't work on
2297 many servers and b) has no effect on the others. */
2298 ftp->dirs[path_part] = curl_unescape(cur_pos - absolute_dir,
2299 slash_pos - cur_pos + absolute_dir);
2301 if (!ftp->dirs[path_part]) { /* run out of memory ... */
2302 failf(data, "no memory");
2304 return CURLE_OUT_OF_MEMORY;
2308 cur_pos = slash_pos + 1; /* jump to the rest of the string */
2313 cur_pos = slash_pos + 1; /* jump to the rest of the string */
2314 if(++path_part >= (CURL_MAX_FTP_DIRDEPTH-1)) {
2315 /* too deep, we need the last entry to be kept NULL at all
2316 times to signal end of list */
2317 failf(data, "too deep dir hierarchy");
2319 return CURLE_URL_MALFORMAT;
2324 ftp->file = cur_pos; /* the rest is the file name */
2327 ftp->file = curl_unescape(ftp->file, 0);
2328 if(NULL == ftp->file) {
2330 failf(data, "no memory");
2331 return CURLE_OUT_OF_MEMORY;
2335 ftp->file=NULL; /* instead of point to a zero byte, we make it a NULL
2338 retcode = ftp_perform(conn, &connected);
2340 if(CURLE_OK == retcode) {
2342 retcode = Curl_ftp_nextconnect(conn);
2344 if(retcode && (conn->sock[SECONDARYSOCKET] >= 0)) {
2345 /* Failure detected, close the second socket if it was created already */
2346 sclose(conn->sock[SECONDARYSOCKET]);
2347 conn->sock[SECONDARYSOCKET] = -1;
2350 if(ftp->no_transfer)
2351 /* no data to transfer */
2352 retcode=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
2354 /* since we didn't connect now, we want do_more to get called */
2355 conn->bits.do_more = TRUE;
2363 /***********************************************************************
2367 * Sends the formated string as a ftp command to a ftp server
2369 * NOTE: we build the command in a fixed-length buffer, which sets length
2370 * restrictions on the command!
2372 CURLcode Curl_ftpsendf(struct connectdata *conn,
2373 const char *fmt, ...)
2375 ssize_t bytes_written;
2379 CURLcode res = CURLE_OK;
2383 vsnprintf(s, 250, fmt, ap);
2386 strcat(s, "\r\n"); /* append a trailing CRLF */
2389 write_len = strlen(s);
2392 res = Curl_write(conn, conn->sock[FIRSTSOCKET], sptr, write_len,
2398 if(conn->data->set.verbose)
2399 Curl_debug(conn->data, CURLINFO_HEADER_OUT, sptr, bytes_written);
2401 if(bytes_written != write_len) {
2402 write_len -= bytes_written;
2403 sptr += bytes_written;
2412 /***********************************************************************
2416 * This should be called before calling sclose() on an ftp control connection
2417 * (not data connections). We should then wait for the response from the
2418 * server before returning. The calling code should then try to close the
2422 CURLcode Curl_ftp_quit(struct connectdata *conn)
2428 ret = Curl_ftpsendf(conn, "%s", "QUIT");
2430 ret = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2435 /***********************************************************************
2437 * Curl_ftp_disconnect()
2439 * Disconnect from an FTP server. Cleanup protocol-specific per-connection
2442 CURLcode Curl_ftp_disconnect(struct connectdata *conn)
2444 struct FTP *ftp= conn->proto.ftp;
2447 /* We cannot send quit unconditionally. If this connection is stale or
2448 bad in any way, sending quit and waiting around here will make the
2449 disconnect wait in vain and cause more problems than we need to.
2451 Until fixed, we keep this #if 0'ed. To be fixed in 7.11.1. Stay tuned.
2453 (void)Curl_ftp_quit(conn); /* ignore errors on the QUIT */
2456 /* The FTP session may or may not have been allocated/setup at this point! */
2459 free(ftp->entrypath);
2466 ftp->file = NULL; /* zero */
2473 /***********************************************************************
2477 * Makes a directory on the FTP server.
2481 CURLcode ftp_mkd(struct connectdata *conn, char *path)
2483 CURLcode result=CURLE_OK;
2484 int ftpcode; /* for ftp status */
2487 /* Create a directory on the remote server */
2488 FTPSENDF(conn, "MKD %s", path);
2490 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2497 infof( conn->data , "Created remote directory %s\n" , path );
2500 failf(conn->data, "Permission denied to make directory %s", path);
2501 result = CURLE_FTP_ACCESS_DENIED;
2504 failf(conn->data, "unrecognized MKD response: %d", ftpcode );
2505 result = CURLE_FTP_ACCESS_DENIED;
2511 /***********************************************************************
2515 * Send 'CWD' to the remote server to Change Working Directory. It is the ftp
2516 * version of the unix 'cd' command. This function is only called from the
2517 * ftp_cwd_and_mkd() function these days.
2519 * This function does NOT call failf().
2522 CURLcode ftp_cwd(struct connectdata *conn, char *path)
2528 FTPSENDF(conn, "CWD %s", path);
2529 result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2531 /* According to RFC959, CWD is supposed to return 250 on success, but
2532 there seem to be non-compliant FTP servers out there that return 200,
2533 so we accept any '2xy' code here. */
2534 if (ftpcode/100 != 2)
2535 result = CURLE_FTP_ACCESS_DENIED;
2541 /***********************************************************************
2545 * Change to the given directory. If the directory is not present, and we
2546 * have been told to allow it, then create the directory and cd to it.
2549 static CURLcode ftp_cwd_and_mkd(struct connectdata *conn, char *path)
2553 result = ftp_cwd(conn, path);
2555 if(conn->data->set.ftp_create_missing_dirs) {
2556 result = ftp_mkd(conn, path);
2558 /* ftp_mkd() calls failf() itself */
2560 result = ftp_cwd(conn, path);
2563 failf(conn->data, "Couldn't cd to %s", path);
2568 #endif /* CURL_DISABLE_FTP */