Annotate all raw assignments with raw_type
[libisds.git] / src / isds.c
blobc53d13b1c921419c8aaeb91e2af442fb0378e239
1 #define _XOPEN_SOURCE 500 /* strdup from string.h, strptime from time.h */
2 #include <stdlib.h>
3 #include <string.h>
4 #include <stdio.h>
5 #include <stdarg.h>
6 #include <ctype.h>
7 #include <stdint.h>
8 #include "isds_priv.h"
9 #include "utils.h"
10 #include "soap.h"
11 #include "validator.h"
12 #include "crypto.h"
13 #include <gpg-error.h> /* Because of ksba or gpgme */
14 #include "physxml.h"
17 /* Free isds_list with all member data.
18 * @list list to free, on return will be NULL */
19 void isds_list_free(struct isds_list **list) {
20 struct isds_list *item, *next_item;
22 if (!list || !*list) return;
24 for(item = *list; item; item = next_item) {
25 (item->destructor)(&(item->data));
26 next_item = item->next;
27 free(item);
30 *list = NULL;
34 /* Deallocate structure isds_hash and NULL it.
35 * @hash hash to to free */
36 void isds_hash_free(struct isds_hash **hash) {
37 if(!hash || !*hash) return;
38 free((*hash)->value);
39 zfree((*hash));
43 /* Deallocate structure isds_PersonName recursively and NULL it */
44 static void isds_PersonName_free(struct isds_PersonName **person_name) {
45 if (!person_name || !*person_name) return;
47 free((*person_name)->pnFirstName);
48 free((*person_name)->pnMiddleName);
49 free((*person_name)->pnLastName);
50 free((*person_name)->pnLastNameAtBirth);
52 free(*person_name);
53 *person_name = NULL;
57 /* Deallocate structure isds_BirthInfo recursively and NULL it */
58 static void isds_BirthInfo_free(struct isds_BirthInfo **birth_info) {
59 if (!birth_info || !*birth_info) return;
61 free((*birth_info)->biDate);
62 free((*birth_info)->biCity);
63 free((*birth_info)->biCounty);
64 free((*birth_info)->biState);
66 free(*birth_info);
67 *birth_info = NULL;
71 /* Deallocate structure isds_Address recursively and NULL it */
72 static void isds_Address_free(struct isds_Address **address) {
73 if (!address || !*address) return;
75 free((*address)->adCity);
76 free((*address)->adStreet);
77 free((*address)->adNumberInStreet);
78 free((*address)->adNumberInMunicipality);
79 free((*address)->adZipCode);
80 free((*address)->adState);
82 free(*address);
83 *address = NULL;
87 /* Deallocate structure isds_DbOwnerInfo recursively and NULL it */
88 void isds_DbOwnerInfo_free(struct isds_DbOwnerInfo **db_owner_info) {
89 if (!db_owner_info || !*db_owner_info) return;
91 free((*db_owner_info)->dbID);
92 free((*db_owner_info)->dbType);
93 free((*db_owner_info)->ic);
94 isds_PersonName_free(&((*db_owner_info)->personName));
95 free((*db_owner_info)->firmName);
96 isds_BirthInfo_free(&((*db_owner_info)->birthInfo));
97 isds_Address_free(&((*db_owner_info)->address));
98 free((*db_owner_info)->nationality);
99 free((*db_owner_info)->email);
100 free((*db_owner_info)->telNumber);
101 free((*db_owner_info)->identifier);
102 free((*db_owner_info)->registryCode);
103 free((*db_owner_info)->dbState);
104 free((*db_owner_info)->dbEffectiveOVM);
106 free(*db_owner_info);
107 *db_owner_info = NULL;
111 /* Deallocate struct isds_event recursively and NULL it */
112 void isds_event_free(struct isds_event **event) {
113 if (!event || !*event) return;
115 free((*event)->time);
116 free((*event)->type);
117 free((*event)->description);
118 zfree(*event);
122 /* Deallocate struct isds_envelope recursively and NULL it */
123 void isds_envelope_free(struct isds_envelope **envelope) {
124 if (!envelope || !*envelope) return;
126 free((*envelope)->dmID);
127 free((*envelope)->dbIDSender);
128 free((*envelope)->dmSender);
129 free((*envelope)->dmSenderAddress);
130 free((*envelope)->dmSenderType);
131 free((*envelope)->dmRecipient);
132 free((*envelope)->dmRecipientAddress);
133 free((*envelope)->dmAmbiguousRecipient);
135 free((*envelope)->dmOrdinal);
136 free((*envelope)->dmMessageStatus);
137 free((*envelope)->dmDeliveryTime);
138 free((*envelope)->dmAcceptanceTime);
139 isds_hash_free(&(*envelope)->hash);
140 free((*envelope)->timestamp);
141 isds_list_free(&(*envelope)->events);
143 free((*envelope)->dmSenderOrgUnit);
144 free((*envelope)->dmSenderOrgUnitNum);
145 free((*envelope)->dbIDRecipient);
146 free((*envelope)->dmRecipientOrgUnit);
147 free((*envelope)->dmRecipientOrgUnitNum);
148 free((*envelope)->dmToHands);
149 free((*envelope)->dmAnnotation);
150 free((*envelope)->dmRecipientRefNumber);
151 free((*envelope)->dmSenderRefNumber);
152 free((*envelope)->dmRecipientIdent);
153 free((*envelope)->dmSenderIdent);
155 free((*envelope)->dmLegalTitleLaw);
156 free((*envelope)->dmLegalTitleYear);
157 free((*envelope)->dmLegalTitleSect);
158 free((*envelope)->dmLegalTitlePar);
159 free((*envelope)->dmLegalTitlePoint);
161 free((*envelope)->dmPersonalDelivery);
162 free((*envelope)->dmAllowSubstDelivery);
163 free((*envelope)->dmOVM);
165 free(*envelope);
166 *envelope = NULL;
170 /* Deallocate struct isds_message recursively and NULL it */
171 void isds_message_free(struct isds_message **message) {
172 if (!message || !*message) return;
174 free((*message)->raw);
175 isds_envelope_free(&((*message)->envelope));
176 isds_list_free(&((*message)->documents));
178 free(*message);
179 *message = NULL;
183 /* Deallocate struct isds_document recursively and NULL it */
184 void isds_document_free(struct isds_document **document) {
185 if (!document || !*document) return;
187 free((*document)->data);
188 free((*document)->dmMimeType);
189 free((*document)->dmFileGuid);
190 free((*document)->dmUpFileGuid);
191 free((*document)->dmFileDescr);
192 free((*document)->dmFormat);
194 free(*document);
195 *document = NULL;
199 /* Initialize ISDS library.
200 * Global function, must be called before other functions.
201 * If it failes you can not use ISDS library and must call isds_cleanup() to
202 * free partially inititialized global variables. */
203 isds_error isds_init(void) {
204 /* NULL global variables */
205 log_facilities = ILF_ALL;
206 log_level = ILL_WARNING;
208 /* Initialize CURL */
209 if (curl_global_init(CURL_GLOBAL_ALL)) {
210 isds_log(ILF_ISDS, ILL_CRIT, _("CURL library initialization failed\n"));
211 return IE_ERROR;
214 /* Inicialize gpg-error because of gpgme and ksba */
215 if (gpg_err_init()) {
216 isds_log(ILF_ISDS, ILL_CRIT,
217 _("gpg-error library initialization failed\n"));
218 return IE_ERROR;
221 /* Initialize GPGME */
222 if (init_gpgme()) {
223 isds_log(ILF_ISDS, ILL_CRIT,
224 _("GPGME library initialization failed\n"));
225 return IE_ERROR;
228 /* Initialize gcrypt */
229 if (init_gcrypt()) {
230 isds_log(ILF_ISDS, ILL_CRIT,
231 _("gcrypt library initialization failed\n"));
232 return IE_ERROR;
235 /* This can _exit() current program. Find not so assertive check. */
236 LIBXML_TEST_VERSION;
238 /* Check expat */
239 if (init_expat()) {
240 isds_log(ILF_ISDS, ILL_CRIT,
241 _("expat library initialization failed\n"));
242 return IE_ERROR;
245 /* Allocate global variables */
248 return IE_SUCCESS;
252 /* Deinicialize ISDS library.
253 * Global function, must be called as last library function. */
254 isds_error isds_cleanup(void) {
255 /* XML */
256 xmlCleanupParser();
258 /* Curl */
259 curl_global_cleanup();
261 return IE_SUCCESS;
265 /* Return text description of ISDS error */
266 char *isds_strerror(const isds_error error) {
267 switch (error) {
268 case IE_SUCCESS:
269 return(_("Success")); break;
270 case IE_ERROR:
271 return(_("Unspecified error")); break;
272 case IE_NOTSUP:
273 return(_("Not supported")); break;
274 case IE_INVAL:
275 return(_("Invalid value")); break;
276 case IE_INVALID_CONTEXT:
277 return(_("Invalid context")); break;
278 case IE_NOT_LOGGED_IN:
279 return(_("Not logged in")); break;
280 case IE_CONNECTION_CLOSED:
281 return(_("Connection closed")); break;
282 case IE_TIMED_OUT:
283 return(_("Timed out")); break;
284 case IE_NOEXIST:
285 return(_("Not exist")); break;
286 case IE_NOMEM:
287 return(_("Out of memory")); break;
288 case IE_NETWORK:
289 return(_("Network problem")); break;
290 case IE_HTTP:
291 return(_("HTTP problem")); break;
292 case IE_SOAP:
293 return(_("SOAP problem")); break;
294 case IE_XML:
295 return(_("XML problem")); break;
296 case IE_ISDS:
297 return(_("ISDS server problem")); break;
298 case IE_ENUM:
299 return(_("Invalid enum value")); break;
300 case IE_DATE:
301 return(_("Invalid date value")); break;
302 case IE_2BIG:
303 return(_("Too big")); break;
304 case IE_NOTUNIQ:
305 return(_("Value not unique")); break;
306 case IE_NOTEQUAL:
307 return(_("Values not uqual")); break;
308 default:
309 return(_("Unknown error"));
314 /* Create ISDS context.
315 * Each context can be used for different sessions to (possibly) differnet
316 * ISDS server with different credentials. */
317 struct isds_ctx *isds_ctx_create(void) {
318 struct isds_ctx *context;
319 context = malloc(sizeof(*context));
320 if (context) memset(context, 0, sizeof(*context));
321 return context;
325 /* Destroy ISDS context and free memmory.
326 * @context will be NULLed on success. */
327 isds_error isds_ctx_free(struct isds_ctx **context) {
328 if (!context || !*context) {
329 return IE_INVALID_CONTEXT;
332 /* Discard credentials */
333 isds_logout(*context);
335 /* Free other structures */
336 free((*context)->tls_verify_server);
337 free((*context)->tls_ca_file);
338 free((*context)->tls_ca_dir);
339 free((*context)->long_message);
341 free(*context);
342 *context = NULL;
343 return IE_SUCCESS;
347 /* Return long message text produced by library fucntion, e.g. detailed error
348 * mesage. Returned pointer is only valid until new library function is
349 * called for the same context. Could be NULL, especially if NULL context is
350 * supplied. Return string is locale encoded. */
351 char *isds_long_message(const struct isds_ctx *context) {
352 if (!context) return NULL;
353 return context->long_message;
357 /* Stores message into context' long_message buffer.
358 * Application can pick the message up using isds_long_message().
359 * NULL @message truncates the buffer but does not deallocate it.
360 * @message is coded in locale encoding */
361 _hidden isds_error isds_log_message(struct isds_ctx *context,
362 const char *message) {
363 char *buffer;
364 size_t length;
366 if (!context) return IE_INVALID_CONTEXT;
368 /* FIXME: Check for integer overflow */
369 length = 1 + ((message) ? strlen(message) : 0);
370 buffer = realloc(context->long_message, length);
371 if (!buffer) return IE_NOMEM;
373 if (message)
374 strcpy(buffer, message);
375 else
376 *buffer = '\0';
378 context->long_message = buffer;
379 return IE_SUCCESS;
383 /* Appends message into context' long_message buffer.
384 * Application can pick the message up using isds_long_message().
385 * NULL message has void effect. */
386 _hidden isds_error isds_append_message(struct isds_ctx *context,
387 const char *message) {
388 char *buffer;
389 size_t old_length, length;
391 if (!context) return IE_INVALID_CONTEXT;
392 if (!message) return IE_SUCCESS;
393 if (!context->long_message)
394 return isds_log_message(context, message);
396 old_length = strlen(context->long_message);
397 /* FIXME: Check for integer overflow */
398 length = 1 + old_length + strlen(message);
399 buffer = realloc(context->long_message, length);
400 if (!buffer) return IE_NOMEM;
402 strcpy(buffer + old_length, message);
404 context->long_message = buffer;
405 return IE_SUCCESS;
409 /* Stores formated message into context' long_message buffer.
410 * Application can pick the message up using isds_long_message(). */
411 _hidden isds_error isds_printf_message(struct isds_ctx *context,
412 const char *format, ...) {
413 va_list ap;
414 int length;
416 if (!context) return IE_INVALID_CONTEXT;
417 va_start(ap, format);
418 length = isds_vasprintf(&(context->long_message), format, ap);
419 va_end(ap);
421 return (length < 0) ? IE_ERROR: IE_SUCCESS;
425 /* Set logging up.
426 * @facilities is bitmask of isds_log_facility values,
427 * @level is verbosity level. */
428 void isds_set_logging(const unsigned int facilities,
429 const isds_log_level level) {
430 log_facilities = facilities;
431 log_level = level;
435 /* Log @message in class @facility with log @level into global log. @message
436 * is printf(3) formating string, variadic arguments may be neccessary.
437 * For debugging purposes. */
438 _hidden isds_error isds_log(const isds_log_facility facility,
439 const isds_log_level level, const char *message, ...) {
440 va_list ap;
442 if (level > log_level) return IE_SUCCESS;
443 if (!(log_facilities & facility)) return IE_SUCCESS;
444 if (!message) return IE_INVAL;
446 /* TODO: Allow to register output function privided by application
447 * (e.g. fprintf to stderr or copy to text area GUI widget). */
449 va_start(ap, message);
450 vfprintf(stderr, message, ap);
451 va_end(ap);
452 /* Line buffered printf is default.
453 * fflush(stderr);*/
455 return IE_SUCCESS;
459 /* Connect to given url.
460 * It just makes TCP connection to ISDS server found in @url hostname part. */
461 /*int isds_connect(struct isds_ctx *context, const char *url);*/
463 /* Set timeout in miliseconds for each network job like connecting to server
464 * or sending message. Use 0 to disable timeout limits. */
465 isds_error isds_set_timeout(struct isds_ctx *context,
466 const unsigned int timeout) {
467 if (!context) return IE_INVALID_CONTEXT;
469 context->timeout = timeout;
471 if (context->curl) {
472 CURLcode curl_err;
474 curl_err = curl_easy_setopt(context->curl, CURLOPT_NOSIGNAL, 1);
475 if (!curl_err)
476 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT_MS,
477 context->timeout);
478 if (curl_err) return IE_ERROR;
481 return IE_SUCCESS;
485 /* Change SSL/TLS settings.
486 * @context is context which setting will be applied to
487 * @option is name of option. It determines the type of last argument. See
488 * isds_tls_option definition for more info.
489 * @... is value of new setting. Type is determined by @option
490 * */
491 isds_error isds_set_tls(struct isds_ctx *context, const isds_tls_option option,
492 ...) {
493 isds_error err = IE_SUCCESS;
494 va_list ap;
495 char *pointer, *string;
497 if (!context) return IE_INVALID_CONTEXT;
499 va_start(ap, option);
501 #define REPLACE_VA_STRING(destination) \
502 string = va_arg(ap, char *); \
503 if (string) { \
504 pointer = realloc((destination), 1 + strlen(string)); \
505 if (!pointer) { err = IE_NOMEM; goto leave; } \
506 strcpy(pointer, string); \
507 (destination) = pointer; \
508 } else { \
509 free(destination); \
510 (destination) = NULL; \
513 switch (option) {
514 case ITLS_VERIFY_SERVER:
515 if (!context->tls_verify_server) {
516 context->tls_verify_server =
517 malloc(sizeof(*context->tls_verify_server));
518 if (!context->tls_verify_server) {
519 err = IE_NOMEM; goto leave;
522 *context->tls_verify_server = (_Bool) (0 != va_arg(ap, int));
523 break;
525 case ITLS_CA_FILE:
526 REPLACE_VA_STRING(context->tls_ca_file);
527 break;
528 case ITLS_CA_DIRECTORY:
529 REPLACE_VA_STRING(context->tls_ca_dir);
530 break;
532 default:
533 err = IE_ENUM; goto leave;
536 #undef REPLACE_VA_STRING
538 leave:
539 va_end(ap);
540 return err;
544 /* Discard credentials.
545 * Only that. It does not cause log out, connection close or similar. */
546 static isds_error discard_credentials(struct isds_ctx *context) {
547 if(!context) return IE_INVALID_CONTEXT;
549 if (context->username) {
550 memset(context->username, 0, strlen(context->username));
551 free(context->username);
552 context->username = NULL;
554 if (context->password) {
555 memset(context->password, 0, strlen(context->password));
556 free(context->password);
557 context->password = NULL;
560 return IE_SUCCESS;
564 /* Connect and log in into ISDS server.
565 * @url is address of ISDS web service
566 * @username is user name of ISDS user
567 * @password is user's secret password
568 * @certificate is NULL terminated string with PEM formated client's
569 * certificate. Use NULL if only password autentication should be performed.
570 * @key is private key for client's certificate as (base64 encoded?) NULL
571 * terminated string. Use NULL if only password autentication is desired.
572 * */
573 isds_error isds_login(struct isds_ctx *context, const char *url,
574 const char *username, const char *password,
575 const char *certificate, const char* key) {
576 isds_error err = IE_NOT_LOGGED_IN;
577 isds_error soap_err;
578 xmlNsPtr isds_ns = NULL;
579 xmlNodePtr request = NULL;
580 xmlNodePtr response = NULL;
582 if (!context) return IE_INVALID_CONTEXT;
583 if (!url || !username || !password) return IE_INVAL;
584 if (certificate || key) return IE_NOTSUP;
586 /* Store configuration */
587 free(context->url);
588 context->url = strdup(url);
589 if (!(context->url))
590 return IE_NOMEM;
592 /* Close connection if already logged in */
593 if (context->curl) {
594 close_connection(context);
597 /* Prepare CURL handle */
598 context->curl = curl_easy_init();
599 if (!(context->curl))
600 return IE_ERROR;
602 /* Build login request */
603 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
604 if (!request) {
605 isds_log_message(context, _("Could build ISDS login request"));
606 return IE_ERROR;
608 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
609 if(!isds_ns) {
610 isds_log_message(context, _("Could not create ISDS name space"));
611 xmlFreeNode(request);
612 return IE_ERROR;
614 xmlSetNs(request, isds_ns);
616 /* Store credentials */
617 /* FIXME: mlock password
618 * (I have a library) */
619 discard_credentials(context);
620 context->username = strdup(username);
621 context->password = strdup(password);
622 if (!(context->username && context->password)) {
623 discard_credentials(context);
624 xmlFreeNode(request);
625 return IE_NOMEM;
628 isds_log(ILF_ISDS, ILL_DEBUG, _("Logging user %s into server %s\n"),
629 username, url);
631 /* Send login request */
632 soap_err = soap(context, "dz", request, &response);
634 /* Remove credentials */
635 discard_credentials(context);
637 /* Destroy login request */
638 xmlFreeNode(request);
640 if (soap_err) {
641 xmlFreeNodeList(response);
642 close_connection(context);
643 return soap_err;
646 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
647 * authentication succeeded if soap_err == IE_SUCCESS */
648 err = IE_SUCCESS;
650 xmlFreeNodeList(response);
652 if (!err)
653 isds_log(ILF_ISDS, ILL_DEBUG,
654 _("User %s has been logged into server %s successfully\n"),
655 username, url);
656 return err;
660 /* Log out from ISDS server discards credentials and connection configuration. */
661 isds_error isds_logout(struct isds_ctx *context) {
662 if (!context) return IE_INVALID_CONTEXT;
664 /* Close connection */
665 if (context->curl) {
666 close_connection(context);
668 /* Discard credentials for sure. They should not survive isds_login(),
669 * even successful .*/
670 discard_credentials(context);
671 free(context->url);
672 context->url = NULL;
674 isds_log(ILF_ISDS, ILL_DEBUG, _("Logged out from ISDS server\n"));
675 } else {
676 discard_credentials(context);
678 return IE_SUCCESS;
682 /* Verify connection to ISDS is alive and server is responding.
683 * Sent dumy request to ISDS and expect dummy response. */
684 isds_error isds_ping(struct isds_ctx *context) {
685 isds_error soap_err;
686 xmlNsPtr isds_ns = NULL;
687 xmlNodePtr request = NULL;
688 xmlNodePtr response = NULL;
690 if (!context) return IE_INVALID_CONTEXT;
692 /* Check if connection is established */
693 if (!context->curl) return IE_CONNECTION_CLOSED;
696 /* Build dummy request */
697 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
698 if (!request) {
699 isds_log_message(context, _("Could build ISDS dummy request"));
700 return IE_ERROR;
702 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
703 if(!isds_ns) {
704 isds_log_message(context, _("Could not create ISDS name space"));
705 xmlFreeNode(request);
706 return IE_ERROR;
708 xmlSetNs(request, isds_ns);
710 isds_log(ILF_ISDS, ILL_DEBUG, _("Pinging ISDS server\n"));
712 /* Sent dummy request */
713 soap_err = soap(context, "dz", request, &response);
715 /* Destroy login request */
716 xmlFreeNode(request);
718 if (soap_err) {
719 isds_log(ILF_ISDS, ILL_DEBUG,
720 _("ISDS server could not be contacted\n"));
721 xmlFreeNodeList(response);
722 close_connection(context);
723 return soap_err;
726 /* XXX: Untill we don't propagate HTTP code 500 or 4xx, we can be sure
727 * authentication succeeded if soap_err == IE_SUCCESS */
728 /* TODO: ISDS documentation does not specify response body.
729 * However real server sends back DummyOperationResponse */
732 xmlFreeNodeList(response);
734 isds_log(ILF_ISDS, ILL_DEBUG, _("ISDS server alive\n"));
736 return IE_SUCCESS;
740 /* Send bogus request to ISDS.
741 * Just for test purposes */
742 isds_error isds_bogus_request(struct isds_ctx *context) {
743 isds_error err;
744 xmlNsPtr isds_ns = NULL;
745 xmlNodePtr request = NULL;
746 xmlDocPtr response = NULL;
747 xmlChar *code = NULL, *message = NULL;
749 if (!context) return IE_INVALID_CONTEXT;
751 /* Check if connection is established */
752 if (!context->curl) {
753 /* Testing printf message */
754 isds_printf_message(context, "%s", _("I said connection closed"));
755 return IE_CONNECTION_CLOSED;
759 /* Build dummy request */
760 request = xmlNewNode(NULL, BAD_CAST "X-BogusOperation");
761 if (!request) {
762 isds_log_message(context, _("Could build ISDS bogus request"));
763 return IE_ERROR;
765 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
766 if(!isds_ns) {
767 isds_log_message(context, _("Could not create ISDS name space"));
768 xmlFreeNode(request);
769 return IE_ERROR;
771 xmlSetNs(request, isds_ns);
773 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending bogus request to ISDS\n"));
775 /* Sent bogus request */
776 err = isds(context, SERVICE_DM_OPERATIONS, request, &response);
778 /* Destroy request */
779 xmlFreeNode(request);
781 if (err) {
782 isds_log(ILF_ISDS, ILL_DEBUG,
783 _("Processing ISDS response on bogus request failed\n"));
784 xmlFreeDoc(response);
785 return err;
788 /* Check for response status */
789 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
790 &code, &message, NULL);
791 if (err) {
792 isds_log(ILF_ISDS, ILL_DEBUG,
793 _("ISDS response on bogus request is missing status\n"));
794 free(code);
795 free(message);
796 xmlFreeDoc(response);
797 return err;
799 if (xmlStrcmp(code, BAD_CAST "0000")) {
800 char *code_locale = utf82locale((char*)code);
801 char *message_locale = utf82locale((char*)message);
802 isds_log(ILF_ISDS, ILL_DEBUG,
803 _("Server refused bogus request (code=%s, message=%s)\n"),
804 code_locale, message_locale);
805 /* XXX: Literal error messages from ISDS are Czech mesages
806 * (English sometimes) in UTF-8. It's hard to catch them for
807 * translation. Successfully gettextized would return in locale
808 * encoding, unsuccessfully translated would pass in UTF-8. */
809 isds_log_message(context, message_locale);
810 free(code_locale);
811 free(message_locale);
812 free(code);
813 free(message);
814 xmlFreeDoc(response);
815 return IE_ISDS;
819 free(code);
820 free(message);
821 xmlFreeDoc(response);
823 isds_log(ILF_ISDS, ILL_DEBUG,
824 _("Bogus message accepted by server. This should not happen.\n"));
826 return IE_SUCCESS;
830 /* Serialize XML subtree to buffer preserving XML indentatition.
831 * @context is session context
832 * @subtree is XML element to be serialized (with childern)
833 * @buffer is automatically reallocated buffer where serialize to
834 * @length is size of serialized stream in bytes
835 * @return standard error code, free @buffer in case of error */
836 static isds_error serialize_subtree(struct isds_ctx *context,
837 xmlNodePtr subtree, void **buffer, size_t *length) {
838 isds_error err = IE_SUCCESS;
839 xmlBufferPtr xml_buffer = NULL;
840 xmlSaveCtxtPtr save_ctx = NULL;
841 xmlDocPtr subtree_doc = NULL;
842 xmlNodePtr subtree_copy;
843 xmlNsPtr isds_ns;
844 void *new_buffer;
846 if (!context) return IE_INVALID_CONTEXT;
847 if (!buffer) return IE_INVAL;
848 zfree(*buffer);
849 if (!subtree || !length) return IE_INVAL;
851 /* Make temporary XML document with @subtree root element */
852 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
853 * It can result in not well-formed on invalid XML tree (e.g. name space
854 * prefix definition can miss. */
855 /*FIXME */
857 subtree_doc = xmlNewDoc(BAD_CAST "1.0");
858 if (!subtree_doc) {
859 isds_log_message(context, _("Could not build temporary document"));
860 err = IE_ERROR;
861 goto leave;
864 /* XXX: Copy subtree and attach the copy to document.
865 * One node can not bee attached into more document at the same time.
866 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
867 * automatically.
868 * XXX: Check xmlSaveTree() too. */
869 subtree_copy = xmlCopyNodeList(subtree);
870 if (!subtree_copy) {
871 isds_log_message(context, _("Could not copy subtree"));
872 err = IE_ERROR;
873 goto leave;
875 xmlDocSetRootElement(subtree_doc, subtree_copy);
877 /* Only this way we get namespace definition as @xmlns:isds,
878 * otherwise we get namespace prefix without definition */
879 /* FIXME: Don't overwrite original default namespace */
880 isds_ns = xmlNewNs(subtree_copy, BAD_CAST ISDS_NS, NULL);
881 if(!isds_ns) {
882 isds_log_message(context, _("Could not create ISDS name space"));
883 err = IE_ERROR;
884 goto leave;
886 xmlSetNs(subtree_copy, isds_ns);
889 /* Serialize the document into buffer */
890 xml_buffer = xmlBufferCreate();
891 if (!xml_buffer) {
892 isds_log_message(context, _("Could not create xmlBuffer"));
893 err = IE_ERROR;
894 goto leave;
896 /* Last argument 0 means to not format the XML tree */
897 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8", 0);
898 if (!save_ctx) {
899 isds_log_message(context, _("Could not create XML serializer"));
900 err = IE_ERROR;
901 goto leave;
903 /* XXX: According LibXML documentation, this function does not return
904 * meaningfull value yet */
905 xmlSaveDoc(save_ctx, subtree_doc);
906 if (-1 == xmlSaveFlush(save_ctx)) {
907 isds_log_message(context,
908 _("Could not serialize XML subtree"));
909 err = IE_ERROR;
910 goto leave;
912 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
913 * even after xmlSaveFlush(). Thus close it here */
914 xmlSaveClose(save_ctx); save_ctx = NULL;
917 /* Store and detach buffer from xml_buffer */
918 *buffer = xml_buffer->content;
919 *length = xml_buffer->use;
920 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
922 /* Shrink buffer */
923 new_buffer = realloc(*buffer, *length);
924 if (new_buffer) *buffer = new_buffer;
926 leave:
927 if (err) {
928 zfree(*buffer);
929 *length = 0;
932 xmlSaveClose(save_ctx);
933 xmlBufferFree(xml_buffer);
934 xmlFreeDoc(subtree_doc); /* Frees subtree_copy, isds_ns etc. */
935 return err;
938 #if 0
939 /* Dump XML subtree to buffer as literral string, not valid XML possibly.
940 * @context is session context
941 * @document is original document where @nodeset points to
942 * @nodeset is XPath node set to dump (recursively)
943 * @buffer is automarically reallocated buffer where serialize to
944 * @length is size of serialized stream in bytes
945 * @return standard error code, free @buffer in case of error */
946 static isds_error dump_nodeset(struct isds_ctx *context,
947 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
948 void **buffer, size_t *length) {
949 isds_error err = IE_SUCCESS;
950 xmlBufferPtr xml_buffer = NULL;
951 void *new_buffer;
953 if (!context) return IE_INVALID_CONTEXT;
954 if (!buffer) return IE_INVAL;
955 zfree(*buffer);
956 if (!document || !nodeset || !length) return IE_INVAL;
957 *length = 0;
959 /* Empty node set results into NULL buffer */
960 if (xmlXPathNodeSetIsEmpty(nodeset)) {
961 goto leave;
964 /* Resuling the document into buffer */
965 xml_buffer = xmlBufferCreate();
966 if (!xml_buffer) {
967 isds_log_message(context, _("Could not create xmlBuffer"));
968 err = IE_ERROR;
969 goto leave;
972 /* Itearate over all nodes */
973 for (int i = 0; i < nodeset->nodeNr; i++) {
974 /* Serialize node.
975 * XXX: xmlNodeDump() appends to xml_buffer. */
976 if (-1 ==
977 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
978 isds_log_message(context, _("Could not dump XML node"));
979 err = IE_ERROR;
980 goto leave;
984 /* Store and detach buffer from xml_buffer */
985 *buffer = xml_buffer->content;
986 *length = xml_buffer->use;
987 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
989 /* Shrink buffer */
990 new_buffer = realloc(*buffer, *length);
991 if (new_buffer) *buffer = new_buffer;
994 leave:
995 if (err) {
996 zfree(*buffer);
997 *length = 0;
1000 xmlBufferFree(xml_buffer);
1001 return err;
1003 #endif
1005 #if 0
1006 /* Dump XML subtree to buffer as literral string, not valid XML possibly.
1007 * @context is session context
1008 * @document is original document where @nodeset points to
1009 * @nodeset is XPath node set to dump (recursively)
1010 * @buffer is automarically reallocated buffer where serialize to
1011 * @length is size of serialized stream in bytes
1012 * @return standard error code, free @buffer in case of error */
1013 static isds_error dump_nodeset(struct isds_ctx *context,
1014 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1015 void **buffer, size_t *length) {
1016 isds_error err = IE_SUCCESS;
1017 xmlBufferPtr xml_buffer = NULL;
1018 xmlSaveCtxtPtr save_ctx = NULL;
1019 void *new_buffer;
1021 if (!context) return IE_INVALID_CONTEXT;
1022 if (!buffer) return IE_INVAL;
1023 zfree(*buffer);
1024 if (!document || !nodeset || !length) return IE_INVAL;
1025 *length = 0;
1027 /* Empty node set results into NULL buffer */
1028 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1029 goto leave;
1032 /* Resuling the document into buffer */
1033 xml_buffer = xmlBufferCreate();
1034 if (!xml_buffer) {
1035 isds_log_message(context, _("Could not create xmlBuffer"));
1036 err = IE_ERROR;
1037 goto leave;
1039 if (xmlSubstituteEntitiesDefault(1)) {
1040 isds_log_message(context, _("Could not disable attribute escaping"));
1041 err = IE_ERROR;
1042 goto leave;
1044 /* Last argument means:
1045 * 0 to not format the XML tree
1046 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1047 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8",
1048 XML_SAVE_NO_DECL|XML_SAVE_NO_EMPTY|XML_SAVE_NO_XHTML);
1049 if (!save_ctx) {
1050 isds_log_message(context, _("Could not create XML serializer"));
1051 err = IE_ERROR;
1052 goto leave;
1054 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1055 isds_log_message(context, _("Could not disable attribute escaping"));
1056 err = IE_ERROR;
1057 goto leave;
1061 /* Itearate over all nodes */
1062 for (int i = 0; i < nodeset->nodeNr; i++) {
1063 /* Serialize node.
1064 * XXX: xmlNodeDump() appends to xml_buffer. */
1065 /*if (-1 ==
1066 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1068 /* XXX: According LibXML documentation, this function does not return
1069 * meaningfull value yet */
1070 xmlSaveTree(save_ctx, nodeset->nodeTab[i]);
1071 if (-1 == xmlSaveFlush(save_ctx)) {
1072 isds_log_message(context,
1073 _("Could not serialize XML subtree"));
1074 err = IE_ERROR;
1075 goto leave;
1079 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1080 * even after xmlSaveFlush(). Thus close it here */
1081 xmlSaveClose(save_ctx); save_ctx = NULL;
1083 /* Store and detach buffer from xml_buffer */
1084 *buffer = xml_buffer->content;
1085 *length = xml_buffer->use;
1086 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1088 /* Shrink buffer */
1089 new_buffer = realloc(*buffer, *length);
1090 if (new_buffer) *buffer = new_buffer;
1092 leave:
1093 if (err) {
1094 zfree(*buffer);
1095 *length = 0;
1098 xmlSaveClose(save_ctx);
1099 xmlBufferFree(xml_buffer);
1100 return err;
1102 #endif
1105 /* Convert UTF-8 @string represantion of ISDS dbType to enum @type */
1106 static isds_error string2isds_DbType(xmlChar *string, isds_DbType *type) {
1107 if (!string || !type) return IE_INVAL;
1109 if (!xmlStrcmp(string, BAD_CAST "FO"))
1110 *type = DBTYPE_FO;
1111 else if (!xmlStrcmp(string, BAD_CAST "PFO"))
1112 *type = DBTYPE_PFO;
1113 else if (!xmlStrcmp(string, BAD_CAST "PFO_ADVOK"))
1114 *type = DBTYPE_PFO_ADVOK;
1115 else if (!xmlStrcmp(string, BAD_CAST "PFO_DANPOR"))
1116 *type = DBTYPE_PFO_DANPOR;
1117 else if (!xmlStrcmp(string, BAD_CAST "PFO_INSSPR"))
1118 *type = DBTYPE_PFO_INSSPR;
1119 else if (!xmlStrcmp(string, BAD_CAST "PO"))
1120 *type = DBTYPE_PO;
1121 else if (!xmlStrcmp(string, BAD_CAST "PO_ZAK"))
1122 *type = DBTYPE_PO_ZAK;
1123 else if (!xmlStrcmp(string, BAD_CAST "PO_REQ"))
1124 *type = DBTYPE_PO_REQ;
1125 else if (!xmlStrcmp(string, BAD_CAST "OVM"))
1126 *type = DBTYPE_OVM;
1127 else if (!xmlStrcmp(string, BAD_CAST "OVM_NOTAR"))
1128 *type = DBTYPE_OVM_NOTAR;
1129 else if (!xmlStrcmp(string, BAD_CAST "OVM_EXEKUT"))
1130 *type = DBTYPE_OVM_EXEKUT;
1131 else if (!xmlStrcmp(string, BAD_CAST "OVM_REQ"))
1132 *type = DBTYPE_OVM_REQ;
1133 else
1134 return IE_ENUM;
1135 return IE_SUCCESS;
1139 /* Convert ISDS dbType enum @type to UTF-8 string.
1140 * @Return pointer to static string, or NULL if unkwnow enum value */
1141 static const xmlChar *isds_DbType2string(const isds_DbType type) {
1142 switch(type) {
1143 case DBTYPE_FO: return(BAD_CAST "FO"); break;
1144 case DBTYPE_PFO: return(BAD_CAST "PFO"); break;
1145 case DBTYPE_PFO_ADVOK: return(BAD_CAST "PFO_ADVOK"); break;
1146 case DBTYPE_PFO_DANPOR: return(BAD_CAST "PFO_DANPOR"); break;
1147 case DBTYPE_PFO_INSSPR: return(BAD_CAST "PFO_INSSPR"); break;
1148 case DBTYPE_PO: return(BAD_CAST "PO"); break;
1149 case DBTYPE_PO_ZAK: return(BAD_CAST "PO_ZAK"); break;
1150 case DBTYPE_PO_REQ: return(BAD_CAST "PO_REQ"); break;
1151 case DBTYPE_OVM: return(BAD_CAST "OVM"); break;
1152 case DBTYPE_OVM_NOTAR: return(BAD_CAST "OVM_NOTAR"); break;
1153 case DBTYPE_OVM_EXEKUT: return(BAD_CAST "OVM_EXEKUT"); break;
1154 case DBTYPE_OVM_REQ: return(BAD_CAST "OVM_REQ"); break;
1155 default: return NULL; break;
1160 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
1161 * @Return pointer to static string, or NULL if unkwnow enum value */
1162 static const xmlChar *isds_FileMetaType2string(const isds_FileMetaType type) {
1163 switch(type) {
1164 case FILEMETATYPE_MAIN: return(BAD_CAST "main"); break;
1165 case FILEMETATYPE_ENCLOSURE: return(BAD_CAST "enclosure"); break;
1166 case FILEMETATYPE_SIGNATURE: return(BAD_CAST "signature"); break;
1167 case FILEMETATYPE_META: return(BAD_CAST "meta"); break;
1168 default: return NULL; break;
1173 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
1174 * @Return IE_ENUM if @string is not valid enum member */
1175 static isds_error string2isds_FileMetaType(const xmlChar *string,
1176 isds_FileMetaType *type) {
1177 if (!string || !type) return IE_INVAL;
1179 if (!xmlStrcmp(string, BAD_CAST "main"))
1180 *type = FILEMETATYPE_MAIN;
1181 else if (!xmlStrcmp(string, BAD_CAST "enclosure"))
1182 *type = FILEMETATYPE_ENCLOSURE;
1183 else if (!xmlStrcmp(string, BAD_CAST "signature"))
1184 *type = FILEMETATYPE_SIGNATURE;
1185 else if (!xmlStrcmp(string, BAD_CAST "meta"))
1186 *type = FILEMETATYPE_META;
1187 else
1188 return IE_ENUM;
1189 return IE_SUCCESS;
1193 /* Convert UTF-8 @string to ISDS hash @algorithm.
1194 * @Return IE_ENUM if @string is not valid enum member */
1195 static isds_error string2isds_hash_algorithm(const xmlChar *string,
1196 isds_hash_algorithm *algorithm) {
1197 if (!string || !algorithm) return IE_INVAL;
1199 if (!xmlStrcmp(string, BAD_CAST "MD5"))
1200 *algorithm = HASH_ALGORITHM_MD5;
1201 else if (!xmlStrcmp(string, BAD_CAST "SHA-1"))
1202 *algorithm = HASH_ALGORITHM_SHA_1;
1203 else if (!xmlStrcmp(string, BAD_CAST "SHA-256"))
1204 *algorithm = HASH_ALGORITHM_SHA_256;
1205 else if (!xmlStrcmp(string, BAD_CAST "SHA-512"))
1206 *algorithm = HASH_ALGORITHM_SHA_512;
1207 else
1208 return IE_ENUM;
1209 return IE_SUCCESS;
1213 /* Convert UTF-8 @string represantion of ISO 8601 date to @time.
1214 * XXX: Not all ISO formats are supported */
1215 static isds_error datestring2tm(const xmlChar *string, struct tm *time) {
1216 char *offset;
1217 if (!string || !time) return IE_INVAL;
1219 /* xsd:date is ISO 8601 string, thus ASCII */
1220 offset = strptime((char*)string, "%Y-%m-%d", time);
1221 if (offset && *offset == '\0')
1222 return IE_SUCCESS;
1224 offset = strptime((char*)string, "%Y%m%d", time);
1225 if (offset && *offset == '\0')
1226 return IE_SUCCESS;
1228 offset = strptime((char*)string, "%Y-%j", time);
1229 if (offset && *offset == '\0')
1230 return IE_SUCCESS;
1232 return IE_NOTSUP;
1236 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
1237 static isds_error tm2datestring(const struct tm *time, xmlChar **string) {
1238 if (!time || !string) return IE_INVAL;
1240 if (-1 == isds_asprintf((char **) string, "%d-%02d-%02d",
1241 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
1242 return IE_ERROR;
1244 return IE_SUCCESS;
1248 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
1249 * respects the @time microseconds too. */
1250 static isds_error timeval2timestring(const struct timeval *time,
1251 xmlChar **string) {
1252 struct tm broken;
1254 if (!time || !string) return IE_INVAL;
1256 if (!gmtime_r(&time->tv_sec, &broken)) return IE_DATE;
1257 if (time->tv_usec < 0 || time->tv_usec > 999999) return IE_DATE;
1259 /* TODO: small negative year should be formated as "-0012". This is not
1260 * true for glibc "%04d". We should implement it.
1261 * TODO: What's type of time->tv_usec exactly? Unsigned? Absolute?
1262 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
1263 if (-1 == isds_asprintf((char **) string,
1264 "%04d-%02d-%02dT%02d:%02d:%02d.%06ld",
1265 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
1266 broken.tm_hour, broken.tm_min, broken.tm_sec,
1267 time->tv_usec))
1268 return IE_ERROR;
1270 return IE_SUCCESS;
1274 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
1275 * It respects microseconds too.
1276 * In case of error, @time will be freed. */
1277 static isds_error timestring2timeval(const xmlChar *string,
1278 struct timeval **time) {
1279 struct tm broken;
1280 char *offset, *delim, *endptr;
1281 char subseconds[7];
1282 int offset_hours, offset_minutes;
1283 int i;
1285 if (!time) return IE_INVAL;
1287 memset(&broken, 0, sizeof(broken));
1289 if (!*time) {
1290 *time = calloc(1, sizeof(**time));
1291 if (!*time) return IE_NOMEM;
1292 } else {
1293 memset(*time, 0, sizeof(**time));
1297 /* xsd:date is ISO 8601 string, thus ASCII */
1298 /*TODO: negative year */
1300 /* Parse date and time without subseconds and offset */
1301 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
1302 if (!offset) {
1303 free(*time); *time = NULL;
1304 return IE_DATE;
1307 /* Get subseconds */
1308 if (*offset == '.' ) {
1309 offset++;
1311 /* Copy first 6 digits, padd it with zeros.
1312 * XXX: It truncates longer number, no round.
1313 * Current server implementation uses only milisecond resolution. */
1314 /* TODO: isdigit() is locale sensitive */
1315 for (i = 0;
1316 i < sizeof(subseconds)/sizeof(char) - 1 && isdigit(*offset);
1317 i++, offset++) {
1318 subseconds[i] = *offset;
1320 for (; i < sizeof(subseconds)/sizeof(char) - 1; i++) {
1321 subseconds[i] = '0';
1323 subseconds[6] = '\0';
1325 /* Convert it into integer */
1326 (*time)->tv_usec = strtol(subseconds, &endptr, 10);
1327 if (*endptr != '\0' || (*time)->tv_usec == LONG_MIN ||
1328 (*time)->tv_usec == LONG_MAX) {
1329 free(*time); *time = NULL;
1330 return IE_DATE;
1333 /* move to the zone offset delimiter */
1334 delim = strchr(offset, '-');
1335 if (!delim)
1336 delim = strchr(offset, '+');
1337 offset = delim;
1340 /* Get zone offset */
1341 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
1342 * "" equals to "Z" and it means UTC zone. */
1343 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
1344 * colon separator */
1345 if (*offset == '-' || *offset == '+') {
1346 offset++;
1347 if (2 != sscanf(offset, "%2d:%2d", &offset_hours, &offset_minutes)) {
1348 free(*time); *time = NULL;
1349 return IE_DATE;
1351 broken.tm_hour -= offset_hours;
1352 broken.tm_min -= offset_minutes * ((offset_hours<0) ? -1 : 1);
1355 /* Convert to time_t */
1356 switch_tz_to_utc();
1357 (*time)->tv_sec = mktime(&broken);
1358 switch_tz_to_native();
1359 if ((*time)->tv_sec == (time_t) -1) {
1360 free(*time); *time = NULL;
1361 return IE_DATE;
1364 return IE_SUCCESS;
1368 /* Convert unsigned int into isds_message_status.
1369 * @context is session context
1370 * @number is pointer to number value. NULL will be treated as invalid value.
1371 * @status is automatically reallocated status
1372 * @return IE_SUCCESS, or error code and free status */
1373 static isds_error uint2isds_message_status(struct isds_ctx *context,
1374 const unsigned long int *number, isds_message_status **status) {
1375 if (!context) return IE_INVALID_CONTEXT;
1376 if (!status) return IE_INVAL;
1378 free(*status); *status = NULL;
1379 if (!number) return IE_INVAL;
1381 if (*number < 1 || *number > 9) {
1382 isds_printf_message(context, _("Invalid messsage status value: %lu"),
1383 *number);
1384 return IE_ENUM;
1387 *status = malloc(sizeof(**status));
1388 if (!*status) return IE_NOMEM;
1390 **status = 1 << *number;
1391 return IE_SUCCESS;
1395 /* Convert event description string into isds_event memebers type and
1396 * description
1397 * @string is raw event decsription starting with event prefix
1398 * @event is structure where to store type and stripped description to
1399 * @return standard error code, unkown prefix is not classified as an error. */
1400 static isds_error eventstring2event(const xmlChar *string,
1401 struct isds_event* event) {
1402 const xmlChar *known_prefixes[] = {
1403 BAD_CAST "EV1:",
1404 BAD_CAST "EV2:",
1405 BAD_CAST "EV3:"
1407 const isds_event_type types[] = {
1408 EVENT_ACCEPTED_BY_RECIPIENT,
1409 EVENT_ACCEPTED_BY_FICTION,
1410 EVENT_UNDELIVERABLE
1412 unsigned int index;
1413 size_t length;
1415 if (!string || !event) return IE_INVAL;
1417 if (!event->type) {
1418 event->type = malloc(sizeof(*event->type));
1419 if (!(event->type)) return IE_NOMEM;
1421 zfree(event->description);
1423 for (index = 0; index < sizeof(known_prefixes)/sizeof(known_prefixes[0]);
1424 index++) {
1425 length = xmlUTF8Strlen(known_prefixes[index]);
1427 if (!xmlStrncmp(string, known_prefixes[index], length)) {
1428 /* Prefix is known */
1429 *event->type = types[index];
1431 /* Strip prefix from description and spaces */
1432 /* TODO: Recognize all wite spaces from UCS blank class and
1433 * operate on UTF-8 chars. */
1434 for (; string[length] != '\0' && string[length] == ' '; length++);
1435 event->description = strdup((char *) (string + length));
1436 if (!(event->description)) return IE_NOMEM;
1438 return IE_SUCCESS;
1442 /* Unknown event prefix.
1443 * XSD allows any string */
1444 char *string_locale = utf82locale((char *) string);
1445 isds_log(ILF_ISDS, ILL_WARNING,
1446 _("Uknown delivery info event prefix: %s\n"), string_locale);
1447 free(string_locale);
1449 *event->type = EVENT_UKNOWN;
1450 event->description = strdup((char *) string);
1451 if (!(event->description)) return IE_NOMEM;
1453 return IE_SUCCESS;
1457 /* Following EXTRACT_* macros expects @result, @xpath_ctx, @err, @context
1458 * and leave lable */
1459 #define EXTRACT_STRING(element, string) \
1460 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
1461 if (!result) { \
1462 err = IE_ERROR; \
1463 goto leave; \
1465 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
1466 if (result->nodesetval->nodeNr > 1) { \
1467 isds_log_message(context, _("Multiple " element " element")); \
1468 err = IE_ERROR; \
1469 goto leave; \
1471 (string) = (char *) \
1472 xmlXPathCastNodeSetToString(result->nodesetval); \
1473 if (!(string)) { \
1474 err = IE_ERROR; \
1475 goto leave; \
1479 #define EXTRACT_BOOLEAN(element, booleanPtr) \
1481 char *string = NULL; \
1482 EXTRACT_STRING(element, string); \
1484 if (string) { \
1485 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
1486 if (!(booleanPtr)) { \
1487 free(string); \
1488 err = IE_NOMEM; \
1489 goto leave; \
1492 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
1493 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
1494 *(booleanPtr) = 1; \
1495 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
1496 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
1497 *(booleanPtr) = 0; \
1498 else { \
1499 char *string_locale = utf82locale((char*)string); \
1500 isds_printf_message(context, \
1501 _(element " value is not valid boolean: "), \
1502 string_locale); \
1503 free(string_locale); \
1504 free(string); \
1505 err = IE_ERROR; \
1506 goto leave; \
1509 free(string); \
1513 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
1515 char *string = NULL; \
1516 EXTRACT_STRING(element, string); \
1517 if (string) { \
1518 long int number; \
1519 char *endptr; \
1521 number = strtol((char*)string, &endptr, 10); \
1523 if (*endptr != '\0') { \
1524 char *string_locale = utf82locale((char *)string); \
1525 isds_printf_message(context, \
1526 _(element" is not valid integer: %s"), \
1527 string_locale); \
1528 free(string_locale); \
1529 free(string); \
1530 err = IE_ISDS; \
1531 goto leave; \
1534 if (number == LONG_MIN || number == LONG_MAX) { \
1535 char *string_locale = utf82locale((char *)string); \
1536 isds_printf_message(context, \
1537 _(element " value out of range of long int: %s"), \
1538 string_locale); \
1539 free(string_locale); \
1540 free(string); \
1541 err = IE_ERROR; \
1542 goto leave; \
1545 free(string); string = NULL; \
1547 if (!(preallocated)) { \
1548 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
1549 if (!(longintPtr)) { \
1550 err = IE_NOMEM; \
1551 goto leave; \
1554 *(longintPtr) = number; \
1558 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
1560 char *string = NULL; \
1561 EXTRACT_STRING(element, string); \
1562 if (string) { \
1563 long int number; \
1564 char *endptr; \
1566 number = strtol((char*)string, &endptr, 10); \
1568 if (*endptr != '\0') { \
1569 char *string_locale = utf82locale((char *)string); \
1570 isds_printf_message(context, \
1571 _(element" is not valid integer: %s"), \
1572 string_locale); \
1573 free(string_locale); \
1574 free(string); \
1575 err = IE_ISDS; \
1576 goto leave; \
1579 if (number == LONG_MIN || number == LONG_MAX) { \
1580 char *string_locale = utf82locale((char *)string); \
1581 isds_printf_message(context, \
1582 _(element " value out of range of long int: %s"), \
1583 string_locale); \
1584 free(string_locale); \
1585 free(string); \
1586 err = IE_ERROR; \
1587 goto leave; \
1590 free(string); string = NULL; \
1591 if (number < 0) { \
1592 isds_printf_message(context, \
1593 _(element " value is negative: %ld"), number); \
1594 err = IE_ERROR; \
1595 goto leave; \
1598 if (!(preallocated)) { \
1599 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
1600 if (!(ulongintPtr)) { \
1601 err = IE_NOMEM; \
1602 goto leave; \
1605 *(ulongintPtr) = number; \
1609 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) \
1610 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
1611 NULL); \
1612 if ((required) && (!string)) { \
1613 char *attribute_locale = utf82locale(attribute); \
1614 char *element_locale = utf82locale((char *)xpath_ctx->node->name); \
1615 isds_printf_message(context, \
1616 _("Could not extract required %s attribute value from " \
1617 "%s element"), attribute_locale, element_locale); \
1618 free(element_locale); \
1619 free(attribute_locale); \
1620 err = IE_ERROR; \
1621 goto leave; \
1625 #define INSERT_STRING(parent, element, string) \
1626 node = xmlNewTextChild(parent, NULL, BAD_CAST (element), \
1627 (xmlChar *) (string)); \
1628 if (!node) { \
1629 isds_printf_message(context, _("Could not add " element " child to " \
1630 "%s element"), (parent)->name); \
1631 err = IE_ERROR; \
1632 goto leave; \
1635 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
1636 if ((booleanPtr)) { \
1637 if (*(booleanPtr)) { INSERT_STRING(parent, element, "true"); } \
1638 else { INSERT_STRING(parent, element, "false") } \
1639 } else { INSERT_STRING(parent, element, NULL) }
1641 #define INSERT_LONGINT(parent, element, longintPtr, buffer) \
1642 if ((longintPtr)) { \
1643 /* FIXME: locale sensitive */ \
1644 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
1645 err = IE_NOMEM; \
1646 goto leave; \
1648 INSERT_STRING(parent, element, buffer) \
1649 free(buffer); (buffer) = NULL; \
1650 } else { INSERT_STRING(parent, element, NULL) }
1652 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) \
1653 if ((ulongintPtr)) { \
1654 /* FIXME: locale sensitive */ \
1655 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
1656 err = IE_NOMEM; \
1657 goto leave; \
1659 INSERT_STRING(parent, element, buffer) \
1660 free(buffer); (buffer) = NULL; \
1661 } else { INSERT_STRING(parent, element, NULL) }
1663 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
1664 /* FIXME: locale sensitive */ \
1665 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
1666 err = IE_NOMEM; \
1667 goto leave; \
1669 INSERT_STRING(parent, element, buffer) \
1670 free(buffer); (buffer) = NULL; \
1672 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
1673 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
1674 (xmlChar *) (string)); \
1675 if (!attribute_node) { \
1676 isds_printf_message(context, _("Could not add " attribute \
1677 " attribute to %s element"), (parent)->name); \
1678 err = IE_ERROR; \
1679 goto leave; \
1683 /* Find child element by name in given XPath context and switch context onto
1684 * it. The child must be uniq and must exist. Otherwise failes.
1685 * @context is ISDS context
1686 * @child is child element name
1687 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
1688 * into it child. In error case, the @xpath_ctx keeps original value. */
1689 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
1690 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
1691 isds_error err = IE_SUCCESS;
1692 xmlXPathObjectPtr result = NULL;
1694 if (!context) return IE_INVALID_CONTEXT;
1695 if (!child || !xpath_ctx) return IE_INVAL;
1697 /* Find child */
1698 result = xmlXPathEvalExpression(child, xpath_ctx);
1699 if (!result) {
1700 err = IE_XML;
1701 goto leave;
1704 /* No match */
1705 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
1706 char *parent_locale = utf82locale((char*) xpath_ctx->node->name);
1707 char *child_locale = utf82locale((char*) child);
1708 isds_printf_message(context,
1709 _("%s element does not contain %s child"),
1710 parent_locale, child_locale);
1711 free(child_locale);
1712 free(parent_locale);
1713 err = IE_NOEXIST;
1714 goto leave;
1717 /* More matches */
1718 if (result->nodesetval->nodeNr > 1) {
1719 char *parent_locale = utf82locale((char*) xpath_ctx->node->name);
1720 char *child_locale = utf82locale((char*) child);
1721 isds_printf_message(context,
1722 _("%s element contains multiple %s childs"),
1723 parent_locale, child_locale);
1724 free(child_locale);
1725 free(parent_locale);
1726 err = IE_NOTUNIQ;
1727 goto leave;
1730 /* Switch context */
1731 xpath_ctx->node = result->nodesetval->nodeTab[0];
1733 leave:
1734 xmlXPathFreeObject(result);
1735 return err;
1740 /* Convert isds:dBOwnerInfo XML tree into structure
1741 * @context is ISDS context
1742 * @db_owner_info is automically reallocated box owner info structure
1743 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
1744 * In case of error @db_owner_info will be freed. */
1745 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
1746 struct isds_DbOwnerInfo **db_owner_info,
1747 xmlXPathContextPtr xpath_ctx) {
1748 isds_error err = IE_SUCCESS;
1749 xmlXPathObjectPtr result = NULL;
1750 char *string = NULL;
1752 if (!context) return IE_INVALID_CONTEXT;
1753 if (!db_owner_info) return IE_INVAL;
1754 isds_DbOwnerInfo_free(db_owner_info);
1755 if (!xpath_ctx) return IE_INVAL;
1758 *db_owner_info = calloc(1, sizeof(**db_owner_info));
1759 if (!*db_owner_info) {
1760 err = IE_NOMEM;
1761 goto leave;
1764 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
1766 EXTRACT_STRING("isds:dbType", string);
1767 if (string) {
1768 (*db_owner_info)->dbType =
1769 calloc(1, sizeof(*((*db_owner_info)->dbType)));
1770 if (!(*db_owner_info)->dbType) {
1771 err = IE_NOMEM;
1772 goto leave;
1774 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
1775 if (err) {
1776 free((*db_owner_info)->dbType);
1777 (*db_owner_info)->dbType = NULL;
1778 if (err == IE_ENUM) {
1779 err = IE_ISDS;
1780 isds_printf_message(context, _("Unknown isds:dbType: %s"),
1781 (char *)string);
1783 goto leave;
1785 free(string); string = NULL;
1788 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
1790 (*db_owner_info)->personName =
1791 calloc(1, sizeof(*((*db_owner_info)->personName)));
1792 if (!(*db_owner_info)->personName) {
1793 err = IE_NOMEM;
1794 goto leave;
1796 EXTRACT_STRING("isds:pnFirstName",
1797 (*db_owner_info)->personName->pnFirstName);
1798 EXTRACT_STRING("isds:pnMiddleName",
1799 (*db_owner_info)->personName->pnMiddleName);
1800 EXTRACT_STRING("isds:pnLastName",
1801 (*db_owner_info)->personName->pnLastName);
1802 EXTRACT_STRING("isds:pnLastNameAtBirth",
1803 (*db_owner_info)->personName->pnLastNameAtBirth);
1804 if (!(*db_owner_info)->personName->pnFirstName &&
1805 !(*db_owner_info)->personName->pnMiddleName &&
1806 !(*db_owner_info)->personName->pnLastName &&
1807 !(*db_owner_info)->personName->pnLastNameAtBirth)
1808 isds_PersonName_free(&(*db_owner_info)->personName);
1810 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
1812 (*db_owner_info)->birthInfo =
1813 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
1814 if (!(*db_owner_info)->birthInfo) {
1815 err = IE_NOMEM;
1816 goto leave;
1818 EXTRACT_STRING("isds:biDate", string);
1819 if (string) {
1820 (*db_owner_info)->birthInfo->biDate =
1821 calloc(1, sizeof(*((*db_owner_info)->birthInfo->biDate)));
1822 if (!(*db_owner_info)->birthInfo->biDate) {
1823 err = IE_NOMEM;
1824 goto leave;
1826 err = datestring2tm((xmlChar *)string,
1827 (*db_owner_info)->birthInfo->biDate);
1828 if (err) {
1829 free((*db_owner_info)->birthInfo->biDate);
1830 (*db_owner_info)->birthInfo->biDate = NULL;
1831 if (err == IE_NOTSUP) {
1832 err = IE_ISDS;
1833 isds_printf_message(context,
1834 _("Invalid isds:biDate value: %s"), (char *)string);
1836 goto leave;
1838 free(string); string = NULL;
1840 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
1841 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
1842 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
1843 if (!(*db_owner_info)->birthInfo->biDate &&
1844 !(*db_owner_info)->birthInfo->biCity &&
1845 !(*db_owner_info)->birthInfo->biCounty &&
1846 !(*db_owner_info)->birthInfo->biState)
1847 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
1849 (*db_owner_info)->address =
1850 calloc(1, sizeof(*((*db_owner_info)->address)));
1851 if (!(*db_owner_info)->address) {
1852 err = IE_NOMEM;
1853 goto leave;
1855 EXTRACT_STRING("isds:adCity",
1856 (*db_owner_info)->address->adCity);
1857 EXTRACT_STRING("isds:adStreet",
1858 (*db_owner_info)->address->adStreet);
1859 EXTRACT_STRING("isds:adNumberInStreet",
1860 (*db_owner_info)->address->adNumberInStreet);
1861 EXTRACT_STRING("isds:adNumberInMunicipality",
1862 (*db_owner_info)->address->adNumberInMunicipality);
1863 EXTRACT_STRING("isds:adZipCode",
1864 (*db_owner_info)->address->adZipCode);
1865 EXTRACT_STRING("isds:adState",
1866 (*db_owner_info)->address->adState);
1867 if (!(*db_owner_info)->address->adCity &&
1868 !(*db_owner_info)->address->adStreet &&
1869 !(*db_owner_info)->address->adNumberInStreet &&
1870 !(*db_owner_info)->address->adNumberInMunicipality &&
1871 !(*db_owner_info)->address->adZipCode &&
1872 !(*db_owner_info)->address->adState)
1873 isds_Address_free(&(*db_owner_info)->address);
1875 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
1876 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
1877 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
1878 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
1879 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
1881 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
1883 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
1884 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
1885 (*db_owner_info)->dbOpenAddressing);
1887 leave:
1888 if (err) isds_DbOwnerInfo_free(db_owner_info);
1889 free(string);
1890 xmlXPathFreeObject(result);
1891 return err;
1895 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
1896 * isds_envelope structure. The envelope is automatically allocated but not
1897 * reallocated. The date are just appended into envelope structure.
1898 * @context is ISDS context
1899 * @envelope is automically allocated message envelope structure
1900 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
1901 * In case of error @envelope will be freed. */
1902 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
1903 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
1904 isds_error err = IE_SUCCESS;
1905 xmlXPathObjectPtr result = NULL;
1907 if (!context) return IE_INVALID_CONTEXT;
1908 if (!envelope) return IE_INVAL;
1909 if (!xpath_ctx) return IE_INVAL;
1912 if (!*envelope) {
1913 /* Allocate envelope */
1914 *envelope = calloc(1, sizeof(**envelope));
1915 if (!*envelope) {
1916 err = IE_NOMEM;
1917 goto leave;
1919 } else {
1920 /* Else free former data */
1921 zfree((*envelope)->dmSenderOrgUnit);
1922 zfree((*envelope)->dmSenderOrgUnitNum);
1923 zfree((*envelope)->dbIDRecipient);
1924 zfree((*envelope)->dmRecipientOrgUnit);
1925 zfree((*envelope)->dmSenderOrgUnitNum);
1926 zfree((*envelope)->dmToHands);
1927 zfree((*envelope)->dmAnnotation);
1928 zfree((*envelope)->dmRecipientRefNumber);
1929 zfree((*envelope)->dmSenderRefNumber);
1930 zfree((*envelope)->dmRecipientIdent);
1931 zfree((*envelope)->dmSenderIdent);
1932 zfree((*envelope)->dmLegalTitleLaw);
1933 zfree((*envelope)->dmLegalTitleYear);
1934 zfree((*envelope)->dmLegalTitleSect);
1935 zfree((*envelope)->dmLegalTitlePar);
1936 zfree((*envelope)->dmLegalTitlePoint);
1937 zfree((*envelope)->dmPersonalDelivery);
1938 zfree((*envelope)->dmAllowSubstDelivery);
1941 /* Extract envelope elements added by sender or ISDS
1942 * (XSD: gMessageEnvelopeSub type) */
1943 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
1944 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
1945 (*envelope)->dmSenderOrgUnitNum, 0);
1946 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
1947 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
1948 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
1949 (*envelope)->dmSenderOrgUnitNum, 0);
1950 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
1951 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
1952 EXTRACT_STRING("isds:dmRecipientRefNumber",
1953 (*envelope)->dmRecipientRefNumber);
1954 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
1955 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
1956 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
1958 /* Extract envelope elements regarding law refference */
1959 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
1960 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
1961 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
1962 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
1963 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
1965 /* Extract envelope other elements */
1966 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
1967 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
1968 (*envelope)->dmAllowSubstDelivery);
1970 leave:
1971 if (err) isds_envelope_free(envelope);
1972 xmlXPathFreeObject(result);
1973 return err;
1978 /* Convert XSD gMessageEnvelope group of elements from XML tree into
1979 * isds_envelope structure. The envelope is automatically allocated but not
1980 * reallocated. The date are just appended into envelope structure.
1981 * @context is ISDS context
1982 * @envelope is automically allocated message envelope structure
1983 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
1984 * In case of error @envelope will be freed. */
1985 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
1986 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
1987 isds_error err = IE_SUCCESS;
1988 xmlXPathObjectPtr result = NULL;
1990 if (!context) return IE_INVALID_CONTEXT;
1991 if (!envelope) return IE_INVAL;
1992 if (!xpath_ctx) return IE_INVAL;
1995 if (!*envelope) {
1996 /* Allocate envelope */
1997 *envelope = calloc(1, sizeof(**envelope));
1998 if (!*envelope) {
1999 err = IE_NOMEM;
2000 goto leave;
2002 } else {
2003 /* Else free former data */
2004 zfree((*envelope)->dmID);
2005 zfree((*envelope)->dbIDSender);
2006 zfree((*envelope)->dmSender);
2007 zfree((*envelope)->dmSenderAddress);
2008 zfree((*envelope)->dmSenderType);
2009 zfree((*envelope)->dmRecipient);
2010 zfree((*envelope)->dmRecipientAddress);
2011 zfree((*envelope)->dmAmbiguousRecipient);
2014 /* Extract envelope elements added by ISDS
2015 * (XSD: gMessageEnvelope type) */
2016 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
2017 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
2018 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
2019 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
2020 /* XML Schema does not guaratee enumratation. It's plain xs:int. */
2021 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
2022 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
2023 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
2024 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
2025 (*envelope)->dmAmbiguousRecipient);
2027 /* Extract envelope elements added by sender and ISDS
2028 * (XSD: gMessageEnvelope type) */
2029 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
2030 if (err) goto leave;
2032 leave:
2033 if (err) isds_envelope_free(envelope);
2034 xmlXPathFreeObject(result);
2035 return err;
2039 /* Convert other envelope elements from XML tree into isds_envelope structure:
2040 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
2041 * The envelope is automatically allocated but not reallocated.
2042 * The data are just appended into envelope structure.
2043 * @context is ISDS context
2044 * @envelope is automically allocated message envelope structure
2045 * @xpath_ctx is XPath context with current node as parent desired elements
2046 * In case of error @envelope will be freed. */
2047 static isds_error append_status_size_times(struct isds_ctx *context,
2048 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2049 isds_error err = IE_SUCCESS;
2050 xmlXPathObjectPtr result = NULL;
2051 char *string = NULL;
2052 unsigned long int *unumber = NULL;
2054 if (!context) return IE_INVALID_CONTEXT;
2055 if (!envelope) return IE_INVAL;
2056 if (!xpath_ctx) return IE_INVAL;
2059 if (!*envelope) {
2060 /* Allocate new */
2061 *envelope = calloc(1, sizeof(**envelope));
2062 if (!*envelope) {
2063 err = IE_NOMEM;
2064 goto leave;
2066 } else {
2067 /* Free old data */
2068 zfree((*envelope)->dmMessageStatus);
2069 zfree((*envelope)->dmAttachmentSize);
2070 zfree((*envelope)->dmDeliveryTime);
2071 zfree((*envelope)->dmAcceptanceTime);
2075 /* dmMessageStatus element is mandatory */
2076 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
2077 if (!unumber) {
2078 isds_log_message(context,
2079 _("Missing mandatory sisds:dmMessageStatus integer"));
2080 err = IE_ISDS;
2081 goto leave;
2083 err = uint2isds_message_status(context, unumber,
2084 &((*envelope)->dmMessageStatus));
2085 if (err) {
2086 if (err == IE_ENUM) err = IE_ISDS;
2087 goto leave;
2089 free(unumber); unumber = NULL;
2091 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
2094 EXTRACT_STRING("sisds:dmDeliveryTime", string);
2095 if (string) {
2096 err = timestring2timeval((xmlChar *) string,
2097 &((*envelope)->dmDeliveryTime));
2098 if (err) {
2099 char *string_locale = utf82locale(string);
2100 if (err == IE_DATE) err = IE_ISDS;
2101 isds_printf_message(context,
2102 _("Could not convert dmDeliveryTime as ISO time: %s"),
2103 string_locale);
2104 free(string_locale);
2105 goto leave;
2107 zfree(string);
2110 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
2111 if (string) {
2112 err = timestring2timeval((xmlChar *) string,
2113 &((*envelope)->dmAcceptanceTime));
2114 if (err) {
2115 char *string_locale = utf82locale(string);
2116 if (err == IE_DATE) err = IE_ISDS;
2117 isds_printf_message(context,
2118 _("Could not convert dmAcceptanceTime as ISO time: %s"),
2119 string_locale);
2120 free(string_locale);
2121 goto leave;
2123 zfree(string);
2126 leave:
2127 if (err) isds_envelope_free(envelope);
2128 free(unumber);
2129 free(string);
2130 xmlXPathFreeObject(result);
2131 return err;
2135 /* Extract message document into reallocated document structure
2136 * @context is ISDS context
2137 * @document is automically reallocated message documents structure
2138 * @xpath_ctx is XPath context with current node as isds:dmFile
2139 * In case of error @document will be freed. */
2140 static isds_error extract_document(struct isds_ctx *context,
2141 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
2142 isds_error err = IE_SUCCESS;
2143 xmlXPathObjectPtr result = NULL;
2144 xmlNodePtr file_node = xpath_ctx->node;
2145 char *string = NULL;
2147 if (!context) return IE_INVALID_CONTEXT;
2148 if (!document) return IE_INVAL;
2149 isds_document_free(document);
2150 if (!xpath_ctx) return IE_INVAL;
2152 *document = calloc(1, sizeof(**document));
2153 if (!*document) {
2154 err = IE_NOMEM;
2155 goto leave;
2158 /* Extract document metadata */
2159 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
2161 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
2162 err = string2isds_FileMetaType((xmlChar*)string,
2163 &((*document)->dmFileMetaType));
2164 if (err) {
2165 char *meta_type_locale = utf82locale(string);
2166 isds_printf_message(context,
2167 _("Document has invalid dmFileMetaType attribute value: %s"),
2168 meta_type_locale);
2169 free(meta_type_locale);
2170 err = IE_ISDS;
2171 goto leave;
2173 zfree(string);
2175 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
2176 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
2177 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
2178 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
2181 /* Extract document data.
2182 * Base64 encoded blob or XML subtree must be presented. */
2184 /* Check from dmEncodedContent */
2185 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
2186 xpath_ctx);
2187 if (!result) {
2188 err = IE_XML;
2189 goto leave;
2192 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2193 /* Here we have Base64 blob */
2195 if (result->nodesetval->nodeNr > 1) {
2196 isds_printf_message(context,
2197 _("Document has more dmEncodedContent elements"));
2198 err = IE_ISDS;
2199 goto leave;
2202 xmlXPathFreeObject(result); result = NULL;
2203 EXTRACT_STRING("isds:dmEncodedContent", string);
2205 /* Decode non-emptys document */
2206 if (string && string[0] != '\0') {
2207 (*document)->data_length = b64decode(string, &((*document)->data));
2208 if ((*document)->data_length == (size_t) -1) {
2209 isds_printf_message(context,
2210 _("Error while Base64-decoding document content"));
2211 err = IE_ERROR;
2212 goto leave;
2215 } else {
2216 /* No Base64 blob, try XML document */
2217 xmlXPathFreeObject(result); result = NULL;
2218 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
2219 xpath_ctx);
2220 if (!result) {
2221 err = IE_XML;
2222 goto leave;
2225 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2226 /* Here we have XML document */
2228 if (result->nodesetval->nodeNr > 1) {
2229 isds_printf_message(context,
2230 _("Document has more dmXMLContent elements"));
2231 err = IE_ISDS;
2232 goto leave;
2235 /* FIXME: Serialize the tree rooted at result's node */
2236 isds_printf_message(context,
2237 _("XML documents not yet supported"));
2238 err = IE_NOTSUP;
2239 goto leave;
2240 } else {
2241 /* No bas64 blob, nor XML document */
2242 isds_printf_message(context,
2243 _("Document has no dmEncodedContent, nor dmXMLContent "
2244 "element"));
2245 err = IE_ISDS;
2246 goto leave;
2251 leave:
2252 if (err) isds_document_free(document);
2253 free(string);
2254 xmlXPathFreeObject(result);
2255 xpath_ctx->node = file_node;
2256 return err;
2261 /* Extract message documents into reallocated list of documents
2262 * @context is ISDS context
2263 * @documents is automically reallocated message documents list structure
2264 * @xpath_ctx is XPath context with current node as XSD tFilesArray
2265 * In case of error @documents will be freed. */
2266 static isds_error extract_documents(struct isds_ctx *context,
2267 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
2268 isds_error err = IE_SUCCESS;
2269 xmlXPathObjectPtr result = NULL;
2270 xmlNodePtr files_node = xpath_ctx->node;
2271 struct isds_list *document, *prev_document;
2273 if (!context) return IE_INVALID_CONTEXT;
2274 if (!documents) return IE_INVAL;
2275 isds_list_free(documents);
2276 if (!xpath_ctx) return IE_INVAL;
2278 /* Find documents */
2279 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
2280 if (!result) {
2281 err = IE_XML;
2282 goto leave;
2285 /* No match */
2286 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2287 isds_printf_message(context,
2288 _("Message does not contain any document"));
2289 err = IE_ISDS;
2290 goto leave;
2294 /* Iterate over documents */
2295 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
2297 /* Allocate and append list item */
2298 document = calloc(1, sizeof(*document));
2299 if (!document) {
2300 err = IE_NOMEM;
2301 goto leave;
2303 document->destructor = (void (*)(void **))isds_document_free;
2304 if (i == 0) *documents = document;
2305 else prev_document->next = document;
2306 prev_document = document;
2308 /* Extract document */
2309 xpath_ctx->node = result->nodesetval->nodeTab[i];
2310 err = extract_document(context,
2311 (struct isds_document **) &(document->data), xpath_ctx);
2312 if (err) goto leave;
2316 leave:
2317 if (err) isds_list_free(documents);
2318 xmlXPathFreeObject(result);
2319 xpath_ctx->node = files_node;
2320 return err;
2324 /* Convert isds:dmRecord XML tree into structure
2325 * @context is ISDS context
2326 * @envelope is automically reallocated message envelope structure
2327 * @xpath_ctx is XPath context with current node as isds:dmRecord element
2328 * In case of error @envelope will be freed. */
2329 static isds_error extract_DmRecord(struct isds_ctx *context,
2330 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2331 isds_error err = IE_SUCCESS;
2332 xmlXPathObjectPtr result = NULL;
2334 if (!context) return IE_INVALID_CONTEXT;
2335 if (!envelope) return IE_INVAL;
2336 isds_envelope_free(envelope);
2337 if (!xpath_ctx) return IE_INVAL;
2340 *envelope = calloc(1, sizeof(**envelope));
2341 if (!*envelope) {
2342 err = IE_NOMEM;
2343 goto leave;
2347 /* Extract tRecord data */
2348 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
2350 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
2351 * dmAcceptanceTime. */
2352 err = append_status_size_times(context, envelope, xpath_ctx);
2353 if (err) goto leave;
2355 /* Extract envelope elements added by sender and ISDS
2356 * (XSD: gMessageEnvelope type) */
2357 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
2358 if (err) goto leave;
2359 /* dmOVM can not be obtained from ISDS */
2361 leave:
2362 if (err) isds_envelope_free(envelope);
2363 xmlXPathFreeObject(result);
2364 return err;
2368 /* Find and convert isds:dmHash XML tree into structure
2369 * @context is ISDS context
2370 * @envelope is automically reallocated message hash structure
2371 * @xpath_ctx is XPath context with current node containing isds:dmHash child
2372 * In case of error @hash will be freed. */
2373 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
2374 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
2375 isds_error err = IE_SUCCESS;
2376 xmlNodePtr old_ctx_node;
2377 xmlXPathObjectPtr result = NULL;
2378 char *string = NULL;
2380 if (!context) return IE_INVALID_CONTEXT;
2381 if (!hash) return IE_INVAL;
2382 isds_hash_free(hash);
2383 if (!xpath_ctx) return IE_INVAL;
2386 *hash = calloc(1, sizeof(**hash));
2387 if (!*hash) {
2388 err = IE_NOMEM;
2389 goto leave;
2392 old_ctx_node = xpath_ctx->node;
2394 /* Locate dmHash */
2395 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
2396 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
2397 err = IE_ISDS;
2398 goto leave;
2400 if (err) {
2401 err = IE_ERROR;
2402 goto leave;
2405 /* Get hash algorithm */
2406 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
2407 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
2408 if (err) {
2409 if (err == IE_ENUM) {
2410 char *string_locale = utf82locale(string);
2411 isds_printf_message(context, _("Unsported hash algorithm: %s"),
2412 string_locale);
2413 free(string_locale);
2415 goto leave;
2417 zfree(string);
2419 /* Get hash value */
2420 EXTRACT_STRING(".", string);
2421 if (!string) {
2422 isds_printf_message(context, _("tHash element is missing hash value"));
2423 err = IE_ISDS;
2424 goto leave;
2426 (*hash)->length = b64decode(string, &((*hash)->value));
2427 if ((*hash)->length == (size_t) -1) {
2428 isds_printf_message(context,
2429 _("Error while Base64-decoding hash value"));
2430 err = IE_ERROR;
2431 goto leave;
2434 leave:
2435 if (err) isds_hash_free(hash);
2436 free(string);
2437 xmlXPathFreeObject(result);
2438 xpath_ctx->node = old_ctx_node;
2439 return err;
2443 /* Find and append isds:dmQTimestamp XML tree into envelope
2444 * @context is ISDS context
2445 * @envelope is automically allocated evnelope structure
2446 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
2447 * child
2448 * In case of error @envelope will be freed. */
2449 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
2450 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2451 isds_error err = IE_SUCCESS;
2452 xmlXPathObjectPtr result = NULL;
2453 char *string = NULL;
2455 if (!context) return IE_INVALID_CONTEXT;
2456 if (!envelope) return IE_INVAL;
2457 if (!xpath_ctx) {
2458 isds_envelope_free(envelope);
2459 return IE_INVAL;
2462 if (!*envelope) {
2463 *envelope = calloc(1, sizeof(**envelope));
2464 if (!*envelope) {
2465 err = IE_NOMEM;
2466 goto leave;
2468 } else {
2469 zfree((*envelope)->timestamp);
2470 (*envelope)->timestamp_length = 0;
2473 /* Get dmQTimestamp */
2474 EXTRACT_STRING("sisds:dmQTimestamp", string);
2475 if (!string) {
2476 isds_printf_message(context, _("Missing dmQTimestamp element content"));
2477 err = IE_ISDS;
2478 goto leave;
2480 (*envelope)->timestamp_length =
2481 b64decode(string, &((*envelope)->timestamp));
2482 if ((*envelope)->timestamp_length == (size_t) -1) {
2483 isds_printf_message(context,
2484 _("Error while Base64-decoding timestamp value"));
2485 err = IE_ERROR;
2486 goto leave;
2489 leave:
2490 if (err) isds_envelope_free(envelope);
2491 free(string);
2492 xmlXPathFreeObject(result);
2493 return err;
2497 /* Convert XSD tReturnedMessage XML tree into message structure.
2498 * It doea not store XML tree into message->raw.
2499 * @context is ISDS context
2500 * @include_documents Use true if documents must be extracted
2501 * (tReturnedMessage XSD type), use false if documents shall be ommited
2502 * (tReturnedMessageEnvelope).
2503 * @message is automically reallocated message structure
2504 * @xpath_ctx is XPath context with current node as tReturnedMessage element
2505 * type
2506 * In case of error @message will be freed. */
2507 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
2508 const _Bool include_documents, struct isds_message **message,
2509 xmlXPathContextPtr xpath_ctx) {
2510 isds_error err = IE_SUCCESS;
2511 xmlNodePtr message_node;
2513 if (!context) return IE_INVALID_CONTEXT;
2514 if (!message) return IE_INVAL;
2515 isds_message_free(message);
2516 if (!xpath_ctx) return IE_INVAL;
2519 *message = calloc(1, sizeof(**message));
2520 if (!*message) {
2521 err = IE_NOMEM;
2522 goto leave;
2525 /* Save message XPATH context node */
2526 message_node = xpath_ctx->node;
2529 /* Extract dmDM */
2530 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
2531 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
2532 if (err) { err = IE_ERROR; goto leave; }
2533 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
2534 if (err) goto leave;
2536 if (include_documents) {
2537 /* Extract dmFiles */
2538 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
2539 xpath_ctx);
2540 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
2541 err = IE_ISDS; goto leave;
2543 if (err) { err = IE_ERROR; goto leave; }
2544 err = extract_documents(context, &((*message)->documents), xpath_ctx);
2545 if (err) goto leave;
2549 /* Restore context to message */
2550 xpath_ctx->node = message_node;
2552 /* Extract dmHash */
2553 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
2554 xpath_ctx);
2555 if (err) goto leave;
2557 /* Extract dmQTimestamp, */
2558 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
2559 xpath_ctx);
2560 if (err) goto leave;
2562 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
2563 * dmAcceptanceTime. */
2564 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
2565 if (err) goto leave;
2567 leave:
2568 if (err) isds_message_free(message);
2569 return err;
2573 /* Extract message event into reallocated isds_event structure
2574 * @context is ISDS context
2575 * @event is automically reallocated message event structure
2576 * @xpath_ctx is XPath context with current node as isds:dmEvent
2577 * In case of error @event will be freed. */
2578 static isds_error extract_event(struct isds_ctx *context,
2579 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
2580 isds_error err = IE_SUCCESS;
2581 xmlXPathObjectPtr result = NULL;
2582 xmlNodePtr event_node = xpath_ctx->node;
2583 char *string = NULL;
2585 if (!context) return IE_INVALID_CONTEXT;
2586 if (!event) return IE_INVAL;
2587 isds_event_free(event);
2588 if (!xpath_ctx) return IE_INVAL;
2590 *event = calloc(1, sizeof(**event));
2591 if (!*event) {
2592 err = IE_NOMEM;
2593 goto leave;
2596 /* Extract event data.
2597 * All elements are optional according XSD. That's funny. */
2598 EXTRACT_STRING("sisds:dmEventTime", string);
2599 if (string) {
2600 err = timestring2timeval((xmlChar *) string, &((*event)->time));
2601 if (err) {
2602 char *string_locale = utf82locale(string);
2603 if (err == IE_DATE) err = IE_ISDS;
2604 isds_printf_message(context,
2605 _("Could not convert dmEventTime as ISO time: %s"),
2606 string_locale);
2607 free(string_locale);
2608 goto leave;
2610 zfree(string);
2613 /* dmEventDescr element has prefix and the rest */
2614 EXTRACT_STRING("sisds:dmEventDescr", string);
2615 if (string) {
2616 err = eventstring2event((xmlChar *) string, *event);
2617 if (err) goto leave;
2618 zfree(string);
2621 leave:
2622 if (err) isds_event_free(event);
2623 free(string);
2624 xmlXPathFreeObject(result);
2625 xpath_ctx->node = event_node;
2626 return err;
2630 /* Convert element of XSD tEventsArray type from XML tree into
2631 * isds_list of isds_event's structure. The list is automatically reallocated.
2632 * @context is ISDS context
2633 * @events is automically reallocated list of event structures
2634 * @xpath_ctx is XPath context with current node as tEventsArray
2635 * In case of error @evnets will be freed. */
2636 static isds_error extract_events(struct isds_ctx *context,
2637 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
2638 isds_error err = IE_SUCCESS;
2639 xmlXPathObjectPtr result = NULL;
2640 xmlNodePtr events_node = xpath_ctx->node;
2641 struct isds_list *event, *prev_event;
2643 if (!context) return IE_INVALID_CONTEXT;
2644 if (!events) return IE_INVAL;
2645 if (!xpath_ctx) return IE_INVAL;
2647 /* Free old list */
2648 isds_list_free(events);
2650 /* Find events */
2651 result = xmlXPathEvalExpression(BAD_CAST "sisds:dmEvent", xpath_ctx);
2652 if (!result) {
2653 err = IE_XML;
2654 goto leave;
2657 /* No match */
2658 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2659 isds_printf_message(context,
2660 _("Delivery info does not contain any event"));
2661 err = IE_ISDS;
2662 goto leave;
2666 /* Iterate over events */
2667 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
2669 /* Allocate and append list item */
2670 event = calloc(1, sizeof(*event));
2671 if (!event) {
2672 err = IE_NOMEM;
2673 goto leave;
2675 event->destructor = (void (*)(void **))isds_event_free;
2676 if (i == 0) *events = event;
2677 else prev_event->next = event;
2678 prev_event = event;
2680 /* Extract event */
2681 xpath_ctx->node = result->nodesetval->nodeTab[i];
2682 err = extract_event(context,
2683 (struct isds_event **) &(event->data), xpath_ctx);
2684 if (err) goto leave;
2688 leave:
2689 if (err) isds_list_free(events);
2690 xmlXPathFreeObject(result);
2691 xpath_ctx->node = events_node;
2692 return err;
2696 /* Convert isds_document structure into XML tree and append to dmFiles node.
2697 * @context is session context
2698 * @document is ISDS document
2699 * @dm_files is XML element the resulting tree will be appended to as a child.
2700 * @return error code, in case of error context' message is filled. */
2701 static isds_error insert_document(struct isds_ctx *context,
2702 struct isds_document *document, xmlNodePtr dm_files) {
2703 isds_error err = IE_SUCCESS;
2704 xmlNodePtr new_file = NULL, file = NULL, node;
2705 xmlAttrPtr attribute_node;
2706 xmlChar *base64data = NULL;
2708 if (!context) return IE_INVALID_CONTEXT;
2709 if (!document || !dm_files) return IE_INVAL;
2711 /* Allocate new dmFile */
2712 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
2713 if (!new_file) {
2714 isds_printf_message(context, _("Could not allocate main dmFile"));
2715 err = IE_ERROR;
2716 goto leave;
2718 /* Append the new dmFile.
2719 * XXX: Main document must go first */
2720 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
2721 file = xmlAddPrevSibling(dm_files->children, new_file);
2722 else
2723 file = xmlAddChild(dm_files, new_file);
2725 if (!file) {
2726 xmlFreeNode(new_file); new_file = NULL;
2727 isds_printf_message(context, _("Could not add dmFile child to "
2728 "%s element"), dm_files->name);
2729 err = IE_ERROR;
2730 goto leave;
2733 /* @dmMimeType is required */
2734 if (!document->dmMimeType) {
2735 isds_log_message(context,
2736 _("Document is missing mandatory MIME type definition"));
2737 err = IE_INVAL;
2738 goto leave;
2740 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
2742 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
2743 if (!string) {
2744 isds_printf_message(context,
2745 _("Document has unkown dmFileMetaType: %ld"),
2746 document->dmFileMetaType);
2747 err = IE_ENUM;
2748 goto leave;
2750 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
2752 if (document->dmFileGuid) {
2753 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
2755 if (document->dmUpFileGuid) {
2756 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
2759 /* @dmFileDescr is required */
2760 if (!document->dmFileDescr) {
2761 isds_log_message(context,
2762 _("Document is missing mandatory description (title)"));
2763 err = IE_INVAL;
2764 goto leave;
2766 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
2768 if (document->dmFormat) {
2769 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
2773 /* Insert content (data) of the document. */
2774 /* XXX; Only base64 is implemented currently. */
2775 base64data = (xmlChar *) b64encode(document->data, document->data_length);
2776 if (!base64data) {
2777 isds_printf_message(context,
2778 _("Not enought memory to encode %zd bytes into Base64"),
2779 document->data_length);
2780 err = IE_NOMEM;
2781 goto leave;
2783 INSERT_STRING(file, "dmEncodedContent", base64data);
2784 free(base64data);
2786 leave:
2787 return err;
2791 /* Get data about logged in user and his box. */
2792 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
2793 struct isds_DbOwnerInfo **db_owner_info) {
2794 isds_error err = IE_SUCCESS;
2795 xmlNsPtr isds_ns = NULL;
2796 xmlNodePtr request = NULL;
2797 xmlDocPtr response = NULL;
2798 xmlChar *code = NULL, *message = NULL;
2799 xmlNodePtr node;
2800 xmlXPathContextPtr xpath_ctx = NULL;
2801 xmlXPathObjectPtr result = NULL;
2802 char *string = NULL;
2804 if (!context) return IE_INVALID_CONTEXT;
2805 if (!db_owner_info) return IE_INVAL;
2807 /* Check if connection is established */
2808 if (!context->curl) return IE_CONNECTION_CLOSED;
2811 /* Build GetOwnerInfoFromLogin request */
2812 request = xmlNewNode(NULL, BAD_CAST "GetOwnerInfoFromLogin");
2813 if (!request) {
2814 isds_log_message(context,
2815 _("Could build GetOwnerInfoFromLogin request"));
2816 return IE_ERROR;
2818 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
2819 if(!isds_ns) {
2820 isds_log_message(context, _("Could not create ISDS name space"));
2821 xmlFreeNode(request);
2822 return IE_ERROR;
2824 xmlSetNs(request, isds_ns);
2825 node = xmlNewChild(request, NULL, BAD_CAST "dbDummy", NULL);
2826 if (!node) {
2827 isds_log_message(context, _("Could nod add dbDummy Child to "
2828 "GetOwnerInfoFromLogin element"));
2829 xmlFreeNode(request);
2830 return IE_ERROR;
2834 isds_log(ILF_ISDS, ILL_DEBUG,
2835 _("Sending GetOwnerInfoFromLogin request to ISDS\n"));
2837 /* Sent request */
2838 err = isds(context, SERVICE_DB_SUPPLEMENTARY, request, &response);
2840 /* Destroy request */
2841 xmlFreeNode(request);
2843 if (err) {
2844 isds_log(ILF_ISDS, ILL_DEBUG,
2845 _("Processing ISDS response on GetOwnerInfoFromLogin "
2846 "request failed\n"));
2847 xmlFreeDoc(response);
2848 return err;
2851 /* Check for response status */
2852 err = isds_response_status(context, SERVICE_DB_SUPPLEMENTARY, response,
2853 &code, &message, NULL);
2854 if (err) {
2855 isds_log(ILF_ISDS, ILL_DEBUG,
2856 _("ISDS response on GetOwnerInfoFromLogin request is "
2857 "missing status\n"));
2858 free(code);
2859 free(message);
2860 xmlFreeDoc(response);
2861 return err;
2863 if (xmlStrcmp(code, BAD_CAST "0000")) {
2864 char *code_locale = utf82locale((char*)code);
2865 char *message_locale = utf82locale((char*)message);
2866 isds_log(ILF_ISDS, ILL_DEBUG,
2867 _("Server refused GetOwnerInfoFromLogin request "
2868 "(code=%s, message=%s)\n"), code_locale, message_locale);
2869 isds_log_message(context, message_locale);
2870 free(code_locale);
2871 free(message_locale);
2872 free(code);
2873 free(message);
2874 xmlFreeDoc(response);
2875 return IE_ISDS;
2878 /* Extract data */
2879 /* Prepare stucture */
2880 isds_DbOwnerInfo_free(db_owner_info);
2881 *db_owner_info = calloc(1, sizeof(**db_owner_info));
2882 if (!*db_owner_info) {
2883 err = IE_NOMEM;
2884 goto leave;
2886 xpath_ctx = xmlXPathNewContext(response);
2887 if (!xpath_ctx) {
2888 err = IE_ERROR;
2889 goto leave;
2891 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
2892 err = IE_ERROR;
2893 goto leave;
2896 /* Set context node */
2897 result = xmlXPathEvalExpression(BAD_CAST
2898 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
2899 if (!result) {
2900 err = IE_ERROR;
2901 goto leave;
2903 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2904 isds_log_message(context, _("Missing dbOwnerInfo element"));
2905 err = IE_ISDS;
2906 goto leave;
2908 if (result->nodesetval->nodeNr > 1) {
2909 isds_log_message(context, _("Multiple dbOwnerInfo element"));
2910 err = IE_ISDS;
2911 goto leave;
2913 xpath_ctx->node = result->nodesetval->nodeTab[0];
2914 xmlXPathFreeObject(result); result = NULL;
2916 /* Extract it */
2917 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
2919 leave:
2920 if (err) {
2921 isds_DbOwnerInfo_free(db_owner_info);
2924 free(string);
2925 xmlXPathFreeObject(result);
2926 xmlXPathFreeContext(xpath_ctx);
2928 free(code);
2929 free(message);
2930 xmlFreeDoc(response);
2932 if (!err)
2933 isds_log(ILF_ISDS, ILL_DEBUG,
2934 _("GetOwnerInfoFromLogin request processed by server "
2935 "successfully.\n"));
2937 return err;
2941 /* Find boxes suiting given criteria.
2942 * @criteria is filter. You should fill in at least some memebers.
2943 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
2944 * possibly empty. Input NULL or valid old structure.
2945 * @return:
2946 * IE_SUCCESS if search sucseeded, @boxes contains usefull data
2947 * IE_NOEXIST if no such box exists, @boxes will be NULL
2948 * IE_2BIG if too much boxes exist and server truncated the resuluts, @boxes
2949 * contains still valid data
2950 * other code if something bad happens. @boxes will be NULL. */
2951 isds_error isds_FindDataBox(struct isds_ctx *context,
2952 const struct isds_DbOwnerInfo *criteria,
2953 struct isds_list **boxes) {
2954 isds_error err = IE_SUCCESS;
2955 _Bool truncated = 0;
2956 xmlNsPtr isds_ns = NULL;
2957 xmlNodePtr request = NULL;
2958 xmlDocPtr response = NULL;
2959 xmlChar *code = NULL, *message = NULL;
2960 xmlNodePtr db_owner_info, node;
2961 xmlXPathContextPtr xpath_ctx = NULL;
2962 xmlXPathObjectPtr result = NULL;
2963 xmlChar *string = NULL;
2966 if (!context) return IE_INVALID_CONTEXT;
2967 if (!boxes) return IE_INVAL;
2968 isds_list_free(boxes);
2970 if (!criteria) {
2971 return IE_INVAL;
2974 /* Check if connection is established
2975 * TODO: This check should be done donwstairs. */
2976 if (!context->curl) return IE_CONNECTION_CLOSED;
2979 /* Build FindDataBox request */
2980 request = xmlNewNode(NULL, BAD_CAST "FindDataBox");
2981 if (!request) {
2982 isds_log_message(context,
2983 _("Could build FindDataBox request"));
2984 return IE_ERROR;
2986 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
2987 if(!isds_ns) {
2988 isds_log_message(context, _("Could not create ISDS name space"));
2989 xmlFreeNode(request);
2990 return IE_ERROR;
2992 xmlSetNs(request, isds_ns);
2993 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
2994 if (!db_owner_info) {
2995 isds_log_message(context, _("Could not add dbOwnerInfo Child to "
2996 "FindDataBox element"));
2997 xmlFreeNode(request);
2998 return IE_ERROR;
3002 INSERT_STRING(db_owner_info, "dbID", criteria->dbID);
3004 /* dbType */
3005 if (criteria->dbType) {
3006 const xmlChar *type_string = isds_DbType2string(*(criteria->dbType));
3007 if (!type_string) {
3008 isds_printf_message(context, _("Invalid dbType value: %d"),
3009 *(criteria->dbType));
3010 err = IE_ENUM;
3011 goto leave;
3013 INSERT_STRING(db_owner_info, "dbType", type_string);
3016 INSERT_STRING(db_owner_info, "firmName", criteria->firmName);
3017 INSERT_STRING(db_owner_info, "ic", criteria->ic);
3018 if (criteria->personName) {
3019 INSERT_STRING(db_owner_info, "pnFirstName",
3020 criteria->personName->pnFirstName);
3021 INSERT_STRING(db_owner_info, "pnMiddleName",
3022 criteria->personName->pnMiddleName);
3023 INSERT_STRING(db_owner_info, "pnLastName",
3024 criteria->personName->pnLastName);
3025 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
3026 criteria->personName->pnLastNameAtBirth);
3028 if (criteria->birthInfo) {
3029 if (criteria->birthInfo->biDate) {
3030 if (!tm2datestring(criteria->birthInfo->biDate, &string))
3031 INSERT_STRING(db_owner_info, "biDate", string);
3032 free(string); string = NULL;
3034 INSERT_STRING(db_owner_info, "biCity", criteria->birthInfo->biCity);
3035 INSERT_STRING(db_owner_info, "biCounty", criteria->birthInfo->biCounty);
3036 INSERT_STRING(db_owner_info, "biState", criteria->birthInfo->biState);
3038 if (criteria->address) {
3039 INSERT_STRING(db_owner_info, "adCity", criteria->address->adCity);
3040 INSERT_STRING(db_owner_info, "adStreet", criteria->address->adStreet);
3041 INSERT_STRING(db_owner_info, "adNumberInStreet",
3042 criteria->address->adNumberInStreet);
3043 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
3044 criteria->address->adNumberInMunicipality);
3045 INSERT_STRING(db_owner_info, "adZipCode", criteria->address->adZipCode);
3046 INSERT_STRING(db_owner_info, "adState", criteria->address->adState);
3048 INSERT_STRING(db_owner_info, "nationality", criteria->nationality);
3049 INSERT_STRING(db_owner_info, "email", criteria->email);
3050 INSERT_STRING(db_owner_info, "telNumber", criteria->telNumber);
3051 INSERT_STRING(db_owner_info, "identifier", criteria->identifier);
3052 INSERT_STRING(db_owner_info, "registryCode", criteria->registryCode);
3054 INSERT_LONGINT(db_owner_info, "dbState", criteria->dbState, string);
3056 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", criteria->dbEffectiveOVM);
3057 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
3058 criteria->dbOpenAddressing);
3061 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending FindDataBox request to ISDS\n"));
3063 /* Sent request */
3064 err = isds(context, SERVICE_DB_SEARCH, request, &response);
3066 /* Destroy request */
3067 xmlFreeNode(request); request = NULL;
3069 if (err) {
3070 isds_log(ILF_ISDS, ILL_DEBUG,
3071 _("Processing ISDS response on FindDataBox "
3072 "request failed\n"));
3073 goto leave;
3076 /* Check for response status */
3077 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
3078 &code, &message, NULL);
3079 if (err) {
3080 isds_log(ILF_ISDS, ILL_DEBUG,
3081 _("ISDS response on FindDataBox request is missing status\n"));
3082 goto leave;
3085 /* Request processed, but nothing found */
3086 if (!xmlStrcmp(code, BAD_CAST "0002") ||
3087 !xmlStrcmp(code, BAD_CAST "5001")) {
3088 char *code_locale = utf82locale((char*)code);
3089 char *message_locale = utf82locale((char*)message);
3090 isds_log(ILF_ISDS, ILL_DEBUG,
3091 _("Server did not found any box on FindDataBox request "
3092 "(code=%s, message=%s)\n"), code_locale, message_locale);
3093 isds_log_message(context, message_locale);
3094 free(code_locale);
3095 free(message_locale);
3096 err = IE_NOEXIST;
3097 goto leave;
3100 /* Warning, not a error */
3101 if (!xmlStrcmp(code, BAD_CAST "0003")) {
3102 char *code_locale = utf82locale((char*)code);
3103 char *message_locale = utf82locale((char*)message);
3104 isds_log(ILF_ISDS, ILL_DEBUG,
3105 _("Server truncated response on FindDataBox request "
3106 "(code=%s, message=%s)\n"), code_locale, message_locale);
3107 isds_log_message(context, message_locale);
3108 free(code_locale);
3109 free(message_locale);
3110 truncated = 1;
3113 /* Other error */
3114 else if (xmlStrcmp(code, BAD_CAST "0000")) {
3115 char *code_locale = utf82locale((char*)code);
3116 char *message_locale = utf82locale((char*)message);
3117 isds_log(ILF_ISDS, ILL_DEBUG,
3118 _("Server refused FindDataBox request "
3119 "(code=%s, message=%s)\n"), code_locale, message_locale);
3120 isds_log_message(context, message_locale);
3121 free(code_locale);
3122 free(message_locale);
3123 err = IE_ISDS;
3124 goto leave;
3127 xpath_ctx = xmlXPathNewContext(response);
3128 if (!xpath_ctx) {
3129 err = IE_ERROR;
3130 goto leave;
3132 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
3133 err = IE_ERROR;
3134 goto leave;
3137 /* Extract boxes if they present */
3138 result = xmlXPathEvalExpression(BAD_CAST
3139 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
3140 xpath_ctx);
3141 if (!result) {
3142 err = IE_ERROR;
3143 goto leave;
3145 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3146 struct isds_list *item, *prev_item = NULL;
3147 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
3148 item = calloc(1, sizeof(*item));
3149 if (!item) {
3150 err = IE_NOMEM;
3151 goto leave;
3154 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
3155 if (i == 0) *boxes = item;
3156 else prev_item->next = item;
3157 prev_item = item;
3159 xpath_ctx->node = result->nodesetval->nodeTab[i];
3160 err = extract_DbOwnerInfo(context,
3161 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
3162 if (err) goto leave;
3166 leave:
3167 if (err) {
3168 isds_list_free(boxes);
3169 } else {
3170 if (truncated) err = IE_2BIG;
3173 free(string);
3174 xmlFreeNode(request);
3175 xmlXPathFreeObject(result);
3176 xmlXPathFreeContext(xpath_ctx);
3178 free(code);
3179 free(message);
3180 xmlFreeDoc(response);
3182 if (!err)
3183 isds_log(ILF_ISDS, ILL_DEBUG,
3184 _("FindDataBox request processed by server successfully.\n"));
3186 return err;
3190 /* Get status of a box.
3191 * @context is ISDS session context.
3192 * @box_id is UTF-8 encoded box identifier as zero terminated string
3193 * @box_status is return value of box status.
3194 * @return:
3195 * IE_SUCCESS if box has been found and its status retrieved
3196 * IE_NOEXIST if box is not known to ISDS server
3197 * or other appropriate error.
3198 * You can use isds_DbState to enumerate box status. However out of enum
3199 * range value can be returned too. This is feature because ISDS
3200 * specification leaves the set of values open.
3201 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
3202 * the box has been deleted, but ISDS still lists its former existence. */
3203 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
3204 long int *box_status) {
3205 isds_error err = IE_SUCCESS;
3206 xmlNsPtr isds_ns = NULL;
3207 xmlNodePtr request = NULL, db_id;
3208 xmlDocPtr response = NULL;
3209 xmlChar *code = NULL, *message = NULL;
3210 xmlXPathContextPtr xpath_ctx = NULL;
3211 xmlXPathObjectPtr result = NULL;
3212 xmlChar *string = NULL;
3214 if (!context) return IE_INVALID_CONTEXT;
3215 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
3217 /* Check if connection is established
3218 * TODO: This check should be done donwstairs. */
3219 if (!context->curl) return IE_CONNECTION_CLOSED;
3222 /* Build CheckDataBox request */
3223 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
3224 if (!request) {
3225 isds_log_message(context,
3226 _("Could build CheckDataBox request"));
3227 return IE_ERROR;
3229 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
3230 if(!isds_ns) {
3231 isds_log_message(context, _("Could not create ISDS name space"));
3232 xmlFreeNode(request);
3233 return IE_ERROR;
3235 xmlSetNs(request, isds_ns);
3236 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
3237 if (!db_id) {
3238 isds_log_message(context, _("Could not add dbId Child to "
3239 "CheckDataBox element"));
3240 xmlFreeNode(request);
3241 return IE_ERROR;
3245 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CheckDataBox request to ISDS\n"));
3247 /* Sent request */
3248 err = isds(context, SERVICE_DB_SEARCH, request, &response);
3250 /* Destroy request */
3251 xmlFreeNode(request);
3253 if (err) {
3254 isds_log(ILF_ISDS, ILL_DEBUG,
3255 _("Processing ISDS response on CheckDataBox "
3256 "request failed\n"));
3257 goto leave;
3260 /* Check for response status */
3261 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
3262 &code, &message, NULL);
3263 if (err) {
3264 isds_log(ILF_ISDS, ILL_DEBUG,
3265 _("ISDS response on CheckDataBox request is missing status\n"));
3266 goto leave;
3269 /* Request processed, but nothing found */
3270 if (!xmlStrcmp(code, BAD_CAST "5001")) {
3271 char *box_id_locale = utf82locale((char*)box_id);
3272 char *code_locale = utf82locale((char*)code);
3273 char *message_locale = utf82locale((char*)message);
3274 isds_log(ILF_ISDS, ILL_DEBUG,
3275 _("Server did not found box %s on CheckDataBox request "
3276 "(code=%s, message=%s)\n"),
3277 box_id_locale, code_locale, message_locale);
3278 isds_log_message(context, message_locale);
3279 free(box_id_locale);
3280 free(code_locale);
3281 free(message_locale);
3282 err = IE_NOEXIST;
3283 goto leave;
3286 /* Other error */
3287 else if (xmlStrcmp(code, BAD_CAST "0000")) {
3288 char *code_locale = utf82locale((char*)code);
3289 char *message_locale = utf82locale((char*)message);
3290 isds_log(ILF_ISDS, ILL_DEBUG,
3291 _("Server refused CheckDataBox request "
3292 "(code=%s, message=%s)\n"), code_locale, message_locale);
3293 isds_log_message(context, message_locale);
3294 free(code_locale);
3295 free(message_locale);
3296 err = IE_ISDS;
3297 goto leave;
3300 /* Extract data */
3301 xpath_ctx = xmlXPathNewContext(response);
3302 if (!xpath_ctx) {
3303 err = IE_ERROR;
3304 goto leave;
3306 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
3307 err = IE_ERROR;
3308 goto leave;
3310 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
3311 xpath_ctx);
3312 if (!result) {
3313 err = IE_ERROR;
3314 goto leave;
3316 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3317 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
3318 err = IE_ISDS;
3319 goto leave;
3321 if (result->nodesetval->nodeNr > 1) {
3322 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
3323 err = IE_ISDS;
3324 goto leave;
3326 xpath_ctx->node = result->nodesetval->nodeTab[0];
3327 xmlXPathFreeObject(result); result = NULL;
3329 EXTRACT_LONGINT("isds:dbState", box_status, 1);
3332 leave:
3333 free(string);
3334 xmlXPathFreeObject(result);
3335 xmlXPathFreeContext(xpath_ctx);
3337 free(code);
3338 free(message);
3339 xmlFreeDoc(response);
3341 if (!err)
3342 isds_log(ILF_ISDS, ILL_DEBUG,
3343 _("CheckDataBox request processed by server successfully.\n"));
3345 return err;
3349 /* Send a message via ISDS to a recipent
3350 * @context is session context
3351 * @outgoing_message is message to send; Some memebers are mandatory (like
3352 * dbIDRecipient), some are optional and some are irrelevant (especialy data
3353 * about sender). Included pointer to isds_list documents must contain at
3354 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
3355 * members will be filled with valid data from ISDS. Exact list of write
3356 * members is subject to change. Currently dmId is changed.
3357 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
3358 isds_error isds_send_message(struct isds_ctx *context,
3359 struct isds_message *outgoing_message) {
3361 isds_error err = IE_SUCCESS;
3362 xmlNsPtr isds_ns = NULL;
3363 xmlNodePtr request = NULL, envelope, dm_files, node;
3364 xmlDocPtr response = NULL;
3365 xmlChar *code = NULL, *message = NULL;
3366 xmlXPathContextPtr xpath_ctx = NULL;
3367 xmlXPathObjectPtr result = NULL;
3368 xmlChar *string = NULL;
3369 _Bool message_is_complete = 0;
3371 if (!context) return IE_INVALID_CONTEXT;
3372 if (!outgoing_message) return IE_INVAL;
3374 /* Check if connection is established
3375 * TODO: This check should be done donwstairs. */
3376 if (!context->curl) return IE_CONNECTION_CLOSED;
3379 /* Build CreateMessage request */
3380 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
3381 if (!request) {
3382 isds_log_message(context,
3383 _("Could build CreateMessage request"));
3384 return IE_ERROR;
3386 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
3387 if(!isds_ns) {
3388 isds_log_message(context, _("Could not create ISDS name space"));
3389 xmlFreeNode(request);
3390 return IE_ERROR;
3392 xmlSetNs(request, isds_ns);
3395 /* Build envelope */
3396 envelope = xmlNewChild(request, NULL, BAD_CAST "dmEnvelope", NULL);
3397 if (!envelope) {
3398 isds_log_message(context, _("Could not add dmEnvelope child to "
3399 "CreateMessage element"));
3400 xmlFreeNode(request);
3401 return IE_ERROR;
3404 if (!outgoing_message->envelope) {
3405 isds_log_message(context, _("outgoing message is missing envelope"));
3406 err = IE_INVAL;
3407 goto leave;
3410 INSERT_STRING(envelope, "dmSenderOrgUnit",
3411 outgoing_message->envelope->dmSenderOrgUnit);
3412 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
3413 outgoing_message->envelope->dmSenderOrgUnitNum, string);
3415 if (!outgoing_message->envelope->dbIDRecipient) {
3416 isds_log_message(context,
3417 _("outgoing message is missing recipient box identifier"));
3418 err = IE_INVAL;
3419 goto leave;
3421 INSERT_STRING(envelope, "dbIDRecipient",
3422 outgoing_message->envelope->dbIDRecipient);
3424 INSERT_STRING(envelope, "dmRecipientOrgUnit",
3425 outgoing_message->envelope->dmRecipientOrgUnit);
3426 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
3427 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
3428 INSERT_STRING(envelope, "dmToHands", outgoing_message->envelope->dmToHands);
3430 #define CHECK_FOR_STRING_LENGTH(string, limit, name) \
3431 if ((string) && xmlUTF8Strlen((xmlChar *) (string)) > (limit)) { \
3432 isds_printf_message(context, \
3433 _("%s has more than %d characters"), (name), (limit)); \
3434 err = IE_2BIG; \
3435 goto leave; \
3438 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 255,
3439 "dmAnnotation");
3440 INSERT_STRING(envelope, "dmAnnotation",
3441 outgoing_message->envelope->dmAnnotation);
3443 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
3444 50, "dmRecipientRefNumber");
3445 INSERT_STRING(envelope, "dmRecipientRefNumber",
3446 outgoing_message->envelope->dmRecipientRefNumber);
3448 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
3449 50, "dmSenderRefNumber");
3450 INSERT_STRING(envelope, "dmSenderRefNumber",
3451 outgoing_message->envelope->dmSenderRefNumber);
3453 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
3454 50, "dmRecipientIdent");
3455 INSERT_STRING(envelope, "dmRecipientIdent",
3456 outgoing_message->envelope->dmRecipientIdent);
3458 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
3459 50, "dmSenderIdent");
3460 INSERT_STRING(envelope, "dmSenderIdent",
3461 outgoing_message->envelope->dmSenderIdent);
3463 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
3464 outgoing_message->envelope->dmLegalTitleLaw, string);
3465 INSERT_LONGINT(envelope, "dmLegalTitleYear",
3466 outgoing_message->envelope->dmLegalTitleYear, string);
3467 INSERT_STRING(envelope, "dmLegalTitleSect",
3468 outgoing_message->envelope->dmLegalTitleSect);
3469 INSERT_STRING(envelope, "dmLegalTitlePar",
3470 outgoing_message->envelope->dmLegalTitlePar);
3471 INSERT_STRING(envelope, "dmLegalTitlePoint",
3472 outgoing_message->envelope->dmLegalTitlePoint);
3474 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
3475 outgoing_message->envelope->dmPersonalDelivery);
3476 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
3477 outgoing_message->envelope->dmAllowSubstDelivery);
3479 #undef CHECK_FOR_STRING_LENGTH
3481 /* ???: Should we require value for dbEffectiveOVM sender?
3482 * ISDS has default as true */
3483 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
3486 /* Append dmFiles */
3487 if (!outgoing_message->documents) {
3488 isds_log_message(context,
3489 _("outgoing message is missing list of documents"));
3490 err = IE_INVAL;
3491 goto leave;
3493 dm_files = xmlNewChild(request, NULL, BAD_CAST "dmFiles", NULL);
3494 if (!dm_files) {
3495 isds_log_message(context, _("Could not add dmFiles child to "
3496 "CreateMessage element"));
3497 err = IE_ERROR;
3498 goto leave;
3501 /* Check for document hieararchy */
3502 err = check_documents_hierarchy(context, outgoing_message->documents);
3503 if (err) goto leave;
3505 /* Process each document */
3506 for (struct isds_list *item =
3507 (struct isds_list *) outgoing_message->documents;
3508 item; item = item->next) {
3509 if (!item->data) {
3510 isds_log_message(context,
3511 _("list of documents contains empty item"));
3512 err = IE_INVAL;
3513 goto leave;
3515 /* FIXME: Check for dmFileMetaType and for document references.
3516 * Only first document can be of MAIN type */
3517 err = insert_document(context, (struct isds_document*) item->data,
3518 dm_files);
3520 if (err) goto leave;
3523 /* Signal we can serilize message since now */
3524 message_is_complete = 1;
3528 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
3530 /* Sent request */
3531 err = isds(context, SERVICE_DM_OPERATIONS, request, &response);
3533 /* Dont' destroy request, we want to privode it to application later */
3535 if (err) {
3536 isds_log(ILF_ISDS, ILL_DEBUG,
3537 _("Processing ISDS response on CreateMessage "
3538 "request failed\n"));
3539 goto leave;
3542 /* Check for response status */
3543 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
3544 &code, &message, NULL);
3545 if (err) {
3546 isds_log(ILF_ISDS, ILL_DEBUG,
3547 _("ISDS response on CreateMessage request "
3548 "is missing status\n"));
3549 goto leave;
3552 /* Request processed, but nothing found */
3553 if (xmlStrcmp(code, BAD_CAST "0000")) {
3554 char *box_id_locale =
3555 utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
3556 char *code_locale = utf82locale((char*)code);
3557 char *message_locale = utf82locale((char*)message);
3558 isds_log(ILF_ISDS, ILL_DEBUG,
3559 _("Server did not accept message for %s on CreateMessage "
3560 "request (code=%s, message=%s)\n"),
3561 box_id_locale, code_locale, message_locale);
3562 isds_log_message(context, message_locale);
3563 free(box_id_locale);
3564 free(code_locale);
3565 free(message_locale);
3566 err = IE_ISDS;
3567 goto leave;
3571 /* Extract data */
3572 xpath_ctx = xmlXPathNewContext(response);
3573 if (!xpath_ctx) {
3574 err = IE_ERROR;
3575 goto leave;
3577 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
3578 err = IE_ERROR;
3579 goto leave;
3581 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
3582 xpath_ctx);
3583 if (!result) {
3584 err = IE_ERROR;
3585 goto leave;
3587 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3588 isds_log_message(context, _("Missing CreateMessageResponse element"));
3589 err = IE_ISDS;
3590 goto leave;
3592 if (result->nodesetval->nodeNr > 1) {
3593 isds_log_message(context, _("Multiple CreateMessageResponse element"));
3594 err = IE_ISDS;
3595 goto leave;
3597 xpath_ctx->node = result->nodesetval->nodeTab[0];
3598 xmlXPathFreeObject(result); result = NULL;
3600 if (outgoing_message->envelope->dmID) {
3601 free(outgoing_message->envelope->dmID);
3602 outgoing_message->envelope->dmID = NULL;
3604 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
3605 if (!outgoing_message->envelope->dmID) {
3606 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
3607 "but did not returen assigned message ID\n"));
3610 leave:
3611 /* TODO: Serialize message into structure member raw */
3612 /* XXX: Each web service transport message in different format.
3613 * Therefore it's not possible to save them directly.
3614 * To save them, one must figure out common format.
3615 * We can leave it on application, or we can implement the ESS format. */
3616 /*if (message_is_complete) {
3617 if (outgoing_message->envelope->dmID) {
3619 /* Add assigned message ID as first child*/
3620 /*xmlNodePtr dmid_text = xmlNewText(
3621 (xmlChar *) outgoing_message->envelope->dmID);
3622 if (!dmid_text) goto serialization_failed;
3624 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
3625 BAD_CAST "dmID");
3626 if (!dmid_element) {
3627 xmlFreeNode(dmid_text);
3628 goto serialization_failed;
3631 xmlNodePtr dmid_element_with_text =
3632 xmlAddChild(dmid_element, dmid_text);
3633 if (!dmid_element_with_text) {
3634 xmlFreeNode(dmid_element);
3635 xmlFreeNode(dmid_text);
3636 goto serialization_failed;
3639 node = xmlAddPrevSibling(envelope->childern,
3640 dmid_element_with_text);
3641 if (!node) {
3642 xmlFreeNodeList(dmid_element_with_text);
3643 goto serialization_failed;
3647 /* Serialize message with ID into raw */
3648 /*buffer = serialize_element(envelope)*/
3649 /* }
3651 serialization_failed:
3656 /* Clean up */
3657 free(string);
3658 xmlXPathFreeObject(result);
3659 xmlXPathFreeContext(xpath_ctx);
3661 free(code);
3662 free(message);
3663 xmlFreeDoc(response);
3664 xmlFreeNode(request);
3666 if (!err)
3667 isds_log(ILF_ISDS, ILL_DEBUG,
3668 _("CreateMessage request processed by server "
3669 "successfully.\n"));
3671 return err;
3675 /* Get list of messages. This is common core for getting sent or received
3676 * messaeges.
3677 * Any criterion argument can be NULL, if you don't care about it.
3678 * @context is session context. Must not be NULL.
3679 * @outgoing_direction is true if you want list of outgoing messages,
3680 * it's false if you want incoming messages.
3681 * @from_time is minimal time and date of message sending inclusive.
3682 * @to_time is maximal time and date of message sending inclusive
3683 * @organization_unit_number is number of sender/recipient respectively.
3684 * @status_filter is bit field of isds_message_status values. Use special
3685 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
3686 * all values, you can use bitwise arithmetic if you want.)
3687 * @offset is index of first message we are interested in. First message is 1.
3688 * Set to 0 (or 1) if you don't care.
3689 * @number is maximal length of list you want to get as input value, outputs
3690 * number of messages matching these criteria. Can be NULL if you don't care
3691 * (applies to output value either).
3692 * @messages is automatically reallocated list of isds_message's. Be ware that
3693 * it returns only brief overview (envelope and some other fields) about each
3694 * message, not the complete message. FIXME: Specify exact fields.
3695 * The list is sorted by delivery time in ascending order.
3696 * Use NULL if
3697 * you don't care about don't need the data (useful if you want to know only
3698 * the @number). If you provide &NULL, list will be allocated on heap, if you
3699 * provide pointer to non-NULL, list will be freed automacally at first. Also
3700 * in case of error the list will be NULLed.
3701 * @return IE_SUCCESS or appropriate error code. */
3702 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
3703 _Bool outgoing_direction,
3704 const struct timeval *from_time, const struct timeval *to_time,
3705 const long int *organization_unit_number,
3706 const unsigned int status_filter,
3707 const unsigned long int offset, unsigned long int *number,
3708 struct isds_list **messages) {
3710 isds_error err = IE_SUCCESS;
3711 xmlNsPtr isds_ns = NULL;
3712 xmlNodePtr request = NULL, node;
3713 xmlDocPtr response = NULL;
3714 xmlChar *code = NULL, *message = NULL;
3715 xmlXPathContextPtr xpath_ctx = NULL;
3716 xmlXPathObjectPtr result = NULL;
3717 xmlChar *string = NULL;
3718 long unsigned int count = 0;
3720 if (!context) return IE_INVALID_CONTEXT;
3722 /* Free former message list if any */
3723 if (messages) isds_list_free(messages);
3725 /* Check if connection is established
3726 * TODO: This check should be done donwstairs. */
3727 if (!context->curl) return IE_CONNECTION_CLOSED;
3729 /* Build GetListOf*Messages request */
3730 request = xmlNewNode(NULL,
3731 (outgoing_direction) ?
3732 BAD_CAST "GetListOfSentMessages" :
3733 BAD_CAST "GetListOfReceivedMessages"
3735 if (!request) {
3736 isds_log_message(context,
3737 (outgoing_direction) ?
3738 _("Could not build GetListOfSentMessages request") :
3739 _("Could not build GetListOfReceivedMessages request")
3741 return IE_ERROR;
3743 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
3744 if(!isds_ns) {
3745 isds_log_message(context, _("Could not create ISDS name space"));
3746 xmlFreeNode(request);
3747 return IE_ERROR;
3749 xmlSetNs(request, isds_ns);
3752 if (from_time) {
3753 err = timeval2timestring(from_time, &string);
3754 if (err) goto leave;
3756 INSERT_STRING(request, "dmFromTime", string);
3757 free(string); string = NULL;
3759 if (to_time) {
3760 err = timeval2timestring(to_time, &string);
3761 if (err) goto leave;
3763 INSERT_STRING(request, "dmToTime", string);
3764 free(string); string = NULL;
3766 if (outgoing_direction) {
3767 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
3768 organization_unit_number, string);
3769 } else {
3770 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
3771 organization_unit_number, string);
3774 if (status_filter > MESSAGESTATE_ANY) {
3775 isds_printf_message(context,
3776 _("Invalid message state filter value: %ld"), status_filter);
3777 err = IE_INVAL;
3778 goto leave;
3780 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
3782 if (offset > 0 ) {
3783 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
3784 } else {
3785 INSERT_STRING(request, "dmOffset", "1");
3788 /* number 0 means no limit */
3789 if (number && *number == 0) {
3790 INSERT_STRING(request, "dmLimit", NULL);
3791 } else {
3792 INSERT_ULONGINT(request, "dmLimit", number, string);
3796 isds_log(ILF_ISDS, ILL_DEBUG,
3797 (outgoing_direction) ?
3798 _("Sending GetListOfSentMessages request to ISDS\n") :
3799 _("Sending GetListOfReceivedMessages request to ISDS\n")
3802 /* Sent request */
3803 err = isds(context, SERVICE_DM_INFO, request, &response);
3804 xmlFreeNode(request); request = NULL;
3806 if (err) {
3807 isds_log(ILF_ISDS, ILL_DEBUG,
3808 (outgoing_direction) ?
3809 _("Processing ISDS response on GetListOfSentMessages "
3810 "request failed\n") :
3811 _("Processing ISDS response on GetListOfReceivedMessages "
3812 "request failed\n")
3814 goto leave;
3817 /* Check for response status */
3818 err = isds_response_status(context, SERVICE_DM_INFO, response,
3819 &code, &message, NULL);
3820 if (err) {
3821 isds_log(ILF_ISDS, ILL_DEBUG,
3822 (outgoing_direction) ?
3823 _("ISDS response on GetListOfSentMessages request "
3824 "is missing status\n") :
3825 _("ISDS response on GetListOfReceivedMessages request "
3826 "is missing status\n")
3828 goto leave;
3831 /* Request processed, but nothing found */
3832 if (xmlStrcmp(code, BAD_CAST "0000")) {
3833 char *code_locale = utf82locale((char*)code);
3834 char *message_locale = utf82locale((char*)message);
3835 isds_log(ILF_ISDS, ILL_DEBUG,
3836 (outgoing_direction) ?
3837 _("Server refused GetListOfSentMessages request "
3838 "(code=%s, message=%s)\n") :
3839 _("Server refused GetListOfReceivedMessages request "
3840 "(code=%s, message=%s)\n"),
3841 code_locale, message_locale);
3842 isds_log_message(context, message_locale);
3843 free(code_locale);
3844 free(message_locale);
3845 err = IE_ISDS;
3846 goto leave;
3850 /* Extract data */
3851 xpath_ctx = xmlXPathNewContext(response);
3852 if (!xpath_ctx) {
3853 err = IE_ERROR;
3854 goto leave;
3856 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
3857 err = IE_ERROR;
3858 goto leave;
3860 result = xmlXPathEvalExpression(
3861 (outgoing_direction) ?
3862 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
3863 "isds:dmRecords/isds:dmRecord" :
3864 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
3865 "isds:dmRecords/isds:dmRecord",
3866 xpath_ctx);
3867 if (!result) {
3868 err = IE_ERROR;
3869 goto leave;
3872 /* Fill output arguments in */
3873 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3874 struct isds_envelope *envelope;
3875 struct isds_list *item = NULL, *last_item = NULL;
3877 for (count = 0; count < result->nodesetval->nodeNr; count++) {
3878 /* Create new message */
3879 item = calloc(1, sizeof(*item));
3880 if (!item) {
3881 err = IE_NOMEM;
3882 goto leave;
3884 item->destructor = (void(*)(void**)) &isds_message_free;
3885 item->data = calloc(1, sizeof(struct isds_message));
3886 if (!item->data) {
3887 isds_list_free(&item);
3888 err = IE_NOMEM;
3889 goto leave;
3892 /* Extract envelope data */
3893 xpath_ctx->node = result->nodesetval->nodeTab[count];
3894 envelope = NULL;
3895 err = extract_DmRecord(context, &envelope, xpath_ctx);
3896 if (err) {
3897 isds_list_free(&item);
3898 goto leave;
3901 /* Attach extracted envelope */
3902 ((struct isds_message *) item->data)->envelope = envelope;
3904 /* Append new message into the list */
3905 if (!*messages) {
3906 *messages = last_item = item;
3907 } else {
3908 last_item->next = item;
3909 last_item = item;
3913 if (number) *number = count;
3915 leave:
3916 if (err) {
3917 isds_list_free(messages);
3920 free(string);
3921 xmlXPathFreeObject(result);
3922 xmlXPathFreeContext(xpath_ctx);
3924 free(code);
3925 free(message);
3926 xmlFreeDoc(response);
3927 xmlFreeNode(request);
3929 if (!err)
3930 isds_log(ILF_ISDS, ILL_DEBUG,
3931 (outgoing_direction) ?
3932 _("GetListOfSentMessages request processed by server "
3933 "successfully.\n") :
3934 _("GetListOfReceivedMessages request processed by server "
3935 "successfully.\n")
3937 return err;
3941 /* Get list of outgoing (already sent) messages.
3942 * Any criterion argument can be NULL, if you don't care about it.
3943 * @context is session context. Must not be NULL.
3944 * @from_time is minimal time and date of message sending inclusive.
3945 * @to_time is maximal time and date of message sending inclusive
3946 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
3947 * @status_filter is bit field of isds_message_status values. Use special
3948 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
3949 * all values, you can use bitwise arithmetic if you want.)
3950 * @offset is index of first message we are interested in. First message is 1.
3951 * Set to 0 (or 1) if you don't care.
3952 * @number is maximal length of list you want to get as input value, outputs
3953 * number of messages matching these criteria. Can be NULL if you don't care
3954 * (applies to output value either).
3955 * @messages is automatically reallocated list of isds_message's. Be ware that
3956 * it returns only brief overview (envelope and some other fields) about each
3957 * message, not the complete message. FIXME: Specify exact fields.
3958 * The list is sorted by delivery time in ascending order.
3959 * Use NULL if you don't care about the metadata (useful if you want to know
3960 * only the @number). If you provide &NULL, list will be allocated on heap,
3961 * if you provide pointer to non-NULL, list will be freed automacally at first.
3962 * Also in case of error the list will be NULLed.
3963 * @return IE_SUCCESS or appropriate error code. */
3964 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
3965 const struct timeval *from_time, const struct timeval *to_time,
3966 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
3967 const unsigned long int offset, unsigned long int *number,
3968 struct isds_list **messages) {
3970 return isds_get_list_of_messages(
3971 context, 1,
3972 from_time, to_time, dmSenderOrgUnitNum, status_filter,
3973 offset, number,
3974 messages);
3978 /* Get list of incoming (addressed to you) messages.
3979 * Any criterion argument can be NULL, if you don't care about it.
3980 * @context is session context. Must not be NULL.
3981 * @from_time is minimal time and date of message sending inclusive.
3982 * @to_time is maximal time and date of message sending inclusive
3983 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
3984 * @status_filter is bit field of isds_message_status values. Use special
3985 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
3986 * all values, you can use bitwise arithmetic if you want.)
3987 * @offset is index of first message we are interested in. First message is 1.
3988 * Set to 0 (or 1) if you don't care.
3989 * @number is maximal length of list you want to get as input value, outputs
3990 * number of messages matching these criteria. Can be NULL if you don't care
3991 * (applies to output value either).
3992 * @messages is automatically reallocated list of isds_message's. Be ware that
3993 * it returns only brief overview (envelope and some other fields) about each
3994 * message, not the complete message. FIXME: Specify exact fields.
3995 * Use NULL if you don't care about the metadata (useful if you want to know
3996 * only the @number). If you provide &NULL, list will be allocated on heap,
3997 * if you provide pointer to non-NULL, list will be freed automacally at first.
3998 * Also in case of error the list will be NULLed.
3999 * @return IE_SUCCESS or appropriate error code. */
4000 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
4001 const struct timeval *from_time, const struct timeval *to_time,
4002 const long int *dmRecipientOrgUnitNum,
4003 const unsigned int status_filter,
4004 const unsigned long int offset, unsigned long int *number,
4005 struct isds_list **messages) {
4007 return isds_get_list_of_messages(
4008 context, 0,
4009 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
4010 offset, number,
4011 messages);
4015 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
4016 * code
4017 * @context is session context
4018 * @service is ISDS WS service handler
4019 * @service_name is name of SERVICE_DM_OPERATIONS
4020 * @message_id is message ID to send as service argument to ISDS
4021 * @response is server SOAP body response as XML document
4022 * @code is ISDS status code
4023 * @status_message is ISDS status message
4024 * @return error coded from lower layer, context message will be set up
4025 * appropriately. */
4026 static isds_error build_send_check_message_request(struct isds_ctx *context,
4027 const isds_service service, const xmlChar *service_name,
4028 const char *message_id,
4029 xmlDocPtr *response, xmlChar **code, xmlChar **status_message) {
4031 isds_error err = IE_SUCCESS;
4032 char *service_name_locale = NULL, *message_id_locale = NULL;
4033 xmlNodePtr request = NULL, node;
4034 xmlNsPtr isds_ns = NULL;
4036 if (!context) return IE_INVALID_CONTEXT;
4037 if (!service_name || !message_id) return IE_INVAL;
4038 if (!response || !code || !status_message) return IE_INVAL;
4040 /* Free output argument */
4041 xmlFreeDoc(*response);
4042 free(*code);
4043 free(*status_message);
4046 /* Check if connection is established
4047 * TODO: This check should be done donwstairs. */
4048 if (!context->curl) return IE_CONNECTION_CLOSED;
4050 service_name_locale = utf82locale((char*)service_name);
4051 message_id_locale = utf82locale(message_id);
4052 if (!service_name_locale || !message_id_locale) {
4053 err = IE_NOMEM;
4054 goto leave;
4057 /* Build request */
4058 request = xmlNewNode(NULL, service_name);
4059 if (!request) {
4060 isds_printf_message(context,
4061 _("Could not build %s request"), service_name_locale);
4062 err = IE_ERROR;
4063 goto leave;
4065 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4066 if(!isds_ns) {
4067 isds_log_message(context, _("Could not create ISDS name space"));
4068 err = IE_ERROR;
4069 goto leave;
4071 xmlSetNs(request, isds_ns);
4074 /* Add requested ID */
4075 err = validate_message_id_length(context, (xmlChar *) message_id);
4076 if (err) goto leave;
4077 INSERT_STRING(request, "dmID", message_id);
4080 isds_log(ILF_ISDS, ILL_DEBUG,
4081 _("Sending %s request for %s message ID to ISDS\n"),
4082 service_name_locale, message_id_locale);
4084 /* Send request */
4085 err = isds(context, service, request, response);
4086 xmlFreeNode(request); request = NULL;
4088 if (err) {
4089 isds_log(ILF_ISDS, ILL_DEBUG,
4090 _("Processing ISDS response on %s request failed\n"),
4091 service_name_locale);
4092 goto leave;
4095 /* Check for response status */
4096 err = isds_response_status(context, service, *response,
4097 code, status_message, NULL);
4098 if (err) {
4099 isds_log(ILF_ISDS, ILL_DEBUG,
4100 _("ISDS response on %s request is missing status\n"),
4101 service_name_locale);
4102 goto leave;
4105 /* Request processed, but nothing found */
4106 if (xmlStrcmp(*code, BAD_CAST "0000")) {
4107 char *code_locale = utf82locale((char*) *code);
4108 char *status_message_locale = utf82locale((char*) *status_message);
4109 isds_log(ILF_ISDS, ILL_DEBUG,
4110 _("Server refused %s request for %s message ID "
4111 "(code=%s, message=%s)\n"),
4112 service_name_locale, message_id_locale,
4113 code_locale, status_message_locale);
4114 isds_log_message(context, status_message_locale);
4115 free(code_locale);
4116 free(status_message_locale);
4117 err = IE_ISDS;
4118 goto leave;
4121 leave:
4122 free(message_id_locale);
4123 free(service_name_locale);
4124 xmlFreeNode(request);
4125 return err;
4129 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
4130 * signed data and free ISDS response.
4131 * @context is session context
4132 * @message_id is UTF-8 encoded message ID for loging purpose
4133 * @response is parsed XML document. It will be freed and NULLed in the middle
4134 * of function run to save memmory. This is not guaranted in case of error.
4135 * @request_name is name of ISDS request used to construct response root
4136 * element name and for logging purpose.
4137 * @raw is reallocated output buffer with DER encoded CMS data
4138 * @raw_length is size of @raw buffer in bytes
4139 * @returns standard error codes, in case of error, @raw will be freed and
4140 * NULLed, @response sometimes. */
4141 static isds_error find_extract_signed_data_free_response(
4142 struct isds_ctx *context, const xmlChar *message_id,
4143 xmlDocPtr *response, const xmlChar *request_name,
4144 void **raw, size_t *raw_length) {
4146 isds_error err = IE_SUCCESS;
4147 char *xpath_expression = NULL;
4148 xmlXPathContextPtr xpath_ctx = NULL;
4149 xmlXPathObjectPtr result = NULL;
4150 char *encoded_structure = NULL;
4152 if (!context) return IE_INVALID_CONTEXT;
4153 if (!raw) return IE_INVAL;
4154 zfree(*raw);
4155 if (!message_id || !response || !*response || !request_name || !raw_length)
4156 return IE_INVAL;
4158 /* Build XPath expression */
4159 xpath_expression = astrcat3("/isds:", (char *) request_name,
4160 "Response/isds:dmSignature");
4161 if (!xpath_expression) return IE_NOMEM;
4163 /* Extract data */
4164 xpath_ctx = xmlXPathNewContext(*response);
4165 if (!xpath_ctx) {
4166 err = IE_ERROR;
4167 goto leave;
4169 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4170 err = IE_ERROR;
4171 goto leave;
4173 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
4174 if (!result) {
4175 err = IE_ERROR;
4176 goto leave;
4178 /* Empty response */
4179 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4180 char *message_id_locale = utf82locale((char*) message_id);
4181 isds_printf_message(context,
4182 _("Server did not return any signed data for mesage ID `%s' "
4183 "on %s request"),
4184 message_id_locale, request_name);
4185 free(message_id_locale);
4186 err = IE_ISDS;
4187 goto leave;
4189 /* More reponses */
4190 if (result->nodesetval->nodeNr > 1) {
4191 char *message_id_locale = utf82locale((char*) message_id);
4192 isds_printf_message(context,
4193 _("Server did return more signed data for message ID `%s' "
4194 "on %s request"),
4195 message_id_locale, request_name);
4196 free(message_id_locale);
4197 err = IE_ISDS;
4198 goto leave;
4200 /* One response */
4201 xpath_ctx->node = result->nodesetval->nodeTab[0];
4203 /* Extract PKCS#7 structure */
4204 EXTRACT_STRING(".", encoded_structure);
4205 if (!encoded_structure) {
4206 isds_log_message(context, _("dmSignature element is empty"));
4209 /* Here we have delivery info as standalone CMS in encoded_structure.
4210 * We don't need any other data, free them: */
4211 xmlXPathFreeObject(result); result = NULL;
4212 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
4213 xmlFreeDoc(*response); *response = NULL;
4216 /* Decode PKCS#7 to DER format */
4217 *raw_length = b64decode(encoded_structure, raw);
4218 if (*raw_length == (size_t) -1) {
4219 isds_log_message(context,
4220 _("Error while Base64-decoding PKCS#7 structure"));
4221 err = IE_ERROR;
4222 goto leave;
4225 leave:
4226 if (err) {
4227 zfree(*raw);
4228 raw_length = 0;
4231 free(encoded_structure);
4232 xmlXPathFreeObject(result);
4233 xmlXPathFreeContext(xpath_ctx);
4234 free(xpath_expression);
4236 return err;
4240 /* Download incoming message envelope identified by ID.
4241 * @context is session context
4242 * @message_id is message identifier (you can get them from
4243 * isds_get_list_of_received_messages())
4244 * @message is automatically reallocated message retrieved from ISDS.
4245 * It will miss documents per se. Use isds_get_received_message(), if you are
4246 * interrested in documents (content) too.
4247 * Returned hash and timestamp require documents to be verifiable. */
4248 isds_error isds_get_received_envelope(struct isds_ctx *context,
4249 const char *message_id, struct isds_message **message) {
4251 isds_error err = IE_SUCCESS;
4252 xmlDocPtr response = NULL;
4253 xmlChar *code = NULL, *status_message = NULL;
4254 xmlXPathContextPtr xpath_ctx = NULL;
4255 xmlXPathObjectPtr result = NULL;
4257 if (!context) return IE_INVALID_CONTEXT;
4259 /* Free former message if any */
4260 if (!message) return IE_INVAL;
4261 isds_message_free(message);
4263 /* Do request and check for success */
4264 err = build_send_check_message_request(context, SERVICE_DM_INFO,
4265 BAD_CAST "MessageEnvelopeDownload", message_id,
4266 &response, &code, &status_message);
4267 if (err) goto leave;
4269 /* Extract data */
4270 xpath_ctx = xmlXPathNewContext(response);
4271 if (!xpath_ctx) {
4272 err = IE_ERROR;
4273 goto leave;
4275 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4276 err = IE_ERROR;
4277 goto leave;
4279 result = xmlXPathEvalExpression(
4280 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
4281 "isds:dmReturnedMessageEnvelope",
4282 xpath_ctx);
4283 if (!result) {
4284 err = IE_ERROR;
4285 goto leave;
4287 /* Empty response */
4288 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4289 char *message_id_locale = utf82locale((char*) message_id);
4290 isds_printf_message(context,
4291 _("Server did not return any envelope for ID `%s' "
4292 "on MessageEnvelopeDownload request"), message_id_locale);
4293 free(message_id_locale);
4294 err = IE_ISDS;
4295 goto leave;
4297 /* More envelops */
4298 if (result->nodesetval->nodeNr > 1) {
4299 char *message_id_locale = utf82locale((char*) message_id);
4300 isds_printf_message(context,
4301 _("Server did return more envelopes for ID `%s' "
4302 "on MessageEnvelopeDownload request"), message_id_locale);
4303 free(message_id_locale);
4304 err = IE_ISDS;
4305 goto leave;
4307 /* One message */
4308 xpath_ctx->node = result->nodesetval->nodeTab[0];
4310 /* Extract the envelope (= message without documents, hence 0) */
4311 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
4312 if (err) goto leave;
4314 /* Save XML blob */
4315 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
4316 &(*message)->raw_length);
4318 leave:
4319 if (err) {
4320 isds_message_free(message);
4323 xmlXPathFreeObject(result);
4324 xmlXPathFreeContext(xpath_ctx);
4326 free(code);
4327 free(status_message);
4328 xmlFreeDoc(response);
4330 if (!err)
4331 isds_log(ILF_ISDS, ILL_DEBUG,
4332 _("MessageEnvelopeDownload request processed by server "
4333 "successfully.\n")
4335 return err;
4339 /* Load signed delivery info from buffer.
4340 * @context is session context
4341 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
4342 * retrieve such data from message->raw after calling
4343 * isds_get_signed_delivery_info().
4344 * @length is length of buffer in bytes.
4345 * @message is automatically reallocated message parsed from @buffer.
4346 * @strategy selects how buffer will be attached into raw isds_message member.
4347 * */
4348 isds_error isds_load_signed_delivery_info(struct isds_ctx *context,
4349 const void *buffer, const size_t length,
4350 struct isds_message **message, const isds_buffer_strategy strategy) {
4352 isds_error err = IE_SUCCESS;
4353 xmlDocPtr message_doc = NULL;
4354 xmlXPathContextPtr xpath_ctx = NULL;
4355 xmlXPathObjectPtr result = NULL;
4356 void *xml_stream = NULL;
4357 size_t xml_stream_length = 0;
4359 if (!context) return IE_INVALID_CONTEXT;
4360 if (!message) return IE_INVAL;
4361 isds_message_free(message);
4362 if (!buffer) return IE_INVAL;
4365 /* Extract delivery info from PKCS#7 structure */
4366 err = extract_cms_data(context, buffer, length,
4367 &xml_stream, &xml_stream_length);
4368 if (err) goto leave;
4370 isds_log(ILF_ISDS, ILL_DEBUG,
4371 _("Signed delivery info content:\n%.*s\nEnd of delivery info\n"),
4372 xml_stream_length, xml_stream);
4374 /* Convert extracted delivery info XML stream into XPath context */
4375 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
4376 if (!message_doc) {
4377 err = IE_XML;
4378 goto leave;
4380 xpath_ctx = xmlXPathNewContext(message_doc);
4381 if (!xpath_ctx) {
4382 err = IE_ERROR;
4383 goto leave;
4385 /* XXX: Name spaces mangled for signed delivery info:
4386 * http://isds.czechpoint.cz/v20/delivery:
4388 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
4389 * <q:dmDelivery>
4390 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
4391 * <p:dmID>170272</p:dmID>
4392 * ...
4393 * </p:dmDm>
4394 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
4395 * ...
4396 * </q:dmEvents>...</q:dmEvents>
4397 * </q:dmDelivery>
4398 * </q:GetDeliveryInfoResponse>
4399 * */
4400 if (register_namespaces(xpath_ctx, MESSAGE_NS_SIGNED_DELIVERY)) {
4401 err = IE_ERROR;
4402 goto leave;
4404 result = xmlXPathEvalExpression(
4405 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
4406 xpath_ctx);
4407 if (!result) {
4408 err = IE_ERROR;
4409 goto leave;
4411 /* Empty embedded delivery info */
4412 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4413 isds_printf_message(context,
4414 _("XML document embedded into PKCS#7 structure is not "
4415 "sisds:dmDelivery document"));
4416 err = IE_ISDS;
4417 goto leave;
4419 /* More embedded delivery infos */
4420 if (result->nodesetval->nodeNr > 1) {
4421 isds_printf_message(context,
4422 _("Embeded XML document into PKCS#7 structure has more "
4423 "root sisds:dmDelivery elements"));
4424 err = IE_ISDS;
4425 goto leave;
4427 /* One embedded delivery info */
4428 xpath_ctx->node = result->nodesetval->nodeTab[0];
4430 /* Extract the envelope (= message without documents, hence 0).
4431 * XXX: extract_TReturnedMessage() can obtain attachments size,
4432 * but delivery info carries none. It's coded as option elements,
4433 * so it should work. */
4434 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
4435 if (err) goto leave;
4437 /* Extract events */
4438 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
4439 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
4440 if (err) { err = IE_ERROR; goto leave; }
4441 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
4442 if (err) goto leave;
4444 /* Append raw CMS structure into message */
4445 (*message)->raw_type = RAWTYPE_CMS_SIGNED_DELIVERYINFO;
4446 switch (strategy) {
4447 case BUFFER_DONT_STORE:
4448 break;
4449 case BUFFER_COPY:
4450 (*message)->raw = malloc(length);
4451 if (!(*message)->raw) {
4452 err = IE_NOMEM;
4453 goto leave;
4455 memcpy((*message)->raw, buffer, length);
4456 (*message)->raw_length = length;
4457 break;
4458 case BUFFER_MOVE:
4459 (*message)->raw = (void *) buffer;
4460 (*message)->raw_length = length;
4461 break;
4462 default:
4463 err = IE_ENUM;
4464 goto leave;
4467 leave:
4468 if (err) {
4469 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
4470 isds_message_free(message);
4473 xmlFreeDoc(message_doc);
4474 cms_data_free(xml_stream);
4475 xmlXPathFreeObject(result);
4476 xmlXPathFreeContext(xpath_ctx);
4478 if (!err)
4479 isds_log(ILF_ISDS, ILL_DEBUG,
4480 _("Signed message loaded successfully.\n"));
4481 return err;
4485 /* Download signed delivery infosheet of given message identified by ID.
4486 * @context is session context
4487 * @message_id is message identifier (you can get them from
4488 * isds_get_list_of_{sent,received}_messages())
4489 * @message is automatically reallocated message retrieved from ISDS.
4490 * It will miss documents per se. Use isds_get_signed_received_message(),
4491 * if you are interrested in documents (content). OTOH, only this function
4492 * can get list events message has gone through. */
4493 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
4494 const char *message_id, struct isds_message **message) {
4496 isds_error err = IE_SUCCESS;
4497 xmlDocPtr response = NULL;
4498 xmlChar *code = NULL, *status_message = NULL;
4499 void *raw = NULL;
4500 size_t raw_length = 0;
4502 if (!context) return IE_INVALID_CONTEXT;
4504 /* Free former message if any */
4505 if (!message) return IE_INVAL;
4506 isds_message_free(message);
4508 /* Do request and check for success */
4509 err = build_send_check_message_request(context, SERVICE_DM_INFO,
4510 BAD_CAST "GetSignedDeliveryInfo", message_id,
4511 &response, &code, &status_message);
4512 if (err) goto leave;
4514 /* Find signed delivery info, extract it into raw and maybe free
4515 * response */
4516 err = find_extract_signed_data_free_response(context,
4517 (xmlChar *)message_id, &response,
4518 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
4519 if (err) goto leave;
4521 /* Parse message */
4522 err = isds_load_signed_delivery_info(context, raw, raw_length,
4523 message, BUFFER_MOVE);
4524 if (err) goto leave;
4526 raw = NULL;
4528 leave:
4529 if (err) {
4530 isds_message_free(message);
4533 free(raw);
4534 free(code);
4535 free(status_message);
4536 xmlFreeDoc(response);
4538 if (!err)
4539 isds_log(ILF_ISDS, ILL_DEBUG,
4540 _("GetSignedDeliveryInfo request processed by server "
4541 "successfully.\n")
4543 return err;
4547 /* Load delivery info from buffer.
4548 * @context is session context
4549 * @buffer is XML document stream with delivery info. You can retrieve such
4550 * data from message->raw after calling isds_get_delivery_info().
4551 * @length is length of buffer in bytes.
4552 * @message is automatically reallocated message parsed from @buffer.
4553 * @strategy selects how buffer will be attached into raw isds_message member.
4554 * */
4555 isds_error isds_load_delivery_info(struct isds_ctx *context,
4556 const void *buffer, const size_t length,
4557 struct isds_message **message, const isds_buffer_strategy strategy) {
4559 isds_error err = IE_SUCCESS;
4560 xmlDocPtr message_doc = NULL;
4561 xmlXPathContextPtr xpath_ctx = NULL;
4562 xmlXPathObjectPtr result = NULL;
4564 if (!context) return IE_INVALID_CONTEXT;
4565 if (!message) return IE_INVAL;
4566 isds_message_free(message);
4567 if (!buffer) return IE_INVAL;
4569 /* Convert delivery info XML stream into XPath context */
4570 message_doc = xmlParseMemory(buffer, length);
4571 if (!message_doc) {
4572 err = IE_XML;
4573 goto leave;
4575 xpath_ctx = xmlXPathNewContext(message_doc);
4576 if (!xpath_ctx) {
4577 err = IE_ERROR;
4578 goto leave;
4580 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4581 err = IE_ERROR;
4582 goto leave;
4584 result = xmlXPathEvalExpression(BAD_CAST "/isds:dmDelivery", xpath_ctx);
4585 if (!result) {
4586 err = IE_ERROR;
4587 goto leave;
4589 /* Empty delivery info */
4590 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4591 isds_printf_message(context,
4592 _("XML document is not isds:dmDelivery document"));
4593 err = IE_ISDS;
4594 goto leave;
4596 /* More delivery infos
4597 * This should never happen */
4598 if (result->nodesetval->nodeNr > 1) {
4599 isds_printf_message(context,
4600 _("XML document has more root isds:dmDelivery elements"));
4601 err = IE_ISDS;
4602 goto leave;
4604 /* One delivery info */
4605 xpath_ctx->node = result->nodesetval->nodeTab[0];
4607 /* Extract the envelope (= message without documents, hence 0).
4608 * XXX: extract_TReturnedMessage() can obtain attachments size,
4609 * but delivery info carries none. It's coded as option elements,
4610 * so it should work. */
4611 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
4612 if (err) goto leave;
4614 /* Extract events */
4615 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmEvents", xpath_ctx);
4616 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
4617 if (err) { err = IE_ERROR; goto leave; }
4618 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
4619 if (err) goto leave;
4621 /* Append raw CMS structure into message */
4622 (*message)->raw_type = RAWTYPE_DELIVERYINFO;
4623 switch (strategy) {
4624 case BUFFER_DONT_STORE:
4625 break;
4626 case BUFFER_COPY:
4627 (*message)->raw = malloc(length);
4628 if (!(*message)->raw) {
4629 err = IE_NOMEM;
4630 goto leave;
4632 memcpy((*message)->raw, buffer, length);
4633 (*message)->raw_length = length;
4634 break;
4635 case BUFFER_MOVE:
4636 (*message)->raw = (void *) buffer;
4637 (*message)->raw_length = length;
4638 break;
4639 default:
4640 err = IE_ENUM;
4641 goto leave;
4644 leave:
4645 if (err) {
4646 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
4647 isds_message_free(message);
4650 xmlFreeDoc(message_doc);
4651 xmlXPathFreeObject(result);
4652 xmlXPathFreeContext(xpath_ctx);
4654 if (!err)
4655 isds_log(ILF_ISDS, ILL_DEBUG,
4656 _("Delivery info loaded successfully.\n"));
4657 return err;
4661 /* Download delivery infosheet of given message identified by ID.
4662 * @context is session context
4663 * @message_id is message identifier (you can get them from
4664 * isds_get_list_of_{sent,received}_messages())
4665 * @message is automatically reallocated message retrieved from ISDS.
4666 * It will miss documents per se. Use isds_get_received_message(), if you are
4667 * interrested in documents (content). OTOH, only this function can get list
4668 * events message has gone through. */
4669 isds_error isds_get_delivery_info(struct isds_ctx *context,
4670 const char *message_id, struct isds_message **message) {
4672 isds_error err = IE_SUCCESS;
4673 xmlDocPtr response = NULL;
4674 xmlChar *code = NULL, *status_message = NULL;
4675 xmlXPathContextPtr xpath_ctx = NULL;
4676 xmlXPathObjectPtr result = NULL;
4677 xmlNodePtr delivery_node = NULL;
4679 if (!context) return IE_INVALID_CONTEXT;
4681 /* Free former message if any */
4682 if (!message) return IE_INVAL;
4683 isds_message_free(message);
4685 /* Do request and check for success */
4686 err = build_send_check_message_request(context, SERVICE_DM_INFO,
4687 BAD_CAST "GetDeliveryInfo", message_id,
4688 &response, &code, &status_message);
4689 if (err) goto leave;
4691 /* Extract data */
4692 xpath_ctx = xmlXPathNewContext(response);
4693 if (!xpath_ctx) {
4694 err = IE_ERROR;
4695 goto leave;
4697 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4698 err = IE_ERROR;
4699 goto leave;
4701 result = xmlXPathEvalExpression(
4702 BAD_CAST "/isds:GetDeliveryInfoResponse/isds:dmDelivery",
4703 xpath_ctx);
4704 if (!result) {
4705 err = IE_ERROR;
4706 goto leave;
4708 /* Empty response */
4709 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4710 char *message_id_locale = utf82locale((char*) message_id);
4711 isds_printf_message(context,
4712 _("Server did not return any delivery info for ID `%s' "
4713 "on GetDeliveryInfo request"), message_id_locale);
4714 free(message_id_locale);
4715 err = IE_ISDS;
4716 goto leave;
4718 /* More delivery infos */
4719 if (result->nodesetval->nodeNr > 1) {
4720 char *message_id_locale = utf82locale((char*) message_id);
4721 isds_printf_message(context,
4722 _("Server did return more delivery infos for ID `%s' "
4723 "on GetDeliveryInfo request"), message_id_locale);
4724 free(message_id_locale);
4725 err = IE_ISDS;
4726 goto leave;
4728 /* One delivery info */
4729 xpath_ctx->node = delivery_node = result->nodesetval->nodeTab[0];
4731 /* Extract the envelope (= message without documents, hence 0).
4732 * XXX: extract_TReturnedMessage() can obtain attachments size,
4733 * but delivery info carries none. It's coded as option elements,
4734 * so it should work. */
4735 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
4736 if (err) goto leave;
4738 /* Extract events */
4739 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmEvents", xpath_ctx);
4740 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
4741 if (err) { err = IE_ERROR; goto leave; }
4742 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
4743 if (err) goto leave;
4745 /* Save XML blob */
4746 err = serialize_subtree(context, delivery_node, &(*message)->raw,
4747 &(*message)->raw_length);
4749 leave:
4750 if (err) {
4751 isds_message_free(message);
4754 xmlXPathFreeObject(result);
4755 xmlXPathFreeContext(xpath_ctx);
4757 free(code);
4758 free(status_message);
4759 xmlFreeDoc(response);
4761 if (!err)
4762 isds_log(ILF_ISDS, ILL_DEBUG,
4763 _("GetDeliveryInfo request processed by server "
4764 "successfully.\n")
4766 return err;
4770 /* Load incoming message from buffer.
4771 * @context is session context
4772 * @buffer XML stream with unsigned message. You can retrieve such data from
4773 * message->raw after calling isds_get_received_message().
4774 * @length is length of buffer in bytes.
4775 * @message is automatically reallocated message parsed from @buffer.
4776 * @strategy selects how buffer will be attached into raw isds_message member.
4777 * */
4778 isds_error isds_load_received_message(struct isds_ctx *context,
4779 const void *buffer, const size_t length,
4780 struct isds_message **message, const isds_buffer_strategy strategy) {
4782 isds_error err = IE_SUCCESS;
4783 xmlDocPtr message_doc = NULL;
4784 xmlXPathContextPtr xpath_ctx = NULL;
4785 xmlXPathObjectPtr result = NULL;
4787 if (!context) return IE_INVALID_CONTEXT;
4788 if (!message) return IE_INVAL;
4789 isds_message_free(message);
4790 if (!buffer) return IE_INVAL;
4793 isds_log(ILF_ISDS, ILL_DEBUG,
4794 _("Incoming message content:\n%.*s\nEnd of message\n"),
4795 length, buffer);
4797 /* Convert extracted messages XML stream into XPath context */
4798 message_doc = xmlParseMemory(buffer, length);
4799 if (!message_doc) {
4800 err = IE_XML;
4801 goto leave;
4803 xpath_ctx = xmlXPathNewContext(message_doc);
4804 if (!xpath_ctx) {
4805 err = IE_ERROR;
4806 goto leave;
4808 /* XXX: Standard name space */
4809 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4810 err = IE_ERROR;
4811 goto leave;
4813 result = xmlXPathEvalExpression(
4814 BAD_CAST "/isds:dmReturnedMessage", xpath_ctx);
4815 if (!result) {
4816 err = IE_ERROR;
4817 goto leave;
4819 /* Bad root element */
4820 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4821 isds_printf_message(context,
4822 _("XML document is not isds:dmReturnedMessage document"));
4823 err = IE_ISDS;
4824 goto leave;
4826 /* More root elements. This should never happen. */
4827 if (result->nodesetval->nodeNr > 1) {
4828 isds_printf_message(context,
4829 _("XML document has more "
4830 "root isds:dmReturnedMessage elements"));
4831 err = IE_ISDS;
4832 goto leave;
4834 /* One message */
4835 xpath_ctx->node = result->nodesetval->nodeTab[0];
4837 /* Extract the message */
4838 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
4839 if (err) goto leave;
4841 /* Append XML stream into message */
4842 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
4843 switch (strategy) {
4844 case BUFFER_DONT_STORE:
4845 break;
4846 case BUFFER_COPY:
4847 (*message)->raw = malloc(length);
4848 if (!(*message)->raw) {
4849 err = IE_NOMEM;
4850 goto leave;
4852 memcpy((*message)->raw, buffer, length);
4853 (*message)->raw_length = length;
4854 break;
4855 case BUFFER_MOVE:
4856 (*message)->raw = (void *) buffer;
4857 (*message)->raw_length = length;
4858 break;
4859 default:
4860 err = IE_ENUM;
4861 goto leave;
4864 leave:
4865 if (err) {
4866 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
4867 isds_message_free(message);
4870 xmlFreeDoc(message_doc);
4871 xmlXPathFreeObject(result);
4872 xmlXPathFreeContext(xpath_ctx);
4874 if (!err)
4875 isds_log(ILF_ISDS, ILL_DEBUG,
4876 _("Incoming message loaded successfully.\n"));
4877 return err;
4881 /* Download incoming message identified by ID.
4882 * @context is session context
4883 * @message_id is message identifier (you can get them from
4884 * isds_get_list_of_received_messages())
4885 * @message is automatically reallocated message retrieved from ISDS */
4886 isds_error isds_get_received_message(struct isds_ctx *context,
4887 const char *message_id, struct isds_message **message) {
4889 isds_error err = IE_SUCCESS;
4890 xmlDocPtr response = NULL;
4891 xmlChar *code = NULL, *status_message = NULL;
4892 xmlXPathContextPtr xpath_ctx = NULL;
4893 xmlXPathObjectPtr result = NULL;
4895 if (!context) return IE_INVALID_CONTEXT;
4897 /* Free former message if any */
4898 if (message) isds_message_free(message);
4900 /* Do request and check for success */
4901 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
4902 BAD_CAST "MessageDownload", message_id,
4903 &response, &code, &status_message);
4904 if (err) goto leave;
4906 /* Extract data */
4907 xpath_ctx = xmlXPathNewContext(response);
4908 if (!xpath_ctx) {
4909 err = IE_ERROR;
4910 goto leave;
4912 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4913 err = IE_ERROR;
4914 goto leave;
4916 result = xmlXPathEvalExpression(
4917 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
4918 xpath_ctx);
4919 if (!result) {
4920 err = IE_ERROR;
4921 goto leave;
4923 /* Empty response */
4924 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4925 char *message_id_locale = utf82locale((char*) message_id);
4926 isds_printf_message(context,
4927 _("Server did not return any message for ID `%s' "
4928 "on MessageDownload request"), message_id_locale);
4929 free(message_id_locale);
4930 err = IE_ISDS;
4931 goto leave;
4933 /* More messages */
4934 if (result->nodesetval->nodeNr > 1) {
4935 char *message_id_locale = utf82locale((char*) message_id);
4936 isds_printf_message(context,
4937 _("Server did return more messages for ID `%s' "
4938 "on MessageDownload request"), message_id_locale);
4939 free(message_id_locale);
4940 err = IE_ISDS;
4941 goto leave;
4943 /* One message */
4944 xpath_ctx->node = result->nodesetval->nodeTab[0];
4946 /* Extract the message */
4947 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
4948 if (err) goto leave;
4950 /* Save XML blob */
4951 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
4952 &(*message)->raw_length);
4954 leave:
4955 if (err) {
4956 isds_message_free(message);
4959 xmlXPathFreeObject(result);
4960 xmlXPathFreeContext(xpath_ctx);
4962 free(code);
4963 free(status_message);
4964 xmlFreeDoc(response);
4966 if (!err)
4967 isds_log(ILF_ISDS, ILL_DEBUG,
4968 _("MessageDownload request processed by server "
4969 "successfully.\n")
4971 return err;
4975 /* Load signed message from buffer.
4976 * @context is session context
4977 * @outgoing is true if message is outgoing, false if message is incoming
4978 * @buffer is DER encoded PKCS#7 structure with signed message. You can
4979 * retrieve such data from message->raw after calling
4980 * isds_get_signed_{received,sent}_message().
4981 * @length is length of buffer in bytes.
4982 * @message is automatically reallocated message parsed from @buffer.
4983 * @strategy selects how buffer will be attached into raw isds_message member.
4984 * */
4985 isds_error isds_load_signed_message(struct isds_ctx *context,
4986 const _Bool outgoing, const void *buffer, const size_t length,
4987 struct isds_message **message, const isds_buffer_strategy strategy) {
4989 isds_error err = IE_SUCCESS;
4990 xmlDocPtr message_doc = NULL;
4991 xmlXPathContextPtr xpath_ctx = NULL;
4992 xmlXPathObjectPtr result = NULL;
4993 void *xml_stream = NULL;
4994 size_t xml_stream_length = 0;
4996 if (!context) return IE_INVALID_CONTEXT;
4997 if (!message) return IE_INVAL;
4998 isds_message_free(message);
4999 if (!buffer) return IE_INVAL;
5002 /* Extract message from PKCS#7 structure */
5003 err = extract_cms_data(context, buffer, length,
5004 &xml_stream, &xml_stream_length);
5005 if (err) goto leave;
5007 isds_log(ILF_ISDS, ILL_DEBUG, (outgoing) ?
5008 _("Signed outgoing message content:\n%.*s\nEnd of message\n") :
5009 _("Signed incoming message content:\n%.*s\nEnd of message\n"),
5010 xml_stream_length, xml_stream);
5012 /* Convert extracted messages XML stream into XPath context */
5013 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
5014 if (!message_doc) {
5015 err = IE_XML;
5016 goto leave;
5018 xpath_ctx = xmlXPathNewContext(message_doc);
5019 if (!xpath_ctx) {
5020 err = IE_ERROR;
5021 goto leave;
5023 /* XXX: Name spaces mangled for outgoing direction:
5024 * http://isds.czechpoint.cz/v20/SentMessage:
5026 * <q:MessageDownloadResponse
5027 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
5028 * <q:dmReturnedMessage>
5029 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
5030 * <p:dmID>151916</p:dmID>
5031 * ...
5032 * </p:dmDm>
5033 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
5034 * ...
5035 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
5036 * </q:dmReturnedMessage>
5037 * </q:MessageDownloadResponse>
5039 * XXX: Name spaces mangled for incoming direction:
5040 * http://isds.czechpoint.cz/v20/message:
5042 * <q:MessageDownloadResponse
5043 * xmlns:q="http://isds.czechpoint.cz/v20/message">
5044 * <q:dmReturnedMessage>
5045 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
5046 * <p:dmID>151916</p:dmID>
5047 * ...
5048 * </p:dmDm>
5049 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
5050 * ...
5051 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
5052 * </q:dmReturnedMessage>
5053 * </q:MessageDownloadResponse>
5055 * Stupidity of ISDS developers is unlimited */
5056 if (register_namespaces(xpath_ctx, (outgoing) ?
5057 MESSAGE_NS_SIGNED_OUTGOING : MESSAGE_NS_SIGNED_INCOMING)) {
5058 err = IE_ERROR;
5059 goto leave;
5061 /* XXX: Embeded message XML document is always rooted as
5062 * /sisds:MessageDownloadResponse (even outgoing message). */
5063 result = xmlXPathEvalExpression(
5064 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
5065 xpath_ctx);
5066 if (!result) {
5067 err = IE_ERROR;
5068 goto leave;
5070 /* Empty embedded message */
5071 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5072 isds_printf_message(context,
5073 _("XML document embedded into PKCS#7 structure is not "
5074 "sisds:dmReturnedMessage document"));
5075 err = IE_ISDS;
5076 goto leave;
5078 /* More embedded messages */
5079 if (result->nodesetval->nodeNr > 1) {
5080 isds_printf_message(context,
5081 _("Embeded XML document into PKCS#7 structure has more "
5082 "root sisds:dmReturnedMessage elements"));
5083 err = IE_ISDS;
5084 goto leave;
5086 /* One embedded message */
5087 xpath_ctx->node = result->nodesetval->nodeTab[0];
5089 /* Extract the message */
5090 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
5091 if (err) goto leave;
5093 /* Append raw CMS structure into message */
5094 (*message)->raw_type = (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
5095 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
5096 switch (strategy) {
5097 case BUFFER_DONT_STORE:
5098 break;
5099 case BUFFER_COPY:
5100 (*message)->raw = malloc(length);
5101 if (!(*message)->raw) {
5102 err = IE_NOMEM;
5103 goto leave;
5105 memcpy((*message)->raw, buffer, length);
5106 (*message)->raw_length = length;
5107 break;
5108 case BUFFER_MOVE:
5109 (*message)->raw = (void *) buffer;
5110 (*message)->raw_length = length;
5111 break;
5112 default:
5113 err = IE_ENUM;
5114 goto leave;
5118 leave:
5119 if (err) {
5120 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
5121 isds_message_free(message);
5124 xmlFreeDoc(message_doc);
5125 cms_data_free(xml_stream);
5126 xmlXPathFreeObject(result);
5127 xmlXPathFreeContext(xpath_ctx);
5129 if (!err)
5130 isds_log(ILF_ISDS, ILL_DEBUG,
5131 _("Signed message loaded successfully.\n"));
5132 return err;
5136 /* Download signed incoming/outgoing message identified by ID.
5137 * @context is session context
5138 * @output is true for outging message, false for incoming message
5139 * @message_id is message identifier (you can get them from
5140 * isds_get_list_of_{sent,received}_messages())
5141 * @message is automatically reallocated message retrieved from ISDS. The raw
5142 * memeber will be filled with PKCS#7 structure in DER format. */
5143 _hidden isds_error isds_get_signed_message(struct isds_ctx *context,
5144 const _Bool outgoing, const char *message_id,
5145 struct isds_message **message) {
5147 isds_error err = IE_SUCCESS;
5148 xmlDocPtr response = NULL;
5149 xmlChar *code = NULL, *status_message = NULL;
5150 xmlXPathContextPtr xpath_ctx = NULL;
5151 xmlXPathObjectPtr result = NULL;
5152 char *encoded_structure = NULL;
5153 void *raw = NULL;
5154 size_t raw_length = 0;
5156 if (!context) return IE_INVALID_CONTEXT;
5157 if (!message) return IE_INVAL;
5158 isds_message_free(message);
5160 /* Do request and check for success */
5161 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
5162 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
5163 BAD_CAST "SignedMessageDownload",
5164 message_id, &response, &code, &status_message);
5165 if (err) goto leave;
5167 /* Find signed message, extract it into raw and maybe free
5168 * response */
5169 err = find_extract_signed_data_free_response(context,
5170 (xmlChar *)message_id, &response,
5171 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
5172 BAD_CAST "SignedMessageDownload",
5173 &raw, &raw_length);
5174 if (err) goto leave;
5176 /* Parse message */
5177 err = isds_load_signed_message(context, outgoing, raw, raw_length,
5178 message, BUFFER_MOVE);
5179 if (err) goto leave;
5181 raw = NULL;
5183 leave:
5184 if (err) {
5185 isds_message_free(message);
5188 free(encoded_structure);
5189 xmlXPathFreeObject(result);
5190 xmlXPathFreeContext(xpath_ctx);
5191 free(raw);
5193 free(code);
5194 free(status_message);
5195 xmlFreeDoc(response);
5197 if (!err)
5198 isds_log(ILF_ISDS, ILL_DEBUG,
5199 (outgoing) ?
5200 _("SignedSentMessageDownload request processed by server "
5201 "successfully.\n") :
5202 _("SignedMessageDownload request processed by server "
5203 "successfully.\n")
5205 return err;
5209 /* Download signed incoming message identified by ID.
5210 * @context is session context
5211 * @message_id is message identifier (you can get them from
5212 * isds_get_list_of_received_messages())
5213 * @message is automatically reallocated message retrieved from ISDS. The raw
5214 * memeber will be filled with PKCS#7 structure in DER format. */
5215 isds_error isds_get_signed_received_message(struct isds_ctx *context,
5216 const char *message_id, struct isds_message **message) {
5217 return isds_get_signed_message(context, 0, message_id, message);
5221 /* Download signed outgoing message identified by ID.
5222 * @context is session context
5223 * @message_id is message identifier (you can get them from
5224 * isds_get_list_of_sent_messages())
5225 * @message is automatically reallocated message retrieved from ISDS. The raw
5226 * memeber will be filled with PKCS#7 structure in DER format. */
5227 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
5228 const char *message_id, struct isds_message **message) {
5229 return isds_get_signed_message(context, 1, message_id, message);
5233 /* Retrieve hash of message identified by ID stored in ISDS.
5234 * @context is session context
5235 * @message_id is message identifier
5236 * @hash is automatically reallocated message hash downloaded from ISDS.
5237 * Message must exist in system and must not be deleted. */
5238 isds_error isds_download_message_hash(struct isds_ctx *context,
5239 const char *message_id, struct isds_hash **hash) {
5241 isds_error err = IE_SUCCESS;
5242 xmlDocPtr response = NULL;
5243 xmlChar *code = NULL, *status_message = NULL;
5244 xmlXPathContextPtr xpath_ctx = NULL;
5245 xmlXPathObjectPtr result = NULL;
5247 if (!context) return IE_INVALID_CONTEXT;
5249 isds_hash_free(hash);
5251 err = build_send_check_message_request(context, SERVICE_DM_INFO,
5252 BAD_CAST "VerifyMessage", message_id,
5253 &response, &code, &status_message);
5254 if (err) goto leave;
5257 /* Extract data */
5258 xpath_ctx = xmlXPathNewContext(response);
5259 if (!xpath_ctx) {
5260 err = IE_ERROR;
5261 goto leave;
5263 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5264 err = IE_ERROR;
5265 goto leave;
5267 result = xmlXPathEvalExpression(
5268 BAD_CAST "/isds:VerifyMessageResponse",
5269 xpath_ctx);
5270 if (!result) {
5271 err = IE_ERROR;
5272 goto leave;
5274 /* Empty response */
5275 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5276 char *message_id_locale = utf82locale((char*) message_id);
5277 isds_printf_message(context,
5278 _("Server did not return any response for ID `%s' "
5279 "on VerifyMessage request"), message_id_locale);
5280 free(message_id_locale);
5281 err = IE_ISDS;
5282 goto leave;
5284 /* More responses */
5285 if (result->nodesetval->nodeNr > 1) {
5286 char *message_id_locale = utf82locale((char*) message_id);
5287 isds_printf_message(context,
5288 _("Server did return more responses for ID `%s' "
5289 "on VerifyMessage request"), message_id_locale);
5290 free(message_id_locale);
5291 err = IE_ISDS;
5292 goto leave;
5294 /* One response */
5295 xpath_ctx->node = result->nodesetval->nodeTab[0];
5297 /* Extract the hash */
5298 err = find_and_extract_DmHash(context, hash, xpath_ctx);
5300 leave:
5301 if (err) {
5302 isds_hash_free(hash);
5305 xmlXPathFreeObject(result);
5306 xmlXPathFreeContext(xpath_ctx);
5308 free(code);
5309 free(status_message);
5310 xmlFreeDoc(response);
5312 if (!err)
5313 isds_log(ILF_ISDS, ILL_DEBUG,
5314 _("VerifyMessage request processed by server "
5315 "successfully.\n")
5317 return err;
5321 /* Mark message as read. This is a transactional commit function to acknoledge
5322 * to ISDS the message has been downloaded and processed by client properly.
5323 * @context is session context
5324 * @message_id is message identifier. */
5325 isds_error isds_mark_message_read(struct isds_ctx *context,
5326 const char *message_id) {
5328 isds_error err = IE_SUCCESS;
5329 xmlDocPtr response = NULL;
5330 xmlChar *code = NULL, *status_message = NULL;
5332 if (!context) return IE_INVALID_CONTEXT;
5334 /* Do request and check for success */
5335 err = build_send_check_message_request(context, SERVICE_DM_INFO,
5336 BAD_CAST "MarkMessageAsDownloaded", message_id,
5337 &response, &code, &status_message);
5339 free(code);
5340 free(status_message);
5341 xmlFreeDoc(response);
5343 if (!err)
5344 isds_log(ILF_ISDS, ILL_DEBUG,
5345 _("MarkMessageAsDownloaded request processed by server "
5346 "successfully.\n")
5348 return err;
5352 #undef INSERT_STRING_ATTRIBUTE
5353 #undef INSERT_ULONGINTNOPTR
5354 #undef INSERT_ULONGINT
5355 #undef INSERT_LONGINT
5356 #undef INSERT_BOOLEAN
5357 #undef INSERT_STRING
5358 #undef EXTRACT_STRING_ATTRIBUTE
5359 #undef EXTRACT_ULONGINT
5360 #undef EXTRACT_LONGINT
5361 #undef EXTRACT_BOOLEAN
5362 #undef EXTRACT_STRING
5365 /* Compute hash of message from raw representation and store it into envelope.
5366 * Original hash structure will be destroyed in envelope.
5367 * @context is session context
5368 * @message is message carrying raw XML message blob
5369 * @algorithm is desired hash algorithm to use */
5370 isds_error isds_compute_message_hash(struct isds_ctx *context,
5371 struct isds_message *message, const isds_hash_algorithm algorithm) {
5372 isds_error err = IE_SUCCESS;
5373 const char *nsuri;
5374 size_t phys_start, phys_end;
5375 char *phys_path = NULL;
5376 struct isds_hash *new_hash = NULL;
5379 if (!context) return IE_INVALID_CONTEXT;
5380 if (!message) return IE_INVAL;
5382 if (!message->raw) {
5383 isds_log_message(context,
5384 _("Message does not carry raw representation"));
5385 return IE_INVAL;
5388 switch (message->raw_type) {
5389 case RAWTYPE_INCOMING_MESSAGE:
5390 nsuri = ISDS_NS;
5391 break;
5392 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
5393 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
5394 nsuri = SISDS_INCOMING_NS;
5395 break;
5396 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
5397 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
5398 nsuri = SISDS_OUTGOING_NS;
5399 break;
5400 default:
5401 isds_log_message(context, _("Bad raw representation type"));
5402 return IE_INVAL;
5403 break;
5407 /* XXX: Hash is computed from original string represinting isds:dmDm
5408 * subtree. That means no encoding, white space, xmlns attributes changes.
5409 * In other words, input for hash can be invalid XML stream. */
5411 /* Extract dmDM content as bit stream */
5412 /* FIXME: Signed messages has mangled namespace and are stored in CMS */
5413 phys_path = astrcat(nsuri,
5414 PHYSXML_NS_SEPARATOR "dmReturnedMessage"
5415 PHYSXML_ELEMENT_SEPARATOR
5416 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm");
5417 if (!phys_path) {
5418 err = IE_NOMEM;
5419 goto leave;
5421 err = find_element_boundary(message->raw, message->raw_length,
5422 phys_path, &phys_start, &phys_end);
5423 zfree(phys_path);
5424 if (err) {
5425 isds_log_message(context,
5426 _("Substring with isds:dmDM could not be located "
5427 "in raw message"));
5428 goto leave;
5432 /* Compute hash */
5433 new_hash = calloc(1, sizeof(*new_hash));
5434 if (!new_hash) {
5435 err = IE_NOMEM;
5436 goto leave;
5438 new_hash->algorithm = algorithm;
5439 err = compute_hash(message->raw + phys_start, phys_end - phys_start + 1,
5440 new_hash);
5441 if (err) {
5442 isds_log_message(context, _("Could not compute message hash"));
5443 goto leave;
5446 /* Save computed hash */
5447 if (!message->envelope) {
5448 message->envelope = calloc(1, sizeof(*message->envelope));
5449 if (!message->envelope) {
5450 err = IE_NOMEM;
5451 goto leave;
5454 isds_hash_free(&message->envelope->hash);
5455 message->envelope->hash = new_hash;
5457 leave:
5458 if (err) {
5459 isds_hash_free(&new_hash);
5462 free(phys_path);
5463 return err;
5467 /* Compare two hashes.
5468 * @h1 is first hash
5469 * @h2 is another hash
5470 * @return
5471 * IE_SUCCESS if hashes equal
5472 * IE_NOTUNIQ if hashes are comparable, but they don't equal
5473 * IE_ENUM if not comparable, but both structures defined
5474 * IE_INVAL if some of the structures are undefined (NULL)
5475 * IE_ERROR if internal error occurs */
5476 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
5477 if (h1 == NULL || h2 == NULL) return IE_INVAL;
5478 if (h1->algorithm != h2->algorithm) return IE_ENUM;
5479 if (h1->length != h2->length) return IE_ERROR;
5480 if (h1->length > 0 && !h1->value) return IE_ERROR;
5481 if (h2->length > 0 && !h2->value) return IE_ERROR;
5483 for (int i = 0; i < h1->length; i++) {
5484 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
5485 return IE_NOTEQUAL;
5487 return IE_SUCCESS;
5491 /* Check message has gone through ISDS by comparing message hash stored in
5492 * ISDS and locally computed hash. You must provide message with valid raw
5493 * member (do not use isds_load_*_message(..., BUFFER_DONT_STORE)).
5494 * This is convenient wrapper for isds_download_message_hash(),
5495 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
5496 * @context is session context
5497 * @message is message with valid raw and envelope member; envelope->hash
5498 * member will be changed during funcion run. Use envelope on heap only.
5499 * @return
5500 * IE_SUCCESS if message originates in ISDS
5501 * IE_NOTEQUAL if message is unknown to ISDS
5502 * other code for other errors */
5503 isds_error isds_verify_message_hash(struct isds_ctx *context,
5504 struct isds_message *message) {
5505 isds_error err = IE_SUCCESS;
5506 struct isds_hash *downloaded_hash = NULL;
5508 if (!context) return IE_INVALID_CONTEXT;
5509 if (!message) return IE_INVAL;
5511 if (!message->envelope) {
5512 isds_log_message(context,
5513 _("Given message structure is missing envelope"));
5514 return IE_INVAL;
5516 if (!message->raw) {
5517 isds_log_message(context,
5518 _("Given message structure is missing raw representation"));
5519 return IE_INVAL;
5522 err = isds_download_message_hash(context, message->envelope->dmID,
5523 &downloaded_hash);
5524 if (err) goto leave;
5526 err = isds_compute_message_hash(context, message,
5527 downloaded_hash->algorithm);
5528 if (err) goto leave;
5530 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
5532 leave:
5533 isds_hash_free(&downloaded_hash);
5534 return err;
5538 /* Search for document by document ID in list of documents. IDs are compared
5539 * as UTF-8 string.
5540 * @documents is list of isds_documents
5541 * @id is document identifier
5542 * @return first matching document or NULL. */
5543 const struct isds_document *isds_find_document_by_id(
5544 const struct isds_list *documents, const char *id) {
5545 const struct isds_list *item;
5546 const struct isds_document *document;
5548 for (item = documents; item; item = item->next) {
5549 document = (struct isds_document *) item->data;
5550 if (!document) continue;
5552 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
5553 return document;
5556 return NULL;
5560 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
5561 struct isds_message **message);
5562 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
5563 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
5564 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
5565 struct isds_address **address);
5567 int isds_message_free(struct isds_message **message);
5568 int isds_address_free(struct isds_address **address);
5572 /* Makes known all relevant namespaces to given XPath context
5573 * @xpat_ctx is XPath context
5574 * @message_ns selects propper message name space. Unsisnged and signed
5575 * messages differs.
5576 * prefix and to URI ISDS_NS */
5577 _hidden isds_error register_namespaces(xmlXPathContextPtr xpath_ctx,
5578 const message_ns_type message_ns) {
5579 const xmlChar *message_namespace = NULL;
5581 if (!xpath_ctx) return IE_ERROR;
5583 switch(message_ns) {
5584 case MESSAGE_NS_UNSIGNED:
5585 message_namespace = BAD_CAST ISDS_NS; break;
5586 case MESSAGE_NS_SIGNED_INCOMING:
5587 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
5588 case MESSAGE_NS_SIGNED_OUTGOING:
5589 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
5590 case MESSAGE_NS_SIGNED_DELIVERY:
5591 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
5592 default:
5593 return IE_ENUM;
5596 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
5597 return IE_ERROR;
5598 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
5599 return IE_ERROR;
5600 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
5601 return IE_ERROR;
5602 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
5603 return IE_ERROR;
5604 return IE_SUCCESS;