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