Coverage Report

Created: 2026-07-14 07:09

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