• 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_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 information
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 information,
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 information.
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 information.
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 information.
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     ctxt->port = port;
1255     res = xmlNanoFTPConnect(ctxt);
1256     if (res < 0) {
1257 	xmlNanoFTPFreeCtxt(ctxt);
1258 	return(NULL);
1259     }
1260     return(ctxt);
1261 }
1262 
1263 /**
1264  * xmlNanoFTPCwd:
1265  * @ctx:  an FTP context
1266  * @directory:  a directory on the server
1267  *
1268  * Tries to change the remote directory
1269  *
1270  * Returns -1 in case of error, 1 if CWD worked, 0 if it failed
1271  */
1272 
1273 int
xmlNanoFTPCwd(void * ctx,const char * directory)1274 xmlNanoFTPCwd(void *ctx, const char *directory) {
1275     xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1276     char buf[400];
1277     int len;
1278     int res;
1279 
1280     if ((ctxt == NULL) || (ctxt->controlFd == INVALID_SOCKET)) return(-1);
1281     if (directory == NULL) return 0;
1282 
1283     /*
1284      * Expected response code for CWD:
1285      *
1286      * CWD
1287      *     250
1288      *     500, 501, 502, 421, 530, 550
1289      */
1290     snprintf(buf, sizeof(buf), "CWD %s\r\n", directory);
1291     buf[sizeof(buf) - 1] = 0;
1292     len = strlen(buf);
1293 #ifdef DEBUG_FTP
1294     xmlGenericError(xmlGenericErrorContext, "%s", buf);
1295 #endif
1296     res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
1297     if (res < 0) {
1298 	__xmlIOErr(XML_FROM_FTP, 0, "send failed");
1299 	return(res);
1300     }
1301     res = xmlNanoFTPGetResponse(ctxt);
1302     if (res == 4) {
1303 	return(-1);
1304     }
1305     if (res == 2) return(1);
1306     if (res == 5) {
1307 	return(0);
1308     }
1309     return(0);
1310 }
1311 
1312 /**
1313  * xmlNanoFTPDele:
1314  * @ctx:  an FTP context
1315  * @file:  a file or directory on the server
1316  *
1317  * Tries to delete an item (file or directory) from server
1318  *
1319  * Returns -1 in case of error, 1 if DELE worked, 0 if it failed
1320  */
1321 
1322 int
xmlNanoFTPDele(void * ctx,const char * file)1323 xmlNanoFTPDele(void *ctx, const char *file) {
1324     xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1325     char buf[400];
1326     int len;
1327     int res;
1328 
1329     if ((ctxt == NULL) || (ctxt->controlFd == INVALID_SOCKET) ||
1330         (file == NULL)) return(-1);
1331 
1332     /*
1333      * Expected response code for DELE:
1334      *
1335      * DELE
1336      *       250
1337      *       450, 550
1338      *       500, 501, 502, 421, 530
1339      */
1340 
1341     snprintf(buf, sizeof(buf), "DELE %s\r\n", file);
1342     buf[sizeof(buf) - 1] = 0;
1343     len = strlen(buf);
1344 #ifdef DEBUG_FTP
1345     xmlGenericError(xmlGenericErrorContext, "%s", buf);
1346 #endif
1347     res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
1348     if (res < 0) {
1349 	__xmlIOErr(XML_FROM_FTP, 0, "send failed");
1350 	return(res);
1351     }
1352     res = xmlNanoFTPGetResponse(ctxt);
1353     if (res == 4) {
1354 	return(-1);
1355     }
1356     if (res == 2) return(1);
1357     if (res == 5) {
1358 	return(0);
1359     }
1360     return(0);
1361 }
1362 /**
1363  * xmlNanoFTPGetConnection:
1364  * @ctx:  an FTP context
1365  *
1366  * Try to open a data connection to the server. Currently only
1367  * passive mode is supported.
1368  *
1369  * Returns -1 in case of error, 0 otherwise
1370  */
1371 
1372 SOCKET
xmlNanoFTPGetConnection(void * ctx)1373 xmlNanoFTPGetConnection(void *ctx) {
1374     xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1375     char buf[200], *cur;
1376     int len, i;
1377     int res;
1378     unsigned char ad[6], *adp, *portp;
1379     unsigned int temp[6];
1380 #ifdef SUPPORT_IP6
1381     struct sockaddr_storage dataAddr;
1382 #else
1383     struct sockaddr_in dataAddr;
1384 #endif
1385     XML_SOCKLEN_T dataAddrLen;
1386 
1387     if (ctxt == NULL) return INVALID_SOCKET;
1388 
1389     memset (&dataAddr, 0, sizeof(dataAddr));
1390 #ifdef SUPPORT_IP6
1391     if ((ctxt->ftpAddr).ss_family == AF_INET6) {
1392 	ctxt->dataFd = socket (AF_INET6, SOCK_STREAM, IPPROTO_TCP);
1393 	((struct sockaddr_in6 *)&dataAddr)->sin6_family = AF_INET6;
1394 	dataAddrLen = sizeof(struct sockaddr_in6);
1395     } else
1396 #endif
1397     {
1398 	ctxt->dataFd = socket (AF_INET, SOCK_STREAM, IPPROTO_TCP);
1399 	((struct sockaddr_in *)&dataAddr)->sin_family = AF_INET;
1400 	dataAddrLen = sizeof (struct sockaddr_in);
1401     }
1402 
1403     if (ctxt->dataFd == INVALID_SOCKET) {
1404 	__xmlIOErr(XML_FROM_FTP, 0, "socket failed");
1405 	return INVALID_SOCKET;
1406     }
1407 
1408     if (ctxt->passive) {
1409 #ifdef SUPPORT_IP6
1410 	if ((ctxt->ftpAddr).ss_family == AF_INET6)
1411 	    snprintf (buf, sizeof(buf), "EPSV\r\n");
1412 	else
1413 #endif
1414 	    snprintf (buf, sizeof(buf), "PASV\r\n");
1415         len = strlen (buf);
1416 #ifdef DEBUG_FTP
1417 	xmlGenericError(xmlGenericErrorContext, "%s", buf);
1418 #endif
1419 	res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
1420 	if (res < 0) {
1421 	    __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1422 	    closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1423 	    return INVALID_SOCKET;
1424 	}
1425         res = xmlNanoFTPReadResponse(ctx);
1426 	if (res != 2) {
1427 	    if (res == 5) {
1428 	        closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1429 		return INVALID_SOCKET;
1430 	    } else {
1431 		/*
1432 		 * retry with an active connection
1433 		 */
1434 	        closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1435 	        ctxt->passive = 0;
1436 	    }
1437 	}
1438 	cur = &ctxt->controlBuf[ctxt->controlBufAnswer];
1439 	while (((*cur < '0') || (*cur > '9')) && *cur != '\0') cur++;
1440 #ifdef SUPPORT_IP6
1441 	if ((ctxt->ftpAddr).ss_family == AF_INET6) {
1442 	    if (sscanf (cur, "%u", &temp[0]) != 1) {
1443 		__xmlIOErr(XML_FROM_FTP, XML_FTP_EPSV_ANSWER,
1444 			"Invalid answer to EPSV\n");
1445 		if (ctxt->dataFd != INVALID_SOCKET) {
1446 		    closesocket (ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1447 		}
1448 		return INVALID_SOCKET;
1449 	    }
1450 	    memcpy (&((struct sockaddr_in6 *)&dataAddr)->sin6_addr, &((struct sockaddr_in6 *)&ctxt->ftpAddr)->sin6_addr, sizeof(struct in6_addr));
1451 	    ((struct sockaddr_in6 *)&dataAddr)->sin6_port = htons (temp[0]);
1452 	}
1453 	else
1454 #endif
1455 	{
1456 	    if (sscanf (cur, "%u,%u,%u,%u,%u,%u", &temp[0], &temp[1], &temp[2],
1457 		&temp[3], &temp[4], &temp[5]) != 6) {
1458 		__xmlIOErr(XML_FROM_FTP, XML_FTP_PASV_ANSWER,
1459 			"Invalid answer to PASV\n");
1460 		if (ctxt->dataFd != INVALID_SOCKET) {
1461 		    closesocket (ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1462 		}
1463 		return INVALID_SOCKET;
1464 	    }
1465 	    for (i=0; i<6; i++) ad[i] = (unsigned char) (temp[i] & 0xff);
1466 	    memcpy (&((struct sockaddr_in *)&dataAddr)->sin_addr, &ad[0], 4);
1467 	    memcpy (&((struct sockaddr_in *)&dataAddr)->sin_port, &ad[4], 2);
1468 	}
1469 
1470 	if (connect(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
1471 	    __xmlIOErr(XML_FROM_FTP, 0, "Failed to create a data connection");
1472 	    closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1473 	    return INVALID_SOCKET;
1474 	}
1475     } else {
1476         getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
1477 #ifdef SUPPORT_IP6
1478 	if ((ctxt->ftpAddr).ss_family == AF_INET6)
1479 	    ((struct sockaddr_in6 *)&dataAddr)->sin6_port = 0;
1480 	else
1481 #endif
1482 	    ((struct sockaddr_in *)&dataAddr)->sin_port = 0;
1483 
1484 	if (bind(ctxt->dataFd, (struct sockaddr *) &dataAddr, dataAddrLen) < 0) {
1485 	    __xmlIOErr(XML_FROM_FTP, 0, "bind failed");
1486 	    closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1487 	    return INVALID_SOCKET;
1488 	}
1489         getsockname(ctxt->dataFd, (struct sockaddr *) &dataAddr, &dataAddrLen);
1490 
1491 	if (listen(ctxt->dataFd, 1) < 0) {
1492 	    __xmlIOErr(XML_FROM_FTP, 0, "listen failed");
1493 	    closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1494 	    return INVALID_SOCKET;
1495 	}
1496 #ifdef SUPPORT_IP6
1497 	if ((ctxt->ftpAddr).ss_family == AF_INET6) {
1498 	    char buf6[INET6_ADDRSTRLEN];
1499 	    inet_ntop (AF_INET6, &((struct sockaddr_in6 *)&dataAddr)->sin6_addr,
1500 		    buf6, INET6_ADDRSTRLEN);
1501 	    adp = (unsigned char *) buf6;
1502 	    portp = (unsigned char *) &((struct sockaddr_in6 *)&dataAddr)->sin6_port;
1503 	    snprintf (buf, sizeof(buf), "EPRT |2|%s|%s|\r\n", adp, portp);
1504         } else
1505 #endif
1506 	{
1507 	    adp = (unsigned char *) &((struct sockaddr_in *)&dataAddr)->sin_addr;
1508 	    portp = (unsigned char *) &((struct sockaddr_in *)&dataAddr)->sin_port;
1509 	    snprintf (buf, sizeof(buf), "PORT %d,%d,%d,%d,%d,%d\r\n",
1510 	    adp[0] & 0xff, adp[1] & 0xff, adp[2] & 0xff, adp[3] & 0xff,
1511 	    portp[0] & 0xff, portp[1] & 0xff);
1512 	}
1513 
1514         buf[sizeof(buf) - 1] = 0;
1515         len = strlen(buf);
1516 #ifdef DEBUG_FTP
1517 	xmlGenericError(xmlGenericErrorContext, "%s", buf);
1518 #endif
1519 
1520 	res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
1521 	if (res < 0) {
1522 	    __xmlIOErr(XML_FROM_FTP, 0, "send failed");
1523 	    closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1524 	    return INVALID_SOCKET;
1525 	}
1526         res = xmlNanoFTPGetResponse(ctxt);
1527 	if (res != 2) {
1528 	    closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1529 	    return INVALID_SOCKET;
1530         }
1531     }
1532     return(ctxt->dataFd);
1533 
1534 }
1535 
1536 /**
1537  * xmlNanoFTPCloseConnection:
1538  * @ctx:  an FTP context
1539  *
1540  * Close the data connection from the server
1541  *
1542  * Returns -1 in case of error, 0 otherwise
1543  */
1544 
1545 int
xmlNanoFTPCloseConnection(void * ctx)1546 xmlNanoFTPCloseConnection(void *ctx) {
1547     xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1548     int res;
1549     fd_set rfd, efd;
1550     struct timeval tv;
1551 
1552     if ((ctxt == NULL) || (ctxt->controlFd == INVALID_SOCKET)) return(-1);
1553 
1554     closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1555     tv.tv_sec = 15;
1556     tv.tv_usec = 0;
1557     FD_ZERO(&rfd);
1558     FD_SET(ctxt->controlFd, &rfd);
1559     FD_ZERO(&efd);
1560     FD_SET(ctxt->controlFd, &efd);
1561     res = select(ctxt->controlFd + 1, &rfd, NULL, &efd, &tv);
1562     if (res < 0) {
1563 #ifdef DEBUG_FTP
1564 	perror("select");
1565 #endif
1566 	closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
1567 	return(-1);
1568     }
1569     if (res == 0) {
1570 #ifdef DEBUG_FTP
1571 	xmlGenericError(xmlGenericErrorContext,
1572 		"xmlNanoFTPCloseConnection: timeout\n");
1573 #endif
1574 	closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
1575     } else {
1576 	res = xmlNanoFTPGetResponse(ctxt);
1577 	if (res != 2) {
1578 	    closesocket(ctxt->controlFd); ctxt->controlFd = INVALID_SOCKET;
1579 	    return(-1);
1580 	}
1581     }
1582     return(0);
1583 }
1584 
1585 /**
1586  * xmlNanoFTPParseList:
1587  * @list:  some data listing received from the server
1588  * @callback:  the user callback
1589  * @userData:  the user callback data
1590  *
1591  * Parse at most one entry from the listing.
1592  *
1593  * Returns -1 in case of error, the length of data parsed otherwise
1594  */
1595 
1596 static int
xmlNanoFTPParseList(const char * list,ftpListCallback callback,void * userData)1597 xmlNanoFTPParseList(const char *list, ftpListCallback callback, void *userData) {
1598     const char *cur = list;
1599     char filename[151];
1600     char attrib[11];
1601     char owner[11];
1602     char group[11];
1603     char month[4];
1604     int year = 0;
1605     int minute = 0;
1606     int hour = 0;
1607     int day = 0;
1608     unsigned long size = 0;
1609     int links = 0;
1610     int i;
1611 
1612     if (!strncmp(cur, "total", 5)) {
1613         cur += 5;
1614 	while (*cur == ' ') cur++;
1615 	while ((*cur >= '0') && (*cur <= '9'))
1616 	    links = (links * 10) + (*cur++ - '0');
1617 	while ((*cur == ' ') || (*cur == '\n')  || (*cur == '\r'))
1618 	    cur++;
1619 	return(cur - list);
1620     } else if (*list == '+') {
1621 	return(0);
1622     } else {
1623 	while ((*cur == ' ') || (*cur == '\n')  || (*cur == '\r'))
1624 	    cur++;
1625 	if (*cur == 0) return(0);
1626 	i = 0;
1627 	while (*cur != ' ') {
1628 	    if (i < 10)
1629 		attrib[i++] = *cur;
1630 	    cur++;
1631 	    if (*cur == 0) return(0);
1632 	}
1633 	attrib[10] = 0;
1634 	while (*cur == ' ') cur++;
1635 	if (*cur == 0) return(0);
1636 	while ((*cur >= '0') && (*cur <= '9'))
1637 	    links = (links * 10) + (*cur++ - '0');
1638 	while (*cur == ' ') cur++;
1639 	if (*cur == 0) return(0);
1640 	i = 0;
1641 	while (*cur != ' ') {
1642 	    if (i < 10)
1643 		owner[i++] = *cur;
1644 	    cur++;
1645 	    if (*cur == 0) return(0);
1646 	}
1647 	owner[i] = 0;
1648 	while (*cur == ' ') cur++;
1649 	if (*cur == 0) return(0);
1650 	i = 0;
1651 	while (*cur != ' ') {
1652 	    if (i < 10)
1653 		group[i++] = *cur;
1654 	    cur++;
1655 	    if (*cur == 0) return(0);
1656 	}
1657 	group[i] = 0;
1658 	while (*cur == ' ') cur++;
1659 	if (*cur == 0) return(0);
1660 	while ((*cur >= '0') && (*cur <= '9'))
1661 	    size = (size * 10) + (*cur++ - '0');
1662 	while (*cur == ' ') cur++;
1663 	if (*cur == 0) return(0);
1664 	i = 0;
1665 	while (*cur != ' ') {
1666 	    if (i < 3)
1667 		month[i++] = *cur;
1668 	    cur++;
1669 	    if (*cur == 0) return(0);
1670 	}
1671 	month[i] = 0;
1672 	while (*cur == ' ') cur++;
1673 	if (*cur == 0) return(0);
1674         while ((*cur >= '0') && (*cur <= '9'))
1675 	    day = (day * 10) + (*cur++ - '0');
1676 	while (*cur == ' ') cur++;
1677 	if (*cur == 0) return(0);
1678 	if ((cur[1] == 0) || (cur[2] == 0)) return(0);
1679 	if ((cur[1] == ':') || (cur[2] == ':')) {
1680 	    while ((*cur >= '0') && (*cur <= '9'))
1681 		hour = (hour * 10) + (*cur++ - '0');
1682 	    if (*cur == ':') cur++;
1683 	    while ((*cur >= '0') && (*cur <= '9'))
1684 		minute = (minute * 10) + (*cur++ - '0');
1685 	} else {
1686 	    while ((*cur >= '0') && (*cur <= '9'))
1687 		year = (year * 10) + (*cur++ - '0');
1688 	}
1689 	while (*cur == ' ') cur++;
1690 	if (*cur == 0) return(0);
1691 	i = 0;
1692 	while ((*cur != '\n')  && (*cur != '\r')) {
1693 	    if (i < 150)
1694 		filename[i++] = *cur;
1695 	    cur++;
1696 	    if (*cur == 0) return(0);
1697 	}
1698 	filename[i] = 0;
1699 	if ((*cur != '\n') && (*cur != '\r'))
1700 	    return(0);
1701 	while ((*cur == '\n')  || (*cur == '\r'))
1702 	    cur++;
1703     }
1704     if (callback != NULL) {
1705         callback(userData, filename, attrib, owner, group, size, links,
1706 		 year, month, day, hour, minute);
1707     }
1708     return(cur - list);
1709 }
1710 
1711 /**
1712  * xmlNanoFTPList:
1713  * @ctx:  an FTP context
1714  * @callback:  the user callback
1715  * @userData:  the user callback data
1716  * @filename:  optional files to list
1717  *
1718  * Do a listing on the server. All files info are passed back
1719  * in the callbacks.
1720  *
1721  * Returns -1 in case of error, 0 otherwise
1722  */
1723 
1724 int
xmlNanoFTPList(void * ctx,ftpListCallback callback,void * userData,const char * filename)1725 xmlNanoFTPList(void *ctx, ftpListCallback callback, void *userData,
1726 	       const char *filename) {
1727     xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1728     char buf[4096 + 1];
1729     int len, res;
1730     int indx = 0, base;
1731     fd_set rfd, efd;
1732     struct timeval tv;
1733 
1734     if (ctxt == NULL) return (-1);
1735     if (filename == NULL) {
1736         if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
1737 	    return(-1);
1738 	ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1739 	if (ctxt->dataFd == INVALID_SOCKET)
1740 	    return(-1);
1741 	snprintf(buf, sizeof(buf), "LIST -L\r\n");
1742     } else {
1743 	if (filename[0] != '/') {
1744 	    if (xmlNanoFTPCwd(ctxt, ctxt->path) < 1)
1745 		return(-1);
1746 	}
1747 	ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1748 	if (ctxt->dataFd == INVALID_SOCKET)
1749 	    return(-1);
1750 	snprintf(buf, sizeof(buf), "LIST -L %s\r\n", filename);
1751     }
1752     buf[sizeof(buf) - 1] = 0;
1753     len = strlen(buf);
1754 #ifdef DEBUG_FTP
1755     xmlGenericError(xmlGenericErrorContext, "%s", buf);
1756 #endif
1757     res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
1758     if (res < 0) {
1759 	__xmlIOErr(XML_FROM_FTP, 0, "send failed");
1760 	closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1761 	return(res);
1762     }
1763     res = xmlNanoFTPReadResponse(ctxt);
1764     if (res != 1) {
1765 	closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1766 	return(-res);
1767     }
1768 
1769     do {
1770 	tv.tv_sec = 1;
1771 	tv.tv_usec = 0;
1772 	FD_ZERO(&rfd);
1773 	FD_SET(ctxt->dataFd, &rfd);
1774 	FD_ZERO(&efd);
1775 	FD_SET(ctxt->dataFd, &efd);
1776 	res = select(ctxt->dataFd + 1, &rfd, NULL, &efd, &tv);
1777 	if (res < 0) {
1778 #ifdef DEBUG_FTP
1779 	    perror("select");
1780 #endif
1781 	    closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1782 	    return(-1);
1783 	}
1784 	if (res == 0) {
1785 	    res = xmlNanoFTPCheckResponse(ctxt);
1786 	    if (res < 0) {
1787 		closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1788 		ctxt->dataFd = INVALID_SOCKET;
1789 		return(-1);
1790 	    }
1791 	    if (res == 2) {
1792 		closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1793 		return(0);
1794 	    }
1795 
1796 	    continue;
1797 	}
1798 
1799 	if ((len = recv(ctxt->dataFd, &buf[indx], sizeof(buf) - (indx + 1), 0)) < 0) {
1800 	    __xmlIOErr(XML_FROM_FTP, 0, "recv");
1801 	    closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1802 	    ctxt->dataFd = INVALID_SOCKET;
1803 	    return(-1);
1804 	}
1805 #ifdef DEBUG_FTP
1806         write(1, &buf[indx], len);
1807 #endif
1808 	indx += len;
1809 	buf[indx] = 0;
1810 	base = 0;
1811 	do {
1812 	    res = xmlNanoFTPParseList(&buf[base], callback, userData);
1813 	    base += res;
1814 	} while (res > 0);
1815 
1816 	memmove(&buf[0], &buf[base], indx - base);
1817 	indx -= base;
1818     } while (len != 0);
1819     xmlNanoFTPCloseConnection(ctxt);
1820     return(0);
1821 }
1822 
1823 /**
1824  * xmlNanoFTPGetSocket:
1825  * @ctx:  an FTP context
1826  * @filename:  the file to retrieve (or NULL if path is in context).
1827  *
1828  * Initiate fetch of the given file from the server.
1829  *
1830  * Returns the socket for the data connection, or <0 in case of error
1831  */
1832 
1833 
1834 SOCKET
xmlNanoFTPGetSocket(void * ctx,const char * filename)1835 xmlNanoFTPGetSocket(void *ctx, const char *filename) {
1836     xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1837     char buf[300];
1838     int res, len;
1839     if (ctx == NULL)
1840 	return INVALID_SOCKET;
1841     if ((filename == NULL) && (ctxt->path == NULL))
1842 	return INVALID_SOCKET;
1843     ctxt->dataFd = xmlNanoFTPGetConnection(ctxt);
1844     if (ctxt->dataFd == INVALID_SOCKET)
1845 	return INVALID_SOCKET;
1846 
1847     snprintf(buf, sizeof(buf), "TYPE I\r\n");
1848     len = strlen(buf);
1849 #ifdef DEBUG_FTP
1850     xmlGenericError(xmlGenericErrorContext, "%s", buf);
1851 #endif
1852     res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
1853     if (res < 0) {
1854 	__xmlIOErr(XML_FROM_FTP, 0, "send failed");
1855 	closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1856 	return INVALID_SOCKET;
1857     }
1858     res = xmlNanoFTPReadResponse(ctxt);
1859     if (res != 2) {
1860 	closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1861 	return INVALID_SOCKET;
1862     }
1863     if (filename == NULL)
1864 	snprintf(buf, sizeof(buf), "RETR %s\r\n", ctxt->path);
1865     else
1866 	snprintf(buf, sizeof(buf), "RETR %s\r\n", filename);
1867     buf[sizeof(buf) - 1] = 0;
1868     len = strlen(buf);
1869 #ifdef DEBUG_FTP
1870     xmlGenericError(xmlGenericErrorContext, "%s", buf);
1871 #endif
1872     res = send(ctxt->controlFd, SEND_ARG2_CAST buf, len, 0);
1873     if (res < 0) {
1874 	__xmlIOErr(XML_FROM_FTP, 0, "send failed");
1875 	closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1876 	return INVALID_SOCKET;
1877     }
1878     res = xmlNanoFTPReadResponse(ctxt);
1879     if (res != 1) {
1880 	closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1881 	return INVALID_SOCKET;
1882     }
1883     return(ctxt->dataFd);
1884 }
1885 
1886 /**
1887  * xmlNanoFTPGet:
1888  * @ctx:  an FTP context
1889  * @callback:  the user callback
1890  * @userData:  the user callback data
1891  * @filename:  the file to retrieve
1892  *
1893  * Fetch the given file from the server. All data are passed back
1894  * in the callbacks. The last callback has a size of 0 block.
1895  *
1896  * Returns -1 in case of error, 0 otherwise
1897  */
1898 
1899 int
xmlNanoFTPGet(void * ctx,ftpDataCallback callback,void * userData,const char * filename)1900 xmlNanoFTPGet(void *ctx, ftpDataCallback callback, void *userData,
1901 	      const char *filename) {
1902     xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1903     char buf[4096];
1904     int len = 0, res;
1905     fd_set rfd;
1906     struct timeval tv;
1907 
1908     if (ctxt == NULL) return(-1);
1909     if ((filename == NULL) && (ctxt->path == NULL))
1910 	return(-1);
1911     if (callback == NULL)
1912 	return(-1);
1913     if (xmlNanoFTPGetSocket(ctxt, filename) == INVALID_SOCKET)
1914 	return(-1);
1915 
1916     do {
1917 	tv.tv_sec = 1;
1918 	tv.tv_usec = 0;
1919 	FD_ZERO(&rfd);
1920 	FD_SET(ctxt->dataFd, &rfd);
1921 	res = select(ctxt->dataFd + 1, &rfd, NULL, NULL, &tv);
1922 	if (res < 0) {
1923 #ifdef DEBUG_FTP
1924 	    perror("select");
1925 #endif
1926 	    closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1927 	    return(-1);
1928 	}
1929 	if (res == 0) {
1930 	    res = xmlNanoFTPCheckResponse(ctxt);
1931 	    if (res < 0) {
1932 		closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1933 		ctxt->dataFd = INVALID_SOCKET;
1934 		return(-1);
1935 	    }
1936 	    if (res == 2) {
1937 		closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1938 		return(0);
1939 	    }
1940 
1941 	    continue;
1942 	}
1943 	if ((len = recv(ctxt->dataFd, buf, sizeof(buf), 0)) < 0) {
1944 	    __xmlIOErr(XML_FROM_FTP, 0, "recv failed");
1945 	    callback(userData, buf, len);
1946 	    closesocket(ctxt->dataFd); ctxt->dataFd = INVALID_SOCKET;
1947 	    return(-1);
1948 	}
1949 	callback(userData, buf, len);
1950     } while (len != 0);
1951 
1952     return(xmlNanoFTPCloseConnection(ctxt));
1953 }
1954 
1955 /**
1956  * xmlNanoFTPRead:
1957  * @ctx:  the FTP context
1958  * @dest:  a buffer
1959  * @len:  the buffer length
1960  *
1961  * This function tries to read @len bytes from the existing FTP connection
1962  * and saves them in @dest. This is a blocking call.
1963  *
1964  * Returns the number of byte read. 0 is an indication of an end of connection.
1965  *         -1 indicates a parameter error.
1966  */
1967 int
xmlNanoFTPRead(void * ctx,void * dest,int len)1968 xmlNanoFTPRead(void *ctx, void *dest, int len) {
1969     xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
1970 
1971     if (ctx == NULL) return(-1);
1972     if (ctxt->dataFd == INVALID_SOCKET) return(0);
1973     if (dest == NULL) return(-1);
1974     if (len <= 0) return(0);
1975 
1976     len = recv(ctxt->dataFd, dest, len, 0);
1977     if (len <= 0) {
1978 	if (len < 0)
1979 	    __xmlIOErr(XML_FROM_FTP, 0, "recv failed");
1980 	xmlNanoFTPCloseConnection(ctxt);
1981     }
1982 #ifdef DEBUG_FTP
1983     xmlGenericError(xmlGenericErrorContext, "Recvd %d bytes\n", len);
1984 #endif
1985     return(len);
1986 }
1987 
1988 /**
1989  * xmlNanoFTPOpen:
1990  * @URL: the URL to the resource
1991  *
1992  * Start to fetch the given ftp:// resource
1993  *
1994  * Returns an FTP context, or NULL
1995  */
1996 
1997 void*
xmlNanoFTPOpen(const char * URL)1998 xmlNanoFTPOpen(const char *URL) {
1999     xmlNanoFTPCtxtPtr ctxt;
2000     SOCKET sock;
2001 
2002     xmlNanoFTPInit();
2003     if (URL == NULL) return(NULL);
2004     if (strncmp("ftp://", URL, 6)) return(NULL);
2005 
2006     ctxt = (xmlNanoFTPCtxtPtr) xmlNanoFTPNewCtxt(URL);
2007     if (ctxt == NULL) return(NULL);
2008     if (xmlNanoFTPConnect(ctxt) < 0) {
2009 	xmlNanoFTPFreeCtxt(ctxt);
2010 	return(NULL);
2011     }
2012     sock = xmlNanoFTPGetSocket(ctxt, ctxt->path);
2013     if (sock == INVALID_SOCKET) {
2014 	xmlNanoFTPFreeCtxt(ctxt);
2015 	return(NULL);
2016     }
2017     return(ctxt);
2018 }
2019 
2020 /**
2021  * xmlNanoFTPClose:
2022  * @ctx: an FTP context
2023  *
2024  * Close the connection and both control and transport
2025  *
2026  * Returns -1 in case of error, 0 otherwise
2027  */
2028 
2029 int
xmlNanoFTPClose(void * ctx)2030 xmlNanoFTPClose(void *ctx) {
2031     xmlNanoFTPCtxtPtr ctxt = (xmlNanoFTPCtxtPtr) ctx;
2032 
2033     if (ctxt == NULL)
2034 	return(-1);
2035 
2036     if (ctxt->dataFd != INVALID_SOCKET) {
2037 	closesocket(ctxt->dataFd);
2038 	ctxt->dataFd = INVALID_SOCKET;
2039     }
2040     if (ctxt->controlFd != INVALID_SOCKET) {
2041 	xmlNanoFTPQuit(ctxt);
2042 	closesocket(ctxt->controlFd);
2043 	ctxt->controlFd = INVALID_SOCKET;
2044     }
2045     xmlNanoFTPFreeCtxt(ctxt);
2046     return(0);
2047 }
2048 
2049 #ifdef STANDALONE
2050 /************************************************************************
2051  *									*
2052  *			Basic test in Standalone mode			*
2053  *									*
2054  ************************************************************************/
2055 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)2056 void ftpList(void *userData, const char *filename, const char* attrib,
2057 	     const char *owner, const char *group, unsigned long size, int links,
2058 	     int year, const char *month, int day, int hour, int minute) {
2059     xmlGenericError(xmlGenericErrorContext,
2060 	    "%s %s %s %ld %s\n", attrib, owner, group, size, filename);
2061 }
2062 static
ftpData(void * userData,const char * data,int len)2063 void ftpData(void *userData, const char *data, int len) {
2064     if (userData == NULL) return;
2065     if (len <= 0) {
2066 	fclose((FILE*)userData);
2067 	return;
2068     }
2069     fwrite(data, len, 1, (FILE*)userData);
2070 }
2071 
main(int argc,char ** argv)2072 int main(int argc, char **argv) {
2073     void *ctxt;
2074     FILE *output;
2075     char *tstfile = NULL;
2076 
2077     xmlNanoFTPInit();
2078     if (argc > 1) {
2079 	ctxt = xmlNanoFTPNewCtxt(argv[1]);
2080 	if (xmlNanoFTPConnect(ctxt) < 0) {
2081 	    xmlGenericError(xmlGenericErrorContext,
2082 		    "Couldn't connect to %s\n", argv[1]);
2083 	    exit(1);
2084 	}
2085 	if (argc > 2)
2086 	    tstfile = argv[2];
2087     } else
2088 	ctxt = xmlNanoFTPConnectTo("localhost", 0);
2089     if (ctxt == NULL) {
2090         xmlGenericError(xmlGenericErrorContext,
2091 		"Couldn't connect to localhost\n");
2092         exit(1);
2093     }
2094     xmlNanoFTPList(ctxt, ftpList, NULL, tstfile);
2095     output = fopen("/tmp/tstdata", "w");
2096     if (output != NULL) {
2097 	if (xmlNanoFTPGet(ctxt, ftpData, (void *) output, tstfile) < 0)
2098 	    xmlGenericError(xmlGenericErrorContext,
2099 		    "Failed to get file\n");
2100 
2101     }
2102     xmlNanoFTPClose(ctxt);
2103     xmlMemoryDump();
2104     exit(0);
2105 }
2106 #endif /* STANDALONE */
2107 #else /* !LIBXML_FTP_ENABLED */
2108 #ifdef STANDALONE
2109 #include <stdio.h>
main(int argc,char ** argv)2110 int main(int argc, char **argv) {
2111     xmlGenericError(xmlGenericErrorContext,
2112 	    "%s : FTP support not compiled in\n", argv[0]);
2113     return(0);
2114 }
2115 #endif /* STANDALONE */
2116 #endif /* LIBXML_FTP_ENABLED */
2117 #define bottom_nanoftp
2118 #include "elfgcchack.h"
2119