Important changes to repositories hosted on mbed.com
Mbed hosted mercurial repositories are deprecated and are due to be permanently deleted in July 2026.
To keep a copy of this software download the repository Zip archive or clone locally using Mercurial.
It is also possible to export all your personal repositories from the account settings page.
nanohttp.c
00001 /* 00002 * nanohttp.c: minimalist HTTP GET implementation to fetch external subsets. 00003 * focuses on size, streamability, reentrancy and portability 00004 * 00005 * This is clearly not a general purpose HTTP implementation 00006 * If you look for one, check: 00007 * http://www.w3.org/Library/ 00008 * 00009 * See Copyright for the status of this software. 00010 * 00011 * daniel@veillard.com 00012 */ 00013 00014 #define NEED_SOCKETS 00015 #define IN_LIBXML 00016 #include "libxml.h" 00017 00018 #ifdef LIBXML_HTTP_ENABLED 00019 #include <string.h> 00020 00021 #ifdef HAVE_STDLIB_H 00022 #include <stdlib.h> 00023 #endif 00024 #ifdef HAVE_UNISTD_H 00025 #include <unistd.h> 00026 #endif 00027 #ifdef HAVE_SYS_TYPES_H 00028 #include <sys/types.h> 00029 #endif 00030 #ifdef HAVE_SYS_SOCKET_H 00031 #include <sys/socket.h> 00032 #endif 00033 #ifdef HAVE_NETINET_IN_H 00034 #include <netinet/in.h> 00035 #endif 00036 #ifdef HAVE_ARPA_INET_H 00037 #include <arpa/inet.h> 00038 #endif 00039 #ifdef HAVE_NETDB_H 00040 #include <netdb.h> 00041 #endif 00042 #ifdef HAVE_RESOLV_H 00043 #ifdef HAVE_ARPA_NAMESER_H 00044 #include <arpa/nameser.h> 00045 #endif 00046 #include <resolv.h> 00047 #endif 00048 #ifdef HAVE_FCNTL_H 00049 #include <fcntl.h> 00050 #endif 00051 #ifdef HAVE_ERRNO_H 00052 #include <errno.h> 00053 #endif 00054 #ifdef HAVE_SYS_TIME_H 00055 #include <sys/time.h> 00056 #endif 00057 #ifndef HAVE_POLL_H 00058 #ifdef HAVE_SYS_SELECT_H 00059 #include <sys/select.h> 00060 #endif 00061 #else 00062 #include <poll.h> 00063 #endif 00064 #ifdef HAVE_STRINGS_H 00065 #include <strings.h> 00066 #endif 00067 #ifdef HAVE_ZLIB_H 00068 #include <zlib.h> 00069 #endif 00070 00071 00072 #ifdef VMS 00073 #include <stropts> 00074 #define XML_SOCKLEN_T unsigned int 00075 #endif 00076 00077 #if defined(__MINGW32__) || defined(_WIN32_WCE) 00078 #ifndef _WINSOCKAPI_ 00079 #define _WINSOCKAPI_ 00080 #endif 00081 #include <wsockcompat.h> 00082 #include <winsock2.h> 00083 #undef XML_SOCKLEN_T 00084 #define XML_SOCKLEN_T unsigned int 00085 #endif 00086 00087 #include <libxml/globals.h> 00088 #include <libxml/xmlerror.h> 00089 #include <libxml/xmlmemory.h> 00090 #include <libxml/parser.h> /* for xmlStr(n)casecmp() */ 00091 #include <libxml/nanohttp.h> 00092 #include <libxml/globals.h> 00093 #include <libxml/uri.h> 00094 00095 /** 00096 * A couple portability macros 00097 */ 00098 #ifndef _WINSOCKAPI_ 00099 #if !defined(__BEOS__) || defined(__HAIKU__) 00100 #define closesocket(s) close(s) 00101 #endif 00102 #define SOCKET int 00103 #define INVALID_SOCKET (-1) 00104 #endif 00105 00106 #ifdef __BEOS__ 00107 #ifndef PF_INET 00108 #define PF_INET AF_INET 00109 #endif 00110 #endif 00111 00112 #ifndef XML_SOCKLEN_T 00113 #define XML_SOCKLEN_T unsigned int 00114 #endif 00115 00116 #ifdef STANDALONE 00117 #define DEBUG_HTTP 00118 #define xmlStrncasecmp(a, b, n) strncasecmp((char *)a, (char *)b, n) 00119 #define xmlStrcasecmpi(a, b) strcasecmp((char *)a, (char *)b) 00120 #endif 00121 00122 #define XML_NANO_HTTP_MAX_REDIR 10 00123 00124 #define XML_NANO_HTTP_CHUNK 4096 00125 00126 #define XML_NANO_HTTP_CLOSED 0 00127 #define XML_NANO_HTTP_WRITE 1 00128 #define XML_NANO_HTTP_READ 2 00129 #define XML_NANO_HTTP_NONE 4 00130 00131 typedef struct xmlNanoHTTPCtxt { 00132 char *protocol; /* the protocol name */ 00133 char *hostname; /* the host name */ 00134 int port; /* the port */ 00135 char *path; /* the path within the URL */ 00136 char *query; /* the query string */ 00137 SOCKET fd; /* the file descriptor for the socket */ 00138 int state; /* WRITE / READ / CLOSED */ 00139 char *out; /* buffer sent (zero terminated) */ 00140 char *outptr; /* index within the buffer sent */ 00141 char *in; /* the receiving buffer */ 00142 char *content; /* the start of the content */ 00143 char *inptr; /* the next byte to read from network */ 00144 char *inrptr; /* the next byte to give back to the client */ 00145 int inlen; /* len of the input buffer */ 00146 int last; /* return code for last operation */ 00147 int returnValue; /* the protocol return value */ 00148 int version; /* the protocol version */ 00149 int ContentLength; /* specified content length from HTTP header */ 00150 char *contentType; /* the MIME type for the input */ 00151 char *location; /* the new URL in case of redirect */ 00152 char *authHeader; /* contents of {WWW,Proxy}-Authenticate header */ 00153 char *encoding; /* encoding extracted from the contentType */ 00154 char *mimeType; /* Mime-Type extracted from the contentType */ 00155 #ifdef HAVE_ZLIB_H 00156 z_stream *strm; /* Zlib stream object */ 00157 int usesGzip; /* "Content-Encoding: gzip" was detected */ 00158 #endif 00159 } xmlNanoHTTPCtxt, *xmlNanoHTTPCtxtPtr; 00160 00161 static int initialized = 0; 00162 static char *proxy = NULL; /* the proxy name if any */ 00163 static int proxyPort; /* the proxy port if any */ 00164 static unsigned int timeout = 60;/* the select() timeout in seconds */ 00165 00166 static int xmlNanoHTTPFetchContent( void * ctx, char ** ptr, int * len ); 00167 00168 /** 00169 * xmlHTTPErrMemory: 00170 * @extra: extra informations 00171 * 00172 * Handle an out of memory condition 00173 */ 00174 static void 00175 xmlHTTPErrMemory(const char *extra) 00176 { 00177 __xmlSimpleError(XML_FROM_HTTP, XML_ERR_NO_MEMORY, NULL, NULL, extra); 00178 } 00179 00180 /** 00181 * A portability function 00182 */ 00183 static int socket_errno(void) { 00184 #ifdef _WINSOCKAPI_ 00185 return(WSAGetLastError()); 00186 #else 00187 return(errno); 00188 #endif 00189 } 00190 00191 #ifdef SUPPORT_IP6 00192 static 00193 int have_ipv6(void) { 00194 SOCKET s; 00195 00196 s = socket (AF_INET6, SOCK_STREAM, 0); 00197 if (s != INVALID_SOCKET) { 00198 close (s); 00199 return (1); 00200 } 00201 return (0); 00202 } 00203 #endif 00204 00205 /** 00206 * xmlNanoHTTPInit: 00207 * 00208 * Initialize the HTTP protocol layer. 00209 * Currently it just checks for proxy informations 00210 */ 00211 00212 void 00213 xmlNanoHTTPInit(void) { 00214 const char *env; 00215 #ifdef _WINSOCKAPI_ 00216 WSADATA wsaData; 00217 #endif 00218 00219 if (initialized) 00220 return; 00221 00222 #ifdef _WINSOCKAPI_ 00223 if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) 00224 return; 00225 #endif 00226 00227 if (proxy == NULL) { 00228 proxyPort = 80; 00229 env = getenv("no_proxy"); 00230 if (env && ((env[0] == '*') && (env[1] == 0))) 00231 goto done; 00232 env = getenv("http_proxy"); 00233 if (env != NULL) { 00234 xmlNanoHTTPScanProxy(env); 00235 goto done; 00236 } 00237 env = getenv("HTTP_PROXY"); 00238 if (env != NULL) { 00239 xmlNanoHTTPScanProxy(env); 00240 goto done; 00241 } 00242 } 00243 done: 00244 initialized = 1; 00245 } 00246 00247 /** 00248 * xmlNanoHTTPCleanup: 00249 * 00250 * Cleanup the HTTP protocol layer. 00251 */ 00252 00253 void 00254 xmlNanoHTTPCleanup(void) { 00255 if (proxy != NULL) { 00256 xmlFree(proxy); 00257 proxy = NULL; 00258 } 00259 #ifdef _WINSOCKAPI_ 00260 if (initialized) 00261 WSACleanup(); 00262 #endif 00263 initialized = 0; 00264 return; 00265 } 00266 00267 /** 00268 * xmlNanoHTTPScanURL: 00269 * @ctxt: an HTTP context 00270 * @URL: The URL used to initialize the context 00271 * 00272 * (Re)Initialize an HTTP context by parsing the URL and finding 00273 * the protocol host port and path it indicates. 00274 */ 00275 00276 static void 00277 xmlNanoHTTPScanURL(xmlNanoHTTPCtxtPtr ctxt, const char *URL) { 00278 xmlURIPtr uri; 00279 int len; 00280 00281 /* 00282 * Clear any existing data from the context 00283 */ 00284 if (ctxt->protocol != NULL) { 00285 xmlFree(ctxt->protocol); 00286 ctxt->protocol = NULL; 00287 } 00288 if (ctxt->hostname != NULL) { 00289 xmlFree(ctxt->hostname); 00290 ctxt->hostname = NULL; 00291 } 00292 if (ctxt->path != NULL) { 00293 xmlFree(ctxt->path); 00294 ctxt->path = NULL; 00295 } 00296 if (ctxt->query != NULL) { 00297 xmlFree(ctxt->query); 00298 ctxt->query = NULL; 00299 } 00300 if (URL == NULL) return; 00301 00302 uri = xmlParseURIRaw(URL, 1); 00303 if (uri == NULL) 00304 return; 00305 00306 if ((uri->scheme == NULL) || (uri->server == NULL)) { 00307 xmlFreeURI(uri); 00308 return; 00309 } 00310 00311 ctxt->protocol = xmlMemStrdup(uri->scheme); 00312 /* special case of IPv6 addresses, the [] need to be removed */ 00313 if ((uri->server != NULL) && (*uri->server == '[')) { 00314 len = strlen(uri->server); 00315 if ((len > 2) && (uri->server[len - 1] == ']')) { 00316 ctxt->hostname = (char *) xmlCharStrndup(uri->server + 1, len -2); 00317 } else 00318 ctxt->hostname = xmlMemStrdup(uri->server); 00319 } else 00320 ctxt->hostname = xmlMemStrdup(uri->server); 00321 if (uri->path != NULL) 00322 ctxt->path = xmlMemStrdup(uri->path); 00323 else 00324 ctxt->path = xmlMemStrdup("/"); 00325 if (uri->query != NULL) 00326 ctxt->query = xmlMemStrdup(uri->query); 00327 if (uri->port != 0) 00328 ctxt->port = uri->port; 00329 00330 xmlFreeURI(uri); 00331 } 00332 00333 /** 00334 * xmlNanoHTTPScanProxy: 00335 * @URL: The proxy URL used to initialize the proxy context 00336 * 00337 * (Re)Initialize the HTTP Proxy context by parsing the URL and finding 00338 * the protocol host port it indicates. 00339 * Should be like http://myproxy/ or http://myproxy:3128/ 00340 * A NULL URL cleans up proxy informations. 00341 */ 00342 00343 void 00344 xmlNanoHTTPScanProxy(const char *URL) { 00345 xmlURIPtr uri; 00346 00347 if (proxy != NULL) { 00348 xmlFree(proxy); 00349 proxy = NULL; 00350 } 00351 proxyPort = 0; 00352 00353 #ifdef DEBUG_HTTP 00354 if (URL == NULL) 00355 xmlGenericError(xmlGenericErrorContext, 00356 "Removing HTTP proxy info\n"); 00357 else 00358 xmlGenericError(xmlGenericErrorContext, 00359 "Using HTTP proxy %s\n", URL); 00360 #endif 00361 if (URL == NULL) return; 00362 00363 uri = xmlParseURIRaw(URL, 1); 00364 if ((uri == NULL) || (uri->scheme == NULL) || 00365 (strcmp(uri->scheme, "http")) || (uri->server == NULL)) { 00366 __xmlIOErr(XML_FROM_HTTP, XML_HTTP_URL_SYNTAX, "Syntax Error\n"); 00367 if (uri != NULL) 00368 xmlFreeURI(uri); 00369 return; 00370 } 00371 00372 proxy = xmlMemStrdup(uri->server); 00373 if (uri->port != 0) 00374 proxyPort = uri->port; 00375 00376 xmlFreeURI(uri); 00377 } 00378 00379 /** 00380 * xmlNanoHTTPNewCtxt: 00381 * @URL: The URL used to initialize the context 00382 * 00383 * Allocate and initialize a new HTTP context. 00384 * 00385 * Returns an HTTP context or NULL in case of error. 00386 */ 00387 00388 static xmlNanoHTTPCtxtPtr 00389 xmlNanoHTTPNewCtxt(const char *URL) { 00390 xmlNanoHTTPCtxtPtr ret; 00391 00392 ret = (xmlNanoHTTPCtxtPtr) xmlMalloc(sizeof(xmlNanoHTTPCtxt)); 00393 if (ret == NULL) { 00394 xmlHTTPErrMemory("allocating context"); 00395 return(NULL); 00396 } 00397 00398 memset(ret, 0, sizeof(xmlNanoHTTPCtxt)); 00399 ret->port = 80; 00400 ret->returnValue = 0; 00401 ret->fd = INVALID_SOCKET; 00402 ret->ContentLength = -1; 00403 00404 xmlNanoHTTPScanURL(ret, URL); 00405 00406 return(ret); 00407 } 00408 00409 /** 00410 * xmlNanoHTTPFreeCtxt: 00411 * @ctxt: an HTTP context 00412 * 00413 * Frees the context after closing the connection. 00414 */ 00415 00416 static void 00417 xmlNanoHTTPFreeCtxt(xmlNanoHTTPCtxtPtr ctxt) { 00418 if (ctxt == NULL) return; 00419 if (ctxt->hostname != NULL) xmlFree(ctxt->hostname); 00420 if (ctxt->protocol != NULL) xmlFree(ctxt->protocol); 00421 if (ctxt->path != NULL) xmlFree(ctxt->path); 00422 if (ctxt->query != NULL) xmlFree(ctxt->query); 00423 if (ctxt->out != NULL) xmlFree(ctxt->out); 00424 if (ctxt->in != NULL) xmlFree(ctxt->in); 00425 if (ctxt->contentType != NULL) xmlFree(ctxt->contentType); 00426 if (ctxt->encoding != NULL) xmlFree(ctxt->encoding); 00427 if (ctxt->mimeType != NULL) xmlFree(ctxt->mimeType); 00428 if (ctxt->location != NULL) xmlFree(ctxt->location); 00429 if (ctxt->authHeader != NULL) xmlFree(ctxt->authHeader); 00430 #ifdef HAVE_ZLIB_H 00431 if (ctxt->strm != NULL) { 00432 inflateEnd(ctxt->strm); 00433 xmlFree(ctxt->strm); 00434 } 00435 #endif 00436 00437 ctxt->state = XML_NANO_HTTP_NONE; 00438 if (ctxt->fd != INVALID_SOCKET) closesocket(ctxt->fd); 00439 ctxt->fd = INVALID_SOCKET; 00440 xmlFree(ctxt); 00441 } 00442 00443 /** 00444 * xmlNanoHTTPSend: 00445 * @ctxt: an HTTP context 00446 * 00447 * Send the input needed to initiate the processing on the server side 00448 * Returns number of bytes sent or -1 on error. 00449 */ 00450 00451 static int 00452 xmlNanoHTTPSend(xmlNanoHTTPCtxtPtr ctxt, const char *xmt_ptr, int outlen) 00453 { 00454 int total_sent = 0; 00455 #ifdef HAVE_POLL_H 00456 struct pollfd p; 00457 #else 00458 struct timeval tv; 00459 fd_set wfd; 00460 #endif 00461 00462 if ((ctxt->state & XML_NANO_HTTP_WRITE) && (xmt_ptr != NULL)) { 00463 while (total_sent < outlen) { 00464 int nsent = send(ctxt->fd, SEND_ARG2_CAST (xmt_ptr + total_sent), 00465 outlen - total_sent, 0); 00466 00467 if (nsent > 0) 00468 total_sent += nsent; 00469 else if ((nsent == -1) && 00470 #if defined(EAGAIN) && EAGAIN != EWOULDBLOCK 00471 (socket_errno() != EAGAIN) && 00472 #endif 00473 (socket_errno() != EWOULDBLOCK)) { 00474 __xmlIOErr(XML_FROM_HTTP, 0, "send failed\n"); 00475 if (total_sent == 0) 00476 total_sent = -1; 00477 break; 00478 } else { 00479 /* 00480 * No data sent 00481 * Since non-blocking sockets are used, wait for 00482 * socket to be writable or default timeout prior 00483 * to retrying. 00484 */ 00485 #ifndef HAVE_POLL_H 00486 #ifndef _WINSOCKAPI_ 00487 if (ctxt->fd > FD_SETSIZE) 00488 return -1; 00489 #endif 00490 00491 tv.tv_sec = timeout; 00492 tv.tv_usec = 0; 00493 FD_ZERO(&wfd); 00494 #ifdef _MSC_VER 00495 #pragma warning(push) 00496 #pragma warning(disable: 4018) 00497 #endif 00498 FD_SET(ctxt->fd, &wfd); 00499 #ifdef _MSC_VER 00500 #pragma warning(pop) 00501 #endif 00502 (void) select(ctxt->fd + 1, NULL, &wfd, NULL, &tv); 00503 #else 00504 p.fd = ctxt->fd; 00505 p.events = POLLOUT; 00506 (void) poll(&p, 1, timeout * 1000); 00507 #endif /* !HAVE_POLL_H */ 00508 } 00509 } 00510 } 00511 00512 return total_sent; 00513 } 00514 00515 /** 00516 * xmlNanoHTTPRecv: 00517 * @ctxt: an HTTP context 00518 * 00519 * Read information coming from the HTTP connection. 00520 * This is a blocking call (but it blocks in select(), not read()). 00521 * 00522 * Returns the number of byte read or -1 in case of error. 00523 */ 00524 00525 static int 00526 xmlNanoHTTPRecv(xmlNanoHTTPCtxtPtr ctxt) 00527 { 00528 #ifdef HAVE_POLL_H 00529 struct pollfd p; 00530 #else 00531 fd_set rfd; 00532 struct timeval tv; 00533 #endif 00534 00535 00536 while (ctxt->state & XML_NANO_HTTP_READ) { 00537 if (ctxt->in == NULL) { 00538 ctxt->in = (char *) xmlMallocAtomic(65000 * sizeof(char)); 00539 if (ctxt->in == NULL) { 00540 xmlHTTPErrMemory("allocating input"); 00541 ctxt->last = -1; 00542 return (-1); 00543 } 00544 ctxt->inlen = 65000; 00545 ctxt->inptr = ctxt->content = ctxt->inrptr = ctxt->in; 00546 } 00547 if (ctxt->inrptr > ctxt->in + XML_NANO_HTTP_CHUNK) { 00548 int delta = ctxt->inrptr - ctxt->in; 00549 int len = ctxt->inptr - ctxt->inrptr; 00550 00551 memmove(ctxt->in, ctxt->inrptr, len); 00552 ctxt->inrptr -= delta; 00553 ctxt->content -= delta; 00554 ctxt->inptr -= delta; 00555 } 00556 if ((ctxt->in + ctxt->inlen) < (ctxt->inptr + XML_NANO_HTTP_CHUNK)) { 00557 int d_inptr = ctxt->inptr - ctxt->in; 00558 int d_content = ctxt->content - ctxt->in; 00559 int d_inrptr = ctxt->inrptr - ctxt->in; 00560 char *tmp_ptr = ctxt->in; 00561 00562 ctxt->inlen *= 2; 00563 ctxt->in = (char *) xmlRealloc(tmp_ptr, ctxt->inlen); 00564 if (ctxt->in == NULL) { 00565 xmlHTTPErrMemory("allocating input buffer"); 00566 xmlFree(tmp_ptr); 00567 ctxt->last = -1; 00568 return (-1); 00569 } 00570 ctxt->inptr = ctxt->in + d_inptr; 00571 ctxt->content = ctxt->in + d_content; 00572 ctxt->inrptr = ctxt->in + d_inrptr; 00573 } 00574 ctxt->last = recv(ctxt->fd, ctxt->inptr, XML_NANO_HTTP_CHUNK, 0); 00575 if (ctxt->last > 0) { 00576 ctxt->inptr += ctxt->last; 00577 return (ctxt->last); 00578 } 00579 if (ctxt->last == 0) { 00580 return (0); 00581 } 00582 if (ctxt->last == -1) { 00583 switch (socket_errno()) { 00584 case EINPROGRESS: 00585 case EWOULDBLOCK: 00586 #if defined(EAGAIN) && EAGAIN != EWOULDBLOCK 00587 case EAGAIN: 00588 #endif 00589 break; 00590 00591 case ECONNRESET: 00592 case ESHUTDOWN: 00593 return (0); 00594 00595 default: 00596 __xmlIOErr(XML_FROM_HTTP, 0, "recv failed\n"); 00597 return (-1); 00598 } 00599 } 00600 #ifdef HAVE_POLL_H 00601 p.fd = ctxt->fd; 00602 p.events = POLLIN; 00603 if ((poll(&p, 1, timeout * 1000) < 1) 00604 #if defined(EINTR) 00605 && (errno != EINTR) 00606 #endif 00607 ) 00608 return (0); 00609 #else /* !HAVE_POLL_H */ 00610 #ifndef _WINSOCKAPI_ 00611 if (ctxt->fd > FD_SETSIZE) 00612 return 0; 00613 #endif 00614 00615 tv.tv_sec = timeout; 00616 tv.tv_usec = 0; 00617 FD_ZERO(&rfd); 00618 00619 #ifdef _MSC_VER 00620 #pragma warning(push) 00621 #pragma warning(disable: 4018) 00622 #endif 00623 00624 FD_SET(ctxt->fd, &rfd); 00625 00626 #ifdef _MSC_VER 00627 #pragma warning(pop) 00628 #endif 00629 00630 if ((select(ctxt->fd + 1, &rfd, NULL, NULL, &tv) < 1) 00631 #if defined(EINTR) 00632 && (errno != EINTR) 00633 #endif 00634 ) 00635 return (0); 00636 #endif /* !HAVE_POLL_H */ 00637 } 00638 return (0); 00639 } 00640 00641 /** 00642 * xmlNanoHTTPReadLine: 00643 * @ctxt: an HTTP context 00644 * 00645 * Read one line in the HTTP server output, usually for extracting 00646 * the HTTP protocol informations from the answer header. 00647 * 00648 * Returns a newly allocated string with a copy of the line, or NULL 00649 * which indicate the end of the input. 00650 */ 00651 00652 static char * 00653 xmlNanoHTTPReadLine(xmlNanoHTTPCtxtPtr ctxt) { 00654 char buf[4096]; 00655 char *bp = buf; 00656 int rc; 00657 00658 while (bp - buf < 4095) { 00659 if (ctxt->inrptr == ctxt->inptr) { 00660 if ( (rc = xmlNanoHTTPRecv(ctxt)) == 0) { 00661 if (bp == buf) 00662 return(NULL); 00663 else 00664 *bp = 0; 00665 return(xmlMemStrdup(buf)); 00666 } 00667 else if ( rc == -1 ) { 00668 return ( NULL ); 00669 } 00670 } 00671 *bp = *ctxt->inrptr++; 00672 if (*bp == '\n') { 00673 *bp = 0; 00674 return(xmlMemStrdup(buf)); 00675 } 00676 if (*bp != '\r') 00677 bp++; 00678 } 00679 buf[4095] = 0; 00680 return(xmlMemStrdup(buf)); 00681 } 00682 00683 00684 /** 00685 * xmlNanoHTTPScanAnswer: 00686 * @ctxt: an HTTP context 00687 * @line: an HTTP header line 00688 * 00689 * Try to extract useful informations from the server answer. 00690 * We currently parse and process: 00691 * - The HTTP revision/ return code 00692 * - The Content-Type, Mime-Type and charset used 00693 * - The Location for redirect processing. 00694 * 00695 * Returns -1 in case of failure, the file descriptor number otherwise 00696 */ 00697 00698 static void 00699 xmlNanoHTTPScanAnswer(xmlNanoHTTPCtxtPtr ctxt, const char *line) { 00700 const char *cur = line; 00701 00702 if (line == NULL) return; 00703 00704 if (!strncmp(line, "HTTP/", 5)) { 00705 int version = 0; 00706 int ret = 0; 00707 00708 cur += 5; 00709 while ((*cur >= '0') && (*cur <= '9')) { 00710 version *= 10; 00711 version += *cur - '0'; 00712 cur++; 00713 } 00714 if (*cur == '.') { 00715 cur++; 00716 if ((*cur >= '0') && (*cur <= '9')) { 00717 version *= 10; 00718 version += *cur - '0'; 00719 cur++; 00720 } 00721 while ((*cur >= '0') && (*cur <= '9')) 00722 cur++; 00723 } else 00724 version *= 10; 00725 if ((*cur != ' ') && (*cur != '\t')) return; 00726 while ((*cur == ' ') || (*cur == '\t')) cur++; 00727 if ((*cur < '0') || (*cur > '9')) return; 00728 while ((*cur >= '0') && (*cur <= '9')) { 00729 ret *= 10; 00730 ret += *cur - '0'; 00731 cur++; 00732 } 00733 if ((*cur != 0) && (*cur != ' ') && (*cur != '\t')) return; 00734 ctxt->returnValue = ret; 00735 ctxt->version = version; 00736 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Content-Type:", 13)) { 00737 const xmlChar *charset, *last, *mime; 00738 cur += 13; 00739 while ((*cur == ' ') || (*cur == '\t')) cur++; 00740 if (ctxt->contentType != NULL) 00741 xmlFree(ctxt->contentType); 00742 ctxt->contentType = xmlMemStrdup(cur); 00743 mime = (const xmlChar *) cur; 00744 last = mime; 00745 while ((*last != 0) && (*last != ' ') && (*last != '\t') && 00746 (*last != ';') && (*last != ',')) 00747 last++; 00748 if (ctxt->mimeType != NULL) 00749 xmlFree(ctxt->mimeType); 00750 ctxt->mimeType = (char *) xmlStrndup(mime, last - mime); 00751 charset = xmlStrstr(BAD_CAST ctxt->contentType, BAD_CAST "charset="); 00752 if (charset != NULL) { 00753 charset += 8; 00754 last = charset; 00755 while ((*last != 0) && (*last != ' ') && (*last != '\t') && 00756 (*last != ';') && (*last != ',')) 00757 last++; 00758 if (ctxt->encoding != NULL) 00759 xmlFree(ctxt->encoding); 00760 ctxt->encoding = (char *) xmlStrndup(charset, last - charset); 00761 } 00762 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"ContentType:", 12)) { 00763 const xmlChar *charset, *last, *mime; 00764 cur += 12; 00765 if (ctxt->contentType != NULL) return; 00766 while ((*cur == ' ') || (*cur == '\t')) cur++; 00767 ctxt->contentType = xmlMemStrdup(cur); 00768 mime = (const xmlChar *) cur; 00769 last = mime; 00770 while ((*last != 0) && (*last != ' ') && (*last != '\t') && 00771 (*last != ';') && (*last != ',')) 00772 last++; 00773 if (ctxt->mimeType != NULL) 00774 xmlFree(ctxt->mimeType); 00775 ctxt->mimeType = (char *) xmlStrndup(mime, last - mime); 00776 charset = xmlStrstr(BAD_CAST ctxt->contentType, BAD_CAST "charset="); 00777 if (charset != NULL) { 00778 charset += 8; 00779 last = charset; 00780 while ((*last != 0) && (*last != ' ') && (*last != '\t') && 00781 (*last != ';') && (*last != ',')) 00782 last++; 00783 if (ctxt->encoding != NULL) 00784 xmlFree(ctxt->encoding); 00785 ctxt->encoding = (char *) xmlStrndup(charset, last - charset); 00786 } 00787 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Location:", 9)) { 00788 cur += 9; 00789 while ((*cur == ' ') || (*cur == '\t')) cur++; 00790 if (ctxt->location != NULL) 00791 xmlFree(ctxt->location); 00792 if (*cur == '/') { 00793 xmlChar *tmp_http = xmlStrdup(BAD_CAST "http://"); 00794 xmlChar *tmp_loc = 00795 xmlStrcat(tmp_http, (const xmlChar *) ctxt->hostname); 00796 ctxt->location = 00797 (char *) xmlStrcat (tmp_loc, (const xmlChar *) cur); 00798 } else { 00799 ctxt->location = xmlMemStrdup(cur); 00800 } 00801 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"WWW-Authenticate:", 17)) { 00802 cur += 17; 00803 while ((*cur == ' ') || (*cur == '\t')) cur++; 00804 if (ctxt->authHeader != NULL) 00805 xmlFree(ctxt->authHeader); 00806 ctxt->authHeader = xmlMemStrdup(cur); 00807 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Proxy-Authenticate:", 19)) { 00808 cur += 19; 00809 while ((*cur == ' ') || (*cur == '\t')) cur++; 00810 if (ctxt->authHeader != NULL) 00811 xmlFree(ctxt->authHeader); 00812 ctxt->authHeader = xmlMemStrdup(cur); 00813 #ifdef HAVE_ZLIB_H 00814 } else if ( !xmlStrncasecmp( BAD_CAST line, BAD_CAST"Content-Encoding:", 17) ) { 00815 cur += 17; 00816 while ((*cur == ' ') || (*cur == '\t')) cur++; 00817 if ( !xmlStrncasecmp( BAD_CAST cur, BAD_CAST"gzip", 4) ) { 00818 ctxt->usesGzip = 1; 00819 00820 ctxt->strm = xmlMalloc(sizeof(z_stream)); 00821 00822 if (ctxt->strm != NULL) { 00823 ctxt->strm->zalloc = Z_NULL; 00824 ctxt->strm->zfree = Z_NULL; 00825 ctxt->strm->opaque = Z_NULL; 00826 ctxt->strm->avail_in = 0; 00827 ctxt->strm->next_in = Z_NULL; 00828 00829 inflateInit2( ctxt->strm, 31 ); 00830 } 00831 } 00832 #endif 00833 } else if ( !xmlStrncasecmp( BAD_CAST line, BAD_CAST"Content-Length:", 15) ) { 00834 cur += 15; 00835 ctxt->ContentLength = strtol( cur, NULL, 10 ); 00836 } 00837 } 00838 00839 /** 00840 * xmlNanoHTTPConnectAttempt: 00841 * @addr: a socket address structure 00842 * 00843 * Attempt a connection to the given IP:port endpoint. It forces 00844 * non-blocking semantic on the socket, and allow 60 seconds for 00845 * the host to answer. 00846 * 00847 * Returns -1 in case of failure, the file descriptor number otherwise 00848 */ 00849 00850 static SOCKET 00851 xmlNanoHTTPConnectAttempt(struct sockaddr *addr) 00852 { 00853 #ifndef HAVE_POLL_H 00854 fd_set wfd; 00855 #ifdef _WINSOCKAPI_ 00856 fd_set xfd; 00857 #endif 00858 struct timeval tv; 00859 #else /* !HAVE_POLL_H */ 00860 struct pollfd p; 00861 #endif /* !HAVE_POLL_H */ 00862 int status; 00863 00864 int addrlen; 00865 00866 SOCKET s; 00867 00868 #ifdef SUPPORT_IP6 00869 if (addr->sa_family == AF_INET6) { 00870 s = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP); 00871 addrlen = sizeof(struct sockaddr_in6); 00872 } else 00873 #endif 00874 { 00875 s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); 00876 addrlen = sizeof(struct sockaddr_in); 00877 } 00878 if (s == INVALID_SOCKET) { 00879 #ifdef DEBUG_HTTP 00880 perror("socket"); 00881 #endif 00882 __xmlIOErr(XML_FROM_HTTP, 0, "socket failed\n"); 00883 return INVALID_SOCKET; 00884 } 00885 #ifdef _WINSOCKAPI_ 00886 { 00887 u_long one = 1; 00888 00889 status = ioctlsocket(s, FIONBIO, &one) == SOCKET_ERROR ? -1 : 0; 00890 } 00891 #else /* _WINSOCKAPI_ */ 00892 #if defined(VMS) 00893 { 00894 int enable = 1; 00895 00896 status = ioctl(s, FIONBIO, &enable); 00897 } 00898 #else /* VMS */ 00899 #if defined(__BEOS__) && !defined(__HAIKU__) 00900 { 00901 bool noblock = true; 00902 00903 status = 00904 setsockopt(s, SOL_SOCKET, SO_NONBLOCK, &noblock, 00905 sizeof(noblock)); 00906 } 00907 #else /* __BEOS__ */ 00908 if ((status = fcntl(s, F_GETFL, 0)) != -1) { 00909 #ifdef O_NONBLOCK 00910 status |= O_NONBLOCK; 00911 #else /* O_NONBLOCK */ 00912 #ifdef F_NDELAY 00913 status |= F_NDELAY; 00914 #endif /* F_NDELAY */ 00915 #endif /* !O_NONBLOCK */ 00916 status = fcntl(s, F_SETFL, status); 00917 } 00918 if (status < 0) { 00919 #ifdef DEBUG_HTTP 00920 perror("nonblocking"); 00921 #endif 00922 __xmlIOErr(XML_FROM_HTTP, 0, "error setting non-blocking IO\n"); 00923 closesocket(s); 00924 return INVALID_SOCKET; 00925 } 00926 #endif /* !__BEOS__ */ 00927 #endif /* !VMS */ 00928 #endif /* !_WINSOCKAPI_ */ 00929 00930 if (connect(s, addr, addrlen) == -1) { 00931 switch (socket_errno()) { 00932 case EINPROGRESS: 00933 case EWOULDBLOCK: 00934 break; 00935 default: 00936 __xmlIOErr(XML_FROM_HTTP, 0, 00937 "error connecting to HTTP server"); 00938 closesocket(s); 00939 return INVALID_SOCKET; 00940 } 00941 } 00942 #ifndef HAVE_POLL_H 00943 tv.tv_sec = timeout; 00944 tv.tv_usec = 0; 00945 00946 #ifdef _MSC_VER 00947 #pragma warning(push) 00948 #pragma warning(disable: 4018) 00949 #endif 00950 #ifndef _WINSOCKAPI_ 00951 if (s > FD_SETSIZE) 00952 return INVALID_SOCKET; 00953 #endif 00954 FD_ZERO(&wfd); 00955 FD_SET(s, &wfd); 00956 00957 #ifdef _WINSOCKAPI_ 00958 FD_ZERO(&xfd); 00959 FD_SET(s, &xfd); 00960 00961 switch (select(s + 1, NULL, &wfd, &xfd, &tv)) 00962 #else 00963 switch (select(s + 1, NULL, &wfd, NULL, &tv)) 00964 #endif 00965 #ifdef _MSC_VER 00966 #pragma warning(pop) 00967 #endif 00968 00969 #else /* !HAVE_POLL_H */ 00970 p.fd = s; 00971 p.events = POLLOUT; 00972 switch (poll(&p, 1, timeout * 1000)) 00973 #endif /* !HAVE_POLL_H */ 00974 00975 { 00976 case 0: 00977 /* Time out */ 00978 __xmlIOErr(XML_FROM_HTTP, 0, "Connect attempt timed out"); 00979 closesocket(s); 00980 return INVALID_SOCKET; 00981 case -1: 00982 /* Ermm.. ?? */ 00983 __xmlIOErr(XML_FROM_HTTP, 0, "Connect failed"); 00984 closesocket(s); 00985 return INVALID_SOCKET; 00986 } 00987 00988 #ifndef HAVE_POLL_H 00989 if (FD_ISSET(s, &wfd) 00990 #ifdef _WINSOCKAPI_ 00991 || FD_ISSET(s, &xfd) 00992 #endif 00993 ) 00994 #else /* !HAVE_POLL_H */ 00995 if (p.revents == POLLOUT) 00996 #endif /* !HAVE_POLL_H */ 00997 { 00998 XML_SOCKLEN_T len; 00999 01000 len = sizeof(status); 01001 #ifdef SO_ERROR 01002 if (getsockopt(s, SOL_SOCKET, SO_ERROR, (char *) &status, &len) < 01003 0) { 01004 /* Solaris error code */ 01005 __xmlIOErr(XML_FROM_HTTP, 0, "getsockopt failed\n"); 01006 closesocket(s); 01007 return INVALID_SOCKET; 01008 } 01009 #endif 01010 if (status) { 01011 __xmlIOErr(XML_FROM_HTTP, 0, 01012 "Error connecting to remote host"); 01013 closesocket(s); 01014 errno = status; 01015 return INVALID_SOCKET; 01016 } 01017 } else { 01018 /* pbm */ 01019 __xmlIOErr(XML_FROM_HTTP, 0, "select failed\n"); 01020 closesocket(s); 01021 return INVALID_SOCKET; 01022 } 01023 01024 return (s); 01025 } 01026 01027 /** 01028 * xmlNanoHTTPConnectHost: 01029 * @host: the host name 01030 * @port: the port number 01031 * 01032 * Attempt a connection to the given host:port endpoint. It tries 01033 * the multiple IP provided by the DNS if available. 01034 * 01035 * Returns -1 in case of failure, the file descriptor number otherwise 01036 */ 01037 01038 static SOCKET 01039 xmlNanoHTTPConnectHost(const char *host, int port) 01040 { 01041 struct hostent *h; 01042 struct sockaddr *addr = NULL; 01043 struct in_addr ia; 01044 struct sockaddr_in sockin; 01045 01046 #ifdef SUPPORT_IP6 01047 struct in6_addr ia6; 01048 struct sockaddr_in6 sockin6; 01049 #endif 01050 int i; 01051 SOCKET s; 01052 01053 memset (&sockin, 0, sizeof(sockin)); 01054 #ifdef SUPPORT_IP6 01055 memset (&sockin6, 0, sizeof(sockin6)); 01056 #endif 01057 01058 #if !defined(HAVE_GETADDRINFO) && defined(SUPPORT_IP6) && defined(RES_USE_INET6) 01059 if (have_ipv6 ()) 01060 { 01061 if (!(_res.options & RES_INIT)) 01062 res_init(); 01063 _res.options |= RES_USE_INET6; 01064 } 01065 #endif 01066 01067 #if defined(HAVE_GETADDRINFO) && defined(SUPPORT_IP6) && !defined(_WIN32) 01068 if (have_ipv6 ()) 01069 #endif 01070 #if defined(HAVE_GETADDRINFO) && (defined(SUPPORT_IP6) || defined(_WIN32)) 01071 { 01072 int status; 01073 struct addrinfo hints, *res, *result; 01074 01075 result = NULL; 01076 memset (&hints, 0,sizeof(hints)); 01077 hints.ai_socktype = SOCK_STREAM; 01078 01079 status = getaddrinfo (host, NULL, &hints, &result); 01080 if (status) { 01081 __xmlIOErr(XML_FROM_HTTP, 0, "getaddrinfo failed\n"); 01082 return INVALID_SOCKET; 01083 } 01084 01085 for (res = result; res; res = res->ai_next) { 01086 if (res->ai_family == AF_INET) { 01087 if (res->ai_addrlen > sizeof(sockin)) { 01088 __xmlIOErr(XML_FROM_HTTP, 0, "address size mismatch\n"); 01089 freeaddrinfo (result); 01090 return INVALID_SOCKET; 01091 } 01092 memcpy (&sockin, res->ai_addr, res->ai_addrlen); 01093 sockin.sin_port = htons (port); 01094 addr = (struct sockaddr *)&sockin; 01095 #ifdef SUPPORT_IP6 01096 } else if (have_ipv6 () && (res->ai_family == AF_INET6)) { 01097 if (res->ai_addrlen > sizeof(sockin6)) { 01098 __xmlIOErr(XML_FROM_HTTP, 0, "address size mismatch\n"); 01099 freeaddrinfo (result); 01100 return INVALID_SOCKET; 01101 } 01102 memcpy (&sockin6, res->ai_addr, res->ai_addrlen); 01103 sockin6.sin6_port = htons (port); 01104 addr = (struct sockaddr *)&sockin6; 01105 #endif 01106 } else 01107 continue; /* for */ 01108 01109 s = xmlNanoHTTPConnectAttempt (addr); 01110 if (s != INVALID_SOCKET) { 01111 freeaddrinfo (result); 01112 return (s); 01113 } 01114 } 01115 01116 if (result) 01117 freeaddrinfo (result); 01118 } 01119 #endif 01120 #if defined(HAVE_GETADDRINFO) && defined(SUPPORT_IP6) && !defined(_WIN32) 01121 else 01122 #endif 01123 #if !defined(HAVE_GETADDRINFO) || !defined(_WIN32) 01124 { 01125 h = gethostbyname (GETHOSTBYNAME_ARG_CAST host); 01126 if (h == NULL) { 01127 01128 /* 01129 * Okay, I got fed up by the non-portability of this error message 01130 * extraction code. it work on Linux, if it work on your platform 01131 * and one want to enable it, send me the defined(foobar) needed 01132 */ 01133 #if defined(HAVE_NETDB_H) && defined(HOST_NOT_FOUND) && defined(linux) 01134 const char *h_err_txt = ""; 01135 01136 switch (h_errno) { 01137 case HOST_NOT_FOUND: 01138 h_err_txt = "Authoritive host not found"; 01139 break; 01140 01141 case TRY_AGAIN: 01142 h_err_txt = 01143 "Non-authoritive host not found or server failure."; 01144 break; 01145 01146 case NO_RECOVERY: 01147 h_err_txt = 01148 "Non-recoverable errors: FORMERR, REFUSED, or NOTIMP."; 01149 break; 01150 01151 #ifdef NO_ADDRESS 01152 case NO_ADDRESS: 01153 h_err_txt = 01154 "Valid name, no data record of requested type."; 01155 break; 01156 #endif 01157 01158 default: 01159 h_err_txt = "No error text defined."; 01160 break; 01161 } 01162 __xmlIOErr(XML_FROM_HTTP, 0, h_err_txt); 01163 #else 01164 __xmlIOErr(XML_FROM_HTTP, 0, "Failed to resolve host"); 01165 #endif 01166 return INVALID_SOCKET; 01167 } 01168 01169 for (i = 0; h->h_addr_list[i]; i++) { 01170 if (h->h_addrtype == AF_INET) { 01171 /* A records (IPv4) */ 01172 if ((unsigned int) h->h_length > sizeof(ia)) { 01173 __xmlIOErr(XML_FROM_HTTP, 0, "address size mismatch\n"); 01174 return INVALID_SOCKET; 01175 } 01176 memcpy (&ia, h->h_addr_list[i], h->h_length); 01177 sockin.sin_family = h->h_addrtype; 01178 sockin.sin_addr = ia; 01179 sockin.sin_port = (unsigned short)htons ((unsigned short)port); 01180 addr = (struct sockaddr *) &sockin; 01181 #ifdef SUPPORT_IP6 01182 } else if (have_ipv6 () && (h->h_addrtype == AF_INET6)) { 01183 /* AAAA records (IPv6) */ 01184 if ((unsigned int) h->h_length > sizeof(ia6)) { 01185 __xmlIOErr(XML_FROM_HTTP, 0, "address size mismatch\n"); 01186 return INVALID_SOCKET; 01187 } 01188 memcpy (&ia6, h->h_addr_list[i], h->h_length); 01189 sockin6.sin6_family = h->h_addrtype; 01190 sockin6.sin6_addr = ia6; 01191 sockin6.sin6_port = htons (port); 01192 addr = (struct sockaddr *) &sockin6; 01193 #endif 01194 } else 01195 break; /* for */ 01196 01197 s = xmlNanoHTTPConnectAttempt (addr); 01198 if (s != INVALID_SOCKET) 01199 return (s); 01200 } 01201 } 01202 #endif 01203 01204 #ifdef DEBUG_HTTP 01205 xmlGenericError(xmlGenericErrorContext, 01206 "xmlNanoHTTPConnectHost: unable to connect to '%s'.\n", 01207 host); 01208 #endif 01209 return INVALID_SOCKET; 01210 } 01211 01212 01213 /** 01214 * xmlNanoHTTPOpen: 01215 * @URL: The URL to load 01216 * @contentType: if available the Content-Type information will be 01217 * returned at that location 01218 * 01219 * This function try to open a connection to the indicated resource 01220 * via HTTP GET. 01221 * 01222 * Returns NULL in case of failure, otherwise a request handler. 01223 * The contentType, if provided must be freed by the caller 01224 */ 01225 01226 void* 01227 xmlNanoHTTPOpen(const char *URL, char **contentType) { 01228 if (contentType != NULL) *contentType = NULL; 01229 return(xmlNanoHTTPMethod(URL, NULL, NULL, contentType, NULL, 0)); 01230 } 01231 01232 /** 01233 * xmlNanoHTTPOpenRedir: 01234 * @URL: The URL to load 01235 * @contentType: if available the Content-Type information will be 01236 * returned at that location 01237 * @redir: if available the redirected URL will be returned 01238 * 01239 * This function try to open a connection to the indicated resource 01240 * via HTTP GET. 01241 * 01242 * Returns NULL in case of failure, otherwise a request handler. 01243 * The contentType, if provided must be freed by the caller 01244 */ 01245 01246 void* 01247 xmlNanoHTTPOpenRedir(const char *URL, char **contentType, char **redir) { 01248 if (contentType != NULL) *contentType = NULL; 01249 if (redir != NULL) *redir = NULL; 01250 return(xmlNanoHTTPMethodRedir(URL, NULL, NULL, contentType, redir, NULL,0)); 01251 } 01252 01253 /** 01254 * xmlNanoHTTPRead: 01255 * @ctx: the HTTP context 01256 * @dest: a buffer 01257 * @len: the buffer length 01258 * 01259 * This function tries to read @len bytes from the existing HTTP connection 01260 * and saves them in @dest. This is a blocking call. 01261 * 01262 * Returns the number of byte read. 0 is an indication of an end of connection. 01263 * -1 indicates a parameter error. 01264 */ 01265 int 01266 xmlNanoHTTPRead(void *ctx, void *dest, int len) { 01267 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx; 01268 #ifdef HAVE_ZLIB_H 01269 int bytes_read = 0; 01270 int orig_avail_in; 01271 int z_ret; 01272 #endif 01273 01274 if (ctx == NULL) return(-1); 01275 if (dest == NULL) return(-1); 01276 if (len <= 0) return(0); 01277 01278 #ifdef HAVE_ZLIB_H 01279 if (ctxt->usesGzip == 1) { 01280 if (ctxt->strm == NULL) return(0); 01281 01282 ctxt->strm->next_out = dest; 01283 ctxt->strm->avail_out = len; 01284 ctxt->strm->avail_in = ctxt->inptr - ctxt->inrptr; 01285 01286 while (ctxt->strm->avail_out > 0 && 01287 (ctxt->strm->avail_in > 0 || xmlNanoHTTPRecv(ctxt) > 0)) { 01288 orig_avail_in = ctxt->strm->avail_in = 01289 ctxt->inptr - ctxt->inrptr - bytes_read; 01290 ctxt->strm->next_in = BAD_CAST (ctxt->inrptr + bytes_read); 01291 01292 z_ret = inflate(ctxt->strm, Z_NO_FLUSH); 01293 bytes_read += orig_avail_in - ctxt->strm->avail_in; 01294 01295 if (z_ret != Z_OK) break; 01296 } 01297 01298 ctxt->inrptr += bytes_read; 01299 return(len - ctxt->strm->avail_out); 01300 } 01301 #endif 01302 01303 while (ctxt->inptr - ctxt->inrptr < len) { 01304 if (xmlNanoHTTPRecv(ctxt) <= 0) break; 01305 } 01306 if (ctxt->inptr - ctxt->inrptr < len) 01307 len = ctxt->inptr - ctxt->inrptr; 01308 memcpy(dest, ctxt->inrptr, len); 01309 ctxt->inrptr += len; 01310 return(len); 01311 } 01312 01313 /** 01314 * xmlNanoHTTPClose: 01315 * @ctx: the HTTP context 01316 * 01317 * This function closes an HTTP context, it ends up the connection and 01318 * free all data related to it. 01319 */ 01320 void 01321 xmlNanoHTTPClose(void *ctx) { 01322 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx; 01323 01324 if (ctx == NULL) return; 01325 01326 xmlNanoHTTPFreeCtxt(ctxt); 01327 } 01328 01329 /** 01330 * xmlNanoHTTPMethodRedir: 01331 * @URL: The URL to load 01332 * @method: the HTTP method to use 01333 * @input: the input string if any 01334 * @contentType: the Content-Type information IN and OUT 01335 * @redir: the redirected URL OUT 01336 * @headers: the extra headers 01337 * @ilen: input length 01338 * 01339 * This function try to open a connection to the indicated resource 01340 * via HTTP using the given @method, adding the given extra headers 01341 * and the input buffer for the request content. 01342 * 01343 * Returns NULL in case of failure, otherwise a request handler. 01344 * The contentType, or redir, if provided must be freed by the caller 01345 */ 01346 01347 void* 01348 xmlNanoHTTPMethodRedir(const char *URL, const char *method, const char *input, 01349 char **contentType, char **redir, 01350 const char *headers, int ilen ) { 01351 xmlNanoHTTPCtxtPtr ctxt; 01352 char *bp, *p; 01353 int blen; 01354 SOCKET ret; 01355 int nbRedirects = 0; 01356 char *redirURL = NULL; 01357 #ifdef DEBUG_HTTP 01358 int xmt_bytes; 01359 #endif 01360 01361 if (URL == NULL) return(NULL); 01362 if (method == NULL) method = "GET"; 01363 xmlNanoHTTPInit(); 01364 01365 retry: 01366 if (redirURL == NULL) { 01367 ctxt = xmlNanoHTTPNewCtxt(URL); 01368 if (ctxt == NULL) 01369 return(NULL); 01370 } else { 01371 ctxt = xmlNanoHTTPNewCtxt(redirURL); 01372 if (ctxt == NULL) 01373 return(NULL); 01374 ctxt->location = xmlMemStrdup(redirURL); 01375 } 01376 01377 if ((ctxt->protocol == NULL) || (strcmp(ctxt->protocol, "http"))) { 01378 __xmlIOErr(XML_FROM_HTTP, XML_HTTP_URL_SYNTAX, "Not a valid HTTP URI"); 01379 xmlNanoHTTPFreeCtxt(ctxt); 01380 if (redirURL != NULL) xmlFree(redirURL); 01381 return(NULL); 01382 } 01383 if (ctxt->hostname == NULL) { 01384 __xmlIOErr(XML_FROM_HTTP, XML_HTTP_UNKNOWN_HOST, 01385 "Failed to identify host in URI"); 01386 xmlNanoHTTPFreeCtxt(ctxt); 01387 if (redirURL != NULL) xmlFree(redirURL); 01388 return(NULL); 01389 } 01390 if (proxy) { 01391 blen = strlen(ctxt->hostname) * 2 + 16; 01392 ret = xmlNanoHTTPConnectHost(proxy, proxyPort); 01393 } 01394 else { 01395 blen = strlen(ctxt->hostname); 01396 ret = xmlNanoHTTPConnectHost(ctxt->hostname, ctxt->port); 01397 } 01398 if (ret == INVALID_SOCKET) { 01399 xmlNanoHTTPFreeCtxt(ctxt); 01400 if (redirURL != NULL) xmlFree(redirURL); 01401 return(NULL); 01402 } 01403 ctxt->fd = ret; 01404 01405 if (input == NULL) 01406 ilen = 0; 01407 else 01408 blen += 36; 01409 01410 if (headers != NULL) 01411 blen += strlen(headers) + 2; 01412 if (contentType && *contentType) 01413 /* reserve for string plus 'Content-Type: \r\n" */ 01414 blen += strlen(*contentType) + 16; 01415 if (ctxt->query != NULL) 01416 /* 1 for '?' */ 01417 blen += strlen(ctxt->query) + 1; 01418 blen += strlen(method) + strlen(ctxt->path) + 24; 01419 #ifdef HAVE_ZLIB_H 01420 /* reserve for possible 'Accept-Encoding: gzip' string */ 01421 blen += 23; 01422 #endif 01423 if (ctxt->port != 80) { 01424 /* reserve space for ':xxxxx', incl. potential proxy */ 01425 if (proxy) 01426 blen += 12; 01427 else 01428 blen += 6; 01429 } 01430 bp = (char*)xmlMallocAtomic(blen); 01431 if ( bp == NULL ) { 01432 xmlNanoHTTPFreeCtxt( ctxt ); 01433 xmlHTTPErrMemory("allocating header buffer"); 01434 return ( NULL ); 01435 } 01436 01437 p = bp; 01438 01439 if (proxy) { 01440 if (ctxt->port != 80) { 01441 p += snprintf( p, blen - (p - bp), "%s http://%s:%d%s", 01442 method, ctxt->hostname, 01443 ctxt->port, ctxt->path ); 01444 } 01445 else 01446 p += snprintf( p, blen - (p - bp), "%s http://%s%s", method, 01447 ctxt->hostname, ctxt->path); 01448 } 01449 else 01450 p += snprintf( p, blen - (p - bp), "%s %s", method, ctxt->path); 01451 01452 if (ctxt->query != NULL) 01453 p += snprintf( p, blen - (p - bp), "?%s", ctxt->query); 01454 01455 if (ctxt->port == 80) { 01456 p += snprintf( p, blen - (p - bp), " HTTP/1.0\r\nHost: %s\r\n", 01457 ctxt->hostname); 01458 } else { 01459 p += snprintf( p, blen - (p - bp), " HTTP/1.0\r\nHost: %s:%d\r\n", 01460 ctxt->hostname, ctxt->port); 01461 } 01462 01463 #ifdef HAVE_ZLIB_H 01464 p += snprintf(p, blen - (p - bp), "Accept-Encoding: gzip\r\n"); 01465 #endif 01466 01467 if (contentType != NULL && *contentType) 01468 p += snprintf(p, blen - (p - bp), "Content-Type: %s\r\n", *contentType); 01469 01470 if (headers != NULL) 01471 p += snprintf( p, blen - (p - bp), "%s", headers ); 01472 01473 if (input != NULL) 01474 snprintf(p, blen - (p - bp), "Content-Length: %d\r\n\r\n", ilen ); 01475 else 01476 snprintf(p, blen - (p - bp), "\r\n"); 01477 01478 #ifdef DEBUG_HTTP 01479 xmlGenericError(xmlGenericErrorContext, 01480 "-> %s%s", proxy? "(Proxy) " : "", bp); 01481 if ((blen -= strlen(bp)+1) < 0) 01482 xmlGenericError(xmlGenericErrorContext, 01483 "ERROR: overflowed buffer by %d bytes\n", -blen); 01484 #endif 01485 ctxt->outptr = ctxt->out = bp; 01486 ctxt->state = XML_NANO_HTTP_WRITE; 01487 blen = strlen( ctxt->out ); 01488 #ifdef DEBUG_HTTP 01489 xmt_bytes = xmlNanoHTTPSend(ctxt, ctxt->out, blen ); 01490 if ( xmt_bytes != blen ) 01491 xmlGenericError( xmlGenericErrorContext, 01492 "xmlNanoHTTPMethodRedir: Only %d of %d %s %s\n", 01493 xmt_bytes, blen, 01494 "bytes of HTTP headers sent to host", 01495 ctxt->hostname ); 01496 #else 01497 xmlNanoHTTPSend(ctxt, ctxt->out, blen ); 01498 #endif 01499 01500 if ( input != NULL ) { 01501 #ifdef DEBUG_HTTP 01502 xmt_bytes = xmlNanoHTTPSend( ctxt, input, ilen ); 01503 01504 if ( xmt_bytes != ilen ) 01505 xmlGenericError( xmlGenericErrorContext, 01506 "xmlNanoHTTPMethodRedir: Only %d of %d %s %s\n", 01507 xmt_bytes, ilen, 01508 "bytes of HTTP content sent to host", 01509 ctxt->hostname ); 01510 #else 01511 xmlNanoHTTPSend( ctxt, input, ilen ); 01512 #endif 01513 } 01514 01515 ctxt->state = XML_NANO_HTTP_READ; 01516 01517 while ((p = xmlNanoHTTPReadLine(ctxt)) != NULL) { 01518 if (*p == 0) { 01519 ctxt->content = ctxt->inrptr; 01520 xmlFree(p); 01521 break; 01522 } 01523 xmlNanoHTTPScanAnswer(ctxt, p); 01524 01525 #ifdef DEBUG_HTTP 01526 xmlGenericError(xmlGenericErrorContext, "<- %s\n", p); 01527 #endif 01528 xmlFree(p); 01529 } 01530 01531 if ((ctxt->location != NULL) && (ctxt->returnValue >= 300) && 01532 (ctxt->returnValue < 400)) { 01533 #ifdef DEBUG_HTTP 01534 xmlGenericError(xmlGenericErrorContext, 01535 "\nRedirect to: %s\n", ctxt->location); 01536 #endif 01537 while ( xmlNanoHTTPRecv(ctxt) > 0 ) ; 01538 if (nbRedirects < XML_NANO_HTTP_MAX_REDIR) { 01539 nbRedirects++; 01540 if (redirURL != NULL) 01541 xmlFree(redirURL); 01542 redirURL = xmlMemStrdup(ctxt->location); 01543 xmlNanoHTTPFreeCtxt(ctxt); 01544 goto retry; 01545 } 01546 xmlNanoHTTPFreeCtxt(ctxt); 01547 if (redirURL != NULL) xmlFree(redirURL); 01548 #ifdef DEBUG_HTTP 01549 xmlGenericError(xmlGenericErrorContext, 01550 "xmlNanoHTTPMethodRedir: Too many redirects, aborting ...\n"); 01551 #endif 01552 return(NULL); 01553 } 01554 01555 if (contentType != NULL) { 01556 if (ctxt->contentType != NULL) 01557 *contentType = xmlMemStrdup(ctxt->contentType); 01558 else 01559 *contentType = NULL; 01560 } 01561 01562 if ((redir != NULL) && (redirURL != NULL)) { 01563 *redir = redirURL; 01564 } else { 01565 if (redirURL != NULL) 01566 xmlFree(redirURL); 01567 if (redir != NULL) 01568 *redir = NULL; 01569 } 01570 01571 #ifdef DEBUG_HTTP 01572 if (ctxt->contentType != NULL) 01573 xmlGenericError(xmlGenericErrorContext, 01574 "\nCode %d, content-type '%s'\n\n", 01575 ctxt->returnValue, ctxt->contentType); 01576 else 01577 xmlGenericError(xmlGenericErrorContext, 01578 "\nCode %d, no content-type\n\n", 01579 ctxt->returnValue); 01580 #endif 01581 01582 return((void *) ctxt); 01583 } 01584 01585 /** 01586 * xmlNanoHTTPMethod: 01587 * @URL: The URL to load 01588 * @method: the HTTP method to use 01589 * @input: the input string if any 01590 * @contentType: the Content-Type information IN and OUT 01591 * @headers: the extra headers 01592 * @ilen: input length 01593 * 01594 * This function try to open a connection to the indicated resource 01595 * via HTTP using the given @method, adding the given extra headers 01596 * and the input buffer for the request content. 01597 * 01598 * Returns NULL in case of failure, otherwise a request handler. 01599 * The contentType, if provided must be freed by the caller 01600 */ 01601 01602 void* 01603 xmlNanoHTTPMethod(const char *URL, const char *method, const char *input, 01604 char **contentType, const char *headers, int ilen) { 01605 return(xmlNanoHTTPMethodRedir(URL, method, input, contentType, 01606 NULL, headers, ilen)); 01607 } 01608 01609 /** 01610 * xmlNanoHTTPFetch: 01611 * @URL: The URL to load 01612 * @filename: the filename where the content should be saved 01613 * @contentType: if available the Content-Type information will be 01614 * returned at that location 01615 * 01616 * This function try to fetch the indicated resource via HTTP GET 01617 * and save it's content in the file. 01618 * 01619 * Returns -1 in case of failure, 0 incase of success. The contentType, 01620 * if provided must be freed by the caller 01621 */ 01622 int 01623 xmlNanoHTTPFetch(const char *URL, const char *filename, char **contentType) { 01624 void *ctxt = NULL; 01625 char *buf = NULL; 01626 int fd; 01627 int len; 01628 int ret = 0; 01629 01630 if (filename == NULL) return(-1); 01631 ctxt = xmlNanoHTTPOpen(URL, contentType); 01632 if (ctxt == NULL) return(-1); 01633 01634 if (!strcmp(filename, "-")) 01635 fd = 0; 01636 else { 01637 fd = open(filename, O_CREAT | O_WRONLY, 00644); 01638 if (fd < 0) { 01639 xmlNanoHTTPClose(ctxt); 01640 if ((contentType != NULL) && (*contentType != NULL)) { 01641 xmlFree(*contentType); 01642 *contentType = NULL; 01643 } 01644 return(-1); 01645 } 01646 } 01647 01648 xmlNanoHTTPFetchContent( ctxt, &buf, &len ); 01649 if ( len > 0 ) { 01650 if (write(fd, buf, len) == -1) { 01651 ret = -1; 01652 } 01653 } 01654 01655 xmlNanoHTTPClose(ctxt); 01656 close(fd); 01657 return(ret); 01658 } 01659 01660 #ifdef LIBXML_OUTPUT_ENABLED 01661 /** 01662 * xmlNanoHTTPSave: 01663 * @ctxt: the HTTP context 01664 * @filename: the filename where the content should be saved 01665 * 01666 * This function saves the output of the HTTP transaction to a file 01667 * It closes and free the context at the end 01668 * 01669 * Returns -1 in case of failure, 0 incase of success. 01670 */ 01671 int 01672 xmlNanoHTTPSave(void *ctxt, const char *filename) { 01673 char *buf = NULL; 01674 int fd; 01675 int len; 01676 int ret = 0; 01677 01678 if ((ctxt == NULL) || (filename == NULL)) return(-1); 01679 01680 if (!strcmp(filename, "-")) 01681 fd = 0; 01682 else { 01683 fd = open(filename, O_CREAT | O_WRONLY, 0666); 01684 if (fd < 0) { 01685 xmlNanoHTTPClose(ctxt); 01686 return(-1); 01687 } 01688 } 01689 01690 xmlNanoHTTPFetchContent( ctxt, &buf, &len ); 01691 if ( len > 0 ) { 01692 if (write(fd, buf, len) == -1) { 01693 ret = -1; 01694 } 01695 } 01696 01697 xmlNanoHTTPClose(ctxt); 01698 close(fd); 01699 return(ret); 01700 } 01701 #endif /* LIBXML_OUTPUT_ENABLED */ 01702 01703 /** 01704 * xmlNanoHTTPReturnCode: 01705 * @ctx: the HTTP context 01706 * 01707 * Get the latest HTTP return code received 01708 * 01709 * Returns the HTTP return code for the request. 01710 */ 01711 int 01712 xmlNanoHTTPReturnCode(void *ctx) { 01713 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx; 01714 01715 if (ctxt == NULL) return(-1); 01716 01717 return(ctxt->returnValue); 01718 } 01719 01720 /** 01721 * xmlNanoHTTPAuthHeader: 01722 * @ctx: the HTTP context 01723 * 01724 * Get the authentication header of an HTTP context 01725 * 01726 * Returns the stashed value of the WWW-Authenticate or Proxy-Authenticate 01727 * header. 01728 */ 01729 const char * 01730 xmlNanoHTTPAuthHeader(void *ctx) { 01731 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx; 01732 01733 if (ctxt == NULL) return(NULL); 01734 01735 return(ctxt->authHeader); 01736 } 01737 01738 /** 01739 * xmlNanoHTTPContentLength: 01740 * @ctx: the HTTP context 01741 * 01742 * Provides the specified content length from the HTTP header. 01743 * 01744 * Return the specified content length from the HTTP header. Note that 01745 * a value of -1 indicates that the content length element was not included in 01746 * the response header. 01747 */ 01748 int 01749 xmlNanoHTTPContentLength( void * ctx ) { 01750 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr)ctx; 01751 01752 return ( ( ctxt == NULL ) ? -1 : ctxt->ContentLength ); 01753 } 01754 01755 /** 01756 * xmlNanoHTTPRedir: 01757 * @ctx: the HTTP context 01758 * 01759 * Provides the specified redirection URL if available from the HTTP header. 01760 * 01761 * Return the specified redirection URL or NULL if not redirected. 01762 */ 01763 const char * 01764 xmlNanoHTTPRedir( void * ctx ) { 01765 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr)ctx; 01766 01767 return ( ( ctxt == NULL ) ? NULL : ctxt->location ); 01768 } 01769 01770 /** 01771 * xmlNanoHTTPEncoding: 01772 * @ctx: the HTTP context 01773 * 01774 * Provides the specified encoding if specified in the HTTP headers. 01775 * 01776 * Return the specified encoding or NULL if not available 01777 */ 01778 const char * 01779 xmlNanoHTTPEncoding( void * ctx ) { 01780 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr)ctx; 01781 01782 return ( ( ctxt == NULL ) ? NULL : ctxt->encoding ); 01783 } 01784 01785 /** 01786 * xmlNanoHTTPMimeType: 01787 * @ctx: the HTTP context 01788 * 01789 * Provides the specified Mime-Type if specified in the HTTP headers. 01790 * 01791 * Return the specified Mime-Type or NULL if not available 01792 */ 01793 const char * 01794 xmlNanoHTTPMimeType( void * ctx ) { 01795 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr)ctx; 01796 01797 return ( ( ctxt == NULL ) ? NULL : ctxt->mimeType ); 01798 } 01799 01800 /** 01801 * xmlNanoHTTPFetchContent: 01802 * @ctx: the HTTP context 01803 * @ptr: pointer to set to the content buffer. 01804 * @len: integer pointer to hold the length of the content 01805 * 01806 * Check if all the content was read 01807 * 01808 * Returns 0 if all the content was read and available, returns 01809 * -1 if received content length was less than specified or an error 01810 * occurred. 01811 */ 01812 static int 01813 xmlNanoHTTPFetchContent( void * ctx, char ** ptr, int * len ) { 01814 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr)ctx; 01815 01816 int rc = 0; 01817 int cur_lgth; 01818 int rcvd_lgth; 01819 int dummy_int; 01820 char * dummy_ptr = NULL; 01821 01822 /* Dummy up return input parameters if not provided */ 01823 01824 if ( len == NULL ) 01825 len = &dummy_int; 01826 01827 if ( ptr == NULL ) 01828 ptr = &dummy_ptr; 01829 01830 /* But can't work without the context pointer */ 01831 01832 if ( ( ctxt == NULL ) || ( ctxt->content == NULL ) ) { 01833 *len = 0; 01834 *ptr = NULL; 01835 return ( -1 ); 01836 } 01837 01838 rcvd_lgth = ctxt->inptr - ctxt->content; 01839 01840 while ( (cur_lgth = xmlNanoHTTPRecv( ctxt )) > 0 ) { 01841 01842 rcvd_lgth += cur_lgth; 01843 if ( (ctxt->ContentLength > 0) && (rcvd_lgth >= ctxt->ContentLength) ) 01844 break; 01845 } 01846 01847 *ptr = ctxt->content; 01848 *len = rcvd_lgth; 01849 01850 if ( ( ctxt->ContentLength > 0 ) && ( rcvd_lgth < ctxt->ContentLength ) ) 01851 rc = -1; 01852 else if ( rcvd_lgth == 0 ) 01853 rc = -1; 01854 01855 return ( rc ); 01856 } 01857 01858 #ifdef STANDALONE 01859 int main(int argc, char **argv) { 01860 char *contentType = NULL; 01861 01862 if (argv[1] != NULL) { 01863 if (argv[2] != NULL) 01864 xmlNanoHTTPFetch(argv[1], argv[2], &contentType); 01865 else 01866 xmlNanoHTTPFetch(argv[1], "-", &contentType); 01867 if (contentType != NULL) xmlFree(contentType); 01868 } else { 01869 xmlGenericError(xmlGenericErrorContext, 01870 "%s: minimal HTTP GET implementation\n", argv[0]); 01871 xmlGenericError(xmlGenericErrorContext, 01872 "\tusage %s [ URL [ filename ] ]\n", argv[0]); 01873 } 01874 xmlNanoHTTPCleanup(); 01875 xmlMemoryDump(); 01876 return(0); 01877 } 01878 #endif /* STANDALONE */ 01879 #else /* !LIBXML_HTTP_ENABLED */ 01880 #ifdef STANDALONE 01881 #include <stdio.h> 01882 int main(int argc, char **argv) { 01883 xmlGenericError(xmlGenericErrorContext, 01884 "%s : HTTP support not compiled in\n", argv[0]); 01885 return(0); 01886 } 01887 #endif /* STANDALONE */ 01888 #endif /* LIBXML_HTTP_ENABLED */ 01889 #define bottom_nanohttp 01890 #include "elfgcchack.h" 01891
Generated on Thu Jul 14 2022 13:59:11 by
