source: trunk/third/libxml2/nanoftp.c @ 19097

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