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