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