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