Coverage Report

Created: 2026-05-30 06:06

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