1 #define _XOPEN_SOURCE 500 /* For strdup(3) */
2 #include "../test-tools.h"
7 #include <libxml/parser.h>
8 #include <libxml/xpath.h>
9 #include <libxml/xpathInternals.h>
10 #include <libxml/xmlsave.h>
12 static const char *soap_mime_type
= "text/xml"; /* SOAP/1.1 requires text/xml */
14 /* Used to choose proper name space for message elements.
15 * See _isds_register_namespaces(). */
19 MESSAGE_NS_SIGNED_INCOMING
,
20 MESSAGE_NS_SIGNED_OUTGOING
,
21 MESSAGE_NS_SIGNED_DELIVERY
,
25 #define SOAP_NS "http://schemas.xmlsoap.org/soap/envelope/"
26 #define SOAP2_NS "http://www.w3.org/2003/05/soap-envelope"
27 #define ISDS1_NS "http://isds.czechpoint.cz"
28 #define ISDS_NS "http://isds.czechpoint.cz/v20"
29 #define OISDS_NS "http://isds.czechpoint.cz/v20/asws"
30 #define SISDS_INCOMING_NS "http://isds.czechpoint.cz/v20/message"
31 #define SISDS_OUTGOING_NS "http://isds.czechpoint.cz/v20/SentMessage"
32 #define SISDS_DELIVERY_NS "http://isds.czechpoint.cz/v20/delivery"
33 #define SCHEMA_NS "http://www.w3.org/2001/XMLSchema"
34 #define DEPOSIT_NS "urn:uschovnaWSDL"
39 const char *end_point
;
40 const xmlChar
*name_space
;
42 http_error (*function
) (const struct http_connection
*, xmlDocPtr
,
43 xmlXPathContextPtr
, xmlNodePtr
, xmlDocPtr
, xmlNodePtr
,
44 const void *arguments
);
47 /* Following EXTRACT_* macros expect @xpath_ctx, @error, @message,
49 #define ELEMENT_EXISTS(element, allow_multiple) { \
50 xmlXPathObjectPtr result = NULL; \
51 result = xmlXPathEvalExpression(BAD_CAST element, xpath_ctx); \
52 if (NULL == result) { \
53 error = HTTP_ERROR_SERVER; \
56 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
57 xmlXPathFreeObject(result); \
58 test_asprintf(&message, "Element %s does not exist", element); \
59 error = HTTP_ERROR_CLIENT; \
62 if (!allow_multiple && result->nodesetval->nodeNr > 1) { \
63 xmlXPathFreeObject(result); \
64 test_asprintf(&message, "Multiple %s element", element); \
65 error = HTTP_ERROR_CLIENT; \
69 xmlXPathFreeObject(result); \
72 #define EXTRACT_STRING(element, string) { \
73 xmlXPathObjectPtr result = NULL; \
74 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
75 if (NULL == result) { \
76 error = HTTP_ERROR_SERVER; \
79 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
80 if (result->nodesetval->nodeNr > 1) { \
81 xmlXPathFreeObject(result); \
82 test_asprintf(&message, "Multiple %s element", element); \
83 error = HTTP_ERROR_CLIENT; \
87 xmlXPathCastNodeSetToString(result->nodesetval); \
89 xmlXPathFreeObject(result); \
90 error = HTTP_ERROR_SERVER; \
94 xmlXPathFreeObject(result); \
97 #define EXTRACT_BOOLEAN(element, booleanPtr) { \
98 char *string = NULL; \
99 EXTRACT_STRING(element, string); \
101 if (NULL != string) { \
102 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
103 if (NULL == (booleanPtr)) { \
105 error = HTTP_ERROR_SERVER; \
109 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
110 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
112 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
113 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
116 test_asprintf(&message, \
117 "%s value is not valid boolean: %s", \
120 error = HTTP_ERROR_CLIENT; \
129 #define EXTRACT_DATE(element, tmPtr) { \
130 char *string = NULL; \
131 EXTRACT_STRING(element, string); \
132 if (NULL != string) { \
133 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
134 if (NULL == (tmPtr)) { \
136 error = HTTP_ERROR_SERVER; \
139 error = _server_datestring2tm(string, (tmPtr)); \
141 if (error == HTTP_ERROR_CLIENT) { \
142 test_asprintf(&message, "%s value is not a valid date: %s", \
152 /* Following INSERT_* macros expect @error and leave label */
153 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
155 xmlNodePtr node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
156 (xmlChar *) (string)); \
157 if (NULL == node) { \
158 error = HTTP_ERROR_SERVER; \
163 #define INSERT_STRING(parent, element, string) \
164 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
166 #define INSERT_LONGINTPTR(parent, element, longintPtr) { \
167 if ((longintPtr)) { \
168 char *buffer = NULL; \
169 /* FIXME: locale sensitive */ \
170 if (-1 == test_asprintf(&buffer, "%ld", *(longintPtr))) { \
171 error = HTTP_ERROR_SERVER; \
174 INSERT_STRING(parent, element, buffer) \
176 } else { INSERT_STRING(parent, element, NULL) } \
179 #define INSERT_ULONGINTPTR(parent, element, ulongintPtr) { \
180 if ((ulongintPtr)) { \
181 char *buffer = NULL; \
182 /* FIXME: locale sensitive */ \
183 if (-1 == test_asprintf(&buffer, "%lu", *(ulongintPtr))) { \
184 error = HTTP_ERROR_SERVER; \
187 INSERT_STRING(parent, element, buffer) \
189 } else { INSERT_STRING(parent, element, NULL) } \
192 #define INSERT_BOOLEANPTR(parent, element, booleanPtr) { \
193 if (NULL != (booleanPtr)) { \
194 char *buffer = NULL; \
195 buffer = *(booleanPtr) ? "true" : "false"; \
196 INSERT_STRING(parent, element, buffer) \
197 } else { INSERT_STRING(parent, element, NULL) } \
200 #define INSERT_TIMEVALPTR(parent, element, timevalPtr) { \
201 if (NULL != (timevalPtr)) { \
202 char *buffer = NULL; \
203 error = timeval2timestring(timevalPtr, &buffer); \
208 INSERT_STRING(parent, element, buffer); \
211 INSERT_STRING(parent, element, NULL); \
215 #define INSERT_TMPTR(parent, element, tmPtr) { \
216 if (NULL != (tmPtr)) { \
217 char *buffer = NULL; \
218 error = tm2datestring(tmPtr, &buffer); \
223 INSERT_STRING(parent, element, buffer); \
226 INSERT_STRING(parent, element, NULL); \
230 #define INSERT_ELEMENT(child, parent, element) \
232 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
233 if (NULL == (child)) { \
234 error = HTTP_ERROR_SERVER; \
239 /* TODO: These functions (element_exists(), extract_...(), ...) will replace
240 * the macros (ELEMENT_EXISTS, EXTRACT_...). The compiled code will be
241 * smaller, the compilation will be faster. */
243 /* Check an element exists.
244 * @code is a static output ISDS error code
245 * @error_message is a reallocated output ISDS error message
246 * @xpath_ctx is a current XPath context
247 * @element_name is name of an element to check
248 * @allow_multiple is false to require exactly one element. True to require
249 * one or more elements.
250 * @return HTTP_ERROR_SUCCESS or an appropriate error code. */
251 static http_error
element_exists(const char **code
, char **message
,
252 xmlXPathContextPtr xpath_ctx
, const char *element_name
,
253 _Bool allow_multiple
) {
254 xmlXPathObjectPtr result
= NULL
;
256 result
= xmlXPathEvalExpression(BAD_CAST element_name
, xpath_ctx
);
257 if (NULL
== result
) {
258 return HTTP_ERROR_SERVER
;
260 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
261 xmlXPathFreeObject(result
);
262 test_asprintf(message
, "Element %s does not exist", element_name
);
263 return HTTP_ERROR_CLIENT
;
265 if (!allow_multiple
&& result
->nodesetval
->nodeNr
> 1) {
266 xmlXPathFreeObject(result
);
267 test_asprintf(message
, "Multiple %s element", element_name
);
268 return HTTP_ERROR_CLIENT
;
271 xmlXPathFreeObject(result
);
273 return HTTP_ERROR_SUCCESS
;
277 /* Extract @element_name's value as a string.
278 * @code is a static output ISDS error code
279 * @error_message is a reallocated output ISDS error message
280 * @xpath_ctx is a current XPath context
281 * @element_name is name of a element whose child text node to extract
282 * @string is the extraced allocated string value, or NULL if empty or the
283 * element does not exist.
284 * @return HTTP_ERROR_SUCCESS or an appropriate error code. */
285 static http_error
extract_string(const char **code
, char **message
,
286 xmlXPathContextPtr xpath_ctx
, const char *element_name
,
288 http_error error
= HTTP_ERROR_SUCCESS
;
289 xmlXPathObjectPtr result
= NULL
;
292 if (-1 == test_asprintf(&buffer
, "%s/text()", element_name
)) {
293 error
= HTTP_ERROR_SERVER
;
296 result
= xmlXPathEvalExpression(BAD_CAST buffer
, xpath_ctx
);
298 if (NULL
== result
) {
299 error
= HTTP_ERROR_SERVER
;
302 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
303 if (result
->nodesetval
->nodeNr
> 1) {
304 xmlXPathFreeObject(result
);
305 test_asprintf(message
, "Multiple %s element", element_name
);
306 error
= HTTP_ERROR_CLIENT
;
310 xmlXPathCastNodeSetToString(result
->nodesetval
);
312 xmlXPathFreeObject(result
);
313 error
= HTTP_ERROR_SERVER
;
317 xmlXPathFreeObject(result
);
324 /* Checks an @element_name's value is an @expected_value string.
325 * @code is a static output ISDS error code
326 * @error_message is a reallocated output ISDS error message
327 * @xpath_ctx is a current XPath context
328 * @element_name is name of a element to check
329 * @must_exist is true if the @element_name must exist even if @expected_value
331 * @expected_value is an expected string value
332 * @return HTTP_ERROR_SUCCESS if the @element_name element's value is
333 * @expected_value. HTTP_ERROR_CLIENT if not equaled, HTTP_ERROR_SERVER if an
334 * internal error occured. */
335 static http_error
element_equals_string(const char **code
, char **message
,
336 xmlXPathContextPtr xpath_ctx
, const char *element_name
,
337 _Bool must_exist
, const char *expected_value
) {
338 http_error error
= HTTP_ERROR_SUCCESS
;
342 error
= element_exists(code
, message
, xpath_ctx
, element_name
, 0);
343 if (HTTP_ERROR_SUCCESS
!= error
)
347 error
= extract_string(code
, message
, xpath_ctx
, element_name
, &string
);
348 if (HTTP_ERROR_SUCCESS
!= error
)
351 if (NULL
!= expected_value
) {
352 if (NULL
== string
) {
354 test_asprintf(message
, "Empty %s element", element_name
);
355 error
= HTTP_ERROR_CLIENT
;
358 if (xmlStrcmp(BAD_CAST expected_value
, BAD_CAST string
)) {
360 test_asprintf(message
,
361 "Unexpected %s element value: expected=`%s', got=`%s'",
362 element_name
, expected_value
, string
);
363 error
= HTTP_ERROR_CLIENT
;
367 if (NULL
!= string
&& *string
!= '\0') {
369 test_asprintf(message
,
370 "Unexpected %s element value: "
371 "expected empty string, got=`%s'",
372 element_name
, string
);
373 error
= HTTP_ERROR_CLIENT
;
384 /* Checks an @element_name's value is an @expected_value integer.
385 * @code is a static output ISDS error code
386 * @error_message is a reallocated output ISDS error message
387 * @xpath_ctx is a current XPath context
388 * @element_name is name of a element to check
389 * @must_exist is true if the @element_name must exist even if @expected_value
391 * @expected_value is an expected integer value.
392 * @return HTTP_ERROR_SUCCESS if the @element_name element's value is
393 * @expected_value. HTTP_ERROR_CLIENT if not equaled, HTTP_ERROR_SERVER if an
394 * internal error occured. */
395 static http_error
element_equals_integer(const char **code
, char **message
,
396 xmlXPathContextPtr xpath_ctx
, const char *element_name
,
397 _Bool must_exist
, const long int *expected_value
) {
398 http_error error
= HTTP_ERROR_SUCCESS
;
404 error
= element_exists(code
, message
, xpath_ctx
, element_name
, 0);
405 if (HTTP_ERROR_SUCCESS
!= error
)
409 error
= extract_string(code
, message
, xpath_ctx
, element_name
, &string
);
410 if (HTTP_ERROR_SUCCESS
!= error
)
413 if (NULL
!= expected_value
) {
414 if (NULL
== string
) {
416 test_asprintf(message
, "Empty %s element", element_name
);
417 error
= HTTP_ERROR_CLIENT
;
420 number
= strtol(string
, &endptr
, 10);
421 if (*endptr
!= '\0') {
423 test_asprintf(message
,
424 "%s element value is not a valid integer: %s",
425 element_name
, string
);
426 error
= HTTP_ERROR_CLIENT
;
429 if (number
== LONG_MIN
|| number
== LONG_MAX
) { \
431 test_asprintf(message
, \
432 "%s element value is out of range of long int: %s",
433 element_name
, string
);
434 error
= HTTP_ERROR_SERVER
;
437 free(string
); string
= NULL
;
438 if (number
!= *expected_value
) {
440 test_asprintf(message
,
441 "Unexpected %s element value: expected=`%ld', got=`%ld'",
442 element_name
, *expected_value
, number
);
443 error
= HTTP_ERROR_CLIENT
;
447 if (NULL
!= string
&& *string
!= '\0') {
449 test_asprintf(message
,
450 "Unexpected %s element value: expected no text node, got=`%s'",
451 element_name
, string
);
452 error
= HTTP_ERROR_CLIENT
;
463 /* Checks an @element_name's value is an @expected_value boolean.
464 * @code is a static output ISDS error code
465 * @error_message is an reallocated output ISDS error message
466 * @xpath_ctx is a current XPath context
467 * @element_name is name of a element to check
468 * @must_exist is true if the @element_name must exist even if @expected_value
470 * @expected_value is an expected boolean value
471 * @return HTTP_ERROR_SUCCESS if the @element_name element's value is
472 * @expected_value. HTTP_ERROR_CLIENT if not equaled, HTTP_ERROR_SERVER if an
473 * internal error occured. */
474 static http_error
element_equals_boolean(const char **code
, char **message
,
475 xmlXPathContextPtr xpath_ctx
, const char *element_name
,
476 _Bool must_exist
, const _Bool
*expected_value
) {
477 http_error error
= HTTP_ERROR_SUCCESS
;
482 error
= element_exists(code
, message
, xpath_ctx
, element_name
, 0);
483 if (HTTP_ERROR_SUCCESS
!= error
)
487 error
= extract_string(code
, message
, xpath_ctx
, element_name
, &string
);
488 if (HTTP_ERROR_SUCCESS
!= error
)
491 if (NULL
!= expected_value
) {
492 if (NULL
== string
) {
494 test_asprintf(message
, "Empty %s element", element_name
);
495 error
= HTTP_ERROR_CLIENT
;
498 if (!xmlStrcmp((xmlChar
*)string
, BAD_CAST
"true") ||
499 !xmlStrcmp((xmlChar
*)string
, BAD_CAST
"1"))
501 else if (!xmlStrcmp((xmlChar
*)string
, BAD_CAST
"false") ||
502 !xmlStrcmp((xmlChar
*)string
, BAD_CAST
"0"))
506 test_asprintf(message
,
507 "%s element value is not a valid boolean: %s",
508 element_name
, string
);
509 error
= HTTP_ERROR_CLIENT
;
512 if (*expected_value
!= value
) {
514 test_asprintf(message
,
515 "Unexpected %s element value: expected=%d, got=%d",
516 element_name
, *expected_value
, string
);
517 error
= HTTP_ERROR_CLIENT
;
521 if (NULL
!= string
&& *string
!= '\0') {
523 test_asprintf(message
,
524 "Unexpected %s element value: "
525 "expected empty string, got=`%s'",
526 element_name
, string
);
527 error
= HTTP_ERROR_CLIENT
;
538 /* Insert dmStatus or similar subtree
539 * @parent is element to insert to
540 * @dm is true for dmStatus, otherwise dbStatus
541 * @code is status code as string
542 * @message is UTF-8 encoded message
543 * @db_ref_number is optinal reference number propagated if not @dm
544 * @return 0 on success, otherwise non-0. */
545 static http_error
insert_isds_status(xmlNodePtr parent
, _Bool dm
,
546 const xmlChar
*code
, const xmlChar
*message
,
547 const xmlChar
*db_ref_number
) {
548 http_error error
= HTTP_ERROR_SUCCESS
;
551 if (NULL
== code
|| NULL
== message
) {
552 error
= HTTP_ERROR_SERVER
;
556 INSERT_ELEMENT(status
, parent
, (dm
) ? "dmStatus" : "dbStatus");
557 INSERT_STRING(status
, (dm
) ? "dmStatusCode" : "dbStatusCode", code
);
558 INSERT_STRING(status
, (dm
) ? "dmStatusMessage" : "dbStatusMessage", message
);
559 if (!dm
&& NULL
!= db_ref_number
) {
560 INSERT_STRING(status
, "dbStatusRefNumber", db_ref_number
);
568 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
569 static http_error
tm2datestring(const struct tm
*time
, char **string
) {
570 if (NULL
== time
|| NULL
== string
) return HTTP_ERROR_SERVER
;
572 if (-1 == test_asprintf(string
, "%d-%02d-%02d",
573 time
->tm_year
+ 1900, time
->tm_mon
+ 1, time
->tm_mday
))
574 return HTTP_ERROR_SERVER
;
576 return HTTP_ERROR_SUCCESS
;
580 /* Convert struct timeval *@time to UTF-8 ISO 8601 date-time @string. It
581 * respects the @time microseconds too. */
582 static http_error
timeval2timestring(const struct timeval
*time
,
586 if (!time
|| !string
) return HTTP_ERROR_SERVER
;
588 if (!gmtime_r(&time
->tv_sec
, &broken
)) return HTTP_ERROR_SERVER
;
589 if (time
->tv_usec
< 0 || time
->tv_usec
> 999999) return HTTP_ERROR_SERVER
;
591 /* TODO: small negative year should be formatted as "-0012". This is not
592 * true for glibc "%04d". We should implement it.
593 * TODO: What's type of time->tv_usec exactly? Unsigned? Absolute?
594 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
595 if (-1 == test_asprintf(string
,
596 "%04d-%02d-%02dT%02d:%02d:%02d.%06ld",
597 broken
.tm_year
+ 1900, broken
.tm_mon
+ 1, broken
.tm_mday
,
598 broken
.tm_hour
, broken
.tm_min
, broken
.tm_sec
,
600 return HTTP_ERROR_SERVER
;
602 return HTTP_ERROR_SUCCESS
;
606 /* Compare dates represented by pointer to struct tm.
607 * @return 0 if equalued, non-0 otherwise. */
608 static int datecmp(const struct tm
*a
, const struct tm
*b
) {
609 if (NULL
== a
&& b
== NULL
) return 0;
610 if ((NULL
== a
&& b
!= NULL
) || (NULL
!= a
&& b
== NULL
)) return 1;
611 if (a
->tm_year
!= b
->tm_year
) return 1;
612 if (a
->tm_mon
!= b
->tm_mon
) return 1;
613 if (a
->tm_mday
!= b
->tm_mday
) return 1;
618 /* Implement DummyOperation */
619 static http_error
service_DummyOperation(
620 const struct http_connection
*connection
, const xmlDocPtr soap_request
,
621 xmlXPathContextPtr xpath_ctx
, xmlNodePtr isds_request
,
622 xmlDocPtr soap_response
, xmlNodePtr isds_response
,
623 const void *arguments
) {
624 return insert_isds_status(isds_response
, 1, BAD_CAST
"0000",
625 BAD_CAST
"Success", NULL
);
629 /* Implement Re-signISDSDocument.
630 * It sends document from request back.
631 * @arguments is pointer to struct arguments_DS_Dz_ResignISDSDocument */
632 static http_error
service_ResignISDSDocument(
633 const struct http_connection
*connection
,
634 const xmlDocPtr soap_request
, xmlXPathContextPtr xpath_ctx
,
635 const xmlNodePtr isds_request
,
636 xmlDocPtr soap_response
, xmlNodePtr isds_response
,
637 const void *arguments
) {
638 http_error error
= HTTP_ERROR_SUCCESS
;
639 const char *code
= "9999";
640 char *message
= NULL
;
641 const struct arguments_DS_Dz_ResignISDSDocument
*configuration
=
642 (const struct arguments_DS_Dz_ResignISDSDocument
*)arguments
;
645 if (NULL
== configuration
|| NULL
== configuration
->status_code
||
646 NULL
== configuration
->status_message
) {
647 error
= HTTP_ERROR_SERVER
;
651 EXTRACT_STRING("isds:dmDoc", data
);
653 message
= strdup("Missing isds:dmDoc");
654 error
= HTTP_ERROR_CLIENT
;
659 /* dmResultDoc is mandatory in response */
660 if (xmlStrcmp(BAD_CAST configuration
->status_code
, BAD_CAST
"0000")) {
664 INSERT_STRING(isds_response
, "dmResultDoc", data
);
666 if (configuration
->valid_to
!= NULL
) {
667 error
= tm2datestring(configuration
->valid_to
, &data
);
669 message
= strdup("Could not format date");
672 INSERT_STRING(isds_response
, "dmValidTo", data
);
675 code
= configuration
->status_code
;
676 message
= strdup(configuration
->status_message
);
679 if (HTTP_ERROR_SERVER
!= error
) {
680 http_error next_error
= insert_isds_status(isds_response
, 1,
681 BAD_CAST code
, BAD_CAST message
, NULL
);
682 if (HTTP_ERROR_SUCCESS
!= next_error
) error
= next_error
;
690 /* Implement EraseMessage.
691 * @arguments is pointer to struct arguments_DS_DsManage_ChangeISDSPassword */
692 static http_error
service_EraseMessage(const struct http_connection
*connection
,
693 const xmlDocPtr soap_request
, xmlXPathContextPtr xpath_ctx
,
694 const xmlNodePtr isds_request
,
695 xmlDocPtr soap_response
, xmlNodePtr isds_response
,
696 const void *arguments
) {
697 http_error error
= HTTP_ERROR_SUCCESS
;
698 char *code
= "9999", *message
= NULL
;
699 const struct arguments_DS_Dx_EraseMessage
*configuration
=
700 (const struct arguments_DS_Dx_EraseMessage
*)arguments
;
701 char *message_id
= NULL
;
702 _Bool
*incoming
= NULL
;
704 if (NULL
== configuration
|| NULL
== configuration
->message_id
) {
705 error
= HTTP_ERROR_SERVER
;
709 EXTRACT_STRING("isds:dmID", message_id
);
710 if (NULL
== message_id
) {
711 message
= strdup("Missing isds:dmID");
712 error
= HTTP_ERROR_CLIENT
;
715 EXTRACT_BOOLEAN("isds:dmIncoming", incoming
);
716 if (NULL
== incoming
) {
717 message
= strdup("Missing isds:dmIncoming");
718 error
= HTTP_ERROR_CLIENT
;
722 if (xmlStrcmp((const xmlChar
*) configuration
->message_id
,
723 (const xmlChar
*) message_id
)) {
725 message
= strdup("Message is not in the long term storage");
726 error
= HTTP_ERROR_CLIENT
;
729 if (configuration
->incoming
!= *incoming
) {
731 message
= strdup("Message direction mismatches");
732 error
= HTTP_ERROR_CLIENT
;
737 message
= strdup("Success");
739 if (HTTP_ERROR_SERVER
!= error
) {
740 http_error next_error
= insert_isds_status(isds_response
, 1,
741 BAD_CAST code
, BAD_CAST message
, NULL
);
742 if (HTTP_ERROR_SUCCESS
!= next_error
) error
= next_error
;
751 /* Insert list of credit info as XSD:tCiRecord XML tree.
752 * @isds_response is XML node with the response
753 * @history is list of struct server_credit_event. If NULL, no ciRecord XML
754 * subtree will be created. */
755 static http_error
insert_ciRecords(xmlNodePtr isds_response
,
756 const struct server_list
*history
) {
757 http_error error
= HTTP_ERROR_SUCCESS
;
758 xmlNodePtr records
, record
;
760 if (NULL
== isds_response
) return HTTP_ERROR_SERVER
;
761 if (NULL
== history
) return HTTP_ERROR_SUCCESS
;
763 INSERT_ELEMENT(records
, isds_response
, "ciRecords");
764 for (const struct server_list
*item
= history
; NULL
!= item
;
766 const struct server_credit_event
*event
=
767 (struct server_credit_event
*)item
->data
;
769 INSERT_ELEMENT(record
, records
, "ciRecord");
770 if (NULL
== event
) continue;
772 INSERT_TIMEVALPTR(record
, "ciEventTime", event
->time
);
773 switch(event
->type
) {
774 case SERVER_CREDIT_CHARGED
:
775 INSERT_STRING(record
, "ciEventType", "1");
776 INSERT_STRING(record
, "ciTransID",
777 event
->details
.charged
.transaction
);
779 case SERVER_CREDIT_DISCHARGED
:
780 INSERT_STRING(record
, "ciEventType", "2");
781 INSERT_STRING(record
, "ciTransID",
782 event
->details
.discharged
.transaction
);
784 case SERVER_CREDIT_MESSAGE_SENT
:
785 INSERT_STRING(record
, "ciEventType", "3");
786 INSERT_STRING(record
, "ciRecipientID",
787 event
->details
.message_sent
.recipient
);
788 INSERT_STRING(record
, "ciPDZID",
789 event
->details
.message_sent
.message_id
);
791 case SERVER_CREDIT_STORAGE_SET
:
792 INSERT_STRING(record
, "ciEventType", "4");
793 INSERT_LONGINTPTR(record
, "ciNewCapacity",
794 &event
->details
.storage_set
.new_capacity
);
795 INSERT_TMPTR(record
, "ciNewFrom",
796 event
->details
.storage_set
.new_valid_from
);
797 INSERT_TMPTR(record
, "ciNewTo",
798 event
->details
.storage_set
.new_valid_to
);
799 INSERT_LONGINTPTR(record
, "ciOldCapacity",
800 event
->details
.storage_set
.old_capacity
);
801 INSERT_TMPTR(record
, "ciOldFrom",
802 event
->details
.storage_set
.old_valid_from
);
803 INSERT_TMPTR(record
, "ciOldTo",
804 event
->details
.storage_set
.old_valid_to
);
805 INSERT_STRING(record
, "ciDoneBy",
806 event
->details
.storage_set
.initiator
);
808 case SERVER_CREDIT_EXPIRED
:
809 INSERT_STRING(record
, "ciEventType", "5");
812 error
= HTTP_ERROR_SERVER
;
815 INSERT_LONGINTPTR(record
, "ciCreditChange", &event
->credit_change
);
816 INSERT_LONGINTPTR(record
, "ciCreditAfter", &event
->new_credit
);
825 /* Implement DataBoxCreditInfo.
826 * @arguments is pointer to struct arguments_DS_df_DataBoxCreditInfo */
827 static http_error
service_DataBoxCreditInfo(
828 const struct http_connection
*connection
,
829 const xmlDocPtr soap_request
, xmlXPathContextPtr xpath_ctx
,
830 const xmlNodePtr isds_request
,
831 xmlDocPtr soap_response
, xmlNodePtr isds_response
,
832 const void *arguments
) {
833 http_error error
= HTTP_ERROR_SUCCESS
;
834 const char *code
= "9999";
835 char *message
= NULL
;
836 const struct arguments_DS_df_DataBoxCreditInfo
*configuration
=
837 (const struct arguments_DS_df_DataBoxCreditInfo
*)arguments
;
839 struct tm
*from_date
= NULL
, *to_date
= NULL
;
841 if (NULL
== configuration
|| NULL
== configuration
->status_code
||
842 NULL
== configuration
->status_message
) {
843 error
= HTTP_ERROR_SERVER
;
847 EXTRACT_STRING("isds:dbID", box_id
);
848 if (NULL
== box_id
) {
849 message
= strdup("Missing isds:dbID");
850 error
= HTTP_ERROR_CLIENT
;
853 if (NULL
!= configuration
->box_id
&&
854 xmlStrcmp(BAD_CAST configuration
->box_id
,
857 message
= strdup("Unexpected isds:dbID value");
858 error
= HTTP_ERROR_CLIENT
;
862 ELEMENT_EXISTS("isds:ciFromDate", 0);
863 EXTRACT_DATE("isds:ciFromDate", from_date
);
864 if (datecmp(configuration
->from_date
, from_date
)) {
866 message
= strdup("Unexpected isds:ciFromDate value");
867 error
= HTTP_ERROR_CLIENT
;
871 ELEMENT_EXISTS("isds:ciTodate", 0);
872 EXTRACT_DATE("isds:ciTodate", to_date
);
873 if (datecmp(configuration
->to_date
, to_date
)) {
875 message
= strdup("Unexpected isds:ciTodate value");
876 error
= HTTP_ERROR_CLIENT
;
880 INSERT_LONGINTPTR(isds_response
, "currentCredit",
881 &configuration
->current_credit
);
882 INSERT_STRING(isds_response
, "notifEmail", configuration
->email
);
883 if ((error
= insert_ciRecords(isds_response
, configuration
->history
))) {
887 code
= configuration
->status_code
;
888 message
= strdup(configuration
->status_message
);
890 if (HTTP_ERROR_SERVER
!= error
) {
891 http_error next_error
= insert_isds_status(isds_response
, 0,
892 BAD_CAST code
, BAD_CAST message
, NULL
);
893 if (HTTP_ERROR_SUCCESS
!= next_error
) error
= next_error
;
903 /* Insert list of fulltext search results as XSD:tdbResultsArray XML tree.
904 * @isds_response is XML node with the response
905 * @results is list of struct server_db_result *.
906 * @create_empty_root is true to create dbResults element even if @results is
908 static http_error
insert_tdbResultsArray(xmlNodePtr isds_response
,
909 const struct server_list
*results
, _Bool create_empty_root
) {
910 http_error error
= HTTP_ERROR_SUCCESS
;
911 xmlNodePtr root
, entry
;
913 if (NULL
== isds_response
) return HTTP_ERROR_SERVER
;
915 if (NULL
!= results
|| create_empty_root
)
916 INSERT_ELEMENT(root
, isds_response
, "dbResults");
918 if (NULL
== results
) return HTTP_ERROR_SUCCESS
;
920 for (const struct server_list
*item
= results
; NULL
!= item
;
922 const struct server_db_result
*result
=
923 (struct server_db_result
*)item
->data
;
925 INSERT_ELEMENT(entry
, root
, "dbResult");
926 if (NULL
== result
) continue;
928 INSERT_STRING(entry
, "dbID", result
->id
);
929 INSERT_STRING(entry
, "dbType", result
->type
);
930 INSERT_STRING(entry
, "dbName", result
->name
);
931 INSERT_STRING(entry
, "dbAddress", result
->address
);
932 INSERT_TMPTR(entry
, "dbBiDate", result
->birth_date
);
933 INSERT_STRING(entry
, "dbICO", result
->ic
);
934 INSERT_BOOLEANPTR(entry
, "dbEffectiveOVM", &result
->ovm
);
935 INSERT_STRING(entry
, "dbSendOptions", result
->send_options
);
943 /* Implement ISDSSearch2.
944 * @arguments is pointer to struct arguments_DS_df_ISDSSearch2 */
945 static http_error
service_ISDSSearch2(
946 const struct http_connection
*connection
,
947 const xmlDocPtr soap_request
, xmlXPathContextPtr xpath_ctx
,
948 const xmlNodePtr isds_request
,
949 xmlDocPtr soap_response
, xmlNodePtr isds_response
,
950 const void *arguments
) {
951 http_error error
= HTTP_ERROR_SUCCESS
;
952 const char *code
= "9999";
953 char *message
= NULL
;
954 const struct arguments_DS_df_ISDSSearch2
*configuration
=
955 (const struct arguments_DS_df_ISDSSearch2
*)arguments
;
958 if (NULL
== configuration
|| NULL
== configuration
->status_code
||
959 NULL
== configuration
->status_message
) {
960 error
= HTTP_ERROR_SERVER
;
965 EXTRACT_STRING("isds:searchText", string
);
966 if (NULL
== string
) {
967 message
= strdup("Missing or empty isds:searchText");
968 error
= HTTP_ERROR_CLIENT
;
971 if (NULL
!= configuration
->search_text
&&
972 xmlStrcmp(BAD_CAST configuration
->search_text
,
975 message
= strdup("Unexpected isds:searchText value");
976 error
= HTTP_ERROR_CLIENT
;
979 free(string
); string
= NULL
;
981 error
= element_equals_string(&code
, &message
, xpath_ctx
,
982 "isds:searchType", 1, configuration
->search_type
);
983 if (error
) goto leave
;
985 error
= element_equals_string(&code
, &message
, xpath_ctx
,
986 "isds:searchScope", 1, configuration
->search_scope
);
987 if (error
) goto leave
;
989 error
= element_equals_integer(&code
, &message
, xpath_ctx
,
990 "isds:page", 1, configuration
->search_page_number
);
991 if (error
) goto leave
;
993 error
= element_equals_integer(&code
, &message
, xpath_ctx
,
994 "isds:pageSize", 1, configuration
->search_page_size
);
995 if (error
) goto leave
;
997 error
= element_equals_boolean(&code
, &message
, xpath_ctx
,
998 "isds:highlighting", 0, configuration
->search_highlighting_value
);
999 if (error
) goto leave
;
1001 /* Build response */
1002 if (NULL
!= configuration
->total_count
)
1003 INSERT_ULONGINTPTR(isds_response
, "totalCount",
1004 configuration
->total_count
);
1005 if (NULL
!= configuration
->current_count
)
1006 INSERT_ULONGINTPTR(isds_response
, "currentCount",
1007 configuration
->current_count
);
1008 if (NULL
!= configuration
->position
)
1009 INSERT_ULONGINTPTR(isds_response
, "position",
1010 configuration
->position
);
1011 if (NULL
!= configuration
->last_page
)
1012 INSERT_BOOLEANPTR(isds_response
, "lastPage",
1013 configuration
->last_page
);
1014 if ((error
= insert_tdbResultsArray(isds_response
, configuration
->results
,
1015 configuration
->results_exists
))) {
1019 code
= configuration
->status_code
;
1020 message
= strdup(configuration
->status_message
);
1023 if (HTTP_ERROR_SERVER
!= error
) {
1024 http_error next_error
= insert_isds_status(isds_response
, 0,
1025 BAD_CAST code
, BAD_CAST message
, NULL
);
1026 if (HTTP_ERROR_SUCCESS
!= next_error
) error
= next_error
;
1034 /* Common part for ChangeISDSPassword and ChangePasswordOTP.
1035 * @code is output pointer to static string
1036 * @pass_message is output pointer to auto-allocated string
1037 * @arguments is pointer to struct arguments_DS_DsManage_ChangeISDSPassword */
1038 static http_error
check_passwd(
1039 const char *username
, const char *current_password
,
1040 xmlXPathContextPtr xpath_ctx
,
1041 char **code
, char **pass_message
) {
1042 http_error error
= HTTP_ERROR_SUCCESS
;
1043 char *message
= NULL
;
1044 char *old_password
= NULL
, *new_password
= NULL
;
1047 if (NULL
== username
|| NULL
== current_password
||
1048 NULL
== code
|| NULL
== pass_message
) {
1049 return HTTP_ERROR_SERVER
;
1056 EXTRACT_STRING("isds:dbOldPassword", old_password
);
1057 if (NULL
== old_password
) {
1058 message
= strdup("Empty isds:dbOldPassword");
1059 error
= HTTP_ERROR_CLIENT
;
1062 EXTRACT_STRING("isds:dbNewPassword", new_password
);
1063 if (NULL
== new_password
) {
1064 message
= strdup("Empty isds:dbOldPassword");
1065 error
= HTTP_ERROR_CLIENT
;
1069 /* Check defined cases */
1070 if (strcmp(current_password
, old_password
)) {
1072 message
= strdup("Bad current password");
1073 error
= HTTP_ERROR_CLIENT
;
1077 length
= strlen(new_password
);
1079 if (length
< 8 || length
> 32) {
1081 message
= strdup("Too short or too long");
1082 error
= HTTP_ERROR_CLIENT
;
1087 const char lower
[] = "abcdefghijklmnopqrstuvwxyz";
1088 const char upper
[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1089 const char digit
[] = "0123456789";
1090 const char special
[] = "!#$%&()*+,-.:=?@[]_{}|~";
1091 _Bool has_lower
= 0, has_upper
= 0, has_digit
=0;
1093 for (int i
= 0; i
< length
; i
++) {
1094 if (NULL
!= strchr(lower
, new_password
[i
]))
1096 else if (NULL
!= strchr(upper
, new_password
[i
]))
1098 else if (NULL
!= strchr(digit
, new_password
[i
]))
1100 else if (NULL
== strchr(special
, new_password
[i
])) {
1102 message
= strdup("Password contains forbidden character");
1103 error
= HTTP_ERROR_CLIENT
;
1108 if (!has_lower
|| !has_upper
|| !has_digit
) {
1110 message
= strdup("Password does not contain lower cased letter, "
1111 "upper cased letter and a digit");
1112 error
= HTTP_ERROR_CLIENT
;
1117 if (!strcmp(old_password
, new_password
)) {
1119 message
= strdup("New password same as current one");
1120 error
= HTTP_ERROR_CLIENT
;
1124 if (NULL
!= strstr(new_password
, username
)) {
1126 message
= strdup("New password contains user ID");
1127 error
= HTTP_ERROR_CLIENT
;
1131 for (int i
= 0; i
< length
- 2; i
++) {
1132 if (new_password
[i
] == new_password
[i
+1] &&
1133 new_password
[i
] == new_password
[i
+2]) {
1135 message
= strdup("Password contains sequence "
1136 "of three identical characters");
1137 error
= HTTP_ERROR_CLIENT
;
1143 const char *forbidden_prefix
[] = { "qwert", "asdgf", "12345" };
1144 for (int i
= 0; i
< sizeof(forbidden_prefix
)/sizeof(*forbidden_prefix
);
1146 if (!strncmp(new_password
, forbidden_prefix
[i
],
1147 strlen(forbidden_prefix
[i
]))) {
1149 message
= strdup("Password has forbidden prefix");
1150 error
= HTTP_ERROR_CLIENT
;
1157 message
= strdup("Success");
1161 *pass_message
= message
;
1166 /* Implement ChangeISDSPassword.
1167 * @arguments is pointer to struct arguments_DS_DsManage_ChangeISDSPassword */
1168 static http_error
service_ChangeISDSPassword(
1169 const struct http_connection
*connection
,
1170 const xmlDocPtr soap_request
, xmlXPathContextPtr xpath_ctx
,
1171 const xmlNodePtr isds_request
,
1172 xmlDocPtr soap_response
, 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 const struct http_connection
*connection
,
1206 const xmlDocPtr soap_request
, xmlXPathContextPtr xpath_ctx
,
1207 const xmlNodePtr isds_request
,
1208 xmlDocPtr soap_response
, xmlNodePtr isds_response
,
1209 const void *arguments
) {
1210 http_error error
= HTTP_ERROR_SUCCESS
;
1211 char *code
= "9999", *message
= NULL
;
1212 const struct arguments_asws_changePassword_ChangePasswordOTP
*configuration
1213 = (const struct arguments_asws_changePassword_ChangePasswordOTP
*)
1215 char *method
= NULL
;
1217 if (NULL
== configuration
|| NULL
== configuration
->username
||
1218 NULL
== configuration
->current_password
) {
1219 error
= HTTP_ERROR_SERVER
;
1223 /* Chek for OTP method */
1224 EXTRACT_STRING("isds:dbOTPType", method
);
1225 if (NULL
== method
) {
1226 message
= strdup("Empty isds:dbOTPType");
1227 error
= HTTP_ERROR_CLIENT
;
1230 if ((configuration
->method
== AUTH_OTP_HMAC
&& strcmp(method
, "HOTP")) ||
1231 (configuration
->method
== AUTH_OTP_TIME
&& strcmp(method
, "TOTP"))) {
1232 message
= strdup("isds:dbOTPType does not match OTP method");
1233 error
= HTTP_ERROR_CLIENT
;
1237 /* Check for common password rules */
1238 error
= check_passwd(
1239 configuration
->username
, configuration
->current_password
,
1240 xpath_ctx
, &code
, &message
);
1243 if (HTTP_ERROR_SERVER
!= error
) {
1244 http_error next_error
= insert_isds_status(isds_response
, 0,
1245 BAD_CAST code
, BAD_CAST message
,
1246 BAD_CAST configuration
->reference_number
);
1247 if (HTTP_ERROR_SUCCESS
!= next_error
) error
= next_error
;
1255 /* Implement SendSMSCode.
1256 * @arguments is pointer to struct arguments_asws_changePassword_SendSMSCode */
1257 static http_error
service_SendSMSCode(
1258 const struct http_connection
*connection
,
1259 const xmlDocPtr soap_request
, xmlXPathContextPtr xpath_ctx
,
1260 const xmlNodePtr isds_request
,
1261 xmlDocPtr soap_response
, xmlNodePtr isds_response
,
1262 const void *arguments
) {
1263 const struct arguments_asws_changePassword_SendSMSCode
*configuration
1264 = (const struct arguments_asws_changePassword_SendSMSCode
*)
1267 if (NULL
== configuration
|| NULL
== configuration
->status_code
||
1268 NULL
== configuration
->status_message
) {
1269 return HTTP_ERROR_SERVER
;
1272 return insert_isds_status(isds_response
, 0,
1273 BAD_CAST configuration
->status_code
,
1274 BAD_CAST configuration
->status_message
,
1275 BAD_CAST configuration
->reference_number
);
1279 /* List of implemented services */
1280 static struct service services
[] = {
1281 { SERVICE_DS_Dz_DummyOperation
,
1282 "DS/dz", BAD_CAST ISDS_NS
, BAD_CAST
"DummyOperation",
1283 service_DummyOperation
},
1284 { SERVICE_DS_Dz_ResignISDSDocument
,
1285 "DS/dz", BAD_CAST ISDS_NS
, BAD_CAST
"Re-signISDSDocument",
1286 service_ResignISDSDocument
},
1287 { SERVICE_DS_df_DataBoxCreditInfo
,
1288 "DS/df", BAD_CAST ISDS_NS
, BAD_CAST
"DataBoxCreditInfo",
1289 service_DataBoxCreditInfo
},
1290 { SERVICE_DS_df_ISDSSearch2
,
1291 "DS/df", BAD_CAST ISDS_NS
, BAD_CAST
"ISDSSearch2",
1292 service_ISDSSearch2
},
1293 { SERVICE_DS_DsManage_ChangeISDSPassword
,
1294 "DS/DsManage", BAD_CAST ISDS_NS
, BAD_CAST
"ChangeISDSPassword",
1295 service_ChangeISDSPassword
},
1296 { SERVICE_DS_Dx_EraseMessage
,
1297 "DS/dx", BAD_CAST ISDS_NS
, BAD_CAST
"EraseMessage",
1298 service_EraseMessage
},
1299 { SERVICE_asws_changePassword_ChangePasswordOTP
,
1300 "/asws/changePassword", BAD_CAST OISDS_NS
, BAD_CAST
"ChangePasswordOTP",
1301 service_ChangePasswordOTP
},
1302 { SERVICE_asws_changePassword_SendSMSCode
,
1303 "/asws/changePassword", BAD_CAST OISDS_NS
, BAD_CAST
"SendSMSCode",
1304 service_SendSMSCode
},
1308 /* Makes known all relevant namespaces to given XPath context
1309 * @xpath_ctx is XPath context
1310 * @otp_ns selects name space for the request and response know as "isds".
1311 * Use true for OTP-authenticated password change services, otherwise false.
1312 * @message_ns selects proper message name space. Unsigned and signed
1313 * messages and delivery info's differ in prefix and URI.
1314 * @return 0 in success, otherwise not 0. */
1315 static int register_namespaces(xmlXPathContextPtr xpath_ctx
,
1316 const _Bool otp_ns
, const message_ns_type message_ns
) {
1317 const xmlChar
*service_namespace
= NULL
;
1318 const xmlChar
*message_namespace
= NULL
;
1320 if (!xpath_ctx
) return -1;
1323 service_namespace
= BAD_CAST OISDS_NS
;
1325 service_namespace
= BAD_CAST ISDS_NS
;
1328 switch(message_ns
) {
1330 message_namespace
= BAD_CAST ISDS1_NS
; break;
1331 case MESSAGE_NS_UNSIGNED
:
1332 message_namespace
= BAD_CAST ISDS_NS
; break;
1333 case MESSAGE_NS_SIGNED_INCOMING
:
1334 message_namespace
= BAD_CAST SISDS_INCOMING_NS
; break;
1335 case MESSAGE_NS_SIGNED_OUTGOING
:
1336 message_namespace
= BAD_CAST SISDS_OUTGOING_NS
; break;
1337 case MESSAGE_NS_SIGNED_DELIVERY
:
1338 message_namespace
= BAD_CAST SISDS_DELIVERY_NS
; break;
1343 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"soap", BAD_CAST SOAP_NS
))
1345 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"isds", service_namespace
))
1347 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"sisds", message_namespace
))
1349 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"xs", BAD_CAST SCHEMA_NS
))
1351 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"deposit", BAD_CAST DEPOSIT_NS
))
1357 /* Parse soap request, pass it to service endpoint and respond to it.
1358 * It sends final HTTP response. */
1359 void soap(const struct http_connection
*connection
,
1360 const struct service_configuration
*configuration
,
1361 const void *request
, size_t request_length
, const char *end_point
) {
1362 xmlDocPtr request_doc
= NULL
;
1363 xmlXPathContextPtr xpath_ctx
= NULL
;
1364 xmlXPathObjectPtr request_soap_body
= NULL
;
1365 xmlNodePtr isds_request
= NULL
; /* pointer only */
1366 _Bool service_handled
= 0, service_passed
= 0;
1367 xmlDocPtr response_doc
= NULL
;
1368 xmlNodePtr response_soap_envelope
= NULL
, response_soap_body
= NULL
,
1369 isds_response
= NULL
;
1370 xmlNsPtr soap_ns
= NULL
, isds_ns
= NULL
;
1371 char *response_name
= NULL
;
1372 xmlBufferPtr http_response_body
= NULL
;
1373 xmlSaveCtxtPtr save_ctx
= NULL
;
1376 if (NULL
== configuration
) {
1377 http_send_response_500(connection
,
1378 "Second argument of soap() is NULL");
1382 if (NULL
== request
|| request_length
== 0) {
1383 http_send_response_400(connection
, "Client sent empty body");
1387 request_doc
= xmlParseMemory(request
, request_length
);
1388 if (NULL
== request_doc
) {
1389 http_send_response_400(connection
, "Client sent invalid XML document");
1393 xpath_ctx
= xmlXPathNewContext(request_doc
);
1394 if (NULL
== xpath_ctx
) {
1395 xmlFreeDoc(request_doc
);
1396 http_send_response_500(connection
, "Could not create XPath context");
1400 if (register_namespaces(xpath_ctx
, 0, MESSAGE_NS_UNSIGNED
)) {
1401 xmlXPathFreeContext(xpath_ctx
);
1402 xmlFreeDoc(request_doc
);
1403 http_send_response_500(connection
,
1404 "Could not register name spaces to the XPath context");
1409 request_soap_body
= xmlXPathEvalExpression(
1410 BAD_CAST
"/soap:Envelope/soap:Body", xpath_ctx
);
1411 if (NULL
== request_soap_body
) {
1412 xmlXPathFreeContext(xpath_ctx
);
1413 xmlFreeDoc(request_doc
);
1414 http_send_response_400(connection
, "Client sent invalid SOAP request");
1417 if (xmlXPathNodeSetIsEmpty(request_soap_body
->nodesetval
)) {
1418 xmlXPathFreeObject(request_soap_body
);
1419 xmlXPathFreeContext(xpath_ctx
);
1420 xmlFreeDoc(request_doc
);
1421 http_send_response_400(connection
,
1422 "SOAP request does not contain SOAP Body element");
1425 if (request_soap_body
->nodesetval
->nodeNr
> 1) {
1426 xmlXPathFreeObject(request_soap_body
);
1427 xmlXPathFreeContext(xpath_ctx
);
1428 xmlFreeDoc(request_doc
);
1429 http_send_response_400(connection
,
1430 "SOAP response has more than one Body element");
1433 isds_request
= request_soap_body
->nodesetval
->nodeTab
[0]->children
;
1434 if (isds_request
->next
!= NULL
) {
1435 xmlXPathFreeObject(request_soap_body
);
1436 xmlXPathFreeContext(xpath_ctx
);
1437 xmlFreeDoc(request_doc
);
1438 http_send_response_400(connection
, "SOAP body has more than one child");
1441 if (isds_request
->type
!= XML_ELEMENT_NODE
|| isds_request
->ns
== NULL
||
1442 NULL
== isds_request
->ns
->href
) {
1443 xmlXPathFreeObject(request_soap_body
);
1444 xmlXPathFreeContext(xpath_ctx
);
1445 xmlFreeDoc(request_doc
);
1446 http_send_response_400(connection
,
1447 "SOAP body does not contain a name-space-qualified element");
1451 /* Build SOAP response envelope */
1452 response_doc
= xmlNewDoc(BAD_CAST
"1.0");
1453 if (!response_doc
) {
1454 http_send_response_500(connection
,
1455 "Could not build SOAP response document");
1458 response_soap_envelope
= xmlNewNode(NULL
, BAD_CAST
"Envelope");
1459 if (!response_soap_envelope
) {
1460 http_send_response_500(connection
,
1461 "Could not build SOAP response envelope");
1464 xmlDocSetRootElement(response_doc
, response_soap_envelope
);
1465 /* Only this way we get namespace definition as @xmlns:soap,
1466 * otherwise we get namespace prefix without definition */
1467 soap_ns
= xmlNewNs(response_soap_envelope
, BAD_CAST SOAP_NS
, NULL
);
1468 if(NULL
== soap_ns
) {
1469 http_send_response_500(connection
, "Could not create SOAP name space");
1472 xmlSetNs(response_soap_envelope
, soap_ns
);
1473 response_soap_body
= xmlNewChild(response_soap_envelope
, NULL
,
1474 BAD_CAST
"Body", NULL
);
1475 if (!response_soap_body
) {
1476 http_send_response_500(connection
,
1477 "Could not add Body to SOAP response envelope");
1480 /* Append ISDS response element */
1481 if (-1 == test_asprintf(&response_name
, "%s%s", isds_request
->name
,
1483 http_send_response_500(connection
,
1484 "Could not buld ISDS resposne element name");
1487 isds_response
= xmlNewChild(response_soap_body
, NULL
,
1488 BAD_CAST response_name
, NULL
);
1489 free(response_name
);
1490 if (NULL
== isds_response
) {
1491 http_send_response_500(connection
,
1492 "Could not add ISDS response element to SOAP response body");
1495 isds_ns
= xmlNewNs(isds_response
, isds_request
->ns
->href
, NULL
);
1496 if(NULL
== isds_ns
) {
1497 http_send_response_500(connection
,
1498 "Could not create a name space for the response body");
1501 xmlSetNs(isds_response
, isds_ns
);
1503 /* Dispatch request to service */
1504 for (int i
= 0; i
< sizeof(services
)/sizeof(services
[0]); i
++) {
1505 if (!strcmp(services
[i
].end_point
, end_point
) &&
1506 !xmlStrcmp(services
[i
].name_space
, isds_request
->ns
->href
) &&
1507 !xmlStrcmp(services
[i
].name
, isds_request
->name
)) {
1508 /* Check if the configuration is enabled and find configuration */
1509 for (const struct service_configuration
*service
= configuration
;
1510 service
->name
!= SERVICE_END
; service
++) {
1511 if (service
->name
== services
[i
].id
) {
1512 service_handled
= 1;
1513 if (!xmlStrcmp(services
[i
].name_space
, BAD_CAST OISDS_NS
)) {
1514 /* Alias "isds" XPath identifier to OISDS_NS */
1515 if (register_namespaces(xpath_ctx
, 1,
1516 MESSAGE_NS_UNSIGNED
)) {
1517 http_send_response_500(connection
,
1518 "Could not register name spaces to the "
1523 xpath_ctx
->node
= isds_request
;
1524 if (HTTP_ERROR_SERVER
!= services
[i
].function(connection
,
1525 request_doc
, xpath_ctx
, isds_request
,
1526 response_doc
, isds_response
,
1527 service
->arguments
)) {
1530 http_send_response_500(connection
,
1531 "Internal server error while processing "
1541 if (service_passed
) {
1542 /* Serialize the SOAP response */
1543 http_response_body
= xmlBufferCreate();
1544 if (NULL
== http_response_body
) {
1545 http_send_response_500(connection
,
1546 "Could not create xmlBuffer for response serialization");
1549 /* Last argument 1 means format the XML tree. This is pretty but it breaks
1550 * XML document transport as it adds text nodes (indentiation) between
1552 save_ctx
= xmlSaveToBuffer(http_response_body
, "UTF-8", 0);
1553 if (NULL
== save_ctx
) {
1554 http_send_response_500(connection
, "Could not create XML serializer");
1557 /* XXX: According LibXML documentation, this function does not return
1558 * meaningful value yet */
1559 xmlSaveDoc(save_ctx
, response_doc
);
1560 if (-1 == xmlSaveFlush(save_ctx
)) {
1561 http_send_response_500(connection
,
1562 "Could not serialize SOAP response");
1566 http_send_response_200(connection
, http_response_body
->content
,
1567 http_response_body
->use
, soap_mime_type
);
1571 xmlSaveClose(save_ctx
);
1572 xmlBufferFree(http_response_body
);
1574 xmlFreeDoc(response_doc
);
1576 xmlXPathFreeObject(request_soap_body
);
1577 xmlXPathFreeContext(xpath_ctx
);
1578 xmlFreeDoc(request_doc
);
1580 if (!service_handled
) {
1581 http_send_response_500(connection
,
1582 "Requested ISDS service not implemented");