Skip to content
ftp.c 66.3 KiB
Newer Older
Daniel Stenberg's avatar
Daniel Stenberg committed
    if(data->set.timecondition) {
      if((data->info.filetime > 0) && (data->set.timevalue > 0)) {
        switch(data->set.timecondition) {
        case TIMECOND_IFMODSINCE:
        default:
          if(data->info.filetime < data->set.timevalue) {
            infof(data, "The requested document is not new enough\n");
            ftp->no_transfer = TRUE; /* mark this to not transfer data */
            return CURLE_OK;
          }
          break;
        case TIMECOND_IFUNMODSINCE:
          if(data->info.filetime > data->set.timevalue) {
            infof(data, "The requested document is not old enough\n");
            ftp->no_transfer = TRUE; /* mark this to not transfer data */
            return CURLE_OK;
          }
          break;
        } /* switch */
      }
      else {
        infof(data, "Skipping time comparison\n");
      }
    }
  }

  /* If we have selected NOBODY and HEADER, it means that we only want file
     information. Which in FTP can't be much more than the file size and
     date. */
  if(data->set.no_body && data->set.include_header && ftp->file) {
    /* The SIZE command is _not_ RFC 959 specified, and therefor many servers
       may not support it! It is however the only way we have to get a file's
       size! */
    ssize_t filesize;

    ftp->no_transfer = TRUE; /* this means no actual transfer is made */
    
    /* Some servers return different sizes for different modes, and thus we
       must set the proper type before we check the size */
    result = ftp_transfertype(conn, data->set.ftp_ascii);
    if(result)
      return result;

    /* failing to get size is not a serious error */
    result = ftp_getsize(conn, ftp->file, &filesize);

    if(CURLE_OK == result) {
      sprintf(buf, "Content-Length: %d\r\n", filesize);
      result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
      if(result)
        return result;
    }

    /* If we asked for a time of the file and we actually got one as
       well, we "emulate" a HTTP-style header in our output. */

#ifdef HAVE_STRFTIME
    if(data->set.get_filetime && (data->info.filetime>=0) ) {
      struct tm *tm;
#ifdef HAVE_LOCALTIME_R
      struct tm buffer;
      tm = (struct tm *)localtime_r(&data->info.filetime, &buffer);
#else
      tm = localtime((unsigned long *)&data->info.filetime);
#endif
      /* format: "Tue, 15 Nov 1994 12:45:26 GMT" */
Daniel Stenberg's avatar
Daniel Stenberg committed
      strftime(buf, BUFSIZE-1, "Last-Modified: %a, %d %b %Y %H:%M:%S GMT\r\n",
               tm);
      result = Curl_client_write(data, CLIENTWRITE_BOTH, buf, 0);
      if(result)
        return result;
    }
#endif

    return CURLE_OK;
  }

  if(data->set.no_body)
    /* doesn't really transfer any data */
    ftp->no_transfer = TRUE;
  /* Get us a second connection up and connected */
  else if(data->set.ftp_use_port) {
    /* We have chosen to use the PORT command */
    result = ftp_use_port(conn);
    if(CURLE_OK == result) {
      /* we have the data connection ready */
      infof(data, "Ordered connect of the data stream with PORT!\n");
      *connected = TRUE; /* mark us "still connected" */
    }
  }
  else {
    /* We have chosen (this is default) to use the PASV command */
    result = ftp_use_pasv(conn, connected);
    if(connected)
      infof(data, "Connected the data stream with PASV!\n");
  }
  
  return result;
}

/***********************************************************************
 *
 * Curl_ftp()
 *
 * This function is registered as 'curl_do' function. It decodes the path
 * parts etc as a wrapper to the actual DO function (ftp_perform).
 *
 * The input argument is already checked for validity.
 */
