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