Annotation of XML/nanohttp.c, revision 1.27

1.1       daniel      1: /*
1.5       daniel      2:  * nanohttp.c: minimalist HTTP GET implementation to fetch external subsets.
                      3:  *             focuses on size, streamability, reentrancy and portability
                      4:  *
                      5:  * This is clearly not a general purpose HTTP implementation
                      6:  * If you look for one, check:
                      7:  *         http://www.w3.org/Library/
1.1       daniel      8:  *
                      9:  * See Copyright for the status of this software.
                     10:  *
                     11:  * Daniel.Veillard@w3.org
                     12:  */
                     13:  
1.5       daniel     14: /* TODO add compression support, Send the Accept- , and decompress on the
                     15:         fly with ZLIB if found at compile-time */
                     16: 
1.9       daniel     17: #ifdef WIN32
1.11      daniel     18: #define INCLUDE_WINSOCK
1.9       daniel     19: #include "win32config.h"
                     20: #else
1.4       daniel     21: #include "config.h"
                     22: #endif
1.9       daniel     23: 
1.25      veillard   24: #include <libxml/xmlversion.h>
1.4       daniel     25: 
1.17      daniel     26: #ifdef LIBXML_HTTP_ENABLED
1.1       daniel     27: #include <stdio.h>
                     28: #include <string.h>
1.4       daniel     29: 
                     30: #ifdef HAVE_STDLIB_H
1.1       daniel     31: #include <stdlib.h>
1.4       daniel     32: #endif
                     33: #ifdef HAVE_UNISTD_H
1.1       daniel     34: #include <unistd.h>
1.4       daniel     35: #endif
                     36: #ifdef HAVE_SYS_SOCKET_H
1.1       daniel     37: #include <sys/socket.h>
1.4       daniel     38: #endif
                     39: #ifdef HAVE_NETINET_IN_H
1.1       daniel     40: #include <netinet/in.h>
1.4       daniel     41: #endif
                     42: #ifdef HAVE_ARPA_INET_H
1.1       daniel     43: #include <arpa/inet.h>
1.4       daniel     44: #endif
                     45: #ifdef HAVE_NETDB_H
1.1       daniel     46: #include <netdb.h>
1.4       daniel     47: #endif
                     48: #ifdef HAVE_FCNTL_H
1.1       daniel     49: #include <fcntl.h> 
1.4       daniel     50: #endif
                     51: #ifdef HAVE_ERRNO_H
1.1       daniel     52: #include <errno.h>
1.4       daniel     53: #endif
                     54: #ifdef HAVE_SYS_TIME_H
1.1       daniel     55: #include <sys/time.h>
1.4       daniel     56: #endif
                     57: #ifdef HAVE_SYS_SELECT_H
1.1       daniel     58: #include <sys/select.h>
1.4       daniel     59: #endif
1.15      daniel     60: #ifdef HAVE_STRINGS_H
                     61: #include <strings.h>
                     62: #endif
1.1       daniel     63: 
1.17      daniel     64: #include <libxml/xmlmemory.h>
1.23      veillard   65: #include <libxml/parser.h> /* for xmlStr(n)casecmp() */
1.17      daniel     66: #include <libxml/nanohttp.h>
1.7       daniel     67: 
1.5       daniel     68: #ifdef STANDALONE
                     69: #define DEBUG_HTTP
1.23      veillard   70: #define xmlStrncasecmp(a, b, n) strncasecmp((char *)a, (char *)b, n)
                     71: #define xmlStrcasecmpi(a, b) strcasecmp((char *)a, (char *)b)
1.5       daniel     72: #endif
                     73: 
1.1       daniel     74: #define XML_NANO_HTTP_MAX_REDIR        10
                     75: 
                     76: #define XML_NANO_HTTP_CHUNK    4096
                     77: 
                     78: #define XML_NANO_HTTP_CLOSED   0
                     79: #define XML_NANO_HTTP_WRITE    1
                     80: #define XML_NANO_HTTP_READ     2
                     81: #define XML_NANO_HTTP_NONE     4
                     82: 
                     83: typedef struct xmlNanoHTTPCtxt {
                     84:     char *protocol;    /* the protocol name */
                     85:     char *hostname;    /* the host name */
                     86:     int port;          /* the port */
                     87:     char *path;                /* the path within the URL */
                     88:     int fd;            /* the file descriptor for the socket */
                     89:     int state;         /* WRITE / READ / CLOSED */
                     90:     char *out;         /* buffer sent (zero terminated) */
                     91:     char *outptr;      /* index within the buffer sent */
                     92:     char *in;          /* the receiving buffer */
                     93:     char *content;     /* the start of the content */
                     94:     char *inptr;       /* the next byte to read from network */
                     95:     char *inrptr;      /* the next byte to give back to the client */
                     96:     int inlen;         /* len of the input buffer */
                     97:     int last;          /* return code for last operation */
                     98:     int returnValue;   /* the protocol return value */
                     99:     char *contentType; /* the MIME type for the input */
                    100:     char *location;    /* the new URL in case of redirect */
                    101: } xmlNanoHTTPCtxt, *xmlNanoHTTPCtxtPtr;
                    102: 
1.12      daniel    103: static int initialized = 0;
                    104: static char *proxy = NULL;     /* the proxy name if any */
                    105: static int proxyPort;  /* the proxy port if any */
                    106: 
                    107: /**
1.26      veillard  108:  * A bit of portability macros and functions
                    109:  */
                    110: #ifdef _WINSOCKAPI_
                    111: 
                    112: WSADATA wsaData;
                    113: 
                    114: #else
                    115: 
                    116: #define closesocket(s) close(s)
                    117: 
                    118: #endif
                    119: 
                    120: int socket_errno(void) {
                    121: #ifdef _WINSOCKAPI_
                    122:     return(WSAGetLastError());
                    123: #else
                    124:     return(errno);
                    125: #endif
                    126: }
                    127: 
                    128: /**
1.12      daniel    129:  * xmlNanoHTTPInit:
                    130:  *
                    131:  * Initialize the HTTP protocol layer.
                    132:  * Currently it just checks for proxy informations
                    133:  */
                    134: 
                    135: void
                    136: xmlNanoHTTPInit(void) {
                    137:     const char *env;
                    138: 
                    139:     if (initialized)
                    140:        return;
                    141: 
1.26      veillard  142: #ifdef _WINSOCKAPI_
                    143:     if (WSAStartup(0x0101, &wsaData) != 0)
                    144:        WSACleanup();
                    145: #endif
                    146: 
1.13      daniel    147:     if (proxy == NULL) {
                    148:        proxyPort = 80;
                    149:        env = getenv("no_proxy");
                    150:        if (env != NULL)
                    151:            goto done;
                    152:        env = getenv("http_proxy");
                    153:        if (env != NULL) {
                    154:            xmlNanoHTTPScanProxy(env);
                    155:            goto done;
                    156:        }
                    157:        env = getenv("HTTP_PROXY");
                    158:        if (env != NULL) {
                    159:            xmlNanoHTTPScanProxy(env);
                    160:            goto done;
                    161:        }
1.12      daniel    162:     }
1.13      daniel    163: done:
1.12      daniel    164:     initialized = 1;
                    165: }
                    166: 
                    167: /**
                    168:  * xmlNanoHTTPClenup:
                    169:  *
                    170:  * Cleanup the HTTP protocol layer.
                    171:  */
                    172: 
                    173: void
                    174: xmlNanoHTTPCleanup(void) {
                    175:     if (proxy != NULL)
                    176:        xmlFree(proxy);
                    177:     initialized = 0;
1.26      veillard  178: #ifdef _WINSOCKAPI_
                    179:     WSACleanup();
                    180: #endif
1.12      daniel    181:     return;
                    182: }
                    183: 
