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