OLD | NEW |
(Empty) | |
| 1 /* |
| 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/ |
| 8 * |
| 9 * See Copyright for the status of this software. |
| 10 * |
| 11 * daniel@veillard.com |
| 12 */ |
| 13 |
| 14 #define NEED_SOCKETS |
| 15 #define IN_LIBXML |
| 16 #include "libxml.h" |
| 17 |
| 18 #ifdef LIBXML_HTTP_ENABLED |
| 19 #include <string.h> |
| 20 |
| 21 #ifdef HAVE_STDLIB_H |
| 22 #include <stdlib.h> |
| 23 #endif |
| 24 #ifdef HAVE_UNISTD_H |
| 25 #include <unistd.h> |
| 26 #endif |
| 27 #ifdef HAVE_SYS_TYPES_H |
| 28 #include <sys/types.h> |
| 29 #endif |
| 30 #ifdef HAVE_SYS_SOCKET_H |
| 31 #include <sys/socket.h> |
| 32 #endif |
| 33 #ifdef HAVE_NETINET_IN_H |
| 34 #include <netinet/in.h> |
| 35 #endif |
| 36 #ifdef HAVE_ARPA_INET_H |
| 37 #include <arpa/inet.h> |
| 38 #endif |
| 39 #ifdef HAVE_NETDB_H |
| 40 #include <netdb.h> |
| 41 #endif |
| 42 #ifdef HAVE_RESOLV_H |
| 43 #ifdef HAVE_ARPA_NAMESER_H |
| 44 #include <arpa/nameser.h> |
| 45 #endif |
| 46 #include <resolv.h> |
| 47 #endif |
| 48 #ifdef HAVE_FCNTL_H |
| 49 #include <fcntl.h> |
| 50 #endif |
| 51 #ifdef HAVE_ERRNO_H |
| 52 #include <errno.h> |
| 53 #endif |
| 54 #ifdef HAVE_SYS_TIME_H |
| 55 #include <sys/time.h> |
| 56 #endif |
| 57 #ifndef HAVE_POLL_H |
| 58 #ifdef HAVE_SYS_SELECT_H |
| 59 #include <sys/select.h> |
| 60 #endif |
| 61 #else |
| 62 #include <poll.h> |
| 63 #endif |
| 64 #ifdef HAVE_STRINGS_H |
| 65 #include <strings.h> |
| 66 #endif |
| 67 #ifdef HAVE_ZLIB_H |
| 68 #include <zlib.h> |
| 69 #endif |
| 70 |
| 71 |
| 72 #ifdef VMS |
| 73 #include <stropts> |
| 74 #define XML_SOCKLEN_T unsigned int |
| 75 #endif |
| 76 |
| 77 #if defined(__MINGW32__) || defined(_WIN32_WCE) |
| 78 #ifndef _WINSOCKAPI_ |
| 79 #define _WINSOCKAPI_ |
| 80 #endif |
| 81 #include <wsockcompat.h> |
| 82 #include <winsock2.h> |
| 83 #undef XML_SOCKLEN_T |
| 84 #define XML_SOCKLEN_T unsigned int |
| 85 #endif |
| 86 |
| 87 #include <libxml/globals.h> |
| 88 #include <libxml/xmlerror.h> |
| 89 #include <libxml/xmlmemory.h> |
| 90 #include <libxml/parser.h> /* for xmlStr(n)casecmp() */ |
| 91 #include <libxml/nanohttp.h> |
| 92 #include <libxml/globals.h> |
| 93 #include <libxml/uri.h> |
| 94 |
| 95 /** |
| 96 * A couple portability macros |
| 97 */ |
| 98 #ifndef _WINSOCKAPI_ |
| 99 #if !defined(__BEOS__) || defined(__HAIKU__) |
| 100 #define closesocket(s) close(s) |
| 101 #endif |
| 102 #define SOCKET int |
| 103 #define INVALID_SOCKET (-1) |
| 104 #endif |
| 105 |
| 106 #ifdef __BEOS__ |
| 107 #ifndef PF_INET |
| 108 #define PF_INET AF_INET |
| 109 #endif |
| 110 #endif |
| 111 |
| 112 #ifndef XML_SOCKLEN_T |
| 113 #define XML_SOCKLEN_T unsigned int |
| 114 #endif |
| 115 |
| 116 #ifdef STANDALONE |
| 117 #define DEBUG_HTTP |
| 118 #define xmlStrncasecmp(a, b, n) strncasecmp((char *)a, (char *)b, n) |
| 119 #define xmlStrcasecmpi(a, b) strcasecmp((char *)a, (char *)b) |
| 120 #endif |
| 121 |
| 122 #define XML_NANO_HTTP_MAX_REDIR 10 |
| 123 |
| 124 #define XML_NANO_HTTP_CHUNK 4096 |
| 125 |
| 126 #define XML_NANO_HTTP_CLOSED 0 |
| 127 #define XML_NANO_HTTP_WRITE 1 |
| 128 #define XML_NANO_HTTP_READ 2 |
| 129 #define XML_NANO_HTTP_NONE 4 |
| 130 |
| 131 typedef struct xmlNanoHTTPCtxt { |
| 132 char *protocol; /* the protocol name */ |
| 133 char *hostname; /* the host name */ |
| 134 int port; /* the port */ |
| 135 char *path; /* the path within the URL */ |
| 136 char *query; /* the query string */ |
| 137 SOCKET fd; /* the file descriptor for the socket */ |
| 138 int state; /* WRITE / READ / CLOSED */ |
| 139 char *out; /* buffer sent (zero terminated) */ |
| 140 char *outptr; /* index within the buffer sent */ |
| 141 char *in; /* the receiving buffer */ |
| 142 char *content; /* the start of the content */ |
| 143 char *inptr; /* the next byte to read from network */ |
| 144 char *inrptr; /* the next byte to give back to the client */ |
| 145 int inlen; /* len of the input buffer */ |
| 146 int last; /* return code for last operation */ |
| 147 int returnValue; /* the protocol return value */ |
| 148 int version; /* the protocol version */ |
| 149 int ContentLength; /* specified content length from HTTP header */ |
| 150 char *contentType; /* the MIME type for the input */ |
| 151 char *location; /* the new URL in case of redirect */ |
| 152 char *authHeader; /* contents of {WWW,Proxy}-Authenticate header */ |
| 153 char *encoding; /* encoding extracted from the contentType */ |
| 154 char *mimeType; /* Mime-Type extracted from the contentType */ |
| 155 #ifdef HAVE_ZLIB_H |
| 156 z_stream *strm; /* Zlib stream object */ |
| 157 int usesGzip; /* "Content-Encoding: gzip" was detected */ |
| 158 #endif |
| 159 } xmlNanoHTTPCtxt, *xmlNanoHTTPCtxtPtr; |
| 160 |
| 161 static int initialized = 0; |
| 162 static char *proxy = NULL; /* the proxy name if any */ |
| 163 static int proxyPort; /* the proxy port if any */ |
| 164 static unsigned int timeout = 60;/* the select() timeout in seconds */ |
| 165 |
| 166 static int xmlNanoHTTPFetchContent( void * ctx, char ** ptr, int * len ); |
| 167 |
| 168 /** |
| 169 * xmlHTTPErrMemory: |
| 170 * @extra: extra informations |
| 171 * |
| 172 * Handle an out of memory condition |
| 173 */ |
| 174 static void |
| 175 xmlHTTPErrMemory(const char *extra) |
| 176 { |
| 177 __xmlSimpleError(XML_FROM_HTTP, XML_ERR_NO_MEMORY, NULL, NULL, extra); |
| 178 } |
| 179 |
| 180 /** |
| 181 * A portability function |
| 182 */ |
| 183 static int socket_errno(void) { |
| 184 #ifdef _WINSOCKAPI_ |
| 185 return(WSAGetLastError()); |
| 186 #else |
| 187 return(errno); |
| 188 #endif |
| 189 } |
| 190 |
| 191 #ifdef SUPPORT_IP6 |
| 192 static |
| 193 int have_ipv6(void) { |
| 194 SOCKET s; |
| 195 |
| 196 s = socket (AF_INET6, SOCK_STREAM, 0); |
| 197 if (s != INVALID_SOCKET) { |
| 198 close (s); |
| 199 return (1); |
| 200 } |
| 201 return (0); |
| 202 } |
| 203 #endif |
| 204 |
| 205 /** |
| 206 * xmlNanoHTTPInit: |
| 207 * |
| 208 * Initialize the HTTP protocol layer. |
| 209 * Currently it just checks for proxy informations |
| 210 */ |
| 211 |
| 212 void |
| 213 xmlNanoHTTPInit(void) { |
| 214 const char *env; |
| 215 #ifdef _WINSOCKAPI_ |
| 216 WSADATA wsaData; |
| 217 #endif |
| 218 |
| 219 if (initialized) |
| 220 return; |
| 221 |
| 222 #ifdef _WINSOCKAPI_ |
| 223 if (WSAStartup(MAKEWORD(1, 1), &wsaData) != 0) |
| 224 return; |
| 225 #endif |
| 226 |
| 227 if (proxy == NULL) { |
| 228 proxyPort = 80; |
| 229 env = getenv("no_proxy"); |
| 230 if (env && ((env[0] == '*') && (env[1] == 0))) |
| 231 goto done; |
| 232 env = getenv("http_proxy"); |
| 233 if (env != NULL) { |
| 234 xmlNanoHTTPScanProxy(env); |
| 235 goto done; |
| 236 } |
| 237 env = getenv("HTTP_PROXY"); |
| 238 if (env != NULL) { |
| 239 xmlNanoHTTPScanProxy(env); |
| 240 goto done; |
| 241 } |
| 242 } |
| 243 done: |
| 244 initialized = 1; |
| 245 } |
| 246 |
| 247 /** |
| 248 * xmlNanoHTTPCleanup: |
| 249 * |
| 250 * Cleanup the HTTP protocol layer. |
| 251 */ |
| 252 |
| 253 void |
| 254 xmlNanoHTTPCleanup(void) { |
| 255 if (proxy != NULL) { |
| 256 xmlFree(proxy); |
| 257 proxy = NULL; |
| 258 } |
| 259 #ifdef _WINSOCKAPI_ |
| 260 if (initialized) |
| 261 WSACleanup(); |
| 262 #endif |
| 263 initialized = 0; |
| 264 return; |
| 265 } |
| 266 |
| 267 /** |
| 268 * xmlNanoHTTPScanURL: |
| 269 * @ctxt: an HTTP context |
| 270 * @URL: The URL used to initialize the context |
| 271 * |
| 272 * (Re)Initialize an HTTP context by parsing the URL and finding |
| 273 * the protocol host port and path it indicates. |
| 274 */ |
| 275 |
| 276 static void |
| 277 xmlNanoHTTPScanURL(xmlNanoHTTPCtxtPtr ctxt, const char *URL) { |
| 278 xmlURIPtr uri; |
| 279 int len; |
| 280 |
| 281 /* |
| 282 * Clear any existing data from the context |
| 283 */ |
| 284 if (ctxt->protocol != NULL) { |
| 285 xmlFree(ctxt->protocol); |
| 286 ctxt->protocol = NULL; |
| 287 } |
| 288 if (ctxt->hostname != NULL) { |
| 289 xmlFree(ctxt->hostname); |
| 290 ctxt->hostname = NULL; |
| 291 } |
| 292 if (ctxt->path != NULL) { |
| 293 xmlFree(ctxt->path); |
| 294 ctxt->path = NULL; |
| 295 } |
| 296 if (ctxt->query != NULL) { |
| 297 xmlFree(ctxt->query); |
| 298 ctxt->query = NULL; |
| 299 } |
| 300 if (URL == NULL) return; |
| 301 |
| 302 uri = xmlParseURIRaw(URL, 1); |
| 303 if (uri == NULL) |
| 304 return; |
| 305 |
| 306 if ((uri->scheme == NULL) || (uri->server == NULL)) { |
| 307 xmlFreeURI(uri); |
| 308 return; |
| 309 } |
| 310 |
| 311 ctxt->protocol = xmlMemStrdup(uri->scheme); |
| 312 /* special case of IPv6 addresses, the [] need to be removed */ |
| 313 if ((uri->server != NULL) && (*uri->server == '[')) { |
| 314 len = strlen(uri->server); |
| 315 if ((len > 2) && (uri->server[len - 1] == ']')) { |
| 316 ctxt->hostname = (char *) xmlCharStrndup(uri->server + 1, len -2); |
| 317 } else |
| 318 ctxt->hostname = xmlMemStrdup(uri->server); |
| 319 } else |
| 320 ctxt->hostname = xmlMemStrdup(uri->server); |
| 321 if (uri->path != NULL) |
| 322 ctxt->path = xmlMemStrdup(uri->path); |
| 323 else |
| 324 ctxt->path = xmlMemStrdup("/"); |
| 325 if (uri->query != NULL) |
| 326 ctxt->query = xmlMemStrdup(uri->query); |
| 327 if (uri->port != 0) |
| 328 ctxt->port = uri->port; |
| 329 |
| 330 xmlFreeURI(uri); |
| 331 } |
| 332 |
| 333 /** |
| 334 * xmlNanoHTTPScanProxy: |
| 335 * @URL: The proxy URL used to initialize the proxy context |
| 336 * |
| 337 * (Re)Initialize the HTTP Proxy context by parsing the URL and finding |
| 338 * the protocol host port it indicates. |
| 339 * Should be like http://myproxy/ or http://myproxy:3128/ |
| 340 * A NULL URL cleans up proxy informations. |
| 341 */ |
| 342 |
| 343 void |
| 344 xmlNanoHTTPScanProxy(const char *URL) { |
| 345 xmlURIPtr uri; |
| 346 |
| 347 if (proxy != NULL) { |
| 348 xmlFree(proxy); |
| 349 proxy = NULL; |
| 350 } |
| 351 proxyPort = 0; |
| 352 |
| 353 #ifdef DEBUG_HTTP |
| 354 if (URL == NULL) |
| 355 xmlGenericError(xmlGenericErrorContext, |
| 356 "Removing HTTP proxy info\n"); |
| 357 else |
| 358 xmlGenericError(xmlGenericErrorContext, |
| 359 "Using HTTP proxy %s\n", URL); |
| 360 #endif |
| 361 if (URL == NULL) return; |
| 362 |
| 363 uri = xmlParseURIRaw(URL, 1); |
| 364 if ((uri == NULL) || (uri->scheme == NULL) || |
| 365 (strcmp(uri->scheme, "http")) || (uri->server == NULL)) { |
| 366 __xmlIOErr(XML_FROM_HTTP, XML_HTTP_URL_SYNTAX, "Syntax Error\n"); |
| 367 if (uri != NULL) |
| 368 xmlFreeURI(uri); |
| 369 return; |
| 370 } |
| 371 |
| 372 proxy = xmlMemStrdup(uri->server); |
| 373 if (uri->port != 0) |
| 374 proxyPort = uri->port; |
| 375 |
| 376 xmlFreeURI(uri); |
| 377 } |
| 378 |
| 379 /** |
| 380 * xmlNanoHTTPNewCtxt: |
| 381 * @URL: The URL used to initialize the context |
| 382 * |
| 383 * Allocate and initialize a new HTTP context. |
| 384 * |
| 385 * Returns an HTTP context or NULL in case of error. |
| 386 */ |
| 387 |
| 388 static xmlNanoHTTPCtxtPtr |
| 389 xmlNanoHTTPNewCtxt(const char *URL) { |
| 390 xmlNanoHTTPCtxtPtr ret; |
| 391 |
| 392 ret = (xmlNanoHTTPCtxtPtr) xmlMalloc(sizeof(xmlNanoHTTPCtxt)); |
| 393 if (ret == NULL) { |
| 394 xmlHTTPErrMemory("allocating context"); |
| 395 return(NULL); |
| 396 } |
| 397 |
| 398 memset(ret, 0, sizeof(xmlNanoHTTPCtxt)); |
| 399 ret->port = 80; |
| 400 ret->returnValue = 0; |
| 401 ret->fd = INVALID_SOCKET; |
| 402 ret->ContentLength = -1; |
| 403 |
| 404 xmlNanoHTTPScanURL(ret, URL); |
| 405 |
| 406 return(ret); |
| 407 } |
| 408 |
| 409 /** |
| 410 * xmlNanoHTTPFreeCtxt: |
| 411 * @ctxt: an HTTP context |
| 412 * |
| 413 * Frees the context after closing the connection. |
| 414 */ |
| 415 |
| 416 static void |
| 417 xmlNanoHTTPFreeCtxt(xmlNanoHTTPCtxtPtr ctxt) { |
| 418 if (ctxt == NULL) return; |
| 419 if (ctxt->hostname != NULL) xmlFree(ctxt->hostname); |
| 420 if (ctxt->protocol != NULL) xmlFree(ctxt->protocol); |
| 421 if (ctxt->path != NULL) xmlFree(ctxt->path); |
| 422 if (ctxt->query != NULL) xmlFree(ctxt->query); |
| 423 if (ctxt->out != NULL) xmlFree(ctxt->out); |
| 424 if (ctxt->in != NULL) xmlFree(ctxt->in); |
| 425 if (ctxt->contentType != NULL) xmlFree(ctxt->contentType); |
| 426 if (ctxt->encoding != NULL) xmlFree(ctxt->encoding); |
| 427 if (ctxt->mimeType != NULL) xmlFree(ctxt->mimeType); |
| 428 if (ctxt->location != NULL) xmlFree(ctxt->location); |
| 429 if (ctxt->authHeader != NULL) xmlFree(ctxt->authHeader); |
| 430 #ifdef HAVE_ZLIB_H |
| 431 if (ctxt->strm != NULL) { |
| 432 inflateEnd(ctxt->strm); |
| 433 xmlFree(ctxt->strm); |
| 434 } |
| 435 #endif |
| 436 |
| 437 ctxt->state = XML_NANO_HTTP_NONE; |
| 438 if (ctxt->fd != INVALID_SOCKET) closesocket(ctxt->fd); |
| 439 ctxt->fd = INVALID_SOCKET; |
| 440 xmlFree(ctxt); |
| 441 } |
| 442 |
| 443 /** |
| 444 * xmlNanoHTTPSend: |
| 445 * @ctxt: an HTTP context |
| 446 * |
| 447 * Send the input needed to initiate the processing on the server side |
| 448 * Returns number of bytes sent or -1 on error. |
| 449 */ |
| 450 |
| 451 static int |
| 452 xmlNanoHTTPSend(xmlNanoHTTPCtxtPtr ctxt, const char *xmt_ptr, int outlen) |
| 453 { |
| 454 int total_sent = 0; |
| 455 #ifdef HAVE_POLL_H |
| 456 struct pollfd p; |
| 457 #else |
| 458 struct timeval tv; |
| 459 fd_set wfd; |
| 460 #endif |
| 461 |
| 462 if ((ctxt->state & XML_NANO_HTTP_WRITE) && (xmt_ptr != NULL)) { |
| 463 while (total_sent < outlen) { |
| 464 int nsent = send(ctxt->fd, SEND_ARG2_CAST (xmt_ptr + total_sent), |
| 465 outlen - total_sent, 0); |
| 466 |
| 467 if (nsent > 0) |
| 468 total_sent += nsent; |
| 469 else if ((nsent == -1) && |
| 470 #if defined(EAGAIN) && EAGAIN != EWOULDBLOCK |
| 471 (socket_errno() != EAGAIN) && |
| 472 #endif |
| 473 (socket_errno() != EWOULDBLOCK)) { |
| 474 __xmlIOErr(XML_FROM_HTTP, 0, "send failed\n"); |
| 475 if (total_sent == 0) |
| 476 total_sent = -1; |
| 477 break; |
| 478 } else { |
| 479 /* |
| 480 * No data sent |
| 481 * Since non-blocking sockets are used, wait for |
| 482 * socket to be writable or default timeout prior |
| 483 * to retrying. |
| 484 */ |
| 485 #ifndef HAVE_POLL_H |
| 486 #ifndef _WINSOCKAPI_ |
| 487 if (ctxt->fd > FD_SETSIZE) |
| 488 return -1; |
| 489 #endif |
| 490 |
| 491 tv.tv_sec = timeout; |
| 492 tv.tv_usec = 0; |
| 493 FD_ZERO(&wfd); |
| 494 #ifdef _MSC_VER |
| 495 #pragma warning(push) |
| 496 #pragma warning(disable: 4018) |
| 497 #endif |
| 498 FD_SET(ctxt->fd, &wfd); |
| 499 #ifdef _MSC_VER |
| 500 #pragma warning(pop) |
| 501 #endif |
| 502 (void) select(ctxt->fd + 1, NULL, &wfd, NULL, &tv); |
| 503 #else |
| 504 p.fd = ctxt->fd; |
| 505 p.events = POLLOUT; |
| 506 (void) poll(&p, 1, timeout * 1000); |
| 507 #endif /* !HAVE_POLL_H */ |
| 508 } |
| 509 } |
| 510 } |
| 511 |
| 512 return total_sent; |
| 513 } |
| 514 |
| 515 /** |
| 516 * xmlNanoHTTPRecv: |
| 517 * @ctxt: an HTTP context |
| 518 * |
| 519 * Read information coming from the HTTP connection. |
| 520 * This is a blocking call (but it blocks in select(), not read()). |
| 521 * |
| 522 * Returns the number of byte read or -1 in case of error. |
| 523 */ |
| 524 |
| 525 static int |
| 526 xmlNanoHTTPRecv(xmlNanoHTTPCtxtPtr ctxt) |
| 527 { |
| 528 #ifdef HAVE_POLL_H |
| 529 struct pollfd p; |
| 530 #else |
| 531 fd_set rfd; |
| 532 struct timeval tv; |
| 533 #endif |
| 534 |
| 535 |
| 536 while (ctxt->state & XML_NANO_HTTP_READ) { |
| 537 if (ctxt->in == NULL) { |
| 538 ctxt->in = (char *) xmlMallocAtomic(65000 * sizeof(char)); |
| 539 if (ctxt->in == NULL) { |
| 540 xmlHTTPErrMemory("allocating input"); |
| 541 ctxt->last = -1; |
| 542 return (-1); |
| 543 } |
| 544 ctxt->inlen = 65000; |
| 545 ctxt->inptr = ctxt->content = ctxt->inrptr = ctxt->in; |
| 546 } |
| 547 if (ctxt->inrptr > ctxt->in + XML_NANO_HTTP_CHUNK) { |
| 548 int delta = ctxt->inrptr - ctxt->in; |
| 549 int len = ctxt->inptr - ctxt->inrptr; |
| 550 |
| 551 memmove(ctxt->in, ctxt->inrptr, len); |
| 552 ctxt->inrptr -= delta; |
| 553 ctxt->content -= delta; |
| 554 ctxt->inptr -= delta; |
| 555 } |
| 556 if ((ctxt->in + ctxt->inlen) < (ctxt->inptr + XML_NANO_HTTP_CHUNK)) { |
| 557 int d_inptr = ctxt->inptr - ctxt->in; |
| 558 int d_content = ctxt->content - ctxt->in; |
| 559 int d_inrptr = ctxt->inrptr - ctxt->in; |
| 560 char *tmp_ptr = ctxt->in; |
| 561 |
| 562 ctxt->inlen *= 2; |
| 563 ctxt->in = (char *) xmlRealloc(tmp_ptr, ctxt->inlen); |
| 564 if (ctxt->in == NULL) { |
| 565 xmlHTTPErrMemory("allocating input buffer"); |
| 566 xmlFree(tmp_ptr); |
| 567 ctxt->last = -1; |
| 568 return (-1); |
| 569 } |
| 570 ctxt->inptr = ctxt->in + d_inptr; |
| 571 ctxt->content = ctxt->in + d_content; |
| 572 ctxt->inrptr = ctxt->in + d_inrptr; |
| 573 } |
| 574 ctxt->last = recv(ctxt->fd, ctxt->inptr, XML_NANO_HTTP_CHUNK, 0); |
| 575 if (ctxt->last > 0) { |
| 576 ctxt->inptr += ctxt->last; |
| 577 return (ctxt->last); |
| 578 } |
| 579 if (ctxt->last == 0) { |
| 580 return (0); |
| 581 } |
| 582 if (ctxt->last == -1) { |
| 583 switch (socket_errno()) { |
| 584 case EINPROGRESS: |
| 585 case EWOULDBLOCK: |
| 586 #if defined(EAGAIN) && EAGAIN != EWOULDBLOCK |
| 587 case EAGAIN: |
| 588 #endif |
| 589 break; |
| 590 |
| 591 case ECONNRESET: |
| 592 case ESHUTDOWN: |
| 593 return (0); |
| 594 |
| 595 default: |
| 596 __xmlIOErr(XML_FROM_HTTP, 0, "recv failed\n"); |
| 597 return (-1); |
| 598 } |
| 599 } |
| 600 #ifdef HAVE_POLL_H |
| 601 p.fd = ctxt->fd; |
| 602 p.events = POLLIN; |
| 603 if ((poll(&p, 1, timeout * 1000) < 1) |
| 604 #if defined(EINTR) |
| 605 && (errno != EINTR) |
| 606 #endif |
| 607 ) |
| 608 return (0); |
| 609 #else /* !HAVE_POLL_H */ |
| 610 #ifndef _WINSOCKAPI_ |
| 611 if (ctxt->fd > FD_SETSIZE) |
| 612 return 0; |
| 613 #endif |
| 614 |
| 615 tv.tv_sec = timeout; |
| 616 tv.tv_usec = 0; |
| 617 FD_ZERO(&rfd); |
| 618 |
| 619 #ifdef _MSC_VER |
| 620 #pragma warning(push) |
| 621 #pragma warning(disable: 4018) |
| 622 #endif |
| 623 |
| 624 FD_SET(ctxt->fd, &rfd); |
| 625 |
| 626 #ifdef _MSC_VER |
| 627 #pragma warning(pop) |
| 628 #endif |
| 629 |
| 630 if ((select(ctxt->fd + 1, &rfd, NULL, NULL, &tv) < 1) |
| 631 #if defined(EINTR) |
| 632 && (errno != EINTR) |
| 633 #endif |
| 634 ) |
| 635 return (0); |
| 636 #endif /* !HAVE_POLL_H */ |
| 637 } |
| 638 return (0); |
| 639 } |
| 640 |
| 641 /** |
| 642 * xmlNanoHTTPReadLine: |
| 643 * @ctxt: an HTTP context |
| 644 * |
| 645 * Read one line in the HTTP server output, usually for extracting |
| 646 * the HTTP protocol informations from the answer header. |
| 647 * |
| 648 * Returns a newly allocated string with a copy of the line, or NULL |
| 649 * which indicate the end of the input. |
| 650 */ |
| 651 |
| 652 static char * |
| 653 xmlNanoHTTPReadLine(xmlNanoHTTPCtxtPtr ctxt) { |
| 654 char buf[4096]; |
| 655 char *bp = buf; |
| 656 int rc; |
| 657 |
| 658 while (bp - buf < 4095) { |
| 659 if (ctxt->inrptr == ctxt->inptr) { |
| 660 if ( (rc = xmlNanoHTTPRecv(ctxt)) == 0) { |
| 661 if (bp == buf) |
| 662 return(NULL); |
| 663 else |
| 664 *bp = 0; |
| 665 return(xmlMemStrdup(buf)); |
| 666 } |
| 667 else if ( rc == -1 ) { |
| 668 return ( NULL ); |
| 669 } |
| 670 } |
| 671 *bp = *ctxt->inrptr++; |
| 672 if (*bp == '\n') { |
| 673 *bp = 0; |
| 674 return(xmlMemStrdup(buf)); |
| 675 } |
| 676 if (*bp != '\r') |
| 677 bp++; |
| 678 } |
| 679 buf[4095] = 0; |
| 680 return(xmlMemStrdup(buf)); |
| 681 } |
| 682 |
| 683 |
| 684 /** |
| 685 * xmlNanoHTTPScanAnswer: |
| 686 * @ctxt: an HTTP context |
| 687 * @line: an HTTP header line |
| 688 * |
| 689 * Try to extract useful informations from the server answer. |
| 690 * We currently parse and process: |
| 691 * - The HTTP revision/ return code |
| 692 * - The Content-Type, Mime-Type and charset used |
| 693 * - The Location for redirect processing. |
| 694 * |
| 695 * Returns -1 in case of failure, the file descriptor number otherwise |
| 696 */ |
| 697 |
| 698 static void |
| 699 xmlNanoHTTPScanAnswer(xmlNanoHTTPCtxtPtr ctxt, const char *line) { |
| 700 const char *cur = line; |
| 701 |
| 702 if (line == NULL) return; |
| 703 |
| 704 if (!strncmp(line, "HTTP/", 5)) { |
| 705 int version = 0; |
| 706 int ret = 0; |
| 707 |
| 708 cur += 5; |
| 709 while ((*cur >= '0') && (*cur <= '9')) { |
| 710 version *= 10; |
| 711 version += *cur - '0'; |
| 712 cur++; |
| 713 } |
| 714 if (*cur == '.') { |
| 715 cur++; |
| 716 if ((*cur >= '0') && (*cur <= '9')) { |
| 717 version *= 10; |
| 718 version += *cur - '0'; |
| 719 cur++; |
| 720 } |
| 721 while ((*cur >= '0') && (*cur <= '9')) |
| 722 cur++; |
| 723 } else |
| 724 version *= 10; |
| 725 if ((*cur != ' ') && (*cur != '\t')) return; |
| 726 while ((*cur == ' ') || (*cur == '\t')) cur++; |
| 727 if ((*cur < '0') || (*cur > '9')) return; |
| 728 while ((*cur >= '0') && (*cur <= '9')) { |
| 729 ret *= 10; |
| 730 ret += *cur - '0'; |
| 731 cur++; |
| 732 } |
| 733 if ((*cur != 0) && (*cur != ' ') && (*cur != '\t')) return; |
| 734 ctxt->returnValue = ret; |
| 735 ctxt->version = version; |
| 736 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Content-Type:", 13)) { |
| 737 const xmlChar *charset, *last, *mime; |
| 738 cur += 13; |
| 739 while ((*cur == ' ') || (*cur == '\t')) cur++; |
| 740 if (ctxt->contentType != NULL) |
| 741 xmlFree(ctxt->contentType); |
| 742 ctxt->contentType = xmlMemStrdup(cur); |
| 743 mime = (const xmlChar *) cur; |
| 744 last = mime; |
| 745 while ((*last != 0) && (*last != ' ') && (*last != '\t') && |
| 746 (*last != ';') && (*last != ',')) |
| 747 last++; |
| 748 if (ctxt->mimeType != NULL) |
| 749 xmlFree(ctxt->mimeType); |
| 750 ctxt->mimeType = (char *) xmlStrndup(mime, last - mime); |
| 751 charset = xmlStrstr(BAD_CAST ctxt->contentType, BAD_CAST "charset="); |
| 752 if (charset != NULL) { |
| 753 charset += 8; |
| 754 last = charset; |
| 755 while ((*last != 0) && (*last != ' ') && (*last != '\t') && |
| 756 (*last != ';') && (*last != ',')) |
| 757 last++; |
| 758 if (ctxt->encoding != NULL) |
| 759 xmlFree(ctxt->encoding); |
| 760 ctxt->encoding = (char *) xmlStrndup(charset, last - charset); |
| 761 } |
| 762 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"ContentType:", 12)) { |
| 763 const xmlChar *charset, *last, *mime; |
| 764 cur += 12; |
| 765 if (ctxt->contentType != NULL) return; |
| 766 while ((*cur == ' ') || (*cur == '\t')) cur++; |
| 767 ctxt->contentType = xmlMemStrdup(cur); |
| 768 mime = (const xmlChar *) cur; |
| 769 last = mime; |
| 770 while ((*last != 0) && (*last != ' ') && (*last != '\t') && |
| 771 (*last != ';') && (*last != ',')) |
| 772 last++; |
| 773 if (ctxt->mimeType != NULL) |
| 774 xmlFree(ctxt->mimeType); |
| 775 ctxt->mimeType = (char *) xmlStrndup(mime, last - mime); |
| 776 charset = xmlStrstr(BAD_CAST ctxt->contentType, BAD_CAST "charset="); |
| 777 if (charset != NULL) { |
| 778 charset += 8; |
| 779 last = charset; |
| 780 while ((*last != 0) && (*last != ' ') && (*last != '\t') && |
| 781 (*last != ';') && (*last != ',')) |
| 782 last++; |
| 783 if (ctxt->encoding != NULL) |
| 784 xmlFree(ctxt->encoding); |
| 785 ctxt->encoding = (char *) xmlStrndup(charset, last - charset); |
| 786 } |
| 787 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Location:", 9)) { |
| 788 cur += 9; |
| 789 while ((*cur == ' ') || (*cur == '\t')) cur++; |
| 790 if (ctxt->location != NULL) |
| 791 xmlFree(ctxt->location); |
| 792 if (*cur == '/') { |
| 793 xmlChar *tmp_http = xmlStrdup(BAD_CAST "http://"); |
| 794 xmlChar *tmp_loc = |
| 795 xmlStrcat(tmp_http, (const xmlChar *) ctxt->hostname); |
| 796 ctxt->location = |
| 797 (char *) xmlStrcat (tmp_loc, (const xmlChar *) cur); |
| 798 } else { |
| 799 ctxt->location = xmlMemStrdup(cur); |
| 800 } |
| 801 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"WWW-Authenticate:", 17))
{ |
| 802 cur += 17; |
| 803 while ((*cur == ' ') || (*cur == '\t')) cur++; |
| 804 if (ctxt->authHeader != NULL) |
| 805 xmlFree(ctxt->authHeader); |
| 806 ctxt->authHeader = xmlMemStrdup(cur); |
| 807 } else if (!xmlStrncasecmp(BAD_CAST line, BAD_CAST"Proxy-Authenticate:", 19)
) { |
| 808 cur += 19; |
| 809 while ((*cur == ' ') || (*cur == '\t')) cur++; |
| 810 if (ctxt->authHeader != NULL) |
| 811 xmlFree(ctxt->authHeader); |
| 812 ctxt->authHeader = xmlMemStrdup(cur); |
| 813 #ifdef HAVE_ZLIB_H |
| 814 } else if ( !xmlStrncasecmp( BAD_CAST line, BAD_CAST"Content-Encoding:", 17)
) { |
| 815 cur += 17; |
| 816 while ((*cur == ' ') || (*cur == '\t')) cur++; |
| 817 if ( !xmlStrncasecmp( BAD_CAST cur, BAD_CAST"gzip", 4) ) { |
| 818 ctxt->usesGzip = 1; |
| 819 |
| 820 ctxt->strm = xmlMalloc(sizeof(z_stream)); |
| 821 |
| 822 if (ctxt->strm != NULL) { |
| 823 ctxt->strm->zalloc = Z_NULL; |
| 824 ctxt->strm->zfree = Z_NULL; |
| 825 ctxt->strm->opaque = Z_NULL; |
| 826 ctxt->strm->avail_in = 0; |
| 827 ctxt->strm->next_in = Z_NULL; |
| 828 |
| 829 inflateInit2( ctxt->strm, 31 ); |
| 830 } |
| 831 } |
| 832 #endif |
| 833 } else if ( !xmlStrncasecmp( BAD_CAST line, BAD_CAST"Content-Length:", 15) )
{ |
| 834 cur += 15; |
| 835 ctxt->ContentLength = strtol( cur, NULL, 10 ); |
| 836 } |
| 837 } |
| 838 |
| 839 /** |
| 840 * xmlNanoHTTPConnectAttempt: |
| 841 * @addr: a socket address structure |
| 842 * |
| 843 * Attempt a connection to the given IP:port endpoint. It forces |
| 844 * non-blocking semantic on the socket, and allow 60 seconds for |
| 845 * the host to answer. |
| 846 * |
| 847 * Returns -1 in case of failure, the file descriptor number otherwise |
| 848 */ |
| 849 |
| 850 static SOCKET |
| 851 xmlNanoHTTPConnectAttempt(struct sockaddr *addr) |
| 852 { |
| 853 #ifndef HAVE_POLL_H |
| 854 fd_set wfd; |
| 855 #ifdef _WINSOCKAPI_ |
| 856 fd_set xfd; |
| 857 #endif |
| 858 struct timeval tv; |
| 859 #else /* !HAVE_POLL_H */ |
| 860 struct pollfd p; |
| 861 #endif /* !HAVE_POLL_H */ |
| 862 int status; |
| 863 |
| 864 int addrlen; |
| 865 |
| 866 SOCKET s; |
| 867 |
| 868 #ifdef SUPPORT_IP6 |
| 869 if (addr->sa_family == AF_INET6) { |
| 870 s = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP); |
| 871 addrlen = sizeof(struct sockaddr_in6); |
| 872 } else |
| 873 #endif |
| 874 { |
| 875 s = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); |
| 876 addrlen = sizeof(struct sockaddr_in); |
| 877 } |
| 878 if (s == INVALID_SOCKET) { |
| 879 #ifdef DEBUG_HTTP |
| 880 perror("socket"); |
| 881 #endif |
| 882 __xmlIOErr(XML_FROM_HTTP, 0, "socket failed\n"); |
| 883 return INVALID_SOCKET; |
| 884 } |
| 885 #ifdef _WINSOCKAPI_ |
| 886 { |
| 887 u_long one = 1; |
| 888 |
| 889 status = ioctlsocket(s, FIONBIO, &one) == SOCKET_ERROR ? -1 : 0; |
| 890 } |
| 891 #else /* _WINSOCKAPI_ */ |
| 892 #if defined(VMS) |
| 893 { |
| 894 int enable = 1; |
| 895 |
| 896 status = ioctl(s, FIONBIO, &enable); |
| 897 } |
| 898 #else /* VMS */ |
| 899 #if defined(__BEOS__) && !defined(__HAIKU__) |
| 900 { |
| 901 bool noblock = true; |
| 902 |
| 903 status = |
| 904 setsockopt(s, SOL_SOCKET, SO_NONBLOCK, &noblock, |
| 905 sizeof(noblock)); |
| 906 } |
| 907 #else /* __BEOS__ */ |
| 908 if ((status = fcntl(s, F_GETFL, 0)) != -1) { |
| 909 #ifdef O_NONBLOCK |
| 910 status |= O_NONBLOCK; |
| 911 #else /* O_NONBLOCK */ |
| 912 #ifdef F_NDELAY |
| 913 status |= F_NDELAY; |
| 914 #endif /* F_NDELAY */ |
| 915 #endif /* !O_NONBLOCK */ |
| 916 status = fcntl(s, F_SETFL, status); |
| 917 } |
| 918 if (status < 0) { |
| 919 #ifdef DEBUG_HTTP |
| 920 perror("nonblocking"); |
| 921 #endif |
| 922 __xmlIOErr(XML_FROM_HTTP, 0, "error setting non-blocking IO\n"); |
| 923 closesocket(s); |
| 924 return INVALID_SOCKET; |
| 925 } |
| 926 #endif /* !__BEOS__ */ |
| 927 #endif /* !VMS */ |
| 928 #endif /* !_WINSOCKAPI_ */ |
| 929 |
| 930 if (connect(s, addr, addrlen) == -1) { |
| 931 switch (socket_errno()) { |
| 932 case EINPROGRESS: |
| 933 case EWOULDBLOCK: |
| 934 break; |
| 935 default: |
| 936 __xmlIOErr(XML_FROM_HTTP, 0, |
| 937 "error connecting to HTTP server"); |
| 938 closesocket(s); |
| 939 return INVALID_SOCKET; |
| 940 } |
| 941 } |
| 942 #ifndef HAVE_POLL_H |
| 943 tv.tv_sec = timeout; |
| 944 tv.tv_usec = 0; |
| 945 |
| 946 #ifdef _MSC_VER |
| 947 #pragma warning(push) |
| 948 #pragma warning(disable: 4018) |
| 949 #endif |
| 950 #ifndef _WINSOCKAPI_ |
| 951 if (s > FD_SETSIZE) |
| 952 return INVALID_SOCKET; |
| 953 #endif |
| 954 FD_ZERO(&wfd); |
| 955 FD_SET(s, &wfd); |
| 956 |
| 957 #ifdef _WINSOCKAPI_ |
| 958 FD_ZERO(&xfd); |
| 959 FD_SET(s, &xfd); |
| 960 |
| 961 switch (select(s + 1, NULL, &wfd, &xfd, &tv)) |
| 962 #else |
| 963 switch (select(s + 1, NULL, &wfd, NULL, &tv)) |
| 964 #endif |
| 965 #ifdef _MSC_VER |
| 966 #pragma warning(pop) |
| 967 #endif |
| 968 |
| 969 #else /* !HAVE_POLL_H */ |
| 970 p.fd = s; |
| 971 p.events = POLLOUT; |
| 972 switch (poll(&p, 1, timeout * 1000)) |
| 973 #endif /* !HAVE_POLL_H */ |
| 974 |
| 975 { |
| 976 case 0: |
| 977 /* Time out */ |
| 978 __xmlIOErr(XML_FROM_HTTP, 0, "Connect attempt timed out"); |
| 979 closesocket(s); |
| 980 return INVALID_SOCKET; |
| 981 case -1: |
| 982 /* Ermm.. ?? */ |
| 983 __xmlIOErr(XML_FROM_HTTP, 0, "Connect failed"); |
| 984 closesocket(s); |
| 985 return INVALID_SOCKET; |
| 986 } |
| 987 |
| 988 #ifndef HAVE_POLL_H |
| 989 if (FD_ISSET(s, &wfd) |
| 990 #ifdef _WINSOCKAPI_ |
| 991 || FD_ISSET(s, &xfd) |
| 992 #endif |
| 993 ) |
| 994 #else /* !HAVE_POLL_H */ |
| 995 if (p.revents == POLLOUT) |
| 996 #endif /* !HAVE_POLL_H */ |
| 997 { |
| 998 XML_SOCKLEN_T len; |
| 999 |
| 1000 len = sizeof(status); |
| 1001 #ifdef SO_ERROR |
| 1002 if (getsockopt(s, SOL_SOCKET, SO_ERROR, (char *) &status, &len) < |
| 1003 0) { |
| 1004 /* Solaris error code */ |
| 1005 __xmlIOErr(XML_FROM_HTTP, 0, "getsockopt failed\n"); |
| 1006 closesocket(s); |
| 1007 return INVALID_SOCKET; |
| 1008 } |
| 1009 #endif |
| 1010 if (status) { |
| 1011 __xmlIOErr(XML_FROM_HTTP, 0, |
| 1012 "Error connecting to remote host"); |
| 1013 closesocket(s); |
| 1014 errno = status; |
| 1015 return INVALID_SOCKET; |
| 1016 } |
| 1017 } else { |
| 1018 /* pbm */ |
| 1019 __xmlIOErr(XML_FROM_HTTP, 0, "select failed\n"); |
| 1020 closesocket(s); |
| 1021 return INVALID_SOCKET; |
| 1022 } |
| 1023 |
| 1024 return (s); |
| 1025 } |
| 1026 |
| 1027 /** |
| 1028 * xmlNanoHTTPConnectHost: |
| 1029 * @host: the host name |
| 1030 * @port: the port number |
| 1031 * |
| 1032 * Attempt a connection to the given host:port endpoint. It tries |
| 1033 * the multiple IP provided by the DNS if available. |
| 1034 * |
| 1035 * Returns -1 in case of failure, the file descriptor number otherwise |
| 1036 */ |
| 1037 |
| 1038 static SOCKET |
| 1039 xmlNanoHTTPConnectHost(const char *host, int port) |
| 1040 { |
| 1041 struct hostent *h; |
| 1042 struct sockaddr *addr = NULL; |
| 1043 struct in_addr ia; |
| 1044 struct sockaddr_in sockin; |
| 1045 |
| 1046 #ifdef SUPPORT_IP6 |
| 1047 struct in6_addr ia6; |
| 1048 struct sockaddr_in6 sockin6; |
| 1049 #endif |
| 1050 int i; |
| 1051 SOCKET s; |
| 1052 |
| 1053 memset (&sockin, 0, sizeof(sockin)); |
| 1054 #ifdef SUPPORT_IP6 |
| 1055 memset (&sockin6, 0, sizeof(sockin6)); |
| 1056 #endif |
| 1057 |
| 1058 #if !defined(HAVE_GETADDRINFO) && defined(SUPPORT_IP6) && defined(RES_USE_INET6) |
| 1059 if (have_ipv6 ()) |
| 1060 { |
| 1061 if (!(_res.options & RES_INIT)) |
| 1062 res_init(); |
| 1063 _res.options |= RES_USE_INET6; |
| 1064 } |
| 1065 #endif |
| 1066 |
| 1067 #if defined(HAVE_GETADDRINFO) && defined(SUPPORT_IP6) && !defined(_WIN32) |
| 1068 if (have_ipv6 ()) |
| 1069 #endif |
| 1070 #if defined(HAVE_GETADDRINFO) && (defined(SUPPORT_IP6) || defined(_WIN32)) |
| 1071 { |
| 1072 int status; |
| 1073 struct addrinfo hints, *res, *result; |
| 1074 |
| 1075 result = NULL; |
| 1076 memset (&hints, 0,sizeof(hints)); |
| 1077 hints.ai_socktype = SOCK_STREAM; |
| 1078 |
| 1079 status = getaddrinfo (host, NULL, &hints, &result); |
| 1080 if (status) { |
| 1081 __xmlIOErr(XML_FROM_HTTP, 0, "getaddrinfo failed\n"); |
| 1082 return INVALID_SOCKET; |
| 1083 } |
| 1084 |
| 1085 for (res = result; res; res = res->ai_next) { |
| 1086 if (res->ai_family == AF_INET) { |
| 1087 if (res->ai_addrlen > sizeof(sockin)) { |
| 1088 __xmlIOErr(XML_FROM_HTTP, 0, "address size mismatch\n"); |
| 1089 freeaddrinfo (result); |
| 1090 return INVALID_SOCKET; |
| 1091 } |
| 1092 memcpy (&sockin, res->ai_addr, res->ai_addrlen); |
| 1093 sockin.sin_port = htons (port); |
| 1094 addr = (struct sockaddr *)&sockin; |
| 1095 #ifdef SUPPORT_IP6 |
| 1096 } else if (have_ipv6 () && (res->ai_family == AF_INET6)) { |
| 1097 if (res->ai_addrlen > sizeof(sockin6)) { |
| 1098 __xmlIOErr(XML_FROM_HTTP, 0, "address size mismatch\n"); |
| 1099 freeaddrinfo (result); |
| 1100 return INVALID_SOCKET; |
| 1101 } |
| 1102 memcpy (&sockin6, res->ai_addr, res->ai_addrlen); |
| 1103 sockin6.sin6_port = htons (port); |
| 1104 addr = (struct sockaddr *)&sockin6; |
| 1105 #endif |
| 1106 } else |
| 1107 continue; /* for */ |
| 1108 |
| 1109 s = xmlNanoHTTPConnectAttempt (addr); |
| 1110 if (s != INVALID_SOCKET) { |
| 1111 freeaddrinfo (result); |
| 1112 return (s); |
| 1113 } |
| 1114 } |
| 1115 |
| 1116 if (result) |
| 1117 freeaddrinfo (result); |
| 1118 } |
| 1119 #endif |
| 1120 #if defined(HAVE_GETADDRINFO) && defined(SUPPORT_IP6) && !defined(_WIN32) |
| 1121 else |
| 1122 #endif |
| 1123 #if !defined(HAVE_GETADDRINFO) || !defined(_WIN32) |
| 1124 { |
| 1125 h = gethostbyname (GETHOSTBYNAME_ARG_CAST host); |
| 1126 if (h == NULL) { |
| 1127 |
| 1128 /* |
| 1129 * Okay, I got fed up by the non-portability of this error message |
| 1130 * extraction code. it work on Linux, if it work on your platform |
| 1131 * and one want to enable it, send me the defined(foobar) needed |
| 1132 */ |
| 1133 #if defined(HAVE_NETDB_H) && defined(HOST_NOT_FOUND) && defined(linux) |
| 1134 const char *h_err_txt = ""; |
| 1135 |
| 1136 switch (h_errno) { |
| 1137 case HOST_NOT_FOUND: |
| 1138 h_err_txt = "Authoritive host not found"; |
| 1139 break; |
| 1140 |
| 1141 case TRY_AGAIN: |
| 1142 h_err_txt = |
| 1143 "Non-authoritive host not found or server failure."; |
| 1144 break; |
| 1145 |
| 1146 case NO_RECOVERY: |
| 1147 h_err_txt = |
| 1148 "Non-recoverable errors: FORMERR, REFUSED, or NOTIMP."; |
| 1149 break; |
| 1150 |
| 1151 #ifdef NO_ADDRESS |
| 1152 case NO_ADDRESS: |
| 1153 h_err_txt = |
| 1154 "Valid name, no data record of requested type."; |
| 1155 break; |
| 1156 #endif |
| 1157 |
| 1158 default: |
| 1159 h_err_txt = "No error text defined."; |
| 1160 break; |
| 1161 } |
| 1162 __xmlIOErr(XML_FROM_HTTP, 0, h_err_txt); |
| 1163 #else |
| 1164 __xmlIOErr(XML_FROM_HTTP, 0, "Failed to resolve host"); |
| 1165 #endif |
| 1166 return INVALID_SOCKET; |
| 1167 } |
| 1168 |
| 1169 for (i = 0; h->h_addr_list[i]; i++) { |
| 1170 if (h->h_addrtype == AF_INET) { |
| 1171 /* A records (IPv4) */ |
| 1172 if ((unsigned int) h->h_length > sizeof(ia)) { |
| 1173 __xmlIOErr(XML_FROM_HTTP, 0, "address size mismatch\n"); |
| 1174 return INVALID_SOCKET; |
| 1175 } |
| 1176 memcpy (&ia, h->h_addr_list[i], h->h_length); |
| 1177 sockin.sin_family = h->h_addrtype; |
| 1178 sockin.sin_addr = ia; |
| 1179 sockin.sin_port = (unsigned short)htons ((unsigned short)port); |
| 1180 addr = (struct sockaddr *) &sockin; |
| 1181 #ifdef SUPPORT_IP6 |
| 1182 } else if (have_ipv6 () && (h->h_addrtype == AF_INET6)) { |
| 1183 /* AAAA records (IPv6) */ |
| 1184 if ((unsigned int) h->h_length > sizeof(ia6)) { |
| 1185 __xmlIOErr(XML_FROM_HTTP, 0, "address size mismatch\n"); |
| 1186 return INVALID_SOCKET; |
| 1187 } |
| 1188 memcpy (&ia6, h->h_addr_list[i], h->h_length); |
| 1189 sockin6.sin6_family = h->h_addrtype; |
| 1190 sockin6.sin6_addr = ia6; |
| 1191 sockin6.sin6_port = htons (port); |
| 1192 addr = (struct sockaddr *) &sockin6; |
| 1193 #endif |
| 1194 } else |
| 1195 break; /* for */ |
| 1196 |
| 1197 s = xmlNanoHTTPConnectAttempt (addr); |
| 1198 if (s != INVALID_SOCKET) |
| 1199 return (s); |
| 1200 } |
| 1201 } |
| 1202 #endif |
| 1203 |
| 1204 #ifdef DEBUG_HTTP |
| 1205 xmlGenericError(xmlGenericErrorContext, |
| 1206 "xmlNanoHTTPConnectHost: unable to connect to '%s'.\n", |
| 1207 host); |
| 1208 #endif |
| 1209 return INVALID_SOCKET; |
| 1210 } |
| 1211 |
| 1212 |
| 1213 /** |
| 1214 * xmlNanoHTTPOpen: |
| 1215 * @URL: The URL to load |
| 1216 * @contentType: if available the Content-Type information will be |
| 1217 * returned at that location |
| 1218 * |
| 1219 * This function try to open a connection to the indicated resource |
| 1220 * via HTTP GET. |
| 1221 * |
| 1222 * Returns NULL in case of failure, otherwise a request handler. |
| 1223 * The contentType, if provided must be freed by the caller |
| 1224 */ |
| 1225 |
| 1226 void* |
| 1227 xmlNanoHTTPOpen(const char *URL, char **contentType) { |
| 1228 if (contentType != NULL) *contentType = NULL; |
| 1229 return(xmlNanoHTTPMethod(URL, NULL, NULL, contentType, NULL, 0)); |
| 1230 } |
| 1231 |
| 1232 /** |
| 1233 * xmlNanoHTTPOpenRedir: |
| 1234 * @URL: The URL to load |
| 1235 * @contentType: if available the Content-Type information will be |
| 1236 * returned at that location |
| 1237 * @redir: if available the redirected URL will be returned |
| 1238 * |
| 1239 * This function try to open a connection to the indicated resource |
| 1240 * via HTTP GET. |
| 1241 * |
| 1242 * Returns NULL in case of failure, otherwise a request handler. |
| 1243 * The contentType, if provided must be freed by the caller |
| 1244 */ |
| 1245 |
| 1246 void* |
| 1247 xmlNanoHTTPOpenRedir(const char *URL, char **contentType, char **redir) { |
| 1248 if (contentType != NULL) *contentType = NULL; |
| 1249 if (redir != NULL) *redir = NULL; |
| 1250 return(xmlNanoHTTPMethodRedir(URL, NULL, NULL, contentType, redir, NULL,0)); |
| 1251 } |
| 1252 |
| 1253 /** |
| 1254 * xmlNanoHTTPRead: |
| 1255 * @ctx: the HTTP context |
| 1256 * @dest: a buffer |
| 1257 * @len: the buffer length |
| 1258 * |
| 1259 * This function tries to read @len bytes from the existing HTTP connection |
| 1260 * and saves them in @dest. This is a blocking call. |
| 1261 * |
| 1262 * Returns the number of byte read. 0 is an indication of an end of connection. |
| 1263 * -1 indicates a parameter error. |
| 1264 */ |
| 1265 int |
| 1266 xmlNanoHTTPRead(void *ctx, void *dest, int len) { |
| 1267 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx; |
| 1268 #ifdef HAVE_ZLIB_H |
| 1269 int bytes_read = 0; |
| 1270 int orig_avail_in; |
| 1271 int z_ret; |
| 1272 #endif |
| 1273 |
| 1274 if (ctx == NULL) return(-1); |
| 1275 if (dest == NULL) return(-1); |
| 1276 if (len <= 0) return(0); |
| 1277 |
| 1278 #ifdef HAVE_ZLIB_H |
| 1279 if (ctxt->usesGzip == 1) { |
| 1280 if (ctxt->strm == NULL) return(0); |
| 1281 |
| 1282 ctxt->strm->next_out = dest; |
| 1283 ctxt->strm->avail_out = len; |
| 1284 ctxt->strm->avail_in = ctxt->inptr - ctxt->inrptr; |
| 1285 |
| 1286 while (ctxt->strm->avail_out > 0 && |
| 1287 (ctxt->strm->avail_in > 0 || xmlNanoHTTPRecv(ctxt) > 0)) { |
| 1288 orig_avail_in = ctxt->strm->avail_in = |
| 1289 ctxt->inptr - ctxt->inrptr - bytes_read; |
| 1290 ctxt->strm->next_in = BAD_CAST (ctxt->inrptr + bytes_read); |
| 1291 |
| 1292 z_ret = inflate(ctxt->strm, Z_NO_FLUSH); |
| 1293 bytes_read += orig_avail_in - ctxt->strm->avail_in; |
| 1294 |
| 1295 if (z_ret != Z_OK) break; |
| 1296 } |
| 1297 |
| 1298 ctxt->inrptr += bytes_read; |
| 1299 return(len - ctxt->strm->avail_out); |
| 1300 } |
| 1301 #endif |
| 1302 |
| 1303 while (ctxt->inptr - ctxt->inrptr < len) { |
| 1304 if (xmlNanoHTTPRecv(ctxt) <= 0) break; |
| 1305 } |
| 1306 if (ctxt->inptr - ctxt->inrptr < len) |
| 1307 len = ctxt->inptr - ctxt->inrptr; |
| 1308 memcpy(dest, ctxt->inrptr, len); |
| 1309 ctxt->inrptr += len; |
| 1310 return(len); |
| 1311 } |
| 1312 |
| 1313 /** |
| 1314 * xmlNanoHTTPClose: |
| 1315 * @ctx: the HTTP context |
| 1316 * |
| 1317 * This function closes an HTTP context, it ends up the connection and |
| 1318 * free all data related to it. |
| 1319 */ |
| 1320 void |
| 1321 xmlNanoHTTPClose(void *ctx) { |
| 1322 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx; |
| 1323 |
| 1324 if (ctx == NULL) return; |
| 1325 |
| 1326 xmlNanoHTTPFreeCtxt(ctxt); |
| 1327 } |
| 1328 |
| 1329 /** |
| 1330 * xmlNanoHTTPMethodRedir: |
| 1331 * @URL: The URL to load |
| 1332 * @method: the HTTP method to use |
| 1333 * @input: the input string if any |
| 1334 * @contentType: the Content-Type information IN and OUT |
| 1335 * @redir: the redirected URL OUT |
| 1336 * @headers: the extra headers |
| 1337 * @ilen: input length |
| 1338 * |
| 1339 * This function try to open a connection to the indicated resource |
| 1340 * via HTTP using the given @method, adding the given extra headers |
| 1341 * and the input buffer for the request content. |
| 1342 * |
| 1343 * Returns NULL in case of failure, otherwise a request handler. |
| 1344 * The contentType, or redir, if provided must be freed by the caller |
| 1345 */ |
| 1346 |
| 1347 void* |
| 1348 xmlNanoHTTPMethodRedir(const char *URL, const char *method, const char *input, |
| 1349 char **contentType, char **redir, |
| 1350 const char *headers, int ilen ) { |
| 1351 xmlNanoHTTPCtxtPtr ctxt; |
| 1352 char *bp, *p; |
| 1353 int blen; |
| 1354 SOCKET ret; |
| 1355 int nbRedirects = 0; |
| 1356 char *redirURL = NULL; |
| 1357 #ifdef DEBUG_HTTP |
| 1358 int xmt_bytes; |
| 1359 #endif |
| 1360 |
| 1361 if (URL == NULL) return(NULL); |
| 1362 if (method == NULL) method = "GET"; |
| 1363 xmlNanoHTTPInit(); |
| 1364 |
| 1365 retry: |
| 1366 if (redirURL == NULL) { |
| 1367 ctxt = xmlNanoHTTPNewCtxt(URL); |
| 1368 if (ctxt == NULL) |
| 1369 return(NULL); |
| 1370 } else { |
| 1371 ctxt = xmlNanoHTTPNewCtxt(redirURL); |
| 1372 if (ctxt == NULL) |
| 1373 return(NULL); |
| 1374 ctxt->location = xmlMemStrdup(redirURL); |
| 1375 } |
| 1376 |
| 1377 if ((ctxt->protocol == NULL) || (strcmp(ctxt->protocol, "http"))) { |
| 1378 __xmlIOErr(XML_FROM_HTTP, XML_HTTP_URL_SYNTAX, "Not a valid HTTP URI"); |
| 1379 xmlNanoHTTPFreeCtxt(ctxt); |
| 1380 if (redirURL != NULL) xmlFree(redirURL); |
| 1381 return(NULL); |
| 1382 } |
| 1383 if (ctxt->hostname == NULL) { |
| 1384 __xmlIOErr(XML_FROM_HTTP, XML_HTTP_UNKNOWN_HOST, |
| 1385 "Failed to identify host in URI"); |
| 1386 xmlNanoHTTPFreeCtxt(ctxt); |
| 1387 if (redirURL != NULL) xmlFree(redirURL); |
| 1388 return(NULL); |
| 1389 } |
| 1390 if (proxy) { |
| 1391 blen = strlen(ctxt->hostname) * 2 + 16; |
| 1392 ret = xmlNanoHTTPConnectHost(proxy, proxyPort); |
| 1393 } |
| 1394 else { |
| 1395 blen = strlen(ctxt->hostname); |
| 1396 ret = xmlNanoHTTPConnectHost(ctxt->hostname, ctxt->port); |
| 1397 } |
| 1398 if (ret == INVALID_SOCKET) { |
| 1399 xmlNanoHTTPFreeCtxt(ctxt); |
| 1400 if (redirURL != NULL) xmlFree(redirURL); |
| 1401 return(NULL); |
| 1402 } |
| 1403 ctxt->fd = ret; |
| 1404 |
| 1405 if (input == NULL) |
| 1406 ilen = 0; |
| 1407 else |
| 1408 blen += 36; |
| 1409 |
| 1410 if (headers != NULL) |
| 1411 blen += strlen(headers) + 2; |
| 1412 if (contentType && *contentType) |
| 1413 /* reserve for string plus 'Content-Type: \r\n" */ |
| 1414 blen += strlen(*contentType) + 16; |
| 1415 if (ctxt->query != NULL) |
| 1416 /* 1 for '?' */ |
| 1417 blen += strlen(ctxt->query) + 1; |
| 1418 blen += strlen(method) + strlen(ctxt->path) + 24; |
| 1419 #ifdef HAVE_ZLIB_H |
| 1420 /* reserve for possible 'Accept-Encoding: gzip' string */ |
| 1421 blen += 23; |
| 1422 #endif |
| 1423 if (ctxt->port != 80) { |
| 1424 /* reserve space for ':xxxxx', incl. potential proxy */ |
| 1425 if (proxy) |
| 1426 blen += 12; |
| 1427 else |
| 1428 blen += 6; |
| 1429 } |
| 1430 bp = (char*)xmlMallocAtomic(blen); |
| 1431 if ( bp == NULL ) { |
| 1432 xmlNanoHTTPFreeCtxt( ctxt ); |
| 1433 xmlHTTPErrMemory("allocating header buffer"); |
| 1434 return ( NULL ); |
| 1435 } |
| 1436 |
| 1437 p = bp; |
| 1438 |
| 1439 if (proxy) { |
| 1440 if (ctxt->port != 80) { |
| 1441 p += snprintf( p, blen - (p - bp), "%s http://%s:%d%s", |
| 1442 method, ctxt->hostname, |
| 1443 ctxt->port, ctxt->path ); |
| 1444 } |
| 1445 else |
| 1446 p += snprintf( p, blen - (p - bp), "%s http://%s%s", method, |
| 1447 ctxt->hostname, ctxt->path); |
| 1448 } |
| 1449 else |
| 1450 p += snprintf( p, blen - (p - bp), "%s %s", method, ctxt->path); |
| 1451 |
| 1452 if (ctxt->query != NULL) |
| 1453 p += snprintf( p, blen - (p - bp), "?%s", ctxt->query); |
| 1454 |
| 1455 if (ctxt->port == 80) { |
| 1456 p += snprintf( p, blen - (p - bp), " HTTP/1.0\r\nHost: %s\r\n", |
| 1457 ctxt->hostname); |
| 1458 } else { |
| 1459 p += snprintf( p, blen - (p - bp), " HTTP/1.0\r\nHost: %s:%d\r\n", |
| 1460 ctxt->hostname, ctxt->port); |
| 1461 } |
| 1462 |
| 1463 #ifdef HAVE_ZLIB_H |
| 1464 p += snprintf(p, blen - (p - bp), "Accept-Encoding: gzip\r\n"); |
| 1465 #endif |
| 1466 |
| 1467 if (contentType != NULL && *contentType) |
| 1468 p += snprintf(p, blen - (p - bp), "Content-Type: %s\r\n", *contentType); |
| 1469 |
| 1470 if (headers != NULL) |
| 1471 p += snprintf( p, blen - (p - bp), "%s", headers ); |
| 1472 |
| 1473 if (input != NULL) |
| 1474 snprintf(p, blen - (p - bp), "Content-Length: %d\r\n\r\n", ilen ); |
| 1475 else |
| 1476 snprintf(p, blen - (p - bp), "\r\n"); |
| 1477 |
| 1478 #ifdef DEBUG_HTTP |
| 1479 xmlGenericError(xmlGenericErrorContext, |
| 1480 "-> %s%s", proxy? "(Proxy) " : "", bp); |
| 1481 if ((blen -= strlen(bp)+1) < 0) |
| 1482 xmlGenericError(xmlGenericErrorContext, |
| 1483 "ERROR: overflowed buffer by %d bytes\n", -blen); |
| 1484 #endif |
| 1485 ctxt->outptr = ctxt->out = bp; |
| 1486 ctxt->state = XML_NANO_HTTP_WRITE; |
| 1487 blen = strlen( ctxt->out ); |
| 1488 #ifdef DEBUG_HTTP |
| 1489 xmt_bytes = xmlNanoHTTPSend(ctxt, ctxt->out, blen ); |
| 1490 if ( xmt_bytes != blen ) |
| 1491 xmlGenericError( xmlGenericErrorContext, |
| 1492 "xmlNanoHTTPMethodRedir: Only %d of %d %s %s\n", |
| 1493 xmt_bytes, blen, |
| 1494 "bytes of HTTP headers sent to host", |
| 1495 ctxt->hostname ); |
| 1496 #else |
| 1497 xmlNanoHTTPSend(ctxt, ctxt->out, blen ); |
| 1498 #endif |
| 1499 |
| 1500 if ( input != NULL ) { |
| 1501 #ifdef DEBUG_HTTP |
| 1502 xmt_bytes = xmlNanoHTTPSend( ctxt, input, ilen ); |
| 1503 |
| 1504 if ( xmt_bytes != ilen ) |
| 1505 xmlGenericError( xmlGenericErrorContext, |
| 1506 "xmlNanoHTTPMethodRedir: Only %d of %d %s %s\n", |
| 1507 xmt_bytes, ilen, |
| 1508 "bytes of HTTP content sent to host", |
| 1509 ctxt->hostname ); |
| 1510 #else |
| 1511 xmlNanoHTTPSend( ctxt, input, ilen ); |
| 1512 #endif |
| 1513 } |
| 1514 |
| 1515 ctxt->state = XML_NANO_HTTP_READ; |
| 1516 |
| 1517 while ((p = xmlNanoHTTPReadLine(ctxt)) != NULL) { |
| 1518 if (*p == 0) { |
| 1519 ctxt->content = ctxt->inrptr; |
| 1520 xmlFree(p); |
| 1521 break; |
| 1522 } |
| 1523 xmlNanoHTTPScanAnswer(ctxt, p); |
| 1524 |
| 1525 #ifdef DEBUG_HTTP |
| 1526 xmlGenericError(xmlGenericErrorContext, "<- %s\n", p); |
| 1527 #endif |
| 1528 xmlFree(p); |
| 1529 } |
| 1530 |
| 1531 if ((ctxt->location != NULL) && (ctxt->returnValue >= 300) && |
| 1532 (ctxt->returnValue < 400)) { |
| 1533 #ifdef DEBUG_HTTP |
| 1534 xmlGenericError(xmlGenericErrorContext, |
| 1535 "\nRedirect to: %s\n", ctxt->location); |
| 1536 #endif |
| 1537 while ( xmlNanoHTTPRecv(ctxt) > 0 ) ; |
| 1538 if (nbRedirects < XML_NANO_HTTP_MAX_REDIR) { |
| 1539 nbRedirects++; |
| 1540 if (redirURL != NULL) |
| 1541 xmlFree(redirURL); |
| 1542 redirURL = xmlMemStrdup(ctxt->location); |
| 1543 xmlNanoHTTPFreeCtxt(ctxt); |
| 1544 goto retry; |
| 1545 } |
| 1546 xmlNanoHTTPFreeCtxt(ctxt); |
| 1547 if (redirURL != NULL) xmlFree(redirURL); |
| 1548 #ifdef DEBUG_HTTP |
| 1549 xmlGenericError(xmlGenericErrorContext, |
| 1550 "xmlNanoHTTPMethodRedir: Too many redirects, aborting ...\n"); |
| 1551 #endif |
| 1552 return(NULL); |
| 1553 } |
| 1554 |
| 1555 if (contentType != NULL) { |
| 1556 if (ctxt->contentType != NULL) |
| 1557 *contentType = xmlMemStrdup(ctxt->contentType); |
| 1558 else |
| 1559 *contentType = NULL; |
| 1560 } |
| 1561 |
| 1562 if ((redir != NULL) && (redirURL != NULL)) { |
| 1563 *redir = redirURL; |
| 1564 } else { |
| 1565 if (redirURL != NULL) |
| 1566 xmlFree(redirURL); |
| 1567 if (redir != NULL) |
| 1568 *redir = NULL; |
| 1569 } |
| 1570 |
| 1571 #ifdef DEBUG_HTTP |
| 1572 if (ctxt->contentType != NULL) |
| 1573 xmlGenericError(xmlGenericErrorContext, |
| 1574 "\nCode %d, content-type '%s'\n\n", |
| 1575 ctxt->returnValue, ctxt->contentType); |
| 1576 else |
| 1577 xmlGenericError(xmlGenericErrorContext, |
| 1578 "\nCode %d, no content-type\n\n", |
| 1579 ctxt->returnValue); |
| 1580 #endif |
| 1581 |
| 1582 return((void *) ctxt); |
| 1583 } |
| 1584 |
| 1585 /** |
| 1586 * xmlNanoHTTPMethod: |
| 1587 * @URL: The URL to load |
| 1588 * @method: the HTTP method to use |
| 1589 * @input: the input string if any |
| 1590 * @contentType: the Content-Type information IN and OUT |
| 1591 * @headers: the extra headers |
| 1592 * @ilen: input length |
| 1593 * |
| 1594 * This function try to open a connection to the indicated resource |
| 1595 * via HTTP using the given @method, adding the given extra headers |
| 1596 * and the input buffer for the request content. |
| 1597 * |
| 1598 * Returns NULL in case of failure, otherwise a request handler. |
| 1599 * The contentType, if provided must be freed by the caller |
| 1600 */ |
| 1601 |
| 1602 void* |
| 1603 xmlNanoHTTPMethod(const char *URL, const char *method, const char *input, |
| 1604 char **contentType, const char *headers, int ilen) { |
| 1605 return(xmlNanoHTTPMethodRedir(URL, method, input, contentType, |
| 1606 NULL, headers, ilen)); |
| 1607 } |
| 1608 |
| 1609 /** |
| 1610 * xmlNanoHTTPFetch: |
| 1611 * @URL: The URL to load |
| 1612 * @filename: the filename where the content should be saved |
| 1613 * @contentType: if available the Content-Type information will be |
| 1614 * returned at that location |
| 1615 * |
| 1616 * This function try to fetch the indicated resource via HTTP GET |
| 1617 * and save it's content in the file. |
| 1618 * |
| 1619 * Returns -1 in case of failure, 0 incase of success. The contentType, |
| 1620 * if provided must be freed by the caller |
| 1621 */ |
| 1622 int |
| 1623 xmlNanoHTTPFetch(const char *URL, const char *filename, char **contentType) { |
| 1624 void *ctxt = NULL; |
| 1625 char *buf = NULL; |
| 1626 int fd; |
| 1627 int len; |
| 1628 int ret = 0; |
| 1629 |
| 1630 if (filename == NULL) return(-1); |
| 1631 ctxt = xmlNanoHTTPOpen(URL, contentType); |
| 1632 if (ctxt == NULL) return(-1); |
| 1633 |
| 1634 if (!strcmp(filename, "-")) |
| 1635 fd = 0; |
| 1636 else { |
| 1637 fd = open(filename, O_CREAT | O_WRONLY, 00644); |
| 1638 if (fd < 0) { |
| 1639 xmlNanoHTTPClose(ctxt); |
| 1640 if ((contentType != NULL) && (*contentType != NULL)) { |
| 1641 xmlFree(*contentType); |
| 1642 *contentType = NULL; |
| 1643 } |
| 1644 return(-1); |
| 1645 } |
| 1646 } |
| 1647 |
| 1648 xmlNanoHTTPFetchContent( ctxt, &buf, &len ); |
| 1649 if ( len > 0 ) { |
| 1650 if (write(fd, buf, len) == -1) { |
| 1651 ret = -1; |
| 1652 } |
| 1653 } |
| 1654 |
| 1655 xmlNanoHTTPClose(ctxt); |
| 1656 close(fd); |
| 1657 return(ret); |
| 1658 } |
| 1659 |
| 1660 #ifdef LIBXML_OUTPUT_ENABLED |
| 1661 /** |
| 1662 * xmlNanoHTTPSave: |
| 1663 * @ctxt: the HTTP context |
| 1664 * @filename: the filename where the content should be saved |
| 1665 * |
| 1666 * This function saves the output of the HTTP transaction to a file |
| 1667 * It closes and free the context at the end |
| 1668 * |
| 1669 * Returns -1 in case of failure, 0 incase of success. |
| 1670 */ |
| 1671 int |
| 1672 xmlNanoHTTPSave(void *ctxt, const char *filename) { |
| 1673 char *buf = NULL; |
| 1674 int fd; |
| 1675 int len; |
| 1676 int ret = 0; |
| 1677 |
| 1678 if ((ctxt == NULL) || (filename == NULL)) return(-1); |
| 1679 |
| 1680 if (!strcmp(filename, "-")) |
| 1681 fd = 0; |
| 1682 else { |
| 1683 fd = open(filename, O_CREAT | O_WRONLY, 0666); |
| 1684 if (fd < 0) { |
| 1685 xmlNanoHTTPClose(ctxt); |
| 1686 return(-1); |
| 1687 } |
| 1688 } |
| 1689 |
| 1690 xmlNanoHTTPFetchContent( ctxt, &buf, &len ); |
| 1691 if ( len > 0 ) { |
| 1692 if (write(fd, buf, len) == -1) { |
| 1693 ret = -1; |
| 1694 } |
| 1695 } |
| 1696 |
| 1697 xmlNanoHTTPClose(ctxt); |
| 1698 close(fd); |
| 1699 return(ret); |
| 1700 } |
| 1701 #endif /* LIBXML_OUTPUT_ENABLED */ |
| 1702 |
| 1703 /** |
| 1704 * xmlNanoHTTPReturnCode: |
| 1705 * @ctx: the HTTP context |
| 1706 * |
| 1707 * Get the latest HTTP return code received |
| 1708 * |
| 1709 * Returns the HTTP return code for the request. |
| 1710 */ |
| 1711 int |
| 1712 xmlNanoHTTPReturnCode(void *ctx) { |
| 1713 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx; |
| 1714 |
| 1715 if (ctxt == NULL) return(-1); |
| 1716 |
| 1717 return(ctxt->returnValue); |
| 1718 } |
| 1719 |
| 1720 /** |
| 1721 * xmlNanoHTTPAuthHeader: |
| 1722 * @ctx: the HTTP context |
| 1723 * |
| 1724 * Get the authentication header of an HTTP context |
| 1725 * |
| 1726 * Returns the stashed value of the WWW-Authenticate or Proxy-Authenticate |
| 1727 * header. |
| 1728 */ |
| 1729 const char * |
| 1730 xmlNanoHTTPAuthHeader(void *ctx) { |
| 1731 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr) ctx; |
| 1732 |
| 1733 if (ctxt == NULL) return(NULL); |
| 1734 |
| 1735 return(ctxt->authHeader); |
| 1736 } |
| 1737 |
| 1738 /** |
| 1739 * xmlNanoHTTPContentLength: |
| 1740 * @ctx: the HTTP context |
| 1741 * |
| 1742 * Provides the specified content length from the HTTP header. |
| 1743 * |
| 1744 * Return the specified content length from the HTTP header. Note that |
| 1745 * a value of -1 indicates that the content length element was not included in |
| 1746 * the response header. |
| 1747 */ |
| 1748 int |
| 1749 xmlNanoHTTPContentLength( void * ctx ) { |
| 1750 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr)ctx; |
| 1751 |
| 1752 return ( ( ctxt == NULL ) ? -1 : ctxt->ContentLength ); |
| 1753 } |
| 1754 |
| 1755 /** |
| 1756 * xmlNanoHTTPRedir: |
| 1757 * @ctx: the HTTP context |
| 1758 * |
| 1759 * Provides the specified redirection URL if available from the HTTP header. |
| 1760 * |
| 1761 * Return the specified redirection URL or NULL if not redirected. |
| 1762 */ |
| 1763 const char * |
| 1764 xmlNanoHTTPRedir( void * ctx ) { |
| 1765 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr)ctx; |
| 1766 |
| 1767 return ( ( ctxt == NULL ) ? NULL : ctxt->location ); |
| 1768 } |
| 1769 |
| 1770 /** |
| 1771 * xmlNanoHTTPEncoding: |
| 1772 * @ctx: the HTTP context |
| 1773 * |
| 1774 * Provides the specified encoding if specified in the HTTP headers. |
| 1775 * |
| 1776 * Return the specified encoding or NULL if not available |
| 1777 */ |
| 1778 const char * |
| 1779 xmlNanoHTTPEncoding( void * ctx ) { |
| 1780 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr)ctx; |
| 1781 |
| 1782 return ( ( ctxt == NULL ) ? NULL : ctxt->encoding ); |
| 1783 } |
| 1784 |
| 1785 /** |
| 1786 * xmlNanoHTTPMimeType: |
| 1787 * @ctx: the HTTP context |
| 1788 * |
| 1789 * Provides the specified Mime-Type if specified in the HTTP headers. |
| 1790 * |
| 1791 * Return the specified Mime-Type or NULL if not available |
| 1792 */ |
| 1793 const char * |
| 1794 xmlNanoHTTPMimeType( void * ctx ) { |
| 1795 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr)ctx; |
| 1796 |
| 1797 return ( ( ctxt == NULL ) ? NULL : ctxt->mimeType ); |
| 1798 } |
| 1799 |
| 1800 /** |
| 1801 * xmlNanoHTTPFetchContent: |
| 1802 * @ctx: the HTTP context |
| 1803 * @ptr: pointer to set to the content buffer. |
| 1804 * @len: integer pointer to hold the length of the content |
| 1805 * |
| 1806 * Check if all the content was read |
| 1807 * |
| 1808 * Returns 0 if all the content was read and available, returns |
| 1809 * -1 if received content length was less than specified or an error |
| 1810 * occurred. |
| 1811 */ |
| 1812 static int |
| 1813 xmlNanoHTTPFetchContent( void * ctx, char ** ptr, int * len ) { |
| 1814 xmlNanoHTTPCtxtPtr ctxt = (xmlNanoHTTPCtxtPtr)ctx; |
| 1815 |
| 1816 int rc = 0; |
| 1817 int cur_lgth; |
| 1818 int rcvd_lgth; |
| 1819 int dummy_int; |
| 1820 char * dummy_ptr = NULL; |
| 1821 |
| 1822 /* Dummy up return input parameters if not provided */ |
| 1823 |
| 1824 if ( len == NULL ) |
| 1825 len = &dummy_int; |
| 1826 |
| 1827 if ( ptr == NULL ) |
| 1828 ptr = &dummy_ptr; |
| 1829 |
| 1830 /* But can't work without the context pointer */ |
| 1831 |
| 1832 if ( ( ctxt == NULL ) || ( ctxt->content == NULL ) ) { |
| 1833 *len = 0; |
| 1834 *ptr = NULL; |
| 1835 return ( -1 ); |
| 1836 } |
| 1837 |
| 1838 rcvd_lgth = ctxt->inptr - ctxt->content; |
| 1839 |
| 1840 while ( (cur_lgth = xmlNanoHTTPRecv( ctxt )) > 0 ) { |
| 1841 |
| 1842 rcvd_lgth += cur_lgth; |
| 1843 if ( (ctxt->ContentLength > 0) && (rcvd_lgth >= ctxt->ContentLength) ) |
| 1844 break; |
| 1845 } |
| 1846 |
| 1847 *ptr = ctxt->content; |
| 1848 *len = rcvd_lgth; |
| 1849 |
| 1850 if ( ( ctxt->ContentLength > 0 ) && ( rcvd_lgth < ctxt->ContentLength ) ) |
| 1851 rc = -1; |
| 1852 else if ( rcvd_lgth == 0 ) |
| 1853 rc = -1; |
| 1854 |
| 1855 return ( rc ); |
| 1856 } |
| 1857 |
| 1858 #ifdef STANDALONE |
| 1859 int main(int argc, char **argv) { |
| 1860 char *contentType = NULL; |
| 1861 |
| 1862 if (argv[1] != NULL) { |
| 1863 if (argv[2] != NULL) |
| 1864 xmlNanoHTTPFetch(argv[1], argv[2], &contentType); |
| 1865 else |
| 1866 xmlNanoHTTPFetch(argv[1], "-", &contentType); |
| 1867 if (contentType != NULL) xmlFree(contentType); |
| 1868 } else { |
| 1869 xmlGenericError(xmlGenericErrorContext, |
| 1870 "%s: minimal HTTP GET implementation\n", argv[0]); |
| 1871 xmlGenericError(xmlGenericErrorContext, |
| 1872 "\tusage %s [ URL [ filename ] ]\n", argv[0]); |
| 1873 } |
| 1874 xmlNanoHTTPCleanup(); |
| 1875 xmlMemoryDump(); |
| 1876 return(0); |
| 1877 } |
| 1878 #endif /* STANDALONE */ |
| 1879 #else /* !LIBXML_HTTP_ENABLED */ |
| 1880 #ifdef STANDALONE |
| 1881 #include <stdio.h> |
| 1882 int main(int argc, char **argv) { |
| 1883 xmlGenericError(xmlGenericErrorContext, |
| 1884 "%s : HTTP support not compiled in\n", argv[0]); |
| 1885 return(0); |
| 1886 } |
| 1887 #endif /* STANDALONE */ |
| 1888 #endif /* LIBXML_HTTP_ENABLED */ |
| 1889 #define bottom_nanohttp |
| 1890 #include "elfgcchack.h" |
OLD | NEW |