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 /* Base URL of production ISDS instance */
18 const char isds_locator
[] = "https://ws1.mojedatovaschranka.cz/";
19 const char isds_cert_locator
[] = "https://ws1c.mojedatovaschranka.cz/";
21 /* Base URL of production ISDS instance */
22 const char isds_testing_locator
[] = "https://ws1.czebox.cz/";
23 const char isds_cert_testing_locator
[] = "https://ws1c.czebox.cz/";
25 /* Extension to MIME type map */
26 static xmlChar
*extension_map_mime
[] = {
27 BAD_CAST
"pdf", BAD_CAST
"application/pdf",
28 BAD_CAST
"xml", BAD_CAST
"application/xml",
29 BAD_CAST
"fo", BAD_CAST
"application/vnd.software602.filler.xml+form",
30 BAD_CAST
"zfo", BAD_CAST
"application/vnd.software602.filler.xml+zip+form",
31 BAD_CAST
"html", BAD_CAST
"text/html",
32 BAD_CAST
"htm", BAD_CAST
"text/html",
33 BAD_CAST
"odt", BAD_CAST
"application/vnd.oasis.opendocument.text",
34 BAD_CAST
"ods", BAD_CAST
"application/vnd.oasis.opendocument.spreadsheet",
35 BAD_CAST
"odp", BAD_CAST
"application/vnd.oasis.opendocument.presentation",
36 BAD_CAST
"txt", BAD_CAST
"text/plain",
37 BAD_CAST
"rtf", BAD_CAST
"application/rtf",
38 BAD_CAST
"doc", BAD_CAST
"application/msword",
39 BAD_CAST
"xls", BAD_CAST
"application/vnd.ms-excel",
40 BAD_CAST
"ppt", BAD_CAST
"application/vnd.ms-powerpoint",
41 BAD_CAST
"jpg", BAD_CAST
"image/jpeg",
42 BAD_CAST
"jpeg", BAD_CAST
"image/jpeg",
43 BAD_CAST
"jfif", BAD_CAST
"image/jpeg",
44 BAD_CAST
"png", BAD_CAST
"image/png",
45 BAD_CAST
"tiff", BAD_CAST
"image/tiff",
46 BAD_CAST
"gif", BAD_CAST
"image/gif",
47 BAD_CAST
"mpeg1", BAD_CAST
"video/mpeg",
48 BAD_CAST
"mpeg2", BAD_CAST
"video/mpeg2",
49 BAD_CAST
"wav", BAD_CAST
"audio/x-wav",
50 BAD_CAST
"mp2", BAD_CAST
"audio/mpeg",
51 BAD_CAST
"mp3", BAD_CAST
"audio/mpeg",
52 /* TODO: Add MIME types for ISDOC, X.509 certificates, CMS and TST */
55 /* Deallocate structure isds_pki_credentials and NULL it.
56 * Pass-phrase is discarded.
57 * @pki credentials to to free */
58 void isds_pki_credentials_free(struct isds_pki_credentials
**pki
) {
59 if(!pki
|| !*pki
) return;
62 free((*pki
)->certificate
);
65 if ((*pki
)->passphrase
) {
66 memset((*pki
)->passphrase
, 0, strlen((*pki
)->passphrase
));
67 free((*pki
)->passphrase
);
74 /* Free isds_list with all member data.
75 * @list list to free, on return will be NULL */
76 void isds_list_free(struct isds_list
**list
) {
77 struct isds_list
*item
, *next_item
;
79 if (!list
|| !*list
) return;
81 for(item
= *list
; item
; item
= next_item
) {
82 (item
->destructor
)(&(item
->data
));
83 next_item
= item
->next
;
91 /* Deallocate structure isds_hash and NULL it.
92 * @hash hash to to free */
93 void isds_hash_free(struct isds_hash
**hash
) {
94 if(!hash
|| !*hash
) return;
100 /* Deallocate structure isds_PersonName recursively and NULL it */
101 static void isds_PersonName_free(struct isds_PersonName
**person_name
) {
102 if (!person_name
|| !*person_name
) return;
104 free((*person_name
)->pnFirstName
);
105 free((*person_name
)->pnMiddleName
);
106 free((*person_name
)->pnLastName
);
107 free((*person_name
)->pnLastNameAtBirth
);
114 /* Deallocate structure isds_BirthInfo recursively and NULL it */
115 static void isds_BirthInfo_free(struct isds_BirthInfo
**birth_info
) {
116 if (!birth_info
|| !*birth_info
) return;
118 free((*birth_info
)->biDate
);
119 free((*birth_info
)->biCity
);
120 free((*birth_info
)->biCounty
);
121 free((*birth_info
)->biState
);
128 /* Deallocate structure isds_Address recursively and NULL it */
129 static void isds_Address_free(struct isds_Address
**address
) {
130 if (!address
|| !*address
) return;
132 free((*address
)->adCity
);
133 free((*address
)->adStreet
);
134 free((*address
)->adNumberInStreet
);
135 free((*address
)->adNumberInMunicipality
);
136 free((*address
)->adZipCode
);
137 free((*address
)->adState
);
144 /* Deallocate structure isds_DbOwnerInfo recursively and NULL it */
145 void isds_DbOwnerInfo_free(struct isds_DbOwnerInfo
**db_owner_info
) {
146 if (!db_owner_info
|| !*db_owner_info
) return;
148 free((*db_owner_info
)->dbID
);
149 free((*db_owner_info
)->dbType
);
150 free((*db_owner_info
)->ic
);
151 isds_PersonName_free(&((*db_owner_info
)->personName
));
152 free((*db_owner_info
)->firmName
);
153 isds_BirthInfo_free(&((*db_owner_info
)->birthInfo
));
154 isds_Address_free(&((*db_owner_info
)->address
));
155 free((*db_owner_info
)->nationality
);
156 free((*db_owner_info
)->email
);
157 free((*db_owner_info
)->telNumber
);
158 free((*db_owner_info
)->identifier
);
159 free((*db_owner_info
)->registryCode
);
160 free((*db_owner_info
)->dbState
);
161 free((*db_owner_info
)->dbEffectiveOVM
);
163 free(*db_owner_info
);
164 *db_owner_info
= NULL
;
167 /* Deallocate structure isds_DbUserInfo recursively and NULL it */
168 void isds_DbUserInfo_free(struct isds_DbUserInfo
**db_user_info
) {
169 if (!db_user_info
|| !*db_user_info
) return;
171 free((*db_user_info
)->userID
);
172 free((*db_user_info
)->userType
);
173 free((*db_user_info
)->userPrivils
);
174 isds_PersonName_free(&((*db_user_info
)->personName
));
175 isds_Address_free(&((*db_user_info
)->address
));
176 free((*db_user_info
)->biDate
);
177 free((*db_user_info
)->ic
);
178 free((*db_user_info
)->firmName
);
179 free((*db_user_info
)->caStreet
);
180 free((*db_user_info
)->caCity
);
181 free((*db_user_info
)->caZipCode
);
182 free((*db_user_info
)->caState
);
184 zfree(*db_user_info
);
188 /* Deallocate struct isds_event recursively and NULL it */
189 void isds_event_free(struct isds_event
**event
) {
190 if (!event
|| !*event
) return;
192 free((*event
)->time
);
193 free((*event
)->type
);
194 free((*event
)->description
);
199 /* Deallocate struct isds_envelope recursively and NULL it */
200 void isds_envelope_free(struct isds_envelope
**envelope
) {
201 if (!envelope
|| !*envelope
) return;
203 free((*envelope
)->dmID
);
204 free((*envelope
)->dbIDSender
);
205 free((*envelope
)->dmSender
);
206 free((*envelope
)->dmSenderAddress
);
207 free((*envelope
)->dmSenderType
);
208 free((*envelope
)->dmRecipient
);
209 free((*envelope
)->dmRecipientAddress
);
210 free((*envelope
)->dmAmbiguousRecipient
);
211 free((*envelope
)->dmType
);
213 free((*envelope
)->dmOrdinal
);
214 free((*envelope
)->dmMessageStatus
);
215 free((*envelope
)->dmDeliveryTime
);
216 free((*envelope
)->dmAcceptanceTime
);
217 isds_hash_free(&(*envelope
)->hash
);
218 free((*envelope
)->timestamp
);
219 isds_list_free(&(*envelope
)->events
);
221 free((*envelope
)->dmSenderOrgUnit
);
222 free((*envelope
)->dmSenderOrgUnitNum
);
223 free((*envelope
)->dbIDRecipient
);
224 free((*envelope
)->dmRecipientOrgUnit
);
225 free((*envelope
)->dmRecipientOrgUnitNum
);
226 free((*envelope
)->dmToHands
);
227 free((*envelope
)->dmAnnotation
);
228 free((*envelope
)->dmRecipientRefNumber
);
229 free((*envelope
)->dmSenderRefNumber
);
230 free((*envelope
)->dmRecipientIdent
);
231 free((*envelope
)->dmSenderIdent
);
233 free((*envelope
)->dmLegalTitleLaw
);
234 free((*envelope
)->dmLegalTitleYear
);
235 free((*envelope
)->dmLegalTitleSect
);
236 free((*envelope
)->dmLegalTitlePar
);
237 free((*envelope
)->dmLegalTitlePoint
);
239 free((*envelope
)->dmPersonalDelivery
);
240 free((*envelope
)->dmAllowSubstDelivery
);
241 free((*envelope
)->dmOVM
);
248 /* Deallocate struct isds_message recursively and NULL it */
249 void isds_message_free(struct isds_message
**message
) {
250 if (!message
|| !*message
) return;
252 free((*message
)->raw
);
253 isds_envelope_free(&((*message
)->envelope
));
254 isds_list_free(&((*message
)->documents
));
261 /* Deallocate struct isds_document recursively and NULL it */
262 void isds_document_free(struct isds_document
**document
) {
263 if (!document
|| !*document
) return;
265 free((*document
)->data
);
266 free((*document
)->dmMimeType
);
267 free((*document
)->dmFileGuid
);
268 free((*document
)->dmUpFileGuid
);
269 free((*document
)->dmFileDescr
);
270 free((*document
)->dmFormat
);
277 /* Deallocate struct isds_message_copy recursively and NULL it */
278 void isds_message_copy_free(struct isds_message_copy
**copy
) {
279 if (!copy
|| !*copy
) return;
281 free((*copy
)->dbIDRecipient
);
282 free((*copy
)->dmRecipientOrgUnit
);
283 free((*copy
)->dmRecipientOrgUnitNum
);
284 free((*copy
)->dmToHands
);
286 free((*copy
)->dmStatus
);
293 /* Deallocate struct isds_approval recursively and NULL it */
294 void isds_approval_free(struct isds_approval
**approval
) {
295 if (!approval
|| !*approval
) return;
297 free((*approval
)->refference
);
303 /* *DUP_OR_ERROR macros needs error label */
304 #define STRDUP_OR_ERROR(new, template) { \
308 (new) = strdup(template); \
309 if (!new) goto error; \
313 #define FLATDUP_OR_ERROR(new, template) { \
317 (new) = malloc(sizeof(*(new))); \
318 if (!new) goto error; \
319 memcpy((new), (template), sizeof(*(template))); \
323 /* Copy structure isds_pki_credentials recursively. */
324 struct isds_pki_credentials
*isds_pki_credentials_duplicate(
325 const struct isds_pki_credentials
*template) {
326 struct isds_pki_credentials
*new = NULL
;
328 if(!template) return NULL
;
330 new = calloc(1, sizeof(*new));
331 if (!new) return NULL
;
333 STRDUP_OR_ERROR(new->engine
, template->engine
);
334 new->certificate_format
= template->certificate_format
;
335 STRDUP_OR_ERROR(new->certificate
, template->certificate
);
336 new->key_format
= template->key_format
;
337 STRDUP_OR_ERROR(new->key
, template->key
);
338 STRDUP_OR_ERROR(new->passphrase
, template->passphrase
);
343 isds_pki_credentials_free(&new);
348 /* Copy structure isds_PersonName recursively */
349 struct isds_PersonName
*isds_PersonName_duplicate(
350 const struct isds_PersonName
*template) {
351 struct isds_PersonName
*new = NULL
;
353 if (!template) return NULL
;
355 new = calloc(1, sizeof(*new));
356 if (!new) return NULL
;
358 STRDUP_OR_ERROR(new->pnFirstName
, template->pnFirstName
);
359 STRDUP_OR_ERROR(new->pnMiddleName
, template->pnMiddleName
);
360 STRDUP_OR_ERROR(new->pnLastName
, template->pnLastName
);
361 STRDUP_OR_ERROR(new->pnLastNameAtBirth
, template->pnLastNameAtBirth
);
366 isds_PersonName_free(&new);
371 /* Copy structure isds_BirthInfo recursively */
372 static struct isds_BirthInfo
*isds_BirthInfo_duplicate(
373 const struct isds_BirthInfo
*template) {
374 struct isds_BirthInfo
*new = NULL
;
376 if (!template) return NULL
;
378 new = calloc(1, sizeof(*new));
379 if (!new) return NULL
;
381 FLATDUP_OR_ERROR(new->biDate
, template->biDate
);
382 STRDUP_OR_ERROR(new->biCity
, template->biCity
);
383 STRDUP_OR_ERROR(new->biCounty
, template->biCounty
);
384 STRDUP_OR_ERROR(new->biState
, template->biState
);
389 isds_BirthInfo_free(&new);
394 /* Copy structure isds_Address recursively */
395 struct isds_Address
*isds_Address_duplicate(
396 const struct isds_Address
*template) {
397 struct isds_Address
*new = NULL
;
399 if (!template) return NULL
;
401 new = calloc(1, sizeof(*new));
402 if (!new) return NULL
;
404 STRDUP_OR_ERROR(new->adCity
, template->adCity
);
405 STRDUP_OR_ERROR(new->adStreet
, template->adStreet
);
406 STRDUP_OR_ERROR(new->adNumberInStreet
, template->adNumberInStreet
);
407 STRDUP_OR_ERROR(new->adNumberInMunicipality
,
408 template->adNumberInMunicipality
);
409 STRDUP_OR_ERROR(new->adZipCode
, template->adZipCode
);
410 STRDUP_OR_ERROR(new->adState
, template->adState
);
415 isds_Address_free(&new);
420 /* Copy structure isds_DbOwnerInfo recursively */
421 struct isds_DbOwnerInfo
*isds_DbOwnerInfo_duplicate(
422 const struct isds_DbOwnerInfo
*template) {
423 struct isds_DbOwnerInfo
*new = NULL
;
424 if (!template) return NULL
;
426 new = calloc(1, sizeof(*new));
427 if (!new) return NULL
;
429 STRDUP_OR_ERROR(new->dbID
, template->dbID
);
430 FLATDUP_OR_ERROR(new->dbType
, template->dbType
);
431 STRDUP_OR_ERROR(new->ic
, template->ic
);
433 if (template->personName
) {
434 if (!(new->personName
=
435 isds_PersonName_duplicate(template->personName
)))
439 STRDUP_OR_ERROR(new->firmName
, template->firmName
);
441 if (template->birthInfo
) {
442 if (!(new->birthInfo
=
443 isds_BirthInfo_duplicate(template->birthInfo
)))
447 if (template->address
) {
448 if (!(new->address
= isds_Address_duplicate(template->address
)))
452 STRDUP_OR_ERROR(new->nationality
, template->nationality
);
453 STRDUP_OR_ERROR(new->email
, template->email
);
454 STRDUP_OR_ERROR(new->telNumber
, template->telNumber
);
455 STRDUP_OR_ERROR(new->identifier
, template->identifier
);
456 STRDUP_OR_ERROR(new->registryCode
, template->registryCode
);
457 FLATDUP_OR_ERROR(new->dbState
, template->dbState
);
458 FLATDUP_OR_ERROR(new->dbEffectiveOVM
, template->dbEffectiveOVM
);
459 FLATDUP_OR_ERROR(new->dbOpenAddressing
, template->dbOpenAddressing
);
464 isds_DbOwnerInfo_free(&new);
469 /* Copy structure isds_DbUserInfo recursively */
470 struct isds_DbUserInfo
*isds_DbUserInfo_duplicate(
471 const struct isds_DbUserInfo
*template) {
472 struct isds_DbUserInfo
*new = NULL
;
473 if (!template) return NULL
;
475 new = calloc(1, sizeof(*new));
476 if (!new) return NULL
;
478 STRDUP_OR_ERROR(new->userID
, template->userID
);
479 FLATDUP_OR_ERROR(new->userType
, template->userType
);
480 FLATDUP_OR_ERROR(new->userPrivils
, template->userPrivils
);
482 if (template->personName
) {
483 if (!(new->personName
=
484 isds_PersonName_duplicate(template->personName
)))
488 if (template->address
) {
489 if (!(new->address
= isds_Address_duplicate(template->address
)))
493 FLATDUP_OR_ERROR(new->biDate
, template->biDate
);
494 STRDUP_OR_ERROR(new->ic
, template->ic
);
495 STRDUP_OR_ERROR(new->firmName
, template->firmName
);
496 STRDUP_OR_ERROR(new->caStreet
, template->caStreet
);
497 STRDUP_OR_ERROR(new->caCity
, template->caCity
);
498 STRDUP_OR_ERROR(new->caZipCode
, template->caZipCode
);
499 STRDUP_OR_ERROR(new->caState
, template->caState
);
504 isds_DbUserInfo_free(&new);
508 #undef FLATDUP_OR_ERROR
509 #undef STRDUP_OR_ERROR
512 /* Initialize ISDS library.
513 * Global function, must be called before other functions.
514 * If it fails you can not use ISDS library and must call isds_cleanup() to
515 * free partially initialized global variables. */
516 isds_error
isds_init(void) {
517 /* NULL global variables */
518 log_facilities
= ILF_ALL
;
519 log_level
= ILL_WARNING
;
521 log_callback_data
= NULL
;
524 /* Initialize gettext */
525 bindtextdomain(PACKAGE
, LOCALEDIR
);
528 /* Initialize CURL */
529 if (curl_global_init(CURL_GLOBAL_ALL
)) {
530 isds_log(ILF_ISDS
, ILL_CRIT
, _("CURL library initialization failed\n"));
534 /* Initialize gpg-error because of gpgme and ksba */
535 if (gpg_err_init()) {
536 isds_log(ILF_ISDS
, ILL_CRIT
,
537 _("gpg-error library initialization failed\n"));
541 /* Initialize GPGME */
542 if (_isds_init_gpgme(&version_gpgme
)) {
543 isds_log(ILF_ISDS
, ILL_CRIT
,
544 _("GPGME library initialization failed\n"));
548 /* Initialize gcrypt */
549 if (_isds_init_gcrypt(&version_gcrypt
)) {
550 isds_log(ILF_ISDS
, ILL_CRIT
,
551 _("gcrypt library initialization failed\n"));
555 /* This can _exit() current program. Find not so assertive check. */
559 if (_isds_init_expat(&version_expat
)) {
560 isds_log(ILF_ISDS
, ILL_CRIT
,
561 _("expat library initialization failed\n"));
565 /* Allocate global variables */
572 /* Deinitialize ISDS library.
573 * Global function, must be called as last library function. */
574 isds_error
isds_cleanup(void) {
579 curl_global_cleanup();
585 /* Return version string of this library. Version of dependencies can be
586 * embedded. Do no try to parse it. You must free it. */
587 char *isds_version(void) {
590 isds_asprintf(&buffer
, _("%s (%s, GPGME %s, gcrypt %s, %s, libxml2 %s)"),
591 PACKAGE_VERSION
, curl_version(), version_gpgme
, version_gcrypt
,
592 version_expat
, xmlParserVersion
);
597 /* Return text description of ISDS error */
598 const char *isds_strerror(const isds_error error
) {
601 return(_("Success")); break;
603 return(_("Unspecified error")); break;
605 return(_("Not supported")); break;
607 return(_("Invalid value")); break;
608 case IE_INVALID_CONTEXT
:
609 return(_("Invalid context")); break;
610 case IE_NOT_LOGGED_IN
:
611 return(_("Not logged in")); break;
612 case IE_CONNECTION_CLOSED
:
613 return(_("Connection closed")); break;
615 return(_("Timed out")); break;
617 return(_("Not exist")); break;
619 return(_("Out of memory")); break;
621 return(_("Network problem")); break;
623 return(_("HTTP problem")); break;
625 return(_("SOAP problem")); break;
627 return(_("XML problem")); break;
629 return(_("ISDS server problem")); break;
631 return(_("Invalid enum value")); break;
633 return(_("Invalid date value")); break;
635 return(_("Too big")); break;
637 return(_("Too small")); break;
639 return(_("Value not unique")); break;
641 return(_("Values not equal")); break;
642 case IE_PARTIAL_SUCCESS
:
643 return(_("Some suboperations failed")); break;
645 return(_("Operation aborted")); break;
647 return(_("Unknown error"));
652 /* Create ISDS context.
653 * Each context can be used for different sessions to (possibly) different
654 * ISDS server with different credentials. */
655 struct isds_ctx
*isds_ctx_create(void) {
656 struct isds_ctx
*context
;
657 context
= malloc(sizeof(*context
));
658 if (context
) memset(context
, 0, sizeof(*context
));
663 /* Close possibly opened connection to Czech POINT document deposit without
664 * resetting long_message buffer.
665 * XXX: Do not use czp_close_connection() if you do not want to destroy log
667 * @context is Czech POINT session context. */
668 static isds_error
czp_do_close_connection(struct isds_ctx
*context
) {
669 if (!context
) return IE_INVALID_CONTEXT
;
670 _isds_close_connection(context
);
675 /* Discard credentials.
676 * Only that. It does not cause log out, connection close or similar. */
677 static isds_error
discard_credentials(struct isds_ctx
*context
) {
678 if(!context
) return IE_INVALID_CONTEXT
;
680 if (context
->username
) {
681 memset(context
->username
, 0, strlen(context
->username
));
682 zfree(context
->username
);
684 if (context
->password
) {
685 memset(context
->password
, 0, strlen(context
->password
));
686 zfree(context
->password
);
688 isds_pki_credentials_free(&context
->pki_credentials
);
694 /* Destroy ISDS context and free memory.
695 * @context will be NULLed on success. */
696 isds_error
isds_ctx_free(struct isds_ctx
**context
) {
697 if (!context
|| !*context
) {
698 return IE_INVALID_CONTEXT
;
701 /* Discard credentials and close connection */
702 switch ((*context
)->type
) {
703 case CTX_TYPE_NONE
: break;
704 case CTX_TYPE_ISDS
: isds_logout(*context
); break;
706 case CTX_TYPE_TESTING_REQUEST_COLLECTOR
:
707 czp_do_close_connection(*context
); break;
711 discard_credentials(*context
);
713 /* Free other structures */
714 free((*context
)->tls_verify_server
);
715 free((*context
)->tls_ca_file
);
716 free((*context
)->tls_ca_dir
);
717 free((*context
)->tls_crl_file
);
718 free((*context
)->long_message
);
726 /* Return long message text produced by library function, e.g. detailed error
727 * message. Returned pointer is only valid until new library function is
728 * called for the same context. Could be NULL, especially if NULL context is
729 * supplied. Return string is locale encoded. */
730 char *isds_long_message(const struct isds_ctx
*context
) {
731 if (!context
) return NULL
;
732 return context
->long_message
;
736 /* Stores message into context' long_message buffer.
737 * Application can pick the message up using isds_long_message().
738 * NULL @message truncates the buffer but does not deallocate it.
739 * @message is coded in locale encoding */
740 _hidden isds_error
isds_log_message(struct isds_ctx
*context
,
741 const char *message
) {
745 if (!context
) return IE_INVALID_CONTEXT
;
747 /* FIXME: Check for integer overflow */
748 length
= 1 + ((message
) ? strlen(message
) : 0);
749 buffer
= realloc(context
->long_message
, length
);
750 if (!buffer
) return IE_NOMEM
;
753 strcpy(buffer
, message
);
757 context
->long_message
= buffer
;
762 /* Appends message into context' long_message buffer.
763 * Application can pick the message up using isds_long_message().
764 * NULL message has void effect. */
765 _hidden isds_error
isds_append_message(struct isds_ctx
*context
,
766 const char *message
) {
768 size_t old_length
, length
;
770 if (!context
) return IE_INVALID_CONTEXT
;
771 if (!message
) return IE_SUCCESS
;
772 if (!context
->long_message
)
773 return isds_log_message(context
, message
);
775 old_length
= strlen(context
->long_message
);
776 /* FIXME: Check for integer overflow */
777 length
= 1 + old_length
+ strlen(message
);
778 buffer
= realloc(context
->long_message
, length
);
779 if (!buffer
) return IE_NOMEM
;
781 strcpy(buffer
+ old_length
, message
);
783 context
->long_message
= buffer
;
788 /* Stores formatted message into context' long_message buffer.
789 * Application can pick the message up using isds_long_message(). */
790 _hidden isds_error
isds_printf_message(struct isds_ctx
*context
,
791 const char *format
, ...) {
795 if (!context
) return IE_INVALID_CONTEXT
;
796 va_start(ap
, format
);
797 length
= isds_vasprintf(&(context
->long_message
), format
, ap
);
800 return (length
< 0) ? IE_ERROR
: IE_SUCCESS
;
805 * @facilities is bit mask of isds_log_facility values,
806 * @level is verbosity level. */
807 void isds_set_logging(const unsigned int facilities
,
808 const isds_log_level level
) {
809 log_facilities
= facilities
;
814 /* Register callback function libisds calls when new global log message is
815 * produced by library. Library logs to stderr by default.
816 * @callback is function provided by application libisds will call. See type
817 * definition for @callback argument explanation. Pass NULL to revert logging to
819 * @data is application specific data @callback gets as last argument */
820 void isds_set_log_callback(isds_log_callback callback
, void *data
) {
821 log_callback
= callback
;
822 log_callback_data
= data
;
826 /* Log @message in class @facility with log @level into global log. @message
827 * is printf(3) formatting string, variadic arguments may be necessary.
828 * For debugging purposes. */
829 _hidden isds_error
isds_log(const isds_log_facility facility
,
830 const isds_log_level level
, const char *message
, ...) {
835 if (level
> log_level
) return IE_SUCCESS
;
836 if (!(log_facilities
& facility
)) return IE_SUCCESS
;
837 if (!message
) return IE_INVAL
;
840 /* Pass message to application supplied callback function */
841 va_start(ap
, message
);
842 length
= isds_vasprintf(&buffer
, message
, ap
);
849 log_callback(facility
, level
, buffer
, length
, log_callback_data
);
853 /* Default: Log it to stderr */
854 va_start(ap
, message
);
855 vfprintf(stderr
, message
, ap
);
857 /* Line buffered printf is default.
865 /* Set timeout in milliseconds for each network job like connecting to server
866 * or sending message. Use 0 to disable timeout limits. */
867 isds_error
isds_set_timeout(struct isds_ctx
*context
,
868 const unsigned int timeout
) {
869 if (!context
) return IE_INVALID_CONTEXT
;
870 zfree(context
->long_message
);
872 context
->timeout
= timeout
;
877 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_NOSIGNAL
, 1);
879 #if HAVE_DECL_CURLOPT_TIMEOUT_MS /* Since curl-7.16.2 */
880 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_TIMEOUT_MS
,
883 curl_err
= curl_easy_setopt(context
->curl
, CURLOPT_TIMEOUT
,
884 context
->timeout
/ 1000);
885 #endif /* not HAVE_DECL_CURLOPT_TIMEOUT_MS */
886 if (curl_err
) return IE_ERROR
;
893 /* Register callback function libisds calls periodically during HTTP data
895 * @context is session context
896 * @callback is function provided by application libisds will call. See type
897 * definition for @callback argument explanation.
898 * @data is application specific data @callback gets as last argument */
899 isds_error
isds_set_progress_callback(struct isds_ctx
*context
,
900 isds_progress_callback callback
, void *data
) {
901 if (!context
) return IE_INVALID_CONTEXT
;
902 zfree(context
->long_message
);
904 context
->progress_callback
= callback
;
905 context
->progress_callback_data
= data
;
911 /* Change context settings.
912 * @context is context which setting will be applied to
913 * @option is name of option. It determines the type of last argument. See
914 * isds_option definition for more info.
915 * @... is value of new setting. Type is determined by @option
917 isds_error
isds_set_opt(struct isds_ctx
*context
, const isds_option option
,
919 isds_error err
= IE_SUCCESS
;
921 char *pointer
, *string
;
923 if (!context
) return IE_INVALID_CONTEXT
;
924 zfree(context
->long_message
);
926 va_start(ap
, option
);
928 #define REPLACE_VA_BOOLEAN(destination) { \
929 if (!(destination)) { \
930 (destination) = malloc(sizeof(*(destination))); \
931 if (!(destination)) { \
932 err = IE_NOMEM; goto leave; \
935 *(destination) = (_Bool) !!va_arg(ap, int); \
938 #define REPLACE_VA_STRING(destination) { \
939 string = va_arg(ap, char *); \
941 pointer = realloc((destination), 1 + strlen(string)); \
942 if (!pointer) { err = IE_NOMEM; goto leave; } \
943 strcpy(pointer, string); \
944 (destination) = pointer; \
947 (destination) = NULL; \
952 case IOPT_TLS_VERIFY_SERVER
:
953 REPLACE_VA_BOOLEAN(context
->tls_verify_server
);
955 case IOPT_TLS_CA_FILE
:
956 REPLACE_VA_STRING(context
->tls_ca_file
);
958 case IOPT_TLS_CA_DIRECTORY
:
959 REPLACE_VA_STRING(context
->tls_ca_dir
);
961 case IOPT_TLS_CRL_FILE
:
962 #if HAVE_DECL_CURLOPT_CRLFILE /* Since curl-7.19.0 */
963 REPLACE_VA_STRING(context
->tls_crl_file
);
965 isds_log_message(context
,
966 _("Curl library does not support CRL definition"));
968 #endif /* not HAVE_DECL_CURLOPT_CRLFILE */
970 case IOPT_NORMALIZE_MIME_TYPE
:
971 context
->normalize_mime_type
= (_Bool
) !!va_arg(ap
, int);
975 err
= IE_ENUM
; goto leave
;
978 #undef REPLACE_VA_STRING
979 #undef REPLACE_VA_BOOLEAN
987 /* Deprecated: Use isds_set_opt() instead.
988 * Change SSL/TLS settings.
989 * @context is context which setting will be applied to
990 * @option is name of option. It determines the type of last argument. See
991 * isds_tls_option definition for more info.
992 * @... is value of new setting. Type is determined by @option
994 isds_error
isds_set_tls(struct isds_ctx
*context
, const isds_tls_option option
,
996 isds_error err
= IE_ENUM
;
999 va_start(ap
, option
);
1002 case ITLS_VERIFY_SERVER
:
1003 err
= isds_set_opt(context
, option
, va_arg(ap
, int));
1006 case ITLS_CA_DIRECTORY
:
1008 err
= isds_set_opt(context
, option
, va_arg(ap
, char*));
1016 /* Connect and log in into ISDS server.
1017 * All required arguments will be copied, you do not have to keep them after
1019 * ISDS supports four different authentication methods. Exact method is
1020 * selected on @username, @passwors and @pki_credentials arguments:
1021 * - If @pki_credentials == NULL, @username and @password must be supplied
1022 * - If @pki_credentials != NULL, then
1023 * - If @username == NULL, only certificate will be used
1024 * - If @username != NULL, then
1025 * - If @password == NULL, then certificate will be used and
1026 * @username shifts meaning to box ID. This is used for hosted
1028 * - Otherwise all three arguments will be used.
1029 * Please note, that different cases requires different certificate type
1030 * (system qualified one or commercial non qualified one). This library does
1031 * not check such political issues. Please see ISDS Specification for more
1033 * @url is base address of ISDS web service. Pass extern isds_locator
1034 * variable to use production ISDS instance without client certificate
1035 * authentication (or extern isds_cert_locator with client certificate
1036 * authentication). Passing NULL has the same effect, autoselection between
1037 * isds_locator and isds_cert_locator is performed in addition. You can pass
1038 * extern isds_testing_locator (or isds_cert_testing_locator) variable to
1039 * select testing instance.
1040 * @username is user name of ISDS user or box ID
1041 * @password is user's secret password
1042 * @pki_credentials defines public key cryptographic material to use in client
1043 * authentication. */
1044 isds_error
isds_login(struct isds_ctx
*context
, const char *url
,
1045 const char *username
, const char *password
,
1046 const struct isds_pki_credentials
*pki_credentials
) {
1047 isds_error err
= IE_NOT_LOGGED_IN
;
1048 isds_error soap_err
;
1049 xmlNsPtr isds_ns
= NULL
;
1050 xmlNodePtr request
= NULL
;
1051 xmlNodePtr response
= NULL
;
1053 if (!context
) return IE_INVALID_CONTEXT
;
1054 zfree(context
->long_message
);
1056 /* Close connection if already logged in */
1057 if (context
->curl
) {
1058 _isds_close_connection(context
);
1061 /* Store configuration */
1062 context
->type
= CTX_TYPE_ISDS
;
1063 zfree(context
->url
);
1065 /* Mangle base URI according requested authentication method */
1066 if (!pki_credentials
) {
1067 isds_log(ILF_SEC
, ILL_INFO
,
1068 _("Selected authentication method: no certificate, "
1069 "username and password\n"));
1070 if (!username
|| !password
) {
1071 isds_log_message(context
,
1072 _("Both username and password must be supplied"));
1075 /* Default locator is official system (without client certificate) */
1076 context
->url
= strdup((url
) ? url
: isds_locator
);
1078 /* Default locator is official system (with client certificate) */
1079 if (!url
) url
= isds_cert_locator
;
1082 isds_log(ILF_SEC
, ILL_INFO
,
1083 _("Selected authentication method: system certificate, "
1084 "no username and no password\n"));
1086 context
->url
= _isds_astrcat(url
, "cert/");
1089 isds_log(ILF_SEC
, ILL_INFO
,
1090 _("Selected authentication method: system certificate, "
1091 "box ID and no password\n"));
1092 context
->url
= _isds_astrcat(url
, "hspis/");
1094 isds_log(ILF_SEC
, ILL_INFO
,
1095 _("Selected authentication method: commercial "
1096 "certificate, username and password\n"));
1097 context
->url
= _isds_astrcat(url
, "certds/");
1101 if (!(context
->url
))
1104 /* Prepare CURL handle */
1105 context
->curl
= curl_easy_init();
1106 if (!(context
->curl
))
1109 /* Build log-in request */
1110 request
= xmlNewNode(NULL
, BAD_CAST
"DummyOperation");
1112 isds_log_message(context
, _("Could build ISDS log-in request"));
1115 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
1117 isds_log_message(context
, _("Could not create ISDS name space"));
1118 xmlFreeNode(request
);
1121 xmlSetNs(request
, isds_ns
);
1123 /* Store credentials */
1124 /* FIXME: mlock password
1125 * (I have a library) */
1126 discard_credentials(context
);
1127 if (username
) context
->username
= strdup(username
);
1128 if (password
) context
->password
= strdup(password
);
1129 context
->pki_credentials
= isds_pki_credentials_duplicate(pki_credentials
);
1130 if ((username
&& !context
->username
) || (password
&& !context
->password
) ||
1131 (pki_credentials
&& !context
->pki_credentials
)) {
1132 discard_credentials(context
);
1133 xmlFreeNode(request
);
1137 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Logging user %s into server %s\n"),
1140 /* Send log-in request */
1141 soap_err
= _isds_soap(context
, "DS/dz", request
, &response
, NULL
, NULL
);
1143 /* Remove credentials */
1144 discard_credentials(context
);
1146 /* Destroy log-in request */
1147 xmlFreeNode(request
);
1150 xmlFreeNodeList(response
);
1151 _isds_close_connection(context
);
1155 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1156 * authentication succeeded if soap_err == IE_SUCCESS */
1159 xmlFreeNodeList(response
);
1162 isds_log(ILF_ISDS
, ILL_DEBUG
,
1163 _("User %s has been logged into server %s successfully\n"),
1169 /* Log out from ISDS server discards credentials and connection configuration. */
1170 isds_error
isds_logout(struct isds_ctx
*context
) {
1171 if (!context
) return IE_INVALID_CONTEXT
;
1172 zfree(context
->long_message
);
1174 /* Close connection */
1175 if (context
->curl
) {
1176 _isds_close_connection(context
);
1178 /* Discard credentials for sure. They should not survive isds_login(),
1179 * even successful .*/
1180 discard_credentials(context
);
1181 zfree(context
->url
);
1183 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Logged out from ISDS server\n"));
1185 discard_credentials(context
);
1191 /* Verify connection to ISDS is alive and server is responding.
1192 * Sent dummy request to ISDS and expect dummy response. */
1193 isds_error
isds_ping(struct isds_ctx
*context
) {
1194 isds_error soap_err
;
1195 xmlNsPtr isds_ns
= NULL
;
1196 xmlNodePtr request
= NULL
;
1197 xmlNodePtr response
= NULL
;
1199 if (!context
) return IE_INVALID_CONTEXT
;
1200 zfree(context
->long_message
);
1202 /* Check if connection is established */
1203 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
1206 /* Build dummy request */
1207 request
= xmlNewNode(NULL
, BAD_CAST
"DummyOperation");
1209 isds_log_message(context
, _("Could build ISDS dummy request"));
1212 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
1214 isds_log_message(context
, _("Could not create ISDS name space"));
1215 xmlFreeNode(request
);
1218 xmlSetNs(request
, isds_ns
);
1220 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Pinging ISDS server\n"));
1222 /* Sent dummy request */
1223 soap_err
= _isds_soap(context
, "DS/dz", request
, &response
, NULL
, NULL
);
1225 /* Destroy log-in request */
1226 xmlFreeNode(request
);
1229 isds_log(ILF_ISDS
, ILL_DEBUG
,
1230 _("ISDS server could not be contacted\n"));
1231 xmlFreeNodeList(response
);
1235 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1236 * authentication succeeded if soap_err == IE_SUCCESS */
1237 /* TODO: ISDS documentation does not specify response body.
1238 * However real server sends back DummyOperationResponse */
1241 xmlFreeNodeList(response
);
1243 isds_log(ILF_ISDS
, ILL_DEBUG
, _("ISDS server alive\n"));
1249 /* Send bogus request to ISDS.
1250 * Just for test purposes */
1251 isds_error
isds_bogus_request(struct isds_ctx
*context
) {
1253 xmlNsPtr isds_ns
= NULL
;
1254 xmlNodePtr request
= NULL
;
1255 xmlDocPtr response
= NULL
;
1256 xmlChar
*code
= NULL
, *message
= NULL
;
1258 if (!context
) return IE_INVALID_CONTEXT
;
1259 zfree(context
->long_message
);
1261 /* Check if connection is established */
1262 if (!context
->curl
) {
1263 /* Testing printf message */
1264 isds_printf_message(context
, "%s", _("I said connection closed"));
1265 return IE_CONNECTION_CLOSED
;
1269 /* Build dummy request */
1270 request
= xmlNewNode(NULL
, BAD_CAST
"X-BogusOperation");
1272 isds_log_message(context
, _("Could build ISDS bogus request"));
1275 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
1277 isds_log_message(context
, _("Could not create ISDS name space"));
1278 xmlFreeNode(request
);
1281 xmlSetNs(request
, isds_ns
);
1283 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending bogus request to ISDS\n"));
1285 /* Sent bogus request */
1286 err
= isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
1288 /* Destroy request */
1289 xmlFreeNode(request
);
1292 isds_log(ILF_ISDS
, ILL_DEBUG
,
1293 _("Processing ISDS response on bogus request failed\n"));
1294 xmlFreeDoc(response
);
1298 /* Check for response status */
1299 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
1300 &code
, &message
, NULL
);
1302 isds_log(ILF_ISDS
, ILL_DEBUG
,
1303 _("ISDS response on bogus request is missing status\n"));
1306 xmlFreeDoc(response
);
1309 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
1310 char *code_locale
= _isds_utf82locale((char*)code
);
1311 char *message_locale
= _isds_utf82locale((char*)message
);
1312 isds_log(ILF_ISDS
, ILL_DEBUG
,
1313 _("Server refused bogus request (code=%s, message=%s)\n"),
1314 code_locale
, message_locale
);
1315 /* XXX: Literal error messages from ISDS are Czech messages
1316 * (English sometimes) in UTF-8. It's hard to catch them for
1317 * translation. Successfully gettextized would return in locale
1318 * encoding, unsuccessfully translated would pass in UTF-8. */
1319 isds_log_message(context
, message_locale
);
1321 free(message_locale
);
1324 xmlFreeDoc(response
);
1331 xmlFreeDoc(response
);
1333 isds_log(ILF_ISDS
, ILL_DEBUG
,
1334 _("Bogus message accepted by server. This should not happen.\n"));
1340 /* Serialize XML subtree to buffer preserving XML indentation.
1341 * @context is session context
1342 * @subtree is XML element to be serialized (with children)
1343 * @buffer is automatically reallocated buffer where serialize to
1344 * @length is size of serialized stream in bytes
1345 * @return standard error code, free @buffer in case of error */
1346 static isds_error
serialize_subtree(struct isds_ctx
*context
,
1347 xmlNodePtr subtree
, void **buffer
, size_t *length
) {
1348 isds_error err
= IE_SUCCESS
;
1349 xmlBufferPtr xml_buffer
= NULL
;
1350 xmlSaveCtxtPtr save_ctx
= NULL
;
1351 xmlDocPtr subtree_doc
= NULL
;
1352 xmlNodePtr subtree_copy
;
1356 if (!context
) return IE_INVALID_CONTEXT
;
1357 if (!buffer
) return IE_INVAL
;
1359 if (!subtree
|| !length
) return IE_INVAL
;
1361 /* Make temporary XML document with @subtree root element */
1362 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1363 * It can result in not well-formed on invalid XML tree (e.g. name space
1364 * prefix definition can miss. */
1367 subtree_doc
= xmlNewDoc(BAD_CAST
"1.0");
1369 isds_log_message(context
, _("Could not build temporary document"));
1374 /* XXX: Copy subtree and attach the copy to document.
1375 * One node can not bee attached into more document at the same time.
1376 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1378 * XXX: Check xmlSaveTree() too. */
1379 subtree_copy
= xmlCopyNodeList(subtree
);
1380 if (!subtree_copy
) {
1381 isds_log_message(context
, _("Could not copy subtree"));
1385 xmlDocSetRootElement(subtree_doc
, subtree_copy
);
1387 /* Only this way we get namespace definition as @xmlns:isds,
1388 * otherwise we get namespace prefix without definition */
1389 /* FIXME: Don't overwrite original default namespace */
1390 isds_ns
= xmlNewNs(subtree_copy
, BAD_CAST ISDS_NS
, NULL
);
1392 isds_log_message(context
, _("Could not create ISDS name space"));
1396 xmlSetNs(subtree_copy
, isds_ns
);
1399 /* Serialize the document into buffer */
1400 xml_buffer
= xmlBufferCreate();
1402 isds_log_message(context
, _("Could not create xmlBuffer"));
1406 /* Last argument 0 means to not format the XML tree */
1407 save_ctx
= xmlSaveToBuffer(xml_buffer
, "UTF-8", 0);
1409 isds_log_message(context
, _("Could not create XML serializer"));
1413 /* XXX: According LibXML documentation, this function does not return
1414 * meaningful value yet */
1415 xmlSaveDoc(save_ctx
, subtree_doc
);
1416 if (-1 == xmlSaveFlush(save_ctx
)) {
1417 isds_log_message(context
,
1418 _("Could not serialize XML subtree"));
1422 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1423 * even after xmlSaveFlush(). Thus close it here */
1424 xmlSaveClose(save_ctx
); save_ctx
= NULL
;
1427 /* Store and detach buffer from xml_buffer */
1428 *buffer
= xml_buffer
->content
;
1429 *length
= xml_buffer
->use
;
1430 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
1433 new_buffer
= realloc(*buffer
, *length
);
1434 if (new_buffer
) *buffer
= new_buffer
;
1442 xmlSaveClose(save_ctx
);
1443 xmlBufferFree(xml_buffer
);
1444 xmlFreeDoc(subtree_doc
); /* Frees subtree_copy, isds_ns etc. */
1449 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1450 * @context is session context
1451 * @document is original document where @nodeset points to
1452 * @nodeset is XPath node set to dump (recursively)
1453 * @buffer is automatically reallocated buffer where serialize to
1454 * @length is size of serialized stream in bytes
1455 * @return standard error code, free @buffer in case of error */
1456 static isds_error
dump_nodeset(struct isds_ctx
*context
,
1457 const xmlDocPtr document
, const xmlNodeSetPtr nodeset
,
1458 void **buffer
, size_t *length
) {
1459 isds_error err
= IE_SUCCESS
;
1460 xmlBufferPtr xml_buffer
= NULL
;
1463 if (!context
) return IE_INVALID_CONTEXT
;
1464 if (!buffer
) return IE_INVAL
;
1466 if (!document
|| !nodeset
|| !length
) return IE_INVAL
;
1469 /* Empty node set results into NULL buffer */
1470 if (xmlXPathNodeSetIsEmpty(nodeset
)) {
1474 /* Resulting the document into buffer */
1475 xml_buffer
= xmlBufferCreate();
1477 isds_log_message(context
, _("Could not create xmlBuffer"));
1482 /* Iterate over all nodes */
1483 for (int i
= 0; i
< nodeset
->nodeNr
; i
++) {
1485 * XXX: xmlNodeDump() appends to xml_buffer. */
1487 xmlNodeDump(xml_buffer
, document
, nodeset
->nodeTab
[i
], 0, 0)) {
1488 isds_log_message(context
, _("Could not dump XML node"));
1494 /* Store and detach buffer from xml_buffer */
1495 *buffer
= xml_buffer
->content
;
1496 *length
= xml_buffer
->use
;
1497 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
1500 new_buffer
= realloc(*buffer
, *length
);
1501 if (new_buffer
) *buffer
= new_buffer
;
1510 xmlBufferFree(xml_buffer
);
1516 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1517 * @context is session context
1518 * @document is original document where @nodeset points to
1519 * @nodeset is XPath node set to dump (recursively)
1520 * @buffer is automatically reallocated buffer where serialize to
1521 * @length is size of serialized stream in bytes
1522 * @return standard error code, free @buffer in case of error */
1523 static isds_error
dump_nodeset(struct isds_ctx
*context
,
1524 const xmlDocPtr document
, const xmlNodeSetPtr nodeset
,
1525 void **buffer
, size_t *length
) {
1526 isds_error err
= IE_SUCCESS
;
1527 xmlBufferPtr xml_buffer
= NULL
;
1528 xmlSaveCtxtPtr save_ctx
= NULL
;
1531 if (!context
) return IE_INVALID_CONTEXT
;
1532 if (!buffer
) return IE_INVAL
;
1534 if (!document
|| !nodeset
|| !length
) return IE_INVAL
;
1537 /* Empty node set results into NULL buffer */
1538 if (xmlXPathNodeSetIsEmpty(nodeset
)) {
1542 /* Resulting the document into buffer */
1543 xml_buffer
= xmlBufferCreate();
1545 isds_log_message(context
, _("Could not create xmlBuffer"));
1549 if (xmlSubstituteEntitiesDefault(1)) {
1550 isds_log_message(context
, _("Could not disable attribute escaping"));
1554 /* Last argument means:
1555 * 0 to not format the XML tree
1556 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1557 save_ctx
= xmlSaveToBuffer(xml_buffer
, "UTF-8",
1558 XML_SAVE_NO_DECL
|XML_SAVE_NO_EMPTY
|XML_SAVE_NO_XHTML
);
1560 isds_log_message(context
, _("Could not create XML serializer"));
1564 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1565 isds_log_message(context, _("Could not disable attribute escaping"));
1571 /* Iterate over all nodes */
1572 for (int i
= 0; i
< nodeset
->nodeNr
; i
++) {
1574 * XXX: xmlNodeDump() appends to xml_buffer. */
1576 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1578 /* XXX: According LibXML documentation, this function does not return
1579 * meaningful value yet */
1580 xmlSaveTree(save_ctx
, nodeset
->nodeTab
[i
]);
1581 if (-1 == xmlSaveFlush(save_ctx
)) {
1582 isds_log_message(context
,
1583 _("Could not serialize XML subtree"));
1589 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1590 * even after xmlSaveFlush(). Thus close it here */
1591 xmlSaveClose(save_ctx
); save_ctx
= NULL
;
1593 /* Store and detach buffer from xml_buffer */
1594 *buffer
= xml_buffer
->content
;
1595 *length
= xml_buffer
->use
;
1596 xmlBufferSetAllocationScheme(xml_buffer
, XML_BUFFER_ALLOC_IMMUTABLE
);
1599 new_buffer
= realloc(*buffer
, *length
);
1600 if (new_buffer
) *buffer
= new_buffer
;
1608 xmlSaveClose(save_ctx
);
1609 xmlBufferFree(xml_buffer
);
1615 /* Convert UTF-8 @string representation of ISDS dbType to enum @type */
1616 static isds_error
string2isds_DbType(xmlChar
*string
, isds_DbType
*type
) {
1617 if (!string
|| !type
) return IE_INVAL
;
1619 if (!xmlStrcmp(string
, BAD_CAST
"FO"))
1621 else if (!xmlStrcmp(string
, BAD_CAST
"PFO"))
1623 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_ADVOK"))
1624 *type
= DBTYPE_PFO_ADVOK
;
1625 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_DANPOR"))
1626 *type
= DBTYPE_PFO_DANPOR
;
1627 else if (!xmlStrcmp(string
, BAD_CAST
"PFO_INSSPR"))
1628 *type
= DBTYPE_PFO_INSSPR
;
1629 else if (!xmlStrcmp(string
, BAD_CAST
"PO"))
1631 else if (!xmlStrcmp(string
, BAD_CAST
"PO_ZAK"))
1632 *type
= DBTYPE_PO_ZAK
;
1633 else if (!xmlStrcmp(string
, BAD_CAST
"PO_REQ"))
1634 *type
= DBTYPE_PO_REQ
;
1635 else if (!xmlStrcmp(string
, BAD_CAST
"OVM"))
1637 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_NOTAR"))
1638 *type
= DBTYPE_OVM_NOTAR
;
1639 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_EXEKUT"))
1640 *type
= DBTYPE_OVM_EXEKUT
;
1641 else if (!xmlStrcmp(string
, BAD_CAST
"OVM_REQ"))
1642 *type
= DBTYPE_OVM_REQ
;
1649 /* Convert ISDS dbType enum @type to UTF-8 string.
1650 * @Return pointer to static string, or NULL if unknown enum value */
1651 static const xmlChar
*isds_DbType2string(const isds_DbType type
) {
1653 /* DBTYPE_SYSTEM is invalid value from point of view of public
1654 * SOAP interface. */
1655 case DBTYPE_FO
: return(BAD_CAST
"FO"); break;
1656 case DBTYPE_PFO
: return(BAD_CAST
"PFO"); break;
1657 case DBTYPE_PFO_ADVOK
: return(BAD_CAST
"PFO_ADVOK"); break;
1658 case DBTYPE_PFO_DANPOR
: return(BAD_CAST
"PFO_DANPOR"); break;
1659 case DBTYPE_PFO_INSSPR
: return(BAD_CAST
"PFO_INSSPR"); break;
1660 case DBTYPE_PO
: return(BAD_CAST
"PO"); break;
1661 case DBTYPE_PO_ZAK
: return(BAD_CAST
"PO_ZAK"); break;
1662 case DBTYPE_PO_REQ
: return(BAD_CAST
"PO_REQ"); break;
1663 case DBTYPE_OVM
: return(BAD_CAST
"OVM"); break;
1664 case DBTYPE_OVM_NOTAR
: return(BAD_CAST
"OVM_NOTAR"); break;
1665 case DBTYPE_OVM_EXEKUT
: return(BAD_CAST
"OVM_EXEKUT"); break;
1666 case DBTYPE_OVM_REQ
: return(BAD_CAST
"OVM_REQ"); break;
1667 default: return NULL
; break;
1672 /* Convert UTF-8 @string representation of ISDS userType to enum @type */
1673 static isds_error
string2isds_UserType(xmlChar
*string
, isds_UserType
*type
) {
1674 if (!string
|| !type
) return IE_INVAL
;
1676 if (!xmlStrcmp(string
, BAD_CAST
"PRIMARY_USER"))
1677 *type
= USERTYPE_PRIMARY
;
1678 else if (!xmlStrcmp(string
, BAD_CAST
"ENTRUSTED_USER"))
1679 *type
= USERTYPE_ENTRUSTED
;
1680 else if (!xmlStrcmp(string
, BAD_CAST
"ADMINISTRATOR"))
1681 *type
= USERTYPE_ADMINISTRATOR
;
1682 else if (!xmlStrcmp(string
, BAD_CAST
"OFFICIAL"))
1683 *type
= USERTYPE_OFFICIAL
;
1690 /* Convert ISDS userType enum @type to UTF-8 string.
1691 * @Return pointer to static string, or NULL if unknown enum value */
1692 static const xmlChar
*isds_UserType2string(const isds_UserType type
) {
1694 case USERTYPE_PRIMARY
: return(BAD_CAST
"PRIMARY_USER"); break;
1695 case USERTYPE_ENTRUSTED
: return(BAD_CAST
"ENTRUSTED_USER"); break;
1696 case USERTYPE_ADMINISTRATOR
: return(BAD_CAST
"ADMINISTRATOR"); break;
1697 case USERTYPE_OFFICIAL
: return(BAD_CAST
"OFFICIAL"); break;
1698 default: return NULL
; break;
1703 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
1704 * @Return pointer to static string, or NULL if unknown enum value */
1705 static const xmlChar
*isds_FileMetaType2string(const isds_FileMetaType type
) {
1707 case FILEMETATYPE_MAIN
: return(BAD_CAST
"main"); break;
1708 case FILEMETATYPE_ENCLOSURE
: return(BAD_CAST
"enclosure"); break;
1709 case FILEMETATYPE_SIGNATURE
: return(BAD_CAST
"signature"); break;
1710 case FILEMETATYPE_META
: return(BAD_CAST
"meta"); break;
1711 default: return NULL
; break;
1716 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
1717 * @Return IE_ENUM if @string is not valid enum member */
1718 static isds_error
string2isds_FileMetaType(const xmlChar
*string
,
1719 isds_FileMetaType
*type
) {
1720 if (!string
|| !type
) return IE_INVAL
;
1722 if (!xmlStrcmp(string
, BAD_CAST
"main"))
1723 *type
= FILEMETATYPE_MAIN
;
1724 else if (!xmlStrcmp(string
, BAD_CAST
"enclosure"))
1725 *type
= FILEMETATYPE_ENCLOSURE
;
1726 else if (!xmlStrcmp(string
, BAD_CAST
"signature"))
1727 *type
= FILEMETATYPE_SIGNATURE
;
1728 else if (!xmlStrcmp(string
, BAD_CAST
"meta"))
1729 *type
= FILEMETATYPE_META
;
1736 /* Convert UTF-8 @string to ISDS hash @algorithm.
1737 * @Return IE_ENUM if @string is not valid enum member */
1738 static isds_error
string2isds_hash_algorithm(const xmlChar
*string
,
1739 isds_hash_algorithm
*algorithm
) {
1740 if (!string
|| !algorithm
) return IE_INVAL
;
1742 if (!xmlStrcmp(string
, BAD_CAST
"MD5"))
1743 *algorithm
= HASH_ALGORITHM_MD5
;
1744 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-1"))
1745 *algorithm
= HASH_ALGORITHM_SHA_1
;
1746 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-224"))
1747 *algorithm
= HASH_ALGORITHM_SHA_224
;
1748 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-256"))
1749 *algorithm
= HASH_ALGORITHM_SHA_256
;
1750 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-384"))
1751 *algorithm
= HASH_ALGORITHM_SHA_384
;
1752 else if (!xmlStrcmp(string
, BAD_CAST
"SHA-512"))
1753 *algorithm
= HASH_ALGORITHM_SHA_512
;
1760 /* Convert UTF-8 @string representation of ISO 8601 date to @time.
1761 * XXX: Not all ISO formats are supported */
1762 static isds_error
datestring2tm(const xmlChar
*string
, struct tm
*time
) {
1764 if (!string
|| !time
) return IE_INVAL
;
1766 /* xsd:date is ISO 8601 string, thus ASCII */
1767 offset
= strptime((char*)string
, "%Y-%m-%d", time
);
1768 if (offset
&& *offset
== '\0')
1771 offset
= strptime((char*)string
, "%Y%m%d", time
);
1772 if (offset
&& *offset
== '\0')
1775 offset
= strptime((char*)string
, "%Y-%j", time
);
1776 if (offset
&& *offset
== '\0')
1783 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
1784 static isds_error
tm2datestring(const struct tm
*time
, xmlChar
**string
) {
1785 if (!time
|| !string
) return IE_INVAL
;
1787 if (-1 == isds_asprintf((char **) string
, "%d-%02d-%02d",
1788 time
->tm_year
+ 1900, time
->tm_mon
+ 1, time
->tm_mday
))
1795 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
1796 * respects the @time microseconds too. */
1797 static isds_error
timeval2timestring(const struct timeval
*time
,
1801 if (!time
|| !string
) return IE_INVAL
;
1803 if (!gmtime_r(&time
->tv_sec
, &broken
)) return IE_DATE
;
1804 if (time
->tv_usec
< 0 || time
->tv_usec
> 999999) return IE_DATE
;
1806 /* TODO: small negative year should be formatted as "-0012". This is not
1807 * true for glibc "%04d". We should implement it.
1808 * TODO: What's type of time->tv_usec exactly? Unsigned? Absolute?
1809 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
1810 if (-1 == isds_asprintf((char **) string
,
1811 "%04d-%02d-%02dT%02d:%02d:%02d.%06ld",
1812 broken
.tm_year
+ 1900, broken
.tm_mon
+ 1, broken
.tm_mday
,
1813 broken
.tm_hour
, broken
.tm_min
, broken
.tm_sec
,
1821 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
1822 * It respects microseconds too.
1823 * In case of error, @time will be freed. */
1824 static isds_error
timestring2timeval(const xmlChar
*string
,
1825 struct timeval
**time
) {
1827 char *offset
, *delim
, *endptr
;
1829 int offset_hours
, offset_minutes
;
1832 if (!time
) return IE_INVAL
;
1834 memset(&broken
, 0, sizeof(broken
));
1837 *time
= calloc(1, sizeof(**time
));
1838 if (!*time
) return IE_NOMEM
;
1840 memset(*time
, 0, sizeof(**time
));
1844 /* xsd:date is ISO 8601 string, thus ASCII */
1845 /*TODO: negative year */
1847 /* Parse date and time without subseconds and offset */
1848 offset
= strptime((char*)string
, "%Y-%m-%dT%T", &broken
);
1850 free(*time
); *time
= NULL
;
1854 /* Get subseconds */
1855 if (*offset
== '.' ) {
1858 /* Copy first 6 digits, pad it with zeros.
1859 * XXX: It truncates longer number, no round.
1860 * Current server implementation uses only millisecond resolution. */
1861 /* TODO: isdigit() is locale sensitive */
1863 i
< sizeof(subseconds
)/sizeof(char) - 1 && isdigit(*offset
);
1865 subseconds
[i
] = *offset
;
1867 for (; i
< sizeof(subseconds
)/sizeof(char) - 1; i
++) {
1868 subseconds
[i
] = '0';
1870 subseconds
[6] = '\0';
1872 /* Convert it into integer */
1873 (*time
)->tv_usec
= strtol(subseconds
, &endptr
, 10);
1874 if (*endptr
!= '\0' || (*time
)->tv_usec
== LONG_MIN
||
1875 (*time
)->tv_usec
== LONG_MAX
) {
1876 free(*time
); *time
= NULL
;
1880 /* move to the zone offset delimiter */
1881 delim
= strchr(offset
, '-');
1883 delim
= strchr(offset
, '+');
1887 /* Get zone offset */
1888 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
1889 * "" equals to "Z" and it means UTC zone. */
1890 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
1891 * colon separator */
1892 if (*offset
== '-' || *offset
== '+') {
1894 if (2 != sscanf(offset
, "%2d:%2d", &offset_hours
, &offset_minutes
)) {
1895 free(*time
); *time
= NULL
;
1898 broken
.tm_hour
-= offset_hours
;
1899 broken
.tm_min
-= offset_minutes
* ((offset_hours
<0) ? -1 : 1);
1902 /* Convert to time_t */
1903 _isds_switch_tz_to_utc();
1904 (*time
)->tv_sec
= mktime(&broken
);
1905 _isds_switch_tz_to_native();
1906 if ((*time
)->tv_sec
== (time_t) -1) {
1907 free(*time
); *time
= NULL
;
1915 /* Convert unsigned int into isds_message_status.
1916 * @context is session context
1917 * @number is pointer to number value. NULL will be treated as invalid value.
1918 * @status is automatically reallocated status
1919 * @return IE_SUCCESS, or error code and free status */
1920 static isds_error
uint2isds_message_status(struct isds_ctx
*context
,
1921 const unsigned long int *number
, isds_message_status
**status
) {
1922 if (!context
) return IE_INVALID_CONTEXT
;
1923 if (!status
) return IE_INVAL
;
1925 free(*status
); *status
= NULL
;
1926 if (!number
) return IE_INVAL
;
1928 if (*number
< 1 || *number
> 10) {
1929 isds_printf_message(context
, _("Invalid message status value: %lu"),
1934 *status
= malloc(sizeof(**status
));
1935 if (!*status
) return IE_NOMEM
;
1937 **status
= 1 << *number
;
1942 /* Convert event description string into isds_event members type and
1944 * @string is raw event description starting with event prefix
1945 * @event is structure where to store type and stripped description to
1946 * @return standard error code, unknown prefix is not classified as an error.
1948 static isds_error
eventstring2event(const xmlChar
*string
,
1949 struct isds_event
* event
) {
1950 const xmlChar
*known_prefixes
[] = {
1956 const isds_event_type types
[] = {
1957 EVENT_ACCEPTED_BY_RECIPIENT
,
1958 EVENT_ACCEPTED_BY_FICTION
,
1959 EVENT_UNDELIVERABLE
,
1960 EVENT_COMMERCIAL_ACCEPTED
1965 if (!string
|| !event
) return IE_INVAL
;
1968 event
->type
= malloc(sizeof(*event
->type
));
1969 if (!(event
->type
)) return IE_NOMEM
;
1971 zfree(event
->description
);
1973 for (index
= 0; index
< sizeof(known_prefixes
)/sizeof(known_prefixes
[0]);
1975 length
= xmlUTF8Strlen(known_prefixes
[index
]);
1977 if (!xmlStrncmp(string
, known_prefixes
[index
], length
)) {
1978 /* Prefix is known */
1979 *event
->type
= types
[index
];
1981 /* Strip prefix from description and spaces */
1982 /* TODO: Recognize all white spaces from UCS blank class and
1983 * operate on UTF-8 chars. */
1984 for (; string
[length
] != '\0' && string
[length
] == ' '; length
++);
1985 event
->description
= strdup((char *) (string
+ length
));
1986 if (!(event
->description
)) return IE_NOMEM
;
1992 /* Unknown event prefix.
1993 * XSD allows any string */
1994 char *string_locale
= _isds_utf82locale((char *) string
);
1995 isds_log(ILF_ISDS
, ILL_WARNING
,
1996 _("Unknown delivery info event prefix: %s\n"), string_locale
);
1997 free(string_locale
);
1999 *event
->type
= EVENT_UKNOWN
;
2000 event
->description
= strdup((char *) string
);
2001 if (!(event
->description
)) return IE_NOMEM
;
2007 /* Following EXTRACT_* macros expects @result, @xpath_ctx, @err, @context
2008 * and leave label */
2009 #define EXTRACT_STRING(element, string) { \
2010 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2015 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2016 if (result->nodesetval->nodeNr > 1) { \
2017 isds_printf_message(context, _("Multiple %s element"), element); \
2021 (string) = (char *) \
2022 xmlXPathCastNodeSetToString(result->nodesetval); \
2030 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2032 char *string = NULL; \
2033 EXTRACT_STRING(element, string); \
2036 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2037 if (!(booleanPtr)) { \
2043 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2044 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2045 *(booleanPtr) = 1; \
2046 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2047 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2048 *(booleanPtr) = 0; \
2050 char *string_locale = _isds_utf82locale((char*)string); \
2051 isds_printf_message(context, \
2052 _("%s value is not valid boolean: %s"), \
2053 element, string_locale); \
2054 free(string_locale); \
2064 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2066 char *string = NULL; \
2067 EXTRACT_STRING(element, string); \
2072 number = strtol((char*)string, &endptr, 10); \
2074 if (*endptr != '\0') { \
2075 char *string_locale = _isds_utf82locale((char *)string); \
2076 isds_printf_message(context, \
2077 _("%s is not valid integer: %s"), \
2078 element, string_locale); \
2079 free(string_locale); \
2085 if (number == LONG_MIN || number == LONG_MAX) { \
2086 char *string_locale = _isds_utf82locale((char *)string); \
2087 isds_printf_message(context, \
2088 _("%s value out of range of long int: %s"), \
2089 element, string_locale); \
2090 free(string_locale); \
2096 free(string); string = NULL; \
2098 if (!(preallocated)) { \
2099 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2100 if (!(longintPtr)) { \
2105 *(longintPtr) = number; \
2109 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2111 char *string = NULL; \
2112 EXTRACT_STRING(element, string); \
2117 number = strtol((char*)string, &endptr, 10); \
2119 if (*endptr != '\0') { \
2120 char *string_locale = _isds_utf82locale((char *)string); \
2121 isds_printf_message(context, \
2122 _("%s is not valid integer: %s"), \
2123 element, string_locale); \
2124 free(string_locale); \
2130 if (number == LONG_MIN || number == LONG_MAX) { \
2131 char *string_locale = _isds_utf82locale((char *)string); \
2132 isds_printf_message(context, \
2133 _("%s value out of range of long int: %s"), \
2134 element, string_locale); \
2135 free(string_locale); \
2141 free(string); string = NULL; \
2143 isds_printf_message(context, \
2144 _("%s value is negative: %ld"), element, number); \
2149 if (!(preallocated)) { \
2150 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2151 if (!(ulongintPtr)) { \
2156 *(ulongintPtr) = number; \
2160 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2161 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2163 if ((required) && (!string)) { \
2164 char *attribute_locale = _isds_utf82locale(attribute); \
2165 char *element_locale = \
2166 _isds_utf82locale((char *)xpath_ctx->node->name); \
2167 isds_printf_message(context, \
2168 _("Could not extract required %s attribute value from " \
2169 "%s element"), attribute_locale, element_locale); \
2170 free(element_locale); \
2171 free(attribute_locale); \
2178 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2180 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2181 (xmlChar *) (string)); \
2183 isds_printf_message(context, \
2184 _("Could not add %s child to %s element"), \
2185 element, (parent)->name); \
2191 #define INSERT_STRING(parent, element, string) \
2192 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2194 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2196 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2197 else { INSERT_STRING(parent, element, "false"); } \
2200 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2203 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2205 INSERT_STRING(parent, element, NULL); \
2209 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2210 if ((longintPtr)) { \
2211 /* FIXME: locale sensitive */ \
2212 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2216 INSERT_STRING(parent, element, buffer) \
2217 free(buffer); (buffer) = NULL; \
2218 } else { INSERT_STRING(parent, element, NULL) } \
2221 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2222 if ((ulongintPtr)) { \
2223 /* FIXME: locale sensitive */ \
2224 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2228 INSERT_STRING(parent, element, buffer) \
2229 free(buffer); (buffer) = NULL; \
2230 } else { INSERT_STRING(parent, element, NULL) } \
2233 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2235 /* FIXME: locale sensitive */ \
2236 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2240 INSERT_STRING(parent, element, buffer) \
2241 free(buffer); (buffer) = NULL; \
2244 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2246 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2248 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2249 (xmlChar *) (string)); \
2250 if (!attribute_node) { \
2251 isds_printf_message(context, _("Could not add %s " \
2252 "attribute to %s element"), \
2253 (attribute), (parent)->name); \
2259 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2261 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2262 if (length > (maximum)) { \
2263 isds_printf_message(context, \
2264 ngettext("%s has more than %d characters", \
2265 "%s has more than %d characters", (maximum)), \
2266 (name), (maximum)); \
2270 if (length < (minimum)) { \
2271 isds_printf_message(context, \
2272 ngettext("%s has less than %d characters", \
2273 "%s has less than %d characters", (minimum)), \
2274 (name), (minimum)); \
2281 #define INSERT_ELEMENT(child, parent, element) \
2283 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2285 isds_printf_message(context, \
2286 _("Could not add %s child to %s element"), \
2287 (element), (parent)->name); \
2294 /* Find child element by name in given XPath context and switch context onto
2295 * it. The child must be uniq and must exist. Otherwise fails.
2296 * @context is ISDS context
2297 * @child is child element name
2298 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2299 * into it child. In error case, the @xpath_ctx keeps original value. */
2300 static isds_error
move_xpathctx_to_child(struct isds_ctx
*context
,
2301 const xmlChar
*child
, xmlXPathContextPtr xpath_ctx
) {
2302 isds_error err
= IE_SUCCESS
;
2303 xmlXPathObjectPtr result
= NULL
;
2305 if (!context
) return IE_INVALID_CONTEXT
;
2306 if (!child
|| !xpath_ctx
) return IE_INVAL
;
2309 result
= xmlXPathEvalExpression(child
, xpath_ctx
);
2316 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
2317 char *parent_locale
= _isds_utf82locale((char*) xpath_ctx
->node
->name
);
2318 char *child_locale
= _isds_utf82locale((char*) child
);
2319 isds_printf_message(context
,
2320 _("%s element does not contain %s child"),
2321 parent_locale
, child_locale
);
2323 free(parent_locale
);
2329 if (result
->nodesetval
->nodeNr
> 1) {
2330 char *parent_locale
= _isds_utf82locale((char*) xpath_ctx
->node
->name
);
2331 char *child_locale
= _isds_utf82locale((char*) child
);
2332 isds_printf_message(context
,
2333 _("%s element contains multiple %s children"),
2334 parent_locale
, child_locale
);
2336 free(parent_locale
);
2341 /* Switch context */
2342 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
2345 xmlXPathFreeObject(result
);
2351 /* Find and convert XSD:gPersonName group in current node into structure
2352 * @context is ISDS context
2353 * @personName is automatically reallocated person name structure. If no member
2354 * value is found, will be freed.
2355 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2357 * In case of error @personName will be freed. */
2358 static isds_error
extract_gPersonName(struct isds_ctx
*context
,
2359 struct isds_PersonName
**personName
, xmlXPathContextPtr xpath_ctx
) {
2360 isds_error err
= IE_SUCCESS
;
2361 xmlXPathObjectPtr result
= NULL
;
2363 if (!context
) return IE_INVALID_CONTEXT
;
2364 if (!personName
) return IE_INVAL
;
2365 isds_PersonName_free(personName
);
2366 if (!xpath_ctx
) return IE_INVAL
;
2369 *personName
= calloc(1, sizeof(**personName
));
2375 EXTRACT_STRING("isds:pnFirstName", (*personName
)->pnFirstName
);
2376 EXTRACT_STRING("isds:pnMiddleName", (*personName
)->pnMiddleName
);
2377 EXTRACT_STRING("isds:pnLastName", (*personName
)->pnLastName
);
2378 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName
)->pnLastNameAtBirth
);
2380 if (!(*personName
)->pnFirstName
&& !(*personName
)->pnMiddleName
&&
2381 !(*personName
)->pnLastName
&& !(*personName
)->pnLastNameAtBirth
)
2382 isds_PersonName_free(personName
);
2385 if (err
) isds_PersonName_free(personName
);
2386 xmlXPathFreeObject(result
);
2391 /* Find and convert XSD:gAddress group in current node into structure
2392 * @context is ISDS context
2393 * @address is automatically reallocated address structure. If no member
2394 * value is found, will be freed.
2395 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2397 * In case of error @address will be freed. */
2398 static isds_error
extract_gAddress(struct isds_ctx
*context
,
2399 struct isds_Address
**address
, xmlXPathContextPtr xpath_ctx
) {
2400 isds_error err
= IE_SUCCESS
;
2401 xmlXPathObjectPtr result
= NULL
;
2403 if (!context
) return IE_INVALID_CONTEXT
;
2404 if (!address
) return IE_INVAL
;
2405 isds_Address_free(address
);
2406 if (!xpath_ctx
) return IE_INVAL
;
2409 *address
= calloc(1, sizeof(**address
));
2415 EXTRACT_STRING("isds:adCity", (*address
)->adCity
);
2416 EXTRACT_STRING("isds:adStreet", (*address
)->adStreet
);
2417 EXTRACT_STRING("isds:adNumberInStreet", (*address
)->adNumberInStreet
);
2418 EXTRACT_STRING("isds:adNumberInMunicipality",
2419 (*address
)->adNumberInMunicipality
);
2420 EXTRACT_STRING("isds:adZipCode", (*address
)->adZipCode
);
2421 EXTRACT_STRING("isds:adState", (*address
)->adState
);
2423 if (!(*address
)->adCity
&& !(*address
)->adStreet
&&
2424 !(*address
)->adNumberInStreet
&&
2425 !(*address
)->adNumberInMunicipality
&&
2426 !(*address
)->adZipCode
&& !(*address
)->adState
)
2427 isds_Address_free(address
);
2430 if (err
) isds_Address_free(address
);
2431 xmlXPathFreeObject(result
);
2436 /* Find and convert isds:biDate element in current node into structure
2437 * @context is ISDS context
2438 * @biDate is automatically reallocated birth date structure. If no member
2439 * value is found, will be freed.
2440 * @xpath_ctx is XPath context with current node as parent for isds:biDate
2442 * In case of error @biDate will be freed. */
2443 static isds_error
extract_BiDate(struct isds_ctx
*context
,
2444 struct tm
**biDate
, xmlXPathContextPtr xpath_ctx
) {
2445 isds_error err
= IE_SUCCESS
;
2446 xmlXPathObjectPtr result
= NULL
;
2447 char *string
= NULL
;
2449 if (!context
) return IE_INVALID_CONTEXT
;
2450 if (!biDate
) return IE_INVAL
;
2452 if (!xpath_ctx
) return IE_INVAL
;
2454 EXTRACT_STRING("isds:biDate", string
);
2456 *biDate
= calloc(1, sizeof(**biDate
));
2461 err
= datestring2tm((xmlChar
*)string
, *biDate
);
2463 if (err
== IE_NOTSUP
) {
2465 char *string_locale
= _isds_utf82locale(string
);
2466 isds_printf_message(context
,
2467 _("Invalid isds:biDate value: %s"), string_locale
);
2468 free(string_locale
);
2475 if (err
) zfree(*biDate
);
2477 xmlXPathFreeObject(result
);
2482 /* Convert isds:dBOwnerInfo XML tree into structure
2483 * @context is ISDS context
2484 * @db_owner_info is automatically reallocated box owner info structure
2485 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
2486 * In case of error @db_owner_info will be freed. */
2487 static isds_error
extract_DbOwnerInfo(struct isds_ctx
*context
,
2488 struct isds_DbOwnerInfo
**db_owner_info
,
2489 xmlXPathContextPtr xpath_ctx
) {
2490 isds_error err
= IE_SUCCESS
;
2491 xmlXPathObjectPtr result
= NULL
;
2492 char *string
= NULL
;
2494 if (!context
) return IE_INVALID_CONTEXT
;
2495 if (!db_owner_info
) return IE_INVAL
;
2496 isds_DbOwnerInfo_free(db_owner_info
);
2497 if (!xpath_ctx
) return IE_INVAL
;
2500 *db_owner_info
= calloc(1, sizeof(**db_owner_info
));
2501 if (!*db_owner_info
) {
2506 EXTRACT_STRING("isds:dbID", (*db_owner_info
)->dbID
);
2508 EXTRACT_STRING("isds:dbType", string
);
2510 (*db_owner_info
)->dbType
=
2511 calloc(1, sizeof(*((*db_owner_info
)->dbType
)));
2512 if (!(*db_owner_info
)->dbType
) {
2516 err
= string2isds_DbType((xmlChar
*)string
, (*db_owner_info
)->dbType
);
2518 zfree((*db_owner_info
)->dbType
);
2519 if (err
== IE_ENUM
) {
2521 char *string_locale
= _isds_utf82locale(string
);
2522 isds_printf_message(context
, _("Unknown isds:dbType: %s"),
2524 free(string_locale
);
2531 EXTRACT_STRING("isds:ic", (*db_owner_info
)->ic
);
2533 err
= extract_gPersonName(context
, &(*db_owner_info
)->personName
,
2535 if (err
) goto leave
;
2537 EXTRACT_STRING("isds:firmName", (*db_owner_info
)->firmName
);
2539 (*db_owner_info
)->birthInfo
=
2540 calloc(1, sizeof(*((*db_owner_info
)->birthInfo
)));
2541 if (!(*db_owner_info
)->birthInfo
) {
2545 err
= extract_BiDate(context
, &(*db_owner_info
)->birthInfo
->biDate
,
2547 if (err
) goto leave
;
2548 EXTRACT_STRING("isds:biCity", (*db_owner_info
)->birthInfo
->biCity
);
2549 EXTRACT_STRING("isds:biCounty", (*db_owner_info
)->birthInfo
->biCounty
);
2550 EXTRACT_STRING("isds:biState", (*db_owner_info
)->birthInfo
->biState
);
2551 if (!(*db_owner_info
)->birthInfo
->biDate
&&
2552 !(*db_owner_info
)->birthInfo
->biCity
&&
2553 !(*db_owner_info
)->birthInfo
->biCounty
&&
2554 !(*db_owner_info
)->birthInfo
->biState
)
2555 isds_BirthInfo_free(&(*db_owner_info
)->birthInfo
);
2557 err
= extract_gAddress(context
, &(*db_owner_info
)->address
, xpath_ctx
);
2558 if (err
) goto leave
;
2560 EXTRACT_STRING("isds:nationality", (*db_owner_info
)->nationality
);
2561 EXTRACT_STRING("isds:email", (*db_owner_info
)->email
);
2562 EXTRACT_STRING("isds:telNumber", (*db_owner_info
)->telNumber
);
2563 EXTRACT_STRING("isds:identifier", (*db_owner_info
)->identifier
);
2564 EXTRACT_STRING("isds:registryCode", (*db_owner_info
)->registryCode
);
2566 EXTRACT_LONGINT("isds:dbState", (*db_owner_info
)->dbState
, 0);
2568 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info
)->dbEffectiveOVM
);
2569 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
2570 (*db_owner_info
)->dbOpenAddressing
);
2573 if (err
) isds_DbOwnerInfo_free(db_owner_info
);
2575 xmlXPathFreeObject(result
);
2580 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
2581 * @context is session context
2582 * @owner is libisds structure with box description
2583 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
2584 static isds_error
insert_DbOwnerInfo(struct isds_ctx
*context
,
2585 const struct isds_DbOwnerInfo
*owner
, xmlNodePtr db_owner_info
) {
2587 isds_error err
= IE_SUCCESS
;
2589 xmlChar
*string
= NULL
;
2591 if (!context
) return IE_INVALID_CONTEXT
;
2592 if (!owner
|| !db_owner_info
) return IE_INVAL
;
2595 /* Build XSD:tDbOwnerInfo */
2596 CHECK_FOR_STRING_LENGTH(owner
->dbID
, 0, 7, "dbID")
2597 INSERT_STRING(db_owner_info
, "dbID", owner
->dbID
);
2600 if (owner
->dbType
) {
2601 const xmlChar
*type_string
= isds_DbType2string(*(owner
->dbType
));
2603 isds_printf_message(context
, _("Invalid dbType value: %d"),
2608 INSERT_STRING(db_owner_info
, "dbType", type_string
);
2610 INSERT_STRING(db_owner_info
, "ic", owner
->ic
);
2611 if (owner
->personName
) {
2612 INSERT_STRING(db_owner_info
, "pnFirstName",
2613 owner
->personName
->pnFirstName
);
2614 INSERT_STRING(db_owner_info
, "pnMiddleName",
2615 owner
->personName
->pnMiddleName
);
2616 INSERT_STRING(db_owner_info
, "pnLastName",
2617 owner
->personName
->pnLastName
);
2618 INSERT_STRING(db_owner_info
, "pnLastNameAtBirth",
2619 owner
->personName
->pnLastNameAtBirth
);
2621 INSERT_STRING(db_owner_info
, "firmName", owner
->firmName
);
2622 if (owner
->birthInfo
) {
2623 if (owner
->birthInfo
->biDate
) {
2624 if (!tm2datestring(owner
->birthInfo
->biDate
, &string
))
2625 INSERT_STRING(db_owner_info
, "biDate", string
);
2626 free(string
); string
= NULL
;
2628 INSERT_STRING(db_owner_info
, "biCity", owner
->birthInfo
->biCity
);
2629 INSERT_STRING(db_owner_info
, "biCounty", owner
->birthInfo
->biCounty
);
2630 INSERT_STRING(db_owner_info
, "biState", owner
->birthInfo
->biState
);
2632 if (owner
->address
) {
2633 INSERT_STRING(db_owner_info
, "adCity", owner
->address
->adCity
);
2634 INSERT_STRING(db_owner_info
, "adStreet", owner
->address
->adStreet
);
2635 INSERT_STRING(db_owner_info
, "adNumberInStreet",
2636 owner
->address
->adNumberInStreet
);
2637 INSERT_STRING(db_owner_info
, "adNumberInMunicipality",
2638 owner
->address
->adNumberInMunicipality
);
2639 INSERT_STRING(db_owner_info
, "adZipCode", owner
->address
->adZipCode
);
2640 INSERT_STRING(db_owner_info
, "adState", owner
->address
->adState
);
2642 INSERT_STRING(db_owner_info
, "nationality", owner
->nationality
);
2643 INSERT_STRING(db_owner_info
, "email", owner
->email
);
2644 INSERT_STRING(db_owner_info
, "telNumber", owner
->telNumber
);
2646 CHECK_FOR_STRING_LENGTH(owner
->identifier
, 0, 20, "identifier")
2647 INSERT_STRING(db_owner_info
, "identifier", owner
->identifier
);
2649 CHECK_FOR_STRING_LENGTH(owner
->registryCode
, 0, 5, "registryCode")
2650 INSERT_STRING(db_owner_info
, "registryCode", owner
->registryCode
);
2652 INSERT_LONGINT(db_owner_info
, "dbState", owner
->dbState
, string
);
2654 INSERT_BOOLEAN(db_owner_info
, "dbEffectiveOVM", owner
->dbEffectiveOVM
);
2655 INSERT_BOOLEAN(db_owner_info
, "dbOpenAddressing",
2656 owner
->dbOpenAddressing
);
2664 /* Convert XSD:tDbUserInfo XML tree into structure
2665 * @context is ISDS context
2666 * @db_user_info is automatically reallocated user info structure
2667 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
2668 * In case of error @db_user_info will be freed. */
2669 static isds_error
extract_DbUserInfo(struct isds_ctx
*context
,
2670 struct isds_DbUserInfo
**db_user_info
, xmlXPathContextPtr xpath_ctx
) {
2671 isds_error err
= IE_SUCCESS
;
2672 xmlXPathObjectPtr result
= NULL
;
2673 char *string
= NULL
;
2675 if (!context
) return IE_INVALID_CONTEXT
;
2676 if (!db_user_info
) return IE_INVAL
;
2677 isds_DbUserInfo_free(db_user_info
);
2678 if (!xpath_ctx
) return IE_INVAL
;
2681 *db_user_info
= calloc(1, sizeof(**db_user_info
));
2682 if (!*db_user_info
) {
2687 EXTRACT_STRING("isds:userID", (*db_user_info
)->userID
);
2689 EXTRACT_STRING("isds:userType", string
);
2691 (*db_user_info
)->userType
=
2692 calloc(1, sizeof(*((*db_user_info
)->userType
)));
2693 if (!(*db_user_info
)->userType
) {
2697 err
= string2isds_UserType((xmlChar
*)string
,
2698 (*db_user_info
)->userType
);
2700 zfree((*db_user_info
)->userType
);
2701 if (err
== IE_ENUM
) {
2703 char *string_locale
= _isds_utf82locale(string
);
2704 isds_printf_message(context
,
2705 _("Unknown isds:userType value: %s"), string_locale
);
2706 free(string_locale
);
2713 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info
)->userPrivils
, 0);
2715 (*db_user_info
)->personName
=
2716 calloc(1, sizeof(*((*db_user_info
)->personName
)));
2717 if (!(*db_user_info
)->personName
) {
2722 err
= extract_gPersonName(context
, &(*db_user_info
)->personName
,
2724 if (err
) goto leave
;
2726 err
= extract_gAddress(context
, &(*db_user_info
)->address
, xpath_ctx
);
2727 if (err
) goto leave
;
2729 err
= extract_BiDate(context
, &(*db_user_info
)->biDate
, xpath_ctx
);
2730 if (err
) goto leave
;
2732 EXTRACT_STRING("isds:ic", (*db_user_info
)->ic
);
2733 EXTRACT_STRING("isds:firmName", (*db_user_info
)->firmName
);
2735 EXTRACT_STRING("isds:caStreet", (*db_user_info
)->caStreet
);
2736 EXTRACT_STRING("isds:caCity", (*db_user_info
)->caCity
);
2737 EXTRACT_STRING("isds:caZipCode", (*db_user_info
)->caZipCode
);
2739 /* ???: Default value is "CZ" according specification. Should we provide
2741 EXTRACT_STRING("isds:caState", (*db_user_info
)->caState
);
2744 if (err
) isds_DbUserInfo_free(db_user_info
);
2746 xmlXPathFreeObject(result
);
2751 /* Insert struct isds_DbUserInfo data (user description) into XML tree
2752 * @context is session context
2753 * @user is libisds structure with user description
2754 * @db_user_info is XML element of XSD:tDbUserInfo */
2755 static isds_error
insert_DbUserInfo(struct isds_ctx
*context
,
2756 const struct isds_DbUserInfo
*user
, xmlNodePtr db_user_info
) {
2758 isds_error err
= IE_SUCCESS
;
2760 xmlChar
*string
= NULL
;
2762 if (!context
) return IE_INVALID_CONTEXT
;
2763 if (!user
|| !db_user_info
) return IE_INVAL
;
2765 /* Build XSD:tDbUserInfo */
2766 if (user
->personName
) {
2767 INSERT_STRING(db_user_info
, "pnFirstName",
2768 user
->personName
->pnFirstName
);
2769 INSERT_STRING(db_user_info
, "pnMiddleName",
2770 user
->personName
->pnMiddleName
);
2771 INSERT_STRING(db_user_info
, "pnLastName",
2772 user
->personName
->pnLastName
);
2773 INSERT_STRING(db_user_info
, "pnLastNameAtBirth",
2774 user
->personName
->pnLastNameAtBirth
);
2776 if (user
->address
) {
2777 INSERT_STRING(db_user_info
, "adCity", user
->address
->adCity
);
2778 INSERT_STRING(db_user_info
, "adStreet", user
->address
->adStreet
);
2779 INSERT_STRING(db_user_info
, "adNumberInStreet",
2780 user
->address
->adNumberInStreet
);
2781 INSERT_STRING(db_user_info
, "adNumberInMunicipality",
2782 user
->address
->adNumberInMunicipality
);
2783 INSERT_STRING(db_user_info
, "adZipCode", user
->address
->adZipCode
);
2784 INSERT_STRING(db_user_info
, "adState", user
->address
->adState
);
2787 if (!tm2datestring(user
->biDate
, &string
))
2788 INSERT_STRING(db_user_info
, "biDate", string
);
2791 CHECK_FOR_STRING_LENGTH(user
->userID
, 6, 12, "userID");
2792 INSERT_STRING(db_user_info
, "userID", user
->userID
);
2795 if (user
->userType
) {
2796 const xmlChar
*type_string
= isds_UserType2string(*(user
->userType
));
2798 isds_printf_message(context
, _("Invalid userType value: %d"),
2803 INSERT_STRING(db_user_info
, "userType", type_string
);
2806 INSERT_LONGINT(db_user_info
, "userPrivils", user
->userPrivils
, string
);
2807 CHECK_FOR_STRING_LENGTH(user
->ic
, 0, 8, "ic")
2808 INSERT_STRING(db_user_info
, "ic", user
->ic
);
2809 CHECK_FOR_STRING_LENGTH(user
->firmName
, 0, 100, "firmName")
2810 INSERT_STRING(db_user_info
, "firmName", user
->firmName
);
2811 INSERT_STRING(db_user_info
, "caStreet", user
->caStreet
);
2812 INSERT_STRING(db_user_info
, "caCity", user
->caCity
);
2813 INSERT_STRING(db_user_info
, "caZipCode", user
->caZipCode
);
2814 INSERT_STRING(db_user_info
, "caState", user
->caState
);
2822 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
2823 * isds_envelope structure. The envelope is automatically allocated but not
2824 * reallocated. The date are just appended into envelope structure.
2825 * @context is ISDS context
2826 * @envelope is automatically allocated message envelope structure
2827 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
2828 * In case of error @envelope will be freed. */
2829 static isds_error
append_GMessageEnvelopeSub(struct isds_ctx
*context
,
2830 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
2831 isds_error err
= IE_SUCCESS
;
2832 xmlXPathObjectPtr result
= NULL
;
2834 if (!context
) return IE_INVALID_CONTEXT
;
2835 if (!envelope
) return IE_INVAL
;
2836 if (!xpath_ctx
) return IE_INVAL
;
2840 /* Allocate envelope */
2841 *envelope
= calloc(1, sizeof(**envelope
));
2847 /* Else free former data */
2848 zfree((*envelope
)->dmSenderOrgUnit
);
2849 zfree((*envelope
)->dmSenderOrgUnitNum
);
2850 zfree((*envelope
)->dbIDRecipient
);
2851 zfree((*envelope
)->dmRecipientOrgUnit
);
2852 zfree((*envelope
)->dmSenderOrgUnitNum
);
2853 zfree((*envelope
)->dmToHands
);
2854 zfree((*envelope
)->dmAnnotation
);
2855 zfree((*envelope
)->dmRecipientRefNumber
);
2856 zfree((*envelope
)->dmSenderRefNumber
);
2857 zfree((*envelope
)->dmRecipientIdent
);
2858 zfree((*envelope
)->dmSenderIdent
);
2859 zfree((*envelope
)->dmLegalTitleLaw
);
2860 zfree((*envelope
)->dmLegalTitleYear
);
2861 zfree((*envelope
)->dmLegalTitleSect
);
2862 zfree((*envelope
)->dmLegalTitlePar
);
2863 zfree((*envelope
)->dmLegalTitlePoint
);
2864 zfree((*envelope
)->dmPersonalDelivery
);
2865 zfree((*envelope
)->dmAllowSubstDelivery
);
2868 /* Extract envelope elements added by sender or ISDS
2869 * (XSD: gMessageEnvelopeSub type) */
2870 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope
)->dmSenderOrgUnit
);
2871 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
2872 (*envelope
)->dmSenderOrgUnitNum
, 0);
2873 EXTRACT_STRING("isds:dbIDRecipient", (*envelope
)->dbIDRecipient
);
2874 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope
)->dmRecipientOrgUnit
);
2875 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
2876 (*envelope
)->dmSenderOrgUnitNum
, 0);
2877 EXTRACT_STRING("isds:dmToHands", (*envelope
)->dmToHands
);
2878 EXTRACT_STRING("isds:dmAnnotation", (*envelope
)->dmAnnotation
);
2879 EXTRACT_STRING("isds:dmRecipientRefNumber",
2880 (*envelope
)->dmRecipientRefNumber
);
2881 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope
)->dmSenderRefNumber
);
2882 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope
)->dmRecipientIdent
);
2883 EXTRACT_STRING("isds:dmSenderIdent", (*envelope
)->dmSenderIdent
);
2885 /* Extract envelope elements regarding law reference */
2886 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope
)->dmLegalTitleLaw
, 0);
2887 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope
)->dmLegalTitleYear
, 0);
2888 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope
)->dmLegalTitleSect
);
2889 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope
)->dmLegalTitlePar
);
2890 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope
)->dmLegalTitlePoint
);
2892 /* Extract envelope other elements */
2893 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope
)->dmPersonalDelivery
);
2894 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
2895 (*envelope
)->dmAllowSubstDelivery
);
2898 if (err
) isds_envelope_free(envelope
);
2899 xmlXPathFreeObject(result
);
2905 /* Convert XSD gMessageEnvelope group of elements from XML tree into
2906 * isds_envelope structure. The envelope is automatically allocated but not
2907 * reallocated. The date are just appended into envelope structure.
2908 * @context is ISDS context
2909 * @envelope is automatically allocated message envelope structure
2910 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
2911 * In case of error @envelope will be freed. */
2912 static isds_error
append_GMessageEnvelope(struct isds_ctx
*context
,
2913 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
2914 isds_error err
= IE_SUCCESS
;
2915 xmlXPathObjectPtr result
= NULL
;
2917 if (!context
) return IE_INVALID_CONTEXT
;
2918 if (!envelope
) return IE_INVAL
;
2919 if (!xpath_ctx
) return IE_INVAL
;
2923 /* Allocate envelope */
2924 *envelope
= calloc(1, sizeof(**envelope
));
2930 /* Else free former data */
2931 zfree((*envelope
)->dmID
);
2932 zfree((*envelope
)->dbIDSender
);
2933 zfree((*envelope
)->dmSender
);
2934 zfree((*envelope
)->dmSenderAddress
);
2935 zfree((*envelope
)->dmSenderType
);
2936 zfree((*envelope
)->dmRecipient
);
2937 zfree((*envelope
)->dmRecipientAddress
);
2938 zfree((*envelope
)->dmAmbiguousRecipient
);
2941 /* Extract envelope elements added by ISDS
2942 * (XSD: gMessageEnvelope type) */
2943 EXTRACT_STRING("isds:dmID", (*envelope
)->dmID
);
2944 EXTRACT_STRING("isds:dbIDSender", (*envelope
)->dbIDSender
);
2945 EXTRACT_STRING("isds:dmSender", (*envelope
)->dmSender
);
2946 EXTRACT_STRING("isds:dmSenderAddress", (*envelope
)->dmSenderAddress
);
2947 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
2948 EXTRACT_LONGINT("isds:dmSenderType", (*envelope
)->dmSenderType
, 0);
2949 EXTRACT_STRING("isds:dmRecipient", (*envelope
)->dmRecipient
);
2950 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope
)->dmRecipientAddress
);
2951 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
2952 (*envelope
)->dmAmbiguousRecipient
);
2954 /* Extract envelope elements added by sender and ISDS
2955 * (XSD: gMessageEnvelope type) */
2956 err
= append_GMessageEnvelopeSub(context
, envelope
, xpath_ctx
);
2957 if (err
) goto leave
;
2960 if (err
) isds_envelope_free(envelope
);
2961 xmlXPathFreeObject(result
);
2966 /* Convert other envelope elements from XML tree into isds_envelope structure:
2967 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
2968 * The envelope is automatically allocated but not reallocated.
2969 * The data are just appended into envelope structure.
2970 * @context is ISDS context
2971 * @envelope is automatically allocated message envelope structure
2972 * @xpath_ctx is XPath context with current node as parent desired elements
2973 * In case of error @envelope will be freed. */
2974 static isds_error
append_status_size_times(struct isds_ctx
*context
,
2975 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
2976 isds_error err
= IE_SUCCESS
;
2977 xmlXPathObjectPtr result
= NULL
;
2978 char *string
= NULL
;
2979 unsigned long int *unumber
= NULL
;
2981 if (!context
) return IE_INVALID_CONTEXT
;
2982 if (!envelope
) return IE_INVAL
;
2983 if (!xpath_ctx
) return IE_INVAL
;
2988 *envelope
= calloc(1, sizeof(**envelope
));
2995 zfree((*envelope
)->dmMessageStatus
);
2996 zfree((*envelope
)->dmAttachmentSize
);
2997 zfree((*envelope
)->dmDeliveryTime
);
2998 zfree((*envelope
)->dmAcceptanceTime
);
3002 /* dmMessageStatus element is mandatory */
3003 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber
, 0);
3005 isds_log_message(context
,
3006 _("Missing mandatory sisds:dmMessageStatus integer"));
3010 err
= uint2isds_message_status(context
, unumber
,
3011 &((*envelope
)->dmMessageStatus
));
3013 if (err
== IE_ENUM
) err
= IE_ISDS
;
3016 free(unumber
); unumber
= NULL
;
3018 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope
)->dmAttachmentSize
,
3021 EXTRACT_STRING("sisds:dmDeliveryTime", string
);
3023 err
= timestring2timeval((xmlChar
*) string
,
3024 &((*envelope
)->dmDeliveryTime
));
3026 char *string_locale
= _isds_utf82locale(string
);
3027 if (err
== IE_DATE
) err
= IE_ISDS
;
3028 isds_printf_message(context
,
3029 _("Could not convert dmDeliveryTime as ISO time: %s"),
3031 free(string_locale
);
3037 EXTRACT_STRING("sisds:dmAcceptanceTime", string
);
3039 err
= timestring2timeval((xmlChar
*) string
,
3040 &((*envelope
)->dmAcceptanceTime
));
3042 char *string_locale
= _isds_utf82locale(string
);
3043 if (err
== IE_DATE
) err
= IE_ISDS
;
3044 isds_printf_message(context
,
3045 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3047 free(string_locale
);
3054 if (err
) isds_envelope_free(envelope
);
3057 xmlXPathFreeObject(result
);
3062 /* Convert message type attribute of current element into isds_envelope
3064 * TODO: This function can be incorporated into append_status_size_times() as
3065 * they are called always together.
3066 * The envelope is automatically allocated but not reallocated.
3067 * The data are just appended into envelope structure.
3068 * @context is ISDS context
3069 * @envelope is automatically allocated message envelope structure
3070 * @xpath_ctx is XPath context with current node as parent of attribute
3071 * carrying message type
3072 * In case of error @envelope will be freed. */
3073 static isds_error
append_message_type(struct isds_ctx
*context
,
3074 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3075 isds_error err
= IE_SUCCESS
;
3077 if (!context
) return IE_INVALID_CONTEXT
;
3078 if (!envelope
) return IE_INVAL
;
3079 if (!xpath_ctx
) return IE_INVAL
;
3084 *envelope
= calloc(1, sizeof(**envelope
));
3091 zfree((*envelope
)->dmType
);
3095 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope
)->dmType
, 0);
3097 if (!(*envelope
)->dmType
) {
3098 /* Use default value */
3099 (*envelope
)->dmType
= strdup("V");
3100 if (!(*envelope
)->dmType
) {
3104 } else if (1 != xmlUTF8Strlen((xmlChar
*) (*envelope
)->dmType
)) {
3105 char *type_locale
= _isds_utf82locale((*envelope
)->dmType
);
3106 isds_printf_message(context
,
3107 _("Message type in dmType attribute is not 1 character long: "
3116 if (err
) isds_envelope_free(envelope
);
3121 /* Convert dmType isds_envelope member into XML attribute and append it to
3123 * @context is ISDS context
3124 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3125 * @dm_envelope is XML element the resulting attribute will be appended to.
3126 * @return error code, in case of error context' message is filled. */
3127 static isds_error
insert_message_type(struct isds_ctx
*context
,
3128 const char *type
, xmlNodePtr dm_envelope
) {
3129 isds_error err
= IE_SUCCESS
;
3130 xmlAttrPtr attribute_node
;
3132 if (!context
) return IE_INVALID_CONTEXT
;
3133 if (!dm_envelope
) return IE_INVAL
;
3135 /* Insert optional message type */
3137 if (1 != xmlUTF8Strlen((xmlChar
*) type
)) {
3138 char *type_locale
= _isds_utf82locale(type
);
3139 isds_printf_message(context
,
3140 _("Message type in envelope is not 1 character long: %s"),
3146 INSERT_STRING_ATTRIBUTE(dm_envelope
, "dmType", type
);
3154 /* Extract message document into reallocated document structure
3155 * @context is ISDS context
3156 * @document is automatically reallocated message documents structure
3157 * @xpath_ctx is XPath context with current node as isds:dmFile
3158 * In case of error @document will be freed. */
3159 static isds_error
extract_document(struct isds_ctx
*context
,
3160 struct isds_document
**document
, xmlXPathContextPtr xpath_ctx
) {
3161 isds_error err
= IE_SUCCESS
;
3162 xmlXPathObjectPtr result
= NULL
;
3163 xmlNodePtr file_node
= xpath_ctx
->node
;
3164 char *string
= NULL
;
3166 if (!context
) return IE_INVALID_CONTEXT
;
3167 if (!document
) return IE_INVAL
;
3168 isds_document_free(document
);
3169 if (!xpath_ctx
) return IE_INVAL
;
3171 *document
= calloc(1, sizeof(**document
));
3177 /* Extract document meta data */
3178 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document
)->dmMimeType
, 1)
3179 if (context
->normalize_mime_type
) {
3180 char *normalized_type
=
3181 isds_normalize_mime_type((*document
)->dmMimeType
);
3182 if (normalized_type
&& normalized_type
!= (*document
)->dmMimeType
) {
3183 char *new_type
= strdup(normalized_type
);
3185 isds_printf_message(context
,
3186 _("No enough memory to normalize document MIME type"));
3190 free((*document
)->dmMimeType
);
3191 (*document
)->dmMimeType
= new_type
;
3195 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string
, 1)
3196 err
= string2isds_FileMetaType((xmlChar
*)string
,
3197 &((*document
)->dmFileMetaType
));
3199 char *meta_type_locale
= _isds_utf82locale(string
);
3200 isds_printf_message(context
,
3201 _("Document has invalid dmFileMetaType attribute value: %s"),
3203 free(meta_type_locale
);
3209 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document
)->dmFileGuid
, 0)
3210 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document
)->dmUpFileGuid
, 0)
3211 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document
)->dmFileDescr
, 0)
3212 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document
)->dmFormat
, 0)
3215 /* Extract document data.
3216 * Base64 encoded blob or XML subtree must be presented. */
3218 /* Check from dmEncodedContent */
3219 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmEncodedContent",
3226 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
3227 /* Here we have Base64 blob */
3229 if (result
->nodesetval
->nodeNr
> 1) {
3230 isds_printf_message(context
,
3231 _("Document has more dmEncodedContent elements"));
3236 xmlXPathFreeObject(result
); result
= NULL
;
3237 EXTRACT_STRING("isds:dmEncodedContent", string
);
3239 /* Decode non-empty document */
3240 if (string
&& string
[0] != '\0') {
3241 (*document
)->data_length
=
3242 _isds_b64decode(string
, &((*document
)->data
));
3243 if ((*document
)->data_length
== (size_t) -1) {
3244 isds_printf_message(context
,
3245 _("Error while Base64-decoding document content"));
3251 /* No Base64 blob, try XML document */
3252 xmlXPathFreeObject(result
); result
= NULL
;
3253 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmXMLContent",
3260 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
3261 /* Here we have XML document */
3263 if (result
->nodesetval
->nodeNr
> 1) {
3264 isds_printf_message(context
,
3265 _("Document has more dmXMLContent elements"));
3270 /* FIXME: Serialize the tree rooted at result's node */
3271 isds_printf_message(context
,
3272 _("XML documents not yet supported"));
3276 /* No base64 blob, nor XML document */
3277 isds_printf_message(context
,
3278 _("Document has no dmEncodedContent, nor dmXMLContent "
3287 if (err
) isds_document_free(document
);
3289 xmlXPathFreeObject(result
);
3290 xpath_ctx
->node
= file_node
;
3296 /* Extract message documents into reallocated list of documents
3297 * @context is ISDS context
3298 * @documents is automatically reallocated message documents list structure
3299 * @xpath_ctx is XPath context with current node as XSD tFilesArray
3300 * In case of error @documents will be freed. */
3301 static isds_error
extract_documents(struct isds_ctx
*context
,
3302 struct isds_list
**documents
, xmlXPathContextPtr xpath_ctx
) {
3303 isds_error err
= IE_SUCCESS
;
3304 xmlXPathObjectPtr result
= NULL
;
3305 xmlNodePtr files_node
= xpath_ctx
->node
;
3306 struct isds_list
*document
, *prev_document
;
3308 if (!context
) return IE_INVALID_CONTEXT
;
3309 if (!documents
) return IE_INVAL
;
3310 isds_list_free(documents
);
3311 if (!xpath_ctx
) return IE_INVAL
;
3313 /* Find documents */
3314 result
= xmlXPathEvalExpression(BAD_CAST
"isds:dmFile", xpath_ctx
);
3321 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
3322 isds_printf_message(context
,
3323 _("Message does not contain any document"));
3329 /* Iterate over documents */
3330 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
3332 /* Allocate and append list item */
3333 document
= calloc(1, sizeof(*document
));
3338 document
->destructor
= (void (*)(void **))isds_document_free
;
3339 if (i
== 0) *documents
= document
;
3340 else prev_document
->next
= document
;
3341 prev_document
= document
;
3343 /* Extract document */
3344 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
3345 err
= extract_document(context
,
3346 (struct isds_document
**) &(document
->data
), xpath_ctx
);
3347 if (err
) goto leave
;
3352 if (err
) isds_list_free(documents
);
3353 xmlXPathFreeObject(result
);
3354 xpath_ctx
->node
= files_node
;
3359 /* Convert isds:dmRecord XML tree into structure
3360 * @context is ISDS context
3361 * @envelope is automatically reallocated message envelope structure
3362 * @xpath_ctx is XPath context with current node as isds:dmRecord element
3363 * In case of error @envelope will be freed. */
3364 static isds_error
extract_DmRecord(struct isds_ctx
*context
,
3365 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3366 isds_error err
= IE_SUCCESS
;
3367 xmlXPathObjectPtr result
= NULL
;
3369 if (!context
) return IE_INVALID_CONTEXT
;
3370 if (!envelope
) return IE_INVAL
;
3371 isds_envelope_free(envelope
);
3372 if (!xpath_ctx
) return IE_INVAL
;
3375 *envelope
= calloc(1, sizeof(**envelope
));
3382 /* Extract tRecord data */
3383 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope
)->dmOrdinal
, 0);
3385 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
3386 * dmAcceptanceTime. */
3387 err
= append_status_size_times(context
, envelope
, xpath_ctx
);
3388 if (err
) goto leave
;
3390 /* Extract envelope elements added by sender and ISDS
3391 * (XSD: gMessageEnvelope type) */
3392 err
= append_GMessageEnvelope(context
, envelope
, xpath_ctx
);
3393 if (err
) goto leave
;
3394 /* dmOVM can not be obtained from ISDS */
3396 /* Get message type */
3397 err
= append_message_type(context
, envelope
, xpath_ctx
);
3398 if (err
) goto leave
;
3402 if (err
) isds_envelope_free(envelope
);
3403 xmlXPathFreeObject(result
);
3408 /* Find and convert isds:dmHash XML tree into structure
3409 * @context is ISDS context
3410 * @envelope is automatically reallocated message hash structure
3411 * @xpath_ctx is XPath context with current node containing isds:dmHash child
3412 * In case of error @hash will be freed. */
3413 static isds_error
find_and_extract_DmHash(struct isds_ctx
*context
,
3414 struct isds_hash
**hash
, xmlXPathContextPtr xpath_ctx
) {
3415 isds_error err
= IE_SUCCESS
;
3416 xmlNodePtr old_ctx_node
;
3417 xmlXPathObjectPtr result
= NULL
;
3418 char *string
= NULL
;
3420 if (!context
) return IE_INVALID_CONTEXT
;
3421 if (!hash
) return IE_INVAL
;
3422 isds_hash_free(hash
);
3423 if (!xpath_ctx
) return IE_INVAL
;
3425 old_ctx_node
= xpath_ctx
->node
;
3427 *hash
= calloc(1, sizeof(**hash
));
3434 err
= move_xpathctx_to_child(context
, BAD_CAST
"sisds:dmHash", xpath_ctx
);
3435 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) {
3444 /* Get hash algorithm */
3445 EXTRACT_STRING_ATTRIBUTE("algorithm", string
, 1);
3446 err
= string2isds_hash_algorithm((xmlChar
*) string
, &(*hash
)->algorithm
);
3448 if (err
== IE_ENUM
) {
3449 char *string_locale
= _isds_utf82locale(string
);
3450 isds_printf_message(context
, _("Unsupported hash algorithm: %s"),
3452 free(string_locale
);
3458 /* Get hash value */
3459 EXTRACT_STRING(".", string
);
3461 isds_printf_message(context
,
3462 _("sisds:dmHash element is missing hash value"));
3466 (*hash
)->length
= _isds_b64decode(string
, &((*hash
)->value
));
3467 if ((*hash
)->length
== (size_t) -1) {
3468 isds_printf_message(context
,
3469 _("Error while Base64-decoding hash value"));
3475 if (err
) isds_hash_free(hash
);
3477 xmlXPathFreeObject(result
);
3478 xpath_ctx
->node
= old_ctx_node
;
3483 /* Find and append isds:dmQTimestamp XML tree into envelope.
3484 * Because one service is allowed to miss time-stamp content, and we think
3485 * other could too (flaw in specification), this function is deliberated and
3486 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
3487 * @context is ISDS context
3488 * @envelope is automatically allocated envelope structure
3489 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
3491 * In case of error @envelope will be freed. */
3492 static isds_error
find_and_append_DmQTimestamp(struct isds_ctx
*context
,
3493 struct isds_envelope
**envelope
, xmlXPathContextPtr xpath_ctx
) {
3494 isds_error err
= IE_SUCCESS
;
3495 xmlXPathObjectPtr result
= NULL
;
3496 char *string
= NULL
;
3498 if (!context
) return IE_INVALID_CONTEXT
;
3499 if (!envelope
) return IE_INVAL
;
3501 isds_envelope_free(envelope
);
3506 *envelope
= calloc(1, sizeof(**envelope
));
3512 zfree((*envelope
)->timestamp
);
3513 (*envelope
)->timestamp_length
= 0;
3516 /* Get dmQTimestamp */
3517 EXTRACT_STRING("sisds:dmQTimestamp", string
);
3519 isds_log(ILF_ISDS
, ILL_INFO
, _("Missing dmQTimestamp element content\n"));
3522 (*envelope
)->timestamp_length
=
3523 _isds_b64decode(string
, &((*envelope
)->timestamp
));
3524 if ((*envelope
)->timestamp_length
== (size_t) -1) {
3525 isds_printf_message(context
,
3526 _("Error while Base64-decoding time stamp value"));
3532 if (err
) isds_envelope_free(envelope
);
3534 xmlXPathFreeObject(result
);
3539 /* Convert XSD tReturnedMessage XML tree into message structure.
3540 * It does not store XML tree into message->raw.
3541 * @context is ISDS context
3542 * @include_documents Use true if documents must be extracted
3543 * (tReturnedMessage XSD type), use false if documents shall be omitted
3544 * (tReturnedMessageEnvelope).
3545 * @message is automatically reallocated message structure
3546 * @xpath_ctx is XPath context with current node as tReturnedMessage element
3548 * In case of error @message will be freed. */
3549 static isds_error
extract_TReturnedMessage(struct isds_ctx
*context
,
3550 const _Bool include_documents
, struct isds_message
**message
,
3551 xmlXPathContextPtr xpath_ctx
) {
3552 isds_error err
= IE_SUCCESS
;
3553 xmlNodePtr message_node
;
3555 if (!context
) return IE_INVALID_CONTEXT
;
3556 if (!message
) return IE_INVAL
;
3557 isds_message_free(message
);
3558 if (!xpath_ctx
) return IE_INVAL
;
3561 *message
= calloc(1, sizeof(**message
));
3567 /* Save message XPATH context node */
3568 message_node
= xpath_ctx
->node
;
3572 err
= move_xpathctx_to_child(context
, BAD_CAST
"isds:dmDm", xpath_ctx
);
3573 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
3574 if (err
) { err
= IE_ERROR
; goto leave
; }
3575 err
= append_GMessageEnvelope(context
, &((*message
)->envelope
), xpath_ctx
);
3576 if (err
) goto leave
;
3578 if (include_documents
) {
3579 /* Extract dmFiles */
3580 err
= move_xpathctx_to_child(context
, BAD_CAST
"isds:dmFiles",
3582 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) {
3583 err
= IE_ISDS
; goto leave
;
3585 if (err
) { err
= IE_ERROR
; goto leave
; }
3586 err
= extract_documents(context
, &((*message
)->documents
), xpath_ctx
);
3587 if (err
) goto leave
;
3591 /* Restore context to message */
3592 xpath_ctx
->node
= message_node
;
3594 /* Extract dmHash */
3595 err
= find_and_extract_DmHash(context
, &(*message
)->envelope
->hash
,
3597 if (err
) goto leave
;
3599 /* Extract dmQTimestamp, */
3600 err
= find_and_append_DmQTimestamp(context
, &(*message
)->envelope
,
3602 if (err
) goto leave
;
3604 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
3605 * dmAcceptanceTime. */
3606 err
= append_status_size_times(context
, &((*message
)->envelope
), xpath_ctx
);
3607 if (err
) goto leave
;
3609 /* Get message type */
3610 err
= append_message_type(context
, &((*message
)->envelope
), xpath_ctx
);
3611 if (err
) goto leave
;
3614 if (err
) isds_message_free(message
);
3619 /* Extract message event into reallocated isds_event structure
3620 * @context is ISDS context
3621 * @event is automatically reallocated message event structure
3622 * @xpath_ctx is XPath context with current node as isds:dmEvent
3623 * In case of error @event will be freed. */
3624 static isds_error
extract_event(struct isds_ctx
*context
,
3625 struct isds_event
**event
, xmlXPathContextPtr xpath_ctx
) {
3626 isds_error err
= IE_SUCCESS
;
3627 xmlXPathObjectPtr result
= NULL
;
3628 xmlNodePtr event_node
= xpath_ctx
->node
;
3629 char *string
= NULL
;
3631 if (!context
) return IE_INVALID_CONTEXT
;
3632 if (!event
) return IE_INVAL
;
3633 isds_event_free(event
);
3634 if (!xpath_ctx
) return IE_INVAL
;
3636 *event
= calloc(1, sizeof(**event
));
3642 /* Extract event data.
3643 * All elements are optional according XSD. That's funny. */
3644 EXTRACT_STRING("sisds:dmEventTime", string
);
3646 err
= timestring2timeval((xmlChar
*) string
, &((*event
)->time
));
3648 char *string_locale
= _isds_utf82locale(string
);
3649 if (err
== IE_DATE
) err
= IE_ISDS
;
3650 isds_printf_message(context
,
3651 _("Could not convert dmEventTime as ISO time: %s"),
3653 free(string_locale
);
3659 /* dmEventDescr element has prefix and the rest */
3660 EXTRACT_STRING("sisds:dmEventDescr", string
);
3662 err
= eventstring2event((xmlChar
*) string
, *event
);
3663 if (err
) goto leave
;
3668 if (err
) isds_event_free(event
);
3670 xmlXPathFreeObject(result
);
3671 xpath_ctx
->node
= event_node
;
3676 /* Convert element of XSD tEventsArray type from XML tree into
3677 * isds_list of isds_event's structure. The list is automatically reallocated.
3678 * @context is ISDS context
3679 * @events is automatically reallocated list of event structures
3680 * @xpath_ctx is XPath context with current node as tEventsArray
3681 * In case of error @events will be freed. */
3682 static isds_error
extract_events(struct isds_ctx
*context
,
3683 struct isds_list
**events
, xmlXPathContextPtr xpath_ctx
) {
3684 isds_error err
= IE_SUCCESS
;
3685 xmlXPathObjectPtr result
= NULL
;
3686 xmlNodePtr events_node
= xpath_ctx
->node
;
3687 struct isds_list
*event
, *prev_event
= NULL
;
3689 if (!context
) return IE_INVALID_CONTEXT
;
3690 if (!events
) return IE_INVAL
;
3691 if (!xpath_ctx
) return IE_INVAL
;
3694 isds_list_free(events
);
3697 result
= xmlXPathEvalExpression(BAD_CAST
"sisds:dmEvent", xpath_ctx
);
3704 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
3705 isds_printf_message(context
,
3706 _("Delivery info does not contain any event"));
3712 /* Iterate over events */
3713 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
3715 /* Allocate and append list item */
3716 event
= calloc(1, sizeof(*event
));
3721 event
->destructor
= (void (*)(void **))isds_event_free
;
3722 if (i
== 0) *events
= event
;
3723 else prev_event
->next
= event
;
3727 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
3728 err
= extract_event(context
,
3729 (struct isds_event
**) &(event
->data
), xpath_ctx
);
3730 if (err
) goto leave
;
3735 if (err
) isds_list_free(events
);
3736 xmlXPathFreeObject(result
);
3737 xpath_ctx
->node
= events_node
;
3742 /* Convert isds_document structure into XML tree and append to dmFiles node.
3743 * @context is session context
3744 * @document is ISDS document
3745 * @dm_files is XML element the resulting tree will be appended to as a child.
3746 * @return error code, in case of error context' message is filled. */
3747 static isds_error
insert_document(struct isds_ctx
*context
,
3748 struct isds_document
*document
, xmlNodePtr dm_files
) {
3749 isds_error err
= IE_SUCCESS
;
3750 xmlNodePtr new_file
= NULL
, file
= NULL
, node
;
3751 xmlAttrPtr attribute_node
;
3752 xmlChar
*base64data
= NULL
;
3754 if (!context
) return IE_INVALID_CONTEXT
;
3755 if (!document
|| !dm_files
) return IE_INVAL
;
3757 /* Allocate new dmFile */
3758 new_file
= xmlNewNode(dm_files
->ns
, BAD_CAST
"dmFile");
3760 isds_printf_message(context
, _("Could not allocate main dmFile"));
3764 /* Append the new dmFile.
3765 * XXX: Main document must go first */
3766 if (document
->dmFileMetaType
== FILEMETATYPE_MAIN
&& dm_files
->children
)
3767 file
= xmlAddPrevSibling(dm_files
->children
, new_file
);
3769 file
= xmlAddChild(dm_files
, new_file
);
3772 xmlFreeNode(new_file
); new_file
= NULL
;
3773 isds_printf_message(context
, _("Could not add dmFile child to "
3774 "%s element"), dm_files
->name
);
3779 /* @dmMimeType is required */
3780 if (!document
->dmMimeType
) {
3781 isds_log_message(context
,
3782 _("Document is missing mandatory MIME type definition"));
3786 INSERT_STRING_ATTRIBUTE(file
, "dmMimeType", document
->dmMimeType
);
3788 const xmlChar
*string
= isds_FileMetaType2string(document
->dmFileMetaType
);
3790 isds_printf_message(context
,
3791 _("Document has unknown dmFileMetaType: %ld"),
3792 document
->dmFileMetaType
);
3796 INSERT_STRING_ATTRIBUTE(file
, "dmFileMetaType", string
);
3798 if (document
->dmFileGuid
) {
3799 INSERT_STRING_ATTRIBUTE(file
, "dmFileGuid", document
->dmFileGuid
);
3801 if (document
->dmUpFileGuid
) {
3802 INSERT_STRING_ATTRIBUTE(file
, "dmUpFileGuid", document
->dmUpFileGuid
);
3805 /* @dmFileDescr is required */
3806 if (!document
->dmFileDescr
) {
3807 isds_log_message(context
,
3808 _("Document is missing mandatory description (title)"));
3812 INSERT_STRING_ATTRIBUTE(file
, "dmFileDescr", document
->dmFileDescr
);
3814 if (document
->dmFormat
) {
3815 INSERT_STRING_ATTRIBUTE(file
, "dmFormat", document
->dmFormat
);
3819 /* Insert content (data) of the document. */
3820 /* XXX; Only base64 is implemented currently. */
3821 base64data
= (xmlChar
*) _isds_b64encode(document
->data
,
3822 document
->data_length
);
3824 isds_printf_message(context
,
3825 ngettext("Not enough memory to encode %zd bytes into Base64",
3826 "Not enough memory to encode %zd bytes into Base64",
3827 document
->data_length
),
3828 document
->data_length
);
3832 INSERT_STRING(file
, "dmEncodedContent", base64data
);
3840 /* Append XSD tMStatus XML tree into isds_message_copy structure.
3841 * The copy must be preallocated, the date are just appended into structure.
3842 * @context is ISDS context
3843 * @copy is message copy structure
3844 * @xpath_ctx is XPath context with current node as tMStatus */
3845 static isds_error
append_TMStatus(struct isds_ctx
*context
,
3846 struct isds_message_copy
*copy
, xmlXPathContextPtr xpath_ctx
) {
3847 isds_error err
= IE_SUCCESS
;
3848 xmlXPathObjectPtr result
= NULL
;
3849 char *code
= NULL
, *message
= NULL
;
3851 if (!context
) return IE_INVALID_CONTEXT
;
3852 if (!copy
|| !xpath_ctx
) return IE_INVAL
;
3854 /* Free old values */
3855 zfree(copy
->dmStatus
);
3858 /* Get error specific to this copy */
3859 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code
);
3861 isds_log_message(context
,
3862 _("Missing isds:dmStatusCode under "
3863 "XSD:tMStatus type element"));
3868 if (xmlStrcmp((const xmlChar
*)code
, BAD_CAST
"0000")) {
3869 /* This copy failed */
3870 copy
->error
= IE_ISDS
;
3871 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message
);
3873 copy
->dmStatus
= _isds_astrcat3(code
, ": ", message
);
3874 if (!copy
->dmStatus
) {
3875 copy
->dmStatus
= code
;
3879 copy
->dmStatus
= code
;
3883 /* This copy succeeded. In this case only, message ID is valid */
3884 copy
->error
= IE_SUCCESS
;
3886 EXTRACT_STRING("isds:dmID", copy
->dmID
);
3888 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted sent message, "
3889 "but did not returned assigned message ID\n"));
3897 xmlXPathFreeObject(result
);
3902 /* Insert struct isds_approval data (box approval) into XML tree
3903 * @context is session context
3904 * @approval is libisds structure with approval description. NULL is
3906 * @parent is XML element to append @approval to */
3907 static isds_error
insert_GExtApproval(struct isds_ctx
*context
,
3908 const struct isds_approval
*approval
, xmlNodePtr parent
) {
3910 isds_error err
= IE_SUCCESS
;
3913 if (!context
) return IE_INVALID_CONTEXT
;
3914 if (!parent
) return IE_INVAL
;
3916 if (!approval
) return IE_SUCCESS
;
3918 /* Build XSD:gExtApproval */
3919 INSERT_SCALAR_BOOLEAN(parent
, "dbApproved", approval
->approved
);
3920 INSERT_STRING(parent
, "dbExternRefNumber", approval
->refference
);
3927 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
3929 * @context is session context
3930 * @service_name is name of SERVICE_DB_ACCESS
3931 * @response is server SOAP body response as XML document
3932 * @raw_response is automatically reallocated bit stream with response body. Use
3933 * NULL if you don't care
3934 * @raw_response_length is size of @raw_response in bytes
3935 * @code is ISDS status code
3936 * @status_message is ISDS status message
3937 * @return error coded from lower layer, context message will be set up
3939 static isds_error
build_send_check_dbdummy_request(struct isds_ctx
*context
,
3940 const xmlChar
*service_name
,
3941 xmlDocPtr
*response
, void **raw_response
, size_t *raw_response_length
,
3942 xmlChar
**code
, xmlChar
**status_message
) {
3944 isds_error err
= IE_SUCCESS
;
3945 char *service_name_locale
= NULL
;
3946 xmlNodePtr request
= NULL
, node
;
3947 xmlNsPtr isds_ns
= NULL
;
3949 if (!context
) return IE_INVALID_CONTEXT
;
3950 if (!service_name
) return IE_INVAL
;
3951 if (!response
|| !code
|| !status_message
) return IE_INVAL
;
3952 if (!raw_response_length
&& raw_response
) return IE_INVAL
;
3954 /* Free output argument */
3955 xmlFreeDoc(*response
); *response
= NULL
;
3956 if (raw_response
) zfree(*raw_response
);
3958 free(*status_message
);
3961 /* Check if connection is established
3962 * TODO: This check should be done downstairs. */
3963 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
3965 service_name_locale
= _isds_utf82locale((char*)service_name
);
3966 if (!service_name_locale
) {
3972 request
= xmlNewNode(NULL
, service_name
);
3974 isds_printf_message(context
,
3975 _("Could not build %s request"), service_name_locale
);
3979 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
3981 isds_log_message(context
, _("Could not create ISDS name space"));
3985 xmlSetNs(request
, isds_ns
);
3988 /* Add XSD:tDummyInput child */
3989 INSERT_STRING(request
, "dbDummy", NULL
);
3992 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
3993 service_name_locale
);
3996 err
= isds(context
, SERVICE_DB_ACCESS
, request
, response
,
3997 raw_response
, raw_response_length
);
3998 xmlFreeNode(request
); request
= NULL
;
4001 isds_log(ILF_ISDS
, ILL_DEBUG
,
4002 _("Processing ISDS response on %s request failed\n"),
4003 service_name_locale
);
4007 /* Check for response status */
4008 err
= isds_response_status(context
, SERVICE_DB_ACCESS
, *response
,
4009 code
, status_message
, NULL
);
4011 isds_log(ILF_ISDS
, ILL_DEBUG
,
4012 _("ISDS response on %s request is missing status\n"),
4013 service_name_locale
);
4017 /* Request processed, but nothing found */
4018 if (xmlStrcmp(*code
, BAD_CAST
"0000")) {
4019 char *code_locale
= _isds_utf82locale((char*) *code
);
4020 char *status_message_locale
=
4021 _isds_utf82locale((char*) *status_message
);
4022 isds_log(ILF_ISDS
, ILL_DEBUG
,
4023 _("Server refused %s request (code=%s, message=%s)\n"),
4024 service_name_locale
, code_locale
, status_message_locale
);
4025 isds_log_message(context
, status_message_locale
);
4027 free(status_message_locale
);
4033 free(service_name_locale
);
4034 xmlFreeNode(request
);
4039 /* Get data about logged in user and his box. */
4040 isds_error
isds_GetOwnerInfoFromLogin(struct isds_ctx
*context
,
4041 struct isds_DbOwnerInfo
**db_owner_info
) {
4042 isds_error err
= IE_SUCCESS
;
4043 xmlDocPtr response
= NULL
;
4044 xmlChar
*code
= NULL
, *message
= NULL
;
4045 xmlXPathContextPtr xpath_ctx
= NULL
;
4046 xmlXPathObjectPtr result
= NULL
;
4047 char *string
= NULL
;
4049 if (!context
) return IE_INVALID_CONTEXT
;
4050 zfree(context
->long_message
);
4051 if (!db_owner_info
) return IE_INVAL
;
4053 /* Check if connection is established */
4054 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4057 /* Do request and check for success */
4058 err
= build_send_check_dbdummy_request(context
,
4059 BAD_CAST
"GetOwnerInfoFromLogin",
4060 &response
, NULL
, NULL
, &code
, &message
);
4061 if (err
) goto leave
;
4065 /* Prepare structure */
4066 isds_DbOwnerInfo_free(db_owner_info
);
4067 *db_owner_info
= calloc(1, sizeof(**db_owner_info
));
4068 if (!*db_owner_info
) {
4072 xpath_ctx
= xmlXPathNewContext(response
);
4077 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
4082 /* Set context node */
4083 result
= xmlXPathEvalExpression(BAD_CAST
4084 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx
);
4089 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4090 isds_log_message(context
, _("Missing dbOwnerInfo element"));
4094 if (result
->nodesetval
->nodeNr
> 1) {
4095 isds_log_message(context
, _("Multiple dbOwnerInfo element"));
4099 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
4100 xmlXPathFreeObject(result
); result
= NULL
;
4103 err
= extract_DbOwnerInfo(context
, db_owner_info
, xpath_ctx
);
4107 isds_DbOwnerInfo_free(db_owner_info
);
4111 xmlXPathFreeObject(result
);
4112 xmlXPathFreeContext(xpath_ctx
);
4116 xmlFreeDoc(response
);
4119 isds_log(ILF_ISDS
, ILL_DEBUG
,
4120 _("GetOwnerInfoFromLogin request processed by server "
4121 "successfully.\n"));
4127 /* Get data about logged in user. */
4128 isds_error
isds_GetUserInfoFromLogin(struct isds_ctx
*context
,
4129 struct isds_DbUserInfo
**db_user_info
) {
4130 isds_error err
= IE_SUCCESS
;
4131 xmlDocPtr response
= NULL
;
4132 xmlChar
*code
= NULL
, *message
= NULL
;
4133 xmlXPathContextPtr xpath_ctx
= NULL
;
4134 xmlXPathObjectPtr result
= NULL
;
4136 if (!context
) return IE_INVALID_CONTEXT
;
4137 zfree(context
->long_message
);
4138 if (!db_user_info
) return IE_INVAL
;
4140 /* Check if connection is established */
4141 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4144 /* Do request and check for success */
4145 err
= build_send_check_dbdummy_request(context
,
4146 BAD_CAST
"GetUserInfoFromLogin",
4147 &response
, NULL
, NULL
, &code
, &message
);
4148 if (err
) goto leave
;
4152 /* Prepare structure */
4153 isds_DbUserInfo_free(db_user_info
);
4154 *db_user_info
= calloc(1, sizeof(**db_user_info
));
4155 if (!*db_user_info
) {
4159 xpath_ctx
= xmlXPathNewContext(response
);
4164 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
4169 /* Set context node */
4170 result
= xmlXPathEvalExpression(BAD_CAST
4171 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx
);
4176 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4177 isds_log_message(context
, _("Missing dbUserInfo element"));
4181 if (result
->nodesetval
->nodeNr
> 1) {
4182 isds_log_message(context
, _("Multiple dbUserInfo element"));
4186 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
4187 xmlXPathFreeObject(result
); result
= NULL
;
4190 err
= extract_DbUserInfo(context
, db_user_info
, xpath_ctx
);
4194 isds_DbUserInfo_free(db_user_info
);
4197 xmlXPathFreeObject(result
);
4198 xmlXPathFreeContext(xpath_ctx
);
4202 xmlFreeDoc(response
);
4205 isds_log(ILF_ISDS
, ILL_DEBUG
,
4206 _("GetUserInfoFromLogin request processed by server "
4207 "successfully.\n"));
4213 /* Get expiration time of current password
4214 * @context is session context
4215 * @expiration is automatically reallocated time when password expires, In
4216 * case of error will be nulled. */
4217 isds_error
isds_get_password_expiration(struct isds_ctx
*context
,
4218 struct timeval
**expiration
) {
4219 isds_error err
= IE_SUCCESS
;
4220 xmlDocPtr response
= NULL
;
4221 xmlChar
*code
= NULL
, *message
= NULL
;
4222 xmlXPathContextPtr xpath_ctx
= NULL
;
4223 xmlXPathObjectPtr result
= NULL
;
4224 char *string
= NULL
;
4226 if (!context
) return IE_INVALID_CONTEXT
;
4227 zfree(context
->long_message
);
4228 if (!expiration
) return IE_INVAL
;
4230 /* Check if connection is established */
4231 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4234 /* Do request and check for success */
4235 err
= build_send_check_dbdummy_request(context
,
4236 BAD_CAST
"GetPasswordInfo",
4237 &response
, NULL
, NULL
, &code
, &message
);
4238 if (err
) goto leave
;
4242 xpath_ctx
= xmlXPathNewContext(response
);
4247 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
4252 /* Set context node */
4253 result
= xmlXPathEvalExpression(BAD_CAST
4254 "/isds:GetPasswordInfoResponse", xpath_ctx
);
4259 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
4260 isds_log_message(context
,
4261 _("Missing GetPasswordInfoResponse element"));
4265 if (result
->nodesetval
->nodeNr
> 1) {
4266 isds_log_message(context
,
4267 _("Multiple GetPasswordInfoResponse element"));
4271 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
4272 xmlXPathFreeObject(result
); result
= NULL
;
4274 /* Extract expiration date */
4275 EXTRACT_STRING("isds:pswExpDate", string
);
4277 isds_log_message(context
, _("Missing pswExpDate element"));
4282 err
= timestring2timeval((xmlChar
*) string
, expiration
);
4284 char *string_locale
= _isds_utf82locale(string
);
4285 if (err
== IE_DATE
) err
= IE_ISDS
;
4286 isds_printf_message(context
,
4287 _("Could not convert pswExpDate as ISO time: %s"),
4289 free(string_locale
);
4301 xmlXPathFreeObject(result
);
4302 xmlXPathFreeContext(xpath_ctx
);
4306 xmlFreeDoc(response
);
4309 isds_log(ILF_ISDS
, ILL_DEBUG
,
4310 _("GetPasswordInfo request processed by server "
4311 "successfully.\n"));
4317 /* Change user password in ISDS.
4318 * User must supply old password, new password will takes effect after some
4319 * time, current session can continue. Password must fulfill some constraints.
4320 * @context is session context
4321 * @old_password is current password.
4322 * @new_password is requested new password */
4323 isds_error
isds_change_password(struct isds_ctx
*context
,
4324 const char *old_password
, const char *new_password
) {
4325 isds_error err
= IE_SUCCESS
;
4326 xmlNsPtr isds_ns
= NULL
;
4327 xmlNodePtr request
= NULL
, node
;
4328 xmlDocPtr response
= NULL
;
4329 xmlChar
*code
= NULL
, *message
= NULL
;
4331 if (!context
) return IE_INVALID_CONTEXT
;
4332 zfree(context
->long_message
);
4333 if (!old_password
|| !new_password
) return IE_INVAL
;
4335 /* Check if connection is established
4336 * TODO: This check should be done downstairs. */
4337 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4340 /* Build ChangeISDSPassword request */
4341 request
= xmlNewNode(NULL
, BAD_CAST
"ChangeISDSPassword");
4343 isds_log_message(context
,
4344 _("Could not build ChangeISDSPassword request"));
4347 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
4349 isds_log_message(context
, _("Could not create ISDS name space"));
4350 xmlFreeNode(request
);
4353 xmlSetNs(request
, isds_ns
);
4355 INSERT_STRING(request
, "dbOldPassword", old_password
);
4356 INSERT_STRING(request
, "dbNewPassword", new_password
);
4359 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending CheckDataBox request to ISDS\n"));
4362 err
= isds(context
, SERVICE_DB_ACCESS
, request
, &response
, NULL
, NULL
);
4364 /* Destroy request */
4365 xmlFreeNode(request
); request
= NULL
;
4368 isds_log(ILF_ISDS
, ILL_DEBUG
,
4369 _("Processing ISDS response on ChangeISDSPassword "
4370 "request failed\n"));
4374 /* Check for response status */
4375 err
= isds_response_status(context
, SERVICE_DB_ACCESS
, response
,
4376 &code
, &message
, NULL
);
4378 isds_log(ILF_ISDS
, ILL_DEBUG
,
4379 _("ISDS response on ChangeISDSPassword request is missing "
4384 /* Request processed, but empty password refused */
4385 if (!xmlStrcmp(code
, BAD_CAST
"1066")) {
4386 char *code_locale
= _isds_utf82locale((char*)code
);
4387 char *message_locale
= _isds_utf82locale((char*)message
);
4388 isds_log(ILF_ISDS
, ILL_DEBUG
,
4389 _("Server refused empty password on ChangeISDSPassword "
4390 "request (code=%s, message=%s)\n"),
4391 code_locale
, message_locale
);
4392 isds_log_message(context
, _("Password must not be empty"));
4394 free(message_locale
);
4399 /* Request processed, but new password was reused */
4400 else if (!xmlStrcmp(code
, BAD_CAST
"1067")) {
4401 char *code_locale
= _isds_utf82locale((char*)code
);
4402 char *message_locale
= _isds_utf82locale((char*)message
);
4403 isds_log(ILF_ISDS
, ILL_DEBUG
,
4404 _("Server refused the same new password on ChangeISDSPassword "
4405 "request (code=%s, message=%s)\n"),
4406 code_locale
, message_locale
);
4407 isds_log_message(context
,
4408 _("New password must differ from the current one"));
4410 free(message_locale
);
4416 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
4417 char *code_locale
= _isds_utf82locale((char*)code
);
4418 char *message_locale
= _isds_utf82locale((char*)message
);
4419 isds_log(ILF_ISDS
, ILL_DEBUG
,
4420 _("Server refused to change password on ChangeISDSPassword "
4421 "request (code=%s, message=%s)\n"),
4422 code_locale
, message_locale
);
4423 isds_log_message(context
, message_locale
);
4425 free(message_locale
);
4430 /* Otherwise password changed successfully */
4435 xmlFreeDoc(response
);
4436 xmlFreeNode(request
);
4439 isds_log(ILF_ISDS
, ILL_DEBUG
,
4440 _("Password changed successfully on ChangeISDSPassword "
4447 /* Generic middle part with request sending and response check.
4448 * It sends prepared request and checks for error code.
4449 * @context is ISDS session context.
4450 * @service is ISDS service handler
4451 * @service_name is name in scope of given @service
4452 * @request is XML tree with request. Will be freed to save memory.
4453 * @response is XML document outputting ISDS response.
4454 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4455 * NULL, if you don't care. */
4456 static isds_error
send_destroy_request_check_response(
4457 struct isds_ctx
*context
,
4458 const isds_service service
, const xmlChar
*service_name
,
4459 xmlNodePtr
*request
, xmlDocPtr
*response
, xmlChar
**refnumber
) {
4460 isds_error err
= IE_SUCCESS
;
4461 char *service_name_locale
= NULL
;
4462 xmlChar
*code
= NULL
, *message
= NULL
;
4465 if (!context
) return IE_INVALID_CONTEXT
;
4466 if (!service_name
|| *service_name
== '\0' || !request
|| !*request
||
4470 /* Check if connection is established
4471 * TODO: This check should be done downstairs. */
4472 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
4474 service_name_locale
= _isds_utf82locale((char*) service_name
);
4475 if (!service_name_locale
) {
4480 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending %s request to ISDS\n"),
4481 service_name_locale
);
4484 err
= isds(context
, service
, *request
, response
, NULL
, NULL
);
4485 xmlFreeNode(*request
); *request
= NULL
;
4488 isds_log(ILF_ISDS
, ILL_DEBUG
,
4489 _("Processing ISDS response on %s request failed\n"),
4490 service_name_locale
);
4494 /* Check for response status */
4495 err
= isds_response_status(context
, service
, *response
,
4496 &code
, &message
, refnumber
);
4498 isds_log(ILF_ISDS
, ILL_DEBUG
,
4499 _("ISDS response on %s request is missing status\n"),
4500 service_name_locale
);
4504 /* Request processed, but server failed */
4505 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
4506 char *code_locale
= _isds_utf82locale((char*) code
);
4507 char *message_locale
= _isds_utf82locale((char*) message
);
4508 isds_log(ILF_ISDS
, ILL_DEBUG
,
4509 _("Server refused %s request (code=%s, message=%s)\n"),
4510 service_name_locale
, code_locale
, message_locale
);
4511 isds_log_message(context
, message_locale
);
4513 free(message_locale
);
4522 if (err
&& *response
) {
4523 xmlFreeDoc(*response
);
4527 xmlFreeNode(*request
);
4530 free(service_name_locale
);
4536 /* Generic bottom half with request sending.
4537 * It sends prepared request, checks for error code, destroys response and
4538 * request and log success or failure.
4539 * @context is ISDS session context.
4540 * @service is ISDS service handler
4541 * @service_name is name in scope of given @service
4542 * @request is XML tree with request. Will be freed to save memory.
4543 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4544 * NULL, if you don't care. */
4545 static isds_error
send_request_check_drop_response(
4546 struct isds_ctx
*context
,
4547 const isds_service service
, const xmlChar
*service_name
,
4548 xmlNodePtr
*request
, xmlChar
**refnumber
) {
4549 isds_error err
= IE_SUCCESS
;
4550 xmlDocPtr response
= NULL
;
4553 if (!context
) return IE_INVALID_CONTEXT
;
4554 if (!service_name
|| *service_name
== '\0' || !request
|| !*request
)
4557 /* Send request and check response*/
4558 err
= send_destroy_request_check_response(context
,
4559 service
, service_name
, request
, &response
, refnumber
);
4561 xmlFreeDoc(response
);
4564 xmlFreeNode(*request
);
4569 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
4570 isds_log(ILF_ISDS
, ILL_DEBUG
,
4571 _("%s request processed by server successfully.\n"),
4572 service_name_locale
);
4573 free(service_name_locale
);
4580 /* Build XSD:tCreateDBInput request type for box creating.
4581 * @context is session context
4582 * @request outputs built XML tree
4583 * @service_name is request name of SERVICE_DB_MANIPULATION service
4584 * @box is box description to create including single primary user (in case of
4586 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
4587 * box, or contact address of PFO box owner)
4588 * @former_names is optional undocumented string. Pass NULL if you don't care.
4589 * @upper_box_id is optional ID of supper box if currently created box is
4591 * @ceo_label is optional title of OVM box owner (e.g. mayor) NULL, if you
4593 * @approval is optional external approval of box manipulation */
4594 static isds_error
build_CreateDBInput_request(struct isds_ctx
*context
,
4595 xmlNodePtr
*request
, const xmlChar
*service_name
,
4596 const struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
4597 const xmlChar
*former_names
, const xmlChar
*upper_box_id
,
4598 const xmlChar
*ceo_label
, const struct isds_approval
*approval
) {
4599 isds_error err
= IE_SUCCESS
;
4600 xmlNsPtr isds_ns
= NULL
;
4601 xmlNodePtr node
, dbPrimaryUsers
;
4602 xmlChar
*string
= NULL
;
4603 const struct isds_list
*item
;
4606 if (!context
) return IE_INVALID_CONTEXT
;
4607 if (!request
|| !service_name
|| service_name
[0] == '\0' || !box
)
4611 /* Build DeleteDataBox request */
4612 *request
= xmlNewNode(NULL
, service_name
);
4614 char *service_name_locale
= _isds_utf82locale((char*) service_name
);
4615 isds_printf_message(context
, _("Could build %s request"),
4616 service_name_locale
);
4617 free(service_name_locale
);
4620 if (context
->type
== CTX_TYPE_TESTING_REQUEST_COLLECTOR
) {
4621 isds_ns
= xmlNewNs(*request
, BAD_CAST ISDS1_NS
, NULL
);
4623 isds_log_message(context
, _("Could not create ISDS1 name space"));
4624 xmlFreeNode(*request
);
4628 isds_ns
= xmlNewNs(*request
, BAD_CAST ISDS_NS
, NULL
);
4630 isds_log_message(context
, _("Could not create ISDS name space"));
4631 xmlFreeNode(*request
);
4635 xmlSetNs(*request
, isds_ns
);
4637 INSERT_ELEMENT(node
, *request
, "dbOwnerInfo");
4638 err
= insert_DbOwnerInfo(context
, box
, node
);
4639 if (err
) goto leave
;
4642 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
4643 * verbose documentation allows none dbUserInfo */
4644 INSERT_ELEMENT(dbPrimaryUsers
, *request
, "dbPrimaryUsers");
4645 for (item
= users
; item
; item
= item
->next
) {
4647 INSERT_ELEMENT(node
, dbPrimaryUsers
, "dbUserInfo");
4648 err
= insert_DbUserInfo(context
,
4649 (struct isds_DbUserInfo
*) item
->data
, node
);
4650 if (err
) goto leave
;
4654 INSERT_STRING(*request
, "dbFormerNames", former_names
);
4655 INSERT_STRING(*request
, "dbUpperDBId", upper_box_id
);
4656 INSERT_STRING(*request
, "dbCEOLabel", ceo_label
);
4658 err
= insert_GExtApproval(context
, approval
, *request
);
4659 if (err
) goto leave
;
4663 xmlFreeNode(*request
);
4672 * @context is session context
4673 * @box is box description to create including single primary user (in case of
4674 * FO box type). It outputs box ID assigned by ISDS in dbID element.
4675 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
4676 * box, or contact address of PFO box owner)
4677 * @former_names is optional undocumented string. Pass NULL if you don't care.
4678 * @upper_box_id is optional ID of supper box if currently created box is
4680 * @ceo_label is optional title of OVM box owner (e.g. mayor)
4681 * @approval is optional external approval of box manipulation
4682 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4683 * NULL, if you don't care.*/
4684 isds_error
isds_add_box(struct isds_ctx
*context
,
4685 struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
4686 const char *former_names
, const char *upper_box_id
,
4687 const char *ceo_label
, const struct isds_approval
*approval
,
4689 isds_error err
= IE_SUCCESS
;
4690 xmlNodePtr request
= NULL
;
4691 xmlDocPtr response
= NULL
;
4692 xmlXPathContextPtr xpath_ctx
= NULL
;
4693 xmlXPathObjectPtr result
= NULL
;
4696 if (!context
) return IE_INVALID_CONTEXT
;
4697 zfree(context
->long_message
);
4698 if (!box
) return IE_INVAL
;
4700 /* Scratch box ID */
4703 /* Build CreateDataBox request */
4704 err
= build_CreateDBInput_request(context
,
4705 &request
, BAD_CAST
"CreateDataBox",
4706 box
, users
, (xmlChar
*) former_names
, (xmlChar
*) upper_box_id
,
4707 (xmlChar
*) ceo_label
, approval
);
4708 if (err
) goto leave
;
4710 /* Send it to server and process response */
4711 err
= send_destroy_request_check_response(context
,
4712 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
4713 &response
, (xmlChar
**) refnumber
);
4715 /* Extract box ID */
4716 xpath_ctx
= xmlXPathNewContext(response
);
4721 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
4725 EXTRACT_STRING("/isds:CreateDataBoxResponse/dbID", box
->dbID
);
4728 xmlXPathFreeObject(result
);
4729 xmlXPathFreeContext(xpath_ctx
);
4730 xmlFreeDoc(response
);
4731 xmlFreeNode(request
);
4734 isds_log(ILF_ISDS
, ILL_DEBUG
,
4735 _("CreateDataBox request processed by server successfully.\n"));
4742 /* Notify ISDS about new PFO entity.
4743 * This function has no real effect.
4744 * @context is session context
4745 * @box is PFO description including single primary user.
4746 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
4747 * @former_names is optional undocumented string. Pass NULL if you don't care.
4748 * @upper_box_id is optional ID of supper box if currently created box is
4750 * @ceo_label is optional title of OVM box owner (e.g. mayor)
4751 * @approval is optional external approval of box manipulation
4752 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4753 * NULL, if you don't care.*/
4754 isds_error
isds_add_pfoinfo(struct isds_ctx
*context
,
4755 const struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
4756 const char *former_names
, const char *upper_box_id
,
4757 const char *ceo_label
, const struct isds_approval
*approval
,
4759 isds_error err
= IE_SUCCESS
;
4760 xmlNodePtr request
= NULL
;
4762 if (!context
) return IE_INVALID_CONTEXT
;
4763 zfree(context
->long_message
);
4764 if (!box
) return IE_INVAL
;
4766 /* Build CreateDataBoxPFOInfo request */
4767 err
= build_CreateDBInput_request(context
,
4768 &request
, BAD_CAST
"CreateDataBoxPFOInfo",
4769 box
, users
, (xmlChar
*) former_names
, (xmlChar
*) upper_box_id
,
4770 (xmlChar
*) ceo_label
, approval
);
4771 if (err
) goto leave
;
4773 /* Send it to server and process response */
4774 err
= send_request_check_drop_response(context
,
4775 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
4776 (xmlChar
**) refnumber
);
4778 xmlFreeNode(request
);
4783 /* Remove given given box permanently.
4784 * @context is session context
4785 * @box is box description to delete
4786 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
4788 * @approval is optional external approval of box manipulation
4789 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4790 * NULL, if you don't care.*/
4791 isds_error
isds_delete_box(struct isds_ctx
*context
,
4792 const struct isds_DbOwnerInfo
*box
, const struct tm
*since
,
4793 const struct isds_approval
*approval
, char **refnumber
) {
4794 isds_error err
= IE_SUCCESS
;
4795 xmlNsPtr isds_ns
= NULL
;
4796 xmlNodePtr request
= NULL
;
4798 xmlChar
*string
= NULL
;
4801 if (!context
) return IE_INVALID_CONTEXT
;
4802 zfree(context
->long_message
);
4803 if (!box
|| !since
) return IE_INVAL
;
4806 /* Build DeleteDataBox request */
4807 request
= xmlNewNode(NULL
, BAD_CAST
"DeleteDataBox");
4809 isds_log_message(context
,
4810 _("Could build DeleteDataBox request"));
4813 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
4815 isds_log_message(context
, _("Could not create ISDS name space"));
4816 xmlFreeNode(request
);
4819 xmlSetNs(request
, isds_ns
);
4821 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
4822 err
= insert_DbOwnerInfo(context
, box
, node
);
4823 if (err
) goto leave
;
4825 err
= tm2datestring(since
, &string
);
4827 isds_log_message(context
,
4828 _("Could not convert `since' argument to ISO date string"));
4831 INSERT_STRING(request
, "dbOwnerTerminationDate", string
);
4834 err
= insert_GExtApproval(context
, approval
, request
);
4835 if (err
) goto leave
;
4838 /* Send it to server and process response */
4839 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
4840 BAD_CAST
"DeleteDataBox", &request
, (xmlChar
**) refnumber
);
4843 xmlFreeNode(request
);
4849 /* Update data about given box.
4850 * @context is session context
4851 * @old_box current box description
4852 * @new_box are updated data about @old_box
4853 * @approval is optional external approval of box manipulation
4854 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4855 * NULL, if you don't care.*/
4856 isds_error
isds_UpdateDataBoxDescr(struct isds_ctx
*context
,
4857 const struct isds_DbOwnerInfo
*old_box
,
4858 const struct isds_DbOwnerInfo
*new_box
,
4859 const struct isds_approval
*approval
, char **refnumber
) {
4860 isds_error err
= IE_SUCCESS
;
4861 xmlNsPtr isds_ns
= NULL
;
4862 xmlNodePtr request
= NULL
;
4866 if (!context
) return IE_INVALID_CONTEXT
;
4867 zfree(context
->long_message
);
4868 if (!old_box
|| !new_box
) return IE_INVAL
;
4871 /* Build UpdateDataBoxDescr request */
4872 request
= xmlNewNode(NULL
, BAD_CAST
"UpdateDataBoxDescr");
4874 isds_log_message(context
,
4875 _("Could build UpdateDataBoxDescr request"));
4878 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
4880 isds_log_message(context
, _("Could not create ISDS name space"));
4881 xmlFreeNode(request
);
4884 xmlSetNs(request
, isds_ns
);
4886 INSERT_ELEMENT(node
, request
, "dbOldOwnerInfo");
4887 err
= insert_DbOwnerInfo(context
, old_box
, node
);
4888 if (err
) goto leave
;
4890 INSERT_ELEMENT(node
, request
, "dbNewOwnerInfo");
4891 err
= insert_DbOwnerInfo(context
, new_box
, node
);
4892 if (err
) goto leave
;
4894 err
= insert_GExtApproval(context
, approval
, request
);
4895 if (err
) goto leave
;
4898 /* Send it to server and process response */
4899 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
4900 BAD_CAST
"UpdateDataBoxDescr", &request
, (xmlChar
**) refnumber
);
4903 xmlFreeNode(request
);
4909 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
4911 * @context is session context
4912 * @service is SOAP service
4913 * @service_name is name of request in @service
4914 * @box_id is box ID of interest
4915 * @approval is optional external approval of box manipulation
4916 * @response is server SOAP body response as XML document
4917 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4918 * NULL, if you don't care.
4919 * @return error coded from lower layer, context message will be set up
4921 static isds_error
build_send_dbid_request_check_response(
4922 struct isds_ctx
*context
, const isds_service service
,
4923 const xmlChar
*service_name
, const xmlChar
*box_id
,
4924 const struct isds_approval
*approval
,
4925 xmlDocPtr
*response
, xmlChar
**refnumber
) {
4927 isds_error err
= IE_SUCCESS
;
4928 char *service_name_locale
= NULL
, *box_id_locale
= NULL
;
4929 xmlNodePtr request
= NULL
, node
;
4930 xmlNsPtr isds_ns
= NULL
;
4932 if (!context
) return IE_INVALID_CONTEXT
;
4933 if (!service_name
|| !box_id
) return IE_INVAL
;
4934 if (!response
) return IE_INVAL
;
4936 /* Free output argument */
4937 xmlFreeDoc(*response
); *response
= NULL
;
4939 /* Prepare strings */
4940 service_name_locale
= _isds_utf82locale((char*)service_name
);
4941 if (!service_name_locale
) {
4945 box_id_locale
= _isds_utf82locale((char*)box_id
);
4946 if (!box_id_locale
) {
4952 request
= xmlNewNode(NULL
, service_name
);
4954 isds_printf_message(context
,
4955 _("Could not build %s request"), service_name_locale
);
4959 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
4961 isds_log_message(context
, _("Could not create ISDS name space"));
4965 xmlSetNs(request
, isds_ns
);
4967 /* Add XSD:tIdDbInput children */
4968 INSERT_STRING(request
, "dbID", box_id
);
4969 err
= insert_GExtApproval(context
, approval
, request
);
4970 if (err
) goto leave
;
4972 /* Send request and check response*/
4973 err
= send_destroy_request_check_response(context
,
4974 service
, service_name
, &request
, response
, refnumber
);
4977 free(service_name_locale
);
4978 free(box_id_locale
);
4979 xmlFreeNode(request
);
4984 /* Get data about all users assigned to given box.
4985 * @context is session context
4987 * @users is automatically reallocated list of struct isds_DbUserInfo */
4988 isds_error
isds_GetDataBoxUsers(struct isds_ctx
*context
, const char *box_id
,
4989 struct isds_list
**users
) {
4990 isds_error err
= IE_SUCCESS
;
4991 xmlDocPtr response
= NULL
;
4992 xmlXPathContextPtr xpath_ctx
= NULL
;
4993 xmlXPathObjectPtr result
= NULL
;
4995 struct isds_list
*item
, *prev_item
= NULL
;
4997 if (!context
) return IE_INVALID_CONTEXT
;
4998 zfree(context
->long_message
);
4999 if (!users
|| !box_id
) return IE_INVAL
;
5002 /* Do request and check for success */
5003 err
= build_send_dbid_request_check_response(context
,
5004 SERVICE_DB_MANIPULATION
, BAD_CAST
"GetDataBoxUsers",
5005 BAD_CAST box_id
, NULL
, &response
, NULL
);
5006 if (err
) goto leave
;
5010 /* Prepare structure */
5011 isds_list_free(users
);
5012 xpath_ctx
= xmlXPathNewContext(response
);
5017 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5022 /* Set context node */
5023 result
= xmlXPathEvalExpression(BAD_CAST
5024 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
5030 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5031 isds_log_message(context
, _("Missing dbUserInfo element"));
5036 /* Iterate over all users */
5037 for (i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
5039 /* Prepare structure */
5040 item
= calloc(1, sizeof(*item
));
5045 item
->destructor
= (void(*)(void**))isds_DbUserInfo_free
;
5046 if (i
== 0) *users
= item
;
5047 else prev_item
->next
= item
;
5051 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
5052 err
= extract_DbUserInfo(context
,
5053 (struct isds_DbUserInfo
**) (&item
->data
), xpath_ctx
);
5054 if (err
) goto leave
;
5059 isds_list_free(users
);
5062 xmlXPathFreeObject(result
);
5063 xmlXPathFreeContext(xpath_ctx
);
5064 xmlFreeDoc(response
);
5067 isds_log(ILF_ISDS
, ILL_DEBUG
,
5068 _("GetDataBoxUsers request processed by server "
5069 "successfully.\n"));
5075 /* Update data about user assigned to given box.
5076 * @context is session context
5077 * @box is box identification
5078 * @old_user identifies user to update
5079 * @new_user are updated data about @old_user
5080 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5081 * NULL, if you don't care.*/
5082 isds_error
isds_UpdateDataBoxUser(struct isds_ctx
*context
,
5083 const struct isds_DbOwnerInfo
*box
,
5084 const struct isds_DbUserInfo
*old_user
,
5085 const struct isds_DbUserInfo
*new_user
,
5087 isds_error err
= IE_SUCCESS
;
5088 xmlNsPtr isds_ns
= NULL
;
5089 xmlNodePtr request
= NULL
;
5093 if (!context
) return IE_INVALID_CONTEXT
;
5094 zfree(context
->long_message
);
5095 if (!box
|| !old_user
|| !new_user
) return IE_INVAL
;
5098 /* Build UpdateDataBoxUser request */
5099 request
= xmlNewNode(NULL
, BAD_CAST
"UpdateDataBoxUser");
5101 isds_log_message(context
,
5102 _("Could build UpdateDataBoxUser request"));
5105 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
5107 isds_log_message(context
, _("Could not create ISDS name space"));
5108 xmlFreeNode(request
);
5111 xmlSetNs(request
, isds_ns
);
5113 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
5114 err
= insert_DbOwnerInfo(context
, box
, node
);
5115 if (err
) goto leave
;
5117 INSERT_ELEMENT(node
, request
, "dbOldUserInfo");
5118 err
= insert_DbUserInfo(context
, old_user
, node
);
5119 if (err
) goto leave
;
5121 INSERT_ELEMENT(node
, request
, "dbNewUserInfo");
5122 err
= insert_DbUserInfo(context
, new_user
, node
);
5123 if (err
) goto leave
;
5125 /* Send it to server and process response */
5126 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
5127 BAD_CAST
"UpdateDataBoxUser", &request
, (xmlChar
**) refnumber
);
5130 xmlFreeNode(request
);
5136 /* Reset credentials of user assigned to given box.
5137 * @context is session context
5138 * @box is box identification
5139 * @user identifies user to reset password
5140 * @fee_paid is true if fee has been paid, false otherwise
5141 * @approval is optional external approval of box manipulation
5142 * @token is NULL if new password should be delivered off-line to the user.
5143 * It is valid pointer if user should obtain new password on-line on dedicated
5144 * web server. Then it output automatically reallocated token user needs to
5145 * use to authorize on the web server to view his new password.
5146 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5147 * NULL, if you don't care.*/
5148 isds_error
isds_reset_password(struct isds_ctx
*context
,
5149 const struct isds_DbOwnerInfo
*box
,
5150 const struct isds_DbUserInfo
*user
,
5151 const _Bool fee_paid
, const struct isds_approval
*approval
,
5152 char **token
, char **refnumber
) {
5153 isds_error err
= IE_SUCCESS
;
5154 xmlNsPtr isds_ns
= NULL
;
5155 xmlNodePtr request
= NULL
, node
;
5156 xmlDocPtr response
= NULL
;
5157 xmlXPathContextPtr xpath_ctx
= NULL
;
5158 xmlXPathObjectPtr result
= NULL
;
5161 if (!context
) return IE_INVALID_CONTEXT
;
5162 zfree(context
->long_message
);
5163 if (!box
|| !user
) return IE_INVAL
;
5165 if (token
) zfree(*token
);
5168 /* Build NewAccessData request */
5169 request
= xmlNewNode(NULL
, BAD_CAST
"NewAccessData");
5171 isds_log_message(context
,
5172 _("Could build NewAccessData request"));
5175 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
5177 isds_log_message(context
, _("Could not create ISDS name space"));
5178 xmlFreeNode(request
);
5181 xmlSetNs(request
, isds_ns
);
5183 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
5184 err
= insert_DbOwnerInfo(context
, box
, node
);
5185 if (err
) goto leave
;
5187 INSERT_ELEMENT(node
, request
, "dbUserInfo");
5188 err
= insert_DbUserInfo(context
, user
, node
);
5189 if (err
) goto leave
;
5191 INSERT_SCALAR_BOOLEAN(request
, "dbFeePaid", fee_paid
);
5194 INSERT_SCALAR_BOOLEAN(request
, "dbVirtual", 1);
5196 INSERT_SCALAR_BOOLEAN(request
, "dbVirtual", 0);
5199 err
= insert_GExtApproval(context
, approval
, request
);
5200 if (err
) goto leave
;
5202 /* Send request and check response*/
5203 err
= send_destroy_request_check_response(context
,
5204 SERVICE_DB_MANIPULATION
, BAD_CAST
"NewAccessData", &request
,
5205 &response
, (xmlChar
**) refnumber
);
5206 if (err
) goto leave
;
5209 /* Extract optional token */
5211 xpath_ctx
= xmlXPathNewContext(response
);
5216 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5221 EXTRACT_STRING("/isds:NewAccessDataResponse/dbAccessDataId", *token
);
5225 xmlXPathFreeObject(result
);
5226 xmlXPathFreeContext(xpath_ctx
);
5227 xmlFreeDoc(response
);
5228 xmlFreeNode(request
);
5231 isds_log(ILF_ISDS
, ILL_DEBUG
,
5232 _("NewAccessData request processed by server "
5233 "successfully.\n"));
5239 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
5240 * code, destroy response and log success.
5241 * @context is ISDS session context.
5242 * @service_name is name of SERVICE_DB_MANIPULATION service
5243 * @box is box identification
5244 * @user identifies user to remove
5245 * @approval is optional external approval of box manipulation
5246 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5247 * NULL, if you don't care. */
5248 static isds_error
build_send_manipulationboxuser_request_check_drop_response(
5249 struct isds_ctx
*context
, const xmlChar
*service_name
,
5250 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
5251 const struct isds_approval
*approval
, xmlChar
**refnumber
) {
5252 isds_error err
= IE_SUCCESS
;
5253 xmlNsPtr isds_ns
= NULL
;
5254 xmlNodePtr request
= NULL
, node
;
5257 if (!context
) return IE_INVALID_CONTEXT
;
5258 zfree(context
->long_message
);
5259 if (!service_name
|| service_name
[0] == '\0' || !box
|| !user
)
5263 /* Build NewAccessData request */
5264 request
= xmlNewNode(NULL
, service_name
);
5266 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
5267 isds_printf_message(context
, _("Could build %s request"),
5268 service_name_locale
);
5269 free(service_name_locale
);
5272 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
5274 isds_log_message(context
, _("Could not create ISDS name space"));
5275 xmlFreeNode(request
);
5278 xmlSetNs(request
, isds_ns
);
5280 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
5281 err
= insert_DbOwnerInfo(context
, box
, node
);
5282 if (err
) goto leave
;
5284 INSERT_ELEMENT(node
, request
, "dbUserInfo");
5285 err
= insert_DbUserInfo(context
, user
, node
);
5286 if (err
) goto leave
;
5288 err
= insert_GExtApproval(context
, approval
, request
);
5289 if (err
) goto leave
;
5291 /* Send request and check response*/
5292 err
= send_request_check_drop_response (context
,
5293 SERVICE_DB_MANIPULATION
, service_name
, &request
, refnumber
);
5296 xmlFreeNode(request
);
5301 /* Assign new user to given box.
5302 * @context is session context
5303 * @box is box identification
5304 * @user defines new user to add
5305 * @approval is optional external approval of box manipulation
5306 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5307 * NULL, if you don't care.*/
5308 isds_error
isds_add_user(struct isds_ctx
*context
,
5309 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
5310 const struct isds_approval
*approval
, char **refnumber
) {
5311 return build_send_manipulationboxuser_request_check_drop_response(context
,
5312 BAD_CAST
"AddDataBoxUser", box
, user
, approval
,
5313 (xmlChar
**) refnumber
);
5317 /* Remove user assigned to given box.
5318 * @context is session context
5319 * @box is box identification
5320 * @user identifies user to remove
5321 * @approval is optional external approval of box manipulation
5322 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5323 * NULL, if you don't care.*/
5324 isds_error
isds_delete_user(struct isds_ctx
*context
,
5325 const struct isds_DbOwnerInfo
*box
, const struct isds_DbUserInfo
*user
,
5326 const struct isds_approval
*approval
, char **refnumber
) {
5327 return build_send_manipulationboxuser_request_check_drop_response(context
,
5328 BAD_CAST
"DeleteDataBoxUser", box
, user
, approval
,
5329 (xmlChar
**) refnumber
);
5333 /* Find boxes suiting given criteria.
5334 * @criteria is filter. You should fill in at least some members.
5335 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
5336 * possibly empty. Input NULL or valid old structure.
5338 * IE_SUCCESS if search succeeded, @boxes contains useful data
5339 * IE_NOEXIST if no such box exists, @boxes will be NULL
5340 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
5341 * contains still valid data
5342 * other code if something bad happens. @boxes will be NULL. */
5343 isds_error
isds_FindDataBox(struct isds_ctx
*context
,
5344 const struct isds_DbOwnerInfo
*criteria
,
5345 struct isds_list
**boxes
) {
5346 isds_error err
= IE_SUCCESS
;
5347 _Bool truncated
= 0;
5348 xmlNsPtr isds_ns
= NULL
;
5349 xmlNodePtr request
= NULL
;
5350 xmlDocPtr response
= NULL
;
5351 xmlChar
*code
= NULL
, *message
= NULL
;
5352 xmlNodePtr db_owner_info
;
5353 xmlXPathContextPtr xpath_ctx
= NULL
;
5354 xmlXPathObjectPtr result
= NULL
;
5355 xmlChar
*string
= NULL
;
5358 if (!context
) return IE_INVALID_CONTEXT
;
5359 zfree(context
->long_message
);
5360 if (!boxes
) return IE_INVAL
;
5361 isds_list_free(boxes
);
5367 /* Check if connection is established
5368 * TODO: This check should be done downstairs. */
5369 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5372 /* Build FindDataBox request */
5373 request
= xmlNewNode(NULL
, BAD_CAST
"FindDataBox");
5375 isds_log_message(context
,
5376 _("Could build FindDataBox request"));
5379 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
5381 isds_log_message(context
, _("Could not create ISDS name space"));
5382 xmlFreeNode(request
);
5385 xmlSetNs(request
, isds_ns
);
5386 db_owner_info
= xmlNewChild(request
, NULL
, BAD_CAST
"dbOwnerInfo", NULL
);
5387 if (!db_owner_info
) {
5388 isds_log_message(context
, _("Could not add dbOwnerInfo child to "
5389 "FindDataBox element"));
5390 xmlFreeNode(request
);
5394 err
= insert_DbOwnerInfo(context
, criteria
, db_owner_info
);
5395 if (err
) goto leave
;
5398 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending FindDataBox request to ISDS\n"));
5401 err
= isds(context
, SERVICE_DB_SEARCH
, request
, &response
, NULL
, NULL
);
5403 /* Destroy request */
5404 xmlFreeNode(request
); request
= NULL
;
5407 isds_log(ILF_ISDS
, ILL_DEBUG
,
5408 _("Processing ISDS response on FindDataBox "
5409 "request failed\n"));
5413 /* Check for response status */
5414 err
= isds_response_status(context
, SERVICE_DB_SEARCH
, response
,
5415 &code
, &message
, NULL
);
5417 isds_log(ILF_ISDS
, ILL_DEBUG
,
5418 _("ISDS response on FindDataBox request is missing status\n"));
5422 /* Request processed, but nothing found */
5423 if (!xmlStrcmp(code
, BAD_CAST
"0002") ||
5424 !xmlStrcmp(code
, BAD_CAST
"5001")) {
5425 char *code_locale
= _isds_utf82locale((char*)code
);
5426 char *message_locale
= _isds_utf82locale((char*)message
);
5427 isds_log(ILF_ISDS
, ILL_DEBUG
,
5428 _("Server did not found any box on FindDataBox request "
5429 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
5430 isds_log_message(context
, message_locale
);
5432 free(message_locale
);
5437 /* Warning, not a error */
5438 if (!xmlStrcmp(code
, BAD_CAST
"0003")) {
5439 char *code_locale
= _isds_utf82locale((char*)code
);
5440 char *message_locale
= _isds_utf82locale((char*)message
);
5441 isds_log(ILF_ISDS
, ILL_DEBUG
,
5442 _("Server truncated response on FindDataBox request "
5443 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
5444 isds_log_message(context
, message_locale
);
5446 free(message_locale
);
5451 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5452 char *code_locale
= _isds_utf82locale((char*)code
);
5453 char *message_locale
= _isds_utf82locale((char*)message
);
5454 isds_log(ILF_ISDS
, ILL_DEBUG
,
5455 _("Server refused FindDataBox request "
5456 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
5457 isds_log_message(context
, message_locale
);
5459 free(message_locale
);
5464 xpath_ctx
= xmlXPathNewContext(response
);
5469 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5474 /* Extract boxes if they present */
5475 result
= xmlXPathEvalExpression(BAD_CAST
5476 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
5482 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5483 struct isds_list
*item
, *prev_item
= NULL
;
5484 for (int i
= 0; i
< result
->nodesetval
->nodeNr
; i
++) {
5485 item
= calloc(1, sizeof(*item
));
5491 item
->destructor
= (void (*)(void **))isds_DbOwnerInfo_free
;
5492 if (i
== 0) *boxes
= item
;
5493 else prev_item
->next
= item
;
5496 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
5497 err
= extract_DbOwnerInfo(context
,
5498 (struct isds_DbOwnerInfo
**) &(item
->data
), xpath_ctx
);
5499 if (err
) goto leave
;
5505 isds_list_free(boxes
);
5507 if (truncated
) err
= IE_2BIG
;
5511 xmlFreeNode(request
);
5512 xmlXPathFreeObject(result
);
5513 xmlXPathFreeContext(xpath_ctx
);
5517 xmlFreeDoc(response
);
5520 isds_log(ILF_ISDS
, ILL_DEBUG
,
5521 _("FindDataBox request processed by server successfully.\n"));
5527 /* Get status of a box.
5528 * @context is ISDS session context.
5529 * @box_id is UTF-8 encoded box identifier as zero terminated string
5530 * @box_status is return value of box status.
5532 * IE_SUCCESS if box has been found and its status retrieved
5533 * IE_NOEXIST if box is not known to ISDS server
5534 * or other appropriate error.
5535 * You can use isds_DbState to enumerate box status. However out of enum
5536 * range value can be returned too. This is feature because ISDS
5537 * specification leaves the set of values open.
5538 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
5539 * the box has been deleted, but ISDS still lists its former existence. */
5540 isds_error
isds_CheckDataBox(struct isds_ctx
*context
, const char *box_id
,
5541 long int *box_status
) {
5542 isds_error err
= IE_SUCCESS
;
5543 xmlNsPtr isds_ns
= NULL
;
5544 xmlNodePtr request
= NULL
, db_id
;
5545 xmlDocPtr response
= NULL
;
5546 xmlChar
*code
= NULL
, *message
= NULL
;
5547 xmlXPathContextPtr xpath_ctx
= NULL
;
5548 xmlXPathObjectPtr result
= NULL
;
5549 xmlChar
*string
= NULL
;
5551 if (!context
) return IE_INVALID_CONTEXT
;
5552 zfree(context
->long_message
);
5553 if (!box_status
|| !box_id
|| *box_id
== '\0') return IE_INVAL
;
5555 /* Check if connection is established
5556 * TODO: This check should be done downstairs. */
5557 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5560 /* Build CheckDataBox request */
5561 request
= xmlNewNode(NULL
, BAD_CAST
"CheckDataBox");
5563 isds_log_message(context
,
5564 _("Could build CheckDataBox request"));
5567 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
5569 isds_log_message(context
, _("Could not create ISDS name space"));
5570 xmlFreeNode(request
);
5573 xmlSetNs(request
, isds_ns
);
5574 db_id
= xmlNewTextChild(request
, NULL
, BAD_CAST
"dbID", (xmlChar
*) box_id
);
5576 isds_log_message(context
, _("Could not add dbID child to "
5577 "CheckDataBox element"));
5578 xmlFreeNode(request
);
5583 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending CheckDataBox request to ISDS\n"));
5586 err
= isds(context
, SERVICE_DB_SEARCH
, request
, &response
, NULL
, NULL
);
5588 /* Destroy request */
5589 xmlFreeNode(request
);
5592 isds_log(ILF_ISDS
, ILL_DEBUG
,
5593 _("Processing ISDS response on CheckDataBox "
5594 "request failed\n"));
5598 /* Check for response status */
5599 err
= isds_response_status(context
, SERVICE_DB_SEARCH
, response
,
5600 &code
, &message
, NULL
);
5602 isds_log(ILF_ISDS
, ILL_DEBUG
,
5603 _("ISDS response on CheckDataBox request is missing status\n"));
5607 /* Request processed, but nothing found */
5608 if (!xmlStrcmp(code
, BAD_CAST
"5001")) {
5609 char *box_id_locale
= _isds_utf82locale((char*)box_id
);
5610 char *code_locale
= _isds_utf82locale((char*)code
);
5611 char *message_locale
= _isds_utf82locale((char*)message
);
5612 isds_log(ILF_ISDS
, ILL_DEBUG
,
5613 _("Server did not found box %s on CheckDataBox request "
5614 "(code=%s, message=%s)\n"),
5615 box_id_locale
, code_locale
, message_locale
);
5616 isds_log_message(context
, message_locale
);
5617 free(box_id_locale
);
5619 free(message_locale
);
5625 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
5626 char *code_locale
= _isds_utf82locale((char*)code
);
5627 char *message_locale
= _isds_utf82locale((char*)message
);
5628 isds_log(ILF_ISDS
, ILL_DEBUG
,
5629 _("Server refused CheckDataBox request "
5630 "(code=%s, message=%s)\n"), code_locale
, message_locale
);
5631 isds_log_message(context
, message_locale
);
5633 free(message_locale
);
5639 xpath_ctx
= xmlXPathNewContext(response
);
5644 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
5648 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CheckDataBoxResponse",
5654 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
5655 isds_log_message(context
, _("Missing CheckDataBoxResponse element"));
5659 if (result
->nodesetval
->nodeNr
> 1) {
5660 isds_log_message(context
, _("Multiple CheckDataBoxResponse element"));
5664 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
5665 xmlXPathFreeObject(result
); result
= NULL
;
5667 EXTRACT_LONGINT("isds:dbState", box_status
, 1);
5672 xmlXPathFreeObject(result
);
5673 xmlXPathFreeContext(xpath_ctx
);
5677 xmlFreeDoc(response
);
5680 isds_log(ILF_ISDS
, ILL_DEBUG
,
5681 _("CheckDataBox request processed by server successfully.\n"));
5687 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
5688 * code, destroy response and log success.
5689 * @context is ISDS session context.
5690 * @service_name is name of SERVICE_DB_MANIPULATION service
5691 * @box_id is UTF-8 encoded box identifier as zero terminated string
5692 * @approval is optional external approval of box manipulation
5693 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5694 * NULL, if you don't care. */
5695 static isds_error
build_send_manipulationdbid_request_check_drop_response(
5696 struct isds_ctx
*context
, const xmlChar
*service_name
,
5697 const xmlChar
*box_id
, const struct isds_approval
*approval
,
5698 xmlChar
**refnumber
) {
5699 isds_error err
= IE_SUCCESS
;
5700 xmlDocPtr response
= NULL
;
5702 if (!context
) return IE_INVALID_CONTEXT
;
5703 zfree(context
->long_message
);
5704 if (!service_name
|| *service_name
== '\0' || !box_id
) return IE_INVAL
;
5706 /* Check if connection is established */
5707 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
5709 /* Do request and check for success */
5710 err
= build_send_dbid_request_check_response(context
,
5711 SERVICE_DB_MANIPULATION
, service_name
, box_id
, approval
,
5712 &response
, refnumber
);
5713 xmlFreeDoc(response
);
5716 char *service_name_locale
= _isds_utf82locale((char *) service_name
);
5717 isds_log(ILF_ISDS
, ILL_DEBUG
,
5718 _("%s request processed by server successfully.\n"),
5719 service_name_locale
);
5720 free(service_name_locale
);
5727 /* Switch box into state where box can receive commercial messages (off by
5729 * @context is ISDS session context.
5730 * @box_id is UTF-8 encoded box identifier as zero terminated string
5731 * @allow is true for enable, false for disable commercial messages income
5732 * @approval is optional external approval of box manipulation
5733 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5734 * NULL, if you don't care. */
5735 isds_error
isds_switch_commercial_receiving(struct isds_ctx
*context
,
5736 const char *box_id
, const _Bool allow
,
5737 const struct isds_approval
*approval
, char **refnumber
) {
5738 return build_send_manipulationdbid_request_check_drop_response(context
,
5739 (allow
) ? BAD_CAST
"SetOpenAddressing" :
5740 BAD_CAST
"ClearOpenAddressing",
5741 BAD_CAST box_id
, approval
, (xmlChar
**) refnumber
);
5745 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
5746 * message acceptance). This is just a box permission. Sender must apply
5747 * such role by sending each message.
5748 * @context is ISDS session context.
5749 * @box_id is UTF-8 encoded box identifier as zero terminated string
5750 * @allow is true for enable, false for disable OVM role permission
5751 * @approval is optional external approval of box manipulation
5752 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5753 * NULL, if you don't care. */
5754 isds_error
isds_switch_effective_ovm(struct isds_ctx
*context
,
5755 const char *box_id
, const _Bool allow
,
5756 const struct isds_approval
*approval
, char **refnumber
) {
5757 return build_send_manipulationdbid_request_check_drop_response(context
,
5758 (allow
) ? BAD_CAST
"SetEffectiveOVM" :
5759 BAD_CAST
"ClearEffectiveOVM",
5760 BAD_CAST box_id
, approval
, (xmlChar
**) refnumber
);
5764 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
5765 * code, destroy response and log success.
5766 * @context is ISDS session context.
5767 * @service_name is name of SERVICE_DB_MANIPULATION service
5768 * @owner is structure describing box
5769 * @approval is optional external approval of box manipulation
5770 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5771 * NULL, if you don't care. */
5772 static isds_error
build_send_manipulationdbowner_request_check_drop_response(
5773 struct isds_ctx
*context
, const xmlChar
*service_name
,
5774 const struct isds_DbOwnerInfo
*owner
,
5775 const struct isds_approval
*approval
, xmlChar
**refnumber
) {
5776 isds_error err
= IE_SUCCESS
;
5777 char *service_name_locale
= NULL
;
5778 xmlNodePtr request
= NULL
, db_owner_info
;
5779 xmlNsPtr isds_ns
= NULL
;
5782 if (!context
) return IE_INVALID_CONTEXT
;
5783 zfree(context
->long_message
);
5784 if (!service_name
|| *service_name
== '\0' || !owner
) return IE_INVAL
;
5786 service_name_locale
= _isds_utf82locale((char*)service_name
);
5787 if (!service_name_locale
) {
5793 request
= xmlNewNode(NULL
, service_name
);
5795 isds_printf_message(context
,
5796 _("Could not build %s request"), service_name_locale
);
5800 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
5802 isds_log_message(context
, _("Could not create ISDS name space"));
5806 xmlSetNs(request
, isds_ns
);
5809 /* Add XSD:tOwnerInfoInput child*/
5810 INSERT_ELEMENT(db_owner_info
, request
, "dbOwnerInfo");
5811 err
= insert_DbOwnerInfo(context
, owner
, db_owner_info
);
5812 if (err
) goto leave
;
5814 /* Add XSD:gExtApproval*/
5815 err
= insert_GExtApproval(context
, approval
, request
);
5816 if (err
) goto leave
;
5818 /* Send it to server and process response */
5819 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
5820 service_name
, &request
, refnumber
);
5823 xmlFreeNode(request
);
5824 free(service_name_locale
);
5830 /* Switch box accessibility state on request of box owner.
5831 * Despite the name, owner must do the request off-line. This function is
5832 * designed for such off-line meeting points (e.g. Czech POINT).
5833 * @context is ISDS session context.
5834 * @box identifies box to switch accessibility state.
5835 * @allow is true for making accessible, false to disallow access.
5836 * @approval is optional external approval of box manipulation
5837 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5838 * NULL, if you don't care. */
5839 isds_error
isds_switch_box_accessibility_on_owner_request(
5840 struct isds_ctx
*context
, const struct isds_DbOwnerInfo
*box
,
5841 const _Bool allow
, const struct isds_approval
*approval
,
5843 return build_send_manipulationdbowner_request_check_drop_response(context
,
5844 (allow
) ? BAD_CAST
"EnableOwnDataBox" :
5845 BAD_CAST
"DisableOwnDataBox",
5846 box
, approval
, (xmlChar
**) refnumber
);
5850 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
5852 * @context is ISDS session context.
5853 * @box identifies box to switch accessibility state.
5854 * @since is date since accessibility has been denied. This can be past too.
5855 * Only tm_year, tm_mon and tm_mday carry sane value.
5856 * @approval is optional external approval of box manipulation
5857 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5858 * NULL, if you don't care. */
5859 isds_error
isds_disable_box_accessibility_externaly(
5860 struct isds_ctx
*context
, const struct isds_DbOwnerInfo
*box
,
5861 const struct tm
*since
, const struct isds_approval
*approval
,
5863 isds_error err
= IE_SUCCESS
;
5864 char *service_name_locale
= NULL
;
5865 xmlNodePtr request
= NULL
, node
;
5866 xmlNsPtr isds_ns
= NULL
;
5867 xmlChar
*string
= NULL
;
5870 if (!context
) return IE_INVALID_CONTEXT
;
5871 zfree(context
->long_message
);
5872 if (!box
|| !since
) return IE_INVAL
;
5875 request
= xmlNewNode(NULL
, BAD_CAST
"DisableDataBoxExternally");
5877 isds_printf_message(context
,
5878 _("Could not build %s request"), "DisableDataBoxExternally");
5882 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
5884 isds_log_message(context
, _("Could not create ISDS name space"));
5888 xmlSetNs(request
, isds_ns
);
5891 /* Add @box identification */
5892 INSERT_ELEMENT(node
, request
, "dbOwnerInfo");
5893 err
= insert_DbOwnerInfo(context
, box
, node
);
5894 if (err
) goto leave
;
5896 /* Add @since date */
5897 err
= tm2datestring(since
, &string
);
5899 isds_log_message(context
,
5900 _("Could not convert `since' argument to ISO date string"));
5903 INSERT_STRING(request
, "dbOwnerDisableDate", string
);
5907 err
= insert_GExtApproval(context
, approval
, request
);
5908 if (err
) goto leave
;
5910 /* Send it to server and process response */
5911 err
= send_request_check_drop_response(context
, SERVICE_DB_MANIPULATION
,
5912 BAD_CAST
"DisableDataBoxExternally", &request
,
5913 (xmlChar
**) refnumber
);
5917 xmlFreeNode(request
);
5918 free(service_name_locale
);
5924 /* Insert struct isds_message data (envelope (recipient data optional) and
5925 * documents) into XML tree
5926 * @context is session context
5927 * @outgoing_message is libisds structure with message data
5928 * @create_message is XML CreateMessage or CreateMultipleMessage element
5929 * @process_recipient true for recipient data serialization, false for no
5931 static isds_error
insert_envelope_files(struct isds_ctx
*context
,
5932 const struct isds_message
*outgoing_message
, xmlNodePtr create_message
,
5933 const _Bool process_recipient
) {
5935 isds_error err
= IE_SUCCESS
;
5936 xmlNodePtr envelope
, dm_files
, node
;
5937 xmlChar
*string
= NULL
;
5939 if (!context
) return IE_INVALID_CONTEXT
;
5940 if (!outgoing_message
|| !create_message
) return IE_INVAL
;
5943 /* Build envelope */
5944 envelope
= xmlNewChild(create_message
, NULL
, BAD_CAST
"dmEnvelope", NULL
);
5946 isds_printf_message(context
, _("Could not add dmEnvelope child to "
5947 "%s element"), create_message
->name
);
5951 if (!outgoing_message
->envelope
) {
5952 isds_log_message(context
, _("Outgoing message is missing envelope"));
5957 /* Insert optional message type */
5958 err
= insert_message_type(context
, outgoing_message
->envelope
->dmType
,
5960 if (err
) goto leave
;
5962 INSERT_STRING(envelope
, "dmSenderOrgUnit",
5963 outgoing_message
->envelope
->dmSenderOrgUnit
);
5964 INSERT_LONGINT(envelope
, "dmSenderOrgUnitNum",
5965 outgoing_message
->envelope
->dmSenderOrgUnitNum
, string
);
5967 if (process_recipient
) {
5968 if (!outgoing_message
->envelope
->dbIDRecipient
) {
5969 isds_log_message(context
,
5970 _("Outgoing message is missing recipient box identifier"));
5974 INSERT_STRING(envelope
, "dbIDRecipient",
5975 outgoing_message
->envelope
->dbIDRecipient
);
5977 INSERT_STRING(envelope
, "dmRecipientOrgUnit",
5978 outgoing_message
->envelope
->dmRecipientOrgUnit
);
5979 INSERT_LONGINT(envelope
, "dmRecipientOrgUnitNum",
5980 outgoing_message
->envelope
->dmRecipientOrgUnitNum
, string
);
5981 INSERT_STRING(envelope
, "dmToHands",
5982 outgoing_message
->envelope
->dmToHands
);
5985 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmAnnotation
, 0, 255,
5987 INSERT_STRING(envelope
, "dmAnnotation",
5988 outgoing_message
->envelope
->dmAnnotation
);
5990 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientRefNumber
,
5991 0, 50, "dmRecipientRefNumber");
5992 INSERT_STRING(envelope
, "dmRecipientRefNumber",
5993 outgoing_message
->envelope
->dmRecipientRefNumber
);
5995 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderRefNumber
,
5996 0, 50, "dmSenderRefNumber");
5997 INSERT_STRING(envelope
, "dmSenderRefNumber",
5998 outgoing_message
->envelope
->dmSenderRefNumber
);
6000 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmRecipientIdent
,
6001 0, 50, "dmRecipientIdent");
6002 INSERT_STRING(envelope
, "dmRecipientIdent",
6003 outgoing_message
->envelope
->dmRecipientIdent
);
6005 CHECK_FOR_STRING_LENGTH(outgoing_message
->envelope
->dmSenderIdent
,
6006 0, 50, "dmSenderIdent");
6007 INSERT_STRING(envelope
, "dmSenderIdent",
6008 outgoing_message
->envelope
->dmSenderIdent
);
6010 INSERT_LONGINT(envelope
, "dmLegalTitleLaw",
6011 outgoing_message
->envelope
->dmLegalTitleLaw
, string
);
6012 INSERT_LONGINT(envelope
, "dmLegalTitleYear",
6013 outgoing_message
->envelope
->dmLegalTitleYear
, string
);
6014 INSERT_STRING(envelope
, "dmLegalTitleSect",
6015 outgoing_message
->envelope
->dmLegalTitleSect
);
6016 INSERT_STRING(envelope
, "dmLegalTitlePar",
6017 outgoing_message
->envelope
->dmLegalTitlePar
);
6018 INSERT_STRING(envelope
, "dmLegalTitlePoint",
6019 outgoing_message
->envelope
->dmLegalTitlePoint
);
6021 INSERT_BOOLEAN(envelope
, "dmPersonalDelivery",
6022 outgoing_message
->envelope
->dmPersonalDelivery
);
6023 INSERT_BOOLEAN(envelope
, "dmAllowSubstDelivery",
6024 outgoing_message
->envelope
->dmAllowSubstDelivery
);
6026 /* ???: Should we require value for dbEffectiveOVM sender?
6027 * ISDS has default as true */
6028 INSERT_BOOLEAN(envelope
, "dmOVM", outgoing_message
->envelope
->dmOVM
);
6031 /* Append dmFiles */
6032 if (!outgoing_message
->documents
) {
6033 isds_log_message(context
,
6034 _("Outgoing message is missing list of documents"));
6038 dm_files
= xmlNewChild(create_message
, NULL
, BAD_CAST
"dmFiles", NULL
);
6040 isds_printf_message(context
, _("Could not add dmFiles child to "
6041 "%s element"), create_message
->name
);
6046 /* Check for document hierarchy */
6047 err
= _isds_check_documents_hierarchy(context
, outgoing_message
->documents
);
6048 if (err
) goto leave
;
6050 /* Process each document */
6051 for (struct isds_list
*item
=
6052 (struct isds_list
*) outgoing_message
->documents
;
6053 item
; item
= item
->next
) {
6055 isds_log_message(context
,
6056 _("List of documents contains empty item"));
6060 /* FIXME: Check for dmFileMetaType and for document references.
6061 * Only first document can be of MAIN type */
6062 err
= insert_document(context
, (struct isds_document
*) item
->data
,
6065 if (err
) goto leave
;
6074 /* Send a message via ISDS to a recipient
6075 * @context is session context
6076 * @outgoing_message is message to send; Some members are mandatory (like
6077 * dbIDRecipient), some are optional and some are irrelevant (especially data
6078 * about sender). Included pointer to isds_list documents must contain at
6079 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
6080 * members will be filled with valid data from ISDS. Exact list of write
6081 * members is subject to change. Currently dmId is changed.
6082 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
6083 isds_error
isds_send_message(struct isds_ctx
*context
,
6084 struct isds_message
*outgoing_message
) {
6086 isds_error err
= IE_SUCCESS
;
6087 xmlNsPtr isds_ns
= NULL
;
6088 xmlNodePtr request
= NULL
;
6089 xmlDocPtr response
= NULL
;
6090 xmlChar
*code
= NULL
, *message
= NULL
;
6091 xmlXPathContextPtr xpath_ctx
= NULL
;
6092 xmlXPathObjectPtr result
= NULL
;
6093 _Bool message_is_complete
= 0;
6095 if (!context
) return IE_INVALID_CONTEXT
;
6096 zfree(context
->long_message
);
6097 if (!outgoing_message
) return IE_INVAL
;
6099 /* Check if connection is established
6100 * TODO: This check should be done downstairs. */
6101 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
6104 /* Build CreateMessage request */
6105 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMessage");
6107 isds_log_message(context
,
6108 _("Could build CreateMessage request"));
6111 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6113 isds_log_message(context
, _("Could not create ISDS name space"));
6114 xmlFreeNode(request
);
6117 xmlSetNs(request
, isds_ns
);
6119 /* Append envelope and files */
6120 err
= insert_envelope_files(context
, outgoing_message
, request
, 1);
6121 if (err
) goto leave
;
6124 /* Signal we can serialize message since now */
6125 message_is_complete
= 1;
6128 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Sending CreateMessage request to ISDS\n"));
6131 err
= isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
6133 /* Don't' destroy request, we want to provide it to application later */
6136 isds_log(ILF_ISDS
, ILL_DEBUG
,
6137 _("Processing ISDS response on CreateMessage "
6138 "request failed\n"));
6142 /* Check for response status */
6143 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
6144 &code
, &message
, NULL
);
6146 isds_log(ILF_ISDS
, ILL_DEBUG
,
6147 _("ISDS response on CreateMessage request "
6148 "is missing status\n"));
6152 /* Request processed, but refused by server or server failed */
6153 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
6154 char *box_id_locale
=
6155 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
6156 char *code_locale
= _isds_utf82locale((char*)code
);
6157 char *message_locale
= _isds_utf82locale((char*)message
);
6158 isds_log(ILF_ISDS
, ILL_DEBUG
,
6159 _("Server did not accept message for %s on CreateMessage "
6160 "request (code=%s, message=%s)\n"),
6161 box_id_locale
, code_locale
, message_locale
);
6162 isds_log_message(context
, message_locale
);
6163 free(box_id_locale
);
6165 free(message_locale
);
6172 xpath_ctx
= xmlXPathNewContext(response
);
6177 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6181 result
= xmlXPathEvalExpression(BAD_CAST
"/isds:CreateMessageResponse",
6187 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6188 isds_log_message(context
, _("Missing CreateMessageResponse element"));
6192 if (result
->nodesetval
->nodeNr
> 1) {
6193 isds_log_message(context
, _("Multiple CreateMessageResponse element"));
6197 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
6198 xmlXPathFreeObject(result
); result
= NULL
;
6200 if (outgoing_message
->envelope
->dmID
) {
6201 free(outgoing_message
->envelope
->dmID
);
6202 outgoing_message
->envelope
->dmID
= NULL
;
6204 EXTRACT_STRING("isds:dmID", outgoing_message
->envelope
->dmID
);
6205 if (!outgoing_message
->envelope
->dmID
) {
6206 isds_log(ILF_ISDS
, ILL_ERR
, _("Server accepted sent message, "
6207 "but did not return assigned message ID\n"));
6211 /* TODO: Serialize message into structure member raw */
6212 /* XXX: Each web service transport message in different format.
6213 * Therefore it's not possible to save them directly.
6214 * To save them, one must figure out common format.
6215 * We can leave it on application, or we can implement the ESS format. */
6216 /*if (message_is_complete) {
6217 if (outgoing_message->envelope->dmID) {
6219 /* Add assigned message ID as first child*/
6220 /*xmlNodePtr dmid_text = xmlNewText(
6221 (xmlChar *) outgoing_message->envelope->dmID);
6222 if (!dmid_text) goto serialization_failed;
6224 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
6226 if (!dmid_element) {
6227 xmlFreeNode(dmid_text);
6228 goto serialization_failed;
6231 xmlNodePtr dmid_element_with_text =
6232 xmlAddChild(dmid_element, dmid_text);
6233 if (!dmid_element_with_text) {
6234 xmlFreeNode(dmid_element);
6235 xmlFreeNode(dmid_text);
6236 goto serialization_failed;
6239 node = xmlAddPrevSibling(envelope->childern,
6240 dmid_element_with_text);
6242 xmlFreeNodeList(dmid_element_with_text);
6243 goto serialization_failed;
6247 /* Serialize message with ID into raw */
6248 /*buffer = serialize_element(envelope)*/
6251 serialization_failed:
6256 xmlXPathFreeObject(result
);
6257 xmlXPathFreeContext(xpath_ctx
);
6261 xmlFreeDoc(response
);
6262 xmlFreeNode(request
);
6265 isds_log(ILF_ISDS
, ILL_DEBUG
,
6266 _("CreateMessage request processed by server "
6267 "successfully.\n"));
6273 /* Send a message via ISDS to a multiple recipients
6274 * @context is session context
6275 * @outgoing_message is message to send; Some members are mandatory,
6276 * some are optional and some are irrelevant (especially data
6277 * about sender). Data about recipient will be substituted by ISDS from
6278 * @copies. Included pointer to isds_list documents must
6279 * contain at least one document of FILEMETATYPE_MAIN.
6280 * @copies is list of isds_message_copy structures addressing all desired
6281 * recipients. This is read-write structure, some members will be filled with
6282 * valid data from ISDS (message IDs, error codes, error descriptions).
6284 * ISDS_SUCCESS if all messages have been sent
6285 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
6286 * succeeded messages can be identified by copies->data->error),
6287 * or other error code if something other goes wrong. */
6288 isds_error
isds_send_message_to_multiple_recipients(struct isds_ctx
*context
,
6289 const struct isds_message
*outgoing_message
,
6290 struct isds_list
*copies
) {
6292 isds_error err
= IE_SUCCESS
, append_err
;
6293 xmlNsPtr isds_ns
= NULL
;
6294 xmlNodePtr request
= NULL
, recipients
, recipient
, node
;
6295 struct isds_list
*item
;
6296 struct isds_message_copy
*copy
;
6297 xmlDocPtr response
= NULL
;
6298 xmlChar
*code
= NULL
, *message
= NULL
;
6299 xmlXPathContextPtr xpath_ctx
= NULL
;
6300 xmlXPathObjectPtr result
= NULL
;
6301 xmlChar
*string
= NULL
;
6304 if (!context
) return IE_INVALID_CONTEXT
;
6305 zfree(context
->long_message
);
6306 if (!outgoing_message
|| !copies
) return IE_INVAL
;
6308 /* Check if connection is established
6309 * TODO: This check should be done downstairs. */
6310 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
6313 /* Build CreateMultipleMessage request */
6314 request
= xmlNewNode(NULL
, BAD_CAST
"CreateMultipleMessage");
6316 isds_log_message(context
,
6317 _("Could not build CreateMultipleMessage request"));
6320 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6322 isds_log_message(context
, _("Could not create ISDS name space"));
6323 xmlFreeNode(request
);
6326 xmlSetNs(request
, isds_ns
);
6329 /* Build recipients */
6330 recipients
= xmlNewChild(request
, NULL
, BAD_CAST
"dmRecipients", NULL
);
6332 isds_log_message(context
, _("Could not add dmRecipients child to "
6333 "CreateMultipleMessage element"));
6334 xmlFreeNode(request
);
6338 /* Insert each recipient */
6339 for (item
= copies
; item
; item
= item
->next
) {
6340 copy
= (struct isds_message_copy
*) item
->data
;
6342 isds_log_message(context
,
6343 _("`copies' list item contains empty data"));
6348 recipient
= xmlNewChild(recipients
, NULL
, BAD_CAST
"dmRecipient", NULL
);
6350 isds_log_message(context
, _("Could not add dmRecipient child to "
6351 "dmRecipients element"));
6356 if (!copy
->dbIDRecipient
) {
6357 isds_log_message(context
,
6358 _("Message copy is missing recipient box identifier"));
6362 INSERT_STRING(recipient
, "dbIDRecipient", copy
->dbIDRecipient
);
6363 INSERT_STRING(recipient
, "dmRecipientOrgUnit",
6364 copy
->dmRecipientOrgUnit
);
6365 INSERT_LONGINT(recipient
, "dmRecipientOrgUnitNum",
6366 copy
->dmRecipientOrgUnitNum
, string
);
6367 INSERT_STRING(recipient
, "dmToHands", copy
->dmToHands
);
6370 /* Append envelope and files */
6371 err
= insert_envelope_files(context
, outgoing_message
, request
, 0);
6372 if (err
) goto leave
;
6375 isds_log(ILF_ISDS
, ILL_DEBUG
,
6376 _("Sending CreateMultipleMessage request to ISDS\n"));
6379 err
= isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
6381 isds_log(ILF_ISDS
, ILL_DEBUG
,
6382 _("Processing ISDS response on CreateMultipleMessage "
6383 "request failed\n"));
6387 /* Check for response status */
6388 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
6389 &code
, &message
, NULL
);
6391 isds_log(ILF_ISDS
, ILL_DEBUG
,
6392 _("ISDS response on CreateMultipleMessage request "
6393 "is missing status\n"));
6397 /* Request processed, but some copies failed */
6398 if (!xmlStrcmp(code
, BAD_CAST
"0004")) {
6399 char *box_id_locale
=
6400 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
6401 char *code_locale
= _isds_utf82locale((char*)code
);
6402 char *message_locale
= _isds_utf82locale((char*)message
);
6403 isds_log(ILF_ISDS
, ILL_DEBUG
,
6404 _("Server did accept message for multiple recipients "
6405 "on CreateMultipleMessage request but delivery to "
6406 "some of them failed (code=%s, message=%s)\n"),
6407 box_id_locale
, code_locale
, message_locale
);
6408 isds_log_message(context
, message_locale
);
6409 free(box_id_locale
);
6411 free(message_locale
);
6412 err
= IE_PARTIAL_SUCCESS
;
6415 /* Request refused by server as whole */
6416 else if (xmlStrcmp(code
, BAD_CAST
"0000")) {
6417 char *box_id_locale
=
6418 _isds_utf82locale((char*)outgoing_message
->envelope
->dbIDRecipient
);
6419 char *code_locale
= _isds_utf82locale((char*)code
);
6420 char *message_locale
= _isds_utf82locale((char*)message
);
6421 isds_log(ILF_ISDS
, ILL_DEBUG
,
6422 _("Server did not accept message for multiple recipients "
6423 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
6424 box_id_locale
, code_locale
, message_locale
);
6425 isds_log_message(context
, message_locale
);
6426 free(box_id_locale
);
6428 free(message_locale
);
6435 xpath_ctx
= xmlXPathNewContext(response
);
6440 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6444 result
= xmlXPathEvalExpression(
6445 BAD_CAST
"/isds:CreateMultipleMessageResponse"
6446 "/isds:dmMultipleStatus/isds:dmSingleStatus",
6452 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6453 isds_log_message(context
, _("Missing isds:dmSingleStatus element"));
6458 /* Extract message ID and delivery status for each copy */
6459 for (item
= copies
, i
= 0; item
&& i
< result
->nodesetval
->nodeNr
;
6460 item
= item
->next
, i
++) {
6461 copy
= (struct isds_message_copy
*) item
->data
;
6462 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[i
];
6464 append_err
= append_TMStatus(context
, copy
, xpath_ctx
);
6470 if (item
|| i
< result
->nodesetval
->nodeNr
) {
6471 isds_printf_message(context
, _("ISDS returned unexpected number of "
6472 "message copy delivery states: %d"),
6473 result
->nodesetval
->nodeNr
);
6482 xmlXPathFreeObject(result
);
6483 xmlXPathFreeContext(xpath_ctx
);
6487 xmlFreeDoc(response
);
6488 xmlFreeNode(request
);
6491 isds_log(ILF_ISDS
, ILL_DEBUG
,
6492 _("CreateMultipleMessageResponse request processed by server "
6493 "successfully.\n"));
6499 /* Get list of messages. This is common core for getting sent or received
6501 * Any criterion argument can be NULL, if you don't care about it.
6502 * @context is session context. Must not be NULL.
6503 * @outgoing_direction is true if you want list of outgoing messages,
6504 * it's false if you want incoming messages.
6505 * @from_time is minimal time and date of message sending inclusive.
6506 * @to_time is maximal time and date of message sending inclusive
6507 * @organization_unit_number is number of sender/recipient respectively.
6508 * @status_filter is bit field of isds_message_status values. Use special
6509 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
6510 * all values, you can use bit-wise arithmetic if you want.)
6511 * @offset is index of first message we are interested in. First message is 1.
6512 * Set to 0 (or 1) if you don't care.
6513 * @number is maximal length of list you want to get as input value, outputs
6514 * number of messages matching these criteria. Can be NULL if you don't care
6515 * (applies to output value either).
6516 * @messages is automatically reallocated list of isds_message's. Be ware that
6517 * it returns only brief overview (envelope and some other fields) about each
6518 * message, not the complete message. FIXME: Specify exact fields.
6519 * The list is sorted by delivery time in ascending order.
6521 * you don't care about don't need the data (useful if you want to know only
6522 * the @number). If you provide &NULL, list will be allocated on heap, if you
6523 * provide pointer to non-NULL, list will be freed automatically at first. Also
6524 * in case of error the list will be NULLed.
6525 * @return IE_SUCCESS or appropriate error code. */
6526 static isds_error
isds_get_list_of_messages(struct isds_ctx
*context
,
6527 _Bool outgoing_direction
,
6528 const struct timeval
*from_time
, const struct timeval
*to_time
,
6529 const long int *organization_unit_number
,
6530 const unsigned int status_filter
,
6531 const unsigned long int offset
, unsigned long int *number
,
6532 struct isds_list
**messages
) {
6534 isds_error err
= IE_SUCCESS
;
6535 xmlNsPtr isds_ns
= NULL
;
6536 xmlNodePtr request
= NULL
, node
;
6537 xmlDocPtr response
= NULL
;
6538 xmlChar
*code
= NULL
, *message
= NULL
;
6539 xmlXPathContextPtr xpath_ctx
= NULL
;
6540 xmlXPathObjectPtr result
= NULL
;
6541 xmlChar
*string
= NULL
;
6542 long unsigned int count
= 0;
6544 if (!context
) return IE_INVALID_CONTEXT
;
6545 zfree(context
->long_message
);
6547 /* Free former message list if any */
6548 if (messages
) isds_list_free(messages
);
6550 /* Check if connection is established
6551 * TODO: This check should be done downstairs. */
6552 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
6554 /* Build GetListOf*Messages request */
6555 request
= xmlNewNode(NULL
,
6556 (outgoing_direction
) ?
6557 BAD_CAST
"GetListOfSentMessages" :
6558 BAD_CAST
"GetListOfReceivedMessages"
6561 isds_log_message(context
,
6562 (outgoing_direction
) ?
6563 _("Could not build GetListOfSentMessages request") :
6564 _("Could not build GetListOfReceivedMessages request")
6568 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6570 isds_log_message(context
, _("Could not create ISDS name space"));
6571 xmlFreeNode(request
);
6574 xmlSetNs(request
, isds_ns
);
6578 err
= timeval2timestring(from_time
, &string
);
6579 if (err
) goto leave
;
6581 INSERT_STRING(request
, "dmFromTime", string
);
6582 free(string
); string
= NULL
;
6585 err
= timeval2timestring(to_time
, &string
);
6586 if (err
) goto leave
;
6588 INSERT_STRING(request
, "dmToTime", string
);
6589 free(string
); string
= NULL
;
6591 if (outgoing_direction
) {
6592 INSERT_LONGINT(request
, "dmSenderOrgUnitNum",
6593 organization_unit_number
, string
);
6595 INSERT_LONGINT(request
, "dmRecipientOrgUnitNum",
6596 organization_unit_number
, string
);
6599 if (status_filter
> MESSAGESTATE_ANY
) {
6600 isds_printf_message(context
,
6601 _("Invalid message state filter value: %ld"), status_filter
);
6605 INSERT_ULONGINTNOPTR(request
, "dmStatusFilter", status_filter
, string
);
6608 INSERT_ULONGINTNOPTR(request
, "dmOffset", offset
, string
);
6610 INSERT_STRING(request
, "dmOffset", "1");
6613 /* number 0 means no limit */
6614 if (number
&& *number
== 0) {
6615 INSERT_STRING(request
, "dmLimit", NULL
);
6617 INSERT_ULONGINT(request
, "dmLimit", number
, string
);
6621 isds_log(ILF_ISDS
, ILL_DEBUG
,
6622 (outgoing_direction
) ?
6623 _("Sending GetListOfSentMessages request to ISDS\n") :
6624 _("Sending GetListOfReceivedMessages request to ISDS\n")
6628 err
= isds(context
, SERVICE_DM_INFO
, request
, &response
, NULL
, NULL
);
6629 xmlFreeNode(request
); request
= NULL
;
6632 isds_log(ILF_ISDS
, ILL_DEBUG
,
6633 (outgoing_direction
) ?
6634 _("Processing ISDS response on GetListOfSentMessages "
6635 "request failed\n") :
6636 _("Processing ISDS response on GetListOfReceivedMessages "
6642 /* Check for response status */
6643 err
= isds_response_status(context
, SERVICE_DM_INFO
, response
,
6644 &code
, &message
, NULL
);
6646 isds_log(ILF_ISDS
, ILL_DEBUG
,
6647 (outgoing_direction
) ?
6648 _("ISDS response on GetListOfSentMessages request "
6649 "is missing status\n") :
6650 _("ISDS response on GetListOfReceivedMessages request "
6651 "is missing status\n")
6656 /* Request processed, but nothing found */
6657 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
6658 char *code_locale
= _isds_utf82locale((char*)code
);
6659 char *message_locale
= _isds_utf82locale((char*)message
);
6660 isds_log(ILF_ISDS
, ILL_DEBUG
,
6661 (outgoing_direction
) ?
6662 _("Server refused GetListOfSentMessages request "
6663 "(code=%s, message=%s)\n") :
6664 _("Server refused GetListOfReceivedMessages request "
6665 "(code=%s, message=%s)\n"),
6666 code_locale
, message_locale
);
6667 isds_log_message(context
, message_locale
);
6669 free(message_locale
);
6676 xpath_ctx
= xmlXPathNewContext(response
);
6681 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
6685 result
= xmlXPathEvalExpression(
6686 (outgoing_direction
) ?
6687 BAD_CAST
"/isds:GetListOfSentMessagesResponse/"
6688 "isds:dmRecords/isds:dmRecord" :
6689 BAD_CAST
"/isds:GetListOfReceivedMessagesResponse/"
6690 "isds:dmRecords/isds:dmRecord",
6697 /* Fill output arguments in */
6698 if (!xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
6699 struct isds_envelope
*envelope
;
6700 struct isds_list
*item
= NULL
, *last_item
= NULL
;
6702 for (count
= 0; count
< result
->nodesetval
->nodeNr
; count
++) {
6703 /* Create new message */
6704 item
= calloc(1, sizeof(*item
));
6709 item
->destructor
= (void(*)(void**)) &isds_message_free
;
6710 item
->data
= calloc(1, sizeof(struct isds_message
));
6712 isds_list_free(&item
);
6717 /* Extract envelope data */
6718 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[count
];
6720 err
= extract_DmRecord(context
, &envelope
, xpath_ctx
);
6722 isds_list_free(&item
);
6726 /* Attach extracted envelope */
6727 ((struct isds_message
*) item
->data
)->envelope
= envelope
;
6729 /* Append new message into the list */
6731 *messages
= last_item
= item
;
6733 last_item
->next
= item
;
6738 if (number
) *number
= count
;
6742 isds_list_free(messages
);
6746 xmlXPathFreeObject(result
);
6747 xmlXPathFreeContext(xpath_ctx
);
6751 xmlFreeDoc(response
);
6752 xmlFreeNode(request
);
6755 isds_log(ILF_ISDS
, ILL_DEBUG
,
6756 (outgoing_direction
) ?
6757 _("GetListOfSentMessages request processed by server "
6758 "successfully.\n") :
6759 _("GetListOfReceivedMessages request processed by server "
6766 /* Get list of outgoing (already sent) messages.
6767 * Any criterion argument can be NULL, if you don't care about it.
6768 * @context is session context. Must not be NULL.
6769 * @from_time is minimal time and date of message sending inclusive.
6770 * @to_time is maximal time and date of message sending inclusive
6771 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
6772 * @status_filter is bit field of isds_message_status values. Use special
6773 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
6774 * all values, you can use bit-wise arithmetic if you want.)
6775 * @offset is index of first message we are interested in. First message is 1.
6776 * Set to 0 (or 1) if you don't care.
6777 * @number is maximal length of list you want to get as input value, outputs
6778 * number of messages matching these criteria. Can be NULL if you don't care
6779 * (applies to output value either).
6780 * @messages is automatically reallocated list of isds_message's. Be ware that
6781 * it returns only brief overview (envelope and some other fields) about each
6782 * message, not the complete message. FIXME: Specify exact fields.
6783 * The list is sorted by delivery time in ascending order.
6784 * Use NULL if you don't care about the meta data (useful if you want to know
6785 * only the @number). If you provide &NULL, list will be allocated on heap,
6786 * if you provide pointer to non-NULL, list will be freed automatically at first.
6787 * Also in case of error the list will be NULLed.
6788 * @return IE_SUCCESS or appropriate error code. */
6789 isds_error
isds_get_list_of_sent_messages(struct isds_ctx
*context
,
6790 const struct timeval
*from_time
, const struct timeval
*to_time
,
6791 const long int *dmSenderOrgUnitNum
, const unsigned int status_filter
,
6792 const unsigned long int offset
, unsigned long int *number
,
6793 struct isds_list
**messages
) {
6795 return isds_get_list_of_messages(
6797 from_time
, to_time
, dmSenderOrgUnitNum
, status_filter
,
6803 /* Get list of incoming (addressed to you) messages.
6804 * Any criterion argument can be NULL, if you don't care about it.
6805 * @context is session context. Must not be NULL.
6806 * @from_time is minimal time and date of message sending inclusive.
6807 * @to_time is maximal time and date of message sending inclusive
6808 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
6809 * @status_filter is bit field of isds_message_status values. Use special
6810 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
6811 * all values, you can use bit-wise arithmetic if you want.)
6812 * @offset is index of first message we are interested in. First message is 1.
6813 * Set to 0 (or 1) if you don't care.
6814 * @number is maximal length of list you want to get as input value, outputs
6815 * number of messages matching these criteria. Can be NULL if you don't care
6816 * (applies to output value either).
6817 * @messages is automatically reallocated list of isds_message's. Be ware that
6818 * it returns only brief overview (envelope and some other fields) about each
6819 * message, not the complete message. FIXME: Specify exact fields.
6820 * Use NULL if you don't care about the meta data (useful if you want to know
6821 * only the @number). If you provide &NULL, list will be allocated on heap,
6822 * if you provide pointer to non-NULL, list will be freed automatically at first.
6823 * Also in case of error the list will be NULLed.
6824 * @return IE_SUCCESS or appropriate error code. */
6825 isds_error
isds_get_list_of_received_messages(struct isds_ctx
*context
,
6826 const struct timeval
*from_time
, const struct timeval
*to_time
,
6827 const long int *dmRecipientOrgUnitNum
,
6828 const unsigned int status_filter
,
6829 const unsigned long int offset
, unsigned long int *number
,
6830 struct isds_list
**messages
) {
6832 return isds_get_list_of_messages(
6834 from_time
, to_time
, dmRecipientOrgUnitNum
, status_filter
,
6840 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
6842 * @context is session context
6843 * @service is ISDS WS service handler
6844 * @service_name is name of SERVICE_DM_OPERATIONS
6845 * @message_id is message ID to send as service argument to ISDS
6846 * @response is server SOAP body response as XML document
6847 * @raw_response is automatically reallocated bit stream with response body. Use
6848 * NULL if you don't care
6849 * @raw_response_length is size of @raw_response in bytes
6850 * @code is ISDS status code
6851 * @status_message is ISDS status message
6852 * @return error coded from lower layer, context message will be set up
6854 static isds_error
build_send_check_message_request(struct isds_ctx
*context
,
6855 const isds_service service
, const xmlChar
*service_name
,
6856 const char *message_id
,
6857 xmlDocPtr
*response
, void **raw_response
, size_t *raw_response_length
,
6858 xmlChar
**code
, xmlChar
**status_message
) {
6860 isds_error err
= IE_SUCCESS
;
6861 char *service_name_locale
= NULL
, *message_id_locale
= NULL
;
6862 xmlNodePtr request
= NULL
, node
;
6863 xmlNsPtr isds_ns
= NULL
;
6865 if (!context
) return IE_INVALID_CONTEXT
;
6866 if (!service_name
|| !message_id
) return IE_INVAL
;
6867 if (!response
|| !code
|| !status_message
) return IE_INVAL
;
6868 if (!raw_response_length
&& raw_response
) return IE_INVAL
;
6870 /* Free output argument */
6871 xmlFreeDoc(*response
); *response
= NULL
;
6872 if (raw_response
) zfree(*raw_response
);
6874 free(*status_message
);
6877 /* Check if connection is established
6878 * TODO: This check should be done downstairs. */
6879 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
6881 service_name_locale
= _isds_utf82locale((char*)service_name
);
6882 message_id_locale
= _isds_utf82locale(message_id
);
6883 if (!service_name_locale
|| !message_id_locale
) {
6889 request
= xmlNewNode(NULL
, service_name
);
6891 isds_printf_message(context
,
6892 _("Could not build %s request"), service_name_locale
);
6896 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
6898 isds_log_message(context
, _("Could not create ISDS name space"));
6902 xmlSetNs(request
, isds_ns
);
6905 /* Add requested ID */
6906 err
= validate_message_id_length(context
, (xmlChar
*) message_id
);
6907 if (err
) goto leave
;
6908 INSERT_STRING(request
, "dmID", message_id
);
6911 isds_log(ILF_ISDS
, ILL_DEBUG
,
6912 _("Sending %s request for %s message ID to ISDS\n"),
6913 service_name_locale
, message_id_locale
);
6916 err
= isds(context
, service
, request
, response
,
6917 raw_response
, raw_response_length
);
6918 xmlFreeNode(request
); request
= NULL
;
6921 isds_log(ILF_ISDS
, ILL_DEBUG
,
6922 _("Processing ISDS response on %s request failed\n"),
6923 service_name_locale
);
6927 /* Check for response status */
6928 err
= isds_response_status(context
, service
, *response
,
6929 code
, status_message
, NULL
);
6931 isds_log(ILF_ISDS
, ILL_DEBUG
,
6932 _("ISDS response on %s request is missing status\n"),
6933 service_name_locale
);
6937 /* Request processed, but nothing found */
6938 if (xmlStrcmp(*code
, BAD_CAST
"0000")) {
6939 char *code_locale
= _isds_utf82locale((char*) *code
);
6940 char *status_message_locale
= _isds_utf82locale((char*) *status_message
);
6941 isds_log(ILF_ISDS
, ILL_DEBUG
,
6942 _("Server refused %s request for %s message ID "
6943 "(code=%s, message=%s)\n"),
6944 service_name_locale
, message_id_locale
,
6945 code_locale
, status_message_locale
);
6946 isds_log_message(context
, status_message_locale
);
6948 free(status_message_locale
);
6954 free(message_id_locale
);
6955 free(service_name_locale
);
6956 xmlFreeNode(request
);
6961 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
6962 * signed data and free ISDS response.
6963 * @context is session context
6964 * @message_id is UTF-8 encoded message ID for logging purpose
6965 * @response is parsed XML document. It will be freed and NULLed in the middle
6966 * of function run to save memory. This is not guaranteed in case of error.
6967 * @request_name is name of ISDS request used to construct response root
6968 * element name and for logging purpose.
6969 * @raw is reallocated output buffer with DER encoded CMS data
6970 * @raw_length is size of @raw buffer in bytes
6971 * @returns standard error codes, in case of error, @raw will be freed and
6972 * NULLed, @response sometimes. */
6973 static isds_error
find_extract_signed_data_free_response(
6974 struct isds_ctx
*context
, const xmlChar
*message_id
,
6975 xmlDocPtr
*response
, const xmlChar
*request_name
,
6976 void **raw
, size_t *raw_length
) {
6978 isds_error err
= IE_SUCCESS
;
6979 char *xpath_expression
= NULL
;
6980 xmlXPathContextPtr xpath_ctx
= NULL
;
6981 xmlXPathObjectPtr result
= NULL
;
6982 char *encoded_structure
= NULL
;
6984 if (!context
) return IE_INVALID_CONTEXT
;
6985 if (!raw
) return IE_INVAL
;
6987 if (!message_id
|| !response
|| !*response
|| !request_name
|| !raw_length
)
6990 /* Build XPath expression */
6991 xpath_expression
= _isds_astrcat3("/isds:", (char *) request_name
,
6992 "Response/isds:dmSignature");
6993 if (!xpath_expression
) return IE_NOMEM
;
6996 xpath_ctx
= xmlXPathNewContext(*response
);
7001 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7005 result
= xmlXPathEvalExpression(BAD_CAST xpath_expression
, xpath_ctx
);
7010 /* Empty response */
7011 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7012 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
7013 isds_printf_message(context
,
7014 _("Server did not return any signed data for message ID `%s' "
7016 message_id_locale
, request_name
);
7017 free(message_id_locale
);
7021 /* More responses */
7022 if (result
->nodesetval
->nodeNr
> 1) {
7023 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
7024 isds_printf_message(context
,
7025 _("Server did return more signed data for message ID `%s' "
7027 message_id_locale
, request_name
);
7028 free(message_id_locale
);
7033 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
7035 /* Extract PKCS#7 structure */
7036 EXTRACT_STRING(".", encoded_structure
);
7037 if (!encoded_structure
) {
7038 isds_log_message(context
, _("dmSignature element is empty"));
7041 /* Here we have delivery info as standalone CMS in encoded_structure.
7042 * We don't need any other data, free them: */
7043 xmlXPathFreeObject(result
); result
= NULL
;
7044 xmlXPathFreeContext(xpath_ctx
); xpath_ctx
= NULL
;
7045 xmlFreeDoc(*response
); *response
= NULL
;
7048 /* Decode PKCS#7 to DER format */
7049 *raw_length
= _isds_b64decode(encoded_structure
, raw
);
7050 if (*raw_length
== (size_t) -1) {
7051 isds_log_message(context
,
7052 _("Error while Base64-decoding PKCS#7 structure"));
7063 free(encoded_structure
);
7064 xmlXPathFreeObject(result
);
7065 xmlXPathFreeContext(xpath_ctx
);
7066 free(xpath_expression
);
7072 /* Download incoming message envelope identified by ID.
7073 * @context is session context
7074 * @message_id is message identifier (you can get them from
7075 * isds_get_list_of_received_messages())
7076 * @message is automatically reallocated message retrieved from ISDS.
7077 * It will miss documents per se. Use isds_get_received_message(), if you are
7078 * interested in documents (content) too.
7079 * Returned hash and timestamp require documents to be verifiable. */
7080 isds_error
isds_get_received_envelope(struct isds_ctx
*context
,
7081 const char *message_id
, struct isds_message
**message
) {
7083 isds_error err
= IE_SUCCESS
;
7084 xmlDocPtr response
= NULL
;
7085 xmlChar
*code
= NULL
, *status_message
= NULL
;
7086 xmlXPathContextPtr xpath_ctx
= NULL
;
7087 xmlXPathObjectPtr result
= NULL
;
7089 if (!context
) return IE_INVALID_CONTEXT
;
7090 zfree(context
->long_message
);
7092 /* Free former message if any */
7093 if (!message
) return IE_INVAL
;
7094 isds_message_free(message
);
7096 /* Do request and check for success */
7097 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
7098 BAD_CAST
"MessageEnvelopeDownload", message_id
,
7099 &response
, NULL
, NULL
, &code
, &status_message
);
7100 if (err
) goto leave
;
7103 xpath_ctx
= xmlXPathNewContext(response
);
7108 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7112 result
= xmlXPathEvalExpression(
7113 BAD_CAST
"/isds:MessageEnvelopeDownloadResponse/"
7114 "isds:dmReturnedMessageEnvelope",
7120 /* Empty response */
7121 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7122 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
7123 isds_printf_message(context
,
7124 _("Server did not return any envelope for ID `%s' "
7125 "on MessageEnvelopeDownload request"), message_id_locale
);
7126 free(message_id_locale
);
7131 if (result
->nodesetval
->nodeNr
> 1) {
7132 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
7133 isds_printf_message(context
,
7134 _("Server did return more envelopes for ID `%s' "
7135 "on MessageEnvelopeDownload request"), message_id_locale
);
7136 free(message_id_locale
);
7141 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
7143 /* Extract the envelope (= message without documents, hence 0) */
7144 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
7145 if (err
) goto leave
;
7148 err
= serialize_subtree(context
, xpath_ctx
->node
, &(*message
)->raw
,
7149 &(*message
)->raw_length
);
7153 isds_message_free(message
);
7156 xmlXPathFreeObject(result
);
7157 xmlXPathFreeContext(xpath_ctx
);
7160 free(status_message
);
7161 xmlFreeDoc(response
);
7164 isds_log(ILF_ISDS
, ILL_DEBUG
,
7165 _("MessageEnvelopeDownload request processed by server "
7172 /* Load delivery info of any format from buffer.
7173 * @context is session context
7174 * @raw_type advertises format of @buffer content. Only delivery info types
7176 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
7177 * retrieve such data from message->raw after calling
7178 * isds_get_signed_delivery_info().
7179 * @length is length of buffer in bytes.
7180 * @message is automatically reallocated message parsed from @buffer.
7181 * @strategy selects how buffer will be attached into raw isds_message member.
7183 isds_error
isds_load_delivery_info(struct isds_ctx
*context
,
7184 const isds_raw_type raw_type
,
7185 const void *buffer
, const size_t length
,
7186 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
7188 isds_error err
= IE_SUCCESS
;
7189 message_ns_type message_ns
;
7190 xmlDocPtr message_doc
= NULL
;
7191 xmlXPathContextPtr xpath_ctx
= NULL
;
7192 xmlXPathObjectPtr result
= NULL
;
7193 void *xml_stream
= NULL
;
7194 size_t xml_stream_length
= 0;
7196 if (!context
) return IE_INVALID_CONTEXT
;
7197 zfree(context
->long_message
);
7198 if (!message
) return IE_INVAL
;
7199 isds_message_free(message
);
7200 if (!buffer
) return IE_INVAL
;
7203 /* Select buffer format and extract XML from CMS*/
7205 case RAWTYPE_DELIVERYINFO
:
7206 message_ns
= MESSAGE_NS_UNSIGNED
;
7207 xml_stream
= (void *) buffer
;
7208 xml_stream_length
= length
;
7211 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
:
7212 message_ns
= MESSAGE_NS_SIGNED_DELIVERY
;
7213 xml_stream
= (void *) buffer
;
7214 xml_stream_length
= length
;
7217 case RAWTYPE_CMS_SIGNED_DELIVERYINFO
:
7218 message_ns
= MESSAGE_NS_SIGNED_DELIVERY
;
7219 err
= _isds_extract_cms_data(context
, buffer
, length
,
7220 &xml_stream
, &xml_stream_length
);
7221 if (err
) goto leave
;
7225 isds_log_message(context
, _("Bad raw delivery representation type"));
7230 isds_log(ILF_ISDS
, ILL_DEBUG
,
7231 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
7232 xml_stream_length
, xml_stream
);
7234 /* Convert delivery info XML stream into XPath context */
7235 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
7240 xpath_ctx
= xmlXPathNewContext(message_doc
);
7245 /* XXX: Name spaces mangled for signed delivery info:
7246 * http://isds.czechpoint.cz/v20/delivery:
7248 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
7250 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
7251 * <p:dmID>170272</p:dmID>
7254 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
7256 * </q:dmEvents>...</q:dmEvents>
7258 * </q:GetDeliveryInfoResponse>
7260 if (_isds_register_namespaces(xpath_ctx
, message_ns
)) {
7264 result
= xmlXPathEvalExpression(
7265 BAD_CAST
"/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
7271 /* Empty delivery info */
7272 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7273 isds_printf_message(context
,
7274 _("XML document is not sisds:dmDelivery document"));
7278 /* More delivery info's */
7279 if (result
->nodesetval
->nodeNr
> 1) {
7280 isds_printf_message(context
,
7281 _("XML document has more sisds:dmDelivery elements"));
7285 /* One delivery info */
7286 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
7288 /* Extract the envelope (= message without documents, hence 0).
7289 * XXX: extract_TReturnedMessage() can obtain attachments size,
7290 * but delivery info carries none. It's coded as option elements,
7291 * so it should work. */
7292 err
= extract_TReturnedMessage(context
, 0, message
, xpath_ctx
);
7293 if (err
) goto leave
;
7295 /* Extract events */
7296 err
= move_xpathctx_to_child(context
, BAD_CAST
"sisds:dmEvents", xpath_ctx
);
7297 if (err
== IE_NOEXIST
|| err
== IE_NOTUNIQ
) { err
= IE_ISDS
; goto leave
; }
7298 if (err
) { err
= IE_ERROR
; goto leave
; }
7299 err
= extract_events(context
, &(*message
)->envelope
->events
, xpath_ctx
);
7300 if (err
) goto leave
;
7302 /* Append raw CMS structure into message */
7303 (*message
)->raw_type
= raw_type
;
7305 case BUFFER_DONT_STORE
:
7308 (*message
)->raw
= malloc(length
);
7309 if (!(*message
)->raw
) {
7313 memcpy((*message
)->raw
, buffer
, length
);
7314 (*message
)->raw_length
= length
;
7317 (*message
)->raw
= (void *) buffer
;
7318 (*message
)->raw_length
= length
;
7327 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
7328 isds_message_free(message
);
7331 xmlXPathFreeObject(result
);
7332 xmlXPathFreeContext(xpath_ctx
);
7333 xmlFreeDoc(message_doc
);
7334 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
7337 isds_log(ILF_ISDS
, ILL_DEBUG
,
7338 _("Delivery info loaded successfully.\n"));
7343 /* Download signed delivery info-sheet of given message identified by ID.
7344 * @context is session context
7345 * @message_id is message identifier (you can get them from
7346 * isds_get_list_of_{sent,received}_messages())
7347 * @message is automatically reallocated message retrieved from ISDS.
7348 * It will miss documents per se. Use isds_get_signed_received_message(),
7349 * if you are interested in documents (content). OTOH, only this function
7350 * can get list events message has gone through. */
7351 isds_error
isds_get_signed_delivery_info(struct isds_ctx
*context
,
7352 const char *message_id
, struct isds_message
**message
) {
7354 isds_error err
= IE_SUCCESS
;
7355 xmlDocPtr response
= NULL
;
7356 xmlChar
*code
= NULL
, *status_message
= NULL
;
7358 size_t raw_length
= 0;
7360 if (!context
) return IE_INVALID_CONTEXT
;
7361 zfree(context
->long_message
);
7363 /* Free former message if any */
7364 if (!message
) return IE_INVAL
;
7365 isds_message_free(message
);
7367 /* Do request and check for success */
7368 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
7369 BAD_CAST
"GetSignedDeliveryInfo", message_id
,
7370 &response
, NULL
, NULL
, &code
, &status_message
);
7371 if (err
) goto leave
;
7373 /* Find signed delivery info, extract it into raw and maybe free
7375 err
= find_extract_signed_data_free_response(context
,
7376 (xmlChar
*)message_id
, &response
,
7377 BAD_CAST
"GetSignedDeliveryInfo", &raw
, &raw_length
);
7378 if (err
) goto leave
;
7380 /* Parse delivery info */
7381 err
= isds_load_delivery_info(context
,
7382 RAWTYPE_CMS_SIGNED_DELIVERYINFO
, raw
, raw_length
,
7383 message
, BUFFER_MOVE
);
7384 if (err
) goto leave
;
7390 isds_message_free(message
);
7395 free(status_message
);
7396 xmlFreeDoc(response
);
7399 isds_log(ILF_ISDS
, ILL_DEBUG
,
7400 _("GetSignedDeliveryInfo request processed by server "
7407 /* Download delivery info-sheet of given message identified by ID.
7408 * @context is session context
7409 * @message_id is message identifier (you can get them from
7410 * isds_get_list_of_{sent,received}_messages())
7411 * @message is automatically reallocated message retrieved from ISDS.
7412 * It will miss documents per se. Use isds_get_received_message(), if you are
7413 * interested in documents (content). OTOH, only this function can get list
7414 * of events message has gone through. */
7415 isds_error
isds_get_delivery_info(struct isds_ctx
*context
,
7416 const char *message_id
, struct isds_message
**message
) {
7418 isds_error err
= IE_SUCCESS
;
7419 xmlDocPtr response
= NULL
;
7420 xmlChar
*code
= NULL
, *status_message
= NULL
;
7421 xmlNodePtr delivery_node
= NULL
;
7423 size_t raw_length
= 0;
7425 if (!context
) return IE_INVALID_CONTEXT
;
7426 zfree(context
->long_message
);
7428 /* Free former message if any */
7429 if (!message
) return IE_INVAL
;
7430 isds_message_free(message
);
7432 /* Do request and check for success */
7433 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
7434 BAD_CAST
"GetDeliveryInfo", message_id
,
7435 &response
, NULL
, NULL
, &code
, &status_message
);
7436 if (err
) goto leave
;
7439 /* Serialize delivery info */
7440 delivery_node
= xmlDocGetRootElement(response
);
7441 if (!delivery_node
) {
7442 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
7443 isds_printf_message(context
,
7444 _("Server did not return any delivery info for ID `%s' "
7445 "on GetDeliveryInfo request"), message_id_locale
);
7446 free(message_id_locale
);
7450 err
= serialize_subtree(context
, delivery_node
, &raw
, &raw_length
);
7451 if (err
) goto leave
;
7453 /* Parse delivery info */
7454 /* TODO: Here we parse the response second time. We could single delivery
7455 * parser from isds_load_delivery_info() to make things faster. */
7456 err
= isds_load_delivery_info(context
,
7457 RAWTYPE_DELIVERYINFO
, raw
, raw_length
,
7458 message
, BUFFER_MOVE
);
7459 if (err
) goto leave
;
7466 isds_message_free(message
);
7471 free(status_message
);
7472 xmlFreeDoc(response
);
7475 isds_log(ILF_ISDS
, ILL_DEBUG
,
7476 _("GetDeliveryInfo request processed by server "
7483 /* Download incoming message identified by ID.
7484 * @context is session context
7485 * @message_id is message identifier (you can get them from
7486 * isds_get_list_of_received_messages())
7487 * @message is automatically reallocated message retrieved from ISDS */
7488 isds_error
isds_get_received_message(struct isds_ctx
*context
,
7489 const char *message_id
, struct isds_message
**message
) {
7491 isds_error err
= IE_SUCCESS
;
7492 xmlDocPtr response
= NULL
;
7493 void *xml_stream
= NULL
;
7494 size_t xml_stream_length
;
7495 xmlChar
*code
= NULL
, *status_message
= NULL
;
7496 xmlXPathContextPtr xpath_ctx
= NULL
;
7497 xmlXPathObjectPtr result
= NULL
;
7498 char *phys_path
= NULL
;
7499 size_t phys_start
, phys_end
;
7501 if (!context
) return IE_INVALID_CONTEXT
;
7502 zfree(context
->long_message
);
7504 /* Free former message if any */
7505 if (message
) isds_message_free(message
);
7507 /* Do request and check for success */
7508 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
7509 BAD_CAST
"MessageDownload", message_id
,
7510 &response
, &xml_stream
, &xml_stream_length
,
7511 &code
, &status_message
);
7512 if (err
) goto leave
;
7515 xpath_ctx
= xmlXPathNewContext(response
);
7520 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
7524 result
= xmlXPathEvalExpression(
7525 BAD_CAST
"/isds:MessageDownloadResponse/isds:dmReturnedMessage",
7531 /* Empty response */
7532 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7533 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
7534 isds_printf_message(context
,
7535 _("Server did not return any message for ID `%s' "
7536 "on MessageDownload request"), message_id_locale
);
7537 free(message_id_locale
);
7542 if (result
->nodesetval
->nodeNr
> 1) {
7543 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
7544 isds_printf_message(context
,
7545 _("Server did return more messages for ID `%s' "
7546 "on MessageDownload request"), message_id_locale
);
7547 free(message_id_locale
);
7552 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
7554 /* Extract the message */
7555 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
7556 if (err
) goto leave
;
7558 /* Locate raw XML blob */
7560 SOAP_NS PHYSXML_NS_SEPARATOR
"Envelope"
7561 PHYSXML_ELEMENT_SEPARATOR
7562 SOAP_NS PHYSXML_NS_SEPARATOR
"Body"
7563 PHYSXML_ELEMENT_SEPARATOR
7564 ISDS_NS PHYSXML_NS_SEPARATOR
"MessageDownloadResponse"
7570 err
= _isds_find_element_boundary(xml_stream
, xml_stream_length
,
7571 phys_path
, &phys_start
, &phys_end
);
7574 isds_log_message(context
,
7575 _("Substring with isds:MessageDownloadResponse element "
7576 "could not be located in raw SOAP message"));
7580 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
7581 &(*message)->raw_length);*/
7582 /* TODO: Store name space declarations from ancestors */
7583 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
7584 (*message
)->raw_type
= RAWTYPE_INCOMING_MESSAGE
;
7585 (*message
)->raw_length
= phys_end
- phys_start
+ 1;
7586 (*message
)->raw
= malloc((*message
)->raw_length
);
7587 if (!(*message
)->raw
) {
7591 memcpy((*message
)->raw
, xml_stream
+ phys_start
, (*message
)->raw_length
);
7596 isds_message_free(message
);
7601 xmlXPathFreeObject(result
);
7602 xmlXPathFreeContext(xpath_ctx
);
7605 free(status_message
);
7607 xmlFreeDoc(response
);
7610 isds_log(ILF_ISDS
, ILL_DEBUG
,
7611 _("MessageDownload request processed by server "
7618 /* Load message of any type from buffer.
7619 * @context is session context
7620 * @raw_type defines content type of @buffer. Only message types are allowed.
7621 * @buffer is message raw representation. Format (CMS, plain signed,
7622 * message direction) is defined in @raw_type. You can retrieve such data
7623 * from message->raw after calling isds_get_[signed]{received,sent}_message().
7624 * @length is length of buffer in bytes.
7625 * @message is automatically reallocated message parsed from @buffer.
7626 * @strategy selects how buffer will be attached into raw isds_message member.
7628 isds_error
isds_load_message(struct isds_ctx
*context
,
7629 const isds_raw_type raw_type
, const void *buffer
, const size_t length
,
7630 struct isds_message
**message
, const isds_buffer_strategy strategy
) {
7632 isds_error err
= IE_SUCCESS
;
7633 void *xml_stream
= NULL
;
7634 size_t xml_stream_length
= 0;
7635 message_ns_type message_ns
;
7636 xmlDocPtr message_doc
= NULL
;
7637 xmlXPathContextPtr xpath_ctx
= NULL
;
7638 xmlXPathObjectPtr result
= NULL
;
7640 if (!context
) return IE_INVALID_CONTEXT
;
7641 zfree(context
->long_message
);
7642 if (!message
) return IE_INVAL
;
7643 isds_message_free(message
);
7644 if (!buffer
) return IE_INVAL
;
7647 /* Select buffer format and extract XML from CMS*/
7649 case RAWTYPE_INCOMING_MESSAGE
:
7650 message_ns
= MESSAGE_NS_UNSIGNED
;
7651 xml_stream
= (void *) buffer
;
7652 xml_stream_length
= length
;
7655 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
7656 message_ns
= MESSAGE_NS_SIGNED_INCOMING
;
7657 xml_stream
= (void *) buffer
;
7658 xml_stream_length
= length
;
7661 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
7662 message_ns
= MESSAGE_NS_SIGNED_INCOMING
;
7663 err
= _isds_extract_cms_data(context
, buffer
, length
,
7664 &xml_stream
, &xml_stream_length
);
7665 if (err
) goto leave
;
7668 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
7669 message_ns
= MESSAGE_NS_SIGNED_OUTGOING
;
7670 xml_stream
= (void *) buffer
;
7671 xml_stream_length
= length
;
7674 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
7675 message_ns
= MESSAGE_NS_SIGNED_OUTGOING
;
7676 err
= _isds_extract_cms_data(context
, buffer
, length
,
7677 &xml_stream
, &xml_stream_length
);
7678 if (err
) goto leave
;
7682 isds_log_message(context
, _("Bad raw message representation type"));
7687 isds_log(ILF_ISDS
, ILL_DEBUG
,
7688 _("Loading message:\n%.*s\nEnd of message\n"),
7689 xml_stream_length
, xml_stream
);
7691 /* Convert messages XML stream into XPath context */
7692 message_doc
= xmlParseMemory(xml_stream
, xml_stream_length
);
7697 xpath_ctx
= xmlXPathNewContext(message_doc
);
7702 /* XXX: Standard name space for unsigned incoming direction:
7703 * http://isds.czechpoint.cz/v20/
7705 * XXX: Name spaces mangled for signed outgoing direction:
7706 * http://isds.czechpoint.cz/v20/SentMessage:
7708 * <q:MessageDownloadResponse
7709 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
7710 * <q:dmReturnedMessage>
7711 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
7712 * <p:dmID>151916</p:dmID>
7715 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
7717 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
7718 * </q:dmReturnedMessage>
7719 * </q:MessageDownloadResponse>
7721 * XXX: Name spaces mangled for signed incoming direction:
7722 * http://isds.czechpoint.cz/v20/message:
7724 * <q:MessageDownloadResponse
7725 * xmlns:q="http://isds.czechpoint.cz/v20/message">
7726 * <q:dmReturnedMessage>
7727 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
7728 * <p:dmID>151916</p:dmID>
7731 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
7733 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
7734 * </q:dmReturnedMessage>
7735 * </q:MessageDownloadResponse>
7737 * Stupidity of ISDS developers is unlimited */
7738 if (_isds_register_namespaces(xpath_ctx
, message_ns
)) {
7742 result
= xmlXPathEvalExpression(
7743 BAD_CAST
"/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
7750 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
7751 isds_printf_message(context
,
7752 _("XML document does not contain "
7753 "sisds:dmReturnedMessage element"));
7758 if (result
->nodesetval
->nodeNr
> 1) {
7759 isds_printf_message(context
,
7760 _("XML document has more sisds:dmReturnedMessage elements"));
7765 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
7767 /* Extract the message */
7768 err
= extract_TReturnedMessage(context
, 1, message
, xpath_ctx
);
7769 if (err
) goto leave
;
7771 /* Append raw buffer into message */
7772 (*message
)->raw_type
= raw_type
;
7774 case BUFFER_DONT_STORE
:
7777 (*message
)->raw
= malloc(length
);
7778 if (!(*message
)->raw
) {
7782 memcpy((*message
)->raw
, buffer
, length
);
7783 (*message
)->raw_length
= length
;
7786 (*message
)->raw
= (void *) buffer
;
7787 (*message
)->raw_length
= length
;
7797 if (*message
&& strategy
== BUFFER_MOVE
) (*message
)->raw
= NULL
;
7798 isds_message_free(message
);
7801 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
7802 xmlXPathFreeObject(result
);
7803 xmlXPathFreeContext(xpath_ctx
);
7804 xmlFreeDoc(message_doc
);
7807 isds_log(ILF_ISDS
, ILL_DEBUG
, _("Message loaded successfully.\n"));
7812 /* Determine type of raw message or delivery info according some heuristics.
7813 * It does not validate the raw blob.
7814 * @context is session context
7815 * @raw_type returns content type of @buffer. Valid only if exit code of this
7816 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
7817 * reallocated memory.
7818 * @buffer is message raw representation.
7819 * @length is length of buffer in bytes. */
7820 isds_error
isds_guess_raw_type(struct isds_ctx
*context
,
7821 isds_raw_type
*raw_type
, const void *buffer
, const size_t length
) {
7823 void *xml_stream
= NULL
;
7824 size_t xml_stream_length
= 0;
7825 xmlDocPtr document
= NULL
;
7826 xmlNodePtr root
= NULL
;
7828 if (!context
) return IE_INVALID_CONTEXT
;
7829 zfree(context
->long_message
);
7830 if (length
== 0 || !buffer
) return IE_INVAL
;
7831 if (!raw_type
) return IE_INVAL
;
7834 err
= _isds_extract_cms_data(context
, buffer
, length
,
7835 &xml_stream
, &xml_stream_length
);
7837 xml_stream
= (void *) buffer
;
7838 xml_stream_length
= (size_t) length
;
7843 document
= xmlParseMemory(xml_stream
, xml_stream_length
);
7845 isds_printf_message(context
,
7846 _("Could not parse data as XML document"));
7851 /* Get root element */
7852 root
= xmlDocGetRootElement(document
);
7854 isds_printf_message(context
,
7855 _("XML document is missing root element"));
7860 if (!root
->ns
|| !root
->ns
->href
) {
7861 isds_printf_message(context
,
7862 _("Root element does not belong to any name space"));
7867 /* Test name space */
7868 if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_INCOMING_NS
)) {
7869 if (xml_stream
== buffer
)
7870 *raw_type
= RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
;
7872 *raw_type
= RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
;
7873 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_OUTGOING_NS
)) {
7874 if (xml_stream
== buffer
)
7875 *raw_type
= RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
;
7877 *raw_type
= RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
;
7878 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST SISDS_DELIVERY_NS
)) {
7879 if (xml_stream
== buffer
)
7880 *raw_type
= RAWTYPE_PLAIN_SIGNED_DELIVERYINFO
;
7882 *raw_type
= RAWTYPE_CMS_SIGNED_DELIVERYINFO
;
7883 } else if (!xmlStrcmp(root
->ns
->href
, BAD_CAST ISDS_NS
)) {
7884 if (xml_stream
!= buffer
) {
7885 isds_printf_message(context
,
7886 _("Document in ISDS name space is encapsulated into CMS" ));
7888 } else if (!xmlStrcmp(root
->name
, BAD_CAST
"MessageDownloadResponse"))
7889 *raw_type
= RAWTYPE_INCOMING_MESSAGE
;
7890 else if (!xmlStrcmp(root
->name
, BAD_CAST
"GetDeliveryInfoResponse"))
7891 *raw_type
= RAWTYPE_DELIVERYINFO
;
7893 isds_printf_message(context
,
7894 _("Unknown root element in ISDS name space"));
7898 isds_printf_message(context
,
7899 _("Unknown namespace"));
7904 if (xml_stream
!= buffer
) _isds_cms_data_free(xml_stream
);
7905 xmlFreeDoc(document
);
7910 /* Download signed incoming/outgoing message identified by ID.
7911 * @context is session context
7912 * @output is true for outgoing message, false for incoming message
7913 * @message_id is message identifier (you can get them from
7914 * isds_get_list_of_{sent,received}_messages())
7915 * @message is automatically reallocated message retrieved from ISDS. The raw
7916 * member will be filled with PKCS#7 structure in DER format. */
7917 static isds_error
isds_get_signed_message(struct isds_ctx
*context
,
7918 const _Bool outgoing
, const char *message_id
,
7919 struct isds_message
**message
) {
7921 isds_error err
= IE_SUCCESS
;
7922 xmlDocPtr response
= NULL
;
7923 xmlChar
*code
= NULL
, *status_message
= NULL
;
7924 xmlXPathContextPtr xpath_ctx
= NULL
;
7925 xmlXPathObjectPtr result
= NULL
;
7926 char *encoded_structure
= NULL
;
7928 size_t raw_length
= 0;
7930 if (!context
) return IE_INVALID_CONTEXT
;
7931 zfree(context
->long_message
);
7932 if (!message
) return IE_INVAL
;
7933 isds_message_free(message
);
7935 /* Do request and check for success */
7936 err
= build_send_check_message_request(context
, SERVICE_DM_OPERATIONS
,
7937 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
7938 BAD_CAST
"SignedMessageDownload",
7939 message_id
, &response
, NULL
, NULL
, &code
, &status_message
);
7940 if (err
) goto leave
;
7942 /* Find signed message, extract it into raw and maybe free
7944 err
= find_extract_signed_data_free_response(context
,
7945 (xmlChar
*)message_id
, &response
,
7946 (outgoing
) ? BAD_CAST
"SignedSentMessageDownload" :
7947 BAD_CAST
"SignedMessageDownload",
7949 if (err
) goto leave
;
7952 err
= isds_load_message(context
,
7953 (outgoing
) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
7954 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
,
7955 raw
, raw_length
, message
, BUFFER_MOVE
);
7956 if (err
) goto leave
;
7962 isds_message_free(message
);
7965 free(encoded_structure
);
7966 xmlXPathFreeObject(result
);
7967 xmlXPathFreeContext(xpath_ctx
);
7971 free(status_message
);
7972 xmlFreeDoc(response
);
7975 isds_log(ILF_ISDS
, ILL_DEBUG
,
7977 _("SignedSentMessageDownload request processed by server "
7978 "successfully.\n") :
7979 _("SignedMessageDownload request processed by server "
7986 /* Download signed incoming message identified by ID.
7987 * @context is session context
7988 * @message_id is message identifier (you can get them from
7989 * isds_get_list_of_received_messages())
7990 * @message is automatically reallocated message retrieved from ISDS. The raw
7991 * member will be filled with PKCS#7 structure in DER format. */
7992 isds_error
isds_get_signed_received_message(struct isds_ctx
*context
,
7993 const char *message_id
, struct isds_message
**message
) {
7994 return isds_get_signed_message(context
, 0, message_id
, message
);
7998 /* Download signed outgoing message identified by ID.
7999 * @context is session context
8000 * @message_id is message identifier (you can get them from
8001 * isds_get_list_of_sent_messages())
8002 * @message is automatically reallocated message retrieved from ISDS. The raw
8003 * member will be filled with PKCS#7 structure in DER format. */
8004 isds_error
isds_get_signed_sent_message(struct isds_ctx
*context
,
8005 const char *message_id
, struct isds_message
**message
) {
8006 return isds_get_signed_message(context
, 1, message_id
, message
);
8010 /* Retrieve hash of message identified by ID stored in ISDS.
8011 * @context is session context
8012 * @message_id is message identifier
8013 * @hash is automatically reallocated message hash downloaded from ISDS.
8014 * Message must exist in system and must not be deleted. */
8015 isds_error
isds_download_message_hash(struct isds_ctx
*context
,
8016 const char *message_id
, struct isds_hash
**hash
) {
8018 isds_error err
= IE_SUCCESS
;
8019 xmlDocPtr response
= NULL
;
8020 xmlChar
*code
= NULL
, *status_message
= NULL
;
8021 xmlXPathContextPtr xpath_ctx
= NULL
;
8022 xmlXPathObjectPtr result
= NULL
;
8024 if (!context
) return IE_INVALID_CONTEXT
;
8025 zfree(context
->long_message
);
8027 isds_hash_free(hash
);
8029 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
8030 BAD_CAST
"VerifyMessage", message_id
,
8031 &response
, NULL
, NULL
, &code
, &status_message
);
8032 if (err
) goto leave
;
8036 xpath_ctx
= xmlXPathNewContext(response
);
8041 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8045 result
= xmlXPathEvalExpression(
8046 BAD_CAST
"/isds:VerifyMessageResponse",
8052 /* Empty response */
8053 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8054 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
8055 isds_printf_message(context
,
8056 _("Server did not return any response for ID `%s' "
8057 "on VerifyMessage request"), message_id_locale
);
8058 free(message_id_locale
);
8062 /* More responses */
8063 if (result
->nodesetval
->nodeNr
> 1) {
8064 char *message_id_locale
= _isds_utf82locale((char*) message_id
);
8065 isds_printf_message(context
,
8066 _("Server did return more responses for ID `%s' "
8067 "on VerifyMessage request"), message_id_locale
);
8068 free(message_id_locale
);
8073 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8075 /* Extract the hash */
8076 err
= find_and_extract_DmHash(context
, hash
, xpath_ctx
);
8080 isds_hash_free(hash
);
8083 xmlXPathFreeObject(result
);
8084 xmlXPathFreeContext(xpath_ctx
);
8087 free(status_message
);
8088 xmlFreeDoc(response
);
8091 isds_log(ILF_ISDS
, ILL_DEBUG
,
8092 _("VerifyMessage request processed by server "
8099 /* Mark message as read. This is a transactional commit function to acknowledge
8100 * to ISDS the message has been downloaded and processed by client properly.
8101 * @context is session context
8102 * @message_id is message identifier. */
8103 isds_error
isds_mark_message_read(struct isds_ctx
*context
,
8104 const char *message_id
) {
8106 isds_error err
= IE_SUCCESS
;
8107 xmlDocPtr response
= NULL
;
8108 xmlChar
*code
= NULL
, *status_message
= NULL
;
8110 if (!context
) return IE_INVALID_CONTEXT
;
8111 zfree(context
->long_message
);
8113 /* Do request and check for success */
8114 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
8115 BAD_CAST
"MarkMessageAsDownloaded", message_id
,
8116 &response
, NULL
, NULL
, &code
, &status_message
);
8119 free(status_message
);
8120 xmlFreeDoc(response
);
8123 isds_log(ILF_ISDS
, ILL_DEBUG
,
8124 _("MarkMessageAsDownloaded request processed by server "
8131 /* Mark message as received by recipient. This is applicable only to
8132 * commercial message. Use envelope->dmType message member to distinguish
8133 * commercial message from government message. Government message is
8134 * received automatically (by law), commercial message on recipient request.
8135 * @context is session context
8136 * @message_id is message identifier. */
8137 isds_error
isds_mark_message_received(struct isds_ctx
*context
,
8138 const char *message_id
) {
8140 isds_error err
= IE_SUCCESS
;
8141 xmlDocPtr response
= NULL
;
8142 xmlChar
*code
= NULL
, *status_message
= NULL
;
8144 if (!context
) return IE_INVALID_CONTEXT
;
8145 zfree(context
->long_message
);
8147 /* Do request and check for success */
8148 err
= build_send_check_message_request(context
, SERVICE_DM_INFO
,
8149 BAD_CAST
"ConfirmDelivery", message_id
,
8150 &response
, NULL
, NULL
, &code
, &status_message
);
8153 free(status_message
);
8154 xmlFreeDoc(response
);
8157 isds_log(ILF_ISDS
, ILL_DEBUG
,
8158 _("ConfirmDelivery request processed by server "
8165 /* Send document for authorize conversion into Czech POINT system.
8166 * This is public anonymous service, no log-in necessary. Special context is
8167 * used to reuse keep-a-live HTTPS connection.
8168 * @context is Czech POINT session context. DO NOT use context connected to
8169 * ISDS server. Use new context or context used by this function previously.
8170 * @document is document to convert. Only data, data_length and dmFileDescr
8171 * members are significant. Be ware that not all document formats can be
8172 * converted (signed PDF 1.3 and higher only (2010-02 state)).
8173 * @id is reallocated identifier assigned by Czech POINT system to
8174 * your document on submit. Use is to tell it to Czech POINT officer.
8175 * @date is reallocated document submit date (submitted documents
8176 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
8178 isds_error
czp_convert_document(struct isds_ctx
*context
,
8179 const struct isds_document
*document
,
8180 char **id
, struct tm
**date
) {
8181 isds_error err
= IE_SUCCESS
;
8182 xmlNsPtr deposit_ns
= NULL
, empty_ns
= NULL
;
8183 xmlNodePtr request
= NULL
, node
;
8184 xmlDocPtr response
= NULL
;
8185 xmlChar
*base64data
= NULL
;
8187 xmlXPathContextPtr xpath_ctx
= NULL
;
8188 xmlXPathObjectPtr result
= NULL
;
8189 long int status
= -1;
8190 long int *status_ptr
= &status
;
8191 char *string
= NULL
;
8194 if (!context
) return IE_INVALID_CONTEXT
;
8195 zfree(context
->long_message
);
8196 if (!document
|| !id
|| !date
) return IE_INVAL
;
8198 /* Free output arguments */
8202 /* Store configuration */
8203 context
->type
= CTX_TYPE_CZP
;
8205 context
->url
= strdup("https://www.czechpoint.cz/uschovna/services.php");
8206 if (!(context
->url
))
8209 /* Prepare CURL handle if not yet connected */
8210 if (!context
->curl
) {
8211 context
->curl
= curl_easy_init();
8212 if (!(context
->curl
))
8216 /* Build conversion request */
8217 request
= xmlNewNode(NULL
, BAD_CAST
"saveDocument");
8219 isds_log_message(context
,
8220 _("Could not build Czech POINT conversion request"));
8223 deposit_ns
= xmlNewNs(request
, BAD_CAST DEPOSIT_NS
, BAD_CAST
"dep");
8225 isds_log_message(context
,
8226 _("Could not create Czech POINT deposit name space"));
8227 xmlFreeNode(request
);
8230 xmlSetNs(request
, deposit_ns
);
8232 /* Insert children. They are in empty namespace! */
8233 empty_ns
= xmlNewNs(request
, BAD_CAST
"", NULL
);
8235 isds_log_message(context
, _("Could not create empty name space"));
8239 INSERT_STRING_WITH_NS(request
, empty_ns
, "conversionID", "0");
8240 INSERT_STRING_WITH_NS(request
, empty_ns
, "fileName",
8241 document
->dmFileDescr
);
8243 /* Document encoded in Base64 */
8244 base64data
= (xmlChar
*) _isds_b64encode(document
->data
, document
->data_length
);
8246 isds_printf_message(context
,
8247 ngettext("Not enough memory to encode %zd bytes into Base64",
8248 "Not enough memory to encode %zd bytes into Base64",
8249 document
->data_length
),
8250 document
->data_length
);
8254 INSERT_STRING_WITH_NS(request
, empty_ns
, "document", base64data
);
8257 isds_log(ILF_ISDS
, ILL_DEBUG
,
8258 _("Submitting document for conversion into Czech POINT deposit"));
8260 /* Send conversion request */
8261 err
= _czp_czpdeposit(context
, request
, &response
);
8262 xmlFreeNode(request
); request
= NULL
;
8265 czp_do_close_connection(context
);
8270 /* Extract response */
8271 xpath_ctx
= xmlXPathNewContext(response
);
8276 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8280 result
= xmlXPathEvalExpression(
8281 BAD_CAST
"/deposit:saveDocumentResponse/return",
8287 /* Empty response */
8288 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8289 isds_printf_message(context
,
8290 _("Missing `return' element in Czech POINT deposit response"));
8294 /* More responses */
8295 if (result
->nodesetval
->nodeNr
> 1) {
8296 isds_printf_message(context
,
8297 _("Multiple `return' element in Czech POINT deposit response"));
8302 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8305 EXTRACT_LONGINT("status", status_ptr
, 1);
8307 EXTRACT_STRING("statusMsg", string
);
8308 char *string_locale
= _isds_utf82locale(string
);
8309 isds_printf_message(context
,
8310 _("Czech POINT deposit refused document for conversion "
8311 "(code=%ld, message=%s)"),
8312 status
, string_locale
);
8313 free(string_locale
);
8318 /* Get document ID */
8319 EXTRACT_STRING("documentID", *id
);
8321 /* Get submit date */
8322 EXTRACT_STRING("dateInserted", string
);
8324 *date
= calloc(1, sizeof(**date
));
8329 err
= datestring2tm((xmlChar
*)string
, *date
);
8331 if (err
== IE_NOTSUP
) {
8333 char *string_locale
= _isds_utf82locale(string
);
8334 isds_printf_message(context
,
8335 _("Invalid dateInserted value: %s"), string_locale
);
8336 free(string_locale
);
8344 xmlXPathFreeObject(result
);
8345 xmlXPathFreeContext(xpath_ctx
);
8347 xmlFreeDoc(response
);
8349 xmlFreeNode(request
);
8352 char *id_locale
= _isds_utf82locale((char *) *id
);
8353 isds_log(ILF_ISDS
, ILL_DEBUG
,
8354 _("Document %s has been submitted for conversion "
8355 "to server successfully\n"), id_locale
);
8362 /* Close possibly opened connection to Czech POINT document deposit.
8363 * @context is Czech POINT session context. */
8364 isds_error
czp_close_connection(struct isds_ctx
*context
) {
8365 if (!context
) return IE_INVALID_CONTEXT
;
8366 zfree(context
->long_message
);
8367 return czp_do_close_connection(context
);
8371 /* Send request for new box creation in testing ISDS instance.
8372 * It's not possible to request for a production box currently, as it
8373 * communicates via e-mail.
8374 * XXX: This function does not work either. Server complains about invalid
8376 * XXX: Remove context->type hacks in isds.c and validator.c when removing
8378 * @context is special session context for box creation request. DO NOT use
8379 * standard context as it could reveal your password. Use fresh new context or
8380 * context previously used by this function.
8381 * @box is box description to create including single primary user (in case of
8382 * FO box type). It outputs box ID assigned by ISDS in dbID element.
8383 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
8384 * box, or contact address of PFO box owner). The email member is mandatory as
8385 * it will be used to deliver credentials.
8386 * @former_names is optional undocumented string. Pass NULL if you don't care.
8387 * @approval is optional external approval of box manipulation
8388 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8389 * NULL, if you don't care.*/
8390 isds_error
isds_request_new_testing_box(struct isds_ctx
*context
,
8391 struct isds_DbOwnerInfo
*box
, const struct isds_list
*users
,
8392 const char *former_names
, const struct isds_approval
*approval
,
8394 isds_error err
= IE_SUCCESS
;
8395 xmlNodePtr request
= NULL
;
8396 xmlDocPtr response
= NULL
;
8397 xmlXPathContextPtr xpath_ctx
= NULL
;
8398 xmlXPathObjectPtr result
= NULL
;
8401 if (!context
) return IE_INVALID_CONTEXT
;
8402 zfree(context
->long_message
);
8403 if (!box
) return IE_INVAL
;
8405 if (!box
->email
|| box
->email
[0] == '\0') {
8406 isds_log_message(context
, _("E-mail field is mandatory"));
8410 /* Scratch box ID */
8413 /* Store configuration */
8414 context
->type
= CTX_TYPE_TESTING_REQUEST_COLLECTOR
;
8416 context
->url
= strdup("http://78.102.19.203/testbox/request_box.php");
8417 if (!(context
->url
))
8420 /* Prepare CURL handle if not yet connected */
8421 if (!context
->curl
) {
8422 context
->curl
= curl_easy_init();
8423 if (!(context
->curl
))
8427 /* Build CreateDataBox request */
8428 err
= build_CreateDBInput_request(context
,
8429 &request
, BAD_CAST
"CreateDataBox",
8430 box
, users
, (xmlChar
*) former_names
, NULL
, NULL
, approval
);
8431 if (err
) goto leave
;
8433 /* Send it to server and process response */
8434 err
= send_destroy_request_check_response(context
,
8435 SERVICE_DB_MANIPULATION
, BAD_CAST
"CreateDataBox", &request
,
8436 &response
, (xmlChar
**) refnumber
);
8438 /* Extract box ID */
8439 xpath_ctx
= xmlXPathNewContext(response
);
8444 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8448 EXTRACT_STRING("/isds:CreateDataBoxResponse/dbID", box
->dbID
);
8451 xmlXPathFreeObject(result
);
8452 xmlXPathFreeContext(xpath_ctx
);
8453 xmlFreeDoc(response
);
8454 xmlFreeNode(request
);
8457 isds_log(ILF_ISDS
, ILL_DEBUG
,
8458 _("CreateDataBox request processed by server successfully.\n"));
8465 /* Submit CMS signed message to ISDS to verify its originality. This is
8466 * stronger form of isds_verify_message_hash() because ISDS does more checks
8467 * than simple one (potentialy old weak) hash comparison.
8468 * @context is session context
8469 * @message is memory with raw CMS signed message bit stream
8470 * @length is @message size in bytes
8472 * IE_SUCCESS if message originates in ISDS
8473 * IE_NOTEQUAL if message is unknown to ISDS
8474 * other code for other errors */
8475 isds_error
isds_authenticate_message(struct isds_ctx
*context
,
8476 const void *message
, size_t length
) {
8477 isds_error err
= IE_SUCCESS
;
8478 xmlNsPtr isds_ns
= NULL
;
8479 xmlNodePtr request
= NULL
;
8480 xmlDocPtr response
= NULL
;
8481 xmlChar
*base64data
= NULL
;
8482 xmlChar
*code
= NULL
, *status_message
= NULL
;
8483 xmlXPathContextPtr xpath_ctx
= NULL
;
8484 xmlXPathObjectPtr result
= NULL
;
8486 _Bool
*authentic
= NULL
;
8488 if (!context
) return IE_INVALID_CONTEXT
;
8489 zfree(context
->long_message
);
8490 if (!message
|| length
== 0) return IE_INVAL
;
8492 /* Check if connection is established
8493 * TODO: This check should be done downstairs. */
8494 if (!context
->curl
) return IE_CONNECTION_CLOSED
;
8497 /* Build AuthenticateMessage request */
8498 request
= xmlNewNode(NULL
, BAD_CAST
"AuthenticateMessage");
8500 isds_log_message(context
,
8501 _("Could not build AuthenticateMessage request"));
8504 isds_ns
= xmlNewNs(request
, BAD_CAST ISDS_NS
, NULL
);
8506 isds_log_message(context
, _("Could not create ISDS name space"));
8507 xmlFreeNode(request
);
8510 xmlSetNs(request
, isds_ns
);
8512 /* Insert Base64 encoded message */
8513 base64data
= (xmlChar
*) _isds_b64encode(message
, length
);
8515 isds_printf_message(context
,
8516 ngettext("Not enough memory to encode %zd bytes into Base64",
8517 "Not enough memory to encode %zd bytes into Base64",
8523 INSERT_STRING(request
, "dmMessage", base64data
);
8527 isds_log(ILF_ISDS
, ILL_DEBUG
,
8528 _("Sending AuthenticateMessage request to ISDS\n"));
8531 err
= isds(context
, SERVICE_DM_OPERATIONS
, request
, &response
, NULL
, NULL
);
8533 /* Destroy request */
8534 xmlFreeNode(request
); request
= NULL
;
8537 isds_log(ILF_ISDS
, ILL_DEBUG
,
8538 _("Processing ISDS response on AuthenticateMessage "
8539 "request failed\n"));
8543 /* Check for response status */
8544 err
= isds_response_status(context
, SERVICE_DM_OPERATIONS
, response
,
8545 &code
, &status_message
, NULL
);
8547 isds_log(ILF_ISDS
, ILL_DEBUG
,
8548 _("ISDS response on AuthenticateMessage request is missing "
8554 if (xmlStrcmp(code
, BAD_CAST
"0000")) {
8555 char *code_locale
= _isds_utf82locale((char*)code
);
8556 char *message_locale
= _isds_utf82locale((char*)status_message
);
8557 isds_log(ILF_ISDS
, ILL_DEBUG
,
8558 _("Server could not decide authenticity of submitted message "
8559 "(code=%s, message=%s)\n"),
8560 code_locale
, message_locale
);
8561 isds_log_message(context
, message_locale
);
8563 free(message_locale
);
8569 /* Otherwise ISDS has decided */
8570 xpath_ctx
= xmlXPathNewContext(response
);
8575 if (_isds_register_namespaces(xpath_ctx
, MESSAGE_NS_UNSIGNED
)) {
8579 result
= xmlXPathEvalExpression(
8580 BAD_CAST
"/isds:AuthenticateMessageResponse",
8586 /* Empty response */
8587 if (xmlXPathNodeSetIsEmpty(result
->nodesetval
)) {
8588 isds_log_message(context
,
8589 _("Server did not return any response on "
8590 "AuthenticateMessage request"));
8594 /* More responses */
8595 if (result
->nodesetval
->nodeNr
> 1) {
8596 isds_log_message(context
,
8597 _("Server did return more responses on "
8598 "AuthenticateMessage request"));
8603 xpath_ctx
->node
= result
->nodesetval
->nodeTab
[0];
8604 EXTRACT_BOOLEAN("dmAuthResult", authentic
);
8607 isds_log_message(context
,
8608 _("Server did not return any response on "
8609 "AuthenticateMessage request"));
8614 isds_log(ILF_ISDS
, ILL_DEBUG
,
8615 _("ISDS authenticated the message successfully\n"));
8618 isds_log_message(context
, _("ISDS does not know the message"));
8625 xmlXPathFreeObject(result
);
8626 xmlXPathFreeContext(xpath_ctx
);
8629 free(status_message
);
8631 xmlFreeDoc(response
);
8632 xmlFreeNode(request
);
8637 #undef INSERT_ELEMENT
8638 #undef CHECK_FOR_STRING_LENGTH
8639 #undef INSERT_STRING_ATTRIBUTE
8640 #undef INSERT_ULONGINTNOPTR
8641 #undef INSERT_ULONGINT
8642 #undef INSERT_LONGINT
8643 #undef INSERT_BOOLEAN
8644 #undef INSERT_SCALAR_BOOLEAN
8645 #undef INSERT_STRING
8646 #undef INSERT_STRING_WITH_NS
8647 #undef EXTRACT_STRING_ATTRIBUTE
8648 #undef EXTRACT_ULONGINT
8649 #undef EXTRACT_LONGINT
8650 #undef EXTRACT_BOOLEAN
8651 #undef EXTRACT_STRING
8654 /* Compute hash of message from raw representation and store it into envelope.
8655 * Original hash structure will be destroyed in envelope.
8656 * @context is session context
8657 * @message is message carrying raw XML message blob
8658 * @algorithm is desired hash algorithm to use */
8659 isds_error
isds_compute_message_hash(struct isds_ctx
*context
,
8660 struct isds_message
*message
, const isds_hash_algorithm algorithm
) {
8661 isds_error err
= IE_SUCCESS
;
8663 void *xml_stream
= NULL
;
8664 size_t xml_stream_length
;
8665 size_t phys_start
, phys_end
;
8666 char *phys_path
= NULL
;
8667 struct isds_hash
*new_hash
= NULL
;
8670 if (!context
) return IE_INVALID_CONTEXT
;
8671 zfree(context
->long_message
);
8672 if (!message
) return IE_INVAL
;
8674 if (!message
->raw
) {
8675 isds_log_message(context
,
8676 _("Message does not carry raw representation"));
8680 switch (message
->raw_type
) {
8681 case RAWTYPE_INCOMING_MESSAGE
:
8683 xml_stream
= message
->raw
;
8684 xml_stream_length
= message
->raw_length
;
8687 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE
:
8688 nsuri
= SISDS_INCOMING_NS
;
8689 xml_stream
= message
->raw
;
8690 xml_stream_length
= message
->raw_length
;
8693 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE
:
8694 nsuri
= SISDS_INCOMING_NS
;
8695 err
= _isds_extract_cms_data(context
,
8696 message
->raw
, message
->raw_length
,
8697 &xml_stream
, &xml_stream_length
);
8698 if (err
) goto leave
;
8701 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE
:
8702 nsuri
= SISDS_OUTGOING_NS
;
8703 xml_stream
= message
->raw
;
8704 xml_stream_length
= message
->raw_length
;
8707 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE
:
8708 nsuri
= SISDS_OUTGOING_NS
;
8709 err
= _isds_extract_cms_data(context
,
8710 message
->raw
, message
->raw_length
,
8711 &xml_stream
, &xml_stream_length
);
8712 if (err
) goto leave
;
8716 isds_log_message(context
, _("Bad raw representation type"));
8722 /* XXX: Hash is computed from original string representing isds:dmDm
8723 * subtree. That means no encoding, white space, xmlns attributes changes.
8724 * In other words, input for hash can be invalid XML stream. */
8725 if (-1 == isds_asprintf(&phys_path
, "%s%s%s%s",
8726 nsuri
, PHYSXML_NS_SEPARATOR
"MessageDownloadResponse"
8727 PHYSXML_ELEMENT_SEPARATOR
,
8728 nsuri
, PHYSXML_NS_SEPARATOR
"dmReturnedMessage"
8729 PHYSXML_ELEMENT_SEPARATOR
8730 ISDS_NS PHYSXML_NS_SEPARATOR
"dmDm")) {
8734 err
= _isds_find_element_boundary(xml_stream
, xml_stream_length
,
8735 phys_path
, &phys_start
, &phys_end
);
8738 isds_log_message(context
,
8739 _("Substring with isds:dmDM element could not be located "
8746 new_hash
= calloc(1, sizeof(*new_hash
));
8751 new_hash
->algorithm
= algorithm
;
8752 err
= _isds_compute_hash(xml_stream
+ phys_start
, phys_end
- phys_start
+ 1,
8755 isds_log_message(context
, _("Could not compute message hash"));
8759 /* Save computed hash */
8760 if (!message
->envelope
) {
8761 message
->envelope
= calloc(1, sizeof(*message
->envelope
));
8762 if (!message
->envelope
) {
8767 isds_hash_free(&message
->envelope
->hash
);
8768 message
->envelope
->hash
= new_hash
;
8772 isds_hash_free(&new_hash
);
8776 if (xml_stream
!= message
->raw
) free(xml_stream
);
8781 /* Compare two hashes.
8783 * @h2 is another hash
8785 * IE_SUCCESS if hashes equal
8786 * IE_NOTUNIQ if hashes are comparable, but they don't equal
8787 * IE_ENUM if not comparable, but both structures defined
8788 * IE_INVAL if some of the structures are undefined (NULL)
8789 * IE_ERROR if internal error occurs */
8790 isds_error
isds_hash_cmp(const struct isds_hash
*h1
, const struct isds_hash
*h2
) {
8791 if (h1
== NULL
|| h2
== NULL
) return IE_INVAL
;
8792 if (h1
->algorithm
!= h2
->algorithm
) return IE_ENUM
;
8793 if (h1
->length
!= h2
->length
) return IE_ERROR
;
8794 if (h1
->length
> 0 && !h1
->value
) return IE_ERROR
;
8795 if (h2
->length
> 0 && !h2
->value
) return IE_ERROR
;
8797 for (int i
= 0; i
< h1
->length
; i
++) {
8798 if (((uint8_t *) (h1
->value
))[i
] != ((uint8_t *) (h2
->value
))[i
])
8805 /* Check message has gone through ISDS by comparing message hash stored in
8806 * ISDS and locally computed hash. You must provide message with valid raw
8807 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
8808 * This is convenient wrapper for isds_download_message_hash(),
8809 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
8810 * @context is session context
8811 * @message is message with valid raw and envelope member; envelope->hash
8812 * member will be changed during function run. Use envelope on heap only.
8814 * IE_SUCCESS if message originates in ISDS
8815 * IE_NOTEQUAL if message is unknown to ISDS
8816 * other code for other errors */
8817 isds_error
isds_verify_message_hash(struct isds_ctx
*context
,
8818 struct isds_message
*message
) {
8819 isds_error err
= IE_SUCCESS
;
8820 struct isds_hash
*downloaded_hash
= NULL
;
8822 if (!context
) return IE_INVALID_CONTEXT
;
8823 zfree(context
->long_message
);
8824 if (!message
) return IE_INVAL
;
8826 if (!message
->envelope
) {
8827 isds_log_message(context
,
8828 _("Given message structure is missing envelope"));
8831 if (!message
->raw
) {
8832 isds_log_message(context
,
8833 _("Given message structure is missing raw representation"));
8837 err
= isds_download_message_hash(context
, message
->envelope
->dmID
,
8839 if (err
) goto leave
;
8841 err
= isds_compute_message_hash(context
, message
,
8842 downloaded_hash
->algorithm
);
8843 if (err
) goto leave
;
8845 err
= isds_hash_cmp(downloaded_hash
, message
->envelope
->hash
);
8848 isds_hash_free(&downloaded_hash
);
8853 /* Search for document by document ID in list of documents. IDs are compared
8855 * @documents is list of isds_documents
8856 * @id is document identifier
8857 * @return first matching document or NULL. */
8858 const struct isds_document
*isds_find_document_by_id(
8859 const struct isds_list
*documents
, const char *id
) {
8860 const struct isds_list
*item
;
8861 const struct isds_document
*document
;
8863 for (item
= documents
; item
; item
= item
->next
) {
8864 document
= (struct isds_document
*) item
->data
;
8865 if (!document
) continue;
8867 if (!xmlStrcmp((xmlChar
*) id
, (xmlChar
*) document
->dmFileGuid
))
8875 /* Normalize @mime_type to be proper MIME type.
8876 * ISDS servers passes invalid MIME types (e.g. "pdf"). This function tries to
8877 * guess regular MIME type (e.g. "application/pdf").
8878 * @mime_type is UTF-8 encoded MIME type to fix
8879 * @return original @mime_type if no better interpretation exists, or array to
8880 * constant static UTF-8 encoded string with proper MIME type. */
8881 char *isds_normalize_mime_type(const char* mime_type
) {
8882 if (!mime_type
) return NULL
;
8884 for (int offset
= 0;
8885 offset
< sizeof(extension_map_mime
)/sizeof(extension_map_mime
[0]);
8887 if (!xmlStrcmp((const xmlChar
*) mime_type
, extension_map_mime
[offset
]))
8888 return (char *) extension_map_mime
[offset
+ 1];
8891 return (char *) mime_type
;
8895 /* Switch MIME type normalization while message loading. Default state for new
8896 * context is no normalization.
8897 * @normalize use true to switch normalization on, false to switch off */
8898 isds_error
isds_set_mime_type_normalization(struct isds_ctx
*context
,
8900 if (!context
) return IE_INVALID_CONTEXT
;
8901 zfree(context
->long_message
);
8903 context
->normalize_mime_type
= normalize
;
8904 isds_log(ILF_FILE
, ILL_INFO
, (context
->normalize_mime_type
) ?
8905 _("MIME type normalization switched on\n") :
8906 _("MIME type normalization switched off\n"));
8911 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
8912 struct isds_message **message);
8913 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
8914 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
8915 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
8916 struct isds_address **address);
8918 int isds_message_free(struct isds_message **message);
8919 int isds_address_free(struct isds_address **address);
8923 /* Makes known all relevant namespaces to given XPath context
8924 * @xpath_ctx is XPath context
8925 * @message_ns selects proper message name space. Unsigned and signed
8926 * messages and delivery info's differ in prefix and URI. */
8927 _hidden isds_error
_isds_register_namespaces(xmlXPathContextPtr xpath_ctx
,
8928 const message_ns_type message_ns
) {
8929 const xmlChar
*message_namespace
= NULL
;
8931 if (!xpath_ctx
) return IE_ERROR
;
8933 switch(message_ns
) {
8935 message_namespace
= BAD_CAST ISDS1_NS
; break;
8936 case MESSAGE_NS_UNSIGNED
:
8937 message_namespace
= BAD_CAST ISDS_NS
; break;
8938 case MESSAGE_NS_SIGNED_INCOMING
:
8939 message_namespace
= BAD_CAST SISDS_INCOMING_NS
; break;
8940 case MESSAGE_NS_SIGNED_OUTGOING
:
8941 message_namespace
= BAD_CAST SISDS_OUTGOING_NS
; break;
8942 case MESSAGE_NS_SIGNED_DELIVERY
:
8943 message_namespace
= BAD_CAST SISDS_DELIVERY_NS
; break;
8948 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"soap", BAD_CAST SOAP_NS
))
8950 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"isds", BAD_CAST ISDS_NS
))
8952 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"sisds", message_namespace
))
8954 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"xs", BAD_CAST SCHEMA_NS
))
8956 if (xmlXPathRegisterNs(xpath_ctx
, BAD_CAST
"deposit", BAD_CAST DEPOSIT_NS
))