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