Implement SHA-224 and SHA-384 hashes
[libisds.git] / src / isds.c
blob2baea48fee4d9da25814b166a22fe0762b1f9076
1 #define _XOPEN_SOURCE 500 /* strdup from string.h, strptime from time.h */
2 #include <stdlib.h>
3 #include <string.h>
4 #include <stdio.h>
5 #include <stdarg.h>
6 #include <ctype.h>
7 #include <stdint.h>
8 #include "isds_priv.h"
9 #include "utils.h"
10 #include "soap.h"
11 #include "validator.h"
12 #include "crypto.h"
13 #include <gpg-error.h> /* Because of ksba or gpgme */
14 #include "physxml.h"
17 /* Free isds_list with all member data.
18 * @list list to free, on return will be NULL */
19 void isds_list_free(struct isds_list **list) {
20 struct isds_list *item, *next_item;
22 if (!list || !*list) return;
24 for(item = *list; item; item = next_item) {
25 (item->destructor)(&(item->data));
26 next_item = item->next;
27 free(item);
30 *list = NULL;
34 /* Deallocate structure isds_hash and NULL it.
35 * @hash hash to to free */
36 void isds_hash_free(struct isds_hash **hash) {
37 if(!hash || !*hash) return;
38 free((*hash)->value);
39 zfree((*hash));
43 /* Deallocate structure isds_PersonName recursively and NULL it */
44 static void isds_PersonName_free(struct isds_PersonName **person_name) {
45 if (!person_name || !*person_name) return;
47 free((*person_name)->pnFirstName);
48 free((*person_name)->pnMiddleName);
49 free((*person_name)->pnLastName);
50 free((*person_name)->pnLastNameAtBirth);
52 free(*person_name);
53 *person_name = NULL;
57 /* Deallocate structure isds_BirthInfo recursively and NULL it */
58 static void isds_BirthInfo_free(struct isds_BirthInfo **birth_info) {
59 if (!birth_info || !*birth_info) return;
61 free((*birth_info)->biDate);
62 free((*birth_info)->biCity);
63 free((*birth_info)->biCounty);
64 free((*birth_info)->biState);
66 free(*birth_info);
67 *birth_info = NULL;
71 /* Deallocate structure isds_Address recursively and NULL it */
72 static void isds_Address_free(struct isds_Address **address) {
73 if (!address || !*address) return;
75 free((*address)->adCity);
76 free((*address)->adStreet);
77 free((*address)->adNumberInStreet);
78 free((*address)->adNumberInMunicipality);
79 free((*address)->adZipCode);
80 free((*address)->adState);
82 free(*address);
83 *address = NULL;
87 /* Deallocate structure isds_DbOwnerInfo recursively and NULL it */
88 void isds_DbOwnerInfo_free(struct isds_DbOwnerInfo **db_owner_info) {
89 if (!db_owner_info || !*db_owner_info) return;
91 free((*db_owner_info)->dbID);
92 free((*db_owner_info)->dbType);
93 free((*db_owner_info)->ic);
94 isds_PersonName_free(&((*db_owner_info)->personName));
95 free((*db_owner_info)->firmName);
96 isds_BirthInfo_free(&((*db_owner_info)->birthInfo));
97 isds_Address_free(&((*db_owner_info)->address));
98 free((*db_owner_info)->nationality);
99 free((*db_owner_info)->email);
100 free((*db_owner_info)->telNumber);
101 free((*db_owner_info)->identifier);
102 free((*db_owner_info)->registryCode);
103 free((*db_owner_info)->dbState);
104 free((*db_owner_info)->dbEffectiveOVM);
106 free(*db_owner_info);
107 *db_owner_info = NULL;
110 /* Deallocate structure isds_DbUserInfo recursively and NULL it */
111 void isds_DbUserInfo_free(struct isds_DbUserInfo **db_user_info) {
112 if (!db_user_info || !*db_user_info) return;
114 free((*db_user_info)->userID);
115 free((*db_user_info)->userType);
116 free((*db_user_info)->userPrivils);
117 isds_PersonName_free(&((*db_user_info)->personName));
118 isds_Address_free(&((*db_user_info)->address));
119 free((*db_user_info)->biDate);
120 free((*db_user_info)->ic);
121 free((*db_user_info)->firmName);
122 free((*db_user_info)->caStreet);
123 free((*db_user_info)->caCity);
124 free((*db_user_info)->caZipCode);
126 zfree(*db_user_info);
130 /* Deallocate struct isds_event recursively and NULL it */
131 void isds_event_free(struct isds_event **event) {
132 if (!event || !*event) return;
134 free((*event)->time);
135 free((*event)->type);
136 free((*event)->description);
137 zfree(*event);
141 /* Deallocate struct isds_envelope recursively and NULL it */
142 void isds_envelope_free(struct isds_envelope **envelope) {
143 if (!envelope || !*envelope) return;
145 free((*envelope)->dmID);
146 free((*envelope)->dbIDSender);
147 free((*envelope)->dmSender);
148 free((*envelope)->dmSenderAddress);
149 free((*envelope)->dmSenderType);
150 free((*envelope)->dmRecipient);
151 free((*envelope)->dmRecipientAddress);
152 free((*envelope)->dmAmbiguousRecipient);
153 free((*envelope)->dmType);
155 free((*envelope)->dmOrdinal);
156 free((*envelope)->dmMessageStatus);
157 free((*envelope)->dmDeliveryTime);
158 free((*envelope)->dmAcceptanceTime);
159 isds_hash_free(&(*envelope)->hash);
160 free((*envelope)->timestamp);
161 isds_list_free(&(*envelope)->events);
163 free((*envelope)->dmSenderOrgUnit);
164 free((*envelope)->dmSenderOrgUnitNum);
165 free((*envelope)->dbIDRecipient);
166 free((*envelope)->dmRecipientOrgUnit);
167 free((*envelope)->dmRecipientOrgUnitNum);
168 free((*envelope)->dmToHands);
169 free((*envelope)->dmAnnotation);
170 free((*envelope)->dmRecipientRefNumber);
171 free((*envelope)->dmSenderRefNumber);
172 free((*envelope)->dmRecipientIdent);
173 free((*envelope)->dmSenderIdent);
175 free((*envelope)->dmLegalTitleLaw);
176 free((*envelope)->dmLegalTitleYear);
177 free((*envelope)->dmLegalTitleSect);
178 free((*envelope)->dmLegalTitlePar);
179 free((*envelope)->dmLegalTitlePoint);
181 free((*envelope)->dmPersonalDelivery);
182 free((*envelope)->dmAllowSubstDelivery);
183 free((*envelope)->dmOVM);
185 free(*envelope);
186 *envelope = NULL;
190 /* Deallocate struct isds_message recursively and NULL it */
191 void isds_message_free(struct isds_message **message) {
192 if (!message || !*message) return;
194 free((*message)->raw);
195 isds_envelope_free(&((*message)->envelope));
196 isds_list_free(&((*message)->documents));
198 free(*message);
199 *message = NULL;
203 /* Deallocate struct isds_document recursively and NULL it */
204 void isds_document_free(struct isds_document **document) {
205 if (!document || !*document) return;
207 free((*document)->data);
208 free((*document)->dmMimeType);
209 free((*document)->dmFileGuid);
210 free((*document)->dmUpFileGuid);
211 free((*document)->dmFileDescr);
212 free((*document)->dmFormat);
214 free(*document);
215 *document = NULL;
219 /* Deallocate struct isds_message_copy recursively and NULL it */
220 void isds_message_copy_free(struct isds_message_copy **copy) {
221 if (!copy || !*copy) return;
223 free((*copy)->dbIDRecipient);
224 free((*copy)->dmRecipientOrgUnit);
225 free((*copy)->dmRecipientOrgUnitNum);
226 free((*copy)->dmToHands);
228 free((*copy)->dmStatus);
229 free((*copy)->dmID);
231 zfree(*copy);
235 /* Deallocate struct isds_approval recursively and NULL it */
236 void isds_approval_free(struct isds_approval **approval) {
237 if (!approval || !*approval) return;
239 free((*approval)->refference);
241 zfree(*approval);
245 /* *DUP_OR_ERROR macros needs error label */
246 #define STRDUP_OR_ERROR(new, template) { \
247 if (!template) { \
248 (new) = NULL; \
249 } else { \
250 (new) = strdup(template); \
251 if (!new) goto error; \
255 #define FLATDUP_OR_ERROR(new, template) { \
256 if (!template) { \
257 (new) = NULL; \
258 } else { \
259 (new) = malloc(sizeof(*(new))); \
260 if (!new) goto error; \
261 memcpy((new), (template), sizeof(*(template))); \
266 /* Copy structure isds_PersonName recursively */
267 struct isds_PersonName *isds_PersonName_duplicate(
268 const struct isds_PersonName *template) {
269 struct isds_PersonName *new = NULL;
271 if (!template) return NULL;
273 new = calloc(1, sizeof(*new));
274 if (!new) return NULL;
276 STRDUP_OR_ERROR(new->pnFirstName, template->pnFirstName);
277 STRDUP_OR_ERROR(new->pnMiddleName, template->pnMiddleName);
278 STRDUP_OR_ERROR(new->pnLastName, template->pnLastName);
279 STRDUP_OR_ERROR(new->pnLastNameAtBirth, template->pnLastNameAtBirth);
281 return new;
283 error:
284 isds_PersonName_free(&new);
285 return NULL;
289 /* Copy structure isds_BirthInfo recursively */
290 static struct isds_BirthInfo *isds_BirthInfo_duplicate(
291 const struct isds_BirthInfo *template) {
292 struct isds_BirthInfo *new = NULL;
294 if (!template) return NULL;
296 new = calloc(1, sizeof(*new));
297 if (!new) return NULL;
299 FLATDUP_OR_ERROR(new->biDate, template->biDate);
300 STRDUP_OR_ERROR(new->biCity, template->biCity);
301 STRDUP_OR_ERROR(new->biCounty, template->biCounty);
302 STRDUP_OR_ERROR(new->biState, template->biState);
304 return new;
306 error:
307 isds_BirthInfo_free(&new);
308 return NULL;
312 /* Copy structure isds_Address recursively */
313 struct isds_Address *isds_Address_duplicate(
314 const struct isds_Address *template) {
315 struct isds_Address *new = NULL;
317 if (!template) return NULL;
319 new = calloc(1, sizeof(*new));
320 if (!new) return NULL;
322 STRDUP_OR_ERROR(new->adCity, template->adCity);
323 STRDUP_OR_ERROR(new->adStreet, template->adStreet);
324 STRDUP_OR_ERROR(new->adNumberInStreet, template->adNumberInStreet);
325 STRDUP_OR_ERROR(new->adNumberInMunicipality,
326 template->adNumberInMunicipality);
327 STRDUP_OR_ERROR(new->adZipCode, template->adZipCode);
328 STRDUP_OR_ERROR(new->adState, template->adState);
330 return new;
332 error:
333 isds_Address_free(&new);
334 return NULL;
338 /* Copy structure isds_DbOwnerInfo recursively */
339 struct isds_DbOwnerInfo *isds_DbOwnerInfo_duplicate(
340 const struct isds_DbOwnerInfo *template) {
341 struct isds_DbOwnerInfo *new = NULL;
342 if (!template) return NULL;
344 new = calloc(1, sizeof(*new));
345 if (!new) return NULL;
347 STRDUP_OR_ERROR(new->dbID, template->dbID);
348 FLATDUP_OR_ERROR(new->dbType, template->dbType);
349 STRDUP_OR_ERROR(new->ic, template->ic);
351 if (template->personName) {
352 if (!(new->personName =
353 isds_PersonName_duplicate(template->personName)))
354 goto error;
357 STRDUP_OR_ERROR(new->firmName, template->firmName);
359 if (template->birthInfo) {
360 if (!(new->birthInfo =
361 isds_BirthInfo_duplicate(template->birthInfo)))
362 goto error;
365 if (template->address) {
366 if (!(new->address = isds_Address_duplicate(template->address)))
367 goto error;
370 STRDUP_OR_ERROR(new->nationality, template->nationality);
371 STRDUP_OR_ERROR(new->email, template->email);
372 STRDUP_OR_ERROR(new->telNumber, template->telNumber);
373 STRDUP_OR_ERROR(new->identifier, template->identifier);
374 STRDUP_OR_ERROR(new->registryCode, template->registryCode);
375 FLATDUP_OR_ERROR(new->dbState, template->dbState);
376 FLATDUP_OR_ERROR(new->dbEffectiveOVM, template->dbEffectiveOVM);
377 FLATDUP_OR_ERROR(new->dbOpenAddressing, template->dbOpenAddressing);
379 return new;
381 error:
382 isds_DbOwnerInfo_free(&new);
383 return NULL;
387 /* Copy structure isds_DbUserInfo recursively */
388 struct isds_DbUserInfo *isds_DbUserInfo_duplicate(
389 const struct isds_DbUserInfo *template) {
390 struct isds_DbUserInfo *new = NULL;
391 if (!template) return NULL;
393 new = calloc(1, sizeof(*new));
394 if (!new) return NULL;
396 STRDUP_OR_ERROR(new->userID, template->userID);
397 FLATDUP_OR_ERROR(new->userType, template->userType);
398 FLATDUP_OR_ERROR(new->userPrivils, template->userPrivils);
400 if (template->personName) {
401 if (!(new->personName =
402 isds_PersonName_duplicate(template->personName)))
403 goto error;
406 if (template->address) {
407 if (!(new->address = isds_Address_duplicate(template->address)))
408 goto error;
411 FLATDUP_OR_ERROR(new->biDate, template->biDate);
412 STRDUP_OR_ERROR(new->ic, template->ic);
413 STRDUP_OR_ERROR(new->firmName, template->firmName);
414 STRDUP_OR_ERROR(new->caStreet, template->caStreet);
415 STRDUP_OR_ERROR(new->caCity, template->caCity);
416 STRDUP_OR_ERROR(new->caZipCode, template->caZipCode);
418 return new;
420 error:
421 isds_DbUserInfo_free(&new);
422 return NULL;
425 #undef FLATDUP_OR_ERROR
426 #undef STRDUP_OR_ERROR
429 /* Initialize ISDS library.
430 * Global function, must be called before other functions.
431 * If it failes you can not use ISDS library and must call isds_cleanup() to
432 * free partially inititialized global variables. */
433 isds_error isds_init(void) {
434 /* NULL global variables */
435 log_facilities = ILF_ALL;
436 log_level = ILL_WARNING;
438 #if ENABLE_NLS
439 /* Initialize gettext */
440 bindtextdomain(PACKAGE, LOCALEDIR);
441 #endif
443 /* Initialize CURL */
444 if (curl_global_init(CURL_GLOBAL_ALL)) {
445 isds_log(ILF_ISDS, ILL_CRIT, _("CURL library initialization failed\n"));
446 return IE_ERROR;
449 /* Inicialize gpg-error because of gpgme and ksba */
450 if (gpg_err_init()) {
451 isds_log(ILF_ISDS, ILL_CRIT,
452 _("gpg-error library initialization failed\n"));
453 return IE_ERROR;
456 /* Initialize GPGME */
457 if (init_gpgme()) {
458 isds_log(ILF_ISDS, ILL_CRIT,
459 _("GPGME library initialization failed\n"));
460 return IE_ERROR;
463 /* Initialize gcrypt */
464 if (init_gcrypt()) {
465 isds_log(ILF_ISDS, ILL_CRIT,
466 _("gcrypt library initialization failed\n"));
467 return IE_ERROR;
470 /* This can _exit() current program. Find not so assertive check. */
471 LIBXML_TEST_VERSION;
473 /* Check expat */
474 if (init_expat()) {
475 isds_log(ILF_ISDS, ILL_CRIT,
476 _("expat library initialization failed\n"));
477 return IE_ERROR;
480 /* Allocate global variables */
483 return IE_SUCCESS;
487 /* Deinicialize ISDS library.
488 * Global function, must be called as last library function. */
489 isds_error isds_cleanup(void) {
490 /* XML */
491 xmlCleanupParser();
493 /* Curl */
494 curl_global_cleanup();
496 return IE_SUCCESS;
500 /* Return text description of ISDS error */
501 const char *isds_strerror(const isds_error error) {
502 switch (error) {
503 case IE_SUCCESS:
504 return(_("Success")); break;
505 case IE_ERROR:
506 return(_("Unspecified error")); break;
507 case IE_NOTSUP:
508 return(_("Not supported")); break;
509 case IE_INVAL:
510 return(_("Invalid value")); break;
511 case IE_INVALID_CONTEXT:
512 return(_("Invalid context")); break;
513 case IE_NOT_LOGGED_IN:
514 return(_("Not logged in")); break;
515 case IE_CONNECTION_CLOSED:
516 return(_("Connection closed")); break;
517 case IE_TIMED_OUT:
518 return(_("Timed out")); break;
519 case IE_NOEXIST:
520 return(_("Not exist")); break;
521 case IE_NOMEM:
522 return(_("Out of memory")); break;
523 case IE_NETWORK:
524 return(_("Network problem")); break;
525 case IE_HTTP:
526 return(_("HTTP problem")); break;
527 case IE_SOAP:
528 return(_("SOAP problem")); break;
529 case IE_XML:
530 return(_("XML problem")); break;
531 case IE_ISDS:
532 return(_("ISDS server problem")); break;
533 case IE_ENUM:
534 return(_("Invalid enum value")); break;
535 case IE_DATE:
536 return(_("Invalid date value")); break;
537 case IE_2BIG:
538 return(_("Too big")); break;
539 case IE_2SMALL:
540 return(_("Too small")); break;
541 case IE_NOTUNIQ:
542 return(_("Value not unique")); break;
543 case IE_NOTEQUAL:
544 return(_("Values not uqual")); break;
545 case IE_PARTIAL_SUCCESS:
546 return(_("Some suboperations failed")); break;
547 default:
548 return(_("Unknown error"));
553 /* Create ISDS context.
554 * Each context can be used for different sessions to (possibly) differnet
555 * ISDS server with different credentials. */
556 struct isds_ctx *isds_ctx_create(void) {
557 struct isds_ctx *context;
558 context = malloc(sizeof(*context));
559 if (context) memset(context, 0, sizeof(*context));
560 return context;
564 /* Destroy ISDS context and free memmory.
565 * @context will be NULLed on success. */
566 isds_error isds_ctx_free(struct isds_ctx **context) {
567 if (!context || !*context) {
568 return IE_INVALID_CONTEXT;
571 /* Discard credentials */
572 isds_logout(*context);
574 /* Free other structures */
575 free((*context)->tls_verify_server);
576 free((*context)->tls_ca_file);
577 free((*context)->tls_ca_dir);
578 free((*context)->long_message);
580 free(*context);
581 *context = NULL;
582 return IE_SUCCESS;
586 /* Return long message text produced by library fucntion, e.g. detailed error
587 * mesage. Returned pointer is only valid until new library function is
588 * called for the same context. Could be NULL, especially if NULL context is
589 * supplied. Return string is locale encoded. */
590 char *isds_long_message(const struct isds_ctx *context) {
591 if (!context) return NULL;
592 return context->long_message;
596 /* Stores message into context' long_message buffer.
597 * Application can pick the message up using isds_long_message().
598 * NULL @message truncates the buffer but does not deallocate it.
599 * @message is coded in locale encoding */
600 _hidden isds_error isds_log_message(struct isds_ctx *context,
601 const char *message) {
602 char *buffer;
603 size_t length;
605 if (!context) return IE_INVALID_CONTEXT;
607 /* FIXME: Check for integer overflow */
608 length = 1 + ((message) ? strlen(message) : 0);
609 buffer = realloc(context->long_message, length);
610 if (!buffer) return IE_NOMEM;
612 if (message)
613 strcpy(buffer, message);
614 else
615 *buffer = '\0';
617 context->long_message = buffer;
618 return IE_SUCCESS;
622 /* Appends message into context' long_message buffer.
623 * Application can pick the message up using isds_long_message().
624 * NULL message has void effect. */
625 _hidden isds_error isds_append_message(struct isds_ctx *context,
626 const char *message) {
627 char *buffer;
628 size_t old_length, length;
630 if (!context) return IE_INVALID_CONTEXT;
631 if (!message) return IE_SUCCESS;
632 if (!context->long_message)
633 return isds_log_message(context, message);
635 old_length = strlen(context->long_message);
636 /* FIXME: Check for integer overflow */
637 length = 1 + old_length + strlen(message);
638 buffer = realloc(context->long_message, length);
639 if (!buffer) return IE_NOMEM;
641 strcpy(buffer + old_length, message);
643 context->long_message = buffer;
644 return IE_SUCCESS;
648 /* Stores formated message into context' long_message buffer.
649 * Application can pick the message up using isds_long_message(). */
650 _hidden isds_error isds_printf_message(struct isds_ctx *context,
651 const char *format, ...) {
652 va_list ap;
653 int length;
655 if (!context) return IE_INVALID_CONTEXT;
656 va_start(ap, format);
657 length = isds_vasprintf(&(context->long_message), format, ap);
658 va_end(ap);
660 return (length < 0) ? IE_ERROR: IE_SUCCESS;
664 /* Set logging up.
665 * @facilities is bitmask of isds_log_facility values,
666 * @level is verbosity level. */
667 void isds_set_logging(const unsigned int facilities,
668 const isds_log_level level) {
669 log_facilities = facilities;
670 log_level = level;
674 /* Log @message in class @facility with log @level into global log. @message
675 * is printf(3) formating string, variadic arguments may be neccessary.
676 * For debugging purposes. */
677 _hidden isds_error isds_log(const isds_log_facility facility,
678 const isds_log_level level, const char *message, ...) {
679 va_list ap;
681 if (level > log_level) return IE_SUCCESS;
682 if (!(log_facilities & facility)) return IE_SUCCESS;
683 if (!message) return IE_INVAL;
685 /* TODO: Allow to register output function provided by application
686 * (e.g. fprintf to stderr or copy to text area GUI widget). */
688 va_start(ap, message);
689 vfprintf(stderr, message, ap);
690 va_end(ap);
691 /* Line buffered printf is default.
692 * fflush(stderr);*/
694 return IE_SUCCESS;
698 /* Set timeout in miliseconds for each network job like connecting to server
699 * or sending message. Use 0 to disable timeout limits. */
700 isds_error isds_set_timeout(struct isds_ctx *context,
701 const unsigned int timeout) {
702 if (!context) return IE_INVALID_CONTEXT;
704 context->timeout = timeout;
706 if (context->curl) {
707 CURLcode curl_err;
709 curl_err = curl_easy_setopt(context->curl, CURLOPT_NOSIGNAL, 1);
710 if (!curl_err)
711 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT_MS,
712 context->timeout);
713 if (curl_err) return IE_ERROR;
716 return IE_SUCCESS;
720 /* Register callback function libisds calls periodocally during HTTP data
721 * transfer.
722 * @context is session context
723 * @callback is function provided by application libsds will call. See type
724 * defition for @callback argument explanation.
725 * @data is application specific data @callback gets as last argument */
726 isds_error isds_set_progress_callback(struct isds_ctx *context,
727 isds_progress_callback callback, void *data) {
728 if (!context) return IE_INVALID_CONTEXT;
730 context->progress_callback = callback;
731 context->progress_callback_data = data;
733 return IE_SUCCESS;
737 /* Change SSL/TLS settings.
738 * @context is context which setting will be applied to
739 * @option is name of option. It determines the type of last argument. See
740 * isds_tls_option definition for more info.
741 * @... is value of new setting. Type is determined by @option
742 * */
743 isds_error isds_set_tls(struct isds_ctx *context, const isds_tls_option option,
744 ...) {
745 isds_error err = IE_SUCCESS;
746 va_list ap;
747 char *pointer, *string;
749 if (!context) return IE_INVALID_CONTEXT;
751 va_start(ap, option);
753 #define REPLACE_VA_STRING(destination) \
754 string = va_arg(ap, char *); \
755 if (string) { \
756 pointer = realloc((destination), 1 + strlen(string)); \
757 if (!pointer) { err = IE_NOMEM; goto leave; } \
758 strcpy(pointer, string); \
759 (destination) = pointer; \
760 } else { \
761 free(destination); \
762 (destination) = NULL; \
765 switch (option) {
766 case ITLS_VERIFY_SERVER:
767 if (!context->tls_verify_server) {
768 context->tls_verify_server =
769 malloc(sizeof(*context->tls_verify_server));
770 if (!context->tls_verify_server) {
771 err = IE_NOMEM; goto leave;
774 *context->tls_verify_server = (_Bool) (0 != va_arg(ap, int));
775 break;
777 case ITLS_CA_FILE:
778 REPLACE_VA_STRING(context->tls_ca_file);
779 break;
780 case ITLS_CA_DIRECTORY:
781 REPLACE_VA_STRING(context->tls_ca_dir);
782 break;
784 default:
785 err = IE_ENUM; goto leave;
788 #undef REPLACE_VA_STRING
790 leave:
791 va_end(ap);
792 return err;
796 /* Discard credentials.
797 * Only that. It does not cause log out, connection close or similar. */
798 static isds_error discard_credentials(struct isds_ctx *context) {
799 if(!context) return IE_INVALID_CONTEXT;
801 if (context->username) {
802 memset(context->username, 0, strlen(context->username));
803 free(context->username);
804 context->username = NULL;
806 if (context->password) {
807 memset(context->password, 0, strlen(context->password));
808 free(context->password);
809 context->password = NULL;
812 return IE_SUCCESS;
816 /* Connect and log in into ISDS server.
817 * @url is address of ISDS web service
818 * @username is user name of ISDS user
819 * @password is user's secret password
820 * @certificate is NULL terminated string with PEM formated client's
821 * certificate. Use NULL if only password autentication should be performed.
822 * @key is private key for client's certificate as (base64 encoded?) NULL
823 * terminated string. Use NULL if only password autentication is desired.
824 * */
825 isds_error isds_login(struct isds_ctx *context, const char *url,
826 const char *username, const char *password,
827 const char *certificate, const char* key) {
828 isds_error err = IE_NOT_LOGGED_IN;
829 isds_error soap_err;
830 xmlNsPtr isds_ns = NULL;
831 xmlNodePtr request = NULL;
832 xmlNodePtr response = NULL;
834 if (!context) return IE_INVALID_CONTEXT;
835 if (!url || !username || !password) return IE_INVAL;
836 if (certificate || key) return IE_NOTSUP;
838 /* Store configuration */
839 free(context->url);
840 context->url = strdup(url);
841 if (!(context->url))
842 return IE_NOMEM;
844 /* Close connection if already logged in */
845 if (context->curl) {
846 close_connection(context);
849 /* Prepare CURL handle */
850 context->curl = curl_easy_init();
851 if (!(context->curl))
852 return IE_ERROR;
854 /* Build login request */
855 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
856 if (!request) {
857 isds_log_message(context, _("Could build ISDS login request"));
858 return IE_ERROR;
860 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
861 if(!isds_ns) {
862 isds_log_message(context, _("Could not create ISDS name space"));
863 xmlFreeNode(request);
864 return IE_ERROR;
866 xmlSetNs(request, isds_ns);
868 /* Store credentials */
869 /* FIXME: mlock password
870 * (I have a library) */
871 discard_credentials(context);
872 context->username = strdup(username);
873 context->password = strdup(password);
874 if (!(context->username && context->password)) {
875 discard_credentials(context);
876 xmlFreeNode(request);
877 return IE_NOMEM;
880 isds_log(ILF_ISDS, ILL_DEBUG, _("Logging user %s into server %s\n"),
881 username, url);
883 /* Send login request */
884 soap_err = soap(context, "dz", request, &response, NULL, NULL);
886 /* Remove credentials */
887 discard_credentials(context);
889 /* Destroy login request */
890 xmlFreeNode(request);
892 if (soap_err) {
893 xmlFreeNodeList(response);
894 close_connection(context);
895 return soap_err;
898 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
899 * authentication succeeded if soap_err == IE_SUCCESS */
900 err = IE_SUCCESS;
902 xmlFreeNodeList(response);
904 if (!err)
905 isds_log(ILF_ISDS, ILL_DEBUG,
906 _("User %s has been logged into server %s successfully\n"),
907 username, url);
908 return err;
912 /* Log out from ISDS server discards credentials and connection configuration. */
913 isds_error isds_logout(struct isds_ctx *context) {
914 if (!context) return IE_INVALID_CONTEXT;
916 /* Close connection */
917 if (context->curl) {
918 close_connection(context);
920 /* Discard credentials for sure. They should not survive isds_login(),
921 * even successful .*/
922 discard_credentials(context);
923 free(context->url);
924 context->url = NULL;
926 isds_log(ILF_ISDS, ILL_DEBUG, _("Logged out from ISDS server\n"));
927 } else {
928 discard_credentials(context);
930 return IE_SUCCESS;
934 /* Verify connection to ISDS is alive and server is responding.
935 * Sent dumy request to ISDS and expect dummy response. */
936 isds_error isds_ping(struct isds_ctx *context) {
937 isds_error soap_err;
938 xmlNsPtr isds_ns = NULL;
939 xmlNodePtr request = NULL;
940 xmlNodePtr response = NULL;
942 if (!context) return IE_INVALID_CONTEXT;
944 /* Check if connection is established */
945 if (!context->curl) return IE_CONNECTION_CLOSED;
948 /* Build dummy request */
949 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
950 if (!request) {
951 isds_log_message(context, _("Could build ISDS dummy request"));
952 return IE_ERROR;
954 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
955 if(!isds_ns) {
956 isds_log_message(context, _("Could not create ISDS name space"));
957 xmlFreeNode(request);
958 return IE_ERROR;
960 xmlSetNs(request, isds_ns);
962 isds_log(ILF_ISDS, ILL_DEBUG, _("Pinging ISDS server\n"));
964 /* Sent dummy request */
965 soap_err = soap(context, "dz", request, &response, NULL, NULL);
967 /* Destroy login request */
968 xmlFreeNode(request);
970 if (soap_err) {
971 isds_log(ILF_ISDS, ILL_DEBUG,
972 _("ISDS server could not be contacted\n"));
973 xmlFreeNodeList(response);
974 close_connection(context);
975 return soap_err;
978 /* XXX: Untill we don't propagate HTTP code 500 or 4xx, we can be sure
979 * authentication succeeded if soap_err == IE_SUCCESS */
980 /* TODO: ISDS documentation does not specify response body.
981 * However real server sends back DummyOperationResponse */
984 xmlFreeNodeList(response);
986 isds_log(ILF_ISDS, ILL_DEBUG, _("ISDS server alive\n"));
988 return IE_SUCCESS;
992 /* Send bogus request to ISDS.
993 * Just for test purposes */
994 isds_error isds_bogus_request(struct isds_ctx *context) {
995 isds_error err;
996 xmlNsPtr isds_ns = NULL;
997 xmlNodePtr request = NULL;
998 xmlDocPtr response = NULL;
999 xmlChar *code = NULL, *message = NULL;
1001 if (!context) return IE_INVALID_CONTEXT;
1003 /* Check if connection is established */
1004 if (!context->curl) {
1005 /* Testing printf message */
1006 isds_printf_message(context, "%s", _("I said connection closed"));
1007 return IE_CONNECTION_CLOSED;
1011 /* Build dummy request */
1012 request = xmlNewNode(NULL, BAD_CAST "X-BogusOperation");
1013 if (!request) {
1014 isds_log_message(context, _("Could build ISDS bogus request"));
1015 return IE_ERROR;
1017 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1018 if(!isds_ns) {
1019 isds_log_message(context, _("Could not create ISDS name space"));
1020 xmlFreeNode(request);
1021 return IE_ERROR;
1023 xmlSetNs(request, isds_ns);
1025 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending bogus request to ISDS\n"));
1027 /* Sent bogus request */
1028 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
1030 /* Destroy request */
1031 xmlFreeNode(request);
1033 if (err) {
1034 isds_log(ILF_ISDS, ILL_DEBUG,
1035 _("Processing ISDS response on bogus request failed\n"));
1036 xmlFreeDoc(response);
1037 return err;
1040 /* Check for response status */
1041 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
1042 &code, &message, NULL);
1043 if (err) {
1044 isds_log(ILF_ISDS, ILL_DEBUG,
1045 _("ISDS response on bogus request is missing status\n"));
1046 free(code);
1047 free(message);
1048 xmlFreeDoc(response);
1049 return err;
1051 if (xmlStrcmp(code, BAD_CAST "0000")) {
1052 char *code_locale = utf82locale((char*)code);
1053 char *message_locale = utf82locale((char*)message);
1054 isds_log(ILF_ISDS, ILL_DEBUG,
1055 _("Server refused bogus request (code=%s, message=%s)\n"),
1056 code_locale, message_locale);
1057 /* XXX: Literal error messages from ISDS are Czech mesages
1058 * (English sometimes) in UTF-8. It's hard to catch them for
1059 * translation. Successfully gettextized would return in locale
1060 * encoding, unsuccessfully translated would pass in UTF-8. */
1061 isds_log_message(context, message_locale);
1062 free(code_locale);
1063 free(message_locale);
1064 free(code);
1065 free(message);
1066 xmlFreeDoc(response);
1067 return IE_ISDS;
1071 free(code);
1072 free(message);
1073 xmlFreeDoc(response);
1075 isds_log(ILF_ISDS, ILL_DEBUG,
1076 _("Bogus message accepted by server. This should not happen.\n"));
1078 return IE_SUCCESS;
1082 /* Serialize XML subtree to buffer preserving XML indentatition.
1083 * @context is session context
1084 * @subtree is XML element to be serialized (with childern)
1085 * @buffer is automatically reallocated buffer where serialize to
1086 * @length is size of serialized stream in bytes
1087 * @return standard error code, free @buffer in case of error */
1088 static isds_error serialize_subtree(struct isds_ctx *context,
1089 xmlNodePtr subtree, void **buffer, size_t *length) {
1090 isds_error err = IE_SUCCESS;
1091 xmlBufferPtr xml_buffer = NULL;
1092 xmlSaveCtxtPtr save_ctx = NULL;
1093 xmlDocPtr subtree_doc = NULL;
1094 xmlNodePtr subtree_copy;
1095 xmlNsPtr isds_ns;
1096 void *new_buffer;
1098 if (!context) return IE_INVALID_CONTEXT;
1099 if (!buffer) return IE_INVAL;
1100 zfree(*buffer);
1101 if (!subtree || !length) return IE_INVAL;
1103 /* Make temporary XML document with @subtree root element */
1104 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1105 * It can result in not well-formed on invalid XML tree (e.g. name space
1106 * prefix definition can miss. */
1107 /*FIXME */
1109 subtree_doc = xmlNewDoc(BAD_CAST "1.0");
1110 if (!subtree_doc) {
1111 isds_log_message(context, _("Could not build temporary document"));
1112 err = IE_ERROR;
1113 goto leave;
1116 /* XXX: Copy subtree and attach the copy to document.
1117 * One node can not bee attached into more document at the same time.
1118 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1119 * automatically.
1120 * XXX: Check xmlSaveTree() too. */
1121 subtree_copy = xmlCopyNodeList(subtree);
1122 if (!subtree_copy) {
1123 isds_log_message(context, _("Could not copy subtree"));
1124 err = IE_ERROR;
1125 goto leave;
1127 xmlDocSetRootElement(subtree_doc, subtree_copy);
1129 /* Only this way we get namespace definition as @xmlns:isds,
1130 * otherwise we get namespace prefix without definition */
1131 /* FIXME: Don't overwrite original default namespace */
1132 isds_ns = xmlNewNs(subtree_copy, BAD_CAST ISDS_NS, NULL);
1133 if(!isds_ns) {
1134 isds_log_message(context, _("Could not create ISDS name space"));
1135 err = IE_ERROR;
1136 goto leave;
1138 xmlSetNs(subtree_copy, isds_ns);
1141 /* Serialize the document into buffer */
1142 xml_buffer = xmlBufferCreate();
1143 if (!xml_buffer) {
1144 isds_log_message(context, _("Could not create xmlBuffer"));
1145 err = IE_ERROR;
1146 goto leave;
1148 /* Last argument 0 means to not format the XML tree */
1149 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8", 0);
1150 if (!save_ctx) {
1151 isds_log_message(context, _("Could not create XML serializer"));
1152 err = IE_ERROR;
1153 goto leave;
1155 /* XXX: According LibXML documentation, this function does not return
1156 * meaningfull value yet */
1157 xmlSaveDoc(save_ctx, subtree_doc);
1158 if (-1 == xmlSaveFlush(save_ctx)) {
1159 isds_log_message(context,
1160 _("Could not serialize XML subtree"));
1161 err = IE_ERROR;
1162 goto leave;
1164 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1165 * even after xmlSaveFlush(). Thus close it here */
1166 xmlSaveClose(save_ctx); save_ctx = NULL;
1169 /* Store and detach buffer from xml_buffer */
1170 *buffer = xml_buffer->content;
1171 *length = xml_buffer->use;
1172 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1174 /* Shrink buffer */
1175 new_buffer = realloc(*buffer, *length);
1176 if (new_buffer) *buffer = new_buffer;
1178 leave:
1179 if (err) {
1180 zfree(*buffer);
1181 *length = 0;
1184 xmlSaveClose(save_ctx);
1185 xmlBufferFree(xml_buffer);
1186 xmlFreeDoc(subtree_doc); /* Frees subtree_copy, isds_ns etc. */
1187 return err;
1190 #if 0
1191 /* Dump XML subtree to buffer as literral string, not valid XML possibly.
1192 * @context is session context
1193 * @document is original document where @nodeset points to
1194 * @nodeset is XPath node set to dump (recursively)
1195 * @buffer is automarically reallocated buffer where serialize to
1196 * @length is size of serialized stream in bytes
1197 * @return standard error code, free @buffer in case of error */
1198 static isds_error dump_nodeset(struct isds_ctx *context,
1199 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1200 void **buffer, size_t *length) {
1201 isds_error err = IE_SUCCESS;
1202 xmlBufferPtr xml_buffer = NULL;
1203 void *new_buffer;
1205 if (!context) return IE_INVALID_CONTEXT;
1206 if (!buffer) return IE_INVAL;
1207 zfree(*buffer);
1208 if (!document || !nodeset || !length) return IE_INVAL;
1209 *length = 0;
1211 /* Empty node set results into NULL buffer */
1212 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1213 goto leave;
1216 /* Resuling the document into buffer */
1217 xml_buffer = xmlBufferCreate();
1218 if (!xml_buffer) {
1219 isds_log_message(context, _("Could not create xmlBuffer"));
1220 err = IE_ERROR;
1221 goto leave;
1224 /* Itearate over all nodes */
1225 for (int i = 0; i < nodeset->nodeNr; i++) {
1226 /* Serialize node.
1227 * XXX: xmlNodeDump() appends to xml_buffer. */
1228 if (-1 ==
1229 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1230 isds_log_message(context, _("Could not dump XML node"));
1231 err = IE_ERROR;
1232 goto leave;
1236 /* Store and detach buffer from xml_buffer */
1237 *buffer = xml_buffer->content;
1238 *length = xml_buffer->use;
1239 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1241 /* Shrink buffer */
1242 new_buffer = realloc(*buffer, *length);
1243 if (new_buffer) *buffer = new_buffer;
1246 leave:
1247 if (err) {
1248 zfree(*buffer);
1249 *length = 0;
1252 xmlBufferFree(xml_buffer);
1253 return err;
1255 #endif
1257 #if 0
1258 /* Dump XML subtree to buffer as literral string, not valid XML possibly.
1259 * @context is session context
1260 * @document is original document where @nodeset points to
1261 * @nodeset is XPath node set to dump (recursively)
1262 * @buffer is automarically reallocated buffer where serialize to
1263 * @length is size of serialized stream in bytes
1264 * @return standard error code, free @buffer in case of error */
1265 static isds_error dump_nodeset(struct isds_ctx *context,
1266 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1267 void **buffer, size_t *length) {
1268 isds_error err = IE_SUCCESS;
1269 xmlBufferPtr xml_buffer = NULL;
1270 xmlSaveCtxtPtr save_ctx = NULL;
1271 void *new_buffer;
1273 if (!context) return IE_INVALID_CONTEXT;
1274 if (!buffer) return IE_INVAL;
1275 zfree(*buffer);
1276 if (!document || !nodeset || !length) return IE_INVAL;
1277 *length = 0;
1279 /* Empty node set results into NULL buffer */
1280 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1281 goto leave;
1284 /* Resuling the document into buffer */
1285 xml_buffer = xmlBufferCreate();
1286 if (!xml_buffer) {
1287 isds_log_message(context, _("Could not create xmlBuffer"));
1288 err = IE_ERROR;
1289 goto leave;
1291 if (xmlSubstituteEntitiesDefault(1)) {
1292 isds_log_message(context, _("Could not disable attribute escaping"));
1293 err = IE_ERROR;
1294 goto leave;
1296 /* Last argument means:
1297 * 0 to not format the XML tree
1298 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1299 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8",
1300 XML_SAVE_NO_DECL|XML_SAVE_NO_EMPTY|XML_SAVE_NO_XHTML);
1301 if (!save_ctx) {
1302 isds_log_message(context, _("Could not create XML serializer"));
1303 err = IE_ERROR;
1304 goto leave;
1306 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1307 isds_log_message(context, _("Could not disable attribute escaping"));
1308 err = IE_ERROR;
1309 goto leave;
1313 /* Itearate over all nodes */
1314 for (int i = 0; i < nodeset->nodeNr; i++) {
1315 /* Serialize node.
1316 * XXX: xmlNodeDump() appends to xml_buffer. */
1317 /*if (-1 ==
1318 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1320 /* XXX: According LibXML documentation, this function does not return
1321 * meaningfull value yet */
1322 xmlSaveTree(save_ctx, nodeset->nodeTab[i]);
1323 if (-1 == xmlSaveFlush(save_ctx)) {
1324 isds_log_message(context,
1325 _("Could not serialize XML subtree"));
1326 err = IE_ERROR;
1327 goto leave;
1331 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1332 * even after xmlSaveFlush(). Thus close it here */
1333 xmlSaveClose(save_ctx); save_ctx = NULL;
1335 /* Store and detach buffer from xml_buffer */
1336 *buffer = xml_buffer->content;
1337 *length = xml_buffer->use;
1338 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1340 /* Shrink buffer */
1341 new_buffer = realloc(*buffer, *length);
1342 if (new_buffer) *buffer = new_buffer;
1344 leave:
1345 if (err) {
1346 zfree(*buffer);
1347 *length = 0;
1350 xmlSaveClose(save_ctx);
1351 xmlBufferFree(xml_buffer);
1352 return err;
1354 #endif
1357 /* Convert UTF-8 @string represantion of ISDS dbType to enum @type */
1358 static isds_error string2isds_DbType(xmlChar *string, isds_DbType *type) {
1359 if (!string || !type) return IE_INVAL;
1361 if (!xmlStrcmp(string, BAD_CAST "FO"))
1362 *type = DBTYPE_FO;
1363 else if (!xmlStrcmp(string, BAD_CAST "PFO"))
1364 *type = DBTYPE_PFO;
1365 else if (!xmlStrcmp(string, BAD_CAST "PFO_ADVOK"))
1366 *type = DBTYPE_PFO_ADVOK;
1367 else if (!xmlStrcmp(string, BAD_CAST "PFO_DANPOR"))
1368 *type = DBTYPE_PFO_DANPOR;
1369 else if (!xmlStrcmp(string, BAD_CAST "PFO_INSSPR"))
1370 *type = DBTYPE_PFO_INSSPR;
1371 else if (!xmlStrcmp(string, BAD_CAST "PO"))
1372 *type = DBTYPE_PO;
1373 else if (!xmlStrcmp(string, BAD_CAST "PO_ZAK"))
1374 *type = DBTYPE_PO_ZAK;
1375 else if (!xmlStrcmp(string, BAD_CAST "PO_REQ"))
1376 *type = DBTYPE_PO_REQ;
1377 else if (!xmlStrcmp(string, BAD_CAST "OVM"))
1378 *type = DBTYPE_OVM;
1379 else if (!xmlStrcmp(string, BAD_CAST "OVM_NOTAR"))
1380 *type = DBTYPE_OVM_NOTAR;
1381 else if (!xmlStrcmp(string, BAD_CAST "OVM_EXEKUT"))
1382 *type = DBTYPE_OVM_EXEKUT;
1383 else if (!xmlStrcmp(string, BAD_CAST "OVM_REQ"))
1384 *type = DBTYPE_OVM_REQ;
1385 else
1386 return IE_ENUM;
1387 return IE_SUCCESS;
1391 /* Convert ISDS dbType enum @type to UTF-8 string.
1392 * @Return pointer to static string, or NULL if unkwnow enum value */
1393 static const xmlChar *isds_DbType2string(const isds_DbType type) {
1394 switch(type) {
1395 case DBTYPE_FO: return(BAD_CAST "FO"); break;
1396 case DBTYPE_PFO: return(BAD_CAST "PFO"); break;
1397 case DBTYPE_PFO_ADVOK: return(BAD_CAST "PFO_ADVOK"); break;
1398 case DBTYPE_PFO_DANPOR: return(BAD_CAST "PFO_DANPOR"); break;
1399 case DBTYPE_PFO_INSSPR: return(BAD_CAST "PFO_INSSPR"); break;
1400 case DBTYPE_PO: return(BAD_CAST "PO"); break;
1401 case DBTYPE_PO_ZAK: return(BAD_CAST "PO_ZAK"); break;
1402 case DBTYPE_PO_REQ: return(BAD_CAST "PO_REQ"); break;
1403 case DBTYPE_OVM: return(BAD_CAST "OVM"); break;
1404 case DBTYPE_OVM_NOTAR: return(BAD_CAST "OVM_NOTAR"); break;
1405 case DBTYPE_OVM_EXEKUT: return(BAD_CAST "OVM_EXEKUT"); break;
1406 case DBTYPE_OVM_REQ: return(BAD_CAST "OVM_REQ"); break;
1407 default: return NULL; break;
1412 /* Convert UTF-8 @string represantion of ISDS userType to enum @type */
1413 static isds_error string2isds_UserType(xmlChar *string, isds_UserType *type) {
1414 if (!string || !type) return IE_INVAL;
1416 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
1417 *type = USERTYPE_PRIMARY;
1418 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
1419 *type = USERTYPE_ENTRUSTED;
1420 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
1421 *type = USERTYPE_ADMINISTRATOR;
1422 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
1423 *type = USERTYPE_OFFICIAL;
1424 else
1425 return IE_ENUM;
1426 return IE_SUCCESS;
1430 /* Convert ISDS userType enum @type to UTF-8 string.
1431 * @Return pointer to static string, or NULL if unkwnow enum value */
1432 static const xmlChar *isds_UserType2string(const isds_UserType type) {
1433 switch(type) {
1434 case USERTYPE_PRIMARY: return(BAD_CAST "PRIMARY_USER"); break;
1435 case USERTYPE_ENTRUSTED: return(BAD_CAST "ENTRUSTED_USER"); break;
1436 case USERTYPE_ADMINISTRATOR: return(BAD_CAST "ADMINISTRATOR"); break;
1437 case USERTYPE_OFFICIAL: return(BAD_CAST "OFFICIAL"); break;
1438 default: return NULL; break;
1443 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
1444 * @Return pointer to static string, or NULL if unkwnow enum value */
1445 static const xmlChar *isds_FileMetaType2string(const isds_FileMetaType type) {
1446 switch(type) {
1447 case FILEMETATYPE_MAIN: return(BAD_CAST "main"); break;
1448 case FILEMETATYPE_ENCLOSURE: return(BAD_CAST "enclosure"); break;
1449 case FILEMETATYPE_SIGNATURE: return(BAD_CAST "signature"); break;
1450 case FILEMETATYPE_META: return(BAD_CAST "meta"); break;
1451 default: return NULL; break;
1456 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
1457 * @Return IE_ENUM if @string is not valid enum member */
1458 static isds_error string2isds_FileMetaType(const xmlChar *string,
1459 isds_FileMetaType *type) {
1460 if (!string || !type) return IE_INVAL;
1462 if (!xmlStrcmp(string, BAD_CAST "main"))
1463 *type = FILEMETATYPE_MAIN;
1464 else if (!xmlStrcmp(string, BAD_CAST "enclosure"))
1465 *type = FILEMETATYPE_ENCLOSURE;
1466 else if (!xmlStrcmp(string, BAD_CAST "signature"))
1467 *type = FILEMETATYPE_SIGNATURE;
1468 else if (!xmlStrcmp(string, BAD_CAST "meta"))
1469 *type = FILEMETATYPE_META;
1470 else
1471 return IE_ENUM;
1472 return IE_SUCCESS;
1476 /* Convert UTF-8 @string to ISDS hash @algorithm.
1477 * @Return IE_ENUM if @string is not valid enum member */
1478 static isds_error string2isds_hash_algorithm(const xmlChar *string,
1479 isds_hash_algorithm *algorithm) {
1480 if (!string || !algorithm) return IE_INVAL;
1482 if (!xmlStrcmp(string, BAD_CAST "MD5"))
1483 *algorithm = HASH_ALGORITHM_MD5;
1484 else if (!xmlStrcmp(string, BAD_CAST "SHA-1"))
1485 *algorithm = HASH_ALGORITHM_SHA_1;
1486 else if (!xmlStrcmp(string, BAD_CAST "SHA-224"))
1487 *algorithm = HASH_ALGORITHM_SHA_224;
1488 else if (!xmlStrcmp(string, BAD_CAST "SHA-256"))
1489 *algorithm = HASH_ALGORITHM_SHA_256;
1490 else if (!xmlStrcmp(string, BAD_CAST "SHA-384"))
1491 *algorithm = HASH_ALGORITHM_SHA_384;
1492 else if (!xmlStrcmp(string, BAD_CAST "SHA-512"))
1493 *algorithm = HASH_ALGORITHM_SHA_512;
1494 else
1495 return IE_ENUM;
1496 return IE_SUCCESS;
1500 /* Convert UTF-8 @string represantion of ISO 8601 date to @time.
1501 * XXX: Not all ISO formats are supported */
1502 static isds_error datestring2tm(const xmlChar *string, struct tm *time) {
1503 char *offset;
1504 if (!string || !time) return IE_INVAL;
1506 /* xsd:date is ISO 8601 string, thus ASCII */
1507 offset = strptime((char*)string, "%Y-%m-%d", time);
1508 if (offset && *offset == '\0')
1509 return IE_SUCCESS;
1511 offset = strptime((char*)string, "%Y%m%d", time);
1512 if (offset && *offset == '\0')
1513 return IE_SUCCESS;
1515 offset = strptime((char*)string, "%Y-%j", time);
1516 if (offset && *offset == '\0')
1517 return IE_SUCCESS;
1519 return IE_NOTSUP;
1523 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
1524 static isds_error tm2datestring(const struct tm *time, xmlChar **string) {
1525 if (!time || !string) return IE_INVAL;
1527 if (-1 == isds_asprintf((char **) string, "%d-%02d-%02d",
1528 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
1529 return IE_ERROR;
1531 return IE_SUCCESS;
1535 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
1536 * respects the @time microseconds too. */
1537 static isds_error timeval2timestring(const struct timeval *time,
1538 xmlChar **string) {
1539 struct tm broken;
1541 if (!time || !string) return IE_INVAL;
1543 if (!gmtime_r(&time->tv_sec, &broken)) return IE_DATE;
1544 if (time->tv_usec < 0 || time->tv_usec > 999999) return IE_DATE;
1546 /* TODO: small negative year should be formated as "-0012". This is not
1547 * true for glibc "%04d". We should implement it.
1548 * TODO: What's type of time->tv_usec exactly? Unsigned? Absolute?
1549 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
1550 if (-1 == isds_asprintf((char **) string,
1551 "%04d-%02d-%02dT%02d:%02d:%02d.%06ld",
1552 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
1553 broken.tm_hour, broken.tm_min, broken.tm_sec,
1554 time->tv_usec))
1555 return IE_ERROR;
1557 return IE_SUCCESS;
1561 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
1562 * It respects microseconds too.
1563 * In case of error, @time will be freed. */
1564 static isds_error timestring2timeval(const xmlChar *string,
1565 struct timeval **time) {
1566 struct tm broken;
1567 char *offset, *delim, *endptr;
1568 char subseconds[7];
1569 int offset_hours, offset_minutes;
1570 int i;
1572 if (!time) return IE_INVAL;
1574 memset(&broken, 0, sizeof(broken));
1576 if (!*time) {
1577 *time = calloc(1, sizeof(**time));
1578 if (!*time) return IE_NOMEM;
1579 } else {
1580 memset(*time, 0, sizeof(**time));
1584 /* xsd:date is ISO 8601 string, thus ASCII */
1585 /*TODO: negative year */
1587 /* Parse date and time without subseconds and offset */
1588 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
1589 if (!offset) {
1590 free(*time); *time = NULL;
1591 return IE_DATE;
1594 /* Get subseconds */
1595 if (*offset == '.' ) {
1596 offset++;
1598 /* Copy first 6 digits, padd it with zeros.
1599 * XXX: It truncates longer number, no round.
1600 * Current server implementation uses only milisecond resolution. */
1601 /* TODO: isdigit() is locale sensitive */
1602 for (i = 0;
1603 i < sizeof(subseconds)/sizeof(char) - 1 && isdigit(*offset);
1604 i++, offset++) {
1605 subseconds[i] = *offset;
1607 for (; i < sizeof(subseconds)/sizeof(char) - 1; i++) {
1608 subseconds[i] = '0';
1610 subseconds[6] = '\0';
1612 /* Convert it into integer */
1613 (*time)->tv_usec = strtol(subseconds, &endptr, 10);
1614 if (*endptr != '\0' || (*time)->tv_usec == LONG_MIN ||
1615 (*time)->tv_usec == LONG_MAX) {
1616 free(*time); *time = NULL;
1617 return IE_DATE;
1620 /* move to the zone offset delimiter */
1621 delim = strchr(offset, '-');
1622 if (!delim)
1623 delim = strchr(offset, '+');
1624 offset = delim;
1627 /* Get zone offset */
1628 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
1629 * "" equals to "Z" and it means UTC zone. */
1630 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
1631 * colon separator */
1632 if (*offset == '-' || *offset == '+') {
1633 offset++;
1634 if (2 != sscanf(offset, "%2d:%2d", &offset_hours, &offset_minutes)) {
1635 free(*time); *time = NULL;
1636 return IE_DATE;
1638 broken.tm_hour -= offset_hours;
1639 broken.tm_min -= offset_minutes * ((offset_hours<0) ? -1 : 1);
1642 /* Convert to time_t */
1643 switch_tz_to_utc();
1644 (*time)->tv_sec = mktime(&broken);
1645 switch_tz_to_native();
1646 if ((*time)->tv_sec == (time_t) -1) {
1647 free(*time); *time = NULL;
1648 return IE_DATE;
1651 return IE_SUCCESS;
1655 /* Convert unsigned int into isds_message_status.
1656 * @context is session context
1657 * @number is pointer to number value. NULL will be treated as invalid value.
1658 * @status is automatically reallocated status
1659 * @return IE_SUCCESS, or error code and free status */
1660 static isds_error uint2isds_message_status(struct isds_ctx *context,
1661 const unsigned long int *number, isds_message_status **status) {
1662 if (!context) return IE_INVALID_CONTEXT;
1663 if (!status) return IE_INVAL;
1665 free(*status); *status = NULL;
1666 if (!number) return IE_INVAL;
1668 if (*number < 1 || *number > 10) {
1669 isds_printf_message(context, _("Invalid messsage status value: %lu"),
1670 *number);
1671 return IE_ENUM;
1674 *status = malloc(sizeof(**status));
1675 if (!*status) return IE_NOMEM;
1677 **status = 1 << *number;
1678 return IE_SUCCESS;
1682 /* Convert event description string into isds_event memebers type and
1683 * description
1684 * @string is raw event decsription starting with event prefix
1685 * @event is structure where to store type and stripped description to
1686 * @return standard error code, unkown prefix is not classified as an error. */
1687 static isds_error eventstring2event(const xmlChar *string,
1688 struct isds_event* event) {
1689 const xmlChar *known_prefixes[] = {
1690 BAD_CAST "EV1:",
1691 BAD_CAST "EV2:",
1692 BAD_CAST "EV3:",
1693 BAD_CAST "EV4:"
1695 const isds_event_type types[] = {
1696 EVENT_ACCEPTED_BY_RECIPIENT,
1697 EVENT_ACCEPTED_BY_FICTION,
1698 EVENT_UNDELIVERABLE,
1699 EVENT_COMMERCIAL_ACCEPTED
1701 unsigned int index;
1702 size_t length;
1704 if (!string || !event) return IE_INVAL;
1706 if (!event->type) {
1707 event->type = malloc(sizeof(*event->type));
1708 if (!(event->type)) return IE_NOMEM;
1710 zfree(event->description);
1712 for (index = 0; index < sizeof(known_prefixes)/sizeof(known_prefixes[0]);
1713 index++) {
1714 length = xmlUTF8Strlen(known_prefixes[index]);
1716 if (!xmlStrncmp(string, known_prefixes[index], length)) {
1717 /* Prefix is known */
1718 *event->type = types[index];
1720 /* Strip prefix from description and spaces */
1721 /* TODO: Recognize all wite spaces from UCS blank class and
1722 * operate on UTF-8 chars. */
1723 for (; string[length] != '\0' && string[length] == ' '; length++);
1724 event->description = strdup((char *) (string + length));
1725 if (!(event->description)) return IE_NOMEM;
1727 return IE_SUCCESS;
1731 /* Unknown event prefix.
1732 * XSD allows any string */
1733 char *string_locale = utf82locale((char *) string);
1734 isds_log(ILF_ISDS, ILL_WARNING,
1735 _("Uknown delivery info event prefix: %s\n"), string_locale);
1736 free(string_locale);
1738 *event->type = EVENT_UKNOWN;
1739 event->description = strdup((char *) string);
1740 if (!(event->description)) return IE_NOMEM;
1742 return IE_SUCCESS;
1746 /* Following EXTRACT_* macros expects @result, @xpath_ctx, @err, @context
1747 * and leave lable */
1748 #define EXTRACT_STRING(element, string) { \
1749 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
1750 if (!result) { \
1751 err = IE_ERROR; \
1752 goto leave; \
1754 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
1755 if (result->nodesetval->nodeNr > 1) { \
1756 isds_printf_message(context, _("Multiple %s element"), element); \
1757 err = IE_ERROR; \
1758 goto leave; \
1760 (string) = (char *) \
1761 xmlXPathCastNodeSetToString(result->nodesetval); \
1762 if (!(string)) { \
1763 err = IE_ERROR; \
1764 goto leave; \
1769 #define EXTRACT_BOOLEAN(element, booleanPtr) \
1771 char *string = NULL; \
1772 EXTRACT_STRING(element, string); \
1774 if (string) { \
1775 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
1776 if (!(booleanPtr)) { \
1777 free(string); \
1778 err = IE_NOMEM; \
1779 goto leave; \
1782 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
1783 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
1784 *(booleanPtr) = 1; \
1785 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
1786 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
1787 *(booleanPtr) = 0; \
1788 else { \
1789 char *string_locale = utf82locale((char*)string); \
1790 isds_printf_message(context, \
1791 _("%s value is not valid boolean: "), \
1792 element, string_locale); \
1793 free(string_locale); \
1794 free(string); \
1795 err = IE_ERROR; \
1796 goto leave; \
1799 free(string); \
1803 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
1805 char *string = NULL; \
1806 EXTRACT_STRING(element, string); \
1807 if (string) { \
1808 long int number; \
1809 char *endptr; \
1811 number = strtol((char*)string, &endptr, 10); \
1813 if (*endptr != '\0') { \
1814 char *string_locale = utf82locale((char *)string); \
1815 isds_printf_message(context, \
1816 _("%s is not valid integer: %s"), \
1817 element, string_locale); \
1818 free(string_locale); \
1819 free(string); \
1820 err = IE_ISDS; \
1821 goto leave; \
1824 if (number == LONG_MIN || number == LONG_MAX) { \
1825 char *string_locale = utf82locale((char *)string); \
1826 isds_printf_message(context, \
1827 _("%s value out of range of long int: %s"), \
1828 element, string_locale); \
1829 free(string_locale); \
1830 free(string); \
1831 err = IE_ERROR; \
1832 goto leave; \
1835 free(string); string = NULL; \
1837 if (!(preallocated)) { \
1838 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
1839 if (!(longintPtr)) { \
1840 err = IE_NOMEM; \
1841 goto leave; \
1844 *(longintPtr) = number; \
1848 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
1850 char *string = NULL; \
1851 EXTRACT_STRING(element, string); \
1852 if (string) { \
1853 long int number; \
1854 char *endptr; \
1856 number = strtol((char*)string, &endptr, 10); \
1858 if (*endptr != '\0') { \
1859 char *string_locale = utf82locale((char *)string); \
1860 isds_printf_message(context, \
1861 _("%s is not valid integer: %s"), \
1862 element, string_locale); \
1863 free(string_locale); \
1864 free(string); \
1865 err = IE_ISDS; \
1866 goto leave; \
1869 if (number == LONG_MIN || number == LONG_MAX) { \
1870 char *string_locale = utf82locale((char *)string); \
1871 isds_printf_message(context, \
1872 _("%s value out of range of long int: %s"), \
1873 element, string_locale); \
1874 free(string_locale); \
1875 free(string); \
1876 err = IE_ERROR; \
1877 goto leave; \
1880 free(string); string = NULL; \
1881 if (number < 0) { \
1882 isds_printf_message(context, \
1883 _("%s value is negative: %ld"), element, number); \
1884 err = IE_ERROR; \
1885 goto leave; \
1888 if (!(preallocated)) { \
1889 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
1890 if (!(ulongintPtr)) { \
1891 err = IE_NOMEM; \
1892 goto leave; \
1895 *(ulongintPtr) = number; \
1899 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
1900 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
1901 NULL); \
1902 if ((required) && (!string)) { \
1903 char *attribute_locale = utf82locale(attribute); \
1904 char *element_locale = utf82locale((char *)xpath_ctx->node->name); \
1905 isds_printf_message(context, \
1906 _("Could not extract required %s attribute value from " \
1907 "%s element"), attribute_locale, element_locale); \
1908 free(element_locale); \
1909 free(attribute_locale); \
1910 err = IE_ERROR; \
1911 goto leave; \
1916 #define INSERT_STRING(parent, element, string) \
1918 node = xmlNewTextChild(parent, NULL, BAD_CAST (element), \
1919 (xmlChar *) (string)); \
1920 if (!node) { \
1921 isds_printf_message(context, \
1922 _("Could not add %s child to %s element"), \
1923 element, (parent)->name); \
1924 err = IE_ERROR; \
1925 goto leave; \
1929 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
1931 if (boolean) { INSERT_STRING(parent, element, "true"); } \
1932 else { INSERT_STRING(parent, element, "false"); } \
1935 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
1937 if (booleanPtr) { \
1938 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
1939 } else { \
1940 INSERT_STRING(parent, element, NULL); \
1944 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
1945 if ((longintPtr)) { \
1946 /* FIXME: locale sensitive */ \
1947 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
1948 err = IE_NOMEM; \
1949 goto leave; \
1951 INSERT_STRING(parent, element, buffer) \
1952 free(buffer); (buffer) = NULL; \
1953 } else { INSERT_STRING(parent, element, NULL) } \
1956 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
1957 if ((ulongintPtr)) { \
1958 /* FIXME: locale sensitive */ \
1959 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
1960 err = IE_NOMEM; \
1961 goto leave; \
1963 INSERT_STRING(parent, element, buffer) \
1964 free(buffer); (buffer) = NULL; \
1965 } else { INSERT_STRING(parent, element, NULL) } \
1968 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
1970 /* FIXME: locale sensitive */ \
1971 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
1972 err = IE_NOMEM; \
1973 goto leave; \
1975 INSERT_STRING(parent, element, buffer) \
1976 free(buffer); (buffer) = NULL; \
1979 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
1981 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
1982 (xmlChar *) (string)); \
1983 if (!attribute_node) { \
1984 isds_printf_message(context, _("Could not add %s " \
1985 "attribute to %s element"), \
1986 (attribute), (parent)->name); \
1987 err = IE_ERROR; \
1988 goto leave; \
1992 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
1993 if (string) { \
1994 int length = xmlUTF8Strlen((xmlChar *) (string)); \
1995 if (length > (maximum)) { \
1996 isds_printf_message(context, \
1997 _("%s has more than %d characters"), (name), (maximum)); \
1998 err = IE_2BIG; \
1999 goto leave; \
2001 if (length < (minimum)) { \
2002 isds_printf_message(context, \
2003 _("%s has less than %d characters"), (name), (minimum)); \
2004 err = IE_2SMALL; \
2005 goto leave; \
2010 #define INSERT_ELEMENT(child, parent, element) \
2012 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2013 if (!(child)) { \
2014 isds_printf_message(context, \
2015 _("Could not add %s child to %s element"), \
2016 (element), (parent)->name); \
2017 err = IE_ERROR; \
2018 goto leave; \
2023 /* Find child element by name in given XPath context and switch context onto
2024 * it. The child must be uniq and must exist. Otherwise failes.
2025 * @context is ISDS context
2026 * @child is child element name
2027 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2028 * into it child. In error case, the @xpath_ctx keeps original value. */
2029 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
2030 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
2031 isds_error err = IE_SUCCESS;
2032 xmlXPathObjectPtr result = NULL;
2034 if (!context) return IE_INVALID_CONTEXT;
2035 if (!child || !xpath_ctx) return IE_INVAL;
2037 /* Find child */
2038 result = xmlXPathEvalExpression(child, xpath_ctx);
2039 if (!result) {
2040 err = IE_XML;
2041 goto leave;
2044 /* No match */
2045 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2046 char *parent_locale = utf82locale((char*) xpath_ctx->node->name);
2047 char *child_locale = utf82locale((char*) child);
2048 isds_printf_message(context,
2049 _("%s element does not contain %s child"),
2050 parent_locale, child_locale);
2051 free(child_locale);
2052 free(parent_locale);
2053 err = IE_NOEXIST;
2054 goto leave;
2057 /* More matches */
2058 if (result->nodesetval->nodeNr > 1) {
2059 char *parent_locale = utf82locale((char*) xpath_ctx->node->name);
2060 char *child_locale = utf82locale((char*) child);
2061 isds_printf_message(context,
2062 _("%s element contains multiple %s childs"),
2063 parent_locale, child_locale);
2064 free(child_locale);
2065 free(parent_locale);
2066 err = IE_NOTUNIQ;
2067 goto leave;
2070 /* Switch context */
2071 xpath_ctx->node = result->nodesetval->nodeTab[0];
2073 leave:
2074 xmlXPathFreeObject(result);
2075 return err;
2080 /* Find and convert XSD:gPersonName group in current node into structure
2081 * @context is ISDS context
2082 * @personName is automically reallocated person name structure. If no member
2083 * value is found, will be freed.
2084 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2085 * elements
2086 * In case of error @personName will be freed. */
2087 static isds_error extract_gPersonName(struct isds_ctx *context,
2088 struct isds_PersonName **personName, xmlXPathContextPtr xpath_ctx) {
2089 isds_error err = IE_SUCCESS;
2090 xmlXPathObjectPtr result = NULL;
2092 if (!context) return IE_INVALID_CONTEXT;
2093 if (!personName) return IE_INVAL;
2094 isds_PersonName_free(personName);
2095 if (!xpath_ctx) return IE_INVAL;
2098 *personName = calloc(1, sizeof(**personName));
2099 if (!*personName) {
2100 err = IE_NOMEM;
2101 goto leave;
2104 EXTRACT_STRING("isds:pnFirstName", (*personName)->pnFirstName);
2105 EXTRACT_STRING("isds:pnMiddleName", (*personName)->pnMiddleName);
2106 EXTRACT_STRING("isds:pnLastName", (*personName)->pnLastName);
2107 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName)->pnLastNameAtBirth);
2109 if (!(*personName)->pnFirstName && !(*personName)->pnMiddleName &&
2110 !(*personName)->pnLastName && !(*personName)->pnLastNameAtBirth)
2111 isds_PersonName_free(personName);
2113 leave:
2114 if (err) isds_PersonName_free(personName);
2115 xmlXPathFreeObject(result);
2116 return err;
2120 /* Find and convert XSD:gAddress group in current node into structure
2121 * @context is ISDS context
2122 * @address is automically reallocated address structure. If no member
2123 * value is found, will be freed.
2124 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2125 * elements
2126 * In case of error @address will be freed. */
2127 static isds_error extract_gAddress(struct isds_ctx *context,
2128 struct isds_Address **address, xmlXPathContextPtr xpath_ctx) {
2129 isds_error err = IE_SUCCESS;
2130 xmlXPathObjectPtr result = NULL;
2132 if (!context) return IE_INVALID_CONTEXT;
2133 if (!address) return IE_INVAL;
2134 isds_Address_free(address);
2135 if (!xpath_ctx) return IE_INVAL;
2138 *address = calloc(1, sizeof(**address));
2139 if (!*address) {
2140 err = IE_NOMEM;
2141 goto leave;
2144 EXTRACT_STRING("isds:adCity", (*address)->adCity);
2145 EXTRACT_STRING("isds:adStreet", (*address)->adStreet);
2146 EXTRACT_STRING("isds:adNumberInStreet", (*address)->adNumberInStreet);
2147 EXTRACT_STRING("isds:adNumberInMunicipality",
2148 (*address)->adNumberInMunicipality);
2149 EXTRACT_STRING("isds:adZipCode", (*address)->adZipCode);
2150 EXTRACT_STRING("isds:adState", (*address)->adState);
2152 if (!(*address)->adCity && !(*address)->adStreet &&
2153 !(*address)->adNumberInStreet &&
2154 !(*address)->adNumberInMunicipality &&
2155 !(*address)->adZipCode && !(*address)->adState)
2156 isds_Address_free(address);
2158 leave:
2159 if (err) isds_Address_free(address);
2160 xmlXPathFreeObject(result);
2161 return err;
2165 /* Find and convert isds:biDate element in current node into structure
2166 * @context is ISDS context
2167 * @biDate is automically reallocated birth date structure. If no member
2168 * value is found, will be freed.
2169 * @xpath_ctx is XPath context with current node as parent for isds:biDate
2170 * element
2171 * In case of error @biDate will be freed. */
2172 static isds_error extract_BiDate(struct isds_ctx *context,
2173 struct tm **biDate, xmlXPathContextPtr xpath_ctx) {
2174 isds_error err = IE_SUCCESS;
2175 xmlXPathObjectPtr result = NULL;
2176 char *string = NULL;
2178 if (!context) return IE_INVALID_CONTEXT;
2179 if (!biDate) return IE_INVAL;
2180 zfree(*biDate);
2181 if (!xpath_ctx) return IE_INVAL;
2183 EXTRACT_STRING("isds:biDate", string);
2184 if (string) {
2185 *biDate = calloc(1, sizeof(**biDate));
2186 if (!*biDate) {
2187 err = IE_NOMEM;
2188 goto leave;
2190 err = datestring2tm((xmlChar *)string, *biDate);
2191 if (err) {
2192 if (err == IE_NOTSUP) {
2193 err = IE_ISDS;
2194 char *string_locale = utf82locale(string);
2195 isds_printf_message(context,
2196 _("Invalid isds:biDate value: %s"), string_locale);
2197 free(string_locale);
2199 goto leave;
2203 leave:
2204 if (err) zfree(*biDate);
2205 free(string);
2206 xmlXPathFreeObject(result);
2207 return err;
2211 /* Convert isds:dBOwnerInfo XML tree into structure
2212 * @context is ISDS context
2213 * @db_owner_info is automically reallocated box owner info structure
2214 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
2215 * In case of error @db_owner_info will be freed. */
2216 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
2217 struct isds_DbOwnerInfo **db_owner_info,
2218 xmlXPathContextPtr xpath_ctx) {
2219 isds_error err = IE_SUCCESS;
2220 xmlXPathObjectPtr result = NULL;
2221 char *string = NULL;
2223 if (!context) return IE_INVALID_CONTEXT;
2224 if (!db_owner_info) return IE_INVAL;
2225 isds_DbOwnerInfo_free(db_owner_info);
2226 if (!xpath_ctx) return IE_INVAL;
2229 *db_owner_info = calloc(1, sizeof(**db_owner_info));
2230 if (!*db_owner_info) {
2231 err = IE_NOMEM;
2232 goto leave;
2235 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
2237 EXTRACT_STRING("isds:dbType", string);
2238 if (string) {
2239 (*db_owner_info)->dbType =
2240 calloc(1, sizeof(*((*db_owner_info)->dbType)));
2241 if (!(*db_owner_info)->dbType) {
2242 err = IE_NOMEM;
2243 goto leave;
2245 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
2246 if (err) {
2247 zfree((*db_owner_info)->dbType);
2248 if (err == IE_ENUM) {
2249 err = IE_ISDS;
2250 char *string_locale = utf82locale(string);
2251 isds_printf_message(context, _("Unknown isds:dbType: %s"),
2252 string_locale);
2253 free(string_locale);
2255 goto leave;
2257 zfree(string);
2260 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
2262 err = extract_gPersonName(context, &(*db_owner_info)->personName,
2263 xpath_ctx);
2264 if (err) goto leave;
2266 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
2268 (*db_owner_info)->birthInfo =
2269 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
2270 if (!(*db_owner_info)->birthInfo) {
2271 err = IE_NOMEM;
2272 goto leave;
2274 err = extract_BiDate(context, &(*db_owner_info)->birthInfo->biDate,
2275 xpath_ctx);
2276 if (err) goto leave;
2277 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
2278 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
2279 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
2280 if (!(*db_owner_info)->birthInfo->biDate &&
2281 !(*db_owner_info)->birthInfo->biCity &&
2282 !(*db_owner_info)->birthInfo->biCounty &&
2283 !(*db_owner_info)->birthInfo->biState)
2284 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
2286 err = extract_gAddress(context, &(*db_owner_info)->address, xpath_ctx);
2287 if (err) goto leave;
2289 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
2290 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
2291 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
2292 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
2293 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
2295 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
2297 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
2298 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
2299 (*db_owner_info)->dbOpenAddressing);
2301 leave:
2302 if (err) isds_DbOwnerInfo_free(db_owner_info);
2303 free(string);
2304 xmlXPathFreeObject(result);
2305 return err;
2309 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
2310 * @context is sesstion context
2311 * @owner is libsids structure with box description
2312 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
2313 static isds_error insert_DbOwnerInfo(struct isds_ctx *context,
2314 const struct isds_DbOwnerInfo *owner, xmlNodePtr db_owner_info) {
2316 isds_error err = IE_SUCCESS;
2317 xmlNodePtr node;
2318 xmlChar *string = NULL;
2320 if (!context) return IE_INVALID_CONTEXT;
2321 if (!owner || !db_owner_info) return IE_INVAL;
2324 /* Build XSD:tDbOwnerInfo */
2325 CHECK_FOR_STRING_LENGTH(owner->dbID, 0, 7, "dbID")
2326 INSERT_STRING(db_owner_info, "dbID", owner->dbID);
2328 /* dbType */
2329 if (owner->dbType) {
2330 const xmlChar *type_string = isds_DbType2string(*(owner->dbType));
2331 if (!type_string) {
2332 isds_printf_message(context, _("Invalid dbType value: %d"),
2333 *(owner->dbType));
2334 err = IE_ENUM;
2335 goto leave;
2337 INSERT_STRING(db_owner_info, "dbType", type_string);
2339 INSERT_STRING(db_owner_info, "ic", owner->ic);
2340 if (owner->personName) {
2341 INSERT_STRING(db_owner_info, "pnFirstName",
2342 owner->personName->pnFirstName);
2343 INSERT_STRING(db_owner_info, "pnMiddleName",
2344 owner->personName->pnMiddleName);
2345 INSERT_STRING(db_owner_info, "pnLastName",
2346 owner->personName->pnLastName);
2347 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
2348 owner->personName->pnLastNameAtBirth);
2350 INSERT_STRING(db_owner_info, "firmName", owner->firmName);
2351 if (owner->birthInfo) {
2352 if (owner->birthInfo->biDate) {
2353 if (!tm2datestring(owner->birthInfo->biDate, &string))
2354 INSERT_STRING(db_owner_info, "biDate", string);
2355 free(string); string = NULL;
2357 INSERT_STRING(db_owner_info, "biCity", owner->birthInfo->biCity);
2358 INSERT_STRING(db_owner_info, "biCounty", owner->birthInfo->biCounty);
2359 INSERT_STRING(db_owner_info, "biState", owner->birthInfo->biState);
2361 if (owner->address) {
2362 INSERT_STRING(db_owner_info, "adCity", owner->address->adCity);
2363 INSERT_STRING(db_owner_info, "adStreet", owner->address->adStreet);
2364 INSERT_STRING(db_owner_info, "adNumberInStreet",
2365 owner->address->adNumberInStreet);
2366 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
2367 owner->address->adNumberInMunicipality);
2368 INSERT_STRING(db_owner_info, "adZipCode", owner->address->adZipCode);
2369 INSERT_STRING(db_owner_info, "adState", owner->address->adState);
2371 INSERT_STRING(db_owner_info, "nationality", owner->nationality);
2372 INSERT_STRING(db_owner_info, "email", owner->email);
2373 INSERT_STRING(db_owner_info, "telNumber", owner->telNumber);
2375 CHECK_FOR_STRING_LENGTH(owner->identifier, 0, 20, "identifier")
2376 INSERT_STRING(db_owner_info, "identifier", owner->identifier);
2378 CHECK_FOR_STRING_LENGTH(owner->registryCode, 0, 5, "registryCode")
2379 INSERT_STRING(db_owner_info, "registryCode", owner->registryCode);
2381 INSERT_LONGINT(db_owner_info, "dbState", owner->dbState, string);
2383 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", owner->dbEffectiveOVM);
2384 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
2385 owner->dbOpenAddressing);
2387 leave:
2388 free(string);
2389 return err;
2393 /* Convert XSD:tDbUserInfo XML tree into structure
2394 * @context is ISDS context
2395 * @db_user_info is automically reallocated user info structure
2396 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
2397 * In case of error @db_user_info will be freed. */
2398 static isds_error extract_DbUserInfo(struct isds_ctx *context,
2399 struct isds_DbUserInfo **db_user_info, xmlXPathContextPtr xpath_ctx) {
2400 isds_error err = IE_SUCCESS;
2401 xmlXPathObjectPtr result = NULL;
2402 char *string = NULL;
2404 if (!context) return IE_INVALID_CONTEXT;
2405 if (!db_user_info) return IE_INVAL;
2406 isds_DbUserInfo_free(db_user_info);
2407 if (!xpath_ctx) return IE_INVAL;
2410 *db_user_info = calloc(1, sizeof(**db_user_info));
2411 if (!*db_user_info) {
2412 err = IE_NOMEM;
2413 goto leave;
2416 EXTRACT_STRING("isds:userID", (*db_user_info)->userID);
2418 EXTRACT_STRING("isds:userType", string);
2419 if (string) {
2420 (*db_user_info)->userType =
2421 calloc(1, sizeof(*((*db_user_info)->userType)));
2422 if (!(*db_user_info)->userType) {
2423 err = IE_NOMEM;
2424 goto leave;
2426 err = string2isds_UserType((xmlChar *)string,
2427 (*db_user_info)->userType);
2428 if (err) {
2429 zfree((*db_user_info)->userType);
2430 if (err == IE_ENUM) {
2431 err = IE_ISDS;
2432 char *string_locale = utf82locale(string);
2433 isds_printf_message(context, _("Unknown isds:userType: %s"),
2434 string_locale);
2435 free(string_locale);
2437 goto leave;
2439 zfree(string);
2442 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info)->userPrivils, 0);
2444 (*db_user_info)->personName =
2445 calloc(1, sizeof(*((*db_user_info)->personName)));
2446 if (!(*db_user_info)->personName) {
2447 err = IE_NOMEM;
2448 goto leave;
2451 err = extract_gPersonName(context, &(*db_user_info)->personName,
2452 xpath_ctx);
2453 if (err) goto leave;
2455 err = extract_gAddress(context, &(*db_user_info)->address, xpath_ctx);
2456 if (err) goto leave;
2458 err = extract_BiDate(context, &(*db_user_info)->biDate, xpath_ctx);
2459 if (err) goto leave;
2461 EXTRACT_STRING("isds:ic", (*db_user_info)->ic);
2462 EXTRACT_STRING("isds:firmName", (*db_user_info)->firmName);
2464 EXTRACT_STRING("isds:caStreet", (*db_user_info)->caStreet);
2465 EXTRACT_STRING("isds:caCity", (*db_user_info)->caCity);
2466 EXTRACT_STRING("isds:caZipCode", (*db_user_info)->caZipCode);
2468 leave:
2469 if (err) isds_DbUserInfo_free(db_user_info);
2470 free(string);
2471 xmlXPathFreeObject(result);
2472 return err;
2476 /* Insert struct isds_DbUserInfo data (user description) into XML tree
2477 * @context is sesstion context
2478 * @user is libsids structure with user description
2479 * @db_user_info is XML element of XSD:tDbUserInfo */
2480 static isds_error insert_DbUserInfo(struct isds_ctx *context,
2481 const struct isds_DbUserInfo *user, xmlNodePtr db_user_info) {
2483 isds_error err = IE_SUCCESS;
2484 xmlNodePtr node;
2485 xmlChar *string = NULL;
2487 if (!context) return IE_INVALID_CONTEXT;
2488 if (!user || !db_user_info) return IE_INVAL;
2490 /* Build XSD:tDbUserInfo */
2491 if (user->personName) {
2492 INSERT_STRING(db_user_info, "pnFirstName",
2493 user->personName->pnFirstName);
2494 INSERT_STRING(db_user_info, "pnMiddleName",
2495 user->personName->pnMiddleName);
2496 INSERT_STRING(db_user_info, "pnLastName",
2497 user->personName->pnLastName);
2498 INSERT_STRING(db_user_info, "pnLastNameAtBirth",
2499 user->personName->pnLastNameAtBirth);
2501 if (user->address) {
2502 INSERT_STRING(db_user_info, "adCity", user->address->adCity);
2503 INSERT_STRING(db_user_info, "adStreet", user->address->adStreet);
2504 INSERT_STRING(db_user_info, "adNumberInStreet",
2505 user->address->adNumberInStreet);
2506 INSERT_STRING(db_user_info, "adNumberInMunicipality",
2507 user->address->adNumberInMunicipality);
2508 INSERT_STRING(db_user_info, "adZipCode", user->address->adZipCode);
2509 INSERT_STRING(db_user_info, "adState", user->address->adState);
2511 if (user->biDate) {
2512 if (!tm2datestring(user->biDate, &string))
2513 INSERT_STRING(db_user_info, "biDate", string);
2514 zfree(string);
2516 CHECK_FOR_STRING_LENGTH(user->userID, 6, 12, "userID");
2517 INSERT_STRING(db_user_info, "userID", user->userID);
2519 /* userType */
2520 if (user->userType) {
2521 const xmlChar *type_string = isds_UserType2string(*(user->userType));
2522 if (!type_string) {
2523 isds_printf_message(context, _("Invalid userType value: %d"),
2524 *(user->userType));
2525 err = IE_ENUM;
2526 goto leave;
2528 INSERT_STRING(db_user_info, "userType", type_string);
2531 INSERT_LONGINT(db_user_info, "userPrivils", user->userPrivils, string);
2532 CHECK_FOR_STRING_LENGTH(user->ic, 0, 8, "ic")
2533 INSERT_STRING(db_user_info, "ic", user->ic);
2534 CHECK_FOR_STRING_LENGTH(user->firmName, 0, 100, "firmName")
2535 INSERT_STRING(db_user_info, "firmName", user->firmName);
2536 INSERT_STRING(db_user_info, "caStreet", user->caStreet);
2537 INSERT_STRING(db_user_info, "caCity", user->caCity);
2538 INSERT_STRING(db_user_info, "caZipCode", user->caZipCode);
2540 leave:
2541 free(string);
2542 return err;
2546 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
2547 * isds_envelope structure. The envelope is automatically allocated but not
2548 * reallocated. The date are just appended into envelope structure.
2549 * @context is ISDS context
2550 * @envelope is automically allocated message envelope structure
2551 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
2552 * In case of error @envelope will be freed. */
2553 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
2554 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2555 isds_error err = IE_SUCCESS;
2556 xmlXPathObjectPtr result = NULL;
2558 if (!context) return IE_INVALID_CONTEXT;
2559 if (!envelope) return IE_INVAL;
2560 if (!xpath_ctx) return IE_INVAL;
2563 if (!*envelope) {
2564 /* Allocate envelope */
2565 *envelope = calloc(1, sizeof(**envelope));
2566 if (!*envelope) {
2567 err = IE_NOMEM;
2568 goto leave;
2570 } else {
2571 /* Else free former data */
2572 zfree((*envelope)->dmSenderOrgUnit);
2573 zfree((*envelope)->dmSenderOrgUnitNum);
2574 zfree((*envelope)->dbIDRecipient);
2575 zfree((*envelope)->dmRecipientOrgUnit);
2576 zfree((*envelope)->dmSenderOrgUnitNum);
2577 zfree((*envelope)->dmToHands);
2578 zfree((*envelope)->dmAnnotation);
2579 zfree((*envelope)->dmRecipientRefNumber);
2580 zfree((*envelope)->dmSenderRefNumber);
2581 zfree((*envelope)->dmRecipientIdent);
2582 zfree((*envelope)->dmSenderIdent);
2583 zfree((*envelope)->dmLegalTitleLaw);
2584 zfree((*envelope)->dmLegalTitleYear);
2585 zfree((*envelope)->dmLegalTitleSect);
2586 zfree((*envelope)->dmLegalTitlePar);
2587 zfree((*envelope)->dmLegalTitlePoint);
2588 zfree((*envelope)->dmPersonalDelivery);
2589 zfree((*envelope)->dmAllowSubstDelivery);
2592 /* Extract envelope elements added by sender or ISDS
2593 * (XSD: gMessageEnvelopeSub type) */
2594 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
2595 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
2596 (*envelope)->dmSenderOrgUnitNum, 0);
2597 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
2598 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
2599 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
2600 (*envelope)->dmSenderOrgUnitNum, 0);
2601 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
2602 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
2603 EXTRACT_STRING("isds:dmRecipientRefNumber",
2604 (*envelope)->dmRecipientRefNumber);
2605 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
2606 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
2607 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
2609 /* Extract envelope elements regarding law refference */
2610 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
2611 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
2612 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
2613 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
2614 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
2616 /* Extract envelope other elements */
2617 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
2618 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
2619 (*envelope)->dmAllowSubstDelivery);
2621 leave:
2622 if (err) isds_envelope_free(envelope);
2623 xmlXPathFreeObject(result);
2624 return err;
2629 /* Convert XSD gMessageEnvelope group of elements from XML tree into
2630 * isds_envelope structure. The envelope is automatically allocated but not
2631 * reallocated. The date are just appended into envelope structure.
2632 * @context is ISDS context
2633 * @envelope is automically allocated message envelope structure
2634 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
2635 * In case of error @envelope will be freed. */
2636 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
2637 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2638 isds_error err = IE_SUCCESS;
2639 xmlXPathObjectPtr result = NULL;
2641 if (!context) return IE_INVALID_CONTEXT;
2642 if (!envelope) return IE_INVAL;
2643 if (!xpath_ctx) return IE_INVAL;
2646 if (!*envelope) {
2647 /* Allocate envelope */
2648 *envelope = calloc(1, sizeof(**envelope));
2649 if (!*envelope) {
2650 err = IE_NOMEM;
2651 goto leave;
2653 } else {
2654 /* Else free former data */
2655 zfree((*envelope)->dmID);
2656 zfree((*envelope)->dbIDSender);
2657 zfree((*envelope)->dmSender);
2658 zfree((*envelope)->dmSenderAddress);
2659 zfree((*envelope)->dmSenderType);
2660 zfree((*envelope)->dmRecipient);
2661 zfree((*envelope)->dmRecipientAddress);
2662 zfree((*envelope)->dmAmbiguousRecipient);
2665 /* Extract envelope elements added by ISDS
2666 * (XSD: gMessageEnvelope type) */
2667 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
2668 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
2669 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
2670 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
2671 /* XML Schema does not guaratee enumratation. It's plain xs:int. */
2672 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
2673 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
2674 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
2675 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
2676 (*envelope)->dmAmbiguousRecipient);
2678 /* Extract envelope elements added by sender and ISDS
2679 * (XSD: gMessageEnvelope type) */
2680 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
2681 if (err) goto leave;
2683 leave:
2684 if (err) isds_envelope_free(envelope);
2685 xmlXPathFreeObject(result);
2686 return err;
2690 /* Convert other envelope elements from XML tree into isds_envelope structure:
2691 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
2692 * The envelope is automatically allocated but not reallocated.
2693 * The data are just appended into envelope structure.
2694 * @context is ISDS context
2695 * @envelope is automically allocated message envelope structure
2696 * @xpath_ctx is XPath context with current node as parent desired elements
2697 * In case of error @envelope will be freed. */
2698 static isds_error append_status_size_times(struct isds_ctx *context,
2699 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2700 isds_error err = IE_SUCCESS;
2701 xmlXPathObjectPtr result = NULL;
2702 char *string = NULL;
2703 unsigned long int *unumber = NULL;
2705 if (!context) return IE_INVALID_CONTEXT;
2706 if (!envelope) return IE_INVAL;
2707 if (!xpath_ctx) return IE_INVAL;
2710 if (!*envelope) {
2711 /* Allocate new */
2712 *envelope = calloc(1, sizeof(**envelope));
2713 if (!*envelope) {
2714 err = IE_NOMEM;
2715 goto leave;
2717 } else {
2718 /* Free old data */
2719 zfree((*envelope)->dmMessageStatus);
2720 zfree((*envelope)->dmAttachmentSize);
2721 zfree((*envelope)->dmDeliveryTime);
2722 zfree((*envelope)->dmAcceptanceTime);
2726 /* dmMessageStatus element is mandatory */
2727 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
2728 if (!unumber) {
2729 isds_log_message(context,
2730 _("Missing mandatory sisds:dmMessageStatus integer"));
2731 err = IE_ISDS;
2732 goto leave;
2734 err = uint2isds_message_status(context, unumber,
2735 &((*envelope)->dmMessageStatus));
2736 if (err) {
2737 if (err == IE_ENUM) err = IE_ISDS;
2738 goto leave;
2740 free(unumber); unumber = NULL;
2742 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
2745 EXTRACT_STRING("sisds:dmDeliveryTime", string);
2746 if (string) {
2747 err = timestring2timeval((xmlChar *) string,
2748 &((*envelope)->dmDeliveryTime));
2749 if (err) {
2750 char *string_locale = utf82locale(string);
2751 if (err == IE_DATE) err = IE_ISDS;
2752 isds_printf_message(context,
2753 _("Could not convert dmDeliveryTime as ISO time: %s"),
2754 string_locale);
2755 free(string_locale);
2756 goto leave;
2758 zfree(string);
2761 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
2762 if (string) {
2763 err = timestring2timeval((xmlChar *) string,
2764 &((*envelope)->dmAcceptanceTime));
2765 if (err) {
2766 char *string_locale = utf82locale(string);
2767 if (err == IE_DATE) err = IE_ISDS;
2768 isds_printf_message(context,
2769 _("Could not convert dmAcceptanceTime as ISO time: %s"),
2770 string_locale);
2771 free(string_locale);
2772 goto leave;
2774 zfree(string);
2777 leave:
2778 if (err) isds_envelope_free(envelope);
2779 free(unumber);
2780 free(string);
2781 xmlXPathFreeObject(result);
2782 return err;
2786 /* Convert message type attribute of current element into isds_envelope
2787 * structure.
2788 * TODO: This function can be incorporated into append_status_size_times() as
2789 * they are called always together.
2790 * The envelope is automatically allocated but not reallocated.
2791 * The data are just appended into envelope structure.
2792 * @context is ISDS context
2793 * @envelope is automically allocated message envelope structure
2794 * @xpath_ctx is XPath context with current node as parent of attribute
2795 * carrying message type
2796 * In case of error @envelope will be freed. */
2797 static isds_error append_message_type(struct isds_ctx *context,
2798 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2799 isds_error err = IE_SUCCESS;
2801 if (!context) return IE_INVALID_CONTEXT;
2802 if (!envelope) return IE_INVAL;
2803 if (!xpath_ctx) return IE_INVAL;
2806 if (!*envelope) {
2807 /* Allocate new */
2808 *envelope = calloc(1, sizeof(**envelope));
2809 if (!*envelope) {
2810 err = IE_NOMEM;
2811 goto leave;
2813 } else {
2814 /* Free old data */
2815 zfree((*envelope)->dmType);
2819 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope)->dmType, 0);
2821 if (!(*envelope)->dmType) {
2822 /* Use default value */
2823 (*envelope)->dmType = strdup("V");
2824 if (!(*envelope)->dmType) {
2825 err = IE_NOMEM;
2826 goto leave;
2828 } else if (1 != xmlUTF8Strlen((xmlChar *) (*envelope)->dmType)) {
2829 char *type_locale = utf82locale((*envelope)->dmType);
2830 isds_printf_message(context,
2831 _("Message type in dmType attribute is not 1 character long: "
2832 "%s"),
2833 type_locale);
2834 free(type_locale);
2835 err = IE_ISDS;
2836 goto leave;
2839 leave:
2840 if (err) isds_envelope_free(envelope);
2841 return err;
2846 /* Extract message document into reallocated document structure
2847 * @context is ISDS context
2848 * @document is automically reallocated message documents structure
2849 * @xpath_ctx is XPath context with current node as isds:dmFile
2850 * In case of error @document will be freed. */
2851 static isds_error extract_document(struct isds_ctx *context,
2852 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
2853 isds_error err = IE_SUCCESS;
2854 xmlXPathObjectPtr result = NULL;
2855 xmlNodePtr file_node = xpath_ctx->node;
2856 char *string = NULL;
2858 if (!context) return IE_INVALID_CONTEXT;
2859 if (!document) return IE_INVAL;
2860 isds_document_free(document);
2861 if (!xpath_ctx) return IE_INVAL;
2863 *document = calloc(1, sizeof(**document));
2864 if (!*document) {
2865 err = IE_NOMEM;
2866 goto leave;
2869 /* Extract document metadata */
2870 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
2872 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
2873 err = string2isds_FileMetaType((xmlChar*)string,
2874 &((*document)->dmFileMetaType));
2875 if (err) {
2876 char *meta_type_locale = utf82locale(string);
2877 isds_printf_message(context,
2878 _("Document has invalid dmFileMetaType attribute value: %s"),
2879 meta_type_locale);
2880 free(meta_type_locale);
2881 err = IE_ISDS;
2882 goto leave;
2884 zfree(string);
2886 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
2887 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
2888 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
2889 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
2892 /* Extract document data.
2893 * Base64 encoded blob or XML subtree must be presented. */
2895 /* Check from dmEncodedContent */
2896 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
2897 xpath_ctx);
2898 if (!result) {
2899 err = IE_XML;
2900 goto leave;
2903 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2904 /* Here we have Base64 blob */
2906 if (result->nodesetval->nodeNr > 1) {
2907 isds_printf_message(context,
2908 _("Document has more dmEncodedContent elements"));
2909 err = IE_ISDS;
2910 goto leave;
2913 xmlXPathFreeObject(result); result = NULL;
2914 EXTRACT_STRING("isds:dmEncodedContent", string);
2916 /* Decode non-emptys document */
2917 if (string && string[0] != '\0') {
2918 (*document)->data_length = b64decode(string, &((*document)->data));
2919 if ((*document)->data_length == (size_t) -1) {
2920 isds_printf_message(context,
2921 _("Error while Base64-decoding document content"));
2922 err = IE_ERROR;
2923 goto leave;
2926 } else {
2927 /* No Base64 blob, try XML document */
2928 xmlXPathFreeObject(result); result = NULL;
2929 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
2930 xpath_ctx);
2931 if (!result) {
2932 err = IE_XML;
2933 goto leave;
2936 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2937 /* Here we have XML document */
2939 if (result->nodesetval->nodeNr > 1) {
2940 isds_printf_message(context,
2941 _("Document has more dmXMLContent elements"));
2942 err = IE_ISDS;
2943 goto leave;
2946 /* FIXME: Serialize the tree rooted at result's node */
2947 isds_printf_message(context,
2948 _("XML documents not yet supported"));
2949 err = IE_NOTSUP;
2950 goto leave;
2951 } else {
2952 /* No bas64 blob, nor XML document */
2953 isds_printf_message(context,
2954 _("Document has no dmEncodedContent, nor dmXMLContent "
2955 "element"));
2956 err = IE_ISDS;
2957 goto leave;
2962 leave:
2963 if (err) isds_document_free(document);
2964 free(string);
2965 xmlXPathFreeObject(result);
2966 xpath_ctx->node = file_node;
2967 return err;
2972 /* Extract message documents into reallocated list of documents
2973 * @context is ISDS context
2974 * @documents is automically reallocated message documents list structure
2975 * @xpath_ctx is XPath context with current node as XSD tFilesArray
2976 * In case of error @documents will be freed. */
2977 static isds_error extract_documents(struct isds_ctx *context,
2978 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
2979 isds_error err = IE_SUCCESS;
2980 xmlXPathObjectPtr result = NULL;
2981 xmlNodePtr files_node = xpath_ctx->node;
2982 struct isds_list *document, *prev_document;
2984 if (!context) return IE_INVALID_CONTEXT;
2985 if (!documents) return IE_INVAL;
2986 isds_list_free(documents);
2987 if (!xpath_ctx) return IE_INVAL;
2989 /* Find documents */
2990 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
2991 if (!result) {
2992 err = IE_XML;
2993 goto leave;
2996 /* No match */
2997 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2998 isds_printf_message(context,
2999 _("Message does not contain any document"));
3000 err = IE_ISDS;
3001 goto leave;
3005 /* Iterate over documents */
3006 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
3008 /* Allocate and append list item */
3009 document = calloc(1, sizeof(*document));
3010 if (!document) {
3011 err = IE_NOMEM;
3012 goto leave;
3014 document->destructor = (void (*)(void **))isds_document_free;
3015 if (i == 0) *documents = document;
3016 else prev_document->next = document;
3017 prev_document = document;
3019 /* Extract document */
3020 xpath_ctx->node = result->nodesetval->nodeTab[i];
3021 err = extract_document(context,
3022 (struct isds_document **) &(document->data), xpath_ctx);
3023 if (err) goto leave;
3027 leave:
3028 if (err) isds_list_free(documents);
3029 xmlXPathFreeObject(result);
3030 xpath_ctx->node = files_node;
3031 return err;
3035 /* Convert isds:dmRecord XML tree into structure
3036 * @context is ISDS context
3037 * @envelope is automically reallocated message envelope structure
3038 * @xpath_ctx is XPath context with current node as isds:dmRecord element
3039 * In case of error @envelope will be freed. */
3040 static isds_error extract_DmRecord(struct isds_ctx *context,
3041 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3042 isds_error err = IE_SUCCESS;
3043 xmlXPathObjectPtr result = NULL;
3045 if (!context) return IE_INVALID_CONTEXT;
3046 if (!envelope) return IE_INVAL;
3047 isds_envelope_free(envelope);
3048 if (!xpath_ctx) return IE_INVAL;
3051 *envelope = calloc(1, sizeof(**envelope));
3052 if (!*envelope) {
3053 err = IE_NOMEM;
3054 goto leave;
3058 /* Extract tRecord data */
3059 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
3061 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
3062 * dmAcceptanceTime. */
3063 err = append_status_size_times(context, envelope, xpath_ctx);
3064 if (err) goto leave;
3066 /* Extract envelope elements added by sender and ISDS
3067 * (XSD: gMessageEnvelope type) */
3068 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
3069 if (err) goto leave;
3070 /* dmOVM can not be obtained from ISDS */
3072 /* Get message type */
3073 err = append_message_type(context, envelope, xpath_ctx);
3074 if (err) goto leave;
3077 leave:
3078 if (err) isds_envelope_free(envelope);
3079 xmlXPathFreeObject(result);
3080 return err;
3084 /* Find and convert isds:dmHash XML tree into structure
3085 * @context is ISDS context
3086 * @envelope is automically reallocated message hash structure
3087 * @xpath_ctx is XPath context with current node containing isds:dmHash child
3088 * In case of error @hash will be freed. */
3089 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
3090 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
3091 isds_error err = IE_SUCCESS;
3092 xmlNodePtr old_ctx_node;
3093 xmlXPathObjectPtr result = NULL;
3094 char *string = NULL;
3096 if (!context) return IE_INVALID_CONTEXT;
3097 if (!hash) return IE_INVAL;
3098 isds_hash_free(hash);
3099 if (!xpath_ctx) return IE_INVAL;
3101 old_ctx_node = xpath_ctx->node;
3103 *hash = calloc(1, sizeof(**hash));
3104 if (!*hash) {
3105 err = IE_NOMEM;
3106 goto leave;
3109 /* Locate dmHash */
3110 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
3111 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
3112 err = IE_ISDS;
3113 goto leave;
3115 if (err) {
3116 err = IE_ERROR;
3117 goto leave;
3120 /* Get hash algorithm */
3121 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
3122 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
3123 if (err) {
3124 if (err == IE_ENUM) {
3125 char *string_locale = utf82locale(string);
3126 isds_printf_message(context, _("Unsported hash algorithm: %s"),
3127 string_locale);
3128 free(string_locale);
3130 goto leave;
3132 zfree(string);
3134 /* Get hash value */
3135 EXTRACT_STRING(".", string);
3136 if (!string) {
3137 isds_printf_message(context, _("tHash element is missing hash value"));
3138 err = IE_ISDS;
3139 goto leave;
3141 (*hash)->length = b64decode(string, &((*hash)->value));
3142 if ((*hash)->length == (size_t) -1) {
3143 isds_printf_message(context,
3144 _("Error while Base64-decoding hash value"));
3145 err = IE_ERROR;
3146 goto leave;
3149 leave:
3150 if (err) isds_hash_free(hash);
3151 free(string);
3152 xmlXPathFreeObject(result);
3153 xpath_ctx->node = old_ctx_node;
3154 return err;
3158 /* Find and append isds:dmQTimestamp XML tree into envelope
3159 * @context is ISDS context
3160 * @envelope is automically allocated evnelope structure
3161 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
3162 * child
3163 * In case of error @envelope will be freed. */
3164 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
3165 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3166 isds_error err = IE_SUCCESS;
3167 xmlXPathObjectPtr result = NULL;
3168 char *string = NULL;
3170 if (!context) return IE_INVALID_CONTEXT;
3171 if (!envelope) return IE_INVAL;
3172 if (!xpath_ctx) {
3173 isds_envelope_free(envelope);
3174 return IE_INVAL;
3177 if (!*envelope) {
3178 *envelope = calloc(1, sizeof(**envelope));
3179 if (!*envelope) {
3180 err = IE_NOMEM;
3181 goto leave;
3183 } else {
3184 zfree((*envelope)->timestamp);
3185 (*envelope)->timestamp_length = 0;
3188 /* Get dmQTimestamp */
3189 EXTRACT_STRING("sisds:dmQTimestamp", string);
3190 if (!string) {
3191 isds_printf_message(context, _("Missing dmQTimestamp element content"));
3192 err = IE_ISDS;
3193 goto leave;
3195 (*envelope)->timestamp_length =
3196 b64decode(string, &((*envelope)->timestamp));
3197 if ((*envelope)->timestamp_length == (size_t) -1) {
3198 isds_printf_message(context,
3199 _("Error while Base64-decoding timestamp value"));
3200 err = IE_ERROR;
3201 goto leave;
3204 leave:
3205 if (err) isds_envelope_free(envelope);
3206 free(string);
3207 xmlXPathFreeObject(result);
3208 return err;
3212 /* Convert XSD tReturnedMessage XML tree into message structure.
3213 * It doea not store XML tree into message->raw.
3214 * @context is ISDS context
3215 * @include_documents Use true if documents must be extracted
3216 * (tReturnedMessage XSD type), use false if documents shall be ommited
3217 * (tReturnedMessageEnvelope).
3218 * @message is automically reallocated message structure
3219 * @xpath_ctx is XPath context with current node as tReturnedMessage element
3220 * type
3221 * In case of error @message will be freed. */
3222 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
3223 const _Bool include_documents, struct isds_message **message,
3224 xmlXPathContextPtr xpath_ctx) {
3225 isds_error err = IE_SUCCESS;
3226 xmlNodePtr message_node;
3228 if (!context) return IE_INVALID_CONTEXT;
3229 if (!message) return IE_INVAL;
3230 isds_message_free(message);
3231 if (!xpath_ctx) return IE_INVAL;
3234 *message = calloc(1, sizeof(**message));
3235 if (!*message) {
3236 err = IE_NOMEM;
3237 goto leave;
3240 /* Save message XPATH context node */
3241 message_node = xpath_ctx->node;
3244 /* Extract dmDM */
3245 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
3246 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
3247 if (err) { err = IE_ERROR; goto leave; }
3248 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
3249 if (err) goto leave;
3251 if (include_documents) {
3252 /* Extract dmFiles */
3253 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
3254 xpath_ctx);
3255 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
3256 err = IE_ISDS; goto leave;
3258 if (err) { err = IE_ERROR; goto leave; }
3259 err = extract_documents(context, &((*message)->documents), xpath_ctx);
3260 if (err) goto leave;
3264 /* Restore context to message */
3265 xpath_ctx->node = message_node;
3267 /* Extract dmHash */
3268 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
3269 xpath_ctx);
3270 if (err) goto leave;
3272 /* Extract dmQTimestamp, */
3273 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
3274 xpath_ctx);
3275 if (err) goto leave;
3277 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
3278 * dmAcceptanceTime. */
3279 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
3280 if (err) goto leave;
3282 /* Get message type */
3283 err = append_message_type(context, &((*message)->envelope), xpath_ctx);
3284 if (err) goto leave;
3286 leave:
3287 if (err) isds_message_free(message);
3288 return err;
3292 /* Extract message event into reallocated isds_event structure
3293 * @context is ISDS context
3294 * @event is automically reallocated message event structure
3295 * @xpath_ctx is XPath context with current node as isds:dmEvent
3296 * In case of error @event will be freed. */
3297 static isds_error extract_event(struct isds_ctx *context,
3298 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
3299 isds_error err = IE_SUCCESS;
3300 xmlXPathObjectPtr result = NULL;
3301 xmlNodePtr event_node = xpath_ctx->node;
3302 char *string = NULL;
3304 if (!context) return IE_INVALID_CONTEXT;
3305 if (!event) return IE_INVAL;
3306 isds_event_free(event);
3307 if (!xpath_ctx) return IE_INVAL;
3309 *event = calloc(1, sizeof(**event));
3310 if (!*event) {
3311 err = IE_NOMEM;
3312 goto leave;
3315 /* Extract event data.
3316 * All elements are optional according XSD. That's funny. */
3317 EXTRACT_STRING("sisds:dmEventTime", string);
3318 if (string) {
3319 err = timestring2timeval((xmlChar *) string, &((*event)->time));
3320 if (err) {
3321 char *string_locale = utf82locale(string);
3322 if (err == IE_DATE) err = IE_ISDS;
3323 isds_printf_message(context,
3324 _("Could not convert dmEventTime as ISO time: %s"),
3325 string_locale);
3326 free(string_locale);
3327 goto leave;
3329 zfree(string);
3332 /* dmEventDescr element has prefix and the rest */
3333 EXTRACT_STRING("sisds:dmEventDescr", string);
3334 if (string) {
3335 err = eventstring2event((xmlChar *) string, *event);
3336 if (err) goto leave;
3337 zfree(string);
3340 leave:
3341 if (err) isds_event_free(event);
3342 free(string);
3343 xmlXPathFreeObject(result);
3344 xpath_ctx->node = event_node;
3345 return err;
3349 /* Convert element of XSD tEventsArray type from XML tree into
3350 * isds_list of isds_event's structure. The list is automatically reallocated.
3351 * @context is ISDS context
3352 * @events is automically reallocated list of event structures
3353 * @xpath_ctx is XPath context with current node as tEventsArray
3354 * In case of error @evnets will be freed. */
3355 static isds_error extract_events(struct isds_ctx *context,
3356 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
3357 isds_error err = IE_SUCCESS;
3358 xmlXPathObjectPtr result = NULL;
3359 xmlNodePtr events_node = xpath_ctx->node;
3360 struct isds_list *event, *prev_event = NULL;
3362 if (!context) return IE_INVALID_CONTEXT;
3363 if (!events) return IE_INVAL;
3364 if (!xpath_ctx) return IE_INVAL;
3366 /* Free old list */
3367 isds_list_free(events);
3369 /* Find events */
3370 result = xmlXPathEvalExpression(BAD_CAST "sisds:dmEvent", xpath_ctx);
3371 if (!result) {
3372 err = IE_XML;
3373 goto leave;
3376 /* No match */
3377 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3378 isds_printf_message(context,
3379 _("Delivery info does not contain any event"));
3380 err = IE_ISDS;
3381 goto leave;
3385 /* Iterate over events */
3386 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
3388 /* Allocate and append list item */
3389 event = calloc(1, sizeof(*event));
3390 if (!event) {
3391 err = IE_NOMEM;
3392 goto leave;
3394 event->destructor = (void (*)(void **))isds_event_free;
3395 if (i == 0) *events = event;
3396 else prev_event->next = event;
3397 prev_event = event;
3399 /* Extract event */
3400 xpath_ctx->node = result->nodesetval->nodeTab[i];
3401 err = extract_event(context,
3402 (struct isds_event **) &(event->data), xpath_ctx);
3403 if (err) goto leave;
3407 leave:
3408 if (err) isds_list_free(events);
3409 xmlXPathFreeObject(result);
3410 xpath_ctx->node = events_node;
3411 return err;
3415 /* Convert isds_document structure into XML tree and append to dmFiles node.
3416 * @context is session context
3417 * @document is ISDS document
3418 * @dm_files is XML element the resulting tree will be appended to as a child.
3419 * @return error code, in case of error context' message is filled. */
3420 static isds_error insert_document(struct isds_ctx *context,
3421 struct isds_document *document, xmlNodePtr dm_files) {
3422 isds_error err = IE_SUCCESS;
3423 xmlNodePtr new_file = NULL, file = NULL, node;
3424 xmlAttrPtr attribute_node;
3425 xmlChar *base64data = NULL;
3427 if (!context) return IE_INVALID_CONTEXT;
3428 if (!document || !dm_files) return IE_INVAL;
3430 /* Allocate new dmFile */
3431 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
3432 if (!new_file) {
3433 isds_printf_message(context, _("Could not allocate main dmFile"));
3434 err = IE_ERROR;
3435 goto leave;
3437 /* Append the new dmFile.
3438 * XXX: Main document must go first */
3439 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
3440 file = xmlAddPrevSibling(dm_files->children, new_file);
3441 else
3442 file = xmlAddChild(dm_files, new_file);
3444 if (!file) {
3445 xmlFreeNode(new_file); new_file = NULL;
3446 isds_printf_message(context, _("Could not add dmFile child to "
3447 "%s element"), dm_files->name);
3448 err = IE_ERROR;
3449 goto leave;
3452 /* @dmMimeType is required */
3453 if (!document->dmMimeType) {
3454 isds_log_message(context,
3455 _("Document is missing mandatory MIME type definition"));
3456 err = IE_INVAL;
3457 goto leave;
3459 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
3461 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
3462 if (!string) {
3463 isds_printf_message(context,
3464 _("Document has unkown dmFileMetaType: %ld"),
3465 document->dmFileMetaType);
3466 err = IE_ENUM;
3467 goto leave;
3469 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
3471 if (document->dmFileGuid) {
3472 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
3474 if (document->dmUpFileGuid) {
3475 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
3478 /* @dmFileDescr is required */
3479 if (!document->dmFileDescr) {
3480 isds_log_message(context,
3481 _("Document is missing mandatory description (title)"));
3482 err = IE_INVAL;
3483 goto leave;
3485 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
3487 if (document->dmFormat) {
3488 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
3492 /* Insert content (data) of the document. */
3493 /* XXX; Only base64 is implemented currently. */
3494 base64data = (xmlChar *) b64encode(document->data, document->data_length);
3495 if (!base64data) {
3496 isds_printf_message(context,
3497 _("Not enought memory to encode %zd bytes into Base64"),
3498 document->data_length);
3499 err = IE_NOMEM;
3500 goto leave;
3502 INSERT_STRING(file, "dmEncodedContent", base64data);
3503 free(base64data);
3505 leave:
3506 return err;
3510 /* Append XSD tMStatus XML tree into isds_message_copy structure.
3511 * The copy must pre prealocated, the date are just appended into structure.
3512 * @context is ISDS context
3513 * @copy is message copy struture
3514 * @xpath_ctx is XPath context with current node as tMStatus */
3515 static isds_error append_TMStatus(struct isds_ctx *context,
3516 struct isds_message_copy *copy, xmlXPathContextPtr xpath_ctx) {
3517 isds_error err = IE_SUCCESS;
3518 xmlXPathObjectPtr result = NULL;
3519 char *code = NULL, *message = NULL;
3521 if (!context) return IE_INVALID_CONTEXT;
3522 if (!copy || !xpath_ctx) return IE_INVAL;
3524 /* Free old values */
3525 zfree(copy->dmStatus);
3526 zfree(copy->dmID);
3528 /* Get error specific to this copy */
3529 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code);
3530 if (!code) {
3531 isds_log_message(context,
3532 _("Missing isds:dmStatusCode under "
3533 "XSD:tMStatus type element"));
3534 err = IE_ISDS;
3535 goto leave;
3538 if (xmlStrcmp((const xmlChar *)code, BAD_CAST "0000")) {
3539 /* This copy failed */
3540 copy->error = IE_ISDS;
3541 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message);
3542 if (message) {
3543 copy->dmStatus = astrcat3(code, ": ", message);
3544 if (!copy->dmStatus) {
3545 copy->dmStatus = code;
3546 code = NULL;
3548 } else {
3549 copy->dmStatus = code;
3550 code = NULL;
3552 } else {
3553 /* This copy succeeded. In this case only, message ID is valid */
3554 copy->error = IE_SUCCESS;
3556 EXTRACT_STRING("isds:dmID", copy->dmID);
3557 if (!copy->dmID) {
3558 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
3559 "but did not returned assigned message ID\n"));
3560 err = IE_ISDS;
3564 leave:
3565 free(code);
3566 free(message);
3567 xmlXPathFreeObject(result);
3568 return err;
3572 /* Insert struct isds_approval data (box approval) into XML tree
3573 * @context is sesstion context
3574 * @approval is libsids structure with approval description. NULL is
3575 * acceptible.
3576 * @parent is XML element to append @approval to */
3577 static isds_error insert_GExtApproval(struct isds_ctx *context,
3578 const struct isds_approval *approval, xmlNodePtr parent) {
3580 isds_error err = IE_SUCCESS;
3581 xmlNodePtr node;
3583 if (!context) return IE_INVALID_CONTEXT;
3584 if (!parent) return IE_INVAL;
3586 if (!approval) return IE_SUCCESS;
3588 /* Build XSD:gExtApproval */
3589 INSERT_SCALAR_BOOLEAN(parent, "dbApproved", approval->approved);
3590 INSERT_STRING(parent, "dbExternRefNumber", approval->refference);
3592 leave:
3593 return err;
3597 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
3598 * code
3599 * @context is session context
3600 * @service_name is name of SERVICE_DB_ACCESS
3601 * @response is server SOAP body response as XML document
3602 * @raw_response is automatically reallocated bitstream with response body. Use
3603 * NULL if you don't care
3604 * @raw_response_length is size of @raw_response in bytes
3605 * @code is ISDS status code
3606 * @status_message is ISDS status message
3607 * @return error coded from lower layer, context message will be set up
3608 * appropriately. */
3609 static isds_error build_send_check_dbdummy_request(struct isds_ctx *context,
3610 const xmlChar *service_name,
3611 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
3612 xmlChar **code, xmlChar **status_message) {
3614 isds_error err = IE_SUCCESS;
3615 char *service_name_locale = NULL;
3616 xmlNodePtr request = NULL, node;
3617 xmlNsPtr isds_ns = NULL;
3619 if (!context) return IE_INVALID_CONTEXT;
3620 if (!service_name) return IE_INVAL;
3621 if (!response || !code || !status_message) return IE_INVAL;
3622 if (!raw_response_length && raw_response) return IE_INVAL;
3624 /* Free output argument */
3625 xmlFreeDoc(*response); *response = NULL;
3626 if (raw_response) zfree(*raw_response);
3627 free(*code);
3628 free(*status_message);
3631 /* Check if connection is established
3632 * TODO: This check should be done donwstairs. */
3633 if (!context->curl) return IE_CONNECTION_CLOSED;
3635 service_name_locale = utf82locale((char*)service_name);
3636 if (!service_name_locale) {
3637 err = IE_NOMEM;
3638 goto leave;
3641 /* Build request */
3642 request = xmlNewNode(NULL, service_name);
3643 if (!request) {
3644 isds_printf_message(context,
3645 _("Could not build %s request"), service_name_locale);
3646 err = IE_ERROR;
3647 goto leave;
3649 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
3650 if(!isds_ns) {
3651 isds_log_message(context, _("Could not create ISDS name space"));
3652 err = IE_ERROR;
3653 goto leave;
3655 xmlSetNs(request, isds_ns);
3658 /* Add XSD:tDummyInput child */
3659 INSERT_STRING(request, "dbDummy", NULL);
3662 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
3663 service_name_locale);
3665 /* Send request */
3666 err = isds(context, SERVICE_DB_ACCESS, request, response,
3667 raw_response, raw_response_length);
3668 xmlFreeNode(request); request = NULL;
3670 if (err) {
3671 isds_log(ILF_ISDS, ILL_DEBUG,
3672 _("Processing ISDS response on %s request failed\n"),
3673 service_name_locale);
3674 goto leave;
3677 /* Check for response status */
3678 err = isds_response_status(context, SERVICE_DB_ACCESS, *response,
3679 code, status_message, NULL);
3680 if (err) {
3681 isds_log(ILF_ISDS, ILL_DEBUG,
3682 _("ISDS response on %s request is missing status\n"),
3683 service_name_locale);
3684 goto leave;
3687 /* Request processed, but nothing found */
3688 if (xmlStrcmp(*code, BAD_CAST "0000")) {
3689 char *code_locale = utf82locale((char*) *code);
3690 char *status_message_locale = utf82locale((char*) *status_message);
3691 isds_log(ILF_ISDS, ILL_DEBUG,
3692 _("Server refused %s request (code=%s, message=%s)\n"),
3693 service_name_locale, code_locale, status_message_locale);
3694 isds_log_message(context, status_message_locale);
3695 free(code_locale);
3696 free(status_message_locale);
3697 err = IE_ISDS;
3698 goto leave;
3701 leave:
3702 free(service_name_locale);
3703 xmlFreeNode(request);
3704 return err;
3708 /* Get data about logged in user and his box. */
3709 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
3710 struct isds_DbOwnerInfo **db_owner_info) {
3711 isds_error err = IE_SUCCESS;
3712 xmlDocPtr response = NULL;
3713 xmlChar *code = NULL, *message = NULL;
3714 xmlXPathContextPtr xpath_ctx = NULL;
3715 xmlXPathObjectPtr result = NULL;
3716 char *string = NULL;
3718 if (!context) return IE_INVALID_CONTEXT;
3719 if (!db_owner_info) return IE_INVAL;
3721 /* Check if connection is established */
3722 if (!context->curl) return IE_CONNECTION_CLOSED;
3725 /* Do request and check for success */
3726 err = build_send_check_dbdummy_request(context,
3727 BAD_CAST "GetOwnerInfoFromLogin",
3728 &response, NULL, NULL, &code, &message);
3729 if (err) goto leave;
3732 /* Extract data */
3733 /* Prepare stucture */
3734 isds_DbOwnerInfo_free(db_owner_info);
3735 *db_owner_info = calloc(1, sizeof(**db_owner_info));
3736 if (!*db_owner_info) {
3737 err = IE_NOMEM;
3738 goto leave;
3740 xpath_ctx = xmlXPathNewContext(response);
3741 if (!xpath_ctx) {
3742 err = IE_ERROR;
3743 goto leave;
3745 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
3746 err = IE_ERROR;
3747 goto leave;
3750 /* Set context node */
3751 result = xmlXPathEvalExpression(BAD_CAST
3752 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
3753 if (!result) {
3754 err = IE_ERROR;
3755 goto leave;
3757 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3758 isds_log_message(context, _("Missing dbOwnerInfo element"));
3759 err = IE_ISDS;
3760 goto leave;
3762 if (result->nodesetval->nodeNr > 1) {
3763 isds_log_message(context, _("Multiple dbOwnerInfo element"));
3764 err = IE_ISDS;
3765 goto leave;
3767 xpath_ctx->node = result->nodesetval->nodeTab[0];
3768 xmlXPathFreeObject(result); result = NULL;
3770 /* Extract it */
3771 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
3773 leave:
3774 if (err) {
3775 isds_DbOwnerInfo_free(db_owner_info);
3778 free(string);
3779 xmlXPathFreeObject(result);
3780 xmlXPathFreeContext(xpath_ctx);
3782 free(code);
3783 free(message);
3784 xmlFreeDoc(response);
3786 if (!err)
3787 isds_log(ILF_ISDS, ILL_DEBUG,
3788 _("GetOwnerInfoFromLogin request processed by server "
3789 "successfully.\n"));
3791 return err;
3795 /* Get data about logged in user. */
3796 isds_error isds_GetUserInfoFromLogin(struct isds_ctx *context,
3797 struct isds_DbUserInfo **db_user_info) {
3798 isds_error err = IE_SUCCESS;
3799 xmlDocPtr response = NULL;
3800 xmlChar *code = NULL, *message = NULL;
3801 xmlXPathContextPtr xpath_ctx = NULL;
3802 xmlXPathObjectPtr result = NULL;
3804 if (!context) return IE_INVALID_CONTEXT;
3805 if (!db_user_info) return IE_INVAL;
3807 /* Check if connection is established */
3808 if (!context->curl) return IE_CONNECTION_CLOSED;
3811 /* Do request and check for success */
3812 err = build_send_check_dbdummy_request(context,
3813 BAD_CAST "GetUserInfoFromLogin",
3814 &response, NULL, NULL, &code, &message);
3815 if (err) goto leave;
3818 /* Extract data */
3819 /* Prepare stucture */
3820 isds_DbUserInfo_free(db_user_info);
3821 *db_user_info = calloc(1, sizeof(**db_user_info));
3822 if (!*db_user_info) {
3823 err = IE_NOMEM;
3824 goto leave;
3826 xpath_ctx = xmlXPathNewContext(response);
3827 if (!xpath_ctx) {
3828 err = IE_ERROR;
3829 goto leave;
3831 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
3832 err = IE_ERROR;
3833 goto leave;
3836 /* Set context node */
3837 result = xmlXPathEvalExpression(BAD_CAST
3838 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx);
3839 if (!result) {
3840 err = IE_ERROR;
3841 goto leave;
3843 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3844 isds_log_message(context, _("Missing dbUserInfo element"));
3845 err = IE_ISDS;
3846 goto leave;
3848 if (result->nodesetval->nodeNr > 1) {
3849 isds_log_message(context, _("Multiple dbUserInfo element"));
3850 err = IE_ISDS;
3851 goto leave;
3853 xpath_ctx->node = result->nodesetval->nodeTab[0];
3854 xmlXPathFreeObject(result); result = NULL;
3856 /* Extract it */
3857 err = extract_DbUserInfo(context, db_user_info, xpath_ctx);
3859 leave:
3860 if (err) {
3861 isds_DbUserInfo_free(db_user_info);
3864 xmlXPathFreeObject(result);
3865 xmlXPathFreeContext(xpath_ctx);
3867 free(code);
3868 free(message);
3869 xmlFreeDoc(response);
3871 if (!err)
3872 isds_log(ILF_ISDS, ILL_DEBUG,
3873 _("GetUserInfoFromLogin request processed by server "
3874 "successfully.\n"));
3876 return err;
3880 /* Get expiration time of current password
3881 * @context is session context
3882 * @expiration is automatically reallocated time when password expires, In
3883 * case of error will be nulled. */
3884 isds_error isds_get_password_expiration(struct isds_ctx *context,
3885 struct timeval **expiration) {
3886 isds_error err = IE_SUCCESS;
3887 xmlDocPtr response = NULL;
3888 xmlChar *code = NULL, *message = NULL;
3889 xmlXPathContextPtr xpath_ctx = NULL;
3890 xmlXPathObjectPtr result = NULL;
3891 char *string = NULL;
3893 if (!context) return IE_INVALID_CONTEXT;
3894 if (!expiration) return IE_INVAL;
3896 /* Check if connection is established */
3897 if (!context->curl) return IE_CONNECTION_CLOSED;
3900 /* Do request and check for success */
3901 err = build_send_check_dbdummy_request(context,
3902 BAD_CAST "GetPasswordInfo",
3903 &response, NULL, NULL, &code, &message);
3904 if (err) goto leave;
3907 /* Extract data */
3908 xpath_ctx = xmlXPathNewContext(response);
3909 if (!xpath_ctx) {
3910 err = IE_ERROR;
3911 goto leave;
3913 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
3914 err = IE_ERROR;
3915 goto leave;
3918 /* Set context node */
3919 result = xmlXPathEvalExpression(BAD_CAST
3920 "/isds:GetPasswordInfoResponse", xpath_ctx);
3921 if (!result) {
3922 err = IE_ERROR;
3923 goto leave;
3925 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3926 isds_log_message(context,
3927 _("Missing GetPasswordInfoResponse element"));
3928 err = IE_ISDS;
3929 goto leave;
3931 if (result->nodesetval->nodeNr > 1) {
3932 isds_log_message(context,
3933 _("Multiple GetPasswordInfoResponse element"));
3934 err = IE_ISDS;
3935 goto leave;
3937 xpath_ctx->node = result->nodesetval->nodeTab[0];
3938 xmlXPathFreeObject(result); result = NULL;
3940 /* Extract expiration date */
3941 EXTRACT_STRING("isds:pswExpDate", string);
3942 if (!string) {
3943 isds_log_message(context, _("Missing pswExpDate element"));
3944 err = IE_ISDS;
3945 goto leave;
3948 err = timestring2timeval((xmlChar *) string, expiration);
3949 if (err) {
3950 char *string_locale = utf82locale(string);
3951 if (err == IE_DATE) err = IE_ISDS;
3952 isds_printf_message(context,
3953 _("Could not convert pswExpDate as ISO time: %s"),
3954 string_locale);
3955 free(string_locale);
3956 goto leave;
3959 leave:
3960 if (err) {
3961 if (*expiration) {
3962 zfree(*expiration);
3966 free(string);
3967 xmlXPathFreeObject(result);
3968 xmlXPathFreeContext(xpath_ctx);
3970 free(code);
3971 free(message);
3972 xmlFreeDoc(response);
3974 if (!err)
3975 isds_log(ILF_ISDS, ILL_DEBUG,
3976 _("GetPasswordInfo request processed by server "
3977 "successfully.\n"));
3979 return err;
3983 /* Change user password in ISDS.
3984 * User must supply old password, new password will takes effect after some
3985 * time, current session can continue. Password must fulfill some constraints.
3986 * @context is session context
3987 * @old_password is current password.
3988 * @new_password is requested new password */
3989 isds_error isds_change_password(struct isds_ctx *context,
3990 const char *old_password, const char *new_password) {
3991 isds_error err = IE_SUCCESS;
3992 xmlNsPtr isds_ns = NULL;
3993 xmlNodePtr request = NULL, node;
3994 xmlDocPtr response = NULL;
3995 xmlChar *code = NULL, *message = NULL;
3997 if (!context) return IE_INVALID_CONTEXT;
3998 if (!old_password || !new_password) return IE_INVAL;
4000 /* Check if connection is established
4001 * TODO: This check should be done donwstairs. */
4002 if (!context->curl) return IE_CONNECTION_CLOSED;
4005 /* Build ChangeISDSPassword request */
4006 request = xmlNewNode(NULL, BAD_CAST "ChangeISDSPassword");
4007 if (!request) {
4008 isds_log_message(context,
4009 _("Could build ChangeISDSPassword request"));
4010 return IE_ERROR;
4012 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4013 if(!isds_ns) {
4014 isds_log_message(context, _("Could not create ISDS name space"));
4015 xmlFreeNode(request);
4016 return IE_ERROR;
4018 xmlSetNs(request, isds_ns);
4020 INSERT_STRING(request, "dbOldPassword", old_password);
4021 INSERT_STRING(request, "dbNewPassword", new_password);
4024 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CheckDataBox request to ISDS\n"));
4026 /* Sent request */
4027 err = isds(context, SERVICE_DB_ACCESS, request, &response, NULL, NULL);
4029 /* Destroy request */
4030 xmlFreeNode(request); request = NULL;
4032 if (err) {
4033 isds_log(ILF_ISDS, ILL_DEBUG,
4034 _("Processing ISDS response on ChangeISDSPassword "
4035 "request failed\n"));
4036 goto leave;
4039 /* Check for response status */
4040 err = isds_response_status(context, SERVICE_DB_ACCESS, response,
4041 &code, &message, NULL);
4042 if (err) {
4043 isds_log(ILF_ISDS, ILL_DEBUG,
4044 _("ISDS response on ChangeISDSPassword request is missing "
4045 "status\n"));
4046 goto leave;
4049 /* Request processed, but empty password refused */
4050 if (!xmlStrcmp(code, BAD_CAST "1066")) {
4051 char *code_locale = utf82locale((char*)code);
4052 char *message_locale = utf82locale((char*)message);
4053 isds_log(ILF_ISDS, ILL_DEBUG,
4054 _("Server refused empty password on ChangeISDSPassword "
4055 "request (code=%s, message=%s)\n"),
4056 code_locale, message_locale);
4057 isds_log_message(context, _("Password must not be empty"));
4058 free(code_locale);
4059 free(message_locale);
4060 err = IE_INVAL;
4061 goto leave;
4064 /* Request processed, but new password was reused */
4065 else if (!xmlStrcmp(code, BAD_CAST "1067")) {
4066 char *code_locale = utf82locale((char*)code);
4067 char *message_locale = utf82locale((char*)message);
4068 isds_log(ILF_ISDS, ILL_DEBUG,
4069 _("Server refused the same new password on ChangeISDSPassword "
4070 "request (code=%s, message=%s)\n"),
4071 code_locale, message_locale);
4072 isds_log_message(context,
4073 _("New password must differ from the current one"));
4074 free(code_locale);
4075 free(message_locale);
4076 err = IE_INVAL;
4077 goto leave;
4080 /* Other error */
4081 else if (xmlStrcmp(code, BAD_CAST "0000")) {
4082 char *code_locale = utf82locale((char*)code);
4083 char *message_locale = utf82locale((char*)message);
4084 isds_log(ILF_ISDS, ILL_DEBUG,
4085 _("Server refused to change password on ChangeISDSPassword "
4086 "request (code=%s, message=%s)\n"),
4087 code_locale, message_locale);
4088 isds_log_message(context, message_locale);
4089 free(code_locale);
4090 free(message_locale);
4091 err = IE_ISDS;
4092 goto leave;
4095 /* Otherwise password changed successfully */
4097 leave:
4098 free(code);
4099 free(message);
4100 xmlFreeDoc(response);
4101 xmlFreeNode(request);
4103 if (!err)
4104 isds_log(ILF_ISDS, ILL_DEBUG,
4105 _("Password changed successfully on ChangeISDSPassword "
4106 "request.\n"));
4108 return err;
4112 /* Generic middle part with request sending and response check.
4113 * It sends prepared request and checks for error code.
4114 * @context is ISDS session context.
4115 * @service is ISDS service handler
4116 * @service_name is name in scope of given @service
4117 * @request is XML tree with request. Will be freed to save memory.
4118 * @response is XML document ouputing ISDS response.
4119 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4120 * NULL, if you don't care. */
4121 static isds_error send_destroy_request_check_response(
4122 struct isds_ctx *context,
4123 const isds_service service, const xmlChar *service_name,
4124 xmlNodePtr *request, xmlDocPtr *response, xmlChar **refnumber) {
4125 isds_error err = IE_SUCCESS;
4126 char *service_name_locale = NULL;
4127 xmlChar *code = NULL, *message = NULL;
4130 if (!context) return IE_INVALID_CONTEXT;
4131 if (!service_name || *service_name == '\0' || !request || !*request ||
4132 !response)
4133 return IE_INVAL;
4135 /* Check if connection is established
4136 * TODO: This check should be done donwstairs. */
4137 if (!context->curl) return IE_CONNECTION_CLOSED;
4139 service_name_locale = utf82locale((char*) service_name);
4140 if (!service_name_locale) {
4141 err = IE_NOMEM;
4142 goto leave;
4145 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
4146 service_name_locale);
4148 /* Send request */
4149 err = isds(context, service, *request, response, NULL, NULL);
4150 xmlFreeNode(*request); *request = NULL;
4152 if (err) {
4153 isds_log(ILF_ISDS, ILL_DEBUG,
4154 _("Processing ISDS response on %s request failed\n"),
4155 service_name_locale);
4156 goto leave;
4159 /* Check for response status */
4160 err = isds_response_status(context, service, *response,
4161 &code, &message, refnumber);
4162 if (err) {
4163 isds_log(ILF_ISDS, ILL_DEBUG,
4164 _("ISDS response on %s request is missing status\n"),
4165 service_name_locale);
4166 goto leave;
4169 /* Request processed, but server failed */
4170 if (xmlStrcmp(code, BAD_CAST "0000")) {
4171 char *code_locale = utf82locale((char*) code);
4172 char *message_locale = utf82locale((char*) message);
4173 isds_log(ILF_ISDS, ILL_DEBUG,
4174 _("Server refused %s request (code=%s, message=%s)\n"),
4175 service_name_locale, code_locale, message_locale);
4176 isds_log_message(context, message_locale);
4177 free(code_locale);
4178 free(message_locale);
4179 err = IE_ISDS;
4180 goto leave;
4184 leave:
4185 free(code);
4186 free(message);
4187 if (err && *response) {
4188 xmlFreeDoc(*response);
4189 *response = NULL;
4191 if (*request) {
4192 xmlFreeNode(*request);
4193 *request = NULL;
4195 free(service_name_locale);
4197 return err;
4201 /* Generic bottom half with request sending.
4202 * It sends prepared request, checks for error code, destroys response and
4203 * request and log success or failure.
4204 * @context is ISDS session context.
4205 * @service is ISDS service handler
4206 * @service_name is name in scope of given @service
4207 * @request is XML tree with request. Will be freed to save memory.
4208 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4209 * NULL, if you don't care. */
4210 static isds_error send_request_check_drop_response(
4211 struct isds_ctx *context,
4212 const isds_service service, const xmlChar *service_name,
4213 xmlNodePtr *request, xmlChar **refnumber) {
4214 isds_error err = IE_SUCCESS;
4215 xmlDocPtr response = NULL;
4218 if (!context) return IE_INVALID_CONTEXT;
4219 if (!service_name || *service_name == '\0' || !request || !*request)
4220 return IE_INVAL;
4222 /* Send request and check response*/
4223 err = send_destroy_request_check_response(context,
4224 service, service_name, request, &response, refnumber);
4226 xmlFreeDoc(response);
4228 if (*request) {
4229 xmlFreeNode(*request);
4230 *request = NULL;
4233 if (!err) {
4234 char *service_name_locale = utf82locale((char *) service_name);
4235 isds_log(ILF_ISDS, ILL_DEBUG,
4236 _("%s request processed by server successfully.\n"),
4237 service_name_locale);
4238 free(service_name_locale);
4241 return err;
4245 /* Build XSD:tCreateDBInput request type for box createing.
4246 * @context is session context
4247 * @request outputs built XML tree
4248 * @service_name is request name of SERVICE_DB_MANIPULATION service
4249 * @box is box description to create including single primary user (in case of
4250 * FO box type)
4251 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
4252 * box, or contact address of PFO box owner)
4253 * @former_names is optional undocumented string. Pass NULL if you don't care.
4254 * @upper_box_id is optional ID of supper box if currently created box is
4255 * subordinated.
4256 * @ceo_label is optional title of OVM box owner (e.g. mayor) NULL, if you
4257 * don't care.
4258 * @approval is optional external approval of box manipulation */
4259 static isds_error build_CreateDBInput_request(struct isds_ctx *context,
4260 xmlNodePtr *request, const xmlChar *service_name,
4261 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
4262 const xmlChar *former_names, const xmlChar *upper_box_id,
4263 const xmlChar *ceo_label, const struct isds_approval *approval) {
4264 isds_error err = IE_SUCCESS;
4265 xmlNsPtr isds_ns = NULL;
4266 xmlNodePtr node, dbPrimaryUsers;
4267 xmlChar *string = NULL;
4268 const struct isds_list *item;
4271 if (!context) return IE_INVALID_CONTEXT;
4272 if (!request || !service_name || service_name[0] == '\0' || !box)
4273 return IE_INVAL;
4276 /* Build DeleteDataBox request */
4277 *request = xmlNewNode(NULL, service_name);
4278 if (!*request) {
4279 char *service_name_locale = utf82locale((char*) service_name);
4280 isds_printf_message(context, _("Could build %s request"),
4281 service_name_locale);
4282 free(service_name_locale);
4283 return IE_ERROR;
4285 isds_ns = xmlNewNs(*request, BAD_CAST ISDS_NS, NULL);
4286 if (!isds_ns) {
4287 isds_log_message(context, _("Could not create ISDS name space"));
4288 xmlFreeNode(*request);
4289 return IE_ERROR;
4291 xmlSetNs(*request, isds_ns);
4293 INSERT_ELEMENT(node, *request, "dbOwnerInfo");
4294 err = insert_DbOwnerInfo(context, box, node);
4295 if (err) goto leave;
4297 /* Insert users */
4298 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
4299 * verbose documentatiot allows none dbUserInfo */
4300 INSERT_ELEMENT(dbPrimaryUsers, *request, "dbPrimaryUsers");
4301 for (item = users; item; item = item->next) {
4302 if (item->data) {
4303 INSERT_ELEMENT(node, dbPrimaryUsers, "dbUserInfo");
4304 err = insert_DbUserInfo(context,
4305 (struct isds_DbUserInfo *) item->data, node);
4306 if (err) goto leave;
4310 INSERT_STRING(*request, "dbFormerNames", former_names);
4311 INSERT_STRING(*request, "dbUpperDBId", upper_box_id);
4312 INSERT_STRING(*request, "dbCEOLabel", ceo_label);
4314 err = insert_GExtApproval(context, approval, *request);
4315 if (err) goto leave;
4317 leave:
4318 if (err) {
4319 xmlFreeNode(*request);
4320 *request = NULL;
4322 free(string);
4323 return err;
4327 /* Create new box.
4328 * @context is session context
4329 * @box is box description to create including single primary user (in case of
4330 * FO box type). It outputs box ID assigned by ISDS in dbID element.
4331 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
4332 * box, or contact address of PFO box owner)
4333 * @former_names is optional undocumented string. Pass NULL if you don't care.
4334 * @upper_box_id is optional ID of supper box if currently created box is
4335 * subordinated.
4336 * @ceo_label is optional title of OVM box owner (e.g. mayor)
4337 * @approval is optional external approval of box manipulation
4338 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4339 * NULL, if you don't care.*/
4340 isds_error isds_add_box(struct isds_ctx *context,
4341 struct isds_DbOwnerInfo *box, const struct isds_list *users,
4342 const char *former_names, const char *upper_box_id,
4343 const char *ceo_label, const struct isds_approval *approval,
4344 char **refnumber) {
4345 isds_error err = IE_SUCCESS;
4346 xmlNodePtr request = NULL;
4347 xmlDocPtr response = NULL;
4348 xmlXPathContextPtr xpath_ctx = NULL;
4349 xmlXPathObjectPtr result = NULL;
4352 if (!context) return IE_INVALID_CONTEXT;
4353 if (!box) return IE_INVAL;
4355 /* Scratch box ID */
4356 zfree(box->dbID);
4358 /* Build CreateDataBox request */
4359 err = build_CreateDBInput_request(context,
4360 &request, BAD_CAST "CreateDataBox",
4361 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
4362 (xmlChar *) ceo_label, approval);
4363 if (err) goto leave;
4365 /* Send it to server and process response */
4366 err = send_destroy_request_check_response(context,
4367 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
4368 &response, (xmlChar **) refnumber);
4370 /* Extract box ID */
4371 xpath_ctx = xmlXPathNewContext(response);
4372 if (!xpath_ctx) {
4373 err = IE_ERROR;
4374 goto leave;
4376 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4377 err = IE_ERROR;
4378 goto leave;
4380 EXTRACT_STRING("/isds:CreateDataBoxResponse/dbID", box->dbID);
4382 leave:
4383 xmlXPathFreeObject(result);
4384 xmlXPathFreeContext(xpath_ctx);
4385 xmlFreeDoc(response);
4386 xmlFreeNode(request);
4388 if (!err) {
4389 isds_log(ILF_ISDS, ILL_DEBUG,
4390 _("CreateDataBox request processed by server successfully.\n"));
4393 return err;
4397 /* Notify ISDS about new PFO entity.
4398 * This function has no real effect.
4399 * @context is session context
4400 * @box is PFO description including single primary user.
4401 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
4402 * @former_names is optional undocumented string. Pass NULL if you don't care.
4403 * @upper_box_id is optional ID of supper box if currently created box is
4404 * subordinated.
4405 * @ceo_label is optional title of OVM box owner (e.g. mayor)
4406 * @approval is optional external approval of box manipulation
4407 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4408 * NULL, if you don't care.*/
4409 isds_error isds_add_pfoinfo(struct isds_ctx *context,
4410 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
4411 const char *former_names, const char *upper_box_id,
4412 const char *ceo_label, const struct isds_approval *approval,
4413 char **refnumber) {
4414 isds_error err = IE_SUCCESS;
4415 xmlNodePtr request = NULL;
4417 if (!context) return IE_INVALID_CONTEXT;
4418 if (!box) return IE_INVAL;
4420 /* Build CreateDataBoxPFOInfo request */
4421 err = build_CreateDBInput_request(context,
4422 &request, BAD_CAST "CreateDataBoxPFOInfo",
4423 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
4424 (xmlChar *) ceo_label, approval);
4425 if (err) goto leave;
4427 /* Send it to server and process response */
4428 err = send_request_check_drop_response(context,
4429 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
4430 (xmlChar **) refnumber);
4431 leave:
4432 xmlFreeNode(request);
4433 return err;
4437 /* Remove given given box permanetly.
4438 * @context is session context
4439 * @box is box description to delete
4440 * @since is date of box owner cancalation. Only tm_year, tm_mon and tm_mday
4441 * carry sane value.
4442 * @approval is optional external approval of box manipulation
4443 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4444 * NULL, if you don't care.*/
4445 isds_error isds_delete_box(struct isds_ctx *context,
4446 const struct isds_DbOwnerInfo *box, const struct tm *since,
4447 const struct isds_approval *approval, char **refnumber) {
4448 isds_error err = IE_SUCCESS;
4449 xmlNsPtr isds_ns = NULL;
4450 xmlNodePtr request = NULL;
4451 xmlNodePtr node;
4452 xmlChar *string = NULL;
4455 if (!context) return IE_INVALID_CONTEXT;
4456 if (!box || !since) return IE_INVAL;
4459 /* Build DeleteDataBox request */
4460 request = xmlNewNode(NULL, BAD_CAST "DeleteDataBox");
4461 if (!request) {
4462 isds_log_message(context,
4463 _("Could build DeleteDataBox request"));
4464 return IE_ERROR;
4466 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4467 if(!isds_ns) {
4468 isds_log_message(context, _("Could not create ISDS name space"));
4469 xmlFreeNode(request);
4470 return IE_ERROR;
4472 xmlSetNs(request, isds_ns);
4474 INSERT_ELEMENT(node, request, "dbOwnerInfo");
4475 err = insert_DbOwnerInfo(context, box, node);
4476 if (err) goto leave;
4478 err = tm2datestring(since, &string);
4479 if (err) {
4480 isds_log_message(context,
4481 _("Could not convert `since' argument to ISO date string"));
4482 goto leave;
4484 INSERT_STRING(request, "dbOwnerTerminationDate", string);
4485 zfree(string);
4487 err = insert_GExtApproval(context, approval, request);
4488 if (err) goto leave;
4491 /* Send it to server and process response */
4492 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
4493 BAD_CAST "DeleteDataBox", &request, (xmlChar **) refnumber);
4495 leave:
4496 xmlFreeNode(request);
4497 free(string);
4498 return err;
4502 /* Update data about given box.
4503 * @context is session context
4504 * @old_box current box description
4505 * @new_box are updated data about @old_box
4506 * @approval is optional external approval of box manipulation
4507 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4508 * NULL, if you don't care.*/
4509 isds_error isds_UpdateDataBoxDescr(struct isds_ctx *context,
4510 const struct isds_DbOwnerInfo *old_box,
4511 const struct isds_DbOwnerInfo *new_box,
4512 const struct isds_approval *approval, char **refnumber) {
4513 isds_error err = IE_SUCCESS;
4514 xmlNsPtr isds_ns = NULL;
4515 xmlNodePtr request = NULL;
4516 xmlNodePtr node;
4519 if (!context) return IE_INVALID_CONTEXT;
4520 if (!old_box || !new_box) return IE_INVAL;
4523 /* Build UpdateDataBoxDescr request */
4524 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxDescr");
4525 if (!request) {
4526 isds_log_message(context,
4527 _("Could build UpdateDataBoxDescr request"));
4528 return IE_ERROR;
4530 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4531 if(!isds_ns) {
4532 isds_log_message(context, _("Could not create ISDS name space"));
4533 xmlFreeNode(request);
4534 return IE_ERROR;
4536 xmlSetNs(request, isds_ns);
4538 INSERT_ELEMENT(node, request, "dbOldOwnerInfo");
4539 err = insert_DbOwnerInfo(context, old_box, node);
4540 if (err) goto leave;
4542 INSERT_ELEMENT(node, request, "dbNewOwnerInfo");
4543 err = insert_DbOwnerInfo(context, new_box, node);
4544 if (err) goto leave;
4546 err = insert_GExtApproval(context, approval, request);
4547 if (err) goto leave;
4550 /* Send it to server and process response */
4551 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
4552 BAD_CAST "UpdateDataBoxDescr", &request, (xmlChar **) refnumber);
4554 leave:
4555 xmlFreeNode(request);
4557 return err;
4561 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
4562 * code
4563 * @context is session context
4564 * @service is SOAP service
4565 * @service_name is name of request in @service
4566 * @box_id is box ID of interrest
4567 * @approval is optional external approval of box manipulation
4568 * @response is server SOAP body response as XML document
4569 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4570 * NULL, if you don't care.
4571 * @return error coded from lower layer, context message will be set up
4572 * appropriately. */
4573 static isds_error build_send_dbid_request_check_response(
4574 struct isds_ctx *context, const isds_service service,
4575 const xmlChar *service_name, const xmlChar *box_id,
4576 const struct isds_approval *approval,
4577 xmlDocPtr *response, xmlChar **refnumber) {
4579 isds_error err = IE_SUCCESS;
4580 char *service_name_locale = NULL, *box_id_locale = NULL;
4581 xmlNodePtr request = NULL, node;
4582 xmlNsPtr isds_ns = NULL;
4584 if (!context) return IE_INVALID_CONTEXT;
4585 if (!service_name || !box_id) return IE_INVAL;
4586 if (!response) return IE_INVAL;
4588 /* Free output argument */
4589 xmlFreeDoc(*response); *response = NULL;
4591 /* Prepare strings */
4592 service_name_locale = utf82locale((char*)service_name);
4593 if (!service_name_locale) {
4594 err = IE_NOMEM;
4595 goto leave;
4597 box_id_locale = utf82locale((char*)box_id);
4598 if (!box_id_locale) {
4599 err = IE_NOMEM;
4600 goto leave;
4603 /* Build request */
4604 request = xmlNewNode(NULL, service_name);
4605 if (!request) {
4606 isds_printf_message(context,
4607 _("Could not build %s request"), service_name_locale);
4608 err = IE_ERROR;
4609 goto leave;
4611 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4612 if(!isds_ns) {
4613 isds_log_message(context, _("Could not create ISDS name space"));
4614 err = IE_ERROR;
4615 goto leave;
4617 xmlSetNs(request, isds_ns);
4619 /* Add XSD:tIdDbInput childs*/
4620 INSERT_STRING(request, "dbID", box_id);
4621 err = insert_GExtApproval(context, approval, request);
4622 if (err) goto leave;
4624 /* Send request and check response*/
4625 err = send_destroy_request_check_response(context,
4626 service, service_name, &request, response, refnumber);
4628 leave:
4629 free(service_name_locale);
4630 free(box_id_locale);
4631 xmlFreeNode(request);
4632 return err;
4636 /* Get data about all users assigned to given box.
4637 * @context is session context
4638 * @box_id is box ID
4639 * @users is automatically reallocated list of struct isds_DbUserInfo */
4640 isds_error isds_GetDataBoxUsers(struct isds_ctx *context, const char *box_id,
4641 struct isds_list **users) {
4642 isds_error err = IE_SUCCESS;
4643 xmlDocPtr response = NULL;
4644 xmlXPathContextPtr xpath_ctx = NULL;
4645 xmlXPathObjectPtr result = NULL;
4646 int i;
4647 struct isds_list *item, *prev_item = NULL;
4649 if (!context) return IE_INVALID_CONTEXT;
4650 if (!users || !box_id) return IE_INVAL;
4653 /* Do request and check for success */
4654 err = build_send_dbid_request_check_response(context,
4655 SERVICE_DB_MANIPULATION, BAD_CAST "GetDataBoxUsers",
4656 BAD_CAST box_id, NULL, &response, NULL);
4657 if (err) goto leave;
4660 /* Extract data */
4661 /* Prepare stucture */
4662 isds_list_free(users);
4663 xpath_ctx = xmlXPathNewContext(response);
4664 if (!xpath_ctx) {
4665 err = IE_ERROR;
4666 goto leave;
4668 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4669 err = IE_ERROR;
4670 goto leave;
4673 /* Set context node */
4674 result = xmlXPathEvalExpression(BAD_CAST
4675 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
4676 xpath_ctx);
4677 if (!result) {
4678 err = IE_ERROR;
4679 goto leave;
4681 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4682 isds_log_message(context, _("Missing dbUserInfo element"));
4683 err = IE_ISDS;
4684 goto leave;
4687 /* Iterate over all users */
4688 for (i = 0; i < result->nodesetval->nodeNr; i++) {
4690 /* Prepare structure */
4691 item = calloc(1, sizeof(*item));
4692 if (!item) {
4693 err = IE_NOMEM;
4694 goto leave;
4696 item->destructor = (void(*)(void**))isds_DbUserInfo_free;
4697 if (i == 0) *users = item;
4698 else prev_item->next = item;
4699 prev_item = item;
4701 /* Extract it */
4702 xpath_ctx->node = result->nodesetval->nodeTab[i];
4703 err = extract_DbUserInfo(context,
4704 (struct isds_DbUserInfo **) (&item->data), xpath_ctx);
4705 if (err) goto leave;
4708 leave:
4709 if (err) {
4710 isds_list_free(users);
4713 xmlXPathFreeObject(result);
4714 xmlXPathFreeContext(xpath_ctx);
4715 xmlFreeDoc(response);
4717 if (!err)
4718 isds_log(ILF_ISDS, ILL_DEBUG,
4719 _("GetDataBoxUsers request processed by server "
4720 "successfully.\n"));
4722 return err;
4726 /* Update data about user assigned to given box.
4727 * @context is session context
4728 * @box is box identification
4729 * @old_user identifies user to update
4730 * @new_user are updated data about @old_user
4731 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4732 * NULL, if you don't care.*/
4733 isds_error isds_UpdateDataBoxUser(struct isds_ctx *context,
4734 const struct isds_DbOwnerInfo *box,
4735 const struct isds_DbUserInfo *old_user,
4736 const struct isds_DbUserInfo *new_user,
4737 char **refnumber) {
4738 isds_error err = IE_SUCCESS;
4739 xmlNsPtr isds_ns = NULL;
4740 xmlNodePtr request = NULL;
4741 xmlNodePtr node;
4744 if (!context) return IE_INVALID_CONTEXT;
4745 if (!box || !old_user || !new_user) return IE_INVAL;
4748 /* Build UpdateDataBoxUser request */
4749 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxUser");
4750 if (!request) {
4751 isds_log_message(context,
4752 _("Could build UpdateDataBoxUser request"));
4753 return IE_ERROR;
4755 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4756 if(!isds_ns) {
4757 isds_log_message(context, _("Could not create ISDS name space"));
4758 xmlFreeNode(request);
4759 return IE_ERROR;
4761 xmlSetNs(request, isds_ns);
4763 INSERT_ELEMENT(node, request, "dbOwnerInfo");
4764 err = insert_DbOwnerInfo(context, box, node);
4765 if (err) goto leave;
4767 INSERT_ELEMENT(node, request, "dbOldUserInfo");
4768 err = insert_DbUserInfo(context, old_user, node);
4769 if (err) goto leave;
4771 INSERT_ELEMENT(node, request, "dbNewUserInfo");
4772 err = insert_DbUserInfo(context, new_user, node);
4773 if (err) goto leave;
4775 /* Send it to server and process response */
4776 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
4777 BAD_CAST "UpdateDataBoxUser", &request, (xmlChar **) refnumber);
4779 leave:
4780 xmlFreeNode(request);
4782 return err;
4786 /* Reset credentials of user assigned to given box.
4787 * @context is session context
4788 * @box is box identification
4789 * @user identifies user to reset password
4790 * @fee_paid is true if fee has been paid, false otherwise
4791 * @approval is optional external approval of box manipulation
4792 * @token is NULL if new password should be delivered off-line to the user.
4793 * It is valid pointer if user should obtain new password on-line on dedicated
4794 * web server. Then it output automatically reallocated token user needs to
4795 * use to athtorize on the web server to view his new password.
4796 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4797 * NULL, if you don't care.*/
4798 isds_error isds_reset_password(struct isds_ctx *context,
4799 const struct isds_DbOwnerInfo *box,
4800 const struct isds_DbUserInfo *user,
4801 const _Bool fee_paid, const struct isds_approval *approval,
4802 char **token, char **refnumber) {
4803 isds_error err = IE_SUCCESS;
4804 xmlNsPtr isds_ns = NULL;
4805 xmlNodePtr request = NULL, node;
4806 xmlDocPtr response = NULL;
4807 xmlXPathContextPtr xpath_ctx = NULL;
4808 xmlXPathObjectPtr result = NULL;
4811 if (!context) return IE_INVALID_CONTEXT;
4812 if (!box || !user) return IE_INVAL;
4814 if (token) zfree(*token);
4817 /* Build NewAccessData request */
4818 request = xmlNewNode(NULL, BAD_CAST "NewAccessData");
4819 if (!request) {
4820 isds_log_message(context,
4821 _("Could build NewAccessData request"));
4822 return IE_ERROR;
4824 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4825 if(!isds_ns) {
4826 isds_log_message(context, _("Could not create ISDS name space"));
4827 xmlFreeNode(request);
4828 return IE_ERROR;
4830 xmlSetNs(request, isds_ns);
4832 INSERT_ELEMENT(node, request, "dbOwnerInfo");
4833 err = insert_DbOwnerInfo(context, box, node);
4834 if (err) goto leave;
4836 INSERT_ELEMENT(node, request, "dbUserInfo");
4837 err = insert_DbUserInfo(context, user, node);
4838 if (err) goto leave;
4840 INSERT_SCALAR_BOOLEAN(request, "dbFeePaid", fee_paid);
4842 if (token) {
4843 INSERT_SCALAR_BOOLEAN(request, "dbVirtual", 1);
4844 } else {
4845 INSERT_SCALAR_BOOLEAN(request, "dbVirtual", 0);
4848 err = insert_GExtApproval(context, approval, request);
4849 if (err) goto leave;
4851 /* Send request and check reposne*/
4852 err = send_destroy_request_check_response(context,
4853 SERVICE_DB_MANIPULATION, BAD_CAST "NewAccessData", &request,
4854 &response, (xmlChar **) refnumber);
4855 if (err) goto leave;
4858 /* Extract optional token */
4859 if (token) {
4860 xpath_ctx = xmlXPathNewContext(response);
4861 if (!xpath_ctx) {
4862 err = IE_ERROR;
4863 goto leave;
4865 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4866 err = IE_ERROR;
4867 goto leave;
4870 EXTRACT_STRING("/isds:NewAccessDataResponse/dbAccessDataId", *token);
4873 leave:
4874 xmlXPathFreeObject(result);
4875 xmlXPathFreeContext(xpath_ctx);
4876 xmlFreeDoc(response);
4877 xmlFreeNode(request);
4879 if (!err)
4880 isds_log(ILF_ISDS, ILL_DEBUG,
4881 _("NewAccessData request processed by server "
4882 "successfully.\n"));
4884 return err;
4888 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
4889 * code, destroy response and log success.
4890 * @context is ISDS session context.
4891 * @service_name is name of SERVICE_DB_MANIPULATION service
4892 * @box is box identification
4893 * @user identifies user to removve
4894 * @approval is optional external approval of box manipulation
4895 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4896 * NULL, if you don't care. */
4897 static isds_error build_send_manipulationboxuser_request_check_drop_response(
4898 struct isds_ctx *context, const xmlChar *service_name,
4899 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
4900 const struct isds_approval *approval, xmlChar **refnumber) {
4901 isds_error err = IE_SUCCESS;
4902 xmlNsPtr isds_ns = NULL;
4903 xmlNodePtr request = NULL, node;
4906 if (!context) return IE_INVALID_CONTEXT;
4907 if (!service_name || service_name[0] == '\0' || !box || !user)
4908 return IE_INVAL;
4911 /* Build NewAccessData request */
4912 request = xmlNewNode(NULL, service_name);
4913 if (!request) {
4914 char *service_name_locale = utf82locale((char *) service_name);
4915 isds_printf_message(context, _("Could build %s request"),
4916 service_name_locale);
4917 free(service_name_locale);
4918 return IE_ERROR;
4920 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4921 if(!isds_ns) {
4922 isds_log_message(context, _("Could not create ISDS name space"));
4923 xmlFreeNode(request);
4924 return IE_ERROR;
4926 xmlSetNs(request, isds_ns);
4928 INSERT_ELEMENT(node, request, "dbOwnerInfo");
4929 err = insert_DbOwnerInfo(context, box, node);
4930 if (err) goto leave;
4932 INSERT_ELEMENT(node, request, "dbUserInfo");
4933 err = insert_DbUserInfo(context, user, node);
4934 if (err) goto leave;
4936 err = insert_GExtApproval(context, approval, request);
4937 if (err) goto leave;
4939 /* Send request and check reposne*/
4940 err = send_request_check_drop_response (context,
4941 SERVICE_DB_MANIPULATION, service_name, &request, refnumber);
4943 leave:
4944 xmlFreeNode(request);
4945 return err;
4949 /* Assign new user to given box.
4950 * @context is session context
4951 * @box is box identification
4952 * @user defines new user to add
4953 * @approval is optional external approval of box manipulation
4954 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4955 * NULL, if you don't care.*/
4956 isds_error isds_add_user(struct isds_ctx *context,
4957 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
4958 const struct isds_approval *approval, char **refnumber) {
4959 return build_send_manipulationboxuser_request_check_drop_response(context,
4960 BAD_CAST "AddDataBoxUser", box, user, approval,
4961 (xmlChar **) refnumber);
4965 /* Remove user assigned to given box.
4966 * @context is session context
4967 * @box is box identification
4968 * @user identifies user to removve
4969 * @approval is optional external approval of box manipulation
4970 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4971 * NULL, if you don't care.*/
4972 isds_error isds_delete_user(struct isds_ctx *context,
4973 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
4974 const struct isds_approval *approval, char **refnumber) {
4975 return build_send_manipulationboxuser_request_check_drop_response(context,
4976 BAD_CAST "DeleteDataBoxUser", box, user, approval,
4977 (xmlChar **) refnumber);
4981 /* Find boxes suiting given criteria.
4982 * @criteria is filter. You should fill in at least some memebers.
4983 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
4984 * possibly empty. Input NULL or valid old structure.
4985 * @return:
4986 * IE_SUCCESS if search sucseeded, @boxes contains usefull data
4987 * IE_NOEXIST if no such box exists, @boxes will be NULL
4988 * IE_2BIG if too much boxes exist and server truncated the resuluts, @boxes
4989 * contains still valid data
4990 * other code if something bad happens. @boxes will be NULL. */
4991 isds_error isds_FindDataBox(struct isds_ctx *context,
4992 const struct isds_DbOwnerInfo *criteria,
4993 struct isds_list **boxes) {
4994 isds_error err = IE_SUCCESS;
4995 _Bool truncated = 0;
4996 xmlNsPtr isds_ns = NULL;
4997 xmlNodePtr request = NULL;
4998 xmlDocPtr response = NULL;
4999 xmlChar *code = NULL, *message = NULL;
5000 xmlNodePtr db_owner_info;
5001 xmlXPathContextPtr xpath_ctx = NULL;
5002 xmlXPathObjectPtr result = NULL;
5003 xmlChar *string = NULL;
5006 if (!context) return IE_INVALID_CONTEXT;
5007 if (!boxes) return IE_INVAL;
5008 isds_list_free(boxes);
5010 if (!criteria) {
5011 return IE_INVAL;
5014 /* Check if connection is established
5015 * TODO: This check should be done donwstairs. */
5016 if (!context->curl) return IE_CONNECTION_CLOSED;
5019 /* Build FindDataBox request */
5020 request = xmlNewNode(NULL, BAD_CAST "FindDataBox");
5021 if (!request) {
5022 isds_log_message(context,
5023 _("Could build FindDataBox request"));
5024 return IE_ERROR;
5026 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5027 if(!isds_ns) {
5028 isds_log_message(context, _("Could not create ISDS name space"));
5029 xmlFreeNode(request);
5030 return IE_ERROR;
5032 xmlSetNs(request, isds_ns);
5033 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
5034 if (!db_owner_info) {
5035 isds_log_message(context, _("Could not add dbOwnerInfo Child to "
5036 "FindDataBox element"));
5037 xmlFreeNode(request);
5038 return IE_ERROR;
5041 err = insert_DbOwnerInfo(context, criteria, db_owner_info);
5042 if (err) goto leave;
5045 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending FindDataBox request to ISDS\n"));
5047 /* Sent request */
5048 err = isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
5050 /* Destroy request */
5051 xmlFreeNode(request); request = NULL;
5053 if (err) {
5054 isds_log(ILF_ISDS, ILL_DEBUG,
5055 _("Processing ISDS response on FindDataBox "
5056 "request failed\n"));
5057 goto leave;
5060 /* Check for response status */
5061 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
5062 &code, &message, NULL);
5063 if (err) {
5064 isds_log(ILF_ISDS, ILL_DEBUG,
5065 _("ISDS response on FindDataBox request is missing status\n"));
5066 goto leave;
5069 /* Request processed, but nothing found */
5070 if (!xmlStrcmp(code, BAD_CAST "0002") ||
5071 !xmlStrcmp(code, BAD_CAST "5001")) {
5072 char *code_locale = utf82locale((char*)code);
5073 char *message_locale = utf82locale((char*)message);
5074 isds_log(ILF_ISDS, ILL_DEBUG,
5075 _("Server did not found any box on FindDataBox request "
5076 "(code=%s, message=%s)\n"), code_locale, message_locale);
5077 isds_log_message(context, message_locale);
5078 free(code_locale);
5079 free(message_locale);
5080 err = IE_NOEXIST;
5081 goto leave;
5084 /* Warning, not a error */
5085 if (!xmlStrcmp(code, BAD_CAST "0003")) {
5086 char *code_locale = utf82locale((char*)code);
5087 char *message_locale = utf82locale((char*)message);
5088 isds_log(ILF_ISDS, ILL_DEBUG,
5089 _("Server truncated response on FindDataBox request "
5090 "(code=%s, message=%s)\n"), code_locale, message_locale);
5091 isds_log_message(context, message_locale);
5092 free(code_locale);
5093 free(message_locale);
5094 truncated = 1;
5097 /* Other error */
5098 else if (xmlStrcmp(code, BAD_CAST "0000")) {
5099 char *code_locale = utf82locale((char*)code);
5100 char *message_locale = utf82locale((char*)message);
5101 isds_log(ILF_ISDS, ILL_DEBUG,
5102 _("Server refused FindDataBox request "
5103 "(code=%s, message=%s)\n"), code_locale, message_locale);
5104 isds_log_message(context, message_locale);
5105 free(code_locale);
5106 free(message_locale);
5107 err = IE_ISDS;
5108 goto leave;
5111 xpath_ctx = xmlXPathNewContext(response);
5112 if (!xpath_ctx) {
5113 err = IE_ERROR;
5114 goto leave;
5116 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5117 err = IE_ERROR;
5118 goto leave;
5121 /* Extract boxes if they present */
5122 result = xmlXPathEvalExpression(BAD_CAST
5123 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
5124 xpath_ctx);
5125 if (!result) {
5126 err = IE_ERROR;
5127 goto leave;
5129 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5130 struct isds_list *item, *prev_item = NULL;
5131 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
5132 item = calloc(1, sizeof(*item));
5133 if (!item) {
5134 err = IE_NOMEM;
5135 goto leave;
5138 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
5139 if (i == 0) *boxes = item;
5140 else prev_item->next = item;
5141 prev_item = item;
5143 xpath_ctx->node = result->nodesetval->nodeTab[i];
5144 err = extract_DbOwnerInfo(context,
5145 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
5146 if (err) goto leave;
5150 leave:
5151 if (err) {
5152 isds_list_free(boxes);
5153 } else {
5154 if (truncated) err = IE_2BIG;
5157 free(string);
5158 xmlFreeNode(request);
5159 xmlXPathFreeObject(result);
5160 xmlXPathFreeContext(xpath_ctx);
5162 free(code);
5163 free(message);
5164 xmlFreeDoc(response);
5166 if (!err)
5167 isds_log(ILF_ISDS, ILL_DEBUG,
5168 _("FindDataBox request processed by server successfully.\n"));
5170 return err;
5174 /* Get status of a box.
5175 * @context is ISDS session context.
5176 * @box_id is UTF-8 encoded box identifier as zero terminated string
5177 * @box_status is return value of box status.
5178 * @return:
5179 * IE_SUCCESS if box has been found and its status retrieved
5180 * IE_NOEXIST if box is not known to ISDS server
5181 * or other appropriate error.
5182 * You can use isds_DbState to enumerate box status. However out of enum
5183 * range value can be returned too. This is feature because ISDS
5184 * specification leaves the set of values open.
5185 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
5186 * the box has been deleted, but ISDS still lists its former existence. */
5187 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
5188 long int *box_status) {
5189 isds_error err = IE_SUCCESS;
5190 xmlNsPtr isds_ns = NULL;
5191 xmlNodePtr request = NULL, db_id;
5192 xmlDocPtr response = NULL;
5193 xmlChar *code = NULL, *message = NULL;
5194 xmlXPathContextPtr xpath_ctx = NULL;
5195 xmlXPathObjectPtr result = NULL;
5196 xmlChar *string = NULL;
5198 if (!context) return IE_INVALID_CONTEXT;
5199 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
5201 /* Check if connection is established
5202 * TODO: This check should be done donwstairs. */
5203 if (!context->curl) return IE_CONNECTION_CLOSED;
5206 /* Build CheckDataBox request */
5207 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
5208 if (!request) {
5209 isds_log_message(context,
5210 _("Could build CheckDataBox request"));
5211 return IE_ERROR;
5213 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5214 if(!isds_ns) {
5215 isds_log_message(context, _("Could not create ISDS name space"));
5216 xmlFreeNode(request);
5217 return IE_ERROR;
5219 xmlSetNs(request, isds_ns);
5220 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
5221 if (!db_id) {
5222 isds_log_message(context, _("Could not add dbId Child to "
5223 "CheckDataBox element"));
5224 xmlFreeNode(request);
5225 return IE_ERROR;
5229 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CheckDataBox request to ISDS\n"));
5231 /* Sent request */
5232 err = isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
5234 /* Destroy request */
5235 xmlFreeNode(request);
5237 if (err) {
5238 isds_log(ILF_ISDS, ILL_DEBUG,
5239 _("Processing ISDS response on CheckDataBox "
5240 "request failed\n"));
5241 goto leave;
5244 /* Check for response status */
5245 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
5246 &code, &message, NULL);
5247 if (err) {
5248 isds_log(ILF_ISDS, ILL_DEBUG,
5249 _("ISDS response on CheckDataBox request is missing status\n"));
5250 goto leave;
5253 /* Request processed, but nothing found */
5254 if (!xmlStrcmp(code, BAD_CAST "5001")) {
5255 char *box_id_locale = utf82locale((char*)box_id);
5256 char *code_locale = utf82locale((char*)code);
5257 char *message_locale = utf82locale((char*)message);
5258 isds_log(ILF_ISDS, ILL_DEBUG,
5259 _("Server did not found box %s on CheckDataBox request "
5260 "(code=%s, message=%s)\n"),
5261 box_id_locale, code_locale, message_locale);
5262 isds_log_message(context, message_locale);
5263 free(box_id_locale);
5264 free(code_locale);
5265 free(message_locale);
5266 err = IE_NOEXIST;
5267 goto leave;
5270 /* Other error */
5271 else if (xmlStrcmp(code, BAD_CAST "0000")) {
5272 char *code_locale = utf82locale((char*)code);
5273 char *message_locale = utf82locale((char*)message);
5274 isds_log(ILF_ISDS, ILL_DEBUG,
5275 _("Server refused CheckDataBox request "
5276 "(code=%s, message=%s)\n"), code_locale, message_locale);
5277 isds_log_message(context, message_locale);
5278 free(code_locale);
5279 free(message_locale);
5280 err = IE_ISDS;
5281 goto leave;
5284 /* Extract data */
5285 xpath_ctx = xmlXPathNewContext(response);
5286 if (!xpath_ctx) {
5287 err = IE_ERROR;
5288 goto leave;
5290 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5291 err = IE_ERROR;
5292 goto leave;
5294 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
5295 xpath_ctx);
5296 if (!result) {
5297 err = IE_ERROR;
5298 goto leave;
5300 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5301 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
5302 err = IE_ISDS;
5303 goto leave;
5305 if (result->nodesetval->nodeNr > 1) {
5306 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
5307 err = IE_ISDS;
5308 goto leave;
5310 xpath_ctx->node = result->nodesetval->nodeTab[0];
5311 xmlXPathFreeObject(result); result = NULL;
5313 EXTRACT_LONGINT("isds:dbState", box_status, 1);
5316 leave:
5317 free(string);
5318 xmlXPathFreeObject(result);
5319 xmlXPathFreeContext(xpath_ctx);
5321 free(code);
5322 free(message);
5323 xmlFreeDoc(response);
5325 if (!err)
5326 isds_log(ILF_ISDS, ILL_DEBUG,
5327 _("CheckDataBox request processed by server successfully.\n"));
5329 return err;
5333 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
5334 * code, destroy response and log success.
5335 * @context is ISDS session context.
5336 * @service_name is name of SERVICE_DB_MANIPULATION service
5337 * @box_id is UTF-8 encoded box identifier as zero terminated string
5338 * @approval is optional external approval of box manipulation
5339 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5340 * NULL, if you don't care. */
5341 static isds_error build_send_manipulationdbid_request_check_drop_response(
5342 struct isds_ctx *context, const xmlChar *service_name,
5343 const xmlChar *box_id, const struct isds_approval *approval,
5344 xmlChar **refnumber) {
5345 isds_error err = IE_SUCCESS;
5346 xmlDocPtr response = NULL;
5348 if (!context) return IE_INVALID_CONTEXT;
5349 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
5351 /* Check if connection is established */
5352 if (!context->curl) return IE_CONNECTION_CLOSED;
5354 /* Do request and check for success */
5355 err = build_send_dbid_request_check_response(context,
5356 SERVICE_DB_MANIPULATION, service_name, box_id, approval,
5357 &response, refnumber);
5358 xmlFreeDoc(response);
5360 if (!err) {
5361 char *service_name_locale = utf82locale((char *) service_name);
5362 isds_log(ILF_ISDS, ILL_DEBUG,
5363 _("%s request processed by server successfully.\n"),
5364 service_name_locale);
5365 free(service_name_locale);
5368 return err;
5372 /* Switch box into state where box can receive commercial messages (off by
5373 * default)
5374 * @context is ISDS session context.
5375 * @box_id is UTF-8 encoded box identifier as zero terminated string
5376 * @allow is true for enable, false for disable commercial messages income
5377 * @approval is optional external approval of box manipulation
5378 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5379 * NULL, if you don't care. */
5380 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
5381 const char *box_id, const _Bool allow,
5382 const struct isds_approval *approval, char **refnumber) {
5383 return build_send_manipulationdbid_request_check_drop_response(context,
5384 (allow) ? BAD_CAST "SetOpenAddressing" :
5385 BAD_CAST "ClearOpenAddressing",
5386 BAD_CAST box_id, approval, (xmlChar **) refnumber);
5390 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
5391 * message acceptance). This is just a box permission. Sender must apply
5392 * such role by sending each message.
5393 * @context is ISDS session context.
5394 * @box_id is UTF-8 encoded box identifier as zero terminated string
5395 * @allow is true for enable, false for disable OVM role permission
5396 * @approval is optional external approval of box manipulation
5397 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5398 * NULL, if you don't care. */
5399 isds_error isds_switch_effective_ovm(struct isds_ctx *context,
5400 const char *box_id, const _Bool allow,
5401 const struct isds_approval *approval, char **refnumber) {
5402 return build_send_manipulationdbid_request_check_drop_response(context,
5403 (allow) ? BAD_CAST "SetEffectiveOVM" :
5404 BAD_CAST "ClearEffectiveOVM",
5405 BAD_CAST box_id, approval, (xmlChar **) refnumber);
5409 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
5410 * code, destroy response and log success.
5411 * @context is ISDS session context.
5412 * @service_name is name of SERVICE_DB_MANIPULATION service
5413 * @owner is structure describing box
5414 * @approval is optional external approval of box manipulation
5415 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5416 * NULL, if you don't care. */
5417 static isds_error build_send_manipulationdbowner_request_check_drop_response(
5418 struct isds_ctx *context, const xmlChar *service_name,
5419 const struct isds_DbOwnerInfo *owner,
5420 const struct isds_approval *approval, xmlChar **refnumber) {
5421 isds_error err = IE_SUCCESS;
5422 char *service_name_locale = NULL;
5423 xmlNodePtr request = NULL, db_owner_info;
5424 xmlNsPtr isds_ns = NULL;
5427 if (!context) return IE_INVALID_CONTEXT;
5428 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
5430 service_name_locale = utf82locale((char*)service_name);
5431 if (!service_name_locale) {
5432 err = IE_NOMEM;
5433 goto leave;
5436 /* Build request */
5437 request = xmlNewNode(NULL, service_name);
5438 if (!request) {
5439 isds_printf_message(context,
5440 _("Could not build %s request"), service_name_locale);
5441 err = IE_ERROR;
5442 goto leave;
5444 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5445 if(!isds_ns) {
5446 isds_log_message(context, _("Could not create ISDS name space"));
5447 err = IE_ERROR;
5448 goto leave;
5450 xmlSetNs(request, isds_ns);
5453 /* Add XSD:tOwnerInfoInput child*/
5454 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
5455 err = insert_DbOwnerInfo(context, owner, db_owner_info);
5456 if (err) goto leave;
5458 /* Add XSD:gExtApproval*/
5459 err = insert_GExtApproval(context, approval, request);
5460 if (err) goto leave;
5462 /* Send it to server and process response */
5463 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
5464 service_name, &request, refnumber);
5466 leave:
5467 xmlFreeNode(request);
5468 free(service_name_locale);
5470 return err;
5474 /* Switch box accessibility state on request of box owner.
5475 * Despite the name, owner must do the request off-line. This function is
5476 * designed for such off-line meeting points (e.g. Czech POINT).
5477 * @context is ISDS session context.
5478 * @box identifies box to swith accesibilty state.
5479 * @allow is true for making accesibale, false to disallow access.
5480 * @approval is optional external approval of box manipulation
5481 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5482 * NULL, if you don't care. */
5483 isds_error isds_switch_box_accessibility_on_owner_request(
5484 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
5485 const _Bool allow, const struct isds_approval *approval,
5486 char **refnumber) {
5487 return build_send_manipulationdbowner_request_check_drop_response(context,
5488 (allow) ? BAD_CAST "EnableOwnDataBox" :
5489 BAD_CAST "DisableOwnDataBox",
5490 box, approval, (xmlChar **) refnumber);
5494 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
5495 * date.
5496 * @context is ISDS session context.
5497 * @box identifies box to swith accesibilty state.
5498 * @since is date since accesseibility has been denied. This can be past too.
5499 * Only tm_year, tm_mon and tm_mday carry sane value.
5500 * @approval is optional external approval of box manipulation
5501 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5502 * NULL, if you don't care. */
5503 isds_error isds_disable_box_accessibility_externaly(
5504 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
5505 const struct tm *since, const struct isds_approval *approval,
5506 char **refnumber) {
5507 isds_error err = IE_SUCCESS;
5508 char *service_name_locale = NULL;
5509 xmlNodePtr request = NULL, node;
5510 xmlNsPtr isds_ns = NULL;
5511 xmlChar *string = NULL;
5514 if (!context) return IE_INVALID_CONTEXT;
5515 if (!box || !since) return IE_INVAL;
5517 /* Build request */
5518 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
5519 if (!request) {
5520 isds_printf_message(context,
5521 _("Could not build %s request"), "DisableDataBoxExternally");
5522 err = IE_ERROR;
5523 goto leave;
5525 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5526 if(!isds_ns) {
5527 isds_log_message(context, _("Could not create ISDS name space"));
5528 err = IE_ERROR;
5529 goto leave;
5531 xmlSetNs(request, isds_ns);
5534 /* Add @box identification */
5535 INSERT_ELEMENT(node, request, "dbOwnerInfo");
5536 err = insert_DbOwnerInfo(context, box, node);
5537 if (err) goto leave;
5539 /* Add @since date */
5540 err = tm2datestring(since, &string);
5541 if(err) {
5542 isds_log_message(context,
5543 _("Could not convert `since' argument to ISO date string"));
5544 goto leave;
5546 INSERT_STRING(request, "dbOwnerDisableDate", string);
5547 zfree(string);
5549 /* Add @approval */
5550 err = insert_GExtApproval(context, approval, request);
5551 if (err) goto leave;
5553 /* Send it to server and process response */
5554 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
5555 BAD_CAST "DisableDataBoxExternally", &request,
5556 (xmlChar **) refnumber);
5558 leave:
5559 free(string);
5560 xmlFreeNode(request);
5561 free(service_name_locale);
5563 return err;
5567 /* Insert struct isds_message data (envelope (recipient data optional) and
5568 * documents) into XML tree
5569 * @context is sesstion context
5570 * @outgoing_message is libsids structure with message data
5571 * @create_message is XML CreateMessage or CreateMultipleMessage element
5572 * @process_recipient true for recipient data serialization, false for no
5573 * serialization */
5574 static isds_error insert_envelope_files(struct isds_ctx *context,
5575 const struct isds_message *outgoing_message, xmlNodePtr create_message,
5576 const _Bool process_recipient) {
5578 isds_error err = IE_SUCCESS;
5579 xmlNodePtr envelope, dm_files, node;
5580 xmlChar *string = NULL;
5582 if (!context) return IE_INVALID_CONTEXT;
5583 if (!outgoing_message || !create_message) return IE_INVAL;
5586 /* Build envelope */
5587 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
5588 if (!envelope) {
5589 isds_printf_message(context, _("Could not add dmEnvelope child to "
5590 "%s element"), create_message->name);
5591 return IE_ERROR;
5594 if (!outgoing_message->envelope) {
5595 isds_log_message(context, _("Outgoing message is missing envelope"));
5596 err = IE_INVAL;
5597 goto leave;
5600 INSERT_STRING(envelope, "dmSenderOrgUnit",
5601 outgoing_message->envelope->dmSenderOrgUnit);
5602 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
5603 outgoing_message->envelope->dmSenderOrgUnitNum, string);
5605 if (process_recipient) {
5606 if (!outgoing_message->envelope->dbIDRecipient) {
5607 isds_log_message(context,
5608 _("Outgoing message is missing recipient box identifier"));
5609 err = IE_INVAL;
5610 goto leave;
5612 INSERT_STRING(envelope, "dbIDRecipient",
5613 outgoing_message->envelope->dbIDRecipient);
5615 INSERT_STRING(envelope, "dmRecipientOrgUnit",
5616 outgoing_message->envelope->dmRecipientOrgUnit);
5617 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
5618 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
5619 INSERT_STRING(envelope, "dmToHands",
5620 outgoing_message->envelope->dmToHands);
5623 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
5624 "dmAnnotation");
5625 INSERT_STRING(envelope, "dmAnnotation",
5626 outgoing_message->envelope->dmAnnotation);
5628 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
5629 0, 50, "dmRecipientRefNumber");
5630 INSERT_STRING(envelope, "dmRecipientRefNumber",
5631 outgoing_message->envelope->dmRecipientRefNumber);
5633 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
5634 0, 50, "dmSenderRefNumber");
5635 INSERT_STRING(envelope, "dmSenderRefNumber",
5636 outgoing_message->envelope->dmSenderRefNumber);
5638 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
5639 0, 50, "dmRecipientIdent");
5640 INSERT_STRING(envelope, "dmRecipientIdent",
5641 outgoing_message->envelope->dmRecipientIdent);
5643 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
5644 0, 50, "dmSenderIdent");
5645 INSERT_STRING(envelope, "dmSenderIdent",
5646 outgoing_message->envelope->dmSenderIdent);
5648 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
5649 outgoing_message->envelope->dmLegalTitleLaw, string);
5650 INSERT_LONGINT(envelope, "dmLegalTitleYear",
5651 outgoing_message->envelope->dmLegalTitleYear, string);
5652 INSERT_STRING(envelope, "dmLegalTitleSect",
5653 outgoing_message->envelope->dmLegalTitleSect);
5654 INSERT_STRING(envelope, "dmLegalTitlePar",
5655 outgoing_message->envelope->dmLegalTitlePar);
5656 INSERT_STRING(envelope, "dmLegalTitlePoint",
5657 outgoing_message->envelope->dmLegalTitlePoint);
5659 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
5660 outgoing_message->envelope->dmPersonalDelivery);
5661 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
5662 outgoing_message->envelope->dmAllowSubstDelivery);
5664 /* ???: Should we require value for dbEffectiveOVM sender?
5665 * ISDS has default as true */
5666 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
5669 /* Append dmFiles */
5670 if (!outgoing_message->documents) {
5671 isds_log_message(context,
5672 _("Outgoing message is missing list of documents"));
5673 err = IE_INVAL;
5674 goto leave;
5676 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
5677 if (!dm_files) {
5678 isds_printf_message(context, _("Could not add dmFiles child to "
5679 "%s element"), create_message->name);
5680 err = IE_ERROR;
5681 goto leave;
5684 /* Check for document hieararchy */
5685 err = check_documents_hierarchy(context, outgoing_message->documents);
5686 if (err) goto leave;
5688 /* Process each document */
5689 for (struct isds_list *item =
5690 (struct isds_list *) outgoing_message->documents;
5691 item; item = item->next) {
5692 if (!item->data) {
5693 isds_log_message(context,
5694 _("List of documents contains empty item"));
5695 err = IE_INVAL;
5696 goto leave;
5698 /* FIXME: Check for dmFileMetaType and for document references.
5699 * Only first document can be of MAIN type */
5700 err = insert_document(context, (struct isds_document*) item->data,
5701 dm_files);
5703 if (err) goto leave;
5706 leave:
5707 free(string);
5708 return err;
5712 /* Send a message via ISDS to a recipent
5713 * @context is session context
5714 * @outgoing_message is message to send; Some memebers are mandatory (like
5715 * dbIDRecipient), some are optional and some are irrelevant (especialy data
5716 * about sender). Included pointer to isds_list documents must contain at
5717 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
5718 * members will be filled with valid data from ISDS. Exact list of write
5719 * members is subject to change. Currently dmId is changed.
5720 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
5721 isds_error isds_send_message(struct isds_ctx *context,
5722 struct isds_message *outgoing_message) {
5724 isds_error err = IE_SUCCESS;
5725 xmlNsPtr isds_ns = NULL;
5726 xmlNodePtr request = NULL;
5727 xmlDocPtr response = NULL;
5728 xmlChar *code = NULL, *message = NULL;
5729 xmlXPathContextPtr xpath_ctx = NULL;
5730 xmlXPathObjectPtr result = NULL;
5731 _Bool message_is_complete = 0;
5733 if (!context) return IE_INVALID_CONTEXT;
5734 if (!outgoing_message) return IE_INVAL;
5736 /* Check if connection is established
5737 * TODO: This check should be done donwstairs. */
5738 if (!context->curl) return IE_CONNECTION_CLOSED;
5741 /* Build CreateMessage request */
5742 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
5743 if (!request) {
5744 isds_log_message(context,
5745 _("Could build CreateMessage request"));
5746 return IE_ERROR;
5748 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5749 if(!isds_ns) {
5750 isds_log_message(context, _("Could not create ISDS name space"));
5751 xmlFreeNode(request);
5752 return IE_ERROR;
5754 xmlSetNs(request, isds_ns);
5756 /* Append envelope and files */
5757 err = insert_envelope_files(context, outgoing_message, request, 1);
5758 if (err) goto leave;
5761 /* Signal we can serilize message since now */
5762 message_is_complete = 1;
5765 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
5767 /* Sent request */
5768 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
5770 /* Dont' destroy request, we want to provide it to application later */
5772 if (err) {
5773 isds_log(ILF_ISDS, ILL_DEBUG,
5774 _("Processing ISDS response on CreateMessage "
5775 "request failed\n"));
5776 goto leave;
5779 /* Check for response status */
5780 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
5781 &code, &message, NULL);
5782 if (err) {
5783 isds_log(ILF_ISDS, ILL_DEBUG,
5784 _("ISDS response on CreateMessage request "
5785 "is missing status\n"));
5786 goto leave;
5789 /* Request processed, but refused by server or server failed */
5790 if (xmlStrcmp(code, BAD_CAST "0000")) {
5791 char *box_id_locale =
5792 utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
5793 char *code_locale = utf82locale((char*)code);
5794 char *message_locale = utf82locale((char*)message);
5795 isds_log(ILF_ISDS, ILL_DEBUG,
5796 _("Server did not accept message for %s on CreateMessage "
5797 "request (code=%s, message=%s)\n"),
5798 box_id_locale, code_locale, message_locale);
5799 isds_log_message(context, message_locale);
5800 free(box_id_locale);
5801 free(code_locale);
5802 free(message_locale);
5803 err = IE_ISDS;
5804 goto leave;
5808 /* Extract data */
5809 xpath_ctx = xmlXPathNewContext(response);
5810 if (!xpath_ctx) {
5811 err = IE_ERROR;
5812 goto leave;
5814 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5815 err = IE_ERROR;
5816 goto leave;
5818 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
5819 xpath_ctx);
5820 if (!result) {
5821 err = IE_ERROR;
5822 goto leave;
5824 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5825 isds_log_message(context, _("Missing CreateMessageResponse element"));
5826 err = IE_ISDS;
5827 goto leave;
5829 if (result->nodesetval->nodeNr > 1) {
5830 isds_log_message(context, _("Multiple CreateMessageResponse element"));
5831 err = IE_ISDS;
5832 goto leave;
5834 xpath_ctx->node = result->nodesetval->nodeTab[0];
5835 xmlXPathFreeObject(result); result = NULL;
5837 if (outgoing_message->envelope->dmID) {
5838 free(outgoing_message->envelope->dmID);
5839 outgoing_message->envelope->dmID = NULL;
5841 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
5842 if (!outgoing_message->envelope->dmID) {
5843 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
5844 "but did not returen assigned message ID\n"));
5847 leave:
5848 /* TODO: Serialize message into structure member raw */
5849 /* XXX: Each web service transport message in different format.
5850 * Therefore it's not possible to save them directly.
5851 * To save them, one must figure out common format.
5852 * We can leave it on application, or we can implement the ESS format. */
5853 /*if (message_is_complete) {
5854 if (outgoing_message->envelope->dmID) {
5856 /* Add assigned message ID as first child*/
5857 /*xmlNodePtr dmid_text = xmlNewText(
5858 (xmlChar *) outgoing_message->envelope->dmID);
5859 if (!dmid_text) goto serialization_failed;
5861 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
5862 BAD_CAST "dmID");
5863 if (!dmid_element) {
5864 xmlFreeNode(dmid_text);
5865 goto serialization_failed;
5868 xmlNodePtr dmid_element_with_text =
5869 xmlAddChild(dmid_element, dmid_text);
5870 if (!dmid_element_with_text) {
5871 xmlFreeNode(dmid_element);
5872 xmlFreeNode(dmid_text);
5873 goto serialization_failed;
5876 node = xmlAddPrevSibling(envelope->childern,
5877 dmid_element_with_text);
5878 if (!node) {
5879 xmlFreeNodeList(dmid_element_with_text);
5880 goto serialization_failed;
5884 /* Serialize message with ID into raw */
5885 /*buffer = serialize_element(envelope)*/
5886 /* }
5888 serialization_failed:
5892 /* Clean up */
5893 xmlXPathFreeObject(result);
5894 xmlXPathFreeContext(xpath_ctx);
5896 free(code);
5897 free(message);
5898 xmlFreeDoc(response);
5899 xmlFreeNode(request);
5901 if (!err)
5902 isds_log(ILF_ISDS, ILL_DEBUG,
5903 _("CreateMessage request processed by server "
5904 "successfully.\n"));
5906 return err;
5910 /* Send a message via ISDS to a multiple recipents
5911 * @context is session context
5912 * @outgoing_message is message to send; Some memebers are mandatory,
5913 * some are optional and some are irrelevant (especialy data
5914 * about sender). Data about recipient will be substituted by ISDS from
5915 * @copies. Included pointer to isds_list documents must
5916 * contain at least one document of FILEMETATYPE_MAIN.
5917 * @copies is list of isds_message_copy structures addressing all desired
5918 * recipients. This is read-write structure, some members will be filled with
5919 * valid data from ISDS (message IDs, error codes, error descriptions).
5920 * @return
5921 * ISDS_SUCCESS if all messages have been sent
5922 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
5923 * succesed messages can be identified by copies->data->error),
5924 * or other error code if something other goes wrong. */
5925 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
5926 const struct isds_message *outgoing_message,
5927 struct isds_list *copies) {
5929 isds_error err = IE_SUCCESS, append_err;
5930 xmlNsPtr isds_ns = NULL;
5931 xmlNodePtr request = NULL, recipients, recipient, node;
5932 struct isds_list *item;
5933 struct isds_message_copy *copy;
5934 xmlDocPtr response = NULL;
5935 xmlChar *code = NULL, *message = NULL;
5936 xmlXPathContextPtr xpath_ctx = NULL;
5937 xmlXPathObjectPtr result = NULL;
5938 xmlChar *string = NULL;
5939 int i;
5941 if (!context) return IE_INVALID_CONTEXT;
5942 if (!outgoing_message || !copies) return IE_INVAL;
5944 /* Check if connection is established
5945 * TODO: This check should be done donwstairs. */
5946 if (!context->curl) return IE_CONNECTION_CLOSED;
5949 /* Build CreateMultipleMessage request */
5950 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
5951 if (!request) {
5952 isds_log_message(context,
5953 _("Could build CreateMultipleMessage request"));
5954 return IE_ERROR;
5956 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5957 if(!isds_ns) {
5958 isds_log_message(context, _("Could not create ISDS name space"));
5959 xmlFreeNode(request);
5960 return IE_ERROR;
5962 xmlSetNs(request, isds_ns);
5965 /* Build recipients */
5966 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
5967 if (!recipients) {
5968 isds_log_message(context, _("Could not add dmRecipients child to "
5969 "CreateMultipleMessage element"));
5970 xmlFreeNode(request);
5971 return IE_ERROR;
5974 /* Insert each recipient */
5975 for (item = copies; item; item = item->next) {
5976 copy = (struct isds_message_copy *) item->data;
5977 if (!copy) {
5978 isds_log_message(context,
5979 _("copies list item contains empty data"));
5980 err = IE_INVAL;
5981 goto leave;
5984 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
5985 if (!recipient) {
5986 isds_log_message(context, _("Could not add dmRecipient child to "
5987 "dmRecipient element"));
5988 err = IE_ERROR;
5989 goto leave;
5992 if (!copy->dbIDRecipient) {
5993 isds_log_message(context,
5994 _("Message copy is missing recipient box identifier"));
5995 err = IE_INVAL;
5996 goto leave;
5998 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
5999 INSERT_STRING(recipient, "dmRecipientOrgUnit",
6000 copy->dmRecipientOrgUnit);
6001 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
6002 copy->dmRecipientOrgUnitNum, string);
6003 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
6006 /* Append envelope and files */
6007 err = insert_envelope_files(context, outgoing_message, request, 0);
6008 if (err) goto leave;
6011 isds_log(ILF_ISDS, ILL_DEBUG,
6012 _("Sending CreateMultipleMessage request to ISDS\n"));
6014 /* Sent request */
6015 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
6016 if (err) {
6017 isds_log(ILF_ISDS, ILL_DEBUG,
6018 _("Processing ISDS response on CreateMultipleMessage "
6019 "request failed\n"));
6020 goto leave;
6023 /* Check for response status */
6024 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
6025 &code, &message, NULL);
6026 if (err) {
6027 isds_log(ILF_ISDS, ILL_DEBUG,
6028 _("ISDS response on CreateMultipleMessage request "
6029 "is missing status\n"));
6030 goto leave;
6033 /* Request processed, but some copies failed */
6034 if (!xmlStrcmp(code, BAD_CAST "0004")) {
6035 char *box_id_locale =
6036 utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
6037 char *code_locale = utf82locale((char*)code);
6038 char *message_locale = utf82locale((char*)message);
6039 isds_log(ILF_ISDS, ILL_DEBUG,
6040 _("Server did accept message for multiple recipients "
6041 "on CreateMultipleMessage request but delivery to "
6042 "some of them failed (code=%s, message=%s)\n"),
6043 box_id_locale, code_locale, message_locale);
6044 isds_log_message(context, message_locale);
6045 free(box_id_locale);
6046 free(code_locale);
6047 free(message_locale);
6048 err = IE_PARTIAL_SUCCESS;
6051 /* Request refused by server as whole */
6052 else if (xmlStrcmp(code, BAD_CAST "0000")) {
6053 char *box_id_locale =
6054 utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
6055 char *code_locale = utf82locale((char*)code);
6056 char *message_locale = utf82locale((char*)message);
6057 isds_log(ILF_ISDS, ILL_DEBUG,
6058 _("Server did not accept message for multiple recipients "
6059 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
6060 box_id_locale, code_locale, message_locale);
6061 isds_log_message(context, message_locale);
6062 free(box_id_locale);
6063 free(code_locale);
6064 free(message_locale);
6065 err = IE_ISDS;
6066 goto leave;
6070 /* Extract data */
6071 xpath_ctx = xmlXPathNewContext(response);
6072 if (!xpath_ctx) {
6073 err = IE_ERROR;
6074 goto leave;
6076 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6077 err = IE_ERROR;
6078 goto leave;
6080 result = xmlXPathEvalExpression(
6081 BAD_CAST "/isds:CreateMultipleMessageResponse"
6082 "/isds:dmMultipleStatus/isds:dmSingleStatus",
6083 xpath_ctx);
6084 if (!result) {
6085 err = IE_ERROR;
6086 goto leave;
6088 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6089 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
6090 err = IE_ISDS;
6091 goto leave;
6094 /* Extract message ID and delivery status for each copy */
6095 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
6096 item = item->next, i++) {
6097 copy = (struct isds_message_copy *) item->data;
6098 xpath_ctx->node = result->nodesetval->nodeTab[i];
6100 append_err = append_TMStatus(context, copy, xpath_ctx);
6101 if (append_err) {
6102 err = append_err;
6103 goto leave;
6106 if (item || i < result->nodesetval->nodeNr) {
6107 isds_printf_message(context, _("ISDS returned unexpected number of "
6108 "message copy delivery states: %d"),
6109 result->nodesetval->nodeNr);
6110 err = IE_ISDS;
6111 goto leave;
6115 leave:
6116 /* Clean up */
6117 free(string);
6118 xmlXPathFreeObject(result);
6119 xmlXPathFreeContext(xpath_ctx);
6121 free(code);
6122 free(message);
6123 xmlFreeDoc(response);
6124 xmlFreeNode(request);
6126 if (!err)
6127 isds_log(ILF_ISDS, ILL_DEBUG,
6128 _("CreateMultipleMessageResponse request processed by server "
6129 "successfully.\n"));
6131 return err;
6135 /* Get list of messages. This is common core for getting sent or received
6136 * messaeges.
6137 * Any criterion argument can be NULL, if you don't care about it.
6138 * @context is session context. Must not be NULL.
6139 * @outgoing_direction is true if you want list of outgoing messages,
6140 * it's false if you want incoming messages.
6141 * @from_time is minimal time and date of message sending inclusive.
6142 * @to_time is maximal time and date of message sending inclusive
6143 * @organization_unit_number is number of sender/recipient respectively.
6144 * @status_filter is bit field of isds_message_status values. Use special
6145 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
6146 * all values, you can use bitwise arithmetic if you want.)
6147 * @offset is index of first message we are interested in. First message is 1.
6148 * Set to 0 (or 1) if you don't care.
6149 * @number is maximal length of list you want to get as input value, outputs
6150 * number of messages matching these criteria. Can be NULL if you don't care
6151 * (applies to output value either).
6152 * @messages is automatically reallocated list of isds_message's. Be ware that
6153 * it returns only brief overview (envelope and some other fields) about each
6154 * message, not the complete message. FIXME: Specify exact fields.
6155 * The list is sorted by delivery time in ascending order.
6156 * Use NULL if
6157 * you don't care about don't need the data (useful if you want to know only
6158 * the @number). If you provide &NULL, list will be allocated on heap, if you
6159 * provide pointer to non-NULL, list will be freed automacally at first. Also
6160 * in case of error the list will be NULLed.
6161 * @return IE_SUCCESS or appropriate error code. */
6162 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
6163 _Bool outgoing_direction,
6164 const struct timeval *from_time, const struct timeval *to_time,
6165 const long int *organization_unit_number,
6166 const unsigned int status_filter,
6167 const unsigned long int offset, unsigned long int *number,
6168 struct isds_list **messages) {
6170 isds_error err = IE_SUCCESS;
6171 xmlNsPtr isds_ns = NULL;
6172 xmlNodePtr request = NULL, node;
6173 xmlDocPtr response = NULL;
6174 xmlChar *code = NULL, *message = NULL;
6175 xmlXPathContextPtr xpath_ctx = NULL;
6176 xmlXPathObjectPtr result = NULL;
6177 xmlChar *string = NULL;
6178 long unsigned int count = 0;
6180 if (!context) return IE_INVALID_CONTEXT;
6182 /* Free former message list if any */
6183 if (messages) isds_list_free(messages);
6185 /* Check if connection is established
6186 * TODO: This check should be done donwstairs. */
6187 if (!context->curl) return IE_CONNECTION_CLOSED;
6189 /* Build GetListOf*Messages request */
6190 request = xmlNewNode(NULL,
6191 (outgoing_direction) ?
6192 BAD_CAST "GetListOfSentMessages" :
6193 BAD_CAST "GetListOfReceivedMessages"
6195 if (!request) {
6196 isds_log_message(context,
6197 (outgoing_direction) ?
6198 _("Could not build GetListOfSentMessages request") :
6199 _("Could not build GetListOfReceivedMessages request")
6201 return IE_ERROR;
6203 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6204 if(!isds_ns) {
6205 isds_log_message(context, _("Could not create ISDS name space"));
6206 xmlFreeNode(request);
6207 return IE_ERROR;
6209 xmlSetNs(request, isds_ns);
6212 if (from_time) {
6213 err = timeval2timestring(from_time, &string);
6214 if (err) goto leave;
6216 INSERT_STRING(request, "dmFromTime", string);
6217 free(string); string = NULL;
6219 if (to_time) {
6220 err = timeval2timestring(to_time, &string);
6221 if (err) goto leave;
6223 INSERT_STRING(request, "dmToTime", string);
6224 free(string); string = NULL;
6226 if (outgoing_direction) {
6227 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
6228 organization_unit_number, string);
6229 } else {
6230 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
6231 organization_unit_number, string);
6234 if (status_filter > MESSAGESTATE_ANY) {
6235 isds_printf_message(context,
6236 _("Invalid message state filter value: %ld"), status_filter);
6237 err = IE_INVAL;
6238 goto leave;
6240 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
6242 if (offset > 0 ) {
6243 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
6244 } else {
6245 INSERT_STRING(request, "dmOffset", "1");
6248 /* number 0 means no limit */
6249 if (number && *number == 0) {
6250 INSERT_STRING(request, "dmLimit", NULL);
6251 } else {
6252 INSERT_ULONGINT(request, "dmLimit", number, string);
6256 isds_log(ILF_ISDS, ILL_DEBUG,
6257 (outgoing_direction) ?
6258 _("Sending GetListOfSentMessages request to ISDS\n") :
6259 _("Sending GetListOfReceivedMessages request to ISDS\n")
6262 /* Sent request */
6263 err = isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
6264 xmlFreeNode(request); request = NULL;
6266 if (err) {
6267 isds_log(ILF_ISDS, ILL_DEBUG,
6268 (outgoing_direction) ?
6269 _("Processing ISDS response on GetListOfSentMessages "
6270 "request failed\n") :
6271 _("Processing ISDS response on GetListOfReceivedMessages "
6272 "request failed\n")
6274 goto leave;
6277 /* Check for response status */
6278 err = isds_response_status(context, SERVICE_DM_INFO, response,
6279 &code, &message, NULL);
6280 if (err) {
6281 isds_log(ILF_ISDS, ILL_DEBUG,
6282 (outgoing_direction) ?
6283 _("ISDS response on GetListOfSentMessages request "
6284 "is missing status\n") :
6285 _("ISDS response on GetListOfReceivedMessages request "
6286 "is missing status\n")
6288 goto leave;
6291 /* Request processed, but nothing found */
6292 if (xmlStrcmp(code, BAD_CAST "0000")) {
6293 char *code_locale = utf82locale((char*)code);
6294 char *message_locale = utf82locale((char*)message);
6295 isds_log(ILF_ISDS, ILL_DEBUG,
6296 (outgoing_direction) ?
6297 _("Server refused GetListOfSentMessages request "
6298 "(code=%s, message=%s)\n") :
6299 _("Server refused GetListOfReceivedMessages request "
6300 "(code=%s, message=%s)\n"),
6301 code_locale, message_locale);
6302 isds_log_message(context, message_locale);
6303 free(code_locale);
6304 free(message_locale);
6305 err = IE_ISDS;
6306 goto leave;
6310 /* Extract data */
6311 xpath_ctx = xmlXPathNewContext(response);
6312 if (!xpath_ctx) {
6313 err = IE_ERROR;
6314 goto leave;
6316 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6317 err = IE_ERROR;
6318 goto leave;
6320 result = xmlXPathEvalExpression(
6321 (outgoing_direction) ?
6322 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
6323 "isds:dmRecords/isds:dmRecord" :
6324 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
6325 "isds:dmRecords/isds:dmRecord",
6326 xpath_ctx);
6327 if (!result) {
6328 err = IE_ERROR;
6329 goto leave;
6332 /* Fill output arguments in */
6333 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6334 struct isds_envelope *envelope;
6335 struct isds_list *item = NULL, *last_item = NULL;
6337 for (count = 0; count < result->nodesetval->nodeNr; count++) {
6338 /* Create new message */
6339 item = calloc(1, sizeof(*item));
6340 if (!item) {
6341 err = IE_NOMEM;
6342 goto leave;
6344 item->destructor = (void(*)(void**)) &isds_message_free;
6345 item->data = calloc(1, sizeof(struct isds_message));
6346 if (!item->data) {
6347 isds_list_free(&item);
6348 err = IE_NOMEM;
6349 goto leave;
6352 /* Extract envelope data */
6353 xpath_ctx->node = result->nodesetval->nodeTab[count];
6354 envelope = NULL;
6355 err = extract_DmRecord(context, &envelope, xpath_ctx);
6356 if (err) {
6357 isds_list_free(&item);
6358 goto leave;
6361 /* Attach extracted envelope */
6362 ((struct isds_message *) item->data)->envelope = envelope;
6364 /* Append new message into the list */
6365 if (!*messages) {
6366 *messages = last_item = item;
6367 } else {
6368 last_item->next = item;
6369 last_item = item;
6373 if (number) *number = count;
6375 leave:
6376 if (err) {
6377 isds_list_free(messages);
6380 free(string);
6381 xmlXPathFreeObject(result);
6382 xmlXPathFreeContext(xpath_ctx);
6384 free(code);
6385 free(message);
6386 xmlFreeDoc(response);
6387 xmlFreeNode(request);
6389 if (!err)
6390 isds_log(ILF_ISDS, ILL_DEBUG,
6391 (outgoing_direction) ?
6392 _("GetListOfSentMessages request processed by server "
6393 "successfully.\n") :
6394 _("GetListOfReceivedMessages request processed by server "
6395 "successfully.\n")
6397 return err;
6401 /* Get list of outgoing (already sent) messages.
6402 * Any criterion argument can be NULL, if you don't care about it.
6403 * @context is session context. Must not be NULL.
6404 * @from_time is minimal time and date of message sending inclusive.
6405 * @to_time is maximal time and date of message sending inclusive
6406 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
6407 * @status_filter is bit field of isds_message_status values. Use special
6408 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
6409 * all values, you can use bitwise arithmetic if you want.)
6410 * @offset is index of first message we are interested in. First message is 1.
6411 * Set to 0 (or 1) if you don't care.
6412 * @number is maximal length of list you want to get as input value, outputs
6413 * number of messages matching these criteria. Can be NULL if you don't care
6414 * (applies to output value either).
6415 * @messages is automatically reallocated list of isds_message's. Be ware that
6416 * it returns only brief overview (envelope and some other fields) about each
6417 * message, not the complete message. FIXME: Specify exact fields.
6418 * The list is sorted by delivery time in ascending order.
6419 * Use NULL if you don't care about the metadata (useful if you want to know
6420 * only the @number). If you provide &NULL, list will be allocated on heap,
6421 * if you provide pointer to non-NULL, list will be freed automacally at first.
6422 * Also in case of error the list will be NULLed.
6423 * @return IE_SUCCESS or appropriate error code. */
6424 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
6425 const struct timeval *from_time, const struct timeval *to_time,
6426 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
6427 const unsigned long int offset, unsigned long int *number,
6428 struct isds_list **messages) {
6430 return isds_get_list_of_messages(
6431 context, 1,
6432 from_time, to_time, dmSenderOrgUnitNum, status_filter,
6433 offset, number,
6434 messages);
6438 /* Get list of incoming (addressed to you) messages.
6439 * Any criterion argument can be NULL, if you don't care about it.
6440 * @context is session context. Must not be NULL.
6441 * @from_time is minimal time and date of message sending inclusive.
6442 * @to_time is maximal time and date of message sending inclusive
6443 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
6444 * @status_filter is bit field of isds_message_status values. Use special
6445 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
6446 * all values, you can use bitwise arithmetic if you want.)
6447 * @offset is index of first message we are interested in. First message is 1.
6448 * Set to 0 (or 1) if you don't care.
6449 * @number is maximal length of list you want to get as input value, outputs
6450 * number of messages matching these criteria. Can be NULL if you don't care
6451 * (applies to output value either).
6452 * @messages is automatically reallocated list of isds_message's. Be ware that
6453 * it returns only brief overview (envelope and some other fields) about each
6454 * message, not the complete message. FIXME: Specify exact fields.
6455 * Use NULL if you don't care about the metadata (useful if you want to know
6456 * only the @number). If you provide &NULL, list will be allocated on heap,
6457 * if you provide pointer to non-NULL, list will be freed automacally at first.
6458 * Also in case of error the list will be NULLed.
6459 * @return IE_SUCCESS or appropriate error code. */
6460 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
6461 const struct timeval *from_time, const struct timeval *to_time,
6462 const long int *dmRecipientOrgUnitNum,
6463 const unsigned int status_filter,
6464 const unsigned long int offset, unsigned long int *number,
6465 struct isds_list **messages) {
6467 return isds_get_list_of_messages(
6468 context, 0,
6469 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
6470 offset, number,
6471 messages);
6475 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
6476 * code
6477 * @context is session context
6478 * @service is ISDS WS service handler
6479 * @service_name is name of SERVICE_DM_OPERATIONS
6480 * @message_id is message ID to send as service argument to ISDS
6481 * @response is server SOAP body response as XML document
6482 * @raw_response is automatically reallocated bitstream with response body. Use
6483 * NULL if you don't care
6484 * @raw_response_length is size of @raw_response in bytes
6485 * @code is ISDS status code
6486 * @status_message is ISDS status message
6487 * @return error coded from lower layer, context message will be set up
6488 * appropriately. */
6489 static isds_error build_send_check_message_request(struct isds_ctx *context,
6490 const isds_service service, const xmlChar *service_name,
6491 const char *message_id,
6492 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
6493 xmlChar **code, xmlChar **status_message) {
6495 isds_error err = IE_SUCCESS;
6496 char *service_name_locale = NULL, *message_id_locale = NULL;
6497 xmlNodePtr request = NULL, node;
6498 xmlNsPtr isds_ns = NULL;
6500 if (!context) return IE_INVALID_CONTEXT;
6501 if (!service_name || !message_id) return IE_INVAL;
6502 if (!response || !code || !status_message) return IE_INVAL;
6503 if (!raw_response_length && raw_response) return IE_INVAL;
6505 /* Free output argument */
6506 xmlFreeDoc(*response); *response = NULL;
6507 if (raw_response) zfree(*raw_response);
6508 free(*code);
6509 free(*status_message);
6512 /* Check if connection is established
6513 * TODO: This check should be done donwstairs. */
6514 if (!context->curl) return IE_CONNECTION_CLOSED;
6516 service_name_locale = utf82locale((char*)service_name);
6517 message_id_locale = utf82locale(message_id);
6518 if (!service_name_locale || !message_id_locale) {
6519 err = IE_NOMEM;
6520 goto leave;
6523 /* Build request */
6524 request = xmlNewNode(NULL, service_name);
6525 if (!request) {
6526 isds_printf_message(context,
6527 _("Could not build %s request"), service_name_locale);
6528 err = IE_ERROR;
6529 goto leave;
6531 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6532 if(!isds_ns) {
6533 isds_log_message(context, _("Could not create ISDS name space"));
6534 err = IE_ERROR;
6535 goto leave;
6537 xmlSetNs(request, isds_ns);
6540 /* Add requested ID */
6541 err = validate_message_id_length(context, (xmlChar *) message_id);
6542 if (err) goto leave;
6543 INSERT_STRING(request, "dmID", message_id);
6546 isds_log(ILF_ISDS, ILL_DEBUG,
6547 _("Sending %s request for %s message ID to ISDS\n"),
6548 service_name_locale, message_id_locale);
6550 /* Send request */
6551 err = isds(context, service, request, response,
6552 raw_response, raw_response_length);
6553 xmlFreeNode(request); request = NULL;
6555 if (err) {
6556 isds_log(ILF_ISDS, ILL_DEBUG,
6557 _("Processing ISDS response on %s request failed\n"),
6558 service_name_locale);
6559 goto leave;
6562 /* Check for response status */
6563 err = isds_response_status(context, service, *response,
6564 code, status_message, NULL);
6565 if (err) {
6566 isds_log(ILF_ISDS, ILL_DEBUG,
6567 _("ISDS response on %s request is missing status\n"),
6568 service_name_locale);
6569 goto leave;
6572 /* Request processed, but nothing found */
6573 if (xmlStrcmp(*code, BAD_CAST "0000")) {
6574 char *code_locale = utf82locale((char*) *code);
6575 char *status_message_locale = utf82locale((char*) *status_message);
6576 isds_log(ILF_ISDS, ILL_DEBUG,
6577 _("Server refused %s request for %s message ID "
6578 "(code=%s, message=%s)\n"),
6579 service_name_locale, message_id_locale,
6580 code_locale, status_message_locale);
6581 isds_log_message(context, status_message_locale);
6582 free(code_locale);
6583 free(status_message_locale);
6584 err = IE_ISDS;
6585 goto leave;
6588 leave:
6589 free(message_id_locale);
6590 free(service_name_locale);
6591 xmlFreeNode(request);
6592 return err;
6596 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
6597 * signed data and free ISDS response.
6598 * @context is session context
6599 * @message_id is UTF-8 encoded message ID for loging purpose
6600 * @response is parsed XML document. It will be freed and NULLed in the middle
6601 * of function run to save memmory. This is not guaranted in case of error.
6602 * @request_name is name of ISDS request used to construct response root
6603 * element name and for logging purpose.
6604 * @raw is reallocated output buffer with DER encoded CMS data
6605 * @raw_length is size of @raw buffer in bytes
6606 * @returns standard error codes, in case of error, @raw will be freed and
6607 * NULLed, @response sometimes. */
6608 static isds_error find_extract_signed_data_free_response(
6609 struct isds_ctx *context, const xmlChar *message_id,
6610 xmlDocPtr *response, const xmlChar *request_name,
6611 void **raw, size_t *raw_length) {
6613 isds_error err = IE_SUCCESS;
6614 char *xpath_expression = NULL;
6615 xmlXPathContextPtr xpath_ctx = NULL;
6616 xmlXPathObjectPtr result = NULL;
6617 char *encoded_structure = NULL;
6619 if (!context) return IE_INVALID_CONTEXT;
6620 if (!raw) return IE_INVAL;
6621 zfree(*raw);
6622 if (!message_id || !response || !*response || !request_name || !raw_length)
6623 return IE_INVAL;
6625 /* Build XPath expression */
6626 xpath_expression = astrcat3("/isds:", (char *) request_name,
6627 "Response/isds:dmSignature");
6628 if (!xpath_expression) return IE_NOMEM;
6630 /* Extract data */
6631 xpath_ctx = xmlXPathNewContext(*response);
6632 if (!xpath_ctx) {
6633 err = IE_ERROR;
6634 goto leave;
6636 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6637 err = IE_ERROR;
6638 goto leave;
6640 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
6641 if (!result) {
6642 err = IE_ERROR;
6643 goto leave;
6645 /* Empty response */
6646 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6647 char *message_id_locale = utf82locale((char*) message_id);
6648 isds_printf_message(context,
6649 _("Server did not return any signed data for mesage ID `%s' "
6650 "on %s request"),
6651 message_id_locale, request_name);
6652 free(message_id_locale);
6653 err = IE_ISDS;
6654 goto leave;
6656 /* More reponses */
6657 if (result->nodesetval->nodeNr > 1) {
6658 char *message_id_locale = utf82locale((char*) message_id);
6659 isds_printf_message(context,
6660 _("Server did return more signed data for message ID `%s' "
6661 "on %s request"),
6662 message_id_locale, request_name);
6663 free(message_id_locale);
6664 err = IE_ISDS;
6665 goto leave;
6667 /* One response */
6668 xpath_ctx->node = result->nodesetval->nodeTab[0];
6670 /* Extract PKCS#7 structure */
6671 EXTRACT_STRING(".", encoded_structure);
6672 if (!encoded_structure) {
6673 isds_log_message(context, _("dmSignature element is empty"));
6676 /* Here we have delivery info as standalone CMS in encoded_structure.
6677 * We don't need any other data, free them: */
6678 xmlXPathFreeObject(result); result = NULL;
6679 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
6680 xmlFreeDoc(*response); *response = NULL;
6683 /* Decode PKCS#7 to DER format */
6684 *raw_length = b64decode(encoded_structure, raw);
6685 if (*raw_length == (size_t) -1) {
6686 isds_log_message(context,
6687 _("Error while Base64-decoding PKCS#7 structure"));
6688 err = IE_ERROR;
6689 goto leave;
6692 leave:
6693 if (err) {
6694 zfree(*raw);
6695 raw_length = 0;
6698 free(encoded_structure);
6699 xmlXPathFreeObject(result);
6700 xmlXPathFreeContext(xpath_ctx);
6701 free(xpath_expression);
6703 return err;
6707 /* Download incoming message envelope identified by ID.
6708 * @context is session context
6709 * @message_id is message identifier (you can get them from
6710 * isds_get_list_of_received_messages())
6711 * @message is automatically reallocated message retrieved from ISDS.
6712 * It will miss documents per se. Use isds_get_received_message(), if you are
6713 * interrested in documents (content) too.
6714 * Returned hash and timestamp require documents to be verifiable. */
6715 isds_error isds_get_received_envelope(struct isds_ctx *context,
6716 const char *message_id, struct isds_message **message) {
6718 isds_error err = IE_SUCCESS;
6719 xmlDocPtr response = NULL;
6720 xmlChar *code = NULL, *status_message = NULL;
6721 xmlXPathContextPtr xpath_ctx = NULL;
6722 xmlXPathObjectPtr result = NULL;
6724 if (!context) return IE_INVALID_CONTEXT;
6726 /* Free former message if any */
6727 if (!message) return IE_INVAL;
6728 isds_message_free(message);
6730 /* Do request and check for success */
6731 err = build_send_check_message_request(context, SERVICE_DM_INFO,
6732 BAD_CAST "MessageEnvelopeDownload", message_id,
6733 &response, NULL, NULL, &code, &status_message);
6734 if (err) goto leave;
6736 /* Extract data */
6737 xpath_ctx = xmlXPathNewContext(response);
6738 if (!xpath_ctx) {
6739 err = IE_ERROR;
6740 goto leave;
6742 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6743 err = IE_ERROR;
6744 goto leave;
6746 result = xmlXPathEvalExpression(
6747 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
6748 "isds:dmReturnedMessageEnvelope",
6749 xpath_ctx);
6750 if (!result) {
6751 err = IE_ERROR;
6752 goto leave;
6754 /* Empty response */
6755 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6756 char *message_id_locale = utf82locale((char*) message_id);
6757 isds_printf_message(context,
6758 _("Server did not return any envelope for ID `%s' "
6759 "on MessageEnvelopeDownload request"), message_id_locale);
6760 free(message_id_locale);
6761 err = IE_ISDS;
6762 goto leave;
6764 /* More envelops */
6765 if (result->nodesetval->nodeNr > 1) {
6766 char *message_id_locale = utf82locale((char*) message_id);
6767 isds_printf_message(context,
6768 _("Server did return more envelopes for ID `%s' "
6769 "on MessageEnvelopeDownload request"), message_id_locale);
6770 free(message_id_locale);
6771 err = IE_ISDS;
6772 goto leave;
6774 /* One message */
6775 xpath_ctx->node = result->nodesetval->nodeTab[0];
6777 /* Extract the envelope (= message without documents, hence 0) */
6778 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
6779 if (err) goto leave;
6781 /* Save XML blob */
6782 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
6783 &(*message)->raw_length);
6785 leave:
6786 if (err) {
6787 isds_message_free(message);
6790 xmlXPathFreeObject(result);
6791 xmlXPathFreeContext(xpath_ctx);
6793 free(code);
6794 free(status_message);
6795 xmlFreeDoc(response);
6797 if (!err)
6798 isds_log(ILF_ISDS, ILL_DEBUG,
6799 _("MessageEnvelopeDownload request processed by server "
6800 "successfully.\n")
6802 return err;
6806 /* Load delivery info of any format from buffer.
6807 * @context is session context
6808 * @raw_type advertises format of @buffer content. Only delivery info types
6809 * are accepted.
6810 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
6811 * retrieve such data from message->raw after calling
6812 * isds_get_signed_delivery_info().
6813 * @length is length of buffer in bytes.
6814 * @message is automatically reallocated message parsed from @buffer.
6815 * @strategy selects how buffer will be attached into raw isds_message member.
6816 * */
6817 isds_error isds_load_delivery_info(struct isds_ctx *context,
6818 const isds_raw_type raw_type,
6819 const void *buffer, const size_t length,
6820 struct isds_message **message, const isds_buffer_strategy strategy) {
6822 isds_error err = IE_SUCCESS;
6823 message_ns_type message_ns;
6824 xmlDocPtr message_doc = NULL;
6825 xmlXPathContextPtr xpath_ctx = NULL;
6826 xmlXPathObjectPtr result = NULL;
6827 void *xml_stream = NULL;
6828 size_t xml_stream_length = 0;
6830 if (!context) return IE_INVALID_CONTEXT;
6831 if (!message) return IE_INVAL;
6832 isds_message_free(message);
6833 if (!buffer) return IE_INVAL;
6836 /* Select buffer format and extract XML from CMS*/
6837 switch (raw_type) {
6838 case RAWTYPE_DELIVERYINFO:
6839 message_ns = MESSAGE_NS_UNSIGNED;
6840 xml_stream = (void *) buffer;
6841 xml_stream_length = length;
6842 break;
6844 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
6845 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
6846 xml_stream = (void *) buffer;
6847 xml_stream_length = length;
6848 break;
6850 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
6851 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
6852 err = extract_cms_data(context, buffer, length,
6853 &xml_stream, &xml_stream_length);
6854 if (err) goto leave;
6855 break;
6857 default:
6858 isds_log_message(context, _("Bad raw delivery representation type"));
6859 return IE_INVAL;
6860 break;
6863 isds_log(ILF_ISDS, ILL_DEBUG,
6864 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
6865 xml_stream_length, xml_stream);
6867 /* Convert delivery info XML stream into XPath context */
6868 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
6869 if (!message_doc) {
6870 err = IE_XML;
6871 goto leave;
6873 xpath_ctx = xmlXPathNewContext(message_doc);
6874 if (!xpath_ctx) {
6875 err = IE_ERROR;
6876 goto leave;
6878 /* XXX: Name spaces mangled for signed delivery info:
6879 * http://isds.czechpoint.cz/v20/delivery:
6881 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
6882 * <q:dmDelivery>
6883 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
6884 * <p:dmID>170272</p:dmID>
6885 * ...
6886 * </p:dmDm>
6887 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
6888 * ...
6889 * </q:dmEvents>...</q:dmEvents>
6890 * </q:dmDelivery>
6891 * </q:GetDeliveryInfoResponse>
6892 * */
6893 if (register_namespaces(xpath_ctx, message_ns)) {
6894 err = IE_ERROR;
6895 goto leave;
6897 result = xmlXPathEvalExpression(
6898 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
6899 xpath_ctx);
6900 if (!result) {
6901 err = IE_ERROR;
6902 goto leave;
6904 /* Empty delivery info */
6905 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6906 isds_printf_message(context,
6907 _("XML document ss not sisds:dmDelivery document"));
6908 err = IE_ISDS;
6909 goto leave;
6911 /* More delivery infos */
6912 if (result->nodesetval->nodeNr > 1) {
6913 isds_printf_message(context,
6914 _("XML document has more sisds:dmDelivery elements"));
6915 err = IE_ISDS;
6916 goto leave;
6918 /* One delivery info */
6919 xpath_ctx->node = result->nodesetval->nodeTab[0];
6921 /* Extract the envelope (= message without documents, hence 0).
6922 * XXX: extract_TReturnedMessage() can obtain attachments size,
6923 * but delivery info carries none. It's coded as option elements,
6924 * so it should work. */
6925 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
6926 if (err) goto leave;
6928 /* Extract events */
6929 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
6930 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
6931 if (err) { err = IE_ERROR; goto leave; }
6932 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
6933 if (err) goto leave;
6935 /* Append raw CMS structure into message */
6936 (*message)->raw_type = raw_type;
6937 switch (strategy) {
6938 case BUFFER_DONT_STORE:
6939 break;
6940 case BUFFER_COPY:
6941 (*message)->raw = malloc(length);
6942 if (!(*message)->raw) {
6943 err = IE_NOMEM;
6944 goto leave;
6946 memcpy((*message)->raw, buffer, length);
6947 (*message)->raw_length = length;
6948 break;
6949 case BUFFER_MOVE:
6950 (*message)->raw = (void *) buffer;
6951 (*message)->raw_length = length;
6952 break;
6953 default:
6954 err = IE_ENUM;
6955 goto leave;
6958 leave:
6959 if (err) {
6960 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
6961 isds_message_free(message);
6964 xmlXPathFreeObject(result);
6965 xmlXPathFreeContext(xpath_ctx);
6966 xmlFreeDoc(message_doc);
6967 if (xml_stream != buffer) cms_data_free(xml_stream);
6969 if (!err)
6970 isds_log(ILF_ISDS, ILL_DEBUG,
6971 _("Delivery info loaded successfully.\n"));
6972 return err;
6976 /* Download signed delivery infosheet of given message identified by ID.
6977 * @context is session context
6978 * @message_id is message identifier (you can get them from
6979 * isds_get_list_of_{sent,received}_messages())
6980 * @message is automatically reallocated message retrieved from ISDS.
6981 * It will miss documents per se. Use isds_get_signed_received_message(),
6982 * if you are interrested in documents (content). OTOH, only this function
6983 * can get list events message has gone through. */
6984 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
6985 const char *message_id, struct isds_message **message) {
6987 isds_error err = IE_SUCCESS;
6988 xmlDocPtr response = NULL;
6989 xmlChar *code = NULL, *status_message = NULL;
6990 void *raw = NULL;
6991 size_t raw_length = 0;
6993 if (!context) return IE_INVALID_CONTEXT;
6995 /* Free former message if any */
6996 if (!message) return IE_INVAL;
6997 isds_message_free(message);
6999 /* Do request and check for success */
7000 err = build_send_check_message_request(context, SERVICE_DM_INFO,
7001 BAD_CAST "GetSignedDeliveryInfo", message_id,
7002 &response, NULL, NULL, &code, &status_message);
7003 if (err) goto leave;
7005 /* Find signed delivery info, extract it into raw and maybe free
7006 * response */
7007 err = find_extract_signed_data_free_response(context,
7008 (xmlChar *)message_id, &response,
7009 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
7010 if (err) goto leave;
7012 /* Parse delivery info */
7013 err = isds_load_delivery_info(context,
7014 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
7015 message, BUFFER_MOVE);
7016 if (err) goto leave;
7018 raw = NULL;
7020 leave:
7021 if (err) {
7022 isds_message_free(message);
7025 free(raw);
7026 free(code);
7027 free(status_message);
7028 xmlFreeDoc(response);
7030 if (!err)
7031 isds_log(ILF_ISDS, ILL_DEBUG,
7032 _("GetSignedDeliveryInfo request processed by server "
7033 "successfully.\n")
7035 return err;
7039 /* Download delivery infosheet of given message identified by ID.
7040 * @context is session context
7041 * @message_id is message identifier (you can get them from
7042 * isds_get_list_of_{sent,received}_messages())
7043 * @message is automatically reallocated message retrieved from ISDS.
7044 * It will miss documents per se. Use isds_get_received_message(), if you are
7045 * interrested in documents (content). OTOH, only this function can get list
7046 * events message has gone through. */
7047 isds_error isds_get_delivery_info(struct isds_ctx *context,
7048 const char *message_id, struct isds_message **message) {
7050 isds_error err = IE_SUCCESS;
7051 xmlDocPtr response = NULL;
7052 xmlChar *code = NULL, *status_message = NULL;
7053 xmlXPathContextPtr xpath_ctx = NULL;
7054 xmlXPathObjectPtr result = NULL;
7055 xmlNodePtr delivery_node = NULL;
7057 if (!context) return IE_INVALID_CONTEXT;
7059 /* Free former message if any */
7060 if (!message) return IE_INVAL;
7061 isds_message_free(message);
7063 /* Do request and check for success */
7064 err = build_send_check_message_request(context, SERVICE_DM_INFO,
7065 BAD_CAST "GetDeliveryInfo", message_id,
7066 &response, NULL, NULL, &code, &status_message);
7067 if (err) goto leave;
7069 /* Extract data */
7070 xpath_ctx = xmlXPathNewContext(response);
7071 if (!xpath_ctx) {
7072 err = IE_ERROR;
7073 goto leave;
7075 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7076 err = IE_ERROR;
7077 goto leave;
7079 result = xmlXPathEvalExpression(
7080 BAD_CAST "/isds:GetDeliveryInfoResponse/isds:dmDelivery",
7081 xpath_ctx);
7082 if (!result) {
7083 err = IE_ERROR;
7084 goto leave;
7086 /* Empty response */
7087 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7088 char *message_id_locale = utf82locale((char*) message_id);
7089 isds_printf_message(context,
7090 _("Server did not return any delivery info for ID `%s' "
7091 "on GetDeliveryInfo request"), message_id_locale);
7092 free(message_id_locale);
7093 err = IE_ISDS;
7094 goto leave;
7096 /* More delivery infos */
7097 if (result->nodesetval->nodeNr > 1) {
7098 char *message_id_locale = utf82locale((char*) message_id);
7099 isds_printf_message(context,
7100 _("Server did return more delivery infos for ID `%s' "
7101 "on GetDeliveryInfo request"), message_id_locale);
7102 free(message_id_locale);
7103 err = IE_ISDS;
7104 goto leave;
7106 /* One delivery info */
7107 xpath_ctx->node = delivery_node = result->nodesetval->nodeTab[0];
7109 /* Extract the envelope (= message without documents, hence 0).
7110 * XXX: extract_TReturnedMessage() can obtain attachments size,
7111 * but delivery info carries none. It's coded as option elements,
7112 * so it should work. */
7113 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
7114 if (err) goto leave;
7116 /* Extract events */
7117 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmEvents", xpath_ctx);
7118 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
7119 if (err) { err = IE_ERROR; goto leave; }
7120 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
7121 if (err) goto leave;
7123 /* Save XML blob */
7124 err = serialize_subtree(context, delivery_node, &(*message)->raw,
7125 &(*message)->raw_length);
7127 leave:
7128 if (err) {
7129 isds_message_free(message);
7132 xmlXPathFreeObject(result);
7133 xmlXPathFreeContext(xpath_ctx);
7135 free(code);
7136 free(status_message);
7137 xmlFreeDoc(response);
7139 if (!err)
7140 isds_log(ILF_ISDS, ILL_DEBUG,
7141 _("GetDeliveryInfo request processed by server "
7142 "successfully.\n")
7144 return err;
7148 /* Load incoming message from buffer.
7149 * @context is session context
7150 * @buffer XML stream with unsigned message. You can retrieve such data from
7151 * message->raw after calling isds_get_received_message().
7152 * @length is length of buffer in bytes.
7153 * @message is automatically reallocated message parsed from @buffer.
7154 * @strategy selects how buffer will be attached into raw isds_message member.
7155 * */
7156 isds_error isds_load_received_message(struct isds_ctx *context,
7157 const void *buffer, const size_t length,
7158 struct isds_message **message, const isds_buffer_strategy strategy) {
7160 isds_error err = IE_SUCCESS;
7161 xmlDocPtr message_doc = NULL;
7162 xmlXPathContextPtr xpath_ctx = NULL;
7163 xmlXPathObjectPtr result = NULL;
7165 if (!context) return IE_INVALID_CONTEXT;
7166 if (!message) return IE_INVAL;
7167 isds_message_free(message);
7168 if (!buffer) return IE_INVAL;
7171 isds_log(ILF_ISDS, ILL_DEBUG,
7172 _("Incoming message content:\n%.*s\nEnd of message\n"),
7173 length, buffer);
7175 /* Convert extracted messages XML stream into XPath context */
7176 message_doc = xmlParseMemory(buffer, length);
7177 if (!message_doc) {
7178 err = IE_XML;
7179 goto leave;
7181 xpath_ctx = xmlXPathNewContext(message_doc);
7182 if (!xpath_ctx) {
7183 err = IE_ERROR;
7184 goto leave;
7186 /* XXX: Standard name space */
7187 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7188 err = IE_ERROR;
7189 goto leave;
7191 result = xmlXPathEvalExpression(
7192 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
7193 xpath_ctx);
7194 if (!result) {
7195 err = IE_ERROR;
7196 goto leave;
7198 /* Missing dmReturnedMessage */
7199 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7200 isds_printf_message(context,
7201 _("XML document does not contain isds:dmReturnedMessage "
7202 "element"));
7203 err = IE_ISDS;
7204 goto leave;
7206 /* More elements. This should never happen. */
7207 if (result->nodesetval->nodeNr > 1) {
7208 isds_printf_message(context,
7209 _("XML document has more isds:dmReturnedMessage elements"));
7210 err = IE_ISDS;
7211 goto leave;
7213 /* One message */
7214 xpath_ctx->node = result->nodesetval->nodeTab[0];
7216 /* Extract the message */
7217 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
7218 if (err) goto leave;
7220 /* Append XML stream into message */
7221 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
7222 switch (strategy) {
7223 case BUFFER_DONT_STORE:
7224 break;
7225 case BUFFER_COPY:
7226 (*message)->raw = malloc(length);
7227 if (!(*message)->raw) {
7228 err = IE_NOMEM;
7229 goto leave;
7231 memcpy((*message)->raw, buffer, length);
7232 (*message)->raw_length = length;
7233 break;
7234 case BUFFER_MOVE:
7235 (*message)->raw = (void *) buffer;
7236 (*message)->raw_length = length;
7237 break;
7238 default:
7239 err = IE_ENUM;
7240 goto leave;
7243 leave:
7244 if (err) {
7245 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
7246 isds_message_free(message);
7249 xmlFreeDoc(message_doc);
7250 xmlXPathFreeObject(result);
7251 xmlXPathFreeContext(xpath_ctx);
7253 if (!err)
7254 isds_log(ILF_ISDS, ILL_DEBUG,
7255 _("Incoming message loaded successfully.\n"));
7256 return err;
7260 /* Download incoming message identified by ID.
7261 * @context is session context
7262 * @message_id is message identifier (you can get them from
7263 * isds_get_list_of_received_messages())
7264 * @message is automatically reallocated message retrieved from ISDS */
7265 isds_error isds_get_received_message(struct isds_ctx *context,
7266 const char *message_id, struct isds_message **message) {
7268 isds_error err = IE_SUCCESS;
7269 xmlDocPtr response = NULL;
7270 void *xml_stream = NULL;
7271 size_t xml_stream_length;
7272 xmlChar *code = NULL, *status_message = NULL;
7273 xmlXPathContextPtr xpath_ctx = NULL;
7274 xmlXPathObjectPtr result = NULL;
7275 char *phys_path = NULL;
7276 size_t phys_start, phys_end;
7278 if (!context) return IE_INVALID_CONTEXT;
7280 /* Free former message if any */
7281 if (message) isds_message_free(message);
7283 /* Do request and check for success */
7284 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
7285 BAD_CAST "MessageDownload", message_id,
7286 &response, &xml_stream, &xml_stream_length,
7287 &code, &status_message);
7288 if (err) goto leave;
7290 /* Extract data */
7291 xpath_ctx = xmlXPathNewContext(response);
7292 if (!xpath_ctx) {
7293 err = IE_ERROR;
7294 goto leave;
7296 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7297 err = IE_ERROR;
7298 goto leave;
7300 result = xmlXPathEvalExpression(
7301 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
7302 xpath_ctx);
7303 if (!result) {
7304 err = IE_ERROR;
7305 goto leave;
7307 /* Empty response */
7308 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7309 char *message_id_locale = utf82locale((char*) message_id);
7310 isds_printf_message(context,
7311 _("Server did not return any message for ID `%s' "
7312 "on MessageDownload request"), message_id_locale);
7313 free(message_id_locale);
7314 err = IE_ISDS;
7315 goto leave;
7317 /* More messages */
7318 if (result->nodesetval->nodeNr > 1) {
7319 char *message_id_locale = utf82locale((char*) message_id);
7320 isds_printf_message(context,
7321 _("Server did return more messages for ID `%s' "
7322 "on MessageDownload request"), message_id_locale);
7323 free(message_id_locale);
7324 err = IE_ISDS;
7325 goto leave;
7327 /* One message */
7328 xpath_ctx->node = result->nodesetval->nodeTab[0];
7330 /* Extract the message */
7331 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
7332 if (err) goto leave;
7334 /* Locate raw XML blob */
7335 phys_path = strdup(
7336 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
7337 PHYSXML_ELEMENT_SEPARATOR
7338 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
7339 PHYSXML_ELEMENT_SEPARATOR
7340 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
7342 if (!phys_path) {
7343 err = IE_NOMEM;
7344 goto leave;
7346 err = find_element_boundary(xml_stream, xml_stream_length,
7347 phys_path, &phys_start, &phys_end);
7348 zfree(phys_path);
7349 if (err) {
7350 isds_log_message(context,
7351 _("Substring with isds:MessageDownloadResponse element "
7352 "could not be located in raw SOAP message"));
7353 goto leave;
7355 /* Save XML blob */
7356 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
7357 &(*message)->raw_length);*/
7358 /* TODO: Store name space declarations from ancestors */
7359 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
7360 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
7361 (*message)->raw_length = phys_end - phys_start + 1;
7362 (*message)->raw = malloc((*message)->raw_length);
7363 if (!(*message)->raw) {
7364 err = IE_NOMEM;
7365 goto leave;
7367 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
7370 leave:
7371 if (err) {
7372 isds_message_free(message);
7375 free(phys_path);
7377 xmlXPathFreeObject(result);
7378 xmlXPathFreeContext(xpath_ctx);
7380 free(code);
7381 free(status_message);
7382 free(xml_stream);
7383 xmlFreeDoc(response);
7385 if (!err)
7386 isds_log(ILF_ISDS, ILL_DEBUG,
7387 _("MessageDownload request processed by server "
7388 "successfully.\n")
7390 return err;
7394 /* Load signed message from buffer.
7395 * @context is session context
7396 * @outgoing is true if message is outgoing, false if message is incoming
7397 * @buffer is DER encoded PKCS#7 structure with signed message. You can
7398 * retrieve such data from message->raw after calling
7399 * isds_get_signed_{received,sent}_message().
7400 * @length is length of buffer in bytes.
7401 * @message is automatically reallocated message parsed from @buffer.
7402 * @strategy selects how buffer will be attached into raw isds_message member.
7403 * */
7404 isds_error isds_load_signed_message(struct isds_ctx *context,
7405 const _Bool outgoing, const void *buffer, const size_t length,
7406 struct isds_message **message, const isds_buffer_strategy strategy) {
7408 isds_error err = IE_SUCCESS;
7409 xmlDocPtr message_doc = NULL;
7410 xmlXPathContextPtr xpath_ctx = NULL;
7411 xmlXPathObjectPtr result = NULL;
7412 void *xml_stream = NULL;
7413 size_t xml_stream_length = 0;
7415 if (!context) return IE_INVALID_CONTEXT;
7416 if (!message) return IE_INVAL;
7417 isds_message_free(message);
7418 if (!buffer) return IE_INVAL;
7421 /* Extract message from PKCS#7 structure */
7422 err = extract_cms_data(context, buffer, length,
7423 &xml_stream, &xml_stream_length);
7424 if (err) goto leave;
7426 isds_log(ILF_ISDS, ILL_DEBUG, (outgoing) ?
7427 _("Signed outgoing message content:\n%.*s\nEnd of message\n") :
7428 _("Signed incoming message content:\n%.*s\nEnd of message\n"),
7429 xml_stream_length, xml_stream);
7431 /* Convert extracted messages XML stream into XPath context */
7432 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
7433 if (!message_doc) {
7434 err = IE_XML;
7435 goto leave;
7437 xpath_ctx = xmlXPathNewContext(message_doc);
7438 if (!xpath_ctx) {
7439 err = IE_ERROR;
7440 goto leave;
7442 /* XXX: Name spaces mangled for outgoing direction:
7443 * http://isds.czechpoint.cz/v20/SentMessage:
7445 * <q:MessageDownloadResponse
7446 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
7447 * <q:dmReturnedMessage>
7448 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
7449 * <p:dmID>151916</p:dmID>
7450 * ...
7451 * </p:dmDm>
7452 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
7453 * ...
7454 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
7455 * </q:dmReturnedMessage>
7456 * </q:MessageDownloadResponse>
7458 * XXX: Name spaces mangled for incoming direction:
7459 * http://isds.czechpoint.cz/v20/message:
7461 * <q:MessageDownloadResponse
7462 * xmlns:q="http://isds.czechpoint.cz/v20/message">
7463 * <q:dmReturnedMessage>
7464 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
7465 * <p:dmID>151916</p:dmID>
7466 * ...
7467 * </p:dmDm>
7468 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
7469 * ...
7470 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
7471 * </q:dmReturnedMessage>
7472 * </q:MessageDownloadResponse>
7474 * Stupidity of ISDS developers is unlimited */
7475 if (register_namespaces(xpath_ctx, (outgoing) ?
7476 MESSAGE_NS_SIGNED_OUTGOING : MESSAGE_NS_SIGNED_INCOMING)) {
7477 err = IE_ERROR;
7478 goto leave;
7480 /* XXX: Embeded message XML document is always rooted as
7481 * /sisds:MessageDownloadResponse (even outgoing message). */
7482 result = xmlXPathEvalExpression(
7483 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
7484 xpath_ctx);
7485 if (!result) {
7486 err = IE_ERROR;
7487 goto leave;
7489 /* Empty embedded message */
7490 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7491 isds_printf_message(context,
7492 _("XML document embedded into PKCS#7 structure is not "
7493 "sisds:dmReturnedMessage document"));
7494 err = IE_ISDS;
7495 goto leave;
7497 /* More embedded messages */
7498 if (result->nodesetval->nodeNr > 1) {
7499 isds_printf_message(context,
7500 _("Embeded XML document into PKCS#7 structure has more "
7501 "root sisds:dmReturnedMessage elements"));
7502 err = IE_ISDS;
7503 goto leave;
7505 /* One embedded message */
7506 xpath_ctx->node = result->nodesetval->nodeTab[0];
7508 /* Extract the message */
7509 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
7510 if (err) goto leave;
7512 /* Append raw CMS structure into message */
7513 (*message)->raw_type = (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
7514 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
7515 switch (strategy) {
7516 case BUFFER_DONT_STORE:
7517 break;
7518 case BUFFER_COPY:
7519 (*message)->raw = malloc(length);
7520 if (!(*message)->raw) {
7521 err = IE_NOMEM;
7522 goto leave;
7524 memcpy((*message)->raw, buffer, length);
7525 (*message)->raw_length = length;
7526 break;
7527 case BUFFER_MOVE:
7528 (*message)->raw = (void *) buffer;
7529 (*message)->raw_length = length;
7530 break;
7531 default:
7532 err = IE_ENUM;
7533 goto leave;
7537 leave:
7538 if (err) {
7539 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
7540 isds_message_free(message);
7543 xmlFreeDoc(message_doc);
7544 cms_data_free(xml_stream);
7545 xmlXPathFreeObject(result);
7546 xmlXPathFreeContext(xpath_ctx);
7548 if (!err)
7549 isds_log(ILF_ISDS, ILL_DEBUG,
7550 _("Signed message loaded successfully.\n"));
7551 return err;
7555 /* Load message of any type from buffer.
7556 * @context is session context
7557 * @raw_type defines content type of @buffer. Only message types are allowed.
7558 * @buffer is message raw representation. Format (CMS, plain signed,
7559 * message direction) is defined in @raw_type. You can retrieve such data
7560 * from message->raw after calling isds_get_[signed]{received,sent}_message().
7561 * @length is length of buffer in bytes.
7562 * @message is automatically reallocated message parsed from @buffer.
7563 * @strategy selects how buffer will be attached into raw isds_message member.
7564 * */
7565 isds_error isds_load_message(struct isds_ctx *context,
7566 const isds_raw_type raw_type, const void *buffer, const size_t length,
7567 struct isds_message **message, const isds_buffer_strategy strategy) {
7569 isds_error err = IE_SUCCESS;
7570 void *xml_stream = NULL;
7571 size_t xml_stream_length = 0;
7572 message_ns_type message_ns;
7573 xmlDocPtr message_doc = NULL;
7574 xmlXPathContextPtr xpath_ctx = NULL;
7575 xmlXPathObjectPtr result = NULL;
7577 if (!context) return IE_INVALID_CONTEXT;
7578 if (!message) return IE_INVAL;
7579 isds_message_free(message);
7580 if (!buffer) return IE_INVAL;
7583 /* Select buffer format and extract XML from CMS*/
7584 switch (raw_type) {
7585 case RAWTYPE_INCOMING_MESSAGE:
7586 message_ns = MESSAGE_NS_UNSIGNED;
7587 xml_stream = (void *) buffer;
7588 xml_stream_length = length;
7589 break;
7591 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
7592 message_ns = MESSAGE_NS_SIGNED_INCOMING;
7593 xml_stream = (void *) buffer;
7594 xml_stream_length = length;
7595 break;
7597 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
7598 message_ns = MESSAGE_NS_SIGNED_INCOMING;
7599 err = extract_cms_data(context, buffer, length,
7600 &xml_stream, &xml_stream_length);
7601 if (err) goto leave;
7602 break;
7604 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
7605 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
7606 xml_stream = (void *) buffer;
7607 xml_stream_length = length;
7608 break;
7610 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
7611 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
7612 err = extract_cms_data(context, buffer, length,
7613 &xml_stream, &xml_stream_length);
7614 if (err) goto leave;
7615 break;
7617 default:
7618 isds_log_message(context, _("Bad raw message representation type"));
7619 return IE_INVAL;
7620 break;
7623 isds_log(ILF_ISDS, ILL_DEBUG,
7624 _("Loading message:\n%.*s\nEnd of message\n"),
7625 xml_stream_length, xml_stream);
7627 /* Convert messages XML stream into XPath context */
7628 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
7629 if (!message_doc) {
7630 err = IE_XML;
7631 goto leave;
7633 xpath_ctx = xmlXPathNewContext(message_doc);
7634 if (!xpath_ctx) {
7635 err = IE_ERROR;
7636 goto leave;
7638 /* XXX: Standard name space for unsigned icoming direction:
7639 * http://isds.czechpoint.cz/v20/SentMessage
7641 * XXX: Name spaces mangled for signed outgoing direction:
7642 * http://isds.czechpoint.cz/v20/SentMessage:
7644 * <q:MessageDownloadResponse
7645 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
7646 * <q:dmReturnedMessage>
7647 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
7648 * <p:dmID>151916</p:dmID>
7649 * ...
7650 * </p:dmDm>
7651 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
7652 * ...
7653 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
7654 * </q:dmReturnedMessage>
7655 * </q:MessageDownloadResponse>
7657 * XXX: Name spaces mangled for signed incoming direction:
7658 * http://isds.czechpoint.cz/v20/message:
7660 * <q:MessageDownloadResponse
7661 * xmlns:q="http://isds.czechpoint.cz/v20/message">
7662 * <q:dmReturnedMessage>
7663 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
7664 * <p:dmID>151916</p:dmID>
7665 * ...
7666 * </p:dmDm>
7667 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
7668 * ...
7669 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
7670 * </q:dmReturnedMessage>
7671 * </q:MessageDownloadResponse>
7673 * Stupidity of ISDS developers is unlimited */
7674 if (register_namespaces(xpath_ctx, message_ns)) {
7675 err = IE_ERROR;
7676 goto leave;
7678 result = xmlXPathEvalExpression(
7679 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
7680 xpath_ctx);
7681 if (!result) {
7682 err = IE_ERROR;
7683 goto leave;
7685 /* Empty message */
7686 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7687 isds_printf_message(context,
7688 _("XML document does not contain "
7689 "sisds:dmReturnedMessage element"));
7690 err = IE_ISDS;
7691 goto leave;
7693 /* More messages */
7694 if (result->nodesetval->nodeNr > 1) {
7695 isds_printf_message(context,
7696 _("XML document has more sisds:dmReturnedMessage elements"));
7697 err = IE_ISDS;
7698 goto leave;
7700 /* One message */
7701 xpath_ctx->node = result->nodesetval->nodeTab[0];
7703 /* Extract the message */
7704 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
7705 if (err) goto leave;
7707 /* Append raw buffer into message */
7708 (*message)->raw_type = raw_type;
7709 switch (strategy) {
7710 case BUFFER_DONT_STORE:
7711 break;
7712 case BUFFER_COPY:
7713 (*message)->raw = malloc(length);
7714 if (!(*message)->raw) {
7715 err = IE_NOMEM;
7716 goto leave;
7718 memcpy((*message)->raw, buffer, length);
7719 (*message)->raw_length = length;
7720 break;
7721 case BUFFER_MOVE:
7722 (*message)->raw = (void *) buffer;
7723 (*message)->raw_length = length;
7724 break;
7725 default:
7726 err = IE_ENUM;
7727 goto leave;
7731 leave:
7732 if (err) {
7733 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
7734 isds_message_free(message);
7737 if (xml_stream != buffer) cms_data_free(xml_stream);
7738 xmlXPathFreeObject(result);
7739 xmlXPathFreeContext(xpath_ctx);
7740 xmlFreeDoc(message_doc);
7742 if (!err)
7743 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
7744 return err;
7748 /* Download signed incoming/outgoing message identified by ID.
7749 * @context is session context
7750 * @output is true for outging message, false for incoming message
7751 * @message_id is message identifier (you can get them from
7752 * isds_get_list_of_{sent,received}_messages())
7753 * @message is automatically reallocated message retrieved from ISDS. The raw
7754 * memeber will be filled with PKCS#7 structure in DER format. */
7755 _hidden isds_error isds_get_signed_message(struct isds_ctx *context,
7756 const _Bool outgoing, const char *message_id,
7757 struct isds_message **message) {
7759 isds_error err = IE_SUCCESS;
7760 xmlDocPtr response = NULL;
7761 xmlChar *code = NULL, *status_message = NULL;
7762 xmlXPathContextPtr xpath_ctx = NULL;
7763 xmlXPathObjectPtr result = NULL;
7764 char *encoded_structure = NULL;
7765 void *raw = NULL;
7766 size_t raw_length = 0;
7768 if (!context) return IE_INVALID_CONTEXT;
7769 if (!message) return IE_INVAL;
7770 isds_message_free(message);
7772 /* Do request and check for success */
7773 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
7774 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
7775 BAD_CAST "SignedMessageDownload",
7776 message_id, &response, NULL, NULL, &code, &status_message);
7777 if (err) goto leave;
7779 /* Find signed message, extract it into raw and maybe free
7780 * response */
7781 err = find_extract_signed_data_free_response(context,
7782 (xmlChar *)message_id, &response,
7783 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
7784 BAD_CAST "SignedMessageDownload",
7785 &raw, &raw_length);
7786 if (err) goto leave;
7788 /* Parse message */
7789 err = isds_load_message(context,
7790 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
7791 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
7792 raw, raw_length, message, BUFFER_MOVE);
7793 if (err) goto leave;
7795 raw = NULL;
7797 leave:
7798 if (err) {
7799 isds_message_free(message);
7802 free(encoded_structure);
7803 xmlXPathFreeObject(result);
7804 xmlXPathFreeContext(xpath_ctx);
7805 free(raw);
7807 free(code);
7808 free(status_message);
7809 xmlFreeDoc(response);
7811 if (!err)
7812 isds_log(ILF_ISDS, ILL_DEBUG,
7813 (outgoing) ?
7814 _("SignedSentMessageDownload request processed by server "
7815 "successfully.\n") :
7816 _("SignedMessageDownload request processed by server "
7817 "successfully.\n")
7819 return err;
7823 /* Download signed incoming message identified by ID.
7824 * @context is session context
7825 * @message_id is message identifier (you can get them from
7826 * isds_get_list_of_received_messages())
7827 * @message is automatically reallocated message retrieved from ISDS. The raw
7828 * memeber will be filled with PKCS#7 structure in DER format. */
7829 isds_error isds_get_signed_received_message(struct isds_ctx *context,
7830 const char *message_id, struct isds_message **message) {
7831 return isds_get_signed_message(context, 0, message_id, message);
7835 /* Download signed outgoing message identified by ID.
7836 * @context is session context
7837 * @message_id is message identifier (you can get them from
7838 * isds_get_list_of_sent_messages())
7839 * @message is automatically reallocated message retrieved from ISDS. The raw
7840 * memeber will be filled with PKCS#7 structure in DER format. */
7841 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
7842 const char *message_id, struct isds_message **message) {
7843 return isds_get_signed_message(context, 1, message_id, message);
7847 /* Retrieve hash of message identified by ID stored in ISDS.
7848 * @context is session context
7849 * @message_id is message identifier
7850 * @hash is automatically reallocated message hash downloaded from ISDS.
7851 * Message must exist in system and must not be deleted. */
7852 isds_error isds_download_message_hash(struct isds_ctx *context,
7853 const char *message_id, struct isds_hash **hash) {
7855 isds_error err = IE_SUCCESS;
7856 xmlDocPtr response = NULL;
7857 xmlChar *code = NULL, *status_message = NULL;
7858 xmlXPathContextPtr xpath_ctx = NULL;
7859 xmlXPathObjectPtr result = NULL;
7861 if (!context) return IE_INVALID_CONTEXT;
7863 isds_hash_free(hash);
7865 err = build_send_check_message_request(context, SERVICE_DM_INFO,
7866 BAD_CAST "VerifyMessage", message_id,
7867 &response, NULL, NULL, &code, &status_message);
7868 if (err) goto leave;
7871 /* Extract data */
7872 xpath_ctx = xmlXPathNewContext(response);
7873 if (!xpath_ctx) {
7874 err = IE_ERROR;
7875 goto leave;
7877 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7878 err = IE_ERROR;
7879 goto leave;
7881 result = xmlXPathEvalExpression(
7882 BAD_CAST "/isds:VerifyMessageResponse",
7883 xpath_ctx);
7884 if (!result) {
7885 err = IE_ERROR;
7886 goto leave;
7888 /* Empty response */
7889 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7890 char *message_id_locale = utf82locale((char*) message_id);
7891 isds_printf_message(context,
7892 _("Server did not return any response for ID `%s' "
7893 "on VerifyMessage request"), message_id_locale);
7894 free(message_id_locale);
7895 err = IE_ISDS;
7896 goto leave;
7898 /* More responses */
7899 if (result->nodesetval->nodeNr > 1) {
7900 char *message_id_locale = utf82locale((char*) message_id);
7901 isds_printf_message(context,
7902 _("Server did return more responses for ID `%s' "
7903 "on VerifyMessage request"), message_id_locale);
7904 free(message_id_locale);
7905 err = IE_ISDS;
7906 goto leave;
7908 /* One response */
7909 xpath_ctx->node = result->nodesetval->nodeTab[0];
7911 /* Extract the hash */
7912 err = find_and_extract_DmHash(context, hash, xpath_ctx);
7914 leave:
7915 if (err) {
7916 isds_hash_free(hash);
7919 xmlXPathFreeObject(result);
7920 xmlXPathFreeContext(xpath_ctx);
7922 free(code);
7923 free(status_message);
7924 xmlFreeDoc(response);
7926 if (!err)
7927 isds_log(ILF_ISDS, ILL_DEBUG,
7928 _("VerifyMessage request processed by server "
7929 "successfully.\n")
7931 return err;
7935 /* Mark message as read. This is a transactional commit function to acknoledge
7936 * to ISDS the message has been downloaded and processed by client properly.
7937 * @context is session context
7938 * @message_id is message identifier. */
7939 isds_error isds_mark_message_read(struct isds_ctx *context,
7940 const char *message_id) {
7942 isds_error err = IE_SUCCESS;
7943 xmlDocPtr response = NULL;
7944 xmlChar *code = NULL, *status_message = NULL;
7946 if (!context) return IE_INVALID_CONTEXT;
7948 /* Do request and check for success */
7949 err = build_send_check_message_request(context, SERVICE_DM_INFO,
7950 BAD_CAST "MarkMessageAsDownloaded", message_id,
7951 &response, NULL, NULL, &code, &status_message);
7953 free(code);
7954 free(status_message);
7955 xmlFreeDoc(response);
7957 if (!err)
7958 isds_log(ILF_ISDS, ILL_DEBUG,
7959 _("MarkMessageAsDownloaded request processed by server "
7960 "successfully.\n")
7962 return err;
7965 /* Mark message as received by recipient. This is applicable only to
7966 * commercial message. There is no specified way how to distinguishe
7967 * commercial message from government message yet. Government message is
7968 * received automatically (by law), commenrcial message on recipient request.
7969 * @context is session context
7970 * @message_id is message identifier. */
7971 isds_error isds_mark_message_received(struct isds_ctx *context,
7972 const char *message_id) {
7974 isds_error err = IE_SUCCESS;
7975 xmlDocPtr response = NULL;
7976 xmlChar *code = NULL, *status_message = NULL;
7978 if (!context) return IE_INVALID_CONTEXT;
7980 /* Do request and check for success */
7981 err = build_send_check_message_request(context, SERVICE_DM_INFO,
7982 BAD_CAST "ConfirmDelivery", message_id,
7983 &response, NULL, NULL, &code, &status_message);
7985 free(code);
7986 free(status_message);
7987 xmlFreeDoc(response);
7989 if (!err)
7990 isds_log(ILF_ISDS, ILL_DEBUG,
7991 _("ConfirmDelivery request processed by server "
7992 "successfully.\n")
7994 return err;
7998 #undef INSERT_ELEMENT
7999 #undef CHECK_FOR_STRING_LENGTH
8000 #undef INSERT_STRING_ATTRIBUTE
8001 #undef INSERT_ULONGINTNOPTR
8002 #undef INSERT_ULONGINT
8003 #undef INSERT_LONGINT
8004 #undef INSERT_BOOLEAN
8005 #undef INSERT_SCALAR_BOOLEAN
8006 #undef INSERT_STRING
8007 #undef EXTRACT_STRING_ATTRIBUTE
8008 #undef EXTRACT_ULONGINT
8009 #undef EXTRACT_LONGINT
8010 #undef EXTRACT_BOOLEAN
8011 #undef EXTRACT_STRING
8014 /* Compute hash of message from raw representation and store it into envelope.
8015 * Original hash structure will be destroyed in envelope.
8016 * @context is session context
8017 * @message is message carrying raw XML message blob
8018 * @algorithm is desired hash algorithm to use */
8019 isds_error isds_compute_message_hash(struct isds_ctx *context,
8020 struct isds_message *message, const isds_hash_algorithm algorithm) {
8021 isds_error err = IE_SUCCESS;
8022 const char *nsuri;
8023 void *xml_stream = NULL;
8024 size_t xml_stream_length;
8025 size_t phys_start, phys_end;
8026 char *phys_path = NULL;
8027 struct isds_hash *new_hash = NULL;
8030 if (!context) return IE_INVALID_CONTEXT;
8031 if (!message) return IE_INVAL;
8033 if (!message->raw) {
8034 isds_log_message(context,
8035 _("Message does not carry raw representation"));
8036 return IE_INVAL;
8039 switch (message->raw_type) {
8040 case RAWTYPE_INCOMING_MESSAGE:
8041 nsuri = ISDS_NS;
8042 xml_stream = message->raw;
8043 xml_stream_length = message->raw_length;
8044 break;
8046 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
8047 nsuri = SISDS_INCOMING_NS;
8048 xml_stream = message->raw;
8049 xml_stream_length = message->raw_length;
8050 break;
8052 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
8053 nsuri = SISDS_INCOMING_NS;
8054 err = extract_cms_data(context, message->raw, message->raw_length,
8055 &xml_stream, &xml_stream_length);
8056 if (err) goto leave;
8057 break;
8059 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
8060 nsuri = SISDS_OUTGOING_NS;
8061 xml_stream = message->raw;
8062 xml_stream_length = message->raw_length;
8063 break;
8065 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
8066 nsuri = SISDS_OUTGOING_NS;
8067 err = extract_cms_data(context, message->raw, message->raw_length,
8068 &xml_stream, &xml_stream_length);
8069 if (err) goto leave;
8070 break;
8072 default:
8073 isds_log_message(context, _("Bad raw representation type"));
8074 return IE_INVAL;
8075 break;
8079 /* XXX: Hash is computed from original string represinting isds:dmDm
8080 * subtree. That means no encoding, white space, xmlns attributes changes.
8081 * In other words, input for hash can be invalid XML stream. */
8082 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
8083 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
8084 PHYSXML_ELEMENT_SEPARATOR,
8085 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
8086 PHYSXML_ELEMENT_SEPARATOR
8087 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
8088 err = IE_NOMEM;
8089 goto leave;
8091 err = find_element_boundary(xml_stream, xml_stream_length,
8092 phys_path, &phys_start, &phys_end);
8093 zfree(phys_path);
8094 if (err) {
8095 isds_log_message(context,
8096 _("Substring with isds:dmDM element could not be located "
8097 "in raw message"));
8098 goto leave;
8102 /* Compute hash */
8103 new_hash = calloc(1, sizeof(*new_hash));
8104 if (!new_hash) {
8105 err = IE_NOMEM;
8106 goto leave;
8108 new_hash->algorithm = algorithm;
8109 err = compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
8110 new_hash);
8111 if (err) {
8112 isds_log_message(context, _("Could not compute message hash"));
8113 goto leave;
8116 /* Save computed hash */
8117 if (!message->envelope) {
8118 message->envelope = calloc(1, sizeof(*message->envelope));
8119 if (!message->envelope) {
8120 err = IE_NOMEM;
8121 goto leave;
8124 isds_hash_free(&message->envelope->hash);
8125 message->envelope->hash = new_hash;
8127 leave:
8128 if (err) {
8129 isds_hash_free(&new_hash);
8132 free(phys_path);
8133 if (xml_stream != message->raw) free(xml_stream);
8134 return err;
8138 /* Compare two hashes.
8139 * @h1 is first hash
8140 * @h2 is another hash
8141 * @return
8142 * IE_SUCCESS if hashes equal
8143 * IE_NOTUNIQ if hashes are comparable, but they don't equal
8144 * IE_ENUM if not comparable, but both structures defined
8145 * IE_INVAL if some of the structures are undefined (NULL)
8146 * IE_ERROR if internal error occurs */
8147 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
8148 if (h1 == NULL || h2 == NULL) return IE_INVAL;
8149 if (h1->algorithm != h2->algorithm) return IE_ENUM;
8150 if (h1->length != h2->length) return IE_ERROR;
8151 if (h1->length > 0 && !h1->value) return IE_ERROR;
8152 if (h2->length > 0 && !h2->value) return IE_ERROR;
8154 for (int i = 0; i < h1->length; i++) {
8155 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
8156 return IE_NOTEQUAL;
8158 return IE_SUCCESS;
8162 /* Check message has gone through ISDS by comparing message hash stored in
8163 * ISDS and locally computed hash. You must provide message with valid raw
8164 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
8165 * This is convenient wrapper for isds_download_message_hash(),
8166 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
8167 * @context is session context
8168 * @message is message with valid raw and envelope member; envelope->hash
8169 * member will be changed during funcion run. Use envelope on heap only.
8170 * @return
8171 * IE_SUCCESS if message originates in ISDS
8172 * IE_NOTEQUAL if message is unknown to ISDS
8173 * other code for other errors */
8174 isds_error isds_verify_message_hash(struct isds_ctx *context,
8175 struct isds_message *message) {
8176 isds_error err = IE_SUCCESS;
8177 struct isds_hash *downloaded_hash = NULL;
8179 if (!context) return IE_INVALID_CONTEXT;
8180 if (!message) return IE_INVAL;
8182 if (!message->envelope) {
8183 isds_log_message(context,
8184 _("Given message structure is missing envelope"));
8185 return IE_INVAL;
8187 if (!message->raw) {
8188 isds_log_message(context,
8189 _("Given message structure is missing raw representation"));
8190 return IE_INVAL;
8193 err = isds_download_message_hash(context, message->envelope->dmID,
8194 &downloaded_hash);
8195 if (err) goto leave;
8197 err = isds_compute_message_hash(context, message,
8198 downloaded_hash->algorithm);
8199 if (err) goto leave;
8201 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
8203 leave:
8204 isds_hash_free(&downloaded_hash);
8205 return err;
8209 /* Search for document by document ID in list of documents. IDs are compared
8210 * as UTF-8 string.
8211 * @documents is list of isds_documents
8212 * @id is document identifier
8213 * @return first matching document or NULL. */
8214 const struct isds_document *isds_find_document_by_id(
8215 const struct isds_list *documents, const char *id) {
8216 const struct isds_list *item;
8217 const struct isds_document *document;
8219 for (item = documents; item; item = item->next) {
8220 document = (struct isds_document *) item->data;
8221 if (!document) continue;
8223 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
8224 return document;
8227 return NULL;
8231 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
8232 struct isds_message **message);
8233 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
8234 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
8235 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
8236 struct isds_address **address);
8238 int isds_message_free(struct isds_message **message);
8239 int isds_address_free(struct isds_address **address);
8243 /* Makes known all relevant namespaces to given XPath context
8244 * @xpat_ctx is XPath context
8245 * @message_ns selects propper message name space. Unsisnged and signed
8246 * messages differs.
8247 * prefix and to URI ISDS_NS */
8248 _hidden isds_error register_namespaces(xmlXPathContextPtr xpath_ctx,
8249 const message_ns_type message_ns) {
8250 const xmlChar *message_namespace = NULL;
8252 if (!xpath_ctx) return IE_ERROR;
8254 switch(message_ns) {
8255 case MESSAGE_NS_UNSIGNED:
8256 message_namespace = BAD_CAST ISDS_NS; break;
8257 case MESSAGE_NS_SIGNED_INCOMING:
8258 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
8259 case MESSAGE_NS_SIGNED_OUTGOING:
8260 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
8261 case MESSAGE_NS_SIGNED_DELIVERY:
8262 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
8263 default:
8264 return IE_ENUM;
8267 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
8268 return IE_ERROR;
8269 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
8270 return IE_ERROR;
8271 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
8272 return IE_ERROR;
8273 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
8274 return IE_ERROR;
8275 return IE_SUCCESS;