test: Add tests for isds_find_box_by_fulltext()
[libisds.git] / test / simline / service.c
blob626a6c805311f8c7c2c11c8c7a148956354d9a4d
1 #define _XOPEN_SOURCE 500 /* For strdup(3) */
2 #include "../test-tools.h"
3 #include "http.h"
4 #include "services.h"
5 #include "system.h"
6 #include <string.h>
7 #include <libxml/parser.h>
8 #include <libxml/xpath.h>
9 #include <libxml/xpathInternals.h>
10 #include <libxml/xmlsave.h>
12 static const char *soap_mime_type = "text/xml"; /* SOAP/1.1 requires text/xml */
14 /* Used to choose proper name space for message elements.
15 * See _isds_register_namespaces(). */
16 typedef enum {
17 MESSAGE_NS_1,
18 MESSAGE_NS_UNSIGNED,
19 MESSAGE_NS_SIGNED_INCOMING,
20 MESSAGE_NS_SIGNED_OUTGOING,
21 MESSAGE_NS_SIGNED_DELIVERY,
22 MESSAGE_NS_OTP
23 } message_ns_type;
25 #define SOAP_NS "http://schemas.xmlsoap.org/soap/envelope/"
26 #define SOAP2_NS "http://www.w3.org/2003/05/soap-envelope"
27 #define ISDS1_NS "http://isds.czechpoint.cz"
28 #define ISDS_NS "http://isds.czechpoint.cz/v20"
29 #define OISDS_NS "http://isds.czechpoint.cz/v20/asws"
30 #define SISDS_INCOMING_NS "http://isds.czechpoint.cz/v20/message"
31 #define SISDS_OUTGOING_NS "http://isds.czechpoint.cz/v20/SentMessage"
32 #define SISDS_DELIVERY_NS "http://isds.czechpoint.cz/v20/delivery"
33 #define SCHEMA_NS "http://www.w3.org/2001/XMLSchema"
34 #define DEPOSIT_NS "urn:uschovnaWSDL"
37 struct service {
38 service_id id;
39 const char *end_point;
40 const xmlChar *name_space;
41 const xmlChar *name;
42 http_error (*function) (const struct http_connection *, xmlDocPtr,
43 xmlXPathContextPtr, xmlNodePtr, xmlDocPtr, xmlNodePtr,
44 const void *arguments);
47 /* Following EXTRACT_* macros expect @xpath_ctx, @error, @message,
48 * and leave label. */
49 #define ELEMENT_EXISTS(element, allow_multiple) { \
50 xmlXPathObjectPtr result = NULL; \
51 result = xmlXPathEvalExpression(BAD_CAST element, xpath_ctx); \
52 if (NULL == result) { \
53 error = HTTP_ERROR_SERVER; \
54 goto leave; \
55 } \
56 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
57 xmlXPathFreeObject(result); \
58 test_asprintf(&message, "Element %s does not exist", element); \
59 error = HTTP_ERROR_CLIENT; \
60 goto leave; \
61 } else { \
62 if (!allow_multiple && result->nodesetval->nodeNr > 1) { \
63 xmlXPathFreeObject(result); \
64 test_asprintf(&message, "Multiple %s element", element); \
65 error = HTTP_ERROR_CLIENT; \
66 goto leave; \
67 } \
68 } \
69 xmlXPathFreeObject(result); \
72 #define EXTRACT_STRING(element, string) { \
73 xmlXPathObjectPtr result = NULL; \
74 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
75 if (NULL == result) { \
76 error = HTTP_ERROR_SERVER; \
77 goto leave; \
78 } \
79 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
80 if (result->nodesetval->nodeNr > 1) { \
81 xmlXPathFreeObject(result); \
82 test_asprintf(&message, "Multiple %s element", element); \
83 error = HTTP_ERROR_CLIENT; \
84 goto leave; \
85 } \
86 (string) = (char *) \
87 xmlXPathCastNodeSetToString(result->nodesetval); \
88 if (!(string)) { \
89 xmlXPathFreeObject(result); \
90 error = HTTP_ERROR_SERVER; \
91 goto leave; \
92 } \
93 } \
94 xmlXPathFreeObject(result); \
97 #define EXTRACT_BOOLEAN(element, booleanPtr) { \
98 char *string = NULL; \
99 EXTRACT_STRING(element, string); \
101 if (NULL != string) { \
102 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
103 if (NULL == (booleanPtr)) { \
104 free(string); \
105 error = HTTP_ERROR_SERVER; \
106 goto leave; \
109 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
110 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
111 *(booleanPtr) = 1; \
112 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
113 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
114 *(booleanPtr) = 0; \
115 else { \
116 test_asprintf(&message, \
117 "%s value is not valid boolean: %s", \
118 element, string); \
119 free(string); \
120 error = HTTP_ERROR_CLIENT; \
121 goto leave; \
124 free(string); \
129 #define EXTRACT_DATE(element, tmPtr) { \
130 char *string = NULL; \
131 EXTRACT_STRING(element, string); \
132 if (NULL != string) { \
133 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
134 if (NULL == (tmPtr)) { \
135 free(string); \
136 error = HTTP_ERROR_SERVER; \
137 goto leave; \
139 error = _server_datestring2tm(string, (tmPtr)); \
140 if (error) { \
141 if (error == HTTP_ERROR_CLIENT) { \
142 test_asprintf(&message, "%s value is not a valid date: %s", \
143 element, string); \
145 free(string); \
146 goto leave; \
148 free(string); \
152 /* Following INSERT_* macros expect @error and leave label */
153 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
155 xmlNodePtr node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
156 (xmlChar *) (string)); \
157 if (NULL == node) { \
158 error = HTTP_ERROR_SERVER; \
159 goto leave; \
163 #define INSERT_STRING(parent, element, string) \
164 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
166 #define INSERT_LONGINTPTR(parent, element, longintPtr) { \
167 if ((longintPtr)) { \
168 char *buffer = NULL; \
169 /* FIXME: locale sensitive */ \
170 if (-1 == test_asprintf(&buffer, "%ld", *(longintPtr))) { \
171 error = HTTP_ERROR_SERVER; \
172 goto leave; \
174 INSERT_STRING(parent, element, buffer) \
175 free(buffer); \
176 } else { INSERT_STRING(parent, element, NULL) } \
179 #define INSERT_ULONGINTPTR(parent, element, ulongintPtr) { \
180 if ((ulongintPtr)) { \
181 char *buffer = NULL; \
182 /* FIXME: locale sensitive */ \
183 if (-1 == test_asprintf(&buffer, "%lu", *(ulongintPtr))) { \
184 error = HTTP_ERROR_SERVER; \
185 goto leave; \
187 INSERT_STRING(parent, element, buffer) \
188 free(buffer); \
189 } else { INSERT_STRING(parent, element, NULL) } \
192 #define INSERT_BOOLEANPTR(parent, element, booleanPtr) { \
193 if (NULL != (booleanPtr)) { \
194 char *buffer = NULL; \
195 buffer = *(booleanPtr) ? "true" : "false"; \
196 INSERT_STRING(parent, element, buffer) \
197 } else { INSERT_STRING(parent, element, NULL) } \
200 #define INSERT_TIMEVALPTR(parent, element, timevalPtr) { \
201 if (NULL != (timevalPtr)) { \
202 char *buffer = NULL; \
203 error = timeval2timestring(timevalPtr, &buffer); \
204 if (error) { \
205 free(buffer); \
206 goto leave; \
208 INSERT_STRING(parent, element, buffer); \
209 free(buffer); \
210 } else { \
211 INSERT_STRING(parent, element, NULL); \
215 #define INSERT_TMPTR(parent, element, tmPtr) { \
216 if (NULL != (tmPtr)) { \
217 char *buffer = NULL; \
218 error = tm2datestring(tmPtr, &buffer); \
219 if (error) { \
220 free(buffer); \
221 goto leave; \
223 INSERT_STRING(parent, element, buffer); \
224 free(buffer); \
225 } else { \
226 INSERT_STRING(parent, element, NULL); \
230 #define INSERT_ELEMENT(child, parent, element) \
232 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
233 if (NULL == (child)) { \
234 error = HTTP_ERROR_SERVER; \
235 goto leave; \
239 /* TODO: These functions (element_exists(), extract_...(), ...) will replace
240 * the macros (ELEMENT_EXISTS, EXTRACT_...). The compiled code will be
241 * smaller, the compilation will be faster. */
243 /* Check an element exists.
244 * @code is a static output ISDS error code
245 * @error_message is a reallocated output ISDS error message
246 * @xpath_ctx is a current XPath context
247 * @element_name is name of an element to check
248 * @allow_multiple is false to require exactly one element. True to require
249 * one or more elements.
250 * @return HTTP_ERROR_SUCCESS or an appropriate error code. */
251 static http_error element_exists(const char **code, char **message,
252 xmlXPathContextPtr xpath_ctx, const char *element_name,
253 _Bool allow_multiple) {
254 xmlXPathObjectPtr result = NULL;
256 result = xmlXPathEvalExpression(BAD_CAST element_name, xpath_ctx);
257 if (NULL == result) {
258 return HTTP_ERROR_SERVER;
260 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
261 xmlXPathFreeObject(result);
262 test_asprintf(message, "Element %s does not exist", element_name);
263 return HTTP_ERROR_CLIENT;
264 } else {
265 if (!allow_multiple && result->nodesetval->nodeNr > 1) {
266 xmlXPathFreeObject(result);
267 test_asprintf(message, "Multiple %s element", element_name);
268 return HTTP_ERROR_CLIENT;
271 xmlXPathFreeObject(result);
273 return HTTP_ERROR_SUCCESS;
277 /* Extract @element_name's value as a string.
278 * @code is a static output ISDS error code
279 * @error_message is a reallocated output ISDS error message
280 * @xpath_ctx is a current XPath context
281 * @element_name is name of a element whose child text node to extract
282 * @string is the extraced allocated string value, or NULL if empty or the
283 * element does not exist.
284 * @return HTTP_ERROR_SUCCESS or an appropriate error code. */
285 static http_error extract_string(const char **code, char **message,
286 xmlXPathContextPtr xpath_ctx, const char *element_name,
287 char **string) {
288 http_error error = HTTP_ERROR_SUCCESS;
289 xmlXPathObjectPtr result = NULL;
290 char *buffer = NULL;
292 if (-1 == test_asprintf(&buffer, "%s/text()", element_name)) {
293 error = HTTP_ERROR_SERVER;
294 goto leave;
296 result = xmlXPathEvalExpression(BAD_CAST buffer, xpath_ctx);
297 free(buffer);
298 if (NULL == result) {
299 error = HTTP_ERROR_SERVER;
300 goto leave;
302 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
303 if (result->nodesetval->nodeNr > 1) {
304 xmlXPathFreeObject(result);
305 test_asprintf(message, "Multiple %s element", element_name);
306 error = HTTP_ERROR_CLIENT;
307 goto leave;
309 *string = (char *) \
310 xmlXPathCastNodeSetToString(result->nodesetval);
311 if (!(*string)) {
312 xmlXPathFreeObject(result);
313 error = HTTP_ERROR_SERVER;
314 goto leave;
317 xmlXPathFreeObject(result);
319 leave:
320 return error;
324 /* Checks an @element_name's value is an @expected_value string.
325 * @code is a static output ISDS error code
326 * @error_message is a reallocated output ISDS error message
327 * @xpath_ctx is a current XPath context
328 * @element_name is name of a element to check
329 * @must_exist is true if the @element_name must exist even if @expected_value
330 * is NULL.
331 * @expected_value is an expected string value
332 * @return HTTP_ERROR_SUCCESS if the @element_name element's value is
333 * @expected_value. HTTP_ERROR_CLIENT if not equaled, HTTP_ERROR_SERVER if an
334 * internal error occured. */
335 static http_error element_equals_string(const char **code, char **message,
336 xmlXPathContextPtr xpath_ctx, const char *element_name,
337 _Bool must_exist, const char *expected_value) {
338 http_error error = HTTP_ERROR_SUCCESS;
339 char *string = NULL;
341 if (must_exist) {
342 error = element_exists(code, message, xpath_ctx, element_name, 0);
343 if (HTTP_ERROR_SUCCESS != error)
344 goto leave;
347 error = extract_string(code, message, xpath_ctx, element_name, &string);
348 if (HTTP_ERROR_SUCCESS != error)
349 goto leave;
351 if (NULL != expected_value) {
352 if (NULL == string) {
353 *code = "9999";
354 test_asprintf(message, "Empty %s element", element_name);
355 error = HTTP_ERROR_CLIENT;
356 goto leave;
358 if (xmlStrcmp(BAD_CAST expected_value, BAD_CAST string)) {
359 *code = "9999";
360 test_asprintf(message,
361 "Unexpected %s element value: expected=`%s', got=`%s'",
362 element_name, expected_value, string);
363 error = HTTP_ERROR_CLIENT;
364 goto leave;
366 } else {
367 if (NULL != string && *string != '\0') {
368 *code = "9999";
369 test_asprintf(message,
370 "Unexpected %s element value: "
371 "expected empty string, got=`%s'",
372 element_name, string);
373 error = HTTP_ERROR_CLIENT;
374 goto leave;
378 leave:
379 free(string);
380 return error;
384 /* Checks an @element_name's value is an @expected_value integer.
385 * @code is a static output ISDS error code
386 * @error_message is a reallocated output ISDS error message
387 * @xpath_ctx is a current XPath context
388 * @element_name is name of a element to check
389 * @must_exist is true if the @element_name must exist even if @expected_value
390 * is NULL.
391 * @expected_value is an expected integer value.
392 * @return HTTP_ERROR_SUCCESS if the @element_name element's value is
393 * @expected_value. HTTP_ERROR_CLIENT if not equaled, HTTP_ERROR_SERVER if an
394 * internal error occured. */
395 static http_error element_equals_integer(const char **code, char **message,
396 xmlXPathContextPtr xpath_ctx, const char *element_name,
397 _Bool must_exist, const long int *expected_value) {
398 http_error error = HTTP_ERROR_SUCCESS;
399 char *string = NULL;
400 long int number;
401 char *endptr;
403 if (must_exist) {
404 error = element_exists(code, message, xpath_ctx, element_name, 0);
405 if (HTTP_ERROR_SUCCESS != error)
406 goto leave;
409 error = extract_string(code, message, xpath_ctx, element_name, &string);
410 if (HTTP_ERROR_SUCCESS != error)
411 goto leave;
413 if (NULL != expected_value) {
414 if (NULL == string) {
415 *code = "9999";
416 test_asprintf(message, "Empty %s element", element_name);
417 error = HTTP_ERROR_CLIENT;
418 goto leave;
420 number = strtol(string, &endptr, 10);
421 if (*endptr != '\0') {
422 *code = "9999";
423 test_asprintf(message,
424 "%s element value is not a valid integer: %s",
425 element_name, string);
426 error = HTTP_ERROR_CLIENT;
427 goto leave;
429 if (number == LONG_MIN || number == LONG_MAX) { \
430 *code = "9999";
431 test_asprintf(message, \
432 "%s element value is out of range of long int: %s",
433 element_name, string);
434 error = HTTP_ERROR_SERVER;
435 goto leave;
437 free(string); string = NULL;
438 if (number != *expected_value) {
439 *code = "9999";
440 test_asprintf(message,
441 "Unexpected %s element value: expected=`%ld', got=`%ld'",
442 element_name, *expected_value, number);
443 error = HTTP_ERROR_CLIENT;
444 goto leave;
446 } else {
447 if (NULL != string && *string != '\0') {
448 *code = "9999";
449 test_asprintf(message,
450 "Unexpected %s element value: expected no text node, got=`%s'",
451 element_name, string);
452 error = HTTP_ERROR_CLIENT;
453 goto leave;
457 leave:
458 free(string);
459 return error;
463 /* Checks an @element_name's value is an @expected_value boolean.
464 * @code is a static output ISDS error code
465 * @error_message is an reallocated output ISDS error message
466 * @xpath_ctx is a current XPath context
467 * @element_name is name of a element to check
468 * @must_exist is true if the @element_name must exist even if @expected_value
469 * is NULL.
470 * @expected_value is an expected boolean value
471 * @return HTTP_ERROR_SUCCESS if the @element_name element's value is
472 * @expected_value. HTTP_ERROR_CLIENT if not equaled, HTTP_ERROR_SERVER if an
473 * internal error occured. */
474 static http_error element_equals_boolean(const char **code, char **message,
475 xmlXPathContextPtr xpath_ctx, const char *element_name,
476 _Bool must_exist, const _Bool *expected_value) {
477 http_error error = HTTP_ERROR_SUCCESS;
478 char *string = NULL;
479 _Bool value;
481 if (must_exist) {
482 error = element_exists(code, message, xpath_ctx, element_name, 0);
483 if (HTTP_ERROR_SUCCESS != error)
484 goto leave;
487 error = extract_string(code, message, xpath_ctx, element_name, &string);
488 if (HTTP_ERROR_SUCCESS != error)
489 goto leave;
491 if (NULL != expected_value) {
492 if (NULL == string) {
493 *code = "9999";
494 test_asprintf(message, "Empty %s element", element_name);
495 error = HTTP_ERROR_CLIENT;
496 goto leave;
498 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") ||
499 !xmlStrcmp((xmlChar *)string, BAD_CAST "1"))
500 value = 1;
501 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") ||
502 !xmlStrcmp((xmlChar *)string, BAD_CAST "0"))
503 value = 0;
504 else {
505 *code = "9999";
506 test_asprintf(message,
507 "%s element value is not a valid boolean: %s",
508 element_name, string);
509 error = HTTP_ERROR_CLIENT;
510 goto leave;
512 if (*expected_value != value) {
513 *code = "9999";
514 test_asprintf(message,
515 "Unexpected %s element value: expected=%d, got=%d",
516 element_name, *expected_value, string);
517 error = HTTP_ERROR_CLIENT;
518 goto leave;
520 } else {
521 if (NULL != string && *string != '\0') {
522 *code = "9999";
523 test_asprintf(message,
524 "Unexpected %s element value: "
525 "expected empty string, got=`%s'",
526 element_name, string);
527 error = HTTP_ERROR_CLIENT;
528 goto leave;
532 leave:
533 free(string);
534 return error;
538 /* Insert dmStatus or similar subtree
539 * @parent is element to insert to
540 * @dm is true for dmStatus, otherwise dbStatus
541 * @code is status code as string
542 * @message is UTF-8 encoded message
543 * @db_ref_number is optinal reference number propagated if not @dm
544 * @return 0 on success, otherwise non-0. */
545 static http_error insert_isds_status(xmlNodePtr parent, _Bool dm,
546 const xmlChar *code, const xmlChar *message,
547 const xmlChar *db_ref_number) {
548 http_error error = HTTP_ERROR_SUCCESS;
549 xmlNodePtr status;
551 if (NULL == code || NULL == message) {
552 error = HTTP_ERROR_SERVER;
553 goto leave;
556 INSERT_ELEMENT(status, parent, (dm) ? "dmStatus" : "dbStatus");
557 INSERT_STRING(status, (dm) ? "dmStatusCode" : "dbStatusCode", code);
558 INSERT_STRING(status, (dm) ? "dmStatusMessage" : "dbStatusMessage", message);
559 if (!dm && NULL != db_ref_number) {
560 INSERT_STRING(status, "dbStatusRefNumber", db_ref_number);
563 leave:
564 return error;
568 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
569 static http_error tm2datestring(const struct tm *time, char **string) {
570 if (NULL == time || NULL == string) return HTTP_ERROR_SERVER;
572 if (-1 == test_asprintf(string, "%d-%02d-%02d",
573 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
574 return HTTP_ERROR_SERVER;
576 return HTTP_ERROR_SUCCESS;
580 /* Convert struct timeval *@time to UTF-8 ISO 8601 date-time @string. It
581 * respects the @time microseconds too. */
582 static http_error timeval2timestring(const struct timeval *time,
583 char **string) {
584 struct tm broken;
586 if (!time || !string) return HTTP_ERROR_SERVER;
588 if (!gmtime_r(&time->tv_sec, &broken)) return HTTP_ERROR_SERVER;
589 if (time->tv_usec < 0 || time->tv_usec > 999999) return HTTP_ERROR_SERVER;
591 /* TODO: small negative year should be formatted as "-0012". This is not
592 * true for glibc "%04d". We should implement it.
593 * TODO: What's type of time->tv_usec exactly? Unsigned? Absolute?
594 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
595 if (-1 == test_asprintf(string,
596 "%04d-%02d-%02dT%02d:%02d:%02d.%06ld",
597 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
598 broken.tm_hour, broken.tm_min, broken.tm_sec,
599 time->tv_usec))
600 return HTTP_ERROR_SERVER;
602 return HTTP_ERROR_SUCCESS;
606 /* Compare dates represented by pointer to struct tm.
607 * @return 0 if equalued, non-0 otherwise. */
608 static int datecmp(const struct tm *a, const struct tm *b) {
609 if (NULL == a && b == NULL) return 0;
610 if ((NULL == a && b != NULL) || (NULL != a && b == NULL)) return 1;
611 if (a->tm_year != b->tm_year) return 1;
612 if (a->tm_mon != b->tm_mon) return 1;
613 if (a->tm_mday != b->tm_mday) return 1;
614 return 0;
618 /* Implement DummyOperation */
619 static http_error service_DummyOperation(
620 const struct http_connection *connection, const xmlDocPtr soap_request,
621 xmlXPathContextPtr xpath_ctx, xmlNodePtr isds_request,
622 xmlDocPtr soap_response, xmlNodePtr isds_response,
623 const void *arguments) {
624 return insert_isds_status(isds_response, 1, BAD_CAST "0000",
625 BAD_CAST "Success", NULL);
629 /* Implement Re-signISDSDocument.
630 * It sends document from request back.
631 * @arguments is pointer to struct arguments_DS_Dz_ResignISDSDocument */
632 static http_error service_ResignISDSDocument(
633 const struct http_connection *connection,
634 const xmlDocPtr soap_request, xmlXPathContextPtr xpath_ctx,
635 const xmlNodePtr isds_request,
636 xmlDocPtr soap_response, xmlNodePtr isds_response,
637 const void *arguments) {
638 http_error error = HTTP_ERROR_SUCCESS;
639 const char *code = "9999";
640 char *message = NULL;
641 const struct arguments_DS_Dz_ResignISDSDocument *configuration =
642 (const struct arguments_DS_Dz_ResignISDSDocument *)arguments;
643 char *data = NULL;
645 if (NULL == configuration || NULL == configuration->status_code ||
646 NULL == configuration->status_message) {
647 error = HTTP_ERROR_SERVER;
648 goto leave;
651 EXTRACT_STRING("isds:dmDoc", data);
652 if (NULL == data) {
653 message = strdup("Missing isds:dmDoc");
654 error = HTTP_ERROR_CLIENT;
655 goto leave;
659 /* dmResultDoc is mandatory in response */
660 if (xmlStrcmp(BAD_CAST configuration->status_code, BAD_CAST "0000")) {
661 free(data);
662 data = NULL;
664 INSERT_STRING(isds_response, "dmResultDoc", data);
666 if (configuration->valid_to != NULL) {
667 error = tm2datestring(configuration->valid_to, &data);
668 if (error) {
669 message = strdup("Could not format date");
670 goto leave;
672 INSERT_STRING(isds_response, "dmValidTo", data);
675 code = configuration->status_code;
676 message = strdup(configuration->status_message);
678 leave:
679 if (HTTP_ERROR_SERVER != error) {
680 http_error next_error = insert_isds_status(isds_response, 1,
681 BAD_CAST code, BAD_CAST message, NULL);
682 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
684 free(data);
685 free(message);
686 return error;
690 /* Implement EraseMessage.
691 * @arguments is pointer to struct arguments_DS_DsManage_ChangeISDSPassword */
692 static http_error service_EraseMessage(const struct http_connection *connection,
693 const xmlDocPtr soap_request, xmlXPathContextPtr xpath_ctx,
694 const xmlNodePtr isds_request,
695 xmlDocPtr soap_response, xmlNodePtr isds_response,
696 const void *arguments) {
697 http_error error = HTTP_ERROR_SUCCESS;
698 char *code = "9999", *message = NULL;
699 const struct arguments_DS_Dx_EraseMessage *configuration =
700 (const struct arguments_DS_Dx_EraseMessage *)arguments;
701 char *message_id = NULL;
702 _Bool *incoming = NULL;
704 if (NULL == configuration || NULL == configuration->message_id) {
705 error = HTTP_ERROR_SERVER;
706 goto leave;
709 EXTRACT_STRING("isds:dmID", message_id);
710 if (NULL == message_id) {
711 message = strdup("Missing isds:dmID");
712 error = HTTP_ERROR_CLIENT;
713 goto leave;
715 EXTRACT_BOOLEAN("isds:dmIncoming", incoming);
716 if (NULL == incoming) {
717 message = strdup("Missing isds:dmIncoming");
718 error = HTTP_ERROR_CLIENT;
719 goto leave;
722 if (xmlStrcmp((const xmlChar *) configuration->message_id,
723 (const xmlChar *) message_id)) {
724 code = "1219";
725 message = strdup("Message is not in the long term storage");
726 error = HTTP_ERROR_CLIENT;
727 goto leave;
729 if (configuration->incoming != *incoming) {
730 code = "1219";
731 message = strdup("Message direction mismatches");
732 error = HTTP_ERROR_CLIENT;
733 goto leave;
736 code = "0000";
737 message = strdup("Success");
738 leave:
739 if (HTTP_ERROR_SERVER != error) {
740 http_error next_error = insert_isds_status(isds_response, 1,
741 BAD_CAST code, BAD_CAST message, NULL);
742 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
744 free(incoming);
745 free(message_id);
746 free(message);
747 return error;
751 /* Insert list of credit info as XSD:tCiRecord XML tree.
752 * @isds_response is XML node with the response
753 * @history is list of struct server_credit_event. If NULL, no ciRecord XML
754 * subtree will be created. */
755 static http_error insert_ciRecords(xmlNodePtr isds_response,
756 const struct server_list *history) {
757 http_error error = HTTP_ERROR_SUCCESS;
758 xmlNodePtr records, record;
760 if (NULL == isds_response) return HTTP_ERROR_SERVER;
761 if (NULL == history) return HTTP_ERROR_SUCCESS;
763 INSERT_ELEMENT(records, isds_response, "ciRecords");
764 for (const struct server_list *item = history; NULL != item;
765 item = item->next) {
766 const struct server_credit_event *event =
767 (struct server_credit_event*)item->data;
769 INSERT_ELEMENT(record, records, "ciRecord");
770 if (NULL == event) continue;
772 INSERT_TIMEVALPTR(record, "ciEventTime", event->time);
773 switch(event->type) {
774 case SERVER_CREDIT_CHARGED:
775 INSERT_STRING(record, "ciEventType", "1");
776 INSERT_STRING(record, "ciTransID",
777 event->details.charged.transaction);
778 break;
779 case SERVER_CREDIT_DISCHARGED:
780 INSERT_STRING(record, "ciEventType", "2");
781 INSERT_STRING(record, "ciTransID",
782 event->details.discharged.transaction);
783 break;
784 case SERVER_CREDIT_MESSAGE_SENT:
785 INSERT_STRING(record, "ciEventType", "3");
786 INSERT_STRING(record, "ciRecipientID",
787 event->details.message_sent.recipient);
788 INSERT_STRING(record, "ciPDZID",
789 event->details.message_sent.message_id);
790 break;
791 case SERVER_CREDIT_STORAGE_SET:
792 INSERT_STRING(record, "ciEventType", "4");
793 INSERT_LONGINTPTR(record, "ciNewCapacity",
794 &event->details.storage_set.new_capacity);
795 INSERT_TMPTR(record, "ciNewFrom",
796 event->details.storage_set.new_valid_from);
797 INSERT_TMPTR(record, "ciNewTo",
798 event->details.storage_set.new_valid_to);
799 INSERT_LONGINTPTR(record, "ciOldCapacity",
800 event->details.storage_set.old_capacity);
801 INSERT_TMPTR(record, "ciOldFrom",
802 event->details.storage_set.old_valid_from);
803 INSERT_TMPTR(record, "ciOldTo",
804 event->details.storage_set.old_valid_to);
805 INSERT_STRING(record, "ciDoneBy",
806 event->details.storage_set.initiator);
807 break;
808 case SERVER_CREDIT_EXPIRED:
809 INSERT_STRING(record, "ciEventType", "5");
810 break;
811 default:
812 error = HTTP_ERROR_SERVER;
813 goto leave;
815 INSERT_LONGINTPTR(record, "ciCreditChange", &event->credit_change);
816 INSERT_LONGINTPTR(record, "ciCreditAfter", &event->new_credit);
820 leave:
821 return error;
825 /* Implement DataBoxCreditInfo.
826 * @arguments is pointer to struct arguments_DS_df_DataBoxCreditInfo */
827 static http_error service_DataBoxCreditInfo(
828 const struct http_connection *connection,
829 const xmlDocPtr soap_request, xmlXPathContextPtr xpath_ctx,
830 const xmlNodePtr isds_request,
831 xmlDocPtr soap_response, xmlNodePtr isds_response,
832 const void *arguments) {
833 http_error error = HTTP_ERROR_SUCCESS;
834 const char *code = "9999";
835 char *message = NULL;
836 const struct arguments_DS_df_DataBoxCreditInfo *configuration =
837 (const struct arguments_DS_df_DataBoxCreditInfo *)arguments;
838 char *box_id = NULL;
839 struct tm *from_date = NULL, *to_date = NULL;
841 if (NULL == configuration || NULL == configuration->status_code ||
842 NULL == configuration->status_message) {
843 error = HTTP_ERROR_SERVER;
844 goto leave;
847 EXTRACT_STRING("isds:dbID", box_id);
848 if (NULL == box_id) {
849 message = strdup("Missing isds:dbID");
850 error = HTTP_ERROR_CLIENT;
851 goto leave;
853 if (NULL != configuration->box_id &&
854 xmlStrcmp(BAD_CAST configuration->box_id,
855 BAD_CAST box_id)) {
856 code = "9999";
857 message = strdup("Unexpected isds:dbID value");
858 error = HTTP_ERROR_CLIENT;
859 goto leave;
862 ELEMENT_EXISTS("isds:ciFromDate", 0);
863 EXTRACT_DATE("isds:ciFromDate", from_date);
864 if (datecmp(configuration->from_date, from_date)) {
865 code = "9999";
866 message = strdup("Unexpected isds:ciFromDate value");
867 error = HTTP_ERROR_CLIENT;
868 goto leave;
871 ELEMENT_EXISTS("isds:ciTodate", 0);
872 EXTRACT_DATE("isds:ciTodate", to_date);
873 if (datecmp(configuration->to_date, to_date)) {
874 code = "9999";
875 message = strdup("Unexpected isds:ciTodate value");
876 error = HTTP_ERROR_CLIENT;
877 goto leave;
880 INSERT_LONGINTPTR(isds_response, "currentCredit",
881 &configuration->current_credit);
882 INSERT_STRING(isds_response, "notifEmail", configuration->email);
883 if ((error = insert_ciRecords(isds_response, configuration->history))) {
884 goto leave;
887 code = configuration->status_code;
888 message = strdup(configuration->status_message);
889 leave:
890 if (HTTP_ERROR_SERVER != error) {
891 http_error next_error = insert_isds_status(isds_response, 0,
892 BAD_CAST code, BAD_CAST message, NULL);
893 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
895 free(box_id);
896 free(from_date);
897 free(to_date);
898 free(message);
899 return error;
903 /* Insert list of fulltext search results as XSD:tdbResultsArray XML tree.
904 * @isds_response is XML node with the response
905 * @results is list of struct server_db_result *.
906 * @create_empty_root is true to create dbResults element even if @results is
907 * empty. */
908 static http_error insert_tdbResultsArray(xmlNodePtr isds_response,
909 const struct server_list *results, _Bool create_empty_root) {
910 http_error error = HTTP_ERROR_SUCCESS;
911 xmlNodePtr root, entry;
913 if (NULL == isds_response) return HTTP_ERROR_SERVER;
915 if (NULL != results || create_empty_root)
916 INSERT_ELEMENT(root, isds_response, "dbResults");
918 if (NULL == results) return HTTP_ERROR_SUCCESS;
920 for (const struct server_list *item = results; NULL != item;
921 item = item->next) {
922 const struct server_db_result *result =
923 (struct server_db_result *)item->data;
925 INSERT_ELEMENT(entry, root, "dbResult");
926 if (NULL == result) continue;
928 INSERT_STRING(entry, "dbID", result->id);
929 INSERT_STRING(entry, "dbType", result->type);
930 INSERT_STRING(entry, "dbName", result->name);
931 INSERT_STRING(entry, "dbAddress", result->address);
932 INSERT_TMPTR(entry, "dbBiDate", result->birth_date);
933 INSERT_STRING(entry, "dbICO", result->ic);
934 INSERT_BOOLEANPTR(entry, "dbEffectiveOVM", &result->ovm);
935 INSERT_STRING(entry, "dbSendOptions", result->send_options);
938 leave:
939 return error;
943 /* Implement ISDSSearch2.
944 * @arguments is pointer to struct arguments_DS_df_ISDSSearch2 */
945 static http_error service_ISDSSearch2(
946 const struct http_connection *connection,
947 const xmlDocPtr soap_request, xmlXPathContextPtr xpath_ctx,
948 const xmlNodePtr isds_request,
949 xmlDocPtr soap_response, xmlNodePtr isds_response,
950 const void *arguments) {
951 http_error error = HTTP_ERROR_SUCCESS;
952 const char *code = "9999";
953 char *message = NULL;
954 const struct arguments_DS_df_ISDSSearch2 *configuration =
955 (const struct arguments_DS_df_ISDSSearch2 *)arguments;
956 char *string = NULL;
958 if (NULL == configuration || NULL == configuration->status_code ||
959 NULL == configuration->status_message) {
960 error = HTTP_ERROR_SERVER;
961 goto leave;
964 /* Check request */
965 EXTRACT_STRING("isds:searchText", string);
966 if (NULL == string) {
967 message = strdup("Missing or empty isds:searchText");
968 error = HTTP_ERROR_CLIENT;
969 goto leave;
971 if (NULL != configuration->search_text &&
972 xmlStrcmp(BAD_CAST configuration->search_text,
973 BAD_CAST string)) {
974 code = "9999";
975 message = strdup("Unexpected isds:searchText value");
976 error = HTTP_ERROR_CLIENT;
977 goto leave;
979 free(string); string = NULL;
981 error = element_equals_string(&code, &message, xpath_ctx,
982 "isds:searchType", 1, configuration->search_type);
983 if (error) goto leave;
985 error = element_equals_string(&code, &message, xpath_ctx,
986 "isds:searchScope", 1, configuration->search_scope);
987 if (error) goto leave;
989 error = element_equals_integer(&code, &message, xpath_ctx,
990 "isds:page", 1, configuration->search_page_number);
991 if (error) goto leave;
993 error = element_equals_integer(&code, &message, xpath_ctx,
994 "isds:pageSize", 1, configuration->search_page_size);
995 if (error) goto leave;
997 error = element_equals_boolean(&code, &message, xpath_ctx,
998 "isds:highlighting", 0, configuration->search_highlighting_value);
999 if (error) goto leave;
1001 /* Build response */
1002 if (NULL != configuration->total_count)
1003 INSERT_ULONGINTPTR(isds_response, "totalCount",
1004 configuration->total_count);
1005 if (NULL != configuration->current_count)
1006 INSERT_ULONGINTPTR(isds_response, "currentCount",
1007 configuration->current_count);
1008 if (NULL != configuration->position)
1009 INSERT_ULONGINTPTR(isds_response, "position",
1010 configuration->position);
1011 if (NULL != configuration->last_page)
1012 INSERT_BOOLEANPTR(isds_response, "lastPage",
1013 configuration->last_page);
1014 if ((error = insert_tdbResultsArray(isds_response, configuration->results,
1015 configuration->results_exists))) {
1016 goto leave;
1019 code = configuration->status_code;
1020 message = strdup(configuration->status_message);
1022 leave:
1023 if (HTTP_ERROR_SERVER != error) {
1024 http_error next_error = insert_isds_status(isds_response, 0,
1025 BAD_CAST code, BAD_CAST message, NULL);
1026 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
1028 free(string);
1029 free(message);
1030 return error;
1034 /* Common part for ChangeISDSPassword and ChangePasswordOTP.
1035 * @code is output pointer to static string
1036 * @pass_message is output pointer to auto-allocated string
1037 * @arguments is pointer to struct arguments_DS_DsManage_ChangeISDSPassword */
1038 static http_error check_passwd(
1039 const char *username, const char *current_password,
1040 xmlXPathContextPtr xpath_ctx,
1041 char **code, char **pass_message) {
1042 http_error error = HTTP_ERROR_SUCCESS;
1043 char *message = NULL;
1044 char *old_password = NULL, *new_password = NULL;
1045 size_t length;
1047 if (NULL == username || NULL == current_password ||
1048 NULL == code || NULL == pass_message) {
1049 return HTTP_ERROR_SERVER;
1052 *code = "9999";
1055 /* Parse request */
1056 EXTRACT_STRING("isds:dbOldPassword", old_password);
1057 if (NULL == old_password) {
1058 message = strdup("Empty isds:dbOldPassword");
1059 error = HTTP_ERROR_CLIENT;
1060 goto leave;
1062 EXTRACT_STRING("isds:dbNewPassword", new_password);
1063 if (NULL == new_password) {
1064 message = strdup("Empty isds:dbOldPassword");
1065 error = HTTP_ERROR_CLIENT;
1066 goto leave;
1069 /* Check defined cases */
1070 if (strcmp(current_password, old_password)) {
1071 *code = "1090";
1072 message = strdup("Bad current password");
1073 error = HTTP_ERROR_CLIENT;
1074 goto leave;
1077 length = strlen(new_password);
1079 if (length < 8 || length > 32) {
1080 *code = "1066";
1081 message = strdup("Too short or too long");
1082 error = HTTP_ERROR_CLIENT;
1083 goto leave;
1087 const char lower[] = "abcdefghijklmnopqrstuvwxyz";
1088 const char upper[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1089 const char digit[] = "0123456789";
1090 const char special[] = "!#$%&()*+,-.:=?@[]_{}|~";
1091 _Bool has_lower = 0, has_upper = 0, has_digit=0;
1093 for (int i = 0; i < length; i++) {
1094 if (NULL != strchr(lower, new_password[i]))
1095 has_lower = 1;
1096 else if (NULL != strchr(upper, new_password[i]))
1097 has_upper = 1;
1098 else if (NULL != strchr(digit, new_password[i]))
1099 has_digit = 1;
1100 else if (NULL == strchr(special, new_password[i])) {
1101 *code = "1079";
1102 message = strdup("Password contains forbidden character");
1103 error = HTTP_ERROR_CLIENT;
1104 goto leave;
1108 if (!has_lower || !has_upper || !has_digit) {
1109 *code = "1080";
1110 message = strdup("Password does not contain lower cased letter, "
1111 "upper cased letter and a digit");
1112 error = HTTP_ERROR_CLIENT;
1113 goto leave;
1117 if (!strcmp(old_password, new_password)) {
1118 *code = "1067";
1119 message = strdup("New password same as current one");
1120 error = HTTP_ERROR_CLIENT;
1121 goto leave;
1124 if (NULL != strstr(new_password, username)) {
1125 *code = "1082";
1126 message = strdup("New password contains user ID");
1127 error = HTTP_ERROR_CLIENT;
1128 goto leave;
1131 for (int i = 0; i < length - 2; i++) {
1132 if (new_password[i] == new_password[i+1] &&
1133 new_password[i] == new_password[i+2]) {
1134 *code = "1083";
1135 message = strdup("Password contains sequence "
1136 "of three identical characters");
1137 error = HTTP_ERROR_CLIENT;
1138 goto leave;
1143 const char *forbidden_prefix[] = { "qwert", "asdgf", "12345" };
1144 for (int i = 0; i < sizeof(forbidden_prefix)/sizeof(*forbidden_prefix);
1145 i++) {
1146 if (!strncmp(new_password, forbidden_prefix[i],
1147 strlen(forbidden_prefix[i]))) {
1148 *code = "1083";
1149 message = strdup("Password has forbidden prefix");
1150 error = HTTP_ERROR_CLIENT;
1151 goto leave;
1156 *code = "0000";
1157 message = strdup("Success");
1158 leave:
1159 free(old_password);
1160 free(new_password);
1161 *pass_message = message;
1162 return error;
1166 /* Implement ChangeISDSPassword.
1167 * @arguments is pointer to struct arguments_DS_DsManage_ChangeISDSPassword */
1168 static http_error service_ChangeISDSPassword(
1169 const struct http_connection *connection,
1170 const xmlDocPtr soap_request, xmlXPathContextPtr xpath_ctx,
1171 const xmlNodePtr isds_request,
1172 xmlDocPtr soap_response, xmlNodePtr isds_response,
1173 const void *arguments) {
1174 http_error error = HTTP_ERROR_SUCCESS;
1175 char *code = "9999", *message = NULL;
1176 const struct arguments_DS_DsManage_ChangeISDSPassword *configuration =
1177 (const struct arguments_DS_DsManage_ChangeISDSPassword *)arguments;
1179 if (NULL == configuration || NULL == configuration->username ||
1180 NULL == configuration->current_password) {
1181 error = HTTP_ERROR_SERVER;
1182 goto leave;
1185 /* Check for common password rules */
1186 error = check_passwd(
1187 configuration->username, configuration->current_password,
1188 xpath_ctx, &code, &message);
1190 leave:
1191 if (HTTP_ERROR_SERVER != error) {
1192 http_error next_error = insert_isds_status(isds_response, 0,
1193 BAD_CAST code, BAD_CAST message, NULL);
1194 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
1196 free(message);
1197 return error;
1201 /* Implement ChangePasswordOTP.
1202 * @arguments is pointer to struct
1203 * arguments_asws_changePassword_ChangePasswordOTP */
1204 static http_error service_ChangePasswordOTP(
1205 const struct http_connection *connection,
1206 const xmlDocPtr soap_request, xmlXPathContextPtr xpath_ctx,
1207 const xmlNodePtr isds_request,
1208 xmlDocPtr soap_response, xmlNodePtr isds_response,
1209 const void *arguments) {
1210 http_error error = HTTP_ERROR_SUCCESS;
1211 char *code = "9999", *message = NULL;
1212 const struct arguments_asws_changePassword_ChangePasswordOTP *configuration
1213 = (const struct arguments_asws_changePassword_ChangePasswordOTP *)
1214 arguments;
1215 char *method = NULL;
1217 if (NULL == configuration || NULL == configuration->username ||
1218 NULL == configuration->current_password) {
1219 error = HTTP_ERROR_SERVER;
1220 goto leave;
1223 /* Chek for OTP method */
1224 EXTRACT_STRING("isds:dbOTPType", method);
1225 if (NULL == method) {
1226 message = strdup("Empty isds:dbOTPType");
1227 error = HTTP_ERROR_CLIENT;
1228 goto leave;
1230 if ((configuration->method == AUTH_OTP_HMAC && strcmp(method, "HOTP")) ||
1231 (configuration->method == AUTH_OTP_TIME && strcmp(method, "TOTP"))) {
1232 message = strdup("isds:dbOTPType does not match OTP method");
1233 error = HTTP_ERROR_CLIENT;
1234 goto leave;
1237 /* Check for common password rules */
1238 error = check_passwd(
1239 configuration->username, configuration->current_password,
1240 xpath_ctx, &code, &message);
1242 leave:
1243 if (HTTP_ERROR_SERVER != error) {
1244 http_error next_error = insert_isds_status(isds_response, 0,
1245 BAD_CAST code, BAD_CAST message,
1246 BAD_CAST configuration->reference_number);
1247 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
1249 free(message);
1250 free(method);
1251 return error;
1255 /* Implement SendSMSCode.
1256 * @arguments is pointer to struct arguments_asws_changePassword_SendSMSCode */
1257 static http_error service_SendSMSCode(
1258 const struct http_connection *connection,
1259 const xmlDocPtr soap_request, xmlXPathContextPtr xpath_ctx,
1260 const xmlNodePtr isds_request,
1261 xmlDocPtr soap_response, xmlNodePtr isds_response,
1262 const void *arguments) {
1263 const struct arguments_asws_changePassword_SendSMSCode *configuration
1264 = (const struct arguments_asws_changePassword_SendSMSCode *)
1265 arguments;
1267 if (NULL == configuration || NULL == configuration->status_code ||
1268 NULL == configuration->status_message) {
1269 return HTTP_ERROR_SERVER;
1272 return insert_isds_status(isds_response, 0,
1273 BAD_CAST configuration->status_code,
1274 BAD_CAST configuration->status_message,
1275 BAD_CAST configuration->reference_number);
1279 /* List of implemented services */
1280 static struct service services[] = {
1281 { SERVICE_DS_Dz_DummyOperation,
1282 "DS/dz", BAD_CAST ISDS_NS, BAD_CAST "DummyOperation",
1283 service_DummyOperation },
1284 { SERVICE_DS_Dz_ResignISDSDocument,
1285 "DS/dz", BAD_CAST ISDS_NS, BAD_CAST "Re-signISDSDocument",
1286 service_ResignISDSDocument },
1287 { SERVICE_DS_df_DataBoxCreditInfo,
1288 "DS/df", BAD_CAST ISDS_NS, BAD_CAST "DataBoxCreditInfo",
1289 service_DataBoxCreditInfo },
1290 { SERVICE_DS_df_ISDSSearch2,
1291 "DS/df", BAD_CAST ISDS_NS, BAD_CAST "ISDSSearch2",
1292 service_ISDSSearch2 },
1293 { SERVICE_DS_DsManage_ChangeISDSPassword,
1294 "DS/DsManage", BAD_CAST ISDS_NS, BAD_CAST "ChangeISDSPassword",
1295 service_ChangeISDSPassword },
1296 { SERVICE_DS_Dx_EraseMessage,
1297 "DS/dx", BAD_CAST ISDS_NS, BAD_CAST "EraseMessage",
1298 service_EraseMessage },
1299 { SERVICE_asws_changePassword_ChangePasswordOTP,
1300 "/asws/changePassword", BAD_CAST OISDS_NS, BAD_CAST "ChangePasswordOTP",
1301 service_ChangePasswordOTP },
1302 { SERVICE_asws_changePassword_SendSMSCode,
1303 "/asws/changePassword", BAD_CAST OISDS_NS, BAD_CAST "SendSMSCode",
1304 service_SendSMSCode },
1308 /* Makes known all relevant namespaces to given XPath context
1309 * @xpath_ctx is XPath context
1310 * @otp_ns selects name space for the request and response know as "isds".
1311 * Use true for OTP-authenticated password change services, otherwise false.
1312 * @message_ns selects proper message name space. Unsigned and signed
1313 * messages and delivery info's differ in prefix and URI.
1314 * @return 0 in success, otherwise not 0. */
1315 static int register_namespaces(xmlXPathContextPtr xpath_ctx,
1316 const _Bool otp_ns, const message_ns_type message_ns) {
1317 const xmlChar *service_namespace = NULL;
1318 const xmlChar *message_namespace = NULL;
1320 if (!xpath_ctx) return -1;
1322 if (otp_ns) {
1323 service_namespace = BAD_CAST OISDS_NS;
1324 } else {
1325 service_namespace = BAD_CAST ISDS_NS;
1328 switch(message_ns) {
1329 case MESSAGE_NS_1:
1330 message_namespace = BAD_CAST ISDS1_NS; break;
1331 case MESSAGE_NS_UNSIGNED:
1332 message_namespace = BAD_CAST ISDS_NS; break;
1333 case MESSAGE_NS_SIGNED_INCOMING:
1334 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
1335 case MESSAGE_NS_SIGNED_OUTGOING:
1336 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
1337 case MESSAGE_NS_SIGNED_DELIVERY:
1338 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
1339 default:
1340 return -1;
1343 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
1344 return -1;
1345 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", service_namespace))
1346 return -1;
1347 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
1348 return -1;
1349 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
1350 return -1;
1351 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
1352 return -1;
1353 return 0;
1357 /* Parse soap request, pass it to service endpoint and respond to it.
1358 * It sends final HTTP response. */
1359 void soap(const struct http_connection *connection,
1360 const struct service_configuration *configuration,
1361 const void *request, size_t request_length, const char *end_point) {
1362 xmlDocPtr request_doc = NULL;
1363 xmlXPathContextPtr xpath_ctx = NULL;
1364 xmlXPathObjectPtr request_soap_body = NULL;
1365 xmlNodePtr isds_request = NULL; /* pointer only */
1366 _Bool service_handled = 0, service_passed = 0;
1367 xmlDocPtr response_doc = NULL;
1368 xmlNodePtr response_soap_envelope = NULL, response_soap_body = NULL,
1369 isds_response = NULL;
1370 xmlNsPtr soap_ns = NULL, isds_ns = NULL;
1371 char *response_name = NULL;
1372 xmlBufferPtr http_response_body = NULL;
1373 xmlSaveCtxtPtr save_ctx = NULL;
1376 if (NULL == configuration) {
1377 http_send_response_500(connection,
1378 "Second argument of soap() is NULL");
1379 return;
1382 if (NULL == request || request_length == 0) {
1383 http_send_response_400(connection, "Client sent empty body");
1384 return;
1387 request_doc = xmlParseMemory(request, request_length);
1388 if (NULL == request_doc) {
1389 http_send_response_400(connection, "Client sent invalid XML document");
1390 return;
1393 xpath_ctx = xmlXPathNewContext(request_doc);
1394 if (NULL == xpath_ctx) {
1395 xmlFreeDoc(request_doc);
1396 http_send_response_500(connection, "Could not create XPath context");
1397 return;
1400 if (register_namespaces(xpath_ctx, 0, MESSAGE_NS_UNSIGNED)) {
1401 xmlXPathFreeContext(xpath_ctx);
1402 xmlFreeDoc(request_doc);
1403 http_send_response_500(connection,
1404 "Could not register name spaces to the XPath context");
1405 return;
1408 /* Get SOAP Body */
1409 request_soap_body = xmlXPathEvalExpression(
1410 BAD_CAST "/soap:Envelope/soap:Body", xpath_ctx);
1411 if (NULL == request_soap_body) {
1412 xmlXPathFreeContext(xpath_ctx);
1413 xmlFreeDoc(request_doc);
1414 http_send_response_400(connection, "Client sent invalid SOAP request");
1415 return;
1417 if (xmlXPathNodeSetIsEmpty(request_soap_body->nodesetval)) {
1418 xmlXPathFreeObject(request_soap_body);
1419 xmlXPathFreeContext(xpath_ctx);
1420 xmlFreeDoc(request_doc);
1421 http_send_response_400(connection,
1422 "SOAP request does not contain SOAP Body element");
1423 return;
1425 if (request_soap_body->nodesetval->nodeNr > 1) {
1426 xmlXPathFreeObject(request_soap_body);
1427 xmlXPathFreeContext(xpath_ctx);
1428 xmlFreeDoc(request_doc);
1429 http_send_response_400(connection,
1430 "SOAP response has more than one Body element");
1431 return;
1433 isds_request = request_soap_body->nodesetval->nodeTab[0]->children;
1434 if (isds_request->next != NULL) {
1435 xmlXPathFreeObject(request_soap_body);
1436 xmlXPathFreeContext(xpath_ctx);
1437 xmlFreeDoc(request_doc);
1438 http_send_response_400(connection, "SOAP body has more than one child");
1439 return;
1441 if (isds_request->type != XML_ELEMENT_NODE || isds_request->ns == NULL ||
1442 NULL == isds_request->ns->href) {
1443 xmlXPathFreeObject(request_soap_body);
1444 xmlXPathFreeContext(xpath_ctx);
1445 xmlFreeDoc(request_doc);
1446 http_send_response_400(connection,
1447 "SOAP body does not contain a name-space-qualified element");
1448 return;
1451 /* Build SOAP response envelope */
1452 response_doc = xmlNewDoc(BAD_CAST "1.0");
1453 if (!response_doc) {
1454 http_send_response_500(connection,
1455 "Could not build SOAP response document");
1456 goto leave;
1458 response_soap_envelope = xmlNewNode(NULL, BAD_CAST "Envelope");
1459 if (!response_soap_envelope) {
1460 http_send_response_500(connection,
1461 "Could not build SOAP response envelope");
1462 goto leave;
1464 xmlDocSetRootElement(response_doc, response_soap_envelope);
1465 /* Only this way we get namespace definition as @xmlns:soap,
1466 * otherwise we get namespace prefix without definition */
1467 soap_ns = xmlNewNs(response_soap_envelope, BAD_CAST SOAP_NS, NULL);
1468 if(NULL == soap_ns) {
1469 http_send_response_500(connection, "Could not create SOAP name space");
1470 goto leave;
1472 xmlSetNs(response_soap_envelope, soap_ns);
1473 response_soap_body = xmlNewChild(response_soap_envelope, NULL,
1474 BAD_CAST "Body", NULL);
1475 if (!response_soap_body) {
1476 http_send_response_500(connection,
1477 "Could not add Body to SOAP response envelope");
1478 goto leave;
1480 /* Append ISDS response element */
1481 if (-1 == test_asprintf(&response_name, "%s%s", isds_request->name,
1482 "Response")) {
1483 http_send_response_500(connection,
1484 "Could not buld ISDS resposne element name");
1485 goto leave;
1487 isds_response = xmlNewChild(response_soap_body, NULL,
1488 BAD_CAST response_name, NULL);
1489 free(response_name);
1490 if (NULL == isds_response) {
1491 http_send_response_500(connection,
1492 "Could not add ISDS response element to SOAP response body");
1493 goto leave;
1495 isds_ns = xmlNewNs(isds_response, isds_request->ns->href, NULL);
1496 if(NULL == isds_ns) {
1497 http_send_response_500(connection,
1498 "Could not create a name space for the response body");
1499 goto leave;
1501 xmlSetNs(isds_response, isds_ns);
1503 /* Dispatch request to service */
1504 for (int i = 0; i < sizeof(services)/sizeof(services[0]); i++) {
1505 if (!strcmp(services[i].end_point, end_point) &&
1506 !xmlStrcmp(services[i].name_space, isds_request->ns->href) &&
1507 !xmlStrcmp(services[i].name, isds_request->name)) {
1508 /* Check if the configuration is enabled and find configuration */
1509 for (const struct service_configuration *service = configuration;
1510 service->name != SERVICE_END; service++) {
1511 if (service->name == services[i].id) {
1512 service_handled = 1;
1513 if (!xmlStrcmp(services[i].name_space, BAD_CAST OISDS_NS)) {
1514 /* Alias "isds" XPath identifier to OISDS_NS */
1515 if (register_namespaces(xpath_ctx, 1,
1516 MESSAGE_NS_UNSIGNED)) {
1517 http_send_response_500(connection,
1518 "Could not register name spaces to the "
1519 "XPath context");
1520 break;
1523 xpath_ctx->node = isds_request;
1524 if (HTTP_ERROR_SERVER != services[i].function(connection,
1525 request_doc, xpath_ctx, isds_request,
1526 response_doc, isds_response,
1527 service->arguments)) {
1528 service_passed = 1;
1529 } else {
1530 http_send_response_500(connection,
1531 "Internal server error while processing "
1532 "ISDS request");
1536 break;
1540 /* Send response */
1541 if (service_passed) {
1542 /* Serialize the SOAP response */
1543 http_response_body = xmlBufferCreate();
1544 if (NULL == http_response_body) {
1545 http_send_response_500(connection,
1546 "Could not create xmlBuffer for response serialization");
1547 goto leave;
1549 /* Last argument 1 means format the XML tree. This is pretty but it breaks
1550 * XML document transport as it adds text nodes (indentiation) between
1551 * elements. */
1552 save_ctx = xmlSaveToBuffer(http_response_body, "UTF-8", 0);
1553 if (NULL == save_ctx) {
1554 http_send_response_500(connection, "Could not create XML serializer");
1555 goto leave;
1557 /* XXX: According LibXML documentation, this function does not return
1558 * meaningful value yet */
1559 xmlSaveDoc(save_ctx, response_doc);
1560 if (-1 == xmlSaveFlush(save_ctx)) {
1561 http_send_response_500(connection,
1562 "Could not serialize SOAP response");
1563 goto leave;
1566 http_send_response_200(connection, http_response_body->content,
1567 http_response_body->use, soap_mime_type);
1570 leave:
1571 xmlSaveClose(save_ctx);
1572 xmlBufferFree(http_response_body);
1574 xmlFreeDoc(response_doc);
1576 xmlXPathFreeObject(request_soap_body);
1577 xmlXPathFreeContext(xpath_ctx);
1578 xmlFreeDoc(request_doc);
1580 if (!service_handled) {
1581 http_send_response_500(connection,
1582 "Requested ISDS service not implemented");