1 #define _XOPEN_SOURCE 500 /* strdup from string.h */
8 /* Private structure for write_body() call back */
15 /* Close connection to server and destroy CURL handle associated
17 _hidden isds_error
close_connection(struct isds_ctx
*context
) {
18 if (!context
) return IE_INVALID_CONTEXT
;
21 curl_easy_cleanup(context
->curl
);
23 isds_log(ILF_HTTP
, ILL_DEBUG
, _("Connection to server %s closed\n"),
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 return 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
;
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
;
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
);
73 isds_log(ILF_HTTP
, ILL_INFO
,
74 _("Application aborted HTTP transfer"));
82 /* CURL call back function called when curl has something to log.
83 * @curl is cURL context
84 * @type is cURL log facility
85 * @buffer points to log data, XXX: not zero-terminated
86 * @size is length of log data
87 * @userp is private structure.
89 static int log_curl(CURL
*curl
, curl_infotype type
, char *buffer
, size_t size
,
91 if (!buffer
|| 0 == size
) return 0;
92 if (type
== CURLINFO_TEXT
|| type
== CURLINFO_HEADER_IN
||
93 type
== CURLINFO_HEADER_OUT
)
94 isds_log(ILF_HTTP
, ILL_DEBUG
, "%*s", size
, buffer
);
100 * @context holds the base URL,
101 * @url is a (CGI) file of SOAP URL,
102 * @request is body for POST request
103 * @request_length is length of @request in bytes
104 * @reponse is automatically reallocated() buffer to fit HTTP response with
105 * @response_length (does not need to match allocatef memory exactly). You must
106 * free() the @response.
107 * @mime_type is automatically allocated MIME type send by server (*NULL if not
108 * sent). Set NULL if you don't care.
109 * @charset is charset of the body signaled by server. The same constrains
110 * like on @mime_type apply.
111 * @http_code is final HTTP code returned by server. This can be 200, 401, 500
112 * or any other one. Pass NULL if you don't interrest.
113 * In case of error, the response memory, MIME type, charset and lenght will be
114 * deallocated and zerod automatically. Thus be sure they are preallocated or
115 * they points to NULL.
116 * Be ware that successful return value does not mean the HTTP request has
117 * been accepted by the server. You must cosult @http_code. OTOH, failure
118 * return value means the request could not been sent (e.g. SSL error).
119 * Side effect: message buffer */
120 static isds_error
http(struct isds_ctx
*context
, const char *url
,
121 const void *request
, const size_t request_length
,
122 void **response
, size_t *response_length
,
123 char **mime_type
, char **charset
, long *http_code
) {
126 isds_error err
= IE_SUCCESS
;
127 struct soap_body body
;
129 struct curl_slist
*headers
= NULL
;
132 if (!context
) return IE_INVALID_CONTEXT
;
133 if (!url
) return IE_INVAL
;
134 if (request_length
> 0 && !request
) return IE_INVAL
;
135 if (!response
|| !response_length
) return IE_INVAL
;
137 /* Set the body here to allow deallocatation in leave block */
138 body
.data
= *response
;
141 /* Set Request-URI */
142 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_URL
, url
);
144 /* Set TLS options */
145 if (!curl_err
&& context
->tls_verify_server
) {
146 if (!*context
->tls_verify_server
)
147 isds_log(ILF_SEC
, ILL_WARNING
,
148 _("Disabling server identity verification. "
149 "That was your decision.\n"));
150 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_SSL_VERIFYPEER
,
151 (*context
->tls_verify_server
)? 1L : 0L);
153 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_SSL_VERIFYHOST
,
154 (*context
->tls_verify_server
)? 2L : 0L);
157 if (!curl_err
&& context
->tls_ca_file
) {
158 isds_log(ILF_SEC
, ILL_INFO
,
159 _("CA certificates will be searched in `%s' file since now\n"),
160 context
->tls_ca_file
);
161 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_CAINFO
,
162 context
->tls_ca_file
);
164 if (!curl_err
&& context
->tls_ca_dir
) {
165 isds_log(ILF_SEC
, ILL_INFO
,
166 _("CA certificates will be searched in `%s' directory "
167 "since now\n"), context
->tls_ca_dir
);
168 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_CAPATH
,
169 context
->tls_ca_dir
);
171 if (!curl_err
&& context
->tls_crl_file
) {
172 #if HAVE_DECL_CURLOPT_CRLFILE /* Since curl-7.19.0 */
173 isds_log(ILF_SEC
, ILL_INFO
,
174 _("CRLs will be searched in `%s' file since now\n"),
175 context
->tls_crl_file
);
176 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_CRLFILE
,
177 context
->tls_crl_file
);
179 isds_log(ILF_SEC
, ILL_WARNING
,
180 _("Your curl library cannot pass certificate revocation "
181 "list to cryptographic library.\n"
182 "Make sure cryptographic library default setting "
183 "delivers proper CRLs,\n"
184 "or upgrade curl.\n"));
185 #endif /* not HAVE_DECL_CURLOPT_CRLFILE */
189 /* Set credentials */
190 #if HAVE_DECL_CURLOPT_USERNAME /* Since curl-7.19.1 */
191 if (!curl_err
&& context
->username
) {
192 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_USERNAME
,
195 if (!curl_err
&& context
->password
) {
196 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_PASSWORD
,
200 if (!curl_err
&& (context
->username
|| context
->password
)) {
201 char *userpwd
= astrcat3(context
->username
, ":", context
->password
);
203 isds_log_message(context
, _("Could not pass credentials to CURL"));
207 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_USERPWD
, userpwd
);
210 #endif /* not HAVE_DECL_CURLOPT_USERNAME */
212 /* Set PKI credentials */
213 if (!curl_err
&& (context
->pki_credentials
)) {
214 if (context
->pki_credentials
->engine
) {
215 /* Select SSL engine */
216 isds_log(ILF_SEC
, ILL_INFO
,
217 _("Cryptograhic engine `%s' will be used for "
218 "key or certificate\n"),
219 context
->pki_credentials
->engine
);
220 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_SSLENGINE
,
221 context
->pki_credentials
->engine
);
225 /* Select certificate format */
226 #if HAVE_DECL_CURLOPT_SSLCERTTYPE /* since curl-7.9.3 */
227 if (context
->pki_credentials
->certificate_format
==
229 /* XXX: It's valid to have certificate in engine without name.
230 * Engines can select certificate according private key and
232 if (context
->pki_credentials
->certificate
)
233 isds_log(ILF_SEC
, ILL_INFO
, _("Client `%s' certificate "
234 "will be read from `%s' engine\n"),
235 context
->pki_credentials
->certificate
,
236 context
->pki_credentials
->engine
);
238 isds_log(ILF_SEC
, ILL_INFO
, _("Client certificate "
239 "will be read from `%s' engine\n"),
240 context
->pki_credentials
->engine
);
241 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_SSLCERTTYPE
,
243 } else if (context
->pki_credentials
->certificate
) {
244 isds_log(ILF_SEC
, ILL_INFO
, _("Client %s certificate "
245 "will be read from `%s' file\n"),
246 (context
->pki_credentials
->certificate_format
==
247 PKI_FORMAT_DER
) ? _("DER") : _("PEM"),
248 context
->pki_credentials
->certificate
);
249 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_SSLCERTTYPE
,
250 (context
->pki_credentials
->certificate_format
==
251 PKI_FORMAT_DER
) ? "DER" : "PEM");
254 if ((context
->pki_credentials
->certificate_format
==
256 context
->pki_credentials
->certificate
))
257 isds_log(ILF_SEC
, ILL_WARNING
,
258 _("Your curl library cannot distinguish certifcate "
259 "formats. Make sure your cryptographic library\n"
260 "understands your certificate file by default, "
261 "or upgrade curl.\n"));
262 #endif /* not HAVE_DECL_CURLOPT_SSLCERTTYPE */
265 if (!curl_err
&& context
->pki_credentials
->certificate
) {
266 /* Select certificate */
268 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_SSLCERT
,
269 context
->pki_credentials
->certificate
);
273 /* Select key format */
274 if (context
->pki_credentials
->key_format
== PKI_FORMAT_ENG
) {
275 if (context
->pki_credentials
->key
)
276 isds_log(ILF_SEC
, ILL_INFO
, _("Client private key `%s' "
277 "from `%s' engine will be used\n"),
278 context
->pki_credentials
->key
,
279 context
->pki_credentials
->engine
);
281 isds_log(ILF_SEC
, ILL_INFO
, _("Client private key "
282 "from `%s' engine will be used\n"),
283 context
->pki_credentials
->engine
);
284 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_SSLKEYTYPE
,
286 } else if (context
->pki_credentials
->key
) {
287 isds_log(ILF_SEC
, ILL_INFO
, _("Client %s private key will be "
288 "read from `%s' file\n"),
289 (context
->pki_credentials
->key_format
==
290 PKI_FORMAT_DER
) ? _("DER") : _("PEM"),
291 context
->pki_credentials
->key
);
292 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_SSLKEYTYPE
,
293 (context
->pki_credentials
->key_format
==
294 PKI_FORMAT_DER
) ? "DER" : "PEM");
299 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_SSLKEY
,
300 context
->pki_credentials
->key
);
303 /* Pass key passphrase */
304 #if HAVE_DECL_CURLOPT_KEYPASSWD /* since curl-7.16.5 */
305 curl_err
= curl_easy_setopt(context
->curl
,
307 context
->pki_credentials
->passphrase
);
308 #elif HAVE_DECL_CURLOPT_SSLKEYPASSWD /* up to curl-7.16.4 */
309 curl_err
= curl_easy_setopt(context
->curl
,
310 CURLOPT_SSLKEYPASSWD
,
311 context
->pki_credentials
->passphrase
);
312 #else /* up to curl-7.9.2 */
313 curl_err
= curl_easy_setopt(context
->curl
,
314 CURLOPT_SSLCERTPASSWD
,
315 context
->pki_credentials
->passphrase
);
323 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_NOSIGNAL
, 1);
325 if (!curl_err
&& context
->timeout
) {
326 #if HAVE_DECL_CURLOPT_TIMEOUT_MS /* Since curl-7.16.2 */
327 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_TIMEOUT_MS
,
330 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_TIMEOUT
,
331 context
->timeout
/ 1000);
332 #endif /* not HAVE_DECL_CURLOPT_TIMEOUT_MS */
335 /* Register callback */
336 if (context
->progress_callback
) {
338 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_NOPROGRESS
, 0);
341 curl_err
= curl_easy_setopt(context
->curl
,
342 CURLOPT_PROGRESSFUNCTION
, progress_proxy
);
345 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_PROGRESSDATA
,
350 /* Set other CURL features */
352 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_FAILONERROR
, 0);
355 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_FOLLOWLOCATION
, 1);
358 /* TODO: Make the redirect depth configurable */
359 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_MAXREDIRS
, 8);
362 curl_err
= curl_easy_setopt(context
->curl
,
363 CURLOPT_UNRESTRICTED_AUTH
, 1);
366 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_COOKIEFILE
, "");
369 /* Set get-response function */
371 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_WRITEFUNCTION
,
375 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_WRITEDATA
, &body
);
378 /* Set MIME types and headers requires by SOAP 1.1.
379 * SOAP 1.1 requires text/xml, SOAP 1.2 requires application/soap+xml */
381 headers
= curl_slist_append(headers
,
382 "Accept: application/soap+xml,application/xml,text/xml");
387 headers
= curl_slist_append(headers
, "Content-Type: text/xml");
392 headers
= curl_slist_append(headers
, "SOAPAction: ");
397 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_HTTPHEADER
, headers
);
400 /* Set user agent identification */
401 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_USERAGENT
,
402 "libisds/" PACKAGE_VERSION
);
405 /* Set POST request body */
407 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_POST
, 1);
410 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_POSTFIELDS
, request
);
413 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_POSTFIELDSIZE
,
417 /* Check for errors so far */
419 isds_log_message(context
, curl_easy_strerror(curl_err
));
424 isds_log(ILF_HTTP
, ILL_DEBUG
, _("Sending POST request to %s\n"), url
);
425 isds_log(ILF_HTTP
, ILL_DEBUG
,
426 _("POST body length: %zu, content follows:\n"), request_length
);
427 isds_log(ILF_HTTP
, ILL_DEBUG
, "%.*s\n", request_length
, request
);
428 isds_log(ILF_HTTP
, ILL_DEBUG
, _("End of POST body\n"));
429 if ((log_facilities
& ILF_HTTP
) && (log_level
>= ILL_DEBUG
) ) {
430 curl_easy_setopt(context
->curl
, CURLOPT_VERBOSE
, 1);
431 curl_easy_setopt(context
->curl
, CURLOPT_DEBUGFUNCTION
, log_curl
);
433 curl_easy_setopt(context
->curl
, CURLOPT_VERBOSE
, 0);
434 curl_easy_setopt(context
->curl
, CURLOPT_DEBUGFUNCTION
, NULL
);
439 curl_err
= curl_easy_perform(context
->curl
);
441 /* Wipe credentials out of the handler */
442 #if HAVE_DECL_CURLOPT_USERNAME /* Since curl-7.19.1 */
443 if (context
->username
) {
444 curl_easy_setopt(context
->curl
, CURLOPT_USERNAME
, NULL
);
446 if (context
->password
) {
447 curl_easy_setopt(context
->curl
, CURLOPT_PASSWORD
, NULL
);
450 if (context
->username
|| context
->password
) {
451 curl_easy_setopt(context
->curl
, CURLOPT_USERPWD
, NULL
);
453 #endif /* not HAVE_DECL_CURLOPT_USERNAME */
456 curl_err
= curl_easy_getinfo(context
->curl
, CURLINFO_CONTENT_TYPE
,
460 /* TODO: CURL is not internationalized yet. Collect CURL messages for
462 isds_printf_message(context
,
463 _("%s: %s"), url
, _(curl_easy_strerror(curl_err
)));
464 if (curl_err
== CURLE_ABORTED_BY_CALLBACK
)
471 isds_log(ILF_HTTP
, ILL_DEBUG
, _("Final response to %s received\n"), url
);
472 isds_log(ILF_HTTP
, ILL_DEBUG
,
473 _("Response body length: %zu, content follows:\n"),
475 isds_log(ILF_HTTP
, ILL_DEBUG
, "%.*s\n", body
.length
, body
.data
);
476 isds_log(ILF_HTTP
, ILL_DEBUG
, _("End of response body\n"));
479 /* Extract MIME type and charset */
484 sep
= strchr(content_type
, ';');
485 if (sep
) offset
= (size_t) (sep
- content_type
);
486 else offset
= strlen(content_type
);
489 *mime_type
= malloc(offset
+ 1);
494 memcpy(*mime_type
, content_type
, offset
);
495 (*mime_type
)[offset
] = '\0';
502 sep
= strstr(sep
, "charset=");
506 *charset
= strdup(sep
+ 8);
516 /* Get HTTP response code */
518 curl_err
= curl_easy_getinfo(context
->curl
,
519 CURLINFO_RESPONSE_CODE
, http_code
);
527 curl_slist_free_all(headers
);
543 if (err
!= IE_ABORTED
) close_connection(context
);
546 *response
= body
.data
;
547 *response_length
= body
.length
;
554 * @context holds the base URL,
555 * @file is a (CGI) file of SOAP URL,
556 * @request is XML node set with SOAP request body.
557 * @file must be NULL, @request should be NULL rather than empty, if they should
558 * not be signaled in the SOAP request.
559 * @reponse is automatically allocated() node set with SOAP response body.
560 * You must xmlFreeNodeList() it. This is literal body, empty (NULL), one node
561 * or more nodes can be returned.
562 * @raw_response is automatically allocated bitstream with response body. Use
563 * NULL if you don't care
564 * @raw_response_length is size of @raw_response in bytes
565 * In case of error the response will be deallocated automatically.
566 * Side effect: message buffer */
567 _hidden isds_error
soap(struct isds_ctx
*context
, const char *file
,
568 const xmlNodePtr request
, xmlNodePtr
*response
,
569 void **raw_response
, size_t *raw_response_length
) {
571 isds_error err
= IE_SUCCESS
;
573 char *mime_type
= NULL
;
575 xmlBufferPtr http_request
= NULL
;
576 xmlSaveCtxtPtr save_ctx
= NULL
;
577 xmlDocPtr request_soap_doc
= NULL
;
578 xmlNodePtr request_soap_envelope
= NULL
, request_soap_body
= NULL
;
579 xmlNsPtr soap_ns
= NULL
;
580 void *http_response
= NULL
;
581 size_t response_length
= 0;
582 xmlDocPtr response_soap_doc
= NULL
;
583 xmlNodePtr response_root
= NULL
;
584 xmlXPathContextPtr xpath_ctx
= NULL
;
585 xmlXPathObjectPtr response_soap_headers
= NULL
, response_soap_body
= NULL
,
586 response_soap_fault
= NULL
;
589 if (!context
) return IE_INVALID_CONTEXT
;
590 if (!response
) return IE_INVAL
;
591 if (!raw_response_length
&& raw_response
) return IE_INVAL
;
593 xmlFreeNodeList(*response
);
595 if (raw_response
) *raw_response
= NULL
;
597 url
= astrcat(context
->url
, file
);
598 if (!url
) return IE_NOMEM
;
600 /* Build SOAP request envelope */
601 request_soap_doc
= xmlNewDoc(BAD_CAST
"1.0");
602 if (!request_soap_doc
) {
603 isds_log_message(context
, _("Could not build SOAP request document"));
607 request_soap_envelope
= xmlNewNode(NULL
, BAD_CAST
"Envelope");
608 if (!request_soap_envelope
) {
609 isds_log_message(context
, _("Could not build SOAP request envelope"));
613 xmlDocSetRootElement(request_soap_doc
, request_soap_envelope
);
614 /* Only this way we get namespace definition as @xmlns:soap,
615 * otherwise we get namespace prefix without definition */
616 soap_ns
= xmlNewNs(request_soap_envelope
, BAD_CAST SOAP_NS
, NULL
);
618 isds_log_message(context
, _("Could not create SOAP name space"));
622 xmlSetNs(request_soap_envelope
, soap_ns
);
623 request_soap_body
= xmlNewChild(request_soap_envelope
, NULL
,
624 BAD_CAST
"Body", NULL
);
625 if (!request_soap_body
) {
626 isds_log_message(context
,
627 _("Could not add Body to SOAP request envelope"));
632 /* Append request XML node set to SOAP body if request is not empty */
633 /* XXX: Copy of request must be used, otherwise xmlFreeDoc(request_soap_doc)
634 * would destroy this outer structure. */
636 xmlNodePtr request_copy
= xmlCopyNodeList(request
);
638 isds_log_message(context
,
639 _("Could not copy request content"));
643 if (!xmlAddChildList(request_soap_body
, request_copy
)) {
644 xmlFreeNodeList(request_copy
);
645 isds_log_message(context
,
646 _("Could not add request content to SOAP "
647 "request envelope"));
654 /* Serialize the SOAP request into HTTP request body */
655 http_request
= xmlBufferCreate();
657 isds_log_message(context
,
658 _("Could not create xmlBuffer for HTTP request body"));
662 /* Last argument 1 means format the XML tree. This is pretty but it breaks
663 * digital signatures probably because ISDS abandoned XMLDSig */
664 save_ctx
= xmlSaveToBuffer(http_request
, "UTF-8", 1);
666 isds_log_message(context
,
667 _("Could not create XML serializer"));
671 /* XXX: According LibXML documentation, this function does not return
672 * meaningfull value yet */
673 xmlSaveDoc(save_ctx
, request_soap_doc
);
674 if (-1 == xmlSaveFlush(save_ctx
)) {
675 isds_log_message(context
,
676 _("Could not serialize SOAP request to HTTP request bddy"));
681 isds_log(ILF_SOAP
, ILL_DEBUG
,
682 _("SOAP request to sent to %s:\n%.*s\nEnd of SOAP request\n"),
683 url
, http_request
->use
, http_request
->content
);
685 err
= http(context
, url
, http_request
->content
, http_request
->use
,
686 &http_response
, &response_length
,
687 &mime_type
, NULL
, &http_code
);
689 /* TODO: HTTP binding for SOAP prescribes non-200 HTTP return codes
690 * to be processes too. */
696 /* Check for HTTP return code */
697 isds_log(ILF_SOAP
, ILL_DEBUG
, _("Server returned %ld HTTP code\n"),
700 /* XXX: We must see which code is used for not permitted ISDS
701 * operation like downloading message without proper user
702 * permissions. In that cat we should keep connection opened. */
704 err
= IE_NOT_LOGGED_IN
;
705 isds_log_message(context
, _("Authentication failed"));
710 isds_printf_message(context
,
711 _("Code 404: Document (%s) not found on server"), url
);
714 /* 500 should return standard SOAP message */
717 /* Check for Content-Type: text/xml.
718 * Do it after HTTP code check because 401 Unauthorized returns HTML web
719 * page for browsers. */
720 if (mime_type
&& strcmp(mime_type
, "text/xml")
721 && strcmp(mime_type
, "application/soap+xml")
722 && strcmp(mime_type
, "application/xml")) {
723 char *mime_type_locale
= utf82locale(mime_type
);
724 isds_printf_message(context
,
725 _("%s: bad MIME type sent by server: %s"), url
,
727 free(mime_type_locale
);
732 /* TODO: Convert returned body into XML default encoding */
734 /* Parse the HTTP body as XML */
735 response_soap_doc
= xmlParseMemory(http_response
, response_length
);
736 if (!response_soap_doc
) {
741 xpath_ctx
= xmlXPathNewContext(response_soap_doc
);
747 if (register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
752 isds_log(ILF_SOAP
, ILL_DEBUG
,
753 _("SOAP response received:\n%.*s\nEnd of SOAP response\n"),
754 response_length
, http_response
);
757 /* Check for SOAP version */
758 response_root
= xmlDocGetRootElement(response_soap_doc
);
759 if (!response_root
) {
760 isds_log_message(context
, "SOAP response has no root element");
764 if (xmlStrcmp(response_root
->name
, BAD_CAST
"Envelope") ||
765 xmlStrcmp(response_root
->ns
->href
, BAD_CAST SOAP_NS
)) {
766 isds_log_message(context
, "SOAP response is not SOAP 1.1 document");
771 /* Check for SOAP Headers */
772 response_soap_headers
= xmlXPathEvalExpression(
773 BAD_CAST
"/soap:Envelope/soap:Header/"
774 "*[@soap:mustUnderstand/text() = true()]", xpath_ctx
);
775 if (!response_soap_headers
) {
779 if (!xmlXPathNodeSetIsEmpty(response_soap_headers
->nodesetval
)) {
780 isds_log_message(context
,
781 _("SOAP response requires unsupported feature"));
782 /* TODO: log the headers
783 * xmlChar *fragment = NULL;
784 * fragment = xmlXPathCastNodeSetToSting(response_soap_headers->nodesetval);*/
790 response_soap_body
= xmlXPathEvalExpression(
791 BAD_CAST
"/soap:Envelope/soap:Body", xpath_ctx
);
792 if (!response_soap_body
) {
796 if (xmlXPathNodeSetIsEmpty(response_soap_body
->nodesetval
)) {
797 isds_log_message(context
,
798 _("SOAP response does not contain SOAP Body element"));
802 if (response_soap_body
->nodesetval
->nodeNr
> 1) {
803 isds_log_message(context
,
804 _("SOAP response has more than one Body element"));
809 /* Check for SOAP Fault */
810 response_soap_fault
= xmlXPathEvalExpression(
811 BAD_CAST
"/soap:Envelope/soap:Body/soap:Fault", xpath_ctx
);
812 if (!response_soap_fault
) {
816 if (!xmlXPathNodeSetIsEmpty(response_soap_fault
->nodesetval
)) {
817 /* TODO: log the faultcode and faultstring */
818 isds_log_message(context
, _("SOAP response signals Fault"));
824 /* Extract XML Tree with ISDS response from SOAP envelope and return it.
825 * XXX: response_soap_body is Body, we need children which may not exist
826 * (i.e. empty Body). */
827 /* TODO: Destroy SOAP response but Body childern. This is more memory
828 * friendly than copying (potentialy) fat body */
829 if (response_soap_body
->nodesetval
->nodeTab
[0]->children
) {
830 *response
= xmlDocCopyNodeList(response_soap_doc
,
831 response_soap_body
->nodesetval
->nodeTab
[0]->children
);
836 } else *response
= NULL
;
838 /* Save raw response */
840 *raw_response
= http_response
;
841 *raw_response_length
= response_length
;
842 http_response
= NULL
;
848 xmlFreeNodeList(*response
);
852 xmlXPathFreeObject(response_soap_fault
);
853 xmlXPathFreeObject(response_soap_body
);
854 xmlXPathFreeObject(response_soap_headers
);
855 xmlXPathFreeContext(xpath_ctx
);
856 xmlFreeDoc(response_soap_doc
);
859 xmlSaveClose(save_ctx
);
860 xmlBufferFree(http_request
);
861 xmlFreeDoc(request_soap_doc
); /* recursive, frees request_body, soap_ns*/
870 * void xmlInitParser(void)
871 * Initialization function for the XML parser. This is not reentrant. Call
872 * once before processing in case of use in multithreaded programs.
874 * int xmlInitParserCtxt(xmlParserCtxtPtr ctxt)
875 * Initialize a parser context
877 * xmlDocPtr xmlCtxtReadDoc(xmlParserCtxtPtr ctxt, const xmlChar * cur,
878 * const * char URL, const char * encoding, int options);
879 * Parse in-memory NULL-terminated document @cur.
881 * xmlDocPtr xmlParseMemory(const char * buffer, int size)
882 * Parse an XML in-memory block and build a tree.
884 * xmlParserCtxtPtr xmlCreateMemoryParserCtxt(const char * buffer, int
886 * Create a parser context for an XML in-memory document.
888 * xmlParserCtxtPtr xmlCreateDocParserCtxt(const xmlChar * cur)
889 * Creates a parser context for an XML in-memory document.
891 * xmlDocPtr xmlCtxtReadMemory(xmlParserCtxtPtr ctxt,
892 * const char * buffer, int size, const char * URL, const char * encoding,
894 * Parse an XML in-memory document and build a tree. This reuses the existing
895 * @ctxt parser context.
897 * void xmlCleanupParser(void)
898 * Cleanup function for the XML library. It tries to reclaim all parsing
899 * related glob document related memory. Calling this function should not
900 * prevent reusing the libr finished using the library or XML document built
903 * void xmlClearParserCtxt(xmlParserCtxtPtr ctxt)
904 * Clear (release owned resources) and reinitialize a parser context.
906 * void xmlCtxtReset(xmlParserCtxtPtr ctxt)
907 * Reset a parser context
909 * void xmlFreeParserCtxt(xmlParserCtxtPtr ctxt)
910 * Free all the memory used by a parser context. However the parsed document
911 * in ctxt->myDoc is not freed.
913 * void xmlFreeDoc(xmlDocPtr cur)
914 * Free up all the structures used by a document, tree included.