Annotation of XML/nanoftp.c, revision 1.25

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

Webmaster