Add @approval argument to almost all DB_MANIPULATION functions
[libisds.git] / src / isds.c
blob4a196f0bb31391ef9ed48e59bfec360323319116
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-256"))
1487 *algorithm = HASH_ALGORITHM_SHA_256;
1488 else if (!xmlStrcmp(string, BAD_CAST "SHA-512"))
1489 *algorithm = HASH_ALGORITHM_SHA_512;
1490 else
1491 return IE_ENUM;
1492 return IE_SUCCESS;
1496 /* Convert UTF-8 @string represantion of ISO 8601 date to @time.
1497 * XXX: Not all ISO formats are supported */
1498 static isds_error datestring2tm(const xmlChar *string, struct tm *time) {
1499 char *offset;
1500 if (!string || !time) return IE_INVAL;
1502 /* xsd:date is ISO 8601 string, thus ASCII */
1503 offset = strptime((char*)string, "%Y-%m-%d", time);
1504 if (offset && *offset == '\0')
1505 return IE_SUCCESS;
1507 offset = strptime((char*)string, "%Y%m%d", time);
1508 if (offset && *offset == '\0')
1509 return IE_SUCCESS;
1511 offset = strptime((char*)string, "%Y-%j", time);
1512 if (offset && *offset == '\0')
1513 return IE_SUCCESS;
1515 return IE_NOTSUP;
1519 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
1520 static isds_error tm2datestring(const struct tm *time, xmlChar **string) {
1521 if (!time || !string) return IE_INVAL;
1523 if (-1 == isds_asprintf((char **) string, "%d-%02d-%02d",
1524 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
1525 return IE_ERROR;
1527 return IE_SUCCESS;
1531 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
1532 * respects the @time microseconds too. */
1533 static isds_error timeval2timestring(const struct timeval *time,
1534 xmlChar **string) {
1535 struct tm broken;
1537 if (!time || !string) return IE_INVAL;
1539 if (!gmtime_r(&time->tv_sec, &broken)) return IE_DATE;
1540 if (time->tv_usec < 0 || time->tv_usec > 999999) return IE_DATE;
1542 /* TODO: small negative year should be formated as "-0012". This is not
1543 * true for glibc "%04d". We should implement it.
1544 * TODO: What's type of time->tv_usec exactly? Unsigned? Absolute?
1545 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
1546 if (-1 == isds_asprintf((char **) string,
1547 "%04d-%02d-%02dT%02d:%02d:%02d.%06ld",
1548 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
1549 broken.tm_hour, broken.tm_min, broken.tm_sec,
1550 time->tv_usec))
1551 return IE_ERROR;
1553 return IE_SUCCESS;
1557 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
1558 * It respects microseconds too.
1559 * In case of error, @time will be freed. */
1560 static isds_error timestring2timeval(const xmlChar *string,
1561 struct timeval **time) {
1562 struct tm broken;
1563 char *offset, *delim, *endptr;
1564 char subseconds[7];
1565 int offset_hours, offset_minutes;
1566 int i;
1568 if (!time) return IE_INVAL;
1570 memset(&broken, 0, sizeof(broken));
1572 if (!*time) {
1573 *time = calloc(1, sizeof(**time));
1574 if (!*time) return IE_NOMEM;
1575 } else {
1576 memset(*time, 0, sizeof(**time));
1580 /* xsd:date is ISO 8601 string, thus ASCII */
1581 /*TODO: negative year */
1583 /* Parse date and time without subseconds and offset */
1584 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
1585 if (!offset) {
1586 free(*time); *time = NULL;
1587 return IE_DATE;
1590 /* Get subseconds */
1591 if (*offset == '.' ) {
1592 offset++;
1594 /* Copy first 6 digits, padd it with zeros.
1595 * XXX: It truncates longer number, no round.
1596 * Current server implementation uses only milisecond resolution. */
1597 /* TODO: isdigit() is locale sensitive */
1598 for (i = 0;
1599 i < sizeof(subseconds)/sizeof(char) - 1 && isdigit(*offset);
1600 i++, offset++) {
1601 subseconds[i] = *offset;
1603 for (; i < sizeof(subseconds)/sizeof(char) - 1; i++) {
1604 subseconds[i] = '0';
1606 subseconds[6] = '\0';
1608 /* Convert it into integer */
1609 (*time)->tv_usec = strtol(subseconds, &endptr, 10);
1610 if (*endptr != '\0' || (*time)->tv_usec == LONG_MIN ||
1611 (*time)->tv_usec == LONG_MAX) {
1612 free(*time); *time = NULL;
1613 return IE_DATE;
1616 /* move to the zone offset delimiter */
1617 delim = strchr(offset, '-');
1618 if (!delim)
1619 delim = strchr(offset, '+');
1620 offset = delim;
1623 /* Get zone offset */
1624 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
1625 * "" equals to "Z" and it means UTC zone. */
1626 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
1627 * colon separator */
1628 if (*offset == '-' || *offset == '+') {
1629 offset++;
1630 if (2 != sscanf(offset, "%2d:%2d", &offset_hours, &offset_minutes)) {
1631 free(*time); *time = NULL;
1632 return IE_DATE;
1634 broken.tm_hour -= offset_hours;
1635 broken.tm_min -= offset_minutes * ((offset_hours<0) ? -1 : 1);
1638 /* Convert to time_t */
1639 switch_tz_to_utc();
1640 (*time)->tv_sec = mktime(&broken);
1641 switch_tz_to_native();
1642 if ((*time)->tv_sec == (time_t) -1) {
1643 free(*time); *time = NULL;
1644 return IE_DATE;
1647 return IE_SUCCESS;
1651 /* Convert unsigned int into isds_message_status.
1652 * @context is session context
1653 * @number is pointer to number value. NULL will be treated as invalid value.
1654 * @status is automatically reallocated status
1655 * @return IE_SUCCESS, or error code and free status */
1656 static isds_error uint2isds_message_status(struct isds_ctx *context,
1657 const unsigned long int *number, isds_message_status **status) {
1658 if (!context) return IE_INVALID_CONTEXT;
1659 if (!status) return IE_INVAL;
1661 free(*status); *status = NULL;
1662 if (!number) return IE_INVAL;
1664 if (*number < 1 || *number > 10) {
1665 isds_printf_message(context, _("Invalid messsage status value: %lu"),
1666 *number);
1667 return IE_ENUM;
1670 *status = malloc(sizeof(**status));
1671 if (!*status) return IE_NOMEM;
1673 **status = 1 << *number;
1674 return IE_SUCCESS;
1678 /* Convert event description string into isds_event memebers type and
1679 * description
1680 * @string is raw event decsription starting with event prefix
1681 * @event is structure where to store type and stripped description to
1682 * @return standard error code, unkown prefix is not classified as an error. */
1683 static isds_error eventstring2event(const xmlChar *string,
1684 struct isds_event* event) {
1685 const xmlChar *known_prefixes[] = {
1686 BAD_CAST "EV1:",
1687 BAD_CAST "EV2:",
1688 BAD_CAST "EV3:",
1689 BAD_CAST "EV4:"
1691 const isds_event_type types[] = {
1692 EVENT_ACCEPTED_BY_RECIPIENT,
1693 EVENT_ACCEPTED_BY_FICTION,
1694 EVENT_UNDELIVERABLE,
1695 EVENT_COMMERCIAL_ACCEPTED
1697 unsigned int index;
1698 size_t length;
1700 if (!string || !event) return IE_INVAL;
1702 if (!event->type) {
1703 event->type = malloc(sizeof(*event->type));
1704 if (!(event->type)) return IE_NOMEM;
1706 zfree(event->description);
1708 for (index = 0; index < sizeof(known_prefixes)/sizeof(known_prefixes[0]);
1709 index++) {
1710 length = xmlUTF8Strlen(known_prefixes[index]);
1712 if (!xmlStrncmp(string, known_prefixes[index], length)) {
1713 /* Prefix is known */
1714 *event->type = types[index];
1716 /* Strip prefix from description and spaces */
1717 /* TODO: Recognize all wite spaces from UCS blank class and
1718 * operate on UTF-8 chars. */
1719 for (; string[length] != '\0' && string[length] == ' '; length++);
1720 event->description = strdup((char *) (string + length));
1721 if (!(event->description)) return IE_NOMEM;
1723 return IE_SUCCESS;
1727 /* Unknown event prefix.
1728 * XSD allows any string */
1729 char *string_locale = utf82locale((char *) string);
1730 isds_log(ILF_ISDS, ILL_WARNING,
1731 _("Uknown delivery info event prefix: %s\n"), string_locale);
1732 free(string_locale);
1734 *event->type = EVENT_UKNOWN;
1735 event->description = strdup((char *) string);
1736 if (!(event->description)) return IE_NOMEM;
1738 return IE_SUCCESS;
1742 /* Following EXTRACT_* macros expects @result, @xpath_ctx, @err, @context
1743 * and leave lable */
1744 #define EXTRACT_STRING(element, string) { \
1745 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
1746 if (!result) { \
1747 err = IE_ERROR; \
1748 goto leave; \
1750 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
1751 if (result->nodesetval->nodeNr > 1) { \
1752 isds_printf_message(context, _("Multiple %s element"), element); \
1753 err = IE_ERROR; \
1754 goto leave; \
1756 (string) = (char *) \
1757 xmlXPathCastNodeSetToString(result->nodesetval); \
1758 if (!(string)) { \
1759 err = IE_ERROR; \
1760 goto leave; \
1765 #define EXTRACT_BOOLEAN(element, booleanPtr) \
1767 char *string = NULL; \
1768 EXTRACT_STRING(element, string); \
1770 if (string) { \
1771 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
1772 if (!(booleanPtr)) { \
1773 free(string); \
1774 err = IE_NOMEM; \
1775 goto leave; \
1778 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
1779 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
1780 *(booleanPtr) = 1; \
1781 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
1782 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
1783 *(booleanPtr) = 0; \
1784 else { \
1785 char *string_locale = utf82locale((char*)string); \
1786 isds_printf_message(context, \
1787 _("%s value is not valid boolean: "), \
1788 element, string_locale); \
1789 free(string_locale); \
1790 free(string); \
1791 err = IE_ERROR; \
1792 goto leave; \
1795 free(string); \
1799 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
1801 char *string = NULL; \
1802 EXTRACT_STRING(element, string); \
1803 if (string) { \
1804 long int number; \
1805 char *endptr; \
1807 number = strtol((char*)string, &endptr, 10); \
1809 if (*endptr != '\0') { \
1810 char *string_locale = utf82locale((char *)string); \
1811 isds_printf_message(context, \
1812 _("%s is not valid integer: %s"), \
1813 element, string_locale); \
1814 free(string_locale); \
1815 free(string); \
1816 err = IE_ISDS; \
1817 goto leave; \
1820 if (number == LONG_MIN || number == LONG_MAX) { \
1821 char *string_locale = utf82locale((char *)string); \
1822 isds_printf_message(context, \
1823 _("%s value out of range of long int: %s"), \
1824 element, string_locale); \
1825 free(string_locale); \
1826 free(string); \
1827 err = IE_ERROR; \
1828 goto leave; \
1831 free(string); string = NULL; \
1833 if (!(preallocated)) { \
1834 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
1835 if (!(longintPtr)) { \
1836 err = IE_NOMEM; \
1837 goto leave; \
1840 *(longintPtr) = number; \
1844 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
1846 char *string = NULL; \
1847 EXTRACT_STRING(element, string); \
1848 if (string) { \
1849 long int number; \
1850 char *endptr; \
1852 number = strtol((char*)string, &endptr, 10); \
1854 if (*endptr != '\0') { \
1855 char *string_locale = utf82locale((char *)string); \
1856 isds_printf_message(context, \
1857 _("%s is not valid integer: %s"), \
1858 element, string_locale); \
1859 free(string_locale); \
1860 free(string); \
1861 err = IE_ISDS; \
1862 goto leave; \
1865 if (number == LONG_MIN || number == LONG_MAX) { \
1866 char *string_locale = utf82locale((char *)string); \
1867 isds_printf_message(context, \
1868 _("%s value out of range of long int: %s"), \
1869 element, string_locale); \
1870 free(string_locale); \
1871 free(string); \
1872 err = IE_ERROR; \
1873 goto leave; \
1876 free(string); string = NULL; \
1877 if (number < 0) { \
1878 isds_printf_message(context, \
1879 _("%s value is negative: %ld"), element, number); \
1880 err = IE_ERROR; \
1881 goto leave; \
1884 if (!(preallocated)) { \
1885 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
1886 if (!(ulongintPtr)) { \
1887 err = IE_NOMEM; \
1888 goto leave; \
1891 *(ulongintPtr) = number; \
1895 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
1896 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
1897 NULL); \
1898 if ((required) && (!string)) { \
1899 char *attribute_locale = utf82locale(attribute); \
1900 char *element_locale = utf82locale((char *)xpath_ctx->node->name); \
1901 isds_printf_message(context, \
1902 _("Could not extract required %s attribute value from " \
1903 "%s element"), attribute_locale, element_locale); \
1904 free(element_locale); \
1905 free(attribute_locale); \
1906 err = IE_ERROR; \
1907 goto leave; \
1912 #define INSERT_STRING(parent, element, string) \
1914 node = xmlNewTextChild(parent, NULL, BAD_CAST (element), \
1915 (xmlChar *) (string)); \
1916 if (!node) { \
1917 isds_printf_message(context, \
1918 _("Could not add %s child to %s element"), \
1919 element, (parent)->name); \
1920 err = IE_ERROR; \
1921 goto leave; \
1925 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
1927 if (boolean) { INSERT_STRING(parent, element, "true"); } \
1928 else { INSERT_STRING(parent, element, "false"); } \
1931 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
1933 if (booleanPtr) { \
1934 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
1935 } else { \
1936 INSERT_STRING(parent, element, NULL); \
1940 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
1941 if ((longintPtr)) { \
1942 /* FIXME: locale sensitive */ \
1943 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
1944 err = IE_NOMEM; \
1945 goto leave; \
1947 INSERT_STRING(parent, element, buffer) \
1948 free(buffer); (buffer) = NULL; \
1949 } else { INSERT_STRING(parent, element, NULL) } \
1952 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
1953 if ((ulongintPtr)) { \
1954 /* FIXME: locale sensitive */ \
1955 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
1956 err = IE_NOMEM; \
1957 goto leave; \
1959 INSERT_STRING(parent, element, buffer) \
1960 free(buffer); (buffer) = NULL; \
1961 } else { INSERT_STRING(parent, element, NULL) } \
1964 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
1966 /* FIXME: locale sensitive */ \
1967 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
1968 err = IE_NOMEM; \
1969 goto leave; \
1971 INSERT_STRING(parent, element, buffer) \
1972 free(buffer); (buffer) = NULL; \
1975 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
1977 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
1978 (xmlChar *) (string)); \
1979 if (!attribute_node) { \
1980 isds_printf_message(context, _("Could not add %s " \
1981 "attribute to %s element"), \
1982 (attribute), (parent)->name); \
1983 err = IE_ERROR; \
1984 goto leave; \
1988 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
1989 if (string) { \
1990 int length = xmlUTF8Strlen((xmlChar *) (string)); \
1991 if (length > (maximum)) { \
1992 isds_printf_message(context, \
1993 _("%s has more than %d characters"), (name), (maximum)); \
1994 err = IE_2BIG; \
1995 goto leave; \
1997 if (length < (minimum)) { \
1998 isds_printf_message(context, \
1999 _("%s has less than %d characters"), (name), (minimum)); \
2000 err = IE_2SMALL; \
2001 goto leave; \
2006 #define INSERT_ELEMENT(child, parent, element) \
2008 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2009 if (!(child)) { \
2010 isds_printf_message(context, \
2011 _("Could not add %s child to %s element"), \
2012 (element), (parent)->name); \
2013 err = IE_ERROR; \
2014 goto leave; \
2019 /* Find child element by name in given XPath context and switch context onto
2020 * it. The child must be uniq and must exist. Otherwise failes.
2021 * @context is ISDS context
2022 * @child is child element name
2023 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2024 * into it child. In error case, the @xpath_ctx keeps original value. */
2025 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
2026 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
2027 isds_error err = IE_SUCCESS;
2028 xmlXPathObjectPtr result = NULL;
2030 if (!context) return IE_INVALID_CONTEXT;
2031 if (!child || !xpath_ctx) return IE_INVAL;
2033 /* Find child */
2034 result = xmlXPathEvalExpression(child, xpath_ctx);
2035 if (!result) {
2036 err = IE_XML;
2037 goto leave;
2040 /* No match */
2041 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2042 char *parent_locale = utf82locale((char*) xpath_ctx->node->name);
2043 char *child_locale = utf82locale((char*) child);
2044 isds_printf_message(context,
2045 _("%s element does not contain %s child"),
2046 parent_locale, child_locale);
2047 free(child_locale);
2048 free(parent_locale);
2049 err = IE_NOEXIST;
2050 goto leave;
2053 /* More matches */
2054 if (result->nodesetval->nodeNr > 1) {
2055 char *parent_locale = utf82locale((char*) xpath_ctx->node->name);
2056 char *child_locale = utf82locale((char*) child);
2057 isds_printf_message(context,
2058 _("%s element contains multiple %s childs"),
2059 parent_locale, child_locale);
2060 free(child_locale);
2061 free(parent_locale);
2062 err = IE_NOTUNIQ;
2063 goto leave;
2066 /* Switch context */
2067 xpath_ctx->node = result->nodesetval->nodeTab[0];
2069 leave:
2070 xmlXPathFreeObject(result);
2071 return err;
2076 /* Find and convert XSD:gPersonName group in current node into structure
2077 * @context is ISDS context
2078 * @personName is automically reallocated person name structure. If no member
2079 * value is found, will be freed.
2080 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2081 * elements
2082 * In case of error @personName will be freed. */
2083 static isds_error extract_gPersonName(struct isds_ctx *context,
2084 struct isds_PersonName **personName, xmlXPathContextPtr xpath_ctx) {
2085 isds_error err = IE_SUCCESS;
2086 xmlXPathObjectPtr result = NULL;
2088 if (!context) return IE_INVALID_CONTEXT;
2089 if (!personName) return IE_INVAL;
2090 isds_PersonName_free(personName);
2091 if (!xpath_ctx) return IE_INVAL;
2094 *personName = calloc(1, sizeof(**personName));
2095 if (!*personName) {
2096 err = IE_NOMEM;
2097 goto leave;
2100 EXTRACT_STRING("isds:pnFirstName", (*personName)->pnFirstName);
2101 EXTRACT_STRING("isds:pnMiddleName", (*personName)->pnMiddleName);
2102 EXTRACT_STRING("isds:pnLastName", (*personName)->pnLastName);
2103 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName)->pnLastNameAtBirth);
2105 if (!(*personName)->pnFirstName && !(*personName)->pnMiddleName &&
2106 !(*personName)->pnLastName && !(*personName)->pnLastNameAtBirth)
2107 isds_PersonName_free(personName);
2109 leave:
2110 if (err) isds_PersonName_free(personName);
2111 xmlXPathFreeObject(result);
2112 return err;
2116 /* Find and convert XSD:gAddress group in current node into structure
2117 * @context is ISDS context
2118 * @address is automically reallocated address structure. If no member
2119 * value is found, will be freed.
2120 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2121 * elements
2122 * In case of error @address will be freed. */
2123 static isds_error extract_gAddress(struct isds_ctx *context,
2124 struct isds_Address **address, xmlXPathContextPtr xpath_ctx) {
2125 isds_error err = IE_SUCCESS;
2126 xmlXPathObjectPtr result = NULL;
2128 if (!context) return IE_INVALID_CONTEXT;
2129 if (!address) return IE_INVAL;
2130 isds_Address_free(address);
2131 if (!xpath_ctx) return IE_INVAL;
2134 *address = calloc(1, sizeof(**address));
2135 if (!*address) {
2136 err = IE_NOMEM;
2137 goto leave;
2140 EXTRACT_STRING("isds:adCity", (*address)->adCity);
2141 EXTRACT_STRING("isds:adStreet", (*address)->adStreet);
2142 EXTRACT_STRING("isds:adNumberInStreet", (*address)->adNumberInStreet);
2143 EXTRACT_STRING("isds:adNumberInMunicipality",
2144 (*address)->adNumberInMunicipality);
2145 EXTRACT_STRING("isds:adZipCode", (*address)->adZipCode);
2146 EXTRACT_STRING("isds:adState", (*address)->adState);
2148 if (!(*address)->adCity && !(*address)->adStreet &&
2149 !(*address)->adNumberInStreet &&
2150 !(*address)->adNumberInMunicipality &&
2151 !(*address)->adZipCode && !(*address)->adState)
2152 isds_Address_free(address);
2154 leave:
2155 if (err) isds_Address_free(address);
2156 xmlXPathFreeObject(result);
2157 return err;
2161 /* Find and convert isds:biDate element in current node into structure
2162 * @context is ISDS context
2163 * @biDate is automically reallocated birth date structure. If no member
2164 * value is found, will be freed.
2165 * @xpath_ctx is XPath context with current node as parent for isds:biDate
2166 * element
2167 * In case of error @biDate will be freed. */
2168 static isds_error extract_BiDate(struct isds_ctx *context,
2169 struct tm **biDate, xmlXPathContextPtr xpath_ctx) {
2170 isds_error err = IE_SUCCESS;
2171 xmlXPathObjectPtr result = NULL;
2172 char *string = NULL;
2174 if (!context) return IE_INVALID_CONTEXT;
2175 if (!biDate) return IE_INVAL;
2176 zfree(*biDate);
2177 if (!xpath_ctx) return IE_INVAL;
2179 EXTRACT_STRING("isds:biDate", string);
2180 if (string) {
2181 *biDate = calloc(1, sizeof(**biDate));
2182 if (!*biDate) {
2183 err = IE_NOMEM;
2184 goto leave;
2186 err = datestring2tm((xmlChar *)string, *biDate);
2187 if (err) {
2188 if (err == IE_NOTSUP) {
2189 err = IE_ISDS;
2190 char *string_locale = utf82locale(string);
2191 isds_printf_message(context,
2192 _("Invalid isds:biDate value: %s"), string_locale);
2193 free(string_locale);
2195 goto leave;
2199 leave:
2200 if (err) zfree(*biDate);
2201 free(string);
2202 xmlXPathFreeObject(result);
2203 return err;
2207 /* Convert isds:dBOwnerInfo XML tree into structure
2208 * @context is ISDS context
2209 * @db_owner_info is automically reallocated box owner info structure
2210 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
2211 * In case of error @db_owner_info will be freed. */
2212 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
2213 struct isds_DbOwnerInfo **db_owner_info,
2214 xmlXPathContextPtr xpath_ctx) {
2215 isds_error err = IE_SUCCESS;
2216 xmlXPathObjectPtr result = NULL;
2217 char *string = NULL;
2219 if (!context) return IE_INVALID_CONTEXT;
2220 if (!db_owner_info) return IE_INVAL;
2221 isds_DbOwnerInfo_free(db_owner_info);
2222 if (!xpath_ctx) return IE_INVAL;
2225 *db_owner_info = calloc(1, sizeof(**db_owner_info));
2226 if (!*db_owner_info) {
2227 err = IE_NOMEM;
2228 goto leave;
2231 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
2233 EXTRACT_STRING("isds:dbType", string);
2234 if (string) {
2235 (*db_owner_info)->dbType =
2236 calloc(1, sizeof(*((*db_owner_info)->dbType)));
2237 if (!(*db_owner_info)->dbType) {
2238 err = IE_NOMEM;
2239 goto leave;
2241 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
2242 if (err) {
2243 zfree((*db_owner_info)->dbType);
2244 if (err == IE_ENUM) {
2245 err = IE_ISDS;
2246 char *string_locale = utf82locale(string);
2247 isds_printf_message(context, _("Unknown isds:dbType: %s"),
2248 string_locale);
2249 free(string_locale);
2251 goto leave;
2253 zfree(string);
2256 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
2258 err = extract_gPersonName(context, &(*db_owner_info)->personName,
2259 xpath_ctx);
2260 if (err) goto leave;
2262 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
2264 (*db_owner_info)->birthInfo =
2265 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
2266 if (!(*db_owner_info)->birthInfo) {
2267 err = IE_NOMEM;
2268 goto leave;
2270 err = extract_BiDate(context, &(*db_owner_info)->birthInfo->biDate,
2271 xpath_ctx);
2272 if (err) goto leave;
2273 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
2274 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
2275 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
2276 if (!(*db_owner_info)->birthInfo->biDate &&
2277 !(*db_owner_info)->birthInfo->biCity &&
2278 !(*db_owner_info)->birthInfo->biCounty &&
2279 !(*db_owner_info)->birthInfo->biState)
2280 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
2282 err = extract_gAddress(context, &(*db_owner_info)->address, xpath_ctx);
2283 if (err) goto leave;
2285 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
2286 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
2287 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
2288 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
2289 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
2291 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
2293 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
2294 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
2295 (*db_owner_info)->dbOpenAddressing);
2297 leave:
2298 if (err) isds_DbOwnerInfo_free(db_owner_info);
2299 free(string);
2300 xmlXPathFreeObject(result);
2301 return err;
2305 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
2306 * @context is sesstion context
2307 * @owner is libsids structure with box description
2308 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
2309 static isds_error insert_DbOwnerInfo(struct isds_ctx *context,
2310 const struct isds_DbOwnerInfo *owner, xmlNodePtr db_owner_info) {
2312 isds_error err = IE_SUCCESS;
2313 xmlNodePtr node;
2314 xmlChar *string = NULL;
2316 if (!context) return IE_INVALID_CONTEXT;
2317 if (!owner || !db_owner_info) return IE_INVAL;
2320 /* Build XSD:tDbOwnerInfo */
2321 CHECK_FOR_STRING_LENGTH(owner->dbID, 0, 7, "dbID")
2322 INSERT_STRING(db_owner_info, "dbID", owner->dbID);
2324 /* dbType */
2325 if (owner->dbType) {
2326 const xmlChar *type_string = isds_DbType2string(*(owner->dbType));
2327 if (!type_string) {
2328 isds_printf_message(context, _("Invalid dbType value: %d"),
2329 *(owner->dbType));
2330 err = IE_ENUM;
2331 goto leave;
2333 INSERT_STRING(db_owner_info, "dbType", type_string);
2335 INSERT_STRING(db_owner_info, "ic", owner->ic);
2336 if (owner->personName) {
2337 INSERT_STRING(db_owner_info, "pnFirstName",
2338 owner->personName->pnFirstName);
2339 INSERT_STRING(db_owner_info, "pnMiddleName",
2340 owner->personName->pnMiddleName);
2341 INSERT_STRING(db_owner_info, "pnLastName",
2342 owner->personName->pnLastName);
2343 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
2344 owner->personName->pnLastNameAtBirth);
2346 INSERT_STRING(db_owner_info, "firmName", owner->firmName);
2347 if (owner->birthInfo) {
2348 if (owner->birthInfo->biDate) {
2349 if (!tm2datestring(owner->birthInfo->biDate, &string))
2350 INSERT_STRING(db_owner_info, "biDate", string);
2351 free(string); string = NULL;
2353 INSERT_STRING(db_owner_info, "biCity", owner->birthInfo->biCity);
2354 INSERT_STRING(db_owner_info, "biCounty", owner->birthInfo->biCounty);
2355 INSERT_STRING(db_owner_info, "biState", owner->birthInfo->biState);
2357 if (owner->address) {
2358 INSERT_STRING(db_owner_info, "adCity", owner->address->adCity);
2359 INSERT_STRING(db_owner_info, "adStreet", owner->address->adStreet);
2360 INSERT_STRING(db_owner_info, "adNumberInStreet",
2361 owner->address->adNumberInStreet);
2362 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
2363 owner->address->adNumberInMunicipality);
2364 INSERT_STRING(db_owner_info, "adZipCode", owner->address->adZipCode);
2365 INSERT_STRING(db_owner_info, "adState", owner->address->adState);
2367 INSERT_STRING(db_owner_info, "nationality", owner->nationality);
2368 INSERT_STRING(db_owner_info, "email", owner->email);
2369 INSERT_STRING(db_owner_info, "telNumber", owner->telNumber);
2371 CHECK_FOR_STRING_LENGTH(owner->identifier, 0, 20, "identifier")
2372 INSERT_STRING(db_owner_info, "identifier", owner->identifier);
2374 CHECK_FOR_STRING_LENGTH(owner->registryCode, 0, 5, "registryCode")
2375 INSERT_STRING(db_owner_info, "registryCode", owner->registryCode);
2377 INSERT_LONGINT(db_owner_info, "dbState", owner->dbState, string);
2379 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", owner->dbEffectiveOVM);
2380 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
2381 owner->dbOpenAddressing);
2383 leave:
2384 free(string);
2385 return err;
2389 /* Convert XSD:tDbUserInfo XML tree into structure
2390 * @context is ISDS context
2391 * @db_user_info is automically reallocated user info structure
2392 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
2393 * In case of error @db_user_info will be freed. */
2394 static isds_error extract_DbUserInfo(struct isds_ctx *context,
2395 struct isds_DbUserInfo **db_user_info, xmlXPathContextPtr xpath_ctx) {
2396 isds_error err = IE_SUCCESS;
2397 xmlXPathObjectPtr result = NULL;
2398 char *string = NULL;
2400 if (!context) return IE_INVALID_CONTEXT;
2401 if (!db_user_info) return IE_INVAL;
2402 isds_DbUserInfo_free(db_user_info);
2403 if (!xpath_ctx) return IE_INVAL;
2406 *db_user_info = calloc(1, sizeof(**db_user_info));
2407 if (!*db_user_info) {
2408 err = IE_NOMEM;
2409 goto leave;
2412 EXTRACT_STRING("isds:userID", (*db_user_info)->userID);
2414 EXTRACT_STRING("isds:userType", string);
2415 if (string) {
2416 (*db_user_info)->userType =
2417 calloc(1, sizeof(*((*db_user_info)->userType)));
2418 if (!(*db_user_info)->userType) {
2419 err = IE_NOMEM;
2420 goto leave;
2422 err = string2isds_UserType((xmlChar *)string,
2423 (*db_user_info)->userType);
2424 if (err) {
2425 zfree((*db_user_info)->userType);
2426 if (err == IE_ENUM) {
2427 err = IE_ISDS;
2428 char *string_locale = utf82locale(string);
2429 isds_printf_message(context, _("Unknown isds:userType: %s"),
2430 string_locale);
2431 free(string_locale);
2433 goto leave;
2435 zfree(string);
2438 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info)->userPrivils, 0);
2440 (*db_user_info)->personName =
2441 calloc(1, sizeof(*((*db_user_info)->personName)));
2442 if (!(*db_user_info)->personName) {
2443 err = IE_NOMEM;
2444 goto leave;
2447 err = extract_gPersonName(context, &(*db_user_info)->personName,
2448 xpath_ctx);
2449 if (err) goto leave;
2451 err = extract_gAddress(context, &(*db_user_info)->address, xpath_ctx);
2452 if (err) goto leave;
2454 err = extract_BiDate(context, &(*db_user_info)->biDate, xpath_ctx);
2455 if (err) goto leave;
2457 EXTRACT_STRING("isds:ic", (*db_user_info)->ic);
2458 EXTRACT_STRING("isds:firmName", (*db_user_info)->firmName);
2460 EXTRACT_STRING("isds:caStreet", (*db_user_info)->caStreet);
2461 EXTRACT_STRING("isds:caCity", (*db_user_info)->caCity);
2462 EXTRACT_STRING("isds:caZipCode", (*db_user_info)->caZipCode);
2464 leave:
2465 if (err) isds_DbUserInfo_free(db_user_info);
2466 free(string);
2467 xmlXPathFreeObject(result);
2468 return err;
2472 /* Insert struct isds_DbUserInfo data (user description) into XML tree
2473 * @context is sesstion context
2474 * @user is libsids structure with user description
2475 * @db_user_info is XML element of XSD:tDbUserInfo */
2476 static isds_error insert_DbUserInfo(struct isds_ctx *context,
2477 const struct isds_DbUserInfo *user, xmlNodePtr db_user_info) {
2479 isds_error err = IE_SUCCESS;
2480 xmlNodePtr node;
2481 xmlChar *string = NULL;
2483 if (!context) return IE_INVALID_CONTEXT;
2484 if (!user || !db_user_info) return IE_INVAL;
2486 /* Build XSD:tDbUserInfo */
2487 if (user->personName) {
2488 INSERT_STRING(db_user_info, "pnFirstName",
2489 user->personName->pnFirstName);
2490 INSERT_STRING(db_user_info, "pnMiddleName",
2491 user->personName->pnMiddleName);
2492 INSERT_STRING(db_user_info, "pnLastName",
2493 user->personName->pnLastName);
2494 INSERT_STRING(db_user_info, "pnLastNameAtBirth",
2495 user->personName->pnLastNameAtBirth);
2497 if (user->address) {
2498 INSERT_STRING(db_user_info, "adCity", user->address->adCity);
2499 INSERT_STRING(db_user_info, "adStreet", user->address->adStreet);
2500 INSERT_STRING(db_user_info, "adNumberInStreet",
2501 user->address->adNumberInStreet);
2502 INSERT_STRING(db_user_info, "adNumberInMunicipality",
2503 user->address->adNumberInMunicipality);
2504 INSERT_STRING(db_user_info, "adZipCode", user->address->adZipCode);
2505 INSERT_STRING(db_user_info, "adState", user->address->adState);
2507 if (user->biDate) {
2508 if (!tm2datestring(user->biDate, &string))
2509 INSERT_STRING(db_user_info, "biDate", string);
2510 zfree(string);
2512 CHECK_FOR_STRING_LENGTH(user->userID, 6, 12, "userID");
2513 INSERT_STRING(db_user_info, "userID", user->userID);
2515 /* userType */
2516 if (user->userType) {
2517 const xmlChar *type_string = isds_UserType2string(*(user->userType));
2518 if (!type_string) {
2519 isds_printf_message(context, _("Invalid userType value: %d"),
2520 *(user->userType));
2521 err = IE_ENUM;
2522 goto leave;
2524 INSERT_STRING(db_user_info, "userType", type_string);
2527 INSERT_LONGINT(db_user_info, "userPrivils", user->userPrivils, string);
2528 CHECK_FOR_STRING_LENGTH(user->ic, 0, 8, "ic")
2529 INSERT_STRING(db_user_info, "ic", user->ic);
2530 CHECK_FOR_STRING_LENGTH(user->firmName, 0, 100, "firmName")
2531 INSERT_STRING(db_user_info, "firmName", user->firmName);
2532 INSERT_STRING(db_user_info, "caStreet", user->caStreet);
2533 INSERT_STRING(db_user_info, "caCity", user->caCity);
2534 INSERT_STRING(db_user_info, "caZipCode", user->caZipCode);
2536 leave:
2537 free(string);
2538 return err;
2542 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
2543 * isds_envelope structure. The envelope is automatically allocated but not
2544 * reallocated. The date are just appended into envelope structure.
2545 * @context is ISDS context
2546 * @envelope is automically allocated message envelope structure
2547 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
2548 * In case of error @envelope will be freed. */
2549 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
2550 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2551 isds_error err = IE_SUCCESS;
2552 xmlXPathObjectPtr result = NULL;
2554 if (!context) return IE_INVALID_CONTEXT;
2555 if (!envelope) return IE_INVAL;
2556 if (!xpath_ctx) return IE_INVAL;
2559 if (!*envelope) {
2560 /* Allocate envelope */
2561 *envelope = calloc(1, sizeof(**envelope));
2562 if (!*envelope) {
2563 err = IE_NOMEM;
2564 goto leave;
2566 } else {
2567 /* Else free former data */
2568 zfree((*envelope)->dmSenderOrgUnit);
2569 zfree((*envelope)->dmSenderOrgUnitNum);
2570 zfree((*envelope)->dbIDRecipient);
2571 zfree((*envelope)->dmRecipientOrgUnit);
2572 zfree((*envelope)->dmSenderOrgUnitNum);
2573 zfree((*envelope)->dmToHands);
2574 zfree((*envelope)->dmAnnotation);
2575 zfree((*envelope)->dmRecipientRefNumber);
2576 zfree((*envelope)->dmSenderRefNumber);
2577 zfree((*envelope)->dmRecipientIdent);
2578 zfree((*envelope)->dmSenderIdent);
2579 zfree((*envelope)->dmLegalTitleLaw);
2580 zfree((*envelope)->dmLegalTitleYear);
2581 zfree((*envelope)->dmLegalTitleSect);
2582 zfree((*envelope)->dmLegalTitlePar);
2583 zfree((*envelope)->dmLegalTitlePoint);
2584 zfree((*envelope)->dmPersonalDelivery);
2585 zfree((*envelope)->dmAllowSubstDelivery);
2588 /* Extract envelope elements added by sender or ISDS
2589 * (XSD: gMessageEnvelopeSub type) */
2590 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
2591 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
2592 (*envelope)->dmSenderOrgUnitNum, 0);
2593 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
2594 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
2595 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
2596 (*envelope)->dmSenderOrgUnitNum, 0);
2597 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
2598 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
2599 EXTRACT_STRING("isds:dmRecipientRefNumber",
2600 (*envelope)->dmRecipientRefNumber);
2601 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
2602 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
2603 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
2605 /* Extract envelope elements regarding law refference */
2606 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
2607 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
2608 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
2609 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
2610 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
2612 /* Extract envelope other elements */
2613 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
2614 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
2615 (*envelope)->dmAllowSubstDelivery);
2617 leave:
2618 if (err) isds_envelope_free(envelope);
2619 xmlXPathFreeObject(result);
2620 return err;
2625 /* Convert XSD gMessageEnvelope group of elements from XML tree into
2626 * isds_envelope structure. The envelope is automatically allocated but not
2627 * reallocated. The date are just appended into envelope structure.
2628 * @context is ISDS context
2629 * @envelope is automically allocated message envelope structure
2630 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
2631 * In case of error @envelope will be freed. */
2632 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
2633 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2634 isds_error err = IE_SUCCESS;
2635 xmlXPathObjectPtr result = NULL;
2637 if (!context) return IE_INVALID_CONTEXT;
2638 if (!envelope) return IE_INVAL;
2639 if (!xpath_ctx) return IE_INVAL;
2642 if (!*envelope) {
2643 /* Allocate envelope */
2644 *envelope = calloc(1, sizeof(**envelope));
2645 if (!*envelope) {
2646 err = IE_NOMEM;
2647 goto leave;
2649 } else {
2650 /* Else free former data */
2651 zfree((*envelope)->dmID);
2652 zfree((*envelope)->dbIDSender);
2653 zfree((*envelope)->dmSender);
2654 zfree((*envelope)->dmSenderAddress);
2655 zfree((*envelope)->dmSenderType);
2656 zfree((*envelope)->dmRecipient);
2657 zfree((*envelope)->dmRecipientAddress);
2658 zfree((*envelope)->dmAmbiguousRecipient);
2661 /* Extract envelope elements added by ISDS
2662 * (XSD: gMessageEnvelope type) */
2663 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
2664 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
2665 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
2666 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
2667 /* XML Schema does not guaratee enumratation. It's plain xs:int. */
2668 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
2669 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
2670 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
2671 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
2672 (*envelope)->dmAmbiguousRecipient);
2674 /* Extract envelope elements added by sender and ISDS
2675 * (XSD: gMessageEnvelope type) */
2676 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
2677 if (err) goto leave;
2679 leave:
2680 if (err) isds_envelope_free(envelope);
2681 xmlXPathFreeObject(result);
2682 return err;
2686 /* Convert other envelope elements from XML tree into isds_envelope structure:
2687 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
2688 * The envelope is automatically allocated but not reallocated.
2689 * The data are just appended into envelope structure.
2690 * @context is ISDS context
2691 * @envelope is automically allocated message envelope structure
2692 * @xpath_ctx is XPath context with current node as parent desired elements
2693 * In case of error @envelope will be freed. */
2694 static isds_error append_status_size_times(struct isds_ctx *context,
2695 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2696 isds_error err = IE_SUCCESS;
2697 xmlXPathObjectPtr result = NULL;
2698 char *string = NULL;
2699 unsigned long int *unumber = NULL;
2701 if (!context) return IE_INVALID_CONTEXT;
2702 if (!envelope) return IE_INVAL;
2703 if (!xpath_ctx) return IE_INVAL;
2706 if (!*envelope) {
2707 /* Allocate new */
2708 *envelope = calloc(1, sizeof(**envelope));
2709 if (!*envelope) {
2710 err = IE_NOMEM;
2711 goto leave;
2713 } else {
2714 /* Free old data */
2715 zfree((*envelope)->dmMessageStatus);
2716 zfree((*envelope)->dmAttachmentSize);
2717 zfree((*envelope)->dmDeliveryTime);
2718 zfree((*envelope)->dmAcceptanceTime);
2722 /* dmMessageStatus element is mandatory */
2723 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
2724 if (!unumber) {
2725 isds_log_message(context,
2726 _("Missing mandatory sisds:dmMessageStatus integer"));
2727 err = IE_ISDS;
2728 goto leave;
2730 err = uint2isds_message_status(context, unumber,
2731 &((*envelope)->dmMessageStatus));
2732 if (err) {
2733 if (err == IE_ENUM) err = IE_ISDS;
2734 goto leave;
2736 free(unumber); unumber = NULL;
2738 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
2741 EXTRACT_STRING("sisds:dmDeliveryTime", string);
2742 if (string) {
2743 err = timestring2timeval((xmlChar *) string,
2744 &((*envelope)->dmDeliveryTime));
2745 if (err) {
2746 char *string_locale = utf82locale(string);
2747 if (err == IE_DATE) err = IE_ISDS;
2748 isds_printf_message(context,
2749 _("Could not convert dmDeliveryTime as ISO time: %s"),
2750 string_locale);
2751 free(string_locale);
2752 goto leave;
2754 zfree(string);
2757 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
2758 if (string) {
2759 err = timestring2timeval((xmlChar *) string,
2760 &((*envelope)->dmAcceptanceTime));
2761 if (err) {
2762 char *string_locale = utf82locale(string);
2763 if (err == IE_DATE) err = IE_ISDS;
2764 isds_printf_message(context,
2765 _("Could not convert dmAcceptanceTime as ISO time: %s"),
2766 string_locale);
2767 free(string_locale);
2768 goto leave;
2770 zfree(string);
2773 leave:
2774 if (err) isds_envelope_free(envelope);
2775 free(unumber);
2776 free(string);
2777 xmlXPathFreeObject(result);
2778 return err;
2782 /* Convert message type attribute of current element into isds_envelope
2783 * structure.
2784 * TODO: This function can be incorporated into append_status_size_times() as
2785 * they are called always together.
2786 * The envelope is automatically allocated but not reallocated.
2787 * The data are just appended into envelope structure.
2788 * @context is ISDS context
2789 * @envelope is automically allocated message envelope structure
2790 * @xpath_ctx is XPath context with current node as parent of attribute
2791 * carrying message type
2792 * In case of error @envelope will be freed. */
2793 static isds_error append_message_type(struct isds_ctx *context,
2794 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2795 isds_error err = IE_SUCCESS;
2797 if (!context) return IE_INVALID_CONTEXT;
2798 if (!envelope) return IE_INVAL;
2799 if (!xpath_ctx) return IE_INVAL;
2802 if (!*envelope) {
2803 /* Allocate new */
2804 *envelope = calloc(1, sizeof(**envelope));
2805 if (!*envelope) {
2806 err = IE_NOMEM;
2807 goto leave;
2809 } else {
2810 /* Free old data */
2811 zfree((*envelope)->dmType);
2815 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope)->dmType, 0);
2817 if (!(*envelope)->dmType) {
2818 /* Use default value */
2819 (*envelope)->dmType = strdup("V");
2820 if (!(*envelope)->dmType) {
2821 err = IE_NOMEM;
2822 goto leave;
2824 } else if (1 != xmlUTF8Strlen((xmlChar *) (*envelope)->dmType)) {
2825 char *type_locale = utf82locale((*envelope)->dmType);
2826 isds_printf_message(context,
2827 _("Message type in dmType attribute is not 1 character long: "
2828 "%s"),
2829 type_locale);
2830 free(type_locale);
2831 err = IE_ISDS;
2832 goto leave;
2835 leave:
2836 if (err) isds_envelope_free(envelope);
2837 return err;
2842 /* Extract message document into reallocated document structure
2843 * @context is ISDS context
2844 * @document is automically reallocated message documents structure
2845 * @xpath_ctx is XPath context with current node as isds:dmFile
2846 * In case of error @document will be freed. */
2847 static isds_error extract_document(struct isds_ctx *context,
2848 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
2849 isds_error err = IE_SUCCESS;
2850 xmlXPathObjectPtr result = NULL;
2851 xmlNodePtr file_node = xpath_ctx->node;
2852 char *string = NULL;
2854 if (!context) return IE_INVALID_CONTEXT;
2855 if (!document) return IE_INVAL;
2856 isds_document_free(document);
2857 if (!xpath_ctx) return IE_INVAL;
2859 *document = calloc(1, sizeof(**document));
2860 if (!*document) {
2861 err = IE_NOMEM;
2862 goto leave;
2865 /* Extract document metadata */
2866 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
2868 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
2869 err = string2isds_FileMetaType((xmlChar*)string,
2870 &((*document)->dmFileMetaType));
2871 if (err) {
2872 char *meta_type_locale = utf82locale(string);
2873 isds_printf_message(context,
2874 _("Document has invalid dmFileMetaType attribute value: %s"),
2875 meta_type_locale);
2876 free(meta_type_locale);
2877 err = IE_ISDS;
2878 goto leave;
2880 zfree(string);
2882 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
2883 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
2884 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
2885 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
2888 /* Extract document data.
2889 * Base64 encoded blob or XML subtree must be presented. */
2891 /* Check from dmEncodedContent */
2892 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
2893 xpath_ctx);
2894 if (!result) {
2895 err = IE_XML;
2896 goto leave;
2899 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2900 /* Here we have Base64 blob */
2902 if (result->nodesetval->nodeNr > 1) {
2903 isds_printf_message(context,
2904 _("Document has more dmEncodedContent elements"));
2905 err = IE_ISDS;
2906 goto leave;
2909 xmlXPathFreeObject(result); result = NULL;
2910 EXTRACT_STRING("isds:dmEncodedContent", string);
2912 /* Decode non-emptys document */
2913 if (string && string[0] != '\0') {
2914 (*document)->data_length = b64decode(string, &((*document)->data));
2915 if ((*document)->data_length == (size_t) -1) {
2916 isds_printf_message(context,
2917 _("Error while Base64-decoding document content"));
2918 err = IE_ERROR;
2919 goto leave;
2922 } else {
2923 /* No Base64 blob, try XML document */
2924 xmlXPathFreeObject(result); result = NULL;
2925 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
2926 xpath_ctx);
2927 if (!result) {
2928 err = IE_XML;
2929 goto leave;
2932 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2933 /* Here we have XML document */
2935 if (result->nodesetval->nodeNr > 1) {
2936 isds_printf_message(context,
2937 _("Document has more dmXMLContent elements"));
2938 err = IE_ISDS;
2939 goto leave;
2942 /* FIXME: Serialize the tree rooted at result's node */
2943 isds_printf_message(context,
2944 _("XML documents not yet supported"));
2945 err = IE_NOTSUP;
2946 goto leave;
2947 } else {
2948 /* No bas64 blob, nor XML document */
2949 isds_printf_message(context,
2950 _("Document has no dmEncodedContent, nor dmXMLContent "
2951 "element"));
2952 err = IE_ISDS;
2953 goto leave;
2958 leave:
2959 if (err) isds_document_free(document);
2960 free(string);
2961 xmlXPathFreeObject(result);
2962 xpath_ctx->node = file_node;
2963 return err;
2968 /* Extract message documents into reallocated list of documents
2969 * @context is ISDS context
2970 * @documents is automically reallocated message documents list structure
2971 * @xpath_ctx is XPath context with current node as XSD tFilesArray
2972 * In case of error @documents will be freed. */
2973 static isds_error extract_documents(struct isds_ctx *context,
2974 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
2975 isds_error err = IE_SUCCESS;
2976 xmlXPathObjectPtr result = NULL;
2977 xmlNodePtr files_node = xpath_ctx->node;
2978 struct isds_list *document, *prev_document;
2980 if (!context) return IE_INVALID_CONTEXT;
2981 if (!documents) return IE_INVAL;
2982 isds_list_free(documents);
2983 if (!xpath_ctx) return IE_INVAL;
2985 /* Find documents */
2986 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
2987 if (!result) {
2988 err = IE_XML;
2989 goto leave;
2992 /* No match */
2993 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2994 isds_printf_message(context,
2995 _("Message does not contain any document"));
2996 err = IE_ISDS;
2997 goto leave;
3001 /* Iterate over documents */
3002 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
3004 /* Allocate and append list item */
3005 document = calloc(1, sizeof(*document));
3006 if (!document) {
3007 err = IE_NOMEM;
3008 goto leave;
3010 document->destructor = (void (*)(void **))isds_document_free;
3011 if (i == 0) *documents = document;
3012 else prev_document->next = document;
3013 prev_document = document;
3015 /* Extract document */
3016 xpath_ctx->node = result->nodesetval->nodeTab[i];
3017 err = extract_document(context,
3018 (struct isds_document **) &(document->data), xpath_ctx);
3019 if (err) goto leave;
3023 leave:
3024 if (err) isds_list_free(documents);
3025 xmlXPathFreeObject(result);
3026 xpath_ctx->node = files_node;
3027 return err;
3031 /* Convert isds:dmRecord XML tree into structure
3032 * @context is ISDS context
3033 * @envelope is automically reallocated message envelope structure
3034 * @xpath_ctx is XPath context with current node as isds:dmRecord element
3035 * In case of error @envelope will be freed. */
3036 static isds_error extract_DmRecord(struct isds_ctx *context,
3037 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3038 isds_error err = IE_SUCCESS;
3039 xmlXPathObjectPtr result = NULL;
3041 if (!context) return IE_INVALID_CONTEXT;
3042 if (!envelope) return IE_INVAL;
3043 isds_envelope_free(envelope);
3044 if (!xpath_ctx) return IE_INVAL;
3047 *envelope = calloc(1, sizeof(**envelope));
3048 if (!*envelope) {
3049 err = IE_NOMEM;
3050 goto leave;
3054 /* Extract tRecord data */
3055 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
3057 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
3058 * dmAcceptanceTime. */
3059 err = append_status_size_times(context, envelope, xpath_ctx);
3060 if (err) goto leave;
3062 /* Extract envelope elements added by sender and ISDS
3063 * (XSD: gMessageEnvelope type) */
3064 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
3065 if (err) goto leave;
3066 /* dmOVM can not be obtained from ISDS */
3068 /* Get message type */
3069 err = append_message_type(context, envelope, xpath_ctx);
3070 if (err) goto leave;
3073 leave:
3074 if (err) isds_envelope_free(envelope);
3075 xmlXPathFreeObject(result);
3076 return err;
3080 /* Find and convert isds:dmHash XML tree into structure
3081 * @context is ISDS context
3082 * @envelope is automically reallocated message hash structure
3083 * @xpath_ctx is XPath context with current node containing isds:dmHash child
3084 * In case of error @hash will be freed. */
3085 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
3086 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
3087 isds_error err = IE_SUCCESS;
3088 xmlNodePtr old_ctx_node;
3089 xmlXPathObjectPtr result = NULL;
3090 char *string = NULL;
3092 if (!context) return IE_INVALID_CONTEXT;
3093 if (!hash) return IE_INVAL;
3094 isds_hash_free(hash);
3095 if (!xpath_ctx) return IE_INVAL;
3097 old_ctx_node = xpath_ctx->node;
3099 *hash = calloc(1, sizeof(**hash));
3100 if (!*hash) {
3101 err = IE_NOMEM;
3102 goto leave;
3105 /* Locate dmHash */
3106 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
3107 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
3108 err = IE_ISDS;
3109 goto leave;
3111 if (err) {
3112 err = IE_ERROR;
3113 goto leave;
3116 /* Get hash algorithm */
3117 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
3118 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
3119 if (err) {
3120 if (err == IE_ENUM) {
3121 char *string_locale = utf82locale(string);
3122 isds_printf_message(context, _("Unsported hash algorithm: %s"),
3123 string_locale);
3124 free(string_locale);
3126 goto leave;
3128 zfree(string);
3130 /* Get hash value */
3131 EXTRACT_STRING(".", string);
3132 if (!string) {
3133 isds_printf_message(context, _("tHash element is missing hash value"));
3134 err = IE_ISDS;
3135 goto leave;
3137 (*hash)->length = b64decode(string, &((*hash)->value));
3138 if ((*hash)->length == (size_t) -1) {
3139 isds_printf_message(context,
3140 _("Error while Base64-decoding hash value"));
3141 err = IE_ERROR;
3142 goto leave;
3145 leave:
3146 if (err) isds_hash_free(hash);
3147 free(string);
3148 xmlXPathFreeObject(result);
3149 xpath_ctx->node = old_ctx_node;
3150 return err;
3154 /* Find and append isds:dmQTimestamp XML tree into envelope
3155 * @context is ISDS context
3156 * @envelope is automically allocated evnelope structure
3157 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
3158 * child
3159 * In case of error @envelope will be freed. */
3160 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
3161 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3162 isds_error err = IE_SUCCESS;
3163 xmlXPathObjectPtr result = NULL;
3164 char *string = NULL;
3166 if (!context) return IE_INVALID_CONTEXT;
3167 if (!envelope) return IE_INVAL;
3168 if (!xpath_ctx) {
3169 isds_envelope_free(envelope);
3170 return IE_INVAL;
3173 if (!*envelope) {
3174 *envelope = calloc(1, sizeof(**envelope));
3175 if (!*envelope) {
3176 err = IE_NOMEM;
3177 goto leave;
3179 } else {
3180 zfree((*envelope)->timestamp);
3181 (*envelope)->timestamp_length = 0;
3184 /* Get dmQTimestamp */
3185 EXTRACT_STRING("sisds:dmQTimestamp", string);
3186 if (!string) {
3187 isds_printf_message(context, _("Missing dmQTimestamp element content"));
3188 err = IE_ISDS;
3189 goto leave;
3191 (*envelope)->timestamp_length =
3192 b64decode(string, &((*envelope)->timestamp));
3193 if ((*envelope)->timestamp_length == (size_t) -1) {
3194 isds_printf_message(context,
3195 _("Error while Base64-decoding timestamp value"));
3196 err = IE_ERROR;
3197 goto leave;
3200 leave:
3201 if (err) isds_envelope_free(envelope);
3202 free(string);
3203 xmlXPathFreeObject(result);
3204 return err;
3208 /* Convert XSD tReturnedMessage XML tree into message structure.
3209 * It doea not store XML tree into message->raw.
3210 * @context is ISDS context
3211 * @include_documents Use true if documents must be extracted
3212 * (tReturnedMessage XSD type), use false if documents shall be ommited
3213 * (tReturnedMessageEnvelope).
3214 * @message is automically reallocated message structure
3215 * @xpath_ctx is XPath context with current node as tReturnedMessage element
3216 * type
3217 * In case of error @message will be freed. */
3218 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
3219 const _Bool include_documents, struct isds_message **message,
3220 xmlXPathContextPtr xpath_ctx) {
3221 isds_error err = IE_SUCCESS;
3222 xmlNodePtr message_node;
3224 if (!context) return IE_INVALID_CONTEXT;
3225 if (!message) return IE_INVAL;
3226 isds_message_free(message);
3227 if (!xpath_ctx) return IE_INVAL;
3230 *message = calloc(1, sizeof(**message));
3231 if (!*message) {
3232 err = IE_NOMEM;
3233 goto leave;
3236 /* Save message XPATH context node */
3237 message_node = xpath_ctx->node;
3240 /* Extract dmDM */
3241 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
3242 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
3243 if (err) { err = IE_ERROR; goto leave; }
3244 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
3245 if (err) goto leave;
3247 if (include_documents) {
3248 /* Extract dmFiles */
3249 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
3250 xpath_ctx);
3251 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
3252 err = IE_ISDS; goto leave;
3254 if (err) { err = IE_ERROR; goto leave; }
3255 err = extract_documents(context, &((*message)->documents), xpath_ctx);
3256 if (err) goto leave;
3260 /* Restore context to message */
3261 xpath_ctx->node = message_node;
3263 /* Extract dmHash */
3264 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
3265 xpath_ctx);
3266 if (err) goto leave;
3268 /* Extract dmQTimestamp, */
3269 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
3270 xpath_ctx);
3271 if (err) goto leave;
3273 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
3274 * dmAcceptanceTime. */
3275 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
3276 if (err) goto leave;
3278 /* Get message type */
3279 err = append_message_type(context, &((*message)->envelope), xpath_ctx);
3280 if (err) goto leave;
3282 leave:
3283 if (err) isds_message_free(message);
3284 return err;
3288 /* Extract message event into reallocated isds_event structure
3289 * @context is ISDS context
3290 * @event is automically reallocated message event structure
3291 * @xpath_ctx is XPath context with current node as isds:dmEvent
3292 * In case of error @event will be freed. */
3293 static isds_error extract_event(struct isds_ctx *context,
3294 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
3295 isds_error err = IE_SUCCESS;
3296 xmlXPathObjectPtr result = NULL;
3297 xmlNodePtr event_node = xpath_ctx->node;
3298 char *string = NULL;
3300 if (!context) return IE_INVALID_CONTEXT;
3301 if (!event) return IE_INVAL;
3302 isds_event_free(event);
3303 if (!xpath_ctx) return IE_INVAL;
3305 *event = calloc(1, sizeof(**event));
3306 if (!*event) {
3307 err = IE_NOMEM;
3308 goto leave;
3311 /* Extract event data.
3312 * All elements are optional according XSD. That's funny. */
3313 EXTRACT_STRING("sisds:dmEventTime", string);
3314 if (string) {
3315 err = timestring2timeval((xmlChar *) string, &((*event)->time));
3316 if (err) {
3317 char *string_locale = utf82locale(string);
3318 if (err == IE_DATE) err = IE_ISDS;
3319 isds_printf_message(context,
3320 _("Could not convert dmEventTime as ISO time: %s"),
3321 string_locale);
3322 free(string_locale);
3323 goto leave;
3325 zfree(string);
3328 /* dmEventDescr element has prefix and the rest */
3329 EXTRACT_STRING("sisds:dmEventDescr", string);
3330 if (string) {
3331 err = eventstring2event((xmlChar *) string, *event);
3332 if (err) goto leave;
3333 zfree(string);
3336 leave:
3337 if (err) isds_event_free(event);
3338 free(string);
3339 xmlXPathFreeObject(result);
3340 xpath_ctx->node = event_node;
3341 return err;
3345 /* Convert element of XSD tEventsArray type from XML tree into
3346 * isds_list of isds_event's structure. The list is automatically reallocated.
3347 * @context is ISDS context
3348 * @events is automically reallocated list of event structures
3349 * @xpath_ctx is XPath context with current node as tEventsArray
3350 * In case of error @evnets will be freed. */
3351 static isds_error extract_events(struct isds_ctx *context,
3352 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
3353 isds_error err = IE_SUCCESS;
3354 xmlXPathObjectPtr result = NULL;
3355 xmlNodePtr events_node = xpath_ctx->node;
3356 struct isds_list *event, *prev_event = NULL;
3358 if (!context) return IE_INVALID_CONTEXT;
3359 if (!events) return IE_INVAL;
3360 if (!xpath_ctx) return IE_INVAL;
3362 /* Free old list */
3363 isds_list_free(events);
3365 /* Find events */
3366 result = xmlXPathEvalExpression(BAD_CAST "sisds:dmEvent", xpath_ctx);
3367 if (!result) {
3368 err = IE_XML;
3369 goto leave;
3372 /* No match */
3373 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3374 isds_printf_message(context,
3375 _("Delivery info does not contain any event"));
3376 err = IE_ISDS;
3377 goto leave;
3381 /* Iterate over events */
3382 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
3384 /* Allocate and append list item */
3385 event = calloc(1, sizeof(*event));
3386 if (!event) {
3387 err = IE_NOMEM;
3388 goto leave;
3390 event->destructor = (void (*)(void **))isds_event_free;
3391 if (i == 0) *events = event;
3392 else prev_event->next = event;
3393 prev_event = event;
3395 /* Extract event */
3396 xpath_ctx->node = result->nodesetval->nodeTab[i];
3397 err = extract_event(context,
3398 (struct isds_event **) &(event->data), xpath_ctx);
3399 if (err) goto leave;
3403 leave:
3404 if (err) isds_list_free(events);
3405 xmlXPathFreeObject(result);
3406 xpath_ctx->node = events_node;
3407 return err;
3411 /* Convert isds_document structure into XML tree and append to dmFiles node.
3412 * @context is session context
3413 * @document is ISDS document
3414 * @dm_files is XML element the resulting tree will be appended to as a child.
3415 * @return error code, in case of error context' message is filled. */
3416 static isds_error insert_document(struct isds_ctx *context,
3417 struct isds_document *document, xmlNodePtr dm_files) {
3418 isds_error err = IE_SUCCESS;
3419 xmlNodePtr new_file = NULL, file = NULL, node;
3420 xmlAttrPtr attribute_node;
3421 xmlChar *base64data = NULL;
3423 if (!context) return IE_INVALID_CONTEXT;
3424 if (!document || !dm_files) return IE_INVAL;
3426 /* Allocate new dmFile */
3427 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
3428 if (!new_file) {
3429 isds_printf_message(context, _("Could not allocate main dmFile"));
3430 err = IE_ERROR;
3431 goto leave;
3433 /* Append the new dmFile.
3434 * XXX: Main document must go first */
3435 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
3436 file = xmlAddPrevSibling(dm_files->children, new_file);
3437 else
3438 file = xmlAddChild(dm_files, new_file);
3440 if (!file) {
3441 xmlFreeNode(new_file); new_file = NULL;
3442 isds_printf_message(context, _("Could not add dmFile child to "
3443 "%s element"), dm_files->name);
3444 err = IE_ERROR;
3445 goto leave;
3448 /* @dmMimeType is required */
3449 if (!document->dmMimeType) {
3450 isds_log_message(context,
3451 _("Document is missing mandatory MIME type definition"));
3452 err = IE_INVAL;
3453 goto leave;
3455 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
3457 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
3458 if (!string) {
3459 isds_printf_message(context,
3460 _("Document has unkown dmFileMetaType: %ld"),
3461 document->dmFileMetaType);
3462 err = IE_ENUM;
3463 goto leave;
3465 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
3467 if (document->dmFileGuid) {
3468 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
3470 if (document->dmUpFileGuid) {
3471 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
3474 /* @dmFileDescr is required */
3475 if (!document->dmFileDescr) {
3476 isds_log_message(context,
3477 _("Document is missing mandatory description (title)"));
3478 err = IE_INVAL;
3479 goto leave;
3481 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
3483 if (document->dmFormat) {
3484 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
3488 /* Insert content (data) of the document. */
3489 /* XXX; Only base64 is implemented currently. */
3490 base64data = (xmlChar *) b64encode(document->data, document->data_length);
3491 if (!base64data) {
3492 isds_printf_message(context,
3493 _("Not enought memory to encode %zd bytes into Base64"),
3494 document->data_length);
3495 err = IE_NOMEM;
3496 goto leave;
3498 INSERT_STRING(file, "dmEncodedContent", base64data);
3499 free(base64data);
3501 leave:
3502 return err;
3506 /* Append XSD tMStatus XML tree into isds_message_copy structure.
3507 * The copy must pre prealocated, the date are just appended into structure.
3508 * @context is ISDS context
3509 * @copy is message copy struture
3510 * @xpath_ctx is XPath context with current node as tMStatus */
3511 static isds_error append_TMStatus(struct isds_ctx *context,
3512 struct isds_message_copy *copy, xmlXPathContextPtr xpath_ctx) {
3513 isds_error err = IE_SUCCESS;
3514 xmlXPathObjectPtr result = NULL;
3515 char *code = NULL, *message = NULL;
3517 if (!context) return IE_INVALID_CONTEXT;
3518 if (!copy || !xpath_ctx) return IE_INVAL;
3520 /* Free old values */
3521 zfree(copy->dmStatus);
3522 zfree(copy->dmID);
3524 /* Get error specific to this copy */
3525 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code);
3526 if (!code) {
3527 isds_log_message(context,
3528 _("Missing isds:dmStatusCode under "
3529 "XSD:tMStatus type element"));
3530 err = IE_ISDS;
3531 goto leave;
3534 if (xmlStrcmp((const xmlChar *)code, BAD_CAST "0000")) {
3535 /* This copy failed */
3536 copy->error = IE_ISDS;
3537 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message);
3538 if (message) {
3539 copy->dmStatus = astrcat3(code, ": ", message);
3540 if (!copy->dmStatus) {
3541 copy->dmStatus = code;
3542 code = NULL;
3544 } else {
3545 copy->dmStatus = code;
3546 code = NULL;
3548 } else {
3549 /* This copy succeeded. In this case only, message ID is valid */
3550 copy->error = IE_SUCCESS;
3552 EXTRACT_STRING("isds:dmID", copy->dmID);
3553 if (!copy->dmID) {
3554 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
3555 "but did not returned assigned message ID\n"));
3556 err = IE_ISDS;
3560 leave:
3561 free(code);
3562 free(message);
3563 xmlXPathFreeObject(result);
3564 return err;
3568 /* Insert struct isds_approval data (box approval) into XML tree
3569 * @context is sesstion context
3570 * @approval is libsids structure with approval description. NULL is
3571 * acceptible.
3572 * @parent is XML element to append @approval to */
3573 static isds_error insert_GExtApproval(struct isds_ctx *context,
3574 const struct isds_approval *approval, xmlNodePtr parent) {
3576 isds_error err = IE_SUCCESS;
3577 xmlNodePtr node;
3579 if (!context) return IE_INVALID_CONTEXT;
3580 if (!parent) return IE_INVAL;
3582 if (!approval) return IE_SUCCESS;
3584 /* Build XSD:gExtApproval */
3585 INSERT_SCALAR_BOOLEAN(parent, "dbApproved", approval->approved);
3586 INSERT_STRING(parent, "dbExternRefNumber", approval->refference);
3588 leave:
3589 return err;
3593 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
3594 * code
3595 * @context is session context
3596 * @service_name is name of SERVICE_DB_ACCESS
3597 * @response is server SOAP body response as XML document
3598 * @raw_response is automatically reallocated bitstream with response body. Use
3599 * NULL if you don't care
3600 * @raw_response_length is size of @raw_response in bytes
3601 * @code is ISDS status code
3602 * @status_message is ISDS status message
3603 * @return error coded from lower layer, context message will be set up
3604 * appropriately. */
3605 static isds_error build_send_check_dbdummy_request(struct isds_ctx *context,
3606 const xmlChar *service_name,
3607 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
3608 xmlChar **code, xmlChar **status_message) {
3610 isds_error err = IE_SUCCESS;
3611 char *service_name_locale = NULL;
3612 xmlNodePtr request = NULL, node;
3613 xmlNsPtr isds_ns = NULL;
3615 if (!context) return IE_INVALID_CONTEXT;
3616 if (!service_name) return IE_INVAL;
3617 if (!response || !code || !status_message) return IE_INVAL;
3618 if (!raw_response_length && raw_response) return IE_INVAL;
3620 /* Free output argument */
3621 xmlFreeDoc(*response); *response = NULL;
3622 if (raw_response) zfree(*raw_response);
3623 free(*code);
3624 free(*status_message);
3627 /* Check if connection is established
3628 * TODO: This check should be done donwstairs. */
3629 if (!context->curl) return IE_CONNECTION_CLOSED;
3631 service_name_locale = utf82locale((char*)service_name);
3632 if (!service_name_locale) {
3633 err = IE_NOMEM;
3634 goto leave;
3637 /* Build request */
3638 request = xmlNewNode(NULL, service_name);
3639 if (!request) {
3640 isds_printf_message(context,
3641 _("Could not build %s request"), service_name_locale);
3642 err = IE_ERROR;
3643 goto leave;
3645 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
3646 if(!isds_ns) {
3647 isds_log_message(context, _("Could not create ISDS name space"));
3648 err = IE_ERROR;
3649 goto leave;
3651 xmlSetNs(request, isds_ns);
3654 /* Add XSD:tDummyInput child */
3655 INSERT_STRING(request, "dbDummy", NULL);
3658 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
3659 service_name_locale);
3661 /* Send request */
3662 err = isds(context, SERVICE_DB_ACCESS, request, response,
3663 raw_response, raw_response_length);
3664 xmlFreeNode(request); request = NULL;
3666 if (err) {
3667 isds_log(ILF_ISDS, ILL_DEBUG,
3668 _("Processing ISDS response on %s request failed\n"),
3669 service_name_locale);
3670 goto leave;
3673 /* Check for response status */
3674 err = isds_response_status(context, SERVICE_DB_ACCESS, *response,
3675 code, status_message, NULL);
3676 if (err) {
3677 isds_log(ILF_ISDS, ILL_DEBUG,
3678 _("ISDS response on %s request is missing status\n"),
3679 service_name_locale);
3680 goto leave;
3683 /* Request processed, but nothing found */
3684 if (xmlStrcmp(*code, BAD_CAST "0000")) {
3685 char *code_locale = utf82locale((char*) *code);
3686 char *status_message_locale = utf82locale((char*) *status_message);
3687 isds_log(ILF_ISDS, ILL_DEBUG,
3688 _("Server refused %s request (code=%s, message=%s)\n"),
3689 service_name_locale, code_locale, status_message_locale);
3690 isds_log_message(context, status_message_locale);
3691 free(code_locale);
3692 free(status_message_locale);
3693 err = IE_ISDS;
3694 goto leave;
3697 leave:
3698 free(service_name_locale);
3699 xmlFreeNode(request);
3700 return err;
3704 /* Get data about logged in user and his box. */
3705 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
3706 struct isds_DbOwnerInfo **db_owner_info) {
3707 isds_error err = IE_SUCCESS;
3708 xmlDocPtr response = NULL;
3709 xmlChar *code = NULL, *message = NULL;
3710 xmlXPathContextPtr xpath_ctx = NULL;
3711 xmlXPathObjectPtr result = NULL;
3712 char *string = NULL;
3714 if (!context) return IE_INVALID_CONTEXT;
3715 if (!db_owner_info) return IE_INVAL;
3717 /* Check if connection is established */
3718 if (!context->curl) return IE_CONNECTION_CLOSED;
3721 /* Do request and check for success */
3722 err = build_send_check_dbdummy_request(context,
3723 BAD_CAST "GetOwnerInfoFromLogin",
3724 &response, NULL, NULL, &code, &message);
3725 if (err) goto leave;
3728 /* Extract data */
3729 /* Prepare stucture */
3730 isds_DbOwnerInfo_free(db_owner_info);
3731 *db_owner_info = calloc(1, sizeof(**db_owner_info));
3732 if (!*db_owner_info) {
3733 err = IE_NOMEM;
3734 goto leave;
3736 xpath_ctx = xmlXPathNewContext(response);
3737 if (!xpath_ctx) {
3738 err = IE_ERROR;
3739 goto leave;
3741 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
3742 err = IE_ERROR;
3743 goto leave;
3746 /* Set context node */
3747 result = xmlXPathEvalExpression(BAD_CAST
3748 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
3749 if (!result) {
3750 err = IE_ERROR;
3751 goto leave;
3753 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3754 isds_log_message(context, _("Missing dbOwnerInfo element"));
3755 err = IE_ISDS;
3756 goto leave;
3758 if (result->nodesetval->nodeNr > 1) {
3759 isds_log_message(context, _("Multiple dbOwnerInfo element"));
3760 err = IE_ISDS;
3761 goto leave;
3763 xpath_ctx->node = result->nodesetval->nodeTab[0];
3764 xmlXPathFreeObject(result); result = NULL;
3766 /* Extract it */
3767 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
3769 leave:
3770 if (err) {
3771 isds_DbOwnerInfo_free(db_owner_info);
3774 free(string);
3775 xmlXPathFreeObject(result);
3776 xmlXPathFreeContext(xpath_ctx);
3778 free(code);
3779 free(message);
3780 xmlFreeDoc(response);
3782 if (!err)
3783 isds_log(ILF_ISDS, ILL_DEBUG,
3784 _("GetOwnerInfoFromLogin request processed by server "
3785 "successfully.\n"));
3787 return err;
3791 /* Get data about logged in user. */
3792 isds_error isds_GetUserInfoFromLogin(struct isds_ctx *context,
3793 struct isds_DbUserInfo **db_user_info) {
3794 isds_error err = IE_SUCCESS;
3795 xmlDocPtr response = NULL;
3796 xmlChar *code = NULL, *message = NULL;
3797 xmlXPathContextPtr xpath_ctx = NULL;
3798 xmlXPathObjectPtr result = NULL;
3800 if (!context) return IE_INVALID_CONTEXT;
3801 if (!db_user_info) return IE_INVAL;
3803 /* Check if connection is established */
3804 if (!context->curl) return IE_CONNECTION_CLOSED;
3807 /* Do request and check for success */
3808 err = build_send_check_dbdummy_request(context,
3809 BAD_CAST "GetUserInfoFromLogin",
3810 &response, NULL, NULL, &code, &message);
3811 if (err) goto leave;
3814 /* Extract data */
3815 /* Prepare stucture */
3816 isds_DbUserInfo_free(db_user_info);
3817 *db_user_info = calloc(1, sizeof(**db_user_info));
3818 if (!*db_user_info) {
3819 err = IE_NOMEM;
3820 goto leave;
3822 xpath_ctx = xmlXPathNewContext(response);
3823 if (!xpath_ctx) {
3824 err = IE_ERROR;
3825 goto leave;
3827 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
3828 err = IE_ERROR;
3829 goto leave;
3832 /* Set context node */
3833 result = xmlXPathEvalExpression(BAD_CAST
3834 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx);
3835 if (!result) {
3836 err = IE_ERROR;
3837 goto leave;
3839 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3840 isds_log_message(context, _("Missing dbUserInfo element"));
3841 err = IE_ISDS;
3842 goto leave;
3844 if (result->nodesetval->nodeNr > 1) {
3845 isds_log_message(context, _("Multiple dbUserInfo element"));
3846 err = IE_ISDS;
3847 goto leave;
3849 xpath_ctx->node = result->nodesetval->nodeTab[0];
3850 xmlXPathFreeObject(result); result = NULL;
3852 /* Extract it */
3853 err = extract_DbUserInfo(context, db_user_info, xpath_ctx);
3855 leave:
3856 if (err) {
3857 isds_DbUserInfo_free(db_user_info);
3860 xmlXPathFreeObject(result);
3861 xmlXPathFreeContext(xpath_ctx);
3863 free(code);
3864 free(message);
3865 xmlFreeDoc(response);
3867 if (!err)
3868 isds_log(ILF_ISDS, ILL_DEBUG,
3869 _("GetUserInfoFromLogin request processed by server "
3870 "successfully.\n"));
3872 return err;
3876 /* Get expiration time of current password
3877 * @context is session context
3878 * @expiration is automatically reallocated time when password expires, In
3879 * case of error will be nulled. */
3880 isds_error isds_get_password_expiration(struct isds_ctx *context,
3881 struct timeval **expiration) {
3882 isds_error err = IE_SUCCESS;
3883 xmlDocPtr response = NULL;
3884 xmlChar *code = NULL, *message = NULL;
3885 xmlXPathContextPtr xpath_ctx = NULL;
3886 xmlXPathObjectPtr result = NULL;
3887 char *string = NULL;
3889 if (!context) return IE_INVALID_CONTEXT;
3890 if (!expiration) return IE_INVAL;
3892 /* Check if connection is established */
3893 if (!context->curl) return IE_CONNECTION_CLOSED;
3896 /* Do request and check for success */
3897 err = build_send_check_dbdummy_request(context,
3898 BAD_CAST "GetPasswordInfo",
3899 &response, NULL, NULL, &code, &message);
3900 if (err) goto leave;
3903 /* Extract data */
3904 xpath_ctx = xmlXPathNewContext(response);
3905 if (!xpath_ctx) {
3906 err = IE_ERROR;
3907 goto leave;
3909 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
3910 err = IE_ERROR;
3911 goto leave;
3914 /* Set context node */
3915 result = xmlXPathEvalExpression(BAD_CAST
3916 "/isds:GetPasswordInfoResponse", xpath_ctx);
3917 if (!result) {
3918 err = IE_ERROR;
3919 goto leave;
3921 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3922 isds_log_message(context,
3923 _("Missing GetPasswordInfoResponse element"));
3924 err = IE_ISDS;
3925 goto leave;
3927 if (result->nodesetval->nodeNr > 1) {
3928 isds_log_message(context,
3929 _("Multiple GetPasswordInfoResponse element"));
3930 err = IE_ISDS;
3931 goto leave;
3933 xpath_ctx->node = result->nodesetval->nodeTab[0];
3934 xmlXPathFreeObject(result); result = NULL;
3936 /* Extract expiration date */
3937 EXTRACT_STRING("isds:pswExpDate", string);
3938 if (!string) {
3939 isds_log_message(context, _("Missing pswExpDate element"));
3940 err = IE_ISDS;
3941 goto leave;
3944 err = timestring2timeval((xmlChar *) string, expiration);
3945 if (err) {
3946 char *string_locale = utf82locale(string);
3947 if (err == IE_DATE) err = IE_ISDS;
3948 isds_printf_message(context,
3949 _("Could not convert pswExpDate as ISO time: %s"),
3950 string_locale);
3951 free(string_locale);
3952 goto leave;
3955 leave:
3956 if (err) {
3957 if (*expiration) {
3958 zfree(*expiration);
3962 free(string);
3963 xmlXPathFreeObject(result);
3964 xmlXPathFreeContext(xpath_ctx);
3966 free(code);
3967 free(message);
3968 xmlFreeDoc(response);
3970 if (!err)
3971 isds_log(ILF_ISDS, ILL_DEBUG,
3972 _("GetPasswordInfo request processed by server "
3973 "successfully.\n"));
3975 return err;
3979 /* Change user password in ISDS.
3980 * User must supply old password, new password will takes effect after some
3981 * time, current session can continue. Password must fulfill some constraints.
3982 * @context is session context
3983 * @old_password is current password.
3984 * @new_password is requested new password */
3985 isds_error isds_change_password(struct isds_ctx *context,
3986 const char *old_password, const char *new_password) {
3987 isds_error err = IE_SUCCESS;
3988 xmlNsPtr isds_ns = NULL;
3989 xmlNodePtr request = NULL, node;
3990 xmlDocPtr response = NULL;
3991 xmlChar *code = NULL, *message = NULL;
3993 if (!context) return IE_INVALID_CONTEXT;
3994 if (!old_password || !new_password) return IE_INVAL;
3996 /* Check if connection is established
3997 * TODO: This check should be done donwstairs. */
3998 if (!context->curl) return IE_CONNECTION_CLOSED;
4001 /* Build ChangeISDSPassword request */
4002 request = xmlNewNode(NULL, BAD_CAST "ChangeISDSPassword");
4003 if (!request) {
4004 isds_log_message(context,
4005 _("Could build ChangeISDSPassword request"));
4006 return IE_ERROR;
4008 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4009 if(!isds_ns) {
4010 isds_log_message(context, _("Could not create ISDS name space"));
4011 xmlFreeNode(request);
4012 return IE_ERROR;
4014 xmlSetNs(request, isds_ns);
4016 INSERT_STRING(request, "dbOldPassword", old_password);
4017 INSERT_STRING(request, "dbNewPassword", new_password);
4020 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CheckDataBox request to ISDS\n"));
4022 /* Sent request */
4023 err = isds(context, SERVICE_DB_ACCESS, request, &response, NULL, NULL);
4025 /* Destroy request */
4026 xmlFreeNode(request); request = NULL;
4028 if (err) {
4029 isds_log(ILF_ISDS, ILL_DEBUG,
4030 _("Processing ISDS response on ChangeISDSPassword "
4031 "request failed\n"));
4032 goto leave;
4035 /* Check for response status */
4036 err = isds_response_status(context, SERVICE_DB_ACCESS, response,
4037 &code, &message, NULL);
4038 if (err) {
4039 isds_log(ILF_ISDS, ILL_DEBUG,
4040 _("ISDS response on ChangeISDSPassword request is missing "
4041 "status\n"));
4042 goto leave;
4045 /* Request processed, but empty password refused */
4046 if (!xmlStrcmp(code, BAD_CAST "1066")) {
4047 char *code_locale = utf82locale((char*)code);
4048 char *message_locale = utf82locale((char*)message);
4049 isds_log(ILF_ISDS, ILL_DEBUG,
4050 _("Server refused empty password on ChangeISDSPassword "
4051 "request (code=%s, message=%s)\n"),
4052 code_locale, message_locale);
4053 isds_log_message(context, _("Password must not be empty"));
4054 free(code_locale);
4055 free(message_locale);
4056 err = IE_INVAL;
4057 goto leave;
4060 /* Request processed, but new password was reused */
4061 else if (!xmlStrcmp(code, BAD_CAST "1067")) {
4062 char *code_locale = utf82locale((char*)code);
4063 char *message_locale = utf82locale((char*)message);
4064 isds_log(ILF_ISDS, ILL_DEBUG,
4065 _("Server refused the same new password on ChangeISDSPassword "
4066 "request (code=%s, message=%s)\n"),
4067 code_locale, message_locale);
4068 isds_log_message(context,
4069 _("New password must differ from the current one"));
4070 free(code_locale);
4071 free(message_locale);
4072 err = IE_INVAL;
4073 goto leave;
4076 /* Other error */
4077 else if (xmlStrcmp(code, BAD_CAST "0000")) {
4078 char *code_locale = utf82locale((char*)code);
4079 char *message_locale = utf82locale((char*)message);
4080 isds_log(ILF_ISDS, ILL_DEBUG,
4081 _("Server refused to change password on ChangeISDSPassword "
4082 "request (code=%s, message=%s)\n"),
4083 code_locale, message_locale);
4084 isds_log_message(context, message_locale);
4085 free(code_locale);
4086 free(message_locale);
4087 err = IE_ISDS;
4088 goto leave;
4091 /* Otherwise password changed successfully */
4093 leave:
4094 free(code);
4095 free(message);
4096 xmlFreeDoc(response);
4097 xmlFreeNode(request);
4099 if (!err)
4100 isds_log(ILF_ISDS, ILL_DEBUG,
4101 _("Password changed successfully on ChangeISDSPassword "
4102 "request.\n"));
4104 return err;
4108 /* Generic middle part with request sending and response check.
4109 * It sends prepared request and checks for error code.
4110 * @context is ISDS session context.
4111 * @service is ISDS service handler
4112 * @service_name is name in scope of given @service
4113 * @request is XML tree with request. Will be freed to save memory.
4114 * @response is XML document ouputing ISDS response.
4115 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4116 * NULL, if you don't care. */
4117 static isds_error send_destroy_request_check_response(
4118 struct isds_ctx *context,
4119 const isds_service service, const xmlChar *service_name,
4120 xmlNodePtr *request, xmlDocPtr *response, xmlChar **refnumber) {
4121 isds_error err = IE_SUCCESS;
4122 char *service_name_locale = NULL;
4123 xmlChar *code = NULL, *message = NULL;
4126 if (!context) return IE_INVALID_CONTEXT;
4127 if (!service_name || *service_name == '\0' || !request || !*request ||
4128 !response)
4129 return IE_INVAL;
4131 /* Check if connection is established
4132 * TODO: This check should be done donwstairs. */
4133 if (!context->curl) return IE_CONNECTION_CLOSED;
4135 service_name_locale = utf82locale((char*) service_name);
4136 if (!service_name_locale) {
4137 err = IE_NOMEM;
4138 goto leave;
4141 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
4142 service_name_locale);
4144 /* Send request */
4145 err = isds(context, service, *request, response, NULL, NULL);
4146 xmlFreeNode(*request); *request = NULL;
4148 if (err) {
4149 isds_log(ILF_ISDS, ILL_DEBUG,
4150 _("Processing ISDS response on %s request failed\n"),
4151 service_name_locale);
4152 goto leave;
4155 /* Check for response status */
4156 err = isds_response_status(context, service, *response,
4157 &code, &message, refnumber);
4158 if (err) {
4159 isds_log(ILF_ISDS, ILL_DEBUG,
4160 _("ISDS response on %s request is missing status\n"),
4161 service_name_locale);
4162 goto leave;
4165 /* Request processed, but server failed */
4166 if (xmlStrcmp(code, BAD_CAST "0000")) {
4167 char *code_locale = utf82locale((char*) code);
4168 char *message_locale = utf82locale((char*) message);
4169 isds_log(ILF_ISDS, ILL_DEBUG,
4170 _("Server refused %s request (code=%s, message=%s)\n"),
4171 service_name_locale, code_locale, message_locale);
4172 isds_log_message(context, message_locale);
4173 free(code_locale);
4174 free(message_locale);
4175 err = IE_ISDS;
4176 goto leave;
4180 leave:
4181 free(code);
4182 free(message);
4183 if (err && *response) {
4184 xmlFreeDoc(*response);
4185 *response = NULL;
4187 if (*request) {
4188 xmlFreeNode(*request);
4189 *request = NULL;
4191 free(service_name_locale);
4193 return err;
4197 /* Generic bottom half with request sending.
4198 * It sends prepared request, checks for error code, destroys response and
4199 * request and log success or failure.
4200 * @context is ISDS session context.
4201 * @service is ISDS service handler
4202 * @service_name is name in scope of given @service
4203 * @request is XML tree with request. Will be freed to save memory.
4204 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4205 * NULL, if you don't care. */
4206 static isds_error send_request_check_drop_response(
4207 struct isds_ctx *context,
4208 const isds_service service, const xmlChar *service_name,
4209 xmlNodePtr *request, xmlChar **refnumber) {
4210 isds_error err = IE_SUCCESS;
4211 char *service_name_locale = NULL;
4212 xmlDocPtr response = NULL;
4215 if (!context) return IE_INVALID_CONTEXT;
4216 if (!service_name || *service_name == '\0' || !request || !*request)
4217 return IE_INVAL;
4219 /* Send request and check response*/
4220 err = send_destroy_request_check_response(context,
4221 service, service_name, request, &response, refnumber);
4223 xmlFreeDoc(response);
4225 if (*request) {
4226 xmlFreeNode(*request);
4227 *request = NULL;
4230 if (!err) {
4231 isds_log(ILF_ISDS, ILL_DEBUG,
4232 _("%s request processed by server successfully.\n"),
4233 service_name_locale);
4235 free(service_name_locale);
4237 return err;
4241 /* Build XSD:tCreateDBInput request type for box createing.
4242 * @context is session context
4243 * @request outputs built XML tree
4244 * @service_name is request name of SERVICE_DB_MANIPULATION service
4245 * @box is box description to create including single primary user (in case of
4246 * FO box type)
4247 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
4248 * box, or contact address of PFO box owner)
4249 * @former_names is optional undocumented string. Pass NULL if you don't care.
4250 * @upper_box_id is optional ID of supper box if currently created box is
4251 * subordinated.
4252 * @ceo_label is optional title of OVM box owner (e.g. mayor) NULL, if you
4253 * don't care.
4254 * @approval is optional external approval of box manipulation */
4255 static isds_error build_CreateDBInput_request(struct isds_ctx *context,
4256 xmlNodePtr *request, const xmlChar *service_name,
4257 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
4258 const xmlChar *former_names, const xmlChar *upper_box_id,
4259 const xmlChar *ceo_label, const struct isds_approval *approval) {
4260 isds_error err = IE_SUCCESS;
4261 xmlNsPtr isds_ns = NULL;
4262 xmlNodePtr node, dbPrimaryUsers;
4263 xmlChar *string = NULL;
4264 const struct isds_list *item;
4267 if (!context) return IE_INVALID_CONTEXT;
4268 if (!request || !service_name || service_name[0] == '\0' || !box)
4269 return IE_INVAL;
4272 /* Build DeleteDataBox request */
4273 *request = xmlNewNode(NULL, service_name);
4274 if (!*request) {
4275 char *service_name_locale = utf82locale((char*) service_name);
4276 isds_printf_message(context, _("Could build %s request"),
4277 service_name_locale);
4278 free(service_name_locale);
4279 return IE_ERROR;
4281 isds_ns = xmlNewNs(*request, BAD_CAST ISDS_NS, NULL);
4282 if (!isds_ns) {
4283 isds_log_message(context, _("Could not create ISDS name space"));
4284 xmlFreeNode(*request);
4285 return IE_ERROR;
4287 xmlSetNs(*request, isds_ns);
4289 INSERT_ELEMENT(node, *request, "dbOwnerInfo");
4290 err = insert_DbOwnerInfo(context, box, node);
4291 if (err) goto leave;
4293 /* Insert users */
4294 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
4295 * verbose documentatiot allows none dbUserInfo */
4296 INSERT_ELEMENT(dbPrimaryUsers, *request, "dbPrimaryUsers");
4297 for (item = users; item; item = item->next) {
4298 if (item->data) {
4299 INSERT_ELEMENT(node, dbPrimaryUsers, "dbUserInfo");
4300 err = insert_DbUserInfo(context,
4301 (struct isds_DbUserInfo *) item->data, node);
4302 if (err) goto leave;
4306 INSERT_STRING(*request, "dbFormerNames", former_names);
4307 INSERT_STRING(*request, "dbUpperDBId", upper_box_id);
4308 INSERT_STRING(*request, "dbCEOLabel", ceo_label);
4310 err = insert_GExtApproval(context, approval, *request);
4311 if (err) goto leave;
4313 leave:
4314 if (err) {
4315 xmlFreeNode(*request);
4316 *request = NULL;
4318 free(string);
4319 return err;
4323 /* Create new box.
4324 * @context is session context
4325 * @box is box description to create including single primary user (in case of
4326 * FO box type). It outputs box ID assigned by ISDS in dbID element.
4327 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
4328 * box, or contact address of PFO box owner)
4329 * @former_names is optional undocumented string. Pass NULL if you don't care.
4330 * @upper_box_id is optional ID of supper box if currently created box is
4331 * subordinated.
4332 * @ceo_label is optional title of OVM box owner (e.g. mayor)
4333 * @approval is optional external approval of box manipulation
4334 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4335 * NULL, if you don't care.*/
4336 isds_error isds_add_box(struct isds_ctx *context,
4337 struct isds_DbOwnerInfo *box, const struct isds_list *users,
4338 const char *former_names, const char *upper_box_id,
4339 const char *ceo_label, const struct isds_approval *approval,
4340 char **refnumber) {
4341 isds_error err = IE_SUCCESS;
4342 xmlNodePtr request = NULL;
4343 xmlDocPtr response = NULL;
4344 xmlXPathContextPtr xpath_ctx = NULL;
4345 xmlXPathObjectPtr result = NULL;
4348 if (!context) return IE_INVALID_CONTEXT;
4349 if (!box) return IE_INVAL;
4351 /* Scratch box ID */
4352 zfree(box->dbID);
4354 /* Build CreateDataBox request */
4355 err = build_CreateDBInput_request(context,
4356 &request, BAD_CAST "CreateDataBox",
4357 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
4358 (xmlChar *) ceo_label, approval);
4359 if (err) goto leave;
4361 /* Send it to server and process response */
4362 err = send_destroy_request_check_response(context,
4363 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
4364 &response, (xmlChar **) refnumber);
4366 /* Extract box ID */
4367 xpath_ctx = xmlXPathNewContext(response);
4368 if (!xpath_ctx) {
4369 err = IE_ERROR;
4370 goto leave;
4372 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4373 err = IE_ERROR;
4374 goto leave;
4376 EXTRACT_STRING("/isds:CreateDataBoxResponse/dbID", box->dbID);
4378 leave:
4379 xmlXPathFreeObject(result);
4380 xmlXPathFreeContext(xpath_ctx);
4381 xmlFreeDoc(response);
4382 xmlFreeNode(request);
4384 if (!err) {
4385 isds_log(ILF_ISDS, ILL_DEBUG,
4386 _("CreateDataBox request processed by server successfully.\n"));
4389 return err;
4393 /* Notify ISDS about new PFO entity.
4394 * This function has no real effect.
4395 * @context is session context
4396 * @box is PFO description including single primary user.
4397 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
4398 * @former_names is optional undocumented string. Pass NULL if you don't care.
4399 * @upper_box_id is optional ID of supper box if currently created box is
4400 * subordinated.
4401 * @ceo_label is optional title of OVM box owner (e.g. mayor)
4402 * @approval is optional external approval of box manipulation
4403 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4404 * NULL, if you don't care.*/
4405 isds_error isds_add_pfoinfo(struct isds_ctx *context,
4406 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
4407 const char *former_names, const char *upper_box_id,
4408 const char *ceo_label, const struct isds_approval *approval,
4409 char **refnumber) {
4410 isds_error err = IE_SUCCESS;
4411 xmlNodePtr request = NULL;
4413 if (!context) return IE_INVALID_CONTEXT;
4414 if (!box) return IE_INVAL;
4416 /* Build CreateDataBoxPFOInfo request */
4417 err = build_CreateDBInput_request(context,
4418 &request, BAD_CAST "CreateDataBoxPFOInfo",
4419 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
4420 (xmlChar *) ceo_label, approval);
4421 if (err) goto leave;
4423 /* Send it to server and process response */
4424 err = send_request_check_drop_response(context,
4425 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
4426 (xmlChar **) refnumber);
4427 leave:
4428 xmlFreeNode(request);
4429 return err;
4433 /* Remove given given box permanetly.
4434 * @context is session context
4435 * @box is box description to delete
4436 * @since is date of box owner cancalation. Only tm_year, tm_mon and tm_mday
4437 * carry sane value.
4438 * @approval is optional external approval of box manipulation
4439 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4440 * NULL, if you don't care.*/
4441 isds_error isds_delete_box(struct isds_ctx *context,
4442 const struct isds_DbOwnerInfo *box, const struct tm *since,
4443 const struct isds_approval *approval, char **refnumber) {
4444 isds_error err = IE_SUCCESS;
4445 xmlNsPtr isds_ns = NULL;
4446 xmlNodePtr request = NULL;
4447 xmlNodePtr node;
4448 xmlChar *string = NULL;
4451 if (!context) return IE_INVALID_CONTEXT;
4452 if (!box || !since) return IE_INVAL;
4455 /* Build DeleteDataBox request */
4456 request = xmlNewNode(NULL, BAD_CAST "DeleteDataBox");
4457 if (!request) {
4458 isds_log_message(context,
4459 _("Could build DeleteDataBox request"));
4460 return IE_ERROR;
4462 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4463 if(!isds_ns) {
4464 isds_log_message(context, _("Could not create ISDS name space"));
4465 xmlFreeNode(request);
4466 return IE_ERROR;
4468 xmlSetNs(request, isds_ns);
4470 INSERT_ELEMENT(node, request, "dbOwnerInfo");
4471 err = insert_DbOwnerInfo(context, box, node);
4472 if (err) goto leave;
4474 err = tm2datestring(since, &string);
4475 if (err) {
4476 isds_log_message(context,
4477 _("Could not convert `since' argument to ISO date string"));
4478 goto leave;
4480 INSERT_STRING(request, "dbOwnerTerminationDate", string);
4481 zfree(string);
4483 err = insert_GExtApproval(context, approval, request);
4484 if (err) goto leave;
4487 /* Send it to server and process response */
4488 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
4489 BAD_CAST "DeleteDataBox", &request, (xmlChar **) refnumber);
4491 leave:
4492 xmlFreeNode(request);
4493 free(string);
4494 return err;
4498 /* Update data about given box.
4499 * @context is session context
4500 * @old_box current box description
4501 * @new_box are updated data about @old_box
4502 * @approval is optional external approval of box manipulation
4503 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4504 * NULL, if you don't care.*/
4505 isds_error isds_UpdateDataBoxDescr(struct isds_ctx *context,
4506 const struct isds_DbOwnerInfo *old_box,
4507 const struct isds_DbOwnerInfo *new_box,
4508 const struct isds_approval *approval, char **refnumber) {
4509 isds_error err = IE_SUCCESS;
4510 xmlNsPtr isds_ns = NULL;
4511 xmlNodePtr request = NULL;
4512 xmlNodePtr node;
4515 if (!context) return IE_INVALID_CONTEXT;
4516 if (!old_box || !new_box) return IE_INVAL;
4519 /* Build UpdateDataBoxDescr request */
4520 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxDescr");
4521 if (!request) {
4522 isds_log_message(context,
4523 _("Could build UpdateDataBoxDescr request"));
4524 return IE_ERROR;
4526 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4527 if(!isds_ns) {
4528 isds_log_message(context, _("Could not create ISDS name space"));
4529 xmlFreeNode(request);
4530 return IE_ERROR;
4532 xmlSetNs(request, isds_ns);
4534 INSERT_ELEMENT(node, request, "dbOldOwnerInfo");
4535 err = insert_DbOwnerInfo(context, old_box, node);
4536 if (err) goto leave;
4538 INSERT_ELEMENT(node, request, "dbNewOwnerInfo");
4539 err = insert_DbOwnerInfo(context, new_box, node);
4540 if (err) goto leave;
4542 err = insert_GExtApproval(context, approval, request);
4543 if (err) goto leave;
4546 /* Send it to server and process response */
4547 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
4548 BAD_CAST "UpdateDataBoxDescr", &request, (xmlChar **) refnumber);
4550 leave:
4551 xmlFreeNode(request);
4553 return err;
4557 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
4558 * code
4559 * @context is session context
4560 * @service is SOAP service
4561 * @service_name is name of request in @service
4562 * @box_id is box ID of interrest
4563 * @approval is optional external approval of box manipulation
4564 * @response is server SOAP body response as XML document
4565 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4566 * NULL, if you don't care.
4567 * @return error coded from lower layer, context message will be set up
4568 * appropriately. */
4569 static isds_error build_send_dbid_request_check_response(
4570 struct isds_ctx *context, const isds_service service,
4571 const xmlChar *service_name, const xmlChar *box_id,
4572 const struct isds_approval *approval,
4573 xmlDocPtr *response, xmlChar **refnumber) {
4575 isds_error err = IE_SUCCESS;
4576 char *service_name_locale = NULL, *box_id_locale = NULL;
4577 xmlNodePtr request = NULL, node;
4578 xmlNsPtr isds_ns = NULL;
4580 if (!context) return IE_INVALID_CONTEXT;
4581 if (!service_name || !box_id) return IE_INVAL;
4582 if (!response) return IE_INVAL;
4584 /* Free output argument */
4585 xmlFreeDoc(*response); *response = NULL;
4587 /* Prepare strings */
4588 service_name_locale = utf82locale((char*)service_name);
4589 if (!service_name_locale) {
4590 err = IE_NOMEM;
4591 goto leave;
4593 box_id_locale = utf82locale((char*)box_id);
4594 if (!box_id_locale) {
4595 err = IE_NOMEM;
4596 goto leave;
4599 /* Build request */
4600 request = xmlNewNode(NULL, service_name);
4601 if (!request) {
4602 isds_printf_message(context,
4603 _("Could not build %s request"), service_name_locale);
4604 err = IE_ERROR;
4605 goto leave;
4607 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4608 if(!isds_ns) {
4609 isds_log_message(context, _("Could not create ISDS name space"));
4610 err = IE_ERROR;
4611 goto leave;
4613 xmlSetNs(request, isds_ns);
4615 /* Add XSD:tIdDbInput childs*/
4616 INSERT_STRING(request, "dbID", box_id);
4617 err = insert_GExtApproval(context, approval, request);
4618 if (err) goto leave;
4620 /* Send request and check response*/
4621 err = send_destroy_request_check_response(context,
4622 service, service_name, &request, response, refnumber);
4624 leave:
4625 free(service_name_locale);
4626 free(box_id_locale);
4627 xmlFreeNode(request);
4628 return err;
4632 /* Get data about all users assigned to given box.
4633 * @context is session context
4634 * @box_id is box ID
4635 * @users is automatically reallocated list of struct isds_DbUserInfo */
4636 isds_error isds_GetDataBoxUsers(struct isds_ctx *context, const char *box_id,
4637 struct isds_list **users) {
4638 isds_error err = IE_SUCCESS;
4639 xmlDocPtr response = NULL;
4640 xmlXPathContextPtr xpath_ctx = NULL;
4641 xmlXPathObjectPtr result = NULL;
4642 int i;
4643 struct isds_list *item, *prev_item = NULL;
4645 if (!context) return IE_INVALID_CONTEXT;
4646 if (!users || !box_id) return IE_INVAL;
4649 /* Do request and check for success */
4650 err = build_send_dbid_request_check_response(context,
4651 SERVICE_DB_MANIPULATION, BAD_CAST "GetDataBoxUsers",
4652 BAD_CAST box_id, NULL, &response, NULL);
4653 if (err) goto leave;
4656 /* Extract data */
4657 /* Prepare stucture */
4658 isds_list_free(users);
4659 xpath_ctx = xmlXPathNewContext(response);
4660 if (!xpath_ctx) {
4661 err = IE_ERROR;
4662 goto leave;
4664 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4665 err = IE_ERROR;
4666 goto leave;
4669 /* Set context node */
4670 result = xmlXPathEvalExpression(BAD_CAST
4671 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
4672 xpath_ctx);
4673 if (!result) {
4674 err = IE_ERROR;
4675 goto leave;
4677 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4678 isds_log_message(context, _("Missing dbUserInfo element"));
4679 err = IE_ISDS;
4680 goto leave;
4683 /* Iterate over all users */
4684 for (i = 0; i < result->nodesetval->nodeNr; i++) {
4686 /* Prepare structure */
4687 item = calloc(1, sizeof(*item));
4688 if (!item) {
4689 err = IE_NOMEM;
4690 goto leave;
4692 item->destructor = (void(*)(void**))isds_DbUserInfo_free;
4693 if (i == 0) *users = item;
4694 else prev_item->next = item;
4695 prev_item = item;
4697 /* Extract it */
4698 xpath_ctx->node = result->nodesetval->nodeTab[i];
4699 err = extract_DbUserInfo(context,
4700 (struct isds_DbUserInfo **) (&item->data), xpath_ctx);
4701 if (err) goto leave;
4704 leave:
4705 if (err) {
4706 isds_list_free(users);
4709 xmlXPathFreeObject(result);
4710 xmlXPathFreeContext(xpath_ctx);
4711 xmlFreeDoc(response);
4713 if (!err)
4714 isds_log(ILF_ISDS, ILL_DEBUG,
4715 _("GetDataBoxUsers request processed by server "
4716 "successfully.\n"));
4718 return err;
4722 /* Update data about user assigned to given box.
4723 * @context is session context
4724 * @box is box identification
4725 * @old_user identifies user to update
4726 * @new_user are updated data about @old_user
4727 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4728 * NULL, if you don't care.*/
4729 isds_error isds_UpdateDataBoxUser(struct isds_ctx *context,
4730 const struct isds_DbOwnerInfo *box,
4731 const struct isds_DbUserInfo *old_user,
4732 const struct isds_DbUserInfo *new_user,
4733 char **refnumber) {
4734 isds_error err = IE_SUCCESS;
4735 xmlNsPtr isds_ns = NULL;
4736 xmlNodePtr request = NULL;
4737 xmlNodePtr node;
4740 if (!context) return IE_INVALID_CONTEXT;
4741 if (!box || !old_user || !new_user) return IE_INVAL;
4744 /* Build UpdateDataBoxUser request */
4745 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxUser");
4746 if (!request) {
4747 isds_log_message(context,
4748 _("Could build UpdateDataBoxUser request"));
4749 return IE_ERROR;
4751 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4752 if(!isds_ns) {
4753 isds_log_message(context, _("Could not create ISDS name space"));
4754 xmlFreeNode(request);
4755 return IE_ERROR;
4757 xmlSetNs(request, isds_ns);
4759 INSERT_ELEMENT(node, request, "dbOwnerInfo");
4760 err = insert_DbOwnerInfo(context, box, node);
4761 if (err) goto leave;
4763 INSERT_ELEMENT(node, request, "dbOldUserInfo");
4764 err = insert_DbUserInfo(context, old_user, node);
4765 if (err) goto leave;
4767 INSERT_ELEMENT(node, request, "dbNewUserInfo");
4768 err = insert_DbUserInfo(context, new_user, node);
4769 if (err) goto leave;
4771 /* Send it to server and process response */
4772 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
4773 BAD_CAST "UpdateDataBoxUser", &request, (xmlChar **) refnumber);
4775 leave:
4776 xmlFreeNode(request);
4778 return err;
4782 /* Reset credentials of user assigned to given box.
4783 * @context is session context
4784 * @box is box identification
4785 * @user identifies user to reset password
4786 * @fee_paid is true if fee has been paid, false otherwise
4787 * @approval is optional external approval of box manipulation
4788 * @token is NULL if new password should be delivered off-line to the user.
4789 * It is valid pointer if user should obtain new password on-line on dedicated
4790 * web server. Then it output automatically reallocated token user needs to
4791 * use to athtorize on the web server to view his new password.
4792 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4793 * NULL, if you don't care.*/
4794 isds_error isds_reset_password(struct isds_ctx *context,
4795 const struct isds_DbOwnerInfo *box,
4796 const struct isds_DbUserInfo *user,
4797 const _Bool fee_paid, const struct isds_approval *approval,
4798 char **token, char **refnumber) {
4799 isds_error err = IE_SUCCESS;
4800 xmlNsPtr isds_ns = NULL;
4801 xmlNodePtr request = NULL, node;
4802 xmlDocPtr response = NULL;
4803 xmlXPathContextPtr xpath_ctx = NULL;
4804 xmlXPathObjectPtr result = NULL;
4807 if (!context) return IE_INVALID_CONTEXT;
4808 if (!box || !user) return IE_INVAL;
4810 if (token) zfree(*token);
4813 /* Build NewAccessData request */
4814 request = xmlNewNode(NULL, BAD_CAST "NewAccessData");
4815 if (!request) {
4816 isds_log_message(context,
4817 _("Could build NewAccessData request"));
4818 return IE_ERROR;
4820 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4821 if(!isds_ns) {
4822 isds_log_message(context, _("Could not create ISDS name space"));
4823 xmlFreeNode(request);
4824 return IE_ERROR;
4826 xmlSetNs(request, isds_ns);
4828 INSERT_ELEMENT(node, request, "dbOwnerInfo");
4829 err = insert_DbOwnerInfo(context, box, node);
4830 if (err) goto leave;
4832 INSERT_ELEMENT(node, request, "dbUserInfo");
4833 err = insert_DbUserInfo(context, user, node);
4834 if (err) goto leave;
4836 INSERT_SCALAR_BOOLEAN(request, "dbFeePaid", fee_paid);
4838 if (token) {
4839 INSERT_SCALAR_BOOLEAN(request, "dbVirtual", 1);
4840 } else {
4841 INSERT_SCALAR_BOOLEAN(request, "dbVirtual", 0);
4844 err = insert_GExtApproval(context, approval, request);
4845 if (err) goto leave;
4847 /* Send request and check reposne*/
4848 err = send_destroy_request_check_response(context,
4849 SERVICE_DB_MANIPULATION, BAD_CAST "NewAccessData", &request,
4850 &response, (xmlChar **) refnumber);
4851 if (err) goto leave;
4854 /* Extract optional token */
4855 if (token) {
4856 xpath_ctx = xmlXPathNewContext(response);
4857 if (!xpath_ctx) {
4858 err = IE_ERROR;
4859 goto leave;
4861 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4862 err = IE_ERROR;
4863 goto leave;
4866 EXTRACT_STRING("/isds:NewAccessDataResponse/dbAccessDataId", *token);
4869 leave:
4870 xmlXPathFreeObject(result);
4871 xmlXPathFreeContext(xpath_ctx);
4872 xmlFreeDoc(response);
4873 xmlFreeNode(request);
4875 if (!err)
4876 isds_log(ILF_ISDS, ILL_DEBUG,
4877 _("NewAccessData request processed by server "
4878 "successfully.\n"));
4880 return err;
4884 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
4885 * code, destroy response and log success.
4886 * @context is ISDS session context.
4887 * @service_name is name of SERVICE_DB_MANIPULATION service
4888 * @box is box identification
4889 * @user identifies user to removve
4890 * @approval is optional external approval of box manipulation
4891 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4892 * NULL, if you don't care. */
4893 static isds_error build_send_manipulationboxuser_request_check_drop_response(
4894 struct isds_ctx *context, const xmlChar *service_name,
4895 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
4896 const struct isds_approval *approval, xmlChar **refnumber) {
4897 isds_error err = IE_SUCCESS;
4898 xmlNsPtr isds_ns = NULL;
4899 xmlNodePtr request = NULL, node;
4902 if (!context) return IE_INVALID_CONTEXT;
4903 if (!service_name || service_name[0] == '\0' || !box || !user)
4904 return IE_INVAL;
4907 /* Build NewAccessData request */
4908 request = xmlNewNode(NULL, service_name);
4909 if (!request) {
4910 char *service_name_locale = utf82locale((char *) service_name);
4911 isds_printf_message(context, _("Could build %s request"),
4912 service_name_locale);
4913 free(service_name_locale);
4914 return IE_ERROR;
4916 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4917 if(!isds_ns) {
4918 isds_log_message(context, _("Could not create ISDS name space"));
4919 xmlFreeNode(request);
4920 return IE_ERROR;
4922 xmlSetNs(request, isds_ns);
4924 INSERT_ELEMENT(node, request, "dbOwnerInfo");
4925 err = insert_DbOwnerInfo(context, box, node);
4926 if (err) goto leave;
4928 INSERT_ELEMENT(node, request, "dbUserInfo");
4929 err = insert_DbUserInfo(context, user, node);
4930 if (err) goto leave;
4932 err = insert_GExtApproval(context, approval, request);
4933 if (err) goto leave;
4935 /* Send request and check reposne*/
4936 err = send_request_check_drop_response (context,
4937 SERVICE_DB_MANIPULATION, service_name, &request, refnumber);
4939 leave:
4940 xmlFreeNode(request);
4941 return err;
4945 /* Assign new user to given box.
4946 * @context is session context
4947 * @box is box identification
4948 * @user defines new user to add
4949 * @approval is optional external approval of box manipulation
4950 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4951 * NULL, if you don't care.*/
4952 isds_error isds_add_user(struct isds_ctx *context,
4953 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
4954 const struct isds_approval *approval, char **refnumber) {
4955 return build_send_manipulationboxuser_request_check_drop_response(context,
4956 BAD_CAST "AddDataBoxUser", box, user, approval,
4957 (xmlChar **) refnumber);
4961 /* Remove user assigned to given box.
4962 * @context is session context
4963 * @box is box identification
4964 * @user identifies user to removve
4965 * @approval is optional external approval of box manipulation
4966 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4967 * NULL, if you don't care.*/
4968 isds_error isds_delete_user(struct isds_ctx *context,
4969 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
4970 const struct isds_approval *approval, char **refnumber) {
4971 return build_send_manipulationboxuser_request_check_drop_response(context,
4972 BAD_CAST "DeleteDataBoxUser", box, user, approval,
4973 (xmlChar **) refnumber);
4977 /* Find boxes suiting given criteria.
4978 * @criteria is filter. You should fill in at least some memebers.
4979 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
4980 * possibly empty. Input NULL or valid old structure.
4981 * @return:
4982 * IE_SUCCESS if search sucseeded, @boxes contains usefull data
4983 * IE_NOEXIST if no such box exists, @boxes will be NULL
4984 * IE_2BIG if too much boxes exist and server truncated the resuluts, @boxes
4985 * contains still valid data
4986 * other code if something bad happens. @boxes will be NULL. */
4987 isds_error isds_FindDataBox(struct isds_ctx *context,
4988 const struct isds_DbOwnerInfo *criteria,
4989 struct isds_list **boxes) {
4990 isds_error err = IE_SUCCESS;
4991 _Bool truncated = 0;
4992 xmlNsPtr isds_ns = NULL;
4993 xmlNodePtr request = NULL;
4994 xmlDocPtr response = NULL;
4995 xmlChar *code = NULL, *message = NULL;
4996 xmlNodePtr db_owner_info;
4997 xmlXPathContextPtr xpath_ctx = NULL;
4998 xmlXPathObjectPtr result = NULL;
4999 xmlChar *string = NULL;
5002 if (!context) return IE_INVALID_CONTEXT;
5003 if (!boxes) return IE_INVAL;
5004 isds_list_free(boxes);
5006 if (!criteria) {
5007 return IE_INVAL;
5010 /* Check if connection is established
5011 * TODO: This check should be done donwstairs. */
5012 if (!context->curl) return IE_CONNECTION_CLOSED;
5015 /* Build FindDataBox request */
5016 request = xmlNewNode(NULL, BAD_CAST "FindDataBox");
5017 if (!request) {
5018 isds_log_message(context,
5019 _("Could build FindDataBox request"));
5020 return IE_ERROR;
5022 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5023 if(!isds_ns) {
5024 isds_log_message(context, _("Could not create ISDS name space"));
5025 xmlFreeNode(request);
5026 return IE_ERROR;
5028 xmlSetNs(request, isds_ns);
5029 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
5030 if (!db_owner_info) {
5031 isds_log_message(context, _("Could not add dbOwnerInfo Child to "
5032 "FindDataBox element"));
5033 xmlFreeNode(request);
5034 return IE_ERROR;
5037 err = insert_DbOwnerInfo(context, criteria, db_owner_info);
5038 if (err) goto leave;
5041 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending FindDataBox request to ISDS\n"));
5043 /* Sent request */
5044 err = isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
5046 /* Destroy request */
5047 xmlFreeNode(request); request = NULL;
5049 if (err) {
5050 isds_log(ILF_ISDS, ILL_DEBUG,
5051 _("Processing ISDS response on FindDataBox "
5052 "request failed\n"));
5053 goto leave;
5056 /* Check for response status */
5057 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
5058 &code, &message, NULL);
5059 if (err) {
5060 isds_log(ILF_ISDS, ILL_DEBUG,
5061 _("ISDS response on FindDataBox request is missing status\n"));
5062 goto leave;
5065 /* Request processed, but nothing found */
5066 if (!xmlStrcmp(code, BAD_CAST "0002") ||
5067 !xmlStrcmp(code, BAD_CAST "5001")) {
5068 char *code_locale = utf82locale((char*)code);
5069 char *message_locale = utf82locale((char*)message);
5070 isds_log(ILF_ISDS, ILL_DEBUG,
5071 _("Server did not found any box on FindDataBox request "
5072 "(code=%s, message=%s)\n"), code_locale, message_locale);
5073 isds_log_message(context, message_locale);
5074 free(code_locale);
5075 free(message_locale);
5076 err = IE_NOEXIST;
5077 goto leave;
5080 /* Warning, not a error */
5081 if (!xmlStrcmp(code, BAD_CAST "0003")) {
5082 char *code_locale = utf82locale((char*)code);
5083 char *message_locale = utf82locale((char*)message);
5084 isds_log(ILF_ISDS, ILL_DEBUG,
5085 _("Server truncated response on FindDataBox request "
5086 "(code=%s, message=%s)\n"), code_locale, message_locale);
5087 isds_log_message(context, message_locale);
5088 free(code_locale);
5089 free(message_locale);
5090 truncated = 1;
5093 /* Other error */
5094 else if (xmlStrcmp(code, BAD_CAST "0000")) {
5095 char *code_locale = utf82locale((char*)code);
5096 char *message_locale = utf82locale((char*)message);
5097 isds_log(ILF_ISDS, ILL_DEBUG,
5098 _("Server refused FindDataBox request "
5099 "(code=%s, message=%s)\n"), code_locale, message_locale);
5100 isds_log_message(context, message_locale);
5101 free(code_locale);
5102 free(message_locale);
5103 err = IE_ISDS;
5104 goto leave;
5107 xpath_ctx = xmlXPathNewContext(response);
5108 if (!xpath_ctx) {
5109 err = IE_ERROR;
5110 goto leave;
5112 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5113 err = IE_ERROR;
5114 goto leave;
5117 /* Extract boxes if they present */
5118 result = xmlXPathEvalExpression(BAD_CAST
5119 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
5120 xpath_ctx);
5121 if (!result) {
5122 err = IE_ERROR;
5123 goto leave;
5125 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5126 struct isds_list *item, *prev_item = NULL;
5127 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
5128 item = calloc(1, sizeof(*item));
5129 if (!item) {
5130 err = IE_NOMEM;
5131 goto leave;
5134 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
5135 if (i == 0) *boxes = item;
5136 else prev_item->next = item;
5137 prev_item = item;
5139 xpath_ctx->node = result->nodesetval->nodeTab[i];
5140 err = extract_DbOwnerInfo(context,
5141 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
5142 if (err) goto leave;
5146 leave:
5147 if (err) {
5148 isds_list_free(boxes);
5149 } else {
5150 if (truncated) err = IE_2BIG;
5153 free(string);
5154 xmlFreeNode(request);
5155 xmlXPathFreeObject(result);
5156 xmlXPathFreeContext(xpath_ctx);
5158 free(code);
5159 free(message);
5160 xmlFreeDoc(response);
5162 if (!err)
5163 isds_log(ILF_ISDS, ILL_DEBUG,
5164 _("FindDataBox request processed by server successfully.\n"));
5166 return err;
5170 /* Get status of a box.
5171 * @context is ISDS session context.
5172 * @box_id is UTF-8 encoded box identifier as zero terminated string
5173 * @box_status is return value of box status.
5174 * @return:
5175 * IE_SUCCESS if box has been found and its status retrieved
5176 * IE_NOEXIST if box is not known to ISDS server
5177 * or other appropriate error.
5178 * You can use isds_DbState to enumerate box status. However out of enum
5179 * range value can be returned too. This is feature because ISDS
5180 * specification leaves the set of values open.
5181 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
5182 * the box has been deleted, but ISDS still lists its former existence. */
5183 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
5184 long int *box_status) {
5185 isds_error err = IE_SUCCESS;
5186 xmlNsPtr isds_ns = NULL;
5187 xmlNodePtr request = NULL, db_id;
5188 xmlDocPtr response = NULL;
5189 xmlChar *code = NULL, *message = NULL;
5190 xmlXPathContextPtr xpath_ctx = NULL;
5191 xmlXPathObjectPtr result = NULL;
5192 xmlChar *string = NULL;
5194 if (!context) return IE_INVALID_CONTEXT;
5195 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
5197 /* Check if connection is established
5198 * TODO: This check should be done donwstairs. */
5199 if (!context->curl) return IE_CONNECTION_CLOSED;
5202 /* Build CheckDataBox request */
5203 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
5204 if (!request) {
5205 isds_log_message(context,
5206 _("Could build CheckDataBox request"));
5207 return IE_ERROR;
5209 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5210 if(!isds_ns) {
5211 isds_log_message(context, _("Could not create ISDS name space"));
5212 xmlFreeNode(request);
5213 return IE_ERROR;
5215 xmlSetNs(request, isds_ns);
5216 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
5217 if (!db_id) {
5218 isds_log_message(context, _("Could not add dbId Child to "
5219 "CheckDataBox element"));
5220 xmlFreeNode(request);
5221 return IE_ERROR;
5225 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CheckDataBox request to ISDS\n"));
5227 /* Sent request */
5228 err = isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
5230 /* Destroy request */
5231 xmlFreeNode(request);
5233 if (err) {
5234 isds_log(ILF_ISDS, ILL_DEBUG,
5235 _("Processing ISDS response on CheckDataBox "
5236 "request failed\n"));
5237 goto leave;
5240 /* Check for response status */
5241 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
5242 &code, &message, NULL);
5243 if (err) {
5244 isds_log(ILF_ISDS, ILL_DEBUG,
5245 _("ISDS response on CheckDataBox request is missing status\n"));
5246 goto leave;
5249 /* Request processed, but nothing found */
5250 if (!xmlStrcmp(code, BAD_CAST "5001")) {
5251 char *box_id_locale = utf82locale((char*)box_id);
5252 char *code_locale = utf82locale((char*)code);
5253 char *message_locale = utf82locale((char*)message);
5254 isds_log(ILF_ISDS, ILL_DEBUG,
5255 _("Server did not found box %s on CheckDataBox request "
5256 "(code=%s, message=%s)\n"),
5257 box_id_locale, code_locale, message_locale);
5258 isds_log_message(context, message_locale);
5259 free(box_id_locale);
5260 free(code_locale);
5261 free(message_locale);
5262 err = IE_NOEXIST;
5263 goto leave;
5266 /* Other error */
5267 else if (xmlStrcmp(code, BAD_CAST "0000")) {
5268 char *code_locale = utf82locale((char*)code);
5269 char *message_locale = utf82locale((char*)message);
5270 isds_log(ILF_ISDS, ILL_DEBUG,
5271 _("Server refused CheckDataBox request "
5272 "(code=%s, message=%s)\n"), code_locale, message_locale);
5273 isds_log_message(context, message_locale);
5274 free(code_locale);
5275 free(message_locale);
5276 err = IE_ISDS;
5277 goto leave;
5280 /* Extract data */
5281 xpath_ctx = xmlXPathNewContext(response);
5282 if (!xpath_ctx) {
5283 err = IE_ERROR;
5284 goto leave;
5286 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5287 err = IE_ERROR;
5288 goto leave;
5290 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
5291 xpath_ctx);
5292 if (!result) {
5293 err = IE_ERROR;
5294 goto leave;
5296 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5297 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
5298 err = IE_ISDS;
5299 goto leave;
5301 if (result->nodesetval->nodeNr > 1) {
5302 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
5303 err = IE_ISDS;
5304 goto leave;
5306 xpath_ctx->node = result->nodesetval->nodeTab[0];
5307 xmlXPathFreeObject(result); result = NULL;
5309 EXTRACT_LONGINT("isds:dbState", box_status, 1);
5312 leave:
5313 free(string);
5314 xmlXPathFreeObject(result);
5315 xmlXPathFreeContext(xpath_ctx);
5317 free(code);
5318 free(message);
5319 xmlFreeDoc(response);
5321 if (!err)
5322 isds_log(ILF_ISDS, ILL_DEBUG,
5323 _("CheckDataBox request processed by server successfully.\n"));
5325 return err;
5329 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
5330 * code, destroy response and log success.
5331 * @context is ISDS session context.
5332 * @service_name is name of SERVICE_DB_MANIPULATION service
5333 * @box_id is UTF-8 encoded box identifier as zero terminated string
5334 * @approval is optional external approval of box manipulation
5335 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5336 * NULL, if you don't care. */
5337 static isds_error build_send_manipulationdbid_request_check_drop_response(
5338 struct isds_ctx *context, const xmlChar *service_name,
5339 const xmlChar *box_id, const struct isds_approval *approval,
5340 xmlChar **refnumber) {
5341 isds_error err = IE_SUCCESS;
5342 xmlDocPtr response = NULL;
5344 if (!context) return IE_INVALID_CONTEXT;
5345 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
5347 /* Check if connection is established */
5348 if (!context->curl) return IE_CONNECTION_CLOSED;
5350 /* Do request and check for success */
5351 err = build_send_dbid_request_check_response(context,
5352 SERVICE_DB_MANIPULATION, service_name, box_id, approval,
5353 &response, refnumber);
5354 xmlFreeDoc(response);
5356 if (!err) {
5357 char *service_name_locale = utf82locale((char *) service_name);
5358 isds_log(ILF_ISDS, ILL_DEBUG,
5359 _("%s request processed by server successfully.\n"),
5360 service_name_locale);
5361 free(service_name_locale);
5364 return err;
5368 /* Switch box into state where box can receive commercial messages (off by
5369 * default)
5370 * @context is ISDS session context.
5371 * @box_id is UTF-8 encoded box identifier as zero terminated string
5372 * @allow is true for enable, false for disable commercial messages income
5373 * @approval is optional external approval of box manipulation
5374 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5375 * NULL, if you don't care. */
5376 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
5377 const char *box_id, const _Bool allow,
5378 const struct isds_approval *approval, char **refnumber) {
5379 return build_send_manipulationdbid_request_check_drop_response(context,
5380 (allow) ? BAD_CAST "SetOpenAddressing" :
5381 BAD_CAST "ClearOpenAddressing",
5382 BAD_CAST box_id, approval, (xmlChar **) refnumber);
5386 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
5387 * message acceptance). This is just a box permission. Sender must apply
5388 * such role by sending each message.
5389 * @context is ISDS session context.
5390 * @box_id is UTF-8 encoded box identifier as zero terminated string
5391 * @allow is true for enable, false for disable OVM role permission
5392 * @approval is optional external approval of box manipulation
5393 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5394 * NULL, if you don't care. */
5395 isds_error isds_switch_effective_ovm(struct isds_ctx *context,
5396 const char *box_id, const _Bool allow,
5397 const struct isds_approval *approval, char **refnumber) {
5398 return build_send_manipulationdbid_request_check_drop_response(context,
5399 (allow) ? BAD_CAST "SetEffectiveOVM" :
5400 BAD_CAST "ClearEffectiveOVM",
5401 BAD_CAST box_id, approval, (xmlChar **) refnumber);
5405 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
5406 * code, destroy response and log success.
5407 * @context is ISDS session context.
5408 * @service_name is name of SERVICE_DB_MANIPULATION service
5409 * @owner is structure describing box
5410 * @approval is optional external approval of box manipulation
5411 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5412 * NULL, if you don't care. */
5413 static isds_error build_send_manipulationdbowner_request_check_drop_response(
5414 struct isds_ctx *context, const xmlChar *service_name,
5415 const struct isds_DbOwnerInfo *owner,
5416 const struct isds_approval *approval, xmlChar **refnumber) {
5417 isds_error err = IE_SUCCESS;
5418 char *service_name_locale = NULL;
5419 xmlNodePtr request = NULL, db_owner_info;
5420 xmlNsPtr isds_ns = NULL;
5423 if (!context) return IE_INVALID_CONTEXT;
5424 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
5426 service_name_locale = utf82locale((char*)service_name);
5427 if (!service_name_locale) {
5428 err = IE_NOMEM;
5429 goto leave;
5432 /* Build request */
5433 request = xmlNewNode(NULL, service_name);
5434 if (!request) {
5435 isds_printf_message(context,
5436 _("Could not build %s request"), service_name_locale);
5437 err = IE_ERROR;
5438 goto leave;
5440 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5441 if(!isds_ns) {
5442 isds_log_message(context, _("Could not create ISDS name space"));
5443 err = IE_ERROR;
5444 goto leave;
5446 xmlSetNs(request, isds_ns);
5449 /* Add XSD:tOwnerInfoInput child*/
5450 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
5451 err = insert_DbOwnerInfo(context, owner, db_owner_info);
5452 if (err) goto leave;
5454 /* Add XSD:gExtApproval*/
5455 err = insert_GExtApproval(context, approval, request);
5456 if (err) goto leave;
5458 /* Send it to server and process response */
5459 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
5460 service_name, &request, refnumber);
5462 leave:
5463 xmlFreeNode(request);
5464 free(service_name_locale);
5466 return err;
5470 /* Switch box accessibility state on request of box owner.
5471 * Despite the name, owner must do the request off-line. This function is
5472 * designed for such off-line meeting points (e.g. Czech POINT).
5473 * @context is ISDS session context.
5474 * @box identifies box to swith accesibilty state.
5475 * @allow is true for making accesibale, false to disallow access.
5476 * @approval is optional external approval of box manipulation
5477 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5478 * NULL, if you don't care. */
5479 isds_error isds_switch_box_accessibility_on_owner_request(
5480 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
5481 const _Bool allow, const struct isds_approval *approval,
5482 char **refnumber) {
5483 return build_send_manipulationdbowner_request_check_drop_response(context,
5484 (allow) ? BAD_CAST "EnableOwnDataBox" :
5485 BAD_CAST "DisableOwnDataBox",
5486 box, approval, (xmlChar **) refnumber);
5490 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
5491 * date.
5492 * @context is ISDS session context.
5493 * @box identifies box to swith accesibilty state.
5494 * @since is date since accesseibility has been denied. This can be past too.
5495 * Only tm_year, tm_mon and tm_mday carry sane value.
5496 * @approval is optional external approval of box manipulation
5497 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5498 * NULL, if you don't care. */
5499 isds_error isds_disable_box_accessibility_externaly(
5500 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
5501 const struct tm *since, const struct isds_approval *approval,
5502 char **refnumber) {
5503 isds_error err = IE_SUCCESS;
5504 char *service_name_locale = NULL;
5505 xmlNodePtr request = NULL, node;
5506 xmlNsPtr isds_ns = NULL;
5507 xmlChar *string = NULL;
5510 if (!context) return IE_INVALID_CONTEXT;
5511 if (!box || !since) return IE_INVAL;
5513 /* Build request */
5514 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
5515 if (!request) {
5516 isds_printf_message(context,
5517 _("Could not build %s request"), "DisableDataBoxExternally");
5518 err = IE_ERROR;
5519 goto leave;
5521 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5522 if(!isds_ns) {
5523 isds_log_message(context, _("Could not create ISDS name space"));
5524 err = IE_ERROR;
5525 goto leave;
5527 xmlSetNs(request, isds_ns);
5530 /* Add @box identification */
5531 INSERT_ELEMENT(node, request, "dbOwnerInfo");
5532 err = insert_DbOwnerInfo(context, box, node);
5533 if (err) goto leave;
5535 /* Add @since date */
5536 err = tm2datestring(since, &string);
5537 if(err) {
5538 isds_log_message(context,
5539 _("Could not convert `since' argument to ISO date string"));
5540 goto leave;
5542 INSERT_STRING(request, "dbOwnerDisableDate", string);
5543 zfree(string);
5545 /* Add @approval */
5546 err = insert_GExtApproval(context, approval, request);
5547 if (err) goto leave;
5549 /* Send it to server and process response */
5550 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
5551 BAD_CAST "DisableDataBoxExternally", &request,
5552 (xmlChar **) refnumber);
5554 leave:
5555 free(string);
5556 xmlFreeNode(request);
5557 free(service_name_locale);
5559 return err;
5563 /* Insert struct isds_message data (envelope (recipient data optional) and
5564 * documents) into XML tree
5565 * @context is sesstion context
5566 * @outgoing_message is libsids structure with message data
5567 * @create_message is XML CreateMessage or CreateMultipleMessage element
5568 * @process_recipient true for recipient data serialization, false for no
5569 * serialization */
5570 static isds_error insert_envelope_files(struct isds_ctx *context,
5571 const struct isds_message *outgoing_message, xmlNodePtr create_message,
5572 const _Bool process_recipient) {
5574 isds_error err = IE_SUCCESS;
5575 xmlNodePtr envelope, dm_files, node;
5576 xmlChar *string = NULL;
5578 if (!context) return IE_INVALID_CONTEXT;
5579 if (!outgoing_message || !create_message) return IE_INVAL;
5582 /* Build envelope */
5583 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
5584 if (!envelope) {
5585 isds_printf_message(context, _("Could not add dmEnvelope child to "
5586 "%s element"), create_message->name);
5587 return IE_ERROR;
5590 if (!outgoing_message->envelope) {
5591 isds_log_message(context, _("Outgoing message is missing envelope"));
5592 err = IE_INVAL;
5593 goto leave;
5596 INSERT_STRING(envelope, "dmSenderOrgUnit",
5597 outgoing_message->envelope->dmSenderOrgUnit);
5598 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
5599 outgoing_message->envelope->dmSenderOrgUnitNum, string);
5601 if (process_recipient) {
5602 if (!outgoing_message->envelope->dbIDRecipient) {
5603 isds_log_message(context,
5604 _("Outgoing message is missing recipient box identifier"));
5605 err = IE_INVAL;
5606 goto leave;
5608 INSERT_STRING(envelope, "dbIDRecipient",
5609 outgoing_message->envelope->dbIDRecipient);
5611 INSERT_STRING(envelope, "dmRecipientOrgUnit",
5612 outgoing_message->envelope->dmRecipientOrgUnit);
5613 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
5614 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
5615 INSERT_STRING(envelope, "dmToHands",
5616 outgoing_message->envelope->dmToHands);
5619 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
5620 "dmAnnotation");
5621 INSERT_STRING(envelope, "dmAnnotation",
5622 outgoing_message->envelope->dmAnnotation);
5624 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
5625 0, 50, "dmRecipientRefNumber");
5626 INSERT_STRING(envelope, "dmRecipientRefNumber",
5627 outgoing_message->envelope->dmRecipientRefNumber);
5629 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
5630 0, 50, "dmSenderRefNumber");
5631 INSERT_STRING(envelope, "dmSenderRefNumber",
5632 outgoing_message->envelope->dmSenderRefNumber);
5634 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
5635 0, 50, "dmRecipientIdent");
5636 INSERT_STRING(envelope, "dmRecipientIdent",
5637 outgoing_message->envelope->dmRecipientIdent);
5639 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
5640 0, 50, "dmSenderIdent");
5641 INSERT_STRING(envelope, "dmSenderIdent",
5642 outgoing_message->envelope->dmSenderIdent);
5644 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
5645 outgoing_message->envelope->dmLegalTitleLaw, string);
5646 INSERT_LONGINT(envelope, "dmLegalTitleYear",
5647 outgoing_message->envelope->dmLegalTitleYear, string);
5648 INSERT_STRING(envelope, "dmLegalTitleSect",
5649 outgoing_message->envelope->dmLegalTitleSect);
5650 INSERT_STRING(envelope, "dmLegalTitlePar",
5651 outgoing_message->envelope->dmLegalTitlePar);
5652 INSERT_STRING(envelope, "dmLegalTitlePoint",
5653 outgoing_message->envelope->dmLegalTitlePoint);
5655 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
5656 outgoing_message->envelope->dmPersonalDelivery);
5657 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
5658 outgoing_message->envelope->dmAllowSubstDelivery);
5660 /* ???: Should we require value for dbEffectiveOVM sender?
5661 * ISDS has default as true */
5662 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
5665 /* Append dmFiles */
5666 if (!outgoing_message->documents) {
5667 isds_log_message(context,
5668 _("Outgoing message is missing list of documents"));
5669 err = IE_INVAL;
5670 goto leave;
5672 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
5673 if (!dm_files) {
5674 isds_printf_message(context, _("Could not add dmFiles child to "
5675 "%s element"), create_message->name);
5676 err = IE_ERROR;
5677 goto leave;
5680 /* Check for document hieararchy */
5681 err = check_documents_hierarchy(context, outgoing_message->documents);
5682 if (err) goto leave;
5684 /* Process each document */
5685 for (struct isds_list *item =
5686 (struct isds_list *) outgoing_message->documents;
5687 item; item = item->next) {
5688 if (!item->data) {
5689 isds_log_message(context,
5690 _("List of documents contains empty item"));
5691 err = IE_INVAL;
5692 goto leave;
5694 /* FIXME: Check for dmFileMetaType and for document references.
5695 * Only first document can be of MAIN type */
5696 err = insert_document(context, (struct isds_document*) item->data,
5697 dm_files);
5699 if (err) goto leave;
5702 leave:
5703 free(string);
5704 return err;
5708 /* Send a message via ISDS to a recipent
5709 * @context is session context
5710 * @outgoing_message is message to send; Some memebers are mandatory (like
5711 * dbIDRecipient), some are optional and some are irrelevant (especialy data
5712 * about sender). Included pointer to isds_list documents must contain at
5713 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
5714 * members will be filled with valid data from ISDS. Exact list of write
5715 * members is subject to change. Currently dmId is changed.
5716 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
5717 isds_error isds_send_message(struct isds_ctx *context,
5718 struct isds_message *outgoing_message) {
5720 isds_error err = IE_SUCCESS;
5721 xmlNsPtr isds_ns = NULL;
5722 xmlNodePtr request = NULL;
5723 xmlDocPtr response = NULL;
5724 xmlChar *code = NULL, *message = NULL;
5725 xmlXPathContextPtr xpath_ctx = NULL;
5726 xmlXPathObjectPtr result = NULL;
5727 _Bool message_is_complete = 0;
5729 if (!context) return IE_INVALID_CONTEXT;
5730 if (!outgoing_message) return IE_INVAL;
5732 /* Check if connection is established
5733 * TODO: This check should be done donwstairs. */
5734 if (!context->curl) return IE_CONNECTION_CLOSED;
5737 /* Build CreateMessage request */
5738 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
5739 if (!request) {
5740 isds_log_message(context,
5741 _("Could build CreateMessage request"));
5742 return IE_ERROR;
5744 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5745 if(!isds_ns) {
5746 isds_log_message(context, _("Could not create ISDS name space"));
5747 xmlFreeNode(request);
5748 return IE_ERROR;
5750 xmlSetNs(request, isds_ns);
5752 /* Append envelope and files */
5753 err = insert_envelope_files(context, outgoing_message, request, 1);
5754 if (err) goto leave;
5757 /* Signal we can serilize message since now */
5758 message_is_complete = 1;
5761 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
5763 /* Sent request */
5764 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
5766 /* Dont' destroy request, we want to provide it to application later */
5768 if (err) {
5769 isds_log(ILF_ISDS, ILL_DEBUG,
5770 _("Processing ISDS response on CreateMessage "
5771 "request failed\n"));
5772 goto leave;
5775 /* Check for response status */
5776 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
5777 &code, &message, NULL);
5778 if (err) {
5779 isds_log(ILF_ISDS, ILL_DEBUG,
5780 _("ISDS response on CreateMessage request "
5781 "is missing status\n"));
5782 goto leave;
5785 /* Request processed, but refused by server or server failed */
5786 if (xmlStrcmp(code, BAD_CAST "0000")) {
5787 char *box_id_locale =
5788 utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
5789 char *code_locale = utf82locale((char*)code);
5790 char *message_locale = utf82locale((char*)message);
5791 isds_log(ILF_ISDS, ILL_DEBUG,
5792 _("Server did not accept message for %s on CreateMessage "
5793 "request (code=%s, message=%s)\n"),
5794 box_id_locale, code_locale, message_locale);
5795 isds_log_message(context, message_locale);
5796 free(box_id_locale);
5797 free(code_locale);
5798 free(message_locale);
5799 err = IE_ISDS;
5800 goto leave;
5804 /* Extract data */
5805 xpath_ctx = xmlXPathNewContext(response);
5806 if (!xpath_ctx) {
5807 err = IE_ERROR;
5808 goto leave;
5810 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5811 err = IE_ERROR;
5812 goto leave;
5814 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
5815 xpath_ctx);
5816 if (!result) {
5817 err = IE_ERROR;
5818 goto leave;
5820 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5821 isds_log_message(context, _("Missing CreateMessageResponse element"));
5822 err = IE_ISDS;
5823 goto leave;
5825 if (result->nodesetval->nodeNr > 1) {
5826 isds_log_message(context, _("Multiple CreateMessageResponse element"));
5827 err = IE_ISDS;
5828 goto leave;
5830 xpath_ctx->node = result->nodesetval->nodeTab[0];
5831 xmlXPathFreeObject(result); result = NULL;
5833 if (outgoing_message->envelope->dmID) {
5834 free(outgoing_message->envelope->dmID);
5835 outgoing_message->envelope->dmID = NULL;
5837 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
5838 if (!outgoing_message->envelope->dmID) {
5839 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
5840 "but did not returen assigned message ID\n"));
5843 leave:
5844 /* TODO: Serialize message into structure member raw */
5845 /* XXX: Each web service transport message in different format.
5846 * Therefore it's not possible to save them directly.
5847 * To save them, one must figure out common format.
5848 * We can leave it on application, or we can implement the ESS format. */
5849 /*if (message_is_complete) {
5850 if (outgoing_message->envelope->dmID) {
5852 /* Add assigned message ID as first child*/
5853 /*xmlNodePtr dmid_text = xmlNewText(
5854 (xmlChar *) outgoing_message->envelope->dmID);
5855 if (!dmid_text) goto serialization_failed;
5857 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
5858 BAD_CAST "dmID");
5859 if (!dmid_element) {
5860 xmlFreeNode(dmid_text);
5861 goto serialization_failed;
5864 xmlNodePtr dmid_element_with_text =
5865 xmlAddChild(dmid_element, dmid_text);
5866 if (!dmid_element_with_text) {
5867 xmlFreeNode(dmid_element);
5868 xmlFreeNode(dmid_text);
5869 goto serialization_failed;
5872 node = xmlAddPrevSibling(envelope->childern,
5873 dmid_element_with_text);
5874 if (!node) {
5875 xmlFreeNodeList(dmid_element_with_text);
5876 goto serialization_failed;
5880 /* Serialize message with ID into raw */
5881 /*buffer = serialize_element(envelope)*/
5882 /* }
5884 serialization_failed:
5888 /* Clean up */
5889 xmlXPathFreeObject(result);
5890 xmlXPathFreeContext(xpath_ctx);
5892 free(code);
5893 free(message);
5894 xmlFreeDoc(response);
5895 xmlFreeNode(request);
5897 if (!err)
5898 isds_log(ILF_ISDS, ILL_DEBUG,
5899 _("CreateMessage request processed by server "
5900 "successfully.\n"));
5902 return err;
5906 /* Send a message via ISDS to a multiple recipents
5907 * @context is session context
5908 * @outgoing_message is message to send; Some memebers are mandatory,
5909 * some are optional and some are irrelevant (especialy data
5910 * about sender). Data about recipient will be substituted by ISDS from
5911 * @copies. Included pointer to isds_list documents must
5912 * contain at least one document of FILEMETATYPE_MAIN.
5913 * @copies is list of isds_message_copy structures addressing all desired
5914 * recipients. This is read-write structure, some members will be filled with
5915 * valid data from ISDS (message IDs, error codes, error descriptions).
5916 * @return
5917 * ISDS_SUCCESS if all messages have been sent
5918 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
5919 * succesed messages can be identified by copies->data->error),
5920 * or other error code if something other goes wrong. */
5921 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
5922 const struct isds_message *outgoing_message,
5923 struct isds_list *copies) {
5925 isds_error err = IE_SUCCESS, append_err;
5926 xmlNsPtr isds_ns = NULL;
5927 xmlNodePtr request = NULL, recipients, recipient, node;
5928 struct isds_list *item;
5929 struct isds_message_copy *copy;
5930 xmlDocPtr response = NULL;
5931 xmlChar *code = NULL, *message = NULL;
5932 xmlXPathContextPtr xpath_ctx = NULL;
5933 xmlXPathObjectPtr result = NULL;
5934 xmlChar *string = NULL;
5935 int i;
5937 if (!context) return IE_INVALID_CONTEXT;
5938 if (!outgoing_message || !copies) return IE_INVAL;
5940 /* Check if connection is established
5941 * TODO: This check should be done donwstairs. */
5942 if (!context->curl) return IE_CONNECTION_CLOSED;
5945 /* Build CreateMultipleMessage request */
5946 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
5947 if (!request) {
5948 isds_log_message(context,
5949 _("Could build CreateMultipleMessage request"));
5950 return IE_ERROR;
5952 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5953 if(!isds_ns) {
5954 isds_log_message(context, _("Could not create ISDS name space"));
5955 xmlFreeNode(request);
5956 return IE_ERROR;
5958 xmlSetNs(request, isds_ns);
5961 /* Build recipients */
5962 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
5963 if (!recipients) {
5964 isds_log_message(context, _("Could not add dmRecipients child to "
5965 "CreateMultipleMessage element"));
5966 xmlFreeNode(request);
5967 return IE_ERROR;
5970 /* Insert each recipient */
5971 for (item = copies; item; item = item->next) {
5972 copy = (struct isds_message_copy *) item->data;
5973 if (!copy) {
5974 isds_log_message(context,
5975 _("copies list item contains empty data"));
5976 err = IE_INVAL;
5977 goto leave;
5980 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
5981 if (!recipient) {
5982 isds_log_message(context, _("Could not add dmRecipient child to "
5983 "dmRecipient element"));
5984 err = IE_ERROR;
5985 goto leave;
5988 if (!copy->dbIDRecipient) {
5989 isds_log_message(context,
5990 _("Message copy is missing recipient box identifier"));
5991 err = IE_INVAL;
5992 goto leave;
5994 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
5995 INSERT_STRING(recipient, "dmRecipientOrgUnit",
5996 copy->dmRecipientOrgUnit);
5997 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
5998 copy->dmRecipientOrgUnitNum, string);
5999 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
6002 /* Append envelope and files */
6003 err = insert_envelope_files(context, outgoing_message, request, 0);
6004 if (err) goto leave;
6007 isds_log(ILF_ISDS, ILL_DEBUG,
6008 _("Sending CreateMultipleMessage request to ISDS\n"));
6010 /* Sent request */
6011 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
6012 if (err) {
6013 isds_log(ILF_ISDS, ILL_DEBUG,
6014 _("Processing ISDS response on CreateMultipleMessage "
6015 "request failed\n"));
6016 goto leave;
6019 /* Check for response status */
6020 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
6021 &code, &message, NULL);
6022 if (err) {
6023 isds_log(ILF_ISDS, ILL_DEBUG,
6024 _("ISDS response on CreateMultipleMessage request "
6025 "is missing status\n"));
6026 goto leave;
6029 /* Request processed, but some copies failed */
6030 if (!xmlStrcmp(code, BAD_CAST "0004")) {
6031 char *box_id_locale =
6032 utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
6033 char *code_locale = utf82locale((char*)code);
6034 char *message_locale = utf82locale((char*)message);
6035 isds_log(ILF_ISDS, ILL_DEBUG,
6036 _("Server did accept message for multiple recipients "
6037 "on CreateMultipleMessage request but delivery to "
6038 "some of them failed (code=%s, message=%s)\n"),
6039 box_id_locale, code_locale, message_locale);
6040 isds_log_message(context, message_locale);
6041 free(box_id_locale);
6042 free(code_locale);
6043 free(message_locale);
6044 err = IE_PARTIAL_SUCCESS;
6047 /* Request refused by server as whole */
6048 else if (xmlStrcmp(code, BAD_CAST "0000")) {
6049 char *box_id_locale =
6050 utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
6051 char *code_locale = utf82locale((char*)code);
6052 char *message_locale = utf82locale((char*)message);
6053 isds_log(ILF_ISDS, ILL_DEBUG,
6054 _("Server did not accept message for multiple recipients "
6055 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
6056 box_id_locale, code_locale, message_locale);
6057 isds_log_message(context, message_locale);
6058 free(box_id_locale);
6059 free(code_locale);
6060 free(message_locale);
6061 err = IE_ISDS;
6062 goto leave;
6066 /* Extract data */
6067 xpath_ctx = xmlXPathNewContext(response);
6068 if (!xpath_ctx) {
6069 err = IE_ERROR;
6070 goto leave;
6072 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6073 err = IE_ERROR;
6074 goto leave;
6076 result = xmlXPathEvalExpression(
6077 BAD_CAST "/isds:CreateMultipleMessageResponse"
6078 "/isds:dmMultipleStatus/isds:dmSingleStatus",
6079 xpath_ctx);
6080 if (!result) {
6081 err = IE_ERROR;
6082 goto leave;
6084 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6085 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
6086 err = IE_ISDS;
6087 goto leave;
6090 /* Extract message ID and delivery status for each copy */
6091 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
6092 item = item->next, i++) {
6093 copy = (struct isds_message_copy *) item->data;
6094 xpath_ctx->node = result->nodesetval->nodeTab[i];
6096 append_err = append_TMStatus(context, copy, xpath_ctx);
6097 if (append_err) {
6098 err = append_err;
6099 goto leave;
6102 if (item || i < result->nodesetval->nodeNr) {
6103 isds_printf_message(context, _("ISDS returned unexpected number of "
6104 "message copy delivery states: %d"),
6105 result->nodesetval->nodeNr);
6106 err = IE_ISDS;
6107 goto leave;
6111 leave:
6112 /* Clean up */
6113 free(string);
6114 xmlXPathFreeObject(result);
6115 xmlXPathFreeContext(xpath_ctx);
6117 free(code);
6118 free(message);
6119 xmlFreeDoc(response);
6120 xmlFreeNode(request);
6122 if (!err)
6123 isds_log(ILF_ISDS, ILL_DEBUG,
6124 _("CreateMultipleMessageResponse request processed by server "
6125 "successfully.\n"));
6127 return err;
6131 /* Get list of messages. This is common core for getting sent or received
6132 * messaeges.
6133 * Any criterion argument can be NULL, if you don't care about it.
6134 * @context is session context. Must not be NULL.
6135 * @outgoing_direction is true if you want list of outgoing messages,
6136 * it's false if you want incoming messages.
6137 * @from_time is minimal time and date of message sending inclusive.
6138 * @to_time is maximal time and date of message sending inclusive
6139 * @organization_unit_number is number of sender/recipient respectively.
6140 * @status_filter is bit field of isds_message_status values. Use special
6141 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
6142 * all values, you can use bitwise arithmetic if you want.)
6143 * @offset is index of first message we are interested in. First message is 1.
6144 * Set to 0 (or 1) if you don't care.
6145 * @number is maximal length of list you want to get as input value, outputs
6146 * number of messages matching these criteria. Can be NULL if you don't care
6147 * (applies to output value either).
6148 * @messages is automatically reallocated list of isds_message's. Be ware that
6149 * it returns only brief overview (envelope and some other fields) about each
6150 * message, not the complete message. FIXME: Specify exact fields.
6151 * The list is sorted by delivery time in ascending order.
6152 * Use NULL if
6153 * you don't care about don't need the data (useful if you want to know only
6154 * the @number). If you provide &NULL, list will be allocated on heap, if you
6155 * provide pointer to non-NULL, list will be freed automacally at first. Also
6156 * in case of error the list will be NULLed.
6157 * @return IE_SUCCESS or appropriate error code. */
6158 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
6159 _Bool outgoing_direction,
6160 const struct timeval *from_time, const struct timeval *to_time,
6161 const long int *organization_unit_number,
6162 const unsigned int status_filter,
6163 const unsigned long int offset, unsigned long int *number,
6164 struct isds_list **messages) {
6166 isds_error err = IE_SUCCESS;
6167 xmlNsPtr isds_ns = NULL;
6168 xmlNodePtr request = NULL, node;
6169 xmlDocPtr response = NULL;
6170 xmlChar *code = NULL, *message = NULL;
6171 xmlXPathContextPtr xpath_ctx = NULL;
6172 xmlXPathObjectPtr result = NULL;
6173 xmlChar *string = NULL;
6174 long unsigned int count = 0;
6176 if (!context) return IE_INVALID_CONTEXT;
6178 /* Free former message list if any */
6179 if (messages) isds_list_free(messages);
6181 /* Check if connection is established
6182 * TODO: This check should be done donwstairs. */
6183 if (!context->curl) return IE_CONNECTION_CLOSED;
6185 /* Build GetListOf*Messages request */
6186 request = xmlNewNode(NULL,
6187 (outgoing_direction) ?
6188 BAD_CAST "GetListOfSentMessages" :
6189 BAD_CAST "GetListOfReceivedMessages"
6191 if (!request) {
6192 isds_log_message(context,
6193 (outgoing_direction) ?
6194 _("Could not build GetListOfSentMessages request") :
6195 _("Could not build GetListOfReceivedMessages request")
6197 return IE_ERROR;
6199 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6200 if(!isds_ns) {
6201 isds_log_message(context, _("Could not create ISDS name space"));
6202 xmlFreeNode(request);
6203 return IE_ERROR;
6205 xmlSetNs(request, isds_ns);
6208 if (from_time) {
6209 err = timeval2timestring(from_time, &string);
6210 if (err) goto leave;
6212 INSERT_STRING(request, "dmFromTime", string);
6213 free(string); string = NULL;
6215 if (to_time) {
6216 err = timeval2timestring(to_time, &string);
6217 if (err) goto leave;
6219 INSERT_STRING(request, "dmToTime", string);
6220 free(string); string = NULL;
6222 if (outgoing_direction) {
6223 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
6224 organization_unit_number, string);
6225 } else {
6226 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
6227 organization_unit_number, string);
6230 if (status_filter > MESSAGESTATE_ANY) {
6231 isds_printf_message(context,
6232 _("Invalid message state filter value: %ld"), status_filter);
6233 err = IE_INVAL;
6234 goto leave;
6236 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
6238 if (offset > 0 ) {
6239 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
6240 } else {
6241 INSERT_STRING(request, "dmOffset", "1");
6244 /* number 0 means no limit */
6245 if (number && *number == 0) {
6246 INSERT_STRING(request, "dmLimit", NULL);
6247 } else {
6248 INSERT_ULONGINT(request, "dmLimit", number, string);
6252 isds_log(ILF_ISDS, ILL_DEBUG,
6253 (outgoing_direction) ?
6254 _("Sending GetListOfSentMessages request to ISDS\n") :
6255 _("Sending GetListOfReceivedMessages request to ISDS\n")
6258 /* Sent request */
6259 err = isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
6260 xmlFreeNode(request); request = NULL;
6262 if (err) {
6263 isds_log(ILF_ISDS, ILL_DEBUG,
6264 (outgoing_direction) ?
6265 _("Processing ISDS response on GetListOfSentMessages "
6266 "request failed\n") :
6267 _("Processing ISDS response on GetListOfReceivedMessages "
6268 "request failed\n")
6270 goto leave;
6273 /* Check for response status */
6274 err = isds_response_status(context, SERVICE_DM_INFO, response,
6275 &code, &message, NULL);
6276 if (err) {
6277 isds_log(ILF_ISDS, ILL_DEBUG,
6278 (outgoing_direction) ?
6279 _("ISDS response on GetListOfSentMessages request "
6280 "is missing status\n") :
6281 _("ISDS response on GetListOfReceivedMessages request "
6282 "is missing status\n")
6284 goto leave;
6287 /* Request processed, but nothing found */
6288 if (xmlStrcmp(code, BAD_CAST "0000")) {
6289 char *code_locale = utf82locale((char*)code);
6290 char *message_locale = utf82locale((char*)message);
6291 isds_log(ILF_ISDS, ILL_DEBUG,
6292 (outgoing_direction) ?
6293 _("Server refused GetListOfSentMessages request "
6294 "(code=%s, message=%s)\n") :
6295 _("Server refused GetListOfReceivedMessages request "
6296 "(code=%s, message=%s)\n"),
6297 code_locale, message_locale);
6298 isds_log_message(context, message_locale);
6299 free(code_locale);
6300 free(message_locale);
6301 err = IE_ISDS;
6302 goto leave;
6306 /* Extract data */
6307 xpath_ctx = xmlXPathNewContext(response);
6308 if (!xpath_ctx) {
6309 err = IE_ERROR;
6310 goto leave;
6312 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6313 err = IE_ERROR;
6314 goto leave;
6316 result = xmlXPathEvalExpression(
6317 (outgoing_direction) ?
6318 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
6319 "isds:dmRecords/isds:dmRecord" :
6320 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
6321 "isds:dmRecords/isds:dmRecord",
6322 xpath_ctx);
6323 if (!result) {
6324 err = IE_ERROR;
6325 goto leave;
6328 /* Fill output arguments in */
6329 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6330 struct isds_envelope *envelope;
6331 struct isds_list *item = NULL, *last_item = NULL;
6333 for (count = 0; count < result->nodesetval->nodeNr; count++) {
6334 /* Create new message */
6335 item = calloc(1, sizeof(*item));
6336 if (!item) {
6337 err = IE_NOMEM;
6338 goto leave;
6340 item->destructor = (void(*)(void**)) &isds_message_free;
6341 item->data = calloc(1, sizeof(struct isds_message));
6342 if (!item->data) {
6343 isds_list_free(&item);
6344 err = IE_NOMEM;
6345 goto leave;
6348 /* Extract envelope data */
6349 xpath_ctx->node = result->nodesetval->nodeTab[count];
6350 envelope = NULL;
6351 err = extract_DmRecord(context, &envelope, xpath_ctx);
6352 if (err) {
6353 isds_list_free(&item);
6354 goto leave;
6357 /* Attach extracted envelope */
6358 ((struct isds_message *) item->data)->envelope = envelope;
6360 /* Append new message into the list */
6361 if (!*messages) {
6362 *messages = last_item = item;
6363 } else {
6364 last_item->next = item;
6365 last_item = item;
6369 if (number) *number = count;
6371 leave:
6372 if (err) {
6373 isds_list_free(messages);
6376 free(string);
6377 xmlXPathFreeObject(result);
6378 xmlXPathFreeContext(xpath_ctx);
6380 free(code);
6381 free(message);
6382 xmlFreeDoc(response);
6383 xmlFreeNode(request);
6385 if (!err)
6386 isds_log(ILF_ISDS, ILL_DEBUG,
6387 (outgoing_direction) ?
6388 _("GetListOfSentMessages request processed by server "
6389 "successfully.\n") :
6390 _("GetListOfReceivedMessages request processed by server "
6391 "successfully.\n")
6393 return err;
6397 /* Get list of outgoing (already sent) messages.
6398 * Any criterion argument can be NULL, if you don't care about it.
6399 * @context is session context. Must not be NULL.
6400 * @from_time is minimal time and date of message sending inclusive.
6401 * @to_time is maximal time and date of message sending inclusive
6402 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
6403 * @status_filter is bit field of isds_message_status values. Use special
6404 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
6405 * all values, you can use bitwise arithmetic if you want.)
6406 * @offset is index of first message we are interested in. First message is 1.
6407 * Set to 0 (or 1) if you don't care.
6408 * @number is maximal length of list you want to get as input value, outputs
6409 * number of messages matching these criteria. Can be NULL if you don't care
6410 * (applies to output value either).
6411 * @messages is automatically reallocated list of isds_message's. Be ware that
6412 * it returns only brief overview (envelope and some other fields) about each
6413 * message, not the complete message. FIXME: Specify exact fields.
6414 * The list is sorted by delivery time in ascending order.
6415 * Use NULL if you don't care about the metadata (useful if you want to know
6416 * only the @number). If you provide &NULL, list will be allocated on heap,
6417 * if you provide pointer to non-NULL, list will be freed automacally at first.
6418 * Also in case of error the list will be NULLed.
6419 * @return IE_SUCCESS or appropriate error code. */
6420 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
6421 const struct timeval *from_time, const struct timeval *to_time,
6422 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
6423 const unsigned long int offset, unsigned long int *number,
6424 struct isds_list **messages) {
6426 return isds_get_list_of_messages(
6427 context, 1,
6428 from_time, to_time, dmSenderOrgUnitNum, status_filter,
6429 offset, number,
6430 messages);
6434 /* Get list of incoming (addressed to you) messages.
6435 * Any criterion argument can be NULL, if you don't care about it.
6436 * @context is session context. Must not be NULL.
6437 * @from_time is minimal time and date of message sending inclusive.
6438 * @to_time is maximal time and date of message sending inclusive
6439 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
6440 * @status_filter is bit field of isds_message_status values. Use special
6441 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
6442 * all values, you can use bitwise arithmetic if you want.)
6443 * @offset is index of first message we are interested in. First message is 1.
6444 * Set to 0 (or 1) if you don't care.
6445 * @number is maximal length of list you want to get as input value, outputs
6446 * number of messages matching these criteria. Can be NULL if you don't care
6447 * (applies to output value either).
6448 * @messages is automatically reallocated list of isds_message's. Be ware that
6449 * it returns only brief overview (envelope and some other fields) about each
6450 * message, not the complete message. FIXME: Specify exact fields.
6451 * Use NULL if you don't care about the metadata (useful if you want to know
6452 * only the @number). If you provide &NULL, list will be allocated on heap,
6453 * if you provide pointer to non-NULL, list will be freed automacally at first.
6454 * Also in case of error the list will be NULLed.
6455 * @return IE_SUCCESS or appropriate error code. */
6456 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
6457 const struct timeval *from_time, const struct timeval *to_time,
6458 const long int *dmRecipientOrgUnitNum,
6459 const unsigned int status_filter,
6460 const unsigned long int offset, unsigned long int *number,
6461 struct isds_list **messages) {
6463 return isds_get_list_of_messages(
6464 context, 0,
6465 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
6466 offset, number,
6467 messages);
6471 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
6472 * code
6473 * @context is session context
6474 * @service is ISDS WS service handler
6475 * @service_name is name of SERVICE_DM_OPERATIONS
6476 * @message_id is message ID to send as service argument to ISDS
6477 * @response is server SOAP body response as XML document
6478 * @raw_response is automatically reallocated bitstream with response body. Use
6479 * NULL if you don't care
6480 * @raw_response_length is size of @raw_response in bytes
6481 * @code is ISDS status code
6482 * @status_message is ISDS status message
6483 * @return error coded from lower layer, context message will be set up
6484 * appropriately. */
6485 static isds_error build_send_check_message_request(struct isds_ctx *context,
6486 const isds_service service, const xmlChar *service_name,
6487 const char *message_id,
6488 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
6489 xmlChar **code, xmlChar **status_message) {
6491 isds_error err = IE_SUCCESS;
6492 char *service_name_locale = NULL, *message_id_locale = NULL;
6493 xmlNodePtr request = NULL, node;
6494 xmlNsPtr isds_ns = NULL;
6496 if (!context) return IE_INVALID_CONTEXT;
6497 if (!service_name || !message_id) return IE_INVAL;
6498 if (!response || !code || !status_message) return IE_INVAL;
6499 if (!raw_response_length && raw_response) return IE_INVAL;
6501 /* Free output argument */
6502 xmlFreeDoc(*response); *response = NULL;
6503 if (raw_response) zfree(*raw_response);
6504 free(*code);
6505 free(*status_message);
6508 /* Check if connection is established
6509 * TODO: This check should be done donwstairs. */
6510 if (!context->curl) return IE_CONNECTION_CLOSED;
6512 service_name_locale = utf82locale((char*)service_name);
6513 message_id_locale = utf82locale(message_id);
6514 if (!service_name_locale || !message_id_locale) {
6515 err = IE_NOMEM;
6516 goto leave;
6519 /* Build request */
6520 request = xmlNewNode(NULL, service_name);
6521 if (!request) {
6522 isds_printf_message(context,
6523 _("Could not build %s request"), service_name_locale);
6524 err = IE_ERROR;
6525 goto leave;
6527 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6528 if(!isds_ns) {
6529 isds_log_message(context, _("Could not create ISDS name space"));
6530 err = IE_ERROR;
6531 goto leave;
6533 xmlSetNs(request, isds_ns);
6536 /* Add requested ID */
6537 err = validate_message_id_length(context, (xmlChar *) message_id);
6538 if (err) goto leave;
6539 INSERT_STRING(request, "dmID", message_id);
6542 isds_log(ILF_ISDS, ILL_DEBUG,
6543 _("Sending %s request for %s message ID to ISDS\n"),
6544 service_name_locale, message_id_locale);
6546 /* Send request */
6547 err = isds(context, service, request, response,
6548 raw_response, raw_response_length);
6549 xmlFreeNode(request); request = NULL;
6551 if (err) {
6552 isds_log(ILF_ISDS, ILL_DEBUG,
6553 _("Processing ISDS response on %s request failed\n"),
6554 service_name_locale);
6555 goto leave;
6558 /* Check for response status */
6559 err = isds_response_status(context, service, *response,
6560 code, status_message, NULL);
6561 if (err) {
6562 isds_log(ILF_ISDS, ILL_DEBUG,
6563 _("ISDS response on %s request is missing status\n"),
6564 service_name_locale);
6565 goto leave;
6568 /* Request processed, but nothing found */
6569 if (xmlStrcmp(*code, BAD_CAST "0000")) {
6570 char *code_locale = utf82locale((char*) *code);
6571 char *status_message_locale = utf82locale((char*) *status_message);
6572 isds_log(ILF_ISDS, ILL_DEBUG,
6573 _("Server refused %s request for %s message ID "
6574 "(code=%s, message=%s)\n"),
6575 service_name_locale, message_id_locale,
6576 code_locale, status_message_locale);
6577 isds_log_message(context, status_message_locale);
6578 free(code_locale);
6579 free(status_message_locale);
6580 err = IE_ISDS;
6581 goto leave;
6584 leave:
6585 free(message_id_locale);
6586 free(service_name_locale);
6587 xmlFreeNode(request);
6588 return err;
6592 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
6593 * signed data and free ISDS response.
6594 * @context is session context
6595 * @message_id is UTF-8 encoded message ID for loging purpose
6596 * @response is parsed XML document. It will be freed and NULLed in the middle
6597 * of function run to save memmory. This is not guaranted in case of error.
6598 * @request_name is name of ISDS request used to construct response root
6599 * element name and for logging purpose.
6600 * @raw is reallocated output buffer with DER encoded CMS data
6601 * @raw_length is size of @raw buffer in bytes
6602 * @returns standard error codes, in case of error, @raw will be freed and
6603 * NULLed, @response sometimes. */
6604 static isds_error find_extract_signed_data_free_response(
6605 struct isds_ctx *context, const xmlChar *message_id,
6606 xmlDocPtr *response, const xmlChar *request_name,
6607 void **raw, size_t *raw_length) {
6609 isds_error err = IE_SUCCESS;
6610 char *xpath_expression = NULL;
6611 xmlXPathContextPtr xpath_ctx = NULL;
6612 xmlXPathObjectPtr result = NULL;
6613 char *encoded_structure = NULL;
6615 if (!context) return IE_INVALID_CONTEXT;
6616 if (!raw) return IE_INVAL;
6617 zfree(*raw);
6618 if (!message_id || !response || !*response || !request_name || !raw_length)
6619 return IE_INVAL;
6621 /* Build XPath expression */
6622 xpath_expression = astrcat3("/isds:", (char *) request_name,
6623 "Response/isds:dmSignature");
6624 if (!xpath_expression) return IE_NOMEM;
6626 /* Extract data */
6627 xpath_ctx = xmlXPathNewContext(*response);
6628 if (!xpath_ctx) {
6629 err = IE_ERROR;
6630 goto leave;
6632 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6633 err = IE_ERROR;
6634 goto leave;
6636 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
6637 if (!result) {
6638 err = IE_ERROR;
6639 goto leave;
6641 /* Empty response */
6642 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6643 char *message_id_locale = utf82locale((char*) message_id);
6644 isds_printf_message(context,
6645 _("Server did not return any signed data for mesage ID `%s' "
6646 "on %s request"),
6647 message_id_locale, request_name);
6648 free(message_id_locale);
6649 err = IE_ISDS;
6650 goto leave;
6652 /* More reponses */
6653 if (result->nodesetval->nodeNr > 1) {
6654 char *message_id_locale = utf82locale((char*) message_id);
6655 isds_printf_message(context,
6656 _("Server did return more signed data for message ID `%s' "
6657 "on %s request"),
6658 message_id_locale, request_name);
6659 free(message_id_locale);
6660 err = IE_ISDS;
6661 goto leave;
6663 /* One response */
6664 xpath_ctx->node = result->nodesetval->nodeTab[0];
6666 /* Extract PKCS#7 structure */
6667 EXTRACT_STRING(".", encoded_structure);
6668 if (!encoded_structure) {
6669 isds_log_message(context, _("dmSignature element is empty"));
6672 /* Here we have delivery info as standalone CMS in encoded_structure.
6673 * We don't need any other data, free them: */
6674 xmlXPathFreeObject(result); result = NULL;
6675 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
6676 xmlFreeDoc(*response); *response = NULL;
6679 /* Decode PKCS#7 to DER format */
6680 *raw_length = b64decode(encoded_structure, raw);
6681 if (*raw_length == (size_t) -1) {
6682 isds_log_message(context,
6683 _("Error while Base64-decoding PKCS#7 structure"));
6684 err = IE_ERROR;
6685 goto leave;
6688 leave:
6689 if (err) {
6690 zfree(*raw);
6691 raw_length = 0;
6694 free(encoded_structure);
6695 xmlXPathFreeObject(result);
6696 xmlXPathFreeContext(xpath_ctx);
6697 free(xpath_expression);
6699 return err;
6703 /* Download incoming message envelope identified by ID.
6704 * @context is session context
6705 * @message_id is message identifier (you can get them from
6706 * isds_get_list_of_received_messages())
6707 * @message is automatically reallocated message retrieved from ISDS.
6708 * It will miss documents per se. Use isds_get_received_message(), if you are
6709 * interrested in documents (content) too.
6710 * Returned hash and timestamp require documents to be verifiable. */
6711 isds_error isds_get_received_envelope(struct isds_ctx *context,
6712 const char *message_id, struct isds_message **message) {
6714 isds_error err = IE_SUCCESS;
6715 xmlDocPtr response = NULL;
6716 xmlChar *code = NULL, *status_message = NULL;
6717 xmlXPathContextPtr xpath_ctx = NULL;
6718 xmlXPathObjectPtr result = NULL;
6720 if (!context) return IE_INVALID_CONTEXT;
6722 /* Free former message if any */
6723 if (!message) return IE_INVAL;
6724 isds_message_free(message);
6726 /* Do request and check for success */
6727 err = build_send_check_message_request(context, SERVICE_DM_INFO,
6728 BAD_CAST "MessageEnvelopeDownload", message_id,
6729 &response, NULL, NULL, &code, &status_message);
6730 if (err) goto leave;
6732 /* Extract data */
6733 xpath_ctx = xmlXPathNewContext(response);
6734 if (!xpath_ctx) {
6735 err = IE_ERROR;
6736 goto leave;
6738 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6739 err = IE_ERROR;
6740 goto leave;
6742 result = xmlXPathEvalExpression(
6743 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
6744 "isds:dmReturnedMessageEnvelope",
6745 xpath_ctx);
6746 if (!result) {
6747 err = IE_ERROR;
6748 goto leave;
6750 /* Empty response */
6751 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6752 char *message_id_locale = utf82locale((char*) message_id);
6753 isds_printf_message(context,
6754 _("Server did not return any envelope for ID `%s' "
6755 "on MessageEnvelopeDownload request"), message_id_locale);
6756 free(message_id_locale);
6757 err = IE_ISDS;
6758 goto leave;
6760 /* More envelops */
6761 if (result->nodesetval->nodeNr > 1) {
6762 char *message_id_locale = utf82locale((char*) message_id);
6763 isds_printf_message(context,
6764 _("Server did return more envelopes for ID `%s' "
6765 "on MessageEnvelopeDownload request"), message_id_locale);
6766 free(message_id_locale);
6767 err = IE_ISDS;
6768 goto leave;
6770 /* One message */
6771 xpath_ctx->node = result->nodesetval->nodeTab[0];
6773 /* Extract the envelope (= message without documents, hence 0) */
6774 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
6775 if (err) goto leave;
6777 /* Save XML blob */
6778 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
6779 &(*message)->raw_length);
6781 leave:
6782 if (err) {
6783 isds_message_free(message);
6786 xmlXPathFreeObject(result);
6787 xmlXPathFreeContext(xpath_ctx);
6789 free(code);
6790 free(status_message);
6791 xmlFreeDoc(response);
6793 if (!err)
6794 isds_log(ILF_ISDS, ILL_DEBUG,
6795 _("MessageEnvelopeDownload request processed by server "
6796 "successfully.\n")
6798 return err;
6802 /* Load delivery info of any format from buffer.
6803 * @context is session context
6804 * @raw_type advertises format of @buffer content. Only delivery info types
6805 * are accepted.
6806 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
6807 * retrieve such data from message->raw after calling
6808 * isds_get_signed_delivery_info().
6809 * @length is length of buffer in bytes.
6810 * @message is automatically reallocated message parsed from @buffer.
6811 * @strategy selects how buffer will be attached into raw isds_message member.
6812 * */
6813 isds_error isds_load_delivery_info(struct isds_ctx *context,
6814 const isds_raw_type raw_type,
6815 const void *buffer, const size_t length,
6816 struct isds_message **message, const isds_buffer_strategy strategy) {
6818 isds_error err = IE_SUCCESS;
6819 message_ns_type message_ns;
6820 xmlDocPtr message_doc = NULL;
6821 xmlXPathContextPtr xpath_ctx = NULL;
6822 xmlXPathObjectPtr result = NULL;
6823 void *xml_stream = NULL;
6824 size_t xml_stream_length = 0;
6826 if (!context) return IE_INVALID_CONTEXT;
6827 if (!message) return IE_INVAL;
6828 isds_message_free(message);
6829 if (!buffer) return IE_INVAL;
6832 /* Select buffer format and extract XML from CMS*/
6833 switch (raw_type) {
6834 case RAWTYPE_DELIVERYINFO:
6835 message_ns = MESSAGE_NS_UNSIGNED;
6836 xml_stream = (void *) buffer;
6837 xml_stream_length = length;
6838 break;
6840 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
6841 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
6842 xml_stream = (void *) buffer;
6843 xml_stream_length = length;
6844 break;
6846 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
6847 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
6848 err = extract_cms_data(context, buffer, length,
6849 &xml_stream, &xml_stream_length);
6850 if (err) goto leave;
6851 break;
6853 default:
6854 isds_log_message(context, _("Bad raw delivery representation type"));
6855 return IE_INVAL;
6856 break;
6859 isds_log(ILF_ISDS, ILL_DEBUG,
6860 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
6861 xml_stream_length, xml_stream);
6863 /* Convert delivery info XML stream into XPath context */
6864 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
6865 if (!message_doc) {
6866 err = IE_XML;
6867 goto leave;
6869 xpath_ctx = xmlXPathNewContext(message_doc);
6870 if (!xpath_ctx) {
6871 err = IE_ERROR;
6872 goto leave;
6874 /* XXX: Name spaces mangled for signed delivery info:
6875 * http://isds.czechpoint.cz/v20/delivery:
6877 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
6878 * <q:dmDelivery>
6879 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
6880 * <p:dmID>170272</p:dmID>
6881 * ...
6882 * </p:dmDm>
6883 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
6884 * ...
6885 * </q:dmEvents>...</q:dmEvents>
6886 * </q:dmDelivery>
6887 * </q:GetDeliveryInfoResponse>
6888 * */
6889 if (register_namespaces(xpath_ctx, message_ns)) {
6890 err = IE_ERROR;
6891 goto leave;
6893 result = xmlXPathEvalExpression(
6894 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
6895 xpath_ctx);
6896 if (!result) {
6897 err = IE_ERROR;
6898 goto leave;
6900 /* Empty delivery info */
6901 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6902 isds_printf_message(context,
6903 _("XML document ss not sisds:dmDelivery document"));
6904 err = IE_ISDS;
6905 goto leave;
6907 /* More delivery infos */
6908 if (result->nodesetval->nodeNr > 1) {
6909 isds_printf_message(context,
6910 _("XML document has more sisds:dmDelivery elements"));
6911 err = IE_ISDS;
6912 goto leave;
6914 /* One delivery info */
6915 xpath_ctx->node = result->nodesetval->nodeTab[0];
6917 /* Extract the envelope (= message without documents, hence 0).
6918 * XXX: extract_TReturnedMessage() can obtain attachments size,
6919 * but delivery info carries none. It's coded as option elements,
6920 * so it should work. */
6921 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
6922 if (err) goto leave;
6924 /* Extract events */
6925 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
6926 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
6927 if (err) { err = IE_ERROR; goto leave; }
6928 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
6929 if (err) goto leave;
6931 /* Append raw CMS structure into message */
6932 (*message)->raw_type = raw_type;
6933 switch (strategy) {
6934 case BUFFER_DONT_STORE:
6935 break;
6936 case BUFFER_COPY:
6937 (*message)->raw = malloc(length);
6938 if (!(*message)->raw) {
6939 err = IE_NOMEM;
6940 goto leave;
6942 memcpy((*message)->raw, buffer, length);
6943 (*message)->raw_length = length;
6944 break;
6945 case BUFFER_MOVE:
6946 (*message)->raw = (void *) buffer;
6947 (*message)->raw_length = length;
6948 break;
6949 default:
6950 err = IE_ENUM;
6951 goto leave;
6954 leave:
6955 if (err) {
6956 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
6957 isds_message_free(message);
6960 xmlXPathFreeObject(result);
6961 xmlXPathFreeContext(xpath_ctx);
6962 xmlFreeDoc(message_doc);
6963 if (xml_stream != buffer) cms_data_free(xml_stream);
6965 if (!err)
6966 isds_log(ILF_ISDS, ILL_DEBUG,
6967 _("Delivery info loaded successfully.\n"));
6968 return err;
6972 /* Download signed delivery infosheet of given message identified by ID.
6973 * @context is session context
6974 * @message_id is message identifier (you can get them from
6975 * isds_get_list_of_{sent,received}_messages())
6976 * @message is automatically reallocated message retrieved from ISDS.
6977 * It will miss documents per se. Use isds_get_signed_received_message(),
6978 * if you are interrested in documents (content). OTOH, only this function
6979 * can get list events message has gone through. */
6980 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
6981 const char *message_id, struct isds_message **message) {
6983 isds_error err = IE_SUCCESS;
6984 xmlDocPtr response = NULL;
6985 xmlChar *code = NULL, *status_message = NULL;
6986 void *raw = NULL;
6987 size_t raw_length = 0;
6989 if (!context) return IE_INVALID_CONTEXT;
6991 /* Free former message if any */
6992 if (!message) return IE_INVAL;
6993 isds_message_free(message);
6995 /* Do request and check for success */
6996 err = build_send_check_message_request(context, SERVICE_DM_INFO,
6997 BAD_CAST "GetSignedDeliveryInfo", message_id,
6998 &response, NULL, NULL, &code, &status_message);
6999 if (err) goto leave;
7001 /* Find signed delivery info, extract it into raw and maybe free
7002 * response */
7003 err = find_extract_signed_data_free_response(context,
7004 (xmlChar *)message_id, &response,
7005 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
7006 if (err) goto leave;
7008 /* Parse delivery info */
7009 err = isds_load_delivery_info(context,
7010 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
7011 message, BUFFER_MOVE);
7012 if (err) goto leave;
7014 raw = NULL;
7016 leave:
7017 if (err) {
7018 isds_message_free(message);
7021 free(raw);
7022 free(code);
7023 free(status_message);
7024 xmlFreeDoc(response);
7026 if (!err)
7027 isds_log(ILF_ISDS, ILL_DEBUG,
7028 _("GetSignedDeliveryInfo request processed by server "
7029 "successfully.\n")
7031 return err;
7035 /* Download delivery infosheet of given message identified by ID.
7036 * @context is session context
7037 * @message_id is message identifier (you can get them from
7038 * isds_get_list_of_{sent,received}_messages())
7039 * @message is automatically reallocated message retrieved from ISDS.
7040 * It will miss documents per se. Use isds_get_received_message(), if you are
7041 * interrested in documents (content). OTOH, only this function can get list
7042 * events message has gone through. */
7043 isds_error isds_get_delivery_info(struct isds_ctx *context,
7044 const char *message_id, struct isds_message **message) {
7046 isds_error err = IE_SUCCESS;
7047 xmlDocPtr response = NULL;
7048 xmlChar *code = NULL, *status_message = NULL;
7049 xmlXPathContextPtr xpath_ctx = NULL;
7050 xmlXPathObjectPtr result = NULL;
7051 xmlNodePtr delivery_node = NULL;
7053 if (!context) return IE_INVALID_CONTEXT;
7055 /* Free former message if any */
7056 if (!message) return IE_INVAL;
7057 isds_message_free(message);
7059 /* Do request and check for success */
7060 err = build_send_check_message_request(context, SERVICE_DM_INFO,
7061 BAD_CAST "GetDeliveryInfo", message_id,
7062 &response, NULL, NULL, &code, &status_message);
7063 if (err) goto leave;
7065 /* Extract data */
7066 xpath_ctx = xmlXPathNewContext(response);
7067 if (!xpath_ctx) {
7068 err = IE_ERROR;
7069 goto leave;
7071 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7072 err = IE_ERROR;
7073 goto leave;
7075 result = xmlXPathEvalExpression(
7076 BAD_CAST "/isds:GetDeliveryInfoResponse/isds:dmDelivery",
7077 xpath_ctx);
7078 if (!result) {
7079 err = IE_ERROR;
7080 goto leave;
7082 /* Empty response */
7083 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7084 char *message_id_locale = utf82locale((char*) message_id);
7085 isds_printf_message(context,
7086 _("Server did not return any delivery info for ID `%s' "
7087 "on GetDeliveryInfo request"), message_id_locale);
7088 free(message_id_locale);
7089 err = IE_ISDS;
7090 goto leave;
7092 /* More delivery infos */
7093 if (result->nodesetval->nodeNr > 1) {
7094 char *message_id_locale = utf82locale((char*) message_id);
7095 isds_printf_message(context,
7096 _("Server did return more delivery infos for ID `%s' "
7097 "on GetDeliveryInfo request"), message_id_locale);
7098 free(message_id_locale);
7099 err = IE_ISDS;
7100 goto leave;
7102 /* One delivery info */
7103 xpath_ctx->node = delivery_node = result->nodesetval->nodeTab[0];
7105 /* Extract the envelope (= message without documents, hence 0).
7106 * XXX: extract_TReturnedMessage() can obtain attachments size,
7107 * but delivery info carries none. It's coded as option elements,
7108 * so it should work. */
7109 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
7110 if (err) goto leave;
7112 /* Extract events */
7113 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmEvents", xpath_ctx);
7114 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
7115 if (err) { err = IE_ERROR; goto leave; }
7116 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
7117 if (err) goto leave;
7119 /* Save XML blob */
7120 err = serialize_subtree(context, delivery_node, &(*message)->raw,
7121 &(*message)->raw_length);
7123 leave:
7124 if (err) {
7125 isds_message_free(message);
7128 xmlXPathFreeObject(result);
7129 xmlXPathFreeContext(xpath_ctx);
7131 free(code);
7132 free(status_message);
7133 xmlFreeDoc(response);
7135 if (!err)
7136 isds_log(ILF_ISDS, ILL_DEBUG,
7137 _("GetDeliveryInfo request processed by server "
7138 "successfully.\n")
7140 return err;
7144 /* Load incoming message from buffer.
7145 * @context is session context
7146 * @buffer XML stream with unsigned message. You can retrieve such data from
7147 * message->raw after calling isds_get_received_message().
7148 * @length is length of buffer in bytes.
7149 * @message is automatically reallocated message parsed from @buffer.
7150 * @strategy selects how buffer will be attached into raw isds_message member.
7151 * */
7152 isds_error isds_load_received_message(struct isds_ctx *context,
7153 const void *buffer, const size_t length,
7154 struct isds_message **message, const isds_buffer_strategy strategy) {
7156 isds_error err = IE_SUCCESS;
7157 xmlDocPtr message_doc = NULL;
7158 xmlXPathContextPtr xpath_ctx = NULL;
7159 xmlXPathObjectPtr result = NULL;
7161 if (!context) return IE_INVALID_CONTEXT;
7162 if (!message) return IE_INVAL;
7163 isds_message_free(message);
7164 if (!buffer) return IE_INVAL;
7167 isds_log(ILF_ISDS, ILL_DEBUG,
7168 _("Incoming message content:\n%.*s\nEnd of message\n"),
7169 length, buffer);
7171 /* Convert extracted messages XML stream into XPath context */
7172 message_doc = xmlParseMemory(buffer, length);
7173 if (!message_doc) {
7174 err = IE_XML;
7175 goto leave;
7177 xpath_ctx = xmlXPathNewContext(message_doc);
7178 if (!xpath_ctx) {
7179 err = IE_ERROR;
7180 goto leave;
7182 /* XXX: Standard name space */
7183 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7184 err = IE_ERROR;
7185 goto leave;
7187 result = xmlXPathEvalExpression(
7188 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
7189 xpath_ctx);
7190 if (!result) {
7191 err = IE_ERROR;
7192 goto leave;
7194 /* Missing dmReturnedMessage */
7195 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7196 isds_printf_message(context,
7197 _("XML document does not contain isds:dmReturnedMessage "
7198 "element"));
7199 err = IE_ISDS;
7200 goto leave;
7202 /* More elements. This should never happen. */
7203 if (result->nodesetval->nodeNr > 1) {
7204 isds_printf_message(context,
7205 _("XML document has more isds:dmReturnedMessage elements"));
7206 err = IE_ISDS;
7207 goto leave;
7209 /* One message */
7210 xpath_ctx->node = result->nodesetval->nodeTab[0];
7212 /* Extract the message */
7213 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
7214 if (err) goto leave;
7216 /* Append XML stream into message */
7217 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
7218 switch (strategy) {
7219 case BUFFER_DONT_STORE:
7220 break;
7221 case BUFFER_COPY:
7222 (*message)->raw = malloc(length);
7223 if (!(*message)->raw) {
7224 err = IE_NOMEM;
7225 goto leave;
7227 memcpy((*message)->raw, buffer, length);
7228 (*message)->raw_length = length;
7229 break;
7230 case BUFFER_MOVE:
7231 (*message)->raw = (void *) buffer;
7232 (*message)->raw_length = length;
7233 break;
7234 default:
7235 err = IE_ENUM;
7236 goto leave;
7239 leave:
7240 if (err) {
7241 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
7242 isds_message_free(message);
7245 xmlFreeDoc(message_doc);
7246 xmlXPathFreeObject(result);
7247 xmlXPathFreeContext(xpath_ctx);
7249 if (!err)
7250 isds_log(ILF_ISDS, ILL_DEBUG,
7251 _("Incoming message loaded successfully.\n"));
7252 return err;
7256 /* Download incoming message identified by ID.
7257 * @context is session context
7258 * @message_id is message identifier (you can get them from
7259 * isds_get_list_of_received_messages())
7260 * @message is automatically reallocated message retrieved from ISDS */
7261 isds_error isds_get_received_message(struct isds_ctx *context,
7262 const char *message_id, struct isds_message **message) {
7264 isds_error err = IE_SUCCESS;
7265 xmlDocPtr response = NULL;
7266 void *xml_stream = NULL;
7267 size_t xml_stream_length;
7268 xmlChar *code = NULL, *status_message = NULL;
7269 xmlXPathContextPtr xpath_ctx = NULL;
7270 xmlXPathObjectPtr result = NULL;
7271 char *phys_path = NULL;
7272 size_t phys_start, phys_end;
7274 if (!context) return IE_INVALID_CONTEXT;
7276 /* Free former message if any */
7277 if (message) isds_message_free(message);
7279 /* Do request and check for success */
7280 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
7281 BAD_CAST "MessageDownload", message_id,
7282 &response, &xml_stream, &xml_stream_length,
7283 &code, &status_message);
7284 if (err) goto leave;
7286 /* Extract data */
7287 xpath_ctx = xmlXPathNewContext(response);
7288 if (!xpath_ctx) {
7289 err = IE_ERROR;
7290 goto leave;
7292 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7293 err = IE_ERROR;
7294 goto leave;
7296 result = xmlXPathEvalExpression(
7297 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
7298 xpath_ctx);
7299 if (!result) {
7300 err = IE_ERROR;
7301 goto leave;
7303 /* Empty response */
7304 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7305 char *message_id_locale = utf82locale((char*) message_id);
7306 isds_printf_message(context,
7307 _("Server did not return any message for ID `%s' "
7308 "on MessageDownload request"), message_id_locale);
7309 free(message_id_locale);
7310 err = IE_ISDS;
7311 goto leave;
7313 /* More messages */
7314 if (result->nodesetval->nodeNr > 1) {
7315 char *message_id_locale = utf82locale((char*) message_id);
7316 isds_printf_message(context,
7317 _("Server did return more messages for ID `%s' "
7318 "on MessageDownload request"), message_id_locale);
7319 free(message_id_locale);
7320 err = IE_ISDS;
7321 goto leave;
7323 /* One message */
7324 xpath_ctx->node = result->nodesetval->nodeTab[0];
7326 /* Extract the message */
7327 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
7328 if (err) goto leave;
7330 /* Locate raw XML blob */
7331 phys_path = strdup(
7332 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
7333 PHYSXML_ELEMENT_SEPARATOR
7334 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
7335 PHYSXML_ELEMENT_SEPARATOR
7336 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
7338 if (!phys_path) {
7339 err = IE_NOMEM;
7340 goto leave;
7342 err = find_element_boundary(xml_stream, xml_stream_length,
7343 phys_path, &phys_start, &phys_end);
7344 zfree(phys_path);
7345 if (err) {
7346 isds_log_message(context,
7347 _("Substring with isds:MessageDownloadResponse element "
7348 "could not be located in raw SOAP message"));
7349 goto leave;
7351 /* Save XML blob */
7352 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
7353 &(*message)->raw_length);*/
7354 /* TODO: Store name space declarations from ancestors */
7355 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
7356 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
7357 (*message)->raw_length = phys_end - phys_start + 1;
7358 (*message)->raw = malloc((*message)->raw_length);
7359 if (!(*message)->raw) {
7360 err = IE_NOMEM;
7361 goto leave;
7363 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
7366 leave:
7367 if (err) {
7368 isds_message_free(message);
7371 free(phys_path);
7373 xmlXPathFreeObject(result);
7374 xmlXPathFreeContext(xpath_ctx);
7376 free(code);
7377 free(status_message);
7378 free(xml_stream);
7379 xmlFreeDoc(response);
7381 if (!err)
7382 isds_log(ILF_ISDS, ILL_DEBUG,
7383 _("MessageDownload request processed by server "
7384 "successfully.\n")
7386 return err;
7390 /* Load signed message from buffer.
7391 * @context is session context
7392 * @outgoing is true if message is outgoing, false if message is incoming
7393 * @buffer is DER encoded PKCS#7 structure with signed message. You can
7394 * retrieve such data from message->raw after calling
7395 * isds_get_signed_{received,sent}_message().
7396 * @length is length of buffer in bytes.
7397 * @message is automatically reallocated message parsed from @buffer.
7398 * @strategy selects how buffer will be attached into raw isds_message member.
7399 * */
7400 isds_error isds_load_signed_message(struct isds_ctx *context,
7401 const _Bool outgoing, const void *buffer, const size_t length,
7402 struct isds_message **message, const isds_buffer_strategy strategy) {
7404 isds_error err = IE_SUCCESS;
7405 xmlDocPtr message_doc = NULL;
7406 xmlXPathContextPtr xpath_ctx = NULL;
7407 xmlXPathObjectPtr result = NULL;
7408 void *xml_stream = NULL;
7409 size_t xml_stream_length = 0;
7411 if (!context) return IE_INVALID_CONTEXT;
7412 if (!message) return IE_INVAL;
7413 isds_message_free(message);
7414 if (!buffer) return IE_INVAL;
7417 /* Extract message from PKCS#7 structure */
7418 err = extract_cms_data(context, buffer, length,
7419 &xml_stream, &xml_stream_length);
7420 if (err) goto leave;
7422 isds_log(ILF_ISDS, ILL_DEBUG, (outgoing) ?
7423 _("Signed outgoing message content:\n%.*s\nEnd of message\n") :
7424 _("Signed incoming message content:\n%.*s\nEnd of message\n"),
7425 xml_stream_length, xml_stream);
7427 /* Convert extracted messages XML stream into XPath context */
7428 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
7429 if (!message_doc) {
7430 err = IE_XML;
7431 goto leave;
7433 xpath_ctx = xmlXPathNewContext(message_doc);
7434 if (!xpath_ctx) {
7435 err = IE_ERROR;
7436 goto leave;
7438 /* XXX: Name spaces mangled for outgoing direction:
7439 * http://isds.czechpoint.cz/v20/SentMessage:
7441 * <q:MessageDownloadResponse
7442 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
7443 * <q:dmReturnedMessage>
7444 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
7445 * <p:dmID>151916</p:dmID>
7446 * ...
7447 * </p:dmDm>
7448 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
7449 * ...
7450 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
7451 * </q:dmReturnedMessage>
7452 * </q:MessageDownloadResponse>
7454 * XXX: Name spaces mangled for incoming direction:
7455 * http://isds.czechpoint.cz/v20/message:
7457 * <q:MessageDownloadResponse
7458 * xmlns:q="http://isds.czechpoint.cz/v20/message">
7459 * <q:dmReturnedMessage>
7460 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
7461 * <p:dmID>151916</p:dmID>
7462 * ...
7463 * </p:dmDm>
7464 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
7465 * ...
7466 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
7467 * </q:dmReturnedMessage>
7468 * </q:MessageDownloadResponse>
7470 * Stupidity of ISDS developers is unlimited */
7471 if (register_namespaces(xpath_ctx, (outgoing) ?
7472 MESSAGE_NS_SIGNED_OUTGOING : MESSAGE_NS_SIGNED_INCOMING)) {
7473 err = IE_ERROR;
7474 goto leave;
7476 /* XXX: Embeded message XML document is always rooted as
7477 * /sisds:MessageDownloadResponse (even outgoing message). */
7478 result = xmlXPathEvalExpression(
7479 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
7480 xpath_ctx);
7481 if (!result) {
7482 err = IE_ERROR;
7483 goto leave;
7485 /* Empty embedded message */
7486 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7487 isds_printf_message(context,
7488 _("XML document embedded into PKCS#7 structure is not "
7489 "sisds:dmReturnedMessage document"));
7490 err = IE_ISDS;
7491 goto leave;
7493 /* More embedded messages */
7494 if (result->nodesetval->nodeNr > 1) {
7495 isds_printf_message(context,
7496 _("Embeded XML document into PKCS#7 structure has more "
7497 "root sisds:dmReturnedMessage elements"));
7498 err = IE_ISDS;
7499 goto leave;
7501 /* One embedded message */
7502 xpath_ctx->node = result->nodesetval->nodeTab[0];
7504 /* Extract the message */
7505 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
7506 if (err) goto leave;
7508 /* Append raw CMS structure into message */
7509 (*message)->raw_type = (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
7510 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
7511 switch (strategy) {
7512 case BUFFER_DONT_STORE:
7513 break;
7514 case BUFFER_COPY:
7515 (*message)->raw = malloc(length);
7516 if (!(*message)->raw) {
7517 err = IE_NOMEM;
7518 goto leave;
7520 memcpy((*message)->raw, buffer, length);
7521 (*message)->raw_length = length;
7522 break;
7523 case BUFFER_MOVE:
7524 (*message)->raw = (void *) buffer;
7525 (*message)->raw_length = length;
7526 break;
7527 default:
7528 err = IE_ENUM;
7529 goto leave;
7533 leave:
7534 if (err) {
7535 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
7536 isds_message_free(message);
7539 xmlFreeDoc(message_doc);
7540 cms_data_free(xml_stream);
7541 xmlXPathFreeObject(result);
7542 xmlXPathFreeContext(xpath_ctx);
7544 if (!err)
7545 isds_log(ILF_ISDS, ILL_DEBUG,
7546 _("Signed message loaded successfully.\n"));
7547 return err;
7551 /* Load message of any type from buffer.
7552 * @context is session context
7553 * @raw_type defines content type of @buffer. Only message types are allowed.
7554 * @buffer is message raw representation. Format (CMS, plain signed,
7555 * message direction) is defined in @raw_type. You can retrieve such data
7556 * from message->raw after calling isds_get_[signed]{received,sent}_message().
7557 * @length is length of buffer in bytes.
7558 * @message is automatically reallocated message parsed from @buffer.
7559 * @strategy selects how buffer will be attached into raw isds_message member.
7560 * */
7561 isds_error isds_load_message(struct isds_ctx *context,
7562 const isds_raw_type raw_type, const void *buffer, const size_t length,
7563 struct isds_message **message, const isds_buffer_strategy strategy) {
7565 isds_error err = IE_SUCCESS;
7566 void *xml_stream = NULL;
7567 size_t xml_stream_length = 0;
7568 message_ns_type message_ns;
7569 xmlDocPtr message_doc = NULL;
7570 xmlXPathContextPtr xpath_ctx = NULL;
7571 xmlXPathObjectPtr result = NULL;
7573 if (!context) return IE_INVALID_CONTEXT;
7574 if (!message) return IE_INVAL;
7575 isds_message_free(message);
7576 if (!buffer) return IE_INVAL;
7579 /* Select buffer format and extract XML from CMS*/
7580 switch (raw_type) {
7581 case RAWTYPE_INCOMING_MESSAGE:
7582 message_ns = MESSAGE_NS_UNSIGNED;
7583 xml_stream = (void *) buffer;
7584 xml_stream_length = length;
7585 break;
7587 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
7588 message_ns = MESSAGE_NS_SIGNED_INCOMING;
7589 xml_stream = (void *) buffer;
7590 xml_stream_length = length;
7591 break;
7593 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
7594 message_ns = MESSAGE_NS_SIGNED_INCOMING;
7595 err = extract_cms_data(context, buffer, length,
7596 &xml_stream, &xml_stream_length);
7597 if (err) goto leave;
7598 break;
7600 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
7601 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
7602 xml_stream = (void *) buffer;
7603 xml_stream_length = length;
7604 break;
7606 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
7607 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
7608 err = extract_cms_data(context, buffer, length,
7609 &xml_stream, &xml_stream_length);
7610 if (err) goto leave;
7611 break;
7613 default:
7614 isds_log_message(context, _("Bad raw message representation type"));
7615 return IE_INVAL;
7616 break;
7619 isds_log(ILF_ISDS, ILL_DEBUG,
7620 _("Loading message:\n%.*s\nEnd of message\n"),
7621 xml_stream_length, xml_stream);
7623 /* Convert messages XML stream into XPath context */
7624 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
7625 if (!message_doc) {
7626 err = IE_XML;
7627 goto leave;
7629 xpath_ctx = xmlXPathNewContext(message_doc);
7630 if (!xpath_ctx) {
7631 err = IE_ERROR;
7632 goto leave;
7634 /* XXX: Standard name space for unsigned icoming direction:
7635 * http://isds.czechpoint.cz/v20/SentMessage
7637 * XXX: Name spaces mangled for signed outgoing direction:
7638 * http://isds.czechpoint.cz/v20/SentMessage:
7640 * <q:MessageDownloadResponse
7641 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
7642 * <q:dmReturnedMessage>
7643 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
7644 * <p:dmID>151916</p:dmID>
7645 * ...
7646 * </p:dmDm>
7647 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
7648 * ...
7649 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
7650 * </q:dmReturnedMessage>
7651 * </q:MessageDownloadResponse>
7653 * XXX: Name spaces mangled for signed incoming direction:
7654 * http://isds.czechpoint.cz/v20/message:
7656 * <q:MessageDownloadResponse
7657 * xmlns:q="http://isds.czechpoint.cz/v20/message">
7658 * <q:dmReturnedMessage>
7659 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
7660 * <p:dmID>151916</p:dmID>
7661 * ...
7662 * </p:dmDm>
7663 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
7664 * ...
7665 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
7666 * </q:dmReturnedMessage>
7667 * </q:MessageDownloadResponse>
7669 * Stupidity of ISDS developers is unlimited */
7670 if (register_namespaces(xpath_ctx, message_ns)) {
7671 err = IE_ERROR;
7672 goto leave;
7674 result = xmlXPathEvalExpression(
7675 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
7676 xpath_ctx);
7677 if (!result) {
7678 err = IE_ERROR;
7679 goto leave;
7681 /* Empty message */
7682 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7683 isds_printf_message(context,
7684 _("XML document does not contain "
7685 "sisds:dmReturnedMessage element"));
7686 err = IE_ISDS;
7687 goto leave;
7689 /* More messages */
7690 if (result->nodesetval->nodeNr > 1) {
7691 isds_printf_message(context,
7692 _("XML document has more sisds:dmReturnedMessage elements"));
7693 err = IE_ISDS;
7694 goto leave;
7696 /* One message */
7697 xpath_ctx->node = result->nodesetval->nodeTab[0];
7699 /* Extract the message */
7700 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
7701 if (err) goto leave;
7703 /* Append raw buffer into message */
7704 (*message)->raw_type = raw_type;
7705 switch (strategy) {
7706 case BUFFER_DONT_STORE:
7707 break;
7708 case BUFFER_COPY:
7709 (*message)->raw = malloc(length);
7710 if (!(*message)->raw) {
7711 err = IE_NOMEM;
7712 goto leave;
7714 memcpy((*message)->raw, buffer, length);
7715 (*message)->raw_length = length;
7716 break;
7717 case BUFFER_MOVE:
7718 (*message)->raw = (void *) buffer;
7719 (*message)->raw_length = length;
7720 break;
7721 default:
7722 err = IE_ENUM;
7723 goto leave;
7727 leave:
7728 if (err) {
7729 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
7730 isds_message_free(message);
7733 if (xml_stream != buffer) cms_data_free(xml_stream);
7734 xmlXPathFreeObject(result);
7735 xmlXPathFreeContext(xpath_ctx);
7736 xmlFreeDoc(message_doc);
7738 if (!err)
7739 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
7740 return err;
7744 /* Download signed incoming/outgoing message identified by ID.
7745 * @context is session context
7746 * @output is true for outging message, false for incoming message
7747 * @message_id is message identifier (you can get them from
7748 * isds_get_list_of_{sent,received}_messages())
7749 * @message is automatically reallocated message retrieved from ISDS. The raw
7750 * memeber will be filled with PKCS#7 structure in DER format. */
7751 _hidden isds_error isds_get_signed_message(struct isds_ctx *context,
7752 const _Bool outgoing, const char *message_id,
7753 struct isds_message **message) {
7755 isds_error err = IE_SUCCESS;
7756 xmlDocPtr response = NULL;
7757 xmlChar *code = NULL, *status_message = NULL;
7758 xmlXPathContextPtr xpath_ctx = NULL;
7759 xmlXPathObjectPtr result = NULL;
7760 char *encoded_structure = NULL;
7761 void *raw = NULL;
7762 size_t raw_length = 0;
7764 if (!context) return IE_INVALID_CONTEXT;
7765 if (!message) return IE_INVAL;
7766 isds_message_free(message);
7768 /* Do request and check for success */
7769 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
7770 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
7771 BAD_CAST "SignedMessageDownload",
7772 message_id, &response, NULL, NULL, &code, &status_message);
7773 if (err) goto leave;
7775 /* Find signed message, extract it into raw and maybe free
7776 * response */
7777 err = find_extract_signed_data_free_response(context,
7778 (xmlChar *)message_id, &response,
7779 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
7780 BAD_CAST "SignedMessageDownload",
7781 &raw, &raw_length);
7782 if (err) goto leave;
7784 /* Parse message */
7785 err = isds_load_message(context,
7786 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
7787 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
7788 raw, raw_length, message, BUFFER_MOVE);
7789 if (err) goto leave;
7791 raw = NULL;
7793 leave:
7794 if (err) {
7795 isds_message_free(message);
7798 free(encoded_structure);
7799 xmlXPathFreeObject(result);
7800 xmlXPathFreeContext(xpath_ctx);
7801 free(raw);
7803 free(code);
7804 free(status_message);
7805 xmlFreeDoc(response);
7807 if (!err)
7808 isds_log(ILF_ISDS, ILL_DEBUG,
7809 (outgoing) ?
7810 _("SignedSentMessageDownload request processed by server "
7811 "successfully.\n") :
7812 _("SignedMessageDownload request processed by server "
7813 "successfully.\n")
7815 return err;
7819 /* Download signed incoming message identified by ID.
7820 * @context is session context
7821 * @message_id is message identifier (you can get them from
7822 * isds_get_list_of_received_messages())
7823 * @message is automatically reallocated message retrieved from ISDS. The raw
7824 * memeber will be filled with PKCS#7 structure in DER format. */
7825 isds_error isds_get_signed_received_message(struct isds_ctx *context,
7826 const char *message_id, struct isds_message **message) {
7827 return isds_get_signed_message(context, 0, message_id, message);
7831 /* Download signed outgoing message identified by ID.
7832 * @context is session context
7833 * @message_id is message identifier (you can get them from
7834 * isds_get_list_of_sent_messages())
7835 * @message is automatically reallocated message retrieved from ISDS. The raw
7836 * memeber will be filled with PKCS#7 structure in DER format. */
7837 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
7838 const char *message_id, struct isds_message **message) {
7839 return isds_get_signed_message(context, 1, message_id, message);
7843 /* Retrieve hash of message identified by ID stored in ISDS.
7844 * @context is session context
7845 * @message_id is message identifier
7846 * @hash is automatically reallocated message hash downloaded from ISDS.
7847 * Message must exist in system and must not be deleted. */
7848 isds_error isds_download_message_hash(struct isds_ctx *context,
7849 const char *message_id, struct isds_hash **hash) {
7851 isds_error err = IE_SUCCESS;
7852 xmlDocPtr response = NULL;
7853 xmlChar *code = NULL, *status_message = NULL;
7854 xmlXPathContextPtr xpath_ctx = NULL;
7855 xmlXPathObjectPtr result = NULL;
7857 if (!context) return IE_INVALID_CONTEXT;
7859 isds_hash_free(hash);
7861 err = build_send_check_message_request(context, SERVICE_DM_INFO,
7862 BAD_CAST "VerifyMessage", message_id,
7863 &response, NULL, NULL, &code, &status_message);
7864 if (err) goto leave;
7867 /* Extract data */
7868 xpath_ctx = xmlXPathNewContext(response);
7869 if (!xpath_ctx) {
7870 err = IE_ERROR;
7871 goto leave;
7873 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7874 err = IE_ERROR;
7875 goto leave;
7877 result = xmlXPathEvalExpression(
7878 BAD_CAST "/isds:VerifyMessageResponse",
7879 xpath_ctx);
7880 if (!result) {
7881 err = IE_ERROR;
7882 goto leave;
7884 /* Empty response */
7885 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7886 char *message_id_locale = utf82locale((char*) message_id);
7887 isds_printf_message(context,
7888 _("Server did not return any response for ID `%s' "
7889 "on VerifyMessage request"), message_id_locale);
7890 free(message_id_locale);
7891 err = IE_ISDS;
7892 goto leave;
7894 /* More responses */
7895 if (result->nodesetval->nodeNr > 1) {
7896 char *message_id_locale = utf82locale((char*) message_id);
7897 isds_printf_message(context,
7898 _("Server did return more responses for ID `%s' "
7899 "on VerifyMessage request"), message_id_locale);
7900 free(message_id_locale);
7901 err = IE_ISDS;
7902 goto leave;
7904 /* One response */
7905 xpath_ctx->node = result->nodesetval->nodeTab[0];
7907 /* Extract the hash */
7908 err = find_and_extract_DmHash(context, hash, xpath_ctx);
7910 leave:
7911 if (err) {
7912 isds_hash_free(hash);
7915 xmlXPathFreeObject(result);
7916 xmlXPathFreeContext(xpath_ctx);
7918 free(code);
7919 free(status_message);
7920 xmlFreeDoc(response);
7922 if (!err)
7923 isds_log(ILF_ISDS, ILL_DEBUG,
7924 _("VerifyMessage request processed by server "
7925 "successfully.\n")
7927 return err;
7931 /* Mark message as read. This is a transactional commit function to acknoledge
7932 * to ISDS the message has been downloaded and processed by client properly.
7933 * @context is session context
7934 * @message_id is message identifier. */
7935 isds_error isds_mark_message_read(struct isds_ctx *context,
7936 const char *message_id) {
7938 isds_error err = IE_SUCCESS;
7939 xmlDocPtr response = NULL;
7940 xmlChar *code = NULL, *status_message = NULL;
7942 if (!context) return IE_INVALID_CONTEXT;
7944 /* Do request and check for success */
7945 err = build_send_check_message_request(context, SERVICE_DM_INFO,
7946 BAD_CAST "MarkMessageAsDownloaded", message_id,
7947 &response, NULL, NULL, &code, &status_message);
7949 free(code);
7950 free(status_message);
7951 xmlFreeDoc(response);
7953 if (!err)
7954 isds_log(ILF_ISDS, ILL_DEBUG,
7955 _("MarkMessageAsDownloaded request processed by server "
7956 "successfully.\n")
7958 return err;
7961 /* Mark message as received by recipient. This is applicable only to
7962 * commercial message. There is no specified way how to distinguishe
7963 * commercial message from government message yet. Government message is
7964 * received automatically (by law), commenrcial message on recipient request.
7965 * @context is session context
7966 * @message_id is message identifier. */
7967 isds_error isds_mark_message_received(struct isds_ctx *context,
7968 const char *message_id) {
7970 isds_error err = IE_SUCCESS;
7971 xmlDocPtr response = NULL;
7972 xmlChar *code = NULL, *status_message = NULL;
7974 if (!context) return IE_INVALID_CONTEXT;
7976 /* Do request and check for success */
7977 err = build_send_check_message_request(context, SERVICE_DM_INFO,
7978 BAD_CAST "ConfirmDelivery", message_id,
7979 &response, NULL, NULL, &code, &status_message);
7981 free(code);
7982 free(status_message);
7983 xmlFreeDoc(response);
7985 if (!err)
7986 isds_log(ILF_ISDS, ILL_DEBUG,
7987 _("ConfirmDelivery request processed by server "
7988 "successfully.\n")
7990 return err;
7994 #undef INSERT_ELEMENT
7995 #undef CHECK_FOR_STRING_LENGTH
7996 #undef INSERT_STRING_ATTRIBUTE
7997 #undef INSERT_ULONGINTNOPTR
7998 #undef INSERT_ULONGINT
7999 #undef INSERT_LONGINT
8000 #undef INSERT_BOOLEAN
8001 #undef INSERT_SCALAR_BOOLEAN
8002 #undef INSERT_STRING
8003 #undef EXTRACT_STRING_ATTRIBUTE
8004 #undef EXTRACT_ULONGINT
8005 #undef EXTRACT_LONGINT
8006 #undef EXTRACT_BOOLEAN
8007 #undef EXTRACT_STRING
8010 /* Compute hash of message from raw representation and store it into envelope.
8011 * Original hash structure will be destroyed in envelope.
8012 * @context is session context
8013 * @message is message carrying raw XML message blob
8014 * @algorithm is desired hash algorithm to use */
8015 isds_error isds_compute_message_hash(struct isds_ctx *context,
8016 struct isds_message *message, const isds_hash_algorithm algorithm) {
8017 isds_error err = IE_SUCCESS;
8018 const char *nsuri;
8019 void *xml_stream = NULL;
8020 size_t xml_stream_length;
8021 size_t phys_start, phys_end;
8022 char *phys_path = NULL;
8023 struct isds_hash *new_hash = NULL;
8026 if (!context) return IE_INVALID_CONTEXT;
8027 if (!message) return IE_INVAL;
8029 if (!message->raw) {
8030 isds_log_message(context,
8031 _("Message does not carry raw representation"));
8032 return IE_INVAL;
8035 switch (message->raw_type) {
8036 case RAWTYPE_INCOMING_MESSAGE:
8037 nsuri = ISDS_NS;
8038 xml_stream = message->raw;
8039 xml_stream_length = message->raw_length;
8040 break;
8042 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
8043 nsuri = SISDS_INCOMING_NS;
8044 xml_stream = message->raw;
8045 xml_stream_length = message->raw_length;
8046 break;
8048 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
8049 nsuri = SISDS_INCOMING_NS;
8050 err = extract_cms_data(context, message->raw, message->raw_length,
8051 &xml_stream, &xml_stream_length);
8052 if (err) goto leave;
8053 break;
8055 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
8056 nsuri = SISDS_OUTGOING_NS;
8057 xml_stream = message->raw;
8058 xml_stream_length = message->raw_length;
8059 break;
8061 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
8062 nsuri = SISDS_OUTGOING_NS;
8063 err = extract_cms_data(context, message->raw, message->raw_length,
8064 &xml_stream, &xml_stream_length);
8065 if (err) goto leave;
8066 break;
8068 default:
8069 isds_log_message(context, _("Bad raw representation type"));
8070 return IE_INVAL;
8071 break;
8075 /* XXX: Hash is computed from original string represinting isds:dmDm
8076 * subtree. That means no encoding, white space, xmlns attributes changes.
8077 * In other words, input for hash can be invalid XML stream. */
8078 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
8079 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
8080 PHYSXML_ELEMENT_SEPARATOR,
8081 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
8082 PHYSXML_ELEMENT_SEPARATOR
8083 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
8084 err = IE_NOMEM;
8085 goto leave;
8087 err = find_element_boundary(xml_stream, xml_stream_length,
8088 phys_path, &phys_start, &phys_end);
8089 zfree(phys_path);
8090 if (err) {
8091 isds_log_message(context,
8092 _("Substring with isds:dmDM element could not be located "
8093 "in raw message"));
8094 goto leave;
8098 /* Compute hash */
8099 new_hash = calloc(1, sizeof(*new_hash));
8100 if (!new_hash) {
8101 err = IE_NOMEM;
8102 goto leave;
8104 new_hash->algorithm = algorithm;
8105 err = compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
8106 new_hash);
8107 if (err) {
8108 isds_log_message(context, _("Could not compute message hash"));
8109 goto leave;
8112 /* Save computed hash */
8113 if (!message->envelope) {
8114 message->envelope = calloc(1, sizeof(*message->envelope));
8115 if (!message->envelope) {
8116 err = IE_NOMEM;
8117 goto leave;
8120 isds_hash_free(&message->envelope->hash);
8121 message->envelope->hash = new_hash;
8123 leave:
8124 if (err) {
8125 isds_hash_free(&new_hash);
8128 free(phys_path);
8129 if (xml_stream != message->raw) free(xml_stream);
8130 return err;
8134 /* Compare two hashes.
8135 * @h1 is first hash
8136 * @h2 is another hash
8137 * @return
8138 * IE_SUCCESS if hashes equal
8139 * IE_NOTUNIQ if hashes are comparable, but they don't equal
8140 * IE_ENUM if not comparable, but both structures defined
8141 * IE_INVAL if some of the structures are undefined (NULL)
8142 * IE_ERROR if internal error occurs */
8143 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
8144 if (h1 == NULL || h2 == NULL) return IE_INVAL;
8145 if (h1->algorithm != h2->algorithm) return IE_ENUM;
8146 if (h1->length != h2->length) return IE_ERROR;
8147 if (h1->length > 0 && !h1->value) return IE_ERROR;
8148 if (h2->length > 0 && !h2->value) return IE_ERROR;
8150 for (int i = 0; i < h1->length; i++) {
8151 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
8152 return IE_NOTEQUAL;
8154 return IE_SUCCESS;
8158 /* Check message has gone through ISDS by comparing message hash stored in
8159 * ISDS and locally computed hash. You must provide message with valid raw
8160 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
8161 * This is convenient wrapper for isds_download_message_hash(),
8162 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
8163 * @context is session context
8164 * @message is message with valid raw and envelope member; envelope->hash
8165 * member will be changed during funcion run. Use envelope on heap only.
8166 * @return
8167 * IE_SUCCESS if message originates in ISDS
8168 * IE_NOTEQUAL if message is unknown to ISDS
8169 * other code for other errors */
8170 isds_error isds_verify_message_hash(struct isds_ctx *context,
8171 struct isds_message *message) {
8172 isds_error err = IE_SUCCESS;
8173 struct isds_hash *downloaded_hash = NULL;
8175 if (!context) return IE_INVALID_CONTEXT;
8176 if (!message) return IE_INVAL;
8178 if (!message->envelope) {
8179 isds_log_message(context,
8180 _("Given message structure is missing envelope"));
8181 return IE_INVAL;
8183 if (!message->raw) {
8184 isds_log_message(context,
8185 _("Given message structure is missing raw representation"));
8186 return IE_INVAL;
8189 err = isds_download_message_hash(context, message->envelope->dmID,
8190 &downloaded_hash);
8191 if (err) goto leave;
8193 err = isds_compute_message_hash(context, message,
8194 downloaded_hash->algorithm);
8195 if (err) goto leave;
8197 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
8199 leave:
8200 isds_hash_free(&downloaded_hash);
8201 return err;
8205 /* Search for document by document ID in list of documents. IDs are compared
8206 * as UTF-8 string.
8207 * @documents is list of isds_documents
8208 * @id is document identifier
8209 * @return first matching document or NULL. */
8210 const struct isds_document *isds_find_document_by_id(
8211 const struct isds_list *documents, const char *id) {
8212 const struct isds_list *item;
8213 const struct isds_document *document;
8215 for (item = documents; item; item = item->next) {
8216 document = (struct isds_document *) item->data;
8217 if (!document) continue;
8219 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
8220 return document;
8223 return NULL;
8227 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
8228 struct isds_message **message);
8229 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
8230 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
8231 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
8232 struct isds_address **address);
8234 int isds_message_free(struct isds_message **message);
8235 int isds_address_free(struct isds_address **address);
8239 /* Makes known all relevant namespaces to given XPath context
8240 * @xpat_ctx is XPath context
8241 * @message_ns selects propper message name space. Unsisnged and signed
8242 * messages differs.
8243 * prefix and to URI ISDS_NS */
8244 _hidden isds_error register_namespaces(xmlXPathContextPtr xpath_ctx,
8245 const message_ns_type message_ns) {
8246 const xmlChar *message_namespace = NULL;
8248 if (!xpath_ctx) return IE_ERROR;
8250 switch(message_ns) {
8251 case MESSAGE_NS_UNSIGNED:
8252 message_namespace = BAD_CAST ISDS_NS; break;
8253 case MESSAGE_NS_SIGNED_INCOMING:
8254 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
8255 case MESSAGE_NS_SIGNED_OUTGOING:
8256 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
8257 case MESSAGE_NS_SIGNED_DELIVERY:
8258 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
8259 default:
8260 return IE_ENUM;
8263 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
8264 return IE_ERROR;
8265 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
8266 return IE_ERROR;
8267 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
8268 return IE_ERROR;
8269 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
8270 return IE_ERROR;
8271 return IE_SUCCESS;