Refuse to convert XML documents
[libisds.git] / src / isds.c
blob8abb0caef495d18730749f3dc361c6ea087d1ae1
1 #define _XOPEN_SOURCE 500 /* strdup from string.h, strptime from time.h */
2 #include <stdlib.h>
3 #include <string.h>
4 #include <stdio.h>
5 #include <stdarg.h>
6 #include <ctype.h>
7 #include <stdint.h>
8 #include "isds_priv.h"
9 #include "utils.h"
10 #include "soap.h"
11 #include "validator.h"
12 #include "crypto.h"
13 #include <gpg-error.h> /* Because of ksba or gpgme */
14 #include "physxml.h"
16 /* Locators */
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 BAD_CAST "isdoc", BAD_CAST "text/isdoc",
53 BAD_CAST "isdocx", BAD_CAST "text/isdocx",
54 BAD_CAST "cer", BAD_CAST "application/x-x509-ca-cert",
55 BAD_CAST "crt", BAD_CAST "application/x-x509-ca-cert",
56 BAD_CAST "der", BAD_CAST "application/x-x509-ca-cert",
57 BAD_CAST "pk7", BAD_CAST "application/pkcs7-mime",
58 BAD_CAST "p7b", BAD_CAST "application/pkcs7-mime",
59 BAD_CAST "p7c", BAD_CAST "application/pkcs7-mime",
60 BAD_CAST "p7f", BAD_CAST "application/pkcs7-signature",
61 BAD_CAST "p7m", BAD_CAST "application/pkcs7-mime",
62 BAD_CAST "p7s", BAD_CAST "application/pkcs7-signature",
63 BAD_CAST "tst", BAD_CAST "application/timestamp-reply"
66 /* Deallocate structure isds_pki_credentials and NULL it.
67 * Pass-phrase is discarded.
68 * @pki credentials to to free */
69 void isds_pki_credentials_free(struct isds_pki_credentials **pki) {
70 if(!pki || !*pki) return;
72 free((*pki)->engine);
73 free((*pki)->certificate);
74 free((*pki)->key);
76 if ((*pki)->passphrase) {
77 memset((*pki)->passphrase, 0, strlen((*pki)->passphrase));
78 free((*pki)->passphrase);
81 zfree((*pki));
85 /* Free isds_list with all member data.
86 * @list list to free, on return will be NULL */
87 void isds_list_free(struct isds_list **list) {
88 struct isds_list *item, *next_item;
90 if (!list || !*list) return;
92 for(item = *list; item; item = next_item) {
93 (item->destructor)(&(item->data));
94 next_item = item->next;
95 free(item);
98 *list = NULL;
102 /* Deallocate structure isds_hash and NULL it.
103 * @hash hash to to free */
104 void isds_hash_free(struct isds_hash **hash) {
105 if(!hash || !*hash) return;
106 free((*hash)->value);
107 zfree((*hash));
111 /* Deallocate structure isds_PersonName recursively and NULL it */
112 static void isds_PersonName_free(struct isds_PersonName **person_name) {
113 if (!person_name || !*person_name) return;
115 free((*person_name)->pnFirstName);
116 free((*person_name)->pnMiddleName);
117 free((*person_name)->pnLastName);
118 free((*person_name)->pnLastNameAtBirth);
120 free(*person_name);
121 *person_name = NULL;
125 /* Deallocate structure isds_BirthInfo recursively and NULL it */
126 static void isds_BirthInfo_free(struct isds_BirthInfo **birth_info) {
127 if (!birth_info || !*birth_info) return;
129 free((*birth_info)->biDate);
130 free((*birth_info)->biCity);
131 free((*birth_info)->biCounty);
132 free((*birth_info)->biState);
134 free(*birth_info);
135 *birth_info = NULL;
139 /* Deallocate structure isds_Address recursively and NULL it */
140 static void isds_Address_free(struct isds_Address **address) {
141 if (!address || !*address) return;
143 free((*address)->adCity);
144 free((*address)->adStreet);
145 free((*address)->adNumberInStreet);
146 free((*address)->adNumberInMunicipality);
147 free((*address)->adZipCode);
148 free((*address)->adState);
150 free(*address);
151 *address = NULL;
155 /* Deallocate structure isds_DbOwnerInfo recursively and NULL it */
156 void isds_DbOwnerInfo_free(struct isds_DbOwnerInfo **db_owner_info) {
157 if (!db_owner_info || !*db_owner_info) return;
159 free((*db_owner_info)->dbID);
160 free((*db_owner_info)->dbType);
161 free((*db_owner_info)->ic);
162 isds_PersonName_free(&((*db_owner_info)->personName));
163 free((*db_owner_info)->firmName);
164 isds_BirthInfo_free(&((*db_owner_info)->birthInfo));
165 isds_Address_free(&((*db_owner_info)->address));
166 free((*db_owner_info)->nationality);
167 free((*db_owner_info)->email);
168 free((*db_owner_info)->telNumber);
169 free((*db_owner_info)->identifier);
170 free((*db_owner_info)->registryCode);
171 free((*db_owner_info)->dbState);
172 free((*db_owner_info)->dbEffectiveOVM);
174 free(*db_owner_info);
175 *db_owner_info = NULL;
178 /* Deallocate structure isds_DbUserInfo recursively and NULL it */
179 void isds_DbUserInfo_free(struct isds_DbUserInfo **db_user_info) {
180 if (!db_user_info || !*db_user_info) return;
182 free((*db_user_info)->userID);
183 free((*db_user_info)->userType);
184 free((*db_user_info)->userPrivils);
185 isds_PersonName_free(&((*db_user_info)->personName));
186 isds_Address_free(&((*db_user_info)->address));
187 free((*db_user_info)->biDate);
188 free((*db_user_info)->ic);
189 free((*db_user_info)->firmName);
190 free((*db_user_info)->caStreet);
191 free((*db_user_info)->caCity);
192 free((*db_user_info)->caZipCode);
193 free((*db_user_info)->caState);
195 zfree(*db_user_info);
199 /* Deallocate struct isds_event recursively and NULL it */
200 void isds_event_free(struct isds_event **event) {
201 if (!event || !*event) return;
203 free((*event)->time);
204 free((*event)->type);
205 free((*event)->description);
206 zfree(*event);
210 /* Deallocate struct isds_envelope recursively and NULL it */
211 void isds_envelope_free(struct isds_envelope **envelope) {
212 if (!envelope || !*envelope) return;
214 free((*envelope)->dmID);
215 free((*envelope)->dbIDSender);
216 free((*envelope)->dmSender);
217 free((*envelope)->dmSenderAddress);
218 free((*envelope)->dmSenderType);
219 free((*envelope)->dmRecipient);
220 free((*envelope)->dmRecipientAddress);
221 free((*envelope)->dmAmbiguousRecipient);
222 free((*envelope)->dmType);
224 free((*envelope)->dmOrdinal);
225 free((*envelope)->dmMessageStatus);
226 free((*envelope)->dmDeliveryTime);
227 free((*envelope)->dmAcceptanceTime);
228 isds_hash_free(&(*envelope)->hash);
229 free((*envelope)->timestamp);
230 isds_list_free(&(*envelope)->events);
232 free((*envelope)->dmSenderOrgUnit);
233 free((*envelope)->dmSenderOrgUnitNum);
234 free((*envelope)->dbIDRecipient);
235 free((*envelope)->dmRecipientOrgUnit);
236 free((*envelope)->dmRecipientOrgUnitNum);
237 free((*envelope)->dmToHands);
238 free((*envelope)->dmAnnotation);
239 free((*envelope)->dmRecipientRefNumber);
240 free((*envelope)->dmSenderRefNumber);
241 free((*envelope)->dmRecipientIdent);
242 free((*envelope)->dmSenderIdent);
244 free((*envelope)->dmLegalTitleLaw);
245 free((*envelope)->dmLegalTitleYear);
246 free((*envelope)->dmLegalTitleSect);
247 free((*envelope)->dmLegalTitlePar);
248 free((*envelope)->dmLegalTitlePoint);
250 free((*envelope)->dmPersonalDelivery);
251 free((*envelope)->dmAllowSubstDelivery);
252 free((*envelope)->dmOVM);
254 free(*envelope);
255 *envelope = NULL;
259 /* Deallocate struct isds_message recursively and NULL it */
260 void isds_message_free(struct isds_message **message) {
261 if (!message || !*message) return;
263 free((*message)->raw);
264 isds_envelope_free(&((*message)->envelope));
265 isds_list_free(&((*message)->documents));
266 xmlFreeDoc((*message)->xml); (*message)->xml = NULL;
268 free(*message);
269 *message = NULL;
273 /* Deallocate struct isds_document recursively and NULL it */
274 void isds_document_free(struct isds_document **document) {
275 if (!document || !*document) return;
277 if (!(*document)->is_xml) {
278 free((*document)->data);
280 free((*document)->dmMimeType);
281 free((*document)->dmFileGuid);
282 free((*document)->dmUpFileGuid);
283 free((*document)->dmFileDescr);
284 free((*document)->dmFormat);
286 free(*document);
287 *document = NULL;
291 /* Deallocate struct isds_message_copy recursively and NULL it */
292 void isds_message_copy_free(struct isds_message_copy **copy) {
293 if (!copy || !*copy) return;
295 free((*copy)->dbIDRecipient);
296 free((*copy)->dmRecipientOrgUnit);
297 free((*copy)->dmRecipientOrgUnitNum);
298 free((*copy)->dmToHands);
300 free((*copy)->dmStatus);
301 free((*copy)->dmID);
303 zfree(*copy);
307 /* Deallocate struct isds_approval recursively and NULL it */
308 void isds_approval_free(struct isds_approval **approval) {
309 if (!approval || !*approval) return;
311 free((*approval)->refference);
313 zfree(*approval);
317 /* *DUP_OR_ERROR macros needs error label */
318 #define STRDUP_OR_ERROR(new, template) { \
319 if (!template) { \
320 (new) = NULL; \
321 } else { \
322 (new) = strdup(template); \
323 if (!new) goto error; \
327 #define FLATDUP_OR_ERROR(new, template) { \
328 if (!template) { \
329 (new) = NULL; \
330 } else { \
331 (new) = malloc(sizeof(*(new))); \
332 if (!new) goto error; \
333 memcpy((new), (template), sizeof(*(template))); \
337 /* Copy structure isds_pki_credentials recursively. */
338 struct isds_pki_credentials *isds_pki_credentials_duplicate(
339 const struct isds_pki_credentials *template) {
340 struct isds_pki_credentials *new = NULL;
342 if(!template) return NULL;
344 new = calloc(1, sizeof(*new));
345 if (!new) return NULL;
347 STRDUP_OR_ERROR(new->engine, template->engine);
348 new->certificate_format = template->certificate_format;
349 STRDUP_OR_ERROR(new->certificate, template->certificate);
350 new->key_format = template->key_format;
351 STRDUP_OR_ERROR(new->key, template->key);
352 STRDUP_OR_ERROR(new->passphrase, template->passphrase);
354 return new;
356 error:
357 isds_pki_credentials_free(&new);
358 return NULL;
362 /* Copy structure isds_PersonName recursively */
363 struct isds_PersonName *isds_PersonName_duplicate(
364 const struct isds_PersonName *template) {
365 struct isds_PersonName *new = NULL;
367 if (!template) return NULL;
369 new = calloc(1, sizeof(*new));
370 if (!new) return NULL;
372 STRDUP_OR_ERROR(new->pnFirstName, template->pnFirstName);
373 STRDUP_OR_ERROR(new->pnMiddleName, template->pnMiddleName);
374 STRDUP_OR_ERROR(new->pnLastName, template->pnLastName);
375 STRDUP_OR_ERROR(new->pnLastNameAtBirth, template->pnLastNameAtBirth);
377 return new;
379 error:
380 isds_PersonName_free(&new);
381 return NULL;
385 /* Copy structure isds_BirthInfo recursively */
386 static struct isds_BirthInfo *isds_BirthInfo_duplicate(
387 const struct isds_BirthInfo *template) {
388 struct isds_BirthInfo *new = NULL;
390 if (!template) return NULL;
392 new = calloc(1, sizeof(*new));
393 if (!new) return NULL;
395 FLATDUP_OR_ERROR(new->biDate, template->biDate);
396 STRDUP_OR_ERROR(new->biCity, template->biCity);
397 STRDUP_OR_ERROR(new->biCounty, template->biCounty);
398 STRDUP_OR_ERROR(new->biState, template->biState);
400 return new;
402 error:
403 isds_BirthInfo_free(&new);
404 return NULL;
408 /* Copy structure isds_Address recursively */
409 struct isds_Address *isds_Address_duplicate(
410 const struct isds_Address *template) {
411 struct isds_Address *new = NULL;
413 if (!template) return NULL;
415 new = calloc(1, sizeof(*new));
416 if (!new) return NULL;
418 STRDUP_OR_ERROR(new->adCity, template->adCity);
419 STRDUP_OR_ERROR(new->adStreet, template->adStreet);
420 STRDUP_OR_ERROR(new->adNumberInStreet, template->adNumberInStreet);
421 STRDUP_OR_ERROR(new->adNumberInMunicipality,
422 template->adNumberInMunicipality);
423 STRDUP_OR_ERROR(new->adZipCode, template->adZipCode);
424 STRDUP_OR_ERROR(new->adState, template->adState);
426 return new;
428 error:
429 isds_Address_free(&new);
430 return NULL;
434 /* Copy structure isds_DbOwnerInfo recursively */
435 struct isds_DbOwnerInfo *isds_DbOwnerInfo_duplicate(
436 const struct isds_DbOwnerInfo *template) {
437 struct isds_DbOwnerInfo *new = NULL;
438 if (!template) return NULL;
440 new = calloc(1, sizeof(*new));
441 if (!new) return NULL;
443 STRDUP_OR_ERROR(new->dbID, template->dbID);
444 FLATDUP_OR_ERROR(new->dbType, template->dbType);
445 STRDUP_OR_ERROR(new->ic, template->ic);
447 if (template->personName) {
448 if (!(new->personName =
449 isds_PersonName_duplicate(template->personName)))
450 goto error;
453 STRDUP_OR_ERROR(new->firmName, template->firmName);
455 if (template->birthInfo) {
456 if (!(new->birthInfo =
457 isds_BirthInfo_duplicate(template->birthInfo)))
458 goto error;
461 if (template->address) {
462 if (!(new->address = isds_Address_duplicate(template->address)))
463 goto error;
466 STRDUP_OR_ERROR(new->nationality, template->nationality);
467 STRDUP_OR_ERROR(new->email, template->email);
468 STRDUP_OR_ERROR(new->telNumber, template->telNumber);
469 STRDUP_OR_ERROR(new->identifier, template->identifier);
470 STRDUP_OR_ERROR(new->registryCode, template->registryCode);
471 FLATDUP_OR_ERROR(new->dbState, template->dbState);
472 FLATDUP_OR_ERROR(new->dbEffectiveOVM, template->dbEffectiveOVM);
473 FLATDUP_OR_ERROR(new->dbOpenAddressing, template->dbOpenAddressing);
475 return new;
477 error:
478 isds_DbOwnerInfo_free(&new);
479 return NULL;
483 /* Copy structure isds_DbUserInfo recursively */
484 struct isds_DbUserInfo *isds_DbUserInfo_duplicate(
485 const struct isds_DbUserInfo *template) {
486 struct isds_DbUserInfo *new = NULL;
487 if (!template) return NULL;
489 new = calloc(1, sizeof(*new));
490 if (!new) return NULL;
492 STRDUP_OR_ERROR(new->userID, template->userID);
493 FLATDUP_OR_ERROR(new->userType, template->userType);
494 FLATDUP_OR_ERROR(new->userPrivils, template->userPrivils);
496 if (template->personName) {
497 if (!(new->personName =
498 isds_PersonName_duplicate(template->personName)))
499 goto error;
502 if (template->address) {
503 if (!(new->address = isds_Address_duplicate(template->address)))
504 goto error;
507 FLATDUP_OR_ERROR(new->biDate, template->biDate);
508 STRDUP_OR_ERROR(new->ic, template->ic);
509 STRDUP_OR_ERROR(new->firmName, template->firmName);
510 STRDUP_OR_ERROR(new->caStreet, template->caStreet);
511 STRDUP_OR_ERROR(new->caCity, template->caCity);
512 STRDUP_OR_ERROR(new->caZipCode, template->caZipCode);
513 STRDUP_OR_ERROR(new->caState, template->caState);
515 return new;
517 error:
518 isds_DbUserInfo_free(&new);
519 return NULL;
522 #undef FLATDUP_OR_ERROR
523 #undef STRDUP_OR_ERROR
526 /* Initialize ISDS library.
527 * Global function, must be called before other functions.
528 * If it fails you can not use ISDS library and must call isds_cleanup() to
529 * free partially initialized global variables. */
530 isds_error isds_init(void) {
531 /* NULL global variables */
532 log_facilities = ILF_ALL;
533 log_level = ILL_WARNING;
534 log_callback = NULL;
535 log_callback_data = NULL;
537 #if ENABLE_NLS
538 /* Initialize gettext */
539 bindtextdomain(PACKAGE, LOCALEDIR);
540 #endif
542 /* Initialize CURL */
543 if (curl_global_init(CURL_GLOBAL_ALL)) {
544 isds_log(ILF_ISDS, ILL_CRIT, _("CURL library initialization failed\n"));
545 return IE_ERROR;
548 /* Initialize gpg-error because of gpgme and ksba */
549 if (gpg_err_init()) {
550 isds_log(ILF_ISDS, ILL_CRIT,
551 _("gpg-error library initialization failed\n"));
552 return IE_ERROR;
555 /* Initialize GPGME */
556 if (_isds_init_gpgme(&version_gpgme)) {
557 isds_log(ILF_ISDS, ILL_CRIT,
558 _("GPGME library initialization failed\n"));
559 return IE_ERROR;
562 /* Initialize gcrypt */
563 if (_isds_init_gcrypt(&version_gcrypt)) {
564 isds_log(ILF_ISDS, ILL_CRIT,
565 _("gcrypt library initialization failed\n"));
566 return IE_ERROR;
569 /* This can _exit() current program. Find not so assertive check. */
570 LIBXML_TEST_VERSION;
572 /* Check expat */
573 if (_isds_init_expat(&version_expat)) {
574 isds_log(ILF_ISDS, ILL_CRIT,
575 _("expat library initialization failed\n"));
576 return IE_ERROR;
579 /* Allocate global variables */
582 return IE_SUCCESS;
586 /* Deinitialize ISDS library.
587 * Global function, must be called as last library function. */
588 isds_error isds_cleanup(void) {
589 /* XML */
590 xmlCleanupParser();
592 /* Curl */
593 curl_global_cleanup();
595 return IE_SUCCESS;
599 /* Return version string of this library. Version of dependencies can be
600 * embedded. Do no try to parse it. You must free it. */
601 char *isds_version(void) {
602 char *buffer = NULL;
604 isds_asprintf(&buffer, _("%s (%s, GPGME %s, gcrypt %s, %s, libxml2 %s)"),
605 PACKAGE_VERSION, curl_version(), version_gpgme, version_gcrypt,
606 version_expat, xmlParserVersion);
607 return buffer;
611 /* Return text description of ISDS error */
612 const char *isds_strerror(const isds_error error) {
613 switch (error) {
614 case IE_SUCCESS:
615 return(_("Success")); break;
616 case IE_ERROR:
617 return(_("Unspecified error")); break;
618 case IE_NOTSUP:
619 return(_("Not supported")); break;
620 case IE_INVAL:
621 return(_("Invalid value")); break;
622 case IE_INVALID_CONTEXT:
623 return(_("Invalid context")); break;
624 case IE_NOT_LOGGED_IN:
625 return(_("Not logged in")); break;
626 case IE_CONNECTION_CLOSED:
627 return(_("Connection closed")); break;
628 case IE_TIMED_OUT:
629 return(_("Timed out")); break;
630 case IE_NOEXIST:
631 return(_("Not exist")); break;
632 case IE_NOMEM:
633 return(_("Out of memory")); break;
634 case IE_NETWORK:
635 return(_("Network problem")); break;
636 case IE_HTTP:
637 return(_("HTTP problem")); break;
638 case IE_SOAP:
639 return(_("SOAP problem")); break;
640 case IE_XML:
641 return(_("XML problem")); break;
642 case IE_ISDS:
643 return(_("ISDS server problem")); break;
644 case IE_ENUM:
645 return(_("Invalid enum value")); break;
646 case IE_DATE:
647 return(_("Invalid date value")); break;
648 case IE_2BIG:
649 return(_("Too big")); break;
650 case IE_2SMALL:
651 return(_("Too small")); break;
652 case IE_NOTUNIQ:
653 return(_("Value not unique")); break;
654 case IE_NOTEQUAL:
655 return(_("Values not equal")); break;
656 case IE_PARTIAL_SUCCESS:
657 return(_("Some suboperations failed")); break;
658 case IE_ABORTED:
659 return(_("Operation aborted")); break;
660 default:
661 return(_("Unknown error"));
666 /* Create ISDS context.
667 * Each context can be used for different sessions to (possibly) different
668 * ISDS server with different credentials. */
669 struct isds_ctx *isds_ctx_create(void) {
670 struct isds_ctx *context;
671 context = malloc(sizeof(*context));
672 if (context) memset(context, 0, sizeof(*context));
673 return context;
677 /* Close possibly opened connection to Czech POINT document deposit without
678 * resetting long_message buffer.
679 * XXX: Do not use czp_close_connection() if you do not want to destroy log
680 * message.
681 * @context is Czech POINT session context. */
682 static isds_error czp_do_close_connection(struct isds_ctx *context) {
683 if (!context) return IE_INVALID_CONTEXT;
684 _isds_close_connection(context);
685 return IE_SUCCESS;
689 /* Discard credentials.
690 * Only that. It does not cause log out, connection close or similar. */
691 static isds_error discard_credentials(struct isds_ctx *context) {
692 if(!context) return IE_INVALID_CONTEXT;
694 if (context->username) {
695 memset(context->username, 0, strlen(context->username));
696 zfree(context->username);
698 if (context->password) {
699 memset(context->password, 0, strlen(context->password));
700 zfree(context->password);
702 isds_pki_credentials_free(&context->pki_credentials);
704 return IE_SUCCESS;
708 /* Destroy ISDS context and free memory.
709 * @context will be NULLed on success. */
710 isds_error isds_ctx_free(struct isds_ctx **context) {
711 if (!context || !*context) {
712 return IE_INVALID_CONTEXT;
715 /* Discard credentials and close connection */
716 switch ((*context)->type) {
717 case CTX_TYPE_NONE: break;
718 case CTX_TYPE_ISDS: isds_logout(*context); break;
719 case CTX_TYPE_CZP:
720 case CTX_TYPE_TESTING_REQUEST_COLLECTOR:
721 czp_do_close_connection(*context); break;
724 /* For sure */
725 discard_credentials(*context);
727 /* Free other structures */
728 free((*context)->tls_verify_server);
729 free((*context)->tls_ca_file);
730 free((*context)->tls_ca_dir);
731 free((*context)->tls_crl_file);
732 free((*context)->long_message);
734 free(*context);
735 *context = NULL;
736 return IE_SUCCESS;
740 /* Return long message text produced by library function, e.g. detailed error
741 * message. Returned pointer is only valid until new library function is
742 * called for the same context. Could be NULL, especially if NULL context is
743 * supplied. Return string is locale encoded. */
744 char *isds_long_message(const struct isds_ctx *context) {
745 if (!context) return NULL;
746 return context->long_message;
750 /* Stores message into context' long_message buffer.
751 * Application can pick the message up using isds_long_message().
752 * NULL @message truncates the buffer but does not deallocate it.
753 * @message is coded in locale encoding */
754 _hidden isds_error isds_log_message(struct isds_ctx *context,
755 const char *message) {
756 char *buffer;
757 size_t length;
759 if (!context) return IE_INVALID_CONTEXT;
761 /* FIXME: Check for integer overflow */
762 length = 1 + ((message) ? strlen(message) : 0);
763 buffer = realloc(context->long_message, length);
764 if (!buffer) return IE_NOMEM;
766 if (message)
767 strcpy(buffer, message);
768 else
769 *buffer = '\0';
771 context->long_message = buffer;
772 return IE_SUCCESS;
776 /* Appends message into context' long_message buffer.
777 * Application can pick the message up using isds_long_message().
778 * NULL message has void effect. */
779 _hidden isds_error isds_append_message(struct isds_ctx *context,
780 const char *message) {
781 char *buffer;
782 size_t old_length, length;
784 if (!context) return IE_INVALID_CONTEXT;
785 if (!message) return IE_SUCCESS;
786 if (!context->long_message)
787 return isds_log_message(context, message);
789 old_length = strlen(context->long_message);
790 /* FIXME: Check for integer overflow */
791 length = 1 + old_length + strlen(message);
792 buffer = realloc(context->long_message, length);
793 if (!buffer) return IE_NOMEM;
795 strcpy(buffer + old_length, message);
797 context->long_message = buffer;
798 return IE_SUCCESS;
802 /* Stores formatted message into context' long_message buffer.
803 * Application can pick the message up using isds_long_message(). */
804 _hidden isds_error isds_printf_message(struct isds_ctx *context,
805 const char *format, ...) {
806 va_list ap;
807 int length;
809 if (!context) return IE_INVALID_CONTEXT;
810 va_start(ap, format);
811 length = isds_vasprintf(&(context->long_message), format, ap);
812 va_end(ap);
814 return (length < 0) ? IE_ERROR: IE_SUCCESS;
818 /* Set logging up.
819 * @facilities is bit mask of isds_log_facility values,
820 * @level is verbosity level. */
821 void isds_set_logging(const unsigned int facilities,
822 const isds_log_level level) {
823 log_facilities = facilities;
824 log_level = level;
828 /* Register callback function libisds calls when new global log message is
829 * produced by library. Library logs to stderr by default.
830 * @callback is function provided by application libisds will call. See type
831 * definition for @callback argument explanation. Pass NULL to revert logging to
832 * default behaviour.
833 * @data is application specific data @callback gets as last argument */
834 void isds_set_log_callback(isds_log_callback callback, void *data) {
835 log_callback = callback;
836 log_callback_data = data;
840 /* Log @message in class @facility with log @level into global log. @message
841 * is printf(3) formatting string, variadic arguments may be necessary.
842 * For debugging purposes. */
843 _hidden isds_error isds_log(const isds_log_facility facility,
844 const isds_log_level level, const char *message, ...) {
845 va_list ap;
846 char *buffer = NULL;
847 int length;
849 if (level > log_level) return IE_SUCCESS;
850 if (!(log_facilities & facility)) return IE_SUCCESS;
851 if (!message) return IE_INVAL;
853 if (log_callback) {
854 /* Pass message to application supplied callback function */
855 va_start(ap, message);
856 length = isds_vasprintf(&buffer, message, ap);
857 va_end(ap);
859 if (length == -1) {
860 return IE_ERROR;
862 if (length > 0) {
863 log_callback(facility, level, buffer, length, log_callback_data);
865 free(buffer);
866 } else {
867 /* Default: Log it to stderr */
868 va_start(ap, message);
869 vfprintf(stderr, message, ap);
870 va_end(ap);
871 /* Line buffered printf is default.
872 * fflush(stderr);*/
875 return IE_SUCCESS;
879 /* Set timeout in milliseconds for each network job like connecting to server
880 * or sending message. Use 0 to disable timeout limits. */
881 isds_error isds_set_timeout(struct isds_ctx *context,
882 const unsigned int timeout) {
883 if (!context) return IE_INVALID_CONTEXT;
884 zfree(context->long_message);
886 context->timeout = timeout;
888 if (context->curl) {
889 CURLcode curl_err;
891 curl_err = curl_easy_setopt(context->curl, CURLOPT_NOSIGNAL, 1);
892 if (!curl_err)
893 #if HAVE_DECL_CURLOPT_TIMEOUT_MS /* Since curl-7.16.2 */
894 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT_MS,
895 context->timeout);
896 #else
897 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT,
898 context->timeout / 1000);
899 #endif /* not HAVE_DECL_CURLOPT_TIMEOUT_MS */
900 if (curl_err) return IE_ERROR;
903 return IE_SUCCESS;
907 /* Register callback function libisds calls periodically during HTTP data
908 * transfer.
909 * @context is session context
910 * @callback is function provided by application libisds will call. See type
911 * definition for @callback argument explanation.
912 * @data is application specific data @callback gets as last argument */
913 isds_error isds_set_progress_callback(struct isds_ctx *context,
914 isds_progress_callback callback, void *data) {
915 if (!context) return IE_INVALID_CONTEXT;
916 zfree(context->long_message);
918 context->progress_callback = callback;
919 context->progress_callback_data = data;
921 return IE_SUCCESS;
925 /* Change context settings.
926 * @context is context which setting will be applied to
927 * @option is name of option. It determines the type of last argument. See
928 * isds_option definition for more info.
929 * @... is value of new setting. Type is determined by @option
930 * */
931 isds_error isds_set_opt(struct isds_ctx *context, const isds_option option,
932 ...) {
933 isds_error err = IE_SUCCESS;
934 va_list ap;
935 char *pointer, *string;
937 if (!context) return IE_INVALID_CONTEXT;
938 zfree(context->long_message);
940 va_start(ap, option);
942 #define REPLACE_VA_BOOLEAN(destination) { \
943 if (!(destination)) { \
944 (destination) = malloc(sizeof(*(destination))); \
945 if (!(destination)) { \
946 err = IE_NOMEM; goto leave; \
949 *(destination) = (_Bool) !!va_arg(ap, int); \
952 #define REPLACE_VA_STRING(destination) { \
953 string = va_arg(ap, char *); \
954 if (string) { \
955 pointer = realloc((destination), 1 + strlen(string)); \
956 if (!pointer) { err = IE_NOMEM; goto leave; } \
957 strcpy(pointer, string); \
958 (destination) = pointer; \
959 } else { \
960 free(destination); \
961 (destination) = NULL; \
965 switch (option) {
966 case IOPT_TLS_VERIFY_SERVER:
967 REPLACE_VA_BOOLEAN(context->tls_verify_server);
968 break;
969 case IOPT_TLS_CA_FILE:
970 REPLACE_VA_STRING(context->tls_ca_file);
971 break;
972 case IOPT_TLS_CA_DIRECTORY:
973 REPLACE_VA_STRING(context->tls_ca_dir);
974 break;
975 case IOPT_TLS_CRL_FILE:
976 #if HAVE_DECL_CURLOPT_CRLFILE /* Since curl-7.19.0 */
977 REPLACE_VA_STRING(context->tls_crl_file);
978 #else
979 isds_log_message(context,
980 _("Curl library does not support CRL definition"));
981 err = IE_NOTSUP;
982 #endif /* not HAVE_DECL_CURLOPT_CRLFILE */
983 break;
984 case IOPT_NORMALIZE_MIME_TYPE:
985 context->normalize_mime_type = (_Bool) !!va_arg(ap, int);
986 break;
988 default:
989 err = IE_ENUM; goto leave;
992 #undef REPLACE_VA_STRING
993 #undef REPLACE_VA_BOOLEAN
995 leave:
996 va_end(ap);
997 return err;
1001 /* Deprecated: Use isds_set_opt() instead.
1002 * Change SSL/TLS settings.
1003 * @context is context which setting will be applied to
1004 * @option is name of option. It determines the type of last argument. See
1005 * isds_tls_option definition for more info.
1006 * @... is value of new setting. Type is determined by @option
1007 * */
1008 isds_error isds_set_tls(struct isds_ctx *context, const isds_tls_option option,
1009 ...) {
1010 isds_error err = IE_ENUM;
1011 va_list ap;
1013 va_start(ap, option);
1015 switch (option) {
1016 case ITLS_VERIFY_SERVER:
1017 err = isds_set_opt(context, option, va_arg(ap, int));
1018 break;
1019 case ITLS_CA_FILE:
1020 case ITLS_CA_DIRECTORY:
1021 case ITLS_CRL_FILE:
1022 err = isds_set_opt(context, option, va_arg(ap, char*));
1025 va_end(ap);
1026 return err;
1030 /* Connect and log in into ISDS server.
1031 * All required arguments will be copied, you do not have to keep them after
1032 * that.
1033 * ISDS supports four different authentication methods. Exact method is
1034 * selected on @username, @passwors and @pki_credentials arguments:
1035 * - If @pki_credentials == NULL, @username and @password must be supplied
1036 * - If @pki_credentials != NULL, then
1037 * - If @username == NULL, only certificate will be used
1038 * - If @username != NULL, then
1039 * - If @password == NULL, then certificate will be used and
1040 * @username shifts meaning to box ID. This is used for hosted
1041 * services.
1042 * - Otherwise all three arguments will be used.
1043 * Please note, that different cases requires different certificate type
1044 * (system qualified one or commercial non qualified one). This library does
1045 * not check such political issues. Please see ISDS Specification for more
1046 * details.
1047 * @url is base address of ISDS web service. Pass extern isds_locator
1048 * variable to use production ISDS instance without client certificate
1049 * authentication (or extern isds_cert_locator with client certificate
1050 * authentication). Passing NULL has the same effect, autoselection between
1051 * isds_locator and isds_cert_locator is performed in addition. You can pass
1052 * extern isds_testing_locator (or isds_cert_testing_locator) variable to
1053 * select testing instance.
1054 * @username is user name of ISDS user or box ID
1055 * @password is user's secret password
1056 * @pki_credentials defines public key cryptographic material to use in client
1057 * authentication. */
1058 isds_error isds_login(struct isds_ctx *context, const char *url,
1059 const char *username, const char *password,
1060 const struct isds_pki_credentials *pki_credentials) {
1061 isds_error err = IE_NOT_LOGGED_IN;
1062 isds_error soap_err;
1063 xmlNsPtr isds_ns = NULL;
1064 xmlNodePtr request = NULL;
1065 xmlNodePtr response = NULL;
1067 if (!context) return IE_INVALID_CONTEXT;
1068 zfree(context->long_message);
1070 /* Close connection if already logged in */
1071 if (context->curl) {
1072 _isds_close_connection(context);
1075 /* Store configuration */
1076 context->type = CTX_TYPE_ISDS;
1077 zfree(context->url);
1079 /* Mangle base URI according requested authentication method */
1080 if (!pki_credentials) {
1081 isds_log(ILF_SEC, ILL_INFO,
1082 _("Selected authentication method: no certificate, "
1083 "username and password\n"));
1084 if (!username || !password) {
1085 isds_log_message(context,
1086 _("Both username and password must be supplied"));
1087 return IE_INVAL;
1089 /* Default locator is official system (without client certificate) */
1090 context->url = strdup((url) ? url : isds_locator);
1091 } else {
1092 /* Default locator is official system (with client certificate) */
1093 if (!url) url = isds_cert_locator;
1095 if (!username) {
1096 isds_log(ILF_SEC, ILL_INFO,
1097 _("Selected authentication method: system certificate, "
1098 "no username and no password\n"));
1099 password = NULL;
1100 context->url = _isds_astrcat(url, "cert/");
1101 } else {
1102 if (!password) {
1103 isds_log(ILF_SEC, ILL_INFO,
1104 _("Selected authentication method: system certificate, "
1105 "box ID and no password\n"));
1106 context->url = _isds_astrcat(url, "hspis/");
1107 } else {
1108 isds_log(ILF_SEC, ILL_INFO,
1109 _("Selected authentication method: commercial "
1110 "certificate, username and password\n"));
1111 context->url = _isds_astrcat(url, "certds/");
1115 if (!(context->url))
1116 return IE_NOMEM;
1118 /* Prepare CURL handle */
1119 context->curl = curl_easy_init();
1120 if (!(context->curl))
1121 return IE_ERROR;
1123 /* Build log-in request */
1124 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1125 if (!request) {
1126 isds_log_message(context, _("Could not build ISDS log-in request"));
1127 return IE_ERROR;
1129 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1130 if(!isds_ns) {
1131 isds_log_message(context, _("Could not create ISDS name space"));
1132 xmlFreeNode(request);
1133 return IE_ERROR;
1135 xmlSetNs(request, isds_ns);
1137 /* Store credentials */
1138 /* FIXME: mlock password
1139 * (I have a library) */
1140 discard_credentials(context);
1141 if (username) context->username = strdup(username);
1142 if (password) context->password = strdup(password);
1143 context->pki_credentials = isds_pki_credentials_duplicate(pki_credentials);
1144 if ((username && !context->username) || (password && !context->password) ||
1145 (pki_credentials && !context->pki_credentials)) {
1146 discard_credentials(context);
1147 xmlFreeNode(request);
1148 return IE_NOMEM;
1151 isds_log(ILF_ISDS, ILL_DEBUG, _("Logging user %s into server %s\n"),
1152 username, url);
1154 /* Send log-in request */
1155 soap_err = _isds_soap(context, "DS/dz", request, &response, NULL, NULL);
1157 /* Remove credentials */
1158 discard_credentials(context);
1160 /* Destroy log-in request */
1161 xmlFreeNode(request);
1163 if (soap_err) {
1164 xmlFreeNodeList(response);
1165 _isds_close_connection(context);
1166 return soap_err;
1169 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1170 * authentication succeeded if soap_err == IE_SUCCESS */
1171 err = IE_SUCCESS;
1173 xmlFreeNodeList(response);
1175 if (!err)
1176 isds_log(ILF_ISDS, ILL_DEBUG,
1177 _("User %s has been logged into server %s successfully\n"),
1178 username, url);
1179 return err;
1183 /* Log out from ISDS server discards credentials and connection configuration. */
1184 isds_error isds_logout(struct isds_ctx *context) {
1185 if (!context) return IE_INVALID_CONTEXT;
1186 zfree(context->long_message);
1188 /* Close connection */
1189 if (context->curl) {
1190 _isds_close_connection(context);
1192 /* Discard credentials for sure. They should not survive isds_login(),
1193 * even successful .*/
1194 discard_credentials(context);
1195 zfree(context->url);
1197 isds_log(ILF_ISDS, ILL_DEBUG, _("Logged out from ISDS server\n"));
1198 } else {
1199 discard_credentials(context);
1201 return IE_SUCCESS;
1205 /* Verify connection to ISDS is alive and server is responding.
1206 * Sent dummy request to ISDS and expect dummy response. */
1207 isds_error isds_ping(struct isds_ctx *context) {
1208 isds_error soap_err;
1209 xmlNsPtr isds_ns = NULL;
1210 xmlNodePtr request = NULL;
1211 xmlNodePtr response = NULL;
1213 if (!context) return IE_INVALID_CONTEXT;
1214 zfree(context->long_message);
1216 /* Check if connection is established */
1217 if (!context->curl) return IE_CONNECTION_CLOSED;
1220 /* Build dummy request */
1221 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1222 if (!request) {
1223 isds_log_message(context, _("Could build ISDS dummy request"));
1224 return IE_ERROR;
1226 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1227 if(!isds_ns) {
1228 isds_log_message(context, _("Could not create ISDS name space"));
1229 xmlFreeNode(request);
1230 return IE_ERROR;
1232 xmlSetNs(request, isds_ns);
1234 isds_log(ILF_ISDS, ILL_DEBUG, _("Pinging ISDS server\n"));
1236 /* Sent dummy request */
1237 soap_err = _isds_soap(context, "DS/dz", request, &response, NULL, NULL);
1239 /* Destroy log-in request */
1240 xmlFreeNode(request);
1242 if (soap_err) {
1243 isds_log(ILF_ISDS, ILL_DEBUG,
1244 _("ISDS server could not be contacted\n"));
1245 xmlFreeNodeList(response);
1246 return soap_err;
1249 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1250 * authentication succeeded if soap_err == IE_SUCCESS */
1251 /* TODO: ISDS documentation does not specify response body.
1252 * However real server sends back DummyOperationResponse */
1255 xmlFreeNodeList(response);
1257 isds_log(ILF_ISDS, ILL_DEBUG, _("ISDS server alive\n"));
1259 return IE_SUCCESS;
1263 /* Send bogus request to ISDS.
1264 * Just for test purposes */
1265 isds_error isds_bogus_request(struct isds_ctx *context) {
1266 isds_error err;
1267 xmlNsPtr isds_ns = NULL;
1268 xmlNodePtr request = NULL;
1269 xmlDocPtr response = NULL;
1270 xmlChar *code = NULL, *message = NULL;
1272 if (!context) return IE_INVALID_CONTEXT;
1273 zfree(context->long_message);
1275 /* Check if connection is established */
1276 if (!context->curl) {
1277 /* Testing printf message */
1278 isds_printf_message(context, "%s", _("I said connection closed"));
1279 return IE_CONNECTION_CLOSED;
1283 /* Build dummy request */
1284 request = xmlNewNode(NULL, BAD_CAST "X-BogusOperation");
1285 if (!request) {
1286 isds_log_message(context, _("Could build ISDS bogus request"));
1287 return IE_ERROR;
1289 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1290 if(!isds_ns) {
1291 isds_log_message(context, _("Could not create ISDS name space"));
1292 xmlFreeNode(request);
1293 return IE_ERROR;
1295 xmlSetNs(request, isds_ns);
1297 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending bogus request to ISDS\n"));
1299 /* Sent bogus request */
1300 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
1302 /* Destroy request */
1303 xmlFreeNode(request);
1305 if (err) {
1306 isds_log(ILF_ISDS, ILL_DEBUG,
1307 _("Processing ISDS response on bogus request failed\n"));
1308 xmlFreeDoc(response);
1309 return err;
1312 /* Check for response status */
1313 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
1314 &code, &message, NULL);
1315 if (err) {
1316 isds_log(ILF_ISDS, ILL_DEBUG,
1317 _("ISDS response on bogus request is missing status\n"));
1318 free(code);
1319 free(message);
1320 xmlFreeDoc(response);
1321 return err;
1323 if (xmlStrcmp(code, BAD_CAST "0000")) {
1324 char *code_locale = _isds_utf82locale((char*)code);
1325 char *message_locale = _isds_utf82locale((char*)message);
1326 isds_log(ILF_ISDS, ILL_DEBUG,
1327 _("Server refused bogus request (code=%s, message=%s)\n"),
1328 code_locale, message_locale);
1329 /* XXX: Literal error messages from ISDS are Czech messages
1330 * (English sometimes) in UTF-8. It's hard to catch them for
1331 * translation. Successfully gettextized would return in locale
1332 * encoding, unsuccessfully translated would pass in UTF-8. */
1333 isds_log_message(context, message_locale);
1334 free(code_locale);
1335 free(message_locale);
1336 free(code);
1337 free(message);
1338 xmlFreeDoc(response);
1339 return IE_ISDS;
1343 free(code);
1344 free(message);
1345 xmlFreeDoc(response);
1347 isds_log(ILF_ISDS, ILL_DEBUG,
1348 _("Bogus message accepted by server. This should not happen.\n"));
1350 return IE_SUCCESS;
1354 /* Serialize XML subtree to buffer preserving XML indentation.
1355 * @context is session context
1356 * @subtree is XML element to be serialized (with children)
1357 * @buffer is automatically reallocated buffer where serialize to
1358 * @length is size of serialized stream in bytes
1359 * @return standard error code, free @buffer in case of error */
1360 static isds_error serialize_subtree(struct isds_ctx *context,
1361 xmlNodePtr subtree, void **buffer, size_t *length) {
1362 isds_error err = IE_SUCCESS;
1363 xmlBufferPtr xml_buffer = NULL;
1364 xmlSaveCtxtPtr save_ctx = NULL;
1365 xmlDocPtr subtree_doc = NULL;
1366 xmlNodePtr subtree_copy;
1367 xmlNsPtr isds_ns;
1368 void *new_buffer;
1370 if (!context) return IE_INVALID_CONTEXT;
1371 if (!buffer) return IE_INVAL;
1372 zfree(*buffer);
1373 if (!subtree || !length) return IE_INVAL;
1375 /* Make temporary XML document with @subtree root element */
1376 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1377 * It can result in not well-formed on invalid XML tree (e.g. name space
1378 * prefix definition can miss. */
1379 /*FIXME */
1381 subtree_doc = xmlNewDoc(BAD_CAST "1.0");
1382 if (!subtree_doc) {
1383 isds_log_message(context, _("Could not build temporary document"));
1384 err = IE_ERROR;
1385 goto leave;
1388 /* XXX: Copy subtree and attach the copy to document.
1389 * One node can not bee attached into more document at the same time.
1390 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1391 * automatically.
1392 * XXX: Check xmlSaveTree() too. */
1393 subtree_copy = xmlCopyNodeList(subtree);
1394 if (!subtree_copy) {
1395 isds_log_message(context, _("Could not copy subtree"));
1396 err = IE_ERROR;
1397 goto leave;
1399 xmlDocSetRootElement(subtree_doc, subtree_copy);
1401 /* Only this way we get namespace definition as @xmlns:isds,
1402 * otherwise we get namespace prefix without definition */
1403 /* FIXME: Don't overwrite original default namespace */
1404 isds_ns = xmlNewNs(subtree_copy, BAD_CAST ISDS_NS, NULL);
1405 if(!isds_ns) {
1406 isds_log_message(context, _("Could not create ISDS name space"));
1407 err = IE_ERROR;
1408 goto leave;
1410 xmlSetNs(subtree_copy, isds_ns);
1413 /* Serialize the document into buffer */
1414 xml_buffer = xmlBufferCreate();
1415 if (!xml_buffer) {
1416 isds_log_message(context, _("Could not create xmlBuffer"));
1417 err = IE_ERROR;
1418 goto leave;
1420 /* Last argument 0 means to not format the XML tree */
1421 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8", 0);
1422 if (!save_ctx) {
1423 isds_log_message(context, _("Could not create XML serializer"));
1424 err = IE_ERROR;
1425 goto leave;
1427 /* XXX: According LibXML documentation, this function does not return
1428 * meaningful value yet */
1429 xmlSaveDoc(save_ctx, subtree_doc);
1430 if (-1 == xmlSaveFlush(save_ctx)) {
1431 isds_log_message(context,
1432 _("Could not serialize XML subtree"));
1433 err = IE_ERROR;
1434 goto leave;
1436 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1437 * even after xmlSaveFlush(). Thus close it here */
1438 xmlSaveClose(save_ctx); save_ctx = NULL;
1441 /* Store and detach buffer from xml_buffer */
1442 *buffer = xml_buffer->content;
1443 *length = xml_buffer->use;
1444 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1446 /* Shrink buffer */
1447 new_buffer = realloc(*buffer, *length);
1448 if (new_buffer) *buffer = new_buffer;
1450 leave:
1451 if (err) {
1452 zfree(*buffer);
1453 *length = 0;
1456 xmlSaveClose(save_ctx);
1457 xmlBufferFree(xml_buffer);
1458 xmlFreeDoc(subtree_doc); /* Frees subtree_copy, isds_ns etc. */
1459 return err;
1462 #if 0
1463 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1464 * @context is session context
1465 * @document is original document where @nodeset points to
1466 * @nodeset is XPath node set to dump (recursively)
1467 * @buffer is automatically reallocated buffer where serialize to
1468 * @length is size of serialized stream in bytes
1469 * @return standard error code, free @buffer in case of error */
1470 static isds_error dump_nodeset(struct isds_ctx *context,
1471 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1472 void **buffer, size_t *length) {
1473 isds_error err = IE_SUCCESS;
1474 xmlBufferPtr xml_buffer = NULL;
1475 void *new_buffer;
1477 if (!context) return IE_INVALID_CONTEXT;
1478 if (!buffer) return IE_INVAL;
1479 zfree(*buffer);
1480 if (!document || !nodeset || !length) return IE_INVAL;
1481 *length = 0;
1483 /* Empty node set results into NULL buffer */
1484 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1485 goto leave;
1488 /* Resulting the document into buffer */
1489 xml_buffer = xmlBufferCreate();
1490 if (!xml_buffer) {
1491 isds_log_message(context, _("Could not create xmlBuffer"));
1492 err = IE_ERROR;
1493 goto leave;
1496 /* Iterate over all nodes */
1497 for (int i = 0; i < nodeset->nodeNr; i++) {
1498 /* Serialize node.
1499 * XXX: xmlNodeDump() appends to xml_buffer. */
1500 if (-1 ==
1501 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1502 isds_log_message(context, _("Could not dump XML node"));
1503 err = IE_ERROR;
1504 goto leave;
1508 /* Store and detach buffer from xml_buffer */
1509 *buffer = xml_buffer->content;
1510 *length = xml_buffer->use;
1511 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1513 /* Shrink buffer */
1514 new_buffer = realloc(*buffer, *length);
1515 if (new_buffer) *buffer = new_buffer;
1518 leave:
1519 if (err) {
1520 zfree(*buffer);
1521 *length = 0;
1524 xmlBufferFree(xml_buffer);
1525 return err;
1527 #endif
1529 #if 0
1530 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1531 * @context is session context
1532 * @document is original document where @nodeset points to
1533 * @nodeset is XPath node set to dump (recursively)
1534 * @buffer is automatically reallocated buffer where serialize to
1535 * @length is size of serialized stream in bytes
1536 * @return standard error code, free @buffer in case of error */
1537 static isds_error dump_nodeset(struct isds_ctx *context,
1538 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1539 void **buffer, size_t *length) {
1540 isds_error err = IE_SUCCESS;
1541 xmlBufferPtr xml_buffer = NULL;
1542 xmlSaveCtxtPtr save_ctx = NULL;
1543 void *new_buffer;
1545 if (!context) return IE_INVALID_CONTEXT;
1546 if (!buffer) return IE_INVAL;
1547 zfree(*buffer);
1548 if (!document || !nodeset || !length) return IE_INVAL;
1549 *length = 0;
1551 /* Empty node set results into NULL buffer */
1552 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1553 goto leave;
1556 /* Resulting the document into buffer */
1557 xml_buffer = xmlBufferCreate();
1558 if (!xml_buffer) {
1559 isds_log_message(context, _("Could not create xmlBuffer"));
1560 err = IE_ERROR;
1561 goto leave;
1563 if (xmlSubstituteEntitiesDefault(1)) {
1564 isds_log_message(context, _("Could not disable attribute escaping"));
1565 err = IE_ERROR;
1566 goto leave;
1568 /* Last argument means:
1569 * 0 to not format the XML tree
1570 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1571 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8",
1572 XML_SAVE_NO_DECL|XML_SAVE_NO_EMPTY|XML_SAVE_NO_XHTML);
1573 if (!save_ctx) {
1574 isds_log_message(context, _("Could not create XML serializer"));
1575 err = IE_ERROR;
1576 goto leave;
1578 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1579 isds_log_message(context, _("Could not disable attribute escaping"));
1580 err = IE_ERROR;
1581 goto leave;
1585 /* Iterate over all nodes */
1586 for (int i = 0; i < nodeset->nodeNr; i++) {
1587 /* Serialize node.
1588 * XXX: xmlNodeDump() appends to xml_buffer. */
1589 /*if (-1 ==
1590 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1592 /* XXX: According LibXML documentation, this function does not return
1593 * meaningful value yet */
1594 xmlSaveTree(save_ctx, nodeset->nodeTab[i]);
1595 if (-1 == xmlSaveFlush(save_ctx)) {
1596 isds_log_message(context,
1597 _("Could not serialize XML subtree"));
1598 err = IE_ERROR;
1599 goto leave;
1603 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1604 * even after xmlSaveFlush(). Thus close it here */
1605 xmlSaveClose(save_ctx); save_ctx = NULL;
1607 /* Store and detach buffer from xml_buffer */
1608 *buffer = xml_buffer->content;
1609 *length = xml_buffer->use;
1610 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1612 /* Shrink buffer */
1613 new_buffer = realloc(*buffer, *length);
1614 if (new_buffer) *buffer = new_buffer;
1616 leave:
1617 if (err) {
1618 zfree(*buffer);
1619 *length = 0;
1622 xmlSaveClose(save_ctx);
1623 xmlBufferFree(xml_buffer);
1624 return err;
1626 #endif
1629 /* Convert UTF-8 @string representation of ISDS dbType to enum @type */
1630 static isds_error string2isds_DbType(xmlChar *string, isds_DbType *type) {
1631 if (!string || !type) return IE_INVAL;
1633 if (!xmlStrcmp(string, BAD_CAST "FO"))
1634 *type = DBTYPE_FO;
1635 else if (!xmlStrcmp(string, BAD_CAST "PFO"))
1636 *type = DBTYPE_PFO;
1637 else if (!xmlStrcmp(string, BAD_CAST "PFO_ADVOK"))
1638 *type = DBTYPE_PFO_ADVOK;
1639 else if (!xmlStrcmp(string, BAD_CAST "PFO_DANPOR"))
1640 *type = DBTYPE_PFO_DANPOR;
1641 else if (!xmlStrcmp(string, BAD_CAST "PFO_INSSPR"))
1642 *type = DBTYPE_PFO_INSSPR;
1643 else if (!xmlStrcmp(string, BAD_CAST "PO"))
1644 *type = DBTYPE_PO;
1645 else if (!xmlStrcmp(string, BAD_CAST "PO_ZAK"))
1646 *type = DBTYPE_PO_ZAK;
1647 else if (!xmlStrcmp(string, BAD_CAST "PO_REQ"))
1648 *type = DBTYPE_PO_REQ;
1649 else if (!xmlStrcmp(string, BAD_CAST "OVM"))
1650 *type = DBTYPE_OVM;
1651 else if (!xmlStrcmp(string, BAD_CAST "OVM_NOTAR"))
1652 *type = DBTYPE_OVM_NOTAR;
1653 else if (!xmlStrcmp(string, BAD_CAST "OVM_EXEKUT"))
1654 *type = DBTYPE_OVM_EXEKUT;
1655 else if (!xmlStrcmp(string, BAD_CAST "OVM_REQ"))
1656 *type = DBTYPE_OVM_REQ;
1657 else
1658 return IE_ENUM;
1659 return IE_SUCCESS;
1663 /* Convert ISDS dbType enum @type to UTF-8 string.
1664 * @Return pointer to static string, or NULL if unknown enum value */
1665 static const xmlChar *isds_DbType2string(const isds_DbType type) {
1666 switch(type) {
1667 /* DBTYPE_SYSTEM is invalid value from point of view of public
1668 * SOAP interface. */
1669 case DBTYPE_FO: return(BAD_CAST "FO"); break;
1670 case DBTYPE_PFO: return(BAD_CAST "PFO"); break;
1671 case DBTYPE_PFO_ADVOK: return(BAD_CAST "PFO_ADVOK"); break;
1672 case DBTYPE_PFO_DANPOR: return(BAD_CAST "PFO_DANPOR"); break;
1673 case DBTYPE_PFO_INSSPR: return(BAD_CAST "PFO_INSSPR"); break;
1674 case DBTYPE_PO: return(BAD_CAST "PO"); break;
1675 case DBTYPE_PO_ZAK: return(BAD_CAST "PO_ZAK"); break;
1676 case DBTYPE_PO_REQ: return(BAD_CAST "PO_REQ"); break;
1677 case DBTYPE_OVM: return(BAD_CAST "OVM"); break;
1678 case DBTYPE_OVM_NOTAR: return(BAD_CAST "OVM_NOTAR"); break;
1679 case DBTYPE_OVM_EXEKUT: return(BAD_CAST "OVM_EXEKUT"); break;
1680 case DBTYPE_OVM_REQ: return(BAD_CAST "OVM_REQ"); break;
1681 default: return NULL; break;
1686 /* Convert UTF-8 @string representation of ISDS userType to enum @type */
1687 static isds_error string2isds_UserType(xmlChar *string, isds_UserType *type) {
1688 if (!string || !type) return IE_INVAL;
1690 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
1691 *type = USERTYPE_PRIMARY;
1692 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
1693 *type = USERTYPE_ENTRUSTED;
1694 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
1695 *type = USERTYPE_ADMINISTRATOR;
1696 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
1697 *type = USERTYPE_OFFICIAL;
1698 else
1699 return IE_ENUM;
1700 return IE_SUCCESS;
1704 /* Convert ISDS userType enum @type to UTF-8 string.
1705 * @Return pointer to static string, or NULL if unknown enum value */
1706 static const xmlChar *isds_UserType2string(const isds_UserType type) {
1707 switch(type) {
1708 case USERTYPE_PRIMARY: return(BAD_CAST "PRIMARY_USER"); break;
1709 case USERTYPE_ENTRUSTED: return(BAD_CAST "ENTRUSTED_USER"); break;
1710 case USERTYPE_ADMINISTRATOR: return(BAD_CAST "ADMINISTRATOR"); break;
1711 case USERTYPE_OFFICIAL: return(BAD_CAST "OFFICIAL"); break;
1712 default: return NULL; break;
1717 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
1718 * @Return pointer to static string, or NULL if unknown enum value */
1719 static const xmlChar *isds_FileMetaType2string(const isds_FileMetaType type) {
1720 switch(type) {
1721 case FILEMETATYPE_MAIN: return(BAD_CAST "main"); break;
1722 case FILEMETATYPE_ENCLOSURE: return(BAD_CAST "enclosure"); break;
1723 case FILEMETATYPE_SIGNATURE: return(BAD_CAST "signature"); break;
1724 case FILEMETATYPE_META: return(BAD_CAST "meta"); break;
1725 default: return NULL; break;
1730 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
1731 * @Return IE_ENUM if @string is not valid enum member */
1732 static isds_error string2isds_FileMetaType(const xmlChar *string,
1733 isds_FileMetaType *type) {
1734 if (!string || !type) return IE_INVAL;
1736 if (!xmlStrcmp(string, BAD_CAST "main"))
1737 *type = FILEMETATYPE_MAIN;
1738 else if (!xmlStrcmp(string, BAD_CAST "enclosure"))
1739 *type = FILEMETATYPE_ENCLOSURE;
1740 else if (!xmlStrcmp(string, BAD_CAST "signature"))
1741 *type = FILEMETATYPE_SIGNATURE;
1742 else if (!xmlStrcmp(string, BAD_CAST "meta"))
1743 *type = FILEMETATYPE_META;
1744 else
1745 return IE_ENUM;
1746 return IE_SUCCESS;
1750 /* Convert UTF-8 @string to ISDS hash @algorithm.
1751 * @Return IE_ENUM if @string is not valid enum member */
1752 static isds_error string2isds_hash_algorithm(const xmlChar *string,
1753 isds_hash_algorithm *algorithm) {
1754 if (!string || !algorithm) return IE_INVAL;
1756 if (!xmlStrcmp(string, BAD_CAST "MD5"))
1757 *algorithm = HASH_ALGORITHM_MD5;
1758 else if (!xmlStrcmp(string, BAD_CAST "SHA-1"))
1759 *algorithm = HASH_ALGORITHM_SHA_1;
1760 else if (!xmlStrcmp(string, BAD_CAST "SHA-224"))
1761 *algorithm = HASH_ALGORITHM_SHA_224;
1762 else if (!xmlStrcmp(string, BAD_CAST "SHA-256"))
1763 *algorithm = HASH_ALGORITHM_SHA_256;
1764 else if (!xmlStrcmp(string, BAD_CAST "SHA-384"))
1765 *algorithm = HASH_ALGORITHM_SHA_384;
1766 else if (!xmlStrcmp(string, BAD_CAST "SHA-512"))
1767 *algorithm = HASH_ALGORITHM_SHA_512;
1768 else
1769 return IE_ENUM;
1770 return IE_SUCCESS;
1774 /* Convert UTF-8 @string representation of ISO 8601 date to @time.
1775 * XXX: Not all ISO formats are supported */
1776 static isds_error datestring2tm(const xmlChar *string, struct tm *time) {
1777 char *offset;
1778 if (!string || !time) return IE_INVAL;
1780 /* xsd:date is ISO 8601 string, thus ASCII */
1781 offset = strptime((char*)string, "%Y-%m-%d", time);
1782 if (offset && *offset == '\0')
1783 return IE_SUCCESS;
1785 offset = strptime((char*)string, "%Y%m%d", time);
1786 if (offset && *offset == '\0')
1787 return IE_SUCCESS;
1789 offset = strptime((char*)string, "%Y-%j", time);
1790 if (offset && *offset == '\0')
1791 return IE_SUCCESS;
1793 return IE_NOTSUP;
1797 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
1798 static isds_error tm2datestring(const struct tm *time, xmlChar **string) {
1799 if (!time || !string) return IE_INVAL;
1801 if (-1 == isds_asprintf((char **) string, "%d-%02d-%02d",
1802 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
1803 return IE_ERROR;
1805 return IE_SUCCESS;
1809 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
1810 * respects the @time microseconds too. */
1811 static isds_error timeval2timestring(const struct timeval *time,
1812 xmlChar **string) {
1813 struct tm broken;
1815 if (!time || !string) return IE_INVAL;
1817 if (!gmtime_r(&time->tv_sec, &broken)) return IE_DATE;
1818 if (time->tv_usec < 0 || time->tv_usec > 999999) return IE_DATE;
1820 /* TODO: small negative year should be formatted as "-0012". This is not
1821 * true for glibc "%04d". We should implement it.
1822 * TODO: What's type of time->tv_usec exactly? Unsigned? Absolute?
1823 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
1824 if (-1 == isds_asprintf((char **) string,
1825 "%04d-%02d-%02dT%02d:%02d:%02d.%06ld",
1826 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
1827 broken.tm_hour, broken.tm_min, broken.tm_sec,
1828 time->tv_usec))
1829 return IE_ERROR;
1831 return IE_SUCCESS;
1835 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
1836 * It respects microseconds too.
1837 * In case of error, @time will be freed. */
1838 static isds_error timestring2timeval(const xmlChar *string,
1839 struct timeval **time) {
1840 struct tm broken;
1841 char *offset, *delim, *endptr;
1842 char subseconds[7];
1843 int offset_hours, offset_minutes;
1844 int i;
1846 if (!time) return IE_INVAL;
1848 memset(&broken, 0, sizeof(broken));
1850 if (!*time) {
1851 *time = calloc(1, sizeof(**time));
1852 if (!*time) return IE_NOMEM;
1853 } else {
1854 memset(*time, 0, sizeof(**time));
1858 /* xsd:date is ISO 8601 string, thus ASCII */
1859 /*TODO: negative year */
1861 /* Parse date and time without subseconds and offset */
1862 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
1863 if (!offset) {
1864 free(*time); *time = NULL;
1865 return IE_DATE;
1868 /* Get subseconds */
1869 if (*offset == '.' ) {
1870 offset++;
1872 /* Copy first 6 digits, pad it with zeros.
1873 * XXX: It truncates longer number, no round.
1874 * Current server implementation uses only millisecond resolution. */
1875 /* TODO: isdigit() is locale sensitive */
1876 for (i = 0;
1877 i < sizeof(subseconds)/sizeof(char) - 1 && isdigit(*offset);
1878 i++, offset++) {
1879 subseconds[i] = *offset;
1881 for (; i < sizeof(subseconds)/sizeof(char) - 1; i++) {
1882 subseconds[i] = '0';
1884 subseconds[6] = '\0';
1886 /* Convert it into integer */
1887 (*time)->tv_usec = strtol(subseconds, &endptr, 10);
1888 if (*endptr != '\0' || (*time)->tv_usec == LONG_MIN ||
1889 (*time)->tv_usec == LONG_MAX) {
1890 free(*time); *time = NULL;
1891 return IE_DATE;
1894 /* move to the zone offset delimiter */
1895 delim = strchr(offset, '-');
1896 if (!delim)
1897 delim = strchr(offset, '+');
1898 offset = delim;
1901 /* Get zone offset */
1902 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
1903 * "" equals to "Z" and it means UTC zone. */
1904 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
1905 * colon separator */
1906 if (*offset == '-' || *offset == '+') {
1907 offset++;
1908 if (2 != sscanf(offset, "%2d:%2d", &offset_hours, &offset_minutes)) {
1909 free(*time); *time = NULL;
1910 return IE_DATE;
1912 broken.tm_hour -= offset_hours;
1913 broken.tm_min -= offset_minutes * ((offset_hours<0) ? -1 : 1);
1916 /* Convert to time_t */
1917 _isds_switch_tz_to_utc();
1918 (*time)->tv_sec = mktime(&broken);
1919 _isds_switch_tz_to_native();
1920 if ((*time)->tv_sec == (time_t) -1) {
1921 free(*time); *time = NULL;
1922 return IE_DATE;
1925 return IE_SUCCESS;
1929 /* Convert unsigned int into isds_message_status.
1930 * @context is session context
1931 * @number is pointer to number value. NULL will be treated as invalid value.
1932 * @status is automatically reallocated status
1933 * @return IE_SUCCESS, or error code and free status */
1934 static isds_error uint2isds_message_status(struct isds_ctx *context,
1935 const unsigned long int *number, isds_message_status **status) {
1936 if (!context) return IE_INVALID_CONTEXT;
1937 if (!status) return IE_INVAL;
1939 free(*status); *status = NULL;
1940 if (!number) return IE_INVAL;
1942 if (*number < 1 || *number > 10) {
1943 isds_printf_message(context, _("Invalid message status value: %lu"),
1944 *number);
1945 return IE_ENUM;
1948 *status = malloc(sizeof(**status));
1949 if (!*status) return IE_NOMEM;
1951 **status = 1 << *number;
1952 return IE_SUCCESS;
1956 /* Convert event description string into isds_event members type and
1957 * description
1958 * @string is raw event description starting with event prefix
1959 * @event is structure where to store type and stripped description to
1960 * @return standard error code, unknown prefix is not classified as an error.
1961 * */
1962 static isds_error eventstring2event(const xmlChar *string,
1963 struct isds_event* event) {
1964 const xmlChar *known_prefixes[] = {
1965 BAD_CAST "EV1:",
1966 BAD_CAST "EV2:",
1967 BAD_CAST "EV3:",
1968 BAD_CAST "EV4:"
1970 const isds_event_type types[] = {
1971 EVENT_ACCEPTED_BY_RECIPIENT,
1972 EVENT_ACCEPTED_BY_FICTION,
1973 EVENT_UNDELIVERABLE,
1974 EVENT_COMMERCIAL_ACCEPTED
1976 unsigned int index;
1977 size_t length;
1979 if (!string || !event) return IE_INVAL;
1981 if (!event->type) {
1982 event->type = malloc(sizeof(*event->type));
1983 if (!(event->type)) return IE_NOMEM;
1985 zfree(event->description);
1987 for (index = 0; index < sizeof(known_prefixes)/sizeof(known_prefixes[0]);
1988 index++) {
1989 length = xmlUTF8Strlen(known_prefixes[index]);
1991 if (!xmlStrncmp(string, known_prefixes[index], length)) {
1992 /* Prefix is known */
1993 *event->type = types[index];
1995 /* Strip prefix from description and spaces */
1996 /* TODO: Recognize all white spaces from UCS blank class and
1997 * operate on UTF-8 chars. */
1998 for (; string[length] != '\0' && string[length] == ' '; length++);
1999 event->description = strdup((char *) (string + length));
2000 if (!(event->description)) return IE_NOMEM;
2002 return IE_SUCCESS;
2006 /* Unknown event prefix.
2007 * XSD allows any string */
2008 char *string_locale = _isds_utf82locale((char *) string);
2009 isds_log(ILF_ISDS, ILL_WARNING,
2010 _("Unknown delivery info event prefix: %s\n"), string_locale);
2011 free(string_locale);
2013 *event->type = EVENT_UKNOWN;
2014 event->description = strdup((char *) string);
2015 if (!(event->description)) return IE_NOMEM;
2017 return IE_SUCCESS;
2021 /* Following EXTRACT_* macros expects @result, @xpath_ctx, @err, @context
2022 * and leave label */
2023 #define EXTRACT_STRING(element, string) { \
2024 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2025 if (!result) { \
2026 err = IE_ERROR; \
2027 goto leave; \
2029 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2030 if (result->nodesetval->nodeNr > 1) { \
2031 isds_printf_message(context, _("Multiple %s element"), element); \
2032 err = IE_ERROR; \
2033 goto leave; \
2035 (string) = (char *) \
2036 xmlXPathCastNodeSetToString(result->nodesetval); \
2037 if (!(string)) { \
2038 err = IE_ERROR; \
2039 goto leave; \
2044 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2046 char *string = NULL; \
2047 EXTRACT_STRING(element, string); \
2049 if (string) { \
2050 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2051 if (!(booleanPtr)) { \
2052 free(string); \
2053 err = IE_NOMEM; \
2054 goto leave; \
2057 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2058 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2059 *(booleanPtr) = 1; \
2060 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2061 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2062 *(booleanPtr) = 0; \
2063 else { \
2064 char *string_locale = _isds_utf82locale((char*)string); \
2065 isds_printf_message(context, \
2066 _("%s value is not valid boolean: %s"), \
2067 element, string_locale); \
2068 free(string_locale); \
2069 free(string); \
2070 err = IE_ERROR; \
2071 goto leave; \
2074 free(string); \
2078 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2080 char *string = NULL; \
2081 EXTRACT_STRING(element, string); \
2082 if (string) { \
2083 long int number; \
2084 char *endptr; \
2086 number = strtol((char*)string, &endptr, 10); \
2088 if (*endptr != '\0') { \
2089 char *string_locale = _isds_utf82locale((char *)string); \
2090 isds_printf_message(context, \
2091 _("%s is not valid integer: %s"), \
2092 element, string_locale); \
2093 free(string_locale); \
2094 free(string); \
2095 err = IE_ISDS; \
2096 goto leave; \
2099 if (number == LONG_MIN || number == LONG_MAX) { \
2100 char *string_locale = _isds_utf82locale((char *)string); \
2101 isds_printf_message(context, \
2102 _("%s value out of range of long int: %s"), \
2103 element, string_locale); \
2104 free(string_locale); \
2105 free(string); \
2106 err = IE_ERROR; \
2107 goto leave; \
2110 free(string); string = NULL; \
2112 if (!(preallocated)) { \
2113 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2114 if (!(longintPtr)) { \
2115 err = IE_NOMEM; \
2116 goto leave; \
2119 *(longintPtr) = number; \
2123 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2125 char *string = NULL; \
2126 EXTRACT_STRING(element, string); \
2127 if (string) { \
2128 long int number; \
2129 char *endptr; \
2131 number = strtol((char*)string, &endptr, 10); \
2133 if (*endptr != '\0') { \
2134 char *string_locale = _isds_utf82locale((char *)string); \
2135 isds_printf_message(context, \
2136 _("%s is not valid integer: %s"), \
2137 element, string_locale); \
2138 free(string_locale); \
2139 free(string); \
2140 err = IE_ISDS; \
2141 goto leave; \
2144 if (number == LONG_MIN || number == LONG_MAX) { \
2145 char *string_locale = _isds_utf82locale((char *)string); \
2146 isds_printf_message(context, \
2147 _("%s value out of range of long int: %s"), \
2148 element, string_locale); \
2149 free(string_locale); \
2150 free(string); \
2151 err = IE_ERROR; \
2152 goto leave; \
2155 free(string); string = NULL; \
2156 if (number < 0) { \
2157 isds_printf_message(context, \
2158 _("%s value is negative: %ld"), element, number); \
2159 err = IE_ERROR; \
2160 goto leave; \
2163 if (!(preallocated)) { \
2164 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2165 if (!(ulongintPtr)) { \
2166 err = IE_NOMEM; \
2167 goto leave; \
2170 *(ulongintPtr) = number; \
2174 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2175 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2176 NULL); \
2177 if ((required) && (!string)) { \
2178 char *attribute_locale = _isds_utf82locale(attribute); \
2179 char *element_locale = \
2180 _isds_utf82locale((char *)xpath_ctx->node->name); \
2181 isds_printf_message(context, \
2182 _("Could not extract required %s attribute value from " \
2183 "%s element"), attribute_locale, element_locale); \
2184 free(element_locale); \
2185 free(attribute_locale); \
2186 err = IE_ERROR; \
2187 goto leave; \
2192 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2194 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2195 (xmlChar *) (string)); \
2196 if (!node) { \
2197 isds_printf_message(context, \
2198 _("Could not add %s child to %s element"), \
2199 element, (parent)->name); \
2200 err = IE_ERROR; \
2201 goto leave; \
2205 #define INSERT_STRING(parent, element, string) \
2206 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2208 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2210 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2211 else { INSERT_STRING(parent, element, "false"); } \
2214 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2216 if (booleanPtr) { \
2217 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2218 } else { \
2219 INSERT_STRING(parent, element, NULL); \
2223 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2224 if ((longintPtr)) { \
2225 /* FIXME: locale sensitive */ \
2226 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2227 err = IE_NOMEM; \
2228 goto leave; \
2230 INSERT_STRING(parent, element, buffer) \
2231 free(buffer); (buffer) = NULL; \
2232 } else { INSERT_STRING(parent, element, NULL) } \
2235 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2236 if ((ulongintPtr)) { \
2237 /* FIXME: locale sensitive */ \
2238 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2239 err = IE_NOMEM; \
2240 goto leave; \
2242 INSERT_STRING(parent, element, buffer) \
2243 free(buffer); (buffer) = NULL; \
2244 } else { INSERT_STRING(parent, element, NULL) } \
2247 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2249 /* FIXME: locale sensitive */ \
2250 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2251 err = IE_NOMEM; \
2252 goto leave; \
2254 INSERT_STRING(parent, element, buffer) \
2255 free(buffer); (buffer) = NULL; \
2258 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2259 * new attribute. */
2260 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2262 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2263 (xmlChar *) (string)); \
2264 if (!attribute_node) { \
2265 isds_printf_message(context, _("Could not add %s " \
2266 "attribute to %s element"), \
2267 (attribute), (parent)->name); \
2268 err = IE_ERROR; \
2269 goto leave; \
2273 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2274 if (string) { \
2275 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2276 if (length > (maximum)) { \
2277 isds_printf_message(context, \
2278 ngettext("%s has more than %d characters", \
2279 "%s has more than %d characters", (maximum)), \
2280 (name), (maximum)); \
2281 err = IE_2BIG; \
2282 goto leave; \
2284 if (length < (minimum)) { \
2285 isds_printf_message(context, \
2286 ngettext("%s has less than %d characters", \
2287 "%s has less than %d characters", (minimum)), \
2288 (name), (minimum)); \
2289 err = IE_2SMALL; \
2290 goto leave; \
2295 #define INSERT_ELEMENT(child, parent, element) \
2297 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2298 if (!(child)) { \
2299 isds_printf_message(context, \
2300 _("Could not add %s child to %s element"), \
2301 (element), (parent)->name); \
2302 err = IE_ERROR; \
2303 goto leave; \
2308 /* Find child element by name in given XPath context and switch context onto
2309 * it. The child must be uniq and must exist. Otherwise fails.
2310 * @context is ISDS context
2311 * @child is child element name
2312 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2313 * into it child. In error case, the @xpath_ctx keeps original value. */
2314 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
2315 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
2316 isds_error err = IE_SUCCESS;
2317 xmlXPathObjectPtr result = NULL;
2319 if (!context) return IE_INVALID_CONTEXT;
2320 if (!child || !xpath_ctx) return IE_INVAL;
2322 /* Find child */
2323 result = xmlXPathEvalExpression(child, xpath_ctx);
2324 if (!result) {
2325 err = IE_XML;
2326 goto leave;
2329 /* No match */
2330 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2331 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2332 char *child_locale = _isds_utf82locale((char*) child);
2333 isds_printf_message(context,
2334 _("%s element does not contain %s child"),
2335 parent_locale, child_locale);
2336 free(child_locale);
2337 free(parent_locale);
2338 err = IE_NOEXIST;
2339 goto leave;
2342 /* More matches */
2343 if (result->nodesetval->nodeNr > 1) {
2344 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2345 char *child_locale = _isds_utf82locale((char*) child);
2346 isds_printf_message(context,
2347 _("%s element contains multiple %s children"),
2348 parent_locale, child_locale);
2349 free(child_locale);
2350 free(parent_locale);
2351 err = IE_NOTUNIQ;
2352 goto leave;
2355 /* Switch context */
2356 xpath_ctx->node = result->nodesetval->nodeTab[0];
2358 leave:
2359 xmlXPathFreeObject(result);
2360 return err;
2365 /* Find and convert XSD:gPersonName group in current node into structure
2366 * @context is ISDS context
2367 * @personName is automatically reallocated person name structure. If no member
2368 * value is found, will be freed.
2369 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2370 * elements
2371 * In case of error @personName will be freed. */
2372 static isds_error extract_gPersonName(struct isds_ctx *context,
2373 struct isds_PersonName **personName, xmlXPathContextPtr xpath_ctx) {
2374 isds_error err = IE_SUCCESS;
2375 xmlXPathObjectPtr result = NULL;
2377 if (!context) return IE_INVALID_CONTEXT;
2378 if (!personName) return IE_INVAL;
2379 isds_PersonName_free(personName);
2380 if (!xpath_ctx) return IE_INVAL;
2383 *personName = calloc(1, sizeof(**personName));
2384 if (!*personName) {
2385 err = IE_NOMEM;
2386 goto leave;
2389 EXTRACT_STRING("isds:pnFirstName", (*personName)->pnFirstName);
2390 EXTRACT_STRING("isds:pnMiddleName", (*personName)->pnMiddleName);
2391 EXTRACT_STRING("isds:pnLastName", (*personName)->pnLastName);
2392 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName)->pnLastNameAtBirth);
2394 if (!(*personName)->pnFirstName && !(*personName)->pnMiddleName &&
2395 !(*personName)->pnLastName && !(*personName)->pnLastNameAtBirth)
2396 isds_PersonName_free(personName);
2398 leave:
2399 if (err) isds_PersonName_free(personName);
2400 xmlXPathFreeObject(result);
2401 return err;
2405 /* Find and convert XSD:gAddress group in current node into structure
2406 * @context is ISDS context
2407 * @address is automatically reallocated address structure. If no member
2408 * value is found, will be freed.
2409 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2410 * elements
2411 * In case of error @address will be freed. */
2412 static isds_error extract_gAddress(struct isds_ctx *context,
2413 struct isds_Address **address, xmlXPathContextPtr xpath_ctx) {
2414 isds_error err = IE_SUCCESS;
2415 xmlXPathObjectPtr result = NULL;
2417 if (!context) return IE_INVALID_CONTEXT;
2418 if (!address) return IE_INVAL;
2419 isds_Address_free(address);
2420 if (!xpath_ctx) return IE_INVAL;
2423 *address = calloc(1, sizeof(**address));
2424 if (!*address) {
2425 err = IE_NOMEM;
2426 goto leave;
2429 EXTRACT_STRING("isds:adCity", (*address)->adCity);
2430 EXTRACT_STRING("isds:adStreet", (*address)->adStreet);
2431 EXTRACT_STRING("isds:adNumberInStreet", (*address)->adNumberInStreet);
2432 EXTRACT_STRING("isds:adNumberInMunicipality",
2433 (*address)->adNumberInMunicipality);
2434 EXTRACT_STRING("isds:adZipCode", (*address)->adZipCode);
2435 EXTRACT_STRING("isds:adState", (*address)->adState);
2437 if (!(*address)->adCity && !(*address)->adStreet &&
2438 !(*address)->adNumberInStreet &&
2439 !(*address)->adNumberInMunicipality &&
2440 !(*address)->adZipCode && !(*address)->adState)
2441 isds_Address_free(address);
2443 leave:
2444 if (err) isds_Address_free(address);
2445 xmlXPathFreeObject(result);
2446 return err;
2450 /* Find and convert isds:biDate element in current node into structure
2451 * @context is ISDS context
2452 * @biDate is automatically reallocated birth date structure. If no member
2453 * value is found, will be freed.
2454 * @xpath_ctx is XPath context with current node as parent for isds:biDate
2455 * element
2456 * In case of error @biDate will be freed. */
2457 static isds_error extract_BiDate(struct isds_ctx *context,
2458 struct tm **biDate, xmlXPathContextPtr xpath_ctx) {
2459 isds_error err = IE_SUCCESS;
2460 xmlXPathObjectPtr result = NULL;
2461 char *string = NULL;
2463 if (!context) return IE_INVALID_CONTEXT;
2464 if (!biDate) return IE_INVAL;
2465 zfree(*biDate);
2466 if (!xpath_ctx) return IE_INVAL;
2468 EXTRACT_STRING("isds:biDate", string);
2469 if (string) {
2470 *biDate = calloc(1, sizeof(**biDate));
2471 if (!*biDate) {
2472 err = IE_NOMEM;
2473 goto leave;
2475 err = datestring2tm((xmlChar *)string, *biDate);
2476 if (err) {
2477 if (err == IE_NOTSUP) {
2478 err = IE_ISDS;
2479 char *string_locale = _isds_utf82locale(string);
2480 isds_printf_message(context,
2481 _("Invalid isds:biDate value: %s"), string_locale);
2482 free(string_locale);
2484 goto leave;
2488 leave:
2489 if (err) zfree(*biDate);
2490 free(string);
2491 xmlXPathFreeObject(result);
2492 return err;
2496 /* Convert isds:dBOwnerInfo XML tree into structure
2497 * @context is ISDS context
2498 * @db_owner_info is automatically reallocated box owner info structure
2499 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
2500 * In case of error @db_owner_info will be freed. */
2501 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
2502 struct isds_DbOwnerInfo **db_owner_info,
2503 xmlXPathContextPtr xpath_ctx) {
2504 isds_error err = IE_SUCCESS;
2505 xmlXPathObjectPtr result = NULL;
2506 char *string = NULL;
2508 if (!context) return IE_INVALID_CONTEXT;
2509 if (!db_owner_info) return IE_INVAL;
2510 isds_DbOwnerInfo_free(db_owner_info);
2511 if (!xpath_ctx) return IE_INVAL;
2514 *db_owner_info = calloc(1, sizeof(**db_owner_info));
2515 if (!*db_owner_info) {
2516 err = IE_NOMEM;
2517 goto leave;
2520 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
2522 EXTRACT_STRING("isds:dbType", string);
2523 if (string) {
2524 (*db_owner_info)->dbType =
2525 calloc(1, sizeof(*((*db_owner_info)->dbType)));
2526 if (!(*db_owner_info)->dbType) {
2527 err = IE_NOMEM;
2528 goto leave;
2530 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
2531 if (err) {
2532 zfree((*db_owner_info)->dbType);
2533 if (err == IE_ENUM) {
2534 err = IE_ISDS;
2535 char *string_locale = _isds_utf82locale(string);
2536 isds_printf_message(context, _("Unknown isds:dbType: %s"),
2537 string_locale);
2538 free(string_locale);
2540 goto leave;
2542 zfree(string);
2545 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
2547 err = extract_gPersonName(context, &(*db_owner_info)->personName,
2548 xpath_ctx);
2549 if (err) goto leave;
2551 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
2553 (*db_owner_info)->birthInfo =
2554 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
2555 if (!(*db_owner_info)->birthInfo) {
2556 err = IE_NOMEM;
2557 goto leave;
2559 err = extract_BiDate(context, &(*db_owner_info)->birthInfo->biDate,
2560 xpath_ctx);
2561 if (err) goto leave;
2562 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
2563 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
2564 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
2565 if (!(*db_owner_info)->birthInfo->biDate &&
2566 !(*db_owner_info)->birthInfo->biCity &&
2567 !(*db_owner_info)->birthInfo->biCounty &&
2568 !(*db_owner_info)->birthInfo->biState)
2569 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
2571 err = extract_gAddress(context, &(*db_owner_info)->address, xpath_ctx);
2572 if (err) goto leave;
2574 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
2575 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
2576 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
2577 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
2578 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
2580 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
2582 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
2583 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
2584 (*db_owner_info)->dbOpenAddressing);
2586 leave:
2587 if (err) isds_DbOwnerInfo_free(db_owner_info);
2588 free(string);
2589 xmlXPathFreeObject(result);
2590 return err;
2594 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
2595 * @context is session context
2596 * @owner is libisds structure with box description
2597 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
2598 static isds_error insert_DbOwnerInfo(struct isds_ctx *context,
2599 const struct isds_DbOwnerInfo *owner, xmlNodePtr db_owner_info) {
2601 isds_error err = IE_SUCCESS;
2602 xmlNodePtr node;
2603 xmlChar *string = NULL;
2605 if (!context) return IE_INVALID_CONTEXT;
2606 if (!owner || !db_owner_info) return IE_INVAL;
2609 /* Build XSD:tDbOwnerInfo */
2610 CHECK_FOR_STRING_LENGTH(owner->dbID, 0, 7, "dbID")
2611 INSERT_STRING(db_owner_info, "dbID", owner->dbID);
2613 /* dbType */
2614 if (owner->dbType) {
2615 const xmlChar *type_string = isds_DbType2string(*(owner->dbType));
2616 if (!type_string) {
2617 isds_printf_message(context, _("Invalid dbType value: %d"),
2618 *(owner->dbType));
2619 err = IE_ENUM;
2620 goto leave;
2622 INSERT_STRING(db_owner_info, "dbType", type_string);
2624 INSERT_STRING(db_owner_info, "ic", owner->ic);
2625 if (owner->personName) {
2626 INSERT_STRING(db_owner_info, "pnFirstName",
2627 owner->personName->pnFirstName);
2628 INSERT_STRING(db_owner_info, "pnMiddleName",
2629 owner->personName->pnMiddleName);
2630 INSERT_STRING(db_owner_info, "pnLastName",
2631 owner->personName->pnLastName);
2632 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
2633 owner->personName->pnLastNameAtBirth);
2635 INSERT_STRING(db_owner_info, "firmName", owner->firmName);
2636 if (owner->birthInfo) {
2637 if (owner->birthInfo->biDate) {
2638 if (!tm2datestring(owner->birthInfo->biDate, &string))
2639 INSERT_STRING(db_owner_info, "biDate", string);
2640 free(string); string = NULL;
2642 INSERT_STRING(db_owner_info, "biCity", owner->birthInfo->biCity);
2643 INSERT_STRING(db_owner_info, "biCounty", owner->birthInfo->biCounty);
2644 INSERT_STRING(db_owner_info, "biState", owner->birthInfo->biState);
2646 if (owner->address) {
2647 INSERT_STRING(db_owner_info, "adCity", owner->address->adCity);
2648 INSERT_STRING(db_owner_info, "adStreet", owner->address->adStreet);
2649 INSERT_STRING(db_owner_info, "adNumberInStreet",
2650 owner->address->adNumberInStreet);
2651 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
2652 owner->address->adNumberInMunicipality);
2653 INSERT_STRING(db_owner_info, "adZipCode", owner->address->adZipCode);
2654 INSERT_STRING(db_owner_info, "adState", owner->address->adState);
2656 INSERT_STRING(db_owner_info, "nationality", owner->nationality);
2657 INSERT_STRING(db_owner_info, "email", owner->email);
2658 INSERT_STRING(db_owner_info, "telNumber", owner->telNumber);
2660 CHECK_FOR_STRING_LENGTH(owner->identifier, 0, 20, "identifier")
2661 INSERT_STRING(db_owner_info, "identifier", owner->identifier);
2663 CHECK_FOR_STRING_LENGTH(owner->registryCode, 0, 5, "registryCode")
2664 INSERT_STRING(db_owner_info, "registryCode", owner->registryCode);
2666 INSERT_LONGINT(db_owner_info, "dbState", owner->dbState, string);
2668 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", owner->dbEffectiveOVM);
2669 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
2670 owner->dbOpenAddressing);
2672 leave:
2673 free(string);
2674 return err;
2678 /* Convert XSD:tDbUserInfo XML tree into structure
2679 * @context is ISDS context
2680 * @db_user_info is automatically reallocated user info structure
2681 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
2682 * In case of error @db_user_info will be freed. */
2683 static isds_error extract_DbUserInfo(struct isds_ctx *context,
2684 struct isds_DbUserInfo **db_user_info, xmlXPathContextPtr xpath_ctx) {
2685 isds_error err = IE_SUCCESS;
2686 xmlXPathObjectPtr result = NULL;
2687 char *string = NULL;
2689 if (!context) return IE_INVALID_CONTEXT;
2690 if (!db_user_info) return IE_INVAL;
2691 isds_DbUserInfo_free(db_user_info);
2692 if (!xpath_ctx) return IE_INVAL;
2695 *db_user_info = calloc(1, sizeof(**db_user_info));
2696 if (!*db_user_info) {
2697 err = IE_NOMEM;
2698 goto leave;
2701 EXTRACT_STRING("isds:userID", (*db_user_info)->userID);
2703 EXTRACT_STRING("isds:userType", string);
2704 if (string) {
2705 (*db_user_info)->userType =
2706 calloc(1, sizeof(*((*db_user_info)->userType)));
2707 if (!(*db_user_info)->userType) {
2708 err = IE_NOMEM;
2709 goto leave;
2711 err = string2isds_UserType((xmlChar *)string,
2712 (*db_user_info)->userType);
2713 if (err) {
2714 zfree((*db_user_info)->userType);
2715 if (err == IE_ENUM) {
2716 err = IE_ISDS;
2717 char *string_locale = _isds_utf82locale(string);
2718 isds_printf_message(context,
2719 _("Unknown isds:userType value: %s"), string_locale);
2720 free(string_locale);
2722 goto leave;
2724 zfree(string);
2727 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info)->userPrivils, 0);
2729 (*db_user_info)->personName =
2730 calloc(1, sizeof(*((*db_user_info)->personName)));
2731 if (!(*db_user_info)->personName) {
2732 err = IE_NOMEM;
2733 goto leave;
2736 err = extract_gPersonName(context, &(*db_user_info)->personName,
2737 xpath_ctx);
2738 if (err) goto leave;
2740 err = extract_gAddress(context, &(*db_user_info)->address, xpath_ctx);
2741 if (err) goto leave;
2743 err = extract_BiDate(context, &(*db_user_info)->biDate, xpath_ctx);
2744 if (err) goto leave;
2746 EXTRACT_STRING("isds:ic", (*db_user_info)->ic);
2747 EXTRACT_STRING("isds:firmName", (*db_user_info)->firmName);
2749 EXTRACT_STRING("isds:caStreet", (*db_user_info)->caStreet);
2750 EXTRACT_STRING("isds:caCity", (*db_user_info)->caCity);
2751 EXTRACT_STRING("isds:caZipCode", (*db_user_info)->caZipCode);
2753 /* ???: Default value is "CZ" according specification. Should we provide
2754 * it? */
2755 EXTRACT_STRING("isds:caState", (*db_user_info)->caState);
2757 leave:
2758 if (err) isds_DbUserInfo_free(db_user_info);
2759 free(string);
2760 xmlXPathFreeObject(result);
2761 return err;
2765 /* Insert struct isds_DbUserInfo data (user description) into XML tree
2766 * @context is session context
2767 * @user is libisds structure with user description
2768 * @db_user_info is XML element of XSD:tDbUserInfo */
2769 static isds_error insert_DbUserInfo(struct isds_ctx *context,
2770 const struct isds_DbUserInfo *user, xmlNodePtr db_user_info) {
2772 isds_error err = IE_SUCCESS;
2773 xmlNodePtr node;
2774 xmlChar *string = NULL;
2776 if (!context) return IE_INVALID_CONTEXT;
2777 if (!user || !db_user_info) return IE_INVAL;
2779 /* Build XSD:tDbUserInfo */
2780 if (user->personName) {
2781 INSERT_STRING(db_user_info, "pnFirstName",
2782 user->personName->pnFirstName);
2783 INSERT_STRING(db_user_info, "pnMiddleName",
2784 user->personName->pnMiddleName);
2785 INSERT_STRING(db_user_info, "pnLastName",
2786 user->personName->pnLastName);
2787 INSERT_STRING(db_user_info, "pnLastNameAtBirth",
2788 user->personName->pnLastNameAtBirth);
2790 if (user->address) {
2791 INSERT_STRING(db_user_info, "adCity", user->address->adCity);
2792 INSERT_STRING(db_user_info, "adStreet", user->address->adStreet);
2793 INSERT_STRING(db_user_info, "adNumberInStreet",
2794 user->address->adNumberInStreet);
2795 INSERT_STRING(db_user_info, "adNumberInMunicipality",
2796 user->address->adNumberInMunicipality);
2797 INSERT_STRING(db_user_info, "adZipCode", user->address->adZipCode);
2798 INSERT_STRING(db_user_info, "adState", user->address->adState);
2800 if (user->biDate) {
2801 if (!tm2datestring(user->biDate, &string))
2802 INSERT_STRING(db_user_info, "biDate", string);
2803 zfree(string);
2805 CHECK_FOR_STRING_LENGTH(user->userID, 6, 12, "userID");
2806 INSERT_STRING(db_user_info, "userID", user->userID);
2808 /* userType */
2809 if (user->userType) {
2810 const xmlChar *type_string = isds_UserType2string(*(user->userType));
2811 if (!type_string) {
2812 isds_printf_message(context, _("Invalid userType value: %d"),
2813 *(user->userType));
2814 err = IE_ENUM;
2815 goto leave;
2817 INSERT_STRING(db_user_info, "userType", type_string);
2820 INSERT_LONGINT(db_user_info, "userPrivils", user->userPrivils, string);
2821 CHECK_FOR_STRING_LENGTH(user->ic, 0, 8, "ic")
2822 INSERT_STRING(db_user_info, "ic", user->ic);
2823 CHECK_FOR_STRING_LENGTH(user->firmName, 0, 100, "firmName")
2824 INSERT_STRING(db_user_info, "firmName", user->firmName);
2825 INSERT_STRING(db_user_info, "caStreet", user->caStreet);
2826 INSERT_STRING(db_user_info, "caCity", user->caCity);
2827 INSERT_STRING(db_user_info, "caZipCode", user->caZipCode);
2828 INSERT_STRING(db_user_info, "caState", user->caState);
2830 leave:
2831 free(string);
2832 return err;
2836 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
2837 * isds_envelope structure. The envelope is automatically allocated but not
2838 * reallocated. The date are just appended into envelope structure.
2839 * @context is ISDS context
2840 * @envelope is automatically allocated message envelope structure
2841 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
2842 * In case of error @envelope will be freed. */
2843 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
2844 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2845 isds_error err = IE_SUCCESS;
2846 xmlXPathObjectPtr result = NULL;
2848 if (!context) return IE_INVALID_CONTEXT;
2849 if (!envelope) return IE_INVAL;
2850 if (!xpath_ctx) return IE_INVAL;
2853 if (!*envelope) {
2854 /* Allocate envelope */
2855 *envelope = calloc(1, sizeof(**envelope));
2856 if (!*envelope) {
2857 err = IE_NOMEM;
2858 goto leave;
2860 } else {
2861 /* Else free former data */
2862 zfree((*envelope)->dmSenderOrgUnit);
2863 zfree((*envelope)->dmSenderOrgUnitNum);
2864 zfree((*envelope)->dbIDRecipient);
2865 zfree((*envelope)->dmRecipientOrgUnit);
2866 zfree((*envelope)->dmSenderOrgUnitNum);
2867 zfree((*envelope)->dmToHands);
2868 zfree((*envelope)->dmAnnotation);
2869 zfree((*envelope)->dmRecipientRefNumber);
2870 zfree((*envelope)->dmSenderRefNumber);
2871 zfree((*envelope)->dmRecipientIdent);
2872 zfree((*envelope)->dmSenderIdent);
2873 zfree((*envelope)->dmLegalTitleLaw);
2874 zfree((*envelope)->dmLegalTitleYear);
2875 zfree((*envelope)->dmLegalTitleSect);
2876 zfree((*envelope)->dmLegalTitlePar);
2877 zfree((*envelope)->dmLegalTitlePoint);
2878 zfree((*envelope)->dmPersonalDelivery);
2879 zfree((*envelope)->dmAllowSubstDelivery);
2882 /* Extract envelope elements added by sender or ISDS
2883 * (XSD: gMessageEnvelopeSub type) */
2884 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
2885 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
2886 (*envelope)->dmSenderOrgUnitNum, 0);
2887 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
2888 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
2889 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
2890 (*envelope)->dmSenderOrgUnitNum, 0);
2891 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
2892 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
2893 EXTRACT_STRING("isds:dmRecipientRefNumber",
2894 (*envelope)->dmRecipientRefNumber);
2895 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
2896 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
2897 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
2899 /* Extract envelope elements regarding law reference */
2900 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
2901 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
2902 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
2903 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
2904 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
2906 /* Extract envelope other elements */
2907 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
2908 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
2909 (*envelope)->dmAllowSubstDelivery);
2911 leave:
2912 if (err) isds_envelope_free(envelope);
2913 xmlXPathFreeObject(result);
2914 return err;
2919 /* Convert XSD gMessageEnvelope group of elements from XML tree into
2920 * isds_envelope structure. The envelope is automatically allocated but not
2921 * reallocated. The date are just appended into envelope structure.
2922 * @context is ISDS context
2923 * @envelope is automatically allocated message envelope structure
2924 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
2925 * In case of error @envelope will be freed. */
2926 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
2927 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2928 isds_error err = IE_SUCCESS;
2929 xmlXPathObjectPtr result = NULL;
2931 if (!context) return IE_INVALID_CONTEXT;
2932 if (!envelope) return IE_INVAL;
2933 if (!xpath_ctx) return IE_INVAL;
2936 if (!*envelope) {
2937 /* Allocate envelope */
2938 *envelope = calloc(1, sizeof(**envelope));
2939 if (!*envelope) {
2940 err = IE_NOMEM;
2941 goto leave;
2943 } else {
2944 /* Else free former data */
2945 zfree((*envelope)->dmID);
2946 zfree((*envelope)->dbIDSender);
2947 zfree((*envelope)->dmSender);
2948 zfree((*envelope)->dmSenderAddress);
2949 zfree((*envelope)->dmSenderType);
2950 zfree((*envelope)->dmRecipient);
2951 zfree((*envelope)->dmRecipientAddress);
2952 zfree((*envelope)->dmAmbiguousRecipient);
2955 /* Extract envelope elements added by ISDS
2956 * (XSD: gMessageEnvelope type) */
2957 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
2958 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
2959 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
2960 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
2961 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
2962 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
2963 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
2964 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
2965 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
2966 (*envelope)->dmAmbiguousRecipient);
2968 /* Extract envelope elements added by sender and ISDS
2969 * (XSD: gMessageEnvelope type) */
2970 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
2971 if (err) goto leave;
2973 leave:
2974 if (err) isds_envelope_free(envelope);
2975 xmlXPathFreeObject(result);
2976 return err;
2980 /* Convert other envelope elements from XML tree into isds_envelope structure:
2981 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
2982 * The envelope is automatically allocated but not reallocated.
2983 * The data are just appended into envelope structure.
2984 * @context is ISDS context
2985 * @envelope is automatically allocated message envelope structure
2986 * @xpath_ctx is XPath context with current node as parent desired elements
2987 * In case of error @envelope will be freed. */
2988 static isds_error append_status_size_times(struct isds_ctx *context,
2989 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2990 isds_error err = IE_SUCCESS;
2991 xmlXPathObjectPtr result = NULL;
2992 char *string = NULL;
2993 unsigned long int *unumber = NULL;
2995 if (!context) return IE_INVALID_CONTEXT;
2996 if (!envelope) return IE_INVAL;
2997 if (!xpath_ctx) return IE_INVAL;
3000 if (!*envelope) {
3001 /* Allocate new */
3002 *envelope = calloc(1, sizeof(**envelope));
3003 if (!*envelope) {
3004 err = IE_NOMEM;
3005 goto leave;
3007 } else {
3008 /* Free old data */
3009 zfree((*envelope)->dmMessageStatus);
3010 zfree((*envelope)->dmAttachmentSize);
3011 zfree((*envelope)->dmDeliveryTime);
3012 zfree((*envelope)->dmAcceptanceTime);
3016 /* dmMessageStatus element is mandatory */
3017 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
3018 if (!unumber) {
3019 isds_log_message(context,
3020 _("Missing mandatory sisds:dmMessageStatus integer"));
3021 err = IE_ISDS;
3022 goto leave;
3024 err = uint2isds_message_status(context, unumber,
3025 &((*envelope)->dmMessageStatus));
3026 if (err) {
3027 if (err == IE_ENUM) err = IE_ISDS;
3028 goto leave;
3030 free(unumber); unumber = NULL;
3032 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
3035 EXTRACT_STRING("sisds:dmDeliveryTime", string);
3036 if (string) {
3037 err = timestring2timeval((xmlChar *) string,
3038 &((*envelope)->dmDeliveryTime));
3039 if (err) {
3040 char *string_locale = _isds_utf82locale(string);
3041 if (err == IE_DATE) err = IE_ISDS;
3042 isds_printf_message(context,
3043 _("Could not convert dmDeliveryTime as ISO time: %s"),
3044 string_locale);
3045 free(string_locale);
3046 goto leave;
3048 zfree(string);
3051 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
3052 if (string) {
3053 err = timestring2timeval((xmlChar *) string,
3054 &((*envelope)->dmAcceptanceTime));
3055 if (err) {
3056 char *string_locale = _isds_utf82locale(string);
3057 if (err == IE_DATE) err = IE_ISDS;
3058 isds_printf_message(context,
3059 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3060 string_locale);
3061 free(string_locale);
3062 goto leave;
3064 zfree(string);
3067 leave:
3068 if (err) isds_envelope_free(envelope);
3069 free(unumber);
3070 free(string);
3071 xmlXPathFreeObject(result);
3072 return err;
3076 /* Convert message type attribute of current element into isds_envelope
3077 * structure.
3078 * TODO: This function can be incorporated into append_status_size_times() as
3079 * they are called always together.
3080 * The envelope is automatically allocated but not reallocated.
3081 * The data are just appended into envelope structure.
3082 * @context is ISDS context
3083 * @envelope is automatically allocated message envelope structure
3084 * @xpath_ctx is XPath context with current node as parent of attribute
3085 * carrying message type
3086 * In case of error @envelope will be freed. */
3087 static isds_error append_message_type(struct isds_ctx *context,
3088 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3089 isds_error err = IE_SUCCESS;
3091 if (!context) return IE_INVALID_CONTEXT;
3092 if (!envelope) return IE_INVAL;
3093 if (!xpath_ctx) return IE_INVAL;
3096 if (!*envelope) {
3097 /* Allocate new */
3098 *envelope = calloc(1, sizeof(**envelope));
3099 if (!*envelope) {
3100 err = IE_NOMEM;
3101 goto leave;
3103 } else {
3104 /* Free old data */
3105 zfree((*envelope)->dmType);
3109 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope)->dmType, 0);
3111 if (!(*envelope)->dmType) {
3112 /* Use default value */
3113 (*envelope)->dmType = strdup("V");
3114 if (!(*envelope)->dmType) {
3115 err = IE_NOMEM;
3116 goto leave;
3118 } else if (1 != xmlUTF8Strlen((xmlChar *) (*envelope)->dmType)) {
3119 char *type_locale = _isds_utf82locale((*envelope)->dmType);
3120 isds_printf_message(context,
3121 _("Message type in dmType attribute is not 1 character long: "
3122 "%s"),
3123 type_locale);
3124 free(type_locale);
3125 err = IE_ISDS;
3126 goto leave;
3129 leave:
3130 if (err) isds_envelope_free(envelope);
3131 return err;
3135 /* Convert dmType isds_envelope member into XML attribute and append it to
3136 * current node.
3137 * @context is ISDS context
3138 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3139 * @dm_envelope is XML element the resulting attribute will be appended to.
3140 * @return error code, in case of error context' message is filled. */
3141 static isds_error insert_message_type(struct isds_ctx *context,
3142 const char *type, xmlNodePtr dm_envelope) {
3143 isds_error err = IE_SUCCESS;
3144 xmlAttrPtr attribute_node;
3146 if (!context) return IE_INVALID_CONTEXT;
3147 if (!dm_envelope) return IE_INVAL;
3149 /* Insert optional message type */
3150 if (type) {
3151 if (1 != xmlUTF8Strlen((xmlChar *) type)) {
3152 char *type_locale = _isds_utf82locale(type);
3153 isds_printf_message(context,
3154 _("Message type in envelope is not 1 character long: %s"),
3155 type_locale);
3156 free(type_locale);
3157 err = IE_INVAL;
3158 goto leave;
3160 INSERT_STRING_ATTRIBUTE(dm_envelope, "dmType", type);
3163 leave:
3164 return err;
3168 /* Extract message document into reallocated document structure
3169 * @context is ISDS context
3170 * @document is automatically reallocated message documents structure
3171 * @xpath_ctx is XPath context with current node as isds:dmFile
3172 * In case of error @document will be freed. */
3173 static isds_error extract_document(struct isds_ctx *context,
3174 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
3175 isds_error err = IE_SUCCESS;
3176 xmlXPathObjectPtr result = NULL;
3177 xmlNodePtr file_node = xpath_ctx->node;
3178 char *string = NULL;
3180 if (!context) return IE_INVALID_CONTEXT;
3181 if (!document) return IE_INVAL;
3182 isds_document_free(document);
3183 if (!xpath_ctx) return IE_INVAL;
3185 *document = calloc(1, sizeof(**document));
3186 if (!*document) {
3187 err = IE_NOMEM;
3188 goto leave;
3191 /* Extract document meta data */
3192 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
3193 if (context->normalize_mime_type) {
3194 char *normalized_type =
3195 isds_normalize_mime_type((*document)->dmMimeType);
3196 if (normalized_type && normalized_type != (*document)->dmMimeType) {
3197 char *new_type = strdup(normalized_type);
3198 if (!new_type) {
3199 isds_printf_message(context,
3200 _("Not enough memory to normalize document MIME type"));
3201 err = IE_NOMEM;
3202 goto leave;
3204 free((*document)->dmMimeType);
3205 (*document)->dmMimeType = new_type;
3209 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
3210 err = string2isds_FileMetaType((xmlChar*)string,
3211 &((*document)->dmFileMetaType));
3212 if (err) {
3213 char *meta_type_locale = _isds_utf82locale(string);
3214 isds_printf_message(context,
3215 _("Document has invalid dmFileMetaType attribute value: %s"),
3216 meta_type_locale);
3217 free(meta_type_locale);
3218 err = IE_ISDS;
3219 goto leave;
3221 zfree(string);
3223 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
3224 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
3225 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
3226 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
3229 /* Extract document data.
3230 * Base64 encoded blob or XML subtree must be presented. */
3232 /* Check for dmEncodedContent */
3233 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
3234 xpath_ctx);
3235 if (!result) {
3236 err = IE_XML;
3237 goto leave;
3240 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3241 /* Here we have Base64 blob */
3242 (*document)->is_xml = 0;
3244 if (result->nodesetval->nodeNr > 1) {
3245 isds_printf_message(context,
3246 _("Document has more dmEncodedContent elements"));
3247 err = IE_ISDS;
3248 goto leave;
3251 xmlXPathFreeObject(result); result = NULL;
3252 EXTRACT_STRING("isds:dmEncodedContent", string);
3254 /* Decode non-empty document */
3255 if (string && string[0] != '\0') {
3256 (*document)->data_length =
3257 _isds_b64decode(string, &((*document)->data));
3258 if ((*document)->data_length == (size_t) -1) {
3259 isds_printf_message(context,
3260 _("Error while Base64-decoding document content"));
3261 err = IE_ERROR;
3262 goto leave;
3265 } else {
3266 /* No Base64 blob, try XML document */
3267 xmlXPathFreeObject(result); result = NULL;
3268 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
3269 xpath_ctx);
3270 if (!result) {
3271 err = IE_XML;
3272 goto leave;
3275 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3276 /* Here we have XML document */
3277 (*document)->is_xml = 1;
3279 if (result->nodesetval->nodeNr > 1) {
3280 isds_printf_message(context,
3281 _("Document has more dmXMLContent elements"));
3282 err = IE_ISDS;
3283 goto leave;
3286 /* XXX: We cannot serialize the content simply because:
3287 * - XML document may point out of its scope (e.g. to message
3288 * envelope)
3289 * - isds:dmXMLContent can contain more elements, no element,
3290 * a text node only
3291 * - it's not the XML way
3292 * Thus we provide the only right solution: XML DOM. Let's
3293 * application to cope with this hot potato :) */
3294 (*document)->xml_node_list =
3295 result->nodesetval->nodeTab[0]->children;
3296 } else {
3297 /* No base64 blob, nor XML document */
3298 isds_printf_message(context,
3299 _("Document has no dmEncodedContent, nor dmXMLContent "
3300 "element"));
3301 err = IE_ISDS;
3302 goto leave;
3307 leave:
3308 if (err) isds_document_free(document);
3309 free(string);
3310 xmlXPathFreeObject(result);
3311 xpath_ctx->node = file_node;
3312 return err;
3317 /* Extract message documents into reallocated list of documents
3318 * @context is ISDS context
3319 * @documents is automatically reallocated message documents list structure
3320 * @xpath_ctx is XPath context with current node as XSD tFilesArray
3321 * In case of error @documents will be freed. */
3322 static isds_error extract_documents(struct isds_ctx *context,
3323 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
3324 isds_error err = IE_SUCCESS;
3325 xmlXPathObjectPtr result = NULL;
3326 xmlNodePtr files_node = xpath_ctx->node;
3327 struct isds_list *document, *prev_document;
3329 if (!context) return IE_INVALID_CONTEXT;
3330 if (!documents) return IE_INVAL;
3331 isds_list_free(documents);
3332 if (!xpath_ctx) return IE_INVAL;
3334 /* Find documents */
3335 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
3336 if (!result) {
3337 err = IE_XML;
3338 goto leave;
3341 /* No match */
3342 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3343 isds_printf_message(context,
3344 _("Message does not contain any document"));
3345 err = IE_ISDS;
3346 goto leave;
3350 /* Iterate over documents */
3351 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
3353 /* Allocate and append list item */
3354 document = calloc(1, sizeof(*document));
3355 if (!document) {
3356 err = IE_NOMEM;
3357 goto leave;
3359 document->destructor = (void (*)(void **))isds_document_free;
3360 if (i == 0) *documents = document;
3361 else prev_document->next = document;
3362 prev_document = document;
3364 /* Extract document */
3365 xpath_ctx->node = result->nodesetval->nodeTab[i];
3366 err = extract_document(context,
3367 (struct isds_document **) &(document->data), xpath_ctx);
3368 if (err) goto leave;
3372 leave:
3373 if (err) isds_list_free(documents);
3374 xmlXPathFreeObject(result);
3375 xpath_ctx->node = files_node;
3376 return err;
3380 /* Convert isds:dmRecord XML tree into structure
3381 * @context is ISDS context
3382 * @envelope is automatically reallocated message envelope structure
3383 * @xpath_ctx is XPath context with current node as isds:dmRecord element
3384 * In case of error @envelope will be freed. */
3385 static isds_error extract_DmRecord(struct isds_ctx *context,
3386 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3387 isds_error err = IE_SUCCESS;
3388 xmlXPathObjectPtr result = NULL;
3390 if (!context) return IE_INVALID_CONTEXT;
3391 if (!envelope) return IE_INVAL;
3392 isds_envelope_free(envelope);
3393 if (!xpath_ctx) return IE_INVAL;
3396 *envelope = calloc(1, sizeof(**envelope));
3397 if (!*envelope) {
3398 err = IE_NOMEM;
3399 goto leave;
3403 /* Extract tRecord data */
3404 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
3406 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
3407 * dmAcceptanceTime. */
3408 err = append_status_size_times(context, envelope, xpath_ctx);
3409 if (err) goto leave;
3411 /* Extract envelope elements added by sender and ISDS
3412 * (XSD: gMessageEnvelope type) */
3413 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
3414 if (err) goto leave;
3415 /* dmOVM can not be obtained from ISDS */
3417 /* Get message type */
3418 err = append_message_type(context, envelope, xpath_ctx);
3419 if (err) goto leave;
3422 leave:
3423 if (err) isds_envelope_free(envelope);
3424 xmlXPathFreeObject(result);
3425 return err;
3429 /* Find and convert isds:dmHash XML tree into structure
3430 * @context is ISDS context
3431 * @envelope is automatically reallocated message hash structure
3432 * @xpath_ctx is XPath context with current node containing isds:dmHash child
3433 * In case of error @hash will be freed. */
3434 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
3435 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
3436 isds_error err = IE_SUCCESS;
3437 xmlNodePtr old_ctx_node;
3438 xmlXPathObjectPtr result = NULL;
3439 char *string = NULL;
3441 if (!context) return IE_INVALID_CONTEXT;
3442 if (!hash) return IE_INVAL;
3443 isds_hash_free(hash);
3444 if (!xpath_ctx) return IE_INVAL;
3446 old_ctx_node = xpath_ctx->node;
3448 *hash = calloc(1, sizeof(**hash));
3449 if (!*hash) {
3450 err = IE_NOMEM;
3451 goto leave;
3454 /* Locate dmHash */
3455 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
3456 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
3457 err = IE_ISDS;
3458 goto leave;
3460 if (err) {
3461 err = IE_ERROR;
3462 goto leave;
3465 /* Get hash algorithm */
3466 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
3467 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
3468 if (err) {
3469 if (err == IE_ENUM) {
3470 char *string_locale = _isds_utf82locale(string);
3471 isds_printf_message(context, _("Unsupported hash algorithm: %s"),
3472 string_locale);
3473 free(string_locale);
3475 goto leave;
3477 zfree(string);
3479 /* Get hash value */
3480 EXTRACT_STRING(".", string);
3481 if (!string) {
3482 isds_printf_message(context,
3483 _("sisds:dmHash element is missing hash value"));
3484 err = IE_ISDS;
3485 goto leave;
3487 (*hash)->length = _isds_b64decode(string, &((*hash)->value));
3488 if ((*hash)->length == (size_t) -1) {
3489 isds_printf_message(context,
3490 _("Error while Base64-decoding hash value"));
3491 err = IE_ERROR;
3492 goto leave;
3495 leave:
3496 if (err) isds_hash_free(hash);
3497 free(string);
3498 xmlXPathFreeObject(result);
3499 xpath_ctx->node = old_ctx_node;
3500 return err;
3504 /* Find and append isds:dmQTimestamp XML tree into envelope.
3505 * Because one service is allowed to miss time-stamp content, and we think
3506 * other could too (flaw in specification), this function is deliberated and
3507 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
3508 * @context is ISDS context
3509 * @envelope is automatically allocated envelope structure
3510 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
3511 * child
3512 * In case of error @envelope will be freed. */
3513 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
3514 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3515 isds_error err = IE_SUCCESS;
3516 xmlXPathObjectPtr result = NULL;
3517 char *string = NULL;
3519 if (!context) return IE_INVALID_CONTEXT;
3520 if (!envelope) return IE_INVAL;
3521 if (!xpath_ctx) {
3522 isds_envelope_free(envelope);
3523 return IE_INVAL;
3526 if (!*envelope) {
3527 *envelope = calloc(1, sizeof(**envelope));
3528 if (!*envelope) {
3529 err = IE_NOMEM;
3530 goto leave;
3532 } else {
3533 zfree((*envelope)->timestamp);
3534 (*envelope)->timestamp_length = 0;
3537 /* Get dmQTimestamp */
3538 EXTRACT_STRING("sisds:dmQTimestamp", string);
3539 if (!string) {
3540 isds_log(ILF_ISDS, ILL_INFO, _("Missing dmQTimestamp element content\n"));
3541 goto leave;
3543 (*envelope)->timestamp_length =
3544 _isds_b64decode(string, &((*envelope)->timestamp));
3545 if ((*envelope)->timestamp_length == (size_t) -1) {
3546 isds_printf_message(context,
3547 _("Error while Base64-decoding time stamp value"));
3548 err = IE_ERROR;
3549 goto leave;
3552 leave:
3553 if (err) isds_envelope_free(envelope);
3554 free(string);
3555 xmlXPathFreeObject(result);
3556 return err;
3560 /* Convert XSD tReturnedMessage XML tree into message structure.
3561 * It does not store serialized XML tree into message->raw.
3562 * It does store (pointer to) parsed XML tree into message->xml if needed.
3563 * @context is ISDS context
3564 * @include_documents Use true if documents must be extracted
3565 * (tReturnedMessage XSD type), use false if documents shall be omitted
3566 * (tReturnedMessageEnvelope).
3567 * @message is automatically reallocated message structure
3568 * @xpath_ctx is XPath context with current node as tReturnedMessage element
3569 * type
3570 * In case of error @message will be freed. */
3571 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
3572 const _Bool include_documents, struct isds_message **message,
3573 xmlXPathContextPtr xpath_ctx) {
3574 isds_error err = IE_SUCCESS;
3575 xmlNodePtr message_node;
3577 if (!context) return IE_INVALID_CONTEXT;
3578 if (!message) return IE_INVAL;
3579 isds_message_free(message);
3580 if (!xpath_ctx) return IE_INVAL;
3583 *message = calloc(1, sizeof(**message));
3584 if (!*message) {
3585 err = IE_NOMEM;
3586 goto leave;
3589 /* Save message XPATH context node */
3590 message_node = xpath_ctx->node;
3593 /* Extract dmDM */
3594 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
3595 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
3596 if (err) { err = IE_ERROR; goto leave; }
3597 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
3598 if (err) goto leave;
3600 if (include_documents) {
3601 struct isds_list *item;
3603 /* Extract dmFiles */
3604 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
3605 xpath_ctx);
3606 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
3607 err = IE_ISDS; goto leave;
3609 if (err) { err = IE_ERROR; goto leave; }
3610 err = extract_documents(context, &((*message)->documents), xpath_ctx);
3611 if (err) goto leave;
3613 /* Store xmlDoc of this message if needed */
3614 /* Only if we got a XML document in all the documents. */
3615 for (item = (*message)->documents; item; item = item->next) {
3616 if (item->data && ((struct isds_document *)item->data)->is_xml) {
3617 (*message)->xml = xpath_ctx->doc;
3618 break;
3624 /* Restore context to message */
3625 xpath_ctx->node = message_node;
3627 /* Extract dmHash */
3628 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
3629 xpath_ctx);
3630 if (err) goto leave;
3632 /* Extract dmQTimestamp, */
3633 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
3634 xpath_ctx);
3635 if (err) goto leave;
3637 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
3638 * dmAcceptanceTime. */
3639 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
3640 if (err) goto leave;
3642 /* Get message type */
3643 err = append_message_type(context, &((*message)->envelope), xpath_ctx);
3644 if (err) goto leave;
3646 leave:
3647 if (err) isds_message_free(message);
3648 return err;
3652 /* Extract message event into reallocated isds_event structure
3653 * @context is ISDS context
3654 * @event is automatically reallocated message event structure
3655 * @xpath_ctx is XPath context with current node as isds:dmEvent
3656 * In case of error @event will be freed. */
3657 static isds_error extract_event(struct isds_ctx *context,
3658 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
3659 isds_error err = IE_SUCCESS;
3660 xmlXPathObjectPtr result = NULL;
3661 xmlNodePtr event_node = xpath_ctx->node;
3662 char *string = NULL;
3664 if (!context) return IE_INVALID_CONTEXT;
3665 if (!event) return IE_INVAL;
3666 isds_event_free(event);
3667 if (!xpath_ctx) return IE_INVAL;
3669 *event = calloc(1, sizeof(**event));
3670 if (!*event) {
3671 err = IE_NOMEM;
3672 goto leave;
3675 /* Extract event data.
3676 * All elements are optional according XSD. That's funny. */
3677 EXTRACT_STRING("sisds:dmEventTime", string);
3678 if (string) {
3679 err = timestring2timeval((xmlChar *) string, &((*event)->time));
3680 if (err) {
3681 char *string_locale = _isds_utf82locale(string);
3682 if (err == IE_DATE) err = IE_ISDS;
3683 isds_printf_message(context,
3684 _("Could not convert dmEventTime as ISO time: %s"),
3685 string_locale);
3686 free(string_locale);
3687 goto leave;
3689 zfree(string);
3692 /* dmEventDescr element has prefix and the rest */
3693 EXTRACT_STRING("sisds:dmEventDescr", string);
3694 if (string) {
3695 err = eventstring2event((xmlChar *) string, *event);
3696 if (err) goto leave;
3697 zfree(string);
3700 leave:
3701 if (err) isds_event_free(event);
3702 free(string);
3703 xmlXPathFreeObject(result);
3704 xpath_ctx->node = event_node;
3705 return err;
3709 /* Convert element of XSD tEventsArray type from XML tree into
3710 * isds_list of isds_event's structure. The list is automatically reallocated.
3711 * @context is ISDS context
3712 * @events is automatically reallocated list of event structures
3713 * @xpath_ctx is XPath context with current node as tEventsArray
3714 * In case of error @events will be freed. */
3715 static isds_error extract_events(struct isds_ctx *context,
3716 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
3717 isds_error err = IE_SUCCESS;
3718 xmlXPathObjectPtr result = NULL;
3719 xmlNodePtr events_node = xpath_ctx->node;
3720 struct isds_list *event, *prev_event = NULL;
3722 if (!context) return IE_INVALID_CONTEXT;
3723 if (!events) return IE_INVAL;
3724 if (!xpath_ctx) return IE_INVAL;
3726 /* Free old list */
3727 isds_list_free(events);
3729 /* Find events */
3730 result = xmlXPathEvalExpression(BAD_CAST "sisds:dmEvent", xpath_ctx);
3731 if (!result) {
3732 err = IE_XML;
3733 goto leave;
3736 /* No match */
3737 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3738 isds_printf_message(context,
3739 _("Delivery info does not contain any event"));
3740 err = IE_ISDS;
3741 goto leave;
3745 /* Iterate over events */
3746 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
3748 /* Allocate and append list item */
3749 event = calloc(1, sizeof(*event));
3750 if (!event) {
3751 err = IE_NOMEM;
3752 goto leave;
3754 event->destructor = (void (*)(void **))isds_event_free;
3755 if (i == 0) *events = event;
3756 else prev_event->next = event;
3757 prev_event = event;
3759 /* Extract event */
3760 xpath_ctx->node = result->nodesetval->nodeTab[i];
3761 err = extract_event(context,
3762 (struct isds_event **) &(event->data), xpath_ctx);
3763 if (err) goto leave;
3767 leave:
3768 if (err) isds_list_free(events);
3769 xmlXPathFreeObject(result);
3770 xpath_ctx->node = events_node;
3771 return err;
3775 /* Insert Base64 encoded data as element with text child.
3776 * @context is session context
3777 * @parent is XML node to append @element with @data as child
3778 * @ns is XML namespace of @element, use NULL to inherit from @parent
3779 * @element is UTF-8 encoded name of new element
3780 * @data is bit stream to encode into @element
3781 * @length is size of @data in bytes
3782 * @return standard error code and fill long error message if needed */
3783 static isds_error insert_base64_encoded_string(struct isds_ctx *context,
3784 xmlNodePtr parent, const xmlNsPtr ns, const char *element,
3785 const void *data, size_t length) {
3786 isds_error err = IE_SUCCESS;
3787 xmlNodePtr node;
3789 if (!context) return IE_INVALID_CONTEXT;
3790 if (!data && length > 0) return IE_INVAL;
3791 if (!parent || !element) return IE_INVAL;
3793 xmlChar *base64data = NULL;
3794 base64data = (xmlChar *) _isds_b64encode(data, length);
3795 if (!base64data) {
3796 isds_printf_message(context,
3797 ngettext("Not enough memory to encode %zd byte into Base64",
3798 "Not enough memory to encode %zd bytes into Base64",
3799 length),
3800 length);
3801 err = IE_NOMEM;
3802 goto leave;
3804 INSERT_STRING_WITH_NS(parent, ns, element, base64data);
3806 leave:
3807 free(base64data);
3808 return err;
3812 /* Convert isds_document structure into XML tree and append to dmFiles node.
3813 * @context is session context
3814 * @document is ISDS document
3815 * @dm_files is XML element the resulting tree will be appended to as a child.
3816 * @return error code, in case of error context' message is filled. */
3817 static isds_error insert_document(struct isds_ctx *context,
3818 struct isds_document *document, xmlNodePtr dm_files) {
3819 isds_error err = IE_SUCCESS;
3820 xmlNodePtr new_file = NULL, file = NULL, node;
3821 xmlAttrPtr attribute_node;
3823 if (!context) return IE_INVALID_CONTEXT;
3824 if (!document || !dm_files) return IE_INVAL;
3826 /* Allocate new dmFile */
3827 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
3828 if (!new_file) {
3829 isds_printf_message(context, _("Could not allocate main dmFile"));
3830 err = IE_ERROR;
3831 goto leave;
3833 /* Append the new dmFile.
3834 * XXX: Main document must go first */
3835 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
3836 file = xmlAddPrevSibling(dm_files->children, new_file);
3837 else
3838 file = xmlAddChild(dm_files, new_file);
3840 if (!file) {
3841 xmlFreeNode(new_file); new_file = NULL;
3842 isds_printf_message(context, _("Could not add dmFile child to "
3843 "%s element"), dm_files->name);
3844 err = IE_ERROR;
3845 goto leave;
3848 /* @dmMimeType is required */
3849 if (!document->dmMimeType) {
3850 isds_log_message(context,
3851 _("Document is missing mandatory MIME type definition"));
3852 err = IE_INVAL;
3853 goto leave;
3855 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
3857 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
3858 if (!string) {
3859 isds_printf_message(context,
3860 _("Document has unknown dmFileMetaType: %ld"),
3861 document->dmFileMetaType);
3862 err = IE_ENUM;
3863 goto leave;
3865 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
3867 if (document->dmFileGuid) {
3868 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
3870 if (document->dmUpFileGuid) {
3871 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
3874 /* @dmFileDescr is required */
3875 if (!document->dmFileDescr) {
3876 isds_log_message(context,
3877 _("Document is missing mandatory description (title)"));
3878 err = IE_INVAL;
3879 goto leave;
3881 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
3883 if (document->dmFormat) {
3884 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
3888 /* Insert content (body) of the document. */
3889 if (document->is_xml) {
3890 /* XML document requested */
3892 /* Allocate new dmXMLContent */
3893 xmlNodePtr xmlcontent = xmlNewNode(file->ns, BAD_CAST "dmXMLContent");
3894 if (!xmlcontent) {
3895 isds_printf_message(context,
3896 _("Could not allocate dmXMLContent element"));
3897 err = IE_ERROR;
3898 goto leave;
3900 /* Append it */
3901 node = xmlAddChild(file, xmlcontent);
3902 if (!node) {
3903 xmlFreeNode(xmlcontent); xmlcontent = NULL;
3904 isds_printf_message(context,
3905 _("Could not add dmXMLContent child to %s element"),
3906 file->name);
3907 err = IE_ERROR;
3908 goto leave;
3911 /* Copy non-empty node list */
3912 if (document->xml_node_list) {
3913 xmlNodePtr content = xmlDocCopyNodeList(node->doc,
3914 document->xml_node_list);
3915 if (!content) {
3916 isds_printf_message(context,
3917 _("Not enough memory to copy XML document"));
3918 err = IE_NOMEM;
3919 goto leave;
3922 if (!xmlAddChildList(node, content)) {
3923 xmlFreeNodeList(content);
3924 isds_printf_message(context,
3925 _("Error while adding XML document into dmXMLContent"));
3926 err = IE_XML;
3927 goto leave;
3929 /* XXX: We cannot free the content here because it's part of node's
3930 * document since now. It will be freed with it automatically. */
3932 } else {
3933 /* Binary document requested */
3934 err = insert_base64_encoded_string(context, file, NULL, "dmEncodedContent",
3935 document->data, document->data_length);
3936 if (err) goto leave;
3939 leave:
3940 return err;
3944 /* Append XSD tMStatus XML tree into isds_message_copy structure.
3945 * The copy must be preallocated, the date are just appended into structure.
3946 * @context is ISDS context
3947 * @copy is message copy structure
3948 * @xpath_ctx is XPath context with current node as tMStatus */
3949 static isds_error append_TMStatus(struct isds_ctx *context,
3950 struct isds_message_copy *copy, xmlXPathContextPtr xpath_ctx) {
3951 isds_error err = IE_SUCCESS;
3952 xmlXPathObjectPtr result = NULL;
3953 char *code = NULL, *message = NULL;
3955 if (!context) return IE_INVALID_CONTEXT;
3956 if (!copy || !xpath_ctx) return IE_INVAL;
3958 /* Free old values */
3959 zfree(copy->dmStatus);
3960 zfree(copy->dmID);
3962 /* Get error specific to this copy */
3963 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code);
3964 if (!code) {
3965 isds_log_message(context,
3966 _("Missing isds:dmStatusCode under "
3967 "XSD:tMStatus type element"));
3968 err = IE_ISDS;
3969 goto leave;
3972 if (xmlStrcmp((const xmlChar *)code, BAD_CAST "0000")) {
3973 /* This copy failed */
3974 copy->error = IE_ISDS;
3975 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message);
3976 if (message) {
3977 copy->dmStatus = _isds_astrcat3(code, ": ", message);
3978 if (!copy->dmStatus) {
3979 copy->dmStatus = code;
3980 code = NULL;
3982 } else {
3983 copy->dmStatus = code;
3984 code = NULL;
3986 } else {
3987 /* This copy succeeded. In this case only, message ID is valid */
3988 copy->error = IE_SUCCESS;
3990 EXTRACT_STRING("isds:dmID", copy->dmID);
3991 if (!copy->dmID) {
3992 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
3993 "but did not returned assigned message ID\n"));
3994 err = IE_ISDS;
3998 leave:
3999 free(code);
4000 free(message);
4001 xmlXPathFreeObject(result);
4002 return err;
4006 /* Insert struct isds_approval data (box approval) into XML tree
4007 * @context is session context
4008 * @approval is libisds structure with approval description. NULL is
4009 * acceptable.
4010 * @parent is XML element to append @approval to */
4011 static isds_error insert_GExtApproval(struct isds_ctx *context,
4012 const struct isds_approval *approval, xmlNodePtr parent) {
4014 isds_error err = IE_SUCCESS;
4015 xmlNodePtr node;
4017 if (!context) return IE_INVALID_CONTEXT;
4018 if (!parent) return IE_INVAL;
4020 if (!approval) return IE_SUCCESS;
4022 /* Build XSD:gExtApproval */
4023 INSERT_SCALAR_BOOLEAN(parent, "dbApproved", approval->approved);
4024 INSERT_STRING(parent, "dbExternRefNumber", approval->refference);
4026 leave:
4027 return err;
4031 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4032 * code
4033 * @context is session context
4034 * @service_name is name of SERVICE_DB_ACCESS
4035 * @response is server SOAP body response as XML document
4036 * @raw_response is automatically reallocated bit stream with response body. Use
4037 * NULL if you don't care
4038 * @raw_response_length is size of @raw_response in bytes
4039 * @code is ISDS status code
4040 * @status_message is ISDS status message
4041 * @return error coded from lower layer, context message will be set up
4042 * appropriately. */
4043 static isds_error build_send_check_dbdummy_request(struct isds_ctx *context,
4044 const xmlChar *service_name,
4045 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
4046 xmlChar **code, xmlChar **status_message) {
4048 isds_error err = IE_SUCCESS;
4049 char *service_name_locale = NULL;
4050 xmlNodePtr request = NULL, node;
4051 xmlNsPtr isds_ns = NULL;
4053 if (!context) return IE_INVALID_CONTEXT;
4054 if (!service_name) return IE_INVAL;
4055 if (!response || !code || !status_message) return IE_INVAL;
4056 if (!raw_response_length && raw_response) return IE_INVAL;
4058 /* Free output argument */
4059 xmlFreeDoc(*response); *response = NULL;
4060 if (raw_response) zfree(*raw_response);
4061 free(*code);
4062 free(*status_message);
4065 /* Check if connection is established
4066 * TODO: This check should be done downstairs. */
4067 if (!context->curl) return IE_CONNECTION_CLOSED;
4069 service_name_locale = _isds_utf82locale((char*)service_name);
4070 if (!service_name_locale) {
4071 err = IE_NOMEM;
4072 goto leave;
4075 /* Build request */
4076 request = xmlNewNode(NULL, service_name);
4077 if (!request) {
4078 isds_printf_message(context,
4079 _("Could not build %s request"), service_name_locale);
4080 err = IE_ERROR;
4081 goto leave;
4083 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4084 if(!isds_ns) {
4085 isds_log_message(context, _("Could not create ISDS name space"));
4086 err = IE_ERROR;
4087 goto leave;
4089 xmlSetNs(request, isds_ns);
4092 /* Add XSD:tDummyInput child */
4093 INSERT_STRING(request, "dbDummy", NULL);
4096 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
4097 service_name_locale);
4099 /* Send request */
4100 err = isds(context, SERVICE_DB_ACCESS, request, response,
4101 raw_response, raw_response_length);
4102 xmlFreeNode(request); request = NULL;
4104 if (err) {
4105 isds_log(ILF_ISDS, ILL_DEBUG,
4106 _("Processing ISDS response on %s request failed\n"),
4107 service_name_locale);
4108 goto leave;
4111 /* Check for response status */
4112 err = isds_response_status(context, SERVICE_DB_ACCESS, *response,
4113 code, status_message, NULL);
4114 if (err) {
4115 isds_log(ILF_ISDS, ILL_DEBUG,
4116 _("ISDS response on %s request is missing status\n"),
4117 service_name_locale);
4118 goto leave;
4121 /* Request processed, but nothing found */
4122 if (xmlStrcmp(*code, BAD_CAST "0000")) {
4123 char *code_locale = _isds_utf82locale((char*) *code);
4124 char *status_message_locale =
4125 _isds_utf82locale((char*) *status_message);
4126 isds_log(ILF_ISDS, ILL_DEBUG,
4127 _("Server refused %s request (code=%s, message=%s)\n"),
4128 service_name_locale, code_locale, status_message_locale);
4129 isds_log_message(context, status_message_locale);
4130 free(code_locale);
4131 free(status_message_locale);
4132 err = IE_ISDS;
4133 goto leave;
4136 leave:
4137 free(service_name_locale);
4138 xmlFreeNode(request);
4139 return err;
4143 /* Get data about logged in user and his box. */
4144 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
4145 struct isds_DbOwnerInfo **db_owner_info) {
4146 isds_error err = IE_SUCCESS;
4147 xmlDocPtr response = NULL;
4148 xmlChar *code = NULL, *message = NULL;
4149 xmlXPathContextPtr xpath_ctx = NULL;
4150 xmlXPathObjectPtr result = NULL;
4151 char *string = NULL;
4153 if (!context) return IE_INVALID_CONTEXT;
4154 zfree(context->long_message);
4155 if (!db_owner_info) return IE_INVAL;
4157 /* Check if connection is established */
4158 if (!context->curl) return IE_CONNECTION_CLOSED;
4161 /* Do request and check for success */
4162 err = build_send_check_dbdummy_request(context,
4163 BAD_CAST "GetOwnerInfoFromLogin",
4164 &response, NULL, NULL, &code, &message);
4165 if (err) goto leave;
4168 /* Extract data */
4169 /* Prepare structure */
4170 isds_DbOwnerInfo_free(db_owner_info);
4171 *db_owner_info = calloc(1, sizeof(**db_owner_info));
4172 if (!*db_owner_info) {
4173 err = IE_NOMEM;
4174 goto leave;
4176 xpath_ctx = xmlXPathNewContext(response);
4177 if (!xpath_ctx) {
4178 err = IE_ERROR;
4179 goto leave;
4181 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4182 err = IE_ERROR;
4183 goto leave;
4186 /* Set context node */
4187 result = xmlXPathEvalExpression(BAD_CAST
4188 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
4189 if (!result) {
4190 err = IE_ERROR;
4191 goto leave;
4193 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4194 isds_log_message(context, _("Missing dbOwnerInfo element"));
4195 err = IE_ISDS;
4196 goto leave;
4198 if (result->nodesetval->nodeNr > 1) {
4199 isds_log_message(context, _("Multiple dbOwnerInfo element"));
4200 err = IE_ISDS;
4201 goto leave;
4203 xpath_ctx->node = result->nodesetval->nodeTab[0];
4204 xmlXPathFreeObject(result); result = NULL;
4206 /* Extract it */
4207 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
4209 leave:
4210 if (err) {
4211 isds_DbOwnerInfo_free(db_owner_info);
4214 free(string);
4215 xmlXPathFreeObject(result);
4216 xmlXPathFreeContext(xpath_ctx);
4218 free(code);
4219 free(message);
4220 xmlFreeDoc(response);
4222 if (!err)
4223 isds_log(ILF_ISDS, ILL_DEBUG,
4224 _("GetOwnerInfoFromLogin request processed by server "
4225 "successfully.\n"));
4227 return err;
4231 /* Get data about logged in user. */
4232 isds_error isds_GetUserInfoFromLogin(struct isds_ctx *context,
4233 struct isds_DbUserInfo **db_user_info) {
4234 isds_error err = IE_SUCCESS;
4235 xmlDocPtr response = NULL;
4236 xmlChar *code = NULL, *message = NULL;
4237 xmlXPathContextPtr xpath_ctx = NULL;
4238 xmlXPathObjectPtr result = NULL;
4240 if (!context) return IE_INVALID_CONTEXT;
4241 zfree(context->long_message);
4242 if (!db_user_info) return IE_INVAL;
4244 /* Check if connection is established */
4245 if (!context->curl) return IE_CONNECTION_CLOSED;
4248 /* Do request and check for success */
4249 err = build_send_check_dbdummy_request(context,
4250 BAD_CAST "GetUserInfoFromLogin",
4251 &response, NULL, NULL, &code, &message);
4252 if (err) goto leave;
4255 /* Extract data */
4256 /* Prepare structure */
4257 isds_DbUserInfo_free(db_user_info);
4258 *db_user_info = calloc(1, sizeof(**db_user_info));
4259 if (!*db_user_info) {
4260 err = IE_NOMEM;
4261 goto leave;
4263 xpath_ctx = xmlXPathNewContext(response);
4264 if (!xpath_ctx) {
4265 err = IE_ERROR;
4266 goto leave;
4268 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4269 err = IE_ERROR;
4270 goto leave;
4273 /* Set context node */
4274 result = xmlXPathEvalExpression(BAD_CAST
4275 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx);
4276 if (!result) {
4277 err = IE_ERROR;
4278 goto leave;
4280 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4281 isds_log_message(context, _("Missing dbUserInfo element"));
4282 err = IE_ISDS;
4283 goto leave;
4285 if (result->nodesetval->nodeNr > 1) {
4286 isds_log_message(context, _("Multiple dbUserInfo element"));
4287 err = IE_ISDS;
4288 goto leave;
4290 xpath_ctx->node = result->nodesetval->nodeTab[0];
4291 xmlXPathFreeObject(result); result = NULL;
4293 /* Extract it */
4294 err = extract_DbUserInfo(context, db_user_info, xpath_ctx);
4296 leave:
4297 if (err) {
4298 isds_DbUserInfo_free(db_user_info);
4301 xmlXPathFreeObject(result);
4302 xmlXPathFreeContext(xpath_ctx);
4304 free(code);
4305 free(message);
4306 xmlFreeDoc(response);
4308 if (!err)
4309 isds_log(ILF_ISDS, ILL_DEBUG,
4310 _("GetUserInfoFromLogin request processed by server "
4311 "successfully.\n"));
4313 return err;
4317 /* Get expiration time of current password
4318 * @context is session context
4319 * @expiration is automatically reallocated time when password expires, In
4320 * case of error will be nulled. */
4321 isds_error isds_get_password_expiration(struct isds_ctx *context,
4322 struct timeval **expiration) {
4323 isds_error err = IE_SUCCESS;
4324 xmlDocPtr response = NULL;
4325 xmlChar *code = NULL, *message = NULL;
4326 xmlXPathContextPtr xpath_ctx = NULL;
4327 xmlXPathObjectPtr result = NULL;
4328 char *string = NULL;
4330 if (!context) return IE_INVALID_CONTEXT;
4331 zfree(context->long_message);
4332 if (!expiration) return IE_INVAL;
4334 /* Check if connection is established */
4335 if (!context->curl) return IE_CONNECTION_CLOSED;
4338 /* Do request and check for success */
4339 err = build_send_check_dbdummy_request(context,
4340 BAD_CAST "GetPasswordInfo",
4341 &response, NULL, NULL, &code, &message);
4342 if (err) goto leave;
4345 /* Extract data */
4346 xpath_ctx = xmlXPathNewContext(response);
4347 if (!xpath_ctx) {
4348 err = IE_ERROR;
4349 goto leave;
4351 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4352 err = IE_ERROR;
4353 goto leave;
4356 /* Set context node */
4357 result = xmlXPathEvalExpression(BAD_CAST
4358 "/isds:GetPasswordInfoResponse", xpath_ctx);
4359 if (!result) {
4360 err = IE_ERROR;
4361 goto leave;
4363 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4364 isds_log_message(context,
4365 _("Missing GetPasswordInfoResponse element"));
4366 err = IE_ISDS;
4367 goto leave;
4369 if (result->nodesetval->nodeNr > 1) {
4370 isds_log_message(context,
4371 _("Multiple GetPasswordInfoResponse element"));
4372 err = IE_ISDS;
4373 goto leave;
4375 xpath_ctx->node = result->nodesetval->nodeTab[0];
4376 xmlXPathFreeObject(result); result = NULL;
4378 /* Extract expiration date */
4379 EXTRACT_STRING("isds:pswExpDate", string);
4380 if (!string) {
4381 isds_log_message(context, _("Missing pswExpDate element"));
4382 err = IE_ISDS;
4383 goto leave;
4386 err = timestring2timeval((xmlChar *) string, expiration);
4387 if (err) {
4388 char *string_locale = _isds_utf82locale(string);
4389 if (err == IE_DATE) err = IE_ISDS;
4390 isds_printf_message(context,
4391 _("Could not convert pswExpDate as ISO time: %s"),
4392 string_locale);
4393 free(string_locale);
4394 goto leave;
4397 leave:
4398 if (err) {
4399 if (*expiration) {
4400 zfree(*expiration);
4404 free(string);
4405 xmlXPathFreeObject(result);
4406 xmlXPathFreeContext(xpath_ctx);
4408 free(code);
4409 free(message);
4410 xmlFreeDoc(response);
4412 if (!err)
4413 isds_log(ILF_ISDS, ILL_DEBUG,
4414 _("GetPasswordInfo request processed by server "
4415 "successfully.\n"));
4417 return err;
4421 /* Change user password in ISDS.
4422 * User must supply old password, new password will takes effect after some
4423 * time, current session can continue. Password must fulfill some constraints.
4424 * @context is session context
4425 * @old_password is current password.
4426 * @new_password is requested new password */
4427 isds_error isds_change_password(struct isds_ctx *context,
4428 const char *old_password, const char *new_password) {
4429 isds_error err = IE_SUCCESS;
4430 xmlNsPtr isds_ns = NULL;
4431 xmlNodePtr request = NULL, node;
4432 xmlDocPtr response = NULL;
4433 xmlChar *code = NULL, *message = NULL;
4435 if (!context) return IE_INVALID_CONTEXT;
4436 zfree(context->long_message);
4437 if (!old_password || !new_password) return IE_INVAL;
4439 /* Check if connection is established
4440 * TODO: This check should be done downstairs. */
4441 if (!context->curl) return IE_CONNECTION_CLOSED;
4444 /* Build ChangeISDSPassword request */
4445 request = xmlNewNode(NULL, BAD_CAST "ChangeISDSPassword");
4446 if (!request) {
4447 isds_log_message(context,
4448 _("Could not build ChangeISDSPassword request"));
4449 return IE_ERROR;
4451 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4452 if(!isds_ns) {
4453 isds_log_message(context, _("Could not create ISDS name space"));
4454 xmlFreeNode(request);
4455 return IE_ERROR;
4457 xmlSetNs(request, isds_ns);
4459 INSERT_STRING(request, "dbOldPassword", old_password);
4460 INSERT_STRING(request, "dbNewPassword", new_password);
4463 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CheckDataBox request to ISDS\n"));
4465 /* Sent request */
4466 err = isds(context, SERVICE_DB_ACCESS, request, &response, NULL, NULL);
4468 /* Destroy request */
4469 xmlFreeNode(request); request = NULL;
4471 if (err) {
4472 isds_log(ILF_ISDS, ILL_DEBUG,
4473 _("Processing ISDS response on ChangeISDSPassword "
4474 "request failed\n"));
4475 goto leave;
4478 /* Check for response status */
4479 err = isds_response_status(context, SERVICE_DB_ACCESS, response,
4480 &code, &message, NULL);
4481 if (err) {
4482 isds_log(ILF_ISDS, ILL_DEBUG,
4483 _("ISDS response on ChangeISDSPassword request is missing "
4484 "status\n"));
4485 goto leave;
4488 /* Request processed, but empty password refused */
4489 if (!xmlStrcmp(code, BAD_CAST "1066")) {
4490 char *code_locale = _isds_utf82locale((char*)code);
4491 char *message_locale = _isds_utf82locale((char*)message);
4492 isds_log(ILF_ISDS, ILL_DEBUG,
4493 _("Server refused empty password on ChangeISDSPassword "
4494 "request (code=%s, message=%s)\n"),
4495 code_locale, message_locale);
4496 isds_log_message(context, _("Password must not be empty"));
4497 free(code_locale);
4498 free(message_locale);
4499 err = IE_INVAL;
4500 goto leave;
4503 /* Request processed, but new password was reused */
4504 else if (!xmlStrcmp(code, BAD_CAST "1067")) {
4505 char *code_locale = _isds_utf82locale((char*)code);
4506 char *message_locale = _isds_utf82locale((char*)message);
4507 isds_log(ILF_ISDS, ILL_DEBUG,
4508 _("Server refused the same new password on ChangeISDSPassword "
4509 "request (code=%s, message=%s)\n"),
4510 code_locale, message_locale);
4511 isds_log_message(context,
4512 _("New password must differ from the current one"));
4513 free(code_locale);
4514 free(message_locale);
4515 err = IE_INVAL;
4516 goto leave;
4519 /* Other error */
4520 else if (xmlStrcmp(code, BAD_CAST "0000")) {
4521 char *code_locale = _isds_utf82locale((char*)code);
4522 char *message_locale = _isds_utf82locale((char*)message);
4523 isds_log(ILF_ISDS, ILL_DEBUG,
4524 _("Server refused to change password on ChangeISDSPassword "
4525 "request (code=%s, message=%s)\n"),
4526 code_locale, message_locale);
4527 isds_log_message(context, message_locale);
4528 free(code_locale);
4529 free(message_locale);
4530 err = IE_ISDS;
4531 goto leave;
4534 /* Otherwise password changed successfully */
4536 leave:
4537 free(code);
4538 free(message);
4539 xmlFreeDoc(response);
4540 xmlFreeNode(request);
4542 if (!err)
4543 isds_log(ILF_ISDS, ILL_DEBUG,
4544 _("Password changed successfully on ChangeISDSPassword "
4545 "request.\n"));
4547 return err;
4551 /* Generic middle part with request sending and response check.
4552 * It sends prepared request and checks for error code.
4553 * @context is ISDS session context.
4554 * @service is ISDS service handler
4555 * @service_name is name in scope of given @service
4556 * @request is XML tree with request. Will be freed to save memory.
4557 * @response is XML document outputting ISDS response.
4558 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4559 * NULL, if you don't care. */
4560 static isds_error send_destroy_request_check_response(
4561 struct isds_ctx *context,
4562 const isds_service service, const xmlChar *service_name,
4563 xmlNodePtr *request, xmlDocPtr *response, xmlChar **refnumber) {
4564 isds_error err = IE_SUCCESS;
4565 char *service_name_locale = NULL;
4566 xmlChar *code = NULL, *message = NULL;
4569 if (!context) return IE_INVALID_CONTEXT;
4570 if (!service_name || *service_name == '\0' || !request || !*request ||
4571 !response)
4572 return IE_INVAL;
4574 /* Check if connection is established
4575 * TODO: This check should be done downstairs. */
4576 if (!context->curl) return IE_CONNECTION_CLOSED;
4578 service_name_locale = _isds_utf82locale((char*) service_name);
4579 if (!service_name_locale) {
4580 err = IE_NOMEM;
4581 goto leave;
4584 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
4585 service_name_locale);
4587 /* Send request */
4588 err = isds(context, service, *request, response, NULL, NULL);
4589 xmlFreeNode(*request); *request = NULL;
4591 if (err) {
4592 isds_log(ILF_ISDS, ILL_DEBUG,
4593 _("Processing ISDS response on %s request failed\n"),
4594 service_name_locale);
4595 goto leave;
4598 /* Check for response status */
4599 err = isds_response_status(context, service, *response,
4600 &code, &message, refnumber);
4601 if (err) {
4602 isds_log(ILF_ISDS, ILL_DEBUG,
4603 _("ISDS response on %s request is missing status\n"),
4604 service_name_locale);
4605 goto leave;
4608 /* Request processed, but server failed */
4609 if (xmlStrcmp(code, BAD_CAST "0000")) {
4610 char *code_locale = _isds_utf82locale((char*) code);
4611 char *message_locale = _isds_utf82locale((char*) message);
4612 isds_log(ILF_ISDS, ILL_DEBUG,
4613 _("Server refused %s request (code=%s, message=%s)\n"),
4614 service_name_locale, code_locale, message_locale);
4615 isds_log_message(context, message_locale);
4616 free(code_locale);
4617 free(message_locale);
4618 err = IE_ISDS;
4619 goto leave;
4623 leave:
4624 free(code);
4625 free(message);
4626 if (err && *response) {
4627 xmlFreeDoc(*response);
4628 *response = NULL;
4630 if (*request) {
4631 xmlFreeNode(*request);
4632 *request = NULL;
4634 free(service_name_locale);
4636 return err;
4640 /* Generic bottom half with request sending.
4641 * It sends prepared request, checks for error code, destroys response and
4642 * request and log success or failure.
4643 * @context is ISDS session context.
4644 * @service is ISDS service handler
4645 * @service_name is name in scope of given @service
4646 * @request is XML tree with request. Will be freed to save memory.
4647 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4648 * NULL, if you don't care. */
4649 static isds_error send_request_check_drop_response(
4650 struct isds_ctx *context,
4651 const isds_service service, const xmlChar *service_name,
4652 xmlNodePtr *request, xmlChar **refnumber) {
4653 isds_error err = IE_SUCCESS;
4654 xmlDocPtr response = NULL;
4657 if (!context) return IE_INVALID_CONTEXT;
4658 if (!service_name || *service_name == '\0' || !request || !*request)
4659 return IE_INVAL;
4661 /* Send request and check response*/
4662 err = send_destroy_request_check_response(context,
4663 service, service_name, request, &response, refnumber);
4665 xmlFreeDoc(response);
4667 if (*request) {
4668 xmlFreeNode(*request);
4669 *request = NULL;
4672 if (!err) {
4673 char *service_name_locale = _isds_utf82locale((char *) service_name);
4674 isds_log(ILF_ISDS, ILL_DEBUG,
4675 _("%s request processed by server successfully.\n"),
4676 service_name_locale);
4677 free(service_name_locale);
4680 return err;
4684 /* Build XSD:tCreateDBInput request type for box creating.
4685 * @context is session context
4686 * @request outputs built XML tree
4687 * @service_name is request name of SERVICE_DB_MANIPULATION service
4688 * @box is box description to create including single primary user (in case of
4689 * FO box type)
4690 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
4691 * box, or contact address of PFO box owner)
4692 * @former_names is optional undocumented string. Pass NULL if you don't care.
4693 * @upper_box_id is optional ID of supper box if currently created box is
4694 * subordinated.
4695 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
4696 * don't care.
4697 * @request_token is true if ISDS should return token that box owner can use
4698 * to obtain his new credentials in on-line way
4699 * @approval is optional external approval of box manipulation */
4700 static isds_error build_CreateDBInput_request(struct isds_ctx *context,
4701 xmlNodePtr *request, const xmlChar *service_name,
4702 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
4703 const xmlChar *former_names, const xmlChar *upper_box_id,
4704 const xmlChar *ceo_label, _Bool request_token,
4705 const struct isds_approval *approval) {
4706 isds_error err = IE_SUCCESS;
4707 xmlNsPtr isds_ns = NULL;
4708 xmlNodePtr node, dbPrimaryUsers;
4709 xmlChar *string = NULL;
4710 const struct isds_list *item;
4713 if (!context) return IE_INVALID_CONTEXT;
4714 if (!request || !service_name || service_name[0] == '\0' || !box)
4715 return IE_INVAL;
4718 /* Build CreateDataBox-similar request */
4719 *request = xmlNewNode(NULL, service_name);
4720 if (!*request) {
4721 char *service_name_locale = _isds_utf82locale((char*) service_name);
4722 isds_printf_message(context, _("Could build %s request"),
4723 service_name_locale);
4724 free(service_name_locale);
4725 return IE_ERROR;
4727 if (context->type == CTX_TYPE_TESTING_REQUEST_COLLECTOR) {
4728 isds_ns = xmlNewNs(*request, BAD_CAST ISDS1_NS, NULL);
4729 if (!isds_ns) {
4730 isds_log_message(context, _("Could not create ISDS1 name space"));
4731 xmlFreeNode(*request);
4732 return IE_ERROR;
4734 } else {
4735 isds_ns = xmlNewNs(*request, BAD_CAST ISDS_NS, NULL);
4736 if (!isds_ns) {
4737 isds_log_message(context, _("Could not create ISDS name space"));
4738 xmlFreeNode(*request);
4739 return IE_ERROR;
4742 xmlSetNs(*request, isds_ns);
4744 INSERT_ELEMENT(node, *request, "dbOwnerInfo");
4745 err = insert_DbOwnerInfo(context, box, node);
4746 if (err) goto leave;
4748 /* Insert users */
4749 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
4750 * verbose documentation allows none dbUserInfo */
4751 INSERT_ELEMENT(dbPrimaryUsers, *request, "dbPrimaryUsers");
4752 for (item = users; item; item = item->next) {
4753 if (item->data) {
4754 INSERT_ELEMENT(node, dbPrimaryUsers, "dbUserInfo");
4755 err = insert_DbUserInfo(context,
4756 (struct isds_DbUserInfo *) item->data, node);
4757 if (err) goto leave;
4761 INSERT_STRING(*request, "dbFormerNames", former_names);
4762 INSERT_STRING(*request, "dbUpperDBId", upper_box_id);
4763 INSERT_STRING(*request, "dbCEOLabel", ceo_label);
4765 if (request_token) {
4766 /* This element is allowed only for CreateDataBox and there is optional
4767 * with default to false */
4768 INSERT_SCALAR_BOOLEAN(*request, "dbUseActPortal", 1);
4771 err = insert_GExtApproval(context, approval, *request);
4772 if (err) goto leave;
4774 leave:
4775 if (err) {
4776 xmlFreeNode(*request);
4777 *request = NULL;
4779 free(string);
4780 return err;
4784 /* Create new box.
4785 * @context is session context
4786 * @box is box description to create including single primary user (in case of
4787 * FO box type). It outputs box ID assigned by ISDS in dbID element.
4788 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
4789 * box, or contact address of PFO box owner)
4790 * @former_names is optional undocumented string. Pass NULL if you don't care.
4791 * @upper_box_id is optional ID of supper box if currently created box is
4792 * subordinated.
4793 * @ceo_label is optional title of OVM box owner (e.g. mayor)
4794 * @token is NULL if new password should be delivered off-line to the user.
4795 * It is valid pointer if user should obtain new password on-line on dedicated
4796 * web server. Then it outputs automatically reallocated token user needs to
4797 * use to authorize on the web server to view his new password.
4798 * @approval is optional external approval of box manipulation
4799 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4800 * NULL, if you don't care.*/
4801 isds_error isds_add_box(struct isds_ctx *context,
4802 struct isds_DbOwnerInfo *box, const struct isds_list *users,
4803 const char *former_names, const char *upper_box_id,
4804 const char *ceo_label, char **token,
4805 const struct isds_approval *approval, char **refnumber) {
4806 isds_error err = IE_SUCCESS;
4807 xmlNodePtr request = NULL;
4808 xmlDocPtr response = NULL;
4809 xmlXPathContextPtr xpath_ctx = NULL;
4810 xmlXPathObjectPtr result = NULL;
4813 if (!context) return IE_INVALID_CONTEXT;
4814 zfree(context->long_message);
4815 if (token) zfree(*token);
4816 if (!box) return IE_INVAL;
4818 /* Scratch box ID */
4819 zfree(box->dbID);
4821 /* Build CreateDataBox request */
4822 err = build_CreateDBInput_request(context,
4823 &request, BAD_CAST "CreateDataBox",
4824 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
4825 (xmlChar *) ceo_label, token, approval);
4826 if (err) goto leave;
4828 /* Send it to server and process response */
4829 err = send_destroy_request_check_response(context,
4830 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
4831 &response, (xmlChar **) refnumber);
4833 /* Extract box ID */
4834 xpath_ctx = xmlXPathNewContext(response);
4835 if (!xpath_ctx) {
4836 err = IE_ERROR;
4837 goto leave;
4839 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4840 err = IE_ERROR;
4841 goto leave;
4843 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
4845 /* Extract optional token */
4846 if (token) {
4847 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbAccessDataId", *token);
4848 if (!*token)
4849 isds_log(ILF_ISDS, ILL_WARNING,
4850 _("ISDS did not return token on CreateDataBox request "
4851 "even if requested\n"));
4854 leave:
4855 xmlXPathFreeObject(result);
4856 xmlXPathFreeContext(xpath_ctx);
4857 xmlFreeDoc(response);
4858 xmlFreeNode(request);
4860 if (!err) {
4861 isds_log(ILF_ISDS, ILL_DEBUG,
4862 _("CreateDataBox request processed by server successfully.\n"));
4865 return err;
4869 /* Notify ISDS about new PFO entity.
4870 * This function has no real effect.
4871 * @context is session context
4872 * @box is PFO description including single primary user.
4873 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
4874 * @former_names is optional undocumented string. Pass NULL if you don't care.
4875 * @upper_box_id is optional ID of supper box if currently created box is
4876 * subordinated.
4877 * @ceo_label is optional title of OVM box owner (e.g. mayor)
4878 * @approval is optional external approval of box manipulation
4879 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4880 * NULL, if you don't care.*/
4881 isds_error isds_add_pfoinfo(struct isds_ctx *context,
4882 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
4883 const char *former_names, const char *upper_box_id,
4884 const char *ceo_label, const struct isds_approval *approval,
4885 char **refnumber) {
4886 isds_error err = IE_SUCCESS;
4887 xmlNodePtr request = NULL;
4889 if (!context) return IE_INVALID_CONTEXT;
4890 zfree(context->long_message);
4891 if (!box) return IE_INVAL;
4893 /* Build CreateDataBoxPFOInfo request */
4894 err = build_CreateDBInput_request(context,
4895 &request, BAD_CAST "CreateDataBoxPFOInfo",
4896 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
4897 (xmlChar *) ceo_label, 0, approval);
4898 if (err) goto leave;
4900 /* Send it to server and process response */
4901 err = send_request_check_drop_response(context,
4902 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
4903 (xmlChar **) refnumber);
4904 leave:
4905 xmlFreeNode(request);
4906 return err;
4910 /* Remove given given box permanently.
4911 * @context is session context
4912 * @box is box description to delete
4913 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
4914 * carry sane value.
4915 * @approval is optional external approval of box manipulation
4916 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4917 * NULL, if you don't care.*/
4918 isds_error isds_delete_box(struct isds_ctx *context,
4919 const struct isds_DbOwnerInfo *box, const struct tm *since,
4920 const struct isds_approval *approval, char **refnumber) {
4921 isds_error err = IE_SUCCESS;
4922 xmlNsPtr isds_ns = NULL;
4923 xmlNodePtr request = NULL;
4924 xmlNodePtr node;
4925 xmlChar *string = NULL;
4928 if (!context) return IE_INVALID_CONTEXT;
4929 zfree(context->long_message);
4930 if (!box || !since) return IE_INVAL;
4933 /* Build DeleteDataBox request */
4934 request = xmlNewNode(NULL, BAD_CAST "DeleteDataBox");
4935 if (!request) {
4936 isds_log_message(context,
4937 _("Could build DeleteDataBox request"));
4938 return IE_ERROR;
4940 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4941 if(!isds_ns) {
4942 isds_log_message(context, _("Could not create ISDS name space"));
4943 xmlFreeNode(request);
4944 return IE_ERROR;
4946 xmlSetNs(request, isds_ns);
4948 INSERT_ELEMENT(node, request, "dbOwnerInfo");
4949 err = insert_DbOwnerInfo(context, box, node);
4950 if (err) goto leave;
4952 err = tm2datestring(since, &string);
4953 if (err) {
4954 isds_log_message(context,
4955 _("Could not convert `since' argument to ISO date string"));
4956 goto leave;
4958 INSERT_STRING(request, "dbOwnerTerminationDate", string);
4959 zfree(string);
4961 err = insert_GExtApproval(context, approval, request);
4962 if (err) goto leave;
4965 /* Send it to server and process response */
4966 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
4967 BAD_CAST "DeleteDataBox", &request, (xmlChar **) refnumber);
4969 leave:
4970 xmlFreeNode(request);
4971 free(string);
4972 return err;
4976 /* Update data about given box.
4977 * @context is session context
4978 * @old_box current box description
4979 * @new_box are updated data about @old_box
4980 * @approval is optional external approval of box manipulation
4981 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4982 * NULL, if you don't care.*/
4983 isds_error isds_UpdateDataBoxDescr(struct isds_ctx *context,
4984 const struct isds_DbOwnerInfo *old_box,
4985 const struct isds_DbOwnerInfo *new_box,
4986 const struct isds_approval *approval, char **refnumber) {
4987 isds_error err = IE_SUCCESS;
4988 xmlNsPtr isds_ns = NULL;
4989 xmlNodePtr request = NULL;
4990 xmlNodePtr node;
4993 if (!context) return IE_INVALID_CONTEXT;
4994 zfree(context->long_message);
4995 if (!old_box || !new_box) return IE_INVAL;
4998 /* Build UpdateDataBoxDescr request */
4999 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxDescr");
5000 if (!request) {
5001 isds_log_message(context,
5002 _("Could build UpdateDataBoxDescr request"));
5003 return IE_ERROR;
5005 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5006 if(!isds_ns) {
5007 isds_log_message(context, _("Could not create ISDS name space"));
5008 xmlFreeNode(request);
5009 return IE_ERROR;
5011 xmlSetNs(request, isds_ns);
5013 INSERT_ELEMENT(node, request, "dbOldOwnerInfo");
5014 err = insert_DbOwnerInfo(context, old_box, node);
5015 if (err) goto leave;
5017 INSERT_ELEMENT(node, request, "dbNewOwnerInfo");
5018 err = insert_DbOwnerInfo(context, new_box, node);
5019 if (err) goto leave;
5021 err = insert_GExtApproval(context, approval, request);
5022 if (err) goto leave;
5025 /* Send it to server and process response */
5026 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
5027 BAD_CAST "UpdateDataBoxDescr", &request, (xmlChar **) refnumber);
5029 leave:
5030 xmlFreeNode(request);
5032 return err;
5036 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
5037 * code
5038 * @context is session context
5039 * @service is SOAP service
5040 * @service_name is name of request in @service
5041 * @box_id is box ID of interest
5042 * @approval is optional external approval of box manipulation
5043 * @response is server SOAP body response as XML document
5044 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5045 * NULL, if you don't care.
5046 * @return error coded from lower layer, context message will be set up
5047 * appropriately. */
5048 static isds_error build_send_dbid_request_check_response(
5049 struct isds_ctx *context, const isds_service service,
5050 const xmlChar *service_name, const xmlChar *box_id,
5051 const struct isds_approval *approval,
5052 xmlDocPtr *response, xmlChar **refnumber) {
5054 isds_error err = IE_SUCCESS;
5055 char *service_name_locale = NULL, *box_id_locale = NULL;
5056 xmlNodePtr request = NULL, node;
5057 xmlNsPtr isds_ns = NULL;
5059 if (!context) return IE_INVALID_CONTEXT;
5060 if (!service_name || !box_id) return IE_INVAL;
5061 if (!response) return IE_INVAL;
5063 /* Free output argument */
5064 xmlFreeDoc(*response); *response = NULL;
5066 /* Prepare strings */
5067 service_name_locale = _isds_utf82locale((char*)service_name);
5068 if (!service_name_locale) {
5069 err = IE_NOMEM;
5070 goto leave;
5072 box_id_locale = _isds_utf82locale((char*)box_id);
5073 if (!box_id_locale) {
5074 err = IE_NOMEM;
5075 goto leave;
5078 /* Build request */
5079 request = xmlNewNode(NULL, service_name);
5080 if (!request) {
5081 isds_printf_message(context,
5082 _("Could not build %s request"), service_name_locale);
5083 err = IE_ERROR;
5084 goto leave;
5086 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5087 if(!isds_ns) {
5088 isds_log_message(context, _("Could not create ISDS name space"));
5089 err = IE_ERROR;
5090 goto leave;
5092 xmlSetNs(request, isds_ns);
5094 /* Add XSD:tIdDbInput children */
5095 INSERT_STRING(request, "dbID", box_id);
5096 err = insert_GExtApproval(context, approval, request);
5097 if (err) goto leave;
5099 /* Send request and check response*/
5100 err = send_destroy_request_check_response(context,
5101 service, service_name, &request, response, refnumber);
5103 leave:
5104 free(service_name_locale);
5105 free(box_id_locale);
5106 xmlFreeNode(request);
5107 return err;
5111 /* Get data about all users assigned to given box.
5112 * @context is session context
5113 * @box_id is box ID
5114 * @users is automatically reallocated list of struct isds_DbUserInfo */
5115 isds_error isds_GetDataBoxUsers(struct isds_ctx *context, const char *box_id,
5116 struct isds_list **users) {
5117 isds_error err = IE_SUCCESS;
5118 xmlDocPtr response = NULL;
5119 xmlXPathContextPtr xpath_ctx = NULL;
5120 xmlXPathObjectPtr result = NULL;
5121 int i;
5122 struct isds_list *item, *prev_item = NULL;
5124 if (!context) return IE_INVALID_CONTEXT;
5125 zfree(context->long_message);
5126 if (!users || !box_id) return IE_INVAL;
5129 /* Do request and check for success */
5130 err = build_send_dbid_request_check_response(context,
5131 SERVICE_DB_MANIPULATION, BAD_CAST "GetDataBoxUsers",
5132 BAD_CAST box_id, NULL, &response, NULL);
5133 if (err) goto leave;
5136 /* Extract data */
5137 /* Prepare structure */
5138 isds_list_free(users);
5139 xpath_ctx = xmlXPathNewContext(response);
5140 if (!xpath_ctx) {
5141 err = IE_ERROR;
5142 goto leave;
5144 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5145 err = IE_ERROR;
5146 goto leave;
5149 /* Set context node */
5150 result = xmlXPathEvalExpression(BAD_CAST
5151 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
5152 xpath_ctx);
5153 if (!result) {
5154 err = IE_ERROR;
5155 goto leave;
5157 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5158 isds_log_message(context, _("Missing dbUserInfo element"));
5159 err = IE_ISDS;
5160 goto leave;
5163 /* Iterate over all users */
5164 for (i = 0; i < result->nodesetval->nodeNr; i++) {
5166 /* Prepare structure */
5167 item = calloc(1, sizeof(*item));
5168 if (!item) {
5169 err = IE_NOMEM;
5170 goto leave;
5172 item->destructor = (void(*)(void**))isds_DbUserInfo_free;
5173 if (i == 0) *users = item;
5174 else prev_item->next = item;
5175 prev_item = item;
5177 /* Extract it */
5178 xpath_ctx->node = result->nodesetval->nodeTab[i];
5179 err = extract_DbUserInfo(context,
5180 (struct isds_DbUserInfo **) (&item->data), xpath_ctx);
5181 if (err) goto leave;
5184 leave:
5185 if (err) {
5186 isds_list_free(users);
5189 xmlXPathFreeObject(result);
5190 xmlXPathFreeContext(xpath_ctx);
5191 xmlFreeDoc(response);
5193 if (!err)
5194 isds_log(ILF_ISDS, ILL_DEBUG,
5195 _("GetDataBoxUsers request processed by server "
5196 "successfully.\n"));
5198 return err;
5202 /* Update data about user assigned to given box.
5203 * @context is session context
5204 * @box is box identification
5205 * @old_user identifies user to update
5206 * @new_user are updated data about @old_user
5207 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5208 * NULL, if you don't care.*/
5209 isds_error isds_UpdateDataBoxUser(struct isds_ctx *context,
5210 const struct isds_DbOwnerInfo *box,
5211 const struct isds_DbUserInfo *old_user,
5212 const struct isds_DbUserInfo *new_user,
5213 char **refnumber) {
5214 isds_error err = IE_SUCCESS;
5215 xmlNsPtr isds_ns = NULL;
5216 xmlNodePtr request = NULL;
5217 xmlNodePtr node;
5220 if (!context) return IE_INVALID_CONTEXT;
5221 zfree(context->long_message);
5222 if (!box || !old_user || !new_user) return IE_INVAL;
5225 /* Build UpdateDataBoxUser request */
5226 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxUser");
5227 if (!request) {
5228 isds_log_message(context,
5229 _("Could build UpdateDataBoxUser request"));
5230 return IE_ERROR;
5232 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5233 if(!isds_ns) {
5234 isds_log_message(context, _("Could not create ISDS name space"));
5235 xmlFreeNode(request);
5236 return IE_ERROR;
5238 xmlSetNs(request, isds_ns);
5240 INSERT_ELEMENT(node, request, "dbOwnerInfo");
5241 err = insert_DbOwnerInfo(context, box, node);
5242 if (err) goto leave;
5244 INSERT_ELEMENT(node, request, "dbOldUserInfo");
5245 err = insert_DbUserInfo(context, old_user, node);
5246 if (err) goto leave;
5248 INSERT_ELEMENT(node, request, "dbNewUserInfo");
5249 err = insert_DbUserInfo(context, new_user, node);
5250 if (err) goto leave;
5252 /* Send it to server and process response */
5253 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
5254 BAD_CAST "UpdateDataBoxUser", &request, (xmlChar **) refnumber);
5256 leave:
5257 xmlFreeNode(request);
5259 return err;
5263 /* Reset credentials of user assigned to given box.
5264 * @context is session context
5265 * @box is box identification
5266 * @user identifies user to reset password
5267 * @fee_paid is true if fee has been paid, false otherwise
5268 * @approval is optional external approval of box manipulation
5269 * @token is NULL if new password should be delivered off-line to the user.
5270 * It is valid pointer if user should obtain new password on-line on dedicated
5271 * web server. Then it outputs automatically reallocated token user needs to
5272 * use to authorize on the web server to view his new password.
5273 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5274 * NULL, if you don't care.*/
5275 isds_error isds_reset_password(struct isds_ctx *context,
5276 const struct isds_DbOwnerInfo *box,
5277 const struct isds_DbUserInfo *user,
5278 const _Bool fee_paid, const struct isds_approval *approval,
5279 char **token, char **refnumber) {
5280 isds_error err = IE_SUCCESS;
5281 xmlNsPtr isds_ns = NULL;
5282 xmlNodePtr request = NULL, node;
5283 xmlDocPtr response = NULL;
5284 xmlXPathContextPtr xpath_ctx = NULL;
5285 xmlXPathObjectPtr result = NULL;
5288 if (!context) return IE_INVALID_CONTEXT;
5289 zfree(context->long_message);
5291 if (token) zfree(*token);
5292 if (!box || !user) return IE_INVAL;
5295 /* Build NewAccessData request */
5296 request = xmlNewNode(NULL, BAD_CAST "NewAccessData");
5297 if (!request) {
5298 isds_log_message(context,
5299 _("Could build NewAccessData request"));
5300 return IE_ERROR;
5302 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5303 if(!isds_ns) {
5304 isds_log_message(context, _("Could not create ISDS name space"));
5305 xmlFreeNode(request);
5306 return IE_ERROR;
5308 xmlSetNs(request, isds_ns);
5310 INSERT_ELEMENT(node, request, "dbOwnerInfo");
5311 err = insert_DbOwnerInfo(context, box, node);
5312 if (err) goto leave;
5314 INSERT_ELEMENT(node, request, "dbUserInfo");
5315 err = insert_DbUserInfo(context, user, node);
5316 if (err) goto leave;
5318 INSERT_SCALAR_BOOLEAN(request, "dbFeePaid", fee_paid);
5320 if (token) {
5321 INSERT_SCALAR_BOOLEAN(request, "dbVirtual", 1);
5322 } else {
5323 INSERT_SCALAR_BOOLEAN(request, "dbVirtual", 0);
5326 err = insert_GExtApproval(context, approval, request);
5327 if (err) goto leave;
5329 /* Send request and check response*/
5330 err = send_destroy_request_check_response(context,
5331 SERVICE_DB_MANIPULATION, BAD_CAST "NewAccessData", &request,
5332 &response, (xmlChar **) refnumber);
5333 if (err) goto leave;
5336 /* Extract optional token */
5337 if (token) {
5338 xpath_ctx = xmlXPathNewContext(response);
5339 if (!xpath_ctx) {
5340 err = IE_ERROR;
5341 goto leave;
5343 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5344 err = IE_ERROR;
5345 goto leave;
5348 EXTRACT_STRING("/isds:NewAccessDataResponse/isds:dbAccessDataId", *token);
5349 if (!*token)
5350 isds_log(ILF_ISDS, ILL_WARNING,
5351 _("ISDS did not return token on CreateDataBox request "
5352 "even if requested\n"));
5355 leave:
5356 xmlXPathFreeObject(result);
5357 xmlXPathFreeContext(xpath_ctx);
5358 xmlFreeDoc(response);
5359 xmlFreeNode(request);
5361 if (!err)
5362 isds_log(ILF_ISDS, ILL_DEBUG,
5363 _("NewAccessData request processed by server "
5364 "successfully.\n"));
5366 return err;
5370 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
5371 * code, destroy response and log success.
5372 * @context is ISDS session context.
5373 * @service_name is name of SERVICE_DB_MANIPULATION service
5374 * @box is box identification
5375 * @user identifies user to remove
5376 * @approval is optional external approval of box manipulation
5377 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5378 * NULL, if you don't care. */
5379 static isds_error build_send_manipulationboxuser_request_check_drop_response(
5380 struct isds_ctx *context, const xmlChar *service_name,
5381 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
5382 const struct isds_approval *approval, xmlChar **refnumber) {
5383 isds_error err = IE_SUCCESS;
5384 xmlNsPtr isds_ns = NULL;
5385 xmlNodePtr request = NULL, node;
5388 if (!context) return IE_INVALID_CONTEXT;
5389 zfree(context->long_message);
5390 if (!service_name || service_name[0] == '\0' || !box || !user)
5391 return IE_INVAL;
5394 /* Build NewAccessData request */
5395 request = xmlNewNode(NULL, service_name);
5396 if (!request) {
5397 char *service_name_locale = _isds_utf82locale((char *) service_name);
5398 isds_printf_message(context, _("Could build %s request"),
5399 service_name_locale);
5400 free(service_name_locale);
5401 return IE_ERROR;
5403 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5404 if(!isds_ns) {
5405 isds_log_message(context, _("Could not create ISDS name space"));
5406 xmlFreeNode(request);
5407 return IE_ERROR;
5409 xmlSetNs(request, isds_ns);
5411 INSERT_ELEMENT(node, request, "dbOwnerInfo");
5412 err = insert_DbOwnerInfo(context, box, node);
5413 if (err) goto leave;
5415 INSERT_ELEMENT(node, request, "dbUserInfo");
5416 err = insert_DbUserInfo(context, user, node);
5417 if (err) goto leave;
5419 err = insert_GExtApproval(context, approval, request);
5420 if (err) goto leave;
5422 /* Send request and check response*/
5423 err = send_request_check_drop_response (context,
5424 SERVICE_DB_MANIPULATION, service_name, &request, refnumber);
5426 leave:
5427 xmlFreeNode(request);
5428 return err;
5432 /* Assign new user to given box.
5433 * @context is session context
5434 * @box is box identification
5435 * @user defines new user to add
5436 * @approval is optional external approval of box manipulation
5437 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5438 * NULL, if you don't care.*/
5439 isds_error isds_add_user(struct isds_ctx *context,
5440 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
5441 const struct isds_approval *approval, char **refnumber) {
5442 return build_send_manipulationboxuser_request_check_drop_response(context,
5443 BAD_CAST "AddDataBoxUser", box, user, approval,
5444 (xmlChar **) refnumber);
5448 /* Remove user assigned to given box.
5449 * @context is session context
5450 * @box is box identification
5451 * @user identifies user to remove
5452 * @approval is optional external approval of box manipulation
5453 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5454 * NULL, if you don't care.*/
5455 isds_error isds_delete_user(struct isds_ctx *context,
5456 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
5457 const struct isds_approval *approval, char **refnumber) {
5458 return build_send_manipulationboxuser_request_check_drop_response(context,
5459 BAD_CAST "DeleteDataBoxUser", box, user, approval,
5460 (xmlChar **) refnumber);
5464 /* Find boxes suiting given criteria.
5465 * @criteria is filter. You should fill in at least some members.
5466 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
5467 * possibly empty. Input NULL or valid old structure.
5468 * @return:
5469 * IE_SUCCESS if search succeeded, @boxes contains useful data
5470 * IE_NOEXIST if no such box exists, @boxes will be NULL
5471 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
5472 * contains still valid data
5473 * other code if something bad happens. @boxes will be NULL. */
5474 isds_error isds_FindDataBox(struct isds_ctx *context,
5475 const struct isds_DbOwnerInfo *criteria,
5476 struct isds_list **boxes) {
5477 isds_error err = IE_SUCCESS;
5478 _Bool truncated = 0;
5479 xmlNsPtr isds_ns = NULL;
5480 xmlNodePtr request = NULL;
5481 xmlDocPtr response = NULL;
5482 xmlChar *code = NULL, *message = NULL;
5483 xmlNodePtr db_owner_info;
5484 xmlXPathContextPtr xpath_ctx = NULL;
5485 xmlXPathObjectPtr result = NULL;
5486 xmlChar *string = NULL;
5489 if (!context) return IE_INVALID_CONTEXT;
5490 zfree(context->long_message);
5491 if (!boxes) return IE_INVAL;
5492 isds_list_free(boxes);
5494 if (!criteria) {
5495 return IE_INVAL;
5498 /* Check if connection is established
5499 * TODO: This check should be done downstairs. */
5500 if (!context->curl) return IE_CONNECTION_CLOSED;
5503 /* Build FindDataBox request */
5504 request = xmlNewNode(NULL, BAD_CAST "FindDataBox");
5505 if (!request) {
5506 isds_log_message(context,
5507 _("Could build FindDataBox request"));
5508 return IE_ERROR;
5510 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5511 if(!isds_ns) {
5512 isds_log_message(context, _("Could not create ISDS name space"));
5513 xmlFreeNode(request);
5514 return IE_ERROR;
5516 xmlSetNs(request, isds_ns);
5517 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
5518 if (!db_owner_info) {
5519 isds_log_message(context, _("Could not add dbOwnerInfo child to "
5520 "FindDataBox element"));
5521 xmlFreeNode(request);
5522 return IE_ERROR;
5525 err = insert_DbOwnerInfo(context, criteria, db_owner_info);
5526 if (err) goto leave;
5529 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending FindDataBox request to ISDS\n"));
5531 /* Sent request */
5532 err = isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
5534 /* Destroy request */
5535 xmlFreeNode(request); request = NULL;
5537 if (err) {
5538 isds_log(ILF_ISDS, ILL_DEBUG,
5539 _("Processing ISDS response on FindDataBox "
5540 "request failed\n"));
5541 goto leave;
5544 /* Check for response status */
5545 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
5546 &code, &message, NULL);
5547 if (err) {
5548 isds_log(ILF_ISDS, ILL_DEBUG,
5549 _("ISDS response on FindDataBox request is missing status\n"));
5550 goto leave;
5553 /* Request processed, but nothing found */
5554 if (!xmlStrcmp(code, BAD_CAST "0002") ||
5555 !xmlStrcmp(code, BAD_CAST "5001")) {
5556 char *code_locale = _isds_utf82locale((char*)code);
5557 char *message_locale = _isds_utf82locale((char*)message);
5558 isds_log(ILF_ISDS, ILL_DEBUG,
5559 _("Server did not found any box on FindDataBox request "
5560 "(code=%s, message=%s)\n"), code_locale, message_locale);
5561 isds_log_message(context, message_locale);
5562 free(code_locale);
5563 free(message_locale);
5564 err = IE_NOEXIST;
5565 goto leave;
5568 /* Warning, not a error */
5569 if (!xmlStrcmp(code, BAD_CAST "0003")) {
5570 char *code_locale = _isds_utf82locale((char*)code);
5571 char *message_locale = _isds_utf82locale((char*)message);
5572 isds_log(ILF_ISDS, ILL_DEBUG,
5573 _("Server truncated response on FindDataBox request "
5574 "(code=%s, message=%s)\n"), code_locale, message_locale);
5575 isds_log_message(context, message_locale);
5576 free(code_locale);
5577 free(message_locale);
5578 truncated = 1;
5581 /* Other error */
5582 else if (xmlStrcmp(code, BAD_CAST "0000")) {
5583 char *code_locale = _isds_utf82locale((char*)code);
5584 char *message_locale = _isds_utf82locale((char*)message);
5585 isds_log(ILF_ISDS, ILL_DEBUG,
5586 _("Server refused FindDataBox request "
5587 "(code=%s, message=%s)\n"), code_locale, message_locale);
5588 isds_log_message(context, message_locale);
5589 free(code_locale);
5590 free(message_locale);
5591 err = IE_ISDS;
5592 goto leave;
5595 xpath_ctx = xmlXPathNewContext(response);
5596 if (!xpath_ctx) {
5597 err = IE_ERROR;
5598 goto leave;
5600 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5601 err = IE_ERROR;
5602 goto leave;
5605 /* Extract boxes if they present */
5606 result = xmlXPathEvalExpression(BAD_CAST
5607 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
5608 xpath_ctx);
5609 if (!result) {
5610 err = IE_ERROR;
5611 goto leave;
5613 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5614 struct isds_list *item, *prev_item = NULL;
5615 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
5616 item = calloc(1, sizeof(*item));
5617 if (!item) {
5618 err = IE_NOMEM;
5619 goto leave;
5622 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
5623 if (i == 0) *boxes = item;
5624 else prev_item->next = item;
5625 prev_item = item;
5627 xpath_ctx->node = result->nodesetval->nodeTab[i];
5628 err = extract_DbOwnerInfo(context,
5629 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
5630 if (err) goto leave;
5634 leave:
5635 if (err) {
5636 isds_list_free(boxes);
5637 } else {
5638 if (truncated) err = IE_2BIG;
5641 free(string);
5642 xmlFreeNode(request);
5643 xmlXPathFreeObject(result);
5644 xmlXPathFreeContext(xpath_ctx);
5646 free(code);
5647 free(message);
5648 xmlFreeDoc(response);
5650 if (!err)
5651 isds_log(ILF_ISDS, ILL_DEBUG,
5652 _("FindDataBox request processed by server successfully.\n"));
5654 return err;
5658 /* Get status of a box.
5659 * @context is ISDS session context.
5660 * @box_id is UTF-8 encoded box identifier as zero terminated string
5661 * @box_status is return value of box status.
5662 * @return:
5663 * IE_SUCCESS if box has been found and its status retrieved
5664 * IE_NOEXIST if box is not known to ISDS server
5665 * or other appropriate error.
5666 * You can use isds_DbState to enumerate box status. However out of enum
5667 * range value can be returned too. This is feature because ISDS
5668 * specification leaves the set of values open.
5669 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
5670 * the box has been deleted, but ISDS still lists its former existence. */
5671 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
5672 long int *box_status) {
5673 isds_error err = IE_SUCCESS;
5674 xmlNsPtr isds_ns = NULL;
5675 xmlNodePtr request = NULL, db_id;
5676 xmlDocPtr response = NULL;
5677 xmlChar *code = NULL, *message = NULL;
5678 xmlXPathContextPtr xpath_ctx = NULL;
5679 xmlXPathObjectPtr result = NULL;
5680 xmlChar *string = NULL;
5682 if (!context) return IE_INVALID_CONTEXT;
5683 zfree(context->long_message);
5684 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
5686 /* Check if connection is established
5687 * TODO: This check should be done downstairs. */
5688 if (!context->curl) return IE_CONNECTION_CLOSED;
5691 /* Build CheckDataBox request */
5692 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
5693 if (!request) {
5694 isds_log_message(context,
5695 _("Could build CheckDataBox request"));
5696 return IE_ERROR;
5698 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5699 if(!isds_ns) {
5700 isds_log_message(context, _("Could not create ISDS name space"));
5701 xmlFreeNode(request);
5702 return IE_ERROR;
5704 xmlSetNs(request, isds_ns);
5705 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
5706 if (!db_id) {
5707 isds_log_message(context, _("Could not add dbID child to "
5708 "CheckDataBox element"));
5709 xmlFreeNode(request);
5710 return IE_ERROR;
5714 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CheckDataBox request to ISDS\n"));
5716 /* Sent request */
5717 err = isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
5719 /* Destroy request */
5720 xmlFreeNode(request);
5722 if (err) {
5723 isds_log(ILF_ISDS, ILL_DEBUG,
5724 _("Processing ISDS response on CheckDataBox "
5725 "request failed\n"));
5726 goto leave;
5729 /* Check for response status */
5730 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
5731 &code, &message, NULL);
5732 if (err) {
5733 isds_log(ILF_ISDS, ILL_DEBUG,
5734 _("ISDS response on CheckDataBox request is missing status\n"));
5735 goto leave;
5738 /* Request processed, but nothing found */
5739 if (!xmlStrcmp(code, BAD_CAST "5001")) {
5740 char *box_id_locale = _isds_utf82locale((char*)box_id);
5741 char *code_locale = _isds_utf82locale((char*)code);
5742 char *message_locale = _isds_utf82locale((char*)message);
5743 isds_log(ILF_ISDS, ILL_DEBUG,
5744 _("Server did not found box %s on CheckDataBox request "
5745 "(code=%s, message=%s)\n"),
5746 box_id_locale, code_locale, message_locale);
5747 isds_log_message(context, message_locale);
5748 free(box_id_locale);
5749 free(code_locale);
5750 free(message_locale);
5751 err = IE_NOEXIST;
5752 goto leave;
5755 /* Other error */
5756 else if (xmlStrcmp(code, BAD_CAST "0000")) {
5757 char *code_locale = _isds_utf82locale((char*)code);
5758 char *message_locale = _isds_utf82locale((char*)message);
5759 isds_log(ILF_ISDS, ILL_DEBUG,
5760 _("Server refused CheckDataBox request "
5761 "(code=%s, message=%s)\n"), code_locale, message_locale);
5762 isds_log_message(context, message_locale);
5763 free(code_locale);
5764 free(message_locale);
5765 err = IE_ISDS;
5766 goto leave;
5769 /* Extract data */
5770 xpath_ctx = xmlXPathNewContext(response);
5771 if (!xpath_ctx) {
5772 err = IE_ERROR;
5773 goto leave;
5775 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5776 err = IE_ERROR;
5777 goto leave;
5779 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
5780 xpath_ctx);
5781 if (!result) {
5782 err = IE_ERROR;
5783 goto leave;
5785 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5786 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
5787 err = IE_ISDS;
5788 goto leave;
5790 if (result->nodesetval->nodeNr > 1) {
5791 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
5792 err = IE_ISDS;
5793 goto leave;
5795 xpath_ctx->node = result->nodesetval->nodeTab[0];
5796 xmlXPathFreeObject(result); result = NULL;
5798 EXTRACT_LONGINT("isds:dbState", box_status, 1);
5801 leave:
5802 free(string);
5803 xmlXPathFreeObject(result);
5804 xmlXPathFreeContext(xpath_ctx);
5806 free(code);
5807 free(message);
5808 xmlFreeDoc(response);
5810 if (!err)
5811 isds_log(ILF_ISDS, ILL_DEBUG,
5812 _("CheckDataBox request processed by server successfully.\n"));
5814 return err;
5818 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
5819 * code, destroy response and log success.
5820 * @context is ISDS session context.
5821 * @service_name is name of SERVICE_DB_MANIPULATION service
5822 * @box_id is UTF-8 encoded box identifier as zero terminated string
5823 * @approval is optional external approval of box manipulation
5824 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5825 * NULL, if you don't care. */
5826 static isds_error build_send_manipulationdbid_request_check_drop_response(
5827 struct isds_ctx *context, const xmlChar *service_name,
5828 const xmlChar *box_id, const struct isds_approval *approval,
5829 xmlChar **refnumber) {
5830 isds_error err = IE_SUCCESS;
5831 xmlDocPtr response = NULL;
5833 if (!context) return IE_INVALID_CONTEXT;
5834 zfree(context->long_message);
5835 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
5837 /* Check if connection is established */
5838 if (!context->curl) return IE_CONNECTION_CLOSED;
5840 /* Do request and check for success */
5841 err = build_send_dbid_request_check_response(context,
5842 SERVICE_DB_MANIPULATION, service_name, box_id, approval,
5843 &response, refnumber);
5844 xmlFreeDoc(response);
5846 if (!err) {
5847 char *service_name_locale = _isds_utf82locale((char *) service_name);
5848 isds_log(ILF_ISDS, ILL_DEBUG,
5849 _("%s request processed by server successfully.\n"),
5850 service_name_locale);
5851 free(service_name_locale);
5854 return err;
5858 /* Switch box into state where box can receive commercial messages (off by
5859 * default)
5860 * @context is ISDS session context.
5861 * @box_id is UTF-8 encoded box identifier as zero terminated string
5862 * @allow is true for enable, false for disable commercial messages income
5863 * @approval is optional external approval of box manipulation
5864 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5865 * NULL, if you don't care. */
5866 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
5867 const char *box_id, const _Bool allow,
5868 const struct isds_approval *approval, char **refnumber) {
5869 return build_send_manipulationdbid_request_check_drop_response(context,
5870 (allow) ? BAD_CAST "SetOpenAddressing" :
5871 BAD_CAST "ClearOpenAddressing",
5872 BAD_CAST box_id, approval, (xmlChar **) refnumber);
5876 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
5877 * message acceptance). This is just a box permission. Sender must apply
5878 * such role by sending each message.
5879 * @context is ISDS session context.
5880 * @box_id is UTF-8 encoded box identifier as zero terminated string
5881 * @allow is true for enable, false for disable OVM role permission
5882 * @approval is optional external approval of box manipulation
5883 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5884 * NULL, if you don't care. */
5885 isds_error isds_switch_effective_ovm(struct isds_ctx *context,
5886 const char *box_id, const _Bool allow,
5887 const struct isds_approval *approval, char **refnumber) {
5888 return build_send_manipulationdbid_request_check_drop_response(context,
5889 (allow) ? BAD_CAST "SetEffectiveOVM" :
5890 BAD_CAST "ClearEffectiveOVM",
5891 BAD_CAST box_id, approval, (xmlChar **) refnumber);
5895 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
5896 * code, destroy response and log success.
5897 * @context is ISDS session context.
5898 * @service_name is name of SERVICE_DB_MANIPULATION service
5899 * @owner is structure describing box
5900 * @approval is optional external approval of box manipulation
5901 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5902 * NULL, if you don't care. */
5903 static isds_error build_send_manipulationdbowner_request_check_drop_response(
5904 struct isds_ctx *context, const xmlChar *service_name,
5905 const struct isds_DbOwnerInfo *owner,
5906 const struct isds_approval *approval, xmlChar **refnumber) {
5907 isds_error err = IE_SUCCESS;
5908 char *service_name_locale = NULL;
5909 xmlNodePtr request = NULL, db_owner_info;
5910 xmlNsPtr isds_ns = NULL;
5913 if (!context) return IE_INVALID_CONTEXT;
5914 zfree(context->long_message);
5915 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
5917 service_name_locale = _isds_utf82locale((char*)service_name);
5918 if (!service_name_locale) {
5919 err = IE_NOMEM;
5920 goto leave;
5923 /* Build request */
5924 request = xmlNewNode(NULL, service_name);
5925 if (!request) {
5926 isds_printf_message(context,
5927 _("Could not build %s request"), service_name_locale);
5928 err = IE_ERROR;
5929 goto leave;
5931 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5932 if(!isds_ns) {
5933 isds_log_message(context, _("Could not create ISDS name space"));
5934 err = IE_ERROR;
5935 goto leave;
5937 xmlSetNs(request, isds_ns);
5940 /* Add XSD:tOwnerInfoInput child*/
5941 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
5942 err = insert_DbOwnerInfo(context, owner, db_owner_info);
5943 if (err) goto leave;
5945 /* Add XSD:gExtApproval*/
5946 err = insert_GExtApproval(context, approval, request);
5947 if (err) goto leave;
5949 /* Send it to server and process response */
5950 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
5951 service_name, &request, refnumber);
5953 leave:
5954 xmlFreeNode(request);
5955 free(service_name_locale);
5957 return err;
5961 /* Switch box accessibility state on request of box owner.
5962 * Despite the name, owner must do the request off-line. This function is
5963 * designed for such off-line meeting points (e.g. Czech POINT).
5964 * @context is ISDS session context.
5965 * @box identifies box to switch accessibility state.
5966 * @allow is true for making accessible, false to disallow access.
5967 * @approval is optional external approval of box manipulation
5968 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5969 * NULL, if you don't care. */
5970 isds_error isds_switch_box_accessibility_on_owner_request(
5971 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
5972 const _Bool allow, const struct isds_approval *approval,
5973 char **refnumber) {
5974 return build_send_manipulationdbowner_request_check_drop_response(context,
5975 (allow) ? BAD_CAST "EnableOwnDataBox" :
5976 BAD_CAST "DisableOwnDataBox",
5977 box, approval, (xmlChar **) refnumber);
5981 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
5982 * date.
5983 * @context is ISDS session context.
5984 * @box identifies box to switch accessibility state.
5985 * @since is date since accessibility has been denied. This can be past too.
5986 * Only tm_year, tm_mon and tm_mday carry sane value.
5987 * @approval is optional external approval of box manipulation
5988 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5989 * NULL, if you don't care. */
5990 isds_error isds_disable_box_accessibility_externaly(
5991 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
5992 const struct tm *since, const struct isds_approval *approval,
5993 char **refnumber) {
5994 isds_error err = IE_SUCCESS;
5995 char *service_name_locale = NULL;
5996 xmlNodePtr request = NULL, node;
5997 xmlNsPtr isds_ns = NULL;
5998 xmlChar *string = NULL;
6001 if (!context) return IE_INVALID_CONTEXT;
6002 zfree(context->long_message);
6003 if (!box || !since) return IE_INVAL;
6005 /* Build request */
6006 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
6007 if (!request) {
6008 isds_printf_message(context,
6009 _("Could not build %s request"), "DisableDataBoxExternally");
6010 err = IE_ERROR;
6011 goto leave;
6013 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6014 if(!isds_ns) {
6015 isds_log_message(context, _("Could not create ISDS name space"));
6016 err = IE_ERROR;
6017 goto leave;
6019 xmlSetNs(request, isds_ns);
6022 /* Add @box identification */
6023 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6024 err = insert_DbOwnerInfo(context, box, node);
6025 if (err) goto leave;
6027 /* Add @since date */
6028 err = tm2datestring(since, &string);
6029 if(err) {
6030 isds_log_message(context,
6031 _("Could not convert `since' argument to ISO date string"));
6032 goto leave;
6034 INSERT_STRING(request, "dbOwnerDisableDate", string);
6035 zfree(string);
6037 /* Add @approval */
6038 err = insert_GExtApproval(context, approval, request);
6039 if (err) goto leave;
6041 /* Send it to server and process response */
6042 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6043 BAD_CAST "DisableDataBoxExternally", &request,
6044 (xmlChar **) refnumber);
6046 leave:
6047 free(string);
6048 xmlFreeNode(request);
6049 free(service_name_locale);
6051 return err;
6055 /* Insert struct isds_message data (envelope (recipient data optional) and
6056 * documents) into XML tree
6057 * @context is session context
6058 * @outgoing_message is libisds structure with message data
6059 * @create_message is XML CreateMessage or CreateMultipleMessage element
6060 * @process_recipient true for recipient data serialization, false for no
6061 * serialization */
6062 static isds_error insert_envelope_files(struct isds_ctx *context,
6063 const struct isds_message *outgoing_message, xmlNodePtr create_message,
6064 const _Bool process_recipient) {
6066 isds_error err = IE_SUCCESS;
6067 xmlNodePtr envelope, dm_files, node;
6068 xmlChar *string = NULL;
6070 if (!context) return IE_INVALID_CONTEXT;
6071 if (!outgoing_message || !create_message) return IE_INVAL;
6074 /* Build envelope */
6075 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
6076 if (!envelope) {
6077 isds_printf_message(context, _("Could not add dmEnvelope child to "
6078 "%s element"), create_message->name);
6079 return IE_ERROR;
6082 if (!outgoing_message->envelope) {
6083 isds_log_message(context, _("Outgoing message is missing envelope"));
6084 err = IE_INVAL;
6085 goto leave;
6088 /* Insert optional message type */
6089 err = insert_message_type(context, outgoing_message->envelope->dmType,
6090 envelope);
6091 if (err) goto leave;
6093 INSERT_STRING(envelope, "dmSenderOrgUnit",
6094 outgoing_message->envelope->dmSenderOrgUnit);
6095 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
6096 outgoing_message->envelope->dmSenderOrgUnitNum, string);
6098 if (process_recipient) {
6099 if (!outgoing_message->envelope->dbIDRecipient) {
6100 isds_log_message(context,
6101 _("Outgoing message is missing recipient box identifier"));
6102 err = IE_INVAL;
6103 goto leave;
6105 INSERT_STRING(envelope, "dbIDRecipient",
6106 outgoing_message->envelope->dbIDRecipient);
6108 INSERT_STRING(envelope, "dmRecipientOrgUnit",
6109 outgoing_message->envelope->dmRecipientOrgUnit);
6110 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
6111 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
6112 INSERT_STRING(envelope, "dmToHands",
6113 outgoing_message->envelope->dmToHands);
6116 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
6117 "dmAnnotation");
6118 INSERT_STRING(envelope, "dmAnnotation",
6119 outgoing_message->envelope->dmAnnotation);
6121 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
6122 0, 50, "dmRecipientRefNumber");
6123 INSERT_STRING(envelope, "dmRecipientRefNumber",
6124 outgoing_message->envelope->dmRecipientRefNumber);
6126 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
6127 0, 50, "dmSenderRefNumber");
6128 INSERT_STRING(envelope, "dmSenderRefNumber",
6129 outgoing_message->envelope->dmSenderRefNumber);
6131 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
6132 0, 50, "dmRecipientIdent");
6133 INSERT_STRING(envelope, "dmRecipientIdent",
6134 outgoing_message->envelope->dmRecipientIdent);
6136 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
6137 0, 50, "dmSenderIdent");
6138 INSERT_STRING(envelope, "dmSenderIdent",
6139 outgoing_message->envelope->dmSenderIdent);
6141 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
6142 outgoing_message->envelope->dmLegalTitleLaw, string);
6143 INSERT_LONGINT(envelope, "dmLegalTitleYear",
6144 outgoing_message->envelope->dmLegalTitleYear, string);
6145 INSERT_STRING(envelope, "dmLegalTitleSect",
6146 outgoing_message->envelope->dmLegalTitleSect);
6147 INSERT_STRING(envelope, "dmLegalTitlePar",
6148 outgoing_message->envelope->dmLegalTitlePar);
6149 INSERT_STRING(envelope, "dmLegalTitlePoint",
6150 outgoing_message->envelope->dmLegalTitlePoint);
6152 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
6153 outgoing_message->envelope->dmPersonalDelivery);
6154 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
6155 outgoing_message->envelope->dmAllowSubstDelivery);
6157 /* ???: Should we require value for dbEffectiveOVM sender?
6158 * ISDS has default as true */
6159 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
6162 /* Append dmFiles */
6163 if (!outgoing_message->documents) {
6164 isds_log_message(context,
6165 _("Outgoing message is missing list of documents"));
6166 err = IE_INVAL;
6167 goto leave;
6169 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
6170 if (!dm_files) {
6171 isds_printf_message(context, _("Could not add dmFiles child to "
6172 "%s element"), create_message->name);
6173 err = IE_ERROR;
6174 goto leave;
6177 /* Check for document hierarchy */
6178 err = _isds_check_documents_hierarchy(context, outgoing_message->documents);
6179 if (err) goto leave;
6181 /* Process each document */
6182 for (struct isds_list *item =
6183 (struct isds_list *) outgoing_message->documents;
6184 item; item = item->next) {
6185 if (!item->data) {
6186 isds_log_message(context,
6187 _("List of documents contains empty item"));
6188 err = IE_INVAL;
6189 goto leave;
6191 /* FIXME: Check for dmFileMetaType and for document references.
6192 * Only first document can be of MAIN type */
6193 err = insert_document(context, (struct isds_document*) item->data,
6194 dm_files);
6196 if (err) goto leave;
6199 leave:
6200 free(string);
6201 return err;
6205 /* Send a message via ISDS to a recipient
6206 * @context is session context
6207 * @outgoing_message is message to send; Some members are mandatory (like
6208 * dbIDRecipient), some are optional and some are irrelevant (especially data
6209 * about sender). Included pointer to isds_list documents must contain at
6210 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
6211 * members will be filled with valid data from ISDS. Exact list of write
6212 * members is subject to change. Currently dmId is changed.
6213 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
6214 isds_error isds_send_message(struct isds_ctx *context,
6215 struct isds_message *outgoing_message) {
6217 isds_error err = IE_SUCCESS;
6218 xmlNsPtr isds_ns = NULL;
6219 xmlNodePtr request = NULL;
6220 xmlDocPtr response = NULL;
6221 xmlChar *code = NULL, *message = NULL;
6222 xmlXPathContextPtr xpath_ctx = NULL;
6223 xmlXPathObjectPtr result = NULL;
6224 _Bool message_is_complete = 0;
6226 if (!context) return IE_INVALID_CONTEXT;
6227 zfree(context->long_message);
6228 if (!outgoing_message) return IE_INVAL;
6230 /* Check if connection is established
6231 * TODO: This check should be done downstairs. */
6232 if (!context->curl) return IE_CONNECTION_CLOSED;
6235 /* Build CreateMessage request */
6236 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
6237 if (!request) {
6238 isds_log_message(context,
6239 _("Could not build CreateMessage request"));
6240 return IE_ERROR;
6242 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6243 if(!isds_ns) {
6244 isds_log_message(context, _("Could not create ISDS name space"));
6245 xmlFreeNode(request);
6246 return IE_ERROR;
6248 xmlSetNs(request, isds_ns);
6250 /* Append envelope and files */
6251 err = insert_envelope_files(context, outgoing_message, request, 1);
6252 if (err) goto leave;
6255 /* Signal we can serialize message since now */
6256 message_is_complete = 1;
6259 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
6261 /* Sent request */
6262 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
6264 /* Don't' destroy request, we want to provide it to application later */
6266 if (err) {
6267 isds_log(ILF_ISDS, ILL_DEBUG,
6268 _("Processing ISDS response on CreateMessage "
6269 "request failed\n"));
6270 goto leave;
6273 /* Check for response status */
6274 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
6275 &code, &message, NULL);
6276 if (err) {
6277 isds_log(ILF_ISDS, ILL_DEBUG,
6278 _("ISDS response on CreateMessage request "
6279 "is missing status\n"));
6280 goto leave;
6283 /* Request processed, but refused by server or server failed */
6284 if (xmlStrcmp(code, BAD_CAST "0000")) {
6285 char *box_id_locale =
6286 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
6287 char *code_locale = _isds_utf82locale((char*)code);
6288 char *message_locale = _isds_utf82locale((char*)message);
6289 isds_log(ILF_ISDS, ILL_DEBUG,
6290 _("Server did not accept message for %s on CreateMessage "
6291 "request (code=%s, message=%s)\n"),
6292 box_id_locale, code_locale, message_locale);
6293 isds_log_message(context, message_locale);
6294 free(box_id_locale);
6295 free(code_locale);
6296 free(message_locale);
6297 err = IE_ISDS;
6298 goto leave;
6302 /* Extract data */
6303 xpath_ctx = xmlXPathNewContext(response);
6304 if (!xpath_ctx) {
6305 err = IE_ERROR;
6306 goto leave;
6308 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6309 err = IE_ERROR;
6310 goto leave;
6312 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
6313 xpath_ctx);
6314 if (!result) {
6315 err = IE_ERROR;
6316 goto leave;
6318 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6319 isds_log_message(context, _("Missing CreateMessageResponse element"));
6320 err = IE_ISDS;
6321 goto leave;
6323 if (result->nodesetval->nodeNr > 1) {
6324 isds_log_message(context, _("Multiple CreateMessageResponse element"));
6325 err = IE_ISDS;
6326 goto leave;
6328 xpath_ctx->node = result->nodesetval->nodeTab[0];
6329 xmlXPathFreeObject(result); result = NULL;
6331 if (outgoing_message->envelope->dmID) {
6332 free(outgoing_message->envelope->dmID);
6333 outgoing_message->envelope->dmID = NULL;
6335 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
6336 if (!outgoing_message->envelope->dmID) {
6337 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
6338 "but did not return assigned message ID\n"));
6341 leave:
6342 /* TODO: Serialize message into structure member raw */
6343 /* XXX: Each web service transport message in different format.
6344 * Therefore it's not possible to save them directly.
6345 * To save them, one must figure out common format.
6346 * We can leave it on application, or we can implement the ESS format. */
6347 /*if (message_is_complete) {
6348 if (outgoing_message->envelope->dmID) {
6350 /* Add assigned message ID as first child*/
6351 /*xmlNodePtr dmid_text = xmlNewText(
6352 (xmlChar *) outgoing_message->envelope->dmID);
6353 if (!dmid_text) goto serialization_failed;
6355 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
6356 BAD_CAST "dmID");
6357 if (!dmid_element) {
6358 xmlFreeNode(dmid_text);
6359 goto serialization_failed;
6362 xmlNodePtr dmid_element_with_text =
6363 xmlAddChild(dmid_element, dmid_text);
6364 if (!dmid_element_with_text) {
6365 xmlFreeNode(dmid_element);
6366 xmlFreeNode(dmid_text);
6367 goto serialization_failed;
6370 node = xmlAddPrevSibling(envelope->childern,
6371 dmid_element_with_text);
6372 if (!node) {
6373 xmlFreeNodeList(dmid_element_with_text);
6374 goto serialization_failed;
6378 /* Serialize message with ID into raw */
6379 /*buffer = serialize_element(envelope)*/
6380 /* }
6382 serialization_failed:
6386 /* Clean up */
6387 xmlXPathFreeObject(result);
6388 xmlXPathFreeContext(xpath_ctx);
6390 free(code);
6391 free(message);
6392 xmlFreeDoc(response);
6393 xmlFreeNode(request);
6395 if (!err)
6396 isds_log(ILF_ISDS, ILL_DEBUG,
6397 _("CreateMessage request processed by server "
6398 "successfully.\n"));
6400 return err;
6404 /* Send a message via ISDS to a multiple recipients
6405 * @context is session context
6406 * @outgoing_message is message to send; Some members are mandatory,
6407 * some are optional and some are irrelevant (especially data
6408 * about sender). Data about recipient will be substituted by ISDS from
6409 * @copies. Included pointer to isds_list documents must
6410 * contain at least one document of FILEMETATYPE_MAIN.
6411 * @copies is list of isds_message_copy structures addressing all desired
6412 * recipients. This is read-write structure, some members will be filled with
6413 * valid data from ISDS (message IDs, error codes, error descriptions).
6414 * @return
6415 * ISDS_SUCCESS if all messages have been sent
6416 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
6417 * succeeded messages can be identified by copies->data->error),
6418 * or other error code if something other goes wrong. */
6419 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
6420 const struct isds_message *outgoing_message,
6421 struct isds_list *copies) {
6423 isds_error err = IE_SUCCESS, append_err;
6424 xmlNsPtr isds_ns = NULL;
6425 xmlNodePtr request = NULL, recipients, recipient, node;
6426 struct isds_list *item;
6427 struct isds_message_copy *copy;
6428 xmlDocPtr response = NULL;
6429 xmlChar *code = NULL, *message = NULL;
6430 xmlXPathContextPtr xpath_ctx = NULL;
6431 xmlXPathObjectPtr result = NULL;
6432 xmlChar *string = NULL;
6433 int i;
6435 if (!context) return IE_INVALID_CONTEXT;
6436 zfree(context->long_message);
6437 if (!outgoing_message || !copies) return IE_INVAL;
6439 /* Check if connection is established
6440 * TODO: This check should be done downstairs. */
6441 if (!context->curl) return IE_CONNECTION_CLOSED;
6444 /* Build CreateMultipleMessage request */
6445 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
6446 if (!request) {
6447 isds_log_message(context,
6448 _("Could not build CreateMultipleMessage request"));
6449 return IE_ERROR;
6451 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6452 if(!isds_ns) {
6453 isds_log_message(context, _("Could not create ISDS name space"));
6454 xmlFreeNode(request);
6455 return IE_ERROR;
6457 xmlSetNs(request, isds_ns);
6460 /* Build recipients */
6461 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
6462 if (!recipients) {
6463 isds_log_message(context, _("Could not add dmRecipients child to "
6464 "CreateMultipleMessage element"));
6465 xmlFreeNode(request);
6466 return IE_ERROR;
6469 /* Insert each recipient */
6470 for (item = copies; item; item = item->next) {
6471 copy = (struct isds_message_copy *) item->data;
6472 if (!copy) {
6473 isds_log_message(context,
6474 _("`copies' list item contains empty data"));
6475 err = IE_INVAL;
6476 goto leave;
6479 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
6480 if (!recipient) {
6481 isds_log_message(context, _("Could not add dmRecipient child to "
6482 "dmRecipients element"));
6483 err = IE_ERROR;
6484 goto leave;
6487 if (!copy->dbIDRecipient) {
6488 isds_log_message(context,
6489 _("Message copy is missing recipient box identifier"));
6490 err = IE_INVAL;
6491 goto leave;
6493 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
6494 INSERT_STRING(recipient, "dmRecipientOrgUnit",
6495 copy->dmRecipientOrgUnit);
6496 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
6497 copy->dmRecipientOrgUnitNum, string);
6498 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
6501 /* Append envelope and files */
6502 err = insert_envelope_files(context, outgoing_message, request, 0);
6503 if (err) goto leave;
6506 isds_log(ILF_ISDS, ILL_DEBUG,
6507 _("Sending CreateMultipleMessage request to ISDS\n"));
6509 /* Sent request */
6510 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
6511 if (err) {
6512 isds_log(ILF_ISDS, ILL_DEBUG,
6513 _("Processing ISDS response on CreateMultipleMessage "
6514 "request failed\n"));
6515 goto leave;
6518 /* Check for response status */
6519 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
6520 &code, &message, NULL);
6521 if (err) {
6522 isds_log(ILF_ISDS, ILL_DEBUG,
6523 _("ISDS response on CreateMultipleMessage request "
6524 "is missing status\n"));
6525 goto leave;
6528 /* Request processed, but some copies failed */
6529 if (!xmlStrcmp(code, BAD_CAST "0004")) {
6530 char *box_id_locale =
6531 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
6532 char *code_locale = _isds_utf82locale((char*)code);
6533 char *message_locale = _isds_utf82locale((char*)message);
6534 isds_log(ILF_ISDS, ILL_DEBUG,
6535 _("Server did accept message for multiple recipients "
6536 "on CreateMultipleMessage request but delivery to "
6537 "some of them failed (code=%s, message=%s)\n"),
6538 box_id_locale, code_locale, message_locale);
6539 isds_log_message(context, message_locale);
6540 free(box_id_locale);
6541 free(code_locale);
6542 free(message_locale);
6543 err = IE_PARTIAL_SUCCESS;
6546 /* Request refused by server as whole */
6547 else if (xmlStrcmp(code, BAD_CAST "0000")) {
6548 char *box_id_locale =
6549 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
6550 char *code_locale = _isds_utf82locale((char*)code);
6551 char *message_locale = _isds_utf82locale((char*)message);
6552 isds_log(ILF_ISDS, ILL_DEBUG,
6553 _("Server did not accept message for multiple recipients "
6554 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
6555 box_id_locale, code_locale, message_locale);
6556 isds_log_message(context, message_locale);
6557 free(box_id_locale);
6558 free(code_locale);
6559 free(message_locale);
6560 err = IE_ISDS;
6561 goto leave;
6565 /* Extract data */
6566 xpath_ctx = xmlXPathNewContext(response);
6567 if (!xpath_ctx) {
6568 err = IE_ERROR;
6569 goto leave;
6571 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6572 err = IE_ERROR;
6573 goto leave;
6575 result = xmlXPathEvalExpression(
6576 BAD_CAST "/isds:CreateMultipleMessageResponse"
6577 "/isds:dmMultipleStatus/isds:dmSingleStatus",
6578 xpath_ctx);
6579 if (!result) {
6580 err = IE_ERROR;
6581 goto leave;
6583 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6584 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
6585 err = IE_ISDS;
6586 goto leave;
6589 /* Extract message ID and delivery status for each copy */
6590 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
6591 item = item->next, i++) {
6592 copy = (struct isds_message_copy *) item->data;
6593 xpath_ctx->node = result->nodesetval->nodeTab[i];
6595 append_err = append_TMStatus(context, copy, xpath_ctx);
6596 if (append_err) {
6597 err = append_err;
6598 goto leave;
6601 if (item || i < result->nodesetval->nodeNr) {
6602 isds_printf_message(context, _("ISDS returned unexpected number of "
6603 "message copy delivery states: %d"),
6604 result->nodesetval->nodeNr);
6605 err = IE_ISDS;
6606 goto leave;
6610 leave:
6611 /* Clean up */
6612 free(string);
6613 xmlXPathFreeObject(result);
6614 xmlXPathFreeContext(xpath_ctx);
6616 free(code);
6617 free(message);
6618 xmlFreeDoc(response);
6619 xmlFreeNode(request);
6621 if (!err)
6622 isds_log(ILF_ISDS, ILL_DEBUG,
6623 _("CreateMultipleMessageResponse request processed by server "
6624 "successfully.\n"));
6626 return err;
6630 /* Get list of messages. This is common core for getting sent or received
6631 * messages.
6632 * Any criterion argument can be NULL, if you don't care about it.
6633 * @context is session context. Must not be NULL.
6634 * @outgoing_direction is true if you want list of outgoing messages,
6635 * it's false if you want incoming messages.
6636 * @from_time is minimal time and date of message sending inclusive.
6637 * @to_time is maximal time and date of message sending inclusive
6638 * @organization_unit_number is number of sender/recipient respectively.
6639 * @status_filter is bit field of isds_message_status values. Use special
6640 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
6641 * all values, you can use bit-wise arithmetic if you want.)
6642 * @offset is index of first message we are interested in. First message is 1.
6643 * Set to 0 (or 1) if you don't care.
6644 * @number is maximal length of list you want to get as input value, outputs
6645 * number of messages matching these criteria. Can be NULL if you don't care
6646 * (applies to output value either).
6647 * @messages is automatically reallocated list of isds_message's. Be ware that
6648 * it returns only brief overview (envelope and some other fields) about each
6649 * message, not the complete message. FIXME: Specify exact fields.
6650 * The list is sorted by delivery time in ascending order.
6651 * Use NULL if
6652 * you don't care about don't need the data (useful if you want to know only
6653 * the @number). If you provide &NULL, list will be allocated on heap, if you
6654 * provide pointer to non-NULL, list will be freed automatically at first. Also
6655 * in case of error the list will be NULLed.
6656 * @return IE_SUCCESS or appropriate error code. */
6657 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
6658 _Bool outgoing_direction,
6659 const struct timeval *from_time, const struct timeval *to_time,
6660 const long int *organization_unit_number,
6661 const unsigned int status_filter,
6662 const unsigned long int offset, unsigned long int *number,
6663 struct isds_list **messages) {
6665 isds_error err = IE_SUCCESS;
6666 xmlNsPtr isds_ns = NULL;
6667 xmlNodePtr request = NULL, node;
6668 xmlDocPtr response = NULL;
6669 xmlChar *code = NULL, *message = NULL;
6670 xmlXPathContextPtr xpath_ctx = NULL;
6671 xmlXPathObjectPtr result = NULL;
6672 xmlChar *string = NULL;
6673 long unsigned int count = 0;
6675 if (!context) return IE_INVALID_CONTEXT;
6676 zfree(context->long_message);
6678 /* Free former message list if any */
6679 if (messages) isds_list_free(messages);
6681 /* Check if connection is established
6682 * TODO: This check should be done downstairs. */
6683 if (!context->curl) return IE_CONNECTION_CLOSED;
6685 /* Build GetListOf*Messages request */
6686 request = xmlNewNode(NULL,
6687 (outgoing_direction) ?
6688 BAD_CAST "GetListOfSentMessages" :
6689 BAD_CAST "GetListOfReceivedMessages"
6691 if (!request) {
6692 isds_log_message(context,
6693 (outgoing_direction) ?
6694 _("Could not build GetListOfSentMessages request") :
6695 _("Could not build GetListOfReceivedMessages request")
6697 return IE_ERROR;
6699 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6700 if(!isds_ns) {
6701 isds_log_message(context, _("Could not create ISDS name space"));
6702 xmlFreeNode(request);
6703 return IE_ERROR;
6705 xmlSetNs(request, isds_ns);
6708 if (from_time) {
6709 err = timeval2timestring(from_time, &string);
6710 if (err) goto leave;
6712 INSERT_STRING(request, "dmFromTime", string);
6713 free(string); string = NULL;
6715 if (to_time) {
6716 err = timeval2timestring(to_time, &string);
6717 if (err) goto leave;
6719 INSERT_STRING(request, "dmToTime", string);
6720 free(string); string = NULL;
6722 if (outgoing_direction) {
6723 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
6724 organization_unit_number, string);
6725 } else {
6726 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
6727 organization_unit_number, string);
6730 if (status_filter > MESSAGESTATE_ANY) {
6731 isds_printf_message(context,
6732 _("Invalid message state filter value: %ld"), status_filter);
6733 err = IE_INVAL;
6734 goto leave;
6736 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
6738 if (offset > 0 ) {
6739 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
6740 } else {
6741 INSERT_STRING(request, "dmOffset", "1");
6744 /* number 0 means no limit */
6745 if (number && *number == 0) {
6746 INSERT_STRING(request, "dmLimit", NULL);
6747 } else {
6748 INSERT_ULONGINT(request, "dmLimit", number, string);
6752 isds_log(ILF_ISDS, ILL_DEBUG,
6753 (outgoing_direction) ?
6754 _("Sending GetListOfSentMessages request to ISDS\n") :
6755 _("Sending GetListOfReceivedMessages request to ISDS\n")
6758 /* Sent request */
6759 err = isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
6760 xmlFreeNode(request); request = NULL;
6762 if (err) {
6763 isds_log(ILF_ISDS, ILL_DEBUG,
6764 (outgoing_direction) ?
6765 _("Processing ISDS response on GetListOfSentMessages "
6766 "request failed\n") :
6767 _("Processing ISDS response on GetListOfReceivedMessages "
6768 "request failed\n")
6770 goto leave;
6773 /* Check for response status */
6774 err = isds_response_status(context, SERVICE_DM_INFO, response,
6775 &code, &message, NULL);
6776 if (err) {
6777 isds_log(ILF_ISDS, ILL_DEBUG,
6778 (outgoing_direction) ?
6779 _("ISDS response on GetListOfSentMessages request "
6780 "is missing status\n") :
6781 _("ISDS response on GetListOfReceivedMessages request "
6782 "is missing status\n")
6784 goto leave;
6787 /* Request processed, but nothing found */
6788 if (xmlStrcmp(code, BAD_CAST "0000")) {
6789 char *code_locale = _isds_utf82locale((char*)code);
6790 char *message_locale = _isds_utf82locale((char*)message);
6791 isds_log(ILF_ISDS, ILL_DEBUG,
6792 (outgoing_direction) ?
6793 _("Server refused GetListOfSentMessages request "
6794 "(code=%s, message=%s)\n") :
6795 _("Server refused GetListOfReceivedMessages request "
6796 "(code=%s, message=%s)\n"),
6797 code_locale, message_locale);
6798 isds_log_message(context, message_locale);
6799 free(code_locale);
6800 free(message_locale);
6801 err = IE_ISDS;
6802 goto leave;
6806 /* Extract data */
6807 xpath_ctx = xmlXPathNewContext(response);
6808 if (!xpath_ctx) {
6809 err = IE_ERROR;
6810 goto leave;
6812 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6813 err = IE_ERROR;
6814 goto leave;
6816 result = xmlXPathEvalExpression(
6817 (outgoing_direction) ?
6818 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
6819 "isds:dmRecords/isds:dmRecord" :
6820 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
6821 "isds:dmRecords/isds:dmRecord",
6822 xpath_ctx);
6823 if (!result) {
6824 err = IE_ERROR;
6825 goto leave;
6828 /* Fill output arguments in */
6829 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6830 struct isds_envelope *envelope;
6831 struct isds_list *item = NULL, *last_item = NULL;
6833 for (count = 0; count < result->nodesetval->nodeNr; count++) {
6834 /* Create new message */
6835 item = calloc(1, sizeof(*item));
6836 if (!item) {
6837 err = IE_NOMEM;
6838 goto leave;
6840 item->destructor = (void(*)(void**)) &isds_message_free;
6841 item->data = calloc(1, sizeof(struct isds_message));
6842 if (!item->data) {
6843 isds_list_free(&item);
6844 err = IE_NOMEM;
6845 goto leave;
6848 /* Extract envelope data */
6849 xpath_ctx->node = result->nodesetval->nodeTab[count];
6850 envelope = NULL;
6851 err = extract_DmRecord(context, &envelope, xpath_ctx);
6852 if (err) {
6853 isds_list_free(&item);
6854 goto leave;
6857 /* Attach extracted envelope */
6858 ((struct isds_message *) item->data)->envelope = envelope;
6860 /* Append new message into the list */
6861 if (!*messages) {
6862 *messages = last_item = item;
6863 } else {
6864 last_item->next = item;
6865 last_item = item;
6869 if (number) *number = count;
6871 leave:
6872 if (err) {
6873 isds_list_free(messages);
6876 free(string);
6877 xmlXPathFreeObject(result);
6878 xmlXPathFreeContext(xpath_ctx);
6880 free(code);
6881 free(message);
6882 xmlFreeDoc(response);
6883 xmlFreeNode(request);
6885 if (!err)
6886 isds_log(ILF_ISDS, ILL_DEBUG,
6887 (outgoing_direction) ?
6888 _("GetListOfSentMessages request processed by server "
6889 "successfully.\n") :
6890 _("GetListOfReceivedMessages request processed by server "
6891 "successfully.\n")
6893 return err;
6897 /* Get list of outgoing (already sent) messages.
6898 * Any criterion argument can be NULL, if you don't care about it.
6899 * @context is session context. Must not be NULL.
6900 * @from_time is minimal time and date of message sending inclusive.
6901 * @to_time is maximal time and date of message sending inclusive
6902 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
6903 * @status_filter is bit field of isds_message_status values. Use special
6904 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
6905 * all values, you can use bit-wise arithmetic if you want.)
6906 * @offset is index of first message we are interested in. First message is 1.
6907 * Set to 0 (or 1) if you don't care.
6908 * @number is maximal length of list you want to get as input value, outputs
6909 * number of messages matching these criteria. Can be NULL if you don't care
6910 * (applies to output value either).
6911 * @messages is automatically reallocated list of isds_message's. Be ware that
6912 * it returns only brief overview (envelope and some other fields) about each
6913 * message, not the complete message. FIXME: Specify exact fields.
6914 * The list is sorted by delivery time in ascending order.
6915 * Use NULL if you don't care about the meta data (useful if you want to know
6916 * only the @number). If you provide &NULL, list will be allocated on heap,
6917 * if you provide pointer to non-NULL, list will be freed automatically at first.
6918 * Also in case of error the list will be NULLed.
6919 * @return IE_SUCCESS or appropriate error code. */
6920 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
6921 const struct timeval *from_time, const struct timeval *to_time,
6922 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
6923 const unsigned long int offset, unsigned long int *number,
6924 struct isds_list **messages) {
6926 return isds_get_list_of_messages(
6927 context, 1,
6928 from_time, to_time, dmSenderOrgUnitNum, status_filter,
6929 offset, number,
6930 messages);
6934 /* Get list of incoming (addressed to you) messages.
6935 * Any criterion argument can be NULL, if you don't care about it.
6936 * @context is session context. Must not be NULL.
6937 * @from_time is minimal time and date of message sending inclusive.
6938 * @to_time is maximal time and date of message sending inclusive
6939 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
6940 * @status_filter is bit field of isds_message_status values. Use special
6941 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
6942 * all values, you can use bit-wise arithmetic if you want.)
6943 * @offset is index of first message we are interested in. First message is 1.
6944 * Set to 0 (or 1) if you don't care.
6945 * @number is maximal length of list you want to get as input value, outputs
6946 * number of messages matching these criteria. Can be NULL if you don't care
6947 * (applies to output value either).
6948 * @messages is automatically reallocated list of isds_message's. Be ware that
6949 * it returns only brief overview (envelope and some other fields) about each
6950 * message, not the complete message. FIXME: Specify exact fields.
6951 * Use NULL if you don't care about the meta data (useful if you want to know
6952 * only the @number). If you provide &NULL, list will be allocated on heap,
6953 * if you provide pointer to non-NULL, list will be freed automatically at first.
6954 * Also in case of error the list will be NULLed.
6955 * @return IE_SUCCESS or appropriate error code. */
6956 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
6957 const struct timeval *from_time, const struct timeval *to_time,
6958 const long int *dmRecipientOrgUnitNum,
6959 const unsigned int status_filter,
6960 const unsigned long int offset, unsigned long int *number,
6961 struct isds_list **messages) {
6963 return isds_get_list_of_messages(
6964 context, 0,
6965 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
6966 offset, number,
6967 messages);
6971 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
6972 * code
6973 * @context is session context
6974 * @service is ISDS WS service handler
6975 * @service_name is name of SERVICE_DM_OPERATIONS
6976 * @message_id is message ID to send as service argument to ISDS
6977 * @response is server SOAP body response as XML document
6978 * @raw_response is automatically reallocated bit stream with response body. Use
6979 * NULL if you don't care
6980 * @raw_response_length is size of @raw_response in bytes
6981 * @code is ISDS status code
6982 * @status_message is ISDS status message
6983 * @return error coded from lower layer, context message will be set up
6984 * appropriately. */
6985 static isds_error build_send_check_message_request(struct isds_ctx *context,
6986 const isds_service service, const xmlChar *service_name,
6987 const char *message_id,
6988 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
6989 xmlChar **code, xmlChar **status_message) {
6991 isds_error err = IE_SUCCESS;
6992 char *service_name_locale = NULL, *message_id_locale = NULL;
6993 xmlNodePtr request = NULL, node;
6994 xmlNsPtr isds_ns = NULL;
6996 if (!context) return IE_INVALID_CONTEXT;
6997 if (!service_name || !message_id) return IE_INVAL;
6998 if (!response || !code || !status_message) return IE_INVAL;
6999 if (!raw_response_length && raw_response) return IE_INVAL;
7001 /* Free output argument */
7002 xmlFreeDoc(*response); *response = NULL;
7003 if (raw_response) zfree(*raw_response);
7004 free(*code);
7005 free(*status_message);
7008 /* Check if connection is established
7009 * TODO: This check should be done downstairs. */
7010 if (!context->curl) return IE_CONNECTION_CLOSED;
7012 service_name_locale = _isds_utf82locale((char*)service_name);
7013 message_id_locale = _isds_utf82locale(message_id);
7014 if (!service_name_locale || !message_id_locale) {
7015 err = IE_NOMEM;
7016 goto leave;
7019 /* Build request */
7020 request = xmlNewNode(NULL, service_name);
7021 if (!request) {
7022 isds_printf_message(context,
7023 _("Could not build %s request"), service_name_locale);
7024 err = IE_ERROR;
7025 goto leave;
7027 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7028 if(!isds_ns) {
7029 isds_log_message(context, _("Could not create ISDS name space"));
7030 err = IE_ERROR;
7031 goto leave;
7033 xmlSetNs(request, isds_ns);
7036 /* Add requested ID */
7037 err = validate_message_id_length(context, (xmlChar *) message_id);
7038 if (err) goto leave;
7039 INSERT_STRING(request, "dmID", message_id);
7042 isds_log(ILF_ISDS, ILL_DEBUG,
7043 _("Sending %s request for %s message ID to ISDS\n"),
7044 service_name_locale, message_id_locale);
7046 /* Send request */
7047 err = isds(context, service, request, response,
7048 raw_response, raw_response_length);
7049 xmlFreeNode(request); request = NULL;
7051 if (err) {
7052 isds_log(ILF_ISDS, ILL_DEBUG,
7053 _("Processing ISDS response on %s request failed\n"),
7054 service_name_locale);
7055 goto leave;
7058 /* Check for response status */
7059 err = isds_response_status(context, service, *response,
7060 code, status_message, NULL);
7061 if (err) {
7062 isds_log(ILF_ISDS, ILL_DEBUG,
7063 _("ISDS response on %s request is missing status\n"),
7064 service_name_locale);
7065 goto leave;
7068 /* Request processed, but nothing found */
7069 if (xmlStrcmp(*code, BAD_CAST "0000")) {
7070 char *code_locale = _isds_utf82locale((char*) *code);
7071 char *status_message_locale = _isds_utf82locale((char*) *status_message);
7072 isds_log(ILF_ISDS, ILL_DEBUG,
7073 _("Server refused %s request for %s message ID "
7074 "(code=%s, message=%s)\n"),
7075 service_name_locale, message_id_locale,
7076 code_locale, status_message_locale);
7077 isds_log_message(context, status_message_locale);
7078 free(code_locale);
7079 free(status_message_locale);
7080 err = IE_ISDS;
7081 goto leave;
7084 leave:
7085 free(message_id_locale);
7086 free(service_name_locale);
7087 xmlFreeNode(request);
7088 return err;
7092 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
7093 * signed data and free ISDS response.
7094 * @context is session context
7095 * @message_id is UTF-8 encoded message ID for logging purpose
7096 * @response is parsed XML document. It will be freed and NULLed in the middle
7097 * of function run to save memory. This is not guaranteed in case of error.
7098 * @request_name is name of ISDS request used to construct response root
7099 * element name and for logging purpose.
7100 * @raw is reallocated output buffer with DER encoded CMS data
7101 * @raw_length is size of @raw buffer in bytes
7102 * @returns standard error codes, in case of error, @raw will be freed and
7103 * NULLed, @response sometimes. */
7104 static isds_error find_extract_signed_data_free_response(
7105 struct isds_ctx *context, const xmlChar *message_id,
7106 xmlDocPtr *response, const xmlChar *request_name,
7107 void **raw, size_t *raw_length) {
7109 isds_error err = IE_SUCCESS;
7110 char *xpath_expression = NULL;
7111 xmlXPathContextPtr xpath_ctx = NULL;
7112 xmlXPathObjectPtr result = NULL;
7113 char *encoded_structure = NULL;
7115 if (!context) return IE_INVALID_CONTEXT;
7116 if (!raw) return IE_INVAL;
7117 zfree(*raw);
7118 if (!message_id || !response || !*response || !request_name || !raw_length)
7119 return IE_INVAL;
7121 /* Build XPath expression */
7122 xpath_expression = _isds_astrcat3("/isds:", (char *) request_name,
7123 "Response/isds:dmSignature");
7124 if (!xpath_expression) return IE_NOMEM;
7126 /* Extract data */
7127 xpath_ctx = xmlXPathNewContext(*response);
7128 if (!xpath_ctx) {
7129 err = IE_ERROR;
7130 goto leave;
7132 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7133 err = IE_ERROR;
7134 goto leave;
7136 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
7137 if (!result) {
7138 err = IE_ERROR;
7139 goto leave;
7141 /* Empty response */
7142 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7143 char *message_id_locale = _isds_utf82locale((char*) message_id);
7144 isds_printf_message(context,
7145 _("Server did not return any signed data for message ID `%s' "
7146 "on %s request"),
7147 message_id_locale, request_name);
7148 free(message_id_locale);
7149 err = IE_ISDS;
7150 goto leave;
7152 /* More responses */
7153 if (result->nodesetval->nodeNr > 1) {
7154 char *message_id_locale = _isds_utf82locale((char*) message_id);
7155 isds_printf_message(context,
7156 _("Server did return more signed data for message ID `%s' "
7157 "on %s request"),
7158 message_id_locale, request_name);
7159 free(message_id_locale);
7160 err = IE_ISDS;
7161 goto leave;
7163 /* One response */
7164 xpath_ctx->node = result->nodesetval->nodeTab[0];
7166 /* Extract PKCS#7 structure */
7167 EXTRACT_STRING(".", encoded_structure);
7168 if (!encoded_structure) {
7169 isds_log_message(context, _("dmSignature element is empty"));
7172 /* Here we have delivery info as standalone CMS in encoded_structure.
7173 * We don't need any other data, free them: */
7174 xmlXPathFreeObject(result); result = NULL;
7175 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
7176 xmlFreeDoc(*response); *response = NULL;
7179 /* Decode PKCS#7 to DER format */
7180 *raw_length = _isds_b64decode(encoded_structure, raw);
7181 if (*raw_length == (size_t) -1) {
7182 isds_log_message(context,
7183 _("Error while Base64-decoding PKCS#7 structure"));
7184 err = IE_ERROR;
7185 goto leave;
7188 leave:
7189 if (err) {
7190 zfree(*raw);
7191 raw_length = 0;
7194 free(encoded_structure);
7195 xmlXPathFreeObject(result);
7196 xmlXPathFreeContext(xpath_ctx);
7197 free(xpath_expression);
7199 return err;
7203 /* Download incoming message envelope identified by ID.
7204 * @context is session context
7205 * @message_id is message identifier (you can get them from
7206 * isds_get_list_of_received_messages())
7207 * @message is automatically reallocated message retrieved from ISDS.
7208 * It will miss documents per se. Use isds_get_received_message(), if you are
7209 * interested in documents (content) too.
7210 * Returned hash and timestamp require documents to be verifiable. */
7211 isds_error isds_get_received_envelope(struct isds_ctx *context,
7212 const char *message_id, struct isds_message **message) {
7214 isds_error err = IE_SUCCESS;
7215 xmlDocPtr response = NULL;
7216 xmlChar *code = NULL, *status_message = NULL;
7217 xmlXPathContextPtr xpath_ctx = NULL;
7218 xmlXPathObjectPtr result = NULL;
7220 if (!context) return IE_INVALID_CONTEXT;
7221 zfree(context->long_message);
7223 /* Free former message if any */
7224 if (!message) return IE_INVAL;
7225 isds_message_free(message);
7227 /* Do request and check for success */
7228 err = build_send_check_message_request(context, SERVICE_DM_INFO,
7229 BAD_CAST "MessageEnvelopeDownload", message_id,
7230 &response, NULL, NULL, &code, &status_message);
7231 if (err) goto leave;
7233 /* Extract data */
7234 xpath_ctx = xmlXPathNewContext(response);
7235 if (!xpath_ctx) {
7236 err = IE_ERROR;
7237 goto leave;
7239 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7240 err = IE_ERROR;
7241 goto leave;
7243 result = xmlXPathEvalExpression(
7244 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
7245 "isds:dmReturnedMessageEnvelope",
7246 xpath_ctx);
7247 if (!result) {
7248 err = IE_ERROR;
7249 goto leave;
7251 /* Empty response */
7252 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7253 char *message_id_locale = _isds_utf82locale((char*) message_id);
7254 isds_printf_message(context,
7255 _("Server did not return any envelope for ID `%s' "
7256 "on MessageEnvelopeDownload request"), message_id_locale);
7257 free(message_id_locale);
7258 err = IE_ISDS;
7259 goto leave;
7261 /* More envelops */
7262 if (result->nodesetval->nodeNr > 1) {
7263 char *message_id_locale = _isds_utf82locale((char*) message_id);
7264 isds_printf_message(context,
7265 _("Server did return more envelopes for ID `%s' "
7266 "on MessageEnvelopeDownload request"), message_id_locale);
7267 free(message_id_locale);
7268 err = IE_ISDS;
7269 goto leave;
7271 /* One message */
7272 xpath_ctx->node = result->nodesetval->nodeTab[0];
7274 /* Extract the envelope (= message without documents, hence 0) */
7275 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
7276 if (err) goto leave;
7278 /* Save XML blob */
7279 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
7280 &(*message)->raw_length);
7282 leave:
7283 if (err) {
7284 isds_message_free(message);
7287 xmlXPathFreeObject(result);
7288 xmlXPathFreeContext(xpath_ctx);
7290 free(code);
7291 free(status_message);
7292 if (!*message || !(*message)->xml) {
7293 xmlFreeDoc(response);
7296 if (!err)
7297 isds_log(ILF_ISDS, ILL_DEBUG,
7298 _("MessageEnvelopeDownload request processed by server "
7299 "successfully.\n")
7301 return err;
7305 /* Load delivery info of any format from buffer.
7306 * @context is session context
7307 * @raw_type advertises format of @buffer content. Only delivery info types
7308 * are accepted.
7309 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
7310 * retrieve such data from message->raw after calling
7311 * isds_get_signed_delivery_info().
7312 * @length is length of buffer in bytes.
7313 * @message is automatically reallocated message parsed from @buffer.
7314 * @strategy selects how buffer will be attached into raw isds_message member.
7315 * */
7316 isds_error isds_load_delivery_info(struct isds_ctx *context,
7317 const isds_raw_type raw_type,
7318 const void *buffer, const size_t length,
7319 struct isds_message **message, const isds_buffer_strategy strategy) {
7321 isds_error err = IE_SUCCESS;
7322 message_ns_type message_ns;
7323 xmlDocPtr message_doc = NULL;
7324 xmlXPathContextPtr xpath_ctx = NULL;
7325 xmlXPathObjectPtr result = NULL;
7326 void *xml_stream = NULL;
7327 size_t xml_stream_length = 0;
7329 if (!context) return IE_INVALID_CONTEXT;
7330 zfree(context->long_message);
7331 if (!message) return IE_INVAL;
7332 isds_message_free(message);
7333 if (!buffer) return IE_INVAL;
7336 /* Select buffer format and extract XML from CMS*/
7337 switch (raw_type) {
7338 case RAWTYPE_DELIVERYINFO:
7339 message_ns = MESSAGE_NS_UNSIGNED;
7340 xml_stream = (void *) buffer;
7341 xml_stream_length = length;
7342 break;
7344 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
7345 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
7346 xml_stream = (void *) buffer;
7347 xml_stream_length = length;
7348 break;
7350 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
7351 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
7352 err = _isds_extract_cms_data(context, buffer, length,
7353 &xml_stream, &xml_stream_length);
7354 if (err) goto leave;
7355 break;
7357 default:
7358 isds_log_message(context, _("Bad raw delivery representation type"));
7359 return IE_INVAL;
7360 break;
7363 isds_log(ILF_ISDS, ILL_DEBUG,
7364 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
7365 xml_stream_length, xml_stream);
7367 /* Convert delivery info XML stream into XPath context */
7368 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
7369 if (!message_doc) {
7370 err = IE_XML;
7371 goto leave;
7373 xpath_ctx = xmlXPathNewContext(message_doc);
7374 if (!xpath_ctx) {
7375 err = IE_ERROR;
7376 goto leave;
7378 /* XXX: Name spaces mangled for signed delivery info:
7379 * http://isds.czechpoint.cz/v20/delivery:
7381 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
7382 * <q:dmDelivery>
7383 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
7384 * <p:dmID>170272</p:dmID>
7385 * ...
7386 * </p:dmDm>
7387 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
7388 * ...
7389 * </q:dmEvents>...</q:dmEvents>
7390 * </q:dmDelivery>
7391 * </q:GetDeliveryInfoResponse>
7392 * */
7393 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
7394 err = IE_ERROR;
7395 goto leave;
7397 result = xmlXPathEvalExpression(
7398 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
7399 xpath_ctx);
7400 if (!result) {
7401 err = IE_ERROR;
7402 goto leave;
7404 /* Empty delivery info */
7405 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7406 isds_printf_message(context,
7407 _("XML document is not sisds:dmDelivery document"));
7408 err = IE_ISDS;
7409 goto leave;
7411 /* More delivery info's */
7412 if (result->nodesetval->nodeNr > 1) {
7413 isds_printf_message(context,
7414 _("XML document has more sisds:dmDelivery elements"));
7415 err = IE_ISDS;
7416 goto leave;
7418 /* One delivery info */
7419 xpath_ctx->node = result->nodesetval->nodeTab[0];
7421 /* Extract the envelope (= message without documents, hence 0).
7422 * XXX: extract_TReturnedMessage() can obtain attachments size,
7423 * but delivery info carries none. It's coded as option elements,
7424 * so it should work. */
7425 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
7426 if (err) goto leave;
7428 /* Extract events */
7429 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
7430 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
7431 if (err) { err = IE_ERROR; goto leave; }
7432 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
7433 if (err) goto leave;
7435 /* Append raw CMS structure into message */
7436 (*message)->raw_type = raw_type;
7437 switch (strategy) {
7438 case BUFFER_DONT_STORE:
7439 break;
7440 case BUFFER_COPY:
7441 (*message)->raw = malloc(length);
7442 if (!(*message)->raw) {
7443 err = IE_NOMEM;
7444 goto leave;
7446 memcpy((*message)->raw, buffer, length);
7447 (*message)->raw_length = length;
7448 break;
7449 case BUFFER_MOVE:
7450 (*message)->raw = (void *) buffer;
7451 (*message)->raw_length = length;
7452 break;
7453 default:
7454 err = IE_ENUM;
7455 goto leave;
7458 leave:
7459 if (err) {
7460 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
7461 isds_message_free(message);
7464 xmlXPathFreeObject(result);
7465 xmlXPathFreeContext(xpath_ctx);
7466 if (!*message || !(*message)->xml) {
7467 xmlFreeDoc(message_doc);
7469 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
7471 if (!err)
7472 isds_log(ILF_ISDS, ILL_DEBUG,
7473 _("Delivery info loaded successfully.\n"));
7474 return err;
7478 /* Download signed delivery info-sheet of given message identified by ID.
7479 * @context is session context
7480 * @message_id is message identifier (you can get them from
7481 * isds_get_list_of_{sent,received}_messages())
7482 * @message is automatically reallocated message retrieved from ISDS.
7483 * It will miss documents per se. Use isds_get_signed_received_message(),
7484 * if you are interested in documents (content). OTOH, only this function
7485 * can get list events message has gone through. */
7486 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
7487 const char *message_id, struct isds_message **message) {
7489 isds_error err = IE_SUCCESS;
7490 xmlDocPtr response = NULL;
7491 xmlChar *code = NULL, *status_message = NULL;
7492 void *raw = NULL;
7493 size_t raw_length = 0;
7495 if (!context) return IE_INVALID_CONTEXT;
7496 zfree(context->long_message);
7498 /* Free former message if any */
7499 if (!message) return IE_INVAL;
7500 isds_message_free(message);
7502 /* Do request and check for success */
7503 err = build_send_check_message_request(context, SERVICE_DM_INFO,
7504 BAD_CAST "GetSignedDeliveryInfo", message_id,
7505 &response, NULL, NULL, &code, &status_message);
7506 if (err) goto leave;
7508 /* Find signed delivery info, extract it into raw and maybe free
7509 * response */
7510 err = find_extract_signed_data_free_response(context,
7511 (xmlChar *)message_id, &response,
7512 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
7513 if (err) goto leave;
7515 /* Parse delivery info */
7516 err = isds_load_delivery_info(context,
7517 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
7518 message, BUFFER_MOVE);
7519 if (err) goto leave;
7521 raw = NULL;
7523 leave:
7524 if (err) {
7525 isds_message_free(message);
7528 free(raw);
7529 free(code);
7530 free(status_message);
7531 xmlFreeDoc(response);
7533 if (!err)
7534 isds_log(ILF_ISDS, ILL_DEBUG,
7535 _("GetSignedDeliveryInfo request processed by server "
7536 "successfully.\n")
7538 return err;
7542 /* Download delivery info-sheet of given message identified by ID.
7543 * @context is session context
7544 * @message_id is message identifier (you can get them from
7545 * isds_get_list_of_{sent,received}_messages())
7546 * @message is automatically reallocated message retrieved from ISDS.
7547 * It will miss documents per se. Use isds_get_received_message(), if you are
7548 * interested in documents (content). OTOH, only this function can get list
7549 * of events message has gone through. */
7550 isds_error isds_get_delivery_info(struct isds_ctx *context,
7551 const char *message_id, struct isds_message **message) {
7553 isds_error err = IE_SUCCESS;
7554 xmlDocPtr response = NULL;
7555 xmlChar *code = NULL, *status_message = NULL;
7556 xmlNodePtr delivery_node = NULL;
7557 void *raw = NULL;
7558 size_t raw_length = 0;
7560 if (!context) return IE_INVALID_CONTEXT;
7561 zfree(context->long_message);
7563 /* Free former message if any */
7564 if (!message) return IE_INVAL;
7565 isds_message_free(message);
7567 /* Do request and check for success */
7568 err = build_send_check_message_request(context, SERVICE_DM_INFO,
7569 BAD_CAST "GetDeliveryInfo", message_id,
7570 &response, NULL, NULL, &code, &status_message);
7571 if (err) goto leave;
7574 /* Serialize delivery info */
7575 delivery_node = xmlDocGetRootElement(response);
7576 if (!delivery_node) {
7577 char *message_id_locale = _isds_utf82locale((char*) message_id);
7578 isds_printf_message(context,
7579 _("Server did not return any delivery info for ID `%s' "
7580 "on GetDeliveryInfo request"), message_id_locale);
7581 free(message_id_locale);
7582 err = IE_ISDS;
7583 goto leave;
7585 err = serialize_subtree(context, delivery_node, &raw, &raw_length);
7586 if (err) goto leave;
7588 /* Parse delivery info */
7589 /* TODO: Here we parse the response second time. We could single delivery
7590 * parser from isds_load_delivery_info() to make things faster. */
7591 err = isds_load_delivery_info(context,
7592 RAWTYPE_DELIVERYINFO, raw, raw_length,
7593 message, BUFFER_MOVE);
7594 if (err) goto leave;
7596 raw = NULL;
7599 leave:
7600 if (err) {
7601 isds_message_free(message);
7604 free(raw);
7605 free(code);
7606 free(status_message);
7607 xmlFreeDoc(response);
7609 if (!err)
7610 isds_log(ILF_ISDS, ILL_DEBUG,
7611 _("GetDeliveryInfo request processed by server "
7612 "successfully.\n")
7614 return err;
7618 /* Download incoming message identified by ID.
7619 * @context is session context
7620 * @message_id is message identifier (you can get them from
7621 * isds_get_list_of_received_messages())
7622 * @message is automatically reallocated message retrieved from ISDS */
7623 isds_error isds_get_received_message(struct isds_ctx *context,
7624 const char *message_id, struct isds_message **message) {
7626 isds_error err = IE_SUCCESS;
7627 xmlDocPtr response = NULL;
7628 void *xml_stream = NULL;
7629 size_t xml_stream_length;
7630 xmlChar *code = NULL, *status_message = NULL;
7631 xmlXPathContextPtr xpath_ctx = NULL;
7632 xmlXPathObjectPtr result = NULL;
7633 char *phys_path = NULL;
7634 size_t phys_start, phys_end;
7636 if (!context) return IE_INVALID_CONTEXT;
7637 zfree(context->long_message);
7639 /* Free former message if any */
7640 if (message) isds_message_free(message);
7642 /* Do request and check for success */
7643 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
7644 BAD_CAST "MessageDownload", message_id,
7645 &response, &xml_stream, &xml_stream_length,
7646 &code, &status_message);
7647 if (err) goto leave;
7649 /* Extract data */
7650 xpath_ctx = xmlXPathNewContext(response);
7651 if (!xpath_ctx) {
7652 err = IE_ERROR;
7653 goto leave;
7655 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7656 err = IE_ERROR;
7657 goto leave;
7659 result = xmlXPathEvalExpression(
7660 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
7661 xpath_ctx);
7662 if (!result) {
7663 err = IE_ERROR;
7664 goto leave;
7666 /* Empty response */
7667 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7668 char *message_id_locale = _isds_utf82locale((char*) message_id);
7669 isds_printf_message(context,
7670 _("Server did not return any message for ID `%s' "
7671 "on MessageDownload request"), message_id_locale);
7672 free(message_id_locale);
7673 err = IE_ISDS;
7674 goto leave;
7676 /* More messages */
7677 if (result->nodesetval->nodeNr > 1) {
7678 char *message_id_locale = _isds_utf82locale((char*) message_id);
7679 isds_printf_message(context,
7680 _("Server did return more messages for ID `%s' "
7681 "on MessageDownload request"), message_id_locale);
7682 free(message_id_locale);
7683 err = IE_ISDS;
7684 goto leave;
7686 /* One message */
7687 xpath_ctx->node = result->nodesetval->nodeTab[0];
7689 /* Extract the message */
7690 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
7691 if (err) goto leave;
7693 /* Locate raw XML blob */
7694 phys_path = strdup(
7695 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
7696 PHYSXML_ELEMENT_SEPARATOR
7697 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
7698 PHYSXML_ELEMENT_SEPARATOR
7699 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
7701 if (!phys_path) {
7702 err = IE_NOMEM;
7703 goto leave;
7705 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
7706 phys_path, &phys_start, &phys_end);
7707 zfree(phys_path);
7708 if (err) {
7709 isds_log_message(context,
7710 _("Substring with isds:MessageDownloadResponse element "
7711 "could not be located in raw SOAP message"));
7712 goto leave;
7714 /* Save XML blob */
7715 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
7716 &(*message)->raw_length);*/
7717 /* TODO: Store name space declarations from ancestors */
7718 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
7719 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
7720 (*message)->raw_length = phys_end - phys_start + 1;
7721 (*message)->raw = malloc((*message)->raw_length);
7722 if (!(*message)->raw) {
7723 err = IE_NOMEM;
7724 goto leave;
7726 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
7729 leave:
7730 if (err) {
7731 isds_message_free(message);
7734 free(phys_path);
7736 xmlXPathFreeObject(result);
7737 xmlXPathFreeContext(xpath_ctx);
7739 free(code);
7740 free(status_message);
7741 free(xml_stream);
7742 if (!*message || !(*message)->xml) {
7743 xmlFreeDoc(response);
7746 if (!err)
7747 isds_log(ILF_ISDS, ILL_DEBUG,
7748 _("MessageDownload request processed by server "
7749 "successfully.\n")
7751 return err;
7755 /* Load message of any type from buffer.
7756 * @context is session context
7757 * @raw_type defines content type of @buffer. Only message types are allowed.
7758 * @buffer is message raw representation. Format (CMS, plain signed,
7759 * message direction) is defined in @raw_type. You can retrieve such data
7760 * from message->raw after calling isds_get_[signed]{received,sent}_message().
7761 * @length is length of buffer in bytes.
7762 * @message is automatically reallocated message parsed from @buffer.
7763 * @strategy selects how buffer will be attached into raw isds_message member.
7764 * */
7765 isds_error isds_load_message(struct isds_ctx *context,
7766 const isds_raw_type raw_type, const void *buffer, const size_t length,
7767 struct isds_message **message, const isds_buffer_strategy strategy) {
7769 isds_error err = IE_SUCCESS;
7770 void *xml_stream = NULL;
7771 size_t xml_stream_length = 0;
7772 message_ns_type message_ns;
7773 xmlDocPtr message_doc = NULL;
7774 xmlXPathContextPtr xpath_ctx = NULL;
7775 xmlXPathObjectPtr result = NULL;
7777 if (!context) return IE_INVALID_CONTEXT;
7778 zfree(context->long_message);
7779 if (!message) return IE_INVAL;
7780 isds_message_free(message);
7781 if (!buffer) return IE_INVAL;
7784 /* Select buffer format and extract XML from CMS*/
7785 switch (raw_type) {
7786 case RAWTYPE_INCOMING_MESSAGE:
7787 message_ns = MESSAGE_NS_UNSIGNED;
7788 xml_stream = (void *) buffer;
7789 xml_stream_length = length;
7790 break;
7792 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
7793 message_ns = MESSAGE_NS_SIGNED_INCOMING;
7794 xml_stream = (void *) buffer;
7795 xml_stream_length = length;
7796 break;
7798 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
7799 message_ns = MESSAGE_NS_SIGNED_INCOMING;
7800 err = _isds_extract_cms_data(context, buffer, length,
7801 &xml_stream, &xml_stream_length);
7802 if (err) goto leave;
7803 break;
7805 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
7806 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
7807 xml_stream = (void *) buffer;
7808 xml_stream_length = length;
7809 break;
7811 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
7812 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
7813 err = _isds_extract_cms_data(context, buffer, length,
7814 &xml_stream, &xml_stream_length);
7815 if (err) goto leave;
7816 break;
7818 default:
7819 isds_log_message(context, _("Bad raw message representation type"));
7820 return IE_INVAL;
7821 break;
7824 isds_log(ILF_ISDS, ILL_DEBUG,
7825 _("Loading message:\n%.*s\nEnd of message\n"),
7826 xml_stream_length, xml_stream);
7828 /* Convert messages XML stream into XPath context */
7829 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
7830 if (!message_doc) {
7831 err = IE_XML;
7832 goto leave;
7834 xpath_ctx = xmlXPathNewContext(message_doc);
7835 if (!xpath_ctx) {
7836 err = IE_ERROR;
7837 goto leave;
7839 /* XXX: Standard name space for unsigned incoming direction:
7840 * http://isds.czechpoint.cz/v20/
7842 * XXX: Name spaces mangled for signed outgoing direction:
7843 * http://isds.czechpoint.cz/v20/SentMessage:
7845 * <q:MessageDownloadResponse
7846 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
7847 * <q:dmReturnedMessage>
7848 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
7849 * <p:dmID>151916</p:dmID>
7850 * ...
7851 * </p:dmDm>
7852 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
7853 * ...
7854 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
7855 * </q:dmReturnedMessage>
7856 * </q:MessageDownloadResponse>
7858 * XXX: Name spaces mangled for signed incoming direction:
7859 * http://isds.czechpoint.cz/v20/message:
7861 * <q:MessageDownloadResponse
7862 * xmlns:q="http://isds.czechpoint.cz/v20/message">
7863 * <q:dmReturnedMessage>
7864 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
7865 * <p:dmID>151916</p:dmID>
7866 * ...
7867 * </p:dmDm>
7868 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
7869 * ...
7870 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
7871 * </q:dmReturnedMessage>
7872 * </q:MessageDownloadResponse>
7874 * Stupidity of ISDS developers is unlimited */
7875 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
7876 err = IE_ERROR;
7877 goto leave;
7879 result = xmlXPathEvalExpression(
7880 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
7881 xpath_ctx);
7882 if (!result) {
7883 err = IE_ERROR;
7884 goto leave;
7886 /* Empty message */
7887 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7888 isds_printf_message(context,
7889 _("XML document does not contain "
7890 "sisds:dmReturnedMessage element"));
7891 err = IE_ISDS;
7892 goto leave;
7894 /* More messages */
7895 if (result->nodesetval->nodeNr > 1) {
7896 isds_printf_message(context,
7897 _("XML document has more sisds:dmReturnedMessage elements"));
7898 err = IE_ISDS;
7899 goto leave;
7901 /* One message */
7902 xpath_ctx->node = result->nodesetval->nodeTab[0];
7904 /* Extract the message */
7905 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
7906 if (err) goto leave;
7908 /* Append raw buffer into message */
7909 (*message)->raw_type = raw_type;
7910 switch (strategy) {
7911 case BUFFER_DONT_STORE:
7912 break;
7913 case BUFFER_COPY:
7914 (*message)->raw = malloc(length);
7915 if (!(*message)->raw) {
7916 err = IE_NOMEM;
7917 goto leave;
7919 memcpy((*message)->raw, buffer, length);
7920 (*message)->raw_length = length;
7921 break;
7922 case BUFFER_MOVE:
7923 (*message)->raw = (void *) buffer;
7924 (*message)->raw_length = length;
7925 break;
7926 default:
7927 err = IE_ENUM;
7928 goto leave;
7932 leave:
7933 if (err) {
7934 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
7935 isds_message_free(message);
7938 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
7939 xmlXPathFreeObject(result);
7940 xmlXPathFreeContext(xpath_ctx);
7941 if (!*message || !(*message)->xml) {
7942 xmlFreeDoc(message_doc);
7945 if (!err)
7946 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
7947 return err;
7951 /* Determine type of raw message or delivery info according some heuristics.
7952 * It does not validate the raw blob.
7953 * @context is session context
7954 * @raw_type returns content type of @buffer. Valid only if exit code of this
7955 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
7956 * reallocated memory.
7957 * @buffer is message raw representation.
7958 * @length is length of buffer in bytes. */
7959 isds_error isds_guess_raw_type(struct isds_ctx *context,
7960 isds_raw_type *raw_type, const void *buffer, const size_t length) {
7961 isds_error err;
7962 void *xml_stream = NULL;
7963 size_t xml_stream_length = 0;
7964 xmlDocPtr document = NULL;
7965 xmlNodePtr root = NULL;
7967 if (!context) return IE_INVALID_CONTEXT;
7968 zfree(context->long_message);
7969 if (length == 0 || !buffer) return IE_INVAL;
7970 if (!raw_type) return IE_INVAL;
7972 /* Try CMS */
7973 err = _isds_extract_cms_data(context, buffer, length,
7974 &xml_stream, &xml_stream_length);
7975 if (err) {
7976 xml_stream = (void *) buffer;
7977 xml_stream_length = (size_t) length;
7978 err = IE_SUCCESS;
7981 /* Try XML */
7982 document = xmlParseMemory(xml_stream, xml_stream_length);
7983 if (!document) {
7984 isds_printf_message(context,
7985 _("Could not parse data as XML document"));
7986 err = IE_NOTSUP;
7987 goto leave;
7990 /* Get root element */
7991 root = xmlDocGetRootElement(document);
7992 if (!root) {
7993 isds_printf_message(context,
7994 _("XML document is missing root element"));
7995 err = IE_XML;
7996 goto leave;
7999 if (!root->ns || !root->ns->href) {
8000 isds_printf_message(context,
8001 _("Root element does not belong to any name space"));
8002 err = IE_NOTSUP;
8003 goto leave;
8006 /* Test name space */
8007 if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_INCOMING_NS)) {
8008 if (xml_stream == buffer)
8009 *raw_type = RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE;
8010 else
8011 *raw_type = RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
8012 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_OUTGOING_NS)) {
8013 if (xml_stream == buffer)
8014 *raw_type = RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE;
8015 else
8016 *raw_type = RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE;
8017 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_DELIVERY_NS)) {
8018 if (xml_stream == buffer)
8019 *raw_type = RAWTYPE_PLAIN_SIGNED_DELIVERYINFO;
8020 else
8021 *raw_type = RAWTYPE_CMS_SIGNED_DELIVERYINFO;
8022 } else if (!xmlStrcmp(root->ns->href, BAD_CAST ISDS_NS)) {
8023 if (xml_stream != buffer) {
8024 isds_printf_message(context,
8025 _("Document in ISDS name space is encapsulated into CMS" ));
8026 err = IE_NOTSUP;
8027 } else if (!xmlStrcmp(root->name, BAD_CAST "MessageDownloadResponse"))
8028 *raw_type = RAWTYPE_INCOMING_MESSAGE;
8029 else if (!xmlStrcmp(root->name, BAD_CAST "GetDeliveryInfoResponse"))
8030 *raw_type = RAWTYPE_DELIVERYINFO;
8031 else {
8032 isds_printf_message(context,
8033 _("Unknown root element in ISDS name space"));
8034 err = IE_NOTSUP;
8036 } else {
8037 isds_printf_message(context,
8038 _("Unknown name space"));
8039 err = IE_NOTSUP;
8042 leave:
8043 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
8044 xmlFreeDoc(document);
8045 return err;
8049 /* Download signed incoming/outgoing message identified by ID.
8050 * @context is session context
8051 * @output is true for outgoing message, false for incoming message
8052 * @message_id is message identifier (you can get them from
8053 * isds_get_list_of_{sent,received}_messages())
8054 * @message is automatically reallocated message retrieved from ISDS. The raw
8055 * member will be filled with PKCS#7 structure in DER format. */
8056 static isds_error isds_get_signed_message(struct isds_ctx *context,
8057 const _Bool outgoing, const char *message_id,
8058 struct isds_message **message) {
8060 isds_error err = IE_SUCCESS;
8061 xmlDocPtr response = NULL;
8062 xmlChar *code = NULL, *status_message = NULL;
8063 xmlXPathContextPtr xpath_ctx = NULL;
8064 xmlXPathObjectPtr result = NULL;
8065 char *encoded_structure = NULL;
8066 void *raw = NULL;
8067 size_t raw_length = 0;
8069 if (!context) return IE_INVALID_CONTEXT;
8070 zfree(context->long_message);
8071 if (!message) return IE_INVAL;
8072 isds_message_free(message);
8074 /* Do request and check for success */
8075 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
8076 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
8077 BAD_CAST "SignedMessageDownload",
8078 message_id, &response, NULL, NULL, &code, &status_message);
8079 if (err) goto leave;
8081 /* Find signed message, extract it into raw and maybe free
8082 * response */
8083 err = find_extract_signed_data_free_response(context,
8084 (xmlChar *)message_id, &response,
8085 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
8086 BAD_CAST "SignedMessageDownload",
8087 &raw, &raw_length);
8088 if (err) goto leave;
8090 /* Parse message */
8091 err = isds_load_message(context,
8092 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
8093 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
8094 raw, raw_length, message, BUFFER_MOVE);
8095 if (err) goto leave;
8097 raw = NULL;
8099 leave:
8100 if (err) {
8101 isds_message_free(message);
8104 free(encoded_structure);
8105 xmlXPathFreeObject(result);
8106 xmlXPathFreeContext(xpath_ctx);
8107 free(raw);
8109 free(code);
8110 free(status_message);
8111 xmlFreeDoc(response);
8113 if (!err)
8114 isds_log(ILF_ISDS, ILL_DEBUG,
8115 (outgoing) ?
8116 _("SignedSentMessageDownload request processed by server "
8117 "successfully.\n") :
8118 _("SignedMessageDownload request processed by server "
8119 "successfully.\n")
8121 return err;
8125 /* Download signed incoming message identified by ID.
8126 * @context is session context
8127 * @message_id is message identifier (you can get them from
8128 * isds_get_list_of_received_messages())
8129 * @message is automatically reallocated message retrieved from ISDS. The raw
8130 * member will be filled with PKCS#7 structure in DER format. */
8131 isds_error isds_get_signed_received_message(struct isds_ctx *context,
8132 const char *message_id, struct isds_message **message) {
8133 return isds_get_signed_message(context, 0, message_id, message);
8137 /* Download signed outgoing message identified by ID.
8138 * @context is session context
8139 * @message_id is message identifier (you can get them from
8140 * isds_get_list_of_sent_messages())
8141 * @message is automatically reallocated message retrieved from ISDS. The raw
8142 * member will be filled with PKCS#7 structure in DER format. */
8143 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
8144 const char *message_id, struct isds_message **message) {
8145 return isds_get_signed_message(context, 1, message_id, message);
8149 /* Retrieve hash of message identified by ID stored in ISDS.
8150 * @context is session context
8151 * @message_id is message identifier
8152 * @hash is automatically reallocated message hash downloaded from ISDS.
8153 * Message must exist in system and must not be deleted. */
8154 isds_error isds_download_message_hash(struct isds_ctx *context,
8155 const char *message_id, struct isds_hash **hash) {
8157 isds_error err = IE_SUCCESS;
8158 xmlDocPtr response = NULL;
8159 xmlChar *code = NULL, *status_message = NULL;
8160 xmlXPathContextPtr xpath_ctx = NULL;
8161 xmlXPathObjectPtr result = NULL;
8163 if (!context) return IE_INVALID_CONTEXT;
8164 zfree(context->long_message);
8166 isds_hash_free(hash);
8168 err = build_send_check_message_request(context, SERVICE_DM_INFO,
8169 BAD_CAST "VerifyMessage", message_id,
8170 &response, NULL, NULL, &code, &status_message);
8171 if (err) goto leave;
8174 /* Extract data */
8175 xpath_ctx = xmlXPathNewContext(response);
8176 if (!xpath_ctx) {
8177 err = IE_ERROR;
8178 goto leave;
8180 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8181 err = IE_ERROR;
8182 goto leave;
8184 result = xmlXPathEvalExpression(
8185 BAD_CAST "/isds:VerifyMessageResponse",
8186 xpath_ctx);
8187 if (!result) {
8188 err = IE_ERROR;
8189 goto leave;
8191 /* Empty response */
8192 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8193 char *message_id_locale = _isds_utf82locale((char*) message_id);
8194 isds_printf_message(context,
8195 _("Server did not return any response for ID `%s' "
8196 "on VerifyMessage request"), message_id_locale);
8197 free(message_id_locale);
8198 err = IE_ISDS;
8199 goto leave;
8201 /* More responses */
8202 if (result->nodesetval->nodeNr > 1) {
8203 char *message_id_locale = _isds_utf82locale((char*) message_id);
8204 isds_printf_message(context,
8205 _("Server did return more responses for ID `%s' "
8206 "on VerifyMessage request"), message_id_locale);
8207 free(message_id_locale);
8208 err = IE_ISDS;
8209 goto leave;
8211 /* One response */
8212 xpath_ctx->node = result->nodesetval->nodeTab[0];
8214 /* Extract the hash */
8215 err = find_and_extract_DmHash(context, hash, xpath_ctx);
8217 leave:
8218 if (err) {
8219 isds_hash_free(hash);
8222 xmlXPathFreeObject(result);
8223 xmlXPathFreeContext(xpath_ctx);
8225 free(code);
8226 free(status_message);
8227 xmlFreeDoc(response);
8229 if (!err)
8230 isds_log(ILF_ISDS, ILL_DEBUG,
8231 _("VerifyMessage request processed by server "
8232 "successfully.\n")
8234 return err;
8238 /* Mark message as read. This is a transactional commit function to acknowledge
8239 * to ISDS the message has been downloaded and processed by client properly.
8240 * @context is session context
8241 * @message_id is message identifier. */
8242 isds_error isds_mark_message_read(struct isds_ctx *context,
8243 const char *message_id) {
8245 isds_error err = IE_SUCCESS;
8246 xmlDocPtr response = NULL;
8247 xmlChar *code = NULL, *status_message = NULL;
8249 if (!context) return IE_INVALID_CONTEXT;
8250 zfree(context->long_message);
8252 /* Do request and check for success */
8253 err = build_send_check_message_request(context, SERVICE_DM_INFO,
8254 BAD_CAST "MarkMessageAsDownloaded", message_id,
8255 &response, NULL, NULL, &code, &status_message);
8257 free(code);
8258 free(status_message);
8259 xmlFreeDoc(response);
8261 if (!err)
8262 isds_log(ILF_ISDS, ILL_DEBUG,
8263 _("MarkMessageAsDownloaded request processed by server "
8264 "successfully.\n")
8266 return err;
8270 /* Mark message as received by recipient. This is applicable only to
8271 * commercial message. Use envelope->dmType message member to distinguish
8272 * commercial message from government message. Government message is
8273 * received automatically (by law), commercial message on recipient request.
8274 * @context is session context
8275 * @message_id is message identifier. */
8276 isds_error isds_mark_message_received(struct isds_ctx *context,
8277 const char *message_id) {
8279 isds_error err = IE_SUCCESS;
8280 xmlDocPtr response = NULL;
8281 xmlChar *code = NULL, *status_message = NULL;
8283 if (!context) return IE_INVALID_CONTEXT;
8284 zfree(context->long_message);
8286 /* Do request and check for success */
8287 err = build_send_check_message_request(context, SERVICE_DM_INFO,
8288 BAD_CAST "ConfirmDelivery", message_id,
8289 &response, NULL, NULL, &code, &status_message);
8291 free(code);
8292 free(status_message);
8293 xmlFreeDoc(response);
8295 if (!err)
8296 isds_log(ILF_ISDS, ILL_DEBUG,
8297 _("ConfirmDelivery request processed by server "
8298 "successfully.\n")
8300 return err;
8304 /* Send document for authorize conversion into Czech POINT system.
8305 * This is public anonymous service, no log-in necessary. Special context is
8306 * used to reuse keep-a-live HTTPS connection.
8307 * @context is Czech POINT session context. DO NOT use context connected to
8308 * ISDS server. Use new context or context used by this function previously.
8309 * @document is document to convert. Only data, data_length, dmFileDescr and
8310 * is_xml members are significant. Be ware that not all document formats can be
8311 * converted (signed PDF 1.3 and higher only (2010-02 state)).
8312 * @id is reallocated identifier assigned by Czech POINT system to
8313 * your document on submit. Use is to tell it to Czech POINT officer.
8314 * @date is reallocated document submit date (submitted documents
8315 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
8316 * value. */
8317 isds_error czp_convert_document(struct isds_ctx *context,
8318 const struct isds_document *document,
8319 char **id, struct tm **date) {
8320 isds_error err = IE_SUCCESS;
8321 xmlNsPtr deposit_ns = NULL, empty_ns = NULL;
8322 xmlNodePtr request = NULL, node;
8323 xmlDocPtr response = NULL;
8325 xmlXPathContextPtr xpath_ctx = NULL;
8326 xmlXPathObjectPtr result = NULL;
8327 long int status = -1;
8328 long int *status_ptr = &status;
8329 char *string = NULL;
8332 if (!context) return IE_INVALID_CONTEXT;
8333 zfree(context->long_message);
8334 if (!document || !id || !date) return IE_INVAL;
8336 if (document->is_xml) {
8337 isds_log_message(context,
8338 _("XML documents cannot be sent to conversion"));
8339 return IE_NOTSUP;
8342 /* Free output arguments */
8343 zfree(*id);
8344 zfree(*date);
8346 /* Store configuration */
8347 context->type = CTX_TYPE_CZP;
8348 free(context->url);
8349 context->url = strdup("https://www.czechpoint.cz/uschovna/services.php");
8350 if (!(context->url))
8351 return IE_NOMEM;
8353 /* Prepare CURL handle if not yet connected */
8354 if (!context->curl) {
8355 context->curl = curl_easy_init();
8356 if (!(context->curl))
8357 return IE_ERROR;
8360 /* Build conversion request */
8361 request = xmlNewNode(NULL, BAD_CAST "saveDocument");
8362 if (!request) {
8363 isds_log_message(context,
8364 _("Could not build Czech POINT conversion request"));
8365 return IE_ERROR;
8367 deposit_ns = xmlNewNs(request, BAD_CAST DEPOSIT_NS, BAD_CAST "dep");
8368 if(!deposit_ns) {
8369 isds_log_message(context,
8370 _("Could not create Czech POINT deposit name space"));
8371 xmlFreeNode(request);
8372 return IE_ERROR;
8374 xmlSetNs(request, deposit_ns);
8376 /* Insert children. They are in empty namespace! */
8377 empty_ns = xmlNewNs(request, BAD_CAST "", NULL);
8378 if(!empty_ns) {
8379 isds_log_message(context, _("Could not create empty name space"));
8380 err = IE_ERROR;
8381 goto leave;
8383 INSERT_STRING_WITH_NS(request, empty_ns, "conversionID", "0");
8384 INSERT_STRING_WITH_NS(request, empty_ns, "fileName",
8385 document->dmFileDescr);
8387 /* Document encoded in Base64 */
8388 err = insert_base64_encoded_string(context, request, empty_ns, "document",
8389 document->data, document->data_length);
8390 if (err) goto leave;
8392 isds_log(ILF_ISDS, ILL_DEBUG,
8393 _("Submitting document for conversion into Czech POINT deposit"));
8395 /* Send conversion request */
8396 err = _czp_czpdeposit(context, request, &response);
8397 xmlFreeNode(request); request = NULL;
8399 if (err) {
8400 czp_do_close_connection(context);
8401 goto leave;
8405 /* Extract response */
8406 xpath_ctx = xmlXPathNewContext(response);
8407 if (!xpath_ctx) {
8408 err = IE_ERROR;
8409 goto leave;
8411 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8412 err = IE_ERROR;
8413 goto leave;
8415 result = xmlXPathEvalExpression(
8416 BAD_CAST "/deposit:saveDocumentResponse/return",
8417 xpath_ctx);
8418 if (!result) {
8419 err = IE_ERROR;
8420 goto leave;
8422 /* Empty response */
8423 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8424 isds_printf_message(context,
8425 _("Missing `return' element in Czech POINT deposit response"));
8426 err = IE_ISDS;
8427 goto leave;
8429 /* More responses */
8430 if (result->nodesetval->nodeNr > 1) {
8431 isds_printf_message(context,
8432 _("Multiple `return' element in Czech POINT deposit response"));
8433 err = IE_ISDS;
8434 goto leave;
8436 /* One response */
8437 xpath_ctx->node = result->nodesetval->nodeTab[0];
8439 /* Get status */
8440 EXTRACT_LONGINT("status", status_ptr, 1);
8441 if (status) {
8442 EXTRACT_STRING("statusMsg", string);
8443 char *string_locale = _isds_utf82locale(string);
8444 isds_printf_message(context,
8445 _("Czech POINT deposit refused document for conversion "
8446 "(code=%ld, message=%s)"),
8447 status, string_locale);
8448 free(string_locale);
8449 err = IE_ISDS;
8450 goto leave;
8453 /* Get document ID */
8454 EXTRACT_STRING("documentID", *id);
8456 /* Get submit date */
8457 EXTRACT_STRING("dateInserted", string);
8458 if (string) {
8459 *date = calloc(1, sizeof(**date));
8460 if (!*date) {
8461 err = IE_NOMEM;
8462 goto leave;
8464 err = datestring2tm((xmlChar *)string, *date);
8465 if (err) {
8466 if (err == IE_NOTSUP) {
8467 err = IE_ISDS;
8468 char *string_locale = _isds_utf82locale(string);
8469 isds_printf_message(context,
8470 _("Invalid dateInserted value: %s"), string_locale);
8471 free(string_locale);
8473 goto leave;
8477 leave:
8478 free(string);
8479 xmlXPathFreeObject(result);
8480 xmlXPathFreeContext(xpath_ctx);
8482 xmlFreeDoc(response);
8483 xmlFreeNode(request);
8485 if (!err) {
8486 char *id_locale = _isds_utf82locale((char *) *id);
8487 isds_log(ILF_ISDS, ILL_DEBUG,
8488 _("Document %s has been submitted for conversion "
8489 "to server successfully\n"), id_locale);
8490 free(id_locale);
8492 return err;
8496 /* Close possibly opened connection to Czech POINT document deposit.
8497 * @context is Czech POINT session context. */
8498 isds_error czp_close_connection(struct isds_ctx *context) {
8499 if (!context) return IE_INVALID_CONTEXT;
8500 zfree(context->long_message);
8501 return czp_do_close_connection(context);
8505 /* Send request for new box creation in testing ISDS instance.
8506 * It's not possible to request for a production box currently, as it
8507 * communicates via e-mail.
8508 * XXX: This function does not work either. Server complains about invalid
8509 * e-mail address.
8510 * XXX: Remove context->type hacks in isds.c and validator.c when removing
8511 * this function
8512 * @context is special session context for box creation request. DO NOT use
8513 * standard context as it could reveal your password. Use fresh new context or
8514 * context previously used by this function.
8515 * @box is box description to create including single primary user (in case of
8516 * FO box type). It outputs box ID assigned by ISDS in dbID element.
8517 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
8518 * box, or contact address of PFO box owner). The email member is mandatory as
8519 * it will be used to deliver credentials.
8520 * @former_names is optional undocumented string. Pass NULL if you don't care.
8521 * @approval is optional external approval of box manipulation
8522 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8523 * NULL, if you don't care.*/
8524 isds_error isds_request_new_testing_box(struct isds_ctx *context,
8525 struct isds_DbOwnerInfo *box, const struct isds_list *users,
8526 const char *former_names, const struct isds_approval *approval,
8527 char **refnumber) {
8528 isds_error err = IE_SUCCESS;
8529 xmlNodePtr request = NULL;
8530 xmlDocPtr response = NULL;
8531 xmlXPathContextPtr xpath_ctx = NULL;
8532 xmlXPathObjectPtr result = NULL;
8535 if (!context) return IE_INVALID_CONTEXT;
8536 zfree(context->long_message);
8537 if (!box) return IE_INVAL;
8539 if (!box->email || box->email[0] == '\0') {
8540 isds_log_message(context, _("E-mail field is mandatory"));
8541 return IE_INVAL;
8544 /* Scratch box ID */
8545 zfree(box->dbID);
8547 /* Store configuration */
8548 context->type = CTX_TYPE_TESTING_REQUEST_COLLECTOR;
8549 free(context->url);
8550 context->url = strdup("http://78.102.19.203/testbox/request_box.php");
8551 if (!(context->url))
8552 return IE_NOMEM;
8554 /* Prepare CURL handle if not yet connected */
8555 if (!context->curl) {
8556 context->curl = curl_easy_init();
8557 if (!(context->curl))
8558 return IE_ERROR;
8561 /* Build CreateDataBox request */
8562 err = build_CreateDBInput_request(context,
8563 &request, BAD_CAST "CreateDataBox",
8564 box, users, (xmlChar *) former_names, NULL, NULL, 0, approval);
8565 if (err) goto leave;
8567 /* Send it to server and process response */
8568 err = send_destroy_request_check_response(context,
8569 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
8570 &response, (xmlChar **) refnumber);
8571 if (err) goto leave;
8573 /* Extract box ID */
8574 xpath_ctx = xmlXPathNewContext(response);
8575 if (!xpath_ctx) {
8576 err = IE_ERROR;
8577 goto leave;
8579 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8580 err = IE_ERROR;
8581 goto leave;
8583 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
8585 leave:
8586 xmlXPathFreeObject(result);
8587 xmlXPathFreeContext(xpath_ctx);
8588 xmlFreeDoc(response);
8589 xmlFreeNode(request);
8591 if (!err) {
8592 isds_log(ILF_ISDS, ILL_DEBUG,
8593 _("CreateDataBox request processed by server successfully.\n"));
8596 return err;
8600 /* Submit CMS signed message to ISDS to verify its originality. This is
8601 * stronger form of isds_verify_message_hash() because ISDS does more checks
8602 * than simple one (potentialy old weak) hash comparison.
8603 * @context is session context
8604 * @message is memory with raw CMS signed message bit stream
8605 * @length is @message size in bytes
8606 * @return
8607 * IE_SUCCESS if message originates in ISDS
8608 * IE_NOTEQUAL if message is unknown to ISDS
8609 * other code for other errors */
8610 isds_error isds_authenticate_message(struct isds_ctx *context,
8611 const void *message, size_t length) {
8612 isds_error err = IE_SUCCESS;
8613 xmlNsPtr isds_ns = NULL;
8614 xmlNodePtr request = NULL;
8615 xmlDocPtr response = NULL;
8616 xmlXPathContextPtr xpath_ctx = NULL;
8617 xmlXPathObjectPtr result = NULL;
8618 _Bool *authentic = NULL;
8620 if (!context) return IE_INVALID_CONTEXT;
8621 zfree(context->long_message);
8622 if (!message || length == 0) return IE_INVAL;
8624 /* Check if connection is established
8625 * TODO: This check should be done downstairs. */
8626 if (!context->curl) return IE_CONNECTION_CLOSED;
8629 /* Build AuthenticateMessage request */
8630 request = xmlNewNode(NULL, BAD_CAST "AuthenticateMessage");
8631 if (!request) {
8632 isds_log_message(context,
8633 _("Could not build AuthenticateMessage request"));
8634 return IE_ERROR;
8636 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8637 if(!isds_ns) {
8638 isds_log_message(context, _("Could not create ISDS name space"));
8639 xmlFreeNode(request);
8640 return IE_ERROR;
8642 xmlSetNs(request, isds_ns);
8644 /* Insert Base64 encoded message */
8645 err = insert_base64_encoded_string(context, request, NULL, "dmMessage",
8646 message, length);
8647 if (err) goto leave;
8649 /* Send request to server and process response */
8650 err = send_destroy_request_check_response(context,
8651 SERVICE_DM_OPERATIONS, BAD_CAST "AuthenticateMessage", &request,
8652 &response, NULL);
8653 if (err) goto leave;
8656 /* ISDS has decided */
8657 xpath_ctx = xmlXPathNewContext(response);
8658 if (!xpath_ctx) {
8659 err = IE_ERROR;
8660 goto leave;
8662 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8663 err = IE_ERROR;
8664 goto leave;
8667 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic);
8669 if (!authentic) {
8670 isds_log_message(context,
8671 _("Server did not return any response on "
8672 "AuthenticateMessage request"));
8673 err = IE_ISDS;
8674 goto leave;
8676 if (*authentic) {
8677 isds_log(ILF_ISDS, ILL_DEBUG,
8678 _("ISDS authenticated the message successfully\n"));
8679 } else {
8680 isds_log_message(context, _("ISDS does not know the message"));
8681 err = IE_NOTEQUAL;
8685 leave:
8686 free(authentic);
8687 xmlXPathFreeObject(result);
8688 xmlXPathFreeContext(xpath_ctx);
8690 xmlFreeDoc(response);
8691 xmlFreeNode(request);
8693 return err;
8696 #undef INSERT_ELEMENT
8697 #undef CHECK_FOR_STRING_LENGTH
8698 #undef INSERT_STRING_ATTRIBUTE
8699 #undef INSERT_ULONGINTNOPTR
8700 #undef INSERT_ULONGINT
8701 #undef INSERT_LONGINT
8702 #undef INSERT_BOOLEAN
8703 #undef INSERT_SCALAR_BOOLEAN
8704 #undef INSERT_STRING
8705 #undef INSERT_STRING_WITH_NS
8706 #undef EXTRACT_STRING_ATTRIBUTE
8707 #undef EXTRACT_ULONGINT
8708 #undef EXTRACT_LONGINT
8709 #undef EXTRACT_BOOLEAN
8710 #undef EXTRACT_STRING
8713 /* Compute hash of message from raw representation and store it into envelope.
8714 * Original hash structure will be destroyed in envelope.
8715 * @context is session context
8716 * @message is message carrying raw XML message blob
8717 * @algorithm is desired hash algorithm to use */
8718 isds_error isds_compute_message_hash(struct isds_ctx *context,
8719 struct isds_message *message, const isds_hash_algorithm algorithm) {
8720 isds_error err = IE_SUCCESS;
8721 const char *nsuri;
8722 void *xml_stream = NULL;
8723 size_t xml_stream_length;
8724 size_t phys_start, phys_end;
8725 char *phys_path = NULL;
8726 struct isds_hash *new_hash = NULL;
8729 if (!context) return IE_INVALID_CONTEXT;
8730 zfree(context->long_message);
8731 if (!message) return IE_INVAL;
8733 if (!message->raw) {
8734 isds_log_message(context,
8735 _("Message does not carry raw representation"));
8736 return IE_INVAL;
8739 switch (message->raw_type) {
8740 case RAWTYPE_INCOMING_MESSAGE:
8741 nsuri = ISDS_NS;
8742 xml_stream = message->raw;
8743 xml_stream_length = message->raw_length;
8744 break;
8746 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
8747 nsuri = SISDS_INCOMING_NS;
8748 xml_stream = message->raw;
8749 xml_stream_length = message->raw_length;
8750 break;
8752 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
8753 nsuri = SISDS_INCOMING_NS;
8754 err = _isds_extract_cms_data(context,
8755 message->raw, message->raw_length,
8756 &xml_stream, &xml_stream_length);
8757 if (err) goto leave;
8758 break;
8760 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
8761 nsuri = SISDS_OUTGOING_NS;
8762 xml_stream = message->raw;
8763 xml_stream_length = message->raw_length;
8764 break;
8766 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
8767 nsuri = SISDS_OUTGOING_NS;
8768 err = _isds_extract_cms_data(context,
8769 message->raw, message->raw_length,
8770 &xml_stream, &xml_stream_length);
8771 if (err) goto leave;
8772 break;
8774 default:
8775 isds_log_message(context, _("Bad raw representation type"));
8776 return IE_INVAL;
8777 break;
8781 /* XXX: Hash is computed from original string representing isds:dmDm
8782 * subtree. That means no encoding, white space, xmlns attributes changes.
8783 * In other words, input for hash can be invalid XML stream. */
8784 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
8785 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
8786 PHYSXML_ELEMENT_SEPARATOR,
8787 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
8788 PHYSXML_ELEMENT_SEPARATOR
8789 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
8790 err = IE_NOMEM;
8791 goto leave;
8793 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
8794 phys_path, &phys_start, &phys_end);
8795 zfree(phys_path);
8796 if (err) {
8797 isds_log_message(context,
8798 _("Substring with isds:dmDM element could not be located "
8799 "in raw message"));
8800 goto leave;
8804 /* Compute hash */
8805 new_hash = calloc(1, sizeof(*new_hash));
8806 if (!new_hash) {
8807 err = IE_NOMEM;
8808 goto leave;
8810 new_hash->algorithm = algorithm;
8811 err = _isds_compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
8812 new_hash);
8813 if (err) {
8814 isds_log_message(context, _("Could not compute message hash"));
8815 goto leave;
8818 /* Save computed hash */
8819 if (!message->envelope) {
8820 message->envelope = calloc(1, sizeof(*message->envelope));
8821 if (!message->envelope) {
8822 err = IE_NOMEM;
8823 goto leave;
8826 isds_hash_free(&message->envelope->hash);
8827 message->envelope->hash = new_hash;
8829 leave:
8830 if (err) {
8831 isds_hash_free(&new_hash);
8834 free(phys_path);
8835 if (xml_stream != message->raw) free(xml_stream);
8836 return err;
8840 /* Compare two hashes.
8841 * @h1 is first hash
8842 * @h2 is another hash
8843 * @return
8844 * IE_SUCCESS if hashes equal
8845 * IE_NOTUNIQ if hashes are comparable, but they don't equal
8846 * IE_ENUM if not comparable, but both structures defined
8847 * IE_INVAL if some of the structures are undefined (NULL)
8848 * IE_ERROR if internal error occurs */
8849 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
8850 if (h1 == NULL || h2 == NULL) return IE_INVAL;
8851 if (h1->algorithm != h2->algorithm) return IE_ENUM;
8852 if (h1->length != h2->length) return IE_ERROR;
8853 if (h1->length > 0 && !h1->value) return IE_ERROR;
8854 if (h2->length > 0 && !h2->value) return IE_ERROR;
8856 for (int i = 0; i < h1->length; i++) {
8857 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
8858 return IE_NOTEQUAL;
8860 return IE_SUCCESS;
8864 /* Check message has gone through ISDS by comparing message hash stored in
8865 * ISDS and locally computed hash. You must provide message with valid raw
8866 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
8867 * This is convenient wrapper for isds_download_message_hash(),
8868 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
8869 * @context is session context
8870 * @message is message with valid raw and envelope member; envelope->hash
8871 * member will be changed during function run. Use envelope on heap only.
8872 * @return
8873 * IE_SUCCESS if message originates in ISDS
8874 * IE_NOTEQUAL if message is unknown to ISDS
8875 * other code for other errors */
8876 isds_error isds_verify_message_hash(struct isds_ctx *context,
8877 struct isds_message *message) {
8878 isds_error err = IE_SUCCESS;
8879 struct isds_hash *downloaded_hash = NULL;
8881 if (!context) return IE_INVALID_CONTEXT;
8882 zfree(context->long_message);
8883 if (!message) return IE_INVAL;
8885 if (!message->envelope) {
8886 isds_log_message(context,
8887 _("Given message structure is missing envelope"));
8888 return IE_INVAL;
8890 if (!message->raw) {
8891 isds_log_message(context,
8892 _("Given message structure is missing raw representation"));
8893 return IE_INVAL;
8896 err = isds_download_message_hash(context, message->envelope->dmID,
8897 &downloaded_hash);
8898 if (err) goto leave;
8900 err = isds_compute_message_hash(context, message,
8901 downloaded_hash->algorithm);
8902 if (err) goto leave;
8904 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
8906 leave:
8907 isds_hash_free(&downloaded_hash);
8908 return err;
8912 /* Search for document by document ID in list of documents. IDs are compared
8913 * as UTF-8 string.
8914 * @documents is list of isds_documents
8915 * @id is document identifier
8916 * @return first matching document or NULL. */
8917 const struct isds_document *isds_find_document_by_id(
8918 const struct isds_list *documents, const char *id) {
8919 const struct isds_list *item;
8920 const struct isds_document *document;
8922 for (item = documents; item; item = item->next) {
8923 document = (struct isds_document *) item->data;
8924 if (!document) continue;
8926 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
8927 return document;
8930 return NULL;
8934 /* Normalize @mime_type to be proper MIME type.
8935 * ISDS servers passes invalid MIME types (e.g. "pdf"). This function tries to
8936 * guess regular MIME type (e.g. "application/pdf").
8937 * @mime_type is UTF-8 encoded MIME type to fix
8938 * @return original @mime_type if no better interpretation exists, or array to
8939 * constant static UTF-8 encoded string with proper MIME type. */
8940 char *isds_normalize_mime_type(const char* mime_type) {
8941 if (!mime_type) return NULL;
8943 for (int offset = 0;
8944 offset < sizeof(extension_map_mime)/sizeof(extension_map_mime[0]);
8945 offset += 2) {
8946 if (!xmlStrcmp((const xmlChar*) mime_type, extension_map_mime[offset]))
8947 return (char *) extension_map_mime[offset + 1];
8950 return (char *) mime_type;
8954 /* Switch MIME type normalization while message loading. Default state for new
8955 * context is no normalization.
8956 * @normalize use true to switch normalization on, false to switch off */
8957 isds_error isds_set_mime_type_normalization(struct isds_ctx *context,
8958 _Bool normalize) {
8959 if (!context) return IE_INVALID_CONTEXT;
8960 zfree(context->long_message);
8962 context->normalize_mime_type = normalize;
8963 isds_log(ILF_FILE, ILL_INFO, (context->normalize_mime_type) ?
8964 _("MIME type normalization switched on\n") :
8965 _("MIME type normalization switched off\n"));
8966 return IE_SUCCESS;
8970 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
8971 struct isds_message **message);
8972 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
8973 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
8974 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
8975 struct isds_address **address);
8977 int isds_message_free(struct isds_message **message);
8978 int isds_address_free(struct isds_address **address);
8982 /* Makes known all relevant namespaces to given XPath context
8983 * @xpath_ctx is XPath context
8984 * @message_ns selects proper message name space. Unsigned and signed
8985 * messages and delivery info's differ in prefix and URI. */
8986 _hidden isds_error _isds_register_namespaces(xmlXPathContextPtr xpath_ctx,
8987 const message_ns_type message_ns) {
8988 const xmlChar *message_namespace = NULL;
8990 if (!xpath_ctx) return IE_ERROR;
8992 switch(message_ns) {
8993 case MESSAGE_NS_1:
8994 message_namespace = BAD_CAST ISDS1_NS; break;
8995 case MESSAGE_NS_UNSIGNED:
8996 message_namespace = BAD_CAST ISDS_NS; break;
8997 case MESSAGE_NS_SIGNED_INCOMING:
8998 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
8999 case MESSAGE_NS_SIGNED_OUTGOING:
9000 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
9001 case MESSAGE_NS_SIGNED_DELIVERY:
9002 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
9003 default:
9004 return IE_ENUM;
9007 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
9008 return IE_ERROR;
9009 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
9010 return IE_ERROR;
9011 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
9012 return IE_ERROR;
9013 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
9014 return IE_ERROR;
9015 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
9016 return IE_ERROR;
9017 return IE_SUCCESS;