test: guess_raw_type: plain signed incoming message
[libisds.git] / src / isds.c
blob2641bcece9b5a8637cbe701d95abe045f0c64563
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"
16 /* Locators */
17 /* Base URL of production ISDS instance */
18 const char isds_locator[] = "https://www.mojedatovaschranka.cz/";
20 /* Base URL of production ISDS instance */
21 const char isds_testing_locator[] = "https://www.czebox.cz/";
24 /* Deallocate structure isds_pki_credentials and NULL it.
25 * Passphrase is discarded.
26 * @pki credentials to to free */
27 void isds_pki_credentials_free(struct isds_pki_credentials **pki) {
28 if(!pki || !*pki) return;
30 free((*pki)->engine);
31 free((*pki)->certificate);
32 free((*pki)->key);
34 if ((*pki)->passphrase) {
35 memset((*pki)->passphrase, 0, strlen((*pki)->passphrase));
36 free((*pki)->passphrase);
39 zfree((*pki));
43 /* Free isds_list with all member data.
44 * @list list to free, on return will be NULL */
45 void isds_list_free(struct isds_list **list) {
46 struct isds_list *item, *next_item;
48 if (!list || !*list) return;
50 for(item = *list; item; item = next_item) {
51 (item->destructor)(&(item->data));
52 next_item = item->next;
53 free(item);
56 *list = NULL;
60 /* Deallocate structure isds_hash and NULL it.
61 * @hash hash to to free */
62 void isds_hash_free(struct isds_hash **hash) {
63 if(!hash || !*hash) return;
64 free((*hash)->value);
65 zfree((*hash));
69 /* Deallocate structure isds_PersonName recursively and NULL it */
70 static void isds_PersonName_free(struct isds_PersonName **person_name) {
71 if (!person_name || !*person_name) return;
73 free((*person_name)->pnFirstName);
74 free((*person_name)->pnMiddleName);
75 free((*person_name)->pnLastName);
76 free((*person_name)->pnLastNameAtBirth);
78 free(*person_name);
79 *person_name = NULL;
83 /* Deallocate structure isds_BirthInfo recursively and NULL it */
84 static void isds_BirthInfo_free(struct isds_BirthInfo **birth_info) {
85 if (!birth_info || !*birth_info) return;
87 free((*birth_info)->biDate);
88 free((*birth_info)->biCity);
89 free((*birth_info)->biCounty);
90 free((*birth_info)->biState);
92 free(*birth_info);
93 *birth_info = NULL;
97 /* Deallocate structure isds_Address recursively and NULL it */
98 static void isds_Address_free(struct isds_Address **address) {
99 if (!address || !*address) return;
101 free((*address)->adCity);
102 free((*address)->adStreet);
103 free((*address)->adNumberInStreet);
104 free((*address)->adNumberInMunicipality);
105 free((*address)->adZipCode);
106 free((*address)->adState);
108 free(*address);
109 *address = NULL;
113 /* Deallocate structure isds_DbOwnerInfo recursively and NULL it */
114 void isds_DbOwnerInfo_free(struct isds_DbOwnerInfo **db_owner_info) {
115 if (!db_owner_info || !*db_owner_info) return;
117 free((*db_owner_info)->dbID);
118 free((*db_owner_info)->dbType);
119 free((*db_owner_info)->ic);
120 isds_PersonName_free(&((*db_owner_info)->personName));
121 free((*db_owner_info)->firmName);
122 isds_BirthInfo_free(&((*db_owner_info)->birthInfo));
123 isds_Address_free(&((*db_owner_info)->address));
124 free((*db_owner_info)->nationality);
125 free((*db_owner_info)->email);
126 free((*db_owner_info)->telNumber);
127 free((*db_owner_info)->identifier);
128 free((*db_owner_info)->registryCode);
129 free((*db_owner_info)->dbState);
130 free((*db_owner_info)->dbEffectiveOVM);
132 free(*db_owner_info);
133 *db_owner_info = NULL;
136 /* Deallocate structure isds_DbUserInfo recursively and NULL it */
137 void isds_DbUserInfo_free(struct isds_DbUserInfo **db_user_info) {
138 if (!db_user_info || !*db_user_info) return;
140 free((*db_user_info)->userID);
141 free((*db_user_info)->userType);
142 free((*db_user_info)->userPrivils);
143 isds_PersonName_free(&((*db_user_info)->personName));
144 isds_Address_free(&((*db_user_info)->address));
145 free((*db_user_info)->biDate);
146 free((*db_user_info)->ic);
147 free((*db_user_info)->firmName);
148 free((*db_user_info)->caStreet);
149 free((*db_user_info)->caCity);
150 free((*db_user_info)->caZipCode);
152 zfree(*db_user_info);
156 /* Deallocate struct isds_event recursively and NULL it */
157 void isds_event_free(struct isds_event **event) {
158 if (!event || !*event) return;
160 free((*event)->time);
161 free((*event)->type);
162 free((*event)->description);
163 zfree(*event);
167 /* Deallocate struct isds_envelope recursively and NULL it */
168 void isds_envelope_free(struct isds_envelope **envelope) {
169 if (!envelope || !*envelope) return;
171 free((*envelope)->dmID);
172 free((*envelope)->dbIDSender);
173 free((*envelope)->dmSender);
174 free((*envelope)->dmSenderAddress);
175 free((*envelope)->dmSenderType);
176 free((*envelope)->dmRecipient);
177 free((*envelope)->dmRecipientAddress);
178 free((*envelope)->dmAmbiguousRecipient);
179 free((*envelope)->dmType);
181 free((*envelope)->dmOrdinal);
182 free((*envelope)->dmMessageStatus);
183 free((*envelope)->dmDeliveryTime);
184 free((*envelope)->dmAcceptanceTime);
185 isds_hash_free(&(*envelope)->hash);
186 free((*envelope)->timestamp);
187 isds_list_free(&(*envelope)->events);
189 free((*envelope)->dmSenderOrgUnit);
190 free((*envelope)->dmSenderOrgUnitNum);
191 free((*envelope)->dbIDRecipient);
192 free((*envelope)->dmRecipientOrgUnit);
193 free((*envelope)->dmRecipientOrgUnitNum);
194 free((*envelope)->dmToHands);
195 free((*envelope)->dmAnnotation);
196 free((*envelope)->dmRecipientRefNumber);
197 free((*envelope)->dmSenderRefNumber);
198 free((*envelope)->dmRecipientIdent);
199 free((*envelope)->dmSenderIdent);
201 free((*envelope)->dmLegalTitleLaw);
202 free((*envelope)->dmLegalTitleYear);
203 free((*envelope)->dmLegalTitleSect);
204 free((*envelope)->dmLegalTitlePar);
205 free((*envelope)->dmLegalTitlePoint);
207 free((*envelope)->dmPersonalDelivery);
208 free((*envelope)->dmAllowSubstDelivery);
209 free((*envelope)->dmOVM);
211 free(*envelope);
212 *envelope = NULL;
216 /* Deallocate struct isds_message recursively and NULL it */
217 void isds_message_free(struct isds_message **message) {
218 if (!message || !*message) return;
220 free((*message)->raw);
221 isds_envelope_free(&((*message)->envelope));
222 isds_list_free(&((*message)->documents));
224 free(*message);
225 *message = NULL;
229 /* Deallocate struct isds_document recursively and NULL it */
230 void isds_document_free(struct isds_document **document) {
231 if (!document || !*document) return;
233 free((*document)->data);
234 free((*document)->dmMimeType);
235 free((*document)->dmFileGuid);
236 free((*document)->dmUpFileGuid);
237 free((*document)->dmFileDescr);
238 free((*document)->dmFormat);
240 free(*document);
241 *document = NULL;
245 /* Deallocate struct isds_message_copy recursively and NULL it */
246 void isds_message_copy_free(struct isds_message_copy **copy) {
247 if (!copy || !*copy) return;
249 free((*copy)->dbIDRecipient);
250 free((*copy)->dmRecipientOrgUnit);
251 free((*copy)->dmRecipientOrgUnitNum);
252 free((*copy)->dmToHands);
254 free((*copy)->dmStatus);
255 free((*copy)->dmID);
257 zfree(*copy);
261 /* Deallocate struct isds_approval recursively and NULL it */
262 void isds_approval_free(struct isds_approval **approval) {
263 if (!approval || !*approval) return;
265 free((*approval)->refference);
267 zfree(*approval);
271 /* *DUP_OR_ERROR macros needs error label */
272 #define STRDUP_OR_ERROR(new, template) { \
273 if (!template) { \
274 (new) = NULL; \
275 } else { \
276 (new) = strdup(template); \
277 if (!new) goto error; \
281 #define FLATDUP_OR_ERROR(new, template) { \
282 if (!template) { \
283 (new) = NULL; \
284 } else { \
285 (new) = malloc(sizeof(*(new))); \
286 if (!new) goto error; \
287 memcpy((new), (template), sizeof(*(template))); \
291 /* Copy structure isds_pki_credentials recursively. */
292 struct isds_pki_credentials *isds_pki_credentials_duplicate(
293 const struct isds_pki_credentials *template) {
294 struct isds_pki_credentials *new = NULL;
296 if(!template) return NULL;
298 new = calloc(1, sizeof(*new));
299 if (!new) return NULL;
301 STRDUP_OR_ERROR(new->engine, template->engine);
302 new->certificate_format = template->certificate_format;
303 STRDUP_OR_ERROR(new->certificate, template->certificate);
304 new->key_format = template->key_format;
305 STRDUP_OR_ERROR(new->key, template->key);
306 STRDUP_OR_ERROR(new->passphrase, template->passphrase);
308 return new;
310 error:
311 isds_pki_credentials_free(&new);
312 return NULL;
316 /* Copy structure isds_PersonName recursively */
317 struct isds_PersonName *isds_PersonName_duplicate(
318 const struct isds_PersonName *template) {
319 struct isds_PersonName *new = NULL;
321 if (!template) return NULL;
323 new = calloc(1, sizeof(*new));
324 if (!new) return NULL;
326 STRDUP_OR_ERROR(new->pnFirstName, template->pnFirstName);
327 STRDUP_OR_ERROR(new->pnMiddleName, template->pnMiddleName);
328 STRDUP_OR_ERROR(new->pnLastName, template->pnLastName);
329 STRDUP_OR_ERROR(new->pnLastNameAtBirth, template->pnLastNameAtBirth);
331 return new;
333 error:
334 isds_PersonName_free(&new);
335 return NULL;
339 /* Copy structure isds_BirthInfo recursively */
340 static struct isds_BirthInfo *isds_BirthInfo_duplicate(
341 const struct isds_BirthInfo *template) {
342 struct isds_BirthInfo *new = NULL;
344 if (!template) return NULL;
346 new = calloc(1, sizeof(*new));
347 if (!new) return NULL;
349 FLATDUP_OR_ERROR(new->biDate, template->biDate);
350 STRDUP_OR_ERROR(new->biCity, template->biCity);
351 STRDUP_OR_ERROR(new->biCounty, template->biCounty);
352 STRDUP_OR_ERROR(new->biState, template->biState);
354 return new;
356 error:
357 isds_BirthInfo_free(&new);
358 return NULL;
362 /* Copy structure isds_Address recursively */
363 struct isds_Address *isds_Address_duplicate(
364 const struct isds_Address *template) {
365 struct isds_Address *new = NULL;
367 if (!template) return NULL;
369 new = calloc(1, sizeof(*new));
370 if (!new) return NULL;
372 STRDUP_OR_ERROR(new->adCity, template->adCity);
373 STRDUP_OR_ERROR(new->adStreet, template->adStreet);
374 STRDUP_OR_ERROR(new->adNumberInStreet, template->adNumberInStreet);
375 STRDUP_OR_ERROR(new->adNumberInMunicipality,
376 template->adNumberInMunicipality);
377 STRDUP_OR_ERROR(new->adZipCode, template->adZipCode);
378 STRDUP_OR_ERROR(new->adState, template->adState);
380 return new;
382 error:
383 isds_Address_free(&new);
384 return NULL;
388 /* Copy structure isds_DbOwnerInfo recursively */
389 struct isds_DbOwnerInfo *isds_DbOwnerInfo_duplicate(
390 const struct isds_DbOwnerInfo *template) {
391 struct isds_DbOwnerInfo *new = NULL;
392 if (!template) return NULL;
394 new = calloc(1, sizeof(*new));
395 if (!new) return NULL;
397 STRDUP_OR_ERROR(new->dbID, template->dbID);
398 FLATDUP_OR_ERROR(new->dbType, template->dbType);
399 STRDUP_OR_ERROR(new->ic, template->ic);
401 if (template->personName) {
402 if (!(new->personName =
403 isds_PersonName_duplicate(template->personName)))
404 goto error;
407 STRDUP_OR_ERROR(new->firmName, template->firmName);
409 if (template->birthInfo) {
410 if (!(new->birthInfo =
411 isds_BirthInfo_duplicate(template->birthInfo)))
412 goto error;
415 if (template->address) {
416 if (!(new->address = isds_Address_duplicate(template->address)))
417 goto error;
420 STRDUP_OR_ERROR(new->nationality, template->nationality);
421 STRDUP_OR_ERROR(new->email, template->email);
422 STRDUP_OR_ERROR(new->telNumber, template->telNumber);
423 STRDUP_OR_ERROR(new->identifier, template->identifier);
424 STRDUP_OR_ERROR(new->registryCode, template->registryCode);
425 FLATDUP_OR_ERROR(new->dbState, template->dbState);
426 FLATDUP_OR_ERROR(new->dbEffectiveOVM, template->dbEffectiveOVM);
427 FLATDUP_OR_ERROR(new->dbOpenAddressing, template->dbOpenAddressing);
429 return new;
431 error:
432 isds_DbOwnerInfo_free(&new);
433 return NULL;
437 /* Copy structure isds_DbUserInfo recursively */
438 struct isds_DbUserInfo *isds_DbUserInfo_duplicate(
439 const struct isds_DbUserInfo *template) {
440 struct isds_DbUserInfo *new = NULL;
441 if (!template) return NULL;
443 new = calloc(1, sizeof(*new));
444 if (!new) return NULL;
446 STRDUP_OR_ERROR(new->userID, template->userID);
447 FLATDUP_OR_ERROR(new->userType, template->userType);
448 FLATDUP_OR_ERROR(new->userPrivils, template->userPrivils);
450 if (template->personName) {
451 if (!(new->personName =
452 isds_PersonName_duplicate(template->personName)))
453 goto error;
456 if (template->address) {
457 if (!(new->address = isds_Address_duplicate(template->address)))
458 goto error;
461 FLATDUP_OR_ERROR(new->biDate, template->biDate);
462 STRDUP_OR_ERROR(new->ic, template->ic);
463 STRDUP_OR_ERROR(new->firmName, template->firmName);
464 STRDUP_OR_ERROR(new->caStreet, template->caStreet);
465 STRDUP_OR_ERROR(new->caCity, template->caCity);
466 STRDUP_OR_ERROR(new->caZipCode, template->caZipCode);
468 return new;
470 error:
471 isds_DbUserInfo_free(&new);
472 return NULL;
475 #undef FLATDUP_OR_ERROR
476 #undef STRDUP_OR_ERROR
479 /* Initialize ISDS library.
480 * Global function, must be called before other functions.
481 * If it failes you can not use ISDS library and must call isds_cleanup() to
482 * free partially inititialized global variables. */
483 isds_error isds_init(void) {
484 /* NULL global variables */
485 log_facilities = ILF_ALL;
486 log_level = ILL_WARNING;
487 log_callback = NULL;
488 log_callback_data = NULL;
490 #if ENABLE_NLS
491 /* Initialize gettext */
492 bindtextdomain(PACKAGE, LOCALEDIR);
493 #endif
495 /* Initialize CURL */
496 if (curl_global_init(CURL_GLOBAL_ALL)) {
497 isds_log(ILF_ISDS, ILL_CRIT, _("CURL library initialization failed\n"));
498 return IE_ERROR;
501 /* Inicialize gpg-error because of gpgme and ksba */
502 if (gpg_err_init()) {
503 isds_log(ILF_ISDS, ILL_CRIT,
504 _("gpg-error library initialization failed\n"));
505 return IE_ERROR;
508 /* Initialize GPGME */
509 if (init_gpgme(&version_gpgme)) {
510 isds_log(ILF_ISDS, ILL_CRIT,
511 _("GPGME library initialization failed\n"));
512 return IE_ERROR;
515 /* Initialize gcrypt */
516 if (init_gcrypt(&version_gcrypt)) {
517 isds_log(ILF_ISDS, ILL_CRIT,
518 _("gcrypt library initialization failed\n"));
519 return IE_ERROR;
522 /* This can _exit() current program. Find not so assertive check. */
523 LIBXML_TEST_VERSION;
525 /* Check expat */
526 if (init_expat(&version_expat)) {
527 isds_log(ILF_ISDS, ILL_CRIT,
528 _("expat library initialization failed\n"));
529 return IE_ERROR;
532 /* Allocate global variables */
535 return IE_SUCCESS;
539 /* Deinicialize ISDS library.
540 * Global function, must be called as last library function. */
541 isds_error isds_cleanup(void) {
542 /* XML */
543 xmlCleanupParser();
545 /* Curl */
546 curl_global_cleanup();
548 return IE_SUCCESS;
552 /* Return version string of this library. Version of dependecies can be
553 * embedded. Do no try to parse it. You must free it. */
554 char *isds_version(void) {
555 char *buffer = NULL;
557 isds_asprintf(&buffer, _("%s (%s, GPGME %s, gcrypt %s, %s, libxml2 %s)"),
558 PACKAGE_VERSION, curl_version(), version_gpgme, version_gcrypt,
559 version_expat, xmlParserVersion);
560 return buffer;
564 /* Return text description of ISDS error */
565 const char *isds_strerror(const isds_error error) {
566 switch (error) {
567 case IE_SUCCESS:
568 return(_("Success")); break;
569 case IE_ERROR:
570 return(_("Unspecified error")); break;
571 case IE_NOTSUP:
572 return(_("Not supported")); break;
573 case IE_INVAL:
574 return(_("Invalid value")); break;
575 case IE_INVALID_CONTEXT:
576 return(_("Invalid context")); break;
577 case IE_NOT_LOGGED_IN:
578 return(_("Not logged in")); break;
579 case IE_CONNECTION_CLOSED:
580 return(_("Connection closed")); break;
581 case IE_TIMED_OUT:
582 return(_("Timed out")); break;
583 case IE_NOEXIST:
584 return(_("Not exist")); break;
585 case IE_NOMEM:
586 return(_("Out of memory")); break;
587 case IE_NETWORK:
588 return(_("Network problem")); break;
589 case IE_HTTP:
590 return(_("HTTP problem")); break;
591 case IE_SOAP:
592 return(_("SOAP problem")); break;
593 case IE_XML:
594 return(_("XML problem")); break;
595 case IE_ISDS:
596 return(_("ISDS server problem")); break;
597 case IE_ENUM:
598 return(_("Invalid enum value")); break;
599 case IE_DATE:
600 return(_("Invalid date value")); break;
601 case IE_2BIG:
602 return(_("Too big")); break;
603 case IE_2SMALL:
604 return(_("Too small")); break;
605 case IE_NOTUNIQ:
606 return(_("Value not unique")); break;
607 case IE_NOTEQUAL:
608 return(_("Values not uqual")); break;
609 case IE_PARTIAL_SUCCESS:
610 return(_("Some suboperations failed")); break;
611 case IE_ABORTED:
612 return(_("Operation aborted")); break;
613 default:
614 return(_("Unknown error"));
619 /* Create ISDS context.
620 * Each context can be used for different sessions to (possibly) differnet
621 * ISDS server with different credentials. */
622 struct isds_ctx *isds_ctx_create(void) {
623 struct isds_ctx *context;
624 context = malloc(sizeof(*context));
625 if (context) memset(context, 0, sizeof(*context));
626 return context;
630 /* Close possibly opened connection to Czech POINT document deposit without
631 * reseting long_message buffer.
632 * XXX: Do not use czp_close_connection() if you do not want to destroy log
633 * message.
634 * @context is Czech POINT session context. */
635 static isds_error czp_do_close_connection(struct isds_ctx *context) {
636 if (!context) return IE_INVALID_CONTEXT;
637 close_connection(context);
638 return IE_SUCCESS;
642 /* Discard credentials.
643 * Only that. It does not cause log out, connection close or similar. */
644 static isds_error discard_credentials(struct isds_ctx *context) {
645 if(!context) return IE_INVALID_CONTEXT;
647 if (context->username) {
648 memset(context->username, 0, strlen(context->username));
649 zfree(context->username);
651 if (context->password) {
652 memset(context->password, 0, strlen(context->password));
653 zfree(context->password);
655 isds_pki_credentials_free(&context->pki_credentials);
657 return IE_SUCCESS;
661 /* Destroy ISDS context and free memmory.
662 * @context will be NULLed on success. */
663 isds_error isds_ctx_free(struct isds_ctx **context) {
664 if (!context || !*context) {
665 return IE_INVALID_CONTEXT;
668 /* Discard credentials and close connection */
669 switch ((*context)->type) {
670 case CTX_TYPE_NONE: break;
671 case CTX_TYPE_ISDS: isds_logout(*context); break;
672 case CTX_TYPE_CZP:
673 case CTX_TYPE_TESTING_REQUEST_COLLECTOR:
674 czp_do_close_connection(*context); break;
677 /* For sure */
678 discard_credentials(*context);
680 /* Free other structures */
681 free((*context)->tls_verify_server);
682 free((*context)->tls_ca_file);
683 free((*context)->tls_ca_dir);
684 free((*context)->tls_crl_file);
685 free((*context)->long_message);
687 free(*context);
688 *context = NULL;
689 return IE_SUCCESS;
693 /* Return long message text produced by library fucntion, e.g. detailed error
694 * mesage. Returned pointer is only valid until new library function is
695 * called for the same context. Could be NULL, especially if NULL context is
696 * supplied. Return string is locale encoded. */
697 char *isds_long_message(const struct isds_ctx *context) {
698 if (!context) return NULL;
699 return context->long_message;
703 /* Stores message into context' long_message buffer.
704 * Application can pick the message up using isds_long_message().
705 * NULL @message truncates the buffer but does not deallocate it.
706 * @message is coded in locale encoding */
707 _hidden isds_error isds_log_message(struct isds_ctx *context,
708 const char *message) {
709 char *buffer;
710 size_t length;
712 if (!context) return IE_INVALID_CONTEXT;
714 /* FIXME: Check for integer overflow */
715 length = 1 + ((message) ? strlen(message) : 0);
716 buffer = realloc(context->long_message, length);
717 if (!buffer) return IE_NOMEM;
719 if (message)
720 strcpy(buffer, message);
721 else
722 *buffer = '\0';
724 context->long_message = buffer;
725 return IE_SUCCESS;
729 /* Appends message into context' long_message buffer.
730 * Application can pick the message up using isds_long_message().
731 * NULL message has void effect. */
732 _hidden isds_error isds_append_message(struct isds_ctx *context,
733 const char *message) {
734 char *buffer;
735 size_t old_length, length;
737 if (!context) return IE_INVALID_CONTEXT;
738 if (!message) return IE_SUCCESS;
739 if (!context->long_message)
740 return isds_log_message(context, message);
742 old_length = strlen(context->long_message);
743 /* FIXME: Check for integer overflow */
744 length = 1 + old_length + strlen(message);
745 buffer = realloc(context->long_message, length);
746 if (!buffer) return IE_NOMEM;
748 strcpy(buffer + old_length, message);
750 context->long_message = buffer;
751 return IE_SUCCESS;
755 /* Stores formated message into context' long_message buffer.
756 * Application can pick the message up using isds_long_message(). */
757 _hidden isds_error isds_printf_message(struct isds_ctx *context,
758 const char *format, ...) {
759 va_list ap;
760 int length;
762 if (!context) return IE_INVALID_CONTEXT;
763 va_start(ap, format);
764 length = isds_vasprintf(&(context->long_message), format, ap);
765 va_end(ap);
767 return (length < 0) ? IE_ERROR: IE_SUCCESS;
771 /* Set logging up.
772 * @facilities is bitmask of isds_log_facility values,
773 * @level is verbosity level. */
774 void isds_set_logging(const unsigned int facilities,
775 const isds_log_level level) {
776 log_facilities = facilities;
777 log_level = level;
781 /* Register callback function libisds calls when new global log message is
782 * produced by library. Library logs to stderr by default.
783 * @callback is function provided by application libsds will call. See type
784 * defition for @callback argument explanation. Pass NULL to revert logging to
785 * default behaviour.
786 * @data is application specific data @callback gets as last argument */
787 void isds_set_log_callback(isds_log_callback callback, void *data) {
788 log_callback = callback;
789 log_callback_data = data;
793 /* Log @message in class @facility with log @level into global log. @message
794 * is printf(3) formating string, variadic arguments may be neccessary.
795 * For debugging purposes. */
796 _hidden isds_error isds_log(const isds_log_facility facility,
797 const isds_log_level level, const char *message, ...) {
798 va_list ap;
799 char *buffer = NULL;
800 int length;
802 if (level > log_level) return IE_SUCCESS;
803 if (!(log_facilities & facility)) return IE_SUCCESS;
804 if (!message) return IE_INVAL;
806 if (log_callback) {
807 /* Pass message to application supplied callback function */
808 va_start(ap, message);
809 length = isds_vasprintf(&buffer, message, ap);
810 va_end(ap);
812 if (length == -1) {
813 return IE_ERROR;
815 if (length > 0) {
816 log_callback(facility, level, buffer, length, log_callback_data);
818 free(buffer);
819 } else {
820 /* Default: Log it to stderr */
821 va_start(ap, message);
822 vfprintf(stderr, message, ap);
823 va_end(ap);
824 /* Line buffered printf is default.
825 * fflush(stderr);*/
828 return IE_SUCCESS;
832 /* Set timeout in miliseconds for each network job like connecting to server
833 * or sending message. Use 0 to disable timeout limits. */
834 isds_error isds_set_timeout(struct isds_ctx *context,
835 const unsigned int timeout) {
836 if (!context) return IE_INVALID_CONTEXT;
837 zfree(context->long_message);
839 context->timeout = timeout;
841 if (context->curl) {
842 CURLcode curl_err;
844 curl_err = curl_easy_setopt(context->curl, CURLOPT_NOSIGNAL, 1);
845 if (!curl_err)
846 #if HAVE_DECL_CURLOPT_TIMEOUT_MS /* Since curl-7.16.2 */
847 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT_MS,
848 context->timeout);
849 #else
850 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT,
851 context->timeout / 1000);
852 #endif /* not HAVE_DECL_CURLOPT_TIMEOUT_MS */
853 if (curl_err) return IE_ERROR;
856 return IE_SUCCESS;
860 /* Register callback function libisds calls periodocally during HTTP data
861 * transfer.
862 * @context is session context
863 * @callback is function provided by application libsds will call. See type
864 * defition for @callback argument explanation.
865 * @data is application specific data @callback gets as last argument */
866 isds_error isds_set_progress_callback(struct isds_ctx *context,
867 isds_progress_callback callback, void *data) {
868 if (!context) return IE_INVALID_CONTEXT;
869 zfree(context->long_message);
871 context->progress_callback = callback;
872 context->progress_callback_data = data;
874 return IE_SUCCESS;
878 /* Change SSL/TLS settings.
879 * @context is context which setting will be applied to
880 * @option is name of option. It determines the type of last argument. See
881 * isds_tls_option definition for more info.
882 * @... is value of new setting. Type is determined by @option
883 * */
884 isds_error isds_set_tls(struct isds_ctx *context, const isds_tls_option option,
885 ...) {
886 isds_error err = IE_SUCCESS;
887 va_list ap;
888 char *pointer, *string;
890 if (!context) return IE_INVALID_CONTEXT;
891 zfree(context->long_message);
893 va_start(ap, option);
895 #define REPLACE_VA_STRING(destination) \
896 string = va_arg(ap, char *); \
897 if (string) { \
898 pointer = realloc((destination), 1 + strlen(string)); \
899 if (!pointer) { err = IE_NOMEM; goto leave; } \
900 strcpy(pointer, string); \
901 (destination) = pointer; \
902 } else { \
903 free(destination); \
904 (destination) = NULL; \
907 switch (option) {
908 case ITLS_VERIFY_SERVER:
909 if (!context->tls_verify_server) {
910 context->tls_verify_server =
911 malloc(sizeof(*context->tls_verify_server));
912 if (!context->tls_verify_server) {
913 err = IE_NOMEM; goto leave;
916 *context->tls_verify_server = (_Bool) (0 != va_arg(ap, int));
917 break;
919 case ITLS_CA_FILE:
920 REPLACE_VA_STRING(context->tls_ca_file);
921 break;
922 case ITLS_CA_DIRECTORY:
923 REPLACE_VA_STRING(context->tls_ca_dir);
924 break;
925 case ITLS_CRL_FILE:
926 #if HAVE_DECL_CURLOPT_CRLFILE /* Since curl-7.19.0 */
927 REPLACE_VA_STRING(context->tls_crl_file);
928 #else
929 isds_log_message(ILF_SEC, ILL_ERR,
930 _("Curl library does not support CRL definition"));
931 err = IE_NOTSUP;
932 #endif /* not HAVE_DECL_CURLOPT_CRLFILE */
933 break;
935 default:
936 err = IE_ENUM; goto leave;
939 #undef REPLACE_VA_STRING
941 leave:
942 va_end(ap);
943 return err;
947 /* Connect and log in into ISDS server.
948 * All required arguments will be copied, you do not have to keep them after
949 * that.
950 * ISDS supports four different authentication methods. Exact method is
951 * selected on @username, @passwors and @pki_credentials arguments:
952 * - If @pki_credentials == NULL, @username and @password must be supplied
953 * - If @pki_credentials != NULL, then
954 * - If @username == NULL, only certificate will be used
955 * - If @username != NULL, then
956 * - If @password == NULL, then certificate will be used and
957 * @username shifts meaning to box ID. This is used for hosted
958 * services.
959 * - Otherwise all three arguments will be used.
960 * Please note, that differen cases requires different certificate type
961 * (system qualified one or commercial non qualified one). This library does
962 * not check such political issues. Please see ISDS Specification for more
963 * details.
964 * @url is base address of ISDS web service. Pass NULL or extern isds_locator
965 * variable to use production ISDS instance. You can pass extern
966 * isds_testing_locator variable to select testing instance.
967 * @username is user name of ISDS user or box ID
968 * @password is user's secret password
969 * @pki_credentials defines public key cryptographic material to use in client
970 * authentication. */
971 isds_error isds_login(struct isds_ctx *context, const char *url,
972 const char *username, const char *password,
973 const struct isds_pki_credentials *pki_credentials) {
974 isds_error err = IE_NOT_LOGGED_IN;
975 isds_error soap_err;
976 xmlNsPtr isds_ns = NULL;
977 xmlNodePtr request = NULL;
978 xmlNodePtr response = NULL;
980 if (!context) return IE_INVALID_CONTEXT;
981 zfree(context->long_message);
983 /* Close connection if already logged in */
984 if (context->curl) {
985 close_connection(context);
988 /* Default locator is offical system */
989 if (!url) url = isds_locator;
991 /* Store configuration */
992 context->type = CTX_TYPE_ISDS;
993 zfree(context->url);
995 /* Mangle base URI according requested authentication method */
996 if (!pki_credentials) {
997 isds_log(ILF_SEC, ILL_INFO,
998 _("Selected authentication method: no certificate, "
999 "username and password\n"));
1000 if (!username || !password) {
1001 isds_log_message(context,
1002 _("Both username and password must be supplied"));
1003 return IE_INVAL;
1005 context->url = strdup(url);
1006 } else {
1007 if (!username) {
1008 isds_log(ILF_SEC, ILL_INFO,
1009 _("Selected authentication method: system certificate, "
1010 "no username and no password\n"));
1011 password = NULL;
1012 context->url = astrcat(url, "cert/");
1013 } else {
1014 if (!password) {
1015 isds_log(ILF_SEC, ILL_INFO,
1016 _("Selected authentication method: system certificate, "
1017 "box ID and no password\n"));
1018 context->url = astrcat(url, "hspis/");
1019 } else {
1020 isds_log(ILF_SEC, ILL_INFO,
1021 _("Selected authentication method: commercial "
1022 "certificate, username and password\n"));
1023 context->url = astrcat(url, "cert/");
1027 if (!(context->url))
1028 return IE_NOMEM;
1030 /* Prepare CURL handle */
1031 context->curl = curl_easy_init();
1032 if (!(context->curl))
1033 return IE_ERROR;
1035 /* Build login request */
1036 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1037 if (!request) {
1038 isds_log_message(context, _("Could build ISDS login request"));
1039 return IE_ERROR;
1041 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1042 if(!isds_ns) {
1043 isds_log_message(context, _("Could not create ISDS name space"));
1044 xmlFreeNode(request);
1045 return IE_ERROR;
1047 xmlSetNs(request, isds_ns);
1049 /* Store credentials */
1050 /* FIXME: mlock password
1051 * (I have a library) */
1052 discard_credentials(context);
1053 if (username) context->username = strdup(username);
1054 if (password) context->password = strdup(password);
1055 context->pki_credentials = isds_pki_credentials_duplicate(pki_credentials);
1056 if ((username && !context->username) || (password && !context->password) ||
1057 (pki_credentials && !context->pki_credentials)) {
1058 discard_credentials(context);
1059 xmlFreeNode(request);
1060 return IE_NOMEM;
1063 isds_log(ILF_ISDS, ILL_DEBUG, _("Logging user %s into server %s\n"),
1064 username, url);
1066 /* Send login request */
1067 soap_err = soap(context, "DS/dz", request, &response, NULL, NULL);
1069 /* Remove credentials */
1070 discard_credentials(context);
1072 /* Destroy login request */
1073 xmlFreeNode(request);
1075 if (soap_err) {
1076 xmlFreeNodeList(response);
1077 close_connection(context);
1078 return soap_err;
1081 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1082 * authentication succeeded if soap_err == IE_SUCCESS */
1083 err = IE_SUCCESS;
1085 xmlFreeNodeList(response);
1087 if (!err)
1088 isds_log(ILF_ISDS, ILL_DEBUG,
1089 _("User %s has been logged into server %s successfully\n"),
1090 username, url);
1091 return err;
1095 /* Log out from ISDS server discards credentials and connection configuration. */
1096 isds_error isds_logout(struct isds_ctx *context) {
1097 if (!context) return IE_INVALID_CONTEXT;
1098 zfree(context->long_message);
1100 /* Close connection */
1101 if (context->curl) {
1102 close_connection(context);
1104 /* Discard credentials for sure. They should not survive isds_login(),
1105 * even successful .*/
1106 discard_credentials(context);
1107 zfree(context->url);
1109 isds_log(ILF_ISDS, ILL_DEBUG, _("Logged out from ISDS server\n"));
1110 } else {
1111 discard_credentials(context);
1113 return IE_SUCCESS;
1117 /* Verify connection to ISDS is alive and server is responding.
1118 * Sent dumy request to ISDS and expect dummy response. */
1119 isds_error isds_ping(struct isds_ctx *context) {
1120 isds_error soap_err;
1121 xmlNsPtr isds_ns = NULL;
1122 xmlNodePtr request = NULL;
1123 xmlNodePtr response = NULL;
1125 if (!context) return IE_INVALID_CONTEXT;
1126 zfree(context->long_message);
1128 /* Check if connection is established */
1129 if (!context->curl) return IE_CONNECTION_CLOSED;
1132 /* Build dummy request */
1133 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1134 if (!request) {
1135 isds_log_message(context, _("Could build ISDS dummy request"));
1136 return IE_ERROR;
1138 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1139 if(!isds_ns) {
1140 isds_log_message(context, _("Could not create ISDS name space"));
1141 xmlFreeNode(request);
1142 return IE_ERROR;
1144 xmlSetNs(request, isds_ns);
1146 isds_log(ILF_ISDS, ILL_DEBUG, _("Pinging ISDS server\n"));
1148 /* Sent dummy request */
1149 soap_err = soap(context, "DS/dz", request, &response, NULL, NULL);
1151 /* Destroy login request */
1152 xmlFreeNode(request);
1154 if (soap_err) {
1155 isds_log(ILF_ISDS, ILL_DEBUG,
1156 _("ISDS server could not be contacted\n"));
1157 xmlFreeNodeList(response);
1158 return soap_err;
1161 /* XXX: Untill we don't propagate HTTP code 500 or 4xx, we can be sure
1162 * authentication succeeded if soap_err == IE_SUCCESS */
1163 /* TODO: ISDS documentation does not specify response body.
1164 * However real server sends back DummyOperationResponse */
1167 xmlFreeNodeList(response);
1169 isds_log(ILF_ISDS, ILL_DEBUG, _("ISDS server alive\n"));
1171 return IE_SUCCESS;
1175 /* Send bogus request to ISDS.
1176 * Just for test purposes */
1177 isds_error isds_bogus_request(struct isds_ctx *context) {
1178 isds_error err;
1179 xmlNsPtr isds_ns = NULL;
1180 xmlNodePtr request = NULL;
1181 xmlDocPtr response = NULL;
1182 xmlChar *code = NULL, *message = NULL;
1184 if (!context) return IE_INVALID_CONTEXT;
1185 zfree(context->long_message);
1187 /* Check if connection is established */
1188 if (!context->curl) {
1189 /* Testing printf message */
1190 isds_printf_message(context, "%s", _("I said connection closed"));
1191 return IE_CONNECTION_CLOSED;
1195 /* Build dummy request */
1196 request = xmlNewNode(NULL, BAD_CAST "X-BogusOperation");
1197 if (!request) {
1198 isds_log_message(context, _("Could build ISDS bogus request"));
1199 return IE_ERROR;
1201 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1202 if(!isds_ns) {
1203 isds_log_message(context, _("Could not create ISDS name space"));
1204 xmlFreeNode(request);
1205 return IE_ERROR;
1207 xmlSetNs(request, isds_ns);
1209 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending bogus request to ISDS\n"));
1211 /* Sent bogus request */
1212 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
1214 /* Destroy request */
1215 xmlFreeNode(request);
1217 if (err) {
1218 isds_log(ILF_ISDS, ILL_DEBUG,
1219 _("Processing ISDS response on bogus request failed\n"));
1220 xmlFreeDoc(response);
1221 return err;
1224 /* Check for response status */
1225 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
1226 &code, &message, NULL);
1227 if (err) {
1228 isds_log(ILF_ISDS, ILL_DEBUG,
1229 _("ISDS response on bogus request is missing status\n"));
1230 free(code);
1231 free(message);
1232 xmlFreeDoc(response);
1233 return err;
1235 if (xmlStrcmp(code, BAD_CAST "0000")) {
1236 char *code_locale = utf82locale((char*)code);
1237 char *message_locale = utf82locale((char*)message);
1238 isds_log(ILF_ISDS, ILL_DEBUG,
1239 _("Server refused bogus request (code=%s, message=%s)\n"),
1240 code_locale, message_locale);
1241 /* XXX: Literal error messages from ISDS are Czech mesages
1242 * (English sometimes) in UTF-8. It's hard to catch them for
1243 * translation. Successfully gettextized would return in locale
1244 * encoding, unsuccessfully translated would pass in UTF-8. */
1245 isds_log_message(context, message_locale);
1246 free(code_locale);
1247 free(message_locale);
1248 free(code);
1249 free(message);
1250 xmlFreeDoc(response);
1251 return IE_ISDS;
1255 free(code);
1256 free(message);
1257 xmlFreeDoc(response);
1259 isds_log(ILF_ISDS, ILL_DEBUG,
1260 _("Bogus message accepted by server. This should not happen.\n"));
1262 return IE_SUCCESS;
1266 /* Serialize XML subtree to buffer preserving XML indentatition.
1267 * @context is session context
1268 * @subtree is XML element to be serialized (with childern)
1269 * @buffer is automatically reallocated buffer where serialize to
1270 * @length is size of serialized stream in bytes
1271 * @return standard error code, free @buffer in case of error */
1272 static isds_error serialize_subtree(struct isds_ctx *context,
1273 xmlNodePtr subtree, void **buffer, size_t *length) {
1274 isds_error err = IE_SUCCESS;
1275 xmlBufferPtr xml_buffer = NULL;
1276 xmlSaveCtxtPtr save_ctx = NULL;
1277 xmlDocPtr subtree_doc = NULL;
1278 xmlNodePtr subtree_copy;
1279 xmlNsPtr isds_ns;
1280 void *new_buffer;
1282 if (!context) return IE_INVALID_CONTEXT;
1283 if (!buffer) return IE_INVAL;
1284 zfree(*buffer);
1285 if (!subtree || !length) return IE_INVAL;
1287 /* Make temporary XML document with @subtree root element */
1288 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1289 * It can result in not well-formed on invalid XML tree (e.g. name space
1290 * prefix definition can miss. */
1291 /*FIXME */
1293 subtree_doc = xmlNewDoc(BAD_CAST "1.0");
1294 if (!subtree_doc) {
1295 isds_log_message(context, _("Could not build temporary document"));
1296 err = IE_ERROR;
1297 goto leave;
1300 /* XXX: Copy subtree and attach the copy to document.
1301 * One node can not bee attached into more document at the same time.
1302 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1303 * automatically.
1304 * XXX: Check xmlSaveTree() too. */
1305 subtree_copy = xmlCopyNodeList(subtree);
1306 if (!subtree_copy) {
1307 isds_log_message(context, _("Could not copy subtree"));
1308 err = IE_ERROR;
1309 goto leave;
1311 xmlDocSetRootElement(subtree_doc, subtree_copy);
1313 /* Only this way we get namespace definition as @xmlns:isds,
1314 * otherwise we get namespace prefix without definition */
1315 /* FIXME: Don't overwrite original default namespace */
1316 isds_ns = xmlNewNs(subtree_copy, BAD_CAST ISDS_NS, NULL);
1317 if(!isds_ns) {
1318 isds_log_message(context, _("Could not create ISDS name space"));
1319 err = IE_ERROR;
1320 goto leave;
1322 xmlSetNs(subtree_copy, isds_ns);
1325 /* Serialize the document into buffer */
1326 xml_buffer = xmlBufferCreate();
1327 if (!xml_buffer) {
1328 isds_log_message(context, _("Could not create xmlBuffer"));
1329 err = IE_ERROR;
1330 goto leave;
1332 /* Last argument 0 means to not format the XML tree */
1333 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8", 0);
1334 if (!save_ctx) {
1335 isds_log_message(context, _("Could not create XML serializer"));
1336 err = IE_ERROR;
1337 goto leave;
1339 /* XXX: According LibXML documentation, this function does not return
1340 * meaningfull value yet */
1341 xmlSaveDoc(save_ctx, subtree_doc);
1342 if (-1 == xmlSaveFlush(save_ctx)) {
1343 isds_log_message(context,
1344 _("Could not serialize XML subtree"));
1345 err = IE_ERROR;
1346 goto leave;
1348 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1349 * even after xmlSaveFlush(). Thus close it here */
1350 xmlSaveClose(save_ctx); save_ctx = NULL;
1353 /* Store and detach buffer from xml_buffer */
1354 *buffer = xml_buffer->content;
1355 *length = xml_buffer->use;
1356 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1358 /* Shrink buffer */
1359 new_buffer = realloc(*buffer, *length);
1360 if (new_buffer) *buffer = new_buffer;
1362 leave:
1363 if (err) {
1364 zfree(*buffer);
1365 *length = 0;
1368 xmlSaveClose(save_ctx);
1369 xmlBufferFree(xml_buffer);
1370 xmlFreeDoc(subtree_doc); /* Frees subtree_copy, isds_ns etc. */
1371 return err;
1374 #if 0
1375 /* Dump XML subtree to buffer as literral string, not valid XML possibly.
1376 * @context is session context
1377 * @document is original document where @nodeset points to
1378 * @nodeset is XPath node set to dump (recursively)
1379 * @buffer is automarically reallocated buffer where serialize to
1380 * @length is size of serialized stream in bytes
1381 * @return standard error code, free @buffer in case of error */
1382 static isds_error dump_nodeset(struct isds_ctx *context,
1383 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1384 void **buffer, size_t *length) {
1385 isds_error err = IE_SUCCESS;
1386 xmlBufferPtr xml_buffer = NULL;
1387 void *new_buffer;
1389 if (!context) return IE_INVALID_CONTEXT;
1390 if (!buffer) return IE_INVAL;
1391 zfree(*buffer);
1392 if (!document || !nodeset || !length) return IE_INVAL;
1393 *length = 0;
1395 /* Empty node set results into NULL buffer */
1396 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1397 goto leave;
1400 /* Resuling the document into buffer */
1401 xml_buffer = xmlBufferCreate();
1402 if (!xml_buffer) {
1403 isds_log_message(context, _("Could not create xmlBuffer"));
1404 err = IE_ERROR;
1405 goto leave;
1408 /* Itearate over all nodes */
1409 for (int i = 0; i < nodeset->nodeNr; i++) {
1410 /* Serialize node.
1411 * XXX: xmlNodeDump() appends to xml_buffer. */
1412 if (-1 ==
1413 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1414 isds_log_message(context, _("Could not dump XML node"));
1415 err = IE_ERROR;
1416 goto leave;
1420 /* Store and detach buffer from xml_buffer */
1421 *buffer = xml_buffer->content;
1422 *length = xml_buffer->use;
1423 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1425 /* Shrink buffer */
1426 new_buffer = realloc(*buffer, *length);
1427 if (new_buffer) *buffer = new_buffer;
1430 leave:
1431 if (err) {
1432 zfree(*buffer);
1433 *length = 0;
1436 xmlBufferFree(xml_buffer);
1437 return err;
1439 #endif
1441 #if 0
1442 /* Dump XML subtree to buffer as literral string, not valid XML possibly.
1443 * @context is session context
1444 * @document is original document where @nodeset points to
1445 * @nodeset is XPath node set to dump (recursively)
1446 * @buffer is automarically reallocated buffer where serialize to
1447 * @length is size of serialized stream in bytes
1448 * @return standard error code, free @buffer in case of error */
1449 static isds_error dump_nodeset(struct isds_ctx *context,
1450 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1451 void **buffer, size_t *length) {
1452 isds_error err = IE_SUCCESS;
1453 xmlBufferPtr xml_buffer = NULL;
1454 xmlSaveCtxtPtr save_ctx = NULL;
1455 void *new_buffer;
1457 if (!context) return IE_INVALID_CONTEXT;
1458 if (!buffer) return IE_INVAL;
1459 zfree(*buffer);
1460 if (!document || !nodeset || !length) return IE_INVAL;
1461 *length = 0;
1463 /* Empty node set results into NULL buffer */
1464 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1465 goto leave;
1468 /* Resuling the document into buffer */
1469 xml_buffer = xmlBufferCreate();
1470 if (!xml_buffer) {
1471 isds_log_message(context, _("Could not create xmlBuffer"));
1472 err = IE_ERROR;
1473 goto leave;
1475 if (xmlSubstituteEntitiesDefault(1)) {
1476 isds_log_message(context, _("Could not disable attribute escaping"));
1477 err = IE_ERROR;
1478 goto leave;
1480 /* Last argument means:
1481 * 0 to not format the XML tree
1482 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1483 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8",
1484 XML_SAVE_NO_DECL|XML_SAVE_NO_EMPTY|XML_SAVE_NO_XHTML);
1485 if (!save_ctx) {
1486 isds_log_message(context, _("Could not create XML serializer"));
1487 err = IE_ERROR;
1488 goto leave;
1490 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1491 isds_log_message(context, _("Could not disable attribute escaping"));
1492 err = IE_ERROR;
1493 goto leave;
1497 /* Itearate over all nodes */
1498 for (int i = 0; i < nodeset->nodeNr; i++) {
1499 /* Serialize node.
1500 * XXX: xmlNodeDump() appends to xml_buffer. */
1501 /*if (-1 ==
1502 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1504 /* XXX: According LibXML documentation, this function does not return
1505 * meaningfull value yet */
1506 xmlSaveTree(save_ctx, nodeset->nodeTab[i]);
1507 if (-1 == xmlSaveFlush(save_ctx)) {
1508 isds_log_message(context,
1509 _("Could not serialize XML subtree"));
1510 err = IE_ERROR;
1511 goto leave;
1515 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1516 * even after xmlSaveFlush(). Thus close it here */
1517 xmlSaveClose(save_ctx); save_ctx = NULL;
1519 /* Store and detach buffer from xml_buffer */
1520 *buffer = xml_buffer->content;
1521 *length = xml_buffer->use;
1522 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1524 /* Shrink buffer */
1525 new_buffer = realloc(*buffer, *length);
1526 if (new_buffer) *buffer = new_buffer;
1528 leave:
1529 if (err) {
1530 zfree(*buffer);
1531 *length = 0;
1534 xmlSaveClose(save_ctx);
1535 xmlBufferFree(xml_buffer);
1536 return err;
1538 #endif
1541 /* Convert UTF-8 @string represantion of ISDS dbType to enum @type */
1542 static isds_error string2isds_DbType(xmlChar *string, isds_DbType *type) {
1543 if (!string || !type) return IE_INVAL;
1545 if (!xmlStrcmp(string, BAD_CAST "FO"))
1546 *type = DBTYPE_FO;
1547 else if (!xmlStrcmp(string, BAD_CAST "PFO"))
1548 *type = DBTYPE_PFO;
1549 else if (!xmlStrcmp(string, BAD_CAST "PFO_ADVOK"))
1550 *type = DBTYPE_PFO_ADVOK;
1551 else if (!xmlStrcmp(string, BAD_CAST "PFO_DANPOR"))
1552 *type = DBTYPE_PFO_DANPOR;
1553 else if (!xmlStrcmp(string, BAD_CAST "PFO_INSSPR"))
1554 *type = DBTYPE_PFO_INSSPR;
1555 else if (!xmlStrcmp(string, BAD_CAST "PO"))
1556 *type = DBTYPE_PO;
1557 else if (!xmlStrcmp(string, BAD_CAST "PO_ZAK"))
1558 *type = DBTYPE_PO_ZAK;
1559 else if (!xmlStrcmp(string, BAD_CAST "PO_REQ"))
1560 *type = DBTYPE_PO_REQ;
1561 else if (!xmlStrcmp(string, BAD_CAST "OVM"))
1562 *type = DBTYPE_OVM;
1563 else if (!xmlStrcmp(string, BAD_CAST "OVM_NOTAR"))
1564 *type = DBTYPE_OVM_NOTAR;
1565 else if (!xmlStrcmp(string, BAD_CAST "OVM_EXEKUT"))
1566 *type = DBTYPE_OVM_EXEKUT;
1567 else if (!xmlStrcmp(string, BAD_CAST "OVM_REQ"))
1568 *type = DBTYPE_OVM_REQ;
1569 else
1570 return IE_ENUM;
1571 return IE_SUCCESS;
1575 /* Convert ISDS dbType enum @type to UTF-8 string.
1576 * @Return pointer to static string, or NULL if unkwnow enum value */
1577 static const xmlChar *isds_DbType2string(const isds_DbType type) {
1578 switch(type) {
1579 case DBTYPE_FO: return(BAD_CAST "FO"); break;
1580 case DBTYPE_PFO: return(BAD_CAST "PFO"); break;
1581 case DBTYPE_PFO_ADVOK: return(BAD_CAST "PFO_ADVOK"); break;
1582 case DBTYPE_PFO_DANPOR: return(BAD_CAST "PFO_DANPOR"); break;
1583 case DBTYPE_PFO_INSSPR: return(BAD_CAST "PFO_INSSPR"); break;
1584 case DBTYPE_PO: return(BAD_CAST "PO"); break;
1585 case DBTYPE_PO_ZAK: return(BAD_CAST "PO_ZAK"); break;
1586 case DBTYPE_PO_REQ: return(BAD_CAST "PO_REQ"); break;
1587 case DBTYPE_OVM: return(BAD_CAST "OVM"); break;
1588 case DBTYPE_OVM_NOTAR: return(BAD_CAST "OVM_NOTAR"); break;
1589 case DBTYPE_OVM_EXEKUT: return(BAD_CAST "OVM_EXEKUT"); break;
1590 case DBTYPE_OVM_REQ: return(BAD_CAST "OVM_REQ"); break;
1591 default: return NULL; break;
1596 /* Convert UTF-8 @string represantion of ISDS userType to enum @type */
1597 static isds_error string2isds_UserType(xmlChar *string, isds_UserType *type) {
1598 if (!string || !type) return IE_INVAL;
1600 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
1601 *type = USERTYPE_PRIMARY;
1602 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
1603 *type = USERTYPE_ENTRUSTED;
1604 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
1605 *type = USERTYPE_ADMINISTRATOR;
1606 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
1607 *type = USERTYPE_OFFICIAL;
1608 else
1609 return IE_ENUM;
1610 return IE_SUCCESS;
1614 /* Convert ISDS userType enum @type to UTF-8 string.
1615 * @Return pointer to static string, or NULL if unkwnow enum value */
1616 static const xmlChar *isds_UserType2string(const isds_UserType type) {
1617 switch(type) {
1618 case USERTYPE_PRIMARY: return(BAD_CAST "PRIMARY_USER"); break;
1619 case USERTYPE_ENTRUSTED: return(BAD_CAST "ENTRUSTED_USER"); break;
1620 case USERTYPE_ADMINISTRATOR: return(BAD_CAST "ADMINISTRATOR"); break;
1621 case USERTYPE_OFFICIAL: return(BAD_CAST "OFFICIAL"); break;
1622 default: return NULL; break;
1627 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
1628 * @Return pointer to static string, or NULL if unkwnow enum value */
1629 static const xmlChar *isds_FileMetaType2string(const isds_FileMetaType type) {
1630 switch(type) {
1631 case FILEMETATYPE_MAIN: return(BAD_CAST "main"); break;
1632 case FILEMETATYPE_ENCLOSURE: return(BAD_CAST "enclosure"); break;
1633 case FILEMETATYPE_SIGNATURE: return(BAD_CAST "signature"); break;
1634 case FILEMETATYPE_META: return(BAD_CAST "meta"); break;
1635 default: return NULL; break;
1640 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
1641 * @Return IE_ENUM if @string is not valid enum member */
1642 static isds_error string2isds_FileMetaType(const xmlChar *string,
1643 isds_FileMetaType *type) {
1644 if (!string || !type) return IE_INVAL;
1646 if (!xmlStrcmp(string, BAD_CAST "main"))
1647 *type = FILEMETATYPE_MAIN;
1648 else if (!xmlStrcmp(string, BAD_CAST "enclosure"))
1649 *type = FILEMETATYPE_ENCLOSURE;
1650 else if (!xmlStrcmp(string, BAD_CAST "signature"))
1651 *type = FILEMETATYPE_SIGNATURE;
1652 else if (!xmlStrcmp(string, BAD_CAST "meta"))
1653 *type = FILEMETATYPE_META;
1654 else
1655 return IE_ENUM;
1656 return IE_SUCCESS;
1660 /* Convert UTF-8 @string to ISDS hash @algorithm.
1661 * @Return IE_ENUM if @string is not valid enum member */
1662 static isds_error string2isds_hash_algorithm(const xmlChar *string,
1663 isds_hash_algorithm *algorithm) {
1664 if (!string || !algorithm) return IE_INVAL;
1666 if (!xmlStrcmp(string, BAD_CAST "MD5"))
1667 *algorithm = HASH_ALGORITHM_MD5;
1668 else if (!xmlStrcmp(string, BAD_CAST "SHA-1"))
1669 *algorithm = HASH_ALGORITHM_SHA_1;
1670 else if (!xmlStrcmp(string, BAD_CAST "SHA-224"))
1671 *algorithm = HASH_ALGORITHM_SHA_224;
1672 else if (!xmlStrcmp(string, BAD_CAST "SHA-256"))
1673 *algorithm = HASH_ALGORITHM_SHA_256;
1674 else if (!xmlStrcmp(string, BAD_CAST "SHA-384"))
1675 *algorithm = HASH_ALGORITHM_SHA_384;
1676 else if (!xmlStrcmp(string, BAD_CAST "SHA-512"))
1677 *algorithm = HASH_ALGORITHM_SHA_512;
1678 else
1679 return IE_ENUM;
1680 return IE_SUCCESS;
1684 /* Convert UTF-8 @string represantion of ISO 8601 date to @time.
1685 * XXX: Not all ISO formats are supported */
1686 static isds_error datestring2tm(const xmlChar *string, struct tm *time) {
1687 char *offset;
1688 if (!string || !time) return IE_INVAL;
1690 /* xsd:date is ISO 8601 string, thus ASCII */
1691 offset = strptime((char*)string, "%Y-%m-%d", time);
1692 if (offset && *offset == '\0')
1693 return IE_SUCCESS;
1695 offset = strptime((char*)string, "%Y%m%d", time);
1696 if (offset && *offset == '\0')
1697 return IE_SUCCESS;
1699 offset = strptime((char*)string, "%Y-%j", time);
1700 if (offset && *offset == '\0')
1701 return IE_SUCCESS;
1703 return IE_NOTSUP;
1707 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
1708 static isds_error tm2datestring(const struct tm *time, xmlChar **string) {
1709 if (!time || !string) return IE_INVAL;
1711 if (-1 == isds_asprintf((char **) string, "%d-%02d-%02d",
1712 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
1713 return IE_ERROR;
1715 return IE_SUCCESS;
1719 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
1720 * respects the @time microseconds too. */
1721 static isds_error timeval2timestring(const struct timeval *time,
1722 xmlChar **string) {
1723 struct tm broken;
1725 if (!time || !string) return IE_INVAL;
1727 if (!gmtime_r(&time->tv_sec, &broken)) return IE_DATE;
1728 if (time->tv_usec < 0 || time->tv_usec > 999999) return IE_DATE;
1730 /* TODO: small negative year should be formated as "-0012". This is not
1731 * true for glibc "%04d". We should implement it.
1732 * TODO: What's type of time->tv_usec exactly? Unsigned? Absolute?
1733 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
1734 if (-1 == isds_asprintf((char **) string,
1735 "%04d-%02d-%02dT%02d:%02d:%02d.%06ld",
1736 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
1737 broken.tm_hour, broken.tm_min, broken.tm_sec,
1738 time->tv_usec))
1739 return IE_ERROR;
1741 return IE_SUCCESS;
1745 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
1746 * It respects microseconds too.
1747 * In case of error, @time will be freed. */
1748 static isds_error timestring2timeval(const xmlChar *string,
1749 struct timeval **time) {
1750 struct tm broken;
1751 char *offset, *delim, *endptr;
1752 char subseconds[7];
1753 int offset_hours, offset_minutes;
1754 int i;
1756 if (!time) return IE_INVAL;
1758 memset(&broken, 0, sizeof(broken));
1760 if (!*time) {
1761 *time = calloc(1, sizeof(**time));
1762 if (!*time) return IE_NOMEM;
1763 } else {
1764 memset(*time, 0, sizeof(**time));
1768 /* xsd:date is ISO 8601 string, thus ASCII */
1769 /*TODO: negative year */
1771 /* Parse date and time without subseconds and offset */
1772 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
1773 if (!offset) {
1774 free(*time); *time = NULL;
1775 return IE_DATE;
1778 /* Get subseconds */
1779 if (*offset == '.' ) {
1780 offset++;
1782 /* Copy first 6 digits, padd it with zeros.
1783 * XXX: It truncates longer number, no round.
1784 * Current server implementation uses only milisecond resolution. */
1785 /* TODO: isdigit() is locale sensitive */
1786 for (i = 0;
1787 i < sizeof(subseconds)/sizeof(char) - 1 && isdigit(*offset);
1788 i++, offset++) {
1789 subseconds[i] = *offset;
1791 for (; i < sizeof(subseconds)/sizeof(char) - 1; i++) {
1792 subseconds[i] = '0';
1794 subseconds[6] = '\0';
1796 /* Convert it into integer */
1797 (*time)->tv_usec = strtol(subseconds, &endptr, 10);
1798 if (*endptr != '\0' || (*time)->tv_usec == LONG_MIN ||
1799 (*time)->tv_usec == LONG_MAX) {
1800 free(*time); *time = NULL;
1801 return IE_DATE;
1804 /* move to the zone offset delimiter */
1805 delim = strchr(offset, '-');
1806 if (!delim)
1807 delim = strchr(offset, '+');
1808 offset = delim;
1811 /* Get zone offset */
1812 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
1813 * "" equals to "Z" and it means UTC zone. */
1814 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
1815 * colon separator */
1816 if (*offset == '-' || *offset == '+') {
1817 offset++;
1818 if (2 != sscanf(offset, "%2d:%2d", &offset_hours, &offset_minutes)) {
1819 free(*time); *time = NULL;
1820 return IE_DATE;
1822 broken.tm_hour -= offset_hours;
1823 broken.tm_min -= offset_minutes * ((offset_hours<0) ? -1 : 1);
1826 /* Convert to time_t */
1827 switch_tz_to_utc();
1828 (*time)->tv_sec = mktime(&broken);
1829 switch_tz_to_native();
1830 if ((*time)->tv_sec == (time_t) -1) {
1831 free(*time); *time = NULL;
1832 return IE_DATE;
1835 return IE_SUCCESS;
1839 /* Convert unsigned int into isds_message_status.
1840 * @context is session context
1841 * @number is pointer to number value. NULL will be treated as invalid value.
1842 * @status is automatically reallocated status
1843 * @return IE_SUCCESS, or error code and free status */
1844 static isds_error uint2isds_message_status(struct isds_ctx *context,
1845 const unsigned long int *number, isds_message_status **status) {
1846 if (!context) return IE_INVALID_CONTEXT;
1847 if (!status) return IE_INVAL;
1849 free(*status); *status = NULL;
1850 if (!number) return IE_INVAL;
1852 if (*number < 1 || *number > 10) {
1853 isds_printf_message(context, _("Invalid messsage status value: %lu"),
1854 *number);
1855 return IE_ENUM;
1858 *status = malloc(sizeof(**status));
1859 if (!*status) return IE_NOMEM;
1861 **status = 1 << *number;
1862 return IE_SUCCESS;
1866 /* Convert event description string into isds_event memebers type and
1867 * description
1868 * @string is raw event decsription starting with event prefix
1869 * @event is structure where to store type and stripped description to
1870 * @return standard error code, unknown prefix is not classified as an error.
1871 * */
1872 static isds_error eventstring2event(const xmlChar *string,
1873 struct isds_event* event) {
1874 const xmlChar *known_prefixes[] = {
1875 BAD_CAST "EV1:",
1876 BAD_CAST "EV2:",
1877 BAD_CAST "EV3:",
1878 BAD_CAST "EV4:"
1880 const isds_event_type types[] = {
1881 EVENT_ACCEPTED_BY_RECIPIENT,
1882 EVENT_ACCEPTED_BY_FICTION,
1883 EVENT_UNDELIVERABLE,
1884 EVENT_COMMERCIAL_ACCEPTED
1886 unsigned int index;
1887 size_t length;
1889 if (!string || !event) return IE_INVAL;
1891 if (!event->type) {
1892 event->type = malloc(sizeof(*event->type));
1893 if (!(event->type)) return IE_NOMEM;
1895 zfree(event->description);
1897 for (index = 0; index < sizeof(known_prefixes)/sizeof(known_prefixes[0]);
1898 index++) {
1899 length = xmlUTF8Strlen(known_prefixes[index]);
1901 if (!xmlStrncmp(string, known_prefixes[index], length)) {
1902 /* Prefix is known */
1903 *event->type = types[index];
1905 /* Strip prefix from description and spaces */
1906 /* TODO: Recognize all wite spaces from UCS blank class and
1907 * operate on UTF-8 chars. */
1908 for (; string[length] != '\0' && string[length] == ' '; length++);
1909 event->description = strdup((char *) (string + length));
1910 if (!(event->description)) return IE_NOMEM;
1912 return IE_SUCCESS;
1916 /* Unknown event prefix.
1917 * XSD allows any string */
1918 char *string_locale = utf82locale((char *) string);
1919 isds_log(ILF_ISDS, ILL_WARNING,
1920 _("Uknown delivery info event prefix: %s\n"), string_locale);
1921 free(string_locale);
1923 *event->type = EVENT_UKNOWN;
1924 event->description = strdup((char *) string);
1925 if (!(event->description)) return IE_NOMEM;
1927 return IE_SUCCESS;
1931 /* Following EXTRACT_* macros expects @result, @xpath_ctx, @err, @context
1932 * and leave lable */
1933 #define EXTRACT_STRING(element, string) { \
1934 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
1935 if (!result) { \
1936 err = IE_ERROR; \
1937 goto leave; \
1939 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
1940 if (result->nodesetval->nodeNr > 1) { \
1941 isds_printf_message(context, _("Multiple %s element"), element); \
1942 err = IE_ERROR; \
1943 goto leave; \
1945 (string) = (char *) \
1946 xmlXPathCastNodeSetToString(result->nodesetval); \
1947 if (!(string)) { \
1948 err = IE_ERROR; \
1949 goto leave; \
1954 #define EXTRACT_BOOLEAN(element, booleanPtr) \
1956 char *string = NULL; \
1957 EXTRACT_STRING(element, string); \
1959 if (string) { \
1960 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
1961 if (!(booleanPtr)) { \
1962 free(string); \
1963 err = IE_NOMEM; \
1964 goto leave; \
1967 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
1968 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
1969 *(booleanPtr) = 1; \
1970 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
1971 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
1972 *(booleanPtr) = 0; \
1973 else { \
1974 char *string_locale = utf82locale((char*)string); \
1975 isds_printf_message(context, \
1976 _("%s value is not valid boolean: %s"), \
1977 element, string_locale); \
1978 free(string_locale); \
1979 free(string); \
1980 err = IE_ERROR; \
1981 goto leave; \
1984 free(string); \
1988 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
1990 char *string = NULL; \
1991 EXTRACT_STRING(element, string); \
1992 if (string) { \
1993 long int number; \
1994 char *endptr; \
1996 number = strtol((char*)string, &endptr, 10); \
1998 if (*endptr != '\0') { \
1999 char *string_locale = utf82locale((char *)string); \
2000 isds_printf_message(context, \
2001 _("%s is not valid integer: %s"), \
2002 element, string_locale); \
2003 free(string_locale); \
2004 free(string); \
2005 err = IE_ISDS; \
2006 goto leave; \
2009 if (number == LONG_MIN || number == LONG_MAX) { \
2010 char *string_locale = utf82locale((char *)string); \
2011 isds_printf_message(context, \
2012 _("%s value out of range of long int: %s"), \
2013 element, string_locale); \
2014 free(string_locale); \
2015 free(string); \
2016 err = IE_ERROR; \
2017 goto leave; \
2020 free(string); string = NULL; \
2022 if (!(preallocated)) { \
2023 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2024 if (!(longintPtr)) { \
2025 err = IE_NOMEM; \
2026 goto leave; \
2029 *(longintPtr) = number; \
2033 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2035 char *string = NULL; \
2036 EXTRACT_STRING(element, string); \
2037 if (string) { \
2038 long int number; \
2039 char *endptr; \
2041 number = strtol((char*)string, &endptr, 10); \
2043 if (*endptr != '\0') { \
2044 char *string_locale = utf82locale((char *)string); \
2045 isds_printf_message(context, \
2046 _("%s is not valid integer: %s"), \
2047 element, string_locale); \
2048 free(string_locale); \
2049 free(string); \
2050 err = IE_ISDS; \
2051 goto leave; \
2054 if (number == LONG_MIN || number == LONG_MAX) { \
2055 char *string_locale = utf82locale((char *)string); \
2056 isds_printf_message(context, \
2057 _("%s value out of range of long int: %s"), \
2058 element, string_locale); \
2059 free(string_locale); \
2060 free(string); \
2061 err = IE_ERROR; \
2062 goto leave; \
2065 free(string); string = NULL; \
2066 if (number < 0) { \
2067 isds_printf_message(context, \
2068 _("%s value is negative: %ld"), element, number); \
2069 err = IE_ERROR; \
2070 goto leave; \
2073 if (!(preallocated)) { \
2074 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2075 if (!(ulongintPtr)) { \
2076 err = IE_NOMEM; \
2077 goto leave; \
2080 *(ulongintPtr) = number; \
2084 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2085 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2086 NULL); \
2087 if ((required) && (!string)) { \
2088 char *attribute_locale = utf82locale(attribute); \
2089 char *element_locale = utf82locale((char *)xpath_ctx->node->name); \
2090 isds_printf_message(context, \
2091 _("Could not extract required %s attribute value from " \
2092 "%s element"), attribute_locale, element_locale); \
2093 free(element_locale); \
2094 free(attribute_locale); \
2095 err = IE_ERROR; \
2096 goto leave; \
2101 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2103 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2104 (xmlChar *) (string)); \
2105 if (!node) { \
2106 isds_printf_message(context, \
2107 _("Could not add %s child to %s element"), \
2108 element, (parent)->name); \
2109 err = IE_ERROR; \
2110 goto leave; \
2114 #define INSERT_STRING(parent, element, string) \
2115 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2117 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2119 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2120 else { INSERT_STRING(parent, element, "false"); } \
2123 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2125 if (booleanPtr) { \
2126 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2127 } else { \
2128 INSERT_STRING(parent, element, NULL); \
2132 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2133 if ((longintPtr)) { \
2134 /* FIXME: locale sensitive */ \
2135 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2136 err = IE_NOMEM; \
2137 goto leave; \
2139 INSERT_STRING(parent, element, buffer) \
2140 free(buffer); (buffer) = NULL; \
2141 } else { INSERT_STRING(parent, element, NULL) } \
2144 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2145 if ((ulongintPtr)) { \
2146 /* FIXME: locale sensitive */ \
2147 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2148 err = IE_NOMEM; \
2149 goto leave; \
2151 INSERT_STRING(parent, element, buffer) \
2152 free(buffer); (buffer) = NULL; \
2153 } else { INSERT_STRING(parent, element, NULL) } \
2156 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2158 /* FIXME: locale sensitive */ \
2159 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2160 err = IE_NOMEM; \
2161 goto leave; \
2163 INSERT_STRING(parent, element, buffer) \
2164 free(buffer); (buffer) = NULL; \
2167 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2169 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2170 (xmlChar *) (string)); \
2171 if (!attribute_node) { \
2172 isds_printf_message(context, _("Could not add %s " \
2173 "attribute to %s element"), \
2174 (attribute), (parent)->name); \
2175 err = IE_ERROR; \
2176 goto leave; \
2180 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2181 if (string) { \
2182 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2183 if (length > (maximum)) { \
2184 isds_printf_message(context, \
2185 ngettext("%s has more than %d characters", \
2186 "%s has more than %d characters", (maximum)), \
2187 (name), (maximum)); \
2188 err = IE_2BIG; \
2189 goto leave; \
2191 if (length < (minimum)) { \
2192 isds_printf_message(context, \
2193 ngettext("%s has less than %d characters", \
2194 "%s has less than %d characters", (minimum)), \
2195 (name), (minimum)); \
2196 err = IE_2SMALL; \
2197 goto leave; \
2202 #define INSERT_ELEMENT(child, parent, element) \
2204 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2205 if (!(child)) { \
2206 isds_printf_message(context, \
2207 _("Could not add %s child to %s element"), \
2208 (element), (parent)->name); \
2209 err = IE_ERROR; \
2210 goto leave; \
2215 /* Find child element by name in given XPath context and switch context onto
2216 * it. The child must be uniq and must exist. Otherwise failes.
2217 * @context is ISDS context
2218 * @child is child element name
2219 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2220 * into it child. In error case, the @xpath_ctx keeps original value. */
2221 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
2222 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
2223 isds_error err = IE_SUCCESS;
2224 xmlXPathObjectPtr result = NULL;
2226 if (!context) return IE_INVALID_CONTEXT;
2227 if (!child || !xpath_ctx) return IE_INVAL;
2229 /* Find child */
2230 result = xmlXPathEvalExpression(child, xpath_ctx);
2231 if (!result) {
2232 err = IE_XML;
2233 goto leave;
2236 /* No match */
2237 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2238 char *parent_locale = utf82locale((char*) xpath_ctx->node->name);
2239 char *child_locale = utf82locale((char*) child);
2240 isds_printf_message(context,
2241 _("%s element does not contain %s child"),
2242 parent_locale, child_locale);
2243 free(child_locale);
2244 free(parent_locale);
2245 err = IE_NOEXIST;
2246 goto leave;
2249 /* More matches */
2250 if (result->nodesetval->nodeNr > 1) {
2251 char *parent_locale = utf82locale((char*) xpath_ctx->node->name);
2252 char *child_locale = utf82locale((char*) child);
2253 isds_printf_message(context,
2254 _("%s element contains multiple %s children"),
2255 parent_locale, child_locale);
2256 free(child_locale);
2257 free(parent_locale);
2258 err = IE_NOTUNIQ;
2259 goto leave;
2262 /* Switch context */
2263 xpath_ctx->node = result->nodesetval->nodeTab[0];
2265 leave:
2266 xmlXPathFreeObject(result);
2267 return err;
2272 /* Find and convert XSD:gPersonName group in current node into structure
2273 * @context is ISDS context
2274 * @personName is automically reallocated person name structure. If no member
2275 * value is found, will be freed.
2276 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2277 * elements
2278 * In case of error @personName will be freed. */
2279 static isds_error extract_gPersonName(struct isds_ctx *context,
2280 struct isds_PersonName **personName, xmlXPathContextPtr xpath_ctx) {
2281 isds_error err = IE_SUCCESS;
2282 xmlXPathObjectPtr result = NULL;
2284 if (!context) return IE_INVALID_CONTEXT;
2285 if (!personName) return IE_INVAL;
2286 isds_PersonName_free(personName);
2287 if (!xpath_ctx) return IE_INVAL;
2290 *personName = calloc(1, sizeof(**personName));
2291 if (!*personName) {
2292 err = IE_NOMEM;
2293 goto leave;
2296 EXTRACT_STRING("isds:pnFirstName", (*personName)->pnFirstName);
2297 EXTRACT_STRING("isds:pnMiddleName", (*personName)->pnMiddleName);
2298 EXTRACT_STRING("isds:pnLastName", (*personName)->pnLastName);
2299 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName)->pnLastNameAtBirth);
2301 if (!(*personName)->pnFirstName && !(*personName)->pnMiddleName &&
2302 !(*personName)->pnLastName && !(*personName)->pnLastNameAtBirth)
2303 isds_PersonName_free(personName);
2305 leave:
2306 if (err) isds_PersonName_free(personName);
2307 xmlXPathFreeObject(result);
2308 return err;
2312 /* Find and convert XSD:gAddress group in current node into structure
2313 * @context is ISDS context
2314 * @address is automically reallocated address structure. If no member
2315 * value is found, will be freed.
2316 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2317 * elements
2318 * In case of error @address will be freed. */
2319 static isds_error extract_gAddress(struct isds_ctx *context,
2320 struct isds_Address **address, xmlXPathContextPtr xpath_ctx) {
2321 isds_error err = IE_SUCCESS;
2322 xmlXPathObjectPtr result = NULL;
2324 if (!context) return IE_INVALID_CONTEXT;
2325 if (!address) return IE_INVAL;
2326 isds_Address_free(address);
2327 if (!xpath_ctx) return IE_INVAL;
2330 *address = calloc(1, sizeof(**address));
2331 if (!*address) {
2332 err = IE_NOMEM;
2333 goto leave;
2336 EXTRACT_STRING("isds:adCity", (*address)->adCity);
2337 EXTRACT_STRING("isds:adStreet", (*address)->adStreet);
2338 EXTRACT_STRING("isds:adNumberInStreet", (*address)->adNumberInStreet);
2339 EXTRACT_STRING("isds:adNumberInMunicipality",
2340 (*address)->adNumberInMunicipality);
2341 EXTRACT_STRING("isds:adZipCode", (*address)->adZipCode);
2342 EXTRACT_STRING("isds:adState", (*address)->adState);
2344 if (!(*address)->adCity && !(*address)->adStreet &&
2345 !(*address)->adNumberInStreet &&
2346 !(*address)->adNumberInMunicipality &&
2347 !(*address)->adZipCode && !(*address)->adState)
2348 isds_Address_free(address);
2350 leave:
2351 if (err) isds_Address_free(address);
2352 xmlXPathFreeObject(result);
2353 return err;
2357 /* Find and convert isds:biDate element in current node into structure
2358 * @context is ISDS context
2359 * @biDate is automically reallocated birth date structure. If no member
2360 * value is found, will be freed.
2361 * @xpath_ctx is XPath context with current node as parent for isds:biDate
2362 * element
2363 * In case of error @biDate will be freed. */
2364 static isds_error extract_BiDate(struct isds_ctx *context,
2365 struct tm **biDate, xmlXPathContextPtr xpath_ctx) {
2366 isds_error err = IE_SUCCESS;
2367 xmlXPathObjectPtr result = NULL;
2368 char *string = NULL;
2370 if (!context) return IE_INVALID_CONTEXT;
2371 if (!biDate) return IE_INVAL;
2372 zfree(*biDate);
2373 if (!xpath_ctx) return IE_INVAL;
2375 EXTRACT_STRING("isds:biDate", string);
2376 if (string) {
2377 *biDate = calloc(1, sizeof(**biDate));
2378 if (!*biDate) {
2379 err = IE_NOMEM;
2380 goto leave;
2382 err = datestring2tm((xmlChar *)string, *biDate);
2383 if (err) {
2384 if (err == IE_NOTSUP) {
2385 err = IE_ISDS;
2386 char *string_locale = utf82locale(string);
2387 isds_printf_message(context,
2388 _("Invalid isds:biDate value: %s"), string_locale);
2389 free(string_locale);
2391 goto leave;
2395 leave:
2396 if (err) zfree(*biDate);
2397 free(string);
2398 xmlXPathFreeObject(result);
2399 return err;
2403 /* Convert isds:dBOwnerInfo XML tree into structure
2404 * @context is ISDS context
2405 * @db_owner_info is automically reallocated box owner info structure
2406 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
2407 * In case of error @db_owner_info will be freed. */
2408 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
2409 struct isds_DbOwnerInfo **db_owner_info,
2410 xmlXPathContextPtr xpath_ctx) {
2411 isds_error err = IE_SUCCESS;
2412 xmlXPathObjectPtr result = NULL;
2413 char *string = NULL;
2415 if (!context) return IE_INVALID_CONTEXT;
2416 if (!db_owner_info) return IE_INVAL;
2417 isds_DbOwnerInfo_free(db_owner_info);
2418 if (!xpath_ctx) return IE_INVAL;
2421 *db_owner_info = calloc(1, sizeof(**db_owner_info));
2422 if (!*db_owner_info) {
2423 err = IE_NOMEM;
2424 goto leave;
2427 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
2429 EXTRACT_STRING("isds:dbType", string);
2430 if (string) {
2431 (*db_owner_info)->dbType =
2432 calloc(1, sizeof(*((*db_owner_info)->dbType)));
2433 if (!(*db_owner_info)->dbType) {
2434 err = IE_NOMEM;
2435 goto leave;
2437 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
2438 if (err) {
2439 zfree((*db_owner_info)->dbType);
2440 if (err == IE_ENUM) {
2441 err = IE_ISDS;
2442 char *string_locale = utf82locale(string);
2443 isds_printf_message(context, _("Unknown isds:dbType: %s"),
2444 string_locale);
2445 free(string_locale);
2447 goto leave;
2449 zfree(string);
2452 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
2454 err = extract_gPersonName(context, &(*db_owner_info)->personName,
2455 xpath_ctx);
2456 if (err) goto leave;
2458 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
2460 (*db_owner_info)->birthInfo =
2461 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
2462 if (!(*db_owner_info)->birthInfo) {
2463 err = IE_NOMEM;
2464 goto leave;
2466 err = extract_BiDate(context, &(*db_owner_info)->birthInfo->biDate,
2467 xpath_ctx);
2468 if (err) goto leave;
2469 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
2470 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
2471 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
2472 if (!(*db_owner_info)->birthInfo->biDate &&
2473 !(*db_owner_info)->birthInfo->biCity &&
2474 !(*db_owner_info)->birthInfo->biCounty &&
2475 !(*db_owner_info)->birthInfo->biState)
2476 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
2478 err = extract_gAddress(context, &(*db_owner_info)->address, xpath_ctx);
2479 if (err) goto leave;
2481 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
2482 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
2483 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
2484 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
2485 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
2487 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
2489 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
2490 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
2491 (*db_owner_info)->dbOpenAddressing);
2493 leave:
2494 if (err) isds_DbOwnerInfo_free(db_owner_info);
2495 free(string);
2496 xmlXPathFreeObject(result);
2497 return err;
2501 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
2502 * @context is sesstion context
2503 * @owner is libsids structure with box description
2504 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
2505 static isds_error insert_DbOwnerInfo(struct isds_ctx *context,
2506 const struct isds_DbOwnerInfo *owner, xmlNodePtr db_owner_info) {
2508 isds_error err = IE_SUCCESS;
2509 xmlNodePtr node;
2510 xmlChar *string = NULL;
2512 if (!context) return IE_INVALID_CONTEXT;
2513 if (!owner || !db_owner_info) return IE_INVAL;
2516 /* Build XSD:tDbOwnerInfo */
2517 CHECK_FOR_STRING_LENGTH(owner->dbID, 0, 7, "dbID")
2518 INSERT_STRING(db_owner_info, "dbID", owner->dbID);
2520 /* dbType */
2521 if (owner->dbType) {
2522 const xmlChar *type_string = isds_DbType2string(*(owner->dbType));
2523 if (!type_string) {
2524 isds_printf_message(context, _("Invalid dbType value: %d"),
2525 *(owner->dbType));
2526 err = IE_ENUM;
2527 goto leave;
2529 INSERT_STRING(db_owner_info, "dbType", type_string);
2531 INSERT_STRING(db_owner_info, "ic", owner->ic);
2532 if (owner->personName) {
2533 INSERT_STRING(db_owner_info, "pnFirstName",
2534 owner->personName->pnFirstName);
2535 INSERT_STRING(db_owner_info, "pnMiddleName",
2536 owner->personName->pnMiddleName);
2537 INSERT_STRING(db_owner_info, "pnLastName",
2538 owner->personName->pnLastName);
2539 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
2540 owner->personName->pnLastNameAtBirth);
2542 INSERT_STRING(db_owner_info, "firmName", owner->firmName);
2543 if (owner->birthInfo) {
2544 if (owner->birthInfo->biDate) {
2545 if (!tm2datestring(owner->birthInfo->biDate, &string))
2546 INSERT_STRING(db_owner_info, "biDate", string);
2547 free(string); string = NULL;
2549 INSERT_STRING(db_owner_info, "biCity", owner->birthInfo->biCity);
2550 INSERT_STRING(db_owner_info, "biCounty", owner->birthInfo->biCounty);
2551 INSERT_STRING(db_owner_info, "biState", owner->birthInfo->biState);
2553 if (owner->address) {
2554 INSERT_STRING(db_owner_info, "adCity", owner->address->adCity);
2555 INSERT_STRING(db_owner_info, "adStreet", owner->address->adStreet);
2556 INSERT_STRING(db_owner_info, "adNumberInStreet",
2557 owner->address->adNumberInStreet);
2558 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
2559 owner->address->adNumberInMunicipality);
2560 INSERT_STRING(db_owner_info, "adZipCode", owner->address->adZipCode);
2561 INSERT_STRING(db_owner_info, "adState", owner->address->adState);
2563 INSERT_STRING(db_owner_info, "nationality", owner->nationality);
2564 INSERT_STRING(db_owner_info, "email", owner->email);
2565 INSERT_STRING(db_owner_info, "telNumber", owner->telNumber);
2567 CHECK_FOR_STRING_LENGTH(owner->identifier, 0, 20, "identifier")
2568 INSERT_STRING(db_owner_info, "identifier", owner->identifier);
2570 CHECK_FOR_STRING_LENGTH(owner->registryCode, 0, 5, "registryCode")
2571 INSERT_STRING(db_owner_info, "registryCode", owner->registryCode);
2573 INSERT_LONGINT(db_owner_info, "dbState", owner->dbState, string);
2575 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", owner->dbEffectiveOVM);
2576 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
2577 owner->dbOpenAddressing);
2579 leave:
2580 free(string);
2581 return err;
2585 /* Convert XSD:tDbUserInfo XML tree into structure
2586 * @context is ISDS context
2587 * @db_user_info is automically reallocated user info structure
2588 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
2589 * In case of error @db_user_info will be freed. */
2590 static isds_error extract_DbUserInfo(struct isds_ctx *context,
2591 struct isds_DbUserInfo **db_user_info, xmlXPathContextPtr xpath_ctx) {
2592 isds_error err = IE_SUCCESS;
2593 xmlXPathObjectPtr result = NULL;
2594 char *string = NULL;
2596 if (!context) return IE_INVALID_CONTEXT;
2597 if (!db_user_info) return IE_INVAL;
2598 isds_DbUserInfo_free(db_user_info);
2599 if (!xpath_ctx) return IE_INVAL;
2602 *db_user_info = calloc(1, sizeof(**db_user_info));
2603 if (!*db_user_info) {
2604 err = IE_NOMEM;
2605 goto leave;
2608 EXTRACT_STRING("isds:userID", (*db_user_info)->userID);
2610 EXTRACT_STRING("isds:userType", string);
2611 if (string) {
2612 (*db_user_info)->userType =
2613 calloc(1, sizeof(*((*db_user_info)->userType)));
2614 if (!(*db_user_info)->userType) {
2615 err = IE_NOMEM;
2616 goto leave;
2618 err = string2isds_UserType((xmlChar *)string,
2619 (*db_user_info)->userType);
2620 if (err) {
2621 zfree((*db_user_info)->userType);
2622 if (err == IE_ENUM) {
2623 err = IE_ISDS;
2624 char *string_locale = utf82locale(string);
2625 isds_printf_message(context,
2626 _("Unknown isds:userType value: %s"), string_locale);
2627 free(string_locale);
2629 goto leave;
2631 zfree(string);
2634 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info)->userPrivils, 0);
2636 (*db_user_info)->personName =
2637 calloc(1, sizeof(*((*db_user_info)->personName)));
2638 if (!(*db_user_info)->personName) {
2639 err = IE_NOMEM;
2640 goto leave;
2643 err = extract_gPersonName(context, &(*db_user_info)->personName,
2644 xpath_ctx);
2645 if (err) goto leave;
2647 err = extract_gAddress(context, &(*db_user_info)->address, xpath_ctx);
2648 if (err) goto leave;
2650 err = extract_BiDate(context, &(*db_user_info)->biDate, xpath_ctx);
2651 if (err) goto leave;
2653 EXTRACT_STRING("isds:ic", (*db_user_info)->ic);
2654 EXTRACT_STRING("isds:firmName", (*db_user_info)->firmName);
2656 EXTRACT_STRING("isds:caStreet", (*db_user_info)->caStreet);
2657 EXTRACT_STRING("isds:caCity", (*db_user_info)->caCity);
2658 EXTRACT_STRING("isds:caZipCode", (*db_user_info)->caZipCode);
2660 leave:
2661 if (err) isds_DbUserInfo_free(db_user_info);
2662 free(string);
2663 xmlXPathFreeObject(result);
2664 return err;
2668 /* Insert struct isds_DbUserInfo data (user description) into XML tree
2669 * @context is sesstion context
2670 * @user is libsids structure with user description
2671 * @db_user_info is XML element of XSD:tDbUserInfo */
2672 static isds_error insert_DbUserInfo(struct isds_ctx *context,
2673 const struct isds_DbUserInfo *user, xmlNodePtr db_user_info) {
2675 isds_error err = IE_SUCCESS;
2676 xmlNodePtr node;
2677 xmlChar *string = NULL;
2679 if (!context) return IE_INVALID_CONTEXT;
2680 if (!user || !db_user_info) return IE_INVAL;
2682 /* Build XSD:tDbUserInfo */
2683 if (user->personName) {
2684 INSERT_STRING(db_user_info, "pnFirstName",
2685 user->personName->pnFirstName);
2686 INSERT_STRING(db_user_info, "pnMiddleName",
2687 user->personName->pnMiddleName);
2688 INSERT_STRING(db_user_info, "pnLastName",
2689 user->personName->pnLastName);
2690 INSERT_STRING(db_user_info, "pnLastNameAtBirth",
2691 user->personName->pnLastNameAtBirth);
2693 if (user->address) {
2694 INSERT_STRING(db_user_info, "adCity", user->address->adCity);
2695 INSERT_STRING(db_user_info, "adStreet", user->address->adStreet);
2696 INSERT_STRING(db_user_info, "adNumberInStreet",
2697 user->address->adNumberInStreet);
2698 INSERT_STRING(db_user_info, "adNumberInMunicipality",
2699 user->address->adNumberInMunicipality);
2700 INSERT_STRING(db_user_info, "adZipCode", user->address->adZipCode);
2701 INSERT_STRING(db_user_info, "adState", user->address->adState);
2703 if (user->biDate) {
2704 if (!tm2datestring(user->biDate, &string))
2705 INSERT_STRING(db_user_info, "biDate", string);
2706 zfree(string);
2708 CHECK_FOR_STRING_LENGTH(user->userID, 6, 12, "userID");
2709 INSERT_STRING(db_user_info, "userID", user->userID);
2711 /* userType */
2712 if (user->userType) {
2713 const xmlChar *type_string = isds_UserType2string(*(user->userType));
2714 if (!type_string) {
2715 isds_printf_message(context, _("Invalid userType value: %d"),
2716 *(user->userType));
2717 err = IE_ENUM;
2718 goto leave;
2720 INSERT_STRING(db_user_info, "userType", type_string);
2723 INSERT_LONGINT(db_user_info, "userPrivils", user->userPrivils, string);
2724 CHECK_FOR_STRING_LENGTH(user->ic, 0, 8, "ic")
2725 INSERT_STRING(db_user_info, "ic", user->ic);
2726 CHECK_FOR_STRING_LENGTH(user->firmName, 0, 100, "firmName")
2727 INSERT_STRING(db_user_info, "firmName", user->firmName);
2728 INSERT_STRING(db_user_info, "caStreet", user->caStreet);
2729 INSERT_STRING(db_user_info, "caCity", user->caCity);
2730 INSERT_STRING(db_user_info, "caZipCode", user->caZipCode);
2732 leave:
2733 free(string);
2734 return err;
2738 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
2739 * isds_envelope structure. The envelope is automatically allocated but not
2740 * reallocated. The date are just appended into envelope structure.
2741 * @context is ISDS context
2742 * @envelope is automically allocated message envelope structure
2743 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
2744 * In case of error @envelope will be freed. */
2745 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
2746 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2747 isds_error err = IE_SUCCESS;
2748 xmlXPathObjectPtr result = NULL;
2750 if (!context) return IE_INVALID_CONTEXT;
2751 if (!envelope) return IE_INVAL;
2752 if (!xpath_ctx) return IE_INVAL;
2755 if (!*envelope) {
2756 /* Allocate envelope */
2757 *envelope = calloc(1, sizeof(**envelope));
2758 if (!*envelope) {
2759 err = IE_NOMEM;
2760 goto leave;
2762 } else {
2763 /* Else free former data */
2764 zfree((*envelope)->dmSenderOrgUnit);
2765 zfree((*envelope)->dmSenderOrgUnitNum);
2766 zfree((*envelope)->dbIDRecipient);
2767 zfree((*envelope)->dmRecipientOrgUnit);
2768 zfree((*envelope)->dmSenderOrgUnitNum);
2769 zfree((*envelope)->dmToHands);
2770 zfree((*envelope)->dmAnnotation);
2771 zfree((*envelope)->dmRecipientRefNumber);
2772 zfree((*envelope)->dmSenderRefNumber);
2773 zfree((*envelope)->dmRecipientIdent);
2774 zfree((*envelope)->dmSenderIdent);
2775 zfree((*envelope)->dmLegalTitleLaw);
2776 zfree((*envelope)->dmLegalTitleYear);
2777 zfree((*envelope)->dmLegalTitleSect);
2778 zfree((*envelope)->dmLegalTitlePar);
2779 zfree((*envelope)->dmLegalTitlePoint);
2780 zfree((*envelope)->dmPersonalDelivery);
2781 zfree((*envelope)->dmAllowSubstDelivery);
2784 /* Extract envelope elements added by sender or ISDS
2785 * (XSD: gMessageEnvelopeSub type) */
2786 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
2787 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
2788 (*envelope)->dmSenderOrgUnitNum, 0);
2789 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
2790 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
2791 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
2792 (*envelope)->dmSenderOrgUnitNum, 0);
2793 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
2794 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
2795 EXTRACT_STRING("isds:dmRecipientRefNumber",
2796 (*envelope)->dmRecipientRefNumber);
2797 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
2798 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
2799 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
2801 /* Extract envelope elements regarding law refference */
2802 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
2803 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
2804 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
2805 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
2806 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
2808 /* Extract envelope other elements */
2809 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
2810 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
2811 (*envelope)->dmAllowSubstDelivery);
2813 leave:
2814 if (err) isds_envelope_free(envelope);
2815 xmlXPathFreeObject(result);
2816 return err;
2821 /* Convert XSD gMessageEnvelope group of elements from XML tree into
2822 * isds_envelope structure. The envelope is automatically allocated but not
2823 * reallocated. The date are just appended into envelope structure.
2824 * @context is ISDS context
2825 * @envelope is automically allocated message envelope structure
2826 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
2827 * In case of error @envelope will be freed. */
2828 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
2829 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2830 isds_error err = IE_SUCCESS;
2831 xmlXPathObjectPtr result = NULL;
2833 if (!context) return IE_INVALID_CONTEXT;
2834 if (!envelope) return IE_INVAL;
2835 if (!xpath_ctx) return IE_INVAL;
2838 if (!*envelope) {
2839 /* Allocate envelope */
2840 *envelope = calloc(1, sizeof(**envelope));
2841 if (!*envelope) {
2842 err = IE_NOMEM;
2843 goto leave;
2845 } else {
2846 /* Else free former data */
2847 zfree((*envelope)->dmID);
2848 zfree((*envelope)->dbIDSender);
2849 zfree((*envelope)->dmSender);
2850 zfree((*envelope)->dmSenderAddress);
2851 zfree((*envelope)->dmSenderType);
2852 zfree((*envelope)->dmRecipient);
2853 zfree((*envelope)->dmRecipientAddress);
2854 zfree((*envelope)->dmAmbiguousRecipient);
2857 /* Extract envelope elements added by ISDS
2858 * (XSD: gMessageEnvelope type) */
2859 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
2860 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
2861 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
2862 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
2863 /* XML Schema does not guaratee enumratation. It's plain xs:int. */
2864 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
2865 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
2866 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
2867 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
2868 (*envelope)->dmAmbiguousRecipient);
2870 /* Extract envelope elements added by sender and ISDS
2871 * (XSD: gMessageEnvelope type) */
2872 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
2873 if (err) goto leave;
2875 leave:
2876 if (err) isds_envelope_free(envelope);
2877 xmlXPathFreeObject(result);
2878 return err;
2882 /* Convert other envelope elements from XML tree into isds_envelope structure:
2883 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
2884 * The envelope is automatically allocated but not reallocated.
2885 * The data are just appended into envelope structure.
2886 * @context is ISDS context
2887 * @envelope is automically allocated message envelope structure
2888 * @xpath_ctx is XPath context with current node as parent desired elements
2889 * In case of error @envelope will be freed. */
2890 static isds_error append_status_size_times(struct isds_ctx *context,
2891 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2892 isds_error err = IE_SUCCESS;
2893 xmlXPathObjectPtr result = NULL;
2894 char *string = NULL;
2895 unsigned long int *unumber = NULL;
2897 if (!context) return IE_INVALID_CONTEXT;
2898 if (!envelope) return IE_INVAL;
2899 if (!xpath_ctx) return IE_INVAL;
2902 if (!*envelope) {
2903 /* Allocate new */
2904 *envelope = calloc(1, sizeof(**envelope));
2905 if (!*envelope) {
2906 err = IE_NOMEM;
2907 goto leave;
2909 } else {
2910 /* Free old data */
2911 zfree((*envelope)->dmMessageStatus);
2912 zfree((*envelope)->dmAttachmentSize);
2913 zfree((*envelope)->dmDeliveryTime);
2914 zfree((*envelope)->dmAcceptanceTime);
2918 /* dmMessageStatus element is mandatory */
2919 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
2920 if (!unumber) {
2921 isds_log_message(context,
2922 _("Missing mandatory sisds:dmMessageStatus integer"));
2923 err = IE_ISDS;
2924 goto leave;
2926 err = uint2isds_message_status(context, unumber,
2927 &((*envelope)->dmMessageStatus));
2928 if (err) {
2929 if (err == IE_ENUM) err = IE_ISDS;
2930 goto leave;
2932 free(unumber); unumber = NULL;
2934 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
2937 EXTRACT_STRING("sisds:dmDeliveryTime", string);
2938 if (string) {
2939 err = timestring2timeval((xmlChar *) string,
2940 &((*envelope)->dmDeliveryTime));
2941 if (err) {
2942 char *string_locale = utf82locale(string);
2943 if (err == IE_DATE) err = IE_ISDS;
2944 isds_printf_message(context,
2945 _("Could not convert dmDeliveryTime as ISO time: %s"),
2946 string_locale);
2947 free(string_locale);
2948 goto leave;
2950 zfree(string);
2953 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
2954 if (string) {
2955 err = timestring2timeval((xmlChar *) string,
2956 &((*envelope)->dmAcceptanceTime));
2957 if (err) {
2958 char *string_locale = utf82locale(string);
2959 if (err == IE_DATE) err = IE_ISDS;
2960 isds_printf_message(context,
2961 _("Could not convert dmAcceptanceTime as ISO time: %s"),
2962 string_locale);
2963 free(string_locale);
2964 goto leave;
2966 zfree(string);
2969 leave:
2970 if (err) isds_envelope_free(envelope);
2971 free(unumber);
2972 free(string);
2973 xmlXPathFreeObject(result);
2974 return err;
2978 /* Convert message type attribute of current element into isds_envelope
2979 * structure.
2980 * TODO: This function can be incorporated into append_status_size_times() as
2981 * they are called always together.
2982 * The envelope is automatically allocated but not reallocated.
2983 * The data are just appended into envelope structure.
2984 * @context is ISDS context
2985 * @envelope is automically allocated message envelope structure
2986 * @xpath_ctx is XPath context with current node as parent of attribute
2987 * carrying message type
2988 * In case of error @envelope will be freed. */
2989 static isds_error append_message_type(struct isds_ctx *context,
2990 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2991 isds_error err = IE_SUCCESS;
2993 if (!context) return IE_INVALID_CONTEXT;
2994 if (!envelope) return IE_INVAL;
2995 if (!xpath_ctx) return IE_INVAL;
2998 if (!*envelope) {
2999 /* Allocate new */
3000 *envelope = calloc(1, sizeof(**envelope));
3001 if (!*envelope) {
3002 err = IE_NOMEM;
3003 goto leave;
3005 } else {
3006 /* Free old data */
3007 zfree((*envelope)->dmType);
3011 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope)->dmType, 0);
3013 if (!(*envelope)->dmType) {
3014 /* Use default value */
3015 (*envelope)->dmType = strdup("V");
3016 if (!(*envelope)->dmType) {
3017 err = IE_NOMEM;
3018 goto leave;
3020 } else if (1 != xmlUTF8Strlen((xmlChar *) (*envelope)->dmType)) {
3021 char *type_locale = utf82locale((*envelope)->dmType);
3022 isds_printf_message(context,
3023 _("Message type in dmType attribute is not 1 character long: "
3024 "%s"),
3025 type_locale);
3026 free(type_locale);
3027 err = IE_ISDS;
3028 goto leave;
3031 leave:
3032 if (err) isds_envelope_free(envelope);
3033 return err;
3038 /* Extract message document into reallocated document structure
3039 * @context is ISDS context
3040 * @document is automically reallocated message documents structure
3041 * @xpath_ctx is XPath context with current node as isds:dmFile
3042 * In case of error @document will be freed. */
3043 static isds_error extract_document(struct isds_ctx *context,
3044 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
3045 isds_error err = IE_SUCCESS;
3046 xmlXPathObjectPtr result = NULL;
3047 xmlNodePtr file_node = xpath_ctx->node;
3048 char *string = NULL;
3050 if (!context) return IE_INVALID_CONTEXT;
3051 if (!document) return IE_INVAL;
3052 isds_document_free(document);
3053 if (!xpath_ctx) return IE_INVAL;
3055 *document = calloc(1, sizeof(**document));
3056 if (!*document) {
3057 err = IE_NOMEM;
3058 goto leave;
3061 /* Extract document metadata */
3062 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
3064 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
3065 err = string2isds_FileMetaType((xmlChar*)string,
3066 &((*document)->dmFileMetaType));
3067 if (err) {
3068 char *meta_type_locale = utf82locale(string);
3069 isds_printf_message(context,
3070 _("Document has invalid dmFileMetaType attribute value: %s"),
3071 meta_type_locale);
3072 free(meta_type_locale);
3073 err = IE_ISDS;
3074 goto leave;
3076 zfree(string);
3078 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
3079 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
3080 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
3081 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
3084 /* Extract document data.
3085 * Base64 encoded blob or XML subtree must be presented. */
3087 /* Check from dmEncodedContent */
3088 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
3089 xpath_ctx);
3090 if (!result) {
3091 err = IE_XML;
3092 goto leave;
3095 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3096 /* Here we have Base64 blob */
3098 if (result->nodesetval->nodeNr > 1) {
3099 isds_printf_message(context,
3100 _("Document has more dmEncodedContent elements"));
3101 err = IE_ISDS;
3102 goto leave;
3105 xmlXPathFreeObject(result); result = NULL;
3106 EXTRACT_STRING("isds:dmEncodedContent", string);
3108 /* Decode non-emptys document */
3109 if (string && string[0] != '\0') {
3110 (*document)->data_length = b64decode(string, &((*document)->data));
3111 if ((*document)->data_length == (size_t) -1) {
3112 isds_printf_message(context,
3113 _("Error while Base64-decoding document content"));
3114 err = IE_ERROR;
3115 goto leave;
3118 } else {
3119 /* No Base64 blob, try XML document */
3120 xmlXPathFreeObject(result); result = NULL;
3121 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
3122 xpath_ctx);
3123 if (!result) {
3124 err = IE_XML;
3125 goto leave;
3128 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3129 /* Here we have XML document */
3131 if (result->nodesetval->nodeNr > 1) {
3132 isds_printf_message(context,
3133 _("Document has more dmXMLContent elements"));
3134 err = IE_ISDS;
3135 goto leave;
3138 /* FIXME: Serialize the tree rooted at result's node */
3139 isds_printf_message(context,
3140 _("XML documents not yet supported"));
3141 err = IE_NOTSUP;
3142 goto leave;
3143 } else {
3144 /* No bas64 blob, nor XML document */
3145 isds_printf_message(context,
3146 _("Document has no dmEncodedContent, nor dmXMLContent "
3147 "element"));
3148 err = IE_ISDS;
3149 goto leave;
3154 leave:
3155 if (err) isds_document_free(document);
3156 free(string);
3157 xmlXPathFreeObject(result);
3158 xpath_ctx->node = file_node;
3159 return err;
3164 /* Extract message documents into reallocated list of documents
3165 * @context is ISDS context
3166 * @documents is automically reallocated message documents list structure
3167 * @xpath_ctx is XPath context with current node as XSD tFilesArray
3168 * In case of error @documents will be freed. */
3169 static isds_error extract_documents(struct isds_ctx *context,
3170 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
3171 isds_error err = IE_SUCCESS;
3172 xmlXPathObjectPtr result = NULL;
3173 xmlNodePtr files_node = xpath_ctx->node;
3174 struct isds_list *document, *prev_document;
3176 if (!context) return IE_INVALID_CONTEXT;
3177 if (!documents) return IE_INVAL;
3178 isds_list_free(documents);
3179 if (!xpath_ctx) return IE_INVAL;
3181 /* Find documents */
3182 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
3183 if (!result) {
3184 err = IE_XML;
3185 goto leave;
3188 /* No match */
3189 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3190 isds_printf_message(context,
3191 _("Message does not contain any document"));
3192 err = IE_ISDS;
3193 goto leave;
3197 /* Iterate over documents */
3198 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
3200 /* Allocate and append list item */
3201 document = calloc(1, sizeof(*document));
3202 if (!document) {
3203 err = IE_NOMEM;
3204 goto leave;
3206 document->destructor = (void (*)(void **))isds_document_free;
3207 if (i == 0) *documents = document;
3208 else prev_document->next = document;
3209 prev_document = document;
3211 /* Extract document */
3212 xpath_ctx->node = result->nodesetval->nodeTab[i];
3213 err = extract_document(context,
3214 (struct isds_document **) &(document->data), xpath_ctx);
3215 if (err) goto leave;
3219 leave:
3220 if (err) isds_list_free(documents);
3221 xmlXPathFreeObject(result);
3222 xpath_ctx->node = files_node;
3223 return err;
3227 /* Convert isds:dmRecord XML tree into structure
3228 * @context is ISDS context
3229 * @envelope is automically reallocated message envelope structure
3230 * @xpath_ctx is XPath context with current node as isds:dmRecord element
3231 * In case of error @envelope will be freed. */
3232 static isds_error extract_DmRecord(struct isds_ctx *context,
3233 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3234 isds_error err = IE_SUCCESS;
3235 xmlXPathObjectPtr result = NULL;
3237 if (!context) return IE_INVALID_CONTEXT;
3238 if (!envelope) return IE_INVAL;
3239 isds_envelope_free(envelope);
3240 if (!xpath_ctx) return IE_INVAL;
3243 *envelope = calloc(1, sizeof(**envelope));
3244 if (!*envelope) {
3245 err = IE_NOMEM;
3246 goto leave;
3250 /* Extract tRecord data */
3251 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
3253 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
3254 * dmAcceptanceTime. */
3255 err = append_status_size_times(context, envelope, xpath_ctx);
3256 if (err) goto leave;
3258 /* Extract envelope elements added by sender and ISDS
3259 * (XSD: gMessageEnvelope type) */
3260 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
3261 if (err) goto leave;
3262 /* dmOVM can not be obtained from ISDS */
3264 /* Get message type */
3265 err = append_message_type(context, envelope, xpath_ctx);
3266 if (err) goto leave;
3269 leave:
3270 if (err) isds_envelope_free(envelope);
3271 xmlXPathFreeObject(result);
3272 return err;
3276 /* Find and convert isds:dmHash XML tree into structure
3277 * @context is ISDS context
3278 * @envelope is automically reallocated message hash structure
3279 * @xpath_ctx is XPath context with current node containing isds:dmHash child
3280 * In case of error @hash will be freed. */
3281 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
3282 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
3283 isds_error err = IE_SUCCESS;
3284 xmlNodePtr old_ctx_node;
3285 xmlXPathObjectPtr result = NULL;
3286 char *string = NULL;
3288 if (!context) return IE_INVALID_CONTEXT;
3289 if (!hash) return IE_INVAL;
3290 isds_hash_free(hash);
3291 if (!xpath_ctx) return IE_INVAL;
3293 old_ctx_node = xpath_ctx->node;
3295 *hash = calloc(1, sizeof(**hash));
3296 if (!*hash) {
3297 err = IE_NOMEM;
3298 goto leave;
3301 /* Locate dmHash */
3302 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
3303 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
3304 err = IE_ISDS;
3305 goto leave;
3307 if (err) {
3308 err = IE_ERROR;
3309 goto leave;
3312 /* Get hash algorithm */
3313 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
3314 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
3315 if (err) {
3316 if (err == IE_ENUM) {
3317 char *string_locale = utf82locale(string);
3318 isds_printf_message(context, _("Unsported hash algorithm: %s"),
3319 string_locale);
3320 free(string_locale);
3322 goto leave;
3324 zfree(string);
3326 /* Get hash value */
3327 EXTRACT_STRING(".", string);
3328 if (!string) {
3329 isds_printf_message(context,
3330 _("sisds:dmHash element is missing hash value"));
3331 err = IE_ISDS;
3332 goto leave;
3334 (*hash)->length = b64decode(string, &((*hash)->value));
3335 if ((*hash)->length == (size_t) -1) {
3336 isds_printf_message(context,
3337 _("Error while Base64-decoding hash value"));
3338 err = IE_ERROR;
3339 goto leave;
3342 leave:
3343 if (err) isds_hash_free(hash);
3344 free(string);
3345 xmlXPathFreeObject(result);
3346 xpath_ctx->node = old_ctx_node;
3347 return err;
3351 /* Find and append isds:dmQTimestamp XML tree into envelope
3352 * @context is ISDS context
3353 * @envelope is automically allocated evnelope structure
3354 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
3355 * child
3356 * In case of error @envelope will be freed. */
3357 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
3358 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3359 isds_error err = IE_SUCCESS;
3360 xmlXPathObjectPtr result = NULL;
3361 char *string = NULL;
3363 if (!context) return IE_INVALID_CONTEXT;
3364 if (!envelope) return IE_INVAL;
3365 if (!xpath_ctx) {
3366 isds_envelope_free(envelope);
3367 return IE_INVAL;
3370 if (!*envelope) {
3371 *envelope = calloc(1, sizeof(**envelope));
3372 if (!*envelope) {
3373 err = IE_NOMEM;
3374 goto leave;
3376 } else {
3377 zfree((*envelope)->timestamp);
3378 (*envelope)->timestamp_length = 0;
3381 /* Get dmQTimestamp */
3382 EXTRACT_STRING("sisds:dmQTimestamp", string);
3383 if (!string) {
3384 isds_printf_message(context, _("Missing dmQTimestamp element content"));
3385 err = IE_ISDS;
3386 goto leave;
3388 (*envelope)->timestamp_length =
3389 b64decode(string, &((*envelope)->timestamp));
3390 if ((*envelope)->timestamp_length == (size_t) -1) {
3391 isds_printf_message(context,
3392 _("Error while Base64-decoding timestamp value"));
3393 err = IE_ERROR;
3394 goto leave;
3397 leave:
3398 if (err) isds_envelope_free(envelope);
3399 free(string);
3400 xmlXPathFreeObject(result);
3401 return err;
3405 /* Convert XSD tReturnedMessage XML tree into message structure.
3406 * It doea not store XML tree into message->raw.
3407 * @context is ISDS context
3408 * @include_documents Use true if documents must be extracted
3409 * (tReturnedMessage XSD type), use false if documents shall be ommited
3410 * (tReturnedMessageEnvelope).
3411 * @message is automically reallocated message structure
3412 * @xpath_ctx is XPath context with current node as tReturnedMessage element
3413 * type
3414 * In case of error @message will be freed. */
3415 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
3416 const _Bool include_documents, struct isds_message **message,
3417 xmlXPathContextPtr xpath_ctx) {
3418 isds_error err = IE_SUCCESS;
3419 xmlNodePtr message_node;
3421 if (!context) return IE_INVALID_CONTEXT;
3422 if (!message) return IE_INVAL;
3423 isds_message_free(message);
3424 if (!xpath_ctx) return IE_INVAL;
3427 *message = calloc(1, sizeof(**message));
3428 if (!*message) {
3429 err = IE_NOMEM;
3430 goto leave;
3433 /* Save message XPATH context node */
3434 message_node = xpath_ctx->node;
3437 /* Extract dmDM */
3438 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
3439 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
3440 if (err) { err = IE_ERROR; goto leave; }
3441 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
3442 if (err) goto leave;
3444 if (include_documents) {
3445 /* Extract dmFiles */
3446 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
3447 xpath_ctx);
3448 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
3449 err = IE_ISDS; goto leave;
3451 if (err) { err = IE_ERROR; goto leave; }
3452 err = extract_documents(context, &((*message)->documents), xpath_ctx);
3453 if (err) goto leave;
3457 /* Restore context to message */
3458 xpath_ctx->node = message_node;
3460 /* Extract dmHash */
3461 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
3462 xpath_ctx);
3463 if (err) goto leave;
3465 /* Extract dmQTimestamp, */
3466 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
3467 xpath_ctx);
3468 if (err) goto leave;
3470 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
3471 * dmAcceptanceTime. */
3472 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
3473 if (err) goto leave;
3475 /* Get message type */
3476 err = append_message_type(context, &((*message)->envelope), xpath_ctx);
3477 if (err) goto leave;
3479 leave:
3480 if (err) isds_message_free(message);
3481 return err;
3485 /* Extract message event into reallocated isds_event structure
3486 * @context is ISDS context
3487 * @event is automically reallocated message event structure
3488 * @xpath_ctx is XPath context with current node as isds:dmEvent
3489 * In case of error @event will be freed. */
3490 static isds_error extract_event(struct isds_ctx *context,
3491 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
3492 isds_error err = IE_SUCCESS;
3493 xmlXPathObjectPtr result = NULL;
3494 xmlNodePtr event_node = xpath_ctx->node;
3495 char *string = NULL;
3497 if (!context) return IE_INVALID_CONTEXT;
3498 if (!event) return IE_INVAL;
3499 isds_event_free(event);
3500 if (!xpath_ctx) return IE_INVAL;
3502 *event = calloc(1, sizeof(**event));
3503 if (!*event) {
3504 err = IE_NOMEM;
3505 goto leave;
3508 /* Extract event data.
3509 * All elements are optional according XSD. That's funny. */
3510 EXTRACT_STRING("sisds:dmEventTime", string);
3511 if (string) {
3512 err = timestring2timeval((xmlChar *) string, &((*event)->time));
3513 if (err) {
3514 char *string_locale = utf82locale(string);
3515 if (err == IE_DATE) err = IE_ISDS;
3516 isds_printf_message(context,
3517 _("Could not convert dmEventTime as ISO time: %s"),
3518 string_locale);
3519 free(string_locale);
3520 goto leave;
3522 zfree(string);
3525 /* dmEventDescr element has prefix and the rest */
3526 EXTRACT_STRING("sisds:dmEventDescr", string);
3527 if (string) {
3528 err = eventstring2event((xmlChar *) string, *event);
3529 if (err) goto leave;
3530 zfree(string);
3533 leave:
3534 if (err) isds_event_free(event);
3535 free(string);
3536 xmlXPathFreeObject(result);
3537 xpath_ctx->node = event_node;
3538 return err;
3542 /* Convert element of XSD tEventsArray type from XML tree into
3543 * isds_list of isds_event's structure. The list is automatically reallocated.
3544 * @context is ISDS context
3545 * @events is automically reallocated list of event structures
3546 * @xpath_ctx is XPath context with current node as tEventsArray
3547 * In case of error @evnets will be freed. */
3548 static isds_error extract_events(struct isds_ctx *context,
3549 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
3550 isds_error err = IE_SUCCESS;
3551 xmlXPathObjectPtr result = NULL;
3552 xmlNodePtr events_node = xpath_ctx->node;
3553 struct isds_list *event, *prev_event = NULL;
3555 if (!context) return IE_INVALID_CONTEXT;
3556 if (!events) return IE_INVAL;
3557 if (!xpath_ctx) return IE_INVAL;
3559 /* Free old list */
3560 isds_list_free(events);
3562 /* Find events */
3563 result = xmlXPathEvalExpression(BAD_CAST "sisds:dmEvent", xpath_ctx);
3564 if (!result) {
3565 err = IE_XML;
3566 goto leave;
3569 /* No match */
3570 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3571 isds_printf_message(context,
3572 _("Delivery info does not contain any event"));
3573 err = IE_ISDS;
3574 goto leave;
3578 /* Iterate over events */
3579 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
3581 /* Allocate and append list item */
3582 event = calloc(1, sizeof(*event));
3583 if (!event) {
3584 err = IE_NOMEM;
3585 goto leave;
3587 event->destructor = (void (*)(void **))isds_event_free;
3588 if (i == 0) *events = event;
3589 else prev_event->next = event;
3590 prev_event = event;
3592 /* Extract event */
3593 xpath_ctx->node = result->nodesetval->nodeTab[i];
3594 err = extract_event(context,
3595 (struct isds_event **) &(event->data), xpath_ctx);
3596 if (err) goto leave;
3600 leave:
3601 if (err) isds_list_free(events);
3602 xmlXPathFreeObject(result);
3603 xpath_ctx->node = events_node;
3604 return err;
3608 /* Convert isds_document structure into XML tree and append to dmFiles node.
3609 * @context is session context
3610 * @document is ISDS document
3611 * @dm_files is XML element the resulting tree will be appended to as a child.
3612 * @return error code, in case of error context' message is filled. */
3613 static isds_error insert_document(struct isds_ctx *context,
3614 struct isds_document *document, xmlNodePtr dm_files) {
3615 isds_error err = IE_SUCCESS;
3616 xmlNodePtr new_file = NULL, file = NULL, node;
3617 xmlAttrPtr attribute_node;
3618 xmlChar *base64data = NULL;
3620 if (!context) return IE_INVALID_CONTEXT;
3621 if (!document || !dm_files) return IE_INVAL;
3623 /* Allocate new dmFile */
3624 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
3625 if (!new_file) {
3626 isds_printf_message(context, _("Could not allocate main dmFile"));
3627 err = IE_ERROR;
3628 goto leave;
3630 /* Append the new dmFile.
3631 * XXX: Main document must go first */
3632 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
3633 file = xmlAddPrevSibling(dm_files->children, new_file);
3634 else
3635 file = xmlAddChild(dm_files, new_file);
3637 if (!file) {
3638 xmlFreeNode(new_file); new_file = NULL;
3639 isds_printf_message(context, _("Could not add dmFile child to "
3640 "%s element"), dm_files->name);
3641 err = IE_ERROR;
3642 goto leave;
3645 /* @dmMimeType is required */
3646 if (!document->dmMimeType) {
3647 isds_log_message(context,
3648 _("Document is missing mandatory MIME type definition"));
3649 err = IE_INVAL;
3650 goto leave;
3652 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
3654 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
3655 if (!string) {
3656 isds_printf_message(context,
3657 _("Document has unknown dmFileMetaType: %ld"),
3658 document->dmFileMetaType);
3659 err = IE_ENUM;
3660 goto leave;
3662 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
3664 if (document->dmFileGuid) {
3665 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
3667 if (document->dmUpFileGuid) {
3668 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
3671 /* @dmFileDescr is required */
3672 if (!document->dmFileDescr) {
3673 isds_log_message(context,
3674 _("Document is missing mandatory description (title)"));
3675 err = IE_INVAL;
3676 goto leave;
3678 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
3680 if (document->dmFormat) {
3681 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
3685 /* Insert content (data) of the document. */
3686 /* XXX; Only base64 is implemented currently. */
3687 base64data = (xmlChar *) b64encode(document->data, document->data_length);
3688 if (!base64data) {
3689 isds_printf_message(context,
3690 ngettext("Not enought memory to encode %zd bytes into Base64",
3691 "Not enought memory to encode %zd bytes into Base64",
3692 document->data_length),
3693 document->data_length);
3694 err = IE_NOMEM;
3695 goto leave;
3697 INSERT_STRING(file, "dmEncodedContent", base64data);
3698 free(base64data);
3700 leave:
3701 return err;
3705 /* Append XSD tMStatus XML tree into isds_message_copy structure.
3706 * The copy must pre prealocated, the date are just appended into structure.
3707 * @context is ISDS context
3708 * @copy is message copy struture
3709 * @xpath_ctx is XPath context with current node as tMStatus */
3710 static isds_error append_TMStatus(struct isds_ctx *context,
3711 struct isds_message_copy *copy, xmlXPathContextPtr xpath_ctx) {
3712 isds_error err = IE_SUCCESS;
3713 xmlXPathObjectPtr result = NULL;
3714 char *code = NULL, *message = NULL;
3716 if (!context) return IE_INVALID_CONTEXT;
3717 if (!copy || !xpath_ctx) return IE_INVAL;
3719 /* Free old values */
3720 zfree(copy->dmStatus);
3721 zfree(copy->dmID);
3723 /* Get error specific to this copy */
3724 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code);
3725 if (!code) {
3726 isds_log_message(context,
3727 _("Missing isds:dmStatusCode under "
3728 "XSD:tMStatus type element"));
3729 err = IE_ISDS;
3730 goto leave;
3733 if (xmlStrcmp((const xmlChar *)code, BAD_CAST "0000")) {
3734 /* This copy failed */
3735 copy->error = IE_ISDS;
3736 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message);
3737 if (message) {
3738 copy->dmStatus = astrcat3(code, ": ", message);
3739 if (!copy->dmStatus) {
3740 copy->dmStatus = code;
3741 code = NULL;
3743 } else {
3744 copy->dmStatus = code;
3745 code = NULL;
3747 } else {
3748 /* This copy succeeded. In this case only, message ID is valid */
3749 copy->error = IE_SUCCESS;
3751 EXTRACT_STRING("isds:dmID", copy->dmID);
3752 if (!copy->dmID) {
3753 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
3754 "but did not returned assigned message ID\n"));
3755 err = IE_ISDS;
3759 leave:
3760 free(code);
3761 free(message);
3762 xmlXPathFreeObject(result);
3763 return err;
3767 /* Insert struct isds_approval data (box approval) into XML tree
3768 * @context is sesstion context
3769 * @approval is libsids structure with approval description. NULL is
3770 * acceptible.
3771 * @parent is XML element to append @approval to */
3772 static isds_error insert_GExtApproval(struct isds_ctx *context,
3773 const struct isds_approval *approval, xmlNodePtr parent) {
3775 isds_error err = IE_SUCCESS;
3776 xmlNodePtr node;
3778 if (!context) return IE_INVALID_CONTEXT;
3779 if (!parent) return IE_INVAL;
3781 if (!approval) return IE_SUCCESS;
3783 /* Build XSD:gExtApproval */
3784 INSERT_SCALAR_BOOLEAN(parent, "dbApproved", approval->approved);
3785 INSERT_STRING(parent, "dbExternRefNumber", approval->refference);
3787 leave:
3788 return err;
3792 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
3793 * code
3794 * @context is session context
3795 * @service_name is name of SERVICE_DB_ACCESS
3796 * @response is server SOAP body response as XML document
3797 * @raw_response is automatically reallocated bitstream with response body. Use
3798 * NULL if you don't care
3799 * @raw_response_length is size of @raw_response in bytes
3800 * @code is ISDS status code
3801 * @status_message is ISDS status message
3802 * @return error coded from lower layer, context message will be set up
3803 * appropriately. */
3804 static isds_error build_send_check_dbdummy_request(struct isds_ctx *context,
3805 const xmlChar *service_name,
3806 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
3807 xmlChar **code, xmlChar **status_message) {
3809 isds_error err = IE_SUCCESS;
3810 char *service_name_locale = NULL;
3811 xmlNodePtr request = NULL, node;
3812 xmlNsPtr isds_ns = NULL;
3814 if (!context) return IE_INVALID_CONTEXT;
3815 if (!service_name) return IE_INVAL;
3816 if (!response || !code || !status_message) return IE_INVAL;
3817 if (!raw_response_length && raw_response) return IE_INVAL;
3819 /* Free output argument */
3820 xmlFreeDoc(*response); *response = NULL;
3821 if (raw_response) zfree(*raw_response);
3822 free(*code);
3823 free(*status_message);
3826 /* Check if connection is established
3827 * TODO: This check should be done donwstairs. */
3828 if (!context->curl) return IE_CONNECTION_CLOSED;
3830 service_name_locale = utf82locale((char*)service_name);
3831 if (!service_name_locale) {
3832 err = IE_NOMEM;
3833 goto leave;
3836 /* Build request */
3837 request = xmlNewNode(NULL, service_name);
3838 if (!request) {
3839 isds_printf_message(context,
3840 _("Could not build %s request"), service_name_locale);
3841 err = IE_ERROR;
3842 goto leave;
3844 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
3845 if(!isds_ns) {
3846 isds_log_message(context, _("Could not create ISDS name space"));
3847 err = IE_ERROR;
3848 goto leave;
3850 xmlSetNs(request, isds_ns);
3853 /* Add XSD:tDummyInput child */
3854 INSERT_STRING(request, "dbDummy", NULL);
3857 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
3858 service_name_locale);
3860 /* Send request */
3861 err = isds(context, SERVICE_DB_ACCESS, request, response,
3862 raw_response, raw_response_length);
3863 xmlFreeNode(request); request = NULL;
3865 if (err) {
3866 isds_log(ILF_ISDS, ILL_DEBUG,
3867 _("Processing ISDS response on %s request failed\n"),
3868 service_name_locale);
3869 goto leave;
3872 /* Check for response status */
3873 err = isds_response_status(context, SERVICE_DB_ACCESS, *response,
3874 code, status_message, NULL);
3875 if (err) {
3876 isds_log(ILF_ISDS, ILL_DEBUG,
3877 _("ISDS response on %s request is missing status\n"),
3878 service_name_locale);
3879 goto leave;
3882 /* Request processed, but nothing found */
3883 if (xmlStrcmp(*code, BAD_CAST "0000")) {
3884 char *code_locale = utf82locale((char*) *code);
3885 char *status_message_locale = utf82locale((char*) *status_message);
3886 isds_log(ILF_ISDS, ILL_DEBUG,
3887 _("Server refused %s request (code=%s, message=%s)\n"),
3888 service_name_locale, code_locale, status_message_locale);
3889 isds_log_message(context, status_message_locale);
3890 free(code_locale);
3891 free(status_message_locale);
3892 err = IE_ISDS;
3893 goto leave;
3896 leave:
3897 free(service_name_locale);
3898 xmlFreeNode(request);
3899 return err;
3903 /* Get data about logged in user and his box. */
3904 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
3905 struct isds_DbOwnerInfo **db_owner_info) {
3906 isds_error err = IE_SUCCESS;
3907 xmlDocPtr response = NULL;
3908 xmlChar *code = NULL, *message = NULL;
3909 xmlXPathContextPtr xpath_ctx = NULL;
3910 xmlXPathObjectPtr result = NULL;
3911 char *string = NULL;
3913 if (!context) return IE_INVALID_CONTEXT;
3914 zfree(context->long_message);
3915 if (!db_owner_info) return IE_INVAL;
3917 /* Check if connection is established */
3918 if (!context->curl) return IE_CONNECTION_CLOSED;
3921 /* Do request and check for success */
3922 err = build_send_check_dbdummy_request(context,
3923 BAD_CAST "GetOwnerInfoFromLogin",
3924 &response, NULL, NULL, &code, &message);
3925 if (err) goto leave;
3928 /* Extract data */
3929 /* Prepare stucture */
3930 isds_DbOwnerInfo_free(db_owner_info);
3931 *db_owner_info = calloc(1, sizeof(**db_owner_info));
3932 if (!*db_owner_info) {
3933 err = IE_NOMEM;
3934 goto leave;
3936 xpath_ctx = xmlXPathNewContext(response);
3937 if (!xpath_ctx) {
3938 err = IE_ERROR;
3939 goto leave;
3941 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
3942 err = IE_ERROR;
3943 goto leave;
3946 /* Set context node */
3947 result = xmlXPathEvalExpression(BAD_CAST
3948 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
3949 if (!result) {
3950 err = IE_ERROR;
3951 goto leave;
3953 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3954 isds_log_message(context, _("Missing dbOwnerInfo element"));
3955 err = IE_ISDS;
3956 goto leave;
3958 if (result->nodesetval->nodeNr > 1) {
3959 isds_log_message(context, _("Multiple dbOwnerInfo element"));
3960 err = IE_ISDS;
3961 goto leave;
3963 xpath_ctx->node = result->nodesetval->nodeTab[0];
3964 xmlXPathFreeObject(result); result = NULL;
3966 /* Extract it */
3967 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
3969 leave:
3970 if (err) {
3971 isds_DbOwnerInfo_free(db_owner_info);
3974 free(string);
3975 xmlXPathFreeObject(result);
3976 xmlXPathFreeContext(xpath_ctx);
3978 free(code);
3979 free(message);
3980 xmlFreeDoc(response);
3982 if (!err)
3983 isds_log(ILF_ISDS, ILL_DEBUG,
3984 _("GetOwnerInfoFromLogin request processed by server "
3985 "successfully.\n"));
3987 return err;
3991 /* Get data about logged in user. */
3992 isds_error isds_GetUserInfoFromLogin(struct isds_ctx *context,
3993 struct isds_DbUserInfo **db_user_info) {
3994 isds_error err = IE_SUCCESS;
3995 xmlDocPtr response = NULL;
3996 xmlChar *code = NULL, *message = NULL;
3997 xmlXPathContextPtr xpath_ctx = NULL;
3998 xmlXPathObjectPtr result = NULL;
4000 if (!context) return IE_INVALID_CONTEXT;
4001 zfree(context->long_message);
4002 if (!db_user_info) return IE_INVAL;
4004 /* Check if connection is established */
4005 if (!context->curl) return IE_CONNECTION_CLOSED;
4008 /* Do request and check for success */
4009 err = build_send_check_dbdummy_request(context,
4010 BAD_CAST "GetUserInfoFromLogin",
4011 &response, NULL, NULL, &code, &message);
4012 if (err) goto leave;
4015 /* Extract data */
4016 /* Prepare stucture */
4017 isds_DbUserInfo_free(db_user_info);
4018 *db_user_info = calloc(1, sizeof(**db_user_info));
4019 if (!*db_user_info) {
4020 err = IE_NOMEM;
4021 goto leave;
4023 xpath_ctx = xmlXPathNewContext(response);
4024 if (!xpath_ctx) {
4025 err = IE_ERROR;
4026 goto leave;
4028 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4029 err = IE_ERROR;
4030 goto leave;
4033 /* Set context node */
4034 result = xmlXPathEvalExpression(BAD_CAST
4035 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx);
4036 if (!result) {
4037 err = IE_ERROR;
4038 goto leave;
4040 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4041 isds_log_message(context, _("Missing dbUserInfo element"));
4042 err = IE_ISDS;
4043 goto leave;
4045 if (result->nodesetval->nodeNr > 1) {
4046 isds_log_message(context, _("Multiple dbUserInfo element"));
4047 err = IE_ISDS;
4048 goto leave;
4050 xpath_ctx->node = result->nodesetval->nodeTab[0];
4051 xmlXPathFreeObject(result); result = NULL;
4053 /* Extract it */
4054 err = extract_DbUserInfo(context, db_user_info, xpath_ctx);
4056 leave:
4057 if (err) {
4058 isds_DbUserInfo_free(db_user_info);
4061 xmlXPathFreeObject(result);
4062 xmlXPathFreeContext(xpath_ctx);
4064 free(code);
4065 free(message);
4066 xmlFreeDoc(response);
4068 if (!err)
4069 isds_log(ILF_ISDS, ILL_DEBUG,
4070 _("GetUserInfoFromLogin request processed by server "
4071 "successfully.\n"));
4073 return err;
4077 /* Get expiration time of current password
4078 * @context is session context
4079 * @expiration is automatically reallocated time when password expires, In
4080 * case of error will be nulled. */
4081 isds_error isds_get_password_expiration(struct isds_ctx *context,
4082 struct timeval **expiration) {
4083 isds_error err = IE_SUCCESS;
4084 xmlDocPtr response = NULL;
4085 xmlChar *code = NULL, *message = NULL;
4086 xmlXPathContextPtr xpath_ctx = NULL;
4087 xmlXPathObjectPtr result = NULL;
4088 char *string = NULL;
4090 if (!context) return IE_INVALID_CONTEXT;
4091 zfree(context->long_message);
4092 if (!expiration) return IE_INVAL;
4094 /* Check if connection is established */
4095 if (!context->curl) return IE_CONNECTION_CLOSED;
4098 /* Do request and check for success */
4099 err = build_send_check_dbdummy_request(context,
4100 BAD_CAST "GetPasswordInfo",
4101 &response, NULL, NULL, &code, &message);
4102 if (err) goto leave;
4105 /* Extract data */
4106 xpath_ctx = xmlXPathNewContext(response);
4107 if (!xpath_ctx) {
4108 err = IE_ERROR;
4109 goto leave;
4111 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4112 err = IE_ERROR;
4113 goto leave;
4116 /* Set context node */
4117 result = xmlXPathEvalExpression(BAD_CAST
4118 "/isds:GetPasswordInfoResponse", xpath_ctx);
4119 if (!result) {
4120 err = IE_ERROR;
4121 goto leave;
4123 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4124 isds_log_message(context,
4125 _("Missing GetPasswordInfoResponse element"));
4126 err = IE_ISDS;
4127 goto leave;
4129 if (result->nodesetval->nodeNr > 1) {
4130 isds_log_message(context,
4131 _("Multiple GetPasswordInfoResponse element"));
4132 err = IE_ISDS;
4133 goto leave;
4135 xpath_ctx->node = result->nodesetval->nodeTab[0];
4136 xmlXPathFreeObject(result); result = NULL;
4138 /* Extract expiration date */
4139 EXTRACT_STRING("isds:pswExpDate", string);
4140 if (!string) {
4141 isds_log_message(context, _("Missing pswExpDate element"));
4142 err = IE_ISDS;
4143 goto leave;
4146 err = timestring2timeval((xmlChar *) string, expiration);
4147 if (err) {
4148 char *string_locale = utf82locale(string);
4149 if (err == IE_DATE) err = IE_ISDS;
4150 isds_printf_message(context,
4151 _("Could not convert pswExpDate as ISO time: %s"),
4152 string_locale);
4153 free(string_locale);
4154 goto leave;
4157 leave:
4158 if (err) {
4159 if (*expiration) {
4160 zfree(*expiration);
4164 free(string);
4165 xmlXPathFreeObject(result);
4166 xmlXPathFreeContext(xpath_ctx);
4168 free(code);
4169 free(message);
4170 xmlFreeDoc(response);
4172 if (!err)
4173 isds_log(ILF_ISDS, ILL_DEBUG,
4174 _("GetPasswordInfo request processed by server "
4175 "successfully.\n"));
4177 return err;
4181 /* Change user password in ISDS.
4182 * User must supply old password, new password will takes effect after some
4183 * time, current session can continue. Password must fulfill some constraints.
4184 * @context is session context
4185 * @old_password is current password.
4186 * @new_password is requested new password */
4187 isds_error isds_change_password(struct isds_ctx *context,
4188 const char *old_password, const char *new_password) {
4189 isds_error err = IE_SUCCESS;
4190 xmlNsPtr isds_ns = NULL;
4191 xmlNodePtr request = NULL, node;
4192 xmlDocPtr response = NULL;
4193 xmlChar *code = NULL, *message = NULL;
4195 if (!context) return IE_INVALID_CONTEXT;
4196 zfree(context->long_message);
4197 if (!old_password || !new_password) return IE_INVAL;
4199 /* Check if connection is established
4200 * TODO: This check should be done donwstairs. */
4201 if (!context->curl) return IE_CONNECTION_CLOSED;
4204 /* Build ChangeISDSPassword request */
4205 request = xmlNewNode(NULL, BAD_CAST "ChangeISDSPassword");
4206 if (!request) {
4207 isds_log_message(context,
4208 _("Could not build ChangeISDSPassword request"));
4209 return IE_ERROR;
4211 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4212 if(!isds_ns) {
4213 isds_log_message(context, _("Could not create ISDS name space"));
4214 xmlFreeNode(request);
4215 return IE_ERROR;
4217 xmlSetNs(request, isds_ns);
4219 INSERT_STRING(request, "dbOldPassword", old_password);
4220 INSERT_STRING(request, "dbNewPassword", new_password);
4223 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CheckDataBox request to ISDS\n"));
4225 /* Sent request */
4226 err = isds(context, SERVICE_DB_ACCESS, request, &response, NULL, NULL);
4228 /* Destroy request */
4229 xmlFreeNode(request); request = NULL;
4231 if (err) {
4232 isds_log(ILF_ISDS, ILL_DEBUG,
4233 _("Processing ISDS response on ChangeISDSPassword "
4234 "request failed\n"));
4235 goto leave;
4238 /* Check for response status */
4239 err = isds_response_status(context, SERVICE_DB_ACCESS, response,
4240 &code, &message, NULL);
4241 if (err) {
4242 isds_log(ILF_ISDS, ILL_DEBUG,
4243 _("ISDS response on ChangeISDSPassword request is missing "
4244 "status\n"));
4245 goto leave;
4248 /* Request processed, but empty password refused */
4249 if (!xmlStrcmp(code, BAD_CAST "1066")) {
4250 char *code_locale = utf82locale((char*)code);
4251 char *message_locale = utf82locale((char*)message);
4252 isds_log(ILF_ISDS, ILL_DEBUG,
4253 _("Server refused empty password on ChangeISDSPassword "
4254 "request (code=%s, message=%s)\n"),
4255 code_locale, message_locale);
4256 isds_log_message(context, _("Password must not be empty"));
4257 free(code_locale);
4258 free(message_locale);
4259 err = IE_INVAL;
4260 goto leave;
4263 /* Request processed, but new password was reused */
4264 else if (!xmlStrcmp(code, BAD_CAST "1067")) {
4265 char *code_locale = utf82locale((char*)code);
4266 char *message_locale = utf82locale((char*)message);
4267 isds_log(ILF_ISDS, ILL_DEBUG,
4268 _("Server refused the same new password on ChangeISDSPassword "
4269 "request (code=%s, message=%s)\n"),
4270 code_locale, message_locale);
4271 isds_log_message(context,
4272 _("New password must differ from the current one"));
4273 free(code_locale);
4274 free(message_locale);
4275 err = IE_INVAL;
4276 goto leave;
4279 /* Other error */
4280 else if (xmlStrcmp(code, BAD_CAST "0000")) {
4281 char *code_locale = utf82locale((char*)code);
4282 char *message_locale = utf82locale((char*)message);
4283 isds_log(ILF_ISDS, ILL_DEBUG,
4284 _("Server refused to change password on ChangeISDSPassword "
4285 "request (code=%s, message=%s)\n"),
4286 code_locale, message_locale);
4287 isds_log_message(context, message_locale);
4288 free(code_locale);
4289 free(message_locale);
4290 err = IE_ISDS;
4291 goto leave;
4294 /* Otherwise password changed successfully */
4296 leave:
4297 free(code);
4298 free(message);
4299 xmlFreeDoc(response);
4300 xmlFreeNode(request);
4302 if (!err)
4303 isds_log(ILF_ISDS, ILL_DEBUG,
4304 _("Password changed successfully on ChangeISDSPassword "
4305 "request.\n"));
4307 return err;
4311 /* Generic middle part with request sending and response check.
4312 * It sends prepared request and checks for error code.
4313 * @context is ISDS session context.
4314 * @service is ISDS service handler
4315 * @service_name is name in scope of given @service
4316 * @request is XML tree with request. Will be freed to save memory.
4317 * @response is XML document ouputing ISDS response.
4318 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4319 * NULL, if you don't care. */
4320 static isds_error send_destroy_request_check_response(
4321 struct isds_ctx *context,
4322 const isds_service service, const xmlChar *service_name,
4323 xmlNodePtr *request, xmlDocPtr *response, xmlChar **refnumber) {
4324 isds_error err = IE_SUCCESS;
4325 char *service_name_locale = NULL;
4326 xmlChar *code = NULL, *message = NULL;
4329 if (!context) return IE_INVALID_CONTEXT;
4330 if (!service_name || *service_name == '\0' || !request || !*request ||
4331 !response)
4332 return IE_INVAL;
4334 /* Check if connection is established
4335 * TODO: This check should be done donwstairs. */
4336 if (!context->curl) return IE_CONNECTION_CLOSED;
4338 service_name_locale = utf82locale((char*) service_name);
4339 if (!service_name_locale) {
4340 err = IE_NOMEM;
4341 goto leave;
4344 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
4345 service_name_locale);
4347 /* Send request */
4348 err = isds(context, service, *request, response, NULL, NULL);
4349 xmlFreeNode(*request); *request = NULL;
4351 if (err) {
4352 isds_log(ILF_ISDS, ILL_DEBUG,
4353 _("Processing ISDS response on %s request failed\n"),
4354 service_name_locale);
4355 goto leave;
4358 /* Check for response status */
4359 err = isds_response_status(context, service, *response,
4360 &code, &message, refnumber);
4361 if (err) {
4362 isds_log(ILF_ISDS, ILL_DEBUG,
4363 _("ISDS response on %s request is missing status\n"),
4364 service_name_locale);
4365 goto leave;
4368 /* Request processed, but server failed */
4369 if (xmlStrcmp(code, BAD_CAST "0000")) {
4370 char *code_locale = utf82locale((char*) code);
4371 char *message_locale = utf82locale((char*) message);
4372 isds_log(ILF_ISDS, ILL_DEBUG,
4373 _("Server refused %s request (code=%s, message=%s)\n"),
4374 service_name_locale, code_locale, message_locale);
4375 isds_log_message(context, message_locale);
4376 free(code_locale);
4377 free(message_locale);
4378 err = IE_ISDS;
4379 goto leave;
4383 leave:
4384 free(code);
4385 free(message);
4386 if (err && *response) {
4387 xmlFreeDoc(*response);
4388 *response = NULL;
4390 if (*request) {
4391 xmlFreeNode(*request);
4392 *request = NULL;
4394 free(service_name_locale);
4396 return err;
4400 /* Generic bottom half with request sending.
4401 * It sends prepared request, checks for error code, destroys response and
4402 * request and log success or failure.
4403 * @context is ISDS session context.
4404 * @service is ISDS service handler
4405 * @service_name is name in scope of given @service
4406 * @request is XML tree with request. Will be freed to save memory.
4407 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4408 * NULL, if you don't care. */
4409 static isds_error send_request_check_drop_response(
4410 struct isds_ctx *context,
4411 const isds_service service, const xmlChar *service_name,
4412 xmlNodePtr *request, xmlChar **refnumber) {
4413 isds_error err = IE_SUCCESS;
4414 xmlDocPtr response = NULL;
4417 if (!context) return IE_INVALID_CONTEXT;
4418 if (!service_name || *service_name == '\0' || !request || !*request)
4419 return IE_INVAL;
4421 /* Send request and check response*/
4422 err = send_destroy_request_check_response(context,
4423 service, service_name, request, &response, refnumber);
4425 xmlFreeDoc(response);
4427 if (*request) {
4428 xmlFreeNode(*request);
4429 *request = NULL;
4432 if (!err) {
4433 char *service_name_locale = utf82locale((char *) service_name);
4434 isds_log(ILF_ISDS, ILL_DEBUG,
4435 _("%s request processed by server successfully.\n"),
4436 service_name_locale);
4437 free(service_name_locale);
4440 return err;
4444 /* Build XSD:tCreateDBInput request type for box createing.
4445 * @context is session context
4446 * @request outputs built XML tree
4447 * @service_name is request name of SERVICE_DB_MANIPULATION service
4448 * @box is box description to create including single primary user (in case of
4449 * FO box type)
4450 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
4451 * box, or contact address of PFO box owner)
4452 * @former_names is optional undocumented string. Pass NULL if you don't care.
4453 * @upper_box_id is optional ID of supper box if currently created box is
4454 * subordinated.
4455 * @ceo_label is optional title of OVM box owner (e.g. mayor) NULL, if you
4456 * don't care.
4457 * @approval is optional external approval of box manipulation */
4458 static isds_error build_CreateDBInput_request(struct isds_ctx *context,
4459 xmlNodePtr *request, const xmlChar *service_name,
4460 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
4461 const xmlChar *former_names, const xmlChar *upper_box_id,
4462 const xmlChar *ceo_label, const struct isds_approval *approval) {
4463 isds_error err = IE_SUCCESS;
4464 xmlNsPtr isds_ns = NULL;
4465 xmlNodePtr node, dbPrimaryUsers;
4466 xmlChar *string = NULL;
4467 const struct isds_list *item;
4470 if (!context) return IE_INVALID_CONTEXT;
4471 if (!request || !service_name || service_name[0] == '\0' || !box)
4472 return IE_INVAL;
4475 /* Build DeleteDataBox request */
4476 *request = xmlNewNode(NULL, service_name);
4477 if (!*request) {
4478 char *service_name_locale = utf82locale((char*) service_name);
4479 isds_printf_message(context, _("Could build %s request"),
4480 service_name_locale);
4481 free(service_name_locale);
4482 return IE_ERROR;
4484 if (context->type == CTX_TYPE_TESTING_REQUEST_COLLECTOR) {
4485 isds_ns = xmlNewNs(*request, BAD_CAST ISDS1_NS, NULL);
4486 if (!isds_ns) {
4487 isds_log_message(context, _("Could not create ISDS1 name space"));
4488 xmlFreeNode(*request);
4489 return IE_ERROR;
4491 } else {
4492 isds_ns = xmlNewNs(*request, BAD_CAST ISDS_NS, NULL);
4493 if (!isds_ns) {
4494 isds_log_message(context, _("Could not create ISDS name space"));
4495 xmlFreeNode(*request);
4496 return IE_ERROR;
4499 xmlSetNs(*request, isds_ns);
4501 INSERT_ELEMENT(node, *request, "dbOwnerInfo");
4502 err = insert_DbOwnerInfo(context, box, node);
4503 if (err) goto leave;
4505 /* Insert users */
4506 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
4507 * verbose documentatiot allows none dbUserInfo */
4508 INSERT_ELEMENT(dbPrimaryUsers, *request, "dbPrimaryUsers");
4509 for (item = users; item; item = item->next) {
4510 if (item->data) {
4511 INSERT_ELEMENT(node, dbPrimaryUsers, "dbUserInfo");
4512 err = insert_DbUserInfo(context,
4513 (struct isds_DbUserInfo *) item->data, node);
4514 if (err) goto leave;
4518 INSERT_STRING(*request, "dbFormerNames", former_names);
4519 INSERT_STRING(*request, "dbUpperDBId", upper_box_id);
4520 INSERT_STRING(*request, "dbCEOLabel", ceo_label);
4522 err = insert_GExtApproval(context, approval, *request);
4523 if (err) goto leave;
4525 leave:
4526 if (err) {
4527 xmlFreeNode(*request);
4528 *request = NULL;
4530 free(string);
4531 return err;
4535 /* Create new box.
4536 * @context is session context
4537 * @box is box description to create including single primary user (in case of
4538 * FO box type). It outputs box ID assigned by ISDS in dbID element.
4539 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
4540 * box, or contact address of PFO box owner)
4541 * @former_names is optional undocumented string. Pass NULL if you don't care.
4542 * @upper_box_id is optional ID of supper box if currently created box is
4543 * subordinated.
4544 * @ceo_label is optional title of OVM box owner (e.g. mayor)
4545 * @approval is optional external approval of box manipulation
4546 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4547 * NULL, if you don't care.*/
4548 isds_error isds_add_box(struct isds_ctx *context,
4549 struct isds_DbOwnerInfo *box, const struct isds_list *users,
4550 const char *former_names, const char *upper_box_id,
4551 const char *ceo_label, const struct isds_approval *approval,
4552 char **refnumber) {
4553 isds_error err = IE_SUCCESS;
4554 xmlNodePtr request = NULL;
4555 xmlDocPtr response = NULL;
4556 xmlXPathContextPtr xpath_ctx = NULL;
4557 xmlXPathObjectPtr result = NULL;
4560 if (!context) return IE_INVALID_CONTEXT;
4561 zfree(context->long_message);
4562 if (!box) return IE_INVAL;
4564 /* Scratch box ID */
4565 zfree(box->dbID);
4567 /* Build CreateDataBox request */
4568 err = build_CreateDBInput_request(context,
4569 &request, BAD_CAST "CreateDataBox",
4570 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
4571 (xmlChar *) ceo_label, approval);
4572 if (err) goto leave;
4574 /* Send it to server and process response */
4575 err = send_destroy_request_check_response(context,
4576 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
4577 &response, (xmlChar **) refnumber);
4579 /* Extract box ID */
4580 xpath_ctx = xmlXPathNewContext(response);
4581 if (!xpath_ctx) {
4582 err = IE_ERROR;
4583 goto leave;
4585 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4586 err = IE_ERROR;
4587 goto leave;
4589 EXTRACT_STRING("/isds:CreateDataBoxResponse/dbID", box->dbID);
4591 leave:
4592 xmlXPathFreeObject(result);
4593 xmlXPathFreeContext(xpath_ctx);
4594 xmlFreeDoc(response);
4595 xmlFreeNode(request);
4597 if (!err) {
4598 isds_log(ILF_ISDS, ILL_DEBUG,
4599 _("CreateDataBox request processed by server successfully.\n"));
4602 return err;
4606 /* Notify ISDS about new PFO entity.
4607 * This function has no real effect.
4608 * @context is session context
4609 * @box is PFO description including single primary user.
4610 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
4611 * @former_names is optional undocumented string. Pass NULL if you don't care.
4612 * @upper_box_id is optional ID of supper box if currently created box is
4613 * subordinated.
4614 * @ceo_label is optional title of OVM box owner (e.g. mayor)
4615 * @approval is optional external approval of box manipulation
4616 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4617 * NULL, if you don't care.*/
4618 isds_error isds_add_pfoinfo(struct isds_ctx *context,
4619 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
4620 const char *former_names, const char *upper_box_id,
4621 const char *ceo_label, const struct isds_approval *approval,
4622 char **refnumber) {
4623 isds_error err = IE_SUCCESS;
4624 xmlNodePtr request = NULL;
4626 if (!context) return IE_INVALID_CONTEXT;
4627 zfree(context->long_message);
4628 if (!box) return IE_INVAL;
4630 /* Build CreateDataBoxPFOInfo request */
4631 err = build_CreateDBInput_request(context,
4632 &request, BAD_CAST "CreateDataBoxPFOInfo",
4633 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
4634 (xmlChar *) ceo_label, approval);
4635 if (err) goto leave;
4637 /* Send it to server and process response */
4638 err = send_request_check_drop_response(context,
4639 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
4640 (xmlChar **) refnumber);
4641 leave:
4642 xmlFreeNode(request);
4643 return err;
4647 /* Remove given given box permanetly.
4648 * @context is session context
4649 * @box is box description to delete
4650 * @since is date of box owner cancalation. Only tm_year, tm_mon and tm_mday
4651 * carry sane value.
4652 * @approval is optional external approval of box manipulation
4653 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4654 * NULL, if you don't care.*/
4655 isds_error isds_delete_box(struct isds_ctx *context,
4656 const struct isds_DbOwnerInfo *box, const struct tm *since,
4657 const struct isds_approval *approval, char **refnumber) {
4658 isds_error err = IE_SUCCESS;
4659 xmlNsPtr isds_ns = NULL;
4660 xmlNodePtr request = NULL;
4661 xmlNodePtr node;
4662 xmlChar *string = NULL;
4665 if (!context) return IE_INVALID_CONTEXT;
4666 zfree(context->long_message);
4667 if (!box || !since) return IE_INVAL;
4670 /* Build DeleteDataBox request */
4671 request = xmlNewNode(NULL, BAD_CAST "DeleteDataBox");
4672 if (!request) {
4673 isds_log_message(context,
4674 _("Could build DeleteDataBox request"));
4675 return IE_ERROR;
4677 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4678 if(!isds_ns) {
4679 isds_log_message(context, _("Could not create ISDS name space"));
4680 xmlFreeNode(request);
4681 return IE_ERROR;
4683 xmlSetNs(request, isds_ns);
4685 INSERT_ELEMENT(node, request, "dbOwnerInfo");
4686 err = insert_DbOwnerInfo(context, box, node);
4687 if (err) goto leave;
4689 err = tm2datestring(since, &string);
4690 if (err) {
4691 isds_log_message(context,
4692 _("Could not convert `since' argument to ISO date string"));
4693 goto leave;
4695 INSERT_STRING(request, "dbOwnerTerminationDate", string);
4696 zfree(string);
4698 err = insert_GExtApproval(context, approval, request);
4699 if (err) goto leave;
4702 /* Send it to server and process response */
4703 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
4704 BAD_CAST "DeleteDataBox", &request, (xmlChar **) refnumber);
4706 leave:
4707 xmlFreeNode(request);
4708 free(string);
4709 return err;
4713 /* Update data about given box.
4714 * @context is session context
4715 * @old_box current box description
4716 * @new_box are updated data about @old_box
4717 * @approval is optional external approval of box manipulation
4718 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4719 * NULL, if you don't care.*/
4720 isds_error isds_UpdateDataBoxDescr(struct isds_ctx *context,
4721 const struct isds_DbOwnerInfo *old_box,
4722 const struct isds_DbOwnerInfo *new_box,
4723 const struct isds_approval *approval, char **refnumber) {
4724 isds_error err = IE_SUCCESS;
4725 xmlNsPtr isds_ns = NULL;
4726 xmlNodePtr request = NULL;
4727 xmlNodePtr node;
4730 if (!context) return IE_INVALID_CONTEXT;
4731 zfree(context->long_message);
4732 if (!old_box || !new_box) return IE_INVAL;
4735 /* Build UpdateDataBoxDescr request */
4736 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxDescr");
4737 if (!request) {
4738 isds_log_message(context,
4739 _("Could build UpdateDataBoxDescr request"));
4740 return IE_ERROR;
4742 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4743 if(!isds_ns) {
4744 isds_log_message(context, _("Could not create ISDS name space"));
4745 xmlFreeNode(request);
4746 return IE_ERROR;
4748 xmlSetNs(request, isds_ns);
4750 INSERT_ELEMENT(node, request, "dbOldOwnerInfo");
4751 err = insert_DbOwnerInfo(context, old_box, node);
4752 if (err) goto leave;
4754 INSERT_ELEMENT(node, request, "dbNewOwnerInfo");
4755 err = insert_DbOwnerInfo(context, new_box, node);
4756 if (err) goto leave;
4758 err = insert_GExtApproval(context, approval, request);
4759 if (err) goto leave;
4762 /* Send it to server and process response */
4763 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
4764 BAD_CAST "UpdateDataBoxDescr", &request, (xmlChar **) refnumber);
4766 leave:
4767 xmlFreeNode(request);
4769 return err;
4773 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
4774 * code
4775 * @context is session context
4776 * @service is SOAP service
4777 * @service_name is name of request in @service
4778 * @box_id is box ID of interrest
4779 * @approval is optional external approval of box manipulation
4780 * @response is server SOAP body response as XML document
4781 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4782 * NULL, if you don't care.
4783 * @return error coded from lower layer, context message will be set up
4784 * appropriately. */
4785 static isds_error build_send_dbid_request_check_response(
4786 struct isds_ctx *context, const isds_service service,
4787 const xmlChar *service_name, const xmlChar *box_id,
4788 const struct isds_approval *approval,
4789 xmlDocPtr *response, xmlChar **refnumber) {
4791 isds_error err = IE_SUCCESS;
4792 char *service_name_locale = NULL, *box_id_locale = NULL;
4793 xmlNodePtr request = NULL, node;
4794 xmlNsPtr isds_ns = NULL;
4796 if (!context) return IE_INVALID_CONTEXT;
4797 if (!service_name || !box_id) return IE_INVAL;
4798 if (!response) return IE_INVAL;
4800 /* Free output argument */
4801 xmlFreeDoc(*response); *response = NULL;
4803 /* Prepare strings */
4804 service_name_locale = utf82locale((char*)service_name);
4805 if (!service_name_locale) {
4806 err = IE_NOMEM;
4807 goto leave;
4809 box_id_locale = utf82locale((char*)box_id);
4810 if (!box_id_locale) {
4811 err = IE_NOMEM;
4812 goto leave;
4815 /* Build request */
4816 request = xmlNewNode(NULL, service_name);
4817 if (!request) {
4818 isds_printf_message(context,
4819 _("Could not build %s request"), service_name_locale);
4820 err = IE_ERROR;
4821 goto leave;
4823 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4824 if(!isds_ns) {
4825 isds_log_message(context, _("Could not create ISDS name space"));
4826 err = IE_ERROR;
4827 goto leave;
4829 xmlSetNs(request, isds_ns);
4831 /* Add XSD:tIdDbInput children */
4832 INSERT_STRING(request, "dbID", box_id);
4833 err = insert_GExtApproval(context, approval, request);
4834 if (err) goto leave;
4836 /* Send request and check response*/
4837 err = send_destroy_request_check_response(context,
4838 service, service_name, &request, response, refnumber);
4840 leave:
4841 free(service_name_locale);
4842 free(box_id_locale);
4843 xmlFreeNode(request);
4844 return err;
4848 /* Get data about all users assigned to given box.
4849 * @context is session context
4850 * @box_id is box ID
4851 * @users is automatically reallocated list of struct isds_DbUserInfo */
4852 isds_error isds_GetDataBoxUsers(struct isds_ctx *context, const char *box_id,
4853 struct isds_list **users) {
4854 isds_error err = IE_SUCCESS;
4855 xmlDocPtr response = NULL;
4856 xmlXPathContextPtr xpath_ctx = NULL;
4857 xmlXPathObjectPtr result = NULL;
4858 int i;
4859 struct isds_list *item, *prev_item = NULL;
4861 if (!context) return IE_INVALID_CONTEXT;
4862 zfree(context->long_message);
4863 if (!users || !box_id) return IE_INVAL;
4866 /* Do request and check for success */
4867 err = build_send_dbid_request_check_response(context,
4868 SERVICE_DB_MANIPULATION, BAD_CAST "GetDataBoxUsers",
4869 BAD_CAST box_id, NULL, &response, NULL);
4870 if (err) goto leave;
4873 /* Extract data */
4874 /* Prepare stucture */
4875 isds_list_free(users);
4876 xpath_ctx = xmlXPathNewContext(response);
4877 if (!xpath_ctx) {
4878 err = IE_ERROR;
4879 goto leave;
4881 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4882 err = IE_ERROR;
4883 goto leave;
4886 /* Set context node */
4887 result = xmlXPathEvalExpression(BAD_CAST
4888 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
4889 xpath_ctx);
4890 if (!result) {
4891 err = IE_ERROR;
4892 goto leave;
4894 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4895 isds_log_message(context, _("Missing dbUserInfo element"));
4896 err = IE_ISDS;
4897 goto leave;
4900 /* Iterate over all users */
4901 for (i = 0; i < result->nodesetval->nodeNr; i++) {
4903 /* Prepare structure */
4904 item = calloc(1, sizeof(*item));
4905 if (!item) {
4906 err = IE_NOMEM;
4907 goto leave;
4909 item->destructor = (void(*)(void**))isds_DbUserInfo_free;
4910 if (i == 0) *users = item;
4911 else prev_item->next = item;
4912 prev_item = item;
4914 /* Extract it */
4915 xpath_ctx->node = result->nodesetval->nodeTab[i];
4916 err = extract_DbUserInfo(context,
4917 (struct isds_DbUserInfo **) (&item->data), xpath_ctx);
4918 if (err) goto leave;
4921 leave:
4922 if (err) {
4923 isds_list_free(users);
4926 xmlXPathFreeObject(result);
4927 xmlXPathFreeContext(xpath_ctx);
4928 xmlFreeDoc(response);
4930 if (!err)
4931 isds_log(ILF_ISDS, ILL_DEBUG,
4932 _("GetDataBoxUsers request processed by server "
4933 "successfully.\n"));
4935 return err;
4939 /* Update data about user assigned to given box.
4940 * @context is session context
4941 * @box is box identification
4942 * @old_user identifies user to update
4943 * @new_user are updated data about @old_user
4944 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4945 * NULL, if you don't care.*/
4946 isds_error isds_UpdateDataBoxUser(struct isds_ctx *context,
4947 const struct isds_DbOwnerInfo *box,
4948 const struct isds_DbUserInfo *old_user,
4949 const struct isds_DbUserInfo *new_user,
4950 char **refnumber) {
4951 isds_error err = IE_SUCCESS;
4952 xmlNsPtr isds_ns = NULL;
4953 xmlNodePtr request = NULL;
4954 xmlNodePtr node;
4957 if (!context) return IE_INVALID_CONTEXT;
4958 zfree(context->long_message);
4959 if (!box || !old_user || !new_user) return IE_INVAL;
4962 /* Build UpdateDataBoxUser request */
4963 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxUser");
4964 if (!request) {
4965 isds_log_message(context,
4966 _("Could build UpdateDataBoxUser request"));
4967 return IE_ERROR;
4969 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4970 if(!isds_ns) {
4971 isds_log_message(context, _("Could not create ISDS name space"));
4972 xmlFreeNode(request);
4973 return IE_ERROR;
4975 xmlSetNs(request, isds_ns);
4977 INSERT_ELEMENT(node, request, "dbOwnerInfo");
4978 err = insert_DbOwnerInfo(context, box, node);
4979 if (err) goto leave;
4981 INSERT_ELEMENT(node, request, "dbOldUserInfo");
4982 err = insert_DbUserInfo(context, old_user, node);
4983 if (err) goto leave;
4985 INSERT_ELEMENT(node, request, "dbNewUserInfo");
4986 err = insert_DbUserInfo(context, new_user, node);
4987 if (err) goto leave;
4989 /* Send it to server and process response */
4990 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
4991 BAD_CAST "UpdateDataBoxUser", &request, (xmlChar **) refnumber);
4993 leave:
4994 xmlFreeNode(request);
4996 return err;
5000 /* Reset credentials of user assigned to given box.
5001 * @context is session context
5002 * @box is box identification
5003 * @user identifies user to reset password
5004 * @fee_paid is true if fee has been paid, false otherwise
5005 * @approval is optional external approval of box manipulation
5006 * @token is NULL if new password should be delivered off-line to the user.
5007 * It is valid pointer if user should obtain new password on-line on dedicated
5008 * web server. Then it output automatically reallocated token user needs to
5009 * use to athtorize on the web server to view his new password.
5010 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5011 * NULL, if you don't care.*/
5012 isds_error isds_reset_password(struct isds_ctx *context,
5013 const struct isds_DbOwnerInfo *box,
5014 const struct isds_DbUserInfo *user,
5015 const _Bool fee_paid, const struct isds_approval *approval,
5016 char **token, char **refnumber) {
5017 isds_error err = IE_SUCCESS;
5018 xmlNsPtr isds_ns = NULL;
5019 xmlNodePtr request = NULL, node;
5020 xmlDocPtr response = NULL;
5021 xmlXPathContextPtr xpath_ctx = NULL;
5022 xmlXPathObjectPtr result = NULL;
5025 if (!context) return IE_INVALID_CONTEXT;
5026 zfree(context->long_message);
5027 if (!box || !user) return IE_INVAL;
5029 if (token) zfree(*token);
5032 /* Build NewAccessData request */
5033 request = xmlNewNode(NULL, BAD_CAST "NewAccessData");
5034 if (!request) {
5035 isds_log_message(context,
5036 _("Could build NewAccessData request"));
5037 return IE_ERROR;
5039 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5040 if(!isds_ns) {
5041 isds_log_message(context, _("Could not create ISDS name space"));
5042 xmlFreeNode(request);
5043 return IE_ERROR;
5045 xmlSetNs(request, isds_ns);
5047 INSERT_ELEMENT(node, request, "dbOwnerInfo");
5048 err = insert_DbOwnerInfo(context, box, node);
5049 if (err) goto leave;
5051 INSERT_ELEMENT(node, request, "dbUserInfo");
5052 err = insert_DbUserInfo(context, user, node);
5053 if (err) goto leave;
5055 INSERT_SCALAR_BOOLEAN(request, "dbFeePaid", fee_paid);
5057 if (token) {
5058 INSERT_SCALAR_BOOLEAN(request, "dbVirtual", 1);
5059 } else {
5060 INSERT_SCALAR_BOOLEAN(request, "dbVirtual", 0);
5063 err = insert_GExtApproval(context, approval, request);
5064 if (err) goto leave;
5066 /* Send request and check reposne*/
5067 err = send_destroy_request_check_response(context,
5068 SERVICE_DB_MANIPULATION, BAD_CAST "NewAccessData", &request,
5069 &response, (xmlChar **) refnumber);
5070 if (err) goto leave;
5073 /* Extract optional token */
5074 if (token) {
5075 xpath_ctx = xmlXPathNewContext(response);
5076 if (!xpath_ctx) {
5077 err = IE_ERROR;
5078 goto leave;
5080 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5081 err = IE_ERROR;
5082 goto leave;
5085 EXTRACT_STRING("/isds:NewAccessDataResponse/dbAccessDataId", *token);
5088 leave:
5089 xmlXPathFreeObject(result);
5090 xmlXPathFreeContext(xpath_ctx);
5091 xmlFreeDoc(response);
5092 xmlFreeNode(request);
5094 if (!err)
5095 isds_log(ILF_ISDS, ILL_DEBUG,
5096 _("NewAccessData request processed by server "
5097 "successfully.\n"));
5099 return err;
5103 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
5104 * code, destroy response and log success.
5105 * @context is ISDS session context.
5106 * @service_name is name of SERVICE_DB_MANIPULATION service
5107 * @box is box identification
5108 * @user identifies user to removve
5109 * @approval is optional external approval of box manipulation
5110 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5111 * NULL, if you don't care. */
5112 static isds_error build_send_manipulationboxuser_request_check_drop_response(
5113 struct isds_ctx *context, const xmlChar *service_name,
5114 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
5115 const struct isds_approval *approval, xmlChar **refnumber) {
5116 isds_error err = IE_SUCCESS;
5117 xmlNsPtr isds_ns = NULL;
5118 xmlNodePtr request = NULL, node;
5121 if (!context) return IE_INVALID_CONTEXT;
5122 zfree(context->long_message);
5123 if (!service_name || service_name[0] == '\0' || !box || !user)
5124 return IE_INVAL;
5127 /* Build NewAccessData request */
5128 request = xmlNewNode(NULL, service_name);
5129 if (!request) {
5130 char *service_name_locale = utf82locale((char *) service_name);
5131 isds_printf_message(context, _("Could build %s request"),
5132 service_name_locale);
5133 free(service_name_locale);
5134 return IE_ERROR;
5136 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5137 if(!isds_ns) {
5138 isds_log_message(context, _("Could not create ISDS name space"));
5139 xmlFreeNode(request);
5140 return IE_ERROR;
5142 xmlSetNs(request, isds_ns);
5144 INSERT_ELEMENT(node, request, "dbOwnerInfo");
5145 err = insert_DbOwnerInfo(context, box, node);
5146 if (err) goto leave;
5148 INSERT_ELEMENT(node, request, "dbUserInfo");
5149 err = insert_DbUserInfo(context, user, node);
5150 if (err) goto leave;
5152 err = insert_GExtApproval(context, approval, request);
5153 if (err) goto leave;
5155 /* Send request and check reposne*/
5156 err = send_request_check_drop_response (context,
5157 SERVICE_DB_MANIPULATION, service_name, &request, refnumber);
5159 leave:
5160 xmlFreeNode(request);
5161 return err;
5165 /* Assign new user to given box.
5166 * @context is session context
5167 * @box is box identification
5168 * @user defines new user to add
5169 * @approval is optional external approval of box manipulation
5170 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5171 * NULL, if you don't care.*/
5172 isds_error isds_add_user(struct isds_ctx *context,
5173 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
5174 const struct isds_approval *approval, char **refnumber) {
5175 return build_send_manipulationboxuser_request_check_drop_response(context,
5176 BAD_CAST "AddDataBoxUser", box, user, approval,
5177 (xmlChar **) refnumber);
5181 /* Remove user assigned to given box.
5182 * @context is session context
5183 * @box is box identification
5184 * @user identifies user to removve
5185 * @approval is optional external approval of box manipulation
5186 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5187 * NULL, if you don't care.*/
5188 isds_error isds_delete_user(struct isds_ctx *context,
5189 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
5190 const struct isds_approval *approval, char **refnumber) {
5191 return build_send_manipulationboxuser_request_check_drop_response(context,
5192 BAD_CAST "DeleteDataBoxUser", box, user, approval,
5193 (xmlChar **) refnumber);
5197 /* Find boxes suiting given criteria.
5198 * @criteria is filter. You should fill in at least some memebers.
5199 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
5200 * possibly empty. Input NULL or valid old structure.
5201 * @return:
5202 * IE_SUCCESS if search sucseeded, @boxes contains usefull data
5203 * IE_NOEXIST if no such box exists, @boxes will be NULL
5204 * IE_2BIG if too much boxes exist and server truncated the resuluts, @boxes
5205 * contains still valid data
5206 * other code if something bad happens. @boxes will be NULL. */
5207 isds_error isds_FindDataBox(struct isds_ctx *context,
5208 const struct isds_DbOwnerInfo *criteria,
5209 struct isds_list **boxes) {
5210 isds_error err = IE_SUCCESS;
5211 _Bool truncated = 0;
5212 xmlNsPtr isds_ns = NULL;
5213 xmlNodePtr request = NULL;
5214 xmlDocPtr response = NULL;
5215 xmlChar *code = NULL, *message = NULL;
5216 xmlNodePtr db_owner_info;
5217 xmlXPathContextPtr xpath_ctx = NULL;
5218 xmlXPathObjectPtr result = NULL;
5219 xmlChar *string = NULL;
5222 if (!context) return IE_INVALID_CONTEXT;
5223 zfree(context->long_message);
5224 if (!boxes) return IE_INVAL;
5225 isds_list_free(boxes);
5227 if (!criteria) {
5228 return IE_INVAL;
5231 /* Check if connection is established
5232 * TODO: This check should be done donwstairs. */
5233 if (!context->curl) return IE_CONNECTION_CLOSED;
5236 /* Build FindDataBox request */
5237 request = xmlNewNode(NULL, BAD_CAST "FindDataBox");
5238 if (!request) {
5239 isds_log_message(context,
5240 _("Could build FindDataBox request"));
5241 return IE_ERROR;
5243 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5244 if(!isds_ns) {
5245 isds_log_message(context, _("Could not create ISDS name space"));
5246 xmlFreeNode(request);
5247 return IE_ERROR;
5249 xmlSetNs(request, isds_ns);
5250 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
5251 if (!db_owner_info) {
5252 isds_log_message(context, _("Could not add dbOwnerInfo child to "
5253 "FindDataBox element"));
5254 xmlFreeNode(request);
5255 return IE_ERROR;
5258 err = insert_DbOwnerInfo(context, criteria, db_owner_info);
5259 if (err) goto leave;
5262 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending FindDataBox request to ISDS\n"));
5264 /* Sent request */
5265 err = isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
5267 /* Destroy request */
5268 xmlFreeNode(request); request = NULL;
5270 if (err) {
5271 isds_log(ILF_ISDS, ILL_DEBUG,
5272 _("Processing ISDS response on FindDataBox "
5273 "request failed\n"));
5274 goto leave;
5277 /* Check for response status */
5278 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
5279 &code, &message, NULL);
5280 if (err) {
5281 isds_log(ILF_ISDS, ILL_DEBUG,
5282 _("ISDS response on FindDataBox request is missing status\n"));
5283 goto leave;
5286 /* Request processed, but nothing found */
5287 if (!xmlStrcmp(code, BAD_CAST "0002") ||
5288 !xmlStrcmp(code, BAD_CAST "5001")) {
5289 char *code_locale = utf82locale((char*)code);
5290 char *message_locale = utf82locale((char*)message);
5291 isds_log(ILF_ISDS, ILL_DEBUG,
5292 _("Server did not found any box on FindDataBox request "
5293 "(code=%s, message=%s)\n"), code_locale, message_locale);
5294 isds_log_message(context, message_locale);
5295 free(code_locale);
5296 free(message_locale);
5297 err = IE_NOEXIST;
5298 goto leave;
5301 /* Warning, not a error */
5302 if (!xmlStrcmp(code, BAD_CAST "0003")) {
5303 char *code_locale = utf82locale((char*)code);
5304 char *message_locale = utf82locale((char*)message);
5305 isds_log(ILF_ISDS, ILL_DEBUG,
5306 _("Server truncated response on FindDataBox request "
5307 "(code=%s, message=%s)\n"), code_locale, message_locale);
5308 isds_log_message(context, message_locale);
5309 free(code_locale);
5310 free(message_locale);
5311 truncated = 1;
5314 /* Other error */
5315 else if (xmlStrcmp(code, BAD_CAST "0000")) {
5316 char *code_locale = utf82locale((char*)code);
5317 char *message_locale = utf82locale((char*)message);
5318 isds_log(ILF_ISDS, ILL_DEBUG,
5319 _("Server refused FindDataBox request "
5320 "(code=%s, message=%s)\n"), code_locale, message_locale);
5321 isds_log_message(context, message_locale);
5322 free(code_locale);
5323 free(message_locale);
5324 err = IE_ISDS;
5325 goto leave;
5328 xpath_ctx = xmlXPathNewContext(response);
5329 if (!xpath_ctx) {
5330 err = IE_ERROR;
5331 goto leave;
5333 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5334 err = IE_ERROR;
5335 goto leave;
5338 /* Extract boxes if they present */
5339 result = xmlXPathEvalExpression(BAD_CAST
5340 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
5341 xpath_ctx);
5342 if (!result) {
5343 err = IE_ERROR;
5344 goto leave;
5346 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5347 struct isds_list *item, *prev_item = NULL;
5348 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
5349 item = calloc(1, sizeof(*item));
5350 if (!item) {
5351 err = IE_NOMEM;
5352 goto leave;
5355 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
5356 if (i == 0) *boxes = item;
5357 else prev_item->next = item;
5358 prev_item = item;
5360 xpath_ctx->node = result->nodesetval->nodeTab[i];
5361 err = extract_DbOwnerInfo(context,
5362 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
5363 if (err) goto leave;
5367 leave:
5368 if (err) {
5369 isds_list_free(boxes);
5370 } else {
5371 if (truncated) err = IE_2BIG;
5374 free(string);
5375 xmlFreeNode(request);
5376 xmlXPathFreeObject(result);
5377 xmlXPathFreeContext(xpath_ctx);
5379 free(code);
5380 free(message);
5381 xmlFreeDoc(response);
5383 if (!err)
5384 isds_log(ILF_ISDS, ILL_DEBUG,
5385 _("FindDataBox request processed by server successfully.\n"));
5387 return err;
5391 /* Get status of a box.
5392 * @context is ISDS session context.
5393 * @box_id is UTF-8 encoded box identifier as zero terminated string
5394 * @box_status is return value of box status.
5395 * @return:
5396 * IE_SUCCESS if box has been found and its status retrieved
5397 * IE_NOEXIST if box is not known to ISDS server
5398 * or other appropriate error.
5399 * You can use isds_DbState to enumerate box status. However out of enum
5400 * range value can be returned too. This is feature because ISDS
5401 * specification leaves the set of values open.
5402 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
5403 * the box has been deleted, but ISDS still lists its former existence. */
5404 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
5405 long int *box_status) {
5406 isds_error err = IE_SUCCESS;
5407 xmlNsPtr isds_ns = NULL;
5408 xmlNodePtr request = NULL, db_id;
5409 xmlDocPtr response = NULL;
5410 xmlChar *code = NULL, *message = NULL;
5411 xmlXPathContextPtr xpath_ctx = NULL;
5412 xmlXPathObjectPtr result = NULL;
5413 xmlChar *string = NULL;
5415 if (!context) return IE_INVALID_CONTEXT;
5416 zfree(context->long_message);
5417 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
5419 /* Check if connection is established
5420 * TODO: This check should be done donwstairs. */
5421 if (!context->curl) return IE_CONNECTION_CLOSED;
5424 /* Build CheckDataBox request */
5425 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
5426 if (!request) {
5427 isds_log_message(context,
5428 _("Could build CheckDataBox request"));
5429 return IE_ERROR;
5431 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5432 if(!isds_ns) {
5433 isds_log_message(context, _("Could not create ISDS name space"));
5434 xmlFreeNode(request);
5435 return IE_ERROR;
5437 xmlSetNs(request, isds_ns);
5438 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
5439 if (!db_id) {
5440 isds_log_message(context, _("Could not add dbID child to "
5441 "CheckDataBox element"));
5442 xmlFreeNode(request);
5443 return IE_ERROR;
5447 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CheckDataBox request to ISDS\n"));
5449 /* Sent request */
5450 err = isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
5452 /* Destroy request */
5453 xmlFreeNode(request);
5455 if (err) {
5456 isds_log(ILF_ISDS, ILL_DEBUG,
5457 _("Processing ISDS response on CheckDataBox "
5458 "request failed\n"));
5459 goto leave;
5462 /* Check for response status */
5463 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
5464 &code, &message, NULL);
5465 if (err) {
5466 isds_log(ILF_ISDS, ILL_DEBUG,
5467 _("ISDS response on CheckDataBox request is missing status\n"));
5468 goto leave;
5471 /* Request processed, but nothing found */
5472 if (!xmlStrcmp(code, BAD_CAST "5001")) {
5473 char *box_id_locale = utf82locale((char*)box_id);
5474 char *code_locale = utf82locale((char*)code);
5475 char *message_locale = utf82locale((char*)message);
5476 isds_log(ILF_ISDS, ILL_DEBUG,
5477 _("Server did not found box %s on CheckDataBox request "
5478 "(code=%s, message=%s)\n"),
5479 box_id_locale, code_locale, message_locale);
5480 isds_log_message(context, message_locale);
5481 free(box_id_locale);
5482 free(code_locale);
5483 free(message_locale);
5484 err = IE_NOEXIST;
5485 goto leave;
5488 /* Other error */
5489 else if (xmlStrcmp(code, BAD_CAST "0000")) {
5490 char *code_locale = utf82locale((char*)code);
5491 char *message_locale = utf82locale((char*)message);
5492 isds_log(ILF_ISDS, ILL_DEBUG,
5493 _("Server refused CheckDataBox request "
5494 "(code=%s, message=%s)\n"), code_locale, message_locale);
5495 isds_log_message(context, message_locale);
5496 free(code_locale);
5497 free(message_locale);
5498 err = IE_ISDS;
5499 goto leave;
5502 /* Extract data */
5503 xpath_ctx = xmlXPathNewContext(response);
5504 if (!xpath_ctx) {
5505 err = IE_ERROR;
5506 goto leave;
5508 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5509 err = IE_ERROR;
5510 goto leave;
5512 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
5513 xpath_ctx);
5514 if (!result) {
5515 err = IE_ERROR;
5516 goto leave;
5518 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5519 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
5520 err = IE_ISDS;
5521 goto leave;
5523 if (result->nodesetval->nodeNr > 1) {
5524 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
5525 err = IE_ISDS;
5526 goto leave;
5528 xpath_ctx->node = result->nodesetval->nodeTab[0];
5529 xmlXPathFreeObject(result); result = NULL;
5531 EXTRACT_LONGINT("isds:dbState", box_status, 1);
5534 leave:
5535 free(string);
5536 xmlXPathFreeObject(result);
5537 xmlXPathFreeContext(xpath_ctx);
5539 free(code);
5540 free(message);
5541 xmlFreeDoc(response);
5543 if (!err)
5544 isds_log(ILF_ISDS, ILL_DEBUG,
5545 _("CheckDataBox request processed by server successfully.\n"));
5547 return err;
5551 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
5552 * code, destroy response and log success.
5553 * @context is ISDS session context.
5554 * @service_name is name of SERVICE_DB_MANIPULATION service
5555 * @box_id is UTF-8 encoded box identifier as zero terminated string
5556 * @approval is optional external approval of box manipulation
5557 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5558 * NULL, if you don't care. */
5559 static isds_error build_send_manipulationdbid_request_check_drop_response(
5560 struct isds_ctx *context, const xmlChar *service_name,
5561 const xmlChar *box_id, const struct isds_approval *approval,
5562 xmlChar **refnumber) {
5563 isds_error err = IE_SUCCESS;
5564 xmlDocPtr response = NULL;
5566 if (!context) return IE_INVALID_CONTEXT;
5567 zfree(context->long_message);
5568 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
5570 /* Check if connection is established */
5571 if (!context->curl) return IE_CONNECTION_CLOSED;
5573 /* Do request and check for success */
5574 err = build_send_dbid_request_check_response(context,
5575 SERVICE_DB_MANIPULATION, service_name, box_id, approval,
5576 &response, refnumber);
5577 xmlFreeDoc(response);
5579 if (!err) {
5580 char *service_name_locale = utf82locale((char *) service_name);
5581 isds_log(ILF_ISDS, ILL_DEBUG,
5582 _("%s request processed by server successfully.\n"),
5583 service_name_locale);
5584 free(service_name_locale);
5587 return err;
5591 /* Switch box into state where box can receive commercial messages (off by
5592 * default)
5593 * @context is ISDS session context.
5594 * @box_id is UTF-8 encoded box identifier as zero terminated string
5595 * @allow is true for enable, false for disable commercial messages income
5596 * @approval is optional external approval of box manipulation
5597 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5598 * NULL, if you don't care. */
5599 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
5600 const char *box_id, const _Bool allow,
5601 const struct isds_approval *approval, char **refnumber) {
5602 return build_send_manipulationdbid_request_check_drop_response(context,
5603 (allow) ? BAD_CAST "SetOpenAddressing" :
5604 BAD_CAST "ClearOpenAddressing",
5605 BAD_CAST box_id, approval, (xmlChar **) refnumber);
5609 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
5610 * message acceptance). This is just a box permission. Sender must apply
5611 * such role by sending each message.
5612 * @context is ISDS session context.
5613 * @box_id is UTF-8 encoded box identifier as zero terminated string
5614 * @allow is true for enable, false for disable OVM role permission
5615 * @approval is optional external approval of box manipulation
5616 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5617 * NULL, if you don't care. */
5618 isds_error isds_switch_effective_ovm(struct isds_ctx *context,
5619 const char *box_id, const _Bool allow,
5620 const struct isds_approval *approval, char **refnumber) {
5621 return build_send_manipulationdbid_request_check_drop_response(context,
5622 (allow) ? BAD_CAST "SetEffectiveOVM" :
5623 BAD_CAST "ClearEffectiveOVM",
5624 BAD_CAST box_id, approval, (xmlChar **) refnumber);
5628 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
5629 * code, destroy response and log success.
5630 * @context is ISDS session context.
5631 * @service_name is name of SERVICE_DB_MANIPULATION service
5632 * @owner is structure describing box
5633 * @approval is optional external approval of box manipulation
5634 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5635 * NULL, if you don't care. */
5636 static isds_error build_send_manipulationdbowner_request_check_drop_response(
5637 struct isds_ctx *context, const xmlChar *service_name,
5638 const struct isds_DbOwnerInfo *owner,
5639 const struct isds_approval *approval, xmlChar **refnumber) {
5640 isds_error err = IE_SUCCESS;
5641 char *service_name_locale = NULL;
5642 xmlNodePtr request = NULL, db_owner_info;
5643 xmlNsPtr isds_ns = NULL;
5646 if (!context) return IE_INVALID_CONTEXT;
5647 zfree(context->long_message);
5648 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
5650 service_name_locale = utf82locale((char*)service_name);
5651 if (!service_name_locale) {
5652 err = IE_NOMEM;
5653 goto leave;
5656 /* Build request */
5657 request = xmlNewNode(NULL, service_name);
5658 if (!request) {
5659 isds_printf_message(context,
5660 _("Could not build %s request"), service_name_locale);
5661 err = IE_ERROR;
5662 goto leave;
5664 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5665 if(!isds_ns) {
5666 isds_log_message(context, _("Could not create ISDS name space"));
5667 err = IE_ERROR;
5668 goto leave;
5670 xmlSetNs(request, isds_ns);
5673 /* Add XSD:tOwnerInfoInput child*/
5674 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
5675 err = insert_DbOwnerInfo(context, owner, db_owner_info);
5676 if (err) goto leave;
5678 /* Add XSD:gExtApproval*/
5679 err = insert_GExtApproval(context, approval, request);
5680 if (err) goto leave;
5682 /* Send it to server and process response */
5683 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
5684 service_name, &request, refnumber);
5686 leave:
5687 xmlFreeNode(request);
5688 free(service_name_locale);
5690 return err;
5694 /* Switch box accessibility state on request of box owner.
5695 * Despite the name, owner must do the request off-line. This function is
5696 * designed for such off-line meeting points (e.g. Czech POINT).
5697 * @context is ISDS session context.
5698 * @box identifies box to swith accesibilty state.
5699 * @allow is true for making accesibale, false to disallow access.
5700 * @approval is optional external approval of box manipulation
5701 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5702 * NULL, if you don't care. */
5703 isds_error isds_switch_box_accessibility_on_owner_request(
5704 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
5705 const _Bool allow, const struct isds_approval *approval,
5706 char **refnumber) {
5707 return build_send_manipulationdbowner_request_check_drop_response(context,
5708 (allow) ? BAD_CAST "EnableOwnDataBox" :
5709 BAD_CAST "DisableOwnDataBox",
5710 box, approval, (xmlChar **) refnumber);
5714 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
5715 * date.
5716 * @context is ISDS session context.
5717 * @box identifies box to swith accesibilty state.
5718 * @since is date since accesseibility has been denied. This can be past too.
5719 * Only tm_year, tm_mon and tm_mday carry sane value.
5720 * @approval is optional external approval of box manipulation
5721 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5722 * NULL, if you don't care. */
5723 isds_error isds_disable_box_accessibility_externaly(
5724 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
5725 const struct tm *since, const struct isds_approval *approval,
5726 char **refnumber) {
5727 isds_error err = IE_SUCCESS;
5728 char *service_name_locale = NULL;
5729 xmlNodePtr request = NULL, node;
5730 xmlNsPtr isds_ns = NULL;
5731 xmlChar *string = NULL;
5734 if (!context) return IE_INVALID_CONTEXT;
5735 zfree(context->long_message);
5736 if (!box || !since) return IE_INVAL;
5738 /* Build request */
5739 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
5740 if (!request) {
5741 isds_printf_message(context,
5742 _("Could not build %s request"), "DisableDataBoxExternally");
5743 err = IE_ERROR;
5744 goto leave;
5746 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5747 if(!isds_ns) {
5748 isds_log_message(context, _("Could not create ISDS name space"));
5749 err = IE_ERROR;
5750 goto leave;
5752 xmlSetNs(request, isds_ns);
5755 /* Add @box identification */
5756 INSERT_ELEMENT(node, request, "dbOwnerInfo");
5757 err = insert_DbOwnerInfo(context, box, node);
5758 if (err) goto leave;
5760 /* Add @since date */
5761 err = tm2datestring(since, &string);
5762 if(err) {
5763 isds_log_message(context,
5764 _("Could not convert `since' argument to ISO date string"));
5765 goto leave;
5767 INSERT_STRING(request, "dbOwnerDisableDate", string);
5768 zfree(string);
5770 /* Add @approval */
5771 err = insert_GExtApproval(context, approval, request);
5772 if (err) goto leave;
5774 /* Send it to server and process response */
5775 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
5776 BAD_CAST "DisableDataBoxExternally", &request,
5777 (xmlChar **) refnumber);
5779 leave:
5780 free(string);
5781 xmlFreeNode(request);
5782 free(service_name_locale);
5784 return err;
5788 /* Insert struct isds_message data (envelope (recipient data optional) and
5789 * documents) into XML tree
5790 * @context is sesstion context
5791 * @outgoing_message is libsids structure with message data
5792 * @create_message is XML CreateMessage or CreateMultipleMessage element
5793 * @process_recipient true for recipient data serialization, false for no
5794 * serialization */
5795 static isds_error insert_envelope_files(struct isds_ctx *context,
5796 const struct isds_message *outgoing_message, xmlNodePtr create_message,
5797 const _Bool process_recipient) {
5799 isds_error err = IE_SUCCESS;
5800 xmlNodePtr envelope, dm_files, node;
5801 xmlChar *string = NULL;
5803 if (!context) return IE_INVALID_CONTEXT;
5804 if (!outgoing_message || !create_message) return IE_INVAL;
5807 /* Build envelope */
5808 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
5809 if (!envelope) {
5810 isds_printf_message(context, _("Could not add dmEnvelope child to "
5811 "%s element"), create_message->name);
5812 return IE_ERROR;
5815 if (!outgoing_message->envelope) {
5816 isds_log_message(context, _("Outgoing message is missing envelope"));
5817 err = IE_INVAL;
5818 goto leave;
5821 INSERT_STRING(envelope, "dmSenderOrgUnit",
5822 outgoing_message->envelope->dmSenderOrgUnit);
5823 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
5824 outgoing_message->envelope->dmSenderOrgUnitNum, string);
5826 if (process_recipient) {
5827 if (!outgoing_message->envelope->dbIDRecipient) {
5828 isds_log_message(context,
5829 _("Outgoing message is missing recipient box identifier"));
5830 err = IE_INVAL;
5831 goto leave;
5833 INSERT_STRING(envelope, "dbIDRecipient",
5834 outgoing_message->envelope->dbIDRecipient);
5836 INSERT_STRING(envelope, "dmRecipientOrgUnit",
5837 outgoing_message->envelope->dmRecipientOrgUnit);
5838 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
5839 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
5840 INSERT_STRING(envelope, "dmToHands",
5841 outgoing_message->envelope->dmToHands);
5844 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
5845 "dmAnnotation");
5846 INSERT_STRING(envelope, "dmAnnotation",
5847 outgoing_message->envelope->dmAnnotation);
5849 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
5850 0, 50, "dmRecipientRefNumber");
5851 INSERT_STRING(envelope, "dmRecipientRefNumber",
5852 outgoing_message->envelope->dmRecipientRefNumber);
5854 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
5855 0, 50, "dmSenderRefNumber");
5856 INSERT_STRING(envelope, "dmSenderRefNumber",
5857 outgoing_message->envelope->dmSenderRefNumber);
5859 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
5860 0, 50, "dmRecipientIdent");
5861 INSERT_STRING(envelope, "dmRecipientIdent",
5862 outgoing_message->envelope->dmRecipientIdent);
5864 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
5865 0, 50, "dmSenderIdent");
5866 INSERT_STRING(envelope, "dmSenderIdent",
5867 outgoing_message->envelope->dmSenderIdent);
5869 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
5870 outgoing_message->envelope->dmLegalTitleLaw, string);
5871 INSERT_LONGINT(envelope, "dmLegalTitleYear",
5872 outgoing_message->envelope->dmLegalTitleYear, string);
5873 INSERT_STRING(envelope, "dmLegalTitleSect",
5874 outgoing_message->envelope->dmLegalTitleSect);
5875 INSERT_STRING(envelope, "dmLegalTitlePar",
5876 outgoing_message->envelope->dmLegalTitlePar);
5877 INSERT_STRING(envelope, "dmLegalTitlePoint",
5878 outgoing_message->envelope->dmLegalTitlePoint);
5880 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
5881 outgoing_message->envelope->dmPersonalDelivery);
5882 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
5883 outgoing_message->envelope->dmAllowSubstDelivery);
5885 /* ???: Should we require value for dbEffectiveOVM sender?
5886 * ISDS has default as true */
5887 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
5890 /* Append dmFiles */
5891 if (!outgoing_message->documents) {
5892 isds_log_message(context,
5893 _("Outgoing message is missing list of documents"));
5894 err = IE_INVAL;
5895 goto leave;
5897 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
5898 if (!dm_files) {
5899 isds_printf_message(context, _("Could not add dmFiles child to "
5900 "%s element"), create_message->name);
5901 err = IE_ERROR;
5902 goto leave;
5905 /* Check for document hieararchy */
5906 err = check_documents_hierarchy(context, outgoing_message->documents);
5907 if (err) goto leave;
5909 /* Process each document */
5910 for (struct isds_list *item =
5911 (struct isds_list *) outgoing_message->documents;
5912 item; item = item->next) {
5913 if (!item->data) {
5914 isds_log_message(context,
5915 _("List of documents contains empty item"));
5916 err = IE_INVAL;
5917 goto leave;
5919 /* FIXME: Check for dmFileMetaType and for document references.
5920 * Only first document can be of MAIN type */
5921 err = insert_document(context, (struct isds_document*) item->data,
5922 dm_files);
5924 if (err) goto leave;
5927 leave:
5928 free(string);
5929 return err;
5933 /* Send a message via ISDS to a recipent
5934 * @context is session context
5935 * @outgoing_message is message to send; Some memebers are mandatory (like
5936 * dbIDRecipient), some are optional and some are irrelevant (especialy data
5937 * about sender). Included pointer to isds_list documents must contain at
5938 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
5939 * members will be filled with valid data from ISDS. Exact list of write
5940 * members is subject to change. Currently dmId is changed.
5941 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
5942 isds_error isds_send_message(struct isds_ctx *context,
5943 struct isds_message *outgoing_message) {
5945 isds_error err = IE_SUCCESS;
5946 xmlNsPtr isds_ns = NULL;
5947 xmlNodePtr request = NULL;
5948 xmlDocPtr response = NULL;
5949 xmlChar *code = NULL, *message = NULL;
5950 xmlXPathContextPtr xpath_ctx = NULL;
5951 xmlXPathObjectPtr result = NULL;
5952 _Bool message_is_complete = 0;
5954 if (!context) return IE_INVALID_CONTEXT;
5955 zfree(context->long_message);
5956 if (!outgoing_message) return IE_INVAL;
5958 /* Check if connection is established
5959 * TODO: This check should be done donwstairs. */
5960 if (!context->curl) return IE_CONNECTION_CLOSED;
5963 /* Build CreateMessage request */
5964 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
5965 if (!request) {
5966 isds_log_message(context,
5967 _("Could build CreateMessage request"));
5968 return IE_ERROR;
5970 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5971 if(!isds_ns) {
5972 isds_log_message(context, _("Could not create ISDS name space"));
5973 xmlFreeNode(request);
5974 return IE_ERROR;
5976 xmlSetNs(request, isds_ns);
5978 /* Append envelope and files */
5979 err = insert_envelope_files(context, outgoing_message, request, 1);
5980 if (err) goto leave;
5983 /* Signal we can serilize message since now */
5984 message_is_complete = 1;
5987 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
5989 /* Sent request */
5990 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
5992 /* Dont' destroy request, we want to provide it to application later */
5994 if (err) {
5995 isds_log(ILF_ISDS, ILL_DEBUG,
5996 _("Processing ISDS response on CreateMessage "
5997 "request failed\n"));
5998 goto leave;
6001 /* Check for response status */
6002 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
6003 &code, &message, NULL);
6004 if (err) {
6005 isds_log(ILF_ISDS, ILL_DEBUG,
6006 _("ISDS response on CreateMessage request "
6007 "is missing status\n"));
6008 goto leave;
6011 /* Request processed, but refused by server or server failed */
6012 if (xmlStrcmp(code, BAD_CAST "0000")) {
6013 char *box_id_locale =
6014 utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
6015 char *code_locale = utf82locale((char*)code);
6016 char *message_locale = utf82locale((char*)message);
6017 isds_log(ILF_ISDS, ILL_DEBUG,
6018 _("Server did not accept message for %s on CreateMessage "
6019 "request (code=%s, message=%s)\n"),
6020 box_id_locale, code_locale, message_locale);
6021 isds_log_message(context, message_locale);
6022 free(box_id_locale);
6023 free(code_locale);
6024 free(message_locale);
6025 err = IE_ISDS;
6026 goto leave;
6030 /* Extract data */
6031 xpath_ctx = xmlXPathNewContext(response);
6032 if (!xpath_ctx) {
6033 err = IE_ERROR;
6034 goto leave;
6036 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6037 err = IE_ERROR;
6038 goto leave;
6040 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
6041 xpath_ctx);
6042 if (!result) {
6043 err = IE_ERROR;
6044 goto leave;
6046 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6047 isds_log_message(context, _("Missing CreateMessageResponse element"));
6048 err = IE_ISDS;
6049 goto leave;
6051 if (result->nodesetval->nodeNr > 1) {
6052 isds_log_message(context, _("Multiple CreateMessageResponse element"));
6053 err = IE_ISDS;
6054 goto leave;
6056 xpath_ctx->node = result->nodesetval->nodeTab[0];
6057 xmlXPathFreeObject(result); result = NULL;
6059 if (outgoing_message->envelope->dmID) {
6060 free(outgoing_message->envelope->dmID);
6061 outgoing_message->envelope->dmID = NULL;
6063 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
6064 if (!outgoing_message->envelope->dmID) {
6065 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
6066 "but did not return assigned message ID\n"));
6069 leave:
6070 /* TODO: Serialize message into structure member raw */
6071 /* XXX: Each web service transport message in different format.
6072 * Therefore it's not possible to save them directly.
6073 * To save them, one must figure out common format.
6074 * We can leave it on application, or we can implement the ESS format. */
6075 /*if (message_is_complete) {
6076 if (outgoing_message->envelope->dmID) {
6078 /* Add assigned message ID as first child*/
6079 /*xmlNodePtr dmid_text = xmlNewText(
6080 (xmlChar *) outgoing_message->envelope->dmID);
6081 if (!dmid_text) goto serialization_failed;
6083 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
6084 BAD_CAST "dmID");
6085 if (!dmid_element) {
6086 xmlFreeNode(dmid_text);
6087 goto serialization_failed;
6090 xmlNodePtr dmid_element_with_text =
6091 xmlAddChild(dmid_element, dmid_text);
6092 if (!dmid_element_with_text) {
6093 xmlFreeNode(dmid_element);
6094 xmlFreeNode(dmid_text);
6095 goto serialization_failed;
6098 node = xmlAddPrevSibling(envelope->childern,
6099 dmid_element_with_text);
6100 if (!node) {
6101 xmlFreeNodeList(dmid_element_with_text);
6102 goto serialization_failed;
6106 /* Serialize message with ID into raw */
6107 /*buffer = serialize_element(envelope)*/
6108 /* }
6110 serialization_failed:
6114 /* Clean up */
6115 xmlXPathFreeObject(result);
6116 xmlXPathFreeContext(xpath_ctx);
6118 free(code);
6119 free(message);
6120 xmlFreeDoc(response);
6121 xmlFreeNode(request);
6123 if (!err)
6124 isds_log(ILF_ISDS, ILL_DEBUG,
6125 _("CreateMessage request processed by server "
6126 "successfully.\n"));
6128 return err;
6132 /* Send a message via ISDS to a multiple recipents
6133 * @context is session context
6134 * @outgoing_message is message to send; Some memebers are mandatory,
6135 * some are optional and some are irrelevant (especialy data
6136 * about sender). Data about recipient will be substituted by ISDS from
6137 * @copies. Included pointer to isds_list documents must
6138 * contain at least one document of FILEMETATYPE_MAIN.
6139 * @copies is list of isds_message_copy structures addressing all desired
6140 * recipients. This is read-write structure, some members will be filled with
6141 * valid data from ISDS (message IDs, error codes, error descriptions).
6142 * @return
6143 * ISDS_SUCCESS if all messages have been sent
6144 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
6145 * succesed messages can be identified by copies->data->error),
6146 * or other error code if something other goes wrong. */
6147 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
6148 const struct isds_message *outgoing_message,
6149 struct isds_list *copies) {
6151 isds_error err = IE_SUCCESS, append_err;
6152 xmlNsPtr isds_ns = NULL;
6153 xmlNodePtr request = NULL, recipients, recipient, node;
6154 struct isds_list *item;
6155 struct isds_message_copy *copy;
6156 xmlDocPtr response = NULL;
6157 xmlChar *code = NULL, *message = NULL;
6158 xmlXPathContextPtr xpath_ctx = NULL;
6159 xmlXPathObjectPtr result = NULL;
6160 xmlChar *string = NULL;
6161 int i;
6163 if (!context) return IE_INVALID_CONTEXT;
6164 zfree(context->long_message);
6165 if (!outgoing_message || !copies) return IE_INVAL;
6167 /* Check if connection is established
6168 * TODO: This check should be done donwstairs. */
6169 if (!context->curl) return IE_CONNECTION_CLOSED;
6172 /* Build CreateMultipleMessage request */
6173 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
6174 if (!request) {
6175 isds_log_message(context,
6176 _("Could not build CreateMultipleMessage request"));
6177 return IE_ERROR;
6179 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6180 if(!isds_ns) {
6181 isds_log_message(context, _("Could not create ISDS name space"));
6182 xmlFreeNode(request);
6183 return IE_ERROR;
6185 xmlSetNs(request, isds_ns);
6188 /* Build recipients */
6189 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
6190 if (!recipients) {
6191 isds_log_message(context, _("Could not add dmRecipients child to "
6192 "CreateMultipleMessage element"));
6193 xmlFreeNode(request);
6194 return IE_ERROR;
6197 /* Insert each recipient */
6198 for (item = copies; item; item = item->next) {
6199 copy = (struct isds_message_copy *) item->data;
6200 if (!copy) {
6201 isds_log_message(context,
6202 _("`copies' list item contains empty data"));
6203 err = IE_INVAL;
6204 goto leave;
6207 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
6208 if (!recipient) {
6209 isds_log_message(context, _("Could not add dmRecipient child to "
6210 "dmRecipients element"));
6211 err = IE_ERROR;
6212 goto leave;
6215 if (!copy->dbIDRecipient) {
6216 isds_log_message(context,
6217 _("Message copy is missing recipient box identifier"));
6218 err = IE_INVAL;
6219 goto leave;
6221 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
6222 INSERT_STRING(recipient, "dmRecipientOrgUnit",
6223 copy->dmRecipientOrgUnit);
6224 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
6225 copy->dmRecipientOrgUnitNum, string);
6226 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
6229 /* Append envelope and files */
6230 err = insert_envelope_files(context, outgoing_message, request, 0);
6231 if (err) goto leave;
6234 isds_log(ILF_ISDS, ILL_DEBUG,
6235 _("Sending CreateMultipleMessage request to ISDS\n"));
6237 /* Sent request */
6238 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
6239 if (err) {
6240 isds_log(ILF_ISDS, ILL_DEBUG,
6241 _("Processing ISDS response on CreateMultipleMessage "
6242 "request failed\n"));
6243 goto leave;
6246 /* Check for response status */
6247 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
6248 &code, &message, NULL);
6249 if (err) {
6250 isds_log(ILF_ISDS, ILL_DEBUG,
6251 _("ISDS response on CreateMultipleMessage request "
6252 "is missing status\n"));
6253 goto leave;
6256 /* Request processed, but some copies failed */
6257 if (!xmlStrcmp(code, BAD_CAST "0004")) {
6258 char *box_id_locale =
6259 utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
6260 char *code_locale = utf82locale((char*)code);
6261 char *message_locale = utf82locale((char*)message);
6262 isds_log(ILF_ISDS, ILL_DEBUG,
6263 _("Server did accept message for multiple recipients "
6264 "on CreateMultipleMessage request but delivery to "
6265 "some of them failed (code=%s, message=%s)\n"),
6266 box_id_locale, code_locale, message_locale);
6267 isds_log_message(context, message_locale);
6268 free(box_id_locale);
6269 free(code_locale);
6270 free(message_locale);
6271 err = IE_PARTIAL_SUCCESS;
6274 /* Request refused by server as whole */
6275 else if (xmlStrcmp(code, BAD_CAST "0000")) {
6276 char *box_id_locale =
6277 utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
6278 char *code_locale = utf82locale((char*)code);
6279 char *message_locale = utf82locale((char*)message);
6280 isds_log(ILF_ISDS, ILL_DEBUG,
6281 _("Server did not accept message for multiple recipients "
6282 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
6283 box_id_locale, code_locale, message_locale);
6284 isds_log_message(context, message_locale);
6285 free(box_id_locale);
6286 free(code_locale);
6287 free(message_locale);
6288 err = IE_ISDS;
6289 goto leave;
6293 /* Extract data */
6294 xpath_ctx = xmlXPathNewContext(response);
6295 if (!xpath_ctx) {
6296 err = IE_ERROR;
6297 goto leave;
6299 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6300 err = IE_ERROR;
6301 goto leave;
6303 result = xmlXPathEvalExpression(
6304 BAD_CAST "/isds:CreateMultipleMessageResponse"
6305 "/isds:dmMultipleStatus/isds:dmSingleStatus",
6306 xpath_ctx);
6307 if (!result) {
6308 err = IE_ERROR;
6309 goto leave;
6311 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6312 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
6313 err = IE_ISDS;
6314 goto leave;
6317 /* Extract message ID and delivery status for each copy */
6318 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
6319 item = item->next, i++) {
6320 copy = (struct isds_message_copy *) item->data;
6321 xpath_ctx->node = result->nodesetval->nodeTab[i];
6323 append_err = append_TMStatus(context, copy, xpath_ctx);
6324 if (append_err) {
6325 err = append_err;
6326 goto leave;
6329 if (item || i < result->nodesetval->nodeNr) {
6330 isds_printf_message(context, _("ISDS returned unexpected number of "
6331 "message copy delivery states: %d"),
6332 result->nodesetval->nodeNr);
6333 err = IE_ISDS;
6334 goto leave;
6338 leave:
6339 /* Clean up */
6340 free(string);
6341 xmlXPathFreeObject(result);
6342 xmlXPathFreeContext(xpath_ctx);
6344 free(code);
6345 free(message);
6346 xmlFreeDoc(response);
6347 xmlFreeNode(request);
6349 if (!err)
6350 isds_log(ILF_ISDS, ILL_DEBUG,
6351 _("CreateMultipleMessageResponse request processed by server "
6352 "successfully.\n"));
6354 return err;
6358 /* Get list of messages. This is common core for getting sent or received
6359 * messaeges.
6360 * Any criterion argument can be NULL, if you don't care about it.
6361 * @context is session context. Must not be NULL.
6362 * @outgoing_direction is true if you want list of outgoing messages,
6363 * it's false if you want incoming messages.
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 * @organization_unit_number is number of sender/recipient respectively.
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 * The list is sorted by delivery time in ascending order.
6379 * Use NULL if
6380 * you don't care about don't need the data (useful if you want to know only
6381 * the @number). If you provide &NULL, list will be allocated on heap, if you
6382 * provide pointer to non-NULL, list will be freed automacally at first. Also
6383 * in case of error the list will be NULLed.
6384 * @return IE_SUCCESS or appropriate error code. */
6385 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
6386 _Bool outgoing_direction,
6387 const struct timeval *from_time, const struct timeval *to_time,
6388 const long int *organization_unit_number,
6389 const unsigned int status_filter,
6390 const unsigned long int offset, unsigned long int *number,
6391 struct isds_list **messages) {
6393 isds_error err = IE_SUCCESS;
6394 xmlNsPtr isds_ns = NULL;
6395 xmlNodePtr request = NULL, node;
6396 xmlDocPtr response = NULL;
6397 xmlChar *code = NULL, *message = NULL;
6398 xmlXPathContextPtr xpath_ctx = NULL;
6399 xmlXPathObjectPtr result = NULL;
6400 xmlChar *string = NULL;
6401 long unsigned int count = 0;
6403 if (!context) return IE_INVALID_CONTEXT;
6404 zfree(context->long_message);
6406 /* Free former message list if any */
6407 if (messages) isds_list_free(messages);
6409 /* Check if connection is established
6410 * TODO: This check should be done donwstairs. */
6411 if (!context->curl) return IE_CONNECTION_CLOSED;
6413 /* Build GetListOf*Messages request */
6414 request = xmlNewNode(NULL,
6415 (outgoing_direction) ?
6416 BAD_CAST "GetListOfSentMessages" :
6417 BAD_CAST "GetListOfReceivedMessages"
6419 if (!request) {
6420 isds_log_message(context,
6421 (outgoing_direction) ?
6422 _("Could not build GetListOfSentMessages request") :
6423 _("Could not build GetListOfReceivedMessages request")
6425 return IE_ERROR;
6427 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6428 if(!isds_ns) {
6429 isds_log_message(context, _("Could not create ISDS name space"));
6430 xmlFreeNode(request);
6431 return IE_ERROR;
6433 xmlSetNs(request, isds_ns);
6436 if (from_time) {
6437 err = timeval2timestring(from_time, &string);
6438 if (err) goto leave;
6440 INSERT_STRING(request, "dmFromTime", string);
6441 free(string); string = NULL;
6443 if (to_time) {
6444 err = timeval2timestring(to_time, &string);
6445 if (err) goto leave;
6447 INSERT_STRING(request, "dmToTime", string);
6448 free(string); string = NULL;
6450 if (outgoing_direction) {
6451 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
6452 organization_unit_number, string);
6453 } else {
6454 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
6455 organization_unit_number, string);
6458 if (status_filter > MESSAGESTATE_ANY) {
6459 isds_printf_message(context,
6460 _("Invalid message state filter value: %ld"), status_filter);
6461 err = IE_INVAL;
6462 goto leave;
6464 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
6466 if (offset > 0 ) {
6467 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
6468 } else {
6469 INSERT_STRING(request, "dmOffset", "1");
6472 /* number 0 means no limit */
6473 if (number && *number == 0) {
6474 INSERT_STRING(request, "dmLimit", NULL);
6475 } else {
6476 INSERT_ULONGINT(request, "dmLimit", number, string);
6480 isds_log(ILF_ISDS, ILL_DEBUG,
6481 (outgoing_direction) ?
6482 _("Sending GetListOfSentMessages request to ISDS\n") :
6483 _("Sending GetListOfReceivedMessages request to ISDS\n")
6486 /* Sent request */
6487 err = isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
6488 xmlFreeNode(request); request = NULL;
6490 if (err) {
6491 isds_log(ILF_ISDS, ILL_DEBUG,
6492 (outgoing_direction) ?
6493 _("Processing ISDS response on GetListOfSentMessages "
6494 "request failed\n") :
6495 _("Processing ISDS response on GetListOfReceivedMessages "
6496 "request failed\n")
6498 goto leave;
6501 /* Check for response status */
6502 err = isds_response_status(context, SERVICE_DM_INFO, response,
6503 &code, &message, NULL);
6504 if (err) {
6505 isds_log(ILF_ISDS, ILL_DEBUG,
6506 (outgoing_direction) ?
6507 _("ISDS response on GetListOfSentMessages request "
6508 "is missing status\n") :
6509 _("ISDS response on GetListOfReceivedMessages request "
6510 "is missing status\n")
6512 goto leave;
6515 /* Request processed, but nothing found */
6516 if (xmlStrcmp(code, BAD_CAST "0000")) {
6517 char *code_locale = utf82locale((char*)code);
6518 char *message_locale = utf82locale((char*)message);
6519 isds_log(ILF_ISDS, ILL_DEBUG,
6520 (outgoing_direction) ?
6521 _("Server refused GetListOfSentMessages request "
6522 "(code=%s, message=%s)\n") :
6523 _("Server refused GetListOfReceivedMessages request "
6524 "(code=%s, message=%s)\n"),
6525 code_locale, message_locale);
6526 isds_log_message(context, message_locale);
6527 free(code_locale);
6528 free(message_locale);
6529 err = IE_ISDS;
6530 goto leave;
6534 /* Extract data */
6535 xpath_ctx = xmlXPathNewContext(response);
6536 if (!xpath_ctx) {
6537 err = IE_ERROR;
6538 goto leave;
6540 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6541 err = IE_ERROR;
6542 goto leave;
6544 result = xmlXPathEvalExpression(
6545 (outgoing_direction) ?
6546 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
6547 "isds:dmRecords/isds:dmRecord" :
6548 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
6549 "isds:dmRecords/isds:dmRecord",
6550 xpath_ctx);
6551 if (!result) {
6552 err = IE_ERROR;
6553 goto leave;
6556 /* Fill output arguments in */
6557 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6558 struct isds_envelope *envelope;
6559 struct isds_list *item = NULL, *last_item = NULL;
6561 for (count = 0; count < result->nodesetval->nodeNr; count++) {
6562 /* Create new message */
6563 item = calloc(1, sizeof(*item));
6564 if (!item) {
6565 err = IE_NOMEM;
6566 goto leave;
6568 item->destructor = (void(*)(void**)) &isds_message_free;
6569 item->data = calloc(1, sizeof(struct isds_message));
6570 if (!item->data) {
6571 isds_list_free(&item);
6572 err = IE_NOMEM;
6573 goto leave;
6576 /* Extract envelope data */
6577 xpath_ctx->node = result->nodesetval->nodeTab[count];
6578 envelope = NULL;
6579 err = extract_DmRecord(context, &envelope, xpath_ctx);
6580 if (err) {
6581 isds_list_free(&item);
6582 goto leave;
6585 /* Attach extracted envelope */
6586 ((struct isds_message *) item->data)->envelope = envelope;
6588 /* Append new message into the list */
6589 if (!*messages) {
6590 *messages = last_item = item;
6591 } else {
6592 last_item->next = item;
6593 last_item = item;
6597 if (number) *number = count;
6599 leave:
6600 if (err) {
6601 isds_list_free(messages);
6604 free(string);
6605 xmlXPathFreeObject(result);
6606 xmlXPathFreeContext(xpath_ctx);
6608 free(code);
6609 free(message);
6610 xmlFreeDoc(response);
6611 xmlFreeNode(request);
6613 if (!err)
6614 isds_log(ILF_ISDS, ILL_DEBUG,
6615 (outgoing_direction) ?
6616 _("GetListOfSentMessages request processed by server "
6617 "successfully.\n") :
6618 _("GetListOfReceivedMessages request processed by server "
6619 "successfully.\n")
6621 return err;
6625 /* Get list of outgoing (already sent) messages.
6626 * Any criterion argument can be NULL, if you don't care about it.
6627 * @context is session context. Must not be NULL.
6628 * @from_time is minimal time and date of message sending inclusive.
6629 * @to_time is maximal time and date of message sending inclusive
6630 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
6631 * @status_filter is bit field of isds_message_status values. Use special
6632 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
6633 * all values, you can use bitwise arithmetic if you want.)
6634 * @offset is index of first message we are interested in. First message is 1.
6635 * Set to 0 (or 1) if you don't care.
6636 * @number is maximal length of list you want to get as input value, outputs
6637 * number of messages matching these criteria. Can be NULL if you don't care
6638 * (applies to output value either).
6639 * @messages is automatically reallocated list of isds_message's. Be ware that
6640 * it returns only brief overview (envelope and some other fields) about each
6641 * message, not the complete message. FIXME: Specify exact fields.
6642 * The list is sorted by delivery time in ascending order.
6643 * Use NULL if you don't care about the metadata (useful if you want to know
6644 * only the @number). If you provide &NULL, list will be allocated on heap,
6645 * if you provide pointer to non-NULL, list will be freed automacally at first.
6646 * Also in case of error the list will be NULLed.
6647 * @return IE_SUCCESS or appropriate error code. */
6648 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
6649 const struct timeval *from_time, const struct timeval *to_time,
6650 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
6651 const unsigned long int offset, unsigned long int *number,
6652 struct isds_list **messages) {
6654 return isds_get_list_of_messages(
6655 context, 1,
6656 from_time, to_time, dmSenderOrgUnitNum, status_filter,
6657 offset, number,
6658 messages);
6662 /* Get list of incoming (addressed to you) messages.
6663 * Any criterion argument can be NULL, if you don't care about it.
6664 * @context is session context. Must not be NULL.
6665 * @from_time is minimal time and date of message sending inclusive.
6666 * @to_time is maximal time and date of message sending inclusive
6667 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
6668 * @status_filter is bit field of isds_message_status values. Use special
6669 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
6670 * all values, you can use bitwise arithmetic if you want.)
6671 * @offset is index of first message we are interested in. First message is 1.
6672 * Set to 0 (or 1) if you don't care.
6673 * @number is maximal length of list you want to get as input value, outputs
6674 * number of messages matching these criteria. Can be NULL if you don't care
6675 * (applies to output value either).
6676 * @messages is automatically reallocated list of isds_message's. Be ware that
6677 * it returns only brief overview (envelope and some other fields) about each
6678 * message, not the complete message. FIXME: Specify exact fields.
6679 * Use NULL if you don't care about the metadata (useful if you want to know
6680 * only the @number). If you provide &NULL, list will be allocated on heap,
6681 * if you provide pointer to non-NULL, list will be freed automacally at first.
6682 * Also in case of error the list will be NULLed.
6683 * @return IE_SUCCESS or appropriate error code. */
6684 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
6685 const struct timeval *from_time, const struct timeval *to_time,
6686 const long int *dmRecipientOrgUnitNum,
6687 const unsigned int status_filter,
6688 const unsigned long int offset, unsigned long int *number,
6689 struct isds_list **messages) {
6691 return isds_get_list_of_messages(
6692 context, 0,
6693 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
6694 offset, number,
6695 messages);
6699 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
6700 * code
6701 * @context is session context
6702 * @service is ISDS WS service handler
6703 * @service_name is name of SERVICE_DM_OPERATIONS
6704 * @message_id is message ID to send as service argument to ISDS
6705 * @response is server SOAP body response as XML document
6706 * @raw_response is automatically reallocated bitstream with response body. Use
6707 * NULL if you don't care
6708 * @raw_response_length is size of @raw_response in bytes
6709 * @code is ISDS status code
6710 * @status_message is ISDS status message
6711 * @return error coded from lower layer, context message will be set up
6712 * appropriately. */
6713 static isds_error build_send_check_message_request(struct isds_ctx *context,
6714 const isds_service service, const xmlChar *service_name,
6715 const char *message_id,
6716 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
6717 xmlChar **code, xmlChar **status_message) {
6719 isds_error err = IE_SUCCESS;
6720 char *service_name_locale = NULL, *message_id_locale = NULL;
6721 xmlNodePtr request = NULL, node;
6722 xmlNsPtr isds_ns = NULL;
6724 if (!context) return IE_INVALID_CONTEXT;
6725 if (!service_name || !message_id) return IE_INVAL;
6726 if (!response || !code || !status_message) return IE_INVAL;
6727 if (!raw_response_length && raw_response) return IE_INVAL;
6729 /* Free output argument */
6730 xmlFreeDoc(*response); *response = NULL;
6731 if (raw_response) zfree(*raw_response);
6732 free(*code);
6733 free(*status_message);
6736 /* Check if connection is established
6737 * TODO: This check should be done donwstairs. */
6738 if (!context->curl) return IE_CONNECTION_CLOSED;
6740 service_name_locale = utf82locale((char*)service_name);
6741 message_id_locale = utf82locale(message_id);
6742 if (!service_name_locale || !message_id_locale) {
6743 err = IE_NOMEM;
6744 goto leave;
6747 /* Build request */
6748 request = xmlNewNode(NULL, service_name);
6749 if (!request) {
6750 isds_printf_message(context,
6751 _("Could not build %s request"), service_name_locale);
6752 err = IE_ERROR;
6753 goto leave;
6755 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6756 if(!isds_ns) {
6757 isds_log_message(context, _("Could not create ISDS name space"));
6758 err = IE_ERROR;
6759 goto leave;
6761 xmlSetNs(request, isds_ns);
6764 /* Add requested ID */
6765 err = validate_message_id_length(context, (xmlChar *) message_id);
6766 if (err) goto leave;
6767 INSERT_STRING(request, "dmID", message_id);
6770 isds_log(ILF_ISDS, ILL_DEBUG,
6771 _("Sending %s request for %s message ID to ISDS\n"),
6772 service_name_locale, message_id_locale);
6774 /* Send request */
6775 err = isds(context, service, request, response,
6776 raw_response, raw_response_length);
6777 xmlFreeNode(request); request = NULL;
6779 if (err) {
6780 isds_log(ILF_ISDS, ILL_DEBUG,
6781 _("Processing ISDS response on %s request failed\n"),
6782 service_name_locale);
6783 goto leave;
6786 /* Check for response status */
6787 err = isds_response_status(context, service, *response,
6788 code, status_message, NULL);
6789 if (err) {
6790 isds_log(ILF_ISDS, ILL_DEBUG,
6791 _("ISDS response on %s request is missing status\n"),
6792 service_name_locale);
6793 goto leave;
6796 /* Request processed, but nothing found */
6797 if (xmlStrcmp(*code, BAD_CAST "0000")) {
6798 char *code_locale = utf82locale((char*) *code);
6799 char *status_message_locale = utf82locale((char*) *status_message);
6800 isds_log(ILF_ISDS, ILL_DEBUG,
6801 _("Server refused %s request for %s message ID "
6802 "(code=%s, message=%s)\n"),
6803 service_name_locale, message_id_locale,
6804 code_locale, status_message_locale);
6805 isds_log_message(context, status_message_locale);
6806 free(code_locale);
6807 free(status_message_locale);
6808 err = IE_ISDS;
6809 goto leave;
6812 leave:
6813 free(message_id_locale);
6814 free(service_name_locale);
6815 xmlFreeNode(request);
6816 return err;
6820 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
6821 * signed data and free ISDS response.
6822 * @context is session context
6823 * @message_id is UTF-8 encoded message ID for loging purpose
6824 * @response is parsed XML document. It will be freed and NULLed in the middle
6825 * of function run to save memmory. This is not guaranted in case of error.
6826 * @request_name is name of ISDS request used to construct response root
6827 * element name and for logging purpose.
6828 * @raw is reallocated output buffer with DER encoded CMS data
6829 * @raw_length is size of @raw buffer in bytes
6830 * @returns standard error codes, in case of error, @raw will be freed and
6831 * NULLed, @response sometimes. */
6832 static isds_error find_extract_signed_data_free_response(
6833 struct isds_ctx *context, const xmlChar *message_id,
6834 xmlDocPtr *response, const xmlChar *request_name,
6835 void **raw, size_t *raw_length) {
6837 isds_error err = IE_SUCCESS;
6838 char *xpath_expression = NULL;
6839 xmlXPathContextPtr xpath_ctx = NULL;
6840 xmlXPathObjectPtr result = NULL;
6841 char *encoded_structure = NULL;
6843 if (!context) return IE_INVALID_CONTEXT;
6844 if (!raw) return IE_INVAL;
6845 zfree(*raw);
6846 if (!message_id || !response || !*response || !request_name || !raw_length)
6847 return IE_INVAL;
6849 /* Build XPath expression */
6850 xpath_expression = astrcat3("/isds:", (char *) request_name,
6851 "Response/isds:dmSignature");
6852 if (!xpath_expression) return IE_NOMEM;
6854 /* Extract data */
6855 xpath_ctx = xmlXPathNewContext(*response);
6856 if (!xpath_ctx) {
6857 err = IE_ERROR;
6858 goto leave;
6860 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6861 err = IE_ERROR;
6862 goto leave;
6864 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
6865 if (!result) {
6866 err = IE_ERROR;
6867 goto leave;
6869 /* Empty response */
6870 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6871 char *message_id_locale = utf82locale((char*) message_id);
6872 isds_printf_message(context,
6873 _("Server did not return any signed data for mesage ID `%s' "
6874 "on %s request"),
6875 message_id_locale, request_name);
6876 free(message_id_locale);
6877 err = IE_ISDS;
6878 goto leave;
6880 /* More reponses */
6881 if (result->nodesetval->nodeNr > 1) {
6882 char *message_id_locale = utf82locale((char*) message_id);
6883 isds_printf_message(context,
6884 _("Server did return more signed data for message ID `%s' "
6885 "on %s request"),
6886 message_id_locale, request_name);
6887 free(message_id_locale);
6888 err = IE_ISDS;
6889 goto leave;
6891 /* One response */
6892 xpath_ctx->node = result->nodesetval->nodeTab[0];
6894 /* Extract PKCS#7 structure */
6895 EXTRACT_STRING(".", encoded_structure);
6896 if (!encoded_structure) {
6897 isds_log_message(context, _("dmSignature element is empty"));
6900 /* Here we have delivery info as standalone CMS in encoded_structure.
6901 * We don't need any other data, free them: */
6902 xmlXPathFreeObject(result); result = NULL;
6903 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
6904 xmlFreeDoc(*response); *response = NULL;
6907 /* Decode PKCS#7 to DER format */
6908 *raw_length = b64decode(encoded_structure, raw);
6909 if (*raw_length == (size_t) -1) {
6910 isds_log_message(context,
6911 _("Error while Base64-decoding PKCS#7 structure"));
6912 err = IE_ERROR;
6913 goto leave;
6916 leave:
6917 if (err) {
6918 zfree(*raw);
6919 raw_length = 0;
6922 free(encoded_structure);
6923 xmlXPathFreeObject(result);
6924 xmlXPathFreeContext(xpath_ctx);
6925 free(xpath_expression);
6927 return err;
6931 /* Download incoming message envelope identified by ID.
6932 * @context is session context
6933 * @message_id is message identifier (you can get them from
6934 * isds_get_list_of_received_messages())
6935 * @message is automatically reallocated message retrieved from ISDS.
6936 * It will miss documents per se. Use isds_get_received_message(), if you are
6937 * interrested in documents (content) too.
6938 * Returned hash and timestamp require documents to be verifiable. */
6939 isds_error isds_get_received_envelope(struct isds_ctx *context,
6940 const char *message_id, struct isds_message **message) {
6942 isds_error err = IE_SUCCESS;
6943 xmlDocPtr response = NULL;
6944 xmlChar *code = NULL, *status_message = NULL;
6945 xmlXPathContextPtr xpath_ctx = NULL;
6946 xmlXPathObjectPtr result = NULL;
6948 if (!context) return IE_INVALID_CONTEXT;
6949 zfree(context->long_message);
6951 /* Free former message if any */
6952 if (!message) return IE_INVAL;
6953 isds_message_free(message);
6955 /* Do request and check for success */
6956 err = build_send_check_message_request(context, SERVICE_DM_INFO,
6957 BAD_CAST "MessageEnvelopeDownload", message_id,
6958 &response, NULL, NULL, &code, &status_message);
6959 if (err) goto leave;
6961 /* Extract data */
6962 xpath_ctx = xmlXPathNewContext(response);
6963 if (!xpath_ctx) {
6964 err = IE_ERROR;
6965 goto leave;
6967 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6968 err = IE_ERROR;
6969 goto leave;
6971 result = xmlXPathEvalExpression(
6972 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
6973 "isds:dmReturnedMessageEnvelope",
6974 xpath_ctx);
6975 if (!result) {
6976 err = IE_ERROR;
6977 goto leave;
6979 /* Empty response */
6980 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6981 char *message_id_locale = utf82locale((char*) message_id);
6982 isds_printf_message(context,
6983 _("Server did not return any envelope for ID `%s' "
6984 "on MessageEnvelopeDownload request"), message_id_locale);
6985 free(message_id_locale);
6986 err = IE_ISDS;
6987 goto leave;
6989 /* More envelops */
6990 if (result->nodesetval->nodeNr > 1) {
6991 char *message_id_locale = utf82locale((char*) message_id);
6992 isds_printf_message(context,
6993 _("Server did return more envelopes for ID `%s' "
6994 "on MessageEnvelopeDownload request"), message_id_locale);
6995 free(message_id_locale);
6996 err = IE_ISDS;
6997 goto leave;
6999 /* One message */
7000 xpath_ctx->node = result->nodesetval->nodeTab[0];
7002 /* Extract the envelope (= message without documents, hence 0) */
7003 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
7004 if (err) goto leave;
7006 /* Save XML blob */
7007 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
7008 &(*message)->raw_length);
7010 leave:
7011 if (err) {
7012 isds_message_free(message);
7015 xmlXPathFreeObject(result);
7016 xmlXPathFreeContext(xpath_ctx);
7018 free(code);
7019 free(status_message);
7020 xmlFreeDoc(response);
7022 if (!err)
7023 isds_log(ILF_ISDS, ILL_DEBUG,
7024 _("MessageEnvelopeDownload request processed by server "
7025 "successfully.\n")
7027 return err;
7031 /* Load delivery info of any format from buffer.
7032 * @context is session context
7033 * @raw_type advertises format of @buffer content. Only delivery info types
7034 * are accepted.
7035 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
7036 * retrieve such data from message->raw after calling
7037 * isds_get_signed_delivery_info().
7038 * @length is length of buffer in bytes.
7039 * @message is automatically reallocated message parsed from @buffer.
7040 * @strategy selects how buffer will be attached into raw isds_message member.
7041 * */
7042 isds_error isds_load_delivery_info(struct isds_ctx *context,
7043 const isds_raw_type raw_type,
7044 const void *buffer, const size_t length,
7045 struct isds_message **message, const isds_buffer_strategy strategy) {
7047 isds_error err = IE_SUCCESS;
7048 message_ns_type message_ns;
7049 xmlDocPtr message_doc = NULL;
7050 xmlXPathContextPtr xpath_ctx = NULL;
7051 xmlXPathObjectPtr result = NULL;
7052 void *xml_stream = NULL;
7053 size_t xml_stream_length = 0;
7055 if (!context) return IE_INVALID_CONTEXT;
7056 zfree(context->long_message);
7057 if (!message) return IE_INVAL;
7058 isds_message_free(message);
7059 if (!buffer) return IE_INVAL;
7062 /* Select buffer format and extract XML from CMS*/
7063 switch (raw_type) {
7064 case RAWTYPE_DELIVERYINFO:
7065 message_ns = MESSAGE_NS_UNSIGNED;
7066 xml_stream = (void *) buffer;
7067 xml_stream_length = length;
7068 break;
7070 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
7071 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
7072 xml_stream = (void *) buffer;
7073 xml_stream_length = length;
7074 break;
7076 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
7077 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
7078 err = extract_cms_data(context, buffer, length,
7079 &xml_stream, &xml_stream_length);
7080 if (err) goto leave;
7081 break;
7083 default:
7084 isds_log_message(context, _("Bad raw delivery representation type"));
7085 return IE_INVAL;
7086 break;
7089 isds_log(ILF_ISDS, ILL_DEBUG,
7090 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
7091 xml_stream_length, xml_stream);
7093 /* Convert delivery info XML stream into XPath context */
7094 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
7095 if (!message_doc) {
7096 err = IE_XML;
7097 goto leave;
7099 xpath_ctx = xmlXPathNewContext(message_doc);
7100 if (!xpath_ctx) {
7101 err = IE_ERROR;
7102 goto leave;
7104 /* XXX: Name spaces mangled for signed delivery info:
7105 * http://isds.czechpoint.cz/v20/delivery:
7107 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
7108 * <q:dmDelivery>
7109 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
7110 * <p:dmID>170272</p:dmID>
7111 * ...
7112 * </p:dmDm>
7113 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
7114 * ...
7115 * </q:dmEvents>...</q:dmEvents>
7116 * </q:dmDelivery>
7117 * </q:GetDeliveryInfoResponse>
7118 * */
7119 if (register_namespaces(xpath_ctx, message_ns)) {
7120 err = IE_ERROR;
7121 goto leave;
7123 result = xmlXPathEvalExpression(
7124 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
7125 xpath_ctx);
7126 if (!result) {
7127 err = IE_ERROR;
7128 goto leave;
7130 /* Empty delivery info */
7131 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7132 isds_printf_message(context,
7133 _("XML document ss not sisds:dmDelivery document"));
7134 err = IE_ISDS;
7135 goto leave;
7137 /* More delivery infos */
7138 if (result->nodesetval->nodeNr > 1) {
7139 isds_printf_message(context,
7140 _("XML document has more sisds:dmDelivery elements"));
7141 err = IE_ISDS;
7142 goto leave;
7144 /* One delivery info */
7145 xpath_ctx->node = result->nodesetval->nodeTab[0];
7147 /* Extract the envelope (= message without documents, hence 0).
7148 * XXX: extract_TReturnedMessage() can obtain attachments size,
7149 * but delivery info carries none. It's coded as option elements,
7150 * so it should work. */
7151 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
7152 if (err) goto leave;
7154 /* Extract events */
7155 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
7156 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
7157 if (err) { err = IE_ERROR; goto leave; }
7158 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
7159 if (err) goto leave;
7161 /* Append raw CMS structure into message */
7162 (*message)->raw_type = raw_type;
7163 switch (strategy) {
7164 case BUFFER_DONT_STORE:
7165 break;
7166 case BUFFER_COPY:
7167 (*message)->raw = malloc(length);
7168 if (!(*message)->raw) {
7169 err = IE_NOMEM;
7170 goto leave;
7172 memcpy((*message)->raw, buffer, length);
7173 (*message)->raw_length = length;
7174 break;
7175 case BUFFER_MOVE:
7176 (*message)->raw = (void *) buffer;
7177 (*message)->raw_length = length;
7178 break;
7179 default:
7180 err = IE_ENUM;
7181 goto leave;
7184 leave:
7185 if (err) {
7186 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
7187 isds_message_free(message);
7190 xmlXPathFreeObject(result);
7191 xmlXPathFreeContext(xpath_ctx);
7192 xmlFreeDoc(message_doc);
7193 if (xml_stream != buffer) cms_data_free(xml_stream);
7195 if (!err)
7196 isds_log(ILF_ISDS, ILL_DEBUG,
7197 _("Delivery info loaded successfully.\n"));
7198 return err;
7202 /* Download signed delivery infosheet of given message identified by ID.
7203 * @context is session context
7204 * @message_id is message identifier (you can get them from
7205 * isds_get_list_of_{sent,received}_messages())
7206 * @message is automatically reallocated message retrieved from ISDS.
7207 * It will miss documents per se. Use isds_get_signed_received_message(),
7208 * if you are interrested in documents (content). OTOH, only this function
7209 * can get list events message has gone through. */
7210 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
7211 const char *message_id, struct isds_message **message) {
7213 isds_error err = IE_SUCCESS;
7214 xmlDocPtr response = NULL;
7215 xmlChar *code = NULL, *status_message = NULL;
7216 void *raw = NULL;
7217 size_t raw_length = 0;
7219 if (!context) return IE_INVALID_CONTEXT;
7220 zfree(context->long_message);
7222 /* Free former message if any */
7223 if (!message) return IE_INVAL;
7224 isds_message_free(message);
7226 /* Do request and check for success */
7227 err = build_send_check_message_request(context, SERVICE_DM_INFO,
7228 BAD_CAST "GetSignedDeliveryInfo", message_id,
7229 &response, NULL, NULL, &code, &status_message);
7230 if (err) goto leave;
7232 /* Find signed delivery info, extract it into raw and maybe free
7233 * response */
7234 err = find_extract_signed_data_free_response(context,
7235 (xmlChar *)message_id, &response,
7236 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
7237 if (err) goto leave;
7239 /* Parse delivery info */
7240 err = isds_load_delivery_info(context,
7241 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
7242 message, BUFFER_MOVE);
7243 if (err) goto leave;
7245 raw = NULL;
7247 leave:
7248 if (err) {
7249 isds_message_free(message);
7252 free(raw);
7253 free(code);
7254 free(status_message);
7255 xmlFreeDoc(response);
7257 if (!err)
7258 isds_log(ILF_ISDS, ILL_DEBUG,
7259 _("GetSignedDeliveryInfo request processed by server "
7260 "successfully.\n")
7262 return err;
7266 /* Download delivery infosheet of given message identified by ID.
7267 * @context is session context
7268 * @message_id is message identifier (you can get them from
7269 * isds_get_list_of_{sent,received}_messages())
7270 * @message is automatically reallocated message retrieved from ISDS.
7271 * It will miss documents per se. Use isds_get_received_message(), if you are
7272 * interrested in documents (content). OTOH, only this function can get list
7273 * events message has gone through. */
7274 isds_error isds_get_delivery_info(struct isds_ctx *context,
7275 const char *message_id, struct isds_message **message) {
7277 isds_error err = IE_SUCCESS;
7278 xmlDocPtr response = NULL;
7279 xmlChar *code = NULL, *status_message = NULL;
7280 xmlXPathContextPtr xpath_ctx = NULL;
7281 xmlXPathObjectPtr result = NULL;
7282 xmlNodePtr delivery_node = NULL;
7284 if (!context) return IE_INVALID_CONTEXT;
7285 zfree(context->long_message);
7287 /* Free former message if any */
7288 if (!message) return IE_INVAL;
7289 isds_message_free(message);
7291 /* Do request and check for success */
7292 err = build_send_check_message_request(context, SERVICE_DM_INFO,
7293 BAD_CAST "GetDeliveryInfo", message_id,
7294 &response, NULL, NULL, &code, &status_message);
7295 if (err) goto leave;
7297 /* Extract data */
7298 xpath_ctx = xmlXPathNewContext(response);
7299 if (!xpath_ctx) {
7300 err = IE_ERROR;
7301 goto leave;
7303 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7304 err = IE_ERROR;
7305 goto leave;
7307 result = xmlXPathEvalExpression(
7308 BAD_CAST "/isds:GetDeliveryInfoResponse/isds:dmDelivery",
7309 xpath_ctx);
7310 if (!result) {
7311 err = IE_ERROR;
7312 goto leave;
7314 /* Empty response */
7315 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7316 char *message_id_locale = utf82locale((char*) message_id);
7317 isds_printf_message(context,
7318 _("Server did not return any delivery info for ID `%s' "
7319 "on GetDeliveryInfo request"), message_id_locale);
7320 free(message_id_locale);
7321 err = IE_ISDS;
7322 goto leave;
7324 /* More delivery infos */
7325 if (result->nodesetval->nodeNr > 1) {
7326 char *message_id_locale = utf82locale((char*) message_id);
7327 isds_printf_message(context,
7328 _("Server did return more delivery infos for ID `%s' "
7329 "on GetDeliveryInfo request"), message_id_locale);
7330 free(message_id_locale);
7331 err = IE_ISDS;
7332 goto leave;
7334 /* One delivery info */
7335 xpath_ctx->node = delivery_node = result->nodesetval->nodeTab[0];
7337 /* Extract the envelope (= message without documents, hence 0).
7338 * XXX: extract_TReturnedMessage() can obtain attachments size,
7339 * but delivery info carries none. It's coded as option elements,
7340 * so it should work. */
7341 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
7342 if (err) goto leave;
7344 /* Extract events */
7345 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmEvents", xpath_ctx);
7346 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
7347 if (err) { err = IE_ERROR; goto leave; }
7348 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
7349 if (err) goto leave;
7351 /* Save XML blob */
7352 err = serialize_subtree(context, delivery_node, &(*message)->raw,
7353 &(*message)->raw_length);
7355 leave:
7356 if (err) {
7357 isds_message_free(message);
7360 xmlXPathFreeObject(result);
7361 xmlXPathFreeContext(xpath_ctx);
7363 free(code);
7364 free(status_message);
7365 xmlFreeDoc(response);
7367 if (!err)
7368 isds_log(ILF_ISDS, ILL_DEBUG,
7369 _("GetDeliveryInfo request processed by server "
7370 "successfully.\n")
7372 return err;
7376 /* Load incoming message from buffer.
7377 * @context is session context
7378 * @buffer XML stream with unsigned message. You can retrieve such data from
7379 * message->raw after calling isds_get_received_message().
7380 * @length is length of buffer in bytes.
7381 * @message is automatically reallocated message parsed from @buffer.
7382 * @strategy selects how buffer will be attached into raw isds_message member.
7383 * */
7384 isds_error isds_load_received_message(struct isds_ctx *context,
7385 const void *buffer, const size_t length,
7386 struct isds_message **message, const isds_buffer_strategy strategy) {
7388 isds_error err = IE_SUCCESS;
7389 xmlDocPtr message_doc = NULL;
7390 xmlXPathContextPtr xpath_ctx = NULL;
7391 xmlXPathObjectPtr result = NULL;
7393 if (!context) return IE_INVALID_CONTEXT;
7394 zfree(context->long_message);
7395 if (!message) return IE_INVAL;
7396 isds_message_free(message);
7397 if (!buffer) return IE_INVAL;
7400 isds_log(ILF_ISDS, ILL_DEBUG,
7401 _("Incoming message content:\n%.*s\nEnd of message\n"),
7402 length, buffer);
7404 /* Convert extracted messages XML stream into XPath context */
7405 message_doc = xmlParseMemory(buffer, length);
7406 if (!message_doc) {
7407 err = IE_XML;
7408 goto leave;
7410 xpath_ctx = xmlXPathNewContext(message_doc);
7411 if (!xpath_ctx) {
7412 err = IE_ERROR;
7413 goto leave;
7415 /* XXX: Standard name space */
7416 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7417 err = IE_ERROR;
7418 goto leave;
7420 result = xmlXPathEvalExpression(
7421 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
7422 xpath_ctx);
7423 if (!result) {
7424 err = IE_ERROR;
7425 goto leave;
7427 /* Missing dmReturnedMessage */
7428 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7429 isds_printf_message(context,
7430 _("XML document does not contain isds:dmReturnedMessage "
7431 "element"));
7432 err = IE_ISDS;
7433 goto leave;
7435 /* More elements. This should never happen. */
7436 if (result->nodesetval->nodeNr > 1) {
7437 isds_printf_message(context,
7438 _("XML document has more isds:dmReturnedMessage elements"));
7439 err = IE_ISDS;
7440 goto leave;
7442 /* One message */
7443 xpath_ctx->node = result->nodesetval->nodeTab[0];
7445 /* Extract the message */
7446 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
7447 if (err) goto leave;
7449 /* Append XML stream into message */
7450 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
7451 switch (strategy) {
7452 case BUFFER_DONT_STORE:
7453 break;
7454 case BUFFER_COPY:
7455 (*message)->raw = malloc(length);
7456 if (!(*message)->raw) {
7457 err = IE_NOMEM;
7458 goto leave;
7460 memcpy((*message)->raw, buffer, length);
7461 (*message)->raw_length = length;
7462 break;
7463 case BUFFER_MOVE:
7464 (*message)->raw = (void *) buffer;
7465 (*message)->raw_length = length;
7466 break;
7467 default:
7468 err = IE_ENUM;
7469 goto leave;
7472 leave:
7473 if (err) {
7474 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
7475 isds_message_free(message);
7478 xmlFreeDoc(message_doc);
7479 xmlXPathFreeObject(result);
7480 xmlXPathFreeContext(xpath_ctx);
7482 if (!err)
7483 isds_log(ILF_ISDS, ILL_DEBUG,
7484 _("Incoming message loaded successfully.\n"));
7485 return err;
7489 /* Download incoming message identified by ID.
7490 * @context is session context
7491 * @message_id is message identifier (you can get them from
7492 * isds_get_list_of_received_messages())
7493 * @message is automatically reallocated message retrieved from ISDS */
7494 isds_error isds_get_received_message(struct isds_ctx *context,
7495 const char *message_id, struct isds_message **message) {
7497 isds_error err = IE_SUCCESS;
7498 xmlDocPtr response = NULL;
7499 void *xml_stream = NULL;
7500 size_t xml_stream_length;
7501 xmlChar *code = NULL, *status_message = NULL;
7502 xmlXPathContextPtr xpath_ctx = NULL;
7503 xmlXPathObjectPtr result = NULL;
7504 char *phys_path = NULL;
7505 size_t phys_start, phys_end;
7507 if (!context) return IE_INVALID_CONTEXT;
7508 zfree(context->long_message);
7510 /* Free former message if any */
7511 if (message) isds_message_free(message);
7513 /* Do request and check for success */
7514 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
7515 BAD_CAST "MessageDownload", message_id,
7516 &response, &xml_stream, &xml_stream_length,
7517 &code, &status_message);
7518 if (err) goto leave;
7520 /* Extract data */
7521 xpath_ctx = xmlXPathNewContext(response);
7522 if (!xpath_ctx) {
7523 err = IE_ERROR;
7524 goto leave;
7526 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7527 err = IE_ERROR;
7528 goto leave;
7530 result = xmlXPathEvalExpression(
7531 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
7532 xpath_ctx);
7533 if (!result) {
7534 err = IE_ERROR;
7535 goto leave;
7537 /* Empty response */
7538 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7539 char *message_id_locale = utf82locale((char*) message_id);
7540 isds_printf_message(context,
7541 _("Server did not return any message for ID `%s' "
7542 "on MessageDownload request"), message_id_locale);
7543 free(message_id_locale);
7544 err = IE_ISDS;
7545 goto leave;
7547 /* More messages */
7548 if (result->nodesetval->nodeNr > 1) {
7549 char *message_id_locale = utf82locale((char*) message_id);
7550 isds_printf_message(context,
7551 _("Server did return more messages for ID `%s' "
7552 "on MessageDownload request"), message_id_locale);
7553 free(message_id_locale);
7554 err = IE_ISDS;
7555 goto leave;
7557 /* One message */
7558 xpath_ctx->node = result->nodesetval->nodeTab[0];
7560 /* Extract the message */
7561 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
7562 if (err) goto leave;
7564 /* Locate raw XML blob */
7565 phys_path = strdup(
7566 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
7567 PHYSXML_ELEMENT_SEPARATOR
7568 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
7569 PHYSXML_ELEMENT_SEPARATOR
7570 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
7572 if (!phys_path) {
7573 err = IE_NOMEM;
7574 goto leave;
7576 err = find_element_boundary(xml_stream, xml_stream_length,
7577 phys_path, &phys_start, &phys_end);
7578 zfree(phys_path);
7579 if (err) {
7580 isds_log_message(context,
7581 _("Substring with isds:MessageDownloadResponse element "
7582 "could not be located in raw SOAP message"));
7583 goto leave;
7585 /* Save XML blob */
7586 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
7587 &(*message)->raw_length);*/
7588 /* TODO: Store name space declarations from ancestors */
7589 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
7590 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
7591 (*message)->raw_length = phys_end - phys_start + 1;
7592 (*message)->raw = malloc((*message)->raw_length);
7593 if (!(*message)->raw) {
7594 err = IE_NOMEM;
7595 goto leave;
7597 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
7600 leave:
7601 if (err) {
7602 isds_message_free(message);
7605 free(phys_path);
7607 xmlXPathFreeObject(result);
7608 xmlXPathFreeContext(xpath_ctx);
7610 free(code);
7611 free(status_message);
7612 free(xml_stream);
7613 xmlFreeDoc(response);
7615 if (!err)
7616 isds_log(ILF_ISDS, ILL_DEBUG,
7617 _("MessageDownload request processed by server "
7618 "successfully.\n")
7620 return err;
7624 /* Load signed message from buffer.
7625 * @context is session context
7626 * @outgoing is true if message is outgoing, false if message is incoming
7627 * @buffer is DER encoded PKCS#7 structure with signed message. You can
7628 * retrieve such data from message->raw after calling
7629 * isds_get_signed_{received,sent}_message().
7630 * @length is length of buffer in bytes.
7631 * @message is automatically reallocated message parsed from @buffer.
7632 * @strategy selects how buffer will be attached into raw isds_message member.
7633 * */
7634 isds_error isds_load_signed_message(struct isds_ctx *context,
7635 const _Bool outgoing, const void *buffer, const size_t length,
7636 struct isds_message **message, const isds_buffer_strategy strategy) {
7638 isds_error err = IE_SUCCESS;
7639 xmlDocPtr message_doc = NULL;
7640 xmlXPathContextPtr xpath_ctx = NULL;
7641 xmlXPathObjectPtr result = NULL;
7642 void *xml_stream = NULL;
7643 size_t xml_stream_length = 0;
7645 if (!context) return IE_INVALID_CONTEXT;
7646 zfree(context->long_message);
7647 if (!message) return IE_INVAL;
7648 isds_message_free(message);
7649 if (!buffer) return IE_INVAL;
7652 /* Extract message from PKCS#7 structure */
7653 err = extract_cms_data(context, buffer, length,
7654 &xml_stream, &xml_stream_length);
7655 if (err) goto leave;
7657 isds_log(ILF_ISDS, ILL_DEBUG, (outgoing) ?
7658 _("Signed outgoing message content:\n%.*s\nEnd of message\n") :
7659 _("Signed incoming message content:\n%.*s\nEnd of message\n"),
7660 xml_stream_length, xml_stream);
7662 /* Convert extracted messages XML stream into XPath context */
7663 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
7664 if (!message_doc) {
7665 err = IE_XML;
7666 goto leave;
7668 xpath_ctx = xmlXPathNewContext(message_doc);
7669 if (!xpath_ctx) {
7670 err = IE_ERROR;
7671 goto leave;
7673 /* XXX: Name spaces mangled for outgoing direction:
7674 * http://isds.czechpoint.cz/v20/SentMessage:
7676 * <q:MessageDownloadResponse
7677 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
7678 * <q:dmReturnedMessage>
7679 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
7680 * <p:dmID>151916</p:dmID>
7681 * ...
7682 * </p:dmDm>
7683 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
7684 * ...
7685 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
7686 * </q:dmReturnedMessage>
7687 * </q:MessageDownloadResponse>
7689 * XXX: Name spaces mangled for incoming direction:
7690 * http://isds.czechpoint.cz/v20/message:
7692 * <q:MessageDownloadResponse
7693 * xmlns:q="http://isds.czechpoint.cz/v20/message">
7694 * <q:dmReturnedMessage>
7695 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
7696 * <p:dmID>151916</p:dmID>
7697 * ...
7698 * </p:dmDm>
7699 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
7700 * ...
7701 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
7702 * </q:dmReturnedMessage>
7703 * </q:MessageDownloadResponse>
7705 * Stupidity of ISDS developers is unlimited */
7706 if (register_namespaces(xpath_ctx, (outgoing) ?
7707 MESSAGE_NS_SIGNED_OUTGOING : MESSAGE_NS_SIGNED_INCOMING)) {
7708 err = IE_ERROR;
7709 goto leave;
7711 /* XXX: Embeded message XML document is always rooted as
7712 * /sisds:MessageDownloadResponse (even outgoing message). */
7713 result = xmlXPathEvalExpression(
7714 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
7715 xpath_ctx);
7716 if (!result) {
7717 err = IE_ERROR;
7718 goto leave;
7720 /* Empty embedded message */
7721 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7722 isds_printf_message(context,
7723 _("XML document embedded into PKCS#7 structure is not "
7724 "sisds:dmReturnedMessage document"));
7725 err = IE_ISDS;
7726 goto leave;
7728 /* More embedded messages */
7729 if (result->nodesetval->nodeNr > 1) {
7730 isds_printf_message(context,
7731 _("XML document embedded into PKCS#7 structure has more "
7732 "root sisds:dmReturnedMessage elements"));
7733 err = IE_ISDS;
7734 goto leave;
7736 /* One embedded message */
7737 xpath_ctx->node = result->nodesetval->nodeTab[0];
7739 /* Extract the message */
7740 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
7741 if (err) goto leave;
7743 /* Append raw CMS structure into message */
7744 (*message)->raw_type = (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
7745 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
7746 switch (strategy) {
7747 case BUFFER_DONT_STORE:
7748 break;
7749 case BUFFER_COPY:
7750 (*message)->raw = malloc(length);
7751 if (!(*message)->raw) {
7752 err = IE_NOMEM;
7753 goto leave;
7755 memcpy((*message)->raw, buffer, length);
7756 (*message)->raw_length = length;
7757 break;
7758 case BUFFER_MOVE:
7759 (*message)->raw = (void *) buffer;
7760 (*message)->raw_length = length;
7761 break;
7762 default:
7763 err = IE_ENUM;
7764 goto leave;
7768 leave:
7769 if (err) {
7770 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
7771 isds_message_free(message);
7774 xmlFreeDoc(message_doc);
7775 cms_data_free(xml_stream);
7776 xmlXPathFreeObject(result);
7777 xmlXPathFreeContext(xpath_ctx);
7779 if (!err)
7780 isds_log(ILF_ISDS, ILL_DEBUG,
7781 _("Signed message loaded successfully.\n"));
7782 return err;
7786 /* Load message of any type from buffer.
7787 * @context is session context
7788 * @raw_type defines content type of @buffer. Only message types are allowed.
7789 * @buffer is message raw representation. Format (CMS, plain signed,
7790 * message direction) is defined in @raw_type. You can retrieve such data
7791 * from message->raw after calling isds_get_[signed]{received,sent}_message().
7792 * @length is length of buffer in bytes.
7793 * @message is automatically reallocated message parsed from @buffer.
7794 * @strategy selects how buffer will be attached into raw isds_message member.
7795 * */
7796 isds_error isds_load_message(struct isds_ctx *context,
7797 const isds_raw_type raw_type, const void *buffer, const size_t length,
7798 struct isds_message **message, const isds_buffer_strategy strategy) {
7800 isds_error err = IE_SUCCESS;
7801 void *xml_stream = NULL;
7802 size_t xml_stream_length = 0;
7803 message_ns_type message_ns;
7804 xmlDocPtr message_doc = NULL;
7805 xmlXPathContextPtr xpath_ctx = NULL;
7806 xmlXPathObjectPtr result = NULL;
7808 if (!context) return IE_INVALID_CONTEXT;
7809 zfree(context->long_message);
7810 if (!message) return IE_INVAL;
7811 isds_message_free(message);
7812 if (!buffer) return IE_INVAL;
7815 /* Select buffer format and extract XML from CMS*/
7816 switch (raw_type) {
7817 case RAWTYPE_INCOMING_MESSAGE:
7818 message_ns = MESSAGE_NS_UNSIGNED;
7819 xml_stream = (void *) buffer;
7820 xml_stream_length = length;
7821 break;
7823 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
7824 message_ns = MESSAGE_NS_SIGNED_INCOMING;
7825 xml_stream = (void *) buffer;
7826 xml_stream_length = length;
7827 break;
7829 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
7830 message_ns = MESSAGE_NS_SIGNED_INCOMING;
7831 err = extract_cms_data(context, buffer, length,
7832 &xml_stream, &xml_stream_length);
7833 if (err) goto leave;
7834 break;
7836 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
7837 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
7838 xml_stream = (void *) buffer;
7839 xml_stream_length = length;
7840 break;
7842 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
7843 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
7844 err = extract_cms_data(context, buffer, length,
7845 &xml_stream, &xml_stream_length);
7846 if (err) goto leave;
7847 break;
7849 default:
7850 isds_log_message(context, _("Bad raw message representation type"));
7851 return IE_INVAL;
7852 break;
7855 isds_log(ILF_ISDS, ILL_DEBUG,
7856 _("Loading message:\n%.*s\nEnd of message\n"),
7857 xml_stream_length, xml_stream);
7859 /* Convert messages XML stream into XPath context */
7860 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
7861 if (!message_doc) {
7862 err = IE_XML;
7863 goto leave;
7865 xpath_ctx = xmlXPathNewContext(message_doc);
7866 if (!xpath_ctx) {
7867 err = IE_ERROR;
7868 goto leave;
7870 /* XXX: Standard name space for unsigned incoming direction:
7871 * http://isds.czechpoint.cz/v20/
7873 * XXX: Name spaces mangled for signed outgoing direction:
7874 * http://isds.czechpoint.cz/v20/SentMessage:
7876 * <q:MessageDownloadResponse
7877 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
7878 * <q:dmReturnedMessage>
7879 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
7880 * <p:dmID>151916</p:dmID>
7881 * ...
7882 * </p:dmDm>
7883 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
7884 * ...
7885 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
7886 * </q:dmReturnedMessage>
7887 * </q:MessageDownloadResponse>
7889 * XXX: Name spaces mangled for signed incoming direction:
7890 * http://isds.czechpoint.cz/v20/message:
7892 * <q:MessageDownloadResponse
7893 * xmlns:q="http://isds.czechpoint.cz/v20/message">
7894 * <q:dmReturnedMessage>
7895 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
7896 * <p:dmID>151916</p:dmID>
7897 * ...
7898 * </p:dmDm>
7899 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
7900 * ...
7901 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
7902 * </q:dmReturnedMessage>
7903 * </q:MessageDownloadResponse>
7905 * Stupidity of ISDS developers is unlimited */
7906 if (register_namespaces(xpath_ctx, message_ns)) {
7907 err = IE_ERROR;
7908 goto leave;
7910 result = xmlXPathEvalExpression(
7911 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
7912 xpath_ctx);
7913 if (!result) {
7914 err = IE_ERROR;
7915 goto leave;
7917 /* Empty message */
7918 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7919 isds_printf_message(context,
7920 _("XML document does not contain "
7921 "sisds:dmReturnedMessage element"));
7922 err = IE_ISDS;
7923 goto leave;
7925 /* More messages */
7926 if (result->nodesetval->nodeNr > 1) {
7927 isds_printf_message(context,
7928 _("XML document has more sisds:dmReturnedMessage elements"));
7929 err = IE_ISDS;
7930 goto leave;
7932 /* One message */
7933 xpath_ctx->node = result->nodesetval->nodeTab[0];
7935 /* Extract the message */
7936 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
7937 if (err) goto leave;
7939 /* Append raw buffer into message */
7940 (*message)->raw_type = raw_type;
7941 switch (strategy) {
7942 case BUFFER_DONT_STORE:
7943 break;
7944 case BUFFER_COPY:
7945 (*message)->raw = malloc(length);
7946 if (!(*message)->raw) {
7947 err = IE_NOMEM;
7948 goto leave;
7950 memcpy((*message)->raw, buffer, length);
7951 (*message)->raw_length = length;
7952 break;
7953 case BUFFER_MOVE:
7954 (*message)->raw = (void *) buffer;
7955 (*message)->raw_length = length;
7956 break;
7957 default:
7958 err = IE_ENUM;
7959 goto leave;
7963 leave:
7964 if (err) {
7965 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
7966 isds_message_free(message);
7969 if (xml_stream != buffer) cms_data_free(xml_stream);
7970 xmlXPathFreeObject(result);
7971 xmlXPathFreeContext(xpath_ctx);
7972 xmlFreeDoc(message_doc);
7974 if (!err)
7975 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
7976 return err;
7980 /* Determine type of raw message or delivery info according some heuristics.
7981 * It does not validate the raw blob.
7982 * @context is session context
7983 * @raw_type returns content type of @buffer. Valid only if exit code of this
7984 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
7985 * reallocted memory.
7986 * @buffer is message raw representation.
7987 * @length is length of buffer in bytes. */
7988 isds_error isds_guess_raw_type(struct isds_ctx *context,
7989 isds_raw_type *raw_type, const void *buffer, const size_t length) {
7990 isds_error err;
7991 void *xml_stream = NULL;
7992 size_t xml_stream_length = 0;
7993 xmlDocPtr document = NULL;
7994 xmlNodePtr root = NULL;
7996 if (!context) return IE_INVALID_CONTEXT;
7997 zfree(context->long_message);
7998 if (length == 0 || !buffer) return IE_INVAL;
7999 if (!raw_type) return IE_INVAL;
8001 /* Try CMS */
8002 err = extract_cms_data(context, buffer, length,
8003 &xml_stream, &xml_stream_length);
8004 if (err) {
8005 xml_stream = (void *) buffer;
8006 xml_stream_length = (size_t) length;
8007 err = IE_SUCCESS;
8010 /* Try XML */
8011 document = xmlParseMemory(xml_stream, xml_stream_length);
8012 if (!document) {
8013 isds_printf_message(context,
8014 _("Could not parse data as XML document"));
8015 err = IE_NOTSUP;
8016 goto leave;
8019 /* Get root element */
8020 root = xmlDocGetRootElement(document);
8021 if (!root) {
8022 isds_printf_message(context,
8023 _("XML document is missing root element"));
8024 err = IE_XML;
8025 goto leave;
8028 if (!root->ns || !root->ns->href) {
8029 isds_printf_message(context,
8030 _("Root element does not belong to any name space"));
8031 err = IE_NOTSUP;
8032 goto leave;
8035 /* Test name space */
8036 if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_INCOMING_NS)) {
8037 if (xml_stream == buffer)
8038 *raw_type = RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE;
8039 else
8040 *raw_type = RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
8041 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_OUTGOING_NS)) {
8042 if (xml_stream == buffer)
8043 *raw_type = RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE;
8044 else
8045 *raw_type = RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE;
8046 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_DELIVERY_NS)) {
8047 if (xml_stream == buffer)
8048 *raw_type = RAWTYPE_PLAIN_SIGNED_DELIVERYINFO;
8049 else
8050 *raw_type = RAWTYPE_CMS_SIGNED_DELIVERYINFO;
8051 } else if (!xmlStrcmp(root->ns->href, BAD_CAST ISDS_NS)) {
8052 if (xml_stream != buffer) {
8053 isds_printf_message(context,
8054 _("Document in ISDS name space is encapsulated into CMS" ));
8055 err = IE_NOTSUP;
8056 } else if (!xmlStrcmp(root->name, BAD_CAST "MessageDownloadResponse"))
8057 *raw_type = RAWTYPE_INCOMING_MESSAGE;
8058 else if (!xmlStrcmp(root->name, BAD_CAST "GetDeliveryInfoResponse"))
8059 *raw_type = RAWTYPE_DELIVERYINFO;
8060 else {
8061 isds_printf_message(context,
8062 _("Unknown root element in ISDS name space"));
8063 err = IE_NOTSUP;
8065 } else {
8066 isds_printf_message(context,
8067 _("Uknown namespace"));
8068 err = IE_NOTSUP;
8071 leave:
8072 if (xml_stream != buffer) cms_data_free(xml_stream);
8073 xmlFreeDoc(document);
8074 return err;
8078 /* Download signed incoming/outgoing message identified by ID.
8079 * @context is session context
8080 * @output is true for outging message, false for incoming message
8081 * @message_id is message identifier (you can get them from
8082 * isds_get_list_of_{sent,received}_messages())
8083 * @message is automatically reallocated message retrieved from ISDS. The raw
8084 * memeber will be filled with PKCS#7 structure in DER format. */
8085 _hidden isds_error isds_get_signed_message(struct isds_ctx *context,
8086 const _Bool outgoing, const char *message_id,
8087 struct isds_message **message) {
8089 isds_error err = IE_SUCCESS;
8090 xmlDocPtr response = NULL;
8091 xmlChar *code = NULL, *status_message = NULL;
8092 xmlXPathContextPtr xpath_ctx = NULL;
8093 xmlXPathObjectPtr result = NULL;
8094 char *encoded_structure = NULL;
8095 void *raw = NULL;
8096 size_t raw_length = 0;
8098 if (!context) return IE_INVALID_CONTEXT;
8099 zfree(context->long_message);
8100 if (!message) return IE_INVAL;
8101 isds_message_free(message);
8103 /* Do request and check for success */
8104 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
8105 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
8106 BAD_CAST "SignedMessageDownload",
8107 message_id, &response, NULL, NULL, &code, &status_message);
8108 if (err) goto leave;
8110 /* Find signed message, extract it into raw and maybe free
8111 * response */
8112 err = find_extract_signed_data_free_response(context,
8113 (xmlChar *)message_id, &response,
8114 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
8115 BAD_CAST "SignedMessageDownload",
8116 &raw, &raw_length);
8117 if (err) goto leave;
8119 /* Parse message */
8120 err = isds_load_message(context,
8121 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
8122 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
8123 raw, raw_length, message, BUFFER_MOVE);
8124 if (err) goto leave;
8126 raw = NULL;
8128 leave:
8129 if (err) {
8130 isds_message_free(message);
8133 free(encoded_structure);
8134 xmlXPathFreeObject(result);
8135 xmlXPathFreeContext(xpath_ctx);
8136 free(raw);
8138 free(code);
8139 free(status_message);
8140 xmlFreeDoc(response);
8142 if (!err)
8143 isds_log(ILF_ISDS, ILL_DEBUG,
8144 (outgoing) ?
8145 _("SignedSentMessageDownload request processed by server "
8146 "successfully.\n") :
8147 _("SignedMessageDownload request processed by server "
8148 "successfully.\n")
8150 return err;
8154 /* Download signed incoming message identified by ID.
8155 * @context is session context
8156 * @message_id is message identifier (you can get them from
8157 * isds_get_list_of_received_messages())
8158 * @message is automatically reallocated message retrieved from ISDS. The raw
8159 * memeber will be filled with PKCS#7 structure in DER format. */
8160 isds_error isds_get_signed_received_message(struct isds_ctx *context,
8161 const char *message_id, struct isds_message **message) {
8162 return isds_get_signed_message(context, 0, message_id, message);
8166 /* Download signed outgoing message identified by ID.
8167 * @context is session context
8168 * @message_id is message identifier (you can get them from
8169 * isds_get_list_of_sent_messages())
8170 * @message is automatically reallocated message retrieved from ISDS. The raw
8171 * memeber will be filled with PKCS#7 structure in DER format. */
8172 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
8173 const char *message_id, struct isds_message **message) {
8174 return isds_get_signed_message(context, 1, message_id, message);
8178 /* Retrieve hash of message identified by ID stored in ISDS.
8179 * @context is session context
8180 * @message_id is message identifier
8181 * @hash is automatically reallocated message hash downloaded from ISDS.
8182 * Message must exist in system and must not be deleted. */
8183 isds_error isds_download_message_hash(struct isds_ctx *context,
8184 const char *message_id, struct isds_hash **hash) {
8186 isds_error err = IE_SUCCESS;
8187 xmlDocPtr response = NULL;
8188 xmlChar *code = NULL, *status_message = NULL;
8189 xmlXPathContextPtr xpath_ctx = NULL;
8190 xmlXPathObjectPtr result = NULL;
8192 if (!context) return IE_INVALID_CONTEXT;
8193 zfree(context->long_message);
8195 isds_hash_free(hash);
8197 err = build_send_check_message_request(context, SERVICE_DM_INFO,
8198 BAD_CAST "VerifyMessage", message_id,
8199 &response, NULL, NULL, &code, &status_message);
8200 if (err) goto leave;
8203 /* Extract data */
8204 xpath_ctx = xmlXPathNewContext(response);
8205 if (!xpath_ctx) {
8206 err = IE_ERROR;
8207 goto leave;
8209 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8210 err = IE_ERROR;
8211 goto leave;
8213 result = xmlXPathEvalExpression(
8214 BAD_CAST "/isds:VerifyMessageResponse",
8215 xpath_ctx);
8216 if (!result) {
8217 err = IE_ERROR;
8218 goto leave;
8220 /* Empty response */
8221 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8222 char *message_id_locale = utf82locale((char*) message_id);
8223 isds_printf_message(context,
8224 _("Server did not return any response for ID `%s' "
8225 "on VerifyMessage request"), message_id_locale);
8226 free(message_id_locale);
8227 err = IE_ISDS;
8228 goto leave;
8230 /* More responses */
8231 if (result->nodesetval->nodeNr > 1) {
8232 char *message_id_locale = utf82locale((char*) message_id);
8233 isds_printf_message(context,
8234 _("Server did return more responses for ID `%s' "
8235 "on VerifyMessage request"), message_id_locale);
8236 free(message_id_locale);
8237 err = IE_ISDS;
8238 goto leave;
8240 /* One response */
8241 xpath_ctx->node = result->nodesetval->nodeTab[0];
8243 /* Extract the hash */
8244 err = find_and_extract_DmHash(context, hash, xpath_ctx);
8246 leave:
8247 if (err) {
8248 isds_hash_free(hash);
8251 xmlXPathFreeObject(result);
8252 xmlXPathFreeContext(xpath_ctx);
8254 free(code);
8255 free(status_message);
8256 xmlFreeDoc(response);
8258 if (!err)
8259 isds_log(ILF_ISDS, ILL_DEBUG,
8260 _("VerifyMessage request processed by server "
8261 "successfully.\n")
8263 return err;
8267 /* Mark message as read. This is a transactional commit function to acknoledge
8268 * to ISDS the message has been downloaded and processed by client properly.
8269 * @context is session context
8270 * @message_id is message identifier. */
8271 isds_error isds_mark_message_read(struct isds_ctx *context,
8272 const char *message_id) {
8274 isds_error err = IE_SUCCESS;
8275 xmlDocPtr response = NULL;
8276 xmlChar *code = NULL, *status_message = NULL;
8278 if (!context) return IE_INVALID_CONTEXT;
8279 zfree(context->long_message);
8281 /* Do request and check for success */
8282 err = build_send_check_message_request(context, SERVICE_DM_INFO,
8283 BAD_CAST "MarkMessageAsDownloaded", message_id,
8284 &response, NULL, NULL, &code, &status_message);
8286 free(code);
8287 free(status_message);
8288 xmlFreeDoc(response);
8290 if (!err)
8291 isds_log(ILF_ISDS, ILL_DEBUG,
8292 _("MarkMessageAsDownloaded request processed by server "
8293 "successfully.\n")
8295 return err;
8298 /* Mark message as received by recipient. This is applicable only to
8299 * commercial message. There is no specified way how to distinguishe
8300 * commercial message from government message yet. Government message is
8301 * received automatically (by law), commenrcial message on recipient request.
8302 * @context is session context
8303 * @message_id is message identifier. */
8304 isds_error isds_mark_message_received(struct isds_ctx *context,
8305 const char *message_id) {
8307 isds_error err = IE_SUCCESS;
8308 xmlDocPtr response = NULL;
8309 xmlChar *code = NULL, *status_message = NULL;
8311 if (!context) return IE_INVALID_CONTEXT;
8312 zfree(context->long_message);
8314 /* Do request and check for success */
8315 err = build_send_check_message_request(context, SERVICE_DM_INFO,
8316 BAD_CAST "ConfirmDelivery", message_id,
8317 &response, NULL, NULL, &code, &status_message);
8319 free(code);
8320 free(status_message);
8321 xmlFreeDoc(response);
8323 if (!err)
8324 isds_log(ILF_ISDS, ILL_DEBUG,
8325 _("ConfirmDelivery request processed by server "
8326 "successfully.\n")
8328 return err;
8332 /* Send document for authorize conversion into Czech POINT system.
8333 * This is public anonymous service, no login necessary. Special context is
8334 * used to reuse keep-a-live HTTPS connection.
8335 * @context is Czech POINT session context. DO NOT use context connected to
8336 * ISDS server. Use new context or context used by this function previously.
8337 * @document is document to convert. Only data, data_length and dmFileDescr
8338 * memebers are signifact. Be ware that not all document formats can be
8339 * converted (signed PDF 1.3 and higher only (2010-02 state)).
8340 * @id is reallocated identifier assigned by Czech POINT system to
8341 * your document on submit. Use is to tell it to Czech POINT officer.
8342 * @date is reallocated document submit date (submitted documents
8343 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
8344 * value. */
8345 isds_error czp_convert_document(struct isds_ctx *context,
8346 const struct isds_document *document,
8347 char **id, struct tm **date) {
8348 isds_error err = IE_SUCCESS;
8349 xmlNsPtr deposit_ns = NULL, empty_ns = NULL;
8350 xmlNodePtr request = NULL, node;
8351 xmlDocPtr response = NULL;
8352 xmlChar *base64data = NULL;
8354 xmlXPathContextPtr xpath_ctx = NULL;
8355 xmlXPathObjectPtr result = NULL;
8356 long int status = -1;
8357 long int *status_ptr = &status;
8358 char *string = NULL;
8361 if (!context) return IE_INVALID_CONTEXT;
8362 zfree(context->long_message);
8363 if (!document || !id || !date) return IE_INVAL;
8365 /* Free output arguments */
8366 zfree(*id);
8367 zfree(*date);
8369 /* Store configuration */
8370 context->type = CTX_TYPE_CZP;
8371 free(context->url);
8372 context->url = strdup("https://www.czechpoint.cz/uschovna/services.php");
8373 if (!(context->url))
8374 return IE_NOMEM;
8376 /* Prepare CURL handle if not yet connected */
8377 if (!context->curl) {
8378 context->curl = curl_easy_init();
8379 if (!(context->curl))
8380 return IE_ERROR;
8383 /* Build conversion request */
8384 request = xmlNewNode(NULL, BAD_CAST "saveDocument");
8385 if (!request) {
8386 isds_log_message(context,
8387 _("Could not build Czech POINT conversion request"));
8388 return IE_ERROR;
8390 deposit_ns = xmlNewNs(request, BAD_CAST DEPOSIT_NS, BAD_CAST "dep");
8391 if(!deposit_ns) {
8392 isds_log_message(context,
8393 _("Could not create Czech POINT deposit name space"));
8394 xmlFreeNode(request);
8395 return IE_ERROR;
8397 xmlSetNs(request, deposit_ns);
8399 /* Insert childern. They are in empty namespace! */
8400 empty_ns = xmlNewNs(request, BAD_CAST "", NULL);
8401 if(!empty_ns) {
8402 isds_log_message(context, _("Could not create empty name space"));
8403 err = IE_ERROR;
8404 goto leave;
8406 INSERT_STRING_WITH_NS(request, empty_ns, "conversionID", "0");
8407 INSERT_STRING_WITH_NS(request, empty_ns, "fileName",
8408 document->dmFileDescr);
8410 /* Document encoded in Base64 */
8411 base64data = (xmlChar *) b64encode(document->data, document->data_length);
8412 if (!base64data) {
8413 isds_printf_message(context,
8414 ngettext("Not enought memory to encode %zd bytes into Base64",
8415 "Not enought memory to encode %zd bytes into Base64",
8416 document->data_length),
8417 document->data_length);
8418 err = IE_NOMEM;
8419 goto leave;
8421 INSERT_STRING_WITH_NS(request, empty_ns, "document", base64data);
8422 zfree(base64data);
8424 isds_log(ILF_ISDS, ILL_DEBUG,
8425 _("Submitting document for conversion into Czech POINT deposit"));
8427 /* Send conversion request */
8428 err = czpdeposit(context, request, &response);
8429 xmlFreeNode(request); request = NULL;
8431 if (err) {
8432 czp_do_close_connection(context);
8433 goto leave;
8437 /* Extract response */
8438 xpath_ctx = xmlXPathNewContext(response);
8439 if (!xpath_ctx) {
8440 err = IE_ERROR;
8441 goto leave;
8443 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8444 err = IE_ERROR;
8445 goto leave;
8447 result = xmlXPathEvalExpression(
8448 BAD_CAST "/deposit:saveDocumentResponse/return",
8449 xpath_ctx);
8450 if (!result) {
8451 err = IE_ERROR;
8452 goto leave;
8454 /* Empty response */
8455 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8456 isds_printf_message(context,
8457 _("Missing `return' element in Czech POINT deposit response"));
8458 err = IE_ISDS;
8459 goto leave;
8461 /* More responses */
8462 if (result->nodesetval->nodeNr > 1) {
8463 isds_printf_message(context,
8464 _("Multiple `return' element in Czech POINT deposit response"));
8465 err = IE_ISDS;
8466 goto leave;
8468 /* One response */
8469 xpath_ctx->node = result->nodesetval->nodeTab[0];
8471 /* Get status */
8472 EXTRACT_LONGINT("status", status_ptr, 1);
8473 if (status) {
8474 EXTRACT_STRING("statusMsg", string);
8475 char *string_locale = utf82locale(string);
8476 isds_printf_message(context,
8477 _("Czech POINT deposit refused document for conversion "
8478 "(code=%ld, message=%s)"),
8479 status, string_locale);
8480 free(string_locale);
8481 err = IE_ISDS;
8482 goto leave;
8485 /* Get docuement ID */
8486 EXTRACT_STRING("documentID", *id);
8488 /* Get submit date */
8489 EXTRACT_STRING("dateInserted", string);
8490 if (string) {
8491 *date = calloc(1, sizeof(**date));
8492 if (!*date) {
8493 err = IE_NOMEM;
8494 goto leave;
8496 err = datestring2tm((xmlChar *)string, *date);
8497 if (err) {
8498 if (err == IE_NOTSUP) {
8499 err = IE_ISDS;
8500 char *string_locale = utf82locale(string);
8501 isds_printf_message(context,
8502 _("Invalid dateInserted value: %s"), string_locale);
8503 free(string_locale);
8505 goto leave;
8509 leave:
8510 free(string);
8511 xmlXPathFreeObject(result);
8512 xmlXPathFreeContext(xpath_ctx);
8514 xmlFreeDoc(response);
8515 free(base64data);
8516 xmlFreeNode(request);
8518 if (!err) {
8519 char *id_locale = utf82locale((char *) *id);
8520 isds_log(ILF_ISDS, ILL_DEBUG,
8521 _("Document %s has been submitted for conversion "
8522 "to server successfully\n"), id_locale);
8523 free(id_locale);
8525 return err;
8529 /* Close possibly opened connection to Czech POINT document deposit.
8530 * @context is Czech POINT session context. */
8531 isds_error czp_close_connection(struct isds_ctx *context) {
8532 if (!context) return IE_INVALID_CONTEXT;
8533 zfree(context->long_message);
8534 return czp_do_close_connection(context);
8538 /* Send request for new box creation in testing ISDS instance.
8539 * It's not possible to requst for a production box currently, as it
8540 * communicates via e-mail.
8541 * XXX: This function does not work either. Server complains about invalid
8542 * e-mail address.
8543 * XXX: Remove context->type hacks in isds.c and validator.c when removing
8544 * this function
8545 * @context is special session context for box creation request. DO NOT use
8546 * standard context as it could reveal your password. Use fresh new context or
8547 * context previously used by this function.
8548 * @box is box description to create including single primary user (in case of
8549 * FO box type). It outputs box ID assigned by ISDS in dbID element.
8550 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
8551 * box, or contact address of PFO box owner). The email member is mandatory as
8552 * it will be used to deliver credentials.
8553 * @former_names is optional undocumented string. Pass NULL if you don't care.
8554 * @approval is optional external approval of box manipulation
8555 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8556 * NULL, if you don't care.*/
8557 isds_error isds_request_new_testing_box(struct isds_ctx *context,
8558 struct isds_DbOwnerInfo *box, const struct isds_list *users,
8559 const char *former_names, const struct isds_approval *approval,
8560 char **refnumber) {
8561 isds_error err = IE_SUCCESS;
8562 xmlNodePtr request = NULL;
8563 xmlDocPtr response = NULL;
8564 xmlXPathContextPtr xpath_ctx = NULL;
8565 xmlXPathObjectPtr result = NULL;
8568 if (!context) return IE_INVALID_CONTEXT;
8569 zfree(context->long_message);
8570 if (!box) return IE_INVAL;
8572 if (!box->email || box->email[0] == '\0') {
8573 isds_log_message(context, _("E-mail field is mandatory"));
8574 return IE_INVAL;
8577 /* Scratch box ID */
8578 zfree(box->dbID);
8580 /* Store configuration */
8581 context->type = CTX_TYPE_TESTING_REQUEST_COLLECTOR;
8582 free(context->url);
8583 context->url = strdup("http://78.102.19.203/testbox/request_box.php");
8584 if (!(context->url))
8585 return IE_NOMEM;
8587 /* Prepare CURL handle if not yet connected */
8588 if (!context->curl) {
8589 context->curl = curl_easy_init();
8590 if (!(context->curl))
8591 return IE_ERROR;
8594 /* Build CreateDataBox request */
8595 err = build_CreateDBInput_request(context,
8596 &request, BAD_CAST "CreateDataBox",
8597 box, users, (xmlChar *) former_names, NULL, NULL, approval);
8598 if (err) goto leave;
8600 /* Send it to server and process response */
8601 err = send_destroy_request_check_response(context,
8602 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
8603 &response, (xmlChar **) refnumber);
8605 /* Extract box ID */
8606 xpath_ctx = xmlXPathNewContext(response);
8607 if (!xpath_ctx) {
8608 err = IE_ERROR;
8609 goto leave;
8611 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8612 err = IE_ERROR;
8613 goto leave;
8615 EXTRACT_STRING("/isds:CreateDataBoxResponse/dbID", box->dbID);
8617 leave:
8618 xmlXPathFreeObject(result);
8619 xmlXPathFreeContext(xpath_ctx);
8620 xmlFreeDoc(response);
8621 xmlFreeNode(request);
8623 if (!err) {
8624 isds_log(ILF_ISDS, ILL_DEBUG,
8625 _("CreateDataBox request processed by server successfully.\n"));
8628 return err;
8631 #undef INSERT_ELEMENT
8632 #undef CHECK_FOR_STRING_LENGTH
8633 #undef INSERT_STRING_ATTRIBUTE
8634 #undef INSERT_ULONGINTNOPTR
8635 #undef INSERT_ULONGINT
8636 #undef INSERT_LONGINT
8637 #undef INSERT_BOOLEAN
8638 #undef INSERT_SCALAR_BOOLEAN
8639 #undef INSERT_STRING
8640 #undef INSERT_STRING_WITH_NS
8641 #undef EXTRACT_STRING_ATTRIBUTE
8642 #undef EXTRACT_ULONGINT
8643 #undef EXTRACT_LONGINT
8644 #undef EXTRACT_BOOLEAN
8645 #undef EXTRACT_STRING
8648 /* Compute hash of message from raw representation and store it into envelope.
8649 * Original hash structure will be destroyed in envelope.
8650 * @context is session context
8651 * @message is message carrying raw XML message blob
8652 * @algorithm is desired hash algorithm to use */
8653 isds_error isds_compute_message_hash(struct isds_ctx *context,
8654 struct isds_message *message, const isds_hash_algorithm algorithm) {
8655 isds_error err = IE_SUCCESS;
8656 const char *nsuri;
8657 void *xml_stream = NULL;
8658 size_t xml_stream_length;
8659 size_t phys_start, phys_end;
8660 char *phys_path = NULL;
8661 struct isds_hash *new_hash = NULL;
8664 if (!context) return IE_INVALID_CONTEXT;
8665 zfree(context->long_message);
8666 if (!message) return IE_INVAL;
8668 if (!message->raw) {
8669 isds_log_message(context,
8670 _("Message does not carry raw representation"));
8671 return IE_INVAL;
8674 switch (message->raw_type) {
8675 case RAWTYPE_INCOMING_MESSAGE:
8676 nsuri = ISDS_NS;
8677 xml_stream = message->raw;
8678 xml_stream_length = message->raw_length;
8679 break;
8681 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
8682 nsuri = SISDS_INCOMING_NS;
8683 xml_stream = message->raw;
8684 xml_stream_length = message->raw_length;
8685 break;
8687 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
8688 nsuri = SISDS_INCOMING_NS;
8689 err = extract_cms_data(context, message->raw, message->raw_length,
8690 &xml_stream, &xml_stream_length);
8691 if (err) goto leave;
8692 break;
8694 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
8695 nsuri = SISDS_OUTGOING_NS;
8696 xml_stream = message->raw;
8697 xml_stream_length = message->raw_length;
8698 break;
8700 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
8701 nsuri = SISDS_OUTGOING_NS;
8702 err = extract_cms_data(context, message->raw, message->raw_length,
8703 &xml_stream, &xml_stream_length);
8704 if (err) goto leave;
8705 break;
8707 default:
8708 isds_log_message(context, _("Bad raw representation type"));
8709 return IE_INVAL;
8710 break;
8714 /* XXX: Hash is computed from original string represinting isds:dmDm
8715 * subtree. That means no encoding, white space, xmlns attributes changes.
8716 * In other words, input for hash can be invalid XML stream. */
8717 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
8718 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
8719 PHYSXML_ELEMENT_SEPARATOR,
8720 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
8721 PHYSXML_ELEMENT_SEPARATOR
8722 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
8723 err = IE_NOMEM;
8724 goto leave;
8726 err = find_element_boundary(xml_stream, xml_stream_length,
8727 phys_path, &phys_start, &phys_end);
8728 zfree(phys_path);
8729 if (err) {
8730 isds_log_message(context,
8731 _("Substring with isds:dmDM element could not be located "
8732 "in raw message"));
8733 goto leave;
8737 /* Compute hash */
8738 new_hash = calloc(1, sizeof(*new_hash));
8739 if (!new_hash) {
8740 err = IE_NOMEM;
8741 goto leave;
8743 new_hash->algorithm = algorithm;
8744 err = compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
8745 new_hash);
8746 if (err) {
8747 isds_log_message(context, _("Could not compute message hash"));
8748 goto leave;
8751 /* Save computed hash */
8752 if (!message->envelope) {
8753 message->envelope = calloc(1, sizeof(*message->envelope));
8754 if (!message->envelope) {
8755 err = IE_NOMEM;
8756 goto leave;
8759 isds_hash_free(&message->envelope->hash);
8760 message->envelope->hash = new_hash;
8762 leave:
8763 if (err) {
8764 isds_hash_free(&new_hash);
8767 free(phys_path);
8768 if (xml_stream != message->raw) free(xml_stream);
8769 return err;
8773 /* Compare two hashes.
8774 * @h1 is first hash
8775 * @h2 is another hash
8776 * @return
8777 * IE_SUCCESS if hashes equal
8778 * IE_NOTUNIQ if hashes are comparable, but they don't equal
8779 * IE_ENUM if not comparable, but both structures defined
8780 * IE_INVAL if some of the structures are undefined (NULL)
8781 * IE_ERROR if internal error occurs */
8782 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
8783 if (h1 == NULL || h2 == NULL) return IE_INVAL;
8784 if (h1->algorithm != h2->algorithm) return IE_ENUM;
8785 if (h1->length != h2->length) return IE_ERROR;
8786 if (h1->length > 0 && !h1->value) return IE_ERROR;
8787 if (h2->length > 0 && !h2->value) return IE_ERROR;
8789 for (int i = 0; i < h1->length; i++) {
8790 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
8791 return IE_NOTEQUAL;
8793 return IE_SUCCESS;
8797 /* Check message has gone through ISDS by comparing message hash stored in
8798 * ISDS and locally computed hash. You must provide message with valid raw
8799 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
8800 * This is convenient wrapper for isds_download_message_hash(),
8801 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
8802 * @context is session context
8803 * @message is message with valid raw and envelope member; envelope->hash
8804 * member will be changed during funcion run. Use envelope on heap only.
8805 * @return
8806 * IE_SUCCESS if message originates in ISDS
8807 * IE_NOTEQUAL if message is unknown to ISDS
8808 * other code for other errors */
8809 isds_error isds_verify_message_hash(struct isds_ctx *context,
8810 struct isds_message *message) {
8811 isds_error err = IE_SUCCESS;
8812 struct isds_hash *downloaded_hash = NULL;
8814 if (!context) return IE_INVALID_CONTEXT;
8815 zfree(context->long_message);
8816 if (!message) return IE_INVAL;
8818 if (!message->envelope) {
8819 isds_log_message(context,
8820 _("Given message structure is missing envelope"));
8821 return IE_INVAL;
8823 if (!message->raw) {
8824 isds_log_message(context,
8825 _("Given message structure is missing raw representation"));
8826 return IE_INVAL;
8829 err = isds_download_message_hash(context, message->envelope->dmID,
8830 &downloaded_hash);
8831 if (err) goto leave;
8833 err = isds_compute_message_hash(context, message,
8834 downloaded_hash->algorithm);
8835 if (err) goto leave;
8837 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
8839 leave:
8840 isds_hash_free(&downloaded_hash);
8841 return err;
8845 /* Search for document by document ID in list of documents. IDs are compared
8846 * as UTF-8 string.
8847 * @documents is list of isds_documents
8848 * @id is document identifier
8849 * @return first matching document or NULL. */
8850 const struct isds_document *isds_find_document_by_id(
8851 const struct isds_list *documents, const char *id) {
8852 const struct isds_list *item;
8853 const struct isds_document *document;
8855 for (item = documents; item; item = item->next) {
8856 document = (struct isds_document *) item->data;
8857 if (!document) continue;
8859 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
8860 return document;
8863 return NULL;
8867 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
8868 struct isds_message **message);
8869 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
8870 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
8871 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
8872 struct isds_address **address);
8874 int isds_message_free(struct isds_message **message);
8875 int isds_address_free(struct isds_address **address);
8879 /* Makes known all relevant namespaces to given XPath context
8880 * @xpat_ctx is XPath context
8881 * @message_ns selects propper message name space. Unsisnged and signed
8882 * messages differs.
8883 * prefix and to URI ISDS_NS */
8884 _hidden isds_error register_namespaces(xmlXPathContextPtr xpath_ctx,
8885 const message_ns_type message_ns) {
8886 const xmlChar *message_namespace = NULL;
8888 if (!xpath_ctx) return IE_ERROR;
8890 switch(message_ns) {
8891 case MESSAGE_NS_1:
8892 message_namespace = BAD_CAST ISDS1_NS; break;
8893 case MESSAGE_NS_UNSIGNED:
8894 message_namespace = BAD_CAST ISDS_NS; break;
8895 case MESSAGE_NS_SIGNED_INCOMING:
8896 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
8897 case MESSAGE_NS_SIGNED_OUTGOING:
8898 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
8899 case MESSAGE_NS_SIGNED_DELIVERY:
8900 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
8901 default:
8902 return IE_ENUM;
8905 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
8906 return IE_ERROR;
8907 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
8908 return IE_ERROR;
8909 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
8910 return IE_ERROR;
8911 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
8912 return IE_ERROR;
8913 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
8914 return IE_ERROR;
8915 return IE_SUCCESS;