use the HAVE_KRB4 define instead of just KRB4
[platform/upstream/curl.git] / lib / ftp.c
1 /***************************************************************************
2  *                                  _   _ ____  _     
3  *  Project                     ___| | | |  _ \| |    
4  *                             / __| | | | |_) | |    
5  *                            | (__| |_| |  _ <| |___ 
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2003, 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 "strequal.h"
86 #include "ssluse.h"
87 #include "connect.h"
88
89 #if defined(HAVE_INET_NTOA_R) && !defined(HAVE_INET_NTOA_R_DECL)
90 #include "inet_ntoa_r.h"
91 #endif
92
93 #define _MPRINTF_REPLACE /* use our functions only */
94 #include <curl/mprintf.h>
95
96 /* The last #include file should be: */
97 #ifdef CURLDEBUG
98 #include "memdebug.h"
99 #endif
100
101 /* Local API functions */
102 static CURLcode ftp_sendquote(struct connectdata *conn,
103                               struct curl_slist *quote);
104 static CURLcode ftp_cwd(struct connectdata *conn, char *path);
105 static CURLcode ftp_mkd(struct connectdata *conn, char *path);
106 static CURLcode ftp_cwd_and_mkd(struct connectdata *conn, char *path);
107
108 /* easy-to-use macro: */
109 #define FTPSENDF(x,y,z) if((result = Curl_ftpsendf(x,y,z))) return result
110
111 static void freedirs(struct FTP *ftp)
112 {
113   int i;
114   for (i=0; ftp->dirs[i]; i++){
115     free(ftp->dirs[i]);
116     ftp->dirs[i]=NULL;
117   }
118 }
119
120 /***********************************************************************
121  *
122  * AllowServerConnect()
123  *
124  * When we've issue the PORT command, we have told the server to connect
125  * to us. This function will sit and wait here until the server has
126  * connected.
127  *
128  */
129 static CURLcode AllowServerConnect(struct connectdata *conn)
130 {
131   fd_set rdset;
132   struct timeval dt;
133   struct SessionHandle *data = conn->data;
134   int sock = conn->sock[SECONDARYSOCKET];
135   
136   FD_ZERO(&rdset);
137
138   FD_SET(sock, &rdset);
139
140   /* we give the server 10 seconds to connect to us */
141   dt.tv_sec = 10;
142   dt.tv_usec = 0;
143
144   switch (select(sock+1, &rdset, NULL, NULL, &dt)) {
145   case -1: /* error */
146     /* let's die here */
147     failf(data, "Error while waiting for server connect");
148     return CURLE_FTP_PORT_FAILED;
149   case 0:  /* timeout */
150     /* let's die here */
151     failf(data, "Timeout while waiting for server connect");
152     return CURLE_FTP_PORT_FAILED;
153   default:
154     /* we have received data here */
155     {
156       int s;
157       size_t size = sizeof(struct sockaddr_in);
158       struct sockaddr_in add;
159
160       getsockname(sock, (struct sockaddr *) &add, (socklen_t *)&size);
161       s=accept(sock, (struct sockaddr *) &add, (socklen_t *)&size);
162
163       sclose(sock); /* close the first socket */
164
165       if (-1 == s) {
166         /* DIE! */
167         failf(data, "Error accept()ing server connect");
168         return CURLE_FTP_PORT_FAILED;
169       }
170       infof(data, "Connection accepted from server\n");
171
172       conn->sock[SECONDARYSOCKET] = s;
173       Curl_nonblock(s, TRUE); /* enable non-blocking */
174     }
175     break;
176   }
177
178   return CURLE_OK;
179 }
180
181
182 /* --- parse FTP server responses --- */
183
184 /*
185  * Curl_GetFTPResponse() is supposed to be invoked after each command sent to
186  * a remote FTP server. This function will wait and read all lines of the
187  * response and extract the relevant return code for the invoking function.
188  */
189
190 CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */
191                              struct connectdata *conn,
192                              int *ftpcode) /* return the ftp-code */
193 {
194   /* Brand new implementation.
195    * We cannot read just one byte per read() and then go back to select()
196    * as it seems that the OpenSSL read() stuff doesn't grok that properly.
197    *
198    * Alas, read as much as possible, split up into lines, use the ending
199    * line in a response or continue reading.  */
200
201   int sockfd = conn->sock[FIRSTSOCKET];
202   int perline; /* count bytes per line */
203   bool keepon=TRUE;
204   ssize_t gotbytes;
205   char *ptr;
206   int timeout;              /* timeout in seconds */
207   struct timeval interval;
208   fd_set rkeepfd;
209   fd_set readfd;
210   struct SessionHandle *data = conn->data;
211   char *line_start;
212   int code=0; /* default ftp "error code" to return */
213   char *buf = data->state.buffer;
214   CURLcode result = CURLE_OK;
215   struct FTP *ftp = conn->proto.ftp;
216   struct timeval now = Curl_tvnow();
217
218   if (ftpcode)
219     *ftpcode = 0; /* 0 for errors */
220
221   FD_ZERO (&readfd);            /* clear it */
222   FD_SET (sockfd, &readfd);     /* read socket */
223
224   /* get this in a backup variable to be able to restore it on each lap in the
225      select() loop */
226   rkeepfd = readfd;
227
228   ptr=buf;
229   line_start = buf;
230
231   *nreadp=0;
232   perline=0;
233   keepon=TRUE;
234
235   while((*nreadp<BUFSIZE) && (keepon && !result)) {
236     /* check and reset timeout value every lap */
237     if(data->set.ftp_response_timeout )
238       /* if CURLOPT_FTP_RESPONSE_TIMEOUT is set, use that to determine
239          remaining time.  Also, use "now" as opposed to "conn->now"
240          because ftp_response_timeout is only supposed to govern
241          the response for any given ftp response, not for the time
242          from connect to the given ftp response. */
243       timeout = data->set.ftp_response_timeout - /* timeout time */
244         Curl_tvdiff(Curl_tvnow(), now)/1000; /* spent time */
245     else if(data->set.timeout)
246       /* if timeout is requested, find out how much remaining time we have */
247       timeout = data->set.timeout - /* timeout time */
248         Curl_tvdiff(Curl_tvnow(), conn->now)/1000; /* spent time */
249     else
250       /* Even without a requested timeout, we only wait response_time
251          seconds for the full response to arrive before we bail out */
252       timeout = ftp->response_time -
253         Curl_tvdiff(Curl_tvnow(), now)/1000; /* spent time */
254
255     if(timeout <=0 ) {
256       failf(data, "Transfer aborted due to timeout");
257       return CURLE_OPERATION_TIMEDOUT; /* already too little time */
258     }
259
260     if(!ftp->cache) {
261       readfd = rkeepfd;            /* set every lap */
262       interval.tv_sec = 1; /* use 1 second timeout intervals */
263       interval.tv_usec = 0;
264
265       switch (select (sockfd+1, &readfd, NULL, NULL, &interval)) {
266       case -1: /* select() error, stop reading */
267         result = CURLE_RECV_ERROR;
268         failf(data, "Transfer aborted due to select() error: %d", errno);
269         break;
270       case 0: /* timeout */
271         if(Curl_pgrsUpdate(conn))
272           return CURLE_ABORTED_BY_CALLBACK;
273         continue; /* just continue in our loop for the timeout duration */
274
275       default:
276         break;
277       }
278     }
279     if(CURLE_OK == result) {
280       /*
281        * This code previously didn't use the kerberos sec_read() code
282        * to read, but when we use Curl_read() it may do so. Do confirm
283        * that this is still ok and then remove this comment!
284        */
285       if(ftp->cache) {
286         /* we had data in the "cache", copy that instead of doing an actual
287            read */
288         memcpy(ptr, ftp->cache, ftp->cache_size);
289         gotbytes = ftp->cache_size;
290         free(ftp->cache);    /* free the cache */
291         ftp->cache = NULL;   /* clear the pointer */
292         ftp->cache_size = 0; /* zero the size just in case */
293       }
294       else {
295         int res = Curl_read(conn, sockfd, ptr, BUFSIZE-*nreadp, &gotbytes);
296         if(res < 0)
297           /* EWOULDBLOCK */
298           continue; /* go looping again */
299
300         if(CURLE_OK != res)
301           keepon = FALSE;
302       }
303
304       if(!keepon)
305         ;
306       else if(gotbytes <= 0) {
307         keepon = FALSE;
308         result = CURLE_RECV_ERROR;
309         failf(data, "Connection aborted");
310       }
311       else {
312         /* we got a whole chunk of data, which can be anything from one
313          * byte to a set of lines and possible just a piece of the last
314          * line */
315         int i;
316
317         *nreadp += gotbytes;
318         for(i = 0; i < gotbytes; ptr++, i++) {
319           perline++;
320           if(*ptr=='\n') {
321             /* a newline is CRLF in ftp-talk, so the CR is ignored as
322                the line isn't really terminated until the LF comes */
323
324             /* output debug output if that is requested */
325             if(data->set.verbose)
326               Curl_debug(data, CURLINFO_HEADER_IN, line_start, perline);
327
328             /*
329              * We pass all response-lines to the callback function registered
330              * for "headers". The response lines can be seen as a kind of
331              * headers.
332              */
333             result = Curl_client_write(data, CLIENTWRITE_HEADER,
334                                        line_start, perline);
335             if(result)
336               return result;
337                                        
338 #define lastline(line) (isdigit((int)line[0]) && isdigit((int)line[1]) && \
339                         isdigit((int)line[2]) && (' ' == line[3]))
340
341             if(perline>3 && lastline(line_start)) {
342               /* This is the end of the last line, copy the last
343                * line to the start of the buffer and zero terminate,
344                * for old times sake (and krb4)! */
345               char *meow;
346               int n;
347               for(meow=line_start, n=0; meow<ptr; meow++, n++)
348                 buf[n] = *meow;
349               *meow=0; /* zero terminate */
350               keepon=FALSE;
351               line_start = ptr+1; /* advance pointer */
352               i++; /* skip this before getting out */
353               break;
354             }
355             perline=0; /* line starts over here */
356             line_start = ptr+1;
357           }
358         }
359         if(!keepon && (i != gotbytes)) {
360           /* We found the end of the response lines, but we didn't parse the
361              full chunk of data we have read from the server. We therefore
362              need to store the rest of the data to be checked on the next
363              invoke as it may actually contain another end of response
364              already!  Cleverly figured out by Eric Lavigne in December
365              2001. */
366           ftp->cache_size = gotbytes - i;
367           ftp->cache = (char *)malloc(ftp->cache_size);
368           if(ftp->cache)
369             memcpy(ftp->cache, line_start, ftp->cache_size);
370           else
371             return CURLE_OUT_OF_MEMORY; /**BANG**/
372         }
373       } /* there was data */
374     } /* if(no error) */
375   } /* while there's buffer left and loop is requested */
376
377   if(!result)
378     code = atoi(buf);
379
380 #ifdef HAVE_KRB4
381   /* handle the security-oriented responses 6xx ***/
382   /* FIXME: some errorchecking perhaps... ***/
383   switch(code) {
384   case 631:
385     Curl_sec_read_msg(conn, buf, prot_safe);
386     break;
387   case 632:
388     Curl_sec_read_msg(conn, buf, prot_private);
389     break;
390   case 633:
391     Curl_sec_read_msg(conn, buf, prot_confidential);
392     break;
393   default:
394     /* normal ftp stuff we pass through! */
395     break;
396   }
397 #endif
398
399   if(ftpcode)
400     *ftpcode=code; /* return the initial number like this */
401
402   /* store the latest code for later retrieval */
403   conn->data->info.httpcode=code;
404
405   return result;
406 }
407
408 static char *ftpauth[]= {
409   "SSL", "TLS", NULL
410 };
411
412 /*
413  * Curl_ftp_connect() should do everything that is to be considered a part of
414  * the connection phase.
415  */
416 CURLcode Curl_ftp_connect(struct connectdata *conn)
417 {
418   /* this is FTP and no proxy */
419   ssize_t nread;
420   struct SessionHandle *data=conn->data;
421   char *buf = data->state.buffer; /* this is our buffer */
422   struct FTP *ftp;
423   CURLcode result;
424   int ftpcode, try;
425
426   ftp = (struct FTP *)malloc(sizeof(struct FTP));
427   if(!ftp)
428     return CURLE_OUT_OF_MEMORY;
429
430   memset(ftp, 0, sizeof(struct FTP));
431   conn->proto.ftp = ftp;
432
433   /* We always support persistant connections on ftp */
434   conn->bits.close = FALSE;
435
436   /* get some initial data into the ftp struct */
437   ftp->bytecountp = &conn->bytecount;
438
439   /* no need to duplicate them, this connectdata struct won't change */
440   ftp->user = conn->user;
441   ftp->passwd = conn->passwd;
442   ftp->response_time = 3600; /* set default response time-out */
443
444   if (data->set.tunnel_thru_httpproxy) {
445     /* We want "seamless" FTP operations through HTTP proxy tunnel */
446     result = Curl_ConnectHTTPProxyTunnel(conn, FIRSTSOCKET,
447                                          conn->hostname, conn->remote_port);
448     if(CURLE_OK != result)
449       return result;
450   }
451
452   if(conn->protocol & PROT_FTPS) {
453     /* FTPS is simply ftp with SSL for the control channel */
454     /* now, perform the SSL initialization for this socket */
455     result = Curl_SSLConnect(conn, FIRSTSOCKET);
456     if(result)
457       return result;
458   }
459
460   /* The first thing we do is wait for the "220*" line: */
461   result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
462   if(result)
463     return result;
464
465   if(ftpcode != 220) {
466     failf(data, "This doesn't seem like a nice ftp-server response");
467     return CURLE_FTP_WEIRD_SERVER_REPLY;
468   }
469
470 #ifdef HAVE_KRB4
471   /* if not anonymous login, try a secure login */
472   if(data->set.krb4) {
473
474     /* request data protection level (default is 'clear') */
475     Curl_sec_request_prot(conn, "private");
476
477     /* We set private first as default, in case the line below fails to
478        set a valid level */
479     Curl_sec_request_prot(conn, data->set.krb4_level);
480
481     if(Curl_sec_login(conn) != 0)
482       infof(data, "Logging in with password in cleartext!\n");
483     else
484       infof(data, "Authentication successful\n");
485   }
486 #endif
487
488   if(data->set.ftp_ssl && !conn->ssl[FIRSTSOCKET].use) {
489     /* we don't have a SSL/TLS connection, try a FTPS connection now */
490
491     for (try = 0; ftpauth[try]; try++) {
492
493       FTPSENDF(conn, "AUTH %s", ftpauth[try]);
494
495       result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
496
497       if(result)
498         return result;
499
500       /* RFC2228 (page 5) says:
501        *
502        * If the server is willing to accept the named security mechanism, and
503        * does not require any security data, it must respond with reply code
504        * 234/334.
505        */
506
507       if((ftpcode == 234) || (ftpcode == 334)) {
508         result = Curl_SSLConnect(conn, FIRSTSOCKET);
509         if(result)
510           return result;
511         conn->protocol |= PROT_FTPS;
512         conn->ssl[SECONDARYSOCKET].use = FALSE; /* clear-text data */
513         break;
514       }
515     }
516   }
517   
518   /* send USER */
519   FTPSENDF(conn, "USER %s", ftp->user?ftp->user:"");
520
521   /* wait for feedback */
522   result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
523   if(result)
524     return result;
525
526   if(ftpcode == 530) {
527     /* 530 User ... access denied
528        (the server denies to log the specified user) */
529     failf(data, "Access denied: %s", &buf[4]);
530     return CURLE_FTP_ACCESS_DENIED;
531   }
532   else if(ftpcode == 331) {
533     /* 331 Password required for ...
534        (the server requires to send the user's password too) */
535     FTPSENDF(conn, "PASS %s", ftp->passwd?ftp->passwd:"");
536     result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
537     if(result)
538       return result;
539
540     if(ftpcode == 530) {
541       /* 530 Login incorrect.
542          (the username and/or the password are incorrect) */
543       failf(data, "the username and/or the password are incorrect");
544       return CURLE_FTP_USER_PASSWORD_INCORRECT;
545     }
546     else if(ftpcode == 230) {
547       /* 230 User ... logged in.
548          (user successfully logged in) */
549         
550       infof(data, "We have successfully logged in\n");
551     }
552     else {
553       failf(data, "Odd return code after PASS");
554       return CURLE_FTP_WEIRD_PASS_REPLY;
555     }
556   }
557   else if(buf[0] == '2') {
558     /* 230 User ... logged in.
559        (the user logged in without password) */
560     infof(data, "We have successfully logged in\n");
561     if (conn->ssl[FIRSTSOCKET].use) {
562 #ifdef HAVE_KRB4
563         /* we are logged in (with Kerberos)
564          * now set the requested protection level
565          */
566     if(conn->sec_complete)
567       Curl_sec_set_protection_level(conn);
568
569     /* we may need to issue a KAUTH here to have access to the files
570      * do it if user supplied a password
571      */
572     if(conn->passwd && *conn->passwd) {
573       result = Curl_krb_kauth(conn);
574       if(result)
575         return result;
576     }
577 #endif
578   }
579   }
580   else {
581     failf(data, "Odd return code after USER");
582     return CURLE_FTP_WEIRD_USER_REPLY;
583   }
584
585   if(conn->ssl[FIRSTSOCKET].use) {
586     /* PBSZ = PROTECTION BUFFER SIZE.
587
588        The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says:
589
590        Specifically, the PROT command MUST be preceded by a PBSZ command
591        and a PBSZ command MUST be preceded by a successful security data
592        exchange (the TLS negotiation in this case)
593
594        ... (and on page 8):
595          
596        Thus the PBSZ command must still be issued, but must have a parameter
597        of '0' to indicate that no buffering is taking place and the data
598        connection should not be encapsulated.
599     */
600     FTPSENDF(conn, "PBSZ %d", 0);
601     result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
602     if(result)
603       return result;
604
605     /* For TLS, the data connection can have one of two security levels.
606
607        1)Clear (requested by 'PROT C')
608
609        2)Private (requested by 'PROT P')
610     */
611     if(!conn->ssl[SECONDARYSOCKET].use) {
612       FTPSENDF(conn, "PROT %c", 'P');
613       result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
614       if(result)
615         return result;
616     
617       if(ftpcode == 200)
618         /* We have enabled SSL for the data connection! */
619         conn->ssl[SECONDARYSOCKET].use = TRUE;
620
621       /* FTP servers typically responds with 500 if they decide to reject
622          our 'P' request */
623     }
624   }
625
626   /* send PWD to discover our entry point */
627   FTPSENDF(conn, "PWD", NULL);
628
629   /* wait for feedback */
630   result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
631   if(result)
632     return result;
633
634   if(ftpcode == 257) {
635     char *dir = (char *)malloc(nread+1);
636     char *store=dir;
637     char *ptr=&buf[4]; /* start on the first letter */
638
639     if(!dir)
640       return CURLE_OUT_OF_MEMORY;
641     
642     /* Reply format is like
643        257<space>"<directory-name>"<space><commentary> and the RFC959 says
644
645        The directory name can contain any character; embedded double-quotes
646        should be escaped by double-quotes (the "quote-doubling" convention).
647     */
648     if('\"' == *ptr) {
649       /* it started good */
650       ptr++;
651       while(ptr && *ptr) {
652         if('\"' == *ptr) {
653           if('\"' == ptr[1]) {
654             /* "quote-doubling" */
655             *store = ptr[1];
656             ptr++;
657           }
658           else {
659             /* end of path */
660             *store = '\0'; /* zero terminate */
661             break; /* get out of this loop */
662           }
663         }
664         else
665           *store = *ptr;
666         store++;
667         ptr++;
668       }
669       ftp->entrypath =dir; /* remember this */
670       infof(data, "Entry path is '%s'\n", ftp->entrypath);
671     }
672     else {
673       /* couldn't get the path */
674       free(dir);
675       infof(data, "Failed to figure out path\n");
676     }
677
678   }
679   else {
680     /* We couldn't read the PWD response! */
681   }
682
683   return CURLE_OK;
684 }
685
686 /***********************************************************************
687  *
688  * Curl_ftp_done()
689  *
690  * The DONE function. This does what needs to be done after a single DO has
691  * performed.
692  *
693  * Input argument is already checked for validity.
694  */
695 CURLcode Curl_ftp_done(struct connectdata *conn)
696 {
697   struct SessionHandle *data = conn->data;
698   struct FTP *ftp = conn->proto.ftp;
699   ssize_t nread;
700   int ftpcode;
701   CURLcode result=CURLE_OK;
702
703   /* free the dir tree parts */
704   freedirs(ftp);
705
706   if(ftp->file) {
707     free(ftp->file);
708     ftp->file = NULL;
709   }
710
711   if(data->set.upload) {
712     if((-1 != data->set.infilesize) &&
713        (data->set.infilesize != *ftp->bytecountp) &&
714        !data->set.crlf) {
715       failf(data, "Uploaded unaligned file size (%d out of %d bytes)",
716             *ftp->bytecountp, data->set.infilesize);
717       conn->bits.close = TRUE; /* close this connection since we don't
718                                   know what state this error leaves us in */
719       return CURLE_PARTIAL_FILE;
720     }
721   }
722   else {
723     if((-1 != conn->size) && (conn->size != *ftp->bytecountp) &&
724        (conn->maxdownload != *ftp->bytecountp)) {
725       failf(data, "Received only partial file: %d bytes", *ftp->bytecountp);
726       conn->bits.close = TRUE; /* close this connection since we don't
727                                   know what state this error leaves us in */
728       return CURLE_PARTIAL_FILE;
729     }
730     else if(!ftp->dont_check &&
731             !*ftp->bytecountp &&
732             (conn->size>0)) {
733       /* We consider this an error, but there's no true FTP error received
734          why we need to continue to "read out" the server response too.
735          We don't want to leave a "waiting" server reply if we'll get told
736          to make a second request on this same connection! */
737       failf(data, "No data was received!");
738       result = CURLE_FTP_COULDNT_RETR_FILE;
739     }
740   }
741
742 #ifdef HAVE_KRB4
743   Curl_sec_fflush_fd(conn, conn->sock[SECONDARYSOCKET]);
744 #endif
745   /* shut down the socket to inform the server we're done */
746   sclose(conn->sock[SECONDARYSOCKET]);
747   conn->sock[SECONDARYSOCKET] = -1;
748
749   if(!ftp->no_transfer) {
750     /* Let's see what the server says about the transfer we just performed,
751        but lower the timeout as sometimes this connection has died while 
752        the data has been transfered. This happens when doing through NATs
753        etc that abandon old silent connections.
754     */
755     ftp->response_time = 60; /* give it only a minute for now */
756
757     result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
758
759     ftp->response_time = 3600; /* set this back to one hour waits */
760   
761     if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) {
762       failf(data, "control connection looks dead");
763       return result;
764     }
765
766     if(result)
767       return result;
768
769     if(!ftp->dont_check) {
770       /* 226 Transfer complete, 250 Requested file action okay, completed. */
771       if((ftpcode != 226) && (ftpcode != 250)) {
772         failf(data, "server did not report OK, got %d", ftpcode);
773         return CURLE_FTP_WRITE_ERROR;
774       }
775     }
776   }
777
778   /* clear these for next connection */
779   ftp->no_transfer = FALSE;
780   ftp->dont_check = FALSE; 
781
782   /* Send any post-transfer QUOTE strings? */
783   if(!result && data->set.postquote)
784     result = ftp_sendquote(conn, data->set.postquote);
785
786   return result;
787 }
788
789 /***********************************************************************
790  *
791  * ftp_sendquote()
792  *
793  * Where a 'quote' means a list of custom commands to send to the server.
794  * The quote list is passed as an argument.
795  */
796
797 static 
798 CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote)
799 {
800   struct curl_slist *item;
801   ssize_t nread;
802   int ftpcode;
803   CURLcode result;
804
805   item = quote;
806   while (item) {
807     if (item->data) {
808       FTPSENDF(conn, "%s", item->data);
809
810       result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
811       if (result)
812         return result;
813
814       if (ftpcode >= 400) {
815         failf(conn->data, "QUOT string not accepted: %s", item->data);
816         return CURLE_FTP_QUOTE_ERROR;
817       }
818     }
819
820     item = item->next;
821   }
822
823   return CURLE_OK;
824 }
825
826 /***********************************************************************
827  *
828  * ftp_getfiletime()
829  *
830  * Get the timestamp of the given file.
831  */
832 static
833 CURLcode ftp_getfiletime(struct connectdata *conn, char *file)
834 {
835   CURLcode result=CURLE_OK;
836   int ftpcode; /* for ftp status */
837   ssize_t nread;
838   char *buf = conn->data->state.buffer;
839
840   /* we have requested to get the modified-time of the file, this is yet
841      again a grey area as the MDTM is not kosher RFC959 */
842   FTPSENDF(conn, "MDTM %s", file);
843
844   result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
845   if(result)
846     return result;
847
848   switch(ftpcode) {
849   case 213:
850     {
851       /* we got a time. Format should be: "YYYYMMDDHHMMSS[.sss]" where the
852          last .sss part is optional and means fractions of a second */
853       int year, month, day, hour, minute, second;
854       if(6 == sscanf(buf+4, "%04d%02d%02d%02d%02d%02d",
855                      &year, &month, &day, &hour, &minute, &second)) {
856         /* we have a time, reformat it */
857         time_t secs=time(NULL);
858         sprintf(buf, "%04d%02d%02d %02d:%02d:%02d GMT",
859                 year, month, day, hour, minute, second);
860         /* now, convert this into a time() value: */
861         conn->data->info.filetime = curl_getdate(buf, &secs);
862       }
863     }
864     break;
865   default:
866     infof(conn->data, "unsupported MDTM reply format\n");
867     break;
868   case 550: /* "No such file or directory" */
869     failf(conn->data, "Given file does not exist");
870     result = CURLE_FTP_COULDNT_RETR_FILE;
871     break;
872   }
873   return  result;
874 }
875
876 /***********************************************************************
877  *
878  * ftp_transfertype()
879  *
880  * Set transfer type. We only deal with ASCII or BINARY so this function
881  * sets one of them.
882  */
883 static CURLcode ftp_transfertype(struct connectdata *conn,
884                                   bool ascii)
885 {
886   struct SessionHandle *data = conn->data;
887   int ftpcode;
888   ssize_t nread;
889   CURLcode result;
890
891   FTPSENDF(conn, "TYPE %s", ascii?"A":"I");
892
893   result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
894   if(result)
895     return result;
896   
897   if(ftpcode != 200) {
898     failf(data, "Couldn't set %s mode",
899           ascii?"ASCII":"binary");
900     return ascii? CURLE_FTP_COULDNT_SET_ASCII:CURLE_FTP_COULDNT_SET_BINARY;
901   }
902
903   return CURLE_OK;
904 }
905
906 /***********************************************************************
907  *
908  * ftp_getsize()
909  *
910  * Returns the file size (in bytes) of the given remote file.
911  */
912
913 static
914 CURLcode ftp_getsize(struct connectdata *conn, char *file,
915                       ssize_t *size)
916 {
917   struct SessionHandle *data = conn->data;
918   int ftpcode;
919   ssize_t nread;
920   char *buf=data->state.buffer;
921   CURLcode result;
922
923   FTPSENDF(conn, "SIZE %s", file);
924   result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
925   if(result)
926     return result;
927
928   if(ftpcode == 213) {
929     /* get the size from the ascii string: */
930     *size = atoi(buf+4);
931   }
932   else
933     return CURLE_FTP_COULDNT_GET_SIZE;
934
935   return CURLE_OK;
936 }
937
938 /***************************************************************************
939  *
940  * ftp_pasv_verbose()
941  *
942  * This function only outputs some informationals about this second connection
943  * when we've issued a PASV command before and thus we have connected to a
944  * possibly new IP address.
945  *
946  */
947 static void
948 ftp_pasv_verbose(struct connectdata *conn,
949                  Curl_ipconnect *addr,
950                  char *newhost, /* ascii version */
951                  int port)
952 {
953 #ifndef ENABLE_IPV6
954   /*****************************************************************
955    *
956    * IPv4-only code section
957    */
958
959   struct in_addr in;
960   struct hostent * answer;
961
962 #ifdef HAVE_INET_NTOA_R
963   char ntoa_buf[64];
964 #endif
965   /* The array size trick below is to make this a large chunk of memory
966      suitably 8-byte aligned on 64-bit platforms. This was thoughtfully
967      suggested by Philip Gladstone. */
968   long bigbuf[9000 / sizeof(long)];
969
970 #if defined(HAVE_INET_ADDR)
971   in_addr_t address;
972 # if defined(HAVE_GETHOSTBYADDR_R)
973   int h_errnop;
974 # endif
975   char *hostent_buf = (char *)bigbuf; /* get a char * to the buffer */
976
977   address = inet_addr(newhost);
978 # ifdef HAVE_GETHOSTBYADDR_R
979
980 #  ifdef HAVE_GETHOSTBYADDR_R_5
981   /* AIX, Digital Unix (OSF1, Tru64) style:
982      extern int gethostbyaddr_r(char *addr, size_t len, int type,
983      struct hostent *htent, struct hostent_data *ht_data); */
984
985   /* Fred Noz helped me try this out, now it at least compiles! */
986
987   /* Bjorn Reese (November 28 2001):
988      The Tru64 man page on gethostbyaddr_r() says that
989      the hostent struct must be filled with zeroes before the call to
990      gethostbyaddr_r(). 
991
992      ... as must be struct hostent_data Craig Markwardt 19 Sep 2002. */
993
994   memset(hostent_buf, 0, sizeof(struct hostent)+sizeof(struct hostent_data));
995
996   if(gethostbyaddr_r((char *) &address,
997                      sizeof(address), AF_INET,
998                      (struct hostent *)hostent_buf,
999                      (struct hostent_data *)(hostent_buf + sizeof(*answer))))
1000     answer=NULL;
1001   else
1002     answer=(struct hostent *)hostent_buf;
1003                            
1004 #  endif
1005 #  ifdef HAVE_GETHOSTBYADDR_R_7
1006   /* Solaris and IRIX */
1007   answer = gethostbyaddr_r((char *) &address, sizeof(address), AF_INET,
1008                            (struct hostent *)bigbuf,
1009                            hostent_buf + sizeof(*answer),
1010                            sizeof(bigbuf) - sizeof(*answer),
1011                            &h_errnop);
1012 #  endif
1013 #  ifdef HAVE_GETHOSTBYADDR_R_8
1014   /* Linux style */
1015   if(gethostbyaddr_r((char *) &address, sizeof(address), AF_INET,
1016                      (struct hostent *)hostent_buf,
1017                      hostent_buf + sizeof(*answer),
1018                      sizeof(bigbuf) - sizeof(*answer),
1019                      &answer,
1020                      &h_errnop))
1021     answer=NULL; /* error */
1022 #  endif
1023         
1024 # else
1025   (void)hostent_buf; /* avoid compiler warning */
1026   answer = gethostbyaddr((char *) &address, sizeof(address), AF_INET);
1027 # endif
1028 #else
1029   answer = NULL;
1030 #endif
1031   (void) memcpy(&in.s_addr, addr, sizeof (Curl_ipconnect));
1032   infof(conn->data, "Connecting to %s (%s) port %u\n",
1033         answer?answer->h_name:newhost,
1034 #if defined(HAVE_INET_NTOA_R)
1035         inet_ntoa_r(in, ntoa_buf, sizeof(ntoa_buf)),
1036 #else
1037         inet_ntoa(in),
1038 #endif
1039         port);
1040
1041 #else
1042   /*****************************************************************
1043    *
1044    * IPv6-only code section
1045    */
1046   char hbuf[NI_MAXHOST]; /* ~1KB */
1047   char nbuf[NI_MAXHOST]; /* ~1KB */
1048   char sbuf[NI_MAXSERV]; /* around 32 */
1049 #ifdef NI_WITHSCOPEID
1050   const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID;
1051 #else
1052   const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
1053 #endif
1054   (void)port; /* prevent compiler warning */
1055   if (getnameinfo(addr->ai_addr, addr->ai_addrlen,
1056                   nbuf, sizeof(nbuf), sbuf, sizeof(sbuf), niflags)) {
1057     snprintf(nbuf, sizeof(nbuf), "?");
1058     snprintf(sbuf, sizeof(sbuf), "?");
1059   }
1060         
1061   if (getnameinfo(addr->ai_addr, addr->ai_addrlen,
1062                   hbuf, sizeof(hbuf), NULL, 0, 0)) {
1063     infof(conn->data, "Connecting to %s (%s) port %s\n", nbuf, newhost, sbuf);
1064   }
1065   else {
1066     infof(conn->data, "Connecting to %s (%s) port %s\n", hbuf, nbuf, sbuf);
1067   }
1068 #endif
1069 }
1070
1071 /***********************************************************************
1072  *
1073  * ftp_use_port()
1074  *
1075  * Send the proper PORT command. PORT is the ftp client's way of telling the
1076  * server that *WE* open a port that we listen on an awaits the server to
1077  * connect to. This is the opposite of PASV.
1078  */
1079
1080 static
1081 CURLcode ftp_use_port(struct connectdata *conn)
1082 {
1083   struct SessionHandle *data=conn->data;
1084   int portsock=-1;
1085   ssize_t nread;
1086   int ftpcode; /* receive FTP response codes in this */
1087   CURLcode result;
1088
1089 #ifdef ENABLE_IPV6
1090   /******************************************************************
1091    *
1092    * Here's a piece of IPv6-specific code coming up
1093    *
1094    */
1095
1096   struct addrinfo hints, *res, *ai;
1097   struct sockaddr_storage ss;
1098   socklen_t sslen;
1099   char hbuf[NI_MAXHOST];
1100
1101   struct sockaddr *sa=(struct sockaddr *)&ss;
1102 #ifdef NI_WITHSCOPEID
1103   const int niflags = NI_NUMERICHOST | NI_NUMERICSERV | NI_WITHSCOPEID;
1104 #else
1105   const int niflags = NI_NUMERICHOST | NI_NUMERICSERV;
1106 #endif
1107   unsigned char *ap;
1108   unsigned char *pp;
1109   char portmsgbuf[4096], tmp[4096];
1110
1111   const char *mode[] = { "EPRT", "LPRT", "PORT", NULL };
1112   char **modep;
1113
1114   /*
1115    * we should use Curl_if2ip?  given pickiness of recent ftpd,
1116    * I believe we should use the same address as the control connection.
1117    */
1118   sslen = sizeof(ss);
1119   if (getsockname(conn->sock[FIRSTSOCKET], (struct sockaddr *)&ss, &sslen) < 0)
1120     return CURLE_FTP_PORT_FAILED;
1121   
1122   if (getnameinfo((struct sockaddr *)&ss, sslen, hbuf, sizeof(hbuf), NULL, 0,
1123                   niflags))
1124     return CURLE_FTP_PORT_FAILED;
1125
1126   memset(&hints, 0, sizeof(hints));
1127   hints.ai_family = sa->sa_family;
1128   /*hints.ai_family = ss.ss_family;
1129     this way can be used if sockaddr_storage is properly defined, as glibc 
1130     2.1.X doesn't do*/
1131   hints.ai_socktype = SOCK_STREAM;
1132   hints.ai_flags = AI_PASSIVE;
1133
1134   if (getaddrinfo(hbuf, (char *)"0", &hints, &res))
1135     return CURLE_FTP_PORT_FAILED;
1136   
1137   portsock = -1;
1138   for (ai = res; ai; ai = ai->ai_next) {
1139     portsock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
1140     if (portsock < 0)
1141       continue;
1142
1143     if (bind(portsock, ai->ai_addr, ai->ai_addrlen) < 0) {
1144       sclose(portsock);
1145       portsock = -1;
1146       continue;
1147     }
1148       
1149     if (listen(portsock, 1) < 0) {
1150       sclose(portsock);
1151       portsock = -1;
1152       continue;
1153     }
1154     
1155     break;
1156   }
1157   freeaddrinfo(res);
1158   if (portsock < 0) {
1159     failf(data, "%s", strerror(errno));
1160     return CURLE_FTP_PORT_FAILED;
1161   }
1162
1163   sslen = sizeof(ss);
1164   if (getsockname(portsock, sa, &sslen) < 0) {
1165     failf(data, "%s", strerror(errno));
1166     return CURLE_FTP_PORT_FAILED;
1167   }
1168
1169   for (modep = (char **)(data->set.ftp_use_eprt?&mode[0]:&mode[2]);
1170        modep && *modep; modep++) {
1171     int lprtaf, eprtaf;
1172     int alen=0, plen=0;
1173     
1174     switch (sa->sa_family) {
1175     case AF_INET:
1176       ap = (unsigned char *)&((struct sockaddr_in *)&ss)->sin_addr;
1177       alen = sizeof(((struct sockaddr_in *)&ss)->sin_addr);
1178       pp = (unsigned char *)&((struct sockaddr_in *)&ss)->sin_port;
1179       plen = sizeof(((struct sockaddr_in *)&ss)->sin_port);
1180       lprtaf = 4;
1181       eprtaf = 1;
1182       break;
1183     case AF_INET6:
1184       ap = (unsigned char *)&((struct sockaddr_in6 *)&ss)->sin6_addr;
1185       alen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_addr);
1186       pp = (unsigned char *)&((struct sockaddr_in6 *)&ss)->sin6_port;
1187       plen = sizeof(((struct sockaddr_in6 *)&ss)->sin6_port);
1188       lprtaf = 6;
1189       eprtaf = 2;
1190       break;
1191     default:
1192       ap = pp = NULL;
1193       lprtaf = eprtaf = -1;
1194       break;
1195     }
1196
1197     if (strcmp(*modep, "EPRT") == 0) {
1198       if (eprtaf < 0)
1199         continue;
1200       if (getnameinfo((struct sockaddr *)&ss, sslen,
1201                       portmsgbuf, sizeof(portmsgbuf), tmp, sizeof(tmp), niflags))
1202         continue;
1203
1204       /* do not transmit IPv6 scope identifier to the wire */
1205       if (sa->sa_family == AF_INET6) {
1206         char *q = strchr(portmsgbuf, '%');
1207           if (q)
1208             *q = '\0';
1209       }
1210
1211       result = Curl_ftpsendf(conn, "%s |%d|%s|%s|", *modep, eprtaf,
1212                              portmsgbuf, tmp);
1213       if(result)
1214         return result;
1215     } else if (strcmp(*modep, "LPRT") == 0 ||
1216                strcmp(*modep, "PORT") == 0) {
1217       int i;
1218       
1219       if (strcmp(*modep, "LPRT") == 0 && lprtaf < 0)
1220         continue;
1221       if (strcmp(*modep, "PORT") == 0 && sa->sa_family != AF_INET)
1222         continue;
1223
1224       portmsgbuf[0] = '\0';
1225       if (strcmp(*modep, "LPRT") == 0) {
1226         snprintf(tmp, sizeof(tmp), "%d,%d", lprtaf, alen);
1227         if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >=
1228             sizeof(portmsgbuf)) {
1229           continue;
1230         }
1231       }
1232
1233       for (i = 0; i < alen; i++) {
1234         if (portmsgbuf[0])
1235           snprintf(tmp, sizeof(tmp), ",%u", ap[i]);
1236         else
1237           snprintf(tmp, sizeof(tmp), "%u", ap[i]);
1238         
1239         if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >=
1240             sizeof(portmsgbuf)) {
1241           continue;
1242         }
1243       }
1244       
1245       if (strcmp(*modep, "LPRT") == 0) {
1246         snprintf(tmp, sizeof(tmp), ",%d", plen);
1247         
1248         if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >= sizeof(portmsgbuf))
1249           continue;
1250       }
1251
1252       for (i = 0; i < plen; i++) {
1253         snprintf(tmp, sizeof(tmp), ",%u", pp[i]);
1254         
1255         if (strlcat(portmsgbuf, tmp, sizeof(portmsgbuf)) >=
1256             sizeof(portmsgbuf)) {
1257           continue;
1258         }
1259       }
1260       
1261       result = Curl_ftpsendf(conn, "%s %s", *modep, portmsgbuf);
1262       if(result)
1263         return result;
1264     }
1265     
1266     result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1267     if(result)
1268       return result;
1269     
1270     if (ftpcode != 200) {
1271       continue;
1272     }
1273     else
1274       break;
1275   }
1276   
1277   if (!*modep) {
1278     sclose(portsock);
1279     failf(data, "PORT command attempts failed");
1280     return CURLE_FTP_PORT_FAILED;
1281   }
1282   /* we set the secondary socket variable to this for now, it
1283      is only so that the cleanup function will close it in case
1284      we fail before the true secondary stuff is made */
1285   conn->sock[SECONDARYSOCKET] = portsock;
1286   
1287 #else
1288   /******************************************************************
1289    *
1290    * Here's a piece of IPv4-specific code coming up
1291    *
1292    */
1293   struct sockaddr_in sa;
1294   struct Curl_dns_entry *h=NULL;
1295   unsigned short porttouse;
1296   char myhost[256] = "";
1297   bool sa_filled_in = FALSE;
1298
1299   if(data->set.ftpport) {
1300     in_addr_t in;
1301     int rc;
1302
1303     /* First check if the given name is an IP address */
1304     in=inet_addr(data->set.ftpport);
1305
1306     if((in == CURL_INADDR_NONE) &&
1307        Curl_if2ip(data->set.ftpport, myhost, sizeof(myhost))) {
1308       rc = Curl_resolv(conn, myhost, 0, &h);
1309       if(rc == 1)
1310         rc = Curl_wait_for_resolv(conn, &h);
1311     }
1312     else {
1313       int len = strlen(data->set.ftpport);
1314       if(len>1) {
1315         rc = Curl_resolv(conn, data->set.ftpport, 0, &h);
1316         if(rc == 1)
1317           rc = Curl_wait_for_resolv(conn, &h);
1318       }
1319       if(h)
1320         strcpy(myhost, data->set.ftpport); /* buffer overflow risk */
1321     }
1322   }
1323   if(! *myhost) {
1324     /* pick a suitable default here */
1325
1326     socklen_t sslen;
1327     
1328     sslen = sizeof(sa);
1329     if (getsockname(conn->sock[FIRSTSOCKET],
1330                     (struct sockaddr *)&sa, &sslen) < 0) {
1331       failf(data, "getsockname() failed");
1332       return CURLE_FTP_PORT_FAILED;
1333     }
1334
1335     sa_filled_in = TRUE; /* the sa struct is filled in */
1336   }
1337
1338   if(h)
1339     /* when we return from here, we can forget about this */
1340     Curl_resolv_unlock(data, h);
1341
1342   if ( h || sa_filled_in) {
1343     if( (portsock = socket(AF_INET, SOCK_STREAM, 0)) >= 0 ) {
1344       int size;
1345       
1346       /* we set the secondary socket variable to this for now, it
1347          is only so that the cleanup function will close it in case
1348          we fail before the true secondary stuff is made */
1349       conn->sock[SECONDARYSOCKET] = portsock;
1350
1351       if(!sa_filled_in) {
1352         memset((char *)&sa, 0, sizeof(sa));
1353         memcpy((char *)&sa.sin_addr,
1354                h->addr->h_addr,
1355                h->addr->h_length);
1356         sa.sin_family = AF_INET;
1357         sa.sin_addr.s_addr = INADDR_ANY;
1358       }
1359
1360       sa.sin_port = 0;
1361       size = sizeof(sa);
1362       
1363       if(bind(portsock, (struct sockaddr *)&sa, size) >= 0) {
1364         /* we succeeded to bind */
1365         struct sockaddr_in add;
1366         socklen_t socksize = sizeof(add);
1367
1368         if(getsockname(portsock, (struct sockaddr *) &add,
1369                        &socksize)<0) {
1370           failf(data, "getsockname() failed");
1371           return CURLE_FTP_PORT_FAILED;
1372         }
1373         porttouse = ntohs(add.sin_port);
1374         
1375         if ( listen(portsock, 1) < 0 ) {
1376           failf(data, "listen(2) failed on socket");
1377           return CURLE_FTP_PORT_FAILED;
1378         }
1379       }
1380       else {
1381         failf(data, "bind(2) failed on socket");
1382         return CURLE_FTP_PORT_FAILED;
1383       }
1384     }
1385     else {
1386       failf(data, "socket(2) failed (%s)");
1387       return CURLE_FTP_PORT_FAILED;
1388     }
1389   }
1390   else {
1391     failf(data, "could't find my own IP address (%s)", myhost);
1392     return CURLE_FTP_PORT_FAILED;
1393   }
1394   {
1395 #ifdef HAVE_INET_NTOA_R
1396     char ntoa_buf[64];
1397 #endif
1398     struct in_addr in;
1399     unsigned short ip[5];
1400     (void) memcpy(&in.s_addr,
1401                   h?*h->addr->h_addr_list:(char *)&sa.sin_addr.s_addr,
1402                   sizeof (in.s_addr));
1403
1404 #ifdef HAVE_INET_NTOA_R
1405     /* ignore the return code from inet_ntoa_r() as it is int or
1406        char * depending on system */
1407     inet_ntoa_r(in, ntoa_buf, sizeof(ntoa_buf));
1408     sscanf( ntoa_buf, "%hu.%hu.%hu.%hu",
1409             &ip[0], &ip[1], &ip[2], &ip[3]);
1410 #else
1411     sscanf( inet_ntoa(in), "%hu.%hu.%hu.%hu",
1412             &ip[0], &ip[1], &ip[2], &ip[3]);
1413 #endif
1414     infof(data, "Telling server to connect to %d.%d.%d.%d:%d\n",
1415           ip[0], ip[1], ip[2], ip[3], porttouse);
1416   
1417     result=Curl_ftpsendf(conn, "PORT %d,%d,%d,%d,%d,%d",
1418                          ip[0], ip[1], ip[2], ip[3],
1419                          porttouse >> 8,
1420                          porttouse & 255);
1421     if(result)
1422       return result;
1423   }
1424
1425   result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1426   if(result)
1427     return result;
1428
1429   if(ftpcode != 200) {
1430     failf(data, "Server does not grok PORT, try without it!");
1431     return CURLE_FTP_PORT_FAILED;
1432   }
1433 #endif /* end of ipv4-specific code */
1434
1435   return CURLE_OK;
1436 }
1437
1438 /***********************************************************************
1439  *
1440  * ftp_use_pasv()
1441  *
1442  * Send the PASV command. PASV is the ftp client's way of asking the server to
1443  * open a second port that we can connect to (for the data transfer). This is
1444  * the opposite of PORT.
1445  */
1446
1447 static
1448 CURLcode ftp_use_pasv(struct connectdata *conn,
1449                       bool *connected)
1450 {
1451   struct SessionHandle *data = conn->data;
1452   ssize_t nread;
1453   char *buf = data->state.buffer; /* this is our buffer */
1454   int ftpcode; /* receive FTP response codes in this */
1455   CURLcode result;
1456   struct Curl_dns_entry *addr=NULL;
1457   Curl_ipconnect *conninfo;
1458   int rc;
1459
1460   /*
1461     Here's the excecutive summary on what to do:
1462
1463     PASV is RFC959, expect:
1464     227 Entering Passive Mode (a1,a2,a3,a4,p1,p2)
1465
1466     LPSV is RFC1639, expect:
1467     228 Entering Long Passive Mode (4,4,a1,a2,a3,a4,2,p1,p2)
1468
1469     EPSV is RFC2428, expect:
1470     229 Entering Extended Passive Mode (|||port|)
1471
1472   */
1473
1474   const char *mode[] = { "EPSV", "PASV", NULL };
1475   int results[] = { 229, 227, 0 };
1476   int modeoff;
1477   unsigned short connectport; /* the local port connect() should use! */
1478   unsigned short newport=0; /* remote port, not necessary the local one */
1479   
1480   /* newhost must be able to hold a full IP-style address in ASCII, which
1481      in the IPv6 case means 5*8-1 = 39 letters */
1482   char newhost[48];
1483   char *newhostp=NULL;
1484   
1485   for (modeoff = (data->set.ftp_use_epsv?0:1);
1486        mode[modeoff]; modeoff++) {
1487     result = Curl_ftpsendf(conn, "%s", mode[modeoff]);
1488     if(result)
1489       return result;
1490     result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1491     if(result)
1492       return result;
1493     if (ftpcode == results[modeoff])
1494       break;
1495   }
1496
1497   if (!mode[modeoff]) {
1498     failf(data, "Odd return code after PASV");
1499     return CURLE_FTP_WEIRD_PASV_REPLY;
1500   }
1501   else if (227 == results[modeoff]) {
1502     int ip[4];
1503     int port[2];
1504     char *str=buf;
1505
1506     /*
1507      * New 227-parser June 3rd 1999.
1508      * It now scans for a sequence of six comma-separated numbers and
1509      * will take them as IP+port indicators.
1510      *
1511      * Found reply-strings include:
1512      * "227 Entering Passive Mode (127,0,0,1,4,51)"
1513      * "227 Data transfer will passively listen to 127,0,0,1,4,51"
1514      * "227 Entering passive mode. 127,0,0,1,4,51"
1515      */
1516       
1517     while(*str) {
1518       if (6 == sscanf(str, "%d,%d,%d,%d,%d,%d",
1519                       &ip[0], &ip[1], &ip[2], &ip[3],
1520                       &port[0], &port[1]))
1521         break;
1522       str++;
1523     }
1524
1525     if(!*str) {
1526       failf(data, "Couldn't interpret this 227-reply: %s", buf);
1527       return CURLE_FTP_WEIRD_227_FORMAT;
1528     }
1529
1530     sprintf(newhost, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
1531     newhostp = newhost;
1532     newport = (port[0]<<8) + port[1];
1533   }
1534   else if (229 == results[modeoff]) {
1535     char *ptr = strchr(buf, '(');
1536     if(ptr) {
1537       unsigned int num;
1538       char separator[4];
1539       ptr++;
1540       if(5  == sscanf(ptr, "%c%c%c%u%c",
1541                       &separator[0],
1542                       &separator[1],
1543                       &separator[2],
1544                       &num,
1545                       &separator[3])) {
1546         char sep1 = separator[0];
1547         int i;
1548
1549         /* The four separators should be identical, or else this is an oddly
1550            formatted reply and we bail out immediately. */
1551         for(i=1; i<4; i++) {
1552           if(separator[i] != sep1) {
1553             ptr=NULL; /* set to NULL to signal error */
1554             break;
1555           }
1556         }
1557         if(ptr) {
1558           newport = num;
1559
1560           /* we should use the same host we already are connected to */
1561           newhostp = conn->name;
1562         }
1563       }                      
1564       else
1565         ptr=NULL;
1566     }
1567     if(!ptr) {
1568       failf(data, "Weirdly formatted EPSV reply");
1569       return CURLE_FTP_WEIRD_PASV_REPLY;
1570     }
1571   }
1572   else
1573     return CURLE_FTP_CANT_RECONNECT;
1574
1575   if(data->change.proxy && *data->change.proxy) {
1576     /*
1577      * This is a tunnel through a http proxy and we need to connect to the
1578      * proxy again here.
1579      *
1580      * We don't want to rely on a former host lookup that might've expired
1581      * now, instead we remake the lookup here and now!
1582      */
1583     rc = Curl_resolv(conn, conn->proxyhost, conn->port, &addr);
1584     if(rc == 1)
1585       rc = Curl_wait_for_resolv(conn, &addr);
1586
1587     connectport =
1588       (unsigned short)conn->port; /* we connect to the proxy's port */    
1589
1590   }
1591   else {
1592     /* normal, direct, ftp connection */
1593     rc = Curl_resolv(conn, newhostp, newport, &addr);
1594     if(rc == 1)
1595       rc = Curl_wait_for_resolv(conn, &addr);
1596
1597     if(!addr) {
1598       failf(data, "Can't resolve new host %s:%d", newhostp, newport);
1599       return CURLE_FTP_CANT_GET_HOST;
1600     }
1601     connectport = newport; /* we connect to the remote port */
1602   }
1603     
1604   result = Curl_connecthost(conn,
1605                             addr,
1606                             connectport,
1607                             &conn->sock[SECONDARYSOCKET],
1608                             &conninfo,
1609                             connected);
1610
1611   Curl_resolv_unlock(data, addr); /* we're done using this address */
1612
1613   if(result)
1614     return result;
1615
1616   /*
1617    * When this is used from the multi interface, this might've returned with
1618    * the 'connected' set to FALSE and thus we are now awaiting a non-blocking
1619    * connect to connect and we should not be "hanging" here waiting.
1620    */
1621   
1622   if(data->set.verbose)
1623     /* this just dumps information about this second connection */
1624     ftp_pasv_verbose(conn, conninfo, newhostp, connectport);
1625   
1626   if(data->set.tunnel_thru_httpproxy) {
1627     /* We want "seamless" FTP operations through HTTP proxy tunnel */
1628     result = Curl_ConnectHTTPProxyTunnel(conn, SECONDARYSOCKET,
1629                                          newhostp, newport);
1630     if(CURLE_OK != result)
1631       return result;
1632   }
1633
1634   return CURLE_OK;
1635 }
1636
1637 /*
1638  * Curl_ftp_nextconnect()
1639  *
1640  * This function shall be called when the second FTP connection has been
1641  * established and is confirmed connected.
1642  */
1643
1644 CURLcode Curl_ftp_nextconnect(struct connectdata *conn)
1645 {
1646   struct SessionHandle *data=conn->data;
1647   char *buf = data->state.buffer; /* this is our buffer */
1648   CURLcode result;
1649   ssize_t nread;
1650   int ftpcode; /* for ftp status */
1651
1652   /* the ftp struct is already inited in Curl_ftp_connect() */
1653   struct FTP *ftp = conn->proto.ftp;
1654   long *bytecountp = ftp->bytecountp;
1655
1656   if(data->set.upload) {
1657
1658     /* Set type to binary (unless specified ASCII) */
1659     result = ftp_transfertype(conn, data->set.ftp_ascii);
1660     if(result)
1661       return result;
1662
1663     /* Send any PREQUOTE strings after transfer type is set? (Wesley Laxton)*/
1664     if(data->set.prequote) {
1665       if ((result = ftp_sendquote(conn, data->set.prequote)) != CURLE_OK)
1666         return result;
1667     }
1668
1669     if(conn->resume_from) {
1670       /* we're about to continue the uploading of a file */
1671       /* 1. get already existing file's size. We use the SIZE
1672          command for this which may not exist in the server!
1673          The SIZE command is not in RFC959. */
1674
1675       /* 2. This used to set REST. But since we can do append, we
1676          don't another ftp command. We just skip the source file
1677          offset and then we APPEND the rest on the file instead */
1678
1679       /* 3. pass file-size number of bytes in the source file */
1680       /* 4. lower the infilesize counter */
1681       /* => transfer as usual */
1682
1683       if(conn->resume_from < 0 ) {
1684         /* we could've got a specified offset from the command line,
1685            but now we know we didn't */
1686         ssize_t gottensize;
1687
1688         if(CURLE_OK != ftp_getsize(conn, ftp->file, &gottensize)) {
1689           failf(data, "Couldn't get remote file size");
1690           return CURLE_FTP_COULDNT_GET_SIZE;
1691         }
1692         conn->resume_from = gottensize;
1693       }
1694
1695       if(conn->resume_from) {
1696         /* do we still game? */
1697         int passed=0;
1698         /* enable append instead */
1699         data->set.ftp_append = 1;
1700
1701         /* Now, let's read off the proper amount of bytes from the
1702            input. If we knew it was a proper file we could've just
1703            fseek()ed but we only have a stream here */
1704         do {
1705           int readthisamountnow = (conn->resume_from - passed);
1706           int actuallyread;
1707
1708           if(readthisamountnow > BUFSIZE)
1709             readthisamountnow = BUFSIZE;
1710
1711           actuallyread =
1712             conn->fread(data->state.buffer, 1, readthisamountnow,
1713                         conn->fread_in);
1714
1715           passed += actuallyread;
1716           if(actuallyread != readthisamountnow) {
1717             failf(data, "Could only read %d bytes from the input", passed);
1718             return CURLE_FTP_COULDNT_USE_REST;
1719           }
1720         }
1721         while(passed != conn->resume_from);
1722
1723         /* now, decrease the size of the read */
1724         if(data->set.infilesize>0) {
1725           data->set.infilesize -= conn->resume_from;
1726
1727           if(data->set.infilesize <= 0) {
1728             infof(data, "File already completely uploaded\n");
1729
1730             /* no data to transfer */
1731             result=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1732             
1733             /* Set no_transfer so that we won't get any error in
1734              * Curl_ftp_done() because we didn't transfer anything! */
1735             ftp->no_transfer = TRUE; 
1736
1737             return CURLE_OK;
1738           }
1739         }
1740         /* we've passed, proceed as normal */
1741       }
1742     }
1743
1744     /* Send everything on data->state.in to the socket */
1745     if(data->set.ftp_append) {
1746       /* we append onto the file instead of rewriting it */
1747       FTPSENDF(conn, "APPE %s", ftp->file);
1748     }
1749     else {
1750       FTPSENDF(conn, "STOR %s", ftp->file);
1751     }
1752
1753     result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1754     if(result)
1755       return result;
1756
1757     if(ftpcode>=400) {
1758       failf(data, "Failed FTP upload:%s", buf+3);
1759       /* oops, we never close the sockets! */
1760       return CURLE_FTP_COULDNT_STOR_FILE;
1761     }
1762
1763     if(data->set.ftp_use_port) {
1764       /* PORT means we are now awaiting the server to connect to us. */
1765       result = AllowServerConnect(conn);
1766       if( result )
1767         return result;
1768     }
1769
1770     if(conn->ssl[SECONDARYSOCKET].use) {
1771       /* since we only have a plaintext TCP connection here, we must now
1772          do the TLS stuff */
1773       infof(data, "Doing the SSL/TLS handshake on the data stream\n");
1774       result = Curl_SSLConnect(conn, SECONDARYSOCKET);
1775       if(result)
1776         return result;
1777     }
1778
1779     *bytecountp=0;
1780
1781     /* When we know we're uploading a specified file, we can get the file
1782        size prior to the actual upload. */
1783
1784     Curl_pgrsSetUploadSize(data, data->set.infilesize);
1785
1786     result = Curl_Transfer(conn, -1, -1, FALSE, NULL, /* no download */
1787                            SECONDARYSOCKET, bytecountp);
1788     if(result)
1789       return result;
1790       
1791   }
1792   else if(!data->set.no_body) {
1793     /* Retrieve file or directory */
1794     bool dirlist=FALSE;
1795     long downloadsize=-1;
1796
1797     if(conn->bits.use_range && conn->range) {
1798       long from, to;
1799       int totalsize=-1;
1800       char *ptr;
1801       char *ptr2;
1802
1803       from=strtol(conn->range, &ptr, 0);
1804       while(ptr && *ptr && (isspace((int)*ptr) || (*ptr=='-')))
1805         ptr++;
1806       to=strtol(ptr, &ptr2, 0);
1807       if(ptr == ptr2) {
1808         /* we didn't get any digit */
1809         to=-1;
1810       }
1811       if((-1 == to) && (from>=0)) {
1812         /* X - */
1813         conn->resume_from = from;
1814         infof(data, "FTP RANGE %d to end of file\n", from);
1815       }
1816       else if(from < 0) {
1817         /* -Y */
1818         totalsize = -from;
1819         conn->maxdownload = -from;
1820         conn->resume_from = from;
1821         infof(data, "FTP RANGE the last %d bytes\n", totalsize);
1822       }
1823       else {
1824         /* X-Y */
1825         totalsize = to-from;
1826         conn->maxdownload = totalsize+1; /* include the last mentioned byte */
1827         conn->resume_from = from;
1828         infof(data, "FTP RANGE from %d getting %d bytes\n", from,
1829               conn->maxdownload);
1830       }
1831       infof(data, "range-download from %d to %d, totally %d bytes\n",
1832             from, to, totalsize);
1833       ftp->dont_check = TRUE; /* dont check for successful transfer */
1834     }
1835
1836     if((data->set.ftp_list_only) || !ftp->file) {
1837       /* The specified path ends with a slash, and therefore we think this
1838          is a directory that is requested, use LIST. But before that we
1839          need to set ASCII transfer mode. */
1840       dirlist = TRUE;
1841
1842       /* Set type to ASCII */
1843       result = ftp_transfertype(conn, TRUE /* ASCII enforced */);
1844       if(result)
1845         return result;
1846
1847       /* if this output is to be machine-parsed, the NLST command will be
1848          better used since the LIST command output is not specified or
1849          standard in any way */
1850
1851       FTPSENDF(conn, "%s",
1852             data->set.customrequest?data->set.customrequest:
1853             (data->set.ftp_list_only?"NLST":"LIST"));
1854     }
1855     else {
1856       ssize_t foundsize;
1857
1858       /* Set type to binary (unless specified ASCII) */
1859       result = ftp_transfertype(conn, data->set.ftp_ascii);
1860       if(result)
1861         return result;
1862
1863       /* Send any PREQUOTE strings after transfer type is set? */
1864       if(data->set.prequote) {
1865         if ((result = ftp_sendquote(conn, data->set.prequote)) != CURLE_OK)
1866           return result;
1867       }
1868
1869       /* Attempt to get the size, it'll be useful in some cases: for resumed
1870          downloads and when talking to servers that don't give away the size
1871          in the RETR response line. */
1872       result = ftp_getsize(conn, ftp->file, &foundsize);
1873       if(CURLE_OK == result) {
1874         if (data->set.max_filesize && foundsize > data->set.max_filesize) {
1875           failf(data, "Maximum file size exceeded");
1876           return CURLE_FILESIZE_EXCEEDED;
1877         }
1878         downloadsize = foundsize;
1879       }
1880
1881       if(conn->resume_from) {
1882
1883         /* Daniel: (August 4, 1999)
1884          *
1885          * We start with trying to use the SIZE command to figure out the size
1886          * of the file we're gonna get. If we can get the size, this is by far
1887          * the best way to know if we're trying to resume beyond the EOF.
1888          *
1889          * Daniel, November 28, 2001. We *always* get the size on downloads
1890          * now, so it is done before this even when not doing resumes. I saved
1891          * the comment above for nostalgical reasons! ;-)
1892          */
1893         if(CURLE_OK != result) {
1894           infof(data, "ftp server doesn't support SIZE\n");
1895           /* We couldn't get the size and therefore we can't know if there
1896              really is a part of the file left to get, although the server
1897              will just close the connection when we start the connection so it
1898              won't cause us any harm, just not make us exit as nicely. */
1899         }
1900         else {
1901           /* We got a file size report, so we check that there actually is a
1902              part of the file left to get, or else we go home.  */
1903           if(conn->resume_from< 0) {
1904             /* We're supposed to download the last abs(from) bytes */
1905             if(foundsize < -conn->resume_from) {
1906               failf(data, "Offset (%d) was beyond file size (%d)",
1907                     conn->resume_from, foundsize);
1908               return CURLE_FTP_BAD_DOWNLOAD_RESUME;
1909             }
1910             /* convert to size to download */
1911             downloadsize = -conn->resume_from;
1912             /* download from where? */
1913             conn->resume_from = foundsize - downloadsize;
1914           }
1915           else {
1916             if(foundsize < conn->resume_from) {
1917               failf(data, "Offset (%d) was beyond file size (%d)",
1918                     conn->resume_from, foundsize);
1919               return CURLE_FTP_BAD_DOWNLOAD_RESUME;
1920             }
1921             /* Now store the number of bytes we are expected to download */
1922             downloadsize = foundsize-conn->resume_from;
1923           }
1924         }
1925
1926         if (downloadsize == 0) {
1927           /* no data to transfer */
1928           result=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1929           infof(data, "File already completely downloaded\n");
1930
1931           /* Set no_transfer so that we won't get any error in Curl_ftp_done()
1932            * because we didn't transfer the any file */
1933           ftp->no_transfer = TRUE;
1934           return CURLE_OK;
1935         }
1936         
1937         /* Set resume file transfer offset */
1938         infof(data, "Instructs server to resume from offset %d\n",
1939               conn->resume_from);
1940
1941         FTPSENDF(conn, "REST %d", conn->resume_from);
1942
1943         result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1944         if(result)
1945           return result;
1946
1947         if(ftpcode != 350) {
1948           failf(data, "Couldn't use REST: %s", buf+4);
1949           return CURLE_FTP_COULDNT_USE_REST;
1950         }
1951       }
1952
1953       FTPSENDF(conn, "RETR %s", ftp->file);
1954     }
1955
1956     result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
1957     if(result)
1958       return result;
1959
1960     if((ftpcode == 150) || (ftpcode == 125)) {
1961
1962       /*
1963         A;
1964         150 Opening BINARY mode data connection for /etc/passwd (2241
1965         bytes).  (ok, the file is being transfered)
1966         
1967         B:
1968         150 Opening ASCII mode data connection for /bin/ls 
1969
1970         C:
1971         150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes).
1972
1973         D:
1974         150 Opening ASCII mode data connection for /linux/fisk/kpanelrc (0.0.0.0,0) (545 bytes).
1975           
1976         E:
1977         125 Data connection already open; Transfer starting. */
1978
1979       int size=-1; /* default unknown size */
1980
1981
1982       /*
1983        * It appears that there are FTP-servers that return size 0 for files
1984        * when SIZE is used on the file while being in BINARY mode. To work
1985        * around that (stupid) behavior, we attempt to parse the RETR response
1986        * even if the SIZE returned size zero.
1987        *
1988        * Debugging help from Salvatore Sorrentino on February 26, 2003.
1989        */
1990
1991       if(!dirlist &&
1992          !data->set.ftp_ascii &&
1993          (downloadsize < 1)) {
1994         /*
1995          * It seems directory listings either don't show the size or very
1996          * often uses size 0 anyway. ASCII transfers may very well turn out
1997          * that the transfered amount of data is not the same as this line
1998          * tells, why using this number in those cases only confuses us.
1999          *
2000          * Example D above makes this parsing a little tricky */
2001         char *bytes;
2002         bytes=strstr(buf, " bytes");
2003         if(bytes--) {
2004           int in=bytes-buf;
2005           /* this is a hint there is size information in there! ;-) */
2006           while(--in) {
2007             /* scan for the parenthesis and break there */
2008             if('(' == *bytes)
2009               break;
2010             /* if only skip digits, or else we're in deep trouble */
2011             if(!isdigit((int)*bytes)) {
2012               bytes=NULL;
2013               break;
2014             }
2015             /* one more estep backwards */
2016             bytes--;
2017           }
2018           /* only if we have nothing but digits: */
2019           if(bytes++) {
2020             /* get the number! */
2021             size = atoi(bytes);
2022           }
2023             
2024         }
2025       }
2026       else if(downloadsize > -1)
2027         size = downloadsize;
2028
2029       if(data->set.ftp_use_port) {
2030         result = AllowServerConnect(conn);
2031         if( result )
2032           return result;
2033       }
2034
2035       if(conn->ssl[SECONDARYSOCKET].use) {
2036         /* since we only have a plaintext TCP connection here, we must now
2037            do the TLS stuff */
2038         infof(data, "Doing the SSL/TLS handshake on the data stream\n");
2039         result = Curl_SSLConnect(conn, SECONDARYSOCKET);
2040         if(result)
2041           return result;
2042       }
2043
2044       infof(data, "Getting file with size: %d\n", size);
2045
2046       /* FTP download: */
2047       result=Curl_Transfer(conn, SECONDARYSOCKET, size, FALSE,
2048                            bytecountp,
2049                            -1, NULL); /* no upload here */
2050       if(result)
2051         return result;
2052     }
2053     else {
2054       if(dirlist && (ftpcode == 450)) {
2055         /* simply no matching files */
2056         ftp->no_transfer = TRUE; /* don't think we should download anything */
2057       }
2058       else {
2059         failf(data, "%s", buf+4);
2060         return CURLE_FTP_COULDNT_RETR_FILE;
2061       }
2062     }
2063         
2064   }
2065   /* end of transfer */
2066
2067   return CURLE_OK;
2068 }
2069
2070 /***********************************************************************
2071  *
2072  * ftp_perform()
2073  *
2074  * This is the actual DO function for FTP. Get a file/directory according to
2075  * the options previously setup.
2076  */
2077
2078 static
2079 CURLcode ftp_perform(struct connectdata *conn,
2080                      bool *connected)  /* for the TCP connect status after
2081                                           PASV / PORT */
2082 {
2083   /* this is FTP and no proxy */
2084   CURLcode result=CURLE_OK;
2085   struct SessionHandle *data=conn->data;
2086   char *buf = data->state.buffer; /* this is our buffer */
2087
2088   /* the ftp struct is already inited in Curl_ftp_connect() */
2089   struct FTP *ftp = conn->proto.ftp;
2090
2091   /* Send any QUOTE strings? */
2092   if(data->set.quote) {
2093     if ((result = ftp_sendquote(conn, data->set.quote)) != CURLE_OK)
2094       return result;
2095   }
2096
2097   /* This is a re-used connection. Since we change directory to where the
2098      transfer is taking place, we must now get back to the original dir
2099      where we ended up after login: */
2100   if (conn->bits.reuse && ftp->entrypath) {
2101     if ((result = ftp_cwd_and_mkd(conn, ftp->entrypath)) != CURLE_OK)
2102       return result;
2103   }
2104
2105   {
2106     int i; /* counter for loop */
2107     for (i=0; ftp->dirs[i]; i++) {
2108       /* RFC 1738 says empty components should be respected too, but
2109          that is plain stupid since CWD can't be used with an empty argument */
2110       if ((result = ftp_cwd_and_mkd(conn, ftp->dirs[i])) != CURLE_OK)
2111         return result;
2112     }
2113   }
2114
2115   /* Requested time of file or time-depended transfer? */
2116   if((data->set.get_filetime || data->set.timecondition) &&
2117      ftp->file) {
2118     result = ftp_getfiletime(conn, ftp->file);
2119     switch( result )
2120       {
2121       case CURLE_FTP_COULDNT_RETR_FILE:
2122       case CURLE_OK:
2123         if(data->set.timecondition) {
2124           if((data->info.filetime > 0) && (data->set.timevalue > 0)) {
2125             switch(data->set.timecondition) {
2126             case TIMECOND_IFMODSINCE:
2127             default:
2128               if(data->info.filetime < data->set.timevalue) {
2129                 infof(data, "The requested document is not new enough\n");
2130                 ftp->no_transfer = TRUE; /* mark this to not transfer data */
2131                 return CURLE_OK;
2132               }
2133               break;
2134             case TIMECOND_IFUNMODSINCE:
2135               if(data->info.filetime > data->set.timevalue) {
2136                 infof(data, "The requested document is not old enough\n");
2137                 ftp->no_transfer = TRUE; /* mark this to not transfer data */
2138                 return CURLE_OK;
2139               }
2140               break;
2141             } /* switch */
2142           }
2143           else {
2144             infof(data, "Skipping time comparison\n");
2145           }
2146         }
2147         break;
2148       default:
2149         return result;
2150       } /* switch */
2151   }
2152
2153   /* If we have selected NOBODY and HEADER, it means that we only want file
2154      information. Which in FTP can't be much more than the file size and
2155      date. */
2156   if(data->set.no_body && data->set.include_header && ftp->file) {
2157     /* The SIZE command is _not_ RFC 959 specified, and therefor many servers
2158        may not support it! It is however the only way we have to get a file's
2159        size! */
2160     ssize_t filesize;
2161     ssize_t nread;
2162     int ftpcode;
2163
2164     ftp->no_transfer = TRUE; /* this means no actual transfer is made */
2165     
2166     /* Some servers return different sizes for different modes, and thus we
2167        must set the proper type before we check the size */
2168     result = ftp_transfertype(conn, data->set.ftp_ascii);
2169     if(result)
2170       return result;
2171
2172     /* failing to get size is not a serious error */
2173     result = ftp_getsize(conn, ftp->file, &filesize);
2174
2175     if(CURLE_OK == result) {
2176       sprintf(buf, "Content-Length: %d\r\n", filesize);
2177       result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
2178       if(result)
2179         return result;
2180     }
2181
2182     /* Determine if server can respond to REST command and therefore
2183        whether it can do a range */
2184     FTPSENDF(conn, "REST 0", NULL);
2185     result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2186
2187     if ((CURLE_OK == result) && (ftpcode == 350)) {
2188       result = Curl_client_write(data, CLIENTWRITE_BOTH,
2189                                  (char *)"Accept-ranges: bytes\r\n", 0);
2190       if(result)
2191         return result;
2192     }
2193
2194     /* If we asked for a time of the file and we actually got one as
2195        well, we "emulate" a HTTP-style header in our output. */
2196
2197 #ifdef HAVE_STRFTIME
2198     if(data->set.get_filetime && (data->info.filetime>=0) ) {
2199       struct tm *tm;
2200 #ifdef HAVE_GMTIME_R
2201       struct tm buffer;
2202       tm = (struct tm *)gmtime_r((time_t *)&data->info.filetime, &buffer);
2203 #else
2204       tm = gmtime((time_t *)&data->info.filetime);
2205 #endif
2206       /* format: "Tue, 15 Nov 1994 12:45:26" */
2207       strftime(buf, BUFSIZE-1, "Last-Modified: %a, %d %b %Y %H:%M:%S GMT\r\n",
2208                tm);
2209       result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
2210       if(result)
2211         return result;
2212     }
2213 #endif
2214
2215     return CURLE_OK;
2216   }
2217
2218   if(data->set.no_body)
2219     /* doesn't really transfer any data */
2220     ftp->no_transfer = TRUE;
2221   /* Get us a second connection up and connected */
2222   else if(data->set.ftp_use_port) {
2223     /* We have chosen to use the PORT command */
2224     result = ftp_use_port(conn);
2225     if(CURLE_OK == result) {
2226       /* we have the data connection ready */
2227       infof(data, "Ordered connect of the data stream with PORT!\n");
2228       *connected = TRUE; /* mark us "still connected" */
2229     }
2230   }
2231   else {
2232     /* We have chosen (this is default) to use the PASV command */
2233     result = ftp_use_pasv(conn, connected);
2234     if(CURLE_OK == result && *connected)
2235       infof(data, "Connected the data stream with PASV!\n");
2236   }
2237   
2238   return result;
2239 }
2240
2241 /***********************************************************************
2242  *
2243  * Curl_ftp()
2244  *
2245  * This function is registered as 'curl_do' function. It decodes the path
2246  * parts etc as a wrapper to the actual DO function (ftp_perform).
2247  *
2248  * The input argument is already checked for validity.
2249  */
2250 CURLcode Curl_ftp(struct connectdata *conn)
2251 {
2252   CURLcode retcode=CURLE_OK;
2253   bool connected=0;
2254
2255   struct SessionHandle *data = conn->data;
2256   struct FTP *ftp;
2257
2258   char *slash_pos;  /* position of the first '/' char in curpos */
2259   char *cur_pos=conn->ppath; /* current position in ppath. point at the begin
2260                                 of next path component */
2261   int path_part=0;/* current path component */
2262
2263   /* the ftp struct is already inited in ftp_connect() */
2264   ftp = conn->proto.ftp;
2265   conn->size = -1; /* make sure this is unknown at this point */
2266
2267   Curl_pgrsSetUploadCounter(data, 0);
2268   Curl_pgrsSetDownloadCounter(data, 0);
2269   Curl_pgrsSetUploadSize(data, 0);
2270   Curl_pgrsSetDownloadSize(data, 0);
2271
2272   /*  fixed : initialize ftp->dirs[xxx] to NULL !
2273       is done in Curl_ftp_connect() */
2274
2275   /* parse the URL path into separate path components */
2276   while((slash_pos=strchr(cur_pos, '/'))) {
2277     /* 1 or 0 to indicate absolute directory */
2278     bool absolute_dir = (cur_pos - conn->ppath > 0) && (path_part == 0);
2279
2280     /* seek out the next path component */
2281     if (slash_pos-cur_pos) {
2282       /* we skip empty path components, like "x//y" since the FTP command CWD
2283          requires a parameter and a non-existant parameter a) doesn't work on
2284          many servers and b) has no effect on the others. */
2285       ftp->dirs[path_part] = curl_unescape(cur_pos - absolute_dir,
2286                                            slash_pos - cur_pos + absolute_dir);
2287     
2288       if (!ftp->dirs[path_part]) { /* run out of memory ... */
2289         failf(data, "no memory");
2290         freedirs(ftp);
2291         return CURLE_OUT_OF_MEMORY;
2292       }
2293     }
2294     else {
2295       cur_pos = slash_pos + 1; /* jump to the rest of the string */
2296       continue;
2297     }
2298
2299     if(!retcode) {
2300       cur_pos = slash_pos + 1; /* jump to the rest of the string */
2301       if(++path_part >= (CURL_MAX_FTP_DIRDEPTH-1)) {
2302         /* too deep, we need the last entry to be kept NULL at all
2303            times to signal end of list */
2304         failf(data, "too deep dir hierarchy");
2305         freedirs(ftp);
2306         return CURLE_URL_MALFORMAT;
2307       }
2308     }
2309   }
2310
2311   ftp->file = cur_pos;  /* the rest is the file name */
2312
2313   if(*ftp->file) {
2314     ftp->file = curl_unescape(ftp->file, 0);
2315     if(NULL == ftp->file) {
2316       freedirs(ftp);
2317       failf(data, "no memory");
2318       return CURLE_OUT_OF_MEMORY;
2319     }
2320   }
2321   else
2322     ftp->file=NULL; /* instead of point to a zero byte, we make it a NULL
2323                        pointer */
2324   
2325   retcode = ftp_perform(conn, &connected);
2326
2327   if(CURLE_OK == retcode) {
2328     if(connected)
2329       retcode = Curl_ftp_nextconnect(conn);
2330
2331     if(retcode && (conn->sock[SECONDARYSOCKET] >= 0)) {
2332       /* Failure detected, close the second socket if it was created already */
2333       sclose(conn->sock[SECONDARYSOCKET]);
2334       conn->sock[SECONDARYSOCKET] = -1;
2335     }
2336
2337     if(ftp->no_transfer)
2338       /* no data to transfer */
2339       retcode=Curl_Transfer(conn, -1, -1, FALSE, NULL, -1, NULL);        
2340     else if(!connected)
2341       /* since we didn't connect now, we want do_more to get called */
2342       conn->bits.do_more = TRUE;
2343   }
2344   else
2345     freedirs(ftp);
2346
2347   return retcode;
2348 }
2349
2350 /***********************************************************************
2351  *
2352  * Curl_ftpsendf()
2353  *
2354  * Sends the formated string as a ftp command to a ftp server
2355  *
2356  * NOTE: we build the command in a fixed-length buffer, which sets length
2357  * restrictions on the command!
2358  */
2359 CURLcode Curl_ftpsendf(struct connectdata *conn,
2360                        const char *fmt, ...)
2361 {
2362   ssize_t bytes_written;
2363   char s[256];
2364   ssize_t write_len;
2365   char *sptr=s;
2366   CURLcode res = CURLE_OK;
2367
2368   va_list ap;
2369   va_start(ap, fmt);
2370   vsnprintf(s, 250, fmt, ap);
2371   va_end(ap);
2372   
2373   strcat(s, "\r\n"); /* append a trailing CRLF */
2374
2375   bytes_written=0;
2376   write_len = strlen(s);
2377
2378   do {
2379     res = Curl_write(conn, conn->sock[FIRSTSOCKET], sptr, write_len,
2380                      &bytes_written);
2381
2382     if(CURLE_OK != res)
2383       break;
2384
2385     if(conn->data->set.verbose)
2386       Curl_debug(conn->data, CURLINFO_HEADER_OUT, sptr, bytes_written);
2387
2388     if(bytes_written != write_len) {
2389       write_len -= bytes_written;
2390       sptr += bytes_written;
2391     }
2392     else
2393       break;
2394   } while(1);
2395
2396   return res;
2397 }
2398
2399 /***********************************************************************
2400  *
2401  * Curl_ftp_disconnect()
2402  *
2403  * Disconnect from an FTP server. Cleanup protocol-specific per-connection
2404  * resources
2405  */
2406 CURLcode Curl_ftp_disconnect(struct connectdata *conn)
2407 {
2408   struct FTP *ftp= conn->proto.ftp;
2409
2410   /* The FTP session may or may not have been allocated/setup at this point! */
2411   if(ftp) {
2412     if(ftp->entrypath)
2413       free(ftp->entrypath);
2414     if(ftp->cache) {
2415       free(ftp->cache);
2416       ftp->cache = NULL;
2417     }
2418     if(ftp->file) {
2419       free(ftp->file);
2420       ftp->file = NULL; /* zero */
2421     }
2422     freedirs(ftp);
2423   }
2424   return CURLE_OK;
2425 }
2426
2427 /***********************************************************************
2428  *
2429  * ftp_mkd()
2430  *
2431  * Makes a directory on the FTP server.
2432  *
2433  * Calls failf()
2434  */
2435 CURLcode ftp_mkd(struct connectdata *conn, char *path)
2436 {
2437   CURLcode result=CURLE_OK;
2438   int ftpcode; /* for ftp status */
2439   ssize_t nread;
2440
2441   /* Create a directory on the remote server */
2442   FTPSENDF(conn, "MKD %s", path);
2443
2444   result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2445   if(result)
2446     return result;
2447   
2448   switch(ftpcode) {
2449   case 257:
2450     /* success! */
2451     infof( conn->data , "Created remote directory %s\n" , path );
2452     break;
2453   case 550:
2454     failf(conn->data, "Permission denied to make directory %s", path);
2455     result = CURLE_FTP_ACCESS_DENIED;
2456     break;
2457   default:
2458     failf(conn->data, "unrecognized MKD response: %d", ftpcode );
2459     result = CURLE_FTP_ACCESS_DENIED;
2460     break;
2461   }
2462   return  result;
2463 }
2464
2465 /***********************************************************************
2466  *
2467  * ftp_cwd()
2468  *
2469  * Send 'CWD' to the remote server to Change Working Directory.  It is the ftp
2470  * version of the unix 'cd' command. This function is only called from the
2471  * ftp_cwd_and_mkd() function these days.
2472  *
2473  * This function does NOT call failf().
2474  */
2475 static 
2476 CURLcode ftp_cwd(struct connectdata *conn, char *path)
2477 {
2478   ssize_t nread;
2479   int     ftpcode;
2480   CURLcode result;
2481   
2482   FTPSENDF(conn, "CWD %s", path);
2483   result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
2484   if (!result) {
2485     /* According to RFC959, CWD is supposed to return 250 on success, but
2486        there seem to be non-compliant FTP servers out there that return 200,
2487        so we accept any '2xy' code here. */
2488     if (ftpcode/100 != 2)
2489       result = CURLE_FTP_ACCESS_DENIED;
2490   }
2491
2492   return result;
2493 }
2494
2495 /***********************************************************************
2496  *
2497  * ftp_cwd_and_mkd()
2498  *
2499  * Change to the given directory.  If the directory is not present, and we
2500  * have been told to allow it, then create the directory and cd to it.
2501  *
2502  */
2503 static CURLcode ftp_cwd_and_mkd(struct connectdata *conn, char *path)
2504 {
2505   CURLcode result;
2506   
2507   result = ftp_cwd(conn, path);
2508   if (result) {
2509     if(conn->data->set.ftp_create_missing_dirs) {
2510       result = ftp_mkd(conn, path);
2511       if (result)
2512         /* ftp_mkd() calls failf() itself */
2513         return result;
2514       result = ftp_cwd(conn, path);
2515     }
2516     if(result)
2517       failf(conn->data, "Couldn't cd to %s", path);
2518   }
2519   return result;
2520 }
2521
2522 #endif /* CURL_DISABLE_FTP */