isds_get_received_message(): Change XPath context to message
[libisds.git] / src / isds.c
blobf786c20ea9d578aae10324fe2d8d4834eaa20848
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"
13 /* Free isds_list with all member data.
14 * @list list to free, on return will be NULL */
15 void isds_list_free(struct isds_list **list) {
16 struct isds_list *item, *next_item;
18 if (!list || !*list) return;
20 for(item = *list; item; item = next_item) {
21 (item->destructor)(&(item->data));
22 next_item = item->next;
23 free(item);
26 *list = NULL;
30 /* Deallocate structure isds_PersonName recursively and NULL it */
31 static void isds_PersonName_free(struct isds_PersonName **person_name) {
32 if (!person_name || !*person_name) return;
34 free((*person_name)->pnFirstName);
35 free((*person_name)->pnMiddleName);
36 free((*person_name)->pnLastName);
37 free((*person_name)->pnLastNameAtBirth);
39 free(*person_name);
40 *person_name = NULL;
44 /* Deallocate structure isds_BirthInfo recursively and NULL it */
45 static void isds_BirthInfo_free(struct isds_BirthInfo **birth_info) {
46 if (!birth_info || !*birth_info) return;
48 free((*birth_info)->biDate);
49 free((*birth_info)->biCity);
50 free((*birth_info)->biCounty);
51 free((*birth_info)->biState);
53 free(*birth_info);
54 *birth_info = NULL;
58 /* Deallocate structure isds_Address recursively and NULL it */
59 static void isds_Address_free(struct isds_Address **address) {
60 if (!address || !*address) return;
62 free((*address)->adCity);
63 free((*address)->adStreet);
64 free((*address)->adNumberInStreet);
65 free((*address)->adNumberInMunicipality);
66 free((*address)->adZipCode);
67 free((*address)->adState);
69 free(*address);
70 *address = NULL;
74 /* Deallocate structure isds_DbOwnerInfo recursively and NULL it */
75 void isds_DbOwnerInfo_free(struct isds_DbOwnerInfo **db_owner_info) {
76 if (!db_owner_info || !*db_owner_info) return;
78 free((*db_owner_info)->dbID);
79 free((*db_owner_info)->dbType);
80 free((*db_owner_info)->ic);
81 isds_PersonName_free(&((*db_owner_info)->personName));
82 free((*db_owner_info)->firmName);
83 isds_BirthInfo_free(&((*db_owner_info)->birthInfo));
84 isds_Address_free(&((*db_owner_info)->address));
85 free((*db_owner_info)->nationality);
86 free((*db_owner_info)->email);
87 free((*db_owner_info)->telNumber);
88 free((*db_owner_info)->identifier);
89 free((*db_owner_info)->registryCode);
90 free((*db_owner_info)->dbState);
91 free((*db_owner_info)->dbEffectiveOVM);
93 free(*db_owner_info);
94 *db_owner_info = NULL;
98 /* Deallocate struct isds_envelope recurisvely and NULL it */
99 void isds_envelope_free(struct isds_envelope **envelope) {
100 if (!envelope || !*envelope) return;
102 free((*envelope)->dmID);
103 free((*envelope)->dbIDSender);
104 free((*envelope)->dmSender);
105 free((*envelope)->dmSenderAddress);
106 free((*envelope)->dmSenderType);
107 free((*envelope)->dmRecipient);
108 free((*envelope)->dmRecipientAddress);
109 free((*envelope)->dmAmbiguousRecipient);
111 free((*envelope)->dmOrdinal);
112 free((*envelope)->dmMessageStatus);
113 free((*envelope)->dmDeliveryTime);
114 free((*envelope)->dmAcceptanceTime);
116 free((*envelope)->dmSenderOrgUnit);
117 free((*envelope)->dmSenderOrgUnitNum);
118 free((*envelope)->dbIDRecipient);
119 free((*envelope)->dmRecipientOrgUnit);
120 free((*envelope)->dmRecipientOrgUnitNum);
121 free((*envelope)->dmToHands);
122 free((*envelope)->dmAnnotation);
123 free((*envelope)->dmRecipientRefNumber);
124 free((*envelope)->dmSenderRefNumber);
125 free((*envelope)->dmRecipientIdent);
126 free((*envelope)->dmSenderIdent);
128 free((*envelope)->dmLegalTitleLaw);
129 free((*envelope)->dmLegalTitleYear);
130 free((*envelope)->dmLegalTitleSect);
131 free((*envelope)->dmLegalTitlePar);
132 free((*envelope)->dmLegalTitlePoint);
134 free((*envelope)->dmPersonalDelivery);
135 free((*envelope)->dmAllowSubstDelivery);
136 free((*envelope)->dmOVM);
138 free(*envelope);
139 *envelope = NULL;
143 /* Deallocate struct isds_message recurisvely and NULL it */
144 void isds_message_free(struct isds_message **message) {
145 if (!message || !*message) return;
147 free((*message)->raw);
148 isds_envelope_free(&((*message)->envelope));
149 isds_list_free(&((*message)->documents));
151 free(*message);
152 *message = NULL;
156 /* Deallocate struct isds_document recurisvely and NULL it */
157 void isds_document_free(struct isds_document **document) {
158 if (!document || !*document) return;
160 free((*document)->data);
161 free((*document)->dmMimeType);
162 free((*document)->dmFileGuid);
163 free((*document)->dmUpFileGuid);
164 free((*document)->dmFileDescr);
165 free((*document)->dmFormat);
167 free(*document);
168 *document = NULL;
172 /* Initialize ISDS library.
173 * Global function, must be called before other functions.
174 * If it failes you can not use ISDS library and must call isds_cleanup() to
175 * free partially inititialized global variables. */
176 isds_error isds_init(void) {
177 /* NULL global variables */
178 xml_node = NULL;
179 soap_ns = NULL;
180 log_facilities = ILF_NONE;
181 log_level = ILL_NONE;
183 /* Initialize CURL */
184 if (curl_global_init(CURL_GLOBAL_ALL)) {
185 return IE_ERROR;
188 /* This can _exit() current program. Find not so assertive check. */
189 LIBXML_TEST_VERSION;
191 /* Allocate global variables */
192 if (!(xml_node = xmlNewNode(NULL, BAD_CAST "global-element")))
193 return IE_ERROR;
194 if (!(soap_ns = xmlNewNs(NULL, BAD_CAST SOAP_NS, BAD_CAST "soap")))
195 return IE_ERROR;
196 if (!(isds_ns = xmlNewNs(NULL, BAD_CAST ISDS_NS, BAD_CAST "isds")))
197 return IE_ERROR;
199 return IE_SUCCESS;
203 /* Deinicialize ISDS library.
204 * Global function, must be called as last library function. */
205 isds_error isds_cleanup(void) {
206 /* XML */
207 xmlFreeNs(isds_ns);
208 xmlFreeNs(soap_ns);
209 xmlFreeNode(xml_node);
210 xmlCleanupParser();
212 /* Curl */
213 curl_global_cleanup();
215 return IE_SUCCESS;
219 /* Return text description of ISDS error */
220 char *isds_strerror(const isds_error error) {
221 switch (error) {
222 case IE_SUCCESS:
223 return(_("Success")); break;
224 case IE_ERROR:
225 return(_("Unspecified error")); break;
226 case IE_NOTSUP:
227 return(_("Not supported")); break;
228 case IE_INVAL:
229 return(_("Invalid value")); break;
230 case IE_INVALID_CONTEXT:
231 return(_("Invalid context")); break;
232 case IE_NOT_LOGGED_IN:
233 return(_("Not logged in")); break;
234 case IE_CONNECTION_CLOSED:
235 return(_("Connection closed")); break;
236 case IE_TIMED_OUT:
237 return(_("Timed out")); break;
238 case IE_NOEXIST:
239 return(_("Not exist")); break;
240 case IE_NOMEM:
241 return(_("Out of memory")); break;
242 case IE_NETWORK:
243 return(_("Network problem")); break;
244 case IE_SOAP:
245 return(_("SOAP problem")); break;
246 case IE_XML:
247 return(_("XML problem")); break;
248 case IE_ISDS:
249 return(_("ISDS server problem")); break;
250 case IE_ENUM:
251 return(_("Invalid enum value")); break;
252 case IE_DATE:
253 return(_("Invalid date value")); break;
254 case IE_2BIG:
255 return(_("Too big")); break;
256 default:
257 return(_("Unknown error"));
262 /* Create ISDS context.
263 * Each context can be used for different sessions to (possibly) differnet
264 * ISDS server with different credentials. */
265 struct isds_ctx *isds_ctx_create(void) {
266 struct isds_ctx *context;
267 context = malloc(sizeof(*context));
268 if (context) memset(context, 0, sizeof(*context));
269 return context;
273 /* Destroy ISDS context and free memmory.
274 * @context will be NULLed on success. */
275 isds_error isds_ctx_free(struct isds_ctx **context) {
276 if (!context || !*context) {
277 return IE_INVALID_CONTEXT;
280 /* Discard credentials */
281 isds_logout(*context);
283 /* Free other structures */
284 free((*context)->tls_verify_server);
285 free((*context)->tls_ca_file);
286 free((*context)->tls_ca_dir);
287 free((*context)->long_message);
289 free(*context);
290 *context = NULL;
291 return IE_SUCCESS;
295 /* Return long message text produced by library fucntion, e.g. detailed error
296 * mesage. Returned pointer is only valid until new library function is
297 * called for the same context. Could be NULL, especially if NULL context is
298 * supplied. Return string is locale encoded. */
299 char *isds_long_message(const struct isds_ctx *context) {
300 if (!context) return NULL;
301 return context->long_message;
305 /* Stores message into context' long_message buffer.
306 * Application can pick the message up using isds_long_message().
307 * NULL @message truncates the buffer but does not deallocate it.
308 * @message is coded in locale encoding */
309 _hidden isds_error isds_log_message(struct isds_ctx *context,
310 const char *message) {
311 char *buffer;
312 size_t length;
314 if (!context) return IE_INVALID_CONTEXT;
316 /* FIXME: Check for integer overflow */
317 length = 1 + ((message) ? strlen(message) : 0);
318 buffer = realloc(context->long_message, length);
319 if (!buffer) return IE_NOMEM;
321 if (message)
322 strcpy(buffer, message);
323 else
324 *buffer = '\0';
326 context->long_message = buffer;
327 return IE_SUCCESS;
331 /* Appends message into context' long_message buffer.
332 * Application can pick the message up using isds_long_message().
333 * NULL message has void effect. */
334 _hidden isds_error isds_append_message(struct isds_ctx *context,
335 const char *message) {
336 char *buffer;
337 size_t old_length, length;
339 if (!context) return IE_INVALID_CONTEXT;
340 if (!message) return IE_SUCCESS;
341 if (!context->long_message)
342 return isds_log_message(context, message);
344 old_length = strlen(context->long_message);
345 /* FIXME: Check for integer overflow */
346 length = 1 + old_length + strlen(message);
347 buffer = realloc(context->long_message, length);
348 if (!buffer) return IE_NOMEM;
350 strcpy(buffer + old_length, message);
352 context->long_message = buffer;
353 return IE_SUCCESS;
357 /* Stores formated message into context' long_message buffer.
358 * Application can pick the message up using isds_long_message(). */
359 _hidden isds_error isds_printf_message(struct isds_ctx *context,
360 const char *format, ...) {
361 va_list ap;
362 int length;
364 if (!context) return IE_INVALID_CONTEXT;
365 va_start(ap, format);
366 length = isds_vasprintf(&(context->long_message), format, ap);
367 va_end(ap);
369 return (length < 0) ? IE_ERROR: IE_SUCCESS;
373 /* Set logging up.
374 * @facilities is bitmask of isds_log_facility values,
375 * @level is verbosity level. */
376 void isds_set_logging(const unsigned int facilities,
377 const isds_log_level level) {
378 log_facilities = facilities;
379 log_level = level;
383 /* Log @message in class @facility with log @level into global log. @message
384 * is printf(3) formating string, variadic arguments may be neccessary.
385 * For debugging purposes. */
386 _hidden isds_error isds_log(const isds_log_facility facility,
387 const isds_log_level level, const char *message, ...) {
388 va_list ap;
390 if (level > log_level) return IE_SUCCESS;
391 if (!(log_facilities & facility)) return IE_SUCCESS;
392 if (!message) return IE_INVAL;
394 /* TODO: Allow to register output function privided by application
395 * (e.g. fprintf to stderr or copy to text area GUI widget). */
397 va_start(ap, message);
398 vfprintf(stderr, message, ap);
399 va_end(ap);
400 /* Line buffered printf is default.
401 * fflush(stderr);*/
403 return IE_SUCCESS;
407 /* Connect to given url.
408 * It just makes TCP connection to ISDS server found in @url hostname part. */
409 /*int isds_connect(struct isds_ctx *context, const char *url);*/
411 /* Set timeout in miliseconds for each network job like connecting to server
412 * or sending message. Use 0 to disable timeout limits. */
413 isds_error isds_set_timeout(struct isds_ctx *context,
414 const unsigned int timeout) {
415 if (!context) return IE_INVALID_CONTEXT;
417 context->timeout = timeout;
419 if (context->curl) {
420 CURLcode curl_err;
422 curl_err = curl_easy_setopt(context->curl, CURLOPT_NOSIGNAL, 1);
423 if (!curl_err)
424 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT_MS,
425 context->timeout);
426 if (curl_err) return IE_ERROR;
429 return IE_SUCCESS;
433 /* Change SSL/TLS settings.
434 * @context is context which setting vill be applied to
435 * @option is name of option. It determines the type of last argument. See
436 * isds_tls_option definition for more info.
437 * @... is value of new setting. Type is determined by @option
438 * */
439 isds_error isds_set_tls(struct isds_ctx *context, const isds_tls_option option,
440 ...) {
441 isds_error err = IE_SUCCESS;
442 va_list ap;
443 char *pointer, *string;
445 if (!context) return IE_INVALID_CONTEXT;
447 va_start(ap, option);
449 #define REPLACE_VA_STRING(destination) \
450 string = va_arg(ap, char *); \
451 if (string) { \
452 pointer = realloc((destination), 1 + strlen(string)); \
453 if (!pointer) { err = IE_NOMEM; goto leave; } \
454 strcpy(pointer, string); \
455 (destination) = pointer; \
456 } else { \
457 free(destination); \
458 (destination) = NULL; \
461 switch (option) {
462 case ITLS_VERIFY_SERVER:
463 if (!context->tls_verify_server) {
464 context->tls_verify_server =
465 malloc(sizeof(*context->tls_verify_server));
466 if (!context->tls_verify_server) {
467 err = IE_NOMEM; goto leave;
470 *context->tls_verify_server = (_Bool) (0 != va_arg(ap, int));
471 break;
473 case ITLS_CA_FILE:
474 REPLACE_VA_STRING(context->tls_ca_file);
475 break;
476 case ITLS_CA_DIRECTORY:
477 REPLACE_VA_STRING(context->tls_ca_dir);
478 break;
480 default:
481 err = IE_ENUM; goto leave;
484 #undef REPLACE_VA_STRING
486 leave:
487 va_end(ap);
488 return err;
492 /* Discard credentials.
493 * Only that. It does not cause log out, connection close or similar. */
494 static isds_error discard_credentials(struct isds_ctx *context) {
495 if(!context) return IE_INVALID_CONTEXT;
497 if (context->username) {
498 memset(context->username, 0, strlen(context->username));
499 free(context->username);
500 context->username = NULL;
502 if (context->password) {
503 memset(context->password, 0, strlen(context->password));
504 free(context->password);
505 context->password = NULL;
508 return IE_SUCCESS;
512 /* Connect and log in into ISDS server.
513 * @url is address of ISDS web service
514 * @username is user name of ISDS user
515 * @password is user's secret password
516 * @certificate is NULL terminated string with PEM formated client's
517 * certificate. Use NULL if only password autentication should be performed.
518 * @key is private key for client's certificate as (base64 encoded?) NULL
519 * terminated string. Use NULL if only password autentication is desired.
520 * */
521 isds_error isds_login(struct isds_ctx *context, const char *url,
522 const char *username, const char *password,
523 const char *certificate, const char* key) {
524 isds_error err = IE_NOT_LOGGED_IN;
525 isds_error soap_err;
526 xmlNsPtr isds_ns = NULL;
527 xmlNodePtr request = NULL;
528 xmlNodePtr response = NULL;
530 if (!context) return IE_INVALID_CONTEXT;
531 if (!url || !username || !password) return IE_INVAL;
532 if (certificate || key) return IE_NOTSUP;
534 /* Store configuration */
535 free(context->url);
536 context->url = strdup(url);
537 if (!(context->url))
538 return IE_NOMEM;
540 /* Close connection if already logged in */
541 if (context->curl) {
542 close_connection(context);
545 /* Prepare CURL handle */
546 context->curl = curl_easy_init();
547 if (!(context->curl))
548 return IE_ERROR;
550 /* Build login request */
551 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
552 if (!request) {
553 isds_log_message(context, _("Could build ISDS login request"));
554 return IE_ERROR;
556 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
557 if(!isds_ns) {
558 isds_log_message(context, _("Could not create ISDS name space"));
559 xmlFreeNode(request);
560 return IE_ERROR;
562 xmlSetNs(request, isds_ns);
564 /* Store credentials */
565 /* FIXME: mlock password
566 * (I have a library) */
567 discard_credentials(context);
568 context->username = strdup(username);
569 context->password = strdup(password);
570 if (!(context->username && context->password)) {
571 discard_credentials(context);
572 xmlFreeNode(request);
573 return IE_NOMEM;
576 isds_log(ILF_ISDS, ILL_DEBUG, _("Logging user %s into server %s\n"),
577 username, url);
579 /* Send login request */
580 soap_err = soap(context, "dz", request, &response);
582 /* Remove credentials */
583 discard_credentials(context);
585 /* Destroy login request */
586 xmlFreeNode(request);
588 if (soap_err) {
589 xmlFreeNodeList(response);
590 close_connection(context);
591 return soap_err;
594 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
595 * authentication succeeded if soap_err == IE_SUCCESS */
596 err = IE_SUCCESS;
598 xmlFreeNodeList(response);
600 if (!err)
601 isds_log(ILF_ISDS, ILL_DEBUG,
602 _("User %s has been logged into server %s successfully\n"),
603 username, url);
604 return err;
608 /* Log out from ISDS server discards credentials and connection configuration. */
609 isds_error isds_logout(struct isds_ctx *context) {
610 if (!context) return IE_INVALID_CONTEXT;
612 /* Close connection */
613 if (context->curl) {
614 close_connection(context);
616 /* Discard credentials for sure. They should not survive isds_login(),
617 * even successful .*/
618 discard_credentials(context);
619 free(context->url);
620 context->url = NULL;
622 isds_log(ILF_ISDS, ILL_DEBUG, _("Logged out from ISDS server\n"));
623 } else {
624 discard_credentials(context);
626 return IE_SUCCESS;
630 /* Verify connection to ISDS is alive and server is responding.
631 * Sent dumy request to ISDS and expect dummy response. */
632 isds_error isds_ping(struct isds_ctx *context) {
633 isds_error soap_err;
634 xmlNsPtr isds_ns = NULL;
635 xmlNodePtr request = NULL;
636 xmlNodePtr response = NULL;
638 if (!context) return IE_INVALID_CONTEXT;
640 /* Check if connection is established */
641 if (!context->curl) return IE_CONNECTION_CLOSED;
644 /* Build dummy request */
645 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
646 if (!request) {
647 isds_log_message(context, _("Could build ISDS dummy request"));
648 return IE_ERROR;
650 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
651 if(!isds_ns) {
652 isds_log_message(context, _("Could not create ISDS name space"));
653 xmlFreeNode(request);
654 return IE_ERROR;
656 xmlSetNs(request, isds_ns);
658 isds_log(ILF_ISDS, ILL_DEBUG, _("Pinging ISDS server\n"));
660 /* Sent dummy request */
661 soap_err = soap(context, "dz", request, &response);
663 /* Destroy login request */
664 xmlFreeNode(request);
666 if (soap_err) {
667 isds_log(ILF_ISDS, ILL_DEBUG,
668 _("ISDS server could not be contacted\n"));
669 xmlFreeNodeList(response);
670 close_connection(context);
671 return soap_err;
674 /* XXX: Untill we don't propagate HTTP code 500 or 4xx, we can be sure
675 * authentication succeeded if soap_err == IE_SUCCESS */
676 /* TODO: ISDS documentation does not specify response body.
677 * However real server sends back DummyOperationResponse */
680 xmlFreeNodeList(response);
682 isds_log(ILF_ISDS, ILL_DEBUG, _("ISDS server alive\n"));
684 return IE_SUCCESS;
688 /* Send bogus request to ISDS.
689 * Just for test purposes */
690 isds_error isds_bogus_request(struct isds_ctx *context) {
691 isds_error err;
692 xmlNsPtr isds_ns = NULL;
693 xmlNodePtr request = NULL;
694 xmlDocPtr response = NULL;
695 xmlChar *code = NULL, *message = NULL;
697 if (!context) return IE_INVALID_CONTEXT;
699 /* Check if connection is established */
700 if (!context->curl) {
701 /* Testing printf message */
702 isds_printf_message(context, "%s", _("I said connection closed"));
703 return IE_CONNECTION_CLOSED;
707 /* Build dummy request */
708 request = xmlNewNode(NULL, BAD_CAST "X-BogusOperation");
709 if (!request) {
710 isds_log_message(context, _("Could build ISDS bogus request"));
711 return IE_ERROR;
713 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
714 if(!isds_ns) {
715 isds_log_message(context, _("Could not create ISDS name space"));
716 xmlFreeNode(request);
717 return IE_ERROR;
719 xmlSetNs(request, isds_ns);
721 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending bogus request to ISDS\n"));
723 /* Sent bogus request */
724 err = isds(context, SERVICE_DM_OPERATIONS, request, &response);
726 /* Destroy request */
727 xmlFreeNode(request);
729 if (err) {
730 isds_log(ILF_ISDS, ILL_DEBUG,
731 _("Processing ISDS response on bogus request failed\n"));
732 xmlFreeDoc(response);
733 return err;
736 /* Check for response status */
737 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
738 &code, &message, NULL);
739 if (err) {
740 isds_log(ILF_ISDS, ILL_DEBUG,
741 _("ISDS response on bogus request is missing status\n"));
742 free(code);
743 free(message);
744 xmlFreeDoc(response);
745 return err;
747 if (xmlStrcmp(code, BAD_CAST "0000")) {
748 char *code_locale = utf82locale((char*)code);
749 char *message_locale = utf82locale((char*)message);
750 isds_log(ILF_ISDS, ILL_DEBUG,
751 _("Server refused bogus request (code=%s, message=%s)\n"),
752 code_locale, message_locale);
753 /* XXX: Literal error messages from ISDS are Czech mesages
754 * (English sometimes) in UTF-8. It's hard to catch them for
755 * translation. Successfully gettextized would return in locale
756 * encoding, unsuccessfully translated would pass in UTF-8. */
757 isds_log_message(context, message_locale);
758 free(code_locale);
759 free(message_locale);
760 free(code);
761 free(message);
762 xmlFreeDoc(response);
763 return IE_ISDS;
767 free(code);
768 free(message);
769 xmlFreeDoc(response);
771 isds_log(ILF_ISDS, ILL_DEBUG,
772 _("Bogus message accepted by server. This should not happen.\n"));
774 return IE_SUCCESS;
778 /* Convert UTF-8 @string represantion of ISDS dbType to enum @type */
779 static isds_error string2isds_DbType(xmlChar *string, isds_DbType *type) {
780 if (!string || !type) return IE_INVAL;
782 if (!xmlStrcmp(string, BAD_CAST "FO"))
783 *type = DBTYPE_FO;
784 else if (!xmlStrcmp(string, BAD_CAST "PFO"))
785 *type = DBTYPE_PFO;
786 else if (!xmlStrcmp(string, BAD_CAST "PFO_ADVOK"))
787 *type = DBTYPE_PFO_ADVOK;
788 else if (!xmlStrcmp(string, BAD_CAST "PFO_DANPOR"))
789 *type = DBTYPE_PFO_DANPOR;
790 else if (!xmlStrcmp(string, BAD_CAST "PFO_INSSPR"))
791 *type = DBTYPE_PFO_INSSPR;
792 else if (!xmlStrcmp(string, BAD_CAST "PO"))
793 *type = DBTYPE_PO;
794 else if (!xmlStrcmp(string, BAD_CAST "PO_ZAK"))
795 *type = DBTYPE_PO_ZAK;
796 else if (!xmlStrcmp(string, BAD_CAST "PO_REQ"))
797 *type = DBTYPE_PO_REQ;
798 else if (!xmlStrcmp(string, BAD_CAST "OVM"))
799 *type = DBTYPE_OVM;
800 else if (!xmlStrcmp(string, BAD_CAST "OVM_NOTAR"))
801 *type = DBTYPE_OVM_NOTAR;
802 else if (!xmlStrcmp(string, BAD_CAST "OVM_EXEKUT"))
803 *type = DBTYPE_OVM_EXEKUT;
804 else if (!xmlStrcmp(string, BAD_CAST "OVM_REQ"))
805 *type = DBTYPE_OVM_REQ;
806 else
807 return IE_ENUM;
808 return IE_SUCCESS;
812 /* Convert ISDS dbType enum @type to UTF-8 string.
813 * @Return pointer to static string, or NULL if unkwnow enum value */
814 static const xmlChar *isds_DbType2string(const isds_DbType type) {
815 switch(type) {
816 case DBTYPE_FO: return(BAD_CAST "FO"); break;
817 case DBTYPE_PFO: return(BAD_CAST "PFO"); break;
818 case DBTYPE_PFO_ADVOK: return(BAD_CAST "PFO_ADVOK"); break;
819 case DBTYPE_PFO_DANPOR: return(BAD_CAST "PFO_DAPOR"); break;
820 case DBTYPE_PFO_INSSPR: return(BAD_CAST "PFO_INSSPR"); break;
821 case DBTYPE_PO: return(BAD_CAST "PO"); break;
822 case DBTYPE_PO_ZAK: return(BAD_CAST "PO_ZAK"); break;
823 case DBTYPE_PO_REQ: return(BAD_CAST "PO_REQ"); break;
824 case DBTYPE_OVM: return(BAD_CAST "OVM"); break;
825 case DBTYPE_OVM_NOTAR: return(BAD_CAST "OVM_NOTAR"); break;
826 case DBTYPE_OVM_EXEKUT: return(BAD_CAST "OVM_EXEKUT"); break;
827 case DBTYPE_OVM_REQ: return(BAD_CAST "OVM_REQ"); break;
828 default: return NULL; break;
833 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
834 * @Return pointer to static string, or NULL if unkwnow enum value */
835 static const xmlChar *isds_FileMetaType2string(const isds_FileMetaType type) {
836 switch(type) {
837 case FILEMETATYPE_MAIN: return(BAD_CAST "main"); break;
838 case FILEMETATYPE_ENCLOSURE: return(BAD_CAST "enclosure"); break;
839 case FILEMETATYPE_SIGNATURE: return(BAD_CAST "signature"); break;
840 case FILEMETATYPE_META: return(BAD_CAST "meta"); break;
841 default: return NULL; break;
846 /* Convert UTF-8 @string represantion of ISO 8601 date to @time.
847 * XXX: Not all ISO formats are supported */
848 static isds_error datestring2tm(const xmlChar *string, struct tm *time) {
849 char *offset;
850 if (!string || !time) return IE_INVAL;
852 /* xsd:date is ISO 8601 string, thus ASCII */
853 offset = strptime((char*)string, "%Y-%m-%d", time);
854 if (offset && *offset == '\0')
855 return IE_SUCCESS;
857 offset = strptime((char*)string, "%Y%m%d", time);
858 if (offset && *offset == '\0')
859 return IE_SUCCESS;
861 offset = strptime((char*)string, "%Y-%j", time);
862 if (offset && *offset == '\0')
863 return IE_SUCCESS;
865 return IE_NOTSUP;
869 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
870 static isds_error tm2datestring(const struct tm *time, xmlChar **string) {
871 if (!time || !string) return IE_INVAL;
873 if (-1 == isds_asprintf((char **) string, "%d-%02d-%02d",
874 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
875 return IE_ERROR;
877 return IE_SUCCESS;
881 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
882 * respects the @time microseconds too. */
883 static isds_error timeval2timestring(const struct timeval *time,
884 xmlChar **string) {
885 struct tm broken;
887 if (!time || !string) return IE_INVAL;
889 if (!gmtime_r(&time->tv_sec, &broken)) return IE_DATE;
890 if (time->tv_usec < 0 || time->tv_usec > 999999) return IE_DATE;
892 /* TODO: small negative year should be formated as "-0012". This is not
893 * true for glibc "%04d". We should implement it.
894 * TODO: What's type of time->tv_usec exactly? Unsigned? Absolute?
895 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
896 if (-1 == isds_asprintf((char **) string,
897 "%04d-%02d-%02dT%02d:%02d:%02d.%06ld",
898 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
899 broken.tm_hour, broken.tm_min, broken.tm_sec,
900 time->tv_usec))
901 return IE_ERROR;
903 return IE_SUCCESS;
907 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
908 * It respects microseconds too.
909 * In case of error, @time will be freed. */
910 static isds_error timestring2timeval(const xmlChar *string,
911 struct timeval **time) {
912 struct tm broken;
913 char *offset, *delim, *endptr;
914 char subseconds[7];
915 int offset_hours, offset_minutes;
916 int i;
918 if (!time) return IE_INVAL;
920 memset(&broken, 0, sizeof(broken));
922 if (!*time) {
923 *time = calloc(1, sizeof(**time));
924 if (!*time) return IE_NOMEM;
925 } else {
926 memset(*time, 0, sizeof(**time));
930 /* xsd:date is ISO 8601 string, thus ASCII */
931 /*TODO: negative year */
933 /* Parse date and time without subseconds and offset */
934 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
935 if (!offset) {
936 free(*time); *time = NULL;
937 return IE_DATE;
940 /* Get subseconds */
941 if (*offset == '.' ) {
942 offset++;
944 /* Copy first 6 digits, padd it with zeros.
945 * XXX: It truncates longer number, no round.
946 * Current server implementation uses only milisecond resolution. */
947 /* TODO: isdigit() is locale sensitive */
948 for (i = 0;
949 i < sizeof(subseconds)/sizeof(char) - 1 && isdigit(*offset);
950 i++, offset++) {
951 subseconds[i] = *offset;
953 for (; i < sizeof(subseconds)/sizeof(char) - 1; i++) {
954 subseconds[i] = '0';
956 subseconds[6] = '\0';
958 /* Convert it into integer */
959 (*time)->tv_usec = strtol(subseconds, &endptr, 10);
960 if (*endptr != '\0' || (*time)->tv_usec == LONG_MIN ||
961 (*time)->tv_usec == LONG_MAX) {
962 free(*time); *time = NULL;
963 return IE_DATE;
966 /* move to the zone offset delimiter */
967 delim = strchr(offset, '-');
968 if (!delim)
969 delim = strchr(offset, '+');
970 offset = delim;
973 /* Get zone offset */
974 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
975 * "" equals to "Z" and it means UTC zone. */
976 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
977 * colon separator */
978 if (*offset == '-' || *offset == '+') {
979 offset++;
980 if (2 != sscanf(offset, "%2d:%2d", &offset_hours, &offset_minutes)) {
981 free(*time); *time = NULL;
982 return IE_DATE;
984 broken.tm_hour -= offset_hours;
985 broken.tm_min -= offset_minutes * ((offset_hours<0) ? -1 : 1);
988 /* Convert to time_t */
989 switch_tz_to_utc();
990 (*time)->tv_sec = mktime(&broken);
991 switch_tz_to_native();
992 if ((*time)->tv_sec == (time_t) -1) {
993 free(*time); *time = NULL;
994 return IE_DATE;
997 return IE_SUCCESS;
1001 /* Convert unsigned int into isds_message_status.
1002 * @context is session context
1003 * @number is pointer to number value. NULL will be treated as invalid value.
1004 * @status is automatically reallocated status
1005 * @return IE_SUCCESS, or error code and free status */
1006 static isds_error uint2isds_message_status(struct isds_ctx *context,
1007 const unsigned long int *number, isds_message_status **status) {
1008 if (!context) return IE_INVALID_CONTEXT;
1009 if (!status) return IE_INVAL;
1011 free(*status); *status = NULL;
1012 if (!number) return IE_INVAL;
1014 if (*number < 1 || *number > 9) {
1015 isds_printf_message(context, _("Invalid messsage status value: %lu"),
1016 *number);
1017 return IE_ENUM;
1020 *status = malloc(sizeof(**status));
1021 if (!*status) return IE_NOMEM;
1023 **status = 1 << *number;
1024 return IE_SUCCESS;
1028 /* Following EXTRACT_* macros expects @result, @xpath_ctx, @err, @context
1029 * and leave lable */
1030 #define EXTRACT_STRING(element, string) \
1031 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
1032 if (!result) { \
1033 err = IE_ERROR; \
1034 goto leave; \
1036 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
1037 if (result->nodesetval->nodeNr > 1) { \
1038 isds_log_message(context, _("Multiple " element " element")); \
1039 err = IE_ERROR; \
1040 goto leave; \
1042 (string) = (char *) \
1043 xmlXPathCastNodeSetToString(result->nodesetval); \
1044 if (!(string)) { \
1045 err = IE_ERROR; \
1046 goto leave; \
1050 #define EXTRACT_BOOLEAN(element, booleanPtr) \
1052 char *string = NULL; \
1053 EXTRACT_STRING(element, string); \
1055 if (string) { \
1056 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
1057 if (!(booleanPtr)) { \
1058 free(string); \
1059 err = IE_NOMEM; \
1060 goto leave; \
1063 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
1064 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
1065 *(booleanPtr) = 1; \
1066 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
1067 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
1068 *(booleanPtr) = 0; \
1069 else { \
1070 char *string_locale = utf82locale((char*)string); \
1071 isds_printf_message(context, \
1072 _(element " value is not valid boolean: "), \
1073 string_locale); \
1074 free(string_locale); \
1075 free(string); \
1076 err = IE_ERROR; \
1077 goto leave; \
1080 free(string); \
1084 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
1086 char *string = NULL; \
1087 EXTRACT_STRING(element, string); \
1088 if (string) { \
1089 long int number; \
1090 char *endptr; \
1092 number = strtol((char*)string, &endptr, 10); \
1094 if (*endptr != '\0') { \
1095 char *string_locale = utf82locale((char *)string); \
1096 isds_printf_message(context, \
1097 _(element" is not valid integer: %s"), \
1098 string_locale); \
1099 free(string_locale); \
1100 free(string); \
1101 err = IE_ISDS; \
1102 goto leave; \
1105 if (number == LONG_MIN || number == LONG_MAX) { \
1106 char *string_locale = utf82locale((char *)string); \
1107 isds_printf_message(context, \
1108 _(element " value out of range of long int: %s"), \
1109 string_locale); \
1110 free(string_locale); \
1111 free(string); \
1112 err = IE_ERROR; \
1113 goto leave; \
1116 free(string); string = NULL; \
1118 if (!(preallocated)) { \
1119 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
1120 if (!(longintPtr)) { \
1121 err = IE_NOMEM; \
1122 goto leave; \
1125 *(longintPtr) = number; \
1129 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
1131 char *string = NULL; \
1132 EXTRACT_STRING(element, string); \
1133 if (string) { \
1134 long int number; \
1135 char *endptr; \
1137 number = strtol((char*)string, &endptr, 10); \
1139 if (*endptr != '\0') { \
1140 char *string_locale = utf82locale((char *)string); \
1141 isds_printf_message(context, \
1142 _(element" is not valid integer: %s"), \
1143 string_locale); \
1144 free(string_locale); \
1145 free(string); \
1146 err = IE_ISDS; \
1147 goto leave; \
1150 if (number == LONG_MIN || number == LONG_MAX) { \
1151 char *string_locale = utf82locale((char *)string); \
1152 isds_printf_message(context, \
1153 _(element " value out of range of long int: %s"), \
1154 string_locale); \
1155 free(string_locale); \
1156 free(string); \
1157 err = IE_ERROR; \
1158 goto leave; \
1161 free(string); string = NULL; \
1162 if (number < 0) { \
1163 isds_printf_message(context, \
1164 _(element " value is negative: %ld"), number); \
1165 err = IE_ERROR; \
1166 goto leave; \
1169 if (!(preallocated)) { \
1170 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
1171 if (!(ulongintPtr)) { \
1172 err = IE_NOMEM; \
1173 goto leave; \
1176 *(ulongintPtr) = number; \
1181 #define INSERT_STRING(parent, element, string) \
1182 node = xmlNewTextChild(parent, NULL, BAD_CAST (element), \
1183 (xmlChar *) (string)); \
1184 if (!node) { \
1185 isds_printf_message(context, _("Could not add " element " child to " \
1186 "%s element"), (parent)->name); \
1187 err = IE_ERROR; \
1188 goto leave; \
1191 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
1192 if ((booleanPtr)) { \
1193 if (*(booleanPtr)) { INSERT_STRING(parent, element, "true"); } \
1194 else { INSERT_STRING(parent, element, "false") } \
1195 } else { INSERT_STRING(parent, element, NULL) }
1197 #define INSERT_LONGINT(parent, element, longintPtr, buffer) \
1198 if ((longintPtr)) { \
1199 /* FIXME: locale sensitive */ \
1200 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
1201 err = IE_NOMEM; \
1202 goto leave; \
1204 INSERT_STRING(parent, element, buffer) \
1205 free(buffer); (buffer) = NULL; \
1206 } else { INSERT_STRING(parent, element, NULL) }
1208 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) \
1209 if ((ulongintPtr)) { \
1210 /* FIXME: locale sensitive */ \
1211 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
1212 err = IE_NOMEM; \
1213 goto leave; \
1215 INSERT_STRING(parent, element, buffer) \
1216 free(buffer); (buffer) = NULL; \
1217 } else { INSERT_STRING(parent, element, NULL) }
1219 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
1220 /* FIXME: locale sensitive */ \
1221 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
1222 err = IE_NOMEM; \
1223 goto leave; \
1225 INSERT_STRING(parent, element, buffer) \
1226 free(buffer); (buffer) = NULL; \
1228 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
1229 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
1230 (xmlChar *) (string)); \
1231 if (!attribute_node) { \
1232 isds_printf_message(context, _("Could not add " attribute \
1233 " attribute to %s element"), (parent)->name); \
1234 err = IE_ERROR; \
1235 goto leave; \
1239 /* Convert isds:dBOwnerInfo XML tree into structure
1240 * @context is ISDS context
1241 * @db_owner_info is automically reallocated box owner info structure
1242 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
1243 * In case of error @db_owner_info will be freed. */
1244 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
1245 struct isds_DbOwnerInfo **db_owner_info,
1246 xmlXPathContextPtr xpath_ctx) {
1247 isds_error err = IE_SUCCESS;
1248 xmlXPathObjectPtr result = NULL;
1249 char *string = NULL;
1251 if (!context) return IE_INVALID_CONTEXT;
1252 if (!db_owner_info) return IE_INVAL;
1253 isds_DbOwnerInfo_free(db_owner_info);
1254 if (!xpath_ctx) return IE_INVAL;
1257 *db_owner_info = calloc(1, sizeof(**db_owner_info));
1258 if (!*db_owner_info) {
1259 err = IE_NOMEM;
1260 goto leave;
1263 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
1265 EXTRACT_STRING("isds:dbType", string);
1266 if (string) {
1267 (*db_owner_info)->dbType =
1268 calloc(1, sizeof(*((*db_owner_info)->dbType)));
1269 if (!(*db_owner_info)->dbType) {
1270 err = IE_NOMEM;
1271 goto leave;
1273 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
1274 if (err) {
1275 free((*db_owner_info)->dbType);
1276 (*db_owner_info)->dbType = NULL;
1277 if (err == IE_ENUM) {
1278 err = IE_ISDS;
1279 isds_printf_message(context, _("Unknown isds:dbType: %s"),
1280 (char *)string);
1282 goto leave;
1284 free(string); string = NULL;
1287 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
1289 (*db_owner_info)->personName =
1290 calloc(1, sizeof(*((*db_owner_info)->personName)));
1291 if (!(*db_owner_info)->personName) {
1292 err = IE_NOMEM;
1293 goto leave;
1295 EXTRACT_STRING("isds:pnFirstName",
1296 (*db_owner_info)->personName->pnFirstName);
1297 EXTRACT_STRING("isds:pnMiddleName",
1298 (*db_owner_info)->personName->pnMiddleName);
1299 EXTRACT_STRING("isds:pnLastName",
1300 (*db_owner_info)->personName->pnLastName);
1301 EXTRACT_STRING("isds:pnLastNameAtBirth",
1302 (*db_owner_info)->personName->pnLastNameAtBirth);
1303 if (!(*db_owner_info)->personName->pnFirstName &&
1304 !(*db_owner_info)->personName->pnMiddleName &&
1305 !(*db_owner_info)->personName->pnLastName &&
1306 !(*db_owner_info)->personName->pnLastNameAtBirth)
1307 isds_PersonName_free(&(*db_owner_info)->personName);
1309 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
1311 (*db_owner_info)->birthInfo =
1312 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
1313 if (!(*db_owner_info)->birthInfo) {
1314 err = IE_NOMEM;
1315 goto leave;
1317 EXTRACT_STRING("isds:biDate", string);
1318 if (string) {
1319 (*db_owner_info)->birthInfo->biDate =
1320 calloc(1, sizeof(*((*db_owner_info)->birthInfo->biDate)));
1321 if (!(*db_owner_info)->birthInfo->biDate) {
1322 err = IE_NOMEM;
1323 goto leave;
1325 err = datestring2tm((xmlChar *)string,
1326 (*db_owner_info)->birthInfo->biDate);
1327 if (err) {
1328 free((*db_owner_info)->birthInfo->biDate);
1329 (*db_owner_info)->birthInfo->biDate = NULL;
1330 if (err == IE_NOTSUP) {
1331 err = IE_ISDS;
1332 isds_printf_message(context,
1333 _("Invalid isds:biDate value: %s"), (char *)string);
1335 goto leave;
1337 free(string); string = NULL;
1339 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
1340 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
1341 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
1342 if (!(*db_owner_info)->birthInfo->biDate &&
1343 !(*db_owner_info)->birthInfo->biCity &&
1344 !(*db_owner_info)->birthInfo->biCounty &&
1345 !(*db_owner_info)->birthInfo->biState)
1346 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
1348 (*db_owner_info)->address =
1349 calloc(1, sizeof(*((*db_owner_info)->address)));
1350 if (!(*db_owner_info)->address) {
1351 err = IE_NOMEM;
1352 goto leave;
1354 EXTRACT_STRING("isds:adCity",
1355 (*db_owner_info)->address->adCity);
1356 EXTRACT_STRING("isds:adStreet",
1357 (*db_owner_info)->address->adStreet);
1358 EXTRACT_STRING("isds:adNumberInStreet",
1359 (*db_owner_info)->address->adNumberInStreet);
1360 EXTRACT_STRING("isds:adNumberInMunicipality",
1361 (*db_owner_info)->address->adNumberInMunicipality);
1362 EXTRACT_STRING("isds:adZipCode",
1363 (*db_owner_info)->address->adZipCode);
1364 EXTRACT_STRING("isds:adState",
1365 (*db_owner_info)->address->adState);
1366 if (!(*db_owner_info)->address->adCity &&
1367 !(*db_owner_info)->address->adStreet &&
1368 !(*db_owner_info)->address->adNumberInStreet &&
1369 !(*db_owner_info)->address->adNumberInMunicipality &&
1370 !(*db_owner_info)->address->adZipCode &&
1371 !(*db_owner_info)->address->adState)
1372 isds_Address_free(&(*db_owner_info)->address);
1374 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
1375 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
1376 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
1377 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
1378 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
1380 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
1382 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
1383 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
1384 (*db_owner_info)->dbOpenAddressing);
1386 leave:
1387 if (err) isds_DbOwnerInfo_free(db_owner_info);
1388 free(string);
1389 xmlXPathFreeObject(result);
1390 return err;
1394 /* Convert XSD gMessageEnvelope group of elements from XML tree into
1395 * isds_envelope structure. The envelope is automatically allocated but not
1396 * reallocated. The date are just appended into envelope structure.
1397 * @context is ISDS context
1398 * @envelope is automically allocated message envelope structure
1399 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
1400 * In case of error @envelope will be freed. */
1401 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
1402 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
1403 isds_error err = IE_SUCCESS;
1404 xmlXPathObjectPtr result = NULL;
1406 if (!context) return IE_INVALID_CONTEXT;
1407 if (!envelope) return IE_INVAL;
1408 if (!xpath_ctx) return IE_INVAL;
1411 if (!*envelope) {
1412 /* Allocate envelope */
1413 *envelope = calloc(1, sizeof(**envelope));
1414 if (!*envelope) {
1415 err = IE_NOMEM;
1416 goto leave;
1418 } else {
1419 /* Else free former data */
1420 zfree((*envelope)->dmID);
1421 zfree((*envelope)->dbIDSender);
1422 zfree((*envelope)->dmSender);
1423 zfree((*envelope)->dmSenderAddress);
1424 zfree((*envelope)->dmSenderType);
1425 zfree((*envelope)->dmRecipient);
1426 zfree((*envelope)->dmRecipientAddress);
1427 zfree((*envelope)->dmAmbiguousRecipient);
1430 /* Extract envelope elements added by ISDS
1431 * (XSD: gMessageEnvelope type) */
1432 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
1433 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
1434 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
1435 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
1436 /* XML Schema does not guaratee enumratation. It's plain xs:int. */
1437 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
1438 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
1439 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
1440 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
1441 (*envelope)->dmAmbiguousRecipient);
1443 leave:
1444 if (err) isds_envelope_free(envelope);
1445 xmlXPathFreeObject(result);
1446 return err;
1450 /* Convert other envelope elements from XML tree into isds_envelope structure:
1451 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
1452 * The envelope is automatically allocated but not reallocated.
1453 * The data are just appended into envelope structure.
1454 * @context is ISDS context
1455 * @envelope is automically allocated message envelope structure
1456 * @xpath_ctx is XPath context with current node as parent desired elements
1457 * In case of error @envelope will be freed. */
1458 static isds_error append_status_size_times(struct isds_ctx *context,
1459 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
1460 isds_error err = IE_SUCCESS;
1461 xmlXPathObjectPtr result = NULL;
1462 char *string = NULL;
1463 unsigned long int *unumber = NULL;
1465 if (!context) return IE_INVALID_CONTEXT;
1466 if (!envelope) return IE_INVAL;
1467 if (!xpath_ctx) return IE_INVAL;
1470 if (!*envelope) {
1471 /* Allocate new */
1472 *envelope = calloc(1, sizeof(**envelope));
1473 if (!*envelope) {
1474 err = IE_NOMEM;
1475 goto leave;
1477 } else {
1478 /* Free old data */
1479 zfree((*envelope)->dmMessageStatus);
1480 zfree((*envelope)->dmAttachmentSize);
1481 zfree((*envelope)->dmDeliveryTime);
1482 zfree((*envelope)->dmAcceptanceTime);
1486 /* dmMessageStatus element is mandatory */
1487 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber, 0);
1488 err = uint2isds_message_status(context, unumber,
1489 &((*envelope)->dmMessageStatus));
1490 if (err) {
1491 if (err == IE_ENUM) err = IE_ISDS;
1492 goto leave;
1494 free(unumber); unumber = NULL;
1496 EXTRACT_ULONGINT("isds:dmAttachmentSize", (*envelope)->dmAttachmentSize, 0);
1498 EXTRACT_STRING("isds:dmDeliveryTime", string);
1499 if (string) {
1500 err = timestring2timeval((xmlChar *) string,
1501 &((*envelope)->dmDeliveryTime));
1502 if (err) {
1503 char *string_locale = utf82locale(string);
1504 if (err == IE_DATE) err = IE_ISDS;
1505 isds_printf_message(context,
1506 _("Could not convert dmDeliveryTime as ISO time: %s"),
1507 string_locale);
1508 free(string_locale);
1509 goto leave;
1513 EXTRACT_STRING("isds:dmAcceptanceTime", string);
1514 if (string) {
1515 err = timestring2timeval((xmlChar *) string,
1516 &((*envelope)->dmAcceptanceTime));
1517 if (err) {
1518 char *string_locale = utf82locale(string);
1519 if (err == IE_DATE) err = IE_ISDS;
1520 isds_printf_message(context,
1521 _("Could not convert dmAcceptanceTime as ISO time: %s"),
1522 string_locale);
1523 free(string_locale);
1524 goto leave;
1528 leave:
1529 if (err) isds_envelope_free(envelope);
1530 free(unumber);
1531 free(string);
1532 xmlXPathFreeObject(result);
1533 return err;
1537 /* Convert isds:dmRecord XML tree into structure
1538 * @context is ISDS context
1539 * @envelope is automically reallocated message envelope structure
1540 * @xpath_ctx is XPath context with current node as isds:dmRecord element
1541 * In case of error @envelope will be freed. */
1542 static isds_error extract_DmRecord(struct isds_ctx *context,
1543 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
1544 isds_error err = IE_SUCCESS;
1545 xmlXPathObjectPtr result = NULL;
1547 if (!context) return IE_INVALID_CONTEXT;
1548 if (!envelope) return IE_INVAL;
1549 isds_envelope_free(envelope);
1550 if (!xpath_ctx) return IE_INVAL;
1553 *envelope = calloc(1, sizeof(**envelope));
1554 if (!*envelope) {
1555 err = IE_NOMEM;
1556 goto leave;
1560 /* Extract tRecord data */
1561 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
1563 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
1564 * dmAcceptanceTime. */
1565 err = append_status_size_times(context, envelope, xpath_ctx);
1566 if (err) goto leave;
1568 /* Extract envelope elements added by ISDS
1569 * (XSD: gMessageEnvelope type) */
1570 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
1571 if (err) goto leave;
1573 /* Extract envelope elements added by sender or ISDS
1574 * (XSD: gMessageEnvelopeSub type) */
1575 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
1576 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
1577 (*envelope)->dmSenderOrgUnitNum, 0);
1578 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
1579 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
1580 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
1581 (*envelope)->dmSenderOrgUnitNum, 0);
1582 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
1583 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
1584 EXTRACT_STRING("isds:dmRecipientRefNumber",
1585 (*envelope)->dmRecipientRefNumber);
1586 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
1587 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
1588 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
1590 /* Extract envelope elements regarding law refference */
1591 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
1592 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
1593 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
1594 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
1595 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
1597 /* Extract envelope other elements */
1598 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
1599 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
1600 (*envelope)->dmAllowSubstDelivery);
1601 /* dmOVM can not be obtained from ISDS */
1603 leave:
1604 if (err) isds_envelope_free(envelope);
1605 xmlXPathFreeObject(result);
1606 return err;
1610 /* Convert XSD tReturnedMessage XML tree into message structure
1611 * @context is ISDS context
1612 * @message is automically reallocated message structure
1613 * @xpath_ctx is XPath context with current node as tReturnedMessage element
1614 * type
1615 * In case of error @message will be freed. */
1616 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
1617 struct isds_message **message, xmlXPathContextPtr xpath_ctx) {
1618 isds_error err = IE_SUCCESS;
1619 xmlXPathObjectPtr result = NULL;
1620 xmlNodePtr message_node;
1622 if (!context) return IE_INVALID_CONTEXT;
1623 if (!message) return IE_INVAL;
1624 isds_message_free(message);
1625 if (!xpath_ctx) return IE_INVAL;
1628 *message = calloc(1, sizeof(**message));
1629 if (!*message) {
1630 err = IE_NOMEM;
1631 goto leave;
1634 /* Save message XPATH context node */
1635 message_node = xpath_ctx->node;
1638 /* extract dmDM */
1639 xmlXPathFreeObject(result); result = NULL;
1640 result = xmlXPathEvalExpression(BAD_CAST "isds:dmDm", xpath_ctx);
1641 if (!result) {
1642 err = IE_ERROR;
1643 goto leave;
1645 /* Empty response */
1646 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
1647 char *parent_locale = utf82locale((char*) xpath_ctx->node->name);
1648 isds_printf_message(context,
1649 _("%s element does not contain dmDM child"), parent_locale);
1650 free(parent_locale);
1651 err = IE_ISDS;
1652 goto leave;
1654 /* More messages */
1655 if (result->nodesetval->nodeNr > 1) {
1656 char *parent_locale = utf82locale((char*) xpath_ctx->node->name);
1657 isds_printf_message(context,
1658 _("%s element contains multiple dmDM childs"), parent_locale);
1659 free(parent_locale);
1660 err = IE_ISDS;
1661 goto leave;
1664 xpath_ctx->node = result->nodesetval->nodeTab[0];
1665 xmlXPathFreeObject(result); result = NULL;
1666 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
1667 if (err) goto leave;
1669 /* TODO: Extract dmFiles */
1672 /* Restore context to message */
1673 xmlXPathFreeObject(result); result = NULL;
1674 xpath_ctx->node = message_node;
1676 /* TODO: dmHash, dmQTimestamp, */
1678 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
1679 * dmAcceptanceTime. */
1680 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
1681 if (err) goto leave;
1683 /* TODO: save XML blob */
1684 leave:
1685 if (err) isds_message_free(message);
1686 xmlXPathFreeObject(result);
1687 return err;
1691 /* Convert isds_document structure into XML tree and append to dmFiles node.
1692 * @context is session context
1693 * @document is ISDS document
1694 * @dm_files is XML element the resulting tree will be appended to as a child.
1695 * @return error code, in case of error context' message is filled. */
1696 static isds_error insert_document(struct isds_ctx *context,
1697 struct isds_document *document, xmlNodePtr dm_files) {
1698 isds_error err = IE_SUCCESS;
1699 xmlNodePtr new_file = NULL, file = NULL, node;
1700 xmlAttrPtr attribute_node;
1701 xmlChar *base64data = NULL;
1703 if (!context) return IE_INVALID_CONTEXT;
1704 if (!document || !dm_files) return IE_INVAL;
1706 /* Allocate new dmFile */
1707 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
1708 if (!new_file) {
1709 isds_printf_message(context, _("Could not allocate main dmFile"));
1710 err = IE_ERROR;
1711 goto leave;
1713 /* Append the new dmFile.
1714 * XXX: Main document must go first */
1715 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
1716 file = xmlAddPrevSibling(dm_files->children, new_file);
1717 else
1718 file = xmlAddChild(dm_files, new_file);
1720 if (!file) {
1721 xmlFreeNode(new_file); new_file = NULL;
1722 isds_printf_message(context, _("Could not add dmFile child to "
1723 "%s element"), dm_files->name);
1724 err = IE_ERROR;
1725 goto leave;
1728 /* @dmMimeType is required */
1729 if (!document->dmMimeType) {
1730 isds_log_message(context,
1731 _("Document is missing mandatory MIME type definition"));
1732 err = IE_INVAL;
1733 goto leave;
1735 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
1737 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
1738 if (!string) {
1739 isds_printf_message(context,
1740 _("Document has unkown dmFileMetaType: %ld"),
1741 document->dmFileMetaType);
1742 err = IE_ENUM;
1743 goto leave;
1745 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
1747 if (document->dmFileGuid) {
1748 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
1750 if (document->dmUpFileGuid) {
1751 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
1754 /* @dmFileDescr is required */
1755 if (!document->dmFileDescr) {
1756 isds_log_message(context,
1757 _("Document is missing mandatory description (title)"));
1758 err = IE_INVAL;
1759 goto leave;
1761 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
1763 if (document->dmFormat) {
1764 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
1768 /* Insert content (data) of the document. */
1769 /* XXX; Only base64 is implemented currently. */
1770 base64data = (xmlChar *) b64encode(document->data, document->data_length);
1771 if (!base64data) {
1772 isds_printf_message(context,
1773 _("Not enought memory to encode %zd bytes into Base64"),
1774 document->data_length);
1775 err = IE_NOMEM;
1776 goto leave;
1778 INSERT_STRING(file, "dmEncodedContent", base64data);
1779 free(base64data);
1781 leave:
1782 return err;
1786 /* Get data about logged in user and his box. */
1787 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
1788 struct isds_DbOwnerInfo **db_owner_info) {
1789 isds_error err = IE_SUCCESS;
1790 xmlNsPtr isds_ns = NULL;
1791 xmlNodePtr request = NULL;
1792 xmlDocPtr response = NULL;
1793 xmlChar *code = NULL, *message = NULL;
1794 xmlNodePtr node;
1795 xmlXPathContextPtr xpath_ctx = NULL;
1796 xmlXPathObjectPtr result = NULL;
1797 char *string = NULL;
1799 if (!context) return IE_INVALID_CONTEXT;
1800 if (!db_owner_info) return IE_INVAL;
1802 /* Check if connection is established */
1803 if (!context->curl) return IE_CONNECTION_CLOSED;
1806 /* Build GetOwnerInfoFromLogin request */
1807 request = xmlNewNode(NULL, BAD_CAST "GetOwnerInfoFromLogin");
1808 if (!request) {
1809 isds_log_message(context,
1810 _("Could build GetOwnerInfoFromLogin request"));
1811 return IE_ERROR;
1813 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1814 if(!isds_ns) {
1815 isds_log_message(context, _("Could not create ISDS name space"));
1816 xmlFreeNode(request);
1817 return IE_ERROR;
1819 xmlSetNs(request, isds_ns);
1820 node = xmlNewChild(request, NULL, BAD_CAST "dbDummy", NULL);
1821 if (!node) {
1822 isds_log_message(context, _("Could nod add dbDummy Child to "
1823 "GetOwnerInfoFromLogin element"));
1824 xmlFreeNode(request);
1825 return IE_ERROR;
1829 isds_log(ILF_ISDS, ILL_DEBUG,
1830 _("Sending GetOwnerInfoFromLogin request to ISDS\n"));
1832 /* Sent request */
1833 err = isds(context, SERVICE_DB_SUPPLEMENTARY, request, &response);
1835 /* Destroy request */
1836 xmlFreeNode(request);
1838 if (err) {
1839 isds_log(ILF_ISDS, ILL_DEBUG,
1840 _("Processing ISDS response on GetOwnerInfoFromLogin "
1841 "request failed\n"));
1842 xmlFreeDoc(response);
1843 return err;
1846 /* Check for response status */
1847 err = isds_response_status(context, SERVICE_DB_SUPPLEMENTARY, response,
1848 &code, &message, NULL);
1849 if (err) {
1850 isds_log(ILF_ISDS, ILL_DEBUG,
1851 _("ISDS response on GetOwnerInfoFromLogin request is "
1852 "missing status\n"));
1853 free(code);
1854 free(message);
1855 xmlFreeDoc(response);
1856 return err;
1858 if (xmlStrcmp(code, BAD_CAST "0000")) {
1859 char *code_locale = utf82locale((char*)code);
1860 char *message_locale = utf82locale((char*)message);
1861 isds_log(ILF_ISDS, ILL_DEBUG,
1862 _("Server refused GetOwnerInfoFromLogin request "
1863 "(code=%s, message=%s)\n"), code_locale, message_locale);
1864 isds_log_message(context, message_locale);
1865 free(code_locale);
1866 free(message_locale);
1867 free(code);
1868 free(message);
1869 xmlFreeDoc(response);
1870 return IE_ISDS;
1873 /* Extract data */
1874 /* Prepare stucture */
1875 isds_DbOwnerInfo_free(db_owner_info);
1876 *db_owner_info = calloc(1, sizeof(**db_owner_info));
1877 if (!*db_owner_info) {
1878 err = IE_NOMEM;
1879 goto leave;
1881 xpath_ctx = xmlXPathNewContext(response);
1882 if (!xpath_ctx) {
1883 err = IE_ERROR;
1884 goto leave;
1886 if (register_namespaces(xpath_ctx)) {
1887 err = IE_ERROR;
1888 goto leave;
1891 /* Set context node */
1892 result = xmlXPathEvalExpression(BAD_CAST
1893 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
1894 if (!result) {
1895 err = IE_ERROR;
1896 goto leave;
1898 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
1899 isds_log_message(context, _("Missing dbOwnerInfo element"));
1900 err = IE_ISDS;
1901 goto leave;
1903 if (result->nodesetval->nodeNr > 1) {
1904 isds_log_message(context, _("Multiple dbOwnerInfo element"));
1905 err = IE_ISDS;
1906 goto leave;
1908 xpath_ctx->node = result->nodesetval->nodeTab[0];
1909 xmlXPathFreeObject(result); result = NULL;
1911 /* Extract it */
1912 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
1914 leave:
1915 if (err) {
1916 isds_DbOwnerInfo_free(db_owner_info);
1919 free(string);
1920 xmlXPathFreeObject(result);
1921 xmlXPathFreeContext(xpath_ctx);
1923 free(code);
1924 free(message);
1925 xmlFreeDoc(response);
1927 if (!err)
1928 isds_log(ILF_ISDS, ILL_DEBUG,
1929 _("GetOwnerInfoFromLogin request processed by server "
1930 "successfully.\n"));
1932 return err;
1936 /* Find boxes suiting given criteria.
1937 * @criteria is filter. You should fill in at least some memebers.
1938 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
1939 * possibly empty. Input NULL or valid old structure.
1940 * @return:
1941 * IE_SUCCESS if search sucseeded, @boxes contains usefull data
1942 * IE_NOEXIST if no such box exists, @boxes will be NULL
1943 * IE_2BIG if too much boxes exist and server truncated the resuluts, @boxes
1944 * contains still valid data
1945 * other code if something bad happens. @boxes will be NULL. */
1946 isds_error isds_FindDataBox(struct isds_ctx *context,
1947 const struct isds_DbOwnerInfo *criteria,
1948 struct isds_list **boxes) {
1949 isds_error err = IE_SUCCESS;
1950 _Bool truncated = 0;
1951 xmlNsPtr isds_ns = NULL;
1952 xmlNodePtr request = NULL;
1953 xmlDocPtr response = NULL;
1954 xmlChar *code = NULL, *message = NULL;
1955 xmlNodePtr db_owner_info, node;
1956 xmlXPathContextPtr xpath_ctx = NULL;
1957 xmlXPathObjectPtr result = NULL;
1958 xmlChar *string = NULL;
1961 if (!context) return IE_INVALID_CONTEXT;
1962 if (!boxes) return IE_INVAL;
1963 isds_list_free(boxes);
1965 if (!criteria) {
1966 return IE_INVAL;
1969 /* Check if connection is established
1970 * TODO: This check should be done donwstairs. */
1971 if (!context->curl) return IE_CONNECTION_CLOSED;
1974 /* Build FindDataBox request */
1975 request = xmlNewNode(NULL, BAD_CAST "FindDataBox");
1976 if (!request) {
1977 isds_log_message(context,
1978 _("Could build FindDataBox request"));
1979 return IE_ERROR;
1981 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1982 if(!isds_ns) {
1983 isds_log_message(context, _("Could not create ISDS name space"));
1984 xmlFreeNode(request);
1985 return IE_ERROR;
1987 xmlSetNs(request, isds_ns);
1988 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
1989 if (!db_owner_info) {
1990 isds_log_message(context, _("Could not add dbOwnerInfo Child to "
1991 "FindDataBox element"));
1992 xmlFreeNode(request);
1993 return IE_ERROR;
1997 INSERT_STRING(db_owner_info, "dbID", criteria->dbID);
1999 /* dbType */
2000 if (criteria->dbType) {
2001 const xmlChar *type_string = isds_DbType2string(*(criteria->dbType));
2002 if (!type_string) {
2003 isds_printf_message(context, _("Invalid dbType value: %d"),
2004 *(criteria->dbType));
2005 err = IE_ENUM;
2006 goto leave;
2008 INSERT_STRING(db_owner_info, "dbType", type_string);
2011 INSERT_STRING(db_owner_info, "firmName", criteria->firmName);
2012 INSERT_STRING(db_owner_info, "ic", criteria->ic);
2013 if (criteria->personName) {
2014 INSERT_STRING(db_owner_info, "pnFirstName",
2015 criteria->personName->pnFirstName);
2016 INSERT_STRING(db_owner_info, "pnMiddleName",
2017 criteria->personName->pnMiddleName);
2018 INSERT_STRING(db_owner_info, "pnLastName",
2019 criteria->personName->pnLastName);
2020 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
2021 criteria->personName->pnLastNameAtBirth);
2023 if (criteria->birthInfo) {
2024 if (criteria->birthInfo->biDate) {
2025 if (!tm2datestring(criteria->birthInfo->biDate, &string))
2026 INSERT_STRING(db_owner_info, "biDate", string);
2027 free(string); string = NULL;
2029 INSERT_STRING(db_owner_info, "biCity", criteria->birthInfo->biCity);
2030 INSERT_STRING(db_owner_info, "biCounty", criteria->birthInfo->biCounty);
2031 INSERT_STRING(db_owner_info, "biState", criteria->birthInfo->biState);
2033 if (criteria->address) {
2034 INSERT_STRING(db_owner_info, "adCity", criteria->address->adCity);
2035 INSERT_STRING(db_owner_info, "adStreet", criteria->address->adStreet);
2036 INSERT_STRING(db_owner_info, "adNumberInStreet",
2037 criteria->address->adNumberInStreet);
2038 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
2039 criteria->address->adNumberInMunicipality);
2040 INSERT_STRING(db_owner_info, "adZipCode", criteria->address->adZipCode);
2041 INSERT_STRING(db_owner_info, "adState", criteria->address->adState);
2043 INSERT_STRING(db_owner_info, "nationality", criteria->nationality);
2044 INSERT_STRING(db_owner_info, "email", criteria->email);
2045 INSERT_STRING(db_owner_info, "telNumber", criteria->telNumber);
2046 INSERT_STRING(db_owner_info, "identifier", criteria->identifier);
2047 INSERT_STRING(db_owner_info, "registryCode", criteria->registryCode);
2049 INSERT_LONGINT(db_owner_info, "dbState", criteria->dbState, string);
2051 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", criteria->dbEffectiveOVM);
2052 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
2053 criteria->dbOpenAddressing);
2056 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending FindDataBox request to ISDS\n"));
2058 /* Sent request */
2059 err = isds(context, SERVICE_DB_SEARCH, request, &response);
2061 /* Destroy request */
2062 xmlFreeNode(request); request = NULL;
2064 if (err) {
2065 isds_log(ILF_ISDS, ILL_DEBUG,
2066 _("Processing ISDS response on FindDataBox "
2067 "request failed\n"));
2068 goto leave;
2071 /* Check for response status */
2072 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
2073 &code, &message, NULL);
2074 if (err) {
2075 isds_log(ILF_ISDS, ILL_DEBUG,
2076 _("ISDS response on FindDataBox request is missing status\n"));
2077 goto leave;
2080 /* Request processed, but nothing found */
2081 if (!xmlStrcmp(code, BAD_CAST "0002") ||
2082 !xmlStrcmp(code, BAD_CAST "5001")) {
2083 char *code_locale = utf82locale((char*)code);
2084 char *message_locale = utf82locale((char*)message);
2085 isds_log(ILF_ISDS, ILL_DEBUG,
2086 _("Server did not found any box on FindDataBox request "
2087 "(code=%s, message=%s)\n"), code_locale, message_locale);
2088 isds_log_message(context, message_locale);
2089 free(code_locale);
2090 free(message_locale);
2091 err = IE_NOEXIST;
2092 goto leave;
2095 /* Warning, not a error */
2096 if (!xmlStrcmp(code, BAD_CAST "0003")) {
2097 char *code_locale = utf82locale((char*)code);
2098 char *message_locale = utf82locale((char*)message);
2099 isds_log(ILF_ISDS, ILL_DEBUG,
2100 _("Server truncated response on FindDataBox request "
2101 "(code=%s, message=%s)\n"), code_locale, message_locale);
2102 isds_log_message(context, message_locale);
2103 free(code_locale);
2104 free(message_locale);
2105 truncated = 1;
2108 /* Other error */
2109 else if (xmlStrcmp(code, BAD_CAST "0000")) {
2110 char *code_locale = utf82locale((char*)code);
2111 char *message_locale = utf82locale((char*)message);
2112 isds_log(ILF_ISDS, ILL_DEBUG,
2113 _("Server refused FindDataBox request "
2114 "(code=%s, message=%s)\n"), code_locale, message_locale);
2115 isds_log_message(context, message_locale);
2116 free(code_locale);
2117 free(message_locale);
2118 err = IE_ISDS;
2119 goto leave;
2122 xpath_ctx = xmlXPathNewContext(response);
2123 if (!xpath_ctx) {
2124 err = IE_ERROR;
2125 goto leave;
2127 if (register_namespaces(xpath_ctx)) {
2128 err = IE_ERROR;
2129 goto leave;
2132 /* Extract boxes if they present */
2133 result = xmlXPathEvalExpression(BAD_CAST
2134 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
2135 xpath_ctx);
2136 if (!result) {
2137 err = IE_ERROR;
2138 goto leave;
2140 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2141 struct isds_list *item, *prev_item = NULL;
2142 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
2143 item = calloc(1, sizeof(*item));
2144 if (!item) {
2145 err = IE_NOMEM;
2146 goto leave;
2149 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
2150 if (i == 0) *boxes = item;
2151 else prev_item->next = item;
2152 prev_item = item;
2154 xpath_ctx->node = result->nodesetval->nodeTab[i];
2155 err = extract_DbOwnerInfo(context,
2156 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
2157 if (err) goto leave;
2161 leave:
2162 if (err) {
2163 isds_list_free(boxes);
2164 } else {
2165 if (truncated) err = IE_2BIG;
2168 free(string);
2169 xmlFreeNode(request);
2170 xmlXPathFreeObject(result);
2171 xmlXPathFreeContext(xpath_ctx);
2173 free(code);
2174 free(message);
2175 xmlFreeDoc(response);
2177 if (!err)
2178 isds_log(ILF_ISDS, ILL_DEBUG,
2179 _("FindDataBox request processed by server successfully.\n"));
2181 return err;
2185 /* Get status of a box.
2186 * @context is ISDS session context.
2187 * @box_id is UTF-8 encoded box identifier as zero terminated string
2188 * @box_status is return value of box status.
2189 * @return:
2190 * IE_SUCCESS if box has been found and its status retrieved
2191 * IE_NOEXIST if box is not known to ISDS server
2192 * or other appropriate error.
2193 * You can use isds_DbState to enumerate box status. However out of enum
2194 * range value can be returned too. This is feature because ISDS
2195 * specification leaves the set of values open.
2196 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
2197 * the box has been deleted, but ISDS still lists its former existence. */
2198 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
2199 long int *box_status) {
2200 isds_error err = IE_SUCCESS;
2201 xmlNsPtr isds_ns = NULL;
2202 xmlNodePtr request = NULL, db_id;
2203 xmlDocPtr response = NULL;
2204 xmlChar *code = NULL, *message = NULL;
2205 xmlXPathContextPtr xpath_ctx = NULL;
2206 xmlXPathObjectPtr result = NULL;
2207 xmlChar *string = NULL;
2209 if (!context) return IE_INVALID_CONTEXT;
2210 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
2212 /* Check if connection is established
2213 * TODO: This check should be done donwstairs. */
2214 if (!context->curl) return IE_CONNECTION_CLOSED;
2217 /* Build CheckDataBox request */
2218 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
2219 if (!request) {
2220 isds_log_message(context,
2221 _("Could build CheckDataBox request"));
2222 return IE_ERROR;
2224 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
2225 if(!isds_ns) {
2226 isds_log_message(context, _("Could not create ISDS name space"));
2227 xmlFreeNode(request);
2228 return IE_ERROR;
2230 xmlSetNs(request, isds_ns);
2231 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
2232 if (!db_id) {
2233 isds_log_message(context, _("Could not add dbId Child to "
2234 "CheckDataBox element"));
2235 xmlFreeNode(request);
2236 return IE_ERROR;
2240 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CheckDataBox request to ISDS\n"));
2242 /* Sent request */
2243 err = isds(context, SERVICE_DB_SEARCH, request, &response);
2245 /* Destroy request */
2246 xmlFreeNode(request);
2248 if (err) {
2249 isds_log(ILF_ISDS, ILL_DEBUG,
2250 _("Processing ISDS response on CheckDataBox "
2251 "request failed\n"));
2252 goto leave;
2255 /* Check for response status */
2256 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
2257 &code, &message, NULL);
2258 if (err) {
2259 isds_log(ILF_ISDS, ILL_DEBUG,
2260 _("ISDS response on CheckDataBox request is missing status\n"));
2261 goto leave;
2264 /* Request processed, but nothing found */
2265 if (!xmlStrcmp(code, BAD_CAST "5001")) {
2266 char *box_id_locale = utf82locale((char*)box_id);
2267 char *code_locale = utf82locale((char*)code);
2268 char *message_locale = utf82locale((char*)message);
2269 isds_log(ILF_ISDS, ILL_DEBUG,
2270 _("Server did not found box %s on CheckDataBox request "
2271 "(code=%s, message=%s)\n"),
2272 box_id_locale, code_locale, message_locale);
2273 isds_log_message(context, message_locale);
2274 free(box_id_locale);
2275 free(code_locale);
2276 free(message_locale);
2277 err = IE_NOEXIST;
2278 goto leave;
2281 /* Other error */
2282 else if (xmlStrcmp(code, BAD_CAST "0000")) {
2283 char *code_locale = utf82locale((char*)code);
2284 char *message_locale = utf82locale((char*)message);
2285 isds_log(ILF_ISDS, ILL_DEBUG,
2286 _("Server refused CheckDataBox request "
2287 "(code=%s, message=%s)\n"), code_locale, message_locale);
2288 isds_log_message(context, message_locale);
2289 free(code_locale);
2290 free(message_locale);
2291 err = IE_ISDS;
2292 goto leave;
2295 /* Extract data */
2296 xpath_ctx = xmlXPathNewContext(response);
2297 if (!xpath_ctx) {
2298 err = IE_ERROR;
2299 goto leave;
2301 if (register_namespaces(xpath_ctx)) {
2302 err = IE_ERROR;
2303 goto leave;
2305 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
2306 xpath_ctx);
2307 if (!result) {
2308 err = IE_ERROR;
2309 goto leave;
2311 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2312 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
2313 err = IE_ISDS;
2314 goto leave;
2316 if (result->nodesetval->nodeNr > 1) {
2317 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
2318 err = IE_ISDS;
2319 goto leave;
2321 xpath_ctx->node = result->nodesetval->nodeTab[0];
2322 xmlXPathFreeObject(result); result = NULL;
2324 EXTRACT_LONGINT("isds:dbState", box_status, 1);
2327 leave:
2328 free(string);
2329 xmlXPathFreeObject(result);
2330 xmlXPathFreeContext(xpath_ctx);
2332 free(code);
2333 free(message);
2334 xmlFreeDoc(response);
2336 if (!err)
2337 isds_log(ILF_ISDS, ILL_DEBUG,
2338 _("CheckDataBox request processed by server successfully.\n"));
2340 return err;
2344 /* Send a message via ISDS to a recipent
2345 * @context is session context
2346 * @outgoing_message is message to send; Some memebers are mandatory (like
2347 * dbIDRecipient), some are optional and some are irrelevant (especialy data
2348 * about sender). Included pointer to isds_list documents must contain at
2349 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
2350 * members will be filled with valid data from ISDS. Exact list of write
2351 * members is subject to change. Currently dmId is changed.
2352 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
2353 isds_error isds_send_message(struct isds_ctx *context,
2354 struct isds_message *outgoing_message) {
2356 isds_error err = IE_SUCCESS;
2357 xmlNsPtr isds_ns = NULL;
2358 xmlNodePtr request = NULL, envelope, dm_files, node;
2359 xmlDocPtr response = NULL;
2360 xmlChar *code = NULL, *message = NULL;
2361 xmlXPathContextPtr xpath_ctx = NULL;
2362 xmlXPathObjectPtr result = NULL;
2363 xmlChar *string = NULL;
2364 _Bool message_is_complete = 0;
2366 if (!context) return IE_INVALID_CONTEXT;
2367 if (!outgoing_message) return IE_INVAL;
2369 /* Check if connection is established
2370 * TODO: This check should be done donwstairs. */
2371 if (!context->curl) return IE_CONNECTION_CLOSED;
2374 /* Build CreateMessage request */
2375 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
2376 if (!request) {
2377 isds_log_message(context,
2378 _("Could build CreateMessage request"));
2379 return IE_ERROR;
2381 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
2382 if(!isds_ns) {
2383 isds_log_message(context, _("Could not create ISDS name space"));
2384 xmlFreeNode(request);
2385 return IE_ERROR;
2387 xmlSetNs(request, isds_ns);
2390 /* Build envelope */
2391 envelope = xmlNewChild(request, NULL, BAD_CAST "dmEnvelope", NULL);
2392 if (!envelope) {
2393 isds_log_message(context, _("Could not add dmEnvelope child to "
2394 "CreateMessage element"));
2395 xmlFreeNode(request);
2396 return IE_ERROR;
2399 if (!outgoing_message->envelope) {
2400 isds_log_message(context, _("outgoing message is missing envelope"));
2401 err = IE_INVAL;
2402 goto leave;
2405 INSERT_STRING(envelope, "dmSenderOrgUnit",
2406 outgoing_message->envelope->dmSenderOrgUnit);
2407 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
2408 outgoing_message->envelope->dmSenderOrgUnitNum, string);
2410 if (!outgoing_message->envelope->dbIDRecipient) {
2411 isds_log_message(context,
2412 _("outgoing message is missing recipient box identifier"));
2413 err = IE_INVAL;
2414 goto leave;
2416 INSERT_STRING(envelope, "dbIDRecipient",
2417 outgoing_message->envelope->dbIDRecipient);
2419 INSERT_STRING(envelope, "dmRecipientOrgUnit",
2420 outgoing_message->envelope->dmRecipientOrgUnit);
2421 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
2422 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
2423 INSERT_STRING(envelope, "dmToHands", outgoing_message->envelope->dmToHands);
2425 #define CHECK_FOR_STRING_LENGTH(string, limit, name) \
2426 if ((string) && xmlUTF8Strlen((xmlChar *) (string)) > (limit)) { \
2427 isds_printf_message(context, \
2428 _("%s has more than %d characters"), (name), (limit)); \
2429 err = IE_2BIG; \
2430 goto leave; \
2433 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 255,
2434 "dmAnnotation");
2435 INSERT_STRING(envelope, "dmAnnotation",
2436 outgoing_message->envelope->dmAnnotation);
2438 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
2439 50, "dmRecipientRefNumber");
2440 INSERT_STRING(envelope, "dmRecipientRefNumber",
2441 outgoing_message->envelope->dmRecipientRefNumber);
2443 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
2444 50, "dmSenderRefNumber");
2445 INSERT_STRING(envelope, "dmSenderRefNumber",
2446 outgoing_message->envelope->dmSenderRefNumber);
2448 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
2449 50, "dmRecipientIdent");
2450 INSERT_STRING(envelope, "dmRecipientIdent",
2451 outgoing_message->envelope->dmRecipientIdent);
2453 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
2454 50, "dmSenderIdent");
2455 INSERT_STRING(envelope, "dmSenderIdent",
2456 outgoing_message->envelope->dmSenderIdent);
2458 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
2459 outgoing_message->envelope->dmLegalTitleLaw, string);
2460 INSERT_LONGINT(envelope, "dmLegalTitleYear",
2461 outgoing_message->envelope->dmLegalTitleYear, string);
2462 INSERT_STRING(envelope, "dmLegalTitleSect",
2463 outgoing_message->envelope->dmLegalTitleSect);
2464 INSERT_STRING(envelope, "dmLegalTitlePar",
2465 outgoing_message->envelope->dmLegalTitlePar);
2466 INSERT_STRING(envelope, "dmLegalTitlePoint",
2467 outgoing_message->envelope->dmLegalTitlePoint);
2469 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
2470 outgoing_message->envelope->dmPersonalDelivery);
2471 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
2472 outgoing_message->envelope->dmAllowSubstDelivery);
2474 #undef CHECK_FOR_STRING_LENGTH
2476 /* ???: Should we require value for dbEffectiveOVM sender?
2477 * ISDS has default as true */
2478 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
2481 /* Append dmFiles */
2482 if (!outgoing_message->documents) {
2483 isds_log_message(context,
2484 _("outgoing message is missing list of documents"));
2485 err = IE_INVAL;
2486 goto leave;
2488 dm_files = xmlNewChild(request, NULL, BAD_CAST "dmFiles", NULL);
2489 if (!dm_files) {
2490 isds_log_message(context, _("Could not add dmFiles child to "
2491 "CreateMessage element"));
2492 err = IE_ERROR;
2493 goto leave;
2496 /* Check for document hieararchy */
2497 err = check_documents_hierarchy(context, outgoing_message->documents);
2498 if (err) goto leave;
2500 /* Process each document */
2501 for (struct isds_list *item =
2502 (struct isds_list *) outgoing_message->documents;
2503 item; item = item->next) {
2504 if (!item->data) {
2505 isds_log_message(context,
2506 _("list of documents contains empty item"));
2507 err = IE_INVAL;
2508 goto leave;
2510 /* FIXME: Check for dmFileMetaType and for document references.
2511 * Only first document can be of MAIN type */
2512 err = insert_document(context, (struct isds_document*) item->data,
2513 dm_files);
2515 if (err) goto leave;
2518 /* Signal we can serilize message since now */
2519 message_is_complete = 1;
2523 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
2525 /* Sent request */
2526 err = isds(context, SERVICE_DM_OPERATIONS, request, &response);
2528 /* Dont' destroy request, we want to privode it to application later */
2530 if (err) {
2531 isds_log(ILF_ISDS, ILL_DEBUG,
2532 _("Processing ISDS response on CreateMessage "
2533 "request failed\n"));
2534 goto leave;
2537 /* Check for response status */
2538 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
2539 &code, &message, NULL);
2540 if (err) {
2541 isds_log(ILF_ISDS, ILL_DEBUG,
2542 _("ISDS response on CreateMessage request "
2543 "is missing status\n"));
2544 goto leave;
2547 /* Request processed, but nothing found */
2548 if (xmlStrcmp(code, BAD_CAST "0000")) {
2549 char *box_id_locale =
2550 utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
2551 char *code_locale = utf82locale((char*)code);
2552 char *message_locale = utf82locale((char*)message);
2553 isds_log(ILF_ISDS, ILL_DEBUG,
2554 _("Server did not accept message for %s on CreateMessage "
2555 "request (code=%s, message=%s)\n"),
2556 box_id_locale, code_locale, message_locale);
2557 isds_log_message(context, message_locale);
2558 free(box_id_locale);
2559 free(code_locale);
2560 free(message_locale);
2561 err = IE_ISDS;
2562 goto leave;
2566 /* Extract data */
2567 xpath_ctx = xmlXPathNewContext(response);
2568 if (!xpath_ctx) {
2569 err = IE_ERROR;
2570 goto leave;
2572 if (register_namespaces(xpath_ctx)) {
2573 err = IE_ERROR;
2574 goto leave;
2576 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
2577 xpath_ctx);
2578 if (!result) {
2579 err = IE_ERROR;
2580 goto leave;
2582 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2583 isds_log_message(context, _("Missing CreateMessageResponse element"));
2584 err = IE_ISDS;
2585 goto leave;
2587 if (result->nodesetval->nodeNr > 1) {
2588 isds_log_message(context, _("Multiple CreateMessageResponse element"));
2589 err = IE_ISDS;
2590 goto leave;
2592 xpath_ctx->node = result->nodesetval->nodeTab[0];
2593 xmlXPathFreeObject(result); result = NULL;
2595 if (outgoing_message->envelope->dmID) {
2596 free(outgoing_message->envelope->dmID);
2597 outgoing_message->envelope->dmID = NULL;
2599 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
2600 if (!outgoing_message->envelope->dmID) {
2601 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
2602 "but did not returen assigned message ID\n"));
2605 leave:
2606 /* TODO: Serialize message into structure member raw */
2607 /* XXX: Each web service transport message in different format.
2608 * Therefore it's not possible to save them directly.
2609 * To save them, one must figure out common format.
2610 * We can leave it on application, or we can implement the ESS format. */
2611 /*if (message_is_complete) {
2612 if (outgoing_message->envelope->dmID) {
2614 /* Add assigned message ID as first child*/
2615 /*xmlNodePtr dmid_text = xmlNewText(
2616 (xmlChar *) outgoing_message->envelope->dmID);
2617 if (!dmid_text) goto serialization_failed;
2619 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
2620 BAD_CAST "dmID");
2621 if (!dmid_element) {
2622 xmlFreeNode(dmid_text);
2623 goto serialization_failed;
2626 xmlNodePtr dmid_element_with_text =
2627 xmlAddChild(dmid_element, dmid_text);
2628 if (!dmid_element_with_text) {
2629 xmlFreeNode(dmid_element);
2630 xmlFreeNode(dmid_text);
2631 goto serialization_failed;
2634 node = xmlAddPrevSibling(envelope->childern,
2635 dmid_element_with_text);
2636 if (!node) {
2637 xmlFreeNodeList(dmid_element_with_text);
2638 goto serialization_failed;
2642 /* Serialize message with ID into raw */
2643 /*buffer = serialize_element(envelope)*/
2644 /* }
2646 serialization_failed:
2651 /* Clean up */
2652 free(string);
2653 xmlXPathFreeObject(result);
2654 xmlXPathFreeContext(xpath_ctx);
2656 free(code);
2657 free(message);
2658 xmlFreeDoc(response);
2659 xmlFreeNode(request);
2661 if (!err)
2662 isds_log(ILF_ISDS, ILL_DEBUG,
2663 _("CreateMessage request processed by server "
2664 "successfully.\n"));
2666 return err;
2670 /* Get list of messages. This is common core for getting sent or received
2671 * messaeges.
2672 * Any criterion argument can be NULL, if you don't care about it.
2673 * @context is session context. Must not be NULL.
2674 * @outgoing_direction is true if you want list of outgoing messages,
2675 * it's false if you want incoming messages.
2676 * @from_time is minimal time and date of message sending inclusive.
2677 * @to_time is maximal time and date of message sending inclusive
2678 * @organization_unit_number is number of sender/recipient respectively.
2679 * @status_filter is bit field of isds_message_status values. Use special
2680 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
2681 * all values, you can use bitwise arithmetic if you want.)
2682 * @offset is index of first message we are interested in. First message is 1.
2683 * Set to 0 (or 1) if you don't care.
2684 * @number is maximal length of list you want to get as input value, outputs
2685 * number of messages matching these criteria. Can be NULL if you don't care
2686 * (applies to output value either).
2687 * @messages is automatically reallocated list of isds_message's. Be ware that
2688 * it returns only brief overview (envelope and some other fields) about each
2689 * message, not the complete message. FIXME: Specify exact fields.
2690 * The list is sorted by delivery time in ascending order.
2691 * Use NULL if
2692 * you don't care about don't need the data (useful if you want to know only
2693 * the @number). If you provide &NULL, list will be allocated on heap, if you
2694 * provide pointer to non-NULL, list will be freed automacally at first. Also
2695 * in case of error the list will be NULLed.
2696 * @return IE_SUCCESS or appropriate error code. */
2697 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
2698 _Bool outgoing_direction,
2699 const struct timeval *from_time, const struct timeval *to_time,
2700 const long int *organization_unit_number,
2701 const unsigned int status_filter,
2702 const unsigned long int offset, unsigned long int *number,
2703 struct isds_list **messages) {
2705 isds_error err = IE_SUCCESS;
2706 xmlNsPtr isds_ns = NULL;
2707 xmlNodePtr request = NULL, node;
2708 xmlDocPtr response = NULL;
2709 xmlChar *code = NULL, *message = NULL;
2710 xmlXPathContextPtr xpath_ctx = NULL;
2711 xmlXPathObjectPtr result = NULL;
2712 xmlChar *string = NULL;
2713 long unsigned int count = 0;
2715 if (!context) return IE_INVALID_CONTEXT;
2717 /* Free former message list if any */
2718 if (messages) isds_list_free(messages);
2720 /* Check if connection is established
2721 * TODO: This check should be done donwstairs. */
2722 if (!context->curl) return IE_CONNECTION_CLOSED;
2724 /* Build GetListOf*Messages request */
2725 request = xmlNewNode(NULL,
2726 (outgoing_direction) ?
2727 BAD_CAST "GetListOfSentMessages" :
2728 BAD_CAST "GetListOfReceivedMessages"
2730 if (!request) {
2731 isds_log_message(context,
2732 (outgoing_direction) ?
2733 _("Could not build GetListOfSentMessages request") :
2734 _("Could not build GetListOfReceivedMessages request")
2736 return IE_ERROR;
2738 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
2739 if(!isds_ns) {
2740 isds_log_message(context, _("Could not create ISDS name space"));
2741 xmlFreeNode(request);
2742 return IE_ERROR;
2744 xmlSetNs(request, isds_ns);
2747 if (from_time) {
2748 err = timeval2timestring(from_time, &string);
2749 if (err) goto leave;
2751 INSERT_STRING(request, "dmFromTime", string);
2752 free(string); string = NULL;
2754 if (to_time) {
2755 err = timeval2timestring(to_time, &string);
2756 if (err) goto leave;
2758 INSERT_STRING(request, "dmToTime", string);
2759 free(string); string = NULL;
2761 if (outgoing_direction) {
2762 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
2763 organization_unit_number, string);
2764 } else {
2765 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
2766 organization_unit_number, string);
2769 if (status_filter > MESSAGESTATE_ANY) {
2770 isds_printf_message(context,
2771 _("Invalid message state filter value: %ld"), status_filter);
2772 err = IE_INVAL;
2773 goto leave;
2775 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
2777 if (offset > 0 ) {
2778 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
2779 } else {
2780 INSERT_STRING(request, "dmOffset", "1");
2783 /* number 0 means no limit */
2784 if (number && *number == 0) {
2785 INSERT_STRING(request, "dmLimit", NULL);
2786 } else {
2787 INSERT_ULONGINT(request, "dmLimit", number, string);
2791 isds_log(ILF_ISDS, ILL_DEBUG,
2792 (outgoing_direction) ?
2793 _("Sending GetListOfSentMessages request to ISDS\n") :
2794 _("Sending GetListOfReceivedMessages request to ISDS\n")
2797 /* Sent request */
2798 err = isds(context, SERVICE_DM_INFO, request, &response);
2799 xmlFreeNode(request); request = NULL;
2801 if (err) {
2802 isds_log(ILF_ISDS, ILL_DEBUG,
2803 (outgoing_direction) ?
2804 _("Processing ISDS response on GetListOfSentMessages "
2805 "request failed\n") :
2806 _("Processing ISDS response on GetListOfReceivedMessages "
2807 "request failed\n")
2809 goto leave;
2812 /* Check for response status */
2813 err = isds_response_status(context, SERVICE_DM_INFO, response,
2814 &code, &message, NULL);
2815 if (err) {
2816 isds_log(ILF_ISDS, ILL_DEBUG,
2817 (outgoing_direction) ?
2818 _("ISDS response on GetListOfSentMessages request "
2819 "is missing status\n") :
2820 _("ISDS response on GetListOfReceivedMessages request "
2821 "is missing status\n")
2823 goto leave;
2826 /* Request processed, but nothing found */
2827 if (xmlStrcmp(code, BAD_CAST "0000")) {
2828 char *code_locale = utf82locale((char*)code);
2829 char *message_locale = utf82locale((char*)message);
2830 isds_log(ILF_ISDS, ILL_DEBUG,
2831 (outgoing_direction) ?
2832 _("Server refused GetListOfSentMessages request "
2833 "(code=%s, message=%s)\n") :
2834 _("Server refused GetListOfReceivedMessages request "
2835 "(code=%s, message=%s)\n"),
2836 code_locale, message_locale);
2837 isds_log_message(context, message_locale);
2838 free(code_locale);
2839 free(message_locale);
2840 err = IE_ISDS;
2841 goto leave;
2845 /* Extract data */
2846 xpath_ctx = xmlXPathNewContext(response);
2847 if (!xpath_ctx) {
2848 err = IE_ERROR;
2849 goto leave;
2851 if (register_namespaces(xpath_ctx)) {
2852 err = IE_ERROR;
2853 goto leave;
2855 result = xmlXPathEvalExpression(
2856 (outgoing_direction) ?
2857 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
2858 "isds:dmRecords/isds:dmRecord" :
2859 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
2860 "isds:dmRecords/isds:dmRecord",
2861 xpath_ctx);
2862 if (!result) {
2863 err = IE_ERROR;
2864 goto leave;
2867 /* Fill output arguments in */
2868 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2869 struct isds_envelope *envelope;
2870 struct isds_list *item = NULL, *last_item = NULL;
2872 for (count = 0; count < result->nodesetval->nodeNr; count++) {
2873 /* Create new message */
2874 item = calloc(1, sizeof(*item));
2875 if (!item) {
2876 err = IE_NOMEM;
2877 goto leave;
2879 item->destructor = (void(*)(void**)) &isds_message_free;
2880 item->data = calloc(1, sizeof(struct isds_message));
2881 if (!item->data) {
2882 isds_list_free(&item);
2883 err = IE_NOMEM;
2884 goto leave;
2887 /* Extract envelope data */
2888 xpath_ctx->node = result->nodesetval->nodeTab[count];
2889 envelope = NULL;
2890 err = extract_DmRecord(context, &envelope, xpath_ctx);
2891 if (err) {
2892 isds_list_free(&item);
2893 goto leave;
2896 /* Attach extracted envelope */
2897 ((struct isds_message *) item->data)->envelope = envelope;
2899 /* Append new message into the list */
2900 if (!*messages) {
2901 *messages = last_item = item;
2902 } else {
2903 last_item->next = item;
2904 last_item = item;
2908 if (number) *number = count;
2910 leave:
2911 if (err) {
2912 isds_list_free(messages);
2915 free(string);
2916 xmlXPathFreeObject(result);
2917 xmlXPathFreeContext(xpath_ctx);
2919 free(code);
2920 free(message);
2921 xmlFreeDoc(response);
2922 xmlFreeNode(request);
2924 if (!err)
2925 isds_log(ILF_ISDS, ILL_DEBUG,
2926 (outgoing_direction) ?
2927 _("GetListOfSentMessages request processed by server "
2928 "successfully.\n") :
2929 _("GetListOfReceivedMessages request processed by server "
2930 "successfully.\n")
2932 return err;
2936 /* Get list of outgoing (already sent) messages.
2937 * Any criterion argument can be NULL, if you don't care about it.
2938 * @context is session context. Must not be NULL.
2939 * @from_time is minimal time and date of message sending inclusive.
2940 * @to_time is maximal time and date of message sending inclusive
2941 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
2942 * @status_filter is bit field of isds_message_status values. Use special
2943 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
2944 * all values, you can use bitwise arithmetic if you want.)
2945 * @offset is index of first message we are interested in. First message is 1.
2946 * Set to 0 (or 1) if you don't care.
2947 * @number is maximal length of list you want to get as input value, outputs
2948 * number of messages matching these criteria. Can be NULL if you don't care
2949 * (applies to output value either).
2950 * @messages is automatically reallocated list of isds_message's. Be ware that
2951 * it returns only brief overview (envelope and some other fields) about each
2952 * message, not the complete message. FIXME: Specify exact fields.
2953 * The list is sorted by delivery time in ascending order.
2954 * Use NULL if you don't care about the metadata (useful if you want to know
2955 * only the @number). If you provide &NULL, list will be allocated on heap,
2956 * if you provide pointer to non-NULL, list will be freed automacally at first.
2957 * Also in case of error the list will be NULLed.
2958 * @return IE_SUCCESS or appropriate error code. */
2959 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
2960 const struct timeval *from_time, const struct timeval *to_time,
2961 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
2962 const unsigned long int offset, unsigned long int *number,
2963 struct isds_list **messages) {
2965 return isds_get_list_of_messages(
2966 context, 1,
2967 from_time, to_time, dmSenderOrgUnitNum, status_filter,
2968 offset, number,
2969 messages);
2973 /* Get list of incoming (addressed to you) messages.
2974 * Any criterion argument can be NULL, if you don't care about it.
2975 * @context is session context. Must not be NULL.
2976 * @from_time is minimal time and date of message sending inclusive.
2977 * @to_time is maximal time and date of message sending inclusive
2978 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
2979 * @status_filter is bit field of isds_message_status values. Use special
2980 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
2981 * all values, you can use bitwise arithmetic if you want.)
2982 * @offset is index of first message we are interested in. First message is 1.
2983 * Set to 0 (or 1) if you don't care.
2984 * @number is maximal length of list you want to get as input value, outputs
2985 * number of messages matching these criteria. Can be NULL if you don't care
2986 * (applies to output value either).
2987 * @messages is automatically reallocated list of isds_message's. Be ware that
2988 * it returns only brief overview (envelope and some other fields) about each
2989 * message, not the complete message. FIXME: Specify exact fields.
2990 * Use NULL if you don't care about the metadata (useful if you want to know
2991 * only the @number). If you provide &NULL, list will be allocated on heap,
2992 * if you provide pointer to non-NULL, list will be freed automacally at first.
2993 * Also in case of error the list will be NULLed.
2994 * @return IE_SUCCESS or appropriate error code. */
2995 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
2996 const struct timeval *from_time, const struct timeval *to_time,
2997 const long int *dmRecipientOrgUnitNum,
2998 const unsigned int status_filter,
2999 const unsigned long int offset, unsigned long int *number,
3000 struct isds_list **messages) {
3002 return isds_get_list_of_messages(
3003 context, 0,
3004 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
3005 offset, number,
3006 messages);
3010 /* Dwwnload incomping message identified by ID.
3011 * @context is session context
3012 * @message_id is message identifier (you can get them from
3013 * isds_get_list_of_received_messages())
3014 * @message is automatically reallocated message retrieved from ISDS */
3015 isds_error isds_get_received_message(struct isds_ctx *context,
3016 const char *message_id, struct isds_message **message) {
3017 /* ???: XSD allows list of @message_id's and list of @message's, but
3018 * documentation talks only about `a message' */
3020 isds_error err = IE_SUCCESS;
3021 xmlNsPtr isds_ns = NULL;
3022 xmlNodePtr request = NULL, node;
3023 xmlDocPtr response = NULL;
3024 xmlChar *code = NULL, *status_message = NULL;
3025 xmlXPathContextPtr xpath_ctx = NULL;
3026 xmlXPathObjectPtr result = NULL;
3028 if (!context) return IE_INVALID_CONTEXT;
3030 /* Free former message if any */
3031 if (message) isds_message_free(message);
3033 if (!message_id) return IE_INVAL;
3035 /* Check if connection is established
3036 * TODO: This check should be done donwstairs. */
3037 if (!context->curl) return IE_CONNECTION_CLOSED;
3039 /* Build MessageDownload request */
3040 request = xmlNewNode(NULL, BAD_CAST "MessageDownload");
3041 if (!request) {
3042 isds_log_message(context,
3043 _("Could not build MessageDownload request"));
3044 return IE_ERROR;
3046 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
3047 if(!isds_ns) {
3048 isds_log_message(context, _("Could not create ISDS name space"));
3049 xmlFreeNode(request);
3050 return IE_ERROR;
3052 xmlSetNs(request, isds_ns);
3055 /* Add requested ID */
3056 err = validate_message_id_length(context, (xmlChar *) message_id);
3057 if (err) goto leave;
3058 INSERT_STRING(request, "dmID", message_id);
3061 isds_log(ILF_ISDS, ILL_DEBUG,
3062 _("Sending MessageDownload request to ISDS\n"));
3064 /* Sent request */
3065 err = isds(context, SERVICE_DM_OPERATIONS, request, &response);
3066 xmlFreeNode(request); request = NULL;
3068 if (err) {
3069 isds_log(ILF_ISDS, ILL_DEBUG,
3070 _("Processing ISDS response on MessageDownload "
3071 "request failed\n"));
3072 goto leave;
3075 /* Check for response status */
3076 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
3077 &code, &status_message, NULL);
3078 if (err) {
3079 isds_log(ILF_ISDS, ILL_DEBUG,
3080 _("ISDS response on MessageDownload request "
3081 "is missing status\n"));
3082 goto leave;
3085 /* Request processed, but nothing found */
3086 if (xmlStrcmp(code, BAD_CAST "0000")) {
3087 char *code_locale = utf82locale((char*)code);
3088 char *status_message_locale = utf82locale((char*)status_message);
3089 isds_log(ILF_ISDS, ILL_DEBUG,
3090 _("Server refused MessageDownload request "
3091 "(code=%s, message=%s)\n"),
3092 code_locale, status_message_locale);
3093 isds_log_message(context, status_message_locale);
3094 free(code_locale);
3095 free(status_message_locale);
3096 err = IE_ISDS;
3097 goto leave;
3101 /* Extract data */
3102 xpath_ctx = xmlXPathNewContext(response);
3103 if (!xpath_ctx) {
3104 err = IE_ERROR;
3105 goto leave;
3107 if (register_namespaces(xpath_ctx)) {
3108 err = IE_ERROR;
3109 goto leave;
3111 result = xmlXPathEvalExpression(
3112 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
3113 xpath_ctx);
3114 if (!result) {
3115 err = IE_ERROR;
3116 goto leave;
3118 /* Empty response */
3119 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3120 char *message_id_locale = utf82locale((char*) message_id);
3121 isds_printf_message(context,
3122 _("Server did not return any message for ID `%s' "
3123 "on MessageDownload request"), message_id);
3124 free(message_id_locale);
3125 err = IE_ISDS;
3126 goto leave;
3128 /* More messages */
3129 if (result->nodesetval->nodeNr > 1) {
3130 char *message_id_locale = utf82locale((char*) message_id);
3131 isds_printf_message(context,
3132 _("Server did return more messages for ID `%s' "
3133 "on MessageDownload request"), message_id);
3134 free(message_id_locale);
3135 err = IE_ISDS;
3136 goto leave;
3138 /* One message */
3139 xpath_ctx->node = result->nodesetval->nodeTab[0];
3141 /* Extract the message */
3142 err = extract_TReturnedMessage(context, message, xpath_ctx);
3144 leave:
3145 if (err) {
3146 isds_message_free(message);
3149 xmlXPathFreeObject(result);
3150 xmlXPathFreeContext(xpath_ctx);
3152 free(code);
3153 free(status_message);
3154 xmlFreeDoc(response);
3155 xmlFreeNode(request);
3157 if (!err)
3158 isds_log(ILF_ISDS, ILL_DEBUG,
3159 _("MessageDownloadrequest processed by server "
3160 "successfully.\n")
3162 return err;
3166 #undef INSERT_STRING_ATTRIBUTE
3167 #undef INSERT_ULONGINTNOPTR
3168 #undef INSERT_ULONGINT
3169 #undef INSERT_LONGINT
3170 #undef INSERT_BOOLEAN
3171 #undef INSERT_STRING
3172 #undef EXTRACT_ULONGINT
3173 #undef EXTRACT_LONGINT
3174 #undef EXTRACT_BOOLEAN
3175 #undef EXTRACT_STRING
3178 /* Search for document by document ID in list of documents. IDs are compared
3179 * as UTF-8 string.
3180 * @documents is list of isds_documents
3181 * @id is document identifier
3182 * @return first matching document or NULL. */
3183 const struct isds_document *isds_find_document_by_id(
3184 const struct isds_list *documents, const char *id) {
3185 const struct isds_list *item;
3186 const struct isds_document *document;
3188 for (item = documents; item; item = item->next) {
3189 document = (struct isds_document *) item->data;
3190 if (!document) continue;
3192 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
3193 return document;
3196 return NULL;
3200 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
3201 struct isds_message **message);
3202 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
3203 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
3204 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
3205 struct isds_address **address);
3207 int isds_message_free(struct isds_message **message);
3208 int isds_address_free(struct isds_address **address);
3212 /* Makes known all relevant namespaces to give @xpat_ctx */
3213 _hidden isds_error register_namespaces(xmlXPathContextPtr xpath_ctx) {
3214 if (!xpath_ctx) return IE_ERROR;
3216 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
3217 return IE_ERROR;
3218 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
3219 return IE_ERROR;
3220 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
3221 return IE_ERROR;
3222 return IE_SUCCESS;