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