Annotation of XML/nanoftp.c, revision 1.28

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

Webmaster