Imported Upstream version 0.18.1.1
[platform/upstream/gettext.git] / gettext-tools / gnulib-lib / libxml / nanoftp.c
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 #ifndef __BEOS__
93 #define closesocket(s) close(s)
94 #endif
95 #define SOCKET int
96 #endif
97
98 #ifdef __BEOS__
99 #ifndef PF_INET
100 #define PF_INET AF_INET
101 #endif
102 #endif
103
104 #ifdef _AIX
105 #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
119 typedef 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
143 static int initialized = 0;
144 static char *proxy = NULL;      /* the proxy name if any */
145 static int proxyPort = 0;       /* the proxy port if any */
146 static char *proxyUser = NULL;  /* user for proxy authentication */
147 static char *proxyPasswd = NULL;/* passwd for proxy authentication */
148 static int proxyType = 0;       /* uses TYPE or a@b ? */
149
150 #ifdef SUPPORT_IP6
151 static
152 int 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  */
170 static void
171 xmlFTPErrMemory(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
184 void
185 xmlNanoFTPInit(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
229 void
230 xmlNanoFTPCleanup(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
263 void
264 xmlNanoFTPProxy(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
297 static void
298 xmlNanoFTPScanURL(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
366 int
367 xmlNanoFTPUpdateURL(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
423 void
424 xmlNanoFTPScanProxy(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
468 void*
469 xmlNanoFTPNewCtxt(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
504 void
505 xmlNanoFTPFreeCtxt(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  */
530 static int
531 xmlNanoFTPParseResponse(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  */
562 static int
563 xmlNanoFTPGetMore(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  */
642 static int
643 xmlNanoFTPReadResponse(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
651 get_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
715 int
716 xmlNanoFTPGetResponse(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
732 int
733 xmlNanoFTPCheckResponse(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
759 static int
760 xmlNanoFTPSendUser(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
787 static int
788 xmlNanoFTPSendPasswd(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
821 int
822 xmlNanoFTPQuit(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
851 int
852 xmlNanoFTPConnect(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
1232 void*
1233 xmlNanoFTPConnectTo(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
1264 int
1265 xmlNanoFTPCwd(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
1313 int
1314 xmlNanoFTPDele(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
1363 int
1364 xmlNanoFTPGetConnection(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
1536 int
1537 xmlNanoFTPCloseConnection(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
1587 static int
1588 xmlNanoFTPParseList(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
1715 int
1716 xmlNanoFTPList(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
1825 int
1826 xmlNanoFTPGetSocket(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
1890 int
1891 xmlNanoFTPGet(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  */
1958 int
1959 xmlNanoFTPRead(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
1988 void*
1989 xmlNanoFTPOpen(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
2020 int
2021 xmlNanoFTPClose(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  ************************************************************************/
2046 static
2047 void 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 }
2053 static
2054 void 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
2063 int 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>
2101 int 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"