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