Skip to content
ftp.c 127 KiB
Newer Older
        return CURLE_OUT_OF_MEMORY;

      ftpc->dirs[0] = curl_easy_unescape(conn->data, slash_pos ? cur_pos : "/",
                                         slash_pos?(int)(slash_pos-cur_pos):1,
                                         NULL);
      if(!ftpc->dirs[0]) {
      ftpc->dirdepth = 1; /* we consider it to be a single dir */
      filename = slash_pos ? slash_pos+1 : cur_pos; /* rest is file name */
      filename = cur_pos;  /* this is a file name only */
  default: /* allow pretty much anything */
  case FTPFILE_MULTICWD:
    ftpc->dirdepth = 0;
    ftpc->diralloc = 5; /* default dir depth to allocate */
    ftpc->dirs = calloc(ftpc->diralloc, sizeof(ftpc->dirs[0]));
      return CURLE_OUT_OF_MEMORY;

    /* we have a special case for listing the root dir only */
    if(strequal(path_to_use, "/")) {
      cur_pos++; /* make it point to the zero byte */
      ftpc->dirs[0] = strdup("/");
      ftpc->dirdepth++;
    }
    else {
      /* parse the URL path into separate path components */
      while((slash_pos = strchr(cur_pos, '/')) != NULL) {
        /* 1 or 0 to indicate absolute directory */
        bool absolute_dir = (bool)((cur_pos - data->state.path > 0) &&
                                   (ftpc->dirdepth == 0));

        /* seek out the next path component */
          /* we skip empty path components, like "x//y" since the FTP command
             CWD requires a parameter and a non-existant parameter a) doesn't
             work on many servers and b) has no effect on the others. */
          int len = (int)(slash_pos - cur_pos + absolute_dir);
          ftpc->dirs[ftpc->dirdepth] =
            curl_easy_unescape(conn->data, cur_pos - absolute_dir, len, NULL);
          if(!ftpc->dirs[ftpc->dirdepth]) { /* run out of memory ... */
          if(isBadFtpString(ftpc->dirs[ftpc->dirdepth])) {
        else {
          cur_pos = slash_pos + 1; /* jump to the rest of the string */
          continue;
        cur_pos = slash_pos + 1; /* jump to the rest of the string */
        if(++ftpc->dirdepth >= ftpc->diralloc) {
          /* enlarge array */
          char *bigger;
          ftpc->diralloc *= 2; /* double the size each time */
          bigger = realloc(ftpc->dirs, ftpc->diralloc * sizeof(ftpc->dirs[0]));
          if(!bigger) {
            return CURLE_OUT_OF_MEMORY;
          }
          ftpc->dirs = (char **)bigger;
        }
    filename = cur_pos;  /* the rest is the file name */
    break;
  } /* switch */
  if(filename && *filename) {
    ftpc->file = curl_easy_unescape(conn->data, filename, 0, NULL);
    if(NULL == ftpc->file) {
      failf(data, "no memory");
      return CURLE_OUT_OF_MEMORY;
    }
    ftpc->file=NULL; /* instead of point to a zero byte, we make it a NULL
  if(data->set.upload && !ftpc->file && (ftp->transfer == FTPTRANSFER_BODY)) {
    /* We need a file name when uploading. Return error! */
    failf(data, "Uploading to a URL without a file name!");
    return CURLE_URL_MALFORMAT;
  }

  ftpc->cwddone = FALSE; /* default to not done */
    /* prevpath is "raw" so we convert the input path before we compare the
       strings */
    int dlen;
    char *path = curl_easy_unescape(conn->data, data->state.path, 0, &dlen);
    dlen -= ftpc->file?(int)strlen(ftpc->file):0;
    if((dlen == (int)strlen(ftpc->prevpath)) &&
       strnequal(path, ftpc->prevpath, dlen)) {
      infof(data, "Request has same path as previous transfer\n");
Daniel Stenberg's avatar
Daniel Stenberg committed
    }
  return CURLE_OK;
/* call this when the DO phase has completed */
static CURLcode ftp_dophase_done(struct connectdata *conn,
                                 bool connected)
{
  CURLcode result = CURLE_OK;
  struct FTP *ftp = conn->data->state.proto.ftp;
  struct ftp_conn *ftpc = &conn->proto.ftpc;

  if(result && (conn->sock[SECONDARYSOCKET] != CURL_SOCKET_BAD)) {
    /* Failure detected, close the second socket if it was created already */
    sclose(conn->sock[SECONDARYSOCKET]);
    conn->sock[SECONDARYSOCKET] = CURL_SOCKET_BAD;
  if(ftp->transfer != FTPTRANSFER_BODY)
    result=Curl_setup_transfer(conn, -1, -1, FALSE, NULL, -1, NULL);
  else if(!connected)
    /* since we didn't connect now, we want do_more to get called */
    conn->bits.do_more = TRUE;

  ftpc->ctl_valid = TRUE; /* seems good */
static CURLcode ftp_doing(struct connectdata *conn,
  result = ftp_multi_statemach(conn, dophase_done);

  if(*dophase_done) {
    result = ftp_dophase_done(conn, FALSE /* not connected */);

    DEBUGF(infof(conn->data, "DO phase is complete\n"));

/***********************************************************************
 *
 * ftp_regular_transfer()
 *
 * The input argument is already checked for validity.
 *
 * Performs all commands done before a regular transfer between a local and a
 * remote host.
 *
 * ftp->ctl_valid starts out as FALSE, and gets set to TRUE if we reach the
 * ftp_done() function without finding any major problem.
CURLcode ftp_regular_transfer(struct connectdata *conn,
                              bool *dophase_done)
  bool connected=FALSE;
  struct SessionHandle *data = conn->data;
  struct ftp_conn *ftpc = &conn->proto.ftpc;
  data->req.size = -1; /* make sure this is unknown at this point */

  Curl_pgrsSetUploadCounter(data, 0);
  Curl_pgrsSetDownloadCounter(data, 0);
  Curl_pgrsSetUploadSize(data, 0);
  Curl_pgrsSetDownloadSize(data, 0);

  ftpc->ctl_valid = TRUE; /* starts good */
  result = ftp_perform(conn,
                       &connected, /* have we connected after PASV/PORT */
                       dophase_done); /* all commands in the DO-phase done? */
    if(!*dophase_done)
      /* the DO phase has not completed yet */
      return CURLE_OK;

    result = ftp_dophase_done(conn, connected);
    if(result)
      return result;
static CURLcode ftp_setup_connection(struct connectdata * conn)
{
  struct SessionHandle *data = conn->data;
  char * type;
  char command;

  if(conn->bits.httpproxy && !data->set.tunnel_thru_httpproxy) {
    /* Unless we have asked to tunnel ftp operations through the proxy, we
       switch and use HTTP operations only */
#ifndef CURL_DISABLE_HTTP
      conn->handler = &Curl_handler_ftp_proxy;
    else {
#ifdef USE_SSL
      conn->handler = &Curl_handler_ftps_proxy;
#else
      failf(data, "FTPS not supported!");
      return CURLE_UNSUPPORTED_PROTOCOL;
#endif
    }
    /*
     * We explicitly mark this connection as persistent here as we're doing
     * FTP over HTTP and thus we accidentally avoid setting this value
     * otherwise.
     */
    conn->bits.close = FALSE;
#else
    failf(data, "FTP over http proxy requires HTTP support built-in!");
    return CURLE_UNSUPPORTED_PROTOCOL;
#endif
  }

  data->state.path++;   /* don't include the initial slash */

  /* FTP URLs support an extension like ";type=<typecode>" that
   * we'll try to get now! */
  type = strstr(data->state.path, ";type=");
    type = strstr(conn->host.rawalloc, ";type=");

    *type = 0;                     /* it was in the middle of the hostname */
    command = Curl_raw_toupper(type[6]);

    switch (command) {
    case 'A': /* ASCII mode */
      data->set.prefer_ascii = TRUE;
      break;

    case 'D': /* directory mode */
      data->set.ftp_list_only = TRUE;
      break;

    case 'I': /* binary mode */
    default:
      /* switch off ASCII */
      data->set.prefer_ascii = FALSE;
      break;
    }
  }

  return CURLE_OK;
}