test: server: Pass http_connection to HTTP funcation
[libisds.git] / test / simline / service.c
blob6467327e4c168e399be1f5ac826832ed1d0f2dbe
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 stautus 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 /* Implement DummyOperation */
158 static http_error service_DummyOperation(
159 const struct http_connection *connection, const xmlDocPtr soap_request,
160 xmlXPathContextPtr xpath_ctx, xmlNodePtr isds_request,
161 xmlDocPtr soap_response, xmlNodePtr isds_response,
162 const void *arguments) {
163 return insert_isds_status(isds_response, 1, BAD_CAST "0000",
164 BAD_CAST "Success", NULL);
168 /* Implement EraseMessage.
169 * @arguments is pointer to struct arguments_DS_DsManage_ChangeISDSPassword */
170 static http_error service_EraseMessage(const struct http_connection *connection,
171 const xmlDocPtr soap_request, xmlXPathContextPtr xpath_ctx,
172 const xmlNodePtr isds_request,
173 xmlDocPtr soap_response, xmlNodePtr isds_response,
174 const void *arguments) {
175 http_error error = HTTP_ERROR_SUCCESS;
176 char *code = "9999", *message = NULL;
177 const struct arguments_DS_Dx_EraseMessage *configuration =
178 (const struct arguments_DS_Dx_EraseMessage *)arguments;
179 char *message_id = NULL;
180 _Bool *incoming = NULL;
182 if (NULL == configuration || NULL == configuration->message_id) {
183 error = HTTP_ERROR_SERVER;
184 goto leave;
187 EXTRACT_STRING("isds:dmID", message_id);
188 if (NULL == message_id) {
189 message = strdup("Missing isds:dmID");
190 error = HTTP_ERROR_CLIENT;
191 goto leave;
193 EXTRACT_BOOLEAN("isds:dmIncoming", incoming);
194 if (NULL == incoming) {
195 message = strdup("Missing isds:dmIncoming");
196 error = HTTP_ERROR_CLIENT;
197 goto leave;
200 if (xmlStrcmp((const xmlChar *) configuration->message_id,
201 (const xmlChar *) message_id)) {
202 code = "1219";
203 message = strdup("Message is not in the long term storage");
204 error = HTTP_ERROR_CLIENT;
205 goto leave;
207 if (configuration->incoming != *incoming) {
208 code = "1219";
209 message = strdup("Message direction mismatches");
210 error = HTTP_ERROR_CLIENT;
211 goto leave;
214 code = "0000";
215 message = strdup("Success");
216 leave:
217 if (HTTP_ERROR_SERVER != error) {
218 http_error next_error = insert_isds_status(isds_response, 1,
219 BAD_CAST code, BAD_CAST message, NULL);
220 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
222 free(incoming);
223 free(message_id);
224 free(message);
225 return error;
229 /* Common part for ChangeISDSPassword and ChangePasswordOTP.
230 * @code is output pointer to static string
231 * @pass_message is output pointer to auto-allocated string
232 * @arguments is pointer to struct arguments_DS_DsManage_ChangeISDSPassword */
233 static http_error check_passwd(
234 const char *username, const char *current_password,
235 xmlXPathContextPtr xpath_ctx,
236 char **code, char **pass_message) {
237 http_error error = HTTP_ERROR_SUCCESS;
238 char *message = NULL;
239 char *old_password = NULL, *new_password = NULL;
240 size_t length;
242 if (NULL == username || NULL == current_password ||
243 NULL == code || NULL == pass_message) {
244 error = HTTP_ERROR_SERVER;
245 goto leave;
248 *code = "9999";
251 /* Parse request */
252 EXTRACT_STRING("isds:dbOldPassword", old_password);
253 if (NULL == old_password) {
254 message = strdup("Empty isds:dbOldPassword");
255 error = HTTP_ERROR_CLIENT;
256 goto leave;
258 EXTRACT_STRING("isds:dbNewPassword", new_password);
259 if (NULL == new_password) {
260 message = strdup("Empty isds:dbOldPassword");
261 error = HTTP_ERROR_CLIENT;
262 goto leave;
265 /* Check defined cases */
266 if (strcmp(current_password, old_password)) {
267 *code = "1090";
268 message = strdup("Bad current password");
269 error = HTTP_ERROR_CLIENT;
270 goto leave;
273 length = strlen(new_password);
275 if (length < 8 || length > 32) {
276 *code = "1066";
277 message = strdup("Too short or too long");
278 error = HTTP_ERROR_CLIENT;
279 goto leave;
283 const char lower[] = "abcdefghijklmnopqrstuvwxyz";
284 const char upper[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
285 const char digit[] = "0123456789";
286 const char special[] = "!#$%&()*+,-.:=?@[]_{}|~";
287 _Bool has_lower = 0, has_upper = 0, has_digit=0;
289 for (int i = 0; i < length; i++) {
290 if (NULL != strchr(lower, new_password[i]))
291 has_lower = 1;
292 else if (NULL != strchr(upper, new_password[i]))
293 has_upper = 1;
294 else if (NULL != strchr(digit, new_password[i]))
295 has_digit = 1;
296 else if (NULL == strchr(special, new_password[i])) {
297 *code = "1079";
298 message = strdup("Password contains forbidden character");
299 error = HTTP_ERROR_CLIENT;
300 goto leave;
304 if (!has_lower || !has_upper || !has_digit) {
305 *code = "1080";
306 message = strdup("Password does not contain lower cased letter, "
307 "upper cased letter and a digit");
308 error = HTTP_ERROR_CLIENT;
309 goto leave;
313 if (!strcmp(old_password, new_password)) {
314 *code = "1067";
315 message = strdup("New password same as current one");
316 error = HTTP_ERROR_CLIENT;
317 goto leave;
320 if (NULL != strstr(new_password, username)) {
321 *code = "1082";
322 message = strdup("New password contains user ID");
323 error = HTTP_ERROR_CLIENT;
324 goto leave;
327 for (int i = 0; i < length - 2; i++) {
328 if (new_password[i] == new_password[i+1] &&
329 new_password[i] == new_password[i+2]) {
330 *code = "1083";
331 message = strdup("Password contains sequence "
332 "of three identical characters");
333 error = HTTP_ERROR_CLIENT;
334 goto leave;
339 const char *forbidden_prefix[] = { "qwert", "asdgf", "12345" };
340 for (int i = 0; i < sizeof(forbidden_prefix)/sizeof(*forbidden_prefix);
341 i++) {
342 if (!strncmp(new_password, forbidden_prefix[i],
343 strlen(forbidden_prefix[i]))) {
344 *code = "1083";
345 message = strdup("Password has forbidden prefix");
346 error = HTTP_ERROR_CLIENT;
347 goto leave;
352 *code = "0000";
353 message = strdup("Success");
354 leave:
355 free(old_password);
356 free(new_password);
357 *pass_message = message;
358 return error;
362 /* Implement ChangeISDSPassword.
363 * @arguments is pointer to struct arguments_DS_DsManage_ChangeISDSPassword */
364 static http_error service_ChangeISDSPassword(
365 const struct http_connection *connection,
366 const xmlDocPtr soap_request, xmlXPathContextPtr xpath_ctx,
367 const xmlNodePtr isds_request,
368 xmlDocPtr soap_response, xmlNodePtr isds_response,
369 const void *arguments) {
370 http_error error = HTTP_ERROR_SUCCESS;
371 char *code = "9999", *message = NULL;
372 const struct arguments_DS_DsManage_ChangeISDSPassword *configuration =
373 (const struct arguments_DS_DsManage_ChangeISDSPassword *)arguments;
375 if (NULL == configuration || NULL == configuration->username ||
376 NULL == configuration->current_password) {
377 error = HTTP_ERROR_SERVER;
378 goto leave;
381 /* Check for common password rules */
382 error = check_passwd(
383 configuration->username, configuration->current_password,
384 xpath_ctx, &code, &message);
386 leave:
387 if (HTTP_ERROR_SERVER != error) {
388 http_error next_error = insert_isds_status(isds_response, 0,
389 BAD_CAST code, BAD_CAST message, NULL);
390 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
392 free(message);
393 return error;
397 /* Implement ChangePasswordOTP.
398 * @arguments is pointer to struct
399 * arguments_asws_changePassword_ChangePasswordOTP */
400 static http_error service_ChangePasswordOTP(
401 const struct http_connection *connection,
402 const xmlDocPtr soap_request, xmlXPathContextPtr xpath_ctx,
403 const xmlNodePtr isds_request,
404 xmlDocPtr soap_response, xmlNodePtr isds_response,
405 const void *arguments) {
406 http_error error = HTTP_ERROR_SUCCESS;
407 char *code = "9999", *message = NULL;
408 const struct arguments_asws_changePassword_ChangePasswordOTP *configuration
409 = (const struct arguments_asws_changePassword_ChangePasswordOTP *)
410 arguments;
411 char *method = NULL;
413 if (NULL == configuration || NULL == configuration->username ||
414 NULL == configuration->current_password) {
415 error = HTTP_ERROR_SERVER;
416 goto leave;
419 /* Chek for OTP method */
420 EXTRACT_STRING("isds:dbOTPType", method);
421 if (NULL == method) {
422 message = strdup("Empty isds:dbOTPType");
423 error = HTTP_ERROR_CLIENT;
424 goto leave;
426 if ((configuration->method == AUTH_OTP_HMAC && strcmp(method, "HOTP")) ||
427 (configuration->method == AUTH_OTP_TIME && strcmp(method, "TOTP"))) {
428 message = strdup("isds:dbOTPType does not match OTP method");
429 error = HTTP_ERROR_CLIENT;
430 goto leave;
433 /* Check for common password rules */
434 error = check_passwd(
435 configuration->username, configuration->current_password,
436 xpath_ctx, &code, &message);
438 leave:
439 if (HTTP_ERROR_SERVER != error) {
440 http_error next_error = insert_isds_status(isds_response, 0,
441 BAD_CAST code, BAD_CAST message,
442 BAD_CAST configuration->reference_number);
443 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
445 free(message);
446 free(method);
447 return error;
451 /* Implement SendSMSCode.
452 * @arguments is pointer to struct arguments_asws_changePassword_SendSMSCode */
453 static http_error service_SendSMSCode(
454 const struct http_connection *connection,
455 const xmlDocPtr soap_request, xmlXPathContextPtr xpath_ctx,
456 const xmlNodePtr isds_request,
457 xmlDocPtr soap_response, xmlNodePtr isds_response,
458 const void *arguments) {
459 const struct arguments_asws_changePassword_SendSMSCode *configuration
460 = (const struct arguments_asws_changePassword_SendSMSCode *)
461 arguments;
463 if (NULL == configuration || NULL == configuration->status_code ||
464 NULL == configuration->status_message) {
465 return HTTP_ERROR_SERVER;
468 return insert_isds_status(isds_response, 0,
469 BAD_CAST configuration->status_code,
470 BAD_CAST configuration->status_message,
471 BAD_CAST configuration->reference_number);
475 /* List of implemented services */
476 static struct service services[] = {
477 { SERVICE_DS_Dz_DummyOperation,
478 "DS/dz", BAD_CAST ISDS_NS, BAD_CAST "DummyOperation",
479 service_DummyOperation },
480 { SERVICE_DS_DsManage_ChangeISDSPassword,
481 "DS/DsManage", BAD_CAST ISDS_NS, BAD_CAST "ChangeISDSPassword",
482 service_ChangeISDSPassword },
483 { SERVICE_DS_Dx_EraseMessage,
484 "DS/dx", BAD_CAST ISDS_NS, BAD_CAST "EraseMessage",
485 service_EraseMessage },
486 { SERVICE_asws_changePassword_ChangePasswordOTP,
487 "/asws/changePassword", BAD_CAST OISDS_NS, BAD_CAST "ChangePasswordOTP",
488 service_ChangePasswordOTP },
489 { SERVICE_asws_changePassword_SendSMSCode,
490 "/asws/changePassword", BAD_CAST OISDS_NS, BAD_CAST "SendSMSCode",
491 service_SendSMSCode },
495 /* Makes known all relevant namespaces to given XPath context
496 * @xpath_ctx is XPath context
497 * @otp_ns selects name space for the request and response know as "isds".
498 * Use true for OTP-authenticated password change services, otherwise false.
499 * @message_ns selects proper message name space. Unsigned and signed
500 * messages and delivery info's differ in prefix and URI.
501 * @return 0 in success, otherwise not 0. */
502 static int register_namespaces(xmlXPathContextPtr xpath_ctx,
503 const _Bool otp_ns, const message_ns_type message_ns) {
504 const xmlChar *service_namespace = NULL;
505 const xmlChar *message_namespace = NULL;
507 if (!xpath_ctx) return -1;
509 if (otp_ns) {
510 service_namespace = BAD_CAST OISDS_NS;
511 } else {
512 service_namespace = BAD_CAST ISDS_NS;
515 switch(message_ns) {
516 case MESSAGE_NS_1:
517 message_namespace = BAD_CAST ISDS1_NS; break;
518 case MESSAGE_NS_UNSIGNED:
519 message_namespace = BAD_CAST ISDS_NS; break;
520 case MESSAGE_NS_SIGNED_INCOMING:
521 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
522 case MESSAGE_NS_SIGNED_OUTGOING:
523 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
524 case MESSAGE_NS_SIGNED_DELIVERY:
525 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
526 default:
527 return -1;
530 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
531 return -1;
532 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", service_namespace))
533 return -1;
534 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
535 return -1;
536 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
537 return -1;
538 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
539 return -1;
540 return 0;
544 /* Parse soap request, pass it to service endpoint and respond to it.
545 * It sends final HTTP response. */
546 void soap(const struct http_connection *connection,
547 const struct service_configuration *configuration,
548 const void *request, size_t request_length, const char *end_point) {
549 xmlDocPtr request_doc = NULL;
550 xmlXPathContextPtr xpath_ctx = NULL;
551 xmlXPathObjectPtr request_soap_body = NULL;
552 xmlNodePtr isds_request = NULL; /* pointer only */
553 _Bool service_handled = 0, service_passed = 0;
554 xmlDocPtr response_doc = NULL;
555 xmlNodePtr response_soap_envelope = NULL, response_soap_body = NULL,
556 isds_response = NULL;
557 xmlNsPtr soap_ns = NULL, isds_ns = NULL;
558 char *response_name = NULL;
559 xmlBufferPtr http_response_body = NULL;
560 xmlSaveCtxtPtr save_ctx = NULL;
563 if (NULL == configuration) {
564 http_send_response_500(connection,
565 "Second argument of soap() is NULL");
566 return;
569 if (NULL == request || request_length == 0) {
570 http_send_response_400(connection, "Client sent empty body");
571 return;
574 request_doc = xmlParseMemory(request, request_length);
575 if (NULL == request_doc) {
576 http_send_response_400(connection, "Client sent invalid XML document");
577 return;
580 xpath_ctx = xmlXPathNewContext(request_doc);
581 if (NULL == xpath_ctx) {
582 xmlFreeDoc(request_doc);
583 http_send_response_500(connection, "Could not create XPath context");
584 return;
587 if (register_namespaces(xpath_ctx, 0, MESSAGE_NS_UNSIGNED)) {
588 xmlXPathFreeContext(xpath_ctx);
589 xmlFreeDoc(request_doc);
590 http_send_response_500(connection,
591 "Could not register name spaces to the XPath context");
592 return;
595 /* Get SOAP Body */
596 request_soap_body = xmlXPathEvalExpression(
597 BAD_CAST "/soap:Envelope/soap:Body", xpath_ctx);
598 if (NULL == request_soap_body) {
599 xmlXPathFreeContext(xpath_ctx);
600 xmlFreeDoc(request_doc);
601 http_send_response_400(connection, "Client sent invalid SOAP request");
602 return;
604 if (xmlXPathNodeSetIsEmpty(request_soap_body->nodesetval)) {
605 xmlXPathFreeObject(request_soap_body);
606 xmlXPathFreeContext(xpath_ctx);
607 xmlFreeDoc(request_doc);
608 http_send_response_400(connection,
609 "SOAP request does not contain SOAP Body element");
610 return;
612 if (request_soap_body->nodesetval->nodeNr > 1) {
613 xmlXPathFreeObject(request_soap_body);
614 xmlXPathFreeContext(xpath_ctx);
615 xmlFreeDoc(request_doc);
616 http_send_response_400(connection,
617 "SOAP response has more than one Body element");
618 return;
620 isds_request = request_soap_body->nodesetval->nodeTab[0]->children;
621 if (isds_request->next != NULL) {
622 xmlXPathFreeObject(request_soap_body);
623 xmlXPathFreeContext(xpath_ctx);
624 xmlFreeDoc(request_doc);
625 http_send_response_400(connection, "SOAP body has more than one child");
626 return;
628 if (isds_request->type != XML_ELEMENT_NODE || isds_request->ns == NULL ||
629 NULL == isds_request->ns->href) {
630 xmlXPathFreeObject(request_soap_body);
631 xmlXPathFreeContext(xpath_ctx);
632 xmlFreeDoc(request_doc);
633 http_send_response_400(connection,
634 "SOAP body does not contain a name-space-qualified element");
635 return;
638 /* Build SOAP response envelope */
639 response_doc = xmlNewDoc(BAD_CAST "1.0");
640 if (!response_doc) {
641 http_send_response_500(connection,
642 "Could not build SOAP response document");
643 goto leave;
645 response_soap_envelope = xmlNewNode(NULL, BAD_CAST "Envelope");
646 if (!response_soap_envelope) {
647 http_send_response_500(connection,
648 "Could not build SOAP response envelope");
649 goto leave;
651 xmlDocSetRootElement(response_doc, response_soap_envelope);
652 /* Only this way we get namespace definition as @xmlns:soap,
653 * otherwise we get namespace prefix without definition */
654 soap_ns = xmlNewNs(response_soap_envelope, BAD_CAST SOAP_NS, NULL);
655 if(NULL == soap_ns) {
656 http_send_response_500(connection, "Could not create SOAP name space");
657 goto leave;
659 xmlSetNs(response_soap_envelope, soap_ns);
660 response_soap_body = xmlNewChild(response_soap_envelope, NULL,
661 BAD_CAST "Body", NULL);
662 if (!response_soap_body) {
663 http_send_response_500(connection,
664 "Could not add Body to SOAP response envelope");
665 goto leave;
667 /* Append ISDS response element */
668 if (-1 == test_asprintf(&response_name, "%s%s", isds_request->name,
669 "Response")) {
670 http_send_response_500(connection,
671 "Could not buld ISDS resposne element name");
672 goto leave;
674 isds_response = xmlNewChild(response_soap_body, NULL,
675 BAD_CAST response_name, NULL);
676 free(response_name);
677 if (NULL == isds_response) {
678 http_send_response_500(connection,
679 "Could not add ISDS response element to SOAP response body");
680 goto leave;
682 isds_ns = xmlNewNs(isds_response, isds_request->ns->href, NULL);
683 if(NULL == isds_ns) {
684 http_send_response_500(connection,
685 "Could not create a name space for the response body");
686 goto leave;
688 xmlSetNs(isds_response, isds_ns);
690 /* Dispatch request to service */
691 for (int i = 0; i < sizeof(services)/sizeof(services[0]); i++) {
692 if (!strcmp(services[i].end_point, end_point) &&
693 !xmlStrcmp(services[i].name_space, isds_request->ns->href) &&
694 !xmlStrcmp(services[i].name, isds_request->name)) {
695 /* Check if the configuration is enabled and find configuration */
696 for (const struct service_configuration *service = configuration;
697 service->name != SERVICE_END; service++) {
698 if (service->name == services[i].id) {
699 service_handled = 1;
700 if (!xmlStrcmp(services[i].name_space, BAD_CAST OISDS_NS)) {
701 /* Alias "isds" XPath identifier to OISDS_NS */
702 if (register_namespaces(xpath_ctx, 1,
703 MESSAGE_NS_UNSIGNED)) {
704 http_send_response_500(connection,
705 "Could not register name spaces to the "
706 "XPath context");
707 break;
710 xpath_ctx->node = isds_request;
711 if (HTTP_ERROR_SERVER != services[i].function(connection,
712 request_doc, xpath_ctx, isds_request,
713 response_doc, isds_response,
714 service->arguments)) {
715 service_passed = 1;
716 } else {
717 http_send_response_500(connection,
718 "Internal server error while processing "
719 "ISDS request");
723 break;
727 /* Send response */
728 if (service_passed) {
729 /* Serialize the SOAP response */
730 http_response_body = xmlBufferCreate();
731 if (NULL == http_response_body) {
732 http_send_response_500(connection,
733 "Could not create xmlBuffer for response serialization");
734 goto leave;
736 /* Last argument 1 means format the XML tree. This is pretty but it breaks
737 * XML document transport as it adds text nodes (indentiation) between
738 * elements. */
739 save_ctx = xmlSaveToBuffer(http_response_body, "UTF-8", 0);
740 if (NULL == save_ctx) {
741 http_send_response_500(connection, "Could not create XML serializer");
742 goto leave;
744 /* XXX: According LibXML documentation, this function does not return
745 * meaningful value yet */
746 xmlSaveDoc(save_ctx, response_doc);
747 if (-1 == xmlSaveFlush(save_ctx)) {
748 http_send_response_500(connection,
749 "Could not serialize SOAP response");
750 goto leave;
753 http_send_response_200(connection, http_response_body->content,
754 http_response_body->use, soap_mime_type);
757 leave:
758 xmlSaveClose(save_ctx);
759 xmlBufferFree(http_response_body);
761 xmlFreeDoc(response_doc);
763 xmlXPathFreeObject(request_soap_body);
764 xmlXPathFreeContext(xpath_ctx);
765 xmlFreeDoc(request_doc);
767 if (!service_handled) {
768 http_send_response_500(connection,
769 "Requested ISDS service not implemented");