Skip to content
Snippets Groups Projects
ftp.c 37.5 KiB
Newer Older
  • Learn to ignore specific revisions
  • Daniel Stenberg's avatar
    Daniel Stenberg committed
        /* When we know we're uploading a specified file, we can get the file
           size prior to the actual upload. */
    
    
        pgrsSetUploadSize(data, data->infilesize);
    #if 0
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        ProgressInit(data, data->infilesize);
    
        result = Transfer(conn, -1, -1, FALSE, NULL, /* no download */
    
                          data->secondarysocket, bytecountp);
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        if(result)
          return result;
          
      }
      else {
        /* Retrieve file or directory */
        bool dirlist=FALSE;
        long downloadsize=-1;
    
    
        if(data->bits.set_range && data->range) {
          long from, to;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          int totalsize=-1;
          char *ptr;
          char *ptr2;
    
          from=strtol(data->range, &ptr, 0);
          while(ptr && *ptr && (isspace((int)*ptr) || (*ptr=='-')))
            ptr++;
          to=strtol(ptr, &ptr2, 0);
          if(ptr == ptr2) {
            /* we didn't get any digit */
            to=-1;
          }
    
          if((-1 == to) && (from>=0)) {
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
            /* X - */
            data->resume_from = from;
    
            infof(data, "FTP RANGE %d to end of file\n", from);
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          }
          else if(from < 0) {
            /* -Y */
    
            totalsize = -from;
            data->maxdownload = -from;
            data->resume_from = from;
            infof(data, "FTP RANGE the last %d bytes\n", totalsize);
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          }
          else {
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
            totalsize = to-from;
    
            data->maxdownload = totalsize+1; /* include the last mentioned byte */
            data->resume_from = from;
            infof(data, "FTP RANGE from %d getting %d bytes\n", from, data->maxdownload);
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          }
          infof(data, "range-download from %d to %d, totally %d bytes\n",
                from, to, totalsize);
        }
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        if(!ppath[0])
          /* make sure this becomes a valid name */
          ppath="./";
    
    #endif
        if((data->bits.ftp_list_only) || !ftp->file) {
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          /* The specified path ends with a slash, and therefore we think this
             is a directory that is requested, use LIST. But before that we
             need to set ASCII transfer mode. */
          dirlist = TRUE;
    
          /* Set type to ASCII */
          sendf(data->firstsocket, data, "TYPE A\r\n");
    	
    
          nread = GetLastResponse(data->firstsocket, buf, conn);
          if(nread < 0)
            return CURLE_OPERATION_TIMEOUTED;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    	
          if(strncmp(buf, "200", 3)) {
            failf(data, "Couldn't set ascii mode");
    
            return CURLE_FTP_COULDNT_SET_ASCII;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          }
    
          /* if this output is to be machine-parsed, the NLST command will be
             better used since the LIST command output is not specified or
             standard in any way */
    
    
          sendf(data->firstsocket, data, "%s\r\n",
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
                data->customrequest?data->customrequest:
    
                (data->bits.ftp_list_only?"NLST":"LIST"));
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        }
        else {
          /* Set type to binary (unless specified ASCII) */
          sendf(data->firstsocket, data, "TYPE %s\r\n",
    
                (data->bits.ftp_list_only)?"A":"I");
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
          nread = GetLastResponse(data->firstsocket, buf, conn);
          if(nread < 0)
            return CURLE_OPERATION_TIMEOUTED;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
          if(strncmp(buf, "200", 3)) {
            failf(data, "Couldn't set %s mode",
    
                  (data->bits.ftp_ascii)?"ASCII":"binary");
            return (data->bits.ftp_ascii)? CURLE_FTP_COULDNT_SET_ASCII:
              CURLE_FTP_COULDNT_SET_BINARY;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          }
    
          if(data->resume_from) {
    
            /* Daniel: (August 4, 1999)
             *
             * We start with trying to use the SIZE command to figure out the size
             * 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, conn);
            if(nread < 0)
              return CURLE_OPERATION_TIMEOUTED;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
            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, conn);
            if(nread < 0)
              return CURLE_OPERATION_TIMEOUTED;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
            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);
    
        nread = GetLastResponse(data->firstsocket, buf, conn);
        if(nread < 0)
          return CURLE_OPERATION_TIMEOUTED;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
        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 &&
             !data->bits.ftp_ascii &&
             (-1 == downloadsize)) {
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
            /*
             * It seems directory listings either don't show the size or very
    
             * often uses size 0 anyway. ASCII transfers may very well turn out
             * that the transfered amount of data is not the same as this line
             * tells, why using this number in those cases only confuses us.
             *
             * Example D above makes this parsing a little tricky */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
            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;
    }