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