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