1.5       daniel    184: /**
                    185:  * xmlNanoHTTPScanURL:
                    186:  * @ctxt:  an HTTP context
                    187:  * @URL:  The URL used to initialize the context
                    188:  *
                    189:  * (Re)Initialize an HTTP context by parsing the URL and finding
                    190:  * the protocol host port and path it indicates.
                    191:  */
                    192: 
                    193: static void
                    194: xmlNanoHTTPScanURL(xmlNanoHTTPCtxtPtr ctxt, const char *URL) {
1.1       daniel    195:     const char *cur = URL;
                    196:     char buf[4096];
                    197:     int index = 0;
                    198:     int port = 0;
                    199: 
                    200:     if (ctxt->protocol != NULL) { 
1.7       daniel    201:         xmlFree(ctxt->protocol);
1.1       daniel    202:        ctxt->protocol = NULL;
                    203:     }
                    204:     if (ctxt->hostname != NULL) { 
1.7       daniel    205:         xmlFree(ctxt->hostname);
1.1       daniel    206:        ctxt->hostname = NULL;
                    207:     }
                    208:     if (ctxt->path != NULL) { 
1.7       daniel    209:         xmlFree(ctxt->path);
1.1       daniel    210:        ctxt->path = NULL;
                    211:     }
1.12      daniel    212:     if (URL == NULL) return;
1.1       daniel    213:     buf[index] = 0;
                    214:     while (*cur != 0) {
                    215:         if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
                    216:            buf[index] = 0;
1.7       daniel    217:            ctxt->protocol = xmlMemStrdup(buf);
1.1       daniel    218:            index = 0;
                    219:             cur += 3;
                    220:            break;
                    221:        }
                    222:        buf[index++] = *cur++;
                    223:     }
                    224:     if (*cur == 0) return;
                    225: 
                    226:     buf[index] = 0;
                    227:     while (1) {
                    228:         if (cur[0] == ':') {
                    229:            buf[index] = 0;
1.7       daniel    230:            ctxt->hostname = xmlMemStrdup(buf);
1.1       daniel    231:            index = 0;
                    232:            cur += 1;
                    233:            while ((*cur >= '0') && (*cur <= '9')) {
                    234:                port *= 10;
                    235:                port += *cur - '0';
                    236:                cur++;
                    237:            }
                    238:            if (port != 0) ctxt->port = port;
                    239:            while ((cur[0] != '/') && (*cur != 0)) 
                    240:                cur++;
                    241:            break;
                    242:        }
                    243:         if ((*cur == '/') || (*cur == 0)) {
                    244:            buf[index] = 0;
1.7       daniel    245:            ctxt->hostname = xmlMemStrdup(buf);
1.1       daniel    246:            index = 0;
                    247:            break;
                    248:        }
                    249:        buf[index++] = *cur++;
                    250:     }
                    251:     if (*cur == 0) 
1.7       daniel    252:         ctxt->path = xmlMemStrdup("/");
1.5       daniel    253:     else {
1.14      daniel    254:         index = 0;
1.5       daniel    255:         buf[index] = 0;
1.14      daniel    256:        while (*cur != 0)
1.5       daniel    257:            buf[index++] = *cur++;
                    258:        buf[index] = 0;
1.7       daniel    259:        ctxt->path = xmlMemStrdup(buf);
1.5       daniel    260:     }  
1.1       daniel    261: }
                    262: 
1.5       daniel    263: /**
1.12      daniel    264:  * xmlNanoHTTPScanProxy:
                    265:  * @URL:  The proxy URL used to initialize the proxy context
                    266:  *
                    267:  * (Re)Initialize the HTTP Proxy context by parsing the URL and finding
                    268:  * the protocol host port it indicates.
                    269:  * Should be like http://myproxy/ or http://myproxy:3128/
                    270:  * A NULL URL cleans up proxy informations.
                    271:  */
                    272: 
                    273: void
                    274: xmlNanoHTTPScanProxy(const char *URL) {
                    275:     const char *cur = URL;
                    276:     char buf[4096];
                    277:     int index = 0;
                    278:     int port = 0;
                    279: 
                    280:     if (proxy != NULL) { 
                    281:         xmlFree(proxy);
                    282:        proxy = NULL;
                    283:     }
                    284:     if (proxyPort != 0) { 
                    285:        proxyPort = 0;
                    286:     }
                    287: #ifdef DEBUG_HTTP
                    288:     if (URL == NULL)
                    289:        printf("Removing HTTP proxy info\n");
                    290:     else
                    291:        printf("Using HTTP proxy %s\n", URL);
                    292: #endif
                    293:     if (URL == NULL) return;
                    294:     buf[index] = 0;
                    295:     while (*cur != 0) {
                    296:         if ((cur[0] == ':') && (cur[1] == '/') && (cur[2] == '/')) {
                    297:            buf[index] = 0;
                    298:            index = 0;
                    299:             cur += 3;
                    300:            break;
                    301:        }
                    302:        buf[index++] = *cur++;
                    303:     }
                    304:     if (*cur == 0) return;
                    305: 
                    306:     buf[index] = 0;
                    307:     while (1) {
                    308:         if (cur[0] == ':') {
                    309:            buf[index] = 0;
                    310:            proxy = xmlMemStrdup(buf);
                    311:            index = 0;
                    312:            cur += 1;
                    313:            while ((*cur >= '0') && (*cur <= '9')) {
                    314:                port *= 10;
                    315:                port += *cur - '0';
                    316:                cur++;
                    317:            }
                    318:            if (port != 0) proxyPort = port;
                    319:            while ((cur[0] != '/') && (*cur != 0)) 
                    320:                cur++;
                    321:            break;
                    322:        }
                    323:         if ((*cur == '/') || (*cur == 0)) {
                    324:            buf[index] = 0;
                    325:            proxy = xmlMemStrdup(buf);
                    326:            index = 0;
                    327:            break;
                    328:        }
                    329:        buf[index++] = *cur++;
                    330:     }
                    331: }
                    332: 
                    333: /**
1.5       daniel    334:  * xmlNanoHTTPNewCtxt:
                    335:  * @URL:  The URL used to initialize the context
                    336:  *
                    337:  * Allocate and initialize a new HTTP context.
                    338:  *
                    339:  * Returns an HTTP context or NULL in case of error.
                    340:  */
                    341: 
                    342: static xmlNanoHTTPCtxtPtr
                    343: xmlNanoHTTPNewCtxt(const char *URL) {
1.1       daniel    344:     xmlNanoHTTPCtxtPtr ret;
                    345: 
1.7       daniel    346:     ret = (xmlNanoHTTPCtxtPtr) xmlMalloc(sizeof(xmlNanoHTTPCtxt));
1.1       daniel    347:     if (ret == NULL) return(NULL);
                    348: 
                    349:     memset(ret, 0, sizeof(xmlNanoHTTPCtxt));
                    350:     ret->port = 80;
                    351:     ret->returnValue = 0;
                    352: 
                    353:     xmlNanoHTTPScanURL(ret, URL);
                    354: 
                    355:     return(ret);
                    356: }
                    357: 
