tests: Adapt utf8locale to musl
[libisds.git] / test / simline / service.c
blobfea6e87e1caa14befa92e8bb4069e377638e926a
1 #include "../../config.h"
2 #define _XOPEN_SOURCE XOPEN_SOURCE_LEVEL_FOR_STRDUP
3 #include "../test-tools.h"
4 #include "http.h"
5 #include "services.h"
6 #include "system.h"
7 #include <string.h>
8 #include <stdint.h> /* For intmax_t */
9 #include <stdlib.h> /* For free() */
10 #include <inttypes.h> /* For PRIdMAX */
11 #include <ctype.h> /* for isdigit() */
12 #include <libxml/parser.h>
13 #include <libxml/xpath.h>
14 #include <libxml/xpathInternals.h>
15 #include <libxml/xmlsave.h>
17 static const char *soap_mime_type = "text/xml"; /* SOAP/1.1 requires text/xml */
19 /* Used to choose proper name space for message elements.
20 * See _isds_register_namespaces(). */
21 typedef enum {
22 MESSAGE_NS_1,
23 MESSAGE_NS_UNSIGNED,
24 MESSAGE_NS_SIGNED_INCOMING,
25 MESSAGE_NS_SIGNED_OUTGOING,
26 MESSAGE_NS_SIGNED_DELIVERY,
27 MESSAGE_NS_OTP
28 } message_ns_type;
30 #define SOAP_NS "http://schemas.xmlsoap.org/soap/envelope/"
31 #define SOAP2_NS "http://www.w3.org/2003/05/soap-envelope"
32 #define ISDS1_NS "http://isds.czechpoint.cz"
33 #define ISDS_NS "http://isds.czechpoint.cz/v20"
34 #define OISDS_NS "http://isds.czechpoint.cz/v20/asws"
35 #define SISDS_INCOMING_NS "http://isds.czechpoint.cz/v20/message"
36 #define SISDS_OUTGOING_NS "http://isds.czechpoint.cz/v20/SentMessage"
37 #define SISDS_DELIVERY_NS "http://isds.czechpoint.cz/v20/delivery"
38 #define SCHEMA_NS "http://www.w3.org/2001/XMLSchema"
39 #define DEPOSIT_NS "urn:uschovnaWSDL"
42 struct service {
43 service_id id;
44 const char *end_point;
45 const xmlChar *name_space;
46 const xmlChar *name;
47 http_error (*function) (
48 xmlXPathContextPtr, xmlNodePtr,
49 const void *arguments);
53 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
54 * It respects microseconds too. Microseconds are rounded half up.
55 * In case of error, @time will be freed. */
56 static http_error timestring2timeval(const char *string,
57 struct timeval **time) {
58 struct tm broken;
59 char *offset, *delim, *endptr;
60 const int subsecond_resolution = 6;
61 char subseconds[subsecond_resolution + 1];
62 _Bool round_up = 0;
63 int offset_hours, offset_minutes;
64 int i;
65 long int long_number;
66 #ifdef _WIN32
67 int tmp;
68 #endif
70 if (!time) return HTTP_ERROR_SERVER;
71 if (!string) {
72 free(*time);
73 *time = NULL;
74 return HTTP_ERROR_CLIENT;
77 memset(&broken, 0, sizeof(broken));
79 if (!*time) {
80 *time = calloc(1, sizeof(**time));
81 if (!*time) return HTTP_ERROR_SERVER;
82 } else {
83 memset(*time, 0, sizeof(**time));
87 /* xsd:date is ISO 8601 string, thus ASCII */
88 /*TODO: negative year */
90 #ifdef _WIN32
91 i = 0;
92 if ((tmp = sscanf((const char*)string, "%d-%d-%dT%d:%d:%d%n",
93 &broken.tm_year, &broken.tm_mon, &broken.tm_mday,
94 &broken.tm_hour, &broken.tm_min, &broken.tm_sec,
95 &i)) < 6) {
96 free(*time);
97 *time = NULL;
98 return HTTP_ERROR_CLIENT;
101 broken.tm_year -= 1900;
102 broken.tm_mon--;
103 broken.tm_isdst = -1;
104 offset = (char*)string + i;
105 #else
106 /* Parse date and time without subseconds and offset */
107 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
108 if (!offset) {
109 free(*time);
110 *time = NULL;
111 return HTTP_ERROR_CLIENT;
113 #endif
115 /* Get subseconds */
116 if (*offset == '.' ) {
117 offset++;
119 /* Copy first 6 digits, pad it with zeros.
120 * Current server implementation uses only millisecond resolution. */
121 /* TODO: isdigit() is locale sensitive */
122 for (i = 0;
123 i < subsecond_resolution && isdigit(*offset);
124 i++, offset++) {
125 subseconds[i] = *offset;
127 if (subsecond_resolution == i && isdigit(*offset)) {
128 /* Check 7th digit for rounding */
129 if (*offset >= '5') round_up = 1;
130 offset++;
132 for (; i < subsecond_resolution; i++) {
133 subseconds[i] = '0';
135 subseconds[subsecond_resolution] = '\0';
137 /* Convert it into integer */
138 long_number = strtol(subseconds, &endptr, 10);
139 if (*endptr != '\0' || long_number == LONG_MIN ||
140 long_number == LONG_MAX) {
141 free(*time);
142 *time = NULL;
143 return HTTP_ERROR_SERVER;
145 /* POSIX sys_time.h(0p) defines tv_usec timeval member as su_seconds_t
146 * type. sys_types.h(0p) defines su_seconds_t as "used for time in
147 * microseconds" and "the type shall be a signed integer capable of
148 * storing values at least in the range [-1, 1000000]. */
149 if (long_number < -1 || long_number >= 1000000) {
150 free(*time);
151 *time = NULL;
152 return HTTP_ERROR_CLIENT;
154 (*time)->tv_usec = long_number;
156 /* Round the subseconds */
157 if (round_up) {
158 if (999999 == (*time)->tv_usec) {
159 (*time)->tv_usec = 0;
160 broken.tm_sec++;
161 } else {
162 (*time)->tv_usec++;
166 /* move to the zone offset delimiter or signal NULL*/
167 delim = strchr(offset, '-');
168 if (!delim)
169 delim = strchr(offset, '+');
170 if (!delim)
171 delim = strchr(offset, 'Z');
172 offset = delim;
175 /* Get zone offset */
176 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
177 * "" equals to "Z" and it means UTC zone. */
178 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
179 * colon separator */
180 if (offset && (*offset == '-' || *offset == '+')) {
181 if (2 != sscanf(offset + 1, "%2d:%2d", &offset_hours, &offset_minutes)) {
182 free(*time);
183 *time = NULL;
184 return HTTP_ERROR_CLIENT;
186 if (*offset == '+') {
187 broken.tm_hour -= offset_hours;
188 broken.tm_min -= offset_minutes;
189 } else {
190 broken.tm_hour += offset_hours;
191 broken.tm_min += offset_minutes;
195 /* Convert to time_t */
196 (*time)->tv_sec = _isds_timegm(&broken);
197 if ((*time)->tv_sec == (time_t) -1) {
198 free(*time);
199 *time = NULL;
200 return HTTP_ERROR_CLIENT;
203 return HTTP_ERROR_SUCCESS;
207 /* Following EXTRACT_* macros expect @xpath_ctx, @error, @message,
208 * and leave label. */
209 #define ELEMENT_EXISTS(element, allow_multiple) { \
210 xmlXPathObjectPtr result = NULL; \
211 result = xmlXPathEvalExpression(BAD_CAST element, xpath_ctx); \
212 if (NULL == result) { \
213 error = HTTP_ERROR_SERVER; \
214 goto leave; \
216 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
217 xmlXPathFreeObject(result); \
218 test_asprintf(&message, "Element %s does not exist", element); \
219 error = HTTP_ERROR_CLIENT; \
220 goto leave; \
221 } else { \
222 if (!allow_multiple && result->nodesetval->nodeNr > 1) { \
223 xmlXPathFreeObject(result); \
224 test_asprintf(&message, "Multiple %s element", element); \
225 error = HTTP_ERROR_CLIENT; \
226 goto leave; \
229 xmlXPathFreeObject(result); \
232 #define EXTRACT_STRING(element, string) { \
233 xmlXPathObjectPtr result = NULL; \
234 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
235 if (NULL == result) { \
236 error = HTTP_ERROR_SERVER; \
237 goto leave; \
239 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
240 if (result->nodesetval->nodeNr > 1) { \
241 xmlXPathFreeObject(result); \
242 test_asprintf(&message, "Multiple %s element", element); \
243 error = HTTP_ERROR_CLIENT; \
244 goto leave; \
246 (string) = (char *) \
247 xmlXPathCastNodeSetToString(result->nodesetval); \
248 if (!(string)) { \
249 xmlXPathFreeObject(result); \
250 error = HTTP_ERROR_SERVER; \
251 goto leave; \
254 xmlXPathFreeObject(result); \
257 #define EXTRACT_BOOLEAN(element, booleanPtr) { \
258 char *string = NULL; \
259 EXTRACT_STRING(element, string); \
261 if (NULL != string) { \
262 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
263 if (NULL == (booleanPtr)) { \
264 free(string); \
265 error = HTTP_ERROR_SERVER; \
266 goto leave; \
269 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
270 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
271 *(booleanPtr) = 1; \
272 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
273 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
274 *(booleanPtr) = 0; \
275 else { \
276 test_asprintf(&message, \
277 "%s value is not valid boolean: %s", \
278 element, string); \
279 free(string); \
280 error = HTTP_ERROR_CLIENT; \
281 goto leave; \
284 free(string); \
289 #define EXTRACT_DATE(element, tmPtr) { \
290 char *string = NULL; \
291 EXTRACT_STRING(element, string); \
292 if (NULL != string) { \
293 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
294 if (NULL == (tmPtr)) { \
295 free(string); \
296 error = HTTP_ERROR_SERVER; \
297 goto leave; \
299 error = _server_datestring2tm(string, (tmPtr)); \
300 if (error) { \
301 if (error == HTTP_ERROR_CLIENT) { \
302 test_asprintf(&message, "%s value is not a valid date: %s", \
303 element, string); \
305 free(string); \
306 goto leave; \
308 free(string); \
312 /* Following INSERT_* macros expect @error and leave label */
313 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
315 xmlNodePtr node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
316 (xmlChar *) (string)); \
317 if (NULL == node) { \
318 error = HTTP_ERROR_SERVER; \
319 goto leave; \
323 #define INSERT_STRING(parent, element, string) \
324 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
326 #define INSERT_LONGINTPTR(parent, element, longintPtr) { \
327 if ((longintPtr)) { \
328 char *buffer = NULL; \
329 /* FIXME: locale sensitive */ \
330 if (-1 == test_asprintf(&buffer, "%ld", *(longintPtr))) { \
331 error = HTTP_ERROR_SERVER; \
332 goto leave; \
334 INSERT_STRING(parent, element, buffer) \
335 free(buffer); \
336 } else { INSERT_STRING(parent, element, NULL) } \
339 #define INSERT_ULONGINTPTR(parent, element, ulongintPtr) { \
340 if ((ulongintPtr)) { \
341 char *buffer = NULL; \
342 /* FIXME: locale sensitive */ \
343 if (-1 == test_asprintf(&buffer, "%lu", *(ulongintPtr))) { \
344 error = HTTP_ERROR_SERVER; \
345 goto leave; \
347 INSERT_STRING(parent, element, buffer) \
348 free(buffer); \
349 } else { INSERT_STRING(parent, element, NULL) } \
352 #define INSERT_BOOLEANPTR(parent, element, booleanPtr) { \
353 if (NULL != (booleanPtr)) { \
354 char *buffer = NULL; \
355 buffer = *(booleanPtr) ? "true" : "false"; \
356 INSERT_STRING(parent, element, buffer) \
357 } else { INSERT_STRING(parent, element, NULL) } \
360 #define INSERT_TIMEVALPTR(parent, element, timevalPtr) { \
361 if (NULL != (timevalPtr)) { \
362 char *buffer = NULL; \
363 error = timeval2timestring(timevalPtr, &buffer); \
364 if (error) { \
365 free(buffer); \
366 goto leave; \
368 INSERT_STRING(parent, element, buffer); \
369 free(buffer); \
370 } else { \
371 INSERT_STRING(parent, element, NULL); \
375 #define INSERT_TMPTR(parent, element, tmPtr) { \
376 if (NULL != (tmPtr)) { \
377 char *buffer = NULL; \
378 error = tm2datestring(tmPtr, &buffer); \
379 if (error) { \
380 free(buffer); \
381 goto leave; \
383 INSERT_STRING(parent, element, buffer); \
384 free(buffer); \
385 } else { \
386 INSERT_STRING(parent, element, NULL); \
390 #define INSERT_ELEMENT(child, parent, element) \
392 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
393 if (NULL == (child)) { \
394 error = HTTP_ERROR_SERVER; \
395 goto leave; \
399 /* TODO: These functions (element_exists(), extract_...(), ...) will replace
400 * the macros (ELEMENT_EXISTS, EXTRACT_...). The compiled code will be
401 * smaller, the compilation will be faster. */
403 /* Check an element exists.
404 * @code is a static output ISDS error code
405 * @error_message is a reallocated output ISDS error message
406 * @xpath_ctx is a current XPath context
407 * @element_name is name of an element to check
408 * @allow_multiple is false to require exactly one element. True to require
409 * one or more elements.
410 * @return HTTP_ERROR_SUCCESS or an appropriate error code. */
411 static http_error element_exists(const char **code, char **message,
412 xmlXPathContextPtr xpath_ctx, const char *element_name,
413 _Bool allow_multiple) {
414 xmlXPathObjectPtr result = NULL;
416 result = xmlXPathEvalExpression(BAD_CAST element_name, xpath_ctx);
417 if (NULL == result) {
418 return HTTP_ERROR_SERVER;
420 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
421 xmlXPathFreeObject(result);
422 *code = "9999";
423 test_asprintf(message, "Element %s does not exist", element_name);
424 return HTTP_ERROR_CLIENT;
425 } else {
426 if (!allow_multiple && result->nodesetval->nodeNr > 1) {
427 xmlXPathFreeObject(result);
428 *code = "9999";
429 test_asprintf(message, "Multiple %s element", element_name);
430 return HTTP_ERROR_CLIENT;
433 xmlXPathFreeObject(result);
435 return HTTP_ERROR_SUCCESS;
439 /* Locate a children element.
440 * @code is a static output ISDS error code
441 * @error_message is a reallocated output ISDS error message
442 * @xpath_ctx is a current XPath context
443 * @element_name is name of an element to select
444 * @node is output pointer to located element node
445 * @return HTTP_ERROR_SUCCESS or an appropriate error code. */
446 static http_error select_element(const char **code, char **message,
447 xmlXPathContextPtr xpath_ctx, const char *element_name,
448 xmlNodePtr *node) {
449 xmlXPathObjectPtr result = NULL;
451 result = xmlXPathEvalExpression(BAD_CAST element_name, xpath_ctx);
452 if (NULL == result) {
453 return HTTP_ERROR_SERVER;
455 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
456 xmlXPathFreeObject(result);
457 *code = "9999";
458 test_asprintf(message, "Element %s does not exist", element_name);
459 return HTTP_ERROR_CLIENT;
460 } else {
461 if (result->nodesetval->nodeNr > 1) {
462 xmlXPathFreeObject(result);
463 *code = "9999";
464 test_asprintf(message, "Multiple %s element", element_name);
465 return HTTP_ERROR_CLIENT;
468 *node = result->nodesetval->nodeTab[0];
469 xmlXPathFreeObject(result);
471 return HTTP_ERROR_SUCCESS;
475 /* Extract @element_name's value as a string.
476 * @code is a static output ISDS error code
477 * @error_message is a reallocated output ISDS error message
478 * @xpath_ctx is a current XPath context
479 * @element_name is name of a element whose child text node to extract
480 * @string is the extraced allocated string value, or NULL if empty or the
481 * element does not exist.
482 * @return HTTP_ERROR_SUCCESS or an appropriate error code. */
483 static http_error extract_string(const char **code, char **message,
484 xmlXPathContextPtr xpath_ctx, const char *element_name,
485 char **string) {
486 http_error error = HTTP_ERROR_SUCCESS;
487 xmlXPathObjectPtr result = NULL;
488 char *buffer = NULL;
490 if (-1 == test_asprintf(&buffer, "%s/text()", element_name)) {
491 error = HTTP_ERROR_SERVER;
492 goto leave;
494 result = xmlXPathEvalExpression(BAD_CAST buffer, xpath_ctx);
495 free(buffer);
496 if (NULL == result) {
497 error = HTTP_ERROR_SERVER;
498 goto leave;
500 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
501 if (result->nodesetval->nodeNr > 1) {
502 xmlXPathFreeObject(result);
503 *code = "9999";
504 test_asprintf(message, "Multiple %s element", element_name);
505 error = HTTP_ERROR_CLIENT;
506 goto leave;
508 *string = (char *) \
509 xmlXPathCastNodeSetToString(result->nodesetval);
510 if (!(*string)) {
511 xmlXPathFreeObject(result);
512 error = HTTP_ERROR_SERVER;
513 goto leave;
516 xmlXPathFreeObject(result);
518 leave:
519 return error;
523 /* Compare dates represented by pointer to struct tm.
524 * @return 0 if equalued, non-0 otherwise. */
525 static int datecmp(const struct tm *a, const struct tm *b) {
526 if (NULL == a && b == NULL) return 0;
527 if ((NULL == a && b != NULL) || (NULL != a && b == NULL)) return 1;
528 if (a->tm_year != b->tm_year) return 1;
529 if (a->tm_mon != b->tm_mon) return 1;
530 if (a->tm_mday != b->tm_mday) return 1;
531 return 0;
535 /* Compare times represented by pointer to struct timeval.
536 * @return 0 if equalued, non-0 otherwise. */
537 static int timecmp(const struct timeval *a, const struct timeval *b) {
538 if (NULL == a && b == NULL) return 0;
539 if ((NULL == a && b != NULL) || (NULL != a && b == NULL)) return 1;
540 if (a->tv_sec != b->tv_sec) return 1;
541 if (a->tv_usec != b->tv_usec) return 1;
542 return 0;
546 /* Checks an @element_name's value is an @expected_value string.
547 * @code is a static output ISDS error code
548 * @error_message is a reallocated output ISDS error message
549 * @xpath_ctx is a current XPath context
550 * @element_name is name of a element to check
551 * @must_exist is true if the @element_name must exist even if @expected_value
552 * is NULL.
553 * @expected_value is an expected string value
554 * @return HTTP_ERROR_SUCCESS if the @element_name element's value is
555 * @expected_value. HTTP_ERROR_CLIENT if not equaled, HTTP_ERROR_SERVER if an
556 * internal error occured. */
557 static http_error element_equals_string(const char **code, char **message,
558 xmlXPathContextPtr xpath_ctx, const char *element_name,
559 _Bool must_exist, const char *expected_value) {
560 http_error error = HTTP_ERROR_SUCCESS;
561 char *string = NULL;
563 if (must_exist) {
564 error = element_exists(code, message, xpath_ctx, element_name, 0);
565 if (HTTP_ERROR_SUCCESS != error)
566 goto leave;
569 error = extract_string(code, message, xpath_ctx, element_name, &string);
570 if (HTTP_ERROR_SUCCESS != error)
571 goto leave;
573 if (NULL != expected_value) {
574 if (NULL == string) {
575 *code = "9999";
576 test_asprintf(message, "Empty %s element", element_name);
577 error = HTTP_ERROR_CLIENT;
578 goto leave;
580 if (xmlStrcmp(BAD_CAST expected_value, BAD_CAST string)) {
581 *code = "9999";
582 test_asprintf(message,
583 "Unexpected %s element value: expected=`%s', got=`%s'",
584 element_name, expected_value, string);
585 error = HTTP_ERROR_CLIENT;
586 goto leave;
588 } else {
589 if (NULL != string && *string != '\0') {
590 *code = "9999";
591 test_asprintf(message,
592 "Unexpected %s element value: "
593 "expected empty string, got=`%s'",
594 element_name, string);
595 error = HTTP_ERROR_CLIENT;
596 goto leave;
600 leave:
601 free(string);
602 return error;
606 /* Checks an @element_name's value is an @expected_value integer.
607 * @code is a static output ISDS error code
608 * @error_message is a reallocated output ISDS error message
609 * @xpath_ctx is a current XPath context
610 * @element_name is name of a element to check
611 * @must_exist is true if the @element_name must exist even if @expected_value
612 * is NULL.
613 * @expected_value is an expected integer value.
614 * @return HTTP_ERROR_SUCCESS if the @element_name element's value is
615 * @expected_value. HTTP_ERROR_CLIENT if not equaled, HTTP_ERROR_SERVER if an
616 * internal error occured. */
617 static http_error element_equals_integer(const char **code, char **message,
618 xmlXPathContextPtr xpath_ctx, const char *element_name,
619 _Bool must_exist, const long int *expected_value) {
620 http_error error = HTTP_ERROR_SUCCESS;
621 char *string = NULL;
622 long int number;
623 char *endptr;
625 if (must_exist) {
626 error = element_exists(code, message, xpath_ctx, element_name, 0);
627 if (HTTP_ERROR_SUCCESS != error)
628 goto leave;
631 error = extract_string(code, message, xpath_ctx, element_name, &string);
632 if (HTTP_ERROR_SUCCESS != error)
633 goto leave;
635 if (NULL != expected_value) {
636 if (NULL == string) {
637 *code = "9999";
638 test_asprintf(message, "Empty %s element", element_name);
639 error = HTTP_ERROR_CLIENT;
640 goto leave;
642 number = strtol(string, &endptr, 10);
643 if (*endptr != '\0') {
644 *code = "9999";
645 test_asprintf(message,
646 "%s element value is not a valid integer: %s",
647 element_name, string);
648 error = HTTP_ERROR_CLIENT;
649 goto leave;
651 if (number == LONG_MIN || number == LONG_MAX) { \
652 *code = "9999";
653 test_asprintf(message, \
654 "%s element value is out of range of long int: %s",
655 element_name, string);
656 error = HTTP_ERROR_SERVER;
657 goto leave;
659 free(string); string = NULL;
660 if (number != *expected_value) {
661 *code = "9999";
662 test_asprintf(message,
663 "Unexpected %s element value: expected=`%ld', got=`%ld'",
664 element_name, *expected_value, number);
665 error = HTTP_ERROR_CLIENT;
666 goto leave;
668 } else {
669 if (NULL != string && *string != '\0') {
670 *code = "9999";
671 test_asprintf(message,
672 "Unexpected %s element value: expected no text node, got=`%s'",
673 element_name, string);
674 error = HTTP_ERROR_CLIENT;
675 goto leave;
679 leave:
680 free(string);
681 return error;
685 /* Checks an @element_name's value is an @expected_value boolean.
686 * @code is a static output ISDS error code
687 * @error_message is an reallocated output ISDS error message
688 * @xpath_ctx is a current XPath context
689 * @element_name is name of a element to check
690 * @must_exist is true if the @element_name must exist even if @expected_value
691 * is NULL.
692 * @expected_value is an expected boolean value
693 * @return HTTP_ERROR_SUCCESS if the @element_name element's value is
694 * @expected_value. HTTP_ERROR_CLIENT if not equaled, HTTP_ERROR_SERVER if an
695 * internal error occured. */
696 static http_error element_equals_boolean(const char **code, char **message,
697 xmlXPathContextPtr xpath_ctx, const char *element_name,
698 _Bool must_exist, const _Bool *expected_value) {
699 http_error error = HTTP_ERROR_SUCCESS;
700 char *string = NULL;
701 _Bool value;
703 if (must_exist) {
704 error = element_exists(code, message, xpath_ctx, element_name, 0);
705 if (HTTP_ERROR_SUCCESS != error)
706 goto leave;
709 error = extract_string(code, message, xpath_ctx, element_name, &string);
710 if (HTTP_ERROR_SUCCESS != error)
711 goto leave;
713 if (NULL != expected_value) {
714 if (NULL == string) {
715 *code = "9999";
716 test_asprintf(message, "Empty %s element", element_name);
717 error = HTTP_ERROR_CLIENT;
718 goto leave;
720 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") ||
721 !xmlStrcmp((xmlChar *)string, BAD_CAST "1"))
722 value = 1;
723 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") ||
724 !xmlStrcmp((xmlChar *)string, BAD_CAST "0"))
725 value = 0;
726 else {
727 *code = "9999";
728 test_asprintf(message,
729 "%s element value is not a valid boolean: %s",
730 element_name, string);
731 error = HTTP_ERROR_CLIENT;
732 goto leave;
734 if (*expected_value != value) {
735 *code = "9999";
736 test_asprintf(message,
737 "Unexpected %s element value: expected=%d, got=%d",
738 element_name, *expected_value, string);
739 error = HTTP_ERROR_CLIENT;
740 goto leave;
742 } else {
743 if (NULL != string && *string != '\0') {
744 *code = "9999";
745 test_asprintf(message,
746 "Unexpected %s element value: "
747 "expected empty string, got=`%s'",
748 element_name, string);
749 error = HTTP_ERROR_CLIENT;
750 goto leave;
754 leave:
755 free(string);
756 return error;
760 /* Checks an @element_name's value is an @expected_value date.
761 * @code is a static output ISDS error code
762 * @error_message is an reallocated output ISDS error message
763 * @xpath_ctx is a current XPath context
764 * @element_name is name of a element to check
765 * @must_exist is true if the @element_name must exist even if @expected_value
766 * is NULL.
767 * @expected_value is an expected boolean value
768 * @return HTTP_ERROR_SUCCESS if the @element_name element's value is
769 * @expected_value. HTTP_ERROR_CLIENT if not equaled, HTTP_ERROR_SERVER if an
770 * internal error occured. */
771 static http_error element_equals_date(const char **code, char **message,
772 xmlXPathContextPtr xpath_ctx, const char *element_name,
773 _Bool must_exist, const struct tm *expected_value) {
774 http_error error = HTTP_ERROR_SUCCESS;
775 char *string = NULL;
776 struct tm value;
778 if (must_exist) {
779 error = element_exists(code, message, xpath_ctx, element_name, 0);
780 if (HTTP_ERROR_SUCCESS != error)
781 goto leave;
784 error = extract_string(code, message, xpath_ctx, element_name, &string);
785 if (HTTP_ERROR_SUCCESS != error)
786 goto leave;
788 if (NULL != expected_value) {
789 if (NULL == string) {
790 *code = "9999";
791 test_asprintf(message, "Empty %s element", element_name);
792 error = HTTP_ERROR_CLIENT;
793 goto leave;
795 error = _server_datestring2tm(string, &value);
796 if (error) {
797 if (error == HTTP_ERROR_CLIENT) { \
798 test_asprintf(message, "%s value is not a valid date: %s",
799 element_name, string);
801 goto leave;
803 if (datecmp(expected_value, &value)) {
804 *code = "9999";
805 test_asprintf(message, "Unexpected %s element value: "
806 "expected=%d-%02d-%02d, got=%d-%02d-%02d", element_name,
807 expected_value->tm_year + 1900, expected_value->tm_mon + 1,
808 expected_value->tm_mday,
809 value.tm_year + 1900, value.tm_mon + 1, value.tm_mday);
810 error = HTTP_ERROR_CLIENT;
811 goto leave;
813 } else {
814 if (NULL != string && *string != '\0') {
815 *code = "9999";
816 test_asprintf(message,
817 "Unexpected %s element value: "
818 "expected empty value, got=`%s'",
819 element_name, string);
820 error = HTTP_ERROR_CLIENT;
821 goto leave;
825 leave:
826 free(string);
827 return error;
831 /* Checks an @element_name's value is an @expected_value time.
832 * @code is a static output ISDS error code
833 * @error_message is an reallocated output ISDS error message
834 * @xpath_ctx is a current XPath context
835 * @element_name is name of a element to check
836 * @must_exist is true if the @element_name must exist even if @expected_value
837 * is NULL.
838 * @expected_value is an expected boolean value
839 * @return HTTP_ERROR_SUCCESS if the @element_name element's value is
840 * @expected_value. HTTP_ERROR_CLIENT if not equaled, HTTP_ERROR_SERVER if an
841 * internal error occured. */
842 static http_error element_equals_time(const char **code, char **message,
843 xmlXPathContextPtr xpath_ctx, const char *element_name,
844 _Bool must_exist, const struct timeval *expected_value) {
845 http_error error = HTTP_ERROR_SUCCESS;
846 char *string = NULL;
847 struct timeval *value = NULL;
849 if (must_exist) {
850 error = element_exists(code, message, xpath_ctx, element_name, 0);
851 if (HTTP_ERROR_SUCCESS != error)
852 goto leave;
855 error = extract_string(code, message, xpath_ctx, element_name, &string);
856 if (HTTP_ERROR_SUCCESS != error)
857 goto leave;
859 if (NULL != expected_value) {
860 if (NULL == string) {
861 *code = "9999";
862 test_asprintf(message, "Empty %s element", element_name);
863 error = HTTP_ERROR_CLIENT;
864 goto leave;
866 error = timestring2timeval(string, &value);
867 if (error) {
868 if (error == HTTP_ERROR_CLIENT) { \
869 test_asprintf(message, "%s value is not a valid time: %s",
870 element_name, string);
872 goto leave;
874 if (timecmp(expected_value, value)) {
875 *code = "9999";
876 test_asprintf(message, "Unexpected %s element value: "
877 "expected=%ds:%" PRIdMAX "us, got=%ds:%" PRIdMAX "us",
878 element_name,
879 expected_value->tv_sec, (intmax_t)expected_value->tv_usec,
880 value->tv_sec, (intmax_t)value->tv_usec);
881 error = HTTP_ERROR_CLIENT;
882 goto leave;
884 } else {
885 if (NULL != string && *string != '\0') {
886 *code = "9999";
887 test_asprintf(message,
888 "Unexpected %s element value: "
889 "expected empty value, got=`%s'",
890 element_name, string);
891 error = HTTP_ERROR_CLIENT;
892 goto leave;
896 leave:
897 free(string);
898 free(value);
899 return error;
903 /* Insert dmStatus or similar subtree
904 * @parent is element to insert to
905 * @dm is true for dmStatus, otherwise dbStatus
906 * @code is status code as string
907 * @message is UTF-8 encoded message
908 * @db_ref_number is optinal reference number propagated if not @dm
909 * @return 0 on success, otherwise non-0. */
910 static http_error insert_isds_status(xmlNodePtr parent, _Bool dm,
911 const xmlChar *code, const xmlChar *message,
912 const xmlChar *db_ref_number) {
913 http_error error = HTTP_ERROR_SUCCESS;
914 xmlNodePtr status;
916 if (NULL == code || NULL == message) {
917 error = HTTP_ERROR_SERVER;
918 goto leave;
921 INSERT_ELEMENT(status, parent, (dm) ? "dmStatus" : "dbStatus");
922 INSERT_STRING(status, (dm) ? "dmStatusCode" : "dbStatusCode", code);
923 INSERT_STRING(status, (dm) ? "dmStatusMessage" : "dbStatusMessage", message);
924 if (!dm && NULL != db_ref_number) {
925 INSERT_STRING(status, "dbStatusRefNumber", db_ref_number);
928 leave:
929 return error;
933 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
934 static http_error tm2datestring(const struct tm *time, char **string) {
935 if (NULL == time || NULL == string) return HTTP_ERROR_SERVER;
937 if (-1 == test_asprintf(string, "%d-%02d-%02d",
938 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
939 return HTTP_ERROR_SERVER;
941 return HTTP_ERROR_SUCCESS;
945 /* Convert struct timeval *@time to UTF-8 ISO 8601 date-time @string. It
946 * respects the @time microseconds too. */
947 static http_error timeval2timestring(const struct timeval *time,
948 char **string) {
949 struct tm broken;
951 if (!time || !string) return HTTP_ERROR_SERVER;
953 if (!gmtime_r(&time->tv_sec, &broken)) return HTTP_ERROR_SERVER;
954 if (time->tv_usec < 0 || time->tv_usec > 999999) return HTTP_ERROR_SERVER;
956 /* TODO: small negative year should be formatted as "-0012". This is not
957 * true for glibc "%04d". We should implement it.
958 * time->tv_usec type is su_seconds_t which is required to be signed
959 * integer to accomodate values from range [-1, 1000000].
960 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
961 if (-1 == test_asprintf(string,
962 "%04d-%02d-%02dT%02d:%02d:%02d.%06" PRIdMAX,
963 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
964 broken.tm_hour, broken.tm_min, broken.tm_sec,
965 (intmax_t)time->tv_usec))
966 return HTTP_ERROR_SERVER;
968 return HTTP_ERROR_SUCCESS;
972 /* Implement DummyOperation */
973 static http_error service_DummyOperation(
974 xmlXPathContextPtr xpath_ctx,
975 xmlNodePtr isds_response,
976 const void *arguments) {
977 (void)xpath_ctx;
978 (void)arguments;
980 return insert_isds_status(isds_response, 1, BAD_CAST "0000",
981 BAD_CAST "Success", NULL);
985 /* Implement Re-signISDSDocument.
986 * It sends document from request back.
987 * @arguments is pointer to struct arguments_DS_Dz_ResignISDSDocument */
988 static http_error service_ResignISDSDocument(
989 xmlXPathContextPtr xpath_ctx,
990 xmlNodePtr isds_response,
991 const void *arguments) {
992 http_error error = HTTP_ERROR_SUCCESS;
993 const char *code = "9999";
994 char *message = NULL;
995 const struct arguments_DS_Dz_ResignISDSDocument *configuration =
996 (const struct arguments_DS_Dz_ResignISDSDocument *)arguments;
997 char *data = NULL;
999 if (NULL == configuration || NULL == configuration->status_code ||
1000 NULL == configuration->status_message) {
1001 error = HTTP_ERROR_SERVER;
1002 goto leave;
1005 EXTRACT_STRING("isds:dmDoc", data);
1006 if (NULL == data) {
1007 message = strdup("Missing isds:dmDoc");
1008 error = HTTP_ERROR_CLIENT;
1009 goto leave;
1013 /* dmResultDoc is mandatory in response */
1014 if (xmlStrcmp(BAD_CAST configuration->status_code, BAD_CAST "0000")) {
1015 free(data);
1016 data = NULL;
1018 INSERT_STRING(isds_response, "dmResultDoc", data);
1020 if (configuration->valid_to != NULL) {
1021 error = tm2datestring(configuration->valid_to, &data);
1022 if (error) {
1023 message = strdup("Could not format date");
1024 goto leave;
1026 INSERT_STRING(isds_response, "dmValidTo", data);
1029 code = configuration->status_code;
1030 message = strdup(configuration->status_message);
1032 leave:
1033 if (HTTP_ERROR_SERVER != error) {
1034 http_error next_error = insert_isds_status(isds_response, 1,
1035 BAD_CAST code, BAD_CAST message, NULL);
1036 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
1038 free(data);
1039 free(message);
1040 return error;
1044 /* Implement EraseMessage.
1045 * @arguments is pointer to struct arguments_DS_DsManage_ChangeISDSPassword */
1046 static http_error service_EraseMessage(
1047 xmlXPathContextPtr xpath_ctx,
1048 xmlNodePtr isds_response,
1049 const void *arguments) {
1050 http_error error = HTTP_ERROR_SUCCESS;
1051 char *code = "9999", *message = NULL;
1052 const struct arguments_DS_Dx_EraseMessage *configuration =
1053 (const struct arguments_DS_Dx_EraseMessage *)arguments;
1054 char *message_id = NULL;
1055 _Bool *incoming = NULL;
1057 if (NULL == configuration || NULL == configuration->message_id) {
1058 error = HTTP_ERROR_SERVER;
1059 goto leave;
1062 EXTRACT_STRING("isds:dmID", message_id);
1063 if (NULL == message_id) {
1064 message = strdup("Missing isds:dmID");
1065 error = HTTP_ERROR_CLIENT;
1066 goto leave;
1068 EXTRACT_BOOLEAN("isds:dmIncoming", incoming);
1069 if (NULL == incoming) {
1070 message = strdup("Missing isds:dmIncoming");
1071 error = HTTP_ERROR_CLIENT;
1072 goto leave;
1075 if (xmlStrcmp((const xmlChar *) configuration->message_id,
1076 (const xmlChar *) message_id)) {
1077 code = "1219";
1078 message = strdup("Message is not in the long term storage");
1079 error = HTTP_ERROR_CLIENT;
1080 goto leave;
1082 if (configuration->incoming != *incoming) {
1083 code = "1219";
1084 message = strdup("Message direction mismatches");
1085 error = HTTP_ERROR_CLIENT;
1086 goto leave;
1089 code = "0000";
1090 message = strdup("Success");
1091 leave:
1092 if (HTTP_ERROR_SERVER != error) {
1093 http_error next_error = insert_isds_status(isds_response, 1,
1094 BAD_CAST code, BAD_CAST message, NULL);
1095 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
1097 free(incoming);
1098 free(message_id);
1099 free(message);
1100 return error;
1104 /* Insert list of credit info as XSD:tCiRecord XML tree.
1105 * @isds_response is XML node with the response
1106 * @history is list of struct server_credit_event. If NULL, no ciRecord XML
1107 * subtree will be created. */
1108 static http_error insert_ciRecords(xmlNodePtr isds_response,
1109 const struct server_list *history) {
1110 http_error error = HTTP_ERROR_SUCCESS;
1111 xmlNodePtr records, record;
1113 if (NULL == isds_response) return HTTP_ERROR_SERVER;
1114 if (NULL == history) return HTTP_ERROR_SUCCESS;
1116 INSERT_ELEMENT(records, isds_response, "ciRecords");
1117 for (const struct server_list *item = history; NULL != item;
1118 item = item->next) {
1119 const struct server_credit_event *event =
1120 (struct server_credit_event*)item->data;
1122 INSERT_ELEMENT(record, records, "ciRecord");
1123 if (NULL == event) continue;
1125 INSERT_TIMEVALPTR(record, "ciEventTime", event->time);
1126 switch(event->type) {
1127 case SERVER_CREDIT_CHARGED:
1128 INSERT_STRING(record, "ciEventType", "1");
1129 INSERT_STRING(record, "ciTransID",
1130 event->details.charged.transaction);
1131 break;
1132 case SERVER_CREDIT_DISCHARGED:
1133 INSERT_STRING(record, "ciEventType", "2");
1134 INSERT_STRING(record, "ciTransID",
1135 event->details.discharged.transaction);
1136 break;
1137 case SERVER_CREDIT_MESSAGE_SENT:
1138 INSERT_STRING(record, "ciEventType", "3");
1139 INSERT_STRING(record, "ciRecipientID",
1140 event->details.message_sent.recipient);
1141 INSERT_STRING(record, "ciPDZID",
1142 event->details.message_sent.message_id);
1143 break;
1144 case SERVER_CREDIT_STORAGE_SET:
1145 INSERT_STRING(record, "ciEventType", "4");
1146 INSERT_LONGINTPTR(record, "ciNewCapacity",
1147 &event->details.storage_set.new_capacity);
1148 INSERT_TMPTR(record, "ciNewFrom",
1149 event->details.storage_set.new_valid_from);
1150 INSERT_TMPTR(record, "ciNewTo",
1151 event->details.storage_set.new_valid_to);
1152 INSERT_LONGINTPTR(record, "ciOldCapacity",
1153 event->details.storage_set.old_capacity);
1154 INSERT_TMPTR(record, "ciOldFrom",
1155 event->details.storage_set.old_valid_from);
1156 INSERT_TMPTR(record, "ciOldTo",
1157 event->details.storage_set.old_valid_to);
1158 INSERT_STRING(record, "ciDoneBy",
1159 event->details.storage_set.initiator);
1160 break;
1161 case SERVER_CREDIT_EXPIRED:
1162 INSERT_STRING(record, "ciEventType", "5");
1163 break;
1164 default:
1165 error = HTTP_ERROR_SERVER;
1166 goto leave;
1168 INSERT_LONGINTPTR(record, "ciCreditChange", &event->credit_change);
1169 INSERT_LONGINTPTR(record, "ciCreditAfter", &event->new_credit);
1173 leave:
1174 return error;
1178 /* Implement DataBoxCreditInfo.
1179 * @arguments is pointer to struct arguments_DS_df_DataBoxCreditInfo */
1180 static http_error service_DataBoxCreditInfo(
1181 xmlXPathContextPtr xpath_ctx,
1182 xmlNodePtr isds_response,
1183 const void *arguments) {
1184 http_error error = HTTP_ERROR_SUCCESS;
1185 const char *code = "9999";
1186 char *message = NULL;
1187 const struct arguments_DS_df_DataBoxCreditInfo *configuration =
1188 (const struct arguments_DS_df_DataBoxCreditInfo *)arguments;
1189 char *box_id = NULL;
1190 struct tm *from_date = NULL, *to_date = NULL;
1192 if (NULL == configuration || NULL == configuration->status_code ||
1193 NULL == configuration->status_message) {
1194 error = HTTP_ERROR_SERVER;
1195 goto leave;
1198 EXTRACT_STRING("isds:dbID", box_id);
1199 if (NULL == box_id) {
1200 message = strdup("Missing isds:dbID");
1201 error = HTTP_ERROR_CLIENT;
1202 goto leave;
1204 if (NULL != configuration->box_id &&
1205 xmlStrcmp(BAD_CAST configuration->box_id,
1206 BAD_CAST box_id)) {
1207 code = "9999";
1208 message = strdup("Unexpected isds:dbID value");
1209 error = HTTP_ERROR_CLIENT;
1210 goto leave;
1213 ELEMENT_EXISTS("isds:ciFromDate", 0);
1214 EXTRACT_DATE("isds:ciFromDate", from_date);
1215 if (datecmp(configuration->from_date, from_date)) {
1216 code = "9999";
1217 message = strdup("Unexpected isds:ciFromDate value");
1218 error = HTTP_ERROR_CLIENT;
1219 goto leave;
1222 ELEMENT_EXISTS("isds:ciTodate", 0);
1223 EXTRACT_DATE("isds:ciTodate", to_date);
1224 if (datecmp(configuration->to_date, to_date)) {
1225 code = "9999";
1226 message = strdup("Unexpected isds:ciTodate value");
1227 error = HTTP_ERROR_CLIENT;
1228 goto leave;
1231 INSERT_LONGINTPTR(isds_response, "currentCredit",
1232 &configuration->current_credit);
1233 INSERT_STRING(isds_response, "notifEmail", configuration->email);
1234 if ((error = insert_ciRecords(isds_response, configuration->history))) {
1235 goto leave;
1238 code = configuration->status_code;
1239 message = strdup(configuration->status_message);
1240 leave:
1241 if (HTTP_ERROR_SERVER != error) {
1242 http_error next_error = insert_isds_status(isds_response, 0,
1243 BAD_CAST code, BAD_CAST message, NULL);
1244 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
1246 free(box_id);
1247 free(from_date);
1248 free(to_date);
1249 free(message);
1250 return error;
1254 /* Insert list of fulltext search results as XSD:tdbResultsArray XML tree.
1255 * @isds_response is XML node with the response
1256 * @results is list of struct server_db_result *.
1257 * @create_empty_root is true to create dbResults element even if @results is
1258 * empty. */
1259 static http_error insert_tdbResultsArray(xmlNodePtr isds_response,
1260 const struct server_list *results, _Bool create_empty_root) {
1261 http_error error = HTTP_ERROR_SUCCESS;
1262 xmlNodePtr root, entry;
1264 if (NULL == isds_response) return HTTP_ERROR_SERVER;
1266 if (NULL != results || create_empty_root)
1267 INSERT_ELEMENT(root, isds_response, "dbResults");
1269 if (NULL == results) return HTTP_ERROR_SUCCESS;
1271 for (const struct server_list *item = results; NULL != item;
1272 item = item->next) {
1273 const struct server_db_result *result =
1274 (struct server_db_result *)item->data;
1276 INSERT_ELEMENT(entry, root, "dbResult");
1277 if (NULL == result) continue;
1279 INSERT_STRING(entry, "dbID", result->id);
1280 INSERT_STRING(entry, "dbType", result->type);
1281 INSERT_STRING(entry, "dbName", result->name);
1282 INSERT_STRING(entry, "dbAddress", result->address);
1283 INSERT_TMPTR(entry, "dbBiDate", result->birth_date);
1284 INSERT_STRING(entry, "dbICO", result->ic);
1285 INSERT_BOOLEANPTR(entry, "dbEffectiveOVM", &result->ovm);
1286 INSERT_STRING(entry, "dbSendOptions", result->send_options);
1289 leave:
1290 return error;
1294 /* Insert list of search results as XSD:tDbOwnersArray or
1295 * XSD:tDbPersOwnerArray XML tree.
1296 * @isds_response is XML node with the response
1297 * @results is list of struct server_owner_info *.
1298 * @create_empty_root is true to create dbResults element even if @results is
1299 * empty.
1300 * @pfo is false to serialize tDbOwnerInfo elements only, is true to
1301 * serialize tdbPersonalOwnerInfo elements only. */
1302 static http_error insert_tDbOwnersArray(xmlNodePtr isds_response,
1303 const struct server_list *results, _Bool create_empty_root,
1304 _Bool pfo) {
1305 http_error error = HTTP_ERROR_SUCCESS;
1306 xmlNodePtr root, entry;
1308 if (NULL == isds_response) return HTTP_ERROR_SERVER;
1310 if (NULL != results || create_empty_root)
1311 INSERT_ELEMENT(root, isds_response, "dbResults");
1313 if (NULL == results) return HTTP_ERROR_SUCCESS;
1315 for (const struct server_list *item = results; NULL != item;
1316 item = item->next) {
1317 const struct server_owner_info *result =
1318 (struct server_owner_info *)item->data;
1320 INSERT_ELEMENT(entry, root, "dbOwnerInfo");
1321 if (NULL == result) continue;
1323 INSERT_STRING(entry, "dbID", result->dbID);
1324 if (pfo) {
1325 INSERT_BOOLEANPTR(entry, "aifoIsds", result->aifoIsds);
1327 if (!pfo) {
1328 INSERT_STRING(entry, "dbType", result->dbType);
1329 INSERT_STRING(entry, "ic", result->ic);
1331 INSERT_STRING(entry, "pnFirstName", result->pnFirstName);
1332 INSERT_STRING(entry, "pnMiddleName", result->pnMiddleName);
1333 INSERT_STRING(entry, "pnLastName", result->pnLastName);
1334 if (!pfo) {
1335 INSERT_STRING(entry, "pnLastNameAtBirth",
1336 result->pnLastNameAtBirth);
1337 INSERT_STRING(entry, "firmName", result->firmName);
1339 INSERT_TMPTR(entry, "biDate", result->biDate);
1340 INSERT_STRING(entry, "biCity", result->biCity);
1341 INSERT_STRING(entry, "biCounty", result->biCounty);
1342 INSERT_STRING(entry, "biState", result->biState);
1343 if (pfo) {
1344 INSERT_LONGINTPTR(entry, "adCode", result->adCode);
1346 INSERT_STRING(entry, "adCity", result->adCity);
1347 if (pfo) {
1348 INSERT_STRING(entry, "adDistrict", result->adDistrict);
1350 INSERT_STRING(entry, "adStreet", result->adStreet);
1351 INSERT_STRING(entry, "adNumberInStreet", result->adNumberInStreet);
1352 INSERT_STRING(entry, "adNumberInMunicipality",
1353 result->adNumberInMunicipality);
1354 INSERT_STRING(entry, "adZipCode", result->adZipCode);
1355 INSERT_STRING(entry, "adState", result->adState);
1356 INSERT_STRING(entry, "nationality", result->nationality);
1357 if (!pfo) {
1358 if (result->email_exists || result->email != NULL) {
1359 INSERT_STRING(entry, "email", result->email);
1361 if (result->telNumber_exists || result->telNumber != NULL) {
1362 INSERT_STRING(entry, "telNumber", result->telNumber);
1364 INSERT_STRING(entry, "identifier", result->identifier);
1365 INSERT_STRING(entry, "registryCode", result->registryCode);
1366 INSERT_LONGINTPTR(entry, "dbState", result->dbState);
1367 INSERT_BOOLEANPTR(entry, "dbEffectiveOVM", result->dbEffectiveOVM);
1368 INSERT_BOOLEANPTR(entry, "dbOpenAddressing",
1369 result->dbOpenAddressing);
1373 leave:
1374 return error;
1378 /* Find isds:dbOwnerInfo child and check its content.
1379 * @code is a static output ISDS error code
1380 * @error_message is a reallocated output ISDS error message
1381 * @criteria is template to check the child against
1382 * @pfo is false if XSD:tDbOwnerInfo type is expected, true if
1383 * XSD:tdbPersonalOwnerInfo is expectd. */
1384 static http_error check_dbOwnerInfo(const char **code, char **message,
1385 xmlXPathContextPtr xpath_ctx,
1386 const struct server_owner_info *criteria, _Bool pfo) {
1387 http_error error = HTTP_ERROR_SUCCESS;
1388 xmlNodePtr old_node = xpath_ctx->node;
1389 xmlNodePtr new_node;
1391 if (NULL == criteria) {
1392 error = HTTP_ERROR_SERVER;
1393 goto leave;
1396 /* Find the required child */
1397 error = select_element(code, message, xpath_ctx, "isds:dbOwnerInfo",
1398 &new_node);
1399 if (error) goto leave;
1400 /* And set context */
1401 xpath_ctx->node = new_node;
1403 error = element_equals_string(code, message, xpath_ctx,
1404 "isds:dbID", 1, criteria->dbID);
1405 if (error) goto leave;
1407 if (pfo) {
1408 error = element_equals_boolean(code, message, xpath_ctx,
1409 "isds:aifoIsds", 1, criteria->aifoIsds);
1410 if (error) goto leave;
1413 if (!pfo) {
1414 error = element_equals_string(code, message, xpath_ctx,
1415 "isds:dbType", 1, criteria->dbType);
1416 if (error) goto leave;
1418 error = element_equals_string(code, message, xpath_ctx,
1419 "isds:ic", 1, criteria->ic);
1420 if (error) goto leave;
1423 error = element_equals_string(code, message, xpath_ctx,
1424 "isds:pnFirstName", 1, criteria->pnFirstName);
1425 if (error) goto leave;
1427 error = element_equals_string(code, message, xpath_ctx,
1428 "isds:pnMiddleName", 1, criteria->pnMiddleName);
1429 if (error) goto leave;
1431 error = element_equals_string(code, message, xpath_ctx,
1432 "isds:pnLastName", 1, criteria->pnLastName);
1433 if (error) goto leave;
1435 if (!pfo) {
1436 error = element_equals_string(code, message, xpath_ctx,
1437 "isds:pnLastNameAtBirth", 1, criteria->pnLastNameAtBirth);
1438 if (error) goto leave;
1440 error = element_equals_string(code, message, xpath_ctx,
1441 "isds:firmName", 1, criteria->firmName);
1442 if (error) goto leave;
1445 error = element_equals_date(code, message, xpath_ctx,
1446 "isds:biDate", 1, criteria->biDate);
1447 if (error) goto leave;
1449 error = element_equals_string(code, message, xpath_ctx,
1450 "isds:biCity", 1, criteria->biCity);
1451 if (error) goto leave;
1453 error = element_equals_string(code, message, xpath_ctx,
1454 "isds:biCounty", 1, criteria->biCounty);
1455 if (error) goto leave;
1457 error = element_equals_string(code, message, xpath_ctx,
1458 "isds:biState", 1, criteria->biState);
1459 if (error) goto leave;
1461 if (pfo) {
1462 error = element_equals_integer(code, message, xpath_ctx,
1463 "isds:adCode", 1, criteria->adCode);
1464 if (error) goto leave;
1467 error = element_equals_string(code, message, xpath_ctx,
1468 "isds:adCity", 1, criteria->adCity);
1469 if (error) goto leave;
1471 if (pfo) {
1472 error = element_equals_string(code, message, xpath_ctx,
1473 "isds:adDistrict", 1, criteria->adDistrict);
1474 if (error) goto leave;
1477 error = element_equals_string(code, message, xpath_ctx,
1478 "isds:adStreet", 1, criteria->adStreet);
1479 if (error) goto leave;
1481 error = element_equals_string(code, message, xpath_ctx,
1482 "isds:adNumberInStreet", 1,
1483 criteria->adNumberInStreet);
1484 if (error) goto leave;
1486 error = element_equals_string(code, message, xpath_ctx,
1487 "isds:adNumberInMunicipality", 1,
1488 criteria->adNumberInMunicipality);
1489 if (error) goto leave;
1491 error = element_equals_string(code, message, xpath_ctx,
1492 "isds:adZipCode", 1, criteria->adZipCode);
1493 if (error) goto leave;
1495 error = element_equals_string(code, message, xpath_ctx,
1496 "isds:adState", 1, criteria->adState);
1497 if (error) goto leave;
1499 error = element_equals_string(code, message, xpath_ctx,
1500 "isds:nationality", 1, criteria->nationality);
1501 if (error) goto leave;
1503 if (!pfo) {
1504 error = element_equals_string(code, message, xpath_ctx,
1505 "isds:email", 0, criteria->email);
1506 if (error) goto leave;
1508 error = element_equals_string(code, message, xpath_ctx,
1509 "isds:telNumber", 0, criteria->telNumber);
1510 if (error) goto leave;
1512 error = element_equals_string(code, message, xpath_ctx,
1513 "isds:identifier", 1, criteria->identifier);
1514 if (error) goto leave;
1516 error = element_equals_string(code, message, xpath_ctx,
1517 "isds:registryCode", 1, criteria->registryCode);
1518 if (error) goto leave;
1520 error = element_equals_integer(code, message, xpath_ctx,
1521 "isds:dbState", 1, criteria->dbState);
1522 if (error) goto leave;
1524 error = element_equals_boolean(code, message, xpath_ctx,
1525 "isds:dbEffectiveOVM", 1, criteria->dbEffectiveOVM);
1526 if (error) goto leave;
1528 error = element_equals_boolean(code, message, xpath_ctx,
1529 "isds:dbOpenAddressing", 1,
1530 criteria->dbOpenAddressing);
1531 if (error) goto leave;
1534 leave:
1535 /* Restore context */
1536 xpath_ctx->node = old_node;
1537 return error;
1541 /* Implement FindDataBox.
1542 * @arguments is pointer to struct arguments_DS_df_FindDataBox */
1543 static http_error service_FindDataBox(
1544 xmlXPathContextPtr xpath_ctx,
1545 xmlNodePtr isds_response,
1546 const void *arguments) {
1547 http_error error = HTTP_ERROR_SUCCESS;
1548 const char *code = "9999";
1549 char *message = NULL;
1550 const struct arguments_DS_df_FindDataBox *configuration =
1551 (const struct arguments_DS_df_FindDataBox *)arguments;
1553 if (NULL == configuration || NULL == configuration->status_code ||
1554 NULL == configuration->status_message) {
1555 error = HTTP_ERROR_SERVER;
1556 goto leave;
1559 /* Check request */
1560 error = check_dbOwnerInfo(&code, &message, xpath_ctx,
1561 configuration->criteria, 0);
1562 if (error) goto leave;
1564 /* Build response */
1565 if ((error = insert_tDbOwnersArray(isds_response, configuration->results,
1566 configuration->results_exists, 0))) {
1567 goto leave;
1570 code = configuration->status_code;
1571 message = strdup(configuration->status_message);
1573 leave:
1574 if (HTTP_ERROR_SERVER != error) {
1575 http_error next_error = insert_isds_status(isds_response, 0,
1576 BAD_CAST code, BAD_CAST message, NULL);
1577 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
1579 free(message);
1580 return error;
1584 /* Implement FindPersonalDataBox.
1585 * @arguments is pointer to struct arguments_DS_df_FindPersonalDataBox */
1586 static http_error service_FindPersonalDataBox(
1587 xmlXPathContextPtr xpath_ctx,
1588 xmlNodePtr isds_response,
1589 const void *arguments) {
1590 http_error error = HTTP_ERROR_SUCCESS;
1591 const char *code = "9999";
1592 char *message = NULL;
1593 const struct arguments_DS_df_FindPersonalDataBox *configuration =
1594 (const struct arguments_DS_df_FindPersonalDataBox *)arguments;
1596 if (NULL == configuration || NULL == configuration->status_code ||
1597 NULL == configuration->status_message) {
1598 error = HTTP_ERROR_SERVER;
1599 goto leave;
1602 /* Check request */
1603 error = check_dbOwnerInfo(&code, &message, xpath_ctx,
1604 configuration->criteria, 1);
1605 if (error) goto leave;
1607 /* Build response */
1608 if ((error = insert_tDbOwnersArray(isds_response, configuration->results,
1609 configuration->results_exists, 1))) {
1610 goto leave;
1613 code = configuration->status_code;
1614 message = strdup(configuration->status_message);
1616 leave:
1617 if (HTTP_ERROR_SERVER != error) {
1618 http_error next_error = insert_isds_status(isds_response, 0,
1619 BAD_CAST code, BAD_CAST message, NULL);
1620 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
1622 free(message);
1623 return error;
1627 /* Insert list of period results as XSD:tdbPeriodsArray XML tree.
1628 * @isds_response is XML node with the response
1629 * @results is list of struct server_box_state_period *.
1630 * @create_empty_root is true to create Periods element even if @results is
1631 * empty. */
1632 static http_error insert_tdbPeriodsArray(xmlNodePtr isds_response,
1633 const struct server_list *results, _Bool create_empty_root) {
1634 http_error error = HTTP_ERROR_SUCCESS;
1635 xmlNodePtr root, entry;
1637 if (NULL == isds_response) return HTTP_ERROR_SERVER;
1639 if (NULL != results || create_empty_root)
1640 INSERT_ELEMENT(root, isds_response, "Periods");
1642 if (NULL == results) return HTTP_ERROR_SUCCESS;
1644 for (const struct server_list *item = results; NULL != item;
1645 item = item->next) {
1646 const struct server_box_state_period *result =
1647 (struct server_box_state_period *)item->data;
1649 INSERT_ELEMENT(entry, root, "Period");
1650 if (NULL == result) continue;
1652 INSERT_TIMEVALPTR(entry, "PeriodFrom", result->from);
1653 INSERT_TIMEVALPTR(entry, "PeriodTo", result->to);
1654 INSERT_LONGINTPTR(entry, "DbState", &(result->dbState));
1657 leave:
1658 return error;
1662 /* Implement GetDataBoxActivityStatus.
1663 * @arguments is pointer to struct arguments_DS_df_GetDataBoxActivityStatus */
1664 static http_error service_GetDataBoxActivityStatus(
1665 xmlXPathContextPtr xpath_ctx,
1666 xmlNodePtr isds_response,
1667 const void *arguments) {
1668 http_error error = HTTP_ERROR_SUCCESS;
1669 const char *code = "9999";
1670 char *message = NULL;
1671 const struct arguments_DS_df_GetDataBoxActivityStatus *configuration =
1672 (const struct arguments_DS_df_GetDataBoxActivityStatus *)arguments;
1674 if (NULL == configuration || NULL == configuration->status_code ||
1675 NULL == configuration->status_message) {
1676 error = HTTP_ERROR_SERVER;
1677 goto leave;
1680 /* Check request */
1681 error = element_equals_string(&code, &message, xpath_ctx,
1682 "isds:dbID", 1, configuration->box_id);
1683 /* ??? XML schema and textual documentation does not agree on obligatority
1684 * of the isds:baFrom and isds:baTo value or presence. */
1685 error = element_equals_time(&code, &message, xpath_ctx,
1686 "isds:baFrom", 1, configuration->from);
1687 error = element_equals_time(&code, &message, xpath_ctx,
1688 "isds:baTo", 1, configuration->to);
1689 if (error) goto leave;
1691 /* Build response */
1692 if ((error = insert_tdbPeriodsArray(isds_response, configuration->results,
1693 configuration->results_exists))) {
1694 goto leave;
1697 code = configuration->status_code;
1698 message = strdup(configuration->status_message);
1700 leave:
1701 if (HTTP_ERROR_SERVER != error) {
1702 http_error next_error = insert_isds_status(isds_response, 0,
1703 BAD_CAST code, BAD_CAST message, NULL);
1704 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
1706 free(message);
1707 return error;
1711 /* Implement ISDSSearch2.
1712 * @arguments is pointer to struct arguments_DS_df_ISDSSearch2 */
1713 static http_error service_ISDSSearch2(
1714 xmlXPathContextPtr xpath_ctx,
1715 xmlNodePtr isds_response,
1716 const void *arguments) {
1717 http_error error = HTTP_ERROR_SUCCESS;
1718 const char *code = "9999";
1719 char *message = NULL;
1720 const struct arguments_DS_df_ISDSSearch2 *configuration =
1721 (const struct arguments_DS_df_ISDSSearch2 *)arguments;
1722 char *string = NULL;
1724 if (NULL == configuration || NULL == configuration->status_code ||
1725 NULL == configuration->status_message) {
1726 error = HTTP_ERROR_SERVER;
1727 goto leave;
1730 /* Check request */
1731 EXTRACT_STRING("isds:searchText", string);
1732 if (NULL == string) {
1733 message = strdup("Missing or empty isds:searchText");
1734 error = HTTP_ERROR_CLIENT;
1735 goto leave;
1737 if (NULL != configuration->search_text &&
1738 xmlStrcmp(BAD_CAST configuration->search_text,
1739 BAD_CAST string)) {
1740 code = "9999";
1741 message = strdup("Unexpected isds:searchText value");
1742 error = HTTP_ERROR_CLIENT;
1743 goto leave;
1745 free(string); string = NULL;
1747 error = element_equals_string(&code, &message, xpath_ctx,
1748 "isds:searchType", 1, configuration->search_type);
1749 if (error) goto leave;
1751 error = element_equals_string(&code, &message, xpath_ctx,
1752 "isds:searchScope", 1, configuration->search_scope);
1753 if (error) goto leave;
1755 error = element_equals_integer(&code, &message, xpath_ctx,
1756 "isds:page", 1, configuration->search_page_number);
1757 if (error) goto leave;
1759 error = element_equals_integer(&code, &message, xpath_ctx,
1760 "isds:pageSize", 1, configuration->search_page_size);
1761 if (error) goto leave;
1763 error = element_equals_boolean(&code, &message, xpath_ctx,
1764 "isds:highlighting", 0, configuration->search_highlighting_value);
1765 if (error) goto leave;
1767 /* Build response */
1768 if (NULL != configuration->total_count)
1769 INSERT_ULONGINTPTR(isds_response, "totalCount",
1770 configuration->total_count);
1771 if (NULL != configuration->current_count)
1772 INSERT_ULONGINTPTR(isds_response, "currentCount",
1773 configuration->current_count);
1774 if (NULL != configuration->position)
1775 INSERT_ULONGINTPTR(isds_response, "position",
1776 configuration->position);
1777 if (NULL != configuration->last_page)
1778 INSERT_BOOLEANPTR(isds_response, "lastPage",
1779 configuration->last_page);
1780 if ((error = insert_tdbResultsArray(isds_response, configuration->results,
1781 configuration->results_exists))) {
1782 goto leave;
1785 code = configuration->status_code;
1786 message = strdup(configuration->status_message);
1788 leave:
1789 if (HTTP_ERROR_SERVER != error) {
1790 http_error next_error = insert_isds_status(isds_response, 0,
1791 BAD_CAST code, BAD_CAST message, NULL);
1792 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
1794 free(string);
1795 free(message);
1796 return error;
1800 /* Common part for ChangeISDSPassword and ChangePasswordOTP.
1801 * @code is output pointer to static string
1802 * @pass_message is output pointer to auto-allocated string
1803 * @arguments is pointer to struct arguments_DS_DsManage_ChangeISDSPassword */
1804 static http_error check_passwd(
1805 const char *username, const char *current_password,
1806 xmlXPathContextPtr xpath_ctx,
1807 char **code, char **pass_message) {
1808 http_error error = HTTP_ERROR_SUCCESS;
1809 char *message = NULL;
1810 char *old_password = NULL, *new_password = NULL;
1811 size_t length;
1813 if (NULL == username || NULL == current_password ||
1814 NULL == code || NULL == pass_message) {
1815 return HTTP_ERROR_SERVER;
1818 *code = "9999";
1821 /* Parse request */
1822 EXTRACT_STRING("isds:dbOldPassword", old_password);
1823 if (NULL == old_password) {
1824 message = strdup("Empty isds:dbOldPassword");
1825 error = HTTP_ERROR_CLIENT;
1826 goto leave;
1828 EXTRACT_STRING("isds:dbNewPassword", new_password);
1829 if (NULL == new_password) {
1830 message = strdup("Empty isds:dbOldPassword");
1831 error = HTTP_ERROR_CLIENT;
1832 goto leave;
1835 /* Check defined cases */
1836 if (strcmp(current_password, old_password)) {
1837 *code = "1090";
1838 message = strdup("Bad current password");
1839 error = HTTP_ERROR_CLIENT;
1840 goto leave;
1843 length = strlen(new_password);
1845 if (length < 8 || length > 32) {
1846 *code = "1066";
1847 message = strdup("Too short or too long");
1848 error = HTTP_ERROR_CLIENT;
1849 goto leave;
1853 const char lower[] = "abcdefghijklmnopqrstuvwxyz";
1854 const char upper[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1855 const char digit[] = "0123456789";
1856 const char special[] = "!#$%&()*+,-.:=?@[]_{}|~";
1857 _Bool has_lower = 0, has_upper = 0, has_digit=0;
1859 for (size_t i = 0; i < length; i++) {
1860 if (NULL != strchr(lower, new_password[i]))
1861 has_lower = 1;
1862 else if (NULL != strchr(upper, new_password[i]))
1863 has_upper = 1;
1864 else if (NULL != strchr(digit, new_password[i]))
1865 has_digit = 1;
1866 else if (NULL == strchr(special, new_password[i])) {
1867 *code = "1079";
1868 message = strdup("Password contains forbidden character");
1869 error = HTTP_ERROR_CLIENT;
1870 goto leave;
1874 if (!has_lower || !has_upper || !has_digit) {
1875 *code = "1080";
1876 message = strdup("Password does not contain lower cased letter, "
1877 "upper cased letter and a digit");
1878 error = HTTP_ERROR_CLIENT;
1879 goto leave;
1883 if (!strcmp(old_password, new_password)) {
1884 *code = "1067";
1885 message = strdup("New password same as current one");
1886 error = HTTP_ERROR_CLIENT;
1887 goto leave;
1890 if (NULL != strstr(new_password, username)) {
1891 *code = "1082";
1892 message = strdup("New password contains user ID");
1893 error = HTTP_ERROR_CLIENT;
1894 goto leave;
1897 for (size_t i = 0; i < length - 2; i++) {
1898 if (new_password[i] == new_password[i+1] &&
1899 new_password[i] == new_password[i+2]) {
1900 *code = "1083";
1901 message = strdup("Password contains sequence "
1902 "of three identical characters");
1903 error = HTTP_ERROR_CLIENT;
1904 goto leave;
1909 const char *forbidden_prefix[] = { "qwert", "asdgf", "12345" };
1910 for (size_t i = 0; i < sizeof(forbidden_prefix)/sizeof(*forbidden_prefix);
1911 i++) {
1912 if (!strncmp(new_password, forbidden_prefix[i],
1913 strlen(forbidden_prefix[i]))) {
1914 *code = "1083";
1915 message = strdup("Password has forbidden prefix");
1916 error = HTTP_ERROR_CLIENT;
1917 goto leave;
1922 *code = "0000";
1923 message = strdup("Success");
1924 leave:
1925 free(old_password);
1926 free(new_password);
1927 *pass_message = message;
1928 return error;
1932 /* Implement ChangeISDSPassword.
1933 * @arguments is pointer to struct arguments_DS_DsManage_ChangeISDSPassword */
1934 static http_error service_ChangeISDSPassword(
1935 xmlXPathContextPtr xpath_ctx,
1936 xmlNodePtr isds_response,
1937 const void *arguments) {
1938 http_error error = HTTP_ERROR_SUCCESS;
1939 char *code = "9999", *message = NULL;
1940 const struct arguments_DS_DsManage_ChangeISDSPassword *configuration =
1941 (const struct arguments_DS_DsManage_ChangeISDSPassword *)arguments;
1943 if (NULL == configuration || NULL == configuration->username ||
1944 NULL == configuration->current_password) {
1945 error = HTTP_ERROR_SERVER;
1946 goto leave;
1949 /* Check for common password rules */
1950 error = check_passwd(
1951 configuration->username, configuration->current_password,
1952 xpath_ctx, &code, &message);
1954 leave:
1955 if (HTTP_ERROR_SERVER != error) {
1956 http_error next_error = insert_isds_status(isds_response, 0,
1957 BAD_CAST code, BAD_CAST message, NULL);
1958 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
1960 free(message);
1961 return error;
1965 /* Implement ChangePasswordOTP.
1966 * @arguments is pointer to struct
1967 * arguments_asws_changePassword_ChangePasswordOTP */
1968 static http_error service_ChangePasswordOTP(
1969 xmlXPathContextPtr xpath_ctx,
1970 xmlNodePtr isds_response,
1971 const void *arguments) {
1972 http_error error = HTTP_ERROR_SUCCESS;
1973 char *code = "9999", *message = NULL;
1974 const struct arguments_asws_changePassword_ChangePasswordOTP *configuration
1975 = (const struct arguments_asws_changePassword_ChangePasswordOTP *)
1976 arguments;
1977 char *method = NULL;
1979 if (NULL == configuration || NULL == configuration->username ||
1980 NULL == configuration->current_password) {
1981 error = HTTP_ERROR_SERVER;
1982 goto leave;
1985 /* Chek for OTP method */
1986 EXTRACT_STRING("isds:dbOTPType", method);
1987 if (NULL == method) {
1988 message = strdup("Empty isds:dbOTPType");
1989 error = HTTP_ERROR_CLIENT;
1990 goto leave;
1992 if ((configuration->method == AUTH_OTP_HMAC && strcmp(method, "HOTP")) ||
1993 (configuration->method == AUTH_OTP_TIME && strcmp(method, "TOTP"))) {
1994 message = strdup("isds:dbOTPType does not match OTP method");
1995 error = HTTP_ERROR_CLIENT;
1996 goto leave;
1999 /* Check for common password rules */
2000 error = check_passwd(
2001 configuration->username, configuration->current_password,
2002 xpath_ctx, &code, &message);
2004 leave:
2005 if (HTTP_ERROR_SERVER != error) {
2006 http_error next_error = insert_isds_status(isds_response, 0,
2007 BAD_CAST code, BAD_CAST message,
2008 BAD_CAST configuration->reference_number);
2009 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
2011 free(message);
2012 free(method);
2013 return error;
2017 /* Implement SendSMSCode.
2018 * @arguments is pointer to struct arguments_asws_changePassword_SendSMSCode */
2019 static http_error service_SendSMSCode(
2020 xmlXPathContextPtr xpath_ctx,
2021 xmlNodePtr isds_response,
2022 const void *arguments) {
2023 const struct arguments_asws_changePassword_SendSMSCode *configuration
2024 = (const struct arguments_asws_changePassword_SendSMSCode *)
2025 arguments;
2026 (void)xpath_ctx;
2028 if (NULL == configuration || NULL == configuration->status_code ||
2029 NULL == configuration->status_message) {
2030 return HTTP_ERROR_SERVER;
2033 return insert_isds_status(isds_response, 0,
2034 BAD_CAST configuration->status_code,
2035 BAD_CAST configuration->status_message,
2036 BAD_CAST configuration->reference_number);
2040 /* List of implemented services */
2041 static struct service services[] = {
2042 { SERVICE_DS_Dz_DummyOperation,
2043 "DS/dz", BAD_CAST ISDS_NS, BAD_CAST "DummyOperation",
2044 service_DummyOperation },
2045 { SERVICE_DS_Dz_ResignISDSDocument,
2046 "DS/dz", BAD_CAST ISDS_NS, BAD_CAST "Re-signISDSDocument",
2047 service_ResignISDSDocument },
2048 { SERVICE_DS_df_DataBoxCreditInfo,
2049 "DS/df", BAD_CAST ISDS_NS, BAD_CAST "DataBoxCreditInfo",
2050 service_DataBoxCreditInfo },
2051 { SERVICE_DS_df_FindDataBox,
2052 "DS/df", BAD_CAST ISDS_NS, BAD_CAST "FindDataBox",
2053 service_FindDataBox },
2054 { SERVICE_DS_df_FindPersonalDataBox,
2055 "DS/df", BAD_CAST ISDS_NS, BAD_CAST "FindPersonalDataBox",
2056 service_FindPersonalDataBox },
2057 { SERVICE_DS_df_GetDataBoxActivityStatus,
2058 "DS/df", BAD_CAST ISDS_NS, BAD_CAST "GetDataBoxActivityStatus",
2059 service_GetDataBoxActivityStatus },
2060 { SERVICE_DS_df_ISDSSearch2,
2061 "DS/df", BAD_CAST ISDS_NS, BAD_CAST "ISDSSearch2",
2062 service_ISDSSearch2 },
2063 { SERVICE_DS_DsManage_ChangeISDSPassword,
2064 "DS/DsManage", BAD_CAST ISDS_NS, BAD_CAST "ChangeISDSPassword",
2065 service_ChangeISDSPassword },
2066 { SERVICE_DS_Dx_EraseMessage,
2067 "DS/dx", BAD_CAST ISDS_NS, BAD_CAST "EraseMessage",
2068 service_EraseMessage },
2069 { SERVICE_asws_changePassword_ChangePasswordOTP,
2070 "/asws/changePassword", BAD_CAST OISDS_NS, BAD_CAST "ChangePasswordOTP",
2071 service_ChangePasswordOTP },
2072 { SERVICE_asws_changePassword_SendSMSCode,
2073 "/asws/changePassword", BAD_CAST OISDS_NS, BAD_CAST "SendSMSCode",
2074 service_SendSMSCode },
2078 /* Makes known all relevant namespaces to given XPath context
2079 * @xpath_ctx is XPath context
2080 * @otp_ns selects name space for the request and response know as "isds".
2081 * Use true for OTP-authenticated password change services, otherwise false.
2082 * @message_ns selects proper message name space. Unsigned and signed
2083 * messages and delivery info's differ in prefix and URI.
2084 * @return 0 in success, otherwise not 0. */
2085 static int register_namespaces(xmlXPathContextPtr xpath_ctx,
2086 const _Bool otp_ns, const message_ns_type message_ns) {
2087 const xmlChar *service_namespace = NULL;
2088 const xmlChar *message_namespace = NULL;
2090 if (!xpath_ctx) return -1;
2092 if (otp_ns) {
2093 service_namespace = BAD_CAST OISDS_NS;
2094 } else {
2095 service_namespace = BAD_CAST ISDS_NS;
2098 switch(message_ns) {
2099 case MESSAGE_NS_1:
2100 message_namespace = BAD_CAST ISDS1_NS; break;
2101 case MESSAGE_NS_UNSIGNED:
2102 message_namespace = BAD_CAST ISDS_NS; break;
2103 case MESSAGE_NS_SIGNED_INCOMING:
2104 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
2105 case MESSAGE_NS_SIGNED_OUTGOING:
2106 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
2107 case MESSAGE_NS_SIGNED_DELIVERY:
2108 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
2109 default:
2110 return -1;
2113 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
2114 return -1;
2115 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", service_namespace))
2116 return -1;
2117 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
2118 return -1;
2119 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
2120 return -1;
2121 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
2122 return -1;
2123 return 0;
2127 /* Parse soap request, pass it to service endpoint and respond to it.
2128 * It sends final HTTP response. */
2129 void soap(const struct http_connection *connection,
2130 const struct service_configuration *configuration,
2131 const void *request, size_t request_length, const char *end_point) {
2132 xmlDocPtr request_doc = NULL;
2133 xmlXPathContextPtr xpath_ctx = NULL;
2134 xmlXPathObjectPtr request_soap_body = NULL;
2135 xmlNodePtr isds_request = NULL; /* pointer only */
2136 _Bool service_handled = 0, service_passed = 0;
2137 xmlDocPtr response_doc = NULL;
2138 xmlNodePtr response_soap_envelope = NULL, response_soap_body = NULL,
2139 isds_response = NULL;
2140 xmlNsPtr soap_ns = NULL, isds_ns = NULL;
2141 char *response_name = NULL;
2142 xmlBufferPtr http_response_body = NULL;
2143 xmlSaveCtxtPtr save_ctx = NULL;
2146 if (NULL == configuration) {
2147 http_send_response_500(connection,
2148 "Second argument of soap() is NULL");
2149 return;
2152 if (NULL == request || request_length == 0) {
2153 http_send_response_400(connection, "Client sent empty body");
2154 return;
2157 request_doc = xmlParseMemory(request, request_length);
2158 if (NULL == request_doc) {
2159 http_send_response_400(connection, "Client sent invalid XML document");
2160 return;
2163 xpath_ctx = xmlXPathNewContext(request_doc);
2164 if (NULL == xpath_ctx) {
2165 xmlFreeDoc(request_doc);
2166 http_send_response_500(connection, "Could not create XPath context");
2167 return;
2170 if (register_namespaces(xpath_ctx, 0, MESSAGE_NS_UNSIGNED)) {
2171 xmlXPathFreeContext(xpath_ctx);
2172 xmlFreeDoc(request_doc);
2173 http_send_response_500(connection,
2174 "Could not register name spaces to the XPath context");
2175 return;
2178 /* Get SOAP Body */
2179 request_soap_body = xmlXPathEvalExpression(
2180 BAD_CAST "/soap:Envelope/soap:Body", xpath_ctx);
2181 if (NULL == request_soap_body) {
2182 xmlXPathFreeContext(xpath_ctx);
2183 xmlFreeDoc(request_doc);
2184 http_send_response_400(connection, "Client sent invalid SOAP request");
2185 return;
2187 if (xmlXPathNodeSetIsEmpty(request_soap_body->nodesetval)) {
2188 xmlXPathFreeObject(request_soap_body);
2189 xmlXPathFreeContext(xpath_ctx);
2190 xmlFreeDoc(request_doc);
2191 http_send_response_400(connection,
2192 "SOAP request does not contain SOAP Body element");
2193 return;
2195 if (request_soap_body->nodesetval->nodeNr > 1) {
2196 xmlXPathFreeObject(request_soap_body);
2197 xmlXPathFreeContext(xpath_ctx);
2198 xmlFreeDoc(request_doc);
2199 http_send_response_400(connection,
2200 "SOAP response has more than one Body element");
2201 return;
2203 isds_request = request_soap_body->nodesetval->nodeTab[0]->children;
2204 if (isds_request->next != NULL) {
2205 xmlXPathFreeObject(request_soap_body);
2206 xmlXPathFreeContext(xpath_ctx);
2207 xmlFreeDoc(request_doc);
2208 http_send_response_400(connection, "SOAP body has more than one child");
2209 return;
2211 if (isds_request->type != XML_ELEMENT_NODE || isds_request->ns == NULL ||
2212 NULL == isds_request->ns->href) {
2213 xmlXPathFreeObject(request_soap_body);
2214 xmlXPathFreeContext(xpath_ctx);
2215 xmlFreeDoc(request_doc);
2216 http_send_response_400(connection,
2217 "SOAP body does not contain a name-space-qualified element");
2218 return;
2221 /* Build SOAP response envelope */
2222 response_doc = xmlNewDoc(BAD_CAST "1.0");
2223 if (!response_doc) {
2224 http_send_response_500(connection,
2225 "Could not build SOAP response document");
2226 goto leave;
2228 response_soap_envelope = xmlNewNode(NULL, BAD_CAST "Envelope");
2229 if (!response_soap_envelope) {
2230 http_send_response_500(connection,
2231 "Could not build SOAP response envelope");
2232 goto leave;
2234 xmlDocSetRootElement(response_doc, response_soap_envelope);
2235 /* Only this way we get namespace definition as @xmlns:soap,
2236 * otherwise we get namespace prefix without definition */
2237 soap_ns = xmlNewNs(response_soap_envelope, BAD_CAST SOAP_NS, NULL);
2238 if(NULL == soap_ns) {
2239 http_send_response_500(connection, "Could not create SOAP name space");
2240 goto leave;
2242 xmlSetNs(response_soap_envelope, soap_ns);
2243 response_soap_body = xmlNewChild(response_soap_envelope, NULL,
2244 BAD_CAST "Body", NULL);
2245 if (!response_soap_body) {
2246 http_send_response_500(connection,
2247 "Could not add Body to SOAP response envelope");
2248 goto leave;
2250 /* Append ISDS response element */
2251 if (-1 == test_asprintf(&response_name, "%s%s", isds_request->name,
2252 "Response")) {
2253 http_send_response_500(connection,
2254 "Could not buld ISDS resposne element name");
2255 goto leave;
2257 isds_response = xmlNewChild(response_soap_body, NULL,
2258 BAD_CAST response_name, NULL);
2259 free(response_name);
2260 if (NULL == isds_response) {
2261 http_send_response_500(connection,
2262 "Could not add ISDS response element to SOAP response body");
2263 goto leave;
2265 isds_ns = xmlNewNs(isds_response, isds_request->ns->href, NULL);
2266 if(NULL == isds_ns) {
2267 http_send_response_500(connection,
2268 "Could not create a name space for the response body");
2269 goto leave;
2271 xmlSetNs(isds_response, isds_ns);
2273 /* Dispatch request to service */
2274 for (size_t i = 0; i < sizeof(services)/sizeof(services[0]); i++) {
2275 if (!strcmp(services[i].end_point, end_point) &&
2276 !xmlStrcmp(services[i].name_space, isds_request->ns->href) &&
2277 !xmlStrcmp(services[i].name, isds_request->name)) {
2278 /* Check if the configuration is enabled and find configuration */
2279 for (const struct service_configuration *service = configuration;
2280 service->name != SERVICE_END; service++) {
2281 if (service->name == services[i].id) {
2282 service_handled = 1;
2283 if (!xmlStrcmp(services[i].name_space, BAD_CAST OISDS_NS)) {
2284 /* Alias "isds" XPath identifier to OISDS_NS */
2285 if (register_namespaces(xpath_ctx, 1,
2286 MESSAGE_NS_UNSIGNED)) {
2287 http_send_response_500(connection,
2288 "Could not register name spaces to the "
2289 "XPath context");
2290 break;
2293 xpath_ctx->node = isds_request;
2294 if (HTTP_ERROR_SERVER != services[i].function(
2295 xpath_ctx,
2296 isds_response,
2297 service->arguments)) {
2298 service_passed = 1;
2299 } else {
2300 http_send_response_500(connection,
2301 "Internal server error while processing "
2302 "ISDS request");
2306 break;
2310 /* Send response */
2311 if (service_passed) {
2312 /* Serialize the SOAP response */
2313 http_response_body = xmlBufferCreate();
2314 if (NULL == http_response_body) {
2315 http_send_response_500(connection,
2316 "Could not create xmlBuffer for response serialization");
2317 goto leave;
2319 /* Last argument 1 means format the XML tree. This is pretty but it breaks
2320 * XML document transport as it adds text nodes (indentiation) between
2321 * elements. */
2322 save_ctx = xmlSaveToBuffer(http_response_body, "UTF-8", 0);
2323 if (NULL == save_ctx) {
2324 http_send_response_500(connection, "Could not create XML serializer");
2325 goto leave;
2327 /* XXX: According LibXML documentation, this function does not return
2328 * meaningful value yet */
2329 xmlSaveDoc(save_ctx, response_doc);
2330 if (-1 == xmlSaveFlush(save_ctx)) {
2331 http_send_response_500(connection,
2332 "Could not serialize SOAP response");
2333 goto leave;
2336 http_send_response_200(connection, http_response_body->content,
2337 http_response_body->use, soap_mime_type);
2340 leave:
2341 xmlSaveClose(save_ctx);
2342 xmlBufferFree(http_response_body);
2344 xmlFreeDoc(response_doc);
2346 xmlXPathFreeObject(request_soap_body);
2347 xmlXPathFreeContext(xpath_ctx);
2348 xmlFreeDoc(request_doc);
2350 if (!service_handled) {
2351 http_send_response_500(connection,
2352 "Requested ISDS service not implemented");