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