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 /* Extract @element_name's value as a string.
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 a element whose child text node to extract
287 * @string is the extraced allocated string value, or NULL if empty or the
288 * element does not exist.
289 * @return HTTP_ERROR_SUCCESS or an appropriate error code. */
290 static http_error
extract_string(const char **code
, char **message
,
291 xmlXPathContextPtr xpath_ctx
, const char *element_name
,
293 http_error error
= HTTP_ERROR_SUCCESS
;
294 xmlXPathObjectPtr result
= NULL
;
297 if (-1 == test_asprintf(&buffer
, "%s/text()", element_name
)) {
298 error
= HTTP_ERROR_SERVER
;
301 result
= xmlXPathEvalExpression(BAD_CAST buffer
, xpath_ctx
);
303 if (NULL
== result
) {
304 error
= HTTP_ERROR_SERVER
;
307 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
308 if (result
->nodesetval
->nodeNr
> 1) {
309 xmlXPathFreeObject(result
);
311 test_asprintf(message
, "Multiple %s element", element_name
);
312 error
= HTTP_ERROR_CLIENT
;
316 xmlXPathCastNodeSetToString(result
->nodesetval
);
318 xmlXPathFreeObject(result
);
319 error
= HTTP_ERROR_SERVER
;
323 xmlXPathFreeObject(result
);
330 /* Checks an @element_name's value is an @expected_value string.
331 * @code is a static output ISDS error code
332 * @error_message is a reallocated output ISDS error message
333 * @xpath_ctx is a current XPath context
334 * @element_name is name of a element to check
335 * @must_exist is true if the @element_name must exist even if @expected_value
337 * @expected_value is an expected string value
338 * @return HTTP_ERROR_SUCCESS if the @element_name element's value is
339 * @expected_value. HTTP_ERROR_CLIENT if not equaled, HTTP_ERROR_SERVER if an
340 * internal error occured. */
341 static http_error
element_equals_string(const char **code
, char **message
,
342 xmlXPathContextPtr xpath_ctx
, const char *element_name
,
343 _Bool must_exist
, const char *expected_value
) {
344 http_error error
= HTTP_ERROR_SUCCESS
;
348 error
= element_exists(code
, message
, xpath_ctx
, element_name
, 0);
349 if (HTTP_ERROR_SUCCESS
!= error
)
353 error
= extract_string(code
, message
, xpath_ctx
, element_name
, &string
);
354 if (HTTP_ERROR_SUCCESS
!= error
)
357 if (NULL
!= expected_value
) {
358 if (NULL
== string
) {
360 test_asprintf(message
, "Empty %s element", element_name
);
361 error
= HTTP_ERROR_CLIENT
;
364 if (xmlStrcmp(BAD_CAST expected_value
, BAD_CAST string
)) {
366 test_asprintf(message
,
367 "Unexpected %s element value: expected=`%s', got=`%s'",
368 element_name
, expected_value
, string
);
369 error
= HTTP_ERROR_CLIENT
;
373 if (NULL
!= string
&& *string
!= '\0') {
375 test_asprintf(message
,
376 "Unexpected %s element value: "
377 "expected empty string, got=`%s'",
378 element_name
, string
);
379 error
= HTTP_ERROR_CLIENT
;
390 /* Checks an @element_name's value is an @expected_value integer.
391 * @code is a static output ISDS error code
392 * @error_message is a reallocated output ISDS error message
393 * @xpath_ctx is a current XPath context
394 * @element_name is name of a element to check
395 * @must_exist is true if the @element_name must exist even if @expected_value
397 * @expected_value is an expected integer value.
398 * @return HTTP_ERROR_SUCCESS if the @element_name element's value is
399 * @expected_value. HTTP_ERROR_CLIENT if not equaled, HTTP_ERROR_SERVER if an
400 * internal error occured. */
401 static http_error
element_equals_integer(const char **code
, char **message
,
402 xmlXPathContextPtr xpath_ctx
, const char *element_name
,
403 _Bool must_exist
, const long int *expected_value
) {
404 http_error error
= HTTP_ERROR_SUCCESS
;
410 error
= element_exists(code
, message
, xpath_ctx
, element_name
, 0);
411 if (HTTP_ERROR_SUCCESS
!= error
)
415 error
= extract_string(code
, message
, xpath_ctx
, element_name
, &string
);
416 if (HTTP_ERROR_SUCCESS
!= error
)
419 if (NULL
!= expected_value
) {
420 if (NULL
== string
) {
422 test_asprintf(message
, "Empty %s element", element_name
);
423 error
= HTTP_ERROR_CLIENT
;
426 number
= strtol(string
, &endptr
, 10);
427 if (*endptr
!= '\0') {
429 test_asprintf(message
,
430 "%s element value is not a valid integer: %s",
431 element_name
, string
);
432 error
= HTTP_ERROR_CLIENT
;
435 if (number
== LONG_MIN
|| number
== LONG_MAX
) { \
437 test_asprintf(message
, \
438 "%s element value is out of range of long int: %s",
439 element_name
, string
);
440 error
= HTTP_ERROR_SERVER
;
443 free(string
); string
= NULL
;
444 if (number
!= *expected_value
) {
446 test_asprintf(message
,
447 "Unexpected %s element value: expected=`%ld', got=`%ld'",
448 element_name
, *expected_value
, number
);
449 error
= HTTP_ERROR_CLIENT
;
453 if (NULL
!= string
&& *string
!= '\0') {
455 test_asprintf(message
,
456 "Unexpected %s element value: expected no text node, got=`%s'",
457 element_name
, string
);
458 error
= HTTP_ERROR_CLIENT
;
469 /* Checks an @element_name's value is an @expected_value boolean.
470 * @code is a static output ISDS error code
471 * @error_message is an reallocated output ISDS error message
472 * @xpath_ctx is a current XPath context
473 * @element_name is name of a element to check
474 * @must_exist is true if the @element_name must exist even if @expected_value
476 * @expected_value is an expected boolean value
477 * @return HTTP_ERROR_SUCCESS if the @element_name element's value is
478 * @expected_value. HTTP_ERROR_CLIENT if not equaled, HTTP_ERROR_SERVER if an
479 * internal error occured. */
480 static http_error
element_equals_boolean(const char **code
, char **message
,
481 xmlXPathContextPtr xpath_ctx
, const char *element_name
,
482 _Bool must_exist
, const _Bool
*expected_value
) {
483 http_error error
= HTTP_ERROR_SUCCESS
;
488 error
= element_exists(code
, message
, xpath_ctx
, element_name
, 0);
489 if (HTTP_ERROR_SUCCESS
!= error
)
493 error
= extract_string(code
, message
, xpath_ctx
, element_name
, &string
);
494 if (HTTP_ERROR_SUCCESS
!= error
)
497 if (NULL
!= expected_value
) {
498 if (NULL
== string
) {
500 test_asprintf(message
, "Empty %s element", element_name
);
501 error
= HTTP_ERROR_CLIENT
;
504 if (!xmlStrcmp((xmlChar
*)string
, BAD_CAST
"true") ||
505 !xmlStrcmp((xmlChar
*)string
, BAD_CAST
"1"))
507 else if (!xmlStrcmp((xmlChar
*)string
, BAD_CAST
"false") ||
508 !xmlStrcmp((xmlChar
*)string
, BAD_CAST
"0"))
512 test_asprintf(message
,
513 "%s element value is not a valid boolean: %s",
514 element_name
, string
);
515 error
= HTTP_ERROR_CLIENT
;
518 if (*expected_value
!= value
) {
520 test_asprintf(message
,
521 "Unexpected %s element value: expected=%d, got=%d",
522 element_name
, *expected_value
, string
);
523 error
= HTTP_ERROR_CLIENT
;
527 if (NULL
!= string
&& *string
!= '\0') {
529 test_asprintf(message
,
530 "Unexpected %s element value: "
531 "expected empty string, got=`%s'",
532 element_name
, string
);
533 error
= HTTP_ERROR_CLIENT
;
544 /* Insert dmStatus or similar subtree
545 * @parent is element to insert to
546 * @dm is true for dmStatus, otherwise dbStatus
547 * @code is status code as string
548 * @message is UTF-8 encoded message
549 * @db_ref_number is optinal reference number propagated if not @dm
550 * @return 0 on success, otherwise non-0. */
551 static http_error
insert_isds_status(xmlNodePtr parent
, _Bool dm
,
552 const xmlChar
*code
, const xmlChar
*message
,
553 const xmlChar
*db_ref_number
) {
554 http_error error
= HTTP_ERROR_SUCCESS
;
557 if (NULL
== code
|| NULL
== message
) {
558 error
= HTTP_ERROR_SERVER
;
562 INSERT_ELEMENT(status
, parent
, (dm
) ? "dmStatus" : "dbStatus");
563 INSERT_STRING(status
, (dm
) ? "dmStatusCode" : "dbStatusCode", code
);
564 INSERT_STRING(status
, (dm
) ? "dmStatusMessage" : "dbStatusMessage", message
);
565 if (!dm
&& NULL
!= db_ref_number
) {
566 INSERT_STRING(status
, "dbStatusRefNumber", db_ref_number
);
574 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
575 static http_error
tm2datestring(const struct tm
*time
, char **string
) {
576 if (NULL
== time
|| NULL
== string
) return HTTP_ERROR_SERVER
;
578 if (-1 == test_asprintf(string
, "%d-%02d-%02d",
579 time
->tm_year
+ 1900, time
->tm_mon
+ 1, time
->tm_mday
))
580 return HTTP_ERROR_SERVER
;
582 return HTTP_ERROR_SUCCESS
;
586 /* Convert struct timeval *@time to UTF-8 ISO 8601 date-time @string. It
587 * respects the @time microseconds too. */
588 static http_error
timeval2timestring(const struct timeval
*time
,
592 if (!time
|| !string
) return HTTP_ERROR_SERVER
;
594 if (!gmtime_r(&time
->tv_sec
, &broken
)) return HTTP_ERROR_SERVER
;
595 if (time
->tv_usec
< 0 || time
->tv_usec
> 999999) return HTTP_ERROR_SERVER
;
597 /* TODO: small negative year should be formatted as "-0012". This is not
598 * true for glibc "%04d". We should implement it.
599 * time->tv_usec type is su_seconds_t which is required to be signed
600 * integer to accomodate values from range [-1, 1000000].
601 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
602 if (-1 == test_asprintf(string
,
603 "%04d-%02d-%02dT%02d:%02d:%02d.%06" PRIdMAX
,
604 broken
.tm_year
+ 1900, broken
.tm_mon
+ 1, broken
.tm_mday
,
605 broken
.tm_hour
, broken
.tm_min
, broken
.tm_sec
,
606 (intmax_t)time
->tv_usec
))
607 return HTTP_ERROR_SERVER
;
609 return HTTP_ERROR_SUCCESS
;
613 /* Compare dates represented by pointer to struct tm.
614 * @return 0 if equalued, non-0 otherwise. */
615 static int datecmp(const struct tm
*a
, const struct tm
*b
) {
616 if (NULL
== a
&& b
== NULL
) return 0;
617 if ((NULL
== a
&& b
!= NULL
) || (NULL
!= a
&& b
== NULL
)) return 1;
618 if (a
->tm_year
!= b
->tm_year
) return 1;
619 if (a
->tm_mon
!= b
->tm_mon
) return 1;
620 if (a
->tm_mday
!= b
->tm_mday
) return 1;
625 /* Implement DummyOperation */
626 static http_error
service_DummyOperation(
627 xmlXPathContextPtr xpath_ctx
,
628 xmlNodePtr isds_response
,
629 const void *arguments
) {
633 return insert_isds_status(isds_response
, 1, BAD_CAST
"0000",
634 BAD_CAST
"Success", NULL
);
638 /* Implement Re-signISDSDocument.
639 * It sends document from request back.
640 * @arguments is pointer to struct arguments_DS_Dz_ResignISDSDocument */
641 static http_error
service_ResignISDSDocument(
642 xmlXPathContextPtr xpath_ctx
,
643 xmlNodePtr isds_response
,
644 const void *arguments
) {
645 http_error error
= HTTP_ERROR_SUCCESS
;
646 const char *code
= "9999";
647 char *message
= NULL
;
648 const struct arguments_DS_Dz_ResignISDSDocument
*configuration
=
649 (const struct arguments_DS_Dz_ResignISDSDocument
*)arguments
;
652 if (NULL
== configuration
|| NULL
== configuration
->status_code
||
653 NULL
== configuration
->status_message
) {
654 error
= HTTP_ERROR_SERVER
;
658 EXTRACT_STRING("isds:dmDoc", data
);
660 message
= strdup("Missing isds:dmDoc");
661 error
= HTTP_ERROR_CLIENT
;
666 /* dmResultDoc is mandatory in response */
667 if (xmlStrcmp(BAD_CAST configuration
->status_code
, BAD_CAST
"0000")) {
671 INSERT_STRING(isds_response
, "dmResultDoc", data
);
673 if (configuration
->valid_to
!= NULL
) {
674 error
= tm2datestring(configuration
->valid_to
, &data
);
676 message
= strdup("Could not format date");
679 INSERT_STRING(isds_response
, "dmValidTo", data
);
682 code
= configuration
->status_code
;
683 message
= strdup(configuration
->status_message
);
686 if (HTTP_ERROR_SERVER
!= error
) {
687 http_error next_error
= insert_isds_status(isds_response
, 1,
688 BAD_CAST code
, BAD_CAST message
, NULL
);
689 if (HTTP_ERROR_SUCCESS
!= next_error
) error
= next_error
;
697 /* Implement EraseMessage.
698 * @arguments is pointer to struct arguments_DS_DsManage_ChangeISDSPassword */
699 static http_error
service_EraseMessage(
700 xmlXPathContextPtr xpath_ctx
,
701 xmlNodePtr isds_response
,
702 const void *arguments
) {
703 http_error error
= HTTP_ERROR_SUCCESS
;
704 char *code
= "9999", *message
= NULL
;
705 const struct arguments_DS_Dx_EraseMessage
*configuration
=
706 (const struct arguments_DS_Dx_EraseMessage
*)arguments
;
707 char *message_id
= NULL
;
708 _Bool
*incoming
= NULL
;
710 if (NULL
== configuration
|| NULL
== configuration
->message_id
) {
711 error
= HTTP_ERROR_SERVER
;
715 EXTRACT_STRING("isds:dmID", message_id
);
716 if (NULL
== message_id
) {
717 message
= strdup("Missing isds:dmID");
718 error
= HTTP_ERROR_CLIENT
;
721 EXTRACT_BOOLEAN("isds:dmIncoming", incoming
);
722 if (NULL
== incoming
) {
723 message
= strdup("Missing isds:dmIncoming");
724 error
= HTTP_ERROR_CLIENT
;
728 if (xmlStrcmp((const xmlChar
*) configuration
->message_id
,
729 (const xmlChar
*) message_id
)) {
731 message
= strdup("Message is not in the long term storage");
732 error
= HTTP_ERROR_CLIENT
;
735 if (configuration
->incoming
!= *incoming
) {
737 message
= strdup("Message direction mismatches");
738 error
= HTTP_ERROR_CLIENT
;
743 message
= strdup("Success");
745 if (HTTP_ERROR_SERVER
!= error
) {
746 http_error next_error
= insert_isds_status(isds_response
, 1,
747 BAD_CAST code
, BAD_CAST message
, NULL
);
748 if (HTTP_ERROR_SUCCESS
!= next_error
) error
= next_error
;
757 /* Insert list of credit info as XSD:tCiRecord XML tree.
758 * @isds_response is XML node with the response
759 * @history is list of struct server_credit_event. If NULL, no ciRecord XML
760 * subtree will be created. */
761 static http_error
insert_ciRecords(xmlNodePtr isds_response
,
762 const struct server_list
*history
) {
763 http_error error
= HTTP_ERROR_SUCCESS
;
764 xmlNodePtr records
, record
;
766 if (NULL
== isds_response
) return HTTP_ERROR_SERVER
;
767 if (NULL
== history
) return HTTP_ERROR_SUCCESS
;
769 INSERT_ELEMENT(records
, isds_response
, "ciRecords");
770 for (const struct server_list
*item
= history
; NULL
!= item
;
772 const struct server_credit_event
*event
=
773 (struct server_credit_event
*)item
->data
;
775 INSERT_ELEMENT(record
, records
, "ciRecord");
776 if (NULL
== event
) continue;
778 INSERT_TIMEVALPTR(record
, "ciEventTime", event
->time
);
779 switch(event
->type
) {
780 case SERVER_CREDIT_CHARGED
:
781 INSERT_STRING(record
, "ciEventType", "1");
782 INSERT_STRING(record
, "ciTransID",
783 event
->details
.charged
.transaction
);
785 case SERVER_CREDIT_DISCHARGED
:
786 INSERT_STRING(record
, "ciEventType", "2");
787 INSERT_STRING(record
, "ciTransID",
788 event
->details
.discharged
.transaction
);
790 case SERVER_CREDIT_MESSAGE_SENT
:
791 INSERT_STRING(record
, "ciEventType", "3");
792 INSERT_STRING(record
, "ciRecipientID",
793 event
->details
.message_sent
.recipient
);
794 INSERT_STRING(record
, "ciPDZID",
795 event
->details
.message_sent
.message_id
);
797 case SERVER_CREDIT_STORAGE_SET
:
798 INSERT_STRING(record
, "ciEventType", "4");
799 INSERT_LONGINTPTR(record
, "ciNewCapacity",
800 &event
->details
.storage_set
.new_capacity
);
801 INSERT_TMPTR(record
, "ciNewFrom",
802 event
->details
.storage_set
.new_valid_from
);
803 INSERT_TMPTR(record
, "ciNewTo",
804 event
->details
.storage_set
.new_valid_to
);
805 INSERT_LONGINTPTR(record
, "ciOldCapacity",
806 event
->details
.storage_set
.old_capacity
);
807 INSERT_TMPTR(record
, "ciOldFrom",
808 event
->details
.storage_set
.old_valid_from
);
809 INSERT_TMPTR(record
, "ciOldTo",
810 event
->details
.storage_set
.old_valid_to
);
811 INSERT_STRING(record
, "ciDoneBy",
812 event
->details
.storage_set
.initiator
);
814 case SERVER_CREDIT_EXPIRED
:
815 INSERT_STRING(record
, "ciEventType", "5");
818 error
= HTTP_ERROR_SERVER
;
821 INSERT_LONGINTPTR(record
, "ciCreditChange", &event
->credit_change
);
822 INSERT_LONGINTPTR(record
, "ciCreditAfter", &event
->new_credit
);
831 /* Implement DataBoxCreditInfo.
832 * @arguments is pointer to struct arguments_DS_df_DataBoxCreditInfo */
833 static http_error
service_DataBoxCreditInfo(
834 xmlXPathContextPtr xpath_ctx
,
835 xmlNodePtr isds_response
,
836 const void *arguments
) {
837 http_error error
= HTTP_ERROR_SUCCESS
;
838 const char *code
= "9999";
839 char *message
= NULL
;
840 const struct arguments_DS_df_DataBoxCreditInfo
*configuration
=
841 (const struct arguments_DS_df_DataBoxCreditInfo
*)arguments
;
843 struct tm
*from_date
= NULL
, *to_date
= NULL
;
845 if (NULL
== configuration
|| NULL
== configuration
->status_code
||
846 NULL
== configuration
->status_message
) {
847 error
= HTTP_ERROR_SERVER
;
851 EXTRACT_STRING("isds:dbID", box_id
);
852 if (NULL
== box_id
) {
853 message
= strdup("Missing isds:dbID");
854 error
= HTTP_ERROR_CLIENT
;
857 if (NULL
!= configuration
->box_id
&&
858 xmlStrcmp(BAD_CAST configuration
->box_id
,
861 message
= strdup("Unexpected isds:dbID value");
862 error
= HTTP_ERROR_CLIENT
;
866 ELEMENT_EXISTS("isds:ciFromDate", 0);
867 EXTRACT_DATE("isds:ciFromDate", from_date
);
868 if (datecmp(configuration
->from_date
, from_date
)) {
870 message
= strdup("Unexpected isds:ciFromDate value");
871 error
= HTTP_ERROR_CLIENT
;
875 ELEMENT_EXISTS("isds:ciTodate", 0);
876 EXTRACT_DATE("isds:ciTodate", to_date
);
877 if (datecmp(configuration
->to_date
, to_date
)) {
879 message
= strdup("Unexpected isds:ciTodate value");
880 error
= HTTP_ERROR_CLIENT
;
884 INSERT_LONGINTPTR(isds_response
, "currentCredit",
885 &configuration
->current_credit
);
886 INSERT_STRING(isds_response
, "notifEmail", configuration
->email
);
887 if ((error
= insert_ciRecords(isds_response
, configuration
->history
))) {
891 code
= configuration
->status_code
;
892 message
= strdup(configuration
->status_message
);
894 if (HTTP_ERROR_SERVER
!= error
) {
895 http_error next_error
= insert_isds_status(isds_response
, 0,
896 BAD_CAST code
, BAD_CAST message
, NULL
);
897 if (HTTP_ERROR_SUCCESS
!= next_error
) error
= next_error
;
907 /* Insert list of fulltext search results as XSD:tdbResultsArray XML tree.
908 * @isds_response is XML node with the response
909 * @results is list of struct server_db_result *.
910 * @create_empty_root is true to create dbResults element even if @results is
912 static http_error
insert_tdbResultsArray(xmlNodePtr isds_response
,
913 const struct server_list
*results
, _Bool create_empty_root
) {
914 http_error error
= HTTP_ERROR_SUCCESS
;
915 xmlNodePtr root
, entry
;
917 if (NULL
== isds_response
) return HTTP_ERROR_SERVER
;
919 if (NULL
!= results
|| create_empty_root
)
920 INSERT_ELEMENT(root
, isds_response
, "dbResults");
922 if (NULL
== results
) return HTTP_ERROR_SUCCESS
;
924 for (const struct server_list
*item
= results
; NULL
!= item
;
926 const struct server_db_result
*result
=
927 (struct server_db_result
*)item
->data
;
929 INSERT_ELEMENT(entry
, root
, "dbResult");
930 if (NULL
== result
) continue;
932 INSERT_STRING(entry
, "dbID", result
->id
);
933 INSERT_STRING(entry
, "dbType", result
->type
);
934 INSERT_STRING(entry
, "dbName", result
->name
);
935 INSERT_STRING(entry
, "dbAddress", result
->address
);
936 INSERT_TMPTR(entry
, "dbBiDate", result
->birth_date
);
937 INSERT_STRING(entry
, "dbICO", result
->ic
);
938 INSERT_BOOLEANPTR(entry
, "dbEffectiveOVM", &result
->ovm
);
939 INSERT_STRING(entry
, "dbSendOptions", result
->send_options
);
947 /* Implement ISDSSearch2.
948 * @arguments is pointer to struct arguments_DS_df_ISDSSearch2 */
949 static http_error
service_ISDSSearch2(
950 xmlXPathContextPtr xpath_ctx
,
951 xmlNodePtr isds_response
,
952 const void *arguments
) {
953 http_error error
= HTTP_ERROR_SUCCESS
;
954 const char *code
= "9999";
955 char *message
= NULL
;
956 const struct arguments_DS_df_ISDSSearch2
*configuration
=
957 (const struct arguments_DS_df_ISDSSearch2
*)arguments
;
960 if (NULL
== configuration
|| NULL
== configuration
->status_code
||
961 NULL
== configuration
->status_message
) {
962 error
= HTTP_ERROR_SERVER
;
967 EXTRACT_STRING("isds:searchText", string
);
968 if (NULL
== string
) {
969 message
= strdup("Missing or empty isds:searchText");
970 error
= HTTP_ERROR_CLIENT
;
973 if (NULL
!= configuration
->search_text
&&
974 xmlStrcmp(BAD_CAST configuration
->search_text
,
977 message
= strdup("Unexpected isds:searchText value");
978 error
= HTTP_ERROR_CLIENT
;
981 free(string
); string
= NULL
;
983 error
= element_equals_string(&code
, &message
, xpath_ctx
,
984 "isds:searchType", 1, configuration
->search_type
);
985 if (error
) goto leave
;
987 error
= element_equals_string(&code
, &message
, xpath_ctx
,
988 "isds:searchScope", 1, configuration
->search_scope
);
989 if (error
) goto leave
;
991 error
= element_equals_integer(&code
, &message
, xpath_ctx
,
992 "isds:page", 1, configuration
->search_page_number
);
993 if (error
) goto leave
;
995 error
= element_equals_integer(&code
, &message
, xpath_ctx
,
996 "isds:pageSize", 1, configuration
->search_page_size
);
997 if (error
) goto leave
;
999 error
= element_equals_boolean(&code
, &message
, xpath_ctx
,
1000 "isds:highlighting", 0, configuration
->search_highlighting_value
);
1001 if (error
) goto leave
;
1003 /* Build response */
1004 if (NULL
!= configuration
->total_count
)
1005 INSERT_ULONGINTPTR(isds_response
, "totalCount",
1006 configuration
->total_count
);
1007 if (NULL
!= configuration
->current_count
)
1008 INSERT_ULONGINTPTR(isds_response
, "currentCount",
1009 configuration
->current_count
);
1010 if (NULL
!= configuration
->position
)
1011 INSERT_ULONGINTPTR(isds_response
, "position",
1012 configuration
->position
);
1013 if (NULL
!= configuration
->last_page
)
1014 INSERT_BOOLEANPTR(isds_response
, "lastPage",
1015 configuration
->last_page
);
1016 if ((error
= insert_tdbResultsArray(isds_response
, configuration
->results
,
1017 configuration
->results_exists
))) {
1021 code
= configuration
->status_code
;
1022 message
= strdup(configuration
->status_message
);
1025 if (HTTP_ERROR_SERVER
!= error
) {
1026 http_error next_error
= insert_isds_status(isds_response
, 0,
1027 BAD_CAST code
, BAD_CAST message
, NULL
);
1028 if (HTTP_ERROR_SUCCESS
!= next_error
) error
= next_error
;
1036 /* Common part for ChangeISDSPassword and ChangePasswordOTP.
1037 * @code is output pointer to static string
1038 * @pass_message is output pointer to auto-allocated string
1039 * @arguments is pointer to struct arguments_DS_DsManage_ChangeISDSPassword */
1040 static http_error
check_passwd(
1041 const char *username
, const char *current_password
,
1042 xmlXPathContextPtr xpath_ctx
,
1043 char **code
, char **pass_message
) {
1044 http_error error
= HTTP_ERROR_SUCCESS
;
1045 char *message
= NULL
;
1046 char *old_password
= NULL
, *new_password
= NULL
;
1049 if (NULL
== username
|| NULL
== current_password
||
1050 NULL
== code
|| NULL
== pass_message
) {
1051 return HTTP_ERROR_SERVER
;
1058 EXTRACT_STRING("isds:dbOldPassword", old_password
);
1059 if (NULL
== old_password
) {
1060 message
= strdup("Empty isds:dbOldPassword");
1061 error
= HTTP_ERROR_CLIENT
;
1064 EXTRACT_STRING("isds:dbNewPassword", new_password
);
1065 if (NULL
== new_password
) {
1066 message
= strdup("Empty isds:dbOldPassword");
1067 error
= HTTP_ERROR_CLIENT
;
1071 /* Check defined cases */
1072 if (strcmp(current_password
, old_password
)) {
1074 message
= strdup("Bad current password");
1075 error
= HTTP_ERROR_CLIENT
;
1079 length
= strlen(new_password
);
1081 if (length
< 8 || length
> 32) {
1083 message
= strdup("Too short or too long");
1084 error
= HTTP_ERROR_CLIENT
;
1089 const char lower
[] = "abcdefghijklmnopqrstuvwxyz";
1090 const char upper
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1091 const char digit
[] = "0123456789";
1092 const char special
[] = "!#$%&()*+,-.:=?@[]_{}|~";
1093 _Bool has_lower
= 0, has_upper
= 0, has_digit
=0;
1095 for (size_t i
= 0; i
< length
; i
++) {
1096 if (NULL
!= strchr(lower
, new_password
[i
]))
1098 else if (NULL
!= strchr(upper
, new_password
[i
]))
1100 else if (NULL
!= strchr(digit
, new_password
[i
]))
1102 else if (NULL
== strchr(special
, new_password
[i
])) {
1104 message
= strdup("Password contains forbidden character");
1105 error
= HTTP_ERROR_CLIENT
;
1110 if (!has_lower
|| !has_upper
|| !has_digit
) {
1112 message
= strdup("Password does not contain lower cased letter, "
1113 "upper cased letter and a digit");
1114 error
= HTTP_ERROR_CLIENT
;
1119 if (!strcmp(old_password
, new_password
)) {
1121 message
= strdup("New password same as current one");
1122 error
= HTTP_ERROR_CLIENT
;
1126 if (NULL
!= strstr(new_password
, username
)) {
1128 message
= strdup("New password contains user ID");
1129 error
= HTTP_ERROR_CLIENT
;
1133 for (size_t i
= 0; i
< length
- 2; i
++) {
1134 if (new_password
[i
] == new_password
[i
+1] &&
1135 new_password
[i
] == new_password
[i
+2]) {
1137 message
= strdup("Password contains sequence "
1138 "of three identical characters");
1139 error
= HTTP_ERROR_CLIENT
;
1145 const char *forbidden_prefix
[] = { "qwert", "asdgf", "12345" };
1146 for (size_t i
= 0; i
< sizeof(forbidden_prefix
)/sizeof(*forbidden_prefix
);
1148 if (!strncmp(new_password
, forbidden_prefix
[i
],
1149 strlen(forbidden_prefix
[i
]))) {
1151 message
= strdup("Password has forbidden prefix");
1152 error
= HTTP_ERROR_CLIENT
;
1159 message
= strdup("Success");
1163 *pass_message
= message
;
1168 /* Implement ChangeISDSPassword.
1169 * @arguments is pointer to struct arguments_DS_DsManage_ChangeISDSPassword */
1170 static http_error
service_ChangeISDSPassword(
1171 xmlXPathContextPtr xpath_ctx
,
1172 xmlNodePtr isds_response
,
1173 const void *arguments
) {
1174 http_error error
= HTTP_ERROR_SUCCESS
;
1175 char *code
= "9999", *message
= NULL
;
1176 const struct arguments_DS_DsManage_ChangeISDSPassword
*configuration
=
1177 (const struct arguments_DS_DsManage_ChangeISDSPassword
*)arguments
;
1179 if (NULL
== configuration
|| NULL
== configuration
->username
||
1180 NULL
== configuration
->current_password
) {
1181 error
= HTTP_ERROR_SERVER
;
1185 /* Check for common password rules */
1186 error
= check_passwd(
1187 configuration
->username
, configuration
->current_password
,
1188 xpath_ctx
, &code
, &message
);
1191 if (HTTP_ERROR_SERVER
!= error
) {
1192 http_error next_error
= insert_isds_status(isds_response
, 0,
1193 BAD_CAST code
, BAD_CAST message
, NULL
);
1194 if (HTTP_ERROR_SUCCESS
!= next_error
) error
= next_error
;
1201 /* Implement ChangePasswordOTP.
1202 * @arguments is pointer to struct
1203 * arguments_asws_changePassword_ChangePasswordOTP */
1204 static http_error
service_ChangePasswordOTP(
1205 xmlXPathContextPtr xpath_ctx
,
1206 xmlNodePtr isds_response
,
1207 const void *arguments
) {
1208 http_error error
= HTTP_ERROR_SUCCESS
;
1209 char *code
= "9999", *message
= NULL
;
1210 const struct arguments_asws_changePassword_ChangePasswordOTP
*configuration
1211 = (const struct arguments_asws_changePassword_ChangePasswordOTP
*)
1213 char *method
= NULL
;
1215 if (NULL
== configuration
|| NULL
== configuration
->username
||
1216 NULL
== configuration
->current_password
) {
1217 error
= HTTP_ERROR_SERVER
;
1221 /* Chek for OTP method */
1222 EXTRACT_STRING("isds:dbOTPType", method
);
1223 if (NULL
== method
) {
1224 message
= strdup("Empty isds:dbOTPType");
1225 error
= HTTP_ERROR_CLIENT
;
1228 if ((configuration
->method
== AUTH_OTP_HMAC
&& strcmp(method
, "HOTP")) ||
1229 (configuration
->method
== AUTH_OTP_TIME
&& strcmp(method
, "TOTP"))) {
1230 message
= strdup("isds:dbOTPType does not match OTP method");
1231 error
= HTTP_ERROR_CLIENT
;
1235 /* Check for common password rules */
1236 error
= check_passwd(
1237 configuration
->username
, configuration
->current_password
,
1238 xpath_ctx
, &code
, &message
);
1241 if (HTTP_ERROR_SERVER
!= error
) {
1242 http_error next_error
= insert_isds_status(isds_response
, 0,
1243 BAD_CAST code
, BAD_CAST message
,
1244 BAD_CAST configuration
->reference_number
);
1245 if (HTTP_ERROR_SUCCESS
!= next_error
) error
= next_error
;
1253 /* Implement SendSMSCode.
1254 * @arguments is pointer to struct arguments_asws_changePassword_SendSMSCode */
1255 static http_error
service_SendSMSCode(
1256 xmlXPathContextPtr xpath_ctx
,
1257 xmlNodePtr isds_response
,
1258 const void *arguments
) {
1259 const struct arguments_asws_changePassword_SendSMSCode
*configuration
1260 = (const struct arguments_asws_changePassword_SendSMSCode
*)
1264 if (NULL
== configuration
|| NULL
== configuration
->status_code
||
1265 NULL
== configuration
->status_message
) {
1266 return HTTP_ERROR_SERVER
;
1269 return insert_isds_status(isds_response
, 0,
1270 BAD_CAST configuration
->status_code
,
1271 BAD_CAST configuration
->status_message
,
1272 BAD_CAST configuration
->reference_number
);
1276 /* List of implemented services */
1277 static struct service services
[] = {
1278 { SERVICE_DS_Dz_DummyOperation
,
1279 "DS/dz", BAD_CAST ISDS_NS
, BAD_CAST
"DummyOperation",
1280 service_DummyOperation
},
1281 { SERVICE_DS_Dz_ResignISDSDocument
,
1282 "DS/dz", BAD_CAST ISDS_NS
, BAD_CAST
"Re-signISDSDocument",
1283 service_ResignISDSDocument
},
1284 { SERVICE_DS_df_DataBoxCreditInfo
,
1285 "DS/df", BAD_CAST ISDS_NS
, BAD_CAST
"DataBoxCreditInfo",
1286 service_DataBoxCreditInfo
},
1287 { SERVICE_DS_df_ISDSSearch2
,
1288 "DS/df", BAD_CAST ISDS_NS
, BAD_CAST
"ISDSSearch2",
1289 service_ISDSSearch2
},
1290 { SERVICE_DS_DsManage_ChangeISDSPassword
,
1291 "DS/DsManage", BAD_CAST ISDS_NS
, BAD_CAST
"ChangeISDSPassword",
1292 service_ChangeISDSPassword
},
1293 { SERVICE_DS_Dx_EraseMessage
,
1294 "DS/dx", BAD_CAST ISDS_NS
, BAD_CAST
"EraseMessage",
1295 service_EraseMessage
},
1296 { SERVICE_asws_changePassword_ChangePasswordOTP
,
1297 "/asws/changePassword", BAD_CAST OISDS_NS
, BAD_CAST
"ChangePasswordOTP",
1298 service_ChangePasswordOTP
},
1299 { SERVICE_asws_changePassword_SendSMSCode
,
1300 "/asws/changePassword", BAD_CAST OISDS_NS
, BAD_CAST
"SendSMSCode",
1301 service_SendSMSCode
},
1305 /* Makes known all relevant namespaces to given XPath context
1306 * @xpath_ctx is XPath context
1307 * @otp_ns selects name space for the request and response know as "isds".
1308 * Use true for OTP-authenticated password change services, otherwise false.
1309 * @message_ns selects proper message name space. Unsigned and signed
1310 * messages and delivery info's differ in prefix and URI.
1311 * @return 0 in success, otherwise not 0. */
1312 static int register_namespaces(xmlXPathContextPtr xpath_ctx
,
1313 const _Bool otp_ns
, const message_ns_type message_ns
) {
1314 const xmlChar
*service_namespace
= NULL
;
1315 const xmlChar
*message_namespace
= NULL
;
1317 if (!xpath_ctx
) return -1;
1320 service_namespace
= BAD_CAST OISDS_NS
;
1322 service_namespace
= BAD_CAST ISDS_NS
;
1325 switch(message_ns
) {
1327 message_namespace
= BAD_CAST ISDS1_NS
; break;
1328 case MESSAGE_NS_UNSIGNED
:
1329 message_namespace
= BAD_CAST ISDS_NS
; break;
1330 case MESSAGE_NS_SIGNED_INCOMING
:
1331 message_namespace
= BAD_CAST SISDS_INCOMING_NS
; break;
1332 case MESSAGE_NS_SIGNED_OUTGOING
:
1333 message_namespace
= BAD_CAST SISDS_OUTGOING_NS
; break;
1334 case MESSAGE_NS_SIGNED_DELIVERY
:
1335 message_namespace
= BAD_CAST SISDS_DELIVERY_NS
; break;
1340 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"soap", BAD_CAST SOAP_NS
))
1342 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"isds", service_namespace
))
1344 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"sisds", message_namespace
))
1346 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"xs", BAD_CAST SCHEMA_NS
))
1348 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"deposit", BAD_CAST DEPOSIT_NS
))
1354 /* Parse soap request, pass it to service endpoint and respond to it.
1355 * It sends final HTTP response. */
1356 void soap(const struct http_connection
*connection
,
1357 const struct service_configuration
*configuration
,
1358 const void *request
, size_t request_length
, const char *end_point
) {
1359 xmlDocPtr request_doc
= NULL
;
1360 xmlXPathContextPtr xpath_ctx
= NULL
;
1361 xmlXPathObjectPtr request_soap_body
= NULL
;
1362 xmlNodePtr isds_request
= NULL
; /* pointer only */
1363 _Bool service_handled
= 0, service_passed
= 0;
1364 xmlDocPtr response_doc
= NULL
;
1365 xmlNodePtr response_soap_envelope
= NULL
, response_soap_body
= NULL
,
1366 isds_response
= NULL
;
1367 xmlNsPtr soap_ns
= NULL
, isds_ns
= NULL
;
1368 char *response_name
= NULL
;
1369 xmlBufferPtr http_response_body
= NULL
;
1370 xmlSaveCtxtPtr save_ctx
= NULL
;
1373 if (NULL
== configuration
) {
1374 http_send_response_500(connection
,
1375 "Second argument of soap() is NULL");
1379 if (NULL
== request
|| request_length
== 0) {
1380 http_send_response_400(connection
, "Client sent empty body");
1384 request_doc
= xmlParseMemory(request
, request_length
);
1385 if (NULL
== request_doc
) {
1386 http_send_response_400(connection
, "Client sent invalid XML document");
1390 xpath_ctx
= xmlXPathNewContext(request_doc
);
1391 if (NULL
== xpath_ctx
) {
1392 xmlFreeDoc(request_doc
);
1393 http_send_response_500(connection
, "Could not create XPath context");
1397 if (register_namespaces(xpath_ctx
, 0, MESSAGE_NS_UNSIGNED
)) {
1398 xmlXPathFreeContext(xpath_ctx
);
1399 xmlFreeDoc(request_doc
);
1400 http_send_response_500(connection
,
1401 "Could not register name spaces to the XPath context");
1406 request_soap_body
= xmlXPathEvalExpression(
1407 BAD_CAST
"/soap:Envelope/soap:Body", xpath_ctx
);
1408 if (NULL
== request_soap_body
) {
1409 xmlXPathFreeContext(xpath_ctx
);
1410 xmlFreeDoc(request_doc
);
1411 http_send_response_400(connection
, "Client sent invalid SOAP request");
1414 if (xmlXPathNodeSetIsEmpty(request_soap_body
->nodesetval
)) {
1415 xmlXPathFreeObject(request_soap_body
);
1416 xmlXPathFreeContext(xpath_ctx
);
1417 xmlFreeDoc(request_doc
);
1418 http_send_response_400(connection
,
1419 "SOAP request does not contain SOAP Body element");
1422 if (request_soap_body
->nodesetval
->nodeNr
> 1) {
1423 xmlXPathFreeObject(request_soap_body
);
1424 xmlXPathFreeContext(xpath_ctx
);
1425 xmlFreeDoc(request_doc
);
1426 http_send_response_400(connection
,
1427 "SOAP response has more than one Body element");
1430 isds_request
= request_soap_body
->nodesetval
->nodeTab
[0]->children
;
1431 if (isds_request
->next
!= NULL
) {
1432 xmlXPathFreeObject(request_soap_body
);
1433 xmlXPathFreeContext(xpath_ctx
);
1434 xmlFreeDoc(request_doc
);
1435 http_send_response_400(connection
, "SOAP body has more than one child");
1438 if (isds_request
->type
!= XML_ELEMENT_NODE
|| isds_request
->ns
== NULL
||
1439 NULL
== isds_request
->ns
->href
) {
1440 xmlXPathFreeObject(request_soap_body
);
1441 xmlXPathFreeContext(xpath_ctx
);
1442 xmlFreeDoc(request_doc
);
1443 http_send_response_400(connection
,
1444 "SOAP body does not contain a name-space-qualified element");
1448 /* Build SOAP response envelope */
1449 response_doc
= xmlNewDoc(BAD_CAST
"1.0");
1450 if (!response_doc
) {
1451 http_send_response_500(connection
,
1452 "Could not build SOAP response document");
1455 response_soap_envelope
= xmlNewNode(NULL
, BAD_CAST
"Envelope");
1456 if (!response_soap_envelope
) {
1457 http_send_response_500(connection
,
1458 "Could not build SOAP response envelope");
1461 xmlDocSetRootElement(response_doc
, response_soap_envelope
);
1462 /* Only this way we get namespace definition as @xmlns:soap,
1463 * otherwise we get namespace prefix without definition */
1464 soap_ns
= xmlNewNs(response_soap_envelope
, BAD_CAST SOAP_NS
, NULL
);
1465 if(NULL
== soap_ns
) {
1466 http_send_response_500(connection
, "Could not create SOAP name space");
1469 xmlSetNs(response_soap_envelope
, soap_ns
);
1470 response_soap_body
= xmlNewChild(response_soap_envelope
, NULL
,
1471 BAD_CAST
"Body", NULL
);
1472 if (!response_soap_body
) {
1473 http_send_response_500(connection
,
1474 "Could not add Body to SOAP response envelope");
1477 /* Append ISDS response element */
1478 if (-1 == test_asprintf(&response_name
, "%s%s", isds_request
->name
,
1480 http_send_response_500(connection
,
1481 "Could not buld ISDS resposne element name");
1484 isds_response
= xmlNewChild(response_soap_body
, NULL
,
1485 BAD_CAST response_name
, NULL
);
1486 free(response_name
);
1487 if (NULL
== isds_response
) {
1488 http_send_response_500(connection
,
1489 "Could not add ISDS response element to SOAP response body");
1492 isds_ns
= xmlNewNs(isds_response
, isds_request
->ns
->href
, NULL
);
1493 if(NULL
== isds_ns
) {
1494 http_send_response_500(connection
,
1495 "Could not create a name space for the response body");
1498 xmlSetNs(isds_response
, isds_ns
);
1500 /* Dispatch request to service */
1501 for (size_t i
= 0; i
< sizeof(services
)/sizeof(services
[0]); i
++) {
1502 if (!strcmp(services
[i
].end_point
, end_point
) &&
1503 !xmlStrcmp(services
[i
].name_space
, isds_request
->ns
->href
) &&
1504 !xmlStrcmp(services
[i
].name
, isds_request
->name
)) {
1505 /* Check if the configuration is enabled and find configuration */
1506 for (const struct service_configuration
*service
= configuration
;
1507 service
->name
!= SERVICE_END
; service
++) {
1508 if (service
->name
== services
[i
].id
) {
1509 service_handled
= 1;
1510 if (!xmlStrcmp(services
[i
].name_space
, BAD_CAST OISDS_NS
)) {
1511 /* Alias "isds" XPath identifier to OISDS_NS */
1512 if (register_namespaces(xpath_ctx
, 1,
1513 MESSAGE_NS_UNSIGNED
)) {
1514 http_send_response_500(connection
,
1515 "Could not register name spaces to the "
1520 xpath_ctx
->node
= isds_request
;
1521 if (HTTP_ERROR_SERVER
!= services
[i
].function(
1524 service
->arguments
)) {
1527 http_send_response_500(connection
,
1528 "Internal server error while processing "
1538 if (service_passed
) {
1539 /* Serialize the SOAP response */
1540 http_response_body
= xmlBufferCreate();
1541 if (NULL
== http_response_body
) {
1542 http_send_response_500(connection
,
1543 "Could not create xmlBuffer for response serialization");
1546 /* Last argument 1 means format the XML tree. This is pretty but it breaks
1547 * XML document transport as it adds text nodes (indentiation) between
1549 save_ctx
= xmlSaveToBuffer(http_response_body
, "UTF-8", 0);
1550 if (NULL
== save_ctx
) {
1551 http_send_response_500(connection
, "Could not create XML serializer");
1554 /* XXX: According LibXML documentation, this function does not return
1555 * meaningful value yet */
1556 xmlSaveDoc(save_ctx
, response_doc
);
1557 if (-1 == xmlSaveFlush(save_ctx
)) {
1558 http_send_response_500(connection
,
1559 "Could not serialize SOAP response");
1563 http_send_response_200(connection
, http_response_body
->content
,
1564 http_response_body
->use
, soap_mime_type
);
1568 xmlSaveClose(save_ctx
);
1569 xmlBufferFree(http_response_body
);
1571 xmlFreeDoc(response_doc
);
1573 xmlXPathFreeObject(request_soap_body
);
1574 xmlXPathFreeContext(xpath_ctx
);
1575 xmlFreeDoc(request_doc
);
1577 if (!service_handled
) {
1578 http_send_response_500(connection
,
1579 "Requested ISDS service not implemented");