Implement GetDataBoxUsers as isds_GetDataBoxUsers()
[libisds.git] / src / isds.c
blob365ecb230971b417f62ba12d3b7700065a8358c0
1 #define _XOPEN_SOURCE 500 /* strdup from string.h, strptime from time.h */
2 #include <stdlib.h>
3 #include <string.h>
4 #include <stdio.h>
5 #include <stdarg.h>
6 #include <ctype.h>
7 #include <stdint.h>
8 #include "isds_priv.h"
9 #include "utils.h"
10 #include "soap.h"
11 #include "validator.h"
12 #include "crypto.h"
13 #include <gpg-error.h> /* Because of ksba or gpgme */
14 #include "physxml.h"
17 /* Free isds_list with all member data.
18 * @list list to free, on return will be NULL */
19 void isds_list_free(struct isds_list **list) {
20 struct isds_list *item, *next_item;
22 if (!list || !*list) return;
24 for(item = *list; item; item = next_item) {
25 (item->destructor)(&(item->data));
26 next_item = item->next;
27 free(item);
30 *list = NULL;
34 /* Deallocate structure isds_hash and NULL it.
35 * @hash hash to to free */
36 void isds_hash_free(struct isds_hash **hash) {
37 if(!hash || !*hash) return;
38 free((*hash)->value);
39 zfree((*hash));
43 /* Deallocate structure isds_PersonName recursively and NULL it */
44 static void isds_PersonName_free(struct isds_PersonName **person_name) {
45 if (!person_name || !*person_name) return;
47 free((*person_name)->pnFirstName);
48 free((*person_name)->pnMiddleName);
49 free((*person_name)->pnLastName);
50 free((*person_name)->pnLastNameAtBirth);
52 free(*person_name);
53 *person_name = NULL;
57 /* Deallocate structure isds_BirthInfo recursively and NULL it */
58 static void isds_BirthInfo_free(struct isds_BirthInfo **birth_info) {
59 if (!birth_info || !*birth_info) return;
61 free((*birth_info)->biDate);
62 free((*birth_info)->biCity);
63 free((*birth_info)->biCounty);
64 free((*birth_info)->biState);
66 free(*birth_info);
67 *birth_info = NULL;
71 /* Deallocate structure isds_Address recursively and NULL it */
72 static void isds_Address_free(struct isds_Address **address) {
73 if (!address || !*address) return;
75 free((*address)->adCity);
76 free((*address)->adStreet);
77 free((*address)->adNumberInStreet);
78 free((*address)->adNumberInMunicipality);
79 free((*address)->adZipCode);
80 free((*address)->adState);
82 free(*address);
83 *address = NULL;
87 /* Deallocate structure isds_DbOwnerInfo recursively and NULL it */
88 void isds_DbOwnerInfo_free(struct isds_DbOwnerInfo **db_owner_info) {
89 if (!db_owner_info || !*db_owner_info) return;
91 free((*db_owner_info)->dbID);
92 free((*db_owner_info)->dbType);
93 free((*db_owner_info)->ic);
94 isds_PersonName_free(&((*db_owner_info)->personName));
95 free((*db_owner_info)->firmName);
96 isds_BirthInfo_free(&((*db_owner_info)->birthInfo));
97 isds_Address_free(&((*db_owner_info)->address));
98 free((*db_owner_info)->nationality);
99 free((*db_owner_info)->email);
100 free((*db_owner_info)->telNumber);
101 free((*db_owner_info)->identifier);
102 free((*db_owner_info)->registryCode);
103 free((*db_owner_info)->dbState);
104 free((*db_owner_info)->dbEffectiveOVM);
106 free(*db_owner_info);
107 *db_owner_info = NULL;
110 /* Deallocate structure isds_DbUserInfo recursively and NULL it */
111 void isds_DbUserInfo_free(struct isds_DbUserInfo **db_user_info) {
112 if (!db_user_info || !*db_user_info) return;
114 free((*db_user_info)->userID);
115 free((*db_user_info)->userType);
116 free((*db_user_info)->userPrivils);
117 isds_PersonName_free(&((*db_user_info)->personName));
118 isds_Address_free(&((*db_user_info)->address));
119 free((*db_user_info)->biDate);
120 free((*db_user_info)->ic);
121 free((*db_user_info)->firmName);
122 free((*db_user_info)->caStreet);
123 free((*db_user_info)->caCity);
124 free((*db_user_info)->caZipCode);
126 zfree(*db_user_info);
130 /* Deallocate struct isds_event recursively and NULL it */
131 void isds_event_free(struct isds_event **event) {
132 if (!event || !*event) return;
134 free((*event)->time);
135 free((*event)->type);
136 free((*event)->description);
137 zfree(*event);
141 /* Deallocate struct isds_envelope recursively and NULL it */
142 void isds_envelope_free(struct isds_envelope **envelope) {
143 if (!envelope || !*envelope) return;
145 free((*envelope)->dmID);
146 free((*envelope)->dbIDSender);
147 free((*envelope)->dmSender);
148 free((*envelope)->dmSenderAddress);
149 free((*envelope)->dmSenderType);
150 free((*envelope)->dmRecipient);
151 free((*envelope)->dmRecipientAddress);
152 free((*envelope)->dmAmbiguousRecipient);
153 free((*envelope)->dmType);
155 free((*envelope)->dmOrdinal);
156 free((*envelope)->dmMessageStatus);
157 free((*envelope)->dmDeliveryTime);
158 free((*envelope)->dmAcceptanceTime);
159 isds_hash_free(&(*envelope)->hash);
160 free((*envelope)->timestamp);
161 isds_list_free(&(*envelope)->events);
163 free((*envelope)->dmSenderOrgUnit);
164 free((*envelope)->dmSenderOrgUnitNum);
165 free((*envelope)->dbIDRecipient);
166 free((*envelope)->dmRecipientOrgUnit);
167 free((*envelope)->dmRecipientOrgUnitNum);
168 free((*envelope)->dmToHands);
169 free((*envelope)->dmAnnotation);
170 free((*envelope)->dmRecipientRefNumber);
171 free((*envelope)->dmSenderRefNumber);
172 free((*envelope)->dmRecipientIdent);
173 free((*envelope)->dmSenderIdent);
175 free((*envelope)->dmLegalTitleLaw);
176 free((*envelope)->dmLegalTitleYear);
177 free((*envelope)->dmLegalTitleSect);
178 free((*envelope)->dmLegalTitlePar);
179 free((*envelope)->dmLegalTitlePoint);
181 free((*envelope)->dmPersonalDelivery);
182 free((*envelope)->dmAllowSubstDelivery);
183 free((*envelope)->dmOVM);
185 free(*envelope);
186 *envelope = NULL;
190 /* Deallocate struct isds_message recursively and NULL it */
191 void isds_message_free(struct isds_message **message) {
192 if (!message || !*message) return;
194 free((*message)->raw);
195 isds_envelope_free(&((*message)->envelope));
196 isds_list_free(&((*message)->documents));
198 free(*message);
199 *message = NULL;
203 /* Deallocate struct isds_document recursively and NULL it */
204 void isds_document_free(struct isds_document **document) {
205 if (!document || !*document) return;
207 free((*document)->data);
208 free((*document)->dmMimeType);
209 free((*document)->dmFileGuid);
210 free((*document)->dmUpFileGuid);
211 free((*document)->dmFileDescr);
212 free((*document)->dmFormat);
214 free(*document);
215 *document = NULL;
219 /* Deallocate struct isds_message_copy recursively and NULL it */
220 void isds_message_copy_free(struct isds_message_copy **copy) {
221 if (!copy || !*copy) return;
223 free((*copy)->dbIDRecipient);
224 free((*copy)->dmRecipientOrgUnit);
225 free((*copy)->dmRecipientOrgUnitNum);
226 free((*copy)->dmToHands);
228 free((*copy)->dmStatus);
229 free((*copy)->dmID);
231 zfree(*copy);
235 /* Initialize ISDS library.
236 * Global function, must be called before other functions.
237 * If it failes you can not use ISDS library and must call isds_cleanup() to
238 * free partially inititialized global variables. */
239 isds_error isds_init(void) {
240 /* NULL global variables */
241 log_facilities = ILF_ALL;
242 log_level = ILL_WARNING;
244 #if ENABLE_NLS
245 /* Initialize gettext */
246 bindtextdomain(PACKAGE, LOCALEDIR);
247 #endif
249 /* Initialize CURL */
250 if (curl_global_init(CURL_GLOBAL_ALL)) {
251 isds_log(ILF_ISDS, ILL_CRIT, _("CURL library initialization failed\n"));
252 return IE_ERROR;
255 /* Inicialize gpg-error because of gpgme and ksba */
256 if (gpg_err_init()) {
257 isds_log(ILF_ISDS, ILL_CRIT,
258 _("gpg-error library initialization failed\n"));
259 return IE_ERROR;
262 /* Initialize GPGME */
263 if (init_gpgme()) {
264 isds_log(ILF_ISDS, ILL_CRIT,
265 _("GPGME library initialization failed\n"));
266 return IE_ERROR;
269 /* Initialize gcrypt */
270 if (init_gcrypt()) {
271 isds_log(ILF_ISDS, ILL_CRIT,
272 _("gcrypt library initialization failed\n"));
273 return IE_ERROR;
276 /* This can _exit() current program. Find not so assertive check. */
277 LIBXML_TEST_VERSION;
279 /* Check expat */
280 if (init_expat()) {
281 isds_log(ILF_ISDS, ILL_CRIT,
282 _("expat library initialization failed\n"));
283 return IE_ERROR;
286 /* Allocate global variables */
289 return IE_SUCCESS;
293 /* Deinicialize ISDS library.
294 * Global function, must be called as last library function. */
295 isds_error isds_cleanup(void) {
296 /* XML */
297 xmlCleanupParser();
299 /* Curl */
300 curl_global_cleanup();
302 return IE_SUCCESS;
306 /* Return text description of ISDS error */
307 const char *isds_strerror(const isds_error error) {
308 switch (error) {
309 case IE_SUCCESS:
310 return(_("Success")); break;
311 case IE_ERROR:
312 return(_("Unspecified error")); break;
313 case IE_NOTSUP:
314 return(_("Not supported")); break;
315 case IE_INVAL:
316 return(_("Invalid value")); break;
317 case IE_INVALID_CONTEXT:
318 return(_("Invalid context")); break;
319 case IE_NOT_LOGGED_IN:
320 return(_("Not logged in")); break;
321 case IE_CONNECTION_CLOSED:
322 return(_("Connection closed")); break;
323 case IE_TIMED_OUT:
324 return(_("Timed out")); break;
325 case IE_NOEXIST:
326 return(_("Not exist")); break;
327 case IE_NOMEM:
328 return(_("Out of memory")); break;
329 case IE_NETWORK:
330 return(_("Network problem")); break;
331 case IE_HTTP:
332 return(_("HTTP problem")); break;
333 case IE_SOAP:
334 return(_("SOAP problem")); break;
335 case IE_XML:
336 return(_("XML problem")); break;
337 case IE_ISDS:
338 return(_("ISDS server problem")); break;
339 case IE_ENUM:
340 return(_("Invalid enum value")); break;
341 case IE_DATE:
342 return(_("Invalid date value")); break;
343 case IE_2BIG:
344 return(_("Too big")); break;
345 case IE_NOTUNIQ:
346 return(_("Value not unique")); break;
347 case IE_NOTEQUAL:
348 return(_("Values not uqual")); break;
349 case IE_PARTIAL_SUCCESS:
350 return(_("Some suboperations failed")); break;
351 default:
352 return(_("Unknown error"));
357 /* Create ISDS context.
358 * Each context can be used for different sessions to (possibly) differnet
359 * ISDS server with different credentials. */
360 struct isds_ctx *isds_ctx_create(void) {
361 struct isds_ctx *context;
362 context = malloc(sizeof(*context));
363 if (context) memset(context, 0, sizeof(*context));
364 return context;
368 /* Destroy ISDS context and free memmory.
369 * @context will be NULLed on success. */
370 isds_error isds_ctx_free(struct isds_ctx **context) {
371 if (!context || !*context) {
372 return IE_INVALID_CONTEXT;
375 /* Discard credentials */
376 isds_logout(*context);
378 /* Free other structures */
379 free((*context)->tls_verify_server);
380 free((*context)->tls_ca_file);
381 free((*context)->tls_ca_dir);
382 free((*context)->long_message);
384 free(*context);
385 *context = NULL;
386 return IE_SUCCESS;
390 /* Return long message text produced by library fucntion, e.g. detailed error
391 * mesage. Returned pointer is only valid until new library function is
392 * called for the same context. Could be NULL, especially if NULL context is
393 * supplied. Return string is locale encoded. */
394 char *isds_long_message(const struct isds_ctx *context) {
395 if (!context) return NULL;
396 return context->long_message;
400 /* Stores message into context' long_message buffer.
401 * Application can pick the message up using isds_long_message().
402 * NULL @message truncates the buffer but does not deallocate it.
403 * @message is coded in locale encoding */
404 _hidden isds_error isds_log_message(struct isds_ctx *context,
405 const char *message) {
406 char *buffer;
407 size_t length;
409 if (!context) return IE_INVALID_CONTEXT;
411 /* FIXME: Check for integer overflow */
412 length = 1 + ((message) ? strlen(message) : 0);
413 buffer = realloc(context->long_message, length);
414 if (!buffer) return IE_NOMEM;
416 if (message)
417 strcpy(buffer, message);
418 else
419 *buffer = '\0';
421 context->long_message = buffer;
422 return IE_SUCCESS;
426 /* Appends message into context' long_message buffer.
427 * Application can pick the message up using isds_long_message().
428 * NULL message has void effect. */
429 _hidden isds_error isds_append_message(struct isds_ctx *context,
430 const char *message) {
431 char *buffer;
432 size_t old_length, length;
434 if (!context) return IE_INVALID_CONTEXT;
435 if (!message) return IE_SUCCESS;
436 if (!context->long_message)
437 return isds_log_message(context, message);
439 old_length = strlen(context->long_message);
440 /* FIXME: Check for integer overflow */
441 length = 1 + old_length + strlen(message);
442 buffer = realloc(context->long_message, length);
443 if (!buffer) return IE_NOMEM;
445 strcpy(buffer + old_length, message);
447 context->long_message = buffer;
448 return IE_SUCCESS;
452 /* Stores formated message into context' long_message buffer.
453 * Application can pick the message up using isds_long_message(). */
454 _hidden isds_error isds_printf_message(struct isds_ctx *context,
455 const char *format, ...) {
456 va_list ap;
457 int length;
459 if (!context) return IE_INVALID_CONTEXT;
460 va_start(ap, format);
461 length = isds_vasprintf(&(context->long_message), format, ap);
462 va_end(ap);
464 return (length < 0) ? IE_ERROR: IE_SUCCESS;
468 /* Set logging up.
469 * @facilities is bitmask of isds_log_facility values,
470 * @level is verbosity level. */
471 void isds_set_logging(const unsigned int facilities,
472 const isds_log_level level) {
473 log_facilities = facilities;
474 log_level = level;
478 /* Log @message in class @facility with log @level into global log. @message
479 * is printf(3) formating string, variadic arguments may be neccessary.
480 * For debugging purposes. */
481 _hidden isds_error isds_log(const isds_log_facility facility,
482 const isds_log_level level, const char *message, ...) {
483 va_list ap;
485 if (level > log_level) return IE_SUCCESS;
486 if (!(log_facilities & facility)) return IE_SUCCESS;
487 if (!message) return IE_INVAL;
489 /* TODO: Allow to register output function provided by application
490 * (e.g. fprintf to stderr or copy to text area GUI widget). */
492 va_start(ap, message);
493 vfprintf(stderr, message, ap);
494 va_end(ap);
495 /* Line buffered printf is default.
496 * fflush(stderr);*/
498 return IE_SUCCESS;
502 /* Set timeout in miliseconds for each network job like connecting to server
503 * or sending message. Use 0 to disable timeout limits. */
504 isds_error isds_set_timeout(struct isds_ctx *context,
505 const unsigned int timeout) {
506 if (!context) return IE_INVALID_CONTEXT;
508 context->timeout = timeout;
510 if (context->curl) {
511 CURLcode curl_err;
513 curl_err = curl_easy_setopt(context->curl, CURLOPT_NOSIGNAL, 1);
514 if (!curl_err)
515 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT_MS,
516 context->timeout);
517 if (curl_err) return IE_ERROR;
520 return IE_SUCCESS;
524 /* Register callback function libisds calls periodocally during HTTP data
525 * transfer.
526 * @context is session context
527 * @callback is function provided by application libsds will call. See type
528 * defition for @callback argument explanation.
529 * @data is application specific data @callback gets as last argument */
530 isds_error isds_set_progress_callback(struct isds_ctx *context,
531 isds_progress_callback callback, void *data) {
532 if (!context) return IE_INVALID_CONTEXT;
534 context->progress_callback = callback;
535 context->progress_callback_data = data;
537 return IE_SUCCESS;
541 /* Change SSL/TLS settings.
542 * @context is context which setting will be applied to
543 * @option is name of option. It determines the type of last argument. See
544 * isds_tls_option definition for more info.
545 * @... is value of new setting. Type is determined by @option
546 * */
547 isds_error isds_set_tls(struct isds_ctx *context, const isds_tls_option option,
548 ...) {
549 isds_error err = IE_SUCCESS;
550 va_list ap;
551 char *pointer, *string;
553 if (!context) return IE_INVALID_CONTEXT;
555 va_start(ap, option);
557 #define REPLACE_VA_STRING(destination) \
558 string = va_arg(ap, char *); \
559 if (string) { \
560 pointer = realloc((destination), 1 + strlen(string)); \
561 if (!pointer) { err = IE_NOMEM; goto leave; } \
562 strcpy(pointer, string); \
563 (destination) = pointer; \
564 } else { \
565 free(destination); \
566 (destination) = NULL; \
569 switch (option) {
570 case ITLS_VERIFY_SERVER:
571 if (!context->tls_verify_server) {
572 context->tls_verify_server =
573 malloc(sizeof(*context->tls_verify_server));
574 if (!context->tls_verify_server) {
575 err = IE_NOMEM; goto leave;
578 *context->tls_verify_server = (_Bool) (0 != va_arg(ap, int));
579 break;
581 case ITLS_CA_FILE:
582 REPLACE_VA_STRING(context->tls_ca_file);
583 break;
584 case ITLS_CA_DIRECTORY:
585 REPLACE_VA_STRING(context->tls_ca_dir);
586 break;
588 default:
589 err = IE_ENUM; goto leave;
592 #undef REPLACE_VA_STRING
594 leave:
595 va_end(ap);
596 return err;
600 /* Discard credentials.
601 * Only that. It does not cause log out, connection close or similar. */
602 static isds_error discard_credentials(struct isds_ctx *context) {
603 if(!context) return IE_INVALID_CONTEXT;
605 if (context->username) {
606 memset(context->username, 0, strlen(context->username));
607 free(context->username);
608 context->username = NULL;
610 if (context->password) {
611 memset(context->password, 0, strlen(context->password));
612 free(context->password);
613 context->password = NULL;
616 return IE_SUCCESS;
620 /* Connect and log in into ISDS server.
621 * @url is address of ISDS web service
622 * @username is user name of ISDS user
623 * @password is user's secret password
624 * @certificate is NULL terminated string with PEM formated client's
625 * certificate. Use NULL if only password autentication should be performed.
626 * @key is private key for client's certificate as (base64 encoded?) NULL
627 * terminated string. Use NULL if only password autentication is desired.
628 * */
629 isds_error isds_login(struct isds_ctx *context, const char *url,
630 const char *username, const char *password,
631 const char *certificate, const char* key) {
632 isds_error err = IE_NOT_LOGGED_IN;
633 isds_error soap_err;
634 xmlNsPtr isds_ns = NULL;
635 xmlNodePtr request = NULL;
636 xmlNodePtr response = NULL;
638 if (!context) return IE_INVALID_CONTEXT;
639 if (!url || !username || !password) return IE_INVAL;
640 if (certificate || key) return IE_NOTSUP;
642 /* Store configuration */
643 free(context->url);
644 context->url = strdup(url);
645 if (!(context->url))
646 return IE_NOMEM;
648 /* Close connection if already logged in */
649 if (context->curl) {
650 close_connection(context);
653 /* Prepare CURL handle */
654 context->curl = curl_easy_init();
655 if (!(context->curl))
656 return IE_ERROR;
658 /* Build login request */
659 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
660 if (!request) {
661 isds_log_message(context, _("Could build ISDS login request"));
662 return IE_ERROR;
664 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
665 if(!isds_ns) {
666 isds_log_message(context, _("Could not create ISDS name space"));
667 xmlFreeNode(request);
668 return IE_ERROR;
670 xmlSetNs(request, isds_ns);
672 /* Store credentials */
673 /* FIXME: mlock password
674 * (I have a library) */
675 discard_credentials(context);
676 context->username = strdup(username);
677 context->password = strdup(password);
678 if (!(context->username && context->password)) {
679 discard_credentials(context);
680 xmlFreeNode(request);
681 return IE_NOMEM;
684 isds_log(ILF_ISDS, ILL_DEBUG, _("Logging user %s into server %s\n"),
685 username, url);
687 /* Send login request */
688 soap_err = soap(context, "dz", request, &response, NULL, NULL);
690 /* Remove credentials */
691 discard_credentials(context);
693 /* Destroy login request */
694 xmlFreeNode(request);
696 if (soap_err) {
697 xmlFreeNodeList(response);
698 close_connection(context);
699 return soap_err;
702 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
703 * authentication succeeded if soap_err == IE_SUCCESS */
704 err = IE_SUCCESS;
706 xmlFreeNodeList(response);
708 if (!err)
709 isds_log(ILF_ISDS, ILL_DEBUG,
710 _("User %s has been logged into server %s successfully\n"),
711 username, url);
712 return err;
716 /* Log out from ISDS server discards credentials and connection configuration. */
717 isds_error isds_logout(struct isds_ctx *context) {
718 if (!context) return IE_INVALID_CONTEXT;
720 /* Close connection */
721 if (context->curl) {
722 close_connection(context);
724 /* Discard credentials for sure. They should not survive isds_login(),
725 * even successful .*/
726 discard_credentials(context);
727 free(context->url);
728 context->url = NULL;
730 isds_log(ILF_ISDS, ILL_DEBUG, _("Logged out from ISDS server\n"));
731 } else {
732 discard_credentials(context);
734 return IE_SUCCESS;
738 /* Verify connection to ISDS is alive and server is responding.
739 * Sent dumy request to ISDS and expect dummy response. */
740 isds_error isds_ping(struct isds_ctx *context) {
741 isds_error soap_err;
742 xmlNsPtr isds_ns = NULL;
743 xmlNodePtr request = NULL;
744 xmlNodePtr response = NULL;
746 if (!context) return IE_INVALID_CONTEXT;
748 /* Check if connection is established */
749 if (!context->curl) return IE_CONNECTION_CLOSED;
752 /* Build dummy request */
753 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
754 if (!request) {
755 isds_log_message(context, _("Could build ISDS dummy request"));
756 return IE_ERROR;
758 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
759 if(!isds_ns) {
760 isds_log_message(context, _("Could not create ISDS name space"));
761 xmlFreeNode(request);
762 return IE_ERROR;
764 xmlSetNs(request, isds_ns);
766 isds_log(ILF_ISDS, ILL_DEBUG, _("Pinging ISDS server\n"));
768 /* Sent dummy request */
769 soap_err = soap(context, "dz", request, &response, NULL, NULL);
771 /* Destroy login request */
772 xmlFreeNode(request);
774 if (soap_err) {
775 isds_log(ILF_ISDS, ILL_DEBUG,
776 _("ISDS server could not be contacted\n"));
777 xmlFreeNodeList(response);
778 close_connection(context);
779 return soap_err;
782 /* XXX: Untill we don't propagate HTTP code 500 or 4xx, we can be sure
783 * authentication succeeded if soap_err == IE_SUCCESS */
784 /* TODO: ISDS documentation does not specify response body.
785 * However real server sends back DummyOperationResponse */
788 xmlFreeNodeList(response);
790 isds_log(ILF_ISDS, ILL_DEBUG, _("ISDS server alive\n"));
792 return IE_SUCCESS;
796 /* Send bogus request to ISDS.
797 * Just for test purposes */
798 isds_error isds_bogus_request(struct isds_ctx *context) {
799 isds_error err;
800 xmlNsPtr isds_ns = NULL;
801 xmlNodePtr request = NULL;
802 xmlDocPtr response = NULL;
803 xmlChar *code = NULL, *message = NULL;
805 if (!context) return IE_INVALID_CONTEXT;
807 /* Check if connection is established */
808 if (!context->curl) {
809 /* Testing printf message */
810 isds_printf_message(context, "%s", _("I said connection closed"));
811 return IE_CONNECTION_CLOSED;
815 /* Build dummy request */
816 request = xmlNewNode(NULL, BAD_CAST "X-BogusOperation");
817 if (!request) {
818 isds_log_message(context, _("Could build ISDS bogus request"));
819 return IE_ERROR;
821 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
822 if(!isds_ns) {
823 isds_log_message(context, _("Could not create ISDS name space"));
824 xmlFreeNode(request);
825 return IE_ERROR;
827 xmlSetNs(request, isds_ns);
829 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending bogus request to ISDS\n"));
831 /* Sent bogus request */
832 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
834 /* Destroy request */
835 xmlFreeNode(request);
837 if (err) {
838 isds_log(ILF_ISDS, ILL_DEBUG,
839 _("Processing ISDS response on bogus request failed\n"));
840 xmlFreeDoc(response);
841 return err;
844 /* Check for response status */
845 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
846 &code, &message, NULL);
847 if (err) {
848 isds_log(ILF_ISDS, ILL_DEBUG,
849 _("ISDS response on bogus request is missing status\n"));
850 free(code);
851 free(message);
852 xmlFreeDoc(response);
853 return err;
855 if (xmlStrcmp(code, BAD_CAST "0000")) {
856 char *code_locale = utf82locale((char*)code);
857 char *message_locale = utf82locale((char*)message);
858 isds_log(ILF_ISDS, ILL_DEBUG,
859 _("Server refused bogus request (code=%s, message=%s)\n"),
860 code_locale, message_locale);
861 /* XXX: Literal error messages from ISDS are Czech mesages
862 * (English sometimes) in UTF-8. It's hard to catch them for
863 * translation. Successfully gettextized would return in locale
864 * encoding, unsuccessfully translated would pass in UTF-8. */
865 isds_log_message(context, message_locale);
866 free(code_locale);
867 free(message_locale);
868 free(code);
869 free(message);
870 xmlFreeDoc(response);
871 return IE_ISDS;
875 free(code);
876 free(message);
877 xmlFreeDoc(response);
879 isds_log(ILF_ISDS, ILL_DEBUG,
880 _("Bogus message accepted by server. This should not happen.\n"));
882 return IE_SUCCESS;
886 /* Serialize XML subtree to buffer preserving XML indentatition.
887 * @context is session context
888 * @subtree is XML element to be serialized (with childern)
889 * @buffer is automatically reallocated buffer where serialize to
890 * @length is size of serialized stream in bytes
891 * @return standard error code, free @buffer in case of error */
892 static isds_error serialize_subtree(struct isds_ctx *context,
893 xmlNodePtr subtree, void **buffer, size_t *length) {
894 isds_error err = IE_SUCCESS;
895 xmlBufferPtr xml_buffer = NULL;
896 xmlSaveCtxtPtr save_ctx = NULL;
897 xmlDocPtr subtree_doc = NULL;
898 xmlNodePtr subtree_copy;
899 xmlNsPtr isds_ns;
900 void *new_buffer;
902 if (!context) return IE_INVALID_CONTEXT;
903 if (!buffer) return IE_INVAL;
904 zfree(*buffer);
905 if (!subtree || !length) return IE_INVAL;
907 /* Make temporary XML document with @subtree root element */
908 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
909 * It can result in not well-formed on invalid XML tree (e.g. name space
910 * prefix definition can miss. */
911 /*FIXME */
913 subtree_doc = xmlNewDoc(BAD_CAST "1.0");
914 if (!subtree_doc) {
915 isds_log_message(context, _("Could not build temporary document"));
916 err = IE_ERROR;
917 goto leave;
920 /* XXX: Copy subtree and attach the copy to document.
921 * One node can not bee attached into more document at the same time.
922 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
923 * automatically.
924 * XXX: Check xmlSaveTree() too. */
925 subtree_copy = xmlCopyNodeList(subtree);
926 if (!subtree_copy) {
927 isds_log_message(context, _("Could not copy subtree"));
928 err = IE_ERROR;
929 goto leave;
931 xmlDocSetRootElement(subtree_doc, subtree_copy);
933 /* Only this way we get namespace definition as @xmlns:isds,
934 * otherwise we get namespace prefix without definition */
935 /* FIXME: Don't overwrite original default namespace */
936 isds_ns = xmlNewNs(subtree_copy, BAD_CAST ISDS_NS, NULL);
937 if(!isds_ns) {
938 isds_log_message(context, _("Could not create ISDS name space"));
939 err = IE_ERROR;
940 goto leave;
942 xmlSetNs(subtree_copy, isds_ns);
945 /* Serialize the document into buffer */
946 xml_buffer = xmlBufferCreate();
947 if (!xml_buffer) {
948 isds_log_message(context, _("Could not create xmlBuffer"));
949 err = IE_ERROR;
950 goto leave;
952 /* Last argument 0 means to not format the XML tree */
953 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8", 0);
954 if (!save_ctx) {
955 isds_log_message(context, _("Could not create XML serializer"));
956 err = IE_ERROR;
957 goto leave;
959 /* XXX: According LibXML documentation, this function does not return
960 * meaningfull value yet */
961 xmlSaveDoc(save_ctx, subtree_doc);
962 if (-1 == xmlSaveFlush(save_ctx)) {
963 isds_log_message(context,
964 _("Could not serialize XML subtree"));
965 err = IE_ERROR;
966 goto leave;
968 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
969 * even after xmlSaveFlush(). Thus close it here */
970 xmlSaveClose(save_ctx); save_ctx = NULL;
973 /* Store and detach buffer from xml_buffer */
974 *buffer = xml_buffer->content;
975 *length = xml_buffer->use;
976 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
978 /* Shrink buffer */
979 new_buffer = realloc(*buffer, *length);
980 if (new_buffer) *buffer = new_buffer;
982 leave:
983 if (err) {
984 zfree(*buffer);
985 *length = 0;
988 xmlSaveClose(save_ctx);
989 xmlBufferFree(xml_buffer);
990 xmlFreeDoc(subtree_doc); /* Frees subtree_copy, isds_ns etc. */
991 return err;
994 #if 0
995 /* Dump XML subtree to buffer as literral string, not valid XML possibly.
996 * @context is session context
997 * @document is original document where @nodeset points to
998 * @nodeset is XPath node set to dump (recursively)
999 * @buffer is automarically reallocated buffer where serialize to
1000 * @length is size of serialized stream in bytes
1001 * @return standard error code, free @buffer in case of error */
1002 static isds_error dump_nodeset(struct isds_ctx *context,
1003 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1004 void **buffer, size_t *length) {
1005 isds_error err = IE_SUCCESS;
1006 xmlBufferPtr xml_buffer = NULL;
1007 void *new_buffer;
1009 if (!context) return IE_INVALID_CONTEXT;
1010 if (!buffer) return IE_INVAL;
1011 zfree(*buffer);
1012 if (!document || !nodeset || !length) return IE_INVAL;
1013 *length = 0;
1015 /* Empty node set results into NULL buffer */
1016 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1017 goto leave;
1020 /* Resuling the document into buffer */
1021 xml_buffer = xmlBufferCreate();
1022 if (!xml_buffer) {
1023 isds_log_message(context, _("Could not create xmlBuffer"));
1024 err = IE_ERROR;
1025 goto leave;
1028 /* Itearate over all nodes */
1029 for (int i = 0; i < nodeset->nodeNr; i++) {
1030 /* Serialize node.
1031 * XXX: xmlNodeDump() appends to xml_buffer. */
1032 if (-1 ==
1033 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1034 isds_log_message(context, _("Could not dump XML node"));
1035 err = IE_ERROR;
1036 goto leave;
1040 /* Store and detach buffer from xml_buffer */
1041 *buffer = xml_buffer->content;
1042 *length = xml_buffer->use;
1043 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1045 /* Shrink buffer */
1046 new_buffer = realloc(*buffer, *length);
1047 if (new_buffer) *buffer = new_buffer;
1050 leave:
1051 if (err) {
1052 zfree(*buffer);
1053 *length = 0;
1056 xmlBufferFree(xml_buffer);
1057 return err;
1059 #endif
1061 #if 0
1062 /* Dump XML subtree to buffer as literral string, not valid XML possibly.
1063 * @context is session context
1064 * @document is original document where @nodeset points to
1065 * @nodeset is XPath node set to dump (recursively)
1066 * @buffer is automarically reallocated buffer where serialize to
1067 * @length is size of serialized stream in bytes
1068 * @return standard error code, free @buffer in case of error */
1069 static isds_error dump_nodeset(struct isds_ctx *context,
1070 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1071 void **buffer, size_t *length) {
1072 isds_error err = IE_SUCCESS;
1073 xmlBufferPtr xml_buffer = NULL;
1074 xmlSaveCtxtPtr save_ctx = NULL;
1075 void *new_buffer;
1077 if (!context) return IE_INVALID_CONTEXT;
1078 if (!buffer) return IE_INVAL;
1079 zfree(*buffer);
1080 if (!document || !nodeset || !length) return IE_INVAL;
1081 *length = 0;
1083 /* Empty node set results into NULL buffer */
1084 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1085 goto leave;
1088 /* Resuling the document into buffer */
1089 xml_buffer = xmlBufferCreate();
1090 if (!xml_buffer) {
1091 isds_log_message(context, _("Could not create xmlBuffer"));
1092 err = IE_ERROR;
1093 goto leave;
1095 if (xmlSubstituteEntitiesDefault(1)) {
1096 isds_log_message(context, _("Could not disable attribute escaping"));
1097 err = IE_ERROR;
1098 goto leave;
1100 /* Last argument means:
1101 * 0 to not format the XML tree
1102 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1103 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8",
1104 XML_SAVE_NO_DECL|XML_SAVE_NO_EMPTY|XML_SAVE_NO_XHTML);
1105 if (!save_ctx) {
1106 isds_log_message(context, _("Could not create XML serializer"));
1107 err = IE_ERROR;
1108 goto leave;
1110 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1111 isds_log_message(context, _("Could not disable attribute escaping"));
1112 err = IE_ERROR;
1113 goto leave;
1117 /* Itearate over all nodes */
1118 for (int i = 0; i < nodeset->nodeNr; i++) {
1119 /* Serialize node.
1120 * XXX: xmlNodeDump() appends to xml_buffer. */
1121 /*if (-1 ==
1122 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1124 /* XXX: According LibXML documentation, this function does not return
1125 * meaningfull value yet */
1126 xmlSaveTree(save_ctx, nodeset->nodeTab[i]);
1127 if (-1 == xmlSaveFlush(save_ctx)) {
1128 isds_log_message(context,
1129 _("Could not serialize XML subtree"));
1130 err = IE_ERROR;
1131 goto leave;
1135 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1136 * even after xmlSaveFlush(). Thus close it here */
1137 xmlSaveClose(save_ctx); save_ctx = NULL;
1139 /* Store and detach buffer from xml_buffer */
1140 *buffer = xml_buffer->content;
1141 *length = xml_buffer->use;
1142 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1144 /* Shrink buffer */
1145 new_buffer = realloc(*buffer, *length);
1146 if (new_buffer) *buffer = new_buffer;
1148 leave:
1149 if (err) {
1150 zfree(*buffer);
1151 *length = 0;
1154 xmlSaveClose(save_ctx);
1155 xmlBufferFree(xml_buffer);
1156 return err;
1158 #endif
1161 /* Convert UTF-8 @string represantion of ISDS dbType to enum @type */
1162 static isds_error string2isds_DbType(xmlChar *string, isds_DbType *type) {
1163 if (!string || !type) return IE_INVAL;
1165 if (!xmlStrcmp(string, BAD_CAST "FO"))
1166 *type = DBTYPE_FO;
1167 else if (!xmlStrcmp(string, BAD_CAST "PFO"))
1168 *type = DBTYPE_PFO;
1169 else if (!xmlStrcmp(string, BAD_CAST "PFO_ADVOK"))
1170 *type = DBTYPE_PFO_ADVOK;
1171 else if (!xmlStrcmp(string, BAD_CAST "PFO_DANPOR"))
1172 *type = DBTYPE_PFO_DANPOR;
1173 else if (!xmlStrcmp(string, BAD_CAST "PFO_INSSPR"))
1174 *type = DBTYPE_PFO_INSSPR;
1175 else if (!xmlStrcmp(string, BAD_CAST "PO"))
1176 *type = DBTYPE_PO;
1177 else if (!xmlStrcmp(string, BAD_CAST "PO_ZAK"))
1178 *type = DBTYPE_PO_ZAK;
1179 else if (!xmlStrcmp(string, BAD_CAST "PO_REQ"))
1180 *type = DBTYPE_PO_REQ;
1181 else if (!xmlStrcmp(string, BAD_CAST "OVM"))
1182 *type = DBTYPE_OVM;
1183 else if (!xmlStrcmp(string, BAD_CAST "OVM_NOTAR"))
1184 *type = DBTYPE_OVM_NOTAR;
1185 else if (!xmlStrcmp(string, BAD_CAST "OVM_EXEKUT"))
1186 *type = DBTYPE_OVM_EXEKUT;
1187 else if (!xmlStrcmp(string, BAD_CAST "OVM_REQ"))
1188 *type = DBTYPE_OVM_REQ;
1189 else
1190 return IE_ENUM;
1191 return IE_SUCCESS;
1195 /* Convert ISDS dbType enum @type to UTF-8 string.
1196 * @Return pointer to static string, or NULL if unkwnow enum value */
1197 static const xmlChar *isds_DbType2string(const isds_DbType type) {
1198 switch(type) {
1199 case DBTYPE_FO: return(BAD_CAST "FO"); break;
1200 case DBTYPE_PFO: return(BAD_CAST "PFO"); break;
1201 case DBTYPE_PFO_ADVOK: return(BAD_CAST "PFO_ADVOK"); break;
1202 case DBTYPE_PFO_DANPOR: return(BAD_CAST "PFO_DANPOR"); break;
1203 case DBTYPE_PFO_INSSPR: return(BAD_CAST "PFO_INSSPR"); break;
1204 case DBTYPE_PO: return(BAD_CAST "PO"); break;
1205 case DBTYPE_PO_ZAK: return(BAD_CAST "PO_ZAK"); break;
1206 case DBTYPE_PO_REQ: return(BAD_CAST "PO_REQ"); break;
1207 case DBTYPE_OVM: return(BAD_CAST "OVM"); break;
1208 case DBTYPE_OVM_NOTAR: return(BAD_CAST "OVM_NOTAR"); break;
1209 case DBTYPE_OVM_EXEKUT: return(BAD_CAST "OVM_EXEKUT"); break;
1210 case DBTYPE_OVM_REQ: return(BAD_CAST "OVM_REQ"); break;
1211 default: return NULL; break;
1216 /* Convert UTF-8 @string represantion of ISDS userType to enum @type */
1217 static isds_error string2isds_UserType(xmlChar *string, isds_UserType *type) {
1218 if (!string || !type) return IE_INVAL;
1220 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
1221 *type = USERTYPE_PRIMARY;
1222 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
1223 *type = USERTYPE_ENTRUSTED;
1224 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
1225 *type = USERTYPE_ADMINISTRATOR;
1226 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
1227 *type = USERTYPE_OFFICIAL;
1228 else
1229 return IE_ENUM;
1230 return IE_SUCCESS;
1233 #if 0 /* Not yet used */
1234 /* Convert ISDS userType enum @type to UTF-8 string.
1235 * @Return pointer to static string, or NULL if unkwnow enum value */
1236 static const xmlChar *isds_UserType2string(const isds_UserType type) {
1237 switch(type) {
1238 case USERTYPE_PRIMARY: return(BAD_CAST "PRIMARY_USER"); break;
1239 case USERTYPE_ENTRUSTED: return(BAD_CAST "ENTRUSTED_USER"); break;
1240 case USERTYPE_ADMINISTRATOR: return(BAD_CAST "ADMINISTRATOR"); break;
1241 case USERTYPE_OFFICIAL: return(BAD_CAST "OFFICIAL"); break;
1242 default: return NULL; break;
1245 #endif
1248 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
1249 * @Return pointer to static string, or NULL if unkwnow enum value */
1250 static const xmlChar *isds_FileMetaType2string(const isds_FileMetaType type) {
1251 switch(type) {
1252 case FILEMETATYPE_MAIN: return(BAD_CAST "main"); break;
1253 case FILEMETATYPE_ENCLOSURE: return(BAD_CAST "enclosure"); break;
1254 case FILEMETATYPE_SIGNATURE: return(BAD_CAST "signature"); break;
1255 case FILEMETATYPE_META: return(BAD_CAST "meta"); break;
1256 default: return NULL; break;
1261 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
1262 * @Return IE_ENUM if @string is not valid enum member */
1263 static isds_error string2isds_FileMetaType(const xmlChar *string,
1264 isds_FileMetaType *type) {
1265 if (!string || !type) return IE_INVAL;
1267 if (!xmlStrcmp(string, BAD_CAST "main"))
1268 *type = FILEMETATYPE_MAIN;
1269 else if (!xmlStrcmp(string, BAD_CAST "enclosure"))
1270 *type = FILEMETATYPE_ENCLOSURE;
1271 else if (!xmlStrcmp(string, BAD_CAST "signature"))
1272 *type = FILEMETATYPE_SIGNATURE;
1273 else if (!xmlStrcmp(string, BAD_CAST "meta"))
1274 *type = FILEMETATYPE_META;
1275 else
1276 return IE_ENUM;
1277 return IE_SUCCESS;
1281 /* Convert UTF-8 @string to ISDS hash @algorithm.
1282 * @Return IE_ENUM if @string is not valid enum member */
1283 static isds_error string2isds_hash_algorithm(const xmlChar *string,
1284 isds_hash_algorithm *algorithm) {
1285 if (!string || !algorithm) return IE_INVAL;
1287 if (!xmlStrcmp(string, BAD_CAST "MD5"))
1288 *algorithm = HASH_ALGORITHM_MD5;
1289 else if (!xmlStrcmp(string, BAD_CAST "SHA-1"))
1290 *algorithm = HASH_ALGORITHM_SHA_1;
1291 else if (!xmlStrcmp(string, BAD_CAST "SHA-256"))
1292 *algorithm = HASH_ALGORITHM_SHA_256;
1293 else if (!xmlStrcmp(string, BAD_CAST "SHA-512"))
1294 *algorithm = HASH_ALGORITHM_SHA_512;
1295 else
1296 return IE_ENUM;
1297 return IE_SUCCESS;
1301 /* Convert UTF-8 @string represantion of ISO 8601 date to @time.
1302 * XXX: Not all ISO formats are supported */
1303 static isds_error datestring2tm(const xmlChar *string, struct tm *time) {
1304 char *offset;
1305 if (!string || !time) return IE_INVAL;
1307 /* xsd:date is ISO 8601 string, thus ASCII */
1308 offset = strptime((char*)string, "%Y-%m-%d", time);
1309 if (offset && *offset == '\0')
1310 return IE_SUCCESS;
1312 offset = strptime((char*)string, "%Y%m%d", time);
1313 if (offset && *offset == '\0')
1314 return IE_SUCCESS;
1316 offset = strptime((char*)string, "%Y-%j", time);
1317 if (offset && *offset == '\0')
1318 return IE_SUCCESS;
1320 return IE_NOTSUP;
1324 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
1325 static isds_error tm2datestring(const struct tm *time, xmlChar **string) {
1326 if (!time || !string) return IE_INVAL;
1328 if (-1 == isds_asprintf((char **) string, "%d-%02d-%02d",
1329 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
1330 return IE_ERROR;
1332 return IE_SUCCESS;
1336 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
1337 * respects the @time microseconds too. */
1338 static isds_error timeval2timestring(const struct timeval *time,
1339 xmlChar **string) {
1340 struct tm broken;
1342 if (!time || !string) return IE_INVAL;
1344 if (!gmtime_r(&time->tv_sec, &broken)) return IE_DATE;
1345 if (time->tv_usec < 0 || time->tv_usec > 999999) return IE_DATE;
1347 /* TODO: small negative year should be formated as "-0012". This is not
1348 * true for glibc "%04d". We should implement it.
1349 * TODO: What's type of time->tv_usec exactly? Unsigned? Absolute?
1350 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
1351 if (-1 == isds_asprintf((char **) string,
1352 "%04d-%02d-%02dT%02d:%02d:%02d.%06ld",
1353 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
1354 broken.tm_hour, broken.tm_min, broken.tm_sec,
1355 time->tv_usec))
1356 return IE_ERROR;
1358 return IE_SUCCESS;
1362 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
1363 * It respects microseconds too.
1364 * In case of error, @time will be freed. */
1365 static isds_error timestring2timeval(const xmlChar *string,
1366 struct timeval **time) {
1367 struct tm broken;
1368 char *offset, *delim, *endptr;
1369 char subseconds[7];
1370 int offset_hours, offset_minutes;
1371 int i;
1373 if (!time) return IE_INVAL;
1375 memset(&broken, 0, sizeof(broken));
1377 if (!*time) {
1378 *time = calloc(1, sizeof(**time));
1379 if (!*time) return IE_NOMEM;
1380 } else {
1381 memset(*time, 0, sizeof(**time));
1385 /* xsd:date is ISO 8601 string, thus ASCII */
1386 /*TODO: negative year */
1388 /* Parse date and time without subseconds and offset */
1389 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
1390 if (!offset) {
1391 free(*time); *time = NULL;
1392 return IE_DATE;
1395 /* Get subseconds */
1396 if (*offset == '.' ) {
1397 offset++;
1399 /* Copy first 6 digits, padd it with zeros.
1400 * XXX: It truncates longer number, no round.
1401 * Current server implementation uses only milisecond resolution. */
1402 /* TODO: isdigit() is locale sensitive */
1403 for (i = 0;
1404 i < sizeof(subseconds)/sizeof(char) - 1 && isdigit(*offset);
1405 i++, offset++) {
1406 subseconds[i] = *offset;
1408 for (; i < sizeof(subseconds)/sizeof(char) - 1; i++) {
1409 subseconds[i] = '0';
1411 subseconds[6] = '\0';
1413 /* Convert it into integer */
1414 (*time)->tv_usec = strtol(subseconds, &endptr, 10);
1415 if (*endptr != '\0' || (*time)->tv_usec == LONG_MIN ||
1416 (*time)->tv_usec == LONG_MAX) {
1417 free(*time); *time = NULL;
1418 return IE_DATE;
1421 /* move to the zone offset delimiter */
1422 delim = strchr(offset, '-');
1423 if (!delim)
1424 delim = strchr(offset, '+');
1425 offset = delim;
1428 /* Get zone offset */
1429 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
1430 * "" equals to "Z" and it means UTC zone. */
1431 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
1432 * colon separator */
1433 if (*offset == '-' || *offset == '+') {
1434 offset++;
1435 if (2 != sscanf(offset, "%2d:%2d", &offset_hours, &offset_minutes)) {
1436 free(*time); *time = NULL;
1437 return IE_DATE;
1439 broken.tm_hour -= offset_hours;
1440 broken.tm_min -= offset_minutes * ((offset_hours<0) ? -1 : 1);
1443 /* Convert to time_t */
1444 switch_tz_to_utc();
1445 (*time)->tv_sec = mktime(&broken);
1446 switch_tz_to_native();
1447 if ((*time)->tv_sec == (time_t) -1) {
1448 free(*time); *time = NULL;
1449 return IE_DATE;
1452 return IE_SUCCESS;
1456 /* Convert unsigned int into isds_message_status.
1457 * @context is session context
1458 * @number is pointer to number value. NULL will be treated as invalid value.
1459 * @status is automatically reallocated status
1460 * @return IE_SUCCESS, or error code and free status */
1461 static isds_error uint2isds_message_status(struct isds_ctx *context,
1462 const unsigned long int *number, isds_message_status **status) {
1463 if (!context) return IE_INVALID_CONTEXT;
1464 if (!status) return IE_INVAL;
1466 free(*status); *status = NULL;
1467 if (!number) return IE_INVAL;
1469 if (*number < 1 || *number > 10) {
1470 isds_printf_message(context, _("Invalid messsage status value: %lu"),
1471 *number);
1472 return IE_ENUM;
1475 *status = malloc(sizeof(**status));
1476 if (!*status) return IE_NOMEM;
1478 **status = 1 << *number;
1479 return IE_SUCCESS;
1483 /* Convert event description string into isds_event memebers type and
1484 * description
1485 * @string is raw event decsription starting with event prefix
1486 * @event is structure where to store type and stripped description to
1487 * @return standard error code, unkown prefix is not classified as an error. */
1488 static isds_error eventstring2event(const xmlChar *string,
1489 struct isds_event* event) {
1490 const xmlChar *known_prefixes[] = {
1491 BAD_CAST "EV1:",
1492 BAD_CAST "EV2:",
1493 BAD_CAST "EV3:",
1494 BAD_CAST "EV4:"
1496 const isds_event_type types[] = {
1497 EVENT_ACCEPTED_BY_RECIPIENT,
1498 EVENT_ACCEPTED_BY_FICTION,
1499 EVENT_UNDELIVERABLE,
1500 EVENT_COMMERCIAL_ACCEPTED
1502 unsigned int index;
1503 size_t length;
1505 if (!string || !event) return IE_INVAL;
1507 if (!event->type) {
1508 event->type = malloc(sizeof(*event->type));
1509 if (!(event->type)) return IE_NOMEM;
1511 zfree(event->description);
1513 for (index = 0; index < sizeof(known_prefixes)/sizeof(known_prefixes[0]);
1514 index++) {
1515 length = xmlUTF8Strlen(known_prefixes[index]);
1517 if (!xmlStrncmp(string, known_prefixes[index], length)) {
1518 /* Prefix is known */
1519 *event->type = types[index];
1521 /* Strip prefix from description and spaces */
1522 /* TODO: Recognize all wite spaces from UCS blank class and
1523 * operate on UTF-8 chars. */
1524 for (; string[length] != '\0' && string[length] == ' '; length++);
1525 event->description = strdup((char *) (string + length));
1526 if (!(event->description)) return IE_NOMEM;
1528 return IE_SUCCESS;
1532 /* Unknown event prefix.
1533 * XSD allows any string */
1534 char *string_locale = utf82locale((char *) string);
1535 isds_log(ILF_ISDS, ILL_WARNING,
1536 _("Uknown delivery info event prefix: %s\n"), string_locale);
1537 free(string_locale);
1539 *event->type = EVENT_UKNOWN;
1540 event->description = strdup((char *) string);
1541 if (!(event->description)) return IE_NOMEM;
1543 return IE_SUCCESS;
1547 /* Following EXTRACT_* macros expects @result, @xpath_ctx, @err, @context
1548 * and leave lable */
1549 #define EXTRACT_STRING(element, string) \
1550 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
1551 if (!result) { \
1552 err = IE_ERROR; \
1553 goto leave; \
1555 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
1556 if (result->nodesetval->nodeNr > 1) { \
1557 isds_printf_message(context, _("Multiple %s element"), element); \
1558 err = IE_ERROR; \
1559 goto leave; \
1561 (string) = (char *) \
1562 xmlXPathCastNodeSetToString(result->nodesetval); \
1563 if (!(string)) { \
1564 err = IE_ERROR; \
1565 goto leave; \
1569 #define EXTRACT_BOOLEAN(element, booleanPtr) \
1571 char *string = NULL; \
1572 EXTRACT_STRING(element, string); \
1574 if (string) { \
1575 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
1576 if (!(booleanPtr)) { \
1577 free(string); \
1578 err = IE_NOMEM; \
1579 goto leave; \
1582 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
1583 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
1584 *(booleanPtr) = 1; \
1585 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
1586 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
1587 *(booleanPtr) = 0; \
1588 else { \
1589 char *string_locale = utf82locale((char*)string); \
1590 isds_printf_message(context, \
1591 _("%s value is not valid boolean: "), \
1592 element, string_locale); \
1593 free(string_locale); \
1594 free(string); \
1595 err = IE_ERROR; \
1596 goto leave; \
1599 free(string); \
1603 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
1605 char *string = NULL; \
1606 EXTRACT_STRING(element, string); \
1607 if (string) { \
1608 long int number; \
1609 char *endptr; \
1611 number = strtol((char*)string, &endptr, 10); \
1613 if (*endptr != '\0') { \
1614 char *string_locale = utf82locale((char *)string); \
1615 isds_printf_message(context, \
1616 _("%s is not valid integer: %s"), \
1617 element, string_locale); \
1618 free(string_locale); \
1619 free(string); \
1620 err = IE_ISDS; \
1621 goto leave; \
1624 if (number == LONG_MIN || number == LONG_MAX) { \
1625 char *string_locale = utf82locale((char *)string); \
1626 isds_printf_message(context, \
1627 _("%s value out of range of long int: %s"), \
1628 element, string_locale); \
1629 free(string_locale); \
1630 free(string); \
1631 err = IE_ERROR; \
1632 goto leave; \
1635 free(string); string = NULL; \
1637 if (!(preallocated)) { \
1638 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
1639 if (!(longintPtr)) { \
1640 err = IE_NOMEM; \
1641 goto leave; \
1644 *(longintPtr) = number; \
1648 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
1650 char *string = NULL; \
1651 EXTRACT_STRING(element, string); \
1652 if (string) { \
1653 long int number; \
1654 char *endptr; \
1656 number = strtol((char*)string, &endptr, 10); \
1658 if (*endptr != '\0') { \
1659 char *string_locale = utf82locale((char *)string); \
1660 isds_printf_message(context, \
1661 _("%s is not valid integer: %s"), \
1662 element, string_locale); \
1663 free(string_locale); \
1664 free(string); \
1665 err = IE_ISDS; \
1666 goto leave; \
1669 if (number == LONG_MIN || number == LONG_MAX) { \
1670 char *string_locale = utf82locale((char *)string); \
1671 isds_printf_message(context, \
1672 _("%s value out of range of long int: %s"), \
1673 element, string_locale); \
1674 free(string_locale); \
1675 free(string); \
1676 err = IE_ERROR; \
1677 goto leave; \
1680 free(string); string = NULL; \
1681 if (number < 0) { \
1682 isds_printf_message(context, \
1683 _("%s value is negative: %ld"), element, number); \
1684 err = IE_ERROR; \
1685 goto leave; \
1688 if (!(preallocated)) { \
1689 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
1690 if (!(ulongintPtr)) { \
1691 err = IE_NOMEM; \
1692 goto leave; \
1695 *(ulongintPtr) = number; \
1699 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) \
1700 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
1701 NULL); \
1702 if ((required) && (!string)) { \
1703 char *attribute_locale = utf82locale(attribute); \
1704 char *element_locale = utf82locale((char *)xpath_ctx->node->name); \
1705 isds_printf_message(context, \
1706 _("Could not extract required %s attribute value from " \
1707 "%s element"), attribute_locale, element_locale); \
1708 free(element_locale); \
1709 free(attribute_locale); \
1710 err = IE_ERROR; \
1711 goto leave; \
1715 #define INSERT_STRING(parent, element, string) \
1716 node = xmlNewTextChild(parent, NULL, BAD_CAST (element), \
1717 (xmlChar *) (string)); \
1718 if (!node) { \
1719 isds_printf_message(context, \
1720 _("Could not add %s child to %s element"), \
1721 element, (parent)->name); \
1722 err = IE_ERROR; \
1723 goto leave; \
1726 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
1727 if ((booleanPtr)) { \
1728 if (*(booleanPtr)) { INSERT_STRING(parent, element, "true"); } \
1729 else { INSERT_STRING(parent, element, "false") } \
1730 } else { INSERT_STRING(parent, element, NULL) }
1732 #define INSERT_LONGINT(parent, element, longintPtr, buffer) \
1733 if ((longintPtr)) { \
1734 /* FIXME: locale sensitive */ \
1735 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
1736 err = IE_NOMEM; \
1737 goto leave; \
1739 INSERT_STRING(parent, element, buffer) \
1740 free(buffer); (buffer) = NULL; \
1741 } else { INSERT_STRING(parent, element, NULL) }
1743 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) \
1744 if ((ulongintPtr)) { \
1745 /* FIXME: locale sensitive */ \
1746 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
1747 err = IE_NOMEM; \
1748 goto leave; \
1750 INSERT_STRING(parent, element, buffer) \
1751 free(buffer); (buffer) = NULL; \
1752 } else { INSERT_STRING(parent, element, NULL) }
1754 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
1755 /* FIXME: locale sensitive */ \
1756 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
1757 err = IE_NOMEM; \
1758 goto leave; \
1760 INSERT_STRING(parent, element, buffer) \
1761 free(buffer); (buffer) = NULL; \
1763 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
1764 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
1765 (xmlChar *) (string)); \
1766 if (!attribute_node) { \
1767 isds_printf_message(context, _("Could not add %s " \
1768 "attribute to %s element"), (attribute), (parent)->name); \
1769 err = IE_ERROR; \
1770 goto leave; \
1774 /* Find child element by name in given XPath context and switch context onto
1775 * it. The child must be uniq and must exist. Otherwise failes.
1776 * @context is ISDS context
1777 * @child is child element name
1778 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
1779 * into it child. In error case, the @xpath_ctx keeps original value. */
1780 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
1781 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
1782 isds_error err = IE_SUCCESS;
1783 xmlXPathObjectPtr result = NULL;
1785 if (!context) return IE_INVALID_CONTEXT;
1786 if (!child || !xpath_ctx) return IE_INVAL;
1788 /* Find child */
1789 result = xmlXPathEvalExpression(child, xpath_ctx);
1790 if (!result) {
1791 err = IE_XML;
1792 goto leave;
1795 /* No match */
1796 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
1797 char *parent_locale = utf82locale((char*) xpath_ctx->node->name);
1798 char *child_locale = utf82locale((char*) child);
1799 isds_printf_message(context,
1800 _("%s element does not contain %s child"),
1801 parent_locale, child_locale);
1802 free(child_locale);
1803 free(parent_locale);
1804 err = IE_NOEXIST;
1805 goto leave;
1808 /* More matches */
1809 if (result->nodesetval->nodeNr > 1) {
1810 char *parent_locale = utf82locale((char*) xpath_ctx->node->name);
1811 char *child_locale = utf82locale((char*) child);
1812 isds_printf_message(context,
1813 _("%s element contains multiple %s childs"),
1814 parent_locale, child_locale);
1815 free(child_locale);
1816 free(parent_locale);
1817 err = IE_NOTUNIQ;
1818 goto leave;
1821 /* Switch context */
1822 xpath_ctx->node = result->nodesetval->nodeTab[0];
1824 leave:
1825 xmlXPathFreeObject(result);
1826 return err;
1831 /* Find and convert XSD:gPersonName group in current node into structure
1832 * @context is ISDS context
1833 * @personName is automically reallocated person name structure. If no member
1834 * value is found, will be freed.
1835 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
1836 * elements
1837 * In case of error @personName will be freed. */
1838 static isds_error extract_gPersonName(struct isds_ctx *context,
1839 struct isds_PersonName **personName, xmlXPathContextPtr xpath_ctx) {
1840 isds_error err = IE_SUCCESS;
1841 xmlXPathObjectPtr result = NULL;
1843 if (!context) return IE_INVALID_CONTEXT;
1844 if (!personName) return IE_INVAL;
1845 isds_PersonName_free(personName);
1846 if (!xpath_ctx) return IE_INVAL;
1849 *personName = calloc(1, sizeof(**personName));
1850 if (!*personName) {
1851 err = IE_NOMEM;
1852 goto leave;
1855 EXTRACT_STRING("isds:pnFirstName", (*personName)->pnFirstName);
1856 EXTRACT_STRING("isds:pnMiddleName", (*personName)->pnMiddleName);
1857 EXTRACT_STRING("isds:pnLastName", (*personName)->pnLastName);
1858 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName)->pnLastNameAtBirth);
1860 if (!(*personName)->pnFirstName && !(*personName)->pnMiddleName &&
1861 !(*personName)->pnLastName && !(*personName)->pnLastNameAtBirth)
1862 isds_PersonName_free(personName);
1864 leave:
1865 if (err) isds_PersonName_free(personName);
1866 xmlXPathFreeObject(result);
1867 return err;
1871 /* Find and convert XSD:gAddress group in current node into structure
1872 * @context is ISDS context
1873 * @address is automically reallocated address structure. If no member
1874 * value is found, will be freed.
1875 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
1876 * elements
1877 * In case of error @address will be freed. */
1878 static isds_error extract_gAddress(struct isds_ctx *context,
1879 struct isds_Address **address, xmlXPathContextPtr xpath_ctx) {
1880 isds_error err = IE_SUCCESS;
1881 xmlXPathObjectPtr result = NULL;
1883 if (!context) return IE_INVALID_CONTEXT;
1884 if (!address) return IE_INVAL;
1885 isds_Address_free(address);
1886 if (!xpath_ctx) return IE_INVAL;
1889 *address = calloc(1, sizeof(**address));
1890 if (!*address) {
1891 err = IE_NOMEM;
1892 goto leave;
1895 EXTRACT_STRING("isds:adCity", (*address)->adCity);
1896 EXTRACT_STRING("isds:adStreet", (*address)->adStreet);
1897 EXTRACT_STRING("isds:adNumberInStreet", (*address)->adNumberInStreet);
1898 EXTRACT_STRING("isds:adNumberInMunicipality",
1899 (*address)->adNumberInMunicipality);
1900 EXTRACT_STRING("isds:adZipCode", (*address)->adZipCode);
1901 EXTRACT_STRING("isds:adState", (*address)->adState);
1903 if (!(*address)->adCity && !(*address)->adStreet &&
1904 !(*address)->adNumberInStreet &&
1905 !(*address)->adNumberInMunicipality &&
1906 !(*address)->adZipCode && !(*address)->adState)
1907 isds_Address_free(address);
1909 leave:
1910 if (err) isds_Address_free(address);
1911 xmlXPathFreeObject(result);
1912 return err;
1916 /* Find and convert isds:biDate element in current node into structure
1917 * @context is ISDS context
1918 * @biDate is automically reallocated birth date structure. If no member
1919 * value is found, will be freed.
1920 * @xpath_ctx is XPath context with current node as parent for isds:biDate
1921 * element
1922 * In case of error @biDate will be freed. */
1923 static isds_error extract_BiDate(struct isds_ctx *context,
1924 struct tm **biDate, xmlXPathContextPtr xpath_ctx) {
1925 isds_error err = IE_SUCCESS;
1926 xmlXPathObjectPtr result = NULL;
1927 char *string = NULL;
1929 if (!context) return IE_INVALID_CONTEXT;
1930 if (!biDate) return IE_INVAL;
1931 zfree(*biDate);
1932 if (!xpath_ctx) return IE_INVAL;
1934 EXTRACT_STRING("isds:biDate", string);
1935 if (string) {
1936 *biDate = calloc(1, sizeof(**biDate));
1937 if (!*biDate) {
1938 err = IE_NOMEM;
1939 goto leave;
1941 err = datestring2tm((xmlChar *)string, *biDate);
1942 if (err) {
1943 if (err == IE_NOTSUP) {
1944 err = IE_ISDS;
1945 char *string_locale = utf82locale(string);
1946 isds_printf_message(context,
1947 _("Invalid isds:biDate value: %s"), string_locale);
1948 free(string_locale);
1950 goto leave;
1954 leave:
1955 if (err) zfree(*biDate);
1956 free(string);
1957 xmlXPathFreeObject(result);
1958 return err;
1962 /* Convert isds:dBOwnerInfo XML tree into structure
1963 * @context is ISDS context
1964 * @db_owner_info is automically reallocated box owner info structure
1965 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
1966 * In case of error @db_owner_info will be freed. */
1967 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
1968 struct isds_DbOwnerInfo **db_owner_info,
1969 xmlXPathContextPtr xpath_ctx) {
1970 isds_error err = IE_SUCCESS;
1971 xmlXPathObjectPtr result = NULL;
1972 char *string = NULL;
1974 if (!context) return IE_INVALID_CONTEXT;
1975 if (!db_owner_info) return IE_INVAL;
1976 isds_DbOwnerInfo_free(db_owner_info);
1977 if (!xpath_ctx) return IE_INVAL;
1980 *db_owner_info = calloc(1, sizeof(**db_owner_info));
1981 if (!*db_owner_info) {
1982 err = IE_NOMEM;
1983 goto leave;
1986 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
1988 EXTRACT_STRING("isds:dbType", string);
1989 if (string) {
1990 (*db_owner_info)->dbType =
1991 calloc(1, sizeof(*((*db_owner_info)->dbType)));
1992 if (!(*db_owner_info)->dbType) {
1993 err = IE_NOMEM;
1994 goto leave;
1996 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
1997 if (err) {
1998 zfree((*db_owner_info)->dbType);
1999 if (err == IE_ENUM) {
2000 err = IE_ISDS;
2001 char *string_locale = utf82locale(string);
2002 isds_printf_message(context, _("Unknown isds:dbType: %s"),
2003 string_locale);
2004 free(string_locale);
2006 goto leave;
2008 zfree(string);
2011 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
2013 err = extract_gPersonName(context, &(*db_owner_info)->personName,
2014 xpath_ctx);
2015 if (err) goto leave;
2017 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
2019 (*db_owner_info)->birthInfo =
2020 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
2021 if (!(*db_owner_info)->birthInfo) {
2022 err = IE_NOMEM;
2023 goto leave;
2025 err = extract_BiDate(context, &(*db_owner_info)->birthInfo->biDate,
2026 xpath_ctx);
2027 if (err) goto leave;
2028 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
2029 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
2030 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
2031 if (!(*db_owner_info)->birthInfo->biDate &&
2032 !(*db_owner_info)->birthInfo->biCity &&
2033 !(*db_owner_info)->birthInfo->biCounty &&
2034 !(*db_owner_info)->birthInfo->biState)
2035 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
2037 err = extract_gAddress(context, &(*db_owner_info)->address, xpath_ctx);
2038 if (err) goto leave;
2040 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
2041 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
2042 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
2043 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
2044 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
2046 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
2048 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
2049 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
2050 (*db_owner_info)->dbOpenAddressing);
2052 leave:
2053 if (err) isds_DbOwnerInfo_free(db_owner_info);
2054 free(string);
2055 xmlXPathFreeObject(result);
2056 return err;
2060 /* Convert XSD:tDbUserInfo XML tree into structure
2061 * @context is ISDS context
2062 * @db_user_info is automically reallocated user info structure
2063 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
2064 * In case of error @db_user_info will be freed. */
2065 static isds_error extract_DbUserInfo(struct isds_ctx *context,
2066 struct isds_DbUserInfo **db_user_info, xmlXPathContextPtr xpath_ctx) {
2067 isds_error err = IE_SUCCESS;
2068 xmlXPathObjectPtr result = NULL;
2069 char *string = NULL;
2071 if (!context) return IE_INVALID_CONTEXT;
2072 if (!db_user_info) return IE_INVAL;
2073 isds_DbUserInfo_free(db_user_info);
2074 if (!xpath_ctx) return IE_INVAL;
2077 *db_user_info = calloc(1, sizeof(**db_user_info));
2078 if (!*db_user_info) {
2079 err = IE_NOMEM;
2080 goto leave;
2083 EXTRACT_STRING("isds:userID", (*db_user_info)->userID);
2085 EXTRACT_STRING("isds:userType", string);
2086 if (string) {
2087 (*db_user_info)->userType =
2088 calloc(1, sizeof(*((*db_user_info)->userType)));
2089 if (!(*db_user_info)->userType) {
2090 err = IE_NOMEM;
2091 goto leave;
2093 err = string2isds_UserType((xmlChar *)string,
2094 (*db_user_info)->userType);
2095 if (err) {
2096 zfree((*db_user_info)->userType);
2097 if (err == IE_ENUM) {
2098 err = IE_ISDS;
2099 char *string_locale = utf82locale(string);
2100 isds_printf_message(context, _("Unknown isds:userType: %s"),
2101 string_locale);
2102 free(string_locale);
2104 goto leave;
2106 zfree(string);
2109 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info)->userPrivils, 0);
2111 (*db_user_info)->personName =
2112 calloc(1, sizeof(*((*db_user_info)->personName)));
2113 if (!(*db_user_info)->personName) {
2114 err = IE_NOMEM;
2115 goto leave;
2118 err = extract_gPersonName(context, &(*db_user_info)->personName,
2119 xpath_ctx);
2120 if (err) goto leave;
2122 err = extract_gAddress(context, &(*db_user_info)->address, xpath_ctx);
2123 if (err) goto leave;
2125 err = extract_BiDate(context, &(*db_user_info)->biDate, xpath_ctx);
2126 if (err) goto leave;
2128 EXTRACT_STRING("isds:ic", (*db_user_info)->ic);
2129 EXTRACT_STRING("isds:firmName", (*db_user_info)->firmName);
2131 EXTRACT_STRING("isds:caStreet", (*db_user_info)->caStreet);
2132 EXTRACT_STRING("isds:caCity", (*db_user_info)->caCity);
2133 EXTRACT_STRING("isds:caZipCode", (*db_user_info)->caZipCode);
2135 leave:
2136 if (err) isds_DbUserInfo_free(db_user_info);
2137 free(string);
2138 xmlXPathFreeObject(result);
2139 return err;
2143 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
2144 * isds_envelope structure. The envelope is automatically allocated but not
2145 * reallocated. The date are just appended into envelope structure.
2146 * @context is ISDS context
2147 * @envelope is automically allocated message envelope structure
2148 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
2149 * In case of error @envelope will be freed. */
2150 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
2151 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2152 isds_error err = IE_SUCCESS;
2153 xmlXPathObjectPtr result = NULL;
2155 if (!context) return IE_INVALID_CONTEXT;
2156 if (!envelope) return IE_INVAL;
2157 if (!xpath_ctx) return IE_INVAL;
2160 if (!*envelope) {
2161 /* Allocate envelope */
2162 *envelope = calloc(1, sizeof(**envelope));
2163 if (!*envelope) {
2164 err = IE_NOMEM;
2165 goto leave;
2167 } else {
2168 /* Else free former data */
2169 zfree((*envelope)->dmSenderOrgUnit);
2170 zfree((*envelope)->dmSenderOrgUnitNum);
2171 zfree((*envelope)->dbIDRecipient);
2172 zfree((*envelope)->dmRecipientOrgUnit);
2173 zfree((*envelope)->dmSenderOrgUnitNum);
2174 zfree((*envelope)->dmToHands);
2175 zfree((*envelope)->dmAnnotation);
2176 zfree((*envelope)->dmRecipientRefNumber);
2177 zfree((*envelope)->dmSenderRefNumber);
2178 zfree((*envelope)->dmRecipientIdent);
2179 zfree((*envelope)->dmSenderIdent);
2180 zfree((*envelope)->dmLegalTitleLaw);
2181 zfree((*envelope)->dmLegalTitleYear);
2182 zfree((*envelope)->dmLegalTitleSect);
2183 zfree((*envelope)->dmLegalTitlePar);
2184 zfree((*envelope)->dmLegalTitlePoint);
2185 zfree((*envelope)->dmPersonalDelivery);
2186 zfree((*envelope)->dmAllowSubstDelivery);
2189 /* Extract envelope elements added by sender or ISDS
2190 * (XSD: gMessageEnvelopeSub type) */
2191 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
2192 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
2193 (*envelope)->dmSenderOrgUnitNum, 0);
2194 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
2195 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
2196 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
2197 (*envelope)->dmSenderOrgUnitNum, 0);
2198 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
2199 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
2200 EXTRACT_STRING("isds:dmRecipientRefNumber",
2201 (*envelope)->dmRecipientRefNumber);
2202 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
2203 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
2204 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
2206 /* Extract envelope elements regarding law refference */
2207 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
2208 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
2209 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
2210 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
2211 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
2213 /* Extract envelope other elements */
2214 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
2215 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
2216 (*envelope)->dmAllowSubstDelivery);
2218 leave:
2219 if (err) isds_envelope_free(envelope);
2220 xmlXPathFreeObject(result);
2221 return err;
2226 /* Convert XSD gMessageEnvelope group of elements from XML tree into
2227 * isds_envelope structure. The envelope is automatically allocated but not
2228 * reallocated. The date are just appended into envelope structure.
2229 * @context is ISDS context
2230 * @envelope is automically allocated message envelope structure
2231 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
2232 * In case of error @envelope will be freed. */
2233 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
2234 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2235 isds_error err = IE_SUCCESS;
2236 xmlXPathObjectPtr result = NULL;
2238 if (!context) return IE_INVALID_CONTEXT;
2239 if (!envelope) return IE_INVAL;
2240 if (!xpath_ctx) return IE_INVAL;
2243 if (!*envelope) {
2244 /* Allocate envelope */
2245 *envelope = calloc(1, sizeof(**envelope));
2246 if (!*envelope) {
2247 err = IE_NOMEM;
2248 goto leave;
2250 } else {
2251 /* Else free former data */
2252 zfree((*envelope)->dmID);
2253 zfree((*envelope)->dbIDSender);
2254 zfree((*envelope)->dmSender);
2255 zfree((*envelope)->dmSenderAddress);
2256 zfree((*envelope)->dmSenderType);
2257 zfree((*envelope)->dmRecipient);
2258 zfree((*envelope)->dmRecipientAddress);
2259 zfree((*envelope)->dmAmbiguousRecipient);
2262 /* Extract envelope elements added by ISDS
2263 * (XSD: gMessageEnvelope type) */
2264 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
2265 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
2266 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
2267 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
2268 /* XML Schema does not guaratee enumratation. It's plain xs:int. */
2269 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
2270 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
2271 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
2272 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
2273 (*envelope)->dmAmbiguousRecipient);
2275 /* Extract envelope elements added by sender and ISDS
2276 * (XSD: gMessageEnvelope type) */
2277 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
2278 if (err) goto leave;
2280 leave:
2281 if (err) isds_envelope_free(envelope);
2282 xmlXPathFreeObject(result);
2283 return err;
2287 /* Convert other envelope elements from XML tree into isds_envelope structure:
2288 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
2289 * The envelope is automatically allocated but not reallocated.
2290 * The data are just appended into envelope structure.
2291 * @context is ISDS context
2292 * @envelope is automically allocated message envelope structure
2293 * @xpath_ctx is XPath context with current node as parent desired elements
2294 * In case of error @envelope will be freed. */
2295 static isds_error append_status_size_times(struct isds_ctx *context,
2296 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2297 isds_error err = IE_SUCCESS;
2298 xmlXPathObjectPtr result = NULL;
2299 char *string = NULL;
2300 unsigned long int *unumber = NULL;
2302 if (!context) return IE_INVALID_CONTEXT;
2303 if (!envelope) return IE_INVAL;
2304 if (!xpath_ctx) return IE_INVAL;
2307 if (!*envelope) {
2308 /* Allocate new */
2309 *envelope = calloc(1, sizeof(**envelope));
2310 if (!*envelope) {
2311 err = IE_NOMEM;
2312 goto leave;
2314 } else {
2315 /* Free old data */
2316 zfree((*envelope)->dmMessageStatus);
2317 zfree((*envelope)->dmAttachmentSize);
2318 zfree((*envelope)->dmDeliveryTime);
2319 zfree((*envelope)->dmAcceptanceTime);
2323 /* dmMessageStatus element is mandatory */
2324 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
2325 if (!unumber) {
2326 isds_log_message(context,
2327 _("Missing mandatory sisds:dmMessageStatus integer"));
2328 err = IE_ISDS;
2329 goto leave;
2331 err = uint2isds_message_status(context, unumber,
2332 &((*envelope)->dmMessageStatus));
2333 if (err) {
2334 if (err == IE_ENUM) err = IE_ISDS;
2335 goto leave;
2337 free(unumber); unumber = NULL;
2339 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
2342 EXTRACT_STRING("sisds:dmDeliveryTime", string);
2343 if (string) {
2344 err = timestring2timeval((xmlChar *) string,
2345 &((*envelope)->dmDeliveryTime));
2346 if (err) {
2347 char *string_locale = utf82locale(string);
2348 if (err == IE_DATE) err = IE_ISDS;
2349 isds_printf_message(context,
2350 _("Could not convert dmDeliveryTime as ISO time: %s"),
2351 string_locale);
2352 free(string_locale);
2353 goto leave;
2355 zfree(string);
2358 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
2359 if (string) {
2360 err = timestring2timeval((xmlChar *) string,
2361 &((*envelope)->dmAcceptanceTime));
2362 if (err) {
2363 char *string_locale = utf82locale(string);
2364 if (err == IE_DATE) err = IE_ISDS;
2365 isds_printf_message(context,
2366 _("Could not convert dmAcceptanceTime as ISO time: %s"),
2367 string_locale);
2368 free(string_locale);
2369 goto leave;
2371 zfree(string);
2374 leave:
2375 if (err) isds_envelope_free(envelope);
2376 free(unumber);
2377 free(string);
2378 xmlXPathFreeObject(result);
2379 return err;
2383 /* Convert message type attribute of current element into isds_envelope
2384 * structure.
2385 * TODO: This function can be incorporated into append_status_size_times() as
2386 * they are called always together.
2387 * The envelope is automatically allocated but not reallocated.
2388 * The data are just appended into envelope structure.
2389 * @context is ISDS context
2390 * @envelope is automically allocated message envelope structure
2391 * @xpath_ctx is XPath context with current node as parent of attribute
2392 * carrying message type
2393 * In case of error @envelope will be freed. */
2394 static isds_error append_message_type(struct isds_ctx *context,
2395 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2396 isds_error err = IE_SUCCESS;
2398 if (!context) return IE_INVALID_CONTEXT;
2399 if (!envelope) return IE_INVAL;
2400 if (!xpath_ctx) return IE_INVAL;
2403 if (!*envelope) {
2404 /* Allocate new */
2405 *envelope = calloc(1, sizeof(**envelope));
2406 if (!*envelope) {
2407 err = IE_NOMEM;
2408 goto leave;
2410 } else {
2411 /* Free old data */
2412 zfree((*envelope)->dmType);
2416 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope)->dmType, 0);
2418 if (!(*envelope)->dmType) {
2419 /* Use default value */
2420 (*envelope)->dmType = strdup("V");
2421 if (!(*envelope)->dmType) {
2422 err = IE_NOMEM;
2423 goto leave;
2425 } else if (1 != xmlUTF8Strlen((xmlChar *) (*envelope)->dmType)) {
2426 char *type_locale = utf82locale((*envelope)->dmType);
2427 isds_printf_message(context,
2428 _("Message type in dmType attribute is not 1 character long: "
2429 "%s"),
2430 type_locale);
2431 free(type_locale);
2432 err = IE_ISDS;
2433 goto leave;
2436 leave:
2437 if (err) isds_envelope_free(envelope);
2438 return err;
2443 /* Extract message document into reallocated document structure
2444 * @context is ISDS context
2445 * @document is automically reallocated message documents structure
2446 * @xpath_ctx is XPath context with current node as isds:dmFile
2447 * In case of error @document will be freed. */
2448 static isds_error extract_document(struct isds_ctx *context,
2449 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
2450 isds_error err = IE_SUCCESS;
2451 xmlXPathObjectPtr result = NULL;
2452 xmlNodePtr file_node = xpath_ctx->node;
2453 char *string = NULL;
2455 if (!context) return IE_INVALID_CONTEXT;
2456 if (!document) return IE_INVAL;
2457 isds_document_free(document);
2458 if (!xpath_ctx) return IE_INVAL;
2460 *document = calloc(1, sizeof(**document));
2461 if (!*document) {
2462 err = IE_NOMEM;
2463 goto leave;
2466 /* Extract document metadata */
2467 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
2469 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
2470 err = string2isds_FileMetaType((xmlChar*)string,
2471 &((*document)->dmFileMetaType));
2472 if (err) {
2473 char *meta_type_locale = utf82locale(string);
2474 isds_printf_message(context,
2475 _("Document has invalid dmFileMetaType attribute value: %s"),
2476 meta_type_locale);
2477 free(meta_type_locale);
2478 err = IE_ISDS;
2479 goto leave;
2481 zfree(string);
2483 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
2484 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
2485 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
2486 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
2489 /* Extract document data.
2490 * Base64 encoded blob or XML subtree must be presented. */
2492 /* Check from dmEncodedContent */
2493 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
2494 xpath_ctx);
2495 if (!result) {
2496 err = IE_XML;
2497 goto leave;
2500 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2501 /* Here we have Base64 blob */
2503 if (result->nodesetval->nodeNr > 1) {
2504 isds_printf_message(context,
2505 _("Document has more dmEncodedContent elements"));
2506 err = IE_ISDS;
2507 goto leave;
2510 xmlXPathFreeObject(result); result = NULL;
2511 EXTRACT_STRING("isds:dmEncodedContent", string);
2513 /* Decode non-emptys document */
2514 if (string && string[0] != '\0') {
2515 (*document)->data_length = b64decode(string, &((*document)->data));
2516 if ((*document)->data_length == (size_t) -1) {
2517 isds_printf_message(context,
2518 _("Error while Base64-decoding document content"));
2519 err = IE_ERROR;
2520 goto leave;
2523 } else {
2524 /* No Base64 blob, try XML document */
2525 xmlXPathFreeObject(result); result = NULL;
2526 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
2527 xpath_ctx);
2528 if (!result) {
2529 err = IE_XML;
2530 goto leave;
2533 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2534 /* Here we have XML document */
2536 if (result->nodesetval->nodeNr > 1) {
2537 isds_printf_message(context,
2538 _("Document has more dmXMLContent elements"));
2539 err = IE_ISDS;
2540 goto leave;
2543 /* FIXME: Serialize the tree rooted at result's node */
2544 isds_printf_message(context,
2545 _("XML documents not yet supported"));
2546 err = IE_NOTSUP;
2547 goto leave;
2548 } else {
2549 /* No bas64 blob, nor XML document */
2550 isds_printf_message(context,
2551 _("Document has no dmEncodedContent, nor dmXMLContent "
2552 "element"));
2553 err = IE_ISDS;
2554 goto leave;
2559 leave:
2560 if (err) isds_document_free(document);
2561 free(string);
2562 xmlXPathFreeObject(result);
2563 xpath_ctx->node = file_node;
2564 return err;
2569 /* Extract message documents into reallocated list of documents
2570 * @context is ISDS context
2571 * @documents is automically reallocated message documents list structure
2572 * @xpath_ctx is XPath context with current node as XSD tFilesArray
2573 * In case of error @documents will be freed. */
2574 static isds_error extract_documents(struct isds_ctx *context,
2575 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
2576 isds_error err = IE_SUCCESS;
2577 xmlXPathObjectPtr result = NULL;
2578 xmlNodePtr files_node = xpath_ctx->node;
2579 struct isds_list *document, *prev_document;
2581 if (!context) return IE_INVALID_CONTEXT;
2582 if (!documents) return IE_INVAL;
2583 isds_list_free(documents);
2584 if (!xpath_ctx) return IE_INVAL;
2586 /* Find documents */
2587 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
2588 if (!result) {
2589 err = IE_XML;
2590 goto leave;
2593 /* No match */
2594 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2595 isds_printf_message(context,
2596 _("Message does not contain any document"));
2597 err = IE_ISDS;
2598 goto leave;
2602 /* Iterate over documents */
2603 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
2605 /* Allocate and append list item */
2606 document = calloc(1, sizeof(*document));
2607 if (!document) {
2608 err = IE_NOMEM;
2609 goto leave;
2611 document->destructor = (void (*)(void **))isds_document_free;
2612 if (i == 0) *documents = document;
2613 else prev_document->next = document;
2614 prev_document = document;
2616 /* Extract document */
2617 xpath_ctx->node = result->nodesetval->nodeTab[i];
2618 err = extract_document(context,
2619 (struct isds_document **) &(document->data), xpath_ctx);
2620 if (err) goto leave;
2624 leave:
2625 if (err) isds_list_free(documents);
2626 xmlXPathFreeObject(result);
2627 xpath_ctx->node = files_node;
2628 return err;
2632 /* Convert isds:dmRecord XML tree into structure
2633 * @context is ISDS context
2634 * @envelope is automically reallocated message envelope structure
2635 * @xpath_ctx is XPath context with current node as isds:dmRecord element
2636 * In case of error @envelope will be freed. */
2637 static isds_error extract_DmRecord(struct isds_ctx *context,
2638 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2639 isds_error err = IE_SUCCESS;
2640 xmlXPathObjectPtr result = NULL;
2642 if (!context) return IE_INVALID_CONTEXT;
2643 if (!envelope) return IE_INVAL;
2644 isds_envelope_free(envelope);
2645 if (!xpath_ctx) return IE_INVAL;
2648 *envelope = calloc(1, sizeof(**envelope));
2649 if (!*envelope) {
2650 err = IE_NOMEM;
2651 goto leave;
2655 /* Extract tRecord data */
2656 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
2658 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
2659 * dmAcceptanceTime. */
2660 err = append_status_size_times(context, envelope, xpath_ctx);
2661 if (err) goto leave;
2663 /* Extract envelope elements added by sender and ISDS
2664 * (XSD: gMessageEnvelope type) */
2665 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
2666 if (err) goto leave;
2667 /* dmOVM can not be obtained from ISDS */
2669 /* Get message type */
2670 err = append_message_type(context, envelope, xpath_ctx);
2671 if (err) goto leave;
2674 leave:
2675 if (err) isds_envelope_free(envelope);
2676 xmlXPathFreeObject(result);
2677 return err;
2681 /* Find and convert isds:dmHash XML tree into structure
2682 * @context is ISDS context
2683 * @envelope is automically reallocated message hash structure
2684 * @xpath_ctx is XPath context with current node containing isds:dmHash child
2685 * In case of error @hash will be freed. */
2686 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
2687 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
2688 isds_error err = IE_SUCCESS;
2689 xmlNodePtr old_ctx_node;
2690 xmlXPathObjectPtr result = NULL;
2691 char *string = NULL;
2693 if (!context) return IE_INVALID_CONTEXT;
2694 if (!hash) return IE_INVAL;
2695 isds_hash_free(hash);
2696 if (!xpath_ctx) return IE_INVAL;
2698 old_ctx_node = xpath_ctx->node;
2700 *hash = calloc(1, sizeof(**hash));
2701 if (!*hash) {
2702 err = IE_NOMEM;
2703 goto leave;
2706 /* Locate dmHash */
2707 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
2708 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
2709 err = IE_ISDS;
2710 goto leave;
2712 if (err) {
2713 err = IE_ERROR;
2714 goto leave;
2717 /* Get hash algorithm */
2718 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
2719 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
2720 if (err) {
2721 if (err == IE_ENUM) {
2722 char *string_locale = utf82locale(string);
2723 isds_printf_message(context, _("Unsported hash algorithm: %s"),
2724 string_locale);
2725 free(string_locale);
2727 goto leave;
2729 zfree(string);
2731 /* Get hash value */
2732 EXTRACT_STRING(".", string);
2733 if (!string) {
2734 isds_printf_message(context, _("tHash element is missing hash value"));
2735 err = IE_ISDS;
2736 goto leave;
2738 (*hash)->length = b64decode(string, &((*hash)->value));
2739 if ((*hash)->length == (size_t) -1) {
2740 isds_printf_message(context,
2741 _("Error while Base64-decoding hash value"));
2742 err = IE_ERROR;
2743 goto leave;
2746 leave:
2747 if (err) isds_hash_free(hash);
2748 free(string);
2749 xmlXPathFreeObject(result);
2750 xpath_ctx->node = old_ctx_node;
2751 return err;
2755 /* Find and append isds:dmQTimestamp XML tree into envelope
2756 * @context is ISDS context
2757 * @envelope is automically allocated evnelope structure
2758 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
2759 * child
2760 * In case of error @envelope will be freed. */
2761 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
2762 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2763 isds_error err = IE_SUCCESS;
2764 xmlXPathObjectPtr result = NULL;
2765 char *string = NULL;
2767 if (!context) return IE_INVALID_CONTEXT;
2768 if (!envelope) return IE_INVAL;
2769 if (!xpath_ctx) {
2770 isds_envelope_free(envelope);
2771 return IE_INVAL;
2774 if (!*envelope) {
2775 *envelope = calloc(1, sizeof(**envelope));
2776 if (!*envelope) {
2777 err = IE_NOMEM;
2778 goto leave;
2780 } else {
2781 zfree((*envelope)->timestamp);
2782 (*envelope)->timestamp_length = 0;
2785 /* Get dmQTimestamp */
2786 EXTRACT_STRING("sisds:dmQTimestamp", string);
2787 if (!string) {
2788 isds_printf_message(context, _("Missing dmQTimestamp element content"));
2789 err = IE_ISDS;
2790 goto leave;
2792 (*envelope)->timestamp_length =
2793 b64decode(string, &((*envelope)->timestamp));
2794 if ((*envelope)->timestamp_length == (size_t) -1) {
2795 isds_printf_message(context,
2796 _("Error while Base64-decoding timestamp value"));
2797 err = IE_ERROR;
2798 goto leave;
2801 leave:
2802 if (err) isds_envelope_free(envelope);
2803 free(string);
2804 xmlXPathFreeObject(result);
2805 return err;
2809 /* Convert XSD tReturnedMessage XML tree into message structure.
2810 * It doea not store XML tree into message->raw.
2811 * @context is ISDS context
2812 * @include_documents Use true if documents must be extracted
2813 * (tReturnedMessage XSD type), use false if documents shall be ommited
2814 * (tReturnedMessageEnvelope).
2815 * @message is automically reallocated message structure
2816 * @xpath_ctx is XPath context with current node as tReturnedMessage element
2817 * type
2818 * In case of error @message will be freed. */
2819 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
2820 const _Bool include_documents, struct isds_message **message,
2821 xmlXPathContextPtr xpath_ctx) {
2822 isds_error err = IE_SUCCESS;
2823 xmlNodePtr message_node;
2825 if (!context) return IE_INVALID_CONTEXT;
2826 if (!message) return IE_INVAL;
2827 isds_message_free(message);
2828 if (!xpath_ctx) return IE_INVAL;
2831 *message = calloc(1, sizeof(**message));
2832 if (!*message) {
2833 err = IE_NOMEM;
2834 goto leave;
2837 /* Save message XPATH context node */
2838 message_node = xpath_ctx->node;
2841 /* Extract dmDM */
2842 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
2843 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
2844 if (err) { err = IE_ERROR; goto leave; }
2845 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
2846 if (err) goto leave;
2848 if (include_documents) {
2849 /* Extract dmFiles */
2850 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
2851 xpath_ctx);
2852 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
2853 err = IE_ISDS; goto leave;
2855 if (err) { err = IE_ERROR; goto leave; }
2856 err = extract_documents(context, &((*message)->documents), xpath_ctx);
2857 if (err) goto leave;
2861 /* Restore context to message */
2862 xpath_ctx->node = message_node;
2864 /* Extract dmHash */
2865 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
2866 xpath_ctx);
2867 if (err) goto leave;
2869 /* Extract dmQTimestamp, */
2870 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
2871 xpath_ctx);
2872 if (err) goto leave;
2874 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
2875 * dmAcceptanceTime. */
2876 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
2877 if (err) goto leave;
2879 /* Get message type */
2880 err = append_message_type(context, &((*message)->envelope), xpath_ctx);
2881 if (err) goto leave;
2883 leave:
2884 if (err) isds_message_free(message);
2885 return err;
2889 /* Extract message event into reallocated isds_event structure
2890 * @context is ISDS context
2891 * @event is automically reallocated message event structure
2892 * @xpath_ctx is XPath context with current node as isds:dmEvent
2893 * In case of error @event will be freed. */
2894 static isds_error extract_event(struct isds_ctx *context,
2895 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
2896 isds_error err = IE_SUCCESS;
2897 xmlXPathObjectPtr result = NULL;
2898 xmlNodePtr event_node = xpath_ctx->node;
2899 char *string = NULL;
2901 if (!context) return IE_INVALID_CONTEXT;
2902 if (!event) return IE_INVAL;
2903 isds_event_free(event);
2904 if (!xpath_ctx) return IE_INVAL;
2906 *event = calloc(1, sizeof(**event));
2907 if (!*event) {
2908 err = IE_NOMEM;
2909 goto leave;
2912 /* Extract event data.
2913 * All elements are optional according XSD. That's funny. */
2914 EXTRACT_STRING("sisds:dmEventTime", string);
2915 if (string) {
2916 err = timestring2timeval((xmlChar *) string, &((*event)->time));
2917 if (err) {
2918 char *string_locale = utf82locale(string);
2919 if (err == IE_DATE) err = IE_ISDS;
2920 isds_printf_message(context,
2921 _("Could not convert dmEventTime as ISO time: %s"),
2922 string_locale);
2923 free(string_locale);
2924 goto leave;
2926 zfree(string);
2929 /* dmEventDescr element has prefix and the rest */
2930 EXTRACT_STRING("sisds:dmEventDescr", string);
2931 if (string) {
2932 err = eventstring2event((xmlChar *) string, *event);
2933 if (err) goto leave;
2934 zfree(string);
2937 leave:
2938 if (err) isds_event_free(event);
2939 free(string);
2940 xmlXPathFreeObject(result);
2941 xpath_ctx->node = event_node;
2942 return err;
2946 /* Convert element of XSD tEventsArray type from XML tree into
2947 * isds_list of isds_event's structure. The list is automatically reallocated.
2948 * @context is ISDS context
2949 * @events is automically reallocated list of event structures
2950 * @xpath_ctx is XPath context with current node as tEventsArray
2951 * In case of error @evnets will be freed. */
2952 static isds_error extract_events(struct isds_ctx *context,
2953 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
2954 isds_error err = IE_SUCCESS;
2955 xmlXPathObjectPtr result = NULL;
2956 xmlNodePtr events_node = xpath_ctx->node;
2957 struct isds_list *event, *prev_event = NULL;
2959 if (!context) return IE_INVALID_CONTEXT;
2960 if (!events) return IE_INVAL;
2961 if (!xpath_ctx) return IE_INVAL;
2963 /* Free old list */
2964 isds_list_free(events);
2966 /* Find events */
2967 result = xmlXPathEvalExpression(BAD_CAST "sisds:dmEvent", xpath_ctx);
2968 if (!result) {
2969 err = IE_XML;
2970 goto leave;
2973 /* No match */
2974 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2975 isds_printf_message(context,
2976 _("Delivery info does not contain any event"));
2977 err = IE_ISDS;
2978 goto leave;
2982 /* Iterate over events */
2983 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
2985 /* Allocate and append list item */
2986 event = calloc(1, sizeof(*event));
2987 if (!event) {
2988 err = IE_NOMEM;
2989 goto leave;
2991 event->destructor = (void (*)(void **))isds_event_free;
2992 if (i == 0) *events = event;
2993 else prev_event->next = event;
2994 prev_event = event;
2996 /* Extract event */
2997 xpath_ctx->node = result->nodesetval->nodeTab[i];
2998 err = extract_event(context,
2999 (struct isds_event **) &(event->data), xpath_ctx);
3000 if (err) goto leave;
3004 leave:
3005 if (err) isds_list_free(events);
3006 xmlXPathFreeObject(result);
3007 xpath_ctx->node = events_node;
3008 return err;
3012 /* Convert isds_document structure into XML tree and append to dmFiles node.
3013 * @context is session context
3014 * @document is ISDS document
3015 * @dm_files is XML element the resulting tree will be appended to as a child.
3016 * @return error code, in case of error context' message is filled. */
3017 static isds_error insert_document(struct isds_ctx *context,
3018 struct isds_document *document, xmlNodePtr dm_files) {
3019 isds_error err = IE_SUCCESS;
3020 xmlNodePtr new_file = NULL, file = NULL, node;
3021 xmlAttrPtr attribute_node;
3022 xmlChar *base64data = NULL;
3024 if (!context) return IE_INVALID_CONTEXT;
3025 if (!document || !dm_files) return IE_INVAL;
3027 /* Allocate new dmFile */
3028 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
3029 if (!new_file) {
3030 isds_printf_message(context, _("Could not allocate main dmFile"));
3031 err = IE_ERROR;
3032 goto leave;
3034 /* Append the new dmFile.
3035 * XXX: Main document must go first */
3036 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
3037 file = xmlAddPrevSibling(dm_files->children, new_file);
3038 else
3039 file = xmlAddChild(dm_files, new_file);
3041 if (!file) {
3042 xmlFreeNode(new_file); new_file = NULL;
3043 isds_printf_message(context, _("Could not add dmFile child to "
3044 "%s element"), dm_files->name);
3045 err = IE_ERROR;
3046 goto leave;
3049 /* @dmMimeType is required */
3050 if (!document->dmMimeType) {
3051 isds_log_message(context,
3052 _("Document is missing mandatory MIME type definition"));
3053 err = IE_INVAL;
3054 goto leave;
3056 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
3058 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
3059 if (!string) {
3060 isds_printf_message(context,
3061 _("Document has unkown dmFileMetaType: %ld"),
3062 document->dmFileMetaType);
3063 err = IE_ENUM;
3064 goto leave;
3066 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
3068 if (document->dmFileGuid) {
3069 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
3071 if (document->dmUpFileGuid) {
3072 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
3075 /* @dmFileDescr is required */
3076 if (!document->dmFileDescr) {
3077 isds_log_message(context,
3078 _("Document is missing mandatory description (title)"));
3079 err = IE_INVAL;
3080 goto leave;
3082 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
3084 if (document->dmFormat) {
3085 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
3089 /* Insert content (data) of the document. */
3090 /* XXX; Only base64 is implemented currently. */
3091 base64data = (xmlChar *) b64encode(document->data, document->data_length);
3092 if (!base64data) {
3093 isds_printf_message(context,
3094 _("Not enought memory to encode %zd bytes into Base64"),
3095 document->data_length);
3096 err = IE_NOMEM;
3097 goto leave;
3099 INSERT_STRING(file, "dmEncodedContent", base64data);
3100 free(base64data);
3102 leave:
3103 return err;
3107 /* Append XSD tMStatus XML tree into isds_message_copy structure.
3108 * The copy must pre prealocated, the date are just appended into structure.
3109 * @context is ISDS context
3110 * @copy is message copy struture
3111 * @xpath_ctx is XPath context with current node as tMStatus */
3112 static isds_error append_TMStatus(struct isds_ctx *context,
3113 struct isds_message_copy *copy, xmlXPathContextPtr xpath_ctx) {
3114 isds_error err = IE_SUCCESS;
3115 xmlXPathObjectPtr result = NULL;
3116 char *code = NULL, *message = NULL;
3118 if (!context) return IE_INVALID_CONTEXT;
3119 if (!copy || !xpath_ctx) return IE_INVAL;
3121 /* Free old values */
3122 zfree(copy->dmStatus);
3123 zfree(copy->dmID);
3125 /* Get error specific to this copy */
3126 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code);
3127 if (!code) {
3128 isds_log_message(context,
3129 _("Missing isds:dmStatusCode under "
3130 "XSD:tMStatus type element"));
3131 err = IE_ISDS;
3132 goto leave;
3135 if (xmlStrcmp((const xmlChar *)code, BAD_CAST "0000")) {
3136 /* This copy failed */
3137 copy->error = IE_ISDS;
3138 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message);
3139 if (message) {
3140 copy->dmStatus = astrcat3(code, ": ", message);
3141 if (!copy->dmStatus) {
3142 copy->dmStatus = code;
3143 code = NULL;
3145 } else {
3146 copy->dmStatus = code;
3147 code = NULL;
3149 } else {
3150 /* This copy succeeded. In this case only, message ID is valid */
3151 copy->error = IE_SUCCESS;
3153 EXTRACT_STRING("isds:dmID", copy->dmID);
3154 if (!copy->dmID) {
3155 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
3156 "but did not returned assigned message ID\n"));
3157 err = IE_ISDS;
3161 leave:
3162 free(code);
3163 free(message);
3164 xmlXPathFreeObject(result);
3165 return err;
3169 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
3170 * code
3171 * @context is session context
3172 * @service_name is name of SERVICE_DB_ACCESS
3173 * @response is server SOAP body response as XML document
3174 * @raw_response is automatically reallocated bitstream with response body. Use
3175 * NULL if you don't care
3176 * @raw_response_length is size of @raw_response in bytes
3177 * @code is ISDS status code
3178 * @status_message is ISDS status message
3179 * @return error coded from lower layer, context message will be set up
3180 * appropriately. */
3181 static isds_error build_send_check_dbdummy_request(struct isds_ctx *context,
3182 const xmlChar *service_name,
3183 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
3184 xmlChar **code, xmlChar **status_message) {
3186 isds_error err = IE_SUCCESS;
3187 char *service_name_locale = NULL;
3188 xmlNodePtr request = NULL, node;
3189 xmlNsPtr isds_ns = NULL;
3191 if (!context) return IE_INVALID_CONTEXT;
3192 if (!service_name) return IE_INVAL;
3193 if (!response || !code || !status_message) return IE_INVAL;
3194 if (!raw_response_length && raw_response) return IE_INVAL;
3196 /* Free output argument */
3197 xmlFreeDoc(*response); *response = NULL;
3198 if (raw_response) zfree(*raw_response);
3199 free(*code);
3200 free(*status_message);
3203 /* Check if connection is established
3204 * TODO: This check should be done donwstairs. */
3205 if (!context->curl) return IE_CONNECTION_CLOSED;
3207 service_name_locale = utf82locale((char*)service_name);
3208 if (!service_name_locale) {
3209 err = IE_NOMEM;
3210 goto leave;
3213 /* Build request */
3214 request = xmlNewNode(NULL, service_name);
3215 if (!request) {
3216 isds_printf_message(context,
3217 _("Could not build %s request"), service_name_locale);
3218 err = IE_ERROR;
3219 goto leave;
3221 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
3222 if(!isds_ns) {
3223 isds_log_message(context, _("Could not create ISDS name space"));
3224 err = IE_ERROR;
3225 goto leave;
3227 xmlSetNs(request, isds_ns);
3230 /* Add XSD:tDummyInput child */
3231 INSERT_STRING(request, "dbDummy", NULL);
3234 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
3235 service_name_locale);
3237 /* Send request */
3238 err = isds(context, SERVICE_DB_ACCESS, request, response,
3239 raw_response, raw_response_length);
3240 xmlFreeNode(request); request = NULL;
3242 if (err) {
3243 isds_log(ILF_ISDS, ILL_DEBUG,
3244 _("Processing ISDS response on %s request failed\n"),
3245 service_name_locale);
3246 goto leave;
3249 /* Check for response status */
3250 err = isds_response_status(context, SERVICE_DB_ACCESS, *response,
3251 code, status_message, NULL);
3252 if (err) {
3253 isds_log(ILF_ISDS, ILL_DEBUG,
3254 _("ISDS response on %s request is missing status\n"),
3255 service_name_locale);
3256 goto leave;
3259 /* Request processed, but nothing found */
3260 if (xmlStrcmp(*code, BAD_CAST "0000")) {
3261 char *code_locale = utf82locale((char*) *code);
3262 char *status_message_locale = utf82locale((char*) *status_message);
3263 isds_log(ILF_ISDS, ILL_DEBUG,
3264 _("Server refused %s request (code=%s, message=%s)\n"),
3265 service_name_locale, code_locale, status_message_locale);
3266 isds_log_message(context, status_message_locale);
3267 free(code_locale);
3268 free(status_message_locale);
3269 err = IE_ISDS;
3270 goto leave;
3273 leave:
3274 free(service_name_locale);
3275 xmlFreeNode(request);
3276 return err;
3280 /* Get data about logged in user and his box. */
3281 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
3282 struct isds_DbOwnerInfo **db_owner_info) {
3283 isds_error err = IE_SUCCESS;
3284 xmlDocPtr response = NULL;
3285 xmlChar *code = NULL, *message = NULL;
3286 xmlXPathContextPtr xpath_ctx = NULL;
3287 xmlXPathObjectPtr result = NULL;
3288 char *string = NULL;
3290 if (!context) return IE_INVALID_CONTEXT;
3291 if (!db_owner_info) return IE_INVAL;
3293 /* Check if connection is established */
3294 if (!context->curl) return IE_CONNECTION_CLOSED;
3297 /* Do request and check for success */
3298 err = build_send_check_dbdummy_request(context,
3299 BAD_CAST "GetOwnerInfoFromLogin",
3300 &response, NULL, NULL, &code, &message);
3301 if (err) goto leave;
3304 /* Extract data */
3305 /* Prepare stucture */
3306 isds_DbOwnerInfo_free(db_owner_info);
3307 *db_owner_info = calloc(1, sizeof(**db_owner_info));
3308 if (!*db_owner_info) {
3309 err = IE_NOMEM;
3310 goto leave;
3312 xpath_ctx = xmlXPathNewContext(response);
3313 if (!xpath_ctx) {
3314 err = IE_ERROR;
3315 goto leave;
3317 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
3318 err = IE_ERROR;
3319 goto leave;
3322 /* Set context node */
3323 result = xmlXPathEvalExpression(BAD_CAST
3324 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
3325 if (!result) {
3326 err = IE_ERROR;
3327 goto leave;
3329 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3330 isds_log_message(context, _("Missing dbOwnerInfo element"));
3331 err = IE_ISDS;
3332 goto leave;
3334 if (result->nodesetval->nodeNr > 1) {
3335 isds_log_message(context, _("Multiple dbOwnerInfo element"));
3336 err = IE_ISDS;
3337 goto leave;
3339 xpath_ctx->node = result->nodesetval->nodeTab[0];
3340 xmlXPathFreeObject(result); result = NULL;
3342 /* Extract it */
3343 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
3345 leave:
3346 if (err) {
3347 isds_DbOwnerInfo_free(db_owner_info);
3350 free(string);
3351 xmlXPathFreeObject(result);
3352 xmlXPathFreeContext(xpath_ctx);
3354 free(code);
3355 free(message);
3356 xmlFreeDoc(response);
3358 if (!err)
3359 isds_log(ILF_ISDS, ILL_DEBUG,
3360 _("GetOwnerInfoFromLogin request processed by server "
3361 "successfully.\n"));
3363 return err;
3367 /* Get data about logged in user. */
3368 isds_error isds_GetUserInfoFromLogin(struct isds_ctx *context,
3369 struct isds_DbUserInfo **db_user_info) {
3370 isds_error err = IE_SUCCESS;
3371 xmlDocPtr response = NULL;
3372 xmlChar *code = NULL, *message = NULL;
3373 xmlXPathContextPtr xpath_ctx = NULL;
3374 xmlXPathObjectPtr result = NULL;
3376 if (!context) return IE_INVALID_CONTEXT;
3377 if (!db_user_info) return IE_INVAL;
3379 /* Check if connection is established */
3380 if (!context->curl) return IE_CONNECTION_CLOSED;
3383 /* Do request and check for success */
3384 err = build_send_check_dbdummy_request(context,
3385 BAD_CAST "GetUserInfoFromLogin",
3386 &response, NULL, NULL, &code, &message);
3387 if (err) goto leave;
3390 /* Extract data */
3391 /* Prepare stucture */
3392 isds_DbUserInfo_free(db_user_info);
3393 *db_user_info = calloc(1, sizeof(**db_user_info));
3394 if (!*db_user_info) {
3395 err = IE_NOMEM;
3396 goto leave;
3398 xpath_ctx = xmlXPathNewContext(response);
3399 if (!xpath_ctx) {
3400 err = IE_ERROR;
3401 goto leave;
3403 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
3404 err = IE_ERROR;
3405 goto leave;
3408 /* Set context node */
3409 result = xmlXPathEvalExpression(BAD_CAST
3410 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx);
3411 if (!result) {
3412 err = IE_ERROR;
3413 goto leave;
3415 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3416 isds_log_message(context, _("Missing dbUserInfo element"));
3417 err = IE_ISDS;
3418 goto leave;
3420 if (result->nodesetval->nodeNr > 1) {
3421 isds_log_message(context, _("Multiple dbUserInfo element"));
3422 err = IE_ISDS;
3423 goto leave;
3425 xpath_ctx->node = result->nodesetval->nodeTab[0];
3426 xmlXPathFreeObject(result); result = NULL;
3428 /* Extract it */
3429 err = extract_DbUserInfo(context, db_user_info, xpath_ctx);
3431 leave:
3432 if (err) {
3433 isds_DbUserInfo_free(db_user_info);
3436 xmlXPathFreeObject(result);
3437 xmlXPathFreeContext(xpath_ctx);
3439 free(code);
3440 free(message);
3441 xmlFreeDoc(response);
3443 if (!err)
3444 isds_log(ILF_ISDS, ILL_DEBUG,
3445 _("GetUserInfoFromLogin request processed by server "
3446 "successfully.\n"));
3448 return err;
3452 /* Get data about all users with access to your box.
3453 * @context is session context
3454 * @users is automatically reallocated list of struct isds_DbUserInfo */
3455 isds_error isds_GetDataBoxUsers(struct isds_ctx *context,
3456 struct isds_list **users) {
3457 isds_error err = IE_SUCCESS;
3458 xmlDocPtr response = NULL;
3459 xmlChar *code = NULL, *message = NULL;
3460 xmlXPathContextPtr xpath_ctx = NULL;
3461 xmlXPathObjectPtr result = NULL;
3462 int i;
3463 struct isds_list *item, *prev_item = NULL;
3465 if (!context) return IE_INVALID_CONTEXT;
3466 if (!users) return IE_INVAL;
3468 /* Check if connection is established */
3469 if (!context->curl) return IE_CONNECTION_CLOSED;
3472 /* Do request and check for success */
3473 err = build_send_check_dbdummy_request(context,
3474 BAD_CAST "GetDataBoxUsers",
3475 &response, NULL, NULL, &code, &message);
3476 if (err) goto leave;
3479 /* Extract data */
3480 /* Prepare stucture */
3481 isds_list_free(users);
3482 xpath_ctx = xmlXPathNewContext(response);
3483 if (!xpath_ctx) {
3484 err = IE_ERROR;
3485 goto leave;
3487 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
3488 err = IE_ERROR;
3489 goto leave;
3492 /* Set context node */
3493 result = xmlXPathEvalExpression(BAD_CAST
3494 "/isds:EnableOwnDataBoxResponse/isds:dbUsers/isds:dbUserInfo",
3495 xpath_ctx);
3496 if (!result) {
3497 err = IE_ERROR;
3498 goto leave;
3500 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3501 isds_log_message(context, _("Missing dbUserInfo element"));
3502 err = IE_ISDS;
3503 goto leave;
3506 /* Iterate over all users */
3507 for (i = 0; i < result->nodesetval->nodeNr; i++) {
3509 /* Prepare structure */
3510 item = calloc(1, sizeof(*item));
3511 if (!item) {
3512 err = IE_NOMEM;
3513 goto leave;
3515 item->destructor = (void(*)(void**))isds_DbUserInfo_free;
3516 if (i == 0) *users = item;
3517 else prev_item->next = item;
3518 prev_item = item;
3520 /* Extract it */
3521 xpath_ctx->node = result->nodesetval->nodeTab[i];
3522 err = extract_DbUserInfo(context,
3523 (struct isds_DbUserInfo **) (&item->data), xpath_ctx);
3524 if (err) goto leave;
3527 leave:
3528 if (err) {
3529 isds_list_free(users);
3532 xmlXPathFreeObject(result);
3533 xmlXPathFreeContext(xpath_ctx);
3535 free(code);
3536 free(message);
3537 xmlFreeDoc(response);
3539 if (!err)
3540 isds_log(ILF_ISDS, ILL_DEBUG,
3541 _("GetUserInfoFromLogin request processed by server "
3542 "successfully.\n"));
3544 return err;
3548 /* Get expiration time of current password
3549 * @context is session context
3550 * @expiration is automatically reallocated time when password expires, In
3551 * case of error will be nulled. */
3552 isds_error isds_get_password_expiration(struct isds_ctx *context,
3553 struct timeval **expiration) {
3554 isds_error err = IE_SUCCESS;
3555 xmlDocPtr response = NULL;
3556 xmlChar *code = NULL, *message = NULL;
3557 xmlXPathContextPtr xpath_ctx = NULL;
3558 xmlXPathObjectPtr result = NULL;
3559 char *string = NULL;
3561 if (!context) return IE_INVALID_CONTEXT;
3562 if (!expiration) return IE_INVAL;
3564 /* Check if connection is established */
3565 if (!context->curl) return IE_CONNECTION_CLOSED;
3568 /* Do request and check for success */
3569 err = build_send_check_dbdummy_request(context,
3570 BAD_CAST "GetPasswordInfo",
3571 &response, NULL, NULL, &code, &message);
3572 if (err) goto leave;
3575 /* Extract data */
3576 xpath_ctx = xmlXPathNewContext(response);
3577 if (!xpath_ctx) {
3578 err = IE_ERROR;
3579 goto leave;
3581 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
3582 err = IE_ERROR;
3583 goto leave;
3586 /* Set context node */
3587 result = xmlXPathEvalExpression(BAD_CAST
3588 "/isds:GetPasswordInfoResponse", xpath_ctx);
3589 if (!result) {
3590 err = IE_ERROR;
3591 goto leave;
3593 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3594 isds_log_message(context,
3595 _("Missing GetPasswordInfoResponse element"));
3596 err = IE_ISDS;
3597 goto leave;
3599 if (result->nodesetval->nodeNr > 1) {
3600 isds_log_message(context,
3601 _("Multiple GetPasswordInfoResponse element"));
3602 err = IE_ISDS;
3603 goto leave;
3605 xpath_ctx->node = result->nodesetval->nodeTab[0];
3606 xmlXPathFreeObject(result); result = NULL;
3608 /* Extract expiration date */
3609 EXTRACT_STRING("isds:pswExpDate", string);
3610 if (!string) {
3611 isds_log_message(context, _("Missing pswExpDate element"));
3612 err = IE_ISDS;
3613 goto leave;
3616 err = timestring2timeval((xmlChar *) string, expiration);
3617 if (err) {
3618 char *string_locale = utf82locale(string);
3619 if (err == IE_DATE) err = IE_ISDS;
3620 isds_printf_message(context,
3621 _("Could not convert pswExpDate as ISO time: %s"),
3622 string_locale);
3623 free(string_locale);
3624 goto leave;
3627 leave:
3628 if (err) {
3629 if (*expiration) {
3630 zfree(*expiration);
3634 free(string);
3635 xmlXPathFreeObject(result);
3636 xmlXPathFreeContext(xpath_ctx);
3638 free(code);
3639 free(message);
3640 xmlFreeDoc(response);
3642 if (!err)
3643 isds_log(ILF_ISDS, ILL_DEBUG,
3644 _("GetPasswordInfo request processed by server "
3645 "successfully.\n"));
3647 return err;
3651 /* Change user password in ISDS.
3652 * User must supply old password, new password will takes effect after some
3653 * time, current session can continue. Password must fulfill some constraints.
3654 * @context is session context
3655 * @old_password is current password.
3656 * @new_password is requested new password */
3657 isds_error isds_change_password(struct isds_ctx *context,
3658 const char *old_password, const char *new_password) {
3659 isds_error err = IE_SUCCESS;
3660 xmlNsPtr isds_ns = NULL;
3661 xmlNodePtr request = NULL, node;
3662 xmlDocPtr response = NULL;
3663 xmlChar *code = NULL, *message = NULL;
3665 if (!context) return IE_INVALID_CONTEXT;
3666 if (!old_password || !new_password) return IE_INVAL;
3668 /* Check if connection is established
3669 * TODO: This check should be done donwstairs. */
3670 if (!context->curl) return IE_CONNECTION_CLOSED;
3673 /* Build ChangeISDSPassword request */
3674 request = xmlNewNode(NULL, BAD_CAST "ChangeISDSPassword");
3675 if (!request) {
3676 isds_log_message(context,
3677 _("Could build ChangeISDSPassword request"));
3678 return IE_ERROR;
3680 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
3681 if(!isds_ns) {
3682 isds_log_message(context, _("Could not create ISDS name space"));
3683 xmlFreeNode(request);
3684 return IE_ERROR;
3686 xmlSetNs(request, isds_ns);
3688 INSERT_STRING(request, "dbOldPassword", old_password);
3689 INSERT_STRING(request, "dbNewPassword", new_password);
3692 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CheckDataBox request to ISDS\n"));
3694 /* Sent request */
3695 err = isds(context, SERVICE_DB_ACCESS, request, &response, NULL, NULL);
3697 /* Destroy request */
3698 xmlFreeNode(request); request = NULL;
3700 if (err) {
3701 isds_log(ILF_ISDS, ILL_DEBUG,
3702 _("Processing ISDS response on ChangeISDSPassword "
3703 "request failed\n"));
3704 goto leave;
3707 /* Check for response status */
3708 err = isds_response_status(context, SERVICE_DB_ACCESS, response,
3709 &code, &message, NULL);
3710 if (err) {
3711 isds_log(ILF_ISDS, ILL_DEBUG,
3712 _("ISDS response on ChangeISDSPassword request is missing "
3713 "status\n"));
3714 goto leave;
3717 /* Request processed, but empty password refused */
3718 if (!xmlStrcmp(code, BAD_CAST "1066")) {
3719 char *code_locale = utf82locale((char*)code);
3720 char *message_locale = utf82locale((char*)message);
3721 isds_log(ILF_ISDS, ILL_DEBUG,
3722 _("Server refused empty password on ChangeISDSPassword "
3723 "request (code=%s, message=%s)\n"),
3724 code_locale, message_locale);
3725 isds_log_message(context, _("Password must not be empty"));
3726 free(code_locale);
3727 free(message_locale);
3728 err = IE_INVAL;
3729 goto leave;
3732 /* Request processed, but new password was reused */
3733 else if (!xmlStrcmp(code, BAD_CAST "1067")) {
3734 char *code_locale = utf82locale((char*)code);
3735 char *message_locale = utf82locale((char*)message);
3736 isds_log(ILF_ISDS, ILL_DEBUG,
3737 _("Server refused the same new password on ChangeISDSPassword "
3738 "request (code=%s, message=%s)\n"),
3739 code_locale, message_locale);
3740 isds_log_message(context,
3741 _("New password must differ from the current one"));
3742 free(code_locale);
3743 free(message_locale);
3744 err = IE_INVAL;
3745 goto leave;
3748 /* Other error */
3749 else if (xmlStrcmp(code, BAD_CAST "0000")) {
3750 char *code_locale = utf82locale((char*)code);
3751 char *message_locale = utf82locale((char*)message);
3752 isds_log(ILF_ISDS, ILL_DEBUG,
3753 _("Server refused to change password on ChangeISDSPassword "
3754 "request (code=%s, message=%s)\n"),
3755 code_locale, message_locale);
3756 isds_log_message(context, message_locale);
3757 free(code_locale);
3758 free(message_locale);
3759 err = IE_ISDS;
3760 goto leave;
3763 /* Otherwise password changed successfully */
3765 leave:
3766 free(code);
3767 free(message);
3768 xmlFreeDoc(response);
3769 xmlFreeNode(request);
3771 if (!err)
3772 isds_log(ILF_ISDS, ILL_DEBUG,
3773 _("Password changed successfully on ChangeISDSPassword "
3774 "request.\n"));
3776 return err;
3780 /* Find boxes suiting given criteria.
3781 * @criteria is filter. You should fill in at least some memebers.
3782 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
3783 * possibly empty. Input NULL or valid old structure.
3784 * @return:
3785 * IE_SUCCESS if search sucseeded, @boxes contains usefull data
3786 * IE_NOEXIST if no such box exists, @boxes will be NULL
3787 * IE_2BIG if too much boxes exist and server truncated the resuluts, @boxes
3788 * contains still valid data
3789 * other code if something bad happens. @boxes will be NULL. */
3790 isds_error isds_FindDataBox(struct isds_ctx *context,
3791 const struct isds_DbOwnerInfo *criteria,
3792 struct isds_list **boxes) {
3793 isds_error err = IE_SUCCESS;
3794 _Bool truncated = 0;
3795 xmlNsPtr isds_ns = NULL;
3796 xmlNodePtr request = NULL;
3797 xmlDocPtr response = NULL;
3798 xmlChar *code = NULL, *message = NULL;
3799 xmlNodePtr db_owner_info, node;
3800 xmlXPathContextPtr xpath_ctx = NULL;
3801 xmlXPathObjectPtr result = NULL;
3802 xmlChar *string = NULL;
3805 if (!context) return IE_INVALID_CONTEXT;
3806 if (!boxes) return IE_INVAL;
3807 isds_list_free(boxes);
3809 if (!criteria) {
3810 return IE_INVAL;
3813 /* Check if connection is established
3814 * TODO: This check should be done donwstairs. */
3815 if (!context->curl) return IE_CONNECTION_CLOSED;
3818 /* Build FindDataBox request */
3819 request = xmlNewNode(NULL, BAD_CAST "FindDataBox");
3820 if (!request) {
3821 isds_log_message(context,
3822 _("Could build FindDataBox request"));
3823 return IE_ERROR;
3825 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
3826 if(!isds_ns) {
3827 isds_log_message(context, _("Could not create ISDS name space"));
3828 xmlFreeNode(request);
3829 return IE_ERROR;
3831 xmlSetNs(request, isds_ns);
3832 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
3833 if (!db_owner_info) {
3834 isds_log_message(context, _("Could not add dbOwnerInfo Child to "
3835 "FindDataBox element"));
3836 xmlFreeNode(request);
3837 return IE_ERROR;
3841 INSERT_STRING(db_owner_info, "dbID", criteria->dbID);
3843 /* dbType */
3844 if (criteria->dbType) {
3845 const xmlChar *type_string = isds_DbType2string(*(criteria->dbType));
3846 if (!type_string) {
3847 isds_printf_message(context, _("Invalid dbType value: %d"),
3848 *(criteria->dbType));
3849 err = IE_ENUM;
3850 goto leave;
3852 INSERT_STRING(db_owner_info, "dbType", type_string);
3855 INSERT_STRING(db_owner_info, "firmName", criteria->firmName);
3856 INSERT_STRING(db_owner_info, "ic", criteria->ic);
3857 if (criteria->personName) {
3858 INSERT_STRING(db_owner_info, "pnFirstName",
3859 criteria->personName->pnFirstName);
3860 INSERT_STRING(db_owner_info, "pnMiddleName",
3861 criteria->personName->pnMiddleName);
3862 INSERT_STRING(db_owner_info, "pnLastName",
3863 criteria->personName->pnLastName);
3864 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
3865 criteria->personName->pnLastNameAtBirth);
3867 if (criteria->birthInfo) {
3868 if (criteria->birthInfo->biDate) {
3869 if (!tm2datestring(criteria->birthInfo->biDate, &string))
3870 INSERT_STRING(db_owner_info, "biDate", string);
3871 free(string); string = NULL;
3873 INSERT_STRING(db_owner_info, "biCity", criteria->birthInfo->biCity);
3874 INSERT_STRING(db_owner_info, "biCounty", criteria->birthInfo->biCounty);
3875 INSERT_STRING(db_owner_info, "biState", criteria->birthInfo->biState);
3877 if (criteria->address) {
3878 INSERT_STRING(db_owner_info, "adCity", criteria->address->adCity);
3879 INSERT_STRING(db_owner_info, "adStreet", criteria->address->adStreet);
3880 INSERT_STRING(db_owner_info, "adNumberInStreet",
3881 criteria->address->adNumberInStreet);
3882 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
3883 criteria->address->adNumberInMunicipality);
3884 INSERT_STRING(db_owner_info, "adZipCode", criteria->address->adZipCode);
3885 INSERT_STRING(db_owner_info, "adState", criteria->address->adState);
3887 INSERT_STRING(db_owner_info, "nationality", criteria->nationality);
3888 INSERT_STRING(db_owner_info, "email", criteria->email);
3889 INSERT_STRING(db_owner_info, "telNumber", criteria->telNumber);
3890 INSERT_STRING(db_owner_info, "identifier", criteria->identifier);
3891 INSERT_STRING(db_owner_info, "registryCode", criteria->registryCode);
3893 INSERT_LONGINT(db_owner_info, "dbState", criteria->dbState, string);
3895 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", criteria->dbEffectiveOVM);
3896 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
3897 criteria->dbOpenAddressing);
3900 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending FindDataBox request to ISDS\n"));
3902 /* Sent request */
3903 err = isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
3905 /* Destroy request */
3906 xmlFreeNode(request); request = NULL;
3908 if (err) {
3909 isds_log(ILF_ISDS, ILL_DEBUG,
3910 _("Processing ISDS response on FindDataBox "
3911 "request failed\n"));
3912 goto leave;
3915 /* Check for response status */
3916 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
3917 &code, &message, NULL);
3918 if (err) {
3919 isds_log(ILF_ISDS, ILL_DEBUG,
3920 _("ISDS response on FindDataBox request is missing status\n"));
3921 goto leave;
3924 /* Request processed, but nothing found */
3925 if (!xmlStrcmp(code, BAD_CAST "0002") ||
3926 !xmlStrcmp(code, BAD_CAST "5001")) {
3927 char *code_locale = utf82locale((char*)code);
3928 char *message_locale = utf82locale((char*)message);
3929 isds_log(ILF_ISDS, ILL_DEBUG,
3930 _("Server did not found any box on FindDataBox request "
3931 "(code=%s, message=%s)\n"), code_locale, message_locale);
3932 isds_log_message(context, message_locale);
3933 free(code_locale);
3934 free(message_locale);
3935 err = IE_NOEXIST;
3936 goto leave;
3939 /* Warning, not a error */
3940 if (!xmlStrcmp(code, BAD_CAST "0003")) {
3941 char *code_locale = utf82locale((char*)code);
3942 char *message_locale = utf82locale((char*)message);
3943 isds_log(ILF_ISDS, ILL_DEBUG,
3944 _("Server truncated response on FindDataBox request "
3945 "(code=%s, message=%s)\n"), code_locale, message_locale);
3946 isds_log_message(context, message_locale);
3947 free(code_locale);
3948 free(message_locale);
3949 truncated = 1;
3952 /* Other error */
3953 else if (xmlStrcmp(code, BAD_CAST "0000")) {
3954 char *code_locale = utf82locale((char*)code);
3955 char *message_locale = utf82locale((char*)message);
3956 isds_log(ILF_ISDS, ILL_DEBUG,
3957 _("Server refused FindDataBox request "
3958 "(code=%s, message=%s)\n"), code_locale, message_locale);
3959 isds_log_message(context, message_locale);
3960 free(code_locale);
3961 free(message_locale);
3962 err = IE_ISDS;
3963 goto leave;
3966 xpath_ctx = xmlXPathNewContext(response);
3967 if (!xpath_ctx) {
3968 err = IE_ERROR;
3969 goto leave;
3971 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
3972 err = IE_ERROR;
3973 goto leave;
3976 /* Extract boxes if they present */
3977 result = xmlXPathEvalExpression(BAD_CAST
3978 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
3979 xpath_ctx);
3980 if (!result) {
3981 err = IE_ERROR;
3982 goto leave;
3984 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3985 struct isds_list *item, *prev_item = NULL;
3986 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
3987 item = calloc(1, sizeof(*item));
3988 if (!item) {
3989 err = IE_NOMEM;
3990 goto leave;
3993 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
3994 if (i == 0) *boxes = item;
3995 else prev_item->next = item;
3996 prev_item = item;
3998 xpath_ctx->node = result->nodesetval->nodeTab[i];
3999 err = extract_DbOwnerInfo(context,
4000 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
4001 if (err) goto leave;
4005 leave:
4006 if (err) {
4007 isds_list_free(boxes);
4008 } else {
4009 if (truncated) err = IE_2BIG;
4012 free(string);
4013 xmlFreeNode(request);
4014 xmlXPathFreeObject(result);
4015 xmlXPathFreeContext(xpath_ctx);
4017 free(code);
4018 free(message);
4019 xmlFreeDoc(response);
4021 if (!err)
4022 isds_log(ILF_ISDS, ILL_DEBUG,
4023 _("FindDataBox request processed by server successfully.\n"));
4025 return err;
4029 /* Get status of a box.
4030 * @context is ISDS session context.
4031 * @box_id is UTF-8 encoded box identifier as zero terminated string
4032 * @box_status is return value of box status.
4033 * @return:
4034 * IE_SUCCESS if box has been found and its status retrieved
4035 * IE_NOEXIST if box is not known to ISDS server
4036 * or other appropriate error.
4037 * You can use isds_DbState to enumerate box status. However out of enum
4038 * range value can be returned too. This is feature because ISDS
4039 * specification leaves the set of values open.
4040 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
4041 * the box has been deleted, but ISDS still lists its former existence. */
4042 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
4043 long int *box_status) {
4044 isds_error err = IE_SUCCESS;
4045 xmlNsPtr isds_ns = NULL;
4046 xmlNodePtr request = NULL, db_id;
4047 xmlDocPtr response = NULL;
4048 xmlChar *code = NULL, *message = NULL;
4049 xmlXPathContextPtr xpath_ctx = NULL;
4050 xmlXPathObjectPtr result = NULL;
4051 xmlChar *string = NULL;
4053 if (!context) return IE_INVALID_CONTEXT;
4054 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
4056 /* Check if connection is established
4057 * TODO: This check should be done donwstairs. */
4058 if (!context->curl) return IE_CONNECTION_CLOSED;
4061 /* Build CheckDataBox request */
4062 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
4063 if (!request) {
4064 isds_log_message(context,
4065 _("Could build CheckDataBox request"));
4066 return IE_ERROR;
4068 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4069 if(!isds_ns) {
4070 isds_log_message(context, _("Could not create ISDS name space"));
4071 xmlFreeNode(request);
4072 return IE_ERROR;
4074 xmlSetNs(request, isds_ns);
4075 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
4076 if (!db_id) {
4077 isds_log_message(context, _("Could not add dbId Child to "
4078 "CheckDataBox element"));
4079 xmlFreeNode(request);
4080 return IE_ERROR;
4084 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CheckDataBox request to ISDS\n"));
4086 /* Sent request */
4087 err = isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
4089 /* Destroy request */
4090 xmlFreeNode(request);
4092 if (err) {
4093 isds_log(ILF_ISDS, ILL_DEBUG,
4094 _("Processing ISDS response on CheckDataBox "
4095 "request failed\n"));
4096 goto leave;
4099 /* Check for response status */
4100 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
4101 &code, &message, NULL);
4102 if (err) {
4103 isds_log(ILF_ISDS, ILL_DEBUG,
4104 _("ISDS response on CheckDataBox request is missing status\n"));
4105 goto leave;
4108 /* Request processed, but nothing found */
4109 if (!xmlStrcmp(code, BAD_CAST "5001")) {
4110 char *box_id_locale = utf82locale((char*)box_id);
4111 char *code_locale = utf82locale((char*)code);
4112 char *message_locale = utf82locale((char*)message);
4113 isds_log(ILF_ISDS, ILL_DEBUG,
4114 _("Server did not found box %s on CheckDataBox request "
4115 "(code=%s, message=%s)\n"),
4116 box_id_locale, code_locale, message_locale);
4117 isds_log_message(context, message_locale);
4118 free(box_id_locale);
4119 free(code_locale);
4120 free(message_locale);
4121 err = IE_NOEXIST;
4122 goto leave;
4125 /* Other error */
4126 else if (xmlStrcmp(code, BAD_CAST "0000")) {
4127 char *code_locale = utf82locale((char*)code);
4128 char *message_locale = utf82locale((char*)message);
4129 isds_log(ILF_ISDS, ILL_DEBUG,
4130 _("Server refused CheckDataBox request "
4131 "(code=%s, message=%s)\n"), code_locale, message_locale);
4132 isds_log_message(context, message_locale);
4133 free(code_locale);
4134 free(message_locale);
4135 err = IE_ISDS;
4136 goto leave;
4139 /* Extract data */
4140 xpath_ctx = xmlXPathNewContext(response);
4141 if (!xpath_ctx) {
4142 err = IE_ERROR;
4143 goto leave;
4145 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4146 err = IE_ERROR;
4147 goto leave;
4149 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
4150 xpath_ctx);
4151 if (!result) {
4152 err = IE_ERROR;
4153 goto leave;
4155 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4156 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
4157 err = IE_ISDS;
4158 goto leave;
4160 if (result->nodesetval->nodeNr > 1) {
4161 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
4162 err = IE_ISDS;
4163 goto leave;
4165 xpath_ctx->node = result->nodesetval->nodeTab[0];
4166 xmlXPathFreeObject(result); result = NULL;
4168 EXTRACT_LONGINT("isds:dbState", box_status, 1);
4171 leave:
4172 free(string);
4173 xmlXPathFreeObject(result);
4174 xmlXPathFreeContext(xpath_ctx);
4176 free(code);
4177 free(message);
4178 xmlFreeDoc(response);
4180 if (!err)
4181 isds_log(ILF_ISDS, ILL_DEBUG,
4182 _("CheckDataBox request processed by server successfully.\n"));
4184 return err;
4188 /* Insert struct isds_message data (envelope (recipient data optional) and
4189 * documents) into XML tree
4190 * @context is sesstion context
4191 * @outgoing_message is libsids structure with message data
4192 * @create_message is XML CreateMessage or CreateMultipleMessage element
4193 * @process_recipient true for recipient data serialization, false for no
4194 * serialization */
4195 static isds_error insert_envelope_files(struct isds_ctx *context,
4196 const struct isds_message *outgoing_message, xmlNodePtr create_message,
4197 const _Bool process_recipient) {
4199 isds_error err = IE_SUCCESS;
4200 xmlNodePtr envelope, dm_files, node;
4201 xmlChar *string = NULL;
4203 if (!context) return IE_INVALID_CONTEXT;
4204 if (!outgoing_message || !create_message) return IE_INVAL;
4207 /* Build envelope */
4208 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
4209 if (!envelope) {
4210 isds_printf_message(context, _("Could not add dmEnvelope child to "
4211 "%s element"), create_message->name);
4212 return IE_ERROR;
4215 if (!outgoing_message->envelope) {
4216 isds_log_message(context, _("Outgoing message is missing envelope"));
4217 err = IE_INVAL;
4218 goto leave;
4221 INSERT_STRING(envelope, "dmSenderOrgUnit",
4222 outgoing_message->envelope->dmSenderOrgUnit);
4223 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
4224 outgoing_message->envelope->dmSenderOrgUnitNum, string);
4226 if (process_recipient) {
4227 if (!outgoing_message->envelope->dbIDRecipient) {
4228 isds_log_message(context,
4229 _("Outgoing message is missing recipient box identifier"));
4230 err = IE_INVAL;
4231 goto leave;
4233 INSERT_STRING(envelope, "dbIDRecipient",
4234 outgoing_message->envelope->dbIDRecipient);
4236 INSERT_STRING(envelope, "dmRecipientOrgUnit",
4237 outgoing_message->envelope->dmRecipientOrgUnit);
4238 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
4239 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
4240 INSERT_STRING(envelope, "dmToHands",
4241 outgoing_message->envelope->dmToHands);
4244 #define CHECK_FOR_STRING_LENGTH(string, limit, name) \
4245 if ((string) && xmlUTF8Strlen((xmlChar *) (string)) > (limit)) { \
4246 isds_printf_message(context, \
4247 _("%s has more than %d characters"), (name), (limit)); \
4248 err = IE_2BIG; \
4249 goto leave; \
4252 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 255,
4253 "dmAnnotation");
4254 INSERT_STRING(envelope, "dmAnnotation",
4255 outgoing_message->envelope->dmAnnotation);
4257 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
4258 50, "dmRecipientRefNumber");
4259 INSERT_STRING(envelope, "dmRecipientRefNumber",
4260 outgoing_message->envelope->dmRecipientRefNumber);
4262 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
4263 50, "dmSenderRefNumber");
4264 INSERT_STRING(envelope, "dmSenderRefNumber",
4265 outgoing_message->envelope->dmSenderRefNumber);
4267 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
4268 50, "dmRecipientIdent");
4269 INSERT_STRING(envelope, "dmRecipientIdent",
4270 outgoing_message->envelope->dmRecipientIdent);
4272 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
4273 50, "dmSenderIdent");
4274 INSERT_STRING(envelope, "dmSenderIdent",
4275 outgoing_message->envelope->dmSenderIdent);
4277 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
4278 outgoing_message->envelope->dmLegalTitleLaw, string);
4279 INSERT_LONGINT(envelope, "dmLegalTitleYear",
4280 outgoing_message->envelope->dmLegalTitleYear, string);
4281 INSERT_STRING(envelope, "dmLegalTitleSect",
4282 outgoing_message->envelope->dmLegalTitleSect);
4283 INSERT_STRING(envelope, "dmLegalTitlePar",
4284 outgoing_message->envelope->dmLegalTitlePar);
4285 INSERT_STRING(envelope, "dmLegalTitlePoint",
4286 outgoing_message->envelope->dmLegalTitlePoint);
4288 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
4289 outgoing_message->envelope->dmPersonalDelivery);
4290 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
4291 outgoing_message->envelope->dmAllowSubstDelivery);
4293 #undef CHECK_FOR_STRING_LENGTH
4295 /* ???: Should we require value for dbEffectiveOVM sender?
4296 * ISDS has default as true */
4297 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
4300 /* Append dmFiles */
4301 if (!outgoing_message->documents) {
4302 isds_log_message(context,
4303 _("Outgoing message is missing list of documents"));
4304 err = IE_INVAL;
4305 goto leave;
4307 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
4308 if (!dm_files) {
4309 isds_printf_message(context, _("Could not add dmFiles child to "
4310 "%s element"), create_message->name);
4311 err = IE_ERROR;
4312 goto leave;
4315 /* Check for document hieararchy */
4316 err = check_documents_hierarchy(context, outgoing_message->documents);
4317 if (err) goto leave;
4319 /* Process each document */
4320 for (struct isds_list *item =
4321 (struct isds_list *) outgoing_message->documents;
4322 item; item = item->next) {
4323 if (!item->data) {
4324 isds_log_message(context,
4325 _("List of documents contains empty item"));
4326 err = IE_INVAL;
4327 goto leave;
4329 /* FIXME: Check for dmFileMetaType and for document references.
4330 * Only first document can be of MAIN type */
4331 err = insert_document(context, (struct isds_document*) item->data,
4332 dm_files);
4334 if (err) goto leave;
4337 leave:
4338 free(string);
4339 return err;
4343 /* Send a message via ISDS to a recipent
4344 * @context is session context
4345 * @outgoing_message is message to send; Some memebers are mandatory (like
4346 * dbIDRecipient), some are optional and some are irrelevant (especialy data
4347 * about sender). Included pointer to isds_list documents must contain at
4348 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
4349 * members will be filled with valid data from ISDS. Exact list of write
4350 * members is subject to change. Currently dmId is changed.
4351 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
4352 isds_error isds_send_message(struct isds_ctx *context,
4353 struct isds_message *outgoing_message) {
4355 isds_error err = IE_SUCCESS;
4356 xmlNsPtr isds_ns = NULL;
4357 xmlNodePtr request = NULL;
4358 xmlDocPtr response = NULL;
4359 xmlChar *code = NULL, *message = NULL;
4360 xmlXPathContextPtr xpath_ctx = NULL;
4361 xmlXPathObjectPtr result = NULL;
4362 _Bool message_is_complete = 0;
4364 if (!context) return IE_INVALID_CONTEXT;
4365 if (!outgoing_message) return IE_INVAL;
4367 /* Check if connection is established
4368 * TODO: This check should be done donwstairs. */
4369 if (!context->curl) return IE_CONNECTION_CLOSED;
4372 /* Build CreateMessage request */
4373 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
4374 if (!request) {
4375 isds_log_message(context,
4376 _("Could build CreateMessage request"));
4377 return IE_ERROR;
4379 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4380 if(!isds_ns) {
4381 isds_log_message(context, _("Could not create ISDS name space"));
4382 xmlFreeNode(request);
4383 return IE_ERROR;
4385 xmlSetNs(request, isds_ns);
4387 /* Append envelope and files */
4388 err = insert_envelope_files(context, outgoing_message, request, 1);
4389 if (err) goto leave;
4392 /* Signal we can serilize message since now */
4393 message_is_complete = 1;
4396 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
4398 /* Sent request */
4399 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
4401 /* Dont' destroy request, we want to provide it to application later */
4403 if (err) {
4404 isds_log(ILF_ISDS, ILL_DEBUG,
4405 _("Processing ISDS response on CreateMessage "
4406 "request failed\n"));
4407 goto leave;
4410 /* Check for response status */
4411 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
4412 &code, &message, NULL);
4413 if (err) {
4414 isds_log(ILF_ISDS, ILL_DEBUG,
4415 _("ISDS response on CreateMessage request "
4416 "is missing status\n"));
4417 goto leave;
4420 /* Request processed, but refused by server or server failed */
4421 if (xmlStrcmp(code, BAD_CAST "0000")) {
4422 char *box_id_locale =
4423 utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
4424 char *code_locale = utf82locale((char*)code);
4425 char *message_locale = utf82locale((char*)message);
4426 isds_log(ILF_ISDS, ILL_DEBUG,
4427 _("Server did not accept message for %s on CreateMessage "
4428 "request (code=%s, message=%s)\n"),
4429 box_id_locale, code_locale, message_locale);
4430 isds_log_message(context, message_locale);
4431 free(box_id_locale);
4432 free(code_locale);
4433 free(message_locale);
4434 err = IE_ISDS;
4435 goto leave;
4439 /* Extract data */
4440 xpath_ctx = xmlXPathNewContext(response);
4441 if (!xpath_ctx) {
4442 err = IE_ERROR;
4443 goto leave;
4445 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4446 err = IE_ERROR;
4447 goto leave;
4449 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
4450 xpath_ctx);
4451 if (!result) {
4452 err = IE_ERROR;
4453 goto leave;
4455 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4456 isds_log_message(context, _("Missing CreateMessageResponse element"));
4457 err = IE_ISDS;
4458 goto leave;
4460 if (result->nodesetval->nodeNr > 1) {
4461 isds_log_message(context, _("Multiple CreateMessageResponse element"));
4462 err = IE_ISDS;
4463 goto leave;
4465 xpath_ctx->node = result->nodesetval->nodeTab[0];
4466 xmlXPathFreeObject(result); result = NULL;
4468 if (outgoing_message->envelope->dmID) {
4469 free(outgoing_message->envelope->dmID);
4470 outgoing_message->envelope->dmID = NULL;
4472 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
4473 if (!outgoing_message->envelope->dmID) {
4474 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
4475 "but did not returen assigned message ID\n"));
4478 leave:
4479 /* TODO: Serialize message into structure member raw */
4480 /* XXX: Each web service transport message in different format.
4481 * Therefore it's not possible to save them directly.
4482 * To save them, one must figure out common format.
4483 * We can leave it on application, or we can implement the ESS format. */
4484 /*if (message_is_complete) {
4485 if (outgoing_message->envelope->dmID) {
4487 /* Add assigned message ID as first child*/
4488 /*xmlNodePtr dmid_text = xmlNewText(
4489 (xmlChar *) outgoing_message->envelope->dmID);
4490 if (!dmid_text) goto serialization_failed;
4492 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
4493 BAD_CAST "dmID");
4494 if (!dmid_element) {
4495 xmlFreeNode(dmid_text);
4496 goto serialization_failed;
4499 xmlNodePtr dmid_element_with_text =
4500 xmlAddChild(dmid_element, dmid_text);
4501 if (!dmid_element_with_text) {
4502 xmlFreeNode(dmid_element);
4503 xmlFreeNode(dmid_text);
4504 goto serialization_failed;
4507 node = xmlAddPrevSibling(envelope->childern,
4508 dmid_element_with_text);
4509 if (!node) {
4510 xmlFreeNodeList(dmid_element_with_text);
4511 goto serialization_failed;
4515 /* Serialize message with ID into raw */
4516 /*buffer = serialize_element(envelope)*/
4517 /* }
4519 serialization_failed:
4523 /* Clean up */
4524 xmlXPathFreeObject(result);
4525 xmlXPathFreeContext(xpath_ctx);
4527 free(code);
4528 free(message);
4529 xmlFreeDoc(response);
4530 xmlFreeNode(request);
4532 if (!err)
4533 isds_log(ILF_ISDS, ILL_DEBUG,
4534 _("CreateMessage request processed by server "
4535 "successfully.\n"));
4537 return err;
4541 /* Send a message via ISDS to a multiple recipents
4542 * @context is session context
4543 * @outgoing_message is message to send; Some memebers are mandatory,
4544 * some are optional and some are irrelevant (especialy data
4545 * about sender). Data about recipient will be substituted by ISDS from
4546 * @copies. Included pointer to isds_list documents must
4547 * contain at least one document of FILEMETATYPE_MAIN.
4548 * @copies is list of isds_message_copy structures addressing all desired
4549 * recipients. This is read-write structure, some members will be filled with
4550 * valid data from ISDS (message IDs, error codes, error descriptions).
4551 * @return
4552 * ISDS_SUCCESS if all messages have been sent
4553 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
4554 * succesed messages can be identified by copies->data->error),
4555 * or other error code if something other goes wrong. */
4556 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
4557 const struct isds_message *outgoing_message,
4558 struct isds_list *copies) {
4560 isds_error err = IE_SUCCESS, append_err;
4561 xmlNsPtr isds_ns = NULL;
4562 xmlNodePtr request = NULL, recipients, recipient, node;
4563 struct isds_list *item;
4564 struct isds_message_copy *copy;
4565 xmlDocPtr response = NULL;
4566 xmlChar *code = NULL, *message = NULL;
4567 xmlXPathContextPtr xpath_ctx = NULL;
4568 xmlXPathObjectPtr result = NULL;
4569 xmlChar *string = NULL;
4570 int i;
4572 if (!context) return IE_INVALID_CONTEXT;
4573 if (!outgoing_message || !copies) return IE_INVAL;
4575 /* Check if connection is established
4576 * TODO: This check should be done donwstairs. */
4577 if (!context->curl) return IE_CONNECTION_CLOSED;
4580 /* Build CreateMultipleMessage request */
4581 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
4582 if (!request) {
4583 isds_log_message(context,
4584 _("Could build CreateMultipleMessage request"));
4585 return IE_ERROR;
4587 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4588 if(!isds_ns) {
4589 isds_log_message(context, _("Could not create ISDS name space"));
4590 xmlFreeNode(request);
4591 return IE_ERROR;
4593 xmlSetNs(request, isds_ns);
4596 /* Build recipients */
4597 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
4598 if (!recipients) {
4599 isds_log_message(context, _("Could not add dmRecipients child to "
4600 "CreateMultipleMessage element"));
4601 xmlFreeNode(request);
4602 return IE_ERROR;
4605 /* Insert each recipient */
4606 for (item = copies; item; item = item->next) {
4607 copy = (struct isds_message_copy *) item->data;
4608 if (!copy) {
4609 isds_log_message(context,
4610 _("copies list item contains empty data"));
4611 err = IE_INVAL;
4612 goto leave;
4615 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
4616 if (!recipient) {
4617 isds_log_message(context, _("Could not add dmRecipient child to "
4618 "dmRecipient element"));
4619 err = IE_ERROR;
4620 goto leave;
4623 if (!copy->dbIDRecipient) {
4624 isds_log_message(context,
4625 _("Message copy is missing recipient box identifier"));
4626 err = IE_INVAL;
4627 goto leave;
4629 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
4630 INSERT_STRING(recipient, "dmRecipientOrgUnit",
4631 copy->dmRecipientOrgUnit);
4632 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
4633 copy->dmRecipientOrgUnitNum, string);
4634 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
4637 /* Append envelope and files */
4638 err = insert_envelope_files(context, outgoing_message, request, 0);
4639 if (err) goto leave;
4642 isds_log(ILF_ISDS, ILL_DEBUG,
4643 _("Sending CreateMultipleMessage request to ISDS\n"));
4645 /* Sent request */
4646 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
4647 if (err) {
4648 isds_log(ILF_ISDS, ILL_DEBUG,
4649 _("Processing ISDS response on CreateMultipleMessage "
4650 "request failed\n"));
4651 goto leave;
4654 /* Check for response status */
4655 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
4656 &code, &message, NULL);
4657 if (err) {
4658 isds_log(ILF_ISDS, ILL_DEBUG,
4659 _("ISDS response on CreateMultipleMessage request "
4660 "is missing status\n"));
4661 goto leave;
4664 /* Request processed, but some copies failed */
4665 if (!xmlStrcmp(code, BAD_CAST "0004")) {
4666 char *box_id_locale =
4667 utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
4668 char *code_locale = utf82locale((char*)code);
4669 char *message_locale = utf82locale((char*)message);
4670 isds_log(ILF_ISDS, ILL_DEBUG,
4671 _("Server did accept message for multiple recipients "
4672 "on CreateMultipleMessage request but delivery to "
4673 "some of them failed (code=%s, message=%s)\n"),
4674 box_id_locale, code_locale, message_locale);
4675 isds_log_message(context, message_locale);
4676 free(box_id_locale);
4677 free(code_locale);
4678 free(message_locale);
4679 err = IE_PARTIAL_SUCCESS;
4682 /* Request refused by server as whole */
4683 else if (xmlStrcmp(code, BAD_CAST "0000")) {
4684 char *box_id_locale =
4685 utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
4686 char *code_locale = utf82locale((char*)code);
4687 char *message_locale = utf82locale((char*)message);
4688 isds_log(ILF_ISDS, ILL_DEBUG,
4689 _("Server did not accept message for multiple recipients "
4690 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
4691 box_id_locale, code_locale, message_locale);
4692 isds_log_message(context, message_locale);
4693 free(box_id_locale);
4694 free(code_locale);
4695 free(message_locale);
4696 err = IE_ISDS;
4697 goto leave;
4701 /* Extract data */
4702 xpath_ctx = xmlXPathNewContext(response);
4703 if (!xpath_ctx) {
4704 err = IE_ERROR;
4705 goto leave;
4707 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4708 err = IE_ERROR;
4709 goto leave;
4711 result = xmlXPathEvalExpression(
4712 BAD_CAST "/isds:CreateMultipleMessageResponse"
4713 "/isds:dmMultipleStatus/isds:dmSingleStatus",
4714 xpath_ctx);
4715 if (!result) {
4716 err = IE_ERROR;
4717 goto leave;
4719 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4720 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
4721 err = IE_ISDS;
4722 goto leave;
4725 /* Extract message ID and delivery status for each copy */
4726 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
4727 item = item->next, i++) {
4728 copy = (struct isds_message_copy *) item->data;
4729 xpath_ctx->node = result->nodesetval->nodeTab[i];
4731 append_err = append_TMStatus(context, copy, xpath_ctx);
4732 if (append_err) {
4733 err = append_err;
4734 goto leave;
4737 if (item || i < result->nodesetval->nodeNr) {
4738 isds_printf_message(context, _("ISDS returned unexpected number of "
4739 "message copy delivery states: %d"),
4740 result->nodesetval->nodeNr);
4741 err = IE_ISDS;
4742 goto leave;
4746 leave:
4747 /* Clean up */
4748 free(string);
4749 xmlXPathFreeObject(result);
4750 xmlXPathFreeContext(xpath_ctx);
4752 free(code);
4753 free(message);
4754 xmlFreeDoc(response);
4755 xmlFreeNode(request);
4757 if (!err)
4758 isds_log(ILF_ISDS, ILL_DEBUG,
4759 _("CreateMultipleMessageResponse request processed by server "
4760 "successfully.\n"));
4762 return err;
4766 /* Get list of messages. This is common core for getting sent or received
4767 * messaeges.
4768 * Any criterion argument can be NULL, if you don't care about it.
4769 * @context is session context. Must not be NULL.
4770 * @outgoing_direction is true if you want list of outgoing messages,
4771 * it's false if you want incoming messages.
4772 * @from_time is minimal time and date of message sending inclusive.
4773 * @to_time is maximal time and date of message sending inclusive
4774 * @organization_unit_number is number of sender/recipient respectively.
4775 * @status_filter is bit field of isds_message_status values. Use special
4776 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
4777 * all values, you can use bitwise arithmetic if you want.)
4778 * @offset is index of first message we are interested in. First message is 1.
4779 * Set to 0 (or 1) if you don't care.
4780 * @number is maximal length of list you want to get as input value, outputs
4781 * number of messages matching these criteria. Can be NULL if you don't care
4782 * (applies to output value either).
4783 * @messages is automatically reallocated list of isds_message's. Be ware that
4784 * it returns only brief overview (envelope and some other fields) about each
4785 * message, not the complete message. FIXME: Specify exact fields.
4786 * The list is sorted by delivery time in ascending order.
4787 * Use NULL if
4788 * you don't care about don't need the data (useful if you want to know only
4789 * the @number). If you provide &NULL, list will be allocated on heap, if you
4790 * provide pointer to non-NULL, list will be freed automacally at first. Also
4791 * in case of error the list will be NULLed.
4792 * @return IE_SUCCESS or appropriate error code. */
4793 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
4794 _Bool outgoing_direction,
4795 const struct timeval *from_time, const struct timeval *to_time,
4796 const long int *organization_unit_number,
4797 const unsigned int status_filter,
4798 const unsigned long int offset, unsigned long int *number,
4799 struct isds_list **messages) {
4801 isds_error err = IE_SUCCESS;
4802 xmlNsPtr isds_ns = NULL;
4803 xmlNodePtr request = NULL, node;
4804 xmlDocPtr response = NULL;
4805 xmlChar *code = NULL, *message = NULL;
4806 xmlXPathContextPtr xpath_ctx = NULL;
4807 xmlXPathObjectPtr result = NULL;
4808 xmlChar *string = NULL;
4809 long unsigned int count = 0;
4811 if (!context) return IE_INVALID_CONTEXT;
4813 /* Free former message list if any */
4814 if (messages) isds_list_free(messages);
4816 /* Check if connection is established
4817 * TODO: This check should be done donwstairs. */
4818 if (!context->curl) return IE_CONNECTION_CLOSED;
4820 /* Build GetListOf*Messages request */
4821 request = xmlNewNode(NULL,
4822 (outgoing_direction) ?
4823 BAD_CAST "GetListOfSentMessages" :
4824 BAD_CAST "GetListOfReceivedMessages"
4826 if (!request) {
4827 isds_log_message(context,
4828 (outgoing_direction) ?
4829 _("Could not build GetListOfSentMessages request") :
4830 _("Could not build GetListOfReceivedMessages request")
4832 return IE_ERROR;
4834 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4835 if(!isds_ns) {
4836 isds_log_message(context, _("Could not create ISDS name space"));
4837 xmlFreeNode(request);
4838 return IE_ERROR;
4840 xmlSetNs(request, isds_ns);
4843 if (from_time) {
4844 err = timeval2timestring(from_time, &string);
4845 if (err) goto leave;
4847 INSERT_STRING(request, "dmFromTime", string);
4848 free(string); string = NULL;
4850 if (to_time) {
4851 err = timeval2timestring(to_time, &string);
4852 if (err) goto leave;
4854 INSERT_STRING(request, "dmToTime", string);
4855 free(string); string = NULL;
4857 if (outgoing_direction) {
4858 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
4859 organization_unit_number, string);
4860 } else {
4861 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
4862 organization_unit_number, string);
4865 if (status_filter > MESSAGESTATE_ANY) {
4866 isds_printf_message(context,
4867 _("Invalid message state filter value: %ld"), status_filter);
4868 err = IE_INVAL;
4869 goto leave;
4871 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
4873 if (offset > 0 ) {
4874 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
4875 } else {
4876 INSERT_STRING(request, "dmOffset", "1");
4879 /* number 0 means no limit */
4880 if (number && *number == 0) {
4881 INSERT_STRING(request, "dmLimit", NULL);
4882 } else {
4883 INSERT_ULONGINT(request, "dmLimit", number, string);
4887 isds_log(ILF_ISDS, ILL_DEBUG,
4888 (outgoing_direction) ?
4889 _("Sending GetListOfSentMessages request to ISDS\n") :
4890 _("Sending GetListOfReceivedMessages request to ISDS\n")
4893 /* Sent request */
4894 err = isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
4895 xmlFreeNode(request); request = NULL;
4897 if (err) {
4898 isds_log(ILF_ISDS, ILL_DEBUG,
4899 (outgoing_direction) ?
4900 _("Processing ISDS response on GetListOfSentMessages "
4901 "request failed\n") :
4902 _("Processing ISDS response on GetListOfReceivedMessages "
4903 "request failed\n")
4905 goto leave;
4908 /* Check for response status */
4909 err = isds_response_status(context, SERVICE_DM_INFO, response,
4910 &code, &message, NULL);
4911 if (err) {
4912 isds_log(ILF_ISDS, ILL_DEBUG,
4913 (outgoing_direction) ?
4914 _("ISDS response on GetListOfSentMessages request "
4915 "is missing status\n") :
4916 _("ISDS response on GetListOfReceivedMessages request "
4917 "is missing status\n")
4919 goto leave;
4922 /* Request processed, but nothing found */
4923 if (xmlStrcmp(code, BAD_CAST "0000")) {
4924 char *code_locale = utf82locale((char*)code);
4925 char *message_locale = utf82locale((char*)message);
4926 isds_log(ILF_ISDS, ILL_DEBUG,
4927 (outgoing_direction) ?
4928 _("Server refused GetListOfSentMessages request "
4929 "(code=%s, message=%s)\n") :
4930 _("Server refused GetListOfReceivedMessages request "
4931 "(code=%s, message=%s)\n"),
4932 code_locale, message_locale);
4933 isds_log_message(context, message_locale);
4934 free(code_locale);
4935 free(message_locale);
4936 err = IE_ISDS;
4937 goto leave;
4941 /* Extract data */
4942 xpath_ctx = xmlXPathNewContext(response);
4943 if (!xpath_ctx) {
4944 err = IE_ERROR;
4945 goto leave;
4947 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4948 err = IE_ERROR;
4949 goto leave;
4951 result = xmlXPathEvalExpression(
4952 (outgoing_direction) ?
4953 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
4954 "isds:dmRecords/isds:dmRecord" :
4955 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
4956 "isds:dmRecords/isds:dmRecord",
4957 xpath_ctx);
4958 if (!result) {
4959 err = IE_ERROR;
4960 goto leave;
4963 /* Fill output arguments in */
4964 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4965 struct isds_envelope *envelope;
4966 struct isds_list *item = NULL, *last_item = NULL;
4968 for (count = 0; count < result->nodesetval->nodeNr; count++) {
4969 /* Create new message */
4970 item = calloc(1, sizeof(*item));
4971 if (!item) {
4972 err = IE_NOMEM;
4973 goto leave;
4975 item->destructor = (void(*)(void**)) &isds_message_free;
4976 item->data = calloc(1, sizeof(struct isds_message));
4977 if (!item->data) {
4978 isds_list_free(&item);
4979 err = IE_NOMEM;
4980 goto leave;
4983 /* Extract envelope data */
4984 xpath_ctx->node = result->nodesetval->nodeTab[count];
4985 envelope = NULL;
4986 err = extract_DmRecord(context, &envelope, xpath_ctx);
4987 if (err) {
4988 isds_list_free(&item);
4989 goto leave;
4992 /* Attach extracted envelope */
4993 ((struct isds_message *) item->data)->envelope = envelope;
4995 /* Append new message into the list */
4996 if (!*messages) {
4997 *messages = last_item = item;
4998 } else {
4999 last_item->next = item;
5000 last_item = item;
5004 if (number) *number = count;
5006 leave:
5007 if (err) {
5008 isds_list_free(messages);
5011 free(string);
5012 xmlXPathFreeObject(result);
5013 xmlXPathFreeContext(xpath_ctx);
5015 free(code);
5016 free(message);
5017 xmlFreeDoc(response);
5018 xmlFreeNode(request);
5020 if (!err)
5021 isds_log(ILF_ISDS, ILL_DEBUG,
5022 (outgoing_direction) ?
5023 _("GetListOfSentMessages request processed by server "
5024 "successfully.\n") :
5025 _("GetListOfReceivedMessages request processed by server "
5026 "successfully.\n")
5028 return err;
5032 /* Get list of outgoing (already sent) messages.
5033 * Any criterion argument can be NULL, if you don't care about it.
5034 * @context is session context. Must not be NULL.
5035 * @from_time is minimal time and date of message sending inclusive.
5036 * @to_time is maximal time and date of message sending inclusive
5037 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
5038 * @status_filter is bit field of isds_message_status values. Use special
5039 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
5040 * all values, you can use bitwise arithmetic if you want.)
5041 * @offset is index of first message we are interested in. First message is 1.
5042 * Set to 0 (or 1) if you don't care.
5043 * @number is maximal length of list you want to get as input value, outputs
5044 * number of messages matching these criteria. Can be NULL if you don't care
5045 * (applies to output value either).
5046 * @messages is automatically reallocated list of isds_message's. Be ware that
5047 * it returns only brief overview (envelope and some other fields) about each
5048 * message, not the complete message. FIXME: Specify exact fields.
5049 * The list is sorted by delivery time in ascending order.
5050 * Use NULL if you don't care about the metadata (useful if you want to know
5051 * only the @number). If you provide &NULL, list will be allocated on heap,
5052 * if you provide pointer to non-NULL, list will be freed automacally at first.
5053 * Also in case of error the list will be NULLed.
5054 * @return IE_SUCCESS or appropriate error code. */
5055 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
5056 const struct timeval *from_time, const struct timeval *to_time,
5057 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
5058 const unsigned long int offset, unsigned long int *number,
5059 struct isds_list **messages) {
5061 return isds_get_list_of_messages(
5062 context, 1,
5063 from_time, to_time, dmSenderOrgUnitNum, status_filter,
5064 offset, number,
5065 messages);
5069 /* Get list of incoming (addressed to you) messages.
5070 * Any criterion argument can be NULL, if you don't care about it.
5071 * @context is session context. Must not be NULL.
5072 * @from_time is minimal time and date of message sending inclusive.
5073 * @to_time is maximal time and date of message sending inclusive
5074 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
5075 * @status_filter is bit field of isds_message_status values. Use special
5076 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
5077 * all values, you can use bitwise arithmetic if you want.)
5078 * @offset is index of first message we are interested in. First message is 1.
5079 * Set to 0 (or 1) if you don't care.
5080 * @number is maximal length of list you want to get as input value, outputs
5081 * number of messages matching these criteria. Can be NULL if you don't care
5082 * (applies to output value either).
5083 * @messages is automatically reallocated list of isds_message's. Be ware that
5084 * it returns only brief overview (envelope and some other fields) about each
5085 * message, not the complete message. FIXME: Specify exact fields.
5086 * Use NULL if you don't care about the metadata (useful if you want to know
5087 * only the @number). If you provide &NULL, list will be allocated on heap,
5088 * if you provide pointer to non-NULL, list will be freed automacally at first.
5089 * Also in case of error the list will be NULLed.
5090 * @return IE_SUCCESS or appropriate error code. */
5091 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
5092 const struct timeval *from_time, const struct timeval *to_time,
5093 const long int *dmRecipientOrgUnitNum,
5094 const unsigned int status_filter,
5095 const unsigned long int offset, unsigned long int *number,
5096 struct isds_list **messages) {
5098 return isds_get_list_of_messages(
5099 context, 0,
5100 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
5101 offset, number,
5102 messages);
5106 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
5107 * code
5108 * @context is session context
5109 * @service is ISDS WS service handler
5110 * @service_name is name of SERVICE_DM_OPERATIONS
5111 * @message_id is message ID to send as service argument to ISDS
5112 * @response is server SOAP body response as XML document
5113 * @raw_response is automatically reallocated bitstream with response body. Use
5114 * NULL if you don't care
5115 * @raw_response_length is size of @raw_response in bytes
5116 * @code is ISDS status code
5117 * @status_message is ISDS status message
5118 * @return error coded from lower layer, context message will be set up
5119 * appropriately. */
5120 static isds_error build_send_check_message_request(struct isds_ctx *context,
5121 const isds_service service, const xmlChar *service_name,
5122 const char *message_id,
5123 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
5124 xmlChar **code, xmlChar **status_message) {
5126 isds_error err = IE_SUCCESS;
5127 char *service_name_locale = NULL, *message_id_locale = NULL;
5128 xmlNodePtr request = NULL, node;
5129 xmlNsPtr isds_ns = NULL;
5131 if (!context) return IE_INVALID_CONTEXT;
5132 if (!service_name || !message_id) return IE_INVAL;
5133 if (!response || !code || !status_message) return IE_INVAL;
5134 if (!raw_response_length && raw_response) return IE_INVAL;
5136 /* Free output argument */
5137 xmlFreeDoc(*response); *response = NULL;
5138 if (raw_response) zfree(*raw_response);
5139 free(*code);
5140 free(*status_message);
5143 /* Check if connection is established
5144 * TODO: This check should be done donwstairs. */
5145 if (!context->curl) return IE_CONNECTION_CLOSED;
5147 service_name_locale = utf82locale((char*)service_name);
5148 message_id_locale = utf82locale(message_id);
5149 if (!service_name_locale || !message_id_locale) {
5150 err = IE_NOMEM;
5151 goto leave;
5154 /* Build request */
5155 request = xmlNewNode(NULL, service_name);
5156 if (!request) {
5157 isds_printf_message(context,
5158 _("Could not build %s request"), service_name_locale);
5159 err = IE_ERROR;
5160 goto leave;
5162 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5163 if(!isds_ns) {
5164 isds_log_message(context, _("Could not create ISDS name space"));
5165 err = IE_ERROR;
5166 goto leave;
5168 xmlSetNs(request, isds_ns);
5171 /* Add requested ID */
5172 err = validate_message_id_length(context, (xmlChar *) message_id);
5173 if (err) goto leave;
5174 INSERT_STRING(request, "dmID", message_id);
5177 isds_log(ILF_ISDS, ILL_DEBUG,
5178 _("Sending %s request for %s message ID to ISDS\n"),
5179 service_name_locale, message_id_locale);
5181 /* Send request */
5182 err = isds(context, service, request, response,
5183 raw_response, raw_response_length);
5184 xmlFreeNode(request); request = NULL;
5186 if (err) {
5187 isds_log(ILF_ISDS, ILL_DEBUG,
5188 _("Processing ISDS response on %s request failed\n"),
5189 service_name_locale);
5190 goto leave;
5193 /* Check for response status */
5194 err = isds_response_status(context, service, *response,
5195 code, status_message, NULL);
5196 if (err) {
5197 isds_log(ILF_ISDS, ILL_DEBUG,
5198 _("ISDS response on %s request is missing status\n"),
5199 service_name_locale);
5200 goto leave;
5203 /* Request processed, but nothing found */
5204 if (xmlStrcmp(*code, BAD_CAST "0000")) {
5205 char *code_locale = utf82locale((char*) *code);
5206 char *status_message_locale = utf82locale((char*) *status_message);
5207 isds_log(ILF_ISDS, ILL_DEBUG,
5208 _("Server refused %s request for %s message ID "
5209 "(code=%s, message=%s)\n"),
5210 service_name_locale, message_id_locale,
5211 code_locale, status_message_locale);
5212 isds_log_message(context, status_message_locale);
5213 free(code_locale);
5214 free(status_message_locale);
5215 err = IE_ISDS;
5216 goto leave;
5219 leave:
5220 free(message_id_locale);
5221 free(service_name_locale);
5222 xmlFreeNode(request);
5223 return err;
5227 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
5228 * signed data and free ISDS response.
5229 * @context is session context
5230 * @message_id is UTF-8 encoded message ID for loging purpose
5231 * @response is parsed XML document. It will be freed and NULLed in the middle
5232 * of function run to save memmory. This is not guaranted in case of error.
5233 * @request_name is name of ISDS request used to construct response root
5234 * element name and for logging purpose.
5235 * @raw is reallocated output buffer with DER encoded CMS data
5236 * @raw_length is size of @raw buffer in bytes
5237 * @returns standard error codes, in case of error, @raw will be freed and
5238 * NULLed, @response sometimes. */
5239 static isds_error find_extract_signed_data_free_response(
5240 struct isds_ctx *context, const xmlChar *message_id,
5241 xmlDocPtr *response, const xmlChar *request_name,
5242 void **raw, size_t *raw_length) {
5244 isds_error err = IE_SUCCESS;
5245 char *xpath_expression = NULL;
5246 xmlXPathContextPtr xpath_ctx = NULL;
5247 xmlXPathObjectPtr result = NULL;
5248 char *encoded_structure = NULL;
5250 if (!context) return IE_INVALID_CONTEXT;
5251 if (!raw) return IE_INVAL;
5252 zfree(*raw);
5253 if (!message_id || !response || !*response || !request_name || !raw_length)
5254 return IE_INVAL;
5256 /* Build XPath expression */
5257 xpath_expression = astrcat3("/isds:", (char *) request_name,
5258 "Response/isds:dmSignature");
5259 if (!xpath_expression) return IE_NOMEM;
5261 /* Extract data */
5262 xpath_ctx = xmlXPathNewContext(*response);
5263 if (!xpath_ctx) {
5264 err = IE_ERROR;
5265 goto leave;
5267 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5268 err = IE_ERROR;
5269 goto leave;
5271 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
5272 if (!result) {
5273 err = IE_ERROR;
5274 goto leave;
5276 /* Empty response */
5277 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5278 char *message_id_locale = utf82locale((char*) message_id);
5279 isds_printf_message(context,
5280 _("Server did not return any signed data for mesage ID `%s' "
5281 "on %s request"),
5282 message_id_locale, request_name);
5283 free(message_id_locale);
5284 err = IE_ISDS;
5285 goto leave;
5287 /* More reponses */
5288 if (result->nodesetval->nodeNr > 1) {
5289 char *message_id_locale = utf82locale((char*) message_id);
5290 isds_printf_message(context,
5291 _("Server did return more signed data for message ID `%s' "
5292 "on %s request"),
5293 message_id_locale, request_name);
5294 free(message_id_locale);
5295 err = IE_ISDS;
5296 goto leave;
5298 /* One response */
5299 xpath_ctx->node = result->nodesetval->nodeTab[0];
5301 /* Extract PKCS#7 structure */
5302 EXTRACT_STRING(".", encoded_structure);
5303 if (!encoded_structure) {
5304 isds_log_message(context, _("dmSignature element is empty"));
5307 /* Here we have delivery info as standalone CMS in encoded_structure.
5308 * We don't need any other data, free them: */
5309 xmlXPathFreeObject(result); result = NULL;
5310 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
5311 xmlFreeDoc(*response); *response = NULL;
5314 /* Decode PKCS#7 to DER format */
5315 *raw_length = b64decode(encoded_structure, raw);
5316 if (*raw_length == (size_t) -1) {
5317 isds_log_message(context,
5318 _("Error while Base64-decoding PKCS#7 structure"));
5319 err = IE_ERROR;
5320 goto leave;
5323 leave:
5324 if (err) {
5325 zfree(*raw);
5326 raw_length = 0;
5329 free(encoded_structure);
5330 xmlXPathFreeObject(result);
5331 xmlXPathFreeContext(xpath_ctx);
5332 free(xpath_expression);
5334 return err;
5338 /* Download incoming message envelope identified by ID.
5339 * @context is session context
5340 * @message_id is message identifier (you can get them from
5341 * isds_get_list_of_received_messages())
5342 * @message is automatically reallocated message retrieved from ISDS.
5343 * It will miss documents per se. Use isds_get_received_message(), if you are
5344 * interrested in documents (content) too.
5345 * Returned hash and timestamp require documents to be verifiable. */
5346 isds_error isds_get_received_envelope(struct isds_ctx *context,
5347 const char *message_id, struct isds_message **message) {
5349 isds_error err = IE_SUCCESS;
5350 xmlDocPtr response = NULL;
5351 xmlChar *code = NULL, *status_message = NULL;
5352 xmlXPathContextPtr xpath_ctx = NULL;
5353 xmlXPathObjectPtr result = NULL;
5355 if (!context) return IE_INVALID_CONTEXT;
5357 /* Free former message if any */
5358 if (!message) return IE_INVAL;
5359 isds_message_free(message);
5361 /* Do request and check for success */
5362 err = build_send_check_message_request(context, SERVICE_DM_INFO,
5363 BAD_CAST "MessageEnvelopeDownload", message_id,
5364 &response, NULL, NULL, &code, &status_message);
5365 if (err) goto leave;
5367 /* Extract data */
5368 xpath_ctx = xmlXPathNewContext(response);
5369 if (!xpath_ctx) {
5370 err = IE_ERROR;
5371 goto leave;
5373 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5374 err = IE_ERROR;
5375 goto leave;
5377 result = xmlXPathEvalExpression(
5378 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
5379 "isds:dmReturnedMessageEnvelope",
5380 xpath_ctx);
5381 if (!result) {
5382 err = IE_ERROR;
5383 goto leave;
5385 /* Empty response */
5386 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5387 char *message_id_locale = utf82locale((char*) message_id);
5388 isds_printf_message(context,
5389 _("Server did not return any envelope for ID `%s' "
5390 "on MessageEnvelopeDownload request"), message_id_locale);
5391 free(message_id_locale);
5392 err = IE_ISDS;
5393 goto leave;
5395 /* More envelops */
5396 if (result->nodesetval->nodeNr > 1) {
5397 char *message_id_locale = utf82locale((char*) message_id);
5398 isds_printf_message(context,
5399 _("Server did return more envelopes for ID `%s' "
5400 "on MessageEnvelopeDownload request"), message_id_locale);
5401 free(message_id_locale);
5402 err = IE_ISDS;
5403 goto leave;
5405 /* One message */
5406 xpath_ctx->node = result->nodesetval->nodeTab[0];
5408 /* Extract the envelope (= message without documents, hence 0) */
5409 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
5410 if (err) goto leave;
5412 /* Save XML blob */
5413 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
5414 &(*message)->raw_length);
5416 leave:
5417 if (err) {
5418 isds_message_free(message);
5421 xmlXPathFreeObject(result);
5422 xmlXPathFreeContext(xpath_ctx);
5424 free(code);
5425 free(status_message);
5426 xmlFreeDoc(response);
5428 if (!err)
5429 isds_log(ILF_ISDS, ILL_DEBUG,
5430 _("MessageEnvelopeDownload request processed by server "
5431 "successfully.\n")
5433 return err;
5437 /* Load delivery info of any format from buffer.
5438 * @context is session context
5439 * @raw_type advertises format of @buffer content. Only delivery info types
5440 * are accepted.
5441 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
5442 * retrieve such data from message->raw after calling
5443 * isds_get_signed_delivery_info().
5444 * @length is length of buffer in bytes.
5445 * @message is automatically reallocated message parsed from @buffer.
5446 * @strategy selects how buffer will be attached into raw isds_message member.
5447 * */
5448 isds_error isds_load_delivery_info(struct isds_ctx *context,
5449 const isds_raw_type raw_type,
5450 const void *buffer, const size_t length,
5451 struct isds_message **message, const isds_buffer_strategy strategy) {
5453 isds_error err = IE_SUCCESS;
5454 message_ns_type message_ns;
5455 xmlDocPtr message_doc = NULL;
5456 xmlXPathContextPtr xpath_ctx = NULL;
5457 xmlXPathObjectPtr result = NULL;
5458 void *xml_stream = NULL;
5459 size_t xml_stream_length = 0;
5461 if (!context) return IE_INVALID_CONTEXT;
5462 if (!message) return IE_INVAL;
5463 isds_message_free(message);
5464 if (!buffer) return IE_INVAL;
5467 /* Select buffer format and extract XML from CMS*/
5468 switch (raw_type) {
5469 case RAWTYPE_DELIVERYINFO:
5470 message_ns = MESSAGE_NS_UNSIGNED;
5471 xml_stream = (void *) buffer;
5472 xml_stream_length = length;
5473 break;
5475 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
5476 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
5477 xml_stream = (void *) buffer;
5478 xml_stream_length = length;
5479 break;
5481 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
5482 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
5483 err = extract_cms_data(context, buffer, length,
5484 &xml_stream, &xml_stream_length);
5485 if (err) goto leave;
5486 break;
5488 default:
5489 isds_log_message(context, _("Bad raw delivery representation type"));
5490 return IE_INVAL;
5491 break;
5494 isds_log(ILF_ISDS, ILL_DEBUG,
5495 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
5496 xml_stream_length, xml_stream);
5498 /* Convert delivery info XML stream into XPath context */
5499 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
5500 if (!message_doc) {
5501 err = IE_XML;
5502 goto leave;
5504 xpath_ctx = xmlXPathNewContext(message_doc);
5505 if (!xpath_ctx) {
5506 err = IE_ERROR;
5507 goto leave;
5509 /* XXX: Name spaces mangled for signed delivery info:
5510 * http://isds.czechpoint.cz/v20/delivery:
5512 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
5513 * <q:dmDelivery>
5514 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
5515 * <p:dmID>170272</p:dmID>
5516 * ...
5517 * </p:dmDm>
5518 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
5519 * ...
5520 * </q:dmEvents>...</q:dmEvents>
5521 * </q:dmDelivery>
5522 * </q:GetDeliveryInfoResponse>
5523 * */
5524 if (register_namespaces(xpath_ctx, message_ns)) {
5525 err = IE_ERROR;
5526 goto leave;
5528 result = xmlXPathEvalExpression(
5529 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
5530 xpath_ctx);
5531 if (!result) {
5532 err = IE_ERROR;
5533 goto leave;
5535 /* Empty delivery info */
5536 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5537 isds_printf_message(context,
5538 _("XML document ss not sisds:dmDelivery document"));
5539 err = IE_ISDS;
5540 goto leave;
5542 /* More delivery infos */
5543 if (result->nodesetval->nodeNr > 1) {
5544 isds_printf_message(context,
5545 _("XML document has more sisds:dmDelivery elements"));
5546 err = IE_ISDS;
5547 goto leave;
5549 /* One delivery info */
5550 xpath_ctx->node = result->nodesetval->nodeTab[0];
5552 /* Extract the envelope (= message without documents, hence 0).
5553 * XXX: extract_TReturnedMessage() can obtain attachments size,
5554 * but delivery info carries none. It's coded as option elements,
5555 * so it should work. */
5556 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
5557 if (err) goto leave;
5559 /* Extract events */
5560 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
5561 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
5562 if (err) { err = IE_ERROR; goto leave; }
5563 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
5564 if (err) goto leave;
5566 /* Append raw CMS structure into message */
5567 (*message)->raw_type = raw_type;
5568 switch (strategy) {
5569 case BUFFER_DONT_STORE:
5570 break;
5571 case BUFFER_COPY:
5572 (*message)->raw = malloc(length);
5573 if (!(*message)->raw) {
5574 err = IE_NOMEM;
5575 goto leave;
5577 memcpy((*message)->raw, buffer, length);
5578 (*message)->raw_length = length;
5579 break;
5580 case BUFFER_MOVE:
5581 (*message)->raw = (void *) buffer;
5582 (*message)->raw_length = length;
5583 break;
5584 default:
5585 err = IE_ENUM;
5586 goto leave;
5589 leave:
5590 if (err) {
5591 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
5592 isds_message_free(message);
5595 xmlXPathFreeObject(result);
5596 xmlXPathFreeContext(xpath_ctx);
5597 xmlFreeDoc(message_doc);
5598 if (xml_stream != buffer) cms_data_free(xml_stream);
5600 if (!err)
5601 isds_log(ILF_ISDS, ILL_DEBUG,
5602 _("Delivery info loaded successfully.\n"));
5603 return err;
5607 /* Download signed delivery infosheet of given message identified by ID.
5608 * @context is session context
5609 * @message_id is message identifier (you can get them from
5610 * isds_get_list_of_{sent,received}_messages())
5611 * @message is automatically reallocated message retrieved from ISDS.
5612 * It will miss documents per se. Use isds_get_signed_received_message(),
5613 * if you are interrested in documents (content). OTOH, only this function
5614 * can get list events message has gone through. */
5615 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
5616 const char *message_id, struct isds_message **message) {
5618 isds_error err = IE_SUCCESS;
5619 xmlDocPtr response = NULL;
5620 xmlChar *code = NULL, *status_message = NULL;
5621 void *raw = NULL;
5622 size_t raw_length = 0;
5624 if (!context) return IE_INVALID_CONTEXT;
5626 /* Free former message if any */
5627 if (!message) return IE_INVAL;
5628 isds_message_free(message);
5630 /* Do request and check for success */
5631 err = build_send_check_message_request(context, SERVICE_DM_INFO,
5632 BAD_CAST "GetSignedDeliveryInfo", message_id,
5633 &response, NULL, NULL, &code, &status_message);
5634 if (err) goto leave;
5636 /* Find signed delivery info, extract it into raw and maybe free
5637 * response */
5638 err = find_extract_signed_data_free_response(context,
5639 (xmlChar *)message_id, &response,
5640 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
5641 if (err) goto leave;
5643 /* Parse delivery info */
5644 err = isds_load_delivery_info(context,
5645 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
5646 message, BUFFER_MOVE);
5647 if (err) goto leave;
5649 raw = NULL;
5651 leave:
5652 if (err) {
5653 isds_message_free(message);
5656 free(raw);
5657 free(code);
5658 free(status_message);
5659 xmlFreeDoc(response);
5661 if (!err)
5662 isds_log(ILF_ISDS, ILL_DEBUG,
5663 _("GetSignedDeliveryInfo request processed by server "
5664 "successfully.\n")
5666 return err;
5670 /* Download delivery infosheet of given message identified by ID.
5671 * @context is session context
5672 * @message_id is message identifier (you can get them from
5673 * isds_get_list_of_{sent,received}_messages())
5674 * @message is automatically reallocated message retrieved from ISDS.
5675 * It will miss documents per se. Use isds_get_received_message(), if you are
5676 * interrested in documents (content). OTOH, only this function can get list
5677 * events message has gone through. */
5678 isds_error isds_get_delivery_info(struct isds_ctx *context,
5679 const char *message_id, struct isds_message **message) {
5681 isds_error err = IE_SUCCESS;
5682 xmlDocPtr response = NULL;
5683 xmlChar *code = NULL, *status_message = NULL;
5684 xmlXPathContextPtr xpath_ctx = NULL;
5685 xmlXPathObjectPtr result = NULL;
5686 xmlNodePtr delivery_node = NULL;
5688 if (!context) return IE_INVALID_CONTEXT;
5690 /* Free former message if any */
5691 if (!message) return IE_INVAL;
5692 isds_message_free(message);
5694 /* Do request and check for success */
5695 err = build_send_check_message_request(context, SERVICE_DM_INFO,
5696 BAD_CAST "GetDeliveryInfo", message_id,
5697 &response, NULL, NULL, &code, &status_message);
5698 if (err) goto leave;
5700 /* Extract data */
5701 xpath_ctx = xmlXPathNewContext(response);
5702 if (!xpath_ctx) {
5703 err = IE_ERROR;
5704 goto leave;
5706 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5707 err = IE_ERROR;
5708 goto leave;
5710 result = xmlXPathEvalExpression(
5711 BAD_CAST "/isds:GetDeliveryInfoResponse/isds:dmDelivery",
5712 xpath_ctx);
5713 if (!result) {
5714 err = IE_ERROR;
5715 goto leave;
5717 /* Empty response */
5718 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5719 char *message_id_locale = utf82locale((char*) message_id);
5720 isds_printf_message(context,
5721 _("Server did not return any delivery info for ID `%s' "
5722 "on GetDeliveryInfo request"), message_id_locale);
5723 free(message_id_locale);
5724 err = IE_ISDS;
5725 goto leave;
5727 /* More delivery infos */
5728 if (result->nodesetval->nodeNr > 1) {
5729 char *message_id_locale = utf82locale((char*) message_id);
5730 isds_printf_message(context,
5731 _("Server did return more delivery infos for ID `%s' "
5732 "on GetDeliveryInfo request"), message_id_locale);
5733 free(message_id_locale);
5734 err = IE_ISDS;
5735 goto leave;
5737 /* One delivery info */
5738 xpath_ctx->node = delivery_node = result->nodesetval->nodeTab[0];
5740 /* Extract the envelope (= message without documents, hence 0).
5741 * XXX: extract_TReturnedMessage() can obtain attachments size,
5742 * but delivery info carries none. It's coded as option elements,
5743 * so it should work. */
5744 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
5745 if (err) goto leave;
5747 /* Extract events */
5748 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmEvents", xpath_ctx);
5749 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
5750 if (err) { err = IE_ERROR; goto leave; }
5751 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
5752 if (err) goto leave;
5754 /* Save XML blob */
5755 err = serialize_subtree(context, delivery_node, &(*message)->raw,
5756 &(*message)->raw_length);
5758 leave:
5759 if (err) {
5760 isds_message_free(message);
5763 xmlXPathFreeObject(result);
5764 xmlXPathFreeContext(xpath_ctx);
5766 free(code);
5767 free(status_message);
5768 xmlFreeDoc(response);
5770 if (!err)
5771 isds_log(ILF_ISDS, ILL_DEBUG,
5772 _("GetDeliveryInfo request processed by server "
5773 "successfully.\n")
5775 return err;
5779 /* Load incoming message from buffer.
5780 * @context is session context
5781 * @buffer XML stream with unsigned message. You can retrieve such data from
5782 * message->raw after calling isds_get_received_message().
5783 * @length is length of buffer in bytes.
5784 * @message is automatically reallocated message parsed from @buffer.
5785 * @strategy selects how buffer will be attached into raw isds_message member.
5786 * */
5787 isds_error isds_load_received_message(struct isds_ctx *context,
5788 const void *buffer, const size_t length,
5789 struct isds_message **message, const isds_buffer_strategy strategy) {
5791 isds_error err = IE_SUCCESS;
5792 xmlDocPtr message_doc = NULL;
5793 xmlXPathContextPtr xpath_ctx = NULL;
5794 xmlXPathObjectPtr result = NULL;
5796 if (!context) return IE_INVALID_CONTEXT;
5797 if (!message) return IE_INVAL;
5798 isds_message_free(message);
5799 if (!buffer) return IE_INVAL;
5802 isds_log(ILF_ISDS, ILL_DEBUG,
5803 _("Incoming message content:\n%.*s\nEnd of message\n"),
5804 length, buffer);
5806 /* Convert extracted messages XML stream into XPath context */
5807 message_doc = xmlParseMemory(buffer, length);
5808 if (!message_doc) {
5809 err = IE_XML;
5810 goto leave;
5812 xpath_ctx = xmlXPathNewContext(message_doc);
5813 if (!xpath_ctx) {
5814 err = IE_ERROR;
5815 goto leave;
5817 /* XXX: Standard name space */
5818 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5819 err = IE_ERROR;
5820 goto leave;
5822 result = xmlXPathEvalExpression(
5823 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
5824 xpath_ctx);
5825 if (!result) {
5826 err = IE_ERROR;
5827 goto leave;
5829 /* Missing dmReturnedMessage */
5830 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5831 isds_printf_message(context,
5832 _("XML document does not contain isds:dmReturnedMessage "
5833 "element"));
5834 err = IE_ISDS;
5835 goto leave;
5837 /* More elements. This should never happen. */
5838 if (result->nodesetval->nodeNr > 1) {
5839 isds_printf_message(context,
5840 _("XML document has more isds:dmReturnedMessage elements"));
5841 err = IE_ISDS;
5842 goto leave;
5844 /* One message */
5845 xpath_ctx->node = result->nodesetval->nodeTab[0];
5847 /* Extract the message */
5848 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
5849 if (err) goto leave;
5851 /* Append XML stream into message */
5852 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
5853 switch (strategy) {
5854 case BUFFER_DONT_STORE:
5855 break;
5856 case BUFFER_COPY:
5857 (*message)->raw = malloc(length);
5858 if (!(*message)->raw) {
5859 err = IE_NOMEM;
5860 goto leave;
5862 memcpy((*message)->raw, buffer, length);
5863 (*message)->raw_length = length;
5864 break;
5865 case BUFFER_MOVE:
5866 (*message)->raw = (void *) buffer;
5867 (*message)->raw_length = length;
5868 break;
5869 default:
5870 err = IE_ENUM;
5871 goto leave;
5874 leave:
5875 if (err) {
5876 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
5877 isds_message_free(message);
5880 xmlFreeDoc(message_doc);
5881 xmlXPathFreeObject(result);
5882 xmlXPathFreeContext(xpath_ctx);
5884 if (!err)
5885 isds_log(ILF_ISDS, ILL_DEBUG,
5886 _("Incoming message loaded successfully.\n"));
5887 return err;
5891 /* Download incoming message identified by ID.
5892 * @context is session context
5893 * @message_id is message identifier (you can get them from
5894 * isds_get_list_of_received_messages())
5895 * @message is automatically reallocated message retrieved from ISDS */
5896 isds_error isds_get_received_message(struct isds_ctx *context,
5897 const char *message_id, struct isds_message **message) {
5899 isds_error err = IE_SUCCESS;
5900 xmlDocPtr response = NULL;
5901 void *xml_stream = NULL;
5902 size_t xml_stream_length;
5903 xmlChar *code = NULL, *status_message = NULL;
5904 xmlXPathContextPtr xpath_ctx = NULL;
5905 xmlXPathObjectPtr result = NULL;
5906 char *phys_path = NULL;
5907 size_t phys_start, phys_end;
5909 if (!context) return IE_INVALID_CONTEXT;
5911 /* Free former message if any */
5912 if (message) isds_message_free(message);
5914 /* Do request and check for success */
5915 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
5916 BAD_CAST "MessageDownload", message_id,
5917 &response, &xml_stream, &xml_stream_length,
5918 &code, &status_message);
5919 if (err) goto leave;
5921 /* Extract data */
5922 xpath_ctx = xmlXPathNewContext(response);
5923 if (!xpath_ctx) {
5924 err = IE_ERROR;
5925 goto leave;
5927 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5928 err = IE_ERROR;
5929 goto leave;
5931 result = xmlXPathEvalExpression(
5932 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
5933 xpath_ctx);
5934 if (!result) {
5935 err = IE_ERROR;
5936 goto leave;
5938 /* Empty response */
5939 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5940 char *message_id_locale = utf82locale((char*) message_id);
5941 isds_printf_message(context,
5942 _("Server did not return any message for ID `%s' "
5943 "on MessageDownload request"), message_id_locale);
5944 free(message_id_locale);
5945 err = IE_ISDS;
5946 goto leave;
5948 /* More messages */
5949 if (result->nodesetval->nodeNr > 1) {
5950 char *message_id_locale = utf82locale((char*) message_id);
5951 isds_printf_message(context,
5952 _("Server did return more messages for ID `%s' "
5953 "on MessageDownload request"), message_id_locale);
5954 free(message_id_locale);
5955 err = IE_ISDS;
5956 goto leave;
5958 /* One message */
5959 xpath_ctx->node = result->nodesetval->nodeTab[0];
5961 /* Extract the message */
5962 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
5963 if (err) goto leave;
5965 /* Locate raw XML blob */
5966 phys_path = strdup(
5967 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
5968 PHYSXML_ELEMENT_SEPARATOR
5969 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
5970 PHYSXML_ELEMENT_SEPARATOR
5971 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
5973 if (!phys_path) {
5974 err = IE_NOMEM;
5975 goto leave;
5977 err = find_element_boundary(xml_stream, xml_stream_length,
5978 phys_path, &phys_start, &phys_end);
5979 zfree(phys_path);
5980 if (err) {
5981 isds_log_message(context,
5982 _("Substring with isds:MessageDownloadResponse element "
5983 "could not be located in raw SOAP message"));
5984 goto leave;
5986 /* Save XML blob */
5987 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
5988 &(*message)->raw_length);*/
5989 /* TODO: Store name space declarations from ancestors */
5990 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
5991 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
5992 (*message)->raw_length = phys_end - phys_start + 1;
5993 (*message)->raw = malloc((*message)->raw_length);
5994 if (!(*message)->raw) {
5995 err = IE_NOMEM;
5996 goto leave;
5998 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
6001 leave:
6002 if (err) {
6003 isds_message_free(message);
6006 free(phys_path);
6008 xmlXPathFreeObject(result);
6009 xmlXPathFreeContext(xpath_ctx);
6011 free(code);
6012 free(status_message);
6013 free(xml_stream);
6014 xmlFreeDoc(response);
6016 if (!err)
6017 isds_log(ILF_ISDS, ILL_DEBUG,
6018 _("MessageDownload request processed by server "
6019 "successfully.\n")
6021 return err;
6025 /* Load signed message from buffer.
6026 * @context is session context
6027 * @outgoing is true if message is outgoing, false if message is incoming
6028 * @buffer is DER encoded PKCS#7 structure with signed message. You can
6029 * retrieve such data from message->raw after calling
6030 * isds_get_signed_{received,sent}_message().
6031 * @length is length of buffer in bytes.
6032 * @message is automatically reallocated message parsed from @buffer.
6033 * @strategy selects how buffer will be attached into raw isds_message member.
6034 * */
6035 isds_error isds_load_signed_message(struct isds_ctx *context,
6036 const _Bool outgoing, const void *buffer, const size_t length,
6037 struct isds_message **message, const isds_buffer_strategy strategy) {
6039 isds_error err = IE_SUCCESS;
6040 xmlDocPtr message_doc = NULL;
6041 xmlXPathContextPtr xpath_ctx = NULL;
6042 xmlXPathObjectPtr result = NULL;
6043 void *xml_stream = NULL;
6044 size_t xml_stream_length = 0;
6046 if (!context) return IE_INVALID_CONTEXT;
6047 if (!message) return IE_INVAL;
6048 isds_message_free(message);
6049 if (!buffer) return IE_INVAL;
6052 /* Extract message from PKCS#7 structure */
6053 err = extract_cms_data(context, buffer, length,
6054 &xml_stream, &xml_stream_length);
6055 if (err) goto leave;
6057 isds_log(ILF_ISDS, ILL_DEBUG, (outgoing) ?
6058 _("Signed outgoing message content:\n%.*s\nEnd of message\n") :
6059 _("Signed incoming message content:\n%.*s\nEnd of message\n"),
6060 xml_stream_length, xml_stream);
6062 /* Convert extracted messages XML stream into XPath context */
6063 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
6064 if (!message_doc) {
6065 err = IE_XML;
6066 goto leave;
6068 xpath_ctx = xmlXPathNewContext(message_doc);
6069 if (!xpath_ctx) {
6070 err = IE_ERROR;
6071 goto leave;
6073 /* XXX: Name spaces mangled for outgoing direction:
6074 * http://isds.czechpoint.cz/v20/SentMessage:
6076 * <q:MessageDownloadResponse
6077 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
6078 * <q:dmReturnedMessage>
6079 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
6080 * <p:dmID>151916</p:dmID>
6081 * ...
6082 * </p:dmDm>
6083 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
6084 * ...
6085 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
6086 * </q:dmReturnedMessage>
6087 * </q:MessageDownloadResponse>
6089 * XXX: Name spaces mangled for incoming direction:
6090 * http://isds.czechpoint.cz/v20/message:
6092 * <q:MessageDownloadResponse
6093 * xmlns:q="http://isds.czechpoint.cz/v20/message">
6094 * <q:dmReturnedMessage>
6095 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
6096 * <p:dmID>151916</p:dmID>
6097 * ...
6098 * </p:dmDm>
6099 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
6100 * ...
6101 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
6102 * </q:dmReturnedMessage>
6103 * </q:MessageDownloadResponse>
6105 * Stupidity of ISDS developers is unlimited */
6106 if (register_namespaces(xpath_ctx, (outgoing) ?
6107 MESSAGE_NS_SIGNED_OUTGOING : MESSAGE_NS_SIGNED_INCOMING)) {
6108 err = IE_ERROR;
6109 goto leave;
6111 /* XXX: Embeded message XML document is always rooted as
6112 * /sisds:MessageDownloadResponse (even outgoing message). */
6113 result = xmlXPathEvalExpression(
6114 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
6115 xpath_ctx);
6116 if (!result) {
6117 err = IE_ERROR;
6118 goto leave;
6120 /* Empty embedded message */
6121 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6122 isds_printf_message(context,
6123 _("XML document embedded into PKCS#7 structure is not "
6124 "sisds:dmReturnedMessage document"));
6125 err = IE_ISDS;
6126 goto leave;
6128 /* More embedded messages */
6129 if (result->nodesetval->nodeNr > 1) {
6130 isds_printf_message(context,
6131 _("Embeded XML document into PKCS#7 structure has more "
6132 "root sisds:dmReturnedMessage elements"));
6133 err = IE_ISDS;
6134 goto leave;
6136 /* One embedded message */
6137 xpath_ctx->node = result->nodesetval->nodeTab[0];
6139 /* Extract the message */
6140 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
6141 if (err) goto leave;
6143 /* Append raw CMS structure into message */
6144 (*message)->raw_type = (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
6145 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
6146 switch (strategy) {
6147 case BUFFER_DONT_STORE:
6148 break;
6149 case BUFFER_COPY:
6150 (*message)->raw = malloc(length);
6151 if (!(*message)->raw) {
6152 err = IE_NOMEM;
6153 goto leave;
6155 memcpy((*message)->raw, buffer, length);
6156 (*message)->raw_length = length;
6157 break;
6158 case BUFFER_MOVE:
6159 (*message)->raw = (void *) buffer;
6160 (*message)->raw_length = length;
6161 break;
6162 default:
6163 err = IE_ENUM;
6164 goto leave;
6168 leave:
6169 if (err) {
6170 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
6171 isds_message_free(message);
6174 xmlFreeDoc(message_doc);
6175 cms_data_free(xml_stream);
6176 xmlXPathFreeObject(result);
6177 xmlXPathFreeContext(xpath_ctx);
6179 if (!err)
6180 isds_log(ILF_ISDS, ILL_DEBUG,
6181 _("Signed message loaded successfully.\n"));
6182 return err;
6186 /* Load message of any type from buffer.
6187 * @context is session context
6188 * @raw_type defines content type of @buffer. Only message types are allowed.
6189 * @buffer is message raw representation. Format (CMS, plain signed,
6190 * message direction) is defined in @raw_type. You can retrieve such data
6191 * from message->raw after calling isds_get_[signed]{received,sent}_message().
6192 * @length is length of buffer in bytes.
6193 * @message is automatically reallocated message parsed from @buffer.
6194 * @strategy selects how buffer will be attached into raw isds_message member.
6195 * */
6196 isds_error isds_load_message(struct isds_ctx *context,
6197 const isds_raw_type raw_type, const void *buffer, const size_t length,
6198 struct isds_message **message, const isds_buffer_strategy strategy) {
6200 isds_error err = IE_SUCCESS;
6201 void *xml_stream = NULL;
6202 size_t xml_stream_length = 0;
6203 message_ns_type message_ns;
6204 xmlDocPtr message_doc = NULL;
6205 xmlXPathContextPtr xpath_ctx = NULL;
6206 xmlXPathObjectPtr result = NULL;
6208 if (!context) return IE_INVALID_CONTEXT;
6209 if (!message) return IE_INVAL;
6210 isds_message_free(message);
6211 if (!buffer) return IE_INVAL;
6214 /* Select buffer format and extract XML from CMS*/
6215 switch (raw_type) {
6216 case RAWTYPE_INCOMING_MESSAGE:
6217 message_ns = MESSAGE_NS_UNSIGNED;
6218 xml_stream = (void *) buffer;
6219 xml_stream_length = length;
6220 break;
6222 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
6223 message_ns = MESSAGE_NS_SIGNED_INCOMING;
6224 xml_stream = (void *) buffer;
6225 xml_stream_length = length;
6226 break;
6228 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
6229 message_ns = MESSAGE_NS_SIGNED_INCOMING;
6230 err = extract_cms_data(context, buffer, length,
6231 &xml_stream, &xml_stream_length);
6232 if (err) goto leave;
6233 break;
6235 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
6236 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
6237 xml_stream = (void *) buffer;
6238 xml_stream_length = length;
6239 break;
6241 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
6242 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
6243 err = extract_cms_data(context, buffer, length,
6244 &xml_stream, &xml_stream_length);
6245 if (err) goto leave;
6246 break;
6248 default:
6249 isds_log_message(context, _("Bad raw message representation type"));
6250 return IE_INVAL;
6251 break;
6254 isds_log(ILF_ISDS, ILL_DEBUG,
6255 _("Loading message:\n%.*s\nEnd of message\n"),
6256 xml_stream_length, xml_stream);
6258 /* Convert messages XML stream into XPath context */
6259 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
6260 if (!message_doc) {
6261 err = IE_XML;
6262 goto leave;
6264 xpath_ctx = xmlXPathNewContext(message_doc);
6265 if (!xpath_ctx) {
6266 err = IE_ERROR;
6267 goto leave;
6269 /* XXX: Standard name space for unsigned icoming direction:
6270 * http://isds.czechpoint.cz/v20/SentMessage
6272 * XXX: Name spaces mangled for signed outgoing direction:
6273 * http://isds.czechpoint.cz/v20/SentMessage:
6275 * <q:MessageDownloadResponse
6276 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
6277 * <q:dmReturnedMessage>
6278 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
6279 * <p:dmID>151916</p:dmID>
6280 * ...
6281 * </p:dmDm>
6282 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
6283 * ...
6284 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
6285 * </q:dmReturnedMessage>
6286 * </q:MessageDownloadResponse>
6288 * XXX: Name spaces mangled for signed incoming direction:
6289 * http://isds.czechpoint.cz/v20/message:
6291 * <q:MessageDownloadResponse
6292 * xmlns:q="http://isds.czechpoint.cz/v20/message">
6293 * <q:dmReturnedMessage>
6294 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
6295 * <p:dmID>151916</p:dmID>
6296 * ...
6297 * </p:dmDm>
6298 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
6299 * ...
6300 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
6301 * </q:dmReturnedMessage>
6302 * </q:MessageDownloadResponse>
6304 * Stupidity of ISDS developers is unlimited */
6305 if (register_namespaces(xpath_ctx, message_ns)) {
6306 err = IE_ERROR;
6307 goto leave;
6309 result = xmlXPathEvalExpression(
6310 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
6311 xpath_ctx);
6312 if (!result) {
6313 err = IE_ERROR;
6314 goto leave;
6316 /* Empty message */
6317 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6318 isds_printf_message(context,
6319 _("XML document does not contain "
6320 "sisds:dmReturnedMessage element"));
6321 err = IE_ISDS;
6322 goto leave;
6324 /* More messages */
6325 if (result->nodesetval->nodeNr > 1) {
6326 isds_printf_message(context,
6327 _("XML document has more sisds:dmReturnedMessage elements"));
6328 err = IE_ISDS;
6329 goto leave;
6331 /* One message */
6332 xpath_ctx->node = result->nodesetval->nodeTab[0];
6334 /* Extract the message */
6335 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
6336 if (err) goto leave;
6338 /* Append raw buffer into message */
6339 (*message)->raw_type = raw_type;
6340 switch (strategy) {
6341 case BUFFER_DONT_STORE:
6342 break;
6343 case BUFFER_COPY:
6344 (*message)->raw = malloc(length);
6345 if (!(*message)->raw) {
6346 err = IE_NOMEM;
6347 goto leave;
6349 memcpy((*message)->raw, buffer, length);
6350 (*message)->raw_length = length;
6351 break;
6352 case BUFFER_MOVE:
6353 (*message)->raw = (void *) buffer;
6354 (*message)->raw_length = length;
6355 break;
6356 default:
6357 err = IE_ENUM;
6358 goto leave;
6362 leave:
6363 if (err) {
6364 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
6365 isds_message_free(message);
6368 if (xml_stream != buffer) cms_data_free(xml_stream);
6369 xmlXPathFreeObject(result);
6370 xmlXPathFreeContext(xpath_ctx);
6371 xmlFreeDoc(message_doc);
6373 if (!err)
6374 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
6375 return err;
6379 /* Download signed incoming/outgoing message identified by ID.
6380 * @context is session context
6381 * @output is true for outging message, false for incoming message
6382 * @message_id is message identifier (you can get them from
6383 * isds_get_list_of_{sent,received}_messages())
6384 * @message is automatically reallocated message retrieved from ISDS. The raw
6385 * memeber will be filled with PKCS#7 structure in DER format. */
6386 _hidden isds_error isds_get_signed_message(struct isds_ctx *context,
6387 const _Bool outgoing, const char *message_id,
6388 struct isds_message **message) {
6390 isds_error err = IE_SUCCESS;
6391 xmlDocPtr response = NULL;
6392 xmlChar *code = NULL, *status_message = NULL;
6393 xmlXPathContextPtr xpath_ctx = NULL;
6394 xmlXPathObjectPtr result = NULL;
6395 char *encoded_structure = NULL;
6396 void *raw = NULL;
6397 size_t raw_length = 0;
6399 if (!context) return IE_INVALID_CONTEXT;
6400 if (!message) return IE_INVAL;
6401 isds_message_free(message);
6403 /* Do request and check for success */
6404 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
6405 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
6406 BAD_CAST "SignedMessageDownload",
6407 message_id, &response, NULL, NULL, &code, &status_message);
6408 if (err) goto leave;
6410 /* Find signed message, extract it into raw and maybe free
6411 * response */
6412 err = find_extract_signed_data_free_response(context,
6413 (xmlChar *)message_id, &response,
6414 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
6415 BAD_CAST "SignedMessageDownload",
6416 &raw, &raw_length);
6417 if (err) goto leave;
6419 /* Parse message */
6420 err = isds_load_message(context,
6421 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
6422 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
6423 raw, raw_length, message, BUFFER_MOVE);
6424 if (err) goto leave;
6426 raw = NULL;
6428 leave:
6429 if (err) {
6430 isds_message_free(message);
6433 free(encoded_structure);
6434 xmlXPathFreeObject(result);
6435 xmlXPathFreeContext(xpath_ctx);
6436 free(raw);
6438 free(code);
6439 free(status_message);
6440 xmlFreeDoc(response);
6442 if (!err)
6443 isds_log(ILF_ISDS, ILL_DEBUG,
6444 (outgoing) ?
6445 _("SignedSentMessageDownload request processed by server "
6446 "successfully.\n") :
6447 _("SignedMessageDownload request processed by server "
6448 "successfully.\n")
6450 return err;
6454 /* Download signed incoming message identified by ID.
6455 * @context is session context
6456 * @message_id is message identifier (you can get them from
6457 * isds_get_list_of_received_messages())
6458 * @message is automatically reallocated message retrieved from ISDS. The raw
6459 * memeber will be filled with PKCS#7 structure in DER format. */
6460 isds_error isds_get_signed_received_message(struct isds_ctx *context,
6461 const char *message_id, struct isds_message **message) {
6462 return isds_get_signed_message(context, 0, message_id, message);
6466 /* Download signed outgoing message identified by ID.
6467 * @context is session context
6468 * @message_id is message identifier (you can get them from
6469 * isds_get_list_of_sent_messages())
6470 * @message is automatically reallocated message retrieved from ISDS. The raw
6471 * memeber will be filled with PKCS#7 structure in DER format. */
6472 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
6473 const char *message_id, struct isds_message **message) {
6474 return isds_get_signed_message(context, 1, message_id, message);
6478 /* Retrieve hash of message identified by ID stored in ISDS.
6479 * @context is session context
6480 * @message_id is message identifier
6481 * @hash is automatically reallocated message hash downloaded from ISDS.
6482 * Message must exist in system and must not be deleted. */
6483 isds_error isds_download_message_hash(struct isds_ctx *context,
6484 const char *message_id, struct isds_hash **hash) {
6486 isds_error err = IE_SUCCESS;
6487 xmlDocPtr response = NULL;
6488 xmlChar *code = NULL, *status_message = NULL;
6489 xmlXPathContextPtr xpath_ctx = NULL;
6490 xmlXPathObjectPtr result = NULL;
6492 if (!context) return IE_INVALID_CONTEXT;
6494 isds_hash_free(hash);
6496 err = build_send_check_message_request(context, SERVICE_DM_INFO,
6497 BAD_CAST "VerifyMessage", message_id,
6498 &response, NULL, NULL, &code, &status_message);
6499 if (err) goto leave;
6502 /* Extract data */
6503 xpath_ctx = xmlXPathNewContext(response);
6504 if (!xpath_ctx) {
6505 err = IE_ERROR;
6506 goto leave;
6508 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6509 err = IE_ERROR;
6510 goto leave;
6512 result = xmlXPathEvalExpression(
6513 BAD_CAST "/isds:VerifyMessageResponse",
6514 xpath_ctx);
6515 if (!result) {
6516 err = IE_ERROR;
6517 goto leave;
6519 /* Empty response */
6520 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6521 char *message_id_locale = utf82locale((char*) message_id);
6522 isds_printf_message(context,
6523 _("Server did not return any response for ID `%s' "
6524 "on VerifyMessage request"), message_id_locale);
6525 free(message_id_locale);
6526 err = IE_ISDS;
6527 goto leave;
6529 /* More responses */
6530 if (result->nodesetval->nodeNr > 1) {
6531 char *message_id_locale = utf82locale((char*) message_id);
6532 isds_printf_message(context,
6533 _("Server did return more responses for ID `%s' "
6534 "on VerifyMessage request"), message_id_locale);
6535 free(message_id_locale);
6536 err = IE_ISDS;
6537 goto leave;
6539 /* One response */
6540 xpath_ctx->node = result->nodesetval->nodeTab[0];
6542 /* Extract the hash */
6543 err = find_and_extract_DmHash(context, hash, xpath_ctx);
6545 leave:
6546 if (err) {
6547 isds_hash_free(hash);
6550 xmlXPathFreeObject(result);
6551 xmlXPathFreeContext(xpath_ctx);
6553 free(code);
6554 free(status_message);
6555 xmlFreeDoc(response);
6557 if (!err)
6558 isds_log(ILF_ISDS, ILL_DEBUG,
6559 _("VerifyMessage request processed by server "
6560 "successfully.\n")
6562 return err;
6566 /* Mark message as read. This is a transactional commit function to acknoledge
6567 * to ISDS the message has been downloaded and processed by client properly.
6568 * @context is session context
6569 * @message_id is message identifier. */
6570 isds_error isds_mark_message_read(struct isds_ctx *context,
6571 const char *message_id) {
6573 isds_error err = IE_SUCCESS;
6574 xmlDocPtr response = NULL;
6575 xmlChar *code = NULL, *status_message = NULL;
6577 if (!context) return IE_INVALID_CONTEXT;
6579 /* Do request and check for success */
6580 err = build_send_check_message_request(context, SERVICE_DM_INFO,
6581 BAD_CAST "MarkMessageAsDownloaded", message_id,
6582 &response, NULL, NULL, &code, &status_message);
6584 free(code);
6585 free(status_message);
6586 xmlFreeDoc(response);
6588 if (!err)
6589 isds_log(ILF_ISDS, ILL_DEBUG,
6590 _("MarkMessageAsDownloaded request processed by server "
6591 "successfully.\n")
6593 return err;
6596 /* Mark message as received by recipient. This is applicable only to
6597 * commercial message. There is no specified way how to distinguishe
6598 * commercial message from government message yet. Government message is
6599 * received automatically (by law), commenrcial message on recipient request.
6600 * @context is session context
6601 * @message_id is message identifier. */
6602 isds_error isds_mark_message_received(struct isds_ctx *context,
6603 const char *message_id) {
6605 isds_error err = IE_SUCCESS;
6606 xmlDocPtr response = NULL;
6607 xmlChar *code = NULL, *status_message = NULL;
6609 if (!context) return IE_INVALID_CONTEXT;
6611 /* Do request and check for success */
6612 err = build_send_check_message_request(context, SERVICE_DM_INFO,
6613 BAD_CAST "ConfirmDelivery", message_id,
6614 &response, NULL, NULL, &code, &status_message);
6616 free(code);
6617 free(status_message);
6618 xmlFreeDoc(response);
6620 if (!err)
6621 isds_log(ILF_ISDS, ILL_DEBUG,
6622 _("ConfirmDelivery request processed by server "
6623 "successfully.\n")
6625 return err;
6629 #undef INSERT_STRING_ATTRIBUTE
6630 #undef INSERT_ULONGINTNOPTR
6631 #undef INSERT_ULONGINT
6632 #undef INSERT_LONGINT
6633 #undef INSERT_BOOLEAN
6634 #undef INSERT_STRING
6635 #undef EXTRACT_STRING_ATTRIBUTE
6636 #undef EXTRACT_ULONGINT
6637 #undef EXTRACT_LONGINT
6638 #undef EXTRACT_BOOLEAN
6639 #undef EXTRACT_STRING
6642 /* Compute hash of message from raw representation and store it into envelope.
6643 * Original hash structure will be destroyed in envelope.
6644 * @context is session context
6645 * @message is message carrying raw XML message blob
6646 * @algorithm is desired hash algorithm to use */
6647 isds_error isds_compute_message_hash(struct isds_ctx *context,
6648 struct isds_message *message, const isds_hash_algorithm algorithm) {
6649 isds_error err = IE_SUCCESS;
6650 const char *nsuri;
6651 void *xml_stream = NULL;
6652 size_t xml_stream_length;
6653 size_t phys_start, phys_end;
6654 char *phys_path = NULL;
6655 struct isds_hash *new_hash = NULL;
6658 if (!context) return IE_INVALID_CONTEXT;
6659 if (!message) return IE_INVAL;
6661 if (!message->raw) {
6662 isds_log_message(context,
6663 _("Message does not carry raw representation"));
6664 return IE_INVAL;
6667 switch (message->raw_type) {
6668 case RAWTYPE_INCOMING_MESSAGE:
6669 nsuri = ISDS_NS;
6670 xml_stream = message->raw;
6671 xml_stream_length = message->raw_length;
6672 break;
6674 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
6675 nsuri = SISDS_INCOMING_NS;
6676 xml_stream = message->raw;
6677 xml_stream_length = message->raw_length;
6678 break;
6680 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
6681 nsuri = SISDS_INCOMING_NS;
6682 err = extract_cms_data(context, message->raw, message->raw_length,
6683 &xml_stream, &xml_stream_length);
6684 if (err) goto leave;
6685 break;
6687 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
6688 nsuri = SISDS_OUTGOING_NS;
6689 xml_stream = message->raw;
6690 xml_stream_length = message->raw_length;
6691 break;
6693 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
6694 nsuri = SISDS_OUTGOING_NS;
6695 err = extract_cms_data(context, message->raw, message->raw_length,
6696 &xml_stream, &xml_stream_length);
6697 if (err) goto leave;
6698 break;
6700 default:
6701 isds_log_message(context, _("Bad raw representation type"));
6702 return IE_INVAL;
6703 break;
6707 /* XXX: Hash is computed from original string represinting isds:dmDm
6708 * subtree. That means no encoding, white space, xmlns attributes changes.
6709 * In other words, input for hash can be invalid XML stream. */
6710 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
6711 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
6712 PHYSXML_ELEMENT_SEPARATOR,
6713 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
6714 PHYSXML_ELEMENT_SEPARATOR
6715 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
6716 err = IE_NOMEM;
6717 goto leave;
6719 err = find_element_boundary(xml_stream, xml_stream_length,
6720 phys_path, &phys_start, &phys_end);
6721 zfree(phys_path);
6722 if (err) {
6723 isds_log_message(context,
6724 _("Substring with isds:dmDM element could not be located "
6725 "in raw message"));
6726 goto leave;
6730 /* Compute hash */
6731 new_hash = calloc(1, sizeof(*new_hash));
6732 if (!new_hash) {
6733 err = IE_NOMEM;
6734 goto leave;
6736 new_hash->algorithm = algorithm;
6737 err = compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
6738 new_hash);
6739 if (err) {
6740 isds_log_message(context, _("Could not compute message hash"));
6741 goto leave;
6744 /* Save computed hash */
6745 if (!message->envelope) {
6746 message->envelope = calloc(1, sizeof(*message->envelope));
6747 if (!message->envelope) {
6748 err = IE_NOMEM;
6749 goto leave;
6752 isds_hash_free(&message->envelope->hash);
6753 message->envelope->hash = new_hash;
6755 leave:
6756 if (err) {
6757 isds_hash_free(&new_hash);
6760 free(phys_path);
6761 if (xml_stream != message->raw) free(xml_stream);
6762 return err;
6766 /* Compare two hashes.
6767 * @h1 is first hash
6768 * @h2 is another hash
6769 * @return
6770 * IE_SUCCESS if hashes equal
6771 * IE_NOTUNIQ if hashes are comparable, but they don't equal
6772 * IE_ENUM if not comparable, but both structures defined
6773 * IE_INVAL if some of the structures are undefined (NULL)
6774 * IE_ERROR if internal error occurs */
6775 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
6776 if (h1 == NULL || h2 == NULL) return IE_INVAL;
6777 if (h1->algorithm != h2->algorithm) return IE_ENUM;
6778 if (h1->length != h2->length) return IE_ERROR;
6779 if (h1->length > 0 && !h1->value) return IE_ERROR;
6780 if (h2->length > 0 && !h2->value) return IE_ERROR;
6782 for (int i = 0; i < h1->length; i++) {
6783 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
6784 return IE_NOTEQUAL;
6786 return IE_SUCCESS;
6790 /* Check message has gone through ISDS by comparing message hash stored in
6791 * ISDS and locally computed hash. You must provide message with valid raw
6792 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
6793 * This is convenient wrapper for isds_download_message_hash(),
6794 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
6795 * @context is session context
6796 * @message is message with valid raw and envelope member; envelope->hash
6797 * member will be changed during funcion run. Use envelope on heap only.
6798 * @return
6799 * IE_SUCCESS if message originates in ISDS
6800 * IE_NOTEQUAL if message is unknown to ISDS
6801 * other code for other errors */
6802 isds_error isds_verify_message_hash(struct isds_ctx *context,
6803 struct isds_message *message) {
6804 isds_error err = IE_SUCCESS;
6805 struct isds_hash *downloaded_hash = NULL;
6807 if (!context) return IE_INVALID_CONTEXT;
6808 if (!message) return IE_INVAL;
6810 if (!message->envelope) {
6811 isds_log_message(context,
6812 _("Given message structure is missing envelope"));
6813 return IE_INVAL;
6815 if (!message->raw) {
6816 isds_log_message(context,
6817 _("Given message structure is missing raw representation"));
6818 return IE_INVAL;
6821 err = isds_download_message_hash(context, message->envelope->dmID,
6822 &downloaded_hash);
6823 if (err) goto leave;
6825 err = isds_compute_message_hash(context, message,
6826 downloaded_hash->algorithm);
6827 if (err) goto leave;
6829 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
6831 leave:
6832 isds_hash_free(&downloaded_hash);
6833 return err;
6837 /* Search for document by document ID in list of documents. IDs are compared
6838 * as UTF-8 string.
6839 * @documents is list of isds_documents
6840 * @id is document identifier
6841 * @return first matching document or NULL. */
6842 const struct isds_document *isds_find_document_by_id(
6843 const struct isds_list *documents, const char *id) {
6844 const struct isds_list *item;
6845 const struct isds_document *document;
6847 for (item = documents; item; item = item->next) {
6848 document = (struct isds_document *) item->data;
6849 if (!document) continue;
6851 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
6852 return document;
6855 return NULL;
6859 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
6860 struct isds_message **message);
6861 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
6862 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
6863 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
6864 struct isds_address **address);
6866 int isds_message_free(struct isds_message **message);
6867 int isds_address_free(struct isds_address **address);
6871 /* Makes known all relevant namespaces to given XPath context
6872 * @xpat_ctx is XPath context
6873 * @message_ns selects propper message name space. Unsisnged and signed
6874 * messages differs.
6875 * prefix and to URI ISDS_NS */
6876 _hidden isds_error register_namespaces(xmlXPathContextPtr xpath_ctx,
6877 const message_ns_type message_ns) {
6878 const xmlChar *message_namespace = NULL;
6880 if (!xpath_ctx) return IE_ERROR;
6882 switch(message_ns) {
6883 case MESSAGE_NS_UNSIGNED:
6884 message_namespace = BAD_CAST ISDS_NS; break;
6885 case MESSAGE_NS_SIGNED_INCOMING:
6886 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
6887 case MESSAGE_NS_SIGNED_OUTGOING:
6888 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
6889 case MESSAGE_NS_SIGNED_DELIVERY:
6890 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
6891 default:
6892 return IE_ENUM;
6895 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
6896 return IE_ERROR;
6897 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
6898 return IE_ERROR;
6899 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
6900 return IE_ERROR;
6901 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
6902 return IE_ERROR;
6903 return IE_SUCCESS;