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