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