1.5       daniel    358: /**
                    359:  * xmlNanoHTTPFreeCtxt:
                    360:  * @ctxt:  an HTTP context
                    361:  *
                    362:  * Frees the context after closing the connection.
                    363:  */
                    364: 
                    365: static void
                    366: xmlNanoHTTPFreeCtxt(xmlNanoHTTPCtxtPtr ctxt) {
                    367:     if (ctxt == NULL) return;
1.7       daniel    368:     if (ctxt->hostname != NULL) xmlFree(ctxt->hostname);
                    369:     if (ctxt->protocol != NULL) xmlFree(ctxt->protocol);
                    370:     if (ctxt->path != NULL) xmlFree(ctxt->path);
                    371:     if (ctxt->out != NULL) xmlFree(ctxt->out);
                    372:     if (ctxt->in != NULL) xmlFree(ctxt->in);
                    373:     if (ctxt->contentType != NULL) xmlFree(ctxt->contentType);
                    374:     if (ctxt->location != NULL) xmlFree(ctxt->location);
1.1       daniel    375:     ctxt->state = XML_NANO_HTTP_NONE;
1.26      veillard  376:     if (ctxt->fd >= 0) closesocket(ctxt->fd);
1.1       daniel    377:     ctxt->fd = -1;
1.7       daniel    378:     xmlFree(ctxt);
1.1       daniel    379: }
                    380: 
1.5       daniel    381: /**
                    382:  * xmlNanoHTTPSend:
                    383:  * @ctxt:  an HTTP context
                    384:  *
                    385:  * Send the input needed to initiate the processing on the server side
                    386:  */
                    387: 
                    388: static void
                    389: xmlNanoHTTPSend(xmlNanoHTTPCtxtPtr ctxt) {
1.1       daniel    390:     if (ctxt->state & XML_NANO_HTTP_WRITE)
1.26      veillard  391:        ctxt->last = send(ctxt->fd, ctxt->outptr, strlen(ctxt->outptr), 0);
1.1       daniel    392: }
                    393: 
1.5       daniel    394: /**
                    395:  * xmlNanoHTTPRecv:
                    396:  * @ctxt:  an HTTP context
                    397:  *
                    398:  * Read information coming from the HTTP connection.
                    399:  * This is a blocking call (but it blocks in select(), not read()).
                    400:  *
                    401:  * Returns the number of byte read or -1 in case of error.
                    402:  */
                    403: 
                    404: static int
                    405: xmlNanoHTTPRecv(xmlNanoHTTPCtxtPtr ctxt) {
1.1       daniel    406:     fd_set rfd;
                    407:     struct timeval tv;
                    408: 
                    409: 
                    410:     while (ctxt->state & XML_NANO_HTTP_READ) {
                    411:        if (ctxt->in == NULL) {
1.7       daniel    412:            ctxt->in = (char *) xmlMalloc(65000 * sizeof(char));
1.1       daniel    413:            if (ctxt->in == NULL) {
                    414:                ctxt->last = -1;
                    415:                return(-1);
                    416:            }
                    417:            ctxt->inlen = 65000;
                    418:            ctxt->inptr = ctxt->content = ctxt->inrptr = ctxt->in;
                    419:        }
                    420:        if (ctxt->inrptr > ctxt->in + XML_NANO_HTTP_CHUNK) {
                    421:            int delta = ctxt->inrptr - ctxt->in;
                    422:            int len = ctxt->inptr - ctxt->inrptr;
                    423:            
                    424:            memmove(ctxt->in, ctxt->inrptr, len);
                    425:            ctxt->inrptr -= delta;
                    426:            ctxt->content -= delta;
                    427:            ctxt->inptr -= delta;
                    428:        }
                    429:         if ((ctxt->in + ctxt->inlen) < (ctxt->inptr + XML_NANO_HTTP_CHUNK)) {
                    430:            int d_inptr = ctxt->inptr - ctxt->in;
                    431:            int d_content = ctxt->content - ctxt->in;
                    432:            int d_inrptr = ctxt->inrptr - ctxt->in;
                    433: 
                    434:            ctxt->inlen *= 2;
1.7       daniel    435:             ctxt->in = (char *) xmlRealloc(ctxt->in, ctxt->inlen);
1.1       daniel    436:            if (ctxt->in == NULL) {
                    437:                ctxt->last = -1;
                    438:                return(-1);
                    439:            }
                    440:             ctxt->inptr = ctxt->in + d_inptr;
                    441:             ctxt->content = ctxt->in + d_content;
                    442:             ctxt->inrptr = ctxt->in + d_inrptr;
                    443:        }
1.26      veillard  444:        ctxt->last = recv(ctxt->fd, ctxt->inptr, XML_NANO_HTTP_CHUNK, 0);
1.1       daniel    445:        if (ctxt->last > 0) {
                    446:            ctxt->inptr += ctxt->last;
                    447:            return(ctxt->last);
                    448:        }
                    449:        if (ctxt->last == 0) {
                    450:            return(0);
                    451:        }
1.26      veillard  452:        if (ctxt->last == -1) {
                    453:            switch (socket_errno()) {
                    454:                case EINPROGRESS:
                    455:                case EWOULDBLOCK:
                    456: #if defined(EAGAIN) && EAGAIN != EWOULDBLOCK
                    457:                case EAGAIN:
                    458: #endif
                    459:                    break;
                    460:                default:
                    461:                    return(0);
                    462:            }
1.1       daniel    463:        }
1.26      veillard  464: 
                    465:        tv.tv_sec = 10;
                    466:        tv.tv_usec = 0;
1.1       daniel    467:        FD_ZERO(&rfd);
                    468:        FD_SET(ctxt->fd, &rfd);
                    469:        
1.26      veillard  470:        if (select(ctxt->fd+1, &rfd, NULL, NULL, &tv)<1)
1.5       daniel    471:                return(0);
1.1       daniel    472:     }
                    473:     return(0);
                    474: }
                    475: 
1.5       daniel    476: /**
                    477:  * xmlNanoHTTPReadLine:
                    478:  * @ctxt:  an HTTP context
                    479:  *
                    480:  * Read one line in the HTTP server output, usually for extracting
                    481:  * the HTTP protocol informations from the answer header.
                    482:  *
                    483:  * Returns a newly allocated string with a copy of the line, or NULL
                    484:  *         which indicate the end of the input.
                    485:  */
                    486: 
                    487: static char *
                    488: xmlNanoHTTPReadLine(xmlNanoHTTPCtxtPtr ctxt) {
                    489:     char buf[4096];
1.26      veillard  490:     char *bp = buf;
1.1       daniel    491:     
1.26      veillard  492:     while (bp - buf < 4095) {
                    493:        if (ctxt->inrptr == ctxt->inptr) {
1.1       daniel    494:            if (xmlNanoHTTPRecv(ctxt) == 0) {
                    495:                if (bp == buf)
1.5       daniel    496:                    return(NULL);
1.1       daniel    497:                else
                    498:                    *bp = 0;
1.7       daniel    499:                return(xmlMemStrdup(buf));
1.1       daniel    500:            }
                    501:        }
                    502:        *bp = *ctxt->inrptr++;
1.26      veillard  503:        if (*bp == '\n') {
1.1       daniel    504:            *bp = 0;
1.7       daniel    505:            return(xmlMemStrdup(buf));
1.1       daniel    506:        }
1.26      veillard  507:        if (*bp != '\r')
1.1       daniel    508:            bp++;
                    509:     }
                    510:     buf[4095] = 0;
1.7       daniel    511:     return(xmlMemStrdup(buf));
1.1       daniel    512: }
                    513: 
