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
,
24 #define SOAP_NS "http://schemas.xmlsoap.org/soap/envelope/"
25 #define SOAP2_NS "http://www.w3.org/2003/05/soap-envelope"
26 #define ISDS1_NS "http://isds.czechpoint.cz"
27 #define ISDS_NS "http://isds.czechpoint.cz/v20"
28 #define OISDS_NS "http://isds.czechpoint.cz/v20/asws"
29 #define SISDS_INCOMING_NS "http://isds.czechpoint.cz/v20/message"
30 #define SISDS_OUTGOING_NS "http://isds.czechpoint.cz/v20/SentMessage"
31 #define SISDS_DELIVERY_NS "http://isds.czechpoint.cz/v20/delivery"
32 #define SCHEMA_NS "http://www.w3.org/2001/XMLSchema"
33 #define DEPOSIT_NS "urn:uschovnaWSDL"
38 const char *end_point
;
39 const xmlChar
*name_space
;
41 http_error (*function
) (const struct http_connection
*, xmlDocPtr
,
42 xmlXPathContextPtr
, xmlNodePtr
, xmlDocPtr
, xmlNodePtr
,
43 const void *arguments
);
46 /* Following EXTRACT_* macros expect @xpath_ctx, @message, and leave label */
47 #define EXTRACT_STRING(element, string) { \
48 xmlXPathObjectPtr result = NULL; \
49 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
50 if (NULL == result) { \
51 error = HTTP_ERROR_SERVER; \
54 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
55 if (result->nodesetval->nodeNr > 1) { \
56 xmlXPathFreeObject(result); \
57 test_asprintf(&message, "Multiple %s element", element); \
58 error = HTTP_ERROR_CLIENT; \
62 xmlXPathCastNodeSetToString(result->nodesetval); \
64 xmlXPathFreeObject(result); \
65 error = HTTP_ERROR_SERVER; \
69 xmlXPathFreeObject(result); \
72 #define EXTRACT_BOOLEAN(element, booleanPtr) { \
73 char *string = NULL; \
74 EXTRACT_STRING(element, string); \
76 if (NULL != string) { \
77 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
78 if (NULL == (booleanPtr)) { \
80 error = HTTP_ERROR_SERVER; \
84 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
85 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
87 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
88 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
91 test_asprintf(&message, \
92 "%s value is not valid boolean: %s", \
95 error = HTTP_ERROR_CLIENT; \
104 /* Following INSERT_* macros expect @error and leave label */
105 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
107 xmlNodePtr node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
108 (xmlChar *) (string)); \
109 if (NULL == node) { \
110 error = HTTP_ERROR_SERVER; \
115 #define INSERT_STRING(parent, element, string) \
116 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
118 #define INSERT_ELEMENT(child, parent, element) \
120 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
121 if (NULL == (child)) { \
122 error = HTTP_ERROR_SERVER; \
127 /* Insert dmStatus or similar subtree
128 * @parent is element to insert to
129 * @dm is true for dmStatus, otherwise dbStatus
130 * @code is status code as string
131 * @message is UTF-8 encoded message
132 * @db_ref_number is optinal reference number propagated if not @dm
133 * @return 0 on success, otherwise non-0. */
134 static http_error
insert_isds_status(xmlNodePtr parent
, _Bool dm
,
135 const xmlChar
*code
, const xmlChar
*message
,
136 const xmlChar
*db_ref_number
) {
137 http_error error
= HTTP_ERROR_SUCCESS
;
140 if (NULL
== code
|| NULL
== message
) {
141 error
= HTTP_ERROR_SERVER
;
145 INSERT_ELEMENT(status
, parent
, (dm
) ? "dmStatus" : "dbStatus");
146 INSERT_STRING(status
, (dm
) ? "dmStatusCode" : "dbStatusCode", code
);
147 INSERT_STRING(status
, (dm
) ? "dmStatusMessage" : "dbStatusMessage", message
);
148 if (!dm
&& NULL
!= db_ref_number
) {
149 INSERT_STRING(status
, "dbStatusRefNumber", db_ref_number
);
157 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
158 static http_error
tm2datestring(const struct tm
*time
, char **string
) {
159 if (NULL
== time
|| NULL
== string
) return HTTP_ERROR_SERVER
;
161 if (-1 == test_asprintf(string
, "%d-%02d-%02d",
162 time
->tm_year
+ 1900, time
->tm_mon
+ 1, time
->tm_mday
))
163 return HTTP_ERROR_SERVER
;
165 return HTTP_ERROR_SUCCESS
;
169 /* Implement DummyOperation */
170 static http_error
service_DummyOperation(
171 const struct http_connection
*connection
, const xmlDocPtr soap_request
,
172 xmlXPathContextPtr xpath_ctx
, xmlNodePtr isds_request
,
173 xmlDocPtr soap_response
, xmlNodePtr isds_response
,
174 const void *arguments
) {
175 return insert_isds_status(isds_response
, 1, BAD_CAST
"0000",
176 BAD_CAST
"Success", NULL
);
180 /* Implement Re-signISDSDocument.
181 * It sends document from request back.
182 * @arguments is pointer to struct arguments_DS_Dz_ResignISDSDocument */
183 static http_error
service_ResignISDSDocument(
184 const struct http_connection
*connection
,
185 const xmlDocPtr soap_request
, xmlXPathContextPtr xpath_ctx
,
186 const xmlNodePtr isds_request
,
187 xmlDocPtr soap_response
, xmlNodePtr isds_response
,
188 const void *arguments
) {
189 http_error error
= HTTP_ERROR_SUCCESS
;
190 const char *code
= "9999";
191 char *message
= NULL
;
192 const struct arguments_DS_Dz_ResignISDSDocument
*configuration
=
193 (const struct arguments_DS_Dz_ResignISDSDocument
*)arguments
;
196 if (NULL
== configuration
|| NULL
== configuration
->status_code
||
197 NULL
== configuration
->status_message
) {
198 error
= HTTP_ERROR_SERVER
;
202 EXTRACT_STRING("isds:dmDoc", data
);
204 message
= strdup("Missing isds:dmDoc");
205 error
= HTTP_ERROR_CLIENT
;
210 /* dmResultDoc is mandatory in response */
211 if (xmlStrcmp(BAD_CAST configuration
->status_code
, BAD_CAST
"0000")) {
215 INSERT_STRING(isds_response
, "dmResultDoc", data
);
217 if (configuration
->valid_to
!= NULL
) {
218 error
= tm2datestring(configuration
->valid_to
, &data
);
220 message
= strdup("Could not format date");
223 INSERT_STRING(isds_response
, "dmValidTo", data
);
226 code
= configuration
->status_code
;
227 message
= strdup(configuration
->status_message
);
230 if (HTTP_ERROR_SERVER
!= error
) {
231 http_error next_error
= insert_isds_status(isds_response
, 1,
232 BAD_CAST code
, BAD_CAST message
, NULL
);
233 if (HTTP_ERROR_SUCCESS
!= next_error
) error
= next_error
;
241 /* Implement EraseMessage.
242 * @arguments is pointer to struct arguments_DS_DsManage_ChangeISDSPassword */
243 static http_error
service_EraseMessage(const struct http_connection
*connection
,
244 const xmlDocPtr soap_request
, xmlXPathContextPtr xpath_ctx
,
245 const xmlNodePtr isds_request
,
246 xmlDocPtr soap_response
, xmlNodePtr isds_response
,
247 const void *arguments
) {
248 http_error error
= HTTP_ERROR_SUCCESS
;
249 char *code
= "9999", *message
= NULL
;
250 const struct arguments_DS_Dx_EraseMessage
*configuration
=
251 (const struct arguments_DS_Dx_EraseMessage
*)arguments
;
252 char *message_id
= NULL
;
253 _Bool
*incoming
= NULL
;
255 if (NULL
== configuration
|| NULL
== configuration
->message_id
) {
256 error
= HTTP_ERROR_SERVER
;
260 EXTRACT_STRING("isds:dmID", message_id
);
261 if (NULL
== message_id
) {
262 message
= strdup("Missing isds:dmID");
263 error
= HTTP_ERROR_CLIENT
;
266 EXTRACT_BOOLEAN("isds:dmIncoming", incoming
);
267 if (NULL
== incoming
) {
268 message
= strdup("Missing isds:dmIncoming");
269 error
= HTTP_ERROR_CLIENT
;
273 if (xmlStrcmp((const xmlChar
*) configuration
->message_id
,
274 (const xmlChar
*) message_id
)) {
276 message
= strdup("Message is not in the long term storage");
277 error
= HTTP_ERROR_CLIENT
;
280 if (configuration
->incoming
!= *incoming
) {
282 message
= strdup("Message direction mismatches");
283 error
= HTTP_ERROR_CLIENT
;
288 message
= strdup("Success");
290 if (HTTP_ERROR_SERVER
!= error
) {
291 http_error next_error
= insert_isds_status(isds_response
, 1,
292 BAD_CAST code
, BAD_CAST message
, NULL
);
293 if (HTTP_ERROR_SUCCESS
!= next_error
) error
= next_error
;
302 /* Common part for ChangeISDSPassword and ChangePasswordOTP.
303 * @code is output pointer to static string
304 * @pass_message is output pointer to auto-allocated string
305 * @arguments is pointer to struct arguments_DS_DsManage_ChangeISDSPassword */
306 static http_error
check_passwd(
307 const char *username
, const char *current_password
,
308 xmlXPathContextPtr xpath_ctx
,
309 char **code
, char **pass_message
) {
310 http_error error
= HTTP_ERROR_SUCCESS
;
311 char *message
= NULL
;
312 char *old_password
= NULL
, *new_password
= NULL
;
315 if (NULL
== username
|| NULL
== current_password
||
316 NULL
== code
|| NULL
== pass_message
) {
317 error
= HTTP_ERROR_SERVER
;
325 EXTRACT_STRING("isds:dbOldPassword", old_password
);
326 if (NULL
== old_password
) {
327 message
= strdup("Empty isds:dbOldPassword");
328 error
= HTTP_ERROR_CLIENT
;
331 EXTRACT_STRING("isds:dbNewPassword", new_password
);
332 if (NULL
== new_password
) {
333 message
= strdup("Empty isds:dbOldPassword");
334 error
= HTTP_ERROR_CLIENT
;
338 /* Check defined cases */
339 if (strcmp(current_password
, old_password
)) {
341 message
= strdup("Bad current password");
342 error
= HTTP_ERROR_CLIENT
;
346 length
= strlen(new_password
);
348 if (length
< 8 || length
> 32) {
350 message
= strdup("Too short or too long");
351 error
= HTTP_ERROR_CLIENT
;
356 const char lower
[] = "abcdefghijklmnopqrstuvwxyz";
357 const char upper
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
358 const char digit
[] = "0123456789";
359 const char special
[] = "!#$%&()*+,-.:=?@[]_{}|~";
360 _Bool has_lower
= 0, has_upper
= 0, has_digit
=0;
362 for (int i
= 0; i
< length
; i
++) {
363 if (NULL
!= strchr(lower
, new_password
[i
]))
365 else if (NULL
!= strchr(upper
, new_password
[i
]))
367 else if (NULL
!= strchr(digit
, new_password
[i
]))
369 else if (NULL
== strchr(special
, new_password
[i
])) {
371 message
= strdup("Password contains forbidden character");
372 error
= HTTP_ERROR_CLIENT
;
377 if (!has_lower
|| !has_upper
|| !has_digit
) {
379 message
= strdup("Password does not contain lower cased letter, "
380 "upper cased letter and a digit");
381 error
= HTTP_ERROR_CLIENT
;
386 if (!strcmp(old_password
, new_password
)) {
388 message
= strdup("New password same as current one");
389 error
= HTTP_ERROR_CLIENT
;
393 if (NULL
!= strstr(new_password
, username
)) {
395 message
= strdup("New password contains user ID");
396 error
= HTTP_ERROR_CLIENT
;
400 for (int i
= 0; i
< length
- 2; i
++) {
401 if (new_password
[i
] == new_password
[i
+1] &&
402 new_password
[i
] == new_password
[i
+2]) {
404 message
= strdup("Password contains sequence "
405 "of three identical characters");
406 error
= HTTP_ERROR_CLIENT
;
412 const char *forbidden_prefix
[] = { "qwert", "asdgf", "12345" };
413 for (int i
= 0; i
< sizeof(forbidden_prefix
)/sizeof(*forbidden_prefix
);
415 if (!strncmp(new_password
, forbidden_prefix
[i
],
416 strlen(forbidden_prefix
[i
]))) {
418 message
= strdup("Password has forbidden prefix");
419 error
= HTTP_ERROR_CLIENT
;
426 message
= strdup("Success");
430 *pass_message
= message
;
435 /* Implement ChangeISDSPassword.
436 * @arguments is pointer to struct arguments_DS_DsManage_ChangeISDSPassword */
437 static http_error
service_ChangeISDSPassword(
438 const struct http_connection
*connection
,
439 const xmlDocPtr soap_request
, xmlXPathContextPtr xpath_ctx
,
440 const xmlNodePtr isds_request
,
441 xmlDocPtr soap_response
, xmlNodePtr isds_response
,
442 const void *arguments
) {
443 http_error error
= HTTP_ERROR_SUCCESS
;
444 char *code
= "9999", *message
= NULL
;
445 const struct arguments_DS_DsManage_ChangeISDSPassword
*configuration
=
446 (const struct arguments_DS_DsManage_ChangeISDSPassword
*)arguments
;
448 if (NULL
== configuration
|| NULL
== configuration
->username
||
449 NULL
== configuration
->current_password
) {
450 error
= HTTP_ERROR_SERVER
;
454 /* Check for common password rules */
455 error
= check_passwd(
456 configuration
->username
, configuration
->current_password
,
457 xpath_ctx
, &code
, &message
);
460 if (HTTP_ERROR_SERVER
!= error
) {
461 http_error next_error
= insert_isds_status(isds_response
, 0,
462 BAD_CAST code
, BAD_CAST message
, NULL
);
463 if (HTTP_ERROR_SUCCESS
!= next_error
) error
= next_error
;
470 /* Implement ChangePasswordOTP.
471 * @arguments is pointer to struct
472 * arguments_asws_changePassword_ChangePasswordOTP */
473 static http_error
service_ChangePasswordOTP(
474 const struct http_connection
*connection
,
475 const xmlDocPtr soap_request
, xmlXPathContextPtr xpath_ctx
,
476 const xmlNodePtr isds_request
,
477 xmlDocPtr soap_response
, xmlNodePtr isds_response
,
478 const void *arguments
) {
479 http_error error
= HTTP_ERROR_SUCCESS
;
480 char *code
= "9999", *message
= NULL
;
481 const struct arguments_asws_changePassword_ChangePasswordOTP
*configuration
482 = (const struct arguments_asws_changePassword_ChangePasswordOTP
*)
486 if (NULL
== configuration
|| NULL
== configuration
->username
||
487 NULL
== configuration
->current_password
) {
488 error
= HTTP_ERROR_SERVER
;
492 /* Chek for OTP method */
493 EXTRACT_STRING("isds:dbOTPType", method
);
494 if (NULL
== method
) {
495 message
= strdup("Empty isds:dbOTPType");
496 error
= HTTP_ERROR_CLIENT
;
499 if ((configuration
->method
== AUTH_OTP_HMAC
&& strcmp(method
, "HOTP")) ||
500 (configuration
->method
== AUTH_OTP_TIME
&& strcmp(method
, "TOTP"))) {
501 message
= strdup("isds:dbOTPType does not match OTP method");
502 error
= HTTP_ERROR_CLIENT
;
506 /* Check for common password rules */
507 error
= check_passwd(
508 configuration
->username
, configuration
->current_password
,
509 xpath_ctx
, &code
, &message
);
512 if (HTTP_ERROR_SERVER
!= error
) {
513 http_error next_error
= insert_isds_status(isds_response
, 0,
514 BAD_CAST code
, BAD_CAST message
,
515 BAD_CAST configuration
->reference_number
);
516 if (HTTP_ERROR_SUCCESS
!= next_error
) error
= next_error
;
524 /* Implement SendSMSCode.
525 * @arguments is pointer to struct arguments_asws_changePassword_SendSMSCode */
526 static http_error
service_SendSMSCode(
527 const struct http_connection
*connection
,
528 const xmlDocPtr soap_request
, xmlXPathContextPtr xpath_ctx
,
529 const xmlNodePtr isds_request
,
530 xmlDocPtr soap_response
, xmlNodePtr isds_response
,
531 const void *arguments
) {
532 const struct arguments_asws_changePassword_SendSMSCode
*configuration
533 = (const struct arguments_asws_changePassword_SendSMSCode
*)
536 if (NULL
== configuration
|| NULL
== configuration
->status_code
||
537 NULL
== configuration
->status_message
) {
538 return HTTP_ERROR_SERVER
;
541 return insert_isds_status(isds_response
, 0,
542 BAD_CAST configuration
->status_code
,
543 BAD_CAST configuration
->status_message
,
544 BAD_CAST configuration
->reference_number
);
548 /* List of implemented services */
549 static struct service services
[] = {
550 { SERVICE_DS_Dz_DummyOperation
,
551 "DS/dz", BAD_CAST ISDS_NS
, BAD_CAST
"DummyOperation",
552 service_DummyOperation
},
553 { SERVICE_DS_Dz_ResignISDSDocument
,
554 "DS/dz", BAD_CAST ISDS_NS
, BAD_CAST
"Re-signISDSDocument",
555 service_ResignISDSDocument
},
556 { SERVICE_DS_DsManage_ChangeISDSPassword
,
557 "DS/DsManage", BAD_CAST ISDS_NS
, BAD_CAST
"ChangeISDSPassword",
558 service_ChangeISDSPassword
},
559 { SERVICE_DS_Dx_EraseMessage
,
560 "DS/dx", BAD_CAST ISDS_NS
, BAD_CAST
"EraseMessage",
561 service_EraseMessage
},
562 { SERVICE_asws_changePassword_ChangePasswordOTP
,
563 "/asws/changePassword", BAD_CAST OISDS_NS
, BAD_CAST
"ChangePasswordOTP",
564 service_ChangePasswordOTP
},
565 { SERVICE_asws_changePassword_SendSMSCode
,
566 "/asws/changePassword", BAD_CAST OISDS_NS
, BAD_CAST
"SendSMSCode",
567 service_SendSMSCode
},
571 /* Makes known all relevant namespaces to given XPath context
572 * @xpath_ctx is XPath context
573 * @otp_ns selects name space for the request and response know as "isds".
574 * Use true for OTP-authenticated password change services, otherwise false.
575 * @message_ns selects proper message name space. Unsigned and signed
576 * messages and delivery info's differ in prefix and URI.
577 * @return 0 in success, otherwise not 0. */
578 static int register_namespaces(xmlXPathContextPtr xpath_ctx
,
579 const _Bool otp_ns
, const message_ns_type message_ns
) {
580 const xmlChar
*service_namespace
= NULL
;
581 const xmlChar
*message_namespace
= NULL
;
583 if (!xpath_ctx
) return -1;
586 service_namespace
= BAD_CAST OISDS_NS
;
588 service_namespace
= BAD_CAST ISDS_NS
;
593 message_namespace
= BAD_CAST ISDS1_NS
; break;
594 case MESSAGE_NS_UNSIGNED
:
595 message_namespace
= BAD_CAST ISDS_NS
; break;
596 case MESSAGE_NS_SIGNED_INCOMING
:
597 message_namespace
= BAD_CAST SISDS_INCOMING_NS
; break;
598 case MESSAGE_NS_SIGNED_OUTGOING
:
599 message_namespace
= BAD_CAST SISDS_OUTGOING_NS
; break;
600 case MESSAGE_NS_SIGNED_DELIVERY
:
601 message_namespace
= BAD_CAST SISDS_DELIVERY_NS
; break;
606 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"soap", BAD_CAST SOAP_NS
))
608 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"isds", service_namespace
))
610 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"sisds", message_namespace
))
612 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"xs", BAD_CAST SCHEMA_NS
))
614 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"deposit", BAD_CAST DEPOSIT_NS
))
620 /* Parse soap request, pass it to service endpoint and respond to it.
621 * It sends final HTTP response. */
622 void soap(const struct http_connection
*connection
,
623 const struct service_configuration
*configuration
,
624 const void *request
, size_t request_length
, const char *end_point
) {
625 xmlDocPtr request_doc
= NULL
;
626 xmlXPathContextPtr xpath_ctx
= NULL
;
627 xmlXPathObjectPtr request_soap_body
= NULL
;
628 xmlNodePtr isds_request
= NULL
; /* pointer only */
629 _Bool service_handled
= 0, service_passed
= 0;
630 xmlDocPtr response_doc
= NULL
;
631 xmlNodePtr response_soap_envelope
= NULL
, response_soap_body
= NULL
,
632 isds_response
= NULL
;
633 xmlNsPtr soap_ns
= NULL
, isds_ns
= NULL
;
634 char *response_name
= NULL
;
635 xmlBufferPtr http_response_body
= NULL
;
636 xmlSaveCtxtPtr save_ctx
= NULL
;
639 if (NULL
== configuration
) {
640 http_send_response_500(connection
,
641 "Second argument of soap() is NULL");
645 if (NULL
== request
|| request_length
== 0) {
646 http_send_response_400(connection
, "Client sent empty body");
650 request_doc
= xmlParseMemory(request
, request_length
);
651 if (NULL
== request_doc
) {
652 http_send_response_400(connection
, "Client sent invalid XML document");
656 xpath_ctx
= xmlXPathNewContext(request_doc
);
657 if (NULL
== xpath_ctx
) {
658 xmlFreeDoc(request_doc
);
659 http_send_response_500(connection
, "Could not create XPath context");
663 if (register_namespaces(xpath_ctx
, 0, MESSAGE_NS_UNSIGNED
)) {
664 xmlXPathFreeContext(xpath_ctx
);
665 xmlFreeDoc(request_doc
);
666 http_send_response_500(connection
,
667 "Could not register name spaces to the XPath context");
672 request_soap_body
= xmlXPathEvalExpression(
673 BAD_CAST
"/soap:Envelope/soap:Body", xpath_ctx
);
674 if (NULL
== request_soap_body
) {
675 xmlXPathFreeContext(xpath_ctx
);
676 xmlFreeDoc(request_doc
);
677 http_send_response_400(connection
, "Client sent invalid SOAP request");
680 if (xmlXPathNodeSetIsEmpty(request_soap_body
->nodesetval
)) {
681 xmlXPathFreeObject(request_soap_body
);
682 xmlXPathFreeContext(xpath_ctx
);
683 xmlFreeDoc(request_doc
);
684 http_send_response_400(connection
,
685 "SOAP request does not contain SOAP Body element");
688 if (request_soap_body
->nodesetval
->nodeNr
> 1) {
689 xmlXPathFreeObject(request_soap_body
);
690 xmlXPathFreeContext(xpath_ctx
);
691 xmlFreeDoc(request_doc
);
692 http_send_response_400(connection
,
693 "SOAP response has more than one Body element");
696 isds_request
= request_soap_body
->nodesetval
->nodeTab
[0]->children
;
697 if (isds_request
->next
!= NULL
) {
698 xmlXPathFreeObject(request_soap_body
);
699 xmlXPathFreeContext(xpath_ctx
);
700 xmlFreeDoc(request_doc
);
701 http_send_response_400(connection
, "SOAP body has more than one child");
704 if (isds_request
->type
!= XML_ELEMENT_NODE
|| isds_request
->ns
== NULL
||
705 NULL
== isds_request
->ns
->href
) {
706 xmlXPathFreeObject(request_soap_body
);
707 xmlXPathFreeContext(xpath_ctx
);
708 xmlFreeDoc(request_doc
);
709 http_send_response_400(connection
,
710 "SOAP body does not contain a name-space-qualified element");
714 /* Build SOAP response envelope */
715 response_doc
= xmlNewDoc(BAD_CAST
"1.0");
717 http_send_response_500(connection
,
718 "Could not build SOAP response document");
721 response_soap_envelope
= xmlNewNode(NULL
, BAD_CAST
"Envelope");
722 if (!response_soap_envelope
) {
723 http_send_response_500(connection
,
724 "Could not build SOAP response envelope");
727 xmlDocSetRootElement(response_doc
, response_soap_envelope
);
728 /* Only this way we get namespace definition as @xmlns:soap,
729 * otherwise we get namespace prefix without definition */
730 soap_ns
= xmlNewNs(response_soap_envelope
, BAD_CAST SOAP_NS
, NULL
);
731 if(NULL
== soap_ns
) {
732 http_send_response_500(connection
, "Could not create SOAP name space");
735 xmlSetNs(response_soap_envelope
, soap_ns
);
736 response_soap_body
= xmlNewChild(response_soap_envelope
, NULL
,
737 BAD_CAST
"Body", NULL
);
738 if (!response_soap_body
) {
739 http_send_response_500(connection
,
740 "Could not add Body to SOAP response envelope");
743 /* Append ISDS response element */
744 if (-1 == test_asprintf(&response_name
, "%s%s", isds_request
->name
,
746 http_send_response_500(connection
,
747 "Could not buld ISDS resposne element name");
750 isds_response
= xmlNewChild(response_soap_body
, NULL
,
751 BAD_CAST response_name
, NULL
);
753 if (NULL
== isds_response
) {
754 http_send_response_500(connection
,
755 "Could not add ISDS response element to SOAP response body");
758 isds_ns
= xmlNewNs(isds_response
, isds_request
->ns
->href
, NULL
);
759 if(NULL
== isds_ns
) {
760 http_send_response_500(connection
,
761 "Could not create a name space for the response body");
764 xmlSetNs(isds_response
, isds_ns
);
766 /* Dispatch request to service */
767 for (int i
= 0; i
< sizeof(services
)/sizeof(services
[0]); i
++) {
768 if (!strcmp(services
[i
].end_point
, end_point
) &&
769 !xmlStrcmp(services
[i
].name_space
, isds_request
->ns
->href
) &&
770 !xmlStrcmp(services
[i
].name
, isds_request
->name
)) {
771 /* Check if the configuration is enabled and find configuration */
772 for (const struct service_configuration
*service
= configuration
;
773 service
->name
!= SERVICE_END
; service
++) {
774 if (service
->name
== services
[i
].id
) {
776 if (!xmlStrcmp(services
[i
].name_space
, BAD_CAST OISDS_NS
)) {
777 /* Alias "isds" XPath identifier to OISDS_NS */
778 if (register_namespaces(xpath_ctx
, 1,
779 MESSAGE_NS_UNSIGNED
)) {
780 http_send_response_500(connection
,
781 "Could not register name spaces to the "
786 xpath_ctx
->node
= isds_request
;
787 if (HTTP_ERROR_SERVER
!= services
[i
].function(connection
,
788 request_doc
, xpath_ctx
, isds_request
,
789 response_doc
, isds_response
,
790 service
->arguments
)) {
793 http_send_response_500(connection
,
794 "Internal server error while processing "
804 if (service_passed
) {
805 /* Serialize the SOAP response */
806 http_response_body
= xmlBufferCreate();
807 if (NULL
== http_response_body
) {
808 http_send_response_500(connection
,
809 "Could not create xmlBuffer for response serialization");
812 /* Last argument 1 means format the XML tree. This is pretty but it breaks
813 * XML document transport as it adds text nodes (indentiation) between
815 save_ctx
= xmlSaveToBuffer(http_response_body
, "UTF-8", 0);
816 if (NULL
== save_ctx
) {
817 http_send_response_500(connection
, "Could not create XML serializer");
820 /* XXX: According LibXML documentation, this function does not return
821 * meaningful value yet */
822 xmlSaveDoc(save_ctx
, response_doc
);
823 if (-1 == xmlSaveFlush(save_ctx
)) {
824 http_send_response_500(connection
,
825 "Could not serialize SOAP response");
829 http_send_response_200(connection
, http_response_body
->content
,
830 http_response_body
->use
, soap_mime_type
);
834 xmlSaveClose(save_ctx
);
835 xmlBufferFree(http_response_body
);
837 xmlFreeDoc(response_doc
);
839 xmlXPathFreeObject(request_soap_body
);
840 xmlXPathFreeContext(xpath_ctx
);
841 xmlFreeDoc(request_doc
);
843 if (!service_handled
) {
844 http_send_response_500(connection
,
845 "Requested ISDS service not implemented");