use the proper timecond defines, not the obsolete ones I've removed! ;-)
[platform/upstream/curl.git] / lib / ftp.c
1 /***************************************************************************
2  *                                  _   _ ____  _     
3  *  Project                     ___| | | |  _ \| |    
4  *                             / __| | | | |_) | |    
5  *                            | (__| |_| |  _ <| |___ 
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2004, Daniel Stenberg, <daniel@haxx.se>, et al.
9  *
10  * This software is licensed as described in the file COPYING, which
11  * you should have received as part of this distribution. The terms
12  * are also available at http://curl.haxx.se/docs/copyright.html.
13  * 
14  * You may opt to use, copy, modify, merge, publish, distribute and/or sell
15  * copies of the Software, and permit persons to whom the Software is
16  * furnished to do so, under the terms of the COPYING file.
17  *
18  * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
19  * KIND, either express or implied.
20  *
21  * $Id$
22  ***************************************************************************/
23
24 #include "setup.h"
25
26 #ifndef CURL_DISABLE_FTP
27 #include <stdio.h>
28 #include <string.h>
29 #include <stdlib.h>
30 #include <stdarg.h>
31 #include <ctype.h>
32 #include <errno.h>
33
34 #ifdef HAVE_UNISTD_H
35 #include <unistd.h>
36 #endif
37 #ifdef HAVE_SYS_SELECT_H
38 #include <sys/select.h>
39 #endif
40
41 #if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
42
43 #else /* some kind of unix */
44 #ifdef HAVE_SYS_SOCKET_H
45 #include <sys/socket.h>
46 #endif
47 #include <sys/types.h>
48 #ifdef HAVE_NETINET_IN_H
49 #include <netinet/in.h>
50 #endif
51 #ifdef HAVE_ARPA_INET_H
52 #include <arpa/inet.h>
53 #endif
54 #include <sys/utsname.h>
55 #ifdef HAVE_NETDB_H
56 #include <netdb.h>
57 #endif
58 #ifdef  VMS
59 #include <in.h>
60 #include <inet.h>
61 #endif
62 #endif
63
64 #if defined(WIN32) && defined(__GNUC__) || defined(__MINGW32__)
65 #include <errno.h>
66 #endif
67
68 #include <curl/curl.h>
69 #include "urldata.h"
70 #include "sendf.h"
71
72 #include "if2ip.h"
73 #include "hostip.h"
74 #include "progress.h"
75 #include "transfer.h"
76 #include "escape.h"
77 #include "http.h" /* for HTTP proxy tunnel stuff */
78 #include "ftp.h"
79
80 #ifdef HAVE_KRB4
81 #include "security.h"
82 #include "krb4.h"
83 #endif
84
85 #include "strtoofft.h"
86 #include "strequal.h"
87 #include "ssluse.h"
88 #include "connect.h"
89
90 #if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
91 #include "inet_ntoa_r.h"
92 #endif
93
94 #define _MPRINTF_REPLACE /* use our functions only */
95 #include <curl/mprintf.h>
96
97 /* The last #include file should be: */
98 #ifdef CURLDEBUG
99 #include "memdebug.h"
100 #endif
101
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);
108
109 /* easy-to-use macro: */
110 #define FTPSENDF(x,y,z) if((result = Curl_ftpsendf(x,y,z))) return result
111
112 static void freedirs(struct FTP *ftp)
113 {
114   int i;
115   for (i=0; ftp->dirs[i]; i++){
116     free(ftp->dirs[i]);
117     ftp->dirs[i]=NULL;
118   }
119 }
120
121 /***********************************************************************
122  *
123  * AllowServerConnect()
124  *
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
127  * connected.
128  *
129  */
130 static CURLcode AllowServerConnect(struct connectdata *conn)
131 {
132   fd_set rdset;
133   struct timeval dt;
134   struct SessionHandle *data = conn->data;
135   int sock = conn->sock[SECONDARYSOCKET];
136   
137   FD_ZERO(&rdset);
138
139   FD_SET(sock, &rdset);
140
141   /* we give the server 10 seconds to connect to us */
142   dt.tv_sec = 10;
143   dt.tv_usec = 0;
144
145   switch (select(sock+1, &rdset, NULL, NULL, &dt)) {
146   case -1: /* error */
147     /* let's die here */
148     failf(data, "Error while waiting for server connect");
149     return CURLE_FTP_PORT_FAILED;
150   case 0:  /* timeout */
151     /* let's die here */
152     failf(data, "Timeout while waiting for server connect");
153     return CURLE_FTP_PORT_FAILED;
154   default:
155     /* we have received data here */
156     {
157       int s;
158       size_t size = sizeof(struct sockaddr_in);
159       struct sockaddr_in add;
160
161       getsockname(sock, (struct sockaddr *) &add, (socklen_t *)&size);
162       s=accept(sock, (struct sockaddr *) &add, (socklen_t *)&size);
163
164       sclose(sock); /* close the first socket */
165
166       if (-1 == s) {
167         /* DIE! */
168         failf(data, "Error accept()ing server connect");
169         return CURLE_FTP_PORT_FAILED;
170       }
171       infof(data, "Connection accepted from server\n");
172
173       conn->sock[SECONDARYSOCKET] = s;
174       Curl_nonblock(s, TRUE); /* enable non-blocking */
175     }
176     break;
177   }
178
179   return CURLE_OK;
180 }
181
182
183 /* --- parse FTP server responses --- */
184
185 /*
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.
189  */
190
191 CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */
192                              struct connectdata *conn,
193                              int *ftpcode) /* return the ftp-code */
194 {
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.
198    *
199    * Alas, read as much as possible, split up into lines, use the ending
200    * line in a response or continue reading.  */
201
202   int sockfd = conn->sock[FIRSTSOCKET];
203   int perline; /* count bytes per line */
204   bool keepon=TRUE;
205   ssize_t gotbytes;
206   char *ptr;
207   int timeout;              /* timeout in seconds */
208   struct timeval interval;
209   fd_set rkeepfd;
210   fd_set readfd;
211   struct SessionHandle *data = conn->data;
212   char *line_start;
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();
218
219   if (ftpcode)
220     *ftpcode = 0; /* 0 for errors */
221
222   FD_ZERO (&readfd);            /* clear it */
223   FD_SET (sockfd, &readfd);     /* read socket */
224
225   /* get this in a backup variable to be able to restore it on each lap in the
226      select() loop */
227   rkeepfd = readfd;
228
229   ptr=buf;
230   line_start = buf;
231
232   *nreadp=0;
233   perline=0;
234   keepon=TRUE;
235
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 */
250     else
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 */
255
256     if(timeout <=0 ) {
257       failf(data, "Transfer aborted due to timeout");
258       return CURLE_OPERATION_TIMEDOUT; /* already too little time */
259     }
260
261     if(!ftp->cache) {
262       readfd = rkeepfd;            /* set every lap */
263       interval.tv_sec = 1; /* use 1 second timeout intervals */
264       interval.tv_usec = 0;
265
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);
270         break;
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 */
275
276       default:
277         break;
278       }
279     }
280     if(CURLE_OK == result) {
281       /*
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!
285        */
286       if(ftp->cache) {
287         /* we had data in the "cache", copy that instead of doing an actual
288          * read
289          *
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
294          * than an int.
295          */
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 */
301       }
302       else {
303         int res = Curl_read(conn, sockfd, ptr, BUFSIZE-*nreadp, &gotbytes);
304         if(res < 0)
305           /* EWOULDBLOCK */
306           continue; /* go looping again */
307
308         if(CURLE_OK != res)
309           keepon = FALSE;
310       }
311
312       if(!keepon)
313         ;
314       else if(gotbytes <= 0) {
315         keepon = FALSE;
316         result = CURLE_RECV_ERROR;
317         failf(data, "FTP response reading failed");
318       }
319       else {
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
322          * line */
323         int i;
324
325         *nreadp += gotbytes;
326         for(i = 0; i < gotbytes; ptr++, i++) {
327           perline++;
328           if(*ptr=='\n') {
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 */
331
332             /* output debug output if that is requested */
333             if(data->set.verbose)
334               Curl_debug(data, CURLINFO_HEADER_IN, line_start, perline);
335
336             /*
337              * We pass all response-lines to the callback function registered
338              * for "headers". The response lines can be seen as a kind of
339              * headers.
340              */
341             result = Curl_client_write(data, CLIENTWRITE_HEADER,
342                                        line_start, perline);
343             if(result)
344               return result;
345                                        
346 #define lastline(line) (isdigit((int)line[0]) && isdigit((int)line[1]) && \
347                         isdigit((int)line[2]) && (' ' == line[3]))
348
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)! */
353               char *meow;
354               int n;
355               for(meow=line_start, n=0; meow<ptr; meow++, n++)
356                 buf[n] = *meow;
357               *meow=0; /* zero terminate */
358               keepon=FALSE;
359               line_start = ptr+1; /* advance pointer */
360               i++; /* skip this before getting out */
361               break;
362             }
363             perline=0; /* line starts over here */
364             line_start = ptr+1;
365           }
366         }
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
373              2001. */
374           ftp->cache_size = gotbytes - i;
375           ftp->cache = (char *)malloc((int)ftp->cache_size);
376           if(ftp->cache)
377             memcpy(ftp->cache, line_start, (int)ftp->cache_size);
378           else
379             return CURLE_OUT_OF_MEMORY; /**BANG**/
380         }
381       } /* there was data */
382     } /* if(no error) */
383   } /* while there's buffer left and loop is requested */
384
385   if(!result)
386     code = atoi(buf);
387
388 #ifdef HAVE_KRB4
389   /* handle the security-oriented responses 6xx ***/
390   /* FIXME: some errorchecking perhaps... ***/
391   switch(code) {
392   case 631:
393     Curl_sec_read_msg(conn, buf, prot_safe);
394     break;
395   case 632:
396     Curl_sec_read_msg(conn, buf, prot_private);
397     break;
398   case 633:
399     Curl_sec_read_msg(conn, buf, prot_confidential);
400     break;
401   default:
402     /* normal ftp stuff we pass through! */
403     break;
404   }
405 #endif
406
407   if(ftpcode)
408     *ftpcode=code; /* return the initial number like this */
409
410   /* store the latest code for later retrieval */
411   conn->data->info.httpcode=code;
412
413   return result;
414 }
415
416 static const char *ftpauth[]= {
417   "SSL", "TLS", NULL
418 };
419
420 /*
421  * Curl_ftp_connect() should do everything that is to be considered a part of
422  * the connection phase.
423  */
424 CURLcode Curl_ftp_connect(struct connectdata *conn)
425 {
426   /* this is FTP and no proxy */
427   ssize_t nread;
428   struct SessionHandle *data=conn->data;
429   char *buf = data->state.buffer; /* this is our buffer */
430   struct FTP *ftp;
431   CURLcode result;
432   int ftpcode, try;
433
434   ftp = (struct FTP *)malloc(sizeof(struct FTP));
435   if(!ftp)
436     return CURLE_OUT_OF_MEMORY;
437
438   memset(ftp, 0, sizeof(struct FTP));
439   conn->proto.ftp = ftp;
440
441   /* We always support persistant connections on ftp */
442   conn->bits.close = FALSE;
443
444   /* get some initial data into the ftp struct */
445   ftp->bytecountp = &conn->bytecount;
446
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 */
451
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)
457       return result;
458   }
459
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);
464     if(result)
465       return result;
466   }
467
468   /* The first thing we do is wait for the "220*" line: */
469   result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
470   if(result)
471     return result;
472
473   if(ftpcode != 220) {
474     failf(data, "This doesn't seem like a nice ftp-server response");
475     return CURLE_FTP_WEIRD_SERVER_REPLY;
476   }
477
478 #ifdef HAVE_KRB4
479   /* if not anonymous login, try a secure login */
480   if(data->set.krb4) {
481
482     /* request data protection level (default is 'clear') */
483     Curl_sec_request_prot(conn, "private");
484
485     /* We set private first as default, in case the line below fails to
486        set a valid level */
487     Curl_sec_request_prot(conn, data->set.krb4_level);
488
489     if(Curl_sec_login(conn) != 0)
490       infof(data, "Logging in with password in cleartext!\n");
491     else
492       infof(data, "Authentication successful\n");
493   }
494 #endif
495
496   if(data->set.ftp_ssl && !conn->ssl[FIRSTSOCKET].use) {
497     /* we don't have a SSL/TLS connection, try a FTPS connection now */
498
499     for (try = 0; ftpauth[try]; try++) {
500
501       FTPSENDF(conn, "AUTH %s", ftpauth[try]);
502
503       result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
504
505       if(result)
506         return result;
507
508       /* RFC2228 (page 5) says:
509        *
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
512        * 234/334.
513        */
514
515       if((ftpcode == 234) || (ftpcode == 334)) {
516         result = Curl_SSLConnect(conn, FIRSTSOCKET);
517         if(result)
518           return result;
519         conn->protocol |= PROT_FTPS;
520         conn->ssl[SECONDARYSOCKET].use = FALSE; /* clear-text data */
521         break;
522       }
523     }
524   }
525   
526   /* send USER */
527   FTPSENDF(conn, "USER %s", ftp->user?ftp->user:"");
528
529   /* wait for feedback */
530   result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
531   if(result)
532     return result;
533
534   if(ftpcode == 530) {
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;
539   }
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);
545     if(result)
546       return result;
547
548     if(ftpcode == 530) {
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;
553     }
554     else if(ftpcode == 230) {
555       /* 230 User ... logged in.
556          (user successfully logged in) */
557         
558       infof(data, "We have successfully logged in\n");
559     }
560     else {
561       failf(data, "Odd return code after PASS");
562       return CURLE_FTP_WEIRD_PASS_REPLY;
563     }
564   }
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) {
570 #ifdef HAVE_KRB4
571         /* we are logged in (with Kerberos)
572          * now set the requested protection level
573          */
574     if(conn->sec_complete)
575       Curl_sec_set_protection_level(conn);
576
577     /* we may need to issue a KAUTH here to have access to the files
578      * do it if user supplied a password
579      */
580     if(conn->passwd && *conn->passwd) {
581       result = Curl_krb_kauth(conn);
582       if(result)
583         return result;
584     }
585 #endif
586   }
587   }
588   else {
589     failf(data, "Odd return code after USER");
590     return CURLE_FTP_WEIRD_USER_REPLY;
591   }
592
593   if(conn->ssl[FIRSTSOCKET].use) {
594     /* PBSZ = PROTECTION BUFFER SIZE.
595
596        The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says:
597
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)
601
602        ... (and on page 8):
603          
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.
607     */
608     FTPSENDF(conn, "PBSZ %d", 0);
609     result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
610     if(result)
611       return result;
612
613     /* For TLS, the data connection can have one of two security levels.
614
615        1)Clear (requested by 'PROT C')
616
617        2)Private (requested by 'PROT P')
618     */
619     if(!conn->ssl[SECONDARYSOCKET].use) {
620       FTPSENDF(conn, "PROT %c", 'P');
621       result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
622       if(result)
623         return result;
624     
625       if(ftpcode == 200)
626         /* We have enabled SSL for the data connection! */
627         conn->ssl[SECONDARYSOCKET].use = TRUE;
628
629       /* FTP servers typically responds with 500 if they decide to reject
630          our 'P' request */
631     }
632   }
633
634   /* send PWD to discover our entry point */
635   FTPSENDF(conn, "PWD", NULL);
636
637   /* wait for feedback */
638   result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
639   if(result)
640     return result;
641
642   if(ftpcode == 257) {
643     char *dir = (char *)malloc(nread+1);
644     char *store=dir;
645     char *ptr=&buf[4]; /* start on the first letter */
646
647     if(!dir)
648       return CURLE_OUT_OF_MEMORY;
649     
650     /* Reply format is like
651        257<space>"<directory-name>"<space><commentary> and the RFC959 says
652
653        The directory name can contain any character; embedded double-quotes
654        should be escaped by double-quotes (the "quote-doubling" convention).
655     */
656     if('\"' == *ptr) {
657       /* it started good */
658       ptr++;
659       while(ptr && *ptr) {
660         if('\"' == *ptr) {
661           if('\"' == ptr[1]) {
662             /* "quote-doubling" */
663             *store = ptr[1];
664             ptr++;
665           }
666           else {
667             /* end of path */
668             *store = '\0'; /* zero terminate */
669             break; /* get out of this loop */
670           }
671         }
672         else
673           *store = *ptr;
674         store++;
675         ptr++;
676       }
677       ftp->entrypath =dir; /* remember this */
678       infof(data, "Entry path is '%s'\n", ftp->entrypath);
679     }
680     else {
681       /* couldn't get the path */
682       free(dir);
683       infof(data, "Failed to figure out path\n");
684     }
685
686   }
687   else {
688     /* We couldn't read the PWD response! */
689   }
690
691   return CURLE_OK;
692 }
693
694 /***********************************************************************
695  *
696  * Curl_ftp_done()
697  *
698  * The DONE function. This does what needs to be done after a single DO has
699  * performed.
700  *
701  * Input argument is already checked for validity.
702  */
703 CURLcode Curl_ftp_done(struct connectdata *conn)
704 {
705   struct SessionHandle *data = conn->data;
706   struct FTP *ftp = conn->proto.ftp;
707   ssize_t nread;
708   int ftpcode;
709   CURLcode result=CURLE_OK;
710
711   /* free the dir tree parts */
712   freedirs(ftp);
713
714   if(ftp->file) {
715     free(ftp->file);
716     ftp->file = NULL;
717   }
718
719   if(data->set.upload) {
720     if((-1 != data->set.infilesize) &&
721        (data->set.infilesize != *ftp->bytecountp) &&
722        !data->set.crlf) {
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;
728     }
729   }
730   else {
731     if((-1 != conn->size) && (conn->size != *ftp->bytecountp) &&
732        (conn->maxdownload != *ftp->bytecountp)) {
733       failf(data, "Received only partial file: %Od bytes",
734             *ftp->bytecountp);
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;
738     }
739     else if(!ftp->dont_check &&
740             !*ftp->bytecountp &&
741             (conn->size>0)) {
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;
748     }
749   }
750
751 #ifdef HAVE_KRB4
752   Curl_sec_fflush_fd(conn, conn->sock[SECONDARYSOCKET]);
753 #endif
754   /* shut down the socket to inform the server we're done */
755   sclose(conn->sock[SECONDARYSOCKET]);
756   conn->sock[SECONDARYSOCKET] = -1;
757
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.
763     */
764     ftp->response_time = 60; /* give it only a minute for now */
765
766     result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
767
768     ftp->response_time = 3600; /* set this back to one hour waits */
769   
770     if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) {
771       failf(data, "control connection looks dead");
772       return result;
773     }
774
775     if(result)
776       return result;
777
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;
783       }
784     }
785   }
786
787   /* clear these for next connection */
788   ftp->no_transfer = FALSE;
789   ftp->dont_check = FALSE; 
790
791   /* Send any post-transfer QUOTE strings? */
792   if(!result && data->set.postquote)
793     result = ftp_sendquote(conn, data->set.postquote);
794
795   return result;
796 }
797
798 /***********************************************************************
799  *
800  * ftp_sendquote()
801  *
802  * Where a 'quote' means a list of custom commands to send to the server.
803  * The quote list is passed as an argument.
804  */
805
806 static 
807 CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote)
808 {
809   struct curl_slist *item;
810   ssize_t nread;
811   int ftpcode;
812   CURLcode result;
813
814   item = quote;
815   while (item) {
816     if (item->data) {
817       FTPSENDF(conn, "%s", item->data);
818
819       result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
820       if (result)
821         return result;
822
823       if (ftpcode >= 400) {
824         failf(conn->data, "QUOT string not accepted: %s", item->data);
825         return CURLE_FTP_QUOTE_ERROR;
826       }
827     }
828
829     item = item->next;
830   }
831
832   return CURLE_OK;
833 }
834
835 /***********************************************************************
836  *
837  * ftp_getfiletime()
838  *
839  * Get the timestamp of the given file.
840  */
841 static
842 CURLcode ftp_getfiletime(struct connectdata *conn, char *file)
843 {
844   CURLcode result=CURLE_OK;
845   int ftpcode; /* for ftp status */
846   ssize_t nread;
847   char *buf = conn->data->state.buffer;
848
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);
852
853   result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
854   if(result)
855     return result;
856
857   switch(ftpcode) {
858   case 213:
859     {
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);
871       }
872     }
873     break;
874   default:
875     infof(conn->data, "unsupported MDTM reply format\n");
876     break;
877   case 550: /* "No such file or directory" */
878     failf(conn->data, "Given file does not exist");
879     result = CURLE_FTP_COULDNT_RETR_FILE;
880     break;
881   }
882   return  result;
883 }
884
885 /***********************************************************************
886  *
887  * ftp_transfertype()
888  *
889  * Set transfer type. We only deal with ASCII or BINARY so this function
890  * sets one of them.
891  */
892 static CURLcode ftp_transfertype(struct connectdata *conn,
893                                   bool ascii)
894 {
895   struct SessionHandle *data = conn->data;
896   int ftpcode;
897   ssize_t nread;
898   CURLcode result;
899
900   FTPSENDF(conn, "TYPE %s", ascii?"A":"I");
901
902   result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
903   if(result)
904     return result;
905   
906   if(ftpcode != 200) {
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;
910   }
911
912   return CURLE_OK;
913 }
914
915 /***********************************************************************
916  *
917  * ftp_getsize()
918  *
919  * Returns the file size (in bytes) of the given remote file.
920  */
921
922 static
923 CURLcode ftp_getsize(struct connectdata *conn, char *file,
924                       off_t *size)
925 {
926   struct SessionHandle *data = conn->data;
927   int ftpcode;
928   ssize_t nread;
929   char *buf=data->state.buffer;
930   CURLcode result;
931
932   FTPSENDF(conn, "SIZE %s", file);
933   result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
934   if(result)
935     return result;
936
937   if(ftpcode == 213) {
938     /* get the size from the ascii string: */
939     *size = strtoofft(buf+4, NULL, 0);
940   }
941   else
942     return CURLE_FTP_COULDNT_GET_SIZE;
943
944   return CURLE_OK;
945 }
946
947 /***************************************************************************
948  *
949  * ftp_pasv_verbose()
950  *
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.
954  *
955  */
956 static void
957 ftp_pasv_verbose(struct connectdata *conn,
958                  Curl_ipconnect *addr,
959                  char *newhost, /* ascii version */
960                  int port)
961 {
962 #ifndef ENABLE_IPV6
963   /*****************************************************************
964    *
965    * IPv4-only code section
966    */
967
968   struct in_addr in;
969   struct hostent * answer;
970
971 #ifdef HAVE_INET_NTOA_R
972   char ntoa_buf[64];
973 #endif
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)];
978
979 #if defined(HAVE_INET_ADDR)
980   in_addr_t address;
981 # if defined(HAVE_GETHOSTBYADDR_R)
982   int h_errnop;
983 # endif
984   char *hostent_buf = (char *)bigbuf; /* get a char * to the buffer */
985
986   address = inet_addr(newhost);
987 # ifdef HAVE_GETHOSTBYADDR_R
988
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); */
993
994   /* Fred Noz helped me try this out, now it at least compiles! */
995
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
999      gethostbyaddr_r(). 
1000
1001      ... as must be struct hostent_data Craig Markwardt 19 Sep 2002. */
1002
1003   memset(hostent_buf, 0, sizeof(struct hostent)+sizeof(struct hostent_data));
1004
1005   if(gethostbyaddr_r((char *) &address,
1006                      sizeof(address), AF_INET,
1007                      (struct hostent *)hostent_buf,
1008                      (struct hostent_data *)(hostent_buf + sizeof(*answer))))
1009     answer=NULL;
1010   else
1011     answer=(struct hostent *)hostent_buf;
1012                            
1013 #  endif
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),
1020                            &h_errnop);
1021 #  endif
1022 #  ifdef HAVE_GETHOSTBYADDR_R_8
1023   /* Linux style */
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),
1028                      &answer,
1029                      &h_errnop))
1030     answer=NULL; /* error */
1031 #  endif
1032         
1033 # else
1034   (void)hostent_buf; /* avoid compiler warning */
1035   answer = gethostbyaddr((char *) &address, sizeof(address), AF_INET);
1036 # endif
1037 #else
1038   answer = NULL;
1039 #endif
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)),
1045 #else
1046         inet_ntoa(in),
1047 #endif
1048         port);
1049
1050 #else
1051   /*****************************************************************
1052    *
1053    * IPv6-only code section
1054    */
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;
1060 #else
1061   const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
1062 #endif
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), "?");
1068   }
1069         
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);
1073   }
1074   else {
1075     infof(conn->data, "Connecting to %s (%s) port %s\n", hbuf, nbuf, sbuf);
1076   }
1077 #endif
1078 }
1079
1080 /***********************************************************************
1081  *
1082  * ftp_use_port()
1083  *
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.
1087  */
1088
1089 static
1090 CURLcode ftp_use_port(struct connectdata *conn)
1091 {
1092   struct SessionHandle *data=conn->data;
1093   int portsock=-1;
1094   ssize_t nread;
1095   int ftpcode; /* receive FTP response codes in this */
1096   CURLcode result;
1097
1098 #ifdef ENABLE_IPV6
1099   /******************************************************************
1100    *
1101    * Here's a piece of IPv6-specific code coming up
1102    *
1103    */
1104
1105   struct addrinfo hints, *res, *ai;
1106   struct sockaddr_storage ss;
1107   socklen_t sslen;
1108   char hbuf[NI_MAXHOST];
1109
1110   struct sockaddr *sa=(struct sockaddr *)&ss;
1111 #ifdef NI_WITHSCOPEID
1112   const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID;
1113 #else
1114   const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
1115 #endif
1116   unsigned char *ap;
1117   unsigned char *pp;
1118   char portmsgbuf[4096], tmp[4096];
1119
1120   const char *mode[] = { "EPRT", "LPRT", "PORT", NULL };
1121   char **modep;
1122
1123   /*
1124    * we should use Curl_if2ip?  given pickiness of recent ftpd,
1125    * I believe we should use the same address as the control connection.
1126    */
1127   sslen = sizeof(ss);
1128   if (getsockname(conn->sock[FIRSTSOCKET], (struct sockaddr *)&ss, &sslen) < 0)
1129     return CURLE_FTP_PORT_FAILED;
1130   
1131   if (getnameinfo((struct sockaddr *)&ss, sslen, hbuf, sizeof(hbuf), NULL, 0,
1132                   niflags))
1133     return CURLE_FTP_PORT_FAILED;
1134
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 
1139     2.1.X doesn't do*/
1140   hints.ai_socktype = SOCK_STREAM;
1141   hints.ai_flags = AI_PASSIVE;
1142
1143   if (getaddrinfo(hbuf, (char *)"0", &hints, &res))
1144     return CURLE_FTP_PORT_FAILED;
1145   
1146   portsock = -1;
1147   for (ai = res; ai; ai = ai->ai_next) {
1148     portsock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
1149     if (portsock < 0)
1150       continue;
1151
1152     if (bind(portsock, ai->ai_addr, ai->ai_addrlen) < 0) {
1153       sclose(portsock);
1154       portsock = -1;
1155       continue;
1156     }
1157       
1158     if (listen(portsock, 1) < 0) {
1159       sclose(portsock);
1160       portsock = -1;
1161       continue;
1162     }
1163     
1164     break;
1165   }
1166   freeaddrinfo(res);
1167   if (portsock < 0) {
1168     failf(data, "%s", strerror(errno));
1169     return CURLE_FTP_PORT_FAILED;
1170   }
1171
1172   sslen = sizeof(ss);
1173   if (getsockname(portsock, sa, &sslen) < 0) {
1174     failf(data, "%s", strerror(errno));
1175     return CURLE_FTP_PORT_FAILED;
1176   }
1177
1178   for (modep = (char **)(data->set.ftp_use_eprt?&mode[0]:&mode[2]);
1179        modep && *modep; modep++) {
1180     int lprtaf, eprtaf;
1181     int alen=0, plen=0;
1182     
1183     switch (sa->sa_family) {
1184     case AF_INET:
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);
1189       lprtaf = 4;
1190       eprtaf = 1;
1191       break;
1192     case AF_INET6:
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);
1197       lprtaf = 6;
1198       eprtaf = 2;
1199       break;
1200     default:
1201       ap = pp = NULL;
1202       lprtaf = eprtaf = -1;
1203       break;
1204     }
1205
1206     if (strcmp(*modep, "EPRT") == 0) {
1207       if (eprtaf < 0)
1208         continue;
1209       if (getnameinfo((struct sockaddr *)&ss, sslen,
1210                       portmsgbuf, sizeof(portmsgbuf), tmp, sizeof(tmp), niflags))
1211         continue;
1212
1213       /* do not transmit IPv6 scope identifier to the wire */
1214       if (sa->sa_family == AF_INET6) {
1215         char *q = strchr(portmsgbuf, '%');
1216           if (q)
1217             *q = '\0';
1218       }
1219
1220       result = Curl_ftpsendf(conn, "%s |%d|%s|%s|", *modep, eprtaf,
1221                              portmsgbuf, tmp);
1222       if(result)
1223         return result;
1224     } else if (strcmp(*modep, "LPRT") == 0 ||
1225                strcmp(*modep, "PORT") == 0) {
1226       int i;
1227       
1228       if (strcmp(*modep, "LPRT") == 0 && lprtaf < 0)
1229         continue;
1230       if (strcmp(*modep, "PORT") == 0 && sa->sa_family != AF_INET)
1231         continue;
1232
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)) {
1238           continue;
1239         }
1240       }
1241
1242       for (i = 0; i < alen; i++) {
1243         if (portmsgbuf[0])
1244           snprintf(tmp, sizeof(tmp), ",%u", ap[i]);
1245         else
1246           snprintf(tmp, sizeof(tmp), "%u", ap[i]);
1247         
1248         if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >=
1249             sizeof(portmsgbuf)) {
1250           continue;
1251         }
1252       }
1253       
1254       if (strcmp(*modep, "LPRT") == 0) {
1255         snprintf(tmp, sizeof(tmp), ",%d", plen);
1256         
1257         if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= sizeof(portmsgbuf))
1258           continue;
1259       }
1260
1261       for (i = 0; i < plen; i++) {
1262         snprintf(tmp, sizeof(tmp), ",%u", pp[i]);
1263         
1264         if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >=
1265             sizeof(portmsgbuf)) {
1266           continue;
1267         }
1268       }
1269       
1270       result = Curl_ftpsendf(conn, "%s %s", *modep, portmsgbuf);
1271       if(result)
1272         return result;
1273     }
1274     
1275     result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1276     if(result)
1277       return result;
1278     
1279     if (ftpcode != 200) {
1280       continue;
1281     }
1282     else
1283       break;
1284   }
1285   
1286   if (!*modep) {
1287     sclose(portsock);
1288     failf(data, "PORT command attempts failed");
1289     return CURLE_FTP_PORT_FAILED;
1290   }
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;
1295   
1296 #else
1297   /******************************************************************
1298    *
1299    * Here's a piece of IPv4-specific code coming up
1300    *
1301    */
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;
1307
1308   if(data->set.ftpport) {
1309     in_addr_t in;
1310     int rc;
1311
1312     /* First check if the given name is an IP address */
1313     in=inet_addr(data->set.ftpport);
1314
1315     if((in == CURL_INADDR_NONE) &&
1316        Curl_if2ip(data->set.ftpport, myhost, sizeof(myhost))) {
1317       rc = Curl_resolv(conn, myhost, 0, &h);
1318       if(rc == 1)
1319         rc = Curl_wait_for_resolv(conn, &h);
1320     }
1321     else {
1322       int len = strlen(data->set.ftpport);
1323       if(len>1) {
1324         rc = Curl_resolv(conn, data->set.ftpport, 0, &h);
1325         if(rc == 1)
1326           rc = Curl_wait_for_resolv(conn, &h);
1327       }
1328       if(h)
1329         strcpy(myhost, data->set.ftpport); /* buffer overflow risk */
1330     }
1331   }
1332   if(! *myhost) {
1333     /* pick a suitable default here */
1334
1335     socklen_t sslen;
1336     
1337     sslen = sizeof(sa);
1338     if (getsockname(conn->sock[FIRSTSOCKET],
1339                     (struct sockaddr *)&sa, &sslen) < 0) {
1340       failf(data, "getsockname() failed");
1341       return CURLE_FTP_PORT_FAILED;
1342     }
1343
1344     sa_filled_in = TRUE; /* the sa struct is filled in */
1345   }
1346
1347   if(h)
1348     /* when we return from here, we can forget about this */
1349     Curl_resolv_unlock(data, h);
1350
1351   if ( h || sa_filled_in) {
1352     if( (portsock = socket(AF_INET, SOCK_STREAM, 0)) >= 0 ) {
1353       int size;
1354       
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;
1359
1360       if(!sa_filled_in) {
1361         memset((char *)&sa, 0, sizeof(sa));
1362         memcpy((char *)&sa.sin_addr,
1363                h->addr->h_addr,
1364                h->addr->h_length);
1365         sa.sin_family = AF_INET;
1366         sa.sin_addr.s_addr = INADDR_ANY;
1367       }
1368
1369       sa.sin_port = 0;
1370       size = sizeof(sa);
1371       
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);
1376
1377         if(getsockname(portsock, (struct sockaddr *) &add,
1378                        &socksize)<0) {
1379           failf(data, "getsockname() failed");
1380           return CURLE_FTP_PORT_FAILED;
1381         }
1382         porttouse = ntohs(add.sin_port);
1383         
1384         if ( listen(portsock, 1) < 0 ) {
1385           failf(data, "listen(2) failed on socket");
1386           return CURLE_FTP_PORT_FAILED;
1387         }
1388       }
1389       else {
1390         failf(data, "bind(2) failed on socket");
1391         return CURLE_FTP_PORT_FAILED;
1392       }
1393     }
1394     else {
1395       failf(data, "socket(2) failed (%s)");
1396       return CURLE_FTP_PORT_FAILED;
1397     }
1398   }
1399   else {
1400     failf(data, "could't find my own IP address (%s)", myhost);
1401     return CURLE_FTP_PORT_FAILED;
1402   }
1403   {
1404 #ifdef HAVE_INET_NTOA_R
1405     char ntoa_buf[64];
1406 #endif
1407     struct in_addr in;
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));
1412
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]);
1419 #else
1420     sscanf( inet_ntoa(in), "%hu.%hu.%hu.%hu",
1421             &ip[0], &ip[1], &ip[2], &ip[3]);
1422 #endif
1423     infof(data, "Telling server to connect to %d.%d.%d.%d:%d\n",
1424           ip[0], ip[1], ip[2], ip[3], porttouse);
1425   
1426     result=Curl_ftpsendf(conn, "PORT %d,%d,%d,%d,%d,%d",
1427                          ip[0], ip[1], ip[2], ip[3],
1428                          porttouse >> 8,
1429                          porttouse & 255);
1430     if(result)
1431       return result;
1432   }
1433
1434   result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1435   if(result)
1436     return result;
1437
1438   if(ftpcode != 200) {
1439     failf(data, "Server does not grok PORT, try without it!");
1440     return CURLE_FTP_PORT_FAILED;
1441   }
1442 #endif /* end of ipv4-specific code */
1443
1444   return CURLE_OK;
1445 }
1446
1447 /***********************************************************************
1448  *
1449  * ftp_use_pasv()
1450  *
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.
1454  */
1455
1456 static
1457 CURLcode ftp_use_pasv(struct connectdata *conn,
1458                       bool *connected)
1459 {
1460   struct SessionHandle *data = conn->data;
1461   ssize_t nread;
1462   char *buf = data->state.buffer; /* this is our buffer */
1463   int ftpcode; /* receive FTP response codes in this */
1464   CURLcode result;
1465   struct Curl_dns_entry *addr=NULL;
1466   Curl_ipconnect *conninfo;
1467   int rc;
1468
1469   /*
1470     Here's the excecutive summary on what to do:
1471
1472     PASV is RFC959, expect:
1473     227 Entering Passive Mode (a1,a2,a3,a4,p1,p2)
1474
1475     LPSV is RFC1639, expect:
1476     228 Entering Long Passive Mode (4,4,a1,a2,a3,a4,2,p1,p2)
1477
1478     EPSV is RFC2428, expect:
1479     229 Entering Extended Passive Mode (|||port|)
1480
1481   */
1482
1483   const char *mode[] = { "EPSV", "PASV", NULL };
1484   int results[] = { 229, 227, 0 };
1485   int modeoff;
1486   unsigned short connectport; /* the local port connect() should use! */
1487   unsigned short newport=0; /* remote port, not necessary the local one */
1488   
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 */
1491   char newhost[48];
1492   char *newhostp=NULL;
1493   
1494   for (modeoff = (data->set.ftp_use_epsv?0:1);
1495        mode[modeoff]; modeoff++) {
1496     result = Curl_ftpsendf(conn, "%s", mode[modeoff]);
1497     if(result)
1498       return result;
1499     result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1500     if(result)
1501       return result;
1502     if (ftpcode == results[modeoff])
1503       break;
1504   }
1505
1506   if (!mode[modeoff]) {
1507     failf(data, "Odd return code after PASV");
1508     return CURLE_FTP_WEIRD_PASV_REPLY;
1509   }
1510   else if (227 == results[modeoff]) {
1511     int ip[4];
1512     int port[2];
1513     char *str=buf;
1514
1515     /*
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.
1519      *
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"
1524      */
1525       
1526     while(*str) {
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]))
1530         break;
1531       str++;
1532     }
1533
1534     if(!*str) {
1535       failf(data, "Couldn't interpret this 227-reply: %s", buf);
1536       return CURLE_FTP_WEIRD_227_FORMAT;
1537     }
1538
1539     sprintf(newhost, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
1540     newhostp = newhost;
1541     newport = (port[0]<<8) + port[1];
1542   }
1543   else if (229 == results[modeoff]) {
1544     char *ptr = strchr(buf, '(');
1545     if(ptr) {
1546       unsigned int num;
1547       char separator[4];
1548       ptr++;
1549       if(5  == sscanf(ptr, "%c%c%c%u%c",
1550                       &separator[0],
1551                       &separator[1],
1552                       &separator[2],
1553                       &num,
1554                       &separator[3])) {
1555         char sep1 = separator[0];
1556         int i;
1557
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 */
1563             break;
1564           }
1565         }
1566         if(ptr) {
1567           newport = num;
1568
1569           /* we should use the same host we already are connected to */
1570           newhostp = conn->name;
1571         }
1572       }                      
1573       else
1574         ptr=NULL;
1575     }
1576     if(!ptr) {
1577       failf(data, "Weirdly formatted EPSV reply");
1578       return CURLE_FTP_WEIRD_PASV_REPLY;
1579     }
1580   }
1581   else
1582     return CURLE_FTP_CANT_RECONNECT;
1583
1584   if(data->change.proxy && *data->change.proxy) {
1585     /*
1586      * This is a tunnel through a http proxy and we need to connect to the
1587      * proxy again here.
1588      *
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!
1591      */
1592     rc = Curl_resolv(conn, conn->proxyhost, conn->port, &addr);
1593     if(rc == 1)
1594       rc = Curl_wait_for_resolv(conn, &addr);
1595
1596     connectport =
1597       (unsigned short)conn->port; /* we connect to the proxy's port */    
1598
1599   }
1600   else {
1601     /* normal, direct, ftp connection */
1602     rc = Curl_resolv(conn, newhostp, newport, &addr);
1603     if(rc == 1)
1604       rc = Curl_wait_for_resolv(conn, &addr);
1605
1606     if(!addr) {
1607       failf(data, "Can't resolve new host %s:%d", newhostp, newport);
1608       return CURLE_FTP_CANT_GET_HOST;
1609     }
1610     connectport = newport; /* we connect to the remote port */
1611   }
1612     
1613   result = Curl_connecthost(conn,
1614                             addr,
1615                             connectport,
1616                             &conn->sock[SECONDARYSOCKET],
1617                             &conninfo,
1618                             connected);
1619
1620   Curl_resolv_unlock(data, addr); /* we're done using this address */
1621
1622   if(result)
1623     return result;
1624
1625   /*
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.
1629    */
1630   
1631   if(data->set.verbose)
1632     /* this just dumps information about this second connection */
1633     ftp_pasv_verbose(conn, conninfo, newhostp, connectport);
1634   
1635   if(data->set.tunnel_thru_httpproxy) {
1636     /* We want "seamless" FTP operations through HTTP proxy tunnel */
1637     result = Curl_ConnectHTTPProxyTunnel(conn, SECONDARYSOCKET,
1638                                          newhostp, newport);
1639     if(CURLE_OK != result)
1640       return result;
1641   }
1642
1643   return CURLE_OK;
1644 }
1645
1646 /*
1647  * Curl_ftp_nextconnect()
1648  *
1649  * This function shall be called when the second FTP connection has been
1650  * established and is confirmed connected.
1651  */
1652
1653 CURLcode Curl_ftp_nextconnect(struct connectdata *conn)
1654 {
1655   struct SessionHandle *data=conn->data;
1656   char *buf = data->state.buffer; /* this is our buffer */
1657   CURLcode result;
1658   ssize_t nread;
1659   int ftpcode; /* for ftp status */
1660
1661   /* the ftp struct is already inited in Curl_ftp_connect() */
1662   struct FTP *ftp = conn->proto.ftp;
1663   off_t *bytecountp = ftp->bytecountp;
1664
1665   if(data->set.upload) {
1666
1667     /* Set type to binary (unless specified ASCII) */
1668     result = ftp_transfertype(conn, data->set.ftp_ascii);
1669     if(result)
1670       return result;
1671
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)
1675         return result;
1676     }
1677
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. */
1683
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 */
1687
1688       /* 3. pass file-size number of bytes in the source file */
1689       /* 4. lower the infilesize counter */
1690       /* => transfer as usual */
1691
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 */
1695         off_t gottensize;
1696
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;
1700         }
1701         conn->resume_from = gottensize;
1702       }
1703
1704       if(conn->resume_from) {
1705         /* do we still game? */
1706         off_t passed=0;
1707         /* enable append instead */
1708         data->set.ftp_append = 1;
1709
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 */
1713         do {
1714           off_t readthisamountnow = (conn->resume_from - passed);
1715           off_t actuallyread;
1716
1717           if(readthisamountnow > BUFSIZE)
1718             readthisamountnow = BUFSIZE;
1719
1720           actuallyread =
1721             conn->fread(data->state.buffer, 1, (size_t)readthisamountnow,
1722                         conn->fread_in);
1723
1724           passed += actuallyread;
1725           if(actuallyread != readthisamountnow) {
1726             failf(data, "Could only read %Od bytes from the input",
1727                   passed);
1728             return CURLE_FTP_COULDNT_USE_REST;
1729           }
1730         }
1731         while(passed != conn->resume_from);
1732
1733         /* now, decrease the size of the read */
1734         if(data->set.infilesize>0) {
1735           data->set.infilesize -= conn->resume_from;
1736
1737           if(data->set.infilesize <= 0) {
1738             infof(data, "File already completely uploaded\n");
1739
1740             /* no data to transfer */
1741             result=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1742             
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; 
1746
1747             return CURLE_OK;
1748           }
1749         }
1750         /* we've passed, proceed as normal */
1751       }
1752     }
1753
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);
1758     }
1759     else {
1760       FTPSENDF(conn, "STOR %s", ftp->file);
1761     }
1762
1763     result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1764     if(result)
1765       return result;
1766
1767     if(ftpcode>=400) {
1768       failf(data, "Failed FTP upload:%s", buf+3);
1769       /* oops, we never close the sockets! */
1770       return CURLE_FTP_COULDNT_STOR_FILE;
1771     }
1772
1773     if(data->set.ftp_use_port) {
1774       /* PORT means we are now awaiting the server to connect to us. */
1775       result = AllowServerConnect(conn);
1776       if( result )
1777         return result;
1778     }
1779
1780     if(conn->ssl[SECONDARYSOCKET].use) {
1781       /* since we only have a plaintext TCP connection here, we must now
1782          do the TLS stuff */
1783       infof(data, "Doing the SSL/TLS handshake on the data stream\n");
1784       result = Curl_SSLConnect(conn, SECONDARYSOCKET);
1785       if(result)
1786         return result;
1787     }
1788
1789     *bytecountp=0;
1790
1791     /* When we know we're uploading a specified file, we can get the file
1792        size prior to the actual upload. */
1793
1794     Curl_pgrsSetUploadSize(data, (double)data->set.infilesize);
1795
1796     result = Curl_Transfer(conn, -1, -1, FALSE, NULL, /* no download */
1797                            SECONDARYSOCKET, bytecountp);
1798     if(result)
1799       return result;
1800       
1801   }
1802   else if(!data->set.no_body) {
1803     /* Retrieve file or directory */
1804     bool dirlist=FALSE;
1805     off_t downloadsize=-1;
1806
1807     if(conn->bits.use_range && conn->range) {
1808       off_t from, to;
1809       off_t totalsize=-1;
1810       char *ptr;
1811       char *ptr2;
1812
1813       from=strtoofft(conn->range, &ptr, 0);
1814       while(ptr && *ptr && (isspace((int)*ptr) || (*ptr=='-')))
1815         ptr++;
1816       to=strtoofft(ptr, &ptr2, 0);
1817       if(ptr == ptr2) {
1818         /* we didn't get any digit */
1819         to=-1;
1820       }
1821       if((-1 == to) && (from>=0)) {
1822         /* X - */
1823         conn->resume_from = from;
1824         infof(data, "FTP RANGE %Od to end of file\n", from);
1825       }
1826       else if(from < 0) {
1827         /* -Y */
1828         totalsize = -from;
1829         conn->maxdownload = -from;
1830         conn->resume_from = from;
1831         infof(data, "FTP RANGE the last %Od bytes\n", totalsize);
1832       }
1833       else {
1834         /* X-Y */
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,
1839               conn->maxdownload);
1840       }
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 */
1844     }
1845
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. */
1850       dirlist = TRUE;
1851
1852       /* Set type to ASCII */
1853       result = ftp_transfertype(conn, TRUE /* ASCII enforced */);
1854       if(result)
1855         return result;
1856
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 */
1860
1861       FTPSENDF(conn, "%s",
1862             data->set.customrequest?data->set.customrequest:
1863             (data->set.ftp_list_only?"NLST":"LIST"));
1864     }
1865     else {
1866       off_t foundsize;
1867
1868       /* Set type to binary (unless specified ASCII) */
1869       result = ftp_transfertype(conn, data->set.ftp_ascii);
1870       if(result)
1871         return result;
1872
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)
1876           return result;
1877       }
1878
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;
1887         }
1888         downloadsize = foundsize;
1889       }
1890
1891       if(conn->resume_from) {
1892
1893         /* Daniel: (August 4, 1999)
1894          *
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.
1898          *
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! ;-)
1902          */
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. */
1909         }
1910         else {
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;
1919             }
1920             /* convert to size to download */
1921             downloadsize = -conn->resume_from;
1922             /* download from where? */
1923             conn->resume_from = foundsize - downloadsize;
1924           }
1925           else {
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;
1930             }
1931             /* Now store the number of bytes we are expected to download */
1932             downloadsize = foundsize-conn->resume_from;
1933           }
1934         }
1935
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");
1940
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;
1944           return CURLE_OK;
1945         }
1946         
1947         /* Set resume file transfer offset */
1948         infof(data, "Instructs server to resume from offset %Od\n",
1949               conn->resume_from);
1950
1951         FTPSENDF(conn, "REST %Od", conn->resume_from);
1952
1953         result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1954         if(result)
1955           return result;
1956
1957         if(ftpcode != 350) {
1958           failf(data, "Couldn't use REST: %s", buf+4);
1959           return CURLE_FTP_COULDNT_USE_REST;
1960         }
1961       }
1962
1963       FTPSENDF(conn, "RETR %s", ftp->file);
1964     }
1965
1966     result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1967     if(result)
1968       return result;
1969
1970     if((ftpcode == 150) || (ftpcode == 125)) {
1971
1972       /*
1973         A;
1974         150 Opening BINARY mode data connection for /etc/passwd (2241
1975         bytes).  (ok, the file is being transfered)
1976         
1977         B:
1978         150 Opening ASCII mode data connection for /bin/ls 
1979
1980         C:
1981         150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes).
1982
1983         D:
1984         150 Opening ASCII mode data connection for /linux/fisk/kpanelrc (0.0.0.0,0) (545 bytes).
1985           
1986         E:
1987         125 Data connection already open; Transfer starting. */
1988
1989       off_t size=-1; /* default unknown size */
1990
1991
1992       /*
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.
1997        *
1998        * Debugging help from Salvatore Sorrentino on February 26, 2003.
1999        */
2000
2001       if(!dirlist &&
2002          !data->set.ftp_ascii &&
2003          (downloadsize < 1)) {
2004         /*
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.
2009          *
2010          * Example D above makes this parsing a little tricky */
2011         char *bytes;
2012         bytes=strstr(buf, " bytes");
2013         if(bytes--) {
2014           int in=bytes-buf;
2015           /* this is a hint there is size information in there! ;-) */
2016           while(--in) {
2017             /* scan for the parenthesis and break there */
2018             if('(' == *bytes)
2019               break;
2020             /* if only skip digits, or else we're in deep trouble */
2021             if(!isdigit((int)*bytes)) {
2022               bytes=NULL;
2023               break;
2024             }
2025             /* one more estep backwards */
2026             bytes--;
2027           }
2028           /* only if we have nothing but digits: */
2029           if(bytes++) {
2030             /* get the number! */
2031             size = strtoofft(bytes, NULL, 0);
2032           }
2033             
2034         }
2035       }
2036       else if(downloadsize > -1)
2037         size = downloadsize;
2038
2039       if(data->set.ftp_use_port) {
2040         result = AllowServerConnect(conn);
2041         if( result )
2042           return result;
2043       }
2044
2045       if(conn->ssl[SECONDARYSOCKET].use) {
2046         /* since we only have a plaintext TCP connection here, we must now
2047            do the TLS stuff */
2048         infof(data, "Doing the SSL/TLS handshake on the data stream\n");
2049         result = Curl_SSLConnect(conn, SECONDARYSOCKET);
2050         if(result)
2051           return result;
2052       }
2053
2054       if(size > conn->maxdownload && conn->maxdownload > 0)
2055         size = conn->size = conn->maxdownload;
2056
2057       infof(data, "Getting file with size: %Od\n", size);
2058
2059       /* FTP download: */
2060       result=Curl_Transfer(conn, SECONDARYSOCKET, size, FALSE,
2061                            bytecountp,
2062                            -1, NULL); /* no upload here */
2063       if(result)
2064         return result;
2065     }
2066     else {
2067       if(dirlist && (ftpcode == 450)) {
2068         /* simply no matching files */
2069         ftp->no_transfer = TRUE; /* don't think we should download anything */
2070       }
2071       else {
2072         failf(data, "%s", buf+4);
2073         return CURLE_FTP_COULDNT_RETR_FILE;
2074       }
2075     }
2076         
2077   }
2078   /* end of transfer */
2079
2080   return CURLE_OK;
2081 }
2082
2083 /***********************************************************************
2084  *
2085  * ftp_perform()
2086  *
2087  * This is the actual DO function for FTP. Get a file/directory according to
2088  * the options previously setup.
2089  */
2090
2091 static
2092 CURLcode ftp_perform(struct connectdata *conn,
2093                      bool *connected)  /* for the TCP connect status after
2094                                           PASV / PORT */
2095 {
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 */
2100
2101   /* the ftp struct is already inited in Curl_ftp_connect() */
2102   struct FTP *ftp = conn->proto.ftp;
2103
2104   /* Send any QUOTE strings? */
2105   if(data->set.quote) {
2106     if ((result = ftp_sendquote(conn, data->set.quote)) != CURLE_OK)
2107       return result;
2108   }
2109
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)
2115       return result;
2116   }
2117
2118   {
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)
2124         return result;
2125     }
2126   }
2127
2128   /* Requested time of file or time-depended transfer? */
2129   if((data->set.get_filetime || data->set.timecondition) &&
2130      ftp->file) {
2131     result = ftp_getfiletime(conn, ftp->file);
2132     switch( result )
2133       {
2134       case CURLE_FTP_COULDNT_RETR_FILE:
2135       case CURLE_OK:
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:
2140             default:
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 */
2144                 return CURLE_OK;
2145               }
2146               break;
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 */
2151                 return CURLE_OK;
2152               }
2153               break;
2154             } /* switch */
2155           }
2156           else {
2157             infof(data, "Skipping time comparison\n");
2158           }
2159         }
2160         break;
2161       default:
2162         return result;
2163       } /* switch */
2164   }
2165
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
2168      date. */
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
2172        size! */
2173     off_t filesize;
2174     ssize_t nread;
2175     int ftpcode;
2176
2177     ftp->no_transfer = TRUE; /* this means no actual transfer is made */
2178     
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);
2182     if(result)
2183       return result;
2184
2185     /* failing to get size is not a serious error */
2186     result = ftp_getsize(conn, ftp->file, &filesize);
2187
2188     if(CURLE_OK == result) {
2189       sprintf(buf, "Content-Length: %Od\r\n", filesize);
2190       result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
2191       if(result)
2192         return result;
2193     }
2194
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);
2199
2200     if ((CURLE_OK == result) && (ftpcode == 350)) {
2201       result = Curl_client_write(data, CLIENTWRITE_BOTH,
2202                                  (char *)"Accept-ranges: bytes\r\n", 0);
2203       if(result)
2204         return result;
2205     }
2206
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. */
2209
2210 #ifdef HAVE_STRFTIME
2211     if(data->set.get_filetime && (data->info.filetime>=0) ) {
2212       struct tm *tm;
2213 #ifdef HAVE_GMTIME_R
2214       struct tm buffer;
2215       tm = (struct tm *)gmtime_r((time_t *)&data->info.filetime, &buffer);
2216 #else
2217       tm = gmtime((time_t *)&data->info.filetime);
2218 #endif
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",
2221                tm);
2222       result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
2223       if(result)
2224         return result;
2225     }
2226 #endif
2227
2228     return CURLE_OK;
2229   }
2230
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" */
2242     }
2243   }
2244   else {
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");
2249   }
2250   
2251   return result;
2252 }
2253
2254 /***********************************************************************
2255  *
2256  * Curl_ftp()
2257  *
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).
2260  *
2261  * The input argument is already checked for validity.
2262  */
2263 CURLcode Curl_ftp(struct connectdata *conn)
2264 {
2265   CURLcode retcode=CURLE_OK;
2266   bool connected=0;
2267
2268   struct SessionHandle *data = conn->data;
2269   struct FTP *ftp;
2270
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 */
2275
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 */
2279
2280   Curl_pgrsSetUploadCounter(data, 0);
2281   Curl_pgrsSetDownloadCounter(data, 0);
2282   Curl_pgrsSetUploadSize(data, 0);
2283   Curl_pgrsSetDownloadSize(data, 0);
2284
2285   /*  fixed : initialize ftp->dirs[xxx] to NULL !
2286       is done in Curl_ftp_connect() */
2287
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);
2292
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);
2300     
2301       if (!ftp->dirs[path_part]) { /* run out of memory ... */
2302         failf(data, "no memory");
2303         freedirs(ftp);
2304         return CURLE_OUT_OF_MEMORY;
2305       }
2306     }
2307     else {
2308       cur_pos = slash_pos + 1; /* jump to the rest of the string */
2309       continue;
2310     }
2311
2312     if(!retcode) {
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");
2318         freedirs(ftp);
2319         return CURLE_URL_MALFORMAT;
2320       }
2321     }
2322   }
2323
2324   ftp->file = cur_pos;  /* the rest is the file name */
2325
2326   if(*ftp->file) {
2327     ftp->file = curl_unescape(ftp->file, 0);
2328     if(NULL == ftp->file) {
2329       freedirs(ftp);
2330       failf(data, "no memory");
2331       return CURLE_OUT_OF_MEMORY;
2332     }
2333   }
2334   else
2335     ftp->file=NULL; /* instead of point to a zero byte, we make it a NULL
2336                        pointer */
2337   
2338   retcode = ftp_perform(conn, &connected);
2339
2340   if(CURLE_OK == retcode) {
2341     if(connected)
2342       retcode = Curl_ftp_nextconnect(conn);
2343
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;
2348     }
2349
2350     if(ftp->no_transfer)
2351       /* no data to transfer */
2352       retcode=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);        
2353     else if(!connected)
2354       /* since we didn't connect now, we want do_more to get called */
2355       conn->bits.do_more = TRUE;
2356   }
2357   else
2358     freedirs(ftp);
2359
2360   return retcode;
2361 }
2362
2363 /***********************************************************************
2364  *
2365  * Curl_ftpsendf()
2366  *
2367  * Sends the formated string as a ftp command to a ftp server
2368  *
2369  * NOTE: we build the command in a fixed-length buffer, which sets length
2370  * restrictions on the command!
2371  */
2372 CURLcode Curl_ftpsendf(struct connectdata *conn,
2373                        const char *fmt, ...)
2374 {
2375   ssize_t bytes_written;
2376   char s[256];
2377   ssize_t write_len;
2378   char *sptr=s;
2379   CURLcode res = CURLE_OK;
2380
2381   va_list ap;
2382   va_start(ap, fmt);
2383   vsnprintf(s, 250, fmt, ap);
2384   va_end(ap);
2385   
2386   strcat(s, "\r\n"); /* append a trailing CRLF */
2387
2388   bytes_written=0;
2389   write_len = strlen(s);
2390
2391   do {
2392     res = Curl_write(conn, conn->sock[FIRSTSOCKET], sptr, write_len,
2393                      &bytes_written);
2394
2395     if(CURLE_OK != res)
2396       break;
2397
2398     if(conn->data->set.verbose)
2399       Curl_debug(conn->data, CURLINFO_HEADER_OUT, sptr, bytes_written);
2400
2401     if(bytes_written != write_len) {
2402       write_len -= bytes_written;
2403       sptr += bytes_written;
2404     }
2405     else
2406       break;
2407   } while(1);
2408
2409   return res;
2410 }
2411
2412 /***********************************************************************
2413  *
2414  * Curl_ftp_quit()
2415  *
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
2419  * connection.
2420  *
2421  */
2422 CURLcode Curl_ftp_quit(struct connectdata *conn)
2423 {
2424   int nread;
2425   int ftpcode;
2426   CURLcode ret;
2427
2428   ret = Curl_ftpsendf(conn, "%s", "QUIT");
2429   if(CURLE_OK == ret)
2430     ret = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2431
2432   return ret;
2433 }
2434
2435 /***********************************************************************
2436  *
2437  * Curl_ftp_disconnect()
2438  *
2439  * Disconnect from an FTP server. Cleanup protocol-specific per-connection
2440  * resources
2441  */
2442 CURLcode Curl_ftp_disconnect(struct connectdata *conn)
2443 {
2444   struct FTP *ftp= conn->proto.ftp;
2445
2446 #if 0
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.
2450      
2451      Until fixed, we keep this #if 0'ed. To be fixed in 7.11.1. Stay tuned.
2452   */
2453   (void)Curl_ftp_quit(conn); /* ignore errors on the QUIT */
2454 #endif
2455
2456   /* The FTP session may or may not have been allocated/setup at this point! */
2457   if(ftp) {
2458     if(ftp->entrypath)
2459       free(ftp->entrypath);
2460     if(ftp->cache) {
2461       free(ftp->cache);
2462       ftp->cache = NULL;
2463     }
2464     if(ftp->file) {
2465       free(ftp->file);
2466       ftp->file = NULL; /* zero */
2467     }
2468     freedirs(ftp);
2469   }
2470   return CURLE_OK;
2471 }
2472
2473 /***********************************************************************
2474  *
2475  * ftp_mkd()
2476  *
2477  * Makes a directory on the FTP server.
2478  *
2479  * Calls failf()
2480  */
2481 CURLcode ftp_mkd(struct connectdata *conn, char *path)
2482 {
2483   CURLcode result=CURLE_OK;
2484   int ftpcode; /* for ftp status */
2485   ssize_t nread;
2486
2487   /* Create a directory on the remote server */
2488   FTPSENDF(conn, "MKD %s", path);
2489
2490   result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2491   if(result)
2492     return result;
2493   
2494   switch(ftpcode) {
2495   case 257:
2496     /* success! */
2497     infof( conn->data , "Created remote directory %s\n" , path );
2498     break;
2499   case 550:
2500     failf(conn->data, "Permission denied to make directory %s", path);
2501     result = CURLE_FTP_ACCESS_DENIED;
2502     break;
2503   default:
2504     failf(conn->data, "unrecognized MKD response: %d", ftpcode );
2505     result = CURLE_FTP_ACCESS_DENIED;
2506     break;
2507   }
2508   return  result;
2509 }
2510
2511 /***********************************************************************
2512  *
2513  * ftp_cwd()
2514  *
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.
2518  *
2519  * This function does NOT call failf().
2520  */
2521 static 
2522 CURLcode ftp_cwd(struct connectdata *conn, char *path)
2523 {
2524   ssize_t nread;
2525   int     ftpcode;
2526   CURLcode result;
2527   
2528   FTPSENDF(conn, "CWD %s", path);
2529   result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2530   if (!result) {
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;
2536   }
2537
2538   return result;
2539 }
2540
2541 /***********************************************************************
2542  *
2543  * ftp_cwd_and_mkd()
2544  *
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.
2547  *
2548  */
2549 static CURLcode ftp_cwd_and_mkd(struct connectdata *conn, char *path)
2550 {
2551   CURLcode result;
2552   
2553   result = ftp_cwd(conn, path);
2554   if (result) {
2555     if(conn->data->set.ftp_create_missing_dirs) {
2556       result = ftp_mkd(conn, path);
2557       if (result)
2558         /* ftp_mkd() calls failf() itself */
2559         return result;
2560       result = ftp_cwd(conn, path);
2561     }
2562     if(result)
2563       failf(conn->data, "Couldn't cd to %s", path);
2564   }
2565   return result;
2566 }
2567
2568 #endif /* CURL_DISABLE_FTP */