Pass PKI material into curl
[libisds.git] / src / soap.c
blobd86cde73cf1276abfb933b9ffb764975358fdd66
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 %s closed\n"),
24 context->url);
25 return IE_SUCCESS;
26 } else {
27 return IE_CONNECTION_CLOSED;
32 /* CURL call back function called when chunk of HTTP reponse body is available.
33 * @buffer points to new data
34 * @size * @nmemb is length of the chunk in bytes. Zero means empty body.
35 * @userp is private structure.
36 * Must reuturn the length of the chunk, otherwise CURL will signal
37 * CURL_WRITE_ERROR. */
38 static size_t write_body(void *buffer, size_t size, size_t nmemb, void *userp) {
39 struct soap_body *body = (struct soap_body *) userp;
40 void *new_data;
42 /* FIXME: Check for (size * nmemb + body->lengt) !> SIZE_T_MAX.
43 * Precompute the product then. */
45 if (!body) return 0; /* This should never happen */
46 if (0 == (size * nmemb)) return 0; /* Empty body */
48 new_data = realloc(body->data, body->length + size * nmemb);
49 if (!new_data) return 0;
51 memcpy(new_data + body->length, buffer, size * nmemb);
53 body->data = new_data;
54 body->length += size * nmemb;
56 return (size * nmemb);
60 /* CURL progress callback proxy to rearrange arguments.
61 * @curl_data is session context */
62 static int progress_proxy(void *curl_data, double download_total,
63 double download_current, double upload_total, double upload_current) {
64 struct isds_ctx *context = (struct isds_ctx *) curl_data;
65 int abort = 0;
67 if (context && context->progress_callback) {
68 abort = context->progress_callback(
69 upload_total, upload_current,
70 download_total, download_current,
71 context->progress_callback_data);
72 if (abort) {
73 isds_log(ILF_HTTP, ILL_INFO,
74 _("Application aborted HTTP transfer"));
78 return abort;
82 /* Do HTTP request.
83 * @context holds the base URL,
84 * @url is a (CGI) file of SOAP URL,
85 * @request is body for POST request
86 * @request_length is length of @request in bytes
87 * @reponse is automatically reallocated() buffer to fit HTTP response with
88 * @response_length (does not need to match allocatef memory exactly). You must
89 * free() the @response.
90 * @mime_type is automatically allocated MIME type send by server (*NULL if not
91 * sent). Set NULL if you don't care.
92 * @charset is charset of the body signaled by server. The same constrains
93 * like on @mime_type apply.
94 * @http_code is final HTTP code returned by server. This can be 200, 401, 500
95 * or any other one. Pass NULL if you don't interrest.
96 * In case of error, the response memory, MIME type, charset and lenght will be
97 * deallocated and zerod automatically. Thus be sure they are preallocated or
98 * they points to NULL.
99 * Be ware that successful return value does not mean the HTTP request has
100 * been accepted by the server. You must cosult @http_code. OTOH, failure
101 * return value means the request could not been sent (e.g. SSL error).
102 * Side effect: message buffer */
103 static isds_error http(struct isds_ctx *context, const char *url,
104 const void *request, const size_t request_length,
105 void **response, size_t *response_length,
106 char **mime_type, char **charset, long *http_code) {
108 CURLcode curl_err;
109 isds_error err = IE_SUCCESS;
110 struct soap_body body;
111 char *content_type;
112 struct curl_slist *headers = NULL;
115 if (!context) return IE_INVALID_CONTEXT;
116 if (!url) return IE_INVAL;
117 if (request_length > 0 && !request) return IE_INVAL;
118 if (!response || !response_length) return IE_INVAL;
120 /* Set the body here to allow deallocatation in leave block */
121 body.data = *response;
122 body.length = 0;
124 /* Set Request-URI */
125 curl_err = curl_easy_setopt(context->curl, CURLOPT_URL, url);
127 /* Set TLS options */
128 if (!curl_err && context->tls_verify_server) {
129 if (!*context->tls_verify_server)
130 isds_log(ILF_SEC, ILL_WARNING,
131 _("Disabling server identity verification. "
132 "That was your decision.\n"));
133 curl_err = curl_easy_setopt(context->curl, CURLOPT_SSL_VERIFYPEER,
134 (*context->tls_verify_server)? 1L : 0L);
135 if (!curl_err) {
136 curl_err = curl_easy_setopt(context->curl, CURLOPT_SSL_VERIFYHOST,
137 (*context->tls_verify_server)? 2L : 0L);
140 if (!curl_err && context->tls_ca_file) {
141 isds_log(ILF_SEC, ILL_INFO,
142 _("CA certificates will be searched in `%s' file since now\n"),
143 context->tls_ca_file);
144 curl_err = curl_easy_setopt(context->curl, CURLOPT_CAINFO,
145 context->tls_ca_file);
147 if (!curl_err && context->tls_ca_dir) {
148 isds_log(ILF_SEC, ILL_INFO,
149 _("CA certificates will be searched in `%s' directory "
150 "since now\n"), context->tls_ca_file);
151 curl_err = curl_easy_setopt(context->curl, CURLOPT_CAINFO,
152 context->tls_ca_file);
156 /* Set credentials */
157 #if HAVE_DECL_CURLOPT_USERNAME /* Since curl-7.19.1 */
158 if (!curl_err && context->username) {
159 curl_err = curl_easy_setopt(context->curl, CURLOPT_USERNAME,
160 context->username);
162 if (!curl_err && context->password) {
163 curl_err = curl_easy_setopt(context->curl, CURLOPT_PASSWORD,
164 context->password);
166 #else
167 if (!curl_err && (context->username || context->password)) {
168 char *userpwd = astrcat3(context->username, ":", context->password);
169 if (!userpwd) {
170 isds_log_message(context, _("Could not pass credentials to CURL"));
171 err = IE_NOMEM;
172 goto leave;
174 curl_err = curl_easy_setopt(context->curl, CURLOPT_USERPWD, userpwd);
175 free(userpwd);
177 #endif /* not HAVE_DECL_CURLOPT_USERNAME */
179 /* Set PKI credentials */
180 if (!curl_err && (context->pki_credentials)) {
181 if (context->pki_credentials->certificate) {
182 isds_log(ILF_SEC, ILL_INFO,
183 _("Client %s certificate will be read from `%s' file\n"),
184 (context->pki_credentials->certificate_format ==
185 PKI_FORMAT_DER) ? _("DER") : _("PEM"),
186 context->pki_credentials->certificate);
187 curl_err = curl_easy_setopt(context->curl, CURLOPT_SSLCERTTYPE,
188 context->pki_credentials->certificate_format);
189 if (!curl_err)
190 curl_err = curl_easy_setopt(context->curl, CURLOPT_SSLCERT,
191 context->pki_credentials->certificate);
193 if (!curl_err && context->pki_credentials->key) {
194 if (context->pki_credentials->engine) {
195 /* Select SSL engine */
196 isds_log(ILF_SEC, ILL_INFO, _("Client private key `%s' "
197 "from `%s' engine will be used\n"),
198 context->pki_credentials->key,
199 context->pki_credentials->engine);
200 curl_err = curl_easy_setopt(context->curl, CURLOPT_SSLKEYTYPE,
201 "ENG");
202 if (!curl_err)
203 curl_err = curl_easy_setopt(context->curl,
204 CURLOPT_SSLENGINE,
205 context->pki_credentials->engine);
206 } else {
207 /* Select no SSL engine */
208 isds_log(ILF_SEC, ILL_INFO, _("Client %s private key will be "
209 "read from `%s' file\n"),
210 (context->pki_credentials->key_format ==
211 PKI_FORMAT_DER) ? _("DER") : _("PEM"),
212 context->pki_credentials->key);
213 curl_err = curl_easy_setopt(context->curl, CURLOPT_SSLKEYTYPE,
214 context->pki_credentials->key_format);
216 if (!curl_err)
217 curl_err = curl_easy_setopt(context->curl, CURLOPT_SSLKEY,
218 context->pki_credentials->key);
219 if (!curl_err)
220 curl_err = curl_easy_setopt(context->curl, CURLOPT_KEYPASSWD,
221 context->pki_credentials->passphrase);
225 /* Set timeout */
226 if (!curl_err) {
227 curl_err = curl_easy_setopt(context->curl, CURLOPT_NOSIGNAL, 1);
229 if (!curl_err && context->timeout) {
230 #if HAVE_DECL_CURLOPT_TIMEOUT_MS /* Since curl-7.16.2 */
231 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT_MS,
232 context->timeout);
233 #else
234 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT,
235 context->timeout / 1000);
236 #endif /* not HAVE_DECL_CURLOPT_TIMEOUT_MS */
239 /* Register callback */
240 if (context->progress_callback) {
241 if (!curl_err) {
242 curl_err = curl_easy_setopt(context->curl, CURLOPT_NOPROGRESS, 0);
244 if (!curl_err) {
245 curl_err = curl_easy_setopt(context->curl,
246 CURLOPT_PROGRESSFUNCTION, progress_proxy);
248 if (!curl_err) {
249 curl_err = curl_easy_setopt(context->curl, CURLOPT_PROGRESSDATA,
250 context);
254 /* Set other CURL features */
255 if (!curl_err) {
256 curl_err = curl_easy_setopt(context->curl, CURLOPT_FAILONERROR, 0);
258 if (!curl_err) {
259 curl_err = curl_easy_setopt(context->curl, CURLOPT_FOLLOWLOCATION, 1);
261 if (!curl_err) {
262 /* TODO: Make the redirect depth configurable */
263 curl_err = curl_easy_setopt(context->curl, CURLOPT_MAXREDIRS, 8);
265 if (!curl_err) {
266 curl_err = curl_easy_setopt(context->curl,
267 CURLOPT_UNRESTRICTED_AUTH, 1);
269 if (!curl_err) {
270 curl_err = curl_easy_setopt(context->curl, CURLOPT_COOKIEFILE, "");
273 /* Set get-response function */
274 if (!curl_err) {
275 curl_err = curl_easy_setopt(context->curl, CURLOPT_WRITEFUNCTION,
276 write_body);
278 if (!curl_err) {
279 curl_err = curl_easy_setopt(context->curl, CURLOPT_WRITEDATA, &body);
282 /* Set MIME types and headers requires by SOAP 1.1.
283 * SOAP 1.1 requires text/xml, SOAP 1.2 requires application/soap+xml */
284 if (!curl_err) {
285 headers = curl_slist_append(headers,
286 "Accept: application/soap+xml,application/xml,text/xml");
287 if (!headers) {
288 err = IE_NOMEM;
289 goto leave;
291 headers = curl_slist_append(headers, "Content-Type: text/xml");
292 if (!headers) {
293 err = IE_NOMEM;
294 goto leave;
296 headers = curl_slist_append(headers, "SOAPAction: ");
297 if (!headers) {
298 err = IE_NOMEM;
299 goto leave;
301 curl_err = curl_easy_setopt(context->curl, CURLOPT_HTTPHEADER, headers);
303 if (!curl_err) {
304 /* Set user agent identification */
305 /* TODO: Present library version, curl etc. in User-Agent */
306 curl_err = curl_easy_setopt(context->curl, CURLOPT_USERAGENT, "libisds");
309 /* Set POST request body */
310 if (!curl_err) {
311 curl_err = curl_easy_setopt(context->curl, CURLOPT_POST, 1);
313 if (!curl_err) {
314 curl_err = curl_easy_setopt(context->curl, CURLOPT_POSTFIELDS, request);
316 if (!curl_err) {
317 curl_err = curl_easy_setopt(context->curl, CURLOPT_POSTFIELDSIZE,
318 request_length);
321 /* Check for errors so far */
322 if (curl_err) {
323 isds_log_message(context, curl_easy_strerror(curl_err));
324 err = IE_NETWORK;
325 goto leave;
328 isds_log(ILF_HTTP, ILL_DEBUG, _("Sending POST request to %s\n"), url);
329 isds_log(ILF_HTTP, ILL_DEBUG,
330 _("POST body length: %zu, content follows:\n"), request_length);
331 isds_log(ILF_HTTP, ILL_DEBUG, "%.*s\n", request_length, request);
332 isds_log(ILF_HTTP, ILL_DEBUG, _("End of POST body\n"));
333 if ((log_facilities & ILF_HTTP) && (log_level >= ILL_DEBUG) ) {
334 curl_easy_setopt(context->curl, CURLOPT_VERBOSE, 1);
338 /* Do the request */
339 curl_err = curl_easy_perform(context->curl);
341 /* Wipe credentials out of the handler */
342 #if HAVE_DECL_CURLOPT_USERNAME /* Since curl-7.19.1 */
343 if (context->username) {
344 curl_easy_setopt(context->curl, CURLOPT_USERNAME, NULL);
346 if (context->password) {
347 curl_easy_setopt(context->curl, CURLOPT_PASSWORD, NULL);
349 #else
350 if (context->username || context->password) {
351 curl_easy_setopt(context->curl, CURLOPT_USERPWD, NULL);
353 #endif /* not HAVE_DECL_CURLOPT_USERNAME */
355 if (!curl_err)
356 curl_err = curl_easy_getinfo(context->curl, CURLINFO_CONTENT_TYPE,
357 &content_type);
359 if (curl_err) {
360 /* TODO: CURL is not internationalized yet. Collect CURL messages for
361 * I18N. */
362 isds_printf_message(context,
363 _("%s: %s"), url, _(curl_easy_strerror(curl_err)));
364 if (curl_err == CURLE_ABORTED_BY_CALLBACK)
365 err = IE_ABORTED;
366 else
367 err = IE_NETWORK;
368 goto leave;
371 isds_log(ILF_HTTP, ILL_DEBUG, _("Final response to %s received\n"), url);
372 isds_log(ILF_HTTP, ILL_DEBUG,
373 _("Response body length: %zu, content follows:\n"),
374 body.length);
375 isds_log(ILF_HTTP, ILL_DEBUG, "%.*s\n", body.length, body.data);
376 isds_log(ILF_HTTP, ILL_DEBUG, _("End of response body\n"));
379 /* Extract MIME type and charset */
380 if (content_type) {
381 char *sep;
382 size_t offset;
384 sep = strchr(content_type, ';');
385 if (sep) offset = (size_t) (sep - content_type);
386 else offset = strlen(content_type);
388 if (mime_type) {
389 *mime_type = malloc(offset + 1);
390 if (!*mime_type) {
391 err = IE_NOMEM;
392 goto leave;
394 memcpy(*mime_type, content_type, offset);
395 (*mime_type)[offset] = '\0';
398 if (charset) {
399 if (!sep) {
400 *charset = NULL;
401 } else {
402 sep = strstr(sep, "charset=");
403 if (!sep) {
404 *charset = NULL;
405 } else {
406 *charset = strdup(sep + 8);
407 if (!*charset) {
408 err = IE_NOMEM;
409 goto leave;
416 /* Get HTTP response code */
417 if (http_code) {
418 curl_err = curl_easy_getinfo(context->curl,
419 CURLINFO_RESPONSE_CODE, http_code);
420 if (curl_err) {
421 err = IE_ERROR;
422 goto leave;
426 leave:
427 curl_slist_free_all(headers);
429 if (err) {
430 free(body.data);
431 body.data = NULL;
432 body.length = 0;
434 if (mime_type) {
435 free(*mime_type);
436 *mime_type = NULL;
438 if (charset) {
439 free(*charset);
440 *charset = NULL;
443 if (err != IE_ABORTED) close_connection(context);
446 *response = body.data;
447 *response_length = body.length;
449 return err;
453 /* Do SOAP request.
454 * @context holds the base URL,
455 * @file is a (CGI) file of SOAP URL,
456 * @request is XML node set with SOAP request body.
457 * @file must be NULL, @request should be NULL rather than empty, if they should
458 * not be signaled in the SOAP request.
459 * @reponse is automatically allocated() node set with SOAP response body.
460 * You must xmlFreeNodeList() it. This is literal body, empty (NULL), one node
461 * or more nodes can be returned.
462 * @raw_response is automatically allocated bitstream with response body. Use
463 * NULL if you don't care
464 * @raw_response_length is size of @raw_response in bytes
465 * In case of error the response will be deallocated automatically.
466 * Side effect: message buffer */
467 _hidden isds_error soap(struct isds_ctx *context, const char *file,
468 const xmlNodePtr request, xmlNodePtr *response,
469 void **raw_response, size_t *raw_response_length) {
471 isds_error err = IE_SUCCESS;
472 char *url = NULL;
473 char *mime_type = NULL;
474 long http_code = 0;
475 xmlBufferPtr http_request = NULL;
476 xmlSaveCtxtPtr save_ctx = NULL;
477 xmlDocPtr request_soap_doc = NULL;
478 xmlNodePtr request_soap_envelope = NULL, request_soap_body = NULL;
479 xmlNsPtr soap_ns = NULL;
480 void *http_response = NULL;
481 size_t response_length = 0;
482 xmlDocPtr response_soap_doc = NULL;
483 xmlNodePtr response_root = NULL;
484 xmlXPathContextPtr xpath_ctx = NULL;
485 xmlXPathObjectPtr response_soap_headers = NULL, response_soap_body = NULL,
486 response_soap_fault = NULL;
489 if (!context) return IE_INVALID_CONTEXT;
490 if (!response) return IE_INVAL;
491 if (!raw_response_length && raw_response) return IE_INVAL;
493 xmlFreeNodeList(*response);
494 *response = NULL;
495 if (raw_response) *raw_response = NULL;
497 url = astrcat(context->url, file);
498 if (!url) return IE_NOMEM;
500 /* Build SOAP request envelope */
501 request_soap_doc = xmlNewDoc(BAD_CAST "1.0");
502 if (!request_soap_doc) {
503 isds_log_message(context, _("Could not build SOAP request document"));
504 err = IE_ERROR;
505 goto leave;
507 request_soap_envelope = xmlNewNode(NULL, BAD_CAST "Envelope");
508 if (!request_soap_envelope) {
509 isds_log_message(context, _("Could not build SOAP request envelope"));
510 err = IE_ERROR;
511 goto leave;
513 xmlDocSetRootElement(request_soap_doc, request_soap_envelope);
514 /* Only this way we get namespace definition as @xmlns:soap,
515 * otherwise we get namespace prefix without definition */
516 soap_ns = xmlNewNs(request_soap_envelope, BAD_CAST SOAP_NS, NULL);
517 if(!soap_ns) {
518 isds_log_message(context, _("Could not create SOAP name space"));
519 err = IE_ERROR;
520 goto leave;
522 xmlSetNs(request_soap_envelope, soap_ns);
523 request_soap_body = xmlNewChild(request_soap_envelope, NULL,
524 BAD_CAST "Body", NULL);
525 if (!request_soap_body) {
526 isds_log_message(context,
527 _("Could not add Body to SOAP request envelope"));
528 err = IE_ERROR;
529 goto leave;
532 /* Append request XML node set to SOAP body if request is not empty */
533 /* XXX: Copy of request must be used, otherwise xmlFreeDoc(request_soap_doc)
534 * would destroy this outer structure. */
535 if (request) {
536 xmlNodePtr request_copy = xmlCopyNodeList(request);
537 if (!request_copy) {
538 isds_log_message(context,
539 _("Could not copy request content"));
540 err = IE_ERROR;
541 goto leave;
543 if (!xmlAddChildList(request_soap_body, request_copy)) {
544 xmlFreeNodeList(request_copy);
545 isds_log_message(context,
546 _("Could not add request content to SOAP "
547 "request envelope"));
548 err = IE_ERROR;
549 goto leave;
554 /* Serialize the SOAP request into HTTP request body */
555 http_request = xmlBufferCreate();
556 if (!http_request) {
557 isds_log_message(context,
558 _("Could not create xmlBuffer for HTTP request body"));
559 err = IE_ERROR;
560 goto leave;
562 /* Last argument 1 means format the XML tree. This is pretty but it breaks
563 * digital signatures probably because ISDS abandoned XMLDSig */
564 save_ctx = xmlSaveToBuffer(http_request, "UTF-8", 1);
565 if (!save_ctx) {
566 isds_log_message(context,
567 _("Could not create XML serializer"));
568 err = IE_ERROR;
569 goto leave;
571 /* XXX: According LibXML documentation, this function does not return
572 * meaningfull value yet */
573 xmlSaveDoc(save_ctx, request_soap_doc);
574 if (-1 == xmlSaveFlush(save_ctx)) {
575 isds_log_message(context,
576 _("Could not serialize SOAP request to HTTP request bddy"));
577 err = IE_ERROR;
578 goto leave;
581 isds_log(ILF_SOAP, ILL_DEBUG,
582 _("SOAP request to sent to %s:\n%.*s\nEnd of SOAP request\n"),
583 url, http_request->use, http_request->content);
585 err = http(context, url, http_request->content, http_request->use,
586 &http_response, &response_length,
587 &mime_type, NULL, &http_code);
589 /* TODO: HTTP binding for SOAP prescribes non-200 HTTP return codes
590 * to be processes too. */
592 if (err) {
593 goto leave;
596 /* Check for HTTP return code */
597 isds_log(ILF_SOAP, ILL_DEBUG, _("Server returned %ld HTTP code\n"),
598 http_code);
599 switch (http_code) {
600 /* XXX: We must see which code is used for not permitted ISDS
601 * operation like downloading message without proper user
602 * permissions. In that cat we should keep connection opened. */
603 case 401:
604 err = IE_NOT_LOGGED_IN;
605 isds_log_message(context, _("Authentication failed"));
606 goto leave;
607 break;
608 case 404:
609 err = IE_HTTP;
610 isds_printf_message(context,
611 _("Code 404: Document (%s) not found on server"), url);
612 goto leave;
613 break;
614 /* 500 should return standard SOAP message */
617 /* Check for Content-Type: text/xml.
618 * Do it after HTTP code check because 401 Unauthorized returns HTML web
619 * page for browsers. */
620 if (mime_type && strcmp(mime_type, "text/xml")
621 && strcmp(mime_type, "application/soap+xml")
622 && strcmp(mime_type, "application/xml")) {
623 char *mime_type_locale = utf82locale(mime_type);
624 isds_printf_message(context,
625 _("%s: bad MIME type sent by server: %s"), url,
626 mime_type_locale);
627 free(mime_type_locale);
628 err = IE_SOAP;
629 goto leave;
632 /* TODO: Convert returned body into XML default encoding */
634 /* Parse the HTTP body as XML */
635 response_soap_doc = xmlParseMemory(http_response, response_length);
636 if (!response_soap_doc) {
637 err = IE_XML;
638 goto leave;
641 xpath_ctx = xmlXPathNewContext(response_soap_doc);
642 if (!xpath_ctx) {
643 err = IE_ERROR;
644 goto leave;
647 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
648 err = IE_ERROR;
649 goto leave;
652 isds_log(ILF_SOAP, ILL_DEBUG,
653 _("SOAP response received:\n%.*s\nEnd of SOAP response\n"),
654 response_length, http_response);
657 /* Check for SOAP version */
658 response_root = xmlDocGetRootElement(response_soap_doc);
659 if (!response_root) {
660 isds_log_message(context, "SOAP response has no root element");
661 err = IE_SOAP;
662 goto leave;
664 if (xmlStrcmp(response_root->name, BAD_CAST "Envelope") ||
665 xmlStrcmp(response_root->ns->href, BAD_CAST SOAP_NS)) {
666 isds_log_message(context, "SOAP response is not SOAP 1.1 document");
667 err = IE_SOAP;
668 goto leave;
671 /* Check for SOAP Headers */
672 response_soap_headers = xmlXPathEvalExpression(
673 BAD_CAST "/soap:Envelope/soap:Header/"
674 "*[@soap:mustUnderstand/text() = true()]", xpath_ctx);
675 if (!response_soap_headers) {
676 err = IE_ERROR;
677 goto leave;
679 if (!xmlXPathNodeSetIsEmpty(response_soap_headers->nodesetval)) {
680 isds_log_message(context,
681 _("SOAP response requires unsupported feature"));
682 /* TODO: log the headers
683 * xmlChar *fragment = NULL;
684 * fragment = xmlXPathCastNodeSetToSting(response_soap_headers->nodesetval);*/
685 err = IE_NOTSUP;
686 goto leave;
689 /* Get SOAP Body */
690 response_soap_body = xmlXPathEvalExpression(
691 BAD_CAST "/soap:Envelope/soap:Body", xpath_ctx);
692 if (!response_soap_body) {
693 err = IE_ERROR;
694 goto leave;
696 if (xmlXPathNodeSetIsEmpty(response_soap_body->nodesetval)) {
697 isds_log_message(context,
698 _("SOAP response does not contain SOAP Body element"));
699 err = IE_SOAP;
700 goto leave;
702 if (response_soap_body->nodesetval->nodeNr > 1) {
703 isds_log_message(context,
704 _("SOAP response has more than one Body element"));
705 err = IE_SOAP;
706 goto leave;
709 /* Check for SOAP Fault */
710 response_soap_fault = xmlXPathEvalExpression(
711 BAD_CAST "/soap:Envelope/soap:Body/soap:Fault", xpath_ctx);
712 if (!response_soap_fault) {
713 err = IE_ERROR;
714 goto leave;
716 if (!xmlXPathNodeSetIsEmpty(response_soap_fault->nodesetval)) {
717 /* TODO: log the faultcode and faultstring */
718 isds_log_message(context, _("SOAP response signals Fault"));
719 err = IE_SOAP;
720 goto leave;
724 /* Extract XML Tree with ISDS response from SOAP envelope and return it.
725 * XXX: response_soap_body is Body, we need children which may not exist
726 * (i.e. empty Body). */
727 /* TODO: Destroy SOAP response but Body childern. This is more memory
728 * friendly than copying (potentialy) fat body */
729 if (response_soap_body->nodesetval->nodeTab[0]->children) {
730 *response = xmlDocCopyNodeList(response_soap_doc,
731 response_soap_body->nodesetval->nodeTab[0]->children);
732 if (!*response) {
733 err = IE_NOMEM;
734 goto leave;
736 } else *response = NULL;
738 /* Save raw response */
739 if (raw_response) {
740 *raw_response = http_response;
741 *raw_response_length = response_length;
742 http_response = NULL;
746 leave:
747 if (err) {
748 xmlFreeNodeList(*response);
749 *response = NULL;
752 xmlXPathFreeObject(response_soap_fault);
753 xmlXPathFreeObject(response_soap_body);
754 xmlXPathFreeObject(response_soap_headers);
755 xmlXPathFreeContext(xpath_ctx);
756 xmlFreeDoc(response_soap_doc);
757 free(mime_type);
758 free(http_response);
759 xmlSaveClose(save_ctx);
760 xmlBufferFree(http_request);
761 xmlFreeDoc(request_soap_doc); /* recursive, frees request_body, soap_ns*/
762 free(url);
764 return err;
768 /* LibXML functions:
770 * void xmlInitParser(void)
771 * Initialization function for the XML parser. This is not reentrant. Call
772 * once before processing in case of use in multithreaded programs.
774 * int xmlInitParserCtxt(xmlParserCtxtPtr ctxt)
775 * Initialize a parser context
777 * xmlDocPtr xmlCtxtReadDoc(xmlParserCtxtPtr ctxt, const xmlChar * cur,
778 * const * char URL, const char * encoding, int options);
779 * Parse in-memory NULL-terminated document @cur.
781 * xmlDocPtr xmlParseMemory(const char * buffer, int size)
782 * Parse an XML in-memory block and build a tree.
784 * xmlParserCtxtPtr xmlCreateMemoryParserCtxt(const char * buffer, int
785 * size);
786 * Create a parser context for an XML in-memory document.
788 * xmlParserCtxtPtr xmlCreateDocParserCtxt(const xmlChar * cur)
789 * Creates a parser context for an XML in-memory document.
791 * xmlDocPtr xmlCtxtReadMemory(xmlParserCtxtPtr ctxt,
792 * const char * buffer, int size, const char * URL, const char * encoding,
793 * int options)
794 * Parse an XML in-memory document and build a tree. This reuses the existing
795 * @ctxt parser context.
797 * void xmlCleanupParser(void)
798 * Cleanup function for the XML library. It tries to reclaim all parsing
799 * related glob document related memory. Calling this function should not
800 * prevent reusing the libr finished using the library or XML document built
801 * with it.
803 * void xmlClearParserCtxt(xmlParserCtxtPtr ctxt)
804 * Clear (release owned resources) and reinitialize a parser context.
806 * void xmlCtxtReset(xmlParserCtxtPtr ctxt)
807 * Reset a parser context
809 * void xmlFreeParserCtxt(xmlParserCtxtPtr ctxt)
810 * Free all the memory used by a parser context. However the parsed document
811 * in ctxt->myDoc is not freed.
813 * void xmlFreeDoc(xmlDocPtr cur)
814 * Free up all the structures used by a document, tree included.