1 #define _XOPEN_SOURCE 500 /* strdup from string.h, strptime from time.h */
11 #include "validator.h"
13 #include <gpg-error.h> /* Because of ksba or gpgme */
17 /* Free isds_list with all member data.
18 * @list list to free, on return will be NULL */
19 void isds_list_free(struct isds_list
**list
) {
20 struct isds_list
*item
, *next_item
;
22 if (!list
|| !*list
) return;
24 for(item
= *list
; item
; item
= next_item
) {
25 (item
->destructor
)(&(item
->data
));
26 next_item
= item
->next
;
34 /* Deallocate structure isds_hash and NULL it.
35 * @hash hash to to free */
36 void isds_hash_free(struct isds_hash
**hash
) {
37 if(!hash
|| !*hash
) return;
43 /* Deallocate structure isds_PersonName recursively and NULL it */
44 static void isds_PersonName_free(struct isds_PersonName
**person_name
) {
45 if (!person_name
|| !*person_name
) return;
47 free((*person_name
)->pnFirstName
);
48 free((*person_name
)->pnMiddleName
);
49 free((*person_name
)->pnLastName
);
50 free((*person_name
)->pnLastNameAtBirth
);
57 /* Deallocate structure isds_BirthInfo recursively and NULL it */
58 static void isds_BirthInfo_free(struct isds_BirthInfo
**birth_info
) {
59 if (!birth_info
|| !*birth_info
) return;
61 free((*birth_info
)->biDate
);
62 free((*birth_info
)->biCity
);
63 free((*birth_info
)->biCounty
);
64 free((*birth_info
)->biState
);
71 /* Deallocate structure isds_Address recursively and NULL it */
72 static void isds_Address_free(struct isds_Address
**address
) {
73 if (!address
|| !*address
) return;
75 free((*address
)->adCity
);
76 free((*address
)->adStreet
);
77 free((*address
)->adNumberInStreet
);
78 free((*address
)->adNumberInMunicipality
);
79 free((*address
)->adZipCode
);
80 free((*address
)->adState
);
87 /* Deallocate structure isds_DbOwnerInfo recursively and NULL it */
88 void isds_DbOwnerInfo_free(struct isds_DbOwnerInfo
**db_owner_info
) {
89 if (!db_owner_info
|| !*db_owner_info
) return;
91 free((*db_owner_info
)->dbID
);
92 free((*db_owner_info
)->dbType
);
93 free((*db_owner_info
)->ic
);
94 isds_PersonName_free(&((*db_owner_info
)->personName
));
95 free((*db_owner_info
)->firmName
);
96 isds_BirthInfo_free(&((*db_owner_info
)->birthInfo
));
97 isds_Address_free(&((*db_owner_info
)->address
));
98 free((*db_owner_info
)->nationality
);
99 free((*db_owner_info
)->email
);
100 free((*db_owner_info
)->telNumber
);
101 free((*db_owner_info
)->identifier
);
102 free((*db_owner_info
)->registryCode
);
103 free((*db_owner_info
)->dbState
);
104 free((*db_owner_info
)->dbEffectiveOVM
);
106 free(*db_owner_info
);
107 *db_owner_info
= NULL
;
111 /* Deallocate struct isds_event recursively and NULL it */
112 void isds_event_free(struct isds_event
**event
) {
113 if (!event
|| !*event
) return;
115 free((*event
)->time
);
116 free((*event
)->type
);
117 free((*event
)->description
);
122 /* Deallocate struct isds_envelope recursively and NULL it */
123 void isds_envelope_free(struct isds_envelope
**envelope
) {
124 if (!envelope
|| !*envelope
) return;
126 free((*envelope
)->dmID
);
127 free((*envelope
)->dbIDSender
);
128 free((*envelope
)->dmSender
);
129 free((*envelope
)->dmSenderAddress
);
130 free((*envelope
)->dmSenderType
);
131 free((*envelope
)->dmRecipient
);
132 free((*envelope
)->dmRecipientAddress
);
133 free((*envelope
)->dmAmbiguousRecipient
);
135 free((*envelope
)->dmOrdinal
);
136 free((*envelope
)->dmMessageStatus
);
137 free((*envelope
)->dmDeliveryTime
);
138 free((*envelope
)->dmAcceptanceTime
);
139 isds_hash_free(&(*envelope
)->hash
);
140 free((*envelope
)->timestamp
);
141 isds_list_free(&(*envelope
)->events
);
143 free((*envelope
)->dmSenderOrgUnit
);
144 free((*envelope
)->dmSenderOrgUnitNum
);
145 free((*envelope
)->dbIDRecipient
);
146 free((*envelope
)->dmRecipientOrgUnit
);
147 free((*envelope
)->dmRecipientOrgUnitNum
);
148 free((*envelope
)->dmToHands
);
149 free((*envelope
)->dmAnnotation
);
150 free((*envelope
)->dmRecipientRefNumber
);
151 free((*envelope
)->dmSenderRefNumber
);
152 free((*envelope
)->dmRecipientIdent
);
153 free((*envelope
)->dmSenderIdent
);
155 free((*envelope
)->dmLegalTitleLaw
);
156 free((*envelope
)->dmLegalTitleYear
);
157 free((*envelope
)->dmLegalTitleSect
);
158 free((*envelope
)->dmLegalTitlePar
);
159 free((*envelope
)->dmLegalTitlePoint
);
161 free((*envelope
)->dmPersonalDelivery
);
162 free((*envelope
)->dmAllowSubstDelivery
);
163 free((*envelope
)->dmOVM
);
170 /* Deallocate struct isds_message recursively and NULL it */
171 void isds_message_free(struct isds_message
**message
) {
172 if (!message
|| !*message
) return;
174 free((*message
)->raw
);
175 isds_envelope_free(&((*message
)->envelope
));
176 isds_list_free(&((*message
)->documents
));
183 /* Deallocate struct isds_document recursively and NULL it */
184 void isds_document_free(struct isds_document
**document
) {
185 if (!document
|| !*document
) return;
187 free((*document
)->data
);
188 free((*document
)->dmMimeType
);
189 free((*document
)->dmFileGuid
);
190 free((*document
)->dmUpFileGuid
);
191 free((*document
)->dmFileDescr
);
192 free((*document
)->dmFormat
);
199 /* Initialize ISDS library.
200 * Global function, must be called before other functions.
201 * If it failes you can not use ISDS library and must call isds_cleanup() to
202 * free partially inititialized global variables. */
203 isds_error
isds_init(void) {
204 /* NULL global variables */
205 log_facilities
= ILF_ALL
;
206 log_level
= ILL_WARNING
;
208 /* Initialize CURL */
209 if (curl_global_init(CURL_GLOBAL_ALL
)) {
210 isds_log(ILF_ISDS
, ILL_CRIT
, _("CURL library initialization failed\n"));
214 /* Inicialize gpg-error because of gpgme and ksba */
215 if (gpg_err_init()) {
216 isds_log(ILF_ISDS
, ILL_CRIT
,
217 _("gpg-error library initialization failed\n"));
221 /* Initialize GPGME */
223 isds_log(ILF_ISDS
, ILL_CRIT
,
224 _("GPGME library initialization failed\n"));
228 /* Initialize gcrypt */
230 isds_log(ILF_ISDS
, ILL_CRIT
,
231 _("gcrypt library initialization failed\n"));
235 /* This can _exit() current program. Find not so assertive check. */
240 isds_log(ILF_ISDS
, ILL_CRIT
,
241 _("expat library initialization failed\n"));
245 /* Allocate global variables */
252 /* Deinicialize ISDS library.
253 * Global function, must be called as last library function. */
254 isds_error
isds_cleanup(void) {
259 curl_global_cleanup();
265 /* Return text description of ISDS error */
266 char *isds_strerror(const isds_error error
) {
269 return(_("Success")); break;
271 return(_("Unspecified error")); break;
273 return(_("Not supported")); break;
275 return(_("Invalid value")); break;
276 case IE_INVALID_CONTEXT
:
277 return(_("Invalid context")); break;
278 case IE_NOT_LOGGED_IN
:
279 return(_("Not logged in")); break;
280 case IE_CONNECTION_CLOSED
:
281 return(_("Connection closed")); break;
283 return(_("Timed out")); break;
285 return(_("Not exist")); break;
287 return(_("Out of memory")); break;
289 return(_("Network problem")); break;
291 return(_("HTTP problem")); break;
293 return(_("SOAP problem")); break;
295 return(_("XML problem")); break;
297 return(_("ISDS server problem")); break;
299 return(_("Invalid enum value")); break;
301 return(_("Invalid date value")); break;
303 return(_("Too big")); break;
305 return(_("Value not unique")); break;
307 return(_("Values not uqual")); break;
309 return(_("Unknown error"));
314 /* Create ISDS context.
315 * Each context can be used for different sessions to (possibly) differnet
316 * ISDS server with different credentials. */
317 struct isds_ctx
*isds_ctx_create(void) {
318 struct isds_ctx
*context
;
319 context
= malloc(sizeof(*context
));
320 if (context
) memset(context
, 0, sizeof(*context
));
325 /* Destroy ISDS context and free memmory.
326 * @context will be NULLed on success. */
327 isds_error
isds_ctx_free(struct isds_ctx
**context
) {
328 if (!context
|| !*context
) {
329 return IE_INVALID_CONTEXT
;
332 /* Discard credentials */
333 isds_logout(*context
);
335 /* Free other structures */
336 free((*context
)->tls_verify_server
);
337 free((*context
)->tls_ca_file
);
338 free((*context
)->tls_ca_dir
);
339 free((*context
)->long_message
);
347 /* Return long message text produced by library fucntion, e.g. detailed error
348 * mesage. Returned pointer is only valid until new library function is
349 * called for the same context. Could be NULL, especially if NULL context is
350 * supplied. Return string is locale encoded. */
351 char *isds_long_message(const struct isds_ctx
*context
) {
352 if (!context
) return NULL
;
353 return context
->long_message
;
357 /* Stores message into context' long_message buffer.
358 * Application can pick the message up using isds_long_message().
359 * NULL @message truncates the buffer but does not deallocate it.
360 * @message is coded in locale encoding */
361 _hidden isds_error
isds_log_message(struct isds_ctx
*context
,
362 const char *message
) {
366 if (!context
) return IE_INVALID_CONTEXT
;
368 /* FIXME: Check for integer overflow */
369 length
= 1 + ((message
) ? strlen(message
) : 0);
370 buffer
= realloc(context
->long_message
, length
);
371 if (!buffer
) return IE_NOMEM
;
374 strcpy(buffer
, message
);
378 context
->long_message
= buffer
;
383 /* Appends message into context' long_message buffer.
384 * Application can pick the message up using isds_long_message().
385 * NULL message has void effect. */
386 _hidden isds_error
isds_append_message(struct isds_ctx
*context
,
387 const char *message
) {
389 size_t old_length
, length
;
391 if (!context
) return IE_INVALID_CONTEXT
;
392 if (!message
) return IE_SUCCESS
;
393 if (!context
->long_message
)
394 return isds_log_message(context
, message
);
396 old_length
= strlen(context
->long_message
);
397 /* FIXME: Check for integer overflow */
398 length
= 1 + old_length
+ strlen(message
);
399 buffer
= realloc(context
->long_message
, length
);
400 if (!buffer
) return IE_NOMEM
;
402 strcpy(buffer
+ old_length
, message
);
404 context
->long_message
= buffer
;
409 /* Stores formated message into context' long_message buffer.
410 * Application can pick the message up using isds_long_message(). */
411 _hidden isds_error
isds_printf_message(struct isds_ctx
*context
,
412 const char *format
, ...) {
416 if (!context
) return IE_INVALID_CONTEXT
;
417 va_start(ap
, format
);
418 length
= isds_vasprintf(&(context
->long_message
), format
, ap
);
421 return (length
< 0) ? IE_ERROR
: IE_SUCCESS
;
426 * @facilities is bitmask of isds_log_facility values,
427 * @level is verbosity level. */
428 void isds_set_logging(const unsigned int facilities
,
429 const isds_log_level level
) {
430 log_facilities
= facilities
;
435 /* Log @message in class @facility with log @level into global log. @message
436 * is printf(3) formating string, variadic arguments may be neccessary.
437 * For debugging purposes. */
438 _hidden isds_error
isds_log(const isds_log_facility facility
,
439 const isds_log_level level
, const char *message
, ...) {
442 if (level
> log_level
) return IE_SUCCESS
;
443 if (!(log_facilities
& facility
)) return IE_SUCCESS
;
444 if (!message
) return IE_INVAL
;
446 /* TODO: Allow to register output function privided by application
447 * (e.g. fprintf to stderr or copy to text area GUI widget). */
449 va_start(ap
, message
);
450 vfprintf(stderr
, message
, ap
);
452 /* Line buffered printf is default.
459 /* Connect to given url.
460 * It just makes TCP connection to ISDS server found in @url hostname part. */
461 /*int isds_connect(struct isds_ctx *context, const char *url);*/
463 /* Set timeout in miliseconds for each network job like connecting to server
464 * or sending message. Use 0 to disable timeout limits. */
465 isds_error
isds_set_timeout(struct isds_ctx
*context
,
466 const unsigned int timeout
) {
467 if (!context
) return IE_INVALID_CONTEXT
;
469 context
->timeout
= timeout
;
474 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_NOSIGNAL
, 1);
476 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_TIMEOUT_MS
,
478 if (curl_err
) return IE_ERROR
;
485 /* Change SSL/TLS settings.
486 * @context is context which setting will be applied to
487 * @option is name of option. It determines the type of last argument. See
488 * isds_tls_option definition for more info.
489 * @... is value of new setting. Type is determined by @option
491 isds_error
isds_set_tls(struct isds_ctx
*context
, const isds_tls_option option
,
493 isds_error err
= IE_SUCCESS
;
495 char *pointer
, *string
;
497 if (!context
) return IE_INVALID_CONTEXT
;
499 va_start(ap
, option
);
501 #define REPLACE_VA_STRING(destination) \
502 string = va_arg(ap, char *); \
504 pointer = realloc((destination), 1 + strlen(string)); \
505 if (!pointer) { err = IE_NOMEM; goto leave; } \
506 strcpy(pointer, string); \
507 (destination) = pointer; \
510 (destination) = NULL; \
514 case ITLS_VERIFY_SERVER
:
515 if (!context
->tls_verify_server
) {
516 context
->tls_verify_server
=
517 malloc(sizeof(*context
->tls_verify_server
));
518 if (!context
->tls_verify_server
) {
519 err
= IE_NOMEM
; goto leave
;
522 *context
->tls_verify_server
= (_Bool
) (0 != va_arg(ap
, int));
526 REPLACE_VA_STRING(context
->tls_ca_file
);
528 case ITLS_CA_DIRECTORY
:
529 REPLACE_VA_STRING(context
->tls_ca_dir
);
533 err
= IE_ENUM
; goto leave
;
536 #undef REPLACE_VA_STRING
544 /* Discard credentials.
545 * Only that. It does not cause log out, connection close or similar. */
546 static isds_error
discard_credentials(struct isds_ctx
*context
) {
547 if(!context
) return IE_INVALID_CONTEXT
;
549 if (context
->username
) {
550 memset(context
->username
, 0, strlen(context
->username
));
551 free(context
->username
);
552 context
->username
= NULL
;
554 if (context
->password
) {
555 memset(context
->password
, 0, strlen(context
->password
));
556 free(context
->password
);
557 context
->password
= NULL
;
564 /* Connect and log in into ISDS server.
565 * @url is address of ISDS web service
566 * @username is user name of ISDS user
567 * @password is user's secret password
568 * @certificate is NULL terminated string with PEM formated client's
569 * certificate. Use NULL if only password autentication should be performed.
570 * @key is private key for client's certificate as (base64 encoded?) NULL
571 * terminated string. Use NULL if only password autentication is desired.
573 isds_error
isds_login(struct isds_ctx
*context
, const char *url
,
574 const char *username
, const char *password
,
575 const char *certificate
, const char* key
) {
576 isds_error err
= IE_NOT_LOGGED_IN
;
578 xmlNsPtr isds_ns
= NULL
;
579 xmlNodePtr request
= NULL
;
580 xmlNodePtr response
= NULL
;
582 if (!context
) return IE_INVALID_CONTEXT
;
583 if (!url
|| !username
|| !password
) return IE_INVAL
;
584 if (certificate
|| key
) return IE_NOTSUP
;
586 /* Store configuration */
588 context
->url
= strdup(url
);
592 /* Close connection if already logged in */
594 close_connection(context
);
597 /* Prepare CURL handle */
598 context
->curl
= curl_easy_init();
599 if (!(context
->curl
))
602 /* Build login request */
603 request
= xmlNewNode(NULL
, BAD_CAST
"DummyOperation");
605 isds_log_message(context
, _("Could build ISDS login request"));
608 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
610 isds_log_message(context
, _("Could not create ISDS name space"));
611 xmlFreeNode(request
);
614 xmlSetNs(request
, isds_ns
);
616 /* Store credentials */
617 /* FIXME: mlock password
618 * (I have a library) */
619 discard_credentials(context
);
620 context
->username
= strdup(username
);
621 context
->password
= strdup(password
);
622 if (!(context
->username
&& context
->password
)) {
623 discard_credentials(context
);
624 xmlFreeNode(request
);
628 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Logging user %s into server %s\n"),
631 /* Send login request */
632 soap_err
= soap(context
, "dz", request
, &response
);
634 /* Remove credentials */
635 discard_credentials(context
);
637 /* Destroy login request */
638 xmlFreeNode(request
);
641 xmlFreeNodeList(response
);
642 close_connection(context
);
646 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
647 * authentication succeeded if soap_err == IE_SUCCESS */
650 xmlFreeNodeList(response
);
653 isds_log(ILF_ISDS
, ILL_DEBUG
,
654 _("User %s has been logged into server %s successfully\n"),
660 /* Log out from ISDS server discards credentials and connection configuration. */
661 isds_error
isds_logout(struct isds_ctx
*context
) {
662 if (!context
) return IE_INVALID_CONTEXT
;
664 /* Close connection */
666 close_connection(context
);
668 /* Discard credentials for sure. They should not survive isds_login(),
669 * even successful .*/
670 discard_credentials(context
);
674 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Logged out from ISDS server\n"));
676 discard_credentials(context
);
682 /* Verify connection to ISDS is alive and server is responding.
683 * Sent dumy request to ISDS and expect dummy response. */
684 isds_error
isds_ping(struct isds_ctx
*context
) {
686 xmlNsPtr isds_ns
= NULL
;
687 xmlNodePtr request
= NULL
;
688 xmlNodePtr response
= NULL
;
690 if (!context
) return IE_INVALID_CONTEXT
;
692 /* Check if connection is established */
693 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
696 /* Build dummy request */
697 request
= xmlNewNode(NULL
, BAD_CAST
"DummyOperation");
699 isds_log_message(context
, _("Could build ISDS dummy request"));
702 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
704 isds_log_message(context
, _("Could not create ISDS name space"));
705 xmlFreeNode(request
);
708 xmlSetNs(request
, isds_ns
);
710 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Pinging ISDS server\n"));
712 /* Sent dummy request */
713 soap_err
= soap(context
, "dz", request
, &response
);
715 /* Destroy login request */
716 xmlFreeNode(request
);
719 isds_log(ILF_ISDS
, ILL_DEBUG
,
720 _("ISDS server could not be contacted\n"));
721 xmlFreeNodeList(response
);
722 close_connection(context
);
726 /* XXX: Untill we don't propagate HTTP code 500 or 4xx, we can be sure
727 * authentication succeeded if soap_err == IE_SUCCESS */
728 /* TODO: ISDS documentation does not specify response body.
729 * However real server sends back DummyOperationResponse */
732 xmlFreeNodeList(response
);
734 isds_log(ILF_ISDS
, ILL_DEBUG
, _("ISDS server alive\n"));
740 /* Send bogus request to ISDS.
741 * Just for test purposes */
742 isds_error
isds_bogus_request(struct isds_ctx
*context
) {
744 xmlNsPtr isds_ns
= NULL
;
745 xmlNodePtr request
= NULL
;
746 xmlDocPtr response
= NULL
;
747 xmlChar
*code
= NULL
, *message
= NULL
;
749 if (!context
) return IE_INVALID_CONTEXT
;
751 /* Check if connection is established */
752 if (!context
->curl
) {
753 /* Testing printf message */
754 isds_printf_message(context
, "%s", _("I said connection closed"));
755 return IE_CONNECTION_CLOSED
;
759 /* Build dummy request */
760 request
= xmlNewNode(NULL
, BAD_CAST
"X-BogusOperation");
762 isds_log_message(context
, _("Could build ISDS bogus request"));
765 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
767 isds_log_message(context
, _("Could not create ISDS name space"));
768 xmlFreeNode(request
);
771 xmlSetNs(request
, isds_ns
);
773 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending bogus request to ISDS\n"));
775 /* Sent bogus request */
776 err
= isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
);
778 /* Destroy request */
779 xmlFreeNode(request
);
782 isds_log(ILF_ISDS
, ILL_DEBUG
,
783 _("Processing ISDS response on bogus request failed\n"));
784 xmlFreeDoc(response
);
788 /* Check for response status */
789 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
790 &code
, &message
, NULL
);
792 isds_log(ILF_ISDS
, ILL_DEBUG
,
793 _("ISDS response on bogus request is missing status\n"));
796 xmlFreeDoc(response
);
799 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
800 char *code_locale
= utf82locale((char*)code
);
801 char *message_locale
= utf82locale((char*)message
);
802 isds_log(ILF_ISDS
, ILL_DEBUG
,
803 _("Server refused bogus request (code=%s, message=%s)\n"),
804 code_locale
, message_locale
);
805 /* XXX: Literal error messages from ISDS are Czech mesages
806 * (English sometimes) in UTF-8. It's hard to catch them for
807 * translation. Successfully gettextized would return in locale
808 * encoding, unsuccessfully translated would pass in UTF-8. */
809 isds_log_message(context
, message_locale
);
811 free(message_locale
);
814 xmlFreeDoc(response
);
821 xmlFreeDoc(response
);
823 isds_log(ILF_ISDS
, ILL_DEBUG
,
824 _("Bogus message accepted by server. This should not happen.\n"));
830 /* Serialize XML subtree to buffer preserving XML indentatition.
831 * @context is session context
832 * @subtree is XML element to be serialized (with childern)
833 * @buffer is automatically reallocated buffer where serialize to
834 * @length is size of serialized stream in bytes
835 * @return standard error code, free @buffer in case of error */
836 static isds_error
serialize_subtree(struct isds_ctx
*context
,
837 xmlNodePtr subtree
, void **buffer
, size_t *length
) {
838 isds_error err
= IE_SUCCESS
;
839 xmlBufferPtr xml_buffer
= NULL
;
840 xmlSaveCtxtPtr save_ctx
= NULL
;
841 xmlDocPtr subtree_doc
= NULL
;
842 xmlNodePtr subtree_copy
;
846 if (!context
) return IE_INVALID_CONTEXT
;
847 if (!buffer
) return IE_INVAL
;
849 if (!subtree
|| !length
) return IE_INVAL
;
851 /* Make temporary XML document with @subtree root element */
852 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
853 * It can result in not well-formed on invalid XML tree (e.g. name space
854 * prefix definition can miss. */
857 subtree_doc
= xmlNewDoc(BAD_CAST
"1.0");
859 isds_log_message(context
, _("Could not build temporary document"));
864 /* XXX: Copy subtree and attach the copy to document.
865 * One node can not bee attached into more document at the same time.
866 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
868 * XXX: Check xmlSaveTree() too. */
869 subtree_copy
= xmlCopyNodeList(subtree
);
871 isds_log_message(context
, _("Could not copy subtree"));
875 xmlDocSetRootElement(subtree_doc
, subtree_copy
);
877 /* Only this way we get namespace definition as @xmlns:isds,
878 * otherwise we get namespace prefix without definition */
879 /* FIXME: Don't overwrite original default namespace */
880 isds_ns
= xmlNewNs(subtree_copy
, BAD_CAST ISDS_NS
, NULL
);
882 isds_log_message(context
, _("Could not create ISDS name space"));
886 xmlSetNs(subtree_copy
, isds_ns
);
889 /* Serialize the document into buffer */
890 xml_buffer
= xmlBufferCreate();
892 isds_log_message(context
, _("Could not create xmlBuffer"));
896 /* Last argument 0 means to not format the XML tree */
897 save_ctx
= xmlSaveToBuffer(xml_buffer
, "UTF-8", 0);
899 isds_log_message(context
, _("Could not create XML serializer"));
903 /* XXX: According LibXML documentation, this function does not return
904 * meaningfull value yet */
905 xmlSaveDoc(save_ctx
, subtree_doc
);
906 if (-1 == xmlSaveFlush(save_ctx
)) {
907 isds_log_message(context
,
908 _("Could not serialize XML subtree"));
912 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
913 * even after xmlSaveFlush(). Thus close it here */
914 xmlSaveClose(save_ctx
); save_ctx
= NULL
;
917 /* Store and detach buffer from xml_buffer */
918 *buffer
= xml_buffer
->content
;
919 *length
= xml_buffer
->use
;
920 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
923 new_buffer
= realloc(*buffer
, *length
);
924 if (new_buffer
) *buffer
= new_buffer
;
932 xmlSaveClose(save_ctx
);
933 xmlBufferFree(xml_buffer
);
934 xmlFreeDoc(subtree_doc
); /* Frees subtree_copy, isds_ns etc. */
939 /* Dump XML subtree to buffer as literral string, not valid XML possibly.
940 * @context is session context
941 * @document is original document where @nodeset points to
942 * @nodeset is XPath node set to dump (recursively)
943 * @buffer is automarically reallocated buffer where serialize to
944 * @length is size of serialized stream in bytes
945 * @return standard error code, free @buffer in case of error */
946 static isds_error
dump_nodeset(struct isds_ctx
*context
,
947 const xmlDocPtr document
, const xmlNodeSetPtr nodeset
,
948 void **buffer
, size_t *length
) {
949 isds_error err
= IE_SUCCESS
;
950 xmlBufferPtr xml_buffer
= NULL
;
953 if (!context
) return IE_INVALID_CONTEXT
;
954 if (!buffer
) return IE_INVAL
;
956 if (!document
|| !nodeset
|| !length
) return IE_INVAL
;
959 /* Empty node set results into NULL buffer */
960 if (xmlXPathNodeSetIsEmpty(nodeset
)) {
964 /* Resuling the document into buffer */
965 xml_buffer
= xmlBufferCreate();
967 isds_log_message(context
, _("Could not create xmlBuffer"));
972 /* Itearate over all nodes */
973 for (int i
= 0; i
< nodeset
->nodeNr
; i
++) {
975 * XXX: xmlNodeDump() appends to xml_buffer. */
977 xmlNodeDump(xml_buffer
, document
, nodeset
->nodeTab
[i
], 0, 0)) {
978 isds_log_message(context
, _("Could not dump XML node"));
984 /* Store and detach buffer from xml_buffer */
985 *buffer
= xml_buffer
->content
;
986 *length
= xml_buffer
->use
;
987 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
990 new_buffer
= realloc(*buffer
, *length
);
991 if (new_buffer
) *buffer
= new_buffer
;
1000 xmlBufferFree(xml_buffer
);
1006 /* Dump XML subtree to buffer as literral string, not valid XML possibly.
1007 * @context is session context
1008 * @document is original document where @nodeset points to
1009 * @nodeset is XPath node set to dump (recursively)
1010 * @buffer is automarically reallocated buffer where serialize to
1011 * @length is size of serialized stream in bytes
1012 * @return standard error code, free @buffer in case of error */
1013 static isds_error
dump_nodeset(struct isds_ctx
*context
,
1014 const xmlDocPtr document
, const xmlNodeSetPtr nodeset
,
1015 void **buffer
, size_t *length
) {
1016 isds_error err
= IE_SUCCESS
;
1017 xmlBufferPtr xml_buffer
= NULL
;
1018 xmlSaveCtxtPtr save_ctx
= NULL
;
1021 if (!context
) return IE_INVALID_CONTEXT
;
1022 if (!buffer
) return IE_INVAL
;
1024 if (!document
|| !nodeset
|| !length
) return IE_INVAL
;
1027 /* Empty node set results into NULL buffer */
1028 if (xmlXPathNodeSetIsEmpty(nodeset
)) {
1032 /* Resuling the document into buffer */
1033 xml_buffer
= xmlBufferCreate();
1035 isds_log_message(context
, _("Could not create xmlBuffer"));
1039 if (xmlSubstituteEntitiesDefault(1)) {
1040 isds_log_message(context
, _("Could not disable attribute escaping"));
1044 /* Last argument means:
1045 * 0 to not format the XML tree
1046 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1047 save_ctx
= xmlSaveToBuffer(xml_buffer
, "UTF-8",
1048 XML_SAVE_NO_DECL
|XML_SAVE_NO_EMPTY
|XML_SAVE_NO_XHTML
);
1050 isds_log_message(context
, _("Could not create XML serializer"));
1054 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1055 isds_log_message(context, _("Could not disable attribute escaping"));
1061 /* Itearate over all nodes */
1062 for (int i
= 0; i
< nodeset
->nodeNr
; i
++) {
1064 * XXX: xmlNodeDump() appends to xml_buffer. */
1066 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1068 /* XXX: According LibXML documentation, this function does not return
1069 * meaningfull value yet */
1070 xmlSaveTree(save_ctx
, nodeset
->nodeTab
[i
]);
1071 if (-1 == xmlSaveFlush(save_ctx
)) {
1072 isds_log_message(context
,
1073 _("Could not serialize XML subtree"));
1079 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1080 * even after xmlSaveFlush(). Thus close it here */
1081 xmlSaveClose(save_ctx
); save_ctx
= NULL
;
1083 /* Store and detach buffer from xml_buffer */
1084 *buffer
= xml_buffer
->content
;
1085 *length
= xml_buffer
->use
;
1086 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
1089 new_buffer
= realloc(*buffer
, *length
);
1090 if (new_buffer
) *buffer
= new_buffer
;
1098 xmlSaveClose(save_ctx
);
1099 xmlBufferFree(xml_buffer
);
1105 /* Convert UTF-8 @string represantion of ISDS dbType to enum @type */
1106 static isds_error
string2isds_DbType(xmlChar
*string
, isds_DbType
*type
) {
1107 if (!string
|| !type
) return IE_INVAL
;
1109 if (!xmlStrcmp(string
, BAD_CAST
"FO"))
1111 else if (!xmlStrcmp(string
, BAD_CAST
"PFO"))
1113 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_ADVOK"))
1114 *type
= DBTYPE_PFO_ADVOK
;
1115 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_DANPOR"))
1116 *type
= DBTYPE_PFO_DANPOR
;
1117 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_INSSPR"))
1118 *type
= DBTYPE_PFO_INSSPR
;
1119 else if (!xmlStrcmp(string
, BAD_CAST
"PO"))
1121 else if (!xmlStrcmp(string
, BAD_CAST
"PO_ZAK"))
1122 *type
= DBTYPE_PO_ZAK
;
1123 else if (!xmlStrcmp(string
, BAD_CAST
"PO_REQ"))
1124 *type
= DBTYPE_PO_REQ
;
1125 else if (!xmlStrcmp(string
, BAD_CAST
"OVM"))
1127 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_NOTAR"))
1128 *type
= DBTYPE_OVM_NOTAR
;
1129 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_EXEKUT"))
1130 *type
= DBTYPE_OVM_EXEKUT
;
1131 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_REQ"))
1132 *type
= DBTYPE_OVM_REQ
;
1139 /* Convert ISDS dbType enum @type to UTF-8 string.
1140 * @Return pointer to static string, or NULL if unkwnow enum value */
1141 static const xmlChar
*isds_DbType2string(const isds_DbType type
) {
1143 case DBTYPE_FO
: return(BAD_CAST
"FO"); break;
1144 case DBTYPE_PFO
: return(BAD_CAST
"PFO"); break;
1145 case DBTYPE_PFO_ADVOK
: return(BAD_CAST
"PFO_ADVOK"); break;
1146 case DBTYPE_PFO_DANPOR
: return(BAD_CAST
"PFO_DANPOR"); break;
1147 case DBTYPE_PFO_INSSPR
: return(BAD_CAST
"PFO_INSSPR"); break;
1148 case DBTYPE_PO
: return(BAD_CAST
"PO"); break;
1149 case DBTYPE_PO_ZAK
: return(BAD_CAST
"PO_ZAK"); break;
1150 case DBTYPE_PO_REQ
: return(BAD_CAST
"PO_REQ"); break;
1151 case DBTYPE_OVM
: return(BAD_CAST
"OVM"); break;
1152 case DBTYPE_OVM_NOTAR
: return(BAD_CAST
"OVM_NOTAR"); break;
1153 case DBTYPE_OVM_EXEKUT
: return(BAD_CAST
"OVM_EXEKUT"); break;
1154 case DBTYPE_OVM_REQ
: return(BAD_CAST
"OVM_REQ"); break;
1155 default: return NULL
; break;
1160 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
1161 * @Return pointer to static string, or NULL if unkwnow enum value */
1162 static const xmlChar
*isds_FileMetaType2string(const isds_FileMetaType type
) {
1164 case FILEMETATYPE_MAIN
: return(BAD_CAST
"main"); break;
1165 case FILEMETATYPE_ENCLOSURE
: return(BAD_CAST
"enclosure"); break;
1166 case FILEMETATYPE_SIGNATURE
: return(BAD_CAST
"signature"); break;
1167 case FILEMETATYPE_META
: return(BAD_CAST
"meta"); break;
1168 default: return NULL
; break;
1173 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
1174 * @Return IE_ENUM if @string is not valid enum member */
1175 static isds_error
string2isds_FileMetaType(const xmlChar
*string
,
1176 isds_FileMetaType
*type
) {
1177 if (!string
|| !type
) return IE_INVAL
;
1179 if (!xmlStrcmp(string
, BAD_CAST
"main"))
1180 *type
= FILEMETATYPE_MAIN
;
1181 else if (!xmlStrcmp(string
, BAD_CAST
"enclosure"))
1182 *type
= FILEMETATYPE_ENCLOSURE
;
1183 else if (!xmlStrcmp(string
, BAD_CAST
"signature"))
1184 *type
= FILEMETATYPE_SIGNATURE
;
1185 else if (!xmlStrcmp(string
, BAD_CAST
"meta"))
1186 *type
= FILEMETATYPE_META
;
1193 /* Convert UTF-8 @string to ISDS hash @algorithm.
1194 * @Return IE_ENUM if @string is not valid enum member */
1195 static isds_error
string2isds_hash_algorithm(const xmlChar
*string
,
1196 isds_hash_algorithm
*algorithm
) {
1197 if (!string
|| !algorithm
) return IE_INVAL
;
1199 if (!xmlStrcmp(string
, BAD_CAST
"MD5"))
1200 *algorithm
= HASH_ALGORITHM_MD5
;
1201 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-1"))
1202 *algorithm
= HASH_ALGORITHM_SHA_1
;
1203 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-256"))
1204 *algorithm
= HASH_ALGORITHM_SHA_256
;
1205 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-512"))
1206 *algorithm
= HASH_ALGORITHM_SHA_512
;
1213 /* Convert UTF-8 @string represantion of ISO 8601 date to @time.
1214 * XXX: Not all ISO formats are supported */
1215 static isds_error
datestring2tm(const xmlChar
*string
, struct tm
*time
) {
1217 if (!string
|| !time
) return IE_INVAL
;
1219 /* xsd:date is ISO 8601 string, thus ASCII */
1220 offset
= strptime((char*)string
, "%Y-%m-%d", time
);
1221 if (offset
&& *offset
== '\0')
1224 offset
= strptime((char*)string
, "%Y%m%d", time
);
1225 if (offset
&& *offset
== '\0')
1228 offset
= strptime((char*)string
, "%Y-%j", time
);
1229 if (offset
&& *offset
== '\0')
1236 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
1237 static isds_error
tm2datestring(const struct tm
*time
, xmlChar
**string
) {
1238 if (!time
|| !string
) return IE_INVAL
;
1240 if (-1 == isds_asprintf((char **) string
, "%d-%02d-%02d",
1241 time
->tm_year
+ 1900, time
->tm_mon
+ 1, time
->tm_mday
))
1248 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
1249 * respects the @time microseconds too. */
1250 static isds_error
timeval2timestring(const struct timeval
*time
,
1254 if (!time
|| !string
) return IE_INVAL
;
1256 if (!gmtime_r(&time
->tv_sec
, &broken
)) return IE_DATE
;
1257 if (time
->tv_usec
< 0 || time
->tv_usec
> 999999) return IE_DATE
;
1259 /* TODO: small negative year should be formated as "-0012". This is not
1260 * true for glibc "%04d". We should implement it.
1261 * TODO: What's type of time->tv_usec exactly? Unsigned? Absolute?
1262 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
1263 if (-1 == isds_asprintf((char **) string
,
1264 "%04d-%02d-%02dT%02d:%02d:%02d.%06ld",
1265 broken
.tm_year
+ 1900, broken
.tm_mon
+ 1, broken
.tm_mday
,
1266 broken
.tm_hour
, broken
.tm_min
, broken
.tm_sec
,
1274 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
1275 * It respects microseconds too.
1276 * In case of error, @time will be freed. */
1277 static isds_error
timestring2timeval(const xmlChar
*string
,
1278 struct timeval
**time
) {
1280 char *offset
, *delim
, *endptr
;
1282 int offset_hours
, offset_minutes
;
1285 if (!time
) return IE_INVAL
;
1287 memset(&broken
, 0, sizeof(broken
));
1290 *time
= calloc(1, sizeof(**time
));
1291 if (!*time
) return IE_NOMEM
;
1293 memset(*time
, 0, sizeof(**time
));
1297 /* xsd:date is ISO 8601 string, thus ASCII */
1298 /*TODO: negative year */
1300 /* Parse date and time without subseconds and offset */
1301 offset
= strptime((char*)string
, "%Y-%m-%dT%T", &broken
);
1303 free(*time
); *time
= NULL
;
1307 /* Get subseconds */
1308 if (*offset
== '.' ) {
1311 /* Copy first 6 digits, padd it with zeros.
1312 * XXX: It truncates longer number, no round.
1313 * Current server implementation uses only milisecond resolution. */
1314 /* TODO: isdigit() is locale sensitive */
1316 i
< sizeof(subseconds
)/sizeof(char) - 1 && isdigit(*offset
);
1318 subseconds
[i
] = *offset
;
1320 for (; i
< sizeof(subseconds
)/sizeof(char) - 1; i
++) {
1321 subseconds
[i
] = '0';
1323 subseconds
[6] = '\0';
1325 /* Convert it into integer */
1326 (*time
)->tv_usec
= strtol(subseconds
, &endptr
, 10);
1327 if (*endptr
!= '\0' || (*time
)->tv_usec
== LONG_MIN
||
1328 (*time
)->tv_usec
== LONG_MAX
) {
1329 free(*time
); *time
= NULL
;
1333 /* move to the zone offset delimiter */
1334 delim
= strchr(offset
, '-');
1336 delim
= strchr(offset
, '+');
1340 /* Get zone offset */
1341 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
1342 * "" equals to "Z" and it means UTC zone. */
1343 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
1344 * colon separator */
1345 if (*offset
== '-' || *offset
== '+') {
1347 if (2 != sscanf(offset
, "%2d:%2d", &offset_hours
, &offset_minutes
)) {
1348 free(*time
); *time
= NULL
;
1351 broken
.tm_hour
-= offset_hours
;
1352 broken
.tm_min
-= offset_minutes
* ((offset_hours
<0) ? -1 : 1);
1355 /* Convert to time_t */
1357 (*time
)->tv_sec
= mktime(&broken
);
1358 switch_tz_to_native();
1359 if ((*time
)->tv_sec
== (time_t) -1) {
1360 free(*time
); *time
= NULL
;
1368 /* Convert unsigned int into isds_message_status.
1369 * @context is session context
1370 * @number is pointer to number value. NULL will be treated as invalid value.
1371 * @status is automatically reallocated status
1372 * @return IE_SUCCESS, or error code and free status */
1373 static isds_error
uint2isds_message_status(struct isds_ctx
*context
,
1374 const unsigned long int *number
, isds_message_status
**status
) {
1375 if (!context
) return IE_INVALID_CONTEXT
;
1376 if (!status
) return IE_INVAL
;
1378 free(*status
); *status
= NULL
;
1379 if (!number
) return IE_INVAL
;
1381 if (*number
< 1 || *number
> 9) {
1382 isds_printf_message(context
, _("Invalid messsage status value: %lu"),
1387 *status
= malloc(sizeof(**status
));
1388 if (!*status
) return IE_NOMEM
;
1390 **status
= 1 << *number
;
1395 /* Convert event description string into isds_event memebers type and
1397 * @string is raw event decsription starting with event prefix
1398 * @event is structure where to store type and stripped description to
1399 * @return standard error code, unkown prefix is not classified as an error. */
1400 static isds_error
eventstring2event(const xmlChar
*string
,
1401 struct isds_event
* event
) {
1402 const xmlChar
*known_prefixes
[] = {
1407 const isds_event_type types
[] = {
1408 EVENT_ACCEPTED_BY_RECIPIENT
,
1409 EVENT_ACCEPTED_BY_FICTION
,
1415 if (!string
|| !event
) return IE_INVAL
;
1418 event
->type
= malloc(sizeof(*event
->type
));
1419 if (!(event
->type
)) return IE_NOMEM
;
1421 zfree(event
->description
);
1423 for (index
= 0; index
< sizeof(known_prefixes
)/sizeof(known_prefixes
[0]);
1425 length
= xmlUTF8Strlen(known_prefixes
[index
]);
1427 if (!xmlStrncmp(string
, known_prefixes
[index
], length
)) {
1428 /* Prefix is known */
1429 *event
->type
= types
[index
];
1431 /* Strip prefix from description and spaces */
1432 /* TODO: Recognize all wite spaces from UCS blank class and
1433 * operate on UTF-8 chars. */
1434 for (; string
[length
] != '\0' && string
[length
] == ' '; length
++);
1435 event
->description
= strdup((char *) (string
+ length
));
1436 if (!(event
->description
)) return IE_NOMEM
;
1442 /* Unknown event prefix.
1443 * XSD allows any string */
1444 char *string_locale
= utf82locale((char *) string
);
1445 isds_log(ILF_ISDS
, ILL_WARNING
,
1446 _("Uknown delivery info event prefix: %s\n"), string_locale
);
1447 free(string_locale
);
1449 *event
->type
= EVENT_UKNOWN
;
1450 event
->description
= strdup((char *) string
);
1451 if (!(event
->description
)) return IE_NOMEM
;
1457 /* Following EXTRACT_* macros expects @result, @xpath_ctx, @err, @context
1458 * and leave lable */
1459 #define EXTRACT_STRING(element, string) \
1460 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
1465 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
1466 if (result->nodesetval->nodeNr > 1) { \
1467 isds_log_message(context, _("Multiple " element " element")); \
1471 (string) = (char *) \
1472 xmlXPathCastNodeSetToString(result->nodesetval); \
1479 #define EXTRACT_BOOLEAN(element, booleanPtr) \
1481 char *string = NULL; \
1482 EXTRACT_STRING(element, string); \
1485 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
1486 if (!(booleanPtr)) { \
1492 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
1493 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
1494 *(booleanPtr) = 1; \
1495 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
1496 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
1497 *(booleanPtr) = 0; \
1499 char *string_locale = utf82locale((char*)string); \
1500 isds_printf_message(context, \
1501 _(element " value is not valid boolean: "), \
1503 free(string_locale); \
1513 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
1515 char *string = NULL; \
1516 EXTRACT_STRING(element, string); \
1521 number = strtol((char*)string, &endptr, 10); \
1523 if (*endptr != '\0') { \
1524 char *string_locale = utf82locale((char *)string); \
1525 isds_printf_message(context, \
1526 _(element" is not valid integer: %s"), \
1528 free(string_locale); \
1534 if (number == LONG_MIN || number == LONG_MAX) { \
1535 char *string_locale = utf82locale((char *)string); \
1536 isds_printf_message(context, \
1537 _(element " value out of range of long int: %s"), \
1539 free(string_locale); \
1545 free(string); string = NULL; \
1547 if (!(preallocated)) { \
1548 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
1549 if (!(longintPtr)) { \
1554 *(longintPtr) = number; \
1558 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
1560 char *string = NULL; \
1561 EXTRACT_STRING(element, string); \
1566 number = strtol((char*)string, &endptr, 10); \
1568 if (*endptr != '\0') { \
1569 char *string_locale = utf82locale((char *)string); \
1570 isds_printf_message(context, \
1571 _(element" is not valid integer: %s"), \
1573 free(string_locale); \
1579 if (number == LONG_MIN || number == LONG_MAX) { \
1580 char *string_locale = utf82locale((char *)string); \
1581 isds_printf_message(context, \
1582 _(element " value out of range of long int: %s"), \
1584 free(string_locale); \
1590 free(string); string = NULL; \
1592 isds_printf_message(context, \
1593 _(element " value is negative: %ld"), number); \
1598 if (!(preallocated)) { \
1599 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
1600 if (!(ulongintPtr)) { \
1605 *(ulongintPtr) = number; \
1609 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) \
1610 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
1612 if ((required) && (!string)) { \
1613 char *attribute_locale = utf82locale(attribute); \
1614 char *element_locale = utf82locale((char *)xpath_ctx->node->name); \
1615 isds_printf_message(context, \
1616 _("Could not extract required %s attribute value from " \
1617 "%s element"), attribute_locale, element_locale); \
1618 free(element_locale); \
1619 free(attribute_locale); \
1625 #define INSERT_STRING(parent, element, string) \
1626 node = xmlNewTextChild(parent, NULL, BAD_CAST (element), \
1627 (xmlChar *) (string)); \
1629 isds_printf_message(context, _("Could not add " element " child to " \
1630 "%s element"), (parent)->name); \
1635 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
1636 if ((booleanPtr)) { \
1637 if (*(booleanPtr)) { INSERT_STRING(parent, element, "true"); } \
1638 else { INSERT_STRING(parent, element, "false") } \
1639 } else { INSERT_STRING(parent, element, NULL) }
1641 #define INSERT_LONGINT(parent, element, longintPtr, buffer) \
1642 if ((longintPtr)) { \
1643 /* FIXME: locale sensitive */ \
1644 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
1648 INSERT_STRING(parent, element, buffer) \
1649 free(buffer); (buffer) = NULL; \
1650 } else { INSERT_STRING(parent, element, NULL) }
1652 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) \
1653 if ((ulongintPtr)) { \
1654 /* FIXME: locale sensitive */ \
1655 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
1659 INSERT_STRING(parent, element, buffer) \
1660 free(buffer); (buffer) = NULL; \
1661 } else { INSERT_STRING(parent, element, NULL) }
1663 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
1664 /* FIXME: locale sensitive */ \
1665 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
1669 INSERT_STRING(parent, element, buffer) \
1670 free(buffer); (buffer) = NULL; \
1672 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
1673 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
1674 (xmlChar *) (string)); \
1675 if (!attribute_node) { \
1676 isds_printf_message(context, _("Could not add " attribute \
1677 " attribute to %s element"), (parent)->name); \
1683 /* Find child element by name in given XPath context and switch context onto
1684 * it. The child must be uniq and must exist. Otherwise failes.
1685 * @context is ISDS context
1686 * @child is child element name
1687 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
1688 * into it child. In error case, the @xpath_ctx keeps original value. */
1689 static isds_error
move_xpathctx_to_child(struct isds_ctx
*context
,
1690 const xmlChar
*child
, xmlXPathContextPtr xpath_ctx
) {
1691 isds_error err
= IE_SUCCESS
;
1692 xmlXPathObjectPtr result
= NULL
;
1694 if (!context
) return IE_INVALID_CONTEXT
;
1695 if (!child
|| !xpath_ctx
) return IE_INVAL
;
1698 result
= xmlXPathEvalExpression(child
, xpath_ctx
);
1705 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
1706 char *parent_locale
= utf82locale((char*) xpath_ctx
->node
->name
);
1707 char *child_locale
= utf82locale((char*) child
);
1708 isds_printf_message(context
,
1709 _("%s element does not contain %s child"),
1710 parent_locale
, child_locale
);
1712 free(parent_locale
);
1718 if (result
->nodesetval
->nodeNr
> 1) {
1719 char *parent_locale
= utf82locale((char*) xpath_ctx
->node
->name
);
1720 char *child_locale
= utf82locale((char*) child
);
1721 isds_printf_message(context
,
1722 _("%s element contains multiple %s childs"),
1723 parent_locale
, child_locale
);
1725 free(parent_locale
);
1730 /* Switch context */
1731 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
1734 xmlXPathFreeObject(result
);
1740 /* Convert isds:dBOwnerInfo XML tree into structure
1741 * @context is ISDS context
1742 * @db_owner_info is automically reallocated box owner info structure
1743 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
1744 * In case of error @db_owner_info will be freed. */
1745 static isds_error
extract_DbOwnerInfo(struct isds_ctx
*context
,
1746 struct isds_DbOwnerInfo
**db_owner_info
,
1747 xmlXPathContextPtr xpath_ctx
) {
1748 isds_error err
= IE_SUCCESS
;
1749 xmlXPathObjectPtr result
= NULL
;
1750 char *string
= NULL
;
1752 if (!context
) return IE_INVALID_CONTEXT
;
1753 if (!db_owner_info
) return IE_INVAL
;
1754 isds_DbOwnerInfo_free(db_owner_info
);
1755 if (!xpath_ctx
) return IE_INVAL
;
1758 *db_owner_info
= calloc(1, sizeof(**db_owner_info
));
1759 if (!*db_owner_info
) {
1764 EXTRACT_STRING("isds:dbID", (*db_owner_info
)->dbID
);
1766 EXTRACT_STRING("isds:dbType", string
);
1768 (*db_owner_info
)->dbType
=
1769 calloc(1, sizeof(*((*db_owner_info
)->dbType
)));
1770 if (!(*db_owner_info
)->dbType
) {
1774 err
= string2isds_DbType((xmlChar
*)string
, (*db_owner_info
)->dbType
);
1776 free((*db_owner_info
)->dbType
);
1777 (*db_owner_info
)->dbType
= NULL
;
1778 if (err
== IE_ENUM
) {
1780 isds_printf_message(context
, _("Unknown isds:dbType: %s"),
1785 free(string
); string
= NULL
;
1788 EXTRACT_STRING("isds:ic", (*db_owner_info
)->ic
);
1790 (*db_owner_info
)->personName
=
1791 calloc(1, sizeof(*((*db_owner_info
)->personName
)));
1792 if (!(*db_owner_info
)->personName
) {
1796 EXTRACT_STRING("isds:pnFirstName",
1797 (*db_owner_info
)->personName
->pnFirstName
);
1798 EXTRACT_STRING("isds:pnMiddleName",
1799 (*db_owner_info
)->personName
->pnMiddleName
);
1800 EXTRACT_STRING("isds:pnLastName",
1801 (*db_owner_info
)->personName
->pnLastName
);
1802 EXTRACT_STRING("isds:pnLastNameAtBirth",
1803 (*db_owner_info
)->personName
->pnLastNameAtBirth
);
1804 if (!(*db_owner_info
)->personName
->pnFirstName
&&
1805 !(*db_owner_info
)->personName
->pnMiddleName
&&
1806 !(*db_owner_info
)->personName
->pnLastName
&&
1807 !(*db_owner_info
)->personName
->pnLastNameAtBirth
)
1808 isds_PersonName_free(&(*db_owner_info
)->personName
);
1810 EXTRACT_STRING("isds:firmName", (*db_owner_info
)->firmName
);
1812 (*db_owner_info
)->birthInfo
=
1813 calloc(1, sizeof(*((*db_owner_info
)->birthInfo
)));
1814 if (!(*db_owner_info
)->birthInfo
) {
1818 EXTRACT_STRING("isds:biDate", string
);
1820 (*db_owner_info
)->birthInfo
->biDate
=
1821 calloc(1, sizeof(*((*db_owner_info
)->birthInfo
->biDate
)));
1822 if (!(*db_owner_info
)->birthInfo
->biDate
) {
1826 err
= datestring2tm((xmlChar
*)string
,
1827 (*db_owner_info
)->birthInfo
->biDate
);
1829 free((*db_owner_info
)->birthInfo
->biDate
);
1830 (*db_owner_info
)->birthInfo
->biDate
= NULL
;
1831 if (err
== IE_NOTSUP
) {
1833 isds_printf_message(context
,
1834 _("Invalid isds:biDate value: %s"), (char *)string
);
1838 free(string
); string
= NULL
;
1840 EXTRACT_STRING("isds:biCity", (*db_owner_info
)->birthInfo
->biCity
);
1841 EXTRACT_STRING("isds:biCounty", (*db_owner_info
)->birthInfo
->biCounty
);
1842 EXTRACT_STRING("isds:biState", (*db_owner_info
)->birthInfo
->biState
);
1843 if (!(*db_owner_info
)->birthInfo
->biDate
&&
1844 !(*db_owner_info
)->birthInfo
->biCity
&&
1845 !(*db_owner_info
)->birthInfo
->biCounty
&&
1846 !(*db_owner_info
)->birthInfo
->biState
)
1847 isds_BirthInfo_free(&(*db_owner_info
)->birthInfo
);
1849 (*db_owner_info
)->address
=
1850 calloc(1, sizeof(*((*db_owner_info
)->address
)));
1851 if (!(*db_owner_info
)->address
) {
1855 EXTRACT_STRING("isds:adCity",
1856 (*db_owner_info
)->address
->adCity
);
1857 EXTRACT_STRING("isds:adStreet",
1858 (*db_owner_info
)->address
->adStreet
);
1859 EXTRACT_STRING("isds:adNumberInStreet",
1860 (*db_owner_info
)->address
->adNumberInStreet
);
1861 EXTRACT_STRING("isds:adNumberInMunicipality",
1862 (*db_owner_info
)->address
->adNumberInMunicipality
);
1863 EXTRACT_STRING("isds:adZipCode",
1864 (*db_owner_info
)->address
->adZipCode
);
1865 EXTRACT_STRING("isds:adState",
1866 (*db_owner_info
)->address
->adState
);
1867 if (!(*db_owner_info
)->address
->adCity
&&
1868 !(*db_owner_info
)->address
->adStreet
&&
1869 !(*db_owner_info
)->address
->adNumberInStreet
&&
1870 !(*db_owner_info
)->address
->adNumberInMunicipality
&&
1871 !(*db_owner_info
)->address
->adZipCode
&&
1872 !(*db_owner_info
)->address
->adState
)
1873 isds_Address_free(&(*db_owner_info
)->address
);
1875 EXTRACT_STRING("isds:nationality", (*db_owner_info
)->nationality
);
1876 EXTRACT_STRING("isds:email", (*db_owner_info
)->email
);
1877 EXTRACT_STRING("isds:telNumber", (*db_owner_info
)->telNumber
);
1878 EXTRACT_STRING("isds:identifier", (*db_owner_info
)->identifier
);
1879 EXTRACT_STRING("isds:registryCode", (*db_owner_info
)->registryCode
);
1881 EXTRACT_LONGINT("isds:dbState", (*db_owner_info
)->dbState
, 0);
1883 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info
)->dbEffectiveOVM
);
1884 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
1885 (*db_owner_info
)->dbOpenAddressing
);
1888 if (err
) isds_DbOwnerInfo_free(db_owner_info
);
1890 xmlXPathFreeObject(result
);
1895 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
1896 * isds_envelope structure. The envelope is automatically allocated but not
1897 * reallocated. The date are just appended into envelope structure.
1898 * @context is ISDS context
1899 * @envelope is automically allocated message envelope structure
1900 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
1901 * In case of error @envelope will be freed. */
1902 static isds_error
append_GMessageEnvelopeSub(struct isds_ctx
*context
,
1903 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
1904 isds_error err
= IE_SUCCESS
;
1905 xmlXPathObjectPtr result
= NULL
;
1907 if (!context
) return IE_INVALID_CONTEXT
;
1908 if (!envelope
) return IE_INVAL
;
1909 if (!xpath_ctx
) return IE_INVAL
;
1913 /* Allocate envelope */
1914 *envelope
= calloc(1, sizeof(**envelope
));
1920 /* Else free former data */
1921 zfree((*envelope
)->dmSenderOrgUnit
);
1922 zfree((*envelope
)->dmSenderOrgUnitNum
);
1923 zfree((*envelope
)->dbIDRecipient
);
1924 zfree((*envelope
)->dmRecipientOrgUnit
);
1925 zfree((*envelope
)->dmSenderOrgUnitNum
);
1926 zfree((*envelope
)->dmToHands
);
1927 zfree((*envelope
)->dmAnnotation
);
1928 zfree((*envelope
)->dmRecipientRefNumber
);
1929 zfree((*envelope
)->dmSenderRefNumber
);
1930 zfree((*envelope
)->dmRecipientIdent
);
1931 zfree((*envelope
)->dmSenderIdent
);
1932 zfree((*envelope
)->dmLegalTitleLaw
);
1933 zfree((*envelope
)->dmLegalTitleYear
);
1934 zfree((*envelope
)->dmLegalTitleSect
);
1935 zfree((*envelope
)->dmLegalTitlePar
);
1936 zfree((*envelope
)->dmLegalTitlePoint
);
1937 zfree((*envelope
)->dmPersonalDelivery
);
1938 zfree((*envelope
)->dmAllowSubstDelivery
);
1941 /* Extract envelope elements added by sender or ISDS
1942 * (XSD: gMessageEnvelopeSub type) */
1943 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope
)->dmSenderOrgUnit
);
1944 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
1945 (*envelope
)->dmSenderOrgUnitNum
, 0);
1946 EXTRACT_STRING("isds:dbIDRecipient", (*envelope
)->dbIDRecipient
);
1947 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope
)->dmRecipientOrgUnit
);
1948 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
1949 (*envelope
)->dmSenderOrgUnitNum
, 0);
1950 EXTRACT_STRING("isds:dmToHands", (*envelope
)->dmToHands
);
1951 EXTRACT_STRING("isds:dmAnnotation", (*envelope
)->dmAnnotation
);
1952 EXTRACT_STRING("isds:dmRecipientRefNumber",
1953 (*envelope
)->dmRecipientRefNumber
);
1954 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope
)->dmSenderRefNumber
);
1955 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope
)->dmRecipientIdent
);
1956 EXTRACT_STRING("isds:dmSenderIdent", (*envelope
)->dmSenderIdent
);
1958 /* Extract envelope elements regarding law refference */
1959 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope
)->dmLegalTitleLaw
, 0);
1960 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope
)->dmLegalTitleYear
, 0);
1961 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope
)->dmLegalTitleSect
);
1962 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope
)->dmLegalTitlePar
);
1963 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope
)->dmLegalTitlePoint
);
1965 /* Extract envelope other elements */
1966 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope
)->dmPersonalDelivery
);
1967 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
1968 (*envelope
)->dmAllowSubstDelivery
);
1971 if (err
) isds_envelope_free(envelope
);
1972 xmlXPathFreeObject(result
);
1978 /* Convert XSD gMessageEnvelope group of elements from XML tree into
1979 * isds_envelope structure. The envelope is automatically allocated but not
1980 * reallocated. The date are just appended into envelope structure.
1981 * @context is ISDS context
1982 * @envelope is automically allocated message envelope structure
1983 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
1984 * In case of error @envelope will be freed. */
1985 static isds_error
append_GMessageEnvelope(struct isds_ctx
*context
,
1986 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
1987 isds_error err
= IE_SUCCESS
;
1988 xmlXPathObjectPtr result
= NULL
;
1990 if (!context
) return IE_INVALID_CONTEXT
;
1991 if (!envelope
) return IE_INVAL
;
1992 if (!xpath_ctx
) return IE_INVAL
;
1996 /* Allocate envelope */
1997 *envelope
= calloc(1, sizeof(**envelope
));
2003 /* Else free former data */
2004 zfree((*envelope
)->dmID
);
2005 zfree((*envelope
)->dbIDSender
);
2006 zfree((*envelope
)->dmSender
);
2007 zfree((*envelope
)->dmSenderAddress
);
2008 zfree((*envelope
)->dmSenderType
);
2009 zfree((*envelope
)->dmRecipient
);
2010 zfree((*envelope
)->dmRecipientAddress
);
2011 zfree((*envelope
)->dmAmbiguousRecipient
);
2014 /* Extract envelope elements added by ISDS
2015 * (XSD: gMessageEnvelope type) */
2016 EXTRACT_STRING("isds:dmID", (*envelope
)->dmID
);
2017 EXTRACT_STRING("isds:dbIDSender", (*envelope
)->dbIDSender
);
2018 EXTRACT_STRING("isds:dmSender", (*envelope
)->dmSender
);
2019 EXTRACT_STRING("isds:dmSenderAddress", (*envelope
)->dmSenderAddress
);
2020 /* XML Schema does not guaratee enumratation. It's plain xs:int. */
2021 EXTRACT_LONGINT("isds:dmSenderType", (*envelope
)->dmSenderType
, 0);
2022 EXTRACT_STRING("isds:dmRecipient", (*envelope
)->dmRecipient
);
2023 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope
)->dmRecipientAddress
);
2024 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
2025 (*envelope
)->dmAmbiguousRecipient
);
2027 /* Extract envelope elements added by sender and ISDS
2028 * (XSD: gMessageEnvelope type) */
2029 err
= append_GMessageEnvelopeSub(context
, envelope
, xpath_ctx
);
2030 if (err
) goto leave
;
2033 if (err
) isds_envelope_free(envelope
);
2034 xmlXPathFreeObject(result
);
2039 /* Convert other envelope elements from XML tree into isds_envelope structure:
2040 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
2041 * The envelope is automatically allocated but not reallocated.
2042 * The data are just appended into envelope structure.
2043 * @context is ISDS context
2044 * @envelope is automically allocated message envelope structure
2045 * @xpath_ctx is XPath context with current node as parent desired elements
2046 * In case of error @envelope will be freed. */
2047 static isds_error
append_status_size_times(struct isds_ctx
*context
,
2048 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
2049 isds_error err
= IE_SUCCESS
;
2050 xmlXPathObjectPtr result
= NULL
;
2051 char *string
= NULL
;
2052 unsigned long int *unumber
= NULL
;
2054 if (!context
) return IE_INVALID_CONTEXT
;
2055 if (!envelope
) return IE_INVAL
;
2056 if (!xpath_ctx
) return IE_INVAL
;
2061 *envelope
= calloc(1, sizeof(**envelope
));
2068 zfree((*envelope
)->dmMessageStatus
);
2069 zfree((*envelope
)->dmAttachmentSize
);
2070 zfree((*envelope
)->dmDeliveryTime
);
2071 zfree((*envelope
)->dmAcceptanceTime
);
2075 /* dmMessageStatus element is mandatory */
2076 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber
, 0);
2078 isds_log_message(context
,
2079 _("Missing mandatory sisds:dmMessageStatus integer"));
2083 err
= uint2isds_message_status(context
, unumber
,
2084 &((*envelope
)->dmMessageStatus
));
2086 if (err
== IE_ENUM
) err
= IE_ISDS
;
2089 free(unumber
); unumber
= NULL
;
2091 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope
)->dmAttachmentSize
,
2094 EXTRACT_STRING("sisds:dmDeliveryTime", string
);
2096 err
= timestring2timeval((xmlChar
*) string
,
2097 &((*envelope
)->dmDeliveryTime
));
2099 char *string_locale
= utf82locale(string
);
2100 if (err
== IE_DATE
) err
= IE_ISDS
;
2101 isds_printf_message(context
,
2102 _("Could not convert dmDeliveryTime as ISO time: %s"),
2104 free(string_locale
);
2110 EXTRACT_STRING("sisds:dmAcceptanceTime", string
);
2112 err
= timestring2timeval((xmlChar
*) string
,
2113 &((*envelope
)->dmAcceptanceTime
));
2115 char *string_locale
= utf82locale(string
);
2116 if (err
== IE_DATE
) err
= IE_ISDS
;
2117 isds_printf_message(context
,
2118 _("Could not convert dmAcceptanceTime as ISO time: %s"),
2120 free(string_locale
);
2127 if (err
) isds_envelope_free(envelope
);
2130 xmlXPathFreeObject(result
);
2135 /* Extract message document into reallocated document structure
2136 * @context is ISDS context
2137 * @document is automically reallocated message documents structure
2138 * @xpath_ctx is XPath context with current node as isds:dmFile
2139 * In case of error @document will be freed. */
2140 static isds_error
extract_document(struct isds_ctx
*context
,
2141 struct isds_document
**document
, xmlXPathContextPtr xpath_ctx
) {
2142 isds_error err
= IE_SUCCESS
;
2143 xmlXPathObjectPtr result
= NULL
;
2144 xmlNodePtr file_node
= xpath_ctx
->node
;
2145 char *string
= NULL
;
2147 if (!context
) return IE_INVALID_CONTEXT
;
2148 if (!document
) return IE_INVAL
;
2149 isds_document_free(document
);
2150 if (!xpath_ctx
) return IE_INVAL
;
2152 *document
= calloc(1, sizeof(**document
));
2158 /* Extract document metadata */
2159 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document
)->dmMimeType
, 1)
2161 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string
, 1)
2162 err
= string2isds_FileMetaType((xmlChar
*)string
,
2163 &((*document
)->dmFileMetaType
));
2165 char *meta_type_locale
= utf82locale(string
);
2166 isds_printf_message(context
,
2167 _("Document has invalid dmFileMetaType attribute value: %s"),
2169 free(meta_type_locale
);
2175 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document
)->dmFileGuid
, 0)
2176 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document
)->dmUpFileGuid
, 0)
2177 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document
)->dmFileDescr
, 0)
2178 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document
)->dmFormat
, 0)
2181 /* Extract document data.
2182 * Base64 encoded blob or XML subtree must be presented. */
2184 /* Check from dmEncodedContent */
2185 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmEncodedContent",
2192 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
2193 /* Here we have Base64 blob */
2195 if (result
->nodesetval
->nodeNr
> 1) {
2196 isds_printf_message(context
,
2197 _("Document has more dmEncodedContent elements"));
2202 xmlXPathFreeObject(result
); result
= NULL
;
2203 EXTRACT_STRING("isds:dmEncodedContent", string
);
2205 /* Decode non-emptys document */
2206 if (string
&& string
[0] != '\0') {
2207 (*document
)->data_length
= b64decode(string
, &((*document
)->data
));
2208 if ((*document
)->data_length
== (size_t) -1) {
2209 isds_printf_message(context
,
2210 _("Error while Base64-decoding document content"));
2216 /* No Base64 blob, try XML document */
2217 xmlXPathFreeObject(result
); result
= NULL
;
2218 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmXMLContent",
2225 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
2226 /* Here we have XML document */
2228 if (result
->nodesetval
->nodeNr
> 1) {
2229 isds_printf_message(context
,
2230 _("Document has more dmXMLContent elements"));
2235 /* FIXME: Serialize the tree rooted at result's node */
2236 isds_printf_message(context
,
2237 _("XML documents not yet supported"));
2241 /* No bas64 blob, nor XML document */
2242 isds_printf_message(context
,
2243 _("Document has no dmEncodedContent, nor dmXMLContent "
2252 if (err
) isds_document_free(document
);
2254 xmlXPathFreeObject(result
);
2255 xpath_ctx
->node
= file_node
;
2261 /* Extract message documents into reallocated list of documents
2262 * @context is ISDS context
2263 * @documents is automically reallocated message documents list structure
2264 * @xpath_ctx is XPath context with current node as XSD tFilesArray
2265 * In case of error @documents will be freed. */
2266 static isds_error
extract_documents(struct isds_ctx
*context
,
2267 struct isds_list
**documents
, xmlXPathContextPtr xpath_ctx
) {
2268 isds_error err
= IE_SUCCESS
;
2269 xmlXPathObjectPtr result
= NULL
;
2270 xmlNodePtr files_node
= xpath_ctx
->node
;
2271 struct isds_list
*document
, *prev_document
;
2273 if (!context
) return IE_INVALID_CONTEXT
;
2274 if (!documents
) return IE_INVAL
;
2275 isds_list_free(documents
);
2276 if (!xpath_ctx
) return IE_INVAL
;
2278 /* Find documents */
2279 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmFile", xpath_ctx
);
2286 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
2287 isds_printf_message(context
,
2288 _("Message does not contain any document"));
2294 /* Iterate over documents */
2295 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
2297 /* Allocate and append list item */
2298 document
= calloc(1, sizeof(*document
));
2303 document
->destructor
= (void (*)(void **))isds_document_free
;
2304 if (i
== 0) *documents
= document
;
2305 else prev_document
->next
= document
;
2306 prev_document
= document
;
2308 /* Extract document */
2309 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
2310 err
= extract_document(context
,
2311 (struct isds_document
**) &(document
->data
), xpath_ctx
);
2312 if (err
) goto leave
;
2317 if (err
) isds_list_free(documents
);
2318 xmlXPathFreeObject(result
);
2319 xpath_ctx
->node
= files_node
;
2324 /* Convert isds:dmRecord XML tree into structure
2325 * @context is ISDS context
2326 * @envelope is automically reallocated message envelope structure
2327 * @xpath_ctx is XPath context with current node as isds:dmRecord element
2328 * In case of error @envelope will be freed. */
2329 static isds_error
extract_DmRecord(struct isds_ctx
*context
,
2330 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
2331 isds_error err
= IE_SUCCESS
;
2332 xmlXPathObjectPtr result
= NULL
;
2334 if (!context
) return IE_INVALID_CONTEXT
;
2335 if (!envelope
) return IE_INVAL
;
2336 isds_envelope_free(envelope
);
2337 if (!xpath_ctx
) return IE_INVAL
;
2340 *envelope
= calloc(1, sizeof(**envelope
));
2347 /* Extract tRecord data */
2348 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope
)->dmOrdinal
, 0);
2350 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
2351 * dmAcceptanceTime. */
2352 err
= append_status_size_times(context
, envelope
, xpath_ctx
);
2353 if (err
) goto leave
;
2355 /* Extract envelope elements added by sender and ISDS
2356 * (XSD: gMessageEnvelope type) */
2357 err
= append_GMessageEnvelope(context
, envelope
, xpath_ctx
);
2358 if (err
) goto leave
;
2359 /* dmOVM can not be obtained from ISDS */
2362 if (err
) isds_envelope_free(envelope
);
2363 xmlXPathFreeObject(result
);
2368 /* Find and convert isds:dmHash XML tree into structure
2369 * @context is ISDS context
2370 * @envelope is automically reallocated message hash structure
2371 * @xpath_ctx is XPath context with current node containing isds:dmHash child
2372 * In case of error @hash will be freed. */
2373 static isds_error
find_and_extract_DmHash(struct isds_ctx
*context
,
2374 struct isds_hash
**hash
, xmlXPathContextPtr xpath_ctx
) {
2375 isds_error err
= IE_SUCCESS
;
2376 xmlNodePtr old_ctx_node
;
2377 xmlXPathObjectPtr result
= NULL
;
2378 char *string
= NULL
;
2380 if (!context
) return IE_INVALID_CONTEXT
;
2381 if (!hash
) return IE_INVAL
;
2382 isds_hash_free(hash
);
2383 if (!xpath_ctx
) return IE_INVAL
;
2386 *hash
= calloc(1, sizeof(**hash
));
2392 old_ctx_node
= xpath_ctx
->node
;
2395 err
= move_xpathctx_to_child(context
, BAD_CAST
"sisds:dmHash", xpath_ctx
);
2396 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) {
2405 /* Get hash algorithm */
2406 EXTRACT_STRING_ATTRIBUTE("algorithm", string
, 1);
2407 err
= string2isds_hash_algorithm((xmlChar
*) string
, &(*hash
)->algorithm
);
2409 if (err
== IE_ENUM
) {
2410 char *string_locale
= utf82locale(string
);
2411 isds_printf_message(context
, _("Unsported hash algorithm: %s"),
2413 free(string_locale
);
2419 /* Get hash value */
2420 EXTRACT_STRING(".", string
);
2422 isds_printf_message(context
, _("tHash element is missing hash value"));
2426 (*hash
)->length
= b64decode(string
, &((*hash
)->value
));
2427 if ((*hash
)->length
== (size_t) -1) {
2428 isds_printf_message(context
,
2429 _("Error while Base64-decoding hash value"));
2435 if (err
) isds_hash_free(hash
);
2437 xmlXPathFreeObject(result
);
2438 xpath_ctx
->node
= old_ctx_node
;
2443 /* Find and append isds:dmQTimestamp XML tree into envelope
2444 * @context is ISDS context
2445 * @envelope is automically allocated evnelope structure
2446 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
2448 * In case of error @envelope will be freed. */
2449 static isds_error
find_and_append_DmQTimestamp(struct isds_ctx
*context
,
2450 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
2451 isds_error err
= IE_SUCCESS
;
2452 xmlXPathObjectPtr result
= NULL
;
2453 char *string
= NULL
;
2455 if (!context
) return IE_INVALID_CONTEXT
;
2456 if (!envelope
) return IE_INVAL
;
2458 isds_envelope_free(envelope
);
2463 *envelope
= calloc(1, sizeof(**envelope
));
2469 zfree((*envelope
)->timestamp
);
2470 (*envelope
)->timestamp_length
= 0;
2473 /* Get dmQTimestamp */
2474 EXTRACT_STRING("sisds:dmQTimestamp", string
);
2476 isds_printf_message(context
, _("Missing dmQTimestamp element content"));
2480 (*envelope
)->timestamp_length
=
2481 b64decode(string
, &((*envelope
)->timestamp
));
2482 if ((*envelope
)->timestamp_length
== (size_t) -1) {
2483 isds_printf_message(context
,
2484 _("Error while Base64-decoding timestamp value"));
2490 if (err
) isds_envelope_free(envelope
);
2492 xmlXPathFreeObject(result
);
2497 /* Convert XSD tReturnedMessage XML tree into message structure.
2498 * It doea not store XML tree into message->raw.
2499 * @context is ISDS context
2500 * @include_documents Use true if documents must be extracted
2501 * (tReturnedMessage XSD type), use false if documents shall be ommited
2502 * (tReturnedMessageEnvelope).
2503 * @message is automically reallocated message structure
2504 * @xpath_ctx is XPath context with current node as tReturnedMessage element
2506 * In case of error @message will be freed. */
2507 static isds_error
extract_TReturnedMessage(struct isds_ctx
*context
,
2508 const _Bool include_documents
, struct isds_message
**message
,
2509 xmlXPathContextPtr xpath_ctx
) {
2510 isds_error err
= IE_SUCCESS
;
2511 xmlNodePtr message_node
;
2513 if (!context
) return IE_INVALID_CONTEXT
;
2514 if (!message
) return IE_INVAL
;
2515 isds_message_free(message
);
2516 if (!xpath_ctx
) return IE_INVAL
;
2519 *message
= calloc(1, sizeof(**message
));
2525 /* Save message XPATH context node */
2526 message_node
= xpath_ctx
->node
;
2530 err
= move_xpathctx_to_child(context
, BAD_CAST
"isds:dmDm", xpath_ctx
);
2531 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
2532 if (err
) { err
= IE_ERROR
; goto leave
; }
2533 err
= append_GMessageEnvelope(context
, &((*message
)->envelope
), xpath_ctx
);
2534 if (err
) goto leave
;
2536 if (include_documents
) {
2537 /* Extract dmFiles */
2538 err
= move_xpathctx_to_child(context
, BAD_CAST
"isds:dmFiles",
2540 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) {
2541 err
= IE_ISDS
; goto leave
;
2543 if (err
) { err
= IE_ERROR
; goto leave
; }
2544 err
= extract_documents(context
, &((*message
)->documents
), xpath_ctx
);
2545 if (err
) goto leave
;
2549 /* Restore context to message */
2550 xpath_ctx
->node
= message_node
;
2552 /* Extract dmHash */
2553 err
= find_and_extract_DmHash(context
, &(*message
)->envelope
->hash
,
2555 if (err
) goto leave
;
2557 /* Extract dmQTimestamp, */
2558 err
= find_and_append_DmQTimestamp(context
, &(*message
)->envelope
,
2560 if (err
) goto leave
;
2562 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
2563 * dmAcceptanceTime. */
2564 err
= append_status_size_times(context
, &((*message
)->envelope
), xpath_ctx
);
2565 if (err
) goto leave
;
2568 if (err
) isds_message_free(message
);
2573 /* Extract message event into reallocated isds_event structure
2574 * @context is ISDS context
2575 * @event is automically reallocated message event structure
2576 * @xpath_ctx is XPath context with current node as isds:dmEvent
2577 * In case of error @event will be freed. */
2578 static isds_error
extract_event(struct isds_ctx
*context
,
2579 struct isds_event
**event
, xmlXPathContextPtr xpath_ctx
) {
2580 isds_error err
= IE_SUCCESS
;
2581 xmlXPathObjectPtr result
= NULL
;
2582 xmlNodePtr event_node
= xpath_ctx
->node
;
2583 char *string
= NULL
;
2585 if (!context
) return IE_INVALID_CONTEXT
;
2586 if (!event
) return IE_INVAL
;
2587 isds_event_free(event
);
2588 if (!xpath_ctx
) return IE_INVAL
;
2590 *event
= calloc(1, sizeof(**event
));
2596 /* Extract event data.
2597 * All elements are optional according XSD. That's funny. */
2598 EXTRACT_STRING("sisds:dmEventTime", string
);
2600 err
= timestring2timeval((xmlChar
*) string
, &((*event
)->time
));
2602 char *string_locale
= utf82locale(string
);
2603 if (err
== IE_DATE
) err
= IE_ISDS
;
2604 isds_printf_message(context
,
2605 _("Could not convert dmEventTime as ISO time: %s"),
2607 free(string_locale
);
2613 /* dmEventDescr element has prefix and the rest */
2614 EXTRACT_STRING("sisds:dmEventDescr", string
);
2616 err
= eventstring2event((xmlChar
*) string
, *event
);
2617 if (err
) goto leave
;
2622 if (err
) isds_event_free(event
);
2624 xmlXPathFreeObject(result
);
2625 xpath_ctx
->node
= event_node
;
2630 /* Convert element of XSD tEventsArray type from XML tree into
2631 * isds_list of isds_event's structure. The list is automatically reallocated.
2632 * @context is ISDS context
2633 * @events is automically reallocated list of event structures
2634 * @xpath_ctx is XPath context with current node as tEventsArray
2635 * In case of error @evnets will be freed. */
2636 static isds_error
extract_events(struct isds_ctx
*context
,
2637 struct isds_list
**events
, xmlXPathContextPtr xpath_ctx
) {
2638 isds_error err
= IE_SUCCESS
;
2639 xmlXPathObjectPtr result
= NULL
;
2640 xmlNodePtr events_node
= xpath_ctx
->node
;
2641 struct isds_list
*event
, *prev_event
;
2643 if (!context
) return IE_INVALID_CONTEXT
;
2644 if (!events
) return IE_INVAL
;
2645 if (!xpath_ctx
) return IE_INVAL
;
2648 isds_list_free(events
);
2651 result
= xmlXPathEvalExpression(BAD_CAST
"sisds:dmEvent", xpath_ctx
);
2658 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
2659 isds_printf_message(context
,
2660 _("Delivery info does not contain any event"));
2666 /* Iterate over events */
2667 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
2669 /* Allocate and append list item */
2670 event
= calloc(1, sizeof(*event
));
2675 event
->destructor
= (void (*)(void **))isds_event_free
;
2676 if (i
== 0) *events
= event
;
2677 else prev_event
->next
= event
;
2681 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
2682 err
= extract_event(context
,
2683 (struct isds_event
**) &(event
->data
), xpath_ctx
);
2684 if (err
) goto leave
;
2689 if (err
) isds_list_free(events
);
2690 xmlXPathFreeObject(result
);
2691 xpath_ctx
->node
= events_node
;
2696 /* Convert isds_document structure into XML tree and append to dmFiles node.
2697 * @context is session context
2698 * @document is ISDS document
2699 * @dm_files is XML element the resulting tree will be appended to as a child.
2700 * @return error code, in case of error context' message is filled. */
2701 static isds_error
insert_document(struct isds_ctx
*context
,
2702 struct isds_document
*document
, xmlNodePtr dm_files
) {
2703 isds_error err
= IE_SUCCESS
;
2704 xmlNodePtr new_file
= NULL
, file
= NULL
, node
;
2705 xmlAttrPtr attribute_node
;
2706 xmlChar
*base64data
= NULL
;
2708 if (!context
) return IE_INVALID_CONTEXT
;
2709 if (!document
|| !dm_files
) return IE_INVAL
;
2711 /* Allocate new dmFile */
2712 new_file
= xmlNewNode(dm_files
->ns
, BAD_CAST
"dmFile");
2714 isds_printf_message(context
, _("Could not allocate main dmFile"));
2718 /* Append the new dmFile.
2719 * XXX: Main document must go first */
2720 if (document
->dmFileMetaType
== FILEMETATYPE_MAIN
&& dm_files
->children
)
2721 file
= xmlAddPrevSibling(dm_files
->children
, new_file
);
2723 file
= xmlAddChild(dm_files
, new_file
);
2726 xmlFreeNode(new_file
); new_file
= NULL
;
2727 isds_printf_message(context
, _("Could not add dmFile child to "
2728 "%s element"), dm_files
->name
);
2733 /* @dmMimeType is required */
2734 if (!document
->dmMimeType
) {
2735 isds_log_message(context
,
2736 _("Document is missing mandatory MIME type definition"));
2740 INSERT_STRING_ATTRIBUTE(file
, "dmMimeType", document
->dmMimeType
);
2742 const xmlChar
*string
= isds_FileMetaType2string(document
->dmFileMetaType
);
2744 isds_printf_message(context
,
2745 _("Document has unkown dmFileMetaType: %ld"),
2746 document
->dmFileMetaType
);
2750 INSERT_STRING_ATTRIBUTE(file
, "dmFileMetaType", string
);
2752 if (document
->dmFileGuid
) {
2753 INSERT_STRING_ATTRIBUTE(file
, "dmFileGuid", document
->dmFileGuid
);
2755 if (document
->dmUpFileGuid
) {
2756 INSERT_STRING_ATTRIBUTE(file
, "dmUpFileGuid", document
->dmUpFileGuid
);
2759 /* @dmFileDescr is required */
2760 if (!document
->dmFileDescr
) {
2761 isds_log_message(context
,
2762 _("Document is missing mandatory description (title)"));
2766 INSERT_STRING_ATTRIBUTE(file
, "dmFileDescr", document
->dmFileDescr
);
2768 if (document
->dmFormat
) {
2769 INSERT_STRING_ATTRIBUTE(file
, "dmFormat", document
->dmFormat
);
2773 /* Insert content (data) of the document. */
2774 /* XXX; Only base64 is implemented currently. */
2775 base64data
= (xmlChar
*) b64encode(document
->data
, document
->data_length
);
2777 isds_printf_message(context
,
2778 _("Not enought memory to encode %zd bytes into Base64"),
2779 document
->data_length
);
2783 INSERT_STRING(file
, "dmEncodedContent", base64data
);
2791 /* Get data about logged in user and his box. */
2792 isds_error
isds_GetOwnerInfoFromLogin(struct isds_ctx
*context
,
2793 struct isds_DbOwnerInfo
**db_owner_info
) {
2794 isds_error err
= IE_SUCCESS
;
2795 xmlNsPtr isds_ns
= NULL
;
2796 xmlNodePtr request
= NULL
;
2797 xmlDocPtr response
= NULL
;
2798 xmlChar
*code
= NULL
, *message
= NULL
;
2800 xmlXPathContextPtr xpath_ctx
= NULL
;
2801 xmlXPathObjectPtr result
= NULL
;
2802 char *string
= NULL
;
2804 if (!context
) return IE_INVALID_CONTEXT
;
2805 if (!db_owner_info
) return IE_INVAL
;
2807 /* Check if connection is established */
2808 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
2811 /* Build GetOwnerInfoFromLogin request */
2812 request
= xmlNewNode(NULL
, BAD_CAST
"GetOwnerInfoFromLogin");
2814 isds_log_message(context
,
2815 _("Could build GetOwnerInfoFromLogin request"));
2818 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
2820 isds_log_message(context
, _("Could not create ISDS name space"));
2821 xmlFreeNode(request
);
2824 xmlSetNs(request
, isds_ns
);
2825 node
= xmlNewChild(request
, NULL
, BAD_CAST
"dbDummy", NULL
);
2827 isds_log_message(context
, _("Could nod add dbDummy Child to "
2828 "GetOwnerInfoFromLogin element"));
2829 xmlFreeNode(request
);
2834 isds_log(ILF_ISDS
, ILL_DEBUG
,
2835 _("Sending GetOwnerInfoFromLogin request to ISDS\n"));
2838 err
= isds(context
, SERVICE_DB_SUPPLEMENTARY
, request
, &response
);
2840 /* Destroy request */
2841 xmlFreeNode(request
);
2844 isds_log(ILF_ISDS
, ILL_DEBUG
,
2845 _("Processing ISDS response on GetOwnerInfoFromLogin "
2846 "request failed\n"));
2847 xmlFreeDoc(response
);
2851 /* Check for response status */
2852 err
= isds_response_status(context
, SERVICE_DB_SUPPLEMENTARY
, response
,
2853 &code
, &message
, NULL
);
2855 isds_log(ILF_ISDS
, ILL_DEBUG
,
2856 _("ISDS response on GetOwnerInfoFromLogin request is "
2857 "missing status\n"));
2860 xmlFreeDoc(response
);
2863 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
2864 char *code_locale
= utf82locale((char*)code
);
2865 char *message_locale
= utf82locale((char*)message
);
2866 isds_log(ILF_ISDS
, ILL_DEBUG
,
2867 _("Server refused GetOwnerInfoFromLogin request "
2868 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
2869 isds_log_message(context
, message_locale
);
2871 free(message_locale
);
2874 xmlFreeDoc(response
);
2879 /* Prepare stucture */
2880 isds_DbOwnerInfo_free(db_owner_info
);
2881 *db_owner_info
= calloc(1, sizeof(**db_owner_info
));
2882 if (!*db_owner_info
) {
2886 xpath_ctx
= xmlXPathNewContext(response
);
2891 if (register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
2896 /* Set context node */
2897 result
= xmlXPathEvalExpression(BAD_CAST
2898 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx
);
2903 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
2904 isds_log_message(context
, _("Missing dbOwnerInfo element"));
2908 if (result
->nodesetval
->nodeNr
> 1) {
2909 isds_log_message(context
, _("Multiple dbOwnerInfo element"));
2913 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
2914 xmlXPathFreeObject(result
); result
= NULL
;
2917 err
= extract_DbOwnerInfo(context
, db_owner_info
, xpath_ctx
);
2921 isds_DbOwnerInfo_free(db_owner_info
);
2925 xmlXPathFreeObject(result
);
2926 xmlXPathFreeContext(xpath_ctx
);
2930 xmlFreeDoc(response
);
2933 isds_log(ILF_ISDS
, ILL_DEBUG
,
2934 _("GetOwnerInfoFromLogin request processed by server "
2935 "successfully.\n"));
2941 /* Find boxes suiting given criteria.
2942 * @criteria is filter. You should fill in at least some memebers.
2943 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
2944 * possibly empty. Input NULL or valid old structure.
2946 * IE_SUCCESS if search sucseeded, @boxes contains usefull data
2947 * IE_NOEXIST if no such box exists, @boxes will be NULL
2948 * IE_2BIG if too much boxes exist and server truncated the resuluts, @boxes
2949 * contains still valid data
2950 * other code if something bad happens. @boxes will be NULL. */
2951 isds_error
isds_FindDataBox(struct isds_ctx
*context
,
2952 const struct isds_DbOwnerInfo
*criteria
,
2953 struct isds_list
**boxes
) {
2954 isds_error err
= IE_SUCCESS
;
2955 _Bool truncated
= 0;
2956 xmlNsPtr isds_ns
= NULL
;
2957 xmlNodePtr request
= NULL
;
2958 xmlDocPtr response
= NULL
;
2959 xmlChar
*code
= NULL
, *message
= NULL
;
2960 xmlNodePtr db_owner_info
, node
;
2961 xmlXPathContextPtr xpath_ctx
= NULL
;
2962 xmlXPathObjectPtr result
= NULL
;
2963 xmlChar
*string
= NULL
;
2966 if (!context
) return IE_INVALID_CONTEXT
;
2967 if (!boxes
) return IE_INVAL
;
2968 isds_list_free(boxes
);
2974 /* Check if connection is established
2975 * TODO: This check should be done donwstairs. */
2976 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
2979 /* Build FindDataBox request */
2980 request
= xmlNewNode(NULL
, BAD_CAST
"FindDataBox");
2982 isds_log_message(context
,
2983 _("Could build FindDataBox request"));
2986 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
2988 isds_log_message(context
, _("Could not create ISDS name space"));
2989 xmlFreeNode(request
);
2992 xmlSetNs(request
, isds_ns
);
2993 db_owner_info
= xmlNewChild(request
, NULL
, BAD_CAST
"dbOwnerInfo", NULL
);
2994 if (!db_owner_info
) {
2995 isds_log_message(context
, _("Could not add dbOwnerInfo Child to "
2996 "FindDataBox element"));
2997 xmlFreeNode(request
);
3002 INSERT_STRING(db_owner_info
, "dbID", criteria
->dbID
);
3005 if (criteria
->dbType
) {
3006 const xmlChar
*type_string
= isds_DbType2string(*(criteria
->dbType
));
3008 isds_printf_message(context
, _("Invalid dbType value: %d"),
3009 *(criteria
->dbType
));
3013 INSERT_STRING(db_owner_info
, "dbType", type_string
);
3016 INSERT_STRING(db_owner_info
, "firmName", criteria
->firmName
);
3017 INSERT_STRING(db_owner_info
, "ic", criteria
->ic
);
3018 if (criteria
->personName
) {
3019 INSERT_STRING(db_owner_info
, "pnFirstName",
3020 criteria
->personName
->pnFirstName
);
3021 INSERT_STRING(db_owner_info
, "pnMiddleName",
3022 criteria
->personName
->pnMiddleName
);
3023 INSERT_STRING(db_owner_info
, "pnLastName",
3024 criteria
->personName
->pnLastName
);
3025 INSERT_STRING(db_owner_info
, "pnLastNameAtBirth",
3026 criteria
->personName
->pnLastNameAtBirth
);
3028 if (criteria
->birthInfo
) {
3029 if (criteria
->birthInfo
->biDate
) {
3030 if (!tm2datestring(criteria
->birthInfo
->biDate
, &string
))
3031 INSERT_STRING(db_owner_info
, "biDate", string
);
3032 free(string
); string
= NULL
;
3034 INSERT_STRING(db_owner_info
, "biCity", criteria
->birthInfo
->biCity
);
3035 INSERT_STRING(db_owner_info
, "biCounty", criteria
->birthInfo
->biCounty
);
3036 INSERT_STRING(db_owner_info
, "biState", criteria
->birthInfo
->biState
);
3038 if (criteria
->address
) {
3039 INSERT_STRING(db_owner_info
, "adCity", criteria
->address
->adCity
);
3040 INSERT_STRING(db_owner_info
, "adStreet", criteria
->address
->adStreet
);
3041 INSERT_STRING(db_owner_info
, "adNumberInStreet",
3042 criteria
->address
->adNumberInStreet
);
3043 INSERT_STRING(db_owner_info
, "adNumberInMunicipality",
3044 criteria
->address
->adNumberInMunicipality
);
3045 INSERT_STRING(db_owner_info
, "adZipCode", criteria
->address
->adZipCode
);
3046 INSERT_STRING(db_owner_info
, "adState", criteria
->address
->adState
);
3048 INSERT_STRING(db_owner_info
, "nationality", criteria
->nationality
);
3049 INSERT_STRING(db_owner_info
, "email", criteria
->email
);
3050 INSERT_STRING(db_owner_info
, "telNumber", criteria
->telNumber
);
3051 INSERT_STRING(db_owner_info
, "identifier", criteria
->identifier
);
3052 INSERT_STRING(db_owner_info
, "registryCode", criteria
->registryCode
);
3054 INSERT_LONGINT(db_owner_info
, "dbState", criteria
->dbState
, string
);
3056 INSERT_BOOLEAN(db_owner_info
, "dbEffectiveOVM", criteria
->dbEffectiveOVM
);
3057 INSERT_BOOLEAN(db_owner_info
, "dbOpenAddressing",
3058 criteria
->dbOpenAddressing
);
3061 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending FindDataBox request to ISDS\n"));
3064 err
= isds(context
, SERVICE_DB_SEARCH
, request
, &response
);
3066 /* Destroy request */
3067 xmlFreeNode(request
); request
= NULL
;
3070 isds_log(ILF_ISDS
, ILL_DEBUG
,
3071 _("Processing ISDS response on FindDataBox "
3072 "request failed\n"));
3076 /* Check for response status */
3077 err
= isds_response_status(context
, SERVICE_DB_SEARCH
, response
,
3078 &code
, &message
, NULL
);
3080 isds_log(ILF_ISDS
, ILL_DEBUG
,
3081 _("ISDS response on FindDataBox request is missing status\n"));
3085 /* Request processed, but nothing found */
3086 if (!xmlStrcmp(code
, BAD_CAST
"0002") ||
3087 !xmlStrcmp(code
, BAD_CAST
"5001")) {
3088 char *code_locale
= utf82locale((char*)code
);
3089 char *message_locale
= utf82locale((char*)message
);
3090 isds_log(ILF_ISDS
, ILL_DEBUG
,
3091 _("Server did not found any box on FindDataBox request "
3092 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
3093 isds_log_message(context
, message_locale
);
3095 free(message_locale
);
3100 /* Warning, not a error */
3101 if (!xmlStrcmp(code
, BAD_CAST
"0003")) {
3102 char *code_locale
= utf82locale((char*)code
);
3103 char *message_locale
= utf82locale((char*)message
);
3104 isds_log(ILF_ISDS
, ILL_DEBUG
,
3105 _("Server truncated response on FindDataBox request "
3106 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
3107 isds_log_message(context
, message_locale
);
3109 free(message_locale
);
3114 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
3115 char *code_locale
= utf82locale((char*)code
);
3116 char *message_locale
= utf82locale((char*)message
);
3117 isds_log(ILF_ISDS
, ILL_DEBUG
,
3118 _("Server refused FindDataBox request "
3119 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
3120 isds_log_message(context
, message_locale
);
3122 free(message_locale
);
3127 xpath_ctx
= xmlXPathNewContext(response
);
3132 if (register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
3137 /* Extract boxes if they present */
3138 result
= xmlXPathEvalExpression(BAD_CAST
3139 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
3145 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
3146 struct isds_list
*item
, *prev_item
= NULL
;
3147 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
3148 item
= calloc(1, sizeof(*item
));
3154 item
->destructor
= (void (*)(void **))isds_DbOwnerInfo_free
;
3155 if (i
== 0) *boxes
= item
;
3156 else prev_item
->next
= item
;
3159 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
3160 err
= extract_DbOwnerInfo(context
,
3161 (struct isds_DbOwnerInfo
**) &(item
->data
), xpath_ctx
);
3162 if (err
) goto leave
;
3168 isds_list_free(boxes
);
3170 if (truncated
) err
= IE_2BIG
;
3174 xmlFreeNode(request
);
3175 xmlXPathFreeObject(result
);
3176 xmlXPathFreeContext(xpath_ctx
);
3180 xmlFreeDoc(response
);
3183 isds_log(ILF_ISDS
, ILL_DEBUG
,
3184 _("FindDataBox request processed by server successfully.\n"));
3190 /* Get status of a box.
3191 * @context is ISDS session context.
3192 * @box_id is UTF-8 encoded box identifier as zero terminated string
3193 * @box_status is return value of box status.
3195 * IE_SUCCESS if box has been found and its status retrieved
3196 * IE_NOEXIST if box is not known to ISDS server
3197 * or other appropriate error.
3198 * You can use isds_DbState to enumerate box status. However out of enum
3199 * range value can be returned too. This is feature because ISDS
3200 * specification leaves the set of values open.
3201 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
3202 * the box has been deleted, but ISDS still lists its former existence. */
3203 isds_error
isds_CheckDataBox(struct isds_ctx
*context
, const char *box_id
,
3204 long int *box_status
) {
3205 isds_error err
= IE_SUCCESS
;
3206 xmlNsPtr isds_ns
= NULL
;
3207 xmlNodePtr request
= NULL
, db_id
;
3208 xmlDocPtr response
= NULL
;
3209 xmlChar
*code
= NULL
, *message
= NULL
;
3210 xmlXPathContextPtr xpath_ctx
= NULL
;
3211 xmlXPathObjectPtr result
= NULL
;
3212 xmlChar
*string
= NULL
;
3214 if (!context
) return IE_INVALID_CONTEXT
;
3215 if (!box_status
|| !box_id
|| *box_id
== '\0') return IE_INVAL
;
3217 /* Check if connection is established
3218 * TODO: This check should be done donwstairs. */
3219 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
3222 /* Build CheckDataBox request */
3223 request
= xmlNewNode(NULL
, BAD_CAST
"CheckDataBox");
3225 isds_log_message(context
,
3226 _("Could build CheckDataBox request"));
3229 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
3231 isds_log_message(context
, _("Could not create ISDS name space"));
3232 xmlFreeNode(request
);
3235 xmlSetNs(request
, isds_ns
);
3236 db_id
= xmlNewTextChild(request
, NULL
, BAD_CAST
"dbID", (xmlChar
*) box_id
);
3238 isds_log_message(context
, _("Could not add dbId Child to "
3239 "CheckDataBox element"));
3240 xmlFreeNode(request
);
3245 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending CheckDataBox request to ISDS\n"));
3248 err
= isds(context
, SERVICE_DB_SEARCH
, request
, &response
);
3250 /* Destroy request */
3251 xmlFreeNode(request
);
3254 isds_log(ILF_ISDS
, ILL_DEBUG
,
3255 _("Processing ISDS response on CheckDataBox "
3256 "request failed\n"));
3260 /* Check for response status */
3261 err
= isds_response_status(context
, SERVICE_DB_SEARCH
, response
,
3262 &code
, &message
, NULL
);
3264 isds_log(ILF_ISDS
, ILL_DEBUG
,
3265 _("ISDS response on CheckDataBox request is missing status\n"));
3269 /* Request processed, but nothing found */
3270 if (!xmlStrcmp(code
, BAD_CAST
"5001")) {
3271 char *box_id_locale
= utf82locale((char*)box_id
);
3272 char *code_locale
= utf82locale((char*)code
);
3273 char *message_locale
= utf82locale((char*)message
);
3274 isds_log(ILF_ISDS
, ILL_DEBUG
,
3275 _("Server did not found box %s on CheckDataBox request "
3276 "(code=%s, message=%s)\n"),
3277 box_id_locale
, code_locale
, message_locale
);
3278 isds_log_message(context
, message_locale
);
3279 free(box_id_locale
);
3281 free(message_locale
);
3287 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
3288 char *code_locale
= utf82locale((char*)code
);
3289 char *message_locale
= utf82locale((char*)message
);
3290 isds_log(ILF_ISDS
, ILL_DEBUG
,
3291 _("Server refused CheckDataBox request "
3292 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
3293 isds_log_message(context
, message_locale
);
3295 free(message_locale
);
3301 xpath_ctx
= xmlXPathNewContext(response
);
3306 if (register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
3310 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CheckDataBoxResponse",
3316 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
3317 isds_log_message(context
, _("Missing CheckDataBoxResponse element"));
3321 if (result
->nodesetval
->nodeNr
> 1) {
3322 isds_log_message(context
, _("Multiple CheckDataBoxResponse element"));
3326 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
3327 xmlXPathFreeObject(result
); result
= NULL
;
3329 EXTRACT_LONGINT("isds:dbState", box_status
, 1);
3334 xmlXPathFreeObject(result
);
3335 xmlXPathFreeContext(xpath_ctx
);
3339 xmlFreeDoc(response
);
3342 isds_log(ILF_ISDS
, ILL_DEBUG
,
3343 _("CheckDataBox request processed by server successfully.\n"));
3349 /* Send a message via ISDS to a recipent
3350 * @context is session context
3351 * @outgoing_message is message to send; Some memebers are mandatory (like
3352 * dbIDRecipient), some are optional and some are irrelevant (especialy data
3353 * about sender). Included pointer to isds_list documents must contain at
3354 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
3355 * members will be filled with valid data from ISDS. Exact list of write
3356 * members is subject to change. Currently dmId is changed.
3357 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
3358 isds_error
isds_send_message(struct isds_ctx
*context
,
3359 struct isds_message
*outgoing_message
) {
3361 isds_error err
= IE_SUCCESS
;
3362 xmlNsPtr isds_ns
= NULL
;
3363 xmlNodePtr request
= NULL
, envelope
, dm_files
, node
;
3364 xmlDocPtr response
= NULL
;
3365 xmlChar
*code
= NULL
, *message
= NULL
;
3366 xmlXPathContextPtr xpath_ctx
= NULL
;
3367 xmlXPathObjectPtr result
= NULL
;
3368 xmlChar
*string
= NULL
;
3369 _Bool message_is_complete
= 0;
3371 if (!context
) return IE_INVALID_CONTEXT
;
3372 if (!outgoing_message
) return IE_INVAL
;
3374 /* Check if connection is established
3375 * TODO: This check should be done donwstairs. */
3376 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
3379 /* Build CreateMessage request */
3380 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMessage");
3382 isds_log_message(context
,
3383 _("Could build CreateMessage request"));
3386 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
3388 isds_log_message(context
, _("Could not create ISDS name space"));
3389 xmlFreeNode(request
);
3392 xmlSetNs(request
, isds_ns
);
3395 /* Build envelope */
3396 envelope
= xmlNewChild(request
, NULL
, BAD_CAST
"dmEnvelope", NULL
);
3398 isds_log_message(context
, _("Could not add dmEnvelope child to "
3399 "CreateMessage element"));
3400 xmlFreeNode(request
);
3404 if (!outgoing_message
->envelope
) {
3405 isds_log_message(context
, _("outgoing message is missing envelope"));
3410 INSERT_STRING(envelope
, "dmSenderOrgUnit",
3411 outgoing_message
->envelope
->dmSenderOrgUnit
);
3412 INSERT_LONGINT(envelope
, "dmSenderOrgUnitNum",
3413 outgoing_message
->envelope
->dmSenderOrgUnitNum
, string
);
3415 if (!outgoing_message
->envelope
->dbIDRecipient
) {
3416 isds_log_message(context
,
3417 _("outgoing message is missing recipient box identifier"));
3421 INSERT_STRING(envelope
, "dbIDRecipient",
3422 outgoing_message
->envelope
->dbIDRecipient
);
3424 INSERT_STRING(envelope
, "dmRecipientOrgUnit",
3425 outgoing_message
->envelope
->dmRecipientOrgUnit
);
3426 INSERT_LONGINT(envelope
, "dmRecipientOrgUnitNum",
3427 outgoing_message
->envelope
->dmRecipientOrgUnitNum
, string
);
3428 INSERT_STRING(envelope
, "dmToHands", outgoing_message
->envelope
->dmToHands
);
3430 #define CHECK_FOR_STRING_LENGTH(string, limit, name) \
3431 if ((string) && xmlUTF8Strlen((xmlChar *) (string)) > (limit)) { \
3432 isds_printf_message(context, \
3433 _("%s has more than %d characters"), (name), (limit)); \
3438 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmAnnotation
, 255,
3440 INSERT_STRING(envelope
, "dmAnnotation",
3441 outgoing_message
->envelope
->dmAnnotation
);
3443 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientRefNumber
,
3444 50, "dmRecipientRefNumber");
3445 INSERT_STRING(envelope
, "dmRecipientRefNumber",
3446 outgoing_message
->envelope
->dmRecipientRefNumber
);
3448 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderRefNumber
,
3449 50, "dmSenderRefNumber");
3450 INSERT_STRING(envelope
, "dmSenderRefNumber",
3451 outgoing_message
->envelope
->dmSenderRefNumber
);
3453 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientIdent
,
3454 50, "dmRecipientIdent");
3455 INSERT_STRING(envelope
, "dmRecipientIdent",
3456 outgoing_message
->envelope
->dmRecipientIdent
);
3458 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderIdent
,
3459 50, "dmSenderIdent");
3460 INSERT_STRING(envelope
, "dmSenderIdent",
3461 outgoing_message
->envelope
->dmSenderIdent
);
3463 INSERT_LONGINT(envelope
, "dmLegalTitleLaw",
3464 outgoing_message
->envelope
->dmLegalTitleLaw
, string
);
3465 INSERT_LONGINT(envelope
, "dmLegalTitleYear",
3466 outgoing_message
->envelope
->dmLegalTitleYear
, string
);
3467 INSERT_STRING(envelope
, "dmLegalTitleSect",
3468 outgoing_message
->envelope
->dmLegalTitleSect
);
3469 INSERT_STRING(envelope
, "dmLegalTitlePar",
3470 outgoing_message
->envelope
->dmLegalTitlePar
);
3471 INSERT_STRING(envelope
, "dmLegalTitlePoint",
3472 outgoing_message
->envelope
->dmLegalTitlePoint
);
3474 INSERT_BOOLEAN(envelope
, "dmPersonalDelivery",
3475 outgoing_message
->envelope
->dmPersonalDelivery
);
3476 INSERT_BOOLEAN(envelope
, "dmAllowSubstDelivery",
3477 outgoing_message
->envelope
->dmAllowSubstDelivery
);
3479 #undef CHECK_FOR_STRING_LENGTH
3481 /* ???: Should we require value for dbEffectiveOVM sender?
3482 * ISDS has default as true */
3483 INSERT_BOOLEAN(envelope
, "dmOVM", outgoing_message
->envelope
->dmOVM
);
3486 /* Append dmFiles */
3487 if (!outgoing_message
->documents
) {
3488 isds_log_message(context
,
3489 _("outgoing message is missing list of documents"));
3493 dm_files
= xmlNewChild(request
, NULL
, BAD_CAST
"dmFiles", NULL
);
3495 isds_log_message(context
, _("Could not add dmFiles child to "
3496 "CreateMessage element"));
3501 /* Check for document hieararchy */
3502 err
= check_documents_hierarchy(context
, outgoing_message
->documents
);
3503 if (err
) goto leave
;
3505 /* Process each document */
3506 for (struct isds_list
*item
=
3507 (struct isds_list
*) outgoing_message
->documents
;
3508 item
; item
= item
->next
) {
3510 isds_log_message(context
,
3511 _("list of documents contains empty item"));
3515 /* FIXME: Check for dmFileMetaType and for document references.
3516 * Only first document can be of MAIN type */
3517 err
= insert_document(context
, (struct isds_document
*) item
->data
,
3520 if (err
) goto leave
;
3523 /* Signal we can serilize message since now */
3524 message_is_complete
= 1;
3528 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending CreateMessage request to ISDS\n"));
3531 err
= isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
);
3533 /* Dont' destroy request, we want to privode it to application later */
3536 isds_log(ILF_ISDS
, ILL_DEBUG
,
3537 _("Processing ISDS response on CreateMessage "
3538 "request failed\n"));
3542 /* Check for response status */
3543 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
3544 &code
, &message
, NULL
);
3546 isds_log(ILF_ISDS
, ILL_DEBUG
,
3547 _("ISDS response on CreateMessage request "
3548 "is missing status\n"));
3552 /* Request processed, but nothing found */
3553 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
3554 char *box_id_locale
=
3555 utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
3556 char *code_locale
= utf82locale((char*)code
);
3557 char *message_locale
= utf82locale((char*)message
);
3558 isds_log(ILF_ISDS
, ILL_DEBUG
,
3559 _("Server did not accept message for %s on CreateMessage "
3560 "request (code=%s, message=%s)\n"),
3561 box_id_locale
, code_locale
, message_locale
);
3562 isds_log_message(context
, message_locale
);
3563 free(box_id_locale
);
3565 free(message_locale
);
3572 xpath_ctx
= xmlXPathNewContext(response
);
3577 if (register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
3581 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CreateMessageResponse",
3587 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
3588 isds_log_message(context
, _("Missing CreateMessageResponse element"));
3592 if (result
->nodesetval
->nodeNr
> 1) {
3593 isds_log_message(context
, _("Multiple CreateMessageResponse element"));
3597 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
3598 xmlXPathFreeObject(result
); result
= NULL
;
3600 if (outgoing_message
->envelope
->dmID
) {
3601 free(outgoing_message
->envelope
->dmID
);
3602 outgoing_message
->envelope
->dmID
= NULL
;
3604 EXTRACT_STRING("isds:dmID", outgoing_message
->envelope
->dmID
);
3605 if (!outgoing_message
->envelope
->dmID
) {
3606 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted sent message, "
3607 "but did not returen assigned message ID\n"));
3611 /* TODO: Serialize message into structure member raw */
3612 /* XXX: Each web service transport message in different format.
3613 * Therefore it's not possible to save them directly.
3614 * To save them, one must figure out common format.
3615 * We can leave it on application, or we can implement the ESS format. */
3616 /*if (message_is_complete) {
3617 if (outgoing_message->envelope->dmID) {
3619 /* Add assigned message ID as first child*/
3620 /*xmlNodePtr dmid_text = xmlNewText(
3621 (xmlChar *) outgoing_message->envelope->dmID);
3622 if (!dmid_text) goto serialization_failed;
3624 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
3626 if (!dmid_element) {
3627 xmlFreeNode(dmid_text);
3628 goto serialization_failed;
3631 xmlNodePtr dmid_element_with_text =
3632 xmlAddChild(dmid_element, dmid_text);
3633 if (!dmid_element_with_text) {
3634 xmlFreeNode(dmid_element);
3635 xmlFreeNode(dmid_text);
3636 goto serialization_failed;
3639 node = xmlAddPrevSibling(envelope->childern,
3640 dmid_element_with_text);
3642 xmlFreeNodeList(dmid_element_with_text);
3643 goto serialization_failed;
3647 /* Serialize message with ID into raw */
3648 /*buffer = serialize_element(envelope)*/
3651 serialization_failed:
3658 xmlXPathFreeObject(result
);
3659 xmlXPathFreeContext(xpath_ctx
);
3663 xmlFreeDoc(response
);
3664 xmlFreeNode(request
);
3667 isds_log(ILF_ISDS
, ILL_DEBUG
,
3668 _("CreateMessage request processed by server "
3669 "successfully.\n"));
3675 /* Get list of messages. This is common core for getting sent or received
3677 * Any criterion argument can be NULL, if you don't care about it.
3678 * @context is session context. Must not be NULL.
3679 * @outgoing_direction is true if you want list of outgoing messages,
3680 * it's false if you want incoming messages.
3681 * @from_time is minimal time and date of message sending inclusive.
3682 * @to_time is maximal time and date of message sending inclusive
3683 * @organization_unit_number is number of sender/recipient respectively.
3684 * @status_filter is bit field of isds_message_status values. Use special
3685 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
3686 * all values, you can use bitwise arithmetic if you want.)
3687 * @offset is index of first message we are interested in. First message is 1.
3688 * Set to 0 (or 1) if you don't care.
3689 * @number is maximal length of list you want to get as input value, outputs
3690 * number of messages matching these criteria. Can be NULL if you don't care
3691 * (applies to output value either).
3692 * @messages is automatically reallocated list of isds_message's. Be ware that
3693 * it returns only brief overview (envelope and some other fields) about each
3694 * message, not the complete message. FIXME: Specify exact fields.
3695 * The list is sorted by delivery time in ascending order.
3697 * you don't care about don't need the data (useful if you want to know only
3698 * the @number). If you provide &NULL, list will be allocated on heap, if you
3699 * provide pointer to non-NULL, list will be freed automacally at first. Also
3700 * in case of error the list will be NULLed.
3701 * @return IE_SUCCESS or appropriate error code. */
3702 static isds_error
isds_get_list_of_messages(struct isds_ctx
*context
,
3703 _Bool outgoing_direction
,
3704 const struct timeval
*from_time
, const struct timeval
*to_time
,
3705 const long int *organization_unit_number
,
3706 const unsigned int status_filter
,
3707 const unsigned long int offset
, unsigned long int *number
,
3708 struct isds_list
**messages
) {
3710 isds_error err
= IE_SUCCESS
;
3711 xmlNsPtr isds_ns
= NULL
;
3712 xmlNodePtr request
= NULL
, node
;
3713 xmlDocPtr response
= NULL
;
3714 xmlChar
*code
= NULL
, *message
= NULL
;
3715 xmlXPathContextPtr xpath_ctx
= NULL
;
3716 xmlXPathObjectPtr result
= NULL
;
3717 xmlChar
*string
= NULL
;
3718 long unsigned int count
= 0;
3720 if (!context
) return IE_INVALID_CONTEXT
;
3722 /* Free former message list if any */
3723 if (messages
) isds_list_free(messages
);
3725 /* Check if connection is established
3726 * TODO: This check should be done donwstairs. */
3727 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
3729 /* Build GetListOf*Messages request */
3730 request
= xmlNewNode(NULL
,
3731 (outgoing_direction
) ?
3732 BAD_CAST
"GetListOfSentMessages" :
3733 BAD_CAST
"GetListOfReceivedMessages"
3736 isds_log_message(context
,
3737 (outgoing_direction
) ?
3738 _("Could not build GetListOfSentMessages request") :
3739 _("Could not build GetListOfReceivedMessages request")
3743 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
3745 isds_log_message(context
, _("Could not create ISDS name space"));
3746 xmlFreeNode(request
);
3749 xmlSetNs(request
, isds_ns
);
3753 err
= timeval2timestring(from_time
, &string
);
3754 if (err
) goto leave
;
3756 INSERT_STRING(request
, "dmFromTime", string
);
3757 free(string
); string
= NULL
;
3760 err
= timeval2timestring(to_time
, &string
);
3761 if (err
) goto leave
;
3763 INSERT_STRING(request
, "dmToTime", string
);
3764 free(string
); string
= NULL
;
3766 if (outgoing_direction
) {
3767 INSERT_LONGINT(request
, "dmSenderOrgUnitNum",
3768 organization_unit_number
, string
);
3770 INSERT_LONGINT(request
, "dmRecipientOrgUnitNum",
3771 organization_unit_number
, string
);
3774 if (status_filter
> MESSAGESTATE_ANY
) {
3775 isds_printf_message(context
,
3776 _("Invalid message state filter value: %ld"), status_filter
);
3780 INSERT_ULONGINTNOPTR(request
, "dmStatusFilter", status_filter
, string
);
3783 INSERT_ULONGINTNOPTR(request
, "dmOffset", offset
, string
);
3785 INSERT_STRING(request
, "dmOffset", "1");
3788 /* number 0 means no limit */
3789 if (number
&& *number
== 0) {
3790 INSERT_STRING(request
, "dmLimit", NULL
);
3792 INSERT_ULONGINT(request
, "dmLimit", number
, string
);
3796 isds_log(ILF_ISDS
, ILL_DEBUG
,
3797 (outgoing_direction
) ?
3798 _("Sending GetListOfSentMessages request to ISDS\n") :
3799 _("Sending GetListOfReceivedMessages request to ISDS\n")
3803 err
= isds(context
, SERVICE_DM_INFO
, request
, &response
);
3804 xmlFreeNode(request
); request
= NULL
;
3807 isds_log(ILF_ISDS
, ILL_DEBUG
,
3808 (outgoing_direction
) ?
3809 _("Processing ISDS response on GetListOfSentMessages "
3810 "request failed\n") :
3811 _("Processing ISDS response on GetListOfReceivedMessages "
3817 /* Check for response status */
3818 err
= isds_response_status(context
, SERVICE_DM_INFO
, response
,
3819 &code
, &message
, NULL
);
3821 isds_log(ILF_ISDS
, ILL_DEBUG
,
3822 (outgoing_direction
) ?
3823 _("ISDS response on GetListOfSentMessages request "
3824 "is missing status\n") :
3825 _("ISDS response on GetListOfReceivedMessages request "
3826 "is missing status\n")
3831 /* Request processed, but nothing found */
3832 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
3833 char *code_locale
= utf82locale((char*)code
);
3834 char *message_locale
= utf82locale((char*)message
);
3835 isds_log(ILF_ISDS
, ILL_DEBUG
,
3836 (outgoing_direction
) ?
3837 _("Server refused GetListOfSentMessages request "
3838 "(code=%s, message=%s)\n") :
3839 _("Server refused GetListOfReceivedMessages request "
3840 "(code=%s, message=%s)\n"),
3841 code_locale
, message_locale
);
3842 isds_log_message(context
, message_locale
);
3844 free(message_locale
);
3851 xpath_ctx
= xmlXPathNewContext(response
);
3856 if (register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
3860 result
= xmlXPathEvalExpression(
3861 (outgoing_direction
) ?
3862 BAD_CAST
"/isds:GetListOfSentMessagesResponse/"
3863 "isds:dmRecords/isds:dmRecord" :
3864 BAD_CAST
"/isds:GetListOfReceivedMessagesResponse/"
3865 "isds:dmRecords/isds:dmRecord",
3872 /* Fill output arguments in */
3873 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
3874 struct isds_envelope
*envelope
;
3875 struct isds_list
*item
= NULL
, *last_item
= NULL
;
3877 for (count
= 0; count
< result
->nodesetval
->nodeNr
; count
++) {
3878 /* Create new message */
3879 item
= calloc(1, sizeof(*item
));
3884 item
->destructor
= (void(*)(void**)) &isds_message_free
;
3885 item
->data
= calloc(1, sizeof(struct isds_message
));
3887 isds_list_free(&item
);
3892 /* Extract envelope data */
3893 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[count
];
3895 err
= extract_DmRecord(context
, &envelope
, xpath_ctx
);
3897 isds_list_free(&item
);
3901 /* Attach extracted envelope */
3902 ((struct isds_message
*) item
->data
)->envelope
= envelope
;
3904 /* Append new message into the list */
3906 *messages
= last_item
= item
;
3908 last_item
->next
= item
;
3913 if (number
) *number
= count
;
3917 isds_list_free(messages
);
3921 xmlXPathFreeObject(result
);
3922 xmlXPathFreeContext(xpath_ctx
);
3926 xmlFreeDoc(response
);
3927 xmlFreeNode(request
);
3930 isds_log(ILF_ISDS
, ILL_DEBUG
,
3931 (outgoing_direction
) ?
3932 _("GetListOfSentMessages request processed by server "
3933 "successfully.\n") :
3934 _("GetListOfReceivedMessages request processed by server "
3941 /* Get list of outgoing (already sent) messages.
3942 * Any criterion argument can be NULL, if you don't care about it.
3943 * @context is session context. Must not be NULL.
3944 * @from_time is minimal time and date of message sending inclusive.
3945 * @to_time is maximal time and date of message sending inclusive
3946 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
3947 * @status_filter is bit field of isds_message_status values. Use special
3948 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
3949 * all values, you can use bitwise arithmetic if you want.)
3950 * @offset is index of first message we are interested in. First message is 1.
3951 * Set to 0 (or 1) if you don't care.
3952 * @number is maximal length of list you want to get as input value, outputs
3953 * number of messages matching these criteria. Can be NULL if you don't care
3954 * (applies to output value either).
3955 * @messages is automatically reallocated list of isds_message's. Be ware that
3956 * it returns only brief overview (envelope and some other fields) about each
3957 * message, not the complete message. FIXME: Specify exact fields.
3958 * The list is sorted by delivery time in ascending order.
3959 * Use NULL if you don't care about the metadata (useful if you want to know
3960 * only the @number). If you provide &NULL, list will be allocated on heap,
3961 * if you provide pointer to non-NULL, list will be freed automacally at first.
3962 * Also in case of error the list will be NULLed.
3963 * @return IE_SUCCESS or appropriate error code. */
3964 isds_error
isds_get_list_of_sent_messages(struct isds_ctx
*context
,
3965 const struct timeval
*from_time
, const struct timeval
*to_time
,
3966 const long int *dmSenderOrgUnitNum
, const unsigned int status_filter
,
3967 const unsigned long int offset
, unsigned long int *number
,
3968 struct isds_list
**messages
) {
3970 return isds_get_list_of_messages(
3972 from_time
, to_time
, dmSenderOrgUnitNum
, status_filter
,
3978 /* Get list of incoming (addressed to you) messages.
3979 * Any criterion argument can be NULL, if you don't care about it.
3980 * @context is session context. Must not be NULL.
3981 * @from_time is minimal time and date of message sending inclusive.
3982 * @to_time is maximal time and date of message sending inclusive
3983 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
3984 * @status_filter is bit field of isds_message_status values. Use special
3985 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
3986 * all values, you can use bitwise arithmetic if you want.)
3987 * @offset is index of first message we are interested in. First message is 1.
3988 * Set to 0 (or 1) if you don't care.
3989 * @number is maximal length of list you want to get as input value, outputs
3990 * number of messages matching these criteria. Can be NULL if you don't care
3991 * (applies to output value either).
3992 * @messages is automatically reallocated list of isds_message's. Be ware that
3993 * it returns only brief overview (envelope and some other fields) about each
3994 * message, not the complete message. FIXME: Specify exact fields.
3995 * Use NULL if you don't care about the metadata (useful if you want to know
3996 * only the @number). If you provide &NULL, list will be allocated on heap,
3997 * if you provide pointer to non-NULL, list will be freed automacally at first.
3998 * Also in case of error the list will be NULLed.
3999 * @return IE_SUCCESS or appropriate error code. */
4000 isds_error
isds_get_list_of_received_messages(struct isds_ctx
*context
,
4001 const struct timeval
*from_time
, const struct timeval
*to_time
,
4002 const long int *dmRecipientOrgUnitNum
,
4003 const unsigned int status_filter
,
4004 const unsigned long int offset
, unsigned long int *number
,
4005 struct isds_list
**messages
) {
4007 return isds_get_list_of_messages(
4009 from_time
, to_time
, dmRecipientOrgUnitNum
, status_filter
,
4015 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
4017 * @context is session context
4018 * @service is ISDS WS service handler
4019 * @service_name is name of SERVICE_DM_OPERATIONS
4020 * @message_id is message ID to send as service argument to ISDS
4021 * @response is server SOAP body response as XML document
4022 * @code is ISDS status code
4023 * @status_message is ISDS status message
4024 * @return error coded from lower layer, context message will be set up
4026 static isds_error
build_send_check_message_request(struct isds_ctx
*context
,
4027 const isds_service service
, const xmlChar
*service_name
,
4028 const char *message_id
,
4029 xmlDocPtr
*response
, xmlChar
**code
, xmlChar
**status_message
) {
4031 isds_error err
= IE_SUCCESS
;
4032 char *service_name_locale
= NULL
, *message_id_locale
= NULL
;
4033 xmlNodePtr request
= NULL
, node
;
4034 xmlNsPtr isds_ns
= NULL
;
4036 if (!context
) return IE_INVALID_CONTEXT
;
4037 if (!service_name
|| !message_id
) return IE_INVAL
;
4038 if (!response
|| !code
|| !status_message
) return IE_INVAL
;
4040 /* Free output argument */
4041 xmlFreeDoc(*response
);
4043 free(*status_message
);
4046 /* Check if connection is established
4047 * TODO: This check should be done donwstairs. */
4048 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4050 service_name_locale
= utf82locale((char*)service_name
);
4051 message_id_locale
= utf82locale(message_id
);
4052 if (!service_name_locale
|| !message_id_locale
) {
4058 request
= xmlNewNode(NULL
, service_name
);
4060 isds_printf_message(context
,
4061 _("Could not build %s request"), service_name_locale
);
4065 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
4067 isds_log_message(context
, _("Could not create ISDS name space"));
4071 xmlSetNs(request
, isds_ns
);
4074 /* Add requested ID */
4075 err
= validate_message_id_length(context
, (xmlChar
*) message_id
);
4076 if (err
) goto leave
;
4077 INSERT_STRING(request
, "dmID", message_id
);
4080 isds_log(ILF_ISDS
, ILL_DEBUG
,
4081 _("Sending %s request for %s message ID to ISDS\n"),
4082 service_name_locale
, message_id_locale
);
4085 err
= isds(context
, service
, request
, response
);
4086 xmlFreeNode(request
); request
= NULL
;
4089 isds_log(ILF_ISDS
, ILL_DEBUG
,
4090 _("Processing ISDS response on %s request failed\n"),
4091 service_name_locale
);
4095 /* Check for response status */
4096 err
= isds_response_status(context
, service
, *response
,
4097 code
, status_message
, NULL
);
4099 isds_log(ILF_ISDS
, ILL_DEBUG
,
4100 _("ISDS response on %s request is missing status\n"),
4101 service_name_locale
);
4105 /* Request processed, but nothing found */
4106 if (xmlStrcmp(*code
, BAD_CAST
"0000")) {
4107 char *code_locale
= utf82locale((char*) *code
);
4108 char *status_message_locale
= utf82locale((char*) *status_message
);
4109 isds_log(ILF_ISDS
, ILL_DEBUG
,
4110 _("Server refused %s request for %s message ID "
4111 "(code=%s, message=%s)\n"),
4112 service_name_locale
, message_id_locale
,
4113 code_locale
, status_message_locale
);
4114 isds_log_message(context
, status_message_locale
);
4116 free(status_message_locale
);
4122 free(message_id_locale
);
4123 free(service_name_locale
);
4124 xmlFreeNode(request
);
4129 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
4130 * signed data and free ISDS response.
4131 * @context is session context
4132 * @message_id is UTF-8 encoded message ID for loging purpose
4133 * @response is parsed XML document. It will be freed and NULLed in the middle
4134 * of function run to save memmory. This is not guaranted in case of error.
4135 * @request_name is name of ISDS request used to construct response root
4136 * element name and for logging purpose.
4137 * @raw is reallocated output buffer with DER encoded CMS data
4138 * @raw_length is size of @raw buffer in bytes
4139 * @returns standard error codes, in case of error, @raw will be freed and
4140 * NULLed, @response sometimes. */
4141 static isds_error
find_extract_signed_data_free_response(
4142 struct isds_ctx
*context
, const xmlChar
*message_id
,
4143 xmlDocPtr
*response
, const xmlChar
*request_name
,
4144 void **raw
, size_t *raw_length
) {
4146 isds_error err
= IE_SUCCESS
;
4147 char *xpath_expression
= NULL
;
4148 xmlXPathContextPtr xpath_ctx
= NULL
;
4149 xmlXPathObjectPtr result
= NULL
;
4150 char *encoded_structure
= NULL
;
4152 if (!context
) return IE_INVALID_CONTEXT
;
4153 if (!raw
) return IE_INVAL
;
4155 if (!message_id
|| !response
|| !*response
|| !request_name
|| !raw_length
)
4158 /* Build XPath expression */
4159 xpath_expression
= astrcat3("/isds:", (char *) request_name
,
4160 "Response/isds:dmSignature");
4161 if (!xpath_expression
) return IE_NOMEM
;
4164 xpath_ctx
= xmlXPathNewContext(*response
);
4169 if (register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
4173 result
= xmlXPathEvalExpression(BAD_CAST xpath_expression
, xpath_ctx
);
4178 /* Empty response */
4179 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4180 char *message_id_locale
= utf82locale((char*) message_id
);
4181 isds_printf_message(context
,
4182 _("Server did not return any signed data for mesage ID `%s' "
4184 message_id_locale
, request_name
);
4185 free(message_id_locale
);
4190 if (result
->nodesetval
->nodeNr
> 1) {
4191 char *message_id_locale
= utf82locale((char*) message_id
);
4192 isds_printf_message(context
,
4193 _("Server did return more signed data for message ID `%s' "
4195 message_id_locale
, request_name
);
4196 free(message_id_locale
);
4201 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
4203 /* Extract PKCS#7 structure */
4204 EXTRACT_STRING(".", encoded_structure
);
4205 if (!encoded_structure
) {
4206 isds_log_message(context
, _("dmSignature element is empty"));
4209 /* Here we have delivery info as standalone CMS in encoded_structure.
4210 * We don't need any other data, free them: */
4211 xmlXPathFreeObject(result
); result
= NULL
;
4212 xmlXPathFreeContext(xpath_ctx
); xpath_ctx
= NULL
;
4213 xmlFreeDoc(*response
); *response
= NULL
;
4216 /* Decode PKCS#7 to DER format */
4217 *raw_length
= b64decode(encoded_structure
, raw
);
4218 if (*raw_length
== (size_t) -1) {
4219 isds_log_message(context
,
4220 _("Error while Base64-decoding PKCS#7 structure"));
4231 free(encoded_structure
);
4232 xmlXPathFreeObject(result
);
4233 xmlXPathFreeContext(xpath_ctx
);
4234 free(xpath_expression
);
4240 /* Download incoming message envelope identified by ID.
4241 * @context is session context
4242 * @message_id is message identifier (you can get them from
4243 * isds_get_list_of_received_messages())
4244 * @message is automatically reallocated message retrieved from ISDS.
4245 * It will miss documents per se. Use isds_get_received_message(), if you are
4246 * interrested in documents (content) too.
4247 * Returned hash and timestamp require documents to be verifiable. */
4248 isds_error
isds_get_received_envelope(struct isds_ctx
*context
,
4249 const char *message_id
, struct isds_message
**message
) {
4251 isds_error err
= IE_SUCCESS
;
4252 xmlDocPtr response
= NULL
;
4253 xmlChar
*code
= NULL
, *status_message
= NULL
;
4254 xmlXPathContextPtr xpath_ctx
= NULL
;
4255 xmlXPathObjectPtr result
= NULL
;
4257 if (!context
) return IE_INVALID_CONTEXT
;
4259 /* Free former message if any */
4260 if (!message
) return IE_INVAL
;
4261 isds_message_free(message
);
4263 /* Do request and check for success */
4264 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
4265 BAD_CAST
"MessageEnvelopeDownload", message_id
,
4266 &response
, &code
, &status_message
);
4267 if (err
) goto leave
;
4270 xpath_ctx
= xmlXPathNewContext(response
);
4275 if (register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
4279 result
= xmlXPathEvalExpression(
4280 BAD_CAST
"/isds:MessageEnvelopeDownloadResponse/"
4281 "isds:dmReturnedMessageEnvelope",
4287 /* Empty response */
4288 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4289 char *message_id_locale
= utf82locale((char*) message_id
);
4290 isds_printf_message(context
,
4291 _("Server did not return any envelope for ID `%s' "
4292 "on MessageEnvelopeDownload request"), message_id_locale
);
4293 free(message_id_locale
);
4298 if (result
->nodesetval
->nodeNr
> 1) {
4299 char *message_id_locale
= utf82locale((char*) message_id
);
4300 isds_printf_message(context
,
4301 _("Server did return more envelopes for ID `%s' "
4302 "on MessageEnvelopeDownload request"), message_id_locale
);
4303 free(message_id_locale
);
4308 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
4310 /* Extract the envelope (= message without documents, hence 0) */
4311 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
4312 if (err
) goto leave
;
4315 err
= serialize_subtree(context
, xpath_ctx
->node
, &(*message
)->raw
,
4316 &(*message
)->raw_length
);
4320 isds_message_free(message
);
4323 xmlXPathFreeObject(result
);
4324 xmlXPathFreeContext(xpath_ctx
);
4327 free(status_message
);
4328 xmlFreeDoc(response
);
4331 isds_log(ILF_ISDS
, ILL_DEBUG
,
4332 _("MessageEnvelopeDownload request processed by server "
4339 /* Load signed delivery info from buffer.
4340 * @context is session context
4341 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
4342 * retrieve such data from message->raw after calling
4343 * isds_get_signed_delivery_info().
4344 * @length is length of buffer in bytes.
4345 * @message is automatically reallocated message parsed from @buffer.
4346 * @strategy selects how buffer will be attached into raw isds_message member.
4348 isds_error
isds_load_signed_delivery_info(struct isds_ctx
*context
,
4349 const void *buffer
, const size_t length
,
4350 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
4352 isds_error err
= IE_SUCCESS
;
4353 xmlDocPtr message_doc
= NULL
;
4354 xmlXPathContextPtr xpath_ctx
= NULL
;
4355 xmlXPathObjectPtr result
= NULL
;
4356 void *xml_stream
= NULL
;
4357 size_t xml_stream_length
= 0;
4359 if (!context
) return IE_INVALID_CONTEXT
;
4360 if (!message
) return IE_INVAL
;
4361 isds_message_free(message
);
4362 if (!buffer
) return IE_INVAL
;
4365 /* Extract delivery info from PKCS#7 structure */
4366 err
= extract_cms_data(context
, buffer
, length
,
4367 &xml_stream
, &xml_stream_length
);
4368 if (err
) goto leave
;
4370 isds_log(ILF_ISDS
, ILL_DEBUG
,
4371 _("Signed delivery info content:\n%.*s\nEnd of delivery info\n"),
4372 xml_stream_length
, xml_stream
);
4374 /* Convert extracted delivery info XML stream into XPath context */
4375 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
4380 xpath_ctx
= xmlXPathNewContext(message_doc
);
4385 /* XXX: Name spaces mangled for signed delivery info:
4386 * http://isds.czechpoint.cz/v20/delivery:
4388 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
4390 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
4391 * <p:dmID>170272</p:dmID>
4394 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
4396 * </q:dmEvents>...</q:dmEvents>
4398 * </q:GetDeliveryInfoResponse>
4400 if (register_namespaces(xpath_ctx
, MESSAGE_NS_SIGNED_DELIVERY
)) {
4404 result
= xmlXPathEvalExpression(
4405 BAD_CAST
"/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
4411 /* Empty embedded delivery info */
4412 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4413 isds_printf_message(context
,
4414 _("XML document embedded into PKCS#7 structure is not "
4415 "sisds:dmDelivery document"));
4419 /* More embedded delivery infos */
4420 if (result
->nodesetval
->nodeNr
> 1) {
4421 isds_printf_message(context
,
4422 _("Embeded XML document into PKCS#7 structure has more "
4423 "root sisds:dmDelivery elements"));
4427 /* One embedded delivery info */
4428 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
4430 /* Extract the envelope (= message without documents, hence 0).
4431 * XXX: extract_TReturnedMessage() can obtain attachments size,
4432 * but delivery info carries none. It's coded as option elements,
4433 * so it should work. */
4434 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
4435 if (err
) goto leave
;
4437 /* Extract events */
4438 err
= move_xpathctx_to_child(context
, BAD_CAST
"sisds:dmEvents", xpath_ctx
);
4439 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
4440 if (err
) { err
= IE_ERROR
; goto leave
; }
4441 err
= extract_events(context
, &(*message
)->envelope
->events
, xpath_ctx
);
4442 if (err
) goto leave
;
4444 /* Append raw CMS structure into message */
4445 (*message
)->raw_type
= RAWTYPE_CMS_SIGNED_DELIVERYINFO
;
4447 case BUFFER_DONT_STORE
:
4450 (*message
)->raw
= malloc(length
);
4451 if (!(*message
)->raw
) {
4455 memcpy((*message
)->raw
, buffer
, length
);
4456 (*message
)->raw_length
= length
;
4459 (*message
)->raw
= (void *) buffer
;
4460 (*message
)->raw_length
= length
;
4469 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
4470 isds_message_free(message
);
4473 xmlFreeDoc(message_doc
);
4474 cms_data_free(xml_stream
);
4475 xmlXPathFreeObject(result
);
4476 xmlXPathFreeContext(xpath_ctx
);
4479 isds_log(ILF_ISDS
, ILL_DEBUG
,
4480 _("Signed message loaded successfully.\n"));
4485 /* Download signed delivery infosheet of given message identified by ID.
4486 * @context is session context
4487 * @message_id is message identifier (you can get them from
4488 * isds_get_list_of_{sent,received}_messages())
4489 * @message is automatically reallocated message retrieved from ISDS.
4490 * It will miss documents per se. Use isds_get_signed_received_message(),
4491 * if you are interrested in documents (content). OTOH, only this function
4492 * can get list events message has gone through. */
4493 isds_error
isds_get_signed_delivery_info(struct isds_ctx
*context
,
4494 const char *message_id
, struct isds_message
**message
) {
4496 isds_error err
= IE_SUCCESS
;
4497 xmlDocPtr response
= NULL
;
4498 xmlChar
*code
= NULL
, *status_message
= NULL
;
4500 size_t raw_length
= 0;
4502 if (!context
) return IE_INVALID_CONTEXT
;
4504 /* Free former message if any */
4505 if (!message
) return IE_INVAL
;
4506 isds_message_free(message
);
4508 /* Do request and check for success */
4509 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
4510 BAD_CAST
"GetSignedDeliveryInfo", message_id
,
4511 &response
, &code
, &status_message
);
4512 if (err
) goto leave
;
4514 /* Find signed delivery info, extract it into raw and maybe free
4516 err
= find_extract_signed_data_free_response(context
,
4517 (xmlChar
*)message_id
, &response
,
4518 BAD_CAST
"GetSignedDeliveryInfo", &raw
, &raw_length
);
4519 if (err
) goto leave
;
4522 err
= isds_load_signed_delivery_info(context
, raw
, raw_length
,
4523 message
, BUFFER_MOVE
);
4524 if (err
) goto leave
;
4530 isds_message_free(message
);
4535 free(status_message
);
4536 xmlFreeDoc(response
);
4539 isds_log(ILF_ISDS
, ILL_DEBUG
,
4540 _("GetSignedDeliveryInfo request processed by server "
4547 /* Load delivery info from buffer.
4548 * @context is session context
4549 * @buffer is XML document stream with delivery info. You can retrieve such
4550 * data from message->raw after calling isds_get_delivery_info().
4551 * @length is length of buffer in bytes.
4552 * @message is automatically reallocated message parsed from @buffer.
4553 * @strategy selects how buffer will be attached into raw isds_message member.
4555 isds_error
isds_load_delivery_info(struct isds_ctx
*context
,
4556 const void *buffer
, const size_t length
,
4557 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
4559 isds_error err
= IE_SUCCESS
;
4560 xmlDocPtr message_doc
= NULL
;
4561 xmlXPathContextPtr xpath_ctx
= NULL
;
4562 xmlXPathObjectPtr result
= NULL
;
4564 if (!context
) return IE_INVALID_CONTEXT
;
4565 if (!message
) return IE_INVAL
;
4566 isds_message_free(message
);
4567 if (!buffer
) return IE_INVAL
;
4569 /* Convert delivery info XML stream into XPath context */
4570 message_doc
= xmlParseMemory(buffer
, length
);
4575 xpath_ctx
= xmlXPathNewContext(message_doc
);
4580 if (register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
4584 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:dmDelivery", xpath_ctx
);
4589 /* Empty delivery info */
4590 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4591 isds_printf_message(context
,
4592 _("XML document is not isds:dmDelivery document"));
4596 /* More delivery infos
4597 * This should never happen */
4598 if (result
->nodesetval
->nodeNr
> 1) {
4599 isds_printf_message(context
,
4600 _("XML document has more root isds:dmDelivery elements"));
4604 /* One delivery info */
4605 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
4607 /* Extract the envelope (= message without documents, hence 0).
4608 * XXX: extract_TReturnedMessage() can obtain attachments size,
4609 * but delivery info carries none. It's coded as option elements,
4610 * so it should work. */
4611 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
4612 if (err
) goto leave
;
4614 /* Extract events */
4615 err
= move_xpathctx_to_child(context
, BAD_CAST
"isds:dmEvents", xpath_ctx
);
4616 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
4617 if (err
) { err
= IE_ERROR
; goto leave
; }
4618 err
= extract_events(context
, &(*message
)->envelope
->events
, xpath_ctx
);
4619 if (err
) goto leave
;
4621 /* Append raw CMS structure into message */
4622 (*message
)->raw_type
= RAWTYPE_DELIVERYINFO
;
4624 case BUFFER_DONT_STORE
:
4627 (*message
)->raw
= malloc(length
);
4628 if (!(*message
)->raw
) {
4632 memcpy((*message
)->raw
, buffer
, length
);
4633 (*message
)->raw_length
= length
;
4636 (*message
)->raw
= (void *) buffer
;
4637 (*message
)->raw_length
= length
;
4646 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
4647 isds_message_free(message
);
4650 xmlFreeDoc(message_doc
);
4651 xmlXPathFreeObject(result
);
4652 xmlXPathFreeContext(xpath_ctx
);
4655 isds_log(ILF_ISDS
, ILL_DEBUG
,
4656 _("Delivery info loaded successfully.\n"));
4661 /* Download delivery infosheet of given message identified by ID.
4662 * @context is session context
4663 * @message_id is message identifier (you can get them from
4664 * isds_get_list_of_{sent,received}_messages())
4665 * @message is automatically reallocated message retrieved from ISDS.
4666 * It will miss documents per se. Use isds_get_received_message(), if you are
4667 * interrested in documents (content). OTOH, only this function can get list
4668 * events message has gone through. */
4669 isds_error
isds_get_delivery_info(struct isds_ctx
*context
,
4670 const char *message_id
, struct isds_message
**message
) {
4672 isds_error err
= IE_SUCCESS
;
4673 xmlDocPtr response
= NULL
;
4674 xmlChar
*code
= NULL
, *status_message
= NULL
;
4675 xmlXPathContextPtr xpath_ctx
= NULL
;
4676 xmlXPathObjectPtr result
= NULL
;
4677 xmlNodePtr delivery_node
= NULL
;
4679 if (!context
) return IE_INVALID_CONTEXT
;
4681 /* Free former message if any */
4682 if (!message
) return IE_INVAL
;
4683 isds_message_free(message
);
4685 /* Do request and check for success */
4686 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
4687 BAD_CAST
"GetDeliveryInfo", message_id
,
4688 &response
, &code
, &status_message
);
4689 if (err
) goto leave
;
4692 xpath_ctx
= xmlXPathNewContext(response
);
4697 if (register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
4701 result
= xmlXPathEvalExpression(
4702 BAD_CAST
"/isds:GetDeliveryInfoResponse/isds:dmDelivery",
4708 /* Empty response */
4709 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4710 char *message_id_locale
= utf82locale((char*) message_id
);
4711 isds_printf_message(context
,
4712 _("Server did not return any delivery info for ID `%s' "
4713 "on GetDeliveryInfo request"), message_id_locale
);
4714 free(message_id_locale
);
4718 /* More delivery infos */
4719 if (result
->nodesetval
->nodeNr
> 1) {
4720 char *message_id_locale
= utf82locale((char*) message_id
);
4721 isds_printf_message(context
,
4722 _("Server did return more delivery infos for ID `%s' "
4723 "on GetDeliveryInfo request"), message_id_locale
);
4724 free(message_id_locale
);
4728 /* One delivery info */
4729 xpath_ctx
->node
= delivery_node
= result
->nodesetval
->nodeTab
[0];
4731 /* Extract the envelope (= message without documents, hence 0).
4732 * XXX: extract_TReturnedMessage() can obtain attachments size,
4733 * but delivery info carries none. It's coded as option elements,
4734 * so it should work. */
4735 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
4736 if (err
) goto leave
;
4738 /* Extract events */
4739 err
= move_xpathctx_to_child(context
, BAD_CAST
"isds:dmEvents", xpath_ctx
);
4740 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
4741 if (err
) { err
= IE_ERROR
; goto leave
; }
4742 err
= extract_events(context
, &(*message
)->envelope
->events
, xpath_ctx
);
4743 if (err
) goto leave
;
4746 err
= serialize_subtree(context
, delivery_node
, &(*message
)->raw
,
4747 &(*message
)->raw_length
);
4751 isds_message_free(message
);
4754 xmlXPathFreeObject(result
);
4755 xmlXPathFreeContext(xpath_ctx
);
4758 free(status_message
);
4759 xmlFreeDoc(response
);
4762 isds_log(ILF_ISDS
, ILL_DEBUG
,
4763 _("GetDeliveryInfo request processed by server "
4770 /* Load incoming message from buffer.
4771 * @context is session context
4772 * @buffer XML stream with unsigned message. You can retrieve such data from
4773 * message->raw after calling isds_get_received_message().
4774 * @length is length of buffer in bytes.
4775 * @message is automatically reallocated message parsed from @buffer.
4776 * @strategy selects how buffer will be attached into raw isds_message member.
4778 isds_error
isds_load_received_message(struct isds_ctx
*context
,
4779 const void *buffer
, const size_t length
,
4780 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
4782 isds_error err
= IE_SUCCESS
;
4783 xmlDocPtr message_doc
= NULL
;
4784 xmlXPathContextPtr xpath_ctx
= NULL
;
4785 xmlXPathObjectPtr result
= NULL
;
4787 if (!context
) return IE_INVALID_CONTEXT
;
4788 if (!message
) return IE_INVAL
;
4789 isds_message_free(message
);
4790 if (!buffer
) return IE_INVAL
;
4793 isds_log(ILF_ISDS
, ILL_DEBUG
,
4794 _("Incoming message content:\n%.*s\nEnd of message\n"),
4797 /* Convert extracted messages XML stream into XPath context */
4798 message_doc
= xmlParseMemory(buffer
, length
);
4803 xpath_ctx
= xmlXPathNewContext(message_doc
);
4808 /* XXX: Standard name space */
4809 if (register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
4813 result
= xmlXPathEvalExpression(
4814 BAD_CAST
"/isds:dmReturnedMessage", xpath_ctx
);
4819 /* Bad root element */
4820 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4821 isds_printf_message(context
,
4822 _("XML document is not isds:dmReturnedMessage document"));
4826 /* More root elements. This should never happen. */
4827 if (result
->nodesetval
->nodeNr
> 1) {
4828 isds_printf_message(context
,
4829 _("XML document has more "
4830 "root isds:dmReturnedMessage elements"));
4835 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
4837 /* Extract the message */
4838 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
4839 if (err
) goto leave
;
4841 /* Append XML stream into message */
4842 (*message
)->raw_type
= RAWTYPE_INCOMING_MESSAGE
;
4844 case BUFFER_DONT_STORE
:
4847 (*message
)->raw
= malloc(length
);
4848 if (!(*message
)->raw
) {
4852 memcpy((*message
)->raw
, buffer
, length
);
4853 (*message
)->raw_length
= length
;
4856 (*message
)->raw
= (void *) buffer
;
4857 (*message
)->raw_length
= length
;
4866 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
4867 isds_message_free(message
);
4870 xmlFreeDoc(message_doc
);
4871 xmlXPathFreeObject(result
);
4872 xmlXPathFreeContext(xpath_ctx
);
4875 isds_log(ILF_ISDS
, ILL_DEBUG
,
4876 _("Incoming message loaded successfully.\n"));
4881 /* Download incoming message identified by ID.
4882 * @context is session context
4883 * @message_id is message identifier (you can get them from
4884 * isds_get_list_of_received_messages())
4885 * @message is automatically reallocated message retrieved from ISDS */
4886 isds_error
isds_get_received_message(struct isds_ctx
*context
,
4887 const char *message_id
, struct isds_message
**message
) {
4889 isds_error err
= IE_SUCCESS
;
4890 xmlDocPtr response
= NULL
;
4891 xmlChar
*code
= NULL
, *status_message
= NULL
;
4892 xmlXPathContextPtr xpath_ctx
= NULL
;
4893 xmlXPathObjectPtr result
= NULL
;
4895 if (!context
) return IE_INVALID_CONTEXT
;
4897 /* Free former message if any */
4898 if (message
) isds_message_free(message
);
4900 /* Do request and check for success */
4901 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
4902 BAD_CAST
"MessageDownload", message_id
,
4903 &response
, &code
, &status_message
);
4904 if (err
) goto leave
;
4907 xpath_ctx
= xmlXPathNewContext(response
);
4912 if (register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
4916 result
= xmlXPathEvalExpression(
4917 BAD_CAST
"/isds:MessageDownloadResponse/isds:dmReturnedMessage",
4923 /* Empty response */
4924 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4925 char *message_id_locale
= utf82locale((char*) message_id
);
4926 isds_printf_message(context
,
4927 _("Server did not return any message for ID `%s' "
4928 "on MessageDownload request"), message_id_locale
);
4929 free(message_id_locale
);
4934 if (result
->nodesetval
->nodeNr
> 1) {
4935 char *message_id_locale
= utf82locale((char*) message_id
);
4936 isds_printf_message(context
,
4937 _("Server did return more messages for ID `%s' "
4938 "on MessageDownload request"), message_id_locale
);
4939 free(message_id_locale
);
4944 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
4946 /* Extract the message */
4947 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
4948 if (err
) goto leave
;
4951 err
= serialize_subtree(context
, xpath_ctx
->node
, &(*message
)->raw
,
4952 &(*message
)->raw_length
);
4956 isds_message_free(message
);
4959 xmlXPathFreeObject(result
);
4960 xmlXPathFreeContext(xpath_ctx
);
4963 free(status_message
);
4964 xmlFreeDoc(response
);
4967 isds_log(ILF_ISDS
, ILL_DEBUG
,
4968 _("MessageDownload request processed by server "
4975 /* Load signed message from buffer.
4976 * @context is session context
4977 * @outgoing is true if message is outgoing, false if message is incoming
4978 * @buffer is DER encoded PKCS#7 structure with signed message. You can
4979 * retrieve such data from message->raw after calling
4980 * isds_get_signed_{received,sent}_message().
4981 * @length is length of buffer in bytes.
4982 * @message is automatically reallocated message parsed from @buffer.
4983 * @strategy selects how buffer will be attached into raw isds_message member.
4985 isds_error
isds_load_signed_message(struct isds_ctx
*context
,
4986 const _Bool outgoing
, const void *buffer
, const size_t length
,
4987 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
4989 isds_error err
= IE_SUCCESS
;
4990 xmlDocPtr message_doc
= NULL
;
4991 xmlXPathContextPtr xpath_ctx
= NULL
;
4992 xmlXPathObjectPtr result
= NULL
;
4993 void *xml_stream
= NULL
;
4994 size_t xml_stream_length
= 0;
4996 if (!context
) return IE_INVALID_CONTEXT
;
4997 if (!message
) return IE_INVAL
;
4998 isds_message_free(message
);
4999 if (!buffer
) return IE_INVAL
;
5002 /* Extract message from PKCS#7 structure */
5003 err
= extract_cms_data(context
, buffer
, length
,
5004 &xml_stream
, &xml_stream_length
);
5005 if (err
) goto leave
;
5007 isds_log(ILF_ISDS
, ILL_DEBUG
, (outgoing
) ?
5008 _("Signed outgoing message content:\n%.*s\nEnd of message\n") :
5009 _("Signed incoming message content:\n%.*s\nEnd of message\n"),
5010 xml_stream_length
, xml_stream
);
5012 /* Convert extracted messages XML stream into XPath context */
5013 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
5018 xpath_ctx
= xmlXPathNewContext(message_doc
);
5023 /* XXX: Name spaces mangled for outgoing direction:
5024 * http://isds.czechpoint.cz/v20/SentMessage:
5026 * <q:MessageDownloadResponse
5027 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
5028 * <q:dmReturnedMessage>
5029 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
5030 * <p:dmID>151916</p:dmID>
5033 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
5035 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
5036 * </q:dmReturnedMessage>
5037 * </q:MessageDownloadResponse>
5039 * XXX: Name spaces mangled for incoming direction:
5040 * http://isds.czechpoint.cz/v20/message:
5042 * <q:MessageDownloadResponse
5043 * xmlns:q="http://isds.czechpoint.cz/v20/message">
5044 * <q:dmReturnedMessage>
5045 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
5046 * <p:dmID>151916</p:dmID>
5049 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
5051 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
5052 * </q:dmReturnedMessage>
5053 * </q:MessageDownloadResponse>
5055 * Stupidity of ISDS developers is unlimited */
5056 if (register_namespaces(xpath_ctx
, (outgoing
) ?
5057 MESSAGE_NS_SIGNED_OUTGOING
: MESSAGE_NS_SIGNED_INCOMING
)) {
5061 /* XXX: Embeded message XML document is always rooted as
5062 * /sisds:MessageDownloadResponse (even outgoing message). */
5063 result
= xmlXPathEvalExpression(
5064 BAD_CAST
"/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
5070 /* Empty embedded message */
5071 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5072 isds_printf_message(context
,
5073 _("XML document embedded into PKCS#7 structure is not "
5074 "sisds:dmReturnedMessage document"));
5078 /* More embedded messages */
5079 if (result
->nodesetval
->nodeNr
> 1) {
5080 isds_printf_message(context
,
5081 _("Embeded XML document into PKCS#7 structure has more "
5082 "root sisds:dmReturnedMessage elements"));
5086 /* One embedded message */
5087 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5089 /* Extract the message */
5090 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
5091 if (err
) goto leave
;
5093 /* Append raw CMS structure into message */
5094 (*message
)->raw_type
= (outgoing
) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
5095 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
;
5097 case BUFFER_DONT_STORE
:
5100 (*message
)->raw
= malloc(length
);
5101 if (!(*message
)->raw
) {
5105 memcpy((*message
)->raw
, buffer
, length
);
5106 (*message
)->raw_length
= length
;
5109 (*message
)->raw
= (void *) buffer
;
5110 (*message
)->raw_length
= length
;
5120 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
5121 isds_message_free(message
);
5124 xmlFreeDoc(message_doc
);
5125 cms_data_free(xml_stream
);
5126 xmlXPathFreeObject(result
);
5127 xmlXPathFreeContext(xpath_ctx
);
5130 isds_log(ILF_ISDS
, ILL_DEBUG
,
5131 _("Signed message loaded successfully.\n"));
5136 /* Download signed incoming/outgoing message identified by ID.
5137 * @context is session context
5138 * @output is true for outging message, false for incoming message
5139 * @message_id is message identifier (you can get them from
5140 * isds_get_list_of_{sent,received}_messages())
5141 * @message is automatically reallocated message retrieved from ISDS. The raw
5142 * memeber will be filled with PKCS#7 structure in DER format. */
5143 _hidden isds_error
isds_get_signed_message(struct isds_ctx
*context
,
5144 const _Bool outgoing
, const char *message_id
,
5145 struct isds_message
**message
) {
5147 isds_error err
= IE_SUCCESS
;
5148 xmlDocPtr response
= NULL
;
5149 xmlChar
*code
= NULL
, *status_message
= NULL
;
5150 xmlXPathContextPtr xpath_ctx
= NULL
;
5151 xmlXPathObjectPtr result
= NULL
;
5152 char *encoded_structure
= NULL
;
5154 size_t raw_length
= 0;
5156 if (!context
) return IE_INVALID_CONTEXT
;
5157 if (!message
) return IE_INVAL
;
5158 isds_message_free(message
);
5160 /* Do request and check for success */
5161 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
5162 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
5163 BAD_CAST
"SignedMessageDownload",
5164 message_id
, &response
, &code
, &status_message
);
5165 if (err
) goto leave
;
5167 /* Find signed message, extract it into raw and maybe free
5169 err
= find_extract_signed_data_free_response(context
,
5170 (xmlChar
*)message_id
, &response
,
5171 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
5172 BAD_CAST
"SignedMessageDownload",
5174 if (err
) goto leave
;
5177 err
= isds_load_signed_message(context
, outgoing
, raw
, raw_length
,
5178 message
, BUFFER_MOVE
);
5179 if (err
) goto leave
;
5185 isds_message_free(message
);
5188 free(encoded_structure
);
5189 xmlXPathFreeObject(result
);
5190 xmlXPathFreeContext(xpath_ctx
);
5194 free(status_message
);
5195 xmlFreeDoc(response
);
5198 isds_log(ILF_ISDS
, ILL_DEBUG
,
5200 _("SignedSentMessageDownload request processed by server "
5201 "successfully.\n") :
5202 _("SignedMessageDownload request processed by server "
5209 /* Download signed incoming message identified by ID.
5210 * @context is session context
5211 * @message_id is message identifier (you can get them from
5212 * isds_get_list_of_received_messages())
5213 * @message is automatically reallocated message retrieved from ISDS. The raw
5214 * memeber will be filled with PKCS#7 structure in DER format. */
5215 isds_error
isds_get_signed_received_message(struct isds_ctx
*context
,
5216 const char *message_id
, struct isds_message
**message
) {
5217 return isds_get_signed_message(context
, 0, message_id
, message
);
5221 /* Download signed outgoing message identified by ID.
5222 * @context is session context
5223 * @message_id is message identifier (you can get them from
5224 * isds_get_list_of_sent_messages())
5225 * @message is automatically reallocated message retrieved from ISDS. The raw
5226 * memeber will be filled with PKCS#7 structure in DER format. */
5227 isds_error
isds_get_signed_sent_message(struct isds_ctx
*context
,
5228 const char *message_id
, struct isds_message
**message
) {
5229 return isds_get_signed_message(context
, 1, message_id
, message
);
5233 /* Retrieve hash of message identified by ID stored in ISDS.
5234 * @context is session context
5235 * @message_id is message identifier
5236 * @hash is automatically reallocated message hash downloaded from ISDS.
5237 * Message must exist in system and must not be deleted. */
5238 isds_error
isds_download_message_hash(struct isds_ctx
*context
,
5239 const char *message_id
, struct isds_hash
**hash
) {
5241 isds_error err
= IE_SUCCESS
;
5242 xmlDocPtr response
= NULL
;
5243 xmlChar
*code
= NULL
, *status_message
= NULL
;
5244 xmlXPathContextPtr xpath_ctx
= NULL
;
5245 xmlXPathObjectPtr result
= NULL
;
5247 if (!context
) return IE_INVALID_CONTEXT
;
5249 isds_hash_free(hash
);
5251 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
5252 BAD_CAST
"VerifyMessage", message_id
,
5253 &response
, &code
, &status_message
);
5254 if (err
) goto leave
;
5258 xpath_ctx
= xmlXPathNewContext(response
);
5263 if (register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5267 result
= xmlXPathEvalExpression(
5268 BAD_CAST
"/isds:VerifyMessageResponse",
5274 /* Empty response */
5275 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5276 char *message_id_locale
= utf82locale((char*) message_id
);
5277 isds_printf_message(context
,
5278 _("Server did not return any response for ID `%s' "
5279 "on VerifyMessage request"), message_id_locale
);
5280 free(message_id_locale
);
5284 /* More responses */
5285 if (result
->nodesetval
->nodeNr
> 1) {
5286 char *message_id_locale
= utf82locale((char*) message_id
);
5287 isds_printf_message(context
,
5288 _("Server did return more responses for ID `%s' "
5289 "on VerifyMessage request"), message_id_locale
);
5290 free(message_id_locale
);
5295 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5297 /* Extract the hash */
5298 err
= find_and_extract_DmHash(context
, hash
, xpath_ctx
);
5302 isds_hash_free(hash
);
5305 xmlXPathFreeObject(result
);
5306 xmlXPathFreeContext(xpath_ctx
);
5309 free(status_message
);
5310 xmlFreeDoc(response
);
5313 isds_log(ILF_ISDS
, ILL_DEBUG
,
5314 _("VerifyMessage request processed by server "
5321 /* Mark message as read. This is a transactional commit function to acknoledge
5322 * to ISDS the message has been downloaded and processed by client properly.
5323 * @context is session context
5324 * @message_id is message identifier. */
5325 isds_error
isds_mark_message_read(struct isds_ctx
*context
,
5326 const char *message_id
) {
5328 isds_error err
= IE_SUCCESS
;
5329 xmlDocPtr response
= NULL
;
5330 xmlChar
*code
= NULL
, *status_message
= NULL
;
5332 if (!context
) return IE_INVALID_CONTEXT
;
5334 /* Do request and check for success */
5335 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
5336 BAD_CAST
"MarkMessageAsDownloaded", message_id
,
5337 &response
, &code
, &status_message
);
5340 free(status_message
);
5341 xmlFreeDoc(response
);
5344 isds_log(ILF_ISDS
, ILL_DEBUG
,
5345 _("MarkMessageAsDownloaded request processed by server "
5352 #undef INSERT_STRING_ATTRIBUTE
5353 #undef INSERT_ULONGINTNOPTR
5354 #undef INSERT_ULONGINT
5355 #undef INSERT_LONGINT
5356 #undef INSERT_BOOLEAN
5357 #undef INSERT_STRING
5358 #undef EXTRACT_STRING_ATTRIBUTE
5359 #undef EXTRACT_ULONGINT
5360 #undef EXTRACT_LONGINT
5361 #undef EXTRACT_BOOLEAN
5362 #undef EXTRACT_STRING
5365 /* Compute hash of message from raw representation and store it into envelope.
5366 * Original hash structure will be destroyed in envelope.
5367 * @context is session context
5368 * @message is message carrying raw XML message blob
5369 * @algorithm is desired hash algorithm to use */
5370 isds_error
isds_compute_message_hash(struct isds_ctx
*context
,
5371 struct isds_message
*message
, const isds_hash_algorithm algorithm
) {
5372 isds_error err
= IE_SUCCESS
;
5374 size_t phys_start
, phys_end
;
5375 char *phys_path
= NULL
;
5376 struct isds_hash
*new_hash
= NULL
;
5379 if (!context
) return IE_INVALID_CONTEXT
;
5380 if (!message
) return IE_INVAL
;
5382 if (!message
->raw
) {
5383 isds_log_message(context
,
5384 _("Message does not carry raw representation"));
5388 switch (message
->raw_type
) {
5389 case RAWTYPE_INCOMING_MESSAGE
:
5392 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
5393 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
5394 nsuri
= SISDS_INCOMING_NS
;
5396 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
5397 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
5398 nsuri
= SISDS_OUTGOING_NS
;
5401 isds_log_message(context
, _("Bad raw representation type"));
5407 /* XXX: Hash is computed from original string represinting isds:dmDm
5408 * subtree. That means no encoding, white space, xmlns attributes changes.
5409 * In other words, input for hash can be invalid XML stream. */
5411 /* Extract dmDM content as bit stream */
5412 /* FIXME: Signed messages has mangled namespace and are stored in CMS */
5413 phys_path
= astrcat(nsuri
,
5414 PHYSXML_NS_SEPARATOR
"dmReturnedMessage"
5415 PHYSXML_ELEMENT_SEPARATOR
5416 ISDS_NS PHYSXML_NS_SEPARATOR
"dmDm");
5421 err
= find_element_boundary(message
->raw
, message
->raw_length
,
5422 phys_path
, &phys_start
, &phys_end
);
5425 isds_log_message(context
,
5426 _("Substring with isds:dmDM could not be located "
5433 new_hash
= calloc(1, sizeof(*new_hash
));
5438 new_hash
->algorithm
= algorithm
;
5439 err
= compute_hash(message
->raw
+ phys_start
, phys_end
- phys_start
+ 1,
5442 isds_log_message(context
, _("Could not compute message hash"));
5446 /* Save computed hash */
5447 if (!message
->envelope
) {
5448 message
->envelope
= calloc(1, sizeof(*message
->envelope
));
5449 if (!message
->envelope
) {
5454 isds_hash_free(&message
->envelope
->hash
);
5455 message
->envelope
->hash
= new_hash
;
5459 isds_hash_free(&new_hash
);
5467 /* Compare two hashes.
5469 * @h2 is another hash
5471 * IE_SUCCESS if hashes equal
5472 * IE_NOTUNIQ if hashes are comparable, but they don't equal
5473 * IE_ENUM if not comparable, but both structures defined
5474 * IE_INVAL if some of the structures are undefined (NULL)
5475 * IE_ERROR if internal error occurs */
5476 isds_error
isds_hash_cmp(const struct isds_hash
*h1
, const struct isds_hash
*h2
) {
5477 if (h1
== NULL
|| h2
== NULL
) return IE_INVAL
;
5478 if (h1
->algorithm
!= h2
->algorithm
) return IE_ENUM
;
5479 if (h1
->length
!= h2
->length
) return IE_ERROR
;
5480 if (h1
->length
> 0 && !h1
->value
) return IE_ERROR
;
5481 if (h2
->length
> 0 && !h2
->value
) return IE_ERROR
;
5483 for (int i
= 0; i
< h1
->length
; i
++) {
5484 if (((uint8_t *) (h1
->value
))[i
] != ((uint8_t *) (h2
->value
))[i
])
5491 /* Check message has gone through ISDS by comparing message hash stored in
5492 * ISDS and locally computed hash. You must provide message with valid raw
5493 * member (do not use isds_load_*_message(..., BUFFER_DONT_STORE)).
5494 * This is convenient wrapper for isds_download_message_hash(),
5495 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
5496 * @context is session context
5497 * @message is message with valid raw and envelope member; envelope->hash
5498 * member will be changed during funcion run. Use envelope on heap only.
5500 * IE_SUCCESS if message originates in ISDS
5501 * IE_NOTEQUAL if message is unknown to ISDS
5502 * other code for other errors */
5503 isds_error
isds_verify_message_hash(struct isds_ctx
*context
,
5504 struct isds_message
*message
) {
5505 isds_error err
= IE_SUCCESS
;
5506 struct isds_hash
*downloaded_hash
= NULL
;
5508 if (!context
) return IE_INVALID_CONTEXT
;
5509 if (!message
) return IE_INVAL
;
5511 if (!message
->envelope
) {
5512 isds_log_message(context
,
5513 _("Given message structure is missing envelope"));
5516 if (!message
->raw
) {
5517 isds_log_message(context
,
5518 _("Given message structure is missing raw representation"));
5522 err
= isds_download_message_hash(context
, message
->envelope
->dmID
,
5524 if (err
) goto leave
;
5526 err
= isds_compute_message_hash(context
, message
,
5527 downloaded_hash
->algorithm
);
5528 if (err
) goto leave
;
5530 err
= isds_hash_cmp(downloaded_hash
, message
->envelope
->hash
);
5533 isds_hash_free(&downloaded_hash
);
5538 /* Search for document by document ID in list of documents. IDs are compared
5540 * @documents is list of isds_documents
5541 * @id is document identifier
5542 * @return first matching document or NULL. */
5543 const struct isds_document
*isds_find_document_by_id(
5544 const struct isds_list
*documents
, const char *id
) {
5545 const struct isds_list
*item
;
5546 const struct isds_document
*document
;
5548 for (item
= documents
; item
; item
= item
->next
) {
5549 document
= (struct isds_document
*) item
->data
;
5550 if (!document
) continue;
5552 if (!xmlStrcmp((xmlChar
*) id
, (xmlChar
*) document
->dmFileGuid
))
5560 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
5561 struct isds_message **message);
5562 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
5563 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
5564 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
5565 struct isds_address **address);
5567 int isds_message_free(struct isds_message **message);
5568 int isds_address_free(struct isds_address **address);
5572 /* Makes known all relevant namespaces to given XPath context
5573 * @xpat_ctx is XPath context
5574 * @message_ns selects propper message name space. Unsisnged and signed
5576 * prefix and to URI ISDS_NS */
5577 _hidden isds_error
register_namespaces(xmlXPathContextPtr xpath_ctx
,
5578 const message_ns_type message_ns
) {
5579 const xmlChar
*message_namespace
= NULL
;
5581 if (!xpath_ctx
) return IE_ERROR
;
5583 switch(message_ns
) {
5584 case MESSAGE_NS_UNSIGNED
:
5585 message_namespace
= BAD_CAST ISDS_NS
; break;
5586 case MESSAGE_NS_SIGNED_INCOMING
:
5587 message_namespace
= BAD_CAST SISDS_INCOMING_NS
; break;
5588 case MESSAGE_NS_SIGNED_OUTGOING
:
5589 message_namespace
= BAD_CAST SISDS_OUTGOING_NS
; break;
5590 case MESSAGE_NS_SIGNED_DELIVERY
:
5591 message_namespace
= BAD_CAST SISDS_DELIVERY_NS
; break;
5596 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"soap", BAD_CAST SOAP_NS
))
5598 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"isds", BAD_CAST ISDS_NS
))
5600 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"sisds", message_namespace
))
5602 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"xs", BAD_CAST SCHEMA_NS
))