Use PRIdMAX instead of %jd formatting string
[libisds.git] / test / simline / service.c
blob644ba0e52bfdc261d4331abd154eb1a6a8042628
1 #include "../../config.h"
2 #define _XOPEN_SOURCE XOPEN_SOURCE_LEVEL_FOR_STRDUP
3 #include "../test-tools.h"
4 #include "http.h"
5 #include "services.h"
6 #include "system.h"
7 #include <string.h>
8 #include <stdint.h> /* For intmax_t */
9 #include <inttypes.h> /* For PRIdMAX */
10 #include <libxml/parser.h>
11 #include <libxml/xpath.h>
12 #include <libxml/xpathInternals.h>
13 #include <libxml/xmlsave.h>
15 static const char *soap_mime_type = "text/xml"; /* SOAP/1.1 requires text/xml */
17 /* Used to choose proper name space for message elements.
18 * See _isds_register_namespaces(). */
19 typedef enum {
20 MESSAGE_NS_1,
21 MESSAGE_NS_UNSIGNED,
22 MESSAGE_NS_SIGNED_INCOMING,
23 MESSAGE_NS_SIGNED_OUTGOING,
24 MESSAGE_NS_SIGNED_DELIVERY,
25 MESSAGE_NS_OTP
26 } message_ns_type;
28 #define SOAP_NS "http://schemas.xmlsoap.org/soap/envelope/"
29 #define SOAP2_NS "http://www.w3.org/2003/05/soap-envelope"
30 #define ISDS1_NS "http://isds.czechpoint.cz"
31 #define ISDS_NS "http://isds.czechpoint.cz/v20"
32 #define OISDS_NS "http://isds.czechpoint.cz/v20/asws"
33 #define SISDS_INCOMING_NS "http://isds.czechpoint.cz/v20/message"
34 #define SISDS_OUTGOING_NS "http://isds.czechpoint.cz/v20/SentMessage"
35 #define SISDS_DELIVERY_NS "http://isds.czechpoint.cz/v20/delivery"
36 #define SCHEMA_NS "http://www.w3.org/2001/XMLSchema"
37 #define DEPOSIT_NS "urn:uschovnaWSDL"
40 struct service {
41 service_id id;
42 const char *end_point;
43 const xmlChar *name_space;
44 const xmlChar *name;
45 http_error (*function) (
46 xmlXPathContextPtr, xmlNodePtr,
47 const void *arguments);
50 /* Following EXTRACT_* macros expect @xpath_ctx, @error, @message,
51 * and leave label. */
52 #define ELEMENT_EXISTS(element, allow_multiple) { \
53 xmlXPathObjectPtr result = NULL; \
54 result = xmlXPathEvalExpression(BAD_CAST element, xpath_ctx); \
55 if (NULL == result) { \
56 error = HTTP_ERROR_SERVER; \
57 goto leave; \
58 } \
59 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
60 xmlXPathFreeObject(result); \
61 test_asprintf(&message, "Element %s does not exist", element); \
62 error = HTTP_ERROR_CLIENT; \
63 goto leave; \
64 } else { \
65 if (!allow_multiple && result->nodesetval->nodeNr > 1) { \
66 xmlXPathFreeObject(result); \
67 test_asprintf(&message, "Multiple %s element", element); \
68 error = HTTP_ERROR_CLIENT; \
69 goto leave; \
70 } \
71 } \
72 xmlXPathFreeObject(result); \
75 #define EXTRACT_STRING(element, string) { \
76 xmlXPathObjectPtr result = NULL; \
77 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
78 if (NULL == result) { \
79 error = HTTP_ERROR_SERVER; \
80 goto leave; \
81 } \
82 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
83 if (result->nodesetval->nodeNr > 1) { \
84 xmlXPathFreeObject(result); \
85 test_asprintf(&message, "Multiple %s element", element); \
86 error = HTTP_ERROR_CLIENT; \
87 goto leave; \
88 } \
89 (string) = (char *) \
90 xmlXPathCastNodeSetToString(result->nodesetval); \
91 if (!(string)) { \
92 xmlXPathFreeObject(result); \
93 error = HTTP_ERROR_SERVER; \
94 goto leave; \
95 } \
96 } \
97 xmlXPathFreeObject(result); \
100 #define EXTRACT_BOOLEAN(element, booleanPtr) { \
101 char *string = NULL; \
102 EXTRACT_STRING(element, string); \
104 if (NULL != string) { \
105 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
106 if (NULL == (booleanPtr)) { \
107 free(string); \
108 error = HTTP_ERROR_SERVER; \
109 goto leave; \
112 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
113 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
114 *(booleanPtr) = 1; \
115 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
116 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
117 *(booleanPtr) = 0; \
118 else { \
119 test_asprintf(&message, \
120 "%s value is not valid boolean: %s", \
121 element, string); \
122 free(string); \
123 error = HTTP_ERROR_CLIENT; \
124 goto leave; \
127 free(string); \
132 #define EXTRACT_DATE(element, tmPtr) { \
133 char *string = NULL; \
134 EXTRACT_STRING(element, string); \
135 if (NULL != string) { \
136 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
137 if (NULL == (tmPtr)) { \
138 free(string); \
139 error = HTTP_ERROR_SERVER; \
140 goto leave; \
142 error = _server_datestring2tm(string, (tmPtr)); \
143 if (error) { \
144 if (error == HTTP_ERROR_CLIENT) { \
145 test_asprintf(&message, "%s value is not a valid date: %s", \
146 element, string); \
148 free(string); \
149 goto leave; \
151 free(string); \
155 /* Following INSERT_* macros expect @error and leave label */
156 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
158 xmlNodePtr node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
159 (xmlChar *) (string)); \
160 if (NULL == node) { \
161 error = HTTP_ERROR_SERVER; \
162 goto leave; \
166 #define INSERT_STRING(parent, element, string) \
167 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
169 #define INSERT_LONGINTPTR(parent, element, longintPtr) { \
170 if ((longintPtr)) { \
171 char *buffer = NULL; \
172 /* FIXME: locale sensitive */ \
173 if (-1 == test_asprintf(&buffer, "%ld", *(longintPtr))) { \
174 error = HTTP_ERROR_SERVER; \
175 goto leave; \
177 INSERT_STRING(parent, element, buffer) \
178 free(buffer); \
179 } else { INSERT_STRING(parent, element, NULL) } \
182 #define INSERT_ULONGINTPTR(parent, element, ulongintPtr) { \
183 if ((ulongintPtr)) { \
184 char *buffer = NULL; \
185 /* FIXME: locale sensitive */ \
186 if (-1 == test_asprintf(&buffer, "%lu", *(ulongintPtr))) { \
187 error = HTTP_ERROR_SERVER; \
188 goto leave; \
190 INSERT_STRING(parent, element, buffer) \
191 free(buffer); \
192 } else { INSERT_STRING(parent, element, NULL) } \
195 #define INSERT_BOOLEANPTR(parent, element, booleanPtr) { \
196 if (NULL != (booleanPtr)) { \
197 char *buffer = NULL; \
198 buffer = *(booleanPtr) ? "true" : "false"; \
199 INSERT_STRING(parent, element, buffer) \
200 } else { INSERT_STRING(parent, element, NULL) } \
203 #define INSERT_TIMEVALPTR(parent, element, timevalPtr) { \
204 if (NULL != (timevalPtr)) { \
205 char *buffer = NULL; \
206 error = timeval2timestring(timevalPtr, &buffer); \
207 if (error) { \
208 free(buffer); \
209 goto leave; \
211 INSERT_STRING(parent, element, buffer); \
212 free(buffer); \
213 } else { \
214 INSERT_STRING(parent, element, NULL); \
218 #define INSERT_TMPTR(parent, element, tmPtr) { \
219 if (NULL != (tmPtr)) { \
220 char *buffer = NULL; \
221 error = tm2datestring(tmPtr, &buffer); \
222 if (error) { \
223 free(buffer); \
224 goto leave; \
226 INSERT_STRING(parent, element, buffer); \
227 free(buffer); \
228 } else { \
229 INSERT_STRING(parent, element, NULL); \
233 #define INSERT_ELEMENT(child, parent, element) \
235 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
236 if (NULL == (child)) { \
237 error = HTTP_ERROR_SERVER; \
238 goto leave; \
242 /* TODO: These functions (element_exists(), extract_...(), ...) will replace
243 * the macros (ELEMENT_EXISTS, EXTRACT_...). The compiled code will be
244 * smaller, the compilation will be faster. */
246 /* Check an element exists.
247 * @code is a static output ISDS error code
248 * @error_message is a reallocated output ISDS error message
249 * @xpath_ctx is a current XPath context
250 * @element_name is name of an element to check
251 * @allow_multiple is false to require exactly one element. True to require
252 * one or more elements.
253 * @return HTTP_ERROR_SUCCESS or an appropriate error code. */
254 static http_error element_exists(const char **code, char **message,
255 xmlXPathContextPtr xpath_ctx, const char *element_name,
256 _Bool allow_multiple) {
257 xmlXPathObjectPtr result = NULL;
259 result = xmlXPathEvalExpression(BAD_CAST element_name, xpath_ctx);
260 if (NULL == result) {
261 return HTTP_ERROR_SERVER;
263 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
264 xmlXPathFreeObject(result);
265 *code = "9999";
266 test_asprintf(message, "Element %s does not exist", element_name);
267 return HTTP_ERROR_CLIENT;
268 } else {
269 if (!allow_multiple && result->nodesetval->nodeNr > 1) {
270 xmlXPathFreeObject(result);
271 *code = "9999";
272 test_asprintf(message, "Multiple %s element", element_name);
273 return HTTP_ERROR_CLIENT;
276 xmlXPathFreeObject(result);
278 return HTTP_ERROR_SUCCESS;
282 /* Extract @element_name's value as a string.
283 * @code is a static output ISDS error code
284 * @error_message is a reallocated output ISDS error message
285 * @xpath_ctx is a current XPath context
286 * @element_name is name of a element whose child text node to extract
287 * @string is the extraced allocated string value, or NULL if empty or the
288 * element does not exist.
289 * @return HTTP_ERROR_SUCCESS or an appropriate error code. */
290 static http_error extract_string(const char **code, char **message,
291 xmlXPathContextPtr xpath_ctx, const char *element_name,
292 char **string) {
293 http_error error = HTTP_ERROR_SUCCESS;
294 xmlXPathObjectPtr result = NULL;
295 char *buffer = NULL;
297 if (-1 == test_asprintf(&buffer, "%s/text()", element_name)) {
298 error = HTTP_ERROR_SERVER;
299 goto leave;
301 result = xmlXPathEvalExpression(BAD_CAST buffer, xpath_ctx);
302 free(buffer);
303 if (NULL == result) {
304 error = HTTP_ERROR_SERVER;
305 goto leave;
307 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
308 if (result->nodesetval->nodeNr > 1) {
309 xmlXPathFreeObject(result);
310 *code = "9999";
311 test_asprintf(message, "Multiple %s element", element_name);
312 error = HTTP_ERROR_CLIENT;
313 goto leave;
315 *string = (char *) \
316 xmlXPathCastNodeSetToString(result->nodesetval);
317 if (!(*string)) {
318 xmlXPathFreeObject(result);
319 error = HTTP_ERROR_SERVER;
320 goto leave;
323 xmlXPathFreeObject(result);
325 leave:
326 return error;
330 /* Checks an @element_name's value is an @expected_value string.
331 * @code is a static output ISDS error code
332 * @error_message is a reallocated output ISDS error message
333 * @xpath_ctx is a current XPath context
334 * @element_name is name of a element to check
335 * @must_exist is true if the @element_name must exist even if @expected_value
336 * is NULL.
337 * @expected_value is an expected string value
338 * @return HTTP_ERROR_SUCCESS if the @element_name element's value is
339 * @expected_value. HTTP_ERROR_CLIENT if not equaled, HTTP_ERROR_SERVER if an
340 * internal error occured. */
341 static http_error element_equals_string(const char **code, char **message,
342 xmlXPathContextPtr xpath_ctx, const char *element_name,
343 _Bool must_exist, const char *expected_value) {
344 http_error error = HTTP_ERROR_SUCCESS;
345 char *string = NULL;
347 if (must_exist) {
348 error = element_exists(code, message, xpath_ctx, element_name, 0);
349 if (HTTP_ERROR_SUCCESS != error)
350 goto leave;
353 error = extract_string(code, message, xpath_ctx, element_name, &string);
354 if (HTTP_ERROR_SUCCESS != error)
355 goto leave;
357 if (NULL != expected_value) {
358 if (NULL == string) {
359 *code = "9999";
360 test_asprintf(message, "Empty %s element", element_name);
361 error = HTTP_ERROR_CLIENT;
362 goto leave;
364 if (xmlStrcmp(BAD_CAST expected_value, BAD_CAST string)) {
365 *code = "9999";
366 test_asprintf(message,
367 "Unexpected %s element value: expected=`%s', got=`%s'",
368 element_name, expected_value, string);
369 error = HTTP_ERROR_CLIENT;
370 goto leave;
372 } else {
373 if (NULL != string && *string != '\0') {
374 *code = "9999";
375 test_asprintf(message,
376 "Unexpected %s element value: "
377 "expected empty string, got=`%s'",
378 element_name, string);
379 error = HTTP_ERROR_CLIENT;
380 goto leave;
384 leave:
385 free(string);
386 return error;
390 /* Checks an @element_name's value is an @expected_value integer.
391 * @code is a static output ISDS error code
392 * @error_message is a reallocated output ISDS error message
393 * @xpath_ctx is a current XPath context
394 * @element_name is name of a element to check
395 * @must_exist is true if the @element_name must exist even if @expected_value
396 * is NULL.
397 * @expected_value is an expected integer value.
398 * @return HTTP_ERROR_SUCCESS if the @element_name element's value is
399 * @expected_value. HTTP_ERROR_CLIENT if not equaled, HTTP_ERROR_SERVER if an
400 * internal error occured. */
401 static http_error element_equals_integer(const char **code, char **message,
402 xmlXPathContextPtr xpath_ctx, const char *element_name,
403 _Bool must_exist, const long int *expected_value) {
404 http_error error = HTTP_ERROR_SUCCESS;
405 char *string = NULL;
406 long int number;
407 char *endptr;
409 if (must_exist) {
410 error = element_exists(code, message, xpath_ctx, element_name, 0);
411 if (HTTP_ERROR_SUCCESS != error)
412 goto leave;
415 error = extract_string(code, message, xpath_ctx, element_name, &string);
416 if (HTTP_ERROR_SUCCESS != error)
417 goto leave;
419 if (NULL != expected_value) {
420 if (NULL == string) {
421 *code = "9999";
422 test_asprintf(message, "Empty %s element", element_name);
423 error = HTTP_ERROR_CLIENT;
424 goto leave;
426 number = strtol(string, &endptr, 10);
427 if (*endptr != '\0') {
428 *code = "9999";
429 test_asprintf(message,
430 "%s element value is not a valid integer: %s",
431 element_name, string);
432 error = HTTP_ERROR_CLIENT;
433 goto leave;
435 if (number == LONG_MIN || number == LONG_MAX) { \
436 *code = "9999";
437 test_asprintf(message, \
438 "%s element value is out of range of long int: %s",
439 element_name, string);
440 error = HTTP_ERROR_SERVER;
441 goto leave;
443 free(string); string = NULL;
444 if (number != *expected_value) {
445 *code = "9999";
446 test_asprintf(message,
447 "Unexpected %s element value: expected=`%ld', got=`%ld'",
448 element_name, *expected_value, number);
449 error = HTTP_ERROR_CLIENT;
450 goto leave;
452 } else {
453 if (NULL != string && *string != '\0') {
454 *code = "9999";
455 test_asprintf(message,
456 "Unexpected %s element value: expected no text node, got=`%s'",
457 element_name, string);
458 error = HTTP_ERROR_CLIENT;
459 goto leave;
463 leave:
464 free(string);
465 return error;
469 /* Checks an @element_name's value is an @expected_value boolean.
470 * @code is a static output ISDS error code
471 * @error_message is an reallocated output ISDS error message
472 * @xpath_ctx is a current XPath context
473 * @element_name is name of a element to check
474 * @must_exist is true if the @element_name must exist even if @expected_value
475 * is NULL.
476 * @expected_value is an expected boolean value
477 * @return HTTP_ERROR_SUCCESS if the @element_name element's value is
478 * @expected_value. HTTP_ERROR_CLIENT if not equaled, HTTP_ERROR_SERVER if an
479 * internal error occured. */
480 static http_error element_equals_boolean(const char **code, char **message,
481 xmlXPathContextPtr xpath_ctx, const char *element_name,
482 _Bool must_exist, const _Bool *expected_value) {
483 http_error error = HTTP_ERROR_SUCCESS;
484 char *string = NULL;
485 _Bool value;
487 if (must_exist) {
488 error = element_exists(code, message, xpath_ctx, element_name, 0);
489 if (HTTP_ERROR_SUCCESS != error)
490 goto leave;
493 error = extract_string(code, message, xpath_ctx, element_name, &string);
494 if (HTTP_ERROR_SUCCESS != error)
495 goto leave;
497 if (NULL != expected_value) {
498 if (NULL == string) {
499 *code = "9999";
500 test_asprintf(message, "Empty %s element", element_name);
501 error = HTTP_ERROR_CLIENT;
502 goto leave;
504 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") ||
505 !xmlStrcmp((xmlChar *)string, BAD_CAST "1"))
506 value = 1;
507 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") ||
508 !xmlStrcmp((xmlChar *)string, BAD_CAST "0"))
509 value = 0;
510 else {
511 *code = "9999";
512 test_asprintf(message,
513 "%s element value is not a valid boolean: %s",
514 element_name, string);
515 error = HTTP_ERROR_CLIENT;
516 goto leave;
518 if (*expected_value != value) {
519 *code = "9999";
520 test_asprintf(message,
521 "Unexpected %s element value: expected=%d, got=%d",
522 element_name, *expected_value, string);
523 error = HTTP_ERROR_CLIENT;
524 goto leave;
526 } else {
527 if (NULL != string && *string != '\0') {
528 *code = "9999";
529 test_asprintf(message,
530 "Unexpected %s element value: "
531 "expected empty string, got=`%s'",
532 element_name, string);
533 error = HTTP_ERROR_CLIENT;
534 goto leave;
538 leave:
539 free(string);
540 return error;
544 /* Insert dmStatus or similar subtree
545 * @parent is element to insert to
546 * @dm is true for dmStatus, otherwise dbStatus
547 * @code is status code as string
548 * @message is UTF-8 encoded message
549 * @db_ref_number is optinal reference number propagated if not @dm
550 * @return 0 on success, otherwise non-0. */
551 static http_error insert_isds_status(xmlNodePtr parent, _Bool dm,
552 const xmlChar *code, const xmlChar *message,
553 const xmlChar *db_ref_number) {
554 http_error error = HTTP_ERROR_SUCCESS;
555 xmlNodePtr status;
557 if (NULL == code || NULL == message) {
558 error = HTTP_ERROR_SERVER;
559 goto leave;
562 INSERT_ELEMENT(status, parent, (dm) ? "dmStatus" : "dbStatus");
563 INSERT_STRING(status, (dm) ? "dmStatusCode" : "dbStatusCode", code);
564 INSERT_STRING(status, (dm) ? "dmStatusMessage" : "dbStatusMessage", message);
565 if (!dm && NULL != db_ref_number) {
566 INSERT_STRING(status, "dbStatusRefNumber", db_ref_number);
569 leave:
570 return error;
574 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
575 static http_error tm2datestring(const struct tm *time, char **string) {
576 if (NULL == time || NULL == string) return HTTP_ERROR_SERVER;
578 if (-1 == test_asprintf(string, "%d-%02d-%02d",
579 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
580 return HTTP_ERROR_SERVER;
582 return HTTP_ERROR_SUCCESS;
586 /* Convert struct timeval *@time to UTF-8 ISO 8601 date-time @string. It
587 * respects the @time microseconds too. */
588 static http_error timeval2timestring(const struct timeval *time,
589 char **string) {
590 struct tm broken;
592 if (!time || !string) return HTTP_ERROR_SERVER;
594 if (!gmtime_r(&time->tv_sec, &broken)) return HTTP_ERROR_SERVER;
595 if (time->tv_usec < 0 || time->tv_usec > 999999) return HTTP_ERROR_SERVER;
597 /* TODO: small negative year should be formatted as "-0012". This is not
598 * true for glibc "%04d". We should implement it.
599 * time->tv_usec type is su_seconds_t which is required to be signed
600 * integer to accomodate values from range [-1, 1000000].
601 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
602 if (-1 == test_asprintf(string,
603 "%04d-%02d-%02dT%02d:%02d:%02d.%06" PRIdMAX,
604 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
605 broken.tm_hour, broken.tm_min, broken.tm_sec,
606 (intmax_t)time->tv_usec))
607 return HTTP_ERROR_SERVER;
609 return HTTP_ERROR_SUCCESS;
613 /* Compare dates represented by pointer to struct tm.
614 * @return 0 if equalued, non-0 otherwise. */
615 static int datecmp(const struct tm *a, const struct tm *b) {
616 if (NULL == a && b == NULL) return 0;
617 if ((NULL == a && b != NULL) || (NULL != a && b == NULL)) return 1;
618 if (a->tm_year != b->tm_year) return 1;
619 if (a->tm_mon != b->tm_mon) return 1;
620 if (a->tm_mday != b->tm_mday) return 1;
621 return 0;
625 /* Implement DummyOperation */
626 static http_error service_DummyOperation(
627 xmlXPathContextPtr xpath_ctx,
628 xmlNodePtr isds_response,
629 const void *arguments) {
630 (void)xpath_ctx;
631 (void)arguments;
633 return insert_isds_status(isds_response, 1, BAD_CAST "0000",
634 BAD_CAST "Success", NULL);
638 /* Implement Re-signISDSDocument.
639 * It sends document from request back.
640 * @arguments is pointer to struct arguments_DS_Dz_ResignISDSDocument */
641 static http_error service_ResignISDSDocument(
642 xmlXPathContextPtr xpath_ctx,
643 xmlNodePtr isds_response,
644 const void *arguments) {
645 http_error error = HTTP_ERROR_SUCCESS;
646 const char *code = "9999";
647 char *message = NULL;
648 const struct arguments_DS_Dz_ResignISDSDocument *configuration =
649 (const struct arguments_DS_Dz_ResignISDSDocument *)arguments;
650 char *data = NULL;
652 if (NULL == configuration || NULL == configuration->status_code ||
653 NULL == configuration->status_message) {
654 error = HTTP_ERROR_SERVER;
655 goto leave;
658 EXTRACT_STRING("isds:dmDoc", data);
659 if (NULL == data) {
660 message = strdup("Missing isds:dmDoc");
661 error = HTTP_ERROR_CLIENT;
662 goto leave;
666 /* dmResultDoc is mandatory in response */
667 if (xmlStrcmp(BAD_CAST configuration->status_code, BAD_CAST "0000")) {
668 free(data);
669 data = NULL;
671 INSERT_STRING(isds_response, "dmResultDoc", data);
673 if (configuration->valid_to != NULL) {
674 error = tm2datestring(configuration->valid_to, &data);
675 if (error) {
676 message = strdup("Could not format date");
677 goto leave;
679 INSERT_STRING(isds_response, "dmValidTo", data);
682 code = configuration->status_code;
683 message = strdup(configuration->status_message);
685 leave:
686 if (HTTP_ERROR_SERVER != error) {
687 http_error next_error = insert_isds_status(isds_response, 1,
688 BAD_CAST code, BAD_CAST message, NULL);
689 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
691 free(data);
692 free(message);
693 return error;
697 /* Implement EraseMessage.
698 * @arguments is pointer to struct arguments_DS_DsManage_ChangeISDSPassword */
699 static http_error service_EraseMessage(
700 xmlXPathContextPtr xpath_ctx,
701 xmlNodePtr isds_response,
702 const void *arguments) {
703 http_error error = HTTP_ERROR_SUCCESS;
704 char *code = "9999", *message = NULL;
705 const struct arguments_DS_Dx_EraseMessage *configuration =
706 (const struct arguments_DS_Dx_EraseMessage *)arguments;
707 char *message_id = NULL;
708 _Bool *incoming = NULL;
710 if (NULL == configuration || NULL == configuration->message_id) {
711 error = HTTP_ERROR_SERVER;
712 goto leave;
715 EXTRACT_STRING("isds:dmID", message_id);
716 if (NULL == message_id) {
717 message = strdup("Missing isds:dmID");
718 error = HTTP_ERROR_CLIENT;
719 goto leave;
721 EXTRACT_BOOLEAN("isds:dmIncoming", incoming);
722 if (NULL == incoming) {
723 message = strdup("Missing isds:dmIncoming");
724 error = HTTP_ERROR_CLIENT;
725 goto leave;
728 if (xmlStrcmp((const xmlChar *) configuration->message_id,
729 (const xmlChar *) message_id)) {
730 code = "1219";
731 message = strdup("Message is not in the long term storage");
732 error = HTTP_ERROR_CLIENT;
733 goto leave;
735 if (configuration->incoming != *incoming) {
736 code = "1219";
737 message = strdup("Message direction mismatches");
738 error = HTTP_ERROR_CLIENT;
739 goto leave;
742 code = "0000";
743 message = strdup("Success");
744 leave:
745 if (HTTP_ERROR_SERVER != error) {
746 http_error next_error = insert_isds_status(isds_response, 1,
747 BAD_CAST code, BAD_CAST message, NULL);
748 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
750 free(incoming);
751 free(message_id);
752 free(message);
753 return error;
757 /* Insert list of credit info as XSD:tCiRecord XML tree.
758 * @isds_response is XML node with the response
759 * @history is list of struct server_credit_event. If NULL, no ciRecord XML
760 * subtree will be created. */
761 static http_error insert_ciRecords(xmlNodePtr isds_response,
762 const struct server_list *history) {
763 http_error error = HTTP_ERROR_SUCCESS;
764 xmlNodePtr records, record;
766 if (NULL == isds_response) return HTTP_ERROR_SERVER;
767 if (NULL == history) return HTTP_ERROR_SUCCESS;
769 INSERT_ELEMENT(records, isds_response, "ciRecords");
770 for (const struct server_list *item = history; NULL != item;
771 item = item->next) {
772 const struct server_credit_event *event =
773 (struct server_credit_event*)item->data;
775 INSERT_ELEMENT(record, records, "ciRecord");
776 if (NULL == event) continue;
778 INSERT_TIMEVALPTR(record, "ciEventTime", event->time);
779 switch(event->type) {
780 case SERVER_CREDIT_CHARGED:
781 INSERT_STRING(record, "ciEventType", "1");
782 INSERT_STRING(record, "ciTransID",
783 event->details.charged.transaction);
784 break;
785 case SERVER_CREDIT_DISCHARGED:
786 INSERT_STRING(record, "ciEventType", "2");
787 INSERT_STRING(record, "ciTransID",
788 event->details.discharged.transaction);
789 break;
790 case SERVER_CREDIT_MESSAGE_SENT:
791 INSERT_STRING(record, "ciEventType", "3");
792 INSERT_STRING(record, "ciRecipientID",
793 event->details.message_sent.recipient);
794 INSERT_STRING(record, "ciPDZID",
795 event->details.message_sent.message_id);
796 break;
797 case SERVER_CREDIT_STORAGE_SET:
798 INSERT_STRING(record, "ciEventType", "4");
799 INSERT_LONGINTPTR(record, "ciNewCapacity",
800 &event->details.storage_set.new_capacity);
801 INSERT_TMPTR(record, "ciNewFrom",
802 event->details.storage_set.new_valid_from);
803 INSERT_TMPTR(record, "ciNewTo",
804 event->details.storage_set.new_valid_to);
805 INSERT_LONGINTPTR(record, "ciOldCapacity",
806 event->details.storage_set.old_capacity);
807 INSERT_TMPTR(record, "ciOldFrom",
808 event->details.storage_set.old_valid_from);
809 INSERT_TMPTR(record, "ciOldTo",
810 event->details.storage_set.old_valid_to);
811 INSERT_STRING(record, "ciDoneBy",
812 event->details.storage_set.initiator);
813 break;
814 case SERVER_CREDIT_EXPIRED:
815 INSERT_STRING(record, "ciEventType", "5");
816 break;
817 default:
818 error = HTTP_ERROR_SERVER;
819 goto leave;
821 INSERT_LONGINTPTR(record, "ciCreditChange", &event->credit_change);
822 INSERT_LONGINTPTR(record, "ciCreditAfter", &event->new_credit);
826 leave:
827 return error;
831 /* Implement DataBoxCreditInfo.
832 * @arguments is pointer to struct arguments_DS_df_DataBoxCreditInfo */
833 static http_error service_DataBoxCreditInfo(
834 xmlXPathContextPtr xpath_ctx,
835 xmlNodePtr isds_response,
836 const void *arguments) {
837 http_error error = HTTP_ERROR_SUCCESS;
838 const char *code = "9999";
839 char *message = NULL;
840 const struct arguments_DS_df_DataBoxCreditInfo *configuration =
841 (const struct arguments_DS_df_DataBoxCreditInfo *)arguments;
842 char *box_id = NULL;
843 struct tm *from_date = NULL, *to_date = NULL;
845 if (NULL == configuration || NULL == configuration->status_code ||
846 NULL == configuration->status_message) {
847 error = HTTP_ERROR_SERVER;
848 goto leave;
851 EXTRACT_STRING("isds:dbID", box_id);
852 if (NULL == box_id) {
853 message = strdup("Missing isds:dbID");
854 error = HTTP_ERROR_CLIENT;
855 goto leave;
857 if (NULL != configuration->box_id &&
858 xmlStrcmp(BAD_CAST configuration->box_id,
859 BAD_CAST box_id)) {
860 code = "9999";
861 message = strdup("Unexpected isds:dbID value");
862 error = HTTP_ERROR_CLIENT;
863 goto leave;
866 ELEMENT_EXISTS("isds:ciFromDate", 0);
867 EXTRACT_DATE("isds:ciFromDate", from_date);
868 if (datecmp(configuration->from_date, from_date)) {
869 code = "9999";
870 message = strdup("Unexpected isds:ciFromDate value");
871 error = HTTP_ERROR_CLIENT;
872 goto leave;
875 ELEMENT_EXISTS("isds:ciTodate", 0);
876 EXTRACT_DATE("isds:ciTodate", to_date);
877 if (datecmp(configuration->to_date, to_date)) {
878 code = "9999";
879 message = strdup("Unexpected isds:ciTodate value");
880 error = HTTP_ERROR_CLIENT;
881 goto leave;
884 INSERT_LONGINTPTR(isds_response, "currentCredit",
885 &configuration->current_credit);
886 INSERT_STRING(isds_response, "notifEmail", configuration->email);
887 if ((error = insert_ciRecords(isds_response, configuration->history))) {
888 goto leave;
891 code = configuration->status_code;
892 message = strdup(configuration->status_message);
893 leave:
894 if (HTTP_ERROR_SERVER != error) {
895 http_error next_error = insert_isds_status(isds_response, 0,
896 BAD_CAST code, BAD_CAST message, NULL);
897 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
899 free(box_id);
900 free(from_date);
901 free(to_date);
902 free(message);
903 return error;
907 /* Insert list of fulltext search results as XSD:tdbResultsArray XML tree.
908 * @isds_response is XML node with the response
909 * @results is list of struct server_db_result *.
910 * @create_empty_root is true to create dbResults element even if @results is
911 * empty. */
912 static http_error insert_tdbResultsArray(xmlNodePtr isds_response,
913 const struct server_list *results, _Bool create_empty_root) {
914 http_error error = HTTP_ERROR_SUCCESS;
915 xmlNodePtr root, entry;
917 if (NULL == isds_response) return HTTP_ERROR_SERVER;
919 if (NULL != results || create_empty_root)
920 INSERT_ELEMENT(root, isds_response, "dbResults");
922 if (NULL == results) return HTTP_ERROR_SUCCESS;
924 for (const struct server_list *item = results; NULL != item;
925 item = item->next) {
926 const struct server_db_result *result =
927 (struct server_db_result *)item->data;
929 INSERT_ELEMENT(entry, root, "dbResult");
930 if (NULL == result) continue;
932 INSERT_STRING(entry, "dbID", result->id);
933 INSERT_STRING(entry, "dbType", result->type);
934 INSERT_STRING(entry, "dbName", result->name);
935 INSERT_STRING(entry, "dbAddress", result->address);
936 INSERT_TMPTR(entry, "dbBiDate", result->birth_date);
937 INSERT_STRING(entry, "dbICO", result->ic);
938 INSERT_BOOLEANPTR(entry, "dbEffectiveOVM", &result->ovm);
939 INSERT_STRING(entry, "dbSendOptions", result->send_options);
942 leave:
943 return error;
947 /* Implement ISDSSearch2.
948 * @arguments is pointer to struct arguments_DS_df_ISDSSearch2 */
949 static http_error service_ISDSSearch2(
950 xmlXPathContextPtr xpath_ctx,
951 xmlNodePtr isds_response,
952 const void *arguments) {
953 http_error error = HTTP_ERROR_SUCCESS;
954 const char *code = "9999";
955 char *message = NULL;
956 const struct arguments_DS_df_ISDSSearch2 *configuration =
957 (const struct arguments_DS_df_ISDSSearch2 *)arguments;
958 char *string = NULL;
960 if (NULL == configuration || NULL == configuration->status_code ||
961 NULL == configuration->status_message) {
962 error = HTTP_ERROR_SERVER;
963 goto leave;
966 /* Check request */
967 EXTRACT_STRING("isds:searchText", string);
968 if (NULL == string) {
969 message = strdup("Missing or empty isds:searchText");
970 error = HTTP_ERROR_CLIENT;
971 goto leave;
973 if (NULL != configuration->search_text &&
974 xmlStrcmp(BAD_CAST configuration->search_text,
975 BAD_CAST string)) {
976 code = "9999";
977 message = strdup("Unexpected isds:searchText value");
978 error = HTTP_ERROR_CLIENT;
979 goto leave;
981 free(string); string = NULL;
983 error = element_equals_string(&code, &message, xpath_ctx,
984 "isds:searchType", 1, configuration->search_type);
985 if (error) goto leave;
987 error = element_equals_string(&code, &message, xpath_ctx,
988 "isds:searchScope", 1, configuration->search_scope);
989 if (error) goto leave;
991 error = element_equals_integer(&code, &message, xpath_ctx,
992 "isds:page", 1, configuration->search_page_number);
993 if (error) goto leave;
995 error = element_equals_integer(&code, &message, xpath_ctx,
996 "isds:pageSize", 1, configuration->search_page_size);
997 if (error) goto leave;
999 error = element_equals_boolean(&code, &message, xpath_ctx,
1000 "isds:highlighting", 0, configuration->search_highlighting_value);
1001 if (error) goto leave;
1003 /* Build response */
1004 if (NULL != configuration->total_count)
1005 INSERT_ULONGINTPTR(isds_response, "totalCount",
1006 configuration->total_count);
1007 if (NULL != configuration->current_count)
1008 INSERT_ULONGINTPTR(isds_response, "currentCount",
1009 configuration->current_count);
1010 if (NULL != configuration->position)
1011 INSERT_ULONGINTPTR(isds_response, "position",
1012 configuration->position);
1013 if (NULL != configuration->last_page)
1014 INSERT_BOOLEANPTR(isds_response, "lastPage",
1015 configuration->last_page);
1016 if ((error = insert_tdbResultsArray(isds_response, configuration->results,
1017 configuration->results_exists))) {
1018 goto leave;
1021 code = configuration->status_code;
1022 message = strdup(configuration->status_message);
1024 leave:
1025 if (HTTP_ERROR_SERVER != error) {
1026 http_error next_error = insert_isds_status(isds_response, 0,
1027 BAD_CAST code, BAD_CAST message, NULL);
1028 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
1030 free(string);
1031 free(message);
1032 return error;
1036 /* Common part for ChangeISDSPassword and ChangePasswordOTP.
1037 * @code is output pointer to static string
1038 * @pass_message is output pointer to auto-allocated string
1039 * @arguments is pointer to struct arguments_DS_DsManage_ChangeISDSPassword */
1040 static http_error check_passwd(
1041 const char *username, const char *current_password,
1042 xmlXPathContextPtr xpath_ctx,
1043 char **code, char **pass_message) {
1044 http_error error = HTTP_ERROR_SUCCESS;
1045 char *message = NULL;
1046 char *old_password = NULL, *new_password = NULL;
1047 size_t length;
1049 if (NULL == username || NULL == current_password ||
1050 NULL == code || NULL == pass_message) {
1051 return HTTP_ERROR_SERVER;
1054 *code = "9999";
1057 /* Parse request */
1058 EXTRACT_STRING("isds:dbOldPassword", old_password);
1059 if (NULL == old_password) {
1060 message = strdup("Empty isds:dbOldPassword");
1061 error = HTTP_ERROR_CLIENT;
1062 goto leave;
1064 EXTRACT_STRING("isds:dbNewPassword", new_password);
1065 if (NULL == new_password) {
1066 message = strdup("Empty isds:dbOldPassword");
1067 error = HTTP_ERROR_CLIENT;
1068 goto leave;
1071 /* Check defined cases */
1072 if (strcmp(current_password, old_password)) {
1073 *code = "1090";
1074 message = strdup("Bad current password");
1075 error = HTTP_ERROR_CLIENT;
1076 goto leave;
1079 length = strlen(new_password);
1081 if (length < 8 || length > 32) {
1082 *code = "1066";
1083 message = strdup("Too short or too long");
1084 error = HTTP_ERROR_CLIENT;
1085 goto leave;
1089 const char lower[] = "abcdefghijklmnopqrstuvwxyz";
1090 const char upper[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
1091 const char digit[] = "0123456789";
1092 const char special[] = "!#$%&()*+,-.:=?@[]_{}|~";
1093 _Bool has_lower = 0, has_upper = 0, has_digit=0;
1095 for (size_t i = 0; i < length; i++) {
1096 if (NULL != strchr(lower, new_password[i]))
1097 has_lower = 1;
1098 else if (NULL != strchr(upper, new_password[i]))
1099 has_upper = 1;
1100 else if (NULL != strchr(digit, new_password[i]))
1101 has_digit = 1;
1102 else if (NULL == strchr(special, new_password[i])) {
1103 *code = "1079";
1104 message = strdup("Password contains forbidden character");
1105 error = HTTP_ERROR_CLIENT;
1106 goto leave;
1110 if (!has_lower || !has_upper || !has_digit) {
1111 *code = "1080";
1112 message = strdup("Password does not contain lower cased letter, "
1113 "upper cased letter and a digit");
1114 error = HTTP_ERROR_CLIENT;
1115 goto leave;
1119 if (!strcmp(old_password, new_password)) {
1120 *code = "1067";
1121 message = strdup("New password same as current one");
1122 error = HTTP_ERROR_CLIENT;
1123 goto leave;
1126 if (NULL != strstr(new_password, username)) {
1127 *code = "1082";
1128 message = strdup("New password contains user ID");
1129 error = HTTP_ERROR_CLIENT;
1130 goto leave;
1133 for (size_t i = 0; i < length - 2; i++) {
1134 if (new_password[i] == new_password[i+1] &&
1135 new_password[i] == new_password[i+2]) {
1136 *code = "1083";
1137 message = strdup("Password contains sequence "
1138 "of three identical characters");
1139 error = HTTP_ERROR_CLIENT;
1140 goto leave;
1145 const char *forbidden_prefix[] = { "qwert", "asdgf", "12345" };
1146 for (size_t i = 0; i < sizeof(forbidden_prefix)/sizeof(*forbidden_prefix);
1147 i++) {
1148 if (!strncmp(new_password, forbidden_prefix[i],
1149 strlen(forbidden_prefix[i]))) {
1150 *code = "1083";
1151 message = strdup("Password has forbidden prefix");
1152 error = HTTP_ERROR_CLIENT;
1153 goto leave;
1158 *code = "0000";
1159 message = strdup("Success");
1160 leave:
1161 free(old_password);
1162 free(new_password);
1163 *pass_message = message;
1164 return error;
1168 /* Implement ChangeISDSPassword.
1169 * @arguments is pointer to struct arguments_DS_DsManage_ChangeISDSPassword */
1170 static http_error service_ChangeISDSPassword(
1171 xmlXPathContextPtr xpath_ctx,
1172 xmlNodePtr isds_response,
1173 const void *arguments) {
1174 http_error error = HTTP_ERROR_SUCCESS;
1175 char *code = "9999", *message = NULL;
1176 const struct arguments_DS_DsManage_ChangeISDSPassword *configuration =
1177 (const struct arguments_DS_DsManage_ChangeISDSPassword *)arguments;
1179 if (NULL == configuration || NULL == configuration->username ||
1180 NULL == configuration->current_password) {
1181 error = HTTP_ERROR_SERVER;
1182 goto leave;
1185 /* Check for common password rules */
1186 error = check_passwd(
1187 configuration->username, configuration->current_password,
1188 xpath_ctx, &code, &message);
1190 leave:
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;
1196 free(message);
1197 return error;
1201 /* Implement ChangePasswordOTP.
1202 * @arguments is pointer to struct
1203 * arguments_asws_changePassword_ChangePasswordOTP */
1204 static http_error service_ChangePasswordOTP(
1205 xmlXPathContextPtr xpath_ctx,
1206 xmlNodePtr isds_response,
1207 const void *arguments) {
1208 http_error error = HTTP_ERROR_SUCCESS;
1209 char *code = "9999", *message = NULL;
1210 const struct arguments_asws_changePassword_ChangePasswordOTP *configuration
1211 = (const struct arguments_asws_changePassword_ChangePasswordOTP *)
1212 arguments;
1213 char *method = NULL;
1215 if (NULL == configuration || NULL == configuration->username ||
1216 NULL == configuration->current_password) {
1217 error = HTTP_ERROR_SERVER;
1218 goto leave;
1221 /* Chek for OTP method */
1222 EXTRACT_STRING("isds:dbOTPType", method);
1223 if (NULL == method) {
1224 message = strdup("Empty isds:dbOTPType");
1225 error = HTTP_ERROR_CLIENT;
1226 goto leave;
1228 if ((configuration->method == AUTH_OTP_HMAC && strcmp(method, "HOTP")) ||
1229 (configuration->method == AUTH_OTP_TIME && strcmp(method, "TOTP"))) {
1230 message = strdup("isds:dbOTPType does not match OTP method");
1231 error = HTTP_ERROR_CLIENT;
1232 goto leave;
1235 /* Check for common password rules */
1236 error = check_passwd(
1237 configuration->username, configuration->current_password,
1238 xpath_ctx, &code, &message);
1240 leave:
1241 if (HTTP_ERROR_SERVER != error) {
1242 http_error next_error = insert_isds_status(isds_response, 0,
1243 BAD_CAST code, BAD_CAST message,
1244 BAD_CAST configuration->reference_number);
1245 if (HTTP_ERROR_SUCCESS != next_error) error = next_error;
1247 free(message);
1248 free(method);
1249 return error;
1253 /* Implement SendSMSCode.
1254 * @arguments is pointer to struct arguments_asws_changePassword_SendSMSCode */
1255 static http_error service_SendSMSCode(
1256 xmlXPathContextPtr xpath_ctx,
1257 xmlNodePtr isds_response,
1258 const void *arguments) {
1259 const struct arguments_asws_changePassword_SendSMSCode *configuration
1260 = (const struct arguments_asws_changePassword_SendSMSCode *)
1261 arguments;
1262 (void)xpath_ctx;
1264 if (NULL == configuration || NULL == configuration->status_code ||
1265 NULL == configuration->status_message) {
1266 return HTTP_ERROR_SERVER;
1269 return insert_isds_status(isds_response, 0,
1270 BAD_CAST configuration->status_code,
1271 BAD_CAST configuration->status_message,
1272 BAD_CAST configuration->reference_number);
1276 /* List of implemented services */
1277 static struct service services[] = {
1278 { SERVICE_DS_Dz_DummyOperation,
1279 "DS/dz", BAD_CAST ISDS_NS, BAD_CAST "DummyOperation",
1280 service_DummyOperation },
1281 { SERVICE_DS_Dz_ResignISDSDocument,
1282 "DS/dz", BAD_CAST ISDS_NS, BAD_CAST "Re-signISDSDocument",
1283 service_ResignISDSDocument },
1284 { SERVICE_DS_df_DataBoxCreditInfo,
1285 "DS/df", BAD_CAST ISDS_NS, BAD_CAST "DataBoxCreditInfo",
1286 service_DataBoxCreditInfo },
1287 { SERVICE_DS_df_ISDSSearch2,
1288 "DS/df", BAD_CAST ISDS_NS, BAD_CAST "ISDSSearch2",
1289 service_ISDSSearch2 },
1290 { SERVICE_DS_DsManage_ChangeISDSPassword,
1291 "DS/DsManage", BAD_CAST ISDS_NS, BAD_CAST "ChangeISDSPassword",
1292 service_ChangeISDSPassword },
1293 { SERVICE_DS_Dx_EraseMessage,
1294 "DS/dx", BAD_CAST ISDS_NS, BAD_CAST "EraseMessage",
1295 service_EraseMessage },
1296 { SERVICE_asws_changePassword_ChangePasswordOTP,
1297 "/asws/changePassword", BAD_CAST OISDS_NS, BAD_CAST "ChangePasswordOTP",
1298 service_ChangePasswordOTP },
1299 { SERVICE_asws_changePassword_SendSMSCode,
1300 "/asws/changePassword", BAD_CAST OISDS_NS, BAD_CAST "SendSMSCode",
1301 service_SendSMSCode },
1305 /* Makes known all relevant namespaces to given XPath context
1306 * @xpath_ctx is XPath context
1307 * @otp_ns selects name space for the request and response know as "isds".
1308 * Use true for OTP-authenticated password change services, otherwise false.
1309 * @message_ns selects proper message name space. Unsigned and signed
1310 * messages and delivery info's differ in prefix and URI.
1311 * @return 0 in success, otherwise not 0. */
1312 static int register_namespaces(xmlXPathContextPtr xpath_ctx,
1313 const _Bool otp_ns, const message_ns_type message_ns) {
1314 const xmlChar *service_namespace = NULL;
1315 const xmlChar *message_namespace = NULL;
1317 if (!xpath_ctx) return -1;
1319 if (otp_ns) {
1320 service_namespace = BAD_CAST OISDS_NS;
1321 } else {
1322 service_namespace = BAD_CAST ISDS_NS;
1325 switch(message_ns) {
1326 case MESSAGE_NS_1:
1327 message_namespace = BAD_CAST ISDS1_NS; break;
1328 case MESSAGE_NS_UNSIGNED:
1329 message_namespace = BAD_CAST ISDS_NS; break;
1330 case MESSAGE_NS_SIGNED_INCOMING:
1331 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
1332 case MESSAGE_NS_SIGNED_OUTGOING:
1333 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
1334 case MESSAGE_NS_SIGNED_DELIVERY:
1335 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
1336 default:
1337 return -1;
1340 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
1341 return -1;
1342 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", service_namespace))
1343 return -1;
1344 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
1345 return -1;
1346 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
1347 return -1;
1348 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
1349 return -1;
1350 return 0;
1354 /* Parse soap request, pass it to service endpoint and respond to it.
1355 * It sends final HTTP response. */
1356 void soap(const struct http_connection *connection,
1357 const struct service_configuration *configuration,
1358 const void *request, size_t request_length, const char *end_point) {
1359 xmlDocPtr request_doc = NULL;
1360 xmlXPathContextPtr xpath_ctx = NULL;
1361 xmlXPathObjectPtr request_soap_body = NULL;
1362 xmlNodePtr isds_request = NULL; /* pointer only */
1363 _Bool service_handled = 0, service_passed = 0;
1364 xmlDocPtr response_doc = NULL;
1365 xmlNodePtr response_soap_envelope = NULL, response_soap_body = NULL,
1366 isds_response = NULL;
1367 xmlNsPtr soap_ns = NULL, isds_ns = NULL;
1368 char *response_name = NULL;
1369 xmlBufferPtr http_response_body = NULL;
1370 xmlSaveCtxtPtr save_ctx = NULL;
1373 if (NULL == configuration) {
1374 http_send_response_500(connection,
1375 "Second argument of soap() is NULL");
1376 return;
1379 if (NULL == request || request_length == 0) {
1380 http_send_response_400(connection, "Client sent empty body");
1381 return;
1384 request_doc = xmlParseMemory(request, request_length);
1385 if (NULL == request_doc) {
1386 http_send_response_400(connection, "Client sent invalid XML document");
1387 return;
1390 xpath_ctx = xmlXPathNewContext(request_doc);
1391 if (NULL == xpath_ctx) {
1392 xmlFreeDoc(request_doc);
1393 http_send_response_500(connection, "Could not create XPath context");
1394 return;
1397 if (register_namespaces(xpath_ctx, 0, MESSAGE_NS_UNSIGNED)) {
1398 xmlXPathFreeContext(xpath_ctx);
1399 xmlFreeDoc(request_doc);
1400 http_send_response_500(connection,
1401 "Could not register name spaces to the XPath context");
1402 return;
1405 /* Get SOAP Body */
1406 request_soap_body = xmlXPathEvalExpression(
1407 BAD_CAST "/soap:Envelope/soap:Body", xpath_ctx);
1408 if (NULL == request_soap_body) {
1409 xmlXPathFreeContext(xpath_ctx);
1410 xmlFreeDoc(request_doc);
1411 http_send_response_400(connection, "Client sent invalid SOAP request");
1412 return;
1414 if (xmlXPathNodeSetIsEmpty(request_soap_body->nodesetval)) {
1415 xmlXPathFreeObject(request_soap_body);
1416 xmlXPathFreeContext(xpath_ctx);
1417 xmlFreeDoc(request_doc);
1418 http_send_response_400(connection,
1419 "SOAP request does not contain SOAP Body element");
1420 return;
1422 if (request_soap_body->nodesetval->nodeNr > 1) {
1423 xmlXPathFreeObject(request_soap_body);
1424 xmlXPathFreeContext(xpath_ctx);
1425 xmlFreeDoc(request_doc);
1426 http_send_response_400(connection,
1427 "SOAP response has more than one Body element");
1428 return;
1430 isds_request = request_soap_body->nodesetval->nodeTab[0]->children;
1431 if (isds_request->next != NULL) {
1432 xmlXPathFreeObject(request_soap_body);
1433 xmlXPathFreeContext(xpath_ctx);
1434 xmlFreeDoc(request_doc);
1435 http_send_response_400(connection, "SOAP body has more than one child");
1436 return;
1438 if (isds_request->type != XML_ELEMENT_NODE || isds_request->ns == NULL ||
1439 NULL == isds_request->ns->href) {
1440 xmlXPathFreeObject(request_soap_body);
1441 xmlXPathFreeContext(xpath_ctx);
1442 xmlFreeDoc(request_doc);
1443 http_send_response_400(connection,
1444 "SOAP body does not contain a name-space-qualified element");
1445 return;
1448 /* Build SOAP response envelope */
1449 response_doc = xmlNewDoc(BAD_CAST "1.0");
1450 if (!response_doc) {
1451 http_send_response_500(connection,
1452 "Could not build SOAP response document");
1453 goto leave;
1455 response_soap_envelope = xmlNewNode(NULL, BAD_CAST "Envelope");
1456 if (!response_soap_envelope) {
1457 http_send_response_500(connection,
1458 "Could not build SOAP response envelope");
1459 goto leave;
1461 xmlDocSetRootElement(response_doc, response_soap_envelope);
1462 /* Only this way we get namespace definition as @xmlns:soap,
1463 * otherwise we get namespace prefix without definition */
1464 soap_ns = xmlNewNs(response_soap_envelope, BAD_CAST SOAP_NS, NULL);
1465 if(NULL == soap_ns) {
1466 http_send_response_500(connection, "Could not create SOAP name space");
1467 goto leave;
1469 xmlSetNs(response_soap_envelope, soap_ns);
1470 response_soap_body = xmlNewChild(response_soap_envelope, NULL,
1471 BAD_CAST "Body", NULL);
1472 if (!response_soap_body) {
1473 http_send_response_500(connection,
1474 "Could not add Body to SOAP response envelope");
1475 goto leave;
1477 /* Append ISDS response element */
1478 if (-1 == test_asprintf(&response_name, "%s%s", isds_request->name,
1479 "Response")) {
1480 http_send_response_500(connection,
1481 "Could not buld ISDS resposne element name");
1482 goto leave;
1484 isds_response = xmlNewChild(response_soap_body, NULL,
1485 BAD_CAST response_name, NULL);
1486 free(response_name);
1487 if (NULL == isds_response) {
1488 http_send_response_500(connection,
1489 "Could not add ISDS response element to SOAP response body");
1490 goto leave;
1492 isds_ns = xmlNewNs(isds_response, isds_request->ns->href, NULL);
1493 if(NULL == isds_ns) {
1494 http_send_response_500(connection,
1495 "Could not create a name space for the response body");
1496 goto leave;
1498 xmlSetNs(isds_response, isds_ns);
1500 /* Dispatch request to service */
1501 for (size_t i = 0; i < sizeof(services)/sizeof(services[0]); i++) {
1502 if (!strcmp(services[i].end_point, end_point) &&
1503 !xmlStrcmp(services[i].name_space, isds_request->ns->href) &&
1504 !xmlStrcmp(services[i].name, isds_request->name)) {
1505 /* Check if the configuration is enabled and find configuration */
1506 for (const struct service_configuration *service = configuration;
1507 service->name != SERVICE_END; service++) {
1508 if (service->name == services[i].id) {
1509 service_handled = 1;
1510 if (!xmlStrcmp(services[i].name_space, BAD_CAST OISDS_NS)) {
1511 /* Alias "isds" XPath identifier to OISDS_NS */
1512 if (register_namespaces(xpath_ctx, 1,
1513 MESSAGE_NS_UNSIGNED)) {
1514 http_send_response_500(connection,
1515 "Could not register name spaces to the "
1516 "XPath context");
1517 break;
1520 xpath_ctx->node = isds_request;
1521 if (HTTP_ERROR_SERVER != services[i].function(
1522 xpath_ctx,
1523 isds_response,
1524 service->arguments)) {
1525 service_passed = 1;
1526 } else {
1527 http_send_response_500(connection,
1528 "Internal server error while processing "
1529 "ISDS request");
1533 break;
1537 /* Send response */
1538 if (service_passed) {
1539 /* Serialize the SOAP response */
1540 http_response_body = xmlBufferCreate();
1541 if (NULL == http_response_body) {
1542 http_send_response_500(connection,
1543 "Could not create xmlBuffer for response serialization");
1544 goto leave;
1546 /* Last argument 1 means format the XML tree. This is pretty but it breaks
1547 * XML document transport as it adds text nodes (indentiation) between
1548 * elements. */
1549 save_ctx = xmlSaveToBuffer(http_response_body, "UTF-8", 0);
1550 if (NULL == save_ctx) {
1551 http_send_response_500(connection, "Could not create XML serializer");
1552 goto leave;
1554 /* XXX: According LibXML documentation, this function does not return
1555 * meaningful value yet */
1556 xmlSaveDoc(save_ctx, response_doc);
1557 if (-1 == xmlSaveFlush(save_ctx)) {
1558 http_send_response_500(connection,
1559 "Could not serialize SOAP response");
1560 goto leave;
1563 http_send_response_200(connection, http_response_body->content,
1564 http_response_body->use, soap_mime_type);
1567 leave:
1568 xmlSaveClose(save_ctx);
1569 xmlBufferFree(http_response_body);
1571 xmlFreeDoc(response_doc);
1573 xmlXPathFreeObject(request_soap_body);
1574 xmlXPathFreeContext(xpath_ctx);
1575 xmlFreeDoc(request_doc);
1577 if (!service_handled) {
1578 http_send_response_500(connection,
1579 "Requested ISDS service not implemented");