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