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