1 /***************************************************************************
2  *                                  _   _ ____  _
3  *  Project                     ___| | | |  _ \| |
4  *                             / __| | | | |_) | |
5  *                            | (__| |_| |  _ <| |___
6  *                             \___|\___/|_| \_\_____|
7  *
8  * Copyright (C) 1998 - 2011, 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  ***************************************************************************/
22 
23 #include "setup.h"
24 
25 #ifndef CURL_DISABLE_FTP
26 #include <stdio.h>
27 #include <string.h>
28 #include <stdlib.h>
29 #include <stdarg.h>
30 #include <ctype.h>
31 
32 #ifdef HAVE_UNISTD_H
33 #include <unistd.h>
34 #endif
35 
36 #ifdef HAVE_SYS_SOCKET_H
37 #include <sys/socket.h>
38 #endif
39 #ifdef HAVE_NETINET_IN_H
40 #include <netinet/in.h>
41 #endif
42 #ifdef HAVE_ARPA_INET_H
43 #include <arpa/inet.h>
44 #endif
45 #ifdef HAVE_UTSNAME_H
46 #include <sys/utsname.h>
47 #endif
48 #ifdef HAVE_NETDB_H
49 #include <netdb.h>
50 #endif
51 #ifdef __VMS
52 #include <in.h>
53 #include <inet.h>
54 #endif
55 
56 #if (defined(NETWARE) && defined(__NOVELL_LIBC__))
57 #undef in_addr_t
58 #define in_addr_t unsigned long
59 #endif
60 
61 #include <curl/curl.h>
62 #include "urldata.h"
63 #include "sendf.h"
64 #include "if2ip.h"
65 #include "hostip.h"
66 #include "progress.h"
67 #include "transfer.h"
68 #include "escape.h"
69 #include "http.h" /* for HTTP proxy tunnel stuff */
70 #include "socks.h"
71 #include "ftp.h"
72 #include "fileinfo.h"
73 #include "ftplistparser.h"
74 
75 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
76 #include "krb4.h"
77 #endif
78 
79 #include "strtoofft.h"
80 #include "strequal.h"
81 #include "sslgen.h"
82 #include "connect.h"
83 #include "strerror.h"
84 #include "inet_ntop.h"
85 #include "inet_pton.h"
86 #include "select.h"
87 #include "parsedate.h" /* for the week day and month names */
88 #include "sockaddr.h" /* required for Curl_sockaddr_storage */
89 #include "multiif.h"
90 #include "url.h"
91 #include "rawstr.h"
92 #include "speedcheck.h"
93 #include "warnless.h"
94 #include "http_proxy.h"
95 #include "non-ascii.h"
96 
97 #define _MPRINTF_REPLACE /* use our functions only */
98 #include <curl/mprintf.h>
99 
100 #include "curl_memory.h"
101 /* The last #include file should be: */
102 #include "memdebug.h"
103 
104 #ifndef NI_MAXHOST
105 #define NI_MAXHOST 1025
106 #endif
107 #ifndef INET_ADDRSTRLEN
108 #define INET_ADDRSTRLEN 16
109 #endif
110 
111 #ifdef CURL_DISABLE_VERBOSE_STRINGS
112 #define ftp_pasv_verbose(a,b,c,d)  do { } while(0)
113 #endif
114 
115 /* Local API functions */
116 static CURLcode ftp_sendquote(struct connectdata *conn,
117                               struct curl_slist *quote);
118 static CURLcode ftp_quit(struct connectdata *conn);
119 static CURLcode ftp_parse_url_path(struct connectdata *conn);
120 static CURLcode ftp_regular_transfer(struct connectdata *conn, bool *done);
121 #ifndef CURL_DISABLE_VERBOSE_STRINGS
122 static void ftp_pasv_verbose(struct connectdata *conn,
123                              Curl_addrinfo *ai,
124                              char *newhost, /* ascii version */
125                              int port);
126 #endif
127 static CURLcode ftp_state_post_rest(struct connectdata *conn);
128 static CURLcode ftp_state_post_cwd(struct connectdata *conn);
129 static CURLcode ftp_state_quote(struct connectdata *conn,
130                                 bool init, ftpstate instate);
131 static CURLcode ftp_nb_type(struct connectdata *conn,
132                             bool ascii, ftpstate newstate);
133 static int ftp_need_type(struct connectdata *conn,
134                          bool ascii);
135 static CURLcode ftp_do(struct connectdata *conn, bool *done);
136 static CURLcode ftp_done(struct connectdata *conn,
137                          CURLcode, bool premature);
138 static CURLcode ftp_connect(struct connectdata *conn, bool *done);
139 static CURLcode ftp_disconnect(struct connectdata *conn, bool dead_connection);
140 static CURLcode ftp_nextconnect(struct connectdata *conn);
141 static CURLcode ftp_multi_statemach(struct connectdata *conn, bool *done);
142 static int ftp_getsock(struct connectdata *conn,
143                        curl_socket_t *socks,
144                        int numsocks);
145 static CURLcode ftp_doing(struct connectdata *conn,
146                           bool *dophase_done);
147 static CURLcode ftp_setup_connection(struct connectdata * conn);
148 
149 static CURLcode init_wc_data(struct connectdata *conn);
150 static CURLcode wc_statemach(struct connectdata *conn);
151 
152 static void wc_data_dtor(void *ptr);
153 
154 static CURLcode ftp_state_post_retr_size(struct connectdata *conn,
155                                          curl_off_t filesize);
156 
157 /* easy-to-use macro: */
158 #define FTPSENDF(x,y,z)    if((result = Curl_ftpsendf(x,y,z)) != CURLE_OK) \
159                               return result
160 #define PPSENDF(x,y,z)  if((result = Curl_pp_sendf(x,y,z)) != CURLE_OK) \
161                               return result
162 
163 
164 /*
165  * FTP protocol handler.
166  */
167 
168 const struct Curl_handler Curl_handler_ftp = {
169   "FTP",                           /* scheme */
170   ftp_setup_connection,            /* setup_connection */
171   ftp_do,                          /* do_it */
172   ftp_done,                        /* done */
173   ftp_nextconnect,                 /* do_more */
174   ftp_connect,                     /* connect_it */
175   ftp_multi_statemach,             /* connecting */
176   ftp_doing,                       /* doing */
177   ftp_getsock,                     /* proto_getsock */
178   ftp_getsock,                     /* doing_getsock */
179   ZERO_NULL,                       /* perform_getsock */
180   ftp_disconnect,                  /* disconnect */
181   PORT_FTP,                        /* defport */
182   CURLPROTO_FTP,                   /* protocol */
183   PROTOPT_DUAL | PROTOPT_CLOSEACTION /* flags */
184 };
185 
186 
187 #ifdef USE_SSL
188 /*
189  * FTPS protocol handler.
190  */
191 
192 const struct Curl_handler Curl_handler_ftps = {
193   "FTPS",                          /* scheme */
194   ftp_setup_connection,            /* setup_connection */
195   ftp_do,                          /* do_it */
196   ftp_done,                        /* done */
197   ftp_nextconnect,                 /* do_more */
198   ftp_connect,                     /* connect_it */
199   ftp_multi_statemach,             /* connecting */
200   ftp_doing,                       /* doing */
201   ftp_getsock,                     /* proto_getsock */
202   ftp_getsock,                     /* doing_getsock */
203   ZERO_NULL,                       /* perform_getsock */
204   ftp_disconnect,                  /* disconnect */
205   PORT_FTPS,                       /* defport */
206   CURLPROTO_FTP | CURLPROTO_FTPS,  /* protocol */
207   PROTOPT_SSL | PROTOPT_DUAL | PROTOPT_CLOSEACTION /* flags */
208 };
209 #endif
210 
211 #ifndef CURL_DISABLE_HTTP
212 /*
213  * HTTP-proxyed FTP protocol handler.
214  */
215 
216 static const struct Curl_handler Curl_handler_ftp_proxy = {
217   "FTP",                                /* scheme */
218   ZERO_NULL,                            /* setup_connection */
219   Curl_http,                            /* do_it */
220   Curl_http_done,                       /* done */
221   ZERO_NULL,                            /* do_more */
222   ZERO_NULL,                            /* connect_it */
223   ZERO_NULL,                            /* connecting */
224   ZERO_NULL,                            /* doing */
225   ZERO_NULL,                            /* proto_getsock */
226   ZERO_NULL,                            /* doing_getsock */
227   ZERO_NULL,                            /* perform_getsock */
228   ZERO_NULL,                            /* disconnect */
229   PORT_FTP,                             /* defport */
230   CURLPROTO_HTTP,                       /* protocol */
231   PROTOPT_NONE                          /* flags */
232 };
233 
234 
235 #ifdef USE_SSL
236 /*
237  * HTTP-proxyed FTPS protocol handler.
238  */
239 
240 static const struct Curl_handler Curl_handler_ftps_proxy = {
241   "FTPS",                               /* scheme */
242   ZERO_NULL,                            /* setup_connection */
243   Curl_http,                            /* do_it */
244   Curl_http_done,                       /* done */
245   ZERO_NULL,                            /* do_more */
246   ZERO_NULL,                            /* connect_it */
247   ZERO_NULL,                            /* connecting */
248   ZERO_NULL,                            /* doing */
249   ZERO_NULL,                            /* proto_getsock */
250   ZERO_NULL,                            /* doing_getsock */
251   ZERO_NULL,                            /* perform_getsock */
252   ZERO_NULL,                            /* disconnect */
253   PORT_FTPS,                            /* defport */
254   CURLPROTO_HTTP,                       /* protocol */
255   PROTOPT_NONE                          /* flags */
256 };
257 #endif
258 #endif
259 
260 
261 /*
262  * NOTE: back in the old days, we added code in the FTP code that made NOBODY
263  * requests on files respond with headers passed to the client/stdout that
264  * looked like HTTP ones.
265  *
266  * This approach is not very elegant, it causes confusion and is error-prone.
267  * It is subject for removal at the next (or at least a future) soname bump.
268  * Until then you can test the effects of the removal by undefining the
269  * following define named CURL_FTP_HTTPSTYLE_HEAD.
270  */
271 #define CURL_FTP_HTTPSTYLE_HEAD 1
272 
freedirs(struct ftp_conn * ftpc)273 static void freedirs(struct ftp_conn *ftpc)
274 {
275   int i;
276   if(ftpc->dirs) {
277     for (i=0; i < ftpc->dirdepth; i++){
278       if(ftpc->dirs[i]) {
279         free(ftpc->dirs[i]);
280         ftpc->dirs[i]=NULL;
281       }
282     }
283     free(ftpc->dirs);
284     ftpc->dirs = NULL;
285     ftpc->dirdepth = 0;
286   }
287   if(ftpc->file) {
288     free(ftpc->file);
289     ftpc->file = NULL;
290   }
291 }
292 
293 /* Returns non-zero if the given string contains CR (\r) or LF (\n),
294    which are not allowed within RFC 959 <string>.
295    Note: The input string is in the client's encoding which might
296    not be ASCII, so escape sequences \r & \n must be used instead
297    of hex values 0x0d & 0x0a.
298 */
isBadFtpString(const char * string)299 static bool isBadFtpString(const char *string)
300 {
301   return (bool)((NULL != strchr(string, '\r')) ||
302                 (NULL != strchr(string, '\n')));
303 }
304 
305 /***********************************************************************
306  *
307  * AllowServerConnect()
308  *
309  * When we've issue the PORT command, we have told the server to connect
310  * to us. This function will sit and wait here until the server has
311  * connected.
312  *
313  */
AllowServerConnect(struct connectdata * conn)314 static CURLcode AllowServerConnect(struct connectdata *conn)
315 {
316   struct SessionHandle *data = conn->data;
317   curl_socket_t sock = conn->sock[SECONDARYSOCKET];
318   long timeout_ms;
319   long interval_ms;
320   curl_socket_t s = CURL_SOCKET_BAD;
321 #ifdef ENABLE_IPV6
322   struct Curl_sockaddr_storage add;
323 #else
324   struct sockaddr_in add;
325 #endif
326   curl_socklen_t size = (curl_socklen_t) sizeof(add);
327 
328   for(;;) {
329     timeout_ms = Curl_timeleft(data, NULL, TRUE);
330 
331     if(timeout_ms < 0) {
332       /* if a timeout was already reached, bail out */
333       failf(data, "Timeout while waiting for server connect");
334       return CURLE_OPERATION_TIMEDOUT;
335     }
336 
337     interval_ms = 1000;  /* use 1 second timeout intervals */
338     if(timeout_ms < interval_ms)
339       interval_ms = timeout_ms;
340 
341     switch (Curl_socket_ready(sock, CURL_SOCKET_BAD, (int)interval_ms)) {
342     case -1: /* error */
343       /* let's die here */
344       failf(data, "Error while waiting for server connect");
345       return CURLE_FTP_PORT_FAILED;
346     case 0:  /* timeout */
347       break; /* loop */
348     default:
349       /* we have received data here */
350       if(0 == getsockname(sock, (struct sockaddr *) &add, &size)) {
351         size = sizeof(add);
352 
353         s=accept(sock, (struct sockaddr *) &add, &size);
354       }
355       sclose(sock); /* close the first socket */
356 
357       if(CURL_SOCKET_BAD == s) {
358         failf(data, "Error accept()ing server connect");
359         return CURLE_FTP_PORT_FAILED;
360       }
361       infof(data, "Connection accepted from server\n");
362 
363       conn->sock[SECONDARYSOCKET] = s;
364       curlx_nonblock(s, TRUE); /* enable non-blocking */
365       return CURLE_OK;
366     } /* switch() */
367   }
368   /* never reaches this point */
369 }
370 
371 /* macro to check for a three-digit ftp status code at the start of the
372    given string */
373 #define STATUSCODE(line) (ISDIGIT(line[0]) && ISDIGIT(line[1]) &&       \
374                           ISDIGIT(line[2]))
375 
376 /* macro to check for the last line in an FTP server response */
377 #define LASTLINE(line) (STATUSCODE(line) && (' ' == line[3]))
378 
ftp_endofresp(struct pingpong * pp,int * code)379 static int ftp_endofresp(struct pingpong *pp,
380                          int *code)
381 {
382   char *line = pp->linestart_resp;
383   size_t len = pp->nread_resp;
384 
385   if((len > 3) && LASTLINE(line)) {
386     *code = curlx_sltosi(strtol(line, NULL, 10));
387     return 1;
388   }
389   return 0;
390 }
391 
ftp_readresp(curl_socket_t sockfd,struct pingpong * pp,int * ftpcode,size_t * size)392 static CURLcode ftp_readresp(curl_socket_t sockfd,
393                              struct pingpong *pp,
394                              int *ftpcode, /* return the ftp-code if done */
395                              size_t *size) /* size of the response */
396 {
397   struct connectdata *conn = pp->conn;
398 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
399   struct SessionHandle *data = conn->data;
400   char * const buf = data->state.buffer;
401 #endif
402   CURLcode result = CURLE_OK;
403   int code;
404 
405   result = Curl_pp_readresp(sockfd, pp, &code, size);
406 
407 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
408   /* handle the security-oriented responses 6xx ***/
409   /* FIXME: some errorchecking perhaps... ***/
410   switch(code) {
411   case 631:
412     code = Curl_sec_read_msg(conn, buf, PROT_SAFE);
413     break;
414   case 632:
415     code = Curl_sec_read_msg(conn, buf, PROT_PRIVATE);
416     break;
417   case 633:
418     code = Curl_sec_read_msg(conn, buf, PROT_CONFIDENTIAL);
419     break;
420   default:
421     /* normal ftp stuff we pass through! */
422     break;
423   }
424 #endif
425 
426   /* store the latest code for later retrieval */
427   conn->data->info.httpcode=code;
428 
429   if(ftpcode)
430     *ftpcode = code;
431 
432   if(421 == code)
433     /* 421 means "Service not available, closing control connection." and FTP
434      * servers use it to signal that idle session timeout has been exceeded.
435      * If we ignored the response, it could end up hanging in some cases. */
436     return CURLE_OPERATION_TIMEDOUT;
437 
438   return result;
439 }
440 
441 /* --- parse FTP server responses --- */
442 
443 /*
444  * Curl_GetFTPResponse() is a BLOCKING function to read the full response
445  * from a server after a command.
446  *
447  */
448 
Curl_GetFTPResponse(ssize_t * nreadp,struct connectdata * conn,int * ftpcode)449 CURLcode Curl_GetFTPResponse(ssize_t *nreadp, /* return number of bytes read */
450                              struct connectdata *conn,
451                              int *ftpcode) /* return the ftp-code */
452 {
453   /*
454    * We cannot read just one byte per read() and then go back to select() as
455    * the OpenSSL read() doesn't grok that properly.
456    *
457    * Alas, read as much as possible, split up into lines, use the ending
458    * line in a response or continue reading.  */
459 
460   curl_socket_t sockfd = conn->sock[FIRSTSOCKET];
461   long timeout;              /* timeout in milliseconds */
462   long interval_ms;
463   struct SessionHandle *data = conn->data;
464   CURLcode result = CURLE_OK;
465   struct ftp_conn *ftpc = &conn->proto.ftpc;
466   struct pingpong *pp = &ftpc->pp;
467   size_t nread;
468   int cache_skip=0;
469   int value_to_be_ignored=0;
470 
471   if(ftpcode)
472     *ftpcode = 0; /* 0 for errors */
473   else
474     /* make the pointer point to something for the rest of this function */
475     ftpcode = &value_to_be_ignored;
476 
477   *nreadp=0;
478 
479   while(!*ftpcode && !result) {
480     /* check and reset timeout value every lap */
481     timeout = Curl_pp_state_timeout(pp);
482 
483     if(timeout <=0 ) {
484       failf(data, "FTP response timeout");
485       return CURLE_OPERATION_TIMEDOUT; /* already too little time */
486     }
487 
488     interval_ms = 1000;  /* use 1 second timeout intervals */
489     if(timeout < interval_ms)
490       interval_ms = timeout;
491 
492     /*
493      * Since this function is blocking, we need to wait here for input on the
494      * connection and only then we call the response reading function. We do
495      * timeout at least every second to make the timeout check run.
496      *
497      * A caution here is that the ftp_readresp() function has a cache that may
498      * contain pieces of a response from the previous invoke and we need to
499      * make sure we don't just wait for input while there is unhandled data in
500      * that cache. But also, if the cache is there, we call ftp_readresp() and
501      * the cache wasn't good enough to continue we must not just busy-loop
502      * around this function.
503      *
504      */
505 
506     if(pp->cache && (cache_skip < 2)) {
507       /*
508        * There's a cache left since before. We then skipping the wait for
509        * socket action, unless this is the same cache like the previous round
510        * as then the cache was deemed not enough to act on and we then need to
511        * wait for more data anyway.
512        */
513     }
514     else {
515       switch (Curl_socket_ready(sockfd, CURL_SOCKET_BAD, (int)interval_ms)) {
516       case -1: /* select() error, stop reading */
517         failf(data, "FTP response aborted due to select/poll error: %d",
518               SOCKERRNO);
519         return CURLE_RECV_ERROR;
520 
521       case 0: /* timeout */
522         if(Curl_pgrsUpdate(conn))
523           return CURLE_ABORTED_BY_CALLBACK;
524         continue; /* just continue in our loop for the timeout duration */
525 
526       default: /* for clarity */
527         break;
528       }
529     }
530     result = ftp_readresp(sockfd, pp, ftpcode, &nread);
531     if(result)
532       break;
533 
534     if(!nread && pp->cache)
535       /* bump cache skip counter as on repeated skips we must wait for more
536          data */
537       cache_skip++;
538     else
539       /* when we got data or there is no cache left, we reset the cache skip
540          counter */
541       cache_skip=0;
542 
543     *nreadp += nread;
544 
545   } /* while there's buffer left and loop is requested */
546 
547   pp->pending_resp = FALSE;
548 
549   return result;
550 }
551 
552 /* This is the ONLY way to change FTP state! */
state(struct connectdata * conn,ftpstate newstate)553 static void state(struct connectdata *conn,
554                   ftpstate newstate)
555 {
556 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
557   /* for debug purposes */
558   static const char * const names[]={
559     "STOP",
560     "WAIT220",
561     "AUTH",
562     "USER",
563     "PASS",
564     "ACCT",
565     "PBSZ",
566     "PROT",
567     "CCC",
568     "PWD",
569     "SYST",
570     "NAMEFMT",
571     "QUOTE",
572     "RETR_PREQUOTE",
573     "STOR_PREQUOTE",
574     "POSTQUOTE",
575     "CWD",
576     "MKD",
577     "MDTM",
578     "TYPE",
579     "LIST_TYPE",
580     "RETR_TYPE",
581     "STOR_TYPE",
582     "SIZE",
583     "RETR_SIZE",
584     "STOR_SIZE",
585     "REST",
586     "RETR_REST",
587     "PORT",
588     "PRET",
589     "PASV",
590     "LIST",
591     "RETR",
592     "STOR",
593     "QUIT"
594   };
595 #endif
596   struct ftp_conn *ftpc = &conn->proto.ftpc;
597 #if defined(DEBUGBUILD) && !defined(CURL_DISABLE_VERBOSE_STRINGS)
598   if(ftpc->state != newstate)
599     infof(conn->data, "FTP %p state change from %s to %s\n",
600           ftpc, names[ftpc->state], names[newstate]);
601 #endif
602   ftpc->state = newstate;
603 }
604 
ftp_state_user(struct connectdata * conn)605 static CURLcode ftp_state_user(struct connectdata *conn)
606 {
607   CURLcode result;
608   struct FTP *ftp = conn->data->state.proto.ftp;
609   /* send USER */
610   PPSENDF(&conn->proto.ftpc.pp, "USER %s", ftp->user?ftp->user:"");
611 
612   state(conn, FTP_USER);
613   conn->data->state.ftp_trying_alternative = FALSE;
614 
615   return CURLE_OK;
616 }
617 
ftp_state_pwd(struct connectdata * conn)618 static CURLcode ftp_state_pwd(struct connectdata *conn)
619 {
620   CURLcode result;
621 
622   /* send PWD to discover our entry point */
623   PPSENDF(&conn->proto.ftpc.pp, "PWD", NULL);
624   state(conn, FTP_PWD);
625 
626   return CURLE_OK;
627 }
628 
629 /* For the FTP "protocol connect" and "doing" phases only */
ftp_getsock(struct connectdata * conn,curl_socket_t * socks,int numsocks)630 static int ftp_getsock(struct connectdata *conn,
631                        curl_socket_t *socks,
632                        int numsocks)
633 {
634   return Curl_pp_getsock(&conn->proto.ftpc.pp, socks, numsocks);
635 }
636 
637 /* This is called after the FTP_QUOTE state is passed.
638 
639    ftp_state_cwd() sends the range of CWD commands to the server to change to
640    the correct directory. It may also need to send MKD commands to create
641    missing ones, if that option is enabled.
642 */
ftp_state_cwd(struct connectdata * conn)643 static CURLcode ftp_state_cwd(struct connectdata *conn)
644 {
645   CURLcode result = CURLE_OK;
646   struct ftp_conn *ftpc = &conn->proto.ftpc;
647 
648   if(ftpc->cwddone)
649     /* already done and fine */
650     result = ftp_state_post_cwd(conn);
651   else {
652     ftpc->count2 = 0; /* count2 counts failed CWDs */
653 
654     /* count3 is set to allow a MKD to fail once. In the case when first CWD
655        fails and then MKD fails (due to another session raced it to create the
656        dir) this then allows for a second try to CWD to it */
657     ftpc->count3 = (conn->data->set.ftp_create_missing_dirs==2)?1:0;
658 
659     if(conn->bits.reuse && ftpc->entrypath) {
660       /* This is a re-used connection. Since we change directory to where the
661          transfer is taking place, we must first get back to the original dir
662          where we ended up after login: */
663       ftpc->count1 = 0; /* we count this as the first path, then we add one
664                           for all upcoming ones in the ftp->dirs[] array */
665       PPSENDF(&conn->proto.ftpc.pp, "CWD %s", ftpc->entrypath);
666       state(conn, FTP_CWD);
667     }
668     else {
669       if(ftpc->dirdepth) {
670         ftpc->count1 = 1;
671         /* issue the first CWD, the rest is sent when the CWD responses are
672            received... */
673         PPSENDF(&conn->proto.ftpc.pp, "CWD %s", ftpc->dirs[ftpc->count1 -1]);
674         state(conn, FTP_CWD);
675       }
676       else {
677         /* No CWD necessary */
678         result = ftp_state_post_cwd(conn);
679       }
680     }
681   }
682   return result;
683 }
684 
685 typedef enum {
686   EPRT,
687   PORT,
688   DONE
689 } ftpport;
690 
ftp_state_use_port(struct connectdata * conn,ftpport fcmd)691 static CURLcode ftp_state_use_port(struct connectdata *conn,
692                                    ftpport fcmd) /* start with this */
693 
694 {
695   CURLcode result = CURLE_OK;
696   struct ftp_conn *ftpc = &conn->proto.ftpc;
697   struct SessionHandle *data=conn->data;
698   curl_socket_t portsock= CURL_SOCKET_BAD;
699   char myhost[256] = "";
700 
701   struct Curl_sockaddr_storage ss;
702   Curl_addrinfo *res, *ai;
703   curl_socklen_t sslen;
704   char hbuf[NI_MAXHOST];
705   struct sockaddr *sa=(struct sockaddr *)&ss;
706   struct sockaddr_in * const sa4 = (void *)sa;
707 #ifdef ENABLE_IPV6
708   struct sockaddr_in6 * const sa6 = (void *)sa;
709 #endif
710   char tmp[1024];
711   static const char mode[][5] = { "EPRT", "PORT" };
712   int rc;
713   int error;
714   char *host=NULL;
715   char *string_ftpport = data->set.str[STRING_FTPPORT];
716   struct Curl_dns_entry *h=NULL;
717   unsigned short port_min = 0;
718   unsigned short port_max = 0;
719   unsigned short port;
720 
721   char *addr = NULL;
722 
723   /* Step 1, figure out what is requested,
724    * accepted format :
725    * (ipv4|ipv6|domain|interface)?(:port(-range)?)?
726    */
727 
728   if(data->set.str[STRING_FTPPORT] &&
729      (strlen(data->set.str[STRING_FTPPORT]) > 1)) {
730 
731 #ifdef ENABLE_IPV6
732     size_t addrlen = INET6_ADDRSTRLEN > strlen(string_ftpport) ?
733       INET6_ADDRSTRLEN : strlen(string_ftpport);
734 #else
735     size_t addrlen = INET_ADDRSTRLEN > strlen(string_ftpport) ?
736       INET_ADDRSTRLEN : strlen(string_ftpport);
737 #endif
738     char *ip_start = string_ftpport;
739     char *ip_end = NULL;
740     char *port_start = NULL;
741     char *port_sep = NULL;
742 
743     addr = calloc(addrlen+1, 1);
744     if (!addr)
745       return CURLE_OUT_OF_MEMORY;
746 
747 #ifdef ENABLE_IPV6
748     if(*string_ftpport == '[') {
749       /* [ipv6]:port(-range) */
750       ip_start = string_ftpport + 1;
751       if((ip_end = strchr(string_ftpport, ']')) != NULL )
752         strncpy(addr, ip_start, ip_end - ip_start);
753     }
754     else
755 #endif
756       if( *string_ftpport == ':') {
757         /* :port */
758         ip_end = string_ftpport;
759     }
760     else if( (ip_end = strchr(string_ftpport, ':')) != NULL) {
761         /* either ipv6 or (ipv4|domain|interface):port(-range) */
762 #ifdef ENABLE_IPV6
763       if(Curl_inet_pton(AF_INET6, string_ftpport, sa6) == 1) {
764         /* ipv6 */
765         port_min = port_max = 0;
766         strcpy(addr, string_ftpport);
767         ip_end = NULL; /* this got no port ! */
768       } else
769 #endif
770       {
771         /* (ipv4|domain|interface):port(-range) */
772         strncpy(addr, string_ftpport, ip_end - ip_start );
773       }
774     }
775     else
776       /* ipv4|interface */
777       strcpy(addr, string_ftpport);
778 
779     /* parse the port */
780     if( ip_end != NULL ) {
781       if((port_start = strchr(ip_end, ':')) != NULL) {
782         port_min = curlx_ultous(strtoul(port_start+1, NULL, 10));
783         if((port_sep = strchr(port_start, '-')) != NULL) {
784           port_max = curlx_ultous(strtoul(port_sep + 1, NULL, 10));
785         }
786         else
787           port_max = port_min;
788       }
789     }
790 
791     /* correct errors like:
792      *  :1234-1230
793      *  :-4711 , in this case port_min is (unsigned)-1,
794      *           therefore port_min > port_max for all cases
795      *           but port_max = (unsigned)-1
796      */
797     if(port_min > port_max )
798       port_min = port_max = 0;
799 
800 
801     if(*addr != '\0') {
802       /* attempt to get the address of the given interface name */
803       if(!Curl_if2ip(conn->ip_addr->ai_family, addr,
804                      hbuf, sizeof(hbuf)))
805         /* not an interface, use the given string as host name instead */
806         host = addr;
807       else
808         host = hbuf; /* use the hbuf for host name */
809     }else
810       /* there was only a port(-range) given, default the host */
811       host = NULL;
812   } /* data->set.ftpport */
813 
814   if(!host) {
815     /* not an interface and not a host name, get default by extracting
816        the IP from the control connection */
817 
818     sslen = sizeof(ss);
819     if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
820       failf(data, "getsockname() failed: %s",
821           Curl_strerror(conn, SOCKERRNO) );
822       if (addr)
823         free(addr);
824       return CURLE_FTP_PORT_FAILED;
825     }
826     switch(sa->sa_family)
827     {
828 #ifdef ENABLE_IPV6
829       case AF_INET6:
830         Curl_inet_ntop(sa->sa_family, &sa6->sin6_addr, hbuf, sizeof(hbuf));
831         break;
832 #endif
833       default:
834         Curl_inet_ntop(sa->sa_family, &sa4->sin_addr, hbuf, sizeof(hbuf));
835         break;
836     }
837     host = hbuf; /* use this host name */
838   }
839 
840   /* resolv ip/host to ip */
841   rc = Curl_resolv(conn, host, 0, &h);
842   if(rc == CURLRESOLV_PENDING)
843     (void)Curl_wait_for_resolv(conn, &h);
844   if(h) {
845     res = h->addr;
846     /* when we return from this function, we can forget about this entry
847        to we can unlock it now already */
848     Curl_resolv_unlock(data, h);
849   } /* (h) */
850   else
851     res = NULL; /* failure! */
852 
853   if (addr)
854     free(addr);
855 
856   if (res == NULL) {
857     failf(data, "Curl_resolv failed, we can not recover!");
858     return CURLE_FTP_PORT_FAILED;
859   }
860 
861   /* step 2, create a socket for the requested address */
862 
863   portsock = CURL_SOCKET_BAD;
864   error = 0;
865   for (ai = res; ai; ai = ai->ai_next) {
866     /*
867      * Workaround for AIX5 getaddrinfo() problem (it doesn't set ai_socktype):
868      */
869     if(ai->ai_socktype == 0)
870       ai->ai_socktype = conn->socktype;
871 
872     portsock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
873     if(portsock == CURL_SOCKET_BAD) {
874       error = SOCKERRNO;
875       continue;
876     }
877     break;
878   }
879   if(!ai) {
880     failf(data, "socket failure: %s", Curl_strerror(conn, error));
881     return CURLE_FTP_PORT_FAILED;
882   }
883 
884   /* step 3, bind to a suitable local address */
885 
886   memcpy(sa, ai->ai_addr, ai->ai_addrlen);
887   sslen = ai->ai_addrlen;
888 
889   for( port = port_min; port <= port_max; ) {
890     if( sa->sa_family == AF_INET )
891       sa4->sin_port = htons(port);
892 #ifdef ENABLE_IPV6
893     else
894       sa6->sin6_port = htons(port);
895 #endif
896     /* Try binding the given address. */
897     if(bind(portsock, sa, sslen) ) {
898       /* It failed. */
899       error = SOCKERRNO;
900       if(error == EADDRNOTAVAIL) {
901 
902         /* The requested bind address is not local.  Use the address used for
903          * the control connection instead and restart the port loop
904          */
905         failf(data, "bind(port=%hu) failed: %s", port,
906               Curl_strerror(conn, error) );
907 
908         sslen = sizeof(ss);
909         if(getsockname(conn->sock[FIRSTSOCKET], sa, &sslen)) {
910           failf(data, "getsockname() failed: %s",
911                 Curl_strerror(conn, SOCKERRNO) );
912           sclose(portsock);
913           return CURLE_FTP_PORT_FAILED;
914         }
915         port = port_min;
916         continue;
917       }
918       else if(error != EADDRINUSE && error != EACCES) {
919         failf(data, "bind(port=%hu) failed: %s", port,
920               Curl_strerror(conn, error) );
921         sclose(portsock);
922         return CURLE_FTP_PORT_FAILED;
923       }
924     }
925     else
926       break;
927 
928     port++;
929   }
930 
931   /* maybe all ports were in use already*/
932   if (port > port_max) {
933     failf(data, "bind() failed, we ran out of ports!");
934     sclose(portsock);
935     return CURLE_FTP_PORT_FAILED;
936   }
937 
938   /* get the name again after the bind() so that we can extract the
939      port number it uses now */
940   sslen = sizeof(ss);
941   if(getsockname(portsock, (struct sockaddr *)sa, &sslen)) {
942     failf(data, "getsockname() failed: %s",
943           Curl_strerror(conn, SOCKERRNO) );
944     sclose(portsock);
945     return CURLE_FTP_PORT_FAILED;
946   }
947 
948   /* step 4, listen on the socket */
949 
950   if(listen(portsock, 1)) {
951     failf(data, "socket failure: %s", Curl_strerror(conn, SOCKERRNO));
952     sclose(portsock);
953     return CURLE_FTP_PORT_FAILED;
954   }
955 
956   /* step 5, send the proper FTP command */
957 
958   /* get a plain printable version of the numerical address to work with
959      below */
960   Curl_printable_address(ai, myhost, sizeof(myhost));
961 
962 #ifdef ENABLE_IPV6
963   if(!conn->bits.ftp_use_eprt && conn->bits.ipv6)
964     /* EPRT is disabled but we are connected to a IPv6 host, so we ignore the
965        request and enable EPRT again! */
966     conn->bits.ftp_use_eprt = TRUE;
967 #endif
968 
969   for (; fcmd != DONE; fcmd++) {
970 
971     if(!conn->bits.ftp_use_eprt && (EPRT == fcmd))
972       /* if disabled, goto next */
973       continue;
974 
975     if((PORT == fcmd) && sa->sa_family != AF_INET)
976       /* PORT is ipv4 only */
977       continue;
978 
979     switch (sa->sa_family) {
980     case AF_INET:
981       port = ntohs(sa4->sin_port);
982       break;
983 #ifdef ENABLE_IPV6
984     case AF_INET6:
985       port = ntohs(sa6->sin6_port);
986       break;
987 #endif
988     default:
989       continue; /* might as well skip this */
990     }
991 
992     if(EPRT == fcmd) {
993       /*
994        * Two fine examples from RFC2428;
995        *
996        * EPRT |1|132.235.1.2|6275|
997        *
998        * EPRT |2|1080::8:800:200C:417A|5282|
999        */
1000 
1001       result = Curl_pp_sendf(&ftpc->pp, "%s |%d|%s|%hu|", mode[fcmd],
1002                              sa->sa_family == AF_INET?1:2,
1003                              myhost, port);
1004       if(result)
1005         return result;
1006       break;
1007     }
1008     else if(PORT == fcmd) {
1009       char *source = myhost;
1010       char *dest = tmp;
1011 
1012       /* translate x.x.x.x to x,x,x,x */
1013       while(source && *source) {
1014         if(*source == '.')
1015           *dest=',';
1016         else
1017           *dest = *source;
1018         dest++;
1019         source++;
1020       }
1021       *dest = 0;
1022       snprintf(dest, 20, ",%d,%d", (int)(port>>8), (int)(port&0xff));
1023 
1024       result = Curl_pp_sendf(&ftpc->pp, "%s %s", mode[fcmd], tmp);
1025       if(result)
1026         return result;
1027       break;
1028     }
1029   }
1030 
1031   /* store which command was sent */
1032   ftpc->count1 = fcmd;
1033 
1034   /* we set the secondary socket variable to this for now, it is only so that
1035      the cleanup function will close it in case we fail before the true
1036      secondary stuff is made */
1037   if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET])
1038     sclose(conn->sock[SECONDARYSOCKET]);
1039   conn->sock[SECONDARYSOCKET] = portsock;
1040 
1041   /* this tcpconnect assignment below is a hackish work-around to make the
1042      multi interface with active FTP work - as it will not wait for a
1043      (passive) connect in Curl_is_connected().
1044 
1045      The *proper* fix is to make sure that the active connection from the
1046      server is done in a non-blocking way. Currently, it is still BLOCKING.
1047   */
1048   conn->bits.tcpconnect = TRUE;
1049 
1050   state(conn, FTP_PORT);
1051   return result;
1052 }
1053 
ftp_state_use_pasv(struct connectdata * conn)1054 static CURLcode ftp_state_use_pasv(struct connectdata *conn)
1055 {
1056   struct ftp_conn *ftpc = &conn->proto.ftpc;
1057   CURLcode result = CURLE_OK;
1058   /*
1059     Here's the excecutive summary on what to do:
1060 
1061     PASV is RFC959, expect:
1062     227 Entering Passive Mode (a1,a2,a3,a4,p1,p2)
1063 
1064     LPSV is RFC1639, expect:
1065     228 Entering Long Passive Mode (4,4,a1,a2,a3,a4,2,p1,p2)
1066 
1067     EPSV is RFC2428, expect:
1068     229 Entering Extended Passive Mode (|||port|)
1069 
1070   */
1071 
1072   static const char mode[][5] = { "EPSV", "PASV" };
1073   int modeoff;
1074 
1075 #ifdef PF_INET6
1076   if(!conn->bits.ftp_use_epsv && conn->bits.ipv6)
1077     /* EPSV is disabled but we are connected to a IPv6 host, so we ignore the
1078        request and enable EPSV again! */
1079     conn->bits.ftp_use_epsv = TRUE;
1080 #endif
1081 
1082   modeoff = conn->bits.ftp_use_epsv?0:1;
1083 
1084   PPSENDF(&ftpc->pp, "%s", mode[modeoff]);
1085 
1086   ftpc->count1 = modeoff;
1087   state(conn, FTP_PASV);
1088   infof(conn->data, "Connect data stream passively\n");
1089 
1090   return result;
1091 }
1092 
1093 /* REST is the last command in the chain of commands when a "head"-like
1094    request is made. Thus, if an actual transfer is to be made this is where
1095    we take off for real. */
ftp_state_post_rest(struct connectdata * conn)1096 static CURLcode ftp_state_post_rest(struct connectdata *conn)
1097 {
1098   CURLcode result = CURLE_OK;
1099   struct FTP *ftp = conn->data->state.proto.ftp;
1100   struct SessionHandle *data = conn->data;
1101 
1102   if(ftp->transfer != FTPTRANSFER_BODY) {
1103     /* doesn't transfer any data */
1104 
1105     /* still possibly do PRE QUOTE jobs */
1106     state(conn, FTP_RETR_PREQUOTE);
1107     result = ftp_state_quote(conn, TRUE, FTP_RETR_PREQUOTE);
1108   }
1109   else if(data->set.ftp_use_port) {
1110     /* We have chosen to use the PORT (or similar) command */
1111     result = ftp_state_use_port(conn, EPRT);
1112   }
1113   else {
1114     /* We have chosen (this is default) to use the PASV (or similar) command */
1115     if(data->set.ftp_use_pret) {
1116       /* The user has requested that we send a PRET command
1117          to prepare the server for the upcoming PASV */
1118       if(!conn->proto.ftpc.file) {
1119         PPSENDF(&conn->proto.ftpc.pp, "PRET %s",
1120                 data->set.str[STRING_CUSTOMREQUEST]?
1121                 data->set.str[STRING_CUSTOMREQUEST]:
1122                 (data->set.ftp_list_only?"NLST":"LIST"));
1123       }
1124       else if(data->set.upload) {
1125         PPSENDF(&conn->proto.ftpc.pp, "PRET STOR %s", conn->proto.ftpc.file);
1126       }
1127       else {
1128         PPSENDF(&conn->proto.ftpc.pp, "PRET RETR %s", conn->proto.ftpc.file);
1129       }
1130       state(conn, FTP_PRET);
1131     }
1132     else {
1133       result = ftp_state_use_pasv(conn);
1134     }
1135   }
1136   return result;
1137 }
1138 
ftp_state_post_size(struct connectdata * conn)1139 static CURLcode ftp_state_post_size(struct connectdata *conn)
1140 {
1141   CURLcode result = CURLE_OK;
1142   struct FTP *ftp = conn->data->state.proto.ftp;
1143   struct ftp_conn *ftpc = &conn->proto.ftpc;
1144 
1145   if((ftp->transfer != FTPTRANSFER_BODY) && ftpc->file) {
1146     /* if a "head"-like request is being made (on a file) */
1147 
1148     /* Determine if server can respond to REST command and therefore
1149        whether it supports range */
1150     PPSENDF(&conn->proto.ftpc.pp, "REST %d", 0);
1151 
1152     state(conn, FTP_REST);
1153   }
1154   else
1155     result = ftp_state_post_rest(conn);
1156 
1157   return result;
1158 }
1159 
ftp_state_post_type(struct connectdata * conn)1160 static CURLcode ftp_state_post_type(struct connectdata *conn)
1161 {
1162   CURLcode result = CURLE_OK;
1163   struct FTP *ftp = conn->data->state.proto.ftp;
1164   struct ftp_conn *ftpc = &conn->proto.ftpc;
1165 
1166   if((ftp->transfer == FTPTRANSFER_INFO) && ftpc->file) {
1167     /* if a "head"-like request is being made (on a file) */
1168 
1169     /* we know ftpc->file is a valid pointer to a file name */
1170     PPSENDF(&ftpc->pp, "SIZE %s", ftpc->file);
1171 
1172     state(conn, FTP_SIZE);
1173   }
1174   else
1175     result = ftp_state_post_size(conn);
1176 
1177   return result;
1178 }
1179 
ftp_state_post_listtype(struct connectdata * conn)1180 static CURLcode ftp_state_post_listtype(struct connectdata *conn)
1181 {
1182   CURLcode result = CURLE_OK;
1183   struct SessionHandle *data = conn->data;
1184 
1185   /* If this output is to be machine-parsed, the NLST command might be better
1186      to use, since the LIST command output is not specified or standard in any
1187      way. It has turned out that the NLST list output is not the same on all
1188      servers either... */
1189 
1190   /*
1191      if FTPFILE_NOCWD was specified, we are currently in
1192      the user's home directory, so we should add the path
1193      as argument for the LIST / NLST / or custom command.
1194      Whether the server will support this, is uncertain.
1195 
1196      The other ftp_filemethods will CWD into dir/dir/ first and
1197      then just do LIST (in that case: nothing to do here)
1198   */
1199   char *cmd,*lstArg,*slashPos;
1200 
1201   lstArg = NULL;
1202   if((data->set.ftp_filemethod == FTPFILE_NOCWD) &&
1203      data->state.path &&
1204      data->state.path[0] &&
1205      strchr(data->state.path,'/')) {
1206 
1207     lstArg = strdup(data->state.path);
1208     if(!lstArg)
1209       return CURLE_OUT_OF_MEMORY;
1210 
1211     /* Check if path does not end with /, as then we cut off the file part */
1212     if(lstArg[strlen(lstArg) - 1] != '/')  {
1213 
1214       /* chop off the file part if format is dir/dir/file */
1215       slashPos = strrchr(lstArg,'/');
1216       if(slashPos)
1217         *(slashPos+1) = '\0';
1218     }
1219   }
1220 
1221   cmd = aprintf( "%s%s%s",
1222                  data->set.str[STRING_CUSTOMREQUEST]?
1223                  data->set.str[STRING_CUSTOMREQUEST]:
1224                  (data->set.ftp_list_only?"NLST":"LIST"),
1225                  lstArg? " ": "",
1226                  lstArg? lstArg: "" );
1227 
1228   if(!cmd) {
1229     if(lstArg)
1230       free(lstArg);
1231     return CURLE_OUT_OF_MEMORY;
1232   }
1233 
1234   PPSENDF(&conn->proto.ftpc.pp, "%s",cmd);
1235 
1236   if(lstArg)
1237     free(lstArg);
1238 
1239   free(cmd);
1240 
1241   state(conn, FTP_LIST);
1242 
1243   return result;
1244 }
1245 
ftp_state_post_retrtype(struct connectdata * conn)1246 static CURLcode ftp_state_post_retrtype(struct connectdata *conn)
1247 {
1248   CURLcode result = CURLE_OK;
1249 
1250   /* We've sent the TYPE, now we must send the list of prequote strings */
1251 
1252   result = ftp_state_quote(conn, TRUE, FTP_RETR_PREQUOTE);
1253 
1254   return result;
1255 }
1256 
ftp_state_post_stortype(struct connectdata * conn)1257 static CURLcode ftp_state_post_stortype(struct connectdata *conn)
1258 {
1259   CURLcode result = CURLE_OK;
1260 
1261   /* We've sent the TYPE, now we must send the list of prequote strings */
1262 
1263   result = ftp_state_quote(conn, TRUE, FTP_STOR_PREQUOTE);
1264 
1265   return result;
1266 }
1267 
ftp_state_post_mdtm(struct connectdata * conn)1268 static CURLcode ftp_state_post_mdtm(struct connectdata *conn)
1269 {
1270   CURLcode result = CURLE_OK;
1271   struct FTP *ftp = conn->data->state.proto.ftp;
1272   struct SessionHandle *data = conn->data;
1273   struct ftp_conn *ftpc = &conn->proto.ftpc;
1274 
1275   /* If we have selected NOBODY and HEADER, it means that we only want file
1276      information. Which in FTP can't be much more than the file size and
1277      date. */
1278   if(data->set.opt_no_body && ftpc->file &&
1279      ftp_need_type(conn, data->set.prefer_ascii)) {
1280     /* The SIZE command is _not_ RFC 959 specified, and therefor many servers
1281        may not support it! It is however the only way we have to get a file's
1282        size! */
1283 
1284     ftp->transfer = FTPTRANSFER_INFO;
1285     /* this means no actual transfer will be made */
1286 
1287     /* Some servers return different sizes for different modes, and thus we
1288        must set the proper type before we check the size */
1289     result = ftp_nb_type(conn, data->set.prefer_ascii, FTP_TYPE);
1290     if(result)
1291       return result;
1292   }
1293   else
1294     result = ftp_state_post_type(conn);
1295 
1296   return result;
1297 }
1298 
1299 /* This is called after the CWD commands have been done in the beginning of
1300    the DO phase */
ftp_state_post_cwd(struct connectdata * conn)1301 static CURLcode ftp_state_post_cwd(struct connectdata *conn)
1302 {
1303   CURLcode result = CURLE_OK;
1304   struct SessionHandle *data = conn->data;
1305   struct ftp_conn *ftpc = &conn->proto.ftpc;
1306 
1307   /* Requested time of file or time-depended transfer? */
1308   if((data->set.get_filetime || data->set.timecondition) && ftpc->file) {
1309 
1310     /* we have requested to get the modified-time of the file, this is a white
1311        spot as the MDTM is not mentioned in RFC959 */
1312     PPSENDF(&ftpc->pp, "MDTM %s", ftpc->file);
1313 
1314     state(conn, FTP_MDTM);
1315   }
1316   else
1317     result = ftp_state_post_mdtm(conn);
1318 
1319   return result;
1320 }
1321 
1322 
1323 /* This is called after the TYPE and possible quote commands have been sent */
ftp_state_ul_setup(struct connectdata * conn,bool sizechecked)1324 static CURLcode ftp_state_ul_setup(struct connectdata *conn,
1325                                    bool sizechecked)
1326 {
1327   CURLcode result = CURLE_OK;
1328   struct FTP *ftp = conn->data->state.proto.ftp;
1329   struct SessionHandle *data = conn->data;
1330   struct ftp_conn *ftpc = &conn->proto.ftpc;
1331   int seekerr = CURL_SEEKFUNC_OK;
1332 
1333   if((data->state.resume_from && !sizechecked) ||
1334      ((data->state.resume_from > 0) && sizechecked)) {
1335     /* we're about to continue the uploading of a file */
1336     /* 1. get already existing file's size. We use the SIZE command for this
1337        which may not exist in the server!  The SIZE command is not in
1338        RFC959. */
1339 
1340     /* 2. This used to set REST. But since we can do append, we
1341        don't another ftp command. We just skip the source file
1342        offset and then we APPEND the rest on the file instead */
1343 
1344     /* 3. pass file-size number of bytes in the source file */
1345     /* 4. lower the infilesize counter */
1346     /* => transfer as usual */
1347 
1348     if(data->state.resume_from < 0 ) {
1349       /* Got no given size to start from, figure it out */
1350       PPSENDF(&ftpc->pp, "SIZE %s", ftpc->file);
1351       state(conn, FTP_STOR_SIZE);
1352       return result;
1353     }
1354 
1355     /* enable append */
1356     data->set.ftp_append = TRUE;
1357 
1358     /* Let's read off the proper amount of bytes from the input. */
1359     if(conn->seek_func) {
1360       seekerr = conn->seek_func(conn->seek_client, data->state.resume_from,
1361                                 SEEK_SET);
1362     }
1363 
1364     if(seekerr != CURL_SEEKFUNC_OK) {
1365       if(seekerr != CURL_SEEKFUNC_CANTSEEK) {
1366         failf(data, "Could not seek stream");
1367         return CURLE_FTP_COULDNT_USE_REST;
1368       }
1369       /* seekerr == CURL_SEEKFUNC_CANTSEEK (can't seek to offset) */
1370       else {
1371         curl_off_t passed=0;
1372         do {
1373           size_t readthisamountnow =
1374             (data->state.resume_from - passed > CURL_OFF_T_C(BUFSIZE)) ?
1375             BUFSIZE : curlx_sotouz(data->state.resume_from - passed);
1376 
1377           size_t actuallyread =
1378             conn->fread_func(data->state.buffer, 1, readthisamountnow,
1379                              conn->fread_in);
1380 
1381           passed += actuallyread;
1382           if((actuallyread == 0) || (actuallyread > readthisamountnow)) {
1383             /* this checks for greater-than only to make sure that the
1384                CURL_READFUNC_ABORT return code still aborts */
1385             failf(data, "Failed to read data");
1386             return CURLE_FTP_COULDNT_USE_REST;
1387           }
1388         } while(passed < data->state.resume_from);
1389       }
1390     }
1391     /* now, decrease the size of the read */
1392     if(data->set.infilesize>0) {
1393       data->set.infilesize -= data->state.resume_from;
1394 
1395       if(data->set.infilesize <= 0) {
1396         infof(data, "File already completely uploaded\n");
1397 
1398         /* no data to transfer */
1399         Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
1400 
1401         /* Set ->transfer so that we won't get any error in
1402          * ftp_done() because we didn't transfer anything! */
1403         ftp->transfer = FTPTRANSFER_NONE;
1404 
1405         state(conn, FTP_STOP);
1406         return CURLE_OK;
1407       }
1408     }
1409     /* we've passed, proceed as normal */
1410   } /* resume_from */
1411 
1412   PPSENDF(&ftpc->pp, data->set.ftp_append?"APPE %s":"STOR %s",
1413           ftpc->file);
1414 
1415   state(conn, FTP_STOR);
1416 
1417   return result;
1418 }
1419 
ftp_state_quote(struct connectdata * conn,bool init,ftpstate instate)1420 static CURLcode ftp_state_quote(struct connectdata *conn,
1421                                 bool init,
1422                                 ftpstate instate)
1423 {
1424   CURLcode result = CURLE_OK;
1425   struct SessionHandle *data = conn->data;
1426   struct FTP *ftp = data->state.proto.ftp;
1427   struct ftp_conn *ftpc = &conn->proto.ftpc;
1428   bool quote=FALSE;
1429   struct curl_slist *item;
1430 
1431   switch(instate) {
1432   case FTP_QUOTE:
1433   default:
1434     item = data->set.quote;
1435     break;
1436   case FTP_RETR_PREQUOTE:
1437   case FTP_STOR_PREQUOTE:
1438     item = data->set.prequote;
1439     break;
1440   case FTP_POSTQUOTE:
1441     item = data->set.postquote;
1442     break;
1443   }
1444 
1445   /*
1446    * This state uses:
1447    * 'count1' to iterate over the commands to send
1448    * 'count2' to store wether to allow commands to fail
1449    */
1450 
1451   if(init)
1452     ftpc->count1 = 0;
1453   else
1454     ftpc->count1++;
1455 
1456   if(item) {
1457     int i = 0;
1458 
1459     /* Skip count1 items in the linked list */
1460     while((i< ftpc->count1) && item) {
1461       item = item->next;
1462       i++;
1463     }
1464     if(item) {
1465       char *cmd = item->data;
1466       if(cmd[0] == '*') {
1467         cmd++;
1468         ftpc->count2 = 1; /* the sent command is allowed to fail */
1469       }
1470       else
1471         ftpc->count2 = 0; /* failure means cancel operation */
1472 
1473       PPSENDF(&ftpc->pp, "%s", cmd);
1474       state(conn, instate);
1475       quote = TRUE;
1476     }
1477   }
1478 
1479   if(!quote) {
1480     /* No more quote to send, continue to ... */
1481     switch(instate) {
1482     case FTP_QUOTE:
1483     default:
1484       result = ftp_state_cwd(conn);
1485       break;
1486     case FTP_RETR_PREQUOTE:
1487       if(ftp->transfer != FTPTRANSFER_BODY)
1488         state(conn, FTP_STOP);
1489       else {
1490         if(ftpc->known_filesize != -1) {
1491           Curl_pgrsSetDownloadSize(data, ftpc->known_filesize);
1492           result = ftp_state_post_retr_size(conn, ftpc->known_filesize);
1493         }
1494         else {
1495           PPSENDF(&ftpc->pp, "SIZE %s", ftpc->file);
1496           state(conn, FTP_RETR_SIZE);
1497         }
1498       }
1499       break;
1500     case FTP_STOR_PREQUOTE:
1501       result = ftp_state_ul_setup(conn, FALSE);
1502       break;
1503     case FTP_POSTQUOTE:
1504       break;
1505     }
1506   }
1507 
1508   return result;
1509 }
1510 
ftp_state_pasv_resp(struct connectdata * conn,int ftpcode)1511 static CURLcode ftp_state_pasv_resp(struct connectdata *conn,
1512                                     int ftpcode)
1513 {
1514   struct ftp_conn *ftpc = &conn->proto.ftpc;
1515   CURLcode result;
1516   struct SessionHandle *data=conn->data;
1517   Curl_addrinfo *conninfo;
1518   struct Curl_dns_entry *addr=NULL;
1519   int rc;
1520   unsigned short connectport; /* the local port connect() should use! */
1521   unsigned short newport=0; /* remote port */
1522   bool connected;
1523 
1524   /* newhost must be able to hold a full IP-style address in ASCII, which
1525      in the IPv6 case means 5*8-1 = 39 letters */
1526 #define NEWHOST_BUFSIZE 48
1527   char newhost[NEWHOST_BUFSIZE];
1528   char *str=&data->state.buffer[4];  /* start on the first letter */
1529 
1530   if((ftpc->count1 == 0) &&
1531      (ftpcode == 229)) {
1532     /* positive EPSV response */
1533     char *ptr = strchr(str, '(');
1534     if(ptr) {
1535       unsigned int num;
1536       char separator[4];
1537       ptr++;
1538       if(5  == sscanf(ptr, "%c%c%c%u%c",
1539                       &separator[0],
1540                       &separator[1],
1541                       &separator[2],
1542                       &num,
1543                       &separator[3])) {
1544         const char sep1 = separator[0];
1545         int i;
1546 
1547         /* The four separators should be identical, or else this is an oddly
1548            formatted reply and we bail out immediately. */
1549         for(i=1; i<4; i++) {
1550           if(separator[i] != sep1) {
1551             ptr=NULL; /* set to NULL to signal error */
1552             break;
1553           }
1554         }
1555         if(ptr) {
1556           newport = (unsigned short)(num & 0xffff);
1557 
1558           if(conn->bits.tunnel_proxy ||
1559              data->set.proxytype == CURLPROXY_SOCKS5 ||
1560              data->set.proxytype == CURLPROXY_SOCKS5_HOSTNAME ||
1561              data->set.proxytype == CURLPROXY_SOCKS4 ||
1562              data->set.proxytype == CURLPROXY_SOCKS4A)
1563             /* proxy tunnel -> use other host info because ip_addr_str is the
1564                proxy address not the ftp host */
1565             snprintf(newhost, sizeof(newhost), "%s", conn->host.name);
1566           else
1567             /* use the same IP we are already connected to */
1568             snprintf(newhost, NEWHOST_BUFSIZE, "%s", conn->ip_addr_str);
1569         }
1570       }
1571       else
1572         ptr=NULL;
1573     }
1574     if(!ptr) {
1575       failf(data, "Weirdly formatted EPSV reply");
1576       return CURLE_FTP_WEIRD_PASV_REPLY;
1577     }
1578   }
1579   else if((ftpc->count1 == 1) &&
1580           (ftpcode == 227)) {
1581     /* positive PASV response */
1582     int ip[4];
1583     int port[2];
1584 
1585     /*
1586      * Scan for a sequence of six comma-separated numbers and use them as
1587      * IP+port indicators.
1588      *
1589      * Found reply-strings include:
1590      * "227 Entering Passive Mode (127,0,0,1,4,51)"
1591      * "227 Data transfer will passively listen to 127,0,0,1,4,51"
1592      * "227 Entering passive mode. 127,0,0,1,4,51"
1593      */
1594     while(*str) {
1595       if(6 == sscanf(str, "%d,%d,%d,%d,%d,%d",
1596                       &ip[0], &ip[1], &ip[2], &ip[3],
1597                       &port[0], &port[1]))
1598         break;
1599       str++;
1600     }
1601 
1602     if(!*str) {
1603       failf(data, "Couldn't interpret the 227-response");
1604       return CURLE_FTP_WEIRD_227_FORMAT;
1605     }
1606 
1607     /* we got OK from server */
1608     if(data->set.ftp_skip_ip) {
1609       /* told to ignore the remotely given IP but instead use the one we used
1610          for the control connection */
1611       infof(data, "Skips %d.%d.%d.%d for data connection, uses %s instead\n",
1612             ip[0], ip[1], ip[2], ip[3],
1613             conn->ip_addr_str);
1614       if(conn->bits.tunnel_proxy ||
1615           data->set.proxytype == CURLPROXY_SOCKS5 ||
1616           data->set.proxytype == CURLPROXY_SOCKS5_HOSTNAME ||
1617           data->set.proxytype == CURLPROXY_SOCKS4 ||
1618           data->set.proxytype == CURLPROXY_SOCKS4A)
1619         /* proxy tunnel -> use other host info because ip_addr_str is the
1620            proxy address not the ftp host */
1621         snprintf(newhost, sizeof(newhost), "%s", conn->host.name);
1622       else
1623         snprintf(newhost, sizeof(newhost), "%s", conn->ip_addr_str);
1624     }
1625     else
1626       snprintf(newhost, sizeof(newhost),
1627                "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
1628     newport = (unsigned short)(((port[0]<<8) + port[1]) & 0xffff);
1629   }
1630   else if(ftpc->count1 == 0) {
1631     /* EPSV failed, move on to PASV */
1632 
1633     /* disable it for next transfer */
1634     conn->bits.ftp_use_epsv = FALSE;
1635     infof(data, "disabling EPSV usage\n");
1636 
1637     PPSENDF(&ftpc->pp, "PASV", NULL);
1638     ftpc->count1++;
1639     /* remain in the FTP_PASV state */
1640     return result;
1641   }
1642   else {
1643     failf(data, "Bad PASV/EPSV response: %03d", ftpcode);
1644     return CURLE_FTP_WEIRD_PASV_REPLY;
1645   }
1646 
1647   if(data->set.str[STRING_PROXY] && *data->set.str[STRING_PROXY]) {
1648     /*
1649      * This is a tunnel through a http proxy and we need to connect to the
1650      * proxy again here.
1651      *
1652      * We don't want to rely on a former host lookup that might've expired
1653      * now, instead we remake the lookup here and now!
1654      */
1655     rc = Curl_resolv(conn, conn->proxy.name, (int)conn->port, &addr);
1656     if(rc == CURLRESOLV_PENDING)
1657       /* BLOCKING, ignores the return code but 'addr' will be NULL in
1658          case of failure */
1659       (void)Curl_wait_for_resolv(conn, &addr);
1660 
1661     connectport =
1662       (unsigned short)conn->port; /* we connect to the proxy's port */
1663 
1664     if(!addr) {
1665       failf(data, "Can't resolve proxy host %s:%hu",
1666             conn->proxy.name, connectport);
1667       return CURLE_FTP_CANT_GET_HOST;
1668     }
1669   }
1670   else {
1671     /* normal, direct, ftp connection */
1672     rc = Curl_resolv(conn, newhost, newport, &addr);
1673     if(rc == CURLRESOLV_PENDING)
1674       /* BLOCKING */
1675       (void)Curl_wait_for_resolv(conn, &addr);
1676 
1677     connectport = newport; /* we connect to the remote port */
1678 
1679     if(!addr) {
1680       failf(data, "Can't resolve new host %s:%hu", newhost, connectport);
1681       return CURLE_FTP_CANT_GET_HOST;
1682     }
1683   }
1684 
1685   result = Curl_connecthost(conn,
1686                             addr,
1687                             &conn->sock[SECONDARYSOCKET],
1688                             &conninfo,
1689                             &connected);
1690 
1691   Curl_resolv_unlock(data, addr); /* we're done using this address */
1692 
1693   if(result && ftpc->count1 == 0 && ftpcode == 229) {
1694     infof(data, "got positive EPSV response, but can't connect. "
1695           "Disabling EPSV\n");
1696     /* disable it for next transfer */
1697     conn->bits.ftp_use_epsv = FALSE;
1698     data->state.errorbuf = FALSE; /* allow error message to get rewritten */
1699     PPSENDF(&ftpc->pp, "PASV", NULL);
1700     ftpc->count1++;
1701     /* remain in the FTP_PASV state */
1702     return result;
1703  }
1704 
1705   if(result)
1706     return result;
1707 
1708   conn->bits.tcpconnect = connected; /* simply TRUE or FALSE */
1709 
1710   /*
1711    * When this is used from the multi interface, this might've returned with
1712    * the 'connected' set to FALSE and thus we are now awaiting a non-blocking
1713    * connect to connect and we should not be "hanging" here waiting.
1714    */
1715 
1716   if(data->set.verbose)
1717     /* this just dumps information about this second connection */
1718     ftp_pasv_verbose(conn, conninfo, newhost, connectport);
1719 
1720   switch(data->set.proxytype) {
1721     /* FIX: this MUST wait for a proper connect first if 'connected' is
1722      * FALSE */
1723   case CURLPROXY_SOCKS5:
1724   case CURLPROXY_SOCKS5_HOSTNAME:
1725     result = Curl_SOCKS5(conn->proxyuser, conn->proxypasswd, newhost, newport,
1726                          SECONDARYSOCKET, conn);
1727     break;
1728   case CURLPROXY_SOCKS4:
1729     result = Curl_SOCKS4(conn->proxyuser, newhost, newport,
1730                          SECONDARYSOCKET, conn, FALSE);
1731     break;
1732   case CURLPROXY_SOCKS4A:
1733     result = Curl_SOCKS4(conn->proxyuser, newhost, newport,
1734                          SECONDARYSOCKET, conn, TRUE);
1735     break;
1736   case CURLPROXY_HTTP:
1737   case CURLPROXY_HTTP_1_0:
1738     /* do nothing here. handled later. */
1739     break;
1740   default:
1741     failf(data, "unknown proxytype option given");
1742     result = CURLE_COULDNT_CONNECT;
1743     break;
1744   }
1745 
1746   if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
1747     /* FIX: this MUST wait for a proper connect first if 'connected' is
1748      * FALSE */
1749 
1750     /* BLOCKING */
1751     /* We want "seamless" FTP operations through HTTP proxy tunnel */
1752 
1753     /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the member
1754      * conn->proto.http; we want FTP through HTTP and we have to change the
1755      * member temporarily for connecting to the HTTP proxy. After
1756      * Curl_proxyCONNECT we have to set back the member to the original struct
1757      * FTP pointer
1758      */
1759     struct HTTP http_proxy;
1760     struct FTP *ftp_save = data->state.proto.ftp;
1761     memset(&http_proxy, 0, sizeof(http_proxy));
1762     data->state.proto.http = &http_proxy;
1763 
1764     result = Curl_proxyCONNECT(conn, SECONDARYSOCKET, newhost, newport);
1765 
1766     data->state.proto.ftp = ftp_save;
1767 
1768     if(CURLE_OK != result)
1769       return result;
1770   }
1771 
1772 
1773   state(conn, FTP_STOP); /* this phase is completed */
1774 
1775   return result;
1776 }
1777 
ftp_state_port_resp(struct connectdata * conn,int ftpcode)1778 static CURLcode ftp_state_port_resp(struct connectdata *conn,
1779                                     int ftpcode)
1780 {
1781   struct SessionHandle *data = conn->data;
1782   struct ftp_conn *ftpc = &conn->proto.ftpc;
1783   ftpport fcmd = (ftpport)ftpc->count1;
1784   CURLcode result = CURLE_OK;
1785 
1786   if(ftpcode != 200) {
1787     /* the command failed */
1788 
1789     if(EPRT == fcmd) {
1790       infof(data, "disabling EPRT usage\n");
1791       conn->bits.ftp_use_eprt = FALSE;
1792     }
1793     fcmd++;
1794 
1795     if(fcmd == DONE) {
1796       failf(data, "Failed to do PORT");
1797       result = CURLE_FTP_PORT_FAILED;
1798     }
1799     else
1800       /* try next */
1801       result = ftp_state_use_port(conn, fcmd);
1802   }
1803   else {
1804     infof(data, "Connect data stream actively\n");
1805     state(conn, FTP_STOP); /* end of DO phase */
1806   }
1807 
1808   return result;
1809 }
1810 
ftp_state_mdtm_resp(struct connectdata * conn,int ftpcode)1811 static CURLcode ftp_state_mdtm_resp(struct connectdata *conn,
1812                                     int ftpcode)
1813 {
1814   CURLcode result = CURLE_OK;
1815   struct SessionHandle *data=conn->data;
1816   struct FTP *ftp = data->state.proto.ftp;
1817   struct ftp_conn *ftpc = &conn->proto.ftpc;
1818 
1819   switch(ftpcode) {
1820   case 213:
1821     {
1822       /* we got a time. Format should be: "YYYYMMDDHHMMSS[.sss]" where the
1823          last .sss part is optional and means fractions of a second */
1824       int year, month, day, hour, minute, second;
1825       char *buf = data->state.buffer;
1826       if(6 == sscanf(buf+4, "%04d%02d%02d%02d%02d%02d",
1827                      &year, &month, &day, &hour, &minute, &second)) {
1828         /* we have a time, reformat it */
1829         time_t secs=time(NULL);
1830         /* using the good old yacc/bison yuck */
1831         snprintf(buf, sizeof(conn->data->state.buffer),
1832                  "%04d%02d%02d %02d:%02d:%02d GMT",
1833                  year, month, day, hour, minute, second);
1834         /* now, convert this into a time() value: */
1835         data->info.filetime = (long)curl_getdate(buf, &secs);
1836       }
1837 
1838 #ifdef CURL_FTP_HTTPSTYLE_HEAD
1839       /* If we asked for a time of the file and we actually got one as well,
1840          we "emulate" a HTTP-style header in our output. */
1841 
1842       if(data->set.opt_no_body &&
1843          ftpc->file &&
1844          data->set.get_filetime &&
1845          (data->info.filetime>=0) ) {
1846         time_t filetime = (time_t)data->info.filetime;
1847         struct tm buffer;
1848         const struct tm *tm = &buffer;
1849 
1850         result = Curl_gmtime(filetime, &buffer);
1851         if(result)
1852           return result;
1853 
1854         /* format: "Tue, 15 Nov 1994 12:45:26" */
1855         snprintf(buf, BUFSIZE-1,
1856                  "Last-Modified: %s, %02d %s %4d %02d:%02d:%02d GMT\r\n",
1857                  Curl_wkday[tm->tm_wday?tm->tm_wday-1:6],
1858                  tm->tm_mday,
1859                  Curl_month[tm->tm_mon],
1860                  tm->tm_year + 1900,
1861                  tm->tm_hour,
1862                  tm->tm_min,
1863                  tm->tm_sec);
1864         result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
1865         if(result)
1866           return result;
1867       } /* end of a ridiculous amount of conditionals */
1868 #endif
1869     }
1870     break;
1871   default:
1872     infof(data, "unsupported MDTM reply format\n");
1873     break;
1874   case 550: /* "No such file or directory" */
1875     failf(data, "Given file does not exist");
1876     result = CURLE_FTP_COULDNT_RETR_FILE;
1877     break;
1878   }
1879 
1880   if(data->set.timecondition) {
1881     if((data->info.filetime > 0) && (data->set.timevalue > 0)) {
1882       switch(data->set.timecondition) {
1883       case CURL_TIMECOND_IFMODSINCE:
1884       default:
1885         if(data->info.filetime <= data->set.timevalue) {
1886           infof(data, "The requested document is not new enough\n");
1887           ftp->transfer = FTPTRANSFER_NONE; /* mark to not transfer data */
1888           data->info.timecond = TRUE;
1889           state(conn, FTP_STOP);
1890           return CURLE_OK;
1891         }
1892         break;
1893       case CURL_TIMECOND_IFUNMODSINCE:
1894         if(data->info.filetime > data->set.timevalue) {
1895           infof(data, "The requested document is not old enough\n");
1896           ftp->transfer = FTPTRANSFER_NONE; /* mark to not transfer data */
1897           data->info.timecond = TRUE;
1898           state(conn, FTP_STOP);
1899           return CURLE_OK;
1900         }
1901         break;
1902       } /* switch */
1903     }
1904     else {
1905       infof(data, "Skipping time comparison\n");
1906     }
1907   }
1908 
1909   if(!result)
1910     result = ftp_state_post_mdtm(conn);
1911 
1912   return result;
1913 }
1914 
ftp_state_type_resp(struct connectdata * conn,int ftpcode,ftpstate instate)1915 static CURLcode ftp_state_type_resp(struct connectdata *conn,
1916                                     int ftpcode,
1917                                     ftpstate instate)
1918 {
1919   CURLcode result = CURLE_OK;
1920   struct SessionHandle *data=conn->data;
1921 
1922   if(ftpcode/100 != 2) {
1923     /* "sasserftpd" and "(u)r(x)bot ftpd" both responds with 226 after a
1924        successful 'TYPE I'. While that is not as RFC959 says, it is still a
1925        positive response code and we allow that. */
1926     failf(data, "Couldn't set desired mode");
1927     return CURLE_FTP_COULDNT_SET_TYPE;
1928   }
1929   if(ftpcode != 200)
1930     infof(data, "Got a %03d response code instead of the assumed 200\n",
1931           ftpcode);
1932 
1933   if(instate == FTP_TYPE)
1934     result = ftp_state_post_type(conn);
1935   else if(instate == FTP_LIST_TYPE)
1936     result = ftp_state_post_listtype(conn);
1937   else if(instate == FTP_RETR_TYPE)
1938     result = ftp_state_post_retrtype(conn);
1939   else if(instate == FTP_STOR_TYPE)
1940     result = ftp_state_post_stortype(conn);
1941 
1942   return result;
1943 }
1944 
ftp_state_post_retr_size(struct connectdata * conn,curl_off_t filesize)1945 static CURLcode ftp_state_post_retr_size(struct connectdata *conn,
1946                                          curl_off_t filesize)
1947 {
1948   CURLcode result = CURLE_OK;
1949   struct SessionHandle *data=conn->data;
1950   struct FTP *ftp = data->state.proto.ftp;
1951   struct ftp_conn *ftpc = &conn->proto.ftpc;
1952 
1953   if(data->set.max_filesize && (filesize > data->set.max_filesize)) {
1954     failf(data, "Maximum file size exceeded");
1955     return CURLE_FILESIZE_EXCEEDED;
1956   }
1957   ftp->downloadsize = filesize;
1958 
1959   if(data->state.resume_from) {
1960     /* We always (attempt to) get the size of downloads, so it is done before
1961        this even when not doing resumes. */
1962     if(filesize == -1) {
1963       infof(data, "ftp server doesn't support SIZE\n");
1964       /* We couldn't get the size and therefore we can't know if there really
1965          is a part of the file left to get, although the server will just
1966          close the connection when we start the connection so it won't cause
1967          us any harm, just not make us exit as nicely. */
1968     }
1969     else {
1970       /* We got a file size report, so we check that there actually is a
1971          part of the file left to get, or else we go home.  */
1972       if(data->state.resume_from< 0) {
1973         /* We're supposed to download the last abs(from) bytes */
1974         if(filesize < -data->state.resume_from) {
1975           failf(data, "Offset (%" FORMAT_OFF_T
1976                 ") was beyond file size (%" FORMAT_OFF_T ")",
1977                 data->state.resume_from, filesize);
1978           return CURLE_BAD_DOWNLOAD_RESUME;
1979         }
1980         /* convert to size to download */
1981         ftp->downloadsize = -data->state.resume_from;
1982         /* download from where? */
1983         data->state.resume_from = filesize - ftp->downloadsize;
1984       }
1985       else {
1986         if(filesize < data->state.resume_from) {
1987           failf(data, "Offset (%" FORMAT_OFF_T
1988                 ") was beyond file size (%" FORMAT_OFF_T ")",
1989                 data->state.resume_from, filesize);
1990           return CURLE_BAD_DOWNLOAD_RESUME;
1991         }
1992         /* Now store the number of bytes we are expected to download */
1993         ftp->downloadsize = filesize-data->state.resume_from;
1994       }
1995     }
1996 
1997     if(ftp->downloadsize == 0) {
1998       /* no data to transfer */
1999       Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
2000       infof(data, "File already completely downloaded\n");
2001 
2002       /* Set ->transfer so that we won't get any error in ftp_done()
2003        * because we didn't transfer the any file */
2004       ftp->transfer = FTPTRANSFER_NONE;
2005       state(conn, FTP_STOP);
2006       return CURLE_OK;
2007     }
2008 
2009     /* Set resume file transfer offset */
2010     infof(data, "Instructs server to resume from offset %" FORMAT_OFF_T
2011           "\n", data->state.resume_from);
2012 
2013     PPSENDF(&ftpc->pp, "REST %" FORMAT_OFF_T, data->state.resume_from);
2014 
2015     state(conn, FTP_RETR_REST);
2016 
2017   }
2018   else {
2019     /* no resume */
2020     PPSENDF(&ftpc->pp, "RETR %s", ftpc->file);
2021     state(conn, FTP_RETR);
2022   }
2023 
2024   return result;
2025 }
2026 
ftp_state_size_resp(struct connectdata * conn,int ftpcode,ftpstate instate)2027 static CURLcode ftp_state_size_resp(struct connectdata *conn,
2028                                     int ftpcode,
2029                                     ftpstate instate)
2030 {
2031   CURLcode result = CURLE_OK;
2032   struct SessionHandle *data=conn->data;
2033   curl_off_t filesize;
2034   char *buf = data->state.buffer;
2035 
2036   /* get the size from the ascii string: */
2037   filesize = (ftpcode == 213)?curlx_strtoofft(buf+4, NULL, 0):-1;
2038 
2039   if(instate == FTP_SIZE) {
2040 #ifdef CURL_FTP_HTTPSTYLE_HEAD
2041     if(-1 != filesize) {
2042       snprintf(buf, sizeof(data->state.buffer),
2043                "Content-Length: %" FORMAT_OFF_T "\r\n", filesize);
2044       result = Curl_client_write(conn, CLIENTWRITE_BOTH, buf, 0);
2045       if(result)
2046         return result;
2047     }
2048 #endif
2049     Curl_pgrsSetDownloadSize(data, filesize);
2050     result = ftp_state_post_size(conn);
2051   }
2052   else if(instate == FTP_RETR_SIZE) {
2053     Curl_pgrsSetDownloadSize(data, filesize);
2054     result = ftp_state_post_retr_size(conn, filesize);
2055   }
2056   else if(instate == FTP_STOR_SIZE) {
2057     data->state.resume_from = filesize;
2058     result = ftp_state_ul_setup(conn, TRUE);
2059   }
2060 
2061   return result;
2062 }
2063 
ftp_state_rest_resp(struct connectdata * conn,int ftpcode,ftpstate instate)2064 static CURLcode ftp_state_rest_resp(struct connectdata *conn,
2065                                     int ftpcode,
2066                                     ftpstate instate)
2067 {
2068   CURLcode result = CURLE_OK;
2069   struct ftp_conn *ftpc = &conn->proto.ftpc;
2070 
2071   switch(instate) {
2072   case FTP_REST:
2073   default:
2074 #ifdef CURL_FTP_HTTPSTYLE_HEAD
2075     if(ftpcode == 350) {
2076       char buffer[24]= { "Accept-ranges: bytes\r\n" };
2077       result = Curl_client_write(conn, CLIENTWRITE_BOTH, buffer, 0);
2078       if(result)
2079         return result;
2080     }
2081 #endif
2082     result = ftp_state_post_rest(conn);
2083     break;
2084 
2085   case FTP_RETR_REST:
2086     if(ftpcode != 350) {
2087       failf(conn->data, "Couldn't use REST");
2088       result = CURLE_FTP_COULDNT_USE_REST;
2089     }
2090     else {
2091       PPSENDF(&ftpc->pp, "RETR %s", ftpc->file);
2092       state(conn, FTP_RETR);
2093     }
2094     break;
2095   }
2096 
2097   return result;
2098 }
2099 
ftp_state_stor_resp(struct connectdata * conn,int ftpcode)2100 static CURLcode ftp_state_stor_resp(struct connectdata *conn,
2101                                     int ftpcode)
2102 {
2103   CURLcode result = CURLE_OK;
2104   struct SessionHandle *data = conn->data;
2105   struct FTP *ftp = data->state.proto.ftp;
2106 
2107   if(ftpcode>=400) {
2108     failf(data, "Failed FTP upload: %0d", ftpcode);
2109     /* oops, we never close the sockets! */
2110     return CURLE_UPLOAD_FAILED;
2111   }
2112 
2113   if(data->set.ftp_use_port) {
2114     /* BLOCKING */
2115     /* PORT means we are now awaiting the server to connect to us. */
2116     result = AllowServerConnect(conn);
2117     if( result )
2118       return result;
2119   }
2120 
2121   if(conn->ssl[SECONDARYSOCKET].use) {
2122     /* since we only have a plaintext TCP connection here, we must now
2123        do the TLS stuff */
2124     infof(data, "Doing the SSL/TLS handshake on the data stream\n");
2125     /* BLOCKING */
2126     result = Curl_ssl_connect(conn, SECONDARYSOCKET);
2127     if(result)
2128       return result;
2129   }
2130 
2131   *(ftp->bytecountp)=0;
2132 
2133   /* When we know we're uploading a specified file, we can get the file
2134      size prior to the actual upload. */
2135 
2136   Curl_pgrsSetUploadSize(data, data->set.infilesize);
2137 
2138   /* set the SO_SNDBUF for the secondary socket for those who need it */
2139   Curl_sndbufset(conn->sock[SECONDARYSOCKET]);
2140 
2141   Curl_setup_transfer(conn, -1, -1, FALSE, NULL, /* no download */
2142                       SECONDARYSOCKET, ftp->bytecountp);
2143   state(conn, FTP_STOP);
2144 
2145   conn->proto.ftpc.pp.pending_resp = TRUE; /* expect a server response */
2146 
2147   return result;
2148 }
2149 
2150 /* for LIST and RETR responses */
ftp_state_get_resp(struct connectdata * conn,int ftpcode,ftpstate instate)2151 static CURLcode ftp_state_get_resp(struct connectdata *conn,
2152                                     int ftpcode,
2153                                     ftpstate instate)
2154 {
2155   CURLcode result = CURLE_OK;
2156   struct SessionHandle *data = conn->data;
2157   struct FTP *ftp = data->state.proto.ftp;
2158   char *buf = data->state.buffer;
2159 
2160   if((ftpcode == 150) || (ftpcode == 125)) {
2161 
2162     /*
2163       A;
2164       150 Opening BINARY mode data connection for /etc/passwd (2241
2165       bytes).  (ok, the file is being transferred)
2166 
2167       B:
2168       150 Opening ASCII mode data connection for /bin/ls
2169 
2170       C:
2171       150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes).
2172 
2173       D:
2174       150 Opening ASCII mode data connection for /linux/fisk/kpanelrc (0.0.0.0,0) (545 bytes).
2175 
2176       E:
2177       125 Data connection already open; Transfer starting. */
2178 
2179     curl_off_t size=-1; /* default unknown size */
2180 
2181 
2182     /*
2183      * It appears that there are FTP-servers that return size 0 for files when
2184      * SIZE is used on the file while being in BINARY mode. To work around
2185      * that (stupid) behavior, we attempt to parse the RETR response even if
2186      * the SIZE returned size zero.
2187      *
2188      * Debugging help from Salvatore Sorrentino on February 26, 2003.
2189      */
2190 
2191     if((instate != FTP_LIST) &&
2192        !data->set.prefer_ascii &&
2193        (ftp->downloadsize < 1)) {
2194       /*
2195        * It seems directory listings either don't show the size or very
2196        * often uses size 0 anyway. ASCII transfers may very well turn out
2197        * that the transferred amount of data is not the same as this line
2198        * tells, why using this number in those cases only confuses us.
2199        *
2200        * Example D above makes this parsing a little tricky */
2201       char *bytes;
2202       bytes=strstr(buf, " bytes");
2203       if(bytes--) {
2204         long in=(long)(bytes-buf);
2205         /* this is a hint there is size information in there! ;-) */
2206         while(--in) {
2207           /* scan for the left parenthesis and break there */
2208           if('(' == *bytes)
2209             break;
2210           /* skip only digits */
2211           if(!ISDIGIT(*bytes)) {
2212             bytes=NULL;
2213             break;
2214           }
2215           /* one more estep backwards */
2216           bytes--;
2217         }
2218         /* if we have nothing but digits: */
2219         if(bytes++) {
2220           /* get the number! */
2221           size = curlx_strtoofft(bytes, NULL, 0);
2222         }
2223       }
2224     }
2225     else if(ftp->downloadsize > -1)
2226       size = ftp->downloadsize;
2227 
2228     if(data->set.ftp_use_port) {
2229       /* BLOCKING */
2230       result = AllowServerConnect(conn);
2231       if( result )
2232         return result;
2233     }
2234 
2235     if(conn->ssl[SECONDARYSOCKET].use) {
2236       /* since we only have a plaintext TCP connection here, we must now
2237          do the TLS stuff */
2238       infof(data, "Doing the SSL/TLS handshake on the data stream\n");
2239       result = Curl_ssl_connect(conn, SECONDARYSOCKET);
2240       if(result)
2241         return result;
2242     }
2243 
2244     if(size > data->req.maxdownload && data->req.maxdownload > 0)
2245       size = data->req.size = data->req.maxdownload;
2246     else if((instate != FTP_LIST) && (data->set.prefer_ascii))
2247       size = -1; /* kludge for servers that understate ASCII mode file size */
2248 
2249     infof(data, "Maxdownload = %" FORMAT_OFF_T "\n", data->req.maxdownload);
2250 
2251     if(instate != FTP_LIST)
2252       infof(data, "Getting file with size: %" FORMAT_OFF_T "\n", size);
2253 
2254     /* FTP download: */
2255     Curl_setup_transfer(conn, SECONDARYSOCKET, size, FALSE,
2256                         ftp->bytecountp, -1, NULL); /* no upload here */
2257 
2258     conn->proto.ftpc.pp.pending_resp = TRUE; /* expect server response */
2259     state(conn, FTP_STOP);
2260   }
2261   else {
2262     if((instate == FTP_LIST) && (ftpcode == 450)) {
2263       /* simply no matching files in the dir listing */
2264       ftp->transfer = FTPTRANSFER_NONE; /* don't download anything */
2265       state(conn, FTP_STOP); /* this phase is over */
2266     }
2267     else {
2268       failf(data, "RETR response: %03d", ftpcode);
2269       return instate == FTP_RETR && ftpcode == 550?
2270         CURLE_REMOTE_FILE_NOT_FOUND:
2271         CURLE_FTP_COULDNT_RETR_FILE;
2272     }
2273   }
2274 
2275   return result;
2276 }
2277 
2278 /* after USER, PASS and ACCT */
ftp_state_loggedin(struct connectdata * conn)2279 static CURLcode ftp_state_loggedin(struct connectdata *conn)
2280 {
2281   CURLcode result = CURLE_OK;
2282 
2283 #ifdef HAVE_KRB4
2284   if(conn->data->set.krb) {
2285     /* We may need to issue a KAUTH here to have access to the files
2286      * do it if user supplied a password
2287      */
2288     if(conn->passwd && *conn->passwd) {
2289       /* BLOCKING */
2290       result = Curl_krb_kauth(conn);
2291       if(result)
2292         return result;
2293     }
2294   }
2295 #endif
2296   if(conn->ssl[FIRSTSOCKET].use) {
2297     /* PBSZ = PROTECTION BUFFER SIZE.
2298 
2299     The 'draft-murray-auth-ftp-ssl' (draft 12, page 7) says:
2300 
2301     Specifically, the PROT command MUST be preceded by a PBSZ
2302     command and a PBSZ command MUST be preceded by a successful
2303     security data exchange (the TLS negotiation in this case)
2304 
2305     ... (and on page 8):
2306 
2307     Thus the PBSZ command must still be issued, but must have a
2308     parameter of '0' to indicate that no buffering is taking place
2309     and the data connection should not be encapsulated.
2310     */
2311     PPSENDF(&conn->proto.ftpc.pp, "PBSZ %d", 0);
2312     state(conn, FTP_PBSZ);
2313   }
2314   else {
2315     result = ftp_state_pwd(conn);
2316   }
2317   return result;
2318 }
2319 
2320 /* for USER and PASS responses */
ftp_state_user_resp(struct connectdata * conn,int ftpcode,ftpstate instate)2321 static CURLcode ftp_state_user_resp(struct connectdata *conn,
2322                                     int ftpcode,
2323                                     ftpstate instate)
2324 {
2325   CURLcode result = CURLE_OK;
2326   struct SessionHandle *data = conn->data;
2327   struct FTP *ftp = data->state.proto.ftp;
2328   struct ftp_conn *ftpc = &conn->proto.ftpc;
2329   (void)instate; /* no use for this yet */
2330 
2331   /* some need password anyway, and others just return 2xx ignored */
2332   if((ftpcode == 331) && (ftpc->state == FTP_USER)) {
2333     /* 331 Password required for ...
2334        (the server requires to send the user's password too) */
2335     PPSENDF(&ftpc->pp, "PASS %s", ftp->passwd?ftp->passwd:"");
2336     state(conn, FTP_PASS);
2337   }
2338   else if(ftpcode/100 == 2) {
2339     /* 230 User ... logged in.
2340        (the user logged in with or without password) */
2341     result = ftp_state_loggedin(conn);
2342   }
2343   else if(ftpcode == 332) {
2344     if(data->set.str[STRING_FTP_ACCOUNT]) {
2345       PPSENDF(&ftpc->pp, "ACCT %s", data->set.str[STRING_FTP_ACCOUNT]);
2346       state(conn, FTP_ACCT);
2347     }
2348     else {
2349       failf(data, "ACCT requested but none available");
2350       result = CURLE_LOGIN_DENIED;
2351     }
2352   }
2353   else {
2354     /* All other response codes, like:
2355 
2356     530 User ... access denied
2357     (the server denies to log the specified user) */
2358 
2359     if(conn->data->set.str[STRING_FTP_ALTERNATIVE_TO_USER] &&
2360         !conn->data->state.ftp_trying_alternative) {
2361       /* Ok, USER failed.  Let's try the supplied command. */
2362       PPSENDF(&conn->proto.ftpc.pp, "%s",
2363               conn->data->set.str[STRING_FTP_ALTERNATIVE_TO_USER]);
2364       conn->data->state.ftp_trying_alternative = TRUE;
2365       state(conn, FTP_USER);
2366       result = CURLE_OK;
2367     }
2368     else {
2369       failf(data, "Access denied: %03d", ftpcode);
2370       result = CURLE_LOGIN_DENIED;
2371     }
2372   }
2373   return result;
2374 }
2375 
2376 /* for ACCT response */
ftp_state_acct_resp(struct connectdata * conn,int ftpcode)2377 static CURLcode ftp_state_acct_resp(struct connectdata *conn,
2378                                     int ftpcode)
2379 {
2380   CURLcode result = CURLE_OK;
2381   struct SessionHandle *data = conn->data;
2382   if(ftpcode != 230) {
2383     failf(data, "ACCT rejected by server: %03d", ftpcode);
2384     result = CURLE_FTP_WEIRD_PASS_REPLY; /* FIX */
2385   }
2386   else
2387     result = ftp_state_loggedin(conn);
2388 
2389   return result;
2390 }
2391 
2392 
ftp_statemach_act(struct connectdata * conn)2393 static CURLcode ftp_statemach_act(struct connectdata *conn)
2394 {
2395   CURLcode result;
2396   curl_socket_t sock = conn->sock[FIRSTSOCKET];
2397   struct SessionHandle *data=conn->data;
2398   int ftpcode;
2399   struct ftp_conn *ftpc = &conn->proto.ftpc;
2400   struct pingpong *pp = &ftpc->pp;
2401   static const char ftpauth[][4]  = { "SSL", "TLS" };
2402   size_t nread = 0;
2403 
2404   if(pp->sendleft)
2405     return Curl_pp_flushsend(pp);
2406 
2407   /* we read a piece of response */
2408   result = ftp_readresp(sock, pp, &ftpcode, &nread);
2409   if(result)
2410     return result;
2411 
2412   if(ftpcode) {
2413     /* we have now received a full FTP server response */
2414     switch(ftpc->state) {
2415     case FTP_WAIT220:
2416       if(ftpcode != 220) {
2417         failf(data, "Got a %03d ftp-server response when 220 was expected",
2418               ftpcode);
2419         return CURLE_FTP_WEIRD_SERVER_REPLY;
2420       }
2421 
2422       /* We have received a 220 response fine, now we proceed. */
2423 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
2424       if(data->set.krb) {
2425         /* If not anonymous login, try a secure login. Note that this
2426            procedure is still BLOCKING. */
2427 
2428         Curl_sec_request_prot(conn, "private");
2429         /* We set private first as default, in case the line below fails to
2430            set a valid level */
2431         Curl_sec_request_prot(conn, data->set.str[STRING_KRB_LEVEL]);
2432 
2433         if(Curl_sec_login(conn) != CURLE_OK)
2434           infof(data, "Logging in with password in cleartext!\n");
2435         else
2436           infof(data, "Authentication successful\n");
2437       }
2438 #endif
2439 
2440       if(data->set.ftp_ssl && !conn->ssl[FIRSTSOCKET].use) {
2441         /* We don't have a SSL/TLS connection yet, but FTPS is
2442            requested. Try a FTPS connection now */
2443 
2444         ftpc->count3=0;
2445         switch(data->set.ftpsslauth) {
2446         case CURLFTPAUTH_DEFAULT:
2447         case CURLFTPAUTH_SSL:
2448           ftpc->count2 = 1; /* add one to get next */
2449           ftpc->count1 = 0;
2450           break;
2451         case CURLFTPAUTH_TLS:
2452           ftpc->count2 = -1; /* subtract one to get next */
2453           ftpc->count1 = 1;
2454           break;
2455         default:
2456           failf(data, "unsupported parameter to CURLOPT_FTPSSLAUTH: %d",
2457                 (int)data->set.ftpsslauth);
2458           return CURLE_UNKNOWN_OPTION; /* we don't know what to do */
2459         }
2460         PPSENDF(&ftpc->pp, "AUTH %s", ftpauth[ftpc->count1]);
2461         state(conn, FTP_AUTH);
2462       }
2463       else {
2464         result = ftp_state_user(conn);
2465         if(result)
2466           return result;
2467       }
2468 
2469       break;
2470 
2471     case FTP_AUTH:
2472       /* we have gotten the response to a previous AUTH command */
2473 
2474       /* RFC2228 (page 5) says:
2475        *
2476        * If the server is willing to accept the named security mechanism,
2477        * and does not require any security data, it must respond with
2478        * reply code 234/334.
2479        */
2480 
2481       if((ftpcode == 234) || (ftpcode == 334)) {
2482         /* Curl_ssl_connect is BLOCKING */
2483         result = Curl_ssl_connect(conn, FIRSTSOCKET);
2484         if(CURLE_OK == result) {
2485           conn->ssl[SECONDARYSOCKET].use = FALSE; /* clear-text data */
2486           result = ftp_state_user(conn);
2487         }
2488       }
2489       else if(ftpc->count3 < 1) {
2490         ftpc->count3++;
2491         ftpc->count1 += ftpc->count2; /* get next attempt */
2492         result = Curl_pp_sendf(&ftpc->pp, "AUTH %s", ftpauth[ftpc->count1]);
2493         /* remain in this same state */
2494       }
2495       else {
2496         if(data->set.ftp_ssl > CURLUSESSL_TRY)
2497           /* we failed and CURLUSESSL_CONTROL or CURLUSESSL_ALL is set */
2498           result = CURLE_USE_SSL_FAILED;
2499         else
2500           /* ignore the failure and continue */
2501           result = ftp_state_user(conn);
2502       }
2503 
2504       if(result)
2505         return result;
2506       break;
2507 
2508     case FTP_USER:
2509     case FTP_PASS:
2510       result = ftp_state_user_resp(conn, ftpcode, ftpc->state);
2511       break;
2512 
2513     case FTP_ACCT:
2514       result = ftp_state_acct_resp(conn, ftpcode);
2515       break;
2516 
2517     case FTP_PBSZ:
2518       PPSENDF(&ftpc->pp, "PROT %c",
2519               data->set.ftp_ssl == CURLUSESSL_CONTROL ? 'C' : 'P');
2520       state(conn, FTP_PROT);
2521 
2522       break;
2523 
2524     case FTP_PROT:
2525       if(ftpcode/100 == 2)
2526         /* We have enabled SSL for the data connection! */
2527         conn->ssl[SECONDARYSOCKET].use =
2528           (bool)(data->set.ftp_ssl != CURLUSESSL_CONTROL);
2529       /* FTP servers typically responds with 500 if they decide to reject
2530          our 'P' request */
2531       else if(data->set.ftp_ssl > CURLUSESSL_CONTROL)
2532         /* we failed and bails out */
2533         return CURLE_USE_SSL_FAILED;
2534 
2535       if(data->set.ftp_ccc) {
2536         /* CCC - Clear Command Channel
2537          */
2538         PPSENDF(&ftpc->pp, "CCC", NULL);
2539         state(conn, FTP_CCC);
2540       }
2541       else {
2542         result = ftp_state_pwd(conn);
2543         if(result)
2544           return result;
2545       }
2546       break;
2547 
2548     case FTP_CCC:
2549       if(ftpcode < 500) {
2550         /* First shut down the SSL layer (note: this call will block) */
2551         result = Curl_ssl_shutdown(conn, FIRSTSOCKET);
2552 
2553         if(result) {
2554           failf(conn->data, "Failed to clear the command channel (CCC)");
2555           return result;
2556         }
2557       }
2558 
2559       /* Then continue as normal */
2560       result = ftp_state_pwd(conn);
2561       if(result)
2562         return result;
2563       break;
2564 
2565     case FTP_PWD:
2566       if(ftpcode == 257) {
2567         char *ptr=&data->state.buffer[4];  /* start on the first letter */
2568         char *dir;
2569         char *store;
2570 
2571         dir = malloc(nread + 1);
2572         if(!dir)
2573           return CURLE_OUT_OF_MEMORY;
2574 
2575         /* Reply format is like
2576            257<space>"<directory-name>"<space><commentary> and the RFC959
2577            says
2578 
2579            The directory name can contain any character; embedded
2580            double-quotes should be escaped by double-quotes (the
2581            "quote-doubling" convention).
2582         */
2583         if('\"' == *ptr) {
2584           /* it started good */
2585           ptr++;
2586           for (store = dir; *ptr;) {
2587             if('\"' == *ptr) {
2588               if('\"' == ptr[1]) {
2589                 /* "quote-doubling" */
2590                 *store = ptr[1];
2591                 ptr++;
2592               }
2593               else {
2594                 /* end of path */
2595                 *store = '\0'; /* zero terminate */
2596                 break; /* get out of this loop */
2597               }
2598             }
2599             else
2600               *store = *ptr;
2601             store++;
2602             ptr++;
2603           }
2604           if(ftpc->entrypath)
2605             free(ftpc->entrypath);
2606           ftpc->entrypath =dir; /* remember this */
2607           infof(data, "Entry path is '%s'\n", ftpc->entrypath);
2608           /* also save it where getinfo can access it: */
2609           data->state.most_recent_ftp_entrypath = ftpc->entrypath;
2610 
2611           /* If the path name does not look like an absolute path (i.e.: it
2612              does not start with a '/'), we probably need some server-dependent
2613              adjustments. For example, this is the case when connecting to
2614              an OS400 FTP server: this server supports two name syntaxes,
2615              the default one being incompatible with standard pathes. In
2616              addition, this server switches automatically to the regular path
2617              syntax when one is encountered in a command: this results in
2618              having an entrypath in the wrong syntax when later used in CWD.
2619                The method used here is to check the server OS: we do it only
2620              if the path name looks strange to minimize overhead on other
2621              systems. */
2622 
2623           if(!ftpc->server_os && ftpc->entrypath[0] != '/') {
2624             PPSENDF(&ftpc->pp, "SYST", NULL);
2625             state(conn, FTP_SYST);
2626             break;
2627           }
2628         }
2629         else {
2630           /* couldn't get the path */
2631           free(dir);
2632           infof(data, "Failed to figure out path\n");
2633         }
2634       }
2635       state(conn, FTP_STOP); /* we are done with the CONNECT phase! */
2636       DEBUGF(infof(data, "protocol connect phase DONE\n"));
2637       break;
2638 
2639     case FTP_SYST:
2640       if(ftpcode == 215) {
2641         char *ptr=&data->state.buffer[4];  /* start on the first letter */
2642         char *os;
2643         char *store;
2644 
2645         os = malloc(nread + 1);
2646         if(!os)
2647           return CURLE_OUT_OF_MEMORY;
2648 
2649         /* Reply format is like
2650            215<space><OS-name><space><commentary>
2651         */
2652         while (*ptr == ' ')
2653           ptr++;
2654         for (store = os; *ptr && *ptr != ' ';)
2655           *store++ = *ptr++;
2656         *store = '\0'; /* zero terminate */
2657         ftpc->server_os = os;
2658 
2659         /* Check for special servers here. */
2660 
2661         if(strequal(ftpc->server_os, "OS/400")) {
2662           /* Force OS400 name format 1. */
2663           PPSENDF(&ftpc->pp, "SITE NAMEFMT 1", NULL);
2664           state(conn, FTP_NAMEFMT);
2665           break;
2666         }
2667       else {
2668         /* Nothing special for the target server. */
2669        }
2670       }
2671       else {
2672         /* Cannot identify server OS. Continue anyway and cross fingers. */
2673       }
2674 
2675       state(conn, FTP_STOP); /* we are done with the CONNECT phase! */
2676       DEBUGF(infof(data, "protocol connect phase DONE\n"));
2677       break;
2678 
2679     case FTP_NAMEFMT:
2680       if(ftpcode == 250) {
2681         /* Name format change successful: reload initial path. */
2682         ftp_state_pwd(conn);
2683         break;
2684       }
2685 
2686       state(conn, FTP_STOP); /* we are done with the CONNECT phase! */
2687       DEBUGF(infof(data, "protocol connect phase DONE\n"));
2688       break;
2689 
2690     case FTP_QUOTE:
2691     case FTP_POSTQUOTE:
2692     case FTP_RETR_PREQUOTE:
2693     case FTP_STOR_PREQUOTE:
2694       if((ftpcode >= 400) && !ftpc->count2) {
2695         /* failure response code, and not allowed to fail */
2696         failf(conn->data, "QUOT command failed with %03d", ftpcode);
2697         return CURLE_QUOTE_ERROR;
2698       }
2699       result = ftp_state_quote(conn, FALSE, ftpc->state);
2700       if(result)
2701         return result;
2702 
2703       break;
2704 
2705     case FTP_CWD:
2706       if(ftpcode/100 != 2) {
2707         /* failure to CWD there */
2708         if(conn->data->set.ftp_create_missing_dirs &&
2709            ftpc->count1 && !ftpc->count2) {
2710           /* try making it */
2711           ftpc->count2++; /* counter to prevent CWD-MKD loops */
2712           PPSENDF(&ftpc->pp, "MKD %s", ftpc->dirs[ftpc->count1 - 1]);
2713           state(conn, FTP_MKD);
2714         }
2715         else {
2716           /* return failure */
2717           failf(data, "Server denied you to change to the given directory");
2718           ftpc->cwdfail = TRUE; /* don't remember this path as we failed
2719                                    to enter it */
2720           return CURLE_REMOTE_ACCESS_DENIED;
2721         }
2722       }
2723       else {
2724         /* success */
2725         ftpc->count2=0;
2726         if(++ftpc->count1 <= ftpc->dirdepth) {
2727           /* send next CWD */
2728           PPSENDF(&ftpc->pp, "CWD %s", ftpc->dirs[ftpc->count1 - 1]);
2729         }
2730         else {
2731           result = ftp_state_post_cwd(conn);
2732           if(result)
2733             return result;
2734         }
2735       }
2736       break;
2737 
2738     case FTP_MKD:
2739       if((ftpcode/100 != 2) && !ftpc->count3--) {
2740         /* failure to MKD the dir */
2741         failf(data, "Failed to MKD dir: %03d", ftpcode);
2742         return CURLE_REMOTE_ACCESS_DENIED;
2743       }
2744       state(conn, FTP_CWD);
2745       /* send CWD */
2746       PPSENDF(&ftpc->pp, "CWD %s", ftpc->dirs[ftpc->count1 - 1]);
2747       break;
2748 
2749     case FTP_MDTM:
2750       result = ftp_state_mdtm_resp(conn, ftpcode);
2751       break;
2752 
2753     case FTP_TYPE:
2754     case FTP_LIST_TYPE:
2755     case FTP_RETR_TYPE:
2756     case FTP_STOR_TYPE:
2757       result = ftp_state_type_resp(conn, ftpcode, ftpc->state);
2758       break;
2759 
2760     case FTP_SIZE:
2761     case FTP_RETR_SIZE:
2762     case FTP_STOR_SIZE:
2763       result = ftp_state_size_resp(conn, ftpcode, ftpc->state);
2764       break;
2765 
2766     case FTP_REST:
2767     case FTP_RETR_REST:
2768       result = ftp_state_rest_resp(conn, ftpcode, ftpc->state);
2769       break;
2770 
2771     case FTP_PRET:
2772       if(ftpcode != 200) {
2773        /* there only is this one standard OK return code. */
2774         failf(data, "PRET command not accepted: %03d", ftpcode);
2775         return CURLE_FTP_PRET_FAILED;
2776       }
2777       result = ftp_state_use_pasv(conn);
2778       break;
2779 
2780     case FTP_PASV:
2781       result = ftp_state_pasv_resp(conn, ftpcode);
2782       break;
2783 
2784     case FTP_PORT:
2785       result = ftp_state_port_resp(conn, ftpcode);
2786       break;
2787 
2788     case FTP_LIST:
2789     case FTP_RETR:
2790       result = ftp_state_get_resp(conn, ftpcode, ftpc->state);
2791       break;
2792 
2793     case FTP_STOR:
2794       result = ftp_state_stor_resp(conn, ftpcode);
2795       break;
2796 
2797     case FTP_QUIT:
2798       /* fallthrough, just stop! */
2799     default:
2800       /* internal error */
2801       state(conn, FTP_STOP);
2802       break;
2803     }
2804   } /* if(ftpcode) */
2805 
2806   return result;
2807 }
2808 
2809 
2810 /* called repeatedly until done from multi.c */
ftp_multi_statemach(struct connectdata * conn,bool * done)2811 static CURLcode ftp_multi_statemach(struct connectdata *conn,
2812                                     bool *done)
2813 {
2814   struct ftp_conn *ftpc = &conn->proto.ftpc;
2815   CURLcode result = Curl_pp_multi_statemach(&ftpc->pp);
2816 
2817   /* Check for the state outside of the Curl_socket_ready() return code checks
2818      since at times we are in fact already in this state when this function
2819      gets called. */
2820   *done = (bool)(ftpc->state == FTP_STOP);
2821 
2822   return result;
2823 }
2824 
ftp_easy_statemach(struct connectdata * conn)2825 static CURLcode ftp_easy_statemach(struct connectdata *conn)
2826 {
2827   struct ftp_conn *ftpc = &conn->proto.ftpc;
2828   struct pingpong *pp = &ftpc->pp;
2829   CURLcode result = CURLE_OK;
2830 
2831   while(ftpc->state != FTP_STOP) {
2832     result = Curl_pp_easy_statemach(pp);
2833     if(result)
2834       break;
2835   }
2836 
2837   return result;
2838 }
2839 
2840 /*
2841  * Allocate and initialize the struct FTP for the current SessionHandle.  If
2842  * need be.
2843  */
2844 
2845 #if defined(__INTEL_COMPILER) && (__INTEL_COMPILER == 910) && \
2846     defined(__OPTIMIZE__) && defined(__unix__) && defined(__i386__)
2847   /* workaround icc 9.1 optimizer issue */
2848 #pragma optimize("", off)
2849 #endif
2850 
ftp_init(struct connectdata * conn)2851 static CURLcode ftp_init(struct connectdata *conn)
2852 {
2853   struct FTP *ftp;
2854 
2855   if(NULL == conn->data->state.proto.ftp) {
2856     conn->data->state.proto.ftp = malloc(sizeof(struct FTP));
2857     if(NULL == conn->data->state.proto.ftp)
2858       return CURLE_OUT_OF_MEMORY;
2859   }
2860 
2861   ftp = conn->data->state.proto.ftp;
2862 
2863   /* get some initial data into the ftp struct */
2864   ftp->bytecountp = &conn->data->req.bytecount;
2865   ftp->transfer = FTPTRANSFER_BODY;
2866   ftp->downloadsize = 0;
2867 
2868   /* No need to duplicate user+password, the connectdata struct won't change
2869      during a session, but we re-init them here since on subsequent inits
2870      since the conn struct may have changed or been replaced.
2871   */
2872   ftp->user = conn->user;
2873   ftp->passwd = conn->passwd;
2874   if(TRUE == isBadFtpString(ftp->user))
2875     return CURLE_URL_MALFORMAT;
2876   if(TRUE == isBadFtpString(ftp->passwd))
2877     return CURLE_URL_MALFORMAT;
2878 
2879   conn->proto.ftpc.known_filesize = -1; /* unknown size for now */
2880 
2881   return CURLE_OK;
2882 }
2883 
2884 #if defined(__INTEL_COMPILER) && (__INTEL_COMPILER == 910) && \
2885     defined(__OPTIMIZE__) && defined(__unix__) && defined(__i386__)
2886   /* workaround icc 9.1 optimizer issue */
2887 #pragma optimize("", on)
2888 #endif
2889 
2890 /*
2891  * ftp_connect() should do everything that is to be considered a part of
2892  * the connection phase.
2893  *
2894  * The variable 'done' points to will be TRUE if the protocol-layer connect
2895  * phase is done when this function returns, or FALSE is not. When called as
2896  * a part of the easy interface, it will always be TRUE.
2897  */
ftp_connect(struct connectdata * conn,bool * done)2898 static CURLcode ftp_connect(struct connectdata *conn,
2899                                  bool *done) /* see description above */
2900 {
2901   CURLcode result;
2902   struct ftp_conn *ftpc = &conn->proto.ftpc;
2903   struct SessionHandle *data=conn->data;
2904   struct pingpong *pp = &ftpc->pp;
2905 
2906   *done = FALSE; /* default to not done yet */
2907 
2908   /* If there already is a protocol-specific struct allocated for this
2909      sessionhandle, deal with it */
2910   Curl_reset_reqproto(conn);
2911 
2912   result = ftp_init(conn);
2913   if(CURLE_OK != result)
2914     return result;
2915 
2916   /* We always support persistent connections on ftp */
2917   conn->bits.close = FALSE;
2918 
2919   pp->response_time = RESP_TIMEOUT; /* set default response time-out */
2920   pp->statemach_act = ftp_statemach_act;
2921   pp->endofresp = ftp_endofresp;
2922   pp->conn = conn;
2923 
2924   if(conn->bits.tunnel_proxy && conn->bits.httpproxy) {
2925     /* for FTP over HTTP proxy */
2926     struct HTTP http_proxy;
2927     struct FTP *ftp_save;
2928 
2929     /* BLOCKING */
2930     /* We want "seamless" FTP operations through HTTP proxy tunnel */
2931 
2932     /* Curl_proxyCONNECT is based on a pointer to a struct HTTP at the member
2933      * conn->proto.http; we want FTP through HTTP and we have to change the
2934      * member temporarily for connecting to the HTTP proxy. After
2935      * Curl_proxyCONNECT we have to set back the member to the original struct
2936      * FTP pointer
2937      */
2938     ftp_save = data->state.proto.ftp;
2939     memset(&http_proxy, 0, sizeof(http_proxy));
2940     data->state.proto.http = &http_proxy;
2941 
2942     result = Curl_proxyCONNECT(conn, FIRSTSOCKET,
2943                                conn->host.name, conn->remote_port);
2944 
2945     data->state.proto.ftp = ftp_save;
2946 
2947     if(CURLE_OK != result)
2948       return result;
2949   }
2950 
2951   if(conn->handler->protocol & CURLPROTO_FTPS) {
2952     /* BLOCKING */
2953     /* FTPS is simply ftp with SSL for the control channel */
2954     /* now, perform the SSL initialization for this socket */
2955     result = Curl_ssl_connect(conn, FIRSTSOCKET);
2956     if(result)
2957       return result;
2958   }
2959 
2960   Curl_pp_init(pp); /* init the generic pingpong data */
2961 
2962   /* When we connect, we start in the state where we await the 220
2963      response */
2964   state(conn, FTP_WAIT220);
2965 
2966   if(data->state.used_interface == Curl_if_multi)
2967     result = ftp_multi_statemach(conn, done);
2968   else {
2969     result = ftp_easy_statemach(conn);
2970     if(!result)
2971       *done = TRUE;
2972   }
2973 
2974   return result;
2975 }
2976 
2977 /***********************************************************************
2978  *
2979  * ftp_done()
2980  *
2981  * The DONE function. This does what needs to be done after a single DO has
2982  * performed.
2983  *
2984  * Input argument is already checked for validity.
2985  */
ftp_done(struct connectdata * conn,CURLcode status,bool premature)2986 static CURLcode ftp_done(struct connectdata *conn, CURLcode status,
2987                               bool premature)
2988 {
2989   struct SessionHandle *data = conn->data;
2990   struct FTP *ftp = data->state.proto.ftp;
2991   struct ftp_conn *ftpc = &conn->proto.ftpc;
2992   struct pingpong *pp = &ftpc->pp;
2993   ssize_t nread;
2994   int ftpcode;
2995   CURLcode result=CURLE_OK;
2996   bool was_ctl_valid = ftpc->ctl_valid;
2997   char *path;
2998   const char *path_to_use = data->state.path;
2999 
3000   if(!ftp)
3001     /* When the easy handle is removed from the multi while libcurl is still
3002      * trying to resolve the host name, it seems that the ftp struct is not
3003      * yet initialized, but the removal action calls Curl_done() which calls
3004      * this function. So we simply return success if no ftp pointer is set.
3005      */
3006     return CURLE_OK;
3007 
3008   switch(status) {
3009   case CURLE_BAD_DOWNLOAD_RESUME:
3010   case CURLE_FTP_WEIRD_PASV_REPLY:
3011   case CURLE_FTP_PORT_FAILED:
3012   case CURLE_FTP_COULDNT_SET_TYPE:
3013   case CURLE_FTP_COULDNT_RETR_FILE:
3014   case CURLE_UPLOAD_FAILED:
3015   case CURLE_REMOTE_ACCESS_DENIED:
3016   case CURLE_FILESIZE_EXCEEDED:
3017   case CURLE_REMOTE_FILE_NOT_FOUND:
3018   case CURLE_WRITE_ERROR:
3019     /* the connection stays alive fine even though this happened */
3020     /* fall-through */
3021   case CURLE_OK: /* doesn't affect the control connection's status */
3022     if(!premature) {
3023       ftpc->ctl_valid = was_ctl_valid;
3024       break;
3025     }
3026     /* until we cope better with prematurely ended requests, let them
3027      * fallback as if in complete failure */
3028   default:       /* by default, an error means the control connection is
3029                     wedged and should not be used anymore */
3030     ftpc->ctl_valid = FALSE;
3031     ftpc->cwdfail = TRUE; /* set this TRUE to prevent us to remember the
3032                              current path, as this connection is going */
3033     conn->bits.close = TRUE; /* marked for closure */
3034     result = status;      /* use the already set error code */
3035     break;
3036   }
3037 
3038   /* now store a copy of the directory we are in */
3039   if(ftpc->prevpath)
3040     free(ftpc->prevpath);
3041 
3042   if(data->set.wildcardmatch) {
3043     if(data->set.chunk_end && ftpc->file) {
3044       data->set.chunk_end(data->wildcard.customptr);
3045     }
3046     ftpc->known_filesize = -1;
3047   }
3048 
3049   /* get the "raw" path */
3050   path = curl_easy_unescape(data, path_to_use, 0, NULL);
3051   if(!path) {
3052     /* out of memory, but we can limp along anyway (and should try to
3053      * since we're in the out of memory cleanup path) */
3054     ftpc->prevpath = NULL; /* no path */
3055   }
3056   else {
3057     size_t flen = ftpc->file?strlen(ftpc->file):0; /* file is "raw" already */
3058     size_t dlen = strlen(path)-flen;
3059     if(!ftpc->cwdfail) {
3060       if(dlen && (data->set.ftp_filemethod != FTPFILE_NOCWD)) {
3061         ftpc->prevpath = path;
3062         if(flen)
3063           /* if 'path' is not the whole string */
3064           ftpc->prevpath[dlen]=0; /* terminate */
3065       }
3066       else {
3067         /* we never changed dir */
3068         ftpc->prevpath=strdup("");
3069         free(path);
3070       }
3071       if(ftpc->prevpath)
3072         infof(data, "Remembering we are in dir \"%s\"\n", ftpc->prevpath);
3073     }
3074     else {
3075       ftpc->prevpath = NULL; /* no path */
3076       free(path);
3077     }
3078   }
3079   /* free the dir tree and file parts */
3080   freedirs(ftpc);
3081 
3082   /* shut down the socket to inform the server we're done */
3083 
3084 #ifdef _WIN32_WCE
3085   shutdown(conn->sock[SECONDARYSOCKET],2);  /* SD_BOTH */
3086 #endif
3087 
3088   if(conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD) {
3089     if(!result && ftpc->dont_check && data->req.maxdownload > 0)
3090       /* partial download completed */
3091       result = Curl_pp_sendf(pp, "ABOR");
3092 
3093     if(conn->ssl[SECONDARYSOCKET].use) {
3094       /* The secondary socket is using SSL so we must close down that part
3095          first before we close the socket for real */
3096       Curl_ssl_close(conn, SECONDARYSOCKET);
3097 
3098       /* Note that we keep "use" set to TRUE since that (next) connection is
3099          still requested to use SSL */
3100     }
3101     if(CURL_SOCKET_BAD != conn->sock[SECONDARYSOCKET]) {
3102       sclose(conn->sock[SECONDARYSOCKET]);
3103       conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
3104     }
3105   }
3106 
3107   if(!result && (ftp->transfer == FTPTRANSFER_BODY) && ftpc->ctl_valid &&
3108      pp->pending_resp && !premature) {
3109     /*
3110      * Let's see what the server says about the transfer we just performed,
3111      * but lower the timeout as sometimes this connection has died while the
3112      * data has been transferred. This happens when doing through NATs etc that
3113      * abandon old silent connections.
3114      */
3115     long old_time = pp->response_time;
3116 
3117     pp->response_time = 60*1000; /* give it only a minute for now */
3118     pp->response = Curl_tvnow(); /* timeout relative now */
3119 
3120     result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
3121 
3122     pp->response_time = old_time; /* set this back to previous value */
3123 
3124     if(!nread && (CURLE_OPERATION_TIMEDOUT == result)) {
3125       failf(data, "control connection looks dead");
3126       ftpc->ctl_valid = FALSE; /* mark control connection as bad */
3127       conn->bits.close = TRUE; /* mark for closure */
3128     }
3129 
3130     if(result)
3131       return result;
3132 
3133     if(ftpc->dont_check && data->req.maxdownload > 0) {
3134       /* we have just sent ABOR and there is no reliable way to check if it was
3135        * successful or not; we have to close the connection now */
3136       infof(data, "partial download completed, closing connection\n");
3137       conn->bits.close = TRUE; /* mark for closure */
3138       return result;
3139     }
3140 
3141     if(!ftpc->dont_check) {
3142       /* 226 Transfer complete, 250 Requested file action okay, completed. */
3143       if((ftpcode != 226) && (ftpcode != 250)) {
3144         failf(data, "server did not report OK, got %d", ftpcode);
3145         result = CURLE_PARTIAL_FILE;
3146       }
3147     }
3148   }
3149 
3150   if(result || premature)
3151     /* the response code from the transfer showed an error already so no
3152        use checking further */
3153     ;
3154   else if(data->set.upload) {
3155     if((-1 != data->set.infilesize) &&
3156        (data->set.infilesize != *ftp->bytecountp) &&
3157        !data->set.crlf &&
3158        (ftp->transfer == FTPTRANSFER_BODY)) {
3159       failf(data, "Uploaded unaligned file size (%" FORMAT_OFF_T
3160             " out of %" FORMAT_OFF_T " bytes)",
3161             *ftp->bytecountp, data->set.infilesize);
3162       result = CURLE_PARTIAL_FILE;
3163     }
3164   }
3165   else {
3166     if((-1 != data->req.size) &&
3167        (data->req.size != *ftp->bytecountp) &&
3168 #ifdef CURL_DO_LINEEND_CONV
3169        /* Most FTP servers don't adjust their file SIZE response for CRLFs, so
3170         * we'll check to see if the discrepancy can be explained by the number
3171         * of CRLFs we've changed to LFs.
3172         */
3173        ((data->req.size + data->state.crlf_conversions) !=
3174         *ftp->bytecountp) &&
3175 #endif /* CURL_DO_LINEEND_CONV */
3176        (data->req.maxdownload != *ftp->bytecountp)) {
3177       failf(data, "Received only partial file: %" FORMAT_OFF_T " bytes",
3178             *ftp->bytecountp);
3179       result = CURLE_PARTIAL_FILE;
3180     }
3181     else if(!ftpc->dont_check &&
3182             !*ftp->bytecountp &&
3183             (data->req.size>0)) {
3184       failf(data, "No data was received!");
3185       result = CURLE_FTP_COULDNT_RETR_FILE;
3186     }
3187   }
3188 
3189   /* clear these for next connection */
3190   ftp->transfer = FTPTRANSFER_BODY;
3191   ftpc->dont_check = FALSE;
3192 
3193   /* Send any post-transfer QUOTE strings? */
3194   if(!status && !result && !premature && data->set.postquote)
3195     result = ftp_sendquote(conn, data->set.postquote);
3196 
3197   return result;
3198 }
3199 
3200 /***********************************************************************
3201  *
3202  * ftp_sendquote()
3203  *
3204  * Where a 'quote' means a list of custom commands to send to the server.
3205  * The quote list is passed as an argument.
3206  *
3207  * BLOCKING
3208  */
3209 
3210 static
ftp_sendquote(struct connectdata * conn,struct curl_slist * quote)3211 CURLcode ftp_sendquote(struct connectdata *conn, struct curl_slist *quote)
3212 {
3213   struct curl_slist *item;
3214   ssize_t nread;
3215   int ftpcode;
3216   CURLcode result;
3217   struct ftp_conn *ftpc = &conn->proto.ftpc;
3218   struct pingpong *pp = &ftpc->pp;
3219 
3220   item = quote;
3221   while(item) {
3222     if(item->data) {
3223       char *cmd = item->data;
3224       bool acceptfail = FALSE;
3225 
3226       /* if a command starts with an asterisk, which a legal FTP command never
3227          can, the command will be allowed to fail without it causing any
3228          aborts or cancels etc. It will cause libcurl to act as if the command
3229          is successful, whatever the server reponds. */
3230 
3231       if(cmd[0] == '*') {
3232         cmd++;
3233         acceptfail = TRUE;
3234       }
3235 
3236       FTPSENDF(conn, "%s", cmd);
3237 
3238       pp->response = Curl_tvnow(); /* timeout relative now */
3239 
3240       result = Curl_GetFTPResponse(&nread, conn, &ftpcode);
3241       if(result)
3242         return result;
3243 
3244       if(!acceptfail && (ftpcode >= 400)) {
3245         failf(conn->data, "QUOT string not accepted: %s", cmd);
3246         return CURLE_QUOTE_ERROR;
3247       }
3248     }
3249 
3250     item = item->next;
3251   }
3252 
3253   return CURLE_OK;
3254 }
3255 
3256 /***********************************************************************
3257  *
3258  * ftp_need_type()
3259  *
3260  * Returns TRUE if we in the current situation should send TYPE
3261  */
ftp_need_type(struct connectdata * conn,bool ascii_wanted)3262 static int ftp_need_type(struct connectdata *conn,
3263                          bool ascii_wanted)
3264 {
3265   return conn->proto.ftpc.transfertype != (ascii_wanted?'A':'I');
3266 }
3267 
3268 /***********************************************************************
3269  *
3270  * ftp_nb_type()
3271  *
3272  * Set TYPE. We only deal with ASCII or BINARY so this function
3273  * sets one of them.
3274  * If the transfer type is not sent, simulate on OK response in newstate
3275  */
ftp_nb_type(struct connectdata * conn,bool ascii,ftpstate newstate)3276 static CURLcode ftp_nb_type(struct connectdata *conn,
3277                             bool ascii, ftpstate newstate)
3278 {
3279   struct ftp_conn *ftpc = &conn->proto.ftpc;
3280   CURLcode result;
3281   char want = (char)(ascii?'A':'I');
3282 
3283   if(ftpc->transfertype == want) {
3284     state(conn, newstate);
3285     return ftp_state_type_resp(conn, 200, newstate);
3286   }
3287 
3288   PPSENDF(&ftpc->pp, "TYPE %c", want);
3289   state(conn, newstate);
3290 
3291   /* keep track of our current transfer type */
3292   ftpc->transfertype = want;
3293   return CURLE_OK;
3294 }
3295 
3296 /***************************************************************************
3297  *
3298  * ftp_pasv_verbose()
3299  *
3300  * This function only outputs some informationals about this second connection
3301  * when we've issued a PASV command before and thus we have connected to a
3302  * possibly new IP address.
3303  *
3304  */
3305 #ifndef CURL_DISABLE_VERBOSE_STRINGS
3306 static void
ftp_pasv_verbose(struct connectdata * conn,Curl_addrinfo * ai,char * newhost,int port)3307 ftp_pasv_verbose(struct connectdata *conn,
3308                  Curl_addrinfo *ai,
3309                  char *newhost, /* ascii version */
3310                  int port)
3311 {
3312   char buf[256];
3313   Curl_printable_address(ai, buf, sizeof(buf));
3314   infof(conn->data, "Connecting to %s (%s) port %d\n", newhost, buf, port);
3315 }
3316 #endif
3317 
3318 /*
3319   Check if this is a range download, and if so, set the internal variables
3320   properly.
3321  */
3322 
ftp_range(struct connectdata * conn)3323 static CURLcode ftp_range(struct connectdata *conn)
3324 {
3325   curl_off_t from, to;
3326   char *ptr;
3327   char *ptr2;
3328   struct SessionHandle *data = conn->data;
3329   struct ftp_conn *ftpc = &conn->proto.ftpc;
3330 
3331   if(data->state.use_range && data->state.range) {
3332     from=curlx_strtoofft(data->state.range, &ptr, 0);
3333     while(*ptr && (ISSPACE(*ptr) || (*ptr=='-')))
3334       ptr++;
3335     to=curlx_strtoofft(ptr, &ptr2, 0);
3336     if(ptr == ptr2) {
3337       /* we didn't get any digit */
3338       to=-1;
3339     }
3340     if((-1 == to) && (from>=0)) {
3341       /* X - */
3342       data->state.resume_from = from;
3343       DEBUGF(infof(conn->data, "FTP RANGE %" FORMAT_OFF_T " to end of file\n",
3344                    from));
3345     }
3346     else if(from < 0) {
3347       /* -Y */
3348       data->req.maxdownload = -from;
3349       data->state.resume_from = from;
3350       DEBUGF(infof(conn->data, "FTP RANGE the last %" FORMAT_OFF_T " bytes\n",
3351                    -from));
3352     }
3353     else {
3354       /* X-Y */
3355       data->req.maxdownload = (to-from)+1; /* include last byte */
3356       data->state.resume_from = from;
3357       DEBUGF(infof(conn->data, "FTP RANGE from %" FORMAT_OFF_T
3358                    " getting %" FORMAT_OFF_T " bytes\n",
3359                    from, data->req.maxdownload));
3360     }
3361     DEBUGF(infof(conn->data, "range-download from %" FORMAT_OFF_T
3362                  " to %" FORMAT_OFF_T ", totally %" FORMAT_OFF_T " bytes\n",
3363                  from, to, data->req.maxdownload));
3364     ftpc->dont_check = TRUE; /* dont check for successful transfer */
3365   }
3366   else
3367     data->req.maxdownload = -1;
3368   return CURLE_OK;
3369 }
3370 
3371 
3372 /*
3373  * ftp_nextconnect()
3374  *
3375  * This function shall be called when the second FTP (data) connection is
3376  * connected.
3377  */
3378 
ftp_nextconnect(struct connectdata * conn)3379 static CURLcode ftp_nextconnect(struct connectdata *conn)
3380 {
3381   struct SessionHandle *data=conn->data;
3382   struct ftp_conn *ftpc = &conn->proto.ftpc;
3383   CURLcode result = CURLE_OK;
3384 
3385   /* the ftp struct is inited in ftp_connect() */
3386   struct FTP *ftp = data->state.proto.ftp;
3387 
3388   DEBUGF(infof(data, "DO-MORE phase starts\n"));
3389 
3390   if(ftp->transfer <= FTPTRANSFER_INFO) {
3391     /* a transfer is about to take place, or if not a file name was given
3392        so we'll do a SIZE on it later and then we need the right TYPE first */
3393 
3394     if(data->set.upload) {
3395       result = ftp_nb_type(conn, data->set.prefer_ascii, FTP_STOR_TYPE);
3396       if(result)
3397         return result;
3398     }
3399     else {
3400       /* download */
3401       ftp->downloadsize = -1; /* unknown as of yet */
3402 
3403       result = ftp_range(conn);
3404       if(result)
3405         ;
3406       else if(data->set.ftp_list_only || !ftpc->file) {
3407         /* The specified path ends with a slash, and therefore we think this
3408            is a directory that is requested, use LIST. But before that we
3409            need to set ASCII transfer mode. */
3410 
3411         /* But only if a body transfer was requested. */
3412         if(ftp->transfer == FTPTRANSFER_BODY) {
3413           result = ftp_nb_type(conn, TRUE, FTP_LIST_TYPE);
3414           if(result)
3415             return result;
3416         }
3417         /* otherwise just fall through */
3418       }
3419       else {
3420         result = ftp_nb_type(conn, data->set.prefer_ascii, FTP_RETR_TYPE);
3421         if(result)
3422           return result;
3423       }
3424     }
3425     result = ftp_easy_statemach(conn);
3426   }
3427 
3428   if((result == CURLE_OK) && (ftp->transfer != FTPTRANSFER_BODY))
3429     /* no data to transfer. FIX: it feels like a kludge to have this here
3430        too! */
3431     Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
3432 
3433   /* end of transfer */
3434   DEBUGF(infof(data, "DO-MORE phase ends with %d\n", (int)result));
3435 
3436   return result;
3437 }
3438 
3439 
3440 
3441 /***********************************************************************
3442  *
3443  * ftp_perform()
3444  *
3445  * This is the actual DO function for FTP. Get a file/directory according to
3446  * the options previously setup.
3447  */
3448 
3449 static
ftp_perform(struct connectdata * conn,bool * connected,bool * dophase_done)3450 CURLcode ftp_perform(struct connectdata *conn,
3451                      bool *connected,  /* connect status after PASV / PORT */
3452                      bool *dophase_done)
3453 {
3454   /* this is FTP and no proxy */
3455   CURLcode result=CURLE_OK;
3456 
3457   DEBUGF(infof(conn->data, "DO phase starts\n"));
3458 
3459   if(conn->data->set.opt_no_body) {
3460     /* requested no body means no transfer... */
3461     struct FTP *ftp = conn->data->state.proto.ftp;
3462     ftp->transfer = FTPTRANSFER_INFO;
3463   }
3464 
3465 
3466   *dophase_done = FALSE; /* not done yet */
3467 
3468   /* start the first command in the DO phase */
3469   result = ftp_state_quote(conn, TRUE, FTP_QUOTE);
3470   if(result)
3471     return result;
3472 
3473   /* run the state-machine */
3474   if(conn->data->state.used_interface == Curl_if_multi)
3475     result = ftp_multi_statemach(conn, dophase_done);
3476   else {
3477     result = ftp_easy_statemach(conn);
3478     *dophase_done = TRUE; /* with the easy interface we are done here */
3479   }
3480   *connected = conn->bits.tcpconnect;
3481 
3482   if(*dophase_done)
3483     DEBUGF(infof(conn->data, "DO phase is complete\n"));
3484 
3485   return result;
3486 }
3487 
wc_data_dtor(void * ptr)3488 static void wc_data_dtor(void *ptr)
3489 {
3490   struct ftp_wc_tmpdata *tmp = ptr;
3491   if(tmp)
3492     Curl_ftp_parselist_data_free(&tmp->parser);
3493   Curl_safefree(tmp);
3494 }
3495 
init_wc_data(struct connectdata * conn)3496 static CURLcode init_wc_data(struct connectdata *conn)
3497 {
3498   char *last_slash;
3499   char *path = conn->data->state.path;
3500   struct WildcardData *wildcard = &(conn->data->wildcard);
3501   CURLcode ret = CURLE_OK;
3502   struct ftp_wc_tmpdata *ftp_tmp;
3503 
3504   last_slash = strrchr(conn->data->state.path, '/');
3505   if(last_slash) {
3506     last_slash++;
3507     if(last_slash[0] == '\0') {
3508       wildcard->state = CURLWC_CLEAN;
3509       ret = ftp_parse_url_path(conn);
3510       return ret;
3511     }
3512     else {
3513       wildcard->pattern = strdup(last_slash);
3514       if (!wildcard->pattern)
3515         return CURLE_OUT_OF_MEMORY;
3516       last_slash[0] = '\0'; /* cut file from path */
3517     }
3518   }
3519   else { /* there is only 'wildcard pattern' or nothing */
3520     if(path[0]) {
3521       wildcard->pattern = strdup(path);
3522       if (!wildcard->pattern)
3523         return CURLE_OUT_OF_MEMORY;
3524       path[0] = '\0';
3525     }
3526     else { /* only list */
3527       wildcard->state = CURLWC_CLEAN;
3528       ret = ftp_parse_url_path(conn);
3529       return ret;
3530     }
3531   }
3532 
3533   /* program continues only if URL is not ending with slash, allocate needed
3534      resources for wildcard transfer */
3535 
3536   /* allocate ftp protocol specific temporary wildcard data */
3537   ftp_tmp = malloc(sizeof(struct ftp_wc_tmpdata));
3538   if(!ftp_tmp) {
3539     return CURLE_OUT_OF_MEMORY;
3540   }
3541 
3542   /* INITIALIZE parselist structure */
3543   ftp_tmp->parser = Curl_ftp_parselist_data_alloc();
3544   if(!ftp_tmp->parser) {
3545     free(ftp_tmp);
3546     return CURLE_OUT_OF_MEMORY;
3547   }
3548 
3549   wildcard->tmp = ftp_tmp; /* put it to the WildcardData tmp pointer */
3550   wildcard->tmp_dtor = wc_data_dtor;
3551 
3552   /* wildcard does not support NOCWD option (assert it?) */
3553   if(conn->data->set.ftp_filemethod == FTPFILE_NOCWD)
3554     conn->data->set.ftp_filemethod = FTPFILE_MULTICWD;
3555 
3556   /* try to parse ftp url */
3557   ret = ftp_parse_url_path(conn);
3558   if(ret) {
3559     return ret;
3560   }
3561 
3562   /* backup old write_function */
3563   ftp_tmp->backup.write_function = conn->data->set.fwrite_func;
3564   /* parsing write function (callback included directly from ftplistparser.c) */
3565   conn->data->set.fwrite_func = Curl_ftp_parselist;
3566   /* backup old file descriptor */
3567   ftp_tmp->backup.file_descriptor = conn->data->set.out;
3568   /* let the writefunc callback know what curl pointer is working with */
3569   conn->data->set.out = conn;
3570 
3571   wildcard->path = strdup(conn->data->state.path);
3572   if(!wildcard->path) {
3573     return CURLE_OUT_OF_MEMORY;
3574   }
3575 
3576   infof(conn->data, "Wildcard - Parsing started\n");
3577   return CURLE_OK;
3578 }
3579 
3580 /* This is called recursively */
wc_statemach(struct connectdata * conn)3581 static CURLcode wc_statemach(struct connectdata *conn)
3582 {
3583   struct WildcardData * const wildcard = &(conn->data->wildcard);
3584   CURLcode ret = CURLE_OK;
3585 
3586   switch (wildcard->state) {
3587   case CURLWC_INIT:
3588     ret = init_wc_data(conn);
3589     if(wildcard->state == CURLWC_CLEAN)
3590       /* only listing! */
3591       break;
3592     else
3593       wildcard->state = ret ? CURLWC_ERROR : CURLWC_MATCHING;
3594     break;
3595 
3596   case CURLWC_MATCHING: {
3597     /* In this state is LIST response successfully parsed, so lets restore
3598        previous WRITEFUNCTION callback and WRITEDATA pointer */
3599     struct ftp_wc_tmpdata *ftp_tmp = wildcard->tmp;
3600     conn->data->set.fwrite_func = ftp_tmp->backup.write_function;
3601     conn->data->set.out = ftp_tmp->backup.file_descriptor;
3602     wildcard->state = CURLWC_DOWNLOADING;
3603 
3604     if(Curl_ftp_parselist_geterror(ftp_tmp->parser)) {
3605       /* error found in LIST parsing */
3606       wildcard->state = CURLWC_CLEAN;
3607       return wc_statemach(conn);
3608     }
3609     else if(wildcard->filelist->size == 0) {
3610       /* no corresponding file */
3611       wildcard->state = CURLWC_CLEAN;
3612       return CURLE_REMOTE_FILE_NOT_FOUND;
3613     }
3614     return wc_statemach(conn);
3615   }
3616 
3617   case CURLWC_DOWNLOADING: {
3618     /* filelist has at least one file, lets get first one */
3619     struct ftp_conn *ftpc = &conn->proto.ftpc;
3620     struct curl_fileinfo *finfo = wildcard->filelist->head->ptr;
3621     char *tmp_path = malloc(strlen(conn->data->state.path) +
3622                       strlen(finfo->filename) + 1);
3623     if(!tmp_path) {
3624       return CURLE_OUT_OF_MEMORY;
3625     }
3626 
3627     tmp_path[0] = 0;
3628     /* make full path to matched file */
3629     strcat(tmp_path, wildcard->path);
3630     strcat(tmp_path, finfo->filename);
3631     /* switch default "state.pathbuffer" and tmp_path, good to see
3632        ftp_parse_url_path function to understand this trick */
3633     if(conn->data->state.pathbuffer)
3634       free(conn->data->state.pathbuffer);
3635     conn->data->state.pathbuffer = tmp_path;
3636     conn->data->state.path = tmp_path;
3637 
3638     infof(conn->data, "Wildcard - START of \"%s\"\n", finfo->filename);
3639     if(conn->data->set.chunk_bgn) {
3640       long userresponse = conn->data->set.chunk_bgn(
3641           finfo, wildcard->customptr, (int)wildcard->filelist->size);
3642       switch(userresponse) {
3643       case CURL_CHUNK_BGN_FUNC_SKIP:
3644         infof(conn->data, "Wildcard - \"%s\" skipped by user\n",
3645               finfo->filename);
3646         wildcard->state = CURLWC_SKIP;
3647         return wc_statemach(conn);
3648       case CURL_CHUNK_BGN_FUNC_FAIL:
3649         return CURLE_CHUNK_FAILED;
3650       }
3651     }
3652 
3653     if(finfo->filetype != CURLFILETYPE_FILE) {
3654       wildcard->state = CURLWC_SKIP;
3655       return wc_statemach(conn);
3656     }
3657 
3658     if(finfo->flags & CURLFINFOFLAG_KNOWN_SIZE)
3659       ftpc->known_filesize = finfo->size;
3660 
3661     ret = ftp_parse_url_path(conn);
3662     if(ret) {
3663       return ret;
3664     }
3665 
3666     /* we don't need the Curl_fileinfo of first file anymore */
3667     Curl_llist_remove(wildcard->filelist, wildcard->filelist->head, NULL);
3668 
3669     if(wildcard->filelist->size == 0) { /* remains only one file to down. */
3670       wildcard->state = CURLWC_CLEAN;
3671       /* after that will be ftp_do called once again and no transfer
3672          will be done because of CURLWC_CLEAN state */
3673       return CURLE_OK;
3674     }
3675   } break;
3676 
3677   case CURLWC_SKIP: {
3678     if(conn->data->set.chunk_end)
3679       conn->data->set.chunk_end(conn->data->wildcard.customptr);
3680     Curl_llist_remove(wildcard->filelist, wildcard->filelist->head, NULL);
3681     wildcard->state = (wildcard->filelist->size == 0) ?
3682                       CURLWC_CLEAN : CURLWC_DOWNLOADING;
3683     return wc_statemach(conn);
3684   }
3685 
3686   case CURLWC_CLEAN: {
3687     struct ftp_wc_tmpdata *ftp_tmp = wildcard->tmp;
3688     ret = CURLE_OK;
3689     if(ftp_tmp) {
3690       ret = Curl_ftp_parselist_geterror(ftp_tmp->parser);
3691     }
3692     wildcard->state = ret ? CURLWC_ERROR : CURLWC_DONE;
3693   } break;
3694 
3695   case CURLWC_DONE:
3696   case CURLWC_ERROR:
3697     break;
3698   }
3699 
3700   return ret;
3701 }
3702 
3703 /***********************************************************************
3704  *
3705  * ftp_do()
3706  *
3707  * This function is registered as 'curl_do' function. It decodes the path
3708  * parts etc as a wrapper to the actual DO function (ftp_perform).
3709  *
3710  * The input argument is already checked for validity.
3711  */
ftp_do(struct connectdata * conn,bool * done)3712 static CURLcode ftp_do(struct connectdata *conn, bool *done)
3713 {
3714   CURLcode retcode = CURLE_OK;
3715 
3716   *done = FALSE; /* default to false */
3717 
3718   /*
3719     Since connections can be re-used between SessionHandles, this might be a
3720     connection already existing but on a fresh SessionHandle struct so we must
3721     make sure we have a good 'struct FTP' to play with. For new connections,
3722     the struct FTP is allocated and setup in the ftp_connect() function.
3723   */
3724   Curl_reset_reqproto(conn);
3725   retcode = ftp_init(conn);
3726   if(retcode)
3727     return retcode;
3728 
3729   if(conn->data->set.wildcardmatch) {
3730     retcode = wc_statemach(conn);
3731     if(conn->data->wildcard.state == CURLWC_SKIP ||
3732       conn->data->wildcard.state == CURLWC_DONE) {
3733       /* do not call ftp_regular_transfer */
3734       return CURLE_OK;
3735     }
3736     if(retcode) /* error, loop or skipping the file */
3737       return retcode;
3738   }
3739   else { /* no wildcard FSM needed */
3740     retcode = ftp_parse_url_path(conn);
3741     if(retcode)
3742       return retcode;
3743   }
3744 
3745   retcode = ftp_regular_transfer(conn, done);
3746 
3747   return retcode;
3748 }
3749 
3750 
Curl_ftpsendf(struct connectdata * conn,const char * fmt,...)3751 CURLcode Curl_ftpsendf(struct connectdata *conn,
3752                        const char *fmt, ...)
3753 {
3754   ssize_t bytes_written;
3755 #define SBUF_SIZE 1024
3756   char s[SBUF_SIZE];
3757   size_t write_len;
3758   char *sptr=s;
3759   CURLcode res = CURLE_OK;
3760 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
3761   enum protection_level data_sec = conn->data_prot;
3762 #endif
3763 
3764   va_list ap;
3765   va_start(ap, fmt);
3766   vsnprintf(s, SBUF_SIZE-3, fmt, ap);
3767   va_end(ap);
3768 
3769   strcat(s, "\r\n"); /* append a trailing CRLF */
3770 
3771   bytes_written=0;
3772   write_len = strlen(s);
3773 
3774   res = Curl_convert_to_network(conn->data, s, write_len);
3775   /* Curl_convert_to_network calls failf if unsuccessful */
3776   if(res)
3777     return(res);
3778 
3779   for(;;) {
3780 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
3781     conn->data_prot = PROT_CMD;
3782 #endif
3783     res = Curl_write(conn, conn->sock[FIRSTSOCKET], sptr, write_len,
3784                      &bytes_written);
3785 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
3786     DEBUGASSERT(data_sec > PROT_NONE && data_sec < PROT_LAST);
3787     conn->data_prot = data_sec;
3788 #endif
3789 
3790     if(CURLE_OK != res)
3791       break;
3792 
3793     if(conn->data->set.verbose)
3794       Curl_debug(conn->data, CURLINFO_HEADER_OUT,
3795                  sptr, (size_t)bytes_written, conn);
3796 
3797     if(bytes_written != (ssize_t)write_len) {
3798       write_len -= bytes_written;
3799       sptr += bytes_written;
3800     }
3801     else
3802       break;
3803   }
3804 
3805   return res;
3806 }
3807 
3808 /***********************************************************************
3809  *
3810  * ftp_quit()
3811  *
3812  * This should be called before calling sclose() on an ftp control connection
3813  * (not data connections). We should then wait for the response from the
3814  * server before returning. The calling code should then try to close the
3815  * connection.
3816  *
3817  */
ftp_quit(struct connectdata * conn)3818 static CURLcode ftp_quit(struct connectdata *conn)
3819 {
3820   CURLcode result = CURLE_OK;
3821 
3822   if(conn->proto.ftpc.ctl_valid) {
3823     PPSENDF(&conn->proto.ftpc.pp, "QUIT", NULL);
3824     state(conn, FTP_QUIT);
3825 
3826     result = ftp_easy_statemach(conn);
3827   }
3828 
3829   return result;
3830 }
3831 
3832 /***********************************************************************
3833  *
3834  * ftp_disconnect()
3835  *
3836  * Disconnect from an FTP server. Cleanup protocol-specific per-connection
3837  * resources. BLOCKING.
3838  */
ftp_disconnect(struct connectdata * conn,bool dead_connection)3839 static CURLcode ftp_disconnect(struct connectdata *conn, bool dead_connection)
3840 {
3841   struct ftp_conn *ftpc= &conn->proto.ftpc;
3842   struct pingpong *pp = &ftpc->pp;
3843 
3844   /* We cannot send quit unconditionally. If this connection is stale or
3845      bad in any way, sending quit and waiting around here will make the
3846      disconnect wait in vain and cause more problems than we need to.
3847 
3848      ftp_quit() will check the state of ftp->ctl_valid. If it's ok it
3849      will try to send the QUIT command, otherwise it will just return.
3850   */
3851   if(dead_connection)
3852     ftpc->ctl_valid = FALSE;
3853 
3854   /* The FTP session may or may not have been allocated/setup at this point! */
3855   (void)ftp_quit(conn); /* ignore errors on the QUIT */
3856 
3857   if(ftpc->entrypath) {
3858     struct SessionHandle *data = conn->data;
3859     if(data->state.most_recent_ftp_entrypath == ftpc->entrypath) {
3860       data->state.most_recent_ftp_entrypath = NULL;
3861     }
3862     free(ftpc->entrypath);
3863     ftpc->entrypath = NULL;
3864   }
3865 
3866   freedirs(ftpc);
3867   if(ftpc->prevpath) {
3868     free(ftpc->prevpath);
3869     ftpc->prevpath = NULL;
3870   }
3871   if(ftpc->server_os) {
3872     free(ftpc->server_os);
3873     ftpc->server_os = NULL;
3874   }
3875 
3876   Curl_pp_disconnect(pp);
3877 
3878 #if defined(HAVE_KRB4) || defined(HAVE_GSSAPI)
3879   Curl_sec_end(conn);
3880 #endif
3881 
3882   return CURLE_OK;
3883 }
3884 
3885 /***********************************************************************
3886  *
3887  * ftp_parse_url_path()
3888  *
3889  * Parse the URL path into separate path components.
3890  *
3891  */
3892 static
ftp_parse_url_path(struct connectdata * conn)3893 CURLcode ftp_parse_url_path(struct connectdata *conn)
3894 {
3895   struct SessionHandle *data = conn->data;
3896   /* the ftp struct is already inited in ftp_connect() */
3897   struct FTP *ftp = data->state.proto.ftp;
3898   struct ftp_conn *ftpc = &conn->proto.ftpc;
3899   const char *slash_pos;  /* position of the first '/' char in curpos */
3900   const char *path_to_use = data->state.path;
3901   const char *cur_pos;
3902   const char *filename = NULL;
3903 
3904   cur_pos = path_to_use; /* current position in path. point at the begin
3905                             of next path component */
3906 
3907   ftpc->ctl_valid = FALSE;
3908   ftpc->cwdfail = FALSE;
3909 
3910   switch(data->set.ftp_filemethod) {
3911   case FTPFILE_NOCWD:
3912     /* fastest, but less standard-compliant */
3913 
3914     /*
3915       The best time to check whether the path is a file or directory is right
3916       here. so:
3917 
3918       the first condition in the if() right here, is there just in case
3919       someone decides to set path to NULL one day
3920    */
3921     if(data->state.path &&
3922        data->state.path[0] &&
3923        (data->state.path[strlen(data->state.path) - 1] != '/') )
3924       filename = data->state.path;  /* this is a full file path */
3925       /*
3926         ftpc->file is not used anywhere other than for operations on a file.
3927         In other words, never for directory operations.
3928         So we can safely leave filename as NULL here and use it as a
3929         argument in dir/file decisions.
3930       */
3931     break;
3932 
3933   case FTPFILE_SINGLECWD:
3934     /* get the last slash */
3935     if(!path_to_use[0]) {
3936       /* no dir, no file */
3937       ftpc->dirdepth = 0;
3938       break;
3939     }
3940     slash_pos=strrchr(cur_pos, '/');
3941     if(slash_pos || !*cur_pos) {
3942       ftpc->dirs = calloc(1, sizeof(ftpc->dirs[0]));
3943       if(!ftpc->dirs)
3944         return CURLE_OUT_OF_MEMORY;
3945 
3946       ftpc->dirs[0] = curl_easy_unescape(conn->data, slash_pos ? cur_pos : "/",
3947                                          slash_pos?(int)(slash_pos-cur_pos):1,
3948                                          NULL);
3949       if(!ftpc->dirs[0]) {
3950         freedirs(ftpc);
3951         return CURLE_OUT_OF_MEMORY;
3952       }
3953       ftpc->dirdepth = 1; /* we consider it to be a single dir */
3954       filename = slash_pos ? slash_pos+1 : cur_pos; /* rest is file name */
3955     }
3956     else
3957       filename = cur_pos;  /* this is a file name only */
3958     break;
3959 
3960   default: /* allow pretty much anything */
3961   case FTPFILE_MULTICWD:
3962     ftpc->dirdepth = 0;
3963     ftpc->diralloc = 5; /* default dir depth to allocate */
3964     ftpc->dirs = calloc(ftpc->diralloc, sizeof(ftpc->dirs[0]));
3965     if(!ftpc->dirs)
3966       return CURLE_OUT_OF_MEMORY;
3967 
3968     /* we have a special case for listing the root dir only */
3969     if(strequal(path_to_use, "/")) {
3970       cur_pos++; /* make it point to the zero byte */
3971       ftpc->dirs[0] = strdup("/");
3972       ftpc->dirdepth++;
3973     }
3974     else {
3975       /* parse the URL path into separate path components */
3976       while((slash_pos = strchr(cur_pos, '/')) != NULL) {
3977         /* 1 or 0 pointer offset to indicate absolute directory */
3978         ssize_t absolute_dir = ((cur_pos - data->state.path > 0) &&
3979                                 (ftpc->dirdepth == 0))?1:0;
3980 
3981         /* seek out the next path component */
3982         if(slash_pos-cur_pos) {
3983           /* we skip empty path components, like "x//y" since the FTP command
3984              CWD requires a parameter and a non-existent parameter a) doesn't
3985              work on many servers and b) has no effect on the others. */
3986           int len = (int)(slash_pos - cur_pos + absolute_dir);
3987           ftpc->dirs[ftpc->dirdepth] =
3988             curl_easy_unescape(conn->data, cur_pos - absolute_dir, len, NULL);
3989           if(!ftpc->dirs[ftpc->dirdepth]) { /* run out of memory ... */
3990             failf(data, "no memory");
3991             freedirs(ftpc);
3992             return CURLE_OUT_OF_MEMORY;
3993           }
3994           if(isBadFtpString(ftpc->dirs[ftpc->dirdepth])) {
3995             free(ftpc->dirs[ftpc->dirdepth]);
3996             freedirs(ftpc);
3997             return CURLE_URL_MALFORMAT;
3998           }
3999         }
4000         else {
4001           cur_pos = slash_pos + 1; /* jump to the rest of the string */
4002           continue;
4003         }
4004 
4005         cur_pos = slash_pos + 1; /* jump to the rest of the string */
4006         if(++ftpc->dirdepth >= ftpc->diralloc) {
4007           /* enlarge array */
4008           char *bigger;
4009           ftpc->diralloc *= 2; /* double the size each time */
4010           bigger = realloc(ftpc->dirs, ftpc->diralloc * sizeof(ftpc->dirs[0]));
4011           if(!bigger) {
4012             freedirs(ftpc);
4013             return CURLE_OUT_OF_MEMORY;
4014           }
4015           ftpc->dirs = (char **)bigger;
4016         }
4017       }
4018     }
4019     filename = cur_pos;  /* the rest is the file name */
4020     break;
4021   } /* switch */
4022 
4023   if(filename && *filename) {
4024     ftpc->file = curl_easy_unescape(conn->data, filename, 0, NULL);
4025     if(NULL == ftpc->file) {
4026       freedirs(ftpc);
4027       failf(data, "no memory");
4028       return CURLE_OUT_OF_MEMORY;
4029     }
4030     if(isBadFtpString(ftpc->file)) {
4031       freedirs(ftpc);
4032       return CURLE_URL_MALFORMAT;
4033     }
4034   }
4035   else
4036     ftpc->file=NULL; /* instead of point to a zero byte, we make it a NULL
4037                        pointer */
4038 
4039   if(data->set.upload && !ftpc->file && (ftp->transfer == FTPTRANSFER_BODY)) {
4040     /* We need a file name when uploading. Return error! */
4041     failf(data, "Uploading to a URL without a file name!");
4042     return CURLE_URL_MALFORMAT;
4043   }
4044 
4045   ftpc->cwddone = FALSE; /* default to not done */
4046 
4047   if(ftpc->prevpath) {
4048     /* prevpath is "raw" so we convert the input path before we compare the
4049        strings */
4050     int dlen;
4051     char *path = curl_easy_unescape(conn->data, data->state.path, 0, &dlen);
4052     if(!path) {
4053       freedirs(ftpc);
4054       return CURLE_OUT_OF_MEMORY;
4055     }
4056 
4057     dlen -= ftpc->file?(int)strlen(ftpc->file):0;
4058     if((dlen == (int)strlen(ftpc->prevpath)) &&
4059        strnequal(path, ftpc->prevpath, dlen)) {
4060       infof(data, "Request has same path as previous transfer\n");
4061       ftpc->cwddone = TRUE;
4062     }
4063     free(path);
4064   }
4065 
4066   return CURLE_OK;
4067 }
4068 
4069 /* call this when the DO phase has completed */
ftp_dophase_done(struct connectdata * conn,bool connected)4070 static CURLcode ftp_dophase_done(struct connectdata *conn,
4071                                  bool connected)
4072 {
4073   CURLcode result = CURLE_OK;
4074   struct FTP *ftp = conn->data->state.proto.ftp;
4075   struct ftp_conn *ftpc = &conn->proto.ftpc;
4076 
4077   if(connected)
4078     result = ftp_nextconnect(conn);
4079 
4080   if(result && (conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD)) {
4081     /* Failure detected, close the second socket if it was created already */
4082     sclose(conn->sock[SECONDARYSOCKET]);
4083     conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
4084     return result;
4085   }
4086 
4087   if(ftp->transfer != FTPTRANSFER_BODY)
4088     /* no data to transfer */
4089     Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
4090   else if(!connected)
4091     /* since we didn't connect now, we want do_more to get called */
4092     conn->bits.do_more = TRUE;
4093 
4094   ftpc->ctl_valid = TRUE; /* seems good */
4095 
4096   return result;
4097 }
4098 
4099 /* called from multi.c while DOing */
ftp_doing(struct connectdata * conn,bool * dophase_done)4100 static CURLcode ftp_doing(struct connectdata *conn,
4101                                bool *dophase_done)
4102 {
4103   CURLcode result;
4104   result = ftp_multi_statemach(conn, dophase_done);
4105 
4106   if(*dophase_done) {
4107     result = ftp_dophase_done(conn, FALSE /* not connected */);
4108 
4109     DEBUGF(infof(conn->data, "DO phase is complete\n"));
4110   }
4111   return result;
4112 }
4113 
4114 /***********************************************************************
4115  *
4116  * ftp_regular_transfer()
4117  *
4118  * The input argument is already checked for validity.
4119  *
4120  * Performs all commands done before a regular transfer between a local and a
4121  * remote host.
4122  *
4123  * ftp->ctl_valid starts out as FALSE, and gets set to TRUE if we reach the
4124  * ftp_done() function without finding any major problem.
4125  */
4126 static
ftp_regular_transfer(struct connectdata * conn,bool * dophase_done)4127 CURLcode ftp_regular_transfer(struct connectdata *conn,
4128                               bool *dophase_done)
4129 {
4130   CURLcode result=CURLE_OK;
4131   bool connected=FALSE;
4132   struct SessionHandle *data = conn->data;
4133   struct ftp_conn *ftpc = &conn->proto.ftpc;
4134   data->req.size = -1; /* make sure this is unknown at this point */
4135 
4136   Curl_pgrsSetUploadCounter(data, 0);
4137   Curl_pgrsSetDownloadCounter(data, 0);
4138   Curl_pgrsSetUploadSize(data, 0);
4139   Curl_pgrsSetDownloadSize(data, 0);
4140 
4141   ftpc->ctl_valid = TRUE; /* starts good */
4142 
4143   result = ftp_perform(conn,
4144                        &connected, /* have we connected after PASV/PORT */
4145                        dophase_done); /* all commands in the DO-phase done? */
4146 
4147   if(CURLE_OK == result) {
4148 
4149     if(!*dophase_done)
4150       /* the DO phase has not completed yet */
4151       return CURLE_OK;
4152 
4153     result = ftp_dophase_done(conn, connected);
4154     if(result)
4155       return result;
4156   }
4157   else
4158     freedirs(ftpc);
4159 
4160   return result;
4161 }
4162 
ftp_setup_connection(struct connectdata * conn)4163 static CURLcode ftp_setup_connection(struct connectdata * conn)
4164 {
4165   struct SessionHandle *data = conn->data;
4166   char * type;
4167   char command;
4168 
4169   if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
4170     /* Unless we have asked to tunnel ftp operations through the proxy, we
4171        switch and use HTTP operations only */
4172 #ifndef CURL_DISABLE_HTTP
4173     if(conn->handler == &Curl_handler_ftp)
4174       conn->handler = &Curl_handler_ftp_proxy;
4175     else {
4176 #ifdef USE_SSL
4177       conn->handler = &Curl_handler_ftps_proxy;
4178 #else
4179       failf(data, "FTPS not supported!");
4180       return CURLE_UNSUPPORTED_PROTOCOL;
4181 #endif
4182     }
4183     /*
4184      * We explicitly mark this connection as persistent here as we're doing
4185      * FTP over HTTP and thus we accidentally avoid setting this value
4186      * otherwise.
4187      */
4188     conn->bits.close = FALSE;
4189 #else
4190     failf(data, "FTP over http proxy requires HTTP support built-in!");
4191     return CURLE_UNSUPPORTED_PROTOCOL;
4192 #endif
4193   }
4194 
4195   data->state.path++;   /* don't include the initial slash */
4196   data->state.slash_removed = TRUE; /* we've skipped the slash */
4197 
4198   /* FTP URLs support an extension like ";type=<typecode>" that
4199    * we'll try to get now! */
4200   type = strstr(data->state.path, ";type=");
4201 
4202   if(!type)
4203     type = strstr(conn->host.rawalloc, ";type=");
4204 
4205   if(type) {
4206     *type = 0;                     /* it was in the middle of the hostname */
4207     command = Curl_raw_toupper(type[6]);
4208     conn->bits.type_set = TRUE;
4209 
4210     switch (command) {
4211     case 'A': /* ASCII mode */
4212       data->set.prefer_ascii = TRUE;
4213       break;
4214 
4215     case 'D': /* directory mode */
4216       data->set.ftp_list_only = TRUE;
4217       break;
4218 
4219     case 'I': /* binary mode */
4220     default:
4221       /* switch off ASCII */
4222       data->set.prefer_ascii = FALSE;
4223       break;
4224     }
4225   }
4226 
4227   return CURLE_OK;
4228 }
4229 
4230 #endif /* CURL_DISABLE_FTP */
4231