Implement DisableDataBoxExternally
[libisds.git] / src / isds.c
blob4117c3a73ad87351ed2556340de0a2949a233e13
1 #define _XOPEN_SOURCE 500 /* strdup from string.h, strptime from time.h */
2 #include <stdlib.h>
3 #include <string.h>
4 #include <stdio.h>
5 #include <stdarg.h>
6 #include <ctype.h>
7 #include <stdint.h>
8 #include "isds_priv.h"
9 #include "utils.h"
10 #include "soap.h"
11 #include "validator.h"
12 #include "crypto.h"
13 #include <gpg-error.h> /* Because of ksba or gpgme */
14 #include "physxml.h"
17 /* Free isds_list with all member data.
18 * @list list to free, on return will be NULL */
19 void isds_list_free(struct isds_list **list) {
20 struct isds_list *item, *next_item;
22 if (!list || !*list) return;
24 for(item = *list; item; item = next_item) {
25 (item->destructor)(&(item->data));
26 next_item = item->next;
27 free(item);
30 *list = NULL;
34 /* Deallocate structure isds_hash and NULL it.
35 * @hash hash to to free */
36 void isds_hash_free(struct isds_hash **hash) {
37 if(!hash || !*hash) return;
38 free((*hash)->value);
39 zfree((*hash));
43 /* Deallocate structure isds_PersonName recursively and NULL it */
44 static void isds_PersonName_free(struct isds_PersonName **person_name) {
45 if (!person_name || !*person_name) return;
47 free((*person_name)->pnFirstName);
48 free((*person_name)->pnMiddleName);
49 free((*person_name)->pnLastName);
50 free((*person_name)->pnLastNameAtBirth);
52 free(*person_name);
53 *person_name = NULL;
57 /* Deallocate structure isds_BirthInfo recursively and NULL it */
58 static void isds_BirthInfo_free(struct isds_BirthInfo **birth_info) {
59 if (!birth_info || !*birth_info) return;
61 free((*birth_info)->biDate);
62 free((*birth_info)->biCity);
63 free((*birth_info)->biCounty);
64 free((*birth_info)->biState);
66 free(*birth_info);
67 *birth_info = NULL;
71 /* Deallocate structure isds_Address recursively and NULL it */
72 static void isds_Address_free(struct isds_Address **address) {
73 if (!address || !*address) return;
75 free((*address)->adCity);
76 free((*address)->adStreet);
77 free((*address)->adNumberInStreet);
78 free((*address)->adNumberInMunicipality);
79 free((*address)->adZipCode);
80 free((*address)->adState);
82 free(*address);
83 *address = NULL;
87 /* Deallocate structure isds_DbOwnerInfo recursively and NULL it */
88 void isds_DbOwnerInfo_free(struct isds_DbOwnerInfo **db_owner_info) {
89 if (!db_owner_info || !*db_owner_info) return;
91 free((*db_owner_info)->dbID);
92 free((*db_owner_info)->dbType);
93 free((*db_owner_info)->ic);
94 isds_PersonName_free(&((*db_owner_info)->personName));
95 free((*db_owner_info)->firmName);
96 isds_BirthInfo_free(&((*db_owner_info)->birthInfo));
97 isds_Address_free(&((*db_owner_info)->address));
98 free((*db_owner_info)->nationality);
99 free((*db_owner_info)->email);
100 free((*db_owner_info)->telNumber);
101 free((*db_owner_info)->identifier);
102 free((*db_owner_info)->registryCode);
103 free((*db_owner_info)->dbState);
104 free((*db_owner_info)->dbEffectiveOVM);
106 free(*db_owner_info);
107 *db_owner_info = NULL;
110 /* Deallocate structure isds_DbUserInfo recursively and NULL it */
111 void isds_DbUserInfo_free(struct isds_DbUserInfo **db_user_info) {
112 if (!db_user_info || !*db_user_info) return;
114 free((*db_user_info)->userID);
115 free((*db_user_info)->userType);
116 free((*db_user_info)->userPrivils);
117 isds_PersonName_free(&((*db_user_info)->personName));
118 isds_Address_free(&((*db_user_info)->address));
119 free((*db_user_info)->biDate);
120 free((*db_user_info)->ic);
121 free((*db_user_info)->firmName);
122 free((*db_user_info)->caStreet);
123 free((*db_user_info)->caCity);
124 free((*db_user_info)->caZipCode);
126 zfree(*db_user_info);
130 /* Deallocate struct isds_event recursively and NULL it */
131 void isds_event_free(struct isds_event **event) {
132 if (!event || !*event) return;
134 free((*event)->time);
135 free((*event)->type);
136 free((*event)->description);
137 zfree(*event);
141 /* Deallocate struct isds_envelope recursively and NULL it */
142 void isds_envelope_free(struct isds_envelope **envelope) {
143 if (!envelope || !*envelope) return;
145 free((*envelope)->dmID);
146 free((*envelope)->dbIDSender);
147 free((*envelope)->dmSender);
148 free((*envelope)->dmSenderAddress);
149 free((*envelope)->dmSenderType);
150 free((*envelope)->dmRecipient);
151 free((*envelope)->dmRecipientAddress);
152 free((*envelope)->dmAmbiguousRecipient);
153 free((*envelope)->dmType);
155 free((*envelope)->dmOrdinal);
156 free((*envelope)->dmMessageStatus);
157 free((*envelope)->dmDeliveryTime);
158 free((*envelope)->dmAcceptanceTime);
159 isds_hash_free(&(*envelope)->hash);
160 free((*envelope)->timestamp);
161 isds_list_free(&(*envelope)->events);
163 free((*envelope)->dmSenderOrgUnit);
164 free((*envelope)->dmSenderOrgUnitNum);
165 free((*envelope)->dbIDRecipient);
166 free((*envelope)->dmRecipientOrgUnit);
167 free((*envelope)->dmRecipientOrgUnitNum);
168 free((*envelope)->dmToHands);
169 free((*envelope)->dmAnnotation);
170 free((*envelope)->dmRecipientRefNumber);
171 free((*envelope)->dmSenderRefNumber);
172 free((*envelope)->dmRecipientIdent);
173 free((*envelope)->dmSenderIdent);
175 free((*envelope)->dmLegalTitleLaw);
176 free((*envelope)->dmLegalTitleYear);
177 free((*envelope)->dmLegalTitleSect);
178 free((*envelope)->dmLegalTitlePar);
179 free((*envelope)->dmLegalTitlePoint);
181 free((*envelope)->dmPersonalDelivery);
182 free((*envelope)->dmAllowSubstDelivery);
183 free((*envelope)->dmOVM);
185 free(*envelope);
186 *envelope = NULL;
190 /* Deallocate struct isds_message recursively and NULL it */
191 void isds_message_free(struct isds_message **message) {
192 if (!message || !*message) return;
194 free((*message)->raw);
195 isds_envelope_free(&((*message)->envelope));
196 isds_list_free(&((*message)->documents));
198 free(*message);
199 *message = NULL;
203 /* Deallocate struct isds_document recursively and NULL it */
204 void isds_document_free(struct isds_document **document) {
205 if (!document || !*document) return;
207 free((*document)->data);
208 free((*document)->dmMimeType);
209 free((*document)->dmFileGuid);
210 free((*document)->dmUpFileGuid);
211 free((*document)->dmFileDescr);
212 free((*document)->dmFormat);
214 free(*document);
215 *document = NULL;
219 /* Deallocate struct isds_message_copy recursively and NULL it */
220 void isds_message_copy_free(struct isds_message_copy **copy) {
221 if (!copy || !*copy) return;
223 free((*copy)->dbIDRecipient);
224 free((*copy)->dmRecipientOrgUnit);
225 free((*copy)->dmRecipientOrgUnitNum);
226 free((*copy)->dmToHands);
228 free((*copy)->dmStatus);
229 free((*copy)->dmID);
231 zfree(*copy);
235 /* *DUP_OR_ERROR macros needs error label */
236 #define STRDUP_OR_ERROR(new, template) { \
237 if (!template) { \
238 (new) = NULL; \
239 } else { \
240 (new) = strdup(template); \
241 if (!new) goto error; \
245 #define FLATDUP_OR_ERROR(new, template) { \
246 if (!template) { \
247 (new) = NULL; \
248 } else { \
249 (new) = malloc(sizeof(*(new))); \
250 if (!new) goto error; \
251 memcpy((new), (template), sizeof(*(template))); \
256 /* Copy structure isds_PersonName recursively */
257 struct isds_PersonName *isds_PersonName_duplicate(
258 struct isds_PersonName *template) {
259 struct isds_PersonName *new = NULL;
261 if (!template) return NULL;
263 new = calloc(1, sizeof(*new));
264 if (!new) return NULL;
266 STRDUP_OR_ERROR(new->pnFirstName, template->pnFirstName);
267 STRDUP_OR_ERROR(new->pnMiddleName, template->pnMiddleName);
268 STRDUP_OR_ERROR(new->pnLastName, template->pnLastName);
269 STRDUP_OR_ERROR(new->pnLastNameAtBirth, template->pnLastNameAtBirth);
271 return new;
273 error:
274 isds_PersonName_free(&new);
275 return NULL;
279 /* Copy structure isds_Address recursively */
280 struct isds_Address *isds_Address_duplicate(struct isds_Address *template) {
281 struct isds_Address *new = NULL;
283 if (!template) return NULL;
285 new = calloc(1, sizeof(*new));
286 if (!new) return NULL;
288 STRDUP_OR_ERROR(new->adCity, template->adCity);
289 STRDUP_OR_ERROR(new->adStreet, template->adStreet);
290 STRDUP_OR_ERROR(new->adNumberInStreet, template->adNumberInStreet);
291 STRDUP_OR_ERROR(new->adNumberInMunicipality,
292 template->adNumberInMunicipality);
293 STRDUP_OR_ERROR(new->adZipCode, template->adZipCode);
294 STRDUP_OR_ERROR(new->adState, template->adState);
296 return new;
298 error:
299 isds_Address_free(&new);
300 return NULL;
304 /* Copy structure isds_DbUserInfo recursively */
305 struct isds_DbUserInfo *isds_DbUserInfo_duplicate(
306 const struct isds_DbUserInfo *template) {
307 struct isds_DbUserInfo *new = NULL;
308 if (!template) return NULL;
310 new = calloc(1, sizeof(*new));
311 if (!new) return NULL;
313 STRDUP_OR_ERROR(new->userID, template->userID);
314 FLATDUP_OR_ERROR(new->userType, template->userType);
315 FLATDUP_OR_ERROR(new->userPrivils, template->userPrivils);
317 if (template->personName) {
318 if (!(new->personName =
319 isds_PersonName_duplicate(template->personName)))
320 goto error;
323 if (template->address) {
324 if (!(new->address = isds_Address_duplicate(template->address)))
325 goto error;
328 FLATDUP_OR_ERROR(new->biDate, template->biDate);
329 STRDUP_OR_ERROR(new->ic, template->ic);
330 STRDUP_OR_ERROR(new->firmName, template->firmName);
331 STRDUP_OR_ERROR(new->caStreet, template->caStreet);
332 STRDUP_OR_ERROR(new->caCity, template->caCity);
333 STRDUP_OR_ERROR(new->caZipCode, template->caZipCode);
335 return new;
337 error:
338 isds_DbUserInfo_free(&new);
339 return NULL;
342 #undef FLATDUP_OR_ERROR
343 #undef STRDUP_OR_ERROR
346 /* Initialize ISDS library.
347 * Global function, must be called before other functions.
348 * If it failes you can not use ISDS library and must call isds_cleanup() to
349 * free partially inititialized global variables. */
350 isds_error isds_init(void) {
351 /* NULL global variables */
352 log_facilities = ILF_ALL;
353 log_level = ILL_WARNING;
355 #if ENABLE_NLS
356 /* Initialize gettext */
357 bindtextdomain(PACKAGE, LOCALEDIR);
358 #endif
360 /* Initialize CURL */
361 if (curl_global_init(CURL_GLOBAL_ALL)) {
362 isds_log(ILF_ISDS, ILL_CRIT, _("CURL library initialization failed\n"));
363 return IE_ERROR;
366 /* Inicialize gpg-error because of gpgme and ksba */
367 if (gpg_err_init()) {
368 isds_log(ILF_ISDS, ILL_CRIT,
369 _("gpg-error library initialization failed\n"));
370 return IE_ERROR;
373 /* Initialize GPGME */
374 if (init_gpgme()) {
375 isds_log(ILF_ISDS, ILL_CRIT,
376 _("GPGME library initialization failed\n"));
377 return IE_ERROR;
380 /* Initialize gcrypt */
381 if (init_gcrypt()) {
382 isds_log(ILF_ISDS, ILL_CRIT,
383 _("gcrypt library initialization failed\n"));
384 return IE_ERROR;
387 /* This can _exit() current program. Find not so assertive check. */
388 LIBXML_TEST_VERSION;
390 /* Check expat */
391 if (init_expat()) {
392 isds_log(ILF_ISDS, ILL_CRIT,
393 _("expat library initialization failed\n"));
394 return IE_ERROR;
397 /* Allocate global variables */
400 return IE_SUCCESS;
404 /* Deinicialize ISDS library.
405 * Global function, must be called as last library function. */
406 isds_error isds_cleanup(void) {
407 /* XML */
408 xmlCleanupParser();
410 /* Curl */
411 curl_global_cleanup();
413 return IE_SUCCESS;
417 /* Return text description of ISDS error */
418 const char *isds_strerror(const isds_error error) {
419 switch (error) {
420 case IE_SUCCESS:
421 return(_("Success")); break;
422 case IE_ERROR:
423 return(_("Unspecified error")); break;
424 case IE_NOTSUP:
425 return(_("Not supported")); break;
426 case IE_INVAL:
427 return(_("Invalid value")); break;
428 case IE_INVALID_CONTEXT:
429 return(_("Invalid context")); break;
430 case IE_NOT_LOGGED_IN:
431 return(_("Not logged in")); break;
432 case IE_CONNECTION_CLOSED:
433 return(_("Connection closed")); break;
434 case IE_TIMED_OUT:
435 return(_("Timed out")); break;
436 case IE_NOEXIST:
437 return(_("Not exist")); break;
438 case IE_NOMEM:
439 return(_("Out of memory")); break;
440 case IE_NETWORK:
441 return(_("Network problem")); break;
442 case IE_HTTP:
443 return(_("HTTP problem")); break;
444 case IE_SOAP:
445 return(_("SOAP problem")); break;
446 case IE_XML:
447 return(_("XML problem")); break;
448 case IE_ISDS:
449 return(_("ISDS server problem")); break;
450 case IE_ENUM:
451 return(_("Invalid enum value")); break;
452 case IE_DATE:
453 return(_("Invalid date value")); break;
454 case IE_2BIG:
455 return(_("Too big")); break;
456 case IE_2SMALL:
457 return(_("Too small")); break;
458 case IE_NOTUNIQ:
459 return(_("Value not unique")); break;
460 case IE_NOTEQUAL:
461 return(_("Values not uqual")); break;
462 case IE_PARTIAL_SUCCESS:
463 return(_("Some suboperations failed")); break;
464 default:
465 return(_("Unknown error"));
470 /* Create ISDS context.
471 * Each context can be used for different sessions to (possibly) differnet
472 * ISDS server with different credentials. */
473 struct isds_ctx *isds_ctx_create(void) {
474 struct isds_ctx *context;
475 context = malloc(sizeof(*context));
476 if (context) memset(context, 0, sizeof(*context));
477 return context;
481 /* Destroy ISDS context and free memmory.
482 * @context will be NULLed on success. */
483 isds_error isds_ctx_free(struct isds_ctx **context) {
484 if (!context || !*context) {
485 return IE_INVALID_CONTEXT;
488 /* Discard credentials */
489 isds_logout(*context);
491 /* Free other structures */
492 free((*context)->tls_verify_server);
493 free((*context)->tls_ca_file);
494 free((*context)->tls_ca_dir);
495 free((*context)->long_message);
497 free(*context);
498 *context = NULL;
499 return IE_SUCCESS;
503 /* Return long message text produced by library fucntion, e.g. detailed error
504 * mesage. Returned pointer is only valid until new library function is
505 * called for the same context. Could be NULL, especially if NULL context is
506 * supplied. Return string is locale encoded. */
507 char *isds_long_message(const struct isds_ctx *context) {
508 if (!context) return NULL;
509 return context->long_message;
513 /* Stores message into context' long_message buffer.
514 * Application can pick the message up using isds_long_message().
515 * NULL @message truncates the buffer but does not deallocate it.
516 * @message is coded in locale encoding */
517 _hidden isds_error isds_log_message(struct isds_ctx *context,
518 const char *message) {
519 char *buffer;
520 size_t length;
522 if (!context) return IE_INVALID_CONTEXT;
524 /* FIXME: Check for integer overflow */
525 length = 1 + ((message) ? strlen(message) : 0);
526 buffer = realloc(context->long_message, length);
527 if (!buffer) return IE_NOMEM;
529 if (message)
530 strcpy(buffer, message);
531 else
532 *buffer = '\0';
534 context->long_message = buffer;
535 return IE_SUCCESS;
539 /* Appends message into context' long_message buffer.
540 * Application can pick the message up using isds_long_message().
541 * NULL message has void effect. */
542 _hidden isds_error isds_append_message(struct isds_ctx *context,
543 const char *message) {
544 char *buffer;
545 size_t old_length, length;
547 if (!context) return IE_INVALID_CONTEXT;
548 if (!message) return IE_SUCCESS;
549 if (!context->long_message)
550 return isds_log_message(context, message);
552 old_length = strlen(context->long_message);
553 /* FIXME: Check for integer overflow */
554 length = 1 + old_length + strlen(message);
555 buffer = realloc(context->long_message, length);
556 if (!buffer) return IE_NOMEM;
558 strcpy(buffer + old_length, message);
560 context->long_message = buffer;
561 return IE_SUCCESS;
565 /* Stores formated message into context' long_message buffer.
566 * Application can pick the message up using isds_long_message(). */
567 _hidden isds_error isds_printf_message(struct isds_ctx *context,
568 const char *format, ...) {
569 va_list ap;
570 int length;
572 if (!context) return IE_INVALID_CONTEXT;
573 va_start(ap, format);
574 length = isds_vasprintf(&(context->long_message), format, ap);
575 va_end(ap);
577 return (length < 0) ? IE_ERROR: IE_SUCCESS;
581 /* Set logging up.
582 * @facilities is bitmask of isds_log_facility values,
583 * @level is verbosity level. */
584 void isds_set_logging(const unsigned int facilities,
585 const isds_log_level level) {
586 log_facilities = facilities;
587 log_level = level;
591 /* Log @message in class @facility with log @level into global log. @message
592 * is printf(3) formating string, variadic arguments may be neccessary.
593 * For debugging purposes. */
594 _hidden isds_error isds_log(const isds_log_facility facility,
595 const isds_log_level level, const char *message, ...) {
596 va_list ap;
598 if (level > log_level) return IE_SUCCESS;
599 if (!(log_facilities & facility)) return IE_SUCCESS;
600 if (!message) return IE_INVAL;
602 /* TODO: Allow to register output function provided by application
603 * (e.g. fprintf to stderr or copy to text area GUI widget). */
605 va_start(ap, message);
606 vfprintf(stderr, message, ap);
607 va_end(ap);
608 /* Line buffered printf is default.
609 * fflush(stderr);*/
611 return IE_SUCCESS;
615 /* Set timeout in miliseconds for each network job like connecting to server
616 * or sending message. Use 0 to disable timeout limits. */
617 isds_error isds_set_timeout(struct isds_ctx *context,
618 const unsigned int timeout) {
619 if (!context) return IE_INVALID_CONTEXT;
621 context->timeout = timeout;
623 if (context->curl) {
624 CURLcode curl_err;
626 curl_err = curl_easy_setopt(context->curl, CURLOPT_NOSIGNAL, 1);
627 if (!curl_err)
628 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT_MS,
629 context->timeout);
630 if (curl_err) return IE_ERROR;
633 return IE_SUCCESS;
637 /* Register callback function libisds calls periodocally during HTTP data
638 * transfer.
639 * @context is session context
640 * @callback is function provided by application libsds will call. See type
641 * defition for @callback argument explanation.
642 * @data is application specific data @callback gets as last argument */
643 isds_error isds_set_progress_callback(struct isds_ctx *context,
644 isds_progress_callback callback, void *data) {
645 if (!context) return IE_INVALID_CONTEXT;
647 context->progress_callback = callback;
648 context->progress_callback_data = data;
650 return IE_SUCCESS;
654 /* Change SSL/TLS settings.
655 * @context is context which setting will be applied to
656 * @option is name of option. It determines the type of last argument. See
657 * isds_tls_option definition for more info.
658 * @... is value of new setting. Type is determined by @option
659 * */
660 isds_error isds_set_tls(struct isds_ctx *context, const isds_tls_option option,
661 ...) {
662 isds_error err = IE_SUCCESS;
663 va_list ap;
664 char *pointer, *string;
666 if (!context) return IE_INVALID_CONTEXT;
668 va_start(ap, option);
670 #define REPLACE_VA_STRING(destination) \
671 string = va_arg(ap, char *); \
672 if (string) { \
673 pointer = realloc((destination), 1 + strlen(string)); \
674 if (!pointer) { err = IE_NOMEM; goto leave; } \
675 strcpy(pointer, string); \
676 (destination) = pointer; \
677 } else { \
678 free(destination); \
679 (destination) = NULL; \
682 switch (option) {
683 case ITLS_VERIFY_SERVER:
684 if (!context->tls_verify_server) {
685 context->tls_verify_server =
686 malloc(sizeof(*context->tls_verify_server));
687 if (!context->tls_verify_server) {
688 err = IE_NOMEM; goto leave;
691 *context->tls_verify_server = (_Bool) (0 != va_arg(ap, int));
692 break;
694 case ITLS_CA_FILE:
695 REPLACE_VA_STRING(context->tls_ca_file);
696 break;
697 case ITLS_CA_DIRECTORY:
698 REPLACE_VA_STRING(context->tls_ca_dir);
699 break;
701 default:
702 err = IE_ENUM; goto leave;
705 #undef REPLACE_VA_STRING
707 leave:
708 va_end(ap);
709 return err;
713 /* Discard credentials.
714 * Only that. It does not cause log out, connection close or similar. */
715 static isds_error discard_credentials(struct isds_ctx *context) {
716 if(!context) return IE_INVALID_CONTEXT;
718 if (context->username) {
719 memset(context->username, 0, strlen(context->username));
720 free(context->username);
721 context->username = NULL;
723 if (context->password) {
724 memset(context->password, 0, strlen(context->password));
725 free(context->password);
726 context->password = NULL;
729 return IE_SUCCESS;
733 /* Connect and log in into ISDS server.
734 * @url is address of ISDS web service
735 * @username is user name of ISDS user
736 * @password is user's secret password
737 * @certificate is NULL terminated string with PEM formated client's
738 * certificate. Use NULL if only password autentication should be performed.
739 * @key is private key for client's certificate as (base64 encoded?) NULL
740 * terminated string. Use NULL if only password autentication is desired.
741 * */
742 isds_error isds_login(struct isds_ctx *context, const char *url,
743 const char *username, const char *password,
744 const char *certificate, const char* key) {
745 isds_error err = IE_NOT_LOGGED_IN;
746 isds_error soap_err;
747 xmlNsPtr isds_ns = NULL;
748 xmlNodePtr request = NULL;
749 xmlNodePtr response = NULL;
751 if (!context) return IE_INVALID_CONTEXT;
752 if (!url || !username || !password) return IE_INVAL;
753 if (certificate || key) return IE_NOTSUP;
755 /* Store configuration */
756 free(context->url);
757 context->url = strdup(url);
758 if (!(context->url))
759 return IE_NOMEM;
761 /* Close connection if already logged in */
762 if (context->curl) {
763 close_connection(context);
766 /* Prepare CURL handle */
767 context->curl = curl_easy_init();
768 if (!(context->curl))
769 return IE_ERROR;
771 /* Build login request */
772 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
773 if (!request) {
774 isds_log_message(context, _("Could build ISDS login request"));
775 return IE_ERROR;
777 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
778 if(!isds_ns) {
779 isds_log_message(context, _("Could not create ISDS name space"));
780 xmlFreeNode(request);
781 return IE_ERROR;
783 xmlSetNs(request, isds_ns);
785 /* Store credentials */
786 /* FIXME: mlock password
787 * (I have a library) */
788 discard_credentials(context);
789 context->username = strdup(username);
790 context->password = strdup(password);
791 if (!(context->username && context->password)) {
792 discard_credentials(context);
793 xmlFreeNode(request);
794 return IE_NOMEM;
797 isds_log(ILF_ISDS, ILL_DEBUG, _("Logging user %s into server %s\n"),
798 username, url);
800 /* Send login request */
801 soap_err = soap(context, "dz", request, &response, NULL, NULL);
803 /* Remove credentials */
804 discard_credentials(context);
806 /* Destroy login request */
807 xmlFreeNode(request);
809 if (soap_err) {
810 xmlFreeNodeList(response);
811 close_connection(context);
812 return soap_err;
815 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
816 * authentication succeeded if soap_err == IE_SUCCESS */
817 err = IE_SUCCESS;
819 xmlFreeNodeList(response);
821 if (!err)
822 isds_log(ILF_ISDS, ILL_DEBUG,
823 _("User %s has been logged into server %s successfully\n"),
824 username, url);
825 return err;
829 /* Log out from ISDS server discards credentials and connection configuration. */
830 isds_error isds_logout(struct isds_ctx *context) {
831 if (!context) return IE_INVALID_CONTEXT;
833 /* Close connection */
834 if (context->curl) {
835 close_connection(context);
837 /* Discard credentials for sure. They should not survive isds_login(),
838 * even successful .*/
839 discard_credentials(context);
840 free(context->url);
841 context->url = NULL;
843 isds_log(ILF_ISDS, ILL_DEBUG, _("Logged out from ISDS server\n"));
844 } else {
845 discard_credentials(context);
847 return IE_SUCCESS;
851 /* Verify connection to ISDS is alive and server is responding.
852 * Sent dumy request to ISDS and expect dummy response. */
853 isds_error isds_ping(struct isds_ctx *context) {
854 isds_error soap_err;
855 xmlNsPtr isds_ns = NULL;
856 xmlNodePtr request = NULL;
857 xmlNodePtr response = NULL;
859 if (!context) return IE_INVALID_CONTEXT;
861 /* Check if connection is established */
862 if (!context->curl) return IE_CONNECTION_CLOSED;
865 /* Build dummy request */
866 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
867 if (!request) {
868 isds_log_message(context, _("Could build ISDS dummy request"));
869 return IE_ERROR;
871 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
872 if(!isds_ns) {
873 isds_log_message(context, _("Could not create ISDS name space"));
874 xmlFreeNode(request);
875 return IE_ERROR;
877 xmlSetNs(request, isds_ns);
879 isds_log(ILF_ISDS, ILL_DEBUG, _("Pinging ISDS server\n"));
881 /* Sent dummy request */
882 soap_err = soap(context, "dz", request, &response, NULL, NULL);
884 /* Destroy login request */
885 xmlFreeNode(request);
887 if (soap_err) {
888 isds_log(ILF_ISDS, ILL_DEBUG,
889 _("ISDS server could not be contacted\n"));
890 xmlFreeNodeList(response);
891 close_connection(context);
892 return soap_err;
895 /* XXX: Untill we don't propagate HTTP code 500 or 4xx, we can be sure
896 * authentication succeeded if soap_err == IE_SUCCESS */
897 /* TODO: ISDS documentation does not specify response body.
898 * However real server sends back DummyOperationResponse */
901 xmlFreeNodeList(response);
903 isds_log(ILF_ISDS, ILL_DEBUG, _("ISDS server alive\n"));
905 return IE_SUCCESS;
909 /* Send bogus request to ISDS.
910 * Just for test purposes */
911 isds_error isds_bogus_request(struct isds_ctx *context) {
912 isds_error err;
913 xmlNsPtr isds_ns = NULL;
914 xmlNodePtr request = NULL;
915 xmlDocPtr response = NULL;
916 xmlChar *code = NULL, *message = NULL;
918 if (!context) return IE_INVALID_CONTEXT;
920 /* Check if connection is established */
921 if (!context->curl) {
922 /* Testing printf message */
923 isds_printf_message(context, "%s", _("I said connection closed"));
924 return IE_CONNECTION_CLOSED;
928 /* Build dummy request */
929 request = xmlNewNode(NULL, BAD_CAST "X-BogusOperation");
930 if (!request) {
931 isds_log_message(context, _("Could build ISDS bogus request"));
932 return IE_ERROR;
934 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
935 if(!isds_ns) {
936 isds_log_message(context, _("Could not create ISDS name space"));
937 xmlFreeNode(request);
938 return IE_ERROR;
940 xmlSetNs(request, isds_ns);
942 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending bogus request to ISDS\n"));
944 /* Sent bogus request */
945 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
947 /* Destroy request */
948 xmlFreeNode(request);
950 if (err) {
951 isds_log(ILF_ISDS, ILL_DEBUG,
952 _("Processing ISDS response on bogus request failed\n"));
953 xmlFreeDoc(response);
954 return err;
957 /* Check for response status */
958 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
959 &code, &message, NULL);
960 if (err) {
961 isds_log(ILF_ISDS, ILL_DEBUG,
962 _("ISDS response on bogus request is missing status\n"));
963 free(code);
964 free(message);
965 xmlFreeDoc(response);
966 return err;
968 if (xmlStrcmp(code, BAD_CAST "0000")) {
969 char *code_locale = utf82locale((char*)code);
970 char *message_locale = utf82locale((char*)message);
971 isds_log(ILF_ISDS, ILL_DEBUG,
972 _("Server refused bogus request (code=%s, message=%s)\n"),
973 code_locale, message_locale);
974 /* XXX: Literal error messages from ISDS are Czech mesages
975 * (English sometimes) in UTF-8. It's hard to catch them for
976 * translation. Successfully gettextized would return in locale
977 * encoding, unsuccessfully translated would pass in UTF-8. */
978 isds_log_message(context, message_locale);
979 free(code_locale);
980 free(message_locale);
981 free(code);
982 free(message);
983 xmlFreeDoc(response);
984 return IE_ISDS;
988 free(code);
989 free(message);
990 xmlFreeDoc(response);
992 isds_log(ILF_ISDS, ILL_DEBUG,
993 _("Bogus message accepted by server. This should not happen.\n"));
995 return IE_SUCCESS;
999 /* Serialize XML subtree to buffer preserving XML indentatition.
1000 * @context is session context
1001 * @subtree is XML element to be serialized (with childern)
1002 * @buffer is automatically reallocated buffer where serialize to
1003 * @length is size of serialized stream in bytes
1004 * @return standard error code, free @buffer in case of error */
1005 static isds_error serialize_subtree(struct isds_ctx *context,
1006 xmlNodePtr subtree, void **buffer, size_t *length) {
1007 isds_error err = IE_SUCCESS;
1008 xmlBufferPtr xml_buffer = NULL;
1009 xmlSaveCtxtPtr save_ctx = NULL;
1010 xmlDocPtr subtree_doc = NULL;
1011 xmlNodePtr subtree_copy;
1012 xmlNsPtr isds_ns;
1013 void *new_buffer;
1015 if (!context) return IE_INVALID_CONTEXT;
1016 if (!buffer) return IE_INVAL;
1017 zfree(*buffer);
1018 if (!subtree || !length) return IE_INVAL;
1020 /* Make temporary XML document with @subtree root element */
1021 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1022 * It can result in not well-formed on invalid XML tree (e.g. name space
1023 * prefix definition can miss. */
1024 /*FIXME */
1026 subtree_doc = xmlNewDoc(BAD_CAST "1.0");
1027 if (!subtree_doc) {
1028 isds_log_message(context, _("Could not build temporary document"));
1029 err = IE_ERROR;
1030 goto leave;
1033 /* XXX: Copy subtree and attach the copy to document.
1034 * One node can not bee attached into more document at the same time.
1035 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1036 * automatically.
1037 * XXX: Check xmlSaveTree() too. */
1038 subtree_copy = xmlCopyNodeList(subtree);
1039 if (!subtree_copy) {
1040 isds_log_message(context, _("Could not copy subtree"));
1041 err = IE_ERROR;
1042 goto leave;
1044 xmlDocSetRootElement(subtree_doc, subtree_copy);
1046 /* Only this way we get namespace definition as @xmlns:isds,
1047 * otherwise we get namespace prefix without definition */
1048 /* FIXME: Don't overwrite original default namespace */
1049 isds_ns = xmlNewNs(subtree_copy, BAD_CAST ISDS_NS, NULL);
1050 if(!isds_ns) {
1051 isds_log_message(context, _("Could not create ISDS name space"));
1052 err = IE_ERROR;
1053 goto leave;
1055 xmlSetNs(subtree_copy, isds_ns);
1058 /* Serialize the document into buffer */
1059 xml_buffer = xmlBufferCreate();
1060 if (!xml_buffer) {
1061 isds_log_message(context, _("Could not create xmlBuffer"));
1062 err = IE_ERROR;
1063 goto leave;
1065 /* Last argument 0 means to not format the XML tree */
1066 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8", 0);
1067 if (!save_ctx) {
1068 isds_log_message(context, _("Could not create XML serializer"));
1069 err = IE_ERROR;
1070 goto leave;
1072 /* XXX: According LibXML documentation, this function does not return
1073 * meaningfull value yet */
1074 xmlSaveDoc(save_ctx, subtree_doc);
1075 if (-1 == xmlSaveFlush(save_ctx)) {
1076 isds_log_message(context,
1077 _("Could not serialize XML subtree"));
1078 err = IE_ERROR;
1079 goto leave;
1081 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1082 * even after xmlSaveFlush(). Thus close it here */
1083 xmlSaveClose(save_ctx); save_ctx = NULL;
1086 /* Store and detach buffer from xml_buffer */
1087 *buffer = xml_buffer->content;
1088 *length = xml_buffer->use;
1089 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1091 /* Shrink buffer */
1092 new_buffer = realloc(*buffer, *length);
1093 if (new_buffer) *buffer = new_buffer;
1095 leave:
1096 if (err) {
1097 zfree(*buffer);
1098 *length = 0;
1101 xmlSaveClose(save_ctx);
1102 xmlBufferFree(xml_buffer);
1103 xmlFreeDoc(subtree_doc); /* Frees subtree_copy, isds_ns etc. */
1104 return err;
1107 #if 0
1108 /* Dump XML subtree to buffer as literral string, not valid XML possibly.
1109 * @context is session context
1110 * @document is original document where @nodeset points to
1111 * @nodeset is XPath node set to dump (recursively)
1112 * @buffer is automarically reallocated buffer where serialize to
1113 * @length is size of serialized stream in bytes
1114 * @return standard error code, free @buffer in case of error */
1115 static isds_error dump_nodeset(struct isds_ctx *context,
1116 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1117 void **buffer, size_t *length) {
1118 isds_error err = IE_SUCCESS;
1119 xmlBufferPtr xml_buffer = NULL;
1120 void *new_buffer;
1122 if (!context) return IE_INVALID_CONTEXT;
1123 if (!buffer) return IE_INVAL;
1124 zfree(*buffer);
1125 if (!document || !nodeset || !length) return IE_INVAL;
1126 *length = 0;
1128 /* Empty node set results into NULL buffer */
1129 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1130 goto leave;
1133 /* Resuling the document into buffer */
1134 xml_buffer = xmlBufferCreate();
1135 if (!xml_buffer) {
1136 isds_log_message(context, _("Could not create xmlBuffer"));
1137 err = IE_ERROR;
1138 goto leave;
1141 /* Itearate over all nodes */
1142 for (int i = 0; i < nodeset->nodeNr; i++) {
1143 /* Serialize node.
1144 * XXX: xmlNodeDump() appends to xml_buffer. */
1145 if (-1 ==
1146 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1147 isds_log_message(context, _("Could not dump XML node"));
1148 err = IE_ERROR;
1149 goto leave;
1153 /* Store and detach buffer from xml_buffer */
1154 *buffer = xml_buffer->content;
1155 *length = xml_buffer->use;
1156 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1158 /* Shrink buffer */
1159 new_buffer = realloc(*buffer, *length);
1160 if (new_buffer) *buffer = new_buffer;
1163 leave:
1164 if (err) {
1165 zfree(*buffer);
1166 *length = 0;
1169 xmlBufferFree(xml_buffer);
1170 return err;
1172 #endif
1174 #if 0
1175 /* Dump XML subtree to buffer as literral string, not valid XML possibly.
1176 * @context is session context
1177 * @document is original document where @nodeset points to
1178 * @nodeset is XPath node set to dump (recursively)
1179 * @buffer is automarically reallocated buffer where serialize to
1180 * @length is size of serialized stream in bytes
1181 * @return standard error code, free @buffer in case of error */
1182 static isds_error dump_nodeset(struct isds_ctx *context,
1183 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1184 void **buffer, size_t *length) {
1185 isds_error err = IE_SUCCESS;
1186 xmlBufferPtr xml_buffer = NULL;
1187 xmlSaveCtxtPtr save_ctx = NULL;
1188 void *new_buffer;
1190 if (!context) return IE_INVALID_CONTEXT;
1191 if (!buffer) return IE_INVAL;
1192 zfree(*buffer);
1193 if (!document || !nodeset || !length) return IE_INVAL;
1194 *length = 0;
1196 /* Empty node set results into NULL buffer */
1197 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1198 goto leave;
1201 /* Resuling the document into buffer */
1202 xml_buffer = xmlBufferCreate();
1203 if (!xml_buffer) {
1204 isds_log_message(context, _("Could not create xmlBuffer"));
1205 err = IE_ERROR;
1206 goto leave;
1208 if (xmlSubstituteEntitiesDefault(1)) {
1209 isds_log_message(context, _("Could not disable attribute escaping"));
1210 err = IE_ERROR;
1211 goto leave;
1213 /* Last argument means:
1214 * 0 to not format the XML tree
1215 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1216 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8",
1217 XML_SAVE_NO_DECL|XML_SAVE_NO_EMPTY|XML_SAVE_NO_XHTML);
1218 if (!save_ctx) {
1219 isds_log_message(context, _("Could not create XML serializer"));
1220 err = IE_ERROR;
1221 goto leave;
1223 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1224 isds_log_message(context, _("Could not disable attribute escaping"));
1225 err = IE_ERROR;
1226 goto leave;
1230 /* Itearate over all nodes */
1231 for (int i = 0; i < nodeset->nodeNr; i++) {
1232 /* Serialize node.
1233 * XXX: xmlNodeDump() appends to xml_buffer. */
1234 /*if (-1 ==
1235 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1237 /* XXX: According LibXML documentation, this function does not return
1238 * meaningfull value yet */
1239 xmlSaveTree(save_ctx, nodeset->nodeTab[i]);
1240 if (-1 == xmlSaveFlush(save_ctx)) {
1241 isds_log_message(context,
1242 _("Could not serialize XML subtree"));
1243 err = IE_ERROR;
1244 goto leave;
1248 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1249 * even after xmlSaveFlush(). Thus close it here */
1250 xmlSaveClose(save_ctx); save_ctx = NULL;
1252 /* Store and detach buffer from xml_buffer */
1253 *buffer = xml_buffer->content;
1254 *length = xml_buffer->use;
1255 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1257 /* Shrink buffer */
1258 new_buffer = realloc(*buffer, *length);
1259 if (new_buffer) *buffer = new_buffer;
1261 leave:
1262 if (err) {
1263 zfree(*buffer);
1264 *length = 0;
1267 xmlSaveClose(save_ctx);
1268 xmlBufferFree(xml_buffer);
1269 return err;
1271 #endif
1274 /* Convert UTF-8 @string represantion of ISDS dbType to enum @type */
1275 static isds_error string2isds_DbType(xmlChar *string, isds_DbType *type) {
1276 if (!string || !type) return IE_INVAL;
1278 if (!xmlStrcmp(string, BAD_CAST "FO"))
1279 *type = DBTYPE_FO;
1280 else if (!xmlStrcmp(string, BAD_CAST "PFO"))
1281 *type = DBTYPE_PFO;
1282 else if (!xmlStrcmp(string, BAD_CAST "PFO_ADVOK"))
1283 *type = DBTYPE_PFO_ADVOK;
1284 else if (!xmlStrcmp(string, BAD_CAST "PFO_DANPOR"))
1285 *type = DBTYPE_PFO_DANPOR;
1286 else if (!xmlStrcmp(string, BAD_CAST "PFO_INSSPR"))
1287 *type = DBTYPE_PFO_INSSPR;
1288 else if (!xmlStrcmp(string, BAD_CAST "PO"))
1289 *type = DBTYPE_PO;
1290 else if (!xmlStrcmp(string, BAD_CAST "PO_ZAK"))
1291 *type = DBTYPE_PO_ZAK;
1292 else if (!xmlStrcmp(string, BAD_CAST "PO_REQ"))
1293 *type = DBTYPE_PO_REQ;
1294 else if (!xmlStrcmp(string, BAD_CAST "OVM"))
1295 *type = DBTYPE_OVM;
1296 else if (!xmlStrcmp(string, BAD_CAST "OVM_NOTAR"))
1297 *type = DBTYPE_OVM_NOTAR;
1298 else if (!xmlStrcmp(string, BAD_CAST "OVM_EXEKUT"))
1299 *type = DBTYPE_OVM_EXEKUT;
1300 else if (!xmlStrcmp(string, BAD_CAST "OVM_REQ"))
1301 *type = DBTYPE_OVM_REQ;
1302 else
1303 return IE_ENUM;
1304 return IE_SUCCESS;
1308 /* Convert ISDS dbType enum @type to UTF-8 string.
1309 * @Return pointer to static string, or NULL if unkwnow enum value */
1310 static const xmlChar *isds_DbType2string(const isds_DbType type) {
1311 switch(type) {
1312 case DBTYPE_FO: return(BAD_CAST "FO"); break;
1313 case DBTYPE_PFO: return(BAD_CAST "PFO"); break;
1314 case DBTYPE_PFO_ADVOK: return(BAD_CAST "PFO_ADVOK"); break;
1315 case DBTYPE_PFO_DANPOR: return(BAD_CAST "PFO_DANPOR"); break;
1316 case DBTYPE_PFO_INSSPR: return(BAD_CAST "PFO_INSSPR"); break;
1317 case DBTYPE_PO: return(BAD_CAST "PO"); break;
1318 case DBTYPE_PO_ZAK: return(BAD_CAST "PO_ZAK"); break;
1319 case DBTYPE_PO_REQ: return(BAD_CAST "PO_REQ"); break;
1320 case DBTYPE_OVM: return(BAD_CAST "OVM"); break;
1321 case DBTYPE_OVM_NOTAR: return(BAD_CAST "OVM_NOTAR"); break;
1322 case DBTYPE_OVM_EXEKUT: return(BAD_CAST "OVM_EXEKUT"); break;
1323 case DBTYPE_OVM_REQ: return(BAD_CAST "OVM_REQ"); break;
1324 default: return NULL; break;
1329 /* Convert UTF-8 @string represantion of ISDS userType to enum @type */
1330 static isds_error string2isds_UserType(xmlChar *string, isds_UserType *type) {
1331 if (!string || !type) return IE_INVAL;
1333 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
1334 *type = USERTYPE_PRIMARY;
1335 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
1336 *type = USERTYPE_ENTRUSTED;
1337 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
1338 *type = USERTYPE_ADMINISTRATOR;
1339 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
1340 *type = USERTYPE_OFFICIAL;
1341 else
1342 return IE_ENUM;
1343 return IE_SUCCESS;
1347 /* Convert ISDS userType enum @type to UTF-8 string.
1348 * @Return pointer to static string, or NULL if unkwnow enum value */
1349 static const xmlChar *isds_UserType2string(const isds_UserType type) {
1350 switch(type) {
1351 case USERTYPE_PRIMARY: return(BAD_CAST "PRIMARY_USER"); break;
1352 case USERTYPE_ENTRUSTED: return(BAD_CAST "ENTRUSTED_USER"); break;
1353 case USERTYPE_ADMINISTRATOR: return(BAD_CAST "ADMINISTRATOR"); break;
1354 case USERTYPE_OFFICIAL: return(BAD_CAST "OFFICIAL"); break;
1355 default: return NULL; break;
1360 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
1361 * @Return pointer to static string, or NULL if unkwnow enum value */
1362 static const xmlChar *isds_FileMetaType2string(const isds_FileMetaType type) {
1363 switch(type) {
1364 case FILEMETATYPE_MAIN: return(BAD_CAST "main"); break;
1365 case FILEMETATYPE_ENCLOSURE: return(BAD_CAST "enclosure"); break;
1366 case FILEMETATYPE_SIGNATURE: return(BAD_CAST "signature"); break;
1367 case FILEMETATYPE_META: return(BAD_CAST "meta"); break;
1368 default: return NULL; break;
1373 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
1374 * @Return IE_ENUM if @string is not valid enum member */
1375 static isds_error string2isds_FileMetaType(const xmlChar *string,
1376 isds_FileMetaType *type) {
1377 if (!string || !type) return IE_INVAL;
1379 if (!xmlStrcmp(string, BAD_CAST "main"))
1380 *type = FILEMETATYPE_MAIN;
1381 else if (!xmlStrcmp(string, BAD_CAST "enclosure"))
1382 *type = FILEMETATYPE_ENCLOSURE;
1383 else if (!xmlStrcmp(string, BAD_CAST "signature"))
1384 *type = FILEMETATYPE_SIGNATURE;
1385 else if (!xmlStrcmp(string, BAD_CAST "meta"))
1386 *type = FILEMETATYPE_META;
1387 else
1388 return IE_ENUM;
1389 return IE_SUCCESS;
1393 /* Convert UTF-8 @string to ISDS hash @algorithm.
1394 * @Return IE_ENUM if @string is not valid enum member */
1395 static isds_error string2isds_hash_algorithm(const xmlChar *string,
1396 isds_hash_algorithm *algorithm) {
1397 if (!string || !algorithm) return IE_INVAL;
1399 if (!xmlStrcmp(string, BAD_CAST "MD5"))
1400 *algorithm = HASH_ALGORITHM_MD5;
1401 else if (!xmlStrcmp(string, BAD_CAST "SHA-1"))
1402 *algorithm = HASH_ALGORITHM_SHA_1;
1403 else if (!xmlStrcmp(string, BAD_CAST "SHA-256"))
1404 *algorithm = HASH_ALGORITHM_SHA_256;
1405 else if (!xmlStrcmp(string, BAD_CAST "SHA-512"))
1406 *algorithm = HASH_ALGORITHM_SHA_512;
1407 else
1408 return IE_ENUM;
1409 return IE_SUCCESS;
1413 /* Convert UTF-8 @string represantion of ISO 8601 date to @time.
1414 * XXX: Not all ISO formats are supported */
1415 static isds_error datestring2tm(const xmlChar *string, struct tm *time) {
1416 char *offset;
1417 if (!string || !time) return IE_INVAL;
1419 /* xsd:date is ISO 8601 string, thus ASCII */
1420 offset = strptime((char*)string, "%Y-%m-%d", time);
1421 if (offset && *offset == '\0')
1422 return IE_SUCCESS;
1424 offset = strptime((char*)string, "%Y%m%d", time);
1425 if (offset && *offset == '\0')
1426 return IE_SUCCESS;
1428 offset = strptime((char*)string, "%Y-%j", time);
1429 if (offset && *offset == '\0')
1430 return IE_SUCCESS;
1432 return IE_NOTSUP;
1436 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
1437 static isds_error tm2datestring(const struct tm *time, xmlChar **string) {
1438 if (!time || !string) return IE_INVAL;
1440 if (-1 == isds_asprintf((char **) string, "%d-%02d-%02d",
1441 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
1442 return IE_ERROR;
1444 return IE_SUCCESS;
1448 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
1449 * respects the @time microseconds too. */
1450 static isds_error timeval2timestring(const struct timeval *time,
1451 xmlChar **string) {
1452 struct tm broken;
1454 if (!time || !string) return IE_INVAL;
1456 if (!gmtime_r(&time->tv_sec, &broken)) return IE_DATE;
1457 if (time->tv_usec < 0 || time->tv_usec > 999999) return IE_DATE;
1459 /* TODO: small negative year should be formated as "-0012". This is not
1460 * true for glibc "%04d". We should implement it.
1461 * TODO: What's type of time->tv_usec exactly? Unsigned? Absolute?
1462 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
1463 if (-1 == isds_asprintf((char **) string,
1464 "%04d-%02d-%02dT%02d:%02d:%02d.%06ld",
1465 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
1466 broken.tm_hour, broken.tm_min, broken.tm_sec,
1467 time->tv_usec))
1468 return IE_ERROR;
1470 return IE_SUCCESS;
1474 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
1475 * It respects microseconds too.
1476 * In case of error, @time will be freed. */
1477 static isds_error timestring2timeval(const xmlChar *string,
1478 struct timeval **time) {
1479 struct tm broken;
1480 char *offset, *delim, *endptr;
1481 char subseconds[7];
1482 int offset_hours, offset_minutes;
1483 int i;
1485 if (!time) return IE_INVAL;
1487 memset(&broken, 0, sizeof(broken));
1489 if (!*time) {
1490 *time = calloc(1, sizeof(**time));
1491 if (!*time) return IE_NOMEM;
1492 } else {
1493 memset(*time, 0, sizeof(**time));
1497 /* xsd:date is ISO 8601 string, thus ASCII */
1498 /*TODO: negative year */
1500 /* Parse date and time without subseconds and offset */
1501 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
1502 if (!offset) {
1503 free(*time); *time = NULL;
1504 return IE_DATE;
1507 /* Get subseconds */
1508 if (*offset == '.' ) {
1509 offset++;
1511 /* Copy first 6 digits, padd it with zeros.
1512 * XXX: It truncates longer number, no round.
1513 * Current server implementation uses only milisecond resolution. */
1514 /* TODO: isdigit() is locale sensitive */
1515 for (i = 0;
1516 i < sizeof(subseconds)/sizeof(char) - 1 && isdigit(*offset);
1517 i++, offset++) {
1518 subseconds[i] = *offset;
1520 for (; i < sizeof(subseconds)/sizeof(char) - 1; i++) {
1521 subseconds[i] = '0';
1523 subseconds[6] = '\0';
1525 /* Convert it into integer */
1526 (*time)->tv_usec = strtol(subseconds, &endptr, 10);
1527 if (*endptr != '\0' || (*time)->tv_usec == LONG_MIN ||
1528 (*time)->tv_usec == LONG_MAX) {
1529 free(*time); *time = NULL;
1530 return IE_DATE;
1533 /* move to the zone offset delimiter */
1534 delim = strchr(offset, '-');
1535 if (!delim)
1536 delim = strchr(offset, '+');
1537 offset = delim;
1540 /* Get zone offset */
1541 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
1542 * "" equals to "Z" and it means UTC zone. */
1543 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
1544 * colon separator */
1545 if (*offset == '-' || *offset == '+') {
1546 offset++;
1547 if (2 != sscanf(offset, "%2d:%2d", &offset_hours, &offset_minutes)) {
1548 free(*time); *time = NULL;
1549 return IE_DATE;
1551 broken.tm_hour -= offset_hours;
1552 broken.tm_min -= offset_minutes * ((offset_hours<0) ? -1 : 1);
1555 /* Convert to time_t */
1556 switch_tz_to_utc();
1557 (*time)->tv_sec = mktime(&broken);
1558 switch_tz_to_native();
1559 if ((*time)->tv_sec == (time_t) -1) {
1560 free(*time); *time = NULL;
1561 return IE_DATE;
1564 return IE_SUCCESS;
1568 /* Convert unsigned int into isds_message_status.
1569 * @context is session context
1570 * @number is pointer to number value. NULL will be treated as invalid value.
1571 * @status is automatically reallocated status
1572 * @return IE_SUCCESS, or error code and free status */
1573 static isds_error uint2isds_message_status(struct isds_ctx *context,
1574 const unsigned long int *number, isds_message_status **status) {
1575 if (!context) return IE_INVALID_CONTEXT;
1576 if (!status) return IE_INVAL;
1578 free(*status); *status = NULL;
1579 if (!number) return IE_INVAL;
1581 if (*number < 1 || *number > 10) {
1582 isds_printf_message(context, _("Invalid messsage status value: %lu"),
1583 *number);
1584 return IE_ENUM;
1587 *status = malloc(sizeof(**status));
1588 if (!*status) return IE_NOMEM;
1590 **status = 1 << *number;
1591 return IE_SUCCESS;
1595 /* Convert event description string into isds_event memebers type and
1596 * description
1597 * @string is raw event decsription starting with event prefix
1598 * @event is structure where to store type and stripped description to
1599 * @return standard error code, unkown prefix is not classified as an error. */
1600 static isds_error eventstring2event(const xmlChar *string,
1601 struct isds_event* event) {
1602 const xmlChar *known_prefixes[] = {
1603 BAD_CAST "EV1:",
1604 BAD_CAST "EV2:",
1605 BAD_CAST "EV3:",
1606 BAD_CAST "EV4:"
1608 const isds_event_type types[] = {
1609 EVENT_ACCEPTED_BY_RECIPIENT,
1610 EVENT_ACCEPTED_BY_FICTION,
1611 EVENT_UNDELIVERABLE,
1612 EVENT_COMMERCIAL_ACCEPTED
1614 unsigned int index;
1615 size_t length;
1617 if (!string || !event) return IE_INVAL;
1619 if (!event->type) {
1620 event->type = malloc(sizeof(*event->type));
1621 if (!(event->type)) return IE_NOMEM;
1623 zfree(event->description);
1625 for (index = 0; index < sizeof(known_prefixes)/sizeof(known_prefixes[0]);
1626 index++) {
1627 length = xmlUTF8Strlen(known_prefixes[index]);
1629 if (!xmlStrncmp(string, known_prefixes[index], length)) {
1630 /* Prefix is known */
1631 *event->type = types[index];
1633 /* Strip prefix from description and spaces */
1634 /* TODO: Recognize all wite spaces from UCS blank class and
1635 * operate on UTF-8 chars. */
1636 for (; string[length] != '\0' && string[length] == ' '; length++);
1637 event->description = strdup((char *) (string + length));
1638 if (!(event->description)) return IE_NOMEM;
1640 return IE_SUCCESS;
1644 /* Unknown event prefix.
1645 * XSD allows any string */
1646 char *string_locale = utf82locale((char *) string);
1647 isds_log(ILF_ISDS, ILL_WARNING,
1648 _("Uknown delivery info event prefix: %s\n"), string_locale);
1649 free(string_locale);
1651 *event->type = EVENT_UKNOWN;
1652 event->description = strdup((char *) string);
1653 if (!(event->description)) return IE_NOMEM;
1655 return IE_SUCCESS;
1659 /* Following EXTRACT_* macros expects @result, @xpath_ctx, @err, @context
1660 * and leave lable */
1661 #define EXTRACT_STRING(element, string) { \
1662 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
1663 if (!result) { \
1664 err = IE_ERROR; \
1665 goto leave; \
1667 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
1668 if (result->nodesetval->nodeNr > 1) { \
1669 isds_printf_message(context, _("Multiple %s element"), element); \
1670 err = IE_ERROR; \
1671 goto leave; \
1673 (string) = (char *) \
1674 xmlXPathCastNodeSetToString(result->nodesetval); \
1675 if (!(string)) { \
1676 err = IE_ERROR; \
1677 goto leave; \
1682 #define EXTRACT_BOOLEAN(element, booleanPtr) \
1684 char *string = NULL; \
1685 EXTRACT_STRING(element, string); \
1687 if (string) { \
1688 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
1689 if (!(booleanPtr)) { \
1690 free(string); \
1691 err = IE_NOMEM; \
1692 goto leave; \
1695 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
1696 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
1697 *(booleanPtr) = 1; \
1698 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
1699 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
1700 *(booleanPtr) = 0; \
1701 else { \
1702 char *string_locale = utf82locale((char*)string); \
1703 isds_printf_message(context, \
1704 _("%s value is not valid boolean: "), \
1705 element, string_locale); \
1706 free(string_locale); \
1707 free(string); \
1708 err = IE_ERROR; \
1709 goto leave; \
1712 free(string); \
1716 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
1718 char *string = NULL; \
1719 EXTRACT_STRING(element, string); \
1720 if (string) { \
1721 long int number; \
1722 char *endptr; \
1724 number = strtol((char*)string, &endptr, 10); \
1726 if (*endptr != '\0') { \
1727 char *string_locale = utf82locale((char *)string); \
1728 isds_printf_message(context, \
1729 _("%s is not valid integer: %s"), \
1730 element, string_locale); \
1731 free(string_locale); \
1732 free(string); \
1733 err = IE_ISDS; \
1734 goto leave; \
1737 if (number == LONG_MIN || number == LONG_MAX) { \
1738 char *string_locale = utf82locale((char *)string); \
1739 isds_printf_message(context, \
1740 _("%s value out of range of long int: %s"), \
1741 element, string_locale); \
1742 free(string_locale); \
1743 free(string); \
1744 err = IE_ERROR; \
1745 goto leave; \
1748 free(string); string = NULL; \
1750 if (!(preallocated)) { \
1751 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
1752 if (!(longintPtr)) { \
1753 err = IE_NOMEM; \
1754 goto leave; \
1757 *(longintPtr) = number; \
1761 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
1763 char *string = NULL; \
1764 EXTRACT_STRING(element, string); \
1765 if (string) { \
1766 long int number; \
1767 char *endptr; \
1769 number = strtol((char*)string, &endptr, 10); \
1771 if (*endptr != '\0') { \
1772 char *string_locale = utf82locale((char *)string); \
1773 isds_printf_message(context, \
1774 _("%s is not valid integer: %s"), \
1775 element, string_locale); \
1776 free(string_locale); \
1777 free(string); \
1778 err = IE_ISDS; \
1779 goto leave; \
1782 if (number == LONG_MIN || number == LONG_MAX) { \
1783 char *string_locale = utf82locale((char *)string); \
1784 isds_printf_message(context, \
1785 _("%s value out of range of long int: %s"), \
1786 element, string_locale); \
1787 free(string_locale); \
1788 free(string); \
1789 err = IE_ERROR; \
1790 goto leave; \
1793 free(string); string = NULL; \
1794 if (number < 0) { \
1795 isds_printf_message(context, \
1796 _("%s value is negative: %ld"), element, number); \
1797 err = IE_ERROR; \
1798 goto leave; \
1801 if (!(preallocated)) { \
1802 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
1803 if (!(ulongintPtr)) { \
1804 err = IE_NOMEM; \
1805 goto leave; \
1808 *(ulongintPtr) = number; \
1812 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
1813 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
1814 NULL); \
1815 if ((required) && (!string)) { \
1816 char *attribute_locale = utf82locale(attribute); \
1817 char *element_locale = utf82locale((char *)xpath_ctx->node->name); \
1818 isds_printf_message(context, \
1819 _("Could not extract required %s attribute value from " \
1820 "%s element"), attribute_locale, element_locale); \
1821 free(element_locale); \
1822 free(attribute_locale); \
1823 err = IE_ERROR; \
1824 goto leave; \
1829 #define INSERT_STRING(parent, element, string) \
1831 node = xmlNewTextChild(parent, NULL, BAD_CAST (element), \
1832 (xmlChar *) (string)); \
1833 if (!node) { \
1834 isds_printf_message(context, \
1835 _("Could not add %s child to %s element"), \
1836 element, (parent)->name); \
1837 err = IE_ERROR; \
1838 goto leave; \
1842 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
1844 if ((booleanPtr)) { \
1845 if (*(booleanPtr)) { INSERT_STRING(parent, element, "true"); } \
1846 else { INSERT_STRING(parent, element, "false") } \
1847 } else { INSERT_STRING(parent, element, NULL) } \
1850 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
1851 if ((longintPtr)) { \
1852 /* FIXME: locale sensitive */ \
1853 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
1854 err = IE_NOMEM; \
1855 goto leave; \
1857 INSERT_STRING(parent, element, buffer) \
1858 free(buffer); (buffer) = NULL; \
1859 } else { INSERT_STRING(parent, element, NULL) } \
1862 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
1863 if ((ulongintPtr)) { \
1864 /* FIXME: locale sensitive */ \
1865 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
1866 err = IE_NOMEM; \
1867 goto leave; \
1869 INSERT_STRING(parent, element, buffer) \
1870 free(buffer); (buffer) = NULL; \
1871 } else { INSERT_STRING(parent, element, NULL) } \
1874 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
1876 /* FIXME: locale sensitive */ \
1877 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
1878 err = IE_NOMEM; \
1879 goto leave; \
1881 INSERT_STRING(parent, element, buffer) \
1882 free(buffer); (buffer) = NULL; \
1885 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
1887 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
1888 (xmlChar *) (string)); \
1889 if (!attribute_node) { \
1890 isds_printf_message(context, _("Could not add %s " \
1891 "attribute to %s element"), \
1892 (attribute), (parent)->name); \
1893 err = IE_ERROR; \
1894 goto leave; \
1898 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
1899 if (string) { \
1900 int length = xmlUTF8Strlen((xmlChar *) (string)); \
1901 if (length > (maximum)) { \
1902 isds_printf_message(context, \
1903 _("%s has more than %d characters"), (name), (maximum)); \
1904 err = IE_2BIG; \
1905 goto leave; \
1907 if (length < (minimum)) { \
1908 isds_printf_message(context, \
1909 _("%s has less than %d characters"), (name), (minimum)); \
1910 err = IE_2SMALL; \
1911 goto leave; \
1916 #define INSERT_ELEMENT(child, parent, element) \
1918 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
1919 if (!(child)) { \
1920 isds_printf_message(context, \
1921 _("Could not add %s child to %s element"), \
1922 (element), (parent)->name); \
1923 err = IE_ERROR; \
1924 goto leave; \
1929 /* Find child element by name in given XPath context and switch context onto
1930 * it. The child must be uniq and must exist. Otherwise failes.
1931 * @context is ISDS context
1932 * @child is child element name
1933 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
1934 * into it child. In error case, the @xpath_ctx keeps original value. */
1935 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
1936 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
1937 isds_error err = IE_SUCCESS;
1938 xmlXPathObjectPtr result = NULL;
1940 if (!context) return IE_INVALID_CONTEXT;
1941 if (!child || !xpath_ctx) return IE_INVAL;
1943 /* Find child */
1944 result = xmlXPathEvalExpression(child, xpath_ctx);
1945 if (!result) {
1946 err = IE_XML;
1947 goto leave;
1950 /* No match */
1951 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
1952 char *parent_locale = utf82locale((char*) xpath_ctx->node->name);
1953 char *child_locale = utf82locale((char*) child);
1954 isds_printf_message(context,
1955 _("%s element does not contain %s child"),
1956 parent_locale, child_locale);
1957 free(child_locale);
1958 free(parent_locale);
1959 err = IE_NOEXIST;
1960 goto leave;
1963 /* More matches */
1964 if (result->nodesetval->nodeNr > 1) {
1965 char *parent_locale = utf82locale((char*) xpath_ctx->node->name);
1966 char *child_locale = utf82locale((char*) child);
1967 isds_printf_message(context,
1968 _("%s element contains multiple %s childs"),
1969 parent_locale, child_locale);
1970 free(child_locale);
1971 free(parent_locale);
1972 err = IE_NOTUNIQ;
1973 goto leave;
1976 /* Switch context */
1977 xpath_ctx->node = result->nodesetval->nodeTab[0];
1979 leave:
1980 xmlXPathFreeObject(result);
1981 return err;
1986 /* Find and convert XSD:gPersonName group in current node into structure
1987 * @context is ISDS context
1988 * @personName is automically reallocated person name structure. If no member
1989 * value is found, will be freed.
1990 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
1991 * elements
1992 * In case of error @personName will be freed. */
1993 static isds_error extract_gPersonName(struct isds_ctx *context,
1994 struct isds_PersonName **personName, xmlXPathContextPtr xpath_ctx) {
1995 isds_error err = IE_SUCCESS;
1996 xmlXPathObjectPtr result = NULL;
1998 if (!context) return IE_INVALID_CONTEXT;
1999 if (!personName) return IE_INVAL;
2000 isds_PersonName_free(personName);
2001 if (!xpath_ctx) return IE_INVAL;
2004 *personName = calloc(1, sizeof(**personName));
2005 if (!*personName) {
2006 err = IE_NOMEM;
2007 goto leave;
2010 EXTRACT_STRING("isds:pnFirstName", (*personName)->pnFirstName);
2011 EXTRACT_STRING("isds:pnMiddleName", (*personName)->pnMiddleName);
2012 EXTRACT_STRING("isds:pnLastName", (*personName)->pnLastName);
2013 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName)->pnLastNameAtBirth);
2015 if (!(*personName)->pnFirstName && !(*personName)->pnMiddleName &&
2016 !(*personName)->pnLastName && !(*personName)->pnLastNameAtBirth)
2017 isds_PersonName_free(personName);
2019 leave:
2020 if (err) isds_PersonName_free(personName);
2021 xmlXPathFreeObject(result);
2022 return err;
2026 /* Find and convert XSD:gAddress group in current node into structure
2027 * @context is ISDS context
2028 * @address is automically reallocated address structure. If no member
2029 * value is found, will be freed.
2030 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2031 * elements
2032 * In case of error @address will be freed. */
2033 static isds_error extract_gAddress(struct isds_ctx *context,
2034 struct isds_Address **address, xmlXPathContextPtr xpath_ctx) {
2035 isds_error err = IE_SUCCESS;
2036 xmlXPathObjectPtr result = NULL;
2038 if (!context) return IE_INVALID_CONTEXT;
2039 if (!address) return IE_INVAL;
2040 isds_Address_free(address);
2041 if (!xpath_ctx) return IE_INVAL;
2044 *address = calloc(1, sizeof(**address));
2045 if (!*address) {
2046 err = IE_NOMEM;
2047 goto leave;
2050 EXTRACT_STRING("isds:adCity", (*address)->adCity);
2051 EXTRACT_STRING("isds:adStreet", (*address)->adStreet);
2052 EXTRACT_STRING("isds:adNumberInStreet", (*address)->adNumberInStreet);
2053 EXTRACT_STRING("isds:adNumberInMunicipality",
2054 (*address)->adNumberInMunicipality);
2055 EXTRACT_STRING("isds:adZipCode", (*address)->adZipCode);
2056 EXTRACT_STRING("isds:adState", (*address)->adState);
2058 if (!(*address)->adCity && !(*address)->adStreet &&
2059 !(*address)->adNumberInStreet &&
2060 !(*address)->adNumberInMunicipality &&
2061 !(*address)->adZipCode && !(*address)->adState)
2062 isds_Address_free(address);
2064 leave:
2065 if (err) isds_Address_free(address);
2066 xmlXPathFreeObject(result);
2067 return err;
2071 /* Find and convert isds:biDate element in current node into structure
2072 * @context is ISDS context
2073 * @biDate is automically reallocated birth date structure. If no member
2074 * value is found, will be freed.
2075 * @xpath_ctx is XPath context with current node as parent for isds:biDate
2076 * element
2077 * In case of error @biDate will be freed. */
2078 static isds_error extract_BiDate(struct isds_ctx *context,
2079 struct tm **biDate, xmlXPathContextPtr xpath_ctx) {
2080 isds_error err = IE_SUCCESS;
2081 xmlXPathObjectPtr result = NULL;
2082 char *string = NULL;
2084 if (!context) return IE_INVALID_CONTEXT;
2085 if (!biDate) return IE_INVAL;
2086 zfree(*biDate);
2087 if (!xpath_ctx) return IE_INVAL;
2089 EXTRACT_STRING("isds:biDate", string);
2090 if (string) {
2091 *biDate = calloc(1, sizeof(**biDate));
2092 if (!*biDate) {
2093 err = IE_NOMEM;
2094 goto leave;
2096 err = datestring2tm((xmlChar *)string, *biDate);
2097 if (err) {
2098 if (err == IE_NOTSUP) {
2099 err = IE_ISDS;
2100 char *string_locale = utf82locale(string);
2101 isds_printf_message(context,
2102 _("Invalid isds:biDate value: %s"), string_locale);
2103 free(string_locale);
2105 goto leave;
2109 leave:
2110 if (err) zfree(*biDate);
2111 free(string);
2112 xmlXPathFreeObject(result);
2113 return err;
2117 /* Convert isds:dBOwnerInfo XML tree into structure
2118 * @context is ISDS context
2119 * @db_owner_info is automically reallocated box owner info structure
2120 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
2121 * In case of error @db_owner_info will be freed. */
2122 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
2123 struct isds_DbOwnerInfo **db_owner_info,
2124 xmlXPathContextPtr xpath_ctx) {
2125 isds_error err = IE_SUCCESS;
2126 xmlXPathObjectPtr result = NULL;
2127 char *string = NULL;
2129 if (!context) return IE_INVALID_CONTEXT;
2130 if (!db_owner_info) return IE_INVAL;
2131 isds_DbOwnerInfo_free(db_owner_info);
2132 if (!xpath_ctx) return IE_INVAL;
2135 *db_owner_info = calloc(1, sizeof(**db_owner_info));
2136 if (!*db_owner_info) {
2137 err = IE_NOMEM;
2138 goto leave;
2141 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
2143 EXTRACT_STRING("isds:dbType", string);
2144 if (string) {
2145 (*db_owner_info)->dbType =
2146 calloc(1, sizeof(*((*db_owner_info)->dbType)));
2147 if (!(*db_owner_info)->dbType) {
2148 err = IE_NOMEM;
2149 goto leave;
2151 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
2152 if (err) {
2153 zfree((*db_owner_info)->dbType);
2154 if (err == IE_ENUM) {
2155 err = IE_ISDS;
2156 char *string_locale = utf82locale(string);
2157 isds_printf_message(context, _("Unknown isds:dbType: %s"),
2158 string_locale);
2159 free(string_locale);
2161 goto leave;
2163 zfree(string);
2166 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
2168 err = extract_gPersonName(context, &(*db_owner_info)->personName,
2169 xpath_ctx);
2170 if (err) goto leave;
2172 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
2174 (*db_owner_info)->birthInfo =
2175 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
2176 if (!(*db_owner_info)->birthInfo) {
2177 err = IE_NOMEM;
2178 goto leave;
2180 err = extract_BiDate(context, &(*db_owner_info)->birthInfo->biDate,
2181 xpath_ctx);
2182 if (err) goto leave;
2183 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
2184 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
2185 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
2186 if (!(*db_owner_info)->birthInfo->biDate &&
2187 !(*db_owner_info)->birthInfo->biCity &&
2188 !(*db_owner_info)->birthInfo->biCounty &&
2189 !(*db_owner_info)->birthInfo->biState)
2190 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
2192 err = extract_gAddress(context, &(*db_owner_info)->address, xpath_ctx);
2193 if (err) goto leave;
2195 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
2196 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
2197 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
2198 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
2199 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
2201 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
2203 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
2204 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
2205 (*db_owner_info)->dbOpenAddressing);
2207 leave:
2208 if (err) isds_DbOwnerInfo_free(db_owner_info);
2209 free(string);
2210 xmlXPathFreeObject(result);
2211 return err;
2215 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
2216 * @context is sesstion context
2217 * @owner is libsids structure with box description
2218 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
2219 static isds_error insert_DbOwnerInfo(struct isds_ctx *context,
2220 const struct isds_DbOwnerInfo *owner, xmlNodePtr db_owner_info) {
2222 isds_error err = IE_SUCCESS;
2223 xmlNodePtr node;
2224 xmlChar *string = NULL;
2226 if (!context) return IE_INVALID_CONTEXT;
2227 if (!owner || !db_owner_info) return IE_INVAL;
2230 /* Build XSD:tDbOwnerInfo */
2231 CHECK_FOR_STRING_LENGTH(owner->dbID, 0, 7, "dbID")
2232 INSERT_STRING(db_owner_info, "dbID", owner->dbID);
2234 /* dbType */
2235 if (owner->dbType) {
2236 const xmlChar *type_string = isds_DbType2string(*(owner->dbType));
2237 if (!type_string) {
2238 isds_printf_message(context, _("Invalid dbType value: %d"),
2239 *(owner->dbType));
2240 err = IE_ENUM;
2241 goto leave;
2243 INSERT_STRING(db_owner_info, "dbType", type_string);
2245 INSERT_STRING(db_owner_info, "ic", owner->ic);
2246 if (owner->personName) {
2247 INSERT_STRING(db_owner_info, "pnFirstName",
2248 owner->personName->pnFirstName);
2249 INSERT_STRING(db_owner_info, "pnMiddleName",
2250 owner->personName->pnMiddleName);
2251 INSERT_STRING(db_owner_info, "pnLastName",
2252 owner->personName->pnLastName);
2253 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
2254 owner->personName->pnLastNameAtBirth);
2256 INSERT_STRING(db_owner_info, "firmName", owner->firmName);
2257 if (owner->birthInfo) {
2258 if (owner->birthInfo->biDate) {
2259 if (!tm2datestring(owner->birthInfo->biDate, &string))
2260 INSERT_STRING(db_owner_info, "biDate", string);
2261 free(string); string = NULL;
2263 INSERT_STRING(db_owner_info, "biCity", owner->birthInfo->biCity);
2264 INSERT_STRING(db_owner_info, "biCounty", owner->birthInfo->biCounty);
2265 INSERT_STRING(db_owner_info, "biState", owner->birthInfo->biState);
2267 if (owner->address) {
2268 INSERT_STRING(db_owner_info, "adCity", owner->address->adCity);
2269 INSERT_STRING(db_owner_info, "adStreet", owner->address->adStreet);
2270 INSERT_STRING(db_owner_info, "adNumberInStreet",
2271 owner->address->adNumberInStreet);
2272 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
2273 owner->address->adNumberInMunicipality);
2274 INSERT_STRING(db_owner_info, "adZipCode", owner->address->adZipCode);
2275 INSERT_STRING(db_owner_info, "adState", owner->address->adState);
2277 INSERT_STRING(db_owner_info, "nationality", owner->nationality);
2278 INSERT_STRING(db_owner_info, "email", owner->email);
2279 INSERT_STRING(db_owner_info, "telNumber", owner->telNumber);
2281 CHECK_FOR_STRING_LENGTH(owner->identifier, 0, 20, "identifier")
2282 INSERT_STRING(db_owner_info, "identifier", owner->identifier);
2284 CHECK_FOR_STRING_LENGTH(owner->registryCode, 0, 5, "registryCode")
2285 INSERT_STRING(db_owner_info, "registryCode", owner->registryCode);
2287 INSERT_LONGINT(db_owner_info, "dbState", owner->dbState, string);
2289 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", owner->dbEffectiveOVM);
2290 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
2291 owner->dbOpenAddressing);
2293 leave:
2294 free(string);
2295 return err;
2299 /* Convert XSD:tDbUserInfo XML tree into structure
2300 * @context is ISDS context
2301 * @db_user_info is automically reallocated user info structure
2302 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
2303 * In case of error @db_user_info will be freed. */
2304 static isds_error extract_DbUserInfo(struct isds_ctx *context,
2305 struct isds_DbUserInfo **db_user_info, xmlXPathContextPtr xpath_ctx) {
2306 isds_error err = IE_SUCCESS;
2307 xmlXPathObjectPtr result = NULL;
2308 char *string = NULL;
2310 if (!context) return IE_INVALID_CONTEXT;
2311 if (!db_user_info) return IE_INVAL;
2312 isds_DbUserInfo_free(db_user_info);
2313 if (!xpath_ctx) return IE_INVAL;
2316 *db_user_info = calloc(1, sizeof(**db_user_info));
2317 if (!*db_user_info) {
2318 err = IE_NOMEM;
2319 goto leave;
2322 EXTRACT_STRING("isds:userID", (*db_user_info)->userID);
2324 EXTRACT_STRING("isds:userType", string);
2325 if (string) {
2326 (*db_user_info)->userType =
2327 calloc(1, sizeof(*((*db_user_info)->userType)));
2328 if (!(*db_user_info)->userType) {
2329 err = IE_NOMEM;
2330 goto leave;
2332 err = string2isds_UserType((xmlChar *)string,
2333 (*db_user_info)->userType);
2334 if (err) {
2335 zfree((*db_user_info)->userType);
2336 if (err == IE_ENUM) {
2337 err = IE_ISDS;
2338 char *string_locale = utf82locale(string);
2339 isds_printf_message(context, _("Unknown isds:userType: %s"),
2340 string_locale);
2341 free(string_locale);
2343 goto leave;
2345 zfree(string);
2348 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info)->userPrivils, 0);
2350 (*db_user_info)->personName =
2351 calloc(1, sizeof(*((*db_user_info)->personName)));
2352 if (!(*db_user_info)->personName) {
2353 err = IE_NOMEM;
2354 goto leave;
2357 err = extract_gPersonName(context, &(*db_user_info)->personName,
2358 xpath_ctx);
2359 if (err) goto leave;
2361 err = extract_gAddress(context, &(*db_user_info)->address, xpath_ctx);
2362 if (err) goto leave;
2364 err = extract_BiDate(context, &(*db_user_info)->biDate, xpath_ctx);
2365 if (err) goto leave;
2367 EXTRACT_STRING("isds:ic", (*db_user_info)->ic);
2368 EXTRACT_STRING("isds:firmName", (*db_user_info)->firmName);
2370 EXTRACT_STRING("isds:caStreet", (*db_user_info)->caStreet);
2371 EXTRACT_STRING("isds:caCity", (*db_user_info)->caCity);
2372 EXTRACT_STRING("isds:caZipCode", (*db_user_info)->caZipCode);
2374 leave:
2375 if (err) isds_DbUserInfo_free(db_user_info);
2376 free(string);
2377 xmlXPathFreeObject(result);
2378 return err;
2382 /* Insert struct isds_DbUserInfo data (user description) into XML tree
2383 * @context is sesstion context
2384 * @user is libsids structure with user description
2385 * @db_user_info is XML element of XSD:tDbUserInfo */
2386 static isds_error insert_DbUserInfo(struct isds_ctx *context,
2387 const struct isds_DbUserInfo *user, xmlNodePtr db_user_info) {
2389 isds_error err = IE_SUCCESS;
2390 xmlNodePtr node;
2391 xmlChar *string = NULL;
2393 if (!context) return IE_INVALID_CONTEXT;
2394 if (!user || !db_user_info) return IE_INVAL;
2396 /* Build XSD:tDbUserInfo */
2397 if (user->personName) {
2398 INSERT_STRING(db_user_info, "pnFirstName",
2399 user->personName->pnFirstName);
2400 INSERT_STRING(db_user_info, "pnMiddleName",
2401 user->personName->pnMiddleName);
2402 INSERT_STRING(db_user_info, "pnLastName",
2403 user->personName->pnLastName);
2404 INSERT_STRING(db_user_info, "pnLastNameAtBirth",
2405 user->personName->pnLastNameAtBirth);
2407 if (user->address) {
2408 INSERT_STRING(db_user_info, "adCity", user->address->adCity);
2409 INSERT_STRING(db_user_info, "adStreet", user->address->adStreet);
2410 INSERT_STRING(db_user_info, "adNumberInStreet",
2411 user->address->adNumberInStreet);
2412 INSERT_STRING(db_user_info, "adNumberInMunicipality",
2413 user->address->adNumberInMunicipality);
2414 INSERT_STRING(db_user_info, "adZipCode", user->address->adZipCode);
2415 INSERT_STRING(db_user_info, "adState", user->address->adState);
2417 if (user->biDate) {
2418 if (!tm2datestring(user->biDate, &string))
2419 INSERT_STRING(db_user_info, "biDate", string);
2420 zfree(string);
2422 CHECK_FOR_STRING_LENGTH(user->userID, 6, 12, "userID");
2423 INSERT_STRING(db_user_info, "userID", user->userID);
2425 /* userType */
2426 if (user->userType) {
2427 const xmlChar *type_string = isds_UserType2string(*(user->userType));
2428 if (!type_string) {
2429 isds_printf_message(context, _("Invalid userType value: %d"),
2430 *(user->userType));
2431 err = IE_ENUM;
2432 goto leave;
2434 INSERT_STRING(db_user_info, "userType", type_string);
2437 INSERT_LONGINT(db_user_info, "userPrivils", user->userPrivils, string);
2438 CHECK_FOR_STRING_LENGTH(user->ic, 0, 8, "ic")
2439 INSERT_STRING(db_user_info, "ic", user->ic);
2440 CHECK_FOR_STRING_LENGTH(user->firmName, 0, 100, "firmName")
2441 INSERT_STRING(db_user_info, "firmName", user->firmName);
2442 INSERT_STRING(db_user_info, "caStreet", user->caStreet);
2443 INSERT_STRING(db_user_info, "caCity", user->caCity);
2444 INSERT_STRING(db_user_info, "caZipCode", user->caZipCode);
2446 leave:
2447 free(string);
2448 return err;
2452 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
2453 * isds_envelope structure. The envelope is automatically allocated but not
2454 * reallocated. The date are just appended into envelope structure.
2455 * @context is ISDS context
2456 * @envelope is automically allocated message envelope structure
2457 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
2458 * In case of error @envelope will be freed. */
2459 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
2460 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2461 isds_error err = IE_SUCCESS;
2462 xmlXPathObjectPtr result = NULL;
2464 if (!context) return IE_INVALID_CONTEXT;
2465 if (!envelope) return IE_INVAL;
2466 if (!xpath_ctx) return IE_INVAL;
2469 if (!*envelope) {
2470 /* Allocate envelope */
2471 *envelope = calloc(1, sizeof(**envelope));
2472 if (!*envelope) {
2473 err = IE_NOMEM;
2474 goto leave;
2476 } else {
2477 /* Else free former data */
2478 zfree((*envelope)->dmSenderOrgUnit);
2479 zfree((*envelope)->dmSenderOrgUnitNum);
2480 zfree((*envelope)->dbIDRecipient);
2481 zfree((*envelope)->dmRecipientOrgUnit);
2482 zfree((*envelope)->dmSenderOrgUnitNum);
2483 zfree((*envelope)->dmToHands);
2484 zfree((*envelope)->dmAnnotation);
2485 zfree((*envelope)->dmRecipientRefNumber);
2486 zfree((*envelope)->dmSenderRefNumber);
2487 zfree((*envelope)->dmRecipientIdent);
2488 zfree((*envelope)->dmSenderIdent);
2489 zfree((*envelope)->dmLegalTitleLaw);
2490 zfree((*envelope)->dmLegalTitleYear);
2491 zfree((*envelope)->dmLegalTitleSect);
2492 zfree((*envelope)->dmLegalTitlePar);
2493 zfree((*envelope)->dmLegalTitlePoint);
2494 zfree((*envelope)->dmPersonalDelivery);
2495 zfree((*envelope)->dmAllowSubstDelivery);
2498 /* Extract envelope elements added by sender or ISDS
2499 * (XSD: gMessageEnvelopeSub type) */
2500 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
2501 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
2502 (*envelope)->dmSenderOrgUnitNum, 0);
2503 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
2504 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
2505 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
2506 (*envelope)->dmSenderOrgUnitNum, 0);
2507 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
2508 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
2509 EXTRACT_STRING("isds:dmRecipientRefNumber",
2510 (*envelope)->dmRecipientRefNumber);
2511 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
2512 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
2513 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
2515 /* Extract envelope elements regarding law refference */
2516 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
2517 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
2518 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
2519 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
2520 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
2522 /* Extract envelope other elements */
2523 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
2524 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
2525 (*envelope)->dmAllowSubstDelivery);
2527 leave:
2528 if (err) isds_envelope_free(envelope);
2529 xmlXPathFreeObject(result);
2530 return err;
2535 /* Convert XSD gMessageEnvelope group of elements from XML tree into
2536 * isds_envelope structure. The envelope is automatically allocated but not
2537 * reallocated. The date are just appended into envelope structure.
2538 * @context is ISDS context
2539 * @envelope is automically allocated message envelope structure
2540 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
2541 * In case of error @envelope will be freed. */
2542 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
2543 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2544 isds_error err = IE_SUCCESS;
2545 xmlXPathObjectPtr result = NULL;
2547 if (!context) return IE_INVALID_CONTEXT;
2548 if (!envelope) return IE_INVAL;
2549 if (!xpath_ctx) return IE_INVAL;
2552 if (!*envelope) {
2553 /* Allocate envelope */
2554 *envelope = calloc(1, sizeof(**envelope));
2555 if (!*envelope) {
2556 err = IE_NOMEM;
2557 goto leave;
2559 } else {
2560 /* Else free former data */
2561 zfree((*envelope)->dmID);
2562 zfree((*envelope)->dbIDSender);
2563 zfree((*envelope)->dmSender);
2564 zfree((*envelope)->dmSenderAddress);
2565 zfree((*envelope)->dmSenderType);
2566 zfree((*envelope)->dmRecipient);
2567 zfree((*envelope)->dmRecipientAddress);
2568 zfree((*envelope)->dmAmbiguousRecipient);
2571 /* Extract envelope elements added by ISDS
2572 * (XSD: gMessageEnvelope type) */
2573 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
2574 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
2575 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
2576 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
2577 /* XML Schema does not guaratee enumratation. It's plain xs:int. */
2578 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
2579 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
2580 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
2581 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
2582 (*envelope)->dmAmbiguousRecipient);
2584 /* Extract envelope elements added by sender and ISDS
2585 * (XSD: gMessageEnvelope type) */
2586 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
2587 if (err) goto leave;
2589 leave:
2590 if (err) isds_envelope_free(envelope);
2591 xmlXPathFreeObject(result);
2592 return err;
2596 /* Convert other envelope elements from XML tree into isds_envelope structure:
2597 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
2598 * The envelope is automatically allocated but not reallocated.
2599 * The data are just appended into envelope structure.
2600 * @context is ISDS context
2601 * @envelope is automically allocated message envelope structure
2602 * @xpath_ctx is XPath context with current node as parent desired elements
2603 * In case of error @envelope will be freed. */
2604 static isds_error append_status_size_times(struct isds_ctx *context,
2605 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2606 isds_error err = IE_SUCCESS;
2607 xmlXPathObjectPtr result = NULL;
2608 char *string = NULL;
2609 unsigned long int *unumber = NULL;
2611 if (!context) return IE_INVALID_CONTEXT;
2612 if (!envelope) return IE_INVAL;
2613 if (!xpath_ctx) return IE_INVAL;
2616 if (!*envelope) {
2617 /* Allocate new */
2618 *envelope = calloc(1, sizeof(**envelope));
2619 if (!*envelope) {
2620 err = IE_NOMEM;
2621 goto leave;
2623 } else {
2624 /* Free old data */
2625 zfree((*envelope)->dmMessageStatus);
2626 zfree((*envelope)->dmAttachmentSize);
2627 zfree((*envelope)->dmDeliveryTime);
2628 zfree((*envelope)->dmAcceptanceTime);
2632 /* dmMessageStatus element is mandatory */
2633 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
2634 if (!unumber) {
2635 isds_log_message(context,
2636 _("Missing mandatory sisds:dmMessageStatus integer"));
2637 err = IE_ISDS;
2638 goto leave;
2640 err = uint2isds_message_status(context, unumber,
2641 &((*envelope)->dmMessageStatus));
2642 if (err) {
2643 if (err == IE_ENUM) err = IE_ISDS;
2644 goto leave;
2646 free(unumber); unumber = NULL;
2648 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
2651 EXTRACT_STRING("sisds:dmDeliveryTime", string);
2652 if (string) {
2653 err = timestring2timeval((xmlChar *) string,
2654 &((*envelope)->dmDeliveryTime));
2655 if (err) {
2656 char *string_locale = utf82locale(string);
2657 if (err == IE_DATE) err = IE_ISDS;
2658 isds_printf_message(context,
2659 _("Could not convert dmDeliveryTime as ISO time: %s"),
2660 string_locale);
2661 free(string_locale);
2662 goto leave;
2664 zfree(string);
2667 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
2668 if (string) {
2669 err = timestring2timeval((xmlChar *) string,
2670 &((*envelope)->dmAcceptanceTime));
2671 if (err) {
2672 char *string_locale = utf82locale(string);
2673 if (err == IE_DATE) err = IE_ISDS;
2674 isds_printf_message(context,
2675 _("Could not convert dmAcceptanceTime as ISO time: %s"),
2676 string_locale);
2677 free(string_locale);
2678 goto leave;
2680 zfree(string);
2683 leave:
2684 if (err) isds_envelope_free(envelope);
2685 free(unumber);
2686 free(string);
2687 xmlXPathFreeObject(result);
2688 return err;
2692 /* Convert message type attribute of current element into isds_envelope
2693 * structure.
2694 * TODO: This function can be incorporated into append_status_size_times() as
2695 * they are called always together.
2696 * The envelope is automatically allocated but not reallocated.
2697 * The data are just appended into envelope structure.
2698 * @context is ISDS context
2699 * @envelope is automically allocated message envelope structure
2700 * @xpath_ctx is XPath context with current node as parent of attribute
2701 * carrying message type
2702 * In case of error @envelope will be freed. */
2703 static isds_error append_message_type(struct isds_ctx *context,
2704 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2705 isds_error err = IE_SUCCESS;
2707 if (!context) return IE_INVALID_CONTEXT;
2708 if (!envelope) return IE_INVAL;
2709 if (!xpath_ctx) return IE_INVAL;
2712 if (!*envelope) {
2713 /* Allocate new */
2714 *envelope = calloc(1, sizeof(**envelope));
2715 if (!*envelope) {
2716 err = IE_NOMEM;
2717 goto leave;
2719 } else {
2720 /* Free old data */
2721 zfree((*envelope)->dmType);
2725 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope)->dmType, 0);
2727 if (!(*envelope)->dmType) {
2728 /* Use default value */
2729 (*envelope)->dmType = strdup("V");
2730 if (!(*envelope)->dmType) {
2731 err = IE_NOMEM;
2732 goto leave;
2734 } else if (1 != xmlUTF8Strlen((xmlChar *) (*envelope)->dmType)) {
2735 char *type_locale = utf82locale((*envelope)->dmType);
2736 isds_printf_message(context,
2737 _("Message type in dmType attribute is not 1 character long: "
2738 "%s"),
2739 type_locale);
2740 free(type_locale);
2741 err = IE_ISDS;
2742 goto leave;
2745 leave:
2746 if (err) isds_envelope_free(envelope);
2747 return err;
2752 /* Extract message document into reallocated document structure
2753 * @context is ISDS context
2754 * @document is automically reallocated message documents structure
2755 * @xpath_ctx is XPath context with current node as isds:dmFile
2756 * In case of error @document will be freed. */
2757 static isds_error extract_document(struct isds_ctx *context,
2758 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
2759 isds_error err = IE_SUCCESS;
2760 xmlXPathObjectPtr result = NULL;
2761 xmlNodePtr file_node = xpath_ctx->node;
2762 char *string = NULL;
2764 if (!context) return IE_INVALID_CONTEXT;
2765 if (!document) return IE_INVAL;
2766 isds_document_free(document);
2767 if (!xpath_ctx) return IE_INVAL;
2769 *document = calloc(1, sizeof(**document));
2770 if (!*document) {
2771 err = IE_NOMEM;
2772 goto leave;
2775 /* Extract document metadata */
2776 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
2778 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
2779 err = string2isds_FileMetaType((xmlChar*)string,
2780 &((*document)->dmFileMetaType));
2781 if (err) {
2782 char *meta_type_locale = utf82locale(string);
2783 isds_printf_message(context,
2784 _("Document has invalid dmFileMetaType attribute value: %s"),
2785 meta_type_locale);
2786 free(meta_type_locale);
2787 err = IE_ISDS;
2788 goto leave;
2790 zfree(string);
2792 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
2793 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
2794 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
2795 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
2798 /* Extract document data.
2799 * Base64 encoded blob or XML subtree must be presented. */
2801 /* Check from dmEncodedContent */
2802 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
2803 xpath_ctx);
2804 if (!result) {
2805 err = IE_XML;
2806 goto leave;
2809 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2810 /* Here we have Base64 blob */
2812 if (result->nodesetval->nodeNr > 1) {
2813 isds_printf_message(context,
2814 _("Document has more dmEncodedContent elements"));
2815 err = IE_ISDS;
2816 goto leave;
2819 xmlXPathFreeObject(result); result = NULL;
2820 EXTRACT_STRING("isds:dmEncodedContent", string);
2822 /* Decode non-emptys document */
2823 if (string && string[0] != '\0') {
2824 (*document)->data_length = b64decode(string, &((*document)->data));
2825 if ((*document)->data_length == (size_t) -1) {
2826 isds_printf_message(context,
2827 _("Error while Base64-decoding document content"));
2828 err = IE_ERROR;
2829 goto leave;
2832 } else {
2833 /* No Base64 blob, try XML document */
2834 xmlXPathFreeObject(result); result = NULL;
2835 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
2836 xpath_ctx);
2837 if (!result) {
2838 err = IE_XML;
2839 goto leave;
2842 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2843 /* Here we have XML document */
2845 if (result->nodesetval->nodeNr > 1) {
2846 isds_printf_message(context,
2847 _("Document has more dmXMLContent elements"));
2848 err = IE_ISDS;
2849 goto leave;
2852 /* FIXME: Serialize the tree rooted at result's node */
2853 isds_printf_message(context,
2854 _("XML documents not yet supported"));
2855 err = IE_NOTSUP;
2856 goto leave;
2857 } else {
2858 /* No bas64 blob, nor XML document */
2859 isds_printf_message(context,
2860 _("Document has no dmEncodedContent, nor dmXMLContent "
2861 "element"));
2862 err = IE_ISDS;
2863 goto leave;
2868 leave:
2869 if (err) isds_document_free(document);
2870 free(string);
2871 xmlXPathFreeObject(result);
2872 xpath_ctx->node = file_node;
2873 return err;
2878 /* Extract message documents into reallocated list of documents
2879 * @context is ISDS context
2880 * @documents is automically reallocated message documents list structure
2881 * @xpath_ctx is XPath context with current node as XSD tFilesArray
2882 * In case of error @documents will be freed. */
2883 static isds_error extract_documents(struct isds_ctx *context,
2884 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
2885 isds_error err = IE_SUCCESS;
2886 xmlXPathObjectPtr result = NULL;
2887 xmlNodePtr files_node = xpath_ctx->node;
2888 struct isds_list *document, *prev_document;
2890 if (!context) return IE_INVALID_CONTEXT;
2891 if (!documents) return IE_INVAL;
2892 isds_list_free(documents);
2893 if (!xpath_ctx) return IE_INVAL;
2895 /* Find documents */
2896 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
2897 if (!result) {
2898 err = IE_XML;
2899 goto leave;
2902 /* No match */
2903 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2904 isds_printf_message(context,
2905 _("Message does not contain any document"));
2906 err = IE_ISDS;
2907 goto leave;
2911 /* Iterate over documents */
2912 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
2914 /* Allocate and append list item */
2915 document = calloc(1, sizeof(*document));
2916 if (!document) {
2917 err = IE_NOMEM;
2918 goto leave;
2920 document->destructor = (void (*)(void **))isds_document_free;
2921 if (i == 0) *documents = document;
2922 else prev_document->next = document;
2923 prev_document = document;
2925 /* Extract document */
2926 xpath_ctx->node = result->nodesetval->nodeTab[i];
2927 err = extract_document(context,
2928 (struct isds_document **) &(document->data), xpath_ctx);
2929 if (err) goto leave;
2933 leave:
2934 if (err) isds_list_free(documents);
2935 xmlXPathFreeObject(result);
2936 xpath_ctx->node = files_node;
2937 return err;
2941 /* Convert isds:dmRecord XML tree into structure
2942 * @context is ISDS context
2943 * @envelope is automically reallocated message envelope structure
2944 * @xpath_ctx is XPath context with current node as isds:dmRecord element
2945 * In case of error @envelope will be freed. */
2946 static isds_error extract_DmRecord(struct isds_ctx *context,
2947 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2948 isds_error err = IE_SUCCESS;
2949 xmlXPathObjectPtr result = NULL;
2951 if (!context) return IE_INVALID_CONTEXT;
2952 if (!envelope) return IE_INVAL;
2953 isds_envelope_free(envelope);
2954 if (!xpath_ctx) return IE_INVAL;
2957 *envelope = calloc(1, sizeof(**envelope));
2958 if (!*envelope) {
2959 err = IE_NOMEM;
2960 goto leave;
2964 /* Extract tRecord data */
2965 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
2967 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
2968 * dmAcceptanceTime. */
2969 err = append_status_size_times(context, envelope, xpath_ctx);
2970 if (err) goto leave;
2972 /* Extract envelope elements added by sender and ISDS
2973 * (XSD: gMessageEnvelope type) */
2974 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
2975 if (err) goto leave;
2976 /* dmOVM can not be obtained from ISDS */
2978 /* Get message type */
2979 err = append_message_type(context, envelope, xpath_ctx);
2980 if (err) goto leave;
2983 leave:
2984 if (err) isds_envelope_free(envelope);
2985 xmlXPathFreeObject(result);
2986 return err;
2990 /* Find and convert isds:dmHash XML tree into structure
2991 * @context is ISDS context
2992 * @envelope is automically reallocated message hash structure
2993 * @xpath_ctx is XPath context with current node containing isds:dmHash child
2994 * In case of error @hash will be freed. */
2995 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
2996 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
2997 isds_error err = IE_SUCCESS;
2998 xmlNodePtr old_ctx_node;
2999 xmlXPathObjectPtr result = NULL;
3000 char *string = NULL;
3002 if (!context) return IE_INVALID_CONTEXT;
3003 if (!hash) return IE_INVAL;
3004 isds_hash_free(hash);
3005 if (!xpath_ctx) return IE_INVAL;
3007 old_ctx_node = xpath_ctx->node;
3009 *hash = calloc(1, sizeof(**hash));
3010 if (!*hash) {
3011 err = IE_NOMEM;
3012 goto leave;
3015 /* Locate dmHash */
3016 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
3017 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
3018 err = IE_ISDS;
3019 goto leave;
3021 if (err) {
3022 err = IE_ERROR;
3023 goto leave;
3026 /* Get hash algorithm */
3027 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
3028 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
3029 if (err) {
3030 if (err == IE_ENUM) {
3031 char *string_locale = utf82locale(string);
3032 isds_printf_message(context, _("Unsported hash algorithm: %s"),
3033 string_locale);
3034 free(string_locale);
3036 goto leave;
3038 zfree(string);
3040 /* Get hash value */
3041 EXTRACT_STRING(".", string);
3042 if (!string) {
3043 isds_printf_message(context, _("tHash element is missing hash value"));
3044 err = IE_ISDS;
3045 goto leave;
3047 (*hash)->length = b64decode(string, &((*hash)->value));
3048 if ((*hash)->length == (size_t) -1) {
3049 isds_printf_message(context,
3050 _("Error while Base64-decoding hash value"));
3051 err = IE_ERROR;
3052 goto leave;
3055 leave:
3056 if (err) isds_hash_free(hash);
3057 free(string);
3058 xmlXPathFreeObject(result);
3059 xpath_ctx->node = old_ctx_node;
3060 return err;
3064 /* Find and append isds:dmQTimestamp XML tree into envelope
3065 * @context is ISDS context
3066 * @envelope is automically allocated evnelope structure
3067 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
3068 * child
3069 * In case of error @envelope will be freed. */
3070 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
3071 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3072 isds_error err = IE_SUCCESS;
3073 xmlXPathObjectPtr result = NULL;
3074 char *string = NULL;
3076 if (!context) return IE_INVALID_CONTEXT;
3077 if (!envelope) return IE_INVAL;
3078 if (!xpath_ctx) {
3079 isds_envelope_free(envelope);
3080 return IE_INVAL;
3083 if (!*envelope) {
3084 *envelope = calloc(1, sizeof(**envelope));
3085 if (!*envelope) {
3086 err = IE_NOMEM;
3087 goto leave;
3089 } else {
3090 zfree((*envelope)->timestamp);
3091 (*envelope)->timestamp_length = 0;
3094 /* Get dmQTimestamp */
3095 EXTRACT_STRING("sisds:dmQTimestamp", string);
3096 if (!string) {
3097 isds_printf_message(context, _("Missing dmQTimestamp element content"));
3098 err = IE_ISDS;
3099 goto leave;
3101 (*envelope)->timestamp_length =
3102 b64decode(string, &((*envelope)->timestamp));
3103 if ((*envelope)->timestamp_length == (size_t) -1) {
3104 isds_printf_message(context,
3105 _("Error while Base64-decoding timestamp value"));
3106 err = IE_ERROR;
3107 goto leave;
3110 leave:
3111 if (err) isds_envelope_free(envelope);
3112 free(string);
3113 xmlXPathFreeObject(result);
3114 return err;
3118 /* Convert XSD tReturnedMessage XML tree into message structure.
3119 * It doea not store XML tree into message->raw.
3120 * @context is ISDS context
3121 * @include_documents Use true if documents must be extracted
3122 * (tReturnedMessage XSD type), use false if documents shall be ommited
3123 * (tReturnedMessageEnvelope).
3124 * @message is automically reallocated message structure
3125 * @xpath_ctx is XPath context with current node as tReturnedMessage element
3126 * type
3127 * In case of error @message will be freed. */
3128 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
3129 const _Bool include_documents, struct isds_message **message,
3130 xmlXPathContextPtr xpath_ctx) {
3131 isds_error err = IE_SUCCESS;
3132 xmlNodePtr message_node;
3134 if (!context) return IE_INVALID_CONTEXT;
3135 if (!message) return IE_INVAL;
3136 isds_message_free(message);
3137 if (!xpath_ctx) return IE_INVAL;
3140 *message = calloc(1, sizeof(**message));
3141 if (!*message) {
3142 err = IE_NOMEM;
3143 goto leave;
3146 /* Save message XPATH context node */
3147 message_node = xpath_ctx->node;
3150 /* Extract dmDM */
3151 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
3152 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
3153 if (err) { err = IE_ERROR; goto leave; }
3154 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
3155 if (err) goto leave;
3157 if (include_documents) {
3158 /* Extract dmFiles */
3159 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
3160 xpath_ctx);
3161 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
3162 err = IE_ISDS; goto leave;
3164 if (err) { err = IE_ERROR; goto leave; }
3165 err = extract_documents(context, &((*message)->documents), xpath_ctx);
3166 if (err) goto leave;
3170 /* Restore context to message */
3171 xpath_ctx->node = message_node;
3173 /* Extract dmHash */
3174 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
3175 xpath_ctx);
3176 if (err) goto leave;
3178 /* Extract dmQTimestamp, */
3179 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
3180 xpath_ctx);
3181 if (err) goto leave;
3183 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
3184 * dmAcceptanceTime. */
3185 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
3186 if (err) goto leave;
3188 /* Get message type */
3189 err = append_message_type(context, &((*message)->envelope), xpath_ctx);
3190 if (err) goto leave;
3192 leave:
3193 if (err) isds_message_free(message);
3194 return err;
3198 /* Extract message event into reallocated isds_event structure
3199 * @context is ISDS context
3200 * @event is automically reallocated message event structure
3201 * @xpath_ctx is XPath context with current node as isds:dmEvent
3202 * In case of error @event will be freed. */
3203 static isds_error extract_event(struct isds_ctx *context,
3204 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
3205 isds_error err = IE_SUCCESS;
3206 xmlXPathObjectPtr result = NULL;
3207 xmlNodePtr event_node = xpath_ctx->node;
3208 char *string = NULL;
3210 if (!context) return IE_INVALID_CONTEXT;
3211 if (!event) return IE_INVAL;
3212 isds_event_free(event);
3213 if (!xpath_ctx) return IE_INVAL;
3215 *event = calloc(1, sizeof(**event));
3216 if (!*event) {
3217 err = IE_NOMEM;
3218 goto leave;
3221 /* Extract event data.
3222 * All elements are optional according XSD. That's funny. */
3223 EXTRACT_STRING("sisds:dmEventTime", string);
3224 if (string) {
3225 err = timestring2timeval((xmlChar *) string, &((*event)->time));
3226 if (err) {
3227 char *string_locale = utf82locale(string);
3228 if (err == IE_DATE) err = IE_ISDS;
3229 isds_printf_message(context,
3230 _("Could not convert dmEventTime as ISO time: %s"),
3231 string_locale);
3232 free(string_locale);
3233 goto leave;
3235 zfree(string);
3238 /* dmEventDescr element has prefix and the rest */
3239 EXTRACT_STRING("sisds:dmEventDescr", string);
3240 if (string) {
3241 err = eventstring2event((xmlChar *) string, *event);
3242 if (err) goto leave;
3243 zfree(string);
3246 leave:
3247 if (err) isds_event_free(event);
3248 free(string);
3249 xmlXPathFreeObject(result);
3250 xpath_ctx->node = event_node;
3251 return err;
3255 /* Convert element of XSD tEventsArray type from XML tree into
3256 * isds_list of isds_event's structure. The list is automatically reallocated.
3257 * @context is ISDS context
3258 * @events is automically reallocated list of event structures
3259 * @xpath_ctx is XPath context with current node as tEventsArray
3260 * In case of error @evnets will be freed. */
3261 static isds_error extract_events(struct isds_ctx *context,
3262 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
3263 isds_error err = IE_SUCCESS;
3264 xmlXPathObjectPtr result = NULL;
3265 xmlNodePtr events_node = xpath_ctx->node;
3266 struct isds_list *event, *prev_event = NULL;
3268 if (!context) return IE_INVALID_CONTEXT;
3269 if (!events) return IE_INVAL;
3270 if (!xpath_ctx) return IE_INVAL;
3272 /* Free old list */
3273 isds_list_free(events);
3275 /* Find events */
3276 result = xmlXPathEvalExpression(BAD_CAST "sisds:dmEvent", xpath_ctx);
3277 if (!result) {
3278 err = IE_XML;
3279 goto leave;
3282 /* No match */
3283 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3284 isds_printf_message(context,
3285 _("Delivery info does not contain any event"));
3286 err = IE_ISDS;
3287 goto leave;
3291 /* Iterate over events */
3292 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
3294 /* Allocate and append list item */
3295 event = calloc(1, sizeof(*event));
3296 if (!event) {
3297 err = IE_NOMEM;
3298 goto leave;
3300 event->destructor = (void (*)(void **))isds_event_free;
3301 if (i == 0) *events = event;
3302 else prev_event->next = event;
3303 prev_event = event;
3305 /* Extract event */
3306 xpath_ctx->node = result->nodesetval->nodeTab[i];
3307 err = extract_event(context,
3308 (struct isds_event **) &(event->data), xpath_ctx);
3309 if (err) goto leave;
3313 leave:
3314 if (err) isds_list_free(events);
3315 xmlXPathFreeObject(result);
3316 xpath_ctx->node = events_node;
3317 return err;
3321 /* Convert isds_document structure into XML tree and append to dmFiles node.
3322 * @context is session context
3323 * @document is ISDS document
3324 * @dm_files is XML element the resulting tree will be appended to as a child.
3325 * @return error code, in case of error context' message is filled. */
3326 static isds_error insert_document(struct isds_ctx *context,
3327 struct isds_document *document, xmlNodePtr dm_files) {
3328 isds_error err = IE_SUCCESS;
3329 xmlNodePtr new_file = NULL, file = NULL, node;
3330 xmlAttrPtr attribute_node;
3331 xmlChar *base64data = NULL;
3333 if (!context) return IE_INVALID_CONTEXT;
3334 if (!document || !dm_files) return IE_INVAL;
3336 /* Allocate new dmFile */
3337 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
3338 if (!new_file) {
3339 isds_printf_message(context, _("Could not allocate main dmFile"));
3340 err = IE_ERROR;
3341 goto leave;
3343 /* Append the new dmFile.
3344 * XXX: Main document must go first */
3345 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
3346 file = xmlAddPrevSibling(dm_files->children, new_file);
3347 else
3348 file = xmlAddChild(dm_files, new_file);
3350 if (!file) {
3351 xmlFreeNode(new_file); new_file = NULL;
3352 isds_printf_message(context, _("Could not add dmFile child to "
3353 "%s element"), dm_files->name);
3354 err = IE_ERROR;
3355 goto leave;
3358 /* @dmMimeType is required */
3359 if (!document->dmMimeType) {
3360 isds_log_message(context,
3361 _("Document is missing mandatory MIME type definition"));
3362 err = IE_INVAL;
3363 goto leave;
3365 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
3367 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
3368 if (!string) {
3369 isds_printf_message(context,
3370 _("Document has unkown dmFileMetaType: %ld"),
3371 document->dmFileMetaType);
3372 err = IE_ENUM;
3373 goto leave;
3375 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
3377 if (document->dmFileGuid) {
3378 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
3380 if (document->dmUpFileGuid) {
3381 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
3384 /* @dmFileDescr is required */
3385 if (!document->dmFileDescr) {
3386 isds_log_message(context,
3387 _("Document is missing mandatory description (title)"));
3388 err = IE_INVAL;
3389 goto leave;
3391 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
3393 if (document->dmFormat) {
3394 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
3398 /* Insert content (data) of the document. */
3399 /* XXX; Only base64 is implemented currently. */
3400 base64data = (xmlChar *) b64encode(document->data, document->data_length);
3401 if (!base64data) {
3402 isds_printf_message(context,
3403 _("Not enought memory to encode %zd bytes into Base64"),
3404 document->data_length);
3405 err = IE_NOMEM;
3406 goto leave;
3408 INSERT_STRING(file, "dmEncodedContent", base64data);
3409 free(base64data);
3411 leave:
3412 return err;
3416 /* Append XSD tMStatus XML tree into isds_message_copy structure.
3417 * The copy must pre prealocated, the date are just appended into structure.
3418 * @context is ISDS context
3419 * @copy is message copy struture
3420 * @xpath_ctx is XPath context with current node as tMStatus */
3421 static isds_error append_TMStatus(struct isds_ctx *context,
3422 struct isds_message_copy *copy, xmlXPathContextPtr xpath_ctx) {
3423 isds_error err = IE_SUCCESS;
3424 xmlXPathObjectPtr result = NULL;
3425 char *code = NULL, *message = NULL;
3427 if (!context) return IE_INVALID_CONTEXT;
3428 if (!copy || !xpath_ctx) return IE_INVAL;
3430 /* Free old values */
3431 zfree(copy->dmStatus);
3432 zfree(copy->dmID);
3434 /* Get error specific to this copy */
3435 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code);
3436 if (!code) {
3437 isds_log_message(context,
3438 _("Missing isds:dmStatusCode under "
3439 "XSD:tMStatus type element"));
3440 err = IE_ISDS;
3441 goto leave;
3444 if (xmlStrcmp((const xmlChar *)code, BAD_CAST "0000")) {
3445 /* This copy failed */
3446 copy->error = IE_ISDS;
3447 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message);
3448 if (message) {
3449 copy->dmStatus = astrcat3(code, ": ", message);
3450 if (!copy->dmStatus) {
3451 copy->dmStatus = code;
3452 code = NULL;
3454 } else {
3455 copy->dmStatus = code;
3456 code = NULL;
3458 } else {
3459 /* This copy succeeded. In this case only, message ID is valid */
3460 copy->error = IE_SUCCESS;
3462 EXTRACT_STRING("isds:dmID", copy->dmID);
3463 if (!copy->dmID) {
3464 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
3465 "but did not returned assigned message ID\n"));
3466 err = IE_ISDS;
3470 leave:
3471 free(code);
3472 free(message);
3473 xmlXPathFreeObject(result);
3474 return err;
3478 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
3479 * code
3480 * @context is session context
3481 * @service_name is name of SERVICE_DB_ACCESS
3482 * @response is server SOAP body response as XML document
3483 * @raw_response is automatically reallocated bitstream with response body. Use
3484 * NULL if you don't care
3485 * @raw_response_length is size of @raw_response in bytes
3486 * @code is ISDS status code
3487 * @status_message is ISDS status message
3488 * @return error coded from lower layer, context message will be set up
3489 * appropriately. */
3490 static isds_error build_send_check_dbdummy_request(struct isds_ctx *context,
3491 const xmlChar *service_name,
3492 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
3493 xmlChar **code, xmlChar **status_message) {
3495 isds_error err = IE_SUCCESS;
3496 char *service_name_locale = NULL;
3497 xmlNodePtr request = NULL, node;
3498 xmlNsPtr isds_ns = NULL;
3500 if (!context) return IE_INVALID_CONTEXT;
3501 if (!service_name) return IE_INVAL;
3502 if (!response || !code || !status_message) return IE_INVAL;
3503 if (!raw_response_length && raw_response) return IE_INVAL;
3505 /* Free output argument */
3506 xmlFreeDoc(*response); *response = NULL;
3507 if (raw_response) zfree(*raw_response);
3508 free(*code);
3509 free(*status_message);
3512 /* Check if connection is established
3513 * TODO: This check should be done donwstairs. */
3514 if (!context->curl) return IE_CONNECTION_CLOSED;
3516 service_name_locale = utf82locale((char*)service_name);
3517 if (!service_name_locale) {
3518 err = IE_NOMEM;
3519 goto leave;
3522 /* Build request */
3523 request = xmlNewNode(NULL, service_name);
3524 if (!request) {
3525 isds_printf_message(context,
3526 _("Could not build %s request"), service_name_locale);
3527 err = IE_ERROR;
3528 goto leave;
3530 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
3531 if(!isds_ns) {
3532 isds_log_message(context, _("Could not create ISDS name space"));
3533 err = IE_ERROR;
3534 goto leave;
3536 xmlSetNs(request, isds_ns);
3539 /* Add XSD:tDummyInput child */
3540 INSERT_STRING(request, "dbDummy", NULL);
3543 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
3544 service_name_locale);
3546 /* Send request */
3547 err = isds(context, SERVICE_DB_ACCESS, request, response,
3548 raw_response, raw_response_length);
3549 xmlFreeNode(request); request = NULL;
3551 if (err) {
3552 isds_log(ILF_ISDS, ILL_DEBUG,
3553 _("Processing ISDS response on %s request failed\n"),
3554 service_name_locale);
3555 goto leave;
3558 /* Check for response status */
3559 err = isds_response_status(context, SERVICE_DB_ACCESS, *response,
3560 code, status_message, NULL);
3561 if (err) {
3562 isds_log(ILF_ISDS, ILL_DEBUG,
3563 _("ISDS response on %s request is missing status\n"),
3564 service_name_locale);
3565 goto leave;
3568 /* Request processed, but nothing found */
3569 if (xmlStrcmp(*code, BAD_CAST "0000")) {
3570 char *code_locale = utf82locale((char*) *code);
3571 char *status_message_locale = utf82locale((char*) *status_message);
3572 isds_log(ILF_ISDS, ILL_DEBUG,
3573 _("Server refused %s request (code=%s, message=%s)\n"),
3574 service_name_locale, code_locale, status_message_locale);
3575 isds_log_message(context, status_message_locale);
3576 free(code_locale);
3577 free(status_message_locale);
3578 err = IE_ISDS;
3579 goto leave;
3582 leave:
3583 free(service_name_locale);
3584 xmlFreeNode(request);
3585 return err;
3589 /* Get data about logged in user and his box. */
3590 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
3591 struct isds_DbOwnerInfo **db_owner_info) {
3592 isds_error err = IE_SUCCESS;
3593 xmlDocPtr response = NULL;
3594 xmlChar *code = NULL, *message = NULL;
3595 xmlXPathContextPtr xpath_ctx = NULL;
3596 xmlXPathObjectPtr result = NULL;
3597 char *string = NULL;
3599 if (!context) return IE_INVALID_CONTEXT;
3600 if (!db_owner_info) return IE_INVAL;
3602 /* Check if connection is established */
3603 if (!context->curl) return IE_CONNECTION_CLOSED;
3606 /* Do request and check for success */
3607 err = build_send_check_dbdummy_request(context,
3608 BAD_CAST "GetOwnerInfoFromLogin",
3609 &response, NULL, NULL, &code, &message);
3610 if (err) goto leave;
3613 /* Extract data */
3614 /* Prepare stucture */
3615 isds_DbOwnerInfo_free(db_owner_info);
3616 *db_owner_info = calloc(1, sizeof(**db_owner_info));
3617 if (!*db_owner_info) {
3618 err = IE_NOMEM;
3619 goto leave;
3621 xpath_ctx = xmlXPathNewContext(response);
3622 if (!xpath_ctx) {
3623 err = IE_ERROR;
3624 goto leave;
3626 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
3627 err = IE_ERROR;
3628 goto leave;
3631 /* Set context node */
3632 result = xmlXPathEvalExpression(BAD_CAST
3633 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
3634 if (!result) {
3635 err = IE_ERROR;
3636 goto leave;
3638 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3639 isds_log_message(context, _("Missing dbOwnerInfo element"));
3640 err = IE_ISDS;
3641 goto leave;
3643 if (result->nodesetval->nodeNr > 1) {
3644 isds_log_message(context, _("Multiple dbOwnerInfo element"));
3645 err = IE_ISDS;
3646 goto leave;
3648 xpath_ctx->node = result->nodesetval->nodeTab[0];
3649 xmlXPathFreeObject(result); result = NULL;
3651 /* Extract it */
3652 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
3654 leave:
3655 if (err) {
3656 isds_DbOwnerInfo_free(db_owner_info);
3659 free(string);
3660 xmlXPathFreeObject(result);
3661 xmlXPathFreeContext(xpath_ctx);
3663 free(code);
3664 free(message);
3665 xmlFreeDoc(response);
3667 if (!err)
3668 isds_log(ILF_ISDS, ILL_DEBUG,
3669 _("GetOwnerInfoFromLogin request processed by server "
3670 "successfully.\n"));
3672 return err;
3676 /* Get data about logged in user. */
3677 isds_error isds_GetUserInfoFromLogin(struct isds_ctx *context,
3678 struct isds_DbUserInfo **db_user_info) {
3679 isds_error err = IE_SUCCESS;
3680 xmlDocPtr response = NULL;
3681 xmlChar *code = NULL, *message = NULL;
3682 xmlXPathContextPtr xpath_ctx = NULL;
3683 xmlXPathObjectPtr result = NULL;
3685 if (!context) return IE_INVALID_CONTEXT;
3686 if (!db_user_info) return IE_INVAL;
3688 /* Check if connection is established */
3689 if (!context->curl) return IE_CONNECTION_CLOSED;
3692 /* Do request and check for success */
3693 err = build_send_check_dbdummy_request(context,
3694 BAD_CAST "GetUserInfoFromLogin",
3695 &response, NULL, NULL, &code, &message);
3696 if (err) goto leave;
3699 /* Extract data */
3700 /* Prepare stucture */
3701 isds_DbUserInfo_free(db_user_info);
3702 *db_user_info = calloc(1, sizeof(**db_user_info));
3703 if (!*db_user_info) {
3704 err = IE_NOMEM;
3705 goto leave;
3707 xpath_ctx = xmlXPathNewContext(response);
3708 if (!xpath_ctx) {
3709 err = IE_ERROR;
3710 goto leave;
3712 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
3713 err = IE_ERROR;
3714 goto leave;
3717 /* Set context node */
3718 result = xmlXPathEvalExpression(BAD_CAST
3719 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx);
3720 if (!result) {
3721 err = IE_ERROR;
3722 goto leave;
3724 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3725 isds_log_message(context, _("Missing dbUserInfo element"));
3726 err = IE_ISDS;
3727 goto leave;
3729 if (result->nodesetval->nodeNr > 1) {
3730 isds_log_message(context, _("Multiple dbUserInfo element"));
3731 err = IE_ISDS;
3732 goto leave;
3734 xpath_ctx->node = result->nodesetval->nodeTab[0];
3735 xmlXPathFreeObject(result); result = NULL;
3737 /* Extract it */
3738 err = extract_DbUserInfo(context, db_user_info, xpath_ctx);
3740 leave:
3741 if (err) {
3742 isds_DbUserInfo_free(db_user_info);
3745 xmlXPathFreeObject(result);
3746 xmlXPathFreeContext(xpath_ctx);
3748 free(code);
3749 free(message);
3750 xmlFreeDoc(response);
3752 if (!err)
3753 isds_log(ILF_ISDS, ILL_DEBUG,
3754 _("GetUserInfoFromLogin request processed by server "
3755 "successfully.\n"));
3757 return err;
3761 /* Get expiration time of current password
3762 * @context is session context
3763 * @expiration is automatically reallocated time when password expires, In
3764 * case of error will be nulled. */
3765 isds_error isds_get_password_expiration(struct isds_ctx *context,
3766 struct timeval **expiration) {
3767 isds_error err = IE_SUCCESS;
3768 xmlDocPtr response = NULL;
3769 xmlChar *code = NULL, *message = NULL;
3770 xmlXPathContextPtr xpath_ctx = NULL;
3771 xmlXPathObjectPtr result = NULL;
3772 char *string = NULL;
3774 if (!context) return IE_INVALID_CONTEXT;
3775 if (!expiration) return IE_INVAL;
3777 /* Check if connection is established */
3778 if (!context->curl) return IE_CONNECTION_CLOSED;
3781 /* Do request and check for success */
3782 err = build_send_check_dbdummy_request(context,
3783 BAD_CAST "GetPasswordInfo",
3784 &response, NULL, NULL, &code, &message);
3785 if (err) goto leave;
3788 /* Extract data */
3789 xpath_ctx = xmlXPathNewContext(response);
3790 if (!xpath_ctx) {
3791 err = IE_ERROR;
3792 goto leave;
3794 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
3795 err = IE_ERROR;
3796 goto leave;
3799 /* Set context node */
3800 result = xmlXPathEvalExpression(BAD_CAST
3801 "/isds:GetPasswordInfoResponse", xpath_ctx);
3802 if (!result) {
3803 err = IE_ERROR;
3804 goto leave;
3806 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3807 isds_log_message(context,
3808 _("Missing GetPasswordInfoResponse element"));
3809 err = IE_ISDS;
3810 goto leave;
3812 if (result->nodesetval->nodeNr > 1) {
3813 isds_log_message(context,
3814 _("Multiple GetPasswordInfoResponse element"));
3815 err = IE_ISDS;
3816 goto leave;
3818 xpath_ctx->node = result->nodesetval->nodeTab[0];
3819 xmlXPathFreeObject(result); result = NULL;
3821 /* Extract expiration date */
3822 EXTRACT_STRING("isds:pswExpDate", string);
3823 if (!string) {
3824 isds_log_message(context, _("Missing pswExpDate element"));
3825 err = IE_ISDS;
3826 goto leave;
3829 err = timestring2timeval((xmlChar *) string, expiration);
3830 if (err) {
3831 char *string_locale = utf82locale(string);
3832 if (err == IE_DATE) err = IE_ISDS;
3833 isds_printf_message(context,
3834 _("Could not convert pswExpDate as ISO time: %s"),
3835 string_locale);
3836 free(string_locale);
3837 goto leave;
3840 leave:
3841 if (err) {
3842 if (*expiration) {
3843 zfree(*expiration);
3847 free(string);
3848 xmlXPathFreeObject(result);
3849 xmlXPathFreeContext(xpath_ctx);
3851 free(code);
3852 free(message);
3853 xmlFreeDoc(response);
3855 if (!err)
3856 isds_log(ILF_ISDS, ILL_DEBUG,
3857 _("GetPasswordInfo request processed by server "
3858 "successfully.\n"));
3860 return err;
3864 /* Change user password in ISDS.
3865 * User must supply old password, new password will takes effect after some
3866 * time, current session can continue. Password must fulfill some constraints.
3867 * @context is session context
3868 * @old_password is current password.
3869 * @new_password is requested new password */
3870 isds_error isds_change_password(struct isds_ctx *context,
3871 const char *old_password, const char *new_password) {
3872 isds_error err = IE_SUCCESS;
3873 xmlNsPtr isds_ns = NULL;
3874 xmlNodePtr request = NULL, node;
3875 xmlDocPtr response = NULL;
3876 xmlChar *code = NULL, *message = NULL;
3878 if (!context) return IE_INVALID_CONTEXT;
3879 if (!old_password || !new_password) return IE_INVAL;
3881 /* Check if connection is established
3882 * TODO: This check should be done donwstairs. */
3883 if (!context->curl) return IE_CONNECTION_CLOSED;
3886 /* Build ChangeISDSPassword request */
3887 request = xmlNewNode(NULL, BAD_CAST "ChangeISDSPassword");
3888 if (!request) {
3889 isds_log_message(context,
3890 _("Could build ChangeISDSPassword request"));
3891 return IE_ERROR;
3893 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
3894 if(!isds_ns) {
3895 isds_log_message(context, _("Could not create ISDS name space"));
3896 xmlFreeNode(request);
3897 return IE_ERROR;
3899 xmlSetNs(request, isds_ns);
3901 INSERT_STRING(request, "dbOldPassword", old_password);
3902 INSERT_STRING(request, "dbNewPassword", new_password);
3905 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CheckDataBox request to ISDS\n"));
3907 /* Sent request */
3908 err = isds(context, SERVICE_DB_ACCESS, request, &response, NULL, NULL);
3910 /* Destroy request */
3911 xmlFreeNode(request); request = NULL;
3913 if (err) {
3914 isds_log(ILF_ISDS, ILL_DEBUG,
3915 _("Processing ISDS response on ChangeISDSPassword "
3916 "request failed\n"));
3917 goto leave;
3920 /* Check for response status */
3921 err = isds_response_status(context, SERVICE_DB_ACCESS, response,
3922 &code, &message, NULL);
3923 if (err) {
3924 isds_log(ILF_ISDS, ILL_DEBUG,
3925 _("ISDS response on ChangeISDSPassword request is missing "
3926 "status\n"));
3927 goto leave;
3930 /* Request processed, but empty password refused */
3931 if (!xmlStrcmp(code, BAD_CAST "1066")) {
3932 char *code_locale = utf82locale((char*)code);
3933 char *message_locale = utf82locale((char*)message);
3934 isds_log(ILF_ISDS, ILL_DEBUG,
3935 _("Server refused empty password on ChangeISDSPassword "
3936 "request (code=%s, message=%s)\n"),
3937 code_locale, message_locale);
3938 isds_log_message(context, _("Password must not be empty"));
3939 free(code_locale);
3940 free(message_locale);
3941 err = IE_INVAL;
3942 goto leave;
3945 /* Request processed, but new password was reused */
3946 else if (!xmlStrcmp(code, BAD_CAST "1067")) {
3947 char *code_locale = utf82locale((char*)code);
3948 char *message_locale = utf82locale((char*)message);
3949 isds_log(ILF_ISDS, ILL_DEBUG,
3950 _("Server refused the same new password on ChangeISDSPassword "
3951 "request (code=%s, message=%s)\n"),
3952 code_locale, message_locale);
3953 isds_log_message(context,
3954 _("New password must differ from the current one"));
3955 free(code_locale);
3956 free(message_locale);
3957 err = IE_INVAL;
3958 goto leave;
3961 /* Other error */
3962 else if (xmlStrcmp(code, BAD_CAST "0000")) {
3963 char *code_locale = utf82locale((char*)code);
3964 char *message_locale = utf82locale((char*)message);
3965 isds_log(ILF_ISDS, ILL_DEBUG,
3966 _("Server refused to change password on ChangeISDSPassword "
3967 "request (code=%s, message=%s)\n"),
3968 code_locale, message_locale);
3969 isds_log_message(context, message_locale);
3970 free(code_locale);
3971 free(message_locale);
3972 err = IE_ISDS;
3973 goto leave;
3976 /* Otherwise password changed successfully */
3978 leave:
3979 free(code);
3980 free(message);
3981 xmlFreeDoc(response);
3982 xmlFreeNode(request);
3984 if (!err)
3985 isds_log(ILF_ISDS, ILL_DEBUG,
3986 _("Password changed successfully on ChangeISDSPassword "
3987 "request.\n"));
3989 return err;
3993 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
3994 * code
3995 * @context is session context
3996 * @service is SOAP service
3997 * @service_name is name of request in @service
3998 * @box_id is box ID of interrest
3999 * @response is server SOAP body response as XML document
4000 * @raw_response is automatically reallocated bitstream with response body. Use
4001 * NULL if you don't care
4002 * @raw_response_length is size of @raw_response in bytes
4003 * @code is ISDS status code
4004 * @status_message is ISDS status message
4005 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4006 * NULL, if you don't care.
4007 * @return error coded from lower layer, context message will be set up
4008 * appropriately. */
4009 static isds_error build_send_dbid_request_check_response(
4010 struct isds_ctx *context, const isds_service service,
4011 const xmlChar *service_name, const xmlChar *box_id,
4012 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
4013 xmlChar **code, xmlChar **status_message, xmlChar **refnumber) {
4015 isds_error err = IE_SUCCESS;
4016 char *service_name_locale = NULL, *box_id_locale = NULL;
4017 xmlNodePtr request = NULL, node;
4018 xmlNsPtr isds_ns = NULL;
4020 if (!context) return IE_INVALID_CONTEXT;
4021 if (!service_name || !box_id) return IE_INVAL;
4022 if (!response || !code || !status_message) return IE_INVAL;
4023 if (!raw_response_length && raw_response) return IE_INVAL;
4025 /* Free output argument */
4026 xmlFreeDoc(*response); *response = NULL;
4027 if (raw_response) zfree(*raw_response);
4028 free(*code);
4029 free(*status_message);
4032 /* Check if connection is established
4033 * TODO: This check should be done donwstairs. */
4034 if (!context->curl) return IE_CONNECTION_CLOSED;
4036 service_name_locale = utf82locale((char*)service_name);
4037 if (!service_name_locale) {
4038 err = IE_NOMEM;
4039 goto leave;
4041 box_id_locale = utf82locale((char*)box_id);
4042 if (!box_id_locale) {
4043 err = IE_NOMEM;
4044 goto leave;
4047 /* Build request */
4048 request = xmlNewNode(NULL, service_name);
4049 if (!request) {
4050 isds_printf_message(context,
4051 _("Could not build %s request"), service_name_locale);
4052 err = IE_ERROR;
4053 goto leave;
4055 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4056 if(!isds_ns) {
4057 isds_log_message(context, _("Could not create ISDS name space"));
4058 err = IE_ERROR;
4059 goto leave;
4061 xmlSetNs(request, isds_ns);
4064 /* Add XSD:tIdDbInput childs*/
4065 INSERT_STRING(request, "dbID", box_id);
4066 /* TODO: XSD:gExtApproval*/
4069 isds_log(ILF_ISDS, ILL_DEBUG,
4070 _("Sending %s request with %s box ID to ISDS\n"),
4071 service_name_locale, box_id_locale);
4073 /* Send request */
4074 err = isds(context, service, request, response,
4075 raw_response, raw_response_length);
4076 xmlFreeNode(request); request = NULL;
4078 if (err) {
4079 isds_log(ILF_ISDS, ILL_DEBUG,
4080 _("Processing ISDS response on %s request for %s box "
4081 "failed\n"), service_name_locale, box_id_locale);
4082 goto leave;
4085 /* Check for response status */
4086 err = isds_response_status(context, service, *response,
4087 code, status_message, refnumber);
4088 if (err) {
4089 isds_log(ILF_ISDS, ILL_DEBUG,
4090 _("ISDS response on %s request for %s box is missing "
4091 "status\n"), service_name_locale, box_id_locale);
4092 goto leave;
4095 /* Request processed, but nothing found */
4096 if (xmlStrcmp(*code, BAD_CAST "0000")) {
4097 char *code_locale = utf82locale((char*) *code);
4098 char *status_message_locale = utf82locale((char*) *status_message);
4099 isds_log(ILF_ISDS, ILL_DEBUG,
4100 _("Server refused %s request for %s box ID "
4101 "(code=%s, message=%s)\n"),
4102 service_name_locale, box_id_locale,
4103 code_locale, status_message_locale);
4104 isds_log_message(context, status_message_locale);
4105 free(code_locale);
4106 free(status_message_locale);
4107 err = IE_ISDS;
4108 goto leave;
4111 leave:
4112 free(service_name_locale);
4113 free(box_id_locale);
4114 xmlFreeNode(request);
4115 return err;
4119 /* Get data about all users assigned to given box.
4120 * @context is session context
4121 * @box_id is box ID
4122 * @users is automatically reallocated list of struct isds_DbUserInfo */
4123 isds_error isds_GetDataBoxUsers(struct isds_ctx *context, const char *box_id,
4124 struct isds_list **users) {
4125 isds_error err = IE_SUCCESS;
4126 xmlDocPtr response = NULL;
4127 xmlChar *code = NULL, *message = NULL;
4128 xmlXPathContextPtr xpath_ctx = NULL;
4129 xmlXPathObjectPtr result = NULL;
4130 int i;
4131 struct isds_list *item, *prev_item = NULL;
4133 if (!context) return IE_INVALID_CONTEXT;
4134 if (!users || !box_id) return IE_INVAL;
4136 /* Check if connection is established */
4137 if (!context->curl) return IE_CONNECTION_CLOSED;
4140 /* Do request and check for success */
4141 err = build_send_dbid_request_check_response(context,
4142 SERVICE_DB_MANIPULATION,
4143 BAD_CAST "GetDataBoxUsers", BAD_CAST box_id,
4144 &response, NULL, NULL, &code, &message, NULL);
4145 if (err) goto leave;
4148 /* Extract data */
4149 /* Prepare stucture */
4150 isds_list_free(users);
4151 xpath_ctx = xmlXPathNewContext(response);
4152 if (!xpath_ctx) {
4153 err = IE_ERROR;
4154 goto leave;
4156 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4157 err = IE_ERROR;
4158 goto leave;
4161 /* Set context node */
4162 result = xmlXPathEvalExpression(BAD_CAST
4163 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
4164 xpath_ctx);
4165 if (!result) {
4166 err = IE_ERROR;
4167 goto leave;
4169 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4170 isds_log_message(context, _("Missing dbUserInfo element"));
4171 err = IE_ISDS;
4172 goto leave;
4175 /* Iterate over all users */
4176 for (i = 0; i < result->nodesetval->nodeNr; i++) {
4178 /* Prepare structure */
4179 item = calloc(1, sizeof(*item));
4180 if (!item) {
4181 err = IE_NOMEM;
4182 goto leave;
4184 item->destructor = (void(*)(void**))isds_DbUserInfo_free;
4185 if (i == 0) *users = item;
4186 else prev_item->next = item;
4187 prev_item = item;
4189 /* Extract it */
4190 xpath_ctx->node = result->nodesetval->nodeTab[i];
4191 err = extract_DbUserInfo(context,
4192 (struct isds_DbUserInfo **) (&item->data), xpath_ctx);
4193 if (err) goto leave;
4196 leave:
4197 if (err) {
4198 isds_list_free(users);
4201 xmlXPathFreeObject(result);
4202 xmlXPathFreeContext(xpath_ctx);
4204 free(code);
4205 free(message);
4206 xmlFreeDoc(response);
4208 if (!err)
4209 isds_log(ILF_ISDS, ILL_DEBUG,
4210 _("GetDataBoxUsers request processed by server "
4211 "successfully.\n"));
4213 return err;
4217 /* Update data about user assigned to given box.
4218 * @context is session context
4219 * @box is box identification
4220 * @old_user identifies user to update
4221 * @new_user are updated data about @old_user
4222 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4223 * NULL, if you don't care.*/
4224 isds_error isds_UpdateDataBoxUser(struct isds_ctx *context,
4225 const struct isds_DbOwnerInfo *box,
4226 const struct isds_DbUserInfo *old_user,
4227 const struct isds_DbUserInfo *new_user,
4228 char **refnumber) {
4229 isds_error err = IE_SUCCESS;
4230 xmlNsPtr isds_ns = NULL;
4231 xmlNodePtr request = NULL;
4232 xmlDocPtr response = NULL;
4233 xmlChar *code = NULL, *message = NULL;
4234 xmlNodePtr node;
4235 xmlChar *string = NULL;
4238 if (!context) return IE_INVALID_CONTEXT;
4239 if (!box || !old_user || !new_user) return IE_INVAL;
4242 /* Check if connection is established
4243 * TODO: This check should be done donwstairs. */
4244 if (!context->curl) return IE_CONNECTION_CLOSED;
4247 /* Build UpdateDataBoxUser request */
4248 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxUser");
4249 if (!request) {
4250 isds_log_message(context,
4251 _("Could build UpdateDataBoxUser request"));
4252 return IE_ERROR;
4254 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4255 if(!isds_ns) {
4256 isds_log_message(context, _("Could not create ISDS name space"));
4257 xmlFreeNode(request);
4258 return IE_ERROR;
4260 xmlSetNs(request, isds_ns);
4262 INSERT_ELEMENT(node, request, "dbOwnerInfo");
4263 err = insert_DbOwnerInfo(context, box, node);
4264 if (err) goto leave;
4266 INSERT_ELEMENT(node, request, "dbOldUserInfo");
4267 err = insert_DbUserInfo(context, old_user, node);
4268 if (err) goto leave;
4270 INSERT_ELEMENT(node, request, "dbNewUserInfo");
4271 err = insert_DbUserInfo(context, new_user, node);
4272 if (err) goto leave;
4274 isds_log(ILF_ISDS, ILL_DEBUG,
4275 _("Sending UpdateDataBoxUser request to ISDS\n"));
4277 /* Sent request */
4278 err = isds(context, SERVICE_DB_MANIPULATION, request, &response,
4279 NULL, NULL);
4281 /* Destroy request */
4282 xmlFreeNode(request); request = NULL;
4284 if (err) {
4285 isds_log(ILF_ISDS, ILL_DEBUG,
4286 _("Processing ISDS response on UpdateDataBoxUser "
4287 "request failed\n"));
4288 goto leave;
4291 /* Check for response status */
4292 err = isds_response_status(context, SERVICE_DB_MANIPULATION, response,
4293 &code, &message, (xmlChar **) refnumber);
4294 if (err) {
4295 isds_log(ILF_ISDS, ILL_DEBUG,
4296 _("ISDS response on UpdateDataBoxUser request is "
4297 "missing status\n"));
4298 goto leave;
4301 /* Other error */
4302 if (xmlStrcmp(code, BAD_CAST "0000")) {
4303 char *code_locale = utf82locale((char*)code);
4304 char *message_locale = utf82locale((char*)message);
4305 isds_log(ILF_ISDS, ILL_DEBUG,
4306 _("Server refused UpdateDataBoxUser request "
4307 "(code=%s, message=%s)\n"), code_locale, message_locale);
4308 isds_log_message(context, message_locale);
4309 free(code_locale);
4310 free(message_locale);
4311 err = IE_ISDS;
4312 goto leave;
4315 leave:
4316 free(string);
4317 xmlFreeNode(request);
4319 free(code);
4320 free(message);
4321 xmlFreeDoc(response);
4323 if (!err)
4324 isds_log(ILF_ISDS, ILL_DEBUG,
4325 _("UpdateDataBoxUser request processed by server "
4326 "successfully.\n"));
4328 return err;
4332 /* Find boxes suiting given criteria.
4333 * @criteria is filter. You should fill in at least some memebers.
4334 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
4335 * possibly empty. Input NULL or valid old structure.
4336 * @return:
4337 * IE_SUCCESS if search sucseeded, @boxes contains usefull data
4338 * IE_NOEXIST if no such box exists, @boxes will be NULL
4339 * IE_2BIG if too much boxes exist and server truncated the resuluts, @boxes
4340 * contains still valid data
4341 * other code if something bad happens. @boxes will be NULL. */
4342 isds_error isds_FindDataBox(struct isds_ctx *context,
4343 const struct isds_DbOwnerInfo *criteria,
4344 struct isds_list **boxes) {
4345 isds_error err = IE_SUCCESS;
4346 _Bool truncated = 0;
4347 xmlNsPtr isds_ns = NULL;
4348 xmlNodePtr request = NULL;
4349 xmlDocPtr response = NULL;
4350 xmlChar *code = NULL, *message = NULL;
4351 xmlNodePtr db_owner_info;
4352 xmlXPathContextPtr xpath_ctx = NULL;
4353 xmlXPathObjectPtr result = NULL;
4354 xmlChar *string = NULL;
4357 if (!context) return IE_INVALID_CONTEXT;
4358 if (!boxes) return IE_INVAL;
4359 isds_list_free(boxes);
4361 if (!criteria) {
4362 return IE_INVAL;
4365 /* Check if connection is established
4366 * TODO: This check should be done donwstairs. */
4367 if (!context->curl) return IE_CONNECTION_CLOSED;
4370 /* Build FindDataBox request */
4371 request = xmlNewNode(NULL, BAD_CAST "FindDataBox");
4372 if (!request) {
4373 isds_log_message(context,
4374 _("Could build FindDataBox request"));
4375 return IE_ERROR;
4377 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4378 if(!isds_ns) {
4379 isds_log_message(context, _("Could not create ISDS name space"));
4380 xmlFreeNode(request);
4381 return IE_ERROR;
4383 xmlSetNs(request, isds_ns);
4384 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
4385 if (!db_owner_info) {
4386 isds_log_message(context, _("Could not add dbOwnerInfo Child to "
4387 "FindDataBox element"));
4388 xmlFreeNode(request);
4389 return IE_ERROR;
4392 err = insert_DbOwnerInfo(context, criteria, db_owner_info);
4393 if (err) goto leave;
4396 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending FindDataBox request to ISDS\n"));
4398 /* Sent request */
4399 err = isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
4401 /* Destroy request */
4402 xmlFreeNode(request); request = NULL;
4404 if (err) {
4405 isds_log(ILF_ISDS, ILL_DEBUG,
4406 _("Processing ISDS response on FindDataBox "
4407 "request failed\n"));
4408 goto leave;
4411 /* Check for response status */
4412 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
4413 &code, &message, NULL);
4414 if (err) {
4415 isds_log(ILF_ISDS, ILL_DEBUG,
4416 _("ISDS response on FindDataBox request is missing status\n"));
4417 goto leave;
4420 /* Request processed, but nothing found */
4421 if (!xmlStrcmp(code, BAD_CAST "0002") ||
4422 !xmlStrcmp(code, BAD_CAST "5001")) {
4423 char *code_locale = utf82locale((char*)code);
4424 char *message_locale = utf82locale((char*)message);
4425 isds_log(ILF_ISDS, ILL_DEBUG,
4426 _("Server did not found any box on FindDataBox request "
4427 "(code=%s, message=%s)\n"), code_locale, message_locale);
4428 isds_log_message(context, message_locale);
4429 free(code_locale);
4430 free(message_locale);
4431 err = IE_NOEXIST;
4432 goto leave;
4435 /* Warning, not a error */
4436 if (!xmlStrcmp(code, BAD_CAST "0003")) {
4437 char *code_locale = utf82locale((char*)code);
4438 char *message_locale = utf82locale((char*)message);
4439 isds_log(ILF_ISDS, ILL_DEBUG,
4440 _("Server truncated response on FindDataBox request "
4441 "(code=%s, message=%s)\n"), code_locale, message_locale);
4442 isds_log_message(context, message_locale);
4443 free(code_locale);
4444 free(message_locale);
4445 truncated = 1;
4448 /* Other error */
4449 else if (xmlStrcmp(code, BAD_CAST "0000")) {
4450 char *code_locale = utf82locale((char*)code);
4451 char *message_locale = utf82locale((char*)message);
4452 isds_log(ILF_ISDS, ILL_DEBUG,
4453 _("Server refused FindDataBox request "
4454 "(code=%s, message=%s)\n"), code_locale, message_locale);
4455 isds_log_message(context, message_locale);
4456 free(code_locale);
4457 free(message_locale);
4458 err = IE_ISDS;
4459 goto leave;
4462 xpath_ctx = xmlXPathNewContext(response);
4463 if (!xpath_ctx) {
4464 err = IE_ERROR;
4465 goto leave;
4467 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4468 err = IE_ERROR;
4469 goto leave;
4472 /* Extract boxes if they present */
4473 result = xmlXPathEvalExpression(BAD_CAST
4474 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
4475 xpath_ctx);
4476 if (!result) {
4477 err = IE_ERROR;
4478 goto leave;
4480 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4481 struct isds_list *item, *prev_item = NULL;
4482 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4483 item = calloc(1, sizeof(*item));
4484 if (!item) {
4485 err = IE_NOMEM;
4486 goto leave;
4489 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
4490 if (i == 0) *boxes = item;
4491 else prev_item->next = item;
4492 prev_item = item;
4494 xpath_ctx->node = result->nodesetval->nodeTab[i];
4495 err = extract_DbOwnerInfo(context,
4496 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
4497 if (err) goto leave;
4501 leave:
4502 if (err) {
4503 isds_list_free(boxes);
4504 } else {
4505 if (truncated) err = IE_2BIG;
4508 free(string);
4509 xmlFreeNode(request);
4510 xmlXPathFreeObject(result);
4511 xmlXPathFreeContext(xpath_ctx);
4513 free(code);
4514 free(message);
4515 xmlFreeDoc(response);
4517 if (!err)
4518 isds_log(ILF_ISDS, ILL_DEBUG,
4519 _("FindDataBox request processed by server successfully.\n"));
4521 return err;
4525 /* Get status of a box.
4526 * @context is ISDS session context.
4527 * @box_id is UTF-8 encoded box identifier as zero terminated string
4528 * @box_status is return value of box status.
4529 * @return:
4530 * IE_SUCCESS if box has been found and its status retrieved
4531 * IE_NOEXIST if box is not known to ISDS server
4532 * or other appropriate error.
4533 * You can use isds_DbState to enumerate box status. However out of enum
4534 * range value can be returned too. This is feature because ISDS
4535 * specification leaves the set of values open.
4536 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
4537 * the box has been deleted, but ISDS still lists its former existence. */
4538 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
4539 long int *box_status) {
4540 isds_error err = IE_SUCCESS;
4541 xmlNsPtr isds_ns = NULL;
4542 xmlNodePtr request = NULL, db_id;
4543 xmlDocPtr response = NULL;
4544 xmlChar *code = NULL, *message = NULL;
4545 xmlXPathContextPtr xpath_ctx = NULL;
4546 xmlXPathObjectPtr result = NULL;
4547 xmlChar *string = NULL;
4549 if (!context) return IE_INVALID_CONTEXT;
4550 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
4552 /* Check if connection is established
4553 * TODO: This check should be done donwstairs. */
4554 if (!context->curl) return IE_CONNECTION_CLOSED;
4557 /* Build CheckDataBox request */
4558 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
4559 if (!request) {
4560 isds_log_message(context,
4561 _("Could build CheckDataBox request"));
4562 return IE_ERROR;
4564 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4565 if(!isds_ns) {
4566 isds_log_message(context, _("Could not create ISDS name space"));
4567 xmlFreeNode(request);
4568 return IE_ERROR;
4570 xmlSetNs(request, isds_ns);
4571 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
4572 if (!db_id) {
4573 isds_log_message(context, _("Could not add dbId Child to "
4574 "CheckDataBox element"));
4575 xmlFreeNode(request);
4576 return IE_ERROR;
4580 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CheckDataBox request to ISDS\n"));
4582 /* Sent request */
4583 err = isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
4585 /* Destroy request */
4586 xmlFreeNode(request);
4588 if (err) {
4589 isds_log(ILF_ISDS, ILL_DEBUG,
4590 _("Processing ISDS response on CheckDataBox "
4591 "request failed\n"));
4592 goto leave;
4595 /* Check for response status */
4596 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
4597 &code, &message, NULL);
4598 if (err) {
4599 isds_log(ILF_ISDS, ILL_DEBUG,
4600 _("ISDS response on CheckDataBox request is missing status\n"));
4601 goto leave;
4604 /* Request processed, but nothing found */
4605 if (!xmlStrcmp(code, BAD_CAST "5001")) {
4606 char *box_id_locale = utf82locale((char*)box_id);
4607 char *code_locale = utf82locale((char*)code);
4608 char *message_locale = utf82locale((char*)message);
4609 isds_log(ILF_ISDS, ILL_DEBUG,
4610 _("Server did not found box %s on CheckDataBox request "
4611 "(code=%s, message=%s)\n"),
4612 box_id_locale, code_locale, message_locale);
4613 isds_log_message(context, message_locale);
4614 free(box_id_locale);
4615 free(code_locale);
4616 free(message_locale);
4617 err = IE_NOEXIST;
4618 goto leave;
4621 /* Other error */
4622 else if (xmlStrcmp(code, BAD_CAST "0000")) {
4623 char *code_locale = utf82locale((char*)code);
4624 char *message_locale = utf82locale((char*)message);
4625 isds_log(ILF_ISDS, ILL_DEBUG,
4626 _("Server refused CheckDataBox request "
4627 "(code=%s, message=%s)\n"), code_locale, message_locale);
4628 isds_log_message(context, message_locale);
4629 free(code_locale);
4630 free(message_locale);
4631 err = IE_ISDS;
4632 goto leave;
4635 /* Extract data */
4636 xpath_ctx = xmlXPathNewContext(response);
4637 if (!xpath_ctx) {
4638 err = IE_ERROR;
4639 goto leave;
4641 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4642 err = IE_ERROR;
4643 goto leave;
4645 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
4646 xpath_ctx);
4647 if (!result) {
4648 err = IE_ERROR;
4649 goto leave;
4651 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4652 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
4653 err = IE_ISDS;
4654 goto leave;
4656 if (result->nodesetval->nodeNr > 1) {
4657 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
4658 err = IE_ISDS;
4659 goto leave;
4661 xpath_ctx->node = result->nodesetval->nodeTab[0];
4662 xmlXPathFreeObject(result); result = NULL;
4664 EXTRACT_LONGINT("isds:dbState", box_status, 1);
4667 leave:
4668 free(string);
4669 xmlXPathFreeObject(result);
4670 xmlXPathFreeContext(xpath_ctx);
4672 free(code);
4673 free(message);
4674 xmlFreeDoc(response);
4676 if (!err)
4677 isds_log(ILF_ISDS, ILL_DEBUG,
4678 _("CheckDataBox request processed by server successfully.\n"));
4680 return err;
4684 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
4685 * code, destroy response and log success.
4686 * @context is ISDS session context.
4687 * @service_name is name of SERVICE_DB_MANIPULATION service
4688 * @box_id is UTF-8 encoded box identifier as zero terminated string
4689 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4690 * NULL, if you don't care. */
4691 static isds_error build_send_manipulationdbid_request_check_drop_response(
4692 struct isds_ctx *context, const xmlChar *service_name,
4693 const xmlChar *box_id, xmlChar **refnumber) {
4694 isds_error err = IE_SUCCESS;
4695 xmlDocPtr response = NULL;
4696 xmlChar *code = NULL, *message = NULL;
4698 if (!context) return IE_INVALID_CONTEXT;
4699 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
4701 /* Check if connection is established */
4702 if (!context->curl) return IE_CONNECTION_CLOSED;
4704 /* Do request and check for success */
4705 err = build_send_dbid_request_check_response(context,
4706 SERVICE_DB_MANIPULATION, service_name, box_id,
4707 &response, NULL, NULL, &code, &message, refnumber);
4708 free(code);
4709 free(message);
4710 xmlFreeDoc(response);
4712 if (!err) {
4713 char *service_name_locale = utf82locale((char *) service_name);
4714 isds_log(ILF_ISDS, ILL_DEBUG,
4715 _("%s request processed by server successfully.\n"),
4716 service_name_locale);
4717 free(service_name_locale);
4720 return err;
4724 /* Switch box into state where box can receive commercial messages (off by
4725 * default)
4726 * @context is ISDS session context.
4727 * @box_id is UTF-8 encoded box identifier as zero terminated string
4728 * @allow is true for enable, false for disable commercial messages income
4729 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4730 * NULL, if you don't care. */
4731 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
4732 const char *box_id, const _Bool allow, char **refnumber) {
4733 return build_send_manipulationdbid_request_check_drop_response(context,
4734 (allow) ? BAD_CAST "SetOpenAddressing" :
4735 BAD_CAST "ClearOpenAddressing",
4736 BAD_CAST box_id, (xmlChar **) refnumber);
4740 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
4741 * message acceptance). This is just a box permission. Sender must apply
4742 * such role by sending each message.
4743 * @context is ISDS session context.
4744 * @box_id is UTF-8 encoded box identifier as zero terminated string
4745 * @allow is true for enable, false for disable OVM role permission
4746 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4747 * NULL, if you don't care. */
4748 isds_error isds_switch_effective_ovm(struct isds_ctx *context,
4749 const char *box_id, const _Bool allow, char **refnumber) {
4750 return build_send_manipulationdbid_request_check_drop_response(context,
4751 (allow) ? BAD_CAST "SetEffectiveOVM" :
4752 BAD_CAST "ClearEffectiveOVM",
4753 BAD_CAST box_id, (xmlChar **) refnumber);
4757 /* Generic bottom half with request sending.
4758 * It sends prepared request, checks for error code, destroys response and
4759 * request and log success or failure.
4760 * @context is ISDS session context.
4761 * @service is ISDS service handler
4762 * @service_name is name in scope of given @service
4763 * @request is XML tree with request. Will be freed to save memory.
4764 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4765 * NULL, if you don't care. */
4766 static isds_error send_request_check_drop_response(
4767 struct isds_ctx *context,
4768 const isds_service service, const xmlChar *service_name,
4769 xmlNodePtr *request, xmlChar **refnumber) {
4770 isds_error err = IE_SUCCESS;
4771 char *service_name_locale = NULL;
4772 xmlDocPtr response = NULL;
4773 xmlChar *code = NULL, *message = NULL;
4776 if (!context) return IE_INVALID_CONTEXT;
4777 if (!service_name || *service_name == '\0' || !request || !*request)
4778 return IE_INVAL;
4780 /* Check if connection is established
4781 * TODO: This check should be done donwstairs. */
4782 if (!context->curl) return IE_CONNECTION_CLOSED;
4784 service_name_locale = utf82locale((char*) service_name);
4785 if (!service_name_locale) {
4786 err = IE_NOMEM;
4787 goto leave;
4790 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
4791 service_name_locale);
4793 /* Send request */
4794 err = isds(context, service, *request, &response, NULL, NULL);
4795 xmlFreeNode(*request); *request = NULL;
4797 if (err) {
4798 isds_log(ILF_ISDS, ILL_DEBUG,
4799 _("Processing ISDS response on %s request failed\n"),
4800 service_name_locale);
4801 goto leave;
4804 /* Check for response status */
4805 err = isds_response_status(context, service, response,
4806 &code, &message, refnumber);
4807 if (err) {
4808 isds_log(ILF_ISDS, ILL_DEBUG,
4809 _("ISDS response on %s request is missing status\n"),
4810 service_name_locale);
4811 goto leave;
4814 /* Request processed, but server failed */
4815 if (xmlStrcmp(code, BAD_CAST "0000")) {
4816 char *code_locale = utf82locale((char*) code);
4817 char *message_locale = utf82locale((char*) message);
4818 isds_log(ILF_ISDS, ILL_DEBUG,
4819 _("Server refused %s request (code=%s, message=%s)\n"),
4820 service_name_locale, code_locale, message_locale);
4821 isds_log_message(context, message_locale);
4822 free(code_locale);
4823 free(message_locale);
4824 err = IE_ISDS;
4825 goto leave;
4829 leave:
4830 free(code);
4831 free(message);
4832 xmlFreeDoc(response);
4833 if (*request) {
4834 xmlFreeNode(*request);
4835 *request = NULL;
4838 if (!err) {
4839 isds_log(ILF_ISDS, ILL_DEBUG,
4840 _("%s request processed by server successfully.\n"),
4841 service_name_locale);
4844 free(service_name_locale);
4846 return err;
4850 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
4851 * code, destroy response and log success.
4852 * @context is ISDS session context.
4853 * @service_name is name of SERVICE_DB_MANIPULATION service
4854 * @owner is structure describing box
4855 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4856 * NULL, if you don't care. */
4857 static isds_error build_send_manipulationdbowner_request_check_drop_response(
4858 struct isds_ctx *context, const xmlChar *service_name,
4859 const struct isds_DbOwnerInfo *owner, xmlChar **refnumber) {
4860 isds_error err = IE_SUCCESS;
4861 char *service_name_locale = NULL;
4862 xmlNodePtr request = NULL, db_owner_info;
4863 xmlNsPtr isds_ns = NULL;
4866 if (!context) return IE_INVALID_CONTEXT;
4867 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
4869 service_name_locale = utf82locale((char*)service_name);
4870 if (!service_name_locale) {
4871 err = IE_NOMEM;
4872 goto leave;
4875 /* Build request */
4876 request = xmlNewNode(NULL, service_name);
4877 if (!request) {
4878 isds_printf_message(context,
4879 _("Could not build %s request"), service_name_locale);
4880 err = IE_ERROR;
4881 goto leave;
4883 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4884 if(!isds_ns) {
4885 isds_log_message(context, _("Could not create ISDS name space"));
4886 err = IE_ERROR;
4887 goto leave;
4889 xmlSetNs(request, isds_ns);
4892 /* Add XSD:tOwnerInfoInput child*/
4893 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
4894 err = insert_DbOwnerInfo(context, owner, db_owner_info);
4895 if (err) goto leave;
4896 /* TODO: XSD:gExtApproval*/
4898 /* Send it to server and process response */
4899 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
4900 service_name, &request, refnumber);
4902 leave:
4903 xmlFreeNode(request);
4904 free(service_name_locale);
4906 return err;
4910 /* Switch box accessibility state on request of box owner.
4911 * Despite the name, owner must do the requst off-line. This function is
4912 * designed for such off-line meeting points (e.g. Czech POINT).
4913 * @context is ISDS session context.
4914 * @box identifies box to swith accesibilty state.
4915 * @allow is true for making accesibale, false to disallow access.
4916 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4917 * NULL, if you don't care. */
4918 isds_error isds_switch_box_accessibility_on_owner_request(
4919 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
4920 const _Bool allow, char **refnumber) {
4921 return build_send_manipulationdbowner_request_check_drop_response(context,
4922 (allow) ? BAD_CAST "EnableOwnDataBox" :
4923 BAD_CAST "DisableOwnDataBox",
4924 box, (xmlChar **) refnumber);
4928 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
4929 * date.
4930 * @context is ISDS session context.
4931 * @box identifies box to swith accesibilty state.
4932 * @since is date since accesseibility has been denied. This can be past too.
4933 * Only tm_year, tm_mon and tm_mday carry sane value.
4934 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4935 * NULL, if you don't care. */
4936 isds_error isds_disable_box_accessibility_externaly(
4937 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
4938 const struct tm *since, char **refnumber) {
4939 isds_error err = IE_SUCCESS;
4940 char *service_name_locale = NULL;
4941 xmlNodePtr request = NULL, node;
4942 xmlNsPtr isds_ns = NULL;
4943 xmlChar *string = NULL;
4946 if (!context) return IE_INVALID_CONTEXT;
4947 if (!box || !since) return IE_INVAL;
4949 /* Build request */
4950 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
4951 if (!request) {
4952 isds_printf_message(context,
4953 _("Could not build %s request"), "DisableDataBoxExternally");
4954 err = IE_ERROR;
4955 goto leave;
4957 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4958 if(!isds_ns) {
4959 isds_log_message(context, _("Could not create ISDS name space"));
4960 err = IE_ERROR;
4961 goto leave;
4963 xmlSetNs(request, isds_ns);
4966 /* Add @box identification */
4967 INSERT_ELEMENT(node, request, "dbOwnerInfo");
4968 err = insert_DbOwnerInfo(context, box, node);
4969 if (err) goto leave;
4971 /* Add @since date */
4972 err = tm2datestring(since, &string);
4973 if(err) {
4974 isds_log_message(context,
4975 _("Could not convert `since' argument to ISO date string"));
4976 goto leave;
4978 INSERT_STRING(request, "dbOwnerDisableDate", string);
4979 zfree(string);
4981 /* TODO: XSD:gExtApproval*/
4983 /* Send it to server and process response */
4984 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
4985 BAD_CAST "DisableDataBoxExternally", &request,
4986 (xmlChar **) refnumber);
4988 leave:
4989 free(string);
4990 xmlFreeNode(request);
4991 free(service_name_locale);
4993 return err;
4997 /* Insert struct isds_message data (envelope (recipient data optional) and
4998 * documents) into XML tree
4999 * @context is sesstion context
5000 * @outgoing_message is libsids structure with message data
5001 * @create_message is XML CreateMessage or CreateMultipleMessage element
5002 * @process_recipient true for recipient data serialization, false for no
5003 * serialization */
5004 static isds_error insert_envelope_files(struct isds_ctx *context,
5005 const struct isds_message *outgoing_message, xmlNodePtr create_message,
5006 const _Bool process_recipient) {
5008 isds_error err = IE_SUCCESS;
5009 xmlNodePtr envelope, dm_files, node;
5010 xmlChar *string = NULL;
5012 if (!context) return IE_INVALID_CONTEXT;
5013 if (!outgoing_message || !create_message) return IE_INVAL;
5016 /* Build envelope */
5017 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
5018 if (!envelope) {
5019 isds_printf_message(context, _("Could not add dmEnvelope child to "
5020 "%s element"), create_message->name);
5021 return IE_ERROR;
5024 if (!outgoing_message->envelope) {
5025 isds_log_message(context, _("Outgoing message is missing envelope"));
5026 err = IE_INVAL;
5027 goto leave;
5030 INSERT_STRING(envelope, "dmSenderOrgUnit",
5031 outgoing_message->envelope->dmSenderOrgUnit);
5032 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
5033 outgoing_message->envelope->dmSenderOrgUnitNum, string);
5035 if (process_recipient) {
5036 if (!outgoing_message->envelope->dbIDRecipient) {
5037 isds_log_message(context,
5038 _("Outgoing message is missing recipient box identifier"));
5039 err = IE_INVAL;
5040 goto leave;
5042 INSERT_STRING(envelope, "dbIDRecipient",
5043 outgoing_message->envelope->dbIDRecipient);
5045 INSERT_STRING(envelope, "dmRecipientOrgUnit",
5046 outgoing_message->envelope->dmRecipientOrgUnit);
5047 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
5048 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
5049 INSERT_STRING(envelope, "dmToHands",
5050 outgoing_message->envelope->dmToHands);
5053 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
5054 "dmAnnotation");
5055 INSERT_STRING(envelope, "dmAnnotation",
5056 outgoing_message->envelope->dmAnnotation);
5058 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
5059 0, 50, "dmRecipientRefNumber");
5060 INSERT_STRING(envelope, "dmRecipientRefNumber",
5061 outgoing_message->envelope->dmRecipientRefNumber);
5063 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
5064 0, 50, "dmSenderRefNumber");
5065 INSERT_STRING(envelope, "dmSenderRefNumber",
5066 outgoing_message->envelope->dmSenderRefNumber);
5068 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
5069 0, 50, "dmRecipientIdent");
5070 INSERT_STRING(envelope, "dmRecipientIdent",
5071 outgoing_message->envelope->dmRecipientIdent);
5073 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
5074 0, 50, "dmSenderIdent");
5075 INSERT_STRING(envelope, "dmSenderIdent",
5076 outgoing_message->envelope->dmSenderIdent);
5078 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
5079 outgoing_message->envelope->dmLegalTitleLaw, string);
5080 INSERT_LONGINT(envelope, "dmLegalTitleYear",
5081 outgoing_message->envelope->dmLegalTitleYear, string);
5082 INSERT_STRING(envelope, "dmLegalTitleSect",
5083 outgoing_message->envelope->dmLegalTitleSect);
5084 INSERT_STRING(envelope, "dmLegalTitlePar",
5085 outgoing_message->envelope->dmLegalTitlePar);
5086 INSERT_STRING(envelope, "dmLegalTitlePoint",
5087 outgoing_message->envelope->dmLegalTitlePoint);
5089 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
5090 outgoing_message->envelope->dmPersonalDelivery);
5091 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
5092 outgoing_message->envelope->dmAllowSubstDelivery);
5094 /* ???: Should we require value for dbEffectiveOVM sender?
5095 * ISDS has default as true */
5096 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
5099 /* Append dmFiles */
5100 if (!outgoing_message->documents) {
5101 isds_log_message(context,
5102 _("Outgoing message is missing list of documents"));
5103 err = IE_INVAL;
5104 goto leave;
5106 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
5107 if (!dm_files) {
5108 isds_printf_message(context, _("Could not add dmFiles child to "
5109 "%s element"), create_message->name);
5110 err = IE_ERROR;
5111 goto leave;
5114 /* Check for document hieararchy */
5115 err = check_documents_hierarchy(context, outgoing_message->documents);
5116 if (err) goto leave;
5118 /* Process each document */
5119 for (struct isds_list *item =
5120 (struct isds_list *) outgoing_message->documents;
5121 item; item = item->next) {
5122 if (!item->data) {
5123 isds_log_message(context,
5124 _("List of documents contains empty item"));
5125 err = IE_INVAL;
5126 goto leave;
5128 /* FIXME: Check for dmFileMetaType and for document references.
5129 * Only first document can be of MAIN type */
5130 err = insert_document(context, (struct isds_document*) item->data,
5131 dm_files);
5133 if (err) goto leave;
5136 leave:
5137 free(string);
5138 return err;
5142 /* Send a message via ISDS to a recipent
5143 * @context is session context
5144 * @outgoing_message is message to send; Some memebers are mandatory (like
5145 * dbIDRecipient), some are optional and some are irrelevant (especialy data
5146 * about sender). Included pointer to isds_list documents must contain at
5147 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
5148 * members will be filled with valid data from ISDS. Exact list of write
5149 * members is subject to change. Currently dmId is changed.
5150 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
5151 isds_error isds_send_message(struct isds_ctx *context,
5152 struct isds_message *outgoing_message) {
5154 isds_error err = IE_SUCCESS;
5155 xmlNsPtr isds_ns = NULL;
5156 xmlNodePtr request = NULL;
5157 xmlDocPtr response = NULL;
5158 xmlChar *code = NULL, *message = NULL;
5159 xmlXPathContextPtr xpath_ctx = NULL;
5160 xmlXPathObjectPtr result = NULL;
5161 _Bool message_is_complete = 0;
5163 if (!context) return IE_INVALID_CONTEXT;
5164 if (!outgoing_message) return IE_INVAL;
5166 /* Check if connection is established
5167 * TODO: This check should be done donwstairs. */
5168 if (!context->curl) return IE_CONNECTION_CLOSED;
5171 /* Build CreateMessage request */
5172 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
5173 if (!request) {
5174 isds_log_message(context,
5175 _("Could build CreateMessage request"));
5176 return IE_ERROR;
5178 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5179 if(!isds_ns) {
5180 isds_log_message(context, _("Could not create ISDS name space"));
5181 xmlFreeNode(request);
5182 return IE_ERROR;
5184 xmlSetNs(request, isds_ns);
5186 /* Append envelope and files */
5187 err = insert_envelope_files(context, outgoing_message, request, 1);
5188 if (err) goto leave;
5191 /* Signal we can serilize message since now */
5192 message_is_complete = 1;
5195 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
5197 /* Sent request */
5198 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
5200 /* Dont' destroy request, we want to provide it to application later */
5202 if (err) {
5203 isds_log(ILF_ISDS, ILL_DEBUG,
5204 _("Processing ISDS response on CreateMessage "
5205 "request failed\n"));
5206 goto leave;
5209 /* Check for response status */
5210 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
5211 &code, &message, NULL);
5212 if (err) {
5213 isds_log(ILF_ISDS, ILL_DEBUG,
5214 _("ISDS response on CreateMessage request "
5215 "is missing status\n"));
5216 goto leave;
5219 /* Request processed, but refused by server or server failed */
5220 if (xmlStrcmp(code, BAD_CAST "0000")) {
5221 char *box_id_locale =
5222 utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
5223 char *code_locale = utf82locale((char*)code);
5224 char *message_locale = utf82locale((char*)message);
5225 isds_log(ILF_ISDS, ILL_DEBUG,
5226 _("Server did not accept message for %s on CreateMessage "
5227 "request (code=%s, message=%s)\n"),
5228 box_id_locale, code_locale, message_locale);
5229 isds_log_message(context, message_locale);
5230 free(box_id_locale);
5231 free(code_locale);
5232 free(message_locale);
5233 err = IE_ISDS;
5234 goto leave;
5238 /* Extract data */
5239 xpath_ctx = xmlXPathNewContext(response);
5240 if (!xpath_ctx) {
5241 err = IE_ERROR;
5242 goto leave;
5244 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5245 err = IE_ERROR;
5246 goto leave;
5248 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
5249 xpath_ctx);
5250 if (!result) {
5251 err = IE_ERROR;
5252 goto leave;
5254 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5255 isds_log_message(context, _("Missing CreateMessageResponse element"));
5256 err = IE_ISDS;
5257 goto leave;
5259 if (result->nodesetval->nodeNr > 1) {
5260 isds_log_message(context, _("Multiple CreateMessageResponse element"));
5261 err = IE_ISDS;
5262 goto leave;
5264 xpath_ctx->node = result->nodesetval->nodeTab[0];
5265 xmlXPathFreeObject(result); result = NULL;
5267 if (outgoing_message->envelope->dmID) {
5268 free(outgoing_message->envelope->dmID);
5269 outgoing_message->envelope->dmID = NULL;
5271 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
5272 if (!outgoing_message->envelope->dmID) {
5273 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
5274 "but did not returen assigned message ID\n"));
5277 leave:
5278 /* TODO: Serialize message into structure member raw */
5279 /* XXX: Each web service transport message in different format.
5280 * Therefore it's not possible to save them directly.
5281 * To save them, one must figure out common format.
5282 * We can leave it on application, or we can implement the ESS format. */
5283 /*if (message_is_complete) {
5284 if (outgoing_message->envelope->dmID) {
5286 /* Add assigned message ID as first child*/
5287 /*xmlNodePtr dmid_text = xmlNewText(
5288 (xmlChar *) outgoing_message->envelope->dmID);
5289 if (!dmid_text) goto serialization_failed;
5291 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
5292 BAD_CAST "dmID");
5293 if (!dmid_element) {
5294 xmlFreeNode(dmid_text);
5295 goto serialization_failed;
5298 xmlNodePtr dmid_element_with_text =
5299 xmlAddChild(dmid_element, dmid_text);
5300 if (!dmid_element_with_text) {
5301 xmlFreeNode(dmid_element);
5302 xmlFreeNode(dmid_text);
5303 goto serialization_failed;
5306 node = xmlAddPrevSibling(envelope->childern,
5307 dmid_element_with_text);
5308 if (!node) {
5309 xmlFreeNodeList(dmid_element_with_text);
5310 goto serialization_failed;
5314 /* Serialize message with ID into raw */
5315 /*buffer = serialize_element(envelope)*/
5316 /* }
5318 serialization_failed:
5322 /* Clean up */
5323 xmlXPathFreeObject(result);
5324 xmlXPathFreeContext(xpath_ctx);
5326 free(code);
5327 free(message);
5328 xmlFreeDoc(response);
5329 xmlFreeNode(request);
5331 if (!err)
5332 isds_log(ILF_ISDS, ILL_DEBUG,
5333 _("CreateMessage request processed by server "
5334 "successfully.\n"));
5336 return err;
5340 /* Send a message via ISDS to a multiple recipents
5341 * @context is session context
5342 * @outgoing_message is message to send; Some memebers are mandatory,
5343 * some are optional and some are irrelevant (especialy data
5344 * about sender). Data about recipient will be substituted by ISDS from
5345 * @copies. Included pointer to isds_list documents must
5346 * contain at least one document of FILEMETATYPE_MAIN.
5347 * @copies is list of isds_message_copy structures addressing all desired
5348 * recipients. This is read-write structure, some members will be filled with
5349 * valid data from ISDS (message IDs, error codes, error descriptions).
5350 * @return
5351 * ISDS_SUCCESS if all messages have been sent
5352 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
5353 * succesed messages can be identified by copies->data->error),
5354 * or other error code if something other goes wrong. */
5355 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
5356 const struct isds_message *outgoing_message,
5357 struct isds_list *copies) {
5359 isds_error err = IE_SUCCESS, append_err;
5360 xmlNsPtr isds_ns = NULL;
5361 xmlNodePtr request = NULL, recipients, recipient, node;
5362 struct isds_list *item;
5363 struct isds_message_copy *copy;
5364 xmlDocPtr response = NULL;
5365 xmlChar *code = NULL, *message = NULL;
5366 xmlXPathContextPtr xpath_ctx = NULL;
5367 xmlXPathObjectPtr result = NULL;
5368 xmlChar *string = NULL;
5369 int i;
5371 if (!context) return IE_INVALID_CONTEXT;
5372 if (!outgoing_message || !copies) return IE_INVAL;
5374 /* Check if connection is established
5375 * TODO: This check should be done donwstairs. */
5376 if (!context->curl) return IE_CONNECTION_CLOSED;
5379 /* Build CreateMultipleMessage request */
5380 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
5381 if (!request) {
5382 isds_log_message(context,
5383 _("Could build CreateMultipleMessage request"));
5384 return IE_ERROR;
5386 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5387 if(!isds_ns) {
5388 isds_log_message(context, _("Could not create ISDS name space"));
5389 xmlFreeNode(request);
5390 return IE_ERROR;
5392 xmlSetNs(request, isds_ns);
5395 /* Build recipients */
5396 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
5397 if (!recipients) {
5398 isds_log_message(context, _("Could not add dmRecipients child to "
5399 "CreateMultipleMessage element"));
5400 xmlFreeNode(request);
5401 return IE_ERROR;
5404 /* Insert each recipient */
5405 for (item = copies; item; item = item->next) {
5406 copy = (struct isds_message_copy *) item->data;
5407 if (!copy) {
5408 isds_log_message(context,
5409 _("copies list item contains empty data"));
5410 err = IE_INVAL;
5411 goto leave;
5414 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
5415 if (!recipient) {
5416 isds_log_message(context, _("Could not add dmRecipient child to "
5417 "dmRecipient element"));
5418 err = IE_ERROR;
5419 goto leave;
5422 if (!copy->dbIDRecipient) {
5423 isds_log_message(context,
5424 _("Message copy is missing recipient box identifier"));
5425 err = IE_INVAL;
5426 goto leave;
5428 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
5429 INSERT_STRING(recipient, "dmRecipientOrgUnit",
5430 copy->dmRecipientOrgUnit);
5431 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
5432 copy->dmRecipientOrgUnitNum, string);
5433 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
5436 /* Append envelope and files */
5437 err = insert_envelope_files(context, outgoing_message, request, 0);
5438 if (err) goto leave;
5441 isds_log(ILF_ISDS, ILL_DEBUG,
5442 _("Sending CreateMultipleMessage request to ISDS\n"));
5444 /* Sent request */
5445 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
5446 if (err) {
5447 isds_log(ILF_ISDS, ILL_DEBUG,
5448 _("Processing ISDS response on CreateMultipleMessage "
5449 "request failed\n"));
5450 goto leave;
5453 /* Check for response status */
5454 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
5455 &code, &message, NULL);
5456 if (err) {
5457 isds_log(ILF_ISDS, ILL_DEBUG,
5458 _("ISDS response on CreateMultipleMessage request "
5459 "is missing status\n"));
5460 goto leave;
5463 /* Request processed, but some copies failed */
5464 if (!xmlStrcmp(code, BAD_CAST "0004")) {
5465 char *box_id_locale =
5466 utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
5467 char *code_locale = utf82locale((char*)code);
5468 char *message_locale = utf82locale((char*)message);
5469 isds_log(ILF_ISDS, ILL_DEBUG,
5470 _("Server did accept message for multiple recipients "
5471 "on CreateMultipleMessage request but delivery to "
5472 "some of them failed (code=%s, message=%s)\n"),
5473 box_id_locale, code_locale, message_locale);
5474 isds_log_message(context, message_locale);
5475 free(box_id_locale);
5476 free(code_locale);
5477 free(message_locale);
5478 err = IE_PARTIAL_SUCCESS;
5481 /* Request refused by server as whole */
5482 else if (xmlStrcmp(code, BAD_CAST "0000")) {
5483 char *box_id_locale =
5484 utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
5485 char *code_locale = utf82locale((char*)code);
5486 char *message_locale = utf82locale((char*)message);
5487 isds_log(ILF_ISDS, ILL_DEBUG,
5488 _("Server did not accept message for multiple recipients "
5489 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
5490 box_id_locale, code_locale, message_locale);
5491 isds_log_message(context, message_locale);
5492 free(box_id_locale);
5493 free(code_locale);
5494 free(message_locale);
5495 err = IE_ISDS;
5496 goto leave;
5500 /* Extract data */
5501 xpath_ctx = xmlXPathNewContext(response);
5502 if (!xpath_ctx) {
5503 err = IE_ERROR;
5504 goto leave;
5506 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5507 err = IE_ERROR;
5508 goto leave;
5510 result = xmlXPathEvalExpression(
5511 BAD_CAST "/isds:CreateMultipleMessageResponse"
5512 "/isds:dmMultipleStatus/isds:dmSingleStatus",
5513 xpath_ctx);
5514 if (!result) {
5515 err = IE_ERROR;
5516 goto leave;
5518 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5519 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
5520 err = IE_ISDS;
5521 goto leave;
5524 /* Extract message ID and delivery status for each copy */
5525 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
5526 item = item->next, i++) {
5527 copy = (struct isds_message_copy *) item->data;
5528 xpath_ctx->node = result->nodesetval->nodeTab[i];
5530 append_err = append_TMStatus(context, copy, xpath_ctx);
5531 if (append_err) {
5532 err = append_err;
5533 goto leave;
5536 if (item || i < result->nodesetval->nodeNr) {
5537 isds_printf_message(context, _("ISDS returned unexpected number of "
5538 "message copy delivery states: %d"),
5539 result->nodesetval->nodeNr);
5540 err = IE_ISDS;
5541 goto leave;
5545 leave:
5546 /* Clean up */
5547 free(string);
5548 xmlXPathFreeObject(result);
5549 xmlXPathFreeContext(xpath_ctx);
5551 free(code);
5552 free(message);
5553 xmlFreeDoc(response);
5554 xmlFreeNode(request);
5556 if (!err)
5557 isds_log(ILF_ISDS, ILL_DEBUG,
5558 _("CreateMultipleMessageResponse request processed by server "
5559 "successfully.\n"));
5561 return err;
5565 /* Get list of messages. This is common core for getting sent or received
5566 * messaeges.
5567 * Any criterion argument can be NULL, if you don't care about it.
5568 * @context is session context. Must not be NULL.
5569 * @outgoing_direction is true if you want list of outgoing messages,
5570 * it's false if you want incoming messages.
5571 * @from_time is minimal time and date of message sending inclusive.
5572 * @to_time is maximal time and date of message sending inclusive
5573 * @organization_unit_number is number of sender/recipient respectively.
5574 * @status_filter is bit field of isds_message_status values. Use special
5575 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
5576 * all values, you can use bitwise arithmetic if you want.)
5577 * @offset is index of first message we are interested in. First message is 1.
5578 * Set to 0 (or 1) if you don't care.
5579 * @number is maximal length of list you want to get as input value, outputs
5580 * number of messages matching these criteria. Can be NULL if you don't care
5581 * (applies to output value either).
5582 * @messages is automatically reallocated list of isds_message's. Be ware that
5583 * it returns only brief overview (envelope and some other fields) about each
5584 * message, not the complete message. FIXME: Specify exact fields.
5585 * The list is sorted by delivery time in ascending order.
5586 * Use NULL if
5587 * you don't care about don't need the data (useful if you want to know only
5588 * the @number). If you provide &NULL, list will be allocated on heap, if you
5589 * provide pointer to non-NULL, list will be freed automacally at first. Also
5590 * in case of error the list will be NULLed.
5591 * @return IE_SUCCESS or appropriate error code. */
5592 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
5593 _Bool outgoing_direction,
5594 const struct timeval *from_time, const struct timeval *to_time,
5595 const long int *organization_unit_number,
5596 const unsigned int status_filter,
5597 const unsigned long int offset, unsigned long int *number,
5598 struct isds_list **messages) {
5600 isds_error err = IE_SUCCESS;
5601 xmlNsPtr isds_ns = NULL;
5602 xmlNodePtr request = NULL, node;
5603 xmlDocPtr response = NULL;
5604 xmlChar *code = NULL, *message = NULL;
5605 xmlXPathContextPtr xpath_ctx = NULL;
5606 xmlXPathObjectPtr result = NULL;
5607 xmlChar *string = NULL;
5608 long unsigned int count = 0;
5610 if (!context) return IE_INVALID_CONTEXT;
5612 /* Free former message list if any */
5613 if (messages) isds_list_free(messages);
5615 /* Check if connection is established
5616 * TODO: This check should be done donwstairs. */
5617 if (!context->curl) return IE_CONNECTION_CLOSED;
5619 /* Build GetListOf*Messages request */
5620 request = xmlNewNode(NULL,
5621 (outgoing_direction) ?
5622 BAD_CAST "GetListOfSentMessages" :
5623 BAD_CAST "GetListOfReceivedMessages"
5625 if (!request) {
5626 isds_log_message(context,
5627 (outgoing_direction) ?
5628 _("Could not build GetListOfSentMessages request") :
5629 _("Could not build GetListOfReceivedMessages request")
5631 return IE_ERROR;
5633 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5634 if(!isds_ns) {
5635 isds_log_message(context, _("Could not create ISDS name space"));
5636 xmlFreeNode(request);
5637 return IE_ERROR;
5639 xmlSetNs(request, isds_ns);
5642 if (from_time) {
5643 err = timeval2timestring(from_time, &string);
5644 if (err) goto leave;
5646 INSERT_STRING(request, "dmFromTime", string);
5647 free(string); string = NULL;
5649 if (to_time) {
5650 err = timeval2timestring(to_time, &string);
5651 if (err) goto leave;
5653 INSERT_STRING(request, "dmToTime", string);
5654 free(string); string = NULL;
5656 if (outgoing_direction) {
5657 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
5658 organization_unit_number, string);
5659 } else {
5660 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
5661 organization_unit_number, string);
5664 if (status_filter > MESSAGESTATE_ANY) {
5665 isds_printf_message(context,
5666 _("Invalid message state filter value: %ld"), status_filter);
5667 err = IE_INVAL;
5668 goto leave;
5670 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
5672 if (offset > 0 ) {
5673 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
5674 } else {
5675 INSERT_STRING(request, "dmOffset", "1");
5678 /* number 0 means no limit */
5679 if (number && *number == 0) {
5680 INSERT_STRING(request, "dmLimit", NULL);
5681 } else {
5682 INSERT_ULONGINT(request, "dmLimit", number, string);
5686 isds_log(ILF_ISDS, ILL_DEBUG,
5687 (outgoing_direction) ?
5688 _("Sending GetListOfSentMessages request to ISDS\n") :
5689 _("Sending GetListOfReceivedMessages request to ISDS\n")
5692 /* Sent request */
5693 err = isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
5694 xmlFreeNode(request); request = NULL;
5696 if (err) {
5697 isds_log(ILF_ISDS, ILL_DEBUG,
5698 (outgoing_direction) ?
5699 _("Processing ISDS response on GetListOfSentMessages "
5700 "request failed\n") :
5701 _("Processing ISDS response on GetListOfReceivedMessages "
5702 "request failed\n")
5704 goto leave;
5707 /* Check for response status */
5708 err = isds_response_status(context, SERVICE_DM_INFO, response,
5709 &code, &message, NULL);
5710 if (err) {
5711 isds_log(ILF_ISDS, ILL_DEBUG,
5712 (outgoing_direction) ?
5713 _("ISDS response on GetListOfSentMessages request "
5714 "is missing status\n") :
5715 _("ISDS response on GetListOfReceivedMessages request "
5716 "is missing status\n")
5718 goto leave;
5721 /* Request processed, but nothing found */
5722 if (xmlStrcmp(code, BAD_CAST "0000")) {
5723 char *code_locale = utf82locale((char*)code);
5724 char *message_locale = utf82locale((char*)message);
5725 isds_log(ILF_ISDS, ILL_DEBUG,
5726 (outgoing_direction) ?
5727 _("Server refused GetListOfSentMessages request "
5728 "(code=%s, message=%s)\n") :
5729 _("Server refused GetListOfReceivedMessages request "
5730 "(code=%s, message=%s)\n"),
5731 code_locale, message_locale);
5732 isds_log_message(context, message_locale);
5733 free(code_locale);
5734 free(message_locale);
5735 err = IE_ISDS;
5736 goto leave;
5740 /* Extract data */
5741 xpath_ctx = xmlXPathNewContext(response);
5742 if (!xpath_ctx) {
5743 err = IE_ERROR;
5744 goto leave;
5746 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5747 err = IE_ERROR;
5748 goto leave;
5750 result = xmlXPathEvalExpression(
5751 (outgoing_direction) ?
5752 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
5753 "isds:dmRecords/isds:dmRecord" :
5754 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
5755 "isds:dmRecords/isds:dmRecord",
5756 xpath_ctx);
5757 if (!result) {
5758 err = IE_ERROR;
5759 goto leave;
5762 /* Fill output arguments in */
5763 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5764 struct isds_envelope *envelope;
5765 struct isds_list *item = NULL, *last_item = NULL;
5767 for (count = 0; count < result->nodesetval->nodeNr; count++) {
5768 /* Create new message */
5769 item = calloc(1, sizeof(*item));
5770 if (!item) {
5771 err = IE_NOMEM;
5772 goto leave;
5774 item->destructor = (void(*)(void**)) &isds_message_free;
5775 item->data = calloc(1, sizeof(struct isds_message));
5776 if (!item->data) {
5777 isds_list_free(&item);
5778 err = IE_NOMEM;
5779 goto leave;
5782 /* Extract envelope data */
5783 xpath_ctx->node = result->nodesetval->nodeTab[count];
5784 envelope = NULL;
5785 err = extract_DmRecord(context, &envelope, xpath_ctx);
5786 if (err) {
5787 isds_list_free(&item);
5788 goto leave;
5791 /* Attach extracted envelope */
5792 ((struct isds_message *) item->data)->envelope = envelope;
5794 /* Append new message into the list */
5795 if (!*messages) {
5796 *messages = last_item = item;
5797 } else {
5798 last_item->next = item;
5799 last_item = item;
5803 if (number) *number = count;
5805 leave:
5806 if (err) {
5807 isds_list_free(messages);
5810 free(string);
5811 xmlXPathFreeObject(result);
5812 xmlXPathFreeContext(xpath_ctx);
5814 free(code);
5815 free(message);
5816 xmlFreeDoc(response);
5817 xmlFreeNode(request);
5819 if (!err)
5820 isds_log(ILF_ISDS, ILL_DEBUG,
5821 (outgoing_direction) ?
5822 _("GetListOfSentMessages request processed by server "
5823 "successfully.\n") :
5824 _("GetListOfReceivedMessages request processed by server "
5825 "successfully.\n")
5827 return err;
5831 /* Get list of outgoing (already sent) messages.
5832 * Any criterion argument can be NULL, if you don't care about it.
5833 * @context is session context. Must not be NULL.
5834 * @from_time is minimal time and date of message sending inclusive.
5835 * @to_time is maximal time and date of message sending inclusive
5836 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
5837 * @status_filter is bit field of isds_message_status values. Use special
5838 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
5839 * all values, you can use bitwise arithmetic if you want.)
5840 * @offset is index of first message we are interested in. First message is 1.
5841 * Set to 0 (or 1) if you don't care.
5842 * @number is maximal length of list you want to get as input value, outputs
5843 * number of messages matching these criteria. Can be NULL if you don't care
5844 * (applies to output value either).
5845 * @messages is automatically reallocated list of isds_message's. Be ware that
5846 * it returns only brief overview (envelope and some other fields) about each
5847 * message, not the complete message. FIXME: Specify exact fields.
5848 * The list is sorted by delivery time in ascending order.
5849 * Use NULL if you don't care about the metadata (useful if you want to know
5850 * only the @number). If you provide &NULL, list will be allocated on heap,
5851 * if you provide pointer to non-NULL, list will be freed automacally at first.
5852 * Also in case of error the list will be NULLed.
5853 * @return IE_SUCCESS or appropriate error code. */
5854 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
5855 const struct timeval *from_time, const struct timeval *to_time,
5856 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
5857 const unsigned long int offset, unsigned long int *number,
5858 struct isds_list **messages) {
5860 return isds_get_list_of_messages(
5861 context, 1,
5862 from_time, to_time, dmSenderOrgUnitNum, status_filter,
5863 offset, number,
5864 messages);
5868 /* Get list of incoming (addressed to you) messages.
5869 * Any criterion argument can be NULL, if you don't care about it.
5870 * @context is session context. Must not be NULL.
5871 * @from_time is minimal time and date of message sending inclusive.
5872 * @to_time is maximal time and date of message sending inclusive
5873 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
5874 * @status_filter is bit field of isds_message_status values. Use special
5875 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
5876 * all values, you can use bitwise arithmetic if you want.)
5877 * @offset is index of first message we are interested in. First message is 1.
5878 * Set to 0 (or 1) if you don't care.
5879 * @number is maximal length of list you want to get as input value, outputs
5880 * number of messages matching these criteria. Can be NULL if you don't care
5881 * (applies to output value either).
5882 * @messages is automatically reallocated list of isds_message's. Be ware that
5883 * it returns only brief overview (envelope and some other fields) about each
5884 * message, not the complete message. FIXME: Specify exact fields.
5885 * Use NULL if you don't care about the metadata (useful if you want to know
5886 * only the @number). If you provide &NULL, list will be allocated on heap,
5887 * if you provide pointer to non-NULL, list will be freed automacally at first.
5888 * Also in case of error the list will be NULLed.
5889 * @return IE_SUCCESS or appropriate error code. */
5890 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
5891 const struct timeval *from_time, const struct timeval *to_time,
5892 const long int *dmRecipientOrgUnitNum,
5893 const unsigned int status_filter,
5894 const unsigned long int offset, unsigned long int *number,
5895 struct isds_list **messages) {
5897 return isds_get_list_of_messages(
5898 context, 0,
5899 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
5900 offset, number,
5901 messages);
5905 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
5906 * code
5907 * @context is session context
5908 * @service is ISDS WS service handler
5909 * @service_name is name of SERVICE_DM_OPERATIONS
5910 * @message_id is message ID to send as service argument to ISDS
5911 * @response is server SOAP body response as XML document
5912 * @raw_response is automatically reallocated bitstream with response body. Use
5913 * NULL if you don't care
5914 * @raw_response_length is size of @raw_response in bytes
5915 * @code is ISDS status code
5916 * @status_message is ISDS status message
5917 * @return error coded from lower layer, context message will be set up
5918 * appropriately. */
5919 static isds_error build_send_check_message_request(struct isds_ctx *context,
5920 const isds_service service, const xmlChar *service_name,
5921 const char *message_id,
5922 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
5923 xmlChar **code, xmlChar **status_message) {
5925 isds_error err = IE_SUCCESS;
5926 char *service_name_locale = NULL, *message_id_locale = NULL;
5927 xmlNodePtr request = NULL, node;
5928 xmlNsPtr isds_ns = NULL;
5930 if (!context) return IE_INVALID_CONTEXT;
5931 if (!service_name || !message_id) return IE_INVAL;
5932 if (!response || !code || !status_message) return IE_INVAL;
5933 if (!raw_response_length && raw_response) return IE_INVAL;
5935 /* Free output argument */
5936 xmlFreeDoc(*response); *response = NULL;
5937 if (raw_response) zfree(*raw_response);
5938 free(*code);
5939 free(*status_message);
5942 /* Check if connection is established
5943 * TODO: This check should be done donwstairs. */
5944 if (!context->curl) return IE_CONNECTION_CLOSED;
5946 service_name_locale = utf82locale((char*)service_name);
5947 message_id_locale = utf82locale(message_id);
5948 if (!service_name_locale || !message_id_locale) {
5949 err = IE_NOMEM;
5950 goto leave;
5953 /* Build request */
5954 request = xmlNewNode(NULL, service_name);
5955 if (!request) {
5956 isds_printf_message(context,
5957 _("Could not build %s request"), service_name_locale);
5958 err = IE_ERROR;
5959 goto leave;
5961 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5962 if(!isds_ns) {
5963 isds_log_message(context, _("Could not create ISDS name space"));
5964 err = IE_ERROR;
5965 goto leave;
5967 xmlSetNs(request, isds_ns);
5970 /* Add requested ID */
5971 err = validate_message_id_length(context, (xmlChar *) message_id);
5972 if (err) goto leave;
5973 INSERT_STRING(request, "dmID", message_id);
5976 isds_log(ILF_ISDS, ILL_DEBUG,
5977 _("Sending %s request for %s message ID to ISDS\n"),
5978 service_name_locale, message_id_locale);
5980 /* Send request */
5981 err = isds(context, service, request, response,
5982 raw_response, raw_response_length);
5983 xmlFreeNode(request); request = NULL;
5985 if (err) {
5986 isds_log(ILF_ISDS, ILL_DEBUG,
5987 _("Processing ISDS response on %s request failed\n"),
5988 service_name_locale);
5989 goto leave;
5992 /* Check for response status */
5993 err = isds_response_status(context, service, *response,
5994 code, status_message, NULL);
5995 if (err) {
5996 isds_log(ILF_ISDS, ILL_DEBUG,
5997 _("ISDS response on %s request is missing status\n"),
5998 service_name_locale);
5999 goto leave;
6002 /* Request processed, but nothing found */
6003 if (xmlStrcmp(*code, BAD_CAST "0000")) {
6004 char *code_locale = utf82locale((char*) *code);
6005 char *status_message_locale = utf82locale((char*) *status_message);
6006 isds_log(ILF_ISDS, ILL_DEBUG,
6007 _("Server refused %s request for %s message ID "
6008 "(code=%s, message=%s)\n"),
6009 service_name_locale, message_id_locale,
6010 code_locale, status_message_locale);
6011 isds_log_message(context, status_message_locale);
6012 free(code_locale);
6013 free(status_message_locale);
6014 err = IE_ISDS;
6015 goto leave;
6018 leave:
6019 free(message_id_locale);
6020 free(service_name_locale);
6021 xmlFreeNode(request);
6022 return err;
6026 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
6027 * signed data and free ISDS response.
6028 * @context is session context
6029 * @message_id is UTF-8 encoded message ID for loging purpose
6030 * @response is parsed XML document. It will be freed and NULLed in the middle
6031 * of function run to save memmory. This is not guaranted in case of error.
6032 * @request_name is name of ISDS request used to construct response root
6033 * element name and for logging purpose.
6034 * @raw is reallocated output buffer with DER encoded CMS data
6035 * @raw_length is size of @raw buffer in bytes
6036 * @returns standard error codes, in case of error, @raw will be freed and
6037 * NULLed, @response sometimes. */
6038 static isds_error find_extract_signed_data_free_response(
6039 struct isds_ctx *context, const xmlChar *message_id,
6040 xmlDocPtr *response, const xmlChar *request_name,
6041 void **raw, size_t *raw_length) {
6043 isds_error err = IE_SUCCESS;
6044 char *xpath_expression = NULL;
6045 xmlXPathContextPtr xpath_ctx = NULL;
6046 xmlXPathObjectPtr result = NULL;
6047 char *encoded_structure = NULL;
6049 if (!context) return IE_INVALID_CONTEXT;
6050 if (!raw) return IE_INVAL;
6051 zfree(*raw);
6052 if (!message_id || !response || !*response || !request_name || !raw_length)
6053 return IE_INVAL;
6055 /* Build XPath expression */
6056 xpath_expression = astrcat3("/isds:", (char *) request_name,
6057 "Response/isds:dmSignature");
6058 if (!xpath_expression) return IE_NOMEM;
6060 /* Extract data */
6061 xpath_ctx = xmlXPathNewContext(*response);
6062 if (!xpath_ctx) {
6063 err = IE_ERROR;
6064 goto leave;
6066 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6067 err = IE_ERROR;
6068 goto leave;
6070 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
6071 if (!result) {
6072 err = IE_ERROR;
6073 goto leave;
6075 /* Empty response */
6076 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6077 char *message_id_locale = utf82locale((char*) message_id);
6078 isds_printf_message(context,
6079 _("Server did not return any signed data for mesage ID `%s' "
6080 "on %s request"),
6081 message_id_locale, request_name);
6082 free(message_id_locale);
6083 err = IE_ISDS;
6084 goto leave;
6086 /* More reponses */
6087 if (result->nodesetval->nodeNr > 1) {
6088 char *message_id_locale = utf82locale((char*) message_id);
6089 isds_printf_message(context,
6090 _("Server did return more signed data for message ID `%s' "
6091 "on %s request"),
6092 message_id_locale, request_name);
6093 free(message_id_locale);
6094 err = IE_ISDS;
6095 goto leave;
6097 /* One response */
6098 xpath_ctx->node = result->nodesetval->nodeTab[0];
6100 /* Extract PKCS#7 structure */
6101 EXTRACT_STRING(".", encoded_structure);
6102 if (!encoded_structure) {
6103 isds_log_message(context, _("dmSignature element is empty"));
6106 /* Here we have delivery info as standalone CMS in encoded_structure.
6107 * We don't need any other data, free them: */
6108 xmlXPathFreeObject(result); result = NULL;
6109 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
6110 xmlFreeDoc(*response); *response = NULL;
6113 /* Decode PKCS#7 to DER format */
6114 *raw_length = b64decode(encoded_structure, raw);
6115 if (*raw_length == (size_t) -1) {
6116 isds_log_message(context,
6117 _("Error while Base64-decoding PKCS#7 structure"));
6118 err = IE_ERROR;
6119 goto leave;
6122 leave:
6123 if (err) {
6124 zfree(*raw);
6125 raw_length = 0;
6128 free(encoded_structure);
6129 xmlXPathFreeObject(result);
6130 xmlXPathFreeContext(xpath_ctx);
6131 free(xpath_expression);
6133 return err;
6137 /* Download incoming message envelope identified by ID.
6138 * @context is session context
6139 * @message_id is message identifier (you can get them from
6140 * isds_get_list_of_received_messages())
6141 * @message is automatically reallocated message retrieved from ISDS.
6142 * It will miss documents per se. Use isds_get_received_message(), if you are
6143 * interrested in documents (content) too.
6144 * Returned hash and timestamp require documents to be verifiable. */
6145 isds_error isds_get_received_envelope(struct isds_ctx *context,
6146 const char *message_id, struct isds_message **message) {
6148 isds_error err = IE_SUCCESS;
6149 xmlDocPtr response = NULL;
6150 xmlChar *code = NULL, *status_message = NULL;
6151 xmlXPathContextPtr xpath_ctx = NULL;
6152 xmlXPathObjectPtr result = NULL;
6154 if (!context) return IE_INVALID_CONTEXT;
6156 /* Free former message if any */
6157 if (!message) return IE_INVAL;
6158 isds_message_free(message);
6160 /* Do request and check for success */
6161 err = build_send_check_message_request(context, SERVICE_DM_INFO,
6162 BAD_CAST "MessageEnvelopeDownload", message_id,
6163 &response, NULL, NULL, &code, &status_message);
6164 if (err) goto leave;
6166 /* Extract data */
6167 xpath_ctx = xmlXPathNewContext(response);
6168 if (!xpath_ctx) {
6169 err = IE_ERROR;
6170 goto leave;
6172 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6173 err = IE_ERROR;
6174 goto leave;
6176 result = xmlXPathEvalExpression(
6177 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
6178 "isds:dmReturnedMessageEnvelope",
6179 xpath_ctx);
6180 if (!result) {
6181 err = IE_ERROR;
6182 goto leave;
6184 /* Empty response */
6185 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6186 char *message_id_locale = utf82locale((char*) message_id);
6187 isds_printf_message(context,
6188 _("Server did not return any envelope for ID `%s' "
6189 "on MessageEnvelopeDownload request"), message_id_locale);
6190 free(message_id_locale);
6191 err = IE_ISDS;
6192 goto leave;
6194 /* More envelops */
6195 if (result->nodesetval->nodeNr > 1) {
6196 char *message_id_locale = utf82locale((char*) message_id);
6197 isds_printf_message(context,
6198 _("Server did return more envelopes for ID `%s' "
6199 "on MessageEnvelopeDownload request"), message_id_locale);
6200 free(message_id_locale);
6201 err = IE_ISDS;
6202 goto leave;
6204 /* One message */
6205 xpath_ctx->node = result->nodesetval->nodeTab[0];
6207 /* Extract the envelope (= message without documents, hence 0) */
6208 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
6209 if (err) goto leave;
6211 /* Save XML blob */
6212 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
6213 &(*message)->raw_length);
6215 leave:
6216 if (err) {
6217 isds_message_free(message);
6220 xmlXPathFreeObject(result);
6221 xmlXPathFreeContext(xpath_ctx);
6223 free(code);
6224 free(status_message);
6225 xmlFreeDoc(response);
6227 if (!err)
6228 isds_log(ILF_ISDS, ILL_DEBUG,
6229 _("MessageEnvelopeDownload request processed by server "
6230 "successfully.\n")
6232 return err;
6236 /* Load delivery info of any format from buffer.
6237 * @context is session context
6238 * @raw_type advertises format of @buffer content. Only delivery info types
6239 * are accepted.
6240 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
6241 * retrieve such data from message->raw after calling
6242 * isds_get_signed_delivery_info().
6243 * @length is length of buffer in bytes.
6244 * @message is automatically reallocated message parsed from @buffer.
6245 * @strategy selects how buffer will be attached into raw isds_message member.
6246 * */
6247 isds_error isds_load_delivery_info(struct isds_ctx *context,
6248 const isds_raw_type raw_type,
6249 const void *buffer, const size_t length,
6250 struct isds_message **message, const isds_buffer_strategy strategy) {
6252 isds_error err = IE_SUCCESS;
6253 message_ns_type message_ns;
6254 xmlDocPtr message_doc = NULL;
6255 xmlXPathContextPtr xpath_ctx = NULL;
6256 xmlXPathObjectPtr result = NULL;
6257 void *xml_stream = NULL;
6258 size_t xml_stream_length = 0;
6260 if (!context) return IE_INVALID_CONTEXT;
6261 if (!message) return IE_INVAL;
6262 isds_message_free(message);
6263 if (!buffer) return IE_INVAL;
6266 /* Select buffer format and extract XML from CMS*/
6267 switch (raw_type) {
6268 case RAWTYPE_DELIVERYINFO:
6269 message_ns = MESSAGE_NS_UNSIGNED;
6270 xml_stream = (void *) buffer;
6271 xml_stream_length = length;
6272 break;
6274 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
6275 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
6276 xml_stream = (void *) buffer;
6277 xml_stream_length = length;
6278 break;
6280 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
6281 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
6282 err = extract_cms_data(context, buffer, length,
6283 &xml_stream, &xml_stream_length);
6284 if (err) goto leave;
6285 break;
6287 default:
6288 isds_log_message(context, _("Bad raw delivery representation type"));
6289 return IE_INVAL;
6290 break;
6293 isds_log(ILF_ISDS, ILL_DEBUG,
6294 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
6295 xml_stream_length, xml_stream);
6297 /* Convert delivery info XML stream into XPath context */
6298 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
6299 if (!message_doc) {
6300 err = IE_XML;
6301 goto leave;
6303 xpath_ctx = xmlXPathNewContext(message_doc);
6304 if (!xpath_ctx) {
6305 err = IE_ERROR;
6306 goto leave;
6308 /* XXX: Name spaces mangled for signed delivery info:
6309 * http://isds.czechpoint.cz/v20/delivery:
6311 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
6312 * <q:dmDelivery>
6313 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
6314 * <p:dmID>170272</p:dmID>
6315 * ...
6316 * </p:dmDm>
6317 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
6318 * ...
6319 * </q:dmEvents>...</q:dmEvents>
6320 * </q:dmDelivery>
6321 * </q:GetDeliveryInfoResponse>
6322 * */
6323 if (register_namespaces(xpath_ctx, message_ns)) {
6324 err = IE_ERROR;
6325 goto leave;
6327 result = xmlXPathEvalExpression(
6328 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
6329 xpath_ctx);
6330 if (!result) {
6331 err = IE_ERROR;
6332 goto leave;
6334 /* Empty delivery info */
6335 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6336 isds_printf_message(context,
6337 _("XML document ss not sisds:dmDelivery document"));
6338 err = IE_ISDS;
6339 goto leave;
6341 /* More delivery infos */
6342 if (result->nodesetval->nodeNr > 1) {
6343 isds_printf_message(context,
6344 _("XML document has more sisds:dmDelivery elements"));
6345 err = IE_ISDS;
6346 goto leave;
6348 /* One delivery info */
6349 xpath_ctx->node = result->nodesetval->nodeTab[0];
6351 /* Extract the envelope (= message without documents, hence 0).
6352 * XXX: extract_TReturnedMessage() can obtain attachments size,
6353 * but delivery info carries none. It's coded as option elements,
6354 * so it should work. */
6355 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
6356 if (err) goto leave;
6358 /* Extract events */
6359 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
6360 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
6361 if (err) { err = IE_ERROR; goto leave; }
6362 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
6363 if (err) goto leave;
6365 /* Append raw CMS structure into message */
6366 (*message)->raw_type = raw_type;
6367 switch (strategy) {
6368 case BUFFER_DONT_STORE:
6369 break;
6370 case BUFFER_COPY:
6371 (*message)->raw = malloc(length);
6372 if (!(*message)->raw) {
6373 err = IE_NOMEM;
6374 goto leave;
6376 memcpy((*message)->raw, buffer, length);
6377 (*message)->raw_length = length;
6378 break;
6379 case BUFFER_MOVE:
6380 (*message)->raw = (void *) buffer;
6381 (*message)->raw_length = length;
6382 break;
6383 default:
6384 err = IE_ENUM;
6385 goto leave;
6388 leave:
6389 if (err) {
6390 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
6391 isds_message_free(message);
6394 xmlXPathFreeObject(result);
6395 xmlXPathFreeContext(xpath_ctx);
6396 xmlFreeDoc(message_doc);
6397 if (xml_stream != buffer) cms_data_free(xml_stream);
6399 if (!err)
6400 isds_log(ILF_ISDS, ILL_DEBUG,
6401 _("Delivery info loaded successfully.\n"));
6402 return err;
6406 /* Download signed delivery infosheet of given message identified by ID.
6407 * @context is session context
6408 * @message_id is message identifier (you can get them from
6409 * isds_get_list_of_{sent,received}_messages())
6410 * @message is automatically reallocated message retrieved from ISDS.
6411 * It will miss documents per se. Use isds_get_signed_received_message(),
6412 * if you are interrested in documents (content). OTOH, only this function
6413 * can get list events message has gone through. */
6414 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
6415 const char *message_id, struct isds_message **message) {
6417 isds_error err = IE_SUCCESS;
6418 xmlDocPtr response = NULL;
6419 xmlChar *code = NULL, *status_message = NULL;
6420 void *raw = NULL;
6421 size_t raw_length = 0;
6423 if (!context) return IE_INVALID_CONTEXT;
6425 /* Free former message if any */
6426 if (!message) return IE_INVAL;
6427 isds_message_free(message);
6429 /* Do request and check for success */
6430 err = build_send_check_message_request(context, SERVICE_DM_INFO,
6431 BAD_CAST "GetSignedDeliveryInfo", message_id,
6432 &response, NULL, NULL, &code, &status_message);
6433 if (err) goto leave;
6435 /* Find signed delivery info, extract it into raw and maybe free
6436 * response */
6437 err = find_extract_signed_data_free_response(context,
6438 (xmlChar *)message_id, &response,
6439 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
6440 if (err) goto leave;
6442 /* Parse delivery info */
6443 err = isds_load_delivery_info(context,
6444 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
6445 message, BUFFER_MOVE);
6446 if (err) goto leave;
6448 raw = NULL;
6450 leave:
6451 if (err) {
6452 isds_message_free(message);
6455 free(raw);
6456 free(code);
6457 free(status_message);
6458 xmlFreeDoc(response);
6460 if (!err)
6461 isds_log(ILF_ISDS, ILL_DEBUG,
6462 _("GetSignedDeliveryInfo request processed by server "
6463 "successfully.\n")
6465 return err;
6469 /* Download delivery infosheet of given message identified by ID.
6470 * @context is session context
6471 * @message_id is message identifier (you can get them from
6472 * isds_get_list_of_{sent,received}_messages())
6473 * @message is automatically reallocated message retrieved from ISDS.
6474 * It will miss documents per se. Use isds_get_received_message(), if you are
6475 * interrested in documents (content). OTOH, only this function can get list
6476 * events message has gone through. */
6477 isds_error isds_get_delivery_info(struct isds_ctx *context,
6478 const char *message_id, struct isds_message **message) {
6480 isds_error err = IE_SUCCESS;
6481 xmlDocPtr response = NULL;
6482 xmlChar *code = NULL, *status_message = NULL;
6483 xmlXPathContextPtr xpath_ctx = NULL;
6484 xmlXPathObjectPtr result = NULL;
6485 xmlNodePtr delivery_node = NULL;
6487 if (!context) return IE_INVALID_CONTEXT;
6489 /* Free former message if any */
6490 if (!message) return IE_INVAL;
6491 isds_message_free(message);
6493 /* Do request and check for success */
6494 err = build_send_check_message_request(context, SERVICE_DM_INFO,
6495 BAD_CAST "GetDeliveryInfo", message_id,
6496 &response, NULL, NULL, &code, &status_message);
6497 if (err) goto leave;
6499 /* Extract data */
6500 xpath_ctx = xmlXPathNewContext(response);
6501 if (!xpath_ctx) {
6502 err = IE_ERROR;
6503 goto leave;
6505 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6506 err = IE_ERROR;
6507 goto leave;
6509 result = xmlXPathEvalExpression(
6510 BAD_CAST "/isds:GetDeliveryInfoResponse/isds:dmDelivery",
6511 xpath_ctx);
6512 if (!result) {
6513 err = IE_ERROR;
6514 goto leave;
6516 /* Empty response */
6517 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6518 char *message_id_locale = utf82locale((char*) message_id);
6519 isds_printf_message(context,
6520 _("Server did not return any delivery info for ID `%s' "
6521 "on GetDeliveryInfo request"), message_id_locale);
6522 free(message_id_locale);
6523 err = IE_ISDS;
6524 goto leave;
6526 /* More delivery infos */
6527 if (result->nodesetval->nodeNr > 1) {
6528 char *message_id_locale = utf82locale((char*) message_id);
6529 isds_printf_message(context,
6530 _("Server did return more delivery infos for ID `%s' "
6531 "on GetDeliveryInfo request"), message_id_locale);
6532 free(message_id_locale);
6533 err = IE_ISDS;
6534 goto leave;
6536 /* One delivery info */
6537 xpath_ctx->node = delivery_node = result->nodesetval->nodeTab[0];
6539 /* Extract the envelope (= message without documents, hence 0).
6540 * XXX: extract_TReturnedMessage() can obtain attachments size,
6541 * but delivery info carries none. It's coded as option elements,
6542 * so it should work. */
6543 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
6544 if (err) goto leave;
6546 /* Extract events */
6547 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmEvents", xpath_ctx);
6548 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
6549 if (err) { err = IE_ERROR; goto leave; }
6550 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
6551 if (err) goto leave;
6553 /* Save XML blob */
6554 err = serialize_subtree(context, delivery_node, &(*message)->raw,
6555 &(*message)->raw_length);
6557 leave:
6558 if (err) {
6559 isds_message_free(message);
6562 xmlXPathFreeObject(result);
6563 xmlXPathFreeContext(xpath_ctx);
6565 free(code);
6566 free(status_message);
6567 xmlFreeDoc(response);
6569 if (!err)
6570 isds_log(ILF_ISDS, ILL_DEBUG,
6571 _("GetDeliveryInfo request processed by server "
6572 "successfully.\n")
6574 return err;
6578 /* Load incoming message from buffer.
6579 * @context is session context
6580 * @buffer XML stream with unsigned message. You can retrieve such data from
6581 * message->raw after calling isds_get_received_message().
6582 * @length is length of buffer in bytes.
6583 * @message is automatically reallocated message parsed from @buffer.
6584 * @strategy selects how buffer will be attached into raw isds_message member.
6585 * */
6586 isds_error isds_load_received_message(struct isds_ctx *context,
6587 const void *buffer, const size_t length,
6588 struct isds_message **message, const isds_buffer_strategy strategy) {
6590 isds_error err = IE_SUCCESS;
6591 xmlDocPtr message_doc = NULL;
6592 xmlXPathContextPtr xpath_ctx = NULL;
6593 xmlXPathObjectPtr result = NULL;
6595 if (!context) return IE_INVALID_CONTEXT;
6596 if (!message) return IE_INVAL;
6597 isds_message_free(message);
6598 if (!buffer) return IE_INVAL;
6601 isds_log(ILF_ISDS, ILL_DEBUG,
6602 _("Incoming message content:\n%.*s\nEnd of message\n"),
6603 length, buffer);
6605 /* Convert extracted messages XML stream into XPath context */
6606 message_doc = xmlParseMemory(buffer, length);
6607 if (!message_doc) {
6608 err = IE_XML;
6609 goto leave;
6611 xpath_ctx = xmlXPathNewContext(message_doc);
6612 if (!xpath_ctx) {
6613 err = IE_ERROR;
6614 goto leave;
6616 /* XXX: Standard name space */
6617 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6618 err = IE_ERROR;
6619 goto leave;
6621 result = xmlXPathEvalExpression(
6622 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
6623 xpath_ctx);
6624 if (!result) {
6625 err = IE_ERROR;
6626 goto leave;
6628 /* Missing dmReturnedMessage */
6629 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6630 isds_printf_message(context,
6631 _("XML document does not contain isds:dmReturnedMessage "
6632 "element"));
6633 err = IE_ISDS;
6634 goto leave;
6636 /* More elements. This should never happen. */
6637 if (result->nodesetval->nodeNr > 1) {
6638 isds_printf_message(context,
6639 _("XML document has more isds:dmReturnedMessage elements"));
6640 err = IE_ISDS;
6641 goto leave;
6643 /* One message */
6644 xpath_ctx->node = result->nodesetval->nodeTab[0];
6646 /* Extract the message */
6647 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
6648 if (err) goto leave;
6650 /* Append XML stream into message */
6651 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
6652 switch (strategy) {
6653 case BUFFER_DONT_STORE:
6654 break;
6655 case BUFFER_COPY:
6656 (*message)->raw = malloc(length);
6657 if (!(*message)->raw) {
6658 err = IE_NOMEM;
6659 goto leave;
6661 memcpy((*message)->raw, buffer, length);
6662 (*message)->raw_length = length;
6663 break;
6664 case BUFFER_MOVE:
6665 (*message)->raw = (void *) buffer;
6666 (*message)->raw_length = length;
6667 break;
6668 default:
6669 err = IE_ENUM;
6670 goto leave;
6673 leave:
6674 if (err) {
6675 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
6676 isds_message_free(message);
6679 xmlFreeDoc(message_doc);
6680 xmlXPathFreeObject(result);
6681 xmlXPathFreeContext(xpath_ctx);
6683 if (!err)
6684 isds_log(ILF_ISDS, ILL_DEBUG,
6685 _("Incoming message loaded successfully.\n"));
6686 return err;
6690 /* Download incoming message identified by ID.
6691 * @context is session context
6692 * @message_id is message identifier (you can get them from
6693 * isds_get_list_of_received_messages())
6694 * @message is automatically reallocated message retrieved from ISDS */
6695 isds_error isds_get_received_message(struct isds_ctx *context,
6696 const char *message_id, struct isds_message **message) {
6698 isds_error err = IE_SUCCESS;
6699 xmlDocPtr response = NULL;
6700 void *xml_stream = NULL;
6701 size_t xml_stream_length;
6702 xmlChar *code = NULL, *status_message = NULL;
6703 xmlXPathContextPtr xpath_ctx = NULL;
6704 xmlXPathObjectPtr result = NULL;
6705 char *phys_path = NULL;
6706 size_t phys_start, phys_end;
6708 if (!context) return IE_INVALID_CONTEXT;
6710 /* Free former message if any */
6711 if (message) isds_message_free(message);
6713 /* Do request and check for success */
6714 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
6715 BAD_CAST "MessageDownload", message_id,
6716 &response, &xml_stream, &xml_stream_length,
6717 &code, &status_message);
6718 if (err) goto leave;
6720 /* Extract data */
6721 xpath_ctx = xmlXPathNewContext(response);
6722 if (!xpath_ctx) {
6723 err = IE_ERROR;
6724 goto leave;
6726 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6727 err = IE_ERROR;
6728 goto leave;
6730 result = xmlXPathEvalExpression(
6731 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
6732 xpath_ctx);
6733 if (!result) {
6734 err = IE_ERROR;
6735 goto leave;
6737 /* Empty response */
6738 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6739 char *message_id_locale = utf82locale((char*) message_id);
6740 isds_printf_message(context,
6741 _("Server did not return any message for ID `%s' "
6742 "on MessageDownload request"), message_id_locale);
6743 free(message_id_locale);
6744 err = IE_ISDS;
6745 goto leave;
6747 /* More messages */
6748 if (result->nodesetval->nodeNr > 1) {
6749 char *message_id_locale = utf82locale((char*) message_id);
6750 isds_printf_message(context,
6751 _("Server did return more messages for ID `%s' "
6752 "on MessageDownload request"), message_id_locale);
6753 free(message_id_locale);
6754 err = IE_ISDS;
6755 goto leave;
6757 /* One message */
6758 xpath_ctx->node = result->nodesetval->nodeTab[0];
6760 /* Extract the message */
6761 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
6762 if (err) goto leave;
6764 /* Locate raw XML blob */
6765 phys_path = strdup(
6766 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
6767 PHYSXML_ELEMENT_SEPARATOR
6768 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
6769 PHYSXML_ELEMENT_SEPARATOR
6770 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
6772 if (!phys_path) {
6773 err = IE_NOMEM;
6774 goto leave;
6776 err = find_element_boundary(xml_stream, xml_stream_length,
6777 phys_path, &phys_start, &phys_end);
6778 zfree(phys_path);
6779 if (err) {
6780 isds_log_message(context,
6781 _("Substring with isds:MessageDownloadResponse element "
6782 "could not be located in raw SOAP message"));
6783 goto leave;
6785 /* Save XML blob */
6786 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
6787 &(*message)->raw_length);*/
6788 /* TODO: Store name space declarations from ancestors */
6789 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
6790 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
6791 (*message)->raw_length = phys_end - phys_start + 1;
6792 (*message)->raw = malloc((*message)->raw_length);
6793 if (!(*message)->raw) {
6794 err = IE_NOMEM;
6795 goto leave;
6797 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
6800 leave:
6801 if (err) {
6802 isds_message_free(message);
6805 free(phys_path);
6807 xmlXPathFreeObject(result);
6808 xmlXPathFreeContext(xpath_ctx);
6810 free(code);
6811 free(status_message);
6812 free(xml_stream);
6813 xmlFreeDoc(response);
6815 if (!err)
6816 isds_log(ILF_ISDS, ILL_DEBUG,
6817 _("MessageDownload request processed by server "
6818 "successfully.\n")
6820 return err;
6824 /* Load signed message from buffer.
6825 * @context is session context
6826 * @outgoing is true if message is outgoing, false if message is incoming
6827 * @buffer is DER encoded PKCS#7 structure with signed message. You can
6828 * retrieve such data from message->raw after calling
6829 * isds_get_signed_{received,sent}_message().
6830 * @length is length of buffer in bytes.
6831 * @message is automatically reallocated message parsed from @buffer.
6832 * @strategy selects how buffer will be attached into raw isds_message member.
6833 * */
6834 isds_error isds_load_signed_message(struct isds_ctx *context,
6835 const _Bool outgoing, const void *buffer, const size_t length,
6836 struct isds_message **message, const isds_buffer_strategy strategy) {
6838 isds_error err = IE_SUCCESS;
6839 xmlDocPtr message_doc = NULL;
6840 xmlXPathContextPtr xpath_ctx = NULL;
6841 xmlXPathObjectPtr result = NULL;
6842 void *xml_stream = NULL;
6843 size_t xml_stream_length = 0;
6845 if (!context) return IE_INVALID_CONTEXT;
6846 if (!message) return IE_INVAL;
6847 isds_message_free(message);
6848 if (!buffer) return IE_INVAL;
6851 /* Extract message from PKCS#7 structure */
6852 err = extract_cms_data(context, buffer, length,
6853 &xml_stream, &xml_stream_length);
6854 if (err) goto leave;
6856 isds_log(ILF_ISDS, ILL_DEBUG, (outgoing) ?
6857 _("Signed outgoing message content:\n%.*s\nEnd of message\n") :
6858 _("Signed incoming message content:\n%.*s\nEnd of message\n"),
6859 xml_stream_length, xml_stream);
6861 /* Convert extracted messages XML stream into XPath context */
6862 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
6863 if (!message_doc) {
6864 err = IE_XML;
6865 goto leave;
6867 xpath_ctx = xmlXPathNewContext(message_doc);
6868 if (!xpath_ctx) {
6869 err = IE_ERROR;
6870 goto leave;
6872 /* XXX: Name spaces mangled for outgoing direction:
6873 * http://isds.czechpoint.cz/v20/SentMessage:
6875 * <q:MessageDownloadResponse
6876 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
6877 * <q:dmReturnedMessage>
6878 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
6879 * <p:dmID>151916</p:dmID>
6880 * ...
6881 * </p:dmDm>
6882 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
6883 * ...
6884 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
6885 * </q:dmReturnedMessage>
6886 * </q:MessageDownloadResponse>
6888 * XXX: Name spaces mangled for incoming direction:
6889 * http://isds.czechpoint.cz/v20/message:
6891 * <q:MessageDownloadResponse
6892 * xmlns:q="http://isds.czechpoint.cz/v20/message">
6893 * <q:dmReturnedMessage>
6894 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
6895 * <p:dmID>151916</p:dmID>
6896 * ...
6897 * </p:dmDm>
6898 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
6899 * ...
6900 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
6901 * </q:dmReturnedMessage>
6902 * </q:MessageDownloadResponse>
6904 * Stupidity of ISDS developers is unlimited */
6905 if (register_namespaces(xpath_ctx, (outgoing) ?
6906 MESSAGE_NS_SIGNED_OUTGOING : MESSAGE_NS_SIGNED_INCOMING)) {
6907 err = IE_ERROR;
6908 goto leave;
6910 /* XXX: Embeded message XML document is always rooted as
6911 * /sisds:MessageDownloadResponse (even outgoing message). */
6912 result = xmlXPathEvalExpression(
6913 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
6914 xpath_ctx);
6915 if (!result) {
6916 err = IE_ERROR;
6917 goto leave;
6919 /* Empty embedded message */
6920 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6921 isds_printf_message(context,
6922 _("XML document embedded into PKCS#7 structure is not "
6923 "sisds:dmReturnedMessage document"));
6924 err = IE_ISDS;
6925 goto leave;
6927 /* More embedded messages */
6928 if (result->nodesetval->nodeNr > 1) {
6929 isds_printf_message(context,
6930 _("Embeded XML document into PKCS#7 structure has more "
6931 "root sisds:dmReturnedMessage elements"));
6932 err = IE_ISDS;
6933 goto leave;
6935 /* One embedded message */
6936 xpath_ctx->node = result->nodesetval->nodeTab[0];
6938 /* Extract the message */
6939 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
6940 if (err) goto leave;
6942 /* Append raw CMS structure into message */
6943 (*message)->raw_type = (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
6944 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
6945 switch (strategy) {
6946 case BUFFER_DONT_STORE:
6947 break;
6948 case BUFFER_COPY:
6949 (*message)->raw = malloc(length);
6950 if (!(*message)->raw) {
6951 err = IE_NOMEM;
6952 goto leave;
6954 memcpy((*message)->raw, buffer, length);
6955 (*message)->raw_length = length;
6956 break;
6957 case BUFFER_MOVE:
6958 (*message)->raw = (void *) buffer;
6959 (*message)->raw_length = length;
6960 break;
6961 default:
6962 err = IE_ENUM;
6963 goto leave;
6967 leave:
6968 if (err) {
6969 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
6970 isds_message_free(message);
6973 xmlFreeDoc(message_doc);
6974 cms_data_free(xml_stream);
6975 xmlXPathFreeObject(result);
6976 xmlXPathFreeContext(xpath_ctx);
6978 if (!err)
6979 isds_log(ILF_ISDS, ILL_DEBUG,
6980 _("Signed message loaded successfully.\n"));
6981 return err;
6985 /* Load message of any type from buffer.
6986 * @context is session context
6987 * @raw_type defines content type of @buffer. Only message types are allowed.
6988 * @buffer is message raw representation. Format (CMS, plain signed,
6989 * message direction) is defined in @raw_type. You can retrieve such data
6990 * from message->raw after calling isds_get_[signed]{received,sent}_message().
6991 * @length is length of buffer in bytes.
6992 * @message is automatically reallocated message parsed from @buffer.
6993 * @strategy selects how buffer will be attached into raw isds_message member.
6994 * */
6995 isds_error isds_load_message(struct isds_ctx *context,
6996 const isds_raw_type raw_type, const void *buffer, const size_t length,
6997 struct isds_message **message, const isds_buffer_strategy strategy) {
6999 isds_error err = IE_SUCCESS;
7000 void *xml_stream = NULL;
7001 size_t xml_stream_length = 0;
7002 message_ns_type message_ns;
7003 xmlDocPtr message_doc = NULL;
7004 xmlXPathContextPtr xpath_ctx = NULL;
7005 xmlXPathObjectPtr result = NULL;
7007 if (!context) return IE_INVALID_CONTEXT;
7008 if (!message) return IE_INVAL;
7009 isds_message_free(message);
7010 if (!buffer) return IE_INVAL;
7013 /* Select buffer format and extract XML from CMS*/
7014 switch (raw_type) {
7015 case RAWTYPE_INCOMING_MESSAGE:
7016 message_ns = MESSAGE_NS_UNSIGNED;
7017 xml_stream = (void *) buffer;
7018 xml_stream_length = length;
7019 break;
7021 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
7022 message_ns = MESSAGE_NS_SIGNED_INCOMING;
7023 xml_stream = (void *) buffer;
7024 xml_stream_length = length;
7025 break;
7027 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
7028 message_ns = MESSAGE_NS_SIGNED_INCOMING;
7029 err = extract_cms_data(context, buffer, length,
7030 &xml_stream, &xml_stream_length);
7031 if (err) goto leave;
7032 break;
7034 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
7035 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
7036 xml_stream = (void *) buffer;
7037 xml_stream_length = length;
7038 break;
7040 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
7041 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
7042 err = extract_cms_data(context, buffer, length,
7043 &xml_stream, &xml_stream_length);
7044 if (err) goto leave;
7045 break;
7047 default:
7048 isds_log_message(context, _("Bad raw message representation type"));
7049 return IE_INVAL;
7050 break;
7053 isds_log(ILF_ISDS, ILL_DEBUG,
7054 _("Loading message:\n%.*s\nEnd of message\n"),
7055 xml_stream_length, xml_stream);
7057 /* Convert messages XML stream into XPath context */
7058 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
7059 if (!message_doc) {
7060 err = IE_XML;
7061 goto leave;
7063 xpath_ctx = xmlXPathNewContext(message_doc);
7064 if (!xpath_ctx) {
7065 err = IE_ERROR;
7066 goto leave;
7068 /* XXX: Standard name space for unsigned icoming direction:
7069 * http://isds.czechpoint.cz/v20/SentMessage
7071 * XXX: Name spaces mangled for signed outgoing direction:
7072 * http://isds.czechpoint.cz/v20/SentMessage:
7074 * <q:MessageDownloadResponse
7075 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
7076 * <q:dmReturnedMessage>
7077 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
7078 * <p:dmID>151916</p:dmID>
7079 * ...
7080 * </p:dmDm>
7081 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
7082 * ...
7083 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
7084 * </q:dmReturnedMessage>
7085 * </q:MessageDownloadResponse>
7087 * XXX: Name spaces mangled for signed incoming direction:
7088 * http://isds.czechpoint.cz/v20/message:
7090 * <q:MessageDownloadResponse
7091 * xmlns:q="http://isds.czechpoint.cz/v20/message">
7092 * <q:dmReturnedMessage>
7093 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
7094 * <p:dmID>151916</p:dmID>
7095 * ...
7096 * </p:dmDm>
7097 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
7098 * ...
7099 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
7100 * </q:dmReturnedMessage>
7101 * </q:MessageDownloadResponse>
7103 * Stupidity of ISDS developers is unlimited */
7104 if (register_namespaces(xpath_ctx, message_ns)) {
7105 err = IE_ERROR;
7106 goto leave;
7108 result = xmlXPathEvalExpression(
7109 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
7110 xpath_ctx);
7111 if (!result) {
7112 err = IE_ERROR;
7113 goto leave;
7115 /* Empty message */
7116 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7117 isds_printf_message(context,
7118 _("XML document does not contain "
7119 "sisds:dmReturnedMessage element"));
7120 err = IE_ISDS;
7121 goto leave;
7123 /* More messages */
7124 if (result->nodesetval->nodeNr > 1) {
7125 isds_printf_message(context,
7126 _("XML document has more sisds:dmReturnedMessage elements"));
7127 err = IE_ISDS;
7128 goto leave;
7130 /* One message */
7131 xpath_ctx->node = result->nodesetval->nodeTab[0];
7133 /* Extract the message */
7134 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
7135 if (err) goto leave;
7137 /* Append raw buffer into message */
7138 (*message)->raw_type = raw_type;
7139 switch (strategy) {
7140 case BUFFER_DONT_STORE:
7141 break;
7142 case BUFFER_COPY:
7143 (*message)->raw = malloc(length);
7144 if (!(*message)->raw) {
7145 err = IE_NOMEM;
7146 goto leave;
7148 memcpy((*message)->raw, buffer, length);
7149 (*message)->raw_length = length;
7150 break;
7151 case BUFFER_MOVE:
7152 (*message)->raw = (void *) buffer;
7153 (*message)->raw_length = length;
7154 break;
7155 default:
7156 err = IE_ENUM;
7157 goto leave;
7161 leave:
7162 if (err) {
7163 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
7164 isds_message_free(message);
7167 if (xml_stream != buffer) cms_data_free(xml_stream);
7168 xmlXPathFreeObject(result);
7169 xmlXPathFreeContext(xpath_ctx);
7170 xmlFreeDoc(message_doc);
7172 if (!err)
7173 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
7174 return err;
7178 /* Download signed incoming/outgoing message identified by ID.
7179 * @context is session context
7180 * @output is true for outging message, false for incoming message
7181 * @message_id is message identifier (you can get them from
7182 * isds_get_list_of_{sent,received}_messages())
7183 * @message is automatically reallocated message retrieved from ISDS. The raw
7184 * memeber will be filled with PKCS#7 structure in DER format. */
7185 _hidden isds_error isds_get_signed_message(struct isds_ctx *context,
7186 const _Bool outgoing, const char *message_id,
7187 struct isds_message **message) {
7189 isds_error err = IE_SUCCESS;
7190 xmlDocPtr response = NULL;
7191 xmlChar *code = NULL, *status_message = NULL;
7192 xmlXPathContextPtr xpath_ctx = NULL;
7193 xmlXPathObjectPtr result = NULL;
7194 char *encoded_structure = NULL;
7195 void *raw = NULL;
7196 size_t raw_length = 0;
7198 if (!context) return IE_INVALID_CONTEXT;
7199 if (!message) return IE_INVAL;
7200 isds_message_free(message);
7202 /* Do request and check for success */
7203 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
7204 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
7205 BAD_CAST "SignedMessageDownload",
7206 message_id, &response, NULL, NULL, &code, &status_message);
7207 if (err) goto leave;
7209 /* Find signed message, extract it into raw and maybe free
7210 * response */
7211 err = find_extract_signed_data_free_response(context,
7212 (xmlChar *)message_id, &response,
7213 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
7214 BAD_CAST "SignedMessageDownload",
7215 &raw, &raw_length);
7216 if (err) goto leave;
7218 /* Parse message */
7219 err = isds_load_message(context,
7220 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
7221 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
7222 raw, raw_length, message, BUFFER_MOVE);
7223 if (err) goto leave;
7225 raw = NULL;
7227 leave:
7228 if (err) {
7229 isds_message_free(message);
7232 free(encoded_structure);
7233 xmlXPathFreeObject(result);
7234 xmlXPathFreeContext(xpath_ctx);
7235 free(raw);
7237 free(code);
7238 free(status_message);
7239 xmlFreeDoc(response);
7241 if (!err)
7242 isds_log(ILF_ISDS, ILL_DEBUG,
7243 (outgoing) ?
7244 _("SignedSentMessageDownload request processed by server "
7245 "successfully.\n") :
7246 _("SignedMessageDownload request processed by server "
7247 "successfully.\n")
7249 return err;
7253 /* Download signed incoming message identified by ID.
7254 * @context is session context
7255 * @message_id is message identifier (you can get them from
7256 * isds_get_list_of_received_messages())
7257 * @message is automatically reallocated message retrieved from ISDS. The raw
7258 * memeber will be filled with PKCS#7 structure in DER format. */
7259 isds_error isds_get_signed_received_message(struct isds_ctx *context,
7260 const char *message_id, struct isds_message **message) {
7261 return isds_get_signed_message(context, 0, message_id, message);
7265 /* Download signed outgoing message identified by ID.
7266 * @context is session context
7267 * @message_id is message identifier (you can get them from
7268 * isds_get_list_of_sent_messages())
7269 * @message is automatically reallocated message retrieved from ISDS. The raw
7270 * memeber will be filled with PKCS#7 structure in DER format. */
7271 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
7272 const char *message_id, struct isds_message **message) {
7273 return isds_get_signed_message(context, 1, message_id, message);
7277 /* Retrieve hash of message identified by ID stored in ISDS.
7278 * @context is session context
7279 * @message_id is message identifier
7280 * @hash is automatically reallocated message hash downloaded from ISDS.
7281 * Message must exist in system and must not be deleted. */
7282 isds_error isds_download_message_hash(struct isds_ctx *context,
7283 const char *message_id, struct isds_hash **hash) {
7285 isds_error err = IE_SUCCESS;
7286 xmlDocPtr response = NULL;
7287 xmlChar *code = NULL, *status_message = NULL;
7288 xmlXPathContextPtr xpath_ctx = NULL;
7289 xmlXPathObjectPtr result = NULL;
7291 if (!context) return IE_INVALID_CONTEXT;
7293 isds_hash_free(hash);
7295 err = build_send_check_message_request(context, SERVICE_DM_INFO,
7296 BAD_CAST "VerifyMessage", message_id,
7297 &response, NULL, NULL, &code, &status_message);
7298 if (err) goto leave;
7301 /* Extract data */
7302 xpath_ctx = xmlXPathNewContext(response);
7303 if (!xpath_ctx) {
7304 err = IE_ERROR;
7305 goto leave;
7307 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7308 err = IE_ERROR;
7309 goto leave;
7311 result = xmlXPathEvalExpression(
7312 BAD_CAST "/isds:VerifyMessageResponse",
7313 xpath_ctx);
7314 if (!result) {
7315 err = IE_ERROR;
7316 goto leave;
7318 /* Empty response */
7319 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7320 char *message_id_locale = utf82locale((char*) message_id);
7321 isds_printf_message(context,
7322 _("Server did not return any response for ID `%s' "
7323 "on VerifyMessage request"), message_id_locale);
7324 free(message_id_locale);
7325 err = IE_ISDS;
7326 goto leave;
7328 /* More responses */
7329 if (result->nodesetval->nodeNr > 1) {
7330 char *message_id_locale = utf82locale((char*) message_id);
7331 isds_printf_message(context,
7332 _("Server did return more responses for ID `%s' "
7333 "on VerifyMessage request"), message_id_locale);
7334 free(message_id_locale);
7335 err = IE_ISDS;
7336 goto leave;
7338 /* One response */
7339 xpath_ctx->node = result->nodesetval->nodeTab[0];
7341 /* Extract the hash */
7342 err = find_and_extract_DmHash(context, hash, xpath_ctx);
7344 leave:
7345 if (err) {
7346 isds_hash_free(hash);
7349 xmlXPathFreeObject(result);
7350 xmlXPathFreeContext(xpath_ctx);
7352 free(code);
7353 free(status_message);
7354 xmlFreeDoc(response);
7356 if (!err)
7357 isds_log(ILF_ISDS, ILL_DEBUG,
7358 _("VerifyMessage request processed by server "
7359 "successfully.\n")
7361 return err;
7365 /* Mark message as read. This is a transactional commit function to acknoledge
7366 * to ISDS the message has been downloaded and processed by client properly.
7367 * @context is session context
7368 * @message_id is message identifier. */
7369 isds_error isds_mark_message_read(struct isds_ctx *context,
7370 const char *message_id) {
7372 isds_error err = IE_SUCCESS;
7373 xmlDocPtr response = NULL;
7374 xmlChar *code = NULL, *status_message = NULL;
7376 if (!context) return IE_INVALID_CONTEXT;
7378 /* Do request and check for success */
7379 err = build_send_check_message_request(context, SERVICE_DM_INFO,
7380 BAD_CAST "MarkMessageAsDownloaded", message_id,
7381 &response, NULL, NULL, &code, &status_message);
7383 free(code);
7384 free(status_message);
7385 xmlFreeDoc(response);
7387 if (!err)
7388 isds_log(ILF_ISDS, ILL_DEBUG,
7389 _("MarkMessageAsDownloaded request processed by server "
7390 "successfully.\n")
7392 return err;
7395 /* Mark message as received by recipient. This is applicable only to
7396 * commercial message. There is no specified way how to distinguishe
7397 * commercial message from government message yet. Government message is
7398 * received automatically (by law), commenrcial message on recipient request.
7399 * @context is session context
7400 * @message_id is message identifier. */
7401 isds_error isds_mark_message_received(struct isds_ctx *context,
7402 const char *message_id) {
7404 isds_error err = IE_SUCCESS;
7405 xmlDocPtr response = NULL;
7406 xmlChar *code = NULL, *status_message = NULL;
7408 if (!context) return IE_INVALID_CONTEXT;
7410 /* Do request and check for success */
7411 err = build_send_check_message_request(context, SERVICE_DM_INFO,
7412 BAD_CAST "ConfirmDelivery", message_id,
7413 &response, NULL, NULL, &code, &status_message);
7415 free(code);
7416 free(status_message);
7417 xmlFreeDoc(response);
7419 if (!err)
7420 isds_log(ILF_ISDS, ILL_DEBUG,
7421 _("ConfirmDelivery request processed by server "
7422 "successfully.\n")
7424 return err;
7428 #undef INSERT_ELEMENT
7429 #undef CHECK_FOR_STRING_LENGTH
7430 #undef INSERT_STRING_ATTRIBUTE
7431 #undef INSERT_ULONGINTNOPTR
7432 #undef INSERT_ULONGINT
7433 #undef INSERT_LONGINT
7434 #undef INSERT_BOOLEAN
7435 #undef INSERT_STRING
7436 #undef EXTRACT_STRING_ATTRIBUTE
7437 #undef EXTRACT_ULONGINT
7438 #undef EXTRACT_LONGINT
7439 #undef EXTRACT_BOOLEAN
7440 #undef EXTRACT_STRING
7443 /* Compute hash of message from raw representation and store it into envelope.
7444 * Original hash structure will be destroyed in envelope.
7445 * @context is session context
7446 * @message is message carrying raw XML message blob
7447 * @algorithm is desired hash algorithm to use */
7448 isds_error isds_compute_message_hash(struct isds_ctx *context,
7449 struct isds_message *message, const isds_hash_algorithm algorithm) {
7450 isds_error err = IE_SUCCESS;
7451 const char *nsuri;
7452 void *xml_stream = NULL;
7453 size_t xml_stream_length;
7454 size_t phys_start, phys_end;
7455 char *phys_path = NULL;
7456 struct isds_hash *new_hash = NULL;
7459 if (!context) return IE_INVALID_CONTEXT;
7460 if (!message) return IE_INVAL;
7462 if (!message->raw) {
7463 isds_log_message(context,
7464 _("Message does not carry raw representation"));
7465 return IE_INVAL;
7468 switch (message->raw_type) {
7469 case RAWTYPE_INCOMING_MESSAGE:
7470 nsuri = ISDS_NS;
7471 xml_stream = message->raw;
7472 xml_stream_length = message->raw_length;
7473 break;
7475 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
7476 nsuri = SISDS_INCOMING_NS;
7477 xml_stream = message->raw;
7478 xml_stream_length = message->raw_length;
7479 break;
7481 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
7482 nsuri = SISDS_INCOMING_NS;
7483 err = extract_cms_data(context, message->raw, message->raw_length,
7484 &xml_stream, &xml_stream_length);
7485 if (err) goto leave;
7486 break;
7488 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
7489 nsuri = SISDS_OUTGOING_NS;
7490 xml_stream = message->raw;
7491 xml_stream_length = message->raw_length;
7492 break;
7494 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
7495 nsuri = SISDS_OUTGOING_NS;
7496 err = extract_cms_data(context, message->raw, message->raw_length,
7497 &xml_stream, &xml_stream_length);
7498 if (err) goto leave;
7499 break;
7501 default:
7502 isds_log_message(context, _("Bad raw representation type"));
7503 return IE_INVAL;
7504 break;
7508 /* XXX: Hash is computed from original string represinting isds:dmDm
7509 * subtree. That means no encoding, white space, xmlns attributes changes.
7510 * In other words, input for hash can be invalid XML stream. */
7511 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
7512 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
7513 PHYSXML_ELEMENT_SEPARATOR,
7514 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
7515 PHYSXML_ELEMENT_SEPARATOR
7516 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
7517 err = IE_NOMEM;
7518 goto leave;
7520 err = find_element_boundary(xml_stream, xml_stream_length,
7521 phys_path, &phys_start, &phys_end);
7522 zfree(phys_path);
7523 if (err) {
7524 isds_log_message(context,
7525 _("Substring with isds:dmDM element could not be located "
7526 "in raw message"));
7527 goto leave;
7531 /* Compute hash */
7532 new_hash = calloc(1, sizeof(*new_hash));
7533 if (!new_hash) {
7534 err = IE_NOMEM;
7535 goto leave;
7537 new_hash->algorithm = algorithm;
7538 err = compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
7539 new_hash);
7540 if (err) {
7541 isds_log_message(context, _("Could not compute message hash"));
7542 goto leave;
7545 /* Save computed hash */
7546 if (!message->envelope) {
7547 message->envelope = calloc(1, sizeof(*message->envelope));
7548 if (!message->envelope) {
7549 err = IE_NOMEM;
7550 goto leave;
7553 isds_hash_free(&message->envelope->hash);
7554 message->envelope->hash = new_hash;
7556 leave:
7557 if (err) {
7558 isds_hash_free(&new_hash);
7561 free(phys_path);
7562 if (xml_stream != message->raw) free(xml_stream);
7563 return err;
7567 /* Compare two hashes.
7568 * @h1 is first hash
7569 * @h2 is another hash
7570 * @return
7571 * IE_SUCCESS if hashes equal
7572 * IE_NOTUNIQ if hashes are comparable, but they don't equal
7573 * IE_ENUM if not comparable, but both structures defined
7574 * IE_INVAL if some of the structures are undefined (NULL)
7575 * IE_ERROR if internal error occurs */
7576 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
7577 if (h1 == NULL || h2 == NULL) return IE_INVAL;
7578 if (h1->algorithm != h2->algorithm) return IE_ENUM;
7579 if (h1->length != h2->length) return IE_ERROR;
7580 if (h1->length > 0 && !h1->value) return IE_ERROR;
7581 if (h2->length > 0 && !h2->value) return IE_ERROR;
7583 for (int i = 0; i < h1->length; i++) {
7584 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
7585 return IE_NOTEQUAL;
7587 return IE_SUCCESS;
7591 /* Check message has gone through ISDS by comparing message hash stored in
7592 * ISDS and locally computed hash. You must provide message with valid raw
7593 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
7594 * This is convenient wrapper for isds_download_message_hash(),
7595 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
7596 * @context is session context
7597 * @message is message with valid raw and envelope member; envelope->hash
7598 * member will be changed during funcion run. Use envelope on heap only.
7599 * @return
7600 * IE_SUCCESS if message originates in ISDS
7601 * IE_NOTEQUAL if message is unknown to ISDS
7602 * other code for other errors */
7603 isds_error isds_verify_message_hash(struct isds_ctx *context,
7604 struct isds_message *message) {
7605 isds_error err = IE_SUCCESS;
7606 struct isds_hash *downloaded_hash = NULL;
7608 if (!context) return IE_INVALID_CONTEXT;
7609 if (!message) return IE_INVAL;
7611 if (!message->envelope) {
7612 isds_log_message(context,
7613 _("Given message structure is missing envelope"));
7614 return IE_INVAL;
7616 if (!message->raw) {
7617 isds_log_message(context,
7618 _("Given message structure is missing raw representation"));
7619 return IE_INVAL;
7622 err = isds_download_message_hash(context, message->envelope->dmID,
7623 &downloaded_hash);
7624 if (err) goto leave;
7626 err = isds_compute_message_hash(context, message,
7627 downloaded_hash->algorithm);
7628 if (err) goto leave;
7630 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
7632 leave:
7633 isds_hash_free(&downloaded_hash);
7634 return err;
7638 /* Search for document by document ID in list of documents. IDs are compared
7639 * as UTF-8 string.
7640 * @documents is list of isds_documents
7641 * @id is document identifier
7642 * @return first matching document or NULL. */
7643 const struct isds_document *isds_find_document_by_id(
7644 const struct isds_list *documents, const char *id) {
7645 const struct isds_list *item;
7646 const struct isds_document *document;
7648 for (item = documents; item; item = item->next) {
7649 document = (struct isds_document *) item->data;
7650 if (!document) continue;
7652 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
7653 return document;
7656 return NULL;
7660 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
7661 struct isds_message **message);
7662 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
7663 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
7664 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
7665 struct isds_address **address);
7667 int isds_message_free(struct isds_message **message);
7668 int isds_address_free(struct isds_address **address);
7672 /* Makes known all relevant namespaces to given XPath context
7673 * @xpat_ctx is XPath context
7674 * @message_ns selects propper message name space. Unsisnged and signed
7675 * messages differs.
7676 * prefix and to URI ISDS_NS */
7677 _hidden isds_error register_namespaces(xmlXPathContextPtr xpath_ctx,
7678 const message_ns_type message_ns) {
7679 const xmlChar *message_namespace = NULL;
7681 if (!xpath_ctx) return IE_ERROR;
7683 switch(message_ns) {
7684 case MESSAGE_NS_UNSIGNED:
7685 message_namespace = BAD_CAST ISDS_NS; break;
7686 case MESSAGE_NS_SIGNED_INCOMING:
7687 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
7688 case MESSAGE_NS_SIGNED_OUTGOING:
7689 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
7690 case MESSAGE_NS_SIGNED_DELIVERY:
7691 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
7692 default:
7693 return IE_ENUM;
7696 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
7697 return IE_ERROR;
7698 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
7699 return IE_ERROR;
7700 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
7701 return IE_ERROR;
7702 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
7703 return IE_ERROR;
7704 return IE_SUCCESS;