Implement eventstring2event()
[libisds.git] / src / isds.c
blob998938b92c334d42fe790b7ff21df65eedea6e2c
1 #define _XOPEN_SOURCE 500 /* strdup from string.h, strptime from time.h */
2 #include <stdlib.h>
3 #include <string.h>
4 #include <stdio.h>
5 #include <stdarg.h>
6 #include <ctype.h>
7 #include "isds_priv.h"
8 #include "utils.h"
9 #include "soap.h"
10 #include "validator.h"
11 #include "crypto.h"
12 #include <gpg-error.h> /* Because of ksba or gpgme */
15 /* Free isds_list with all member data.
16 * @list list to free, on return will be NULL */
17 void isds_list_free(struct isds_list **list) {
18 struct isds_list *item, *next_item;
20 if (!list || !*list) return;
22 for(item = *list; item; item = next_item) {
23 (item->destructor)(&(item->data));
24 next_item = item->next;
25 free(item);
28 *list = NULL;
32 /* Deallocate structure isds_hash and NULL it.
33 * @hash hash to to free */
34 void isds_hash_free(struct isds_hash **hash) {
35 if(!hash || !*hash) return;
36 free((*hash)->value);
37 zfree((*hash));
41 /* Deallocate structure isds_PersonName recursively and NULL it */
42 static void isds_PersonName_free(struct isds_PersonName **person_name) {
43 if (!person_name || !*person_name) return;
45 free((*person_name)->pnFirstName);
46 free((*person_name)->pnMiddleName);
47 free((*person_name)->pnLastName);
48 free((*person_name)->pnLastNameAtBirth);
50 free(*person_name);
51 *person_name = NULL;
55 /* Deallocate structure isds_BirthInfo recursively and NULL it */
56 static void isds_BirthInfo_free(struct isds_BirthInfo **birth_info) {
57 if (!birth_info || !*birth_info) return;
59 free((*birth_info)->biDate);
60 free((*birth_info)->biCity);
61 free((*birth_info)->biCounty);
62 free((*birth_info)->biState);
64 free(*birth_info);
65 *birth_info = NULL;
69 /* Deallocate structure isds_Address recursively and NULL it */
70 static void isds_Address_free(struct isds_Address **address) {
71 if (!address || !*address) return;
73 free((*address)->adCity);
74 free((*address)->adStreet);
75 free((*address)->adNumberInStreet);
76 free((*address)->adNumberInMunicipality);
77 free((*address)->adZipCode);
78 free((*address)->adState);
80 free(*address);
81 *address = NULL;
85 /* Deallocate structure isds_DbOwnerInfo recursively and NULL it */
86 void isds_DbOwnerInfo_free(struct isds_DbOwnerInfo **db_owner_info) {
87 if (!db_owner_info || !*db_owner_info) return;
89 free((*db_owner_info)->dbID);
90 free((*db_owner_info)->dbType);
91 free((*db_owner_info)->ic);
92 isds_PersonName_free(&((*db_owner_info)->personName));
93 free((*db_owner_info)->firmName);
94 isds_BirthInfo_free(&((*db_owner_info)->birthInfo));
95 isds_Address_free(&((*db_owner_info)->address));
96 free((*db_owner_info)->nationality);
97 free((*db_owner_info)->email);
98 free((*db_owner_info)->telNumber);
99 free((*db_owner_info)->identifier);
100 free((*db_owner_info)->registryCode);
101 free((*db_owner_info)->dbState);
102 free((*db_owner_info)->dbEffectiveOVM);
104 free(*db_owner_info);
105 *db_owner_info = NULL;
109 /* Deallocate struct isds_event recursively and NULL it */
110 void isds_event_free(struct isds_event **event) {
111 if (!event || !*event) return;
113 free((*event)->time);
114 free((*event)->type);
115 free((*event)->description);
116 zfree(*event);
120 /* Deallocate struct isds_envelope recursively and NULL it */
121 void isds_envelope_free(struct isds_envelope **envelope) {
122 if (!envelope || !*envelope) return;
124 free((*envelope)->dmID);
125 free((*envelope)->dbIDSender);
126 free((*envelope)->dmSender);
127 free((*envelope)->dmSenderAddress);
128 free((*envelope)->dmSenderType);
129 free((*envelope)->dmRecipient);
130 free((*envelope)->dmRecipientAddress);
131 free((*envelope)->dmAmbiguousRecipient);
133 free((*envelope)->dmOrdinal);
134 free((*envelope)->dmMessageStatus);
135 free((*envelope)->dmDeliveryTime);
136 free((*envelope)->dmAcceptanceTime);
137 isds_hash_free(&(*envelope)->hash);
138 free((*envelope)->timestamp);
139 isds_list_free(&(*envelope)->events);
141 free((*envelope)->dmSenderOrgUnit);
142 free((*envelope)->dmSenderOrgUnitNum);
143 free((*envelope)->dbIDRecipient);
144 free((*envelope)->dmRecipientOrgUnit);
145 free((*envelope)->dmRecipientOrgUnitNum);
146 free((*envelope)->dmToHands);
147 free((*envelope)->dmAnnotation);
148 free((*envelope)->dmRecipientRefNumber);
149 free((*envelope)->dmSenderRefNumber);
150 free((*envelope)->dmRecipientIdent);
151 free((*envelope)->dmSenderIdent);
153 free((*envelope)->dmLegalTitleLaw);
154 free((*envelope)->dmLegalTitleYear);
155 free((*envelope)->dmLegalTitleSect);
156 free((*envelope)->dmLegalTitlePar);
157 free((*envelope)->dmLegalTitlePoint);
159 free((*envelope)->dmPersonalDelivery);
160 free((*envelope)->dmAllowSubstDelivery);
161 free((*envelope)->dmOVM);
163 free(*envelope);
164 *envelope = NULL;
168 /* Deallocate struct isds_message recursively and NULL it */
169 void isds_message_free(struct isds_message **message) {
170 if (!message || !*message) return;
172 free((*message)->raw);
173 isds_envelope_free(&((*message)->envelope));
174 isds_list_free(&((*message)->documents));
176 free(*message);
177 *message = NULL;
181 /* Deallocate struct isds_document recursively and NULL it */
182 void isds_document_free(struct isds_document **document) {
183 if (!document || !*document) return;
185 free((*document)->data);
186 free((*document)->dmMimeType);
187 free((*document)->dmFileGuid);
188 free((*document)->dmUpFileGuid);
189 free((*document)->dmFileDescr);
190 free((*document)->dmFormat);
192 free(*document);
193 *document = NULL;
197 /* Initialize ISDS library.
198 * Global function, must be called before other functions.
199 * If it failes you can not use ISDS library and must call isds_cleanup() to
200 * free partially inititialized global variables. */
201 isds_error isds_init(void) {
202 /* NULL global variables */
203 log_facilities = ILF_ALL;
204 log_level = ILL_WARNING;
206 /* Initialize CURL */
207 if (curl_global_init(CURL_GLOBAL_ALL)) {
208 isds_log(ILF_ISDS, ILL_CRIT, _("CURL library initialization failed\n"));
209 return IE_ERROR;
212 /* Inicialize gpg-error because of gpgme and ksba */
213 if (gpg_err_init()) {
214 isds_log(ILF_ISDS, ILL_CRIT,
215 _("gpg-error library initialization failed\n"));
216 return IE_ERROR;
219 /* Initialize GPGME */
220 if (init_gpgme()) {
221 isds_log(ILF_ISDS, ILL_CRIT,
222 _("GPGME library initialization failed\n"));
223 return IE_ERROR;
226 /* Initialize gcrypt */
227 if (init_gcrypt()) {
228 isds_log(ILF_ISDS, ILL_CRIT,
229 _("gcrypt library initialization failed\n"));
230 return IE_ERROR;
234 /* This can _exit() current program. Find not so assertive check. */
235 LIBXML_TEST_VERSION;
237 /* Allocate global variables */
240 return IE_SUCCESS;
244 /* Deinicialize ISDS library.
245 * Global function, must be called as last library function. */
246 isds_error isds_cleanup(void) {
247 /* XML */
248 xmlCleanupParser();
250 /* Curl */
251 curl_global_cleanup();
253 return IE_SUCCESS;
257 /* Return text description of ISDS error */
258 char *isds_strerror(const isds_error error) {
259 switch (error) {
260 case IE_SUCCESS:
261 return(_("Success")); break;
262 case IE_ERROR:
263 return(_("Unspecified error")); break;
264 case IE_NOTSUP:
265 return(_("Not supported")); break;
266 case IE_INVAL:
267 return(_("Invalid value")); break;
268 case IE_INVALID_CONTEXT:
269 return(_("Invalid context")); break;
270 case IE_NOT_LOGGED_IN:
271 return(_("Not logged in")); break;
272 case IE_CONNECTION_CLOSED:
273 return(_("Connection closed")); break;
274 case IE_TIMED_OUT:
275 return(_("Timed out")); break;
276 case IE_NOEXIST:
277 return(_("Not exist")); break;
278 case IE_NOMEM:
279 return(_("Out of memory")); break;
280 case IE_NETWORK:
281 return(_("Network problem")); break;
282 case IE_HTTP:
283 return(_("HTTP problem")); break;
284 case IE_SOAP:
285 return(_("SOAP problem")); break;
286 case IE_XML:
287 return(_("XML problem")); break;
288 case IE_ISDS:
289 return(_("ISDS server problem")); break;
290 case IE_ENUM:
291 return(_("Invalid enum value")); break;
292 case IE_DATE:
293 return(_("Invalid date value")); break;
294 case IE_2BIG:
295 return(_("Too big")); break;
296 case IE_NOTUNIQ:
297 return(_("Value not unique")); break;
298 default:
299 return(_("Unknown error"));
304 /* Create ISDS context.
305 * Each context can be used for different sessions to (possibly) differnet
306 * ISDS server with different credentials. */
307 struct isds_ctx *isds_ctx_create(void) {
308 struct isds_ctx *context;
309 context = malloc(sizeof(*context));
310 if (context) memset(context, 0, sizeof(*context));
311 return context;
315 /* Destroy ISDS context and free memmory.
316 * @context will be NULLed on success. */
317 isds_error isds_ctx_free(struct isds_ctx **context) {
318 if (!context || !*context) {
319 return IE_INVALID_CONTEXT;
322 /* Discard credentials */
323 isds_logout(*context);
325 /* Free other structures */
326 free((*context)->tls_verify_server);
327 free((*context)->tls_ca_file);
328 free((*context)->tls_ca_dir);
329 free((*context)->long_message);
331 free(*context);
332 *context = NULL;
333 return IE_SUCCESS;
337 /* Return long message text produced by library fucntion, e.g. detailed error
338 * mesage. Returned pointer is only valid until new library function is
339 * called for the same context. Could be NULL, especially if NULL context is
340 * supplied. Return string is locale encoded. */
341 char *isds_long_message(const struct isds_ctx *context) {
342 if (!context) return NULL;
343 return context->long_message;
347 /* Stores message into context' long_message buffer.
348 * Application can pick the message up using isds_long_message().
349 * NULL @message truncates the buffer but does not deallocate it.
350 * @message is coded in locale encoding */
351 _hidden isds_error isds_log_message(struct isds_ctx *context,
352 const char *message) {
353 char *buffer;
354 size_t length;
356 if (!context) return IE_INVALID_CONTEXT;
358 /* FIXME: Check for integer overflow */
359 length = 1 + ((message) ? strlen(message) : 0);
360 buffer = realloc(context->long_message, length);
361 if (!buffer) return IE_NOMEM;
363 if (message)
364 strcpy(buffer, message);
365 else
366 *buffer = '\0';
368 context->long_message = buffer;
369 return IE_SUCCESS;
373 /* Appends message into context' long_message buffer.
374 * Application can pick the message up using isds_long_message().
375 * NULL message has void effect. */
376 _hidden isds_error isds_append_message(struct isds_ctx *context,
377 const char *message) {
378 char *buffer;
379 size_t old_length, length;
381 if (!context) return IE_INVALID_CONTEXT;
382 if (!message) return IE_SUCCESS;
383 if (!context->long_message)
384 return isds_log_message(context, message);
386 old_length = strlen(context->long_message);
387 /* FIXME: Check for integer overflow */
388 length = 1 + old_length + strlen(message);
389 buffer = realloc(context->long_message, length);
390 if (!buffer) return IE_NOMEM;
392 strcpy(buffer + old_length, message);
394 context->long_message = buffer;
395 return IE_SUCCESS;
399 /* Stores formated message into context' long_message buffer.
400 * Application can pick the message up using isds_long_message(). */
401 _hidden isds_error isds_printf_message(struct isds_ctx *context,
402 const char *format, ...) {
403 va_list ap;
404 int length;
406 if (!context) return IE_INVALID_CONTEXT;
407 va_start(ap, format);
408 length = isds_vasprintf(&(context->long_message), format, ap);
409 va_end(ap);
411 return (length < 0) ? IE_ERROR: IE_SUCCESS;
415 /* Set logging up.
416 * @facilities is bitmask of isds_log_facility values,
417 * @level is verbosity level. */
418 void isds_set_logging(const unsigned int facilities,
419 const isds_log_level level) {
420 log_facilities = facilities;
421 log_level = level;
425 /* Log @message in class @facility with log @level into global log. @message
426 * is printf(3) formating string, variadic arguments may be neccessary.
427 * For debugging purposes. */
428 _hidden isds_error isds_log(const isds_log_facility facility,
429 const isds_log_level level, const char *message, ...) {
430 va_list ap;
432 if (level > log_level) return IE_SUCCESS;
433 if (!(log_facilities & facility)) return IE_SUCCESS;
434 if (!message) return IE_INVAL;
436 /* TODO: Allow to register output function privided by application
437 * (e.g. fprintf to stderr or copy to text area GUI widget). */
439 va_start(ap, message);
440 vfprintf(stderr, message, ap);
441 va_end(ap);
442 /* Line buffered printf is default.
443 * fflush(stderr);*/
445 return IE_SUCCESS;
449 /* Connect to given url.
450 * It just makes TCP connection to ISDS server found in @url hostname part. */
451 /*int isds_connect(struct isds_ctx *context, const char *url);*/
453 /* Set timeout in miliseconds for each network job like connecting to server
454 * or sending message. Use 0 to disable timeout limits. */
455 isds_error isds_set_timeout(struct isds_ctx *context,
456 const unsigned int timeout) {
457 if (!context) return IE_INVALID_CONTEXT;
459 context->timeout = timeout;
461 if (context->curl) {
462 CURLcode curl_err;
464 curl_err = curl_easy_setopt(context->curl, CURLOPT_NOSIGNAL, 1);
465 if (!curl_err)
466 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT_MS,
467 context->timeout);
468 if (curl_err) return IE_ERROR;
471 return IE_SUCCESS;
475 /* Change SSL/TLS settings.
476 * @context is context which setting will be applied to
477 * @option is name of option. It determines the type of last argument. See
478 * isds_tls_option definition for more info.
479 * @... is value of new setting. Type is determined by @option
480 * */
481 isds_error isds_set_tls(struct isds_ctx *context, const isds_tls_option option,
482 ...) {
483 isds_error err = IE_SUCCESS;
484 va_list ap;
485 char *pointer, *string;
487 if (!context) return IE_INVALID_CONTEXT;
489 va_start(ap, option);
491 #define REPLACE_VA_STRING(destination) \
492 string = va_arg(ap, char *); \
493 if (string) { \
494 pointer = realloc((destination), 1 + strlen(string)); \
495 if (!pointer) { err = IE_NOMEM; goto leave; } \
496 strcpy(pointer, string); \
497 (destination) = pointer; \
498 } else { \
499 free(destination); \
500 (destination) = NULL; \
503 switch (option) {
504 case ITLS_VERIFY_SERVER:
505 if (!context->tls_verify_server) {
506 context->tls_verify_server =
507 malloc(sizeof(*context->tls_verify_server));
508 if (!context->tls_verify_server) {
509 err = IE_NOMEM; goto leave;
512 *context->tls_verify_server = (_Bool) (0 != va_arg(ap, int));
513 break;
515 case ITLS_CA_FILE:
516 REPLACE_VA_STRING(context->tls_ca_file);
517 break;
518 case ITLS_CA_DIRECTORY:
519 REPLACE_VA_STRING(context->tls_ca_dir);
520 break;
522 default:
523 err = IE_ENUM; goto leave;
526 #undef REPLACE_VA_STRING
528 leave:
529 va_end(ap);
530 return err;
534 /* Discard credentials.
535 * Only that. It does not cause log out, connection close or similar. */
536 static isds_error discard_credentials(struct isds_ctx *context) {
537 if(!context) return IE_INVALID_CONTEXT;
539 if (context->username) {
540 memset(context->username, 0, strlen(context->username));
541 free(context->username);
542 context->username = NULL;
544 if (context->password) {
545 memset(context->password, 0, strlen(context->password));
546 free(context->password);
547 context->password = NULL;
550 return IE_SUCCESS;
554 /* Connect and log in into ISDS server.
555 * @url is address of ISDS web service
556 * @username is user name of ISDS user
557 * @password is user's secret password
558 * @certificate is NULL terminated string with PEM formated client's
559 * certificate. Use NULL if only password autentication should be performed.
560 * @key is private key for client's certificate as (base64 encoded?) NULL
561 * terminated string. Use NULL if only password autentication is desired.
562 * */
563 isds_error isds_login(struct isds_ctx *context, const char *url,
564 const char *username, const char *password,
565 const char *certificate, const char* key) {
566 isds_error err = IE_NOT_LOGGED_IN;
567 isds_error soap_err;
568 xmlNsPtr isds_ns = NULL;
569 xmlNodePtr request = NULL;
570 xmlNodePtr response = NULL;
572 if (!context) return IE_INVALID_CONTEXT;
573 if (!url || !username || !password) return IE_INVAL;
574 if (certificate || key) return IE_NOTSUP;
576 /* Store configuration */
577 free(context->url);
578 context->url = strdup(url);
579 if (!(context->url))
580 return IE_NOMEM;
582 /* Close connection if already logged in */
583 if (context->curl) {
584 close_connection(context);
587 /* Prepare CURL handle */
588 context->curl = curl_easy_init();
589 if (!(context->curl))
590 return IE_ERROR;
592 /* Build login request */
593 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
594 if (!request) {
595 isds_log_message(context, _("Could build ISDS login request"));
596 return IE_ERROR;
598 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
599 if(!isds_ns) {
600 isds_log_message(context, _("Could not create ISDS name space"));
601 xmlFreeNode(request);
602 return IE_ERROR;
604 xmlSetNs(request, isds_ns);
606 /* Store credentials */
607 /* FIXME: mlock password
608 * (I have a library) */
609 discard_credentials(context);
610 context->username = strdup(username);
611 context->password = strdup(password);
612 if (!(context->username && context->password)) {
613 discard_credentials(context);
614 xmlFreeNode(request);
615 return IE_NOMEM;
618 isds_log(ILF_ISDS, ILL_DEBUG, _("Logging user %s into server %s\n"),
619 username, url);
621 /* Send login request */
622 soap_err = soap(context, "dz", request, &response);
624 /* Remove credentials */
625 discard_credentials(context);
627 /* Destroy login request */
628 xmlFreeNode(request);
630 if (soap_err) {
631 xmlFreeNodeList(response);
632 close_connection(context);
633 return soap_err;
636 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
637 * authentication succeeded if soap_err == IE_SUCCESS */
638 err = IE_SUCCESS;
640 xmlFreeNodeList(response);
642 if (!err)
643 isds_log(ILF_ISDS, ILL_DEBUG,
644 _("User %s has been logged into server %s successfully\n"),
645 username, url);
646 return err;
650 /* Log out from ISDS server discards credentials and connection configuration. */
651 isds_error isds_logout(struct isds_ctx *context) {
652 if (!context) return IE_INVALID_CONTEXT;
654 /* Close connection */
655 if (context->curl) {
656 close_connection(context);
658 /* Discard credentials for sure. They should not survive isds_login(),
659 * even successful .*/
660 discard_credentials(context);
661 free(context->url);
662 context->url = NULL;
664 isds_log(ILF_ISDS, ILL_DEBUG, _("Logged out from ISDS server\n"));
665 } else {
666 discard_credentials(context);
668 return IE_SUCCESS;
672 /* Verify connection to ISDS is alive and server is responding.
673 * Sent dumy request to ISDS and expect dummy response. */
674 isds_error isds_ping(struct isds_ctx *context) {
675 isds_error soap_err;
676 xmlNsPtr isds_ns = NULL;
677 xmlNodePtr request = NULL;
678 xmlNodePtr response = NULL;
680 if (!context) return IE_INVALID_CONTEXT;
682 /* Check if connection is established */
683 if (!context->curl) return IE_CONNECTION_CLOSED;
686 /* Build dummy request */
687 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
688 if (!request) {
689 isds_log_message(context, _("Could build ISDS dummy request"));
690 return IE_ERROR;
692 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
693 if(!isds_ns) {
694 isds_log_message(context, _("Could not create ISDS name space"));
695 xmlFreeNode(request);
696 return IE_ERROR;
698 xmlSetNs(request, isds_ns);
700 isds_log(ILF_ISDS, ILL_DEBUG, _("Pinging ISDS server\n"));
702 /* Sent dummy request */
703 soap_err = soap(context, "dz", request, &response);
705 /* Destroy login request */
706 xmlFreeNode(request);
708 if (soap_err) {
709 isds_log(ILF_ISDS, ILL_DEBUG,
710 _("ISDS server could not be contacted\n"));
711 xmlFreeNodeList(response);
712 close_connection(context);
713 return soap_err;
716 /* XXX: Untill we don't propagate HTTP code 500 or 4xx, we can be sure
717 * authentication succeeded if soap_err == IE_SUCCESS */
718 /* TODO: ISDS documentation does not specify response body.
719 * However real server sends back DummyOperationResponse */
722 xmlFreeNodeList(response);
724 isds_log(ILF_ISDS, ILL_DEBUG, _("ISDS server alive\n"));
726 return IE_SUCCESS;
730 /* Send bogus request to ISDS.
731 * Just for test purposes */
732 isds_error isds_bogus_request(struct isds_ctx *context) {
733 isds_error err;
734 xmlNsPtr isds_ns = NULL;
735 xmlNodePtr request = NULL;
736 xmlDocPtr response = NULL;
737 xmlChar *code = NULL, *message = NULL;
739 if (!context) return IE_INVALID_CONTEXT;
741 /* Check if connection is established */
742 if (!context->curl) {
743 /* Testing printf message */
744 isds_printf_message(context, "%s", _("I said connection closed"));
745 return IE_CONNECTION_CLOSED;
749 /* Build dummy request */
750 request = xmlNewNode(NULL, BAD_CAST "X-BogusOperation");
751 if (!request) {
752 isds_log_message(context, _("Could build ISDS bogus request"));
753 return IE_ERROR;
755 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
756 if(!isds_ns) {
757 isds_log_message(context, _("Could not create ISDS name space"));
758 xmlFreeNode(request);
759 return IE_ERROR;
761 xmlSetNs(request, isds_ns);
763 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending bogus request to ISDS\n"));
765 /* Sent bogus request */
766 err = isds(context, SERVICE_DM_OPERATIONS, request, &response);
768 /* Destroy request */
769 xmlFreeNode(request);
771 if (err) {
772 isds_log(ILF_ISDS, ILL_DEBUG,
773 _("Processing ISDS response on bogus request failed\n"));
774 xmlFreeDoc(response);
775 return err;
778 /* Check for response status */
779 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
780 &code, &message, NULL);
781 if (err) {
782 isds_log(ILF_ISDS, ILL_DEBUG,
783 _("ISDS response on bogus request is missing status\n"));
784 free(code);
785 free(message);
786 xmlFreeDoc(response);
787 return err;
789 if (xmlStrcmp(code, BAD_CAST "0000")) {
790 char *code_locale = utf82locale((char*)code);
791 char *message_locale = utf82locale((char*)message);
792 isds_log(ILF_ISDS, ILL_DEBUG,
793 _("Server refused bogus request (code=%s, message=%s)\n"),
794 code_locale, message_locale);
795 /* XXX: Literal error messages from ISDS are Czech mesages
796 * (English sometimes) in UTF-8. It's hard to catch them for
797 * translation. Successfully gettextized would return in locale
798 * encoding, unsuccessfully translated would pass in UTF-8. */
799 isds_log_message(context, message_locale);
800 free(code_locale);
801 free(message_locale);
802 free(code);
803 free(message);
804 xmlFreeDoc(response);
805 return IE_ISDS;
809 free(code);
810 free(message);
811 xmlFreeDoc(response);
813 isds_log(ILF_ISDS, ILL_DEBUG,
814 _("Bogus message accepted by server. This should not happen.\n"));
816 return IE_SUCCESS;
820 /* Serialize XML subtree to buffer preserving XML indentatition.
821 * @context is session context
822 * @subtree is XML element to be serialized (with childern)
823 * @buffer is automarically reallocated buffer where serialize to
824 * @length is size of serialized stream in bytes
825 * @return standard error code, free @buffer in case of error */
826 static isds_error serialize_subtree(struct isds_ctx *context,
827 xmlNodePtr subtree, void **buffer, size_t *length) {
828 isds_error err = IE_SUCCESS;
829 xmlBufferPtr xml_buffer = NULL;
830 xmlSaveCtxtPtr save_ctx = NULL;
831 xmlDocPtr subtree_doc = NULL;
832 xmlNodePtr subtree_copy;
833 xmlNsPtr isds_ns;
834 void *new_buffer;
836 if (!context) return IE_INVALID_CONTEXT;
837 if (!buffer) return IE_INVAL;
838 zfree(*buffer);
839 if (!subtree || !length) return IE_INVAL;
841 /* Make temporary XML document with @subtree root element */
842 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
843 * It can result in not well-formed on invalid XML tree (e.g. name space
844 * prefix definition can miss. */
845 /*FIXME */
847 subtree_doc = xmlNewDoc(BAD_CAST "1.0");
848 if (!subtree_doc) {
849 isds_log_message(context, _("Could not build temporary document"));
850 err = IE_ERROR;
851 goto leave;
854 /* XXX: Copy subtree and attach the copy to document.
855 * One node can not bee attached into more document at the same time.
856 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
857 * automatically.
858 * XXX: Check xmlSaveTree() too. */
859 subtree_copy = xmlCopyNodeList(subtree);
860 if (!subtree_copy) {
861 isds_log_message(context, _("Could not copy subtree"));
862 err = IE_ERROR;
863 goto leave;
865 xmlDocSetRootElement(subtree_doc, subtree_copy);
867 /* Only this way we get namespace definition as @xmlns:isds,
868 * otherwise we get namespace prefix without definition */
869 /* FIXME: Don't overwrite original default namespace */
870 isds_ns = xmlNewNs(subtree_copy, BAD_CAST ISDS_NS, NULL);
871 if(!isds_ns) {
872 isds_log_message(context, _("Could not create ISDS name space"));
873 err = IE_ERROR;
874 goto leave;
876 xmlSetNs(subtree_copy, isds_ns);
879 /* Serialize the document into buffer */
880 xml_buffer = xmlBufferCreate();
881 if (!xml_buffer) {
882 isds_log_message(context, _("Could not create xmlBuffer"));
883 err = IE_ERROR;
884 goto leave;
886 /* Last argument 0 means to not format the XML tree */
887 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8", 0);
888 if (!save_ctx) {
889 isds_log_message(context, _("Could not create XML serializer"));
890 err = IE_ERROR;
891 goto leave;
893 /* XXX: According LibXML documentation, this function does not return
894 * meaningfull value yet */
895 xmlSaveDoc(save_ctx, subtree_doc);
896 if (-1 == xmlSaveFlush(save_ctx)) {
897 isds_log_message(context,
898 _("Could not serialize XML subtree"));
899 err = IE_ERROR;
900 goto leave;
902 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
903 * even after xmlSaveFlush(). Thus close it here */
904 xmlSaveClose(save_ctx); save_ctx = NULL;
907 /* Store and detach buffer from xml_buffer */
908 *buffer = xml_buffer->content;
909 *length = xml_buffer->use;
910 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
912 /* Shrink buffer */
913 new_buffer = realloc(*buffer, *length);
914 if (new_buffer) *buffer = new_buffer;
916 leave:
917 if (err) {
918 zfree(*buffer);
919 *length = 0;
922 xmlSaveClose(save_ctx);
923 xmlBufferFree(xml_buffer);
924 xmlFreeDoc(subtree_doc); /* Frees subtree_copy, isds_ns etc. */
925 return err;
929 /* Dump XML subtree to buffer as literral string, not valid XML possibly.
930 * @context is session context
931 * @document is original document where @nodeset points to
932 * @nodeset is XPath node set to dump (recursively)
933 * @buffer is automarically reallocated buffer where serialize to
934 * @length is size of serialized stream in bytes
935 * @return standard error code, free @buffer in case of error */
936 static isds_error dump_nodeset(struct isds_ctx *context,
937 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
938 void **buffer, size_t *length) {
939 isds_error err = IE_SUCCESS;
940 xmlBufferPtr xml_buffer = NULL;
941 void *new_buffer;
943 if (!context) return IE_INVALID_CONTEXT;
944 if (!buffer) return IE_INVAL;
945 zfree(*buffer);
946 if (!document || !nodeset || !length) return IE_INVAL;
947 *length = 0;
949 /* Empty node set results into NULL buffer */
950 if (xmlXPathNodeSetIsEmpty(nodeset)) {
951 goto leave;
954 /* Resuling the document into buffer */
955 xml_buffer = xmlBufferCreate();
956 if (!xml_buffer) {
957 isds_log_message(context, _("Could not create xmlBuffer"));
958 err = IE_ERROR;
959 goto leave;
962 /* Itearate over all nodes */
963 for (int i = 0; i < nodeset->nodeNr; i++) {
964 /* Serialize node.
965 * XXX: xmlNodeDump() appends to xml_buffer. */
966 if (-1 ==
967 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
968 isds_log_message(context, _("Could not dump XML node"));
969 err = IE_ERROR;
970 goto leave;
974 /* Store and detach buffer from xml_buffer */
975 *buffer = xml_buffer->content;
976 *length = xml_buffer->use;
977 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
979 /* Shrink buffer */
980 new_buffer = realloc(*buffer, *length);
981 if (new_buffer) *buffer = new_buffer;
984 leave:
985 if (err) {
986 zfree(*buffer);
987 *length = 0;
990 xmlBufferFree(xml_buffer);
991 return err;
995 /* Convert UTF-8 @string represantion of ISDS dbType to enum @type */
996 static isds_error string2isds_DbType(xmlChar *string, isds_DbType *type) {
997 if (!string || !type) return IE_INVAL;
999 if (!xmlStrcmp(string, BAD_CAST "FO"))
1000 *type = DBTYPE_FO;
1001 else if (!xmlStrcmp(string, BAD_CAST "PFO"))
1002 *type = DBTYPE_PFO;
1003 else if (!xmlStrcmp(string, BAD_CAST "PFO_ADVOK"))
1004 *type = DBTYPE_PFO_ADVOK;
1005 else if (!xmlStrcmp(string, BAD_CAST "PFO_DANPOR"))
1006 *type = DBTYPE_PFO_DANPOR;
1007 else if (!xmlStrcmp(string, BAD_CAST "PFO_INSSPR"))
1008 *type = DBTYPE_PFO_INSSPR;
1009 else if (!xmlStrcmp(string, BAD_CAST "PO"))
1010 *type = DBTYPE_PO;
1011 else if (!xmlStrcmp(string, BAD_CAST "PO_ZAK"))
1012 *type = DBTYPE_PO_ZAK;
1013 else if (!xmlStrcmp(string, BAD_CAST "PO_REQ"))
1014 *type = DBTYPE_PO_REQ;
1015 else if (!xmlStrcmp(string, BAD_CAST "OVM"))
1016 *type = DBTYPE_OVM;
1017 else if (!xmlStrcmp(string, BAD_CAST "OVM_NOTAR"))
1018 *type = DBTYPE_OVM_NOTAR;
1019 else if (!xmlStrcmp(string, BAD_CAST "OVM_EXEKUT"))
1020 *type = DBTYPE_OVM_EXEKUT;
1021 else if (!xmlStrcmp(string, BAD_CAST "OVM_REQ"))
1022 *type = DBTYPE_OVM_REQ;
1023 else
1024 return IE_ENUM;
1025 return IE_SUCCESS;
1029 /* Convert ISDS dbType enum @type to UTF-8 string.
1030 * @Return pointer to static string, or NULL if unkwnow enum value */
1031 static const xmlChar *isds_DbType2string(const isds_DbType type) {
1032 switch(type) {
1033 case DBTYPE_FO: return(BAD_CAST "FO"); break;
1034 case DBTYPE_PFO: return(BAD_CAST "PFO"); break;
1035 case DBTYPE_PFO_ADVOK: return(BAD_CAST "PFO_ADVOK"); break;
1036 case DBTYPE_PFO_DANPOR: return(BAD_CAST "PFO_DANPOR"); break;
1037 case DBTYPE_PFO_INSSPR: return(BAD_CAST "PFO_INSSPR"); break;
1038 case DBTYPE_PO: return(BAD_CAST "PO"); break;
1039 case DBTYPE_PO_ZAK: return(BAD_CAST "PO_ZAK"); break;
1040 case DBTYPE_PO_REQ: return(BAD_CAST "PO_REQ"); break;
1041 case DBTYPE_OVM: return(BAD_CAST "OVM"); break;
1042 case DBTYPE_OVM_NOTAR: return(BAD_CAST "OVM_NOTAR"); break;
1043 case DBTYPE_OVM_EXEKUT: return(BAD_CAST "OVM_EXEKUT"); break;
1044 case DBTYPE_OVM_REQ: return(BAD_CAST "OVM_REQ"); break;
1045 default: return NULL; break;
1050 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
1051 * @Return pointer to static string, or NULL if unkwnow enum value */
1052 static const xmlChar *isds_FileMetaType2string(const isds_FileMetaType type) {
1053 switch(type) {
1054 case FILEMETATYPE_MAIN: return(BAD_CAST "main"); break;
1055 case FILEMETATYPE_ENCLOSURE: return(BAD_CAST "enclosure"); break;
1056 case FILEMETATYPE_SIGNATURE: return(BAD_CAST "signature"); break;
1057 case FILEMETATYPE_META: return(BAD_CAST "meta"); break;
1058 default: return NULL; break;
1063 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
1064 * @Return IE_ENUM if @string is not valid enum member */
1065 static isds_error string2isds_FileMetaType(const xmlChar *string,
1066 isds_FileMetaType *type) {
1067 if (!string || !type) return IE_INVAL;
1069 if (!xmlStrcmp(string, BAD_CAST "main"))
1070 *type = FILEMETATYPE_MAIN;
1071 else if (!xmlStrcmp(string, BAD_CAST "enclosure"))
1072 *type = FILEMETATYPE_ENCLOSURE;
1073 else if (!xmlStrcmp(string, BAD_CAST "signature"))
1074 *type = FILEMETATYPE_SIGNATURE;
1075 else if (!xmlStrcmp(string, BAD_CAST "meta"))
1076 *type = FILEMETATYPE_META;
1077 else
1078 return IE_ENUM;
1079 return IE_SUCCESS;
1083 /* Convert UTF-8 @string to ISDS hash @algorithm.
1084 * @Return IE_ENUM if @string is not valid enum member */
1085 static isds_error string2isds_hash_algorithm(const xmlChar *string,
1086 isds_hash_algorithm *algorithm) {
1087 if (!string || !algorithm) return IE_INVAL;
1089 if (!xmlStrcmp(string, BAD_CAST "MD5"))
1090 *algorithm = HASH_ALGORITHM_MD5;
1091 else if (!xmlStrcmp(string, BAD_CAST "SHA-1"))
1092 *algorithm = HASH_ALGORITHM_SHA_1;
1093 else if (!xmlStrcmp(string, BAD_CAST "SHA-256"))
1094 *algorithm = HASH_ALGORITHM_SHA_256;
1095 else if (!xmlStrcmp(string, BAD_CAST "SHA-512"))
1096 *algorithm = HASH_ALGORITHM_SHA_512;
1097 else
1098 return IE_ENUM;
1099 return IE_SUCCESS;
1103 /* Convert UTF-8 @string represantion of ISO 8601 date to @time.
1104 * XXX: Not all ISO formats are supported */
1105 static isds_error datestring2tm(const xmlChar *string, struct tm *time) {
1106 char *offset;
1107 if (!string || !time) return IE_INVAL;
1109 /* xsd:date is ISO 8601 string, thus ASCII */
1110 offset = strptime((char*)string, "%Y-%m-%d", time);
1111 if (offset && *offset == '\0')
1112 return IE_SUCCESS;
1114 offset = strptime((char*)string, "%Y%m%d", time);
1115 if (offset && *offset == '\0')
1116 return IE_SUCCESS;
1118 offset = strptime((char*)string, "%Y-%j", time);
1119 if (offset && *offset == '\0')
1120 return IE_SUCCESS;
1122 return IE_NOTSUP;
1126 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
1127 static isds_error tm2datestring(const struct tm *time, xmlChar **string) {
1128 if (!time || !string) return IE_INVAL;
1130 if (-1 == isds_asprintf((char **) string, "%d-%02d-%02d",
1131 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
1132 return IE_ERROR;
1134 return IE_SUCCESS;
1138 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
1139 * respects the @time microseconds too. */
1140 static isds_error timeval2timestring(const struct timeval *time,
1141 xmlChar **string) {
1142 struct tm broken;
1144 if (!time || !string) return IE_INVAL;
1146 if (!gmtime_r(&time->tv_sec, &broken)) return IE_DATE;
1147 if (time->tv_usec < 0 || time->tv_usec > 999999) return IE_DATE;
1149 /* TODO: small negative year should be formated as "-0012". This is not
1150 * true for glibc "%04d". We should implement it.
1151 * TODO: What's type of time->tv_usec exactly? Unsigned? Absolute?
1152 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
1153 if (-1 == isds_asprintf((char **) string,
1154 "%04d-%02d-%02dT%02d:%02d:%02d.%06ld",
1155 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
1156 broken.tm_hour, broken.tm_min, broken.tm_sec,
1157 time->tv_usec))
1158 return IE_ERROR;
1160 return IE_SUCCESS;
1164 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
1165 * It respects microseconds too.
1166 * In case of error, @time will be freed. */
1167 static isds_error timestring2timeval(const xmlChar *string,
1168 struct timeval **time) {
1169 struct tm broken;
1170 char *offset, *delim, *endptr;
1171 char subseconds[7];
1172 int offset_hours, offset_minutes;
1173 int i;
1175 if (!time) return IE_INVAL;
1177 memset(&broken, 0, sizeof(broken));
1179 if (!*time) {
1180 *time = calloc(1, sizeof(**time));
1181 if (!*time) return IE_NOMEM;
1182 } else {
1183 memset(*time, 0, sizeof(**time));
1187 /* xsd:date is ISO 8601 string, thus ASCII */
1188 /*TODO: negative year */
1190 /* Parse date and time without subseconds and offset */
1191 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
1192 if (!offset) {
1193 free(*time); *time = NULL;
1194 return IE_DATE;
1197 /* Get subseconds */
1198 if (*offset == '.' ) {
1199 offset++;
1201 /* Copy first 6 digits, padd it with zeros.
1202 * XXX: It truncates longer number, no round.
1203 * Current server implementation uses only milisecond resolution. */
1204 /* TODO: isdigit() is locale sensitive */
1205 for (i = 0;
1206 i < sizeof(subseconds)/sizeof(char) - 1 && isdigit(*offset);
1207 i++, offset++) {
1208 subseconds[i] = *offset;
1210 for (; i < sizeof(subseconds)/sizeof(char) - 1; i++) {
1211 subseconds[i] = '0';
1213 subseconds[6] = '\0';
1215 /* Convert it into integer */
1216 (*time)->tv_usec = strtol(subseconds, &endptr, 10);
1217 if (*endptr != '\0' || (*time)->tv_usec == LONG_MIN ||
1218 (*time)->tv_usec == LONG_MAX) {
1219 free(*time); *time = NULL;
1220 return IE_DATE;
1223 /* move to the zone offset delimiter */
1224 delim = strchr(offset, '-');
1225 if (!delim)
1226 delim = strchr(offset, '+');
1227 offset = delim;
1230 /* Get zone offset */
1231 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
1232 * "" equals to "Z" and it means UTC zone. */
1233 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
1234 * colon separator */
1235 if (*offset == '-' || *offset == '+') {
1236 offset++;
1237 if (2 != sscanf(offset, "%2d:%2d", &offset_hours, &offset_minutes)) {
1238 free(*time); *time = NULL;
1239 return IE_DATE;
1241 broken.tm_hour -= offset_hours;
1242 broken.tm_min -= offset_minutes * ((offset_hours<0) ? -1 : 1);
1245 /* Convert to time_t */
1246 switch_tz_to_utc();
1247 (*time)->tv_sec = mktime(&broken);
1248 switch_tz_to_native();
1249 if ((*time)->tv_sec == (time_t) -1) {
1250 free(*time); *time = NULL;
1251 return IE_DATE;
1254 return IE_SUCCESS;
1258 /* Convert unsigned int into isds_message_status.
1259 * @context is session context
1260 * @number is pointer to number value. NULL will be treated as invalid value.
1261 * @status is automatically reallocated status
1262 * @return IE_SUCCESS, or error code and free status */
1263 static isds_error uint2isds_message_status(struct isds_ctx *context,
1264 const unsigned long int *number, isds_message_status **status) {
1265 if (!context) return IE_INVALID_CONTEXT;
1266 if (!status) return IE_INVAL;
1268 free(*status); *status = NULL;
1269 if (!number) return IE_INVAL;
1271 if (*number < 1 || *number > 9) {
1272 isds_printf_message(context, _("Invalid messsage status value: %lu"),
1273 *number);
1274 return IE_ENUM;
1277 *status = malloc(sizeof(**status));
1278 if (!*status) return IE_NOMEM;
1280 **status = 1 << *number;
1281 return IE_SUCCESS;
1285 /* Convert event description string into isds_event memebers type and
1286 * description
1287 * @string is raw event decsription starting with event prefix
1288 * @event is structure where to store type and stripped description to
1289 * @return standard error code, unkown prefix is not classified as an error. */
1290 static isds_error eventstring2event(const xmlChar *string,
1291 struct isds_event* event) {
1292 const xmlChar *known_prefixes[] = {
1293 BAD_CAST "EV1:",
1294 BAD_CAST "EV2:",
1295 BAD_CAST "EV3:"
1297 const isds_event_type types[] = {
1298 EVENT_ACCEPTED_BY_RECIPIENT,
1299 EVENT_DELIVERED_BY_FICTION,
1300 EVENT_UNDELIVERABLE
1302 unsigned int index;
1303 size_t length;
1305 if (!string || !event) return IE_INVAL;
1307 if (!event->type) {
1308 event->type = malloc(sizeof(*event->type));
1309 if (!(event->type)) return IE_NOMEM;
1311 zfree(event->description);
1313 for (index = 0; index < sizeof(known_prefixes)/sizeof(known_prefixes[0]);
1314 index++) {
1315 length = xmlUTF8Strlen(known_prefixes[index]);
1317 if (xmlStrncmp(string, known_prefixes[index], length)) {
1318 /* Prefix is known */
1319 *event->type = types[index];
1321 /* Strip prefix from description and spaces */
1322 /* TODO: Recognize all wite spaces from UCS blank class and
1323 * operate on UTF-8 chars. */
1324 for (; string[length] != '\0' && string[length] == ' '; length++);
1325 event->description = strdup((char *) (string + length));
1326 if (!(event->description)) return IE_NOMEM;
1328 return IE_SUCCESS;
1332 /* Unknown event prefix.
1333 * XSD allows any string */
1334 char *string_locale = utf82locale((char *) string);
1335 isds_log(ILF_ISDS, ILL_WARNING,
1336 _("Uknown delivery info event prefix: %s\n"), string_locale);
1337 free(string_locale);
1339 *event->type = EVENT_UKNOWN;
1340 event->description = strdup((char *) string);
1341 if (!(event->description)) return IE_NOMEM;
1343 return IE_SUCCESS;
1347 /* Following EXTRACT_* macros expects @result, @xpath_ctx, @err, @context
1348 * and leave lable */
1349 #define EXTRACT_STRING(element, string) \
1350 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
1351 if (!result) { \
1352 err = IE_ERROR; \
1353 goto leave; \
1355 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
1356 if (result->nodesetval->nodeNr > 1) { \
1357 isds_log_message(context, _("Multiple " element " element")); \
1358 err = IE_ERROR; \
1359 goto leave; \
1361 (string) = (char *) \
1362 xmlXPathCastNodeSetToString(result->nodesetval); \
1363 if (!(string)) { \
1364 err = IE_ERROR; \
1365 goto leave; \
1369 #define EXTRACT_BOOLEAN(element, booleanPtr) \
1371 char *string = NULL; \
1372 EXTRACT_STRING(element, string); \
1374 if (string) { \
1375 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
1376 if (!(booleanPtr)) { \
1377 free(string); \
1378 err = IE_NOMEM; \
1379 goto leave; \
1382 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
1383 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
1384 *(booleanPtr) = 1; \
1385 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
1386 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
1387 *(booleanPtr) = 0; \
1388 else { \
1389 char *string_locale = utf82locale((char*)string); \
1390 isds_printf_message(context, \
1391 _(element " value is not valid boolean: "), \
1392 string_locale); \
1393 free(string_locale); \
1394 free(string); \
1395 err = IE_ERROR; \
1396 goto leave; \
1399 free(string); \
1403 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
1405 char *string = NULL; \
1406 EXTRACT_STRING(element, string); \
1407 if (string) { \
1408 long int number; \
1409 char *endptr; \
1411 number = strtol((char*)string, &endptr, 10); \
1413 if (*endptr != '\0') { \
1414 char *string_locale = utf82locale((char *)string); \
1415 isds_printf_message(context, \
1416 _(element" is not valid integer: %s"), \
1417 string_locale); \
1418 free(string_locale); \
1419 free(string); \
1420 err = IE_ISDS; \
1421 goto leave; \
1424 if (number == LONG_MIN || number == LONG_MAX) { \
1425 char *string_locale = utf82locale((char *)string); \
1426 isds_printf_message(context, \
1427 _(element " value out of range of long int: %s"), \
1428 string_locale); \
1429 free(string_locale); \
1430 free(string); \
1431 err = IE_ERROR; \
1432 goto leave; \
1435 free(string); string = NULL; \
1437 if (!(preallocated)) { \
1438 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
1439 if (!(longintPtr)) { \
1440 err = IE_NOMEM; \
1441 goto leave; \
1444 *(longintPtr) = number; \
1448 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
1450 char *string = NULL; \
1451 EXTRACT_STRING(element, string); \
1452 if (string) { \
1453 long int number; \
1454 char *endptr; \
1456 number = strtol((char*)string, &endptr, 10); \
1458 if (*endptr != '\0') { \
1459 char *string_locale = utf82locale((char *)string); \
1460 isds_printf_message(context, \
1461 _(element" is not valid integer: %s"), \
1462 string_locale); \
1463 free(string_locale); \
1464 free(string); \
1465 err = IE_ISDS; \
1466 goto leave; \
1469 if (number == LONG_MIN || number == LONG_MAX) { \
1470 char *string_locale = utf82locale((char *)string); \
1471 isds_printf_message(context, \
1472 _(element " value out of range of long int: %s"), \
1473 string_locale); \
1474 free(string_locale); \
1475 free(string); \
1476 err = IE_ERROR; \
1477 goto leave; \
1480 free(string); string = NULL; \
1481 if (number < 0) { \
1482 isds_printf_message(context, \
1483 _(element " value is negative: %ld"), number); \
1484 err = IE_ERROR; \
1485 goto leave; \
1488 if (!(preallocated)) { \
1489 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
1490 if (!(ulongintPtr)) { \
1491 err = IE_NOMEM; \
1492 goto leave; \
1495 *(ulongintPtr) = number; \
1499 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) \
1500 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
1501 NULL); \
1502 if ((required) && (!string)) { \
1503 char *attribute_locale = utf82locale(attribute); \
1504 char *element_locale = utf82locale((char *)xpath_ctx->node->name); \
1505 isds_printf_message(context, \
1506 _("Could not extract required %s attribute value from " \
1507 "%s element"), attribute_locale, element_locale); \
1508 free(element_locale); \
1509 free(attribute_locale); \
1510 err = IE_ERROR; \
1511 goto leave; \
1515 #define INSERT_STRING(parent, element, string) \
1516 node = xmlNewTextChild(parent, NULL, BAD_CAST (element), \
1517 (xmlChar *) (string)); \
1518 if (!node) { \
1519 isds_printf_message(context, _("Could not add " element " child to " \
1520 "%s element"), (parent)->name); \
1521 err = IE_ERROR; \
1522 goto leave; \
1525 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
1526 if ((booleanPtr)) { \
1527 if (*(booleanPtr)) { INSERT_STRING(parent, element, "true"); } \
1528 else { INSERT_STRING(parent, element, "false") } \
1529 } else { INSERT_STRING(parent, element, NULL) }
1531 #define INSERT_LONGINT(parent, element, longintPtr, buffer) \
1532 if ((longintPtr)) { \
1533 /* FIXME: locale sensitive */ \
1534 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
1535 err = IE_NOMEM; \
1536 goto leave; \
1538 INSERT_STRING(parent, element, buffer) \
1539 free(buffer); (buffer) = NULL; \
1540 } else { INSERT_STRING(parent, element, NULL) }
1542 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) \
1543 if ((ulongintPtr)) { \
1544 /* FIXME: locale sensitive */ \
1545 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
1546 err = IE_NOMEM; \
1547 goto leave; \
1549 INSERT_STRING(parent, element, buffer) \
1550 free(buffer); (buffer) = NULL; \
1551 } else { INSERT_STRING(parent, element, NULL) }
1553 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
1554 /* FIXME: locale sensitive */ \
1555 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
1556 err = IE_NOMEM; \
1557 goto leave; \
1559 INSERT_STRING(parent, element, buffer) \
1560 free(buffer); (buffer) = NULL; \
1562 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
1563 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
1564 (xmlChar *) (string)); \
1565 if (!attribute_node) { \
1566 isds_printf_message(context, _("Could not add " attribute \
1567 " attribute to %s element"), (parent)->name); \
1568 err = IE_ERROR; \
1569 goto leave; \
1573 /* Find child element by name in given XPath context and switch context onto
1574 * it. The child must be uniq and must exist. Otherwise failes.
1575 * @context is ISDS context
1576 * @child is child element name
1577 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
1578 * into it child. In error case, the @xpath_ctx keeps original value. */
1579 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
1580 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
1581 isds_error err = IE_SUCCESS;
1582 xmlXPathObjectPtr result = NULL;
1584 if (!context) return IE_INVALID_CONTEXT;
1585 if (!child || !xpath_ctx) return IE_INVAL;
1587 /* Find child */
1588 result = xmlXPathEvalExpression(child, xpath_ctx);
1589 if (!result) {
1590 err = IE_XML;
1591 goto leave;
1594 /* No match */
1595 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
1596 char *parent_locale = utf82locale((char*) xpath_ctx->node->name);
1597 char *child_locale = utf82locale((char*) child);
1598 isds_printf_message(context,
1599 _("%s element does not contain %s child"),
1600 parent_locale, child_locale);
1601 free(child_locale);
1602 free(parent_locale);
1603 err = IE_NOEXIST;
1604 goto leave;
1607 /* More matches */
1608 if (result->nodesetval->nodeNr > 1) {
1609 char *parent_locale = utf82locale((char*) xpath_ctx->node->name);
1610 char *child_locale = utf82locale((char*) child);
1611 isds_printf_message(context,
1612 _("%s element contains multiple %s childs"),
1613 parent_locale, child_locale);
1614 free(child_locale);
1615 free(parent_locale);
1616 err = IE_NOTUNIQ;
1617 goto leave;
1620 /* Switch context */
1621 xpath_ctx->node = result->nodesetval->nodeTab[0];
1623 leave:
1624 xmlXPathFreeObject(result);
1625 return err;
1630 /* Convert isds:dBOwnerInfo XML tree into structure
1631 * @context is ISDS context
1632 * @db_owner_info is automically reallocated box owner info structure
1633 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
1634 * In case of error @db_owner_info will be freed. */
1635 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
1636 struct isds_DbOwnerInfo **db_owner_info,
1637 xmlXPathContextPtr xpath_ctx) {
1638 isds_error err = IE_SUCCESS;
1639 xmlXPathObjectPtr result = NULL;
1640 char *string = NULL;
1642 if (!context) return IE_INVALID_CONTEXT;
1643 if (!db_owner_info) return IE_INVAL;
1644 isds_DbOwnerInfo_free(db_owner_info);
1645 if (!xpath_ctx) return IE_INVAL;
1648 *db_owner_info = calloc(1, sizeof(**db_owner_info));
1649 if (!*db_owner_info) {
1650 err = IE_NOMEM;
1651 goto leave;
1654 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
1656 EXTRACT_STRING("isds:dbType", string);
1657 if (string) {
1658 (*db_owner_info)->dbType =
1659 calloc(1, sizeof(*((*db_owner_info)->dbType)));
1660 if (!(*db_owner_info)->dbType) {
1661 err = IE_NOMEM;
1662 goto leave;
1664 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
1665 if (err) {
1666 free((*db_owner_info)->dbType);
1667 (*db_owner_info)->dbType = NULL;
1668 if (err == IE_ENUM) {
1669 err = IE_ISDS;
1670 isds_printf_message(context, _("Unknown isds:dbType: %s"),
1671 (char *)string);
1673 goto leave;
1675 free(string); string = NULL;
1678 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
1680 (*db_owner_info)->personName =
1681 calloc(1, sizeof(*((*db_owner_info)->personName)));
1682 if (!(*db_owner_info)->personName) {
1683 err = IE_NOMEM;
1684 goto leave;
1686 EXTRACT_STRING("isds:pnFirstName",
1687 (*db_owner_info)->personName->pnFirstName);
1688 EXTRACT_STRING("isds:pnMiddleName",
1689 (*db_owner_info)->personName->pnMiddleName);
1690 EXTRACT_STRING("isds:pnLastName",
1691 (*db_owner_info)->personName->pnLastName);
1692 EXTRACT_STRING("isds:pnLastNameAtBirth",
1693 (*db_owner_info)->personName->pnLastNameAtBirth);
1694 if (!(*db_owner_info)->personName->pnFirstName &&
1695 !(*db_owner_info)->personName->pnMiddleName &&
1696 !(*db_owner_info)->personName->pnLastName &&
1697 !(*db_owner_info)->personName->pnLastNameAtBirth)
1698 isds_PersonName_free(&(*db_owner_info)->personName);
1700 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
1702 (*db_owner_info)->birthInfo =
1703 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
1704 if (!(*db_owner_info)->birthInfo) {
1705 err = IE_NOMEM;
1706 goto leave;
1708 EXTRACT_STRING("isds:biDate", string);
1709 if (string) {
1710 (*db_owner_info)->birthInfo->biDate =
1711 calloc(1, sizeof(*((*db_owner_info)->birthInfo->biDate)));
1712 if (!(*db_owner_info)->birthInfo->biDate) {
1713 err = IE_NOMEM;
1714 goto leave;
1716 err = datestring2tm((xmlChar *)string,
1717 (*db_owner_info)->birthInfo->biDate);
1718 if (err) {
1719 free((*db_owner_info)->birthInfo->biDate);
1720 (*db_owner_info)->birthInfo->biDate = NULL;
1721 if (err == IE_NOTSUP) {
1722 err = IE_ISDS;
1723 isds_printf_message(context,
1724 _("Invalid isds:biDate value: %s"), (char *)string);
1726 goto leave;
1728 free(string); string = NULL;
1730 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
1731 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
1732 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
1733 if (!(*db_owner_info)->birthInfo->biDate &&
1734 !(*db_owner_info)->birthInfo->biCity &&
1735 !(*db_owner_info)->birthInfo->biCounty &&
1736 !(*db_owner_info)->birthInfo->biState)
1737 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
1739 (*db_owner_info)->address =
1740 calloc(1, sizeof(*((*db_owner_info)->address)));
1741 if (!(*db_owner_info)->address) {
1742 err = IE_NOMEM;
1743 goto leave;
1745 EXTRACT_STRING("isds:adCity",
1746 (*db_owner_info)->address->adCity);
1747 EXTRACT_STRING("isds:adStreet",
1748 (*db_owner_info)->address->adStreet);
1749 EXTRACT_STRING("isds:adNumberInStreet",
1750 (*db_owner_info)->address->adNumberInStreet);
1751 EXTRACT_STRING("isds:adNumberInMunicipality",
1752 (*db_owner_info)->address->adNumberInMunicipality);
1753 EXTRACT_STRING("isds:adZipCode",
1754 (*db_owner_info)->address->adZipCode);
1755 EXTRACT_STRING("isds:adState",
1756 (*db_owner_info)->address->adState);
1757 if (!(*db_owner_info)->address->adCity &&
1758 !(*db_owner_info)->address->adStreet &&
1759 !(*db_owner_info)->address->adNumberInStreet &&
1760 !(*db_owner_info)->address->adNumberInMunicipality &&
1761 !(*db_owner_info)->address->adZipCode &&
1762 !(*db_owner_info)->address->adState)
1763 isds_Address_free(&(*db_owner_info)->address);
1765 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
1766 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
1767 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
1768 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
1769 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
1771 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
1773 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
1774 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
1775 (*db_owner_info)->dbOpenAddressing);
1777 leave:
1778 if (err) isds_DbOwnerInfo_free(db_owner_info);
1779 free(string);
1780 xmlXPathFreeObject(result);
1781 return err;
1785 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
1786 * isds_envelope structure. The envelope is automatically allocated but not
1787 * reallocated. The date are just appended into envelope structure.
1788 * @context is ISDS context
1789 * @envelope is automically allocated message envelope structure
1790 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
1791 * In case of error @envelope will be freed. */
1792 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
1793 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
1794 isds_error err = IE_SUCCESS;
1795 xmlXPathObjectPtr result = NULL;
1797 if (!context) return IE_INVALID_CONTEXT;
1798 if (!envelope) return IE_INVAL;
1799 if (!xpath_ctx) return IE_INVAL;
1802 if (!*envelope) {
1803 /* Allocate envelope */
1804 *envelope = calloc(1, sizeof(**envelope));
1805 if (!*envelope) {
1806 err = IE_NOMEM;
1807 goto leave;
1809 } else {
1810 /* Else free former data */
1811 zfree((*envelope)->dmSenderOrgUnit);
1812 zfree((*envelope)->dmSenderOrgUnitNum);
1813 zfree((*envelope)->dbIDRecipient);
1814 zfree((*envelope)->dmRecipientOrgUnit);
1815 zfree((*envelope)->dmSenderOrgUnitNum);
1816 zfree((*envelope)->dmToHands);
1817 zfree((*envelope)->dmAnnotation);
1818 zfree((*envelope)->dmRecipientRefNumber);
1819 zfree((*envelope)->dmSenderRefNumber);
1820 zfree((*envelope)->dmRecipientIdent);
1821 zfree((*envelope)->dmSenderIdent);
1822 zfree((*envelope)->dmLegalTitleLaw);
1823 zfree((*envelope)->dmLegalTitleYear);
1824 zfree((*envelope)->dmLegalTitleSect);
1825 zfree((*envelope)->dmLegalTitlePar);
1826 zfree((*envelope)->dmLegalTitlePoint);
1827 zfree((*envelope)->dmPersonalDelivery);
1828 zfree((*envelope)->dmAllowSubstDelivery);
1831 /* Extract envelope elements added by sender or ISDS
1832 * (XSD: gMessageEnvelopeSub type) */
1833 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
1834 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
1835 (*envelope)->dmSenderOrgUnitNum, 0);
1836 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
1837 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
1838 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
1839 (*envelope)->dmSenderOrgUnitNum, 0);
1840 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
1841 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
1842 EXTRACT_STRING("isds:dmRecipientRefNumber",
1843 (*envelope)->dmRecipientRefNumber);
1844 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
1845 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
1846 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
1848 /* Extract envelope elements regarding law refference */
1849 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
1850 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
1851 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
1852 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
1853 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
1855 /* Extract envelope other elements */
1856 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
1857 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
1858 (*envelope)->dmAllowSubstDelivery);
1860 leave:
1861 if (err) isds_envelope_free(envelope);
1862 xmlXPathFreeObject(result);
1863 return err;
1868 /* Convert XSD gMessageEnvelope group of elements from XML tree into
1869 * isds_envelope structure. The envelope is automatically allocated but not
1870 * reallocated. The date are just appended into envelope structure.
1871 * @context is ISDS context
1872 * @envelope is automically allocated message envelope structure
1873 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
1874 * In case of error @envelope will be freed. */
1875 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
1876 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
1877 isds_error err = IE_SUCCESS;
1878 xmlXPathObjectPtr result = NULL;
1880 if (!context) return IE_INVALID_CONTEXT;
1881 if (!envelope) return IE_INVAL;
1882 if (!xpath_ctx) return IE_INVAL;
1885 if (!*envelope) {
1886 /* Allocate envelope */
1887 *envelope = calloc(1, sizeof(**envelope));
1888 if (!*envelope) {
1889 err = IE_NOMEM;
1890 goto leave;
1892 } else {
1893 /* Else free former data */
1894 zfree((*envelope)->dmID);
1895 zfree((*envelope)->dbIDSender);
1896 zfree((*envelope)->dmSender);
1897 zfree((*envelope)->dmSenderAddress);
1898 zfree((*envelope)->dmSenderType);
1899 zfree((*envelope)->dmRecipient);
1900 zfree((*envelope)->dmRecipientAddress);
1901 zfree((*envelope)->dmAmbiguousRecipient);
1904 /* Extract envelope elements added by ISDS
1905 * (XSD: gMessageEnvelope type) */
1906 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
1907 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
1908 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
1909 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
1910 /* XML Schema does not guaratee enumratation. It's plain xs:int. */
1911 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
1912 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
1913 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
1914 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
1915 (*envelope)->dmAmbiguousRecipient);
1917 /* Extract envelope elements added by sender and ISDS
1918 * (XSD: gMessageEnvelope type) */
1919 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
1920 if (err) goto leave;
1922 leave:
1923 if (err) isds_envelope_free(envelope);
1924 xmlXPathFreeObject(result);
1925 return err;
1929 /* Convert other envelope elements from XML tree into isds_envelope structure:
1930 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
1931 * The envelope is automatically allocated but not reallocated.
1932 * The data are just appended into envelope structure.
1933 * @context is ISDS context
1934 * @envelope is automically allocated message envelope structure
1935 * @xpath_ctx is XPath context with current node as parent desired elements
1936 * In case of error @envelope will be freed. */
1937 static isds_error append_status_size_times(struct isds_ctx *context,
1938 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
1939 isds_error err = IE_SUCCESS;
1940 xmlXPathObjectPtr result = NULL;
1941 char *string = NULL;
1942 unsigned long int *unumber = NULL;
1944 if (!context) return IE_INVALID_CONTEXT;
1945 if (!envelope) return IE_INVAL;
1946 if (!xpath_ctx) return IE_INVAL;
1949 if (!*envelope) {
1950 /* Allocate new */
1951 *envelope = calloc(1, sizeof(**envelope));
1952 if (!*envelope) {
1953 err = IE_NOMEM;
1954 goto leave;
1956 } else {
1957 /* Free old data */
1958 zfree((*envelope)->dmMessageStatus);
1959 zfree((*envelope)->dmAttachmentSize);
1960 zfree((*envelope)->dmDeliveryTime);
1961 zfree((*envelope)->dmAcceptanceTime);
1965 /* dmMessageStatus element is mandatory */
1966 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
1967 if (!unumber) {
1968 isds_log_message(context,
1969 _("Missing mandatory sisds:dmMessageStatus integer"));
1970 err = IE_ISDS;
1971 goto leave;
1973 err = uint2isds_message_status(context, unumber,
1974 &((*envelope)->dmMessageStatus));
1975 if (err) {
1976 if (err == IE_ENUM) err = IE_ISDS;
1977 goto leave;
1979 free(unumber); unumber = NULL;
1981 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
1984 EXTRACT_STRING("sisds:dmDeliveryTime", string);
1985 if (string) {
1986 err = timestring2timeval((xmlChar *) string,
1987 &((*envelope)->dmDeliveryTime));
1988 if (err) {
1989 char *string_locale = utf82locale(string);
1990 if (err == IE_DATE) err = IE_ISDS;
1991 isds_printf_message(context,
1992 _("Could not convert dmDeliveryTime as ISO time: %s"),
1993 string_locale);
1994 free(string_locale);
1995 goto leave;
1997 zfree(string);
2000 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
2001 if (string) {
2002 err = timestring2timeval((xmlChar *) string,
2003 &((*envelope)->dmAcceptanceTime));
2004 if (err) {
2005 char *string_locale = utf82locale(string);
2006 if (err == IE_DATE) err = IE_ISDS;
2007 isds_printf_message(context,
2008 _("Could not convert dmAcceptanceTime as ISO time: %s"),
2009 string_locale);
2010 free(string_locale);
2011 goto leave;
2013 zfree(string);
2016 leave:
2017 if (err) isds_envelope_free(envelope);
2018 free(unumber);
2019 free(string);
2020 xmlXPathFreeObject(result);
2021 return err;
2025 /* Extract message document into reallocated document structure
2026 * @context is ISDS context
2027 * @document is automically reallocated message documents structure
2028 * @xpath_ctx is XPath context with current node as isds:dmFile
2029 * In case of error @document will be freed. */
2030 static isds_error extract_document(struct isds_ctx *context,
2031 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
2032 isds_error err = IE_SUCCESS;
2033 xmlXPathObjectPtr result = NULL;
2034 xmlNodePtr file_node = xpath_ctx->node;
2035 char *string = NULL;
2037 if (!context) return IE_INVALID_CONTEXT;
2038 if (!document) return IE_INVAL;
2039 isds_document_free(document);
2040 if (!xpath_ctx) return IE_INVAL;
2042 *document = calloc(1, sizeof(**document));
2043 if (!*document) {
2044 err = IE_NOMEM;
2045 goto leave;
2048 /* Extract document metadata */
2049 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
2051 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
2052 err = string2isds_FileMetaType((xmlChar*)string,
2053 &((*document)->dmFileMetaType));
2054 if (err) {
2055 char *meta_type_locale = utf82locale(string);
2056 isds_printf_message(context,
2057 _("Document has invalid dmFileMetaType attribute value: %s"),
2058 meta_type_locale);
2059 free(meta_type_locale);
2060 err = IE_ISDS;
2061 goto leave;
2063 zfree(string);
2065 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
2066 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
2067 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
2068 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
2071 /* Extract document data.
2072 * Base64 encoded blob or XML subtree must be presented. */
2074 /* Check from dmEncodedContent */
2075 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
2076 xpath_ctx);
2077 if (!result) {
2078 err = IE_XML;
2079 goto leave;
2082 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2083 /* Here we have Base64 blob */
2085 if (result->nodesetval->nodeNr > 1) {
2086 isds_printf_message(context,
2087 _("Document has more dmEncodedContent elements"));
2088 err = IE_ISDS;
2089 goto leave;
2092 xmlXPathFreeObject(result); result = NULL;
2093 EXTRACT_STRING("isds:dmEncodedContent", string);
2095 /* Decode non-emptys document */
2096 if (string && string[0] != '\0') {
2097 (*document)->data_length = b64decode(string, &((*document)->data));
2098 if ((*document)->data_length == (size_t) -1) {
2099 isds_printf_message(context,
2100 _("Error while Base64-decoding document content"));
2101 err = IE_ERROR;
2102 goto leave;
2105 } else {
2106 /* No Base64 blob, try XML document */
2107 xmlXPathFreeObject(result); result = NULL;
2108 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
2109 xpath_ctx);
2110 if (!result) {
2111 err = IE_XML;
2112 goto leave;
2115 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2116 /* Here we have XML document */
2118 if (result->nodesetval->nodeNr > 1) {
2119 isds_printf_message(context,
2120 _("Document has more dmXMLContent elements"));
2121 err = IE_ISDS;
2122 goto leave;
2125 /* FIXME: Serialize the tree rooted at result's node */
2126 isds_printf_message(context,
2127 _("XML documents not yet supported"));
2128 err = IE_NOTSUP;
2129 goto leave;
2130 } else {
2131 /* No bas64 blob, nor XML document */
2132 isds_printf_message(context,
2133 _("Document has no dmEncodedContent, nor dmXMLContent "
2134 "element"));
2135 err = IE_ISDS;
2136 goto leave;
2141 leave:
2142 if (err) isds_document_free(document);
2143 free(string);
2144 xmlXPathFreeObject(result);
2145 xpath_ctx->node = file_node;
2146 return err;
2151 /* Extract message documents into reallocated list of documents
2152 * @context is ISDS context
2153 * @documents is automically reallocated message documents list structure
2154 * @xpath_ctx is XPath context with current node as XSD tFilesArray
2155 * In case of error @documents will be freed. */
2156 static isds_error extract_documents(struct isds_ctx *context,
2157 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
2158 isds_error err = IE_SUCCESS;
2159 xmlXPathObjectPtr result = NULL;
2160 xmlNodePtr files_node = xpath_ctx->node;
2161 struct isds_list *document, *prev_document;
2163 if (!context) return IE_INVALID_CONTEXT;
2164 if (!documents) return IE_INVAL;
2165 isds_list_free(documents);
2166 if (!xpath_ctx) return IE_INVAL;
2168 /* Find documents */
2169 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
2170 if (!result) {
2171 err = IE_XML;
2172 goto leave;
2175 /* No match */
2176 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2177 isds_printf_message(context,
2178 _("Message does not contain any document"));
2179 err = IE_ISDS;
2180 goto leave;
2184 /* Iterate over documents */
2185 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
2187 /* Allocate and append list item */
2188 document = calloc(1, sizeof(*document));
2189 if (!document) {
2190 err = IE_NOMEM;
2191 goto leave;
2193 document->destructor = (void (*)(void **))isds_document_free;
2194 if (i == 0) *documents = document;
2195 else prev_document->next = document;
2196 prev_document = document;
2198 /* Extract document */
2199 xpath_ctx->node = result->nodesetval->nodeTab[i];
2200 err = extract_document(context,
2201 (struct isds_document **) &(document->data), xpath_ctx);
2202 if (err) goto leave;
2206 leave:
2207 if (err) isds_list_free(documents);
2208 xmlXPathFreeObject(result);
2209 xpath_ctx->node = files_node;
2210 return err;
2214 /* Convert isds:dmRecord XML tree into structure
2215 * @context is ISDS context
2216 * @envelope is automically reallocated message envelope structure
2217 * @xpath_ctx is XPath context with current node as isds:dmRecord element
2218 * In case of error @envelope will be freed. */
2219 static isds_error extract_DmRecord(struct isds_ctx *context,
2220 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2221 isds_error err = IE_SUCCESS;
2222 xmlXPathObjectPtr result = NULL;
2224 if (!context) return IE_INVALID_CONTEXT;
2225 if (!envelope) return IE_INVAL;
2226 isds_envelope_free(envelope);
2227 if (!xpath_ctx) return IE_INVAL;
2230 *envelope = calloc(1, sizeof(**envelope));
2231 if (!*envelope) {
2232 err = IE_NOMEM;
2233 goto leave;
2237 /* Extract tRecord data */
2238 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
2240 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
2241 * dmAcceptanceTime. */
2242 err = append_status_size_times(context, envelope, xpath_ctx);
2243 if (err) goto leave;
2245 /* Extract envelope elements added by sender and ISDS
2246 * (XSD: gMessageEnvelope type) */
2247 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
2248 if (err) goto leave;
2249 /* dmOVM can not be obtained from ISDS */
2251 leave:
2252 if (err) isds_envelope_free(envelope);
2253 xmlXPathFreeObject(result);
2254 return err;
2258 /* Find and convert isds:dmHash XML tree into structure
2259 * @context is ISDS context
2260 * @envelope is automically reallocated message hash structure
2261 * @xpath_ctx is XPath context with current node containing isds:dmHash child
2262 * In case of error @hash will be freed. */
2263 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
2264 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
2265 isds_error err = IE_SUCCESS;
2266 xmlNodePtr old_ctx_node;
2267 xmlXPathObjectPtr result = NULL;
2268 char *string = NULL;
2270 if (!context) return IE_INVALID_CONTEXT;
2271 if (!hash) return IE_INVAL;
2272 isds_hash_free(hash);
2273 if (!xpath_ctx) return IE_INVAL;
2276 *hash = calloc(1, sizeof(**hash));
2277 if (!*hash) {
2278 err = IE_NOMEM;
2279 goto leave;
2282 old_ctx_node = xpath_ctx->node;
2284 /* Locate dmHash */
2285 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
2286 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
2287 err = IE_ISDS;
2288 goto leave;
2290 if (err) {
2291 err = IE_ERROR;
2292 goto leave;
2295 /* Get hash algorithm */
2296 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
2297 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
2298 if (err) {
2299 if (err == IE_ENUM) {
2300 char *string_locale = utf82locale(string);
2301 isds_printf_message(context, _("Unsported hash algorithm: %s"),
2302 string_locale);
2303 free(string_locale);
2305 goto leave;
2307 zfree(string);
2309 /* Get hash value */
2310 EXTRACT_STRING(".", string);
2311 if (!string) {
2312 isds_printf_message(context, _("tHash element is missing hash value"));
2313 err = IE_ISDS;
2314 goto leave;
2316 (*hash)->length = b64decode(string, &((*hash)->value));
2317 if ((*hash)->length == (size_t) -1) {
2318 isds_printf_message(context,
2319 _("Error while Base64-decoding hash value"));
2320 err = IE_ERROR;
2321 goto leave;
2324 leave:
2325 if (err) isds_hash_free(hash);
2326 free(string);
2327 xmlXPathFreeObject(result);
2328 xpath_ctx->node = old_ctx_node;
2329 return err;
2333 /* Find and append isds:dmQTimestamp XML tree into envelope
2334 * @context is ISDS context
2335 * @envelope is automically allocated evnelope structure
2336 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
2337 * child
2338 * In case of error @envelope will be freed. */
2339 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
2340 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2341 isds_error err = IE_SUCCESS;
2342 xmlXPathObjectPtr result = NULL;
2343 char *string = NULL;
2345 if (!context) return IE_INVALID_CONTEXT;
2346 if (!envelope) return IE_INVAL;
2347 if (!xpath_ctx) {
2348 isds_envelope_free(envelope);
2349 return IE_INVAL;
2352 if (!*envelope) {
2353 *envelope = calloc(1, sizeof(**envelope));
2354 if (!*envelope) {
2355 err = IE_NOMEM;
2356 goto leave;
2358 } else {
2359 zfree((*envelope)->timestamp);
2360 (*envelope)->timestamp_length = 0;
2363 /* Get dmQTimestamp */
2364 EXTRACT_STRING("sisds:dmQTimestamp", string);
2365 if (!string) {
2366 isds_printf_message(context, _("Missing dmQTimestamp element content"));
2367 err = IE_ISDS;
2368 goto leave;
2370 (*envelope)->timestamp_length =
2371 b64decode(string, &((*envelope)->timestamp));
2372 if ((*envelope)->timestamp_length == (size_t) -1) {
2373 isds_printf_message(context,
2374 _("Error while Base64-decoding timestamp value"));
2375 err = IE_ERROR;
2376 goto leave;
2379 leave:
2380 if (err) isds_envelope_free(envelope);
2381 free(string);
2382 xmlXPathFreeObject(result);
2383 return err;
2387 /* Convert XSD tReturnedMessage XML tree into message structure.
2388 * It doea not store XML tree into message->raw.
2389 * @context is ISDS context
2390 * @include_documents Use true if documents must be extracted
2391 * (tReturnedMessage XSD type), use false if documents shall be ommited
2392 * (tReturnedMessageEnvelope).
2393 * @message is automically reallocated message structure
2394 * @xpath_ctx is XPath context with current node as tReturnedMessage element
2395 * type
2396 * In case of error @message will be freed. */
2397 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
2398 const _Bool include_documents, struct isds_message **message,
2399 xmlXPathContextPtr xpath_ctx) {
2400 isds_error err = IE_SUCCESS;
2401 xmlNodePtr message_node;
2403 if (!context) return IE_INVALID_CONTEXT;
2404 if (!message) return IE_INVAL;
2405 isds_message_free(message);
2406 if (!xpath_ctx) return IE_INVAL;
2409 *message = calloc(1, sizeof(**message));
2410 if (!*message) {
2411 err = IE_NOMEM;
2412 goto leave;
2415 /* Save message XPATH context node */
2416 message_node = xpath_ctx->node;
2419 /* Extract dmDM */
2420 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
2421 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
2422 if (err) { err = IE_ERROR; goto leave; }
2423 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
2424 if (err) goto leave;
2426 if (include_documents) {
2427 /* Extract dmFiles */
2428 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
2429 xpath_ctx);
2430 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
2431 err = IE_ISDS; goto leave;
2433 if (err) { err = IE_ERROR; goto leave; }
2434 err = extract_documents(context, &((*message)->documents), xpath_ctx);
2435 if (err) goto leave;
2439 /* Restore context to message */
2440 xpath_ctx->node = message_node;
2442 /* Extract dmHash */
2443 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
2444 xpath_ctx);
2445 if (err) goto leave;
2447 /* Extract dmQTimestamp, */
2448 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
2449 xpath_ctx);
2450 if (err) goto leave;
2452 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
2453 * dmAcceptanceTime. */
2454 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
2455 if (err) goto leave;
2457 leave:
2458 if (err) isds_message_free(message);
2459 return err;
2463 /* Extract message event into reallocated isds_event structure
2464 * @context is ISDS context
2465 * @event is automically reallocated message event structure
2466 * @xpath_ctx is XPath context with current node as isds:dmEvent
2467 * In case of error @event will be freed. */
2468 static isds_error extract_event(struct isds_ctx *context,
2469 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
2470 isds_error err = IE_SUCCESS;
2471 xmlXPathObjectPtr result = NULL;
2472 xmlNodePtr event_node = xpath_ctx->node;
2473 char *string = NULL;
2475 if (!context) return IE_INVALID_CONTEXT;
2476 if (!event) return IE_INVAL;
2477 isds_event_free(event);
2478 if (!xpath_ctx) return IE_INVAL;
2480 *event = calloc(1, sizeof(**event));
2481 if (!*event) {
2482 err = IE_NOMEM;
2483 goto leave;
2486 /* Extract event data.
2487 * All elements are optional according XSD. That's funny. */
2488 EXTRACT_STRING("isds:dmEventTime", string);
2489 if (string) {
2490 err = timestring2timeval((xmlChar *) string, &((*event)->time));
2491 if (err) {
2492 char *string_locale = utf82locale(string);
2493 if (err == IE_DATE) err = IE_ISDS;
2494 isds_printf_message(context,
2495 _("Could not convert dmEventTime as ISO time: %s"),
2496 string_locale);
2497 free(string_locale);
2498 goto leave;
2500 zfree(string);
2503 /* dmEventDescr element has prefix and the rest */
2504 EXTRACT_STRING("isds:dmEventDescr", string);
2505 if (string) {
2506 err = eventstring2event((xmlChar *) string, *event);
2507 if (err) goto leave;
2508 zfree(string);
2511 leave:
2512 if (err) isds_event_free(event);
2513 free(string);
2514 xmlXPathFreeObject(result);
2515 xpath_ctx->node = event_node;
2516 return err;
2520 /* Convert element of XSD tEventsArray type from XML tree into
2521 * isds_list of isds_event's structure. The list is automatically reallocated.
2522 * @context is ISDS context
2523 * @events is automically reallocated list of event structures
2524 * @xpath_ctx is XPath context with current node as tEventsArray
2525 * In case of error @evnets will be freed. */
2526 static isds_error extract_events(struct isds_ctx *context,
2527 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
2528 isds_error err = IE_SUCCESS;
2529 xmlXPathObjectPtr result = NULL;
2530 xmlNodePtr events_node = xpath_ctx->node;
2531 struct isds_list *event, *prev_event;
2533 if (!context) return IE_INVALID_CONTEXT;
2534 if (!events) return IE_INVAL;
2535 if (!xpath_ctx) return IE_INVAL;
2537 /* Free old list */
2538 isds_list_free(events);
2540 /* Find events */
2541 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEvent", xpath_ctx);
2542 if (!result) {
2543 err = IE_XML;
2544 goto leave;
2547 /* No match */
2548 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2549 isds_printf_message(context,
2550 _("Delivery info does not contain any event"));
2551 err = IE_ISDS;
2552 goto leave;
2556 /* Iterate over events */
2557 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
2559 /* Allocate and append list item */
2560 event = calloc(1, sizeof(*event));
2561 if (!event) {
2562 err = IE_NOMEM;
2563 goto leave;
2565 event->destructor = (void (*)(void **))isds_event_free;
2566 if (i == 0) *events = event;
2567 else prev_event->next = event;
2568 prev_event = event;
2570 /* Extract event */
2571 xpath_ctx->node = result->nodesetval->nodeTab[i];
2572 err = extract_event(context,
2573 (struct isds_event **) &(event->data), xpath_ctx);
2574 if (err) goto leave;
2578 leave:
2579 if (err) isds_list_free(events);
2580 xmlXPathFreeObject(result);
2581 xpath_ctx->node = events_node;
2582 return err;
2586 /* Convert isds_document structure into XML tree and append to dmFiles node.
2587 * @context is session context
2588 * @document is ISDS document
2589 * @dm_files is XML element the resulting tree will be appended to as a child.
2590 * @return error code, in case of error context' message is filled. */
2591 static isds_error insert_document(struct isds_ctx *context,
2592 struct isds_document *document, xmlNodePtr dm_files) {
2593 isds_error err = IE_SUCCESS;
2594 xmlNodePtr new_file = NULL, file = NULL, node;
2595 xmlAttrPtr attribute_node;
2596 xmlChar *base64data = NULL;
2598 if (!context) return IE_INVALID_CONTEXT;
2599 if (!document || !dm_files) return IE_INVAL;
2601 /* Allocate new dmFile */
2602 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
2603 if (!new_file) {
2604 isds_printf_message(context, _("Could not allocate main dmFile"));
2605 err = IE_ERROR;
2606 goto leave;
2608 /* Append the new dmFile.
2609 * XXX: Main document must go first */
2610 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
2611 file = xmlAddPrevSibling(dm_files->children, new_file);
2612 else
2613 file = xmlAddChild(dm_files, new_file);
2615 if (!file) {
2616 xmlFreeNode(new_file); new_file = NULL;
2617 isds_printf_message(context, _("Could not add dmFile child to "
2618 "%s element"), dm_files->name);
2619 err = IE_ERROR;
2620 goto leave;
2623 /* @dmMimeType is required */
2624 if (!document->dmMimeType) {
2625 isds_log_message(context,
2626 _("Document is missing mandatory MIME type definition"));
2627 err = IE_INVAL;
2628 goto leave;
2630 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
2632 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
2633 if (!string) {
2634 isds_printf_message(context,
2635 _("Document has unkown dmFileMetaType: %ld"),
2636 document->dmFileMetaType);
2637 err = IE_ENUM;
2638 goto leave;
2640 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
2642 if (document->dmFileGuid) {
2643 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
2645 if (document->dmUpFileGuid) {
2646 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
2649 /* @dmFileDescr is required */
2650 if (!document->dmFileDescr) {
2651 isds_log_message(context,
2652 _("Document is missing mandatory description (title)"));
2653 err = IE_INVAL;
2654 goto leave;
2656 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
2658 if (document->dmFormat) {
2659 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
2663 /* Insert content (data) of the document. */
2664 /* XXX; Only base64 is implemented currently. */
2665 base64data = (xmlChar *) b64encode(document->data, document->data_length);
2666 if (!base64data) {
2667 isds_printf_message(context,
2668 _("Not enought memory to encode %zd bytes into Base64"),
2669 document->data_length);
2670 err = IE_NOMEM;
2671 goto leave;
2673 INSERT_STRING(file, "dmEncodedContent", base64data);
2674 free(base64data);
2676 leave:
2677 return err;
2681 /* Get data about logged in user and his box. */
2682 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
2683 struct isds_DbOwnerInfo **db_owner_info) {
2684 isds_error err = IE_SUCCESS;
2685 xmlNsPtr isds_ns = NULL;
2686 xmlNodePtr request = NULL;
2687 xmlDocPtr response = NULL;
2688 xmlChar *code = NULL, *message = NULL;
2689 xmlNodePtr node;
2690 xmlXPathContextPtr xpath_ctx = NULL;
2691 xmlXPathObjectPtr result = NULL;
2692 char *string = NULL;
2694 if (!context) return IE_INVALID_CONTEXT;
2695 if (!db_owner_info) return IE_INVAL;
2697 /* Check if connection is established */
2698 if (!context->curl) return IE_CONNECTION_CLOSED;
2701 /* Build GetOwnerInfoFromLogin request */
2702 request = xmlNewNode(NULL, BAD_CAST "GetOwnerInfoFromLogin");
2703 if (!request) {
2704 isds_log_message(context,
2705 _("Could build GetOwnerInfoFromLogin request"));
2706 return IE_ERROR;
2708 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
2709 if(!isds_ns) {
2710 isds_log_message(context, _("Could not create ISDS name space"));
2711 xmlFreeNode(request);
2712 return IE_ERROR;
2714 xmlSetNs(request, isds_ns);
2715 node = xmlNewChild(request, NULL, BAD_CAST "dbDummy", NULL);
2716 if (!node) {
2717 isds_log_message(context, _("Could nod add dbDummy Child to "
2718 "GetOwnerInfoFromLogin element"));
2719 xmlFreeNode(request);
2720 return IE_ERROR;
2724 isds_log(ILF_ISDS, ILL_DEBUG,
2725 _("Sending GetOwnerInfoFromLogin request to ISDS\n"));
2727 /* Sent request */
2728 err = isds(context, SERVICE_DB_SUPPLEMENTARY, request, &response);
2730 /* Destroy request */
2731 xmlFreeNode(request);
2733 if (err) {
2734 isds_log(ILF_ISDS, ILL_DEBUG,
2735 _("Processing ISDS response on GetOwnerInfoFromLogin "
2736 "request failed\n"));
2737 xmlFreeDoc(response);
2738 return err;
2741 /* Check for response status */
2742 err = isds_response_status(context, SERVICE_DB_SUPPLEMENTARY, response,
2743 &code, &message, NULL);
2744 if (err) {
2745 isds_log(ILF_ISDS, ILL_DEBUG,
2746 _("ISDS response on GetOwnerInfoFromLogin request is "
2747 "missing status\n"));
2748 free(code);
2749 free(message);
2750 xmlFreeDoc(response);
2751 return err;
2753 if (xmlStrcmp(code, BAD_CAST "0000")) {
2754 char *code_locale = utf82locale((char*)code);
2755 char *message_locale = utf82locale((char*)message);
2756 isds_log(ILF_ISDS, ILL_DEBUG,
2757 _("Server refused GetOwnerInfoFromLogin request "
2758 "(code=%s, message=%s)\n"), code_locale, message_locale);
2759 isds_log_message(context, message_locale);
2760 free(code_locale);
2761 free(message_locale);
2762 free(code);
2763 free(message);
2764 xmlFreeDoc(response);
2765 return IE_ISDS;
2768 /* Extract data */
2769 /* Prepare stucture */
2770 isds_DbOwnerInfo_free(db_owner_info);
2771 *db_owner_info = calloc(1, sizeof(**db_owner_info));
2772 if (!*db_owner_info) {
2773 err = IE_NOMEM;
2774 goto leave;
2776 xpath_ctx = xmlXPathNewContext(response);
2777 if (!xpath_ctx) {
2778 err = IE_ERROR;
2779 goto leave;
2781 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
2782 err = IE_ERROR;
2783 goto leave;
2786 /* Set context node */
2787 result = xmlXPathEvalExpression(BAD_CAST
2788 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
2789 if (!result) {
2790 err = IE_ERROR;
2791 goto leave;
2793 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2794 isds_log_message(context, _("Missing dbOwnerInfo element"));
2795 err = IE_ISDS;
2796 goto leave;
2798 if (result->nodesetval->nodeNr > 1) {
2799 isds_log_message(context, _("Multiple dbOwnerInfo element"));
2800 err = IE_ISDS;
2801 goto leave;
2803 xpath_ctx->node = result->nodesetval->nodeTab[0];
2804 xmlXPathFreeObject(result); result = NULL;
2806 /* Extract it */
2807 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
2809 leave:
2810 if (err) {
2811 isds_DbOwnerInfo_free(db_owner_info);
2814 free(string);
2815 xmlXPathFreeObject(result);
2816 xmlXPathFreeContext(xpath_ctx);
2818 free(code);
2819 free(message);
2820 xmlFreeDoc(response);
2822 if (!err)
2823 isds_log(ILF_ISDS, ILL_DEBUG,
2824 _("GetOwnerInfoFromLogin request processed by server "
2825 "successfully.\n"));
2827 return err;
2831 /* Find boxes suiting given criteria.
2832 * @criteria is filter. You should fill in at least some memebers.
2833 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
2834 * possibly empty. Input NULL or valid old structure.
2835 * @return:
2836 * IE_SUCCESS if search sucseeded, @boxes contains usefull data
2837 * IE_NOEXIST if no such box exists, @boxes will be NULL
2838 * IE_2BIG if too much boxes exist and server truncated the resuluts, @boxes
2839 * contains still valid data
2840 * other code if something bad happens. @boxes will be NULL. */
2841 isds_error isds_FindDataBox(struct isds_ctx *context,
2842 const struct isds_DbOwnerInfo *criteria,
2843 struct isds_list **boxes) {
2844 isds_error err = IE_SUCCESS;
2845 _Bool truncated = 0;
2846 xmlNsPtr isds_ns = NULL;
2847 xmlNodePtr request = NULL;
2848 xmlDocPtr response = NULL;
2849 xmlChar *code = NULL, *message = NULL;
2850 xmlNodePtr db_owner_info, node;
2851 xmlXPathContextPtr xpath_ctx = NULL;
2852 xmlXPathObjectPtr result = NULL;
2853 xmlChar *string = NULL;
2856 if (!context) return IE_INVALID_CONTEXT;
2857 if (!boxes) return IE_INVAL;
2858 isds_list_free(boxes);
2860 if (!criteria) {
2861 return IE_INVAL;
2864 /* Check if connection is established
2865 * TODO: This check should be done donwstairs. */
2866 if (!context->curl) return IE_CONNECTION_CLOSED;
2869 /* Build FindDataBox request */
2870 request = xmlNewNode(NULL, BAD_CAST "FindDataBox");
2871 if (!request) {
2872 isds_log_message(context,
2873 _("Could build FindDataBox request"));
2874 return IE_ERROR;
2876 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
2877 if(!isds_ns) {
2878 isds_log_message(context, _("Could not create ISDS name space"));
2879 xmlFreeNode(request);
2880 return IE_ERROR;
2882 xmlSetNs(request, isds_ns);
2883 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
2884 if (!db_owner_info) {
2885 isds_log_message(context, _("Could not add dbOwnerInfo Child to "
2886 "FindDataBox element"));
2887 xmlFreeNode(request);
2888 return IE_ERROR;
2892 INSERT_STRING(db_owner_info, "dbID", criteria->dbID);
2894 /* dbType */
2895 if (criteria->dbType) {
2896 const xmlChar *type_string = isds_DbType2string(*(criteria->dbType));
2897 if (!type_string) {
2898 isds_printf_message(context, _("Invalid dbType value: %d"),
2899 *(criteria->dbType));
2900 err = IE_ENUM;
2901 goto leave;
2903 INSERT_STRING(db_owner_info, "dbType", type_string);
2906 INSERT_STRING(db_owner_info, "firmName", criteria->firmName);
2907 INSERT_STRING(db_owner_info, "ic", criteria->ic);
2908 if (criteria->personName) {
2909 INSERT_STRING(db_owner_info, "pnFirstName",
2910 criteria->personName->pnFirstName);
2911 INSERT_STRING(db_owner_info, "pnMiddleName",
2912 criteria->personName->pnMiddleName);
2913 INSERT_STRING(db_owner_info, "pnLastName",
2914 criteria->personName->pnLastName);
2915 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
2916 criteria->personName->pnLastNameAtBirth);
2918 if (criteria->birthInfo) {
2919 if (criteria->birthInfo->biDate) {
2920 if (!tm2datestring(criteria->birthInfo->biDate, &string))
2921 INSERT_STRING(db_owner_info, "biDate", string);
2922 free(string); string = NULL;
2924 INSERT_STRING(db_owner_info, "biCity", criteria->birthInfo->biCity);
2925 INSERT_STRING(db_owner_info, "biCounty", criteria->birthInfo->biCounty);
2926 INSERT_STRING(db_owner_info, "biState", criteria->birthInfo->biState);
2928 if (criteria->address) {
2929 INSERT_STRING(db_owner_info, "adCity", criteria->address->adCity);
2930 INSERT_STRING(db_owner_info, "adStreet", criteria->address->adStreet);
2931 INSERT_STRING(db_owner_info, "adNumberInStreet",
2932 criteria->address->adNumberInStreet);
2933 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
2934 criteria->address->adNumberInMunicipality);
2935 INSERT_STRING(db_owner_info, "adZipCode", criteria->address->adZipCode);
2936 INSERT_STRING(db_owner_info, "adState", criteria->address->adState);
2938 INSERT_STRING(db_owner_info, "nationality", criteria->nationality);
2939 INSERT_STRING(db_owner_info, "email", criteria->email);
2940 INSERT_STRING(db_owner_info, "telNumber", criteria->telNumber);
2941 INSERT_STRING(db_owner_info, "identifier", criteria->identifier);
2942 INSERT_STRING(db_owner_info, "registryCode", criteria->registryCode);
2944 INSERT_LONGINT(db_owner_info, "dbState", criteria->dbState, string);
2946 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", criteria->dbEffectiveOVM);
2947 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
2948 criteria->dbOpenAddressing);
2951 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending FindDataBox request to ISDS\n"));
2953 /* Sent request */
2954 err = isds(context, SERVICE_DB_SEARCH, request, &response);
2956 /* Destroy request */
2957 xmlFreeNode(request); request = NULL;
2959 if (err) {
2960 isds_log(ILF_ISDS, ILL_DEBUG,
2961 _("Processing ISDS response on FindDataBox "
2962 "request failed\n"));
2963 goto leave;
2966 /* Check for response status */
2967 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
2968 &code, &message, NULL);
2969 if (err) {
2970 isds_log(ILF_ISDS, ILL_DEBUG,
2971 _("ISDS response on FindDataBox request is missing status\n"));
2972 goto leave;
2975 /* Request processed, but nothing found */
2976 if (!xmlStrcmp(code, BAD_CAST "0002") ||
2977 !xmlStrcmp(code, BAD_CAST "5001")) {
2978 char *code_locale = utf82locale((char*)code);
2979 char *message_locale = utf82locale((char*)message);
2980 isds_log(ILF_ISDS, ILL_DEBUG,
2981 _("Server did not found any box on FindDataBox request "
2982 "(code=%s, message=%s)\n"), code_locale, message_locale);
2983 isds_log_message(context, message_locale);
2984 free(code_locale);
2985 free(message_locale);
2986 err = IE_NOEXIST;
2987 goto leave;
2990 /* Warning, not a error */
2991 if (!xmlStrcmp(code, BAD_CAST "0003")) {
2992 char *code_locale = utf82locale((char*)code);
2993 char *message_locale = utf82locale((char*)message);
2994 isds_log(ILF_ISDS, ILL_DEBUG,
2995 _("Server truncated response on FindDataBox request "
2996 "(code=%s, message=%s)\n"), code_locale, message_locale);
2997 isds_log_message(context, message_locale);
2998 free(code_locale);
2999 free(message_locale);
3000 truncated = 1;
3003 /* Other error */
3004 else if (xmlStrcmp(code, BAD_CAST "0000")) {
3005 char *code_locale = utf82locale((char*)code);
3006 char *message_locale = utf82locale((char*)message);
3007 isds_log(ILF_ISDS, ILL_DEBUG,
3008 _("Server refused FindDataBox request "
3009 "(code=%s, message=%s)\n"), code_locale, message_locale);
3010 isds_log_message(context, message_locale);
3011 free(code_locale);
3012 free(message_locale);
3013 err = IE_ISDS;
3014 goto leave;
3017 xpath_ctx = xmlXPathNewContext(response);
3018 if (!xpath_ctx) {
3019 err = IE_ERROR;
3020 goto leave;
3022 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
3023 err = IE_ERROR;
3024 goto leave;
3027 /* Extract boxes if they present */
3028 result = xmlXPathEvalExpression(BAD_CAST
3029 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
3030 xpath_ctx);
3031 if (!result) {
3032 err = IE_ERROR;
3033 goto leave;
3035 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3036 struct isds_list *item, *prev_item = NULL;
3037 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
3038 item = calloc(1, sizeof(*item));
3039 if (!item) {
3040 err = IE_NOMEM;
3041 goto leave;
3044 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
3045 if (i == 0) *boxes = item;
3046 else prev_item->next = item;
3047 prev_item = item;
3049 xpath_ctx->node = result->nodesetval->nodeTab[i];
3050 err = extract_DbOwnerInfo(context,
3051 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
3052 if (err) goto leave;
3056 leave:
3057 if (err) {
3058 isds_list_free(boxes);
3059 } else {
3060 if (truncated) err = IE_2BIG;
3063 free(string);
3064 xmlFreeNode(request);
3065 xmlXPathFreeObject(result);
3066 xmlXPathFreeContext(xpath_ctx);
3068 free(code);
3069 free(message);
3070 xmlFreeDoc(response);
3072 if (!err)
3073 isds_log(ILF_ISDS, ILL_DEBUG,
3074 _("FindDataBox request processed by server successfully.\n"));
3076 return err;
3080 /* Get status of a box.
3081 * @context is ISDS session context.
3082 * @box_id is UTF-8 encoded box identifier as zero terminated string
3083 * @box_status is return value of box status.
3084 * @return:
3085 * IE_SUCCESS if box has been found and its status retrieved
3086 * IE_NOEXIST if box is not known to ISDS server
3087 * or other appropriate error.
3088 * You can use isds_DbState to enumerate box status. However out of enum
3089 * range value can be returned too. This is feature because ISDS
3090 * specification leaves the set of values open.
3091 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
3092 * the box has been deleted, but ISDS still lists its former existence. */
3093 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
3094 long int *box_status) {
3095 isds_error err = IE_SUCCESS;
3096 xmlNsPtr isds_ns = NULL;
3097 xmlNodePtr request = NULL, db_id;
3098 xmlDocPtr response = NULL;
3099 xmlChar *code = NULL, *message = NULL;
3100 xmlXPathContextPtr xpath_ctx = NULL;
3101 xmlXPathObjectPtr result = NULL;
3102 xmlChar *string = NULL;
3104 if (!context) return IE_INVALID_CONTEXT;
3105 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
3107 /* Check if connection is established
3108 * TODO: This check should be done donwstairs. */
3109 if (!context->curl) return IE_CONNECTION_CLOSED;
3112 /* Build CheckDataBox request */
3113 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
3114 if (!request) {
3115 isds_log_message(context,
3116 _("Could build CheckDataBox request"));
3117 return IE_ERROR;
3119 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
3120 if(!isds_ns) {
3121 isds_log_message(context, _("Could not create ISDS name space"));
3122 xmlFreeNode(request);
3123 return IE_ERROR;
3125 xmlSetNs(request, isds_ns);
3126 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
3127 if (!db_id) {
3128 isds_log_message(context, _("Could not add dbId Child to "
3129 "CheckDataBox element"));
3130 xmlFreeNode(request);
3131 return IE_ERROR;
3135 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CheckDataBox request to ISDS\n"));
3137 /* Sent request */
3138 err = isds(context, SERVICE_DB_SEARCH, request, &response);
3140 /* Destroy request */
3141 xmlFreeNode(request);
3143 if (err) {
3144 isds_log(ILF_ISDS, ILL_DEBUG,
3145 _("Processing ISDS response on CheckDataBox "
3146 "request failed\n"));
3147 goto leave;
3150 /* Check for response status */
3151 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
3152 &code, &message, NULL);
3153 if (err) {
3154 isds_log(ILF_ISDS, ILL_DEBUG,
3155 _("ISDS response on CheckDataBox request is missing status\n"));
3156 goto leave;
3159 /* Request processed, but nothing found */
3160 if (!xmlStrcmp(code, BAD_CAST "5001")) {
3161 char *box_id_locale = utf82locale((char*)box_id);
3162 char *code_locale = utf82locale((char*)code);
3163 char *message_locale = utf82locale((char*)message);
3164 isds_log(ILF_ISDS, ILL_DEBUG,
3165 _("Server did not found box %s on CheckDataBox request "
3166 "(code=%s, message=%s)\n"),
3167 box_id_locale, code_locale, message_locale);
3168 isds_log_message(context, message_locale);
3169 free(box_id_locale);
3170 free(code_locale);
3171 free(message_locale);
3172 err = IE_NOEXIST;
3173 goto leave;
3176 /* Other error */
3177 else if (xmlStrcmp(code, BAD_CAST "0000")) {
3178 char *code_locale = utf82locale((char*)code);
3179 char *message_locale = utf82locale((char*)message);
3180 isds_log(ILF_ISDS, ILL_DEBUG,
3181 _("Server refused CheckDataBox request "
3182 "(code=%s, message=%s)\n"), code_locale, message_locale);
3183 isds_log_message(context, message_locale);
3184 free(code_locale);
3185 free(message_locale);
3186 err = IE_ISDS;
3187 goto leave;
3190 /* Extract data */
3191 xpath_ctx = xmlXPathNewContext(response);
3192 if (!xpath_ctx) {
3193 err = IE_ERROR;
3194 goto leave;
3196 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
3197 err = IE_ERROR;
3198 goto leave;
3200 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
3201 xpath_ctx);
3202 if (!result) {
3203 err = IE_ERROR;
3204 goto leave;
3206 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3207 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
3208 err = IE_ISDS;
3209 goto leave;
3211 if (result->nodesetval->nodeNr > 1) {
3212 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
3213 err = IE_ISDS;
3214 goto leave;
3216 xpath_ctx->node = result->nodesetval->nodeTab[0];
3217 xmlXPathFreeObject(result); result = NULL;
3219 EXTRACT_LONGINT("isds:dbState", box_status, 1);
3222 leave:
3223 free(string);
3224 xmlXPathFreeObject(result);
3225 xmlXPathFreeContext(xpath_ctx);
3227 free(code);
3228 free(message);
3229 xmlFreeDoc(response);
3231 if (!err)
3232 isds_log(ILF_ISDS, ILL_DEBUG,
3233 _("CheckDataBox request processed by server successfully.\n"));
3235 return err;
3239 /* Send a message via ISDS to a recipent
3240 * @context is session context
3241 * @outgoing_message is message to send; Some memebers are mandatory (like
3242 * dbIDRecipient), some are optional and some are irrelevant (especialy data
3243 * about sender). Included pointer to isds_list documents must contain at
3244 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
3245 * members will be filled with valid data from ISDS. Exact list of write
3246 * members is subject to change. Currently dmId is changed.
3247 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
3248 isds_error isds_send_message(struct isds_ctx *context,
3249 struct isds_message *outgoing_message) {
3251 isds_error err = IE_SUCCESS;
3252 xmlNsPtr isds_ns = NULL;
3253 xmlNodePtr request = NULL, envelope, dm_files, node;
3254 xmlDocPtr response = NULL;
3255 xmlChar *code = NULL, *message = NULL;
3256 xmlXPathContextPtr xpath_ctx = NULL;
3257 xmlXPathObjectPtr result = NULL;
3258 xmlChar *string = NULL;
3259 _Bool message_is_complete = 0;
3261 if (!context) return IE_INVALID_CONTEXT;
3262 if (!outgoing_message) return IE_INVAL;
3264 /* Check if connection is established
3265 * TODO: This check should be done donwstairs. */
3266 if (!context->curl) return IE_CONNECTION_CLOSED;
3269 /* Build CreateMessage request */
3270 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
3271 if (!request) {
3272 isds_log_message(context,
3273 _("Could build CreateMessage request"));
3274 return IE_ERROR;
3276 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
3277 if(!isds_ns) {
3278 isds_log_message(context, _("Could not create ISDS name space"));
3279 xmlFreeNode(request);
3280 return IE_ERROR;
3282 xmlSetNs(request, isds_ns);
3285 /* Build envelope */
3286 envelope = xmlNewChild(request, NULL, BAD_CAST "dmEnvelope", NULL);
3287 if (!envelope) {
3288 isds_log_message(context, _("Could not add dmEnvelope child to "
3289 "CreateMessage element"));
3290 xmlFreeNode(request);
3291 return IE_ERROR;
3294 if (!outgoing_message->envelope) {
3295 isds_log_message(context, _("outgoing message is missing envelope"));
3296 err = IE_INVAL;
3297 goto leave;
3300 INSERT_STRING(envelope, "dmSenderOrgUnit",
3301 outgoing_message->envelope->dmSenderOrgUnit);
3302 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
3303 outgoing_message->envelope->dmSenderOrgUnitNum, string);
3305 if (!outgoing_message->envelope->dbIDRecipient) {
3306 isds_log_message(context,
3307 _("outgoing message is missing recipient box identifier"));
3308 err = IE_INVAL;
3309 goto leave;
3311 INSERT_STRING(envelope, "dbIDRecipient",
3312 outgoing_message->envelope->dbIDRecipient);
3314 INSERT_STRING(envelope, "dmRecipientOrgUnit",
3315 outgoing_message->envelope->dmRecipientOrgUnit);
3316 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
3317 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
3318 INSERT_STRING(envelope, "dmToHands", outgoing_message->envelope->dmToHands);
3320 #define CHECK_FOR_STRING_LENGTH(string, limit, name) \
3321 if ((string) && xmlUTF8Strlen((xmlChar *) (string)) > (limit)) { \
3322 isds_printf_message(context, \
3323 _("%s has more than %d characters"), (name), (limit)); \
3324 err = IE_2BIG; \
3325 goto leave; \
3328 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 255,
3329 "dmAnnotation");
3330 INSERT_STRING(envelope, "dmAnnotation",
3331 outgoing_message->envelope->dmAnnotation);
3333 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
3334 50, "dmRecipientRefNumber");
3335 INSERT_STRING(envelope, "dmRecipientRefNumber",
3336 outgoing_message->envelope->dmRecipientRefNumber);
3338 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
3339 50, "dmSenderRefNumber");
3340 INSERT_STRING(envelope, "dmSenderRefNumber",
3341 outgoing_message->envelope->dmSenderRefNumber);
3343 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
3344 50, "dmRecipientIdent");
3345 INSERT_STRING(envelope, "dmRecipientIdent",
3346 outgoing_message->envelope->dmRecipientIdent);
3348 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
3349 50, "dmSenderIdent");
3350 INSERT_STRING(envelope, "dmSenderIdent",
3351 outgoing_message->envelope->dmSenderIdent);
3353 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
3354 outgoing_message->envelope->dmLegalTitleLaw, string);
3355 INSERT_LONGINT(envelope, "dmLegalTitleYear",
3356 outgoing_message->envelope->dmLegalTitleYear, string);
3357 INSERT_STRING(envelope, "dmLegalTitleSect",
3358 outgoing_message->envelope->dmLegalTitleSect);
3359 INSERT_STRING(envelope, "dmLegalTitlePar",
3360 outgoing_message->envelope->dmLegalTitlePar);
3361 INSERT_STRING(envelope, "dmLegalTitlePoint",
3362 outgoing_message->envelope->dmLegalTitlePoint);
3364 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
3365 outgoing_message->envelope->dmPersonalDelivery);
3366 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
3367 outgoing_message->envelope->dmAllowSubstDelivery);
3369 #undef CHECK_FOR_STRING_LENGTH
3371 /* ???: Should we require value for dbEffectiveOVM sender?
3372 * ISDS has default as true */
3373 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
3376 /* Append dmFiles */
3377 if (!outgoing_message->documents) {
3378 isds_log_message(context,
3379 _("outgoing message is missing list of documents"));
3380 err = IE_INVAL;
3381 goto leave;
3383 dm_files = xmlNewChild(request, NULL, BAD_CAST "dmFiles", NULL);
3384 if (!dm_files) {
3385 isds_log_message(context, _("Could not add dmFiles child to "
3386 "CreateMessage element"));
3387 err = IE_ERROR;
3388 goto leave;
3391 /* Check for document hieararchy */
3392 err = check_documents_hierarchy(context, outgoing_message->documents);
3393 if (err) goto leave;
3395 /* Process each document */
3396 for (struct isds_list *item =
3397 (struct isds_list *) outgoing_message->documents;
3398 item; item = item->next) {
3399 if (!item->data) {
3400 isds_log_message(context,
3401 _("list of documents contains empty item"));
3402 err = IE_INVAL;
3403 goto leave;
3405 /* FIXME: Check for dmFileMetaType and for document references.
3406 * Only first document can be of MAIN type */
3407 err = insert_document(context, (struct isds_document*) item->data,
3408 dm_files);
3410 if (err) goto leave;
3413 /* Signal we can serilize message since now */
3414 message_is_complete = 1;
3418 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
3420 /* Sent request */
3421 err = isds(context, SERVICE_DM_OPERATIONS, request, &response);
3423 /* Dont' destroy request, we want to privode it to application later */
3425 if (err) {
3426 isds_log(ILF_ISDS, ILL_DEBUG,
3427 _("Processing ISDS response on CreateMessage "
3428 "request failed\n"));
3429 goto leave;
3432 /* Check for response status */
3433 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
3434 &code, &message, NULL);
3435 if (err) {
3436 isds_log(ILF_ISDS, ILL_DEBUG,
3437 _("ISDS response on CreateMessage request "
3438 "is missing status\n"));
3439 goto leave;
3442 /* Request processed, but nothing found */
3443 if (xmlStrcmp(code, BAD_CAST "0000")) {
3444 char *box_id_locale =
3445 utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
3446 char *code_locale = utf82locale((char*)code);
3447 char *message_locale = utf82locale((char*)message);
3448 isds_log(ILF_ISDS, ILL_DEBUG,
3449 _("Server did not accept message for %s on CreateMessage "
3450 "request (code=%s, message=%s)\n"),
3451 box_id_locale, code_locale, message_locale);
3452 isds_log_message(context, message_locale);
3453 free(box_id_locale);
3454 free(code_locale);
3455 free(message_locale);
3456 err = IE_ISDS;
3457 goto leave;
3461 /* Extract data */
3462 xpath_ctx = xmlXPathNewContext(response);
3463 if (!xpath_ctx) {
3464 err = IE_ERROR;
3465 goto leave;
3467 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
3468 err = IE_ERROR;
3469 goto leave;
3471 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
3472 xpath_ctx);
3473 if (!result) {
3474 err = IE_ERROR;
3475 goto leave;
3477 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3478 isds_log_message(context, _("Missing CreateMessageResponse element"));
3479 err = IE_ISDS;
3480 goto leave;
3482 if (result->nodesetval->nodeNr > 1) {
3483 isds_log_message(context, _("Multiple CreateMessageResponse element"));
3484 err = IE_ISDS;
3485 goto leave;
3487 xpath_ctx->node = result->nodesetval->nodeTab[0];
3488 xmlXPathFreeObject(result); result = NULL;
3490 if (outgoing_message->envelope->dmID) {
3491 free(outgoing_message->envelope->dmID);
3492 outgoing_message->envelope->dmID = NULL;
3494 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
3495 if (!outgoing_message->envelope->dmID) {
3496 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
3497 "but did not returen assigned message ID\n"));
3500 leave:
3501 /* TODO: Serialize message into structure member raw */
3502 /* XXX: Each web service transport message in different format.
3503 * Therefore it's not possible to save them directly.
3504 * To save them, one must figure out common format.
3505 * We can leave it on application, or we can implement the ESS format. */
3506 /*if (message_is_complete) {
3507 if (outgoing_message->envelope->dmID) {
3509 /* Add assigned message ID as first child*/
3510 /*xmlNodePtr dmid_text = xmlNewText(
3511 (xmlChar *) outgoing_message->envelope->dmID);
3512 if (!dmid_text) goto serialization_failed;
3514 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
3515 BAD_CAST "dmID");
3516 if (!dmid_element) {
3517 xmlFreeNode(dmid_text);
3518 goto serialization_failed;
3521 xmlNodePtr dmid_element_with_text =
3522 xmlAddChild(dmid_element, dmid_text);
3523 if (!dmid_element_with_text) {
3524 xmlFreeNode(dmid_element);
3525 xmlFreeNode(dmid_text);
3526 goto serialization_failed;
3529 node = xmlAddPrevSibling(envelope->childern,
3530 dmid_element_with_text);
3531 if (!node) {
3532 xmlFreeNodeList(dmid_element_with_text);
3533 goto serialization_failed;
3537 /* Serialize message with ID into raw */
3538 /*buffer = serialize_element(envelope)*/
3539 /* }
3541 serialization_failed:
3546 /* Clean up */
3547 free(string);
3548 xmlXPathFreeObject(result);
3549 xmlXPathFreeContext(xpath_ctx);
3551 free(code);
3552 free(message);
3553 xmlFreeDoc(response);
3554 xmlFreeNode(request);
3556 if (!err)
3557 isds_log(ILF_ISDS, ILL_DEBUG,
3558 _("CreateMessage request processed by server "
3559 "successfully.\n"));
3561 return err;
3565 /* Get list of messages. This is common core for getting sent or received
3566 * messaeges.
3567 * Any criterion argument can be NULL, if you don't care about it.
3568 * @context is session context. Must not be NULL.
3569 * @outgoing_direction is true if you want list of outgoing messages,
3570 * it's false if you want incoming messages.
3571 * @from_time is minimal time and date of message sending inclusive.
3572 * @to_time is maximal time and date of message sending inclusive
3573 * @organization_unit_number is number of sender/recipient respectively.
3574 * @status_filter is bit field of isds_message_status values. Use special
3575 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
3576 * all values, you can use bitwise arithmetic if you want.)
3577 * @offset is index of first message we are interested in. First message is 1.
3578 * Set to 0 (or 1) if you don't care.
3579 * @number is maximal length of list you want to get as input value, outputs
3580 * number of messages matching these criteria. Can be NULL if you don't care
3581 * (applies to output value either).
3582 * @messages is automatically reallocated list of isds_message's. Be ware that
3583 * it returns only brief overview (envelope and some other fields) about each
3584 * message, not the complete message. FIXME: Specify exact fields.
3585 * The list is sorted by delivery time in ascending order.
3586 * Use NULL if
3587 * you don't care about don't need the data (useful if you want to know only
3588 * the @number). If you provide &NULL, list will be allocated on heap, if you
3589 * provide pointer to non-NULL, list will be freed automacally at first. Also
3590 * in case of error the list will be NULLed.
3591 * @return IE_SUCCESS or appropriate error code. */
3592 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
3593 _Bool outgoing_direction,
3594 const struct timeval *from_time, const struct timeval *to_time,
3595 const long int *organization_unit_number,
3596 const unsigned int status_filter,
3597 const unsigned long int offset, unsigned long int *number,
3598 struct isds_list **messages) {
3600 isds_error err = IE_SUCCESS;
3601 xmlNsPtr isds_ns = NULL;
3602 xmlNodePtr request = NULL, node;
3603 xmlDocPtr response = NULL;
3604 xmlChar *code = NULL, *message = NULL;
3605 xmlXPathContextPtr xpath_ctx = NULL;
3606 xmlXPathObjectPtr result = NULL;
3607 xmlChar *string = NULL;
3608 long unsigned int count = 0;
3610 if (!context) return IE_INVALID_CONTEXT;
3612 /* Free former message list if any */
3613 if (messages) isds_list_free(messages);
3615 /* Check if connection is established
3616 * TODO: This check should be done donwstairs. */
3617 if (!context->curl) return IE_CONNECTION_CLOSED;
3619 /* Build GetListOf*Messages request */
3620 request = xmlNewNode(NULL,
3621 (outgoing_direction) ?
3622 BAD_CAST "GetListOfSentMessages" :
3623 BAD_CAST "GetListOfReceivedMessages"
3625 if (!request) {
3626 isds_log_message(context,
3627 (outgoing_direction) ?
3628 _("Could not build GetListOfSentMessages request") :
3629 _("Could not build GetListOfReceivedMessages request")
3631 return IE_ERROR;
3633 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
3634 if(!isds_ns) {
3635 isds_log_message(context, _("Could not create ISDS name space"));
3636 xmlFreeNode(request);
3637 return IE_ERROR;
3639 xmlSetNs(request, isds_ns);
3642 if (from_time) {
3643 err = timeval2timestring(from_time, &string);
3644 if (err) goto leave;
3646 INSERT_STRING(request, "dmFromTime", string);
3647 free(string); string = NULL;
3649 if (to_time) {
3650 err = timeval2timestring(to_time, &string);
3651 if (err) goto leave;
3653 INSERT_STRING(request, "dmToTime", string);
3654 free(string); string = NULL;
3656 if (outgoing_direction) {
3657 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
3658 organization_unit_number, string);
3659 } else {
3660 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
3661 organization_unit_number, string);
3664 if (status_filter > MESSAGESTATE_ANY) {
3665 isds_printf_message(context,
3666 _("Invalid message state filter value: %ld"), status_filter);
3667 err = IE_INVAL;
3668 goto leave;
3670 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
3672 if (offset > 0 ) {
3673 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
3674 } else {
3675 INSERT_STRING(request, "dmOffset", "1");
3678 /* number 0 means no limit */
3679 if (number && *number == 0) {
3680 INSERT_STRING(request, "dmLimit", NULL);
3681 } else {
3682 INSERT_ULONGINT(request, "dmLimit", number, string);
3686 isds_log(ILF_ISDS, ILL_DEBUG,
3687 (outgoing_direction) ?
3688 _("Sending GetListOfSentMessages request to ISDS\n") :
3689 _("Sending GetListOfReceivedMessages request to ISDS\n")
3692 /* Sent request */
3693 err = isds(context, SERVICE_DM_INFO, request, &response);
3694 xmlFreeNode(request); request = NULL;
3696 if (err) {
3697 isds_log(ILF_ISDS, ILL_DEBUG,
3698 (outgoing_direction) ?
3699 _("Processing ISDS response on GetListOfSentMessages "
3700 "request failed\n") :
3701 _("Processing ISDS response on GetListOfReceivedMessages "
3702 "request failed\n")
3704 goto leave;
3707 /* Check for response status */
3708 err = isds_response_status(context, SERVICE_DM_INFO, response,
3709 &code, &message, NULL);
3710 if (err) {
3711 isds_log(ILF_ISDS, ILL_DEBUG,
3712 (outgoing_direction) ?
3713 _("ISDS response on GetListOfSentMessages request "
3714 "is missing status\n") :
3715 _("ISDS response on GetListOfReceivedMessages request "
3716 "is missing status\n")
3718 goto leave;
3721 /* Request processed, but nothing found */
3722 if (xmlStrcmp(code, BAD_CAST "0000")) {
3723 char *code_locale = utf82locale((char*)code);
3724 char *message_locale = utf82locale((char*)message);
3725 isds_log(ILF_ISDS, ILL_DEBUG,
3726 (outgoing_direction) ?
3727 _("Server refused GetListOfSentMessages request "
3728 "(code=%s, message=%s)\n") :
3729 _("Server refused GetListOfReceivedMessages request "
3730 "(code=%s, message=%s)\n"),
3731 code_locale, message_locale);
3732 isds_log_message(context, message_locale);
3733 free(code_locale);
3734 free(message_locale);
3735 err = IE_ISDS;
3736 goto leave;
3740 /* Extract data */
3741 xpath_ctx = xmlXPathNewContext(response);
3742 if (!xpath_ctx) {
3743 err = IE_ERROR;
3744 goto leave;
3746 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
3747 err = IE_ERROR;
3748 goto leave;
3750 result = xmlXPathEvalExpression(
3751 (outgoing_direction) ?
3752 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
3753 "isds:dmRecords/isds:dmRecord" :
3754 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
3755 "isds:dmRecords/isds:dmRecord",
3756 xpath_ctx);
3757 if (!result) {
3758 err = IE_ERROR;
3759 goto leave;
3762 /* Fill output arguments in */
3763 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3764 struct isds_envelope *envelope;
3765 struct isds_list *item = NULL, *last_item = NULL;
3767 for (count = 0; count < result->nodesetval->nodeNr; count++) {
3768 /* Create new message */
3769 item = calloc(1, sizeof(*item));
3770 if (!item) {
3771 err = IE_NOMEM;
3772 goto leave;
3774 item->destructor = (void(*)(void**)) &isds_message_free;
3775 item->data = calloc(1, sizeof(struct isds_message));
3776 if (!item->data) {
3777 isds_list_free(&item);
3778 err = IE_NOMEM;
3779 goto leave;
3782 /* Extract envelope data */
3783 xpath_ctx->node = result->nodesetval->nodeTab[count];
3784 envelope = NULL;
3785 err = extract_DmRecord(context, &envelope, xpath_ctx);
3786 if (err) {
3787 isds_list_free(&item);
3788 goto leave;
3791 /* Attach extracted envelope */
3792 ((struct isds_message *) item->data)->envelope = envelope;
3794 /* Append new message into the list */
3795 if (!*messages) {
3796 *messages = last_item = item;
3797 } else {
3798 last_item->next = item;
3799 last_item = item;
3803 if (number) *number = count;
3805 leave:
3806 if (err) {
3807 isds_list_free(messages);
3810 free(string);
3811 xmlXPathFreeObject(result);
3812 xmlXPathFreeContext(xpath_ctx);
3814 free(code);
3815 free(message);
3816 xmlFreeDoc(response);
3817 xmlFreeNode(request);
3819 if (!err)
3820 isds_log(ILF_ISDS, ILL_DEBUG,
3821 (outgoing_direction) ?
3822 _("GetListOfSentMessages request processed by server "
3823 "successfully.\n") :
3824 _("GetListOfReceivedMessages request processed by server "
3825 "successfully.\n")
3827 return err;
3831 /* Get list of outgoing (already sent) messages.
3832 * Any criterion argument can be NULL, if you don't care about it.
3833 * @context is session context. Must not be NULL.
3834 * @from_time is minimal time and date of message sending inclusive.
3835 * @to_time is maximal time and date of message sending inclusive
3836 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
3837 * @status_filter is bit field of isds_message_status values. Use special
3838 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
3839 * all values, you can use bitwise arithmetic if you want.)
3840 * @offset is index of first message we are interested in. First message is 1.
3841 * Set to 0 (or 1) if you don't care.
3842 * @number is maximal length of list you want to get as input value, outputs
3843 * number of messages matching these criteria. Can be NULL if you don't care
3844 * (applies to output value either).
3845 * @messages is automatically reallocated list of isds_message's. Be ware that
3846 * it returns only brief overview (envelope and some other fields) about each
3847 * message, not the complete message. FIXME: Specify exact fields.
3848 * The list is sorted by delivery time in ascending order.
3849 * Use NULL if you don't care about the metadata (useful if you want to know
3850 * only the @number). If you provide &NULL, list will be allocated on heap,
3851 * if you provide pointer to non-NULL, list will be freed automacally at first.
3852 * Also in case of error the list will be NULLed.
3853 * @return IE_SUCCESS or appropriate error code. */
3854 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
3855 const struct timeval *from_time, const struct timeval *to_time,
3856 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
3857 const unsigned long int offset, unsigned long int *number,
3858 struct isds_list **messages) {
3860 return isds_get_list_of_messages(
3861 context, 1,
3862 from_time, to_time, dmSenderOrgUnitNum, status_filter,
3863 offset, number,
3864 messages);
3868 /* Get list of incoming (addressed to you) messages.
3869 * Any criterion argument can be NULL, if you don't care about it.
3870 * @context is session context. Must not be NULL.
3871 * @from_time is minimal time and date of message sending inclusive.
3872 * @to_time is maximal time and date of message sending inclusive
3873 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
3874 * @status_filter is bit field of isds_message_status values. Use special
3875 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
3876 * all values, you can use bitwise arithmetic if you want.)
3877 * @offset is index of first message we are interested in. First message is 1.
3878 * Set to 0 (or 1) if you don't care.
3879 * @number is maximal length of list you want to get as input value, outputs
3880 * number of messages matching these criteria. Can be NULL if you don't care
3881 * (applies to output value either).
3882 * @messages is automatically reallocated list of isds_message's. Be ware that
3883 * it returns only brief overview (envelope and some other fields) about each
3884 * message, not the complete message. FIXME: Specify exact fields.
3885 * Use NULL if you don't care about the metadata (useful if you want to know
3886 * only the @number). If you provide &NULL, list will be allocated on heap,
3887 * if you provide pointer to non-NULL, list will be freed automacally at first.
3888 * Also in case of error the list will be NULLed.
3889 * @return IE_SUCCESS or appropriate error code. */
3890 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
3891 const struct timeval *from_time, const struct timeval *to_time,
3892 const long int *dmRecipientOrgUnitNum,
3893 const unsigned int status_filter,
3894 const unsigned long int offset, unsigned long int *number,
3895 struct isds_list **messages) {
3897 return isds_get_list_of_messages(
3898 context, 0,
3899 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
3900 offset, number,
3901 messages);
3905 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
3906 * code
3907 * @context is session context
3908 * @service is ISDS WS service handler
3909 * @service_name is name of SERVICE_DM_OPERATIONS
3910 * @message_id is message ID to send as service argument to ISDS
3911 * @response is server SOAP body response as XML document
3912 * @code is ISDS status code
3913 * @status_message is ISDS status message
3914 * @return error coded from lower layer, context message will be set up
3915 * appropriately. */
3916 static isds_error build_send_check_message_request(struct isds_ctx *context,
3917 const isds_service service, const xmlChar *service_name,
3918 const char *message_id,
3919 xmlDocPtr *response, xmlChar **code, xmlChar **status_message) {
3921 isds_error err = IE_SUCCESS;
3922 char *service_name_locale = NULL, *message_id_locale = NULL;
3923 xmlNodePtr request = NULL, node;
3924 xmlNsPtr isds_ns = NULL;
3926 if (!context) return IE_INVALID_CONTEXT;
3927 if (!service_name || !message_id) return IE_INVAL;
3928 if (!response || !code || !status_message) return IE_INVAL;
3930 /* Free output argument */
3931 xmlFreeDoc(*response);
3932 free(*code);
3933 free(*status_message);
3936 /* Check if connection is established
3937 * TODO: This check should be done donwstairs. */
3938 if (!context->curl) return IE_CONNECTION_CLOSED;
3940 service_name_locale = utf82locale((char*)service_name);
3941 message_id_locale = utf82locale(message_id);
3942 if (!service_name_locale || !message_id_locale) {
3943 err = IE_NOMEM;
3944 goto leave;
3947 /* Build request */
3948 request = xmlNewNode(NULL, service_name);
3949 if (!request) {
3950 isds_printf_message(context,
3951 _("Could not build %s request"), service_name_locale);
3952 err = IE_ERROR;
3953 goto leave;
3955 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
3956 if(!isds_ns) {
3957 isds_log_message(context, _("Could not create ISDS name space"));
3958 err = IE_ERROR;
3959 goto leave;
3961 xmlSetNs(request, isds_ns);
3964 /* Add requested ID */
3965 err = validate_message_id_length(context, (xmlChar *) message_id);
3966 if (err) goto leave;
3967 INSERT_STRING(request, "dmID", message_id);
3970 isds_log(ILF_ISDS, ILL_DEBUG,
3971 _("Sending %s request for %s message ID to ISDS\n"),
3972 service_name_locale, message_id_locale);
3974 /* Send request */
3975 err = isds(context, service, request, response);
3976 xmlFreeNode(request); request = NULL;
3978 if (err) {
3979 isds_log(ILF_ISDS, ILL_DEBUG,
3980 _("Processing ISDS response on %s request failed\n"),
3981 service_name_locale);
3982 goto leave;
3985 /* Check for response status */
3986 err = isds_response_status(context, service, *response,
3987 code, status_message, NULL);
3988 if (err) {
3989 isds_log(ILF_ISDS, ILL_DEBUG,
3990 _("ISDS response on %s request is missing status\n"),
3991 service_name_locale);
3992 goto leave;
3995 /* Request processed, but nothing found */
3996 if (xmlStrcmp(*code, BAD_CAST "0000")) {
3997 char *code_locale = utf82locale((char*) *code);
3998 char *status_message_locale = utf82locale((char*) *status_message);
3999 isds_log(ILF_ISDS, ILL_DEBUG,
4000 _("Server refused %s request for %s message ID "
4001 "(code=%s, message=%s)\n"),
4002 service_name_locale, message_id_locale,
4003 code_locale, status_message_locale);
4004 isds_log_message(context, status_message_locale);
4005 free(code_locale);
4006 free(status_message_locale);
4007 err = IE_ISDS;
4008 goto leave;
4011 leave:
4012 free(message_id_locale);
4013 free(service_name_locale);
4014 xmlFreeNode(request);
4015 return err;
4019 /* Download incoming message envelope identified by ID.
4020 * @context is session context
4021 * @message_id is message identifier (you can get them from
4022 * isds_get_list_of_received_messages())
4023 * @message is automatically reallocated message retrieved from ISDS.
4024 * It will miss documents per se. Use isds_get_received_message(), if you are
4025 * interrested in documents (content) too.
4026 * Returned hash and timestamp require documents to be verifiable. */
4027 isds_error isds_get_received_envelope(struct isds_ctx *context,
4028 const char *message_id, struct isds_message **message) {
4030 isds_error err = IE_SUCCESS;
4031 xmlDocPtr response = NULL;
4032 xmlChar *code = NULL, *status_message = NULL;
4033 xmlXPathContextPtr xpath_ctx = NULL;
4034 xmlXPathObjectPtr result = NULL;
4036 if (!context) return IE_INVALID_CONTEXT;
4038 /* Free former message if any */
4039 if (!message) return IE_INVAL;
4040 isds_message_free(message);
4042 /* Do request and check for success */
4043 err = build_send_check_message_request(context, SERVICE_DM_INFO,
4044 BAD_CAST "MessageEnvelopeDownload", message_id,
4045 &response, &code, &status_message);
4046 if (err) goto leave;
4048 /* Extract data */
4049 xpath_ctx = xmlXPathNewContext(response);
4050 if (!xpath_ctx) {
4051 err = IE_ERROR;
4052 goto leave;
4054 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4055 err = IE_ERROR;
4056 goto leave;
4058 result = xmlXPathEvalExpression(
4059 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
4060 "isds:dmReturnedMessageEnvelope",
4061 xpath_ctx);
4062 if (!result) {
4063 err = IE_ERROR;
4064 goto leave;
4066 /* Empty response */
4067 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4068 char *message_id_locale = utf82locale((char*) message_id);
4069 isds_printf_message(context,
4070 _("Server did not return any envelope for ID `%s' "
4071 "on MessageEnvelopeDownload request"), message_id_locale);
4072 free(message_id_locale);
4073 err = IE_ISDS;
4074 goto leave;
4076 /* More envelops */
4077 if (result->nodesetval->nodeNr > 1) {
4078 char *message_id_locale = utf82locale((char*) message_id);
4079 isds_printf_message(context,
4080 _("Server did return more envelopes for ID `%s' "
4081 "on MessageEnvelopeDownload request"), message_id_locale);
4082 free(message_id_locale);
4083 err = IE_ISDS;
4084 goto leave;
4086 /* One message */
4087 xpath_ctx->node = result->nodesetval->nodeTab[0];
4089 /* Extract the envelope (= message without documents, hence 0) */
4090 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
4091 if (err) goto leave;
4093 /* Save XML blob */
4094 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
4095 &(*message)->raw_length);
4097 leave:
4098 if (err) {
4099 isds_message_free(message);
4102 xmlXPathFreeObject(result);
4103 xmlXPathFreeContext(xpath_ctx);
4105 free(code);
4106 free(status_message);
4107 xmlFreeDoc(response);
4109 if (!err)
4110 isds_log(ILF_ISDS, ILL_DEBUG,
4111 _("MessageEnvelopeDownload request processed by server "
4112 "successfully.\n")
4114 return err;
4118 /* Download delivery infosheet of given message identified by ID.
4119 * @context is session context
4120 * @message_id is message identifier (you can get them from
4121 * isds_get_list_of_{sent,received}_messages())
4122 * @message is automatically reallocated message retrieved from ISDS.
4123 * It will miss documents per se. Use isds_get_received_message(), if you are
4124 * interrested in documents (content). OTOH, only this function can get list
4125 * events message has gone through. */
4126 isds_error isds_get_delivery_info(struct isds_ctx *context,
4127 const char *message_id, struct isds_message **message) {
4129 isds_error err = IE_SUCCESS;
4130 xmlDocPtr response = NULL;
4131 xmlChar *code = NULL, *status_message = NULL;
4132 xmlXPathContextPtr xpath_ctx = NULL;
4133 xmlXPathObjectPtr result = NULL;
4134 xmlNodePtr delivery_node = NULL;
4136 if (!context) return IE_INVALID_CONTEXT;
4138 /* Free former message if any */
4139 if (!message) return IE_INVAL;
4140 isds_message_free(message);
4142 /* Do request and check for success */
4143 err = build_send_check_message_request(context, SERVICE_DM_INFO,
4144 BAD_CAST "GetDeliveryInfo", message_id,
4145 &response, &code, &status_message);
4146 if (err) goto leave;
4148 /* Extract data */
4149 xpath_ctx = xmlXPathNewContext(response);
4150 if (!xpath_ctx) {
4151 err = IE_ERROR;
4152 goto leave;
4154 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4155 err = IE_ERROR;
4156 goto leave;
4158 result = xmlXPathEvalExpression(
4159 BAD_CAST "/isds:GetDeliveryInfoResponse/isds:dmDelivery",
4160 xpath_ctx);
4161 if (!result) {
4162 err = IE_ERROR;
4163 goto leave;
4165 /* Empty response */
4166 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4167 char *message_id_locale = utf82locale((char*) message_id);
4168 isds_printf_message(context,
4169 _("Server did not return any delivery info for ID `%s' "
4170 "on GetDeliveryInfo request"), message_id_locale);
4171 free(message_id_locale);
4172 err = IE_ISDS;
4173 goto leave;
4175 /* More delivery infos */
4176 if (result->nodesetval->nodeNr > 1) {
4177 char *message_id_locale = utf82locale((char*) message_id);
4178 isds_printf_message(context,
4179 _("Server did return more delivery infos for ID `%s' "
4180 "on GetDeliveryInfo request"), message_id_locale);
4181 free(message_id_locale);
4182 err = IE_ISDS;
4183 goto leave;
4185 /* One delivery info */
4186 xpath_ctx->node = delivery_node = result->nodesetval->nodeTab[0];
4188 /* Extract the envelope (= message without documents, hence 0).
4189 * XXX: extract_TReturnedMessage() can obtain attachments size, but delivery info
4190 * carries none. It's coded as option elements, so it should work. */
4191 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
4192 if (err) goto leave;
4194 /* Extract events */
4195 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmEvents", xpath_ctx);
4196 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
4197 if (err) { err = IE_ERROR; goto leave; }
4198 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
4199 if (err) goto leave;
4201 /* Save XML blob */
4202 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
4203 &(*message)->raw_length);
4205 leave:
4206 if (err) {
4207 isds_message_free(message);
4210 xmlXPathFreeObject(result);
4211 xmlXPathFreeContext(xpath_ctx);
4213 free(code);
4214 free(status_message);
4215 xmlFreeDoc(response);
4217 if (!err)
4218 isds_log(ILF_ISDS, ILL_DEBUG,
4219 _("GetDeliveryInfo request processed by server "
4220 "successfully.\n")
4222 return err;
4226 /* Load incoming message from buffer.
4227 * @context is session context
4228 * @buffer XML stream with unsigned message. You can retrieve such data from
4229 * message->raw after calling isds_get_received_message().
4230 * @length is length of buffer in bytes.
4231 * @message is automatically reallocated message parsed from @buffer.
4232 * @strategy selects how buffer will be attached into raw isds_message member.
4233 * */
4234 isds_error isds_load_received_message(struct isds_ctx *context,
4235 const void *buffer, const size_t length,
4236 struct isds_message **message, const isds_buffer_strategy strategy) {
4238 isds_error err = IE_SUCCESS;
4239 xmlDocPtr message_doc = NULL;
4240 xmlXPathContextPtr xpath_ctx = NULL;
4241 xmlXPathObjectPtr result = NULL;
4243 if (!context) return IE_INVALID_CONTEXT;
4244 if (!message) return IE_INVAL;
4245 isds_message_free(message);
4246 if (!buffer) return IE_INVAL;
4249 isds_log(ILF_ISDS, ILL_DEBUG,
4250 _("Outgoing message content:\n%.*s\nEnd of message\n"),
4251 length, buffer);
4253 /* Convert extracted messages XML stream into XPath context */
4254 message_doc = xmlParseMemory(buffer, length);
4255 if (!message_doc) {
4256 err = IE_XML;
4257 goto leave;
4259 xpath_ctx = xmlXPathNewContext(message_doc);
4260 if (!xpath_ctx) {
4261 err = IE_ERROR;
4262 goto leave;
4264 /* XXX: Standard name space */
4265 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4266 err = IE_ERROR;
4267 goto leave;
4269 result = xmlXPathEvalExpression(
4270 BAD_CAST "/isds:dmReturnedMessage", xpath_ctx);
4271 if (!result) {
4272 err = IE_ERROR;
4273 goto leave;
4275 /* Bad root element */
4276 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4277 isds_printf_message(context,
4278 _("XML document is not isds:dmReturnedMessage document"));
4279 err = IE_ISDS;
4280 goto leave;
4282 /* More root elements. This should never happen. */
4283 if (result->nodesetval->nodeNr > 1) {
4284 isds_printf_message(context,
4285 _("XML document has more "
4286 "root isds:dmReturnedMessage elements"));
4287 err = IE_ISDS;
4288 goto leave;
4290 /* One message */
4291 xpath_ctx->node = result->nodesetval->nodeTab[0];
4293 /* Extract the message */
4294 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
4295 if (err) goto leave;
4297 /* Append XML stream into message */
4298 switch (strategy) {
4299 case BUFFER_DONT_STORE:
4300 break;
4301 case BUFFER_COPY:
4302 (*message)->raw = malloc(length);
4303 if (!(*message)->raw) {
4304 err = IE_NOMEM;
4305 goto leave;
4307 memcpy((*message)->raw, buffer, length);
4308 (*message)->raw_length = length;
4309 break;
4310 case BUFFER_MOVE:
4311 (*message)->raw = (void *) buffer;
4312 (*message)->raw_length = length;
4313 break;
4314 default:
4315 err = IE_ENUM;
4316 goto leave;
4320 leave:
4321 if (err) {
4322 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
4323 isds_message_free(message);
4326 xmlFreeDoc(message_doc);
4327 xmlXPathFreeObject(result);
4328 xmlXPathFreeContext(xpath_ctx);
4330 if (!err)
4331 isds_log(ILF_ISDS, ILL_DEBUG,
4332 _("Outgoing message loaded successfully.\n"));
4333 return err;
4337 /* Download incoming message identified by ID.
4338 * @context is session context
4339 * @message_id is message identifier (you can get them from
4340 * isds_get_list_of_received_messages())
4341 * @message is automatically reallocated message retrieved from ISDS */
4342 isds_error isds_get_received_message(struct isds_ctx *context,
4343 const char *message_id, struct isds_message **message) {
4345 isds_error err = IE_SUCCESS;
4346 xmlDocPtr response = NULL;
4347 xmlChar *code = NULL, *status_message = NULL;
4348 xmlXPathContextPtr xpath_ctx = NULL;
4349 xmlXPathObjectPtr result = NULL;
4351 if (!context) return IE_INVALID_CONTEXT;
4353 /* Free former message if any */
4354 if (message) isds_message_free(message);
4356 /* Do request and check for success */
4357 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
4358 BAD_CAST "MessageDownload", message_id,
4359 &response, &code, &status_message);
4360 if (err) goto leave;
4362 /* Extract data */
4363 xpath_ctx = xmlXPathNewContext(response);
4364 if (!xpath_ctx) {
4365 err = IE_ERROR;
4366 goto leave;
4368 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4369 err = IE_ERROR;
4370 goto leave;
4372 result = xmlXPathEvalExpression(
4373 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
4374 xpath_ctx);
4375 if (!result) {
4376 err = IE_ERROR;
4377 goto leave;
4379 /* Empty response */
4380 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4381 char *message_id_locale = utf82locale((char*) message_id);
4382 isds_printf_message(context,
4383 _("Server did not return any message for ID `%s' "
4384 "on MessageDownload request"), message_id_locale);
4385 free(message_id_locale);
4386 err = IE_ISDS;
4387 goto leave;
4389 /* More messages */
4390 if (result->nodesetval->nodeNr > 1) {
4391 char *message_id_locale = utf82locale((char*) message_id);
4392 isds_printf_message(context,
4393 _("Server did return more messages for ID `%s' "
4394 "on MessageDownload request"), message_id_locale);
4395 free(message_id_locale);
4396 err = IE_ISDS;
4397 goto leave;
4399 /* One message */
4400 xpath_ctx->node = result->nodesetval->nodeTab[0];
4402 /* Extract the message */
4403 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
4404 if (err) goto leave;
4406 /* Save XML blob */
4407 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
4408 &(*message)->raw_length);
4410 leave:
4411 if (err) {
4412 isds_message_free(message);
4415 xmlXPathFreeObject(result);
4416 xmlXPathFreeContext(xpath_ctx);
4418 free(code);
4419 free(status_message);
4420 xmlFreeDoc(response);
4422 if (!err)
4423 isds_log(ILF_ISDS, ILL_DEBUG,
4424 _("MessageDownload request processed by server "
4425 "successfully.\n")
4427 return err;
4431 /* Load signed message from buffer.
4432 * @context is session context
4433 * @outgoing is true if message is outgoing, false if message is incoming
4434 * @buffer is DER encoded PKCS#7 structure with signed message. You can
4435 * retrieve such data from message->raw after calling
4436 * isds_get_signed_{received,sent}_message().
4437 * @length is length of buffer in bytes.
4438 * @message is automatically reallocated message parsed from @buffer.
4439 * @strategy selects how buffer will be attached into raw isds_message member.
4440 * */
4441 isds_error isds_load_signed_message(struct isds_ctx *context,
4442 const _Bool outgoing, const void *buffer, const size_t length,
4443 struct isds_message **message, const isds_buffer_strategy strategy) {
4445 isds_error err = IE_SUCCESS;
4446 xmlDocPtr message_doc = NULL;
4447 xmlXPathContextPtr xpath_ctx = NULL;
4448 xmlXPathObjectPtr result = NULL;
4449 void *xml_stream = NULL;
4450 size_t xml_stream_length = 0;
4452 if (!context) return IE_INVALID_CONTEXT;
4453 if (!message) return IE_INVAL;
4454 isds_message_free(message);
4455 if (!buffer) return IE_INVAL;
4458 /* Extract message from PKCS#7 structure */
4459 err = extract_cms_data(context, buffer, length,
4460 &xml_stream, &xml_stream_length);
4461 if (err) goto leave;
4463 isds_log(ILF_ISDS, ILL_DEBUG, (outgoing) ?
4464 _("Signed outgoing message content:\n%.*s\nEnd of message\n") :
4465 _("Signed incoming message content:\n%.*s\nEnd of message\n"),
4466 xml_stream_length, xml_stream);
4468 /* Convert extracted messages XML stream into XPath context */
4469 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
4470 if (!message_doc) {
4471 err = IE_XML;
4472 goto leave;
4474 xpath_ctx = xmlXPathNewContext(message_doc);
4475 if (!xpath_ctx) {
4476 err = IE_ERROR;
4477 goto leave;
4479 /* XXX: Name spaces mangled for outgoing direction:
4480 * http://isds.czechpoint.cz/v20/SentMessage:
4482 * <q:MessageDownloadResponse
4483 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
4484 * <q:dmReturnedMessage>
4485 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
4486 * <p:dmID>151916</p:dmID>
4487 * ...
4488 * </p:dmDm>
4489 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
4490 * ...
4491 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
4492 * </q:dmReturnedMessage>
4493 * </q:MessageDownloadResponse>
4495 * XXX: Name spaces mangled for incoming direction:
4496 * http://isds.czechpoint.cz/v20/message:
4498 * <q:MessageDownloadResponse
4499 * xmlns:q="http://isds.czechpoint.cz/v20/message">
4500 * <q:dmReturnedMessage>
4501 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
4502 * <p:dmID>151916</p:dmID>
4503 * ...
4504 * </p:dmDm>
4505 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
4506 * ...
4507 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
4508 * </q:dmReturnedMessage>
4509 * </q:MessageDownloadResponse>
4511 * Stupidity of ISDS developers is unlimited */
4512 if (register_namespaces(xpath_ctx, (outgoing) ?
4513 MESSAGE_NS_SIGNED_OUTGOING : MESSAGE_NS_SIGNED_INCOMING)) {
4514 err = IE_ERROR;
4515 goto leave;
4517 /* XXX: Embeded message XML document is always rooted as
4518 * /sisds:MessageDownloadResponse (even outgoing message). */
4519 result = xmlXPathEvalExpression(
4520 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
4521 xpath_ctx);
4522 if (!result) {
4523 err = IE_ERROR;
4524 goto leave;
4526 /* Empty embedded message */
4527 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4528 isds_printf_message(context,
4529 _("XML document embedded into PKCS#7 structure is not "
4530 "sisds:dmReturnedMessage document"));
4531 err = IE_ISDS;
4532 goto leave;
4534 /* More embedded messages */
4535 if (result->nodesetval->nodeNr > 1) {
4536 isds_printf_message(context,
4537 _("Embeded XML document into PKCS#7 structure has more "
4538 "root sisds:dmReturnedMessage elements"));
4539 err = IE_ISDS;
4540 goto leave;
4542 /* One embedded message */
4543 xpath_ctx->node = result->nodesetval->nodeTab[0];
4545 /* Extract the message */
4546 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
4547 if (err) goto leave;
4549 /* Append raw CMS structure into message */
4550 switch (strategy) {
4551 case BUFFER_DONT_STORE:
4552 break;
4553 case BUFFER_COPY:
4554 (*message)->raw = malloc(length);
4555 if (!(*message)->raw) {
4556 err = IE_NOMEM;
4557 goto leave;
4559 memcpy((*message)->raw, buffer, length);
4560 (*message)->raw_length = length;
4561 break;
4562 case BUFFER_MOVE:
4563 (*message)->raw = (void *) buffer;
4564 (*message)->raw_length = length;
4565 break;
4566 default:
4567 err = IE_ENUM;
4568 goto leave;
4572 leave:
4573 if (err) {
4574 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
4575 isds_message_free(message);
4578 xmlFreeDoc(message_doc);
4579 cms_data_free(xml_stream);
4580 xmlXPathFreeObject(result);
4581 xmlXPathFreeContext(xpath_ctx);
4583 if (!err)
4584 isds_log(ILF_ISDS, ILL_DEBUG,
4585 _("Signed message loaded successfully.\n"));
4586 return err;
4590 /* Download signed incoming/outgoing message identified by ID.
4591 * @context is session context
4592 * @output is true for outging message, false for incoming message
4593 * @message_id is message identifier (you can get them from
4594 * isds_get_list_of_{sent,received}_messages())
4595 * @message is automatically reallocated message retrieved from ISDS. The raw
4596 * memeber will be filled with PKCS#7 structure in DER format. */
4597 _hidden isds_error isds_get_signed_message(struct isds_ctx *context,
4598 const _Bool outgoing, const char *message_id,
4599 struct isds_message **message) {
4601 isds_error err = IE_SUCCESS;
4602 xmlDocPtr response = NULL;
4603 xmlChar *code = NULL, *status_message = NULL;
4604 xmlXPathContextPtr xpath_ctx = NULL;
4605 xmlXPathObjectPtr result = NULL;
4606 char *encoded_structure = NULL;
4607 void *raw = NULL;
4608 size_t raw_length = 0;
4610 if (!context) return IE_INVALID_CONTEXT;
4611 if (!message) return IE_INVAL;
4612 isds_message_free(message);
4614 /* Do request and check for success */
4615 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
4616 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
4617 BAD_CAST "SignedMessageDownload",
4618 message_id, &response, &code, &status_message);
4619 if (err) goto leave;
4621 /* Extract data */
4622 xpath_ctx = xmlXPathNewContext(response);
4623 if (!xpath_ctx) {
4624 err = IE_ERROR;
4625 goto leave;
4627 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4628 err = IE_ERROR;
4629 goto leave;
4631 result = xmlXPathEvalExpression(
4632 (outgoing) ? BAD_CAST
4633 "/isds:SignedSentMessageDownloadResponse/isds:dmSignature" :
4634 BAD_CAST "/isds:SignedMessageDownloadResponse/isds:dmSignature",
4635 xpath_ctx);
4636 if (!result) {
4637 err = IE_ERROR;
4638 goto leave;
4640 /* Empty response */
4641 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4642 char *message_id_locale = utf82locale((char*) message_id);
4643 isds_printf_message(context,
4644 (outgoing) ?
4645 _("Server did not return any message for ID `%s' "
4646 "on SignedSentMessageDownload request") :
4647 _("Server did not return any message for ID `%s' "
4648 "on SignedMessageDownload request"),
4649 message_id_locale);
4650 free(message_id_locale);
4651 err = IE_ISDS;
4652 goto leave;
4654 /* More reponses */
4655 if (result->nodesetval->nodeNr > 1) {
4656 char *message_id_locale = utf82locale((char*) message_id);
4657 isds_printf_message(context,
4658 (outgoing) ?
4659 _("Server did return more messages for ID `%s' "
4660 "on SignedSentMessageDownload request") :
4661 _("Server did return more messages for ID `%s' "
4662 "on SignedMessageDownload request"),
4663 message_id_locale);
4664 free(message_id_locale);
4665 err = IE_ISDS;
4666 goto leave;
4668 /* One response */
4669 xpath_ctx->node = result->nodesetval->nodeTab[0];
4671 /* Extract PKCS#7 structure */
4672 EXTRACT_STRING(".", encoded_structure);
4673 if (!encoded_structure) {
4674 isds_log_message(context, _("dmSignature element is empty"));
4677 /* Here we have message as standalone CMS in encoded_structure.
4678 * We don't need any other data, free them: */
4679 xmlXPathFreeObject(result); result = NULL;
4680 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
4681 zfree(code);
4682 zfree(status_message);
4683 xmlFreeDoc(response); response = NULL;
4686 /* Decode PKCS#7 to DER format */
4687 raw_length = b64decode(encoded_structure, &raw);
4688 if (raw_length == (size_t) -1) {
4689 isds_log_message(context,
4690 _("Error while Base64-decoding PKCS#7 structure"));
4691 err = IE_ERROR;
4692 goto leave;
4694 zfree(encoded_structure);
4696 /* Parse message */
4697 err = isds_load_signed_message(context, outgoing, raw, raw_length,
4698 message, BUFFER_MOVE);
4699 if (err) goto leave;
4701 raw = NULL;
4703 leave:
4704 if (err) {
4705 isds_message_free(message);
4708 free(encoded_structure);
4709 xmlXPathFreeObject(result);
4710 xmlXPathFreeContext(xpath_ctx);
4711 free(raw);
4713 free(code);
4714 free(status_message);
4715 xmlFreeDoc(response);
4717 if (!err)
4718 isds_log(ILF_ISDS, ILL_DEBUG,
4719 (outgoing) ?
4720 _("SignedSentMessageDownload request processed by server "
4721 "successfully.\n") :
4722 _("SignedMessageDownload request processed by server "
4723 "successfully.\n")
4725 return err;
4729 /* Download signed incoming message identified by ID.
4730 * @context is session context
4731 * @message_id is message identifier (you can get them from
4732 * isds_get_list_of_received_messages())
4733 * @message is automatically reallocated message retrieved from ISDS. The raw
4734 * memeber will be filled with PKCS#7 structure in DER format. */
4735 isds_error isds_get_signed_received_message(struct isds_ctx *context,
4736 const char *message_id, struct isds_message **message) {
4737 return isds_get_signed_message(context, 0, message_id, message);
4741 /* Download signed outgoing message identified by ID.
4742 * @context is session context
4743 * @message_id is message identifier (you can get them from
4744 * isds_get_list_of_sent_messages())
4745 * @message is automatically reallocated message retrieved from ISDS. The raw
4746 * memeber will be filled with PKCS#7 structure in DER format. */
4747 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
4748 const char *message_id, struct isds_message **message) {
4749 return isds_get_signed_message(context, 1, message_id, message);
4753 /* Retrieve hash of message identified by ID stored in ISDS.
4754 * @context is session context
4755 * @message_id is message identifier
4756 * @hash is automatically reallocated message hash downloaded from ISDS.
4757 * Message must exist in system and must not be deleted. */
4758 isds_error isds_download_message_hash(struct isds_ctx *context,
4759 const char *message_id, struct isds_hash **hash) {
4761 isds_error err = IE_SUCCESS;
4762 xmlDocPtr response = NULL;
4763 xmlChar *code = NULL, *status_message = NULL;
4764 xmlXPathContextPtr xpath_ctx = NULL;
4765 xmlXPathObjectPtr result = NULL;
4767 if (!context) return IE_INVALID_CONTEXT;
4769 isds_hash_free(hash);
4771 err = build_send_check_message_request(context, SERVICE_DM_INFO,
4772 BAD_CAST "VerifyMessage", message_id,
4773 &response, &code, &status_message);
4774 if (err) goto leave;
4777 /* Extract data */
4778 xpath_ctx = xmlXPathNewContext(response);
4779 if (!xpath_ctx) {
4780 err = IE_ERROR;
4781 goto leave;
4783 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4784 err = IE_ERROR;
4785 goto leave;
4787 result = xmlXPathEvalExpression(
4788 BAD_CAST "/isds:VerifyMessageResponse",
4789 xpath_ctx);
4790 if (!result) {
4791 err = IE_ERROR;
4792 goto leave;
4794 /* Empty response */
4795 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4796 char *message_id_locale = utf82locale((char*) message_id);
4797 isds_printf_message(context,
4798 _("Server did not return any response for ID `%s' "
4799 "on VerifyMessage request"), message_id_locale);
4800 free(message_id_locale);
4801 err = IE_ISDS;
4802 goto leave;
4804 /* More responses */
4805 if (result->nodesetval->nodeNr > 1) {
4806 char *message_id_locale = utf82locale((char*) message_id);
4807 isds_printf_message(context,
4808 _("Server did return more responses for ID `%s' "
4809 "on VerifyMessage request"), message_id_locale);
4810 free(message_id_locale);
4811 err = IE_ISDS;
4812 goto leave;
4814 /* One response */
4815 xpath_ctx->node = result->nodesetval->nodeTab[0];
4817 /* Extract the hash */
4818 err = find_and_extract_DmHash(context, hash, xpath_ctx);
4820 leave:
4821 if (err) {
4822 isds_hash_free(hash);
4825 xmlXPathFreeObject(result);
4826 xmlXPathFreeContext(xpath_ctx);
4828 free(code);
4829 free(status_message);
4830 xmlFreeDoc(response);
4832 if (!err)
4833 isds_log(ILF_ISDS, ILL_DEBUG,
4834 _("VerifyMessage request processed by server "
4835 "successfully.\n")
4837 return err;
4841 /* Mark message as read. This is a transactional commit function to acknoledge
4842 * to ISDS the message has been downloaded and processed by client properly.
4843 * @context is session context
4844 * @message_id is message identifier. */
4845 isds_error isds_mark_message_read(struct isds_ctx *context,
4846 const char *message_id) {
4848 isds_error err = IE_SUCCESS;
4849 xmlDocPtr response = NULL;
4850 xmlChar *code = NULL, *status_message = NULL;
4852 if (!context) return IE_INVALID_CONTEXT;
4854 /* Do request and check for success */
4855 err = build_send_check_message_request(context, SERVICE_DM_INFO,
4856 BAD_CAST "MarkMessageAsDownloaded", message_id,
4857 &response, &code, &status_message);
4859 free(code);
4860 free(status_message);
4861 xmlFreeDoc(response);
4863 if (!err)
4864 isds_log(ILF_ISDS, ILL_DEBUG,
4865 _("MarkMessageAsDownloaded request processed by server "
4866 "successfully.\n")
4868 return err;
4872 #undef INSERT_STRING_ATTRIBUTE
4873 #undef INSERT_ULONGINTNOPTR
4874 #undef INSERT_ULONGINT
4875 #undef INSERT_LONGINT
4876 #undef INSERT_BOOLEAN
4877 #undef INSERT_STRING
4878 #undef EXTRACT_STRING_ATTRIBUTE
4879 #undef EXTRACT_ULONGINT
4880 #undef EXTRACT_LONGINT
4881 #undef EXTRACT_BOOLEAN
4882 #undef EXTRACT_STRING
4885 /* Compute hash of message from raw representation and store it into envelope.
4886 * Original hash structure will be destroyed in envelope.
4887 * @context is session context
4888 * @message is message carrying raw XML message blob
4889 * @algorithm is desired hash algorithm to use */
4890 isds_error isds_compute_message_hash(struct isds_ctx *context,
4891 struct isds_message *message, const isds_hash_algorithm algorithm) {
4892 isds_error err = IE_SUCCESS;
4893 xmlDocPtr message_doc = NULL;
4894 xmlXPathContextPtr xpath_ctx = NULL;
4895 xmlXPathObjectPtr result = NULL;
4896 char *buffer = 0;
4897 size_t length;
4898 struct isds_hash *new_hash = NULL;
4901 if (!context) return IE_INVALID_CONTEXT;
4902 if (!message) return IE_INVAL;
4904 if (!message->raw) {
4905 isds_log_message(context,
4906 _("Message does not carry raw XML representation"));
4907 return IE_INVAL;
4910 /* Parse raw message */
4911 message_doc = xmlParseMemory(message->raw, message->raw_length);
4912 if (!message_doc) {
4913 isds_log_message(context,
4914 _("Message does not carry well-formed XML representation"));
4915 err = IE_XML;
4916 goto leave;
4919 /* Find dmDM element */
4920 xpath_ctx = xmlXPathNewContext(message_doc);
4921 if (!xpath_ctx) {
4922 err = IE_ERROR;
4923 goto leave;
4925 if (register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4926 err = IE_ERROR;
4927 goto leave;
4929 result = xmlXPathEvalExpression(
4930 BAD_CAST "/isds:dmReturnedMessage/isds:dmDm", xpath_ctx);
4931 if (!result) {
4932 err = IE_ERROR;
4933 goto leave;
4935 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4936 isds_log_message(context,
4937 _("Raw message does not contain isds:dmDm element"));
4938 err = IE_XML;
4939 goto leave;
4941 if (result->nodesetval->nodeNr > 1) {
4942 isds_log_message(context,
4943 _("Raw message contains more isds:dmDm elements"));
4944 err = IE_XML;
4945 goto leave;
4947 xpath_ctx->node = result->nodesetval->nodeTab[0];
4949 /* XXX: We need all childern of isds:dmDm: elements, text nodes, PIs,
4950 * CDATA, comments. Is asterisk sufficient? */
4951 result = xmlXPathEvalExpression(BAD_CAST "*", xpath_ctx);
4952 if (!result) {
4953 err = IE_ERROR;
4954 goto leave;
4957 /* Extract dmDM content as bit stream */
4958 err = dump_nodeset(context, message_doc, result->nodesetval,
4959 (void**) &buffer, &length);
4960 if (err) goto leave;
4962 /* Free memory */
4963 xmlXPathFreeObject(result); result = NULL;
4964 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
4965 xmlFreeDoc(message_doc); message_doc = NULL;
4967 /* TODO: Compute hash */
4968 new_hash = calloc(1, sizeof(*new_hash));
4969 if (!new_hash) {
4970 err = IE_NOMEM;
4971 goto leave;
4973 new_hash->algorithm = algorithm;
4974 err = compute_hash(buffer, length, new_hash);
4975 if (err) {
4976 isds_log_message(context, _("Could not compute message hash"));
4977 goto leave;
4980 /* Save cumputed hash */
4981 if (!message->envelope) {
4982 message->envelope = calloc(1, sizeof(*message->envelope));
4983 if (!message->envelope) {
4984 err = IE_NOMEM;
4985 goto leave;
4988 isds_hash_free(&message->envelope->hash);
4989 message->envelope->hash = new_hash;
4991 leave:
4992 if (err) {
4993 isds_hash_free(&new_hash);
4996 free(buffer);
4997 xmlXPathFreeObject(result);
4998 xmlXPathFreeContext(xpath_ctx);
4999 xmlFreeDoc(message_doc);
5000 return err;
5004 /* Search for document by document ID in list of documents. IDs are compared
5005 * as UTF-8 string.
5006 * @documents is list of isds_documents
5007 * @id is document identifier
5008 * @return first matching document or NULL. */
5009 const struct isds_document *isds_find_document_by_id(
5010 const struct isds_list *documents, const char *id) {
5011 const struct isds_list *item;
5012 const struct isds_document *document;
5014 for (item = documents; item; item = item->next) {
5015 document = (struct isds_document *) item->data;
5016 if (!document) continue;
5018 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
5019 return document;
5022 return NULL;
5026 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
5027 struct isds_message **message);
5028 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
5029 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
5030 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
5031 struct isds_address **address);
5033 int isds_message_free(struct isds_message **message);
5034 int isds_address_free(struct isds_address **address);
5038 /* Makes known all relevant namespaces to given XPath context
5039 * @xpat_ctx is XPath context
5040 * @message_ns selects propper message name space. Unsisnged and signed
5041 * messages differs.
5042 * prefix and to URI ISDS_NS */
5043 _hidden isds_error register_namespaces(xmlXPathContextPtr xpath_ctx,
5044 const message_ns_type message_ns) {
5045 const xmlChar *message_namespace = NULL;
5047 if (!xpath_ctx) return IE_ERROR;
5049 switch(message_ns) {
5050 case MESSAGE_NS_UNSIGNED:
5051 message_namespace = BAD_CAST ISDS_NS; break;
5052 case MESSAGE_NS_SIGNED_INCOMING:
5053 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
5054 case MESSAGE_NS_SIGNED_OUTGOING:
5055 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
5056 default:
5057 return IE_ENUM;
5060 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
5061 return IE_ERROR;
5062 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
5063 return IE_ERROR;
5064 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
5065 return IE_ERROR;
5066 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
5067 return IE_ERROR;
5068 return IE_SUCCESS;