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