Skip to content
Snippets Groups Projects
ftp.c 41.3 KiB
Newer Older
  • Learn to ignore specific revisions
  • Daniel Stenberg's avatar
    Daniel Stenberg committed
    /*****************************************************************************
     *                                  _   _ ____  _     
     *  Project                     ___| | | |  _ \| |    
     *                             / __| | | | |_) | |    
     *                            | (__| |_| |  _ <| |___ 
     *                             \___|\___/|_| \_\_____|
     *
     *  The contents of this file are subject to the Mozilla Public License
     *  Version 1.0 (the "License"); you may not use this file except in
     *  compliance with the License. You may obtain a copy of the License at
     *  http://www.mozilla.org/MPL/
     *
     *  Software distributed under the License is distributed on an "AS IS"
     *  basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
     *  License for the specific language governing rights and limitations
     *  under the License.
     *
     *  The Original Code is Curl.
     *
     *  The Initial Developer of the Original Code is Daniel Stenberg.
     *
     *  Portions created by the Initial Developer are Copyright (C) 1998.
     *  All Rights Reserved.
     *
     * ------------------------------------------------------------
     * Main author:
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
     * - Daniel Stenberg <daniel@haxx.se>
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
     *
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
     * 	http://curl.haxx.se
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
     *
     * $Source$
     * $Revision$
     * $Date$
     * $Author$
     * $State$
     * $Locker$
     *
     * ------------------------------------------------------------
     ****************************************************************************/
    
    
    #include "setup.h"
    
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <ctype.h>
    #include <errno.h>
    
    #ifdef HAVE_UNISTD_H
    #include <unistd.h>
    #endif
    #ifdef HAVE_SYS_SELECT_H
    #include <sys/select.h>
    #endif
    
    #if defined(WIN32) && !defined(__GNUC__) || defined(__MINGW32__)
    #include <winsock.h>
    #else /* some kind of unix */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #ifdef HAVE_SYS_SOCKET_H
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #include <sys/socket.h>
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #endif
    #include <sys/types.h>
    
    #ifdef HAVE_NETINET_IN_H
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #include <netinet/in.h>
    
    #endif
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #ifdef HAVE_ARPA_INET_H
    #include <arpa/inet.h>
    #endif
    #include <sys/utsname.h>
    
    #ifdef HAVE_NETDB_H
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #include <netdb.h>
    #endif
    
    #endif
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    #if defined(WIN32) && defined(__GNUC__) || defined(__MINGW32__)
    #include <errno.h>
    #endif
    
    #include <curl/curl.h>
    #include "urldata.h"
    #include "sendf.h"
    
    #include "if2ip.h"
    #include "hostip.h"
    #include "progress.h"
    #include "download.h"
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #include "escape.h"
    
    #include "http.h" /* for HTTP proxy tunnel stuff */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
    #ifdef KRB4
    #include "security.h"
    #endif
    
    /* The last #include file should be: */
    #ifdef MALLOCDEBUG
    #include "memdebug.h"
    #endif
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    /* returns last node in linked list */
    static struct curl_slist *slist_get_last(struct curl_slist *list)
    {
    	struct curl_slist	*item;
    
    	/* if caller passed us a NULL, return now */
    	if (!list)
    		return NULL;
    
    	/* loop through to find the last item */
    	item = list;
    	while (item->next) {
    		item = item->next;
    	}
    	return item;
    }
    
    /* append a struct to the linked list. It always retunrs the address of the
     * first record, so that you can sure this function as an initialization
     * function as well as an append function. If you find this bothersome,
     * then simply create a separate _init function and call it appropriately from
     * within the proram. */
    struct curl_slist *curl_slist_append(struct curl_slist *list, char *data)
    {
    	struct curl_slist	*last;
    	struct curl_slist	*new_item;
    
    	new_item = (struct curl_slist *) malloc(sizeof(struct curl_slist));
    	if (new_item) {
    		new_item->next = NULL;
    		new_item->data = strdup(data);
    	}
    	else {
    		fprintf(stderr, "Cannot allocate memory for QUOTE list.\n");
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    	}
    
    	if (list) {
    		last = slist_get_last(list);
    		last->next = new_item;
    		return list;
    	}
    
    	/* if this is the first item, then new_item *is* the list */
    	return new_item;
    }
    
    /* be nice and clean up resources */
    void curl_slist_free_all(struct curl_slist *list)
    {
    	struct curl_slist	*next;
    	struct curl_slist	*item;
    
    	if (!list)
    		return;
    
    	item = list;
    	do {
    		next = item->next;
    		
    		if (item->data) {
    			free(item->data);
    		}
    		free(item);
    		item = next;
    	} while (next);
    }
    
    
    
    static CURLcode AllowServerConnect(struct UrlData *data,
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
                                       int sock)
    {
      fd_set rdset;
      struct timeval dt;
      
      FD_ZERO(&rdset);
    
      FD_SET(sock, &rdset);
    
      /* we give the server 10 seconds to connect to us */
      dt.tv_sec = 10;
      dt.tv_usec = 0;
    
      switch ( select(sock+1, &rdset, NULL, NULL, &dt)) {
      case -1: /* error */
        /* let's die here */
        failf(data, "Error while waiting for server connect");
    
        return CURLE_FTP_PORT_FAILED;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      case 0:  /* timeout */
        /* let's die here */
        failf(data, "Timeout while waiting for server connect");
    
        return CURLE_FTP_PORT_FAILED;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      default:
        /* we have received data here */
        {
          int s;
          size_t size = sizeof(struct sockaddr_in);
          struct sockaddr_in add;
    
          getsockname(sock, (struct sockaddr *) &add, (int *)&size);
          s=accept(sock, (struct sockaddr *) &add, (int *)&size);
    
          if( -1 == s) {
    	/* DIE! */
    	failf(data, "Error accept()ing server connect");
    
    	return CURLE_FTP_PORT_FAILED;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          }
          infof(data, "Connection accepted from server\n");
    
          data->secondarysocket = s;
        }
        break;
      }
    
      return CURLE_OK;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    }
    
    
    /* --- parse FTP server responses --- */
    
    #define lastline(line) (isdigit((int)line[0]) && isdigit((int)line[1]) && \
    			isdigit((int)line[2]) && (' ' == line[3]))
    
    
    int GetLastResponse(int sockfd, char *buf,
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    {
      int nread;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      char *ptr;
    
      int timeout = 3600; /* in seconds */
      struct timeval interval;
      fd_set rkeepfd;
      fd_set readfd;
      struct UrlData *data = conn->data;
    
    #define SELECT_OK      0
    #define SELECT_ERROR   1
    #define SELECT_TIMEOUT 2
      int error = SELECT_OK;
    
      if(data->timeout) {
        /* if timeout is requested, find out how much remaining time we have */
        timeout = data->timeout - /* timeout time */
          (tvlong(tvnow()) - tvlong(conn->now)); /* spent time */
        if(timeout <=0 ) {
          failf(data, "Transfer aborted due to timeout");
          return -SELECT_TIMEOUT; /* already too little time */
        }
      }
    
      FD_ZERO (&readfd);		/* clear it */
      FD_SET (sockfd, &readfd);     /* read socket */
    
      /* get this in a backup variable to be able to restore it on each lap in the
         select() loop */
      rkeepfd = readfd;
    
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      do {
        ptr=buf;
    
        /* get us a full line, terminated with a newline */
    
        nread=0;
        keepon=TRUE;
        while((nread<BUFSIZE) && (keepon && !error)) {
          readfd = rkeepfd;		   /* set every lap */
          interval.tv_sec = timeout;
          interval.tv_usec = 0;
    
          switch (select (sockfd+1, &readfd, NULL, NULL, &interval)) {
          case -1: /* select() error, stop reading */
            error = SELECT_ERROR;
            failf(data, "Transfer aborted due to select() error");
            break;
          case 0: /* timeout */
            error = SELECT_TIMEOUT;
            infof(data, "Transfer aborted due to timeout\n");
            failf(data, "Transfer aborted due to timeout");
            break;
          default:
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #ifdef USE_SSLEAY
    
            if (data->ssl.use) {
              keepon = SSL_read(data->ssl.handle, ptr, 1);
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #endif
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #ifdef USE_SSLEAY
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #endif /* USE_SSLEAY */
    
    
            if ((*ptr == '\n') || (*ptr == '\r'))
              keepon = FALSE;
          }
          if(keepon) {
            nread++;
            ptr++;
          }
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        }
        *ptr=0; /* zero terminate */
    
    
    #if KRB4
        { /* handle the security-oriented responses 6xx ***/
          /* FIXME: some errorchecking perhaps... ***/
          if(strncmp(buf, "631", 3) == 0)
            sec_read_msg(conn, buf, prot_safe);
          else if(strncmp(buf, "632", 3) == 0)
            sec_read_msg(conn, buf, prot_private);
          else if(strncmp(buf, "633", 3) == 0)
            sec_read_msg(conn, buf, prot_confidential);
          nread = strlen(buf);
        }
    #endif
    
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          fputs("< ", data->err);
          fwrite(buf, 1, nread, data->err);
          fputs("\n", data->err);
        }
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    	  (nread<4 || !lastline(buf)) );
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      return nread;
    }
    
    /* -- who are we? -- */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    char *getmyhost(char *buf, int buf_size)
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    {
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #if defined(HAVE_GETHOSTNAME)
      gethostname(buf, buf_size);
    #elif defined(HAVE_UNAME)
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      struct utsname ugnm;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      strncpy(buf, uname(&ugnm) < 0 ? "localhost" : ugnm.nodename, buf_size - 1);
      buf[buf_size - 1] = '\0';
    #else
    
      /* We have no means of finding the local host name! */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      strncpy(buf, "localhost", buf_size);
      buf[buf_size - 1] = '\0';
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #endif
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      return buf;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    }
    
    #if 0
    /*
     * URLfix()
     *
     * This function returns a string converted FROM the input URL format to a
     * format that is more likely usable for the remote server. That is, all
     * special characters (found as %XX-codes) will be eascaped with \<letter>.
     */
    
    static char *URLfix(char *string)
    {
      /* The length of the new string can't be longer than twice the original
         string, if all letters are '+'... */
      int alloc = strlen(string)*2;
      char *ns = malloc(alloc);
      unsigned char in;
      int index=0;
      int hex;
       
      while(*string) {
        in = *string;
        switch(in) {
        case '+':
          ns[index++] = '\\';
          ns[index++] = ' ';
          string++;
          continue;
    
        case '%':
          /* encoded part */
          if(sscanf(string+1, "%02X", &hex)) {
            ns[index++] = '\\';
            ns[index++] = hex;
            string+=3;
            continue;
          }
          /* FALLTHROUGH */
        default:
          ns[index++] = in;
          string++;
        }
      }
      ns[index]=0; /* terminate it */
      return ns;
    }
    #endif
    
    
    /* ftp_connect() should do everything that is to be considered a part
       of the connection phase. */
    CURLcode ftp_connect(struct connectdata *conn)
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    {
      /* this is FTP and no proxy */
    
      struct UrlData *data=conn->data;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      char *buf = data->buffer; /* this is our buffer */
    
      struct FTP *ftp;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
      myalarm(0); /* switch off the alarm stuff */
    
    
      ftp = (struct FTP *)malloc(sizeof(struct FTP));
      if(!ftp)
        return CURLE_OUT_OF_MEMORY;
    
      memset(ftp, 0, sizeof(struct FTP));
      data->proto.ftp = ftp;
    
      /* get some initial data into the ftp struct */
      ftp->bytecountp = &conn->bytecount;
      ftp->user = data->user;
      ftp->passwd = data->passwd;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
      if (data->bits.tunnel_thru_httpproxy) {
        /* We want "seamless" FTP operations through HTTP proxy tunnel */
    
        result = GetHTTPProxyTunnel(data, data->firstsocket,
                                    data->hostname, data->remote_port);
    
        if(CURLE_OK != result)
          return result;
      }
    
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      /* The first thing we do is wait for the "220*" line: */
    
      nread = GetLastResponse(data->firstsocket, buf, conn);
      if(nread < 0)
        return CURLE_OPERATION_TIMEOUTED;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      if(strncmp(buf, "220", 3)) {
        failf(data, "This doesn't seem like a nice ftp-server response");
    
        return CURLE_FTP_WEIRD_SERVER_REPLY;
    
    #ifdef KRB4
      /* if not anonymous login, try a secure login */
      if(data->bits.krb4) {
    
        /* request data protection level (default is 'clear') */
        sec_request_prot(conn, "private");
    
        /* We set private first as default, in case the line below fails to
           set a valid level */
        sec_request_prot(conn, data->krb4_level);
    
        data->cmdchannel = fdopen(data->firstsocket, "w");
    
        if(sec_login(conn) != 0)
          infof(data, "Logging in with password in cleartext!\n");
        else
          infof(data, "Authentication successful\n");
      }
    #endif
      
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      /* send USER */
    
      ftpsendf(data->firstsocket, conn, "USER %s", ftp->user);
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
      /* wait for feedback */
    
      nread = GetLastResponse(data->firstsocket, buf, conn);
      if(nread < 0)
        return CURLE_OPERATION_TIMEOUTED;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
      if(!strncmp(buf, "530", 3)) {
        /* 530 User ... access denied
           (the server denies to log the specified user) */
        failf(data, "Access denied: %s", &buf[4]);
    
        return CURLE_FTP_ACCESS_DENIED;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      }
      else if(!strncmp(buf, "331", 3)) {
        /* 331 Password required for ...
           (the server requires to send the user's password too) */
    
        ftpsendf(data->firstsocket, conn, "PASS %s", ftp->passwd);
    
        nread = GetLastResponse(data->firstsocket, buf, conn);
        if(nread < 0)
          return CURLE_OPERATION_TIMEOUTED;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
        if(!strncmp(buf, "530", 3)) {
          /* 530 Login incorrect.
             (the username and/or the password are incorrect) */
          failf(data, "the username and/or the password are incorrect");
    
          return CURLE_FTP_USER_PASSWORD_INCORRECT;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        }
        else if(!strncmp(buf, "230", 3)) {
          /* 230 User ... logged in.
             (user successfully logged in) */
            
          infof(data, "We have successfully logged in\n");
        }
        else {
          failf(data, "Odd return code after PASS");
    
          return CURLE_FTP_WEIRD_PASS_REPLY;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        }
      }
    
      else if(/*! strncmp(buf, "230", 3)***/ buf[0] == '2') {
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        /* 230 User ... logged in.
           (the user logged in without password) */
        infof(data, "We have successfully logged in\n");
    
    #ifdef KRB4
    	/* we are logged in (with Kerberos)
    	 * now set the requested protection level
    	 */
        if(conn->sec_complete)
          sec_set_protection_level(conn);
    
        /* we may need to issue a KAUTH here to have access to the files
         * do it if user supplied a password
         */
        if(conn->data->passwd && *conn->data->passwd)
          krb_kauth(conn);
    #endif
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      }
      else {
        failf(data, "Odd return code after USER");
    
        return CURLE_FTP_WEIRD_USER_REPLY;
      }
    
      return CURLE_OK;
    }
    
    
    /* argument is already checked for validity */
    CURLcode ftp_done(struct connectdata *conn)
    {
      struct UrlData *data = conn->data;
      struct FTP *ftp = data->proto.ftp;
      size_t nread;
      char *buf = data->buffer; /* this is our buffer */
      struct curl_slist *qitem; /* QUOTE item */
    
      if(data->bits.upload) {
        if((-1 != data->infilesize) && (data->infilesize != *ftp->bytecountp)) {
          failf(data, "Wrote only partial file (%d out of %d bytes)",
                *ftp->bytecountp, data->infilesize);
          return CURLE_PARTIAL_FILE;
        }
      }
      else {
        if((-1 != conn->size) && (conn->size != *ftp->bytecountp) &&
           (data->maxdownload != *ftp->bytecountp)) {
          failf(data, "Received only partial file");
          return CURLE_PARTIAL_FILE;
        }
    
        else if(!data->bits.no_body && (0 == *ftp->bytecountp)) {
    
          failf(data, "No data was received!");
          return CURLE_FTP_COULDNT_RETR_FILE;
        }
      }
    
    #ifdef KRB4
      sec_fflush_fd(conn, data->secondarysocket);
    #endif
    
      /* shut down the socket to inform the server we're done */
      sclose(data->secondarysocket);
      data->secondarysocket = -1;
    
    
      if(!data->bits.no_body) {  
        /* now let's see what the server says about the transfer we
           just performed: */
        nread = GetLastResponse(data->firstsocket, buf, conn);
        if(nread < 0)
          return CURLE_OPERATION_TIMEOUTED;
    
        /* 226 Transfer complete, 250 Requested file action okay, completed. */
        if(!strncmp(buf, "226", 3) && !strncmp(buf, "250", 3)) {
          failf(data, "%s", buf+4);
          return CURLE_FTP_WRITE_ERROR;
        }
    
      }
    
      /* Send any post-transfer QUOTE strings? */
      if(data->postquote) {
        qitem = data->postquote;
        /* Send all QUOTE strings in same order as on command-line */
        while (qitem) {
          /* Send string */
          if (qitem->data) {
    
            ftpsendf(data->firstsocket, conn, "%s", qitem->data);
    
            nread = GetLastResponse(data->firstsocket, buf, conn);
            if(nread < 0)
              return CURLE_OPERATION_TIMEOUTED;
    
    
            if (buf[0] != '2') {
              failf(data, "QUOT string not accepted: %s",
                    qitem->data);
              return CURLE_FTP_QUOTE_ERROR;
            }
          }
          qitem = qitem->next;
        }
    
      free(ftp);
      data->proto.ftp=NULL; /* it is gone */
    
    
      return CURLE_OK;
    }
    
    
    
    static
    CURLcode _ftp(struct connectdata *conn)
    {
      /* this is FTP and no proxy */
      size_t nread;
      CURLcode result;
      struct UrlData *data=conn->data;
      char *buf = data->buffer; /* this is our buffer */
      /* for the ftp PORT mode */
      int portsock=-1;
      struct sockaddr_in serv_addr;
    
      char hostent_buf[8192];
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #if defined (HAVE_INET_NTOA_R)
      char ntoa_buf[64];
    #endif
    
    
      struct curl_slist *qitem; /* QUOTE item */
      /* the ftp struct is already inited in ftp_connect() */
      struct FTP *ftp = data->proto.ftp;
    
      long *bytecountp = ftp->bytecountp;
    
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      /* Send any QUOTE strings? */
      if(data->quote) {
        qitem = data->quote;
        /* Send all QUOTE strings in same order as on command-line */
        while (qitem) {
          /* Send string */
          if (qitem->data) {
    
            ftpsendf(data->firstsocket, conn, "%s", qitem->data);
    
    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 (buf[0] != '2') {
              failf(data, "QUOT string not accepted: %s",
                    qitem->data);
    
              return CURLE_FTP_QUOTE_ERROR;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
            }
          }
          qitem = qitem->next;
        }
      }
    
    
      /* change directory first! */
      if(ftp->dir && ftp->dir[0]) {
    
        ftpsendf(data->firstsocket, conn, "CWD %s", ftp->dir);
    
        nread = GetLastResponse(data->firstsocket, buf, conn);
        if(nread < 0)
          return CURLE_OPERATION_TIMEOUTED;
    
        if(strncmp(buf, "250", 3)) {
          failf(data, "Couldn't change to directory %s", ftp->dir);
          return CURLE_FTP_ACCESS_DENIED;
        }
      }
    
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      /* If we have selected NOBODY, it means that we only want file information.
         Which in FTP can't be much more than the file size! */
    
      if(data->bits.no_body) {
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        /* 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! */
        int filesize;
    
    
        /* Some servers return different sizes for different modes, and thus we
           must set the proper type before we check the size */
        ftpsendf(data->firstsocket, conn, "TYPE %s",
                 (data->bits.ftp_ascii)?"A":"I");
    
        nread = GetLastResponse(data->firstsocket, buf, conn);
        if(nread < 0)
          return CURLE_OPERATION_TIMEOUTED;
    
        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;
        }
    
    
        ftpsendf(data->firstsocket, conn, "SIZE %s", 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)) {
          failf(data, "Couldn't get file size: %s", buf+4);
    
          return CURLE_FTP_COULDNT_GET_SIZE;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        }
        /* get the size from the ascii string: */
        filesize = atoi(buf+4);
    
        sprintf(buf, "Content-Length: %d\n", filesize);
    
        if(strlen(buf) != data->fwrite(buf, 1, strlen(buf), data->out)) {
          failf (data, "Failed writing output");
    
          return CURLE_WRITE_ERROR;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        }
        if(data->writeheader) {
          /* the header is requested to be written to this file */
    
          if(strlen(buf) != data->fwrite (buf, 1, strlen(buf),
                                          data->writeheader)) {
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
            failf (data, "Failed writing output");
    
            return CURLE_WRITE_ERROR;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          }
        }
    
        return CURLE_OK;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      }
    
      /* We have chosen to use the PORT command */
    
      if(data->bits.ftp_use_port) {
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        struct sockaddr_in sa;
        struct hostent *h=NULL;
    
        char *hostdataptr=NULL;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        size_t size;
        unsigned short porttouse;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        char myhost[256] = "";
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
        if(data->ftpport) {
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          if(if2ip(data->ftpport, myhost, sizeof(myhost))) {
    
            h = GetHost(data, myhost, &hostdataptr);
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          }
          else {
            if(strlen(data->ftpport)>1)
    
              h = GetHost(data, data->ftpport, &hostdataptr);
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
            if(h)
    
              strcpy(myhost, data->ftpport); /* buffer overflow risk */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          }
        }
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        if(! *myhost) {
    
          h=GetHost(data, getmyhost(myhost, sizeof(myhost)), &hostdataptr);
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        }
        infof(data, "We connect from %s\n", myhost);
    
        if ( h ) {
          if( (portsock = socket(AF_INET, SOCK_STREAM, 0)) >= 0 ) {
            memset((char *)&sa, 0, sizeof(sa));
            memcpy((char *)&sa.sin_addr,
                   h->h_addr,
                   h->h_length);
            sa.sin_family = AF_INET;
            sa.sin_addr.s_addr = INADDR_ANY;
            sa.sin_port = 0;
            size = sizeof(sa);
    
            if(bind(portsock, (struct sockaddr *)&sa, size) >= 0) {
              /* we succeeded to bind */
              struct sockaddr_in add;
              size = sizeof(add);
    
              if(getsockname(portsock, (struct sockaddr *) &add,
                             (int *)&size)<0) {
                failf(data, "getsockname() failed");
    
                return CURLE_FTP_PORT_FAILED;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
              }
              porttouse = ntohs(add.sin_port);
    
              if ( listen(portsock, 1) < 0 ) {
                failf(data, "listen(2) failed on socket");
    
                free(hostdataptr);
    
                return CURLE_FTP_PORT_FAILED;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
              }
            }
            else {
              failf(data, "bind(2) failed on socket");
    
              free(hostdataptr);
    
              return CURLE_FTP_PORT_FAILED;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
            }
          }
          else {
            failf(data, "socket(2) failed (%s)");
    
            free(hostdataptr);
    
            return CURLE_FTP_PORT_FAILED;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          }
    
          if(hostdataptr)
            /* free the memory used for name lookup */
            free(hostdataptr);
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        }
        else {
          failf(data, "could't find my own IP address (%s)", myhost);
    
          return CURLE_FTP_PORT_FAILED;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        }
        {
          struct in_addr in;
          unsigned short ip[5];
          (void) memcpy(&in.s_addr, *h->h_addr_list, sizeof (in.s_addr));
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #if defined (HAVE_INET_NTOA_R)
    
          /* ignore the return code from inet_ntoa_r() as it is int or
             char * depending on system */
          inet_ntoa_r(in, ntoa_buf, sizeof(ntoa_buf));
          sscanf( ntoa_buf, "%hu.%hu.%hu.%hu",
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
                  &ip[0], &ip[1], &ip[2], &ip[3]);
    #else
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          sscanf( inet_ntoa(in), "%hu.%hu.%hu.%hu",
                  &ip[0], &ip[1], &ip[2], &ip[3]);
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #endif
    
          ftpsendf(data->firstsocket, conn, "PORT %d,%d,%d,%d,%d,%d",
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
                ip[0], ip[1], ip[2], ip[3],
                porttouse >> 8,
                porttouse & 255);
        }
    
    
        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, "Server does not grok PORT, try without it!");
    
          return CURLE_FTP_PORT_FAILED;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        }     
      }
      else { /* we use the PASV command */
    
    
        ftpsendf(data->firstsocket, conn, "PASV");
    
    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, "227", 3)) {
          failf(data, "Odd return code after PASV");
    
          return CURLE_FTP_WEIRD_PASV_REPLY;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
        }
        else {
          int ip[4];
          int port[2];
    
          unsigned short newport; /* remote port, not necessary the local one */
          unsigned short connectport; /* the local port connect() should use! */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          char newhost[32];
          struct hostent *he;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          char *str=buf,*ip_addr;
    
          char *hostdataptr=NULL;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
          /*
           * New 227-parser June 3rd 1999.
           * It now scans for a sequence of six comma-separated numbers and
           * will take them as IP+port indicators.
           *
           * Found reply-strings include:
           * "227 Entering Passive Mode (127,0,0,1,4,51)"
           * "227 Data transfer will passively listen to 127,0,0,1,4,51"
           * "227 Entering passive mode. 127,0,0,1,4,51"
           */
          
          while(*str) {
    	 if (6 == sscanf(str, "%d,%d,%d,%d,%d,%d",
    			 &ip[0], &ip[1], &ip[2], &ip[3],
    			 &port[0], &port[1]))
    	    break;
    	 str++;
          }
          if(!*str) {
    	 failf(data, "Couldn't interpret this 227-reply: %s", buf);
    
    	 return CURLE_FTP_WEIRD_227_FORMAT;
    
          sprintf(newhost, "%d.%d.%d.%d", ip[0], ip[1], ip[2], ip[3]);
          newport = (port[0]<<8) + port[1];
    
          if(data->bits.httpproxy) {
            /*
             * This is a tunnel through a http proxy and we need to connect to the
             * proxy again here. We already have the name info for it since the
             * previous lookup.
             */
            he = conn->hp;
    
            connectport = data->port; /* we connect to the proxy's port */
    
          }
          else {
            /* normal, direct, ftp connection */
    
            he = GetHost(data, newhost, &hostdataptr);
    
            if(!he) {
              failf(data, "Can't resolve new host %s", newhost);
              return CURLE_FTP_CANT_GET_HOST;
            }
    
            connectport = newport; /* we connect to the remote port */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    	
          data->secondarysocket = socket(AF_INET, SOCK_STREAM, 0);
    
          memset((char *) &serv_addr, '\0', sizeof(serv_addr));
          memcpy((char *)&(serv_addr.sin_addr), he->h_addr, he->h_length);
          serv_addr.sin_family = he->h_addrtype;
    
          serv_addr.sin_port = htons(connectport);
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
          if(data->bits.verbose) {
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
            struct in_addr in;
            struct hostent * answer;
    
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
            unsigned long address;
    
    # if defined(HAVE_GETHOSTBYADDR_R)
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
            int h_errnop;
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
            address = inet_addr(newhost);
    
    # ifdef HAVE_GETHOSTBYADDR_R
    
    #  ifdef HAVE_GETHOSTBYADDR_R_5
            /* AIX, Digital Unix style:
               extern int gethostbyaddr_r(char *addr, size_t len, int type,
               struct hostent *htent, struct hostent_data *ht_data); */
    
    
            /* Fred Noz helped me try this out, now it at least compiles! */
    
    
            if(gethostbyaddr_r((char *) &address,
                               sizeof(address), AF_INET,
                               (struct hostent *)hostent_buf,
    
                               hostent_buf + sizeof(*answer)))
    
               answer=NULL;
                               
    #  endif
    #  ifdef HAVE_GETHOSTBYADDR_R_7
    
            /* Solaris and IRIX */
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
            answer = gethostbyaddr_r((char *) &address, sizeof(address), AF_INET,
                                     (struct hostent *)hostent_buf,
                                     hostent_buf + sizeof(*answer),
                                     sizeof(hostent_buf) - sizeof(*answer),
                                     &h_errnop);
    
            /* Linux style */
            if(gethostbyaddr_r((char *) &address, sizeof(address), AF_INET,
                               (struct hostent *)hostent_buf,
                               hostent_buf + sizeof(*answer),
                               sizeof(hostent_buf) - sizeof(*answer),
                               &answer,
                               &h_errnop))
               answer=NULL; /* error */
    #  endif
            
    # else
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
            answer = gethostbyaddr((char *) &address, sizeof(address), AF_INET);
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #else
            answer = NULL;
    #endif
            (void) memcpy(&in.s_addr, *he->h_addr_list, sizeof (in.s_addr));
            infof(data, "Connecting to %s (%s) port %u\n",
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
                  answer?answer->h_name:newhost,
    #if defined(HAVE_INET_NTOA_R)
    
                  inet_ntoa_r(in, ip_addr=ntoa_buf, sizeof(ntoa_buf)),
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #else
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
                  ip_addr = inet_ntoa(in),
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    #endif
    
                  connectport);
    
          if(hostdataptr)
            free(hostdataptr);
    
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
          if (connect(data->secondarysocket, (struct sockaddr *) &serv_addr,
                      sizeof(serv_addr)) < 0) {
            switch(errno) {
    #ifdef ECONNREFUSED
              /* this should be made nicer */
            case ECONNREFUSED:
              failf(data, "Connection refused by ftp server");
              break;
    #endif
    #ifdef EINTR
            case EINTR:
              failf(data, "Connection timeouted to ftp server");
              break;
    #endif
            default:
              failf(data, "Can't connect to ftp server");
              break;
            }
    
            return CURLE_FTP_CANT_RECONNECT;
    
          if (data->bits.tunnel_thru_httpproxy) {
            /* We want "seamless" FTP operations through HTTP proxy tunnel */
    
            result = GetHTTPProxyTunnel(data, data->secondarysocket,
                                        newhost, newport);
    
            if(CURLE_OK != result)
              return result;
          }
        }
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
      }
      /* we have the (new) data connection ready */
    
      infof(data, "Connected the data stream!\n");
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
    
      if(data->bits.upload) {
    
    Daniel Stenberg's avatar
    Daniel Stenberg committed
    
        /* Set type to binary (unless specified ASCII) */
    
        ftpsendf(data->firstsocket, conn, "TYPE %s",
    
              (data->bits.ftp_ascii)?"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) {
          /* we're about to continue the uploading of a file */