Implement isds_mark_message_read()
[libisds.git] / src / isds.c
blobf5a5054366985ee734179ebb11f44c2e70140c9d
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 "isds_priv.h"
8 #include "utils.h"
9 #include "soap.h"
10 #include "validator.h"
11 #include "crypto.h"
12 #include <gpg-error.h> /* Because of ksba or gpgme */
15 /* Free isds_list with all member data.
16 * @list list to free, on return will be NULL */
17 void isds_list_free(struct isds_list **list) {
18 struct isds_list *item, *next_item;
20 if (!list || !*list) return;
22 for(item = *list; item; item = next_item) {
23 (item->destructor)(&(item->data));
24 next_item = item->next;
25 free(item);
28 *list = NULL;
32 /* Deallocate structure isds_hash and NULL it.
33 * @hash hash to to free */
34 void isds_hash_free(struct isds_hash **hash) {
35 if(!hash || !*hash) return;
36 free((*hash)->value);
37 zfree((*hash));
41 /* Deallocate structure isds_PersonName recursively and NULL it */
42 static void isds_PersonName_free(struct isds_PersonName **person_name) {
43 if (!person_name || !*person_name) return;
45 free((*person_name)->pnFirstName);
46 free((*person_name)->pnMiddleName);
47 free((*person_name)->pnLastName);
48 free((*person_name)->pnLastNameAtBirth);
50 free(*person_name);
51 *person_name = NULL;
55 /* Deallocate structure isds_BirthInfo recursively and NULL it */
56 static void isds_BirthInfo_free(struct isds_BirthInfo **birth_info) {
57 if (!birth_info || !*birth_info) return;
59 free((*birth_info)->biDate);
60 free((*birth_info)->biCity);
61 free((*birth_info)->biCounty);
62 free((*birth_info)->biState);
64 free(*birth_info);
65 *birth_info = NULL;
69 /* Deallocate structure isds_Address recursively and NULL it */
70 static void isds_Address_free(struct isds_Address **address) {
71 if (!address || !*address) return;
73 free((*address)->adCity);
74 free((*address)->adStreet);
75 free((*address)->adNumberInStreet);
76 free((*address)->adNumberInMunicipality);
77 free((*address)->adZipCode);
78 free((*address)->adState);
80 free(*address);
81 *address = NULL;
85 /* Deallocate structure isds_DbOwnerInfo recursively and NULL it */
86 void isds_DbOwnerInfo_free(struct isds_DbOwnerInfo **db_owner_info) {
87 if (!db_owner_info || !*db_owner_info) return;
89 free((*db_owner_info)->dbID);
90 free((*db_owner_info)->dbType);
91 free((*db_owner_info)->ic);
92 isds_PersonName_free(&((*db_owner_info)->personName));
93 free((*db_owner_info)->firmName);
94 isds_BirthInfo_free(&((*db_owner_info)->birthInfo));
95 isds_Address_free(&((*db_owner_info)->address));
96 free((*db_owner_info)->nationality);
97 free((*db_owner_info)->email);
98 free((*db_owner_info)->telNumber);
99 free((*db_owner_info)->identifier);
100 free((*db_owner_info)->registryCode);
101 free((*db_owner_info)->dbState);
102 free((*db_owner_info)->dbEffectiveOVM);
104 free(*db_owner_info);
105 *db_owner_info = NULL;
109 /* Deallocate struct isds_envelope recurisvely and NULL it */
110 void isds_envelope_free(struct isds_envelope **envelope) {
111 if (!envelope || !*envelope) return;
113 free((*envelope)->dmID);
114 free((*envelope)->dbIDSender);
115 free((*envelope)->dmSender);
116 free((*envelope)->dmSenderAddress);
117 free((*envelope)->dmSenderType);
118 free((*envelope)->dmRecipient);
119 free((*envelope)->dmRecipientAddress);
120 free((*envelope)->dmAmbiguousRecipient);
122 free((*envelope)->dmOrdinal);
123 free((*envelope)->dmMessageStatus);
124 free((*envelope)->dmDeliveryTime);
125 free((*envelope)->dmAcceptanceTime);
126 isds_hash_free(&(*envelope)->hash);
127 free((*envelope)->timestamp);
129 free((*envelope)->dmSenderOrgUnit);
130 free((*envelope)->dmSenderOrgUnitNum);
131 free((*envelope)->dbIDRecipient);
132 free((*envelope)->dmRecipientOrgUnit);
133 free((*envelope)->dmRecipientOrgUnitNum);
134 free((*envelope)->dmToHands);
135 free((*envelope)->dmAnnotation);
136 free((*envelope)->dmRecipientRefNumber);
137 free((*envelope)->dmSenderRefNumber);
138 free((*envelope)->dmRecipientIdent);
139 free((*envelope)->dmSenderIdent);
141 free((*envelope)->dmLegalTitleLaw);
142 free((*envelope)->dmLegalTitleYear);
143 free((*envelope)->dmLegalTitleSect);
144 free((*envelope)->dmLegalTitlePar);
145 free((*envelope)->dmLegalTitlePoint);
147 free((*envelope)->dmPersonalDelivery);
148 free((*envelope)->dmAllowSubstDelivery);
149 free((*envelope)->dmOVM);
151 free(*envelope);
152 *envelope = NULL;
156 /* Deallocate struct isds_message recurisvely and NULL it */
157 void isds_message_free(struct isds_message **message) {
158 if (!message || !*message) return;
160 free((*message)->raw);
161 isds_envelope_free(&((*message)->envelope));
162 isds_list_free(&((*message)->documents));
164 free(*message);
165 *message = NULL;
169 /* Deallocate struct isds_document recurisvely and NULL it */
170 void isds_document_free(struct isds_document **document) {
171 if (!document || !*document) return;
173 free((*document)->data);
174 free((*document)->dmMimeType);
175 free((*document)->dmFileGuid);
176 free((*document)->dmUpFileGuid);
177 free((*document)->dmFileDescr);
178 free((*document)->dmFormat);
180 free(*document);
181 *document = NULL;
185 /* Initialize ISDS library.
186 * Global function, must be called before other functions.
187 * If it failes you can not use ISDS library and must call isds_cleanup() to
188 * free partially inititialized global variables. */
189 isds_error isds_init(void) {
190 /* NULL global variables */
191 log_facilities = ILF_ALL;
192 log_level = ILL_WARNING;
194 /* Initialize CURL */
195 if (curl_global_init(CURL_GLOBAL_ALL)) {
196 isds_log(ILF_ISDS, ILL_CRIT, _("CURL library initialization failed\n"));
197 return IE_ERROR;
200 /* Inicialize gpg-error because of gpgme and ksba */
201 if (gpg_err_init()) {
202 isds_log(ILF_ISDS, ILL_CRIT,
203 _("gpg-error library initialization failed\n"));
204 return IE_ERROR;
207 /* Initialize GPGME */
208 if (init_gpgme()) {
209 isds_log(ILF_ISDS, ILL_CRIT,
210 _("GPGME library initialization failed\n"));
211 return IE_ERROR;
214 /* Initialize gcrypt */
215 if (init_gcrypt()) {
216 isds_log(ILF_ISDS, ILL_CRIT,
217 _("gcrypt library initialization failed\n"));
218 return IE_ERROR;
222 /* This can _exit() current program. Find not so assertive check. */
223 LIBXML_TEST_VERSION;
225 /* Allocate global variables */
228 return IE_SUCCESS;
232 /* Deinicialize ISDS library.
233 * Global function, must be called as last library function. */
234 isds_error isds_cleanup(void) {
235 /* XML */
236 xmlCleanupParser();
238 /* Curl */
239 curl_global_cleanup();
241 return IE_SUCCESS;
245 /* Return text description of ISDS error */
246 char *isds_strerror(const isds_error error) {
247 switch (error) {
248 case IE_SUCCESS:
249 return(_("Success")); break;
250 case IE_ERROR:
251 return(_("Unspecified error")); break;
252 case IE_NOTSUP:
253 return(_("Not supported")); break;
254 case IE_INVAL:
255 return(_("Invalid value")); break;
256 case IE_INVALID_CONTEXT:
257 return(_("Invalid context")); break;
258 case IE_NOT_LOGGED_IN:
259 return(_("Not logged in")); break;
260 case IE_CONNECTION_CLOSED:
261 return(_("Connection closed")); break;
262 case IE_TIMED_OUT:
263 return(_("Timed out")); break;
264 case IE_NOEXIST:
265 return(_("Not exist")); break;
266 case IE_NOMEM:
267 return(_("Out of memory")); break;
268 case IE_NETWORK:
269 return(_("Network problem")); break;
270 case IE_HTTP:
271 return(_("HTTP problem")); break;
272 case IE_SOAP:
273 return(_("SOAP problem")); break;
274 case IE_XML:
275 return(_("XML problem")); break;
276 case IE_ISDS:
277 return(_("ISDS server problem")); break;
278 case IE_ENUM:
279 return(_("Invalid enum value")); break;
280 case IE_DATE:
281 return(_("Invalid date value")); break;
282 case IE_2BIG:
283 return(_("Too big")); break;
284 case IE_NOTUNIQ:
285 return(_("Value not unique")); break;
286 default:
287 return(_("Unknown error"));
292 /* Create ISDS context.
293 * Each context can be used for different sessions to (possibly) differnet
294 * ISDS server with different credentials. */
295 struct isds_ctx *isds_ctx_create(void) {
296 struct isds_ctx *context;
297 context = malloc(sizeof(*context));
298 if (context) memset(context, 0, sizeof(*context));
299 return context;
303 /* Destroy ISDS context and free memmory.
304 * @context will be NULLed on success. */
305 isds_error isds_ctx_free(struct isds_ctx **context) {
306 if (!context || !*context) {
307 return IE_INVALID_CONTEXT;
310 /* Discard credentials */
311 isds_logout(*context);
313 /* Free other structures */
314 free((*context)->tls_verify_server);
315 free((*context)->tls_ca_file);
316 free((*context)->tls_ca_dir);
317 free((*context)->long_message);
319 free(*context);
320 *context = NULL;
321 return IE_SUCCESS;
325 /* Return long message text produced by library fucntion, e.g. detailed error
326 * mesage. Returned pointer is only valid until new library function is
327 * called for the same context. Could be NULL, especially if NULL context is
328 * supplied. Return string is locale encoded. */
329 char *isds_long_message(const struct isds_ctx *context) {
330 if (!context) return NULL;
331 return context->long_message;
335 /* Stores message into context' long_message buffer.
336 * Application can pick the message up using isds_long_message().
337 * NULL @message truncates the buffer but does not deallocate it.
338 * @message is coded in locale encoding */
339 _hidden isds_error isds_log_message(struct isds_ctx *context,
340 const char *message) {
341 char *buffer;
342 size_t length;
344 if (!context) return IE_INVALID_CONTEXT;
346 /* FIXME: Check for integer overflow */
347 length = 1 + ((message) ? strlen(message) : 0);
348 buffer = realloc(context->long_message, length);
349 if (!buffer) return IE_NOMEM;
351 if (message)
352 strcpy(buffer, message);
353 else
354 *buffer = '\0';
356 context->long_message = buffer;
357 return IE_SUCCESS;
361 /* Appends message into context' long_message buffer.
362 * Application can pick the message up using isds_long_message().
363 * NULL message has void effect. */
364 _hidden isds_error isds_append_message(struct isds_ctx *context,
365 const char *message) {
366 char *buffer;
367 size_t old_length, length;
369 if (!context) return IE_INVALID_CONTEXT;
370 if (!message) return IE_SUCCESS;
371 if (!context->long_message)
372 return isds_log_message(context, message);
374 old_length = strlen(context->long_message);
375 /* FIXME: Check for integer overflow */
376 length = 1 + old_length + strlen(message);
377 buffer = realloc(context->long_message, length);
378 if (!buffer) return IE_NOMEM;
380 strcpy(buffer + old_length, message);
382 context->long_message = buffer;
383 return IE_SUCCESS;
387 /* Stores formated message into context' long_message buffer.
388 * Application can pick the message up using isds_long_message(). */
389 _hidden isds_error isds_printf_message(struct isds_ctx *context,
390 const char *format, ...) {
391 va_list ap;
392 int length;
394 if (!context) return IE_INVALID_CONTEXT;
395 va_start(ap, format);
396 length = isds_vasprintf(&(context->long_message), format, ap);
397 va_end(ap);
399 return (length < 0) ? IE_ERROR: IE_SUCCESS;
403 /* Set logging up.
404 * @facilities is bitmask of isds_log_facility values,
405 * @level is verbosity level. */
406 void isds_set_logging(const unsigned int facilities,
407 const isds_log_level level) {
408 log_facilities = facilities;
409 log_level = level;
413 /* Log @message in class @facility with log @level into global log. @message
414 * is printf(3) formating string, variadic arguments may be neccessary.
415 * For debugging purposes. */
416 _hidden isds_error isds_log(const isds_log_facility facility,
417 const isds_log_level level, const char *message, ...) {
418 va_list ap;
420 if (level > log_level) return IE_SUCCESS;
421 if (!(log_facilities & facility)) return IE_SUCCESS;
422 if (!message) return IE_INVAL;
424 /* TODO: Allow to register output function privided by application
425 * (e.g. fprintf to stderr or copy to text area GUI widget). */
427 va_start(ap, message);
428 vfprintf(stderr, message, ap);
429 va_end(ap);
430 /* Line buffered printf is default.
431 * fflush(stderr);*/
433 return IE_SUCCESS;
437 /* Connect to given url.
438 * It just makes TCP connection to ISDS server found in @url hostname part. */
439 /*int isds_connect(struct isds_ctx *context, const char *url);*/
441 /* Set timeout in miliseconds for each network job like connecting to server
442 * or sending message. Use 0 to disable timeout limits. */
443 isds_error isds_set_timeout(struct isds_ctx *context,
444 const unsigned int timeout) {
445 if (!context) return IE_INVALID_CONTEXT;
447 context->timeout = timeout;
449 if (context->curl) {
450 CURLcode curl_err;
452 curl_err = curl_easy_setopt(context->curl, CURLOPT_NOSIGNAL, 1);
453 if (!curl_err)
454 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT_MS,
455 context->timeout);
456 if (curl_err) return IE_ERROR;
459 return IE_SUCCESS;
463 /* Change SSL/TLS settings.
464 * @context is context which setting will be applied to
465 * @option is name of option. It determines the type of last argument. See
466 * isds_tls_option definition for more info.
467 * @... is value of new setting. Type is determined by @option
468 * */
469 isds_error isds_set_tls(struct isds_ctx *context, const isds_tls_option option,
470 ...) {
471 isds_error err = IE_SUCCESS;
472 va_list ap;
473 char *pointer, *string;
475 if (!context) return IE_INVALID_CONTEXT;
477 va_start(ap, option);
479 #define REPLACE_VA_STRING(destination) \
480 string = va_arg(ap, char *); \
481 if (string) { \
482 pointer = realloc((destination), 1 + strlen(string)); \
483 if (!pointer) { err = IE_NOMEM; goto leave; } \
484 strcpy(pointer, string); \
485 (destination) = pointer; \
486 } else { \
487 free(destination); \
488 (destination) = NULL; \
491 switch (option) {
492 case ITLS_VERIFY_SERVER:
493 if (!context->tls_verify_server) {
494 context->tls_verify_server =
495 malloc(sizeof(*context->tls_verify_server));
496 if (!context->tls_verify_server) {
497 err = IE_NOMEM; goto leave;
500 *context->tls_verify_server = (_Bool) (0 != va_arg(ap, int));
501 break;
503 case ITLS_CA_FILE:
504 REPLACE_VA_STRING(context->tls_ca_file);
505 break;
506 case ITLS_CA_DIRECTORY:
507 REPLACE_VA_STRING(context->tls_ca_dir);
508 break;
510 default:
511 err = IE_ENUM; goto leave;
514 #undef REPLACE_VA_STRING
516 leave:
517 va_end(ap);
518 return err;
522 /* Discard credentials.
523 * Only that. It does not cause log out, connection close or similar. */
524 static isds_error discard_credentials(struct isds_ctx *context) {
525 if(!context) return IE_INVALID_CONTEXT;
527 if (context->username) {
528 memset(context->username, 0, strlen(context->username));
529 free(context->username);
530 context->username = NULL;
532 if (context->password) {
533 memset(context->password, 0, strlen(context->password));
534 free(context->password);
535 context->password = NULL;
538 return IE_SUCCESS;
542 /* Connect and log in into ISDS server.
543 * @url is address of ISDS web service
544 * @username is user name of ISDS user
545 * @password is user's secret password
546 * @certificate is NULL terminated string with PEM formated client's
547 * certificate. Use NULL if only password autentication should be performed.
548 * @key is private key for client's certificate as (base64 encoded?) NULL
549 * terminated string. Use NULL if only password autentication is desired.
550 * */
551 isds_error isds_login(struct isds_ctx *context, const char *url,
552 const char *username, const char *password,
553 const char *certificate, const char* key) {
554 isds_error err = IE_NOT_LOGGED_IN;
555 isds_error soap_err;
556 xmlNsPtr isds_ns = NULL;
557 xmlNodePtr request = NULL;
558 xmlNodePtr response = NULL;
560 if (!context) return IE_INVALID_CONTEXT;
561 if (!url || !username || !password) return IE_INVAL;
562 if (certificate || key) return IE_NOTSUP;
564 /* Store configuration */
565 free(context->url);
566 context->url = strdup(url);
567 if (!(context->url))
568 return IE_NOMEM;
570 /* Close connection if already logged in */
571 if (context->curl) {
572 close_connection(context);
575 /* Prepare CURL handle */
576 context->curl = curl_easy_init();
577 if (!(context->curl))
578 return IE_ERROR;
580 /* Build login request */
581 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
582 if (!request) {
583 isds_log_message(context, _("Could build ISDS login request"));
584 return IE_ERROR;
586 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
587 if(!isds_ns) {
588 isds_log_message(context, _("Could not create ISDS name space"));
589 xmlFreeNode(request);
590 return IE_ERROR;
592 xmlSetNs(request, isds_ns);
594 /* Store credentials */
595 /* FIXME: mlock password
596 * (I have a library) */
597 discard_credentials(context);
598 context->username = strdup(username);
599 context->password = strdup(password);
600 if (!(context->username && context->password)) {
601 discard_credentials(context);
602 xmlFreeNode(request);
603 return IE_NOMEM;
606 isds_log(ILF_ISDS, ILL_DEBUG, _("Logging user %s into server %s\n"),
607 username, url);
609 /* Send login request */
610 soap_err = soap(context, "dz", request, &response);
612 /* Remove credentials */
613 discard_credentials(context);
615 /* Destroy login request */
616 xmlFreeNode(request);
618 if (soap_err) {
619 xmlFreeNodeList(response);
620 close_connection(context);
621 return soap_err;
624 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
625 * authentication succeeded if soap_err == IE_SUCCESS */
626 err = IE_SUCCESS;
628 xmlFreeNodeList(response);
630 if (!err)
631 isds_log(ILF_ISDS, ILL_DEBUG,
632 _("User %s has been logged into server %s successfully\n"),
633 username, url);
634 return err;
638 /* Log out from ISDS server discards credentials and connection configuration. */
639 isds_error isds_logout(struct isds_ctx *context) {
640 if (!context) return IE_INVALID_CONTEXT;
642 /* Close connection */
643 if (context->curl) {
644 close_connection(context);
646 /* Discard credentials for sure. They should not survive isds_login(),
647 * even successful .*/
648 discard_credentials(context);
649 free(context->url);
650 context->url = NULL;
652 isds_log(ILF_ISDS, ILL_DEBUG, _("Logged out from ISDS server\n"));
653 } else {
654 discard_credentials(context);
656 return IE_SUCCESS;
660 /* Verify connection to ISDS is alive and server is responding.
661 * Sent dumy request to ISDS and expect dummy response. */
662 isds_error isds_ping(struct isds_ctx *context) {
663 isds_error soap_err;
664 xmlNsPtr isds_ns = NULL;
665 xmlNodePtr request = NULL;
666 xmlNodePtr response = NULL;
668 if (!context) return IE_INVALID_CONTEXT;
670 /* Check if connection is established */
671 if (!context->curl) return IE_CONNECTION_CLOSED;
674 /* Build dummy request */
675 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
676 if (!request) {
677 isds_log_message(context, _("Could build ISDS dummy request"));
678 return IE_ERROR;
680 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
681 if(!isds_ns) {
682 isds_log_message(context, _("Could not create ISDS name space"));
683 xmlFreeNode(request);
684 return IE_ERROR;
686 xmlSetNs(request, isds_ns);
688 isds_log(ILF_ISDS, ILL_DEBUG, _("Pinging ISDS server\n"));
690 /* Sent dummy request */
691 soap_err = soap(context, "dz", request, &response);
693 /* Destroy login request */
694 xmlFreeNode(request);
696 if (soap_err) {
697 isds_log(ILF_ISDS, ILL_DEBUG,
698 _("ISDS server could not be contacted\n"));
699 xmlFreeNodeList(response);
700 close_connection(context);
701 return soap_err;
704 /* XXX: Untill we don't propagate HTTP code 500 or 4xx, we can be sure
705 * authentication succeeded if soap_err == IE_SUCCESS */
706 /* TODO: ISDS documentation does not specify response body.
707 * However real server sends back DummyOperationResponse */
710 xmlFreeNodeList(response);
712 isds_log(ILF_ISDS, ILL_DEBUG, _("ISDS server alive\n"));
714 return IE_SUCCESS;
718 /* Send bogus request to ISDS.
719 * Just for test purposes */
720 isds_error isds_bogus_request(struct isds_ctx *context) {
721 isds_error err;
722 xmlNsPtr isds_ns = NULL;
723 xmlNodePtr request = NULL;
724 xmlDocPtr response = NULL;
725 xmlChar *code = NULL, *message = NULL;
727 if (!context) return IE_INVALID_CONTEXT;
729 /* Check if connection is established */
730 if (!context->curl) {
731 /* Testing printf message */
732 isds_printf_message(context, "%s", _("I said connection closed"));
733 return IE_CONNECTION_CLOSED;
737 /* Build dummy request */
738 request = xmlNewNode(NULL, BAD_CAST "X-BogusOperation");
739 if (!request) {
740 isds_log_message(context, _("Could build ISDS bogus request"));
741 return IE_ERROR;
743 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
744 if(!isds_ns) {
745 isds_log_message(context, _("Could not create ISDS name space"));
746 xmlFreeNode(request);
747 return IE_ERROR;
749 xmlSetNs(request, isds_ns);
751 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending bogus request to ISDS\n"));
753 /* Sent bogus request */
754 err = isds(context, SERVICE_DM_OPERATIONS, request, &response);
756 /* Destroy request */
757 xmlFreeNode(request);
759 if (err) {
760 isds_log(ILF_ISDS, ILL_DEBUG,
761 _("Processing ISDS response on bogus request failed\n"));
762 xmlFreeDoc(response);
763 return err;
766 /* Check for response status */
767 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
768 &code, &message, NULL);
769 if (err) {
770 isds_log(ILF_ISDS, ILL_DEBUG,
771 _("ISDS response on bogus request is missing status\n"));
772 free(code);
773 free(message);
774 xmlFreeDoc(response);
775 return err;
777 if (xmlStrcmp(code, BAD_CAST "0000")) {
778 char *code_locale = utf82locale((char*)code);
779 char *message_locale = utf82locale((char*)message);
780 isds_log(ILF_ISDS, ILL_DEBUG,
781 _("Server refused bogus request (code=%s, message=%s)\n"),
782 code_locale, message_locale);
783 /* XXX: Literal error messages from ISDS are Czech mesages
784 * (English sometimes) in UTF-8. It's hard to catch them for
785 * translation. Successfully gettextized would return in locale
786 * encoding, unsuccessfully translated would pass in UTF-8. */
787 isds_log_message(context, message_locale);
788 free(code_locale);
789 free(message_locale);
790 free(code);
791 free(message);
792 xmlFreeDoc(response);
793 return IE_ISDS;
797 free(code);
798 free(message);
799 xmlFreeDoc(response);
801 isds_log(ILF_ISDS, ILL_DEBUG,
802 _("Bogus message accepted by server. This should not happen.\n"));
804 return IE_SUCCESS;
808 /* Serialize XML subtree to buffer preserving XML indentatition.
809 * @context is session context
810 * @subtree is XML element to be serialized (with childern)
811 * @buffer is automarically reallocated buffer where serialize to
812 * @length is size of serialized stream in bytes
813 * @return standard error code, free @buffer in case of error */
814 static isds_error serialize_subtree(struct isds_ctx *context,
815 xmlNodePtr subtree, void **buffer, size_t *length) {
816 isds_error err = IE_SUCCESS;
817 xmlBufferPtr xml_buffer = NULL;
818 xmlSaveCtxtPtr save_ctx = NULL;
819 xmlDocPtr subtree_doc = NULL;
820 xmlNodePtr subtree_copy;
821 xmlNsPtr isds_ns;
822 void *new_buffer;
824 if (!context) return IE_INVALID_CONTEXT;
825 if (!buffer) return IE_INVAL;
826 zfree(*buffer);
827 if (!subtree || !length) return IE_INVAL;
829 /* Make temporary XML document with @subtree root element */
830 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
831 * It can result in not well-formed on invalid XML tree (e.g. name space
832 * prefix definition can miss. */
833 /*FIXME */
835 subtree_doc = xmlNewDoc(BAD_CAST "1.0");
836 if (!subtree_doc) {
837 isds_log_message(context, _("Could not build temporary document"));
838 err = IE_ERROR;
839 goto leave;
842 /* XXX: Copy subtree and attach the copy to document.
843 * One node can not bee attached into more document at the same time.
844 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
845 * automatically.
846 * XXX: Check xmlSaveTree() too. */
847 subtree_copy = xmlCopyNodeList(subtree);
848 if (!subtree_copy) {
849 isds_log_message(context, _("Could not copy subtree"));
850 err = IE_ERROR;
851 goto leave;
853 xmlDocSetRootElement(subtree_doc, subtree_copy);
855 /* Only this way we get namespace definition as @xmlns:isds,
856 * otherwise we get namespace prefix without definition */
857 /* FIXME: Don't overwrite original default namespace */
858 isds_ns = xmlNewNs(subtree_copy, BAD_CAST ISDS_NS, NULL);
859 if(!isds_ns) {
860 isds_log_message(context, _("Could not create ISDS name space"));
861 err = IE_ERROR;
862 goto leave;
864 xmlSetNs(subtree_copy, isds_ns);
867 /* Serialize the document into buffer */
868 xml_buffer = xmlBufferCreate();
869 if (!xml_buffer) {
870 isds_log_message(context, _("Could not create xmlBuffer"));
871 err = IE_ERROR;
872 goto leave;
874 /* Last argument 0 means to not format the XML tree */
875 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8", 0);
876 if (!save_ctx) {
877 isds_log_message(context, _("Could not create XML serializer"));
878 err = IE_ERROR;
879 goto leave;
881 /* XXX: According LibXML documentation, this function does not return
882 * meaningfull value yet */
883 xmlSaveDoc(save_ctx, subtree_doc);
884 if (-1 == xmlSaveFlush(save_ctx)) {
885 isds_log_message(context,
886 _("Could not serialize XML subtree"));
887 err = IE_ERROR;
888 goto leave;
890 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
891 * even after xmlSaveFlush(). Thus close it here */
892 xmlSaveClose(save_ctx); save_ctx = NULL;
895 /* Store and detach buffer from xml_buffer */
896 *buffer = xml_buffer->content;
897 *length = xml_buffer->use;
898 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
900 /* Shrink buffer */
901 new_buffer = realloc(*buffer, *length);
902 if (new_buffer) *buffer = new_buffer;
904 leave:
905 if (err) {
906 zfree(*buffer);
907 *length = 0;
910 xmlSaveClose(save_ctx);
911 xmlBufferFree(xml_buffer);
912 xmlFreeDoc(subtree_doc); /* Frees subtree_copy, isds_ns etc. */
913 return err;
917 /* Dump XML subtree to buffer as literral string, not valid XML possibly.
918 * @context is session context
919 * @document is original document where @nodeset points to
920 * @nodeset is XPath node set to dump (recursively)
921 * @buffer is automarically reallocated buffer where serialize to
922 * @length is size of serialized stream in bytes
923 * @return standard error code, free @buffer in case of error */
924 static isds_error dump_nodeset(struct isds_ctx *context,
925 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
926 void **buffer, size_t *length) {
927 isds_error err = IE_SUCCESS;
928 xmlBufferPtr xml_buffer = NULL;
929 void *new_buffer;
931 if (!context) return IE_INVALID_CONTEXT;
932 if (!buffer) return IE_INVAL;
933 zfree(*buffer);
934 if (!document || !nodeset || !length) return IE_INVAL;
935 *length = 0;
937 /* Empty node set results into NULL buffer */
938 if (xmlXPathNodeSetIsEmpty(nodeset)) {
939 goto leave;
942 /* Resuling the document into buffer */
943 xml_buffer = xmlBufferCreate();
944 if (!xml_buffer) {
945 isds_log_message(context, _("Could not create xmlBuffer"));
946 err = IE_ERROR;
947 goto leave;
950 /* Itearate over all nodes */
951 for (int i = 0; i < nodeset->nodeNr; i++) {
952 /* Serialize node.
953 * XXX: xmlNodeDump() appends to xml_buffer. */
954 if (-1 ==
955 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
956 isds_log_message(context, _("Could not dump XML node"));
957 err = IE_ERROR;
958 goto leave;
962 /* Store and detach buffer from xml_buffer */
963 *buffer = xml_buffer->content;
964 *length = xml_buffer->use;
965 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
967 /* Shrink buffer */
968 new_buffer = realloc(*buffer, *length);
969 if (new_buffer) *buffer = new_buffer;
972 leave:
973 if (err) {
974 zfree(*buffer);
975 *length = 0;
978 xmlBufferFree(xml_buffer);
979 return err;
983 /* Convert UTF-8 @string represantion of ISDS dbType to enum @type */
984 static isds_error string2isds_DbType(xmlChar *string, isds_DbType *type) {
985 if (!string || !type) return IE_INVAL;
987 if (!xmlStrcmp(string, BAD_CAST "FO"))
988 *type = DBTYPE_FO;
989 else if (!xmlStrcmp(string, BAD_CAST "PFO"))
990 *type = DBTYPE_PFO;
991 else if (!xmlStrcmp(string, BAD_CAST "PFO_ADVOK"))
992 *type = DBTYPE_PFO_ADVOK;
993 else if (!xmlStrcmp(string, BAD_CAST "PFO_DANPOR"))
994 *type = DBTYPE_PFO_DANPOR;
995 else if (!xmlStrcmp(string, BAD_CAST "PFO_INSSPR"))
996 *type = DBTYPE_PFO_INSSPR;
997 else if (!xmlStrcmp(string, BAD_CAST "PO"))
998 *type = DBTYPE_PO;
999 else if (!xmlStrcmp(string, BAD_CAST "PO_ZAK"))
1000 *type = DBTYPE_PO_ZAK;
1001 else if (!xmlStrcmp(string, BAD_CAST "PO_REQ"))
1002 *type = DBTYPE_PO_REQ;
1003 else if (!xmlStrcmp(string, BAD_CAST "OVM"))
1004 *type = DBTYPE_OVM;
1005 else if (!xmlStrcmp(string, BAD_CAST "OVM_NOTAR"))
1006 *type = DBTYPE_OVM_NOTAR;
1007 else if (!xmlStrcmp(string, BAD_CAST "OVM_EXEKUT"))
1008 *type = DBTYPE_OVM_EXEKUT;
1009 else if (!xmlStrcmp(string, BAD_CAST "OVM_REQ"))
1010 *type = DBTYPE_OVM_REQ;
1011 else
1012 return IE_ENUM;
1013 return IE_SUCCESS;
1017 /* Convert ISDS dbType enum @type to UTF-8 string.
1018 * @Return pointer to static string, or NULL if unkwnow enum value */
1019 static const xmlChar *isds_DbType2string(const isds_DbType type) {
1020 switch(type) {
1021 case DBTYPE_FO: return(BAD_CAST "FO"); break;
1022 case DBTYPE_PFO: return(BAD_CAST "PFO"); break;
1023 case DBTYPE_PFO_ADVOK: return(BAD_CAST "PFO_ADVOK"); break;
1024 case DBTYPE_PFO_DANPOR: return(BAD_CAST "PFO_DAPOR"); break;
1025 case DBTYPE_PFO_INSSPR: return(BAD_CAST "PFO_INSSPR"); break;
1026 case DBTYPE_PO: return(BAD_CAST "PO"); break;
1027 case DBTYPE_PO_ZAK: return(BAD_CAST "PO_ZAK"); break;
1028 case DBTYPE_PO_REQ: return(BAD_CAST "PO_REQ"); break;
1029 case DBTYPE_OVM: return(BAD_CAST "OVM"); break;
1030 case DBTYPE_OVM_NOTAR: return(BAD_CAST "OVM_NOTAR"); break;
1031 case DBTYPE_OVM_EXEKUT: return(BAD_CAST "OVM_EXEKUT"); break;
1032 case DBTYPE_OVM_REQ: return(BAD_CAST "OVM_REQ"); break;
1033 default: return NULL; break;
1038 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
1039 * @Return pointer to static string, or NULL if unkwnow enum value */
1040 static const xmlChar *isds_FileMetaType2string(const isds_FileMetaType type) {
1041 switch(type) {
1042 case FILEMETATYPE_MAIN: return(BAD_CAST "main"); break;
1043 case FILEMETATYPE_ENCLOSURE: return(BAD_CAST "enclosure"); break;
1044 case FILEMETATYPE_SIGNATURE: return(BAD_CAST "signature"); break;
1045 case FILEMETATYPE_META: return(BAD_CAST "meta"); break;
1046 default: return NULL; break;
1051 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
1052 * @Return IE_ENUM if @string is not valid enum member */
1053 static isds_error string2isds_FileMetaType(const xmlChar *string,
1054 isds_FileMetaType *type) {
1055 if (!string || !type) return IE_INVAL;
1057 if (!xmlStrcmp(string, BAD_CAST "main"))
1058 *type = FILEMETATYPE_MAIN;
1059 else if (!xmlStrcmp(string, BAD_CAST "enclosure"))
1060 *type = FILEMETATYPE_ENCLOSURE;
1061 else if (!xmlStrcmp(string, BAD_CAST "signature"))
1062 *type = FILEMETATYPE_SIGNATURE;
1063 else if (!xmlStrcmp(string, BAD_CAST "meta"))
1064 *type = FILEMETATYPE_META;
1065 else
1066 return IE_ENUM;
1067 return IE_SUCCESS;
1071 /* Convert UTF-8 @string to ISDS hash @algorithm.
1072 * @Return IE_ENUM if @string is not valid enum member */
1073 static isds_error string2isds_hash_algorithm(const xmlChar *string,
1074 isds_hash_algorithm *algorithm) {
1075 if (!string || !algorithm) return IE_INVAL;
1077 if (!xmlStrcmp(string, BAD_CAST "MD5"))
1078 *algorithm = HASH_ALGORITHM_MD5;
1079 else if (!xmlStrcmp(string, BAD_CAST "SHA-1"))
1080 *algorithm = HASH_ALGORITHM_SHA_1;
1081 else if (!xmlStrcmp(string, BAD_CAST "SHA-256"))
1082 *algorithm = HASH_ALGORITHM_SHA_256;
1083 else if (!xmlStrcmp(string, BAD_CAST "SHA-512"))
1084 *algorithm = HASH_ALGORITHM_SHA_512;
1085 else
1086 return IE_ENUM;
1087 return IE_SUCCESS;
1091 /* Convert UTF-8 @string represantion of ISO 8601 date to @time.
1092 * XXX: Not all ISO formats are supported */
1093 static isds_error datestring2tm(const xmlChar *string, struct tm *time) {
1094 char *offset;
1095 if (!string || !time) return IE_INVAL;
1097 /* xsd:date is ISO 8601 string, thus ASCII */
1098 offset = strptime((char*)string, "%Y-%m-%d", time);
1099 if (offset && *offset == '\0')
1100 return IE_SUCCESS;
1102 offset = strptime((char*)string, "%Y%m%d", time);
1103 if (offset && *offset == '\0')
1104 return IE_SUCCESS;
1106 offset = strptime((char*)string, "%Y-%j", time);
1107 if (offset && *offset == '\0')
1108 return IE_SUCCESS;
1110 return IE_NOTSUP;
1114 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
1115 static isds_error tm2datestring(const struct tm *time, xmlChar **string) {
1116 if (!time || !string) return IE_INVAL;
1118 if (-1 == isds_asprintf((char **) string, "%d-%02d-%02d",
1119 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
1120 return IE_ERROR;
1122 return IE_SUCCESS;
1126 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
1127 * respects the @time microseconds too. */
1128 static isds_error timeval2timestring(const struct timeval *time,
1129 xmlChar **string) {
1130 struct tm broken;
1132 if (!time || !string) return IE_INVAL;
1134 if (!gmtime_r(&time->tv_sec, &broken)) return IE_DATE;
1135 if (time->tv_usec < 0 || time->tv_usec > 999999) return IE_DATE;
1137 /* TODO: small negative year should be formated as "-0012". This is not
1138 * true for glibc "%04d". We should implement it.
1139 * TODO: What's type of time->tv_usec exactly? Unsigned? Absolute?
1140 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
1141 if (-1 == isds_asprintf((char **) string,
1142 "%04d-%02d-%02dT%02d:%02d:%02d.%06ld",
1143 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
1144 broken.tm_hour, broken.tm_min, broken.tm_sec,
1145 time->tv_usec))
1146 return IE_ERROR;
1148 return IE_SUCCESS;
1152 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
1153 * It respects microseconds too.
1154 * In case of error, @time will be freed. */
1155 static isds_error timestring2timeval(const xmlChar *string,
1156 struct timeval **time) {
1157 struct tm broken;
1158 char *offset, *delim, *endptr;
1159 char subseconds[7];
1160 int offset_hours, offset_minutes;
1161 int i;
1163 if (!time) return IE_INVAL;
1165 memset(&broken, 0, sizeof(broken));
1167 if (!*time) {
1168 *time = calloc(1, sizeof(**time));
1169 if (!*time) return IE_NOMEM;
1170 } else {
1171 memset(*time, 0, sizeof(**time));
1175 /* xsd:date is ISO 8601 string, thus ASCII */
1176 /*TODO: negative year */
1178 /* Parse date and time without subseconds and offset */
1179 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
1180 if (!offset) {
1181 free(*time); *time = NULL;
1182 return IE_DATE;
1185 /* Get subseconds */
1186 if (*offset == '.' ) {
1187 offset++;
1189 /* Copy first 6 digits, padd it with zeros.
1190 * XXX: It truncates longer number, no round.
1191 * Current server implementation uses only milisecond resolution. */
1192 /* TODO: isdigit() is locale sensitive */
1193 for (i = 0;
1194 i < sizeof(subseconds)/sizeof(char) - 1 && isdigit(*offset);
1195 i++, offset++) {
1196 subseconds[i] = *offset;
1198 for (; i < sizeof(subseconds)/sizeof(char) - 1; i++) {
1199 subseconds[i] = '0';
1201 subseconds[6] = '\0';
1203 /* Convert it into integer */
1204 (*time)->tv_usec = strtol(subseconds, &endptr, 10);
1205 if (*endptr != '\0' || (*time)->tv_usec == LONG_MIN ||
1206 (*time)->tv_usec == LONG_MAX) {
1207 free(*time); *time = NULL;
1208 return IE_DATE;
1211 /* move to the zone offset delimiter */
1212 delim = strchr(offset, '-');
1213 if (!delim)
1214 delim = strchr(offset, '+');
1215 offset = delim;
1218 /* Get zone offset */
1219 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
1220 * "" equals to "Z" and it means UTC zone. */
1221 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
1222 * colon separator */
1223 if (*offset == '-' || *offset == '+') {
1224 offset++;
1225 if (2 != sscanf(offset, "%2d:%2d", &offset_hours, &offset_minutes)) {
1226 free(*time); *time = NULL;
1227 return IE_DATE;
1229 broken.tm_hour -= offset_hours;
1230 broken.tm_min -= offset_minutes * ((offset_hours<0) ? -1 : 1);
1233 /* Convert to time_t */
1234 switch_tz_to_utc();
1235 (*time)->tv_sec = mktime(&broken);
1236 switch_tz_to_native();
1237 if ((*time)->tv_sec == (time_t) -1) {
1238 free(*time); *time = NULL;
1239 return IE_DATE;
1242 return IE_SUCCESS;
1246 /* Convert unsigned int into isds_message_status.
1247 * @context is session context
1248 * @number is pointer to number value. NULL will be treated as invalid value.
1249 * @status is automatically reallocated status
1250 * @return IE_SUCCESS, or error code and free status */
1251 static isds_error uint2isds_message_status(struct isds_ctx *context,
1252 const unsigned long int *number, isds_message_status **status) {
1253 if (!context) return IE_INVALID_CONTEXT;
1254 if (!status) return IE_INVAL;
1256 free(*status); *status = NULL;
1257 if (!number) return IE_INVAL;
1259 if (*number < 1 || *number > 9) {
1260 isds_printf_message(context, _("Invalid messsage status value: %lu"),
1261 *number);
1262 return IE_ENUM;
1265 *status = malloc(sizeof(**status));
1266 if (!*status) return IE_NOMEM;
1268 **status = 1 << *number;
1269 return IE_SUCCESS;
1273 /* Following EXTRACT_* macros expects @result, @xpath_ctx, @err, @context
1274 * and leave lable */
1275 #define EXTRACT_STRING(element, string) \
1276 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
1277 if (!result) { \
1278 err = IE_ERROR; \
1279 goto leave; \
1281 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
1282 if (result->nodesetval->nodeNr > 1) { \
1283 isds_log_message(context, _("Multiple " element " element")); \
1284 err = IE_ERROR; \
1285 goto leave; \
1287 (string) = (char *) \
1288 xmlXPathCastNodeSetToString(result->nodesetval); \
1289 if (!(string)) { \
1290 err = IE_ERROR; \
1291 goto leave; \
1295 #define EXTRACT_BOOLEAN(element, booleanPtr) \
1297 char *string = NULL; \
1298 EXTRACT_STRING(element, string); \
1300 if (string) { \
1301 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
1302 if (!(booleanPtr)) { \
1303 free(string); \
1304 err = IE_NOMEM; \
1305 goto leave; \
1308 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
1309 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
1310 *(booleanPtr) = 1; \
1311 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
1312 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
1313 *(booleanPtr) = 0; \
1314 else { \
1315 char *string_locale = utf82locale((char*)string); \
1316 isds_printf_message(context, \
1317 _(element " value is not valid boolean: "), \
1318 string_locale); \
1319 free(string_locale); \
1320 free(string); \
1321 err = IE_ERROR; \
1322 goto leave; \
1325 free(string); \
1329 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
1331 char *string = NULL; \
1332 EXTRACT_STRING(element, string); \
1333 if (string) { \
1334 long int number; \
1335 char *endptr; \
1337 number = strtol((char*)string, &endptr, 10); \
1339 if (*endptr != '\0') { \
1340 char *string_locale = utf82locale((char *)string); \
1341 isds_printf_message(context, \
1342 _(element" is not valid integer: %s"), \
1343 string_locale); \
1344 free(string_locale); \
1345 free(string); \
1346 err = IE_ISDS; \
1347 goto leave; \
1350 if (number == LONG_MIN || number == LONG_MAX) { \
1351 char *string_locale = utf82locale((char *)string); \
1352 isds_printf_message(context, \
1353 _(element " value out of range of long int: %s"), \
1354 string_locale); \
1355 free(string_locale); \
1356 free(string); \
1357 err = IE_ERROR; \
1358 goto leave; \
1361 free(string); string = NULL; \
1363 if (!(preallocated)) { \
1364 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
1365 if (!(longintPtr)) { \
1366 err = IE_NOMEM; \
1367 goto leave; \
1370 *(longintPtr) = number; \
1374 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
1376 char *string = NULL; \
1377 EXTRACT_STRING(element, string); \
1378 if (string) { \
1379 long int number; \
1380 char *endptr; \
1382 number = strtol((char*)string, &endptr, 10); \
1384 if (*endptr != '\0') { \
1385 char *string_locale = utf82locale((char *)string); \
1386 isds_printf_message(context, \
1387 _(element" is not valid integer: %s"), \
1388 string_locale); \
1389 free(string_locale); \
1390 free(string); \
1391 err = IE_ISDS; \
1392 goto leave; \
1395 if (number == LONG_MIN || number == LONG_MAX) { \
1396 char *string_locale = utf82locale((char *)string); \
1397 isds_printf_message(context, \
1398 _(element " value out of range of long int: %s"), \
1399 string_locale); \
1400 free(string_locale); \
1401 free(string); \
1402 err = IE_ERROR; \
1403 goto leave; \
1406 free(string); string = NULL; \
1407 if (number < 0) { \
1408 isds_printf_message(context, \
1409 _(element " value is negative: %ld"), number); \
1410 err = IE_ERROR; \
1411 goto leave; \
1414 if (!(preallocated)) { \
1415 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
1416 if (!(ulongintPtr)) { \
1417 err = IE_NOMEM; \
1418 goto leave; \
1421 *(ulongintPtr) = number; \
1425 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) \
1426 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
1427 NULL); \
1428 if ((required) && (!string)) { \
1429 char *attribute_locale = utf82locale(attribute); \
1430 char *element_locale = utf82locale((char *)xpath_ctx->node->name); \
1431 isds_printf_message(context, \
1432 _("Could not extract required %s attribute value from " \
1433 "%s element"), attribute_locale, element_locale); \
1434 free(element_locale); \
1435 free(attribute_locale); \
1436 err = IE_ERROR; \
1437 goto leave; \
1441 #define INSERT_STRING(parent, element, string) \
1442 node = xmlNewTextChild(parent, NULL, BAD_CAST (element), \
1443 (xmlChar *) (string)); \
1444 if (!node) { \
1445 isds_printf_message(context, _("Could not add " element " child to " \
1446 "%s element"), (parent)->name); \
1447 err = IE_ERROR; \
1448 goto leave; \
1451 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
1452 if ((booleanPtr)) { \
1453 if (*(booleanPtr)) { INSERT_STRING(parent, element, "true"); } \
1454 else { INSERT_STRING(parent, element, "false") } \
1455 } else { INSERT_STRING(parent, element, NULL) }
1457 #define INSERT_LONGINT(parent, element, longintPtr, buffer) \
1458 if ((longintPtr)) { \
1459 /* FIXME: locale sensitive */ \
1460 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
1461 err = IE_NOMEM; \
1462 goto leave; \
1464 INSERT_STRING(parent, element, buffer) \
1465 free(buffer); (buffer) = NULL; \
1466 } else { INSERT_STRING(parent, element, NULL) }
1468 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) \
1469 if ((ulongintPtr)) { \
1470 /* FIXME: locale sensitive */ \
1471 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
1472 err = IE_NOMEM; \
1473 goto leave; \
1475 INSERT_STRING(parent, element, buffer) \
1476 free(buffer); (buffer) = NULL; \
1477 } else { INSERT_STRING(parent, element, NULL) }
1479 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
1480 /* FIXME: locale sensitive */ \
1481 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
1482 err = IE_NOMEM; \
1483 goto leave; \
1485 INSERT_STRING(parent, element, buffer) \
1486 free(buffer); (buffer) = NULL; \
1488 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
1489 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
1490 (xmlChar *) (string)); \
1491 if (!attribute_node) { \
1492 isds_printf_message(context, _("Could not add " attribute \
1493 " attribute to %s element"), (parent)->name); \
1494 err = IE_ERROR; \
1495 goto leave; \
1499 /* Find child element by name in given XPath context and switch context onto
1500 * it. The child must be uniq and must exist. Otherwise failes.
1501 * @context is ISDS context
1502 * @child is child element name
1503 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
1504 * into it child. In error case, the @xpath_ctx keeps original value. */
1505 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
1506 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
1507 isds_error err = IE_SUCCESS;
1508 xmlXPathObjectPtr result = NULL;
1510 if (!context) return IE_INVALID_CONTEXT;
1511 if (!child || !xpath_ctx) return IE_INVAL;
1513 /* Find child */
1514 result = xmlXPathEvalExpression(child, xpath_ctx);
1515 if (!result) {
1516 err = IE_XML;
1517 goto leave;
1520 /* No match */
1521 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
1522 char *parent_locale = utf82locale((char*) xpath_ctx->node->name);
1523 char *child_locale = utf82locale((char*) child);
1524 isds_printf_message(context,
1525 _("%s element does not contain %s child"),
1526 parent_locale, child_locale);
1527 free(child_locale);
1528 free(parent_locale);
1529 err = IE_NOEXIST;
1530 goto leave;
1533 /* More matches */
1534 if (result->nodesetval->nodeNr > 1) {
1535 char *parent_locale = utf82locale((char*) xpath_ctx->node->name);
1536 char *child_locale = utf82locale((char*) child);
1537 isds_printf_message(context,
1538 _("%s element contains multiple %s childs"),
1539 parent_locale, child_locale);
1540 free(child_locale);
1541 free(parent_locale);
1542 err = IE_NOTUNIQ;
1543 goto leave;
1546 /* Switch context */
1547 xpath_ctx->node = result->nodesetval->nodeTab[0];
1549 leave:
1550 xmlXPathFreeObject(result);
1551 return err;
1556 /* Convert isds:dBOwnerInfo XML tree into structure
1557 * @context is ISDS context
1558 * @db_owner_info is automically reallocated box owner info structure
1559 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
1560 * In case of error @db_owner_info will be freed. */
1561 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
1562 struct isds_DbOwnerInfo **db_owner_info,
1563 xmlXPathContextPtr xpath_ctx) {
1564 isds_error err = IE_SUCCESS;
1565 xmlXPathObjectPtr result = NULL;
1566 char *string = NULL;
1568 if (!context) return IE_INVALID_CONTEXT;
1569 if (!db_owner_info) return IE_INVAL;
1570 isds_DbOwnerInfo_free(db_owner_info);
1571 if (!xpath_ctx) return IE_INVAL;
1574 *db_owner_info = calloc(1, sizeof(**db_owner_info));
1575 if (!*db_owner_info) {
1576 err = IE_NOMEM;
1577 goto leave;
1580 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
1582 EXTRACT_STRING("isds:dbType", string);
1583 if (string) {
1584 (*db_owner_info)->dbType =
1585 calloc(1, sizeof(*((*db_owner_info)->dbType)));
1586 if (!(*db_owner_info)->dbType) {
1587 err = IE_NOMEM;
1588 goto leave;
1590 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
1591 if (err) {
1592 free((*db_owner_info)->dbType);
1593 (*db_owner_info)->dbType = NULL;
1594 if (err == IE_ENUM) {
1595 err = IE_ISDS;
1596 isds_printf_message(context, _("Unknown isds:dbType: %s"),
1597 (char *)string);
1599 goto leave;
1601 free(string); string = NULL;
1604 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
1606 (*db_owner_info)->personName =
1607 calloc(1, sizeof(*((*db_owner_info)->personName)));
1608 if (!(*db_owner_info)->personName) {
1609 err = IE_NOMEM;
1610 goto leave;
1612 EXTRACT_STRING("isds:pnFirstName",
1613 (*db_owner_info)->personName->pnFirstName);
1614 EXTRACT_STRING("isds:pnMiddleName",
1615 (*db_owner_info)->personName->pnMiddleName);
1616 EXTRACT_STRING("isds:pnLastName",
1617 (*db_owner_info)->personName->pnLastName);
1618 EXTRACT_STRING("isds:pnLastNameAtBirth",
1619 (*db_owner_info)->personName->pnLastNameAtBirth);
1620 if (!(*db_owner_info)->personName->pnFirstName &&
1621 !(*db_owner_info)->personName->pnMiddleName &&
1622 !(*db_owner_info)->personName->pnLastName &&
1623 !(*db_owner_info)->personName->pnLastNameAtBirth)
1624 isds_PersonName_free(&(*db_owner_info)->personName);
1626 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
1628 (*db_owner_info)->birthInfo =
1629 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
1630 if (!(*db_owner_info)->birthInfo) {
1631 err = IE_NOMEM;
1632 goto leave;
1634 EXTRACT_STRING("isds:biDate", string);
1635 if (string) {
1636 (*db_owner_info)->birthInfo->biDate =
1637 calloc(1, sizeof(*((*db_owner_info)->birthInfo->biDate)));
1638 if (!(*db_owner_info)->birthInfo->biDate) {
1639 err = IE_NOMEM;
1640 goto leave;
1642 err = datestring2tm((xmlChar *)string,
1643 (*db_owner_info)->birthInfo->biDate);
1644 if (err) {
1645 free((*db_owner_info)->birthInfo->biDate);
1646 (*db_owner_info)->birthInfo->biDate = NULL;
1647 if (err == IE_NOTSUP) {
1648 err = IE_ISDS;
1649 isds_printf_message(context,
1650 _("Invalid isds:biDate value: %s"), (char *)string);
1652 goto leave;
1654 free(string); string = NULL;
1656 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
1657 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
1658 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
1659 if (!(*db_owner_info)->birthInfo->biDate &&
1660 !(*db_owner_info)->birthInfo->biCity &&
1661 !(*db_owner_info)->birthInfo->biCounty &&
1662 !(*db_owner_info)->birthInfo->biState)
1663 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
1665 (*db_owner_info)->address =
1666 calloc(1, sizeof(*((*db_owner_info)->address)));
1667 if (!(*db_owner_info)->address) {
1668 err = IE_NOMEM;
1669 goto leave;
1671 EXTRACT_STRING("isds:adCity",
1672 (*db_owner_info)->address->adCity);
1673 EXTRACT_STRING("isds:adStreet",
1674 (*db_owner_info)->address->adStreet);
1675 EXTRACT_STRING("isds:adNumberInStreet",
1676 (*db_owner_info)->address->adNumberInStreet);
1677 EXTRACT_STRING("isds:adNumberInMunicipality",
1678 (*db_owner_info)->address->adNumberInMunicipality);
1679 EXTRACT_STRING("isds:adZipCode",
1680 (*db_owner_info)->address->adZipCode);
1681 EXTRACT_STRING("isds:adState",
1682 (*db_owner_info)->address->adState);
1683 if (!(*db_owner_info)->address->adCity &&
1684 !(*db_owner_info)->address->adStreet &&
1685 !(*db_owner_info)->address->adNumberInStreet &&
1686 !(*db_owner_info)->address->adNumberInMunicipality &&
1687 !(*db_owner_info)->address->adZipCode &&
1688 !(*db_owner_info)->address->adState)
1689 isds_Address_free(&(*db_owner_info)->address);
1691 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
1692 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
1693 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
1694 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
1695 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
1697 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
1699 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
1700 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
1701 (*db_owner_info)->dbOpenAddressing);
1703 leave:
1704 if (err) isds_DbOwnerInfo_free(db_owner_info);
1705 free(string);
1706 xmlXPathFreeObject(result);
1707 return err;
1711 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
1712 * isds_envelope structure. The envelope is automatically allocated but not
1713 * reallocated. The date are just appended into envelope structure.
1714 * @context is ISDS context
1715 * @envelope is automically allocated message envelope structure
1716 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
1717 * In case of error @envelope will be freed. */
1718 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
1719 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
1720 isds_error err = IE_SUCCESS;
1721 xmlXPathObjectPtr result = NULL;
1723 if (!context) return IE_INVALID_CONTEXT;
1724 if (!envelope) return IE_INVAL;
1725 if (!xpath_ctx) return IE_INVAL;
1728 if (!*envelope) {
1729 /* Allocate envelope */
1730 *envelope = calloc(1, sizeof(**envelope));
1731 if (!*envelope) {
1732 err = IE_NOMEM;
1733 goto leave;
1735 } else {
1736 /* Else free former data */
1737 zfree((*envelope)->dmSenderOrgUnit);
1738 zfree((*envelope)->dmSenderOrgUnitNum);
1739 zfree((*envelope)->dbIDRecipient);
1740 zfree((*envelope)->dmRecipientOrgUnit);
1741 zfree((*envelope)->dmSenderOrgUnitNum);
1742 zfree((*envelope)->dmToHands);
1743 zfree((*envelope)->dmAnnotation);
1744 zfree((*envelope)->dmRecipientRefNumber);
1745 zfree((*envelope)->dmSenderRefNumber);
1746 zfree((*envelope)->dmRecipientIdent);
1747 zfree((*envelope)->dmSenderIdent);
1748 zfree((*envelope)->dmLegalTitleLaw);
1749 zfree((*envelope)->dmLegalTitleYear);
1750 zfree((*envelope)->dmLegalTitleSect);
1751 zfree((*envelope)->dmLegalTitlePar);
1752 zfree((*envelope)->dmLegalTitlePoint);
1753 zfree((*envelope)->dmPersonalDelivery);
1754 zfree((*envelope)->dmAllowSubstDelivery);
1757 /* Extract envelope elements added by sender or ISDS
1758 * (XSD: gMessageEnvelopeSub type) */
1759 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
1760 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
1761 (*envelope)->dmSenderOrgUnitNum, 0);
1762 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
1763 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
1764 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
1765 (*envelope)->dmSenderOrgUnitNum, 0);
1766 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
1767 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
1768 EXTRACT_STRING("isds:dmRecipientRefNumber",
1769 (*envelope)->dmRecipientRefNumber);
1770 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
1771 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
1772 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
1774 /* Extract envelope elements regarding law refference */
1775 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
1776 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
1777 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
1778 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
1779 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
1781 /* Extract envelope other elements */
1782 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
1783 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
1784 (*envelope)->dmAllowSubstDelivery);
1786 leave:
1787 if (err) isds_envelope_free(envelope);
1788 xmlXPathFreeObject(result);
1789 return err;
1794 /* Convert XSD gMessageEnvelope group of elements from XML tree into
1795 * isds_envelope structure. The envelope is automatically allocated but not
1796 * reallocated. The date are just appended into envelope structure.
1797 * @context is ISDS context
1798 * @envelope is automically allocated message envelope structure
1799 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
1800 * In case of error @envelope will be freed. */
1801 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
1802 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
1803 isds_error err = IE_SUCCESS;
1804 xmlXPathObjectPtr result = NULL;
1806 if (!context) return IE_INVALID_CONTEXT;
1807 if (!envelope) return IE_INVAL;
1808 if (!xpath_ctx) return IE_INVAL;
1811 if (!*envelope) {
1812 /* Allocate envelope */
1813 *envelope = calloc(1, sizeof(**envelope));
1814 if (!*envelope) {
1815 err = IE_NOMEM;
1816 goto leave;
1818 } else {
1819 /* Else free former data */
1820 zfree((*envelope)->dmID);
1821 zfree((*envelope)->dbIDSender);
1822 zfree((*envelope)->dmSender);
1823 zfree((*envelope)->dmSenderAddress);
1824 zfree((*envelope)->dmSenderType);
1825 zfree((*envelope)->dmRecipient);
1826 zfree((*envelope)->dmRecipientAddress);
1827 zfree((*envelope)->dmAmbiguousRecipient);
1830 /* Extract envelope elements added by ISDS
1831 * (XSD: gMessageEnvelope type) */
1832 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
1833 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
1834 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
1835 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
1836 /* XML Schema does not guaratee enumratation. It's plain xs:int. */
1837 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
1838 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
1839 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
1840 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
1841 (*envelope)->dmAmbiguousRecipient);
1843 /* Extract envelope elements added by sender and ISDS
1844 * (XSD: gMessageEnvelope type) */
1845 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
1846 if (err) goto leave;
1848 leave:
1849 if (err) isds_envelope_free(envelope);
1850 xmlXPathFreeObject(result);
1851 return err;
1855 /* Convert other envelope elements from XML tree into isds_envelope structure:
1856 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
1857 * The envelope is automatically allocated but not reallocated.
1858 * The data are just appended into envelope structure.
1859 * @context is ISDS context
1860 * @envelope is automically allocated message envelope structure
1861 * @xpath_ctx is XPath context with current node as parent desired elements
1862 * In case of error @envelope will be freed. */
1863 static isds_error append_status_size_times(struct isds_ctx *context,
1864 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
1865 isds_error err = IE_SUCCESS;
1866 xmlXPathObjectPtr result = NULL;
1867 char *string = NULL;
1868 unsigned long int *unumber = NULL;
1870 if (!context) return IE_INVALID_CONTEXT;
1871 if (!envelope) return IE_INVAL;
1872 if (!xpath_ctx) return IE_INVAL;
1875 if (!*envelope) {
1876 /* Allocate new */
1877 *envelope = calloc(1, sizeof(**envelope));
1878 if (!*envelope) {
1879 err = IE_NOMEM;
1880 goto leave;
1882 } else {
1883 /* Free old data */
1884 zfree((*envelope)->dmMessageStatus);
1885 zfree((*envelope)->dmAttachmentSize);
1886 zfree((*envelope)->dmDeliveryTime);
1887 zfree((*envelope)->dmAcceptanceTime);
1891 /* dmMessageStatus element is mandatory */
1892 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
1893 if (!unumber) {
1894 isds_log_message(context,
1895 _("Missing mandatory sisds:dmMessageStatus integer"));
1896 err = IE_ISDS;
1897 goto leave;
1899 err = uint2isds_message_status(context, unumber,
1900 &((*envelope)->dmMessageStatus));
1901 if (err) {
1902 if (err == IE_ENUM) err = IE_ISDS;
1903 goto leave;
1905 free(unumber); unumber = NULL;
1907 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
1910 EXTRACT_STRING("sisds:dmDeliveryTime", string);
1911 if (string) {
1912 err = timestring2timeval((xmlChar *) string,
1913 &((*envelope)->dmDeliveryTime));
1914 if (err) {
1915 char *string_locale = utf82locale(string);
1916 if (err == IE_DATE) err = IE_ISDS;
1917 isds_printf_message(context,
1918 _("Could not convert dmDeliveryTime as ISO time: %s"),
1919 string_locale);
1920 free(string_locale);
1921 goto leave;
1925 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
1926 if (string) {
1927 err = timestring2timeval((xmlChar *) string,
1928 &((*envelope)->dmAcceptanceTime));
1929 if (err) {
1930 char *string_locale = utf82locale(string);
1931 if (err == IE_DATE) err = IE_ISDS;
1932 isds_printf_message(context,
1933 _("Could not convert dmAcceptanceTime as ISO time: %s"),
1934 string_locale);
1935 free(string_locale);
1936 goto leave;
1940 leave:
1941 if (err) isds_envelope_free(envelope);
1942 free(unumber);
1943 free(string);
1944 xmlXPathFreeObject(result);
1945 return err;
1949 /* Extract message document into reallocated document structure
1950 * @context is ISDS context
1951 * @document is automically reallocated message documents structure
1952 * @xpath_ctx is XPath context with current node as isds:dmFile
1953 * In case of error @document will be freed. */
1954 static isds_error extract_document(struct isds_ctx *context,
1955 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
1956 isds_error err = IE_SUCCESS;
1957 xmlXPathObjectPtr result = NULL;
1958 xmlNodePtr file_node = xpath_ctx->node;
1959 char *string = NULL;
1961 if (!context) return IE_INVALID_CONTEXT;
1962 if (!document) return IE_INVAL;
1963 isds_document_free(document);
1964 if (!xpath_ctx) return IE_INVAL;
1966 *document = calloc(1, sizeof(**document));
1967 if (!*document) {
1968 err = IE_NOMEM;
1969 goto leave;
1972 /* Extract document metadata */
1973 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
1975 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
1976 err = string2isds_FileMetaType((xmlChar*)string,
1977 &((*document)->dmFileMetaType));
1978 if (err) {
1979 char *meta_type_locale = utf82locale(string);
1980 isds_printf_message(context,
1981 _("Document has invalid dmFileMetaType attribute value: %s"),
1982 meta_type_locale);
1983 free(meta_type_locale);
1984 err = IE_ISDS;
1985 goto leave;
1987 zfree(string);
1989 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
1990 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
1991 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
1992 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
1995 /* Extract document data.
1996 * Base64 encoded blob or XML subtree must be presented. */
1998 /* Check from dmEncodedContent */
1999 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
2000 xpath_ctx);
2001 if (!result) {
2002 err = IE_XML;
2003 goto leave;
2006 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2007 /* Here we have Base64 blob */
2009 if (result->nodesetval->nodeNr > 1) {
2010 isds_printf_message(context,
2011 _("Document has more dmEncodedContent elements"));
2012 err = IE_ISDS;
2013 goto leave;
2016 xmlXPathFreeObject(result); result = NULL;
2017 EXTRACT_STRING("isds:dmEncodedContent", string);
2019 /* Decode non-emptys document */
2020 if (string && string[0] != '\0') {
2021 (*document)->data_length = b64decode(string, &((*document)->data));
2022 if ((*document)->data_length == (size_t) -1) {
2023 isds_printf_message(context,
2024 _("Error while Base64-decoding document content"));
2025 err = IE_ERROR;
2026 goto leave;
2029 } else {
2030 /* No Base64 blob, try XML document */
2031 xmlXPathFreeObject(result); result = NULL;
2032 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
2033 xpath_ctx);
2034 if (!result) {
2035 err = IE_XML;
2036 goto leave;
2039 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2040 /* Here we have XML document */
2042 if (result->nodesetval->nodeNr > 1) {
2043 isds_printf_message(context,
2044 _("Document has more dmXMLContent elements"));
2045 err = IE_ISDS;
2046 goto leave;
2049 /* FIXME: Serialize the tree rooted at result's node */
2050 isds_printf_message(context,
2051 _("XML documents not yet supported"));
2052 err = IE_NOTSUP;
2053 goto leave;
2054 } else {
2055 /* No bas64 blob, nor XML document */
2056 isds_printf_message(context,
2057 _("Document has no dmEncodedContent, nor dmXMLContent "
2058 "element"));
2059 err = IE_ISDS;
2060 goto leave;
2065 leave:
2066 if (err) isds_document_free(document);
2067 free(string);
2068 xmlXPathFreeObject(result);
2069 xpath_ctx->node = file_node;
2070 return err;
2075 /* Extract message documents into reallocated list of documents
2076 * @context is ISDS context
2077 * @documents is automically reallocated message documents list structure
2078 * @xpath_ctx is XPath context with current node as XSD tFilesArray
2079 * In case of error @documents will be freed. */
2080 static isds_error extract_documents(struct isds_ctx *context,
2081 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
2082 isds_error err = IE_SUCCESS;
2083 xmlXPathObjectPtr result = NULL;
2084 xmlNodePtr file;
2085 xmlNodePtr files_node = xpath_ctx->node;
2086 struct isds_list *document, *prev_document;
2088 if (!context) return IE_INVALID_CONTEXT;
2089 if (!documents) return IE_INVAL;
2090 isds_list_free(documents);
2091 if (!xpath_ctx) return IE_INVAL;
2093 /* Find documents */
2094 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
2095 if (!result) {
2096 err = IE_XML;
2097 goto leave;
2100 /* No match */
2101 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2102 isds_printf_message(context,
2103 _("Message does not contain any document"));
2104 err = IE_ISDS;
2105 goto leave;
2109 /* Iterate over documents */
2110 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
2111 file = result->nodesetval->nodeTab[i];
2113 /* Allocate and append list item */
2114 document = calloc(1, sizeof(*document));
2115 if (!document) {
2116 err = IE_NOMEM;
2117 goto leave;
2119 document->destructor = (void (*)(void **))isds_document_free;
2120 if (i == 0) *documents = document;
2121 else prev_document->next = document;
2122 prev_document = document;
2124 /* Extract document */
2125 xpath_ctx->node = result->nodesetval->nodeTab[i];
2126 err = extract_document(context,
2127 (struct isds_document **) &(document->data), xpath_ctx);
2128 if (err) goto leave;
2132 leave:
2133 if (err) isds_list_free(documents);
2134 xmlXPathFreeObject(result);
2135 xpath_ctx->node = files_node;
2136 return err;
2140 /* Convert isds:dmRecord XML tree into structure
2141 * @context is ISDS context
2142 * @envelope is automically reallocated message envelope structure
2143 * @xpath_ctx is XPath context with current node as isds:dmRecord element
2144 * In case of error @envelope will be freed. */
2145 static isds_error extract_DmRecord(struct isds_ctx *context,
2146 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2147 isds_error err = IE_SUCCESS;
2148 xmlXPathObjectPtr result = NULL;
2150 if (!context) return IE_INVALID_CONTEXT;
2151 if (!envelope) return IE_INVAL;
2152 isds_envelope_free(envelope);
2153 if (!xpath_ctx) return IE_INVAL;
2156 *envelope = calloc(1, sizeof(**envelope));
2157 if (!*envelope) {
2158 err = IE_NOMEM;
2159 goto leave;
2163 /* Extract tRecord data */
2164 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
2166 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
2167 * dmAcceptanceTime. */
2168 err = append_status_size_times(context, envelope, xpath_ctx);
2169 if (err) goto leave;
2171 /* Extract envelope elements added by sender and ISDS
2172 * (XSD: gMessageEnvelope type) */
2173 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
2174 if (err) goto leave;
2175 /* dmOVM can not be obtained from ISDS */
2177 leave:
2178 if (err) isds_envelope_free(envelope);
2179 xmlXPathFreeObject(result);
2180 return err;
2184 /* Find and convert isds:dmHash XML tree into structure
2185 * @context is ISDS context
2186 * @envelope is automically reallocated message hash structure
2187 * @xpath_ctx is XPath context with current node containing isds:dmHash child
2188 * In case of error @hash will be freed. */
2189 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
2190 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
2191 isds_error err = IE_SUCCESS;
2192 xmlNodePtr old_ctx_node;
2193 xmlXPathObjectPtr result = NULL;
2194 char *string = NULL;
2196 if (!context) return IE_INVALID_CONTEXT;
2197 if (!hash) return IE_INVAL;
2198 isds_hash_free(hash);
2199 if (!xpath_ctx) return IE_INVAL;
2202 *hash = calloc(1, sizeof(**hash));
2203 if (!*hash) {
2204 err = IE_NOMEM;
2205 goto leave;
2208 old_ctx_node = xpath_ctx->node;
2210 /* Locate dmHash */
2211 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
2212 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
2213 err = IE_ISDS;
2214 goto leave;
2216 if (err) {
2217 err = IE_ERROR;
2218 goto leave;
2221 /* Get hash algorithm */
2222 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
2223 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
2224 if (err) {
2225 if (err == IE_ENUM) {
2226 char *string_locale = utf82locale(string);
2227 isds_printf_message(context, _("Unsported hash algorithm: %s"),
2228 string_locale);
2229 free(string_locale);
2231 goto leave;
2233 zfree(string);
2235 /* Get hash value */
2236 EXTRACT_STRING(".", string);
2237 if (!string) {
2238 isds_printf_message(context, _("tHash element is missing hash value"));
2239 err = IE_ISDS;
2240 goto leave;
2242 (*hash)->length = b64decode(string, &((*hash)->value));
2243 if ((*hash)->length == (size_t) -1) {
2244 isds_printf_message(context,
2245 _("Error while Base64-decoding hash value"));
2246 err = IE_ERROR;
2247 goto leave;
2250 leave:
2251 if (err) isds_hash_free(hash);
2252 free(string);
2253 xmlXPathFreeObject(result);
2254 xpath_ctx->node = old_ctx_node;
2255 return err;
2259 /* Find and append isds:dmQTimestamp XML tree into envelope
2260 * @context is ISDS context
2261 * @envelope is automically allocated evnelope structure
2262 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
2263 * child
2264 * In case of error @envelope will be freed. */
2265 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
2266 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2267 isds_error err = IE_SUCCESS;
2268 xmlXPathObjectPtr result = NULL;
2269 char *string = NULL;
2271 if (!context) return IE_INVALID_CONTEXT;
2272 if (!envelope) return IE_INVAL;
2273 if (!xpath_ctx) {
2274 isds_envelope_free(envelope);
2275 return IE_INVAL;
2278 if (!*envelope) {
2279 *envelope = calloc(1, sizeof(**envelope));
2280 if (!*envelope) {
2281 err = IE_NOMEM;
2282 goto leave;
2284 } else {
2285 zfree((*envelope)->timestamp);
2286 (*envelope)->timestamp_length = 0;
2289 /* Get dmQTimestamp */
2290 EXTRACT_STRING("sisds:dmQTimestamp", string);
2291 if (!string) {
2292 isds_printf_message(context, _("Missing dmQTimestamp element content"));
2293 err = IE_ISDS;
2294 goto leave;
2296 (*envelope)->timestamp_length =
2297 b64decode(string, &((*envelope)->timestamp));
2298 if ((*envelope)->timestamp_length == (size_t) -1) {
2299 isds_printf_message(context,
2300 _("Error while Base64-decoding timestamp value"));
2301 err = IE_ERROR;
2302 goto leave;
2305 leave:
2306 if (err) isds_envelope_free(envelope);
2307 free(string);
2308 xmlXPathFreeObject(result);
2309 return err;
2313 /* Convert XSD tReturnedMessage XML tree into message structure.
2314 * It doea not store XML tree into message->raw.
2315 * @context is ISDS context
2316 * @include_documents Use true if documents must be extracted
2317 * (tReturnedMessage XSD type), use false if documents shall be ommited
2318 * (tReturnedMessageEnvelope).
2319 * @message is automically reallocated message structure
2320 * @xpath_ctx is XPath context with current node as tReturnedMessage element
2321 * type
2322 * In case of error @message will be freed. */
2323 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
2324 const _Bool include_documents, struct isds_message **message,
2325 xmlXPathContextPtr xpath_ctx) {
2326 isds_error err = IE_SUCCESS;
2327 xmlNodePtr message_node;
2329 if (!context) return IE_INVALID_CONTEXT;
2330 if (!message) return IE_INVAL;
2331 isds_message_free(message);
2332 if (!xpath_ctx) return IE_INVAL;
2335 *message = calloc(1, sizeof(**message));
2336 if (!*message) {
2337 err = IE_NOMEM;
2338 goto leave;
2341 /* Save message XPATH context node */
2342 message_node = xpath_ctx->node;
2345 /* Extract dmDM */
2346 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
2347 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
2348 if (err) { err = IE_ERROR; goto leave; }
2349 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
2350 if (err) goto leave;
2352 if (include_documents) {
2353 /* Extract dmFiles */
2354 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
2355 xpath_ctx);
2356 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
2357 err = IE_ISDS; goto leave;
2359 if (err) { err = IE_ERROR; goto leave; }
2360 err = extract_documents(context, &((*message)->documents), xpath_ctx);
2361 if (err) goto leave;
2365 /* Restore context to message */
2366 xpath_ctx->node = message_node;
2368 /* Extract dmHash */
2369 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
2370 xpath_ctx);
2371 if (err) goto leave;
2373 /* Extract dmQTimestamp, */
2374 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
2375 xpath_ctx);
2376 if (err) goto leave;
2378 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
2379 * dmAcceptanceTime. */
2380 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
2381 if (err) goto leave;
2383 leave:
2384 if (err) isds_message_free(message);
2385 return err;
2389 /* Convert isds_document structure into XML tree and append to dmFiles node.
2390 * @context is session context
2391 * @document is ISDS document
2392 * @dm_files is XML element the resulting tree will be appended to as a child.
2393 * @return error code, in case of error context' message is filled. */
2394 static isds_error insert_document(struct isds_ctx *context,
2395 struct isds_document *document, xmlNodePtr dm_files) {
2396 isds_error err = IE_SUCCESS;
2397 xmlNodePtr new_file = NULL, file = NULL, node;
2398 xmlAttrPtr attribute_node;
2399 xmlChar *base64data = NULL;
2401 if (!context) return IE_INVALID_CONTEXT;
2402 if (!document || !dm_files) return IE_INVAL;
2404 /* Allocate new dmFile */
2405 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
2406 if (!new_file) {
2407 isds_printf_message(context, _("Could not allocate main dmFile"));
2408 err = IE_ERROR;
2409 goto leave;
2411 /* Append the new dmFile.
2412 * XXX: Main document must go first */
2413 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
2414 file = xmlAddPrevSibling(dm_files->children, new_file);
2415 else
2416 file = xmlAddChild(dm_files, new_file);
2418 if (!file) {
2419 xmlFreeNode(new_file); new_file = NULL;
2420 isds_printf_message(context, _("Could not add dmFile child to "
2421 "%s element"), dm_files->name);
2422 err = IE_ERROR;
2423 goto leave;
2426 /* @dmMimeType is required */
2427 if (!document->dmMimeType) {
2428 isds_log_message(context,
2429 _("Document is missing mandatory MIME type definition"));
2430 err = IE_INVAL;
2431 goto leave;
2433 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
2435 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
2436 if (!string) {
2437 isds_printf_message(context,
2438 _("Document has unkown dmFileMetaType: %ld"),
2439 document->dmFileMetaType);
2440 err = IE_ENUM;
2441 goto leave;
2443 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
2445 if (document->dmFileGuid) {
2446 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
2448 if (document->dmUpFileGuid) {
2449 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
2452 /* @dmFileDescr is required */
2453 if (!document->dmFileDescr) {
2454 isds_log_message(context,
2455 _("Document is missing mandatory description (title)"));
2456 err = IE_INVAL;
2457 goto leave;
2459 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
2461 if (document->dmFormat) {
2462 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
2466 /* Insert content (data) of the document. */
2467 /* XXX; Only base64 is implemented currently. */
2468 base64data = (xmlChar *) b64encode(document->data, document->data_length);
2469 if (!base64data) {
2470 isds_printf_message(context,
2471 _("Not enought memory to encode %zd bytes into Base64"),
2472 document->data_length);
2473 err = IE_NOMEM;
2474 goto leave;
2476 INSERT_STRING(file, "dmEncodedContent", base64data);
2477 free(base64data);
2479 leave:
2480 return err;
2484 /* Get data about logged in user and his box. */
2485 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
2486 struct isds_DbOwnerInfo **db_owner_info) {
2487 isds_error err = IE_SUCCESS;
2488 xmlNsPtr isds_ns = NULL;
2489 xmlNodePtr request = NULL;
2490 xmlDocPtr response = NULL;
2491 xmlChar *code = NULL, *message = NULL;
2492 xmlNodePtr node;
2493 xmlXPathContextPtr xpath_ctx = NULL;
2494 xmlXPathObjectPtr result = NULL;
2495 char *string = NULL;
2497 if (!context) return IE_INVALID_CONTEXT;
2498 if (!db_owner_info) return IE_INVAL;
2500 /* Check if connection is established */
2501 if (!context->curl) return IE_CONNECTION_CLOSED;
2504 /* Build GetOwnerInfoFromLogin request */
2505 request = xmlNewNode(NULL, BAD_CAST "GetOwnerInfoFromLogin");
2506 if (!request) {
2507 isds_log_message(context,
2508 _("Could build GetOwnerInfoFromLogin request"));
2509 return IE_ERROR;
2511 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
2512 if(!isds_ns) {
2513 isds_log_message(context, _("Could not create ISDS name space"));
2514 xmlFreeNode(request);
2515 return IE_ERROR;
2517 xmlSetNs(request, isds_ns);
2518 node = xmlNewChild(request, NULL, BAD_CAST "dbDummy", NULL);
2519 if (!node) {
2520 isds_log_message(context, _("Could nod add dbDummy Child to "
2521 "GetOwnerInfoFromLogin element"));
2522 xmlFreeNode(request);
2523 return IE_ERROR;
2527 isds_log(ILF_ISDS, ILL_DEBUG,
2528 _("Sending GetOwnerInfoFromLogin request to ISDS\n"));
2530 /* Sent request */
2531 err = isds(context, SERVICE_DB_SUPPLEMENTARY, request, &response);
2533 /* Destroy request */
2534 xmlFreeNode(request);
2536 if (err) {
2537 isds_log(ILF_ISDS, ILL_DEBUG,
2538 _("Processing ISDS response on GetOwnerInfoFromLogin "
2539 "request failed\n"));
2540 xmlFreeDoc(response);
2541 return err;
2544 /* Check for response status */
2545 err = isds_response_status(context, SERVICE_DB_SUPPLEMENTARY, response,
2546 &code, &message, NULL);
2547 if (err) {
2548 isds_log(ILF_ISDS, ILL_DEBUG,
2549 _("ISDS response on GetOwnerInfoFromLogin request is "
2550 "missing status\n"));
2551 free(code);
2552 free(message);
2553 xmlFreeDoc(response);
2554 return err;
2556 if (xmlStrcmp(code, BAD_CAST "0000")) {
2557 char *code_locale = utf82locale((char*)code);
2558 char *message_locale = utf82locale((char*)message);
2559 isds_log(ILF_ISDS, ILL_DEBUG,
2560 _("Server refused GetOwnerInfoFromLogin request "
2561 "(code=%s, message=%s)\n"), code_locale, message_locale);
2562 isds_log_message(context, message_locale);
2563 free(code_locale);
2564 free(message_locale);
2565 free(code);
2566 free(message);
2567 xmlFreeDoc(response);
2568 return IE_ISDS;
2571 /* Extract data */
2572 /* Prepare stucture */
2573 isds_DbOwnerInfo_free(db_owner_info);
2574 *db_owner_info = calloc(1, sizeof(**db_owner_info));
2575 if (!*db_owner_info) {
2576 err = IE_NOMEM;
2577 goto leave;
2579 xpath_ctx = xmlXPathNewContext(response);
2580 if (!xpath_ctx) {
2581 err = IE_ERROR;
2582 goto leave;
2584 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
2585 err = IE_ERROR;
2586 goto leave;
2589 /* Set context node */
2590 result = xmlXPathEvalExpression(BAD_CAST
2591 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
2592 if (!result) {
2593 err = IE_ERROR;
2594 goto leave;
2596 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2597 isds_log_message(context, _("Missing dbOwnerInfo element"));
2598 err = IE_ISDS;
2599 goto leave;
2601 if (result->nodesetval->nodeNr > 1) {
2602 isds_log_message(context, _("Multiple dbOwnerInfo element"));
2603 err = IE_ISDS;
2604 goto leave;
2606 xpath_ctx->node = result->nodesetval->nodeTab[0];
2607 xmlXPathFreeObject(result); result = NULL;
2609 /* Extract it */
2610 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
2612 leave:
2613 if (err) {
2614 isds_DbOwnerInfo_free(db_owner_info);
2617 free(string);
2618 xmlXPathFreeObject(result);
2619 xmlXPathFreeContext(xpath_ctx);
2621 free(code);
2622 free(message);
2623 xmlFreeDoc(response);
2625 if (!err)
2626 isds_log(ILF_ISDS, ILL_DEBUG,
2627 _("GetOwnerInfoFromLogin request processed by server "
2628 "successfully.\n"));
2630 return err;
2634 /* Find boxes suiting given criteria.
2635 * @criteria is filter. You should fill in at least some memebers.
2636 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
2637 * possibly empty. Input NULL or valid old structure.
2638 * @return:
2639 * IE_SUCCESS if search sucseeded, @boxes contains usefull data
2640 * IE_NOEXIST if no such box exists, @boxes will be NULL
2641 * IE_2BIG if too much boxes exist and server truncated the resuluts, @boxes
2642 * contains still valid data
2643 * other code if something bad happens. @boxes will be NULL. */
2644 isds_error isds_FindDataBox(struct isds_ctx *context,
2645 const struct isds_DbOwnerInfo *criteria,
2646 struct isds_list **boxes) {
2647 isds_error err = IE_SUCCESS;
2648 _Bool truncated = 0;
2649 xmlNsPtr isds_ns = NULL;
2650 xmlNodePtr request = NULL;
2651 xmlDocPtr response = NULL;
2652 xmlChar *code = NULL, *message = NULL;
2653 xmlNodePtr db_owner_info, node;
2654 xmlXPathContextPtr xpath_ctx = NULL;
2655 xmlXPathObjectPtr result = NULL;
2656 xmlChar *string = NULL;
2659 if (!context) return IE_INVALID_CONTEXT;
2660 if (!boxes) return IE_INVAL;
2661 isds_list_free(boxes);
2663 if (!criteria) {
2664 return IE_INVAL;
2667 /* Check if connection is established
2668 * TODO: This check should be done donwstairs. */
2669 if (!context->curl) return IE_CONNECTION_CLOSED;
2672 /* Build FindDataBox request */
2673 request = xmlNewNode(NULL, BAD_CAST "FindDataBox");
2674 if (!request) {
2675 isds_log_message(context,
2676 _("Could build FindDataBox request"));
2677 return IE_ERROR;
2679 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
2680 if(!isds_ns) {
2681 isds_log_message(context, _("Could not create ISDS name space"));
2682 xmlFreeNode(request);
2683 return IE_ERROR;
2685 xmlSetNs(request, isds_ns);
2686 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
2687 if (!db_owner_info) {
2688 isds_log_message(context, _("Could not add dbOwnerInfo Child to "
2689 "FindDataBox element"));
2690 xmlFreeNode(request);
2691 return IE_ERROR;
2695 INSERT_STRING(db_owner_info, "dbID", criteria->dbID);
2697 /* dbType */
2698 if (criteria->dbType) {
2699 const xmlChar *type_string = isds_DbType2string(*(criteria->dbType));
2700 if (!type_string) {
2701 isds_printf_message(context, _("Invalid dbType value: %d"),
2702 *(criteria->dbType));
2703 err = IE_ENUM;
2704 goto leave;
2706 INSERT_STRING(db_owner_info, "dbType", type_string);
2709 INSERT_STRING(db_owner_info, "firmName", criteria->firmName);
2710 INSERT_STRING(db_owner_info, "ic", criteria->ic);
2711 if (criteria->personName) {
2712 INSERT_STRING(db_owner_info, "pnFirstName",
2713 criteria->personName->pnFirstName);
2714 INSERT_STRING(db_owner_info, "pnMiddleName",
2715 criteria->personName->pnMiddleName);
2716 INSERT_STRING(db_owner_info, "pnLastName",
2717 criteria->personName->pnLastName);
2718 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
2719 criteria->personName->pnLastNameAtBirth);
2721 if (criteria->birthInfo) {
2722 if (criteria->birthInfo->biDate) {
2723 if (!tm2datestring(criteria->birthInfo->biDate, &string))
2724 INSERT_STRING(db_owner_info, "biDate", string);
2725 free(string); string = NULL;
2727 INSERT_STRING(db_owner_info, "biCity", criteria->birthInfo->biCity);
2728 INSERT_STRING(db_owner_info, "biCounty", criteria->birthInfo->biCounty);
2729 INSERT_STRING(db_owner_info, "biState", criteria->birthInfo->biState);
2731 if (criteria->address) {
2732 INSERT_STRING(db_owner_info, "adCity", criteria->address->adCity);
2733 INSERT_STRING(db_owner_info, "adStreet", criteria->address->adStreet);
2734 INSERT_STRING(db_owner_info, "adNumberInStreet",
2735 criteria->address->adNumberInStreet);
2736 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
2737 criteria->address->adNumberInMunicipality);
2738 INSERT_STRING(db_owner_info, "adZipCode", criteria->address->adZipCode);
2739 INSERT_STRING(db_owner_info, "adState", criteria->address->adState);
2741 INSERT_STRING(db_owner_info, "nationality", criteria->nationality);
2742 INSERT_STRING(db_owner_info, "email", criteria->email);
2743 INSERT_STRING(db_owner_info, "telNumber", criteria->telNumber);
2744 INSERT_STRING(db_owner_info, "identifier", criteria->identifier);
2745 INSERT_STRING(db_owner_info, "registryCode", criteria->registryCode);
2747 INSERT_LONGINT(db_owner_info, "dbState", criteria->dbState, string);
2749 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", criteria->dbEffectiveOVM);
2750 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
2751 criteria->dbOpenAddressing);
2754 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending FindDataBox request to ISDS\n"));
2756 /* Sent request */
2757 err = isds(context, SERVICE_DB_SEARCH, request, &response);
2759 /* Destroy request */
2760 xmlFreeNode(request); request = NULL;
2762 if (err) {
2763 isds_log(ILF_ISDS, ILL_DEBUG,
2764 _("Processing ISDS response on FindDataBox "
2765 "request failed\n"));
2766 goto leave;
2769 /* Check for response status */
2770 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
2771 &code, &message, NULL);
2772 if (err) {
2773 isds_log(ILF_ISDS, ILL_DEBUG,
2774 _("ISDS response on FindDataBox request is missing status\n"));
2775 goto leave;
2778 /* Request processed, but nothing found */
2779 if (!xmlStrcmp(code, BAD_CAST "0002") ||
2780 !xmlStrcmp(code, BAD_CAST "5001")) {
2781 char *code_locale = utf82locale((char*)code);
2782 char *message_locale = utf82locale((char*)message);
2783 isds_log(ILF_ISDS, ILL_DEBUG,
2784 _("Server did not found any box on FindDataBox request "
2785 "(code=%s, message=%s)\n"), code_locale, message_locale);
2786 isds_log_message(context, message_locale);
2787 free(code_locale);
2788 free(message_locale);
2789 err = IE_NOEXIST;
2790 goto leave;
2793 /* Warning, not a error */
2794 if (!xmlStrcmp(code, BAD_CAST "0003")) {
2795 char *code_locale = utf82locale((char*)code);
2796 char *message_locale = utf82locale((char*)message);
2797 isds_log(ILF_ISDS, ILL_DEBUG,
2798 _("Server truncated response on FindDataBox request "
2799 "(code=%s, message=%s)\n"), code_locale, message_locale);
2800 isds_log_message(context, message_locale);
2801 free(code_locale);
2802 free(message_locale);
2803 truncated = 1;
2806 /* Other error */
2807 else if (xmlStrcmp(code, BAD_CAST "0000")) {
2808 char *code_locale = utf82locale((char*)code);
2809 char *message_locale = utf82locale((char*)message);
2810 isds_log(ILF_ISDS, ILL_DEBUG,
2811 _("Server refused FindDataBox request "
2812 "(code=%s, message=%s)\n"), code_locale, message_locale);
2813 isds_log_message(context, message_locale);
2814 free(code_locale);
2815 free(message_locale);
2816 err = IE_ISDS;
2817 goto leave;
2820 xpath_ctx = xmlXPathNewContext(response);
2821 if (!xpath_ctx) {
2822 err = IE_ERROR;
2823 goto leave;
2825 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
2826 err = IE_ERROR;
2827 goto leave;
2830 /* Extract boxes if they present */
2831 result = xmlXPathEvalExpression(BAD_CAST
2832 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
2833 xpath_ctx);
2834 if (!result) {
2835 err = IE_ERROR;
2836 goto leave;
2838 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2839 struct isds_list *item, *prev_item = NULL;
2840 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
2841 item = calloc(1, sizeof(*item));
2842 if (!item) {
2843 err = IE_NOMEM;
2844 goto leave;
2847 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
2848 if (i == 0) *boxes = item;
2849 else prev_item->next = item;
2850 prev_item = item;
2852 xpath_ctx->node = result->nodesetval->nodeTab[i];
2853 err = extract_DbOwnerInfo(context,
2854 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
2855 if (err) goto leave;
2859 leave:
2860 if (err) {
2861 isds_list_free(boxes);
2862 } else {
2863 if (truncated) err = IE_2BIG;
2866 free(string);
2867 xmlFreeNode(request);
2868 xmlXPathFreeObject(result);
2869 xmlXPathFreeContext(xpath_ctx);
2871 free(code);
2872 free(message);
2873 xmlFreeDoc(response);
2875 if (!err)
2876 isds_log(ILF_ISDS, ILL_DEBUG,
2877 _("FindDataBox request processed by server successfully.\n"));
2879 return err;
2883 /* Get status of a box.
2884 * @context is ISDS session context.
2885 * @box_id is UTF-8 encoded box identifier as zero terminated string
2886 * @box_status is return value of box status.
2887 * @return:
2888 * IE_SUCCESS if box has been found and its status retrieved
2889 * IE_NOEXIST if box is not known to ISDS server
2890 * or other appropriate error.
2891 * You can use isds_DbState to enumerate box status. However out of enum
2892 * range value can be returned too. This is feature because ISDS
2893 * specification leaves the set of values open.
2894 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
2895 * the box has been deleted, but ISDS still lists its former existence. */
2896 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
2897 long int *box_status) {
2898 isds_error err = IE_SUCCESS;
2899 xmlNsPtr isds_ns = NULL;
2900 xmlNodePtr request = NULL, db_id;
2901 xmlDocPtr response = NULL;
2902 xmlChar *code = NULL, *message = NULL;
2903 xmlXPathContextPtr xpath_ctx = NULL;
2904 xmlXPathObjectPtr result = NULL;
2905 xmlChar *string = NULL;
2907 if (!context) return IE_INVALID_CONTEXT;
2908 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
2910 /* Check if connection is established
2911 * TODO: This check should be done donwstairs. */
2912 if (!context->curl) return IE_CONNECTION_CLOSED;
2915 /* Build CheckDataBox request */
2916 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
2917 if (!request) {
2918 isds_log_message(context,
2919 _("Could build CheckDataBox request"));
2920 return IE_ERROR;
2922 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
2923 if(!isds_ns) {
2924 isds_log_message(context, _("Could not create ISDS name space"));
2925 xmlFreeNode(request);
2926 return IE_ERROR;
2928 xmlSetNs(request, isds_ns);
2929 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
2930 if (!db_id) {
2931 isds_log_message(context, _("Could not add dbId Child to "
2932 "CheckDataBox element"));
2933 xmlFreeNode(request);
2934 return IE_ERROR;
2938 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CheckDataBox request to ISDS\n"));
2940 /* Sent request */
2941 err = isds(context, SERVICE_DB_SEARCH, request, &response);
2943 /* Destroy request */
2944 xmlFreeNode(request);
2946 if (err) {
2947 isds_log(ILF_ISDS, ILL_DEBUG,
2948 _("Processing ISDS response on CheckDataBox "
2949 "request failed\n"));
2950 goto leave;
2953 /* Check for response status */
2954 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
2955 &code, &message, NULL);
2956 if (err) {
2957 isds_log(ILF_ISDS, ILL_DEBUG,
2958 _("ISDS response on CheckDataBox request is missing status\n"));
2959 goto leave;
2962 /* Request processed, but nothing found */
2963 if (!xmlStrcmp(code, BAD_CAST "5001")) {
2964 char *box_id_locale = utf82locale((char*)box_id);
2965 char *code_locale = utf82locale((char*)code);
2966 char *message_locale = utf82locale((char*)message);
2967 isds_log(ILF_ISDS, ILL_DEBUG,
2968 _("Server did not found box %s on CheckDataBox request "
2969 "(code=%s, message=%s)\n"),
2970 box_id_locale, code_locale, message_locale);
2971 isds_log_message(context, message_locale);
2972 free(box_id_locale);
2973 free(code_locale);
2974 free(message_locale);
2975 err = IE_NOEXIST;
2976 goto leave;
2979 /* Other error */
2980 else if (xmlStrcmp(code, BAD_CAST "0000")) {
2981 char *code_locale = utf82locale((char*)code);
2982 char *message_locale = utf82locale((char*)message);
2983 isds_log(ILF_ISDS, ILL_DEBUG,
2984 _("Server refused CheckDataBox request "
2985 "(code=%s, message=%s)\n"), code_locale, message_locale);
2986 isds_log_message(context, message_locale);
2987 free(code_locale);
2988 free(message_locale);
2989 err = IE_ISDS;
2990 goto leave;
2993 /* Extract data */
2994 xpath_ctx = xmlXPathNewContext(response);
2995 if (!xpath_ctx) {
2996 err = IE_ERROR;
2997 goto leave;
2999 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
3000 err = IE_ERROR;
3001 goto leave;
3003 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
3004 xpath_ctx);
3005 if (!result) {
3006 err = IE_ERROR;
3007 goto leave;
3009 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3010 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
3011 err = IE_ISDS;
3012 goto leave;
3014 if (result->nodesetval->nodeNr > 1) {
3015 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
3016 err = IE_ISDS;
3017 goto leave;
3019 xpath_ctx->node = result->nodesetval->nodeTab[0];
3020 xmlXPathFreeObject(result); result = NULL;
3022 EXTRACT_LONGINT("isds:dbState", box_status, 1);
3025 leave:
3026 free(string);
3027 xmlXPathFreeObject(result);
3028 xmlXPathFreeContext(xpath_ctx);
3030 free(code);
3031 free(message);
3032 xmlFreeDoc(response);
3034 if (!err)
3035 isds_log(ILF_ISDS, ILL_DEBUG,
3036 _("CheckDataBox request processed by server successfully.\n"));
3038 return err;
3042 /* Send a message via ISDS to a recipent
3043 * @context is session context
3044 * @outgoing_message is message to send; Some memebers are mandatory (like
3045 * dbIDRecipient), some are optional and some are irrelevant (especialy data
3046 * about sender). Included pointer to isds_list documents must contain at
3047 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
3048 * members will be filled with valid data from ISDS. Exact list of write
3049 * members is subject to change. Currently dmId is changed.
3050 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
3051 isds_error isds_send_message(struct isds_ctx *context,
3052 struct isds_message *outgoing_message) {
3054 isds_error err = IE_SUCCESS;
3055 xmlNsPtr isds_ns = NULL;
3056 xmlNodePtr request = NULL, envelope, dm_files, node;
3057 xmlDocPtr response = NULL;
3058 xmlChar *code = NULL, *message = NULL;
3059 xmlXPathContextPtr xpath_ctx = NULL;
3060 xmlXPathObjectPtr result = NULL;
3061 xmlChar *string = NULL;
3062 _Bool message_is_complete = 0;
3064 if (!context) return IE_INVALID_CONTEXT;
3065 if (!outgoing_message) return IE_INVAL;
3067 /* Check if connection is established
3068 * TODO: This check should be done donwstairs. */
3069 if (!context->curl) return IE_CONNECTION_CLOSED;
3072 /* Build CreateMessage request */
3073 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
3074 if (!request) {
3075 isds_log_message(context,
3076 _("Could build CreateMessage request"));
3077 return IE_ERROR;
3079 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
3080 if(!isds_ns) {
3081 isds_log_message(context, _("Could not create ISDS name space"));
3082 xmlFreeNode(request);
3083 return IE_ERROR;
3085 xmlSetNs(request, isds_ns);
3088 /* Build envelope */
3089 envelope = xmlNewChild(request, NULL, BAD_CAST "dmEnvelope", NULL);
3090 if (!envelope) {
3091 isds_log_message(context, _("Could not add dmEnvelope child to "
3092 "CreateMessage element"));
3093 xmlFreeNode(request);
3094 return IE_ERROR;
3097 if (!outgoing_message->envelope) {
3098 isds_log_message(context, _("outgoing message is missing envelope"));
3099 err = IE_INVAL;
3100 goto leave;
3103 INSERT_STRING(envelope, "dmSenderOrgUnit",
3104 outgoing_message->envelope->dmSenderOrgUnit);
3105 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
3106 outgoing_message->envelope->dmSenderOrgUnitNum, string);
3108 if (!outgoing_message->envelope->dbIDRecipient) {
3109 isds_log_message(context,
3110 _("outgoing message is missing recipient box identifier"));
3111 err = IE_INVAL;
3112 goto leave;
3114 INSERT_STRING(envelope, "dbIDRecipient",
3115 outgoing_message->envelope->dbIDRecipient);
3117 INSERT_STRING(envelope, "dmRecipientOrgUnit",
3118 outgoing_message->envelope->dmRecipientOrgUnit);
3119 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
3120 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
3121 INSERT_STRING(envelope, "dmToHands", outgoing_message->envelope->dmToHands);
3123 #define CHECK_FOR_STRING_LENGTH(string, limit, name) \
3124 if ((string) && xmlUTF8Strlen((xmlChar *) (string)) > (limit)) { \
3125 isds_printf_message(context, \
3126 _("%s has more than %d characters"), (name), (limit)); \
3127 err = IE_2BIG; \
3128 goto leave; \
3131 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 255,
3132 "dmAnnotation");
3133 INSERT_STRING(envelope, "dmAnnotation",
3134 outgoing_message->envelope->dmAnnotation);
3136 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
3137 50, "dmRecipientRefNumber");
3138 INSERT_STRING(envelope, "dmRecipientRefNumber",
3139 outgoing_message->envelope->dmRecipientRefNumber);
3141 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
3142 50, "dmSenderRefNumber");
3143 INSERT_STRING(envelope, "dmSenderRefNumber",
3144 outgoing_message->envelope->dmSenderRefNumber);
3146 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
3147 50, "dmRecipientIdent");
3148 INSERT_STRING(envelope, "dmRecipientIdent",
3149 outgoing_message->envelope->dmRecipientIdent);
3151 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
3152 50, "dmSenderIdent");
3153 INSERT_STRING(envelope, "dmSenderIdent",
3154 outgoing_message->envelope->dmSenderIdent);
3156 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
3157 outgoing_message->envelope->dmLegalTitleLaw, string);
3158 INSERT_LONGINT(envelope, "dmLegalTitleYear",
3159 outgoing_message->envelope->dmLegalTitleYear, string);
3160 INSERT_STRING(envelope, "dmLegalTitleSect",
3161 outgoing_message->envelope->dmLegalTitleSect);
3162 INSERT_STRING(envelope, "dmLegalTitlePar",
3163 outgoing_message->envelope->dmLegalTitlePar);
3164 INSERT_STRING(envelope, "dmLegalTitlePoint",
3165 outgoing_message->envelope->dmLegalTitlePoint);
3167 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
3168 outgoing_message->envelope->dmPersonalDelivery);
3169 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
3170 outgoing_message->envelope->dmAllowSubstDelivery);
3172 #undef CHECK_FOR_STRING_LENGTH
3174 /* ???: Should we require value for dbEffectiveOVM sender?
3175 * ISDS has default as true */
3176 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
3179 /* Append dmFiles */
3180 if (!outgoing_message->documents) {
3181 isds_log_message(context,
3182 _("outgoing message is missing list of documents"));
3183 err = IE_INVAL;
3184 goto leave;
3186 dm_files = xmlNewChild(request, NULL, BAD_CAST "dmFiles", NULL);
3187 if (!dm_files) {
3188 isds_log_message(context, _("Could not add dmFiles child to "
3189 "CreateMessage element"));
3190 err = IE_ERROR;
3191 goto leave;
3194 /* Check for document hieararchy */
3195 err = check_documents_hierarchy(context, outgoing_message->documents);
3196 if (err) goto leave;
3198 /* Process each document */
3199 for (struct isds_list *item =
3200 (struct isds_list *) outgoing_message->documents;
3201 item; item = item->next) {
3202 if (!item->data) {
3203 isds_log_message(context,
3204 _("list of documents contains empty item"));
3205 err = IE_INVAL;
3206 goto leave;
3208 /* FIXME: Check for dmFileMetaType and for document references.
3209 * Only first document can be of MAIN type */
3210 err = insert_document(context, (struct isds_document*) item->data,
3211 dm_files);
3213 if (err) goto leave;
3216 /* Signal we can serilize message since now */
3217 message_is_complete = 1;
3221 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
3223 /* Sent request */
3224 err = isds(context, SERVICE_DM_OPERATIONS, request, &response);
3226 /* Dont' destroy request, we want to privode it to application later */
3228 if (err) {
3229 isds_log(ILF_ISDS, ILL_DEBUG,
3230 _("Processing ISDS response on CreateMessage "
3231 "request failed\n"));
3232 goto leave;
3235 /* Check for response status */
3236 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
3237 &code, &message, NULL);
3238 if (err) {
3239 isds_log(ILF_ISDS, ILL_DEBUG,
3240 _("ISDS response on CreateMessage request "
3241 "is missing status\n"));
3242 goto leave;
3245 /* Request processed, but nothing found */
3246 if (xmlStrcmp(code, BAD_CAST "0000")) {
3247 char *box_id_locale =
3248 utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
3249 char *code_locale = utf82locale((char*)code);
3250 char *message_locale = utf82locale((char*)message);
3251 isds_log(ILF_ISDS, ILL_DEBUG,
3252 _("Server did not accept message for %s on CreateMessage "
3253 "request (code=%s, message=%s)\n"),
3254 box_id_locale, code_locale, message_locale);
3255 isds_log_message(context, message_locale);
3256 free(box_id_locale);
3257 free(code_locale);
3258 free(message_locale);
3259 err = IE_ISDS;
3260 goto leave;
3264 /* Extract data */
3265 xpath_ctx = xmlXPathNewContext(response);
3266 if (!xpath_ctx) {
3267 err = IE_ERROR;
3268 goto leave;
3270 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
3271 err = IE_ERROR;
3272 goto leave;
3274 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
3275 xpath_ctx);
3276 if (!result) {
3277 err = IE_ERROR;
3278 goto leave;
3280 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3281 isds_log_message(context, _("Missing CreateMessageResponse element"));
3282 err = IE_ISDS;
3283 goto leave;
3285 if (result->nodesetval->nodeNr > 1) {
3286 isds_log_message(context, _("Multiple CreateMessageResponse element"));
3287 err = IE_ISDS;
3288 goto leave;
3290 xpath_ctx->node = result->nodesetval->nodeTab[0];
3291 xmlXPathFreeObject(result); result = NULL;
3293 if (outgoing_message->envelope->dmID) {
3294 free(outgoing_message->envelope->dmID);
3295 outgoing_message->envelope->dmID = NULL;
3297 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
3298 if (!outgoing_message->envelope->dmID) {
3299 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
3300 "but did not returen assigned message ID\n"));
3303 leave:
3304 /* TODO: Serialize message into structure member raw */
3305 /* XXX: Each web service transport message in different format.
3306 * Therefore it's not possible to save them directly.
3307 * To save them, one must figure out common format.
3308 * We can leave it on application, or we can implement the ESS format. */
3309 /*if (message_is_complete) {
3310 if (outgoing_message->envelope->dmID) {
3312 /* Add assigned message ID as first child*/
3313 /*xmlNodePtr dmid_text = xmlNewText(
3314 (xmlChar *) outgoing_message->envelope->dmID);
3315 if (!dmid_text) goto serialization_failed;
3317 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
3318 BAD_CAST "dmID");
3319 if (!dmid_element) {
3320 xmlFreeNode(dmid_text);
3321 goto serialization_failed;
3324 xmlNodePtr dmid_element_with_text =
3325 xmlAddChild(dmid_element, dmid_text);
3326 if (!dmid_element_with_text) {
3327 xmlFreeNode(dmid_element);
3328 xmlFreeNode(dmid_text);
3329 goto serialization_failed;
3332 node = xmlAddPrevSibling(envelope->childern,
3333 dmid_element_with_text);
3334 if (!node) {
3335 xmlFreeNodeList(dmid_element_with_text);
3336 goto serialization_failed;
3340 /* Serialize message with ID into raw */
3341 /*buffer = serialize_element(envelope)*/
3342 /* }
3344 serialization_failed:
3349 /* Clean up */
3350 free(string);
3351 xmlXPathFreeObject(result);
3352 xmlXPathFreeContext(xpath_ctx);
3354 free(code);
3355 free(message);
3356 xmlFreeDoc(response);
3357 xmlFreeNode(request);
3359 if (!err)
3360 isds_log(ILF_ISDS, ILL_DEBUG,
3361 _("CreateMessage request processed by server "
3362 "successfully.\n"));
3364 return err;
3368 /* Get list of messages. This is common core for getting sent or received
3369 * messaeges.
3370 * Any criterion argument can be NULL, if you don't care about it.
3371 * @context is session context. Must not be NULL.
3372 * @outgoing_direction is true if you want list of outgoing messages,
3373 * it's false if you want incoming messages.
3374 * @from_time is minimal time and date of message sending inclusive.
3375 * @to_time is maximal time and date of message sending inclusive
3376 * @organization_unit_number is number of sender/recipient respectively.
3377 * @status_filter is bit field of isds_message_status values. Use special
3378 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
3379 * all values, you can use bitwise arithmetic if you want.)
3380 * @offset is index of first message we are interested in. First message is 1.
3381 * Set to 0 (or 1) if you don't care.
3382 * @number is maximal length of list you want to get as input value, outputs
3383 * number of messages matching these criteria. Can be NULL if you don't care
3384 * (applies to output value either).
3385 * @messages is automatically reallocated list of isds_message's. Be ware that
3386 * it returns only brief overview (envelope and some other fields) about each
3387 * message, not the complete message. FIXME: Specify exact fields.
3388 * The list is sorted by delivery time in ascending order.
3389 * Use NULL if
3390 * you don't care about don't need the data (useful if you want to know only
3391 * the @number). If you provide &NULL, list will be allocated on heap, if you
3392 * provide pointer to non-NULL, list will be freed automacally at first. Also
3393 * in case of error the list will be NULLed.
3394 * @return IE_SUCCESS or appropriate error code. */
3395 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
3396 _Bool outgoing_direction,
3397 const struct timeval *from_time, const struct timeval *to_time,
3398 const long int *organization_unit_number,
3399 const unsigned int status_filter,
3400 const unsigned long int offset, unsigned long int *number,
3401 struct isds_list **messages) {
3403 isds_error err = IE_SUCCESS;
3404 xmlNsPtr isds_ns = NULL;
3405 xmlNodePtr request = NULL, node;
3406 xmlDocPtr response = NULL;
3407 xmlChar *code = NULL, *message = NULL;
3408 xmlXPathContextPtr xpath_ctx = NULL;
3409 xmlXPathObjectPtr result = NULL;
3410 xmlChar *string = NULL;
3411 long unsigned int count = 0;
3413 if (!context) return IE_INVALID_CONTEXT;
3415 /* Free former message list if any */
3416 if (messages) isds_list_free(messages);
3418 /* Check if connection is established
3419 * TODO: This check should be done donwstairs. */
3420 if (!context->curl) return IE_CONNECTION_CLOSED;
3422 /* Build GetListOf*Messages request */
3423 request = xmlNewNode(NULL,
3424 (outgoing_direction) ?
3425 BAD_CAST "GetListOfSentMessages" :
3426 BAD_CAST "GetListOfReceivedMessages"
3428 if (!request) {
3429 isds_log_message(context,
3430 (outgoing_direction) ?
3431 _("Could not build GetListOfSentMessages request") :
3432 _("Could not build GetListOfReceivedMessages request")
3434 return IE_ERROR;
3436 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
3437 if(!isds_ns) {
3438 isds_log_message(context, _("Could not create ISDS name space"));
3439 xmlFreeNode(request);
3440 return IE_ERROR;
3442 xmlSetNs(request, isds_ns);
3445 if (from_time) {
3446 err = timeval2timestring(from_time, &string);
3447 if (err) goto leave;
3449 INSERT_STRING(request, "dmFromTime", string);
3450 free(string); string = NULL;
3452 if (to_time) {
3453 err = timeval2timestring(to_time, &string);
3454 if (err) goto leave;
3456 INSERT_STRING(request, "dmToTime", string);
3457 free(string); string = NULL;
3459 if (outgoing_direction) {
3460 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
3461 organization_unit_number, string);
3462 } else {
3463 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
3464 organization_unit_number, string);
3467 if (status_filter > MESSAGESTATE_ANY) {
3468 isds_printf_message(context,
3469 _("Invalid message state filter value: %ld"), status_filter);
3470 err = IE_INVAL;
3471 goto leave;
3473 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
3475 if (offset > 0 ) {
3476 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
3477 } else {
3478 INSERT_STRING(request, "dmOffset", "1");
3481 /* number 0 means no limit */
3482 if (number && *number == 0) {
3483 INSERT_STRING(request, "dmLimit", NULL);
3484 } else {
3485 INSERT_ULONGINT(request, "dmLimit", number, string);
3489 isds_log(ILF_ISDS, ILL_DEBUG,
3490 (outgoing_direction) ?
3491 _("Sending GetListOfSentMessages request to ISDS\n") :
3492 _("Sending GetListOfReceivedMessages request to ISDS\n")
3495 /* Sent request */
3496 err = isds(context, SERVICE_DM_INFO, request, &response);
3497 xmlFreeNode(request); request = NULL;
3499 if (err) {
3500 isds_log(ILF_ISDS, ILL_DEBUG,
3501 (outgoing_direction) ?
3502 _("Processing ISDS response on GetListOfSentMessages "
3503 "request failed\n") :
3504 _("Processing ISDS response on GetListOfReceivedMessages "
3505 "request failed\n")
3507 goto leave;
3510 /* Check for response status */
3511 err = isds_response_status(context, SERVICE_DM_INFO, response,
3512 &code, &message, NULL);
3513 if (err) {
3514 isds_log(ILF_ISDS, ILL_DEBUG,
3515 (outgoing_direction) ?
3516 _("ISDS response on GetListOfSentMessages request "
3517 "is missing status\n") :
3518 _("ISDS response on GetListOfReceivedMessages request "
3519 "is missing status\n")
3521 goto leave;
3524 /* Request processed, but nothing found */
3525 if (xmlStrcmp(code, BAD_CAST "0000")) {
3526 char *code_locale = utf82locale((char*)code);
3527 char *message_locale = utf82locale((char*)message);
3528 isds_log(ILF_ISDS, ILL_DEBUG,
3529 (outgoing_direction) ?
3530 _("Server refused GetListOfSentMessages request "
3531 "(code=%s, message=%s)\n") :
3532 _("Server refused GetListOfReceivedMessages request "
3533 "(code=%s, message=%s)\n"),
3534 code_locale, message_locale);
3535 isds_log_message(context, message_locale);
3536 free(code_locale);
3537 free(message_locale);
3538 err = IE_ISDS;
3539 goto leave;
3543 /* Extract data */
3544 xpath_ctx = xmlXPathNewContext(response);
3545 if (!xpath_ctx) {
3546 err = IE_ERROR;
3547 goto leave;
3549 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
3550 err = IE_ERROR;
3551 goto leave;
3553 result = xmlXPathEvalExpression(
3554 (outgoing_direction) ?
3555 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
3556 "isds:dmRecords/isds:dmRecord" :
3557 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
3558 "isds:dmRecords/isds:dmRecord",
3559 xpath_ctx);
3560 if (!result) {
3561 err = IE_ERROR;
3562 goto leave;
3565 /* Fill output arguments in */
3566 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3567 struct isds_envelope *envelope;
3568 struct isds_list *item = NULL, *last_item = NULL;
3570 for (count = 0; count < result->nodesetval->nodeNr; count++) {
3571 /* Create new message */
3572 item = calloc(1, sizeof(*item));
3573 if (!item) {
3574 err = IE_NOMEM;
3575 goto leave;
3577 item->destructor = (void(*)(void**)) &isds_message_free;
3578 item->data = calloc(1, sizeof(struct isds_message));
3579 if (!item->data) {
3580 isds_list_free(&item);
3581 err = IE_NOMEM;
3582 goto leave;
3585 /* Extract envelope data */
3586 xpath_ctx->node = result->nodesetval->nodeTab[count];
3587 envelope = NULL;
3588 err = extract_DmRecord(context, &envelope, xpath_ctx);
3589 if (err) {
3590 isds_list_free(&item);
3591 goto leave;
3594 /* Attach extracted envelope */
3595 ((struct isds_message *) item->data)->envelope = envelope;
3597 /* Append new message into the list */
3598 if (!*messages) {
3599 *messages = last_item = item;
3600 } else {
3601 last_item->next = item;
3602 last_item = item;
3606 if (number) *number = count;
3608 leave:
3609 if (err) {
3610 isds_list_free(messages);
3613 free(string);
3614 xmlXPathFreeObject(result);
3615 xmlXPathFreeContext(xpath_ctx);
3617 free(code);
3618 free(message);
3619 xmlFreeDoc(response);
3620 xmlFreeNode(request);
3622 if (!err)
3623 isds_log(ILF_ISDS, ILL_DEBUG,
3624 (outgoing_direction) ?
3625 _("GetListOfSentMessages request processed by server "
3626 "successfully.\n") :
3627 _("GetListOfReceivedMessages request processed by server "
3628 "successfully.\n")
3630 return err;
3634 /* Get list of outgoing (already sent) messages.
3635 * Any criterion argument can be NULL, if you don't care about it.
3636 * @context is session context. Must not be NULL.
3637 * @from_time is minimal time and date of message sending inclusive.
3638 * @to_time is maximal time and date of message sending inclusive
3639 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
3640 * @status_filter is bit field of isds_message_status values. Use special
3641 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
3642 * all values, you can use bitwise arithmetic if you want.)
3643 * @offset is index of first message we are interested in. First message is 1.
3644 * Set to 0 (or 1) if you don't care.
3645 * @number is maximal length of list you want to get as input value, outputs
3646 * number of messages matching these criteria. Can be NULL if you don't care
3647 * (applies to output value either).
3648 * @messages is automatically reallocated list of isds_message's. Be ware that
3649 * it returns only brief overview (envelope and some other fields) about each
3650 * message, not the complete message. FIXME: Specify exact fields.
3651 * The list is sorted by delivery time in ascending order.
3652 * Use NULL if you don't care about the metadata (useful if you want to know
3653 * only the @number). If you provide &NULL, list will be allocated on heap,
3654 * if you provide pointer to non-NULL, list will be freed automacally at first.
3655 * Also in case of error the list will be NULLed.
3656 * @return IE_SUCCESS or appropriate error code. */
3657 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
3658 const struct timeval *from_time, const struct timeval *to_time,
3659 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
3660 const unsigned long int offset, unsigned long int *number,
3661 struct isds_list **messages) {
3663 return isds_get_list_of_messages(
3664 context, 1,
3665 from_time, to_time, dmSenderOrgUnitNum, status_filter,
3666 offset, number,
3667 messages);
3671 /* Get list of incoming (addressed to you) messages.
3672 * Any criterion argument can be NULL, if you don't care about it.
3673 * @context is session context. Must not be NULL.
3674 * @from_time is minimal time and date of message sending inclusive.
3675 * @to_time is maximal time and date of message sending inclusive
3676 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
3677 * @status_filter is bit field of isds_message_status values. Use special
3678 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
3679 * all values, you can use bitwise arithmetic if you want.)
3680 * @offset is index of first message we are interested in. First message is 1.
3681 * Set to 0 (or 1) if you don't care.
3682 * @number is maximal length of list you want to get as input value, outputs
3683 * number of messages matching these criteria. Can be NULL if you don't care
3684 * (applies to output value either).
3685 * @messages is automatically reallocated list of isds_message's. Be ware that
3686 * it returns only brief overview (envelope and some other fields) about each
3687 * message, not the complete message. FIXME: Specify exact fields.
3688 * Use NULL if you don't care about the metadata (useful if you want to know
3689 * only the @number). If you provide &NULL, list will be allocated on heap,
3690 * if you provide pointer to non-NULL, list will be freed automacally at first.
3691 * Also in case of error the list will be NULLed.
3692 * @return IE_SUCCESS or appropriate error code. */
3693 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
3694 const struct timeval *from_time, const struct timeval *to_time,
3695 const long int *dmRecipientOrgUnitNum,
3696 const unsigned int status_filter,
3697 const unsigned long int offset, unsigned long int *number,
3698 struct isds_list **messages) {
3700 return isds_get_list_of_messages(
3701 context, 0,
3702 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
3703 offset, number,
3704 messages);
3708 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
3709 * code
3710 * @context is session context
3711 * @service is ISDS WS service handler
3712 * @service_name is name of SERVICE_DM_OPERATIONS
3713 * @message_id is message ID to send as service argument to ISDS
3714 * @response is server SOAP body response as XML document
3715 * @code is ISDS status code
3716 * @status_message is ISDS status message
3717 * @return error coded from lower layer, context message will be set up
3718 * appropriately. */
3719 static isds_error build_send_check_message_request(struct isds_ctx *context,
3720 const isds_service service, const xmlChar *service_name,
3721 const char *message_id,
3722 xmlDocPtr *response, xmlChar **code, xmlChar **status_message) {
3723 /* ???: XSD allows list of @message_id's and list of @message's, but
3724 * documentation talks only about `a message' */
3726 isds_error err = IE_SUCCESS;
3727 char *service_name_locale = NULL, *message_id_locale = NULL;
3728 xmlNodePtr request = NULL, node;
3729 xmlNsPtr isds_ns = NULL;
3731 if (!context) return IE_INVALID_CONTEXT;
3732 if (!service_name || !message_id) return IE_INVAL;
3733 if (!response || !code || !status_message) return IE_INVAL;
3735 /* Free output argument */
3736 xmlFreeDoc(*response);
3737 free(*code);
3738 free(*status_message);
3741 /* Check if connection is established
3742 * TODO: This check should be done donwstairs. */
3743 if (!context->curl) return IE_CONNECTION_CLOSED;
3745 service_name_locale = utf82locale((char*)service_name);
3746 message_id_locale = utf82locale(message_id);
3747 if (!service_name_locale || !message_id_locale) {
3748 err = IE_NOMEM;
3749 goto leave;
3752 /* Build request */
3753 request = xmlNewNode(NULL, service_name);
3754 if (!request) {
3755 isds_printf_message(context,
3756 _("Could not build %s request"), service_name_locale);
3757 err = IE_ERROR;
3758 goto leave;
3760 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
3761 if(!isds_ns) {
3762 isds_log_message(context, _("Could not create ISDS name space"));
3763 err = IE_ERROR;
3764 goto leave;
3766 xmlSetNs(request, isds_ns);
3769 /* Add requested ID */
3770 err = validate_message_id_length(context, (xmlChar *) message_id);
3771 if (err) goto leave;
3772 INSERT_STRING(request, "dmID", message_id);
3775 isds_log(ILF_ISDS, ILL_DEBUG,
3776 _("Sending %s request for %s message ID to ISDS\n"),
3777 service_name_locale, message_id_locale);
3779 /* Send request */
3780 err = isds(context, service, request, response);
3781 xmlFreeNode(request); request = NULL;
3783 if (err) {
3784 isds_log(ILF_ISDS, ILL_DEBUG,
3785 _("Processing ISDS response on %s request failed\n"),
3786 service_name_locale);
3787 goto leave;
3790 /* Check for response status */
3791 err = isds_response_status(context, service, *response,
3792 code, status_message, NULL);
3793 if (err) {
3794 isds_log(ILF_ISDS, ILL_DEBUG,
3795 _("ISDS response on %s request is missing status\n"),
3796 service_name_locale);
3797 goto leave;
3800 /* Request processed, but nothing found */
3801 if (xmlStrcmp(*code, BAD_CAST "0000")) {
3802 char *code_locale = utf82locale((char*) *code);
3803 char *status_message_locale = utf82locale((char*) *status_message);
3804 isds_log(ILF_ISDS, ILL_DEBUG,
3805 _("Server refused %s request for %s message ID "
3806 "(code=%s, message=%s)\n"),
3807 service_name_locale, message_id_locale,
3808 code_locale, status_message_locale);
3809 isds_log_message(context, status_message_locale);
3810 free(code_locale);
3811 free(status_message_locale);
3812 err = IE_ISDS;
3813 goto leave;
3816 leave:
3817 free(message_id_locale);
3818 free(service_name_locale);
3819 xmlFreeNode(request);
3820 return err;
3824 /* Download incoming message envelope identified by ID.
3825 * @context is session context
3826 * @message_id is message identifier (you can get them from
3827 * isds_get_list_of_received_messages())
3828 * @message is automatically reallocated message retrieved from ISDS.
3829 * It will miss documents per se. Use isds_get_received_message(), if you are
3830 * interrested in documents (content) too.
3831 * Returned hash and timestamp require documents to be verifiable. */
3832 isds_error isds_get_received_envelope(struct isds_ctx *context,
3833 const char *message_id, struct isds_message **message) {
3835 isds_error err = IE_SUCCESS;
3836 xmlDocPtr response = NULL;
3837 xmlChar *code = NULL, *status_message = NULL;
3838 xmlXPathContextPtr xpath_ctx = NULL;
3839 xmlXPathObjectPtr result = NULL;
3841 if (!context) return IE_INVALID_CONTEXT;
3843 /* Free former message if any */
3844 if (message) isds_message_free(message);
3846 /* Do request and check for success */
3847 err = build_send_check_message_request(context, SERVICE_DM_INFO,
3848 BAD_CAST "MessageEnvelopeDownload", message_id,
3849 &response, &code, &status_message);
3850 if (err) goto leave;
3852 /* Extract data */
3853 xpath_ctx = xmlXPathNewContext(response);
3854 if (!xpath_ctx) {
3855 err = IE_ERROR;
3856 goto leave;
3858 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
3859 err = IE_ERROR;
3860 goto leave;
3862 result = xmlXPathEvalExpression(
3863 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
3864 "isds:dmReturnedMessageEnvelope",
3865 xpath_ctx);
3866 if (!result) {
3867 err = IE_ERROR;
3868 goto leave;
3870 /* Empty response */
3871 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3872 char *message_id_locale = utf82locale((char*) message_id);
3873 isds_printf_message(context,
3874 _("Server did not return any envelope for ID `%s' "
3875 "on MessageEnvelopeDownload request"), message_id_locale);
3876 free(message_id_locale);
3877 err = IE_ISDS;
3878 goto leave;
3880 /* More envelops */
3881 if (result->nodesetval->nodeNr > 1) {
3882 char *message_id_locale = utf82locale((char*) message_id);
3883 isds_printf_message(context,
3884 _("Server did return more envelopes for ID `%s' "
3885 "on MessageEnvelopeDownload request"), message_id_locale);
3886 free(message_id_locale);
3887 err = IE_ISDS;
3888 goto leave;
3890 /* One message */
3891 xpath_ctx->node = result->nodesetval->nodeTab[0];
3893 /* Extract the envelope (= message without documents, hence 0) */
3894 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
3895 if (err) goto leave;
3897 /* Save XML blob */
3898 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
3899 &(*message)->raw_length);
3901 leave:
3902 if (err) {
3903 isds_message_free(message);
3906 xmlXPathFreeObject(result);
3907 xmlXPathFreeContext(xpath_ctx);
3909 free(code);
3910 free(status_message);
3911 xmlFreeDoc(response);
3913 if (!err)
3914 isds_log(ILF_ISDS, ILL_DEBUG,
3915 _("MessageEnvelopeDownload request processed by server "
3916 "successfully.\n")
3918 return err;
3922 /* Dwwnload incoming message identified by ID.
3923 * @context is session context
3924 * @message_id is message identifier (you can get them from
3925 * isds_get_list_of_received_messages())
3926 * @message is automatically reallocated message retrieved from ISDS */
3927 isds_error isds_get_received_message(struct isds_ctx *context,
3928 const char *message_id, struct isds_message **message) {
3929 /* ???: XSD allows list of @message_id's and list of @message's, but
3930 * documentation talks only about `a message' */
3932 isds_error err = IE_SUCCESS;
3933 xmlDocPtr response = NULL;
3934 xmlChar *code = NULL, *status_message = NULL;
3935 xmlXPathContextPtr xpath_ctx = NULL;
3936 xmlXPathObjectPtr result = NULL;
3938 if (!context) return IE_INVALID_CONTEXT;
3940 /* Free former message if any */
3941 if (message) isds_message_free(message);
3943 /* Do request and check for success */
3944 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
3945 BAD_CAST "MessageDownload", message_id,
3946 &response, &code, &status_message);
3947 if (err) goto leave;
3949 /* Extract data */
3950 xpath_ctx = xmlXPathNewContext(response);
3951 if (!xpath_ctx) {
3952 err = IE_ERROR;
3953 goto leave;
3955 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
3956 err = IE_ERROR;
3957 goto leave;
3959 result = xmlXPathEvalExpression(
3960 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
3961 xpath_ctx);
3962 if (!result) {
3963 err = IE_ERROR;
3964 goto leave;
3966 /* Empty response */
3967 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3968 char *message_id_locale = utf82locale((char*) message_id);
3969 isds_printf_message(context,
3970 _("Server did not return any message for ID `%s' "
3971 "on MessageDownload request"), message_id_locale);
3972 free(message_id_locale);
3973 err = IE_ISDS;
3974 goto leave;
3976 /* More messages */
3977 if (result->nodesetval->nodeNr > 1) {
3978 char *message_id_locale = utf82locale((char*) message_id);
3979 isds_printf_message(context,
3980 _("Server did return more messages for ID `%s' "
3981 "on MessageDownload request"), message_id_locale);
3982 free(message_id_locale);
3983 err = IE_ISDS;
3984 goto leave;
3986 /* One message */
3987 xpath_ctx->node = result->nodesetval->nodeTab[0];
3989 /* Extract the message */
3990 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
3991 if (err) goto leave;
3993 /* Save XML blob */
3994 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
3995 &(*message)->raw_length);
3997 leave:
3998 if (err) {
3999 isds_message_free(message);
4002 xmlXPathFreeObject(result);
4003 xmlXPathFreeContext(xpath_ctx);
4005 free(code);
4006 free(status_message);
4007 xmlFreeDoc(response);
4009 if (!err)
4010 isds_log(ILF_ISDS, ILL_DEBUG,
4011 _("MessageDownload request processed by server "
4012 "successfully.\n")
4014 return err;
4018 /* Download signed incoming/outgoing message identified by ID.
4019 * @context is session context
4020 * @output is true for outging message, false for incoming message
4021 * @message_id is message identifier (you can get them from
4022 * isds_get_list_of_{sent,received}_messages())
4023 * @message is automatically reallocated message retrieved from ISDS. The raw
4024 * memeber will be filled with PKCS#7 structure in DER format. */
4025 _hidden isds_error isds_get_signed_message(struct isds_ctx *context,
4026 const _Bool outgoing, const char *message_id,
4027 struct isds_message **message) {
4029 isds_error err = IE_SUCCESS;
4030 xmlDocPtr response = NULL, message_doc = NULL;
4031 xmlChar *code = NULL, *status_message = NULL;
4032 xmlXPathContextPtr xpath_ctx = NULL;
4033 xmlXPathObjectPtr result = NULL;
4034 char *encoded_structure = NULL;
4035 void *raw = NULL, *xml_stream = NULL;
4036 size_t raw_length = 0, xml_stream_length = 0;
4038 if (!context) return IE_INVALID_CONTEXT;
4040 /* Free former message if any */
4041 if (message) isds_message_free(message);
4043 /* Do request and check for success */
4044 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
4045 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
4046 BAD_CAST "SignedMessageDownload",
4047 message_id, &response, &code, &status_message);
4048 if (err) goto leave;
4050 /* Extract data */
4051 xpath_ctx = xmlXPathNewContext(response);
4052 if (!xpath_ctx) {
4053 err = IE_ERROR;
4054 goto leave;
4056 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4057 err = IE_ERROR;
4058 goto leave;
4060 result = xmlXPathEvalExpression(
4061 (outgoing) ? BAD_CAST
4062 "/isds:SignedSentMessageDownloadResponse/isds:dmSignature" :
4063 BAD_CAST "/isds:SignedMessageDownloadResponse/isds:dmSignature",
4064 xpath_ctx);
4065 if (!result) {
4066 err = IE_ERROR;
4067 goto leave;
4069 /* Empty response */
4070 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4071 char *message_id_locale = utf82locale((char*) message_id);
4072 isds_printf_message(context,
4073 (outgoing) ?
4074 _("Server did not return any message for ID `%s' "
4075 "on SignedSentMessageDownload request") :
4076 _("Server did not return any message for ID `%s' "
4077 "on SignedMessageDownload request"),
4078 message_id_locale);
4079 free(message_id_locale);
4080 err = IE_ISDS;
4081 goto leave;
4083 /* More reponses */
4084 if (result->nodesetval->nodeNr > 1) {
4085 char *message_id_locale = utf82locale((char*) message_id);
4086 isds_printf_message(context,
4087 (outgoing) ?
4088 _("Server did return more messages for ID `%s' "
4089 "on SignedSentMessageDownload request") :
4090 _("Server did return more messages for ID `%s' "
4091 "on SignedMessageDownload request"),
4092 message_id_locale);
4093 free(message_id_locale);
4094 err = IE_ISDS;
4095 goto leave;
4097 /* One response */
4098 xpath_ctx->node = result->nodesetval->nodeTab[0];
4100 /* Extract PKCS#7 structure */
4101 EXTRACT_STRING(".", encoded_structure);
4102 if (!encoded_structure) {
4103 isds_log_message(context, _("dmSignature element is empty"));
4106 /* Here we have message as standalone CMS in encoded_structure.
4107 * We don't need any other data, free them: */
4108 xmlXPathFreeObject(result); result = NULL;
4109 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
4110 zfree(code);
4111 zfree(status_message);
4112 xmlFreeDoc(response); response = NULL;
4116 /* Decode PKCS#7 to DER format */
4117 raw_length = b64decode(encoded_structure, &raw);
4118 if (raw_length == (size_t) -1) {
4119 isds_log_message(context,
4120 _("Error while Base64-decoding PKCS#7 structure"));
4121 err = IE_ERROR;
4122 goto leave;
4124 zfree(encoded_structure);
4126 /* Extract message from PKCS#7 structure */
4127 err = extract_cms_data(context, raw, raw_length,
4128 &xml_stream, &xml_stream_length);
4129 if (err) goto leave;
4131 isds_log(ILF_ISDS, ILL_DEBUG, (outgoing) ?
4132 _("Signed outgoing message content:\n%.*s\nEnd of message\n") :
4133 _("Signed incoming message content:\n%.*s\nEnd of message\n"),
4134 xml_stream_length, xml_stream);
4136 /* Convert extracted messages XML stream into XPath context */
4137 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
4138 if (!message_doc) {
4139 err = IE_XML;
4140 goto leave;
4142 xpath_ctx = xmlXPathNewContext(message_doc);
4143 if (!xpath_ctx) {
4144 err = IE_ERROR;
4145 goto leave;
4147 /* XXX: Name spaces mangled for outgoing direction:
4148 * http://isds.czechpoint.cz/v20/SentMessage:
4150 * <q:MessageDownloadResponse
4151 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
4152 * <q:dmReturnedMessage>
4153 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
4154 * <p:dmID>151916</p:dmID>
4155 * ...
4156 * </p:dmDm>
4157 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
4158 * ...
4159 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
4160 * </q:dmReturnedMessage>
4161 * </q:MessageDownloadResponse>
4163 * XXX: Name spaces mangled for incoming direction:
4164 * http://isds.czechpoint.cz/v20/message:
4166 * <q:MessageDownloadResponse
4167 * xmlns:q="http://isds.czechpoint.cz/v20/message">
4168 * <q:dmReturnedMessage>
4169 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
4170 * <p:dmID>151916</p:dmID>
4171 * ...
4172 * </p:dmDm>
4173 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
4174 * ...
4175 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
4176 * </q:dmReturnedMessage>
4177 * </q:MessageDownloadResponse>
4179 * Stupidity of ISDS developers is unlimited */
4180 if (register_namespaces(xpath_ctx, (outgoing) ?
4181 MESSAGE_NS_SIGNED_OUTGOING : MESSAGE_NS_SIGNED_INCOMING)) {
4182 err = IE_ERROR;
4183 goto leave;
4185 /* XXX: Embeded message XML document is always rooted as
4186 * /sisds:MessageDownloadResponse (even outgoind message). */
4187 result = xmlXPathEvalExpression(
4188 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
4189 xpath_ctx);
4190 if (!result) {
4191 err = IE_ERROR;
4192 goto leave;
4194 /* Empty embedded message */
4195 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4196 isds_printf_message(context,
4197 _("XML document embedded into PKCS#7 structure is not "
4198 "sisds:dmReturnedMessage document"));
4199 err = IE_ISDS;
4200 goto leave;
4202 /* More embedded messages */
4203 if (result->nodesetval->nodeNr > 1) {
4204 isds_printf_message(context,
4205 _("Embeded XML document into PKCS#7 structure has more "
4206 "root isds:dmReturnedMessage elements"));
4207 err = IE_ISDS;
4208 goto leave;
4210 /* One embedded message */
4211 xpath_ctx->node = result->nodesetval->nodeTab[0];
4213 /* Extract the message */
4214 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
4215 if (err) goto leave;
4217 /* Append raw CMS structure into message */
4218 (*message)->raw = raw;
4219 (*message)->raw_length = raw_length;
4220 raw = NULL;
4223 leave:
4224 if (err) {
4225 isds_message_free(message);
4228 xmlFreeDoc(message_doc);
4229 cms_data_free(xml_stream);
4230 free(encoded_structure);
4231 xmlXPathFreeObject(result);
4232 xmlXPathFreeContext(xpath_ctx);
4233 free(raw);
4235 free(code);
4236 free(status_message);
4237 xmlFreeDoc(response);
4239 if (!err)
4240 isds_log(ILF_ISDS, ILL_DEBUG,
4241 (outgoing) ?
4242 _("signedmessagedownload request processed by server "
4243 "successfully.\n") :
4244 _("signedmessagedownload request processed by server "
4245 "successfully.\n")
4247 return err;
4252 /* Download signed incoming message identified by ID.
4253 * @context is session context
4254 * @message_id is message identifier (you can get them from
4255 * isds_get_list_of_received_messages())
4256 * @message is automatically reallocated message retrieved from ISDS. The raw
4257 * memeber will be filled with PKCS#7 structure in DER format. */
4258 isds_error isds_get_signed_received_message(struct isds_ctx *context,
4259 const char *message_id, struct isds_message **message) {
4260 return isds_get_signed_message(context, 0, message_id, message);
4264 /* Download signed outgoing message identified by ID.
4265 * @context is session context
4266 * @message_id is message identifier (you can get them from
4267 * isds_get_list_of_sent_messages())
4268 * @message is automatically reallocated message retrieved from ISDS. The raw
4269 * memeber will be filled with PKCS#7 structure in DER format. */
4270 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
4271 const char *message_id, struct isds_message **message) {
4272 return isds_get_signed_message(context, 1, message_id, message);
4276 /* Retrieve hash of message identified by ID stored in ISDS.
4277 * @context is session context
4278 * @message_id is message identifier
4279 * @hash is automatically reallocated message hash downloaded from ISDS.
4280 * Message must exist in system and must not be deleted. */
4281 isds_error isds_download_message_hash(struct isds_ctx *context,
4282 const char *message_id, struct isds_hash **hash) {
4283 /* ???: XSD allows list of @message_id's and list of @message's, but
4284 * documentation talks only about `a message' */
4286 isds_error err = IE_SUCCESS;
4287 xmlDocPtr response = NULL;
4288 xmlChar *code = NULL, *status_message = NULL;
4289 xmlXPathContextPtr xpath_ctx = NULL;
4290 xmlXPathObjectPtr result = NULL;
4292 if (!context) return IE_INVALID_CONTEXT;
4294 isds_hash_free(hash);
4296 err = build_send_check_message_request(context, SERVICE_DM_INFO,
4297 BAD_CAST "VerifyMessage", message_id,
4298 &response, &code, &status_message);
4299 if (err) goto leave;
4302 /* Extract data */
4303 xpath_ctx = xmlXPathNewContext(response);
4304 if (!xpath_ctx) {
4305 err = IE_ERROR;
4306 goto leave;
4308 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4309 err = IE_ERROR;
4310 goto leave;
4312 result = xmlXPathEvalExpression(
4313 BAD_CAST "/isds:VerifyMessageResponse",
4314 xpath_ctx);
4315 if (!result) {
4316 err = IE_ERROR;
4317 goto leave;
4319 /* Empty response */
4320 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4321 char *message_id_locale = utf82locale((char*) message_id);
4322 isds_printf_message(context,
4323 _("Server did not return any response for ID `%s' "
4324 "on VerifyMessage request"), message_id_locale);
4325 free(message_id_locale);
4326 err = IE_ISDS;
4327 goto leave;
4329 /* More responses */
4330 if (result->nodesetval->nodeNr > 1) {
4331 char *message_id_locale = utf82locale((char*) message_id);
4332 isds_printf_message(context,
4333 _("Server did return more responses for ID `%s' "
4334 "on VerifyMessage request"), message_id_locale);
4335 free(message_id_locale);
4336 err = IE_ISDS;
4337 goto leave;
4339 /* One response */
4340 xpath_ctx->node = result->nodesetval->nodeTab[0];
4342 /* Extract the hash */
4343 err = find_and_extract_DmHash(context, hash, xpath_ctx);
4345 leave:
4346 if (err) {
4347 isds_hash_free(hash);
4350 xmlXPathFreeObject(result);
4351 xmlXPathFreeContext(xpath_ctx);
4353 free(code);
4354 free(status_message);
4355 xmlFreeDoc(response);
4357 if (!err)
4358 isds_log(ILF_ISDS, ILL_DEBUG,
4359 _("VerifyMessage request processed by server "
4360 "successfully.\n")
4362 return err;
4366 /* Mark message as read. This is a transactional commit function to acknoledge
4367 * to ISDS the message has been downloaded and processed by client properly.
4368 * @context is session context
4369 * @message_id is message identifier. */
4370 isds_error isds_mark_message_read(struct isds_ctx *context,
4371 const char *message_id) {
4372 /* ???: XSD allows list of @message_id's, but
4373 * documentation talks only about `a message' */
4375 isds_error err = IE_SUCCESS;
4376 xmlDocPtr response = NULL;
4377 xmlChar *code = NULL, *status_message = NULL;
4379 if (!context) return IE_INVALID_CONTEXT;
4381 /* Do request and check for success */
4382 err = build_send_check_message_request(context, SERVICE_DM_INFO,
4383 BAD_CAST "MarkMessageAsDownloaded", message_id,
4384 &response, &code, &status_message);
4386 free(code);
4387 free(status_message);
4388 xmlFreeDoc(response);
4390 if (!err)
4391 isds_log(ILF_ISDS, ILL_DEBUG,
4392 _("MarkMessageAsDownloaded request processed by server "
4393 "successfully.\n")
4395 return err;
4399 #undef INSERT_STRING_ATTRIBUTE
4400 #undef INSERT_ULONGINTNOPTR
4401 #undef INSERT_ULONGINT
4402 #undef INSERT_LONGINT
4403 #undef INSERT_BOOLEAN
4404 #undef INSERT_STRING
4405 #undef EXTRACT_STRING_ATTRIBUTE
4406 #undef EXTRACT_ULONGINT
4407 #undef EXTRACT_LONGINT
4408 #undef EXTRACT_BOOLEAN
4409 #undef EXTRACT_STRING
4412 /* Compute hash of message from raw representation and store it into envelope.
4413 * Original hash structure will be destroyed in envelope.
4414 * @context is session context
4415 * @message is message carrying raw XML message blob
4416 * @algorithm is desired hash algorithm to use */
4417 isds_error isds_compute_message_hash(struct isds_ctx *context,
4418 struct isds_message *message, const isds_hash_algorithm algorithm) {
4419 isds_error err = IE_SUCCESS;
4420 xmlDocPtr message_doc = NULL;
4421 xmlXPathContextPtr xpath_ctx = NULL;
4422 xmlXPathObjectPtr result = NULL;
4423 char *buffer = 0;
4424 size_t length;
4425 struct isds_hash *new_hash = NULL;
4428 if (!context) return IE_INVALID_CONTEXT;
4429 if (!message) return IE_INVAL;
4431 if (!message->raw) {
4432 isds_log_message(context,
4433 _("Message does not carry raw XML representation"));
4434 return IE_INVAL;
4437 /* Parse raw message */
4438 message_doc = xmlParseMemory(message->raw, message->raw_length);
4439 if (!message_doc) {
4440 isds_log_message(context,
4441 _("Message does not carry well-formed XML representation"));
4442 err = IE_XML;
4443 goto leave;
4446 /* Find dmDM element */
4447 xpath_ctx = xmlXPathNewContext(message_doc);
4448 if (!xpath_ctx) {
4449 err = IE_ERROR;
4450 goto leave;
4452 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4453 err = IE_ERROR;
4454 goto leave;
4456 result = xmlXPathEvalExpression(
4457 BAD_CAST "/isds:dmReturnedMessage/isds:dmDm", xpath_ctx);
4458 if (!result) {
4459 err = IE_ERROR;
4460 goto leave;
4462 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4463 isds_log_message(context,
4464 _("Raw message does not contain isds:dmDm element"));
4465 err = IE_XML;
4466 goto leave;
4468 if (result->nodesetval->nodeNr > 1) {
4469 isds_log_message(context,
4470 _("Raw message contains more isds:dmDm elements"));
4471 err = IE_XML;
4472 goto leave;
4474 xpath_ctx->node = result->nodesetval->nodeTab[0];
4476 /* XXX: We need all childern of isds:dmDm: elements, text nodes, PIs,
4477 * CDATA, comments. Is asterisk sufficient? */
4478 result = xmlXPathEvalExpression(BAD_CAST "*", xpath_ctx);
4479 if (!result) {
4480 err = IE_ERROR;
4481 goto leave;
4484 /* Extract dmDM content as bit stream */
4485 err = dump_nodeset(context, message_doc, result->nodesetval,
4486 (void**) &buffer, &length);
4487 if (err) goto leave;
4489 /* Free memory */
4490 xmlXPathFreeObject(result); result = NULL;
4491 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
4492 xmlFreeDoc(message_doc); message_doc = NULL;
4494 /* TODO: Compute hash */
4495 new_hash = calloc(1, sizeof(*new_hash));
4496 if (!new_hash) {
4497 err = IE_NOMEM;
4498 goto leave;
4500 new_hash->algorithm = algorithm;
4501 err = compute_hash(buffer, length, new_hash);
4502 if (err) {
4503 isds_log_message(context, _("Could not compute message hash"));
4504 goto leave;
4507 /* Save cumputed hash */
4508 if (!message->envelope) {
4509 message->envelope = calloc(1, sizeof(*message->envelope));
4510 if (!message->envelope) {
4511 err = IE_NOMEM;
4512 goto leave;
4515 isds_hash_free(&message->envelope->hash);
4516 message->envelope->hash = new_hash;
4518 leave:
4519 if (err) {
4520 isds_hash_free(&new_hash);
4523 free(buffer);
4524 xmlXPathFreeObject(result);
4525 xmlXPathFreeContext(xpath_ctx);
4526 xmlFreeDoc(message_doc);
4527 return err;
4531 /* Search for document by document ID in list of documents. IDs are compared
4532 * as UTF-8 string.
4533 * @documents is list of isds_documents
4534 * @id is document identifier
4535 * @return first matching document or NULL. */
4536 const struct isds_document *isds_find_document_by_id(
4537 const struct isds_list *documents, const char *id) {
4538 const struct isds_list *item;
4539 const struct isds_document *document;
4541 for (item = documents; item; item = item->next) {
4542 document = (struct isds_document *) item->data;
4543 if (!document) continue;
4545 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
4546 return document;
4549 return NULL;
4553 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
4554 struct isds_message **message);
4555 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
4556 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
4557 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
4558 struct isds_address **address);
4560 int isds_message_free(struct isds_message **message);
4561 int isds_address_free(struct isds_address **address);
4565 /* Makes known all relevant namespaces to given XPath context
4566 * @xpat_ctx is XPath context
4567 * @message_ns selects propper message name space. Unsisnged and signed
4568 * messages differs.
4569 * prefix and to URI ISDS_NS */
4570 _hidden isds_error register_namespaces(xmlXPathContextPtr xpath_ctx,
4571 const message_ns_type message_ns) {
4572 const xmlChar *message_namespace = NULL;
4574 if (!xpath_ctx) return IE_ERROR;
4576 switch(message_ns) {
4577 case MESSAGE_NS_UNSIGNED:
4578 message_namespace = BAD_CAST ISDS_NS; break;
4579 case MESSAGE_NS_SIGNED_INCOMING:
4580 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
4581 case MESSAGE_NS_SIGNED_OUTGOING:
4582 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
4583 default:
4584 return IE_ENUM;
4587 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
4588 return IE_ERROR;
4589 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
4590 return IE_ERROR;
4591 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
4592 return IE_ERROR;
4593 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
4594 return IE_ERROR;
4595 return IE_SUCCESS;