Skip to content
ftp.c 33.3 KiB
Newer Older
Daniel Stenberg's avatar
Daniel Stenberg committed
        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;
}