Implement CreateDataBoxPFOInfo as isds_add_pfoinfo()
[libisds.git] / src / isds.c
blob29f6783a647960cee74d60c3532bd7fbf6ae2a35
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 const 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_BirthInfo recursively */
280 static struct isds_BirthInfo *isds_BirthInfo_duplicate(
281 const struct isds_BirthInfo *template) {
282 struct isds_BirthInfo *new = NULL;
284 if (!template) return NULL;
286 new = calloc(1, sizeof(*new));
287 if (!new) return NULL;
289 FLATDUP_OR_ERROR(new->biDate, template->biDate);
290 STRDUP_OR_ERROR(new->biCity, template->biCity);
291 STRDUP_OR_ERROR(new->biCounty, template->biCounty);
292 STRDUP_OR_ERROR(new->biState, template->biState);
294 return new;
296 error:
297 isds_BirthInfo_free(&new);
298 return NULL;
302 /* Copy structure isds_Address recursively */
303 struct isds_Address *isds_Address_duplicate(
304 const struct isds_Address *template) {
305 struct isds_Address *new = NULL;
307 if (!template) return NULL;
309 new = calloc(1, sizeof(*new));
310 if (!new) return NULL;
312 STRDUP_OR_ERROR(new->adCity, template->adCity);
313 STRDUP_OR_ERROR(new->adStreet, template->adStreet);
314 STRDUP_OR_ERROR(new->adNumberInStreet, template->adNumberInStreet);
315 STRDUP_OR_ERROR(new->adNumberInMunicipality,
316 template->adNumberInMunicipality);
317 STRDUP_OR_ERROR(new->adZipCode, template->adZipCode);
318 STRDUP_OR_ERROR(new->adState, template->adState);
320 return new;
322 error:
323 isds_Address_free(&new);
324 return NULL;
328 /* Copy structure isds_DbOwnerInfo recursively */
329 struct isds_DbOwnerInfo *isds_DbOwnerInfo_duplicate(
330 const struct isds_DbOwnerInfo *template) {
331 struct isds_DbOwnerInfo *new = NULL;
332 if (!template) return NULL;
334 new = calloc(1, sizeof(*new));
335 if (!new) return NULL;
337 STRDUP_OR_ERROR(new->dbID, template->dbID);
338 FLATDUP_OR_ERROR(new->dbType, template->dbType);
339 STRDUP_OR_ERROR(new->ic, template->ic);
341 if (template->personName) {
342 if (!(new->personName =
343 isds_PersonName_duplicate(template->personName)))
344 goto error;
347 STRDUP_OR_ERROR(new->firmName, template->firmName);
349 if (template->birthInfo) {
350 if (!(new->birthInfo =
351 isds_BirthInfo_duplicate(template->birthInfo)))
352 goto error;
355 if (template->address) {
356 if (!(new->address = isds_Address_duplicate(template->address)))
357 goto error;
360 STRDUP_OR_ERROR(new->nationality, template->nationality);
361 STRDUP_OR_ERROR(new->email, template->email);
362 STRDUP_OR_ERROR(new->telNumber, template->telNumber);
363 STRDUP_OR_ERROR(new->identifier, template->identifier);
364 STRDUP_OR_ERROR(new->registryCode, template->registryCode);
365 FLATDUP_OR_ERROR(new->dbState, template->dbState);
366 FLATDUP_OR_ERROR(new->dbEffectiveOVM, template->dbEffectiveOVM);
367 FLATDUP_OR_ERROR(new->dbOpenAddressing, template->dbOpenAddressing);
369 return new;
371 error:
372 isds_DbOwnerInfo_free(&new);
373 return NULL;
377 /* Copy structure isds_DbUserInfo recursively */
378 struct isds_DbUserInfo *isds_DbUserInfo_duplicate(
379 const struct isds_DbUserInfo *template) {
380 struct isds_DbUserInfo *new = NULL;
381 if (!template) return NULL;
383 new = calloc(1, sizeof(*new));
384 if (!new) return NULL;
386 STRDUP_OR_ERROR(new->userID, template->userID);
387 FLATDUP_OR_ERROR(new->userType, template->userType);
388 FLATDUP_OR_ERROR(new->userPrivils, template->userPrivils);
390 if (template->personName) {
391 if (!(new->personName =
392 isds_PersonName_duplicate(template->personName)))
393 goto error;
396 if (template->address) {
397 if (!(new->address = isds_Address_duplicate(template->address)))
398 goto error;
401 FLATDUP_OR_ERROR(new->biDate, template->biDate);
402 STRDUP_OR_ERROR(new->ic, template->ic);
403 STRDUP_OR_ERROR(new->firmName, template->firmName);
404 STRDUP_OR_ERROR(new->caStreet, template->caStreet);
405 STRDUP_OR_ERROR(new->caCity, template->caCity);
406 STRDUP_OR_ERROR(new->caZipCode, template->caZipCode);
408 return new;
410 error:
411 isds_DbUserInfo_free(&new);
412 return NULL;
415 #undef FLATDUP_OR_ERROR
416 #undef STRDUP_OR_ERROR
419 /* Initialize ISDS library.
420 * Global function, must be called before other functions.
421 * If it failes you can not use ISDS library and must call isds_cleanup() to
422 * free partially inititialized global variables. */
423 isds_error isds_init(void) {
424 /* NULL global variables */
425 log_facilities = ILF_ALL;
426 log_level = ILL_WARNING;
428 #if ENABLE_NLS
429 /* Initialize gettext */
430 bindtextdomain(PACKAGE, LOCALEDIR);
431 #endif
433 /* Initialize CURL */
434 if (curl_global_init(CURL_GLOBAL_ALL)) {
435 isds_log(ILF_ISDS, ILL_CRIT, _("CURL library initialization failed\n"));
436 return IE_ERROR;
439 /* Inicialize gpg-error because of gpgme and ksba */
440 if (gpg_err_init()) {
441 isds_log(ILF_ISDS, ILL_CRIT,
442 _("gpg-error library initialization failed\n"));
443 return IE_ERROR;
446 /* Initialize GPGME */
447 if (init_gpgme()) {
448 isds_log(ILF_ISDS, ILL_CRIT,
449 _("GPGME library initialization failed\n"));
450 return IE_ERROR;
453 /* Initialize gcrypt */
454 if (init_gcrypt()) {
455 isds_log(ILF_ISDS, ILL_CRIT,
456 _("gcrypt library initialization failed\n"));
457 return IE_ERROR;
460 /* This can _exit() current program. Find not so assertive check. */
461 LIBXML_TEST_VERSION;
463 /* Check expat */
464 if (init_expat()) {
465 isds_log(ILF_ISDS, ILL_CRIT,
466 _("expat library initialization failed\n"));
467 return IE_ERROR;
470 /* Allocate global variables */
473 return IE_SUCCESS;
477 /* Deinicialize ISDS library.
478 * Global function, must be called as last library function. */
479 isds_error isds_cleanup(void) {
480 /* XML */
481 xmlCleanupParser();
483 /* Curl */
484 curl_global_cleanup();
486 return IE_SUCCESS;
490 /* Return text description of ISDS error */
491 const char *isds_strerror(const isds_error error) {
492 switch (error) {
493 case IE_SUCCESS:
494 return(_("Success")); break;
495 case IE_ERROR:
496 return(_("Unspecified error")); break;
497 case IE_NOTSUP:
498 return(_("Not supported")); break;
499 case IE_INVAL:
500 return(_("Invalid value")); break;
501 case IE_INVALID_CONTEXT:
502 return(_("Invalid context")); break;
503 case IE_NOT_LOGGED_IN:
504 return(_("Not logged in")); break;
505 case IE_CONNECTION_CLOSED:
506 return(_("Connection closed")); break;
507 case IE_TIMED_OUT:
508 return(_("Timed out")); break;
509 case IE_NOEXIST:
510 return(_("Not exist")); break;
511 case IE_NOMEM:
512 return(_("Out of memory")); break;
513 case IE_NETWORK:
514 return(_("Network problem")); break;
515 case IE_HTTP:
516 return(_("HTTP problem")); break;
517 case IE_SOAP:
518 return(_("SOAP problem")); break;
519 case IE_XML:
520 return(_("XML problem")); break;
521 case IE_ISDS:
522 return(_("ISDS server problem")); break;
523 case IE_ENUM:
524 return(_("Invalid enum value")); break;
525 case IE_DATE:
526 return(_("Invalid date value")); break;
527 case IE_2BIG:
528 return(_("Too big")); break;
529 case IE_2SMALL:
530 return(_("Too small")); break;
531 case IE_NOTUNIQ:
532 return(_("Value not unique")); break;
533 case IE_NOTEQUAL:
534 return(_("Values not uqual")); break;
535 case IE_PARTIAL_SUCCESS:
536 return(_("Some suboperations failed")); break;
537 default:
538 return(_("Unknown error"));
543 /* Create ISDS context.
544 * Each context can be used for different sessions to (possibly) differnet
545 * ISDS server with different credentials. */
546 struct isds_ctx *isds_ctx_create(void) {
547 struct isds_ctx *context;
548 context = malloc(sizeof(*context));
549 if (context) memset(context, 0, sizeof(*context));
550 return context;
554 /* Destroy ISDS context and free memmory.
555 * @context will be NULLed on success. */
556 isds_error isds_ctx_free(struct isds_ctx **context) {
557 if (!context || !*context) {
558 return IE_INVALID_CONTEXT;
561 /* Discard credentials */
562 isds_logout(*context);
564 /* Free other structures */
565 free((*context)->tls_verify_server);
566 free((*context)->tls_ca_file);
567 free((*context)->tls_ca_dir);
568 free((*context)->long_message);
570 free(*context);
571 *context = NULL;
572 return IE_SUCCESS;
576 /* Return long message text produced by library fucntion, e.g. detailed error
577 * mesage. Returned pointer is only valid until new library function is
578 * called for the same context. Could be NULL, especially if NULL context is
579 * supplied. Return string is locale encoded. */
580 char *isds_long_message(const struct isds_ctx *context) {
581 if (!context) return NULL;
582 return context->long_message;
586 /* Stores message into context' long_message buffer.
587 * Application can pick the message up using isds_long_message().
588 * NULL @message truncates the buffer but does not deallocate it.
589 * @message is coded in locale encoding */
590 _hidden isds_error isds_log_message(struct isds_ctx *context,
591 const char *message) {
592 char *buffer;
593 size_t length;
595 if (!context) return IE_INVALID_CONTEXT;
597 /* FIXME: Check for integer overflow */
598 length = 1 + ((message) ? strlen(message) : 0);
599 buffer = realloc(context->long_message, length);
600 if (!buffer) return IE_NOMEM;
602 if (message)
603 strcpy(buffer, message);
604 else
605 *buffer = '\0';
607 context->long_message = buffer;
608 return IE_SUCCESS;
612 /* Appends message into context' long_message buffer.
613 * Application can pick the message up using isds_long_message().
614 * NULL message has void effect. */
615 _hidden isds_error isds_append_message(struct isds_ctx *context,
616 const char *message) {
617 char *buffer;
618 size_t old_length, length;
620 if (!context) return IE_INVALID_CONTEXT;
621 if (!message) return IE_SUCCESS;
622 if (!context->long_message)
623 return isds_log_message(context, message);
625 old_length = strlen(context->long_message);
626 /* FIXME: Check for integer overflow */
627 length = 1 + old_length + strlen(message);
628 buffer = realloc(context->long_message, length);
629 if (!buffer) return IE_NOMEM;
631 strcpy(buffer + old_length, message);
633 context->long_message = buffer;
634 return IE_SUCCESS;
638 /* Stores formated message into context' long_message buffer.
639 * Application can pick the message up using isds_long_message(). */
640 _hidden isds_error isds_printf_message(struct isds_ctx *context,
641 const char *format, ...) {
642 va_list ap;
643 int length;
645 if (!context) return IE_INVALID_CONTEXT;
646 va_start(ap, format);
647 length = isds_vasprintf(&(context->long_message), format, ap);
648 va_end(ap);
650 return (length < 0) ? IE_ERROR: IE_SUCCESS;
654 /* Set logging up.
655 * @facilities is bitmask of isds_log_facility values,
656 * @level is verbosity level. */
657 void isds_set_logging(const unsigned int facilities,
658 const isds_log_level level) {
659 log_facilities = facilities;
660 log_level = level;
664 /* Log @message in class @facility with log @level into global log. @message
665 * is printf(3) formating string, variadic arguments may be neccessary.
666 * For debugging purposes. */
667 _hidden isds_error isds_log(const isds_log_facility facility,
668 const isds_log_level level, const char *message, ...) {
669 va_list ap;
671 if (level > log_level) return IE_SUCCESS;
672 if (!(log_facilities & facility)) return IE_SUCCESS;
673 if (!message) return IE_INVAL;
675 /* TODO: Allow to register output function provided by application
676 * (e.g. fprintf to stderr or copy to text area GUI widget). */
678 va_start(ap, message);
679 vfprintf(stderr, message, ap);
680 va_end(ap);
681 /* Line buffered printf is default.
682 * fflush(stderr);*/
684 return IE_SUCCESS;
688 /* Set timeout in miliseconds for each network job like connecting to server
689 * or sending message. Use 0 to disable timeout limits. */
690 isds_error isds_set_timeout(struct isds_ctx *context,
691 const unsigned int timeout) {
692 if (!context) return IE_INVALID_CONTEXT;
694 context->timeout = timeout;
696 if (context->curl) {
697 CURLcode curl_err;
699 curl_err = curl_easy_setopt(context->curl, CURLOPT_NOSIGNAL, 1);
700 if (!curl_err)
701 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT_MS,
702 context->timeout);
703 if (curl_err) return IE_ERROR;
706 return IE_SUCCESS;
710 /* Register callback function libisds calls periodocally during HTTP data
711 * transfer.
712 * @context is session context
713 * @callback is function provided by application libsds will call. See type
714 * defition for @callback argument explanation.
715 * @data is application specific data @callback gets as last argument */
716 isds_error isds_set_progress_callback(struct isds_ctx *context,
717 isds_progress_callback callback, void *data) {
718 if (!context) return IE_INVALID_CONTEXT;
720 context->progress_callback = callback;
721 context->progress_callback_data = data;
723 return IE_SUCCESS;
727 /* Change SSL/TLS settings.
728 * @context is context which setting will be applied to
729 * @option is name of option. It determines the type of last argument. See
730 * isds_tls_option definition for more info.
731 * @... is value of new setting. Type is determined by @option
732 * */
733 isds_error isds_set_tls(struct isds_ctx *context, const isds_tls_option option,
734 ...) {
735 isds_error err = IE_SUCCESS;
736 va_list ap;
737 char *pointer, *string;
739 if (!context) return IE_INVALID_CONTEXT;
741 va_start(ap, option);
743 #define REPLACE_VA_STRING(destination) \
744 string = va_arg(ap, char *); \
745 if (string) { \
746 pointer = realloc((destination), 1 + strlen(string)); \
747 if (!pointer) { err = IE_NOMEM; goto leave; } \
748 strcpy(pointer, string); \
749 (destination) = pointer; \
750 } else { \
751 free(destination); \
752 (destination) = NULL; \
755 switch (option) {
756 case ITLS_VERIFY_SERVER:
757 if (!context->tls_verify_server) {
758 context->tls_verify_server =
759 malloc(sizeof(*context->tls_verify_server));
760 if (!context->tls_verify_server) {
761 err = IE_NOMEM; goto leave;
764 *context->tls_verify_server = (_Bool) (0 != va_arg(ap, int));
765 break;
767 case ITLS_CA_FILE:
768 REPLACE_VA_STRING(context->tls_ca_file);
769 break;
770 case ITLS_CA_DIRECTORY:
771 REPLACE_VA_STRING(context->tls_ca_dir);
772 break;
774 default:
775 err = IE_ENUM; goto leave;
778 #undef REPLACE_VA_STRING
780 leave:
781 va_end(ap);
782 return err;
786 /* Discard credentials.
787 * Only that. It does not cause log out, connection close or similar. */
788 static isds_error discard_credentials(struct isds_ctx *context) {
789 if(!context) return IE_INVALID_CONTEXT;
791 if (context->username) {
792 memset(context->username, 0, strlen(context->username));
793 free(context->username);
794 context->username = NULL;
796 if (context->password) {
797 memset(context->password, 0, strlen(context->password));
798 free(context->password);
799 context->password = NULL;
802 return IE_SUCCESS;
806 /* Connect and log in into ISDS server.
807 * @url is address of ISDS web service
808 * @username is user name of ISDS user
809 * @password is user's secret password
810 * @certificate is NULL terminated string with PEM formated client's
811 * certificate. Use NULL if only password autentication should be performed.
812 * @key is private key for client's certificate as (base64 encoded?) NULL
813 * terminated string. Use NULL if only password autentication is desired.
814 * */
815 isds_error isds_login(struct isds_ctx *context, const char *url,
816 const char *username, const char *password,
817 const char *certificate, const char* key) {
818 isds_error err = IE_NOT_LOGGED_IN;
819 isds_error soap_err;
820 xmlNsPtr isds_ns = NULL;
821 xmlNodePtr request = NULL;
822 xmlNodePtr response = NULL;
824 if (!context) return IE_INVALID_CONTEXT;
825 if (!url || !username || !password) return IE_INVAL;
826 if (certificate || key) return IE_NOTSUP;
828 /* Store configuration */
829 free(context->url);
830 context->url = strdup(url);
831 if (!(context->url))
832 return IE_NOMEM;
834 /* Close connection if already logged in */
835 if (context->curl) {
836 close_connection(context);
839 /* Prepare CURL handle */
840 context->curl = curl_easy_init();
841 if (!(context->curl))
842 return IE_ERROR;
844 /* Build login request */
845 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
846 if (!request) {
847 isds_log_message(context, _("Could build ISDS login request"));
848 return IE_ERROR;
850 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
851 if(!isds_ns) {
852 isds_log_message(context, _("Could not create ISDS name space"));
853 xmlFreeNode(request);
854 return IE_ERROR;
856 xmlSetNs(request, isds_ns);
858 /* Store credentials */
859 /* FIXME: mlock password
860 * (I have a library) */
861 discard_credentials(context);
862 context->username = strdup(username);
863 context->password = strdup(password);
864 if (!(context->username && context->password)) {
865 discard_credentials(context);
866 xmlFreeNode(request);
867 return IE_NOMEM;
870 isds_log(ILF_ISDS, ILL_DEBUG, _("Logging user %s into server %s\n"),
871 username, url);
873 /* Send login request */
874 soap_err = soap(context, "dz", request, &response, NULL, NULL);
876 /* Remove credentials */
877 discard_credentials(context);
879 /* Destroy login request */
880 xmlFreeNode(request);
882 if (soap_err) {
883 xmlFreeNodeList(response);
884 close_connection(context);
885 return soap_err;
888 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
889 * authentication succeeded if soap_err == IE_SUCCESS */
890 err = IE_SUCCESS;
892 xmlFreeNodeList(response);
894 if (!err)
895 isds_log(ILF_ISDS, ILL_DEBUG,
896 _("User %s has been logged into server %s successfully\n"),
897 username, url);
898 return err;
902 /* Log out from ISDS server discards credentials and connection configuration. */
903 isds_error isds_logout(struct isds_ctx *context) {
904 if (!context) return IE_INVALID_CONTEXT;
906 /* Close connection */
907 if (context->curl) {
908 close_connection(context);
910 /* Discard credentials for sure. They should not survive isds_login(),
911 * even successful .*/
912 discard_credentials(context);
913 free(context->url);
914 context->url = NULL;
916 isds_log(ILF_ISDS, ILL_DEBUG, _("Logged out from ISDS server\n"));
917 } else {
918 discard_credentials(context);
920 return IE_SUCCESS;
924 /* Verify connection to ISDS is alive and server is responding.
925 * Sent dumy request to ISDS and expect dummy response. */
926 isds_error isds_ping(struct isds_ctx *context) {
927 isds_error soap_err;
928 xmlNsPtr isds_ns = NULL;
929 xmlNodePtr request = NULL;
930 xmlNodePtr response = NULL;
932 if (!context) return IE_INVALID_CONTEXT;
934 /* Check if connection is established */
935 if (!context->curl) return IE_CONNECTION_CLOSED;
938 /* Build dummy request */
939 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
940 if (!request) {
941 isds_log_message(context, _("Could build ISDS dummy request"));
942 return IE_ERROR;
944 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
945 if(!isds_ns) {
946 isds_log_message(context, _("Could not create ISDS name space"));
947 xmlFreeNode(request);
948 return IE_ERROR;
950 xmlSetNs(request, isds_ns);
952 isds_log(ILF_ISDS, ILL_DEBUG, _("Pinging ISDS server\n"));
954 /* Sent dummy request */
955 soap_err = soap(context, "dz", request, &response, NULL, NULL);
957 /* Destroy login request */
958 xmlFreeNode(request);
960 if (soap_err) {
961 isds_log(ILF_ISDS, ILL_DEBUG,
962 _("ISDS server could not be contacted\n"));
963 xmlFreeNodeList(response);
964 close_connection(context);
965 return soap_err;
968 /* XXX: Untill we don't propagate HTTP code 500 or 4xx, we can be sure
969 * authentication succeeded if soap_err == IE_SUCCESS */
970 /* TODO: ISDS documentation does not specify response body.
971 * However real server sends back DummyOperationResponse */
974 xmlFreeNodeList(response);
976 isds_log(ILF_ISDS, ILL_DEBUG, _("ISDS server alive\n"));
978 return IE_SUCCESS;
982 /* Send bogus request to ISDS.
983 * Just for test purposes */
984 isds_error isds_bogus_request(struct isds_ctx *context) {
985 isds_error err;
986 xmlNsPtr isds_ns = NULL;
987 xmlNodePtr request = NULL;
988 xmlDocPtr response = NULL;
989 xmlChar *code = NULL, *message = NULL;
991 if (!context) return IE_INVALID_CONTEXT;
993 /* Check if connection is established */
994 if (!context->curl) {
995 /* Testing printf message */
996 isds_printf_message(context, "%s", _("I said connection closed"));
997 return IE_CONNECTION_CLOSED;
1001 /* Build dummy request */
1002 request = xmlNewNode(NULL, BAD_CAST "X-BogusOperation");
1003 if (!request) {
1004 isds_log_message(context, _("Could build ISDS bogus request"));
1005 return IE_ERROR;
1007 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1008 if(!isds_ns) {
1009 isds_log_message(context, _("Could not create ISDS name space"));
1010 xmlFreeNode(request);
1011 return IE_ERROR;
1013 xmlSetNs(request, isds_ns);
1015 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending bogus request to ISDS\n"));
1017 /* Sent bogus request */
1018 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
1020 /* Destroy request */
1021 xmlFreeNode(request);
1023 if (err) {
1024 isds_log(ILF_ISDS, ILL_DEBUG,
1025 _("Processing ISDS response on bogus request failed\n"));
1026 xmlFreeDoc(response);
1027 return err;
1030 /* Check for response status */
1031 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
1032 &code, &message, NULL);
1033 if (err) {
1034 isds_log(ILF_ISDS, ILL_DEBUG,
1035 _("ISDS response on bogus request is missing status\n"));
1036 free(code);
1037 free(message);
1038 xmlFreeDoc(response);
1039 return err;
1041 if (xmlStrcmp(code, BAD_CAST "0000")) {
1042 char *code_locale = utf82locale((char*)code);
1043 char *message_locale = utf82locale((char*)message);
1044 isds_log(ILF_ISDS, ILL_DEBUG,
1045 _("Server refused bogus request (code=%s, message=%s)\n"),
1046 code_locale, message_locale);
1047 /* XXX: Literal error messages from ISDS are Czech mesages
1048 * (English sometimes) in UTF-8. It's hard to catch them for
1049 * translation. Successfully gettextized would return in locale
1050 * encoding, unsuccessfully translated would pass in UTF-8. */
1051 isds_log_message(context, message_locale);
1052 free(code_locale);
1053 free(message_locale);
1054 free(code);
1055 free(message);
1056 xmlFreeDoc(response);
1057 return IE_ISDS;
1061 free(code);
1062 free(message);
1063 xmlFreeDoc(response);
1065 isds_log(ILF_ISDS, ILL_DEBUG,
1066 _("Bogus message accepted by server. This should not happen.\n"));
1068 return IE_SUCCESS;
1072 /* Serialize XML subtree to buffer preserving XML indentatition.
1073 * @context is session context
1074 * @subtree is XML element to be serialized (with childern)
1075 * @buffer is automatically reallocated buffer where serialize to
1076 * @length is size of serialized stream in bytes
1077 * @return standard error code, free @buffer in case of error */
1078 static isds_error serialize_subtree(struct isds_ctx *context,
1079 xmlNodePtr subtree, void **buffer, size_t *length) {
1080 isds_error err = IE_SUCCESS;
1081 xmlBufferPtr xml_buffer = NULL;
1082 xmlSaveCtxtPtr save_ctx = NULL;
1083 xmlDocPtr subtree_doc = NULL;
1084 xmlNodePtr subtree_copy;
1085 xmlNsPtr isds_ns;
1086 void *new_buffer;
1088 if (!context) return IE_INVALID_CONTEXT;
1089 if (!buffer) return IE_INVAL;
1090 zfree(*buffer);
1091 if (!subtree || !length) return IE_INVAL;
1093 /* Make temporary XML document with @subtree root element */
1094 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1095 * It can result in not well-formed on invalid XML tree (e.g. name space
1096 * prefix definition can miss. */
1097 /*FIXME */
1099 subtree_doc = xmlNewDoc(BAD_CAST "1.0");
1100 if (!subtree_doc) {
1101 isds_log_message(context, _("Could not build temporary document"));
1102 err = IE_ERROR;
1103 goto leave;
1106 /* XXX: Copy subtree and attach the copy to document.
1107 * One node can not bee attached into more document at the same time.
1108 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1109 * automatically.
1110 * XXX: Check xmlSaveTree() too. */
1111 subtree_copy = xmlCopyNodeList(subtree);
1112 if (!subtree_copy) {
1113 isds_log_message(context, _("Could not copy subtree"));
1114 err = IE_ERROR;
1115 goto leave;
1117 xmlDocSetRootElement(subtree_doc, subtree_copy);
1119 /* Only this way we get namespace definition as @xmlns:isds,
1120 * otherwise we get namespace prefix without definition */
1121 /* FIXME: Don't overwrite original default namespace */
1122 isds_ns = xmlNewNs(subtree_copy, BAD_CAST ISDS_NS, NULL);
1123 if(!isds_ns) {
1124 isds_log_message(context, _("Could not create ISDS name space"));
1125 err = IE_ERROR;
1126 goto leave;
1128 xmlSetNs(subtree_copy, isds_ns);
1131 /* Serialize the document into buffer */
1132 xml_buffer = xmlBufferCreate();
1133 if (!xml_buffer) {
1134 isds_log_message(context, _("Could not create xmlBuffer"));
1135 err = IE_ERROR;
1136 goto leave;
1138 /* Last argument 0 means to not format the XML tree */
1139 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8", 0);
1140 if (!save_ctx) {
1141 isds_log_message(context, _("Could not create XML serializer"));
1142 err = IE_ERROR;
1143 goto leave;
1145 /* XXX: According LibXML documentation, this function does not return
1146 * meaningfull value yet */
1147 xmlSaveDoc(save_ctx, subtree_doc);
1148 if (-1 == xmlSaveFlush(save_ctx)) {
1149 isds_log_message(context,
1150 _("Could not serialize XML subtree"));
1151 err = IE_ERROR;
1152 goto leave;
1154 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1155 * even after xmlSaveFlush(). Thus close it here */
1156 xmlSaveClose(save_ctx); save_ctx = NULL;
1159 /* Store and detach buffer from xml_buffer */
1160 *buffer = xml_buffer->content;
1161 *length = xml_buffer->use;
1162 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1164 /* Shrink buffer */
1165 new_buffer = realloc(*buffer, *length);
1166 if (new_buffer) *buffer = new_buffer;
1168 leave:
1169 if (err) {
1170 zfree(*buffer);
1171 *length = 0;
1174 xmlSaveClose(save_ctx);
1175 xmlBufferFree(xml_buffer);
1176 xmlFreeDoc(subtree_doc); /* Frees subtree_copy, isds_ns etc. */
1177 return err;
1180 #if 0
1181 /* Dump XML subtree to buffer as literral string, not valid XML possibly.
1182 * @context is session context
1183 * @document is original document where @nodeset points to
1184 * @nodeset is XPath node set to dump (recursively)
1185 * @buffer is automarically reallocated buffer where serialize to
1186 * @length is size of serialized stream in bytes
1187 * @return standard error code, free @buffer in case of error */
1188 static isds_error dump_nodeset(struct isds_ctx *context,
1189 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1190 void **buffer, size_t *length) {
1191 isds_error err = IE_SUCCESS;
1192 xmlBufferPtr xml_buffer = NULL;
1193 void *new_buffer;
1195 if (!context) return IE_INVALID_CONTEXT;
1196 if (!buffer) return IE_INVAL;
1197 zfree(*buffer);
1198 if (!document || !nodeset || !length) return IE_INVAL;
1199 *length = 0;
1201 /* Empty node set results into NULL buffer */
1202 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1203 goto leave;
1206 /* Resuling the document into buffer */
1207 xml_buffer = xmlBufferCreate();
1208 if (!xml_buffer) {
1209 isds_log_message(context, _("Could not create xmlBuffer"));
1210 err = IE_ERROR;
1211 goto leave;
1214 /* Itearate over all nodes */
1215 for (int i = 0; i < nodeset->nodeNr; i++) {
1216 /* Serialize node.
1217 * XXX: xmlNodeDump() appends to xml_buffer. */
1218 if (-1 ==
1219 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1220 isds_log_message(context, _("Could not dump XML node"));
1221 err = IE_ERROR;
1222 goto leave;
1226 /* Store and detach buffer from xml_buffer */
1227 *buffer = xml_buffer->content;
1228 *length = xml_buffer->use;
1229 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1231 /* Shrink buffer */
1232 new_buffer = realloc(*buffer, *length);
1233 if (new_buffer) *buffer = new_buffer;
1236 leave:
1237 if (err) {
1238 zfree(*buffer);
1239 *length = 0;
1242 xmlBufferFree(xml_buffer);
1243 return err;
1245 #endif
1247 #if 0
1248 /* Dump XML subtree to buffer as literral string, not valid XML possibly.
1249 * @context is session context
1250 * @document is original document where @nodeset points to
1251 * @nodeset is XPath node set to dump (recursively)
1252 * @buffer is automarically reallocated buffer where serialize to
1253 * @length is size of serialized stream in bytes
1254 * @return standard error code, free @buffer in case of error */
1255 static isds_error dump_nodeset(struct isds_ctx *context,
1256 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1257 void **buffer, size_t *length) {
1258 isds_error err = IE_SUCCESS;
1259 xmlBufferPtr xml_buffer = NULL;
1260 xmlSaveCtxtPtr save_ctx = NULL;
1261 void *new_buffer;
1263 if (!context) return IE_INVALID_CONTEXT;
1264 if (!buffer) return IE_INVAL;
1265 zfree(*buffer);
1266 if (!document || !nodeset || !length) return IE_INVAL;
1267 *length = 0;
1269 /* Empty node set results into NULL buffer */
1270 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1271 goto leave;
1274 /* Resuling the document into buffer */
1275 xml_buffer = xmlBufferCreate();
1276 if (!xml_buffer) {
1277 isds_log_message(context, _("Could not create xmlBuffer"));
1278 err = IE_ERROR;
1279 goto leave;
1281 if (xmlSubstituteEntitiesDefault(1)) {
1282 isds_log_message(context, _("Could not disable attribute escaping"));
1283 err = IE_ERROR;
1284 goto leave;
1286 /* Last argument means:
1287 * 0 to not format the XML tree
1288 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1289 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8",
1290 XML_SAVE_NO_DECL|XML_SAVE_NO_EMPTY|XML_SAVE_NO_XHTML);
1291 if (!save_ctx) {
1292 isds_log_message(context, _("Could not create XML serializer"));
1293 err = IE_ERROR;
1294 goto leave;
1296 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1297 isds_log_message(context, _("Could not disable attribute escaping"));
1298 err = IE_ERROR;
1299 goto leave;
1303 /* Itearate over all nodes */
1304 for (int i = 0; i < nodeset->nodeNr; i++) {
1305 /* Serialize node.
1306 * XXX: xmlNodeDump() appends to xml_buffer. */
1307 /*if (-1 ==
1308 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1310 /* XXX: According LibXML documentation, this function does not return
1311 * meaningfull value yet */
1312 xmlSaveTree(save_ctx, nodeset->nodeTab[i]);
1313 if (-1 == xmlSaveFlush(save_ctx)) {
1314 isds_log_message(context,
1315 _("Could not serialize XML subtree"));
1316 err = IE_ERROR;
1317 goto leave;
1321 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1322 * even after xmlSaveFlush(). Thus close it here */
1323 xmlSaveClose(save_ctx); save_ctx = NULL;
1325 /* Store and detach buffer from xml_buffer */
1326 *buffer = xml_buffer->content;
1327 *length = xml_buffer->use;
1328 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1330 /* Shrink buffer */
1331 new_buffer = realloc(*buffer, *length);
1332 if (new_buffer) *buffer = new_buffer;
1334 leave:
1335 if (err) {
1336 zfree(*buffer);
1337 *length = 0;
1340 xmlSaveClose(save_ctx);
1341 xmlBufferFree(xml_buffer);
1342 return err;
1344 #endif
1347 /* Convert UTF-8 @string represantion of ISDS dbType to enum @type */
1348 static isds_error string2isds_DbType(xmlChar *string, isds_DbType *type) {
1349 if (!string || !type) return IE_INVAL;
1351 if (!xmlStrcmp(string, BAD_CAST "FO"))
1352 *type = DBTYPE_FO;
1353 else if (!xmlStrcmp(string, BAD_CAST "PFO"))
1354 *type = DBTYPE_PFO;
1355 else if (!xmlStrcmp(string, BAD_CAST "PFO_ADVOK"))
1356 *type = DBTYPE_PFO_ADVOK;
1357 else if (!xmlStrcmp(string, BAD_CAST "PFO_DANPOR"))
1358 *type = DBTYPE_PFO_DANPOR;
1359 else if (!xmlStrcmp(string, BAD_CAST "PFO_INSSPR"))
1360 *type = DBTYPE_PFO_INSSPR;
1361 else if (!xmlStrcmp(string, BAD_CAST "PO"))
1362 *type = DBTYPE_PO;
1363 else if (!xmlStrcmp(string, BAD_CAST "PO_ZAK"))
1364 *type = DBTYPE_PO_ZAK;
1365 else if (!xmlStrcmp(string, BAD_CAST "PO_REQ"))
1366 *type = DBTYPE_PO_REQ;
1367 else if (!xmlStrcmp(string, BAD_CAST "OVM"))
1368 *type = DBTYPE_OVM;
1369 else if (!xmlStrcmp(string, BAD_CAST "OVM_NOTAR"))
1370 *type = DBTYPE_OVM_NOTAR;
1371 else if (!xmlStrcmp(string, BAD_CAST "OVM_EXEKUT"))
1372 *type = DBTYPE_OVM_EXEKUT;
1373 else if (!xmlStrcmp(string, BAD_CAST "OVM_REQ"))
1374 *type = DBTYPE_OVM_REQ;
1375 else
1376 return IE_ENUM;
1377 return IE_SUCCESS;
1381 /* Convert ISDS dbType enum @type to UTF-8 string.
1382 * @Return pointer to static string, or NULL if unkwnow enum value */
1383 static const xmlChar *isds_DbType2string(const isds_DbType type) {
1384 switch(type) {
1385 case DBTYPE_FO: return(BAD_CAST "FO"); break;
1386 case DBTYPE_PFO: return(BAD_CAST "PFO"); break;
1387 case DBTYPE_PFO_ADVOK: return(BAD_CAST "PFO_ADVOK"); break;
1388 case DBTYPE_PFO_DANPOR: return(BAD_CAST "PFO_DANPOR"); break;
1389 case DBTYPE_PFO_INSSPR: return(BAD_CAST "PFO_INSSPR"); break;
1390 case DBTYPE_PO: return(BAD_CAST "PO"); break;
1391 case DBTYPE_PO_ZAK: return(BAD_CAST "PO_ZAK"); break;
1392 case DBTYPE_PO_REQ: return(BAD_CAST "PO_REQ"); break;
1393 case DBTYPE_OVM: return(BAD_CAST "OVM"); break;
1394 case DBTYPE_OVM_NOTAR: return(BAD_CAST "OVM_NOTAR"); break;
1395 case DBTYPE_OVM_EXEKUT: return(BAD_CAST "OVM_EXEKUT"); break;
1396 case DBTYPE_OVM_REQ: return(BAD_CAST "OVM_REQ"); break;
1397 default: return NULL; break;
1402 /* Convert UTF-8 @string represantion of ISDS userType to enum @type */
1403 static isds_error string2isds_UserType(xmlChar *string, isds_UserType *type) {
1404 if (!string || !type) return IE_INVAL;
1406 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
1407 *type = USERTYPE_PRIMARY;
1408 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
1409 *type = USERTYPE_ENTRUSTED;
1410 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
1411 *type = USERTYPE_ADMINISTRATOR;
1412 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
1413 *type = USERTYPE_OFFICIAL;
1414 else
1415 return IE_ENUM;
1416 return IE_SUCCESS;
1420 /* Convert ISDS userType enum @type to UTF-8 string.
1421 * @Return pointer to static string, or NULL if unkwnow enum value */
1422 static const xmlChar *isds_UserType2string(const isds_UserType type) {
1423 switch(type) {
1424 case USERTYPE_PRIMARY: return(BAD_CAST "PRIMARY_USER"); break;
1425 case USERTYPE_ENTRUSTED: return(BAD_CAST "ENTRUSTED_USER"); break;
1426 case USERTYPE_ADMINISTRATOR: return(BAD_CAST "ADMINISTRATOR"); break;
1427 case USERTYPE_OFFICIAL: return(BAD_CAST "OFFICIAL"); break;
1428 default: return NULL; break;
1433 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
1434 * @Return pointer to static string, or NULL if unkwnow enum value */
1435 static const xmlChar *isds_FileMetaType2string(const isds_FileMetaType type) {
1436 switch(type) {
1437 case FILEMETATYPE_MAIN: return(BAD_CAST "main"); break;
1438 case FILEMETATYPE_ENCLOSURE: return(BAD_CAST "enclosure"); break;
1439 case FILEMETATYPE_SIGNATURE: return(BAD_CAST "signature"); break;
1440 case FILEMETATYPE_META: return(BAD_CAST "meta"); break;
1441 default: return NULL; break;
1446 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
1447 * @Return IE_ENUM if @string is not valid enum member */
1448 static isds_error string2isds_FileMetaType(const xmlChar *string,
1449 isds_FileMetaType *type) {
1450 if (!string || !type) return IE_INVAL;
1452 if (!xmlStrcmp(string, BAD_CAST "main"))
1453 *type = FILEMETATYPE_MAIN;
1454 else if (!xmlStrcmp(string, BAD_CAST "enclosure"))
1455 *type = FILEMETATYPE_ENCLOSURE;
1456 else if (!xmlStrcmp(string, BAD_CAST "signature"))
1457 *type = FILEMETATYPE_SIGNATURE;
1458 else if (!xmlStrcmp(string, BAD_CAST "meta"))
1459 *type = FILEMETATYPE_META;
1460 else
1461 return IE_ENUM;
1462 return IE_SUCCESS;
1466 /* Convert UTF-8 @string to ISDS hash @algorithm.
1467 * @Return IE_ENUM if @string is not valid enum member */
1468 static isds_error string2isds_hash_algorithm(const xmlChar *string,
1469 isds_hash_algorithm *algorithm) {
1470 if (!string || !algorithm) return IE_INVAL;
1472 if (!xmlStrcmp(string, BAD_CAST "MD5"))
1473 *algorithm = HASH_ALGORITHM_MD5;
1474 else if (!xmlStrcmp(string, BAD_CAST "SHA-1"))
1475 *algorithm = HASH_ALGORITHM_SHA_1;
1476 else if (!xmlStrcmp(string, BAD_CAST "SHA-256"))
1477 *algorithm = HASH_ALGORITHM_SHA_256;
1478 else if (!xmlStrcmp(string, BAD_CAST "SHA-512"))
1479 *algorithm = HASH_ALGORITHM_SHA_512;
1480 else
1481 return IE_ENUM;
1482 return IE_SUCCESS;
1486 /* Convert UTF-8 @string represantion of ISO 8601 date to @time.
1487 * XXX: Not all ISO formats are supported */
1488 static isds_error datestring2tm(const xmlChar *string, struct tm *time) {
1489 char *offset;
1490 if (!string || !time) return IE_INVAL;
1492 /* xsd:date is ISO 8601 string, thus ASCII */
1493 offset = strptime((char*)string, "%Y-%m-%d", time);
1494 if (offset && *offset == '\0')
1495 return IE_SUCCESS;
1497 offset = strptime((char*)string, "%Y%m%d", time);
1498 if (offset && *offset == '\0')
1499 return IE_SUCCESS;
1501 offset = strptime((char*)string, "%Y-%j", time);
1502 if (offset && *offset == '\0')
1503 return IE_SUCCESS;
1505 return IE_NOTSUP;
1509 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
1510 static isds_error tm2datestring(const struct tm *time, xmlChar **string) {
1511 if (!time || !string) return IE_INVAL;
1513 if (-1 == isds_asprintf((char **) string, "%d-%02d-%02d",
1514 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
1515 return IE_ERROR;
1517 return IE_SUCCESS;
1521 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
1522 * respects the @time microseconds too. */
1523 static isds_error timeval2timestring(const struct timeval *time,
1524 xmlChar **string) {
1525 struct tm broken;
1527 if (!time || !string) return IE_INVAL;
1529 if (!gmtime_r(&time->tv_sec, &broken)) return IE_DATE;
1530 if (time->tv_usec < 0 || time->tv_usec > 999999) return IE_DATE;
1532 /* TODO: small negative year should be formated as "-0012". This is not
1533 * true for glibc "%04d". We should implement it.
1534 * TODO: What's type of time->tv_usec exactly? Unsigned? Absolute?
1535 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
1536 if (-1 == isds_asprintf((char **) string,
1537 "%04d-%02d-%02dT%02d:%02d:%02d.%06ld",
1538 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
1539 broken.tm_hour, broken.tm_min, broken.tm_sec,
1540 time->tv_usec))
1541 return IE_ERROR;
1543 return IE_SUCCESS;
1547 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
1548 * It respects microseconds too.
1549 * In case of error, @time will be freed. */
1550 static isds_error timestring2timeval(const xmlChar *string,
1551 struct timeval **time) {
1552 struct tm broken;
1553 char *offset, *delim, *endptr;
1554 char subseconds[7];
1555 int offset_hours, offset_minutes;
1556 int i;
1558 if (!time) return IE_INVAL;
1560 memset(&broken, 0, sizeof(broken));
1562 if (!*time) {
1563 *time = calloc(1, sizeof(**time));
1564 if (!*time) return IE_NOMEM;
1565 } else {
1566 memset(*time, 0, sizeof(**time));
1570 /* xsd:date is ISO 8601 string, thus ASCII */
1571 /*TODO: negative year */
1573 /* Parse date and time without subseconds and offset */
1574 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
1575 if (!offset) {
1576 free(*time); *time = NULL;
1577 return IE_DATE;
1580 /* Get subseconds */
1581 if (*offset == '.' ) {
1582 offset++;
1584 /* Copy first 6 digits, padd it with zeros.
1585 * XXX: It truncates longer number, no round.
1586 * Current server implementation uses only milisecond resolution. */
1587 /* TODO: isdigit() is locale sensitive */
1588 for (i = 0;
1589 i < sizeof(subseconds)/sizeof(char) - 1 && isdigit(*offset);
1590 i++, offset++) {
1591 subseconds[i] = *offset;
1593 for (; i < sizeof(subseconds)/sizeof(char) - 1; i++) {
1594 subseconds[i] = '0';
1596 subseconds[6] = '\0';
1598 /* Convert it into integer */
1599 (*time)->tv_usec = strtol(subseconds, &endptr, 10);
1600 if (*endptr != '\0' || (*time)->tv_usec == LONG_MIN ||
1601 (*time)->tv_usec == LONG_MAX) {
1602 free(*time); *time = NULL;
1603 return IE_DATE;
1606 /* move to the zone offset delimiter */
1607 delim = strchr(offset, '-');
1608 if (!delim)
1609 delim = strchr(offset, '+');
1610 offset = delim;
1613 /* Get zone offset */
1614 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
1615 * "" equals to "Z" and it means UTC zone. */
1616 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
1617 * colon separator */
1618 if (*offset == '-' || *offset == '+') {
1619 offset++;
1620 if (2 != sscanf(offset, "%2d:%2d", &offset_hours, &offset_minutes)) {
1621 free(*time); *time = NULL;
1622 return IE_DATE;
1624 broken.tm_hour -= offset_hours;
1625 broken.tm_min -= offset_minutes * ((offset_hours<0) ? -1 : 1);
1628 /* Convert to time_t */
1629 switch_tz_to_utc();
1630 (*time)->tv_sec = mktime(&broken);
1631 switch_tz_to_native();
1632 if ((*time)->tv_sec == (time_t) -1) {
1633 free(*time); *time = NULL;
1634 return IE_DATE;
1637 return IE_SUCCESS;
1641 /* Convert unsigned int into isds_message_status.
1642 * @context is session context
1643 * @number is pointer to number value. NULL will be treated as invalid value.
1644 * @status is automatically reallocated status
1645 * @return IE_SUCCESS, or error code and free status */
1646 static isds_error uint2isds_message_status(struct isds_ctx *context,
1647 const unsigned long int *number, isds_message_status **status) {
1648 if (!context) return IE_INVALID_CONTEXT;
1649 if (!status) return IE_INVAL;
1651 free(*status); *status = NULL;
1652 if (!number) return IE_INVAL;
1654 if (*number < 1 || *number > 10) {
1655 isds_printf_message(context, _("Invalid messsage status value: %lu"),
1656 *number);
1657 return IE_ENUM;
1660 *status = malloc(sizeof(**status));
1661 if (!*status) return IE_NOMEM;
1663 **status = 1 << *number;
1664 return IE_SUCCESS;
1668 /* Convert event description string into isds_event memebers type and
1669 * description
1670 * @string is raw event decsription starting with event prefix
1671 * @event is structure where to store type and stripped description to
1672 * @return standard error code, unkown prefix is not classified as an error. */
1673 static isds_error eventstring2event(const xmlChar *string,
1674 struct isds_event* event) {
1675 const xmlChar *known_prefixes[] = {
1676 BAD_CAST "EV1:",
1677 BAD_CAST "EV2:",
1678 BAD_CAST "EV3:",
1679 BAD_CAST "EV4:"
1681 const isds_event_type types[] = {
1682 EVENT_ACCEPTED_BY_RECIPIENT,
1683 EVENT_ACCEPTED_BY_FICTION,
1684 EVENT_UNDELIVERABLE,
1685 EVENT_COMMERCIAL_ACCEPTED
1687 unsigned int index;
1688 size_t length;
1690 if (!string || !event) return IE_INVAL;
1692 if (!event->type) {
1693 event->type = malloc(sizeof(*event->type));
1694 if (!(event->type)) return IE_NOMEM;
1696 zfree(event->description);
1698 for (index = 0; index < sizeof(known_prefixes)/sizeof(known_prefixes[0]);
1699 index++) {
1700 length = xmlUTF8Strlen(known_prefixes[index]);
1702 if (!xmlStrncmp(string, known_prefixes[index], length)) {
1703 /* Prefix is known */
1704 *event->type = types[index];
1706 /* Strip prefix from description and spaces */
1707 /* TODO: Recognize all wite spaces from UCS blank class and
1708 * operate on UTF-8 chars. */
1709 for (; string[length] != '\0' && string[length] == ' '; length++);
1710 event->description = strdup((char *) (string + length));
1711 if (!(event->description)) return IE_NOMEM;
1713 return IE_SUCCESS;
1717 /* Unknown event prefix.
1718 * XSD allows any string */
1719 char *string_locale = utf82locale((char *) string);
1720 isds_log(ILF_ISDS, ILL_WARNING,
1721 _("Uknown delivery info event prefix: %s\n"), string_locale);
1722 free(string_locale);
1724 *event->type = EVENT_UKNOWN;
1725 event->description = strdup((char *) string);
1726 if (!(event->description)) return IE_NOMEM;
1728 return IE_SUCCESS;
1732 /* Following EXTRACT_* macros expects @result, @xpath_ctx, @err, @context
1733 * and leave lable */
1734 #define EXTRACT_STRING(element, string) { \
1735 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
1736 if (!result) { \
1737 err = IE_ERROR; \
1738 goto leave; \
1740 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
1741 if (result->nodesetval->nodeNr > 1) { \
1742 isds_printf_message(context, _("Multiple %s element"), element); \
1743 err = IE_ERROR; \
1744 goto leave; \
1746 (string) = (char *) \
1747 xmlXPathCastNodeSetToString(result->nodesetval); \
1748 if (!(string)) { \
1749 err = IE_ERROR; \
1750 goto leave; \
1755 #define EXTRACT_BOOLEAN(element, booleanPtr) \
1757 char *string = NULL; \
1758 EXTRACT_STRING(element, string); \
1760 if (string) { \
1761 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
1762 if (!(booleanPtr)) { \
1763 free(string); \
1764 err = IE_NOMEM; \
1765 goto leave; \
1768 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
1769 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
1770 *(booleanPtr) = 1; \
1771 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
1772 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
1773 *(booleanPtr) = 0; \
1774 else { \
1775 char *string_locale = utf82locale((char*)string); \
1776 isds_printf_message(context, \
1777 _("%s value is not valid boolean: "), \
1778 element, string_locale); \
1779 free(string_locale); \
1780 free(string); \
1781 err = IE_ERROR; \
1782 goto leave; \
1785 free(string); \
1789 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
1791 char *string = NULL; \
1792 EXTRACT_STRING(element, string); \
1793 if (string) { \
1794 long int number; \
1795 char *endptr; \
1797 number = strtol((char*)string, &endptr, 10); \
1799 if (*endptr != '\0') { \
1800 char *string_locale = utf82locale((char *)string); \
1801 isds_printf_message(context, \
1802 _("%s is not valid integer: %s"), \
1803 element, string_locale); \
1804 free(string_locale); \
1805 free(string); \
1806 err = IE_ISDS; \
1807 goto leave; \
1810 if (number == LONG_MIN || number == LONG_MAX) { \
1811 char *string_locale = utf82locale((char *)string); \
1812 isds_printf_message(context, \
1813 _("%s value out of range of long int: %s"), \
1814 element, string_locale); \
1815 free(string_locale); \
1816 free(string); \
1817 err = IE_ERROR; \
1818 goto leave; \
1821 free(string); string = NULL; \
1823 if (!(preallocated)) { \
1824 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
1825 if (!(longintPtr)) { \
1826 err = IE_NOMEM; \
1827 goto leave; \
1830 *(longintPtr) = number; \
1834 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
1836 char *string = NULL; \
1837 EXTRACT_STRING(element, string); \
1838 if (string) { \
1839 long int number; \
1840 char *endptr; \
1842 number = strtol((char*)string, &endptr, 10); \
1844 if (*endptr != '\0') { \
1845 char *string_locale = utf82locale((char *)string); \
1846 isds_printf_message(context, \
1847 _("%s is not valid integer: %s"), \
1848 element, string_locale); \
1849 free(string_locale); \
1850 free(string); \
1851 err = IE_ISDS; \
1852 goto leave; \
1855 if (number == LONG_MIN || number == LONG_MAX) { \
1856 char *string_locale = utf82locale((char *)string); \
1857 isds_printf_message(context, \
1858 _("%s value out of range of long int: %s"), \
1859 element, string_locale); \
1860 free(string_locale); \
1861 free(string); \
1862 err = IE_ERROR; \
1863 goto leave; \
1866 free(string); string = NULL; \
1867 if (number < 0) { \
1868 isds_printf_message(context, \
1869 _("%s value is negative: %ld"), element, number); \
1870 err = IE_ERROR; \
1871 goto leave; \
1874 if (!(preallocated)) { \
1875 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
1876 if (!(ulongintPtr)) { \
1877 err = IE_NOMEM; \
1878 goto leave; \
1881 *(ulongintPtr) = number; \
1885 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
1886 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
1887 NULL); \
1888 if ((required) && (!string)) { \
1889 char *attribute_locale = utf82locale(attribute); \
1890 char *element_locale = utf82locale((char *)xpath_ctx->node->name); \
1891 isds_printf_message(context, \
1892 _("Could not extract required %s attribute value from " \
1893 "%s element"), attribute_locale, element_locale); \
1894 free(element_locale); \
1895 free(attribute_locale); \
1896 err = IE_ERROR; \
1897 goto leave; \
1902 #define INSERT_STRING(parent, element, string) \
1904 node = xmlNewTextChild(parent, NULL, BAD_CAST (element), \
1905 (xmlChar *) (string)); \
1906 if (!node) { \
1907 isds_printf_message(context, \
1908 _("Could not add %s child to %s element"), \
1909 element, (parent)->name); \
1910 err = IE_ERROR; \
1911 goto leave; \
1915 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
1917 if (boolean) { INSERT_STRING(parent, element, "true"); } \
1918 else { INSERT_STRING(parent, element, "false"); } \
1921 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
1923 if (booleanPtr) { \
1924 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
1925 } else { \
1926 INSERT_STRING(parent, element, NULL); \
1930 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
1931 if ((longintPtr)) { \
1932 /* FIXME: locale sensitive */ \
1933 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
1934 err = IE_NOMEM; \
1935 goto leave; \
1937 INSERT_STRING(parent, element, buffer) \
1938 free(buffer); (buffer) = NULL; \
1939 } else { INSERT_STRING(parent, element, NULL) } \
1942 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
1943 if ((ulongintPtr)) { \
1944 /* FIXME: locale sensitive */ \
1945 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
1946 err = IE_NOMEM; \
1947 goto leave; \
1949 INSERT_STRING(parent, element, buffer) \
1950 free(buffer); (buffer) = NULL; \
1951 } else { INSERT_STRING(parent, element, NULL) } \
1954 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
1956 /* FIXME: locale sensitive */ \
1957 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
1958 err = IE_NOMEM; \
1959 goto leave; \
1961 INSERT_STRING(parent, element, buffer) \
1962 free(buffer); (buffer) = NULL; \
1965 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
1967 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
1968 (xmlChar *) (string)); \
1969 if (!attribute_node) { \
1970 isds_printf_message(context, _("Could not add %s " \
1971 "attribute to %s element"), \
1972 (attribute), (parent)->name); \
1973 err = IE_ERROR; \
1974 goto leave; \
1978 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
1979 if (string) { \
1980 int length = xmlUTF8Strlen((xmlChar *) (string)); \
1981 if (length > (maximum)) { \
1982 isds_printf_message(context, \
1983 _("%s has more than %d characters"), (name), (maximum)); \
1984 err = IE_2BIG; \
1985 goto leave; \
1987 if (length < (minimum)) { \
1988 isds_printf_message(context, \
1989 _("%s has less than %d characters"), (name), (minimum)); \
1990 err = IE_2SMALL; \
1991 goto leave; \
1996 #define INSERT_ELEMENT(child, parent, element) \
1998 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
1999 if (!(child)) { \
2000 isds_printf_message(context, \
2001 _("Could not add %s child to %s element"), \
2002 (element), (parent)->name); \
2003 err = IE_ERROR; \
2004 goto leave; \
2009 /* Find child element by name in given XPath context and switch context onto
2010 * it. The child must be uniq and must exist. Otherwise failes.
2011 * @context is ISDS context
2012 * @child is child element name
2013 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2014 * into it child. In error case, the @xpath_ctx keeps original value. */
2015 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
2016 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
2017 isds_error err = IE_SUCCESS;
2018 xmlXPathObjectPtr result = NULL;
2020 if (!context) return IE_INVALID_CONTEXT;
2021 if (!child || !xpath_ctx) return IE_INVAL;
2023 /* Find child */
2024 result = xmlXPathEvalExpression(child, xpath_ctx);
2025 if (!result) {
2026 err = IE_XML;
2027 goto leave;
2030 /* No match */
2031 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2032 char *parent_locale = utf82locale((char*) xpath_ctx->node->name);
2033 char *child_locale = utf82locale((char*) child);
2034 isds_printf_message(context,
2035 _("%s element does not contain %s child"),
2036 parent_locale, child_locale);
2037 free(child_locale);
2038 free(parent_locale);
2039 err = IE_NOEXIST;
2040 goto leave;
2043 /* More matches */
2044 if (result->nodesetval->nodeNr > 1) {
2045 char *parent_locale = utf82locale((char*) xpath_ctx->node->name);
2046 char *child_locale = utf82locale((char*) child);
2047 isds_printf_message(context,
2048 _("%s element contains multiple %s childs"),
2049 parent_locale, child_locale);
2050 free(child_locale);
2051 free(parent_locale);
2052 err = IE_NOTUNIQ;
2053 goto leave;
2056 /* Switch context */
2057 xpath_ctx->node = result->nodesetval->nodeTab[0];
2059 leave:
2060 xmlXPathFreeObject(result);
2061 return err;
2066 /* Find and convert XSD:gPersonName group in current node into structure
2067 * @context is ISDS context
2068 * @personName is automically reallocated person name structure. If no member
2069 * value is found, will be freed.
2070 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2071 * elements
2072 * In case of error @personName will be freed. */
2073 static isds_error extract_gPersonName(struct isds_ctx *context,
2074 struct isds_PersonName **personName, xmlXPathContextPtr xpath_ctx) {
2075 isds_error err = IE_SUCCESS;
2076 xmlXPathObjectPtr result = NULL;
2078 if (!context) return IE_INVALID_CONTEXT;
2079 if (!personName) return IE_INVAL;
2080 isds_PersonName_free(personName);
2081 if (!xpath_ctx) return IE_INVAL;
2084 *personName = calloc(1, sizeof(**personName));
2085 if (!*personName) {
2086 err = IE_NOMEM;
2087 goto leave;
2090 EXTRACT_STRING("isds:pnFirstName", (*personName)->pnFirstName);
2091 EXTRACT_STRING("isds:pnMiddleName", (*personName)->pnMiddleName);
2092 EXTRACT_STRING("isds:pnLastName", (*personName)->pnLastName);
2093 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName)->pnLastNameAtBirth);
2095 if (!(*personName)->pnFirstName && !(*personName)->pnMiddleName &&
2096 !(*personName)->pnLastName && !(*personName)->pnLastNameAtBirth)
2097 isds_PersonName_free(personName);
2099 leave:
2100 if (err) isds_PersonName_free(personName);
2101 xmlXPathFreeObject(result);
2102 return err;
2106 /* Find and convert XSD:gAddress group in current node into structure
2107 * @context is ISDS context
2108 * @address is automically reallocated address structure. If no member
2109 * value is found, will be freed.
2110 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2111 * elements
2112 * In case of error @address will be freed. */
2113 static isds_error extract_gAddress(struct isds_ctx *context,
2114 struct isds_Address **address, xmlXPathContextPtr xpath_ctx) {
2115 isds_error err = IE_SUCCESS;
2116 xmlXPathObjectPtr result = NULL;
2118 if (!context) return IE_INVALID_CONTEXT;
2119 if (!address) return IE_INVAL;
2120 isds_Address_free(address);
2121 if (!xpath_ctx) return IE_INVAL;
2124 *address = calloc(1, sizeof(**address));
2125 if (!*address) {
2126 err = IE_NOMEM;
2127 goto leave;
2130 EXTRACT_STRING("isds:adCity", (*address)->adCity);
2131 EXTRACT_STRING("isds:adStreet", (*address)->adStreet);
2132 EXTRACT_STRING("isds:adNumberInStreet", (*address)->adNumberInStreet);
2133 EXTRACT_STRING("isds:adNumberInMunicipality",
2134 (*address)->adNumberInMunicipality);
2135 EXTRACT_STRING("isds:adZipCode", (*address)->adZipCode);
2136 EXTRACT_STRING("isds:adState", (*address)->adState);
2138 if (!(*address)->adCity && !(*address)->adStreet &&
2139 !(*address)->adNumberInStreet &&
2140 !(*address)->adNumberInMunicipality &&
2141 !(*address)->adZipCode && !(*address)->adState)
2142 isds_Address_free(address);
2144 leave:
2145 if (err) isds_Address_free(address);
2146 xmlXPathFreeObject(result);
2147 return err;
2151 /* Find and convert isds:biDate element in current node into structure
2152 * @context is ISDS context
2153 * @biDate is automically reallocated birth date structure. If no member
2154 * value is found, will be freed.
2155 * @xpath_ctx is XPath context with current node as parent for isds:biDate
2156 * element
2157 * In case of error @biDate will be freed. */
2158 static isds_error extract_BiDate(struct isds_ctx *context,
2159 struct tm **biDate, xmlXPathContextPtr xpath_ctx) {
2160 isds_error err = IE_SUCCESS;
2161 xmlXPathObjectPtr result = NULL;
2162 char *string = NULL;
2164 if (!context) return IE_INVALID_CONTEXT;
2165 if (!biDate) return IE_INVAL;
2166 zfree(*biDate);
2167 if (!xpath_ctx) return IE_INVAL;
2169 EXTRACT_STRING("isds:biDate", string);
2170 if (string) {
2171 *biDate = calloc(1, sizeof(**biDate));
2172 if (!*biDate) {
2173 err = IE_NOMEM;
2174 goto leave;
2176 err = datestring2tm((xmlChar *)string, *biDate);
2177 if (err) {
2178 if (err == IE_NOTSUP) {
2179 err = IE_ISDS;
2180 char *string_locale = utf82locale(string);
2181 isds_printf_message(context,
2182 _("Invalid isds:biDate value: %s"), string_locale);
2183 free(string_locale);
2185 goto leave;
2189 leave:
2190 if (err) zfree(*biDate);
2191 free(string);
2192 xmlXPathFreeObject(result);
2193 return err;
2197 /* Convert isds:dBOwnerInfo XML tree into structure
2198 * @context is ISDS context
2199 * @db_owner_info is automically reallocated box owner info structure
2200 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
2201 * In case of error @db_owner_info will be freed. */
2202 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
2203 struct isds_DbOwnerInfo **db_owner_info,
2204 xmlXPathContextPtr xpath_ctx) {
2205 isds_error err = IE_SUCCESS;
2206 xmlXPathObjectPtr result = NULL;
2207 char *string = NULL;
2209 if (!context) return IE_INVALID_CONTEXT;
2210 if (!db_owner_info) return IE_INVAL;
2211 isds_DbOwnerInfo_free(db_owner_info);
2212 if (!xpath_ctx) return IE_INVAL;
2215 *db_owner_info = calloc(1, sizeof(**db_owner_info));
2216 if (!*db_owner_info) {
2217 err = IE_NOMEM;
2218 goto leave;
2221 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
2223 EXTRACT_STRING("isds:dbType", string);
2224 if (string) {
2225 (*db_owner_info)->dbType =
2226 calloc(1, sizeof(*((*db_owner_info)->dbType)));
2227 if (!(*db_owner_info)->dbType) {
2228 err = IE_NOMEM;
2229 goto leave;
2231 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
2232 if (err) {
2233 zfree((*db_owner_info)->dbType);
2234 if (err == IE_ENUM) {
2235 err = IE_ISDS;
2236 char *string_locale = utf82locale(string);
2237 isds_printf_message(context, _("Unknown isds:dbType: %s"),
2238 string_locale);
2239 free(string_locale);
2241 goto leave;
2243 zfree(string);
2246 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
2248 err = extract_gPersonName(context, &(*db_owner_info)->personName,
2249 xpath_ctx);
2250 if (err) goto leave;
2252 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
2254 (*db_owner_info)->birthInfo =
2255 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
2256 if (!(*db_owner_info)->birthInfo) {
2257 err = IE_NOMEM;
2258 goto leave;
2260 err = extract_BiDate(context, &(*db_owner_info)->birthInfo->biDate,
2261 xpath_ctx);
2262 if (err) goto leave;
2263 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
2264 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
2265 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
2266 if (!(*db_owner_info)->birthInfo->biDate &&
2267 !(*db_owner_info)->birthInfo->biCity &&
2268 !(*db_owner_info)->birthInfo->biCounty &&
2269 !(*db_owner_info)->birthInfo->biState)
2270 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
2272 err = extract_gAddress(context, &(*db_owner_info)->address, xpath_ctx);
2273 if (err) goto leave;
2275 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
2276 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
2277 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
2278 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
2279 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
2281 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
2283 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
2284 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
2285 (*db_owner_info)->dbOpenAddressing);
2287 leave:
2288 if (err) isds_DbOwnerInfo_free(db_owner_info);
2289 free(string);
2290 xmlXPathFreeObject(result);
2291 return err;
2295 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
2296 * @context is sesstion context
2297 * @owner is libsids structure with box description
2298 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
2299 static isds_error insert_DbOwnerInfo(struct isds_ctx *context,
2300 const struct isds_DbOwnerInfo *owner, xmlNodePtr db_owner_info) {
2302 isds_error err = IE_SUCCESS;
2303 xmlNodePtr node;
2304 xmlChar *string = NULL;
2306 if (!context) return IE_INVALID_CONTEXT;
2307 if (!owner || !db_owner_info) return IE_INVAL;
2310 /* Build XSD:tDbOwnerInfo */
2311 CHECK_FOR_STRING_LENGTH(owner->dbID, 0, 7, "dbID")
2312 INSERT_STRING(db_owner_info, "dbID", owner->dbID);
2314 /* dbType */
2315 if (owner->dbType) {
2316 const xmlChar *type_string = isds_DbType2string(*(owner->dbType));
2317 if (!type_string) {
2318 isds_printf_message(context, _("Invalid dbType value: %d"),
2319 *(owner->dbType));
2320 err = IE_ENUM;
2321 goto leave;
2323 INSERT_STRING(db_owner_info, "dbType", type_string);
2325 INSERT_STRING(db_owner_info, "ic", owner->ic);
2326 if (owner->personName) {
2327 INSERT_STRING(db_owner_info, "pnFirstName",
2328 owner->personName->pnFirstName);
2329 INSERT_STRING(db_owner_info, "pnMiddleName",
2330 owner->personName->pnMiddleName);
2331 INSERT_STRING(db_owner_info, "pnLastName",
2332 owner->personName->pnLastName);
2333 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
2334 owner->personName->pnLastNameAtBirth);
2336 INSERT_STRING(db_owner_info, "firmName", owner->firmName);
2337 if (owner->birthInfo) {
2338 if (owner->birthInfo->biDate) {
2339 if (!tm2datestring(owner->birthInfo->biDate, &string))
2340 INSERT_STRING(db_owner_info, "biDate", string);
2341 free(string); string = NULL;
2343 INSERT_STRING(db_owner_info, "biCity", owner->birthInfo->biCity);
2344 INSERT_STRING(db_owner_info, "biCounty", owner->birthInfo->biCounty);
2345 INSERT_STRING(db_owner_info, "biState", owner->birthInfo->biState);
2347 if (owner->address) {
2348 INSERT_STRING(db_owner_info, "adCity", owner->address->adCity);
2349 INSERT_STRING(db_owner_info, "adStreet", owner->address->adStreet);
2350 INSERT_STRING(db_owner_info, "adNumberInStreet",
2351 owner->address->adNumberInStreet);
2352 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
2353 owner->address->adNumberInMunicipality);
2354 INSERT_STRING(db_owner_info, "adZipCode", owner->address->adZipCode);
2355 INSERT_STRING(db_owner_info, "adState", owner->address->adState);
2357 INSERT_STRING(db_owner_info, "nationality", owner->nationality);
2358 INSERT_STRING(db_owner_info, "email", owner->email);
2359 INSERT_STRING(db_owner_info, "telNumber", owner->telNumber);
2361 CHECK_FOR_STRING_LENGTH(owner->identifier, 0, 20, "identifier")
2362 INSERT_STRING(db_owner_info, "identifier", owner->identifier);
2364 CHECK_FOR_STRING_LENGTH(owner->registryCode, 0, 5, "registryCode")
2365 INSERT_STRING(db_owner_info, "registryCode", owner->registryCode);
2367 INSERT_LONGINT(db_owner_info, "dbState", owner->dbState, string);
2369 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", owner->dbEffectiveOVM);
2370 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
2371 owner->dbOpenAddressing);
2373 leave:
2374 free(string);
2375 return err;
2379 /* Convert XSD:tDbUserInfo XML tree into structure
2380 * @context is ISDS context
2381 * @db_user_info is automically reallocated user info structure
2382 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
2383 * In case of error @db_user_info will be freed. */
2384 static isds_error extract_DbUserInfo(struct isds_ctx *context,
2385 struct isds_DbUserInfo **db_user_info, xmlXPathContextPtr xpath_ctx) {
2386 isds_error err = IE_SUCCESS;
2387 xmlXPathObjectPtr result = NULL;
2388 char *string = NULL;
2390 if (!context) return IE_INVALID_CONTEXT;
2391 if (!db_user_info) return IE_INVAL;
2392 isds_DbUserInfo_free(db_user_info);
2393 if (!xpath_ctx) return IE_INVAL;
2396 *db_user_info = calloc(1, sizeof(**db_user_info));
2397 if (!*db_user_info) {
2398 err = IE_NOMEM;
2399 goto leave;
2402 EXTRACT_STRING("isds:userID", (*db_user_info)->userID);
2404 EXTRACT_STRING("isds:userType", string);
2405 if (string) {
2406 (*db_user_info)->userType =
2407 calloc(1, sizeof(*((*db_user_info)->userType)));
2408 if (!(*db_user_info)->userType) {
2409 err = IE_NOMEM;
2410 goto leave;
2412 err = string2isds_UserType((xmlChar *)string,
2413 (*db_user_info)->userType);
2414 if (err) {
2415 zfree((*db_user_info)->userType);
2416 if (err == IE_ENUM) {
2417 err = IE_ISDS;
2418 char *string_locale = utf82locale(string);
2419 isds_printf_message(context, _("Unknown isds:userType: %s"),
2420 string_locale);
2421 free(string_locale);
2423 goto leave;
2425 zfree(string);
2428 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info)->userPrivils, 0);
2430 (*db_user_info)->personName =
2431 calloc(1, sizeof(*((*db_user_info)->personName)));
2432 if (!(*db_user_info)->personName) {
2433 err = IE_NOMEM;
2434 goto leave;
2437 err = extract_gPersonName(context, &(*db_user_info)->personName,
2438 xpath_ctx);
2439 if (err) goto leave;
2441 err = extract_gAddress(context, &(*db_user_info)->address, xpath_ctx);
2442 if (err) goto leave;
2444 err = extract_BiDate(context, &(*db_user_info)->biDate, xpath_ctx);
2445 if (err) goto leave;
2447 EXTRACT_STRING("isds:ic", (*db_user_info)->ic);
2448 EXTRACT_STRING("isds:firmName", (*db_user_info)->firmName);
2450 EXTRACT_STRING("isds:caStreet", (*db_user_info)->caStreet);
2451 EXTRACT_STRING("isds:caCity", (*db_user_info)->caCity);
2452 EXTRACT_STRING("isds:caZipCode", (*db_user_info)->caZipCode);
2454 leave:
2455 if (err) isds_DbUserInfo_free(db_user_info);
2456 free(string);
2457 xmlXPathFreeObject(result);
2458 return err;
2462 /* Insert struct isds_DbUserInfo data (user description) into XML tree
2463 * @context is sesstion context
2464 * @user is libsids structure with user description
2465 * @db_user_info is XML element of XSD:tDbUserInfo */
2466 static isds_error insert_DbUserInfo(struct isds_ctx *context,
2467 const struct isds_DbUserInfo *user, xmlNodePtr db_user_info) {
2469 isds_error err = IE_SUCCESS;
2470 xmlNodePtr node;
2471 xmlChar *string = NULL;
2473 if (!context) return IE_INVALID_CONTEXT;
2474 if (!user || !db_user_info) return IE_INVAL;
2476 /* Build XSD:tDbUserInfo */
2477 if (user->personName) {
2478 INSERT_STRING(db_user_info, "pnFirstName",
2479 user->personName->pnFirstName);
2480 INSERT_STRING(db_user_info, "pnMiddleName",
2481 user->personName->pnMiddleName);
2482 INSERT_STRING(db_user_info, "pnLastName",
2483 user->personName->pnLastName);
2484 INSERT_STRING(db_user_info, "pnLastNameAtBirth",
2485 user->personName->pnLastNameAtBirth);
2487 if (user->address) {
2488 INSERT_STRING(db_user_info, "adCity", user->address->adCity);
2489 INSERT_STRING(db_user_info, "adStreet", user->address->adStreet);
2490 INSERT_STRING(db_user_info, "adNumberInStreet",
2491 user->address->adNumberInStreet);
2492 INSERT_STRING(db_user_info, "adNumberInMunicipality",
2493 user->address->adNumberInMunicipality);
2494 INSERT_STRING(db_user_info, "adZipCode", user->address->adZipCode);
2495 INSERT_STRING(db_user_info, "adState", user->address->adState);
2497 if (user->biDate) {
2498 if (!tm2datestring(user->biDate, &string))
2499 INSERT_STRING(db_user_info, "biDate", string);
2500 zfree(string);
2502 CHECK_FOR_STRING_LENGTH(user->userID, 6, 12, "userID");
2503 INSERT_STRING(db_user_info, "userID", user->userID);
2505 /* userType */
2506 if (user->userType) {
2507 const xmlChar *type_string = isds_UserType2string(*(user->userType));
2508 if (!type_string) {
2509 isds_printf_message(context, _("Invalid userType value: %d"),
2510 *(user->userType));
2511 err = IE_ENUM;
2512 goto leave;
2514 INSERT_STRING(db_user_info, "userType", type_string);
2517 INSERT_LONGINT(db_user_info, "userPrivils", user->userPrivils, string);
2518 CHECK_FOR_STRING_LENGTH(user->ic, 0, 8, "ic")
2519 INSERT_STRING(db_user_info, "ic", user->ic);
2520 CHECK_FOR_STRING_LENGTH(user->firmName, 0, 100, "firmName")
2521 INSERT_STRING(db_user_info, "firmName", user->firmName);
2522 INSERT_STRING(db_user_info, "caStreet", user->caStreet);
2523 INSERT_STRING(db_user_info, "caCity", user->caCity);
2524 INSERT_STRING(db_user_info, "caZipCode", user->caZipCode);
2526 leave:
2527 free(string);
2528 return err;
2532 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
2533 * isds_envelope structure. The envelope is automatically allocated but not
2534 * reallocated. The date are just appended into envelope structure.
2535 * @context is ISDS context
2536 * @envelope is automically allocated message envelope structure
2537 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
2538 * In case of error @envelope will be freed. */
2539 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
2540 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2541 isds_error err = IE_SUCCESS;
2542 xmlXPathObjectPtr result = NULL;
2544 if (!context) return IE_INVALID_CONTEXT;
2545 if (!envelope) return IE_INVAL;
2546 if (!xpath_ctx) return IE_INVAL;
2549 if (!*envelope) {
2550 /* Allocate envelope */
2551 *envelope = calloc(1, sizeof(**envelope));
2552 if (!*envelope) {
2553 err = IE_NOMEM;
2554 goto leave;
2556 } else {
2557 /* Else free former data */
2558 zfree((*envelope)->dmSenderOrgUnit);
2559 zfree((*envelope)->dmSenderOrgUnitNum);
2560 zfree((*envelope)->dbIDRecipient);
2561 zfree((*envelope)->dmRecipientOrgUnit);
2562 zfree((*envelope)->dmSenderOrgUnitNum);
2563 zfree((*envelope)->dmToHands);
2564 zfree((*envelope)->dmAnnotation);
2565 zfree((*envelope)->dmRecipientRefNumber);
2566 zfree((*envelope)->dmSenderRefNumber);
2567 zfree((*envelope)->dmRecipientIdent);
2568 zfree((*envelope)->dmSenderIdent);
2569 zfree((*envelope)->dmLegalTitleLaw);
2570 zfree((*envelope)->dmLegalTitleYear);
2571 zfree((*envelope)->dmLegalTitleSect);
2572 zfree((*envelope)->dmLegalTitlePar);
2573 zfree((*envelope)->dmLegalTitlePoint);
2574 zfree((*envelope)->dmPersonalDelivery);
2575 zfree((*envelope)->dmAllowSubstDelivery);
2578 /* Extract envelope elements added by sender or ISDS
2579 * (XSD: gMessageEnvelopeSub type) */
2580 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
2581 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
2582 (*envelope)->dmSenderOrgUnitNum, 0);
2583 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
2584 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
2585 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
2586 (*envelope)->dmSenderOrgUnitNum, 0);
2587 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
2588 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
2589 EXTRACT_STRING("isds:dmRecipientRefNumber",
2590 (*envelope)->dmRecipientRefNumber);
2591 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
2592 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
2593 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
2595 /* Extract envelope elements regarding law refference */
2596 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
2597 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
2598 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
2599 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
2600 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
2602 /* Extract envelope other elements */
2603 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
2604 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
2605 (*envelope)->dmAllowSubstDelivery);
2607 leave:
2608 if (err) isds_envelope_free(envelope);
2609 xmlXPathFreeObject(result);
2610 return err;
2615 /* Convert XSD gMessageEnvelope group of elements from XML tree into
2616 * isds_envelope structure. The envelope is automatically allocated but not
2617 * reallocated. The date are just appended into envelope structure.
2618 * @context is ISDS context
2619 * @envelope is automically allocated message envelope structure
2620 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
2621 * In case of error @envelope will be freed. */
2622 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
2623 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2624 isds_error err = IE_SUCCESS;
2625 xmlXPathObjectPtr result = NULL;
2627 if (!context) return IE_INVALID_CONTEXT;
2628 if (!envelope) return IE_INVAL;
2629 if (!xpath_ctx) return IE_INVAL;
2632 if (!*envelope) {
2633 /* Allocate envelope */
2634 *envelope = calloc(1, sizeof(**envelope));
2635 if (!*envelope) {
2636 err = IE_NOMEM;
2637 goto leave;
2639 } else {
2640 /* Else free former data */
2641 zfree((*envelope)->dmID);
2642 zfree((*envelope)->dbIDSender);
2643 zfree((*envelope)->dmSender);
2644 zfree((*envelope)->dmSenderAddress);
2645 zfree((*envelope)->dmSenderType);
2646 zfree((*envelope)->dmRecipient);
2647 zfree((*envelope)->dmRecipientAddress);
2648 zfree((*envelope)->dmAmbiguousRecipient);
2651 /* Extract envelope elements added by ISDS
2652 * (XSD: gMessageEnvelope type) */
2653 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
2654 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
2655 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
2656 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
2657 /* XML Schema does not guaratee enumratation. It's plain xs:int. */
2658 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
2659 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
2660 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
2661 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
2662 (*envelope)->dmAmbiguousRecipient);
2664 /* Extract envelope elements added by sender and ISDS
2665 * (XSD: gMessageEnvelope type) */
2666 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
2667 if (err) goto leave;
2669 leave:
2670 if (err) isds_envelope_free(envelope);
2671 xmlXPathFreeObject(result);
2672 return err;
2676 /* Convert other envelope elements from XML tree into isds_envelope structure:
2677 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
2678 * The envelope is automatically allocated but not reallocated.
2679 * The data are just appended into envelope structure.
2680 * @context is ISDS context
2681 * @envelope is automically allocated message envelope structure
2682 * @xpath_ctx is XPath context with current node as parent desired elements
2683 * In case of error @envelope will be freed. */
2684 static isds_error append_status_size_times(struct isds_ctx *context,
2685 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2686 isds_error err = IE_SUCCESS;
2687 xmlXPathObjectPtr result = NULL;
2688 char *string = NULL;
2689 unsigned long int *unumber = NULL;
2691 if (!context) return IE_INVALID_CONTEXT;
2692 if (!envelope) return IE_INVAL;
2693 if (!xpath_ctx) return IE_INVAL;
2696 if (!*envelope) {
2697 /* Allocate new */
2698 *envelope = calloc(1, sizeof(**envelope));
2699 if (!*envelope) {
2700 err = IE_NOMEM;
2701 goto leave;
2703 } else {
2704 /* Free old data */
2705 zfree((*envelope)->dmMessageStatus);
2706 zfree((*envelope)->dmAttachmentSize);
2707 zfree((*envelope)->dmDeliveryTime);
2708 zfree((*envelope)->dmAcceptanceTime);
2712 /* dmMessageStatus element is mandatory */
2713 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
2714 if (!unumber) {
2715 isds_log_message(context,
2716 _("Missing mandatory sisds:dmMessageStatus integer"));
2717 err = IE_ISDS;
2718 goto leave;
2720 err = uint2isds_message_status(context, unumber,
2721 &((*envelope)->dmMessageStatus));
2722 if (err) {
2723 if (err == IE_ENUM) err = IE_ISDS;
2724 goto leave;
2726 free(unumber); unumber = NULL;
2728 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
2731 EXTRACT_STRING("sisds:dmDeliveryTime", string);
2732 if (string) {
2733 err = timestring2timeval((xmlChar *) string,
2734 &((*envelope)->dmDeliveryTime));
2735 if (err) {
2736 char *string_locale = utf82locale(string);
2737 if (err == IE_DATE) err = IE_ISDS;
2738 isds_printf_message(context,
2739 _("Could not convert dmDeliveryTime as ISO time: %s"),
2740 string_locale);
2741 free(string_locale);
2742 goto leave;
2744 zfree(string);
2747 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
2748 if (string) {
2749 err = timestring2timeval((xmlChar *) string,
2750 &((*envelope)->dmAcceptanceTime));
2751 if (err) {
2752 char *string_locale = utf82locale(string);
2753 if (err == IE_DATE) err = IE_ISDS;
2754 isds_printf_message(context,
2755 _("Could not convert dmAcceptanceTime as ISO time: %s"),
2756 string_locale);
2757 free(string_locale);
2758 goto leave;
2760 zfree(string);
2763 leave:
2764 if (err) isds_envelope_free(envelope);
2765 free(unumber);
2766 free(string);
2767 xmlXPathFreeObject(result);
2768 return err;
2772 /* Convert message type attribute of current element into isds_envelope
2773 * structure.
2774 * TODO: This function can be incorporated into append_status_size_times() as
2775 * they are called always together.
2776 * The envelope is automatically allocated but not reallocated.
2777 * The data are just appended into envelope structure.
2778 * @context is ISDS context
2779 * @envelope is automically allocated message envelope structure
2780 * @xpath_ctx is XPath context with current node as parent of attribute
2781 * carrying message type
2782 * In case of error @envelope will be freed. */
2783 static isds_error append_message_type(struct isds_ctx *context,
2784 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2785 isds_error err = IE_SUCCESS;
2787 if (!context) return IE_INVALID_CONTEXT;
2788 if (!envelope) return IE_INVAL;
2789 if (!xpath_ctx) return IE_INVAL;
2792 if (!*envelope) {
2793 /* Allocate new */
2794 *envelope = calloc(1, sizeof(**envelope));
2795 if (!*envelope) {
2796 err = IE_NOMEM;
2797 goto leave;
2799 } else {
2800 /* Free old data */
2801 zfree((*envelope)->dmType);
2805 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope)->dmType, 0);
2807 if (!(*envelope)->dmType) {
2808 /* Use default value */
2809 (*envelope)->dmType = strdup("V");
2810 if (!(*envelope)->dmType) {
2811 err = IE_NOMEM;
2812 goto leave;
2814 } else if (1 != xmlUTF8Strlen((xmlChar *) (*envelope)->dmType)) {
2815 char *type_locale = utf82locale((*envelope)->dmType);
2816 isds_printf_message(context,
2817 _("Message type in dmType attribute is not 1 character long: "
2818 "%s"),
2819 type_locale);
2820 free(type_locale);
2821 err = IE_ISDS;
2822 goto leave;
2825 leave:
2826 if (err) isds_envelope_free(envelope);
2827 return err;
2832 /* Extract message document into reallocated document structure
2833 * @context is ISDS context
2834 * @document is automically reallocated message documents structure
2835 * @xpath_ctx is XPath context with current node as isds:dmFile
2836 * In case of error @document will be freed. */
2837 static isds_error extract_document(struct isds_ctx *context,
2838 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
2839 isds_error err = IE_SUCCESS;
2840 xmlXPathObjectPtr result = NULL;
2841 xmlNodePtr file_node = xpath_ctx->node;
2842 char *string = NULL;
2844 if (!context) return IE_INVALID_CONTEXT;
2845 if (!document) return IE_INVAL;
2846 isds_document_free(document);
2847 if (!xpath_ctx) return IE_INVAL;
2849 *document = calloc(1, sizeof(**document));
2850 if (!*document) {
2851 err = IE_NOMEM;
2852 goto leave;
2855 /* Extract document metadata */
2856 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
2858 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
2859 err = string2isds_FileMetaType((xmlChar*)string,
2860 &((*document)->dmFileMetaType));
2861 if (err) {
2862 char *meta_type_locale = utf82locale(string);
2863 isds_printf_message(context,
2864 _("Document has invalid dmFileMetaType attribute value: %s"),
2865 meta_type_locale);
2866 free(meta_type_locale);
2867 err = IE_ISDS;
2868 goto leave;
2870 zfree(string);
2872 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
2873 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
2874 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
2875 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
2878 /* Extract document data.
2879 * Base64 encoded blob or XML subtree must be presented. */
2881 /* Check from dmEncodedContent */
2882 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
2883 xpath_ctx);
2884 if (!result) {
2885 err = IE_XML;
2886 goto leave;
2889 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2890 /* Here we have Base64 blob */
2892 if (result->nodesetval->nodeNr > 1) {
2893 isds_printf_message(context,
2894 _("Document has more dmEncodedContent elements"));
2895 err = IE_ISDS;
2896 goto leave;
2899 xmlXPathFreeObject(result); result = NULL;
2900 EXTRACT_STRING("isds:dmEncodedContent", string);
2902 /* Decode non-emptys document */
2903 if (string && string[0] != '\0') {
2904 (*document)->data_length = b64decode(string, &((*document)->data));
2905 if ((*document)->data_length == (size_t) -1) {
2906 isds_printf_message(context,
2907 _("Error while Base64-decoding document content"));
2908 err = IE_ERROR;
2909 goto leave;
2912 } else {
2913 /* No Base64 blob, try XML document */
2914 xmlXPathFreeObject(result); result = NULL;
2915 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
2916 xpath_ctx);
2917 if (!result) {
2918 err = IE_XML;
2919 goto leave;
2922 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2923 /* Here we have XML document */
2925 if (result->nodesetval->nodeNr > 1) {
2926 isds_printf_message(context,
2927 _("Document has more dmXMLContent elements"));
2928 err = IE_ISDS;
2929 goto leave;
2932 /* FIXME: Serialize the tree rooted at result's node */
2933 isds_printf_message(context,
2934 _("XML documents not yet supported"));
2935 err = IE_NOTSUP;
2936 goto leave;
2937 } else {
2938 /* No bas64 blob, nor XML document */
2939 isds_printf_message(context,
2940 _("Document has no dmEncodedContent, nor dmXMLContent "
2941 "element"));
2942 err = IE_ISDS;
2943 goto leave;
2948 leave:
2949 if (err) isds_document_free(document);
2950 free(string);
2951 xmlXPathFreeObject(result);
2952 xpath_ctx->node = file_node;
2953 return err;
2958 /* Extract message documents into reallocated list of documents
2959 * @context is ISDS context
2960 * @documents is automically reallocated message documents list structure
2961 * @xpath_ctx is XPath context with current node as XSD tFilesArray
2962 * In case of error @documents will be freed. */
2963 static isds_error extract_documents(struct isds_ctx *context,
2964 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
2965 isds_error err = IE_SUCCESS;
2966 xmlXPathObjectPtr result = NULL;
2967 xmlNodePtr files_node = xpath_ctx->node;
2968 struct isds_list *document, *prev_document;
2970 if (!context) return IE_INVALID_CONTEXT;
2971 if (!documents) return IE_INVAL;
2972 isds_list_free(documents);
2973 if (!xpath_ctx) return IE_INVAL;
2975 /* Find documents */
2976 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
2977 if (!result) {
2978 err = IE_XML;
2979 goto leave;
2982 /* No match */
2983 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2984 isds_printf_message(context,
2985 _("Message does not contain any document"));
2986 err = IE_ISDS;
2987 goto leave;
2991 /* Iterate over documents */
2992 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
2994 /* Allocate and append list item */
2995 document = calloc(1, sizeof(*document));
2996 if (!document) {
2997 err = IE_NOMEM;
2998 goto leave;
3000 document->destructor = (void (*)(void **))isds_document_free;
3001 if (i == 0) *documents = document;
3002 else prev_document->next = document;
3003 prev_document = document;
3005 /* Extract document */
3006 xpath_ctx->node = result->nodesetval->nodeTab[i];
3007 err = extract_document(context,
3008 (struct isds_document **) &(document->data), xpath_ctx);
3009 if (err) goto leave;
3013 leave:
3014 if (err) isds_list_free(documents);
3015 xmlXPathFreeObject(result);
3016 xpath_ctx->node = files_node;
3017 return err;
3021 /* Convert isds:dmRecord XML tree into structure
3022 * @context is ISDS context
3023 * @envelope is automically reallocated message envelope structure
3024 * @xpath_ctx is XPath context with current node as isds:dmRecord element
3025 * In case of error @envelope will be freed. */
3026 static isds_error extract_DmRecord(struct isds_ctx *context,
3027 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3028 isds_error err = IE_SUCCESS;
3029 xmlXPathObjectPtr result = NULL;
3031 if (!context) return IE_INVALID_CONTEXT;
3032 if (!envelope) return IE_INVAL;
3033 isds_envelope_free(envelope);
3034 if (!xpath_ctx) return IE_INVAL;
3037 *envelope = calloc(1, sizeof(**envelope));
3038 if (!*envelope) {
3039 err = IE_NOMEM;
3040 goto leave;
3044 /* Extract tRecord data */
3045 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
3047 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
3048 * dmAcceptanceTime. */
3049 err = append_status_size_times(context, envelope, xpath_ctx);
3050 if (err) goto leave;
3052 /* Extract envelope elements added by sender and ISDS
3053 * (XSD: gMessageEnvelope type) */
3054 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
3055 if (err) goto leave;
3056 /* dmOVM can not be obtained from ISDS */
3058 /* Get message type */
3059 err = append_message_type(context, envelope, xpath_ctx);
3060 if (err) goto leave;
3063 leave:
3064 if (err) isds_envelope_free(envelope);
3065 xmlXPathFreeObject(result);
3066 return err;
3070 /* Find and convert isds:dmHash XML tree into structure
3071 * @context is ISDS context
3072 * @envelope is automically reallocated message hash structure
3073 * @xpath_ctx is XPath context with current node containing isds:dmHash child
3074 * In case of error @hash will be freed. */
3075 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
3076 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
3077 isds_error err = IE_SUCCESS;
3078 xmlNodePtr old_ctx_node;
3079 xmlXPathObjectPtr result = NULL;
3080 char *string = NULL;
3082 if (!context) return IE_INVALID_CONTEXT;
3083 if (!hash) return IE_INVAL;
3084 isds_hash_free(hash);
3085 if (!xpath_ctx) return IE_INVAL;
3087 old_ctx_node = xpath_ctx->node;
3089 *hash = calloc(1, sizeof(**hash));
3090 if (!*hash) {
3091 err = IE_NOMEM;
3092 goto leave;
3095 /* Locate dmHash */
3096 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
3097 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
3098 err = IE_ISDS;
3099 goto leave;
3101 if (err) {
3102 err = IE_ERROR;
3103 goto leave;
3106 /* Get hash algorithm */
3107 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
3108 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
3109 if (err) {
3110 if (err == IE_ENUM) {
3111 char *string_locale = utf82locale(string);
3112 isds_printf_message(context, _("Unsported hash algorithm: %s"),
3113 string_locale);
3114 free(string_locale);
3116 goto leave;
3118 zfree(string);
3120 /* Get hash value */
3121 EXTRACT_STRING(".", string);
3122 if (!string) {
3123 isds_printf_message(context, _("tHash element is missing hash value"));
3124 err = IE_ISDS;
3125 goto leave;
3127 (*hash)->length = b64decode(string, &((*hash)->value));
3128 if ((*hash)->length == (size_t) -1) {
3129 isds_printf_message(context,
3130 _("Error while Base64-decoding hash value"));
3131 err = IE_ERROR;
3132 goto leave;
3135 leave:
3136 if (err) isds_hash_free(hash);
3137 free(string);
3138 xmlXPathFreeObject(result);
3139 xpath_ctx->node = old_ctx_node;
3140 return err;
3144 /* Find and append isds:dmQTimestamp XML tree into envelope
3145 * @context is ISDS context
3146 * @envelope is automically allocated evnelope structure
3147 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
3148 * child
3149 * In case of error @envelope will be freed. */
3150 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
3151 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3152 isds_error err = IE_SUCCESS;
3153 xmlXPathObjectPtr result = NULL;
3154 char *string = NULL;
3156 if (!context) return IE_INVALID_CONTEXT;
3157 if (!envelope) return IE_INVAL;
3158 if (!xpath_ctx) {
3159 isds_envelope_free(envelope);
3160 return IE_INVAL;
3163 if (!*envelope) {
3164 *envelope = calloc(1, sizeof(**envelope));
3165 if (!*envelope) {
3166 err = IE_NOMEM;
3167 goto leave;
3169 } else {
3170 zfree((*envelope)->timestamp);
3171 (*envelope)->timestamp_length = 0;
3174 /* Get dmQTimestamp */
3175 EXTRACT_STRING("sisds:dmQTimestamp", string);
3176 if (!string) {
3177 isds_printf_message(context, _("Missing dmQTimestamp element content"));
3178 err = IE_ISDS;
3179 goto leave;
3181 (*envelope)->timestamp_length =
3182 b64decode(string, &((*envelope)->timestamp));
3183 if ((*envelope)->timestamp_length == (size_t) -1) {
3184 isds_printf_message(context,
3185 _("Error while Base64-decoding timestamp value"));
3186 err = IE_ERROR;
3187 goto leave;
3190 leave:
3191 if (err) isds_envelope_free(envelope);
3192 free(string);
3193 xmlXPathFreeObject(result);
3194 return err;
3198 /* Convert XSD tReturnedMessage XML tree into message structure.
3199 * It doea not store XML tree into message->raw.
3200 * @context is ISDS context
3201 * @include_documents Use true if documents must be extracted
3202 * (tReturnedMessage XSD type), use false if documents shall be ommited
3203 * (tReturnedMessageEnvelope).
3204 * @message is automically reallocated message structure
3205 * @xpath_ctx is XPath context with current node as tReturnedMessage element
3206 * type
3207 * In case of error @message will be freed. */
3208 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
3209 const _Bool include_documents, struct isds_message **message,
3210 xmlXPathContextPtr xpath_ctx) {
3211 isds_error err = IE_SUCCESS;
3212 xmlNodePtr message_node;
3214 if (!context) return IE_INVALID_CONTEXT;
3215 if (!message) return IE_INVAL;
3216 isds_message_free(message);
3217 if (!xpath_ctx) return IE_INVAL;
3220 *message = calloc(1, sizeof(**message));
3221 if (!*message) {
3222 err = IE_NOMEM;
3223 goto leave;
3226 /* Save message XPATH context node */
3227 message_node = xpath_ctx->node;
3230 /* Extract dmDM */
3231 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
3232 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
3233 if (err) { err = IE_ERROR; goto leave; }
3234 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
3235 if (err) goto leave;
3237 if (include_documents) {
3238 /* Extract dmFiles */
3239 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
3240 xpath_ctx);
3241 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
3242 err = IE_ISDS; goto leave;
3244 if (err) { err = IE_ERROR; goto leave; }
3245 err = extract_documents(context, &((*message)->documents), xpath_ctx);
3246 if (err) goto leave;
3250 /* Restore context to message */
3251 xpath_ctx->node = message_node;
3253 /* Extract dmHash */
3254 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
3255 xpath_ctx);
3256 if (err) goto leave;
3258 /* Extract dmQTimestamp, */
3259 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
3260 xpath_ctx);
3261 if (err) goto leave;
3263 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
3264 * dmAcceptanceTime. */
3265 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
3266 if (err) goto leave;
3268 /* Get message type */
3269 err = append_message_type(context, &((*message)->envelope), xpath_ctx);
3270 if (err) goto leave;
3272 leave:
3273 if (err) isds_message_free(message);
3274 return err;
3278 /* Extract message event into reallocated isds_event structure
3279 * @context is ISDS context
3280 * @event is automically reallocated message event structure
3281 * @xpath_ctx is XPath context with current node as isds:dmEvent
3282 * In case of error @event will be freed. */
3283 static isds_error extract_event(struct isds_ctx *context,
3284 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
3285 isds_error err = IE_SUCCESS;
3286 xmlXPathObjectPtr result = NULL;
3287 xmlNodePtr event_node = xpath_ctx->node;
3288 char *string = NULL;
3290 if (!context) return IE_INVALID_CONTEXT;
3291 if (!event) return IE_INVAL;
3292 isds_event_free(event);
3293 if (!xpath_ctx) return IE_INVAL;
3295 *event = calloc(1, sizeof(**event));
3296 if (!*event) {
3297 err = IE_NOMEM;
3298 goto leave;
3301 /* Extract event data.
3302 * All elements are optional according XSD. That's funny. */
3303 EXTRACT_STRING("sisds:dmEventTime", string);
3304 if (string) {
3305 err = timestring2timeval((xmlChar *) string, &((*event)->time));
3306 if (err) {
3307 char *string_locale = utf82locale(string);
3308 if (err == IE_DATE) err = IE_ISDS;
3309 isds_printf_message(context,
3310 _("Could not convert dmEventTime as ISO time: %s"),
3311 string_locale);
3312 free(string_locale);
3313 goto leave;
3315 zfree(string);
3318 /* dmEventDescr element has prefix and the rest */
3319 EXTRACT_STRING("sisds:dmEventDescr", string);
3320 if (string) {
3321 err = eventstring2event((xmlChar *) string, *event);
3322 if (err) goto leave;
3323 zfree(string);
3326 leave:
3327 if (err) isds_event_free(event);
3328 free(string);
3329 xmlXPathFreeObject(result);
3330 xpath_ctx->node = event_node;
3331 return err;
3335 /* Convert element of XSD tEventsArray type from XML tree into
3336 * isds_list of isds_event's structure. The list is automatically reallocated.
3337 * @context is ISDS context
3338 * @events is automically reallocated list of event structures
3339 * @xpath_ctx is XPath context with current node as tEventsArray
3340 * In case of error @evnets will be freed. */
3341 static isds_error extract_events(struct isds_ctx *context,
3342 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
3343 isds_error err = IE_SUCCESS;
3344 xmlXPathObjectPtr result = NULL;
3345 xmlNodePtr events_node = xpath_ctx->node;
3346 struct isds_list *event, *prev_event = NULL;
3348 if (!context) return IE_INVALID_CONTEXT;
3349 if (!events) return IE_INVAL;
3350 if (!xpath_ctx) return IE_INVAL;
3352 /* Free old list */
3353 isds_list_free(events);
3355 /* Find events */
3356 result = xmlXPathEvalExpression(BAD_CAST "sisds:dmEvent", xpath_ctx);
3357 if (!result) {
3358 err = IE_XML;
3359 goto leave;
3362 /* No match */
3363 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3364 isds_printf_message(context,
3365 _("Delivery info does not contain any event"));
3366 err = IE_ISDS;
3367 goto leave;
3371 /* Iterate over events */
3372 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
3374 /* Allocate and append list item */
3375 event = calloc(1, sizeof(*event));
3376 if (!event) {
3377 err = IE_NOMEM;
3378 goto leave;
3380 event->destructor = (void (*)(void **))isds_event_free;
3381 if (i == 0) *events = event;
3382 else prev_event->next = event;
3383 prev_event = event;
3385 /* Extract event */
3386 xpath_ctx->node = result->nodesetval->nodeTab[i];
3387 err = extract_event(context,
3388 (struct isds_event **) &(event->data), xpath_ctx);
3389 if (err) goto leave;
3393 leave:
3394 if (err) isds_list_free(events);
3395 xmlXPathFreeObject(result);
3396 xpath_ctx->node = events_node;
3397 return err;
3401 /* Convert isds_document structure into XML tree and append to dmFiles node.
3402 * @context is session context
3403 * @document is ISDS document
3404 * @dm_files is XML element the resulting tree will be appended to as a child.
3405 * @return error code, in case of error context' message is filled. */
3406 static isds_error insert_document(struct isds_ctx *context,
3407 struct isds_document *document, xmlNodePtr dm_files) {
3408 isds_error err = IE_SUCCESS;
3409 xmlNodePtr new_file = NULL, file = NULL, node;
3410 xmlAttrPtr attribute_node;
3411 xmlChar *base64data = NULL;
3413 if (!context) return IE_INVALID_CONTEXT;
3414 if (!document || !dm_files) return IE_INVAL;
3416 /* Allocate new dmFile */
3417 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
3418 if (!new_file) {
3419 isds_printf_message(context, _("Could not allocate main dmFile"));
3420 err = IE_ERROR;
3421 goto leave;
3423 /* Append the new dmFile.
3424 * XXX: Main document must go first */
3425 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
3426 file = xmlAddPrevSibling(dm_files->children, new_file);
3427 else
3428 file = xmlAddChild(dm_files, new_file);
3430 if (!file) {
3431 xmlFreeNode(new_file); new_file = NULL;
3432 isds_printf_message(context, _("Could not add dmFile child to "
3433 "%s element"), dm_files->name);
3434 err = IE_ERROR;
3435 goto leave;
3438 /* @dmMimeType is required */
3439 if (!document->dmMimeType) {
3440 isds_log_message(context,
3441 _("Document is missing mandatory MIME type definition"));
3442 err = IE_INVAL;
3443 goto leave;
3445 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
3447 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
3448 if (!string) {
3449 isds_printf_message(context,
3450 _("Document has unkown dmFileMetaType: %ld"),
3451 document->dmFileMetaType);
3452 err = IE_ENUM;
3453 goto leave;
3455 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
3457 if (document->dmFileGuid) {
3458 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
3460 if (document->dmUpFileGuid) {
3461 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
3464 /* @dmFileDescr is required */
3465 if (!document->dmFileDescr) {
3466 isds_log_message(context,
3467 _("Document is missing mandatory description (title)"));
3468 err = IE_INVAL;
3469 goto leave;
3471 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
3473 if (document->dmFormat) {
3474 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
3478 /* Insert content (data) of the document. */
3479 /* XXX; Only base64 is implemented currently. */
3480 base64data = (xmlChar *) b64encode(document->data, document->data_length);
3481 if (!base64data) {
3482 isds_printf_message(context,
3483 _("Not enought memory to encode %zd bytes into Base64"),
3484 document->data_length);
3485 err = IE_NOMEM;
3486 goto leave;
3488 INSERT_STRING(file, "dmEncodedContent", base64data);
3489 free(base64data);
3491 leave:
3492 return err;
3496 /* Append XSD tMStatus XML tree into isds_message_copy structure.
3497 * The copy must pre prealocated, the date are just appended into structure.
3498 * @context is ISDS context
3499 * @copy is message copy struture
3500 * @xpath_ctx is XPath context with current node as tMStatus */
3501 static isds_error append_TMStatus(struct isds_ctx *context,
3502 struct isds_message_copy *copy, xmlXPathContextPtr xpath_ctx) {
3503 isds_error err = IE_SUCCESS;
3504 xmlXPathObjectPtr result = NULL;
3505 char *code = NULL, *message = NULL;
3507 if (!context) return IE_INVALID_CONTEXT;
3508 if (!copy || !xpath_ctx) return IE_INVAL;
3510 /* Free old values */
3511 zfree(copy->dmStatus);
3512 zfree(copy->dmID);
3514 /* Get error specific to this copy */
3515 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code);
3516 if (!code) {
3517 isds_log_message(context,
3518 _("Missing isds:dmStatusCode under "
3519 "XSD:tMStatus type element"));
3520 err = IE_ISDS;
3521 goto leave;
3524 if (xmlStrcmp((const xmlChar *)code, BAD_CAST "0000")) {
3525 /* This copy failed */
3526 copy->error = IE_ISDS;
3527 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message);
3528 if (message) {
3529 copy->dmStatus = astrcat3(code, ": ", message);
3530 if (!copy->dmStatus) {
3531 copy->dmStatus = code;
3532 code = NULL;
3534 } else {
3535 copy->dmStatus = code;
3536 code = NULL;
3538 } else {
3539 /* This copy succeeded. In this case only, message ID is valid */
3540 copy->error = IE_SUCCESS;
3542 EXTRACT_STRING("isds:dmID", copy->dmID);
3543 if (!copy->dmID) {
3544 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
3545 "but did not returned assigned message ID\n"));
3546 err = IE_ISDS;
3550 leave:
3551 free(code);
3552 free(message);
3553 xmlXPathFreeObject(result);
3554 return err;
3558 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
3559 * code
3560 * @context is session context
3561 * @service_name is name of SERVICE_DB_ACCESS
3562 * @response is server SOAP body response as XML document
3563 * @raw_response is automatically reallocated bitstream with response body. Use
3564 * NULL if you don't care
3565 * @raw_response_length is size of @raw_response in bytes
3566 * @code is ISDS status code
3567 * @status_message is ISDS status message
3568 * @return error coded from lower layer, context message will be set up
3569 * appropriately. */
3570 static isds_error build_send_check_dbdummy_request(struct isds_ctx *context,
3571 const xmlChar *service_name,
3572 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
3573 xmlChar **code, xmlChar **status_message) {
3575 isds_error err = IE_SUCCESS;
3576 char *service_name_locale = NULL;
3577 xmlNodePtr request = NULL, node;
3578 xmlNsPtr isds_ns = NULL;
3580 if (!context) return IE_INVALID_CONTEXT;
3581 if (!service_name) return IE_INVAL;
3582 if (!response || !code || !status_message) return IE_INVAL;
3583 if (!raw_response_length && raw_response) return IE_INVAL;
3585 /* Free output argument */
3586 xmlFreeDoc(*response); *response = NULL;
3587 if (raw_response) zfree(*raw_response);
3588 free(*code);
3589 free(*status_message);
3592 /* Check if connection is established
3593 * TODO: This check should be done donwstairs. */
3594 if (!context->curl) return IE_CONNECTION_CLOSED;
3596 service_name_locale = utf82locale((char*)service_name);
3597 if (!service_name_locale) {
3598 err = IE_NOMEM;
3599 goto leave;
3602 /* Build request */
3603 request = xmlNewNode(NULL, service_name);
3604 if (!request) {
3605 isds_printf_message(context,
3606 _("Could not build %s request"), service_name_locale);
3607 err = IE_ERROR;
3608 goto leave;
3610 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
3611 if(!isds_ns) {
3612 isds_log_message(context, _("Could not create ISDS name space"));
3613 err = IE_ERROR;
3614 goto leave;
3616 xmlSetNs(request, isds_ns);
3619 /* Add XSD:tDummyInput child */
3620 INSERT_STRING(request, "dbDummy", NULL);
3623 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
3624 service_name_locale);
3626 /* Send request */
3627 err = isds(context, SERVICE_DB_ACCESS, request, response,
3628 raw_response, raw_response_length);
3629 xmlFreeNode(request); request = NULL;
3631 if (err) {
3632 isds_log(ILF_ISDS, ILL_DEBUG,
3633 _("Processing ISDS response on %s request failed\n"),
3634 service_name_locale);
3635 goto leave;
3638 /* Check for response status */
3639 err = isds_response_status(context, SERVICE_DB_ACCESS, *response,
3640 code, status_message, NULL);
3641 if (err) {
3642 isds_log(ILF_ISDS, ILL_DEBUG,
3643 _("ISDS response on %s request is missing status\n"),
3644 service_name_locale);
3645 goto leave;
3648 /* Request processed, but nothing found */
3649 if (xmlStrcmp(*code, BAD_CAST "0000")) {
3650 char *code_locale = utf82locale((char*) *code);
3651 char *status_message_locale = utf82locale((char*) *status_message);
3652 isds_log(ILF_ISDS, ILL_DEBUG,
3653 _("Server refused %s request (code=%s, message=%s)\n"),
3654 service_name_locale, code_locale, status_message_locale);
3655 isds_log_message(context, status_message_locale);
3656 free(code_locale);
3657 free(status_message_locale);
3658 err = IE_ISDS;
3659 goto leave;
3662 leave:
3663 free(service_name_locale);
3664 xmlFreeNode(request);
3665 return err;
3669 /* Get data about logged in user and his box. */
3670 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
3671 struct isds_DbOwnerInfo **db_owner_info) {
3672 isds_error err = IE_SUCCESS;
3673 xmlDocPtr response = NULL;
3674 xmlChar *code = NULL, *message = NULL;
3675 xmlXPathContextPtr xpath_ctx = NULL;
3676 xmlXPathObjectPtr result = NULL;
3677 char *string = NULL;
3679 if (!context) return IE_INVALID_CONTEXT;
3680 if (!db_owner_info) return IE_INVAL;
3682 /* Check if connection is established */
3683 if (!context->curl) return IE_CONNECTION_CLOSED;
3686 /* Do request and check for success */
3687 err = build_send_check_dbdummy_request(context,
3688 BAD_CAST "GetOwnerInfoFromLogin",
3689 &response, NULL, NULL, &code, &message);
3690 if (err) goto leave;
3693 /* Extract data */
3694 /* Prepare stucture */
3695 isds_DbOwnerInfo_free(db_owner_info);
3696 *db_owner_info = calloc(1, sizeof(**db_owner_info));
3697 if (!*db_owner_info) {
3698 err = IE_NOMEM;
3699 goto leave;
3701 xpath_ctx = xmlXPathNewContext(response);
3702 if (!xpath_ctx) {
3703 err = IE_ERROR;
3704 goto leave;
3706 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
3707 err = IE_ERROR;
3708 goto leave;
3711 /* Set context node */
3712 result = xmlXPathEvalExpression(BAD_CAST
3713 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
3714 if (!result) {
3715 err = IE_ERROR;
3716 goto leave;
3718 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3719 isds_log_message(context, _("Missing dbOwnerInfo element"));
3720 err = IE_ISDS;
3721 goto leave;
3723 if (result->nodesetval->nodeNr > 1) {
3724 isds_log_message(context, _("Multiple dbOwnerInfo element"));
3725 err = IE_ISDS;
3726 goto leave;
3728 xpath_ctx->node = result->nodesetval->nodeTab[0];
3729 xmlXPathFreeObject(result); result = NULL;
3731 /* Extract it */
3732 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
3734 leave:
3735 if (err) {
3736 isds_DbOwnerInfo_free(db_owner_info);
3739 free(string);
3740 xmlXPathFreeObject(result);
3741 xmlXPathFreeContext(xpath_ctx);
3743 free(code);
3744 free(message);
3745 xmlFreeDoc(response);
3747 if (!err)
3748 isds_log(ILF_ISDS, ILL_DEBUG,
3749 _("GetOwnerInfoFromLogin request processed by server "
3750 "successfully.\n"));
3752 return err;
3756 /* Get data about logged in user. */
3757 isds_error isds_GetUserInfoFromLogin(struct isds_ctx *context,
3758 struct isds_DbUserInfo **db_user_info) {
3759 isds_error err = IE_SUCCESS;
3760 xmlDocPtr response = NULL;
3761 xmlChar *code = NULL, *message = NULL;
3762 xmlXPathContextPtr xpath_ctx = NULL;
3763 xmlXPathObjectPtr result = NULL;
3765 if (!context) return IE_INVALID_CONTEXT;
3766 if (!db_user_info) return IE_INVAL;
3768 /* Check if connection is established */
3769 if (!context->curl) return IE_CONNECTION_CLOSED;
3772 /* Do request and check for success */
3773 err = build_send_check_dbdummy_request(context,
3774 BAD_CAST "GetUserInfoFromLogin",
3775 &response, NULL, NULL, &code, &message);
3776 if (err) goto leave;
3779 /* Extract data */
3780 /* Prepare stucture */
3781 isds_DbUserInfo_free(db_user_info);
3782 *db_user_info = calloc(1, sizeof(**db_user_info));
3783 if (!*db_user_info) {
3784 err = IE_NOMEM;
3785 goto leave;
3787 xpath_ctx = xmlXPathNewContext(response);
3788 if (!xpath_ctx) {
3789 err = IE_ERROR;
3790 goto leave;
3792 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
3793 err = IE_ERROR;
3794 goto leave;
3797 /* Set context node */
3798 result = xmlXPathEvalExpression(BAD_CAST
3799 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx);
3800 if (!result) {
3801 err = IE_ERROR;
3802 goto leave;
3804 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3805 isds_log_message(context, _("Missing dbUserInfo element"));
3806 err = IE_ISDS;
3807 goto leave;
3809 if (result->nodesetval->nodeNr > 1) {
3810 isds_log_message(context, _("Multiple dbUserInfo element"));
3811 err = IE_ISDS;
3812 goto leave;
3814 xpath_ctx->node = result->nodesetval->nodeTab[0];
3815 xmlXPathFreeObject(result); result = NULL;
3817 /* Extract it */
3818 err = extract_DbUserInfo(context, db_user_info, xpath_ctx);
3820 leave:
3821 if (err) {
3822 isds_DbUserInfo_free(db_user_info);
3825 xmlXPathFreeObject(result);
3826 xmlXPathFreeContext(xpath_ctx);
3828 free(code);
3829 free(message);
3830 xmlFreeDoc(response);
3832 if (!err)
3833 isds_log(ILF_ISDS, ILL_DEBUG,
3834 _("GetUserInfoFromLogin request processed by server "
3835 "successfully.\n"));
3837 return err;
3841 /* Get expiration time of current password
3842 * @context is session context
3843 * @expiration is automatically reallocated time when password expires, In
3844 * case of error will be nulled. */
3845 isds_error isds_get_password_expiration(struct isds_ctx *context,
3846 struct timeval **expiration) {
3847 isds_error err = IE_SUCCESS;
3848 xmlDocPtr response = NULL;
3849 xmlChar *code = NULL, *message = NULL;
3850 xmlXPathContextPtr xpath_ctx = NULL;
3851 xmlXPathObjectPtr result = NULL;
3852 char *string = NULL;
3854 if (!context) return IE_INVALID_CONTEXT;
3855 if (!expiration) return IE_INVAL;
3857 /* Check if connection is established */
3858 if (!context->curl) return IE_CONNECTION_CLOSED;
3861 /* Do request and check for success */
3862 err = build_send_check_dbdummy_request(context,
3863 BAD_CAST "GetPasswordInfo",
3864 &response, NULL, NULL, &code, &message);
3865 if (err) goto leave;
3868 /* Extract data */
3869 xpath_ctx = xmlXPathNewContext(response);
3870 if (!xpath_ctx) {
3871 err = IE_ERROR;
3872 goto leave;
3874 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
3875 err = IE_ERROR;
3876 goto leave;
3879 /* Set context node */
3880 result = xmlXPathEvalExpression(BAD_CAST
3881 "/isds:GetPasswordInfoResponse", xpath_ctx);
3882 if (!result) {
3883 err = IE_ERROR;
3884 goto leave;
3886 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3887 isds_log_message(context,
3888 _("Missing GetPasswordInfoResponse element"));
3889 err = IE_ISDS;
3890 goto leave;
3892 if (result->nodesetval->nodeNr > 1) {
3893 isds_log_message(context,
3894 _("Multiple GetPasswordInfoResponse element"));
3895 err = IE_ISDS;
3896 goto leave;
3898 xpath_ctx->node = result->nodesetval->nodeTab[0];
3899 xmlXPathFreeObject(result); result = NULL;
3901 /* Extract expiration date */
3902 EXTRACT_STRING("isds:pswExpDate", string);
3903 if (!string) {
3904 isds_log_message(context, _("Missing pswExpDate element"));
3905 err = IE_ISDS;
3906 goto leave;
3909 err = timestring2timeval((xmlChar *) string, expiration);
3910 if (err) {
3911 char *string_locale = utf82locale(string);
3912 if (err == IE_DATE) err = IE_ISDS;
3913 isds_printf_message(context,
3914 _("Could not convert pswExpDate as ISO time: %s"),
3915 string_locale);
3916 free(string_locale);
3917 goto leave;
3920 leave:
3921 if (err) {
3922 if (*expiration) {
3923 zfree(*expiration);
3927 free(string);
3928 xmlXPathFreeObject(result);
3929 xmlXPathFreeContext(xpath_ctx);
3931 free(code);
3932 free(message);
3933 xmlFreeDoc(response);
3935 if (!err)
3936 isds_log(ILF_ISDS, ILL_DEBUG,
3937 _("GetPasswordInfo request processed by server "
3938 "successfully.\n"));
3940 return err;
3944 /* Change user password in ISDS.
3945 * User must supply old password, new password will takes effect after some
3946 * time, current session can continue. Password must fulfill some constraints.
3947 * @context is session context
3948 * @old_password is current password.
3949 * @new_password is requested new password */
3950 isds_error isds_change_password(struct isds_ctx *context,
3951 const char *old_password, const char *new_password) {
3952 isds_error err = IE_SUCCESS;
3953 xmlNsPtr isds_ns = NULL;
3954 xmlNodePtr request = NULL, node;
3955 xmlDocPtr response = NULL;
3956 xmlChar *code = NULL, *message = NULL;
3958 if (!context) return IE_INVALID_CONTEXT;
3959 if (!old_password || !new_password) return IE_INVAL;
3961 /* Check if connection is established
3962 * TODO: This check should be done donwstairs. */
3963 if (!context->curl) return IE_CONNECTION_CLOSED;
3966 /* Build ChangeISDSPassword request */
3967 request = xmlNewNode(NULL, BAD_CAST "ChangeISDSPassword");
3968 if (!request) {
3969 isds_log_message(context,
3970 _("Could build ChangeISDSPassword request"));
3971 return IE_ERROR;
3973 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
3974 if(!isds_ns) {
3975 isds_log_message(context, _("Could not create ISDS name space"));
3976 xmlFreeNode(request);
3977 return IE_ERROR;
3979 xmlSetNs(request, isds_ns);
3981 INSERT_STRING(request, "dbOldPassword", old_password);
3982 INSERT_STRING(request, "dbNewPassword", new_password);
3985 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CheckDataBox request to ISDS\n"));
3987 /* Sent request */
3988 err = isds(context, SERVICE_DB_ACCESS, request, &response, NULL, NULL);
3990 /* Destroy request */
3991 xmlFreeNode(request); request = NULL;
3993 if (err) {
3994 isds_log(ILF_ISDS, ILL_DEBUG,
3995 _("Processing ISDS response on ChangeISDSPassword "
3996 "request failed\n"));
3997 goto leave;
4000 /* Check for response status */
4001 err = isds_response_status(context, SERVICE_DB_ACCESS, response,
4002 &code, &message, NULL);
4003 if (err) {
4004 isds_log(ILF_ISDS, ILL_DEBUG,
4005 _("ISDS response on ChangeISDSPassword request is missing "
4006 "status\n"));
4007 goto leave;
4010 /* Request processed, but empty password refused */
4011 if (!xmlStrcmp(code, BAD_CAST "1066")) {
4012 char *code_locale = utf82locale((char*)code);
4013 char *message_locale = utf82locale((char*)message);
4014 isds_log(ILF_ISDS, ILL_DEBUG,
4015 _("Server refused empty password on ChangeISDSPassword "
4016 "request (code=%s, message=%s)\n"),
4017 code_locale, message_locale);
4018 isds_log_message(context, _("Password must not be empty"));
4019 free(code_locale);
4020 free(message_locale);
4021 err = IE_INVAL;
4022 goto leave;
4025 /* Request processed, but new password was reused */
4026 else if (!xmlStrcmp(code, BAD_CAST "1067")) {
4027 char *code_locale = utf82locale((char*)code);
4028 char *message_locale = utf82locale((char*)message);
4029 isds_log(ILF_ISDS, ILL_DEBUG,
4030 _("Server refused the same new password on ChangeISDSPassword "
4031 "request (code=%s, message=%s)\n"),
4032 code_locale, message_locale);
4033 isds_log_message(context,
4034 _("New password must differ from the current one"));
4035 free(code_locale);
4036 free(message_locale);
4037 err = IE_INVAL;
4038 goto leave;
4041 /* Other error */
4042 else if (xmlStrcmp(code, BAD_CAST "0000")) {
4043 char *code_locale = utf82locale((char*)code);
4044 char *message_locale = utf82locale((char*)message);
4045 isds_log(ILF_ISDS, ILL_DEBUG,
4046 _("Server refused to change password on ChangeISDSPassword "
4047 "request (code=%s, message=%s)\n"),
4048 code_locale, message_locale);
4049 isds_log_message(context, message_locale);
4050 free(code_locale);
4051 free(message_locale);
4052 err = IE_ISDS;
4053 goto leave;
4056 /* Otherwise password changed successfully */
4058 leave:
4059 free(code);
4060 free(message);
4061 xmlFreeDoc(response);
4062 xmlFreeNode(request);
4064 if (!err)
4065 isds_log(ILF_ISDS, ILL_DEBUG,
4066 _("Password changed successfully on ChangeISDSPassword "
4067 "request.\n"));
4069 return err;
4073 /* Generic middle part with request sending and response check.
4074 * It sends prepared request and checks for error code.
4075 * @context is ISDS session context.
4076 * @service is ISDS service handler
4077 * @service_name is name in scope of given @service
4078 * @request is XML tree with request. Will be freed to save memory.
4079 * @response is XML document ouputing ISDS response.
4080 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4081 * NULL, if you don't care. */
4082 static isds_error send_destroy_request_check_response(
4083 struct isds_ctx *context,
4084 const isds_service service, const xmlChar *service_name,
4085 xmlNodePtr *request, xmlDocPtr *response, xmlChar **refnumber) {
4086 isds_error err = IE_SUCCESS;
4087 char *service_name_locale = NULL;
4088 xmlChar *code = NULL, *message = NULL;
4091 if (!context) return IE_INVALID_CONTEXT;
4092 if (!service_name || *service_name == '\0' || !request || !*request ||
4093 !response)
4094 return IE_INVAL;
4096 /* Check if connection is established
4097 * TODO: This check should be done donwstairs. */
4098 if (!context->curl) return IE_CONNECTION_CLOSED;
4100 service_name_locale = utf82locale((char*) service_name);
4101 if (!service_name_locale) {
4102 err = IE_NOMEM;
4103 goto leave;
4106 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
4107 service_name_locale);
4109 /* Send request */
4110 err = isds(context, service, *request, response, NULL, NULL);
4111 xmlFreeNode(*request); *request = NULL;
4113 if (err) {
4114 isds_log(ILF_ISDS, ILL_DEBUG,
4115 _("Processing ISDS response on %s request failed\n"),
4116 service_name_locale);
4117 goto leave;
4120 /* Check for response status */
4121 err = isds_response_status(context, service, *response,
4122 &code, &message, refnumber);
4123 if (err) {
4124 isds_log(ILF_ISDS, ILL_DEBUG,
4125 _("ISDS response on %s request is missing status\n"),
4126 service_name_locale);
4127 goto leave;
4130 /* Request processed, but server failed */
4131 if (xmlStrcmp(code, BAD_CAST "0000")) {
4132 char *code_locale = utf82locale((char*) code);
4133 char *message_locale = utf82locale((char*) message);
4134 isds_log(ILF_ISDS, ILL_DEBUG,
4135 _("Server refused %s request (code=%s, message=%s)\n"),
4136 service_name_locale, code_locale, message_locale);
4137 isds_log_message(context, message_locale);
4138 free(code_locale);
4139 free(message_locale);
4140 err = IE_ISDS;
4141 goto leave;
4145 leave:
4146 free(code);
4147 free(message);
4148 if (err && *response) {
4149 xmlFreeDoc(*response);
4150 *response = NULL;
4152 if (*request) {
4153 xmlFreeNode(*request);
4154 *request = NULL;
4156 free(service_name_locale);
4158 return err;
4162 /* Generic bottom half with request sending.
4163 * It sends prepared request, checks for error code, destroys response and
4164 * request and log success or failure.
4165 * @context is ISDS session context.
4166 * @service is ISDS service handler
4167 * @service_name is name in scope of given @service
4168 * @request is XML tree with request. Will be freed to save memory.
4169 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4170 * NULL, if you don't care. */
4171 static isds_error send_request_check_drop_response(
4172 struct isds_ctx *context,
4173 const isds_service service, const xmlChar *service_name,
4174 xmlNodePtr *request, xmlChar **refnumber) {
4175 isds_error err = IE_SUCCESS;
4176 char *service_name_locale = NULL;
4177 xmlDocPtr response = NULL;
4180 if (!context) return IE_INVALID_CONTEXT;
4181 if (!service_name || *service_name == '\0' || !request || !*request)
4182 return IE_INVAL;
4184 /* Send request and check response*/
4185 err = send_destroy_request_check_response(context,
4186 service, service_name, request, &response, refnumber);
4188 xmlFreeDoc(response);
4190 if (*request) {
4191 xmlFreeNode(*request);
4192 *request = NULL;
4195 if (!err) {
4196 isds_log(ILF_ISDS, ILL_DEBUG,
4197 _("%s request processed by server successfully.\n"),
4198 service_name_locale);
4200 free(service_name_locale);
4202 return err;
4206 /* Build XSD:tCreateDBInput request type for box createing.
4207 * @context is session context
4208 * @request outputs built XML tree
4209 * @service_name is request name of SERVICE_DB_MANIPULATION service
4210 * @box is box description to create including single primary user (in case of
4211 * FO box type)
4212 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
4213 * box, or contact address of PFO box owner)
4214 * @former_names is optional undocumented string. Pass NULL if you don't care.
4215 * @upper_box_id is optional ID of supper box if currently created box is
4216 * subordinated.
4217 * @ceo_label is optional title of OVM box owner (e.g. mayor)
4218 * NULL, if you don't care.*/
4219 static isds_error build_CreateDBInput_request(struct isds_ctx *context,
4220 xmlNodePtr *request, const xmlChar *service_name,
4221 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
4222 const xmlChar *former_names, const xmlChar *upper_box_id,
4223 const xmlChar *ceo_label) {
4224 isds_error err = IE_SUCCESS;
4225 xmlNsPtr isds_ns = NULL;
4226 xmlNodePtr node, dbPrimaryUsers;
4227 xmlChar *string = NULL;
4228 const struct isds_list *item;
4231 if (!context) return IE_INVALID_CONTEXT;
4232 if (!request || !service_name || service_name[0] == '\0' || !box)
4233 return IE_INVAL;
4236 /* Build DeleteDataBox request */
4237 *request = xmlNewNode(NULL, service_name);
4238 if (!*request) {
4239 char *service_name_locale = utf82locale((char*) service_name);
4240 isds_printf_message(context, _("Could build %s request"),
4241 service_name_locale);
4242 free(service_name_locale);
4243 return IE_ERROR;
4245 isds_ns = xmlNewNs(*request, BAD_CAST ISDS_NS, NULL);
4246 if (!isds_ns) {
4247 isds_log_message(context, _("Could not create ISDS name space"));
4248 xmlFreeNode(*request);
4249 return IE_ERROR;
4251 xmlSetNs(*request, isds_ns);
4253 INSERT_ELEMENT(node, *request, "dbOwnerInfo");
4254 err = insert_DbOwnerInfo(context, box, node);
4255 if (err) goto leave;
4257 /* Insert users */
4258 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
4259 * verbose documentatiot allows none dbUserInfo */
4260 INSERT_ELEMENT(dbPrimaryUsers, *request, "dbPrimaryUsers");
4261 for (item = users; item; item = item->next) {
4262 if (item->data) {
4263 INSERT_ELEMENT(node, dbPrimaryUsers, "dbUserInfo");
4264 err = insert_DbUserInfo(context,
4265 (struct isds_DbUserInfo *) item->data, node);
4266 if (err) goto leave;
4270 INSERT_STRING(*request, "dbFormerNames", former_names);
4271 INSERT_STRING(*request, "dbUpperDBId", upper_box_id);
4272 INSERT_STRING(*request, "dbCEOLabel", ceo_label);
4274 /* TODO: gExtApproval */
4276 leave:
4277 if (err) {
4278 xmlFreeNode(*request);
4279 *request = NULL;
4281 free(string);
4282 return err;
4286 /* Create new box.
4287 * @context is session context
4288 * @box is box description to create including single primary user (in case of
4289 * FO box type). It outputs box ID assigned by ISDS in dbID element.
4290 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
4291 * box, or contact address of PFO box owner)
4292 * @former_names is optional undocumented string. Pass NULL if you don't care.
4293 * @upper_box_id is optional ID of supper box if currently created box is
4294 * subordinated.
4295 * @ceo_label is optional title of OVM box owner (e.g. mayor)
4296 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4297 * NULL, if you don't care.*/
4298 isds_error isds_add_box(struct isds_ctx *context,
4299 struct isds_DbOwnerInfo *box, const struct isds_list *users,
4300 const char *former_names, const char *upper_box_id,
4301 const char *ceo_label, char **refnumber) {
4302 isds_error err = IE_SUCCESS;
4303 xmlNodePtr request = NULL;
4304 xmlDocPtr response = NULL;
4305 xmlXPathContextPtr xpath_ctx = NULL;
4306 xmlXPathObjectPtr result = NULL;
4309 if (!context) return IE_INVALID_CONTEXT;
4310 if (!box) return IE_INVAL;
4312 /* Scratch box ID */
4313 zfree(box->dbID);
4315 /* Build CreateDataBox request */
4316 err = build_CreateDBInput_request(context,
4317 &request, BAD_CAST "CreateDataBox",
4318 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
4319 (xmlChar *) ceo_label);
4320 if (err) goto leave;
4322 /* Send it to server and process response */
4323 err = send_destroy_request_check_response(context,
4324 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
4325 &response, (xmlChar **) refnumber);
4327 /* Extract box ID */
4328 xpath_ctx = xmlXPathNewContext(response);
4329 if (!xpath_ctx) {
4330 err = IE_ERROR;
4331 goto leave;
4333 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4334 err = IE_ERROR;
4335 goto leave;
4337 EXTRACT_STRING("/isds:CreateDataBoxResponse/dbID", box->dbID);
4339 leave:
4340 xmlXPathFreeObject(result);
4341 xmlXPathFreeContext(xpath_ctx);
4342 xmlFreeDoc(response);
4343 xmlFreeNode(request);
4345 if (!err) {
4346 isds_log(ILF_ISDS, ILL_DEBUG,
4347 _("CreateDataBox request processed by server successfully.\n"));
4350 return err;
4354 /* Notify ISDS about new PFO entity.
4355 * This function has no real effect.
4356 * @context is session context
4357 * @box is PFO description including single primary user.
4358 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
4359 * @former_names is optional undocumented string. Pass NULL if you don't care.
4360 * @upper_box_id is optional ID of supper box if currently created box is
4361 * subordinated.
4362 * @ceo_label is optional title of OVM box owner (e.g. mayor)
4363 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4364 * NULL, if you don't care.*/
4365 isds_error isds_add_pfoinfo(struct isds_ctx *context,
4366 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
4367 const char *former_names, const char *upper_box_id,
4368 const char *ceo_label, char **refnumber) {
4369 isds_error err = IE_SUCCESS;
4370 xmlNodePtr request = NULL;
4372 if (!context) return IE_INVALID_CONTEXT;
4373 if (!box) return IE_INVAL;
4375 /* Build CreateDataBoxPFOInfo request */
4376 err = build_CreateDBInput_request(context,
4377 &request, BAD_CAST "CreateDataBoxPFOInfo",
4378 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
4379 (xmlChar *) ceo_label);
4380 if (err) goto leave;
4382 /* Send it to server and process response */
4383 err = send_request_check_drop_response(context,
4384 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
4385 (xmlChar **) refnumber);
4386 leave:
4387 xmlFreeNode(request);
4388 return err;
4392 /* Remove given given box permanetly.
4393 * @context is session context
4394 * @box is box description to delete
4395 * @since is date of box owner cancalation. Only tm_year, tm_mon and tm_mday
4396 * carry sane value.
4397 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4398 * NULL, if you don't care.*/
4399 isds_error isds_delete_box(struct isds_ctx *context,
4400 const struct isds_DbOwnerInfo *box, const struct tm *since,
4401 char **refnumber) {
4402 isds_error err = IE_SUCCESS;
4403 xmlNsPtr isds_ns = NULL;
4404 xmlNodePtr request = NULL;
4405 xmlNodePtr node;
4406 xmlChar *string = NULL;
4409 if (!context) return IE_INVALID_CONTEXT;
4410 if (!box || !since) return IE_INVAL;
4413 /* Build DeleteDataBox request */
4414 request = xmlNewNode(NULL, BAD_CAST "DeleteDataBox");
4415 if (!request) {
4416 isds_log_message(context,
4417 _("Could build DeleteDataBox request"));
4418 return IE_ERROR;
4420 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4421 if(!isds_ns) {
4422 isds_log_message(context, _("Could not create ISDS name space"));
4423 xmlFreeNode(request);
4424 return IE_ERROR;
4426 xmlSetNs(request, isds_ns);
4428 INSERT_ELEMENT(node, request, "dbOwnerInfo");
4429 err = insert_DbOwnerInfo(context, box, node);
4430 if (err) goto leave;
4432 err = tm2datestring(since, &string);
4433 if (err) {
4434 isds_log_message(context,
4435 _("Could not convert `since' argument to ISO date string"));
4436 goto leave;
4438 INSERT_STRING(request, "dbOwnerTerminationDate", string);
4439 zfree(string);
4441 /* TODO: gExtApproval */
4444 /* Send it to server and process response */
4445 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
4446 BAD_CAST "DeleteDataBox", &request, (xmlChar **) refnumber);
4448 leave:
4449 xmlFreeNode(request);
4450 free(string);
4451 return err;
4455 /* Update data about given box.
4456 * @context is session context
4457 * @old_box current box description
4458 * @new_box are updated data about @old_box
4459 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4460 * NULL, if you don't care.*/
4461 isds_error isds_UpdateDataBoxDescr(struct isds_ctx *context,
4462 const struct isds_DbOwnerInfo *old_box,
4463 const struct isds_DbOwnerInfo *new_box,
4464 char **refnumber) {
4465 isds_error err = IE_SUCCESS;
4466 xmlNsPtr isds_ns = NULL;
4467 xmlNodePtr request = NULL;
4468 xmlNodePtr node;
4471 if (!context) return IE_INVALID_CONTEXT;
4472 if (!old_box || !new_box) return IE_INVAL;
4475 /* Build UpdateDataBoxDescr request */
4476 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxDescr");
4477 if (!request) {
4478 isds_log_message(context,
4479 _("Could build UpdateDataBoxDescr request"));
4480 return IE_ERROR;
4482 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4483 if(!isds_ns) {
4484 isds_log_message(context, _("Could not create ISDS name space"));
4485 xmlFreeNode(request);
4486 return IE_ERROR;
4488 xmlSetNs(request, isds_ns);
4490 INSERT_ELEMENT(node, request, "dbOldOwnerInfo");
4491 err = insert_DbOwnerInfo(context, old_box, node);
4492 if (err) goto leave;
4494 INSERT_ELEMENT(node, request, "dbNewOwnerInfo");
4495 err = insert_DbOwnerInfo(context, new_box, node);
4496 if (err) goto leave;
4498 /* TODO: gExtApproval */
4501 /* Send it to server and process response */
4502 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
4503 BAD_CAST "UpdateDataBoxDescr", &request, (xmlChar **) refnumber);
4505 leave:
4506 xmlFreeNode(request);
4508 return err;
4512 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
4513 * code
4514 * @context is session context
4515 * @service is SOAP service
4516 * @service_name is name of request in @service
4517 * @box_id is box ID of interrest
4518 * @response is server SOAP body response as XML document
4519 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4520 * NULL, if you don't care.
4521 * @return error coded from lower layer, context message will be set up
4522 * appropriately. */
4523 static isds_error build_send_dbid_request_check_response(
4524 struct isds_ctx *context, const isds_service service,
4525 const xmlChar *service_name, const xmlChar *box_id,
4526 xmlDocPtr *response, xmlChar **refnumber) {
4528 isds_error err = IE_SUCCESS;
4529 char *service_name_locale = NULL, *box_id_locale = NULL;
4530 xmlNodePtr request = NULL, node;
4531 xmlNsPtr isds_ns = NULL;
4533 if (!context) return IE_INVALID_CONTEXT;
4534 if (!service_name || !box_id) return IE_INVAL;
4535 if (!response) return IE_INVAL;
4537 /* Free output argument */
4538 xmlFreeDoc(*response); *response = NULL;
4540 /* Prepare strings */
4541 service_name_locale = utf82locale((char*)service_name);
4542 if (!service_name_locale) {
4543 err = IE_NOMEM;
4544 goto leave;
4546 box_id_locale = utf82locale((char*)box_id);
4547 if (!box_id_locale) {
4548 err = IE_NOMEM;
4549 goto leave;
4552 /* Build request */
4553 request = xmlNewNode(NULL, service_name);
4554 if (!request) {
4555 isds_printf_message(context,
4556 _("Could not build %s request"), service_name_locale);
4557 err = IE_ERROR;
4558 goto leave;
4560 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4561 if(!isds_ns) {
4562 isds_log_message(context, _("Could not create ISDS name space"));
4563 err = IE_ERROR;
4564 goto leave;
4566 xmlSetNs(request, isds_ns);
4568 /* Add XSD:tIdDbInput childs*/
4569 INSERT_STRING(request, "dbID", box_id);
4570 /* TODO: XSD:gExtApproval*/
4572 /* Send request and check response*/
4573 err = send_destroy_request_check_response(context,
4574 service, service_name, &request, response, refnumber);
4576 leave:
4577 free(service_name_locale);
4578 free(box_id_locale);
4579 xmlFreeNode(request);
4580 return err;
4584 /* Get data about all users assigned to given box.
4585 * @context is session context
4586 * @box_id is box ID
4587 * @users is automatically reallocated list of struct isds_DbUserInfo */
4588 isds_error isds_GetDataBoxUsers(struct isds_ctx *context, const char *box_id,
4589 struct isds_list **users) {
4590 isds_error err = IE_SUCCESS;
4591 xmlDocPtr response = NULL;
4592 xmlXPathContextPtr xpath_ctx = NULL;
4593 xmlXPathObjectPtr result = NULL;
4594 int i;
4595 struct isds_list *item, *prev_item = NULL;
4597 if (!context) return IE_INVALID_CONTEXT;
4598 if (!users || !box_id) return IE_INVAL;
4601 /* Do request and check for success */
4602 err = build_send_dbid_request_check_response(context,
4603 SERVICE_DB_MANIPULATION, BAD_CAST "GetDataBoxUsers",
4604 BAD_CAST box_id, &response, NULL);
4605 if (err) goto leave;
4608 /* Extract data */
4609 /* Prepare stucture */
4610 isds_list_free(users);
4611 xpath_ctx = xmlXPathNewContext(response);
4612 if (!xpath_ctx) {
4613 err = IE_ERROR;
4614 goto leave;
4616 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4617 err = IE_ERROR;
4618 goto leave;
4621 /* Set context node */
4622 result = xmlXPathEvalExpression(BAD_CAST
4623 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
4624 xpath_ctx);
4625 if (!result) {
4626 err = IE_ERROR;
4627 goto leave;
4629 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4630 isds_log_message(context, _("Missing dbUserInfo element"));
4631 err = IE_ISDS;
4632 goto leave;
4635 /* Iterate over all users */
4636 for (i = 0; i < result->nodesetval->nodeNr; i++) {
4638 /* Prepare structure */
4639 item = calloc(1, sizeof(*item));
4640 if (!item) {
4641 err = IE_NOMEM;
4642 goto leave;
4644 item->destructor = (void(*)(void**))isds_DbUserInfo_free;
4645 if (i == 0) *users = item;
4646 else prev_item->next = item;
4647 prev_item = item;
4649 /* Extract it */
4650 xpath_ctx->node = result->nodesetval->nodeTab[i];
4651 err = extract_DbUserInfo(context,
4652 (struct isds_DbUserInfo **) (&item->data), xpath_ctx);
4653 if (err) goto leave;
4656 leave:
4657 if (err) {
4658 isds_list_free(users);
4661 xmlXPathFreeObject(result);
4662 xmlXPathFreeContext(xpath_ctx);
4663 xmlFreeDoc(response);
4665 if (!err)
4666 isds_log(ILF_ISDS, ILL_DEBUG,
4667 _("GetDataBoxUsers request processed by server "
4668 "successfully.\n"));
4670 return err;
4674 /* Update data about user assigned to given box.
4675 * @context is session context
4676 * @box is box identification
4677 * @old_user identifies user to update
4678 * @new_user are updated data about @old_user
4679 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4680 * NULL, if you don't care.*/
4681 isds_error isds_UpdateDataBoxUser(struct isds_ctx *context,
4682 const struct isds_DbOwnerInfo *box,
4683 const struct isds_DbUserInfo *old_user,
4684 const struct isds_DbUserInfo *new_user,
4685 char **refnumber) {
4686 isds_error err = IE_SUCCESS;
4687 xmlNsPtr isds_ns = NULL;
4688 xmlNodePtr request = NULL;
4689 xmlNodePtr node;
4692 if (!context) return IE_INVALID_CONTEXT;
4693 if (!box || !old_user || !new_user) return IE_INVAL;
4696 /* Build UpdateDataBoxUser request */
4697 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxUser");
4698 if (!request) {
4699 isds_log_message(context,
4700 _("Could build UpdateDataBoxUser request"));
4701 return IE_ERROR;
4703 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4704 if(!isds_ns) {
4705 isds_log_message(context, _("Could not create ISDS name space"));
4706 xmlFreeNode(request);
4707 return IE_ERROR;
4709 xmlSetNs(request, isds_ns);
4711 INSERT_ELEMENT(node, request, "dbOwnerInfo");
4712 err = insert_DbOwnerInfo(context, box, node);
4713 if (err) goto leave;
4715 INSERT_ELEMENT(node, request, "dbOldUserInfo");
4716 err = insert_DbUserInfo(context, old_user, node);
4717 if (err) goto leave;
4719 INSERT_ELEMENT(node, request, "dbNewUserInfo");
4720 err = insert_DbUserInfo(context, new_user, node);
4721 if (err) goto leave;
4723 /* Send it to server and process response */
4724 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
4725 BAD_CAST "UpdateDataBoxUser", &request, (xmlChar **) refnumber);
4727 leave:
4728 xmlFreeNode(request);
4730 return err;
4734 /* Reset credentials of user assigned to given box.
4735 * @context is session context
4736 * @box is box identification
4737 * @user identifies user to reset password
4738 * @fee_paid is true if fee has been paid, false otherwise
4739 * @token is NULL if new password should be delivered off-line to the user.
4740 * It is valid pointer if user should obtain new password on-line on dedicated
4741 * web server. Then it output automatically reallocated token user needs to
4742 * use to athtorize on the web server to view his new password.
4743 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4744 * NULL, if you don't care.*/
4745 isds_error isds_reset_password(struct isds_ctx *context,
4746 const struct isds_DbOwnerInfo *box,
4747 const struct isds_DbUserInfo *user,
4748 const _Bool fee_paid,
4749 char **token, char **refnumber) {
4750 isds_error err = IE_SUCCESS;
4751 xmlNsPtr isds_ns = NULL;
4752 xmlNodePtr request = NULL, node;
4753 xmlDocPtr response = NULL;
4754 xmlXPathContextPtr xpath_ctx = NULL;
4755 xmlXPathObjectPtr result = NULL;
4758 if (!context) return IE_INVALID_CONTEXT;
4759 if (!box || !user) return IE_INVAL;
4761 if (token) zfree(*token);
4764 /* Build NewAccessData request */
4765 request = xmlNewNode(NULL, BAD_CAST "NewAccessData");
4766 if (!request) {
4767 isds_log_message(context,
4768 _("Could build NewAccessData request"));
4769 return IE_ERROR;
4771 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4772 if(!isds_ns) {
4773 isds_log_message(context, _("Could not create ISDS name space"));
4774 xmlFreeNode(request);
4775 return IE_ERROR;
4777 xmlSetNs(request, isds_ns);
4779 INSERT_ELEMENT(node, request, "dbOwnerInfo");
4780 err = insert_DbOwnerInfo(context, box, node);
4781 if (err) goto leave;
4783 INSERT_ELEMENT(node, request, "dbUserInfo");
4784 err = insert_DbUserInfo(context, user, node);
4785 if (err) goto leave;
4787 INSERT_SCALAR_BOOLEAN(request, "dbFeePaid", fee_paid);
4789 if (token) {
4790 INSERT_SCALAR_BOOLEAN(request, "dbVirtual", 1);
4791 } else {
4792 INSERT_SCALAR_BOOLEAN(request, "dbVirtual", 0);
4795 /* TODO: gExtApproval */
4797 /* Send request and check reposne*/
4798 err = send_destroy_request_check_response(context,
4799 SERVICE_DB_MANIPULATION, BAD_CAST "NewAccessData", &request,
4800 &response, (xmlChar **) refnumber);
4801 if (err) goto leave;
4804 /* Extract optional token */
4805 if (token) {
4806 xpath_ctx = xmlXPathNewContext(response);
4807 if (!xpath_ctx) {
4808 err = IE_ERROR;
4809 goto leave;
4811 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4812 err = IE_ERROR;
4813 goto leave;
4816 EXTRACT_STRING("/isds:NewAccessDataResponse/dbAccessDataId", *token);
4819 leave:
4820 xmlXPathFreeObject(result);
4821 xmlXPathFreeContext(xpath_ctx);
4822 xmlFreeDoc(response);
4823 xmlFreeNode(request);
4825 if (!err)
4826 isds_log(ILF_ISDS, ILL_DEBUG,
4827 _("NewAccessData request processed by server "
4828 "successfully.\n"));
4830 return err;
4834 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
4835 * code, destroy response and log success.
4836 * @context is ISDS session context.
4837 * @service_name is name of SERVICE_DB_MANIPULATION service
4838 * @box is box identification
4839 * @user identifies user to removve
4840 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4841 * NULL, if you don't care. */
4842 static isds_error build_send_manipulationboxuser_request_check_drop_response(
4843 struct isds_ctx *context, const xmlChar *service_name,
4844 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
4845 xmlChar **refnumber) {
4846 isds_error err = IE_SUCCESS;
4847 xmlNsPtr isds_ns = NULL;
4848 xmlNodePtr request = NULL, node;
4851 if (!context) return IE_INVALID_CONTEXT;
4852 if (!service_name || service_name[0] == '\0' || !box || !user)
4853 return IE_INVAL;
4856 /* Build NewAccessData request */
4857 request = xmlNewNode(NULL, service_name);
4858 if (!request) {
4859 char *service_name_locale = utf82locale((char *) service_name);
4860 isds_printf_message(context, _("Could build %s request"),
4861 service_name_locale);
4862 free(service_name_locale);
4863 return IE_ERROR;
4865 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4866 if(!isds_ns) {
4867 isds_log_message(context, _("Could not create ISDS name space"));
4868 xmlFreeNode(request);
4869 return IE_ERROR;
4871 xmlSetNs(request, isds_ns);
4873 INSERT_ELEMENT(node, request, "dbOwnerInfo");
4874 err = insert_DbOwnerInfo(context, box, node);
4875 if (err) goto leave;
4877 INSERT_ELEMENT(node, request, "dbUserInfo");
4878 err = insert_DbUserInfo(context, user, node);
4879 if (err) goto leave;
4881 /* TODO: gExtApproval */
4883 /* Send request and check reposne*/
4884 err = send_request_check_drop_response (context,
4885 SERVICE_DB_MANIPULATION, service_name, &request, refnumber);
4887 leave:
4888 xmlFreeNode(request);
4889 return err;
4893 /* Assign new user to given box.
4894 * @context is session context
4895 * @box is box identification
4896 * @user defines new user to add
4897 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4898 * NULL, if you don't care.*/
4899 isds_error isds_add_user(struct isds_ctx *context,
4900 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
4901 char **refnumber){
4902 return build_send_manipulationboxuser_request_check_drop_response(context,
4903 BAD_CAST "AddDataBoxUser", box, user, (xmlChar **) refnumber);
4907 /* Remove user assigned to given box.
4908 * @context is session context
4909 * @box is box identification
4910 * @user identifies user to removve
4911 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4912 * NULL, if you don't care.*/
4913 isds_error isds_delete_user(struct isds_ctx *context,
4914 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
4915 char **refnumber) {
4916 return build_send_manipulationboxuser_request_check_drop_response(context,
4917 BAD_CAST "DeleteDataBoxUser", box, user, (xmlChar **) refnumber);
4921 /* Find boxes suiting given criteria.
4922 * @criteria is filter. You should fill in at least some memebers.
4923 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
4924 * possibly empty. Input NULL or valid old structure.
4925 * @return:
4926 * IE_SUCCESS if search sucseeded, @boxes contains usefull data
4927 * IE_NOEXIST if no such box exists, @boxes will be NULL
4928 * IE_2BIG if too much boxes exist and server truncated the resuluts, @boxes
4929 * contains still valid data
4930 * other code if something bad happens. @boxes will be NULL. */
4931 isds_error isds_FindDataBox(struct isds_ctx *context,
4932 const struct isds_DbOwnerInfo *criteria,
4933 struct isds_list **boxes) {
4934 isds_error err = IE_SUCCESS;
4935 _Bool truncated = 0;
4936 xmlNsPtr isds_ns = NULL;
4937 xmlNodePtr request = NULL;
4938 xmlDocPtr response = NULL;
4939 xmlChar *code = NULL, *message = NULL;
4940 xmlNodePtr db_owner_info;
4941 xmlXPathContextPtr xpath_ctx = NULL;
4942 xmlXPathObjectPtr result = NULL;
4943 xmlChar *string = NULL;
4946 if (!context) return IE_INVALID_CONTEXT;
4947 if (!boxes) return IE_INVAL;
4948 isds_list_free(boxes);
4950 if (!criteria) {
4951 return IE_INVAL;
4954 /* Check if connection is established
4955 * TODO: This check should be done donwstairs. */
4956 if (!context->curl) return IE_CONNECTION_CLOSED;
4959 /* Build FindDataBox request */
4960 request = xmlNewNode(NULL, BAD_CAST "FindDataBox");
4961 if (!request) {
4962 isds_log_message(context,
4963 _("Could build FindDataBox request"));
4964 return IE_ERROR;
4966 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4967 if(!isds_ns) {
4968 isds_log_message(context, _("Could not create ISDS name space"));
4969 xmlFreeNode(request);
4970 return IE_ERROR;
4972 xmlSetNs(request, isds_ns);
4973 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
4974 if (!db_owner_info) {
4975 isds_log_message(context, _("Could not add dbOwnerInfo Child to "
4976 "FindDataBox element"));
4977 xmlFreeNode(request);
4978 return IE_ERROR;
4981 err = insert_DbOwnerInfo(context, criteria, db_owner_info);
4982 if (err) goto leave;
4985 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending FindDataBox request to ISDS\n"));
4987 /* Sent request */
4988 err = isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
4990 /* Destroy request */
4991 xmlFreeNode(request); request = NULL;
4993 if (err) {
4994 isds_log(ILF_ISDS, ILL_DEBUG,
4995 _("Processing ISDS response on FindDataBox "
4996 "request failed\n"));
4997 goto leave;
5000 /* Check for response status */
5001 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
5002 &code, &message, NULL);
5003 if (err) {
5004 isds_log(ILF_ISDS, ILL_DEBUG,
5005 _("ISDS response on FindDataBox request is missing status\n"));
5006 goto leave;
5009 /* Request processed, but nothing found */
5010 if (!xmlStrcmp(code, BAD_CAST "0002") ||
5011 !xmlStrcmp(code, BAD_CAST "5001")) {
5012 char *code_locale = utf82locale((char*)code);
5013 char *message_locale = utf82locale((char*)message);
5014 isds_log(ILF_ISDS, ILL_DEBUG,
5015 _("Server did not found any box on FindDataBox request "
5016 "(code=%s, message=%s)\n"), code_locale, message_locale);
5017 isds_log_message(context, message_locale);
5018 free(code_locale);
5019 free(message_locale);
5020 err = IE_NOEXIST;
5021 goto leave;
5024 /* Warning, not a error */
5025 if (!xmlStrcmp(code, BAD_CAST "0003")) {
5026 char *code_locale = utf82locale((char*)code);
5027 char *message_locale = utf82locale((char*)message);
5028 isds_log(ILF_ISDS, ILL_DEBUG,
5029 _("Server truncated response on FindDataBox request "
5030 "(code=%s, message=%s)\n"), code_locale, message_locale);
5031 isds_log_message(context, message_locale);
5032 free(code_locale);
5033 free(message_locale);
5034 truncated = 1;
5037 /* Other error */
5038 else if (xmlStrcmp(code, BAD_CAST "0000")) {
5039 char *code_locale = utf82locale((char*)code);
5040 char *message_locale = utf82locale((char*)message);
5041 isds_log(ILF_ISDS, ILL_DEBUG,
5042 _("Server refused FindDataBox request "
5043 "(code=%s, message=%s)\n"), code_locale, message_locale);
5044 isds_log_message(context, message_locale);
5045 free(code_locale);
5046 free(message_locale);
5047 err = IE_ISDS;
5048 goto leave;
5051 xpath_ctx = xmlXPathNewContext(response);
5052 if (!xpath_ctx) {
5053 err = IE_ERROR;
5054 goto leave;
5056 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5057 err = IE_ERROR;
5058 goto leave;
5061 /* Extract boxes if they present */
5062 result = xmlXPathEvalExpression(BAD_CAST
5063 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
5064 xpath_ctx);
5065 if (!result) {
5066 err = IE_ERROR;
5067 goto leave;
5069 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5070 struct isds_list *item, *prev_item = NULL;
5071 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
5072 item = calloc(1, sizeof(*item));
5073 if (!item) {
5074 err = IE_NOMEM;
5075 goto leave;
5078 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
5079 if (i == 0) *boxes = item;
5080 else prev_item->next = item;
5081 prev_item = item;
5083 xpath_ctx->node = result->nodesetval->nodeTab[i];
5084 err = extract_DbOwnerInfo(context,
5085 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
5086 if (err) goto leave;
5090 leave:
5091 if (err) {
5092 isds_list_free(boxes);
5093 } else {
5094 if (truncated) err = IE_2BIG;
5097 free(string);
5098 xmlFreeNode(request);
5099 xmlXPathFreeObject(result);
5100 xmlXPathFreeContext(xpath_ctx);
5102 free(code);
5103 free(message);
5104 xmlFreeDoc(response);
5106 if (!err)
5107 isds_log(ILF_ISDS, ILL_DEBUG,
5108 _("FindDataBox request processed by server successfully.\n"));
5110 return err;
5114 /* Get status of a box.
5115 * @context is ISDS session context.
5116 * @box_id is UTF-8 encoded box identifier as zero terminated string
5117 * @box_status is return value of box status.
5118 * @return:
5119 * IE_SUCCESS if box has been found and its status retrieved
5120 * IE_NOEXIST if box is not known to ISDS server
5121 * or other appropriate error.
5122 * You can use isds_DbState to enumerate box status. However out of enum
5123 * range value can be returned too. This is feature because ISDS
5124 * specification leaves the set of values open.
5125 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
5126 * the box has been deleted, but ISDS still lists its former existence. */
5127 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
5128 long int *box_status) {
5129 isds_error err = IE_SUCCESS;
5130 xmlNsPtr isds_ns = NULL;
5131 xmlNodePtr request = NULL, db_id;
5132 xmlDocPtr response = NULL;
5133 xmlChar *code = NULL, *message = NULL;
5134 xmlXPathContextPtr xpath_ctx = NULL;
5135 xmlXPathObjectPtr result = NULL;
5136 xmlChar *string = NULL;
5138 if (!context) return IE_INVALID_CONTEXT;
5139 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
5141 /* Check if connection is established
5142 * TODO: This check should be done donwstairs. */
5143 if (!context->curl) return IE_CONNECTION_CLOSED;
5146 /* Build CheckDataBox request */
5147 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
5148 if (!request) {
5149 isds_log_message(context,
5150 _("Could build CheckDataBox request"));
5151 return IE_ERROR;
5153 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5154 if(!isds_ns) {
5155 isds_log_message(context, _("Could not create ISDS name space"));
5156 xmlFreeNode(request);
5157 return IE_ERROR;
5159 xmlSetNs(request, isds_ns);
5160 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
5161 if (!db_id) {
5162 isds_log_message(context, _("Could not add dbId Child to "
5163 "CheckDataBox element"));
5164 xmlFreeNode(request);
5165 return IE_ERROR;
5169 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CheckDataBox request to ISDS\n"));
5171 /* Sent request */
5172 err = isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
5174 /* Destroy request */
5175 xmlFreeNode(request);
5177 if (err) {
5178 isds_log(ILF_ISDS, ILL_DEBUG,
5179 _("Processing ISDS response on CheckDataBox "
5180 "request failed\n"));
5181 goto leave;
5184 /* Check for response status */
5185 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
5186 &code, &message, NULL);
5187 if (err) {
5188 isds_log(ILF_ISDS, ILL_DEBUG,
5189 _("ISDS response on CheckDataBox request is missing status\n"));
5190 goto leave;
5193 /* Request processed, but nothing found */
5194 if (!xmlStrcmp(code, BAD_CAST "5001")) {
5195 char *box_id_locale = utf82locale((char*)box_id);
5196 char *code_locale = utf82locale((char*)code);
5197 char *message_locale = utf82locale((char*)message);
5198 isds_log(ILF_ISDS, ILL_DEBUG,
5199 _("Server did not found box %s on CheckDataBox request "
5200 "(code=%s, message=%s)\n"),
5201 box_id_locale, code_locale, message_locale);
5202 isds_log_message(context, message_locale);
5203 free(box_id_locale);
5204 free(code_locale);
5205 free(message_locale);
5206 err = IE_NOEXIST;
5207 goto leave;
5210 /* Other error */
5211 else if (xmlStrcmp(code, BAD_CAST "0000")) {
5212 char *code_locale = utf82locale((char*)code);
5213 char *message_locale = utf82locale((char*)message);
5214 isds_log(ILF_ISDS, ILL_DEBUG,
5215 _("Server refused CheckDataBox request "
5216 "(code=%s, message=%s)\n"), code_locale, message_locale);
5217 isds_log_message(context, message_locale);
5218 free(code_locale);
5219 free(message_locale);
5220 err = IE_ISDS;
5221 goto leave;
5224 /* Extract data */
5225 xpath_ctx = xmlXPathNewContext(response);
5226 if (!xpath_ctx) {
5227 err = IE_ERROR;
5228 goto leave;
5230 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5231 err = IE_ERROR;
5232 goto leave;
5234 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
5235 xpath_ctx);
5236 if (!result) {
5237 err = IE_ERROR;
5238 goto leave;
5240 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5241 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
5242 err = IE_ISDS;
5243 goto leave;
5245 if (result->nodesetval->nodeNr > 1) {
5246 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
5247 err = IE_ISDS;
5248 goto leave;
5250 xpath_ctx->node = result->nodesetval->nodeTab[0];
5251 xmlXPathFreeObject(result); result = NULL;
5253 EXTRACT_LONGINT("isds:dbState", box_status, 1);
5256 leave:
5257 free(string);
5258 xmlXPathFreeObject(result);
5259 xmlXPathFreeContext(xpath_ctx);
5261 free(code);
5262 free(message);
5263 xmlFreeDoc(response);
5265 if (!err)
5266 isds_log(ILF_ISDS, ILL_DEBUG,
5267 _("CheckDataBox request processed by server successfully.\n"));
5269 return err;
5273 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
5274 * code, destroy response and log success.
5275 * @context is ISDS session context.
5276 * @service_name is name of SERVICE_DB_MANIPULATION service
5277 * @box_id is UTF-8 encoded box identifier as zero terminated string
5278 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5279 * NULL, if you don't care. */
5280 static isds_error build_send_manipulationdbid_request_check_drop_response(
5281 struct isds_ctx *context, const xmlChar *service_name,
5282 const xmlChar *box_id, xmlChar **refnumber) {
5283 isds_error err = IE_SUCCESS;
5284 xmlDocPtr response = NULL;
5286 if (!context) return IE_INVALID_CONTEXT;
5287 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
5289 /* Check if connection is established */
5290 if (!context->curl) return IE_CONNECTION_CLOSED;
5292 /* Do request and check for success */
5293 err = build_send_dbid_request_check_response(context,
5294 SERVICE_DB_MANIPULATION, service_name, box_id,
5295 &response, refnumber);
5296 xmlFreeDoc(response);
5298 if (!err) {
5299 char *service_name_locale = utf82locale((char *) service_name);
5300 isds_log(ILF_ISDS, ILL_DEBUG,
5301 _("%s request processed by server successfully.\n"),
5302 service_name_locale);
5303 free(service_name_locale);
5306 return err;
5310 /* Switch box into state where box can receive commercial messages (off by
5311 * default)
5312 * @context is ISDS session context.
5313 * @box_id is UTF-8 encoded box identifier as zero terminated string
5314 * @allow is true for enable, false for disable commercial messages income
5315 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5316 * NULL, if you don't care. */
5317 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
5318 const char *box_id, const _Bool allow, char **refnumber) {
5319 return build_send_manipulationdbid_request_check_drop_response(context,
5320 (allow) ? BAD_CAST "SetOpenAddressing" :
5321 BAD_CAST "ClearOpenAddressing",
5322 BAD_CAST box_id, (xmlChar **) refnumber);
5326 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
5327 * message acceptance). This is just a box permission. Sender must apply
5328 * such role by sending each message.
5329 * @context is ISDS session context.
5330 * @box_id is UTF-8 encoded box identifier as zero terminated string
5331 * @allow is true for enable, false for disable OVM role permission
5332 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5333 * NULL, if you don't care. */
5334 isds_error isds_switch_effective_ovm(struct isds_ctx *context,
5335 const char *box_id, const _Bool allow, char **refnumber) {
5336 return build_send_manipulationdbid_request_check_drop_response(context,
5337 (allow) ? BAD_CAST "SetEffectiveOVM" :
5338 BAD_CAST "ClearEffectiveOVM",
5339 BAD_CAST box_id, (xmlChar **) refnumber);
5343 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
5344 * code, destroy response and log success.
5345 * @context is ISDS session context.
5346 * @service_name is name of SERVICE_DB_MANIPULATION service
5347 * @owner is structure describing box
5348 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5349 * NULL, if you don't care. */
5350 static isds_error build_send_manipulationdbowner_request_check_drop_response(
5351 struct isds_ctx *context, const xmlChar *service_name,
5352 const struct isds_DbOwnerInfo *owner, xmlChar **refnumber) {
5353 isds_error err = IE_SUCCESS;
5354 char *service_name_locale = NULL;
5355 xmlNodePtr request = NULL, db_owner_info;
5356 xmlNsPtr isds_ns = NULL;
5359 if (!context) return IE_INVALID_CONTEXT;
5360 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
5362 service_name_locale = utf82locale((char*)service_name);
5363 if (!service_name_locale) {
5364 err = IE_NOMEM;
5365 goto leave;
5368 /* Build request */
5369 request = xmlNewNode(NULL, service_name);
5370 if (!request) {
5371 isds_printf_message(context,
5372 _("Could not build %s request"), service_name_locale);
5373 err = IE_ERROR;
5374 goto leave;
5376 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5377 if(!isds_ns) {
5378 isds_log_message(context, _("Could not create ISDS name space"));
5379 err = IE_ERROR;
5380 goto leave;
5382 xmlSetNs(request, isds_ns);
5385 /* Add XSD:tOwnerInfoInput child*/
5386 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
5387 err = insert_DbOwnerInfo(context, owner, db_owner_info);
5388 if (err) goto leave;
5389 /* TODO: XSD:gExtApproval*/
5391 /* Send it to server and process response */
5392 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
5393 service_name, &request, refnumber);
5395 leave:
5396 xmlFreeNode(request);
5397 free(service_name_locale);
5399 return err;
5403 /* Switch box accessibility state on request of box owner.
5404 * Despite the name, owner must do the requst off-line. This function is
5405 * designed for such off-line meeting points (e.g. Czech POINT).
5406 * @context is ISDS session context.
5407 * @box identifies box to swith accesibilty state.
5408 * @allow is true for making accesibale, false to disallow access.
5409 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5410 * NULL, if you don't care. */
5411 isds_error isds_switch_box_accessibility_on_owner_request(
5412 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
5413 const _Bool allow, char **refnumber) {
5414 return build_send_manipulationdbowner_request_check_drop_response(context,
5415 (allow) ? BAD_CAST "EnableOwnDataBox" :
5416 BAD_CAST "DisableOwnDataBox",
5417 box, (xmlChar **) refnumber);
5421 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
5422 * date.
5423 * @context is ISDS session context.
5424 * @box identifies box to swith accesibilty state.
5425 * @since is date since accesseibility has been denied. This can be past too.
5426 * Only tm_year, tm_mon and tm_mday carry sane value.
5427 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5428 * NULL, if you don't care. */
5429 isds_error isds_disable_box_accessibility_externaly(
5430 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
5431 const struct tm *since, char **refnumber) {
5432 isds_error err = IE_SUCCESS;
5433 char *service_name_locale = NULL;
5434 xmlNodePtr request = NULL, node;
5435 xmlNsPtr isds_ns = NULL;
5436 xmlChar *string = NULL;
5439 if (!context) return IE_INVALID_CONTEXT;
5440 if (!box || !since) return IE_INVAL;
5442 /* Build request */
5443 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
5444 if (!request) {
5445 isds_printf_message(context,
5446 _("Could not build %s request"), "DisableDataBoxExternally");
5447 err = IE_ERROR;
5448 goto leave;
5450 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5451 if(!isds_ns) {
5452 isds_log_message(context, _("Could not create ISDS name space"));
5453 err = IE_ERROR;
5454 goto leave;
5456 xmlSetNs(request, isds_ns);
5459 /* Add @box identification */
5460 INSERT_ELEMENT(node, request, "dbOwnerInfo");
5461 err = insert_DbOwnerInfo(context, box, node);
5462 if (err) goto leave;
5464 /* Add @since date */
5465 err = tm2datestring(since, &string);
5466 if(err) {
5467 isds_log_message(context,
5468 _("Could not convert `since' argument to ISO date string"));
5469 goto leave;
5471 INSERT_STRING(request, "dbOwnerDisableDate", string);
5472 zfree(string);
5474 /* TODO: XSD:gExtApproval*/
5476 /* Send it to server and process response */
5477 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
5478 BAD_CAST "DisableDataBoxExternally", &request,
5479 (xmlChar **) refnumber);
5481 leave:
5482 free(string);
5483 xmlFreeNode(request);
5484 free(service_name_locale);
5486 return err;
5490 /* Insert struct isds_message data (envelope (recipient data optional) and
5491 * documents) into XML tree
5492 * @context is sesstion context
5493 * @outgoing_message is libsids structure with message data
5494 * @create_message is XML CreateMessage or CreateMultipleMessage element
5495 * @process_recipient true for recipient data serialization, false for no
5496 * serialization */
5497 static isds_error insert_envelope_files(struct isds_ctx *context,
5498 const struct isds_message *outgoing_message, xmlNodePtr create_message,
5499 const _Bool process_recipient) {
5501 isds_error err = IE_SUCCESS;
5502 xmlNodePtr envelope, dm_files, node;
5503 xmlChar *string = NULL;
5505 if (!context) return IE_INVALID_CONTEXT;
5506 if (!outgoing_message || !create_message) return IE_INVAL;
5509 /* Build envelope */
5510 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
5511 if (!envelope) {
5512 isds_printf_message(context, _("Could not add dmEnvelope child to "
5513 "%s element"), create_message->name);
5514 return IE_ERROR;
5517 if (!outgoing_message->envelope) {
5518 isds_log_message(context, _("Outgoing message is missing envelope"));
5519 err = IE_INVAL;
5520 goto leave;
5523 INSERT_STRING(envelope, "dmSenderOrgUnit",
5524 outgoing_message->envelope->dmSenderOrgUnit);
5525 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
5526 outgoing_message->envelope->dmSenderOrgUnitNum, string);
5528 if (process_recipient) {
5529 if (!outgoing_message->envelope->dbIDRecipient) {
5530 isds_log_message(context,
5531 _("Outgoing message is missing recipient box identifier"));
5532 err = IE_INVAL;
5533 goto leave;
5535 INSERT_STRING(envelope, "dbIDRecipient",
5536 outgoing_message->envelope->dbIDRecipient);
5538 INSERT_STRING(envelope, "dmRecipientOrgUnit",
5539 outgoing_message->envelope->dmRecipientOrgUnit);
5540 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
5541 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
5542 INSERT_STRING(envelope, "dmToHands",
5543 outgoing_message->envelope->dmToHands);
5546 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
5547 "dmAnnotation");
5548 INSERT_STRING(envelope, "dmAnnotation",
5549 outgoing_message->envelope->dmAnnotation);
5551 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
5552 0, 50, "dmRecipientRefNumber");
5553 INSERT_STRING(envelope, "dmRecipientRefNumber",
5554 outgoing_message->envelope->dmRecipientRefNumber);
5556 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
5557 0, 50, "dmSenderRefNumber");
5558 INSERT_STRING(envelope, "dmSenderRefNumber",
5559 outgoing_message->envelope->dmSenderRefNumber);
5561 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
5562 0, 50, "dmRecipientIdent");
5563 INSERT_STRING(envelope, "dmRecipientIdent",
5564 outgoing_message->envelope->dmRecipientIdent);
5566 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
5567 0, 50, "dmSenderIdent");
5568 INSERT_STRING(envelope, "dmSenderIdent",
5569 outgoing_message->envelope->dmSenderIdent);
5571 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
5572 outgoing_message->envelope->dmLegalTitleLaw, string);
5573 INSERT_LONGINT(envelope, "dmLegalTitleYear",
5574 outgoing_message->envelope->dmLegalTitleYear, string);
5575 INSERT_STRING(envelope, "dmLegalTitleSect",
5576 outgoing_message->envelope->dmLegalTitleSect);
5577 INSERT_STRING(envelope, "dmLegalTitlePar",
5578 outgoing_message->envelope->dmLegalTitlePar);
5579 INSERT_STRING(envelope, "dmLegalTitlePoint",
5580 outgoing_message->envelope->dmLegalTitlePoint);
5582 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
5583 outgoing_message->envelope->dmPersonalDelivery);
5584 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
5585 outgoing_message->envelope->dmAllowSubstDelivery);
5587 /* ???: Should we require value for dbEffectiveOVM sender?
5588 * ISDS has default as true */
5589 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
5592 /* Append dmFiles */
5593 if (!outgoing_message->documents) {
5594 isds_log_message(context,
5595 _("Outgoing message is missing list of documents"));
5596 err = IE_INVAL;
5597 goto leave;
5599 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
5600 if (!dm_files) {
5601 isds_printf_message(context, _("Could not add dmFiles child to "
5602 "%s element"), create_message->name);
5603 err = IE_ERROR;
5604 goto leave;
5607 /* Check for document hieararchy */
5608 err = check_documents_hierarchy(context, outgoing_message->documents);
5609 if (err) goto leave;
5611 /* Process each document */
5612 for (struct isds_list *item =
5613 (struct isds_list *) outgoing_message->documents;
5614 item; item = item->next) {
5615 if (!item->data) {
5616 isds_log_message(context,
5617 _("List of documents contains empty item"));
5618 err = IE_INVAL;
5619 goto leave;
5621 /* FIXME: Check for dmFileMetaType and for document references.
5622 * Only first document can be of MAIN type */
5623 err = insert_document(context, (struct isds_document*) item->data,
5624 dm_files);
5626 if (err) goto leave;
5629 leave:
5630 free(string);
5631 return err;
5635 /* Send a message via ISDS to a recipent
5636 * @context is session context
5637 * @outgoing_message is message to send; Some memebers are mandatory (like
5638 * dbIDRecipient), some are optional and some are irrelevant (especialy data
5639 * about sender). Included pointer to isds_list documents must contain at
5640 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
5641 * members will be filled with valid data from ISDS. Exact list of write
5642 * members is subject to change. Currently dmId is changed.
5643 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
5644 isds_error isds_send_message(struct isds_ctx *context,
5645 struct isds_message *outgoing_message) {
5647 isds_error err = IE_SUCCESS;
5648 xmlNsPtr isds_ns = NULL;
5649 xmlNodePtr request = NULL;
5650 xmlDocPtr response = NULL;
5651 xmlChar *code = NULL, *message = NULL;
5652 xmlXPathContextPtr xpath_ctx = NULL;
5653 xmlXPathObjectPtr result = NULL;
5654 _Bool message_is_complete = 0;
5656 if (!context) return IE_INVALID_CONTEXT;
5657 if (!outgoing_message) return IE_INVAL;
5659 /* Check if connection is established
5660 * TODO: This check should be done donwstairs. */
5661 if (!context->curl) return IE_CONNECTION_CLOSED;
5664 /* Build CreateMessage request */
5665 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
5666 if (!request) {
5667 isds_log_message(context,
5668 _("Could build CreateMessage request"));
5669 return IE_ERROR;
5671 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5672 if(!isds_ns) {
5673 isds_log_message(context, _("Could not create ISDS name space"));
5674 xmlFreeNode(request);
5675 return IE_ERROR;
5677 xmlSetNs(request, isds_ns);
5679 /* Append envelope and files */
5680 err = insert_envelope_files(context, outgoing_message, request, 1);
5681 if (err) goto leave;
5684 /* Signal we can serilize message since now */
5685 message_is_complete = 1;
5688 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
5690 /* Sent request */
5691 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
5693 /* Dont' destroy request, we want to provide it to application later */
5695 if (err) {
5696 isds_log(ILF_ISDS, ILL_DEBUG,
5697 _("Processing ISDS response on CreateMessage "
5698 "request failed\n"));
5699 goto leave;
5702 /* Check for response status */
5703 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
5704 &code, &message, NULL);
5705 if (err) {
5706 isds_log(ILF_ISDS, ILL_DEBUG,
5707 _("ISDS response on CreateMessage request "
5708 "is missing status\n"));
5709 goto leave;
5712 /* Request processed, but refused by server or server failed */
5713 if (xmlStrcmp(code, BAD_CAST "0000")) {
5714 char *box_id_locale =
5715 utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
5716 char *code_locale = utf82locale((char*)code);
5717 char *message_locale = utf82locale((char*)message);
5718 isds_log(ILF_ISDS, ILL_DEBUG,
5719 _("Server did not accept message for %s on CreateMessage "
5720 "request (code=%s, message=%s)\n"),
5721 box_id_locale, code_locale, message_locale);
5722 isds_log_message(context, message_locale);
5723 free(box_id_locale);
5724 free(code_locale);
5725 free(message_locale);
5726 err = IE_ISDS;
5727 goto leave;
5731 /* Extract data */
5732 xpath_ctx = xmlXPathNewContext(response);
5733 if (!xpath_ctx) {
5734 err = IE_ERROR;
5735 goto leave;
5737 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5738 err = IE_ERROR;
5739 goto leave;
5741 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
5742 xpath_ctx);
5743 if (!result) {
5744 err = IE_ERROR;
5745 goto leave;
5747 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5748 isds_log_message(context, _("Missing CreateMessageResponse element"));
5749 err = IE_ISDS;
5750 goto leave;
5752 if (result->nodesetval->nodeNr > 1) {
5753 isds_log_message(context, _("Multiple CreateMessageResponse element"));
5754 err = IE_ISDS;
5755 goto leave;
5757 xpath_ctx->node = result->nodesetval->nodeTab[0];
5758 xmlXPathFreeObject(result); result = NULL;
5760 if (outgoing_message->envelope->dmID) {
5761 free(outgoing_message->envelope->dmID);
5762 outgoing_message->envelope->dmID = NULL;
5764 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
5765 if (!outgoing_message->envelope->dmID) {
5766 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
5767 "but did not returen assigned message ID\n"));
5770 leave:
5771 /* TODO: Serialize message into structure member raw */
5772 /* XXX: Each web service transport message in different format.
5773 * Therefore it's not possible to save them directly.
5774 * To save them, one must figure out common format.
5775 * We can leave it on application, or we can implement the ESS format. */
5776 /*if (message_is_complete) {
5777 if (outgoing_message->envelope->dmID) {
5779 /* Add assigned message ID as first child*/
5780 /*xmlNodePtr dmid_text = xmlNewText(
5781 (xmlChar *) outgoing_message->envelope->dmID);
5782 if (!dmid_text) goto serialization_failed;
5784 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
5785 BAD_CAST "dmID");
5786 if (!dmid_element) {
5787 xmlFreeNode(dmid_text);
5788 goto serialization_failed;
5791 xmlNodePtr dmid_element_with_text =
5792 xmlAddChild(dmid_element, dmid_text);
5793 if (!dmid_element_with_text) {
5794 xmlFreeNode(dmid_element);
5795 xmlFreeNode(dmid_text);
5796 goto serialization_failed;
5799 node = xmlAddPrevSibling(envelope->childern,
5800 dmid_element_with_text);
5801 if (!node) {
5802 xmlFreeNodeList(dmid_element_with_text);
5803 goto serialization_failed;
5807 /* Serialize message with ID into raw */
5808 /*buffer = serialize_element(envelope)*/
5809 /* }
5811 serialization_failed:
5815 /* Clean up */
5816 xmlXPathFreeObject(result);
5817 xmlXPathFreeContext(xpath_ctx);
5819 free(code);
5820 free(message);
5821 xmlFreeDoc(response);
5822 xmlFreeNode(request);
5824 if (!err)
5825 isds_log(ILF_ISDS, ILL_DEBUG,
5826 _("CreateMessage request processed by server "
5827 "successfully.\n"));
5829 return err;
5833 /* Send a message via ISDS to a multiple recipents
5834 * @context is session context
5835 * @outgoing_message is message to send; Some memebers are mandatory,
5836 * some are optional and some are irrelevant (especialy data
5837 * about sender). Data about recipient will be substituted by ISDS from
5838 * @copies. Included pointer to isds_list documents must
5839 * contain at least one document of FILEMETATYPE_MAIN.
5840 * @copies is list of isds_message_copy structures addressing all desired
5841 * recipients. This is read-write structure, some members will be filled with
5842 * valid data from ISDS (message IDs, error codes, error descriptions).
5843 * @return
5844 * ISDS_SUCCESS if all messages have been sent
5845 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
5846 * succesed messages can be identified by copies->data->error),
5847 * or other error code if something other goes wrong. */
5848 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
5849 const struct isds_message *outgoing_message,
5850 struct isds_list *copies) {
5852 isds_error err = IE_SUCCESS, append_err;
5853 xmlNsPtr isds_ns = NULL;
5854 xmlNodePtr request = NULL, recipients, recipient, node;
5855 struct isds_list *item;
5856 struct isds_message_copy *copy;
5857 xmlDocPtr response = NULL;
5858 xmlChar *code = NULL, *message = NULL;
5859 xmlXPathContextPtr xpath_ctx = NULL;
5860 xmlXPathObjectPtr result = NULL;
5861 xmlChar *string = NULL;
5862 int i;
5864 if (!context) return IE_INVALID_CONTEXT;
5865 if (!outgoing_message || !copies) return IE_INVAL;
5867 /* Check if connection is established
5868 * TODO: This check should be done donwstairs. */
5869 if (!context->curl) return IE_CONNECTION_CLOSED;
5872 /* Build CreateMultipleMessage request */
5873 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
5874 if (!request) {
5875 isds_log_message(context,
5876 _("Could build CreateMultipleMessage request"));
5877 return IE_ERROR;
5879 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5880 if(!isds_ns) {
5881 isds_log_message(context, _("Could not create ISDS name space"));
5882 xmlFreeNode(request);
5883 return IE_ERROR;
5885 xmlSetNs(request, isds_ns);
5888 /* Build recipients */
5889 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
5890 if (!recipients) {
5891 isds_log_message(context, _("Could not add dmRecipients child to "
5892 "CreateMultipleMessage element"));
5893 xmlFreeNode(request);
5894 return IE_ERROR;
5897 /* Insert each recipient */
5898 for (item = copies; item; item = item->next) {
5899 copy = (struct isds_message_copy *) item->data;
5900 if (!copy) {
5901 isds_log_message(context,
5902 _("copies list item contains empty data"));
5903 err = IE_INVAL;
5904 goto leave;
5907 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
5908 if (!recipient) {
5909 isds_log_message(context, _("Could not add dmRecipient child to "
5910 "dmRecipient element"));
5911 err = IE_ERROR;
5912 goto leave;
5915 if (!copy->dbIDRecipient) {
5916 isds_log_message(context,
5917 _("Message copy is missing recipient box identifier"));
5918 err = IE_INVAL;
5919 goto leave;
5921 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
5922 INSERT_STRING(recipient, "dmRecipientOrgUnit",
5923 copy->dmRecipientOrgUnit);
5924 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
5925 copy->dmRecipientOrgUnitNum, string);
5926 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
5929 /* Append envelope and files */
5930 err = insert_envelope_files(context, outgoing_message, request, 0);
5931 if (err) goto leave;
5934 isds_log(ILF_ISDS, ILL_DEBUG,
5935 _("Sending CreateMultipleMessage request to ISDS\n"));
5937 /* Sent request */
5938 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
5939 if (err) {
5940 isds_log(ILF_ISDS, ILL_DEBUG,
5941 _("Processing ISDS response on CreateMultipleMessage "
5942 "request failed\n"));
5943 goto leave;
5946 /* Check for response status */
5947 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
5948 &code, &message, NULL);
5949 if (err) {
5950 isds_log(ILF_ISDS, ILL_DEBUG,
5951 _("ISDS response on CreateMultipleMessage request "
5952 "is missing status\n"));
5953 goto leave;
5956 /* Request processed, but some copies failed */
5957 if (!xmlStrcmp(code, BAD_CAST "0004")) {
5958 char *box_id_locale =
5959 utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
5960 char *code_locale = utf82locale((char*)code);
5961 char *message_locale = utf82locale((char*)message);
5962 isds_log(ILF_ISDS, ILL_DEBUG,
5963 _("Server did accept message for multiple recipients "
5964 "on CreateMultipleMessage request but delivery to "
5965 "some of them failed (code=%s, message=%s)\n"),
5966 box_id_locale, code_locale, message_locale);
5967 isds_log_message(context, message_locale);
5968 free(box_id_locale);
5969 free(code_locale);
5970 free(message_locale);
5971 err = IE_PARTIAL_SUCCESS;
5974 /* Request refused by server as whole */
5975 else if (xmlStrcmp(code, BAD_CAST "0000")) {
5976 char *box_id_locale =
5977 utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
5978 char *code_locale = utf82locale((char*)code);
5979 char *message_locale = utf82locale((char*)message);
5980 isds_log(ILF_ISDS, ILL_DEBUG,
5981 _("Server did not accept message for multiple recipients "
5982 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
5983 box_id_locale, code_locale, message_locale);
5984 isds_log_message(context, message_locale);
5985 free(box_id_locale);
5986 free(code_locale);
5987 free(message_locale);
5988 err = IE_ISDS;
5989 goto leave;
5993 /* Extract data */
5994 xpath_ctx = xmlXPathNewContext(response);
5995 if (!xpath_ctx) {
5996 err = IE_ERROR;
5997 goto leave;
5999 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6000 err = IE_ERROR;
6001 goto leave;
6003 result = xmlXPathEvalExpression(
6004 BAD_CAST "/isds:CreateMultipleMessageResponse"
6005 "/isds:dmMultipleStatus/isds:dmSingleStatus",
6006 xpath_ctx);
6007 if (!result) {
6008 err = IE_ERROR;
6009 goto leave;
6011 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6012 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
6013 err = IE_ISDS;
6014 goto leave;
6017 /* Extract message ID and delivery status for each copy */
6018 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
6019 item = item->next, i++) {
6020 copy = (struct isds_message_copy *) item->data;
6021 xpath_ctx->node = result->nodesetval->nodeTab[i];
6023 append_err = append_TMStatus(context, copy, xpath_ctx);
6024 if (append_err) {
6025 err = append_err;
6026 goto leave;
6029 if (item || i < result->nodesetval->nodeNr) {
6030 isds_printf_message(context, _("ISDS returned unexpected number of "
6031 "message copy delivery states: %d"),
6032 result->nodesetval->nodeNr);
6033 err = IE_ISDS;
6034 goto leave;
6038 leave:
6039 /* Clean up */
6040 free(string);
6041 xmlXPathFreeObject(result);
6042 xmlXPathFreeContext(xpath_ctx);
6044 free(code);
6045 free(message);
6046 xmlFreeDoc(response);
6047 xmlFreeNode(request);
6049 if (!err)
6050 isds_log(ILF_ISDS, ILL_DEBUG,
6051 _("CreateMultipleMessageResponse request processed by server "
6052 "successfully.\n"));
6054 return err;
6058 /* Get list of messages. This is common core for getting sent or received
6059 * messaeges.
6060 * Any criterion argument can be NULL, if you don't care about it.
6061 * @context is session context. Must not be NULL.
6062 * @outgoing_direction is true if you want list of outgoing messages,
6063 * it's false if you want incoming messages.
6064 * @from_time is minimal time and date of message sending inclusive.
6065 * @to_time is maximal time and date of message sending inclusive
6066 * @organization_unit_number is number of sender/recipient respectively.
6067 * @status_filter is bit field of isds_message_status values. Use special
6068 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
6069 * all values, you can use bitwise arithmetic if you want.)
6070 * @offset is index of first message we are interested in. First message is 1.
6071 * Set to 0 (or 1) if you don't care.
6072 * @number is maximal length of list you want to get as input value, outputs
6073 * number of messages matching these criteria. Can be NULL if you don't care
6074 * (applies to output value either).
6075 * @messages is automatically reallocated list of isds_message's. Be ware that
6076 * it returns only brief overview (envelope and some other fields) about each
6077 * message, not the complete message. FIXME: Specify exact fields.
6078 * The list is sorted by delivery time in ascending order.
6079 * Use NULL if
6080 * you don't care about don't need the data (useful if you want to know only
6081 * the @number). If you provide &NULL, list will be allocated on heap, if you
6082 * provide pointer to non-NULL, list will be freed automacally at first. Also
6083 * in case of error the list will be NULLed.
6084 * @return IE_SUCCESS or appropriate error code. */
6085 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
6086 _Bool outgoing_direction,
6087 const struct timeval *from_time, const struct timeval *to_time,
6088 const long int *organization_unit_number,
6089 const unsigned int status_filter,
6090 const unsigned long int offset, unsigned long int *number,
6091 struct isds_list **messages) {
6093 isds_error err = IE_SUCCESS;
6094 xmlNsPtr isds_ns = NULL;
6095 xmlNodePtr request = NULL, node;
6096 xmlDocPtr response = NULL;
6097 xmlChar *code = NULL, *message = NULL;
6098 xmlXPathContextPtr xpath_ctx = NULL;
6099 xmlXPathObjectPtr result = NULL;
6100 xmlChar *string = NULL;
6101 long unsigned int count = 0;
6103 if (!context) return IE_INVALID_CONTEXT;
6105 /* Free former message list if any */
6106 if (messages) isds_list_free(messages);
6108 /* Check if connection is established
6109 * TODO: This check should be done donwstairs. */
6110 if (!context->curl) return IE_CONNECTION_CLOSED;
6112 /* Build GetListOf*Messages request */
6113 request = xmlNewNode(NULL,
6114 (outgoing_direction) ?
6115 BAD_CAST "GetListOfSentMessages" :
6116 BAD_CAST "GetListOfReceivedMessages"
6118 if (!request) {
6119 isds_log_message(context,
6120 (outgoing_direction) ?
6121 _("Could not build GetListOfSentMessages request") :
6122 _("Could not build GetListOfReceivedMessages request")
6124 return IE_ERROR;
6126 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6127 if(!isds_ns) {
6128 isds_log_message(context, _("Could not create ISDS name space"));
6129 xmlFreeNode(request);
6130 return IE_ERROR;
6132 xmlSetNs(request, isds_ns);
6135 if (from_time) {
6136 err = timeval2timestring(from_time, &string);
6137 if (err) goto leave;
6139 INSERT_STRING(request, "dmFromTime", string);
6140 free(string); string = NULL;
6142 if (to_time) {
6143 err = timeval2timestring(to_time, &string);
6144 if (err) goto leave;
6146 INSERT_STRING(request, "dmToTime", string);
6147 free(string); string = NULL;
6149 if (outgoing_direction) {
6150 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
6151 organization_unit_number, string);
6152 } else {
6153 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
6154 organization_unit_number, string);
6157 if (status_filter > MESSAGESTATE_ANY) {
6158 isds_printf_message(context,
6159 _("Invalid message state filter value: %ld"), status_filter);
6160 err = IE_INVAL;
6161 goto leave;
6163 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
6165 if (offset > 0 ) {
6166 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
6167 } else {
6168 INSERT_STRING(request, "dmOffset", "1");
6171 /* number 0 means no limit */
6172 if (number && *number == 0) {
6173 INSERT_STRING(request, "dmLimit", NULL);
6174 } else {
6175 INSERT_ULONGINT(request, "dmLimit", number, string);
6179 isds_log(ILF_ISDS, ILL_DEBUG,
6180 (outgoing_direction) ?
6181 _("Sending GetListOfSentMessages request to ISDS\n") :
6182 _("Sending GetListOfReceivedMessages request to ISDS\n")
6185 /* Sent request */
6186 err = isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
6187 xmlFreeNode(request); request = NULL;
6189 if (err) {
6190 isds_log(ILF_ISDS, ILL_DEBUG,
6191 (outgoing_direction) ?
6192 _("Processing ISDS response on GetListOfSentMessages "
6193 "request failed\n") :
6194 _("Processing ISDS response on GetListOfReceivedMessages "
6195 "request failed\n")
6197 goto leave;
6200 /* Check for response status */
6201 err = isds_response_status(context, SERVICE_DM_INFO, response,
6202 &code, &message, NULL);
6203 if (err) {
6204 isds_log(ILF_ISDS, ILL_DEBUG,
6205 (outgoing_direction) ?
6206 _("ISDS response on GetListOfSentMessages request "
6207 "is missing status\n") :
6208 _("ISDS response on GetListOfReceivedMessages request "
6209 "is missing status\n")
6211 goto leave;
6214 /* Request processed, but nothing found */
6215 if (xmlStrcmp(code, BAD_CAST "0000")) {
6216 char *code_locale = utf82locale((char*)code);
6217 char *message_locale = utf82locale((char*)message);
6218 isds_log(ILF_ISDS, ILL_DEBUG,
6219 (outgoing_direction) ?
6220 _("Server refused GetListOfSentMessages request "
6221 "(code=%s, message=%s)\n") :
6222 _("Server refused GetListOfReceivedMessages request "
6223 "(code=%s, message=%s)\n"),
6224 code_locale, message_locale);
6225 isds_log_message(context, message_locale);
6226 free(code_locale);
6227 free(message_locale);
6228 err = IE_ISDS;
6229 goto leave;
6233 /* Extract data */
6234 xpath_ctx = xmlXPathNewContext(response);
6235 if (!xpath_ctx) {
6236 err = IE_ERROR;
6237 goto leave;
6239 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6240 err = IE_ERROR;
6241 goto leave;
6243 result = xmlXPathEvalExpression(
6244 (outgoing_direction) ?
6245 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
6246 "isds:dmRecords/isds:dmRecord" :
6247 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
6248 "isds:dmRecords/isds:dmRecord",
6249 xpath_ctx);
6250 if (!result) {
6251 err = IE_ERROR;
6252 goto leave;
6255 /* Fill output arguments in */
6256 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6257 struct isds_envelope *envelope;
6258 struct isds_list *item = NULL, *last_item = NULL;
6260 for (count = 0; count < result->nodesetval->nodeNr; count++) {
6261 /* Create new message */
6262 item = calloc(1, sizeof(*item));
6263 if (!item) {
6264 err = IE_NOMEM;
6265 goto leave;
6267 item->destructor = (void(*)(void**)) &isds_message_free;
6268 item->data = calloc(1, sizeof(struct isds_message));
6269 if (!item->data) {
6270 isds_list_free(&item);
6271 err = IE_NOMEM;
6272 goto leave;
6275 /* Extract envelope data */
6276 xpath_ctx->node = result->nodesetval->nodeTab[count];
6277 envelope = NULL;
6278 err = extract_DmRecord(context, &envelope, xpath_ctx);
6279 if (err) {
6280 isds_list_free(&item);
6281 goto leave;
6284 /* Attach extracted envelope */
6285 ((struct isds_message *) item->data)->envelope = envelope;
6287 /* Append new message into the list */
6288 if (!*messages) {
6289 *messages = last_item = item;
6290 } else {
6291 last_item->next = item;
6292 last_item = item;
6296 if (number) *number = count;
6298 leave:
6299 if (err) {
6300 isds_list_free(messages);
6303 free(string);
6304 xmlXPathFreeObject(result);
6305 xmlXPathFreeContext(xpath_ctx);
6307 free(code);
6308 free(message);
6309 xmlFreeDoc(response);
6310 xmlFreeNode(request);
6312 if (!err)
6313 isds_log(ILF_ISDS, ILL_DEBUG,
6314 (outgoing_direction) ?
6315 _("GetListOfSentMessages request processed by server "
6316 "successfully.\n") :
6317 _("GetListOfReceivedMessages request processed by server "
6318 "successfully.\n")
6320 return err;
6324 /* Get list of outgoing (already sent) messages.
6325 * Any criterion argument can be NULL, if you don't care about it.
6326 * @context is session context. Must not be NULL.
6327 * @from_time is minimal time and date of message sending inclusive.
6328 * @to_time is maximal time and date of message sending inclusive
6329 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
6330 * @status_filter is bit field of isds_message_status values. Use special
6331 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
6332 * all values, you can use bitwise arithmetic if you want.)
6333 * @offset is index of first message we are interested in. First message is 1.
6334 * Set to 0 (or 1) if you don't care.
6335 * @number is maximal length of list you want to get as input value, outputs
6336 * number of messages matching these criteria. Can be NULL if you don't care
6337 * (applies to output value either).
6338 * @messages is automatically reallocated list of isds_message's. Be ware that
6339 * it returns only brief overview (envelope and some other fields) about each
6340 * message, not the complete message. FIXME: Specify exact fields.
6341 * The list is sorted by delivery time in ascending order.
6342 * Use NULL if you don't care about the metadata (useful if you want to know
6343 * only the @number). If you provide &NULL, list will be allocated on heap,
6344 * if you provide pointer to non-NULL, list will be freed automacally at first.
6345 * Also in case of error the list will be NULLed.
6346 * @return IE_SUCCESS or appropriate error code. */
6347 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
6348 const struct timeval *from_time, const struct timeval *to_time,
6349 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
6350 const unsigned long int offset, unsigned long int *number,
6351 struct isds_list **messages) {
6353 return isds_get_list_of_messages(
6354 context, 1,
6355 from_time, to_time, dmSenderOrgUnitNum, status_filter,
6356 offset, number,
6357 messages);
6361 /* Get list of incoming (addressed to you) messages.
6362 * Any criterion argument can be NULL, if you don't care about it.
6363 * @context is session context. Must not be NULL.
6364 * @from_time is minimal time and date of message sending inclusive.
6365 * @to_time is maximal time and date of message sending inclusive
6366 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
6367 * @status_filter is bit field of isds_message_status values. Use special
6368 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
6369 * all values, you can use bitwise arithmetic if you want.)
6370 * @offset is index of first message we are interested in. First message is 1.
6371 * Set to 0 (or 1) if you don't care.
6372 * @number is maximal length of list you want to get as input value, outputs
6373 * number of messages matching these criteria. Can be NULL if you don't care
6374 * (applies to output value either).
6375 * @messages is automatically reallocated list of isds_message's. Be ware that
6376 * it returns only brief overview (envelope and some other fields) about each
6377 * message, not the complete message. FIXME: Specify exact fields.
6378 * Use NULL if you don't care about the metadata (useful if you want to know
6379 * only the @number). If you provide &NULL, list will be allocated on heap,
6380 * if you provide pointer to non-NULL, list will be freed automacally at first.
6381 * Also in case of error the list will be NULLed.
6382 * @return IE_SUCCESS or appropriate error code. */
6383 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
6384 const struct timeval *from_time, const struct timeval *to_time,
6385 const long int *dmRecipientOrgUnitNum,
6386 const unsigned int status_filter,
6387 const unsigned long int offset, unsigned long int *number,
6388 struct isds_list **messages) {
6390 return isds_get_list_of_messages(
6391 context, 0,
6392 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
6393 offset, number,
6394 messages);
6398 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
6399 * code
6400 * @context is session context
6401 * @service is ISDS WS service handler
6402 * @service_name is name of SERVICE_DM_OPERATIONS
6403 * @message_id is message ID to send as service argument to ISDS
6404 * @response is server SOAP body response as XML document
6405 * @raw_response is automatically reallocated bitstream with response body. Use
6406 * NULL if you don't care
6407 * @raw_response_length is size of @raw_response in bytes
6408 * @code is ISDS status code
6409 * @status_message is ISDS status message
6410 * @return error coded from lower layer, context message will be set up
6411 * appropriately. */
6412 static isds_error build_send_check_message_request(struct isds_ctx *context,
6413 const isds_service service, const xmlChar *service_name,
6414 const char *message_id,
6415 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
6416 xmlChar **code, xmlChar **status_message) {
6418 isds_error err = IE_SUCCESS;
6419 char *service_name_locale = NULL, *message_id_locale = NULL;
6420 xmlNodePtr request = NULL, node;
6421 xmlNsPtr isds_ns = NULL;
6423 if (!context) return IE_INVALID_CONTEXT;
6424 if (!service_name || !message_id) return IE_INVAL;
6425 if (!response || !code || !status_message) return IE_INVAL;
6426 if (!raw_response_length && raw_response) return IE_INVAL;
6428 /* Free output argument */
6429 xmlFreeDoc(*response); *response = NULL;
6430 if (raw_response) zfree(*raw_response);
6431 free(*code);
6432 free(*status_message);
6435 /* Check if connection is established
6436 * TODO: This check should be done donwstairs. */
6437 if (!context->curl) return IE_CONNECTION_CLOSED;
6439 service_name_locale = utf82locale((char*)service_name);
6440 message_id_locale = utf82locale(message_id);
6441 if (!service_name_locale || !message_id_locale) {
6442 err = IE_NOMEM;
6443 goto leave;
6446 /* Build request */
6447 request = xmlNewNode(NULL, service_name);
6448 if (!request) {
6449 isds_printf_message(context,
6450 _("Could not build %s request"), service_name_locale);
6451 err = IE_ERROR;
6452 goto leave;
6454 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6455 if(!isds_ns) {
6456 isds_log_message(context, _("Could not create ISDS name space"));
6457 err = IE_ERROR;
6458 goto leave;
6460 xmlSetNs(request, isds_ns);
6463 /* Add requested ID */
6464 err = validate_message_id_length(context, (xmlChar *) message_id);
6465 if (err) goto leave;
6466 INSERT_STRING(request, "dmID", message_id);
6469 isds_log(ILF_ISDS, ILL_DEBUG,
6470 _("Sending %s request for %s message ID to ISDS\n"),
6471 service_name_locale, message_id_locale);
6473 /* Send request */
6474 err = isds(context, service, request, response,
6475 raw_response, raw_response_length);
6476 xmlFreeNode(request); request = NULL;
6478 if (err) {
6479 isds_log(ILF_ISDS, ILL_DEBUG,
6480 _("Processing ISDS response on %s request failed\n"),
6481 service_name_locale);
6482 goto leave;
6485 /* Check for response status */
6486 err = isds_response_status(context, service, *response,
6487 code, status_message, NULL);
6488 if (err) {
6489 isds_log(ILF_ISDS, ILL_DEBUG,
6490 _("ISDS response on %s request is missing status\n"),
6491 service_name_locale);
6492 goto leave;
6495 /* Request processed, but nothing found */
6496 if (xmlStrcmp(*code, BAD_CAST "0000")) {
6497 char *code_locale = utf82locale((char*) *code);
6498 char *status_message_locale = utf82locale((char*) *status_message);
6499 isds_log(ILF_ISDS, ILL_DEBUG,
6500 _("Server refused %s request for %s message ID "
6501 "(code=%s, message=%s)\n"),
6502 service_name_locale, message_id_locale,
6503 code_locale, status_message_locale);
6504 isds_log_message(context, status_message_locale);
6505 free(code_locale);
6506 free(status_message_locale);
6507 err = IE_ISDS;
6508 goto leave;
6511 leave:
6512 free(message_id_locale);
6513 free(service_name_locale);
6514 xmlFreeNode(request);
6515 return err;
6519 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
6520 * signed data and free ISDS response.
6521 * @context is session context
6522 * @message_id is UTF-8 encoded message ID for loging purpose
6523 * @response is parsed XML document. It will be freed and NULLed in the middle
6524 * of function run to save memmory. This is not guaranted in case of error.
6525 * @request_name is name of ISDS request used to construct response root
6526 * element name and for logging purpose.
6527 * @raw is reallocated output buffer with DER encoded CMS data
6528 * @raw_length is size of @raw buffer in bytes
6529 * @returns standard error codes, in case of error, @raw will be freed and
6530 * NULLed, @response sometimes. */
6531 static isds_error find_extract_signed_data_free_response(
6532 struct isds_ctx *context, const xmlChar *message_id,
6533 xmlDocPtr *response, const xmlChar *request_name,
6534 void **raw, size_t *raw_length) {
6536 isds_error err = IE_SUCCESS;
6537 char *xpath_expression = NULL;
6538 xmlXPathContextPtr xpath_ctx = NULL;
6539 xmlXPathObjectPtr result = NULL;
6540 char *encoded_structure = NULL;
6542 if (!context) return IE_INVALID_CONTEXT;
6543 if (!raw) return IE_INVAL;
6544 zfree(*raw);
6545 if (!message_id || !response || !*response || !request_name || !raw_length)
6546 return IE_INVAL;
6548 /* Build XPath expression */
6549 xpath_expression = astrcat3("/isds:", (char *) request_name,
6550 "Response/isds:dmSignature");
6551 if (!xpath_expression) return IE_NOMEM;
6553 /* Extract data */
6554 xpath_ctx = xmlXPathNewContext(*response);
6555 if (!xpath_ctx) {
6556 err = IE_ERROR;
6557 goto leave;
6559 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6560 err = IE_ERROR;
6561 goto leave;
6563 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
6564 if (!result) {
6565 err = IE_ERROR;
6566 goto leave;
6568 /* Empty response */
6569 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6570 char *message_id_locale = utf82locale((char*) message_id);
6571 isds_printf_message(context,
6572 _("Server did not return any signed data for mesage ID `%s' "
6573 "on %s request"),
6574 message_id_locale, request_name);
6575 free(message_id_locale);
6576 err = IE_ISDS;
6577 goto leave;
6579 /* More reponses */
6580 if (result->nodesetval->nodeNr > 1) {
6581 char *message_id_locale = utf82locale((char*) message_id);
6582 isds_printf_message(context,
6583 _("Server did return more signed data for message ID `%s' "
6584 "on %s request"),
6585 message_id_locale, request_name);
6586 free(message_id_locale);
6587 err = IE_ISDS;
6588 goto leave;
6590 /* One response */
6591 xpath_ctx->node = result->nodesetval->nodeTab[0];
6593 /* Extract PKCS#7 structure */
6594 EXTRACT_STRING(".", encoded_structure);
6595 if (!encoded_structure) {
6596 isds_log_message(context, _("dmSignature element is empty"));
6599 /* Here we have delivery info as standalone CMS in encoded_structure.
6600 * We don't need any other data, free them: */
6601 xmlXPathFreeObject(result); result = NULL;
6602 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
6603 xmlFreeDoc(*response); *response = NULL;
6606 /* Decode PKCS#7 to DER format */
6607 *raw_length = b64decode(encoded_structure, raw);
6608 if (*raw_length == (size_t) -1) {
6609 isds_log_message(context,
6610 _("Error while Base64-decoding PKCS#7 structure"));
6611 err = IE_ERROR;
6612 goto leave;
6615 leave:
6616 if (err) {
6617 zfree(*raw);
6618 raw_length = 0;
6621 free(encoded_structure);
6622 xmlXPathFreeObject(result);
6623 xmlXPathFreeContext(xpath_ctx);
6624 free(xpath_expression);
6626 return err;
6630 /* Download incoming message envelope identified by ID.
6631 * @context is session context
6632 * @message_id is message identifier (you can get them from
6633 * isds_get_list_of_received_messages())
6634 * @message is automatically reallocated message retrieved from ISDS.
6635 * It will miss documents per se. Use isds_get_received_message(), if you are
6636 * interrested in documents (content) too.
6637 * Returned hash and timestamp require documents to be verifiable. */
6638 isds_error isds_get_received_envelope(struct isds_ctx *context,
6639 const char *message_id, struct isds_message **message) {
6641 isds_error err = IE_SUCCESS;
6642 xmlDocPtr response = NULL;
6643 xmlChar *code = NULL, *status_message = NULL;
6644 xmlXPathContextPtr xpath_ctx = NULL;
6645 xmlXPathObjectPtr result = NULL;
6647 if (!context) return IE_INVALID_CONTEXT;
6649 /* Free former message if any */
6650 if (!message) return IE_INVAL;
6651 isds_message_free(message);
6653 /* Do request and check for success */
6654 err = build_send_check_message_request(context, SERVICE_DM_INFO,
6655 BAD_CAST "MessageEnvelopeDownload", message_id,
6656 &response, NULL, NULL, &code, &status_message);
6657 if (err) goto leave;
6659 /* Extract data */
6660 xpath_ctx = xmlXPathNewContext(response);
6661 if (!xpath_ctx) {
6662 err = IE_ERROR;
6663 goto leave;
6665 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6666 err = IE_ERROR;
6667 goto leave;
6669 result = xmlXPathEvalExpression(
6670 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
6671 "isds:dmReturnedMessageEnvelope",
6672 xpath_ctx);
6673 if (!result) {
6674 err = IE_ERROR;
6675 goto leave;
6677 /* Empty response */
6678 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6679 char *message_id_locale = utf82locale((char*) message_id);
6680 isds_printf_message(context,
6681 _("Server did not return any envelope for ID `%s' "
6682 "on MessageEnvelopeDownload request"), message_id_locale);
6683 free(message_id_locale);
6684 err = IE_ISDS;
6685 goto leave;
6687 /* More envelops */
6688 if (result->nodesetval->nodeNr > 1) {
6689 char *message_id_locale = utf82locale((char*) message_id);
6690 isds_printf_message(context,
6691 _("Server did return more envelopes for ID `%s' "
6692 "on MessageEnvelopeDownload request"), message_id_locale);
6693 free(message_id_locale);
6694 err = IE_ISDS;
6695 goto leave;
6697 /* One message */
6698 xpath_ctx->node = result->nodesetval->nodeTab[0];
6700 /* Extract the envelope (= message without documents, hence 0) */
6701 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
6702 if (err) goto leave;
6704 /* Save XML blob */
6705 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
6706 &(*message)->raw_length);
6708 leave:
6709 if (err) {
6710 isds_message_free(message);
6713 xmlXPathFreeObject(result);
6714 xmlXPathFreeContext(xpath_ctx);
6716 free(code);
6717 free(status_message);
6718 xmlFreeDoc(response);
6720 if (!err)
6721 isds_log(ILF_ISDS, ILL_DEBUG,
6722 _("MessageEnvelopeDownload request processed by server "
6723 "successfully.\n")
6725 return err;
6729 /* Load delivery info of any format from buffer.
6730 * @context is session context
6731 * @raw_type advertises format of @buffer content. Only delivery info types
6732 * are accepted.
6733 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
6734 * retrieve such data from message->raw after calling
6735 * isds_get_signed_delivery_info().
6736 * @length is length of buffer in bytes.
6737 * @message is automatically reallocated message parsed from @buffer.
6738 * @strategy selects how buffer will be attached into raw isds_message member.
6739 * */
6740 isds_error isds_load_delivery_info(struct isds_ctx *context,
6741 const isds_raw_type raw_type,
6742 const void *buffer, const size_t length,
6743 struct isds_message **message, const isds_buffer_strategy strategy) {
6745 isds_error err = IE_SUCCESS;
6746 message_ns_type message_ns;
6747 xmlDocPtr message_doc = NULL;
6748 xmlXPathContextPtr xpath_ctx = NULL;
6749 xmlXPathObjectPtr result = NULL;
6750 void *xml_stream = NULL;
6751 size_t xml_stream_length = 0;
6753 if (!context) return IE_INVALID_CONTEXT;
6754 if (!message) return IE_INVAL;
6755 isds_message_free(message);
6756 if (!buffer) return IE_INVAL;
6759 /* Select buffer format and extract XML from CMS*/
6760 switch (raw_type) {
6761 case RAWTYPE_DELIVERYINFO:
6762 message_ns = MESSAGE_NS_UNSIGNED;
6763 xml_stream = (void *) buffer;
6764 xml_stream_length = length;
6765 break;
6767 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
6768 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
6769 xml_stream = (void *) buffer;
6770 xml_stream_length = length;
6771 break;
6773 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
6774 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
6775 err = extract_cms_data(context, buffer, length,
6776 &xml_stream, &xml_stream_length);
6777 if (err) goto leave;
6778 break;
6780 default:
6781 isds_log_message(context, _("Bad raw delivery representation type"));
6782 return IE_INVAL;
6783 break;
6786 isds_log(ILF_ISDS, ILL_DEBUG,
6787 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
6788 xml_stream_length, xml_stream);
6790 /* Convert delivery info XML stream into XPath context */
6791 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
6792 if (!message_doc) {
6793 err = IE_XML;
6794 goto leave;
6796 xpath_ctx = xmlXPathNewContext(message_doc);
6797 if (!xpath_ctx) {
6798 err = IE_ERROR;
6799 goto leave;
6801 /* XXX: Name spaces mangled for signed delivery info:
6802 * http://isds.czechpoint.cz/v20/delivery:
6804 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
6805 * <q:dmDelivery>
6806 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
6807 * <p:dmID>170272</p:dmID>
6808 * ...
6809 * </p:dmDm>
6810 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
6811 * ...
6812 * </q:dmEvents>...</q:dmEvents>
6813 * </q:dmDelivery>
6814 * </q:GetDeliveryInfoResponse>
6815 * */
6816 if (register_namespaces(xpath_ctx, message_ns)) {
6817 err = IE_ERROR;
6818 goto leave;
6820 result = xmlXPathEvalExpression(
6821 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
6822 xpath_ctx);
6823 if (!result) {
6824 err = IE_ERROR;
6825 goto leave;
6827 /* Empty delivery info */
6828 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6829 isds_printf_message(context,
6830 _("XML document ss not sisds:dmDelivery document"));
6831 err = IE_ISDS;
6832 goto leave;
6834 /* More delivery infos */
6835 if (result->nodesetval->nodeNr > 1) {
6836 isds_printf_message(context,
6837 _("XML document has more sisds:dmDelivery elements"));
6838 err = IE_ISDS;
6839 goto leave;
6841 /* One delivery info */
6842 xpath_ctx->node = result->nodesetval->nodeTab[0];
6844 /* Extract the envelope (= message without documents, hence 0).
6845 * XXX: extract_TReturnedMessage() can obtain attachments size,
6846 * but delivery info carries none. It's coded as option elements,
6847 * so it should work. */
6848 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
6849 if (err) goto leave;
6851 /* Extract events */
6852 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
6853 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
6854 if (err) { err = IE_ERROR; goto leave; }
6855 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
6856 if (err) goto leave;
6858 /* Append raw CMS structure into message */
6859 (*message)->raw_type = raw_type;
6860 switch (strategy) {
6861 case BUFFER_DONT_STORE:
6862 break;
6863 case BUFFER_COPY:
6864 (*message)->raw = malloc(length);
6865 if (!(*message)->raw) {
6866 err = IE_NOMEM;
6867 goto leave;
6869 memcpy((*message)->raw, buffer, length);
6870 (*message)->raw_length = length;
6871 break;
6872 case BUFFER_MOVE:
6873 (*message)->raw = (void *) buffer;
6874 (*message)->raw_length = length;
6875 break;
6876 default:
6877 err = IE_ENUM;
6878 goto leave;
6881 leave:
6882 if (err) {
6883 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
6884 isds_message_free(message);
6887 xmlXPathFreeObject(result);
6888 xmlXPathFreeContext(xpath_ctx);
6889 xmlFreeDoc(message_doc);
6890 if (xml_stream != buffer) cms_data_free(xml_stream);
6892 if (!err)
6893 isds_log(ILF_ISDS, ILL_DEBUG,
6894 _("Delivery info loaded successfully.\n"));
6895 return err;
6899 /* Download signed delivery infosheet of given message identified by ID.
6900 * @context is session context
6901 * @message_id is message identifier (you can get them from
6902 * isds_get_list_of_{sent,received}_messages())
6903 * @message is automatically reallocated message retrieved from ISDS.
6904 * It will miss documents per se. Use isds_get_signed_received_message(),
6905 * if you are interrested in documents (content). OTOH, only this function
6906 * can get list events message has gone through. */
6907 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
6908 const char *message_id, struct isds_message **message) {
6910 isds_error err = IE_SUCCESS;
6911 xmlDocPtr response = NULL;
6912 xmlChar *code = NULL, *status_message = NULL;
6913 void *raw = NULL;
6914 size_t raw_length = 0;
6916 if (!context) return IE_INVALID_CONTEXT;
6918 /* Free former message if any */
6919 if (!message) return IE_INVAL;
6920 isds_message_free(message);
6922 /* Do request and check for success */
6923 err = build_send_check_message_request(context, SERVICE_DM_INFO,
6924 BAD_CAST "GetSignedDeliveryInfo", message_id,
6925 &response, NULL, NULL, &code, &status_message);
6926 if (err) goto leave;
6928 /* Find signed delivery info, extract it into raw and maybe free
6929 * response */
6930 err = find_extract_signed_data_free_response(context,
6931 (xmlChar *)message_id, &response,
6932 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
6933 if (err) goto leave;
6935 /* Parse delivery info */
6936 err = isds_load_delivery_info(context,
6937 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
6938 message, BUFFER_MOVE);
6939 if (err) goto leave;
6941 raw = NULL;
6943 leave:
6944 if (err) {
6945 isds_message_free(message);
6948 free(raw);
6949 free(code);
6950 free(status_message);
6951 xmlFreeDoc(response);
6953 if (!err)
6954 isds_log(ILF_ISDS, ILL_DEBUG,
6955 _("GetSignedDeliveryInfo request processed by server "
6956 "successfully.\n")
6958 return err;
6962 /* Download delivery infosheet of given message identified by ID.
6963 * @context is session context
6964 * @message_id is message identifier (you can get them from
6965 * isds_get_list_of_{sent,received}_messages())
6966 * @message is automatically reallocated message retrieved from ISDS.
6967 * It will miss documents per se. Use isds_get_received_message(), if you are
6968 * interrested in documents (content). OTOH, only this function can get list
6969 * events message has gone through. */
6970 isds_error isds_get_delivery_info(struct isds_ctx *context,
6971 const char *message_id, struct isds_message **message) {
6973 isds_error err = IE_SUCCESS;
6974 xmlDocPtr response = NULL;
6975 xmlChar *code = NULL, *status_message = NULL;
6976 xmlXPathContextPtr xpath_ctx = NULL;
6977 xmlXPathObjectPtr result = NULL;
6978 xmlNodePtr delivery_node = NULL;
6980 if (!context) return IE_INVALID_CONTEXT;
6982 /* Free former message if any */
6983 if (!message) return IE_INVAL;
6984 isds_message_free(message);
6986 /* Do request and check for success */
6987 err = build_send_check_message_request(context, SERVICE_DM_INFO,
6988 BAD_CAST "GetDeliveryInfo", message_id,
6989 &response, NULL, NULL, &code, &status_message);
6990 if (err) goto leave;
6992 /* Extract data */
6993 xpath_ctx = xmlXPathNewContext(response);
6994 if (!xpath_ctx) {
6995 err = IE_ERROR;
6996 goto leave;
6998 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6999 err = IE_ERROR;
7000 goto leave;
7002 result = xmlXPathEvalExpression(
7003 BAD_CAST "/isds:GetDeliveryInfoResponse/isds:dmDelivery",
7004 xpath_ctx);
7005 if (!result) {
7006 err = IE_ERROR;
7007 goto leave;
7009 /* Empty response */
7010 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7011 char *message_id_locale = utf82locale((char*) message_id);
7012 isds_printf_message(context,
7013 _("Server did not return any delivery info for ID `%s' "
7014 "on GetDeliveryInfo request"), message_id_locale);
7015 free(message_id_locale);
7016 err = IE_ISDS;
7017 goto leave;
7019 /* More delivery infos */
7020 if (result->nodesetval->nodeNr > 1) {
7021 char *message_id_locale = utf82locale((char*) message_id);
7022 isds_printf_message(context,
7023 _("Server did return more delivery infos for ID `%s' "
7024 "on GetDeliveryInfo request"), message_id_locale);
7025 free(message_id_locale);
7026 err = IE_ISDS;
7027 goto leave;
7029 /* One delivery info */
7030 xpath_ctx->node = delivery_node = result->nodesetval->nodeTab[0];
7032 /* Extract the envelope (= message without documents, hence 0).
7033 * XXX: extract_TReturnedMessage() can obtain attachments size,
7034 * but delivery info carries none. It's coded as option elements,
7035 * so it should work. */
7036 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
7037 if (err) goto leave;
7039 /* Extract events */
7040 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmEvents", xpath_ctx);
7041 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
7042 if (err) { err = IE_ERROR; goto leave; }
7043 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
7044 if (err) goto leave;
7046 /* Save XML blob */
7047 err = serialize_subtree(context, delivery_node, &(*message)->raw,
7048 &(*message)->raw_length);
7050 leave:
7051 if (err) {
7052 isds_message_free(message);
7055 xmlXPathFreeObject(result);
7056 xmlXPathFreeContext(xpath_ctx);
7058 free(code);
7059 free(status_message);
7060 xmlFreeDoc(response);
7062 if (!err)
7063 isds_log(ILF_ISDS, ILL_DEBUG,
7064 _("GetDeliveryInfo request processed by server "
7065 "successfully.\n")
7067 return err;
7071 /* Load incoming message from buffer.
7072 * @context is session context
7073 * @buffer XML stream with unsigned message. You can retrieve such data from
7074 * message->raw after calling isds_get_received_message().
7075 * @length is length of buffer in bytes.
7076 * @message is automatically reallocated message parsed from @buffer.
7077 * @strategy selects how buffer will be attached into raw isds_message member.
7078 * */
7079 isds_error isds_load_received_message(struct isds_ctx *context,
7080 const void *buffer, const size_t length,
7081 struct isds_message **message, const isds_buffer_strategy strategy) {
7083 isds_error err = IE_SUCCESS;
7084 xmlDocPtr message_doc = NULL;
7085 xmlXPathContextPtr xpath_ctx = NULL;
7086 xmlXPathObjectPtr result = NULL;
7088 if (!context) return IE_INVALID_CONTEXT;
7089 if (!message) return IE_INVAL;
7090 isds_message_free(message);
7091 if (!buffer) return IE_INVAL;
7094 isds_log(ILF_ISDS, ILL_DEBUG,
7095 _("Incoming message content:\n%.*s\nEnd of message\n"),
7096 length, buffer);
7098 /* Convert extracted messages XML stream into XPath context */
7099 message_doc = xmlParseMemory(buffer, length);
7100 if (!message_doc) {
7101 err = IE_XML;
7102 goto leave;
7104 xpath_ctx = xmlXPathNewContext(message_doc);
7105 if (!xpath_ctx) {
7106 err = IE_ERROR;
7107 goto leave;
7109 /* XXX: Standard name space */
7110 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7111 err = IE_ERROR;
7112 goto leave;
7114 result = xmlXPathEvalExpression(
7115 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
7116 xpath_ctx);
7117 if (!result) {
7118 err = IE_ERROR;
7119 goto leave;
7121 /* Missing dmReturnedMessage */
7122 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7123 isds_printf_message(context,
7124 _("XML document does not contain isds:dmReturnedMessage "
7125 "element"));
7126 err = IE_ISDS;
7127 goto leave;
7129 /* More elements. This should never happen. */
7130 if (result->nodesetval->nodeNr > 1) {
7131 isds_printf_message(context,
7132 _("XML document has more isds:dmReturnedMessage elements"));
7133 err = IE_ISDS;
7134 goto leave;
7136 /* One message */
7137 xpath_ctx->node = result->nodesetval->nodeTab[0];
7139 /* Extract the message */
7140 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
7141 if (err) goto leave;
7143 /* Append XML stream into message */
7144 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
7145 switch (strategy) {
7146 case BUFFER_DONT_STORE:
7147 break;
7148 case BUFFER_COPY:
7149 (*message)->raw = malloc(length);
7150 if (!(*message)->raw) {
7151 err = IE_NOMEM;
7152 goto leave;
7154 memcpy((*message)->raw, buffer, length);
7155 (*message)->raw_length = length;
7156 break;
7157 case BUFFER_MOVE:
7158 (*message)->raw = (void *) buffer;
7159 (*message)->raw_length = length;
7160 break;
7161 default:
7162 err = IE_ENUM;
7163 goto leave;
7166 leave:
7167 if (err) {
7168 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
7169 isds_message_free(message);
7172 xmlFreeDoc(message_doc);
7173 xmlXPathFreeObject(result);
7174 xmlXPathFreeContext(xpath_ctx);
7176 if (!err)
7177 isds_log(ILF_ISDS, ILL_DEBUG,
7178 _("Incoming message loaded successfully.\n"));
7179 return err;
7183 /* Download incoming message identified by ID.
7184 * @context is session context
7185 * @message_id is message identifier (you can get them from
7186 * isds_get_list_of_received_messages())
7187 * @message is automatically reallocated message retrieved from ISDS */
7188 isds_error isds_get_received_message(struct isds_ctx *context,
7189 const char *message_id, struct isds_message **message) {
7191 isds_error err = IE_SUCCESS;
7192 xmlDocPtr response = NULL;
7193 void *xml_stream = NULL;
7194 size_t xml_stream_length;
7195 xmlChar *code = NULL, *status_message = NULL;
7196 xmlXPathContextPtr xpath_ctx = NULL;
7197 xmlXPathObjectPtr result = NULL;
7198 char *phys_path = NULL;
7199 size_t phys_start, phys_end;
7201 if (!context) return IE_INVALID_CONTEXT;
7203 /* Free former message if any */
7204 if (message) isds_message_free(message);
7206 /* Do request and check for success */
7207 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
7208 BAD_CAST "MessageDownload", message_id,
7209 &response, &xml_stream, &xml_stream_length,
7210 &code, &status_message);
7211 if (err) goto leave;
7213 /* Extract data */
7214 xpath_ctx = xmlXPathNewContext(response);
7215 if (!xpath_ctx) {
7216 err = IE_ERROR;
7217 goto leave;
7219 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7220 err = IE_ERROR;
7221 goto leave;
7223 result = xmlXPathEvalExpression(
7224 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
7225 xpath_ctx);
7226 if (!result) {
7227 err = IE_ERROR;
7228 goto leave;
7230 /* Empty response */
7231 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7232 char *message_id_locale = utf82locale((char*) message_id);
7233 isds_printf_message(context,
7234 _("Server did not return any message for ID `%s' "
7235 "on MessageDownload request"), message_id_locale);
7236 free(message_id_locale);
7237 err = IE_ISDS;
7238 goto leave;
7240 /* More messages */
7241 if (result->nodesetval->nodeNr > 1) {
7242 char *message_id_locale = utf82locale((char*) message_id);
7243 isds_printf_message(context,
7244 _("Server did return more messages for ID `%s' "
7245 "on MessageDownload request"), message_id_locale);
7246 free(message_id_locale);
7247 err = IE_ISDS;
7248 goto leave;
7250 /* One message */
7251 xpath_ctx->node = result->nodesetval->nodeTab[0];
7253 /* Extract the message */
7254 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
7255 if (err) goto leave;
7257 /* Locate raw XML blob */
7258 phys_path = strdup(
7259 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
7260 PHYSXML_ELEMENT_SEPARATOR
7261 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
7262 PHYSXML_ELEMENT_SEPARATOR
7263 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
7265 if (!phys_path) {
7266 err = IE_NOMEM;
7267 goto leave;
7269 err = find_element_boundary(xml_stream, xml_stream_length,
7270 phys_path, &phys_start, &phys_end);
7271 zfree(phys_path);
7272 if (err) {
7273 isds_log_message(context,
7274 _("Substring with isds:MessageDownloadResponse element "
7275 "could not be located in raw SOAP message"));
7276 goto leave;
7278 /* Save XML blob */
7279 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
7280 &(*message)->raw_length);*/
7281 /* TODO: Store name space declarations from ancestors */
7282 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
7283 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
7284 (*message)->raw_length = phys_end - phys_start + 1;
7285 (*message)->raw = malloc((*message)->raw_length);
7286 if (!(*message)->raw) {
7287 err = IE_NOMEM;
7288 goto leave;
7290 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
7293 leave:
7294 if (err) {
7295 isds_message_free(message);
7298 free(phys_path);
7300 xmlXPathFreeObject(result);
7301 xmlXPathFreeContext(xpath_ctx);
7303 free(code);
7304 free(status_message);
7305 free(xml_stream);
7306 xmlFreeDoc(response);
7308 if (!err)
7309 isds_log(ILF_ISDS, ILL_DEBUG,
7310 _("MessageDownload request processed by server "
7311 "successfully.\n")
7313 return err;
7317 /* Load signed message from buffer.
7318 * @context is session context
7319 * @outgoing is true if message is outgoing, false if message is incoming
7320 * @buffer is DER encoded PKCS#7 structure with signed message. You can
7321 * retrieve such data from message->raw after calling
7322 * isds_get_signed_{received,sent}_message().
7323 * @length is length of buffer in bytes.
7324 * @message is automatically reallocated message parsed from @buffer.
7325 * @strategy selects how buffer will be attached into raw isds_message member.
7326 * */
7327 isds_error isds_load_signed_message(struct isds_ctx *context,
7328 const _Bool outgoing, const void *buffer, const size_t length,
7329 struct isds_message **message, const isds_buffer_strategy strategy) {
7331 isds_error err = IE_SUCCESS;
7332 xmlDocPtr message_doc = NULL;
7333 xmlXPathContextPtr xpath_ctx = NULL;
7334 xmlXPathObjectPtr result = NULL;
7335 void *xml_stream = NULL;
7336 size_t xml_stream_length = 0;
7338 if (!context) return IE_INVALID_CONTEXT;
7339 if (!message) return IE_INVAL;
7340 isds_message_free(message);
7341 if (!buffer) return IE_INVAL;
7344 /* Extract message from PKCS#7 structure */
7345 err = extract_cms_data(context, buffer, length,
7346 &xml_stream, &xml_stream_length);
7347 if (err) goto leave;
7349 isds_log(ILF_ISDS, ILL_DEBUG, (outgoing) ?
7350 _("Signed outgoing message content:\n%.*s\nEnd of message\n") :
7351 _("Signed incoming message content:\n%.*s\nEnd of message\n"),
7352 xml_stream_length, xml_stream);
7354 /* Convert extracted messages XML stream into XPath context */
7355 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
7356 if (!message_doc) {
7357 err = IE_XML;
7358 goto leave;
7360 xpath_ctx = xmlXPathNewContext(message_doc);
7361 if (!xpath_ctx) {
7362 err = IE_ERROR;
7363 goto leave;
7365 /* XXX: Name spaces mangled for outgoing direction:
7366 * http://isds.czechpoint.cz/v20/SentMessage:
7368 * <q:MessageDownloadResponse
7369 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
7370 * <q:dmReturnedMessage>
7371 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
7372 * <p:dmID>151916</p:dmID>
7373 * ...
7374 * </p:dmDm>
7375 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
7376 * ...
7377 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
7378 * </q:dmReturnedMessage>
7379 * </q:MessageDownloadResponse>
7381 * XXX: Name spaces mangled for incoming direction:
7382 * http://isds.czechpoint.cz/v20/message:
7384 * <q:MessageDownloadResponse
7385 * xmlns:q="http://isds.czechpoint.cz/v20/message">
7386 * <q:dmReturnedMessage>
7387 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
7388 * <p:dmID>151916</p:dmID>
7389 * ...
7390 * </p:dmDm>
7391 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
7392 * ...
7393 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
7394 * </q:dmReturnedMessage>
7395 * </q:MessageDownloadResponse>
7397 * Stupidity of ISDS developers is unlimited */
7398 if (register_namespaces(xpath_ctx, (outgoing) ?
7399 MESSAGE_NS_SIGNED_OUTGOING : MESSAGE_NS_SIGNED_INCOMING)) {
7400 err = IE_ERROR;
7401 goto leave;
7403 /* XXX: Embeded message XML document is always rooted as
7404 * /sisds:MessageDownloadResponse (even outgoing message). */
7405 result = xmlXPathEvalExpression(
7406 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
7407 xpath_ctx);
7408 if (!result) {
7409 err = IE_ERROR;
7410 goto leave;
7412 /* Empty embedded message */
7413 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7414 isds_printf_message(context,
7415 _("XML document embedded into PKCS#7 structure is not "
7416 "sisds:dmReturnedMessage document"));
7417 err = IE_ISDS;
7418 goto leave;
7420 /* More embedded messages */
7421 if (result->nodesetval->nodeNr > 1) {
7422 isds_printf_message(context,
7423 _("Embeded XML document into PKCS#7 structure has more "
7424 "root sisds:dmReturnedMessage elements"));
7425 err = IE_ISDS;
7426 goto leave;
7428 /* One embedded message */
7429 xpath_ctx->node = result->nodesetval->nodeTab[0];
7431 /* Extract the message */
7432 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
7433 if (err) goto leave;
7435 /* Append raw CMS structure into message */
7436 (*message)->raw_type = (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
7437 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
7438 switch (strategy) {
7439 case BUFFER_DONT_STORE:
7440 break;
7441 case BUFFER_COPY:
7442 (*message)->raw = malloc(length);
7443 if (!(*message)->raw) {
7444 err = IE_NOMEM;
7445 goto leave;
7447 memcpy((*message)->raw, buffer, length);
7448 (*message)->raw_length = length;
7449 break;
7450 case BUFFER_MOVE:
7451 (*message)->raw = (void *) buffer;
7452 (*message)->raw_length = length;
7453 break;
7454 default:
7455 err = IE_ENUM;
7456 goto leave;
7460 leave:
7461 if (err) {
7462 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
7463 isds_message_free(message);
7466 xmlFreeDoc(message_doc);
7467 cms_data_free(xml_stream);
7468 xmlXPathFreeObject(result);
7469 xmlXPathFreeContext(xpath_ctx);
7471 if (!err)
7472 isds_log(ILF_ISDS, ILL_DEBUG,
7473 _("Signed message loaded successfully.\n"));
7474 return err;
7478 /* Load message of any type from buffer.
7479 * @context is session context
7480 * @raw_type defines content type of @buffer. Only message types are allowed.
7481 * @buffer is message raw representation. Format (CMS, plain signed,
7482 * message direction) is defined in @raw_type. You can retrieve such data
7483 * from message->raw after calling isds_get_[signed]{received,sent}_message().
7484 * @length is length of buffer in bytes.
7485 * @message is automatically reallocated message parsed from @buffer.
7486 * @strategy selects how buffer will be attached into raw isds_message member.
7487 * */
7488 isds_error isds_load_message(struct isds_ctx *context,
7489 const isds_raw_type raw_type, const void *buffer, const size_t length,
7490 struct isds_message **message, const isds_buffer_strategy strategy) {
7492 isds_error err = IE_SUCCESS;
7493 void *xml_stream = NULL;
7494 size_t xml_stream_length = 0;
7495 message_ns_type message_ns;
7496 xmlDocPtr message_doc = NULL;
7497 xmlXPathContextPtr xpath_ctx = NULL;
7498 xmlXPathObjectPtr result = NULL;
7500 if (!context) return IE_INVALID_CONTEXT;
7501 if (!message) return IE_INVAL;
7502 isds_message_free(message);
7503 if (!buffer) return IE_INVAL;
7506 /* Select buffer format and extract XML from CMS*/
7507 switch (raw_type) {
7508 case RAWTYPE_INCOMING_MESSAGE:
7509 message_ns = MESSAGE_NS_UNSIGNED;
7510 xml_stream = (void *) buffer;
7511 xml_stream_length = length;
7512 break;
7514 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
7515 message_ns = MESSAGE_NS_SIGNED_INCOMING;
7516 xml_stream = (void *) buffer;
7517 xml_stream_length = length;
7518 break;
7520 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
7521 message_ns = MESSAGE_NS_SIGNED_INCOMING;
7522 err = extract_cms_data(context, buffer, length,
7523 &xml_stream, &xml_stream_length);
7524 if (err) goto leave;
7525 break;
7527 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
7528 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
7529 xml_stream = (void *) buffer;
7530 xml_stream_length = length;
7531 break;
7533 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
7534 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
7535 err = extract_cms_data(context, buffer, length,
7536 &xml_stream, &xml_stream_length);
7537 if (err) goto leave;
7538 break;
7540 default:
7541 isds_log_message(context, _("Bad raw message representation type"));
7542 return IE_INVAL;
7543 break;
7546 isds_log(ILF_ISDS, ILL_DEBUG,
7547 _("Loading message:\n%.*s\nEnd of message\n"),
7548 xml_stream_length, xml_stream);
7550 /* Convert messages XML stream into XPath context */
7551 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
7552 if (!message_doc) {
7553 err = IE_XML;
7554 goto leave;
7556 xpath_ctx = xmlXPathNewContext(message_doc);
7557 if (!xpath_ctx) {
7558 err = IE_ERROR;
7559 goto leave;
7561 /* XXX: Standard name space for unsigned icoming direction:
7562 * http://isds.czechpoint.cz/v20/SentMessage
7564 * XXX: Name spaces mangled for signed outgoing direction:
7565 * http://isds.czechpoint.cz/v20/SentMessage:
7567 * <q:MessageDownloadResponse
7568 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
7569 * <q:dmReturnedMessage>
7570 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
7571 * <p:dmID>151916</p:dmID>
7572 * ...
7573 * </p:dmDm>
7574 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
7575 * ...
7576 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
7577 * </q:dmReturnedMessage>
7578 * </q:MessageDownloadResponse>
7580 * XXX: Name spaces mangled for signed incoming direction:
7581 * http://isds.czechpoint.cz/v20/message:
7583 * <q:MessageDownloadResponse
7584 * xmlns:q="http://isds.czechpoint.cz/v20/message">
7585 * <q:dmReturnedMessage>
7586 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
7587 * <p:dmID>151916</p:dmID>
7588 * ...
7589 * </p:dmDm>
7590 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
7591 * ...
7592 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
7593 * </q:dmReturnedMessage>
7594 * </q:MessageDownloadResponse>
7596 * Stupidity of ISDS developers is unlimited */
7597 if (register_namespaces(xpath_ctx, message_ns)) {
7598 err = IE_ERROR;
7599 goto leave;
7601 result = xmlXPathEvalExpression(
7602 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
7603 xpath_ctx);
7604 if (!result) {
7605 err = IE_ERROR;
7606 goto leave;
7608 /* Empty message */
7609 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7610 isds_printf_message(context,
7611 _("XML document does not contain "
7612 "sisds:dmReturnedMessage element"));
7613 err = IE_ISDS;
7614 goto leave;
7616 /* More messages */
7617 if (result->nodesetval->nodeNr > 1) {
7618 isds_printf_message(context,
7619 _("XML document has more sisds:dmReturnedMessage elements"));
7620 err = IE_ISDS;
7621 goto leave;
7623 /* One message */
7624 xpath_ctx->node = result->nodesetval->nodeTab[0];
7626 /* Extract the message */
7627 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
7628 if (err) goto leave;
7630 /* Append raw buffer into message */
7631 (*message)->raw_type = raw_type;
7632 switch (strategy) {
7633 case BUFFER_DONT_STORE:
7634 break;
7635 case BUFFER_COPY:
7636 (*message)->raw = malloc(length);
7637 if (!(*message)->raw) {
7638 err = IE_NOMEM;
7639 goto leave;
7641 memcpy((*message)->raw, buffer, length);
7642 (*message)->raw_length = length;
7643 break;
7644 case BUFFER_MOVE:
7645 (*message)->raw = (void *) buffer;
7646 (*message)->raw_length = length;
7647 break;
7648 default:
7649 err = IE_ENUM;
7650 goto leave;
7654 leave:
7655 if (err) {
7656 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
7657 isds_message_free(message);
7660 if (xml_stream != buffer) cms_data_free(xml_stream);
7661 xmlXPathFreeObject(result);
7662 xmlXPathFreeContext(xpath_ctx);
7663 xmlFreeDoc(message_doc);
7665 if (!err)
7666 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
7667 return err;
7671 /* Download signed incoming/outgoing message identified by ID.
7672 * @context is session context
7673 * @output is true for outging message, false for incoming message
7674 * @message_id is message identifier (you can get them from
7675 * isds_get_list_of_{sent,received}_messages())
7676 * @message is automatically reallocated message retrieved from ISDS. The raw
7677 * memeber will be filled with PKCS#7 structure in DER format. */
7678 _hidden isds_error isds_get_signed_message(struct isds_ctx *context,
7679 const _Bool outgoing, const char *message_id,
7680 struct isds_message **message) {
7682 isds_error err = IE_SUCCESS;
7683 xmlDocPtr response = NULL;
7684 xmlChar *code = NULL, *status_message = NULL;
7685 xmlXPathContextPtr xpath_ctx = NULL;
7686 xmlXPathObjectPtr result = NULL;
7687 char *encoded_structure = NULL;
7688 void *raw = NULL;
7689 size_t raw_length = 0;
7691 if (!context) return IE_INVALID_CONTEXT;
7692 if (!message) return IE_INVAL;
7693 isds_message_free(message);
7695 /* Do request and check for success */
7696 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
7697 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
7698 BAD_CAST "SignedMessageDownload",
7699 message_id, &response, NULL, NULL, &code, &status_message);
7700 if (err) goto leave;
7702 /* Find signed message, extract it into raw and maybe free
7703 * response */
7704 err = find_extract_signed_data_free_response(context,
7705 (xmlChar *)message_id, &response,
7706 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
7707 BAD_CAST "SignedMessageDownload",
7708 &raw, &raw_length);
7709 if (err) goto leave;
7711 /* Parse message */
7712 err = isds_load_message(context,
7713 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
7714 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
7715 raw, raw_length, message, BUFFER_MOVE);
7716 if (err) goto leave;
7718 raw = NULL;
7720 leave:
7721 if (err) {
7722 isds_message_free(message);
7725 free(encoded_structure);
7726 xmlXPathFreeObject(result);
7727 xmlXPathFreeContext(xpath_ctx);
7728 free(raw);
7730 free(code);
7731 free(status_message);
7732 xmlFreeDoc(response);
7734 if (!err)
7735 isds_log(ILF_ISDS, ILL_DEBUG,
7736 (outgoing) ?
7737 _("SignedSentMessageDownload request processed by server "
7738 "successfully.\n") :
7739 _("SignedMessageDownload request processed by server "
7740 "successfully.\n")
7742 return err;
7746 /* Download signed incoming message identified by ID.
7747 * @context is session context
7748 * @message_id is message identifier (you can get them from
7749 * isds_get_list_of_received_messages())
7750 * @message is automatically reallocated message retrieved from ISDS. The raw
7751 * memeber will be filled with PKCS#7 structure in DER format. */
7752 isds_error isds_get_signed_received_message(struct isds_ctx *context,
7753 const char *message_id, struct isds_message **message) {
7754 return isds_get_signed_message(context, 0, message_id, message);
7758 /* Download signed outgoing message identified by ID.
7759 * @context is session context
7760 * @message_id is message identifier (you can get them from
7761 * isds_get_list_of_sent_messages())
7762 * @message is automatically reallocated message retrieved from ISDS. The raw
7763 * memeber will be filled with PKCS#7 structure in DER format. */
7764 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
7765 const char *message_id, struct isds_message **message) {
7766 return isds_get_signed_message(context, 1, message_id, message);
7770 /* Retrieve hash of message identified by ID stored in ISDS.
7771 * @context is session context
7772 * @message_id is message identifier
7773 * @hash is automatically reallocated message hash downloaded from ISDS.
7774 * Message must exist in system and must not be deleted. */
7775 isds_error isds_download_message_hash(struct isds_ctx *context,
7776 const char *message_id, struct isds_hash **hash) {
7778 isds_error err = IE_SUCCESS;
7779 xmlDocPtr response = NULL;
7780 xmlChar *code = NULL, *status_message = NULL;
7781 xmlXPathContextPtr xpath_ctx = NULL;
7782 xmlXPathObjectPtr result = NULL;
7784 if (!context) return IE_INVALID_CONTEXT;
7786 isds_hash_free(hash);
7788 err = build_send_check_message_request(context, SERVICE_DM_INFO,
7789 BAD_CAST "VerifyMessage", message_id,
7790 &response, NULL, NULL, &code, &status_message);
7791 if (err) goto leave;
7794 /* Extract data */
7795 xpath_ctx = xmlXPathNewContext(response);
7796 if (!xpath_ctx) {
7797 err = IE_ERROR;
7798 goto leave;
7800 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7801 err = IE_ERROR;
7802 goto leave;
7804 result = xmlXPathEvalExpression(
7805 BAD_CAST "/isds:VerifyMessageResponse",
7806 xpath_ctx);
7807 if (!result) {
7808 err = IE_ERROR;
7809 goto leave;
7811 /* Empty response */
7812 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7813 char *message_id_locale = utf82locale((char*) message_id);
7814 isds_printf_message(context,
7815 _("Server did not return any response for ID `%s' "
7816 "on VerifyMessage request"), message_id_locale);
7817 free(message_id_locale);
7818 err = IE_ISDS;
7819 goto leave;
7821 /* More responses */
7822 if (result->nodesetval->nodeNr > 1) {
7823 char *message_id_locale = utf82locale((char*) message_id);
7824 isds_printf_message(context,
7825 _("Server did return more responses for ID `%s' "
7826 "on VerifyMessage request"), message_id_locale);
7827 free(message_id_locale);
7828 err = IE_ISDS;
7829 goto leave;
7831 /* One response */
7832 xpath_ctx->node = result->nodesetval->nodeTab[0];
7834 /* Extract the hash */
7835 err = find_and_extract_DmHash(context, hash, xpath_ctx);
7837 leave:
7838 if (err) {
7839 isds_hash_free(hash);
7842 xmlXPathFreeObject(result);
7843 xmlXPathFreeContext(xpath_ctx);
7845 free(code);
7846 free(status_message);
7847 xmlFreeDoc(response);
7849 if (!err)
7850 isds_log(ILF_ISDS, ILL_DEBUG,
7851 _("VerifyMessage request processed by server "
7852 "successfully.\n")
7854 return err;
7858 /* Mark message as read. This is a transactional commit function to acknoledge
7859 * to ISDS the message has been downloaded and processed by client properly.
7860 * @context is session context
7861 * @message_id is message identifier. */
7862 isds_error isds_mark_message_read(struct isds_ctx *context,
7863 const char *message_id) {
7865 isds_error err = IE_SUCCESS;
7866 xmlDocPtr response = NULL;
7867 xmlChar *code = NULL, *status_message = NULL;
7869 if (!context) return IE_INVALID_CONTEXT;
7871 /* Do request and check for success */
7872 err = build_send_check_message_request(context, SERVICE_DM_INFO,
7873 BAD_CAST "MarkMessageAsDownloaded", message_id,
7874 &response, NULL, NULL, &code, &status_message);
7876 free(code);
7877 free(status_message);
7878 xmlFreeDoc(response);
7880 if (!err)
7881 isds_log(ILF_ISDS, ILL_DEBUG,
7882 _("MarkMessageAsDownloaded request processed by server "
7883 "successfully.\n")
7885 return err;
7888 /* Mark message as received by recipient. This is applicable only to
7889 * commercial message. There is no specified way how to distinguishe
7890 * commercial message from government message yet. Government message is
7891 * received automatically (by law), commenrcial message on recipient request.
7892 * @context is session context
7893 * @message_id is message identifier. */
7894 isds_error isds_mark_message_received(struct isds_ctx *context,
7895 const char *message_id) {
7897 isds_error err = IE_SUCCESS;
7898 xmlDocPtr response = NULL;
7899 xmlChar *code = NULL, *status_message = NULL;
7901 if (!context) return IE_INVALID_CONTEXT;
7903 /* Do request and check for success */
7904 err = build_send_check_message_request(context, SERVICE_DM_INFO,
7905 BAD_CAST "ConfirmDelivery", message_id,
7906 &response, NULL, NULL, &code, &status_message);
7908 free(code);
7909 free(status_message);
7910 xmlFreeDoc(response);
7912 if (!err)
7913 isds_log(ILF_ISDS, ILL_DEBUG,
7914 _("ConfirmDelivery request processed by server "
7915 "successfully.\n")
7917 return err;
7921 #undef INSERT_ELEMENT
7922 #undef CHECK_FOR_STRING_LENGTH
7923 #undef INSERT_STRING_ATTRIBUTE
7924 #undef INSERT_ULONGINTNOPTR
7925 #undef INSERT_ULONGINT
7926 #undef INSERT_LONGINT
7927 #undef INSERT_BOOLEAN
7928 #undef INSERT_SCALAR_BOOLEAN
7929 #undef INSERT_STRING
7930 #undef EXTRACT_STRING_ATTRIBUTE
7931 #undef EXTRACT_ULONGINT
7932 #undef EXTRACT_LONGINT
7933 #undef EXTRACT_BOOLEAN
7934 #undef EXTRACT_STRING
7937 /* Compute hash of message from raw representation and store it into envelope.
7938 * Original hash structure will be destroyed in envelope.
7939 * @context is session context
7940 * @message is message carrying raw XML message blob
7941 * @algorithm is desired hash algorithm to use */
7942 isds_error isds_compute_message_hash(struct isds_ctx *context,
7943 struct isds_message *message, const isds_hash_algorithm algorithm) {
7944 isds_error err = IE_SUCCESS;
7945 const char *nsuri;
7946 void *xml_stream = NULL;
7947 size_t xml_stream_length;
7948 size_t phys_start, phys_end;
7949 char *phys_path = NULL;
7950 struct isds_hash *new_hash = NULL;
7953 if (!context) return IE_INVALID_CONTEXT;
7954 if (!message) return IE_INVAL;
7956 if (!message->raw) {
7957 isds_log_message(context,
7958 _("Message does not carry raw representation"));
7959 return IE_INVAL;
7962 switch (message->raw_type) {
7963 case RAWTYPE_INCOMING_MESSAGE:
7964 nsuri = ISDS_NS;
7965 xml_stream = message->raw;
7966 xml_stream_length = message->raw_length;
7967 break;
7969 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
7970 nsuri = SISDS_INCOMING_NS;
7971 xml_stream = message->raw;
7972 xml_stream_length = message->raw_length;
7973 break;
7975 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
7976 nsuri = SISDS_INCOMING_NS;
7977 err = extract_cms_data(context, message->raw, message->raw_length,
7978 &xml_stream, &xml_stream_length);
7979 if (err) goto leave;
7980 break;
7982 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
7983 nsuri = SISDS_OUTGOING_NS;
7984 xml_stream = message->raw;
7985 xml_stream_length = message->raw_length;
7986 break;
7988 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
7989 nsuri = SISDS_OUTGOING_NS;
7990 err = extract_cms_data(context, message->raw, message->raw_length,
7991 &xml_stream, &xml_stream_length);
7992 if (err) goto leave;
7993 break;
7995 default:
7996 isds_log_message(context, _("Bad raw representation type"));
7997 return IE_INVAL;
7998 break;
8002 /* XXX: Hash is computed from original string represinting isds:dmDm
8003 * subtree. That means no encoding, white space, xmlns attributes changes.
8004 * In other words, input for hash can be invalid XML stream. */
8005 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
8006 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
8007 PHYSXML_ELEMENT_SEPARATOR,
8008 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
8009 PHYSXML_ELEMENT_SEPARATOR
8010 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
8011 err = IE_NOMEM;
8012 goto leave;
8014 err = find_element_boundary(xml_stream, xml_stream_length,
8015 phys_path, &phys_start, &phys_end);
8016 zfree(phys_path);
8017 if (err) {
8018 isds_log_message(context,
8019 _("Substring with isds:dmDM element could not be located "
8020 "in raw message"));
8021 goto leave;
8025 /* Compute hash */
8026 new_hash = calloc(1, sizeof(*new_hash));
8027 if (!new_hash) {
8028 err = IE_NOMEM;
8029 goto leave;
8031 new_hash->algorithm = algorithm;
8032 err = compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
8033 new_hash);
8034 if (err) {
8035 isds_log_message(context, _("Could not compute message hash"));
8036 goto leave;
8039 /* Save computed hash */
8040 if (!message->envelope) {
8041 message->envelope = calloc(1, sizeof(*message->envelope));
8042 if (!message->envelope) {
8043 err = IE_NOMEM;
8044 goto leave;
8047 isds_hash_free(&message->envelope->hash);
8048 message->envelope->hash = new_hash;
8050 leave:
8051 if (err) {
8052 isds_hash_free(&new_hash);
8055 free(phys_path);
8056 if (xml_stream != message->raw) free(xml_stream);
8057 return err;
8061 /* Compare two hashes.
8062 * @h1 is first hash
8063 * @h2 is another hash
8064 * @return
8065 * IE_SUCCESS if hashes equal
8066 * IE_NOTUNIQ if hashes are comparable, but they don't equal
8067 * IE_ENUM if not comparable, but both structures defined
8068 * IE_INVAL if some of the structures are undefined (NULL)
8069 * IE_ERROR if internal error occurs */
8070 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
8071 if (h1 == NULL || h2 == NULL) return IE_INVAL;
8072 if (h1->algorithm != h2->algorithm) return IE_ENUM;
8073 if (h1->length != h2->length) return IE_ERROR;
8074 if (h1->length > 0 && !h1->value) return IE_ERROR;
8075 if (h2->length > 0 && !h2->value) return IE_ERROR;
8077 for (int i = 0; i < h1->length; i++) {
8078 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
8079 return IE_NOTEQUAL;
8081 return IE_SUCCESS;
8085 /* Check message has gone through ISDS by comparing message hash stored in
8086 * ISDS and locally computed hash. You must provide message with valid raw
8087 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
8088 * This is convenient wrapper for isds_download_message_hash(),
8089 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
8090 * @context is session context
8091 * @message is message with valid raw and envelope member; envelope->hash
8092 * member will be changed during funcion run. Use envelope on heap only.
8093 * @return
8094 * IE_SUCCESS if message originates in ISDS
8095 * IE_NOTEQUAL if message is unknown to ISDS
8096 * other code for other errors */
8097 isds_error isds_verify_message_hash(struct isds_ctx *context,
8098 struct isds_message *message) {
8099 isds_error err = IE_SUCCESS;
8100 struct isds_hash *downloaded_hash = NULL;
8102 if (!context) return IE_INVALID_CONTEXT;
8103 if (!message) return IE_INVAL;
8105 if (!message->envelope) {
8106 isds_log_message(context,
8107 _("Given message structure is missing envelope"));
8108 return IE_INVAL;
8110 if (!message->raw) {
8111 isds_log_message(context,
8112 _("Given message structure is missing raw representation"));
8113 return IE_INVAL;
8116 err = isds_download_message_hash(context, message->envelope->dmID,
8117 &downloaded_hash);
8118 if (err) goto leave;
8120 err = isds_compute_message_hash(context, message,
8121 downloaded_hash->algorithm);
8122 if (err) goto leave;
8124 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
8126 leave:
8127 isds_hash_free(&downloaded_hash);
8128 return err;
8132 /* Search for document by document ID in list of documents. IDs are compared
8133 * as UTF-8 string.
8134 * @documents is list of isds_documents
8135 * @id is document identifier
8136 * @return first matching document or NULL. */
8137 const struct isds_document *isds_find_document_by_id(
8138 const struct isds_list *documents, const char *id) {
8139 const struct isds_list *item;
8140 const struct isds_document *document;
8142 for (item = documents; item; item = item->next) {
8143 document = (struct isds_document *) item->data;
8144 if (!document) continue;
8146 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
8147 return document;
8150 return NULL;
8154 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
8155 struct isds_message **message);
8156 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
8157 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
8158 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
8159 struct isds_address **address);
8161 int isds_message_free(struct isds_message **message);
8162 int isds_address_free(struct isds_address **address);
8166 /* Makes known all relevant namespaces to given XPath context
8167 * @xpat_ctx is XPath context
8168 * @message_ns selects propper message name space. Unsisnged and signed
8169 * messages differs.
8170 * prefix and to URI ISDS_NS */
8171 _hidden isds_error register_namespaces(xmlXPathContextPtr xpath_ctx,
8172 const message_ns_type message_ns) {
8173 const xmlChar *message_namespace = NULL;
8175 if (!xpath_ctx) return IE_ERROR;
8177 switch(message_ns) {
8178 case MESSAGE_NS_UNSIGNED:
8179 message_namespace = BAD_CAST ISDS_NS; break;
8180 case MESSAGE_NS_SIGNED_INCOMING:
8181 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
8182 case MESSAGE_NS_SIGNED_OUTGOING:
8183 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
8184 case MESSAGE_NS_SIGNED_DELIVERY:
8185 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
8186 default:
8187 return IE_ENUM;
8190 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
8191 return IE_ERROR;
8192 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
8193 return IE_ERROR;
8194 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
8195 return IE_ERROR;
8196 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
8197 return IE_ERROR;
8198 return IE_SUCCESS;