Implement Re-signISDSDocument
[libisds.git] / test / simline / service.c
blob599f6a6a57e438d9d079fedc14f6921c98c0171e
1 #define _XOPEN_SOURCE 500 /* For strdup(3) */
2 #include "../test-tools.h"
3 #include "http.h"
4 #include "services.h"
5 #include <string.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(). */
15 typedef enum {
16 MESSAGE_NS_1,
17 MESSAGE_NS_UNSIGNED,
18 MESSAGE_NS_SIGNED_INCOMING,
19 MESSAGE_NS_SIGNED_OUTGOING,
20 MESSAGE_NS_SIGNED_DELIVERY,
21 MESSAGE_NS_OTP
22 } message_ns_type;
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"
36 struct service {
37 service_id id;
38 const char *end_point;
39 const xmlChar *name_space;
40 const xmlChar *name;
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; \
52 goto leave; \
53 } \
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; \
59 goto leave; \
60 } \
61 (string) = (char *) \
62 xmlXPathCastNodeSetToString(result->nodesetval); \
63 if (!(string)) { \
64 xmlXPathFreeObject(result); \
65 error = HTTP_ERROR_SERVER; \
66 goto leave; \
67 } \
68 } \
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)) { \
79 free(string); \
80 error = HTTP_ERROR_SERVER; \
81 goto leave; \
82 } \
84 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
85 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
86 *(booleanPtr) = 1; \
87 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
88 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
89 *(booleanPtr) = 0; \
90 else { \
91 test_asprintf(&message, \
92 "%s value is not valid boolean: %s", \
93 element, string); \
94 free(string); \
95 error = HTTP_ERROR_CLIENT; \
96 goto leave; \
97 } \
99 free(string); \
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; \
111 goto leave; \
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; \
123 goto leave; \
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;
138 xmlNodePtr status;
140 if (NULL == code || NULL == message) {
141 error = HTTP_ERROR_SERVER;
142 goto leave;
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);
152 leave:
153 return error;
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;
194 char *data = NULL;
196 if (NULL == configuration || NULL == configuration->status_code ||
197 NULL == configuration->status_message) {
198 error = HTTP_ERROR_SERVER;
199 goto leave;
202 EXTRACT_STRING("isds:dmDoc", data);
203 if (NULL == data) {
204 message = strdup("Missing isds:dmDoc");
205 error = HTTP_ERROR_CLIENT;
206 goto leave;
210 /* dmResultDoc is mandatory in response */
211 if (xmlStrcmp(BAD_CAST configuration->status_code, BAD_CAST "0000")) {
212 free(data);
213 data = NULL;
215 INSERT_STRING(isds_response, "dmResultDoc", data);
217 if (configuration->valid_to != NULL) {
218 error = tm2datestring(configuration->valid_to, &data);
219 if (error) {
220 message = strdup("Could not format date");
221 goto leave;
223 INSERT_STRING(isds_response, "dmValidTo", data);
226 code = configuration->status_code;
227 message = strdup(configuration->status_message);
229 leave:
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;
235 free(data);
236 free(message);
237 return 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;
257 goto leave;
260 EXTRACT_STRING("isds:dmID", message_id);
261 if (NULL == message_id) {
262 message = strdup("Missing isds:dmID");
263 error = HTTP_ERROR_CLIENT;
264 goto leave;
266 EXTRACT_BOOLEAN("isds:dmIncoming", incoming);
267 if (NULL == incoming) {
268 message = strdup("Missing isds:dmIncoming");
269 error = HTTP_ERROR_CLIENT;
270 goto leave;
273 if (xmlStrcmp((const xmlChar *) configuration->message_id,
274 (const xmlChar *) message_id)) {
275 code = "1219";
276 message = strdup("Message is not in the long term storage");
277 error = HTTP_ERROR_CLIENT;
278 goto leave;
280 if (configuration->incoming != *incoming) {
281 code = "1219";
282 message = strdup("Message direction mismatches");
283 error = HTTP_ERROR_CLIENT;
284 goto leave;
287 code = "0000";
288 message = strdup("Success");
289 leave:
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;
295 free(incoming);
296 free(message_id);
297 free(message);
298 return 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;
313 size_t length;
315 if (NULL == username || NULL == current_password ||
316 NULL == code || NULL == pass_message) {
317 error = HTTP_ERROR_SERVER;
318 goto leave;
321 *code = "9999";
324 /* Parse request */
325 EXTRACT_STRING("isds:dbOldPassword", old_password);
326 if (NULL == old_password) {
327 message = strdup("Empty isds:dbOldPassword");
328 error = HTTP_ERROR_CLIENT;
329 goto leave;
331 EXTRACT_STRING("isds:dbNewPassword", new_password);
332 if (NULL == new_password) {
333 message = strdup("Empty isds:dbOldPassword");
334 error = HTTP_ERROR_CLIENT;
335 goto leave;
338 /* Check defined cases */
339 if (strcmp(current_password, old_password)) {
340 *code = "1090";
341 message = strdup("Bad current password");
342 error = HTTP_ERROR_CLIENT;
343 goto leave;
346 length = strlen(new_password);
348 if (length < 8 || length > 32) {
349 *code = "1066";
350 message = strdup("Too short or too long");
351 error = HTTP_ERROR_CLIENT;
352 goto leave;
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]))
364 has_lower = 1;
365 else if (NULL != strchr(upper, new_password[i]))
366 has_upper = 1;
367 else if (NULL != strchr(digit, new_password[i]))
368 has_digit = 1;
369 else if (NULL == strchr(special, new_password[i])) {
370 *code = "1079";
371 message = strdup("Password contains forbidden character");
372 error = HTTP_ERROR_CLIENT;
373 goto leave;
377 if (!has_lower || !has_upper || !has_digit) {
378 *code = "1080";
379 message = strdup("Password does not contain lower cased letter, "
380 "upper cased letter and a digit");
381 error = HTTP_ERROR_CLIENT;
382 goto leave;
386 if (!strcmp(old_password, new_password)) {
387 *code = "1067";
388 message = strdup("New password same as current one");
389 error = HTTP_ERROR_CLIENT;
390 goto leave;
393 if (NULL != strstr(new_password, username)) {
394 *code = "1082";
395 message = strdup("New password contains user ID");
396 error = HTTP_ERROR_CLIENT;
397 goto leave;
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]) {
403 *code = "1083";
404 message = strdup("Password contains sequence "
405 "of three identical characters");
406 error = HTTP_ERROR_CLIENT;
407 goto leave;
412 const char *forbidden_prefix[] = { "qwert", "asdgf", "12345" };
413 for (int i = 0; i < sizeof(forbidden_prefix)/sizeof(*forbidden_prefix);
414 i++) {
415 if (!strncmp(new_password, forbidden_prefix[i],
416 strlen(forbidden_prefix[i]))) {
417 *code = "1083";
418 message = strdup("Password has forbidden prefix");
419 error = HTTP_ERROR_CLIENT;
420 goto leave;
425 *code = "0000";
426 message = strdup("Success");
427 leave:
428 free(old_password);
429 free(new_password);
430 *pass_message = message;
431 return error;
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;
451 goto leave;
454 /* Check for common password rules */
455 error = check_passwd(
456 configuration->username, configuration->current_password,
457 xpath_ctx, &code, &message);
459 leave:
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;
465 free(message);
466 return 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 *)
483 arguments;
484 char *method = NULL;
486 if (NULL == configuration || NULL == configuration->username ||
487 NULL == configuration->current_password) {
488 error = HTTP_ERROR_SERVER;
489 goto leave;
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;
497 goto leave;
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;
503 goto leave;
506 /* Check for common password rules */
507 error = check_passwd(
508 configuration->username, configuration->current_password,
509 xpath_ctx, &code, &message);
511 leave:
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;
518 free(message);
519 free(method);
520 return 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 *)
534 arguments;
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;
585 if (otp_ns) {
586 service_namespace = BAD_CAST OISDS_NS;
587 } else {
588 service_namespace = BAD_CAST ISDS_NS;
591 switch(message_ns) {
592 case MESSAGE_NS_1:
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;
602 default:
603 return -1;
606 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
607 return -1;
608 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", service_namespace))
609 return -1;
610 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
611 return -1;
612 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
613 return -1;
614 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
615 return -1;
616 return 0;
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");
642 return;
645 if (NULL == request || request_length == 0) {
646 http_send_response_400(connection, "Client sent empty body");
647 return;
650 request_doc = xmlParseMemory(request, request_length);
651 if (NULL == request_doc) {
652 http_send_response_400(connection, "Client sent invalid XML document");
653 return;
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");
660 return;
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");
668 return;
671 /* Get SOAP Body */
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");
678 return;
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");
686 return;
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");
694 return;
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");
702 return;
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");
711 return;
714 /* Build SOAP response envelope */
715 response_doc = xmlNewDoc(BAD_CAST "1.0");
716 if (!response_doc) {
717 http_send_response_500(connection,
718 "Could not build SOAP response document");
719 goto leave;
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");
725 goto leave;
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");
733 goto leave;
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");
741 goto leave;
743 /* Append ISDS response element */
744 if (-1 == test_asprintf(&response_name, "%s%s", isds_request->name,
745 "Response")) {
746 http_send_response_500(connection,
747 "Could not buld ISDS resposne element name");
748 goto leave;
750 isds_response = xmlNewChild(response_soap_body, NULL,
751 BAD_CAST response_name, NULL);
752 free(response_name);
753 if (NULL == isds_response) {
754 http_send_response_500(connection,
755 "Could not add ISDS response element to SOAP response body");
756 goto leave;
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");
762 goto leave;
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) {
775 service_handled = 1;
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 "
782 "XPath context");
783 break;
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)) {
791 service_passed = 1;
792 } else {
793 http_send_response_500(connection,
794 "Internal server error while processing "
795 "ISDS request");
799 break;
803 /* Send response */
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");
810 goto leave;
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
814 * elements. */
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");
818 goto leave;
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");
826 goto leave;
829 http_send_response_200(connection, http_response_body->content,
830 http_response_body->use, soap_mime_type);
833 leave:
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");