1.5       daniel    514: 
                    515: /**
                    516:  * xmlNanoHTTPScanAnswer:
                    517:  * @ctxt:  an HTTP context
                    518:  * @line:  an HTTP header line
                    519:  *
                    520:  * Try to extract useful informations from the server answer.
                    521:  * We currently parse and process:
                    522:  *  - The HTTP revision/ return code
                    523:  *  - The Content-Type
                    524:  *  - The Location for redirrect processing.
                    525:  *
                    526:  * Returns -1 in case of failure, the file descriptor number otherwise
                    527:  */
                    528: 
                    529: static void
                    530: xmlNanoHTTPScanAnswer(xmlNanoHTTPCtxtPtr ctxt, const char *line) {
1.1       daniel    531:     const char *cur = line;
                    532: 
                    533:     if (line == NULL) return;
                    534: 
                    535:     if (!strncmp(line, "HTTP/", 5)) {
                    536:         int version = 0;
                    537:        int ret = 0;
                    538: 
                    539:        cur += 5;
                    540:        while ((*cur >= '0') && (*cur <= '9')) {
                    541:            version *= 10;
                    542:            version += *cur - '0';
                    543:            cur++;
                    544:        }
                    545:        if (*cur == '.') {
                    546:            cur++;
                    547:            if ((*cur >= '0') && (*cur <= '9')) {
                    548:                version *= 10;
                    549:                version += *cur - '0';
                    550:                cur++;
                    551:            }
                    552:            while ((*cur >= '0') && (*cur <= '9'))
                    553:                cur++;
                    554:        } else
                    555:            version *= 10;
                    556:        if ((*cur != ' ') && (*cur != '\t')) return;
                    557:        while ((*cur == ' ') || (*cur == '\t')) cur++;
                    558:        if ((*cur < '0') || (*cur > '9')) return;
                    559:        while ((*cur >= '0') && (*cur <= '9')) {
                    560:            ret *= 10;
                    561:            ret += *cur - '0';
                    562:            cur++;
                    563:        }
                    564:        if ((*cur != 0) && (*cur != ' ') && (*cur != '\t')) return;
                    565:        ctxt->returnValue = ret;
1.23      veillard  566:     } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Content-Type:", 13)) {
1.1       daniel    567:         cur += 13;
                    568:        while ((*cur == ' ') || (*cur == '\t')) cur++;
                    569:        if (ctxt->contentType != NULL)
1.7       daniel    570:            xmlFree(ctxt->contentType);
                    571:        ctxt->contentType = xmlMemStrdup(cur);
1.23      veillard  572:     } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"ContentType:", 12)) {
1.1       daniel    573:         cur += 12;
                    574:        if (ctxt->contentType != NULL) return;
                    575:        while ((*cur == ' ') || (*cur == '\t')) cur++;
1.7       daniel    576:        ctxt->contentType = xmlMemStrdup(cur);
1.23      veillard  577:     } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Location:", 9)) {
1.1       daniel    578:         cur += 9;
                    579:        while ((*cur == ' ') || (*cur == '\t')) cur++;
                    580:        if (ctxt->location != NULL)
1.7       daniel    581:            xmlFree(ctxt->location);
                    582:        ctxt->location = xmlMemStrdup(cur);
1.1       daniel    583:     }
                    584: }
                    585: 
1.5       daniel    586: /**
                    587:  * xmlNanoHTTPConnectAttempt:
                    588:  * @ia:  an internet adress structure
                    589:  * @port:  the port number
                    590:  *
                    591:  * Attempt a connection to the given IP:port endpoint. It forces
                    592:  * non-blocking semantic on the socket, and allow 60 seconds for
                    593:  * the host to answer.
                    594:  *
                    595:  * Returns -1 in case of failure, the file descriptor number otherwise
                    596:  */
                    597: 
                    598: static int
                    599: xmlNanoHTTPConnectAttempt(struct in_addr ia, int port)
1.1       daniel    600: {
                    601:     int s=socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
                    602:     struct sockaddr_in sin;
                    603:     fd_set wfd;
                    604:     struct timeval tv;
1.2       daniel    605:     int status;
1.1       daniel    606:     
1.26      veillard  607:     if (s==-1) {
1.5       daniel    608: #ifdef DEBUG_HTTP
1.1       daniel    609:        perror("socket");
1.5       daniel    610: #endif
1.1       daniel    611:        return(-1);
                    612:     }
                    613:     
1.2       daniel    614: #ifdef _WINSOCKAPI_
                    615:     {
                    616:        u_long one = 1;
                    617: 
1.3       daniel    618:        status = ioctlsocket(s, FIONBIO, &one) == SOCKET_ERROR ? -1 : 0;
1.2       daniel    619:     }
                    620: #else /* _WINSOCKAPI_ */
                    621: #if defined(VMS)
                    622:     {
                    623:        int enable = 1;
1.3       daniel    624:        status = IOCTL(s, FIONBIO, &enable);
1.2       daniel    625:     }
                    626: #else /* VMS */
1.26      veillard  627:     if ((status = fcntl(s, F_GETFL, 0)) != -1) {
1.2       daniel    628: #ifdef O_NONBLOCK
                    629:        status |= O_NONBLOCK;
                    630: #else /* O_NONBLOCK */
                    631: #ifdef F_NDELAY
                    632:        status |= F_NDELAY;
                    633: #endif /* F_NDELAY */
                    634: #endif /* !O_NONBLOCK */
1.3       daniel    635:        status = fcntl(s, F_SETFL, status);
1.2       daniel    636:     }
1.26      veillard  637:     if (status < 0) {
1.5       daniel    638: #ifdef DEBUG_HTTP
1.1       daniel    639:        perror("nonblocking");
1.5       daniel    640: #endif
1.26      veillard  641:        closesocket(s);
1.1       daniel    642:        return(-1);
                    643:     }
1.2       daniel    644: #endif /* !VMS */
                    645: #endif /* !_WINSOCKAPI_ */
                    646: 
1.1       daniel    647: 
                    648:     sin.sin_family = AF_INET;  
                    649:     sin.sin_addr   = ia;
                    650:     sin.sin_port   = htons(port);
                    651:     
1.26      veillard  652:     if ((connect(s, (struct sockaddr *)&sin, sizeof(sin))==-1) &&
                    653:        (socket_errno() != EINPROGRESS) && (socket_errno() != EWOULDBLOCK)) {
1.1       daniel    654:        perror("connect");
1.26      veillard  655:        closesocket(s);
1.1       daniel    656:        return(-1);
                    657:     }  
                    658:     
                    659:     tv.tv_sec = 60;            /* We use 60 second timeouts for now */
                    660:     tv.tv_usec = 0;
                    661:     
                    662:     FD_ZERO(&wfd);
                    663:     FD_SET(s, &wfd);
                    664:     
                    665:     switch(select(s+1, NULL, &wfd, NULL, &tv))
                    666:     {
                    667:        case 0:
                    668:            /* Time out */
1.26      veillard  669:            closesocket(s);
1.1       daniel    670:            return(-1);
                    671:        case -1:
                    672:            /* Ermm.. ?? */
1.5       daniel    673: #ifdef DEBUG_HTTP
1.1       daniel    674:            perror("select");
1.5       daniel    675: #endif
1.26      veillard  676:            closesocket(s);
1.1       daniel    677:            return(-1);
                    678:     }
1.19      daniel    679: 
                    680:     if ( FD_ISSET(s, &wfd) ) {
1.27    ! veillard  681:        SOCKLEN_T len;
1.19      daniel    682:        len = sizeof(status);
                    683:        if (getsockopt(s, SOL_SOCKET, SO_ERROR, &status, &len) < 0 ) {
                    684:            /* Solaris error code */
                    685:            return (-1);
                    686:        }
                    687:        if ( status ) {
1.26      veillard  688:            closesocket(s);
1.19      daniel    689:            errno = status;
                    690:            return (-1);
                    691:        }
                    692:     } else {
                    693:        /* pbm */
                    694:        return (-1);
                    695:     }
1.1       daniel    696:     
1.5       daniel    697:     return(s);
1.1       daniel    698: }
                    699:  
