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