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 <libxml/parser.h>
10 #include <libxml/xpath.h>
11 #include <libxml/xpathInternals.h>
12 #include <libxml/xmlsave.h>
14 static const char *soap_mime_type
= "text/xml"; /* SOAP/1.1 requires text/xml */
16 /* Used to choose proper name space for message elements.
17 * See _isds_register_namespaces(). */
21 MESSAGE_NS_SIGNED_INCOMING
,
22 MESSAGE_NS_SIGNED_OUTGOING
,
23 MESSAGE_NS_SIGNED_DELIVERY
,
27 #define SOAP_NS "http://schemas.xmlsoap.org/soap/envelope/"
28 #define SOAP2_NS "http://www.w3.org/2003/05/soap-envelope"
29 #define ISDS1_NS "http://isds.czechpoint.cz"
30 #define ISDS_NS "http://isds.czechpoint.cz/v20"
31 #define OISDS_NS "http://isds.czechpoint.cz/v20/asws"
32 #define SISDS_INCOMING_NS "http://isds.czechpoint.cz/v20/message"
33 #define SISDS_OUTGOING_NS "http://isds.czechpoint.cz/v20/SentMessage"
34 #define SISDS_DELIVERY_NS "http://isds.czechpoint.cz/v20/delivery"
35 #define SCHEMA_NS "http://www.w3.org/2001/XMLSchema"
36 #define DEPOSIT_NS "urn:uschovnaWSDL"
41 const char *end_point
;
42 const xmlChar
*name_space
;
44 http_error (*function
) (
45 xmlXPathContextPtr
, xmlNodePtr
,
46 const void *arguments
);
49 /* Following EXTRACT_* macros expect @xpath_ctx, @error, @message,
51 #define ELEMENT_EXISTS(element, allow_multiple) { \
52 xmlXPathObjectPtr result = NULL; \
53 result = xmlXPathEvalExpression(BAD_CAST element, xpath_ctx); \
54 if (NULL == result) { \
55 error = HTTP_ERROR_SERVER; \
58 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
59 xmlXPathFreeObject(result); \
60 test_asprintf(&message, "Element %s does not exist", element); \
61 error = HTTP_ERROR_CLIENT; \
64 if (!allow_multiple && result->nodesetval->nodeNr > 1) { \
65 xmlXPathFreeObject(result); \
66 test_asprintf(&message, "Multiple %s element", element); \
67 error = HTTP_ERROR_CLIENT; \
71 xmlXPathFreeObject(result); \
74 #define EXTRACT_STRING(element, string) { \
75 xmlXPathObjectPtr result = NULL; \
76 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
77 if (NULL == result) { \
78 error = HTTP_ERROR_SERVER; \
81 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
82 if (result->nodesetval->nodeNr > 1) { \
83 xmlXPathFreeObject(result); \
84 test_asprintf(&message, "Multiple %s element", element); \
85 error = HTTP_ERROR_CLIENT; \
89 xmlXPathCastNodeSetToString(result->nodesetval); \
91 xmlXPathFreeObject(result); \
92 error = HTTP_ERROR_SERVER; \
96 xmlXPathFreeObject(result); \
99 #define EXTRACT_BOOLEAN(element, booleanPtr) { \
100 char *string = NULL; \
101 EXTRACT_STRING(element, string); \
103 if (NULL != string) { \
104 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
105 if (NULL == (booleanPtr)) { \
107 error = HTTP_ERROR_SERVER; \
111 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
112 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
114 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
115 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
118 test_asprintf(&message, \
119 "%s value is not valid boolean: %s", \
122 error = HTTP_ERROR_CLIENT; \
131 #define EXTRACT_DATE(element, tmPtr) { \
132 char *string = NULL; \
133 EXTRACT_STRING(element, string); \
134 if (NULL != string) { \
135 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
136 if (NULL == (tmPtr)) { \
138 error = HTTP_ERROR_SERVER; \
141 error = _server_datestring2tm(string, (tmPtr)); \
143 if (error == HTTP_ERROR_CLIENT) { \
144 test_asprintf(&message, "%s value is not a valid date: %s", \
154 /* Following INSERT_* macros expect @error and leave label */
155 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
157 xmlNodePtr node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
158 (xmlChar *) (string)); \
159 if (NULL == node) { \
160 error = HTTP_ERROR_SERVER; \
165 #define INSERT_STRING(parent, element, string) \
166 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
168 #define INSERT_LONGINTPTR(parent, element, longintPtr) { \
169 if ((longintPtr)) { \
170 char *buffer = NULL; \
171 /* FIXME: locale sensitive */ \
172 if (-1 == test_asprintf(&buffer, "%ld", *(longintPtr))) { \
173 error = HTTP_ERROR_SERVER; \
176 INSERT_STRING(parent, element, buffer) \
178 } else { INSERT_STRING(parent, element, NULL) } \
181 #define INSERT_ULONGINTPTR(parent, element, ulongintPtr) { \
182 if ((ulongintPtr)) { \
183 char *buffer = NULL; \
184 /* FIXME: locale sensitive */ \
185 if (-1 == test_asprintf(&buffer, "%lu", *(ulongintPtr))) { \
186 error = HTTP_ERROR_SERVER; \
189 INSERT_STRING(parent, element, buffer) \
191 } else { INSERT_STRING(parent, element, NULL) } \
194 #define INSERT_BOOLEANPTR(parent, element, booleanPtr) { \
195 if (NULL != (booleanPtr)) { \
196 char *buffer = NULL; \
197 buffer = *(booleanPtr) ? "true" : "false"; \
198 INSERT_STRING(parent, element, buffer) \
199 } else { INSERT_STRING(parent, element, NULL) } \
202 #define INSERT_TIMEVALPTR(parent, element, timevalPtr) { \
203 if (NULL != (timevalPtr)) { \
204 char *buffer = NULL; \
205 error = timeval2timestring(timevalPtr, &buffer); \
210 INSERT_STRING(parent, element, buffer); \
213 INSERT_STRING(parent, element, NULL); \
217 #define INSERT_TMPTR(parent, element, tmPtr) { \
218 if (NULL != (tmPtr)) { \
219 char *buffer = NULL; \
220 error = tm2datestring(tmPtr, &buffer); \
225 INSERT_STRING(parent, element, buffer); \
228 INSERT_STRING(parent, element, NULL); \
232 #define INSERT_ELEMENT(child, parent, element) \
234 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
235 if (NULL == (child)) { \
236 error = HTTP_ERROR_SERVER; \
241 /* TODO: These functions (element_exists(), extract_...(), ...) will replace
242 * the macros (ELEMENT_EXISTS, EXTRACT_...). The compiled code will be
243 * smaller, the compilation will be faster. */
245 /* Check an element exists.
246 * @code is a static output ISDS error code
247 * @error_message is a reallocated output ISDS error message
248 * @xpath_ctx is a current XPath context
249 * @element_name is name of an element to check
250 * @allow_multiple is false to require exactly one element. True to require
251 * one or more elements.
252 * @return HTTP_ERROR_SUCCESS or an appropriate error code. */
253 static http_error
element_exists(const char **code
, char **message
,
254 xmlXPathContextPtr xpath_ctx
, const char *element_name
,
255 _Bool allow_multiple
) {
256 xmlXPathObjectPtr result
= NULL
;
258 result
= xmlXPathEvalExpression(BAD_CAST element_name
, xpath_ctx
);
259 if (NULL
== result
) {
260 return HTTP_ERROR_SERVER
;
262 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
263 xmlXPathFreeObject(result
);
265 test_asprintf(message
, "Element %s does not exist", element_name
);
266 return HTTP_ERROR_CLIENT
;
268 if (!allow_multiple
&& result
->nodesetval
->nodeNr
> 1) {
269 xmlXPathFreeObject(result
);
271 test_asprintf(message
, "Multiple %s element", element_name
);
272 return HTTP_ERROR_CLIENT
;
275 xmlXPathFreeObject(result
);
277 return HTTP_ERROR_SUCCESS
;
281 /* Extract @element_name's value as a string.
282 * @code is a static output ISDS error code
283 * @error_message is a reallocated output ISDS error message
284 * @xpath_ctx is a current XPath context
285 * @element_name is name of a element whose child text node to extract
286 * @string is the extraced allocated string value, or NULL if empty or the
287 * element does not exist.
288 * @return HTTP_ERROR_SUCCESS or an appropriate error code. */
289 static http_error
extract_string(const char **code
, char **message
,
290 xmlXPathContextPtr xpath_ctx
, const char *element_name
,
292 http_error error
= HTTP_ERROR_SUCCESS
;
293 xmlXPathObjectPtr result
= NULL
;
296 if (-1 == test_asprintf(&buffer
, "%s/text()", element_name
)) {
297 error
= HTTP_ERROR_SERVER
;
300 result
= xmlXPathEvalExpression(BAD_CAST buffer
, xpath_ctx
);
302 if (NULL
== result
) {
303 error
= HTTP_ERROR_SERVER
;
306 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
307 if (result
->nodesetval
->nodeNr
> 1) {
308 xmlXPathFreeObject(result
);
310 test_asprintf(message
, "Multiple %s element", element_name
);
311 error
= HTTP_ERROR_CLIENT
;
315 xmlXPathCastNodeSetToString(result
->nodesetval
);
317 xmlXPathFreeObject(result
);
318 error
= HTTP_ERROR_SERVER
;
322 xmlXPathFreeObject(result
);
329 /* Checks an @element_name's value is an @expected_value string.
330 * @code is a static output ISDS error code
331 * @error_message is a reallocated output ISDS error message
332 * @xpath_ctx is a current XPath context
333 * @element_name is name of a element to check
334 * @must_exist is true if the @element_name must exist even if @expected_value
336 * @expected_value is an expected string value
337 * @return HTTP_ERROR_SUCCESS if the @element_name element's value is
338 * @expected_value. HTTP_ERROR_CLIENT if not equaled, HTTP_ERROR_SERVER if an
339 * internal error occured. */
340 static http_error
element_equals_string(const char **code
, char **message
,
341 xmlXPathContextPtr xpath_ctx
, const char *element_name
,
342 _Bool must_exist
, const char *expected_value
) {
343 http_error error
= HTTP_ERROR_SUCCESS
;
347 error
= element_exists(code
, message
, xpath_ctx
, element_name
, 0);
348 if (HTTP_ERROR_SUCCESS
!= error
)
352 error
= extract_string(code
, message
, xpath_ctx
, element_name
, &string
);
353 if (HTTP_ERROR_SUCCESS
!= error
)
356 if (NULL
!= expected_value
) {
357 if (NULL
== string
) {
359 test_asprintf(message
, "Empty %s element", element_name
);
360 error
= HTTP_ERROR_CLIENT
;
363 if (xmlStrcmp(BAD_CAST expected_value
, BAD_CAST string
)) {
365 test_asprintf(message
,
366 "Unexpected %s element value: expected=`%s', got=`%s'",
367 element_name
, expected_value
, string
);
368 error
= HTTP_ERROR_CLIENT
;
372 if (NULL
!= string
&& *string
!= '\0') {
374 test_asprintf(message
,
375 "Unexpected %s element value: "
376 "expected empty string, got=`%s'",
377 element_name
, string
);
378 error
= HTTP_ERROR_CLIENT
;
389 /* Checks an @element_name's value is an @expected_value integer.
390 * @code is a static output ISDS error code
391 * @error_message is a reallocated output ISDS error message
392 * @xpath_ctx is a current XPath context
393 * @element_name is name of a element to check
394 * @must_exist is true if the @element_name must exist even if @expected_value
396 * @expected_value is an expected integer value.
397 * @return HTTP_ERROR_SUCCESS if the @element_name element's value is
398 * @expected_value. HTTP_ERROR_CLIENT if not equaled, HTTP_ERROR_SERVER if an
399 * internal error occured. */
400 static http_error
element_equals_integer(const char **code
, char **message
,
401 xmlXPathContextPtr xpath_ctx
, const char *element_name
,
402 _Bool must_exist
, const long int *expected_value
) {
403 http_error error
= HTTP_ERROR_SUCCESS
;
409 error
= element_exists(code
, message
, xpath_ctx
, element_name
, 0);
410 if (HTTP_ERROR_SUCCESS
!= error
)
414 error
= extract_string(code
, message
, xpath_ctx
, element_name
, &string
);
415 if (HTTP_ERROR_SUCCESS
!= error
)
418 if (NULL
!= expected_value
) {
419 if (NULL
== string
) {
421 test_asprintf(message
, "Empty %s element", element_name
);
422 error
= HTTP_ERROR_CLIENT
;
425 number
= strtol(string
, &endptr
, 10);
426 if (*endptr
!= '\0') {
428 test_asprintf(message
,
429 "%s element value is not a valid integer: %s",
430 element_name
, string
);
431 error
= HTTP_ERROR_CLIENT
;
434 if (number
== LONG_MIN
|| number
== LONG_MAX
) { \
436 test_asprintf(message
, \
437 "%s element value is out of range of long int: %s",
438 element_name
, string
);
439 error
= HTTP_ERROR_SERVER
;
442 free(string
); string
= NULL
;
443 if (number
!= *expected_value
) {
445 test_asprintf(message
,
446 "Unexpected %s element value: expected=`%ld', got=`%ld'",
447 element_name
, *expected_value
, number
);
448 error
= HTTP_ERROR_CLIENT
;
452 if (NULL
!= string
&& *string
!= '\0') {
454 test_asprintf(message
,
455 "Unexpected %s element value: expected no text node, got=`%s'",
456 element_name
, string
);
457 error
= HTTP_ERROR_CLIENT
;
468 /* Checks an @element_name's value is an @expected_value boolean.
469 * @code is a static output ISDS error code
470 * @error_message is an reallocated output ISDS error message
471 * @xpath_ctx is a current XPath context
472 * @element_name is name of a element to check
473 * @must_exist is true if the @element_name must exist even if @expected_value
475 * @expected_value is an expected boolean value
476 * @return HTTP_ERROR_SUCCESS if the @element_name element's value is
477 * @expected_value. HTTP_ERROR_CLIENT if not equaled, HTTP_ERROR_SERVER if an
478 * internal error occured. */
479 static http_error
element_equals_boolean(const char **code
, char **message
,
480 xmlXPathContextPtr xpath_ctx
, const char *element_name
,
481 _Bool must_exist
, const _Bool
*expected_value
) {
482 http_error error
= HTTP_ERROR_SUCCESS
;
487 error
= element_exists(code
, message
, xpath_ctx
, element_name
, 0);
488 if (HTTP_ERROR_SUCCESS
!= error
)
492 error
= extract_string(code
, message
, xpath_ctx
, element_name
, &string
);
493 if (HTTP_ERROR_SUCCESS
!= error
)
496 if (NULL
!= expected_value
) {
497 if (NULL
== string
) {
499 test_asprintf(message
, "Empty %s element", element_name
);
500 error
= HTTP_ERROR_CLIENT
;
503 if (!xmlStrcmp((xmlChar
*)string
, BAD_CAST
"true") ||
504 !xmlStrcmp((xmlChar
*)string
, BAD_CAST
"1"))
506 else if (!xmlStrcmp((xmlChar
*)string
, BAD_CAST
"false") ||
507 !xmlStrcmp((xmlChar
*)string
, BAD_CAST
"0"))
511 test_asprintf(message
,
512 "%s element value is not a valid boolean: %s",
513 element_name
, string
);
514 error
= HTTP_ERROR_CLIENT
;
517 if (*expected_value
!= value
) {
519 test_asprintf(message
,
520 "Unexpected %s element value: expected=%d, got=%d",
521 element_name
, *expected_value
, string
);
522 error
= HTTP_ERROR_CLIENT
;
526 if (NULL
!= string
&& *string
!= '\0') {
528 test_asprintf(message
,
529 "Unexpected %s element value: "
530 "expected empty string, got=`%s'",
531 element_name
, string
);
532 error
= HTTP_ERROR_CLIENT
;
543 /* Insert dmStatus or similar subtree
544 * @parent is element to insert to
545 * @dm is true for dmStatus, otherwise dbStatus
546 * @code is status code as string
547 * @message is UTF-8 encoded message
548 * @db_ref_number is optinal reference number propagated if not @dm
549 * @return 0 on success, otherwise non-0. */
550 static http_error
insert_isds_status(xmlNodePtr parent
, _Bool dm
,
551 const xmlChar
*code
, const xmlChar
*message
,
552 const xmlChar
*db_ref_number
) {
553 http_error error
= HTTP_ERROR_SUCCESS
;
556 if (NULL
== code
|| NULL
== message
) {
557 error
= HTTP_ERROR_SERVER
;
561 INSERT_ELEMENT(status
, parent
, (dm
) ? "dmStatus" : "dbStatus");
562 INSERT_STRING(status
, (dm
) ? "dmStatusCode" : "dbStatusCode", code
);
563 INSERT_STRING(status
, (dm
) ? "dmStatusMessage" : "dbStatusMessage", message
);
564 if (!dm
&& NULL
!= db_ref_number
) {
565 INSERT_STRING(status
, "dbStatusRefNumber", db_ref_number
);
573 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
574 static http_error
tm2datestring(const struct tm
*time
, char **string
) {
575 if (NULL
== time
|| NULL
== string
) return HTTP_ERROR_SERVER
;
577 if (-1 == test_asprintf(string
, "%d-%02d-%02d",
578 time
->tm_year
+ 1900, time
->tm_mon
+ 1, time
->tm_mday
))
579 return HTTP_ERROR_SERVER
;
581 return HTTP_ERROR_SUCCESS
;
585 /* Convert struct timeval *@time to UTF-8 ISO 8601 date-time @string. It
586 * respects the @time microseconds too. */
587 static http_error
timeval2timestring(const struct timeval
*time
,
591 if (!time
|| !string
) return HTTP_ERROR_SERVER
;
593 if (!gmtime_r(&time
->tv_sec
, &broken
)) return HTTP_ERROR_SERVER
;
594 if (time
->tv_usec
< 0 || time
->tv_usec
> 999999) return HTTP_ERROR_SERVER
;
596 /* TODO: small negative year should be formatted as "-0012". This is not
597 * true for glibc "%04d". We should implement it.
598 * time->tv_usec type is su_seconds_t which is required to be signed
599 * integer to accomodate values from range [-1, 1000000].
600 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
601 if (-1 == test_asprintf(string
,
602 "%04d-%02d-%02dT%02d:%02d:%02d.%06jd",
603 broken
.tm_year
+ 1900, broken
.tm_mon
+ 1, broken
.tm_mday
,
604 broken
.tm_hour
, broken
.tm_min
, broken
.tm_sec
,
605 (intmax_t)time
->tv_usec
))
606 return HTTP_ERROR_SERVER
;
608 return HTTP_ERROR_SUCCESS
;
612 /* Compare dates represented by pointer to struct tm.
613 * @return 0 if equalued, non-0 otherwise. */
614 static int datecmp(const struct tm
*a
, const struct tm
*b
) {
615 if (NULL
== a
&& b
== NULL
) return 0;
616 if ((NULL
== a
&& b
!= NULL
) || (NULL
!= a
&& b
== NULL
)) return 1;
617 if (a
->tm_year
!= b
->tm_year
) return 1;
618 if (a
->tm_mon
!= b
->tm_mon
) return 1;
619 if (a
->tm_mday
!= b
->tm_mday
) return 1;
624 /* Implement DummyOperation */
625 static http_error
service_DummyOperation(
626 xmlXPathContextPtr xpath_ctx
,
627 xmlNodePtr isds_response
,
628 const void *arguments
) {
632 return insert_isds_status(isds_response
, 1, BAD_CAST
"0000",
633 BAD_CAST
"Success", NULL
);
637 /* Implement Re-signISDSDocument.
638 * It sends document from request back.
639 * @arguments is pointer to struct arguments_DS_Dz_ResignISDSDocument */
640 static http_error
service_ResignISDSDocument(
641 xmlXPathContextPtr xpath_ctx
,
642 xmlNodePtr isds_response
,
643 const void *arguments
) {
644 http_error error
= HTTP_ERROR_SUCCESS
;
645 const char *code
= "9999";
646 char *message
= NULL
;
647 const struct arguments_DS_Dz_ResignISDSDocument
*configuration
=
648 (const struct arguments_DS_Dz_ResignISDSDocument
*)arguments
;
651 if (NULL
== configuration
|| NULL
== configuration
->status_code
||
652 NULL
== configuration
->status_message
) {
653 error
= HTTP_ERROR_SERVER
;
657 EXTRACT_STRING("isds:dmDoc", data
);
659 message
= strdup("Missing isds:dmDoc");
660 error
= HTTP_ERROR_CLIENT
;
665 /* dmResultDoc is mandatory in response */
666 if (xmlStrcmp(BAD_CAST configuration
->status_code
, BAD_CAST
"0000")) {
670 INSERT_STRING(isds_response
, "dmResultDoc", data
);
672 if (configuration
->valid_to
!= NULL
) {
673 error
= tm2datestring(configuration
->valid_to
, &data
);
675 message
= strdup("Could not format date");
678 INSERT_STRING(isds_response
, "dmValidTo", data
);
681 code
= configuration
->status_code
;
682 message
= strdup(configuration
->status_message
);
685 if (HTTP_ERROR_SERVER
!= error
) {
686 http_error next_error
= insert_isds_status(isds_response
, 1,
687 BAD_CAST code
, BAD_CAST message
, NULL
);
688 if (HTTP_ERROR_SUCCESS
!= next_error
) error
= next_error
;
696 /* Implement EraseMessage.
697 * @arguments is pointer to struct arguments_DS_DsManage_ChangeISDSPassword */
698 static http_error
service_EraseMessage(
699 xmlXPathContextPtr xpath_ctx
,
700 xmlNodePtr isds_response
,
701 const void *arguments
) {
702 http_error error
= HTTP_ERROR_SUCCESS
;
703 char *code
= "9999", *message
= NULL
;
704 const struct arguments_DS_Dx_EraseMessage
*configuration
=
705 (const struct arguments_DS_Dx_EraseMessage
*)arguments
;
706 char *message_id
= NULL
;
707 _Bool
*incoming
= NULL
;
709 if (NULL
== configuration
|| NULL
== configuration
->message_id
) {
710 error
= HTTP_ERROR_SERVER
;
714 EXTRACT_STRING("isds:dmID", message_id
);
715 if (NULL
== message_id
) {
716 message
= strdup("Missing isds:dmID");
717 error
= HTTP_ERROR_CLIENT
;
720 EXTRACT_BOOLEAN("isds:dmIncoming", incoming
);
721 if (NULL
== incoming
) {
722 message
= strdup("Missing isds:dmIncoming");
723 error
= HTTP_ERROR_CLIENT
;
727 if (xmlStrcmp((const xmlChar
*) configuration
->message_id
,
728 (const xmlChar
*) message_id
)) {
730 message
= strdup("Message is not in the long term storage");
731 error
= HTTP_ERROR_CLIENT
;
734 if (configuration
->incoming
!= *incoming
) {
736 message
= strdup("Message direction mismatches");
737 error
= HTTP_ERROR_CLIENT
;
742 message
= strdup("Success");
744 if (HTTP_ERROR_SERVER
!= error
) {
745 http_error next_error
= insert_isds_status(isds_response
, 1,
746 BAD_CAST code
, BAD_CAST message
, NULL
);
747 if (HTTP_ERROR_SUCCESS
!= next_error
) error
= next_error
;
756 /* Insert list of credit info as XSD:tCiRecord XML tree.
757 * @isds_response is XML node with the response
758 * @history is list of struct server_credit_event. If NULL, no ciRecord XML
759 * subtree will be created. */
760 static http_error
insert_ciRecords(xmlNodePtr isds_response
,
761 const struct server_list
*history
) {
762 http_error error
= HTTP_ERROR_SUCCESS
;
763 xmlNodePtr records
, record
;
765 if (NULL
== isds_response
) return HTTP_ERROR_SERVER
;
766 if (NULL
== history
) return HTTP_ERROR_SUCCESS
;
768 INSERT_ELEMENT(records
, isds_response
, "ciRecords");
769 for (const struct server_list
*item
= history
; NULL
!= item
;
771 const struct server_credit_event
*event
=
772 (struct server_credit_event
*)item
->data
;
774 INSERT_ELEMENT(record
, records
, "ciRecord");
775 if (NULL
== event
) continue;
777 INSERT_TIMEVALPTR(record
, "ciEventTime", event
->time
);
778 switch(event
->type
) {
779 case SERVER_CREDIT_CHARGED
:
780 INSERT_STRING(record
, "ciEventType", "1");
781 INSERT_STRING(record
, "ciTransID",
782 event
->details
.charged
.transaction
);
784 case SERVER_CREDIT_DISCHARGED
:
785 INSERT_STRING(record
, "ciEventType", "2");
786 INSERT_STRING(record
, "ciTransID",
787 event
->details
.discharged
.transaction
);
789 case SERVER_CREDIT_MESSAGE_SENT
:
790 INSERT_STRING(record
, "ciEventType", "3");
791 INSERT_STRING(record
, "ciRecipientID",
792 event
->details
.message_sent
.recipient
);
793 INSERT_STRING(record
, "ciPDZID",
794 event
->details
.message_sent
.message_id
);
796 case SERVER_CREDIT_STORAGE_SET
:
797 INSERT_STRING(record
, "ciEventType", "4");
798 INSERT_LONGINTPTR(record
, "ciNewCapacity",
799 &event
->details
.storage_set
.new_capacity
);
800 INSERT_TMPTR(record
, "ciNewFrom",
801 event
->details
.storage_set
.new_valid_from
);
802 INSERT_TMPTR(record
, "ciNewTo",
803 event
->details
.storage_set
.new_valid_to
);
804 INSERT_LONGINTPTR(record
, "ciOldCapacity",
805 event
->details
.storage_set
.old_capacity
);
806 INSERT_TMPTR(record
, "ciOldFrom",
807 event
->details
.storage_set
.old_valid_from
);
808 INSERT_TMPTR(record
, "ciOldTo",
809 event
->details
.storage_set
.old_valid_to
);
810 INSERT_STRING(record
, "ciDoneBy",
811 event
->details
.storage_set
.initiator
);
813 case SERVER_CREDIT_EXPIRED
:
814 INSERT_STRING(record
, "ciEventType", "5");
817 error
= HTTP_ERROR_SERVER
;
820 INSERT_LONGINTPTR(record
, "ciCreditChange", &event
->credit_change
);
821 INSERT_LONGINTPTR(record
, "ciCreditAfter", &event
->new_credit
);
830 /* Implement DataBoxCreditInfo.
831 * @arguments is pointer to struct arguments_DS_df_DataBoxCreditInfo */
832 static http_error
service_DataBoxCreditInfo(
833 xmlXPathContextPtr xpath_ctx
,
834 xmlNodePtr isds_response
,
835 const void *arguments
) {
836 http_error error
= HTTP_ERROR_SUCCESS
;
837 const char *code
= "9999";
838 char *message
= NULL
;
839 const struct arguments_DS_df_DataBoxCreditInfo
*configuration
=
840 (const struct arguments_DS_df_DataBoxCreditInfo
*)arguments
;
842 struct tm
*from_date
= NULL
, *to_date
= NULL
;
844 if (NULL
== configuration
|| NULL
== configuration
->status_code
||
845 NULL
== configuration
->status_message
) {
846 error
= HTTP_ERROR_SERVER
;
850 EXTRACT_STRING("isds:dbID", box_id
);
851 if (NULL
== box_id
) {
852 message
= strdup("Missing isds:dbID");
853 error
= HTTP_ERROR_CLIENT
;
856 if (NULL
!= configuration
->box_id
&&
857 xmlStrcmp(BAD_CAST configuration
->box_id
,
860 message
= strdup("Unexpected isds:dbID value");
861 error
= HTTP_ERROR_CLIENT
;
865 ELEMENT_EXISTS("isds:ciFromDate", 0);
866 EXTRACT_DATE("isds:ciFromDate", from_date
);
867 if (datecmp(configuration
->from_date
, from_date
)) {
869 message
= strdup("Unexpected isds:ciFromDate value");
870 error
= HTTP_ERROR_CLIENT
;
874 ELEMENT_EXISTS("isds:ciTodate", 0);
875 EXTRACT_DATE("isds:ciTodate", to_date
);
876 if (datecmp(configuration
->to_date
, to_date
)) {
878 message
= strdup("Unexpected isds:ciTodate value");
879 error
= HTTP_ERROR_CLIENT
;
883 INSERT_LONGINTPTR(isds_response
, "currentCredit",
884 &configuration
->current_credit
);
885 INSERT_STRING(isds_response
, "notifEmail", configuration
->email
);
886 if ((error
= insert_ciRecords(isds_response
, configuration
->history
))) {
890 code
= configuration
->status_code
;
891 message
= strdup(configuration
->status_message
);
893 if (HTTP_ERROR_SERVER
!= error
) {
894 http_error next_error
= insert_isds_status(isds_response
, 0,
895 BAD_CAST code
, BAD_CAST message
, NULL
);
896 if (HTTP_ERROR_SUCCESS
!= next_error
) error
= next_error
;
906 /* Insert list of fulltext search results as XSD:tdbResultsArray XML tree.
907 * @isds_response is XML node with the response
908 * @results is list of struct server_db_result *.
909 * @create_empty_root is true to create dbResults element even if @results is
911 static http_error
insert_tdbResultsArray(xmlNodePtr isds_response
,
912 const struct server_list
*results
, _Bool create_empty_root
) {
913 http_error error
= HTTP_ERROR_SUCCESS
;
914 xmlNodePtr root
, entry
;
916 if (NULL
== isds_response
) return HTTP_ERROR_SERVER
;
918 if (NULL
!= results
|| create_empty_root
)
919 INSERT_ELEMENT(root
, isds_response
, "dbResults");
921 if (NULL
== results
) return HTTP_ERROR_SUCCESS
;
923 for (const struct server_list
*item
= results
; NULL
!= item
;
925 const struct server_db_result
*result
=
926 (struct server_db_result
*)item
->data
;
928 INSERT_ELEMENT(entry
, root
, "dbResult");
929 if (NULL
== result
) continue;
931 INSERT_STRING(entry
, "dbID", result
->id
);
932 INSERT_STRING(entry
, "dbType", result
->type
);
933 INSERT_STRING(entry
, "dbName", result
->name
);
934 INSERT_STRING(entry
, "dbAddress", result
->address
);
935 INSERT_TMPTR(entry
, "dbBiDate", result
->birth_date
);
936 INSERT_STRING(entry
, "dbICO", result
->ic
);
937 INSERT_BOOLEANPTR(entry
, "dbEffectiveOVM", &result
->ovm
);
938 INSERT_STRING(entry
, "dbSendOptions", result
->send_options
);
946 /* Implement ISDSSearch2.
947 * @arguments is pointer to struct arguments_DS_df_ISDSSearch2 */
948 static http_error
service_ISDSSearch2(
949 xmlXPathContextPtr xpath_ctx
,
950 xmlNodePtr isds_response
,
951 const void *arguments
) {
952 http_error error
= HTTP_ERROR_SUCCESS
;
953 const char *code
= "9999";
954 char *message
= NULL
;
955 const struct arguments_DS_df_ISDSSearch2
*configuration
=
956 (const struct arguments_DS_df_ISDSSearch2
*)arguments
;
959 if (NULL
== configuration
|| NULL
== configuration
->status_code
||
960 NULL
== configuration
->status_message
) {
961 error
= HTTP_ERROR_SERVER
;
966 EXTRACT_STRING("isds:searchText", string
);
967 if (NULL
== string
) {
968 message
= strdup("Missing or empty isds:searchText");
969 error
= HTTP_ERROR_CLIENT
;
972 if (NULL
!= configuration
->search_text
&&
973 xmlStrcmp(BAD_CAST configuration
->search_text
,
976 message
= strdup("Unexpected isds:searchText value");
977 error
= HTTP_ERROR_CLIENT
;
980 free(string
); string
= NULL
;
982 error
= element_equals_string(&code
, &message
, xpath_ctx
,
983 "isds:searchType", 1, configuration
->search_type
);
984 if (error
) goto leave
;
986 error
= element_equals_string(&code
, &message
, xpath_ctx
,
987 "isds:searchScope", 1, configuration
->search_scope
);
988 if (error
) goto leave
;
990 error
= element_equals_integer(&code
, &message
, xpath_ctx
,
991 "isds:page", 1, configuration
->search_page_number
);
992 if (error
) goto leave
;
994 error
= element_equals_integer(&code
, &message
, xpath_ctx
,
995 "isds:pageSize", 1, configuration
->search_page_size
);
996 if (error
) goto leave
;
998 error
= element_equals_boolean(&code
, &message
, xpath_ctx
,
999 "isds:highlighting", 0, configuration
->search_highlighting_value
);
1000 if (error
) goto leave
;
1002 /* Build response */
1003 if (NULL
!= configuration
->total_count
)
1004 INSERT_ULONGINTPTR(isds_response
, "totalCount",
1005 configuration
->total_count
);
1006 if (NULL
!= configuration
->current_count
)
1007 INSERT_ULONGINTPTR(isds_response
, "currentCount",
1008 configuration
->current_count
);
1009 if (NULL
!= configuration
->position
)
1010 INSERT_ULONGINTPTR(isds_response
, "position",
1011 configuration
->position
);
1012 if (NULL
!= configuration
->last_page
)
1013 INSERT_BOOLEANPTR(isds_response
, "lastPage",
1014 configuration
->last_page
);
1015 if ((error
= insert_tdbResultsArray(isds_response
, configuration
->results
,
1016 configuration
->results_exists
))) {
1020 code
= configuration
->status_code
;
1021 message
= strdup(configuration
->status_message
);
1024 if (HTTP_ERROR_SERVER
!= error
) {
1025 http_error next_error
= insert_isds_status(isds_response
, 0,
1026 BAD_CAST code
, BAD_CAST message
, NULL
);
1027 if (HTTP_ERROR_SUCCESS
!= next_error
) error
= next_error
;
1035 /* Common part for ChangeISDSPassword and ChangePasswordOTP.
1036 * @code is output pointer to static string
1037 * @pass_message is output pointer to auto-allocated string
1038 * @arguments is pointer to struct arguments_DS_DsManage_ChangeISDSPassword */
1039 static http_error
check_passwd(
1040 const char *username
, const char *current_password
,
1041 xmlXPathContextPtr xpath_ctx
,
1042 char **code
, char **pass_message
) {
1043 http_error error
= HTTP_ERROR_SUCCESS
;
1044 char *message
= NULL
;
1045 char *old_password
= NULL
, *new_password
= NULL
;
1048 if (NULL
== username
|| NULL
== current_password
||
1049 NULL
== code
|| NULL
== pass_message
) {
1050 return HTTP_ERROR_SERVER
;
1057 EXTRACT_STRING("isds:dbOldPassword", old_password
);
1058 if (NULL
== old_password
) {
1059 message
= strdup("Empty isds:dbOldPassword");
1060 error
= HTTP_ERROR_CLIENT
;
1063 EXTRACT_STRING("isds:dbNewPassword", new_password
);
1064 if (NULL
== new_password
) {
1065 message
= strdup("Empty isds:dbOldPassword");
1066 error
= HTTP_ERROR_CLIENT
;
1070 /* Check defined cases */
1071 if (strcmp(current_password
, old_password
)) {
1073 message
= strdup("Bad current password");
1074 error
= HTTP_ERROR_CLIENT
;
1078 length
= strlen(new_password
);
1080 if (length
< 8 || length
> 32) {
1082 message
= strdup("Too short or too long");
1083 error
= HTTP_ERROR_CLIENT
;
1088 const char lower
[] = "abcdefghijklmnopqrstuvwxyz";
1089 const char upper
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1090 const char digit
[] = "0123456789";
1091 const char special
[] = "!#$%&()*+,-.:=?@[]_{}|~";
1092 _Bool has_lower
= 0, has_upper
= 0, has_digit
=0;
1094 for (size_t i
= 0; i
< length
; i
++) {
1095 if (NULL
!= strchr(lower
, new_password
[i
]))
1097 else if (NULL
!= strchr(upper
, new_password
[i
]))
1099 else if (NULL
!= strchr(digit
, new_password
[i
]))
1101 else if (NULL
== strchr(special
, new_password
[i
])) {
1103 message
= strdup("Password contains forbidden character");
1104 error
= HTTP_ERROR_CLIENT
;
1109 if (!has_lower
|| !has_upper
|| !has_digit
) {
1111 message
= strdup("Password does not contain lower cased letter, "
1112 "upper cased letter and a digit");
1113 error
= HTTP_ERROR_CLIENT
;
1118 if (!strcmp(old_password
, new_password
)) {
1120 message
= strdup("New password same as current one");
1121 error
= HTTP_ERROR_CLIENT
;
1125 if (NULL
!= strstr(new_password
, username
)) {
1127 message
= strdup("New password contains user ID");
1128 error
= HTTP_ERROR_CLIENT
;
1132 for (size_t i
= 0; i
< length
- 2; i
++) {
1133 if (new_password
[i
] == new_password
[i
+1] &&
1134 new_password
[i
] == new_password
[i
+2]) {
1136 message
= strdup("Password contains sequence "
1137 "of three identical characters");
1138 error
= HTTP_ERROR_CLIENT
;
1144 const char *forbidden_prefix
[] = { "qwert", "asdgf", "12345" };
1145 for (size_t i
= 0; i
< sizeof(forbidden_prefix
)/sizeof(*forbidden_prefix
);
1147 if (!strncmp(new_password
, forbidden_prefix
[i
],
1148 strlen(forbidden_prefix
[i
]))) {
1150 message
= strdup("Password has forbidden prefix");
1151 error
= HTTP_ERROR_CLIENT
;
1158 message
= strdup("Success");
1162 *pass_message
= message
;
1167 /* Implement ChangeISDSPassword.
1168 * @arguments is pointer to struct arguments_DS_DsManage_ChangeISDSPassword */
1169 static http_error
service_ChangeISDSPassword(
1170 xmlXPathContextPtr xpath_ctx
,
1171 xmlNodePtr isds_response
,
1172 const void *arguments
) {
1173 http_error error
= HTTP_ERROR_SUCCESS
;
1174 char *code
= "9999", *message
= NULL
;
1175 const struct arguments_DS_DsManage_ChangeISDSPassword
*configuration
=
1176 (const struct arguments_DS_DsManage_ChangeISDSPassword
*)arguments
;
1178 if (NULL
== configuration
|| NULL
== configuration
->username
||
1179 NULL
== configuration
->current_password
) {
1180 error
= HTTP_ERROR_SERVER
;
1184 /* Check for common password rules */
1185 error
= check_passwd(
1186 configuration
->username
, configuration
->current_password
,
1187 xpath_ctx
, &code
, &message
);
1190 if (HTTP_ERROR_SERVER
!= error
) {
1191 http_error next_error
= insert_isds_status(isds_response
, 0,
1192 BAD_CAST code
, BAD_CAST message
, NULL
);
1193 if (HTTP_ERROR_SUCCESS
!= next_error
) error
= next_error
;
1200 /* Implement ChangePasswordOTP.
1201 * @arguments is pointer to struct
1202 * arguments_asws_changePassword_ChangePasswordOTP */
1203 static http_error
service_ChangePasswordOTP(
1204 xmlXPathContextPtr xpath_ctx
,
1205 xmlNodePtr isds_response
,
1206 const void *arguments
) {
1207 http_error error
= HTTP_ERROR_SUCCESS
;
1208 char *code
= "9999", *message
= NULL
;
1209 const struct arguments_asws_changePassword_ChangePasswordOTP
*configuration
1210 = (const struct arguments_asws_changePassword_ChangePasswordOTP
*)
1212 char *method
= NULL
;
1214 if (NULL
== configuration
|| NULL
== configuration
->username
||
1215 NULL
== configuration
->current_password
) {
1216 error
= HTTP_ERROR_SERVER
;
1220 /* Chek for OTP method */
1221 EXTRACT_STRING("isds:dbOTPType", method
);
1222 if (NULL
== method
) {
1223 message
= strdup("Empty isds:dbOTPType");
1224 error
= HTTP_ERROR_CLIENT
;
1227 if ((configuration
->method
== AUTH_OTP_HMAC
&& strcmp(method
, "HOTP")) ||
1228 (configuration
->method
== AUTH_OTP_TIME
&& strcmp(method
, "TOTP"))) {
1229 message
= strdup("isds:dbOTPType does not match OTP method");
1230 error
= HTTP_ERROR_CLIENT
;
1234 /* Check for common password rules */
1235 error
= check_passwd(
1236 configuration
->username
, configuration
->current_password
,
1237 xpath_ctx
, &code
, &message
);
1240 if (HTTP_ERROR_SERVER
!= error
) {
1241 http_error next_error
= insert_isds_status(isds_response
, 0,
1242 BAD_CAST code
, BAD_CAST message
,
1243 BAD_CAST configuration
->reference_number
);
1244 if (HTTP_ERROR_SUCCESS
!= next_error
) error
= next_error
;
1252 /* Implement SendSMSCode.
1253 * @arguments is pointer to struct arguments_asws_changePassword_SendSMSCode */
1254 static http_error
service_SendSMSCode(
1255 xmlXPathContextPtr xpath_ctx
,
1256 xmlNodePtr isds_response
,
1257 const void *arguments
) {
1258 const struct arguments_asws_changePassword_SendSMSCode
*configuration
1259 = (const struct arguments_asws_changePassword_SendSMSCode
*)
1263 if (NULL
== configuration
|| NULL
== configuration
->status_code
||
1264 NULL
== configuration
->status_message
) {
1265 return HTTP_ERROR_SERVER
;
1268 return insert_isds_status(isds_response
, 0,
1269 BAD_CAST configuration
->status_code
,
1270 BAD_CAST configuration
->status_message
,
1271 BAD_CAST configuration
->reference_number
);
1275 /* List of implemented services */
1276 static struct service services
[] = {
1277 { SERVICE_DS_Dz_DummyOperation
,
1278 "DS/dz", BAD_CAST ISDS_NS
, BAD_CAST
"DummyOperation",
1279 service_DummyOperation
},
1280 { SERVICE_DS_Dz_ResignISDSDocument
,
1281 "DS/dz", BAD_CAST ISDS_NS
, BAD_CAST
"Re-signISDSDocument",
1282 service_ResignISDSDocument
},
1283 { SERVICE_DS_df_DataBoxCreditInfo
,
1284 "DS/df", BAD_CAST ISDS_NS
, BAD_CAST
"DataBoxCreditInfo",
1285 service_DataBoxCreditInfo
},
1286 { SERVICE_DS_df_ISDSSearch2
,
1287 "DS/df", BAD_CAST ISDS_NS
, BAD_CAST
"ISDSSearch2",
1288 service_ISDSSearch2
},
1289 { SERVICE_DS_DsManage_ChangeISDSPassword
,
1290 "DS/DsManage", BAD_CAST ISDS_NS
, BAD_CAST
"ChangeISDSPassword",
1291 service_ChangeISDSPassword
},
1292 { SERVICE_DS_Dx_EraseMessage
,
1293 "DS/dx", BAD_CAST ISDS_NS
, BAD_CAST
"EraseMessage",
1294 service_EraseMessage
},
1295 { SERVICE_asws_changePassword_ChangePasswordOTP
,
1296 "/asws/changePassword", BAD_CAST OISDS_NS
, BAD_CAST
"ChangePasswordOTP",
1297 service_ChangePasswordOTP
},
1298 { SERVICE_asws_changePassword_SendSMSCode
,
1299 "/asws/changePassword", BAD_CAST OISDS_NS
, BAD_CAST
"SendSMSCode",
1300 service_SendSMSCode
},
1304 /* Makes known all relevant namespaces to given XPath context
1305 * @xpath_ctx is XPath context
1306 * @otp_ns selects name space for the request and response know as "isds".
1307 * Use true for OTP-authenticated password change services, otherwise false.
1308 * @message_ns selects proper message name space. Unsigned and signed
1309 * messages and delivery info's differ in prefix and URI.
1310 * @return 0 in success, otherwise not 0. */
1311 static int register_namespaces(xmlXPathContextPtr xpath_ctx
,
1312 const _Bool otp_ns
, const message_ns_type message_ns
) {
1313 const xmlChar
*service_namespace
= NULL
;
1314 const xmlChar
*message_namespace
= NULL
;
1316 if (!xpath_ctx
) return -1;
1319 service_namespace
= BAD_CAST OISDS_NS
;
1321 service_namespace
= BAD_CAST ISDS_NS
;
1324 switch(message_ns
) {
1326 message_namespace
= BAD_CAST ISDS1_NS
; break;
1327 case MESSAGE_NS_UNSIGNED
:
1328 message_namespace
= BAD_CAST ISDS_NS
; break;
1329 case MESSAGE_NS_SIGNED_INCOMING
:
1330 message_namespace
= BAD_CAST SISDS_INCOMING_NS
; break;
1331 case MESSAGE_NS_SIGNED_OUTGOING
:
1332 message_namespace
= BAD_CAST SISDS_OUTGOING_NS
; break;
1333 case MESSAGE_NS_SIGNED_DELIVERY
:
1334 message_namespace
= BAD_CAST SISDS_DELIVERY_NS
; break;
1339 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"soap", BAD_CAST SOAP_NS
))
1341 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"isds", service_namespace
))
1343 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"sisds", message_namespace
))
1345 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"xs", BAD_CAST SCHEMA_NS
))
1347 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"deposit", BAD_CAST DEPOSIT_NS
))
1353 /* Parse soap request, pass it to service endpoint and respond to it.
1354 * It sends final HTTP response. */
1355 void soap(const struct http_connection
*connection
,
1356 const struct service_configuration
*configuration
,
1357 const void *request
, size_t request_length
, const char *end_point
) {
1358 xmlDocPtr request_doc
= NULL
;
1359 xmlXPathContextPtr xpath_ctx
= NULL
;
1360 xmlXPathObjectPtr request_soap_body
= NULL
;
1361 xmlNodePtr isds_request
= NULL
; /* pointer only */
1362 _Bool service_handled
= 0, service_passed
= 0;
1363 xmlDocPtr response_doc
= NULL
;
1364 xmlNodePtr response_soap_envelope
= NULL
, response_soap_body
= NULL
,
1365 isds_response
= NULL
;
1366 xmlNsPtr soap_ns
= NULL
, isds_ns
= NULL
;
1367 char *response_name
= NULL
;
1368 xmlBufferPtr http_response_body
= NULL
;
1369 xmlSaveCtxtPtr save_ctx
= NULL
;
1372 if (NULL
== configuration
) {
1373 http_send_response_500(connection
,
1374 "Second argument of soap() is NULL");
1378 if (NULL
== request
|| request_length
== 0) {
1379 http_send_response_400(connection
, "Client sent empty body");
1383 request_doc
= xmlParseMemory(request
, request_length
);
1384 if (NULL
== request_doc
) {
1385 http_send_response_400(connection
, "Client sent invalid XML document");
1389 xpath_ctx
= xmlXPathNewContext(request_doc
);
1390 if (NULL
== xpath_ctx
) {
1391 xmlFreeDoc(request_doc
);
1392 http_send_response_500(connection
, "Could not create XPath context");
1396 if (register_namespaces(xpath_ctx
, 0, MESSAGE_NS_UNSIGNED
)) {
1397 xmlXPathFreeContext(xpath_ctx
);
1398 xmlFreeDoc(request_doc
);
1399 http_send_response_500(connection
,
1400 "Could not register name spaces to the XPath context");
1405 request_soap_body
= xmlXPathEvalExpression(
1406 BAD_CAST
"/soap:Envelope/soap:Body", xpath_ctx
);
1407 if (NULL
== request_soap_body
) {
1408 xmlXPathFreeContext(xpath_ctx
);
1409 xmlFreeDoc(request_doc
);
1410 http_send_response_400(connection
, "Client sent invalid SOAP request");
1413 if (xmlXPathNodeSetIsEmpty(request_soap_body
->nodesetval
)) {
1414 xmlXPathFreeObject(request_soap_body
);
1415 xmlXPathFreeContext(xpath_ctx
);
1416 xmlFreeDoc(request_doc
);
1417 http_send_response_400(connection
,
1418 "SOAP request does not contain SOAP Body element");
1421 if (request_soap_body
->nodesetval
->nodeNr
> 1) {
1422 xmlXPathFreeObject(request_soap_body
);
1423 xmlXPathFreeContext(xpath_ctx
);
1424 xmlFreeDoc(request_doc
);
1425 http_send_response_400(connection
,
1426 "SOAP response has more than one Body element");
1429 isds_request
= request_soap_body
->nodesetval
->nodeTab
[0]->children
;
1430 if (isds_request
->next
!= NULL
) {
1431 xmlXPathFreeObject(request_soap_body
);
1432 xmlXPathFreeContext(xpath_ctx
);
1433 xmlFreeDoc(request_doc
);
1434 http_send_response_400(connection
, "SOAP body has more than one child");
1437 if (isds_request
->type
!= XML_ELEMENT_NODE
|| isds_request
->ns
== NULL
||
1438 NULL
== isds_request
->ns
->href
) {
1439 xmlXPathFreeObject(request_soap_body
);
1440 xmlXPathFreeContext(xpath_ctx
);
1441 xmlFreeDoc(request_doc
);
1442 http_send_response_400(connection
,
1443 "SOAP body does not contain a name-space-qualified element");
1447 /* Build SOAP response envelope */
1448 response_doc
= xmlNewDoc(BAD_CAST
"1.0");
1449 if (!response_doc
) {
1450 http_send_response_500(connection
,
1451 "Could not build SOAP response document");
1454 response_soap_envelope
= xmlNewNode(NULL
, BAD_CAST
"Envelope");
1455 if (!response_soap_envelope
) {
1456 http_send_response_500(connection
,
1457 "Could not build SOAP response envelope");
1460 xmlDocSetRootElement(response_doc
, response_soap_envelope
);
1461 /* Only this way we get namespace definition as @xmlns:soap,
1462 * otherwise we get namespace prefix without definition */
1463 soap_ns
= xmlNewNs(response_soap_envelope
, BAD_CAST SOAP_NS
, NULL
);
1464 if(NULL
== soap_ns
) {
1465 http_send_response_500(connection
, "Could not create SOAP name space");
1468 xmlSetNs(response_soap_envelope
, soap_ns
);
1469 response_soap_body
= xmlNewChild(response_soap_envelope
, NULL
,
1470 BAD_CAST
"Body", NULL
);
1471 if (!response_soap_body
) {
1472 http_send_response_500(connection
,
1473 "Could not add Body to SOAP response envelope");
1476 /* Append ISDS response element */
1477 if (-1 == test_asprintf(&response_name
, "%s%s", isds_request
->name
,
1479 http_send_response_500(connection
,
1480 "Could not buld ISDS resposne element name");
1483 isds_response
= xmlNewChild(response_soap_body
, NULL
,
1484 BAD_CAST response_name
, NULL
);
1485 free(response_name
);
1486 if (NULL
== isds_response
) {
1487 http_send_response_500(connection
,
1488 "Could not add ISDS response element to SOAP response body");
1491 isds_ns
= xmlNewNs(isds_response
, isds_request
->ns
->href
, NULL
);
1492 if(NULL
== isds_ns
) {
1493 http_send_response_500(connection
,
1494 "Could not create a name space for the response body");
1497 xmlSetNs(isds_response
, isds_ns
);
1499 /* Dispatch request to service */
1500 for (size_t i
= 0; i
< sizeof(services
)/sizeof(services
[0]); i
++) {
1501 if (!strcmp(services
[i
].end_point
, end_point
) &&
1502 !xmlStrcmp(services
[i
].name_space
, isds_request
->ns
->href
) &&
1503 !xmlStrcmp(services
[i
].name
, isds_request
->name
)) {
1504 /* Check if the configuration is enabled and find configuration */
1505 for (const struct service_configuration
*service
= configuration
;
1506 service
->name
!= SERVICE_END
; service
++) {
1507 if (service
->name
== services
[i
].id
) {
1508 service_handled
= 1;
1509 if (!xmlStrcmp(services
[i
].name_space
, BAD_CAST OISDS_NS
)) {
1510 /* Alias "isds" XPath identifier to OISDS_NS */
1511 if (register_namespaces(xpath_ctx
, 1,
1512 MESSAGE_NS_UNSIGNED
)) {
1513 http_send_response_500(connection
,
1514 "Could not register name spaces to the "
1519 xpath_ctx
->node
= isds_request
;
1520 if (HTTP_ERROR_SERVER
!= services
[i
].function(
1523 service
->arguments
)) {
1526 http_send_response_500(connection
,
1527 "Internal server error while processing "
1537 if (service_passed
) {
1538 /* Serialize the SOAP response */
1539 http_response_body
= xmlBufferCreate();
1540 if (NULL
== http_response_body
) {
1541 http_send_response_500(connection
,
1542 "Could not create xmlBuffer for response serialization");
1545 /* Last argument 1 means format the XML tree. This is pretty but it breaks
1546 * XML document transport as it adds text nodes (indentiation) between
1548 save_ctx
= xmlSaveToBuffer(http_response_body
, "UTF-8", 0);
1549 if (NULL
== save_ctx
) {
1550 http_send_response_500(connection
, "Could not create XML serializer");
1553 /* XXX: According LibXML documentation, this function does not return
1554 * meaningful value yet */
1555 xmlSaveDoc(save_ctx
, response_doc
);
1556 if (-1 == xmlSaveFlush(save_ctx
)) {
1557 http_send_response_500(connection
,
1558 "Could not serialize SOAP response");
1562 http_send_response_200(connection
, http_response_body
->content
,
1563 http_response_body
->use
, soap_mime_type
);
1567 xmlSaveClose(save_ctx
);
1568 xmlBufferFree(http_response_body
);
1570 xmlFreeDoc(response_doc
);
1572 xmlXPathFreeObject(request_soap_body
);
1573 xmlXPathFreeContext(xpath_ctx
);
1574 xmlFreeDoc(request_doc
);
1576 if (!service_handled
) {
1577 http_send_response_500(connection
,
1578 "Requested ISDS service not implemented");