Adjust request serial number notation
[libisds.git] / src / soap.c
blob2431472518d0c251d6579af0ebf08bf648159f9c
1 #define _XOPEN_SOURCE 500 /* strdup from string.h */
2 #include "isds_priv.h"
3 #include "soap.h"
4 #include "utils.h"
5 #include <stdlib.h>
6 #include <string.h>
8 /* Private structure for write_body() call back */
9 struct soap_body {
10 void *data;
11 size_t length;
15 /* Close connection to server and destroy CURL handle associated
16 * with @context */
17 _hidden isds_error close_connection(struct isds_ctx *context) {
18 if (!context) return IE_INVALID_CONTEXT;
20 if (context->curl) {
21 curl_easy_cleanup(context->curl);
22 context->curl = NULL;
23 isds_log(ILF_HTTP, ILL_DEBUG, _("Connection to server closed"));
24 return IE_SUCCESS;
25 } else {
26 return IE_CONNECTION_CLOSED;
31 /* CURL call back function called when chunk of HTTP reponse body is available.
32 * @buffer points to new data
33 * @size * @nmemb is length of the chunk in bytes. Zero means empty body.
34 * @userp is private structure.
35 * Must reuturn the length of the chunk, otherwise CURL will signal
36 * CURL_WRITE_ERROR. */
37 static size_t write_body(void *buffer, size_t size, size_t nmemb, void *userp) {
38 struct soap_body *body = (struct soap_body *) userp;
39 void *new_data;
41 /* FIXME: Check for (size * nmemb + body->lengt) !> SIZE_T_MAX.
42 * Precompute the product then. */
44 if (!body) return 0; /* This should never happen */
45 if (0 == (size * nmemb)) return 0; /* Empty body */
47 new_data = realloc(body->data, body->length + size * nmemb);
48 if (!new_data) return 0;
50 memcpy(new_data + body->length, buffer, size * nmemb);
52 body->data = new_data;
53 body->length += size * nmemb;
55 return (size * nmemb);
59 /* CURL progress callback proxy to rearrange arguments.
60 * @curl_data is session context */
61 static int progress_proxy(void *curl_data, double download_total,
62 double download_current, double upload_total, double upload_current) {
63 struct isds_ctx *context = (struct isds_ctx *) curl_data;
64 int abort = 0;
66 if (context && context->progress_callback) {
67 abort = context->progress_callback(
68 upload_total, upload_current,
69 download_total, download_current,
70 context->progress_callback_data);
71 if (abort) {
72 isds_log(ILF_HTTP, ILL_INFO,
73 _("Application aborted HTTP transfer"));
77 return abort;
81 /* Do HTTP request.
82 * @context holds the base URL,
83 * @url is a (CGI) file of SOAP URL,
84 * @request is body for POST request
85 * @request_length is length of @request in bytes
86 * @reponse is automatically reallocated() buffer to fit HTTP response with
87 * @response_length (does not need to match allocatef memory exactly). You must
88 * free() the @response.
89 * @mime_type is automatically allocated MIME type send by server (*NULL if not
90 * sent). Set NULL if you don't care.
91 * @charset is charset of the body signaled by server. The same constrains
92 * like on @mime_type apply.
93 * @http_code is final HTTP code returned by server. This can be 200, 401, 500
94 * or any other one. Pass NULL if you don't interrest.
95 * In case of error, the response memory, MIME type, charset and lenght will be
96 * deallocated and zerod automatically. Thus be sure they are preallocated or
97 * they points to NULL.
98 * Be ware that successful return value does not mean the HTTP request has
99 * been accepted by the server. You must cosult @http_code. OTOH, failure
100 * return value means the request could not been sent (e.g. SSL error).
101 * Side effect: message buffer */
102 static isds_error http(struct isds_ctx *context, const char *url,
103 const void *request, const size_t request_length,
104 void **response, size_t *response_length,
105 char **mime_type, char **charset, long *http_code) {
107 CURLcode curl_err;
108 isds_error err = IE_SUCCESS;
109 struct soap_body body;
110 char *content_type;
111 struct curl_slist *headers = NULL;
114 if (!context) return IE_INVALID_CONTEXT;
115 if (!url) return IE_INVAL;
116 if (request_length > 0 && !request) return IE_INVAL;
117 if (!response || !response_length) return IE_INVAL;
119 /* Set the body here to allow deallocataion in leave block */
120 body.data = *response;
121 body.length = 0;
123 /* Set Request-URI */
124 curl_err = curl_easy_setopt(context->curl, CURLOPT_URL, url);
126 /* Set TLS options */
127 if (!curl_err && context->tls_verify_server) {
128 if (!*context->tls_verify_server)
129 isds_log(ILF_SEC, ILL_WARNING,
130 _("Disabling server identity verification. "
131 "That was your decision.\n"));
132 curl_err = curl_easy_setopt(context->curl, CURLOPT_SSL_VERIFYPEER,
133 (*context->tls_verify_server)? 1L : 0L);
134 if (!curl_err) {
135 curl_err = curl_easy_setopt(context->curl, CURLOPT_SSL_VERIFYHOST,
136 (*context->tls_verify_server)? 2L : 0L);
139 if (!curl_err && context->tls_ca_file) {
140 isds_log(ILF_SEC, ILL_INFO,
141 _("CA certificates will be searched in `%s' file since now\n"),
142 context->tls_ca_file);
143 curl_err = curl_easy_setopt(context->curl, CURLOPT_CAINFO,
144 context->tls_ca_file);
146 if (!curl_err && context->tls_ca_dir) {
147 isds_log(ILF_SEC, ILL_INFO,
148 _("CA certificates will be searched in `%s' directory "
149 "since now\n"), context->tls_ca_file);
150 curl_err = curl_easy_setopt(context->curl, CURLOPT_CAINFO,
151 context->tls_ca_file);
155 /* Set credentials */
156 if (!curl_err && context->username) {
157 curl_err = curl_easy_setopt(context->curl, CURLOPT_USERNAME,
158 context->username);
160 if (!curl_err && context->password) {
161 curl_err = curl_easy_setopt(context->curl, CURLOPT_PASSWORD,
162 context->password);
165 /* Set timeout */
166 if (!curl_err) {
167 curl_err = curl_easy_setopt(context->curl, CURLOPT_NOSIGNAL, 1);
169 if (!curl_err && context->timeout) {
170 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT_MS,
171 context->timeout);
174 /* Register callback */
175 if (context->progress_callback) {
176 if (!curl_err) {
177 curl_err = curl_easy_setopt(context->curl, CURLOPT_NOPROGRESS, 0);
179 if (!curl_err) {
180 curl_err = curl_easy_setopt(context->curl,
181 CURLOPT_PROGRESSFUNCTION, progress_proxy);
183 if (!curl_err) {
184 curl_err = curl_easy_setopt(context->curl, CURLOPT_PROGRESSDATA,
185 context);
189 /* Set other CURL features */
190 if (!curl_err) {
191 curl_err = curl_easy_setopt(context->curl, CURLOPT_FAILONERROR, 0);
193 if (!curl_err) {
194 curl_err = curl_easy_setopt(context->curl, CURLOPT_FOLLOWLOCATION, 1);
196 if (!curl_err) {
197 /* TODO: Make the redirect depth configurable */
198 curl_err = curl_easy_setopt(context->curl, CURLOPT_MAXREDIRS, 8);
200 if (!curl_err) {
201 curl_err = curl_easy_setopt(context->curl,
202 CURLOPT_UNRESTRICTED_AUTH, 1);
204 if (!curl_err) {
205 curl_err = curl_easy_setopt(context->curl, CURLOPT_COOKIEFILE, "");
208 /* Set get-response function */
209 if (!curl_err) {
210 curl_err = curl_easy_setopt(context->curl, CURLOPT_WRITEFUNCTION,
211 write_body);
213 if (!curl_err) {
214 curl_err = curl_easy_setopt(context->curl, CURLOPT_WRITEDATA, &body);
217 /* Set MIME types and headers requires by SOAP 1.1.
218 * SOAP 1.1 requires text/xml, SOAP 1.2 requires application/soap+xml */
219 if (!curl_err) {
220 headers = curl_slist_append(headers,
221 "Accept: application/soap+xml,application/xml,text/xml");
222 if (!headers) {
223 err = IE_NOMEM;
224 goto leave;
226 headers = curl_slist_append(headers, "Content-Type: text/xml");
227 if (!headers) {
228 err = IE_NOMEM;
229 goto leave;
231 headers = curl_slist_append(headers, "SOAPAction: ");
232 if (!headers) {
233 err = IE_NOMEM;
234 goto leave;
236 curl_err = curl_easy_setopt(context->curl, CURLOPT_HTTPHEADER, headers);
238 if (!curl_err) {
239 /* Set user agent identification */
240 /* TODO: Present library version, curl etc. in User-Agent */
241 curl_err = curl_easy_setopt(context->curl, CURLOPT_USERAGENT, "libisds");
244 /* Set POST request body */
245 if (!curl_err) {
246 curl_err = curl_easy_setopt(context->curl, CURLOPT_POST, 1);
248 if (!curl_err) {
249 curl_err = curl_easy_setopt(context->curl, CURLOPT_POSTFIELDS, request);
251 if (!curl_err) {
252 curl_err = curl_easy_setopt(context->curl, CURLOPT_POSTFIELDSIZE,
253 request_length);
256 /* Check for errors so far */
257 if (curl_err) {
258 isds_log_message(context, curl_easy_strerror(curl_err));
259 err = IE_NETWORK;
260 goto leave;
263 isds_log(ILF_HTTP, ILL_DEBUG, _("Sending POST request to %s\n"), url);
264 isds_log(ILF_HTTP, ILL_DEBUG,
265 _("POST body length: %zu, content follows:\n"), request_length);
266 isds_log(ILF_HTTP, ILL_DEBUG, "%.*s\n", request_length, request);
267 isds_log(ILF_HTTP, ILL_DEBUG, _("End of POST body\n"));
268 if ((log_facilities & ILF_HTTP) && (log_level >= ILL_DEBUG) ) {
269 curl_easy_setopt(context->curl, CURLOPT_VERBOSE, 1);
273 /* Do the request */
274 curl_err = curl_easy_perform(context->curl);
276 /* Wipe credentials out of the handler */
277 if (context->username) {
278 curl_easy_setopt(context->curl, CURLOPT_USERNAME, NULL);
280 if (context->password) {
281 curl_easy_setopt(context->curl, CURLOPT_PASSWORD, NULL);
284 if (!curl_err)
285 curl_err = curl_easy_getinfo(context->curl, CURLINFO_CONTENT_TYPE,
286 &content_type);
288 if (curl_err) {
289 /* TODO: CURL is not internationalized yet. Collect CURL messages for
290 * I18N. */
291 isds_printf_message(context,
292 _("%s: %s"), url, _(curl_easy_strerror(curl_err)));
293 err = IE_NETWORK;
294 goto leave;
297 isds_log(ILF_HTTP, ILL_DEBUG, _("Final response to %s received\n"), url);
298 isds_log(ILF_HTTP, ILL_DEBUG,
299 _("Response body length: %zu, content follows:\n"),
300 body.length);
301 isds_log(ILF_HTTP, ILL_DEBUG, "%.*s\n", body.length, body.data);
302 isds_log(ILF_HTTP, ILL_DEBUG, _("End of response body\n"));
305 /* Extract MIME type and charset */
306 if (content_type) {
307 char *sep;
308 size_t offset;
310 sep = strchr(content_type, ';');
311 if (sep) offset = (size_t) (sep - content_type);
312 else offset = strlen(content_type);
314 if (mime_type) {
315 *mime_type = malloc(offset + 1);
316 if (!*mime_type) {
317 err = IE_NOMEM;
318 goto leave;
320 memcpy(*mime_type, content_type, offset);
321 (*mime_type)[offset] = '\0';
324 if (charset) {
325 if (!sep) {
326 *charset = NULL;
327 } else {
328 sep = strstr(sep, "charset=");
329 if (!sep) {
330 *charset = NULL;
331 } else {
332 *charset = strdup(sep + 8);
333 if (!*charset) {
334 err = IE_NOMEM;
335 goto leave;
342 /* Get HTTP response code */
343 if (http_code) {
344 curl_err = curl_easy_getinfo(context->curl,
345 CURLINFO_RESPONSE_CODE, http_code);
346 if (curl_err) {
347 err = IE_ERROR;
348 goto leave;
352 leave:
353 curl_slist_free_all(headers);
355 if (err) {
356 free(body.data);
357 body.data = NULL;
358 body.length = 0;
360 if (mime_type) {
361 free(*mime_type);
362 *mime_type = NULL;
364 if (charset) {
365 free(*charset);
366 *charset = NULL;
369 close_connection(context);
372 *response = body.data;
373 *response_length = body.length;
375 return err;
379 /* Do SOAP request.
380 * @context holds the base URL,
381 * @file is a (CGI) file of SOAP URL,
382 * @request is XML node set with SOAP request body.
383 * @file must be NULL, @request should be NULL rather than empty, if they should
384 * not be signaled in the SOAP request.
385 * @reponse is automatically allocated() node set with SOAP response body.
386 * You must xmlFreeNodeList() it. This is literal body, empty (NULL), one node
387 * or more nodes can be returned.
388 * @raw_response is automatically allocated bitstream with response body. Use
389 * NULL if you don't care
390 * @raw_response_length is size of @raw_response in bytes
391 * In case of error the response will be deallocated automatically.
392 * Side effect: message buffer */
393 _hidden isds_error soap(struct isds_ctx *context, const char *file,
394 const xmlNodePtr request, xmlNodePtr *response,
395 void **raw_response, size_t *raw_response_length) {
397 isds_error err = IE_SUCCESS;
398 char *url = NULL;
399 char *mime_type = NULL;
400 long http_code = 0;
401 xmlBufferPtr http_request = NULL;
402 xmlSaveCtxtPtr save_ctx = NULL;
403 xmlDocPtr request_soap_doc = NULL;
404 xmlNodePtr request_soap_envelope = NULL, request_soap_body = NULL;
405 xmlNsPtr soap_ns = NULL;
406 void *http_response = NULL;
407 size_t response_length = 0;
408 xmlDocPtr response_soap_doc = NULL;
409 xmlNodePtr response_root = NULL;
410 xmlXPathContextPtr xpath_ctx = NULL;
411 xmlXPathObjectPtr response_soap_headers = NULL, response_soap_body = NULL,
412 response_soap_fault = NULL;
415 if (!context) return IE_INVALID_CONTEXT;
416 if (!response) return IE_INVAL;
417 if (!raw_response_length && raw_response) return IE_INVAL;
419 xmlFreeNodeList(*response);
420 *response = NULL;
421 if (raw_response) *raw_response = NULL;
423 url = astrcat(context->url, file);
424 if (!url) return IE_NOMEM;
426 /* Build SOAP request envelope */
427 request_soap_doc = xmlNewDoc(BAD_CAST "1.0");
428 if (!request_soap_doc) {
429 isds_log_message(context, _("Could not build SOAP request document"));
430 err = IE_ERROR;
431 goto leave;
433 request_soap_envelope = xmlNewNode(NULL, BAD_CAST "Envelope");
434 if (!request_soap_envelope) {
435 isds_log_message(context, _("Could not build SOAP request envelope"));
436 err = IE_ERROR;
437 goto leave;
439 xmlDocSetRootElement(request_soap_doc, request_soap_envelope);
440 /* Only this way we get namespace definition as @xmlns:soap,
441 * otherwise we get namespace prefix without definition */
442 soap_ns = xmlNewNs(request_soap_envelope, BAD_CAST SOAP_NS, NULL);
443 if(!soap_ns) {
444 isds_log_message(context, _("Could not create SOAP name space"));
445 err = IE_ERROR;
446 goto leave;
448 xmlSetNs(request_soap_envelope, soap_ns);
449 request_soap_body = xmlNewChild(request_soap_envelope, NULL,
450 BAD_CAST "Body", NULL);
451 if (!request_soap_body) {
452 isds_log_message(context,
453 _("Could not add Body to SOAP request envelope"));
454 err = IE_ERROR;
455 goto leave;
458 /* Append request XML node set to SOAP body if request is not empty */
459 /* XXX: Copy of request must be used, otherwise xmlFreeDoc(request_soap_doc)
460 * would destroy this outer structure. */
461 if (request) {
462 xmlNodePtr request_copy = xmlCopyNodeList(request);
463 if (!request_copy) {
464 isds_log_message(context,
465 _("Could not copy request content"));
466 err = IE_ERROR;
467 goto leave;
469 if (!xmlAddChildList(request_soap_body, request_copy)) {
470 xmlFreeNodeList(request_copy);
471 isds_log_message(context,
472 _("Could not add request content to SOAP "
473 "request envelope"));
474 err = IE_ERROR;
475 goto leave;
480 /* Serialize the SOAP request into HTTP request body */
481 http_request = xmlBufferCreate();
482 if (!http_request) {
483 isds_log_message(context,
484 _("Could not create xmlBuffer for HTTP request body"));
485 err = IE_ERROR;
486 goto leave;
488 /* Last argument 1 means format the XML tree. This is pretty but it breaks
489 * digital signatures probably because ISDS abandoned XMLDSig */
490 save_ctx = xmlSaveToBuffer(http_request, "UTF-8", 1);
491 if (!save_ctx) {
492 isds_log_message(context,
493 _("Could not create XML serializer"));
494 err = IE_ERROR;
495 goto leave;
497 /* XXX: According LibXML documentation, this function does not return
498 * meaningfull value yet */
499 xmlSaveDoc(save_ctx, request_soap_doc);
500 if (-1 == xmlSaveFlush(save_ctx)) {
501 isds_log_message(context,
502 _("Could not serialize SOAP request to HTTP request bddy"));
503 err = IE_ERROR;
504 goto leave;
507 isds_log(ILF_SOAP, ILL_DEBUG,
508 _("SOAP request to sent to %s:\n%.*s\nEnd of SOAP request\n"),
509 url, http_request->use, http_request->content);
511 err = http(context, url, http_request->content, http_request->use,
512 &http_response, &response_length,
513 &mime_type, NULL, &http_code);
515 /* TODO: HTTP binding for SOAP prescribes non-200 HTTP return codes
516 * to be processes too. */
518 if (err) {
519 goto leave;
522 /* Check for HTTP return code */
523 isds_log(ILF_SOAP, ILL_DEBUG, _("Server returned %ld HTTP code\n"),
524 http_code);
525 switch (http_code) {
526 /* XXX: We must see which code is used for not permitted ISDS
527 * operation like downloading message without proper user
528 * permissions. In that cat we should keep connection opened. */
529 case 401:
530 err = IE_NOT_LOGGED_IN;
531 isds_log_message(context, _("Authentication failed"));
532 goto leave;
533 break;
534 case 404:
535 err = IE_HTTP;
536 isds_log_message(context,
537 _("Code 404: Document not found on server"));
538 goto leave;
539 break;
540 /* 500 should return standard SOAP message */
543 /* Check for Content-Type: text/xml.
544 * Do it after HTTP code check because 401 Unauthorized returns HTML web
545 * page for browsers. */
546 if (mime_type && strcmp(mime_type, "text/xml")
547 && strcmp(mime_type, "application/soap+xml")
548 && strcmp(mime_type, "application/xml")) {
549 char *mime_type_locale = utf82locale(mime_type);
550 isds_printf_message(context,
551 _("%s: bad MIME type sent by server: %s"), url,
552 mime_type_locale);
553 free(mime_type_locale);
554 err = IE_SOAP;
555 goto leave;
558 /* TODO: Convert returned body into XML default encoding */
560 /* Parse the HTTP body as XML */
561 response_soap_doc = xmlParseMemory(http_response, response_length);
562 if (!response_soap_doc) {
563 err = IE_XML;
564 goto leave;
567 xpath_ctx = xmlXPathNewContext(response_soap_doc);
568 if (!xpath_ctx) {
569 err = IE_ERROR;
570 goto leave;
573 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
574 err = IE_ERROR;
575 goto leave;
578 isds_log(ILF_SOAP, ILL_DEBUG,
579 _("SOAP response received:\n%.*s\nEnd of SOAP response\n"),
580 response_length, http_response);
583 /* Check for SOAP version */
584 response_root = xmlDocGetRootElement(response_soap_doc);
585 if (!response_root) {
586 isds_log_message(context, "SOAP response has no root element");
587 err = IE_SOAP;
588 goto leave;
590 if (xmlStrcmp(response_root->name, BAD_CAST "Envelope") ||
591 xmlStrcmp(response_root->ns->href, BAD_CAST SOAP_NS)) {
592 isds_log_message(context, "SOAP response is not SOAP 1.1 document");
593 err = IE_SOAP;
594 goto leave;
597 /* Check for SOAP Headers */
598 response_soap_headers = xmlXPathEvalExpression(
599 BAD_CAST "/soap:Envelope/soap:Header/"
600 "*[@soap:mustUnderstand/text() = true()]", xpath_ctx);
601 if (!response_soap_headers) {
602 err = IE_ERROR;
603 goto leave;
605 if (!xmlXPathNodeSetIsEmpty(response_soap_headers->nodesetval)) {
606 isds_log_message(context,
607 _("SOAP response requires unsupported feature"));
608 /* TODO: log the headers
609 * xmlChar *fragment = NULL;
610 * fragment = xmlXPathCastNodeSetToSting(response_soap_headers->nodesetval);*/
611 err = IE_NOTSUP;
612 goto leave;
615 /* Get SOAP Body */
616 response_soap_body = xmlXPathEvalExpression(
617 BAD_CAST "/soap:Envelope/soap:Body", xpath_ctx);
618 if (!response_soap_body) {
619 err = IE_ERROR;
620 goto leave;
622 if (xmlXPathNodeSetIsEmpty(response_soap_body->nodesetval)) {
623 isds_log_message(context,
624 _("SOAP response does not contain SOAP Body element"));
625 err = IE_SOAP;
626 goto leave;
628 if (response_soap_body->nodesetval->nodeNr > 1) {
629 isds_log_message(context,
630 _("SOAP response has more than one Body element"));
631 err = IE_SOAP;
632 goto leave;
635 /* Check for SOAP Fault */
636 response_soap_fault = xmlXPathEvalExpression(
637 BAD_CAST "/soap:Envelope/soap:Body/soap:Fault", xpath_ctx);
638 if (!response_soap_fault) {
639 err = IE_ERROR;
640 goto leave;
642 if (!xmlXPathNodeSetIsEmpty(response_soap_fault->nodesetval)) {
643 /* TODO: log the fultcode and faultstring */
644 isds_log_message(context, _("SOAP response signals Fault"));
645 err = IE_SOAP;
646 goto leave;
650 /* Extract XML Tree with ISDS response from SOAP envelope and return it.
651 * XXX: response_soap_body is Body, we need children which may not exist
652 * (i.e. empty Body). */
653 /* TODO: Destroy SOAP response but Body childern. This is more memory
654 * friendly than copying (potentialy) fat body */
655 if (response_soap_body->nodesetval->nodeTab[0]->children) {
656 *response = xmlDocCopyNodeList(response_soap_doc,
657 response_soap_body->nodesetval->nodeTab[0]->children);
658 if (!*response) {
659 err = IE_NOMEM;
660 goto leave;
662 } else *response = NULL;
664 /* Save raw response */
665 if (raw_response) {
666 *raw_response = http_response;
667 *raw_response_length = response_length;
668 http_response = NULL;
672 leave:
673 if (err) {
674 xmlFreeNodeList(*response);
675 *response = NULL;
678 xmlXPathFreeObject(response_soap_fault);
679 xmlXPathFreeObject(response_soap_body);
680 xmlXPathFreeObject(response_soap_headers);
681 xmlXPathFreeContext(xpath_ctx);
682 xmlFreeDoc(response_soap_doc);
683 free(mime_type);
684 free(http_response);
685 xmlSaveClose(save_ctx);
686 xmlBufferFree(http_request);
687 xmlFreeDoc(request_soap_doc); /* recursive, frees request_body, soap_ns*/
688 free(url);
690 return err;
694 /* LibXML functions:
696 * void xmlInitParser(void)
697 * Initialization function for the XML parser. This is not reentrant. Call
698 * once before processing in case of use in multithreaded programs.
700 * int xmlInitParserCtxt(xmlParserCtxtPtr ctxt)
701 * Initialize a parser context
703 * xmlDocPtr xmlCtxtReadDoc(xmlParserCtxtPtr ctxt, const xmlChar * cur,
704 * const * char URL, const char * encoding, int options);
705 * Parse in-memory NULL-terminated document @cur.
707 * xmlDocPtr xmlParseMemory(const char * buffer, int size)
708 * Parse an XML in-memory block and build a tree.
710 * xmlParserCtxtPtr xmlCreateMemoryParserCtxt(const char * buffer, int
711 * size);
712 * Create a parser context for an XML in-memory document.
714 * xmlParserCtxtPtr xmlCreateDocParserCtxt(const xmlChar * cur)
715 * Creates a parser context for an XML in-memory document.
717 * xmlDocPtr xmlCtxtReadMemory(xmlParserCtxtPtr ctxt,
718 * const char * buffer, int size, const char * URL, const char * encoding,
719 * int options)
720 * Parse an XML in-memory document and build a tree. This reuses the existing
721 * @ctxt parser context.
723 * void xmlCleanupParser(void)
724 * Cleanup function for the XML library. It tries to reclaim all parsing
725 * related glob document related memory. Calling this function should not
726 * prevent reusing the libr finished using the library or XML document built
727 * with it.
729 * void xmlClearParserCtxt(xmlParserCtxtPtr ctxt)
730 * Clear (release owned resources) and reinitialize a parser context.
732 * void xmlCtxtReset(xmlParserCtxtPtr ctxt)
733 * Reset a parser context
735 * void xmlFreeParserCtxt(xmlParserCtxtPtr ctxt)
736 * Free all the memory used by a parser context. However the parsed document
737 * in ctxt->myDoc is not freed.
739 * void xmlFreeDoc(xmlDocPtr cur)
740 * Free up all the structures used by a document, tree included.