1.5       daniel    700: /**
                    701:  * xmlNanoHTTPConnectHost:
                    702:  * @host:  the host name
                    703:  * @port:  the port number
                    704:  *
                    705:  * Attempt a connection to the given host:port endpoint. It tries
                    706:  * the multiple IP provided by the DNS if available.
                    707:  *
                    708:  * Returns -1 in case of failure, the file descriptor number otherwise
                    709:  */
                    710: 
                    711: static int
                    712: xmlNanoHTTPConnectHost(const char *host, int port)
1.1       daniel    713: {
                    714:     struct hostent *h;
                    715:     int i;
                    716:     int s;
                    717:     
                    718:     h=gethostbyname(host);
1.26      veillard  719:     if (h==NULL)
1.1       daniel    720:     {
1.5       daniel    721: #ifdef DEBUG_HTTP
1.1       daniel    722:        fprintf(stderr,"unable to resolve '%s'.\n", host);
1.5       daniel    723: #endif
1.1       daniel    724:        return(-1);
                    725:     }
                    726:     
                    727:     for(i=0; h->h_addr_list[i]; i++)
                    728:     {
                    729:        struct in_addr ia;
                    730:        memcpy(&ia, h->h_addr_list[i],4);
                    731:        s = xmlNanoHTTPConnectAttempt(ia, port);
1.26      veillard  732:        if (s != -1)
1.5       daniel    733:            return(s);
1.1       daniel    734:     }
1.5       daniel    735: 
                    736: #ifdef DEBUG_HTTP
1.1       daniel    737:     fprintf(stderr, "unable to connect to '%s'.\n", host);
1.5       daniel    738: #endif
1.1       daniel    739:     return(-1);
                    740: }
                    741: 
                    742: 
1.5       daniel    743: /**
                    744:  * xmlNanoHTTPOpen:
                    745:  * @URL:  The URL to load
                    746:  * @contentType:  if available the Content-Type information will be
                    747:  *                returned at that location
                    748:  *
                    749:  * This function try to open a connection to the indicated resource
                    750:  * via HTTP GET.
                    751:  *
1.6       daniel    752:  * Returns NULL in case of failure, otherwise a request handler.
                    753:  *     The contentType, if provided must be freed by the caller
1.5       daniel    754:  */
1.1       daniel    755: 
1.18      daniel    756: void*
1.1       daniel    757: xmlNanoHTTPOpen(const char *URL, char **contentType) {
                    758:     xmlNanoHTTPCtxtPtr ctxt;
                    759:     char buf[4096];
                    760:     int ret;
                    761:     char *p;
                    762:     int head;
                    763:     int nbRedirects = 0;
                    764:     char *redirURL = NULL;
                    765:     
1.12      daniel    766:     xmlNanoHTTPInit();
1.5       daniel    767:     if (contentType != NULL) *contentType = NULL;
                    768: 
1.1       daniel    769: retry:
                    770:     if (redirURL == NULL)
                    771:        ctxt = xmlNanoHTTPNewCtxt(URL);
                    772:     else {
                    773:        ctxt = xmlNanoHTTPNewCtxt(redirURL);
1.7       daniel    774:        xmlFree(redirURL);
1.1       daniel    775:        redirURL = NULL;
                    776:     }
                    777: 
                    778:     if ((ctxt->protocol == NULL) || (strcmp(ctxt->protocol, "http"))) {
                    779:         xmlNanoHTTPFreeCtxt(ctxt);
1.7       daniel    780:        if (redirURL != NULL) xmlFree(redirURL);
1.1       daniel    781:         return(NULL);
                    782:     }
                    783:     if (ctxt->hostname == NULL) {
                    784:         xmlNanoHTTPFreeCtxt(ctxt);
                    785:         return(NULL);
                    786:     }
1.12      daniel    787:     if (proxy)
                    788:        ret = xmlNanoHTTPConnectHost(proxy, proxyPort);
                    789:     else
                    790:        ret = xmlNanoHTTPConnectHost(ctxt->hostname, ctxt->port);
1.1       daniel    791:     if (ret < 0) {
                    792:         xmlNanoHTTPFreeCtxt(ctxt);
                    793:         return(NULL);
                    794:     }
                    795:     ctxt->fd = ret;
1.12      daniel    796:     if (proxy) {
1.22      veillard  797:        if (ctxt->port != 80)
1.16      daniel    798: #ifdef HAVE_SNPRINTF
1.12      daniel    799:            snprintf(buf, sizeof(buf),
                    800:                     "GET http://%s:%d%s HTTP/1.0\r\nHost: %s\r\n\r\n",
                    801:                 ctxt->hostname, ctxt->port, ctxt->path, ctxt->hostname);
                    802: #else
                    803:            sprintf(buf, 
                    804:                     "GET http://%s:%d%s HTTP/1.0\r\nHost: %s\r\n\r\n",
                    805:                 ctxt->hostname, ctxt->port, ctxt->path, ctxt->hostname);
1.22      veillard  806: #endif
1.12      daniel    807:        else 
1.22      veillard  808: #ifdef HAVE_SNPRINTF
                    809:            snprintf(buf, sizeof(buf),"GET http://%s%s HTTP/1.0\r\nHost: %s\r\n\r\n",
                    810:                 ctxt->hostname, ctxt->path, ctxt->hostname);
                    811: #else
1.12      daniel    812:            sprintf(buf, "GET http://%s%s HTTP/1.0\r\nHost: %s\r\n\r\n",
                    813:                 ctxt->hostname, ctxt->path, ctxt->hostname);
                    814: #endif
                    815: #ifdef DEBUG_HTTP
                    816:        if (ctxt->port != 80)
                    817:            printf("-> Proxy GET http://%s:%d%s HTTP/1.0\n-> Host: %s\n\n",
                    818:                   ctxt->hostname, ctxt->port, ctxt->path, ctxt->hostname);
                    819:        else
                    820:            printf("-> Proxy GET http://%s%s HTTP/1.0\n-> Host: %s\n\n",
                    821:                   ctxt->hostname, ctxt->path, ctxt->hostname);
                    822: #endif
                    823:     } else {
1.7       daniel    824: #ifdef HAVE_SNPRINTF
1.12      daniel    825:        snprintf(buf, sizeof(buf),"GET %s HTTP/1.0\r\nHost: %s\r\n\r\n",
                    826:                 ctxt->path, ctxt->hostname);
1.7       daniel    827: #else
1.12      daniel    828:        sprintf(buf, "GET %s HTTP/1.0\r\nHost: %s\r\n\r\n",
                    829:                 ctxt->path, ctxt->hostname);
1.7       daniel    830: #endif
1.5       daniel    831: #ifdef DEBUG_HTTP
1.12      daniel    832:        printf("-> GET %s HTTP/1.0\n-> Host: %s\n\n",
                    833:               ctxt->path, ctxt->hostname);
1.5       daniel    834: #endif
1.12      daniel    835:     }
1.22      veillard  836:     buf[sizeof(buf) - 1] = 0;
1.7       daniel    837:     ctxt->outptr = ctxt->out = xmlMemStrdup(buf);
1.1       daniel    838:     ctxt->state = XML_NANO_HTTP_WRITE;
                    839:     xmlNanoHTTPSend(ctxt);
                    840:     ctxt->state = XML_NANO_HTTP_READ;
                    841:     head = 1;
                    842: 
                    843:     while ((p = xmlNanoHTTPReadLine(ctxt)) != NULL) {
                    844:         if (head && (*p == 0)) {
                    845:            head = 0;
                    846:            ctxt->content = ctxt->inrptr;
1.12      daniel    847:            xmlFree(p);
1.1       daniel    848:            break;
                    849:        }
                    850:        xmlNanoHTTPScanAnswer(ctxt, p);
                    851: 
1.5       daniel    852: #ifdef DEBUG_HTTP
                    853:        if (p != NULL) printf("<- %s\n", p);
                    854: #endif
1.7       daniel    855:         if (p != NULL) xmlFree(p);
1.1       daniel    856:     }
                    857: 
                    858:     if ((ctxt->location != NULL) && (ctxt->returnValue >= 300) &&
                    859:         (ctxt->returnValue < 400)) {
1.5       daniel    860: #ifdef DEBUG_HTTP
                    861:        printf("\nRedirect to: %s\n", ctxt->location);
                    862: #endif
1.1       daniel    863:        while (xmlNanoHTTPRecv(ctxt)) ;
                    864:         if (nbRedirects < XML_NANO_HTTP_MAX_REDIR) {
                    865:            nbRedirects++;
1.7       daniel    866:            redirURL = xmlMemStrdup(ctxt->location);
1.1       daniel    867:            xmlNanoHTTPFreeCtxt(ctxt);
                    868:            goto retry;
                    869:        }
                    870:        xmlNanoHTTPFreeCtxt(ctxt);
1.5       daniel    871: #ifdef DEBUG_HTTP
1.26      veillard  872:        printf("Too many redirects, aborting ...\n");
1.5       daniel    873: #endif
1.1       daniel    874:        return(NULL);
                    875: 
                    876:     }
                    877: 
1.5       daniel    878:     if ((contentType != NULL) && (ctxt->contentType != NULL))
1.7       daniel    879:         *contentType = xmlMemStrdup(ctxt->contentType);
1.5       daniel    880: 
                    881: #ifdef DEBUG_HTTP
                    882:     if (ctxt->contentType != NULL)
                    883:        printf("\nCode %d, content-type '%s'\n\n",
                    884:               ctxt->returnValue, ctxt->contentType);
                    885:     else
                    886:        printf("\nCode %d, no content-type\n\n",
                    887:               ctxt->returnValue);
                    888: #endif
1.1       daniel    889: 
                    890:     return((void *) ctxt);
                    891: }
                    892: 
