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