i18n: Fix message typos
[libisds.git] / src / isds.c
blobd8bc5593b8c9a7592b22b8400b80d23d2fc1ebc9
1 #define _XOPEN_SOURCE 500 /* strdup from string.h, strptime from time.h */
2 #include <stdlib.h>
3 #include <string.h>
4 #include <stdio.h>
5 #include <stdarg.h>
6 #include <ctype.h>
7 #include <stdint.h>
8 #include "isds_priv.h"
9 #include "utils.h"
10 #include "soap.h"
11 #include "validator.h"
12 #include "crypto.h"
13 #include <gpg-error.h> /* Because of ksba or gpgme */
14 #include "physxml.h"
17 /* Free isds_list with all member data.
18 * @list list to free, on return will be NULL */
19 void isds_list_free(struct isds_list **list) {
20 struct isds_list *item, *next_item;
22 if (!list || !*list) return;
24 for(item = *list; item; item = next_item) {
25 (item->destructor)(&(item->data));
26 next_item = item->next;
27 free(item);
30 *list = NULL;
34 /* Deallocate structure isds_hash and NULL it.
35 * @hash hash to to free */
36 void isds_hash_free(struct isds_hash **hash) {
37 if(!hash || !*hash) return;
38 free((*hash)->value);
39 zfree((*hash));
43 /* Deallocate structure isds_PersonName recursively and NULL it */
44 static void isds_PersonName_free(struct isds_PersonName **person_name) {
45 if (!person_name || !*person_name) return;
47 free((*person_name)->pnFirstName);
48 free((*person_name)->pnMiddleName);
49 free((*person_name)->pnLastName);
50 free((*person_name)->pnLastNameAtBirth);
52 free(*person_name);
53 *person_name = NULL;
57 /* Deallocate structure isds_BirthInfo recursively and NULL it */
58 static void isds_BirthInfo_free(struct isds_BirthInfo **birth_info) {
59 if (!birth_info || !*birth_info) return;
61 free((*birth_info)->biDate);
62 free((*birth_info)->biCity);
63 free((*birth_info)->biCounty);
64 free((*birth_info)->biState);
66 free(*birth_info);
67 *birth_info = NULL;
71 /* Deallocate structure isds_Address recursively and NULL it */
72 static void isds_Address_free(struct isds_Address **address) {
73 if (!address || !*address) return;
75 free((*address)->adCity);
76 free((*address)->adStreet);
77 free((*address)->adNumberInStreet);
78 free((*address)->adNumberInMunicipality);
79 free((*address)->adZipCode);
80 free((*address)->adState);
82 free(*address);
83 *address = NULL;
87 /* Deallocate structure isds_DbOwnerInfo recursively and NULL it */
88 void isds_DbOwnerInfo_free(struct isds_DbOwnerInfo **db_owner_info) {
89 if (!db_owner_info || !*db_owner_info) return;
91 free((*db_owner_info)->dbID);
92 free((*db_owner_info)->dbType);
93 free((*db_owner_info)->ic);
94 isds_PersonName_free(&((*db_owner_info)->personName));
95 free((*db_owner_info)->firmName);
96 isds_BirthInfo_free(&((*db_owner_info)->birthInfo));
97 isds_Address_free(&((*db_owner_info)->address));
98 free((*db_owner_info)->nationality);
99 free((*db_owner_info)->email);
100 free((*db_owner_info)->telNumber);
101 free((*db_owner_info)->identifier);
102 free((*db_owner_info)->registryCode);
103 free((*db_owner_info)->dbState);
104 free((*db_owner_info)->dbEffectiveOVM);
106 free(*db_owner_info);
107 *db_owner_info = NULL;
110 /* Deallocate structure isds_DbUserInfo recursively and NULL it */
111 void isds_DbUserInfo_free(struct isds_DbUserInfo **db_user_info) {
112 if (!db_user_info || !*db_user_info) return;
114 free((*db_user_info)->userID);
115 free((*db_user_info)->userType);
116 free((*db_user_info)->userPrivils);
117 isds_PersonName_free(&((*db_user_info)->personName));
118 isds_Address_free(&((*db_user_info)->address));
119 free((*db_user_info)->biDate);
120 free((*db_user_info)->ic);
121 free((*db_user_info)->firmName);
122 free((*db_user_info)->caStreet);
123 free((*db_user_info)->caCity);
124 free((*db_user_info)->caZipCode);
126 zfree(*db_user_info);
130 /* Deallocate struct isds_event recursively and NULL it */
131 void isds_event_free(struct isds_event **event) {
132 if (!event || !*event) return;
134 free((*event)->time);
135 free((*event)->type);
136 free((*event)->description);
137 zfree(*event);
141 /* Deallocate struct isds_envelope recursively and NULL it */
142 void isds_envelope_free(struct isds_envelope **envelope) {
143 if (!envelope || !*envelope) return;
145 free((*envelope)->dmID);
146 free((*envelope)->dbIDSender);
147 free((*envelope)->dmSender);
148 free((*envelope)->dmSenderAddress);
149 free((*envelope)->dmSenderType);
150 free((*envelope)->dmRecipient);
151 free((*envelope)->dmRecipientAddress);
152 free((*envelope)->dmAmbiguousRecipient);
153 free((*envelope)->dmType);
155 free((*envelope)->dmOrdinal);
156 free((*envelope)->dmMessageStatus);
157 free((*envelope)->dmDeliveryTime);
158 free((*envelope)->dmAcceptanceTime);
159 isds_hash_free(&(*envelope)->hash);
160 free((*envelope)->timestamp);
161 isds_list_free(&(*envelope)->events);
163 free((*envelope)->dmSenderOrgUnit);
164 free((*envelope)->dmSenderOrgUnitNum);
165 free((*envelope)->dbIDRecipient);
166 free((*envelope)->dmRecipientOrgUnit);
167 free((*envelope)->dmRecipientOrgUnitNum);
168 free((*envelope)->dmToHands);
169 free((*envelope)->dmAnnotation);
170 free((*envelope)->dmRecipientRefNumber);
171 free((*envelope)->dmSenderRefNumber);
172 free((*envelope)->dmRecipientIdent);
173 free((*envelope)->dmSenderIdent);
175 free((*envelope)->dmLegalTitleLaw);
176 free((*envelope)->dmLegalTitleYear);
177 free((*envelope)->dmLegalTitleSect);
178 free((*envelope)->dmLegalTitlePar);
179 free((*envelope)->dmLegalTitlePoint);
181 free((*envelope)->dmPersonalDelivery);
182 free((*envelope)->dmAllowSubstDelivery);
183 free((*envelope)->dmOVM);
185 free(*envelope);
186 *envelope = NULL;
190 /* Deallocate struct isds_message recursively and NULL it */
191 void isds_message_free(struct isds_message **message) {
192 if (!message || !*message) return;
194 free((*message)->raw);
195 isds_envelope_free(&((*message)->envelope));
196 isds_list_free(&((*message)->documents));
198 free(*message);
199 *message = NULL;
203 /* Deallocate struct isds_document recursively and NULL it */
204 void isds_document_free(struct isds_document **document) {
205 if (!document || !*document) return;
207 free((*document)->data);
208 free((*document)->dmMimeType);
209 free((*document)->dmFileGuid);
210 free((*document)->dmUpFileGuid);
211 free((*document)->dmFileDescr);
212 free((*document)->dmFormat);
214 free(*document);
215 *document = NULL;
219 /* Deallocate struct isds_message_copy recursively and NULL it */
220 void isds_message_copy_free(struct isds_message_copy **copy) {
221 if (!copy || !*copy) return;
223 free((*copy)->dbIDRecipient);
224 free((*copy)->dmRecipientOrgUnit);
225 free((*copy)->dmRecipientOrgUnitNum);
226 free((*copy)->dmToHands);
228 free((*copy)->dmStatus);
229 free((*copy)->dmID);
231 zfree(*copy);
235 /* Deallocate struct isds_approval recursively and NULL it */
236 void isds_approval_free(struct isds_approval **approval) {
237 if (!approval || !*approval) return;
239 free((*approval)->refference);
241 zfree(*approval);
245 /* *DUP_OR_ERROR macros needs error label */
246 #define STRDUP_OR_ERROR(new, template) { \
247 if (!template) { \
248 (new) = NULL; \
249 } else { \
250 (new) = strdup(template); \
251 if (!new) goto error; \
255 #define FLATDUP_OR_ERROR(new, template) { \
256 if (!template) { \
257 (new) = NULL; \
258 } else { \
259 (new) = malloc(sizeof(*(new))); \
260 if (!new) goto error; \
261 memcpy((new), (template), sizeof(*(template))); \
266 /* Copy structure isds_PersonName recursively */
267 struct isds_PersonName *isds_PersonName_duplicate(
268 const struct isds_PersonName *template) {
269 struct isds_PersonName *new = NULL;
271 if (!template) return NULL;
273 new = calloc(1, sizeof(*new));
274 if (!new) return NULL;
276 STRDUP_OR_ERROR(new->pnFirstName, template->pnFirstName);
277 STRDUP_OR_ERROR(new->pnMiddleName, template->pnMiddleName);
278 STRDUP_OR_ERROR(new->pnLastName, template->pnLastName);
279 STRDUP_OR_ERROR(new->pnLastNameAtBirth, template->pnLastNameAtBirth);
281 return new;
283 error:
284 isds_PersonName_free(&new);
285 return NULL;
289 /* Copy structure isds_BirthInfo recursively */
290 static struct isds_BirthInfo *isds_BirthInfo_duplicate(
291 const struct isds_BirthInfo *template) {
292 struct isds_BirthInfo *new = NULL;
294 if (!template) return NULL;
296 new = calloc(1, sizeof(*new));
297 if (!new) return NULL;
299 FLATDUP_OR_ERROR(new->biDate, template->biDate);
300 STRDUP_OR_ERROR(new->biCity, template->biCity);
301 STRDUP_OR_ERROR(new->biCounty, template->biCounty);
302 STRDUP_OR_ERROR(new->biState, template->biState);
304 return new;
306 error:
307 isds_BirthInfo_free(&new);
308 return NULL;
312 /* Copy structure isds_Address recursively */
313 struct isds_Address *isds_Address_duplicate(
314 const struct isds_Address *template) {
315 struct isds_Address *new = NULL;
317 if (!template) return NULL;
319 new = calloc(1, sizeof(*new));
320 if (!new) return NULL;
322 STRDUP_OR_ERROR(new->adCity, template->adCity);
323 STRDUP_OR_ERROR(new->adStreet, template->adStreet);
324 STRDUP_OR_ERROR(new->adNumberInStreet, template->adNumberInStreet);
325 STRDUP_OR_ERROR(new->adNumberInMunicipality,
326 template->adNumberInMunicipality);
327 STRDUP_OR_ERROR(new->adZipCode, template->adZipCode);
328 STRDUP_OR_ERROR(new->adState, template->adState);
330 return new;
332 error:
333 isds_Address_free(&new);
334 return NULL;
338 /* Copy structure isds_DbOwnerInfo recursively */
339 struct isds_DbOwnerInfo *isds_DbOwnerInfo_duplicate(
340 const struct isds_DbOwnerInfo *template) {
341 struct isds_DbOwnerInfo *new = NULL;
342 if (!template) return NULL;
344 new = calloc(1, sizeof(*new));
345 if (!new) return NULL;
347 STRDUP_OR_ERROR(new->dbID, template->dbID);
348 FLATDUP_OR_ERROR(new->dbType, template->dbType);
349 STRDUP_OR_ERROR(new->ic, template->ic);
351 if (template->personName) {
352 if (!(new->personName =
353 isds_PersonName_duplicate(template->personName)))
354 goto error;
357 STRDUP_OR_ERROR(new->firmName, template->firmName);
359 if (template->birthInfo) {
360 if (!(new->birthInfo =
361 isds_BirthInfo_duplicate(template->birthInfo)))
362 goto error;
365 if (template->address) {
366 if (!(new->address = isds_Address_duplicate(template->address)))
367 goto error;
370 STRDUP_OR_ERROR(new->nationality, template->nationality);
371 STRDUP_OR_ERROR(new->email, template->email);
372 STRDUP_OR_ERROR(new->telNumber, template->telNumber);
373 STRDUP_OR_ERROR(new->identifier, template->identifier);
374 STRDUP_OR_ERROR(new->registryCode, template->registryCode);
375 FLATDUP_OR_ERROR(new->dbState, template->dbState);
376 FLATDUP_OR_ERROR(new->dbEffectiveOVM, template->dbEffectiveOVM);
377 FLATDUP_OR_ERROR(new->dbOpenAddressing, template->dbOpenAddressing);
379 return new;
381 error:
382 isds_DbOwnerInfo_free(&new);
383 return NULL;
387 /* Copy structure isds_DbUserInfo recursively */
388 struct isds_DbUserInfo *isds_DbUserInfo_duplicate(
389 const struct isds_DbUserInfo *template) {
390 struct isds_DbUserInfo *new = NULL;
391 if (!template) return NULL;
393 new = calloc(1, sizeof(*new));
394 if (!new) return NULL;
396 STRDUP_OR_ERROR(new->userID, template->userID);
397 FLATDUP_OR_ERROR(new->userType, template->userType);
398 FLATDUP_OR_ERROR(new->userPrivils, template->userPrivils);
400 if (template->personName) {
401 if (!(new->personName =
402 isds_PersonName_duplicate(template->personName)))
403 goto error;
406 if (template->address) {
407 if (!(new->address = isds_Address_duplicate(template->address)))
408 goto error;
411 FLATDUP_OR_ERROR(new->biDate, template->biDate);
412 STRDUP_OR_ERROR(new->ic, template->ic);
413 STRDUP_OR_ERROR(new->firmName, template->firmName);
414 STRDUP_OR_ERROR(new->caStreet, template->caStreet);
415 STRDUP_OR_ERROR(new->caCity, template->caCity);
416 STRDUP_OR_ERROR(new->caZipCode, template->caZipCode);
418 return new;
420 error:
421 isds_DbUserInfo_free(&new);
422 return NULL;
425 #undef FLATDUP_OR_ERROR
426 #undef STRDUP_OR_ERROR
429 /* Initialize ISDS library.
430 * Global function, must be called before other functions.
431 * If it failes you can not use ISDS library and must call isds_cleanup() to
432 * free partially inititialized global variables. */
433 isds_error isds_init(void) {
434 /* NULL global variables */
435 log_facilities = ILF_ALL;
436 log_level = ILL_WARNING;
438 #if ENABLE_NLS
439 /* Initialize gettext */
440 bindtextdomain(PACKAGE, LOCALEDIR);
441 #endif
443 /* Initialize CURL */
444 if (curl_global_init(CURL_GLOBAL_ALL)) {
445 isds_log(ILF_ISDS, ILL_CRIT, _("CURL library initialization failed\n"));
446 return IE_ERROR;
449 /* Inicialize gpg-error because of gpgme and ksba */
450 if (gpg_err_init()) {
451 isds_log(ILF_ISDS, ILL_CRIT,
452 _("gpg-error library initialization failed\n"));
453 return IE_ERROR;
456 /* Initialize GPGME */
457 if (init_gpgme()) {
458 isds_log(ILF_ISDS, ILL_CRIT,
459 _("GPGME library initialization failed\n"));
460 return IE_ERROR;
463 /* Initialize gcrypt */
464 if (init_gcrypt()) {
465 isds_log(ILF_ISDS, ILL_CRIT,
466 _("gcrypt library initialization failed\n"));
467 return IE_ERROR;
470 /* This can _exit() current program. Find not so assertive check. */
471 LIBXML_TEST_VERSION;
473 /* Check expat */
474 if (init_expat()) {
475 isds_log(ILF_ISDS, ILL_CRIT,
476 _("expat library initialization failed\n"));
477 return IE_ERROR;
480 /* Allocate global variables */
483 return IE_SUCCESS;
487 /* Deinicialize ISDS library.
488 * Global function, must be called as last library function. */
489 isds_error isds_cleanup(void) {
490 /* XML */
491 xmlCleanupParser();
493 /* Curl */
494 curl_global_cleanup();
496 return IE_SUCCESS;
500 /* Return text description of ISDS error */
501 const char *isds_strerror(const isds_error error) {
502 switch (error) {
503 case IE_SUCCESS:
504 return(_("Success")); break;
505 case IE_ERROR:
506 return(_("Unspecified error")); break;
507 case IE_NOTSUP:
508 return(_("Not supported")); break;
509 case IE_INVAL:
510 return(_("Invalid value")); break;
511 case IE_INVALID_CONTEXT:
512 return(_("Invalid context")); break;
513 case IE_NOT_LOGGED_IN:
514 return(_("Not logged in")); break;
515 case IE_CONNECTION_CLOSED:
516 return(_("Connection closed")); break;
517 case IE_TIMED_OUT:
518 return(_("Timed out")); break;
519 case IE_NOEXIST:
520 return(_("Not exist")); break;
521 case IE_NOMEM:
522 return(_("Out of memory")); break;
523 case IE_NETWORK:
524 return(_("Network problem")); break;
525 case IE_HTTP:
526 return(_("HTTP problem")); break;
527 case IE_SOAP:
528 return(_("SOAP problem")); break;
529 case IE_XML:
530 return(_("XML problem")); break;
531 case IE_ISDS:
532 return(_("ISDS server problem")); break;
533 case IE_ENUM:
534 return(_("Invalid enum value")); break;
535 case IE_DATE:
536 return(_("Invalid date value")); break;
537 case IE_2BIG:
538 return(_("Too big")); break;
539 case IE_2SMALL:
540 return(_("Too small")); break;
541 case IE_NOTUNIQ:
542 return(_("Value not unique")); break;
543 case IE_NOTEQUAL:
544 return(_("Values not uqual")); break;
545 case IE_PARTIAL_SUCCESS:
546 return(_("Some suboperations failed")); break;
547 default:
548 return(_("Unknown error"));
553 /* Create ISDS context.
554 * Each context can be used for different sessions to (possibly) differnet
555 * ISDS server with different credentials. */
556 struct isds_ctx *isds_ctx_create(void) {
557 struct isds_ctx *context;
558 context = malloc(sizeof(*context));
559 if (context) memset(context, 0, sizeof(*context));
560 return context;
564 /* Destroy ISDS context and free memmory.
565 * @context will be NULLed on success. */
566 isds_error isds_ctx_free(struct isds_ctx **context) {
567 if (!context || !*context) {
568 return IE_INVALID_CONTEXT;
571 /* Discard credentials */
572 isds_logout(*context);
574 /* Free other structures */
575 free((*context)->tls_verify_server);
576 free((*context)->tls_ca_file);
577 free((*context)->tls_ca_dir);
578 free((*context)->long_message);
580 free(*context);
581 *context = NULL;
582 return IE_SUCCESS;
586 /* Return long message text produced by library fucntion, e.g. detailed error
587 * mesage. Returned pointer is only valid until new library function is
588 * called for the same context. Could be NULL, especially if NULL context is
589 * supplied. Return string is locale encoded. */
590 char *isds_long_message(const struct isds_ctx *context) {
591 if (!context) return NULL;
592 return context->long_message;
596 /* Stores message into context' long_message buffer.
597 * Application can pick the message up using isds_long_message().
598 * NULL @message truncates the buffer but does not deallocate it.
599 * @message is coded in locale encoding */
600 _hidden isds_error isds_log_message(struct isds_ctx *context,
601 const char *message) {
602 char *buffer;
603 size_t length;
605 if (!context) return IE_INVALID_CONTEXT;
607 /* FIXME: Check for integer overflow */
608 length = 1 + ((message) ? strlen(message) : 0);
609 buffer = realloc(context->long_message, length);
610 if (!buffer) return IE_NOMEM;
612 if (message)
613 strcpy(buffer, message);
614 else
615 *buffer = '\0';
617 context->long_message = buffer;
618 return IE_SUCCESS;
622 /* Appends message into context' long_message buffer.
623 * Application can pick the message up using isds_long_message().
624 * NULL message has void effect. */
625 _hidden isds_error isds_append_message(struct isds_ctx *context,
626 const char *message) {
627 char *buffer;
628 size_t old_length, length;
630 if (!context) return IE_INVALID_CONTEXT;
631 if (!message) return IE_SUCCESS;
632 if (!context->long_message)
633 return isds_log_message(context, message);
635 old_length = strlen(context->long_message);
636 /* FIXME: Check for integer overflow */
637 length = 1 + old_length + strlen(message);
638 buffer = realloc(context->long_message, length);
639 if (!buffer) return IE_NOMEM;
641 strcpy(buffer + old_length, message);
643 context->long_message = buffer;
644 return IE_SUCCESS;
648 /* Stores formated message into context' long_message buffer.
649 * Application can pick the message up using isds_long_message(). */
650 _hidden isds_error isds_printf_message(struct isds_ctx *context,
651 const char *format, ...) {
652 va_list ap;
653 int length;
655 if (!context) return IE_INVALID_CONTEXT;
656 va_start(ap, format);
657 length = isds_vasprintf(&(context->long_message), format, ap);
658 va_end(ap);
660 return (length < 0) ? IE_ERROR: IE_SUCCESS;
664 /* Set logging up.
665 * @facilities is bitmask of isds_log_facility values,
666 * @level is verbosity level. */
667 void isds_set_logging(const unsigned int facilities,
668 const isds_log_level level) {
669 log_facilities = facilities;
670 log_level = level;
674 /* Log @message in class @facility with log @level into global log. @message
675 * is printf(3) formating string, variadic arguments may be neccessary.
676 * For debugging purposes. */
677 _hidden isds_error isds_log(const isds_log_facility facility,
678 const isds_log_level level, const char *message, ...) {
679 va_list ap;
681 if (level > log_level) return IE_SUCCESS;
682 if (!(log_facilities & facility)) return IE_SUCCESS;
683 if (!message) return IE_INVAL;
685 /* TODO: Allow to register output function provided by application
686 * (e.g. fprintf to stderr or copy to text area GUI widget). */
688 va_start(ap, message);
689 vfprintf(stderr, message, ap);
690 va_end(ap);
691 /* Line buffered printf is default.
692 * fflush(stderr);*/
694 return IE_SUCCESS;
698 /* Set timeout in miliseconds for each network job like connecting to server
699 * or sending message. Use 0 to disable timeout limits. */
700 isds_error isds_set_timeout(struct isds_ctx *context,
701 const unsigned int timeout) {
702 if (!context) return IE_INVALID_CONTEXT;
704 context->timeout = timeout;
706 if (context->curl) {
707 CURLcode curl_err;
709 curl_err = curl_easy_setopt(context->curl, CURLOPT_NOSIGNAL, 1);
710 if (!curl_err)
711 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT_MS,
712 context->timeout);
713 if (curl_err) return IE_ERROR;
716 return IE_SUCCESS;
720 /* Register callback function libisds calls periodocally during HTTP data
721 * transfer.
722 * @context is session context
723 * @callback is function provided by application libsds will call. See type
724 * defition for @callback argument explanation.
725 * @data is application specific data @callback gets as last argument */
726 isds_error isds_set_progress_callback(struct isds_ctx *context,
727 isds_progress_callback callback, void *data) {
728 if (!context) return IE_INVALID_CONTEXT;
730 context->progress_callback = callback;
731 context->progress_callback_data = data;
733 return IE_SUCCESS;
737 /* Change SSL/TLS settings.
738 * @context is context which setting will be applied to
739 * @option is name of option. It determines the type of last argument. See
740 * isds_tls_option definition for more info.
741 * @... is value of new setting. Type is determined by @option
742 * */
743 isds_error isds_set_tls(struct isds_ctx *context, const isds_tls_option option,
744 ...) {
745 isds_error err = IE_SUCCESS;
746 va_list ap;
747 char *pointer, *string;
749 if (!context) return IE_INVALID_CONTEXT;
751 va_start(ap, option);
753 #define REPLACE_VA_STRING(destination) \
754 string = va_arg(ap, char *); \
755 if (string) { \
756 pointer = realloc((destination), 1 + strlen(string)); \
757 if (!pointer) { err = IE_NOMEM; goto leave; } \
758 strcpy(pointer, string); \
759 (destination) = pointer; \
760 } else { \
761 free(destination); \
762 (destination) = NULL; \
765 switch (option) {
766 case ITLS_VERIFY_SERVER:
767 if (!context->tls_verify_server) {
768 context->tls_verify_server =
769 malloc(sizeof(*context->tls_verify_server));
770 if (!context->tls_verify_server) {
771 err = IE_NOMEM; goto leave;
774 *context->tls_verify_server = (_Bool) (0 != va_arg(ap, int));
775 break;
777 case ITLS_CA_FILE:
778 REPLACE_VA_STRING(context->tls_ca_file);
779 break;
780 case ITLS_CA_DIRECTORY:
781 REPLACE_VA_STRING(context->tls_ca_dir);
782 break;
784 default:
785 err = IE_ENUM; goto leave;
788 #undef REPLACE_VA_STRING
790 leave:
791 va_end(ap);
792 return err;
796 /* Discard credentials.
797 * Only that. It does not cause log out, connection close or similar. */
798 static isds_error discard_credentials(struct isds_ctx *context) {
799 if(!context) return IE_INVALID_CONTEXT;
801 if (context->username) {
802 memset(context->username, 0, strlen(context->username));
803 free(context->username);
804 context->username = NULL;
806 if (context->password) {
807 memset(context->password, 0, strlen(context->password));
808 free(context->password);
809 context->password = NULL;
812 return IE_SUCCESS;
816 /* Connect and log in into ISDS server.
817 * @url is address of ISDS web service
818 * @username is user name of ISDS user
819 * @password is user's secret password
820 * @certificate is NULL terminated string with PEM formated client's
821 * certificate. Use NULL if only password autentication should be performed.
822 * @key is private key for client's certificate as (base64 encoded?) NULL
823 * terminated string. Use NULL if only password autentication is desired.
824 * */
825 isds_error isds_login(struct isds_ctx *context, const char *url,
826 const char *username, const char *password,
827 const char *certificate, const char* key) {
828 isds_error err = IE_NOT_LOGGED_IN;
829 isds_error soap_err;
830 xmlNsPtr isds_ns = NULL;
831 xmlNodePtr request = NULL;
832 xmlNodePtr response = NULL;
834 if (!context) return IE_INVALID_CONTEXT;
835 if (!url || !username || !password) return IE_INVAL;
836 if (certificate || key) return IE_NOTSUP;
838 /* Store configuration */
839 free(context->url);
840 context->url = strdup(url);
841 if (!(context->url))
842 return IE_NOMEM;
844 /* Close connection if already logged in */
845 if (context->curl) {
846 close_connection(context);
849 /* Prepare CURL handle */
850 context->curl = curl_easy_init();
851 if (!(context->curl))
852 return IE_ERROR;
854 /* Build login request */
855 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
856 if (!request) {
857 isds_log_message(context, _("Could build ISDS login request"));
858 return IE_ERROR;
860 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
861 if(!isds_ns) {
862 isds_log_message(context, _("Could not create ISDS name space"));
863 xmlFreeNode(request);
864 return IE_ERROR;
866 xmlSetNs(request, isds_ns);
868 /* Store credentials */
869 /* FIXME: mlock password
870 * (I have a library) */
871 discard_credentials(context);
872 context->username = strdup(username);
873 context->password = strdup(password);
874 if (!(context->username && context->password)) {
875 discard_credentials(context);
876 xmlFreeNode(request);
877 return IE_NOMEM;
880 isds_log(ILF_ISDS, ILL_DEBUG, _("Logging user %s into server %s\n"),
881 username, url);
883 /* Send login request */
884 soap_err = soap(context, "dz", request, &response, NULL, NULL);
886 /* Remove credentials */
887 discard_credentials(context);
889 /* Destroy login request */
890 xmlFreeNode(request);
892 if (soap_err) {
893 xmlFreeNodeList(response);
894 close_connection(context);
895 return soap_err;
898 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
899 * authentication succeeded if soap_err == IE_SUCCESS */
900 err = IE_SUCCESS;
902 xmlFreeNodeList(response);
904 if (!err)
905 isds_log(ILF_ISDS, ILL_DEBUG,
906 _("User %s has been logged into server %s successfully\n"),
907 username, url);
908 return err;
912 /* Log out from ISDS server discards credentials and connection configuration. */
913 isds_error isds_logout(struct isds_ctx *context) {
914 if (!context) return IE_INVALID_CONTEXT;
916 /* Close connection */
917 if (context->curl) {
918 close_connection(context);
920 /* Discard credentials for sure. They should not survive isds_login(),
921 * even successful .*/
922 discard_credentials(context);
923 free(context->url);
924 context->url = NULL;
926 isds_log(ILF_ISDS, ILL_DEBUG, _("Logged out from ISDS server\n"));
927 } else {
928 discard_credentials(context);
930 return IE_SUCCESS;
934 /* Verify connection to ISDS is alive and server is responding.
935 * Sent dumy request to ISDS and expect dummy response. */
936 isds_error isds_ping(struct isds_ctx *context) {
937 isds_error soap_err;
938 xmlNsPtr isds_ns = NULL;
939 xmlNodePtr request = NULL;
940 xmlNodePtr response = NULL;
942 if (!context) return IE_INVALID_CONTEXT;
944 /* Check if connection is established */
945 if (!context->curl) return IE_CONNECTION_CLOSED;
948 /* Build dummy request */
949 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
950 if (!request) {
951 isds_log_message(context, _("Could build ISDS dummy request"));
952 return IE_ERROR;
954 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
955 if(!isds_ns) {
956 isds_log_message(context, _("Could not create ISDS name space"));
957 xmlFreeNode(request);
958 return IE_ERROR;
960 xmlSetNs(request, isds_ns);
962 isds_log(ILF_ISDS, ILL_DEBUG, _("Pinging ISDS server\n"));
964 /* Sent dummy request */
965 soap_err = soap(context, "dz", request, &response, NULL, NULL);
967 /* Destroy login request */
968 xmlFreeNode(request);
970 if (soap_err) {
971 isds_log(ILF_ISDS, ILL_DEBUG,
972 _("ISDS server could not be contacted\n"));
973 xmlFreeNodeList(response);
974 close_connection(context);
975 return soap_err;
978 /* XXX: Untill we don't propagate HTTP code 500 or 4xx, we can be sure
979 * authentication succeeded if soap_err == IE_SUCCESS */
980 /* TODO: ISDS documentation does not specify response body.
981 * However real server sends back DummyOperationResponse */
984 xmlFreeNodeList(response);
986 isds_log(ILF_ISDS, ILL_DEBUG, _("ISDS server alive\n"));
988 return IE_SUCCESS;
992 /* Send bogus request to ISDS.
993 * Just for test purposes */
994 isds_error isds_bogus_request(struct isds_ctx *context) {
995 isds_error err;
996 xmlNsPtr isds_ns = NULL;
997 xmlNodePtr request = NULL;
998 xmlDocPtr response = NULL;
999 xmlChar *code = NULL, *message = NULL;
1001 if (!context) return IE_INVALID_CONTEXT;
1003 /* Check if connection is established */
1004 if (!context->curl) {
1005 /* Testing printf message */
1006 isds_printf_message(context, "%s", _("I said connection closed"));
1007 return IE_CONNECTION_CLOSED;
1011 /* Build dummy request */
1012 request = xmlNewNode(NULL, BAD_CAST "X-BogusOperation");
1013 if (!request) {
1014 isds_log_message(context, _("Could build ISDS bogus request"));
1015 return IE_ERROR;
1017 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1018 if(!isds_ns) {
1019 isds_log_message(context, _("Could not create ISDS name space"));
1020 xmlFreeNode(request);
1021 return IE_ERROR;
1023 xmlSetNs(request, isds_ns);
1025 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending bogus request to ISDS\n"));
1027 /* Sent bogus request */
1028 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
1030 /* Destroy request */
1031 xmlFreeNode(request);
1033 if (err) {
1034 isds_log(ILF_ISDS, ILL_DEBUG,
1035 _("Processing ISDS response on bogus request failed\n"));
1036 xmlFreeDoc(response);
1037 return err;
1040 /* Check for response status */
1041 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
1042 &code, &message, NULL);
1043 if (err) {
1044 isds_log(ILF_ISDS, ILL_DEBUG,
1045 _("ISDS response on bogus request is missing status\n"));
1046 free(code);
1047 free(message);
1048 xmlFreeDoc(response);
1049 return err;
1051 if (xmlStrcmp(code, BAD_CAST "0000")) {
1052 char *code_locale = utf82locale((char*)code);
1053 char *message_locale = utf82locale((char*)message);
1054 isds_log(ILF_ISDS, ILL_DEBUG,
1055 _("Server refused bogus request (code=%s, message=%s)\n"),
1056 code_locale, message_locale);
1057 /* XXX: Literal error messages from ISDS are Czech mesages
1058 * (English sometimes) in UTF-8. It's hard to catch them for
1059 * translation. Successfully gettextized would return in locale
1060 * encoding, unsuccessfully translated would pass in UTF-8. */
1061 isds_log_message(context, message_locale);
1062 free(code_locale);
1063 free(message_locale);
1064 free(code);
1065 free(message);
1066 xmlFreeDoc(response);
1067 return IE_ISDS;
1071 free(code);
1072 free(message);
1073 xmlFreeDoc(response);
1075 isds_log(ILF_ISDS, ILL_DEBUG,
1076 _("Bogus message accepted by server. This should not happen.\n"));
1078 return IE_SUCCESS;
1082 /* Serialize XML subtree to buffer preserving XML indentatition.
1083 * @context is session context
1084 * @subtree is XML element to be serialized (with childern)
1085 * @buffer is automatically reallocated buffer where serialize to
1086 * @length is size of serialized stream in bytes
1087 * @return standard error code, free @buffer in case of error */
1088 static isds_error serialize_subtree(struct isds_ctx *context,
1089 xmlNodePtr subtree, void **buffer, size_t *length) {
1090 isds_error err = IE_SUCCESS;
1091 xmlBufferPtr xml_buffer = NULL;
1092 xmlSaveCtxtPtr save_ctx = NULL;
1093 xmlDocPtr subtree_doc = NULL;
1094 xmlNodePtr subtree_copy;
1095 xmlNsPtr isds_ns;
1096 void *new_buffer;
1098 if (!context) return IE_INVALID_CONTEXT;
1099 if (!buffer) return IE_INVAL;
1100 zfree(*buffer);
1101 if (!subtree || !length) return IE_INVAL;
1103 /* Make temporary XML document with @subtree root element */
1104 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1105 * It can result in not well-formed on invalid XML tree (e.g. name space
1106 * prefix definition can miss. */
1107 /*FIXME */
1109 subtree_doc = xmlNewDoc(BAD_CAST "1.0");
1110 if (!subtree_doc) {
1111 isds_log_message(context, _("Could not build temporary document"));
1112 err = IE_ERROR;
1113 goto leave;
1116 /* XXX: Copy subtree and attach the copy to document.
1117 * One node can not bee attached into more document at the same time.
1118 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1119 * automatically.
1120 * XXX: Check xmlSaveTree() too. */
1121 subtree_copy = xmlCopyNodeList(subtree);
1122 if (!subtree_copy) {
1123 isds_log_message(context, _("Could not copy subtree"));
1124 err = IE_ERROR;
1125 goto leave;
1127 xmlDocSetRootElement(subtree_doc, subtree_copy);
1129 /* Only this way we get namespace definition as @xmlns:isds,
1130 * otherwise we get namespace prefix without definition */
1131 /* FIXME: Don't overwrite original default namespace */
1132 isds_ns = xmlNewNs(subtree_copy, BAD_CAST ISDS_NS, NULL);
1133 if(!isds_ns) {
1134 isds_log_message(context, _("Could not create ISDS name space"));
1135 err = IE_ERROR;
1136 goto leave;
1138 xmlSetNs(subtree_copy, isds_ns);
1141 /* Serialize the document into buffer */
1142 xml_buffer = xmlBufferCreate();
1143 if (!xml_buffer) {
1144 isds_log_message(context, _("Could not create xmlBuffer"));
1145 err = IE_ERROR;
1146 goto leave;
1148 /* Last argument 0 means to not format the XML tree */
1149 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8", 0);
1150 if (!save_ctx) {
1151 isds_log_message(context, _("Could not create XML serializer"));
1152 err = IE_ERROR;
1153 goto leave;
1155 /* XXX: According LibXML documentation, this function does not return
1156 * meaningfull value yet */
1157 xmlSaveDoc(save_ctx, subtree_doc);
1158 if (-1 == xmlSaveFlush(save_ctx)) {
1159 isds_log_message(context,
1160 _("Could not serialize XML subtree"));
1161 err = IE_ERROR;
1162 goto leave;
1164 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1165 * even after xmlSaveFlush(). Thus close it here */
1166 xmlSaveClose(save_ctx); save_ctx = NULL;
1169 /* Store and detach buffer from xml_buffer */
1170 *buffer = xml_buffer->content;
1171 *length = xml_buffer->use;
1172 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1174 /* Shrink buffer */
1175 new_buffer = realloc(*buffer, *length);
1176 if (new_buffer) *buffer = new_buffer;
1178 leave:
1179 if (err) {
1180 zfree(*buffer);
1181 *length = 0;
1184 xmlSaveClose(save_ctx);
1185 xmlBufferFree(xml_buffer);
1186 xmlFreeDoc(subtree_doc); /* Frees subtree_copy, isds_ns etc. */
1187 return err;
1190 #if 0
1191 /* Dump XML subtree to buffer as literral string, not valid XML possibly.
1192 * @context is session context
1193 * @document is original document where @nodeset points to
1194 * @nodeset is XPath node set to dump (recursively)
1195 * @buffer is automarically reallocated buffer where serialize to
1196 * @length is size of serialized stream in bytes
1197 * @return standard error code, free @buffer in case of error */
1198 static isds_error dump_nodeset(struct isds_ctx *context,
1199 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1200 void **buffer, size_t *length) {
1201 isds_error err = IE_SUCCESS;
1202 xmlBufferPtr xml_buffer = NULL;
1203 void *new_buffer;
1205 if (!context) return IE_INVALID_CONTEXT;
1206 if (!buffer) return IE_INVAL;
1207 zfree(*buffer);
1208 if (!document || !nodeset || !length) return IE_INVAL;
1209 *length = 0;
1211 /* Empty node set results into NULL buffer */
1212 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1213 goto leave;
1216 /* Resuling the document into buffer */
1217 xml_buffer = xmlBufferCreate();
1218 if (!xml_buffer) {
1219 isds_log_message(context, _("Could not create xmlBuffer"));
1220 err = IE_ERROR;
1221 goto leave;
1224 /* Itearate over all nodes */
1225 for (int i = 0; i < nodeset->nodeNr; i++) {
1226 /* Serialize node.
1227 * XXX: xmlNodeDump() appends to xml_buffer. */
1228 if (-1 ==
1229 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1230 isds_log_message(context, _("Could not dump XML node"));
1231 err = IE_ERROR;
1232 goto leave;
1236 /* Store and detach buffer from xml_buffer */
1237 *buffer = xml_buffer->content;
1238 *length = xml_buffer->use;
1239 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1241 /* Shrink buffer */
1242 new_buffer = realloc(*buffer, *length);
1243 if (new_buffer) *buffer = new_buffer;
1246 leave:
1247 if (err) {
1248 zfree(*buffer);
1249 *length = 0;
1252 xmlBufferFree(xml_buffer);
1253 return err;
1255 #endif
1257 #if 0
1258 /* Dump XML subtree to buffer as literral string, not valid XML possibly.
1259 * @context is session context
1260 * @document is original document where @nodeset points to
1261 * @nodeset is XPath node set to dump (recursively)
1262 * @buffer is automarically reallocated buffer where serialize to
1263 * @length is size of serialized stream in bytes
1264 * @return standard error code, free @buffer in case of error */
1265 static isds_error dump_nodeset(struct isds_ctx *context,
1266 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1267 void **buffer, size_t *length) {
1268 isds_error err = IE_SUCCESS;
1269 xmlBufferPtr xml_buffer = NULL;
1270 xmlSaveCtxtPtr save_ctx = NULL;
1271 void *new_buffer;
1273 if (!context) return IE_INVALID_CONTEXT;
1274 if (!buffer) return IE_INVAL;
1275 zfree(*buffer);
1276 if (!document || !nodeset || !length) return IE_INVAL;
1277 *length = 0;
1279 /* Empty node set results into NULL buffer */
1280 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1281 goto leave;
1284 /* Resuling the document into buffer */
1285 xml_buffer = xmlBufferCreate();
1286 if (!xml_buffer) {
1287 isds_log_message(context, _("Could not create xmlBuffer"));
1288 err = IE_ERROR;
1289 goto leave;
1291 if (xmlSubstituteEntitiesDefault(1)) {
1292 isds_log_message(context, _("Could not disable attribute escaping"));
1293 err = IE_ERROR;
1294 goto leave;
1296 /* Last argument means:
1297 * 0 to not format the XML tree
1298 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1299 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8",
1300 XML_SAVE_NO_DECL|XML_SAVE_NO_EMPTY|XML_SAVE_NO_XHTML);
1301 if (!save_ctx) {
1302 isds_log_message(context, _("Could not create XML serializer"));
1303 err = IE_ERROR;
1304 goto leave;
1306 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1307 isds_log_message(context, _("Could not disable attribute escaping"));
1308 err = IE_ERROR;
1309 goto leave;
1313 /* Itearate over all nodes */
1314 for (int i = 0; i < nodeset->nodeNr; i++) {
1315 /* Serialize node.
1316 * XXX: xmlNodeDump() appends to xml_buffer. */
1317 /*if (-1 ==
1318 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1320 /* XXX: According LibXML documentation, this function does not return
1321 * meaningfull value yet */
1322 xmlSaveTree(save_ctx, nodeset->nodeTab[i]);
1323 if (-1 == xmlSaveFlush(save_ctx)) {
1324 isds_log_message(context,
1325 _("Could not serialize XML subtree"));
1326 err = IE_ERROR;
1327 goto leave;
1331 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1332 * even after xmlSaveFlush(). Thus close it here */
1333 xmlSaveClose(save_ctx); save_ctx = NULL;
1335 /* Store and detach buffer from xml_buffer */
1336 *buffer = xml_buffer->content;
1337 *length = xml_buffer->use;
1338 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1340 /* Shrink buffer */
1341 new_buffer = realloc(*buffer, *length);
1342 if (new_buffer) *buffer = new_buffer;
1344 leave:
1345 if (err) {
1346 zfree(*buffer);
1347 *length = 0;
1350 xmlSaveClose(save_ctx);
1351 xmlBufferFree(xml_buffer);
1352 return err;
1354 #endif
1357 /* Convert UTF-8 @string represantion of ISDS dbType to enum @type */
1358 static isds_error string2isds_DbType(xmlChar *string, isds_DbType *type) {
1359 if (!string || !type) return IE_INVAL;
1361 if (!xmlStrcmp(string, BAD_CAST "FO"))
1362 *type = DBTYPE_FO;
1363 else if (!xmlStrcmp(string, BAD_CAST "PFO"))
1364 *type = DBTYPE_PFO;
1365 else if (!xmlStrcmp(string, BAD_CAST "PFO_ADVOK"))
1366 *type = DBTYPE_PFO_ADVOK;
1367 else if (!xmlStrcmp(string, BAD_CAST "PFO_DANPOR"))
1368 *type = DBTYPE_PFO_DANPOR;
1369 else if (!xmlStrcmp(string, BAD_CAST "PFO_INSSPR"))
1370 *type = DBTYPE_PFO_INSSPR;
1371 else if (!xmlStrcmp(string, BAD_CAST "PO"))
1372 *type = DBTYPE_PO;
1373 else if (!xmlStrcmp(string, BAD_CAST "PO_ZAK"))
1374 *type = DBTYPE_PO_ZAK;
1375 else if (!xmlStrcmp(string, BAD_CAST "PO_REQ"))
1376 *type = DBTYPE_PO_REQ;
1377 else if (!xmlStrcmp(string, BAD_CAST "OVM"))
1378 *type = DBTYPE_OVM;
1379 else if (!xmlStrcmp(string, BAD_CAST "OVM_NOTAR"))
1380 *type = DBTYPE_OVM_NOTAR;
1381 else if (!xmlStrcmp(string, BAD_CAST "OVM_EXEKUT"))
1382 *type = DBTYPE_OVM_EXEKUT;
1383 else if (!xmlStrcmp(string, BAD_CAST "OVM_REQ"))
1384 *type = DBTYPE_OVM_REQ;
1385 else
1386 return IE_ENUM;
1387 return IE_SUCCESS;
1391 /* Convert ISDS dbType enum @type to UTF-8 string.
1392 * @Return pointer to static string, or NULL if unkwnow enum value */
1393 static const xmlChar *isds_DbType2string(const isds_DbType type) {
1394 switch(type) {
1395 case DBTYPE_FO: return(BAD_CAST "FO"); break;
1396 case DBTYPE_PFO: return(BAD_CAST "PFO"); break;
1397 case DBTYPE_PFO_ADVOK: return(BAD_CAST "PFO_ADVOK"); break;
1398 case DBTYPE_PFO_DANPOR: return(BAD_CAST "PFO_DANPOR"); break;
1399 case DBTYPE_PFO_INSSPR: return(BAD_CAST "PFO_INSSPR"); break;
1400 case DBTYPE_PO: return(BAD_CAST "PO"); break;
1401 case DBTYPE_PO_ZAK: return(BAD_CAST "PO_ZAK"); break;
1402 case DBTYPE_PO_REQ: return(BAD_CAST "PO_REQ"); break;
1403 case DBTYPE_OVM: return(BAD_CAST "OVM"); break;
1404 case DBTYPE_OVM_NOTAR: return(BAD_CAST "OVM_NOTAR"); break;
1405 case DBTYPE_OVM_EXEKUT: return(BAD_CAST "OVM_EXEKUT"); break;
1406 case DBTYPE_OVM_REQ: return(BAD_CAST "OVM_REQ"); break;
1407 default: return NULL; break;
1412 /* Convert UTF-8 @string represantion of ISDS userType to enum @type */
1413 static isds_error string2isds_UserType(xmlChar *string, isds_UserType *type) {
1414 if (!string || !type) return IE_INVAL;
1416 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
1417 *type = USERTYPE_PRIMARY;
1418 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
1419 *type = USERTYPE_ENTRUSTED;
1420 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
1421 *type = USERTYPE_ADMINISTRATOR;
1422 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
1423 *type = USERTYPE_OFFICIAL;
1424 else
1425 return IE_ENUM;
1426 return IE_SUCCESS;
1430 /* Convert ISDS userType enum @type to UTF-8 string.
1431 * @Return pointer to static string, or NULL if unkwnow enum value */
1432 static const xmlChar *isds_UserType2string(const isds_UserType type) {
1433 switch(type) {
1434 case USERTYPE_PRIMARY: return(BAD_CAST "PRIMARY_USER"); break;
1435 case USERTYPE_ENTRUSTED: return(BAD_CAST "ENTRUSTED_USER"); break;
1436 case USERTYPE_ADMINISTRATOR: return(BAD_CAST "ADMINISTRATOR"); break;
1437 case USERTYPE_OFFICIAL: return(BAD_CAST "OFFICIAL"); break;
1438 default: return NULL; break;
1443 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
1444 * @Return pointer to static string, or NULL if unkwnow enum value */
1445 static const xmlChar *isds_FileMetaType2string(const isds_FileMetaType type) {
1446 switch(type) {
1447 case FILEMETATYPE_MAIN: return(BAD_CAST "main"); break;
1448 case FILEMETATYPE_ENCLOSURE: return(BAD_CAST "enclosure"); break;
1449 case FILEMETATYPE_SIGNATURE: return(BAD_CAST "signature"); break;
1450 case FILEMETATYPE_META: return(BAD_CAST "meta"); break;
1451 default: return NULL; break;
1456 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
1457 * @Return IE_ENUM if @string is not valid enum member */
1458 static isds_error string2isds_FileMetaType(const xmlChar *string,
1459 isds_FileMetaType *type) {
1460 if (!string || !type) return IE_INVAL;
1462 if (!xmlStrcmp(string, BAD_CAST "main"))
1463 *type = FILEMETATYPE_MAIN;
1464 else if (!xmlStrcmp(string, BAD_CAST "enclosure"))
1465 *type = FILEMETATYPE_ENCLOSURE;
1466 else if (!xmlStrcmp(string, BAD_CAST "signature"))
1467 *type = FILEMETATYPE_SIGNATURE;
1468 else if (!xmlStrcmp(string, BAD_CAST "meta"))
1469 *type = FILEMETATYPE_META;
1470 else
1471 return IE_ENUM;
1472 return IE_SUCCESS;
1476 /* Convert UTF-8 @string to ISDS hash @algorithm.
1477 * @Return IE_ENUM if @string is not valid enum member */
1478 static isds_error string2isds_hash_algorithm(const xmlChar *string,
1479 isds_hash_algorithm *algorithm) {
1480 if (!string || !algorithm) return IE_INVAL;
1482 if (!xmlStrcmp(string, BAD_CAST "MD5"))
1483 *algorithm = HASH_ALGORITHM_MD5;
1484 else if (!xmlStrcmp(string, BAD_CAST "SHA-1"))
1485 *algorithm = HASH_ALGORITHM_SHA_1;
1486 else if (!xmlStrcmp(string, BAD_CAST "SHA-224"))
1487 *algorithm = HASH_ALGORITHM_SHA_224;
1488 else if (!xmlStrcmp(string, BAD_CAST "SHA-256"))
1489 *algorithm = HASH_ALGORITHM_SHA_256;
1490 else if (!xmlStrcmp(string, BAD_CAST "SHA-384"))
1491 *algorithm = HASH_ALGORITHM_SHA_384;
1492 else if (!xmlStrcmp(string, BAD_CAST "SHA-512"))
1493 *algorithm = HASH_ALGORITHM_SHA_512;
1494 else
1495 return IE_ENUM;
1496 return IE_SUCCESS;
1500 /* Convert UTF-8 @string represantion of ISO 8601 date to @time.
1501 * XXX: Not all ISO formats are supported */
1502 static isds_error datestring2tm(const xmlChar *string, struct tm *time) {
1503 char *offset;
1504 if (!string || !time) return IE_INVAL;
1506 /* xsd:date is ISO 8601 string, thus ASCII */
1507 offset = strptime((char*)string, "%Y-%m-%d", time);
1508 if (offset && *offset == '\0')
1509 return IE_SUCCESS;
1511 offset = strptime((char*)string, "%Y%m%d", time);
1512 if (offset && *offset == '\0')
1513 return IE_SUCCESS;
1515 offset = strptime((char*)string, "%Y-%j", time);
1516 if (offset && *offset == '\0')
1517 return IE_SUCCESS;
1519 return IE_NOTSUP;
1523 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
1524 static isds_error tm2datestring(const struct tm *time, xmlChar **string) {
1525 if (!time || !string) return IE_INVAL;
1527 if (-1 == isds_asprintf((char **) string, "%d-%02d-%02d",
1528 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
1529 return IE_ERROR;
1531 return IE_SUCCESS;
1535 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
1536 * respects the @time microseconds too. */
1537 static isds_error timeval2timestring(const struct timeval *time,
1538 xmlChar **string) {
1539 struct tm broken;
1541 if (!time || !string) return IE_INVAL;
1543 if (!gmtime_r(&time->tv_sec, &broken)) return IE_DATE;
1544 if (time->tv_usec < 0 || time->tv_usec > 999999) return IE_DATE;
1546 /* TODO: small negative year should be formated as "-0012". This is not
1547 * true for glibc "%04d". We should implement it.
1548 * TODO: What's type of time->tv_usec exactly? Unsigned? Absolute?
1549 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
1550 if (-1 == isds_asprintf((char **) string,
1551 "%04d-%02d-%02dT%02d:%02d:%02d.%06ld",
1552 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
1553 broken.tm_hour, broken.tm_min, broken.tm_sec,
1554 time->tv_usec))
1555 return IE_ERROR;
1557 return IE_SUCCESS;
1561 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
1562 * It respects microseconds too.
1563 * In case of error, @time will be freed. */
1564 static isds_error timestring2timeval(const xmlChar *string,
1565 struct timeval **time) {
1566 struct tm broken;
1567 char *offset, *delim, *endptr;
1568 char subseconds[7];
1569 int offset_hours, offset_minutes;
1570 int i;
1572 if (!time) return IE_INVAL;
1574 memset(&broken, 0, sizeof(broken));
1576 if (!*time) {
1577 *time = calloc(1, sizeof(**time));
1578 if (!*time) return IE_NOMEM;
1579 } else {
1580 memset(*time, 0, sizeof(**time));
1584 /* xsd:date is ISO 8601 string, thus ASCII */
1585 /*TODO: negative year */
1587 /* Parse date and time without subseconds and offset */
1588 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
1589 if (!offset) {
1590 free(*time); *time = NULL;
1591 return IE_DATE;
1594 /* Get subseconds */
1595 if (*offset == '.' ) {
1596 offset++;
1598 /* Copy first 6 digits, padd it with zeros.
1599 * XXX: It truncates longer number, no round.
1600 * Current server implementation uses only milisecond resolution. */
1601 /* TODO: isdigit() is locale sensitive */
1602 for (i = 0;
1603 i < sizeof(subseconds)/sizeof(char) - 1 && isdigit(*offset);
1604 i++, offset++) {
1605 subseconds[i] = *offset;
1607 for (; i < sizeof(subseconds)/sizeof(char) - 1; i++) {
1608 subseconds[i] = '0';
1610 subseconds[6] = '\0';
1612 /* Convert it into integer */
1613 (*time)->tv_usec = strtol(subseconds, &endptr, 10);
1614 if (*endptr != '\0' || (*time)->tv_usec == LONG_MIN ||
1615 (*time)->tv_usec == LONG_MAX) {
1616 free(*time); *time = NULL;
1617 return IE_DATE;
1620 /* move to the zone offset delimiter */
1621 delim = strchr(offset, '-');
1622 if (!delim)
1623 delim = strchr(offset, '+');
1624 offset = delim;
1627 /* Get zone offset */
1628 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
1629 * "" equals to "Z" and it means UTC zone. */
1630 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
1631 * colon separator */
1632 if (*offset == '-' || *offset == '+') {
1633 offset++;
1634 if (2 != sscanf(offset, "%2d:%2d", &offset_hours, &offset_minutes)) {
1635 free(*time); *time = NULL;
1636 return IE_DATE;
1638 broken.tm_hour -= offset_hours;
1639 broken.tm_min -= offset_minutes * ((offset_hours<0) ? -1 : 1);
1642 /* Convert to time_t */
1643 switch_tz_to_utc();
1644 (*time)->tv_sec = mktime(&broken);
1645 switch_tz_to_native();
1646 if ((*time)->tv_sec == (time_t) -1) {
1647 free(*time); *time = NULL;
1648 return IE_DATE;
1651 return IE_SUCCESS;
1655 /* Convert unsigned int into isds_message_status.
1656 * @context is session context
1657 * @number is pointer to number value. NULL will be treated as invalid value.
1658 * @status is automatically reallocated status
1659 * @return IE_SUCCESS, or error code and free status */
1660 static isds_error uint2isds_message_status(struct isds_ctx *context,
1661 const unsigned long int *number, isds_message_status **status) {
1662 if (!context) return IE_INVALID_CONTEXT;
1663 if (!status) return IE_INVAL;
1665 free(*status); *status = NULL;
1666 if (!number) return IE_INVAL;
1668 if (*number < 1 || *number > 10) {
1669 isds_printf_message(context, _("Invalid messsage status value: %lu"),
1670 *number);
1671 return IE_ENUM;
1674 *status = malloc(sizeof(**status));
1675 if (!*status) return IE_NOMEM;
1677 **status = 1 << *number;
1678 return IE_SUCCESS;
1682 /* Convert event description string into isds_event memebers type and
1683 * description
1684 * @string is raw event decsription starting with event prefix
1685 * @event is structure where to store type and stripped description to
1686 * @return standard error code, unkown prefix is not classified as an error. */
1687 static isds_error eventstring2event(const xmlChar *string,
1688 struct isds_event* event) {
1689 const xmlChar *known_prefixes[] = {
1690 BAD_CAST "EV1:",
1691 BAD_CAST "EV2:",
1692 BAD_CAST "EV3:",
1693 BAD_CAST "EV4:"
1695 const isds_event_type types[] = {
1696 EVENT_ACCEPTED_BY_RECIPIENT,
1697 EVENT_ACCEPTED_BY_FICTION,
1698 EVENT_UNDELIVERABLE,
1699 EVENT_COMMERCIAL_ACCEPTED
1701 unsigned int index;
1702 size_t length;
1704 if (!string || !event) return IE_INVAL;
1706 if (!event->type) {
1707 event->type = malloc(sizeof(*event->type));
1708 if (!(event->type)) return IE_NOMEM;
1710 zfree(event->description);
1712 for (index = 0; index < sizeof(known_prefixes)/sizeof(known_prefixes[0]);
1713 index++) {
1714 length = xmlUTF8Strlen(known_prefixes[index]);
1716 if (!xmlStrncmp(string, known_prefixes[index], length)) {
1717 /* Prefix is known */
1718 *event->type = types[index];
1720 /* Strip prefix from description and spaces */
1721 /* TODO: Recognize all wite spaces from UCS blank class and
1722 * operate on UTF-8 chars. */
1723 for (; string[length] != '\0' && string[length] == ' '; length++);
1724 event->description = strdup((char *) (string + length));
1725 if (!(event->description)) return IE_NOMEM;
1727 return IE_SUCCESS;
1731 /* Unknown event prefix.
1732 * XSD allows any string */
1733 char *string_locale = utf82locale((char *) string);
1734 isds_log(ILF_ISDS, ILL_WARNING,
1735 _("Uknown delivery info event prefix: %s\n"), string_locale);
1736 free(string_locale);
1738 *event->type = EVENT_UKNOWN;
1739 event->description = strdup((char *) string);
1740 if (!(event->description)) return IE_NOMEM;
1742 return IE_SUCCESS;
1746 /* Following EXTRACT_* macros expects @result, @xpath_ctx, @err, @context
1747 * and leave lable */
1748 #define EXTRACT_STRING(element, string) { \
1749 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
1750 if (!result) { \
1751 err = IE_ERROR; \
1752 goto leave; \
1754 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
1755 if (result->nodesetval->nodeNr > 1) { \
1756 isds_printf_message(context, _("Multiple %s element"), element); \
1757 err = IE_ERROR; \
1758 goto leave; \
1760 (string) = (char *) \
1761 xmlXPathCastNodeSetToString(result->nodesetval); \
1762 if (!(string)) { \
1763 err = IE_ERROR; \
1764 goto leave; \
1769 #define EXTRACT_BOOLEAN(element, booleanPtr) \
1771 char *string = NULL; \
1772 EXTRACT_STRING(element, string); \
1774 if (string) { \
1775 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
1776 if (!(booleanPtr)) { \
1777 free(string); \
1778 err = IE_NOMEM; \
1779 goto leave; \
1782 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
1783 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
1784 *(booleanPtr) = 1; \
1785 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
1786 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
1787 *(booleanPtr) = 0; \
1788 else { \
1789 char *string_locale = utf82locale((char*)string); \
1790 isds_printf_message(context, \
1791 _("%s value is not valid boolean: %s"), \
1792 element, string_locale); \
1793 free(string_locale); \
1794 free(string); \
1795 err = IE_ERROR; \
1796 goto leave; \
1799 free(string); \
1803 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
1805 char *string = NULL; \
1806 EXTRACT_STRING(element, string); \
1807 if (string) { \
1808 long int number; \
1809 char *endptr; \
1811 number = strtol((char*)string, &endptr, 10); \
1813 if (*endptr != '\0') { \
1814 char *string_locale = utf82locale((char *)string); \
1815 isds_printf_message(context, \
1816 _("%s is not valid integer: %s"), \
1817 element, string_locale); \
1818 free(string_locale); \
1819 free(string); \
1820 err = IE_ISDS; \
1821 goto leave; \
1824 if (number == LONG_MIN || number == LONG_MAX) { \
1825 char *string_locale = utf82locale((char *)string); \
1826 isds_printf_message(context, \
1827 _("%s value out of range of long int: %s"), \
1828 element, string_locale); \
1829 free(string_locale); \
1830 free(string); \
1831 err = IE_ERROR; \
1832 goto leave; \
1835 free(string); string = NULL; \
1837 if (!(preallocated)) { \
1838 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
1839 if (!(longintPtr)) { \
1840 err = IE_NOMEM; \
1841 goto leave; \
1844 *(longintPtr) = number; \
1848 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
1850 char *string = NULL; \
1851 EXTRACT_STRING(element, string); \
1852 if (string) { \
1853 long int number; \
1854 char *endptr; \
1856 number = strtol((char*)string, &endptr, 10); \
1858 if (*endptr != '\0') { \
1859 char *string_locale = utf82locale((char *)string); \
1860 isds_printf_message(context, \
1861 _("%s is not valid integer: %s"), \
1862 element, string_locale); \
1863 free(string_locale); \
1864 free(string); \
1865 err = IE_ISDS; \
1866 goto leave; \
1869 if (number == LONG_MIN || number == LONG_MAX) { \
1870 char *string_locale = utf82locale((char *)string); \
1871 isds_printf_message(context, \
1872 _("%s value out of range of long int: %s"), \
1873 element, string_locale); \
1874 free(string_locale); \
1875 free(string); \
1876 err = IE_ERROR; \
1877 goto leave; \
1880 free(string); string = NULL; \
1881 if (number < 0) { \
1882 isds_printf_message(context, \
1883 _("%s value is negative: %ld"), element, number); \
1884 err = IE_ERROR; \
1885 goto leave; \
1888 if (!(preallocated)) { \
1889 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
1890 if (!(ulongintPtr)) { \
1891 err = IE_NOMEM; \
1892 goto leave; \
1895 *(ulongintPtr) = number; \
1899 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
1900 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
1901 NULL); \
1902 if ((required) && (!string)) { \
1903 char *attribute_locale = utf82locale(attribute); \
1904 char *element_locale = utf82locale((char *)xpath_ctx->node->name); \
1905 isds_printf_message(context, \
1906 _("Could not extract required %s attribute value from " \
1907 "%s element"), attribute_locale, element_locale); \
1908 free(element_locale); \
1909 free(attribute_locale); \
1910 err = IE_ERROR; \
1911 goto leave; \
1916 #define INSERT_STRING(parent, element, string) \
1918 node = xmlNewTextChild(parent, NULL, BAD_CAST (element), \
1919 (xmlChar *) (string)); \
1920 if (!node) { \
1921 isds_printf_message(context, \
1922 _("Could not add %s child to %s element"), \
1923 element, (parent)->name); \
1924 err = IE_ERROR; \
1925 goto leave; \
1929 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
1931 if (boolean) { INSERT_STRING(parent, element, "true"); } \
1932 else { INSERT_STRING(parent, element, "false"); } \
1935 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
1937 if (booleanPtr) { \
1938 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
1939 } else { \
1940 INSERT_STRING(parent, element, NULL); \
1944 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
1945 if ((longintPtr)) { \
1946 /* FIXME: locale sensitive */ \
1947 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
1948 err = IE_NOMEM; \
1949 goto leave; \
1951 INSERT_STRING(parent, element, buffer) \
1952 free(buffer); (buffer) = NULL; \
1953 } else { INSERT_STRING(parent, element, NULL) } \
1956 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
1957 if ((ulongintPtr)) { \
1958 /* FIXME: locale sensitive */ \
1959 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
1960 err = IE_NOMEM; \
1961 goto leave; \
1963 INSERT_STRING(parent, element, buffer) \
1964 free(buffer); (buffer) = NULL; \
1965 } else { INSERT_STRING(parent, element, NULL) } \
1968 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
1970 /* FIXME: locale sensitive */ \
1971 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
1972 err = IE_NOMEM; \
1973 goto leave; \
1975 INSERT_STRING(parent, element, buffer) \
1976 free(buffer); (buffer) = NULL; \
1979 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
1981 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
1982 (xmlChar *) (string)); \
1983 if (!attribute_node) { \
1984 isds_printf_message(context, _("Could not add %s " \
1985 "attribute to %s element"), \
1986 (attribute), (parent)->name); \
1987 err = IE_ERROR; \
1988 goto leave; \
1992 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
1993 if (string) { \
1994 int length = xmlUTF8Strlen((xmlChar *) (string)); \
1995 if (length > (maximum)) { \
1996 isds_printf_message(context, \
1997 ngettext("%s has more than %d characters", \
1998 "%s has more than %d characters", (maximum)), \
1999 (name), (maximum)); \
2000 err = IE_2BIG; \
2001 goto leave; \
2003 if (length < (minimum)) { \
2004 isds_printf_message(context, \
2005 ngettext("%s has less than %d characters", \
2006 "%s has less than %d characters", (minimum)), \
2007 (name), (minimum)); \
2008 err = IE_2SMALL; \
2009 goto leave; \
2014 #define INSERT_ELEMENT(child, parent, element) \
2016 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2017 if (!(child)) { \
2018 isds_printf_message(context, \
2019 _("Could not add %s child to %s element"), \
2020 (element), (parent)->name); \
2021 err = IE_ERROR; \
2022 goto leave; \
2027 /* Find child element by name in given XPath context and switch context onto
2028 * it. The child must be uniq and must exist. Otherwise failes.
2029 * @context is ISDS context
2030 * @child is child element name
2031 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2032 * into it child. In error case, the @xpath_ctx keeps original value. */
2033 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
2034 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
2035 isds_error err = IE_SUCCESS;
2036 xmlXPathObjectPtr result = NULL;
2038 if (!context) return IE_INVALID_CONTEXT;
2039 if (!child || !xpath_ctx) return IE_INVAL;
2041 /* Find child */
2042 result = xmlXPathEvalExpression(child, xpath_ctx);
2043 if (!result) {
2044 err = IE_XML;
2045 goto leave;
2048 /* No match */
2049 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2050 char *parent_locale = utf82locale((char*) xpath_ctx->node->name);
2051 char *child_locale = utf82locale((char*) child);
2052 isds_printf_message(context,
2053 _("%s element does not contain %s child"),
2054 parent_locale, child_locale);
2055 free(child_locale);
2056 free(parent_locale);
2057 err = IE_NOEXIST;
2058 goto leave;
2061 /* More matches */
2062 if (result->nodesetval->nodeNr > 1) {
2063 char *parent_locale = utf82locale((char*) xpath_ctx->node->name);
2064 char *child_locale = utf82locale((char*) child);
2065 isds_printf_message(context,
2066 _("%s element contains multiple %s childs"),
2067 parent_locale, child_locale);
2068 free(child_locale);
2069 free(parent_locale);
2070 err = IE_NOTUNIQ;
2071 goto leave;
2074 /* Switch context */
2075 xpath_ctx->node = result->nodesetval->nodeTab[0];
2077 leave:
2078 xmlXPathFreeObject(result);
2079 return err;
2084 /* Find and convert XSD:gPersonName group in current node into structure
2085 * @context is ISDS context
2086 * @personName is automically reallocated person name structure. If no member
2087 * value is found, will be freed.
2088 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2089 * elements
2090 * In case of error @personName will be freed. */
2091 static isds_error extract_gPersonName(struct isds_ctx *context,
2092 struct isds_PersonName **personName, xmlXPathContextPtr xpath_ctx) {
2093 isds_error err = IE_SUCCESS;
2094 xmlXPathObjectPtr result = NULL;
2096 if (!context) return IE_INVALID_CONTEXT;
2097 if (!personName) return IE_INVAL;
2098 isds_PersonName_free(personName);
2099 if (!xpath_ctx) return IE_INVAL;
2102 *personName = calloc(1, sizeof(**personName));
2103 if (!*personName) {
2104 err = IE_NOMEM;
2105 goto leave;
2108 EXTRACT_STRING("isds:pnFirstName", (*personName)->pnFirstName);
2109 EXTRACT_STRING("isds:pnMiddleName", (*personName)->pnMiddleName);
2110 EXTRACT_STRING("isds:pnLastName", (*personName)->pnLastName);
2111 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName)->pnLastNameAtBirth);
2113 if (!(*personName)->pnFirstName && !(*personName)->pnMiddleName &&
2114 !(*personName)->pnLastName && !(*personName)->pnLastNameAtBirth)
2115 isds_PersonName_free(personName);
2117 leave:
2118 if (err) isds_PersonName_free(personName);
2119 xmlXPathFreeObject(result);
2120 return err;
2124 /* Find and convert XSD:gAddress group in current node into structure
2125 * @context is ISDS context
2126 * @address is automically reallocated address structure. If no member
2127 * value is found, will be freed.
2128 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2129 * elements
2130 * In case of error @address will be freed. */
2131 static isds_error extract_gAddress(struct isds_ctx *context,
2132 struct isds_Address **address, xmlXPathContextPtr xpath_ctx) {
2133 isds_error err = IE_SUCCESS;
2134 xmlXPathObjectPtr result = NULL;
2136 if (!context) return IE_INVALID_CONTEXT;
2137 if (!address) return IE_INVAL;
2138 isds_Address_free(address);
2139 if (!xpath_ctx) return IE_INVAL;
2142 *address = calloc(1, sizeof(**address));
2143 if (!*address) {
2144 err = IE_NOMEM;
2145 goto leave;
2148 EXTRACT_STRING("isds:adCity", (*address)->adCity);
2149 EXTRACT_STRING("isds:adStreet", (*address)->adStreet);
2150 EXTRACT_STRING("isds:adNumberInStreet", (*address)->adNumberInStreet);
2151 EXTRACT_STRING("isds:adNumberInMunicipality",
2152 (*address)->adNumberInMunicipality);
2153 EXTRACT_STRING("isds:adZipCode", (*address)->adZipCode);
2154 EXTRACT_STRING("isds:adState", (*address)->adState);
2156 if (!(*address)->adCity && !(*address)->adStreet &&
2157 !(*address)->adNumberInStreet &&
2158 !(*address)->adNumberInMunicipality &&
2159 !(*address)->adZipCode && !(*address)->adState)
2160 isds_Address_free(address);
2162 leave:
2163 if (err) isds_Address_free(address);
2164 xmlXPathFreeObject(result);
2165 return err;
2169 /* Find and convert isds:biDate element in current node into structure
2170 * @context is ISDS context
2171 * @biDate is automically reallocated birth date structure. If no member
2172 * value is found, will be freed.
2173 * @xpath_ctx is XPath context with current node as parent for isds:biDate
2174 * element
2175 * In case of error @biDate will be freed. */
2176 static isds_error extract_BiDate(struct isds_ctx *context,
2177 struct tm **biDate, xmlXPathContextPtr xpath_ctx) {
2178 isds_error err = IE_SUCCESS;
2179 xmlXPathObjectPtr result = NULL;
2180 char *string = NULL;
2182 if (!context) return IE_INVALID_CONTEXT;
2183 if (!biDate) return IE_INVAL;
2184 zfree(*biDate);
2185 if (!xpath_ctx) return IE_INVAL;
2187 EXTRACT_STRING("isds:biDate", string);
2188 if (string) {
2189 *biDate = calloc(1, sizeof(**biDate));
2190 if (!*biDate) {
2191 err = IE_NOMEM;
2192 goto leave;
2194 err = datestring2tm((xmlChar *)string, *biDate);
2195 if (err) {
2196 if (err == IE_NOTSUP) {
2197 err = IE_ISDS;
2198 char *string_locale = utf82locale(string);
2199 isds_printf_message(context,
2200 _("Invalid isds:biDate value: %s"), string_locale);
2201 free(string_locale);
2203 goto leave;
2207 leave:
2208 if (err) zfree(*biDate);
2209 free(string);
2210 xmlXPathFreeObject(result);
2211 return err;
2215 /* Convert isds:dBOwnerInfo XML tree into structure
2216 * @context is ISDS context
2217 * @db_owner_info is automically reallocated box owner info structure
2218 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
2219 * In case of error @db_owner_info will be freed. */
2220 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
2221 struct isds_DbOwnerInfo **db_owner_info,
2222 xmlXPathContextPtr xpath_ctx) {
2223 isds_error err = IE_SUCCESS;
2224 xmlXPathObjectPtr result = NULL;
2225 char *string = NULL;
2227 if (!context) return IE_INVALID_CONTEXT;
2228 if (!db_owner_info) return IE_INVAL;
2229 isds_DbOwnerInfo_free(db_owner_info);
2230 if (!xpath_ctx) return IE_INVAL;
2233 *db_owner_info = calloc(1, sizeof(**db_owner_info));
2234 if (!*db_owner_info) {
2235 err = IE_NOMEM;
2236 goto leave;
2239 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
2241 EXTRACT_STRING("isds:dbType", string);
2242 if (string) {
2243 (*db_owner_info)->dbType =
2244 calloc(1, sizeof(*((*db_owner_info)->dbType)));
2245 if (!(*db_owner_info)->dbType) {
2246 err = IE_NOMEM;
2247 goto leave;
2249 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
2250 if (err) {
2251 zfree((*db_owner_info)->dbType);
2252 if (err == IE_ENUM) {
2253 err = IE_ISDS;
2254 char *string_locale = utf82locale(string);
2255 isds_printf_message(context, _("Unknown isds:dbType: %s"),
2256 string_locale);
2257 free(string_locale);
2259 goto leave;
2261 zfree(string);
2264 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
2266 err = extract_gPersonName(context, &(*db_owner_info)->personName,
2267 xpath_ctx);
2268 if (err) goto leave;
2270 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
2272 (*db_owner_info)->birthInfo =
2273 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
2274 if (!(*db_owner_info)->birthInfo) {
2275 err = IE_NOMEM;
2276 goto leave;
2278 err = extract_BiDate(context, &(*db_owner_info)->birthInfo->biDate,
2279 xpath_ctx);
2280 if (err) goto leave;
2281 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
2282 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
2283 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
2284 if (!(*db_owner_info)->birthInfo->biDate &&
2285 !(*db_owner_info)->birthInfo->biCity &&
2286 !(*db_owner_info)->birthInfo->biCounty &&
2287 !(*db_owner_info)->birthInfo->biState)
2288 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
2290 err = extract_gAddress(context, &(*db_owner_info)->address, xpath_ctx);
2291 if (err) goto leave;
2293 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
2294 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
2295 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
2296 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
2297 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
2299 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
2301 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
2302 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
2303 (*db_owner_info)->dbOpenAddressing);
2305 leave:
2306 if (err) isds_DbOwnerInfo_free(db_owner_info);
2307 free(string);
2308 xmlXPathFreeObject(result);
2309 return err;
2313 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
2314 * @context is sesstion context
2315 * @owner is libsids structure with box description
2316 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
2317 static isds_error insert_DbOwnerInfo(struct isds_ctx *context,
2318 const struct isds_DbOwnerInfo *owner, xmlNodePtr db_owner_info) {
2320 isds_error err = IE_SUCCESS;
2321 xmlNodePtr node;
2322 xmlChar *string = NULL;
2324 if (!context) return IE_INVALID_CONTEXT;
2325 if (!owner || !db_owner_info) return IE_INVAL;
2328 /* Build XSD:tDbOwnerInfo */
2329 CHECK_FOR_STRING_LENGTH(owner->dbID, 0, 7, "dbID")
2330 INSERT_STRING(db_owner_info, "dbID", owner->dbID);
2332 /* dbType */
2333 if (owner->dbType) {
2334 const xmlChar *type_string = isds_DbType2string(*(owner->dbType));
2335 if (!type_string) {
2336 isds_printf_message(context, _("Invalid dbType value: %d"),
2337 *(owner->dbType));
2338 err = IE_ENUM;
2339 goto leave;
2341 INSERT_STRING(db_owner_info, "dbType", type_string);
2343 INSERT_STRING(db_owner_info, "ic", owner->ic);
2344 if (owner->personName) {
2345 INSERT_STRING(db_owner_info, "pnFirstName",
2346 owner->personName->pnFirstName);
2347 INSERT_STRING(db_owner_info, "pnMiddleName",
2348 owner->personName->pnMiddleName);
2349 INSERT_STRING(db_owner_info, "pnLastName",
2350 owner->personName->pnLastName);
2351 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
2352 owner->personName->pnLastNameAtBirth);
2354 INSERT_STRING(db_owner_info, "firmName", owner->firmName);
2355 if (owner->birthInfo) {
2356 if (owner->birthInfo->biDate) {
2357 if (!tm2datestring(owner->birthInfo->biDate, &string))
2358 INSERT_STRING(db_owner_info, "biDate", string);
2359 free(string); string = NULL;
2361 INSERT_STRING(db_owner_info, "biCity", owner->birthInfo->biCity);
2362 INSERT_STRING(db_owner_info, "biCounty", owner->birthInfo->biCounty);
2363 INSERT_STRING(db_owner_info, "biState", owner->birthInfo->biState);
2365 if (owner->address) {
2366 INSERT_STRING(db_owner_info, "adCity", owner->address->adCity);
2367 INSERT_STRING(db_owner_info, "adStreet", owner->address->adStreet);
2368 INSERT_STRING(db_owner_info, "adNumberInStreet",
2369 owner->address->adNumberInStreet);
2370 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
2371 owner->address->adNumberInMunicipality);
2372 INSERT_STRING(db_owner_info, "adZipCode", owner->address->adZipCode);
2373 INSERT_STRING(db_owner_info, "adState", owner->address->adState);
2375 INSERT_STRING(db_owner_info, "nationality", owner->nationality);
2376 INSERT_STRING(db_owner_info, "email", owner->email);
2377 INSERT_STRING(db_owner_info, "telNumber", owner->telNumber);
2379 CHECK_FOR_STRING_LENGTH(owner->identifier, 0, 20, "identifier")
2380 INSERT_STRING(db_owner_info, "identifier", owner->identifier);
2382 CHECK_FOR_STRING_LENGTH(owner->registryCode, 0, 5, "registryCode")
2383 INSERT_STRING(db_owner_info, "registryCode", owner->registryCode);
2385 INSERT_LONGINT(db_owner_info, "dbState", owner->dbState, string);
2387 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", owner->dbEffectiveOVM);
2388 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
2389 owner->dbOpenAddressing);
2391 leave:
2392 free(string);
2393 return err;
2397 /* Convert XSD:tDbUserInfo XML tree into structure
2398 * @context is ISDS context
2399 * @db_user_info is automically reallocated user info structure
2400 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
2401 * In case of error @db_user_info will be freed. */
2402 static isds_error extract_DbUserInfo(struct isds_ctx *context,
2403 struct isds_DbUserInfo **db_user_info, xmlXPathContextPtr xpath_ctx) {
2404 isds_error err = IE_SUCCESS;
2405 xmlXPathObjectPtr result = NULL;
2406 char *string = NULL;
2408 if (!context) return IE_INVALID_CONTEXT;
2409 if (!db_user_info) return IE_INVAL;
2410 isds_DbUserInfo_free(db_user_info);
2411 if (!xpath_ctx) return IE_INVAL;
2414 *db_user_info = calloc(1, sizeof(**db_user_info));
2415 if (!*db_user_info) {
2416 err = IE_NOMEM;
2417 goto leave;
2420 EXTRACT_STRING("isds:userID", (*db_user_info)->userID);
2422 EXTRACT_STRING("isds:userType", string);
2423 if (string) {
2424 (*db_user_info)->userType =
2425 calloc(1, sizeof(*((*db_user_info)->userType)));
2426 if (!(*db_user_info)->userType) {
2427 err = IE_NOMEM;
2428 goto leave;
2430 err = string2isds_UserType((xmlChar *)string,
2431 (*db_user_info)->userType);
2432 if (err) {
2433 zfree((*db_user_info)->userType);
2434 if (err == IE_ENUM) {
2435 err = IE_ISDS;
2436 char *string_locale = utf82locale(string);
2437 isds_printf_message(context,
2438 _("Unknown isds:userType value: %s"), string_locale);
2439 free(string_locale);
2441 goto leave;
2443 zfree(string);
2446 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info)->userPrivils, 0);
2448 (*db_user_info)->personName =
2449 calloc(1, sizeof(*((*db_user_info)->personName)));
2450 if (!(*db_user_info)->personName) {
2451 err = IE_NOMEM;
2452 goto leave;
2455 err = extract_gPersonName(context, &(*db_user_info)->personName,
2456 xpath_ctx);
2457 if (err) goto leave;
2459 err = extract_gAddress(context, &(*db_user_info)->address, xpath_ctx);
2460 if (err) goto leave;
2462 err = extract_BiDate(context, &(*db_user_info)->biDate, xpath_ctx);
2463 if (err) goto leave;
2465 EXTRACT_STRING("isds:ic", (*db_user_info)->ic);
2466 EXTRACT_STRING("isds:firmName", (*db_user_info)->firmName);
2468 EXTRACT_STRING("isds:caStreet", (*db_user_info)->caStreet);
2469 EXTRACT_STRING("isds:caCity", (*db_user_info)->caCity);
2470 EXTRACT_STRING("isds:caZipCode", (*db_user_info)->caZipCode);
2472 leave:
2473 if (err) isds_DbUserInfo_free(db_user_info);
2474 free(string);
2475 xmlXPathFreeObject(result);
2476 return err;
2480 /* Insert struct isds_DbUserInfo data (user description) into XML tree
2481 * @context is sesstion context
2482 * @user is libsids structure with user description
2483 * @db_user_info is XML element of XSD:tDbUserInfo */
2484 static isds_error insert_DbUserInfo(struct isds_ctx *context,
2485 const struct isds_DbUserInfo *user, xmlNodePtr db_user_info) {
2487 isds_error err = IE_SUCCESS;
2488 xmlNodePtr node;
2489 xmlChar *string = NULL;
2491 if (!context) return IE_INVALID_CONTEXT;
2492 if (!user || !db_user_info) return IE_INVAL;
2494 /* Build XSD:tDbUserInfo */
2495 if (user->personName) {
2496 INSERT_STRING(db_user_info, "pnFirstName",
2497 user->personName->pnFirstName);
2498 INSERT_STRING(db_user_info, "pnMiddleName",
2499 user->personName->pnMiddleName);
2500 INSERT_STRING(db_user_info, "pnLastName",
2501 user->personName->pnLastName);
2502 INSERT_STRING(db_user_info, "pnLastNameAtBirth",
2503 user->personName->pnLastNameAtBirth);
2505 if (user->address) {
2506 INSERT_STRING(db_user_info, "adCity", user->address->adCity);
2507 INSERT_STRING(db_user_info, "adStreet", user->address->adStreet);
2508 INSERT_STRING(db_user_info, "adNumberInStreet",
2509 user->address->adNumberInStreet);
2510 INSERT_STRING(db_user_info, "adNumberInMunicipality",
2511 user->address->adNumberInMunicipality);
2512 INSERT_STRING(db_user_info, "adZipCode", user->address->adZipCode);
2513 INSERT_STRING(db_user_info, "adState", user->address->adState);
2515 if (user->biDate) {
2516 if (!tm2datestring(user->biDate, &string))
2517 INSERT_STRING(db_user_info, "biDate", string);
2518 zfree(string);
2520 CHECK_FOR_STRING_LENGTH(user->userID, 6, 12, "userID");
2521 INSERT_STRING(db_user_info, "userID", user->userID);
2523 /* userType */
2524 if (user->userType) {
2525 const xmlChar *type_string = isds_UserType2string(*(user->userType));
2526 if (!type_string) {
2527 isds_printf_message(context, _("Invalid userType value: %d"),
2528 *(user->userType));
2529 err = IE_ENUM;
2530 goto leave;
2532 INSERT_STRING(db_user_info, "userType", type_string);
2535 INSERT_LONGINT(db_user_info, "userPrivils", user->userPrivils, string);
2536 CHECK_FOR_STRING_LENGTH(user->ic, 0, 8, "ic")
2537 INSERT_STRING(db_user_info, "ic", user->ic);
2538 CHECK_FOR_STRING_LENGTH(user->firmName, 0, 100, "firmName")
2539 INSERT_STRING(db_user_info, "firmName", user->firmName);
2540 INSERT_STRING(db_user_info, "caStreet", user->caStreet);
2541 INSERT_STRING(db_user_info, "caCity", user->caCity);
2542 INSERT_STRING(db_user_info, "caZipCode", user->caZipCode);
2544 leave:
2545 free(string);
2546 return err;
2550 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
2551 * isds_envelope structure. The envelope is automatically allocated but not
2552 * reallocated. The date are just appended into envelope structure.
2553 * @context is ISDS context
2554 * @envelope is automically allocated message envelope structure
2555 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
2556 * In case of error @envelope will be freed. */
2557 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
2558 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2559 isds_error err = IE_SUCCESS;
2560 xmlXPathObjectPtr result = NULL;
2562 if (!context) return IE_INVALID_CONTEXT;
2563 if (!envelope) return IE_INVAL;
2564 if (!xpath_ctx) return IE_INVAL;
2567 if (!*envelope) {
2568 /* Allocate envelope */
2569 *envelope = calloc(1, sizeof(**envelope));
2570 if (!*envelope) {
2571 err = IE_NOMEM;
2572 goto leave;
2574 } else {
2575 /* Else free former data */
2576 zfree((*envelope)->dmSenderOrgUnit);
2577 zfree((*envelope)->dmSenderOrgUnitNum);
2578 zfree((*envelope)->dbIDRecipient);
2579 zfree((*envelope)->dmRecipientOrgUnit);
2580 zfree((*envelope)->dmSenderOrgUnitNum);
2581 zfree((*envelope)->dmToHands);
2582 zfree((*envelope)->dmAnnotation);
2583 zfree((*envelope)->dmRecipientRefNumber);
2584 zfree((*envelope)->dmSenderRefNumber);
2585 zfree((*envelope)->dmRecipientIdent);
2586 zfree((*envelope)->dmSenderIdent);
2587 zfree((*envelope)->dmLegalTitleLaw);
2588 zfree((*envelope)->dmLegalTitleYear);
2589 zfree((*envelope)->dmLegalTitleSect);
2590 zfree((*envelope)->dmLegalTitlePar);
2591 zfree((*envelope)->dmLegalTitlePoint);
2592 zfree((*envelope)->dmPersonalDelivery);
2593 zfree((*envelope)->dmAllowSubstDelivery);
2596 /* Extract envelope elements added by sender or ISDS
2597 * (XSD: gMessageEnvelopeSub type) */
2598 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
2599 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
2600 (*envelope)->dmSenderOrgUnitNum, 0);
2601 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
2602 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
2603 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
2604 (*envelope)->dmSenderOrgUnitNum, 0);
2605 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
2606 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
2607 EXTRACT_STRING("isds:dmRecipientRefNumber",
2608 (*envelope)->dmRecipientRefNumber);
2609 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
2610 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
2611 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
2613 /* Extract envelope elements regarding law refference */
2614 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
2615 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
2616 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
2617 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
2618 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
2620 /* Extract envelope other elements */
2621 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
2622 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
2623 (*envelope)->dmAllowSubstDelivery);
2625 leave:
2626 if (err) isds_envelope_free(envelope);
2627 xmlXPathFreeObject(result);
2628 return err;
2633 /* Convert XSD gMessageEnvelope group of elements from XML tree into
2634 * isds_envelope structure. The envelope is automatically allocated but not
2635 * reallocated. The date are just appended into envelope structure.
2636 * @context is ISDS context
2637 * @envelope is automically allocated message envelope structure
2638 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
2639 * In case of error @envelope will be freed. */
2640 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
2641 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2642 isds_error err = IE_SUCCESS;
2643 xmlXPathObjectPtr result = NULL;
2645 if (!context) return IE_INVALID_CONTEXT;
2646 if (!envelope) return IE_INVAL;
2647 if (!xpath_ctx) return IE_INVAL;
2650 if (!*envelope) {
2651 /* Allocate envelope */
2652 *envelope = calloc(1, sizeof(**envelope));
2653 if (!*envelope) {
2654 err = IE_NOMEM;
2655 goto leave;
2657 } else {
2658 /* Else free former data */
2659 zfree((*envelope)->dmID);
2660 zfree((*envelope)->dbIDSender);
2661 zfree((*envelope)->dmSender);
2662 zfree((*envelope)->dmSenderAddress);
2663 zfree((*envelope)->dmSenderType);
2664 zfree((*envelope)->dmRecipient);
2665 zfree((*envelope)->dmRecipientAddress);
2666 zfree((*envelope)->dmAmbiguousRecipient);
2669 /* Extract envelope elements added by ISDS
2670 * (XSD: gMessageEnvelope type) */
2671 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
2672 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
2673 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
2674 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
2675 /* XML Schema does not guaratee enumratation. It's plain xs:int. */
2676 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
2677 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
2678 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
2679 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
2680 (*envelope)->dmAmbiguousRecipient);
2682 /* Extract envelope elements added by sender and ISDS
2683 * (XSD: gMessageEnvelope type) */
2684 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
2685 if (err) goto leave;
2687 leave:
2688 if (err) isds_envelope_free(envelope);
2689 xmlXPathFreeObject(result);
2690 return err;
2694 /* Convert other envelope elements from XML tree into isds_envelope structure:
2695 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
2696 * The envelope is automatically allocated but not reallocated.
2697 * The data are just appended into envelope structure.
2698 * @context is ISDS context
2699 * @envelope is automically allocated message envelope structure
2700 * @xpath_ctx is XPath context with current node as parent desired elements
2701 * In case of error @envelope will be freed. */
2702 static isds_error append_status_size_times(struct isds_ctx *context,
2703 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2704 isds_error err = IE_SUCCESS;
2705 xmlXPathObjectPtr result = NULL;
2706 char *string = NULL;
2707 unsigned long int *unumber = NULL;
2709 if (!context) return IE_INVALID_CONTEXT;
2710 if (!envelope) return IE_INVAL;
2711 if (!xpath_ctx) return IE_INVAL;
2714 if (!*envelope) {
2715 /* Allocate new */
2716 *envelope = calloc(1, sizeof(**envelope));
2717 if (!*envelope) {
2718 err = IE_NOMEM;
2719 goto leave;
2721 } else {
2722 /* Free old data */
2723 zfree((*envelope)->dmMessageStatus);
2724 zfree((*envelope)->dmAttachmentSize);
2725 zfree((*envelope)->dmDeliveryTime);
2726 zfree((*envelope)->dmAcceptanceTime);
2730 /* dmMessageStatus element is mandatory */
2731 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
2732 if (!unumber) {
2733 isds_log_message(context,
2734 _("Missing mandatory sisds:dmMessageStatus integer"));
2735 err = IE_ISDS;
2736 goto leave;
2738 err = uint2isds_message_status(context, unumber,
2739 &((*envelope)->dmMessageStatus));
2740 if (err) {
2741 if (err == IE_ENUM) err = IE_ISDS;
2742 goto leave;
2744 free(unumber); unumber = NULL;
2746 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
2749 EXTRACT_STRING("sisds:dmDeliveryTime", string);
2750 if (string) {
2751 err = timestring2timeval((xmlChar *) string,
2752 &((*envelope)->dmDeliveryTime));
2753 if (err) {
2754 char *string_locale = utf82locale(string);
2755 if (err == IE_DATE) err = IE_ISDS;
2756 isds_printf_message(context,
2757 _("Could not convert dmDeliveryTime as ISO time: %s"),
2758 string_locale);
2759 free(string_locale);
2760 goto leave;
2762 zfree(string);
2765 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
2766 if (string) {
2767 err = timestring2timeval((xmlChar *) string,
2768 &((*envelope)->dmAcceptanceTime));
2769 if (err) {
2770 char *string_locale = utf82locale(string);
2771 if (err == IE_DATE) err = IE_ISDS;
2772 isds_printf_message(context,
2773 _("Could not convert dmAcceptanceTime as ISO time: %s"),
2774 string_locale);
2775 free(string_locale);
2776 goto leave;
2778 zfree(string);
2781 leave:
2782 if (err) isds_envelope_free(envelope);
2783 free(unumber);
2784 free(string);
2785 xmlXPathFreeObject(result);
2786 return err;
2790 /* Convert message type attribute of current element into isds_envelope
2791 * structure.
2792 * TODO: This function can be incorporated into append_status_size_times() as
2793 * they are called always together.
2794 * The envelope is automatically allocated but not reallocated.
2795 * The data are just appended into envelope structure.
2796 * @context is ISDS context
2797 * @envelope is automically allocated message envelope structure
2798 * @xpath_ctx is XPath context with current node as parent of attribute
2799 * carrying message type
2800 * In case of error @envelope will be freed. */
2801 static isds_error append_message_type(struct isds_ctx *context,
2802 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2803 isds_error err = IE_SUCCESS;
2805 if (!context) return IE_INVALID_CONTEXT;
2806 if (!envelope) return IE_INVAL;
2807 if (!xpath_ctx) return IE_INVAL;
2810 if (!*envelope) {
2811 /* Allocate new */
2812 *envelope = calloc(1, sizeof(**envelope));
2813 if (!*envelope) {
2814 err = IE_NOMEM;
2815 goto leave;
2817 } else {
2818 /* Free old data */
2819 zfree((*envelope)->dmType);
2823 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope)->dmType, 0);
2825 if (!(*envelope)->dmType) {
2826 /* Use default value */
2827 (*envelope)->dmType = strdup("V");
2828 if (!(*envelope)->dmType) {
2829 err = IE_NOMEM;
2830 goto leave;
2832 } else if (1 != xmlUTF8Strlen((xmlChar *) (*envelope)->dmType)) {
2833 char *type_locale = utf82locale((*envelope)->dmType);
2834 isds_printf_message(context,
2835 _("Message type in dmType attribute is not 1 character long: "
2836 "%s"),
2837 type_locale);
2838 free(type_locale);
2839 err = IE_ISDS;
2840 goto leave;
2843 leave:
2844 if (err) isds_envelope_free(envelope);
2845 return err;
2850 /* Extract message document into reallocated document structure
2851 * @context is ISDS context
2852 * @document is automically reallocated message documents structure
2853 * @xpath_ctx is XPath context with current node as isds:dmFile
2854 * In case of error @document will be freed. */
2855 static isds_error extract_document(struct isds_ctx *context,
2856 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
2857 isds_error err = IE_SUCCESS;
2858 xmlXPathObjectPtr result = NULL;
2859 xmlNodePtr file_node = xpath_ctx->node;
2860 char *string = NULL;
2862 if (!context) return IE_INVALID_CONTEXT;
2863 if (!document) return IE_INVAL;
2864 isds_document_free(document);
2865 if (!xpath_ctx) return IE_INVAL;
2867 *document = calloc(1, sizeof(**document));
2868 if (!*document) {
2869 err = IE_NOMEM;
2870 goto leave;
2873 /* Extract document metadata */
2874 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
2876 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
2877 err = string2isds_FileMetaType((xmlChar*)string,
2878 &((*document)->dmFileMetaType));
2879 if (err) {
2880 char *meta_type_locale = utf82locale(string);
2881 isds_printf_message(context,
2882 _("Document has invalid dmFileMetaType attribute value: %s"),
2883 meta_type_locale);
2884 free(meta_type_locale);
2885 err = IE_ISDS;
2886 goto leave;
2888 zfree(string);
2890 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
2891 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
2892 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
2893 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
2896 /* Extract document data.
2897 * Base64 encoded blob or XML subtree must be presented. */
2899 /* Check from dmEncodedContent */
2900 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
2901 xpath_ctx);
2902 if (!result) {
2903 err = IE_XML;
2904 goto leave;
2907 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2908 /* Here we have Base64 blob */
2910 if (result->nodesetval->nodeNr > 1) {
2911 isds_printf_message(context,
2912 _("Document has more dmEncodedContent elements"));
2913 err = IE_ISDS;
2914 goto leave;
2917 xmlXPathFreeObject(result); result = NULL;
2918 EXTRACT_STRING("isds:dmEncodedContent", string);
2920 /* Decode non-emptys document */
2921 if (string && string[0] != '\0') {
2922 (*document)->data_length = b64decode(string, &((*document)->data));
2923 if ((*document)->data_length == (size_t) -1) {
2924 isds_printf_message(context,
2925 _("Error while Base64-decoding document content"));
2926 err = IE_ERROR;
2927 goto leave;
2930 } else {
2931 /* No Base64 blob, try XML document */
2932 xmlXPathFreeObject(result); result = NULL;
2933 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
2934 xpath_ctx);
2935 if (!result) {
2936 err = IE_XML;
2937 goto leave;
2940 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2941 /* Here we have XML document */
2943 if (result->nodesetval->nodeNr > 1) {
2944 isds_printf_message(context,
2945 _("Document has more dmXMLContent elements"));
2946 err = IE_ISDS;
2947 goto leave;
2950 /* FIXME: Serialize the tree rooted at result's node */
2951 isds_printf_message(context,
2952 _("XML documents not yet supported"));
2953 err = IE_NOTSUP;
2954 goto leave;
2955 } else {
2956 /* No bas64 blob, nor XML document */
2957 isds_printf_message(context,
2958 _("Document has no dmEncodedContent, nor dmXMLContent "
2959 "element"));
2960 err = IE_ISDS;
2961 goto leave;
2966 leave:
2967 if (err) isds_document_free(document);
2968 free(string);
2969 xmlXPathFreeObject(result);
2970 xpath_ctx->node = file_node;
2971 return err;
2976 /* Extract message documents into reallocated list of documents
2977 * @context is ISDS context
2978 * @documents is automically reallocated message documents list structure
2979 * @xpath_ctx is XPath context with current node as XSD tFilesArray
2980 * In case of error @documents will be freed. */
2981 static isds_error extract_documents(struct isds_ctx *context,
2982 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
2983 isds_error err = IE_SUCCESS;
2984 xmlXPathObjectPtr result = NULL;
2985 xmlNodePtr files_node = xpath_ctx->node;
2986 struct isds_list *document, *prev_document;
2988 if (!context) return IE_INVALID_CONTEXT;
2989 if (!documents) return IE_INVAL;
2990 isds_list_free(documents);
2991 if (!xpath_ctx) return IE_INVAL;
2993 /* Find documents */
2994 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
2995 if (!result) {
2996 err = IE_XML;
2997 goto leave;
3000 /* No match */
3001 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3002 isds_printf_message(context,
3003 _("Message does not contain any document"));
3004 err = IE_ISDS;
3005 goto leave;
3009 /* Iterate over documents */
3010 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
3012 /* Allocate and append list item */
3013 document = calloc(1, sizeof(*document));
3014 if (!document) {
3015 err = IE_NOMEM;
3016 goto leave;
3018 document->destructor = (void (*)(void **))isds_document_free;
3019 if (i == 0) *documents = document;
3020 else prev_document->next = document;
3021 prev_document = document;
3023 /* Extract document */
3024 xpath_ctx->node = result->nodesetval->nodeTab[i];
3025 err = extract_document(context,
3026 (struct isds_document **) &(document->data), xpath_ctx);
3027 if (err) goto leave;
3031 leave:
3032 if (err) isds_list_free(documents);
3033 xmlXPathFreeObject(result);
3034 xpath_ctx->node = files_node;
3035 return err;
3039 /* Convert isds:dmRecord XML tree into structure
3040 * @context is ISDS context
3041 * @envelope is automically reallocated message envelope structure
3042 * @xpath_ctx is XPath context with current node as isds:dmRecord element
3043 * In case of error @envelope will be freed. */
3044 static isds_error extract_DmRecord(struct isds_ctx *context,
3045 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3046 isds_error err = IE_SUCCESS;
3047 xmlXPathObjectPtr result = NULL;
3049 if (!context) return IE_INVALID_CONTEXT;
3050 if (!envelope) return IE_INVAL;
3051 isds_envelope_free(envelope);
3052 if (!xpath_ctx) return IE_INVAL;
3055 *envelope = calloc(1, sizeof(**envelope));
3056 if (!*envelope) {
3057 err = IE_NOMEM;
3058 goto leave;
3062 /* Extract tRecord data */
3063 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
3065 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
3066 * dmAcceptanceTime. */
3067 err = append_status_size_times(context, envelope, xpath_ctx);
3068 if (err) goto leave;
3070 /* Extract envelope elements added by sender and ISDS
3071 * (XSD: gMessageEnvelope type) */
3072 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
3073 if (err) goto leave;
3074 /* dmOVM can not be obtained from ISDS */
3076 /* Get message type */
3077 err = append_message_type(context, envelope, xpath_ctx);
3078 if (err) goto leave;
3081 leave:
3082 if (err) isds_envelope_free(envelope);
3083 xmlXPathFreeObject(result);
3084 return err;
3088 /* Find and convert isds:dmHash XML tree into structure
3089 * @context is ISDS context
3090 * @envelope is automically reallocated message hash structure
3091 * @xpath_ctx is XPath context with current node containing isds:dmHash child
3092 * In case of error @hash will be freed. */
3093 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
3094 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
3095 isds_error err = IE_SUCCESS;
3096 xmlNodePtr old_ctx_node;
3097 xmlXPathObjectPtr result = NULL;
3098 char *string = NULL;
3100 if (!context) return IE_INVALID_CONTEXT;
3101 if (!hash) return IE_INVAL;
3102 isds_hash_free(hash);
3103 if (!xpath_ctx) return IE_INVAL;
3105 old_ctx_node = xpath_ctx->node;
3107 *hash = calloc(1, sizeof(**hash));
3108 if (!*hash) {
3109 err = IE_NOMEM;
3110 goto leave;
3113 /* Locate dmHash */
3114 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
3115 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
3116 err = IE_ISDS;
3117 goto leave;
3119 if (err) {
3120 err = IE_ERROR;
3121 goto leave;
3124 /* Get hash algorithm */
3125 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
3126 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
3127 if (err) {
3128 if (err == IE_ENUM) {
3129 char *string_locale = utf82locale(string);
3130 isds_printf_message(context, _("Unsported hash algorithm: %s"),
3131 string_locale);
3132 free(string_locale);
3134 goto leave;
3136 zfree(string);
3138 /* Get hash value */
3139 EXTRACT_STRING(".", string);
3140 if (!string) {
3141 isds_printf_message(context, _("sids:dmHash element is missing hash value"));
3142 err = IE_ISDS;
3143 goto leave;
3145 (*hash)->length = b64decode(string, &((*hash)->value));
3146 if ((*hash)->length == (size_t) -1) {
3147 isds_printf_message(context,
3148 _("Error while Base64-decoding hash value"));
3149 err = IE_ERROR;
3150 goto leave;
3153 leave:
3154 if (err) isds_hash_free(hash);
3155 free(string);
3156 xmlXPathFreeObject(result);
3157 xpath_ctx->node = old_ctx_node;
3158 return err;
3162 /* Find and append isds:dmQTimestamp XML tree into envelope
3163 * @context is ISDS context
3164 * @envelope is automically allocated evnelope structure
3165 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
3166 * child
3167 * In case of error @envelope will be freed. */
3168 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
3169 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3170 isds_error err = IE_SUCCESS;
3171 xmlXPathObjectPtr result = NULL;
3172 char *string = NULL;
3174 if (!context) return IE_INVALID_CONTEXT;
3175 if (!envelope) return IE_INVAL;
3176 if (!xpath_ctx) {
3177 isds_envelope_free(envelope);
3178 return IE_INVAL;
3181 if (!*envelope) {
3182 *envelope = calloc(1, sizeof(**envelope));
3183 if (!*envelope) {
3184 err = IE_NOMEM;
3185 goto leave;
3187 } else {
3188 zfree((*envelope)->timestamp);
3189 (*envelope)->timestamp_length = 0;
3192 /* Get dmQTimestamp */
3193 EXTRACT_STRING("sisds:dmQTimestamp", string);
3194 if (!string) {
3195 isds_printf_message(context, _("Missing dmQTimestamp element content"));
3196 err = IE_ISDS;
3197 goto leave;
3199 (*envelope)->timestamp_length =
3200 b64decode(string, &((*envelope)->timestamp));
3201 if ((*envelope)->timestamp_length == (size_t) -1) {
3202 isds_printf_message(context,
3203 _("Error while Base64-decoding timestamp value"));
3204 err = IE_ERROR;
3205 goto leave;
3208 leave:
3209 if (err) isds_envelope_free(envelope);
3210 free(string);
3211 xmlXPathFreeObject(result);
3212 return err;
3216 /* Convert XSD tReturnedMessage XML tree into message structure.
3217 * It doea not store XML tree into message->raw.
3218 * @context is ISDS context
3219 * @include_documents Use true if documents must be extracted
3220 * (tReturnedMessage XSD type), use false if documents shall be ommited
3221 * (tReturnedMessageEnvelope).
3222 * @message is automically reallocated message structure
3223 * @xpath_ctx is XPath context with current node as tReturnedMessage element
3224 * type
3225 * In case of error @message will be freed. */
3226 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
3227 const _Bool include_documents, struct isds_message **message,
3228 xmlXPathContextPtr xpath_ctx) {
3229 isds_error err = IE_SUCCESS;
3230 xmlNodePtr message_node;
3232 if (!context) return IE_INVALID_CONTEXT;
3233 if (!message) return IE_INVAL;
3234 isds_message_free(message);
3235 if (!xpath_ctx) return IE_INVAL;
3238 *message = calloc(1, sizeof(**message));
3239 if (!*message) {
3240 err = IE_NOMEM;
3241 goto leave;
3244 /* Save message XPATH context node */
3245 message_node = xpath_ctx->node;
3248 /* Extract dmDM */
3249 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
3250 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
3251 if (err) { err = IE_ERROR; goto leave; }
3252 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
3253 if (err) goto leave;
3255 if (include_documents) {
3256 /* Extract dmFiles */
3257 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
3258 xpath_ctx);
3259 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
3260 err = IE_ISDS; goto leave;
3262 if (err) { err = IE_ERROR; goto leave; }
3263 err = extract_documents(context, &((*message)->documents), xpath_ctx);
3264 if (err) goto leave;
3268 /* Restore context to message */
3269 xpath_ctx->node = message_node;
3271 /* Extract dmHash */
3272 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
3273 xpath_ctx);
3274 if (err) goto leave;
3276 /* Extract dmQTimestamp, */
3277 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
3278 xpath_ctx);
3279 if (err) goto leave;
3281 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
3282 * dmAcceptanceTime. */
3283 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
3284 if (err) goto leave;
3286 /* Get message type */
3287 err = append_message_type(context, &((*message)->envelope), xpath_ctx);
3288 if (err) goto leave;
3290 leave:
3291 if (err) isds_message_free(message);
3292 return err;
3296 /* Extract message event into reallocated isds_event structure
3297 * @context is ISDS context
3298 * @event is automically reallocated message event structure
3299 * @xpath_ctx is XPath context with current node as isds:dmEvent
3300 * In case of error @event will be freed. */
3301 static isds_error extract_event(struct isds_ctx *context,
3302 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
3303 isds_error err = IE_SUCCESS;
3304 xmlXPathObjectPtr result = NULL;
3305 xmlNodePtr event_node = xpath_ctx->node;
3306 char *string = NULL;
3308 if (!context) return IE_INVALID_CONTEXT;
3309 if (!event) return IE_INVAL;
3310 isds_event_free(event);
3311 if (!xpath_ctx) return IE_INVAL;
3313 *event = calloc(1, sizeof(**event));
3314 if (!*event) {
3315 err = IE_NOMEM;
3316 goto leave;
3319 /* Extract event data.
3320 * All elements are optional according XSD. That's funny. */
3321 EXTRACT_STRING("sisds:dmEventTime", string);
3322 if (string) {
3323 err = timestring2timeval((xmlChar *) string, &((*event)->time));
3324 if (err) {
3325 char *string_locale = utf82locale(string);
3326 if (err == IE_DATE) err = IE_ISDS;
3327 isds_printf_message(context,
3328 _("Could not convert dmEventTime as ISO time: %s"),
3329 string_locale);
3330 free(string_locale);
3331 goto leave;
3333 zfree(string);
3336 /* dmEventDescr element has prefix and the rest */
3337 EXTRACT_STRING("sisds:dmEventDescr", string);
3338 if (string) {
3339 err = eventstring2event((xmlChar *) string, *event);
3340 if (err) goto leave;
3341 zfree(string);
3344 leave:
3345 if (err) isds_event_free(event);
3346 free(string);
3347 xmlXPathFreeObject(result);
3348 xpath_ctx->node = event_node;
3349 return err;
3353 /* Convert element of XSD tEventsArray type from XML tree into
3354 * isds_list of isds_event's structure. The list is automatically reallocated.
3355 * @context is ISDS context
3356 * @events is automically reallocated list of event structures
3357 * @xpath_ctx is XPath context with current node as tEventsArray
3358 * In case of error @evnets will be freed. */
3359 static isds_error extract_events(struct isds_ctx *context,
3360 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
3361 isds_error err = IE_SUCCESS;
3362 xmlXPathObjectPtr result = NULL;
3363 xmlNodePtr events_node = xpath_ctx->node;
3364 struct isds_list *event, *prev_event = NULL;
3366 if (!context) return IE_INVALID_CONTEXT;
3367 if (!events) return IE_INVAL;
3368 if (!xpath_ctx) return IE_INVAL;
3370 /* Free old list */
3371 isds_list_free(events);
3373 /* Find events */
3374 result = xmlXPathEvalExpression(BAD_CAST "sisds:dmEvent", xpath_ctx);
3375 if (!result) {
3376 err = IE_XML;
3377 goto leave;
3380 /* No match */
3381 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3382 isds_printf_message(context,
3383 _("Delivery info does not contain any event"));
3384 err = IE_ISDS;
3385 goto leave;
3389 /* Iterate over events */
3390 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
3392 /* Allocate and append list item */
3393 event = calloc(1, sizeof(*event));
3394 if (!event) {
3395 err = IE_NOMEM;
3396 goto leave;
3398 event->destructor = (void (*)(void **))isds_event_free;
3399 if (i == 0) *events = event;
3400 else prev_event->next = event;
3401 prev_event = event;
3403 /* Extract event */
3404 xpath_ctx->node = result->nodesetval->nodeTab[i];
3405 err = extract_event(context,
3406 (struct isds_event **) &(event->data), xpath_ctx);
3407 if (err) goto leave;
3411 leave:
3412 if (err) isds_list_free(events);
3413 xmlXPathFreeObject(result);
3414 xpath_ctx->node = events_node;
3415 return err;
3419 /* Convert isds_document structure into XML tree and append to dmFiles node.
3420 * @context is session context
3421 * @document is ISDS document
3422 * @dm_files is XML element the resulting tree will be appended to as a child.
3423 * @return error code, in case of error context' message is filled. */
3424 static isds_error insert_document(struct isds_ctx *context,
3425 struct isds_document *document, xmlNodePtr dm_files) {
3426 isds_error err = IE_SUCCESS;
3427 xmlNodePtr new_file = NULL, file = NULL, node;
3428 xmlAttrPtr attribute_node;
3429 xmlChar *base64data = NULL;
3431 if (!context) return IE_INVALID_CONTEXT;
3432 if (!document || !dm_files) return IE_INVAL;
3434 /* Allocate new dmFile */
3435 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
3436 if (!new_file) {
3437 isds_printf_message(context, _("Could not allocate main dmFile"));
3438 err = IE_ERROR;
3439 goto leave;
3441 /* Append the new dmFile.
3442 * XXX: Main document must go first */
3443 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
3444 file = xmlAddPrevSibling(dm_files->children, new_file);
3445 else
3446 file = xmlAddChild(dm_files, new_file);
3448 if (!file) {
3449 xmlFreeNode(new_file); new_file = NULL;
3450 isds_printf_message(context, _("Could not add dmFile child to "
3451 "%s element"), dm_files->name);
3452 err = IE_ERROR;
3453 goto leave;
3456 /* @dmMimeType is required */
3457 if (!document->dmMimeType) {
3458 isds_log_message(context,
3459 _("Document is missing mandatory MIME type definition"));
3460 err = IE_INVAL;
3461 goto leave;
3463 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
3465 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
3466 if (!string) {
3467 isds_printf_message(context,
3468 _("Document has unkown dmFileMetaType: %ld"),
3469 document->dmFileMetaType);
3470 err = IE_ENUM;
3471 goto leave;
3473 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
3475 if (document->dmFileGuid) {
3476 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
3478 if (document->dmUpFileGuid) {
3479 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
3482 /* @dmFileDescr is required */
3483 if (!document->dmFileDescr) {
3484 isds_log_message(context,
3485 _("Document is missing mandatory description (title)"));
3486 err = IE_INVAL;
3487 goto leave;
3489 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
3491 if (document->dmFormat) {
3492 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
3496 /* Insert content (data) of the document. */
3497 /* XXX; Only base64 is implemented currently. */
3498 base64data = (xmlChar *) b64encode(document->data, document->data_length);
3499 if (!base64data) {
3500 isds_printf_message(context,
3501 ngettext("Not enought memory to encode %zd bytes into Base64",
3502 "Not enought memory to encode %zd bytes into Base64",
3503 document->data_length),
3504 document->data_length);
3505 err = IE_NOMEM;
3506 goto leave;
3508 INSERT_STRING(file, "dmEncodedContent", base64data);
3509 free(base64data);
3511 leave:
3512 return err;
3516 /* Append XSD tMStatus XML tree into isds_message_copy structure.
3517 * The copy must pre prealocated, the date are just appended into structure.
3518 * @context is ISDS context
3519 * @copy is message copy struture
3520 * @xpath_ctx is XPath context with current node as tMStatus */
3521 static isds_error append_TMStatus(struct isds_ctx *context,
3522 struct isds_message_copy *copy, xmlXPathContextPtr xpath_ctx) {
3523 isds_error err = IE_SUCCESS;
3524 xmlXPathObjectPtr result = NULL;
3525 char *code = NULL, *message = NULL;
3527 if (!context) return IE_INVALID_CONTEXT;
3528 if (!copy || !xpath_ctx) return IE_INVAL;
3530 /* Free old values */
3531 zfree(copy->dmStatus);
3532 zfree(copy->dmID);
3534 /* Get error specific to this copy */
3535 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code);
3536 if (!code) {
3537 isds_log_message(context,
3538 _("Missing isds:dmStatusCode under "
3539 "XSD:tMStatus type element"));
3540 err = IE_ISDS;
3541 goto leave;
3544 if (xmlStrcmp((const xmlChar *)code, BAD_CAST "0000")) {
3545 /* This copy failed */
3546 copy->error = IE_ISDS;
3547 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message);
3548 if (message) {
3549 copy->dmStatus = astrcat3(code, ": ", message);
3550 if (!copy->dmStatus) {
3551 copy->dmStatus = code;
3552 code = NULL;
3554 } else {
3555 copy->dmStatus = code;
3556 code = NULL;
3558 } else {
3559 /* This copy succeeded. In this case only, message ID is valid */
3560 copy->error = IE_SUCCESS;
3562 EXTRACT_STRING("isds:dmID", copy->dmID);
3563 if (!copy->dmID) {
3564 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
3565 "but did not returned assigned message ID\n"));
3566 err = IE_ISDS;
3570 leave:
3571 free(code);
3572 free(message);
3573 xmlXPathFreeObject(result);
3574 return err;
3578 /* Insert struct isds_approval data (box approval) into XML tree
3579 * @context is sesstion context
3580 * @approval is libsids structure with approval description. NULL is
3581 * acceptible.
3582 * @parent is XML element to append @approval to */
3583 static isds_error insert_GExtApproval(struct isds_ctx *context,
3584 const struct isds_approval *approval, xmlNodePtr parent) {
3586 isds_error err = IE_SUCCESS;
3587 xmlNodePtr node;
3589 if (!context) return IE_INVALID_CONTEXT;
3590 if (!parent) return IE_INVAL;
3592 if (!approval) return IE_SUCCESS;
3594 /* Build XSD:gExtApproval */
3595 INSERT_SCALAR_BOOLEAN(parent, "dbApproved", approval->approved);
3596 INSERT_STRING(parent, "dbExternRefNumber", approval->refference);
3598 leave:
3599 return err;
3603 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
3604 * code
3605 * @context is session context
3606 * @service_name is name of SERVICE_DB_ACCESS
3607 * @response is server SOAP body response as XML document
3608 * @raw_response is automatically reallocated bitstream with response body. Use
3609 * NULL if you don't care
3610 * @raw_response_length is size of @raw_response in bytes
3611 * @code is ISDS status code
3612 * @status_message is ISDS status message
3613 * @return error coded from lower layer, context message will be set up
3614 * appropriately. */
3615 static isds_error build_send_check_dbdummy_request(struct isds_ctx *context,
3616 const xmlChar *service_name,
3617 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
3618 xmlChar **code, xmlChar **status_message) {
3620 isds_error err = IE_SUCCESS;
3621 char *service_name_locale = NULL;
3622 xmlNodePtr request = NULL, node;
3623 xmlNsPtr isds_ns = NULL;
3625 if (!context) return IE_INVALID_CONTEXT;
3626 if (!service_name) return IE_INVAL;
3627 if (!response || !code || !status_message) return IE_INVAL;
3628 if (!raw_response_length && raw_response) return IE_INVAL;
3630 /* Free output argument */
3631 xmlFreeDoc(*response); *response = NULL;
3632 if (raw_response) zfree(*raw_response);
3633 free(*code);
3634 free(*status_message);
3637 /* Check if connection is established
3638 * TODO: This check should be done donwstairs. */
3639 if (!context->curl) return IE_CONNECTION_CLOSED;
3641 service_name_locale = utf82locale((char*)service_name);
3642 if (!service_name_locale) {
3643 err = IE_NOMEM;
3644 goto leave;
3647 /* Build request */
3648 request = xmlNewNode(NULL, service_name);
3649 if (!request) {
3650 isds_printf_message(context,
3651 _("Could not build %s request"), service_name_locale);
3652 err = IE_ERROR;
3653 goto leave;
3655 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
3656 if(!isds_ns) {
3657 isds_log_message(context, _("Could not create ISDS name space"));
3658 err = IE_ERROR;
3659 goto leave;
3661 xmlSetNs(request, isds_ns);
3664 /* Add XSD:tDummyInput child */
3665 INSERT_STRING(request, "dbDummy", NULL);
3668 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
3669 service_name_locale);
3671 /* Send request */
3672 err = isds(context, SERVICE_DB_ACCESS, request, response,
3673 raw_response, raw_response_length);
3674 xmlFreeNode(request); request = NULL;
3676 if (err) {
3677 isds_log(ILF_ISDS, ILL_DEBUG,
3678 _("Processing ISDS response on %s request failed\n"),
3679 service_name_locale);
3680 goto leave;
3683 /* Check for response status */
3684 err = isds_response_status(context, SERVICE_DB_ACCESS, *response,
3685 code, status_message, NULL);
3686 if (err) {
3687 isds_log(ILF_ISDS, ILL_DEBUG,
3688 _("ISDS response on %s request is missing status\n"),
3689 service_name_locale);
3690 goto leave;
3693 /* Request processed, but nothing found */
3694 if (xmlStrcmp(*code, BAD_CAST "0000")) {
3695 char *code_locale = utf82locale((char*) *code);
3696 char *status_message_locale = utf82locale((char*) *status_message);
3697 isds_log(ILF_ISDS, ILL_DEBUG,
3698 _("Server refused %s request (code=%s, message=%s)\n"),
3699 service_name_locale, code_locale, status_message_locale);
3700 isds_log_message(context, status_message_locale);
3701 free(code_locale);
3702 free(status_message_locale);
3703 err = IE_ISDS;
3704 goto leave;
3707 leave:
3708 free(service_name_locale);
3709 xmlFreeNode(request);
3710 return err;
3714 /* Get data about logged in user and his box. */
3715 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
3716 struct isds_DbOwnerInfo **db_owner_info) {
3717 isds_error err = IE_SUCCESS;
3718 xmlDocPtr response = NULL;
3719 xmlChar *code = NULL, *message = NULL;
3720 xmlXPathContextPtr xpath_ctx = NULL;
3721 xmlXPathObjectPtr result = NULL;
3722 char *string = NULL;
3724 if (!context) return IE_INVALID_CONTEXT;
3725 if (!db_owner_info) return IE_INVAL;
3727 /* Check if connection is established */
3728 if (!context->curl) return IE_CONNECTION_CLOSED;
3731 /* Do request and check for success */
3732 err = build_send_check_dbdummy_request(context,
3733 BAD_CAST "GetOwnerInfoFromLogin",
3734 &response, NULL, NULL, &code, &message);
3735 if (err) goto leave;
3738 /* Extract data */
3739 /* Prepare stucture */
3740 isds_DbOwnerInfo_free(db_owner_info);
3741 *db_owner_info = calloc(1, sizeof(**db_owner_info));
3742 if (!*db_owner_info) {
3743 err = IE_NOMEM;
3744 goto leave;
3746 xpath_ctx = xmlXPathNewContext(response);
3747 if (!xpath_ctx) {
3748 err = IE_ERROR;
3749 goto leave;
3751 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
3752 err = IE_ERROR;
3753 goto leave;
3756 /* Set context node */
3757 result = xmlXPathEvalExpression(BAD_CAST
3758 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
3759 if (!result) {
3760 err = IE_ERROR;
3761 goto leave;
3763 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3764 isds_log_message(context, _("Missing dbOwnerInfo element"));
3765 err = IE_ISDS;
3766 goto leave;
3768 if (result->nodesetval->nodeNr > 1) {
3769 isds_log_message(context, _("Multiple dbOwnerInfo element"));
3770 err = IE_ISDS;
3771 goto leave;
3773 xpath_ctx->node = result->nodesetval->nodeTab[0];
3774 xmlXPathFreeObject(result); result = NULL;
3776 /* Extract it */
3777 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
3779 leave:
3780 if (err) {
3781 isds_DbOwnerInfo_free(db_owner_info);
3784 free(string);
3785 xmlXPathFreeObject(result);
3786 xmlXPathFreeContext(xpath_ctx);
3788 free(code);
3789 free(message);
3790 xmlFreeDoc(response);
3792 if (!err)
3793 isds_log(ILF_ISDS, ILL_DEBUG,
3794 _("GetOwnerInfoFromLogin request processed by server "
3795 "successfully.\n"));
3797 return err;
3801 /* Get data about logged in user. */
3802 isds_error isds_GetUserInfoFromLogin(struct isds_ctx *context,
3803 struct isds_DbUserInfo **db_user_info) {
3804 isds_error err = IE_SUCCESS;
3805 xmlDocPtr response = NULL;
3806 xmlChar *code = NULL, *message = NULL;
3807 xmlXPathContextPtr xpath_ctx = NULL;
3808 xmlXPathObjectPtr result = NULL;
3810 if (!context) return IE_INVALID_CONTEXT;
3811 if (!db_user_info) return IE_INVAL;
3813 /* Check if connection is established */
3814 if (!context->curl) return IE_CONNECTION_CLOSED;
3817 /* Do request and check for success */
3818 err = build_send_check_dbdummy_request(context,
3819 BAD_CAST "GetUserInfoFromLogin",
3820 &response, NULL, NULL, &code, &message);
3821 if (err) goto leave;
3824 /* Extract data */
3825 /* Prepare stucture */
3826 isds_DbUserInfo_free(db_user_info);
3827 *db_user_info = calloc(1, sizeof(**db_user_info));
3828 if (!*db_user_info) {
3829 err = IE_NOMEM;
3830 goto leave;
3832 xpath_ctx = xmlXPathNewContext(response);
3833 if (!xpath_ctx) {
3834 err = IE_ERROR;
3835 goto leave;
3837 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
3838 err = IE_ERROR;
3839 goto leave;
3842 /* Set context node */
3843 result = xmlXPathEvalExpression(BAD_CAST
3844 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx);
3845 if (!result) {
3846 err = IE_ERROR;
3847 goto leave;
3849 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3850 isds_log_message(context, _("Missing dbUserInfo element"));
3851 err = IE_ISDS;
3852 goto leave;
3854 if (result->nodesetval->nodeNr > 1) {
3855 isds_log_message(context, _("Multiple dbUserInfo element"));
3856 err = IE_ISDS;
3857 goto leave;
3859 xpath_ctx->node = result->nodesetval->nodeTab[0];
3860 xmlXPathFreeObject(result); result = NULL;
3862 /* Extract it */
3863 err = extract_DbUserInfo(context, db_user_info, xpath_ctx);
3865 leave:
3866 if (err) {
3867 isds_DbUserInfo_free(db_user_info);
3870 xmlXPathFreeObject(result);
3871 xmlXPathFreeContext(xpath_ctx);
3873 free(code);
3874 free(message);
3875 xmlFreeDoc(response);
3877 if (!err)
3878 isds_log(ILF_ISDS, ILL_DEBUG,
3879 _("GetUserInfoFromLogin request processed by server "
3880 "successfully.\n"));
3882 return err;
3886 /* Get expiration time of current password
3887 * @context is session context
3888 * @expiration is automatically reallocated time when password expires, In
3889 * case of error will be nulled. */
3890 isds_error isds_get_password_expiration(struct isds_ctx *context,
3891 struct timeval **expiration) {
3892 isds_error err = IE_SUCCESS;
3893 xmlDocPtr response = NULL;
3894 xmlChar *code = NULL, *message = NULL;
3895 xmlXPathContextPtr xpath_ctx = NULL;
3896 xmlXPathObjectPtr result = NULL;
3897 char *string = NULL;
3899 if (!context) return IE_INVALID_CONTEXT;
3900 if (!expiration) return IE_INVAL;
3902 /* Check if connection is established */
3903 if (!context->curl) return IE_CONNECTION_CLOSED;
3906 /* Do request and check for success */
3907 err = build_send_check_dbdummy_request(context,
3908 BAD_CAST "GetPasswordInfo",
3909 &response, NULL, NULL, &code, &message);
3910 if (err) goto leave;
3913 /* Extract data */
3914 xpath_ctx = xmlXPathNewContext(response);
3915 if (!xpath_ctx) {
3916 err = IE_ERROR;
3917 goto leave;
3919 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
3920 err = IE_ERROR;
3921 goto leave;
3924 /* Set context node */
3925 result = xmlXPathEvalExpression(BAD_CAST
3926 "/isds:GetPasswordInfoResponse", xpath_ctx);
3927 if (!result) {
3928 err = IE_ERROR;
3929 goto leave;
3931 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3932 isds_log_message(context,
3933 _("Missing GetPasswordInfoResponse element"));
3934 err = IE_ISDS;
3935 goto leave;
3937 if (result->nodesetval->nodeNr > 1) {
3938 isds_log_message(context,
3939 _("Multiple GetPasswordInfoResponse element"));
3940 err = IE_ISDS;
3941 goto leave;
3943 xpath_ctx->node = result->nodesetval->nodeTab[0];
3944 xmlXPathFreeObject(result); result = NULL;
3946 /* Extract expiration date */
3947 EXTRACT_STRING("isds:pswExpDate", string);
3948 if (!string) {
3949 isds_log_message(context, _("Missing pswExpDate element"));
3950 err = IE_ISDS;
3951 goto leave;
3954 err = timestring2timeval((xmlChar *) string, expiration);
3955 if (err) {
3956 char *string_locale = utf82locale(string);
3957 if (err == IE_DATE) err = IE_ISDS;
3958 isds_printf_message(context,
3959 _("Could not convert pswExpDate as ISO time: %s"),
3960 string_locale);
3961 free(string_locale);
3962 goto leave;
3965 leave:
3966 if (err) {
3967 if (*expiration) {
3968 zfree(*expiration);
3972 free(string);
3973 xmlXPathFreeObject(result);
3974 xmlXPathFreeContext(xpath_ctx);
3976 free(code);
3977 free(message);
3978 xmlFreeDoc(response);
3980 if (!err)
3981 isds_log(ILF_ISDS, ILL_DEBUG,
3982 _("GetPasswordInfo request processed by server "
3983 "successfully.\n"));
3985 return err;
3989 /* Change user password in ISDS.
3990 * User must supply old password, new password will takes effect after some
3991 * time, current session can continue. Password must fulfill some constraints.
3992 * @context is session context
3993 * @old_password is current password.
3994 * @new_password is requested new password */
3995 isds_error isds_change_password(struct isds_ctx *context,
3996 const char *old_password, const char *new_password) {
3997 isds_error err = IE_SUCCESS;
3998 xmlNsPtr isds_ns = NULL;
3999 xmlNodePtr request = NULL, node;
4000 xmlDocPtr response = NULL;
4001 xmlChar *code = NULL, *message = NULL;
4003 if (!context) return IE_INVALID_CONTEXT;
4004 if (!old_password || !new_password) return IE_INVAL;
4006 /* Check if connection is established
4007 * TODO: This check should be done donwstairs. */
4008 if (!context->curl) return IE_CONNECTION_CLOSED;
4011 /* Build ChangeISDSPassword request */
4012 request = xmlNewNode(NULL, BAD_CAST "ChangeISDSPassword");
4013 if (!request) {
4014 isds_log_message(context,
4015 _("Could not build ChangeISDSPassword request"));
4016 return IE_ERROR;
4018 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4019 if(!isds_ns) {
4020 isds_log_message(context, _("Could not create ISDS name space"));
4021 xmlFreeNode(request);
4022 return IE_ERROR;
4024 xmlSetNs(request, isds_ns);
4026 INSERT_STRING(request, "dbOldPassword", old_password);
4027 INSERT_STRING(request, "dbNewPassword", new_password);
4030 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CheckDataBox request to ISDS\n"));
4032 /* Sent request */
4033 err = isds(context, SERVICE_DB_ACCESS, request, &response, NULL, NULL);
4035 /* Destroy request */
4036 xmlFreeNode(request); request = NULL;
4038 if (err) {
4039 isds_log(ILF_ISDS, ILL_DEBUG,
4040 _("Processing ISDS response on ChangeISDSPassword "
4041 "request failed\n"));
4042 goto leave;
4045 /* Check for response status */
4046 err = isds_response_status(context, SERVICE_DB_ACCESS, response,
4047 &code, &message, NULL);
4048 if (err) {
4049 isds_log(ILF_ISDS, ILL_DEBUG,
4050 _("ISDS response on ChangeISDSPassword request is missing "
4051 "status\n"));
4052 goto leave;
4055 /* Request processed, but empty password refused */
4056 if (!xmlStrcmp(code, BAD_CAST "1066")) {
4057 char *code_locale = utf82locale((char*)code);
4058 char *message_locale = utf82locale((char*)message);
4059 isds_log(ILF_ISDS, ILL_DEBUG,
4060 _("Server refused empty password on ChangeISDSPassword "
4061 "request (code=%s, message=%s)\n"),
4062 code_locale, message_locale);
4063 isds_log_message(context, _("Password must not be empty"));
4064 free(code_locale);
4065 free(message_locale);
4066 err = IE_INVAL;
4067 goto leave;
4070 /* Request processed, but new password was reused */
4071 else if (!xmlStrcmp(code, BAD_CAST "1067")) {
4072 char *code_locale = utf82locale((char*)code);
4073 char *message_locale = utf82locale((char*)message);
4074 isds_log(ILF_ISDS, ILL_DEBUG,
4075 _("Server refused the same new password on ChangeISDSPassword "
4076 "request (code=%s, message=%s)\n"),
4077 code_locale, message_locale);
4078 isds_log_message(context,
4079 _("New password must differ from the current one"));
4080 free(code_locale);
4081 free(message_locale);
4082 err = IE_INVAL;
4083 goto leave;
4086 /* Other error */
4087 else if (xmlStrcmp(code, BAD_CAST "0000")) {
4088 char *code_locale = utf82locale((char*)code);
4089 char *message_locale = utf82locale((char*)message);
4090 isds_log(ILF_ISDS, ILL_DEBUG,
4091 _("Server refused to change password on ChangeISDSPassword "
4092 "request (code=%s, message=%s)\n"),
4093 code_locale, message_locale);
4094 isds_log_message(context, message_locale);
4095 free(code_locale);
4096 free(message_locale);
4097 err = IE_ISDS;
4098 goto leave;
4101 /* Otherwise password changed successfully */
4103 leave:
4104 free(code);
4105 free(message);
4106 xmlFreeDoc(response);
4107 xmlFreeNode(request);
4109 if (!err)
4110 isds_log(ILF_ISDS, ILL_DEBUG,
4111 _("Password changed successfully on ChangeISDSPassword "
4112 "request.\n"));
4114 return err;
4118 /* Generic middle part with request sending and response check.
4119 * It sends prepared request and checks for error code.
4120 * @context is ISDS session context.
4121 * @service is ISDS service handler
4122 * @service_name is name in scope of given @service
4123 * @request is XML tree with request. Will be freed to save memory.
4124 * @response is XML document ouputing ISDS response.
4125 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4126 * NULL, if you don't care. */
4127 static isds_error send_destroy_request_check_response(
4128 struct isds_ctx *context,
4129 const isds_service service, const xmlChar *service_name,
4130 xmlNodePtr *request, xmlDocPtr *response, xmlChar **refnumber) {
4131 isds_error err = IE_SUCCESS;
4132 char *service_name_locale = NULL;
4133 xmlChar *code = NULL, *message = NULL;
4136 if (!context) return IE_INVALID_CONTEXT;
4137 if (!service_name || *service_name == '\0' || !request || !*request ||
4138 !response)
4139 return IE_INVAL;
4141 /* Check if connection is established
4142 * TODO: This check should be done donwstairs. */
4143 if (!context->curl) return IE_CONNECTION_CLOSED;
4145 service_name_locale = utf82locale((char*) service_name);
4146 if (!service_name_locale) {
4147 err = IE_NOMEM;
4148 goto leave;
4151 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
4152 service_name_locale);
4154 /* Send request */
4155 err = isds(context, service, *request, response, NULL, NULL);
4156 xmlFreeNode(*request); *request = NULL;
4158 if (err) {
4159 isds_log(ILF_ISDS, ILL_DEBUG,
4160 _("Processing ISDS response on %s request failed\n"),
4161 service_name_locale);
4162 goto leave;
4165 /* Check for response status */
4166 err = isds_response_status(context, service, *response,
4167 &code, &message, refnumber);
4168 if (err) {
4169 isds_log(ILF_ISDS, ILL_DEBUG,
4170 _("ISDS response on %s request is missing status\n"),
4171 service_name_locale);
4172 goto leave;
4175 /* Request processed, but server failed */
4176 if (xmlStrcmp(code, BAD_CAST "0000")) {
4177 char *code_locale = utf82locale((char*) code);
4178 char *message_locale = utf82locale((char*) message);
4179 isds_log(ILF_ISDS, ILL_DEBUG,
4180 _("Server refused %s request (code=%s, message=%s)\n"),
4181 service_name_locale, code_locale, message_locale);
4182 isds_log_message(context, message_locale);
4183 free(code_locale);
4184 free(message_locale);
4185 err = IE_ISDS;
4186 goto leave;
4190 leave:
4191 free(code);
4192 free(message);
4193 if (err && *response) {
4194 xmlFreeDoc(*response);
4195 *response = NULL;
4197 if (*request) {
4198 xmlFreeNode(*request);
4199 *request = NULL;
4201 free(service_name_locale);
4203 return err;
4207 /* Generic bottom half with request sending.
4208 * It sends prepared request, checks for error code, destroys response and
4209 * request and log success or failure.
4210 * @context is ISDS session context.
4211 * @service is ISDS service handler
4212 * @service_name is name in scope of given @service
4213 * @request is XML tree with request. Will be freed to save memory.
4214 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4215 * NULL, if you don't care. */
4216 static isds_error send_request_check_drop_response(
4217 struct isds_ctx *context,
4218 const isds_service service, const xmlChar *service_name,
4219 xmlNodePtr *request, xmlChar **refnumber) {
4220 isds_error err = IE_SUCCESS;
4221 xmlDocPtr response = NULL;
4224 if (!context) return IE_INVALID_CONTEXT;
4225 if (!service_name || *service_name == '\0' || !request || !*request)
4226 return IE_INVAL;
4228 /* Send request and check response*/
4229 err = send_destroy_request_check_response(context,
4230 service, service_name, request, &response, refnumber);
4232 xmlFreeDoc(response);
4234 if (*request) {
4235 xmlFreeNode(*request);
4236 *request = NULL;
4239 if (!err) {
4240 char *service_name_locale = utf82locale((char *) service_name);
4241 isds_log(ILF_ISDS, ILL_DEBUG,
4242 _("%s request processed by server successfully.\n"),
4243 service_name_locale);
4244 free(service_name_locale);
4247 return err;
4251 /* Build XSD:tCreateDBInput request type for box createing.
4252 * @context is session context
4253 * @request outputs built XML tree
4254 * @service_name is request name of SERVICE_DB_MANIPULATION service
4255 * @box is box description to create including single primary user (in case of
4256 * FO box type)
4257 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
4258 * box, or contact address of PFO box owner)
4259 * @former_names is optional undocumented string. Pass NULL if you don't care.
4260 * @upper_box_id is optional ID of supper box if currently created box is
4261 * subordinated.
4262 * @ceo_label is optional title of OVM box owner (e.g. mayor) NULL, if you
4263 * don't care.
4264 * @approval is optional external approval of box manipulation */
4265 static isds_error build_CreateDBInput_request(struct isds_ctx *context,
4266 xmlNodePtr *request, const xmlChar *service_name,
4267 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
4268 const xmlChar *former_names, const xmlChar *upper_box_id,
4269 const xmlChar *ceo_label, const struct isds_approval *approval) {
4270 isds_error err = IE_SUCCESS;
4271 xmlNsPtr isds_ns = NULL;
4272 xmlNodePtr node, dbPrimaryUsers;
4273 xmlChar *string = NULL;
4274 const struct isds_list *item;
4277 if (!context) return IE_INVALID_CONTEXT;
4278 if (!request || !service_name || service_name[0] == '\0' || !box)
4279 return IE_INVAL;
4282 /* Build DeleteDataBox request */
4283 *request = xmlNewNode(NULL, service_name);
4284 if (!*request) {
4285 char *service_name_locale = utf82locale((char*) service_name);
4286 isds_printf_message(context, _("Could build %s request"),
4287 service_name_locale);
4288 free(service_name_locale);
4289 return IE_ERROR;
4291 isds_ns = xmlNewNs(*request, BAD_CAST ISDS_NS, NULL);
4292 if (!isds_ns) {
4293 isds_log_message(context, _("Could not create ISDS name space"));
4294 xmlFreeNode(*request);
4295 return IE_ERROR;
4297 xmlSetNs(*request, isds_ns);
4299 INSERT_ELEMENT(node, *request, "dbOwnerInfo");
4300 err = insert_DbOwnerInfo(context, box, node);
4301 if (err) goto leave;
4303 /* Insert users */
4304 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
4305 * verbose documentatiot allows none dbUserInfo */
4306 INSERT_ELEMENT(dbPrimaryUsers, *request, "dbPrimaryUsers");
4307 for (item = users; item; item = item->next) {
4308 if (item->data) {
4309 INSERT_ELEMENT(node, dbPrimaryUsers, "dbUserInfo");
4310 err = insert_DbUserInfo(context,
4311 (struct isds_DbUserInfo *) item->data, node);
4312 if (err) goto leave;
4316 INSERT_STRING(*request, "dbFormerNames", former_names);
4317 INSERT_STRING(*request, "dbUpperDBId", upper_box_id);
4318 INSERT_STRING(*request, "dbCEOLabel", ceo_label);
4320 err = insert_GExtApproval(context, approval, *request);
4321 if (err) goto leave;
4323 leave:
4324 if (err) {
4325 xmlFreeNode(*request);
4326 *request = NULL;
4328 free(string);
4329 return err;
4333 /* Create new box.
4334 * @context is session context
4335 * @box is box description to create including single primary user (in case of
4336 * FO box type). It outputs box ID assigned by ISDS in dbID element.
4337 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
4338 * box, or contact address of PFO box owner)
4339 * @former_names is optional undocumented string. Pass NULL if you don't care.
4340 * @upper_box_id is optional ID of supper box if currently created box is
4341 * subordinated.
4342 * @ceo_label is optional title of OVM box owner (e.g. mayor)
4343 * @approval is optional external approval of box manipulation
4344 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4345 * NULL, if you don't care.*/
4346 isds_error isds_add_box(struct isds_ctx *context,
4347 struct isds_DbOwnerInfo *box, const struct isds_list *users,
4348 const char *former_names, const char *upper_box_id,
4349 const char *ceo_label, const struct isds_approval *approval,
4350 char **refnumber) {
4351 isds_error err = IE_SUCCESS;
4352 xmlNodePtr request = NULL;
4353 xmlDocPtr response = NULL;
4354 xmlXPathContextPtr xpath_ctx = NULL;
4355 xmlXPathObjectPtr result = NULL;
4358 if (!context) return IE_INVALID_CONTEXT;
4359 if (!box) return IE_INVAL;
4361 /* Scratch box ID */
4362 zfree(box->dbID);
4364 /* Build CreateDataBox request */
4365 err = build_CreateDBInput_request(context,
4366 &request, BAD_CAST "CreateDataBox",
4367 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
4368 (xmlChar *) ceo_label, approval);
4369 if (err) goto leave;
4371 /* Send it to server and process response */
4372 err = send_destroy_request_check_response(context,
4373 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
4374 &response, (xmlChar **) refnumber);
4376 /* Extract box ID */
4377 xpath_ctx = xmlXPathNewContext(response);
4378 if (!xpath_ctx) {
4379 err = IE_ERROR;
4380 goto leave;
4382 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4383 err = IE_ERROR;
4384 goto leave;
4386 EXTRACT_STRING("/isds:CreateDataBoxResponse/dbID", box->dbID);
4388 leave:
4389 xmlXPathFreeObject(result);
4390 xmlXPathFreeContext(xpath_ctx);
4391 xmlFreeDoc(response);
4392 xmlFreeNode(request);
4394 if (!err) {
4395 isds_log(ILF_ISDS, ILL_DEBUG,
4396 _("CreateDataBox request processed by server successfully.\n"));
4399 return err;
4403 /* Notify ISDS about new PFO entity.
4404 * This function has no real effect.
4405 * @context is session context
4406 * @box is PFO description including single primary user.
4407 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
4408 * @former_names is optional undocumented string. Pass NULL if you don't care.
4409 * @upper_box_id is optional ID of supper box if currently created box is
4410 * subordinated.
4411 * @ceo_label is optional title of OVM box owner (e.g. mayor)
4412 * @approval is optional external approval of box manipulation
4413 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4414 * NULL, if you don't care.*/
4415 isds_error isds_add_pfoinfo(struct isds_ctx *context,
4416 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
4417 const char *former_names, const char *upper_box_id,
4418 const char *ceo_label, const struct isds_approval *approval,
4419 char **refnumber) {
4420 isds_error err = IE_SUCCESS;
4421 xmlNodePtr request = NULL;
4423 if (!context) return IE_INVALID_CONTEXT;
4424 if (!box) return IE_INVAL;
4426 /* Build CreateDataBoxPFOInfo request */
4427 err = build_CreateDBInput_request(context,
4428 &request, BAD_CAST "CreateDataBoxPFOInfo",
4429 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
4430 (xmlChar *) ceo_label, approval);
4431 if (err) goto leave;
4433 /* Send it to server and process response */
4434 err = send_request_check_drop_response(context,
4435 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
4436 (xmlChar **) refnumber);
4437 leave:
4438 xmlFreeNode(request);
4439 return err;
4443 /* Remove given given box permanetly.
4444 * @context is session context
4445 * @box is box description to delete
4446 * @since is date of box owner cancalation. Only tm_year, tm_mon and tm_mday
4447 * carry sane value.
4448 * @approval is optional external approval of box manipulation
4449 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4450 * NULL, if you don't care.*/
4451 isds_error isds_delete_box(struct isds_ctx *context,
4452 const struct isds_DbOwnerInfo *box, const struct tm *since,
4453 const struct isds_approval *approval, char **refnumber) {
4454 isds_error err = IE_SUCCESS;
4455 xmlNsPtr isds_ns = NULL;
4456 xmlNodePtr request = NULL;
4457 xmlNodePtr node;
4458 xmlChar *string = NULL;
4461 if (!context) return IE_INVALID_CONTEXT;
4462 if (!box || !since) return IE_INVAL;
4465 /* Build DeleteDataBox request */
4466 request = xmlNewNode(NULL, BAD_CAST "DeleteDataBox");
4467 if (!request) {
4468 isds_log_message(context,
4469 _("Could build DeleteDataBox request"));
4470 return IE_ERROR;
4472 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4473 if(!isds_ns) {
4474 isds_log_message(context, _("Could not create ISDS name space"));
4475 xmlFreeNode(request);
4476 return IE_ERROR;
4478 xmlSetNs(request, isds_ns);
4480 INSERT_ELEMENT(node, request, "dbOwnerInfo");
4481 err = insert_DbOwnerInfo(context, box, node);
4482 if (err) goto leave;
4484 err = tm2datestring(since, &string);
4485 if (err) {
4486 isds_log_message(context,
4487 _("Could not convert `since' argument to ISO date string"));
4488 goto leave;
4490 INSERT_STRING(request, "dbOwnerTerminationDate", string);
4491 zfree(string);
4493 err = insert_GExtApproval(context, approval, request);
4494 if (err) goto leave;
4497 /* Send it to server and process response */
4498 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
4499 BAD_CAST "DeleteDataBox", &request, (xmlChar **) refnumber);
4501 leave:
4502 xmlFreeNode(request);
4503 free(string);
4504 return err;
4508 /* Update data about given box.
4509 * @context is session context
4510 * @old_box current box description
4511 * @new_box are updated data about @old_box
4512 * @approval is optional external approval of box manipulation
4513 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4514 * NULL, if you don't care.*/
4515 isds_error isds_UpdateDataBoxDescr(struct isds_ctx *context,
4516 const struct isds_DbOwnerInfo *old_box,
4517 const struct isds_DbOwnerInfo *new_box,
4518 const struct isds_approval *approval, char **refnumber) {
4519 isds_error err = IE_SUCCESS;
4520 xmlNsPtr isds_ns = NULL;
4521 xmlNodePtr request = NULL;
4522 xmlNodePtr node;
4525 if (!context) return IE_INVALID_CONTEXT;
4526 if (!old_box || !new_box) return IE_INVAL;
4529 /* Build UpdateDataBoxDescr request */
4530 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxDescr");
4531 if (!request) {
4532 isds_log_message(context,
4533 _("Could build UpdateDataBoxDescr request"));
4534 return IE_ERROR;
4536 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4537 if(!isds_ns) {
4538 isds_log_message(context, _("Could not create ISDS name space"));
4539 xmlFreeNode(request);
4540 return IE_ERROR;
4542 xmlSetNs(request, isds_ns);
4544 INSERT_ELEMENT(node, request, "dbOldOwnerInfo");
4545 err = insert_DbOwnerInfo(context, old_box, node);
4546 if (err) goto leave;
4548 INSERT_ELEMENT(node, request, "dbNewOwnerInfo");
4549 err = insert_DbOwnerInfo(context, new_box, node);
4550 if (err) goto leave;
4552 err = insert_GExtApproval(context, approval, request);
4553 if (err) goto leave;
4556 /* Send it to server and process response */
4557 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
4558 BAD_CAST "UpdateDataBoxDescr", &request, (xmlChar **) refnumber);
4560 leave:
4561 xmlFreeNode(request);
4563 return err;
4567 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
4568 * code
4569 * @context is session context
4570 * @service is SOAP service
4571 * @service_name is name of request in @service
4572 * @box_id is box ID of interrest
4573 * @approval is optional external approval of box manipulation
4574 * @response is server SOAP body response as XML document
4575 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4576 * NULL, if you don't care.
4577 * @return error coded from lower layer, context message will be set up
4578 * appropriately. */
4579 static isds_error build_send_dbid_request_check_response(
4580 struct isds_ctx *context, const isds_service service,
4581 const xmlChar *service_name, const xmlChar *box_id,
4582 const struct isds_approval *approval,
4583 xmlDocPtr *response, xmlChar **refnumber) {
4585 isds_error err = IE_SUCCESS;
4586 char *service_name_locale = NULL, *box_id_locale = NULL;
4587 xmlNodePtr request = NULL, node;
4588 xmlNsPtr isds_ns = NULL;
4590 if (!context) return IE_INVALID_CONTEXT;
4591 if (!service_name || !box_id) return IE_INVAL;
4592 if (!response) return IE_INVAL;
4594 /* Free output argument */
4595 xmlFreeDoc(*response); *response = NULL;
4597 /* Prepare strings */
4598 service_name_locale = utf82locale((char*)service_name);
4599 if (!service_name_locale) {
4600 err = IE_NOMEM;
4601 goto leave;
4603 box_id_locale = utf82locale((char*)box_id);
4604 if (!box_id_locale) {
4605 err = IE_NOMEM;
4606 goto leave;
4609 /* Build request */
4610 request = xmlNewNode(NULL, service_name);
4611 if (!request) {
4612 isds_printf_message(context,
4613 _("Could not build %s request"), service_name_locale);
4614 err = IE_ERROR;
4615 goto leave;
4617 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4618 if(!isds_ns) {
4619 isds_log_message(context, _("Could not create ISDS name space"));
4620 err = IE_ERROR;
4621 goto leave;
4623 xmlSetNs(request, isds_ns);
4625 /* Add XSD:tIdDbInput childs*/
4626 INSERT_STRING(request, "dbID", box_id);
4627 err = insert_GExtApproval(context, approval, request);
4628 if (err) goto leave;
4630 /* Send request and check response*/
4631 err = send_destroy_request_check_response(context,
4632 service, service_name, &request, response, refnumber);
4634 leave:
4635 free(service_name_locale);
4636 free(box_id_locale);
4637 xmlFreeNode(request);
4638 return err;
4642 /* Get data about all users assigned to given box.
4643 * @context is session context
4644 * @box_id is box ID
4645 * @users is automatically reallocated list of struct isds_DbUserInfo */
4646 isds_error isds_GetDataBoxUsers(struct isds_ctx *context, const char *box_id,
4647 struct isds_list **users) {
4648 isds_error err = IE_SUCCESS;
4649 xmlDocPtr response = NULL;
4650 xmlXPathContextPtr xpath_ctx = NULL;
4651 xmlXPathObjectPtr result = NULL;
4652 int i;
4653 struct isds_list *item, *prev_item = NULL;
4655 if (!context) return IE_INVALID_CONTEXT;
4656 if (!users || !box_id) return IE_INVAL;
4659 /* Do request and check for success */
4660 err = build_send_dbid_request_check_response(context,
4661 SERVICE_DB_MANIPULATION, BAD_CAST "GetDataBoxUsers",
4662 BAD_CAST box_id, NULL, &response, NULL);
4663 if (err) goto leave;
4666 /* Extract data */
4667 /* Prepare stucture */
4668 isds_list_free(users);
4669 xpath_ctx = xmlXPathNewContext(response);
4670 if (!xpath_ctx) {
4671 err = IE_ERROR;
4672 goto leave;
4674 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4675 err = IE_ERROR;
4676 goto leave;
4679 /* Set context node */
4680 result = xmlXPathEvalExpression(BAD_CAST
4681 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
4682 xpath_ctx);
4683 if (!result) {
4684 err = IE_ERROR;
4685 goto leave;
4687 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4688 isds_log_message(context, _("Missing dbUserInfo element"));
4689 err = IE_ISDS;
4690 goto leave;
4693 /* Iterate over all users */
4694 for (i = 0; i < result->nodesetval->nodeNr; i++) {
4696 /* Prepare structure */
4697 item = calloc(1, sizeof(*item));
4698 if (!item) {
4699 err = IE_NOMEM;
4700 goto leave;
4702 item->destructor = (void(*)(void**))isds_DbUserInfo_free;
4703 if (i == 0) *users = item;
4704 else prev_item->next = item;
4705 prev_item = item;
4707 /* Extract it */
4708 xpath_ctx->node = result->nodesetval->nodeTab[i];
4709 err = extract_DbUserInfo(context,
4710 (struct isds_DbUserInfo **) (&item->data), xpath_ctx);
4711 if (err) goto leave;
4714 leave:
4715 if (err) {
4716 isds_list_free(users);
4719 xmlXPathFreeObject(result);
4720 xmlXPathFreeContext(xpath_ctx);
4721 xmlFreeDoc(response);
4723 if (!err)
4724 isds_log(ILF_ISDS, ILL_DEBUG,
4725 _("GetDataBoxUsers request processed by server "
4726 "successfully.\n"));
4728 return err;
4732 /* Update data about user assigned to given box.
4733 * @context is session context
4734 * @box is box identification
4735 * @old_user identifies user to update
4736 * @new_user are updated data about @old_user
4737 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4738 * NULL, if you don't care.*/
4739 isds_error isds_UpdateDataBoxUser(struct isds_ctx *context,
4740 const struct isds_DbOwnerInfo *box,
4741 const struct isds_DbUserInfo *old_user,
4742 const struct isds_DbUserInfo *new_user,
4743 char **refnumber) {
4744 isds_error err = IE_SUCCESS;
4745 xmlNsPtr isds_ns = NULL;
4746 xmlNodePtr request = NULL;
4747 xmlNodePtr node;
4750 if (!context) return IE_INVALID_CONTEXT;
4751 if (!box || !old_user || !new_user) return IE_INVAL;
4754 /* Build UpdateDataBoxUser request */
4755 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxUser");
4756 if (!request) {
4757 isds_log_message(context,
4758 _("Could build UpdateDataBoxUser request"));
4759 return IE_ERROR;
4761 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4762 if(!isds_ns) {
4763 isds_log_message(context, _("Could not create ISDS name space"));
4764 xmlFreeNode(request);
4765 return IE_ERROR;
4767 xmlSetNs(request, isds_ns);
4769 INSERT_ELEMENT(node, request, "dbOwnerInfo");
4770 err = insert_DbOwnerInfo(context, box, node);
4771 if (err) goto leave;
4773 INSERT_ELEMENT(node, request, "dbOldUserInfo");
4774 err = insert_DbUserInfo(context, old_user, node);
4775 if (err) goto leave;
4777 INSERT_ELEMENT(node, request, "dbNewUserInfo");
4778 err = insert_DbUserInfo(context, new_user, node);
4779 if (err) goto leave;
4781 /* Send it to server and process response */
4782 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
4783 BAD_CAST "UpdateDataBoxUser", &request, (xmlChar **) refnumber);
4785 leave:
4786 xmlFreeNode(request);
4788 return err;
4792 /* Reset credentials of user assigned to given box.
4793 * @context is session context
4794 * @box is box identification
4795 * @user identifies user to reset password
4796 * @fee_paid is true if fee has been paid, false otherwise
4797 * @approval is optional external approval of box manipulation
4798 * @token is NULL if new password should be delivered off-line to the user.
4799 * It is valid pointer if user should obtain new password on-line on dedicated
4800 * web server. Then it output automatically reallocated token user needs to
4801 * use to athtorize on the web server to view his new password.
4802 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4803 * NULL, if you don't care.*/
4804 isds_error isds_reset_password(struct isds_ctx *context,
4805 const struct isds_DbOwnerInfo *box,
4806 const struct isds_DbUserInfo *user,
4807 const _Bool fee_paid, const struct isds_approval *approval,
4808 char **token, char **refnumber) {
4809 isds_error err = IE_SUCCESS;
4810 xmlNsPtr isds_ns = NULL;
4811 xmlNodePtr request = NULL, node;
4812 xmlDocPtr response = NULL;
4813 xmlXPathContextPtr xpath_ctx = NULL;
4814 xmlXPathObjectPtr result = NULL;
4817 if (!context) return IE_INVALID_CONTEXT;
4818 if (!box || !user) return IE_INVAL;
4820 if (token) zfree(*token);
4823 /* Build NewAccessData request */
4824 request = xmlNewNode(NULL, BAD_CAST "NewAccessData");
4825 if (!request) {
4826 isds_log_message(context,
4827 _("Could build NewAccessData request"));
4828 return IE_ERROR;
4830 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4831 if(!isds_ns) {
4832 isds_log_message(context, _("Could not create ISDS name space"));
4833 xmlFreeNode(request);
4834 return IE_ERROR;
4836 xmlSetNs(request, isds_ns);
4838 INSERT_ELEMENT(node, request, "dbOwnerInfo");
4839 err = insert_DbOwnerInfo(context, box, node);
4840 if (err) goto leave;
4842 INSERT_ELEMENT(node, request, "dbUserInfo");
4843 err = insert_DbUserInfo(context, user, node);
4844 if (err) goto leave;
4846 INSERT_SCALAR_BOOLEAN(request, "dbFeePaid", fee_paid);
4848 if (token) {
4849 INSERT_SCALAR_BOOLEAN(request, "dbVirtual", 1);
4850 } else {
4851 INSERT_SCALAR_BOOLEAN(request, "dbVirtual", 0);
4854 err = insert_GExtApproval(context, approval, request);
4855 if (err) goto leave;
4857 /* Send request and check reposne*/
4858 err = send_destroy_request_check_response(context,
4859 SERVICE_DB_MANIPULATION, BAD_CAST "NewAccessData", &request,
4860 &response, (xmlChar **) refnumber);
4861 if (err) goto leave;
4864 /* Extract optional token */
4865 if (token) {
4866 xpath_ctx = xmlXPathNewContext(response);
4867 if (!xpath_ctx) {
4868 err = IE_ERROR;
4869 goto leave;
4871 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4872 err = IE_ERROR;
4873 goto leave;
4876 EXTRACT_STRING("/isds:NewAccessDataResponse/dbAccessDataId", *token);
4879 leave:
4880 xmlXPathFreeObject(result);
4881 xmlXPathFreeContext(xpath_ctx);
4882 xmlFreeDoc(response);
4883 xmlFreeNode(request);
4885 if (!err)
4886 isds_log(ILF_ISDS, ILL_DEBUG,
4887 _("NewAccessData request processed by server "
4888 "successfully.\n"));
4890 return err;
4894 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
4895 * code, destroy response and log success.
4896 * @context is ISDS session context.
4897 * @service_name is name of SERVICE_DB_MANIPULATION service
4898 * @box is box identification
4899 * @user identifies user to removve
4900 * @approval is optional external approval of box manipulation
4901 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4902 * NULL, if you don't care. */
4903 static isds_error build_send_manipulationboxuser_request_check_drop_response(
4904 struct isds_ctx *context, const xmlChar *service_name,
4905 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
4906 const struct isds_approval *approval, xmlChar **refnumber) {
4907 isds_error err = IE_SUCCESS;
4908 xmlNsPtr isds_ns = NULL;
4909 xmlNodePtr request = NULL, node;
4912 if (!context) return IE_INVALID_CONTEXT;
4913 if (!service_name || service_name[0] == '\0' || !box || !user)
4914 return IE_INVAL;
4917 /* Build NewAccessData request */
4918 request = xmlNewNode(NULL, service_name);
4919 if (!request) {
4920 char *service_name_locale = utf82locale((char *) service_name);
4921 isds_printf_message(context, _("Could build %s request"),
4922 service_name_locale);
4923 free(service_name_locale);
4924 return IE_ERROR;
4926 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4927 if(!isds_ns) {
4928 isds_log_message(context, _("Could not create ISDS name space"));
4929 xmlFreeNode(request);
4930 return IE_ERROR;
4932 xmlSetNs(request, isds_ns);
4934 INSERT_ELEMENT(node, request, "dbOwnerInfo");
4935 err = insert_DbOwnerInfo(context, box, node);
4936 if (err) goto leave;
4938 INSERT_ELEMENT(node, request, "dbUserInfo");
4939 err = insert_DbUserInfo(context, user, node);
4940 if (err) goto leave;
4942 err = insert_GExtApproval(context, approval, request);
4943 if (err) goto leave;
4945 /* Send request and check reposne*/
4946 err = send_request_check_drop_response (context,
4947 SERVICE_DB_MANIPULATION, service_name, &request, refnumber);
4949 leave:
4950 xmlFreeNode(request);
4951 return err;
4955 /* Assign new user to given box.
4956 * @context is session context
4957 * @box is box identification
4958 * @user defines new user to add
4959 * @approval is optional external approval of box manipulation
4960 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4961 * NULL, if you don't care.*/
4962 isds_error isds_add_user(struct isds_ctx *context,
4963 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
4964 const struct isds_approval *approval, char **refnumber) {
4965 return build_send_manipulationboxuser_request_check_drop_response(context,
4966 BAD_CAST "AddDataBoxUser", box, user, approval,
4967 (xmlChar **) refnumber);
4971 /* Remove user assigned to given box.
4972 * @context is session context
4973 * @box is box identification
4974 * @user identifies user to removve
4975 * @approval is optional external approval of box manipulation
4976 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4977 * NULL, if you don't care.*/
4978 isds_error isds_delete_user(struct isds_ctx *context,
4979 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
4980 const struct isds_approval *approval, char **refnumber) {
4981 return build_send_manipulationboxuser_request_check_drop_response(context,
4982 BAD_CAST "DeleteDataBoxUser", box, user, approval,
4983 (xmlChar **) refnumber);
4987 /* Find boxes suiting given criteria.
4988 * @criteria is filter. You should fill in at least some memebers.
4989 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
4990 * possibly empty. Input NULL or valid old structure.
4991 * @return:
4992 * IE_SUCCESS if search sucseeded, @boxes contains usefull data
4993 * IE_NOEXIST if no such box exists, @boxes will be NULL
4994 * IE_2BIG if too much boxes exist and server truncated the resuluts, @boxes
4995 * contains still valid data
4996 * other code if something bad happens. @boxes will be NULL. */
4997 isds_error isds_FindDataBox(struct isds_ctx *context,
4998 const struct isds_DbOwnerInfo *criteria,
4999 struct isds_list **boxes) {
5000 isds_error err = IE_SUCCESS;
5001 _Bool truncated = 0;
5002 xmlNsPtr isds_ns = NULL;
5003 xmlNodePtr request = NULL;
5004 xmlDocPtr response = NULL;
5005 xmlChar *code = NULL, *message = NULL;
5006 xmlNodePtr db_owner_info;
5007 xmlXPathContextPtr xpath_ctx = NULL;
5008 xmlXPathObjectPtr result = NULL;
5009 xmlChar *string = NULL;
5012 if (!context) return IE_INVALID_CONTEXT;
5013 if (!boxes) return IE_INVAL;
5014 isds_list_free(boxes);
5016 if (!criteria) {
5017 return IE_INVAL;
5020 /* Check if connection is established
5021 * TODO: This check should be done donwstairs. */
5022 if (!context->curl) return IE_CONNECTION_CLOSED;
5025 /* Build FindDataBox request */
5026 request = xmlNewNode(NULL, BAD_CAST "FindDataBox");
5027 if (!request) {
5028 isds_log_message(context,
5029 _("Could build FindDataBox request"));
5030 return IE_ERROR;
5032 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5033 if(!isds_ns) {
5034 isds_log_message(context, _("Could not create ISDS name space"));
5035 xmlFreeNode(request);
5036 return IE_ERROR;
5038 xmlSetNs(request, isds_ns);
5039 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
5040 if (!db_owner_info) {
5041 isds_log_message(context, _("Could not add dbOwnerInfo child to "
5042 "FindDataBox element"));
5043 xmlFreeNode(request);
5044 return IE_ERROR;
5047 err = insert_DbOwnerInfo(context, criteria, db_owner_info);
5048 if (err) goto leave;
5051 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending FindDataBox request to ISDS\n"));
5053 /* Sent request */
5054 err = isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
5056 /* Destroy request */
5057 xmlFreeNode(request); request = NULL;
5059 if (err) {
5060 isds_log(ILF_ISDS, ILL_DEBUG,
5061 _("Processing ISDS response on FindDataBox "
5062 "request failed\n"));
5063 goto leave;
5066 /* Check for response status */
5067 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
5068 &code, &message, NULL);
5069 if (err) {
5070 isds_log(ILF_ISDS, ILL_DEBUG,
5071 _("ISDS response on FindDataBox request is missing status\n"));
5072 goto leave;
5075 /* Request processed, but nothing found */
5076 if (!xmlStrcmp(code, BAD_CAST "0002") ||
5077 !xmlStrcmp(code, BAD_CAST "5001")) {
5078 char *code_locale = utf82locale((char*)code);
5079 char *message_locale = utf82locale((char*)message);
5080 isds_log(ILF_ISDS, ILL_DEBUG,
5081 _("Server did not found any box on FindDataBox request "
5082 "(code=%s, message=%s)\n"), code_locale, message_locale);
5083 isds_log_message(context, message_locale);
5084 free(code_locale);
5085 free(message_locale);
5086 err = IE_NOEXIST;
5087 goto leave;
5090 /* Warning, not a error */
5091 if (!xmlStrcmp(code, BAD_CAST "0003")) {
5092 char *code_locale = utf82locale((char*)code);
5093 char *message_locale = utf82locale((char*)message);
5094 isds_log(ILF_ISDS, ILL_DEBUG,
5095 _("Server truncated response on FindDataBox request "
5096 "(code=%s, message=%s)\n"), code_locale, message_locale);
5097 isds_log_message(context, message_locale);
5098 free(code_locale);
5099 free(message_locale);
5100 truncated = 1;
5103 /* Other error */
5104 else if (xmlStrcmp(code, BAD_CAST "0000")) {
5105 char *code_locale = utf82locale((char*)code);
5106 char *message_locale = utf82locale((char*)message);
5107 isds_log(ILF_ISDS, ILL_DEBUG,
5108 _("Server refused FindDataBox request "
5109 "(code=%s, message=%s)\n"), code_locale, message_locale);
5110 isds_log_message(context, message_locale);
5111 free(code_locale);
5112 free(message_locale);
5113 err = IE_ISDS;
5114 goto leave;
5117 xpath_ctx = xmlXPathNewContext(response);
5118 if (!xpath_ctx) {
5119 err = IE_ERROR;
5120 goto leave;
5122 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5123 err = IE_ERROR;
5124 goto leave;
5127 /* Extract boxes if they present */
5128 result = xmlXPathEvalExpression(BAD_CAST
5129 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
5130 xpath_ctx);
5131 if (!result) {
5132 err = IE_ERROR;
5133 goto leave;
5135 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5136 struct isds_list *item, *prev_item = NULL;
5137 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
5138 item = calloc(1, sizeof(*item));
5139 if (!item) {
5140 err = IE_NOMEM;
5141 goto leave;
5144 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
5145 if (i == 0) *boxes = item;
5146 else prev_item->next = item;
5147 prev_item = item;
5149 xpath_ctx->node = result->nodesetval->nodeTab[i];
5150 err = extract_DbOwnerInfo(context,
5151 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
5152 if (err) goto leave;
5156 leave:
5157 if (err) {
5158 isds_list_free(boxes);
5159 } else {
5160 if (truncated) err = IE_2BIG;
5163 free(string);
5164 xmlFreeNode(request);
5165 xmlXPathFreeObject(result);
5166 xmlXPathFreeContext(xpath_ctx);
5168 free(code);
5169 free(message);
5170 xmlFreeDoc(response);
5172 if (!err)
5173 isds_log(ILF_ISDS, ILL_DEBUG,
5174 _("FindDataBox request processed by server successfully.\n"));
5176 return err;
5180 /* Get status of a box.
5181 * @context is ISDS session context.
5182 * @box_id is UTF-8 encoded box identifier as zero terminated string
5183 * @box_status is return value of box status.
5184 * @return:
5185 * IE_SUCCESS if box has been found and its status retrieved
5186 * IE_NOEXIST if box is not known to ISDS server
5187 * or other appropriate error.
5188 * You can use isds_DbState to enumerate box status. However out of enum
5189 * range value can be returned too. This is feature because ISDS
5190 * specification leaves the set of values open.
5191 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
5192 * the box has been deleted, but ISDS still lists its former existence. */
5193 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
5194 long int *box_status) {
5195 isds_error err = IE_SUCCESS;
5196 xmlNsPtr isds_ns = NULL;
5197 xmlNodePtr request = NULL, db_id;
5198 xmlDocPtr response = NULL;
5199 xmlChar *code = NULL, *message = NULL;
5200 xmlXPathContextPtr xpath_ctx = NULL;
5201 xmlXPathObjectPtr result = NULL;
5202 xmlChar *string = NULL;
5204 if (!context) return IE_INVALID_CONTEXT;
5205 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
5207 /* Check if connection is established
5208 * TODO: This check should be done donwstairs. */
5209 if (!context->curl) return IE_CONNECTION_CLOSED;
5212 /* Build CheckDataBox request */
5213 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
5214 if (!request) {
5215 isds_log_message(context,
5216 _("Could build CheckDataBox request"));
5217 return IE_ERROR;
5219 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5220 if(!isds_ns) {
5221 isds_log_message(context, _("Could not create ISDS name space"));
5222 xmlFreeNode(request);
5223 return IE_ERROR;
5225 xmlSetNs(request, isds_ns);
5226 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
5227 if (!db_id) {
5228 isds_log_message(context, _("Could not add dbID child to "
5229 "CheckDataBox element"));
5230 xmlFreeNode(request);
5231 return IE_ERROR;
5235 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CheckDataBox request to ISDS\n"));
5237 /* Sent request */
5238 err = isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
5240 /* Destroy request */
5241 xmlFreeNode(request);
5243 if (err) {
5244 isds_log(ILF_ISDS, ILL_DEBUG,
5245 _("Processing ISDS response on CheckDataBox "
5246 "request failed\n"));
5247 goto leave;
5250 /* Check for response status */
5251 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
5252 &code, &message, NULL);
5253 if (err) {
5254 isds_log(ILF_ISDS, ILL_DEBUG,
5255 _("ISDS response on CheckDataBox request is missing status\n"));
5256 goto leave;
5259 /* Request processed, but nothing found */
5260 if (!xmlStrcmp(code, BAD_CAST "5001")) {
5261 char *box_id_locale = utf82locale((char*)box_id);
5262 char *code_locale = utf82locale((char*)code);
5263 char *message_locale = utf82locale((char*)message);
5264 isds_log(ILF_ISDS, ILL_DEBUG,
5265 _("Server did not found box %s on CheckDataBox request "
5266 "(code=%s, message=%s)\n"),
5267 box_id_locale, code_locale, message_locale);
5268 isds_log_message(context, message_locale);
5269 free(box_id_locale);
5270 free(code_locale);
5271 free(message_locale);
5272 err = IE_NOEXIST;
5273 goto leave;
5276 /* Other error */
5277 else if (xmlStrcmp(code, BAD_CAST "0000")) {
5278 char *code_locale = utf82locale((char*)code);
5279 char *message_locale = utf82locale((char*)message);
5280 isds_log(ILF_ISDS, ILL_DEBUG,
5281 _("Server refused CheckDataBox request "
5282 "(code=%s, message=%s)\n"), code_locale, message_locale);
5283 isds_log_message(context, message_locale);
5284 free(code_locale);
5285 free(message_locale);
5286 err = IE_ISDS;
5287 goto leave;
5290 /* Extract data */
5291 xpath_ctx = xmlXPathNewContext(response);
5292 if (!xpath_ctx) {
5293 err = IE_ERROR;
5294 goto leave;
5296 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5297 err = IE_ERROR;
5298 goto leave;
5300 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
5301 xpath_ctx);
5302 if (!result) {
5303 err = IE_ERROR;
5304 goto leave;
5306 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5307 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
5308 err = IE_ISDS;
5309 goto leave;
5311 if (result->nodesetval->nodeNr > 1) {
5312 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
5313 err = IE_ISDS;
5314 goto leave;
5316 xpath_ctx->node = result->nodesetval->nodeTab[0];
5317 xmlXPathFreeObject(result); result = NULL;
5319 EXTRACT_LONGINT("isds:dbState", box_status, 1);
5322 leave:
5323 free(string);
5324 xmlXPathFreeObject(result);
5325 xmlXPathFreeContext(xpath_ctx);
5327 free(code);
5328 free(message);
5329 xmlFreeDoc(response);
5331 if (!err)
5332 isds_log(ILF_ISDS, ILL_DEBUG,
5333 _("CheckDataBox request processed by server successfully.\n"));
5335 return err;
5339 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
5340 * code, destroy response and log success.
5341 * @context is ISDS session context.
5342 * @service_name is name of SERVICE_DB_MANIPULATION service
5343 * @box_id is UTF-8 encoded box identifier as zero terminated string
5344 * @approval is optional external approval of box manipulation
5345 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5346 * NULL, if you don't care. */
5347 static isds_error build_send_manipulationdbid_request_check_drop_response(
5348 struct isds_ctx *context, const xmlChar *service_name,
5349 const xmlChar *box_id, const struct isds_approval *approval,
5350 xmlChar **refnumber) {
5351 isds_error err = IE_SUCCESS;
5352 xmlDocPtr response = NULL;
5354 if (!context) return IE_INVALID_CONTEXT;
5355 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
5357 /* Check if connection is established */
5358 if (!context->curl) return IE_CONNECTION_CLOSED;
5360 /* Do request and check for success */
5361 err = build_send_dbid_request_check_response(context,
5362 SERVICE_DB_MANIPULATION, service_name, box_id, approval,
5363 &response, refnumber);
5364 xmlFreeDoc(response);
5366 if (!err) {
5367 char *service_name_locale = utf82locale((char *) service_name);
5368 isds_log(ILF_ISDS, ILL_DEBUG,
5369 _("%s request processed by server successfully.\n"),
5370 service_name_locale);
5371 free(service_name_locale);
5374 return err;
5378 /* Switch box into state where box can receive commercial messages (off by
5379 * default)
5380 * @context is ISDS session context.
5381 * @box_id is UTF-8 encoded box identifier as zero terminated string
5382 * @allow is true for enable, false for disable commercial messages income
5383 * @approval is optional external approval of box manipulation
5384 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5385 * NULL, if you don't care. */
5386 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
5387 const char *box_id, const _Bool allow,
5388 const struct isds_approval *approval, char **refnumber) {
5389 return build_send_manipulationdbid_request_check_drop_response(context,
5390 (allow) ? BAD_CAST "SetOpenAddressing" :
5391 BAD_CAST "ClearOpenAddressing",
5392 BAD_CAST box_id, approval, (xmlChar **) refnumber);
5396 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
5397 * message acceptance). This is just a box permission. Sender must apply
5398 * such role by sending each message.
5399 * @context is ISDS session context.
5400 * @box_id is UTF-8 encoded box identifier as zero terminated string
5401 * @allow is true for enable, false for disable OVM role permission
5402 * @approval is optional external approval of box manipulation
5403 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5404 * NULL, if you don't care. */
5405 isds_error isds_switch_effective_ovm(struct isds_ctx *context,
5406 const char *box_id, const _Bool allow,
5407 const struct isds_approval *approval, char **refnumber) {
5408 return build_send_manipulationdbid_request_check_drop_response(context,
5409 (allow) ? BAD_CAST "SetEffectiveOVM" :
5410 BAD_CAST "ClearEffectiveOVM",
5411 BAD_CAST box_id, approval, (xmlChar **) refnumber);
5415 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
5416 * code, destroy response and log success.
5417 * @context is ISDS session context.
5418 * @service_name is name of SERVICE_DB_MANIPULATION service
5419 * @owner is structure describing box
5420 * @approval is optional external approval of box manipulation
5421 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5422 * NULL, if you don't care. */
5423 static isds_error build_send_manipulationdbowner_request_check_drop_response(
5424 struct isds_ctx *context, const xmlChar *service_name,
5425 const struct isds_DbOwnerInfo *owner,
5426 const struct isds_approval *approval, xmlChar **refnumber) {
5427 isds_error err = IE_SUCCESS;
5428 char *service_name_locale = NULL;
5429 xmlNodePtr request = NULL, db_owner_info;
5430 xmlNsPtr isds_ns = NULL;
5433 if (!context) return IE_INVALID_CONTEXT;
5434 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
5436 service_name_locale = utf82locale((char*)service_name);
5437 if (!service_name_locale) {
5438 err = IE_NOMEM;
5439 goto leave;
5442 /* Build request */
5443 request = xmlNewNode(NULL, service_name);
5444 if (!request) {
5445 isds_printf_message(context,
5446 _("Could not build %s request"), service_name_locale);
5447 err = IE_ERROR;
5448 goto leave;
5450 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5451 if(!isds_ns) {
5452 isds_log_message(context, _("Could not create ISDS name space"));
5453 err = IE_ERROR;
5454 goto leave;
5456 xmlSetNs(request, isds_ns);
5459 /* Add XSD:tOwnerInfoInput child*/
5460 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
5461 err = insert_DbOwnerInfo(context, owner, db_owner_info);
5462 if (err) goto leave;
5464 /* Add XSD:gExtApproval*/
5465 err = insert_GExtApproval(context, approval, request);
5466 if (err) goto leave;
5468 /* Send it to server and process response */
5469 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
5470 service_name, &request, refnumber);
5472 leave:
5473 xmlFreeNode(request);
5474 free(service_name_locale);
5476 return err;
5480 /* Switch box accessibility state on request of box owner.
5481 * Despite the name, owner must do the request off-line. This function is
5482 * designed for such off-line meeting points (e.g. Czech POINT).
5483 * @context is ISDS session context.
5484 * @box identifies box to swith accesibilty state.
5485 * @allow is true for making accesibale, false to disallow access.
5486 * @approval is optional external approval of box manipulation
5487 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5488 * NULL, if you don't care. */
5489 isds_error isds_switch_box_accessibility_on_owner_request(
5490 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
5491 const _Bool allow, const struct isds_approval *approval,
5492 char **refnumber) {
5493 return build_send_manipulationdbowner_request_check_drop_response(context,
5494 (allow) ? BAD_CAST "EnableOwnDataBox" :
5495 BAD_CAST "DisableOwnDataBox",
5496 box, approval, (xmlChar **) refnumber);
5500 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
5501 * date.
5502 * @context is ISDS session context.
5503 * @box identifies box to swith accesibilty state.
5504 * @since is date since accesseibility has been denied. This can be past too.
5505 * Only tm_year, tm_mon and tm_mday carry sane value.
5506 * @approval is optional external approval of box manipulation
5507 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5508 * NULL, if you don't care. */
5509 isds_error isds_disable_box_accessibility_externaly(
5510 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
5511 const struct tm *since, const struct isds_approval *approval,
5512 char **refnumber) {
5513 isds_error err = IE_SUCCESS;
5514 char *service_name_locale = NULL;
5515 xmlNodePtr request = NULL, node;
5516 xmlNsPtr isds_ns = NULL;
5517 xmlChar *string = NULL;
5520 if (!context) return IE_INVALID_CONTEXT;
5521 if (!box || !since) return IE_INVAL;
5523 /* Build request */
5524 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
5525 if (!request) {
5526 isds_printf_message(context,
5527 _("Could not build %s request"), "DisableDataBoxExternally");
5528 err = IE_ERROR;
5529 goto leave;
5531 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5532 if(!isds_ns) {
5533 isds_log_message(context, _("Could not create ISDS name space"));
5534 err = IE_ERROR;
5535 goto leave;
5537 xmlSetNs(request, isds_ns);
5540 /* Add @box identification */
5541 INSERT_ELEMENT(node, request, "dbOwnerInfo");
5542 err = insert_DbOwnerInfo(context, box, node);
5543 if (err) goto leave;
5545 /* Add @since date */
5546 err = tm2datestring(since, &string);
5547 if(err) {
5548 isds_log_message(context,
5549 _("Could not convert `since' argument to ISO date string"));
5550 goto leave;
5552 INSERT_STRING(request, "dbOwnerDisableDate", string);
5553 zfree(string);
5555 /* Add @approval */
5556 err = insert_GExtApproval(context, approval, request);
5557 if (err) goto leave;
5559 /* Send it to server and process response */
5560 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
5561 BAD_CAST "DisableDataBoxExternally", &request,
5562 (xmlChar **) refnumber);
5564 leave:
5565 free(string);
5566 xmlFreeNode(request);
5567 free(service_name_locale);
5569 return err;
5573 /* Insert struct isds_message data (envelope (recipient data optional) and
5574 * documents) into XML tree
5575 * @context is sesstion context
5576 * @outgoing_message is libsids structure with message data
5577 * @create_message is XML CreateMessage or CreateMultipleMessage element
5578 * @process_recipient true for recipient data serialization, false for no
5579 * serialization */
5580 static isds_error insert_envelope_files(struct isds_ctx *context,
5581 const struct isds_message *outgoing_message, xmlNodePtr create_message,
5582 const _Bool process_recipient) {
5584 isds_error err = IE_SUCCESS;
5585 xmlNodePtr envelope, dm_files, node;
5586 xmlChar *string = NULL;
5588 if (!context) return IE_INVALID_CONTEXT;
5589 if (!outgoing_message || !create_message) return IE_INVAL;
5592 /* Build envelope */
5593 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
5594 if (!envelope) {
5595 isds_printf_message(context, _("Could not add dmEnvelope child to "
5596 "%s element"), create_message->name);
5597 return IE_ERROR;
5600 if (!outgoing_message->envelope) {
5601 isds_log_message(context, _("Outgoing message is missing envelope"));
5602 err = IE_INVAL;
5603 goto leave;
5606 INSERT_STRING(envelope, "dmSenderOrgUnit",
5607 outgoing_message->envelope->dmSenderOrgUnit);
5608 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
5609 outgoing_message->envelope->dmSenderOrgUnitNum, string);
5611 if (process_recipient) {
5612 if (!outgoing_message->envelope->dbIDRecipient) {
5613 isds_log_message(context,
5614 _("Outgoing message is missing recipient box identifier"));
5615 err = IE_INVAL;
5616 goto leave;
5618 INSERT_STRING(envelope, "dbIDRecipient",
5619 outgoing_message->envelope->dbIDRecipient);
5621 INSERT_STRING(envelope, "dmRecipientOrgUnit",
5622 outgoing_message->envelope->dmRecipientOrgUnit);
5623 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
5624 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
5625 INSERT_STRING(envelope, "dmToHands",
5626 outgoing_message->envelope->dmToHands);
5629 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
5630 "dmAnnotation");
5631 INSERT_STRING(envelope, "dmAnnotation",
5632 outgoing_message->envelope->dmAnnotation);
5634 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
5635 0, 50, "dmRecipientRefNumber");
5636 INSERT_STRING(envelope, "dmRecipientRefNumber",
5637 outgoing_message->envelope->dmRecipientRefNumber);
5639 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
5640 0, 50, "dmSenderRefNumber");
5641 INSERT_STRING(envelope, "dmSenderRefNumber",
5642 outgoing_message->envelope->dmSenderRefNumber);
5644 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
5645 0, 50, "dmRecipientIdent");
5646 INSERT_STRING(envelope, "dmRecipientIdent",
5647 outgoing_message->envelope->dmRecipientIdent);
5649 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
5650 0, 50, "dmSenderIdent");
5651 INSERT_STRING(envelope, "dmSenderIdent",
5652 outgoing_message->envelope->dmSenderIdent);
5654 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
5655 outgoing_message->envelope->dmLegalTitleLaw, string);
5656 INSERT_LONGINT(envelope, "dmLegalTitleYear",
5657 outgoing_message->envelope->dmLegalTitleYear, string);
5658 INSERT_STRING(envelope, "dmLegalTitleSect",
5659 outgoing_message->envelope->dmLegalTitleSect);
5660 INSERT_STRING(envelope, "dmLegalTitlePar",
5661 outgoing_message->envelope->dmLegalTitlePar);
5662 INSERT_STRING(envelope, "dmLegalTitlePoint",
5663 outgoing_message->envelope->dmLegalTitlePoint);
5665 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
5666 outgoing_message->envelope->dmPersonalDelivery);
5667 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
5668 outgoing_message->envelope->dmAllowSubstDelivery);
5670 /* ???: Should we require value for dbEffectiveOVM sender?
5671 * ISDS has default as true */
5672 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
5675 /* Append dmFiles */
5676 if (!outgoing_message->documents) {
5677 isds_log_message(context,
5678 _("Outgoing message is missing list of documents"));
5679 err = IE_INVAL;
5680 goto leave;
5682 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
5683 if (!dm_files) {
5684 isds_printf_message(context, _("Could not add dmFiles child to "
5685 "%s element"), create_message->name);
5686 err = IE_ERROR;
5687 goto leave;
5690 /* Check for document hieararchy */
5691 err = check_documents_hierarchy(context, outgoing_message->documents);
5692 if (err) goto leave;
5694 /* Process each document */
5695 for (struct isds_list *item =
5696 (struct isds_list *) outgoing_message->documents;
5697 item; item = item->next) {
5698 if (!item->data) {
5699 isds_log_message(context,
5700 _("List of documents contains empty item"));
5701 err = IE_INVAL;
5702 goto leave;
5704 /* FIXME: Check for dmFileMetaType and for document references.
5705 * Only first document can be of MAIN type */
5706 err = insert_document(context, (struct isds_document*) item->data,
5707 dm_files);
5709 if (err) goto leave;
5712 leave:
5713 free(string);
5714 return err;
5718 /* Send a message via ISDS to a recipent
5719 * @context is session context
5720 * @outgoing_message is message to send; Some memebers are mandatory (like
5721 * dbIDRecipient), some are optional and some are irrelevant (especialy data
5722 * about sender). Included pointer to isds_list documents must contain at
5723 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
5724 * members will be filled with valid data from ISDS. Exact list of write
5725 * members is subject to change. Currently dmId is changed.
5726 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
5727 isds_error isds_send_message(struct isds_ctx *context,
5728 struct isds_message *outgoing_message) {
5730 isds_error err = IE_SUCCESS;
5731 xmlNsPtr isds_ns = NULL;
5732 xmlNodePtr request = NULL;
5733 xmlDocPtr response = NULL;
5734 xmlChar *code = NULL, *message = NULL;
5735 xmlXPathContextPtr xpath_ctx = NULL;
5736 xmlXPathObjectPtr result = NULL;
5737 _Bool message_is_complete = 0;
5739 if (!context) return IE_INVALID_CONTEXT;
5740 if (!outgoing_message) return IE_INVAL;
5742 /* Check if connection is established
5743 * TODO: This check should be done donwstairs. */
5744 if (!context->curl) return IE_CONNECTION_CLOSED;
5747 /* Build CreateMessage request */
5748 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
5749 if (!request) {
5750 isds_log_message(context,
5751 _("Could build CreateMessage request"));
5752 return IE_ERROR;
5754 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5755 if(!isds_ns) {
5756 isds_log_message(context, _("Could not create ISDS name space"));
5757 xmlFreeNode(request);
5758 return IE_ERROR;
5760 xmlSetNs(request, isds_ns);
5762 /* Append envelope and files */
5763 err = insert_envelope_files(context, outgoing_message, request, 1);
5764 if (err) goto leave;
5767 /* Signal we can serilize message since now */
5768 message_is_complete = 1;
5771 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
5773 /* Sent request */
5774 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
5776 /* Dont' destroy request, we want to provide it to application later */
5778 if (err) {
5779 isds_log(ILF_ISDS, ILL_DEBUG,
5780 _("Processing ISDS response on CreateMessage "
5781 "request failed\n"));
5782 goto leave;
5785 /* Check for response status */
5786 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
5787 &code, &message, NULL);
5788 if (err) {
5789 isds_log(ILF_ISDS, ILL_DEBUG,
5790 _("ISDS response on CreateMessage request "
5791 "is missing status\n"));
5792 goto leave;
5795 /* Request processed, but refused by server or server failed */
5796 if (xmlStrcmp(code, BAD_CAST "0000")) {
5797 char *box_id_locale =
5798 utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
5799 char *code_locale = utf82locale((char*)code);
5800 char *message_locale = utf82locale((char*)message);
5801 isds_log(ILF_ISDS, ILL_DEBUG,
5802 _("Server did not accept message for %s on CreateMessage "
5803 "request (code=%s, message=%s)\n"),
5804 box_id_locale, code_locale, message_locale);
5805 isds_log_message(context, message_locale);
5806 free(box_id_locale);
5807 free(code_locale);
5808 free(message_locale);
5809 err = IE_ISDS;
5810 goto leave;
5814 /* Extract data */
5815 xpath_ctx = xmlXPathNewContext(response);
5816 if (!xpath_ctx) {
5817 err = IE_ERROR;
5818 goto leave;
5820 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5821 err = IE_ERROR;
5822 goto leave;
5824 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
5825 xpath_ctx);
5826 if (!result) {
5827 err = IE_ERROR;
5828 goto leave;
5830 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5831 isds_log_message(context, _("Missing CreateMessageResponse element"));
5832 err = IE_ISDS;
5833 goto leave;
5835 if (result->nodesetval->nodeNr > 1) {
5836 isds_log_message(context, _("Multiple CreateMessageResponse element"));
5837 err = IE_ISDS;
5838 goto leave;
5840 xpath_ctx->node = result->nodesetval->nodeTab[0];
5841 xmlXPathFreeObject(result); result = NULL;
5843 if (outgoing_message->envelope->dmID) {
5844 free(outgoing_message->envelope->dmID);
5845 outgoing_message->envelope->dmID = NULL;
5847 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
5848 if (!outgoing_message->envelope->dmID) {
5849 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
5850 "but did not return assigned message ID\n"));
5853 leave:
5854 /* TODO: Serialize message into structure member raw */
5855 /* XXX: Each web service transport message in different format.
5856 * Therefore it's not possible to save them directly.
5857 * To save them, one must figure out common format.
5858 * We can leave it on application, or we can implement the ESS format. */
5859 /*if (message_is_complete) {
5860 if (outgoing_message->envelope->dmID) {
5862 /* Add assigned message ID as first child*/
5863 /*xmlNodePtr dmid_text = xmlNewText(
5864 (xmlChar *) outgoing_message->envelope->dmID);
5865 if (!dmid_text) goto serialization_failed;
5867 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
5868 BAD_CAST "dmID");
5869 if (!dmid_element) {
5870 xmlFreeNode(dmid_text);
5871 goto serialization_failed;
5874 xmlNodePtr dmid_element_with_text =
5875 xmlAddChild(dmid_element, dmid_text);
5876 if (!dmid_element_with_text) {
5877 xmlFreeNode(dmid_element);
5878 xmlFreeNode(dmid_text);
5879 goto serialization_failed;
5882 node = xmlAddPrevSibling(envelope->childern,
5883 dmid_element_with_text);
5884 if (!node) {
5885 xmlFreeNodeList(dmid_element_with_text);
5886 goto serialization_failed;
5890 /* Serialize message with ID into raw */
5891 /*buffer = serialize_element(envelope)*/
5892 /* }
5894 serialization_failed:
5898 /* Clean up */
5899 xmlXPathFreeObject(result);
5900 xmlXPathFreeContext(xpath_ctx);
5902 free(code);
5903 free(message);
5904 xmlFreeDoc(response);
5905 xmlFreeNode(request);
5907 if (!err)
5908 isds_log(ILF_ISDS, ILL_DEBUG,
5909 _("CreateMessage request processed by server "
5910 "successfully.\n"));
5912 return err;
5916 /* Send a message via ISDS to a multiple recipents
5917 * @context is session context
5918 * @outgoing_message is message to send; Some memebers are mandatory,
5919 * some are optional and some are irrelevant (especialy data
5920 * about sender). Data about recipient will be substituted by ISDS from
5921 * @copies. Included pointer to isds_list documents must
5922 * contain at least one document of FILEMETATYPE_MAIN.
5923 * @copies is list of isds_message_copy structures addressing all desired
5924 * recipients. This is read-write structure, some members will be filled with
5925 * valid data from ISDS (message IDs, error codes, error descriptions).
5926 * @return
5927 * ISDS_SUCCESS if all messages have been sent
5928 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
5929 * succesed messages can be identified by copies->data->error),
5930 * or other error code if something other goes wrong. */
5931 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
5932 const struct isds_message *outgoing_message,
5933 struct isds_list *copies) {
5935 isds_error err = IE_SUCCESS, append_err;
5936 xmlNsPtr isds_ns = NULL;
5937 xmlNodePtr request = NULL, recipients, recipient, node;
5938 struct isds_list *item;
5939 struct isds_message_copy *copy;
5940 xmlDocPtr response = NULL;
5941 xmlChar *code = NULL, *message = NULL;
5942 xmlXPathContextPtr xpath_ctx = NULL;
5943 xmlXPathObjectPtr result = NULL;
5944 xmlChar *string = NULL;
5945 int i;
5947 if (!context) return IE_INVALID_CONTEXT;
5948 if (!outgoing_message || !copies) return IE_INVAL;
5950 /* Check if connection is established
5951 * TODO: This check should be done donwstairs. */
5952 if (!context->curl) return IE_CONNECTION_CLOSED;
5955 /* Build CreateMultipleMessage request */
5956 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
5957 if (!request) {
5958 isds_log_message(context,
5959 _("Could not build CreateMultipleMessage request"));
5960 return IE_ERROR;
5962 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5963 if(!isds_ns) {
5964 isds_log_message(context, _("Could not create ISDS name space"));
5965 xmlFreeNode(request);
5966 return IE_ERROR;
5968 xmlSetNs(request, isds_ns);
5971 /* Build recipients */
5972 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
5973 if (!recipients) {
5974 isds_log_message(context, _("Could not add dmRecipients child to "
5975 "CreateMultipleMessage element"));
5976 xmlFreeNode(request);
5977 return IE_ERROR;
5980 /* Insert each recipient */
5981 for (item = copies; item; item = item->next) {
5982 copy = (struct isds_message_copy *) item->data;
5983 if (!copy) {
5984 isds_log_message(context,
5985 _("`copies' list item contains empty data"));
5986 err = IE_INVAL;
5987 goto leave;
5990 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
5991 if (!recipient) {
5992 isds_log_message(context, _("Could not add dmRecipient child to "
5993 "dmRecipients element"));
5994 err = IE_ERROR;
5995 goto leave;
5998 if (!copy->dbIDRecipient) {
5999 isds_log_message(context,
6000 _("Message copy is missing recipient box identifier"));
6001 err = IE_INVAL;
6002 goto leave;
6004 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
6005 INSERT_STRING(recipient, "dmRecipientOrgUnit",
6006 copy->dmRecipientOrgUnit);
6007 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
6008 copy->dmRecipientOrgUnitNum, string);
6009 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
6012 /* Append envelope and files */
6013 err = insert_envelope_files(context, outgoing_message, request, 0);
6014 if (err) goto leave;
6017 isds_log(ILF_ISDS, ILL_DEBUG,
6018 _("Sending CreateMultipleMessage request to ISDS\n"));
6020 /* Sent request */
6021 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
6022 if (err) {
6023 isds_log(ILF_ISDS, ILL_DEBUG,
6024 _("Processing ISDS response on CreateMultipleMessage "
6025 "request failed\n"));
6026 goto leave;
6029 /* Check for response status */
6030 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
6031 &code, &message, NULL);
6032 if (err) {
6033 isds_log(ILF_ISDS, ILL_DEBUG,
6034 _("ISDS response on CreateMultipleMessage request "
6035 "is missing status\n"));
6036 goto leave;
6039 /* Request processed, but some copies failed */
6040 if (!xmlStrcmp(code, BAD_CAST "0004")) {
6041 char *box_id_locale =
6042 utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
6043 char *code_locale = utf82locale((char*)code);
6044 char *message_locale = utf82locale((char*)message);
6045 isds_log(ILF_ISDS, ILL_DEBUG,
6046 _("Server did accept message for multiple recipients "
6047 "on CreateMultipleMessage request but delivery to "
6048 "some of them failed (code=%s, message=%s)\n"),
6049 box_id_locale, code_locale, message_locale);
6050 isds_log_message(context, message_locale);
6051 free(box_id_locale);
6052 free(code_locale);
6053 free(message_locale);
6054 err = IE_PARTIAL_SUCCESS;
6057 /* Request refused by server as whole */
6058 else if (xmlStrcmp(code, BAD_CAST "0000")) {
6059 char *box_id_locale =
6060 utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
6061 char *code_locale = utf82locale((char*)code);
6062 char *message_locale = utf82locale((char*)message);
6063 isds_log(ILF_ISDS, ILL_DEBUG,
6064 _("Server did not accept message for multiple recipients "
6065 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
6066 box_id_locale, code_locale, message_locale);
6067 isds_log_message(context, message_locale);
6068 free(box_id_locale);
6069 free(code_locale);
6070 free(message_locale);
6071 err = IE_ISDS;
6072 goto leave;
6076 /* Extract data */
6077 xpath_ctx = xmlXPathNewContext(response);
6078 if (!xpath_ctx) {
6079 err = IE_ERROR;
6080 goto leave;
6082 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6083 err = IE_ERROR;
6084 goto leave;
6086 result = xmlXPathEvalExpression(
6087 BAD_CAST "/isds:CreateMultipleMessageResponse"
6088 "/isds:dmMultipleStatus/isds:dmSingleStatus",
6089 xpath_ctx);
6090 if (!result) {
6091 err = IE_ERROR;
6092 goto leave;
6094 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6095 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
6096 err = IE_ISDS;
6097 goto leave;
6100 /* Extract message ID and delivery status for each copy */
6101 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
6102 item = item->next, i++) {
6103 copy = (struct isds_message_copy *) item->data;
6104 xpath_ctx->node = result->nodesetval->nodeTab[i];
6106 append_err = append_TMStatus(context, copy, xpath_ctx);
6107 if (append_err) {
6108 err = append_err;
6109 goto leave;
6112 if (item || i < result->nodesetval->nodeNr) {
6113 isds_printf_message(context, _("ISDS returned unexpected number of "
6114 "message copy delivery states: %d"),
6115 result->nodesetval->nodeNr);
6116 err = IE_ISDS;
6117 goto leave;
6121 leave:
6122 /* Clean up */
6123 free(string);
6124 xmlXPathFreeObject(result);
6125 xmlXPathFreeContext(xpath_ctx);
6127 free(code);
6128 free(message);
6129 xmlFreeDoc(response);
6130 xmlFreeNode(request);
6132 if (!err)
6133 isds_log(ILF_ISDS, ILL_DEBUG,
6134 _("CreateMultipleMessageResponse request processed by server "
6135 "successfully.\n"));
6137 return err;
6141 /* Get list of messages. This is common core for getting sent or received
6142 * messaeges.
6143 * Any criterion argument can be NULL, if you don't care about it.
6144 * @context is session context. Must not be NULL.
6145 * @outgoing_direction is true if you want list of outgoing messages,
6146 * it's false if you want incoming messages.
6147 * @from_time is minimal time and date of message sending inclusive.
6148 * @to_time is maximal time and date of message sending inclusive
6149 * @organization_unit_number is number of sender/recipient respectively.
6150 * @status_filter is bit field of isds_message_status values. Use special
6151 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
6152 * all values, you can use bitwise arithmetic if you want.)
6153 * @offset is index of first message we are interested in. First message is 1.
6154 * Set to 0 (or 1) if you don't care.
6155 * @number is maximal length of list you want to get as input value, outputs
6156 * number of messages matching these criteria. Can be NULL if you don't care
6157 * (applies to output value either).
6158 * @messages is automatically reallocated list of isds_message's. Be ware that
6159 * it returns only brief overview (envelope and some other fields) about each
6160 * message, not the complete message. FIXME: Specify exact fields.
6161 * The list is sorted by delivery time in ascending order.
6162 * Use NULL if
6163 * you don't care about don't need the data (useful if you want to know only
6164 * the @number). If you provide &NULL, list will be allocated on heap, if you
6165 * provide pointer to non-NULL, list will be freed automacally at first. Also
6166 * in case of error the list will be NULLed.
6167 * @return IE_SUCCESS or appropriate error code. */
6168 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
6169 _Bool outgoing_direction,
6170 const struct timeval *from_time, const struct timeval *to_time,
6171 const long int *organization_unit_number,
6172 const unsigned int status_filter,
6173 const unsigned long int offset, unsigned long int *number,
6174 struct isds_list **messages) {
6176 isds_error err = IE_SUCCESS;
6177 xmlNsPtr isds_ns = NULL;
6178 xmlNodePtr request = NULL, node;
6179 xmlDocPtr response = NULL;
6180 xmlChar *code = NULL, *message = NULL;
6181 xmlXPathContextPtr xpath_ctx = NULL;
6182 xmlXPathObjectPtr result = NULL;
6183 xmlChar *string = NULL;
6184 long unsigned int count = 0;
6186 if (!context) return IE_INVALID_CONTEXT;
6188 /* Free former message list if any */
6189 if (messages) isds_list_free(messages);
6191 /* Check if connection is established
6192 * TODO: This check should be done donwstairs. */
6193 if (!context->curl) return IE_CONNECTION_CLOSED;
6195 /* Build GetListOf*Messages request */
6196 request = xmlNewNode(NULL,
6197 (outgoing_direction) ?
6198 BAD_CAST "GetListOfSentMessages" :
6199 BAD_CAST "GetListOfReceivedMessages"
6201 if (!request) {
6202 isds_log_message(context,
6203 (outgoing_direction) ?
6204 _("Could not build GetListOfSentMessages request") :
6205 _("Could not build GetListOfReceivedMessages request")
6207 return IE_ERROR;
6209 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6210 if(!isds_ns) {
6211 isds_log_message(context, _("Could not create ISDS name space"));
6212 xmlFreeNode(request);
6213 return IE_ERROR;
6215 xmlSetNs(request, isds_ns);
6218 if (from_time) {
6219 err = timeval2timestring(from_time, &string);
6220 if (err) goto leave;
6222 INSERT_STRING(request, "dmFromTime", string);
6223 free(string); string = NULL;
6225 if (to_time) {
6226 err = timeval2timestring(to_time, &string);
6227 if (err) goto leave;
6229 INSERT_STRING(request, "dmToTime", string);
6230 free(string); string = NULL;
6232 if (outgoing_direction) {
6233 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
6234 organization_unit_number, string);
6235 } else {
6236 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
6237 organization_unit_number, string);
6240 if (status_filter > MESSAGESTATE_ANY) {
6241 isds_printf_message(context,
6242 _("Invalid message state filter value: %ld"), status_filter);
6243 err = IE_INVAL;
6244 goto leave;
6246 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
6248 if (offset > 0 ) {
6249 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
6250 } else {
6251 INSERT_STRING(request, "dmOffset", "1");
6254 /* number 0 means no limit */
6255 if (number && *number == 0) {
6256 INSERT_STRING(request, "dmLimit", NULL);
6257 } else {
6258 INSERT_ULONGINT(request, "dmLimit", number, string);
6262 isds_log(ILF_ISDS, ILL_DEBUG,
6263 (outgoing_direction) ?
6264 _("Sending GetListOfSentMessages request to ISDS\n") :
6265 _("Sending GetListOfReceivedMessages request to ISDS\n")
6268 /* Sent request */
6269 err = isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
6270 xmlFreeNode(request); request = NULL;
6272 if (err) {
6273 isds_log(ILF_ISDS, ILL_DEBUG,
6274 (outgoing_direction) ?
6275 _("Processing ISDS response on GetListOfSentMessages "
6276 "request failed\n") :
6277 _("Processing ISDS response on GetListOfReceivedMessages "
6278 "request failed\n")
6280 goto leave;
6283 /* Check for response status */
6284 err = isds_response_status(context, SERVICE_DM_INFO, response,
6285 &code, &message, NULL);
6286 if (err) {
6287 isds_log(ILF_ISDS, ILL_DEBUG,
6288 (outgoing_direction) ?
6289 _("ISDS response on GetListOfSentMessages request "
6290 "is missing status\n") :
6291 _("ISDS response on GetListOfReceivedMessages request "
6292 "is missing status\n")
6294 goto leave;
6297 /* Request processed, but nothing found */
6298 if (xmlStrcmp(code, BAD_CAST "0000")) {
6299 char *code_locale = utf82locale((char*)code);
6300 char *message_locale = utf82locale((char*)message);
6301 isds_log(ILF_ISDS, ILL_DEBUG,
6302 (outgoing_direction) ?
6303 _("Server refused GetListOfSentMessages request "
6304 "(code=%s, message=%s)\n") :
6305 _("Server refused GetListOfReceivedMessages request "
6306 "(code=%s, message=%s)\n"),
6307 code_locale, message_locale);
6308 isds_log_message(context, message_locale);
6309 free(code_locale);
6310 free(message_locale);
6311 err = IE_ISDS;
6312 goto leave;
6316 /* Extract data */
6317 xpath_ctx = xmlXPathNewContext(response);
6318 if (!xpath_ctx) {
6319 err = IE_ERROR;
6320 goto leave;
6322 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6323 err = IE_ERROR;
6324 goto leave;
6326 result = xmlXPathEvalExpression(
6327 (outgoing_direction) ?
6328 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
6329 "isds:dmRecords/isds:dmRecord" :
6330 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
6331 "isds:dmRecords/isds:dmRecord",
6332 xpath_ctx);
6333 if (!result) {
6334 err = IE_ERROR;
6335 goto leave;
6338 /* Fill output arguments in */
6339 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6340 struct isds_envelope *envelope;
6341 struct isds_list *item = NULL, *last_item = NULL;
6343 for (count = 0; count < result->nodesetval->nodeNr; count++) {
6344 /* Create new message */
6345 item = calloc(1, sizeof(*item));
6346 if (!item) {
6347 err = IE_NOMEM;
6348 goto leave;
6350 item->destructor = (void(*)(void**)) &isds_message_free;
6351 item->data = calloc(1, sizeof(struct isds_message));
6352 if (!item->data) {
6353 isds_list_free(&item);
6354 err = IE_NOMEM;
6355 goto leave;
6358 /* Extract envelope data */
6359 xpath_ctx->node = result->nodesetval->nodeTab[count];
6360 envelope = NULL;
6361 err = extract_DmRecord(context, &envelope, xpath_ctx);
6362 if (err) {
6363 isds_list_free(&item);
6364 goto leave;
6367 /* Attach extracted envelope */
6368 ((struct isds_message *) item->data)->envelope = envelope;
6370 /* Append new message into the list */
6371 if (!*messages) {
6372 *messages = last_item = item;
6373 } else {
6374 last_item->next = item;
6375 last_item = item;
6379 if (number) *number = count;
6381 leave:
6382 if (err) {
6383 isds_list_free(messages);
6386 free(string);
6387 xmlXPathFreeObject(result);
6388 xmlXPathFreeContext(xpath_ctx);
6390 free(code);
6391 free(message);
6392 xmlFreeDoc(response);
6393 xmlFreeNode(request);
6395 if (!err)
6396 isds_log(ILF_ISDS, ILL_DEBUG,
6397 (outgoing_direction) ?
6398 _("GetListOfSentMessages request processed by server "
6399 "successfully.\n") :
6400 _("GetListOfReceivedMessages request processed by server "
6401 "successfully.\n")
6403 return err;
6407 /* Get list of outgoing (already sent) messages.
6408 * Any criterion argument can be NULL, if you don't care about it.
6409 * @context is session context. Must not be NULL.
6410 * @from_time is minimal time and date of message sending inclusive.
6411 * @to_time is maximal time and date of message sending inclusive
6412 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
6413 * @status_filter is bit field of isds_message_status values. Use special
6414 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
6415 * all values, you can use bitwise arithmetic if you want.)
6416 * @offset is index of first message we are interested in. First message is 1.
6417 * Set to 0 (or 1) if you don't care.
6418 * @number is maximal length of list you want to get as input value, outputs
6419 * number of messages matching these criteria. Can be NULL if you don't care
6420 * (applies to output value either).
6421 * @messages is automatically reallocated list of isds_message's. Be ware that
6422 * it returns only brief overview (envelope and some other fields) about each
6423 * message, not the complete message. FIXME: Specify exact fields.
6424 * The list is sorted by delivery time in ascending order.
6425 * Use NULL if you don't care about the metadata (useful if you want to know
6426 * only the @number). If you provide &NULL, list will be allocated on heap,
6427 * if you provide pointer to non-NULL, list will be freed automacally at first.
6428 * Also in case of error the list will be NULLed.
6429 * @return IE_SUCCESS or appropriate error code. */
6430 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
6431 const struct timeval *from_time, const struct timeval *to_time,
6432 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
6433 const unsigned long int offset, unsigned long int *number,
6434 struct isds_list **messages) {
6436 return isds_get_list_of_messages(
6437 context, 1,
6438 from_time, to_time, dmSenderOrgUnitNum, status_filter,
6439 offset, number,
6440 messages);
6444 /* Get list of incoming (addressed to you) messages.
6445 * Any criterion argument can be NULL, if you don't care about it.
6446 * @context is session context. Must not be NULL.
6447 * @from_time is minimal time and date of message sending inclusive.
6448 * @to_time is maximal time and date of message sending inclusive
6449 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
6450 * @status_filter is bit field of isds_message_status values. Use special
6451 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
6452 * all values, you can use bitwise arithmetic if you want.)
6453 * @offset is index of first message we are interested in. First message is 1.
6454 * Set to 0 (or 1) if you don't care.
6455 * @number is maximal length of list you want to get as input value, outputs
6456 * number of messages matching these criteria. Can be NULL if you don't care
6457 * (applies to output value either).
6458 * @messages is automatically reallocated list of isds_message's. Be ware that
6459 * it returns only brief overview (envelope and some other fields) about each
6460 * message, not the complete message. FIXME: Specify exact fields.
6461 * Use NULL if you don't care about the metadata (useful if you want to know
6462 * only the @number). If you provide &NULL, list will be allocated on heap,
6463 * if you provide pointer to non-NULL, list will be freed automacally at first.
6464 * Also in case of error the list will be NULLed.
6465 * @return IE_SUCCESS or appropriate error code. */
6466 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
6467 const struct timeval *from_time, const struct timeval *to_time,
6468 const long int *dmRecipientOrgUnitNum,
6469 const unsigned int status_filter,
6470 const unsigned long int offset, unsigned long int *number,
6471 struct isds_list **messages) {
6473 return isds_get_list_of_messages(
6474 context, 0,
6475 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
6476 offset, number,
6477 messages);
6481 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
6482 * code
6483 * @context is session context
6484 * @service is ISDS WS service handler
6485 * @service_name is name of SERVICE_DM_OPERATIONS
6486 * @message_id is message ID to send as service argument to ISDS
6487 * @response is server SOAP body response as XML document
6488 * @raw_response is automatically reallocated bitstream with response body. Use
6489 * NULL if you don't care
6490 * @raw_response_length is size of @raw_response in bytes
6491 * @code is ISDS status code
6492 * @status_message is ISDS status message
6493 * @return error coded from lower layer, context message will be set up
6494 * appropriately. */
6495 static isds_error build_send_check_message_request(struct isds_ctx *context,
6496 const isds_service service, const xmlChar *service_name,
6497 const char *message_id,
6498 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
6499 xmlChar **code, xmlChar **status_message) {
6501 isds_error err = IE_SUCCESS;
6502 char *service_name_locale = NULL, *message_id_locale = NULL;
6503 xmlNodePtr request = NULL, node;
6504 xmlNsPtr isds_ns = NULL;
6506 if (!context) return IE_INVALID_CONTEXT;
6507 if (!service_name || !message_id) return IE_INVAL;
6508 if (!response || !code || !status_message) return IE_INVAL;
6509 if (!raw_response_length && raw_response) return IE_INVAL;
6511 /* Free output argument */
6512 xmlFreeDoc(*response); *response = NULL;
6513 if (raw_response) zfree(*raw_response);
6514 free(*code);
6515 free(*status_message);
6518 /* Check if connection is established
6519 * TODO: This check should be done donwstairs. */
6520 if (!context->curl) return IE_CONNECTION_CLOSED;
6522 service_name_locale = utf82locale((char*)service_name);
6523 message_id_locale = utf82locale(message_id);
6524 if (!service_name_locale || !message_id_locale) {
6525 err = IE_NOMEM;
6526 goto leave;
6529 /* Build request */
6530 request = xmlNewNode(NULL, service_name);
6531 if (!request) {
6532 isds_printf_message(context,
6533 _("Could not build %s request"), service_name_locale);
6534 err = IE_ERROR;
6535 goto leave;
6537 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6538 if(!isds_ns) {
6539 isds_log_message(context, _("Could not create ISDS name space"));
6540 err = IE_ERROR;
6541 goto leave;
6543 xmlSetNs(request, isds_ns);
6546 /* Add requested ID */
6547 err = validate_message_id_length(context, (xmlChar *) message_id);
6548 if (err) goto leave;
6549 INSERT_STRING(request, "dmID", message_id);
6552 isds_log(ILF_ISDS, ILL_DEBUG,
6553 _("Sending %s request for %s message ID to ISDS\n"),
6554 service_name_locale, message_id_locale);
6556 /* Send request */
6557 err = isds(context, service, request, response,
6558 raw_response, raw_response_length);
6559 xmlFreeNode(request); request = NULL;
6561 if (err) {
6562 isds_log(ILF_ISDS, ILL_DEBUG,
6563 _("Processing ISDS response on %s request failed\n"),
6564 service_name_locale);
6565 goto leave;
6568 /* Check for response status */
6569 err = isds_response_status(context, service, *response,
6570 code, status_message, NULL);
6571 if (err) {
6572 isds_log(ILF_ISDS, ILL_DEBUG,
6573 _("ISDS response on %s request is missing status\n"),
6574 service_name_locale);
6575 goto leave;
6578 /* Request processed, but nothing found */
6579 if (xmlStrcmp(*code, BAD_CAST "0000")) {
6580 char *code_locale = utf82locale((char*) *code);
6581 char *status_message_locale = utf82locale((char*) *status_message);
6582 isds_log(ILF_ISDS, ILL_DEBUG,
6583 _("Server refused %s request for %s message ID "
6584 "(code=%s, message=%s)\n"),
6585 service_name_locale, message_id_locale,
6586 code_locale, status_message_locale);
6587 isds_log_message(context, status_message_locale);
6588 free(code_locale);
6589 free(status_message_locale);
6590 err = IE_ISDS;
6591 goto leave;
6594 leave:
6595 free(message_id_locale);
6596 free(service_name_locale);
6597 xmlFreeNode(request);
6598 return err;
6602 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
6603 * signed data and free ISDS response.
6604 * @context is session context
6605 * @message_id is UTF-8 encoded message ID for loging purpose
6606 * @response is parsed XML document. It will be freed and NULLed in the middle
6607 * of function run to save memmory. This is not guaranted in case of error.
6608 * @request_name is name of ISDS request used to construct response root
6609 * element name and for logging purpose.
6610 * @raw is reallocated output buffer with DER encoded CMS data
6611 * @raw_length is size of @raw buffer in bytes
6612 * @returns standard error codes, in case of error, @raw will be freed and
6613 * NULLed, @response sometimes. */
6614 static isds_error find_extract_signed_data_free_response(
6615 struct isds_ctx *context, const xmlChar *message_id,
6616 xmlDocPtr *response, const xmlChar *request_name,
6617 void **raw, size_t *raw_length) {
6619 isds_error err = IE_SUCCESS;
6620 char *xpath_expression = NULL;
6621 xmlXPathContextPtr xpath_ctx = NULL;
6622 xmlXPathObjectPtr result = NULL;
6623 char *encoded_structure = NULL;
6625 if (!context) return IE_INVALID_CONTEXT;
6626 if (!raw) return IE_INVAL;
6627 zfree(*raw);
6628 if (!message_id || !response || !*response || !request_name || !raw_length)
6629 return IE_INVAL;
6631 /* Build XPath expression */
6632 xpath_expression = astrcat3("/isds:", (char *) request_name,
6633 "Response/isds:dmSignature");
6634 if (!xpath_expression) return IE_NOMEM;
6636 /* Extract data */
6637 xpath_ctx = xmlXPathNewContext(*response);
6638 if (!xpath_ctx) {
6639 err = IE_ERROR;
6640 goto leave;
6642 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6643 err = IE_ERROR;
6644 goto leave;
6646 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
6647 if (!result) {
6648 err = IE_ERROR;
6649 goto leave;
6651 /* Empty response */
6652 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6653 char *message_id_locale = utf82locale((char*) message_id);
6654 isds_printf_message(context,
6655 _("Server did not return any signed data for mesage ID `%s' "
6656 "on %s request"),
6657 message_id_locale, request_name);
6658 free(message_id_locale);
6659 err = IE_ISDS;
6660 goto leave;
6662 /* More reponses */
6663 if (result->nodesetval->nodeNr > 1) {
6664 char *message_id_locale = utf82locale((char*) message_id);
6665 isds_printf_message(context,
6666 _("Server did return more signed data for message ID `%s' "
6667 "on %s request"),
6668 message_id_locale, request_name);
6669 free(message_id_locale);
6670 err = IE_ISDS;
6671 goto leave;
6673 /* One response */
6674 xpath_ctx->node = result->nodesetval->nodeTab[0];
6676 /* Extract PKCS#7 structure */
6677 EXTRACT_STRING(".", encoded_structure);
6678 if (!encoded_structure) {
6679 isds_log_message(context, _("dmSignature element is empty"));
6682 /* Here we have delivery info as standalone CMS in encoded_structure.
6683 * We don't need any other data, free them: */
6684 xmlXPathFreeObject(result); result = NULL;
6685 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
6686 xmlFreeDoc(*response); *response = NULL;
6689 /* Decode PKCS#7 to DER format */
6690 *raw_length = b64decode(encoded_structure, raw);
6691 if (*raw_length == (size_t) -1) {
6692 isds_log_message(context,
6693 _("Error while Base64-decoding PKCS#7 structure"));
6694 err = IE_ERROR;
6695 goto leave;
6698 leave:
6699 if (err) {
6700 zfree(*raw);
6701 raw_length = 0;
6704 free(encoded_structure);
6705 xmlXPathFreeObject(result);
6706 xmlXPathFreeContext(xpath_ctx);
6707 free(xpath_expression);
6709 return err;
6713 /* Download incoming message envelope identified by ID.
6714 * @context is session context
6715 * @message_id is message identifier (you can get them from
6716 * isds_get_list_of_received_messages())
6717 * @message is automatically reallocated message retrieved from ISDS.
6718 * It will miss documents per se. Use isds_get_received_message(), if you are
6719 * interrested in documents (content) too.
6720 * Returned hash and timestamp require documents to be verifiable. */
6721 isds_error isds_get_received_envelope(struct isds_ctx *context,
6722 const char *message_id, struct isds_message **message) {
6724 isds_error err = IE_SUCCESS;
6725 xmlDocPtr response = NULL;
6726 xmlChar *code = NULL, *status_message = NULL;
6727 xmlXPathContextPtr xpath_ctx = NULL;
6728 xmlXPathObjectPtr result = NULL;
6730 if (!context) return IE_INVALID_CONTEXT;
6732 /* Free former message if any */
6733 if (!message) return IE_INVAL;
6734 isds_message_free(message);
6736 /* Do request and check for success */
6737 err = build_send_check_message_request(context, SERVICE_DM_INFO,
6738 BAD_CAST "MessageEnvelopeDownload", message_id,
6739 &response, NULL, NULL, &code, &status_message);
6740 if (err) goto leave;
6742 /* Extract data */
6743 xpath_ctx = xmlXPathNewContext(response);
6744 if (!xpath_ctx) {
6745 err = IE_ERROR;
6746 goto leave;
6748 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6749 err = IE_ERROR;
6750 goto leave;
6752 result = xmlXPathEvalExpression(
6753 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
6754 "isds:dmReturnedMessageEnvelope",
6755 xpath_ctx);
6756 if (!result) {
6757 err = IE_ERROR;
6758 goto leave;
6760 /* Empty response */
6761 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6762 char *message_id_locale = utf82locale((char*) message_id);
6763 isds_printf_message(context,
6764 _("Server did not return any envelope for ID `%s' "
6765 "on MessageEnvelopeDownload request"), message_id_locale);
6766 free(message_id_locale);
6767 err = IE_ISDS;
6768 goto leave;
6770 /* More envelops */
6771 if (result->nodesetval->nodeNr > 1) {
6772 char *message_id_locale = utf82locale((char*) message_id);
6773 isds_printf_message(context,
6774 _("Server did return more envelopes for ID `%s' "
6775 "on MessageEnvelopeDownload request"), message_id_locale);
6776 free(message_id_locale);
6777 err = IE_ISDS;
6778 goto leave;
6780 /* One message */
6781 xpath_ctx->node = result->nodesetval->nodeTab[0];
6783 /* Extract the envelope (= message without documents, hence 0) */
6784 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
6785 if (err) goto leave;
6787 /* Save XML blob */
6788 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
6789 &(*message)->raw_length);
6791 leave:
6792 if (err) {
6793 isds_message_free(message);
6796 xmlXPathFreeObject(result);
6797 xmlXPathFreeContext(xpath_ctx);
6799 free(code);
6800 free(status_message);
6801 xmlFreeDoc(response);
6803 if (!err)
6804 isds_log(ILF_ISDS, ILL_DEBUG,
6805 _("MessageEnvelopeDownload request processed by server "
6806 "successfully.\n")
6808 return err;
6812 /* Load delivery info of any format from buffer.
6813 * @context is session context
6814 * @raw_type advertises format of @buffer content. Only delivery info types
6815 * are accepted.
6816 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
6817 * retrieve such data from message->raw after calling
6818 * isds_get_signed_delivery_info().
6819 * @length is length of buffer in bytes.
6820 * @message is automatically reallocated message parsed from @buffer.
6821 * @strategy selects how buffer will be attached into raw isds_message member.
6822 * */
6823 isds_error isds_load_delivery_info(struct isds_ctx *context,
6824 const isds_raw_type raw_type,
6825 const void *buffer, const size_t length,
6826 struct isds_message **message, const isds_buffer_strategy strategy) {
6828 isds_error err = IE_SUCCESS;
6829 message_ns_type message_ns;
6830 xmlDocPtr message_doc = NULL;
6831 xmlXPathContextPtr xpath_ctx = NULL;
6832 xmlXPathObjectPtr result = NULL;
6833 void *xml_stream = NULL;
6834 size_t xml_stream_length = 0;
6836 if (!context) return IE_INVALID_CONTEXT;
6837 if (!message) return IE_INVAL;
6838 isds_message_free(message);
6839 if (!buffer) return IE_INVAL;
6842 /* Select buffer format and extract XML from CMS*/
6843 switch (raw_type) {
6844 case RAWTYPE_DELIVERYINFO:
6845 message_ns = MESSAGE_NS_UNSIGNED;
6846 xml_stream = (void *) buffer;
6847 xml_stream_length = length;
6848 break;
6850 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
6851 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
6852 xml_stream = (void *) buffer;
6853 xml_stream_length = length;
6854 break;
6856 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
6857 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
6858 err = extract_cms_data(context, buffer, length,
6859 &xml_stream, &xml_stream_length);
6860 if (err) goto leave;
6861 break;
6863 default:
6864 isds_log_message(context, _("Bad raw delivery representation type"));
6865 return IE_INVAL;
6866 break;
6869 isds_log(ILF_ISDS, ILL_DEBUG,
6870 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
6871 xml_stream_length, xml_stream);
6873 /* Convert delivery info XML stream into XPath context */
6874 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
6875 if (!message_doc) {
6876 err = IE_XML;
6877 goto leave;
6879 xpath_ctx = xmlXPathNewContext(message_doc);
6880 if (!xpath_ctx) {
6881 err = IE_ERROR;
6882 goto leave;
6884 /* XXX: Name spaces mangled for signed delivery info:
6885 * http://isds.czechpoint.cz/v20/delivery:
6887 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
6888 * <q:dmDelivery>
6889 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
6890 * <p:dmID>170272</p:dmID>
6891 * ...
6892 * </p:dmDm>
6893 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
6894 * ...
6895 * </q:dmEvents>...</q:dmEvents>
6896 * </q:dmDelivery>
6897 * </q:GetDeliveryInfoResponse>
6898 * */
6899 if (register_namespaces(xpath_ctx, message_ns)) {
6900 err = IE_ERROR;
6901 goto leave;
6903 result = xmlXPathEvalExpression(
6904 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
6905 xpath_ctx);
6906 if (!result) {
6907 err = IE_ERROR;
6908 goto leave;
6910 /* Empty delivery info */
6911 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6912 isds_printf_message(context,
6913 _("XML document ss not sisds:dmDelivery document"));
6914 err = IE_ISDS;
6915 goto leave;
6917 /* More delivery infos */
6918 if (result->nodesetval->nodeNr > 1) {
6919 isds_printf_message(context,
6920 _("XML document has more sisds:dmDelivery elements"));
6921 err = IE_ISDS;
6922 goto leave;
6924 /* One delivery info */
6925 xpath_ctx->node = result->nodesetval->nodeTab[0];
6927 /* Extract the envelope (= message without documents, hence 0).
6928 * XXX: extract_TReturnedMessage() can obtain attachments size,
6929 * but delivery info carries none. It's coded as option elements,
6930 * so it should work. */
6931 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
6932 if (err) goto leave;
6934 /* Extract events */
6935 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
6936 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
6937 if (err) { err = IE_ERROR; goto leave; }
6938 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
6939 if (err) goto leave;
6941 /* Append raw CMS structure into message */
6942 (*message)->raw_type = raw_type;
6943 switch (strategy) {
6944 case BUFFER_DONT_STORE:
6945 break;
6946 case BUFFER_COPY:
6947 (*message)->raw = malloc(length);
6948 if (!(*message)->raw) {
6949 err = IE_NOMEM;
6950 goto leave;
6952 memcpy((*message)->raw, buffer, length);
6953 (*message)->raw_length = length;
6954 break;
6955 case BUFFER_MOVE:
6956 (*message)->raw = (void *) buffer;
6957 (*message)->raw_length = length;
6958 break;
6959 default:
6960 err = IE_ENUM;
6961 goto leave;
6964 leave:
6965 if (err) {
6966 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
6967 isds_message_free(message);
6970 xmlXPathFreeObject(result);
6971 xmlXPathFreeContext(xpath_ctx);
6972 xmlFreeDoc(message_doc);
6973 if (xml_stream != buffer) cms_data_free(xml_stream);
6975 if (!err)
6976 isds_log(ILF_ISDS, ILL_DEBUG,
6977 _("Delivery info loaded successfully.\n"));
6978 return err;
6982 /* Download signed delivery infosheet of given message identified by ID.
6983 * @context is session context
6984 * @message_id is message identifier (you can get them from
6985 * isds_get_list_of_{sent,received}_messages())
6986 * @message is automatically reallocated message retrieved from ISDS.
6987 * It will miss documents per se. Use isds_get_signed_received_message(),
6988 * if you are interrested in documents (content). OTOH, only this function
6989 * can get list events message has gone through. */
6990 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
6991 const char *message_id, struct isds_message **message) {
6993 isds_error err = IE_SUCCESS;
6994 xmlDocPtr response = NULL;
6995 xmlChar *code = NULL, *status_message = NULL;
6996 void *raw = NULL;
6997 size_t raw_length = 0;
6999 if (!context) return IE_INVALID_CONTEXT;
7001 /* Free former message if any */
7002 if (!message) return IE_INVAL;
7003 isds_message_free(message);
7005 /* Do request and check for success */
7006 err = build_send_check_message_request(context, SERVICE_DM_INFO,
7007 BAD_CAST "GetSignedDeliveryInfo", message_id,
7008 &response, NULL, NULL, &code, &status_message);
7009 if (err) goto leave;
7011 /* Find signed delivery info, extract it into raw and maybe free
7012 * response */
7013 err = find_extract_signed_data_free_response(context,
7014 (xmlChar *)message_id, &response,
7015 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
7016 if (err) goto leave;
7018 /* Parse delivery info */
7019 err = isds_load_delivery_info(context,
7020 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
7021 message, BUFFER_MOVE);
7022 if (err) goto leave;
7024 raw = NULL;
7026 leave:
7027 if (err) {
7028 isds_message_free(message);
7031 free(raw);
7032 free(code);
7033 free(status_message);
7034 xmlFreeDoc(response);
7036 if (!err)
7037 isds_log(ILF_ISDS, ILL_DEBUG,
7038 _("GetSignedDeliveryInfo request processed by server "
7039 "successfully.\n")
7041 return err;
7045 /* Download delivery infosheet of given message identified by ID.
7046 * @context is session context
7047 * @message_id is message identifier (you can get them from
7048 * isds_get_list_of_{sent,received}_messages())
7049 * @message is automatically reallocated message retrieved from ISDS.
7050 * It will miss documents per se. Use isds_get_received_message(), if you are
7051 * interrested in documents (content). OTOH, only this function can get list
7052 * events message has gone through. */
7053 isds_error isds_get_delivery_info(struct isds_ctx *context,
7054 const char *message_id, struct isds_message **message) {
7056 isds_error err = IE_SUCCESS;
7057 xmlDocPtr response = NULL;
7058 xmlChar *code = NULL, *status_message = NULL;
7059 xmlXPathContextPtr xpath_ctx = NULL;
7060 xmlXPathObjectPtr result = NULL;
7061 xmlNodePtr delivery_node = NULL;
7063 if (!context) return IE_INVALID_CONTEXT;
7065 /* Free former message if any */
7066 if (!message) return IE_INVAL;
7067 isds_message_free(message);
7069 /* Do request and check for success */
7070 err = build_send_check_message_request(context, SERVICE_DM_INFO,
7071 BAD_CAST "GetDeliveryInfo", message_id,
7072 &response, NULL, NULL, &code, &status_message);
7073 if (err) goto leave;
7075 /* Extract data */
7076 xpath_ctx = xmlXPathNewContext(response);
7077 if (!xpath_ctx) {
7078 err = IE_ERROR;
7079 goto leave;
7081 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7082 err = IE_ERROR;
7083 goto leave;
7085 result = xmlXPathEvalExpression(
7086 BAD_CAST "/isds:GetDeliveryInfoResponse/isds:dmDelivery",
7087 xpath_ctx);
7088 if (!result) {
7089 err = IE_ERROR;
7090 goto leave;
7092 /* Empty response */
7093 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7094 char *message_id_locale = utf82locale((char*) message_id);
7095 isds_printf_message(context,
7096 _("Server did not return any delivery info for ID `%s' "
7097 "on GetDeliveryInfo request"), message_id_locale);
7098 free(message_id_locale);
7099 err = IE_ISDS;
7100 goto leave;
7102 /* More delivery infos */
7103 if (result->nodesetval->nodeNr > 1) {
7104 char *message_id_locale = utf82locale((char*) message_id);
7105 isds_printf_message(context,
7106 _("Server did return more delivery infos for ID `%s' "
7107 "on GetDeliveryInfo request"), message_id_locale);
7108 free(message_id_locale);
7109 err = IE_ISDS;
7110 goto leave;
7112 /* One delivery info */
7113 xpath_ctx->node = delivery_node = result->nodesetval->nodeTab[0];
7115 /* Extract the envelope (= message without documents, hence 0).
7116 * XXX: extract_TReturnedMessage() can obtain attachments size,
7117 * but delivery info carries none. It's coded as option elements,
7118 * so it should work. */
7119 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
7120 if (err) goto leave;
7122 /* Extract events */
7123 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmEvents", xpath_ctx);
7124 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
7125 if (err) { err = IE_ERROR; goto leave; }
7126 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
7127 if (err) goto leave;
7129 /* Save XML blob */
7130 err = serialize_subtree(context, delivery_node, &(*message)->raw,
7131 &(*message)->raw_length);
7133 leave:
7134 if (err) {
7135 isds_message_free(message);
7138 xmlXPathFreeObject(result);
7139 xmlXPathFreeContext(xpath_ctx);
7141 free(code);
7142 free(status_message);
7143 xmlFreeDoc(response);
7145 if (!err)
7146 isds_log(ILF_ISDS, ILL_DEBUG,
7147 _("GetDeliveryInfo request processed by server "
7148 "successfully.\n")
7150 return err;
7154 /* Load incoming message from buffer.
7155 * @context is session context
7156 * @buffer XML stream with unsigned message. You can retrieve such data from
7157 * message->raw after calling isds_get_received_message().
7158 * @length is length of buffer in bytes.
7159 * @message is automatically reallocated message parsed from @buffer.
7160 * @strategy selects how buffer will be attached into raw isds_message member.
7161 * */
7162 isds_error isds_load_received_message(struct isds_ctx *context,
7163 const void *buffer, const size_t length,
7164 struct isds_message **message, const isds_buffer_strategy strategy) {
7166 isds_error err = IE_SUCCESS;
7167 xmlDocPtr message_doc = NULL;
7168 xmlXPathContextPtr xpath_ctx = NULL;
7169 xmlXPathObjectPtr result = NULL;
7171 if (!context) return IE_INVALID_CONTEXT;
7172 if (!message) return IE_INVAL;
7173 isds_message_free(message);
7174 if (!buffer) return IE_INVAL;
7177 isds_log(ILF_ISDS, ILL_DEBUG,
7178 _("Incoming message content:\n%.*s\nEnd of message\n"),
7179 length, buffer);
7181 /* Convert extracted messages XML stream into XPath context */
7182 message_doc = xmlParseMemory(buffer, length);
7183 if (!message_doc) {
7184 err = IE_XML;
7185 goto leave;
7187 xpath_ctx = xmlXPathNewContext(message_doc);
7188 if (!xpath_ctx) {
7189 err = IE_ERROR;
7190 goto leave;
7192 /* XXX: Standard name space */
7193 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7194 err = IE_ERROR;
7195 goto leave;
7197 result = xmlXPathEvalExpression(
7198 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
7199 xpath_ctx);
7200 if (!result) {
7201 err = IE_ERROR;
7202 goto leave;
7204 /* Missing dmReturnedMessage */
7205 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7206 isds_printf_message(context,
7207 _("XML document does not contain isds:dmReturnedMessage "
7208 "element"));
7209 err = IE_ISDS;
7210 goto leave;
7212 /* More elements. This should never happen. */
7213 if (result->nodesetval->nodeNr > 1) {
7214 isds_printf_message(context,
7215 _("XML document has more isds:dmReturnedMessage elements"));
7216 err = IE_ISDS;
7217 goto leave;
7219 /* One message */
7220 xpath_ctx->node = result->nodesetval->nodeTab[0];
7222 /* Extract the message */
7223 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
7224 if (err) goto leave;
7226 /* Append XML stream into message */
7227 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
7228 switch (strategy) {
7229 case BUFFER_DONT_STORE:
7230 break;
7231 case BUFFER_COPY:
7232 (*message)->raw = malloc(length);
7233 if (!(*message)->raw) {
7234 err = IE_NOMEM;
7235 goto leave;
7237 memcpy((*message)->raw, buffer, length);
7238 (*message)->raw_length = length;
7239 break;
7240 case BUFFER_MOVE:
7241 (*message)->raw = (void *) buffer;
7242 (*message)->raw_length = length;
7243 break;
7244 default:
7245 err = IE_ENUM;
7246 goto leave;
7249 leave:
7250 if (err) {
7251 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
7252 isds_message_free(message);
7255 xmlFreeDoc(message_doc);
7256 xmlXPathFreeObject(result);
7257 xmlXPathFreeContext(xpath_ctx);
7259 if (!err)
7260 isds_log(ILF_ISDS, ILL_DEBUG,
7261 _("Incoming message loaded successfully.\n"));
7262 return err;
7266 /* Download incoming 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_received_messages())
7270 * @message is automatically reallocated message retrieved from ISDS */
7271 isds_error isds_get_received_message(struct isds_ctx *context,
7272 const char *message_id, struct isds_message **message) {
7274 isds_error err = IE_SUCCESS;
7275 xmlDocPtr response = NULL;
7276 void *xml_stream = NULL;
7277 size_t xml_stream_length;
7278 xmlChar *code = NULL, *status_message = NULL;
7279 xmlXPathContextPtr xpath_ctx = NULL;
7280 xmlXPathObjectPtr result = NULL;
7281 char *phys_path = NULL;
7282 size_t phys_start, phys_end;
7284 if (!context) return IE_INVALID_CONTEXT;
7286 /* Free former message if any */
7287 if (message) isds_message_free(message);
7289 /* Do request and check for success */
7290 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
7291 BAD_CAST "MessageDownload", message_id,
7292 &response, &xml_stream, &xml_stream_length,
7293 &code, &status_message);
7294 if (err) goto leave;
7296 /* Extract data */
7297 xpath_ctx = xmlXPathNewContext(response);
7298 if (!xpath_ctx) {
7299 err = IE_ERROR;
7300 goto leave;
7302 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7303 err = IE_ERROR;
7304 goto leave;
7306 result = xmlXPathEvalExpression(
7307 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
7308 xpath_ctx);
7309 if (!result) {
7310 err = IE_ERROR;
7311 goto leave;
7313 /* Empty response */
7314 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7315 char *message_id_locale = utf82locale((char*) message_id);
7316 isds_printf_message(context,
7317 _("Server did not return any message for ID `%s' "
7318 "on MessageDownload request"), message_id_locale);
7319 free(message_id_locale);
7320 err = IE_ISDS;
7321 goto leave;
7323 /* More messages */
7324 if (result->nodesetval->nodeNr > 1) {
7325 char *message_id_locale = utf82locale((char*) message_id);
7326 isds_printf_message(context,
7327 _("Server did return more messages for ID `%s' "
7328 "on MessageDownload request"), message_id_locale);
7329 free(message_id_locale);
7330 err = IE_ISDS;
7331 goto leave;
7333 /* One message */
7334 xpath_ctx->node = result->nodesetval->nodeTab[0];
7336 /* Extract the message */
7337 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
7338 if (err) goto leave;
7340 /* Locate raw XML blob */
7341 phys_path = strdup(
7342 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
7343 PHYSXML_ELEMENT_SEPARATOR
7344 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
7345 PHYSXML_ELEMENT_SEPARATOR
7346 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
7348 if (!phys_path) {
7349 err = IE_NOMEM;
7350 goto leave;
7352 err = find_element_boundary(xml_stream, xml_stream_length,
7353 phys_path, &phys_start, &phys_end);
7354 zfree(phys_path);
7355 if (err) {
7356 isds_log_message(context,
7357 _("Substring with isds:MessageDownloadResponse element "
7358 "could not be located in raw SOAP message"));
7359 goto leave;
7361 /* Save XML blob */
7362 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
7363 &(*message)->raw_length);*/
7364 /* TODO: Store name space declarations from ancestors */
7365 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
7366 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
7367 (*message)->raw_length = phys_end - phys_start + 1;
7368 (*message)->raw = malloc((*message)->raw_length);
7369 if (!(*message)->raw) {
7370 err = IE_NOMEM;
7371 goto leave;
7373 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
7376 leave:
7377 if (err) {
7378 isds_message_free(message);
7381 free(phys_path);
7383 xmlXPathFreeObject(result);
7384 xmlXPathFreeContext(xpath_ctx);
7386 free(code);
7387 free(status_message);
7388 free(xml_stream);
7389 xmlFreeDoc(response);
7391 if (!err)
7392 isds_log(ILF_ISDS, ILL_DEBUG,
7393 _("MessageDownload request processed by server "
7394 "successfully.\n")
7396 return err;
7400 /* Load signed message from buffer.
7401 * @context is session context
7402 * @outgoing is true if message is outgoing, false if message is incoming
7403 * @buffer is DER encoded PKCS#7 structure with signed message. You can
7404 * retrieve such data from message->raw after calling
7405 * isds_get_signed_{received,sent}_message().
7406 * @length is length of buffer in bytes.
7407 * @message is automatically reallocated message parsed from @buffer.
7408 * @strategy selects how buffer will be attached into raw isds_message member.
7409 * */
7410 isds_error isds_load_signed_message(struct isds_ctx *context,
7411 const _Bool outgoing, const void *buffer, const size_t length,
7412 struct isds_message **message, const isds_buffer_strategy strategy) {
7414 isds_error err = IE_SUCCESS;
7415 xmlDocPtr message_doc = NULL;
7416 xmlXPathContextPtr xpath_ctx = NULL;
7417 xmlXPathObjectPtr result = NULL;
7418 void *xml_stream = NULL;
7419 size_t xml_stream_length = 0;
7421 if (!context) return IE_INVALID_CONTEXT;
7422 if (!message) return IE_INVAL;
7423 isds_message_free(message);
7424 if (!buffer) return IE_INVAL;
7427 /* Extract message from PKCS#7 structure */
7428 err = extract_cms_data(context, buffer, length,
7429 &xml_stream, &xml_stream_length);
7430 if (err) goto leave;
7432 isds_log(ILF_ISDS, ILL_DEBUG, (outgoing) ?
7433 _("Signed outgoing message content:\n%.*s\nEnd of message\n") :
7434 _("Signed incoming message content:\n%.*s\nEnd of message\n"),
7435 xml_stream_length, xml_stream);
7437 /* Convert extracted messages XML stream into XPath context */
7438 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
7439 if (!message_doc) {
7440 err = IE_XML;
7441 goto leave;
7443 xpath_ctx = xmlXPathNewContext(message_doc);
7444 if (!xpath_ctx) {
7445 err = IE_ERROR;
7446 goto leave;
7448 /* XXX: Name spaces mangled for outgoing direction:
7449 * http://isds.czechpoint.cz/v20/SentMessage:
7451 * <q:MessageDownloadResponse
7452 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
7453 * <q:dmReturnedMessage>
7454 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
7455 * <p:dmID>151916</p:dmID>
7456 * ...
7457 * </p:dmDm>
7458 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
7459 * ...
7460 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
7461 * </q:dmReturnedMessage>
7462 * </q:MessageDownloadResponse>
7464 * XXX: Name spaces mangled for incoming direction:
7465 * http://isds.czechpoint.cz/v20/message:
7467 * <q:MessageDownloadResponse
7468 * xmlns:q="http://isds.czechpoint.cz/v20/message">
7469 * <q:dmReturnedMessage>
7470 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
7471 * <p:dmID>151916</p:dmID>
7472 * ...
7473 * </p:dmDm>
7474 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
7475 * ...
7476 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
7477 * </q:dmReturnedMessage>
7478 * </q:MessageDownloadResponse>
7480 * Stupidity of ISDS developers is unlimited */
7481 if (register_namespaces(xpath_ctx, (outgoing) ?
7482 MESSAGE_NS_SIGNED_OUTGOING : MESSAGE_NS_SIGNED_INCOMING)) {
7483 err = IE_ERROR;
7484 goto leave;
7486 /* XXX: Embeded message XML document is always rooted as
7487 * /sisds:MessageDownloadResponse (even outgoing message). */
7488 result = xmlXPathEvalExpression(
7489 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
7490 xpath_ctx);
7491 if (!result) {
7492 err = IE_ERROR;
7493 goto leave;
7495 /* Empty embedded message */
7496 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7497 isds_printf_message(context,
7498 _("XML document embedded into PKCS#7 structure is not "
7499 "sisds:dmReturnedMessage document"));
7500 err = IE_ISDS;
7501 goto leave;
7503 /* More embedded messages */
7504 if (result->nodesetval->nodeNr > 1) {
7505 isds_printf_message(context,
7506 _("XML document embedded into PKCS#7 structure has more "
7507 "root sisds:dmReturnedMessage elements"));
7508 err = IE_ISDS;
7509 goto leave;
7511 /* One embedded message */
7512 xpath_ctx->node = result->nodesetval->nodeTab[0];
7514 /* Extract the message */
7515 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
7516 if (err) goto leave;
7518 /* Append raw CMS structure into message */
7519 (*message)->raw_type = (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
7520 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
7521 switch (strategy) {
7522 case BUFFER_DONT_STORE:
7523 break;
7524 case BUFFER_COPY:
7525 (*message)->raw = malloc(length);
7526 if (!(*message)->raw) {
7527 err = IE_NOMEM;
7528 goto leave;
7530 memcpy((*message)->raw, buffer, length);
7531 (*message)->raw_length = length;
7532 break;
7533 case BUFFER_MOVE:
7534 (*message)->raw = (void *) buffer;
7535 (*message)->raw_length = length;
7536 break;
7537 default:
7538 err = IE_ENUM;
7539 goto leave;
7543 leave:
7544 if (err) {
7545 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
7546 isds_message_free(message);
7549 xmlFreeDoc(message_doc);
7550 cms_data_free(xml_stream);
7551 xmlXPathFreeObject(result);
7552 xmlXPathFreeContext(xpath_ctx);
7554 if (!err)
7555 isds_log(ILF_ISDS, ILL_DEBUG,
7556 _("Signed message loaded successfully.\n"));
7557 return err;
7561 /* Load message of any type from buffer.
7562 * @context is session context
7563 * @raw_type defines content type of @buffer. Only message types are allowed.
7564 * @buffer is message raw representation. Format (CMS, plain signed,
7565 * message direction) is defined in @raw_type. You can retrieve such data
7566 * from message->raw after calling isds_get_[signed]{received,sent}_message().
7567 * @length is length of buffer in bytes.
7568 * @message is automatically reallocated message parsed from @buffer.
7569 * @strategy selects how buffer will be attached into raw isds_message member.
7570 * */
7571 isds_error isds_load_message(struct isds_ctx *context,
7572 const isds_raw_type raw_type, const void *buffer, const size_t length,
7573 struct isds_message **message, const isds_buffer_strategy strategy) {
7575 isds_error err = IE_SUCCESS;
7576 void *xml_stream = NULL;
7577 size_t xml_stream_length = 0;
7578 message_ns_type message_ns;
7579 xmlDocPtr message_doc = NULL;
7580 xmlXPathContextPtr xpath_ctx = NULL;
7581 xmlXPathObjectPtr result = NULL;
7583 if (!context) return IE_INVALID_CONTEXT;
7584 if (!message) return IE_INVAL;
7585 isds_message_free(message);
7586 if (!buffer) return IE_INVAL;
7589 /* Select buffer format and extract XML from CMS*/
7590 switch (raw_type) {
7591 case RAWTYPE_INCOMING_MESSAGE:
7592 message_ns = MESSAGE_NS_UNSIGNED;
7593 xml_stream = (void *) buffer;
7594 xml_stream_length = length;
7595 break;
7597 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
7598 message_ns = MESSAGE_NS_SIGNED_INCOMING;
7599 xml_stream = (void *) buffer;
7600 xml_stream_length = length;
7601 break;
7603 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
7604 message_ns = MESSAGE_NS_SIGNED_INCOMING;
7605 err = extract_cms_data(context, buffer, length,
7606 &xml_stream, &xml_stream_length);
7607 if (err) goto leave;
7608 break;
7610 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
7611 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
7612 xml_stream = (void *) buffer;
7613 xml_stream_length = length;
7614 break;
7616 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
7617 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
7618 err = extract_cms_data(context, buffer, length,
7619 &xml_stream, &xml_stream_length);
7620 if (err) goto leave;
7621 break;
7623 default:
7624 isds_log_message(context, _("Bad raw message representation type"));
7625 return IE_INVAL;
7626 break;
7629 isds_log(ILF_ISDS, ILL_DEBUG,
7630 _("Loading message:\n%.*s\nEnd of message\n"),
7631 xml_stream_length, xml_stream);
7633 /* Convert messages XML stream into XPath context */
7634 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
7635 if (!message_doc) {
7636 err = IE_XML;
7637 goto leave;
7639 xpath_ctx = xmlXPathNewContext(message_doc);
7640 if (!xpath_ctx) {
7641 err = IE_ERROR;
7642 goto leave;
7644 /* XXX: Standard name space for unsigned icoming direction:
7645 * http://isds.czechpoint.cz/v20/SentMessage
7647 * XXX: Name spaces mangled for signed outgoing direction:
7648 * http://isds.czechpoint.cz/v20/SentMessage:
7650 * <q:MessageDownloadResponse
7651 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
7652 * <q:dmReturnedMessage>
7653 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
7654 * <p:dmID>151916</p:dmID>
7655 * ...
7656 * </p:dmDm>
7657 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
7658 * ...
7659 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
7660 * </q:dmReturnedMessage>
7661 * </q:MessageDownloadResponse>
7663 * XXX: Name spaces mangled for signed incoming direction:
7664 * http://isds.czechpoint.cz/v20/message:
7666 * <q:MessageDownloadResponse
7667 * xmlns:q="http://isds.czechpoint.cz/v20/message">
7668 * <q:dmReturnedMessage>
7669 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
7670 * <p:dmID>151916</p:dmID>
7671 * ...
7672 * </p:dmDm>
7673 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
7674 * ...
7675 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
7676 * </q:dmReturnedMessage>
7677 * </q:MessageDownloadResponse>
7679 * Stupidity of ISDS developers is unlimited */
7680 if (register_namespaces(xpath_ctx, message_ns)) {
7681 err = IE_ERROR;
7682 goto leave;
7684 result = xmlXPathEvalExpression(
7685 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
7686 xpath_ctx);
7687 if (!result) {
7688 err = IE_ERROR;
7689 goto leave;
7691 /* Empty message */
7692 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7693 isds_printf_message(context,
7694 _("XML document does not contain "
7695 "sisds:dmReturnedMessage element"));
7696 err = IE_ISDS;
7697 goto leave;
7699 /* More messages */
7700 if (result->nodesetval->nodeNr > 1) {
7701 isds_printf_message(context,
7702 _("XML document has more sisds:dmReturnedMessage elements"));
7703 err = IE_ISDS;
7704 goto leave;
7706 /* One message */
7707 xpath_ctx->node = result->nodesetval->nodeTab[0];
7709 /* Extract the message */
7710 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
7711 if (err) goto leave;
7713 /* Append raw buffer into message */
7714 (*message)->raw_type = raw_type;
7715 switch (strategy) {
7716 case BUFFER_DONT_STORE:
7717 break;
7718 case BUFFER_COPY:
7719 (*message)->raw = malloc(length);
7720 if (!(*message)->raw) {
7721 err = IE_NOMEM;
7722 goto leave;
7724 memcpy((*message)->raw, buffer, length);
7725 (*message)->raw_length = length;
7726 break;
7727 case BUFFER_MOVE:
7728 (*message)->raw = (void *) buffer;
7729 (*message)->raw_length = length;
7730 break;
7731 default:
7732 err = IE_ENUM;
7733 goto leave;
7737 leave:
7738 if (err) {
7739 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
7740 isds_message_free(message);
7743 if (xml_stream != buffer) cms_data_free(xml_stream);
7744 xmlXPathFreeObject(result);
7745 xmlXPathFreeContext(xpath_ctx);
7746 xmlFreeDoc(message_doc);
7748 if (!err)
7749 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
7750 return err;
7754 /* Download signed incoming/outgoing message identified by ID.
7755 * @context is session context
7756 * @output is true for outging message, false for incoming message
7757 * @message_id is message identifier (you can get them from
7758 * isds_get_list_of_{sent,received}_messages())
7759 * @message is automatically reallocated message retrieved from ISDS. The raw
7760 * memeber will be filled with PKCS#7 structure in DER format. */
7761 _hidden isds_error isds_get_signed_message(struct isds_ctx *context,
7762 const _Bool outgoing, const char *message_id,
7763 struct isds_message **message) {
7765 isds_error err = IE_SUCCESS;
7766 xmlDocPtr response = NULL;
7767 xmlChar *code = NULL, *status_message = NULL;
7768 xmlXPathContextPtr xpath_ctx = NULL;
7769 xmlXPathObjectPtr result = NULL;
7770 char *encoded_structure = NULL;
7771 void *raw = NULL;
7772 size_t raw_length = 0;
7774 if (!context) return IE_INVALID_CONTEXT;
7775 if (!message) return IE_INVAL;
7776 isds_message_free(message);
7778 /* Do request and check for success */
7779 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
7780 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
7781 BAD_CAST "SignedMessageDownload",
7782 message_id, &response, NULL, NULL, &code, &status_message);
7783 if (err) goto leave;
7785 /* Find signed message, extract it into raw and maybe free
7786 * response */
7787 err = find_extract_signed_data_free_response(context,
7788 (xmlChar *)message_id, &response,
7789 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
7790 BAD_CAST "SignedMessageDownload",
7791 &raw, &raw_length);
7792 if (err) goto leave;
7794 /* Parse message */
7795 err = isds_load_message(context,
7796 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
7797 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
7798 raw, raw_length, message, BUFFER_MOVE);
7799 if (err) goto leave;
7801 raw = NULL;
7803 leave:
7804 if (err) {
7805 isds_message_free(message);
7808 free(encoded_structure);
7809 xmlXPathFreeObject(result);
7810 xmlXPathFreeContext(xpath_ctx);
7811 free(raw);
7813 free(code);
7814 free(status_message);
7815 xmlFreeDoc(response);
7817 if (!err)
7818 isds_log(ILF_ISDS, ILL_DEBUG,
7819 (outgoing) ?
7820 _("SignedSentMessageDownload request processed by server "
7821 "successfully.\n") :
7822 _("SignedMessageDownload request processed by server "
7823 "successfully.\n")
7825 return err;
7829 /* Download signed incoming message identified by ID.
7830 * @context is session context
7831 * @message_id is message identifier (you can get them from
7832 * isds_get_list_of_received_messages())
7833 * @message is automatically reallocated message retrieved from ISDS. The raw
7834 * memeber will be filled with PKCS#7 structure in DER format. */
7835 isds_error isds_get_signed_received_message(struct isds_ctx *context,
7836 const char *message_id, struct isds_message **message) {
7837 return isds_get_signed_message(context, 0, message_id, message);
7841 /* Download signed outgoing message identified by ID.
7842 * @context is session context
7843 * @message_id is message identifier (you can get them from
7844 * isds_get_list_of_sent_messages())
7845 * @message is automatically reallocated message retrieved from ISDS. The raw
7846 * memeber will be filled with PKCS#7 structure in DER format. */
7847 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
7848 const char *message_id, struct isds_message **message) {
7849 return isds_get_signed_message(context, 1, message_id, message);
7853 /* Retrieve hash of message identified by ID stored in ISDS.
7854 * @context is session context
7855 * @message_id is message identifier
7856 * @hash is automatically reallocated message hash downloaded from ISDS.
7857 * Message must exist in system and must not be deleted. */
7858 isds_error isds_download_message_hash(struct isds_ctx *context,
7859 const char *message_id, struct isds_hash **hash) {
7861 isds_error err = IE_SUCCESS;
7862 xmlDocPtr response = NULL;
7863 xmlChar *code = NULL, *status_message = NULL;
7864 xmlXPathContextPtr xpath_ctx = NULL;
7865 xmlXPathObjectPtr result = NULL;
7867 if (!context) return IE_INVALID_CONTEXT;
7869 isds_hash_free(hash);
7871 err = build_send_check_message_request(context, SERVICE_DM_INFO,
7872 BAD_CAST "VerifyMessage", message_id,
7873 &response, NULL, NULL, &code, &status_message);
7874 if (err) goto leave;
7877 /* Extract data */
7878 xpath_ctx = xmlXPathNewContext(response);
7879 if (!xpath_ctx) {
7880 err = IE_ERROR;
7881 goto leave;
7883 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7884 err = IE_ERROR;
7885 goto leave;
7887 result = xmlXPathEvalExpression(
7888 BAD_CAST "/isds:VerifyMessageResponse",
7889 xpath_ctx);
7890 if (!result) {
7891 err = IE_ERROR;
7892 goto leave;
7894 /* Empty response */
7895 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7896 char *message_id_locale = utf82locale((char*) message_id);
7897 isds_printf_message(context,
7898 _("Server did not return any response for ID `%s' "
7899 "on VerifyMessage request"), message_id_locale);
7900 free(message_id_locale);
7901 err = IE_ISDS;
7902 goto leave;
7904 /* More responses */
7905 if (result->nodesetval->nodeNr > 1) {
7906 char *message_id_locale = utf82locale((char*) message_id);
7907 isds_printf_message(context,
7908 _("Server did return more responses for ID `%s' "
7909 "on VerifyMessage request"), message_id_locale);
7910 free(message_id_locale);
7911 err = IE_ISDS;
7912 goto leave;
7914 /* One response */
7915 xpath_ctx->node = result->nodesetval->nodeTab[0];
7917 /* Extract the hash */
7918 err = find_and_extract_DmHash(context, hash, xpath_ctx);
7920 leave:
7921 if (err) {
7922 isds_hash_free(hash);
7925 xmlXPathFreeObject(result);
7926 xmlXPathFreeContext(xpath_ctx);
7928 free(code);
7929 free(status_message);
7930 xmlFreeDoc(response);
7932 if (!err)
7933 isds_log(ILF_ISDS, ILL_DEBUG,
7934 _("VerifyMessage request processed by server "
7935 "successfully.\n")
7937 return err;
7941 /* Mark message as read. This is a transactional commit function to acknoledge
7942 * to ISDS the message has been downloaded and processed by client properly.
7943 * @context is session context
7944 * @message_id is message identifier. */
7945 isds_error isds_mark_message_read(struct isds_ctx *context,
7946 const char *message_id) {
7948 isds_error err = IE_SUCCESS;
7949 xmlDocPtr response = NULL;
7950 xmlChar *code = NULL, *status_message = NULL;
7952 if (!context) return IE_INVALID_CONTEXT;
7954 /* Do request and check for success */
7955 err = build_send_check_message_request(context, SERVICE_DM_INFO,
7956 BAD_CAST "MarkMessageAsDownloaded", message_id,
7957 &response, NULL, NULL, &code, &status_message);
7959 free(code);
7960 free(status_message);
7961 xmlFreeDoc(response);
7963 if (!err)
7964 isds_log(ILF_ISDS, ILL_DEBUG,
7965 _("MarkMessageAsDownloaded request processed by server "
7966 "successfully.\n")
7968 return err;
7971 /* Mark message as received by recipient. This is applicable only to
7972 * commercial message. There is no specified way how to distinguishe
7973 * commercial message from government message yet. Government message is
7974 * received automatically (by law), commenrcial message on recipient request.
7975 * @context is session context
7976 * @message_id is message identifier. */
7977 isds_error isds_mark_message_received(struct isds_ctx *context,
7978 const char *message_id) {
7980 isds_error err = IE_SUCCESS;
7981 xmlDocPtr response = NULL;
7982 xmlChar *code = NULL, *status_message = NULL;
7984 if (!context) return IE_INVALID_CONTEXT;
7986 /* Do request and check for success */
7987 err = build_send_check_message_request(context, SERVICE_DM_INFO,
7988 BAD_CAST "ConfirmDelivery", message_id,
7989 &response, NULL, NULL, &code, &status_message);
7991 free(code);
7992 free(status_message);
7993 xmlFreeDoc(response);
7995 if (!err)
7996 isds_log(ILF_ISDS, ILL_DEBUG,
7997 _("ConfirmDelivery request processed by server "
7998 "successfully.\n")
8000 return err;
8004 #undef INSERT_ELEMENT
8005 #undef CHECK_FOR_STRING_LENGTH
8006 #undef INSERT_STRING_ATTRIBUTE
8007 #undef INSERT_ULONGINTNOPTR
8008 #undef INSERT_ULONGINT
8009 #undef INSERT_LONGINT
8010 #undef INSERT_BOOLEAN
8011 #undef INSERT_SCALAR_BOOLEAN
8012 #undef INSERT_STRING
8013 #undef EXTRACT_STRING_ATTRIBUTE
8014 #undef EXTRACT_ULONGINT
8015 #undef EXTRACT_LONGINT
8016 #undef EXTRACT_BOOLEAN
8017 #undef EXTRACT_STRING
8020 /* Compute hash of message from raw representation and store it into envelope.
8021 * Original hash structure will be destroyed in envelope.
8022 * @context is session context
8023 * @message is message carrying raw XML message blob
8024 * @algorithm is desired hash algorithm to use */
8025 isds_error isds_compute_message_hash(struct isds_ctx *context,
8026 struct isds_message *message, const isds_hash_algorithm algorithm) {
8027 isds_error err = IE_SUCCESS;
8028 const char *nsuri;
8029 void *xml_stream = NULL;
8030 size_t xml_stream_length;
8031 size_t phys_start, phys_end;
8032 char *phys_path = NULL;
8033 struct isds_hash *new_hash = NULL;
8036 if (!context) return IE_INVALID_CONTEXT;
8037 if (!message) return IE_INVAL;
8039 if (!message->raw) {
8040 isds_log_message(context,
8041 _("Message does not carry raw representation"));
8042 return IE_INVAL;
8045 switch (message->raw_type) {
8046 case RAWTYPE_INCOMING_MESSAGE:
8047 nsuri = ISDS_NS;
8048 xml_stream = message->raw;
8049 xml_stream_length = message->raw_length;
8050 break;
8052 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
8053 nsuri = SISDS_INCOMING_NS;
8054 xml_stream = message->raw;
8055 xml_stream_length = message->raw_length;
8056 break;
8058 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
8059 nsuri = SISDS_INCOMING_NS;
8060 err = extract_cms_data(context, message->raw, message->raw_length,
8061 &xml_stream, &xml_stream_length);
8062 if (err) goto leave;
8063 break;
8065 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
8066 nsuri = SISDS_OUTGOING_NS;
8067 xml_stream = message->raw;
8068 xml_stream_length = message->raw_length;
8069 break;
8071 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
8072 nsuri = SISDS_OUTGOING_NS;
8073 err = extract_cms_data(context, message->raw, message->raw_length,
8074 &xml_stream, &xml_stream_length);
8075 if (err) goto leave;
8076 break;
8078 default:
8079 isds_log_message(context, _("Bad raw representation type"));
8080 return IE_INVAL;
8081 break;
8085 /* XXX: Hash is computed from original string represinting isds:dmDm
8086 * subtree. That means no encoding, white space, xmlns attributes changes.
8087 * In other words, input for hash can be invalid XML stream. */
8088 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
8089 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
8090 PHYSXML_ELEMENT_SEPARATOR,
8091 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
8092 PHYSXML_ELEMENT_SEPARATOR
8093 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
8094 err = IE_NOMEM;
8095 goto leave;
8097 err = find_element_boundary(xml_stream, xml_stream_length,
8098 phys_path, &phys_start, &phys_end);
8099 zfree(phys_path);
8100 if (err) {
8101 isds_log_message(context,
8102 _("Substring with isds:dmDM element could not be located "
8103 "in raw message"));
8104 goto leave;
8108 /* Compute hash */
8109 new_hash = calloc(1, sizeof(*new_hash));
8110 if (!new_hash) {
8111 err = IE_NOMEM;
8112 goto leave;
8114 new_hash->algorithm = algorithm;
8115 err = compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
8116 new_hash);
8117 if (err) {
8118 isds_log_message(context, _("Could not compute message hash"));
8119 goto leave;
8122 /* Save computed hash */
8123 if (!message->envelope) {
8124 message->envelope = calloc(1, sizeof(*message->envelope));
8125 if (!message->envelope) {
8126 err = IE_NOMEM;
8127 goto leave;
8130 isds_hash_free(&message->envelope->hash);
8131 message->envelope->hash = new_hash;
8133 leave:
8134 if (err) {
8135 isds_hash_free(&new_hash);
8138 free(phys_path);
8139 if (xml_stream != message->raw) free(xml_stream);
8140 return err;
8144 /* Compare two hashes.
8145 * @h1 is first hash
8146 * @h2 is another hash
8147 * @return
8148 * IE_SUCCESS if hashes equal
8149 * IE_NOTUNIQ if hashes are comparable, but they don't equal
8150 * IE_ENUM if not comparable, but both structures defined
8151 * IE_INVAL if some of the structures are undefined (NULL)
8152 * IE_ERROR if internal error occurs */
8153 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
8154 if (h1 == NULL || h2 == NULL) return IE_INVAL;
8155 if (h1->algorithm != h2->algorithm) return IE_ENUM;
8156 if (h1->length != h2->length) return IE_ERROR;
8157 if (h1->length > 0 && !h1->value) return IE_ERROR;
8158 if (h2->length > 0 && !h2->value) return IE_ERROR;
8160 for (int i = 0; i < h1->length; i++) {
8161 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
8162 return IE_NOTEQUAL;
8164 return IE_SUCCESS;
8168 /* Check message has gone through ISDS by comparing message hash stored in
8169 * ISDS and locally computed hash. You must provide message with valid raw
8170 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
8171 * This is convenient wrapper for isds_download_message_hash(),
8172 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
8173 * @context is session context
8174 * @message is message with valid raw and envelope member; envelope->hash
8175 * member will be changed during funcion run. Use envelope on heap only.
8176 * @return
8177 * IE_SUCCESS if message originates in ISDS
8178 * IE_NOTEQUAL if message is unknown to ISDS
8179 * other code for other errors */
8180 isds_error isds_verify_message_hash(struct isds_ctx *context,
8181 struct isds_message *message) {
8182 isds_error err = IE_SUCCESS;
8183 struct isds_hash *downloaded_hash = NULL;
8185 if (!context) return IE_INVALID_CONTEXT;
8186 if (!message) return IE_INVAL;
8188 if (!message->envelope) {
8189 isds_log_message(context,
8190 _("Given message structure is missing envelope"));
8191 return IE_INVAL;
8193 if (!message->raw) {
8194 isds_log_message(context,
8195 _("Given message structure is missing raw representation"));
8196 return IE_INVAL;
8199 err = isds_download_message_hash(context, message->envelope->dmID,
8200 &downloaded_hash);
8201 if (err) goto leave;
8203 err = isds_compute_message_hash(context, message,
8204 downloaded_hash->algorithm);
8205 if (err) goto leave;
8207 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
8209 leave:
8210 isds_hash_free(&downloaded_hash);
8211 return err;
8215 /* Search for document by document ID in list of documents. IDs are compared
8216 * as UTF-8 string.
8217 * @documents is list of isds_documents
8218 * @id is document identifier
8219 * @return first matching document or NULL. */
8220 const struct isds_document *isds_find_document_by_id(
8221 const struct isds_list *documents, const char *id) {
8222 const struct isds_list *item;
8223 const struct isds_document *document;
8225 for (item = documents; item; item = item->next) {
8226 document = (struct isds_document *) item->data;
8227 if (!document) continue;
8229 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
8230 return document;
8233 return NULL;
8237 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
8238 struct isds_message **message);
8239 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
8240 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
8241 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
8242 struct isds_address **address);
8244 int isds_message_free(struct isds_message **message);
8245 int isds_address_free(struct isds_address **address);
8249 /* Makes known all relevant namespaces to given XPath context
8250 * @xpat_ctx is XPath context
8251 * @message_ns selects propper message name space. Unsisnged and signed
8252 * messages differs.
8253 * prefix and to URI ISDS_NS */
8254 _hidden isds_error register_namespaces(xmlXPathContextPtr xpath_ctx,
8255 const message_ns_type message_ns) {
8256 const xmlChar *message_namespace = NULL;
8258 if (!xpath_ctx) return IE_ERROR;
8260 switch(message_ns) {
8261 case MESSAGE_NS_UNSIGNED:
8262 message_namespace = BAD_CAST ISDS_NS; break;
8263 case MESSAGE_NS_SIGNED_INCOMING:
8264 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
8265 case MESSAGE_NS_SIGNED_OUTGOING:
8266 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
8267 case MESSAGE_NS_SIGNED_DELIVERY:
8268 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
8269 default:
8270 return IE_ENUM;
8273 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
8274 return IE_ERROR;
8275 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
8276 return IE_ERROR;
8277 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
8278 return IE_ERROR;
8279 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
8280 return IE_ERROR;
8281 return IE_SUCCESS;