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