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