test: Add test for isds_FindDataBox()
[libisds.git] / test / simline / service.c
blob25ef43e39c3b198f8f480d66bd6712566aa1b480
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 <libxml/parser.h>
11 #include <libxml/xpath.h>
12 #include <libxml/xpathInternals.h>
13 #include <libxml/xmlsave.h>
15 static const char *soap_mime_type = "text/xml"; /* SOAP/1.1 requires text/xml */
17 /* Used to choose proper name space for message elements.
18 * See _isds_register_namespaces(). */
19 typedef enum {
20 MESSAGE_NS_1,
21 MESSAGE_NS_UNSIGNED,
22 MESSAGE_NS_SIGNED_INCOMING,
23 MESSAGE_NS_SIGNED_OUTGOING,
24 MESSAGE_NS_SIGNED_DELIVERY,
25 MESSAGE_NS_OTP
26 } message_ns_type;
28 #define SOAP_NS "http://schemas.xmlsoap.org/soap/envelope/"
29 #define SOAP2_NS "http://www.w3.org/2003/05/soap-envelope"
30 #define ISDS1_NS "http://isds.czechpoint.cz"
31 #define ISDS_NS "http://isds.czechpoint.cz/v20"
32 #define OISDS_NS "http://isds.czechpoint.cz/v20/asws"
33 #define SISDS_INCOMING_NS "http://isds.czechpoint.cz/v20/message"
34 #define SISDS_OUTGOING_NS "http://isds.czechpoint.cz/v20/SentMessage"
35 #define SISDS_DELIVERY_NS "http://isds.czechpoint.cz/v20/delivery"
36 #define SCHEMA_NS "http://www.w3.org/2001/XMLSchema"
37 #define DEPOSIT_NS "urn:uschovnaWSDL"
40 struct service {
41 service_id id;
42 const char *end_point;
43 const xmlChar *name_space;
44 const xmlChar *name;
45 http_error (*function) (
46 xmlXPathContextPtr, xmlNodePtr,
47 const void *arguments);
50 /* Following EXTRACT_* macros expect @xpath_ctx, @error, @message,
51 * and leave label. */
52 #define ELEMENT_EXISTS(element, allow_multiple) { \
53 xmlXPathObjectPtr result = NULL; \
54 result = xmlXPathEvalExpression(BAD_CAST element, xpath_ctx); \
55 if (NULL == result) { \
56 error = HTTP_ERROR_SERVER; \
57 goto leave; \
58 } \
59 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
60 xmlXPathFreeObject(result); \
61 test_asprintf(&message, "Element %s does not exist", element); \
62 error = HTTP_ERROR_CLIENT; \
63 goto leave; \
64 } else { \
65 if (!allow_multiple && result->nodesetval->nodeNr > 1) { \
66 xmlXPathFreeObject(result); \
67 test_asprintf(&message, "Multiple %s element", element); \
68 error = HTTP_ERROR_CLIENT; \
69 goto leave; \
70 } \
71 } \
72 xmlXPathFreeObject(result); \
75 #define EXTRACT_STRING(element, string) { \
76 xmlXPathObjectPtr result = NULL; \
77 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
78 if (NULL == result) { \
79 error = HTTP_ERROR_SERVER; \
80 goto leave; \
81 } \
82 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
83 if (result->nodesetval->nodeNr > 1) { \
84 xmlXPathFreeObject(result); \
85 test_asprintf(&message, "Multiple %s element", element); \
86 error = HTTP_ERROR_CLIENT; \
87 goto leave; \
88 } \
89 (string) = (char *) \
90 xmlXPathCastNodeSetToString(result->nodesetval); \
91 if (!(string)) { \
92 xmlXPathFreeObject(result); \
93 error = HTTP_ERROR_SERVER; \
94 goto leave; \
95 } \
96 } \
97 xmlXPathFreeObject(result); \
100 #define EXTRACT_BOOLEAN(element, booleanPtr) { \
101 char *string = NULL; \
102 EXTRACT_STRING(element, string); \
104 if (NULL != string) { \
105 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
106 if (NULL == (booleanPtr)) { \
107 free(string); \
108 error = HTTP_ERROR_SERVER; \
109 goto leave; \
112 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
113 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
114 *(booleanPtr) = 1; \
115 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
116 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
117 *(booleanPtr) = 0; \
118 else { \
119 test_asprintf(&message, \
120 "%s value is not valid boolean: %s", \
121 element, string); \
122 free(string); \
123 error = HTTP_ERROR_CLIENT; \
124 goto leave; \
127 free(string); \
132 #define EXTRACT_DATE(element, tmPtr) { \
133 char *string = NULL; \
134 EXTRACT_STRING(element, string); \
135 if (NULL != string) { \
136 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
137 if (NULL == (tmPtr)) { \
138 free(string); \
139 error = HTTP_ERROR_SERVER; \
140 goto leave; \
142 error = _server_datestring2tm(string, (tmPtr)); \
143 if (error) { \
144 if (error == HTTP_ERROR_CLIENT) { \
145 test_asprintf(&message, "%s value is not a valid date: %s", \
146 element, string); \
148 free(string); \
149 goto leave; \
151 free(string); \
155 /* Following INSERT_* macros expect @error and leave label */
156 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
158 xmlNodePtr node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
159 (xmlChar *) (string)); \
160 if (NULL == node) { \
161 error = HTTP_ERROR_SERVER; \
162 goto leave; \
166 #define INSERT_STRING(parent, element, string) \
167 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
169 #define INSERT_LONGINTPTR(parent, element, longintPtr) { \
170 if ((longintPtr)) { \
171 char *buffer = NULL; \
172 /* FIXME: locale sensitive */ \
173 if (-1 == test_asprintf(&buffer, "%ld", *(longintPtr))) { \
174 error = HTTP_ERROR_SERVER; \
175 goto leave; \
177 INSERT_STRING(parent, element, buffer) \
178 free(buffer); \
179 } else { INSERT_STRING(parent, element, NULL) } \
182 #define INSERT_ULONGINTPTR(parent, element, ulongintPtr) { \
183 if ((ulongintPtr)) { \
184 char *buffer = NULL; \
185 /* FIXME: locale sensitive */ \
186 if (-1 == test_asprintf(&buffer, "%lu", *(ulongintPtr))) { \
187 error = HTTP_ERROR_SERVER; \
188 goto leave; \
190 INSERT_STRING(parent, element, buffer) \
191 free(buffer); \
192 } else { INSERT_STRING(parent, element, NULL) } \
195 #define INSERT_BOOLEANPTR(parent, element, booleanPtr) { \
196 if (NULL != (booleanPtr)) { \
197 char *buffer = NULL; \
198 buffer = *(booleanPtr) ? "true" : "false"; \
199 INSERT_STRING(parent, element, buffer) \
200 } else { INSERT_STRING(parent, element, NULL) } \
203 #define INSERT_TIMEVALPTR(parent, element, timevalPtr) { \
204 if (NULL != (timevalPtr)) { \
205 char *buffer = NULL; \
206 error = timeval2timestring(timevalPtr, &buffer); \
207 if (error) { \
208 free(buffer); \
209 goto leave; \
211 INSERT_STRING(parent, element, buffer); \
212 free(buffer); \
213 } else { \
214 INSERT_STRING(parent, element, NULL); \
218 #define INSERT_TMPTR(parent, element, tmPtr) { \
219 if (NULL != (tmPtr)) { \
220 char *buffer = NULL; \
221 error = tm2datestring(tmPtr, &buffer); \
222 if (error) { \
223 free(buffer); \
224 goto leave; \
226 INSERT_STRING(parent, element, buffer); \
227 free(buffer); \
228 } else { \
229 INSERT_STRING(parent, element, NULL); \
233 #define INSERT_ELEMENT(child, parent, element) \
235 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
236 if (NULL == (child)) { \
237 error = HTTP_ERROR_SERVER; \
238 goto leave; \
242 /* TODO: These functions (element_exists(), extract_...(), ...) will replace
243 * the macros (ELEMENT_EXISTS, EXTRACT_...). The compiled code will be
244 * smaller, the compilation will be faster. */
246 /* Check an element exists.
247 * @code is a static output ISDS error code
248 * @error_message is a reallocated output ISDS error message
249 * @xpath_ctx is a current XPath context
250 * @element_name is name of an element to check
251 * @allow_multiple is false to require exactly one element. True to require
252 * one or more elements.
253 * @return HTTP_ERROR_SUCCESS or an appropriate error code. */
254 static http_error element_exists(const char **code, char **message,
255 xmlXPathContextPtr xpath_ctx, const char *element_name,
256 _Bool allow_multiple) {
257 xmlXPathObjectPtr result = NULL;
259 result = xmlXPathEvalExpression(BAD_CAST element_name, xpath_ctx);
260 if (NULL == result) {
261 return HTTP_ERROR_SERVER;
263 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
264 xmlXPathFreeObject(result);
265 *code = "9999";
266 test_asprintf(message, "Element %s does not exist", element_name);
267 return HTTP_ERROR_CLIENT;
268 } else {
269 if (!allow_multiple && result->nodesetval->nodeNr > 1) {
270 xmlXPathFreeObject(result);
271 *code = "9999";
272 test_asprintf(message, "Multiple %s element", element_name);
273 return HTTP_ERROR_CLIENT;
276 xmlXPathFreeObject(result);
278 return HTTP_ERROR_SUCCESS;
282 /* Locate a children element.
283 * @code is a static output ISDS error code
284 * @error_message is a reallocated output ISDS error message
285 * @xpath_ctx is a current XPath context
286 * @element_name is name of an element to select
287 * @node is output pointer to located element node
288 * @return HTTP_ERROR_SUCCESS or an appropriate error code. */
289 static http_error select_element(const char **code, char **message,
290 xmlXPathContextPtr xpath_ctx, const char *element_name,
291 xmlNodePtr *node) {
292 xmlXPathObjectPtr result = NULL;
294 result = xmlXPathEvalExpression(BAD_CAST element_name, xpath_ctx);
295 if (NULL == result) {
296 return HTTP_ERROR_SERVER;
298 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
299 xmlXPathFreeObject(result);
300 *code = "9999";
301 test_asprintf(message, "Element %s does not exist", element_name);
302 return HTTP_ERROR_CLIENT;
303 } else {
304 if (result->nodesetval->nodeNr > 1) {
305 xmlXPathFreeObject(result);
306 *code = "9999";
307 test_asprintf(message, "Multiple %s element", element_name);
308 return HTTP_ERROR_CLIENT;
311 *node = result->nodesetval->nodeTab[0];
312 xmlXPathFreeObject(result);
314 return HTTP_ERROR_SUCCESS;
318 /* Extract @element_name's value as a string.
319 * @code is a static output ISDS error code
320 * @error_message is a reallocated output ISDS error message
321 * @xpath_ctx is a current XPath context
322 * @element_name is name of a element whose child text node to extract
323 * @string is the extraced allocated string value, or NULL if empty or the
324 * element does not exist.
325 * @return HTTP_ERROR_SUCCESS or an appropriate error code. */
326 static http_error extract_string(const char **code, char **message,
327 xmlXPathContextPtr xpath_ctx, const char *element_name,
328 char **string) {
329 http_error error = HTTP_ERROR_SUCCESS;
330 xmlXPathObjectPtr result = NULL;
331 char *buffer = NULL;
333 if (-1 == test_asprintf(&buffer, "%s/text()", element_name)) {
334 error = HTTP_ERROR_SERVER;
335 goto leave;
337 result = xmlXPathEvalExpression(BAD_CAST buffer, xpath_ctx);
338 free(buffer);
339 if (NULL == result) {
340 error = HTTP_ERROR_SERVER;
341 goto leave;
343 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
344 if (result->nodesetval->nodeNr > 1) {
345 xmlXPathFreeObject(result);
346 *code = "9999";
347 test_asprintf(message, "Multiple %s element", element_name);
348 error = HTTP_ERROR_CLIENT;
349 goto leave;
351 *string = (char *) \
352 xmlXPathCastNodeSetToString(result->nodesetval);
353 if (!(*string)) {
354 xmlXPathFreeObject(result);
355 error = HTTP_ERROR_SERVER;
356 goto leave;
359 xmlXPathFreeObject(result);
361 leave:
362 return error;
366 /* Compare dates represented by pointer to struct tm.
367 * @return 0 if equalued, non-0 otherwise. */
368 static int datecmp(const struct tm *a, const struct tm *b) {
369 if (NULL == a && b == NULL) return 0;
370 if ((NULL == a && b != NULL) || (NULL != a && b == NULL)) return 1;
371 if (a->tm_year != b->tm_year) return 1;
372 if (a->tm_mon != b->tm_mon) return 1;
373 if (a->tm_mday != b->tm_mday) return 1;
374 return 0;
378 /* Checks an @element_name's value is an @expected_value string.
379 * @code is a static output ISDS error code
380 * @error_message is a reallocated output ISDS error message
381 * @xpath_ctx is a current XPath context
382 * @element_name is name of a element to check
383 * @must_exist is true if the @element_name must exist even if @expected_value
384 * is NULL.
385 * @expected_value is an expected string value
386 * @return HTTP_ERROR_SUCCESS if the @element_name element's value is
387 * @expected_value. HTTP_ERROR_CLIENT if not equaled, HTTP_ERROR_SERVER if an
388 * internal error occured. */
389 static http_error element_equals_string(const char **code, char **message,
390 xmlXPathContextPtr xpath_ctx, const char *element_name,
391 _Bool must_exist, const char *expected_value) {
392 http_error error = HTTP_ERROR_SUCCESS;
393 char *string = NULL;
395 if (must_exist) {
396 error = element_exists(code, message, xpath_ctx, element_name, 0);
397 if (HTTP_ERROR_SUCCESS != error)
398 goto leave;
401 error = extract_string(code, message, xpath_ctx, element_name, &string);
402 if (HTTP_ERROR_SUCCESS != error)
403 goto leave;
405 if (NULL != expected_value) {
406 if (NULL == string) {
407 *code = "9999";
408 test_asprintf(message, "Empty %s element", element_name);
409 error = HTTP_ERROR_CLIENT;
410 goto leave;
412 if (xmlStrcmp(BAD_CAST expected_value, BAD_CAST string)) {
413 *code = "9999";
414 test_asprintf(message,
415 "Unexpected %s element value: expected=`%s', got=`%s'",
416 element_name, expected_value, string);
417 error = HTTP_ERROR_CLIENT;
418 goto leave;
420 } else {
421 if (NULL != string && *string != '\0') {
422 *code = "9999";
423 test_asprintf(message,
424 "Unexpected %s element value: "
425 "expected empty string, got=`%s'",
426 element_name, string);
427 error = HTTP_ERROR_CLIENT;
428 goto leave;
432 leave:
433 free(string);
434 return error;
438 /* Checks an @element_name's value is an @expected_value integer.
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 a element to check
443 * @must_exist is true if the @element_name must exist even if @expected_value
444 * is NULL.
445 * @expected_value is an expected integer value.
446 * @return HTTP_ERROR_SUCCESS if the @element_name element's value is
447 * @expected_value. HTTP_ERROR_CLIENT if not equaled, HTTP_ERROR_SERVER if an
448 * internal error occured. */
449 static http_error element_equals_integer(const char **code, char **message,
450 xmlXPathContextPtr xpath_ctx, const char *element_name,
451 _Bool must_exist, const long int *expected_value) {
452 http_error error = HTTP_ERROR_SUCCESS;
453 char *string = NULL;
454 long int number;
455 char *endptr;
457 if (must_exist) {
458 error = element_exists(code, message, xpath_ctx, element_name, 0);
459 if (HTTP_ERROR_SUCCESS != error)
460 goto leave;
463 error = extract_string(code, message, xpath_ctx, element_name, &string);
464 if (HTTP_ERROR_SUCCESS != error)
465 goto leave;
467 if (NULL != expected_value) {
468 if (NULL == string) {
469 *code = "9999";
470 test_asprintf(message, "Empty %s element", element_name);
471 error = HTTP_ERROR_CLIENT;
472 goto leave;
474 number = strtol(string, &endptr, 10);
475 if (*endptr != '\0') {
476 *code = "9999";
477 test_asprintf(message,
478 "%s element value is not a valid integer: %s",
479 element_name, string);
480 error = HTTP_ERROR_CLIENT;
481 goto leave;
483 if (number == LONG_MIN || number == LONG_MAX) { \
484 *code = "9999";
485 test_asprintf(message, \
486 "%s element value is out of range of long int: %s",
487 element_name, string);
488 error = HTTP_ERROR_SERVER;
489 goto leave;
491 free(string); string = NULL;
492 if (number != *expected_value) {
493 *code = "9999";
494 test_asprintf(message,
495 "Unexpected %s element value: expected=`%ld', got=`%ld'",
496 element_name, *expected_value, number);
497 error = HTTP_ERROR_CLIENT;
498 goto leave;
500 } else {
501 if (NULL != string && *string != '\0') {
502 *code = "9999";
503 test_asprintf(message,
504 "Unexpected %s element value: expected no text node, got=`%s'",
505 element_name, string);
506 error = HTTP_ERROR_CLIENT;
507 goto leave;
511 leave:
512 free(string);
513 return error;
517 /* Checks an @element_name's value is an @expected_value boolean.
518 * @code is a static output ISDS error code
519 * @error_message is an reallocated output ISDS error message
520 * @xpath_ctx is a current XPath context
521 * @element_name is name of a element to check
522 * @must_exist is true if the @element_name must exist even if @expected_value
523 * is NULL.
524 * @expected_value is an expected boolean value
525 * @return HTTP_ERROR_SUCCESS if the @element_name element's value is
526 * @expected_value. HTTP_ERROR_CLIENT if not equaled, HTTP_ERROR_SERVER if an
527 * internal error occured. */
528 static http_error element_equals_boolean(const char **code, char **message,
529 xmlXPathContextPtr xpath_ctx, const char *element_name,
530 _Bool must_exist, const _Bool *expected_value) {
531 http_error error = HTTP_ERROR_SUCCESS;
532 char *string = NULL;
533 _Bool value;
535 if (must_exist) {
536 error = element_exists(code, message, xpath_ctx, element_name, 0);
537 if (HTTP_ERROR_SUCCESS != error)
538 goto leave;
541 error = extract_string(code, message, xpath_ctx, element_name, &string);
542 if (HTTP_ERROR_SUCCESS != error)
543 goto leave;
545 if (NULL != expected_value) {
546 if (NULL == string) {
547 *code = "9999";
548 test_asprintf(message, "Empty %s element", element_name);
549 error = HTTP_ERROR_CLIENT;
550 goto leave;
552 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") ||
553 !xmlStrcmp((xmlChar *)string, BAD_CAST "1"))
554 value = 1;
555 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") ||
556 !xmlStrcmp((xmlChar *)string, BAD_CAST "0"))
557 value = 0;
558 else {
559 *code = "9999";
560 test_asprintf(message,
561 "%s element value is not a valid boolean: %s",
562 element_name, string);
563 error = HTTP_ERROR_CLIENT;
564 goto leave;
566 if (*expected_value != value) {
567 *code = "9999";
568 test_asprintf(message,
569 "Unexpected %s element value: expected=%d, got=%d",
570 element_name, *expected_value, string);
571 error = HTTP_ERROR_CLIENT;
572 goto leave;
574 } else {
575 if (NULL != string && *string != '\0') {
576 *code = "9999";
577 test_asprintf(message,
578 "Unexpected %s element value: "
579 "expected empty string, got=`%s'",
580 element_name, string);
581 error = HTTP_ERROR_CLIENT;
582 goto leave;
586 leave:
587 free(string);
588 return error;
592 /* Checks an @element_name's value is an @expected_value date.
593 * @code is a static output ISDS error code
594 * @error_message is an reallocated output ISDS error message
595 * @xpath_ctx is a current XPath context
596 * @element_name is name of a element to check
597 * @must_exist is true if the @element_name must exist even if @expected_value
598 * is NULL.
599 * @expected_value is an expected boolean value
600 * @return HTTP_ERROR_SUCCESS if the @element_name element's value is
601 * @expected_value. HTTP_ERROR_CLIENT if not equaled, HTTP_ERROR_SERVER if an
602 * internal error occured. */
603 static http_error element_equals_date(const char **code, char **message,
604 xmlXPathContextPtr xpath_ctx, const char *element_name,
605 _Bool must_exist, const struct tm *expected_value) {
606 http_error error = HTTP_ERROR_SUCCESS;
607 char *string = NULL;
608 struct tm value;
610 if (must_exist) {
611 error = element_exists(code, message, xpath_ctx, element_name, 0);
612 if (HTTP_ERROR_SUCCESS != error)
613 goto leave;
616 error = extract_string(code, message, xpath_ctx, element_name, &string);
617 if (HTTP_ERROR_SUCCESS != error)
618 goto leave;
620 if (NULL != expected_value) {
621 if (NULL == string) {
622 *code = "9999";
623 test_asprintf(message, "Empty %s element", element_name);
624 error = HTTP_ERROR_CLIENT;
625 goto leave;
627 error = _server_datestring2tm(string, &value);
628 if (error) {
629 if (error == HTTP_ERROR_CLIENT) { \
630 test_asprintf(message, "%s value is not a valid date: %s",
631 element_name, string);
633 goto leave;
635 if (datecmp(expected_value, &value)) {
636 *code = "9999";
637 test_asprintf(message, "Unexpected %s element value: "
638 "expected=%d-%02d-%02d, got=%d-%02d-%02d", element_name,
639 expected_value->tm_year + 1900, expected_value->tm_mon + 1,
640 expected_value->tm_mday,
641 value.tm_year + 1900, value.tm_mon + 1, value.tm_mday);
642 error = HTTP_ERROR_CLIENT;
643 goto leave;
645 } else {
646 if (NULL != string && *string != '\0') {
647 *code = "9999";
648 test_asprintf(message,
649 "Unexpected %s element value: "
650 "expected empty value, got=`%s'",
651 element_name, string);
652 error = HTTP_ERROR_CLIENT;
653 goto leave;
657 leave:
658 free(string);
659 return error;
663 /* Insert dmStatus or similar subtree
664 * @parent is element to insert to
665 * @dm is true for dmStatus, otherwise dbStatus
666 * @code is status code as string
667 * @message is UTF-8 encoded message
668 * @db_ref_number is optinal reference number propagated if not @dm
669 * @return 0 on success, otherwise non-0. */
670 static http_error insert_isds_status(xmlNodePtr parent, _Bool dm,
671 const xmlChar *code, const xmlChar *message,
672 const xmlChar *db_ref_number) {
673 http_error error = HTTP_ERROR_SUCCESS;
674 xmlNodePtr status;
676 if (NULL == code || NULL == message) {
677 error = HTTP_ERROR_SERVER;
678 goto leave;
681 INSERT_ELEMENT(status, parent, (dm) ? "dmStatus" : "dbStatus");
682 INSERT_STRING(status, (dm) ? "dmStatusCode" : "dbStatusCode", code);
683 INSERT_STRING(status, (dm) ? "dmStatusMessage" : "dbStatusMessage", message);
684 if (!dm && NULL != db_ref_number) {
685 INSERT_STRING(status, "dbStatusRefNumber", db_ref_number);
688 leave:
689 return error;
693 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
694 static http_error tm2datestring(const struct tm *time, char **string) {
695 if (NULL == time || NULL == string) return HTTP_ERROR_SERVER;
697 if (-1 == test_asprintf(string, "%d-%02d-%02d",
698 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
699 return HTTP_ERROR_SERVER;
701 return HTTP_ERROR_SUCCESS;
705 /* Convert struct timeval *@time to UTF-8 ISO 8601 date-time @string. It
706 * respects the @time microseconds too. */
707 static http_error timeval2timestring(const struct timeval *time,
708 char **string) {
709 struct tm broken;
711 if (!time || !string) return HTTP_ERROR_SERVER;
713 if (!gmtime_r(&time->tv_sec, &broken)) return HTTP_ERROR_SERVER;
714 if (time->tv_usec < 0 || time->tv_usec > 999999) return HTTP_ERROR_SERVER;
716 /* TODO: small negative year should be formatted as "-0012". This is not
717 * true for glibc "%04d". We should implement it.
718 * time->tv_usec type is su_seconds_t which is required to be signed
719 * integer to accomodate values from range [-1, 1000000].
720 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
721 if (-1 == test_asprintf(string,
722 "%04d-%02d-%02dT%02d:%02d:%02d.%06" PRIdMAX,
723 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
724 broken.tm_hour, broken.tm_min, broken.tm_sec,
725 (intmax_t)time->tv_usec))
726 return HTTP_ERROR_SERVER;
728 return HTTP_ERROR_SUCCESS;
732 /* Implement DummyOperation */
733 static http_error service_DummyOperation(
734 xmlXPathContextPtr xpath_ctx,
735 xmlNodePtr isds_response,
736 const void *arguments) {
737 (void)xpath_ctx;
738 (void)arguments;
740 return insert_isds_status(isds_response, 1, BAD_CAST "0000",
741 BAD_CAST "Success", NULL);
745 /* Implement Re-signISDSDocument.
746 * It sends document from request back.
747 * @arguments is pointer to struct arguments_DS_Dz_ResignISDSDocument */
748 static http_error service_ResignISDSDocument(
749 xmlXPathContextPtr xpath_ctx,
750 xmlNodePtr isds_response,
751 const void *arguments) {
752 http_error error = HTTP_ERROR_SUCCESS;
753 const char *code = "9999";
754 char *message = NULL;
755 const struct arguments_DS_Dz_ResignISDSDocument *configuration =
756 (const struct arguments_DS_Dz_ResignISDSDocument *)arguments;
757 char *data = NULL;
759 if (NULL == configuration || NULL == configuration->status_code ||
760 NULL == configuration->status_message) {
761 error = HTTP_ERROR_SERVER;
762 goto leave;
765 EXTRACT_STRING("isds:dmDoc", data);
766 if (NULL == data) {
767 message = strdup("Missing isds:dmDoc");
768 error = HTTP_ERROR_CLIENT;
769 goto leave;
773 /* dmResultDoc is mandatory in response */
774 if (xmlStrcmp(BAD_CAST configuration->status_code, BAD_CAST "0000")) {
775 free(data);
776 data = NULL;
778 INSERT_STRING(isds_response, "dmResultDoc", data);
780 if (configuration->valid_to != NULL) {
781 error = tm2datestring(configuration->valid_to, &data);
782 if (error) {
783 message = strdup("Could not format date");
784 goto leave;
786 INSERT_STRING(isds_response, "dmValidTo", data);
789 code = configuration->status_code;
790 message = strdup(configuration->status_message);
792 leave:
793 if (HTTP_ERROR_SERVER != error) {
794 http_error next_error = insert_isds_status(isds_response, 1,
795 BAD_CAST code, BAD_CAST message, NULL);
796 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
798 free(data);
799 free(message);
800 return error;
804 /* Implement EraseMessage.
805 * @arguments is pointer to struct arguments_DS_DsManage_ChangeISDSPassword */
806 static http_error service_EraseMessage(
807 xmlXPathContextPtr xpath_ctx,
808 xmlNodePtr isds_response,
809 const void *arguments) {
810 http_error error = HTTP_ERROR_SUCCESS;
811 char *code = "9999", *message = NULL;
812 const struct arguments_DS_Dx_EraseMessage *configuration =
813 (const struct arguments_DS_Dx_EraseMessage *)arguments;
814 char *message_id = NULL;
815 _Bool *incoming = NULL;
817 if (NULL == configuration || NULL == configuration->message_id) {
818 error = HTTP_ERROR_SERVER;
819 goto leave;
822 EXTRACT_STRING("isds:dmID", message_id);
823 if (NULL == message_id) {
824 message = strdup("Missing isds:dmID");
825 error = HTTP_ERROR_CLIENT;
826 goto leave;
828 EXTRACT_BOOLEAN("isds:dmIncoming", incoming);
829 if (NULL == incoming) {
830 message = strdup("Missing isds:dmIncoming");
831 error = HTTP_ERROR_CLIENT;
832 goto leave;
835 if (xmlStrcmp((const xmlChar *) configuration->message_id,
836 (const xmlChar *) message_id)) {
837 code = "1219";
838 message = strdup("Message is not in the long term storage");
839 error = HTTP_ERROR_CLIENT;
840 goto leave;
842 if (configuration->incoming != *incoming) {
843 code = "1219";
844 message = strdup("Message direction mismatches");
845 error = HTTP_ERROR_CLIENT;
846 goto leave;
849 code = "0000";
850 message = strdup("Success");
851 leave:
852 if (HTTP_ERROR_SERVER != error) {
853 http_error next_error = insert_isds_status(isds_response, 1,
854 BAD_CAST code, BAD_CAST message, NULL);
855 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
857 free(incoming);
858 free(message_id);
859 free(message);
860 return error;
864 /* Insert list of credit info as XSD:tCiRecord XML tree.
865 * @isds_response is XML node with the response
866 * @history is list of struct server_credit_event. If NULL, no ciRecord XML
867 * subtree will be created. */
868 static http_error insert_ciRecords(xmlNodePtr isds_response,
869 const struct server_list *history) {
870 http_error error = HTTP_ERROR_SUCCESS;
871 xmlNodePtr records, record;
873 if (NULL == isds_response) return HTTP_ERROR_SERVER;
874 if (NULL == history) return HTTP_ERROR_SUCCESS;
876 INSERT_ELEMENT(records, isds_response, "ciRecords");
877 for (const struct server_list *item = history; NULL != item;
878 item = item->next) {
879 const struct server_credit_event *event =
880 (struct server_credit_event*)item->data;
882 INSERT_ELEMENT(record, records, "ciRecord");
883 if (NULL == event) continue;
885 INSERT_TIMEVALPTR(record, "ciEventTime", event->time);
886 switch(event->type) {
887 case SERVER_CREDIT_CHARGED:
888 INSERT_STRING(record, "ciEventType", "1");
889 INSERT_STRING(record, "ciTransID",
890 event->details.charged.transaction);
891 break;
892 case SERVER_CREDIT_DISCHARGED:
893 INSERT_STRING(record, "ciEventType", "2");
894 INSERT_STRING(record, "ciTransID",
895 event->details.discharged.transaction);
896 break;
897 case SERVER_CREDIT_MESSAGE_SENT:
898 INSERT_STRING(record, "ciEventType", "3");
899 INSERT_STRING(record, "ciRecipientID",
900 event->details.message_sent.recipient);
901 INSERT_STRING(record, "ciPDZID",
902 event->details.message_sent.message_id);
903 break;
904 case SERVER_CREDIT_STORAGE_SET:
905 INSERT_STRING(record, "ciEventType", "4");
906 INSERT_LONGINTPTR(record, "ciNewCapacity",
907 &event->details.storage_set.new_capacity);
908 INSERT_TMPTR(record, "ciNewFrom",
909 event->details.storage_set.new_valid_from);
910 INSERT_TMPTR(record, "ciNewTo",
911 event->details.storage_set.new_valid_to);
912 INSERT_LONGINTPTR(record, "ciOldCapacity",
913 event->details.storage_set.old_capacity);
914 INSERT_TMPTR(record, "ciOldFrom",
915 event->details.storage_set.old_valid_from);
916 INSERT_TMPTR(record, "ciOldTo",
917 event->details.storage_set.old_valid_to);
918 INSERT_STRING(record, "ciDoneBy",
919 event->details.storage_set.initiator);
920 break;
921 case SERVER_CREDIT_EXPIRED:
922 INSERT_STRING(record, "ciEventType", "5");
923 break;
924 default:
925 error = HTTP_ERROR_SERVER;
926 goto leave;
928 INSERT_LONGINTPTR(record, "ciCreditChange", &event->credit_change);
929 INSERT_LONGINTPTR(record, "ciCreditAfter", &event->new_credit);
933 leave:
934 return error;
938 /* Implement DataBoxCreditInfo.
939 * @arguments is pointer to struct arguments_DS_df_DataBoxCreditInfo */
940 static http_error service_DataBoxCreditInfo(
941 xmlXPathContextPtr xpath_ctx,
942 xmlNodePtr isds_response,
943 const void *arguments) {
944 http_error error = HTTP_ERROR_SUCCESS;
945 const char *code = "9999";
946 char *message = NULL;
947 const struct arguments_DS_df_DataBoxCreditInfo *configuration =
948 (const struct arguments_DS_df_DataBoxCreditInfo *)arguments;
949 char *box_id = NULL;
950 struct tm *from_date = NULL, *to_date = NULL;
952 if (NULL == configuration || NULL == configuration->status_code ||
953 NULL == configuration->status_message) {
954 error = HTTP_ERROR_SERVER;
955 goto leave;
958 EXTRACT_STRING("isds:dbID", box_id);
959 if (NULL == box_id) {
960 message = strdup("Missing isds:dbID");
961 error = HTTP_ERROR_CLIENT;
962 goto leave;
964 if (NULL != configuration->box_id &&
965 xmlStrcmp(BAD_CAST configuration->box_id,
966 BAD_CAST box_id)) {
967 code = "9999";
968 message = strdup("Unexpected isds:dbID value");
969 error = HTTP_ERROR_CLIENT;
970 goto leave;
973 ELEMENT_EXISTS("isds:ciFromDate", 0);
974 EXTRACT_DATE("isds:ciFromDate", from_date);
975 if (datecmp(configuration->from_date, from_date)) {
976 code = "9999";
977 message = strdup("Unexpected isds:ciFromDate value");
978 error = HTTP_ERROR_CLIENT;
979 goto leave;
982 ELEMENT_EXISTS("isds:ciTodate", 0);
983 EXTRACT_DATE("isds:ciTodate", to_date);
984 if (datecmp(configuration->to_date, to_date)) {
985 code = "9999";
986 message = strdup("Unexpected isds:ciTodate value");
987 error = HTTP_ERROR_CLIENT;
988 goto leave;
991 INSERT_LONGINTPTR(isds_response, "currentCredit",
992 &configuration->current_credit);
993 INSERT_STRING(isds_response, "notifEmail", configuration->email);
994 if ((error = insert_ciRecords(isds_response, configuration->history))) {
995 goto leave;
998 code = configuration->status_code;
999 message = strdup(configuration->status_message);
1000 leave:
1001 if (HTTP_ERROR_SERVER != error) {
1002 http_error next_error = insert_isds_status(isds_response, 0,
1003 BAD_CAST code, BAD_CAST message, NULL);
1004 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
1006 free(box_id);
1007 free(from_date);
1008 free(to_date);
1009 free(message);
1010 return error;
1014 /* Insert list of fulltext search results as XSD:tdbResultsArray XML tree.
1015 * @isds_response is XML node with the response
1016 * @results is list of struct server_db_result *.
1017 * @create_empty_root is true to create dbResults element even if @results is
1018 * empty. */
1019 static http_error insert_tdbResultsArray(xmlNodePtr isds_response,
1020 const struct server_list *results, _Bool create_empty_root) {
1021 http_error error = HTTP_ERROR_SUCCESS;
1022 xmlNodePtr root, entry;
1024 if (NULL == isds_response) return HTTP_ERROR_SERVER;
1026 if (NULL != results || create_empty_root)
1027 INSERT_ELEMENT(root, isds_response, "dbResults");
1029 if (NULL == results) return HTTP_ERROR_SUCCESS;
1031 for (const struct server_list *item = results; NULL != item;
1032 item = item->next) {
1033 const struct server_db_result *result =
1034 (struct server_db_result *)item->data;
1036 INSERT_ELEMENT(entry, root, "dbResult");
1037 if (NULL == result) continue;
1039 INSERT_STRING(entry, "dbID", result->id);
1040 INSERT_STRING(entry, "dbType", result->type);
1041 INSERT_STRING(entry, "dbName", result->name);
1042 INSERT_STRING(entry, "dbAddress", result->address);
1043 INSERT_TMPTR(entry, "dbBiDate", result->birth_date);
1044 INSERT_STRING(entry, "dbICO", result->ic);
1045 INSERT_BOOLEANPTR(entry, "dbEffectiveOVM", &result->ovm);
1046 INSERT_STRING(entry, "dbSendOptions", result->send_options);
1049 leave:
1050 return error;
1054 /* Insert list of search results as XSD:tDbOwnersArray XML tree.
1055 * @isds_response is XML node with the response
1056 * @results is list of struct server_owner_info *.
1057 * @create_empty_root is true to create dbResults element even if @results is
1058 * empty. */
1059 static http_error insert_tDbOwnersArray(xmlNodePtr isds_response,
1060 const struct server_list *results, _Bool create_empty_root) {
1061 http_error error = HTTP_ERROR_SUCCESS;
1062 xmlNodePtr root, entry;
1064 if (NULL == isds_response) return HTTP_ERROR_SERVER;
1066 if (NULL != results || create_empty_root)
1067 INSERT_ELEMENT(root, isds_response, "dbResults");
1069 if (NULL == results) return HTTP_ERROR_SUCCESS;
1071 for (const struct server_list *item = results; NULL != item;
1072 item = item->next) {
1073 const struct server_owner_info *result =
1074 (struct server_owner_info *)item->data;
1076 INSERT_ELEMENT(entry, root, "dbOwnerInfo");
1077 if (NULL == result) continue;
1079 INSERT_STRING(entry, "dbID", result->dbID);
1080 /* Ignore aifoIsds */
1081 INSERT_STRING(entry, "dbType", result->dbType);
1082 INSERT_STRING(entry, "ic", result->ic);
1083 INSERT_STRING(entry, "pnFirstName", result->pnFirstName);
1084 INSERT_STRING(entry, "pnMiddleName", result->pnMiddleName);
1085 INSERT_STRING(entry, "pnLastName", result->pnLastName);
1086 INSERT_STRING(entry, "pnLastNameAtBirth", result->pnLastNameAtBirth);
1087 INSERT_STRING(entry, "firmName", result->firmName);
1088 INSERT_TMPTR(entry, "biDate", result->biDate);
1089 INSERT_STRING(entry, "biCity", result->biCity);
1090 INSERT_STRING(entry, "biCounty", result->biCounty);
1091 INSERT_STRING(entry, "biState", result->biState);
1092 /* Ignore adCode */
1093 INSERT_STRING(entry, "adCity", result->adCity);
1094 /* Ignore adDistrict */
1095 INSERT_STRING(entry, "adStreet", result->adStreet);
1096 INSERT_STRING(entry, "adNumberInStreet", result->adNumberInStreet);
1097 INSERT_STRING(entry, "adNumberInMunicipality",
1098 result->adNumberInMunicipality);
1099 INSERT_STRING(entry, "adZipCode", result->adZipCode);
1100 INSERT_STRING(entry, "adState", result->adState);
1101 INSERT_STRING(entry, "nationality", result->nationality);
1102 if (result->email_exists || result->email != NULL) {
1103 INSERT_STRING(entry, "email", result->email);
1105 if (result->telNumber_exists || result->telNumber != NULL) {
1106 INSERT_STRING(entry, "telNumber", result->telNumber);
1108 INSERT_STRING(entry, "identifier", result->identifier);
1109 INSERT_STRING(entry, "registryCode", result->registryCode);
1110 INSERT_LONGINTPTR(entry, "dbState", result->dbState);
1111 INSERT_BOOLEANPTR(entry, "dbEffectiveOVM", result->dbEffectiveOVM);
1112 INSERT_BOOLEANPTR(entry, "dbOpenAddressing", result->dbOpenAddressing);
1115 leave:
1116 return error;
1120 /* Implement FindDataBox.
1121 * @arguments is pointer to struct arguments_DS_df_FindDataBox */
1122 static http_error service_FindDataBox(
1123 xmlXPathContextPtr xpath_ctx,
1124 xmlNodePtr isds_response,
1125 const void *arguments) {
1126 http_error error = HTTP_ERROR_SUCCESS;
1127 const char *code = "9999";
1128 char *message = NULL;
1129 const struct arguments_DS_df_FindDataBox *configuration =
1130 (const struct arguments_DS_df_FindDataBox *)arguments;
1131 char *string = NULL;
1132 xmlNodePtr node;
1134 if (NULL == configuration || NULL == configuration->status_code ||
1135 NULL == configuration->status_message) {
1136 error = HTTP_ERROR_SERVER;
1137 goto leave;
1140 /* Check request */
1141 error = select_element(&code, &message, xpath_ctx, "isds:dbOwnerInfo",
1142 &node);
1143 if (error) goto leave;
1144 xpath_ctx->node = node;
1146 error = element_equals_string(&code, &message, xpath_ctx,
1147 "isds:dbID", 1, configuration->criteria->dbID);
1148 if (error) goto leave;
1150 error = element_equals_string(&code, &message, xpath_ctx,
1151 "isds:dbType", 1, configuration->criteria->dbType);
1152 if (error) goto leave;
1154 error = element_equals_string(&code, &message, xpath_ctx,
1155 "isds:ic", 1, configuration->criteria->ic);
1156 if (error) goto leave;
1158 error = element_equals_string(&code, &message, xpath_ctx,
1159 "isds:pnFirstName", 1, configuration->criteria->pnFirstName);
1160 if (error) goto leave;
1162 error = element_equals_string(&code, &message, xpath_ctx,
1163 "isds:pnMiddleName", 1, configuration->criteria->pnMiddleName);
1164 if (error) goto leave;
1166 error = element_equals_string(&code, &message, xpath_ctx,
1167 "isds:pnLastName", 1, configuration->criteria->pnLastName);
1168 if (error) goto leave;
1170 error = element_equals_string(&code, &message, xpath_ctx,
1171 "isds:pnLastNameAtBirth", 1, configuration->criteria->pnLastNameAtBirth);
1172 if (error) goto leave;
1174 error = element_equals_string(&code, &message, xpath_ctx,
1175 "isds:firmName", 1, configuration->criteria->firmName);
1176 if (error) goto leave;
1178 error = element_equals_date(&code, &message, xpath_ctx,
1179 "isds:biDate", 1, configuration->criteria->biDate);
1180 if (error) goto leave;
1182 error = element_equals_string(&code, &message, xpath_ctx,
1183 "isds:biCity", 1, configuration->criteria->biCity);
1184 if (error) goto leave;
1186 error = element_equals_string(&code, &message, xpath_ctx,
1187 "isds:biCounty", 1, configuration->criteria->biCounty);
1188 if (error) goto leave;
1190 error = element_equals_string(&code, &message, xpath_ctx,
1191 "isds:biState", 1, configuration->criteria->biState);
1192 if (error) goto leave;
1194 error = element_equals_string(&code, &message, xpath_ctx,
1195 "isds:adCity", 1, configuration->criteria->adCity);
1196 if (error) goto leave;
1198 error = element_equals_string(&code, &message, xpath_ctx,
1199 "isds:adStreet", 1, configuration->criteria->adStreet);
1200 if (error) goto leave;
1202 error = element_equals_string(&code, &message, xpath_ctx,
1203 "isds:adNumberInStreet", 1,
1204 configuration->criteria->adNumberInStreet);
1205 if (error) goto leave;
1207 error = element_equals_string(&code, &message, xpath_ctx,
1208 "isds:adNumberInMunicipality", 1,
1209 configuration->criteria->adNumberInMunicipality);
1210 if (error) goto leave;
1212 error = element_equals_string(&code, &message, xpath_ctx,
1213 "isds:adZipCode", 1, configuration->criteria->adZipCode);
1214 if (error) goto leave;
1216 error = element_equals_string(&code, &message, xpath_ctx,
1217 "isds:adState", 1, configuration->criteria->adState);
1218 if (error) goto leave;
1220 error = element_equals_string(&code, &message, xpath_ctx,
1221 "isds:nationality", 1, configuration->criteria->nationality);
1222 if (error) goto leave;
1224 error = element_equals_string(&code, &message, xpath_ctx,
1225 "isds:email", 0, configuration->criteria->email);
1226 if (error) goto leave;
1228 error = element_equals_string(&code, &message, xpath_ctx,
1229 "isds:telNumber", 0, configuration->criteria->telNumber);
1230 if (error) goto leave;
1232 error = element_equals_string(&code, &message, xpath_ctx,
1233 "isds:identifier", 1, configuration->criteria->identifier);
1234 if (error) goto leave;
1236 error = element_equals_string(&code, &message, xpath_ctx,
1237 "isds:registryCode", 1, configuration->criteria->registryCode);
1238 if (error) goto leave;
1240 error = element_equals_integer(&code, &message, xpath_ctx,
1241 "isds:dbState", 1, configuration->criteria->dbState);
1242 if (error) goto leave;
1244 error = element_equals_boolean(&code, &message, xpath_ctx,
1245 "isds:dbEffectiveOVM", 1, configuration->criteria->dbEffectiveOVM);
1246 if (error) goto leave;
1248 error = element_equals_boolean(&code, &message, xpath_ctx,
1249 "isds:dbOpenAddressing", 1,
1250 configuration->criteria->dbOpenAddressing);
1251 if (error) goto leave;
1253 /* Build response */
1254 if ((error = insert_tDbOwnersArray(isds_response, configuration->results,
1255 configuration->results_exists))) {
1256 goto leave;
1259 code = configuration->status_code;
1260 message = strdup(configuration->status_message);
1262 leave:
1263 if (HTTP_ERROR_SERVER != error) {
1264 http_error next_error = insert_isds_status(isds_response, 0,
1265 BAD_CAST code, BAD_CAST message, NULL);
1266 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
1268 free(string);
1269 free(message);
1270 return error;
1274 /* Implement ISDSSearch2.
1275 * @arguments is pointer to struct arguments_DS_df_ISDSSearch2 */
1276 static http_error service_ISDSSearch2(
1277 xmlXPathContextPtr xpath_ctx,
1278 xmlNodePtr isds_response,
1279 const void *arguments) {
1280 http_error error = HTTP_ERROR_SUCCESS;
1281 const char *code = "9999";
1282 char *message = NULL;
1283 const struct arguments_DS_df_ISDSSearch2 *configuration =
1284 (const struct arguments_DS_df_ISDSSearch2 *)arguments;
1285 char *string = NULL;
1287 if (NULL == configuration || NULL == configuration->status_code ||
1288 NULL == configuration->status_message) {
1289 error = HTTP_ERROR_SERVER;
1290 goto leave;
1293 /* Check request */
1294 EXTRACT_STRING("isds:searchText", string);
1295 if (NULL == string) {
1296 message = strdup("Missing or empty isds:searchText");
1297 error = HTTP_ERROR_CLIENT;
1298 goto leave;
1300 if (NULL != configuration->search_text &&
1301 xmlStrcmp(BAD_CAST configuration->search_text,
1302 BAD_CAST string)) {
1303 code = "9999";
1304 message = strdup("Unexpected isds:searchText value");
1305 error = HTTP_ERROR_CLIENT;
1306 goto leave;
1308 free(string); string = NULL;
1310 error = element_equals_string(&code, &message, xpath_ctx,
1311 "isds:searchType", 1, configuration->search_type);
1312 if (error) goto leave;
1314 error = element_equals_string(&code, &message, xpath_ctx,
1315 "isds:searchScope", 1, configuration->search_scope);
1316 if (error) goto leave;
1318 error = element_equals_integer(&code, &message, xpath_ctx,
1319 "isds:page", 1, configuration->search_page_number);
1320 if (error) goto leave;
1322 error = element_equals_integer(&code, &message, xpath_ctx,
1323 "isds:pageSize", 1, configuration->search_page_size);
1324 if (error) goto leave;
1326 error = element_equals_boolean(&code, &message, xpath_ctx,
1327 "isds:highlighting", 0, configuration->search_highlighting_value);
1328 if (error) goto leave;
1330 /* Build response */
1331 if (NULL != configuration->total_count)
1332 INSERT_ULONGINTPTR(isds_response, "totalCount",
1333 configuration->total_count);
1334 if (NULL != configuration->current_count)
1335 INSERT_ULONGINTPTR(isds_response, "currentCount",
1336 configuration->current_count);
1337 if (NULL != configuration->position)
1338 INSERT_ULONGINTPTR(isds_response, "position",
1339 configuration->position);
1340 if (NULL != configuration->last_page)
1341 INSERT_BOOLEANPTR(isds_response, "lastPage",
1342 configuration->last_page);
1343 if ((error = insert_tdbResultsArray(isds_response, configuration->results,
1344 configuration->results_exists))) {
1345 goto leave;
1348 code = configuration->status_code;
1349 message = strdup(configuration->status_message);
1351 leave:
1352 if (HTTP_ERROR_SERVER != error) {
1353 http_error next_error = insert_isds_status(isds_response, 0,
1354 BAD_CAST code, BAD_CAST message, NULL);
1355 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
1357 free(string);
1358 free(message);
1359 return error;
1363 /* Common part for ChangeISDSPassword and ChangePasswordOTP.
1364 * @code is output pointer to static string
1365 * @pass_message is output pointer to auto-allocated string
1366 * @arguments is pointer to struct arguments_DS_DsManage_ChangeISDSPassword */
1367 static http_error check_passwd(
1368 const char *username, const char *current_password,
1369 xmlXPathContextPtr xpath_ctx,
1370 char **code, char **pass_message) {
1371 http_error error = HTTP_ERROR_SUCCESS;
1372 char *message = NULL;
1373 char *old_password = NULL, *new_password = NULL;
1374 size_t length;
1376 if (NULL == username || NULL == current_password ||
1377 NULL == code || NULL == pass_message) {
1378 return HTTP_ERROR_SERVER;
1381 *code = "9999";
1384 /* Parse request */
1385 EXTRACT_STRING("isds:dbOldPassword", old_password);
1386 if (NULL == old_password) {
1387 message = strdup("Empty isds:dbOldPassword");
1388 error = HTTP_ERROR_CLIENT;
1389 goto leave;
1391 EXTRACT_STRING("isds:dbNewPassword", new_password);
1392 if (NULL == new_password) {
1393 message = strdup("Empty isds:dbOldPassword");
1394 error = HTTP_ERROR_CLIENT;
1395 goto leave;
1398 /* Check defined cases */
1399 if (strcmp(current_password, old_password)) {
1400 *code = "1090";
1401 message = strdup("Bad current password");
1402 error = HTTP_ERROR_CLIENT;
1403 goto leave;
1406 length = strlen(new_password);
1408 if (length < 8 || length > 32) {
1409 *code = "1066";
1410 message = strdup("Too short or too long");
1411 error = HTTP_ERROR_CLIENT;
1412 goto leave;
1416 const char lower[] = "abcdefghijklmnopqrstuvwxyz";
1417 const char upper[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1418 const char digit[] = "0123456789";
1419 const char special[] = "!#$%&()*+,-.:=?@[]_{}|~";
1420 _Bool has_lower = 0, has_upper = 0, has_digit=0;
1422 for (size_t i = 0; i < length; i++) {
1423 if (NULL != strchr(lower, new_password[i]))
1424 has_lower = 1;
1425 else if (NULL != strchr(upper, new_password[i]))
1426 has_upper = 1;
1427 else if (NULL != strchr(digit, new_password[i]))
1428 has_digit = 1;
1429 else if (NULL == strchr(special, new_password[i])) {
1430 *code = "1079";
1431 message = strdup("Password contains forbidden character");
1432 error = HTTP_ERROR_CLIENT;
1433 goto leave;
1437 if (!has_lower || !has_upper || !has_digit) {
1438 *code = "1080";
1439 message = strdup("Password does not contain lower cased letter, "
1440 "upper cased letter and a digit");
1441 error = HTTP_ERROR_CLIENT;
1442 goto leave;
1446 if (!strcmp(old_password, new_password)) {
1447 *code = "1067";
1448 message = strdup("New password same as current one");
1449 error = HTTP_ERROR_CLIENT;
1450 goto leave;
1453 if (NULL != strstr(new_password, username)) {
1454 *code = "1082";
1455 message = strdup("New password contains user ID");
1456 error = HTTP_ERROR_CLIENT;
1457 goto leave;
1460 for (size_t i = 0; i < length - 2; i++) {
1461 if (new_password[i] == new_password[i+1] &&
1462 new_password[i] == new_password[i+2]) {
1463 *code = "1083";
1464 message = strdup("Password contains sequence "
1465 "of three identical characters");
1466 error = HTTP_ERROR_CLIENT;
1467 goto leave;
1472 const char *forbidden_prefix[] = { "qwert", "asdgf", "12345" };
1473 for (size_t i = 0; i < sizeof(forbidden_prefix)/sizeof(*forbidden_prefix);
1474 i++) {
1475 if (!strncmp(new_password, forbidden_prefix[i],
1476 strlen(forbidden_prefix[i]))) {
1477 *code = "1083";
1478 message = strdup("Password has forbidden prefix");
1479 error = HTTP_ERROR_CLIENT;
1480 goto leave;
1485 *code = "0000";
1486 message = strdup("Success");
1487 leave:
1488 free(old_password);
1489 free(new_password);
1490 *pass_message = message;
1491 return error;
1495 /* Implement ChangeISDSPassword.
1496 * @arguments is pointer to struct arguments_DS_DsManage_ChangeISDSPassword */
1497 static http_error service_ChangeISDSPassword(
1498 xmlXPathContextPtr xpath_ctx,
1499 xmlNodePtr isds_response,
1500 const void *arguments) {
1501 http_error error = HTTP_ERROR_SUCCESS;
1502 char *code = "9999", *message = NULL;
1503 const struct arguments_DS_DsManage_ChangeISDSPassword *configuration =
1504 (const struct arguments_DS_DsManage_ChangeISDSPassword *)arguments;
1506 if (NULL == configuration || NULL == configuration->username ||
1507 NULL == configuration->current_password) {
1508 error = HTTP_ERROR_SERVER;
1509 goto leave;
1512 /* Check for common password rules */
1513 error = check_passwd(
1514 configuration->username, configuration->current_password,
1515 xpath_ctx, &code, &message);
1517 leave:
1518 if (HTTP_ERROR_SERVER != error) {
1519 http_error next_error = insert_isds_status(isds_response, 0,
1520 BAD_CAST code, BAD_CAST message, NULL);
1521 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
1523 free(message);
1524 return error;
1528 /* Implement ChangePasswordOTP.
1529 * @arguments is pointer to struct
1530 * arguments_asws_changePassword_ChangePasswordOTP */
1531 static http_error service_ChangePasswordOTP(
1532 xmlXPathContextPtr xpath_ctx,
1533 xmlNodePtr isds_response,
1534 const void *arguments) {
1535 http_error error = HTTP_ERROR_SUCCESS;
1536 char *code = "9999", *message = NULL;
1537 const struct arguments_asws_changePassword_ChangePasswordOTP *configuration
1538 = (const struct arguments_asws_changePassword_ChangePasswordOTP *)
1539 arguments;
1540 char *method = NULL;
1542 if (NULL == configuration || NULL == configuration->username ||
1543 NULL == configuration->current_password) {
1544 error = HTTP_ERROR_SERVER;
1545 goto leave;
1548 /* Chek for OTP method */
1549 EXTRACT_STRING("isds:dbOTPType", method);
1550 if (NULL == method) {
1551 message = strdup("Empty isds:dbOTPType");
1552 error = HTTP_ERROR_CLIENT;
1553 goto leave;
1555 if ((configuration->method == AUTH_OTP_HMAC && strcmp(method, "HOTP")) ||
1556 (configuration->method == AUTH_OTP_TIME && strcmp(method, "TOTP"))) {
1557 message = strdup("isds:dbOTPType does not match OTP method");
1558 error = HTTP_ERROR_CLIENT;
1559 goto leave;
1562 /* Check for common password rules */
1563 error = check_passwd(
1564 configuration->username, configuration->current_password,
1565 xpath_ctx, &code, &message);
1567 leave:
1568 if (HTTP_ERROR_SERVER != error) {
1569 http_error next_error = insert_isds_status(isds_response, 0,
1570 BAD_CAST code, BAD_CAST message,
1571 BAD_CAST configuration->reference_number);
1572 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
1574 free(message);
1575 free(method);
1576 return error;
1580 /* Implement SendSMSCode.
1581 * @arguments is pointer to struct arguments_asws_changePassword_SendSMSCode */
1582 static http_error service_SendSMSCode(
1583 xmlXPathContextPtr xpath_ctx,
1584 xmlNodePtr isds_response,
1585 const void *arguments) {
1586 const struct arguments_asws_changePassword_SendSMSCode *configuration
1587 = (const struct arguments_asws_changePassword_SendSMSCode *)
1588 arguments;
1589 (void)xpath_ctx;
1591 if (NULL == configuration || NULL == configuration->status_code ||
1592 NULL == configuration->status_message) {
1593 return HTTP_ERROR_SERVER;
1596 return insert_isds_status(isds_response, 0,
1597 BAD_CAST configuration->status_code,
1598 BAD_CAST configuration->status_message,
1599 BAD_CAST configuration->reference_number);
1603 /* List of implemented services */
1604 static struct service services[] = {
1605 { SERVICE_DS_Dz_DummyOperation,
1606 "DS/dz", BAD_CAST ISDS_NS, BAD_CAST "DummyOperation",
1607 service_DummyOperation },
1608 { SERVICE_DS_Dz_ResignISDSDocument,
1609 "DS/dz", BAD_CAST ISDS_NS, BAD_CAST "Re-signISDSDocument",
1610 service_ResignISDSDocument },
1611 { SERVICE_DS_df_DataBoxCreditInfo,
1612 "DS/df", BAD_CAST ISDS_NS, BAD_CAST "DataBoxCreditInfo",
1613 service_DataBoxCreditInfo },
1614 { SERVICE_DS_df_FindDataBox,
1615 "DS/df", BAD_CAST ISDS_NS, BAD_CAST "FindDataBox",
1616 service_FindDataBox },
1617 { SERVICE_DS_df_ISDSSearch2,
1618 "DS/df", BAD_CAST ISDS_NS, BAD_CAST "ISDSSearch2",
1619 service_ISDSSearch2 },
1620 { SERVICE_DS_DsManage_ChangeISDSPassword,
1621 "DS/DsManage", BAD_CAST ISDS_NS, BAD_CAST "ChangeISDSPassword",
1622 service_ChangeISDSPassword },
1623 { SERVICE_DS_Dx_EraseMessage,
1624 "DS/dx", BAD_CAST ISDS_NS, BAD_CAST "EraseMessage",
1625 service_EraseMessage },
1626 { SERVICE_asws_changePassword_ChangePasswordOTP,
1627 "/asws/changePassword", BAD_CAST OISDS_NS, BAD_CAST "ChangePasswordOTP",
1628 service_ChangePasswordOTP },
1629 { SERVICE_asws_changePassword_SendSMSCode,
1630 "/asws/changePassword", BAD_CAST OISDS_NS, BAD_CAST "SendSMSCode",
1631 service_SendSMSCode },
1635 /* Makes known all relevant namespaces to given XPath context
1636 * @xpath_ctx is XPath context
1637 * @otp_ns selects name space for the request and response know as "isds".
1638 * Use true for OTP-authenticated password change services, otherwise false.
1639 * @message_ns selects proper message name space. Unsigned and signed
1640 * messages and delivery info's differ in prefix and URI.
1641 * @return 0 in success, otherwise not 0. */
1642 static int register_namespaces(xmlXPathContextPtr xpath_ctx,
1643 const _Bool otp_ns, const message_ns_type message_ns) {
1644 const xmlChar *service_namespace = NULL;
1645 const xmlChar *message_namespace = NULL;
1647 if (!xpath_ctx) return -1;
1649 if (otp_ns) {
1650 service_namespace = BAD_CAST OISDS_NS;
1651 } else {
1652 service_namespace = BAD_CAST ISDS_NS;
1655 switch(message_ns) {
1656 case MESSAGE_NS_1:
1657 message_namespace = BAD_CAST ISDS1_NS; break;
1658 case MESSAGE_NS_UNSIGNED:
1659 message_namespace = BAD_CAST ISDS_NS; break;
1660 case MESSAGE_NS_SIGNED_INCOMING:
1661 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
1662 case MESSAGE_NS_SIGNED_OUTGOING:
1663 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
1664 case MESSAGE_NS_SIGNED_DELIVERY:
1665 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
1666 default:
1667 return -1;
1670 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
1671 return -1;
1672 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", service_namespace))
1673 return -1;
1674 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
1675 return -1;
1676 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
1677 return -1;
1678 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
1679 return -1;
1680 return 0;
1684 /* Parse soap request, pass it to service endpoint and respond to it.
1685 * It sends final HTTP response. */
1686 void soap(const struct http_connection *connection,
1687 const struct service_configuration *configuration,
1688 const void *request, size_t request_length, const char *end_point) {
1689 xmlDocPtr request_doc = NULL;
1690 xmlXPathContextPtr xpath_ctx = NULL;
1691 xmlXPathObjectPtr request_soap_body = NULL;
1692 xmlNodePtr isds_request = NULL; /* pointer only */
1693 _Bool service_handled = 0, service_passed = 0;
1694 xmlDocPtr response_doc = NULL;
1695 xmlNodePtr response_soap_envelope = NULL, response_soap_body = NULL,
1696 isds_response = NULL;
1697 xmlNsPtr soap_ns = NULL, isds_ns = NULL;
1698 char *response_name = NULL;
1699 xmlBufferPtr http_response_body = NULL;
1700 xmlSaveCtxtPtr save_ctx = NULL;
1703 if (NULL == configuration) {
1704 http_send_response_500(connection,
1705 "Second argument of soap() is NULL");
1706 return;
1709 if (NULL == request || request_length == 0) {
1710 http_send_response_400(connection, "Client sent empty body");
1711 return;
1714 request_doc = xmlParseMemory(request, request_length);
1715 if (NULL == request_doc) {
1716 http_send_response_400(connection, "Client sent invalid XML document");
1717 return;
1720 xpath_ctx = xmlXPathNewContext(request_doc);
1721 if (NULL == xpath_ctx) {
1722 xmlFreeDoc(request_doc);
1723 http_send_response_500(connection, "Could not create XPath context");
1724 return;
1727 if (register_namespaces(xpath_ctx, 0, MESSAGE_NS_UNSIGNED)) {
1728 xmlXPathFreeContext(xpath_ctx);
1729 xmlFreeDoc(request_doc);
1730 http_send_response_500(connection,
1731 "Could not register name spaces to the XPath context");
1732 return;
1735 /* Get SOAP Body */
1736 request_soap_body = xmlXPathEvalExpression(
1737 BAD_CAST "/soap:Envelope/soap:Body", xpath_ctx);
1738 if (NULL == request_soap_body) {
1739 xmlXPathFreeContext(xpath_ctx);
1740 xmlFreeDoc(request_doc);
1741 http_send_response_400(connection, "Client sent invalid SOAP request");
1742 return;
1744 if (xmlXPathNodeSetIsEmpty(request_soap_body->nodesetval)) {
1745 xmlXPathFreeObject(request_soap_body);
1746 xmlXPathFreeContext(xpath_ctx);
1747 xmlFreeDoc(request_doc);
1748 http_send_response_400(connection,
1749 "SOAP request does not contain SOAP Body element");
1750 return;
1752 if (request_soap_body->nodesetval->nodeNr > 1) {
1753 xmlXPathFreeObject(request_soap_body);
1754 xmlXPathFreeContext(xpath_ctx);
1755 xmlFreeDoc(request_doc);
1756 http_send_response_400(connection,
1757 "SOAP response has more than one Body element");
1758 return;
1760 isds_request = request_soap_body->nodesetval->nodeTab[0]->children;
1761 if (isds_request->next != NULL) {
1762 xmlXPathFreeObject(request_soap_body);
1763 xmlXPathFreeContext(xpath_ctx);
1764 xmlFreeDoc(request_doc);
1765 http_send_response_400(connection, "SOAP body has more than one child");
1766 return;
1768 if (isds_request->type != XML_ELEMENT_NODE || isds_request->ns == NULL ||
1769 NULL == isds_request->ns->href) {
1770 xmlXPathFreeObject(request_soap_body);
1771 xmlXPathFreeContext(xpath_ctx);
1772 xmlFreeDoc(request_doc);
1773 http_send_response_400(connection,
1774 "SOAP body does not contain a name-space-qualified element");
1775 return;
1778 /* Build SOAP response envelope */
1779 response_doc = xmlNewDoc(BAD_CAST "1.0");
1780 if (!response_doc) {
1781 http_send_response_500(connection,
1782 "Could not build SOAP response document");
1783 goto leave;
1785 response_soap_envelope = xmlNewNode(NULL, BAD_CAST "Envelope");
1786 if (!response_soap_envelope) {
1787 http_send_response_500(connection,
1788 "Could not build SOAP response envelope");
1789 goto leave;
1791 xmlDocSetRootElement(response_doc, response_soap_envelope);
1792 /* Only this way we get namespace definition as @xmlns:soap,
1793 * otherwise we get namespace prefix without definition */
1794 soap_ns = xmlNewNs(response_soap_envelope, BAD_CAST SOAP_NS, NULL);
1795 if(NULL == soap_ns) {
1796 http_send_response_500(connection, "Could not create SOAP name space");
1797 goto leave;
1799 xmlSetNs(response_soap_envelope, soap_ns);
1800 response_soap_body = xmlNewChild(response_soap_envelope, NULL,
1801 BAD_CAST "Body", NULL);
1802 if (!response_soap_body) {
1803 http_send_response_500(connection,
1804 "Could not add Body to SOAP response envelope");
1805 goto leave;
1807 /* Append ISDS response element */
1808 if (-1 == test_asprintf(&response_name, "%s%s", isds_request->name,
1809 "Response")) {
1810 http_send_response_500(connection,
1811 "Could not buld ISDS resposne element name");
1812 goto leave;
1814 isds_response = xmlNewChild(response_soap_body, NULL,
1815 BAD_CAST response_name, NULL);
1816 free(response_name);
1817 if (NULL == isds_response) {
1818 http_send_response_500(connection,
1819 "Could not add ISDS response element to SOAP response body");
1820 goto leave;
1822 isds_ns = xmlNewNs(isds_response, isds_request->ns->href, NULL);
1823 if(NULL == isds_ns) {
1824 http_send_response_500(connection,
1825 "Could not create a name space for the response body");
1826 goto leave;
1828 xmlSetNs(isds_response, isds_ns);
1830 /* Dispatch request to service */
1831 for (size_t i = 0; i < sizeof(services)/sizeof(services[0]); i++) {
1832 if (!strcmp(services[i].end_point, end_point) &&
1833 !xmlStrcmp(services[i].name_space, isds_request->ns->href) &&
1834 !xmlStrcmp(services[i].name, isds_request->name)) {
1835 /* Check if the configuration is enabled and find configuration */
1836 for (const struct service_configuration *service = configuration;
1837 service->name != SERVICE_END; service++) {
1838 if (service->name == services[i].id) {
1839 service_handled = 1;
1840 if (!xmlStrcmp(services[i].name_space, BAD_CAST OISDS_NS)) {
1841 /* Alias "isds" XPath identifier to OISDS_NS */
1842 if (register_namespaces(xpath_ctx, 1,
1843 MESSAGE_NS_UNSIGNED)) {
1844 http_send_response_500(connection,
1845 "Could not register name spaces to the "
1846 "XPath context");
1847 break;
1850 xpath_ctx->node = isds_request;
1851 if (HTTP_ERROR_SERVER != services[i].function(
1852 xpath_ctx,
1853 isds_response,
1854 service->arguments)) {
1855 service_passed = 1;
1856 } else {
1857 http_send_response_500(connection,
1858 "Internal server error while processing "
1859 "ISDS request");
1863 break;
1867 /* Send response */
1868 if (service_passed) {
1869 /* Serialize the SOAP response */
1870 http_response_body = xmlBufferCreate();
1871 if (NULL == http_response_body) {
1872 http_send_response_500(connection,
1873 "Could not create xmlBuffer for response serialization");
1874 goto leave;
1876 /* Last argument 1 means format the XML tree. This is pretty but it breaks
1877 * XML document transport as it adds text nodes (indentiation) between
1878 * elements. */
1879 save_ctx = xmlSaveToBuffer(http_response_body, "UTF-8", 0);
1880 if (NULL == save_ctx) {
1881 http_send_response_500(connection, "Could not create XML serializer");
1882 goto leave;
1884 /* XXX: According LibXML documentation, this function does not return
1885 * meaningful value yet */
1886 xmlSaveDoc(save_ctx, response_doc);
1887 if (-1 == xmlSaveFlush(save_ctx)) {
1888 http_send_response_500(connection,
1889 "Could not serialize SOAP response");
1890 goto leave;
1893 http_send_response_200(connection, http_response_body->content,
1894 http_response_body->use, soap_mime_type);
1897 leave:
1898 xmlSaveClose(save_ctx);
1899 xmlBufferFree(http_response_body);
1901 xmlFreeDoc(response_doc);
1903 xmlXPathFreeObject(request_soap_body);
1904 xmlXPathFreeContext(xpath_ctx);
1905 xmlFreeDoc(request_doc);
1907 if (!service_handled) {
1908 http_send_response_500(connection,
1909 "Requested ISDS service not implemented");