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