1 #define _XOPEN_SOURCE 500 /* For strdup(3) */
2 #include "../test-tools.h"
6 #include <libxml/parser.h>
7 #include <libxml/xpath.h>
8 #include <libxml/xpathInternals.h>
9 #include <libxml/xmlsave.h>
11 static const char *soap_mime_type
= "text/xml"; /* SOAP/1.1 requires text/xml */
13 /* Used to choose proper name space for message elements.
14 * See _isds_register_namespaces(). */
18 MESSAGE_NS_SIGNED_INCOMING
,
19 MESSAGE_NS_SIGNED_OUTGOING
,
20 MESSAGE_NS_SIGNED_DELIVERY
23 #define SOAP_NS "http://schemas.xmlsoap.org/soap/envelope/"
24 #define SOAP2_NS "http://www.w3.org/2003/05/soap-envelope"
25 #define ISDS1_NS "http://isds.czechpoint.cz"
26 #define ISDS_NS "http://isds.czechpoint.cz/v20"
27 #define SISDS_INCOMING_NS "http://isds.czechpoint.cz/v20/message"
28 #define SISDS_OUTGOING_NS "http://isds.czechpoint.cz/v20/SentMessage"
29 #define SISDS_DELIVERY_NS "http://isds.czechpoint.cz/v20/delivery"
30 #define SCHEMA_NS "http://www.w3.org/2001/XMLSchema"
31 #define DEPOSIT_NS "urn:uschovnaWSDL"
36 const char *end_point
;
38 http_error (*function
) (int, xmlDocPtr
, xmlXPathContextPtr
, xmlNodePtr
,
39 xmlDocPtr
, xmlNodePtr
, const void *arguments
);
42 /* Following EXTRACT_* macros expect @xpath_ctx, @message, and leave label */
43 #define EXTRACT_STRING(element, string) { \
44 xmlXPathObjectPtr result = NULL; \
45 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
46 if (NULL == result) { \
47 error = HTTP_ERROR_SERVER; \
50 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
51 if (result->nodesetval->nodeNr > 1) { \
52 xmlXPathFreeObject(result); \
53 test_asprintf(&message, "Multiple %s element", element); \
54 error = HTTP_ERROR_CLIENT; \
58 xmlXPathCastNodeSetToString(result->nodesetval); \
60 xmlXPathFreeObject(result); \
61 error = HTTP_ERROR_SERVER; \
65 xmlXPathFreeObject(result); \
68 /* Following INSERT_* macros expect @error and leave label */
69 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
71 xmlNodePtr node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
72 (xmlChar *) (string)); \
74 error = HTTP_ERROR_SERVER; \
79 #define INSERT_STRING(parent, element, string) \
80 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
82 #define INSERT_ELEMENT(child, parent, element) \
84 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
85 if (NULL == (child)) { \
86 error = HTTP_ERROR_SERVER; \
91 /* Insert dmStatus or similar subtree
92 * @parent is element to insert to
93 * @dm is true for dmStatus, otherwise dbStatus
94 * @code is stautus code as string
95 * @message is UTF-8 encoded message
96 * @db_ref_number is optinal reference number propagated if not @dm
97 * @return 0 on success, otherwise non-0. */
98 static http_error
insert_isds_status(xmlNodePtr parent
, _Bool dm
,
99 const xmlChar
*code
, const xmlChar
*message
,
100 const xmlChar
*db_ref_number
) {
101 http_error error
= HTTP_ERROR_SUCCESS
;
104 if (NULL
== code
|| NULL
== message
) {
105 error
= HTTP_ERROR_SERVER
;
109 INSERT_ELEMENT(status
, parent
, (dm
) ? "dmStatus" : "dbStatus");
110 INSERT_STRING(status
, (dm
) ? "dmStatusCode" : "dbStatusCode", code
);
111 INSERT_STRING(status
, (dm
) ? "dmStatusMessage" : "dbStatusMessage", message
);
112 if (!dm
&& NULL
!= db_ref_number
) {
113 INSERT_STRING(status
, "dbStatusRefNumber", db_ref_number
);
121 /* Implement DummyOperation */
122 static http_error
service_DummyOperation(int socket
, const xmlDocPtr soap_request
,
123 xmlXPathContextPtr xpath_ctx
, xmlNodePtr isds_request
,
124 xmlDocPtr soap_response
, xmlNodePtr isds_response
,
125 const void *arguments
) {
126 return insert_isds_status(isds_response
, 1, BAD_CAST
"0000",
127 BAD_CAST
"Success", NULL
);
131 /* Implement ChangeISDSPassword.
132 * @arguments is current password as const char * */
133 static http_error
service_ChangeISDSPassword(int socket
,
134 const xmlDocPtr soap_request
, xmlXPathContextPtr xpath_ctx
,
135 const xmlNodePtr isds_request
,
136 xmlDocPtr soap_response
, xmlNodePtr isds_response
,
137 const void *arguments
) {
138 http_error error
= HTTP_ERROR_SUCCESS
;
139 char *message
= NULL
;
140 const char *current_password
= (const char *)arguments
;
141 char *old_password
= NULL
, *new_password
= NULL
;
143 if (NULL
== current_password
) {
144 error
= HTTP_ERROR_SERVER
;
149 EXTRACT_STRING("isds:dbOldPassword", old_password
);
150 if (NULL
== old_password
) {
151 message
= strdup("Empty isds:dbOldPassword");
152 error
= HTTP_ERROR_CLIENT
;
155 EXTRACT_STRING("isds:dbNewPassword", new_password
);
156 if (NULL
== new_password
) {
157 message
= strdup("Empty isds:dbOldPassword");
158 error
= HTTP_ERROR_CLIENT
;
162 /* Check defined cases */
163 if (strcmp(current_password
, old_password
)) {
164 error
= insert_isds_status(isds_response
, 0, BAD_CAST
"1090",
165 BAD_CAST
"Bad current password", NULL
);
166 if (error
== HTTP_ERROR_SUCCESS
) error
= HTTP_ERROR_CLIENT
;
168 error
= insert_isds_status(isds_response
, 0, BAD_CAST
"0000",
169 BAD_CAST
"Success", NULL
);
173 if (HTTP_ERROR_CLIENT
== error
) {
174 http_error next_error
= insert_isds_status(isds_response
, 0,
175 BAD_CAST
"9999", BAD_CAST message
, NULL
);
176 if (HTTP_ERROR_SUCCESS
!= next_error
) error
= next_error
;
186 /* List of implemented services */
187 static struct service services
[] = {
188 { SERVICE_DS_Dz_DummyOperation
,
189 "DS/dz", BAD_CAST
"DummyOperation",
190 service_DummyOperation
},
191 { SERVICE_DS_DsManage_ChangeISDSPassword
,
192 "DS/DsManage", BAD_CAST
"ChangeISDSPassword",
193 service_ChangeISDSPassword
},
197 /* Makes known all relevant namespaces to given XPath context
198 * @xpath_ctx is XPath context
199 * @message_ns selects proper message name space. Unsigned and signed
200 * messages and delivery info's differ in prefix and URI.
201 * @return 0 in success, otherwise not 0. */
202 static int register_namespaces(xmlXPathContextPtr xpath_ctx
,
203 const message_ns_type message_ns
) {
204 const xmlChar
*message_namespace
= NULL
;
206 if (!xpath_ctx
) return -1;
210 message_namespace
= BAD_CAST ISDS1_NS
; break;
211 case MESSAGE_NS_UNSIGNED
:
212 message_namespace
= BAD_CAST ISDS_NS
; break;
213 case MESSAGE_NS_SIGNED_INCOMING
:
214 message_namespace
= BAD_CAST SISDS_INCOMING_NS
; break;
215 case MESSAGE_NS_SIGNED_OUTGOING
:
216 message_namespace
= BAD_CAST SISDS_OUTGOING_NS
; break;
217 case MESSAGE_NS_SIGNED_DELIVERY
:
218 message_namespace
= BAD_CAST SISDS_DELIVERY_NS
; break;
223 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"soap", BAD_CAST SOAP_NS
))
225 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"isds", BAD_CAST ISDS_NS
))
227 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"sisds", message_namespace
))
229 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"xs", BAD_CAST SCHEMA_NS
))
231 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"deposit", BAD_CAST DEPOSIT_NS
))
237 /* Parse soap request, pass it to service endpoint and respond to it.
238 * It sends final HTTP response. */
239 void soap(int socket
, const struct service_configuration
*configuration
,
240 const void *request
, size_t request_length
, const char *end_point
) {
241 xmlDocPtr request_doc
= NULL
;
242 xmlXPathContextPtr xpath_ctx
= NULL
;
243 xmlXPathObjectPtr request_soap_body
= NULL
;
244 xmlNodePtr isds_request
= NULL
; /* pointer only */
245 _Bool service_handled
= 0, service_passed
= 0;
246 xmlDocPtr response_doc
= NULL
;
247 xmlNodePtr response_soap_envelope
= NULL
, response_soap_body
= NULL
,
248 isds_response
= NULL
;
249 xmlNsPtr soap_ns
= NULL
, isds_ns
= NULL
;
250 char *response_name
= NULL
;
251 xmlBufferPtr http_response_body
= NULL
;
252 xmlSaveCtxtPtr save_ctx
= NULL
;
255 if (NULL
== configuration
) {
256 http_send_response_500(socket
, "Second argument of soap() is NULL");
260 if (NULL
== request
|| request_length
== 0) {
261 http_send_response_400(socket
, "Client sent empty body");
265 request_doc
= xmlParseMemory(request
, request_length
);
266 if (NULL
== request_doc
) {
267 http_send_response_400(socket
, "Client sent invalid XML document");
271 xpath_ctx
= xmlXPathNewContext(request_doc
);
272 if (NULL
== xpath_ctx
) {
273 xmlFreeDoc(request_doc
);
274 http_send_response_500(socket
, "Could not create XPath context");
278 if (register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
279 xmlXPathFreeContext(xpath_ctx
);
280 xmlFreeDoc(request_doc
);
281 http_send_response_500(socket
,
282 "Could not register name spaces to the XPath context");
287 request_soap_body
= xmlXPathEvalExpression(
288 BAD_CAST
"/soap:Envelope/soap:Body", xpath_ctx
);
289 if (NULL
== request_soap_body
) {
290 xmlXPathFreeContext(xpath_ctx
);
291 xmlFreeDoc(request_doc
);
292 http_send_response_400(socket
, "Client sent invalid SOAP request");
295 if (xmlXPathNodeSetIsEmpty(request_soap_body
->nodesetval
)) {
296 xmlXPathFreeObject(request_soap_body
);
297 xmlXPathFreeContext(xpath_ctx
);
298 xmlFreeDoc(request_doc
);
299 http_send_response_400(socket
,
300 "SOAP request does not contain SOAP Body element");
303 if (request_soap_body
->nodesetval
->nodeNr
> 1) {
304 xmlXPathFreeObject(request_soap_body
);
305 xmlXPathFreeContext(xpath_ctx
);
306 xmlFreeDoc(request_doc
);
307 http_send_response_400(socket
,
308 "SOAP response has more than one Body element");
311 isds_request
= request_soap_body
->nodesetval
->nodeTab
[0]->children
;
312 if (isds_request
->next
!= NULL
) {
313 xmlXPathFreeObject(request_soap_body
);
314 xmlXPathFreeContext(xpath_ctx
);
315 xmlFreeDoc(request_doc
);
316 http_send_response_400(socket
, "SOAP body has more than one child");
319 if (isds_request
->type
!= XML_ELEMENT_NODE
|| isds_request
->ns
== NULL
||
320 xmlStrcmp(isds_request
->ns
->href
, BAD_CAST ISDS_NS
)) {
321 xmlXPathFreeObject(request_soap_body
);
322 xmlXPathFreeContext(xpath_ctx
);
323 xmlFreeDoc(request_doc
);
324 http_send_response_400(socket
,
325 "SOAP body does not contain an ISDS elment");
329 /* Build SOAP response envelope */
330 response_doc
= xmlNewDoc(BAD_CAST
"1.0");
332 http_send_response_500(socket
, "Could not build SOAP response document");
335 response_soap_envelope
= xmlNewNode(NULL
, BAD_CAST
"Envelope");
336 if (!response_soap_envelope
) {
337 http_send_response_500(socket
, "Could not build SOAP response envelope");
340 xmlDocSetRootElement(response_doc
, response_soap_envelope
);
341 /* Only this way we get namespace definition as @xmlns:soap,
342 * otherwise we get namespace prefix without definition */
343 soap_ns
= xmlNewNs(response_soap_envelope
, BAD_CAST SOAP_NS
, NULL
);
344 if(NULL
== soap_ns
) {
345 http_send_response_500(socket
, "Could not create SOAP name space");
348 xmlSetNs(response_soap_envelope
, soap_ns
);
349 response_soap_body
= xmlNewChild(response_soap_envelope
, NULL
,
350 BAD_CAST
"Body", NULL
);
351 if (!response_soap_body
) {
352 http_send_response_500(socket
,
353 "Could not add Body to SOAP response envelope");
356 /* Append ISDS response element */
357 if (-1 == test_asprintf(&response_name
, "%s%s", isds_request
->name
,
359 http_send_response_500(socket
,
360 "Could not buld ISDS resposne element name");
363 isds_response
= xmlNewChild(response_soap_body
, NULL
,
364 BAD_CAST response_name
, NULL
);
366 if (NULL
== isds_response
) {
367 http_send_response_500(socket
,
368 "Could not add ISDS response element to SOAP response body");
371 isds_ns
= xmlNewNs(isds_response
, BAD_CAST ISDS_NS
, NULL
);
372 if(NULL
== isds_ns
) {
373 http_send_response_500(socket
, "Could not create ISDS name space");
376 xmlSetNs(isds_response
, isds_ns
);
378 /* Dispatch request to service */
379 for (int i
= 0; i
< sizeof(services
)/sizeof(services
[0]); i
++) {
380 if (!strcmp(services
[i
].end_point
, end_point
) &&
381 !xmlStrcmp(services
[i
].name
, isds_request
->name
)) {
382 /* Check if the configuration is enabled and find configuration */
383 for (const struct service_configuration
*service
= configuration
;
384 service
->name
!= SERVICE_END
; service
++) {
385 if (service
->name
== services
[i
].id
) {
387 xpath_ctx
->node
= isds_request
;
388 if (HTTP_ERROR_SERVER
!= services
[i
].function(socket
,
389 request_doc
, xpath_ctx
, isds_request
,
390 response_doc
, isds_response
,
391 service
->arguments
)) {
394 http_send_response_500(socket
,
395 "Internal server error while processing "
405 if (service_passed
) {
406 /* Serialize the SOAP response */
407 http_response_body
= xmlBufferCreate();
408 if (NULL
== http_response_body
) {
409 http_send_response_500(socket
,
410 "Could not create xmlBuffer for response serialization");
413 /* Last argument 1 means format the XML tree. This is pretty but it breaks
414 * XML document transport as it adds text nodes (indentiation) between
416 save_ctx
= xmlSaveToBuffer(http_response_body
, "UTF-8", 0);
417 if (NULL
== save_ctx
) {
418 http_send_response_500(socket
, "Could not create XML serializer");
421 /* XXX: According LibXML documentation, this function does not return
422 * meaningful value yet */
423 xmlSaveDoc(save_ctx
, response_doc
);
424 if (-1 == xmlSaveFlush(save_ctx
)) {
425 http_send_response_500(socket
,
426 "Could not serialize SOAP response");
430 http_send_response_200(socket
, http_response_body
->content
,
431 http_response_body
->use
, soap_mime_type
);
435 xmlSaveClose(save_ctx
);
436 xmlBufferFree(http_response_body
);
438 xmlFreeDoc(response_doc
);
440 xmlXPathFreeObject(request_soap_body
);
441 xmlXPathFreeContext(xpath_ctx
);
442 xmlFreeDoc(request_doc
);
444 if (!service_handled
) {
445 http_send_response_500(socket
,
446 "Requested ISDS service not implemented");