Skip to content
ftp.c 33.9 KiB
Newer Older
Daniel Stenberg's avatar
Daniel Stenberg committed
         * of the file we're gonna get. If we can get the size, this is by far
         * the best way to know if we're trying to resume beyond the EOF.  */

        sendf(data->firstsocket, data, "SIZE %s\r\n", ftp->file);
Daniel Stenberg's avatar
Daniel Stenberg committed

        nread = GetLastResponse(data->firstsocket, buf, data);

        if(strncmp(buf, "213", 3)) {
          infof(data, "server doesn't support SIZE: %s", buf+4);
          /* We couldn't get the size and therefore we can't know if there
             really is a part of the file left to get, although the server
             will just close the connection when we start the connection so it
             won't cause us any harm, just not make us exit as nicely. */
        }
        else {
          int foundsize=atoi(buf+4);
          /* We got a file size report, so we check that there actually is a
             part of the file left to get, or else we go home.  */
          if(data->resume_from< 0) {
            /* We're supposed to download the last abs(from) bytes */
            if(foundsize < -data->resume_from) {
              failf(data, "Offset (%d) was beyond file size (%d)",
                    data->resume_from, foundsize);
              return CURLE_FTP_BAD_DOWNLOAD_RESUME;
            }
            /* convert to size to download */
            downloadsize = -data->resume_from;
            /* download from where? */
            data->resume_from = foundsize - downloadsize;
          }
          else {
            if(foundsize <= data->resume_from) {
              failf(data, "Offset (%d) was beyond file size (%d)",
                    data->resume_from, foundsize);
              return CURLE_FTP_BAD_DOWNLOAD_RESUME;
            }
            /* Now store the number of bytes we are expected to download */
            downloadsize = foundsize-data->resume_from;
Daniel Stenberg's avatar
Daniel Stenberg committed
          }
        }

        /* Set resume file transfer offset */
        infof(data, "Instructs server to resume from offset %d\n",
              data->resume_from);

        sendf(data->firstsocket, data, "REST %d\r\n", data->resume_from);

        nread = GetLastResponse(data->firstsocket, buf, data);

        if(strncmp(buf, "350", 3)) {
          failf(data, "Couldn't use REST: %s", buf+4);
          return CURLE_FTP_COULDNT_USE_REST;
      sendf(data->firstsocket, data, "RETR %s\r\n", ftp->file);
Daniel Stenberg's avatar
Daniel Stenberg committed
    }

    nread = GetLastResponse(data->firstsocket, buf, data);

    if(!strncmp(buf, "150", 3) || !strncmp(buf, "125", 3)) {

      /*
        A;
        150 Opening BINARY mode data connection for /etc/passwd (2241
        bytes).  (ok, the file is being transfered)
	
        B:
        150 Opening ASCII mode data connection for /bin/ls 

        C:
        150 ASCII data connection for /bin/ls (137.167.104.91,37445) (0 bytes).

        D:
        150 Opening ASCII mode data connection for /linux/fisk/kpanelrc (0.0.0.0,0) (545 bytes).
          
        E:
        125 Data connection already open; Transfer starting. */

      int size=-1; /* default unknown size */

      if(!dirlist && (-1 == downloadsize)) {
        /*
         * It seems directory listings either don't show the size or very
         * often uses size 0 anyway.
         * Example D above makes this parsing a little tricky
         */
        char *bytes;
        bytes=strstr(buf, " bytes");
        if(bytes--) {
          int index=bytes-buf;
          /* this is a hint there is size information in there! ;-) */
          while(--index) {
            /* scan for the parenthesis and break there */
            if('(' == *bytes)
              break;
            /* if only skip digits, or else we're in deep trouble */
            if(!isdigit((int)*bytes)) {
              bytes=NULL;
              break;
            }
            /* one more estep backwards */
            bytes--;
          }
          /* only if we have nothing but digits: */
          if(bytes++) {
            /* get the number! */
            size = atoi(bytes);
          }
            
        }
#if 0
        if(2 != sscanf(buf, "%*[^(](%d bytes%c", &size, &paren))
          size=-1;
#endif
      }
      else if(downloadsize > -1)
        size = downloadsize;

#if 0
      if((size > -1) && (data->resume_from>0)) {
        size -= data->resume_from;
        if(size <= 0) {
          failf(data, "Offset (%d) was beyond file size (%d)",
                data->resume_from, data->resume_from+size);
          return CURLE_PARTIAL_FILE;
      if(data->bits.ftp_use_port) {
Daniel Stenberg's avatar
Daniel Stenberg committed
        result = AllowServerConnect(data, portsock);
        if( result )
          return result;
      }

      infof(data, "Getting file with size: %d\n", size);

      /* FTP download: */
      result=Transfer(conn, data->secondarysocket, size, FALSE,
                      bytecountp,
                      -1, NULL); /* no upload here */
Daniel Stenberg's avatar
Daniel Stenberg committed
      if(result)
        return result;
    }
    else {
      failf(data, "%s", buf+4);
      return CURLE_FTP_COULDNT_RETR_FILE;
Daniel Stenberg's avatar
Daniel Stenberg committed
    }
	
  }
  /* end of transfer */
  return CURLE_OK;
Daniel Stenberg's avatar
Daniel Stenberg committed
}

/* -- deal with the ftp server!  -- */

/* argument is already checked for validity */
CURLcode ftp(struct connectdata *conn)
Daniel Stenberg's avatar
Daniel Stenberg committed
{
  CURLcode retcode;
Daniel Stenberg's avatar
Daniel Stenberg committed

  struct UrlData *data = conn->data;
  struct FTP *ftp;
  int dirlength=0; /* 0 forces strlen() */

  /* the ftp struct is already inited in ftp_connect() */
  ftp = data->proto.ftp;

  /* We split the path into dir and file parts *before* we URLdecode
     it */
  ftp->file = strrchr(conn->ppath, '/');
  if(ftp->file) {
    ftp->file++; /* point to the first letter in the file name part or
                    remain NULL */
  }
  else {
    ftp->file = conn->ppath; /* there's only a file part */
  }
  dirlength=ftp->file-conn->ppath;

  if(*ftp->file) {
    ftp->file = curl_unescape(ftp->file, 0);
    if(NULL == ftp->file) {
      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 */

  ftp->urlpath = conn->ppath;
  if(dirlength) {
    ftp->dir = curl_unescape(ftp->urlpath, dirlength);
    if(NULL == ftp->dir) {
      if(ftp->file)
        free(ftp->file);
      failf(data, "no memory");
      return CURLE_OUT_OF_MEMORY; /* failure */
    }
  }
  else
    ftp->dir = NULL;

  retcode = _ftp(conn);
Daniel Stenberg's avatar
Daniel Stenberg committed

  return retcode;
}