Unify isds_get_signed_sent_message() and isds_get_signed_received_message()
[libisds.git] / src / isds.c
blob264f3e1fc5fa1696c744b13b5d1e9ec11c7eaf86
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 * @message is automically reallocated message structure
2317 * @xpath_ctx is XPath context with current node as tReturnedMessage element
2318 * type
2319 * In case of error @message will be freed. */
2320 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
2321 struct isds_message **message, xmlXPathContextPtr xpath_ctx) {
2322 isds_error err = IE_SUCCESS;
2323 xmlNodePtr message_node;
2325 if (!context) return IE_INVALID_CONTEXT;
2326 if (!message) return IE_INVAL;
2327 isds_message_free(message);
2328 if (!xpath_ctx) return IE_INVAL;
2331 *message = calloc(1, sizeof(**message));
2332 if (!*message) {
2333 err = IE_NOMEM;
2334 goto leave;
2337 /* Save message XPATH context node */
2338 message_node = xpath_ctx->node;
2341 /* Extract dmDM */
2342 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
2343 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
2344 if (err) { err = IE_ERROR; goto leave; }
2345 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
2346 if (err) goto leave;
2348 /* Extract dmFiles */
2349 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles", xpath_ctx);
2350 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
2351 if (err) { err = IE_ERROR; goto leave; }
2352 err = extract_documents(context, &((*message)->documents), xpath_ctx);
2353 if (err) goto leave;
2356 /* Restore context to message */
2357 xpath_ctx->node = message_node;
2359 /* Extract dmHash */
2360 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
2361 xpath_ctx);
2362 if (err) goto leave;
2364 /* Extract dmQTimestamp, */
2365 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
2366 xpath_ctx);
2367 if (err) goto leave;
2369 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
2370 * dmAcceptanceTime. */
2371 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
2372 if (err) goto leave;
2374 leave:
2375 if (err) isds_message_free(message);
2376 return err;
2380 /* Convert isds_document structure into XML tree and append to dmFiles node.
2381 * @context is session context
2382 * @document is ISDS document
2383 * @dm_files is XML element the resulting tree will be appended to as a child.
2384 * @return error code, in case of error context' message is filled. */
2385 static isds_error insert_document(struct isds_ctx *context,
2386 struct isds_document *document, xmlNodePtr dm_files) {
2387 isds_error err = IE_SUCCESS;
2388 xmlNodePtr new_file = NULL, file = NULL, node;
2389 xmlAttrPtr attribute_node;
2390 xmlChar *base64data = NULL;
2392 if (!context) return IE_INVALID_CONTEXT;
2393 if (!document || !dm_files) return IE_INVAL;
2395 /* Allocate new dmFile */
2396 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
2397 if (!new_file) {
2398 isds_printf_message(context, _("Could not allocate main dmFile"));
2399 err = IE_ERROR;
2400 goto leave;
2402 /* Append the new dmFile.
2403 * XXX: Main document must go first */
2404 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
2405 file = xmlAddPrevSibling(dm_files->children, new_file);
2406 else
2407 file = xmlAddChild(dm_files, new_file);
2409 if (!file) {
2410 xmlFreeNode(new_file); new_file = NULL;
2411 isds_printf_message(context, _("Could not add dmFile child to "
2412 "%s element"), dm_files->name);
2413 err = IE_ERROR;
2414 goto leave;
2417 /* @dmMimeType is required */
2418 if (!document->dmMimeType) {
2419 isds_log_message(context,
2420 _("Document is missing mandatory MIME type definition"));
2421 err = IE_INVAL;
2422 goto leave;
2424 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
2426 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
2427 if (!string) {
2428 isds_printf_message(context,
2429 _("Document has unkown dmFileMetaType: %ld"),
2430 document->dmFileMetaType);
2431 err = IE_ENUM;
2432 goto leave;
2434 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
2436 if (document->dmFileGuid) {
2437 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
2439 if (document->dmUpFileGuid) {
2440 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
2443 /* @dmFileDescr is required */
2444 if (!document->dmFileDescr) {
2445 isds_log_message(context,
2446 _("Document is missing mandatory description (title)"));
2447 err = IE_INVAL;
2448 goto leave;
2450 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
2452 if (document->dmFormat) {
2453 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
2457 /* Insert content (data) of the document. */
2458 /* XXX; Only base64 is implemented currently. */
2459 base64data = (xmlChar *) b64encode(document->data, document->data_length);
2460 if (!base64data) {
2461 isds_printf_message(context,
2462 _("Not enought memory to encode %zd bytes into Base64"),
2463 document->data_length);
2464 err = IE_NOMEM;
2465 goto leave;
2467 INSERT_STRING(file, "dmEncodedContent", base64data);
2468 free(base64data);
2470 leave:
2471 return err;
2475 /* Get data about logged in user and his box. */
2476 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
2477 struct isds_DbOwnerInfo **db_owner_info) {
2478 isds_error err = IE_SUCCESS;
2479 xmlNsPtr isds_ns = NULL;
2480 xmlNodePtr request = NULL;
2481 xmlDocPtr response = NULL;
2482 xmlChar *code = NULL, *message = NULL;
2483 xmlNodePtr node;
2484 xmlXPathContextPtr xpath_ctx = NULL;
2485 xmlXPathObjectPtr result = NULL;
2486 char *string = NULL;
2488 if (!context) return IE_INVALID_CONTEXT;
2489 if (!db_owner_info) return IE_INVAL;
2491 /* Check if connection is established */
2492 if (!context->curl) return IE_CONNECTION_CLOSED;
2495 /* Build GetOwnerInfoFromLogin request */
2496 request = xmlNewNode(NULL, BAD_CAST "GetOwnerInfoFromLogin");
2497 if (!request) {
2498 isds_log_message(context,
2499 _("Could build GetOwnerInfoFromLogin request"));
2500 return IE_ERROR;
2502 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
2503 if(!isds_ns) {
2504 isds_log_message(context, _("Could not create ISDS name space"));
2505 xmlFreeNode(request);
2506 return IE_ERROR;
2508 xmlSetNs(request, isds_ns);
2509 node = xmlNewChild(request, NULL, BAD_CAST "dbDummy", NULL);
2510 if (!node) {
2511 isds_log_message(context, _("Could nod add dbDummy Child to "
2512 "GetOwnerInfoFromLogin element"));
2513 xmlFreeNode(request);
2514 return IE_ERROR;
2518 isds_log(ILF_ISDS, ILL_DEBUG,
2519 _("Sending GetOwnerInfoFromLogin request to ISDS\n"));
2521 /* Sent request */
2522 err = isds(context, SERVICE_DB_SUPPLEMENTARY, request, &response);
2524 /* Destroy request */
2525 xmlFreeNode(request);
2527 if (err) {
2528 isds_log(ILF_ISDS, ILL_DEBUG,
2529 _("Processing ISDS response on GetOwnerInfoFromLogin "
2530 "request failed\n"));
2531 xmlFreeDoc(response);
2532 return err;
2535 /* Check for response status */
2536 err = isds_response_status(context, SERVICE_DB_SUPPLEMENTARY, response,
2537 &code, &message, NULL);
2538 if (err) {
2539 isds_log(ILF_ISDS, ILL_DEBUG,
2540 _("ISDS response on GetOwnerInfoFromLogin request is "
2541 "missing status\n"));
2542 free(code);
2543 free(message);
2544 xmlFreeDoc(response);
2545 return err;
2547 if (xmlStrcmp(code, BAD_CAST "0000")) {
2548 char *code_locale = utf82locale((char*)code);
2549 char *message_locale = utf82locale((char*)message);
2550 isds_log(ILF_ISDS, ILL_DEBUG,
2551 _("Server refused GetOwnerInfoFromLogin request "
2552 "(code=%s, message=%s)\n"), code_locale, message_locale);
2553 isds_log_message(context, message_locale);
2554 free(code_locale);
2555 free(message_locale);
2556 free(code);
2557 free(message);
2558 xmlFreeDoc(response);
2559 return IE_ISDS;
2562 /* Extract data */
2563 /* Prepare stucture */
2564 isds_DbOwnerInfo_free(db_owner_info);
2565 *db_owner_info = calloc(1, sizeof(**db_owner_info));
2566 if (!*db_owner_info) {
2567 err = IE_NOMEM;
2568 goto leave;
2570 xpath_ctx = xmlXPathNewContext(response);
2571 if (!xpath_ctx) {
2572 err = IE_ERROR;
2573 goto leave;
2575 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
2576 err = IE_ERROR;
2577 goto leave;
2580 /* Set context node */
2581 result = xmlXPathEvalExpression(BAD_CAST
2582 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
2583 if (!result) {
2584 err = IE_ERROR;
2585 goto leave;
2587 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2588 isds_log_message(context, _("Missing dbOwnerInfo element"));
2589 err = IE_ISDS;
2590 goto leave;
2592 if (result->nodesetval->nodeNr > 1) {
2593 isds_log_message(context, _("Multiple dbOwnerInfo element"));
2594 err = IE_ISDS;
2595 goto leave;
2597 xpath_ctx->node = result->nodesetval->nodeTab[0];
2598 xmlXPathFreeObject(result); result = NULL;
2600 /* Extract it */
2601 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
2603 leave:
2604 if (err) {
2605 isds_DbOwnerInfo_free(db_owner_info);
2608 free(string);
2609 xmlXPathFreeObject(result);
2610 xmlXPathFreeContext(xpath_ctx);
2612 free(code);
2613 free(message);
2614 xmlFreeDoc(response);
2616 if (!err)
2617 isds_log(ILF_ISDS, ILL_DEBUG,
2618 _("GetOwnerInfoFromLogin request processed by server "
2619 "successfully.\n"));
2621 return err;
2625 /* Find boxes suiting given criteria.
2626 * @criteria is filter. You should fill in at least some memebers.
2627 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
2628 * possibly empty. Input NULL or valid old structure.
2629 * @return:
2630 * IE_SUCCESS if search sucseeded, @boxes contains usefull data
2631 * IE_NOEXIST if no such box exists, @boxes will be NULL
2632 * IE_2BIG if too much boxes exist and server truncated the resuluts, @boxes
2633 * contains still valid data
2634 * other code if something bad happens. @boxes will be NULL. */
2635 isds_error isds_FindDataBox(struct isds_ctx *context,
2636 const struct isds_DbOwnerInfo *criteria,
2637 struct isds_list **boxes) {
2638 isds_error err = IE_SUCCESS;
2639 _Bool truncated = 0;
2640 xmlNsPtr isds_ns = NULL;
2641 xmlNodePtr request = NULL;
2642 xmlDocPtr response = NULL;
2643 xmlChar *code = NULL, *message = NULL;
2644 xmlNodePtr db_owner_info, node;
2645 xmlXPathContextPtr xpath_ctx = NULL;
2646 xmlXPathObjectPtr result = NULL;
2647 xmlChar *string = NULL;
2650 if (!context) return IE_INVALID_CONTEXT;
2651 if (!boxes) return IE_INVAL;
2652 isds_list_free(boxes);
2654 if (!criteria) {
2655 return IE_INVAL;
2658 /* Check if connection is established
2659 * TODO: This check should be done donwstairs. */
2660 if (!context->curl) return IE_CONNECTION_CLOSED;
2663 /* Build FindDataBox request */
2664 request = xmlNewNode(NULL, BAD_CAST "FindDataBox");
2665 if (!request) {
2666 isds_log_message(context,
2667 _("Could build FindDataBox request"));
2668 return IE_ERROR;
2670 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
2671 if(!isds_ns) {
2672 isds_log_message(context, _("Could not create ISDS name space"));
2673 xmlFreeNode(request);
2674 return IE_ERROR;
2676 xmlSetNs(request, isds_ns);
2677 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
2678 if (!db_owner_info) {
2679 isds_log_message(context, _("Could not add dbOwnerInfo Child to "
2680 "FindDataBox element"));
2681 xmlFreeNode(request);
2682 return IE_ERROR;
2686 INSERT_STRING(db_owner_info, "dbID", criteria->dbID);
2688 /* dbType */
2689 if (criteria->dbType) {
2690 const xmlChar *type_string = isds_DbType2string(*(criteria->dbType));
2691 if (!type_string) {
2692 isds_printf_message(context, _("Invalid dbType value: %d"),
2693 *(criteria->dbType));
2694 err = IE_ENUM;
2695 goto leave;
2697 INSERT_STRING(db_owner_info, "dbType", type_string);
2700 INSERT_STRING(db_owner_info, "firmName", criteria->firmName);
2701 INSERT_STRING(db_owner_info, "ic", criteria->ic);
2702 if (criteria->personName) {
2703 INSERT_STRING(db_owner_info, "pnFirstName",
2704 criteria->personName->pnFirstName);
2705 INSERT_STRING(db_owner_info, "pnMiddleName",
2706 criteria->personName->pnMiddleName);
2707 INSERT_STRING(db_owner_info, "pnLastName",
2708 criteria->personName->pnLastName);
2709 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
2710 criteria->personName->pnLastNameAtBirth);
2712 if (criteria->birthInfo) {
2713 if (criteria->birthInfo->biDate) {
2714 if (!tm2datestring(criteria->birthInfo->biDate, &string))
2715 INSERT_STRING(db_owner_info, "biDate", string);
2716 free(string); string = NULL;
2718 INSERT_STRING(db_owner_info, "biCity", criteria->birthInfo->biCity);
2719 INSERT_STRING(db_owner_info, "biCounty", criteria->birthInfo->biCounty);
2720 INSERT_STRING(db_owner_info, "biState", criteria->birthInfo->biState);
2722 if (criteria->address) {
2723 INSERT_STRING(db_owner_info, "adCity", criteria->address->adCity);
2724 INSERT_STRING(db_owner_info, "adStreet", criteria->address->adStreet);
2725 INSERT_STRING(db_owner_info, "adNumberInStreet",
2726 criteria->address->adNumberInStreet);
2727 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
2728 criteria->address->adNumberInMunicipality);
2729 INSERT_STRING(db_owner_info, "adZipCode", criteria->address->adZipCode);
2730 INSERT_STRING(db_owner_info, "adState", criteria->address->adState);
2732 INSERT_STRING(db_owner_info, "nationality", criteria->nationality);
2733 INSERT_STRING(db_owner_info, "email", criteria->email);
2734 INSERT_STRING(db_owner_info, "telNumber", criteria->telNumber);
2735 INSERT_STRING(db_owner_info, "identifier", criteria->identifier);
2736 INSERT_STRING(db_owner_info, "registryCode", criteria->registryCode);
2738 INSERT_LONGINT(db_owner_info, "dbState", criteria->dbState, string);
2740 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", criteria->dbEffectiveOVM);
2741 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
2742 criteria->dbOpenAddressing);
2745 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending FindDataBox request to ISDS\n"));
2747 /* Sent request */
2748 err = isds(context, SERVICE_DB_SEARCH, request, &response);
2750 /* Destroy request */
2751 xmlFreeNode(request); request = NULL;
2753 if (err) {
2754 isds_log(ILF_ISDS, ILL_DEBUG,
2755 _("Processing ISDS response on FindDataBox "
2756 "request failed\n"));
2757 goto leave;
2760 /* Check for response status */
2761 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
2762 &code, &message, NULL);
2763 if (err) {
2764 isds_log(ILF_ISDS, ILL_DEBUG,
2765 _("ISDS response on FindDataBox request is missing status\n"));
2766 goto leave;
2769 /* Request processed, but nothing found */
2770 if (!xmlStrcmp(code, BAD_CAST "0002") ||
2771 !xmlStrcmp(code, BAD_CAST "5001")) {
2772 char *code_locale = utf82locale((char*)code);
2773 char *message_locale = utf82locale((char*)message);
2774 isds_log(ILF_ISDS, ILL_DEBUG,
2775 _("Server did not found any box on FindDataBox request "
2776 "(code=%s, message=%s)\n"), code_locale, message_locale);
2777 isds_log_message(context, message_locale);
2778 free(code_locale);
2779 free(message_locale);
2780 err = IE_NOEXIST;
2781 goto leave;
2784 /* Warning, not a error */
2785 if (!xmlStrcmp(code, BAD_CAST "0003")) {
2786 char *code_locale = utf82locale((char*)code);
2787 char *message_locale = utf82locale((char*)message);
2788 isds_log(ILF_ISDS, ILL_DEBUG,
2789 _("Server truncated response on FindDataBox request "
2790 "(code=%s, message=%s)\n"), code_locale, message_locale);
2791 isds_log_message(context, message_locale);
2792 free(code_locale);
2793 free(message_locale);
2794 truncated = 1;
2797 /* Other error */
2798 else if (xmlStrcmp(code, BAD_CAST "0000")) {
2799 char *code_locale = utf82locale((char*)code);
2800 char *message_locale = utf82locale((char*)message);
2801 isds_log(ILF_ISDS, ILL_DEBUG,
2802 _("Server refused FindDataBox request "
2803 "(code=%s, message=%s)\n"), code_locale, message_locale);
2804 isds_log_message(context, message_locale);
2805 free(code_locale);
2806 free(message_locale);
2807 err = IE_ISDS;
2808 goto leave;
2811 xpath_ctx = xmlXPathNewContext(response);
2812 if (!xpath_ctx) {
2813 err = IE_ERROR;
2814 goto leave;
2816 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
2817 err = IE_ERROR;
2818 goto leave;
2821 /* Extract boxes if they present */
2822 result = xmlXPathEvalExpression(BAD_CAST
2823 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
2824 xpath_ctx);
2825 if (!result) {
2826 err = IE_ERROR;
2827 goto leave;
2829 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2830 struct isds_list *item, *prev_item = NULL;
2831 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
2832 item = calloc(1, sizeof(*item));
2833 if (!item) {
2834 err = IE_NOMEM;
2835 goto leave;
2838 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
2839 if (i == 0) *boxes = item;
2840 else prev_item->next = item;
2841 prev_item = item;
2843 xpath_ctx->node = result->nodesetval->nodeTab[i];
2844 err = extract_DbOwnerInfo(context,
2845 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
2846 if (err) goto leave;
2850 leave:
2851 if (err) {
2852 isds_list_free(boxes);
2853 } else {
2854 if (truncated) err = IE_2BIG;
2857 free(string);
2858 xmlFreeNode(request);
2859 xmlXPathFreeObject(result);
2860 xmlXPathFreeContext(xpath_ctx);
2862 free(code);
2863 free(message);
2864 xmlFreeDoc(response);
2866 if (!err)
2867 isds_log(ILF_ISDS, ILL_DEBUG,
2868 _("FindDataBox request processed by server successfully.\n"));
2870 return err;
2874 /* Get status of a box.
2875 * @context is ISDS session context.
2876 * @box_id is UTF-8 encoded box identifier as zero terminated string
2877 * @box_status is return value of box status.
2878 * @return:
2879 * IE_SUCCESS if box has been found and its status retrieved
2880 * IE_NOEXIST if box is not known to ISDS server
2881 * or other appropriate error.
2882 * You can use isds_DbState to enumerate box status. However out of enum
2883 * range value can be returned too. This is feature because ISDS
2884 * specification leaves the set of values open.
2885 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
2886 * the box has been deleted, but ISDS still lists its former existence. */
2887 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
2888 long int *box_status) {
2889 isds_error err = IE_SUCCESS;
2890 xmlNsPtr isds_ns = NULL;
2891 xmlNodePtr request = NULL, db_id;
2892 xmlDocPtr response = NULL;
2893 xmlChar *code = NULL, *message = NULL;
2894 xmlXPathContextPtr xpath_ctx = NULL;
2895 xmlXPathObjectPtr result = NULL;
2896 xmlChar *string = NULL;
2898 if (!context) return IE_INVALID_CONTEXT;
2899 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
2901 /* Check if connection is established
2902 * TODO: This check should be done donwstairs. */
2903 if (!context->curl) return IE_CONNECTION_CLOSED;
2906 /* Build CheckDataBox request */
2907 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
2908 if (!request) {
2909 isds_log_message(context,
2910 _("Could build CheckDataBox request"));
2911 return IE_ERROR;
2913 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
2914 if(!isds_ns) {
2915 isds_log_message(context, _("Could not create ISDS name space"));
2916 xmlFreeNode(request);
2917 return IE_ERROR;
2919 xmlSetNs(request, isds_ns);
2920 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
2921 if (!db_id) {
2922 isds_log_message(context, _("Could not add dbId Child to "
2923 "CheckDataBox element"));
2924 xmlFreeNode(request);
2925 return IE_ERROR;
2929 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CheckDataBox request to ISDS\n"));
2931 /* Sent request */
2932 err = isds(context, SERVICE_DB_SEARCH, request, &response);
2934 /* Destroy request */
2935 xmlFreeNode(request);
2937 if (err) {
2938 isds_log(ILF_ISDS, ILL_DEBUG,
2939 _("Processing ISDS response on CheckDataBox "
2940 "request failed\n"));
2941 goto leave;
2944 /* Check for response status */
2945 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
2946 &code, &message, NULL);
2947 if (err) {
2948 isds_log(ILF_ISDS, ILL_DEBUG,
2949 _("ISDS response on CheckDataBox request is missing status\n"));
2950 goto leave;
2953 /* Request processed, but nothing found */
2954 if (!xmlStrcmp(code, BAD_CAST "5001")) {
2955 char *box_id_locale = utf82locale((char*)box_id);
2956 char *code_locale = utf82locale((char*)code);
2957 char *message_locale = utf82locale((char*)message);
2958 isds_log(ILF_ISDS, ILL_DEBUG,
2959 _("Server did not found box %s on CheckDataBox request "
2960 "(code=%s, message=%s)\n"),
2961 box_id_locale, code_locale, message_locale);
2962 isds_log_message(context, message_locale);
2963 free(box_id_locale);
2964 free(code_locale);
2965 free(message_locale);
2966 err = IE_NOEXIST;
2967 goto leave;
2970 /* Other error */
2971 else if (xmlStrcmp(code, BAD_CAST "0000")) {
2972 char *code_locale = utf82locale((char*)code);
2973 char *message_locale = utf82locale((char*)message);
2974 isds_log(ILF_ISDS, ILL_DEBUG,
2975 _("Server refused CheckDataBox request "
2976 "(code=%s, message=%s)\n"), code_locale, message_locale);
2977 isds_log_message(context, message_locale);
2978 free(code_locale);
2979 free(message_locale);
2980 err = IE_ISDS;
2981 goto leave;
2984 /* Extract data */
2985 xpath_ctx = xmlXPathNewContext(response);
2986 if (!xpath_ctx) {
2987 err = IE_ERROR;
2988 goto leave;
2990 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
2991 err = IE_ERROR;
2992 goto leave;
2994 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
2995 xpath_ctx);
2996 if (!result) {
2997 err = IE_ERROR;
2998 goto leave;
3000 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3001 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
3002 err = IE_ISDS;
3003 goto leave;
3005 if (result->nodesetval->nodeNr > 1) {
3006 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
3007 err = IE_ISDS;
3008 goto leave;
3010 xpath_ctx->node = result->nodesetval->nodeTab[0];
3011 xmlXPathFreeObject(result); result = NULL;
3013 EXTRACT_LONGINT("isds:dbState", box_status, 1);
3016 leave:
3017 free(string);
3018 xmlXPathFreeObject(result);
3019 xmlXPathFreeContext(xpath_ctx);
3021 free(code);
3022 free(message);
3023 xmlFreeDoc(response);
3025 if (!err)
3026 isds_log(ILF_ISDS, ILL_DEBUG,
3027 _("CheckDataBox request processed by server successfully.\n"));
3029 return err;
3033 /* Send a message via ISDS to a recipent
3034 * @context is session context
3035 * @outgoing_message is message to send; Some memebers are mandatory (like
3036 * dbIDRecipient), some are optional and some are irrelevant (especialy data
3037 * about sender). Included pointer to isds_list documents must contain at
3038 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
3039 * members will be filled with valid data from ISDS. Exact list of write
3040 * members is subject to change. Currently dmId is changed.
3041 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
3042 isds_error isds_send_message(struct isds_ctx *context,
3043 struct isds_message *outgoing_message) {
3045 isds_error err = IE_SUCCESS;
3046 xmlNsPtr isds_ns = NULL;
3047 xmlNodePtr request = NULL, envelope, dm_files, node;
3048 xmlDocPtr response = NULL;
3049 xmlChar *code = NULL, *message = NULL;
3050 xmlXPathContextPtr xpath_ctx = NULL;
3051 xmlXPathObjectPtr result = NULL;
3052 xmlChar *string = NULL;
3053 _Bool message_is_complete = 0;
3055 if (!context) return IE_INVALID_CONTEXT;
3056 if (!outgoing_message) return IE_INVAL;
3058 /* Check if connection is established
3059 * TODO: This check should be done donwstairs. */
3060 if (!context->curl) return IE_CONNECTION_CLOSED;
3063 /* Build CreateMessage request */
3064 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
3065 if (!request) {
3066 isds_log_message(context,
3067 _("Could build CreateMessage request"));
3068 return IE_ERROR;
3070 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
3071 if(!isds_ns) {
3072 isds_log_message(context, _("Could not create ISDS name space"));
3073 xmlFreeNode(request);
3074 return IE_ERROR;
3076 xmlSetNs(request, isds_ns);
3079 /* Build envelope */
3080 envelope = xmlNewChild(request, NULL, BAD_CAST "dmEnvelope", NULL);
3081 if (!envelope) {
3082 isds_log_message(context, _("Could not add dmEnvelope child to "
3083 "CreateMessage element"));
3084 xmlFreeNode(request);
3085 return IE_ERROR;
3088 if (!outgoing_message->envelope) {
3089 isds_log_message(context, _("outgoing message is missing envelope"));
3090 err = IE_INVAL;
3091 goto leave;
3094 INSERT_STRING(envelope, "dmSenderOrgUnit",
3095 outgoing_message->envelope->dmSenderOrgUnit);
3096 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
3097 outgoing_message->envelope->dmSenderOrgUnitNum, string);
3099 if (!outgoing_message->envelope->dbIDRecipient) {
3100 isds_log_message(context,
3101 _("outgoing message is missing recipient box identifier"));
3102 err = IE_INVAL;
3103 goto leave;
3105 INSERT_STRING(envelope, "dbIDRecipient",
3106 outgoing_message->envelope->dbIDRecipient);
3108 INSERT_STRING(envelope, "dmRecipientOrgUnit",
3109 outgoing_message->envelope->dmRecipientOrgUnit);
3110 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
3111 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
3112 INSERT_STRING(envelope, "dmToHands", outgoing_message->envelope->dmToHands);
3114 #define CHECK_FOR_STRING_LENGTH(string, limit, name) \
3115 if ((string) && xmlUTF8Strlen((xmlChar *) (string)) > (limit)) { \
3116 isds_printf_message(context, \
3117 _("%s has more than %d characters"), (name), (limit)); \
3118 err = IE_2BIG; \
3119 goto leave; \
3122 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 255,
3123 "dmAnnotation");
3124 INSERT_STRING(envelope, "dmAnnotation",
3125 outgoing_message->envelope->dmAnnotation);
3127 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
3128 50, "dmRecipientRefNumber");
3129 INSERT_STRING(envelope, "dmRecipientRefNumber",
3130 outgoing_message->envelope->dmRecipientRefNumber);
3132 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
3133 50, "dmSenderRefNumber");
3134 INSERT_STRING(envelope, "dmSenderRefNumber",
3135 outgoing_message->envelope->dmSenderRefNumber);
3137 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
3138 50, "dmRecipientIdent");
3139 INSERT_STRING(envelope, "dmRecipientIdent",
3140 outgoing_message->envelope->dmRecipientIdent);
3142 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
3143 50, "dmSenderIdent");
3144 INSERT_STRING(envelope, "dmSenderIdent",
3145 outgoing_message->envelope->dmSenderIdent);
3147 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
3148 outgoing_message->envelope->dmLegalTitleLaw, string);
3149 INSERT_LONGINT(envelope, "dmLegalTitleYear",
3150 outgoing_message->envelope->dmLegalTitleYear, string);
3151 INSERT_STRING(envelope, "dmLegalTitleSect",
3152 outgoing_message->envelope->dmLegalTitleSect);
3153 INSERT_STRING(envelope, "dmLegalTitlePar",
3154 outgoing_message->envelope->dmLegalTitlePar);
3155 INSERT_STRING(envelope, "dmLegalTitlePoint",
3156 outgoing_message->envelope->dmLegalTitlePoint);
3158 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
3159 outgoing_message->envelope->dmPersonalDelivery);
3160 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
3161 outgoing_message->envelope->dmAllowSubstDelivery);
3163 #undef CHECK_FOR_STRING_LENGTH
3165 /* ???: Should we require value for dbEffectiveOVM sender?
3166 * ISDS has default as true */
3167 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
3170 /* Append dmFiles */
3171 if (!outgoing_message->documents) {
3172 isds_log_message(context,
3173 _("outgoing message is missing list of documents"));
3174 err = IE_INVAL;
3175 goto leave;
3177 dm_files = xmlNewChild(request, NULL, BAD_CAST "dmFiles", NULL);
3178 if (!dm_files) {
3179 isds_log_message(context, _("Could not add dmFiles child to "
3180 "CreateMessage element"));
3181 err = IE_ERROR;
3182 goto leave;
3185 /* Check for document hieararchy */
3186 err = check_documents_hierarchy(context, outgoing_message->documents);
3187 if (err) goto leave;
3189 /* Process each document */
3190 for (struct isds_list *item =
3191 (struct isds_list *) outgoing_message->documents;
3192 item; item = item->next) {
3193 if (!item->data) {
3194 isds_log_message(context,
3195 _("list of documents contains empty item"));
3196 err = IE_INVAL;
3197 goto leave;
3199 /* FIXME: Check for dmFileMetaType and for document references.
3200 * Only first document can be of MAIN type */
3201 err = insert_document(context, (struct isds_document*) item->data,
3202 dm_files);
3204 if (err) goto leave;
3207 /* Signal we can serilize message since now */
3208 message_is_complete = 1;
3212 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
3214 /* Sent request */
3215 err = isds(context, SERVICE_DM_OPERATIONS, request, &response);
3217 /* Dont' destroy request, we want to privode it to application later */
3219 if (err) {
3220 isds_log(ILF_ISDS, ILL_DEBUG,
3221 _("Processing ISDS response on CreateMessage "
3222 "request failed\n"));
3223 goto leave;
3226 /* Check for response status */
3227 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
3228 &code, &message, NULL);
3229 if (err) {
3230 isds_log(ILF_ISDS, ILL_DEBUG,
3231 _("ISDS response on CreateMessage request "
3232 "is missing status\n"));
3233 goto leave;
3236 /* Request processed, but nothing found */
3237 if (xmlStrcmp(code, BAD_CAST "0000")) {
3238 char *box_id_locale =
3239 utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
3240 char *code_locale = utf82locale((char*)code);
3241 char *message_locale = utf82locale((char*)message);
3242 isds_log(ILF_ISDS, ILL_DEBUG,
3243 _("Server did not accept message for %s on CreateMessage "
3244 "request (code=%s, message=%s)\n"),
3245 box_id_locale, code_locale, message_locale);
3246 isds_log_message(context, message_locale);
3247 free(box_id_locale);
3248 free(code_locale);
3249 free(message_locale);
3250 err = IE_ISDS;
3251 goto leave;
3255 /* Extract data */
3256 xpath_ctx = xmlXPathNewContext(response);
3257 if (!xpath_ctx) {
3258 err = IE_ERROR;
3259 goto leave;
3261 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
3262 err = IE_ERROR;
3263 goto leave;
3265 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
3266 xpath_ctx);
3267 if (!result) {
3268 err = IE_ERROR;
3269 goto leave;
3271 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3272 isds_log_message(context, _("Missing CreateMessageResponse element"));
3273 err = IE_ISDS;
3274 goto leave;
3276 if (result->nodesetval->nodeNr > 1) {
3277 isds_log_message(context, _("Multiple CreateMessageResponse element"));
3278 err = IE_ISDS;
3279 goto leave;
3281 xpath_ctx->node = result->nodesetval->nodeTab[0];
3282 xmlXPathFreeObject(result); result = NULL;
3284 if (outgoing_message->envelope->dmID) {
3285 free(outgoing_message->envelope->dmID);
3286 outgoing_message->envelope->dmID = NULL;
3288 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
3289 if (!outgoing_message->envelope->dmID) {
3290 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
3291 "but did not returen assigned message ID\n"));
3294 leave:
3295 /* TODO: Serialize message into structure member raw */
3296 /* XXX: Each web service transport message in different format.
3297 * Therefore it's not possible to save them directly.
3298 * To save them, one must figure out common format.
3299 * We can leave it on application, or we can implement the ESS format. */
3300 /*if (message_is_complete) {
3301 if (outgoing_message->envelope->dmID) {
3303 /* Add assigned message ID as first child*/
3304 /*xmlNodePtr dmid_text = xmlNewText(
3305 (xmlChar *) outgoing_message->envelope->dmID);
3306 if (!dmid_text) goto serialization_failed;
3308 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
3309 BAD_CAST "dmID");
3310 if (!dmid_element) {
3311 xmlFreeNode(dmid_text);
3312 goto serialization_failed;
3315 xmlNodePtr dmid_element_with_text =
3316 xmlAddChild(dmid_element, dmid_text);
3317 if (!dmid_element_with_text) {
3318 xmlFreeNode(dmid_element);
3319 xmlFreeNode(dmid_text);
3320 goto serialization_failed;
3323 node = xmlAddPrevSibling(envelope->childern,
3324 dmid_element_with_text);
3325 if (!node) {
3326 xmlFreeNodeList(dmid_element_with_text);
3327 goto serialization_failed;
3331 /* Serialize message with ID into raw */
3332 /*buffer = serialize_element(envelope)*/
3333 /* }
3335 serialization_failed:
3340 /* Clean up */
3341 free(string);
3342 xmlXPathFreeObject(result);
3343 xmlXPathFreeContext(xpath_ctx);
3345 free(code);
3346 free(message);
3347 xmlFreeDoc(response);
3348 xmlFreeNode(request);
3350 if (!err)
3351 isds_log(ILF_ISDS, ILL_DEBUG,
3352 _("CreateMessage request processed by server "
3353 "successfully.\n"));
3355 return err;
3359 /* Get list of messages. This is common core for getting sent or received
3360 * messaeges.
3361 * Any criterion argument can be NULL, if you don't care about it.
3362 * @context is session context. Must not be NULL.
3363 * @outgoing_direction is true if you want list of outgoing messages,
3364 * it's false if you want incoming messages.
3365 * @from_time is minimal time and date of message sending inclusive.
3366 * @to_time is maximal time and date of message sending inclusive
3367 * @organization_unit_number is number of sender/recipient respectively.
3368 * @status_filter is bit field of isds_message_status values. Use special
3369 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
3370 * all values, you can use bitwise arithmetic if you want.)
3371 * @offset is index of first message we are interested in. First message is 1.
3372 * Set to 0 (or 1) if you don't care.
3373 * @number is maximal length of list you want to get as input value, outputs
3374 * number of messages matching these criteria. Can be NULL if you don't care
3375 * (applies to output value either).
3376 * @messages is automatically reallocated list of isds_message's. Be ware that
3377 * it returns only brief overview (envelope and some other fields) about each
3378 * message, not the complete message. FIXME: Specify exact fields.
3379 * The list is sorted by delivery time in ascending order.
3380 * Use NULL if
3381 * you don't care about don't need the data (useful if you want to know only
3382 * the @number). If you provide &NULL, list will be allocated on heap, if you
3383 * provide pointer to non-NULL, list will be freed automacally at first. Also
3384 * in case of error the list will be NULLed.
3385 * @return IE_SUCCESS or appropriate error code. */
3386 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
3387 _Bool outgoing_direction,
3388 const struct timeval *from_time, const struct timeval *to_time,
3389 const long int *organization_unit_number,
3390 const unsigned int status_filter,
3391 const unsigned long int offset, unsigned long int *number,
3392 struct isds_list **messages) {
3394 isds_error err = IE_SUCCESS;
3395 xmlNsPtr isds_ns = NULL;
3396 xmlNodePtr request = NULL, node;
3397 xmlDocPtr response = NULL;
3398 xmlChar *code = NULL, *message = NULL;
3399 xmlXPathContextPtr xpath_ctx = NULL;
3400 xmlXPathObjectPtr result = NULL;
3401 xmlChar *string = NULL;
3402 long unsigned int count = 0;
3404 if (!context) return IE_INVALID_CONTEXT;
3406 /* Free former message list if any */
3407 if (messages) isds_list_free(messages);
3409 /* Check if connection is established
3410 * TODO: This check should be done donwstairs. */
3411 if (!context->curl) return IE_CONNECTION_CLOSED;
3413 /* Build GetListOf*Messages request */
3414 request = xmlNewNode(NULL,
3415 (outgoing_direction) ?
3416 BAD_CAST "GetListOfSentMessages" :
3417 BAD_CAST "GetListOfReceivedMessages"
3419 if (!request) {
3420 isds_log_message(context,
3421 (outgoing_direction) ?
3422 _("Could not build GetListOfSentMessages request") :
3423 _("Could not build GetListOfReceivedMessages request")
3425 return IE_ERROR;
3427 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
3428 if(!isds_ns) {
3429 isds_log_message(context, _("Could not create ISDS name space"));
3430 xmlFreeNode(request);
3431 return IE_ERROR;
3433 xmlSetNs(request, isds_ns);
3436 if (from_time) {
3437 err = timeval2timestring(from_time, &string);
3438 if (err) goto leave;
3440 INSERT_STRING(request, "dmFromTime", string);
3441 free(string); string = NULL;
3443 if (to_time) {
3444 err = timeval2timestring(to_time, &string);
3445 if (err) goto leave;
3447 INSERT_STRING(request, "dmToTime", string);
3448 free(string); string = NULL;
3450 if (outgoing_direction) {
3451 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
3452 organization_unit_number, string);
3453 } else {
3454 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
3455 organization_unit_number, string);
3458 if (status_filter > MESSAGESTATE_ANY) {
3459 isds_printf_message(context,
3460 _("Invalid message state filter value: %ld"), status_filter);
3461 err = IE_INVAL;
3462 goto leave;
3464 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
3466 if (offset > 0 ) {
3467 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
3468 } else {
3469 INSERT_STRING(request, "dmOffset", "1");
3472 /* number 0 means no limit */
3473 if (number && *number == 0) {
3474 INSERT_STRING(request, "dmLimit", NULL);
3475 } else {
3476 INSERT_ULONGINT(request, "dmLimit", number, string);
3480 isds_log(ILF_ISDS, ILL_DEBUG,
3481 (outgoing_direction) ?
3482 _("Sending GetListOfSentMessages request to ISDS\n") :
3483 _("Sending GetListOfReceivedMessages request to ISDS\n")
3486 /* Sent request */
3487 err = isds(context, SERVICE_DM_INFO, request, &response);
3488 xmlFreeNode(request); request = NULL;
3490 if (err) {
3491 isds_log(ILF_ISDS, ILL_DEBUG,
3492 (outgoing_direction) ?
3493 _("Processing ISDS response on GetListOfSentMessages "
3494 "request failed\n") :
3495 _("Processing ISDS response on GetListOfReceivedMessages "
3496 "request failed\n")
3498 goto leave;
3501 /* Check for response status */
3502 err = isds_response_status(context, SERVICE_DM_INFO, response,
3503 &code, &message, NULL);
3504 if (err) {
3505 isds_log(ILF_ISDS, ILL_DEBUG,
3506 (outgoing_direction) ?
3507 _("ISDS response on GetListOfSentMessages request "
3508 "is missing status\n") :
3509 _("ISDS response on GetListOfReceivedMessages request "
3510 "is missing status\n")
3512 goto leave;
3515 /* Request processed, but nothing found */
3516 if (xmlStrcmp(code, BAD_CAST "0000")) {
3517 char *code_locale = utf82locale((char*)code);
3518 char *message_locale = utf82locale((char*)message);
3519 isds_log(ILF_ISDS, ILL_DEBUG,
3520 (outgoing_direction) ?
3521 _("Server refused GetListOfSentMessages request "
3522 "(code=%s, message=%s)\n") :
3523 _("Server refused GetListOfReceivedMessages request "
3524 "(code=%s, message=%s)\n"),
3525 code_locale, message_locale);
3526 isds_log_message(context, message_locale);
3527 free(code_locale);
3528 free(message_locale);
3529 err = IE_ISDS;
3530 goto leave;
3534 /* Extract data */
3535 xpath_ctx = xmlXPathNewContext(response);
3536 if (!xpath_ctx) {
3537 err = IE_ERROR;
3538 goto leave;
3540 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
3541 err = IE_ERROR;
3542 goto leave;
3544 result = xmlXPathEvalExpression(
3545 (outgoing_direction) ?
3546 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
3547 "isds:dmRecords/isds:dmRecord" :
3548 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
3549 "isds:dmRecords/isds:dmRecord",
3550 xpath_ctx);
3551 if (!result) {
3552 err = IE_ERROR;
3553 goto leave;
3556 /* Fill output arguments in */
3557 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3558 struct isds_envelope *envelope;
3559 struct isds_list *item = NULL, *last_item = NULL;
3561 for (count = 0; count < result->nodesetval->nodeNr; count++) {
3562 /* Create new message */
3563 item = calloc(1, sizeof(*item));
3564 if (!item) {
3565 err = IE_NOMEM;
3566 goto leave;
3568 item->destructor = (void(*)(void**)) &isds_message_free;
3569 item->data = calloc(1, sizeof(struct isds_message));
3570 if (!item->data) {
3571 isds_list_free(&item);
3572 err = IE_NOMEM;
3573 goto leave;
3576 /* Extract envelope data */
3577 xpath_ctx->node = result->nodesetval->nodeTab[count];
3578 envelope = NULL;
3579 err = extract_DmRecord(context, &envelope, xpath_ctx);
3580 if (err) {
3581 isds_list_free(&item);
3582 goto leave;
3585 /* Attach extracted envelope */
3586 ((struct isds_message *) item->data)->envelope = envelope;
3588 /* Append new message into the list */
3589 if (!*messages) {
3590 *messages = last_item = item;
3591 } else {
3592 last_item->next = item;
3593 last_item = item;
3597 if (number) *number = count;
3599 leave:
3600 if (err) {
3601 isds_list_free(messages);
3604 free(string);
3605 xmlXPathFreeObject(result);
3606 xmlXPathFreeContext(xpath_ctx);
3608 free(code);
3609 free(message);
3610 xmlFreeDoc(response);
3611 xmlFreeNode(request);
3613 if (!err)
3614 isds_log(ILF_ISDS, ILL_DEBUG,
3615 (outgoing_direction) ?
3616 _("GetListOfSentMessages request processed by server "
3617 "successfully.\n") :
3618 _("GetListOfReceivedMessages request processed by server "
3619 "successfully.\n")
3621 return err;
3625 /* Get list of outgoing (already sent) messages.
3626 * Any criterion argument can be NULL, if you don't care about it.
3627 * @context is session context. Must not be NULL.
3628 * @from_time is minimal time and date of message sending inclusive.
3629 * @to_time is maximal time and date of message sending inclusive
3630 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
3631 * @status_filter is bit field of isds_message_status values. Use special
3632 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
3633 * all values, you can use bitwise arithmetic if you want.)
3634 * @offset is index of first message we are interested in. First message is 1.
3635 * Set to 0 (or 1) if you don't care.
3636 * @number is maximal length of list you want to get as input value, outputs
3637 * number of messages matching these criteria. Can be NULL if you don't care
3638 * (applies to output value either).
3639 * @messages is automatically reallocated list of isds_message's. Be ware that
3640 * it returns only brief overview (envelope and some other fields) about each
3641 * message, not the complete message. FIXME: Specify exact fields.
3642 * The list is sorted by delivery time in ascending order.
3643 * Use NULL if you don't care about the metadata (useful if you want to know
3644 * only the @number). If you provide &NULL, list will be allocated on heap,
3645 * if you provide pointer to non-NULL, list will be freed automacally at first.
3646 * Also in case of error the list will be NULLed.
3647 * @return IE_SUCCESS or appropriate error code. */
3648 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
3649 const struct timeval *from_time, const struct timeval *to_time,
3650 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
3651 const unsigned long int offset, unsigned long int *number,
3652 struct isds_list **messages) {
3654 return isds_get_list_of_messages(
3655 context, 1,
3656 from_time, to_time, dmSenderOrgUnitNum, status_filter,
3657 offset, number,
3658 messages);
3662 /* Get list of incoming (addressed to you) messages.
3663 * Any criterion argument can be NULL, if you don't care about it.
3664 * @context is session context. Must not be NULL.
3665 * @from_time is minimal time and date of message sending inclusive.
3666 * @to_time is maximal time and date of message sending inclusive
3667 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
3668 * @status_filter is bit field of isds_message_status values. Use special
3669 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
3670 * all values, you can use bitwise arithmetic if you want.)
3671 * @offset is index of first message we are interested in. First message is 1.
3672 * Set to 0 (or 1) if you don't care.
3673 * @number is maximal length of list you want to get as input value, outputs
3674 * number of messages matching these criteria. Can be NULL if you don't care
3675 * (applies to output value either).
3676 * @messages is automatically reallocated list of isds_message's. Be ware that
3677 * it returns only brief overview (envelope and some other fields) about each
3678 * message, not the complete message. FIXME: Specify exact fields.
3679 * Use NULL if you don't care about the metadata (useful if you want to know
3680 * only the @number). If you provide &NULL, list will be allocated on heap,
3681 * if you provide pointer to non-NULL, list will be freed automacally at first.
3682 * Also in case of error the list will be NULLed.
3683 * @return IE_SUCCESS or appropriate error code. */
3684 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
3685 const struct timeval *from_time, const struct timeval *to_time,
3686 const long int *dmRecipientOrgUnitNum,
3687 const unsigned int status_filter,
3688 const unsigned long int offset, unsigned long int *number,
3689 struct isds_list **messages) {
3691 return isds_get_list_of_messages(
3692 context, 0,
3693 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
3694 offset, number,
3695 messages);
3699 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
3700 * code
3701 * @context is session context
3702 * @service is ISDS WS service handler
3703 * @service_name is name of SERVICE_DM_OPERATIONS
3704 * @message_id is message ID to send as service argument to ISDS
3705 * @response is server SOAP body response as XML document
3706 * @code is ISDS status code
3707 * @status_message is ISDS status message
3708 * @return error coded from lower layer, context message will be set up
3709 * appropriately. */
3710 static isds_error build_send_check_message_request(struct isds_ctx *context,
3711 const isds_service service, const xmlChar *service_name,
3712 const char *message_id,
3713 xmlDocPtr *response, xmlChar **code, xmlChar **status_message) {
3714 /* ???: XSD allows list of @message_id's and list of @message's, but
3715 * documentation talks only about `a message' */
3717 isds_error err = IE_SUCCESS;
3718 char *service_name_locale = NULL, *message_id_locale = NULL;
3719 xmlNodePtr request = NULL, node;
3720 xmlNsPtr isds_ns = NULL;
3722 if (!context) return IE_INVALID_CONTEXT;
3723 if (!service_name || !message_id) return IE_INVAL;
3724 if (!response || !code || !status_message) return IE_INVAL;
3726 /* Free output argument */
3727 xmlFreeDoc(*response);
3728 free(*code);
3729 free(*status_message);
3732 /* Check if connection is established
3733 * TODO: This check should be done donwstairs. */
3734 if (!context->curl) return IE_CONNECTION_CLOSED;
3736 service_name_locale = utf82locale((char*)service_name);
3737 message_id_locale = utf82locale(message_id);
3738 if (!service_name_locale || !message_id_locale) {
3739 err = IE_NOMEM;
3740 goto leave;
3743 /* Build request */
3744 request = xmlNewNode(NULL, service_name);
3745 if (!request) {
3746 isds_printf_message(context,
3747 _("Could not build %s request"), service_name_locale);
3748 err = IE_ERROR;
3749 goto leave;
3751 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
3752 if(!isds_ns) {
3753 isds_log_message(context, _("Could not create ISDS name space"));
3754 err = IE_ERROR;
3755 goto leave;
3757 xmlSetNs(request, isds_ns);
3760 /* Add requested ID */
3761 err = validate_message_id_length(context, (xmlChar *) message_id);
3762 if (err) goto leave;
3763 INSERT_STRING(request, "dmID", message_id);
3766 isds_log(ILF_ISDS, ILL_DEBUG,
3767 _("Sending %s request for %s message ID to ISDS\n"),
3768 service_name_locale, message_id_locale);
3770 /* Send request */
3771 err = isds(context, service, request, response);
3772 xmlFreeNode(request); request = NULL;
3774 if (err) {
3775 isds_log(ILF_ISDS, ILL_DEBUG,
3776 _("Processing ISDS response on %s request failed\n"),
3777 service_name_locale);
3778 goto leave;
3781 /* Check for response status */
3782 err = isds_response_status(context, service, *response,
3783 code, status_message, NULL);
3784 if (err) {
3785 isds_log(ILF_ISDS, ILL_DEBUG,
3786 _("ISDS response on %s request is missing status\n"),
3787 service_name_locale);
3788 goto leave;
3791 /* Request processed, but nothing found */
3792 if (xmlStrcmp(*code, BAD_CAST "0000")) {
3793 char *code_locale = utf82locale((char*) *code);
3794 char *status_message_locale = utf82locale((char*) *status_message);
3795 isds_log(ILF_ISDS, ILL_DEBUG,
3796 _("Server refused %s request for %s message ID "
3797 "(code=%s, message=%s)\n"),
3798 service_name_locale, message_id_locale,
3799 code_locale, status_message_locale);
3800 isds_log_message(context, status_message_locale);
3801 free(code_locale);
3802 free(status_message_locale);
3803 err = IE_ISDS;
3804 goto leave;
3807 leave:
3808 free(message_id_locale);
3809 free(service_name_locale);
3810 xmlFreeNode(request);
3811 return err;
3816 /* Dwwnload incomping message identified by ID.
3817 * @context is session context
3818 * @message_id is message identifier (you can get them from
3819 * isds_get_list_of_received_messages())
3820 * @message is automatically reallocated message retrieved from ISDS */
3821 isds_error isds_get_received_message(struct isds_ctx *context,
3822 const char *message_id, struct isds_message **message) {
3823 /* ???: XSD allows list of @message_id's and list of @message's, but
3824 * documentation talks only about `a message' */
3826 isds_error err = IE_SUCCESS;
3827 xmlDocPtr response = NULL;
3828 xmlChar *code = NULL, *status_message = NULL;
3829 xmlXPathContextPtr xpath_ctx = NULL;
3830 xmlXPathObjectPtr result = NULL;
3832 if (!context) return IE_INVALID_CONTEXT;
3834 /* Free former message if any */
3835 if (message) isds_message_free(message);
3837 /* Do request and check for success */
3838 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
3839 BAD_CAST "MessageDownload", message_id,
3840 &response, &code, &status_message);
3841 if (err) goto leave;
3843 /* Extract data */
3844 xpath_ctx = xmlXPathNewContext(response);
3845 if (!xpath_ctx) {
3846 err = IE_ERROR;
3847 goto leave;
3849 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
3850 err = IE_ERROR;
3851 goto leave;
3853 result = xmlXPathEvalExpression(
3854 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
3855 xpath_ctx);
3856 if (!result) {
3857 err = IE_ERROR;
3858 goto leave;
3860 /* Empty response */
3861 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3862 char *message_id_locale = utf82locale((char*) message_id);
3863 isds_printf_message(context,
3864 _("Server did not return any message for ID `%s' "
3865 "on MessageDownload request"), message_id_locale);
3866 free(message_id_locale);
3867 err = IE_ISDS;
3868 goto leave;
3870 /* More messages */
3871 if (result->nodesetval->nodeNr > 1) {
3872 char *message_id_locale = utf82locale((char*) message_id);
3873 isds_printf_message(context,
3874 _("Server did return more messages for ID `%s' "
3875 "on MessageDownload request"), message_id_locale);
3876 free(message_id_locale);
3877 err = IE_ISDS;
3878 goto leave;
3880 /* One message */
3881 xpath_ctx->node = result->nodesetval->nodeTab[0];
3883 /* Extract the message */
3884 err = extract_TReturnedMessage(context, message, xpath_ctx);
3885 if (err) goto leave;
3887 /* Save XML blob */
3888 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
3889 &(*message)->raw_length);
3891 leave:
3892 if (err) {
3893 isds_message_free(message);
3896 xmlXPathFreeObject(result);
3897 xmlXPathFreeContext(xpath_ctx);
3899 free(code);
3900 free(status_message);
3901 xmlFreeDoc(response);
3903 if (!err)
3904 isds_log(ILF_ISDS, ILL_DEBUG,
3905 _("MessageDownload request processed by server "
3906 "successfully.\n")
3908 return err;
3912 /* Download signed incoming/outgoing message identified by ID.
3913 * @context is session context
3914 * @output is true for outging message, false for incoming message
3915 * @message_id is message identifier (you can get them from
3916 * isds_get_list_of_{sent,received}_messages())
3917 * @message is automatically reallocated message retrieved from ISDS. The raw
3918 * memeber will be filled with PKCS#7 structure in DER format. */
3919 _hidden isds_error isds_get_signed_message(struct isds_ctx *context,
3920 const _Bool outgoing, const char *message_id,
3921 struct isds_message **message) {
3923 isds_error err = IE_SUCCESS;
3924 xmlDocPtr response = NULL, message_doc = NULL;
3925 xmlChar *code = NULL, *status_message = NULL;
3926 xmlXPathContextPtr xpath_ctx = NULL;
3927 xmlXPathObjectPtr result = NULL;
3928 char *encoded_structure = NULL;
3929 void *raw = NULL, *xml_stream = NULL;
3930 size_t raw_length = 0, xml_stream_length = 0;
3932 if (!context) return IE_INVALID_CONTEXT;
3934 /* Free former message if any */
3935 if (message) isds_message_free(message);
3937 /* Do request and check for success */
3938 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
3939 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
3940 BAD_CAST "SignedMessageDownload",
3941 message_id, &response, &code, &status_message);
3942 if (err) goto leave;
3944 /* Extract data */
3945 xpath_ctx = xmlXPathNewContext(response);
3946 if (!xpath_ctx) {
3947 err = IE_ERROR;
3948 goto leave;
3950 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
3951 err = IE_ERROR;
3952 goto leave;
3954 result = xmlXPathEvalExpression(
3955 (outgoing) ? BAD_CAST
3956 "/isds:SignedSentMessageDownloadResponse/isds:dmSignature" :
3957 BAD_CAST "/isds:SignedMessageDownloadResponse/isds:dmSignature",
3958 xpath_ctx);
3959 if (!result) {
3960 err = IE_ERROR;
3961 goto leave;
3963 /* Empty response */
3964 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3965 char *message_id_locale = utf82locale((char*) message_id);
3966 isds_printf_message(context,
3967 (outgoing) ?
3968 _("Server did not return any message for ID `%s' "
3969 "on SignedSentMessageDownload request") :
3970 _("Server did not return any message for ID `%s' "
3971 "on SignedMessageDownload request"),
3972 message_id_locale);
3973 free(message_id_locale);
3974 err = IE_ISDS;
3975 goto leave;
3977 /* More reponses */
3978 if (result->nodesetval->nodeNr > 1) {
3979 char *message_id_locale = utf82locale((char*) message_id);
3980 isds_printf_message(context,
3981 (outgoing) ?
3982 _("Server did return more messages for ID `%s' "
3983 "on SignedSentMessageDownload request") :
3984 _("Server did return more messages for ID `%s' "
3985 "on SignedMessageDownload request"),
3986 message_id_locale);
3987 free(message_id_locale);
3988 err = IE_ISDS;
3989 goto leave;
3991 /* One response */
3992 xpath_ctx->node = result->nodesetval->nodeTab[0];
3994 /* Extract PKCS#7 structure */
3995 EXTRACT_STRING(".", encoded_structure);
3996 if (!encoded_structure) {
3997 isds_log_message(context, _("dmSignature element is empty"));
4000 /* Here we have message as standalone CMS in encoded_structure.
4001 * We don't need any other data, free them: */
4002 xmlXPathFreeObject(result); result = NULL;
4003 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
4004 zfree(code);
4005 zfree(status_message);
4006 xmlFreeDoc(response); response = NULL;
4010 /* Decode PKCS#7 to DER format */
4011 raw_length = b64decode(encoded_structure, &raw);
4012 if (raw_length == (size_t) -1) {
4013 isds_log_message(context,
4014 _("Error while Base64-decoding PKCS#7 structure"));
4015 err = IE_ERROR;
4016 goto leave;
4018 zfree(encoded_structure);
4020 /* Extract message from PKCS#7 structure */
4021 err = extract_cms_data(context, raw, raw_length,
4022 &xml_stream, &xml_stream_length);
4023 if (err) goto leave;
4025 isds_log(ILF_ISDS, ILL_DEBUG, (outgoing) ?
4026 _("Signed outgoing message content:\n%.*s\nEnd of message\n") :
4027 _("Signed incoming message content:\n%.*s\nEnd of message\n"),
4028 xml_stream_length, xml_stream);
4030 /* Convert extracted messages XML stream into XPath context */
4031 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
4032 if (!message_doc) {
4033 err = IE_XML;
4034 goto leave;
4036 xpath_ctx = xmlXPathNewContext(message_doc);
4037 if (!xpath_ctx) {
4038 err = IE_ERROR;
4039 goto leave;
4041 /* XXX: Name spaces mangled for outgoing direction:
4042 * http://isds.czechpoint.cz/v20/SentMessage:
4044 * <q:MessageDownloadResponse
4045 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
4046 * <q:dmReturnedMessage>
4047 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
4048 * <p:dmID>151916</p:dmID>
4049 * ...
4050 * </p:dmDm>
4051 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
4052 * ...
4053 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
4054 * </q:dmReturnedMessage>
4055 * </q:MessageDownloadResponse>
4057 * XXX: Name spaces mangled for incoming direction:
4058 * http://isds.czechpoint.cz/v20/message:
4060 * <q:MessageDownloadResponse
4061 * xmlns:q="http://isds.czechpoint.cz/v20/message">
4062 * <q:dmReturnedMessage>
4063 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
4064 * <p:dmID>151916</p:dmID>
4065 * ...
4066 * </p:dmDm>
4067 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
4068 * ...
4069 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
4070 * </q:dmReturnedMessage>
4071 * </q:MessageDownloadResponse>
4073 * Stupidity of ISDS developers is unlimited */
4074 if (register_namespaces(xpath_ctx, (outgoing) ?
4075 MESSAGE_NS_SIGNED_OUTGOING : MESSAGE_NS_SIGNED_INCOMING)) {
4076 err = IE_ERROR;
4077 goto leave;
4079 /* XXX: Embeded message XML document is always rooted as
4080 * /sisds:MessageDownloadResponse (even outgoind message). */
4081 result = xmlXPathEvalExpression(
4082 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
4083 xpath_ctx);
4084 if (!result) {
4085 err = IE_ERROR;
4086 goto leave;
4088 /* Empty embedded message */
4089 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4090 isds_printf_message(context,
4091 _("XML document embedded into PKCS#7 structure is not "
4092 "sisds:dmReturnedMessage document"));
4093 err = IE_ISDS;
4094 goto leave;
4096 /* More embedded messages */
4097 if (result->nodesetval->nodeNr > 1) {
4098 isds_printf_message(context,
4099 _("Embeded XML document into PKCS#7 structure has more "
4100 "root isds:dmReturnedMessage elements"));
4101 err = IE_ISDS;
4102 goto leave;
4104 /* One embedded message */
4105 xpath_ctx->node = result->nodesetval->nodeTab[0];
4107 /* Extract the message */
4108 err = extract_TReturnedMessage(context, message, xpath_ctx);
4109 if (err) goto leave;
4111 /* Append raw CMS structure into message */
4112 (*message)->raw = raw;
4113 (*message)->raw_length = raw_length;
4114 raw = NULL;
4117 leave:
4118 if (err) {
4119 isds_message_free(message);
4122 xmlFreeDoc(message_doc);
4123 cms_data_free(xml_stream);
4124 free(encoded_structure);
4125 xmlXPathFreeObject(result);
4126 xmlXPathFreeContext(xpath_ctx);
4127 free(raw);
4129 free(code);
4130 free(status_message);
4131 xmlFreeDoc(response);
4133 if (!err)
4134 isds_log(ILF_ISDS, ILL_DEBUG,
4135 (outgoing) ?
4136 _("signedmessagedownload request processed by server "
4137 "successfully.\n") :
4138 _("signedmessagedownload request processed by server "
4139 "successfully.\n")
4141 return err;
4146 /* Download signed incoming message identified by ID.
4147 * @context is session context
4148 * @message_id is message identifier (you can get them from
4149 * isds_get_list_of_received_messages())
4150 * @message is automatically reallocated message retrieved from ISDS. The raw
4151 * memeber will be filled with PKCS#7 structure in DER format. */
4152 isds_error isds_get_signed_received_message(struct isds_ctx *context,
4153 const char *message_id, struct isds_message **message) {
4154 return isds_get_signed_message(context, 0, message_id, message);
4158 /* Download signed outgoing message identified by ID.
4159 * @context is session context
4160 * @message_id is message identifier (you can get them from
4161 * isds_get_list_of_sent_messages())
4162 * @message is automatically reallocated message retrieved from ISDS. The raw
4163 * memeber will be filled with PKCS#7 structure in DER format. */
4164 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
4165 const char *message_id, struct isds_message **message) {
4166 return isds_get_signed_message(context, 1, message_id, message);
4170 /* Retrieve hash of message identified by ID stored in ISDS.
4171 * @context is session context
4172 * @message_id is message identifier
4173 * @hash is automatically reallocated message hash downloaded from ISDS.
4174 * Message must exist in system and must not be deleted. */
4175 isds_error isds_download_message_hash(struct isds_ctx *context,
4176 const char *message_id, struct isds_hash **hash) {
4177 /* ???: XSD allows list of @message_id's and list of @message's, but
4178 * documentation talks only about `a message' */
4180 isds_error err = IE_SUCCESS;
4181 xmlDocPtr response = NULL;
4182 xmlChar *code = NULL, *status_message = NULL;
4183 xmlXPathContextPtr xpath_ctx = NULL;
4184 xmlXPathObjectPtr result = NULL;
4186 if (!context) return IE_INVALID_CONTEXT;
4188 isds_hash_free(hash);
4190 err = build_send_check_message_request(context, SERVICE_DM_INFO,
4191 BAD_CAST "VerifyMessage", message_id,
4192 &response, &code, &status_message);
4193 if (err) goto leave;
4196 /* Extract data */
4197 xpath_ctx = xmlXPathNewContext(response);
4198 if (!xpath_ctx) {
4199 err = IE_ERROR;
4200 goto leave;
4202 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4203 err = IE_ERROR;
4204 goto leave;
4206 result = xmlXPathEvalExpression(
4207 BAD_CAST "/isds:VerifyMessageResponse",
4208 xpath_ctx);
4209 if (!result) {
4210 err = IE_ERROR;
4211 goto leave;
4213 /* Empty response */
4214 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4215 char *message_id_locale = utf82locale((char*) message_id);
4216 isds_printf_message(context,
4217 _("Server did not return any response for ID `%s' "
4218 "on VerifyMessage request"), message_id_locale);
4219 free(message_id_locale);
4220 err = IE_ISDS;
4221 goto leave;
4223 /* More responses */
4224 if (result->nodesetval->nodeNr > 1) {
4225 char *message_id_locale = utf82locale((char*) message_id);
4226 isds_printf_message(context,
4227 _("Server did return more responses for ID `%s' "
4228 "on VerifyMessage request"), message_id_locale);
4229 free(message_id_locale);
4230 err = IE_ISDS;
4231 goto leave;
4233 /* One response */
4234 xpath_ctx->node = result->nodesetval->nodeTab[0];
4236 /* Extract the hash */
4237 err = find_and_extract_DmHash(context, hash, xpath_ctx);
4239 leave:
4240 if (err) {
4241 isds_hash_free(hash);
4244 xmlXPathFreeObject(result);
4245 xmlXPathFreeContext(xpath_ctx);
4247 free(code);
4248 free(status_message);
4249 xmlFreeDoc(response);
4251 if (!err)
4252 isds_log(ILF_ISDS, ILL_DEBUG,
4253 _("VerifyMessage request processed by server "
4254 "successfully.\n")
4256 return err;
4260 #undef INSERT_STRING_ATTRIBUTE
4261 #undef INSERT_ULONGINTNOPTR
4262 #undef INSERT_ULONGINT
4263 #undef INSERT_LONGINT
4264 #undef INSERT_BOOLEAN
4265 #undef INSERT_STRING
4266 #undef EXTRACT_STRING_ATTRIBUTE
4267 #undef EXTRACT_ULONGINT
4268 #undef EXTRACT_LONGINT
4269 #undef EXTRACT_BOOLEAN
4270 #undef EXTRACT_STRING
4273 /* Compute hash of message from raw representation and store it into envelope.
4274 * Original hash structure will be destroyed in envelope.
4275 * @context is session context
4276 * @message is message carrying raw XML message blob
4277 * @algorithm is desired hash algorithm to use */
4278 isds_error isds_compute_message_hash(struct isds_ctx *context,
4279 struct isds_message *message, const isds_hash_algorithm algorithm) {
4280 isds_error err = IE_SUCCESS;
4281 xmlDocPtr message_doc = NULL;
4282 xmlXPathContextPtr xpath_ctx = NULL;
4283 xmlXPathObjectPtr result = NULL;
4284 char *buffer = 0;
4285 size_t length;
4286 struct isds_hash *new_hash = NULL;
4289 if (!context) return IE_INVALID_CONTEXT;
4290 if (!message) return IE_INVAL;
4292 if (!message->raw) {
4293 isds_log_message(context,
4294 _("Message does not carry raw XML representation"));
4295 return IE_INVAL;
4298 /* Parse raw message */
4299 message_doc = xmlParseMemory(message->raw, message->raw_length);
4300 if (!message_doc) {
4301 isds_log_message(context,
4302 _("Message does not carry well-formed XML representation"));
4303 err = IE_XML;
4304 goto leave;
4307 /* Find dmDM element */
4308 xpath_ctx = xmlXPathNewContext(message_doc);
4309 if (!xpath_ctx) {
4310 err = IE_ERROR;
4311 goto leave;
4313 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4314 err = IE_ERROR;
4315 goto leave;
4317 result = xmlXPathEvalExpression(
4318 BAD_CAST "/isds:dmReturnedMessage/isds:dmDm", xpath_ctx);
4319 if (!result) {
4320 err = IE_ERROR;
4321 goto leave;
4323 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4324 isds_log_message(context,
4325 _("Raw message does not contain isds:dmDm element"));
4326 err = IE_XML;
4327 goto leave;
4329 if (result->nodesetval->nodeNr > 1) {
4330 isds_log_message(context,
4331 _("Raw message contains more isds:dmDm elements"));
4332 err = IE_XML;
4333 goto leave;
4335 xpath_ctx->node = result->nodesetval->nodeTab[0];
4337 /* XXX: We need all childern of isds:dmDm: elements, text nodes, PIs,
4338 * CDATA, comments. Is asterisk sufficient? */
4339 result = xmlXPathEvalExpression(BAD_CAST "*", xpath_ctx);
4340 if (!result) {
4341 err = IE_ERROR;
4342 goto leave;
4345 /* Extract dmDM content as bit stream */
4346 err = dump_nodeset(context, message_doc, result->nodesetval,
4347 (void**) &buffer, &length);
4348 if (err) goto leave;
4350 /* Free memory */
4351 xmlXPathFreeObject(result); result = NULL;
4352 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
4353 xmlFreeDoc(message_doc); message_doc = NULL;
4355 /* TODO: Compute hash */
4356 new_hash = calloc(1, sizeof(*new_hash));
4357 if (!new_hash) {
4358 err = IE_NOMEM;
4359 goto leave;
4361 new_hash->algorithm = algorithm;
4362 err = compute_hash(buffer, length, new_hash);
4363 if (err) {
4364 isds_log_message(context, _("Could not compute message hash"));
4365 goto leave;
4368 /* Save cumputed hash */
4369 if (!message->envelope) {
4370 message->envelope = calloc(1, sizeof(*message->envelope));
4371 if (!message->envelope) {
4372 err = IE_NOMEM;
4373 goto leave;
4376 isds_hash_free(&message->envelope->hash);
4377 message->envelope->hash = new_hash;
4379 leave:
4380 if (err) {
4381 isds_hash_free(&new_hash);
4384 free(buffer);
4385 xmlXPathFreeObject(result);
4386 xmlXPathFreeContext(xpath_ctx);
4387 xmlFreeDoc(message_doc);
4388 return err;
4392 /* Search for document by document ID in list of documents. IDs are compared
4393 * as UTF-8 string.
4394 * @documents is list of isds_documents
4395 * @id is document identifier
4396 * @return first matching document or NULL. */
4397 const struct isds_document *isds_find_document_by_id(
4398 const struct isds_list *documents, const char *id) {
4399 const struct isds_list *item;
4400 const struct isds_document *document;
4402 for (item = documents; item; item = item->next) {
4403 document = (struct isds_document *) item->data;
4404 if (!document) continue;
4406 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
4407 return document;
4410 return NULL;
4414 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
4415 struct isds_message **message);
4416 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
4417 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
4418 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
4419 struct isds_address **address);
4421 int isds_message_free(struct isds_message **message);
4422 int isds_address_free(struct isds_address **address);
4426 /* Makes known all relevant namespaces to given XPath context
4427 * @xpat_ctx is XPath context
4428 * @message_ns selects propper message name space. Unsisnged and signed
4429 * messages differs.
4430 * prefix and to URI ISDS_NS */
4431 _hidden isds_error register_namespaces(xmlXPathContextPtr xpath_ctx,
4432 const message_ns_type message_ns) {
4433 const xmlChar *message_namespace = NULL;
4435 if (!xpath_ctx) return IE_ERROR;
4437 switch(message_ns) {
4438 case MESSAGE_NS_UNSIGNED:
4439 message_namespace = BAD_CAST ISDS_NS; break;
4440 case MESSAGE_NS_SIGNED_INCOMING:
4441 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
4442 case MESSAGE_NS_SIGNED_OUTGOING:
4443 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
4444 default:
4445 return IE_ENUM;
4448 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
4449 return IE_ERROR;
4450 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
4451 return IE_ERROR;
4452 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
4453 return IE_ERROR;
4454 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
4455 return IE_ERROR;
4456 return IE_SUCCESS;