source: trunk/third/libxml/nanoftp.c @ 15360

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