1.5       daniel    893: /**
                    894:  * xmlNanoHTTPRead:
                    895:  * @ctx:  the HTTP context
                    896:  * @dest:  a buffer
                    897:  * @len:  the buffer length
                    898:  *
                    899:  * This function tries to read @len bytes from the existing HTTP connection
                    900:  * and saves them in @dest. This is a blocking call.
                    901:  *
                    902:  * Returns the number of byte read. 0 is an indication of an end of connection.
                    903:  *         -1 indicates a parameter error.
                    904:  */
1.1       daniel    905: int
                    906: xmlNanoHTTPRead(void *ctx, void *dest, int len) {
                    907:     xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
                    908: 
                    909:     if (ctx == NULL) return(-1);
                    910:     if (dest == NULL) return(-1);
                    911:     if (len <= 0) return(0);
                    912: 
                    913:     while (ctxt->inptr - ctxt->inrptr < len) {
                    914:         if (xmlNanoHTTPRecv(ctxt) == 0) break;
                    915:     }
                    916:     if (ctxt->inptr - ctxt->inrptr < len)
                    917:         len = ctxt->inptr - ctxt->inrptr;
                    918:     memcpy(dest, ctxt->inrptr, len);
                    919:     ctxt->inrptr += len;
                    920:     return(len);
                    921: }
                    922: 
1.5       daniel    923: /**
                    924:  * xmlNanoHTTPClose:
                    925:  * @ctx:  the HTTP context
                    926:  *
                    927:  * This function closes an HTTP context, it ends up the connection and
                    928:  * free all data related to it.
                    929:  */
1.1       daniel    930: void
                    931: xmlNanoHTTPClose(void *ctx) {
                    932:     xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
                    933: 
                    934:     if (ctx == NULL) return;
                    935: 
                    936:     xmlNanoHTTPFreeCtxt(ctxt);
                    937: }
                    938: 
1.8       daniel    939: #ifndef DEBUG_HTTP
                    940: #define DEBUG_HTTP
                    941: #endif
1.5       daniel    942: /**
1.6       daniel    943:  * xmlNanoHTTPMethod:
                    944:  * @URL:  The URL to load
                    945:  * @method:  the HTTP method to use
                    946:  * @input:  the input string if any
                    947:  * @contentType:  the Content-Type information IN and OUT
                    948:  * @headers:  the extra headers
                    949:  *
                    950:  * This function try to open a connection to the indicated resource
                    951:  * via HTTP using the given @method, adding the given extra headers
                    952:  * and the input buffer for the request content.
                    953:  *
                    954:  * Returns NULL in case of failure, otherwise a request handler.
                    955:  *     The contentType, if provided must be freed by the caller
                    956:  */
                    957: 