CURLcode Curl_ftp(struct connectdata *conn)
Daniel Stenberg's avatar
Daniel Stenberg committed
{
Daniel Stenberg's avatar
Daniel Stenberg committed
  bool connected=0;
Daniel Stenberg's avatar
Daniel Stenberg committed

  struct SessionHandle *data = conn->data;
  struct FTP *ftp;

  char *slash_pos;  /* position of the first '/' char in curpos */
  char *cur_pos=conn->ppath; /* current position in ppath. point at the begin
                                of next path component */
  int path_part=0;/* current path component */

  /* the ftp struct is already inited in ftp_connect() */
  ftp = conn->proto.ftp;
  conn->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);

  /*  fixed : initialize ftp->dirs[xxx] to NULL !
      is done in Curl_ftp_connect() */
  /* parse the URL path into separate path components */
  while((slash_pos=strchr(cur_pos, '/'))) {
    /* seek out the next path component */
    if (0 == slash_pos-cur_pos)  /* empty path component, like "x//y" */
      ftp->dirs[path_part] = strdup(""); /* empty string */
    else
      ftp->dirs[path_part] = curl_unescape(cur_pos,slash_pos-cur_pos);
    
    if (!ftp->dirs[path_part]) { /* run out of memory ... */
      failf(data, "no memory");
      retcode = CURLE_OUT_OF_MEMORY;
    }
    else {
      cur_pos = slash_pos + 1; /* jump to the rest of the string */
      if(++path_part >= (CURL_MAX_FTP_DIRDEPTH-1)) {
        /* too deep, we need the last entry to be kept NULL at all
           times to signal end of list */
        failf(data, "too deep dir hierarchy");
        retcode = CURLE_URL_MALFORMAT;
      }
    }
    if (retcode) {
      int i;
      for (i=0;i<path_part;i++) { /* free previous parts */
        free(ftp->dirs[i]);
        ftp->dirs[i]=NULL;
      }
      return retcode; /* failure */
    }
  ftp->file = cur_pos;  /* the rest is the file name */

  if(*ftp->file) {
    ftp->file = curl_unescape(ftp->file, 0);
    if(NULL == ftp->file) {
      int i;
      for (i=0;i<path_part;i++){
        free(ftp->dirs[i]);
        ftp->dirs[i]=NULL;
      }
      failf(data, "no memory");
      return CURLE_OUT_OF_MEMORY;
    }
Daniel Stenberg's avatar
Daniel Stenberg committed
  }
  else
    ftp->file=NULL; /* instead of point to a zero byte, we make it a NULL
                       pointer */
  retcode = ftp_perform(conn, &connected);
  if(CURLE_OK == retcode) {
    if(connected)
      retcode = Curl_ftp_nextconnect(conn);

    if(retcode && (conn->secondarysocket >= 0)) {
      /* Failure detected, close the second socket if it was created already */
      sclose(conn->secondarysocket);
      conn->secondarysocket = -1;

    if(ftp->no_transfer)
      /* no data to transfer */
      retcode=Curl_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;
Daniel Stenberg's avatar
Daniel Stenberg committed
  return retcode;
}

/***********************************************************************
 *
 * Curl_ftpsendf()
 *
 * Sends the formated string as a ftp command to a ftp server
 *
 * NOTE: we build the command in a fixed-length buffer, which sets length
 * restrictions on the command!
 */
CURLcode Curl_ftpsendf(struct connectdata *conn,
                       const char *fmt, ...)
  ssize_t write_len;
  char *sptr=s;
  CURLcode res = CURLE_OK;

  va_list ap;
  va_start(ap, fmt);
  vsnprintf(s, 250, fmt, ap);
  va_end(ap);
  strcat(s, "\r\n"); /* append a trailing CRLF */

Daniel Stenberg's avatar
Daniel Stenberg committed
  bytes_written=0;
  do {
    res = Curl_write(conn, conn->firstsocket, sptr, write_len,
                     &bytes_written);

    if(CURLE_OK != res)
      break;

    if(conn->data->set.verbose)
      Curl_debug(conn->data, CURLINFO_HEADER_OUT, sptr, bytes_written);

    if(bytes_written != write_len) {
      write_len -= bytes_written;
      sptr += bytes_written;
    }
    else
      break;
  } while(1);

  return res;
/***********************************************************************
 *
 * Curl_ftp_disconnect()
 *
 * Disconnect from an FTP server. Cleanup protocol-specific per-connection
 * resources
 */
CURLcode Curl_ftp_disconnect(struct connectdata *conn)
{
  struct FTP *ftp= conn->proto.ftp;
  /* The FTP session may or may not have been allocated/setup at this point! */
  if(ftp) {
    if(ftp->entrypath)
      free(ftp->entrypath);
    for (i=0;ftp->dirs[i];i++){
      free(ftp->dirs[i]);
      ftp->dirs[i]=NULL;
    }