1 #define _XOPEN_SOURCE 500 /* strdup from string.h */
8 /* Private structure for write_body() call back */
15 /* CURL call back function called when chunk of HTTP reponse body is available.
16 * @buffer points to new data
17 * @size * @nmemb is length of the chunk in bytes. Zero means empty body.
18 * @userp is private structure.
19 * Must reuturn the length of the chunk, otherwise CURL will signal
20 * CURL_WRITE_ERROR. */
21 static size_t write_body(void *buffer
, size_t size
, size_t nmemb
, void *userp
) {
22 struct soap_body
*body
= (struct soap_body
*) userp
;
25 /* FIXME: Check for (size * nmemb + body->lengt) !> SIZE_T_MAX.
26 * Precompute the product then. */
28 if (!body
) return 0; /* This should never happen */
29 if (0 == (size
* nmemb
)) return 0; /* Empty body */
31 new_data
= realloc(body
->data
, body
->length
+ size
* nmemb
);
32 if (!new_data
) return 0;
34 memcpy(new_data
+ body
->length
, buffer
, size
* nmemb
);
36 body
->data
= new_data
;
37 body
->length
+= size
* nmemb
;
39 return (size
* nmemb
);
44 * @context holds the base URL,
45 * @url is a (CGI) file of SOAP URL,
46 * @request is body for POST request
47 * @request_length is length of @request in bytes
48 * @reponse is automatically reallocated() buffer to fit HTTP response with
49 * @response_length (does not need to match allocatef memory exactly). You must
50 * free() the @response.
51 * @mime_type is automatically allocated MIME type send by server (*NULL if not
52 * sent). Set NULL if you don't care.
53 * @charset is charset of the body signaled by server. The same constrains
54 * like on @mime_type apply.
55 * In case of error, the response memory, MIME type, charset and lenght will be
56 * deallocated and zerod automatically. Thus be sure they are preallocated or
57 * they points to NULL.
58 * Side effect: message buffer */
59 static isds_error
http(struct isds_ctx
*context
, const char *url
,
60 const void *request
, const size_t request_length
,
61 void **response
, size_t *response_length
,
62 char **mime_type
, char**charset
) {
65 isds_error err
= IE_SUCCESS
;
66 struct soap_body body
;
68 struct curl_slist
*headers
= NULL
;
71 if (!context
) return IE_INVALID_CONTEXT
;
72 if (!url
) return IE_INVAL
;
73 if (request_length
> 0 && !request
) return IE_INVAL
;
74 if (!response
|| !response_length
) return IE_INVAL
;
76 /* Set the body here to allow deallocataion in leave block */
77 body
.data
= *response
;
81 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_URL
, url
);
82 if (!curl_err
&& context
->username
) {
83 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_USERNAME
,
88 if (!curl_err
&& context
->password
) {
89 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_PASSWORD
,
93 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_FAILONERROR
, 1);
96 /* Set get-response function */
98 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_WRITEFUNCTION
,
102 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_WRITEDATA
, &body
);
105 /* Set MIME types and user agent identification */
107 headers
= curl_slist_append(headers
, "Accept: application/soap+xml");
112 headers
= curl_slist_append(headers
, "Content-Type: application/soap+xml");
117 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_HTTPHEADER
, headers
);
120 /* TODO: Present library version, curl etc. in User-Agent */
121 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_USERAGENT
, "libisds");
124 /* Set POST request body */
126 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_POST
, 1);
129 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_POSTFIELDS
, request
);
132 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_POSTFIELDSIZE
,
136 /* Check for errors so far */
138 isds_log_message(context
, curl_easy_strerror(curl_err
));
144 curl_err
= curl_easy_perform(context
->curl
);
147 curl_err
= curl_easy_getinfo(context
->curl
, CURLINFO_CONTENT_TYPE
,
151 isds_log_message(context
, url
);
152 isds_append_message(context
, _(": "));
153 isds_append_message(context
, curl_easy_strerror(curl_err
));
158 /* Extract MIME type and charset */
163 sep
= strchr(content_type
, ';');
164 if (sep
) offset
= (size_t) (sep
- content_type
);
165 else offset
= strlen(content_type
);
168 *mime_type
= malloc(offset
+ 1);
173 memcpy(*mime_type
, content_type
, offset
);
174 (*mime_type
)[offset
] = '\0';
181 sep
= strstr(sep
, "charset=");
185 *charset
= strdup(sep
+ 8);
212 curl_easy_cleanup(context
->curl
);
213 context
->curl
= NULL
;
216 *response
= body
.data
;
217 *response_length
= body
.length
;
224 * @context holds the base URL,
225 * @file is a (CGI) file of SOAP URL,
226 * @request is XML node set with SOAP request body.
227 * @file must be NULL, @request should be NULL rather than empty, if they should
228 * not be signaled in the SOAP request.
229 * @reponse is automatically allocated() node set with SOAP response body.
230 * You must xmlFreeNodeList() it. This is literal body, empty (NULL), one node
231 * or more nodes can be returned.
232 * In case of error the response will be deallocated automatically.
233 * Side effect: message buffer */
234 _hidden isds_error
soap(struct isds_ctx
*context
, const char *file
,
235 const xmlNodePtr request
, xmlNodePtr
*response
) {
237 isds_error err
= IE_SUCCESS
;
239 char *mime_type
= NULL
;
240 xmlBufferPtr http_request
= NULL
;
241 xmlSaveCtxtPtr save_ctx
= NULL
;
242 xmlDocPtr request_soap_doc
= NULL
;
243 xmlNodePtr request_soap_envelope
= NULL
, request_soap_body
= NULL
;
244 xmlNsPtr soap_ns
= NULL
;
245 void *http_response
= NULL
;
246 size_t response_length
= 0;
247 xmlDocPtr response_soap_doc
= NULL
;
248 xmlXPathContextPtr xpath_ctx
= NULL
;
249 xmlXPathObjectPtr response_soap_headers
= NULL
, response_soap_body
= NULL
;
252 if (!context
) return IE_INVALID_CONTEXT
;
253 if (!response
) return IE_INVAL
;
255 xmlFreeNodeList(*response
);
258 url
= astrcat(context
->url
, file
);
259 if (!url
) return IE_NOMEM
;
261 /* Build SOAP request envelope */
262 request_soap_doc
= xmlNewDoc(BAD_CAST
"1.0");
263 if (!request_soap_doc
) {
264 isds_log_message(context
, _("Could not build SOAP request document"));
268 request_soap_envelope
= xmlNewNode(NULL
, BAD_CAST
"Envelope");
269 if (!request_soap_envelope
) {
270 isds_log_message(context
, _("Could not build SOAP request envelope"));
274 xmlDocSetRootElement(request_soap_doc
, request_soap_envelope
);
275 /* Only this way we get namespace definition as @xmlns:soap,
276 * otherwise we get namespace prefix without definition */
277 soap_ns
= xmlNewNs(request_soap_envelope
, BAD_CAST SOAP_NS
, NULL
);
279 isds_log_message(context
, _("Could not create SOAP name space"));
283 xmlSetNs(request_soap_envelope
, soap_ns
);
284 request_soap_body
= xmlNewChild(request_soap_envelope
, NULL
,
285 BAD_CAST
"Body", NULL
);
286 if (!request_soap_body
) {
287 isds_log_message(context
, _("Could not add Body to SOAP request envelope"));
292 /* Append request XML node set to SOAP body if request is not empty */
293 /* XXX: Copy of request must be used, otherwise xmlFreeDoc(request_soap_doc)
294 * would destroy this outer structure. */
296 xmlNodePtr request_copy
= xmlCopyNodeList(request
);
298 isds_log_message(context
,
299 _("Could not copy request content"));
303 if (!xmlAddChildList(request_soap_body
, request_copy
)) {
304 xmlFreeNodeList(request_copy
);
305 isds_log_message(context
,
306 _("Could not add request content to SOAP request envelope"));
313 /* Serialize the SOAP request into HTTP request body */
314 http_request
= xmlBufferCreate();
316 isds_log_message(context
,
317 _("Could not create xmlBuffer for HTTP request body"));
321 /* Last argument 1 means format the XML tree. This is pretty but it breaks
322 * digital signatures probably because ISDS abadoned XMLSec */
323 save_ctx
= xmlSaveToBuffer(http_request
, "UTF-8", 1);
325 isds_log_message(context
,
326 _("Could not create XML serializer"));
330 /* XXX: According LibXML documentation, this function does not return
331 * meaningfull value yet */
332 xmlSaveDoc(save_ctx
, request_soap_doc
);
333 if (-1 == xmlSaveFlush(save_ctx
)) {
334 isds_log_message(context
,
335 _("Could not serialize SOAP request to HTTP request bddy"));
341 err
= http(context
, url
, http_request
->content
, http_request
->use
,
342 &http_response
, &response_length
,
345 /* TODO: HTTP binding for SOAP prescribes non-200 HTTP return codes
346 * to be processes too. See SOAP Part 2, Table 16. */
352 /* Check for Content-Type: application/soap+xml */
353 if (mime_type
&& strcmp(mime_type
, "application/soap+xml")
354 && strcmp(mime_type
, "application/xml")
355 && strcmp(mime_type
, "text/xml")) {
356 isds_log_message(context
, url
);
357 isds_append_message(context
, _(": bad MIME type sent by server: "));
358 isds_append_message(context
, mime_type
);
363 /* TODO: Convert returned body into XML default encoding */
365 /* Parse the HTTP body as XML */
366 response_soap_doc
= xmlParseMemory(http_response
, response_length
);
367 if (!response_soap_doc
) {
372 xpath_ctx
= xmlXPathNewContext(response_soap_doc
);
378 if (register_namespaces(xpath_ctx
)) {
383 /* Check for SOAP requirements */
384 response_soap_headers
= xmlXPathEvalExpression(
385 BAD_CAST
"/soap:Envelope/soap:Header/"
386 "*[@soap:mustUnderstand/text() = true()]", xpath_ctx
);
387 if (!response_soap_headers
) {
391 if (response_soap_headers
->nodesetval
) {
392 isds_log_message(context
, "SOAP response requires unsupported feature");
393 /* TODO: log the headers
394 * xmlChar *fragment = NULL;
395 * fragment = xmlXPathCastNodeSetToSting(response_soap_headers->nodesetval);*/
400 response_soap_body
= xmlXPathEvalExpression(
401 BAD_CAST
"/soap:Envelope/soap:Body", xpath_ctx
);
402 if (!response_soap_body
) {
406 if (!(response_soap_body
->nodesetval
)) {
407 isds_log_message(context
, "SOAP response does not contain Body element");
411 if (response_soap_body
->nodesetval
->nodeNr
> 1) {
412 isds_log_message(context
, "SOAP body has more than Body element");
418 /* Extract XML Tree with ISDS response from SOAP envelope and return it */
419 *response
= xmlDocCopyNodeList(response_soap_doc
,
420 response_soap_body
->nodesetval
->nodeTab
[0]);
430 xmlFreeNodeList(*response
);
434 xmlXPathFreeObject(response_soap_body
);
435 xmlXPathFreeObject(response_soap_headers
);
436 xmlXPathFreeContext(xpath_ctx
);
437 xmlFreeDoc(response_soap_doc
);
440 xmlFreeDoc(request_soap_doc
); /* recursive, frees request_body, soap_ns*/
441 xmlBufferFree(http_request
);
442 xmlSaveClose(save_ctx
);
451 * void xmlInitParser(void)
452 * Initialization function for the XML parser. This is not reentrant. Call
453 * once before processing in case of use in multithreaded programs.
455 * int xmlInitParserCtxt(xmlParserCtxtPtr ctxt)
456 * Initialize a parser context
458 * xmlDocPtr xmlCtxtReadDoc(xmlParserCtxtPtr ctxt, const xmlChar * cur,
459 * const * char URL, const char * encoding, int options);
460 * Parse in-memory NULL-terminated document @cur.
462 * xmlDocPtr xmlParseMemory(const char * buffer, int size)
463 * Parse an XML in-memory block and build a tree.
465 * xmlParserCtxtPtr xmlCreateMemoryParserCtxt(const char * buffer, int
467 * Create a parser context for an XML in-memory document.
469 * xmlParserCtxtPtr xmlCreateDocParserCtxt(const xmlChar * cur)
470 * Creates a parser context for an XML in-memory document.
472 * xmlDocPtr xmlCtxtReadMemory(xmlParserCtxtPtr ctxt,
473 * const char * buffer, int size, const char * URL, const char * encoding,
475 * Parse an XML in-memory document and build a tree. This reuses the existing
476 * @ctxt parser context.
478 * void xmlCleanupParser(void)
479 * Cleanup function for the XML library. It tries to reclaim all parsing
480 * related glob document related memory. Calling this function should not
481 * prevent reusing the libr finished using the library or XML document built
484 * void xmlClearParserCtxt(xmlParserCtxtPtr ctxt)
485 * Clear (release owned resources) and reinitialize a parser context.
487 * void xmlCtxtReset(xmlParserCtxtPtr ctxt)
488 * Reset a parser context
490 * void xmlFreeParserCtxt(xmlParserCtxtPtr ctxt)
491 * Free all the memory used by a parser context. However the parsed document
492 * in ctxt->myDoc is not freed.
494 * void xmlFreeDoc(xmlDocPtr cur)
495 * Free up all the structures used by a document, tree included.