Coverage Report

Created: 2023-06-07 07:02

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