0.6.1 bump
[libisds.git] / test / simline / service.c
blob97fc140b2d58c0034d861cd3af365e08bf6f6e32
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) (int, xmlDocPtr, xmlXPathContextPtr, xmlNodePtr,
42 xmlDocPtr, xmlNodePtr, const void *arguments);
45 /* Following EXTRACT_* macros expect @xpath_ctx, @message, and leave label */
46 #define EXTRACT_STRING(element, string) { \
47 xmlXPathObjectPtr result = NULL; \
48 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
49 if (NULL == result) { \
50 error = HTTP_ERROR_SERVER; \
51 goto leave; \
52 } \
53 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
54 if (result->nodesetval->nodeNr > 1) { \
55 xmlXPathFreeObject(result); \
56 test_asprintf(&message, "Multiple %s element", element); \
57 error = HTTP_ERROR_CLIENT; \
58 goto leave; \
59 } \
60 (string) = (char *) \
61 xmlXPathCastNodeSetToString(result->nodesetval); \
62 if (!(string)) { \
63 xmlXPathFreeObject(result); \
64 error = HTTP_ERROR_SERVER; \
65 goto leave; \
66 } \
67 } \
68 xmlXPathFreeObject(result); \
71 #define EXTRACT_BOOLEAN(element, booleanPtr) { \
72 char *string = NULL; \
73 EXTRACT_STRING(element, string); \
75 if (NULL != string) { \
76 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
77 if (NULL == (booleanPtr)) { \
78 free(string); \
79 error = HTTP_ERROR_SERVER; \
80 goto leave; \
81 } \
83 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
84 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
85 *(booleanPtr) = 1; \
86 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
87 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
88 *(booleanPtr) = 0; \
89 else { \
90 test_asprintf(&message, \
91 "%s value is not valid boolean: %s", \
92 element, string); \
93 free(string); \
94 error = HTTP_ERROR_CLIENT; \
95 goto leave; \
96 } \
98 free(string); \
99 } \
103 /* Following INSERT_* macros expect @error and leave label */
104 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
106 xmlNodePtr node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
107 (xmlChar *) (string)); \
108 if (NULL == node) { \
109 error = HTTP_ERROR_SERVER; \
110 goto leave; \
114 #define INSERT_STRING(parent, element, string) \
115 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
117 #define INSERT_ELEMENT(child, parent, element) \
119 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
120 if (NULL == (child)) { \
121 error = HTTP_ERROR_SERVER; \
122 goto leave; \
126 /* Insert dmStatus or similar subtree
127 * @parent is element to insert to
128 * @dm is true for dmStatus, otherwise dbStatus
129 * @code is stautus code as string
130 * @message is UTF-8 encoded message
131 * @db_ref_number is optinal reference number propagated if not @dm
132 * @return 0 on success, otherwise non-0. */
133 static http_error insert_isds_status(xmlNodePtr parent, _Bool dm,
134 const xmlChar *code, const xmlChar *message,
135 const xmlChar *db_ref_number) {
136 http_error error = HTTP_ERROR_SUCCESS;
137 xmlNodePtr status;
139 if (NULL == code || NULL == message) {
140 error = HTTP_ERROR_SERVER;
141 goto leave;
144 INSERT_ELEMENT(status, parent, (dm) ? "dmStatus" : "dbStatus");
145 INSERT_STRING(status, (dm) ? "dmStatusCode" : "dbStatusCode", code);
146 INSERT_STRING(status, (dm) ? "dmStatusMessage" : "dbStatusMessage", message);
147 if (!dm && NULL != db_ref_number) {
148 INSERT_STRING(status, "dbStatusRefNumber", db_ref_number);
151 leave:
152 return error;
156 /* Implement DummyOperation */
157 static http_error service_DummyOperation(int socket, const xmlDocPtr soap_request,
158 xmlXPathContextPtr xpath_ctx, xmlNodePtr isds_request,
159 xmlDocPtr soap_response, xmlNodePtr isds_response,
160 const void *arguments) {
161 return insert_isds_status(isds_response, 1, BAD_CAST "0000",
162 BAD_CAST "Success", NULL);
166 /* Implement EraseMessage.
167 * @arguments is pointer to struct arguments_DS_DsManage_ChangeISDSPassword */
168 static http_error service_EraseMessage(int socket,
169 const xmlDocPtr soap_request, xmlXPathContextPtr xpath_ctx,
170 const xmlNodePtr isds_request,
171 xmlDocPtr soap_response, xmlNodePtr isds_response,
172 const void *arguments) {
173 http_error error = HTTP_ERROR_SUCCESS;
174 char *code = "9999", *message = NULL;
175 const struct arguments_DS_Dx_EraseMessage *configuration =
176 (const struct arguments_DS_Dx_EraseMessage *)arguments;
177 char *message_id = NULL;
178 _Bool *incoming = NULL;
180 if (NULL == configuration || NULL == configuration->message_id) {
181 error = HTTP_ERROR_SERVER;
182 goto leave;
185 EXTRACT_STRING("isds:dmID", message_id);
186 if (NULL == message_id) {
187 message = strdup("Missing isds:dmID");
188 error = HTTP_ERROR_CLIENT;
189 goto leave;
191 EXTRACT_BOOLEAN("isds:dmIncoming", incoming);
192 if (NULL == incoming) {
193 message = strdup("Missing isds:dmIncoming");
194 error = HTTP_ERROR_CLIENT;
195 goto leave;
198 if (xmlStrcmp((const xmlChar *) configuration->message_id,
199 (const xmlChar *) message_id)) {
200 code = "1219";
201 message = strdup("Message is not in the long term storage");
202 error = HTTP_ERROR_CLIENT;
203 goto leave;
205 if (configuration->incoming != *incoming) {
206 code = "1219";
207 message = strdup("Message direction mismatches");
208 error = HTTP_ERROR_CLIENT;
209 goto leave;
212 code = "0000";
213 message = strdup("Success");
214 leave:
215 if (HTTP_ERROR_SERVER != error) {
216 http_error next_error = insert_isds_status(isds_response, 1,
217 BAD_CAST code, BAD_CAST message, NULL);
218 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
220 free(incoming);
221 free(message_id);
222 free(message);
223 return error;
227 /* Common part for ChangeISDSPassword and ChangePasswordOTP.
228 * @code is output pointer to static string
229 * @pass_message is output pointer to auto-allocated string
230 * @arguments is pointer to struct arguments_DS_DsManage_ChangeISDSPassword */
231 static http_error check_passwd(
232 const char *username, const char *current_password,
233 xmlXPathContextPtr xpath_ctx,
234 char **code, char **pass_message) {
235 http_error error = HTTP_ERROR_SUCCESS;
236 char *message = NULL;
237 char *old_password = NULL, *new_password = NULL;
238 size_t length;
240 if (NULL == username || NULL == current_password ||
241 NULL == code || NULL == pass_message) {
242 error = HTTP_ERROR_SERVER;
243 goto leave;
246 *code = "9999";
249 /* Parse request */
250 EXTRACT_STRING("isds:dbOldPassword", old_password);
251 if (NULL == old_password) {
252 message = strdup("Empty isds:dbOldPassword");
253 error = HTTP_ERROR_CLIENT;
254 goto leave;
256 EXTRACT_STRING("isds:dbNewPassword", new_password);
257 if (NULL == new_password) {
258 message = strdup("Empty isds:dbOldPassword");
259 error = HTTP_ERROR_CLIENT;
260 goto leave;
263 /* Check defined cases */
264 if (strcmp(current_password, old_password)) {
265 *code = "1090";
266 message = strdup("Bad current password");
267 error = HTTP_ERROR_CLIENT;
268 goto leave;
271 length = strlen(new_password);
273 if (length < 8 || length > 32) {
274 *code = "1066";
275 message = strdup("Too short or too long");
276 error = HTTP_ERROR_CLIENT;
277 goto leave;
281 const char lower[] = "abcdefghijklmnopqrstuvwxyz";
282 const char upper[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
283 const char digit[] = "0123456789";
284 const char special[] = "!#$%&()*+,-.:=?@[]_{}|~";
285 _Bool has_lower = 0, has_upper = 0, has_digit=0;
287 for (int i = 0; i < length; i++) {
288 if (NULL != strchr(lower, new_password[i]))
289 has_lower = 1;
290 else if (NULL != strchr(upper, new_password[i]))
291 has_upper = 1;
292 else if (NULL != strchr(digit, new_password[i]))
293 has_digit = 1;
294 else if (NULL == strchr(special, new_password[i])) {
295 *code = "1079";
296 message = strdup("Password contains forbidden character");
297 error = HTTP_ERROR_CLIENT;
298 goto leave;
302 if (!has_lower || !has_upper || !has_digit) {
303 *code = "1080";
304 message = strdup("Password does not contain lower cased letter, "
305 "upper cased letter and a digit");
306 error = HTTP_ERROR_CLIENT;
307 goto leave;
311 if (!strcmp(old_password, new_password)) {
312 *code = "1067";
313 message = strdup("New password same as current one");
314 error = HTTP_ERROR_CLIENT;
315 goto leave;
318 if (NULL != strstr(new_password, username)) {
319 *code = "1082";
320 message = strdup("New password contains user ID");
321 error = HTTP_ERROR_CLIENT;
322 goto leave;
325 for (int i = 0; i < length - 2; i++) {
326 if (new_password[i] == new_password[i+1] &&
327 new_password[i] == new_password[i+2]) {
328 *code = "1083";
329 message = strdup("Password contains sequence "
330 "of three identical characters");
331 error = HTTP_ERROR_CLIENT;
332 goto leave;
337 const char *forbidden_prefix[] = { "qwert", "asdgf", "12345" };
338 for (int i = 0; i < sizeof(forbidden_prefix)/sizeof(*forbidden_prefix);
339 i++) {
340 if (!strncmp(new_password, forbidden_prefix[i],
341 strlen(forbidden_prefix[i]))) {
342 *code = "1083";
343 message = strdup("Password has forbidden prefix");
344 error = HTTP_ERROR_CLIENT;
345 goto leave;
350 *code = "0000";
351 message = strdup("Success");
352 leave:
353 free(old_password);
354 free(new_password);
355 *pass_message = message;
356 return error;
360 /* Implement ChangeISDSPassword.
361 * @arguments is pointer to struct arguments_DS_DsManage_ChangeISDSPassword */
362 static http_error service_ChangeISDSPassword(int socket,
363 const xmlDocPtr soap_request, xmlXPathContextPtr xpath_ctx,
364 const xmlNodePtr isds_request,
365 xmlDocPtr soap_response, xmlNodePtr isds_response,
366 const void *arguments) {
367 http_error error = HTTP_ERROR_SUCCESS;
368 char *code = "9999", *message = NULL;
369 const struct arguments_DS_DsManage_ChangeISDSPassword *configuration =
370 (const struct arguments_DS_DsManage_ChangeISDSPassword *)arguments;
372 if (NULL == configuration || NULL == configuration->username ||
373 NULL == configuration->current_password) {
374 error = HTTP_ERROR_SERVER;
375 goto leave;
378 /* Check for common password rules */
379 error = check_passwd(
380 configuration->username, configuration->current_password,
381 xpath_ctx, &code, &message);
383 leave:
384 if (HTTP_ERROR_SERVER != error) {
385 http_error next_error = insert_isds_status(isds_response, 0,
386 BAD_CAST code, BAD_CAST message, NULL);
387 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
389 free(message);
390 return error;
394 /* Implement ChangePasswordOTP.
395 * @arguments is pointer to struct
396 * arguments_asws_changePassword_ChangePasswordOTP */
397 static http_error service_ChangePasswordOTP(int socket,
398 const xmlDocPtr soap_request, xmlXPathContextPtr xpath_ctx,
399 const xmlNodePtr isds_request,
400 xmlDocPtr soap_response, xmlNodePtr isds_response,
401 const void *arguments) {
402 http_error error = HTTP_ERROR_SUCCESS;
403 char *code = "9999", *message = NULL;
404 const struct arguments_asws_changePassword_ChangePasswordOTP *configuration
405 = (const struct arguments_asws_changePassword_ChangePasswordOTP *)
406 arguments;
407 char *method = NULL;
409 if (NULL == configuration || NULL == configuration->username ||
410 NULL == configuration->current_password) {
411 error = HTTP_ERROR_SERVER;
412 goto leave;
415 /* Chek for OTP method */
416 EXTRACT_STRING("isds:dbOTPType", method);
417 if (NULL == method) {
418 message = strdup("Empty isds:dbOTPType");
419 error = HTTP_ERROR_CLIENT;
420 goto leave;
422 if ((configuration->method == AUTH_OTP_HMAC && strcmp(method, "HOTP")) ||
423 (configuration->method == AUTH_OTP_TIME && strcmp(method, "TOTP"))) {
424 message = strdup("isds:dbOTPType does not match OTP method");
425 error = HTTP_ERROR_CLIENT;
426 goto leave;
429 /* Check for common password rules */
430 error = check_passwd(
431 configuration->username, configuration->current_password,
432 xpath_ctx, &code, &message);
434 leave:
435 if (HTTP_ERROR_SERVER != error) {
436 http_error next_error = insert_isds_status(isds_response, 0,
437 BAD_CAST code, BAD_CAST message,
438 BAD_CAST configuration->reference_number);
439 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
441 free(message);
442 free(method);
443 return error;
447 /* Implement SendSMSCode.
448 * @arguments is pointer to struct arguments_asws_changePassword_SendSMSCode */
449 static http_error service_SendSMSCode(int socket,
450 const xmlDocPtr soap_request, xmlXPathContextPtr xpath_ctx,
451 const xmlNodePtr isds_request,
452 xmlDocPtr soap_response, xmlNodePtr isds_response,
453 const void *arguments) {
454 const struct arguments_asws_changePassword_SendSMSCode *configuration
455 = (const struct arguments_asws_changePassword_SendSMSCode *)
456 arguments;
458 if (NULL == configuration || NULL == configuration->status_code ||
459 NULL == configuration->status_message) {
460 return HTTP_ERROR_SERVER;
463 return insert_isds_status(isds_response, 0,
464 BAD_CAST configuration->status_code,
465 BAD_CAST configuration->status_message,
466 BAD_CAST configuration->reference_number);
470 /* List of implemented services */
471 static struct service services[] = {
472 { SERVICE_DS_Dz_DummyOperation,
473 "DS/dz", BAD_CAST ISDS_NS, BAD_CAST "DummyOperation",
474 service_DummyOperation },
475 { SERVICE_DS_DsManage_ChangeISDSPassword,
476 "DS/DsManage", BAD_CAST ISDS_NS, BAD_CAST "ChangeISDSPassword",
477 service_ChangeISDSPassword },
478 { SERVICE_DS_Dx_EraseMessage,
479 "DS/dx", BAD_CAST ISDS_NS, BAD_CAST "EraseMessage",
480 service_EraseMessage },
481 { SERVICE_asws_changePassword_ChangePasswordOTP,
482 "/asws/changePassword", BAD_CAST OISDS_NS, BAD_CAST "ChangePasswordOTP",
483 service_ChangePasswordOTP },
484 { SERVICE_asws_changePassword_SendSMSCode,
485 "/asws/changePassword", BAD_CAST OISDS_NS, BAD_CAST "SendSMSCode",
486 service_SendSMSCode },
490 /* Makes known all relevant namespaces to given XPath context
491 * @xpath_ctx is XPath context
492 * @otp_ns selects name space for the request and response know as "isds".
493 * Use true for OTP-authenticated password change services, otherwise false.
494 * @message_ns selects proper message name space. Unsigned and signed
495 * messages and delivery info's differ in prefix and URI.
496 * @return 0 in success, otherwise not 0. */
497 static int register_namespaces(xmlXPathContextPtr xpath_ctx,
498 const _Bool otp_ns, const message_ns_type message_ns) {
499 const xmlChar *service_namespace = NULL;
500 const xmlChar *message_namespace = NULL;
502 if (!xpath_ctx) return -1;
504 if (otp_ns) {
505 service_namespace = BAD_CAST OISDS_NS;
506 } else {
507 service_namespace = BAD_CAST ISDS_NS;
510 switch(message_ns) {
511 case MESSAGE_NS_1:
512 message_namespace = BAD_CAST ISDS1_NS; break;
513 case MESSAGE_NS_UNSIGNED:
514 message_namespace = BAD_CAST ISDS_NS; break;
515 case MESSAGE_NS_SIGNED_INCOMING:
516 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
517 case MESSAGE_NS_SIGNED_OUTGOING:
518 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
519 case MESSAGE_NS_SIGNED_DELIVERY:
520 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
521 default:
522 return -1;
525 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
526 return -1;
527 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", service_namespace))
528 return -1;
529 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
530 return -1;
531 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
532 return -1;
533 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
534 return -1;
535 return 0;
539 /* Parse soap request, pass it to service endpoint and respond to it.
540 * It sends final HTTP response. */
541 void soap(int socket, const struct service_configuration *configuration,
542 const void *request, size_t request_length, const char *end_point) {
543 xmlDocPtr request_doc = NULL;
544 xmlXPathContextPtr xpath_ctx = NULL;
545 xmlXPathObjectPtr request_soap_body = NULL;
546 xmlNodePtr isds_request = NULL; /* pointer only */
547 _Bool service_handled = 0, service_passed = 0;
548 xmlDocPtr response_doc = NULL;
549 xmlNodePtr response_soap_envelope = NULL, response_soap_body = NULL,
550 isds_response = NULL;
551 xmlNsPtr soap_ns = NULL, isds_ns = NULL;
552 char *response_name = NULL;
553 xmlBufferPtr http_response_body = NULL;
554 xmlSaveCtxtPtr save_ctx = NULL;
557 if (NULL == configuration) {
558 http_send_response_500(socket, "Second argument of soap() is NULL");
559 return;
562 if (NULL == request || request_length == 0) {
563 http_send_response_400(socket, "Client sent empty body");
564 return;
567 request_doc = xmlParseMemory(request, request_length);
568 if (NULL == request_doc) {
569 http_send_response_400(socket, "Client sent invalid XML document");
570 return;
573 xpath_ctx = xmlXPathNewContext(request_doc);
574 if (NULL == xpath_ctx) {
575 xmlFreeDoc(request_doc);
576 http_send_response_500(socket, "Could not create XPath context");
577 return;
580 if (register_namespaces(xpath_ctx, 0, MESSAGE_NS_UNSIGNED)) {
581 xmlXPathFreeContext(xpath_ctx);
582 xmlFreeDoc(request_doc);
583 http_send_response_500(socket,
584 "Could not register name spaces to the XPath context");
585 return;
588 /* Get SOAP Body */
589 request_soap_body = xmlXPathEvalExpression(
590 BAD_CAST "/soap:Envelope/soap:Body", xpath_ctx);
591 if (NULL == request_soap_body) {
592 xmlXPathFreeContext(xpath_ctx);
593 xmlFreeDoc(request_doc);
594 http_send_response_400(socket, "Client sent invalid SOAP request");
595 return;
597 if (xmlXPathNodeSetIsEmpty(request_soap_body->nodesetval)) {
598 xmlXPathFreeObject(request_soap_body);
599 xmlXPathFreeContext(xpath_ctx);
600 xmlFreeDoc(request_doc);
601 http_send_response_400(socket,
602 "SOAP request does not contain SOAP Body element");
603 return;
605 if (request_soap_body->nodesetval->nodeNr > 1) {
606 xmlXPathFreeObject(request_soap_body);
607 xmlXPathFreeContext(xpath_ctx);
608 xmlFreeDoc(request_doc);
609 http_send_response_400(socket,
610 "SOAP response has more than one Body element");
611 return;
613 isds_request = request_soap_body->nodesetval->nodeTab[0]->children;
614 if (isds_request->next != NULL) {
615 xmlXPathFreeObject(request_soap_body);
616 xmlXPathFreeContext(xpath_ctx);
617 xmlFreeDoc(request_doc);
618 http_send_response_400(socket, "SOAP body has more than one child");
619 return;
621 if (isds_request->type != XML_ELEMENT_NODE || isds_request->ns == NULL ||
622 NULL == isds_request->ns->href) {
623 xmlXPathFreeObject(request_soap_body);
624 xmlXPathFreeContext(xpath_ctx);
625 xmlFreeDoc(request_doc);
626 http_send_response_400(socket,
627 "SOAP body does not contain a name-space-qualified element");
628 return;
631 /* Build SOAP response envelope */
632 response_doc = xmlNewDoc(BAD_CAST "1.0");
633 if (!response_doc) {
634 http_send_response_500(socket, "Could not build SOAP response document");
635 goto leave;
637 response_soap_envelope = xmlNewNode(NULL, BAD_CAST "Envelope");
638 if (!response_soap_envelope) {
639 http_send_response_500(socket, "Could not build SOAP response envelope");
640 goto leave;
642 xmlDocSetRootElement(response_doc, response_soap_envelope);
643 /* Only this way we get namespace definition as @xmlns:soap,
644 * otherwise we get namespace prefix without definition */
645 soap_ns = xmlNewNs(response_soap_envelope, BAD_CAST SOAP_NS, NULL);
646 if(NULL == soap_ns) {
647 http_send_response_500(socket, "Could not create SOAP name space");
648 goto leave;
650 xmlSetNs(response_soap_envelope, soap_ns);
651 response_soap_body = xmlNewChild(response_soap_envelope, NULL,
652 BAD_CAST "Body", NULL);
653 if (!response_soap_body) {
654 http_send_response_500(socket,
655 "Could not add Body to SOAP response envelope");
656 goto leave;
658 /* Append ISDS response element */
659 if (-1 == test_asprintf(&response_name, "%s%s", isds_request->name,
660 "Response")) {
661 http_send_response_500(socket,
662 "Could not buld ISDS resposne element name");
663 goto leave;
665 isds_response = xmlNewChild(response_soap_body, NULL,
666 BAD_CAST response_name, NULL);
667 free(response_name);
668 if (NULL == isds_response) {
669 http_send_response_500(socket,
670 "Could not add ISDS response element to SOAP response body");
671 goto leave;
673 isds_ns = xmlNewNs(isds_response, isds_request->ns->href, NULL);
674 if(NULL == isds_ns) {
675 http_send_response_500(socket,
676 "Could not create a name space for the response body");
677 goto leave;
679 xmlSetNs(isds_response, isds_ns);
681 /* Dispatch request to service */
682 for (int i = 0; i < sizeof(services)/sizeof(services[0]); i++) {
683 if (!strcmp(services[i].end_point, end_point) &&
684 !xmlStrcmp(services[i].name_space, isds_request->ns->href) &&
685 !xmlStrcmp(services[i].name, isds_request->name)) {
686 /* Check if the configuration is enabled and find configuration */
687 for (const struct service_configuration *service = configuration;
688 service->name != SERVICE_END; service++) {
689 if (service->name == services[i].id) {
690 service_handled = 1;
691 if (!xmlStrcmp(services[i].name_space, BAD_CAST OISDS_NS)) {
692 /* Alias "isds" XPath identifier to OISDS_NS */
693 if (register_namespaces(xpath_ctx, 1,
694 MESSAGE_NS_UNSIGNED)) {
695 http_send_response_500(socket,
696 "Could not register name spaces to the "
697 "XPath context");
698 break;
701 xpath_ctx->node = isds_request;
702 if (HTTP_ERROR_SERVER != services[i].function(socket,
703 request_doc, xpath_ctx, isds_request,
704 response_doc, isds_response,
705 service->arguments)) {
706 service_passed = 1;
707 } else {
708 http_send_response_500(socket,
709 "Internal server error while processing "
710 "ISDS request");
714 break;
718 /* Send response */
719 if (service_passed) {
720 /* Serialize the SOAP response */
721 http_response_body = xmlBufferCreate();
722 if (NULL == http_response_body) {
723 http_send_response_500(socket,
724 "Could not create xmlBuffer for response serialization");
725 goto leave;
727 /* Last argument 1 means format the XML tree. This is pretty but it breaks
728 * XML document transport as it adds text nodes (indentiation) between
729 * elements. */
730 save_ctx = xmlSaveToBuffer(http_response_body, "UTF-8", 0);
731 if (NULL == save_ctx) {
732 http_send_response_500(socket, "Could not create XML serializer");
733 goto leave;
735 /* XXX: According LibXML documentation, this function does not return
736 * meaningful value yet */
737 xmlSaveDoc(save_ctx, response_doc);
738 if (-1 == xmlSaveFlush(save_ctx)) {
739 http_send_response_500(socket,
740 "Could not serialize SOAP response");
741 goto leave;
744 http_send_response_200(socket, http_response_body->content,
745 http_response_body->use, soap_mime_type);
748 leave:
749 xmlSaveClose(save_ctx);
750 xmlBufferFree(http_response_body);
752 xmlFreeDoc(response_doc);
754 xmlXPathFreeObject(request_soap_body);
755 xmlXPathFreeContext(xpath_ctx);
756 xmlFreeDoc(request_doc);
758 if (!service_handled) {
759 http_send_response_500(socket,
760 "Requested ISDS service not implemented");