1.18      daniel    958: void*
1.6       daniel    959: xmlNanoHTTPMethod(const char *URL, const char *method, const char *input,
                    960:                   char **contentType, const char *headers) {
                    961:     xmlNanoHTTPCtxtPtr ctxt;
                    962:     char buf[20000];
                    963:     int ret;
                    964:     char *p;
                    965:     int head;
                    966:     int nbRedirects = 0;
                    967:     char *redirURL = NULL;
                    968:     
                    969:     if (URL == NULL) return(NULL);
                    970:     if (method == NULL) method = "GET";
                    971:     if (contentType != NULL) *contentType = NULL;
                    972: 
                    973: retry:
                    974:     if (redirURL == NULL)
                    975:        ctxt = xmlNanoHTTPNewCtxt(URL);
                    976:     else {
                    977:        ctxt = xmlNanoHTTPNewCtxt(redirURL);
1.7       daniel    978:        xmlFree(redirURL);
1.6       daniel    979:        redirURL = NULL;
                    980:     }
                    981: 
                    982:     if ((ctxt->protocol == NULL) || (strcmp(ctxt->protocol, "http"))) {
                    983:         xmlNanoHTTPFreeCtxt(ctxt);
1.7       daniel    984:        if (redirURL != NULL) xmlFree(redirURL);
1.6       daniel    985:         return(NULL);
                    986:     }
                    987:     if (ctxt->hostname == NULL) {
                    988:         xmlNanoHTTPFreeCtxt(ctxt);
                    989:         return(NULL);
                    990:     }
                    991:     ret = xmlNanoHTTPConnectHost(ctxt->hostname, ctxt->port);
                    992:     if (ret < 0) {
                    993:         xmlNanoHTTPFreeCtxt(ctxt);
                    994:         return(NULL);
                    995:     }
                    996:     ctxt->fd = ret;
                    997: 
                    998:     if (input == NULL) {
                    999:         if (headers == NULL) {
                   1000:            if ((contentType == NULL) || (*contentType == NULL)) {
1.7       daniel   1001: #ifdef HAVE_SNPRINTF
1.6       daniel   1002:                snprintf(buf, sizeof(buf),
                   1003:                         "%s %s HTTP/1.0\r\nHost: %s\r\n\r\n",
                   1004:                         method, ctxt->path, ctxt->hostname);
1.7       daniel   1005: #else
                   1006:                sprintf(buf,
                   1007:                         "%s %s HTTP/1.0\r\nHost: %s\r\n\r\n",
                   1008:                         method, ctxt->path, ctxt->hostname);
                   1009: #endif
1.6       daniel   1010:            } else {
1.7       daniel   1011: #ifdef HAVE_SNPRINTF
1.6       daniel   1012:                snprintf(buf, sizeof(buf),
                   1013:                     "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Type: %s\r\n\r\n",
                   1014:                         method, ctxt->path, ctxt->hostname, *contentType);
1.7       daniel   1015: #else
                   1016:                sprintf(buf,
                   1017:                     "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Type: %s\r\n\r\n",
                   1018:                         method, ctxt->path, ctxt->hostname, *contentType);
                   1019: #endif
1.6       daniel   1020:            }
                   1021:        } else {
                   1022:            if ((contentType == NULL) || (*contentType == NULL)) {
1.7       daniel   1023: #ifdef HAVE_SNPRINTF
1.6       daniel   1024:                snprintf(buf, sizeof(buf),
                   1025:                         "%s %s HTTP/1.0\r\nHost: %s\r\n%s\r\n",
                   1026:                         method, ctxt->path, ctxt->hostname, headers);
1.7       daniel   1027: #else
                   1028:                sprintf(buf,
                   1029:                         "%s %s HTTP/1.0\r\nHost: %s\r\n%s\r\n",
                   1030:                         method, ctxt->path, ctxt->hostname, headers);
                   1031: #endif
1.6       daniel   1032:            } else {
1.7       daniel   1033: #ifdef HAVE_SNPRINTF
1.6       daniel   1034:                snprintf(buf, sizeof(buf),
                   1035:                 "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Type: %s\r\n%s\r\n",
                   1036:                         method, ctxt->path, ctxt->hostname, *contentType,
                   1037:                         headers);
1.7       daniel   1038: #else
                   1039:                sprintf(buf,
                   1040:                 "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Type: %s\r\n%s\r\n",
                   1041:                         method, ctxt->path, ctxt->hostname, *contentType,
                   1042:                         headers);
                   1043: #endif
1.6       daniel   1044:            }
                   1045:        }
                   1046:     } else {
                   1047:         int len = strlen(input);
                   1048:         if (headers == NULL) {
                   1049:            if ((contentType == NULL) || (*contentType == NULL)) {
1.7       daniel   1050: #ifdef HAVE_SNPRINTF
1.6       daniel   1051:                snprintf(buf, sizeof(buf),
                   1052:                 "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Length: %d\r\n\r\n%s",
                   1053:                         method, ctxt->path, ctxt->hostname, len, input);
1.7       daniel   1054: #else
                   1055:                sprintf(buf,
                   1056:                 "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Length: %d\r\n\r\n%s",
                   1057:                         method, ctxt->path, ctxt->hostname, len, input);
                   1058: #endif
1.6       daniel   1059:            } else {
1.7       daniel   1060: #ifdef HAVE_SNPRINTF
1.6       daniel   1061:                snprintf(buf, sizeof(buf),
                   1062: "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Type: %s\r\nContent-Length: %d\r\n\r\n%s",
                   1063:                         method, ctxt->path, ctxt->hostname, *contentType, len,
                   1064:                         input);
1.7       daniel   1065: #else
                   1066:                sprintf(buf,
                   1067: "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Type: %s\r\nContent-Length: %d\r\n\r\n%s",
                   1068:                         method, ctxt->path, ctxt->hostname, *contentType, len,
                   1069:                         input);
                   1070: #endif
1.6       daniel   1071:            }
                   1072:        } else {
                   1073:            if ((contentType == NULL) || (*contentType == NULL)) {
1.7       daniel   1074: #ifdef HAVE_SNPRINTF
1.6       daniel   1075:                snprintf(buf, sizeof(buf),
                   1076:             "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Length: %d\r\n%s\r\n%s",
                   1077:                         method, ctxt->path, ctxt->hostname, len,
                   1078:                         headers, input);
1.7       daniel   1079: #else
                   1080:                sprintf(buf,
                   1081:             "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Length: %d\r\n%s\r\n%s",
                   1082:                         method, ctxt->path, ctxt->hostname, len,
                   1083:                         headers, input);
                   1084: #endif
1.6       daniel   1085:            } else {
1.7       daniel   1086: #ifdef HAVE_SNPRINTF
1.6       daniel   1087:                snprintf(buf, sizeof(buf),
                   1088: "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Type: %s\r\nContent-Length: %d\r\n%s\r\n%s",
                   1089:                         method, ctxt->path, ctxt->hostname, *contentType,
                   1090:                         len, headers, input);
1.7       daniel   1091: #else
                   1092:                sprintf(buf,
                   1093: "%s %s HTTP/1.0\r\nHost: %s\r\nContent-Type: %s\r\nContent-Length: %d\r\n%s\r\n%s",
                   1094:                         method, ctxt->path, ctxt->hostname, *contentType,
                   1095:                         len, headers, input);
                   1096: #endif
1.6       daniel   1097:            }
                   1098:        }
                   1099:     }
1.22      veillard 1100:     buf[sizeof(buf) - 1] = 0;
1.6       daniel   1101: #ifdef DEBUG_HTTP
                   1102:     printf("-> %s", buf);
                   1103: #endif
1.7       daniel   1104:     ctxt->outptr = ctxt->out = xmlMemStrdup(buf);
1.6       daniel   1105:     ctxt->state = XML_NANO_HTTP_WRITE;
                   1106:     xmlNanoHTTPSend(ctxt);
                   1107:     ctxt->state = XML_NANO_HTTP_READ;
                   1108:     head = 1;
                   1109: 
                   1110:     while ((p = xmlNanoHTTPReadLine(ctxt)) != NULL) {
                   1111:         if (head && (*p == 0)) {
                   1112:            head = 0;
                   1113:            ctxt->content = ctxt->inrptr;
1.7       daniel   1114:            if (p != NULL) xmlFree(p);
1.6       daniel   1115:            break;
                   1116:        }
                   1117:        xmlNanoHTTPScanAnswer(ctxt, p);
                   1118: 
                   1119: #ifdef DEBUG_HTTP
                   1120:        if (p != NULL) printf("<- %s\n", p);
                   1121: #endif
1.7       daniel   1122:         if (p != NULL) xmlFree(p);
1.6       daniel   1123:     }
                   1124: 
                   1125:     if ((ctxt->location != NULL) && (ctxt->returnValue >= 300) &&
                   1126:         (ctxt->returnValue < 400)) {
                   1127: #ifdef DEBUG_HTTP
                   1128:        printf("\nRedirect to: %s\n", ctxt->location);
                   1129: #endif
                   1130:        while (xmlNanoHTTPRecv(ctxt)) ;
                   1131:         if (nbRedirects < XML_NANO_HTTP_MAX_REDIR) {
                   1132:            nbRedirects++;
1.7       daniel   1133:            redirURL = xmlMemStrdup(ctxt->location);
1.6       daniel   1134:            xmlNanoHTTPFreeCtxt(ctxt);
                   1135:            goto retry;
                   1136:        }
                   1137:        xmlNanoHTTPFreeCtxt(ctxt);
                   1138: #ifdef DEBUG_HTTP
                   1139:        printf("Too many redirrects, aborting ...\n");
                   1140: #endif
                   1141:        return(NULL);
                   1142: 
                   1143:     }
                   1144: 
                   1145:     if ((contentType != NULL) && (ctxt->contentType != NULL))
1.7       daniel   1146:         *contentType = xmlMemStrdup(ctxt->contentType);
                   1147:     else if (contentType != NULL)
                   1148:         *contentType = NULL;
1.6       daniel   1149: 
                   1150: #ifdef DEBUG_HTTP
                   1151:     if (ctxt->contentType != NULL)
                   1152:        printf("\nCode %d, content-type '%s'\n\n",
                   1153:               ctxt->returnValue, ctxt->contentType);
                   1154:     else
                   1155:        printf("\nCode %d, no content-type\n\n",
                   1156:               ctxt->returnValue);
                   1157: #endif
                   1158: 
                   1159:     return((void *) ctxt);
                   1160: }
                   1161: 
                   1162: /**
1.5       daniel   1163:  * xmlNanoHTTPFetch:
                   1164:  * @URL:  The URL to load
                   1165:  * @filename:  the filename where the content should be saved
                   1166:  * @contentType:  if available the Content-Type information will be
                   1167:  *                returned at that location
                   1168:  *
                   1169:  * This function try to fetch the indicated resource via HTTP GET
                   1170:  * and save it's content in the file.
                   1171:  *
                   1172:  * Returns -1 in case of failure, 0 incase of success. The contentType,
                   1173:  *     if provided must be freed by the caller
                   1174:  */
                   1175: int
                   1176: xmlNanoHTTPFetch(const char *URL, const char *filename, char **contentType) {
1.1       daniel   1177:     void *ctxt;
                   1178:     char buf[4096];
                   1179:     int fd;
                   1180:     int len;
                   1181:     
                   1182:     ctxt = xmlNanoHTTPOpen(URL, contentType);
                   1183:     if (ctxt == NULL) return(-1);
                   1184: 
                   1185:     if (!strcmp(filename, "-")) 
                   1186:         fd = 0;
                   1187:     else {
1.13      daniel   1188:         fd = open(filename, O_CREAT | O_WRONLY, 00644);
1.1       daniel   1189:        if (fd < 0) {
                   1190:            xmlNanoHTTPClose(ctxt);
1.5       daniel   1191:            if ((contentType != NULL) && (*contentType != NULL)) {
1.7       daniel   1192:                xmlFree(*contentType);
1.5       daniel   1193:                *contentType = NULL;
                   1194:            }
1.1       daniel   1195:            return(-1);
                   1196:        }
                   1197:     }
                   1198: 
                   1199:     while ((len = xmlNanoHTTPRead(ctxt, buf, sizeof(buf))) > 0) {
                   1200:        write(fd, buf, len);
                   1201:     }
                   1202: 
                   1203:     xmlNanoHTTPClose(ctxt);
1.13      daniel   1204:     close(fd);
1.1       daniel   1205:     return(0);
1.6       daniel   1206: }
                   1207: 
                   1208: /**
                   1209:  * xmlNanoHTTPSave:
1.8       daniel   1210:  * @ctxt:  the HTTP context
1.6       daniel   1211:  * @filename:  the filename where the content should be saved
                   1212:  *
                   1213:  * This function saves the output of the HTTP transaction to a file
                   1214:  * It closes and free the context at the end
                   1215:  *
                   1216:  * Returns -1 in case of failure, 0 incase of success.
                   1217:  */
                   1218: int
                   1219: xmlNanoHTTPSave(void *ctxt, const char *filename) {
                   1220:     char buf[4096];
                   1221:     int fd;
                   1222:     int len;
                   1223:     
                   1224:     if (ctxt == NULL) return(-1);
                   1225: 
                   1226:     if (!strcmp(filename, "-")) 
                   1227:         fd = 0;
                   1228:     else {
                   1229:         fd = open(filename, O_CREAT | O_WRONLY);
                   1230:        if (fd < 0) {
                   1231:            xmlNanoHTTPClose(ctxt);
                   1232:            return(-1);
                   1233:        }
                   1234:     }
                   1235: 
                   1236:     while ((len = xmlNanoHTTPRead(ctxt, buf, sizeof(buf))) > 0) {
                   1237:        write(fd, buf, len);
                   1238:     }
                   1239: 
                   1240:     xmlNanoHTTPClose(ctxt);
                   1241:     return(0);
                   1242: }
                   1243: 
                   1244: /**
                   1245:  * xmlNanoHTTPReturnCode:
                   1246:  * @ctx:  the HTTP context
                   1247:  *
                   1248:  * Returns the HTTP return code for the request.
                   1249:  */
                   1250: int
                   1251: xmlNanoHTTPReturnCode(void *ctx) {
                   1252:     xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx;
                   1253: 
                   1254:     if (ctxt == NULL) return(-1);
                   1255: 
                   1256:     return(ctxt->returnValue);
1.1       daniel   1257: }
                   1258: 
                   1259: #ifdef STANDALONE
                   1260: int main(int argc, char **argv) {
                   1261:     char *contentType = NULL;
                   1262: 
                   1263:     if (argv[1] != NULL) {
                   1264:        if (argv[2] != NULL) 
                   1265:            xmlNanoHTTPFetch(argv[1], argv[2], &contentType);
                   1266:         else
                   1267:            xmlNanoHTTPFetch(argv[1], "-", &contentType);
1.7       daniel   1268:        if (contentType != NULL) xmlFree(contentType);
1.1       daniel   1269:     } else {
                   1270:         printf("%s: minimal HTTP GET implementation\n", argv[0]);
                   1271:         printf("\tusage %s [ URL [ filename ] ]\n", argv[0]);
                   1272:     }
1.12      daniel   1273:     xmlNanoHTTPCleanup();
                   1274:     xmlMemoryDump();
1.1       daniel   1275:     return(0);
                   1276: }
                   1277: #endif /* STANDALONE */
1.17      daniel   1278: #else /* !LIBXML_HTTP_ENABLED */
                   1279: #ifdef STANDALONE
                   1280: #include <stdio.h>
                   1281: int main(int argc, char **argv) {
                   1282:     printf("%s : HTTP support not compiled in\n", argv[0]);
                   1283:     return(0);
                   1284: }
                   1285: #endif /* STANDALONE */
                   1286: #endif /* LIBXML_HTTP_ENABLED */

Webmaster