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