Implement GetDataBoxActivityStatus as isds_get_box_state_history()
[libisds.git] / test / simline / service.c
blob533bed5b5328dd8ec639d00351e289aa1159b5e4
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 <inttypes.h> /* For PRIdMAX */
10 #include <ctype.h> /* for isdigit() */
11 #include <libxml/parser.h>
12 #include <libxml/xpath.h>
13 #include <libxml/xpathInternals.h>
14 #include <libxml/xmlsave.h>
16 static const char *soap_mime_type = "text/xml"; /* SOAP/1.1 requires text/xml */
18 /* Used to choose proper name space for message elements.
19 * See _isds_register_namespaces(). */
20 typedef enum {
21 MESSAGE_NS_1,
22 MESSAGE_NS_UNSIGNED,
23 MESSAGE_NS_SIGNED_INCOMING,
24 MESSAGE_NS_SIGNED_OUTGOING,
25 MESSAGE_NS_SIGNED_DELIVERY,
26 MESSAGE_NS_OTP
27 } message_ns_type;
29 #define SOAP_NS "http://schemas.xmlsoap.org/soap/envelope/"
30 #define SOAP2_NS "http://www.w3.org/2003/05/soap-envelope"
31 #define ISDS1_NS "http://isds.czechpoint.cz"
32 #define ISDS_NS "http://isds.czechpoint.cz/v20"
33 #define OISDS_NS "http://isds.czechpoint.cz/v20/asws"
34 #define SISDS_INCOMING_NS "http://isds.czechpoint.cz/v20/message"
35 #define SISDS_OUTGOING_NS "http://isds.czechpoint.cz/v20/SentMessage"
36 #define SISDS_DELIVERY_NS "http://isds.czechpoint.cz/v20/delivery"
37 #define SCHEMA_NS "http://www.w3.org/2001/XMLSchema"
38 #define DEPOSIT_NS "urn:uschovnaWSDL"
41 struct service {
42 service_id id;
43 const char *end_point;
44 const xmlChar *name_space;
45 const xmlChar *name;
46 http_error (*function) (
47 xmlXPathContextPtr, xmlNodePtr,
48 const void *arguments);
52 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
53 * It respects microseconds too. Microseconds are rounded half up.
54 * In case of error, @time will be freed. */
55 static http_error timestring2timeval(const char *string,
56 struct timeval **time) {
57 struct tm broken;
58 char *offset, *delim, *endptr;
59 const int subsecond_resolution = 6;
60 char subseconds[subsecond_resolution + 1];
61 _Bool round_up = 0;
62 int offset_hours, offset_minutes;
63 int i;
64 long int long_number;
65 #ifdef _WIN32
66 int tmp;
67 #endif
69 if (!time) return HTTP_ERROR_SERVER;
70 if (!string) {
71 free(*time);
72 *time = NULL;
73 return HTTP_ERROR_CLIENT;
76 memset(&broken, 0, sizeof(broken));
78 if (!*time) {
79 *time = calloc(1, sizeof(**time));
80 if (!*time) return HTTP_ERROR_SERVER;
81 } else {
82 memset(*time, 0, sizeof(**time));
86 /* xsd:date is ISO 8601 string, thus ASCII */
87 /*TODO: negative year */
89 #ifdef _WIN32
90 i = 0;
91 if ((tmp = sscanf((const char*)string, "%d-%d-%dT%d:%d:%d%n",
92 &broken.tm_year, &broken.tm_mon, &broken.tm_mday,
93 &broken.tm_hour, &broken.tm_min, &broken.tm_sec,
94 &i)) < 6) {
95 free(*time);
96 *time = NULL;
97 return HTTP_ERROR_CLIENT;
100 broken.tm_year -= 1900;
101 broken.tm_mon--;
102 broken.tm_isdst = -1;
103 offset = (char*)string + i;
104 #else
105 /* Parse date and time without subseconds and offset */
106 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
107 if (!offset) {
108 free(*time);
109 *time = NULL;
110 return HTTP_ERROR_CLIENT;
112 #endif
114 /* Get subseconds */
115 if (*offset == '.' ) {
116 offset++;
118 /* Copy first 6 digits, pad it with zeros.
119 * Current server implementation uses only millisecond resolution. */
120 /* TODO: isdigit() is locale sensitive */
121 for (i = 0;
122 i < subsecond_resolution && isdigit(*offset);
123 i++, offset++) {
124 subseconds[i] = *offset;
126 if (subsecond_resolution == i && isdigit(*offset)) {
127 /* Check 7th digit for rounding */
128 if (*offset >= '5') round_up = 1;
129 offset++;
131 for (; i < subsecond_resolution; i++) {
132 subseconds[i] = '0';
134 subseconds[subsecond_resolution] = '\0';
136 /* Convert it into integer */
137 long_number = strtol(subseconds, &endptr, 10);
138 if (*endptr != '\0' || long_number == LONG_MIN ||
139 long_number == LONG_MAX) {
140 free(*time);
141 *time = NULL;
142 return HTTP_ERROR_SERVER;
144 /* POSIX sys_time.h(0p) defines tv_usec timeval member as su_seconds_t
145 * type. sys_types.h(0p) defines su_seconds_t as "used for time in
146 * microseconds" and "the type shall be a signed integer capable of
147 * storing values at least in the range [-1, 1000000]. */
148 if (long_number < -1 || long_number >= 1000000) {
149 free(*time);
150 *time = NULL;
151 return HTTP_ERROR_CLIENT;
153 (*time)->tv_usec = long_number;
155 /* Round the subseconds */
156 if (round_up) {
157 if (999999 == (*time)->tv_usec) {
158 (*time)->tv_usec = 0;
159 broken.tm_sec++;
160 } else {
161 (*time)->tv_usec++;
165 /* move to the zone offset delimiter or signal NULL*/
166 delim = strchr(offset, '-');
167 if (!delim)
168 delim = strchr(offset, '+');
169 if (!delim)
170 delim = strchr(offset, 'Z');
171 offset = delim;
174 /* Get zone offset */
175 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
176 * "" equals to "Z" and it means UTC zone. */
177 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
178 * colon separator */
179 if (offset && (*offset == '-' || *offset == '+')) {
180 if (2 != sscanf(offset + 1, "%2d:%2d", &offset_hours, &offset_minutes)) {
181 free(*time);
182 *time = NULL;
183 return HTTP_ERROR_CLIENT;
185 if (*offset == '+') {
186 broken.tm_hour -= offset_hours;
187 broken.tm_min -= offset_minutes;
188 } else {
189 broken.tm_hour += offset_hours;
190 broken.tm_min += offset_minutes;
194 /* Convert to time_t */
195 (*time)->tv_sec = _isds_timegm(&broken);
196 if ((*time)->tv_sec == (time_t) -1) {
197 free(*time);
198 *time = NULL;
199 return HTTP_ERROR_CLIENT;
202 return HTTP_ERROR_SUCCESS;
206 /* Following EXTRACT_* macros expect @xpath_ctx, @error, @message,
207 * and leave label. */
208 #define ELEMENT_EXISTS(element, allow_multiple) { \
209 xmlXPathObjectPtr result = NULL; \
210 result = xmlXPathEvalExpression(BAD_CAST element, xpath_ctx); \
211 if (NULL == result) { \
212 error = HTTP_ERROR_SERVER; \
213 goto leave; \
215 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
216 xmlXPathFreeObject(result); \
217 test_asprintf(&message, "Element %s does not exist", element); \
218 error = HTTP_ERROR_CLIENT; \
219 goto leave; \
220 } else { \
221 if (!allow_multiple && result->nodesetval->nodeNr > 1) { \
222 xmlXPathFreeObject(result); \
223 test_asprintf(&message, "Multiple %s element", element); \
224 error = HTTP_ERROR_CLIENT; \
225 goto leave; \
228 xmlXPathFreeObject(result); \
231 #define EXTRACT_STRING(element, string) { \
232 xmlXPathObjectPtr result = NULL; \
233 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
234 if (NULL == result) { \
235 error = HTTP_ERROR_SERVER; \
236 goto leave; \
238 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
239 if (result->nodesetval->nodeNr > 1) { \
240 xmlXPathFreeObject(result); \
241 test_asprintf(&message, "Multiple %s element", element); \
242 error = HTTP_ERROR_CLIENT; \
243 goto leave; \
245 (string) = (char *) \
246 xmlXPathCastNodeSetToString(result->nodesetval); \
247 if (!(string)) { \
248 xmlXPathFreeObject(result); \
249 error = HTTP_ERROR_SERVER; \
250 goto leave; \
253 xmlXPathFreeObject(result); \
256 #define EXTRACT_BOOLEAN(element, booleanPtr) { \
257 char *string = NULL; \
258 EXTRACT_STRING(element, string); \
260 if (NULL != string) { \
261 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
262 if (NULL == (booleanPtr)) { \
263 free(string); \
264 error = HTTP_ERROR_SERVER; \
265 goto leave; \
268 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
269 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
270 *(booleanPtr) = 1; \
271 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
272 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
273 *(booleanPtr) = 0; \
274 else { \
275 test_asprintf(&message, \
276 "%s value is not valid boolean: %s", \
277 element, string); \
278 free(string); \
279 error = HTTP_ERROR_CLIENT; \
280 goto leave; \
283 free(string); \
288 #define EXTRACT_DATE(element, tmPtr) { \
289 char *string = NULL; \
290 EXTRACT_STRING(element, string); \
291 if (NULL != string) { \
292 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
293 if (NULL == (tmPtr)) { \
294 free(string); \
295 error = HTTP_ERROR_SERVER; \
296 goto leave; \
298 error = _server_datestring2tm(string, (tmPtr)); \
299 if (error) { \
300 if (error == HTTP_ERROR_CLIENT) { \
301 test_asprintf(&message, "%s value is not a valid date: %s", \
302 element, string); \
304 free(string); \
305 goto leave; \
307 free(string); \
311 /* Following INSERT_* macros expect @error and leave label */
312 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
314 xmlNodePtr node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
315 (xmlChar *) (string)); \
316 if (NULL == node) { \
317 error = HTTP_ERROR_SERVER; \
318 goto leave; \
322 #define INSERT_STRING(parent, element, string) \
323 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
325 #define INSERT_LONGINTPTR(parent, element, longintPtr) { \
326 if ((longintPtr)) { \
327 char *buffer = NULL; \
328 /* FIXME: locale sensitive */ \
329 if (-1 == test_asprintf(&buffer, "%ld", *(longintPtr))) { \
330 error = HTTP_ERROR_SERVER; \
331 goto leave; \
333 INSERT_STRING(parent, element, buffer) \
334 free(buffer); \
335 } else { INSERT_STRING(parent, element, NULL) } \
338 #define INSERT_ULONGINTPTR(parent, element, ulongintPtr) { \
339 if ((ulongintPtr)) { \
340 char *buffer = NULL; \
341 /* FIXME: locale sensitive */ \
342 if (-1 == test_asprintf(&buffer, "%lu", *(ulongintPtr))) { \
343 error = HTTP_ERROR_SERVER; \
344 goto leave; \
346 INSERT_STRING(parent, element, buffer) \
347 free(buffer); \
348 } else { INSERT_STRING(parent, element, NULL) } \
351 #define INSERT_BOOLEANPTR(parent, element, booleanPtr) { \
352 if (NULL != (booleanPtr)) { \
353 char *buffer = NULL; \
354 buffer = *(booleanPtr) ? "true" : "false"; \
355 INSERT_STRING(parent, element, buffer) \
356 } else { INSERT_STRING(parent, element, NULL) } \
359 #define INSERT_TIMEVALPTR(parent, element, timevalPtr) { \
360 if (NULL != (timevalPtr)) { \
361 char *buffer = NULL; \
362 error = timeval2timestring(timevalPtr, &buffer); \
363 if (error) { \
364 free(buffer); \
365 goto leave; \
367 INSERT_STRING(parent, element, buffer); \
368 free(buffer); \
369 } else { \
370 INSERT_STRING(parent, element, NULL); \
374 #define INSERT_TMPTR(parent, element, tmPtr) { \
375 if (NULL != (tmPtr)) { \
376 char *buffer = NULL; \
377 error = tm2datestring(tmPtr, &buffer); \
378 if (error) { \
379 free(buffer); \
380 goto leave; \
382 INSERT_STRING(parent, element, buffer); \
383 free(buffer); \
384 } else { \
385 INSERT_STRING(parent, element, NULL); \
389 #define INSERT_ELEMENT(child, parent, element) \
391 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
392 if (NULL == (child)) { \
393 error = HTTP_ERROR_SERVER; \
394 goto leave; \
398 /* TODO: These functions (element_exists(), extract_...(), ...) will replace
399 * the macros (ELEMENT_EXISTS, EXTRACT_...). The compiled code will be
400 * smaller, the compilation will be faster. */
402 /* Check an element exists.
403 * @code is a static output ISDS error code
404 * @error_message is a reallocated output ISDS error message
405 * @xpath_ctx is a current XPath context
406 * @element_name is name of an element to check
407 * @allow_multiple is false to require exactly one element. True to require
408 * one or more elements.
409 * @return HTTP_ERROR_SUCCESS or an appropriate error code. */
410 static http_error element_exists(const char **code, char **message,
411 xmlXPathContextPtr xpath_ctx, const char *element_name,
412 _Bool allow_multiple) {
413 xmlXPathObjectPtr result = NULL;
415 result = xmlXPathEvalExpression(BAD_CAST element_name, xpath_ctx);
416 if (NULL == result) {
417 return HTTP_ERROR_SERVER;
419 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
420 xmlXPathFreeObject(result);
421 *code = "9999";
422 test_asprintf(message, "Element %s does not exist", element_name);
423 return HTTP_ERROR_CLIENT;
424 } else {
425 if (!allow_multiple && result->nodesetval->nodeNr > 1) {
426 xmlXPathFreeObject(result);
427 *code = "9999";
428 test_asprintf(message, "Multiple %s element", element_name);
429 return HTTP_ERROR_CLIENT;
432 xmlXPathFreeObject(result);
434 return HTTP_ERROR_SUCCESS;
438 /* Locate a children element.
439 * @code is a static output ISDS error code
440 * @error_message is a reallocated output ISDS error message
441 * @xpath_ctx is a current XPath context
442 * @element_name is name of an element to select
443 * @node is output pointer to located element node
444 * @return HTTP_ERROR_SUCCESS or an appropriate error code. */
445 static http_error select_element(const char **code, char **message,
446 xmlXPathContextPtr xpath_ctx, const char *element_name,
447 xmlNodePtr *node) {
448 xmlXPathObjectPtr result = NULL;
450 result = xmlXPathEvalExpression(BAD_CAST element_name, xpath_ctx);
451 if (NULL == result) {
452 return HTTP_ERROR_SERVER;
454 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
455 xmlXPathFreeObject(result);
456 *code = "9999";
457 test_asprintf(message, "Element %s does not exist", element_name);
458 return HTTP_ERROR_CLIENT;
459 } else {
460 if (result->nodesetval->nodeNr > 1) {
461 xmlXPathFreeObject(result);
462 *code = "9999";
463 test_asprintf(message, "Multiple %s element", element_name);
464 return HTTP_ERROR_CLIENT;
467 *node = result->nodesetval->nodeTab[0];
468 xmlXPathFreeObject(result);
470 return HTTP_ERROR_SUCCESS;
474 /* Extract @element_name's value as a string.
475 * @code is a static output ISDS error code
476 * @error_message is a reallocated output ISDS error message
477 * @xpath_ctx is a current XPath context
478 * @element_name is name of a element whose child text node to extract
479 * @string is the extraced allocated string value, or NULL if empty or the
480 * element does not exist.
481 * @return HTTP_ERROR_SUCCESS or an appropriate error code. */
482 static http_error extract_string(const char **code, char **message,
483 xmlXPathContextPtr xpath_ctx, const char *element_name,
484 char **string) {
485 http_error error = HTTP_ERROR_SUCCESS;
486 xmlXPathObjectPtr result = NULL;
487 char *buffer = NULL;
489 if (-1 == test_asprintf(&buffer, "%s/text()", element_name)) {
490 error = HTTP_ERROR_SERVER;
491 goto leave;
493 result = xmlXPathEvalExpression(BAD_CAST buffer, xpath_ctx);
494 free(buffer);
495 if (NULL == result) {
496 error = HTTP_ERROR_SERVER;
497 goto leave;
499 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
500 if (result->nodesetval->nodeNr > 1) {
501 xmlXPathFreeObject(result);
502 *code = "9999";
503 test_asprintf(message, "Multiple %s element", element_name);
504 error = HTTP_ERROR_CLIENT;
505 goto leave;
507 *string = (char *) \
508 xmlXPathCastNodeSetToString(result->nodesetval);
509 if (!(*string)) {
510 xmlXPathFreeObject(result);
511 error = HTTP_ERROR_SERVER;
512 goto leave;
515 xmlXPathFreeObject(result);
517 leave:
518 return error;
522 /* Compare dates represented by pointer to struct tm.
523 * @return 0 if equalued, non-0 otherwise. */
524 static int datecmp(const struct tm *a, const struct tm *b) {
525 if (NULL == a && b == NULL) return 0;
526 if ((NULL == a && b != NULL) || (NULL != a && b == NULL)) return 1;
527 if (a->tm_year != b->tm_year) return 1;
528 if (a->tm_mon != b->tm_mon) return 1;
529 if (a->tm_mday != b->tm_mday) return 1;
530 return 0;
534 /* Compare times represented by pointer to struct timeval.
535 * @return 0 if equalued, non-0 otherwise. */
536 static int timecmp(const struct timeval *a, const struct timeval *b) {
537 if (NULL == a && b == NULL) return 0;
538 if ((NULL == a && b != NULL) || (NULL != a && b == NULL)) return 1;
539 if (a->tv_sec != b->tv_sec) return 1;
540 if (a->tv_usec != b->tv_usec) return 1;
541 return 0;
545 /* Checks an @element_name's value is an @expected_value string.
546 * @code is a static output ISDS error code
547 * @error_message is a reallocated output ISDS error message
548 * @xpath_ctx is a current XPath context
549 * @element_name is name of a element to check
550 * @must_exist is true if the @element_name must exist even if @expected_value
551 * is NULL.
552 * @expected_value is an expected string value
553 * @return HTTP_ERROR_SUCCESS if the @element_name element's value is
554 * @expected_value. HTTP_ERROR_CLIENT if not equaled, HTTP_ERROR_SERVER if an
555 * internal error occured. */
556 static http_error element_equals_string(const char **code, char **message,
557 xmlXPathContextPtr xpath_ctx, const char *element_name,
558 _Bool must_exist, const char *expected_value) {
559 http_error error = HTTP_ERROR_SUCCESS;
560 char *string = NULL;
562 if (must_exist) {
563 error = element_exists(code, message, xpath_ctx, element_name, 0);
564 if (HTTP_ERROR_SUCCESS != error)
565 goto leave;
568 error = extract_string(code, message, xpath_ctx, element_name, &string);
569 if (HTTP_ERROR_SUCCESS != error)
570 goto leave;
572 if (NULL != expected_value) {
573 if (NULL == string) {
574 *code = "9999";
575 test_asprintf(message, "Empty %s element", element_name);
576 error = HTTP_ERROR_CLIENT;
577 goto leave;
579 if (xmlStrcmp(BAD_CAST expected_value, BAD_CAST string)) {
580 *code = "9999";
581 test_asprintf(message,
582 "Unexpected %s element value: expected=`%s', got=`%s'",
583 element_name, expected_value, string);
584 error = HTTP_ERROR_CLIENT;
585 goto leave;
587 } else {
588 if (NULL != string && *string != '\0') {
589 *code = "9999";
590 test_asprintf(message,
591 "Unexpected %s element value: "
592 "expected empty string, got=`%s'",
593 element_name, string);
594 error = HTTP_ERROR_CLIENT;
595 goto leave;
599 leave:
600 free(string);
601 return error;
605 /* Checks an @element_name's value is an @expected_value integer.
606 * @code is a static output ISDS error code
607 * @error_message is a reallocated output ISDS error message
608 * @xpath_ctx is a current XPath context
609 * @element_name is name of a element to check
610 * @must_exist is true if the @element_name must exist even if @expected_value
611 * is NULL.
612 * @expected_value is an expected integer value.
613 * @return HTTP_ERROR_SUCCESS if the @element_name element's value is
614 * @expected_value. HTTP_ERROR_CLIENT if not equaled, HTTP_ERROR_SERVER if an
615 * internal error occured. */
616 static http_error element_equals_integer(const char **code, char **message,
617 xmlXPathContextPtr xpath_ctx, const char *element_name,
618 _Bool must_exist, const long int *expected_value) {
619 http_error error = HTTP_ERROR_SUCCESS;
620 char *string = NULL;
621 long int number;
622 char *endptr;
624 if (must_exist) {
625 error = element_exists(code, message, xpath_ctx, element_name, 0);
626 if (HTTP_ERROR_SUCCESS != error)
627 goto leave;
630 error = extract_string(code, message, xpath_ctx, element_name, &string);
631 if (HTTP_ERROR_SUCCESS != error)
632 goto leave;
634 if (NULL != expected_value) {
635 if (NULL == string) {
636 *code = "9999";
637 test_asprintf(message, "Empty %s element", element_name);
638 error = HTTP_ERROR_CLIENT;
639 goto leave;
641 number = strtol(string, &endptr, 10);
642 if (*endptr != '\0') {
643 *code = "9999";
644 test_asprintf(message,
645 "%s element value is not a valid integer: %s",
646 element_name, string);
647 error = HTTP_ERROR_CLIENT;
648 goto leave;
650 if (number == LONG_MIN || number == LONG_MAX) { \
651 *code = "9999";
652 test_asprintf(message, \
653 "%s element value is out of range of long int: %s",
654 element_name, string);
655 error = HTTP_ERROR_SERVER;
656 goto leave;
658 free(string); string = NULL;
659 if (number != *expected_value) {
660 *code = "9999";
661 test_asprintf(message,
662 "Unexpected %s element value: expected=`%ld', got=`%ld'",
663 element_name, *expected_value, number);
664 error = HTTP_ERROR_CLIENT;
665 goto leave;
667 } else {
668 if (NULL != string && *string != '\0') {
669 *code = "9999";
670 test_asprintf(message,
671 "Unexpected %s element value: expected no text node, got=`%s'",
672 element_name, string);
673 error = HTTP_ERROR_CLIENT;
674 goto leave;
678 leave:
679 free(string);
680 return error;
684 /* Checks an @element_name's value is an @expected_value boolean.
685 * @code is a static output ISDS error code
686 * @error_message is an reallocated output ISDS error message
687 * @xpath_ctx is a current XPath context
688 * @element_name is name of a element to check
689 * @must_exist is true if the @element_name must exist even if @expected_value
690 * is NULL.
691 * @expected_value is an expected boolean value
692 * @return HTTP_ERROR_SUCCESS if the @element_name element's value is
693 * @expected_value. HTTP_ERROR_CLIENT if not equaled, HTTP_ERROR_SERVER if an
694 * internal error occured. */
695 static http_error element_equals_boolean(const char **code, char **message,
696 xmlXPathContextPtr xpath_ctx, const char *element_name,
697 _Bool must_exist, const _Bool *expected_value) {
698 http_error error = HTTP_ERROR_SUCCESS;
699 char *string = NULL;
700 _Bool value;
702 if (must_exist) {
703 error = element_exists(code, message, xpath_ctx, element_name, 0);
704 if (HTTP_ERROR_SUCCESS != error)
705 goto leave;
708 error = extract_string(code, message, xpath_ctx, element_name, &string);
709 if (HTTP_ERROR_SUCCESS != error)
710 goto leave;
712 if (NULL != expected_value) {
713 if (NULL == string) {
714 *code = "9999";
715 test_asprintf(message, "Empty %s element", element_name);
716 error = HTTP_ERROR_CLIENT;
717 goto leave;
719 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") ||
720 !xmlStrcmp((xmlChar *)string, BAD_CAST "1"))
721 value = 1;
722 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") ||
723 !xmlStrcmp((xmlChar *)string, BAD_CAST "0"))
724 value = 0;
725 else {
726 *code = "9999";
727 test_asprintf(message,
728 "%s element value is not a valid boolean: %s",
729 element_name, string);
730 error = HTTP_ERROR_CLIENT;
731 goto leave;
733 if (*expected_value != value) {
734 *code = "9999";
735 test_asprintf(message,
736 "Unexpected %s element value: expected=%d, got=%d",
737 element_name, *expected_value, string);
738 error = HTTP_ERROR_CLIENT;
739 goto leave;
741 } else {
742 if (NULL != string && *string != '\0') {
743 *code = "9999";
744 test_asprintf(message,
745 "Unexpected %s element value: "
746 "expected empty string, got=`%s'",
747 element_name, string);
748 error = HTTP_ERROR_CLIENT;
749 goto leave;
753 leave:
754 free(string);
755 return error;
759 /* Checks an @element_name's value is an @expected_value date.
760 * @code is a static output ISDS error code
761 * @error_message is an reallocated output ISDS error message
762 * @xpath_ctx is a current XPath context
763 * @element_name is name of a element to check
764 * @must_exist is true if the @element_name must exist even if @expected_value
765 * is NULL.
766 * @expected_value is an expected boolean value
767 * @return HTTP_ERROR_SUCCESS if the @element_name element's value is
768 * @expected_value. HTTP_ERROR_CLIENT if not equaled, HTTP_ERROR_SERVER if an
769 * internal error occured. */
770 static http_error element_equals_date(const char **code, char **message,
771 xmlXPathContextPtr xpath_ctx, const char *element_name,
772 _Bool must_exist, const struct tm *expected_value) {
773 http_error error = HTTP_ERROR_SUCCESS;
774 char *string = NULL;
775 struct tm value;
777 if (must_exist) {
778 error = element_exists(code, message, xpath_ctx, element_name, 0);
779 if (HTTP_ERROR_SUCCESS != error)
780 goto leave;
783 error = extract_string(code, message, xpath_ctx, element_name, &string);
784 if (HTTP_ERROR_SUCCESS != error)
785 goto leave;
787 if (NULL != expected_value) {
788 if (NULL == string) {
789 *code = "9999";
790 test_asprintf(message, "Empty %s element", element_name);
791 error = HTTP_ERROR_CLIENT;
792 goto leave;
794 error = _server_datestring2tm(string, &value);
795 if (error) {
796 if (error == HTTP_ERROR_CLIENT) { \
797 test_asprintf(message, "%s value is not a valid date: %s",
798 element_name, string);
800 goto leave;
802 if (datecmp(expected_value, &value)) {
803 *code = "9999";
804 test_asprintf(message, "Unexpected %s element value: "
805 "expected=%d-%02d-%02d, got=%d-%02d-%02d", element_name,
806 expected_value->tm_year + 1900, expected_value->tm_mon + 1,
807 expected_value->tm_mday,
808 value.tm_year + 1900, value.tm_mon + 1, value.tm_mday);
809 error = HTTP_ERROR_CLIENT;
810 goto leave;
812 } else {
813 if (NULL != string && *string != '\0') {
814 *code = "9999";
815 test_asprintf(message,
816 "Unexpected %s element value: "
817 "expected empty value, got=`%s'",
818 element_name, string);
819 error = HTTP_ERROR_CLIENT;
820 goto leave;
824 leave:
825 free(string);
826 return error;
830 /* Checks an @element_name's value is an @expected_value time.
831 * @code is a static output ISDS error code
832 * @error_message is an reallocated output ISDS error message
833 * @xpath_ctx is a current XPath context
834 * @element_name is name of a element to check
835 * @must_exist is true if the @element_name must exist even if @expected_value
836 * is NULL.
837 * @expected_value is an expected boolean value
838 * @return HTTP_ERROR_SUCCESS if the @element_name element's value is
839 * @expected_value. HTTP_ERROR_CLIENT if not equaled, HTTP_ERROR_SERVER if an
840 * internal error occured. */
841 static http_error element_equals_time(const char **code, char **message,
842 xmlXPathContextPtr xpath_ctx, const char *element_name,
843 _Bool must_exist, const struct timeval *expected_value) {
844 http_error error = HTTP_ERROR_SUCCESS;
845 char *string = NULL;
846 struct timeval *value = NULL;
848 if (must_exist) {
849 error = element_exists(code, message, xpath_ctx, element_name, 0);
850 if (HTTP_ERROR_SUCCESS != error)
851 goto leave;
854 error = extract_string(code, message, xpath_ctx, element_name, &string);
855 if (HTTP_ERROR_SUCCESS != error)
856 goto leave;
858 if (NULL != expected_value) {
859 if (NULL == string) {
860 *code = "9999";
861 test_asprintf(message, "Empty %s element", element_name);
862 error = HTTP_ERROR_CLIENT;
863 goto leave;
865 error = timestring2timeval(string, &value);
866 if (error) {
867 if (error == HTTP_ERROR_CLIENT) { \
868 test_asprintf(message, "%s value is not a valid time: %s",
869 element_name, string);
871 goto leave;
873 if (timecmp(expected_value, value)) {
874 *code = "9999";
875 test_asprintf(message, "Unexpected %s element value: "
876 "expected=%ds:%" PRIdMAX "us, got=%ds:%" PRIdMAX "us",
877 element_name,
878 expected_value->tv_sec, (intmax_t)expected_value->tv_usec,
879 value->tv_sec, (intmax_t)value->tv_usec);
880 error = HTTP_ERROR_CLIENT;
881 goto leave;
883 } else {
884 if (NULL != string && *string != '\0') {
885 *code = "9999";
886 test_asprintf(message,
887 "Unexpected %s element value: "
888 "expected empty value, got=`%s'",
889 element_name, string);
890 error = HTTP_ERROR_CLIENT;
891 goto leave;
895 leave:
896 free(string);
897 free(value);
898 return error;
902 /* Insert dmStatus or similar subtree
903 * @parent is element to insert to
904 * @dm is true for dmStatus, otherwise dbStatus
905 * @code is status code as string
906 * @message is UTF-8 encoded message
907 * @db_ref_number is optinal reference number propagated if not @dm
908 * @return 0 on success, otherwise non-0. */
909 static http_error insert_isds_status(xmlNodePtr parent, _Bool dm,
910 const xmlChar *code, const xmlChar *message,
911 const xmlChar *db_ref_number) {
912 http_error error = HTTP_ERROR_SUCCESS;
913 xmlNodePtr status;
915 if (NULL == code || NULL == message) {
916 error = HTTP_ERROR_SERVER;
917 goto leave;
920 INSERT_ELEMENT(status, parent, (dm) ? "dmStatus" : "dbStatus");
921 INSERT_STRING(status, (dm) ? "dmStatusCode" : "dbStatusCode", code);
922 INSERT_STRING(status, (dm) ? "dmStatusMessage" : "dbStatusMessage", message);
923 if (!dm && NULL != db_ref_number) {
924 INSERT_STRING(status, "dbStatusRefNumber", db_ref_number);
927 leave:
928 return error;
932 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
933 static http_error tm2datestring(const struct tm *time, char **string) {
934 if (NULL == time || NULL == string) return HTTP_ERROR_SERVER;
936 if (-1 == test_asprintf(string, "%d-%02d-%02d",
937 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
938 return HTTP_ERROR_SERVER;
940 return HTTP_ERROR_SUCCESS;
944 /* Convert struct timeval *@time to UTF-8 ISO 8601 date-time @string. It
945 * respects the @time microseconds too. */
946 static http_error timeval2timestring(const struct timeval *time,
947 char **string) {
948 struct tm broken;
950 if (!time || !string) return HTTP_ERROR_SERVER;
952 if (!gmtime_r(&time->tv_sec, &broken)) return HTTP_ERROR_SERVER;
953 if (time->tv_usec < 0 || time->tv_usec > 999999) return HTTP_ERROR_SERVER;
955 /* TODO: small negative year should be formatted as "-0012". This is not
956 * true for glibc "%04d". We should implement it.
957 * time->tv_usec type is su_seconds_t which is required to be signed
958 * integer to accomodate values from range [-1, 1000000].
959 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
960 if (-1 == test_asprintf(string,
961 "%04d-%02d-%02dT%02d:%02d:%02d.%06" PRIdMAX,
962 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
963 broken.tm_hour, broken.tm_min, broken.tm_sec,
964 (intmax_t)time->tv_usec))
965 return HTTP_ERROR_SERVER;
967 return HTTP_ERROR_SUCCESS;
971 /* Implement DummyOperation */
972 static http_error service_DummyOperation(
973 xmlXPathContextPtr xpath_ctx,
974 xmlNodePtr isds_response,
975 const void *arguments) {
976 (void)xpath_ctx;
977 (void)arguments;
979 return insert_isds_status(isds_response, 1, BAD_CAST "0000",
980 BAD_CAST "Success", NULL);
984 /* Implement Re-signISDSDocument.
985 * It sends document from request back.
986 * @arguments is pointer to struct arguments_DS_Dz_ResignISDSDocument */
987 static http_error service_ResignISDSDocument(
988 xmlXPathContextPtr xpath_ctx,
989 xmlNodePtr isds_response,
990 const void *arguments) {
991 http_error error = HTTP_ERROR_SUCCESS;
992 const char *code = "9999";
993 char *message = NULL;
994 const struct arguments_DS_Dz_ResignISDSDocument *configuration =
995 (const struct arguments_DS_Dz_ResignISDSDocument *)arguments;
996 char *data = NULL;
998 if (NULL == configuration || NULL == configuration->status_code ||
999 NULL == configuration->status_message) {
1000 error = HTTP_ERROR_SERVER;
1001 goto leave;
1004 EXTRACT_STRING("isds:dmDoc", data);
1005 if (NULL == data) {
1006 message = strdup("Missing isds:dmDoc");
1007 error = HTTP_ERROR_CLIENT;
1008 goto leave;
1012 /* dmResultDoc is mandatory in response */
1013 if (xmlStrcmp(BAD_CAST configuration->status_code, BAD_CAST "0000")) {
1014 free(data);
1015 data = NULL;
1017 INSERT_STRING(isds_response, "dmResultDoc", data);
1019 if (configuration->valid_to != NULL) {
1020 error = tm2datestring(configuration->valid_to, &data);
1021 if (error) {
1022 message = strdup("Could not format date");
1023 goto leave;
1025 INSERT_STRING(isds_response, "dmValidTo", data);
1028 code = configuration->status_code;
1029 message = strdup(configuration->status_message);
1031 leave:
1032 if (HTTP_ERROR_SERVER != error) {
1033 http_error next_error = insert_isds_status(isds_response, 1,
1034 BAD_CAST code, BAD_CAST message, NULL);
1035 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
1037 free(data);
1038 free(message);
1039 return error;
1043 /* Implement EraseMessage.
1044 * @arguments is pointer to struct arguments_DS_DsManage_ChangeISDSPassword */
1045 static http_error service_EraseMessage(
1046 xmlXPathContextPtr xpath_ctx,
1047 xmlNodePtr isds_response,
1048 const void *arguments) {
1049 http_error error = HTTP_ERROR_SUCCESS;
1050 char *code = "9999", *message = NULL;
1051 const struct arguments_DS_Dx_EraseMessage *configuration =
1052 (const struct arguments_DS_Dx_EraseMessage *)arguments;
1053 char *message_id = NULL;
1054 _Bool *incoming = NULL;
1056 if (NULL == configuration || NULL == configuration->message_id) {
1057 error = HTTP_ERROR_SERVER;
1058 goto leave;
1061 EXTRACT_STRING("isds:dmID", message_id);
1062 if (NULL == message_id) {
1063 message = strdup("Missing isds:dmID");
1064 error = HTTP_ERROR_CLIENT;
1065 goto leave;
1067 EXTRACT_BOOLEAN("isds:dmIncoming", incoming);
1068 if (NULL == incoming) {
1069 message = strdup("Missing isds:dmIncoming");
1070 error = HTTP_ERROR_CLIENT;
1071 goto leave;
1074 if (xmlStrcmp((const xmlChar *) configuration->message_id,
1075 (const xmlChar *) message_id)) {
1076 code = "1219";
1077 message = strdup("Message is not in the long term storage");
1078 error = HTTP_ERROR_CLIENT;
1079 goto leave;
1081 if (configuration->incoming != *incoming) {
1082 code = "1219";
1083 message = strdup("Message direction mismatches");
1084 error = HTTP_ERROR_CLIENT;
1085 goto leave;
1088 code = "0000";
1089 message = strdup("Success");
1090 leave:
1091 if (HTTP_ERROR_SERVER != error) {
1092 http_error next_error = insert_isds_status(isds_response, 1,
1093 BAD_CAST code, BAD_CAST message, NULL);
1094 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
1096 free(incoming);
1097 free(message_id);
1098 free(message);
1099 return error;
1103 /* Insert list of credit info as XSD:tCiRecord XML tree.
1104 * @isds_response is XML node with the response
1105 * @history is list of struct server_credit_event. If NULL, no ciRecord XML
1106 * subtree will be created. */
1107 static http_error insert_ciRecords(xmlNodePtr isds_response,
1108 const struct server_list *history) {
1109 http_error error = HTTP_ERROR_SUCCESS;
1110 xmlNodePtr records, record;
1112 if (NULL == isds_response) return HTTP_ERROR_SERVER;
1113 if (NULL == history) return HTTP_ERROR_SUCCESS;
1115 INSERT_ELEMENT(records, isds_response, "ciRecords");
1116 for (const struct server_list *item = history; NULL != item;
1117 item = item->next) {
1118 const struct server_credit_event *event =
1119 (struct server_credit_event*)item->data;
1121 INSERT_ELEMENT(record, records, "ciRecord");
1122 if (NULL == event) continue;
1124 INSERT_TIMEVALPTR(record, "ciEventTime", event->time);
1125 switch(event->type) {
1126 case SERVER_CREDIT_CHARGED:
1127 INSERT_STRING(record, "ciEventType", "1");
1128 INSERT_STRING(record, "ciTransID",
1129 event->details.charged.transaction);
1130 break;
1131 case SERVER_CREDIT_DISCHARGED:
1132 INSERT_STRING(record, "ciEventType", "2");
1133 INSERT_STRING(record, "ciTransID",
1134 event->details.discharged.transaction);
1135 break;
1136 case SERVER_CREDIT_MESSAGE_SENT:
1137 INSERT_STRING(record, "ciEventType", "3");
1138 INSERT_STRING(record, "ciRecipientID",
1139 event->details.message_sent.recipient);
1140 INSERT_STRING(record, "ciPDZID",
1141 event->details.message_sent.message_id);
1142 break;
1143 case SERVER_CREDIT_STORAGE_SET:
1144 INSERT_STRING(record, "ciEventType", "4");
1145 INSERT_LONGINTPTR(record, "ciNewCapacity",
1146 &event->details.storage_set.new_capacity);
1147 INSERT_TMPTR(record, "ciNewFrom",
1148 event->details.storage_set.new_valid_from);
1149 INSERT_TMPTR(record, "ciNewTo",
1150 event->details.storage_set.new_valid_to);
1151 INSERT_LONGINTPTR(record, "ciOldCapacity",
1152 event->details.storage_set.old_capacity);
1153 INSERT_TMPTR(record, "ciOldFrom",
1154 event->details.storage_set.old_valid_from);
1155 INSERT_TMPTR(record, "ciOldTo",
1156 event->details.storage_set.old_valid_to);
1157 INSERT_STRING(record, "ciDoneBy",
1158 event->details.storage_set.initiator);
1159 break;
1160 case SERVER_CREDIT_EXPIRED:
1161 INSERT_STRING(record, "ciEventType", "5");
1162 break;
1163 default:
1164 error = HTTP_ERROR_SERVER;
1165 goto leave;
1167 INSERT_LONGINTPTR(record, "ciCreditChange", &event->credit_change);
1168 INSERT_LONGINTPTR(record, "ciCreditAfter", &event->new_credit);
1172 leave:
1173 return error;
1177 /* Implement DataBoxCreditInfo.
1178 * @arguments is pointer to struct arguments_DS_df_DataBoxCreditInfo */
1179 static http_error service_DataBoxCreditInfo(
1180 xmlXPathContextPtr xpath_ctx,
1181 xmlNodePtr isds_response,
1182 const void *arguments) {
1183 http_error error = HTTP_ERROR_SUCCESS;
1184 const char *code = "9999";
1185 char *message = NULL;
1186 const struct arguments_DS_df_DataBoxCreditInfo *configuration =
1187 (const struct arguments_DS_df_DataBoxCreditInfo *)arguments;
1188 char *box_id = NULL;
1189 struct tm *from_date = NULL, *to_date = NULL;
1191 if (NULL == configuration || NULL == configuration->status_code ||
1192 NULL == configuration->status_message) {
1193 error = HTTP_ERROR_SERVER;
1194 goto leave;
1197 EXTRACT_STRING("isds:dbID", box_id);
1198 if (NULL == box_id) {
1199 message = strdup("Missing isds:dbID");
1200 error = HTTP_ERROR_CLIENT;
1201 goto leave;
1203 if (NULL != configuration->box_id &&
1204 xmlStrcmp(BAD_CAST configuration->box_id,
1205 BAD_CAST box_id)) {
1206 code = "9999";
1207 message = strdup("Unexpected isds:dbID value");
1208 error = HTTP_ERROR_CLIENT;
1209 goto leave;
1212 ELEMENT_EXISTS("isds:ciFromDate", 0);
1213 EXTRACT_DATE("isds:ciFromDate", from_date);
1214 if (datecmp(configuration->from_date, from_date)) {
1215 code = "9999";
1216 message = strdup("Unexpected isds:ciFromDate value");
1217 error = HTTP_ERROR_CLIENT;
1218 goto leave;
1221 ELEMENT_EXISTS("isds:ciTodate", 0);
1222 EXTRACT_DATE("isds:ciTodate", to_date);
1223 if (datecmp(configuration->to_date, to_date)) {
1224 code = "9999";
1225 message = strdup("Unexpected isds:ciTodate value");
1226 error = HTTP_ERROR_CLIENT;
1227 goto leave;
1230 INSERT_LONGINTPTR(isds_response, "currentCredit",
1231 &configuration->current_credit);
1232 INSERT_STRING(isds_response, "notifEmail", configuration->email);
1233 if ((error = insert_ciRecords(isds_response, configuration->history))) {
1234 goto leave;
1237 code = configuration->status_code;
1238 message = strdup(configuration->status_message);
1239 leave:
1240 if (HTTP_ERROR_SERVER != error) {
1241 http_error next_error = insert_isds_status(isds_response, 0,
1242 BAD_CAST code, BAD_CAST message, NULL);
1243 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
1245 free(box_id);
1246 free(from_date);
1247 free(to_date);
1248 free(message);
1249 return error;
1253 /* Insert list of fulltext search results as XSD:tdbResultsArray XML tree.
1254 * @isds_response is XML node with the response
1255 * @results is list of struct server_db_result *.
1256 * @create_empty_root is true to create dbResults element even if @results is
1257 * empty. */
1258 static http_error insert_tdbResultsArray(xmlNodePtr isds_response,
1259 const struct server_list *results, _Bool create_empty_root) {
1260 http_error error = HTTP_ERROR_SUCCESS;
1261 xmlNodePtr root, entry;
1263 if (NULL == isds_response) return HTTP_ERROR_SERVER;
1265 if (NULL != results || create_empty_root)
1266 INSERT_ELEMENT(root, isds_response, "dbResults");
1268 if (NULL == results) return HTTP_ERROR_SUCCESS;
1270 for (const struct server_list *item = results; NULL != item;
1271 item = item->next) {
1272 const struct server_db_result *result =
1273 (struct server_db_result *)item->data;
1275 INSERT_ELEMENT(entry, root, "dbResult");
1276 if (NULL == result) continue;
1278 INSERT_STRING(entry, "dbID", result->id);
1279 INSERT_STRING(entry, "dbType", result->type);
1280 INSERT_STRING(entry, "dbName", result->name);
1281 INSERT_STRING(entry, "dbAddress", result->address);
1282 INSERT_TMPTR(entry, "dbBiDate", result->birth_date);
1283 INSERT_STRING(entry, "dbICO", result->ic);
1284 INSERT_BOOLEANPTR(entry, "dbEffectiveOVM", &result->ovm);
1285 INSERT_STRING(entry, "dbSendOptions", result->send_options);
1288 leave:
1289 return error;
1293 /* Insert list of search results as XSD:tDbOwnersArray or
1294 * XSD:tDbPersOwnerArray XML tree.
1295 * @isds_response is XML node with the response
1296 * @results is list of struct server_owner_info *.
1297 * @create_empty_root is true to create dbResults element even if @results is
1298 * empty.
1299 * @pfo is false to serialize tDbOwnerInfo elements only, is true to
1300 * serialize tdbPersonalOwnerInfo elements only. */
1301 static http_error insert_tDbOwnersArray(xmlNodePtr isds_response,
1302 const struct server_list *results, _Bool create_empty_root,
1303 _Bool pfo) {
1304 http_error error = HTTP_ERROR_SUCCESS;
1305 xmlNodePtr root, entry;
1307 if (NULL == isds_response) return HTTP_ERROR_SERVER;
1309 if (NULL != results || create_empty_root)
1310 INSERT_ELEMENT(root, isds_response, "dbResults");
1312 if (NULL == results) return HTTP_ERROR_SUCCESS;
1314 for (const struct server_list *item = results; NULL != item;
1315 item = item->next) {
1316 const struct server_owner_info *result =
1317 (struct server_owner_info *)item->data;
1319 INSERT_ELEMENT(entry, root, "dbOwnerInfo");
1320 if (NULL == result) continue;
1322 INSERT_STRING(entry, "dbID", result->dbID);
1323 if (pfo) {
1324 INSERT_BOOLEANPTR(entry, "aifoIsds", result->aifoIsds);
1326 if (!pfo) {
1327 INSERT_STRING(entry, "dbType", result->dbType);
1328 INSERT_STRING(entry, "ic", result->ic);
1330 INSERT_STRING(entry, "pnFirstName", result->pnFirstName);
1331 INSERT_STRING(entry, "pnMiddleName", result->pnMiddleName);
1332 INSERT_STRING(entry, "pnLastName", result->pnLastName);
1333 if (!pfo) {
1334 INSERT_STRING(entry, "pnLastNameAtBirth",
1335 result->pnLastNameAtBirth);
1336 INSERT_STRING(entry, "firmName", result->firmName);
1338 INSERT_TMPTR(entry, "biDate", result->biDate);
1339 INSERT_STRING(entry, "biCity", result->biCity);
1340 INSERT_STRING(entry, "biCounty", result->biCounty);
1341 INSERT_STRING(entry, "biState", result->biState);
1342 if (pfo) {
1343 INSERT_LONGINTPTR(entry, "adCode", result->adCode);
1345 INSERT_STRING(entry, "adCity", result->adCity);
1346 if (pfo) {
1347 INSERT_STRING(entry, "adDistrict", result->adDistrict);
1349 INSERT_STRING(entry, "adStreet", result->adStreet);
1350 INSERT_STRING(entry, "adNumberInStreet", result->adNumberInStreet);
1351 INSERT_STRING(entry, "adNumberInMunicipality",
1352 result->adNumberInMunicipality);
1353 INSERT_STRING(entry, "adZipCode", result->adZipCode);
1354 INSERT_STRING(entry, "adState", result->adState);
1355 INSERT_STRING(entry, "nationality", result->nationality);
1356 if (!pfo) {
1357 if (result->email_exists || result->email != NULL) {
1358 INSERT_STRING(entry, "email", result->email);
1360 if (result->telNumber_exists || result->telNumber != NULL) {
1361 INSERT_STRING(entry, "telNumber", result->telNumber);
1363 INSERT_STRING(entry, "identifier", result->identifier);
1364 INSERT_STRING(entry, "registryCode", result->registryCode);
1365 INSERT_LONGINTPTR(entry, "dbState", result->dbState);
1366 INSERT_BOOLEANPTR(entry, "dbEffectiveOVM", result->dbEffectiveOVM);
1367 INSERT_BOOLEANPTR(entry, "dbOpenAddressing",
1368 result->dbOpenAddressing);
1372 leave:
1373 return error;
1377 /* Find isds:dbOwnerInfo child and check its content.
1378 * @code is a static output ISDS error code
1379 * @error_message is a reallocated output ISDS error message
1380 * @criteria is template to check the child against
1381 * @pfo is false if XSD:tDbOwnerInfo type is expected, true if
1382 * XSD:tdbPersonalOwnerInfo is expectd. */
1383 static http_error check_dbOwnerInfo(const char **code, char **message,
1384 xmlXPathContextPtr xpath_ctx,
1385 const struct server_owner_info *criteria, _Bool pfo) {
1386 http_error error = HTTP_ERROR_SUCCESS;
1387 xmlNodePtr old_node = xpath_ctx->node;
1388 xmlNodePtr new_node;
1390 if (NULL == criteria) {
1391 error = HTTP_ERROR_SERVER;
1392 goto leave;
1395 /* Find the required child */
1396 error = select_element(code, message, xpath_ctx, "isds:dbOwnerInfo",
1397 &new_node);
1398 if (error) goto leave;
1399 /* And set context */
1400 xpath_ctx->node = new_node;
1402 error = element_equals_string(code, message, xpath_ctx,
1403 "isds:dbID", 1, criteria->dbID);
1404 if (error) goto leave;
1406 if (pfo) {
1407 error = element_equals_boolean(code, message, xpath_ctx,
1408 "isds:aifoIsds", 1, criteria->aifoIsds);
1409 if (error) goto leave;
1412 if (!pfo) {
1413 error = element_equals_string(code, message, xpath_ctx,
1414 "isds:dbType", 1, criteria->dbType);
1415 if (error) goto leave;
1417 error = element_equals_string(code, message, xpath_ctx,
1418 "isds:ic", 1, criteria->ic);
1419 if (error) goto leave;
1422 error = element_equals_string(code, message, xpath_ctx,
1423 "isds:pnFirstName", 1, criteria->pnFirstName);
1424 if (error) goto leave;
1426 error = element_equals_string(code, message, xpath_ctx,
1427 "isds:pnMiddleName", 1, criteria->pnMiddleName);
1428 if (error) goto leave;
1430 error = element_equals_string(code, message, xpath_ctx,
1431 "isds:pnLastName", 1, criteria->pnLastName);
1432 if (error) goto leave;
1434 if (!pfo) {
1435 error = element_equals_string(code, message, xpath_ctx,
1436 "isds:pnLastNameAtBirth", 1, criteria->pnLastNameAtBirth);
1437 if (error) goto leave;
1439 error = element_equals_string(code, message, xpath_ctx,
1440 "isds:firmName", 1, criteria->firmName);
1441 if (error) goto leave;
1444 error = element_equals_date(code, message, xpath_ctx,
1445 "isds:biDate", 1, criteria->biDate);
1446 if (error) goto leave;
1448 error = element_equals_string(code, message, xpath_ctx,
1449 "isds:biCity", 1, criteria->biCity);
1450 if (error) goto leave;
1452 error = element_equals_string(code, message, xpath_ctx,
1453 "isds:biCounty", 1, criteria->biCounty);
1454 if (error) goto leave;
1456 error = element_equals_string(code, message, xpath_ctx,
1457 "isds:biState", 1, criteria->biState);
1458 if (error) goto leave;
1460 if (pfo) {
1461 error = element_equals_integer(code, message, xpath_ctx,
1462 "isds:adCode", 1, criteria->adCode);
1463 if (error) goto leave;
1466 error = element_equals_string(code, message, xpath_ctx,
1467 "isds:adCity", 1, criteria->adCity);
1468 if (error) goto leave;
1470 if (pfo) {
1471 error = element_equals_string(code, message, xpath_ctx,
1472 "isds:adDistrict", 1, criteria->adDistrict);
1473 if (error) goto leave;
1476 error = element_equals_string(code, message, xpath_ctx,
1477 "isds:adStreet", 1, criteria->adStreet);
1478 if (error) goto leave;
1480 error = element_equals_string(code, message, xpath_ctx,
1481 "isds:adNumberInStreet", 1,
1482 criteria->adNumberInStreet);
1483 if (error) goto leave;
1485 error = element_equals_string(code, message, xpath_ctx,
1486 "isds:adNumberInMunicipality", 1,
1487 criteria->adNumberInMunicipality);
1488 if (error) goto leave;
1490 error = element_equals_string(code, message, xpath_ctx,
1491 "isds:adZipCode", 1, criteria->adZipCode);
1492 if (error) goto leave;
1494 error = element_equals_string(code, message, xpath_ctx,
1495 "isds:adState", 1, criteria->adState);
1496 if (error) goto leave;
1498 error = element_equals_string(code, message, xpath_ctx,
1499 "isds:nationality", 1, criteria->nationality);
1500 if (error) goto leave;
1502 if (!pfo) {
1503 error = element_equals_string(code, message, xpath_ctx,
1504 "isds:email", 0, criteria->email);
1505 if (error) goto leave;
1507 error = element_equals_string(code, message, xpath_ctx,
1508 "isds:telNumber", 0, criteria->telNumber);
1509 if (error) goto leave;
1511 error = element_equals_string(code, message, xpath_ctx,
1512 "isds:identifier", 1, criteria->identifier);
1513 if (error) goto leave;
1515 error = element_equals_string(code, message, xpath_ctx,
1516 "isds:registryCode", 1, criteria->registryCode);
1517 if (error) goto leave;
1519 error = element_equals_integer(code, message, xpath_ctx,
1520 "isds:dbState", 1, criteria->dbState);
1521 if (error) goto leave;
1523 error = element_equals_boolean(code, message, xpath_ctx,
1524 "isds:dbEffectiveOVM", 1, criteria->dbEffectiveOVM);
1525 if (error) goto leave;
1527 error = element_equals_boolean(code, message, xpath_ctx,
1528 "isds:dbOpenAddressing", 1,
1529 criteria->dbOpenAddressing);
1530 if (error) goto leave;
1533 leave:
1534 /* Restore context */
1535 xpath_ctx->node = old_node;
1536 return error;
1540 /* Implement FindDataBox.
1541 * @arguments is pointer to struct arguments_DS_df_FindDataBox */
1542 static http_error service_FindDataBox(
1543 xmlXPathContextPtr xpath_ctx,
1544 xmlNodePtr isds_response,
1545 const void *arguments) {
1546 http_error error = HTTP_ERROR_SUCCESS;
1547 const char *code = "9999";
1548 char *message = NULL;
1549 const struct arguments_DS_df_FindDataBox *configuration =
1550 (const struct arguments_DS_df_FindDataBox *)arguments;
1552 if (NULL == configuration || NULL == configuration->status_code ||
1553 NULL == configuration->status_message) {
1554 error = HTTP_ERROR_SERVER;
1555 goto leave;
1558 /* Check request */
1559 error = check_dbOwnerInfo(&code, &message, xpath_ctx,
1560 configuration->criteria, 0);
1561 if (error) goto leave;
1563 /* Build response */
1564 if ((error = insert_tDbOwnersArray(isds_response, configuration->results,
1565 configuration->results_exists, 0))) {
1566 goto leave;
1569 code = configuration->status_code;
1570 message = strdup(configuration->status_message);
1572 leave:
1573 if (HTTP_ERROR_SERVER != error) {
1574 http_error next_error = insert_isds_status(isds_response, 0,
1575 BAD_CAST code, BAD_CAST message, NULL);
1576 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
1578 free(message);
1579 return error;
1583 /* Implement FindPersonalDataBox.
1584 * @arguments is pointer to struct arguments_DS_df_FindPersonalDataBox */
1585 static http_error service_FindPersonalDataBox(
1586 xmlXPathContextPtr xpath_ctx,
1587 xmlNodePtr isds_response,
1588 const void *arguments) {
1589 http_error error = HTTP_ERROR_SUCCESS;
1590 const char *code = "9999";
1591 char *message = NULL;
1592 const struct arguments_DS_df_FindPersonalDataBox *configuration =
1593 (const struct arguments_DS_df_FindPersonalDataBox *)arguments;
1595 if (NULL == configuration || NULL == configuration->status_code ||
1596 NULL == configuration->status_message) {
1597 error = HTTP_ERROR_SERVER;
1598 goto leave;
1601 /* Check request */
1602 error = check_dbOwnerInfo(&code, &message, xpath_ctx,
1603 configuration->criteria, 1);
1604 if (error) goto leave;
1606 /* Build response */
1607 if ((error = insert_tDbOwnersArray(isds_response, configuration->results,
1608 configuration->results_exists, 1))) {
1609 goto leave;
1612 code = configuration->status_code;
1613 message = strdup(configuration->status_message);
1615 leave:
1616 if (HTTP_ERROR_SERVER != error) {
1617 http_error next_error = insert_isds_status(isds_response, 0,
1618 BAD_CAST code, BAD_CAST message, NULL);
1619 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
1621 free(message);
1622 return error;
1626 /* Insert list of period results as XSD:tdbPeriodsArray XML tree.
1627 * @isds_response is XML node with the response
1628 * @results is list of struct server_box_state_period *.
1629 * @create_empty_root is true to create Periods element even if @results is
1630 * empty. */
1631 static http_error insert_tdbPeriodsArray(xmlNodePtr isds_response,
1632 const struct server_list *results, _Bool create_empty_root) {
1633 http_error error = HTTP_ERROR_SUCCESS;
1634 xmlNodePtr root, entry;
1636 if (NULL == isds_response) return HTTP_ERROR_SERVER;
1638 if (NULL != results || create_empty_root)
1639 INSERT_ELEMENT(root, isds_response, "Periods");
1641 if (NULL == results) return HTTP_ERROR_SUCCESS;
1643 for (const struct server_list *item = results; NULL != item;
1644 item = item->next) {
1645 const struct server_box_state_period *result =
1646 (struct server_box_state_period *)item->data;
1648 INSERT_ELEMENT(entry, root, "Period");
1649 if (NULL == result) continue;
1651 INSERT_TIMEVALPTR(entry, "PeriodFrom", result->from);
1652 INSERT_TIMEVALPTR(entry, "PeriodTo", result->to);
1653 INSERT_LONGINTPTR(entry, "DbState", &(result->dbState));
1656 leave:
1657 return error;
1661 /* Implement GetDataBoxActivityStatus.
1662 * @arguments is pointer to struct arguments_DS_df_GetDataBoxActivityStatus */
1663 static http_error service_GetDataBoxActivityStatus(
1664 xmlXPathContextPtr xpath_ctx,
1665 xmlNodePtr isds_response,
1666 const void *arguments) {
1667 http_error error = HTTP_ERROR_SUCCESS;
1668 const char *code = "9999";
1669 char *message = NULL;
1670 const struct arguments_DS_df_GetDataBoxActivityStatus *configuration =
1671 (const struct arguments_DS_df_GetDataBoxActivityStatus *)arguments;
1673 if (NULL == configuration || NULL == configuration->status_code ||
1674 NULL == configuration->status_message) {
1675 error = HTTP_ERROR_SERVER;
1676 goto leave;
1679 /* Check request */
1680 error = element_equals_string(&code, &message, xpath_ctx,
1681 "isds:dbID", 1, configuration->box_id);
1682 /* ??? XML schema and textual documentation does not agree on obligatority
1683 * of the isds:baFrom and isds:baTo value or presence. */
1684 error = element_equals_time(&code, &message, xpath_ctx,
1685 "isds:baFrom", 1, configuration->from);
1686 error = element_equals_time(&code, &message, xpath_ctx,
1687 "isds:baTo", 1, configuration->to);
1688 if (error) goto leave;
1690 /* Build response */
1691 if ((error = insert_tdbPeriodsArray(isds_response, configuration->results,
1692 configuration->results_exists))) {
1693 goto leave;
1696 code = configuration->status_code;
1697 message = strdup(configuration->status_message);
1699 leave:
1700 if (HTTP_ERROR_SERVER != error) {
1701 http_error next_error = insert_isds_status(isds_response, 0,
1702 BAD_CAST code, BAD_CAST message, NULL);
1703 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
1705 free(message);
1706 return error;
1710 /* Implement ISDSSearch2.
1711 * @arguments is pointer to struct arguments_DS_df_ISDSSearch2 */
1712 static http_error service_ISDSSearch2(
1713 xmlXPathContextPtr xpath_ctx,
1714 xmlNodePtr isds_response,
1715 const void *arguments) {
1716 http_error error = HTTP_ERROR_SUCCESS;
1717 const char *code = "9999";
1718 char *message = NULL;
1719 const struct arguments_DS_df_ISDSSearch2 *configuration =
1720 (const struct arguments_DS_df_ISDSSearch2 *)arguments;
1721 char *string = NULL;
1723 if (NULL == configuration || NULL == configuration->status_code ||
1724 NULL == configuration->status_message) {
1725 error = HTTP_ERROR_SERVER;
1726 goto leave;
1729 /* Check request */
1730 EXTRACT_STRING("isds:searchText", string);
1731 if (NULL == string) {
1732 message = strdup("Missing or empty isds:searchText");
1733 error = HTTP_ERROR_CLIENT;
1734 goto leave;
1736 if (NULL != configuration->search_text &&
1737 xmlStrcmp(BAD_CAST configuration->search_text,
1738 BAD_CAST string)) {
1739 code = "9999";
1740 message = strdup("Unexpected isds:searchText value");
1741 error = HTTP_ERROR_CLIENT;
1742 goto leave;
1744 free(string); string = NULL;
1746 error = element_equals_string(&code, &message, xpath_ctx,
1747 "isds:searchType", 1, configuration->search_type);
1748 if (error) goto leave;
1750 error = element_equals_string(&code, &message, xpath_ctx,
1751 "isds:searchScope", 1, configuration->search_scope);
1752 if (error) goto leave;
1754 error = element_equals_integer(&code, &message, xpath_ctx,
1755 "isds:page", 1, configuration->search_page_number);
1756 if (error) goto leave;
1758 error = element_equals_integer(&code, &message, xpath_ctx,
1759 "isds:pageSize", 1, configuration->search_page_size);
1760 if (error) goto leave;
1762 error = element_equals_boolean(&code, &message, xpath_ctx,
1763 "isds:highlighting", 0, configuration->search_highlighting_value);
1764 if (error) goto leave;
1766 /* Build response */
1767 if (NULL != configuration->total_count)
1768 INSERT_ULONGINTPTR(isds_response, "totalCount",
1769 configuration->total_count);
1770 if (NULL != configuration->current_count)
1771 INSERT_ULONGINTPTR(isds_response, "currentCount",
1772 configuration->current_count);
1773 if (NULL != configuration->position)
1774 INSERT_ULONGINTPTR(isds_response, "position",
1775 configuration->position);
1776 if (NULL != configuration->last_page)
1777 INSERT_BOOLEANPTR(isds_response, "lastPage",
1778 configuration->last_page);
1779 if ((error = insert_tdbResultsArray(isds_response, configuration->results,
1780 configuration->results_exists))) {
1781 goto leave;
1784 code = configuration->status_code;
1785 message = strdup(configuration->status_message);
1787 leave:
1788 if (HTTP_ERROR_SERVER != error) {
1789 http_error next_error = insert_isds_status(isds_response, 0,
1790 BAD_CAST code, BAD_CAST message, NULL);
1791 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
1793 free(string);
1794 free(message);
1795 return error;
1799 /* Common part for ChangeISDSPassword and ChangePasswordOTP.
1800 * @code is output pointer to static string
1801 * @pass_message is output pointer to auto-allocated string
1802 * @arguments is pointer to struct arguments_DS_DsManage_ChangeISDSPassword */
1803 static http_error check_passwd(
1804 const char *username, const char *current_password,
1805 xmlXPathContextPtr xpath_ctx,
1806 char **code, char **pass_message) {
1807 http_error error = HTTP_ERROR_SUCCESS;
1808 char *message = NULL;
1809 char *old_password = NULL, *new_password = NULL;
1810 size_t length;
1812 if (NULL == username || NULL == current_password ||
1813 NULL == code || NULL == pass_message) {
1814 return HTTP_ERROR_SERVER;
1817 *code = "9999";
1820 /* Parse request */
1821 EXTRACT_STRING("isds:dbOldPassword", old_password);
1822 if (NULL == old_password) {
1823 message = strdup("Empty isds:dbOldPassword");
1824 error = HTTP_ERROR_CLIENT;
1825 goto leave;
1827 EXTRACT_STRING("isds:dbNewPassword", new_password);
1828 if (NULL == new_password) {
1829 message = strdup("Empty isds:dbOldPassword");
1830 error = HTTP_ERROR_CLIENT;
1831 goto leave;
1834 /* Check defined cases */
1835 if (strcmp(current_password, old_password)) {
1836 *code = "1090";
1837 message = strdup("Bad current password");
1838 error = HTTP_ERROR_CLIENT;
1839 goto leave;
1842 length = strlen(new_password);
1844 if (length < 8 || length > 32) {
1845 *code = "1066";
1846 message = strdup("Too short or too long");
1847 error = HTTP_ERROR_CLIENT;
1848 goto leave;
1852 const char lower[] = "abcdefghijklmnopqrstuvwxyz";
1853 const char upper[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1854 const char digit[] = "0123456789";
1855 const char special[] = "!#$%&()*+,-.:=?@[]_{}|~";
1856 _Bool has_lower = 0, has_upper = 0, has_digit=0;
1858 for (size_t i = 0; i < length; i++) {
1859 if (NULL != strchr(lower, new_password[i]))
1860 has_lower = 1;
1861 else if (NULL != strchr(upper, new_password[i]))
1862 has_upper = 1;
1863 else if (NULL != strchr(digit, new_password[i]))
1864 has_digit = 1;
1865 else if (NULL == strchr(special, new_password[i])) {
1866 *code = "1079";
1867 message = strdup("Password contains forbidden character");
1868 error = HTTP_ERROR_CLIENT;
1869 goto leave;
1873 if (!has_lower || !has_upper || !has_digit) {
1874 *code = "1080";
1875 message = strdup("Password does not contain lower cased letter, "
1876 "upper cased letter and a digit");
1877 error = HTTP_ERROR_CLIENT;
1878 goto leave;
1882 if (!strcmp(old_password, new_password)) {
1883 *code = "1067";
1884 message = strdup("New password same as current one");
1885 error = HTTP_ERROR_CLIENT;
1886 goto leave;
1889 if (NULL != strstr(new_password, username)) {
1890 *code = "1082";
1891 message = strdup("New password contains user ID");
1892 error = HTTP_ERROR_CLIENT;
1893 goto leave;
1896 for (size_t i = 0; i < length - 2; i++) {
1897 if (new_password[i] == new_password[i+1] &&
1898 new_password[i] == new_password[i+2]) {
1899 *code = "1083";
1900 message = strdup("Password contains sequence "
1901 "of three identical characters");
1902 error = HTTP_ERROR_CLIENT;
1903 goto leave;
1908 const char *forbidden_prefix[] = { "qwert", "asdgf", "12345" };
1909 for (size_t i = 0; i < sizeof(forbidden_prefix)/sizeof(*forbidden_prefix);
1910 i++) {
1911 if (!strncmp(new_password, forbidden_prefix[i],
1912 strlen(forbidden_prefix[i]))) {
1913 *code = "1083";
1914 message = strdup("Password has forbidden prefix");
1915 error = HTTP_ERROR_CLIENT;
1916 goto leave;
1921 *code = "0000";
1922 message = strdup("Success");
1923 leave:
1924 free(old_password);
1925 free(new_password);
1926 *pass_message = message;
1927 return error;
1931 /* Implement ChangeISDSPassword.
1932 * @arguments is pointer to struct arguments_DS_DsManage_ChangeISDSPassword */
1933 static http_error service_ChangeISDSPassword(
1934 xmlXPathContextPtr xpath_ctx,
1935 xmlNodePtr isds_response,
1936 const void *arguments) {
1937 http_error error = HTTP_ERROR_SUCCESS;
1938 char *code = "9999", *message = NULL;
1939 const struct arguments_DS_DsManage_ChangeISDSPassword *configuration =
1940 (const struct arguments_DS_DsManage_ChangeISDSPassword *)arguments;
1942 if (NULL == configuration || NULL == configuration->username ||
1943 NULL == configuration->current_password) {
1944 error = HTTP_ERROR_SERVER;
1945 goto leave;
1948 /* Check for common password rules */
1949 error = check_passwd(
1950 configuration->username, configuration->current_password,
1951 xpath_ctx, &code, &message);
1953 leave:
1954 if (HTTP_ERROR_SERVER != error) {
1955 http_error next_error = insert_isds_status(isds_response, 0,
1956 BAD_CAST code, BAD_CAST message, NULL);
1957 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
1959 free(message);
1960 return error;
1964 /* Implement ChangePasswordOTP.
1965 * @arguments is pointer to struct
1966 * arguments_asws_changePassword_ChangePasswordOTP */
1967 static http_error service_ChangePasswordOTP(
1968 xmlXPathContextPtr xpath_ctx,
1969 xmlNodePtr isds_response,
1970 const void *arguments) {
1971 http_error error = HTTP_ERROR_SUCCESS;
1972 char *code = "9999", *message = NULL;
1973 const struct arguments_asws_changePassword_ChangePasswordOTP *configuration
1974 = (const struct arguments_asws_changePassword_ChangePasswordOTP *)
1975 arguments;
1976 char *method = NULL;
1978 if (NULL == configuration || NULL == configuration->username ||
1979 NULL == configuration->current_password) {
1980 error = HTTP_ERROR_SERVER;
1981 goto leave;
1984 /* Chek for OTP method */
1985 EXTRACT_STRING("isds:dbOTPType", method);
1986 if (NULL == method) {
1987 message = strdup("Empty isds:dbOTPType");
1988 error = HTTP_ERROR_CLIENT;
1989 goto leave;
1991 if ((configuration->method == AUTH_OTP_HMAC && strcmp(method, "HOTP")) ||
1992 (configuration->method == AUTH_OTP_TIME && strcmp(method, "TOTP"))) {
1993 message = strdup("isds:dbOTPType does not match OTP method");
1994 error = HTTP_ERROR_CLIENT;
1995 goto leave;
1998 /* Check for common password rules */
1999 error = check_passwd(
2000 configuration->username, configuration->current_password,
2001 xpath_ctx, &code, &message);
2003 leave:
2004 if (HTTP_ERROR_SERVER != error) {
2005 http_error next_error = insert_isds_status(isds_response, 0,
2006 BAD_CAST code, BAD_CAST message,
2007 BAD_CAST configuration->reference_number);
2008 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
2010 free(message);
2011 free(method);
2012 return error;
2016 /* Implement SendSMSCode.
2017 * @arguments is pointer to struct arguments_asws_changePassword_SendSMSCode */
2018 static http_error service_SendSMSCode(
2019 xmlXPathContextPtr xpath_ctx,
2020 xmlNodePtr isds_response,
2021 const void *arguments) {
2022 const struct arguments_asws_changePassword_SendSMSCode *configuration
2023 = (const struct arguments_asws_changePassword_SendSMSCode *)
2024 arguments;
2025 (void)xpath_ctx;
2027 if (NULL == configuration || NULL == configuration->status_code ||
2028 NULL == configuration->status_message) {
2029 return HTTP_ERROR_SERVER;
2032 return insert_isds_status(isds_response, 0,
2033 BAD_CAST configuration->status_code,
2034 BAD_CAST configuration->status_message,
2035 BAD_CAST configuration->reference_number);
2039 /* List of implemented services */
2040 static struct service services[] = {
2041 { SERVICE_DS_Dz_DummyOperation,
2042 "DS/dz", BAD_CAST ISDS_NS, BAD_CAST "DummyOperation",
2043 service_DummyOperation },
2044 { SERVICE_DS_Dz_ResignISDSDocument,
2045 "DS/dz", BAD_CAST ISDS_NS, BAD_CAST "Re-signISDSDocument",
2046 service_ResignISDSDocument },
2047 { SERVICE_DS_df_DataBoxCreditInfo,
2048 "DS/df", BAD_CAST ISDS_NS, BAD_CAST "DataBoxCreditInfo",
2049 service_DataBoxCreditInfo },
2050 { SERVICE_DS_df_FindDataBox,
2051 "DS/df", BAD_CAST ISDS_NS, BAD_CAST "FindDataBox",
2052 service_FindDataBox },
2053 { SERVICE_DS_df_FindPersonalDataBox,
2054 "DS/df", BAD_CAST ISDS_NS, BAD_CAST "FindPersonalDataBox",
2055 service_FindPersonalDataBox },
2056 { SERVICE_DS_df_GetDataBoxActivityStatus,
2057 "DS/df", BAD_CAST ISDS_NS, BAD_CAST "GetDataBoxActivityStatus",
2058 service_GetDataBoxActivityStatus },
2059 { SERVICE_DS_df_ISDSSearch2,
2060 "DS/df", BAD_CAST ISDS_NS, BAD_CAST "ISDSSearch2",
2061 service_ISDSSearch2 },
2062 { SERVICE_DS_DsManage_ChangeISDSPassword,
2063 "DS/DsManage", BAD_CAST ISDS_NS, BAD_CAST "ChangeISDSPassword",
2064 service_ChangeISDSPassword },
2065 { SERVICE_DS_Dx_EraseMessage,
2066 "DS/dx", BAD_CAST ISDS_NS, BAD_CAST "EraseMessage",
2067 service_EraseMessage },
2068 { SERVICE_asws_changePassword_ChangePasswordOTP,
2069 "/asws/changePassword", BAD_CAST OISDS_NS, BAD_CAST "ChangePasswordOTP",
2070 service_ChangePasswordOTP },
2071 { SERVICE_asws_changePassword_SendSMSCode,
2072 "/asws/changePassword", BAD_CAST OISDS_NS, BAD_CAST "SendSMSCode",
2073 service_SendSMSCode },
2077 /* Makes known all relevant namespaces to given XPath context
2078 * @xpath_ctx is XPath context
2079 * @otp_ns selects name space for the request and response know as "isds".
2080 * Use true for OTP-authenticated password change services, otherwise false.
2081 * @message_ns selects proper message name space. Unsigned and signed
2082 * messages and delivery info's differ in prefix and URI.
2083 * @return 0 in success, otherwise not 0. */
2084 static int register_namespaces(xmlXPathContextPtr xpath_ctx,
2085 const _Bool otp_ns, const message_ns_type message_ns) {
2086 const xmlChar *service_namespace = NULL;
2087 const xmlChar *message_namespace = NULL;
2089 if (!xpath_ctx) return -1;
2091 if (otp_ns) {
2092 service_namespace = BAD_CAST OISDS_NS;
2093 } else {
2094 service_namespace = BAD_CAST ISDS_NS;
2097 switch(message_ns) {
2098 case MESSAGE_NS_1:
2099 message_namespace = BAD_CAST ISDS1_NS; break;
2100 case MESSAGE_NS_UNSIGNED:
2101 message_namespace = BAD_CAST ISDS_NS; break;
2102 case MESSAGE_NS_SIGNED_INCOMING:
2103 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
2104 case MESSAGE_NS_SIGNED_OUTGOING:
2105 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
2106 case MESSAGE_NS_SIGNED_DELIVERY:
2107 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
2108 default:
2109 return -1;
2112 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
2113 return -1;
2114 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", service_namespace))
2115 return -1;
2116 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
2117 return -1;
2118 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
2119 return -1;
2120 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
2121 return -1;
2122 return 0;
2126 /* Parse soap request, pass it to service endpoint and respond to it.
2127 * It sends final HTTP response. */
2128 void soap(const struct http_connection *connection,
2129 const struct service_configuration *configuration,
2130 const void *request, size_t request_length, const char *end_point) {
2131 xmlDocPtr request_doc = NULL;
2132 xmlXPathContextPtr xpath_ctx = NULL;
2133 xmlXPathObjectPtr request_soap_body = NULL;
2134 xmlNodePtr isds_request = NULL; /* pointer only */
2135 _Bool service_handled = 0, service_passed = 0;
2136 xmlDocPtr response_doc = NULL;
2137 xmlNodePtr response_soap_envelope = NULL, response_soap_body = NULL,
2138 isds_response = NULL;
2139 xmlNsPtr soap_ns = NULL, isds_ns = NULL;
2140 char *response_name = NULL;
2141 xmlBufferPtr http_response_body = NULL;
2142 xmlSaveCtxtPtr save_ctx = NULL;
2145 if (NULL == configuration) {
2146 http_send_response_500(connection,
2147 "Second argument of soap() is NULL");
2148 return;
2151 if (NULL == request || request_length == 0) {
2152 http_send_response_400(connection, "Client sent empty body");
2153 return;
2156 request_doc = xmlParseMemory(request, request_length);
2157 if (NULL == request_doc) {
2158 http_send_response_400(connection, "Client sent invalid XML document");
2159 return;
2162 xpath_ctx = xmlXPathNewContext(request_doc);
2163 if (NULL == xpath_ctx) {
2164 xmlFreeDoc(request_doc);
2165 http_send_response_500(connection, "Could not create XPath context");
2166 return;
2169 if (register_namespaces(xpath_ctx, 0, MESSAGE_NS_UNSIGNED)) {
2170 xmlXPathFreeContext(xpath_ctx);
2171 xmlFreeDoc(request_doc);
2172 http_send_response_500(connection,
2173 "Could not register name spaces to the XPath context");
2174 return;
2177 /* Get SOAP Body */
2178 request_soap_body = xmlXPathEvalExpression(
2179 BAD_CAST "/soap:Envelope/soap:Body", xpath_ctx);
2180 if (NULL == request_soap_body) {
2181 xmlXPathFreeContext(xpath_ctx);
2182 xmlFreeDoc(request_doc);
2183 http_send_response_400(connection, "Client sent invalid SOAP request");
2184 return;
2186 if (xmlXPathNodeSetIsEmpty(request_soap_body->nodesetval)) {
2187 xmlXPathFreeObject(request_soap_body);
2188 xmlXPathFreeContext(xpath_ctx);
2189 xmlFreeDoc(request_doc);
2190 http_send_response_400(connection,
2191 "SOAP request does not contain SOAP Body element");
2192 return;
2194 if (request_soap_body->nodesetval->nodeNr > 1) {
2195 xmlXPathFreeObject(request_soap_body);
2196 xmlXPathFreeContext(xpath_ctx);
2197 xmlFreeDoc(request_doc);
2198 http_send_response_400(connection,
2199 "SOAP response has more than one Body element");
2200 return;
2202 isds_request = request_soap_body->nodesetval->nodeTab[0]->children;
2203 if (isds_request->next != NULL) {
2204 xmlXPathFreeObject(request_soap_body);
2205 xmlXPathFreeContext(xpath_ctx);
2206 xmlFreeDoc(request_doc);
2207 http_send_response_400(connection, "SOAP body has more than one child");
2208 return;
2210 if (isds_request->type != XML_ELEMENT_NODE || isds_request->ns == NULL ||
2211 NULL == isds_request->ns->href) {
2212 xmlXPathFreeObject(request_soap_body);
2213 xmlXPathFreeContext(xpath_ctx);
2214 xmlFreeDoc(request_doc);
2215 http_send_response_400(connection,
2216 "SOAP body does not contain a name-space-qualified element");
2217 return;
2220 /* Build SOAP response envelope */
2221 response_doc = xmlNewDoc(BAD_CAST "1.0");
2222 if (!response_doc) {
2223 http_send_response_500(connection,
2224 "Could not build SOAP response document");
2225 goto leave;
2227 response_soap_envelope = xmlNewNode(NULL, BAD_CAST "Envelope");
2228 if (!response_soap_envelope) {
2229 http_send_response_500(connection,
2230 "Could not build SOAP response envelope");
2231 goto leave;
2233 xmlDocSetRootElement(response_doc, response_soap_envelope);
2234 /* Only this way we get namespace definition as @xmlns:soap,
2235 * otherwise we get namespace prefix without definition */
2236 soap_ns = xmlNewNs(response_soap_envelope, BAD_CAST SOAP_NS, NULL);
2237 if(NULL == soap_ns) {
2238 http_send_response_500(connection, "Could not create SOAP name space");
2239 goto leave;
2241 xmlSetNs(response_soap_envelope, soap_ns);
2242 response_soap_body = xmlNewChild(response_soap_envelope, NULL,
2243 BAD_CAST "Body", NULL);
2244 if (!response_soap_body) {
2245 http_send_response_500(connection,
2246 "Could not add Body to SOAP response envelope");
2247 goto leave;
2249 /* Append ISDS response element */
2250 if (-1 == test_asprintf(&response_name, "%s%s", isds_request->name,
2251 "Response")) {
2252 http_send_response_500(connection,
2253 "Could not buld ISDS resposne element name");
2254 goto leave;
2256 isds_response = xmlNewChild(response_soap_body, NULL,
2257 BAD_CAST response_name, NULL);
2258 free(response_name);
2259 if (NULL == isds_response) {
2260 http_send_response_500(connection,
2261 "Could not add ISDS response element to SOAP response body");
2262 goto leave;
2264 isds_ns = xmlNewNs(isds_response, isds_request->ns->href, NULL);
2265 if(NULL == isds_ns) {
2266 http_send_response_500(connection,
2267 "Could not create a name space for the response body");
2268 goto leave;
2270 xmlSetNs(isds_response, isds_ns);
2272 /* Dispatch request to service */
2273 for (size_t i = 0; i < sizeof(services)/sizeof(services[0]); i++) {
2274 if (!strcmp(services[i].end_point, end_point) &&
2275 !xmlStrcmp(services[i].name_space, isds_request->ns->href) &&
2276 !xmlStrcmp(services[i].name, isds_request->name)) {
2277 /* Check if the configuration is enabled and find configuration */
2278 for (const struct service_configuration *service = configuration;
2279 service->name != SERVICE_END; service++) {
2280 if (service->name == services[i].id) {
2281 service_handled = 1;
2282 if (!xmlStrcmp(services[i].name_space, BAD_CAST OISDS_NS)) {
2283 /* Alias "isds" XPath identifier to OISDS_NS */
2284 if (register_namespaces(xpath_ctx, 1,
2285 MESSAGE_NS_UNSIGNED)) {
2286 http_send_response_500(connection,
2287 "Could not register name spaces to the "
2288 "XPath context");
2289 break;
2292 xpath_ctx->node = isds_request;
2293 if (HTTP_ERROR_SERVER != services[i].function(
2294 xpath_ctx,
2295 isds_response,
2296 service->arguments)) {
2297 service_passed = 1;
2298 } else {
2299 http_send_response_500(connection,
2300 "Internal server error while processing "
2301 "ISDS request");
2305 break;
2309 /* Send response */
2310 if (service_passed) {
2311 /* Serialize the SOAP response */
2312 http_response_body = xmlBufferCreate();
2313 if (NULL == http_response_body) {
2314 http_send_response_500(connection,
2315 "Could not create xmlBuffer for response serialization");
2316 goto leave;
2318 /* Last argument 1 means format the XML tree. This is pretty but it breaks
2319 * XML document transport as it adds text nodes (indentiation) between
2320 * elements. */
2321 save_ctx = xmlSaveToBuffer(http_response_body, "UTF-8", 0);
2322 if (NULL == save_ctx) {
2323 http_send_response_500(connection, "Could not create XML serializer");
2324 goto leave;
2326 /* XXX: According LibXML documentation, this function does not return
2327 * meaningful value yet */
2328 xmlSaveDoc(save_ctx, response_doc);
2329 if (-1 == xmlSaveFlush(save_ctx)) {
2330 http_send_response_500(connection,
2331 "Could not serialize SOAP response");
2332 goto leave;
2335 http_send_response_200(connection, http_response_body->content,
2336 http_response_body->use, soap_mime_type);
2339 leave:
2340 xmlSaveClose(save_ctx);
2341 xmlBufferFree(http_response_body);
2343 xmlFreeDoc(response_doc);
2345 xmlXPathFreeObject(request_soap_body);
2346 xmlXPathFreeContext(xpath_ctx);
2347 xmlFreeDoc(request_doc);
2349 if (!service_handled) {
2350 http_send_response_500(connection,
2351 "Requested ISDS service not implemented");