(root)/
libxml2-2.12.3/
nanoftp.c
       1  /*
       2   * nanoftp.c: basic FTP client support
       3   *
       4   *  Reference: RFC 959
       5   */
       6  
       7  #ifdef TESTING
       8  #define STANDALONE
       9  #define HAVE_UNISTD_H
      10  #define HAVE_SYS_SOCKET_H
      11  #define HAVE_NETINET_IN_H
      12  #define HAVE_NETDB_H
      13  #define HAVE_SYS_TIME_H
      14  #endif /* TESTING */
      15  
      16  #define IN_LIBXML
      17  #include "libxml.h"
      18  
      19  #ifdef LIBXML_FTP_ENABLED
      20  #include <string.h>
      21  #include <stdlib.h>
      22  #include <errno.h>
      23  
      24  #ifdef HAVE_UNISTD_H
      25  #include <unistd.h>
      26  #elif defined (_WIN32)
      27  #include <io.h>
      28  #endif
      29  #ifdef HAVE_SYS_SOCKET_H
      30  #include <sys/socket.h>
      31  #endif
      32  #ifdef HAVE_NETINET_IN_H
      33  #include <netinet/in.h>
      34  #endif
      35  #ifdef HAVE_ARPA_INET_H
      36  #include <arpa/inet.h>
      37  #endif
      38  #ifdef HAVE_NETDB_H
      39  #include <netdb.h>
      40  #endif
      41  #ifdef HAVE_FCNTL_H
      42  #include <fcntl.h>
      43  #endif
      44  #ifdef HAVE_SYS_TIME_H
      45  #include <sys/time.h>
      46  #endif
      47  #ifdef HAVE_SYS_SELECT_H
      48  #include <sys/select.h>
      49  #endif
      50  #ifdef HAVE_SYS_SOCKET_H
      51  #include <sys/socket.h>
      52  #endif
      53  
      54  #include <libxml/xmlmemory.h>
      55  #include <libxml/parser.h>
      56  #include <libxml/xmlerror.h>
      57  #include <libxml/uri.h>
      58  #include <libxml/nanoftp.h>
      59  
      60  #include "private/error.h"
      61  #include "private/io.h"
      62  
      63  #if defined(_WIN32)
      64  #include <wsockcompat.h>
      65  #endif
      66  
      67  /**
      68   * A couple portability macros
      69   */
      70  #ifndef _WINSOCKAPI_
      71  #define closesocket(s) close(s)
      72  #endif
      73  
      74  #ifndef XML_SOCKLEN_T
      75  #define XML_SOCKLEN_T unsigned int
      76  #endif
      77  
      78  #define GETHOSTBYNAME_ARG_CAST (char *)
      79  #define SEND_ARG2_CAST (char *)
      80  
      81  #define FTP_COMMAND_OK		200
      82  #define FTP_SYNTAX_ERROR	500
      83  #define FTP_GET_PASSWD		331
      84  #define FTP_BUF_SIZE		1024
      85  
      86  #define XML_NANO_MAX_URLBUF	4096
      87  
      88  typedef struct xmlNanoFTPCtxt {
      89      char *protocol;	/* the protocol name */
      90      char *hostname;	/* the host name */
      91      int port;		/* the port */
      92      char *path;		/* the path within the URL */
      93      char *user;		/* user string */
      94      char *passwd;	/* passwd string */
      95  #ifdef SUPPORT_IP6
      96      struct sockaddr_storage ftpAddr; /* this is large enough to hold IPv6 address*/
      97  #else
      98      struct sockaddr_in ftpAddr; /* the socket address struct */
      99  #endif
     100      int passive;	/* currently we support only passive !!! */
     101      SOCKET controlFd;	/* the file descriptor for the control socket */
     102      SOCKET dataFd;	/* the file descriptor for the data socket */
     103      int state;		/* WRITE / READ / CLOSED */
     104      int returnValue;	/* the protocol return value */
     105      /* buffer for data received from the control connection */
     106      char controlBuf[FTP_BUF_SIZE + 1];
     107      int controlBufIndex;
     108      int controlBufUsed;
     109      int controlBufAnswer;
     110  } xmlNanoFTPCtxt, *xmlNanoFTPCtxtPtr;
     111  
     112  static int initialized = 0;
     113  static char *proxy = NULL;	/* the proxy name if any */
     114  static int proxyPort = 0;	/* the proxy port if any */
     115  static char *proxyUser = NULL;	/* user for proxy authentication */
     116  static char *proxyPasswd = NULL;/* passwd for proxy authentication */
     117  static int proxyType = 0;	/* uses TYPE or a@b ? */
     118  
     119  #ifdef SUPPORT_IP6
     120  static
     121  int have_ipv6(void) {
     122      int s;
     123  
     124      s = socket (AF_INET6, SOCK_STREAM, 0);
     125      if (s != -1) {
     126  	close (s);
     127  	return (1);
     128      }
     129      return (0);
     130  }
     131  #endif
     132  
     133  /**
     134   * xmlFTPErrMemory:
     135   * @extra:  extra information
     136   *
     137   * Handle an out of memory condition
     138   */
     139  static void
     140  xmlFTPErrMemory(const char *extra)
     141  {
     142      __xmlSimpleError(XML_FROM_FTP, XML_ERR_NO_MEMORY, NULL, NULL, extra);
     143  }
     144  
     145  /**
     146   * xmlNanoFTPInit:
     147   *
     148   * Initialize the FTP protocol layer.
     149   * Currently it just checks for proxy information,
     150   * and get the hostname
     151   */
     152  
     153  void
     154  xmlNanoFTPInit(void) {
     155      const char *env;
     156  #ifdef _WINSOCKAPI_
     157      WSADATA wsaData;
     158  #endif
     159  
     160      if (initialized)
     161  	return;
     162  
     163  #ifdef _WINSOCKAPI_
     164      if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0)
     165  	return;
     166  #endif
     167  
     168      proxyPort = 21;
     169      env = getenv("no_proxy");
     170      if (env && ((env[0] == '*' ) && (env[1] == 0)))
     171  	return;
     172      env = getenv("ftp_proxy");
     173      if (env != NULL) {
     174  	xmlNanoFTPScanProxy(env);
     175      } else {
     176  	env = getenv("FTP_PROXY");
     177  	if (env != NULL) {
     178  	    xmlNanoFTPScanProxy(env);
     179  	}
     180      }
     181      env = getenv("ftp_proxy_user");
     182      if (env != NULL) {
     183  	proxyUser = xmlMemStrdup(env);
     184      }
     185      env = getenv("ftp_proxy_password");
     186      if (env != NULL) {
     187  	proxyPasswd = xmlMemStrdup(env);
     188      }
     189      initialized = 1;
     190  }
     191  
     192  /**
     193   * xmlNanoFTPCleanup:
     194   *
     195   * Cleanup the FTP protocol layer. This cleanup proxy information.
     196   */
     197  
     198  void
     199  xmlNanoFTPCleanup(void) {
     200      if (proxy != NULL) {
     201  	xmlFree(proxy);
     202  	proxy = NULL;
     203      }
     204      if (proxyUser != NULL) {
     205  	xmlFree(proxyUser);
     206  	proxyUser = NULL;
     207      }
     208      if (proxyPasswd != NULL) {
     209  	xmlFree(proxyPasswd);
     210  	proxyPasswd = NULL;
     211      }
     212  #ifdef _WINSOCKAPI_
     213      if (initialized)
     214  	WSACleanup();
     215  #endif
     216      initialized = 0;
     217  }
     218  
     219  /**
     220   * xmlNanoFTPProxy:
     221   * @host:  the proxy host name
     222   * @port:  the proxy port
     223   * @user:  the proxy user name
     224   * @passwd:  the proxy password
     225   * @type:  the type of proxy 1 for using SITE, 2 for USER a@b
     226   *
     227   * Setup the FTP proxy information.
     228   * This can also be done by using ftp_proxy ftp_proxy_user and
     229   * ftp_proxy_password environment variables.
     230   */
     231  
     232  void
     233  xmlNanoFTPProxy(const char *host, int port, const char *user,
     234  	        const char *passwd, int type) {
     235      if (proxy != NULL) {
     236  	xmlFree(proxy);
     237  	proxy = NULL;
     238      }
     239      if (proxyUser != NULL) {
     240  	xmlFree(proxyUser);
     241  	proxyUser = NULL;
     242      }
     243      if (proxyPasswd != NULL) {
     244  	xmlFree(proxyPasswd);
     245  	proxyPasswd = NULL;
     246      }
     247      if (host)
     248  	proxy = xmlMemStrdup(host);
     249      if (user)
     250  	proxyUser = xmlMemStrdup(user);
     251      if (passwd)
     252  	proxyPasswd = xmlMemStrdup(passwd);
     253      proxyPort = port;
     254      proxyType = type;
     255  }
     256  
     257  /**
     258   * xmlNanoFTPScanURL:
     259   * @ctx:  an FTP context
     260   * @URL:  The URL used to initialize the context
     261   *
     262   * (Re)Initialize an FTP context by parsing the URL and finding
     263   * the protocol host port and path it indicates.
     264   */
     265  
     266  static void
     267  xmlNanoFTPScanURL(void *ctx, const char *URL) {
     268      xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
     269      xmlURIPtr uri;
     270  
     271      /*
     272       * Clear any existing data from the context
     273       */
     274      if (ctxt->protocol != NULL) {
     275          xmlFree(ctxt->protocol);
     276  	ctxt->protocol = NULL;
     277      }
     278      if (ctxt->hostname != NULL) {
     279          xmlFree(ctxt->hostname);
     280  	ctxt->hostname = NULL;
     281      }
     282      if (ctxt->path != NULL) {
     283          xmlFree(ctxt->path);
     284  	ctxt->path = NULL;
     285      }
     286      if (URL == NULL) return;
     287  
     288      uri = xmlParseURIRaw(URL, 1);
     289      if (uri == NULL)
     290  	return;
     291  
     292      if ((uri->scheme == NULL) || (uri->server == NULL)) {
     293  	xmlFreeURI(uri);
     294  	return;
     295      }
     296  
     297      ctxt->protocol = xmlMemStrdup(uri->scheme);
     298      ctxt->hostname = xmlMemStrdup(uri->server);
     299      if (uri->path != NULL)
     300  	ctxt->path = xmlMemStrdup(uri->path);
     301      else
     302  	ctxt->path = xmlMemStrdup("/");
     303      if (uri->port != 0)
     304  	ctxt->port = uri->port;
     305  
     306      if (uri->user != NULL) {
     307  	char *cptr;
     308  	if ((cptr=strchr(uri->user, ':')) == NULL)
     309  	    ctxt->user = xmlMemStrdup(uri->user);
     310  	else {
     311  	    ctxt->user = (char *)xmlStrndup((xmlChar *)uri->user,
     312  			    (cptr - uri->user));
     313  	    ctxt->passwd = xmlMemStrdup(cptr+1);
     314  	}
     315      }
     316  
     317      xmlFreeURI(uri);
     318  
     319  }
     320  
     321  /**
     322   * xmlNanoFTPUpdateURL:
     323   * @ctx:  an FTP context
     324   * @URL:  The URL used to update the context
     325   *
     326   * Update an FTP context by parsing the URL and finding
     327   * new path it indicates. If there is an error in the
     328   * protocol, hostname, port or other information, the
     329   * error is raised. It indicates a new connection has to
     330   * be established.
     331   *
     332   * Returns 0 if Ok, -1 in case of error (other host).
     333   */
     334  
     335  int
     336  xmlNanoFTPUpdateURL(void *ctx, const char *URL) {
     337      xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
     338      xmlURIPtr uri;
     339  
     340      if (URL == NULL)
     341  	return(-1);
     342      if (ctxt == NULL)
     343  	return(-1);
     344      if (ctxt->protocol == NULL)
     345  	return(-1);
     346      if (ctxt->hostname == NULL)
     347  	return(-1);
     348  
     349      uri = xmlParseURIRaw(URL, 1);
     350      if (uri == NULL)
     351  	return(-1);
     352  
     353      if ((uri->scheme == NULL) || (uri->server == NULL)) {
     354  	xmlFreeURI(uri);
     355  	return(-1);
     356      }
     357      if ((strcmp(ctxt->protocol, uri->scheme)) ||
     358  	(strcmp(ctxt->hostname, uri->server)) ||
     359  	((uri->port != 0) && (ctxt->port != uri->port))) {
     360  	xmlFreeURI(uri);
     361  	return(-1);
     362      }
     363  
     364      if (uri->port != 0)
     365  	ctxt->port = uri->port;
     366  
     367      if (ctxt->path != NULL) {
     368  	xmlFree(ctxt->path);
     369  	ctxt->path = NULL;
     370      }
     371  
     372      if (uri->path == NULL)
     373          ctxt->path = xmlMemStrdup("/");
     374      else
     375  	ctxt->path = xmlMemStrdup(uri->path);
     376  
     377      xmlFreeURI(uri);
     378  
     379      return(0);
     380  }
     381  
     382  /**
     383   * xmlNanoFTPScanProxy:
     384   * @URL:  The proxy URL used to initialize the proxy context
     385   *
     386   * (Re)Initialize the FTP Proxy context by parsing the URL and finding
     387   * the protocol host port it indicates.
     388   * Should be like ftp://myproxy/ or ftp://myproxy:3128/
     389   * A NULL URL cleans up proxy information.
     390   */
     391  
     392  void
     393  xmlNanoFTPScanProxy(const char *URL) {
     394      xmlURIPtr uri;
     395  
     396      if (proxy != NULL) {
     397          xmlFree(proxy);
     398  	proxy = NULL;
     399      }
     400      proxyPort = 0;
     401  
     402      if (URL == NULL) return;
     403  
     404      uri = xmlParseURIRaw(URL, 1);
     405      if ((uri == NULL) || (uri->scheme == NULL) ||
     406  	(strcmp(uri->scheme, "ftp")) || (uri->server == NULL)) {
     407  	__xmlIOErr(XML_FROM_FTP, XML_FTP_URL_SYNTAX, "Syntax Error\n");
     408  	if (uri != NULL)
     409  	    xmlFreeURI(uri);
     410  	return;
     411      }
     412  
     413      proxy = xmlMemStrdup(uri->server);
     414      if (uri->port != 0)
     415  	proxyPort = uri->port;
     416  
     417      xmlFreeURI(uri);
     418  }
     419  
     420  /**
     421   * xmlNanoFTPNewCtxt:
     422   * @URL:  The URL used to initialize the context
     423   *
     424   * Allocate and initialize a new FTP context.
     425   *
     426   * Returns an FTP context or NULL in case of error.
     427   */
     428  
     429  void*
     430  xmlNanoFTPNewCtxt(const char *URL) {
     431      xmlNanoFTPCtxtPtr ret;
     432      char *unescaped;
     433  
     434      ret = (xmlNanoFTPCtxtPtr) xmlMalloc(sizeof(xmlNanoFTPCtxt));
     435      if (ret == NULL) {
     436          xmlFTPErrMemory("allocating FTP context");
     437          return(NULL);
     438      }
     439  
     440      memset(ret, 0, sizeof(xmlNanoFTPCtxt));
     441      ret->port = 21;
     442      ret->passive = 1;
     443      ret->returnValue = 0;
     444      ret->controlBufIndex = 0;
     445      ret->controlBufUsed = 0;
     446      ret->controlFd = INVALID_SOCKET;
     447  
     448      unescaped = xmlURIUnescapeString(URL, 0, NULL);
     449      if (unescaped != NULL) {
     450  	xmlNanoFTPScanURL(ret, unescaped);
     451  	xmlFree(unescaped);
     452      } else if (URL != NULL)
     453  	xmlNanoFTPScanURL(ret, URL);
     454  
     455      return(ret);
     456  }
     457  
     458  /**
     459   * xmlNanoFTPFreeCtxt:
     460   * @ctx:  an FTP context
     461   *
     462   * Frees the context after closing the connection.
     463   */
     464  
     465  void
     466  xmlNanoFTPFreeCtxt(void * ctx) {
     467      xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
     468      if (ctxt == NULL) return;
     469      if (ctxt->hostname != NULL) xmlFree(ctxt->hostname);
     470      if (ctxt->protocol != NULL) xmlFree(ctxt->protocol);
     471      if (ctxt->path != NULL) xmlFree(ctxt->path);
     472      if (ctxt->user != NULL) xmlFree(ctxt->user);
     473      if (ctxt->passwd != NULL) xmlFree(ctxt->passwd);
     474      ctxt->passive = 1;
     475      if (ctxt->controlFd != INVALID_SOCKET) closesocket(ctxt->controlFd);
     476      ctxt->controlFd = INVALID_SOCKET;
     477      ctxt->controlBufIndex = -1;
     478      ctxt->controlBufUsed = -1;
     479      xmlFree(ctxt);
     480  }
     481  
     482  /**
     483   * xmlNanoFTPParseResponse:
     484   * @buf:  the buffer containing the response
     485   * @len:  the buffer length
     486   *
     487   * Parsing of the server answer, we just extract the code.
     488   *
     489   * returns 0 for errors
     490   *     +XXX for last line of response
     491   *     -XXX for response to be continued
     492   */
     493  static int
     494  xmlNanoFTPParseResponse(char *buf, int len) {
     495      int val = 0;
     496  
     497      if (len < 3) return(-1);
     498      if ((*buf >= '0') && (*buf <= '9'))
     499          val = val * 10 + (*buf - '0');
     500      else
     501          return(0);
     502      buf++;
     503      if ((*buf >= '0') && (*buf <= '9'))
     504          val = val * 10 + (*buf - '0');
     505      else
     506          return(0);
     507      buf++;
     508      if ((*buf >= '0') && (*buf <= '9'))
     509          val = val * 10 + (*buf - '0');
     510      else
     511          return(0);
     512      buf++;
     513      if (*buf == '-')
     514          return(-val);
     515      return(val);
     516  }
     517  
     518  /**
     519   * xmlNanoFTPGetMore:
     520   * @ctx:  an FTP context
     521   *
     522   * Read more information from the FTP control connection
     523   * Returns the number of bytes read, < 0 indicates an error
     524   */
     525  static int
     526  xmlNanoFTPGetMore(void *ctx) {
     527      xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
     528      int len;
     529      int size;
     530  
     531      if ((ctxt == NULL) || (ctxt->controlFd == INVALID_SOCKET)) return(-1);
     532  
     533      if ((ctxt->controlBufIndex < 0) || (ctxt->controlBufIndex > FTP_BUF_SIZE)) {
     534  	return(-1);
     535      }
     536  
     537      if ((ctxt->controlBufUsed < 0) || (ctxt->controlBufUsed > FTP_BUF_SIZE)) {
     538  	return(-1);
     539      }
     540      if (ctxt->controlBufIndex > ctxt->controlBufUsed) {
     541  	return(-1);
     542      }
     543  
     544      /*
     545       * First pack the control buffer
     546       */
     547      if (ctxt->controlBufIndex > 0) {
     548  	memmove(&ctxt->controlBuf[0], &ctxt->controlBuf[ctxt->controlBufIndex],
     549  		ctxt->controlBufUsed - ctxt->controlBufIndex);
     550  	ctxt->controlBufUsed -= ctxt->controlBufIndex;
     551  	ctxt->controlBufIndex = 0;
     552      }
     553      size = FTP_BUF_SIZE - ctxt->controlBufUsed;
     554      if (size == 0) {
     555  	return(0);
     556      }
     557  
     558      /*
     559       * Read the amount left on the control connection
     560       */
     561      if ((len = recv(ctxt->controlFd, &ctxt->controlBuf[ctxt->controlBufIndex],
     562  		    size, 0)) < 0) {
     563  	__xmlIOErr(XML_FROM_FTP, 0, "recv failed");
     564  	closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
     565          ctxt->controlFd = INVALID_SOCKET;
     566          return(-1);
     567      }
     568      ctxt->controlBufUsed += len;
     569      ctxt->controlBuf[ctxt->controlBufUsed] = 0;
     570  
     571      return(len);
     572  }
     573  
     574  /**
     575   * xmlNanoFTPReadResponse:
     576   * @ctx:  an FTP context
     577   *
     578   * Read the response from the FTP server after a command.
     579   * Returns the code number
     580   */
     581  static int
     582  xmlNanoFTPReadResponse(void *ctx) {
     583      xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
     584      char *ptr, *end;
     585      int len;
     586      int res = -1, cur = -1;
     587  
     588      if ((ctxt == NULL) || (ctxt->controlFd == INVALID_SOCKET)) return(-1);
     589  
     590  get_more:
     591      /*
     592       * Assumes everything up to controlBuf[controlBufIndex] has been read
     593       * and analyzed.
     594       */
     595      len = xmlNanoFTPGetMore(ctx);
     596      if (len < 0) {
     597          return(-1);
     598      }
     599      if ((ctxt->controlBufUsed == 0) && (len == 0)) {
     600          return(-1);
     601      }
     602      ptr = &ctxt->controlBuf[ctxt->controlBufIndex];
     603      end = &ctxt->controlBuf[ctxt->controlBufUsed];
     604  
     605      while (ptr < end) {
     606          cur = xmlNanoFTPParseResponse(ptr, end - ptr);
     607  	if (cur > 0) {
     608  	    /*
     609  	     * Successfully scanned the control code, scratch
     610  	     * till the end of the line, but keep the index to be
     611  	     * able to analyze the result if needed.
     612  	     */
     613  	    res = cur;
     614  	    ptr += 3;
     615  	    ctxt->controlBufAnswer = ptr - ctxt->controlBuf;
     616  	    while ((ptr < end) && (*ptr != '\n')) ptr++;
     617  	    if (*ptr == '\n') ptr++;
     618  	    if (*ptr == '\r') ptr++;
     619  	    break;
     620  	}
     621  	while ((ptr < end) && (*ptr != '\n')) ptr++;
     622  	if (ptr >= end) {
     623  	    ctxt->controlBufIndex = ctxt->controlBufUsed;
     624  	    goto get_more;
     625  	}
     626  	if (*ptr != '\r') ptr++;
     627      }
     628  
     629      if (res < 0) goto get_more;
     630      ctxt->controlBufIndex = ptr - ctxt->controlBuf;
     631  
     632      return(res / 100);
     633  }
     634  
     635  /**
     636   * xmlNanoFTPGetResponse:
     637   * @ctx:  an FTP context
     638   *
     639   * Get the response from the FTP server after a command.
     640   * Returns the code number
     641   */
     642  
     643  int
     644  xmlNanoFTPGetResponse(void *ctx) {
     645      int res;
     646  
     647      res = xmlNanoFTPReadResponse(ctx);
     648  
     649      return(res);
     650  }
     651  
     652  /**
     653   * xmlNanoFTPCheckResponse:
     654   * @ctx:  an FTP context
     655   *
     656   * Check if there is a response from the FTP server after a command.
     657   * Returns the code number, or 0
     658   */
     659  
     660  int
     661  xmlNanoFTPCheckResponse(void *ctx) {
     662      xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
     663      fd_set rfd;
     664      struct timeval tv;
     665  
     666      if ((ctxt == NULL) || (ctxt->controlFd == INVALID_SOCKET)) return(-1);
     667      tv.tv_sec = 0;
     668      tv.tv_usec = 0;
     669      FD_ZERO(&rfd);
     670      FD_SET(ctxt->controlFd, &rfd);
     671      switch(select(ctxt->controlFd + 1, &rfd, NULL, NULL, &tv)) {
     672  	case 0:
     673  	    return(0);
     674  	case -1:
     675  	    __xmlIOErr(XML_FROM_FTP, 0, "select");
     676  	    return(-1);
     677  
     678      }
     679  
     680      return(xmlNanoFTPReadResponse(ctx));
     681  }
     682  
     683  /**
     684   * Send the user authentication
     685   */
     686  
     687  static int
     688  xmlNanoFTPSendUser(void *ctx) {
     689      xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
     690      char buf[200];
     691      int len;
     692      int res;
     693  
     694      if (ctxt->user == NULL)
     695  	snprintf(buf, sizeof(buf), "USER anonymous\r\n");
     696      else
     697  	snprintf(buf, sizeof(buf), "USER %s\r\n", ctxt->user);
     698      buf[sizeof(buf) - 1] = 0;
     699      len = strlen(buf);
     700      res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
     701      if (res < 0) {
     702  	__xmlIOErr(XML_FROM_FTP, 0, "send failed");
     703  	return(res);
     704      }
     705      return(0);
     706  }
     707  
     708  /**
     709   * Send the password authentication
     710   */
     711  
     712  static int
     713  xmlNanoFTPSendPasswd(void *ctx) {
     714      xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
     715      char buf[200];
     716      int len;
     717      int res;
     718  
     719      if (ctxt->passwd == NULL)
     720  	snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
     721      else
     722  	snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd);
     723      buf[sizeof(buf) - 1] = 0;
     724      len = strlen(buf);
     725      res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
     726      if (res < 0) {
     727  	__xmlIOErr(XML_FROM_FTP, 0, "send failed");
     728  	return(res);
     729      }
     730      return(0);
     731  }
     732  
     733  /**
     734   * xmlNanoFTPQuit:
     735   * @ctx:  an FTP context
     736   *
     737   * Send a QUIT command to the server
     738   *
     739   * Returns -1 in case of error, 0 otherwise
     740   */
     741  
     742  
     743  int
     744  xmlNanoFTPQuit(void *ctx) {
     745      xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
     746      char buf[200];
     747      int len, res;
     748  
     749      if ((ctxt == NULL) || (ctxt->controlFd == INVALID_SOCKET)) return(-1);
     750  
     751      snprintf(buf, sizeof(buf), "QUIT\r\n");
     752      len = strlen(buf);
     753      res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
     754      if (res < 0) {
     755  	__xmlIOErr(XML_FROM_FTP, 0, "send failed");
     756  	return(res);
     757      }
     758      return(0);
     759  }
     760  
     761  /**
     762   * xmlNanoFTPConnect:
     763   * @ctx:  an FTP context
     764   *
     765   * Tries to open a control connection
     766   *
     767   * Returns -1 in case of error, 0 otherwise
     768   */
     769  
     770  int
     771  xmlNanoFTPConnect(void *ctx) {
     772      xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
     773      struct hostent *hp;
     774      int port;
     775      int res;
     776      int addrlen = sizeof (struct sockaddr_in);
     777  
     778      if (ctxt == NULL)
     779  	return(-1);
     780      if (ctxt->hostname == NULL)
     781  	return(-1);
     782  
     783      /*
     784       * do the blocking DNS query.
     785       */
     786      if (proxy) {
     787          port = proxyPort;
     788      } else {
     789  	port = ctxt->port;
     790      }
     791      if (port == 0)
     792  	port = 21;
     793  
     794      memset (&ctxt->ftpAddr, 0, sizeof(ctxt->ftpAddr));
     795  
     796  #ifdef SUPPORT_IP6
     797      if (have_ipv6 ()) {
     798  	struct addrinfo hints, *tmp, *result;
     799  
     800  	result = NULL;
     801  	memset (&hints, 0, sizeof(hints));
     802  	hints.ai_socktype = SOCK_STREAM;
     803  
     804  	if (proxy) {
     805  	    if (getaddrinfo (proxy, NULL, &hints, &result) != 0) {
     806  		__xmlIOErr(XML_FROM_FTP, 0, "getaddrinfo failed");
     807  		return (-1);
     808  	    }
     809  	}
     810  	else
     811  	    if (getaddrinfo (ctxt->hostname, NULL, &hints, &result) != 0) {
     812  		__xmlIOErr(XML_FROM_FTP, 0, "getaddrinfo failed");
     813  		return (-1);
     814  	    }
     815  
     816  	for (tmp = result; tmp; tmp = tmp->ai_next)
     817  	    if (tmp->ai_family == AF_INET || tmp->ai_family == AF_INET6)
     818  		break;
     819  
     820  	if (!tmp) {
     821  	    if (result)
     822  		freeaddrinfo (result);
     823  	    __xmlIOErr(XML_FROM_FTP, 0, "getaddrinfo failed");
     824  	    return (-1);
     825  	}
     826  	if ((size_t)tmp->ai_addrlen > sizeof(ctxt->ftpAddr)) {
     827  	    if (result)
     828  		freeaddrinfo (result);
     829  	    __xmlIOErr(XML_FROM_FTP, 0, "gethostbyname address mismatch");
     830  	    return (-1);
     831  	}
     832  	if (tmp->ai_family == AF_INET6) {
     833  	    memcpy (&ctxt->ftpAddr, tmp->ai_addr, tmp->ai_addrlen);
     834  	    ((struct sockaddr_in6 *) &ctxt->ftpAddr)->sin6_port = htons (port);
     835  	    ctxt->controlFd = socket (AF_INET6, SOCK_STREAM, 0);
     836  	}
     837  	else {
     838  	    memcpy (&ctxt->ftpAddr, tmp->ai_addr, tmp->ai_addrlen);
     839  	    ((struct sockaddr_in *) &ctxt->ftpAddr)->sin_port = htons (port);
     840  	    ctxt->controlFd = socket (AF_INET, SOCK_STREAM, 0);
     841  	}
     842  	addrlen = tmp->ai_addrlen;
     843  	freeaddrinfo (result);
     844      }
     845      else
     846  #endif
     847      {
     848  	if (proxy)
     849  	    hp = gethostbyname (GETHOSTBYNAME_ARG_CAST proxy);
     850  	else
     851  	    hp = gethostbyname (GETHOSTBYNAME_ARG_CAST ctxt->hostname);
     852  	if (hp == NULL) {
     853  	    __xmlIOErr(XML_FROM_FTP, 0, "gethostbyname failed");
     854  	    return (-1);
     855  	}
     856  	if ((unsigned int) hp->h_length >
     857  	    sizeof(((struct sockaddr_in *)&ctxt->ftpAddr)->sin_addr)) {
     858  	    __xmlIOErr(XML_FROM_FTP, 0, "gethostbyname address mismatch");
     859  	    return (-1);
     860  	}
     861  
     862  	/*
     863  	 * Prepare the socket
     864  	 */
     865  	((struct sockaddr_in *)&ctxt->ftpAddr)->sin_family = AF_INET;
     866  	memcpy (&((struct sockaddr_in *)&ctxt->ftpAddr)->sin_addr,
     867  		hp->h_addr_list[0], hp->h_length);
     868  	((struct sockaddr_in *)&ctxt->ftpAddr)->sin_port =
     869               (unsigned short)htons ((unsigned short)port);
     870  	ctxt->controlFd = socket (AF_INET, SOCK_STREAM, 0);
     871  	addrlen = sizeof (struct sockaddr_in);
     872      }
     873  
     874      if (ctxt->controlFd == INVALID_SOCKET) {
     875  	__xmlIOErr(XML_FROM_FTP, 0, "socket failed");
     876          return(-1);
     877      }
     878  
     879      /*
     880       * Do the connect.
     881       */
     882      if (connect(ctxt->controlFd, (struct sockaddr *) &ctxt->ftpAddr,
     883  	    addrlen) < 0) {
     884  	__xmlIOErr(XML_FROM_FTP, 0, "Failed to create a connection");
     885          closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
     886          ctxt->controlFd = INVALID_SOCKET;
     887  	return(-1);
     888      }
     889  
     890      /*
     891       * Wait for the HELLO from the server.
     892       */
     893      res = xmlNanoFTPGetResponse(ctxt);
     894      if (res != 2) {
     895          closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
     896          ctxt->controlFd = INVALID_SOCKET;
     897  	return(-1);
     898      }
     899  
     900      /*
     901       * State diagram for the login operation on the FTP server
     902       *
     903       * Reference: RFC 959
     904       *
     905       *                       1
     906       * +---+   USER    +---+------------->+---+
     907       * | B |---------->| W | 2       ---->| E |
     908       * +---+           +---+------  |  -->+---+
     909       *                  | |       | | |
     910       *                3 | | 4,5   | | |
     911       *    --------------   -----  | | |
     912       *   |                      | | | |
     913       *   |                      | | | |
     914       *   |                 ---------  |
     915       *   |               1|     | |   |
     916       *   V                |     | |   |
     917       * +---+   PASS    +---+ 2  |  ------>+---+
     918       * |   |---------->| W |------------->| S |
     919       * +---+           +---+   ---------->+---+
     920       *                  | |   | |     |
     921       *                3 | |4,5| |     |
     922       *    --------------   --------   |
     923       *   |                    | |  |  |
     924       *   |                    | |  |  |
     925       *   |                 -----------
     926       *   |             1,3|   | |  |
     927       *   V                |  2| |  |
     928       * +---+   ACCT    +---+--  |   ----->+---+
     929       * |   |---------->| W | 4,5 -------->| F |
     930       * +---+           +---+------------->+---+
     931       *
     932       * Of course in case of using a proxy this get really nasty and is not
     933       * standardized at all :-(
     934       */
     935      if (proxy) {
     936          int len;
     937  	char buf[400];
     938  
     939          if (proxyUser != NULL) {
     940  	    /*
     941  	     * We need proxy auth
     942  	     */
     943  	    snprintf(buf, sizeof(buf), "USER %s\r\n", proxyUser);
     944              buf[sizeof(buf) - 1] = 0;
     945              len = strlen(buf);
     946  	    res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
     947  	    if (res < 0) {
     948  		__xmlIOErr(XML_FROM_FTP, 0, "send failed");
     949  		closesocket(ctxt->controlFd);
     950  		ctxt->controlFd = INVALID_SOCKET;
     951  	        return(res);
     952  	    }
     953  	    res = xmlNanoFTPGetResponse(ctxt);
     954  	    switch (res) {
     955  		case 2:
     956  		    if (proxyPasswd == NULL)
     957  			break;
     958                      /* Falls through. */
     959  		case 3:
     960  		    if (proxyPasswd != NULL)
     961  			snprintf(buf, sizeof(buf), "PASS %s\r\n", proxyPasswd);
     962  		    else
     963  			snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
     964                      buf[sizeof(buf) - 1] = 0;
     965                      len = strlen(buf);
     966  		    res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
     967  		    if (res < 0) {
     968  			__xmlIOErr(XML_FROM_FTP, 0, "send failed");
     969  			closesocket(ctxt->controlFd);
     970  			ctxt->controlFd = INVALID_SOCKET;
     971  			return(res);
     972  		    }
     973  		    res = xmlNanoFTPGetResponse(ctxt);
     974  		    if (res > 3) {
     975  			closesocket(ctxt->controlFd);
     976  			ctxt->controlFd = INVALID_SOCKET;
     977  			return(-1);
     978  		    }
     979  		    break;
     980  		case 1:
     981  		    break;
     982  		case 4:
     983  		case 5:
     984  		case -1:
     985  		default:
     986  		    closesocket(ctxt->controlFd);
     987  		    ctxt->controlFd = INVALID_SOCKET;
     988  		    return(-1);
     989  	    }
     990  	}
     991  
     992  	/*
     993  	 * We assume we don't need more authentication to the proxy
     994  	 * and that it succeeded :-\
     995  	 */
     996  	switch (proxyType) {
     997  	    case 0:
     998  		/* we will try in sequence */
     999  	    case 1:
    1000  		/* Using SITE command */
    1001  		snprintf(buf, sizeof(buf), "SITE %s\r\n", ctxt->hostname);
    1002                  buf[sizeof(buf) - 1] = 0;
    1003                  len = strlen(buf);
    1004  		res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
    1005  		if (res < 0) {
    1006  		    __xmlIOErr(XML_FROM_FTP, 0, "send failed");
    1007  		    closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
    1008  		    ctxt->controlFd = INVALID_SOCKET;
    1009  		    return(res);
    1010  		}
    1011  		res = xmlNanoFTPGetResponse(ctxt);
    1012  		if (res == 2) {
    1013  		    /* we assume it worked :-\ 1 is error for SITE command */
    1014  		    proxyType = 1;
    1015  		    break;
    1016  		}
    1017  		if (proxyType == 1) {
    1018  		    closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
    1019  		    ctxt->controlFd = INVALID_SOCKET;
    1020  		    return(-1);
    1021  		}
    1022                  /* Falls through. */
    1023  	    case 2:
    1024  		/* USER user@host command */
    1025  		if (ctxt->user == NULL)
    1026  		    snprintf(buf, sizeof(buf), "USER anonymous@%s\r\n",
    1027  			           ctxt->hostname);
    1028  		else
    1029  		    snprintf(buf, sizeof(buf), "USER %s@%s\r\n",
    1030  			           ctxt->user, ctxt->hostname);
    1031                  buf[sizeof(buf) - 1] = 0;
    1032                  len = strlen(buf);
    1033  		res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
    1034  		if (res < 0) {
    1035  		    __xmlIOErr(XML_FROM_FTP, 0, "send failed");
    1036  		    closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
    1037  		    ctxt->controlFd = INVALID_SOCKET;
    1038  		    return(res);
    1039  		}
    1040  		res = xmlNanoFTPGetResponse(ctxt);
    1041  		if ((res == 1) || (res == 2)) {
    1042  		    /* we assume it worked :-\ */
    1043  		    proxyType = 2;
    1044  		    return(0);
    1045  		}
    1046  		if (ctxt->passwd == NULL)
    1047  		    snprintf(buf, sizeof(buf), "PASS anonymous@\r\n");
    1048  		else
    1049  		    snprintf(buf, sizeof(buf), "PASS %s\r\n", ctxt->passwd);
    1050                  buf[sizeof(buf) - 1] = 0;
    1051                  len = strlen(buf);
    1052  		res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
    1053  		if (res < 0) {
    1054  		    __xmlIOErr(XML_FROM_FTP, 0, "send failed");
    1055  		    closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
    1056  		    ctxt->controlFd = INVALID_SOCKET;
    1057  		    return(res);
    1058  		}
    1059  		res = xmlNanoFTPGetResponse(ctxt);
    1060  		if ((res == 1) || (res == 2)) {
    1061  		    /* we assume it worked :-\ */
    1062  		    proxyType = 2;
    1063  		    return(0);
    1064  		}
    1065  		if (proxyType == 2) {
    1066  		    closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
    1067  		    ctxt->controlFd = INVALID_SOCKET;
    1068  		    return(-1);
    1069  		}
    1070                  /* Falls through. */
    1071  	    case 3:
    1072  		/*
    1073  		 * If you need support for other Proxy authentication scheme
    1074  		 * send the code or at least the sequence in use.
    1075  		 */
    1076  	    default:
    1077  		closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
    1078  		ctxt->controlFd = INVALID_SOCKET;
    1079  		return(-1);
    1080  	}
    1081      }
    1082      /*
    1083       * Non-proxy handling.
    1084       */
    1085      res = xmlNanoFTPSendUser(ctxt);
    1086      if (res < 0) {
    1087          closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
    1088          ctxt->controlFd = INVALID_SOCKET;
    1089  	return(-1);
    1090      }
    1091      res = xmlNanoFTPGetResponse(ctxt);
    1092      switch (res) {
    1093  	case 2:
    1094  	    return(0);
    1095  	case 3:
    1096  	    break;
    1097  	case 1:
    1098  	case 4:
    1099  	case 5:
    1100          case -1:
    1101  	default:
    1102  	    closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
    1103  	    ctxt->controlFd = INVALID_SOCKET;
    1104  	    return(-1);
    1105      }
    1106      res = xmlNanoFTPSendPasswd(ctxt);
    1107      if (res < 0) {
    1108          closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
    1109          ctxt->controlFd = INVALID_SOCKET;
    1110  	return(-1);
    1111      }
    1112      res = xmlNanoFTPGetResponse(ctxt);
    1113      switch (res) {
    1114  	case 2:
    1115  	    break;
    1116  	case 3:
    1117  	    __xmlIOErr(XML_FROM_FTP, XML_FTP_ACCNT,
    1118  		       "FTP server asking for ACCNT on anonymous\n");
    1119             /* Falls through. */
    1120  	case 1:
    1121  	case 4:
    1122  	case 5:
    1123          case -1:
    1124  	default:
    1125  	    closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
    1126  	    ctxt->controlFd = INVALID_SOCKET;
    1127  	    return(-1);
    1128      }
    1129  
    1130      return(0);
    1131  }
    1132  
    1133  /**
    1134   * xmlNanoFTPConnectTo:
    1135   * @server:  an FTP server name
    1136   * @port:  the port (use 21 if 0)
    1137   *
    1138   * Tries to open a control connection to the given server/port
    1139   *
    1140   * Returns an fTP context or NULL if it failed
    1141   */
    1142  
    1143  void*
    1144  xmlNanoFTPConnectTo(const char *server, int port) {
    1145      xmlNanoFTPCtxtPtr ctxt;
    1146      int res;
    1147  
    1148      xmlNanoFTPInit();
    1149      if (server == NULL)
    1150  	return(NULL);
    1151      if (port <= 0)
    1152  	return(NULL);
    1153      ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(NULL);
    1154      if (ctxt == NULL)
    1155          return(NULL);
    1156      ctxt->hostname = xmlMemStrdup(server);
    1157      if (ctxt->hostname == NULL) {
    1158  	xmlNanoFTPFreeCtxt(ctxt);
    1159  	return(NULL);
    1160      }
    1161      ctxt->port = port;
    1162      res = xmlNanoFTPConnect(ctxt);
    1163      if (res < 0) {
    1164  	xmlNanoFTPFreeCtxt(ctxt);
    1165  	return(NULL);
    1166      }
    1167      return(ctxt);
    1168  }
    1169  
    1170  /**
    1171   * xmlNanoFTPCwd:
    1172   * @ctx:  an FTP context
    1173   * @directory:  a directory on the server
    1174   *
    1175   * Tries to change the remote directory
    1176   *
    1177   * Returns -1 in case of error, 1 if CWD worked, 0 if it failed
    1178   */
    1179  
    1180  int
    1181  xmlNanoFTPCwd(void *ctx, const char *directory) {
    1182      xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
    1183      char buf[400];
    1184      int len;
    1185      int res;
    1186  
    1187      if ((ctxt == NULL) || (ctxt->controlFd == INVALID_SOCKET)) return(-1);
    1188      if (directory == NULL) return 0;
    1189  
    1190      /*
    1191       * Expected response code for CWD:
    1192       *
    1193       * CWD
    1194       *     250
    1195       *     500, 501, 502, 421, 530, 550
    1196       */
    1197      snprintf(buf, sizeof(buf), "CWD %s\r\n", directory);
    1198      buf[sizeof(buf) - 1] = 0;
    1199      len = strlen(buf);
    1200      res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
    1201      if (res < 0) {
    1202  	__xmlIOErr(XML_FROM_FTP, 0, "send failed");
    1203  	return(res);
    1204      }
    1205      res = xmlNanoFTPGetResponse(ctxt);
    1206      if (res == 4) {
    1207  	return(-1);
    1208      }
    1209      if (res == 2) return(1);
    1210      if (res == 5) {
    1211  	return(0);
    1212      }
    1213      return(0);
    1214  }
    1215  
    1216  /**
    1217   * xmlNanoFTPDele:
    1218   * @ctx:  an FTP context
    1219   * @file:  a file or directory on the server
    1220   *
    1221   * Tries to delete an item (file or directory) from server
    1222   *
    1223   * Returns -1 in case of error, 1 if DELE worked, 0 if it failed
    1224   */
    1225  
    1226  int
    1227  xmlNanoFTPDele(void *ctx, const char *file) {
    1228      xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
    1229      char buf[400];
    1230      int len;
    1231      int res;
    1232  
    1233      if ((ctxt == NULL) || (ctxt->controlFd == INVALID_SOCKET) ||
    1234          (file == NULL)) return(-1);
    1235  
    1236      /*
    1237       * Expected response code for DELE:
    1238       *
    1239       * DELE
    1240       *       250
    1241       *       450, 550
    1242       *       500, 501, 502, 421, 530
    1243       */
    1244  
    1245      snprintf(buf, sizeof(buf), "DELE %s\r\n", file);
    1246      buf[sizeof(buf) - 1] = 0;
    1247      len = strlen(buf);
    1248      res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
    1249      if (res < 0) {
    1250  	__xmlIOErr(XML_FROM_FTP, 0, "send failed");
    1251  	return(res);
    1252      }
    1253      res = xmlNanoFTPGetResponse(ctxt);
    1254      if (res == 4) {
    1255  	return(-1);
    1256      }
    1257      if (res == 2) return(1);
    1258      if (res == 5) {
    1259  	return(0);
    1260      }
    1261      return(0);
    1262  }
    1263  /**
    1264   * xmlNanoFTPGetConnection:
    1265   * @ctx:  an FTP context
    1266   *
    1267   * Try to open a data connection to the server. Currently only
    1268   * passive mode is supported.
    1269   *
    1270   * Returns -1 in case of error, 0 otherwise
    1271   */
    1272  
    1273  SOCKET
    1274  xmlNanoFTPGetConnection(void *ctx) {
    1275      xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
    1276      char buf[200], *cur;
    1277      int len, i;
    1278      int res;
    1279      unsigned char ad[6], *adp, *portp;
    1280      unsigned int temp[6];
    1281  #ifdef SUPPORT_IP6
    1282      struct sockaddr_storage dataAddr;
    1283  #else
    1284      struct sockaddr_in dataAddr;
    1285  #endif
    1286      XML_SOCKLEN_T dataAddrLen;
    1287  
    1288      if (ctxt == NULL) return INVALID_SOCKET;
    1289  
    1290      memset (&dataAddr, 0, sizeof(dataAddr));
    1291  #ifdef SUPPORT_IP6
    1292      if ((ctxt->ftpAddr).ss_family == AF_INET6) {
    1293  	ctxt->dataFd = socket (AF_INET6, SOCK_STREAM, IPPROTO_TCP);
    1294  	((struct sockaddr_in6 *)&dataAddr)->sin6_family = AF_INET6;
    1295  	dataAddrLen = sizeof(struct sockaddr_in6);
    1296      } else
    1297  #endif
    1298      {
    1299  	ctxt->dataFd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
    1300  	((struct sockaddr_in *)&dataAddr)->sin_family = AF_INET;
    1301  	dataAddrLen = sizeof (struct sockaddr_in);
    1302      }
    1303  
    1304      if (ctxt->dataFd == INVALID_SOCKET) {
    1305  	__xmlIOErr(XML_FROM_FTP, 0, "socket failed");
    1306  	return INVALID_SOCKET;
    1307      }
    1308  
    1309      if (ctxt->passive) {
    1310  #ifdef SUPPORT_IP6
    1311  	if ((ctxt->ftpAddr).ss_family == AF_INET6)
    1312  	    snprintf (buf, sizeof(buf), "EPSV\r\n");
    1313  	else
    1314  #endif
    1315  	    snprintf (buf, sizeof(buf), "PASV\r\n");
    1316          len = strlen (buf);
    1317  	res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
    1318  	if (res < 0) {
    1319  	    __xmlIOErr(XML_FROM_FTP, 0, "send failed");
    1320  	    closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
    1321  	    return INVALID_SOCKET;
    1322  	}
    1323          res = xmlNanoFTPReadResponse(ctx);
    1324  	if (res != 2) {
    1325  	    if (res == 5) {
    1326  	        closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
    1327  		return INVALID_SOCKET;
    1328  	    } else {
    1329  		/*
    1330  		 * retry with an active connection
    1331  		 */
    1332  	        closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
    1333  	        ctxt->passive = 0;
    1334  	    }
    1335  	}
    1336  	cur = &ctxt->controlBuf[ctxt->controlBufAnswer];
    1337  	while (((*cur < '0') || (*cur > '9')) && *cur != '\0') cur++;
    1338  #ifdef SUPPORT_IP6
    1339  	if ((ctxt->ftpAddr).ss_family == AF_INET6) {
    1340  	    if (sscanf (cur, "%u", &temp[0]) != 1) {
    1341  		__xmlIOErr(XML_FROM_FTP, XML_FTP_EPSV_ANSWER,
    1342  			"Invalid answer to EPSV\n");
    1343  		if (ctxt->dataFd != INVALID_SOCKET) {
    1344  		    closesocket (ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
    1345  		}
    1346  		return INVALID_SOCKET;
    1347  	    }
    1348  	    memcpy (&((struct sockaddr_in6 *)&dataAddr)->sin6_addr, &((struct sockaddr_in6 *)&ctxt->ftpAddr)->sin6_addr, sizeof(struct in6_addr));
    1349  	    ((struct sockaddr_in6 *)&dataAddr)->sin6_port = htons (temp[0]);
    1350  	}
    1351  	else
    1352  #endif
    1353  	{
    1354  	    if (sscanf (cur, "%u,%u,%u,%u,%u,%u", &temp[0], &temp[1], &temp[2],
    1355  		&temp[3], &temp[4], &temp[5]) != 6) {
    1356  		__xmlIOErr(XML_FROM_FTP, XML_FTP_PASV_ANSWER,
    1357  			"Invalid answer to PASV\n");
    1358  		if (ctxt->dataFd != INVALID_SOCKET) {
    1359  		    closesocket (ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
    1360  		}
    1361  		return INVALID_SOCKET;
    1362  	    }
    1363  	    for (i=0; i<6; i++) ad[i] = (unsigned char) (temp[i] & 0xff);
    1364  	    memcpy (&((struct sockaddr_in *)&dataAddr)->sin_addr, &ad[0], 4);
    1365  	    memcpy (&((struct sockaddr_in *)&dataAddr)->sin_port, &ad[4], 2);
    1366  	}
    1367  
    1368  	if (connect(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
    1369  	    __xmlIOErr(XML_FROM_FTP, 0, "Failed to create a data connection");
    1370  	    closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
    1371  	    return INVALID_SOCKET;
    1372  	}
    1373      } else {
    1374          getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
    1375  #ifdef SUPPORT_IP6
    1376  	if ((ctxt->ftpAddr).ss_family == AF_INET6)
    1377  	    ((struct sockaddr_in6 *)&dataAddr)->sin6_port = 0;
    1378  	else
    1379  #endif
    1380  	    ((struct sockaddr_in *)&dataAddr)->sin_port = 0;
    1381  
    1382  	if (bind(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
    1383  	    __xmlIOErr(XML_FROM_FTP, 0, "bind failed");
    1384  	    closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
    1385  	    return INVALID_SOCKET;
    1386  	}
    1387          getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
    1388  
    1389  	if (listen(ctxt->dataFd, 1) < 0) {
    1390  	    __xmlIOErr(XML_FROM_FTP, 0, "listen failed");
    1391  	    closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
    1392  	    return INVALID_SOCKET;
    1393  	}
    1394  #ifdef SUPPORT_IP6
    1395  	if ((ctxt->ftpAddr).ss_family == AF_INET6) {
    1396  	    char buf6[INET6_ADDRSTRLEN];
    1397  	    inet_ntop (AF_INET6, &((struct sockaddr_in6 *)&dataAddr)->sin6_addr,
    1398  		    buf6, INET6_ADDRSTRLEN);
    1399  	    adp = (unsigned char *) buf6;
    1400  	    portp = (unsigned char *) &((struct sockaddr_in6 *)&dataAddr)->sin6_port;
    1401  	    snprintf (buf, sizeof(buf), "EPRT |2|%s|%s|\r\n", adp, portp);
    1402          } else
    1403  #endif
    1404  	{
    1405  	    adp = (unsigned char *) &((struct sockaddr_in *)&dataAddr)->sin_addr;
    1406  	    portp = (unsigned char *) &((struct sockaddr_in *)&dataAddr)->sin_port;
    1407  	    snprintf (buf, sizeof(buf), "PORT %d,%d,%d,%d,%d,%d\r\n",
    1408  	    adp[0] & 0xff, adp[1] & 0xff, adp[2] & 0xff, adp[3] & 0xff,
    1409  	    portp[0] & 0xff, portp[1] & 0xff);
    1410  	}
    1411  
    1412          buf[sizeof(buf) - 1] = 0;
    1413          len = strlen(buf);
    1414  
    1415  	res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
    1416  	if (res < 0) {
    1417  	    __xmlIOErr(XML_FROM_FTP, 0, "send failed");
    1418  	    closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
    1419  	    return INVALID_SOCKET;
    1420  	}
    1421          res = xmlNanoFTPGetResponse(ctxt);
    1422  	if (res != 2) {
    1423  	    closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
    1424  	    return INVALID_SOCKET;
    1425          }
    1426      }
    1427      return(ctxt->dataFd);
    1428  
    1429  }
    1430  
    1431  /**
    1432   * xmlNanoFTPCloseConnection:
    1433   * @ctx:  an FTP context
    1434   *
    1435   * Close the data connection from the server
    1436   *
    1437   * Returns -1 in case of error, 0 otherwise
    1438   */
    1439  
    1440  int
    1441  xmlNanoFTPCloseConnection(void *ctx) {
    1442      xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
    1443      int res;
    1444      fd_set rfd, efd;
    1445      struct timeval tv;
    1446  
    1447      if ((ctxt == NULL) || (ctxt->controlFd == INVALID_SOCKET)) return(-1);
    1448  
    1449      closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
    1450      tv.tv_sec = 15;
    1451      tv.tv_usec = 0;
    1452      FD_ZERO(&rfd);
    1453      FD_SET(ctxt->controlFd, &rfd);
    1454      FD_ZERO(&efd);
    1455      FD_SET(ctxt->controlFd, &efd);
    1456      res = select(ctxt->controlFd + 1, &rfd, NULL, &efd, &tv);
    1457      if (res < 0) {
    1458  	closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
    1459  	return(-1);
    1460      }
    1461      if (res == 0) {
    1462  	closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
    1463      } else {
    1464  	res = xmlNanoFTPGetResponse(ctxt);
    1465  	if (res != 2) {
    1466  	    closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
    1467  	    return(-1);
    1468  	}
    1469      }
    1470      return(0);
    1471  }
    1472  
    1473  /**
    1474   * xmlNanoFTPParseList:
    1475   * @list:  some data listing received from the server
    1476   * @callback:  the user callback
    1477   * @userData:  the user callback data
    1478   *
    1479   * Parse at most one entry from the listing.
    1480   *
    1481   * Returns -1 in case of error, the length of data parsed otherwise
    1482   */
    1483  
    1484  static int
    1485  xmlNanoFTPParseList(const char *list, ftpListCallback callback, void *userData) {
    1486      const char *cur = list;
    1487      char filename[151];
    1488      char attrib[11];
    1489      char owner[11];
    1490      char group[11];
    1491      char month[4];
    1492      int year = 0;
    1493      int minute = 0;
    1494      int hour = 0;
    1495      int day = 0;
    1496      unsigned long size = 0;
    1497      int links = 0;
    1498      int i;
    1499  
    1500      if (!strncmp(cur, "total", 5)) {
    1501          cur += 5;
    1502  	while (*cur == ' ') cur++;
    1503  	while ((*cur >= '0') && (*cur <= '9'))
    1504  	    links = (links * 10) + (*cur++ - '0');
    1505  	while ((*cur == ' ') || (*cur == '\n')  || (*cur == '\r'))
    1506  	    cur++;
    1507  	return(cur - list);
    1508      } else if (*list == '+') {
    1509  	return(0);
    1510      } else {
    1511  	while ((*cur == ' ') || (*cur == '\n')  || (*cur == '\r'))
    1512  	    cur++;
    1513  	if (*cur == 0) return(0);
    1514  	i = 0;
    1515  	while (*cur != ' ') {
    1516  	    if (i < 10)
    1517  		attrib[i++] = *cur;
    1518  	    cur++;
    1519  	    if (*cur == 0) return(0);
    1520  	}
    1521  	attrib[10] = 0;
    1522  	while (*cur == ' ') cur++;
    1523  	if (*cur == 0) return(0);
    1524  	while ((*cur >= '0') && (*cur <= '9'))
    1525  	    links = (links * 10) + (*cur++ - '0');
    1526  	while (*cur == ' ') cur++;
    1527  	if (*cur == 0) return(0);
    1528  	i = 0;
    1529  	while (*cur != ' ') {
    1530  	    if (i < 10)
    1531  		owner[i++] = *cur;
    1532  	    cur++;
    1533  	    if (*cur == 0) return(0);
    1534  	}
    1535  	owner[i] = 0;
    1536  	while (*cur == ' ') cur++;
    1537  	if (*cur == 0) return(0);
    1538  	i = 0;
    1539  	while (*cur != ' ') {
    1540  	    if (i < 10)
    1541  		group[i++] = *cur;
    1542  	    cur++;
    1543  	    if (*cur == 0) return(0);
    1544  	}
    1545  	group[i] = 0;
    1546  	while (*cur == ' ') cur++;
    1547  	if (*cur == 0) return(0);
    1548  	while ((*cur >= '0') && (*cur <= '9'))
    1549  	    size = (size * 10) + (*cur++ - '0');
    1550  	while (*cur == ' ') cur++;
    1551  	if (*cur == 0) return(0);
    1552  	i = 0;
    1553  	while (*cur != ' ') {
    1554  	    if (i < 3)
    1555  		month[i++] = *cur;
    1556  	    cur++;
    1557  	    if (*cur == 0) return(0);
    1558  	}
    1559  	month[i] = 0;
    1560  	while (*cur == ' ') cur++;
    1561  	if (*cur == 0) return(0);
    1562          while ((*cur >= '0') && (*cur <= '9'))
    1563  	    day = (day * 10) + (*cur++ - '0');
    1564  	while (*cur == ' ') cur++;
    1565  	if (*cur == 0) return(0);
    1566  	if ((cur[1] == 0) || (cur[2] == 0)) return(0);
    1567  	if ((cur[1] == ':') || (cur[2] == ':')) {
    1568  	    while ((*cur >= '0') && (*cur <= '9'))
    1569  		hour = (hour * 10) + (*cur++ - '0');
    1570  	    if (*cur == ':') cur++;
    1571  	    while ((*cur >= '0') && (*cur <= '9'))
    1572  		minute = (minute * 10) + (*cur++ - '0');
    1573  	} else {
    1574  	    while ((*cur >= '0') && (*cur <= '9'))
    1575  		year = (year * 10) + (*cur++ - '0');
    1576  	}
    1577  	while (*cur == ' ') cur++;
    1578  	if (*cur == 0) return(0);
    1579  	i = 0;
    1580  	while ((*cur != '\n')  && (*cur != '\r')) {
    1581  	    if (i < 150)
    1582  		filename[i++] = *cur;
    1583  	    cur++;
    1584  	    if (*cur == 0) return(0);
    1585  	}
    1586  	filename[i] = 0;
    1587  	if ((*cur != '\n') && (*cur != '\r'))
    1588  	    return(0);
    1589  	while ((*cur == '\n')  || (*cur == '\r'))
    1590  	    cur++;
    1591      }
    1592      if (callback != NULL) {
    1593          callback(userData, filename, attrib, owner, group, size, links,
    1594  		 year, month, day, hour, minute);
    1595      }
    1596      return(cur - list);
    1597  }
    1598  
    1599  /**
    1600   * xmlNanoFTPList:
    1601   * @ctx:  an FTP context
    1602   * @callback:  the user callback
    1603   * @userData:  the user callback data
    1604   * @filename:  optional files to list
    1605   *
    1606   * Do a listing on the server. All files info are passed back
    1607   * in the callbacks.
    1608   *
    1609   * Returns -1 in case of error, 0 otherwise
    1610   */
    1611  
    1612  int
    1613  xmlNanoFTPList(void *ctx, ftpListCallback callback, void *userData,
    1614  	       const char *filename) {
    1615      xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
    1616      char buf[4096 + 1];
    1617      int len, res;
    1618      int indx = 0, base;
    1619      fd_set rfd, efd;
    1620      struct timeval tv;
    1621  
    1622      if (ctxt == NULL) return (-1);
    1623      if (filename == NULL) {
    1624          if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
    1625  	    return(-1);
    1626  	ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
    1627  	if (ctxt->dataFd == INVALID_SOCKET)
    1628  	    return(-1);
    1629  	snprintf(buf, sizeof(buf), "LIST -L\r\n");
    1630      } else {
    1631  	if (filename[0] != '/') {
    1632  	    if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
    1633  		return(-1);
    1634  	}
    1635  	ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
    1636  	if (ctxt->dataFd == INVALID_SOCKET)
    1637  	    return(-1);
    1638  	snprintf(buf, sizeof(buf), "LIST -L %s\r\n", filename);
    1639      }
    1640      buf[sizeof(buf) - 1] = 0;
    1641      len = strlen(buf);
    1642      res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
    1643      if (res < 0) {
    1644  	__xmlIOErr(XML_FROM_FTP, 0, "send failed");
    1645  	closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
    1646  	return(res);
    1647      }
    1648      res = xmlNanoFTPReadResponse(ctxt);
    1649      if (res != 1) {
    1650  	closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
    1651  	return(-res);
    1652      }
    1653  
    1654      do {
    1655  	tv.tv_sec = 1;
    1656  	tv.tv_usec = 0;
    1657  	FD_ZERO(&rfd);
    1658  	FD_SET(ctxt->dataFd, &rfd);
    1659  	FD_ZERO(&efd);
    1660  	FD_SET(ctxt->dataFd, &efd);
    1661  	res = select(ctxt->dataFd + 1, &rfd, NULL, &efd, &tv);
    1662  	if (res < 0) {
    1663  	    closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
    1664  	    return(-1);
    1665  	}
    1666  	if (res == 0) {
    1667  	    res = xmlNanoFTPCheckResponse(ctxt);
    1668  	    if (res < 0) {
    1669  		closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
    1670  		ctxt->dataFd = INVALID_SOCKET;
    1671  		return(-1);
    1672  	    }
    1673  	    if (res == 2) {
    1674  		closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
    1675  		return(0);
    1676  	    }
    1677  
    1678  	    continue;
    1679  	}
    1680  
    1681  	if ((len = recv(ctxt->dataFd, &buf[indx], sizeof(buf) - (indx + 1), 0)) < 0) {
    1682  	    __xmlIOErr(XML_FROM_FTP, 0, "recv");
    1683  	    closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
    1684  	    ctxt->dataFd = INVALID_SOCKET;
    1685  	    return(-1);
    1686  	}
    1687  	indx += len;
    1688  	buf[indx] = 0;
    1689  	base = 0;
    1690  	do {
    1691  	    res = xmlNanoFTPParseList(&buf[base], callback, userData);
    1692  	    base += res;
    1693  	} while (res > 0);
    1694  
    1695  	memmove(&buf[0], &buf[base], indx - base);
    1696  	indx -= base;
    1697      } while (len != 0);
    1698      xmlNanoFTPCloseConnection(ctxt);
    1699      return(0);
    1700  }
    1701  
    1702  /**
    1703   * xmlNanoFTPGetSocket:
    1704   * @ctx:  an FTP context
    1705   * @filename:  the file to retrieve (or NULL if path is in context).
    1706   *
    1707   * Initiate fetch of the given file from the server.
    1708   *
    1709   * Returns the socket for the data connection, or <0 in case of error
    1710   */
    1711  
    1712  
    1713  SOCKET
    1714  xmlNanoFTPGetSocket(void *ctx, const char *filename) {
    1715      xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
    1716      char buf[300];
    1717      int res, len;
    1718      if (ctx == NULL)
    1719  	return INVALID_SOCKET;
    1720      if ((filename == NULL) && (ctxt->path == NULL))
    1721  	return INVALID_SOCKET;
    1722      ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
    1723      if (ctxt->dataFd == INVALID_SOCKET)
    1724  	return INVALID_SOCKET;
    1725  
    1726      snprintf(buf, sizeof(buf), "TYPE I\r\n");
    1727      len = strlen(buf);
    1728      res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
    1729      if (res < 0) {
    1730  	__xmlIOErr(XML_FROM_FTP, 0, "send failed");
    1731  	closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
    1732  	return INVALID_SOCKET;
    1733      }
    1734      res = xmlNanoFTPReadResponse(ctxt);
    1735      if (res != 2) {
    1736  	closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
    1737  	return INVALID_SOCKET;
    1738      }
    1739      if (filename == NULL)
    1740  	snprintf(buf, sizeof(buf), "RETR %s\r\n", ctxt->path);
    1741      else
    1742  	snprintf(buf, sizeof(buf), "RETR %s\r\n", filename);
    1743      buf[sizeof(buf) - 1] = 0;
    1744      len = strlen(buf);
    1745      res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
    1746      if (res < 0) {
    1747  	__xmlIOErr(XML_FROM_FTP, 0, "send failed");
    1748  	closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
    1749  	return INVALID_SOCKET;
    1750      }
    1751      res = xmlNanoFTPReadResponse(ctxt);
    1752      if (res != 1) {
    1753  	closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
    1754  	return INVALID_SOCKET;
    1755      }
    1756      return(ctxt->dataFd);
    1757  }
    1758  
    1759  /**
    1760   * xmlNanoFTPGet:
    1761   * @ctx:  an FTP context
    1762   * @callback:  the user callback
    1763   * @userData:  the user callback data
    1764   * @filename:  the file to retrieve
    1765   *
    1766   * Fetch the given file from the server. All data are passed back
    1767   * in the callbacks. The last callback has a size of 0 block.
    1768   *
    1769   * Returns -1 in case of error, 0 otherwise
    1770   */
    1771  
    1772  int
    1773  xmlNanoFTPGet(void *ctx, ftpDataCallback callback, void *userData,
    1774  	      const char *filename) {
    1775      xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
    1776      char buf[4096];
    1777      int len = 0, res;
    1778      fd_set rfd;
    1779      struct timeval tv;
    1780  
    1781      if (ctxt == NULL) return(-1);
    1782      if ((filename == NULL) && (ctxt->path == NULL))
    1783  	return(-1);
    1784      if (callback == NULL)
    1785  	return(-1);
    1786      if (xmlNanoFTPGetSocket(ctxt, filename) == INVALID_SOCKET)
    1787  	return(-1);
    1788  
    1789      do {
    1790  	tv.tv_sec = 1;
    1791  	tv.tv_usec = 0;
    1792  	FD_ZERO(&rfd);
    1793  	FD_SET(ctxt->dataFd, &rfd);
    1794  	res = select(ctxt->dataFd + 1, &rfd, NULL, NULL, &tv);
    1795  	if (res < 0) {
    1796  	    closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
    1797  	    return(-1);
    1798  	}
    1799  	if (res == 0) {
    1800  	    res = xmlNanoFTPCheckResponse(ctxt);
    1801  	    if (res < 0) {
    1802  		closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
    1803  		ctxt->dataFd = INVALID_SOCKET;
    1804  		return(-1);
    1805  	    }
    1806  	    if (res == 2) {
    1807  		closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
    1808  		return(0);
    1809  	    }
    1810  
    1811  	    continue;
    1812  	}
    1813  	if ((len = recv(ctxt->dataFd, buf, sizeof(buf), 0)) < 0) {
    1814  	    __xmlIOErr(XML_FROM_FTP, 0, "recv failed");
    1815  	    callback(userData, buf, len);
    1816  	    closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
    1817  	    return(-1);
    1818  	}
    1819  	callback(userData, buf, len);
    1820      } while (len != 0);
    1821  
    1822      return(xmlNanoFTPCloseConnection(ctxt));
    1823  }
    1824  
    1825  /**
    1826   * xmlNanoFTPRead:
    1827   * @ctx:  the FTP context
    1828   * @dest:  a buffer
    1829   * @len:  the buffer length
    1830   *
    1831   * This function tries to read @len bytes from the existing FTP connection
    1832   * and saves them in @dest. This is a blocking call.
    1833   *
    1834   * Returns the number of byte read. 0 is an indication of an end of connection.
    1835   *         -1 indicates a parameter error.
    1836   */
    1837  int
    1838  xmlNanoFTPRead(void *ctx, void *dest, int len) {
    1839      xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
    1840  
    1841      if (ctx == NULL) return(-1);
    1842      if (ctxt->dataFd == INVALID_SOCKET) return(0);
    1843      if (dest == NULL) return(-1);
    1844      if (len <= 0) return(0);
    1845  
    1846      len = recv(ctxt->dataFd, dest, len, 0);
    1847      if (len <= 0) {
    1848  	if (len < 0)
    1849  	    __xmlIOErr(XML_FROM_FTP, 0, "recv failed");
    1850  	xmlNanoFTPCloseConnection(ctxt);
    1851      }
    1852      return(len);
    1853  }
    1854  
    1855  /**
    1856   * xmlNanoFTPOpen:
    1857   * @URL: the URL to the resource
    1858   *
    1859   * Start to fetch the given ftp:// resource
    1860   *
    1861   * Returns an FTP context, or NULL
    1862   */
    1863  
    1864  void*
    1865  xmlNanoFTPOpen(const char *URL) {
    1866      xmlNanoFTPCtxtPtr ctxt;
    1867      SOCKET sock;
    1868  
    1869      xmlNanoFTPInit();
    1870      if (URL == NULL) return(NULL);
    1871      if (strncmp("ftp://", URL, 6)) return(NULL);
    1872  
    1873      ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(URL);
    1874      if (ctxt == NULL) return(NULL);
    1875      if (xmlNanoFTPConnect(ctxt) < 0) {
    1876  	xmlNanoFTPFreeCtxt(ctxt);
    1877  	return(NULL);
    1878      }
    1879      sock = xmlNanoFTPGetSocket(ctxt, ctxt->path);
    1880      if (sock == INVALID_SOCKET) {
    1881  	xmlNanoFTPFreeCtxt(ctxt);
    1882  	return(NULL);
    1883      }
    1884      return(ctxt);
    1885  }
    1886  
    1887  /**
    1888   * xmlNanoFTPClose:
    1889   * @ctx: an FTP context
    1890   *
    1891   * Close the connection and both control and transport
    1892   *
    1893   * Returns -1 in case of error, 0 otherwise
    1894   */
    1895  
    1896  int
    1897  xmlNanoFTPClose(void *ctx) {
    1898      xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
    1899  
    1900      if (ctxt == NULL)
    1901  	return(-1);
    1902  
    1903      if (ctxt->dataFd != INVALID_SOCKET) {
    1904  	closesocket(ctxt->dataFd);
    1905  	ctxt->dataFd = INVALID_SOCKET;
    1906      }
    1907      if (ctxt->controlFd != INVALID_SOCKET) {
    1908  	xmlNanoFTPQuit(ctxt);
    1909  	closesocket(ctxt->controlFd);
    1910  	ctxt->controlFd = INVALID_SOCKET;
    1911      }
    1912      xmlNanoFTPFreeCtxt(ctxt);
    1913      return(0);
    1914  }
    1915  
    1916  #ifdef STANDALONE
    1917  /************************************************************************
    1918   *									*
    1919   *			Basic test in Standalone mode			*
    1920   *									*
    1921   ************************************************************************/
    1922  static
    1923  void ftpList(void *userData, const char *filename, const char* attrib,
    1924  	     const char *owner, const char *group, unsigned long size, int links,
    1925  	     int year, const char *month, int day, int hour, int minute) {
    1926      xmlGenericError(xmlGenericErrorContext,
    1927  	    "%s %s %s %ld %s\n", attrib, owner, group, size, filename);
    1928  }
    1929  static
    1930  void ftpData(void *userData, const char *data, int len) {
    1931      if (userData == NULL) return;
    1932      if (len <= 0) {
    1933  	fclose((FILE*)userData);
    1934  	return;
    1935      }
    1936      fwrite(data, len, 1, (FILE*)userData);
    1937  }
    1938  
    1939  int main(int argc, char **argv) {
    1940      void *ctxt;
    1941      FILE *output;
    1942      char *tstfile = NULL;
    1943  
    1944      xmlNanoFTPInit();
    1945      if (argc > 1) {
    1946  	ctxt = xmlNanoFTPNewCtxt(argv[1]);
    1947  	if (xmlNanoFTPConnect(ctxt) < 0) {
    1948  	    xmlGenericError(xmlGenericErrorContext,
    1949  		    "Couldn't connect to %s\n", argv[1]);
    1950  	    exit(1);
    1951  	}
    1952  	if (argc > 2)
    1953  	    tstfile = argv[2];
    1954      } else
    1955  	ctxt = xmlNanoFTPConnectTo("localhost", 0);
    1956      if (ctxt == NULL) {
    1957          xmlGenericError(xmlGenericErrorContext,
    1958  		"Couldn't connect to localhost\n");
    1959          exit(1);
    1960      }
    1961      xmlNanoFTPList(ctxt, ftpList, NULL, tstfile);
    1962      output = fopen("/tmp/tstdata", "w");
    1963      if (output != NULL) {
    1964  	if (xmlNanoFTPGet(ctxt, ftpData, (void *) output, tstfile) < 0)
    1965  	    xmlGenericError(xmlGenericErrorContext,
    1966  		    "Failed to get file\n");
    1967  
    1968      }
    1969      xmlNanoFTPClose(ctxt);
    1970      exit(0);
    1971  }
    1972  #endif /* STANDALONE */
    1973  #else /* !LIBXML_FTP_ENABLED */
    1974  #ifdef STANDALONE
    1975  #include <stdio.h>
    1976  int main(int argc, char **argv) {
    1977      xmlGenericError(xmlGenericErrorContext,
    1978  	    "%s : FTP support not compiled in\n", argv[0]);
    1979      return(0);
    1980  }
    1981  #endif /* STANDALONE */
    1982  #endif /* LIBXML_FTP_ENABLED */