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