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