Fix typo (add new line to log message)
[libisds.git] / src / isds.c
blob251194ca09a44c425ac5fa1ecf71aac423e20c81
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 /* TODO: Add MIME types for ISDOC, X.509 certificates, CMS and TST */
55 /* Deallocate structure isds_pki_credentials and NULL it.
56 * Pass-phrase is discarded.
57 * @pki credentials to to free */
58 void isds_pki_credentials_free(struct isds_pki_credentials **pki) {
59 if(!pki || !*pki) return;
61 free((*pki)->engine);
62 free((*pki)->certificate);
63 free((*pki)->key);
65 if ((*pki)->passphrase) {
66 memset((*pki)->passphrase, 0, strlen((*pki)->passphrase));
67 free((*pki)->passphrase);
70 zfree((*pki));
74 /* Free isds_list with all member data.
75 * @list list to free, on return will be NULL */
76 void isds_list_free(struct isds_list **list) {
77 struct isds_list *item, *next_item;
79 if (!list || !*list) return;
81 for(item = *list; item; item = next_item) {
82 (item->destructor)(&(item->data));
83 next_item = item->next;
84 free(item);
87 *list = NULL;
91 /* Deallocate structure isds_hash and NULL it.
92 * @hash hash to to free */
93 void isds_hash_free(struct isds_hash **hash) {
94 if(!hash || !*hash) return;
95 free((*hash)->value);
96 zfree((*hash));
100 /* Deallocate structure isds_PersonName recursively and NULL it */
101 static void isds_PersonName_free(struct isds_PersonName **person_name) {
102 if (!person_name || !*person_name) return;
104 free((*person_name)->pnFirstName);
105 free((*person_name)->pnMiddleName);
106 free((*person_name)->pnLastName);
107 free((*person_name)->pnLastNameAtBirth);
109 free(*person_name);
110 *person_name = NULL;
114 /* Deallocate structure isds_BirthInfo recursively and NULL it */
115 static void isds_BirthInfo_free(struct isds_BirthInfo **birth_info) {
116 if (!birth_info || !*birth_info) return;
118 free((*birth_info)->biDate);
119 free((*birth_info)->biCity);
120 free((*birth_info)->biCounty);
121 free((*birth_info)->biState);
123 free(*birth_info);
124 *birth_info = NULL;
128 /* Deallocate structure isds_Address recursively and NULL it */
129 static void isds_Address_free(struct isds_Address **address) {
130 if (!address || !*address) return;
132 free((*address)->adCity);
133 free((*address)->adStreet);
134 free((*address)->adNumberInStreet);
135 free((*address)->adNumberInMunicipality);
136 free((*address)->adZipCode);
137 free((*address)->adState);
139 free(*address);
140 *address = NULL;
144 /* Deallocate structure isds_DbOwnerInfo recursively and NULL it */
145 void isds_DbOwnerInfo_free(struct isds_DbOwnerInfo **db_owner_info) {
146 if (!db_owner_info || !*db_owner_info) return;
148 free((*db_owner_info)->dbID);
149 free((*db_owner_info)->dbType);
150 free((*db_owner_info)->ic);
151 isds_PersonName_free(&((*db_owner_info)->personName));
152 free((*db_owner_info)->firmName);
153 isds_BirthInfo_free(&((*db_owner_info)->birthInfo));
154 isds_Address_free(&((*db_owner_info)->address));
155 free((*db_owner_info)->nationality);
156 free((*db_owner_info)->email);
157 free((*db_owner_info)->telNumber);
158 free((*db_owner_info)->identifier);
159 free((*db_owner_info)->registryCode);
160 free((*db_owner_info)->dbState);
161 free((*db_owner_info)->dbEffectiveOVM);
163 free(*db_owner_info);
164 *db_owner_info = NULL;
167 /* Deallocate structure isds_DbUserInfo recursively and NULL it */
168 void isds_DbUserInfo_free(struct isds_DbUserInfo **db_user_info) {
169 if (!db_user_info || !*db_user_info) return;
171 free((*db_user_info)->userID);
172 free((*db_user_info)->userType);
173 free((*db_user_info)->userPrivils);
174 isds_PersonName_free(&((*db_user_info)->personName));
175 isds_Address_free(&((*db_user_info)->address));
176 free((*db_user_info)->biDate);
177 free((*db_user_info)->ic);
178 free((*db_user_info)->firmName);
179 free((*db_user_info)->caStreet);
180 free((*db_user_info)->caCity);
181 free((*db_user_info)->caZipCode);
182 free((*db_user_info)->caState);
184 zfree(*db_user_info);
188 /* Deallocate struct isds_event recursively and NULL it */
189 void isds_event_free(struct isds_event **event) {
190 if (!event || !*event) return;
192 free((*event)->time);
193 free((*event)->type);
194 free((*event)->description);
195 zfree(*event);
199 /* Deallocate struct isds_envelope recursively and NULL it */
200 void isds_envelope_free(struct isds_envelope **envelope) {
201 if (!envelope || !*envelope) return;
203 free((*envelope)->dmID);
204 free((*envelope)->dbIDSender);
205 free((*envelope)->dmSender);
206 free((*envelope)->dmSenderAddress);
207 free((*envelope)->dmSenderType);
208 free((*envelope)->dmRecipient);
209 free((*envelope)->dmRecipientAddress);
210 free((*envelope)->dmAmbiguousRecipient);
211 free((*envelope)->dmType);
213 free((*envelope)->dmOrdinal);
214 free((*envelope)->dmMessageStatus);
215 free((*envelope)->dmDeliveryTime);
216 free((*envelope)->dmAcceptanceTime);
217 isds_hash_free(&(*envelope)->hash);
218 free((*envelope)->timestamp);
219 isds_list_free(&(*envelope)->events);
221 free((*envelope)->dmSenderOrgUnit);
222 free((*envelope)->dmSenderOrgUnitNum);
223 free((*envelope)->dbIDRecipient);
224 free((*envelope)->dmRecipientOrgUnit);
225 free((*envelope)->dmRecipientOrgUnitNum);
226 free((*envelope)->dmToHands);
227 free((*envelope)->dmAnnotation);
228 free((*envelope)->dmRecipientRefNumber);
229 free((*envelope)->dmSenderRefNumber);
230 free((*envelope)->dmRecipientIdent);
231 free((*envelope)->dmSenderIdent);
233 free((*envelope)->dmLegalTitleLaw);
234 free((*envelope)->dmLegalTitleYear);
235 free((*envelope)->dmLegalTitleSect);
236 free((*envelope)->dmLegalTitlePar);
237 free((*envelope)->dmLegalTitlePoint);
239 free((*envelope)->dmPersonalDelivery);
240 free((*envelope)->dmAllowSubstDelivery);
241 free((*envelope)->dmOVM);
243 free(*envelope);
244 *envelope = NULL;
248 /* Deallocate struct isds_message recursively and NULL it */
249 void isds_message_free(struct isds_message **message) {
250 if (!message || !*message) return;
252 free((*message)->raw);
253 isds_envelope_free(&((*message)->envelope));
254 isds_list_free(&((*message)->documents));
256 free(*message);
257 *message = NULL;
261 /* Deallocate struct isds_document recursively and NULL it */
262 void isds_document_free(struct isds_document **document) {
263 if (!document || !*document) return;
265 free((*document)->data);
266 free((*document)->dmMimeType);
267 free((*document)->dmFileGuid);
268 free((*document)->dmUpFileGuid);
269 free((*document)->dmFileDescr);
270 free((*document)->dmFormat);
272 free(*document);
273 *document = NULL;
277 /* Deallocate struct isds_message_copy recursively and NULL it */
278 void isds_message_copy_free(struct isds_message_copy **copy) {
279 if (!copy || !*copy) return;
281 free((*copy)->dbIDRecipient);
282 free((*copy)->dmRecipientOrgUnit);
283 free((*copy)->dmRecipientOrgUnitNum);
284 free((*copy)->dmToHands);
286 free((*copy)->dmStatus);
287 free((*copy)->dmID);
289 zfree(*copy);
293 /* Deallocate struct isds_approval recursively and NULL it */
294 void isds_approval_free(struct isds_approval **approval) {
295 if (!approval || !*approval) return;
297 free((*approval)->refference);
299 zfree(*approval);
303 /* *DUP_OR_ERROR macros needs error label */
304 #define STRDUP_OR_ERROR(new, template) { \
305 if (!template) { \
306 (new) = NULL; \
307 } else { \
308 (new) = strdup(template); \
309 if (!new) goto error; \
313 #define FLATDUP_OR_ERROR(new, template) { \
314 if (!template) { \
315 (new) = NULL; \
316 } else { \
317 (new) = malloc(sizeof(*(new))); \
318 if (!new) goto error; \
319 memcpy((new), (template), sizeof(*(template))); \
323 /* Copy structure isds_pki_credentials recursively. */
324 struct isds_pki_credentials *isds_pki_credentials_duplicate(
325 const struct isds_pki_credentials *template) {
326 struct isds_pki_credentials *new = NULL;
328 if(!template) return NULL;
330 new = calloc(1, sizeof(*new));
331 if (!new) return NULL;
333 STRDUP_OR_ERROR(new->engine, template->engine);
334 new->certificate_format = template->certificate_format;
335 STRDUP_OR_ERROR(new->certificate, template->certificate);
336 new->key_format = template->key_format;
337 STRDUP_OR_ERROR(new->key, template->key);
338 STRDUP_OR_ERROR(new->passphrase, template->passphrase);
340 return new;
342 error:
343 isds_pki_credentials_free(&new);
344 return NULL;
348 /* Copy structure isds_PersonName recursively */
349 struct isds_PersonName *isds_PersonName_duplicate(
350 const struct isds_PersonName *template) {
351 struct isds_PersonName *new = NULL;
353 if (!template) return NULL;
355 new = calloc(1, sizeof(*new));
356 if (!new) return NULL;
358 STRDUP_OR_ERROR(new->pnFirstName, template->pnFirstName);
359 STRDUP_OR_ERROR(new->pnMiddleName, template->pnMiddleName);
360 STRDUP_OR_ERROR(new->pnLastName, template->pnLastName);
361 STRDUP_OR_ERROR(new->pnLastNameAtBirth, template->pnLastNameAtBirth);
363 return new;
365 error:
366 isds_PersonName_free(&new);
367 return NULL;
371 /* Copy structure isds_BirthInfo recursively */
372 static struct isds_BirthInfo *isds_BirthInfo_duplicate(
373 const struct isds_BirthInfo *template) {
374 struct isds_BirthInfo *new = NULL;
376 if (!template) return NULL;
378 new = calloc(1, sizeof(*new));
379 if (!new) return NULL;
381 FLATDUP_OR_ERROR(new->biDate, template->biDate);
382 STRDUP_OR_ERROR(new->biCity, template->biCity);
383 STRDUP_OR_ERROR(new->biCounty, template->biCounty);
384 STRDUP_OR_ERROR(new->biState, template->biState);
386 return new;
388 error:
389 isds_BirthInfo_free(&new);
390 return NULL;
394 /* Copy structure isds_Address recursively */
395 struct isds_Address *isds_Address_duplicate(
396 const struct isds_Address *template) {
397 struct isds_Address *new = NULL;
399 if (!template) return NULL;
401 new = calloc(1, sizeof(*new));
402 if (!new) return NULL;
404 STRDUP_OR_ERROR(new->adCity, template->adCity);
405 STRDUP_OR_ERROR(new->adStreet, template->adStreet);
406 STRDUP_OR_ERROR(new->adNumberInStreet, template->adNumberInStreet);
407 STRDUP_OR_ERROR(new->adNumberInMunicipality,
408 template->adNumberInMunicipality);
409 STRDUP_OR_ERROR(new->adZipCode, template->adZipCode);
410 STRDUP_OR_ERROR(new->adState, template->adState);
412 return new;
414 error:
415 isds_Address_free(&new);
416 return NULL;
420 /* Copy structure isds_DbOwnerInfo recursively */
421 struct isds_DbOwnerInfo *isds_DbOwnerInfo_duplicate(
422 const struct isds_DbOwnerInfo *template) {
423 struct isds_DbOwnerInfo *new = NULL;
424 if (!template) return NULL;
426 new = calloc(1, sizeof(*new));
427 if (!new) return NULL;
429 STRDUP_OR_ERROR(new->dbID, template->dbID);
430 FLATDUP_OR_ERROR(new->dbType, template->dbType);
431 STRDUP_OR_ERROR(new->ic, template->ic);
433 if (template->personName) {
434 if (!(new->personName =
435 isds_PersonName_duplicate(template->personName)))
436 goto error;
439 STRDUP_OR_ERROR(new->firmName, template->firmName);
441 if (template->birthInfo) {
442 if (!(new->birthInfo =
443 isds_BirthInfo_duplicate(template->birthInfo)))
444 goto error;
447 if (template->address) {
448 if (!(new->address = isds_Address_duplicate(template->address)))
449 goto error;
452 STRDUP_OR_ERROR(new->nationality, template->nationality);
453 STRDUP_OR_ERROR(new->email, template->email);
454 STRDUP_OR_ERROR(new->telNumber, template->telNumber);
455 STRDUP_OR_ERROR(new->identifier, template->identifier);
456 STRDUP_OR_ERROR(new->registryCode, template->registryCode);
457 FLATDUP_OR_ERROR(new->dbState, template->dbState);
458 FLATDUP_OR_ERROR(new->dbEffectiveOVM, template->dbEffectiveOVM);
459 FLATDUP_OR_ERROR(new->dbOpenAddressing, template->dbOpenAddressing);
461 return new;
463 error:
464 isds_DbOwnerInfo_free(&new);
465 return NULL;
469 /* Copy structure isds_DbUserInfo recursively */
470 struct isds_DbUserInfo *isds_DbUserInfo_duplicate(
471 const struct isds_DbUserInfo *template) {
472 struct isds_DbUserInfo *new = NULL;
473 if (!template) return NULL;
475 new = calloc(1, sizeof(*new));
476 if (!new) return NULL;
478 STRDUP_OR_ERROR(new->userID, template->userID);
479 FLATDUP_OR_ERROR(new->userType, template->userType);
480 FLATDUP_OR_ERROR(new->userPrivils, template->userPrivils);
482 if (template->personName) {
483 if (!(new->personName =
484 isds_PersonName_duplicate(template->personName)))
485 goto error;
488 if (template->address) {
489 if (!(new->address = isds_Address_duplicate(template->address)))
490 goto error;
493 FLATDUP_OR_ERROR(new->biDate, template->biDate);
494 STRDUP_OR_ERROR(new->ic, template->ic);
495 STRDUP_OR_ERROR(new->firmName, template->firmName);
496 STRDUP_OR_ERROR(new->caStreet, template->caStreet);
497 STRDUP_OR_ERROR(new->caCity, template->caCity);
498 STRDUP_OR_ERROR(new->caZipCode, template->caZipCode);
499 STRDUP_OR_ERROR(new->caState, template->caState);
501 return new;
503 error:
504 isds_DbUserInfo_free(&new);
505 return NULL;
508 #undef FLATDUP_OR_ERROR
509 #undef STRDUP_OR_ERROR
512 /* Initialize ISDS library.
513 * Global function, must be called before other functions.
514 * If it fails you can not use ISDS library and must call isds_cleanup() to
515 * free partially initialized global variables. */
516 isds_error isds_init(void) {
517 /* NULL global variables */
518 log_facilities = ILF_ALL;
519 log_level = ILL_WARNING;
520 log_callback = NULL;
521 log_callback_data = NULL;
523 #if ENABLE_NLS
524 /* Initialize gettext */
525 bindtextdomain(PACKAGE, LOCALEDIR);
526 #endif
528 /* Initialize CURL */
529 if (curl_global_init(CURL_GLOBAL_ALL)) {
530 isds_log(ILF_ISDS, ILL_CRIT, _("CURL library initialization failed\n"));
531 return IE_ERROR;
534 /* Initialize gpg-error because of gpgme and ksba */
535 if (gpg_err_init()) {
536 isds_log(ILF_ISDS, ILL_CRIT,
537 _("gpg-error library initialization failed\n"));
538 return IE_ERROR;
541 /* Initialize GPGME */
542 if (_isds_init_gpgme(&version_gpgme)) {
543 isds_log(ILF_ISDS, ILL_CRIT,
544 _("GPGME library initialization failed\n"));
545 return IE_ERROR;
548 /* Initialize gcrypt */
549 if (_isds_init_gcrypt(&version_gcrypt)) {
550 isds_log(ILF_ISDS, ILL_CRIT,
551 _("gcrypt library initialization failed\n"));
552 return IE_ERROR;
555 /* This can _exit() current program. Find not so assertive check. */
556 LIBXML_TEST_VERSION;
558 /* Check expat */
559 if (_isds_init_expat(&version_expat)) {
560 isds_log(ILF_ISDS, ILL_CRIT,
561 _("expat library initialization failed\n"));
562 return IE_ERROR;
565 /* Allocate global variables */
568 return IE_SUCCESS;
572 /* Deinitialize ISDS library.
573 * Global function, must be called as last library function. */
574 isds_error isds_cleanup(void) {
575 /* XML */
576 xmlCleanupParser();
578 /* Curl */
579 curl_global_cleanup();
581 return IE_SUCCESS;
585 /* Return version string of this library. Version of dependencies can be
586 * embedded. Do no try to parse it. You must free it. */
587 char *isds_version(void) {
588 char *buffer = NULL;
590 isds_asprintf(&buffer, _("%s (%s, GPGME %s, gcrypt %s, %s, libxml2 %s)"),
591 PACKAGE_VERSION, curl_version(), version_gpgme, version_gcrypt,
592 version_expat, xmlParserVersion);
593 return buffer;
597 /* Return text description of ISDS error */
598 const char *isds_strerror(const isds_error error) {
599 switch (error) {
600 case IE_SUCCESS:
601 return(_("Success")); break;
602 case IE_ERROR:
603 return(_("Unspecified error")); break;
604 case IE_NOTSUP:
605 return(_("Not supported")); break;
606 case IE_INVAL:
607 return(_("Invalid value")); break;
608 case IE_INVALID_CONTEXT:
609 return(_("Invalid context")); break;
610 case IE_NOT_LOGGED_IN:
611 return(_("Not logged in")); break;
612 case IE_CONNECTION_CLOSED:
613 return(_("Connection closed")); break;
614 case IE_TIMED_OUT:
615 return(_("Timed out")); break;
616 case IE_NOEXIST:
617 return(_("Not exist")); break;
618 case IE_NOMEM:
619 return(_("Out of memory")); break;
620 case IE_NETWORK:
621 return(_("Network problem")); break;
622 case IE_HTTP:
623 return(_("HTTP problem")); break;
624 case IE_SOAP:
625 return(_("SOAP problem")); break;
626 case IE_XML:
627 return(_("XML problem")); break;
628 case IE_ISDS:
629 return(_("ISDS server problem")); break;
630 case IE_ENUM:
631 return(_("Invalid enum value")); break;
632 case IE_DATE:
633 return(_("Invalid date value")); break;
634 case IE_2BIG:
635 return(_("Too big")); break;
636 case IE_2SMALL:
637 return(_("Too small")); break;
638 case IE_NOTUNIQ:
639 return(_("Value not unique")); break;
640 case IE_NOTEQUAL:
641 return(_("Values not equal")); break;
642 case IE_PARTIAL_SUCCESS:
643 return(_("Some suboperations failed")); break;
644 case IE_ABORTED:
645 return(_("Operation aborted")); break;
646 default:
647 return(_("Unknown error"));
652 /* Create ISDS context.
653 * Each context can be used for different sessions to (possibly) different
654 * ISDS server with different credentials. */
655 struct isds_ctx *isds_ctx_create(void) {
656 struct isds_ctx *context;
657 context = malloc(sizeof(*context));
658 if (context) memset(context, 0, sizeof(*context));
659 return context;
663 /* Close possibly opened connection to Czech POINT document deposit without
664 * resetting long_message buffer.
665 * XXX: Do not use czp_close_connection() if you do not want to destroy log
666 * message.
667 * @context is Czech POINT session context. */
668 static isds_error czp_do_close_connection(struct isds_ctx *context) {
669 if (!context) return IE_INVALID_CONTEXT;
670 _isds_close_connection(context);
671 return IE_SUCCESS;
675 /* Discard credentials.
676 * Only that. It does not cause log out, connection close or similar. */
677 static isds_error discard_credentials(struct isds_ctx *context) {
678 if(!context) return IE_INVALID_CONTEXT;
680 if (context->username) {
681 memset(context->username, 0, strlen(context->username));
682 zfree(context->username);
684 if (context->password) {
685 memset(context->password, 0, strlen(context->password));
686 zfree(context->password);
688 isds_pki_credentials_free(&context->pki_credentials);
690 return IE_SUCCESS;
694 /* Destroy ISDS context and free memory.
695 * @context will be NULLed on success. */
696 isds_error isds_ctx_free(struct isds_ctx **context) {
697 if (!context || !*context) {
698 return IE_INVALID_CONTEXT;
701 /* Discard credentials and close connection */
702 switch ((*context)->type) {
703 case CTX_TYPE_NONE: break;
704 case CTX_TYPE_ISDS: isds_logout(*context); break;
705 case CTX_TYPE_CZP:
706 case CTX_TYPE_TESTING_REQUEST_COLLECTOR:
707 czp_do_close_connection(*context); break;
710 /* For sure */
711 discard_credentials(*context);
713 /* Free other structures */
714 free((*context)->tls_verify_server);
715 free((*context)->tls_ca_file);
716 free((*context)->tls_ca_dir);
717 free((*context)->tls_crl_file);
718 free((*context)->long_message);
720 free(*context);
721 *context = NULL;
722 return IE_SUCCESS;
726 /* Return long message text produced by library function, e.g. detailed error
727 * message. Returned pointer is only valid until new library function is
728 * called for the same context. Could be NULL, especially if NULL context is
729 * supplied. Return string is locale encoded. */
730 char *isds_long_message(const struct isds_ctx *context) {
731 if (!context) return NULL;
732 return context->long_message;
736 /* Stores message into context' long_message buffer.
737 * Application can pick the message up using isds_long_message().
738 * NULL @message truncates the buffer but does not deallocate it.
739 * @message is coded in locale encoding */
740 _hidden isds_error isds_log_message(struct isds_ctx *context,
741 const char *message) {
742 char *buffer;
743 size_t length;
745 if (!context) return IE_INVALID_CONTEXT;
747 /* FIXME: Check for integer overflow */
748 length = 1 + ((message) ? strlen(message) : 0);
749 buffer = realloc(context->long_message, length);
750 if (!buffer) return IE_NOMEM;
752 if (message)
753 strcpy(buffer, message);
754 else
755 *buffer = '\0';
757 context->long_message = buffer;
758 return IE_SUCCESS;
762 /* Appends message into context' long_message buffer.
763 * Application can pick the message up using isds_long_message().
764 * NULL message has void effect. */
765 _hidden isds_error isds_append_message(struct isds_ctx *context,
766 const char *message) {
767 char *buffer;
768 size_t old_length, length;
770 if (!context) return IE_INVALID_CONTEXT;
771 if (!message) return IE_SUCCESS;
772 if (!context->long_message)
773 return isds_log_message(context, message);
775 old_length = strlen(context->long_message);
776 /* FIXME: Check for integer overflow */
777 length = 1 + old_length + strlen(message);
778 buffer = realloc(context->long_message, length);
779 if (!buffer) return IE_NOMEM;
781 strcpy(buffer + old_length, message);
783 context->long_message = buffer;
784 return IE_SUCCESS;
788 /* Stores formatted message into context' long_message buffer.
789 * Application can pick the message up using isds_long_message(). */
790 _hidden isds_error isds_printf_message(struct isds_ctx *context,
791 const char *format, ...) {
792 va_list ap;
793 int length;
795 if (!context) return IE_INVALID_CONTEXT;
796 va_start(ap, format);
797 length = isds_vasprintf(&(context->long_message), format, ap);
798 va_end(ap);
800 return (length < 0) ? IE_ERROR: IE_SUCCESS;
804 /* Set logging up.
805 * @facilities is bit mask of isds_log_facility values,
806 * @level is verbosity level. */
807 void isds_set_logging(const unsigned int facilities,
808 const isds_log_level level) {
809 log_facilities = facilities;
810 log_level = level;
814 /* Register callback function libisds calls when new global log message is
815 * produced by library. Library logs to stderr by default.
816 * @callback is function provided by application libisds will call. See type
817 * definition for @callback argument explanation. Pass NULL to revert logging to
818 * default behaviour.
819 * @data is application specific data @callback gets as last argument */
820 void isds_set_log_callback(isds_log_callback callback, void *data) {
821 log_callback = callback;
822 log_callback_data = data;
826 /* Log @message in class @facility with log @level into global log. @message
827 * is printf(3) formatting string, variadic arguments may be necessary.
828 * For debugging purposes. */
829 _hidden isds_error isds_log(const isds_log_facility facility,
830 const isds_log_level level, const char *message, ...) {
831 va_list ap;
832 char *buffer = NULL;
833 int length;
835 if (level > log_level) return IE_SUCCESS;
836 if (!(log_facilities & facility)) return IE_SUCCESS;
837 if (!message) return IE_INVAL;
839 if (log_callback) {
840 /* Pass message to application supplied callback function */
841 va_start(ap, message);
842 length = isds_vasprintf(&buffer, message, ap);
843 va_end(ap);
845 if (length == -1) {
846 return IE_ERROR;
848 if (length > 0) {
849 log_callback(facility, level, buffer, length, log_callback_data);
851 free(buffer);
852 } else {
853 /* Default: Log it to stderr */
854 va_start(ap, message);
855 vfprintf(stderr, message, ap);
856 va_end(ap);
857 /* Line buffered printf is default.
858 * fflush(stderr);*/
861 return IE_SUCCESS;
865 /* Set timeout in milliseconds for each network job like connecting to server
866 * or sending message. Use 0 to disable timeout limits. */
867 isds_error isds_set_timeout(struct isds_ctx *context,
868 const unsigned int timeout) {
869 if (!context) return IE_INVALID_CONTEXT;
870 zfree(context->long_message);
872 context->timeout = timeout;
874 if (context->curl) {
875 CURLcode curl_err;
877 curl_err = curl_easy_setopt(context->curl, CURLOPT_NOSIGNAL, 1);
878 if (!curl_err)
879 #if HAVE_DECL_CURLOPT_TIMEOUT_MS /* Since curl-7.16.2 */
880 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT_MS,
881 context->timeout);
882 #else
883 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT,
884 context->timeout / 1000);
885 #endif /* not HAVE_DECL_CURLOPT_TIMEOUT_MS */
886 if (curl_err) return IE_ERROR;
889 return IE_SUCCESS;
893 /* Register callback function libisds calls periodically during HTTP data
894 * transfer.
895 * @context is session context
896 * @callback is function provided by application libisds will call. See type
897 * definition for @callback argument explanation.
898 * @data is application specific data @callback gets as last argument */
899 isds_error isds_set_progress_callback(struct isds_ctx *context,
900 isds_progress_callback callback, void *data) {
901 if (!context) return IE_INVALID_CONTEXT;
902 zfree(context->long_message);
904 context->progress_callback = callback;
905 context->progress_callback_data = data;
907 return IE_SUCCESS;
911 /* Change context settings.
912 * @context is context which setting will be applied to
913 * @option is name of option. It determines the type of last argument. See
914 * isds_option definition for more info.
915 * @... is value of new setting. Type is determined by @option
916 * */
917 isds_error isds_set_opt(struct isds_ctx *context, const isds_option option,
918 ...) {
919 isds_error err = IE_SUCCESS;
920 va_list ap;
921 char *pointer, *string;
923 if (!context) return IE_INVALID_CONTEXT;
924 zfree(context->long_message);
926 va_start(ap, option);
928 #define REPLACE_VA_BOOLEAN(destination) { \
929 if (!(destination)) { \
930 (destination) = malloc(sizeof(*(destination))); \
931 if (!(destination)) { \
932 err = IE_NOMEM; goto leave; \
935 *(destination) = (_Bool) !!va_arg(ap, int); \
938 #define REPLACE_VA_STRING(destination) { \
939 string = va_arg(ap, char *); \
940 if (string) { \
941 pointer = realloc((destination), 1 + strlen(string)); \
942 if (!pointer) { err = IE_NOMEM; goto leave; } \
943 strcpy(pointer, string); \
944 (destination) = pointer; \
945 } else { \
946 free(destination); \
947 (destination) = NULL; \
951 switch (option) {
952 case IOPT_TLS_VERIFY_SERVER:
953 REPLACE_VA_BOOLEAN(context->tls_verify_server);
954 break;
955 case IOPT_TLS_CA_FILE:
956 REPLACE_VA_STRING(context->tls_ca_file);
957 break;
958 case IOPT_TLS_CA_DIRECTORY:
959 REPLACE_VA_STRING(context->tls_ca_dir);
960 break;
961 case IOPT_TLS_CRL_FILE:
962 #if HAVE_DECL_CURLOPT_CRLFILE /* Since curl-7.19.0 */
963 REPLACE_VA_STRING(context->tls_crl_file);
964 #else
965 isds_log_message(context,
966 _("Curl library does not support CRL definition"));
967 err = IE_NOTSUP;
968 #endif /* not HAVE_DECL_CURLOPT_CRLFILE */
969 break;
970 case IOPT_NORMALIZE_MIME_TYPE:
971 context->normalize_mime_type = (_Bool) !!va_arg(ap, int);
972 break;
974 default:
975 err = IE_ENUM; goto leave;
978 #undef REPLACE_VA_STRING
979 #undef REPLACE_VA_BOOLEAN
981 leave:
982 va_end(ap);
983 return err;
987 /* Deprecated: Use isds_set_opt() instead.
988 * Change SSL/TLS settings.
989 * @context is context which setting will be applied to
990 * @option is name of option. It determines the type of last argument. See
991 * isds_tls_option definition for more info.
992 * @... is value of new setting. Type is determined by @option
993 * */
994 isds_error isds_set_tls(struct isds_ctx *context, const isds_tls_option option,
995 ...) {
996 isds_error err = IE_ENUM;
997 va_list ap;
999 va_start(ap, option);
1001 switch (option) {
1002 case ITLS_VERIFY_SERVER:
1003 err = isds_set_opt(context, option, va_arg(ap, int));
1004 break;
1005 case ITLS_CA_FILE:
1006 case ITLS_CA_DIRECTORY:
1007 case ITLS_CRL_FILE:
1008 err = isds_set_opt(context, option, va_arg(ap, char*));
1011 va_end(ap);
1012 return err;
1016 /* Connect and log in into ISDS server.
1017 * All required arguments will be copied, you do not have to keep them after
1018 * that.
1019 * ISDS supports four different authentication methods. Exact method is
1020 * selected on @username, @passwors and @pki_credentials arguments:
1021 * - If @pki_credentials == NULL, @username and @password must be supplied
1022 * - If @pki_credentials != NULL, then
1023 * - If @username == NULL, only certificate will be used
1024 * - If @username != NULL, then
1025 * - If @password == NULL, then certificate will be used and
1026 * @username shifts meaning to box ID. This is used for hosted
1027 * services.
1028 * - Otherwise all three arguments will be used.
1029 * Please note, that different cases requires different certificate type
1030 * (system qualified one or commercial non qualified one). This library does
1031 * not check such political issues. Please see ISDS Specification for more
1032 * details.
1033 * @url is base address of ISDS web service. Pass extern isds_locator
1034 * variable to use production ISDS instance without client certificate
1035 * authentication (or extern isds_cert_locator with client certificate
1036 * authentication). Passing NULL has the same effect, autoselection between
1037 * isds_locator and isds_cert_locator is performed in addition. You can pass
1038 * extern isds_testing_locator (or isds_cert_testing_locator) variable to
1039 * select testing instance.
1040 * @username is user name of ISDS user or box ID
1041 * @password is user's secret password
1042 * @pki_credentials defines public key cryptographic material to use in client
1043 * authentication. */
1044 isds_error isds_login(struct isds_ctx *context, const char *url,
1045 const char *username, const char *password,
1046 const struct isds_pki_credentials *pki_credentials) {
1047 isds_error err = IE_NOT_LOGGED_IN;
1048 isds_error soap_err;
1049 xmlNsPtr isds_ns = NULL;
1050 xmlNodePtr request = NULL;
1051 xmlNodePtr response = NULL;
1053 if (!context) return IE_INVALID_CONTEXT;
1054 zfree(context->long_message);
1056 /* Close connection if already logged in */
1057 if (context->curl) {
1058 _isds_close_connection(context);
1061 /* Store configuration */
1062 context->type = CTX_TYPE_ISDS;
1063 zfree(context->url);
1065 /* Mangle base URI according requested authentication method */
1066 if (!pki_credentials) {
1067 isds_log(ILF_SEC, ILL_INFO,
1068 _("Selected authentication method: no certificate, "
1069 "username and password\n"));
1070 if (!username || !password) {
1071 isds_log_message(context,
1072 _("Both username and password must be supplied"));
1073 return IE_INVAL;
1075 /* Default locator is official system (without client certificate) */
1076 context->url = strdup((url) ? url : isds_locator);
1077 } else {
1078 /* Default locator is official system (with client certificate) */
1079 if (!url) url = isds_cert_locator;
1081 if (!username) {
1082 isds_log(ILF_SEC, ILL_INFO,
1083 _("Selected authentication method: system certificate, "
1084 "no username and no password\n"));
1085 password = NULL;
1086 context->url = _isds_astrcat(url, "cert/");
1087 } else {
1088 if (!password) {
1089 isds_log(ILF_SEC, ILL_INFO,
1090 _("Selected authentication method: system certificate, "
1091 "box ID and no password\n"));
1092 context->url = _isds_astrcat(url, "hspis/");
1093 } else {
1094 isds_log(ILF_SEC, ILL_INFO,
1095 _("Selected authentication method: commercial "
1096 "certificate, username and password\n"));
1097 context->url = _isds_astrcat(url, "certds/");
1101 if (!(context->url))
1102 return IE_NOMEM;
1104 /* Prepare CURL handle */
1105 context->curl = curl_easy_init();
1106 if (!(context->curl))
1107 return IE_ERROR;
1109 /* Build log-in request */
1110 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1111 if (!request) {
1112 isds_log_message(context, _("Could build ISDS log-in request"));
1113 return IE_ERROR;
1115 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1116 if(!isds_ns) {
1117 isds_log_message(context, _("Could not create ISDS name space"));
1118 xmlFreeNode(request);
1119 return IE_ERROR;
1121 xmlSetNs(request, isds_ns);
1123 /* Store credentials */
1124 /* FIXME: mlock password
1125 * (I have a library) */
1126 discard_credentials(context);
1127 if (username) context->username = strdup(username);
1128 if (password) context->password = strdup(password);
1129 context->pki_credentials = isds_pki_credentials_duplicate(pki_credentials);
1130 if ((username && !context->username) || (password && !context->password) ||
1131 (pki_credentials && !context->pki_credentials)) {
1132 discard_credentials(context);
1133 xmlFreeNode(request);
1134 return IE_NOMEM;
1137 isds_log(ILF_ISDS, ILL_DEBUG, _("Logging user %s into server %s\n"),
1138 username, url);
1140 /* Send log-in request */
1141 soap_err = _isds_soap(context, "DS/dz", request, &response, NULL, NULL);
1143 /* Remove credentials */
1144 discard_credentials(context);
1146 /* Destroy log-in request */
1147 xmlFreeNode(request);
1149 if (soap_err) {
1150 xmlFreeNodeList(response);
1151 _isds_close_connection(context);
1152 return soap_err;
1155 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1156 * authentication succeeded if soap_err == IE_SUCCESS */
1157 err = IE_SUCCESS;
1159 xmlFreeNodeList(response);
1161 if (!err)
1162 isds_log(ILF_ISDS, ILL_DEBUG,
1163 _("User %s has been logged into server %s successfully\n"),
1164 username, url);
1165 return err;
1169 /* Log out from ISDS server discards credentials and connection configuration. */
1170 isds_error isds_logout(struct isds_ctx *context) {
1171 if (!context) return IE_INVALID_CONTEXT;
1172 zfree(context->long_message);
1174 /* Close connection */
1175 if (context->curl) {
1176 _isds_close_connection(context);
1178 /* Discard credentials for sure. They should not survive isds_login(),
1179 * even successful .*/
1180 discard_credentials(context);
1181 zfree(context->url);
1183 isds_log(ILF_ISDS, ILL_DEBUG, _("Logged out from ISDS server\n"));
1184 } else {
1185 discard_credentials(context);
1187 return IE_SUCCESS;
1191 /* Verify connection to ISDS is alive and server is responding.
1192 * Sent dummy request to ISDS and expect dummy response. */
1193 isds_error isds_ping(struct isds_ctx *context) {
1194 isds_error soap_err;
1195 xmlNsPtr isds_ns = NULL;
1196 xmlNodePtr request = NULL;
1197 xmlNodePtr response = NULL;
1199 if (!context) return IE_INVALID_CONTEXT;
1200 zfree(context->long_message);
1202 /* Check if connection is established */
1203 if (!context->curl) return IE_CONNECTION_CLOSED;
1206 /* Build dummy request */
1207 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1208 if (!request) {
1209 isds_log_message(context, _("Could build ISDS dummy request"));
1210 return IE_ERROR;
1212 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1213 if(!isds_ns) {
1214 isds_log_message(context, _("Could not create ISDS name space"));
1215 xmlFreeNode(request);
1216 return IE_ERROR;
1218 xmlSetNs(request, isds_ns);
1220 isds_log(ILF_ISDS, ILL_DEBUG, _("Pinging ISDS server\n"));
1222 /* Sent dummy request */
1223 soap_err = _isds_soap(context, "DS/dz", request, &response, NULL, NULL);
1225 /* Destroy log-in request */
1226 xmlFreeNode(request);
1228 if (soap_err) {
1229 isds_log(ILF_ISDS, ILL_DEBUG,
1230 _("ISDS server could not be contacted\n"));
1231 xmlFreeNodeList(response);
1232 return soap_err;
1235 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1236 * authentication succeeded if soap_err == IE_SUCCESS */
1237 /* TODO: ISDS documentation does not specify response body.
1238 * However real server sends back DummyOperationResponse */
1241 xmlFreeNodeList(response);
1243 isds_log(ILF_ISDS, ILL_DEBUG, _("ISDS server alive\n"));
1245 return IE_SUCCESS;
1249 /* Send bogus request to ISDS.
1250 * Just for test purposes */
1251 isds_error isds_bogus_request(struct isds_ctx *context) {
1252 isds_error err;
1253 xmlNsPtr isds_ns = NULL;
1254 xmlNodePtr request = NULL;
1255 xmlDocPtr response = NULL;
1256 xmlChar *code = NULL, *message = NULL;
1258 if (!context) return IE_INVALID_CONTEXT;
1259 zfree(context->long_message);
1261 /* Check if connection is established */
1262 if (!context->curl) {
1263 /* Testing printf message */
1264 isds_printf_message(context, "%s", _("I said connection closed"));
1265 return IE_CONNECTION_CLOSED;
1269 /* Build dummy request */
1270 request = xmlNewNode(NULL, BAD_CAST "X-BogusOperation");
1271 if (!request) {
1272 isds_log_message(context, _("Could build ISDS bogus request"));
1273 return IE_ERROR;
1275 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1276 if(!isds_ns) {
1277 isds_log_message(context, _("Could not create ISDS name space"));
1278 xmlFreeNode(request);
1279 return IE_ERROR;
1281 xmlSetNs(request, isds_ns);
1283 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending bogus request to ISDS\n"));
1285 /* Sent bogus request */
1286 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
1288 /* Destroy request */
1289 xmlFreeNode(request);
1291 if (err) {
1292 isds_log(ILF_ISDS, ILL_DEBUG,
1293 _("Processing ISDS response on bogus request failed\n"));
1294 xmlFreeDoc(response);
1295 return err;
1298 /* Check for response status */
1299 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
1300 &code, &message, NULL);
1301 if (err) {
1302 isds_log(ILF_ISDS, ILL_DEBUG,
1303 _("ISDS response on bogus request is missing status\n"));
1304 free(code);
1305 free(message);
1306 xmlFreeDoc(response);
1307 return err;
1309 if (xmlStrcmp(code, BAD_CAST "0000")) {
1310 char *code_locale = _isds_utf82locale((char*)code);
1311 char *message_locale = _isds_utf82locale((char*)message);
1312 isds_log(ILF_ISDS, ILL_DEBUG,
1313 _("Server refused bogus request (code=%s, message=%s)\n"),
1314 code_locale, message_locale);
1315 /* XXX: Literal error messages from ISDS are Czech messages
1316 * (English sometimes) in UTF-8. It's hard to catch them for
1317 * translation. Successfully gettextized would return in locale
1318 * encoding, unsuccessfully translated would pass in UTF-8. */
1319 isds_log_message(context, message_locale);
1320 free(code_locale);
1321 free(message_locale);
1322 free(code);
1323 free(message);
1324 xmlFreeDoc(response);
1325 return IE_ISDS;
1329 free(code);
1330 free(message);
1331 xmlFreeDoc(response);
1333 isds_log(ILF_ISDS, ILL_DEBUG,
1334 _("Bogus message accepted by server. This should not happen.\n"));
1336 return IE_SUCCESS;
1340 /* Serialize XML subtree to buffer preserving XML indentation.
1341 * @context is session context
1342 * @subtree is XML element to be serialized (with children)
1343 * @buffer is automatically reallocated buffer where serialize to
1344 * @length is size of serialized stream in bytes
1345 * @return standard error code, free @buffer in case of error */
1346 static isds_error serialize_subtree(struct isds_ctx *context,
1347 xmlNodePtr subtree, void **buffer, size_t *length) {
1348 isds_error err = IE_SUCCESS;
1349 xmlBufferPtr xml_buffer = NULL;
1350 xmlSaveCtxtPtr save_ctx = NULL;
1351 xmlDocPtr subtree_doc = NULL;
1352 xmlNodePtr subtree_copy;
1353 xmlNsPtr isds_ns;
1354 void *new_buffer;
1356 if (!context) return IE_INVALID_CONTEXT;
1357 if (!buffer) return IE_INVAL;
1358 zfree(*buffer);
1359 if (!subtree || !length) return IE_INVAL;
1361 /* Make temporary XML document with @subtree root element */
1362 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1363 * It can result in not well-formed on invalid XML tree (e.g. name space
1364 * prefix definition can miss. */
1365 /*FIXME */
1367 subtree_doc = xmlNewDoc(BAD_CAST "1.0");
1368 if (!subtree_doc) {
1369 isds_log_message(context, _("Could not build temporary document"));
1370 err = IE_ERROR;
1371 goto leave;
1374 /* XXX: Copy subtree and attach the copy to document.
1375 * One node can not bee attached into more document at the same time.
1376 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1377 * automatically.
1378 * XXX: Check xmlSaveTree() too. */
1379 subtree_copy = xmlCopyNodeList(subtree);
1380 if (!subtree_copy) {
1381 isds_log_message(context, _("Could not copy subtree"));
1382 err = IE_ERROR;
1383 goto leave;
1385 xmlDocSetRootElement(subtree_doc, subtree_copy);
1387 /* Only this way we get namespace definition as @xmlns:isds,
1388 * otherwise we get namespace prefix without definition */
1389 /* FIXME: Don't overwrite original default namespace */
1390 isds_ns = xmlNewNs(subtree_copy, BAD_CAST ISDS_NS, NULL);
1391 if(!isds_ns) {
1392 isds_log_message(context, _("Could not create ISDS name space"));
1393 err = IE_ERROR;
1394 goto leave;
1396 xmlSetNs(subtree_copy, isds_ns);
1399 /* Serialize the document into buffer */
1400 xml_buffer = xmlBufferCreate();
1401 if (!xml_buffer) {
1402 isds_log_message(context, _("Could not create xmlBuffer"));
1403 err = IE_ERROR;
1404 goto leave;
1406 /* Last argument 0 means to not format the XML tree */
1407 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8", 0);
1408 if (!save_ctx) {
1409 isds_log_message(context, _("Could not create XML serializer"));
1410 err = IE_ERROR;
1411 goto leave;
1413 /* XXX: According LibXML documentation, this function does not return
1414 * meaningful value yet */
1415 xmlSaveDoc(save_ctx, subtree_doc);
1416 if (-1 == xmlSaveFlush(save_ctx)) {
1417 isds_log_message(context,
1418 _("Could not serialize XML subtree"));
1419 err = IE_ERROR;
1420 goto leave;
1422 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1423 * even after xmlSaveFlush(). Thus close it here */
1424 xmlSaveClose(save_ctx); save_ctx = NULL;
1427 /* Store and detach buffer from xml_buffer */
1428 *buffer = xml_buffer->content;
1429 *length = xml_buffer->use;
1430 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1432 /* Shrink buffer */
1433 new_buffer = realloc(*buffer, *length);
1434 if (new_buffer) *buffer = new_buffer;
1436 leave:
1437 if (err) {
1438 zfree(*buffer);
1439 *length = 0;
1442 xmlSaveClose(save_ctx);
1443 xmlBufferFree(xml_buffer);
1444 xmlFreeDoc(subtree_doc); /* Frees subtree_copy, isds_ns etc. */
1445 return err;
1448 #if 0
1449 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1450 * @context is session context
1451 * @document is original document where @nodeset points to
1452 * @nodeset is XPath node set to dump (recursively)
1453 * @buffer is automatically reallocated buffer where serialize to
1454 * @length is size of serialized stream in bytes
1455 * @return standard error code, free @buffer in case of error */
1456 static isds_error dump_nodeset(struct isds_ctx *context,
1457 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1458 void **buffer, size_t *length) {
1459 isds_error err = IE_SUCCESS;
1460 xmlBufferPtr xml_buffer = NULL;
1461 void *new_buffer;
1463 if (!context) return IE_INVALID_CONTEXT;
1464 if (!buffer) return IE_INVAL;
1465 zfree(*buffer);
1466 if (!document || !nodeset || !length) return IE_INVAL;
1467 *length = 0;
1469 /* Empty node set results into NULL buffer */
1470 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1471 goto leave;
1474 /* Resulting the document into buffer */
1475 xml_buffer = xmlBufferCreate();
1476 if (!xml_buffer) {
1477 isds_log_message(context, _("Could not create xmlBuffer"));
1478 err = IE_ERROR;
1479 goto leave;
1482 /* Iterate over all nodes */
1483 for (int i = 0; i < nodeset->nodeNr; i++) {
1484 /* Serialize node.
1485 * XXX: xmlNodeDump() appends to xml_buffer. */
1486 if (-1 ==
1487 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1488 isds_log_message(context, _("Could not dump XML node"));
1489 err = IE_ERROR;
1490 goto leave;
1494 /* Store and detach buffer from xml_buffer */
1495 *buffer = xml_buffer->content;
1496 *length = xml_buffer->use;
1497 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1499 /* Shrink buffer */
1500 new_buffer = realloc(*buffer, *length);
1501 if (new_buffer) *buffer = new_buffer;
1504 leave:
1505 if (err) {
1506 zfree(*buffer);
1507 *length = 0;
1510 xmlBufferFree(xml_buffer);
1511 return err;
1513 #endif
1515 #if 0
1516 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1517 * @context is session context
1518 * @document is original document where @nodeset points to
1519 * @nodeset is XPath node set to dump (recursively)
1520 * @buffer is automatically reallocated buffer where serialize to
1521 * @length is size of serialized stream in bytes
1522 * @return standard error code, free @buffer in case of error */
1523 static isds_error dump_nodeset(struct isds_ctx *context,
1524 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1525 void **buffer, size_t *length) {
1526 isds_error err = IE_SUCCESS;
1527 xmlBufferPtr xml_buffer = NULL;
1528 xmlSaveCtxtPtr save_ctx = NULL;
1529 void *new_buffer;
1531 if (!context) return IE_INVALID_CONTEXT;
1532 if (!buffer) return IE_INVAL;
1533 zfree(*buffer);
1534 if (!document || !nodeset || !length) return IE_INVAL;
1535 *length = 0;
1537 /* Empty node set results into NULL buffer */
1538 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1539 goto leave;
1542 /* Resulting the document into buffer */
1543 xml_buffer = xmlBufferCreate();
1544 if (!xml_buffer) {
1545 isds_log_message(context, _("Could not create xmlBuffer"));
1546 err = IE_ERROR;
1547 goto leave;
1549 if (xmlSubstituteEntitiesDefault(1)) {
1550 isds_log_message(context, _("Could not disable attribute escaping"));
1551 err = IE_ERROR;
1552 goto leave;
1554 /* Last argument means:
1555 * 0 to not format the XML tree
1556 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1557 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8",
1558 XML_SAVE_NO_DECL|XML_SAVE_NO_EMPTY|XML_SAVE_NO_XHTML);
1559 if (!save_ctx) {
1560 isds_log_message(context, _("Could not create XML serializer"));
1561 err = IE_ERROR;
1562 goto leave;
1564 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1565 isds_log_message(context, _("Could not disable attribute escaping"));
1566 err = IE_ERROR;
1567 goto leave;
1571 /* Iterate over all nodes */
1572 for (int i = 0; i < nodeset->nodeNr; i++) {
1573 /* Serialize node.
1574 * XXX: xmlNodeDump() appends to xml_buffer. */
1575 /*if (-1 ==
1576 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1578 /* XXX: According LibXML documentation, this function does not return
1579 * meaningful value yet */
1580 xmlSaveTree(save_ctx, nodeset->nodeTab[i]);
1581 if (-1 == xmlSaveFlush(save_ctx)) {
1582 isds_log_message(context,
1583 _("Could not serialize XML subtree"));
1584 err = IE_ERROR;
1585 goto leave;
1589 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1590 * even after xmlSaveFlush(). Thus close it here */
1591 xmlSaveClose(save_ctx); save_ctx = NULL;
1593 /* Store and detach buffer from xml_buffer */
1594 *buffer = xml_buffer->content;
1595 *length = xml_buffer->use;
1596 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1598 /* Shrink buffer */
1599 new_buffer = realloc(*buffer, *length);
1600 if (new_buffer) *buffer = new_buffer;
1602 leave:
1603 if (err) {
1604 zfree(*buffer);
1605 *length = 0;
1608 xmlSaveClose(save_ctx);
1609 xmlBufferFree(xml_buffer);
1610 return err;
1612 #endif
1615 /* Convert UTF-8 @string representation of ISDS dbType to enum @type */
1616 static isds_error string2isds_DbType(xmlChar *string, isds_DbType *type) {
1617 if (!string || !type) return IE_INVAL;
1619 if (!xmlStrcmp(string, BAD_CAST "FO"))
1620 *type = DBTYPE_FO;
1621 else if (!xmlStrcmp(string, BAD_CAST "PFO"))
1622 *type = DBTYPE_PFO;
1623 else if (!xmlStrcmp(string, BAD_CAST "PFO_ADVOK"))
1624 *type = DBTYPE_PFO_ADVOK;
1625 else if (!xmlStrcmp(string, BAD_CAST "PFO_DANPOR"))
1626 *type = DBTYPE_PFO_DANPOR;
1627 else if (!xmlStrcmp(string, BAD_CAST "PFO_INSSPR"))
1628 *type = DBTYPE_PFO_INSSPR;
1629 else if (!xmlStrcmp(string, BAD_CAST "PO"))
1630 *type = DBTYPE_PO;
1631 else if (!xmlStrcmp(string, BAD_CAST "PO_ZAK"))
1632 *type = DBTYPE_PO_ZAK;
1633 else if (!xmlStrcmp(string, BAD_CAST "PO_REQ"))
1634 *type = DBTYPE_PO_REQ;
1635 else if (!xmlStrcmp(string, BAD_CAST "OVM"))
1636 *type = DBTYPE_OVM;
1637 else if (!xmlStrcmp(string, BAD_CAST "OVM_NOTAR"))
1638 *type = DBTYPE_OVM_NOTAR;
1639 else if (!xmlStrcmp(string, BAD_CAST "OVM_EXEKUT"))
1640 *type = DBTYPE_OVM_EXEKUT;
1641 else if (!xmlStrcmp(string, BAD_CAST "OVM_REQ"))
1642 *type = DBTYPE_OVM_REQ;
1643 else
1644 return IE_ENUM;
1645 return IE_SUCCESS;
1649 /* Convert ISDS dbType enum @type to UTF-8 string.
1650 * @Return pointer to static string, or NULL if unknown enum value */
1651 static const xmlChar *isds_DbType2string(const isds_DbType type) {
1652 switch(type) {
1653 /* DBTYPE_SYSTEM is invalid value from point of view of public
1654 * SOAP interface. */
1655 case DBTYPE_FO: return(BAD_CAST "FO"); break;
1656 case DBTYPE_PFO: return(BAD_CAST "PFO"); break;
1657 case DBTYPE_PFO_ADVOK: return(BAD_CAST "PFO_ADVOK"); break;
1658 case DBTYPE_PFO_DANPOR: return(BAD_CAST "PFO_DANPOR"); break;
1659 case DBTYPE_PFO_INSSPR: return(BAD_CAST "PFO_INSSPR"); break;
1660 case DBTYPE_PO: return(BAD_CAST "PO"); break;
1661 case DBTYPE_PO_ZAK: return(BAD_CAST "PO_ZAK"); break;
1662 case DBTYPE_PO_REQ: return(BAD_CAST "PO_REQ"); break;
1663 case DBTYPE_OVM: return(BAD_CAST "OVM"); break;
1664 case DBTYPE_OVM_NOTAR: return(BAD_CAST "OVM_NOTAR"); break;
1665 case DBTYPE_OVM_EXEKUT: return(BAD_CAST "OVM_EXEKUT"); break;
1666 case DBTYPE_OVM_REQ: return(BAD_CAST "OVM_REQ"); break;
1667 default: return NULL; break;
1672 /* Convert UTF-8 @string representation of ISDS userType to enum @type */
1673 static isds_error string2isds_UserType(xmlChar *string, isds_UserType *type) {
1674 if (!string || !type) return IE_INVAL;
1676 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
1677 *type = USERTYPE_PRIMARY;
1678 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
1679 *type = USERTYPE_ENTRUSTED;
1680 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
1681 *type = USERTYPE_ADMINISTRATOR;
1682 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
1683 *type = USERTYPE_OFFICIAL;
1684 else
1685 return IE_ENUM;
1686 return IE_SUCCESS;
1690 /* Convert ISDS userType enum @type to UTF-8 string.
1691 * @Return pointer to static string, or NULL if unknown enum value */
1692 static const xmlChar *isds_UserType2string(const isds_UserType type) {
1693 switch(type) {
1694 case USERTYPE_PRIMARY: return(BAD_CAST "PRIMARY_USER"); break;
1695 case USERTYPE_ENTRUSTED: return(BAD_CAST "ENTRUSTED_USER"); break;
1696 case USERTYPE_ADMINISTRATOR: return(BAD_CAST "ADMINISTRATOR"); break;
1697 case USERTYPE_OFFICIAL: return(BAD_CAST "OFFICIAL"); break;
1698 default: return NULL; break;
1703 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
1704 * @Return pointer to static string, or NULL if unknown enum value */
1705 static const xmlChar *isds_FileMetaType2string(const isds_FileMetaType type) {
1706 switch(type) {
1707 case FILEMETATYPE_MAIN: return(BAD_CAST "main"); break;
1708 case FILEMETATYPE_ENCLOSURE: return(BAD_CAST "enclosure"); break;
1709 case FILEMETATYPE_SIGNATURE: return(BAD_CAST "signature"); break;
1710 case FILEMETATYPE_META: return(BAD_CAST "meta"); break;
1711 default: return NULL; break;
1716 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
1717 * @Return IE_ENUM if @string is not valid enum member */
1718 static isds_error string2isds_FileMetaType(const xmlChar *string,
1719 isds_FileMetaType *type) {
1720 if (!string || !type) return IE_INVAL;
1722 if (!xmlStrcmp(string, BAD_CAST "main"))
1723 *type = FILEMETATYPE_MAIN;
1724 else if (!xmlStrcmp(string, BAD_CAST "enclosure"))
1725 *type = FILEMETATYPE_ENCLOSURE;
1726 else if (!xmlStrcmp(string, BAD_CAST "signature"))
1727 *type = FILEMETATYPE_SIGNATURE;
1728 else if (!xmlStrcmp(string, BAD_CAST "meta"))
1729 *type = FILEMETATYPE_META;
1730 else
1731 return IE_ENUM;
1732 return IE_SUCCESS;
1736 /* Convert UTF-8 @string to ISDS hash @algorithm.
1737 * @Return IE_ENUM if @string is not valid enum member */
1738 static isds_error string2isds_hash_algorithm(const xmlChar *string,
1739 isds_hash_algorithm *algorithm) {
1740 if (!string || !algorithm) return IE_INVAL;
1742 if (!xmlStrcmp(string, BAD_CAST "MD5"))
1743 *algorithm = HASH_ALGORITHM_MD5;
1744 else if (!xmlStrcmp(string, BAD_CAST "SHA-1"))
1745 *algorithm = HASH_ALGORITHM_SHA_1;
1746 else if (!xmlStrcmp(string, BAD_CAST "SHA-224"))
1747 *algorithm = HASH_ALGORITHM_SHA_224;
1748 else if (!xmlStrcmp(string, BAD_CAST "SHA-256"))
1749 *algorithm = HASH_ALGORITHM_SHA_256;
1750 else if (!xmlStrcmp(string, BAD_CAST "SHA-384"))
1751 *algorithm = HASH_ALGORITHM_SHA_384;
1752 else if (!xmlStrcmp(string, BAD_CAST "SHA-512"))
1753 *algorithm = HASH_ALGORITHM_SHA_512;
1754 else
1755 return IE_ENUM;
1756 return IE_SUCCESS;
1760 /* Convert UTF-8 @string representation of ISO 8601 date to @time.
1761 * XXX: Not all ISO formats are supported */
1762 static isds_error datestring2tm(const xmlChar *string, struct tm *time) {
1763 char *offset;
1764 if (!string || !time) return IE_INVAL;
1766 /* xsd:date is ISO 8601 string, thus ASCII */
1767 offset = strptime((char*)string, "%Y-%m-%d", time);
1768 if (offset && *offset == '\0')
1769 return IE_SUCCESS;
1771 offset = strptime((char*)string, "%Y%m%d", time);
1772 if (offset && *offset == '\0')
1773 return IE_SUCCESS;
1775 offset = strptime((char*)string, "%Y-%j", time);
1776 if (offset && *offset == '\0')
1777 return IE_SUCCESS;
1779 return IE_NOTSUP;
1783 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
1784 static isds_error tm2datestring(const struct tm *time, xmlChar **string) {
1785 if (!time || !string) return IE_INVAL;
1787 if (-1 == isds_asprintf((char **) string, "%d-%02d-%02d",
1788 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
1789 return IE_ERROR;
1791 return IE_SUCCESS;
1795 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
1796 * respects the @time microseconds too. */
1797 static isds_error timeval2timestring(const struct timeval *time,
1798 xmlChar **string) {
1799 struct tm broken;
1801 if (!time || !string) return IE_INVAL;
1803 if (!gmtime_r(&time->tv_sec, &broken)) return IE_DATE;
1804 if (time->tv_usec < 0 || time->tv_usec > 999999) return IE_DATE;
1806 /* TODO: small negative year should be formatted as "-0012". This is not
1807 * true for glibc "%04d". We should implement it.
1808 * TODO: What's type of time->tv_usec exactly? Unsigned? Absolute?
1809 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
1810 if (-1 == isds_asprintf((char **) string,
1811 "%04d-%02d-%02dT%02d:%02d:%02d.%06ld",
1812 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
1813 broken.tm_hour, broken.tm_min, broken.tm_sec,
1814 time->tv_usec))
1815 return IE_ERROR;
1817 return IE_SUCCESS;
1821 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
1822 * It respects microseconds too.
1823 * In case of error, @time will be freed. */
1824 static isds_error timestring2timeval(const xmlChar *string,
1825 struct timeval **time) {
1826 struct tm broken;
1827 char *offset, *delim, *endptr;
1828 char subseconds[7];
1829 int offset_hours, offset_minutes;
1830 int i;
1832 if (!time) return IE_INVAL;
1834 memset(&broken, 0, sizeof(broken));
1836 if (!*time) {
1837 *time = calloc(1, sizeof(**time));
1838 if (!*time) return IE_NOMEM;
1839 } else {
1840 memset(*time, 0, sizeof(**time));
1844 /* xsd:date is ISO 8601 string, thus ASCII */
1845 /*TODO: negative year */
1847 /* Parse date and time without subseconds and offset */
1848 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
1849 if (!offset) {
1850 free(*time); *time = NULL;
1851 return IE_DATE;
1854 /* Get subseconds */
1855 if (*offset == '.' ) {
1856 offset++;
1858 /* Copy first 6 digits, pad it with zeros.
1859 * XXX: It truncates longer number, no round.
1860 * Current server implementation uses only millisecond resolution. */
1861 /* TODO: isdigit() is locale sensitive */
1862 for (i = 0;
1863 i < sizeof(subseconds)/sizeof(char) - 1 && isdigit(*offset);
1864 i++, offset++) {
1865 subseconds[i] = *offset;
1867 for (; i < sizeof(subseconds)/sizeof(char) - 1; i++) {
1868 subseconds[i] = '0';
1870 subseconds[6] = '\0';
1872 /* Convert it into integer */
1873 (*time)->tv_usec = strtol(subseconds, &endptr, 10);
1874 if (*endptr != '\0' || (*time)->tv_usec == LONG_MIN ||
1875 (*time)->tv_usec == LONG_MAX) {
1876 free(*time); *time = NULL;
1877 return IE_DATE;
1880 /* move to the zone offset delimiter */
1881 delim = strchr(offset, '-');
1882 if (!delim)
1883 delim = strchr(offset, '+');
1884 offset = delim;
1887 /* Get zone offset */
1888 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
1889 * "" equals to "Z" and it means UTC zone. */
1890 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
1891 * colon separator */
1892 if (*offset == '-' || *offset == '+') {
1893 offset++;
1894 if (2 != sscanf(offset, "%2d:%2d", &offset_hours, &offset_minutes)) {
1895 free(*time); *time = NULL;
1896 return IE_DATE;
1898 broken.tm_hour -= offset_hours;
1899 broken.tm_min -= offset_minutes * ((offset_hours<0) ? -1 : 1);
1902 /* Convert to time_t */
1903 _isds_switch_tz_to_utc();
1904 (*time)->tv_sec = mktime(&broken);
1905 _isds_switch_tz_to_native();
1906 if ((*time)->tv_sec == (time_t) -1) {
1907 free(*time); *time = NULL;
1908 return IE_DATE;
1911 return IE_SUCCESS;
1915 /* Convert unsigned int into isds_message_status.
1916 * @context is session context
1917 * @number is pointer to number value. NULL will be treated as invalid value.
1918 * @status is automatically reallocated status
1919 * @return IE_SUCCESS, or error code and free status */
1920 static isds_error uint2isds_message_status(struct isds_ctx *context,
1921 const unsigned long int *number, isds_message_status **status) {
1922 if (!context) return IE_INVALID_CONTEXT;
1923 if (!status) return IE_INVAL;
1925 free(*status); *status = NULL;
1926 if (!number) return IE_INVAL;
1928 if (*number < 1 || *number > 10) {
1929 isds_printf_message(context, _("Invalid message status value: %lu"),
1930 *number);
1931 return IE_ENUM;
1934 *status = malloc(sizeof(**status));
1935 if (!*status) return IE_NOMEM;
1937 **status = 1 << *number;
1938 return IE_SUCCESS;
1942 /* Convert event description string into isds_event members type and
1943 * description
1944 * @string is raw event description starting with event prefix
1945 * @event is structure where to store type and stripped description to
1946 * @return standard error code, unknown prefix is not classified as an error.
1947 * */
1948 static isds_error eventstring2event(const xmlChar *string,
1949 struct isds_event* event) {
1950 const xmlChar *known_prefixes[] = {
1951 BAD_CAST "EV1:",
1952 BAD_CAST "EV2:",
1953 BAD_CAST "EV3:",
1954 BAD_CAST "EV4:"
1956 const isds_event_type types[] = {
1957 EVENT_ACCEPTED_BY_RECIPIENT,
1958 EVENT_ACCEPTED_BY_FICTION,
1959 EVENT_UNDELIVERABLE,
1960 EVENT_COMMERCIAL_ACCEPTED
1962 unsigned int index;
1963 size_t length;
1965 if (!string || !event) return IE_INVAL;
1967 if (!event->type) {
1968 event->type = malloc(sizeof(*event->type));
1969 if (!(event->type)) return IE_NOMEM;
1971 zfree(event->description);
1973 for (index = 0; index < sizeof(known_prefixes)/sizeof(known_prefixes[0]);
1974 index++) {
1975 length = xmlUTF8Strlen(known_prefixes[index]);
1977 if (!xmlStrncmp(string, known_prefixes[index], length)) {
1978 /* Prefix is known */
1979 *event->type = types[index];
1981 /* Strip prefix from description and spaces */
1982 /* TODO: Recognize all white spaces from UCS blank class and
1983 * operate on UTF-8 chars. */
1984 for (; string[length] != '\0' && string[length] == ' '; length++);
1985 event->description = strdup((char *) (string + length));
1986 if (!(event->description)) return IE_NOMEM;
1988 return IE_SUCCESS;
1992 /* Unknown event prefix.
1993 * XSD allows any string */
1994 char *string_locale = _isds_utf82locale((char *) string);
1995 isds_log(ILF_ISDS, ILL_WARNING,
1996 _("Unknown delivery info event prefix: %s\n"), string_locale);
1997 free(string_locale);
1999 *event->type = EVENT_UKNOWN;
2000 event->description = strdup((char *) string);
2001 if (!(event->description)) return IE_NOMEM;
2003 return IE_SUCCESS;
2007 /* Following EXTRACT_* macros expects @result, @xpath_ctx, @err, @context
2008 * and leave label */
2009 #define EXTRACT_STRING(element, string) { \
2010 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2011 if (!result) { \
2012 err = IE_ERROR; \
2013 goto leave; \
2015 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2016 if (result->nodesetval->nodeNr > 1) { \
2017 isds_printf_message(context, _("Multiple %s element"), element); \
2018 err = IE_ERROR; \
2019 goto leave; \
2021 (string) = (char *) \
2022 xmlXPathCastNodeSetToString(result->nodesetval); \
2023 if (!(string)) { \
2024 err = IE_ERROR; \
2025 goto leave; \
2030 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2032 char *string = NULL; \
2033 EXTRACT_STRING(element, string); \
2035 if (string) { \
2036 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2037 if (!(booleanPtr)) { \
2038 free(string); \
2039 err = IE_NOMEM; \
2040 goto leave; \
2043 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2044 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2045 *(booleanPtr) = 1; \
2046 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2047 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2048 *(booleanPtr) = 0; \
2049 else { \
2050 char *string_locale = _isds_utf82locale((char*)string); \
2051 isds_printf_message(context, \
2052 _("%s value is not valid boolean: %s"), \
2053 element, string_locale); \
2054 free(string_locale); \
2055 free(string); \
2056 err = IE_ERROR; \
2057 goto leave; \
2060 free(string); \
2064 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2066 char *string = NULL; \
2067 EXTRACT_STRING(element, string); \
2068 if (string) { \
2069 long int number; \
2070 char *endptr; \
2072 number = strtol((char*)string, &endptr, 10); \
2074 if (*endptr != '\0') { \
2075 char *string_locale = _isds_utf82locale((char *)string); \
2076 isds_printf_message(context, \
2077 _("%s is not valid integer: %s"), \
2078 element, string_locale); \
2079 free(string_locale); \
2080 free(string); \
2081 err = IE_ISDS; \
2082 goto leave; \
2085 if (number == LONG_MIN || number == LONG_MAX) { \
2086 char *string_locale = _isds_utf82locale((char *)string); \
2087 isds_printf_message(context, \
2088 _("%s value out of range of long int: %s"), \
2089 element, string_locale); \
2090 free(string_locale); \
2091 free(string); \
2092 err = IE_ERROR; \
2093 goto leave; \
2096 free(string); string = NULL; \
2098 if (!(preallocated)) { \
2099 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2100 if (!(longintPtr)) { \
2101 err = IE_NOMEM; \
2102 goto leave; \
2105 *(longintPtr) = number; \
2109 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2111 char *string = NULL; \
2112 EXTRACT_STRING(element, string); \
2113 if (string) { \
2114 long int number; \
2115 char *endptr; \
2117 number = strtol((char*)string, &endptr, 10); \
2119 if (*endptr != '\0') { \
2120 char *string_locale = _isds_utf82locale((char *)string); \
2121 isds_printf_message(context, \
2122 _("%s is not valid integer: %s"), \
2123 element, string_locale); \
2124 free(string_locale); \
2125 free(string); \
2126 err = IE_ISDS; \
2127 goto leave; \
2130 if (number == LONG_MIN || number == LONG_MAX) { \
2131 char *string_locale = _isds_utf82locale((char *)string); \
2132 isds_printf_message(context, \
2133 _("%s value out of range of long int: %s"), \
2134 element, string_locale); \
2135 free(string_locale); \
2136 free(string); \
2137 err = IE_ERROR; \
2138 goto leave; \
2141 free(string); string = NULL; \
2142 if (number < 0) { \
2143 isds_printf_message(context, \
2144 _("%s value is negative: %ld"), element, number); \
2145 err = IE_ERROR; \
2146 goto leave; \
2149 if (!(preallocated)) { \
2150 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2151 if (!(ulongintPtr)) { \
2152 err = IE_NOMEM; \
2153 goto leave; \
2156 *(ulongintPtr) = number; \
2160 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2161 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2162 NULL); \
2163 if ((required) && (!string)) { \
2164 char *attribute_locale = _isds_utf82locale(attribute); \
2165 char *element_locale = \
2166 _isds_utf82locale((char *)xpath_ctx->node->name); \
2167 isds_printf_message(context, \
2168 _("Could not extract required %s attribute value from " \
2169 "%s element"), attribute_locale, element_locale); \
2170 free(element_locale); \
2171 free(attribute_locale); \
2172 err = IE_ERROR; \
2173 goto leave; \
2178 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2180 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2181 (xmlChar *) (string)); \
2182 if (!node) { \
2183 isds_printf_message(context, \
2184 _("Could not add %s child to %s element"), \
2185 element, (parent)->name); \
2186 err = IE_ERROR; \
2187 goto leave; \
2191 #define INSERT_STRING(parent, element, string) \
2192 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2194 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2196 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2197 else { INSERT_STRING(parent, element, "false"); } \
2200 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2202 if (booleanPtr) { \
2203 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2204 } else { \
2205 INSERT_STRING(parent, element, NULL); \
2209 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2210 if ((longintPtr)) { \
2211 /* FIXME: locale sensitive */ \
2212 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2213 err = IE_NOMEM; \
2214 goto leave; \
2216 INSERT_STRING(parent, element, buffer) \
2217 free(buffer); (buffer) = NULL; \
2218 } else { INSERT_STRING(parent, element, NULL) } \
2221 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2222 if ((ulongintPtr)) { \
2223 /* FIXME: locale sensitive */ \
2224 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2225 err = IE_NOMEM; \
2226 goto leave; \
2228 INSERT_STRING(parent, element, buffer) \
2229 free(buffer); (buffer) = NULL; \
2230 } else { INSERT_STRING(parent, element, NULL) } \
2233 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2235 /* FIXME: locale sensitive */ \
2236 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2237 err = IE_NOMEM; \
2238 goto leave; \
2240 INSERT_STRING(parent, element, buffer) \
2241 free(buffer); (buffer) = NULL; \
2244 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2245 * new attribute. */
2246 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2248 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2249 (xmlChar *) (string)); \
2250 if (!attribute_node) { \
2251 isds_printf_message(context, _("Could not add %s " \
2252 "attribute to %s element"), \
2253 (attribute), (parent)->name); \
2254 err = IE_ERROR; \
2255 goto leave; \
2259 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2260 if (string) { \
2261 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2262 if (length > (maximum)) { \
2263 isds_printf_message(context, \
2264 ngettext("%s has more than %d characters", \
2265 "%s has more than %d characters", (maximum)), \
2266 (name), (maximum)); \
2267 err = IE_2BIG; \
2268 goto leave; \
2270 if (length < (minimum)) { \
2271 isds_printf_message(context, \
2272 ngettext("%s has less than %d characters", \
2273 "%s has less than %d characters", (minimum)), \
2274 (name), (minimum)); \
2275 err = IE_2SMALL; \
2276 goto leave; \
2281 #define INSERT_ELEMENT(child, parent, element) \
2283 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2284 if (!(child)) { \
2285 isds_printf_message(context, \
2286 _("Could not add %s child to %s element"), \
2287 (element), (parent)->name); \
2288 err = IE_ERROR; \
2289 goto leave; \
2294 /* Find child element by name in given XPath context and switch context onto
2295 * it. The child must be uniq and must exist. Otherwise fails.
2296 * @context is ISDS context
2297 * @child is child element name
2298 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2299 * into it child. In error case, the @xpath_ctx keeps original value. */
2300 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
2301 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
2302 isds_error err = IE_SUCCESS;
2303 xmlXPathObjectPtr result = NULL;
2305 if (!context) return IE_INVALID_CONTEXT;
2306 if (!child || !xpath_ctx) return IE_INVAL;
2308 /* Find child */
2309 result = xmlXPathEvalExpression(child, xpath_ctx);
2310 if (!result) {
2311 err = IE_XML;
2312 goto leave;
2315 /* No match */
2316 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2317 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2318 char *child_locale = _isds_utf82locale((char*) child);
2319 isds_printf_message(context,
2320 _("%s element does not contain %s child"),
2321 parent_locale, child_locale);
2322 free(child_locale);
2323 free(parent_locale);
2324 err = IE_NOEXIST;
2325 goto leave;
2328 /* More matches */
2329 if (result->nodesetval->nodeNr > 1) {
2330 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2331 char *child_locale = _isds_utf82locale((char*) child);
2332 isds_printf_message(context,
2333 _("%s element contains multiple %s children"),
2334 parent_locale, child_locale);
2335 free(child_locale);
2336 free(parent_locale);
2337 err = IE_NOTUNIQ;
2338 goto leave;
2341 /* Switch context */
2342 xpath_ctx->node = result->nodesetval->nodeTab[0];
2344 leave:
2345 xmlXPathFreeObject(result);
2346 return err;
2351 /* Find and convert XSD:gPersonName group in current node into structure
2352 * @context is ISDS context
2353 * @personName is automatically reallocated person name structure. If no member
2354 * value is found, will be freed.
2355 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2356 * elements
2357 * In case of error @personName will be freed. */
2358 static isds_error extract_gPersonName(struct isds_ctx *context,
2359 struct isds_PersonName **personName, xmlXPathContextPtr xpath_ctx) {
2360 isds_error err = IE_SUCCESS;
2361 xmlXPathObjectPtr result = NULL;
2363 if (!context) return IE_INVALID_CONTEXT;
2364 if (!personName) return IE_INVAL;
2365 isds_PersonName_free(personName);
2366 if (!xpath_ctx) return IE_INVAL;
2369 *personName = calloc(1, sizeof(**personName));
2370 if (!*personName) {
2371 err = IE_NOMEM;
2372 goto leave;
2375 EXTRACT_STRING("isds:pnFirstName", (*personName)->pnFirstName);
2376 EXTRACT_STRING("isds:pnMiddleName", (*personName)->pnMiddleName);
2377 EXTRACT_STRING("isds:pnLastName", (*personName)->pnLastName);
2378 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName)->pnLastNameAtBirth);
2380 if (!(*personName)->pnFirstName && !(*personName)->pnMiddleName &&
2381 !(*personName)->pnLastName && !(*personName)->pnLastNameAtBirth)
2382 isds_PersonName_free(personName);
2384 leave:
2385 if (err) isds_PersonName_free(personName);
2386 xmlXPathFreeObject(result);
2387 return err;
2391 /* Find and convert XSD:gAddress group in current node into structure
2392 * @context is ISDS context
2393 * @address is automatically reallocated address structure. If no member
2394 * value is found, will be freed.
2395 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2396 * elements
2397 * In case of error @address will be freed. */
2398 static isds_error extract_gAddress(struct isds_ctx *context,
2399 struct isds_Address **address, xmlXPathContextPtr xpath_ctx) {
2400 isds_error err = IE_SUCCESS;
2401 xmlXPathObjectPtr result = NULL;
2403 if (!context) return IE_INVALID_CONTEXT;
2404 if (!address) return IE_INVAL;
2405 isds_Address_free(address);
2406 if (!xpath_ctx) return IE_INVAL;
2409 *address = calloc(1, sizeof(**address));
2410 if (!*address) {
2411 err = IE_NOMEM;
2412 goto leave;
2415 EXTRACT_STRING("isds:adCity", (*address)->adCity);
2416 EXTRACT_STRING("isds:adStreet", (*address)->adStreet);
2417 EXTRACT_STRING("isds:adNumberInStreet", (*address)->adNumberInStreet);
2418 EXTRACT_STRING("isds:adNumberInMunicipality",
2419 (*address)->adNumberInMunicipality);
2420 EXTRACT_STRING("isds:adZipCode", (*address)->adZipCode);
2421 EXTRACT_STRING("isds:adState", (*address)->adState);
2423 if (!(*address)->adCity && !(*address)->adStreet &&
2424 !(*address)->adNumberInStreet &&
2425 !(*address)->adNumberInMunicipality &&
2426 !(*address)->adZipCode && !(*address)->adState)
2427 isds_Address_free(address);
2429 leave:
2430 if (err) isds_Address_free(address);
2431 xmlXPathFreeObject(result);
2432 return err;
2436 /* Find and convert isds:biDate element in current node into structure
2437 * @context is ISDS context
2438 * @biDate is automatically reallocated birth date structure. If no member
2439 * value is found, will be freed.
2440 * @xpath_ctx is XPath context with current node as parent for isds:biDate
2441 * element
2442 * In case of error @biDate will be freed. */
2443 static isds_error extract_BiDate(struct isds_ctx *context,
2444 struct tm **biDate, xmlXPathContextPtr xpath_ctx) {
2445 isds_error err = IE_SUCCESS;
2446 xmlXPathObjectPtr result = NULL;
2447 char *string = NULL;
2449 if (!context) return IE_INVALID_CONTEXT;
2450 if (!biDate) return IE_INVAL;
2451 zfree(*biDate);
2452 if (!xpath_ctx) return IE_INVAL;
2454 EXTRACT_STRING("isds:biDate", string);
2455 if (string) {
2456 *biDate = calloc(1, sizeof(**biDate));
2457 if (!*biDate) {
2458 err = IE_NOMEM;
2459 goto leave;
2461 err = datestring2tm((xmlChar *)string, *biDate);
2462 if (err) {
2463 if (err == IE_NOTSUP) {
2464 err = IE_ISDS;
2465 char *string_locale = _isds_utf82locale(string);
2466 isds_printf_message(context,
2467 _("Invalid isds:biDate value: %s"), string_locale);
2468 free(string_locale);
2470 goto leave;
2474 leave:
2475 if (err) zfree(*biDate);
2476 free(string);
2477 xmlXPathFreeObject(result);
2478 return err;
2482 /* Convert isds:dBOwnerInfo XML tree into structure
2483 * @context is ISDS context
2484 * @db_owner_info is automatically reallocated box owner info structure
2485 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
2486 * In case of error @db_owner_info will be freed. */
2487 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
2488 struct isds_DbOwnerInfo **db_owner_info,
2489 xmlXPathContextPtr xpath_ctx) {
2490 isds_error err = IE_SUCCESS;
2491 xmlXPathObjectPtr result = NULL;
2492 char *string = NULL;
2494 if (!context) return IE_INVALID_CONTEXT;
2495 if (!db_owner_info) return IE_INVAL;
2496 isds_DbOwnerInfo_free(db_owner_info);
2497 if (!xpath_ctx) return IE_INVAL;
2500 *db_owner_info = calloc(1, sizeof(**db_owner_info));
2501 if (!*db_owner_info) {
2502 err = IE_NOMEM;
2503 goto leave;
2506 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
2508 EXTRACT_STRING("isds:dbType", string);
2509 if (string) {
2510 (*db_owner_info)->dbType =
2511 calloc(1, sizeof(*((*db_owner_info)->dbType)));
2512 if (!(*db_owner_info)->dbType) {
2513 err = IE_NOMEM;
2514 goto leave;
2516 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
2517 if (err) {
2518 zfree((*db_owner_info)->dbType);
2519 if (err == IE_ENUM) {
2520 err = IE_ISDS;
2521 char *string_locale = _isds_utf82locale(string);
2522 isds_printf_message(context, _("Unknown isds:dbType: %s"),
2523 string_locale);
2524 free(string_locale);
2526 goto leave;
2528 zfree(string);
2531 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
2533 err = extract_gPersonName(context, &(*db_owner_info)->personName,
2534 xpath_ctx);
2535 if (err) goto leave;
2537 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
2539 (*db_owner_info)->birthInfo =
2540 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
2541 if (!(*db_owner_info)->birthInfo) {
2542 err = IE_NOMEM;
2543 goto leave;
2545 err = extract_BiDate(context, &(*db_owner_info)->birthInfo->biDate,
2546 xpath_ctx);
2547 if (err) goto leave;
2548 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
2549 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
2550 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
2551 if (!(*db_owner_info)->birthInfo->biDate &&
2552 !(*db_owner_info)->birthInfo->biCity &&
2553 !(*db_owner_info)->birthInfo->biCounty &&
2554 !(*db_owner_info)->birthInfo->biState)
2555 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
2557 err = extract_gAddress(context, &(*db_owner_info)->address, xpath_ctx);
2558 if (err) goto leave;
2560 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
2561 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
2562 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
2563 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
2564 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
2566 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
2568 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
2569 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
2570 (*db_owner_info)->dbOpenAddressing);
2572 leave:
2573 if (err) isds_DbOwnerInfo_free(db_owner_info);
2574 free(string);
2575 xmlXPathFreeObject(result);
2576 return err;
2580 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
2581 * @context is session context
2582 * @owner is libisds structure with box description
2583 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
2584 static isds_error insert_DbOwnerInfo(struct isds_ctx *context,
2585 const struct isds_DbOwnerInfo *owner, xmlNodePtr db_owner_info) {
2587 isds_error err = IE_SUCCESS;
2588 xmlNodePtr node;
2589 xmlChar *string = NULL;
2591 if (!context) return IE_INVALID_CONTEXT;
2592 if (!owner || !db_owner_info) return IE_INVAL;
2595 /* Build XSD:tDbOwnerInfo */
2596 CHECK_FOR_STRING_LENGTH(owner->dbID, 0, 7, "dbID")
2597 INSERT_STRING(db_owner_info, "dbID", owner->dbID);
2599 /* dbType */
2600 if (owner->dbType) {
2601 const xmlChar *type_string = isds_DbType2string(*(owner->dbType));
2602 if (!type_string) {
2603 isds_printf_message(context, _("Invalid dbType value: %d"),
2604 *(owner->dbType));
2605 err = IE_ENUM;
2606 goto leave;
2608 INSERT_STRING(db_owner_info, "dbType", type_string);
2610 INSERT_STRING(db_owner_info, "ic", owner->ic);
2611 if (owner->personName) {
2612 INSERT_STRING(db_owner_info, "pnFirstName",
2613 owner->personName->pnFirstName);
2614 INSERT_STRING(db_owner_info, "pnMiddleName",
2615 owner->personName->pnMiddleName);
2616 INSERT_STRING(db_owner_info, "pnLastName",
2617 owner->personName->pnLastName);
2618 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
2619 owner->personName->pnLastNameAtBirth);
2621 INSERT_STRING(db_owner_info, "firmName", owner->firmName);
2622 if (owner->birthInfo) {
2623 if (owner->birthInfo->biDate) {
2624 if (!tm2datestring(owner->birthInfo->biDate, &string))
2625 INSERT_STRING(db_owner_info, "biDate", string);
2626 free(string); string = NULL;
2628 INSERT_STRING(db_owner_info, "biCity", owner->birthInfo->biCity);
2629 INSERT_STRING(db_owner_info, "biCounty", owner->birthInfo->biCounty);
2630 INSERT_STRING(db_owner_info, "biState", owner->birthInfo->biState);
2632 if (owner->address) {
2633 INSERT_STRING(db_owner_info, "adCity", owner->address->adCity);
2634 INSERT_STRING(db_owner_info, "adStreet", owner->address->adStreet);
2635 INSERT_STRING(db_owner_info, "adNumberInStreet",
2636 owner->address->adNumberInStreet);
2637 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
2638 owner->address->adNumberInMunicipality);
2639 INSERT_STRING(db_owner_info, "adZipCode", owner->address->adZipCode);
2640 INSERT_STRING(db_owner_info, "adState", owner->address->adState);
2642 INSERT_STRING(db_owner_info, "nationality", owner->nationality);
2643 INSERT_STRING(db_owner_info, "email", owner->email);
2644 INSERT_STRING(db_owner_info, "telNumber", owner->telNumber);
2646 CHECK_FOR_STRING_LENGTH(owner->identifier, 0, 20, "identifier")
2647 INSERT_STRING(db_owner_info, "identifier", owner->identifier);
2649 CHECK_FOR_STRING_LENGTH(owner->registryCode, 0, 5, "registryCode")
2650 INSERT_STRING(db_owner_info, "registryCode", owner->registryCode);
2652 INSERT_LONGINT(db_owner_info, "dbState", owner->dbState, string);
2654 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", owner->dbEffectiveOVM);
2655 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
2656 owner->dbOpenAddressing);
2658 leave:
2659 free(string);
2660 return err;
2664 /* Convert XSD:tDbUserInfo XML tree into structure
2665 * @context is ISDS context
2666 * @db_user_info is automatically reallocated user info structure
2667 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
2668 * In case of error @db_user_info will be freed. */
2669 static isds_error extract_DbUserInfo(struct isds_ctx *context,
2670 struct isds_DbUserInfo **db_user_info, xmlXPathContextPtr xpath_ctx) {
2671 isds_error err = IE_SUCCESS;
2672 xmlXPathObjectPtr result = NULL;
2673 char *string = NULL;
2675 if (!context) return IE_INVALID_CONTEXT;
2676 if (!db_user_info) return IE_INVAL;
2677 isds_DbUserInfo_free(db_user_info);
2678 if (!xpath_ctx) return IE_INVAL;
2681 *db_user_info = calloc(1, sizeof(**db_user_info));
2682 if (!*db_user_info) {
2683 err = IE_NOMEM;
2684 goto leave;
2687 EXTRACT_STRING("isds:userID", (*db_user_info)->userID);
2689 EXTRACT_STRING("isds:userType", string);
2690 if (string) {
2691 (*db_user_info)->userType =
2692 calloc(1, sizeof(*((*db_user_info)->userType)));
2693 if (!(*db_user_info)->userType) {
2694 err = IE_NOMEM;
2695 goto leave;
2697 err = string2isds_UserType((xmlChar *)string,
2698 (*db_user_info)->userType);
2699 if (err) {
2700 zfree((*db_user_info)->userType);
2701 if (err == IE_ENUM) {
2702 err = IE_ISDS;
2703 char *string_locale = _isds_utf82locale(string);
2704 isds_printf_message(context,
2705 _("Unknown isds:userType value: %s"), string_locale);
2706 free(string_locale);
2708 goto leave;
2710 zfree(string);
2713 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info)->userPrivils, 0);
2715 (*db_user_info)->personName =
2716 calloc(1, sizeof(*((*db_user_info)->personName)));
2717 if (!(*db_user_info)->personName) {
2718 err = IE_NOMEM;
2719 goto leave;
2722 err = extract_gPersonName(context, &(*db_user_info)->personName,
2723 xpath_ctx);
2724 if (err) goto leave;
2726 err = extract_gAddress(context, &(*db_user_info)->address, xpath_ctx);
2727 if (err) goto leave;
2729 err = extract_BiDate(context, &(*db_user_info)->biDate, xpath_ctx);
2730 if (err) goto leave;
2732 EXTRACT_STRING("isds:ic", (*db_user_info)->ic);
2733 EXTRACT_STRING("isds:firmName", (*db_user_info)->firmName);
2735 EXTRACT_STRING("isds:caStreet", (*db_user_info)->caStreet);
2736 EXTRACT_STRING("isds:caCity", (*db_user_info)->caCity);
2737 EXTRACT_STRING("isds:caZipCode", (*db_user_info)->caZipCode);
2739 /* ???: Default value is "CZ" according specification. Should we provide
2740 * it? */
2741 EXTRACT_STRING("isds:caState", (*db_user_info)->caState);
2743 leave:
2744 if (err) isds_DbUserInfo_free(db_user_info);
2745 free(string);
2746 xmlXPathFreeObject(result);
2747 return err;
2751 /* Insert struct isds_DbUserInfo data (user description) into XML tree
2752 * @context is session context
2753 * @user is libisds structure with user description
2754 * @db_user_info is XML element of XSD:tDbUserInfo */
2755 static isds_error insert_DbUserInfo(struct isds_ctx *context,
2756 const struct isds_DbUserInfo *user, xmlNodePtr db_user_info) {
2758 isds_error err = IE_SUCCESS;
2759 xmlNodePtr node;
2760 xmlChar *string = NULL;
2762 if (!context) return IE_INVALID_CONTEXT;
2763 if (!user || !db_user_info) return IE_INVAL;
2765 /* Build XSD:tDbUserInfo */
2766 if (user->personName) {
2767 INSERT_STRING(db_user_info, "pnFirstName",
2768 user->personName->pnFirstName);
2769 INSERT_STRING(db_user_info, "pnMiddleName",
2770 user->personName->pnMiddleName);
2771 INSERT_STRING(db_user_info, "pnLastName",
2772 user->personName->pnLastName);
2773 INSERT_STRING(db_user_info, "pnLastNameAtBirth",
2774 user->personName->pnLastNameAtBirth);
2776 if (user->address) {
2777 INSERT_STRING(db_user_info, "adCity", user->address->adCity);
2778 INSERT_STRING(db_user_info, "adStreet", user->address->adStreet);
2779 INSERT_STRING(db_user_info, "adNumberInStreet",
2780 user->address->adNumberInStreet);
2781 INSERT_STRING(db_user_info, "adNumberInMunicipality",
2782 user->address->adNumberInMunicipality);
2783 INSERT_STRING(db_user_info, "adZipCode", user->address->adZipCode);
2784 INSERT_STRING(db_user_info, "adState", user->address->adState);
2786 if (user->biDate) {
2787 if (!tm2datestring(user->biDate, &string))
2788 INSERT_STRING(db_user_info, "biDate", string);
2789 zfree(string);
2791 CHECK_FOR_STRING_LENGTH(user->userID, 6, 12, "userID");
2792 INSERT_STRING(db_user_info, "userID", user->userID);
2794 /* userType */
2795 if (user->userType) {
2796 const xmlChar *type_string = isds_UserType2string(*(user->userType));
2797 if (!type_string) {
2798 isds_printf_message(context, _("Invalid userType value: %d"),
2799 *(user->userType));
2800 err = IE_ENUM;
2801 goto leave;
2803 INSERT_STRING(db_user_info, "userType", type_string);
2806 INSERT_LONGINT(db_user_info, "userPrivils", user->userPrivils, string);
2807 CHECK_FOR_STRING_LENGTH(user->ic, 0, 8, "ic")
2808 INSERT_STRING(db_user_info, "ic", user->ic);
2809 CHECK_FOR_STRING_LENGTH(user->firmName, 0, 100, "firmName")
2810 INSERT_STRING(db_user_info, "firmName", user->firmName);
2811 INSERT_STRING(db_user_info, "caStreet", user->caStreet);
2812 INSERT_STRING(db_user_info, "caCity", user->caCity);
2813 INSERT_STRING(db_user_info, "caZipCode", user->caZipCode);
2814 INSERT_STRING(db_user_info, "caState", user->caState);
2816 leave:
2817 free(string);
2818 return err;
2822 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
2823 * isds_envelope structure. The envelope is automatically allocated but not
2824 * reallocated. The date are just appended into envelope structure.
2825 * @context is ISDS context
2826 * @envelope is automatically allocated message envelope structure
2827 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
2828 * In case of error @envelope will be freed. */
2829 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
2830 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2831 isds_error err = IE_SUCCESS;
2832 xmlXPathObjectPtr result = NULL;
2834 if (!context) return IE_INVALID_CONTEXT;
2835 if (!envelope) return IE_INVAL;
2836 if (!xpath_ctx) return IE_INVAL;
2839 if (!*envelope) {
2840 /* Allocate envelope */
2841 *envelope = calloc(1, sizeof(**envelope));
2842 if (!*envelope) {
2843 err = IE_NOMEM;
2844 goto leave;
2846 } else {
2847 /* Else free former data */
2848 zfree((*envelope)->dmSenderOrgUnit);
2849 zfree((*envelope)->dmSenderOrgUnitNum);
2850 zfree((*envelope)->dbIDRecipient);
2851 zfree((*envelope)->dmRecipientOrgUnit);
2852 zfree((*envelope)->dmSenderOrgUnitNum);
2853 zfree((*envelope)->dmToHands);
2854 zfree((*envelope)->dmAnnotation);
2855 zfree((*envelope)->dmRecipientRefNumber);
2856 zfree((*envelope)->dmSenderRefNumber);
2857 zfree((*envelope)->dmRecipientIdent);
2858 zfree((*envelope)->dmSenderIdent);
2859 zfree((*envelope)->dmLegalTitleLaw);
2860 zfree((*envelope)->dmLegalTitleYear);
2861 zfree((*envelope)->dmLegalTitleSect);
2862 zfree((*envelope)->dmLegalTitlePar);
2863 zfree((*envelope)->dmLegalTitlePoint);
2864 zfree((*envelope)->dmPersonalDelivery);
2865 zfree((*envelope)->dmAllowSubstDelivery);
2868 /* Extract envelope elements added by sender or ISDS
2869 * (XSD: gMessageEnvelopeSub type) */
2870 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
2871 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
2872 (*envelope)->dmSenderOrgUnitNum, 0);
2873 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
2874 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
2875 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
2876 (*envelope)->dmSenderOrgUnitNum, 0);
2877 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
2878 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
2879 EXTRACT_STRING("isds:dmRecipientRefNumber",
2880 (*envelope)->dmRecipientRefNumber);
2881 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
2882 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
2883 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
2885 /* Extract envelope elements regarding law reference */
2886 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
2887 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
2888 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
2889 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
2890 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
2892 /* Extract envelope other elements */
2893 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
2894 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
2895 (*envelope)->dmAllowSubstDelivery);
2897 leave:
2898 if (err) isds_envelope_free(envelope);
2899 xmlXPathFreeObject(result);
2900 return err;
2905 /* Convert XSD gMessageEnvelope group of elements from XML tree into
2906 * isds_envelope structure. The envelope is automatically allocated but not
2907 * reallocated. The date are just appended into envelope structure.
2908 * @context is ISDS context
2909 * @envelope is automatically allocated message envelope structure
2910 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
2911 * In case of error @envelope will be freed. */
2912 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
2913 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2914 isds_error err = IE_SUCCESS;
2915 xmlXPathObjectPtr result = NULL;
2917 if (!context) return IE_INVALID_CONTEXT;
2918 if (!envelope) return IE_INVAL;
2919 if (!xpath_ctx) return IE_INVAL;
2922 if (!*envelope) {
2923 /* Allocate envelope */
2924 *envelope = calloc(1, sizeof(**envelope));
2925 if (!*envelope) {
2926 err = IE_NOMEM;
2927 goto leave;
2929 } else {
2930 /* Else free former data */
2931 zfree((*envelope)->dmID);
2932 zfree((*envelope)->dbIDSender);
2933 zfree((*envelope)->dmSender);
2934 zfree((*envelope)->dmSenderAddress);
2935 zfree((*envelope)->dmSenderType);
2936 zfree((*envelope)->dmRecipient);
2937 zfree((*envelope)->dmRecipientAddress);
2938 zfree((*envelope)->dmAmbiguousRecipient);
2941 /* Extract envelope elements added by ISDS
2942 * (XSD: gMessageEnvelope type) */
2943 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
2944 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
2945 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
2946 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
2947 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
2948 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
2949 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
2950 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
2951 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
2952 (*envelope)->dmAmbiguousRecipient);
2954 /* Extract envelope elements added by sender and ISDS
2955 * (XSD: gMessageEnvelope type) */
2956 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
2957 if (err) goto leave;
2959 leave:
2960 if (err) isds_envelope_free(envelope);
2961 xmlXPathFreeObject(result);
2962 return err;
2966 /* Convert other envelope elements from XML tree into isds_envelope structure:
2967 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
2968 * The envelope is automatically allocated but not reallocated.
2969 * The data are just appended into envelope structure.
2970 * @context is ISDS context
2971 * @envelope is automatically allocated message envelope structure
2972 * @xpath_ctx is XPath context with current node as parent desired elements
2973 * In case of error @envelope will be freed. */
2974 static isds_error append_status_size_times(struct isds_ctx *context,
2975 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2976 isds_error err = IE_SUCCESS;
2977 xmlXPathObjectPtr result = NULL;
2978 char *string = NULL;
2979 unsigned long int *unumber = NULL;
2981 if (!context) return IE_INVALID_CONTEXT;
2982 if (!envelope) return IE_INVAL;
2983 if (!xpath_ctx) return IE_INVAL;
2986 if (!*envelope) {
2987 /* Allocate new */
2988 *envelope = calloc(1, sizeof(**envelope));
2989 if (!*envelope) {
2990 err = IE_NOMEM;
2991 goto leave;
2993 } else {
2994 /* Free old data */
2995 zfree((*envelope)->dmMessageStatus);
2996 zfree((*envelope)->dmAttachmentSize);
2997 zfree((*envelope)->dmDeliveryTime);
2998 zfree((*envelope)->dmAcceptanceTime);
3002 /* dmMessageStatus element is mandatory */
3003 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
3004 if (!unumber) {
3005 isds_log_message(context,
3006 _("Missing mandatory sisds:dmMessageStatus integer"));
3007 err = IE_ISDS;
3008 goto leave;
3010 err = uint2isds_message_status(context, unumber,
3011 &((*envelope)->dmMessageStatus));
3012 if (err) {
3013 if (err == IE_ENUM) err = IE_ISDS;
3014 goto leave;
3016 free(unumber); unumber = NULL;
3018 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
3021 EXTRACT_STRING("sisds:dmDeliveryTime", string);
3022 if (string) {
3023 err = timestring2timeval((xmlChar *) string,
3024 &((*envelope)->dmDeliveryTime));
3025 if (err) {
3026 char *string_locale = _isds_utf82locale(string);
3027 if (err == IE_DATE) err = IE_ISDS;
3028 isds_printf_message(context,
3029 _("Could not convert dmDeliveryTime as ISO time: %s"),
3030 string_locale);
3031 free(string_locale);
3032 goto leave;
3034 zfree(string);
3037 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
3038 if (string) {
3039 err = timestring2timeval((xmlChar *) string,
3040 &((*envelope)->dmAcceptanceTime));
3041 if (err) {
3042 char *string_locale = _isds_utf82locale(string);
3043 if (err == IE_DATE) err = IE_ISDS;
3044 isds_printf_message(context,
3045 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3046 string_locale);
3047 free(string_locale);
3048 goto leave;
3050 zfree(string);
3053 leave:
3054 if (err) isds_envelope_free(envelope);
3055 free(unumber);
3056 free(string);
3057 xmlXPathFreeObject(result);
3058 return err;
3062 /* Convert message type attribute of current element into isds_envelope
3063 * structure.
3064 * TODO: This function can be incorporated into append_status_size_times() as
3065 * they are called always together.
3066 * The envelope is automatically allocated but not reallocated.
3067 * The data are just appended into envelope structure.
3068 * @context is ISDS context
3069 * @envelope is automatically allocated message envelope structure
3070 * @xpath_ctx is XPath context with current node as parent of attribute
3071 * carrying message type
3072 * In case of error @envelope will be freed. */
3073 static isds_error append_message_type(struct isds_ctx *context,
3074 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3075 isds_error err = IE_SUCCESS;
3077 if (!context) return IE_INVALID_CONTEXT;
3078 if (!envelope) return IE_INVAL;
3079 if (!xpath_ctx) return IE_INVAL;
3082 if (!*envelope) {
3083 /* Allocate new */
3084 *envelope = calloc(1, sizeof(**envelope));
3085 if (!*envelope) {
3086 err = IE_NOMEM;
3087 goto leave;
3089 } else {
3090 /* Free old data */
3091 zfree((*envelope)->dmType);
3095 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope)->dmType, 0);
3097 if (!(*envelope)->dmType) {
3098 /* Use default value */
3099 (*envelope)->dmType = strdup("V");
3100 if (!(*envelope)->dmType) {
3101 err = IE_NOMEM;
3102 goto leave;
3104 } else if (1 != xmlUTF8Strlen((xmlChar *) (*envelope)->dmType)) {
3105 char *type_locale = _isds_utf82locale((*envelope)->dmType);
3106 isds_printf_message(context,
3107 _("Message type in dmType attribute is not 1 character long: "
3108 "%s"),
3109 type_locale);
3110 free(type_locale);
3111 err = IE_ISDS;
3112 goto leave;
3115 leave:
3116 if (err) isds_envelope_free(envelope);
3117 return err;
3121 /* Convert dmType isds_envelope member into XML attribute and append it to
3122 * current node.
3123 * @context is ISDS context
3124 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3125 * @dm_envelope is XML element the resulting attribute will be appended to.
3126 * @return error code, in case of error context' message is filled. */
3127 static isds_error insert_message_type(struct isds_ctx *context,
3128 const char *type, xmlNodePtr dm_envelope) {
3129 isds_error err = IE_SUCCESS;
3130 xmlAttrPtr attribute_node;
3132 if (!context) return IE_INVALID_CONTEXT;
3133 if (!dm_envelope) return IE_INVAL;
3135 /* Insert optional message type */
3136 if (type) {
3137 if (1 != xmlUTF8Strlen((xmlChar *) type)) {
3138 char *type_locale = _isds_utf82locale(type);
3139 isds_printf_message(context,
3140 _("Message type in envelope is not 1 character long: %s"),
3141 type_locale);
3142 free(type_locale);
3143 err = IE_INVAL;
3144 goto leave;
3146 INSERT_STRING_ATTRIBUTE(dm_envelope, "dmType", type);
3149 leave:
3150 return err;
3154 /* Extract message document into reallocated document structure
3155 * @context is ISDS context
3156 * @document is automatically reallocated message documents structure
3157 * @xpath_ctx is XPath context with current node as isds:dmFile
3158 * In case of error @document will be freed. */
3159 static isds_error extract_document(struct isds_ctx *context,
3160 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
3161 isds_error err = IE_SUCCESS;
3162 xmlXPathObjectPtr result = NULL;
3163 xmlNodePtr file_node = xpath_ctx->node;
3164 char *string = NULL;
3166 if (!context) return IE_INVALID_CONTEXT;
3167 if (!document) return IE_INVAL;
3168 isds_document_free(document);
3169 if (!xpath_ctx) return IE_INVAL;
3171 *document = calloc(1, sizeof(**document));
3172 if (!*document) {
3173 err = IE_NOMEM;
3174 goto leave;
3177 /* Extract document meta data */
3178 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
3179 if (context->normalize_mime_type) {
3180 char *normalized_type =
3181 isds_normalize_mime_type((*document)->dmMimeType);
3182 if (normalized_type && normalized_type != (*document)->dmMimeType) {
3183 char *new_type = strdup(normalized_type);
3184 if (!new_type) {
3185 isds_printf_message(context,
3186 _("No enough memory to normalize document MIME type"));
3187 err = IE_NOMEM;
3188 goto leave;
3190 free((*document)->dmMimeType);
3191 (*document)->dmMimeType = new_type;
3195 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
3196 err = string2isds_FileMetaType((xmlChar*)string,
3197 &((*document)->dmFileMetaType));
3198 if (err) {
3199 char *meta_type_locale = _isds_utf82locale(string);
3200 isds_printf_message(context,
3201 _("Document has invalid dmFileMetaType attribute value: %s"),
3202 meta_type_locale);
3203 free(meta_type_locale);
3204 err = IE_ISDS;
3205 goto leave;
3207 zfree(string);
3209 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
3210 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
3211 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
3212 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
3215 /* Extract document data.
3216 * Base64 encoded blob or XML subtree must be presented. */
3218 /* Check from dmEncodedContent */
3219 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
3220 xpath_ctx);
3221 if (!result) {
3222 err = IE_XML;
3223 goto leave;
3226 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3227 /* Here we have Base64 blob */
3229 if (result->nodesetval->nodeNr > 1) {
3230 isds_printf_message(context,
3231 _("Document has more dmEncodedContent elements"));
3232 err = IE_ISDS;
3233 goto leave;
3236 xmlXPathFreeObject(result); result = NULL;
3237 EXTRACT_STRING("isds:dmEncodedContent", string);
3239 /* Decode non-empty document */
3240 if (string && string[0] != '\0') {
3241 (*document)->data_length =
3242 _isds_b64decode(string, &((*document)->data));
3243 if ((*document)->data_length == (size_t) -1) {
3244 isds_printf_message(context,
3245 _("Error while Base64-decoding document content"));
3246 err = IE_ERROR;
3247 goto leave;
3250 } else {
3251 /* No Base64 blob, try XML document */
3252 xmlXPathFreeObject(result); result = NULL;
3253 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
3254 xpath_ctx);
3255 if (!result) {
3256 err = IE_XML;
3257 goto leave;
3260 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3261 /* Here we have XML document */
3263 if (result->nodesetval->nodeNr > 1) {
3264 isds_printf_message(context,
3265 _("Document has more dmXMLContent elements"));
3266 err = IE_ISDS;
3267 goto leave;
3270 /* FIXME: Serialize the tree rooted at result's node */
3271 isds_printf_message(context,
3272 _("XML documents not yet supported"));
3273 err = IE_NOTSUP;
3274 goto leave;
3275 } else {
3276 /* No base64 blob, nor XML document */
3277 isds_printf_message(context,
3278 _("Document has no dmEncodedContent, nor dmXMLContent "
3279 "element"));
3280 err = IE_ISDS;
3281 goto leave;
3286 leave:
3287 if (err) isds_document_free(document);
3288 free(string);
3289 xmlXPathFreeObject(result);
3290 xpath_ctx->node = file_node;
3291 return err;
3296 /* Extract message documents into reallocated list of documents
3297 * @context is ISDS context
3298 * @documents is automatically reallocated message documents list structure
3299 * @xpath_ctx is XPath context with current node as XSD tFilesArray
3300 * In case of error @documents will be freed. */
3301 static isds_error extract_documents(struct isds_ctx *context,
3302 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
3303 isds_error err = IE_SUCCESS;
3304 xmlXPathObjectPtr result = NULL;
3305 xmlNodePtr files_node = xpath_ctx->node;
3306 struct isds_list *document, *prev_document;
3308 if (!context) return IE_INVALID_CONTEXT;
3309 if (!documents) return IE_INVAL;
3310 isds_list_free(documents);
3311 if (!xpath_ctx) return IE_INVAL;
3313 /* Find documents */
3314 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
3315 if (!result) {
3316 err = IE_XML;
3317 goto leave;
3320 /* No match */
3321 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3322 isds_printf_message(context,
3323 _("Message does not contain any document"));
3324 err = IE_ISDS;
3325 goto leave;
3329 /* Iterate over documents */
3330 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
3332 /* Allocate and append list item */
3333 document = calloc(1, sizeof(*document));
3334 if (!document) {
3335 err = IE_NOMEM;
3336 goto leave;
3338 document->destructor = (void (*)(void **))isds_document_free;
3339 if (i == 0) *documents = document;
3340 else prev_document->next = document;
3341 prev_document = document;
3343 /* Extract document */
3344 xpath_ctx->node = result->nodesetval->nodeTab[i];
3345 err = extract_document(context,
3346 (struct isds_document **) &(document->data), xpath_ctx);
3347 if (err) goto leave;
3351 leave:
3352 if (err) isds_list_free(documents);
3353 xmlXPathFreeObject(result);
3354 xpath_ctx->node = files_node;
3355 return err;
3359 /* Convert isds:dmRecord XML tree into structure
3360 * @context is ISDS context
3361 * @envelope is automatically reallocated message envelope structure
3362 * @xpath_ctx is XPath context with current node as isds:dmRecord element
3363 * In case of error @envelope will be freed. */
3364 static isds_error extract_DmRecord(struct isds_ctx *context,
3365 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3366 isds_error err = IE_SUCCESS;
3367 xmlXPathObjectPtr result = NULL;
3369 if (!context) return IE_INVALID_CONTEXT;
3370 if (!envelope) return IE_INVAL;
3371 isds_envelope_free(envelope);
3372 if (!xpath_ctx) return IE_INVAL;
3375 *envelope = calloc(1, sizeof(**envelope));
3376 if (!*envelope) {
3377 err = IE_NOMEM;
3378 goto leave;
3382 /* Extract tRecord data */
3383 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
3385 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
3386 * dmAcceptanceTime. */
3387 err = append_status_size_times(context, envelope, xpath_ctx);
3388 if (err) goto leave;
3390 /* Extract envelope elements added by sender and ISDS
3391 * (XSD: gMessageEnvelope type) */
3392 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
3393 if (err) goto leave;
3394 /* dmOVM can not be obtained from ISDS */
3396 /* Get message type */
3397 err = append_message_type(context, envelope, xpath_ctx);
3398 if (err) goto leave;
3401 leave:
3402 if (err) isds_envelope_free(envelope);
3403 xmlXPathFreeObject(result);
3404 return err;
3408 /* Find and convert isds:dmHash XML tree into structure
3409 * @context is ISDS context
3410 * @envelope is automatically reallocated message hash structure
3411 * @xpath_ctx is XPath context with current node containing isds:dmHash child
3412 * In case of error @hash will be freed. */
3413 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
3414 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
3415 isds_error err = IE_SUCCESS;
3416 xmlNodePtr old_ctx_node;
3417 xmlXPathObjectPtr result = NULL;
3418 char *string = NULL;
3420 if (!context) return IE_INVALID_CONTEXT;
3421 if (!hash) return IE_INVAL;
3422 isds_hash_free(hash);
3423 if (!xpath_ctx) return IE_INVAL;
3425 old_ctx_node = xpath_ctx->node;
3427 *hash = calloc(1, sizeof(**hash));
3428 if (!*hash) {
3429 err = IE_NOMEM;
3430 goto leave;
3433 /* Locate dmHash */
3434 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
3435 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
3436 err = IE_ISDS;
3437 goto leave;
3439 if (err) {
3440 err = IE_ERROR;
3441 goto leave;
3444 /* Get hash algorithm */
3445 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
3446 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
3447 if (err) {
3448 if (err == IE_ENUM) {
3449 char *string_locale = _isds_utf82locale(string);
3450 isds_printf_message(context, _("Unsupported hash algorithm: %s"),
3451 string_locale);
3452 free(string_locale);
3454 goto leave;
3456 zfree(string);
3458 /* Get hash value */
3459 EXTRACT_STRING(".", string);
3460 if (!string) {
3461 isds_printf_message(context,
3462 _("sisds:dmHash element is missing hash value"));
3463 err = IE_ISDS;
3464 goto leave;
3466 (*hash)->length = _isds_b64decode(string, &((*hash)->value));
3467 if ((*hash)->length == (size_t) -1) {
3468 isds_printf_message(context,
3469 _("Error while Base64-decoding hash value"));
3470 err = IE_ERROR;
3471 goto leave;
3474 leave:
3475 if (err) isds_hash_free(hash);
3476 free(string);
3477 xmlXPathFreeObject(result);
3478 xpath_ctx->node = old_ctx_node;
3479 return err;
3483 /* Find and append isds:dmQTimestamp XML tree into envelope.
3484 * Because one service is allowed to miss time-stamp content, and we think
3485 * other could too (flaw in specification), this function is deliberated and
3486 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
3487 * @context is ISDS context
3488 * @envelope is automatically allocated envelope structure
3489 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
3490 * child
3491 * In case of error @envelope will be freed. */
3492 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
3493 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3494 isds_error err = IE_SUCCESS;
3495 xmlXPathObjectPtr result = NULL;
3496 char *string = NULL;
3498 if (!context) return IE_INVALID_CONTEXT;
3499 if (!envelope) return IE_INVAL;
3500 if (!xpath_ctx) {
3501 isds_envelope_free(envelope);
3502 return IE_INVAL;
3505 if (!*envelope) {
3506 *envelope = calloc(1, sizeof(**envelope));
3507 if (!*envelope) {
3508 err = IE_NOMEM;
3509 goto leave;
3511 } else {
3512 zfree((*envelope)->timestamp);
3513 (*envelope)->timestamp_length = 0;
3516 /* Get dmQTimestamp */
3517 EXTRACT_STRING("sisds:dmQTimestamp", string);
3518 if (!string) {
3519 isds_log(ILF_ISDS, ILL_INFO, _("Missing dmQTimestamp element content\n"));
3520 goto leave;
3522 (*envelope)->timestamp_length =
3523 _isds_b64decode(string, &((*envelope)->timestamp));
3524 if ((*envelope)->timestamp_length == (size_t) -1) {
3525 isds_printf_message(context,
3526 _("Error while Base64-decoding time stamp value"));
3527 err = IE_ERROR;
3528 goto leave;
3531 leave:
3532 if (err) isds_envelope_free(envelope);
3533 free(string);
3534 xmlXPathFreeObject(result);
3535 return err;
3539 /* Convert XSD tReturnedMessage XML tree into message structure.
3540 * It does not store XML tree into message->raw.
3541 * @context is ISDS context
3542 * @include_documents Use true if documents must be extracted
3543 * (tReturnedMessage XSD type), use false if documents shall be omitted
3544 * (tReturnedMessageEnvelope).
3545 * @message is automatically reallocated message structure
3546 * @xpath_ctx is XPath context with current node as tReturnedMessage element
3547 * type
3548 * In case of error @message will be freed. */
3549 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
3550 const _Bool include_documents, struct isds_message **message,
3551 xmlXPathContextPtr xpath_ctx) {
3552 isds_error err = IE_SUCCESS;
3553 xmlNodePtr message_node;
3555 if (!context) return IE_INVALID_CONTEXT;
3556 if (!message) return IE_INVAL;
3557 isds_message_free(message);
3558 if (!xpath_ctx) return IE_INVAL;
3561 *message = calloc(1, sizeof(**message));
3562 if (!*message) {
3563 err = IE_NOMEM;
3564 goto leave;
3567 /* Save message XPATH context node */
3568 message_node = xpath_ctx->node;
3571 /* Extract dmDM */
3572 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
3573 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
3574 if (err) { err = IE_ERROR; goto leave; }
3575 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
3576 if (err) goto leave;
3578 if (include_documents) {
3579 /* Extract dmFiles */
3580 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
3581 xpath_ctx);
3582 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
3583 err = IE_ISDS; goto leave;
3585 if (err) { err = IE_ERROR; goto leave; }
3586 err = extract_documents(context, &((*message)->documents), xpath_ctx);
3587 if (err) goto leave;
3591 /* Restore context to message */
3592 xpath_ctx->node = message_node;
3594 /* Extract dmHash */
3595 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
3596 xpath_ctx);
3597 if (err) goto leave;
3599 /* Extract dmQTimestamp, */
3600 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
3601 xpath_ctx);
3602 if (err) goto leave;
3604 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
3605 * dmAcceptanceTime. */
3606 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
3607 if (err) goto leave;
3609 /* Get message type */
3610 err = append_message_type(context, &((*message)->envelope), xpath_ctx);
3611 if (err) goto leave;
3613 leave:
3614 if (err) isds_message_free(message);
3615 return err;
3619 /* Extract message event into reallocated isds_event structure
3620 * @context is ISDS context
3621 * @event is automatically reallocated message event structure
3622 * @xpath_ctx is XPath context with current node as isds:dmEvent
3623 * In case of error @event will be freed. */
3624 static isds_error extract_event(struct isds_ctx *context,
3625 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
3626 isds_error err = IE_SUCCESS;
3627 xmlXPathObjectPtr result = NULL;
3628 xmlNodePtr event_node = xpath_ctx->node;
3629 char *string = NULL;
3631 if (!context) return IE_INVALID_CONTEXT;
3632 if (!event) return IE_INVAL;
3633 isds_event_free(event);
3634 if (!xpath_ctx) return IE_INVAL;
3636 *event = calloc(1, sizeof(**event));
3637 if (!*event) {
3638 err = IE_NOMEM;
3639 goto leave;
3642 /* Extract event data.
3643 * All elements are optional according XSD. That's funny. */
3644 EXTRACT_STRING("sisds:dmEventTime", string);
3645 if (string) {
3646 err = timestring2timeval((xmlChar *) string, &((*event)->time));
3647 if (err) {
3648 char *string_locale = _isds_utf82locale(string);
3649 if (err == IE_DATE) err = IE_ISDS;
3650 isds_printf_message(context,
3651 _("Could not convert dmEventTime as ISO time: %s"),
3652 string_locale);
3653 free(string_locale);
3654 goto leave;
3656 zfree(string);
3659 /* dmEventDescr element has prefix and the rest */
3660 EXTRACT_STRING("sisds:dmEventDescr", string);
3661 if (string) {
3662 err = eventstring2event((xmlChar *) string, *event);
3663 if (err) goto leave;
3664 zfree(string);
3667 leave:
3668 if (err) isds_event_free(event);
3669 free(string);
3670 xmlXPathFreeObject(result);
3671 xpath_ctx->node = event_node;
3672 return err;
3676 /* Convert element of XSD tEventsArray type from XML tree into
3677 * isds_list of isds_event's structure. The list is automatically reallocated.
3678 * @context is ISDS context
3679 * @events is automatically reallocated list of event structures
3680 * @xpath_ctx is XPath context with current node as tEventsArray
3681 * In case of error @events will be freed. */
3682 static isds_error extract_events(struct isds_ctx *context,
3683 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
3684 isds_error err = IE_SUCCESS;
3685 xmlXPathObjectPtr result = NULL;
3686 xmlNodePtr events_node = xpath_ctx->node;
3687 struct isds_list *event, *prev_event = NULL;
3689 if (!context) return IE_INVALID_CONTEXT;
3690 if (!events) return IE_INVAL;
3691 if (!xpath_ctx) return IE_INVAL;
3693 /* Free old list */
3694 isds_list_free(events);
3696 /* Find events */
3697 result = xmlXPathEvalExpression(BAD_CAST "sisds:dmEvent", xpath_ctx);
3698 if (!result) {
3699 err = IE_XML;
3700 goto leave;
3703 /* No match */
3704 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3705 isds_printf_message(context,
3706 _("Delivery info does not contain any event"));
3707 err = IE_ISDS;
3708 goto leave;
3712 /* Iterate over events */
3713 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
3715 /* Allocate and append list item */
3716 event = calloc(1, sizeof(*event));
3717 if (!event) {
3718 err = IE_NOMEM;
3719 goto leave;
3721 event->destructor = (void (*)(void **))isds_event_free;
3722 if (i == 0) *events = event;
3723 else prev_event->next = event;
3724 prev_event = event;
3726 /* Extract event */
3727 xpath_ctx->node = result->nodesetval->nodeTab[i];
3728 err = extract_event(context,
3729 (struct isds_event **) &(event->data), xpath_ctx);
3730 if (err) goto leave;
3734 leave:
3735 if (err) isds_list_free(events);
3736 xmlXPathFreeObject(result);
3737 xpath_ctx->node = events_node;
3738 return err;
3742 /* Convert isds_document structure into XML tree and append to dmFiles node.
3743 * @context is session context
3744 * @document is ISDS document
3745 * @dm_files is XML element the resulting tree will be appended to as a child.
3746 * @return error code, in case of error context' message is filled. */
3747 static isds_error insert_document(struct isds_ctx *context,
3748 struct isds_document *document, xmlNodePtr dm_files) {
3749 isds_error err = IE_SUCCESS;
3750 xmlNodePtr new_file = NULL, file = NULL, node;
3751 xmlAttrPtr attribute_node;
3752 xmlChar *base64data = NULL;
3754 if (!context) return IE_INVALID_CONTEXT;
3755 if (!document || !dm_files) return IE_INVAL;
3757 /* Allocate new dmFile */
3758 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
3759 if (!new_file) {
3760 isds_printf_message(context, _("Could not allocate main dmFile"));
3761 err = IE_ERROR;
3762 goto leave;
3764 /* Append the new dmFile.
3765 * XXX: Main document must go first */
3766 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
3767 file = xmlAddPrevSibling(dm_files->children, new_file);
3768 else
3769 file = xmlAddChild(dm_files, new_file);
3771 if (!file) {
3772 xmlFreeNode(new_file); new_file = NULL;
3773 isds_printf_message(context, _("Could not add dmFile child to "
3774 "%s element"), dm_files->name);
3775 err = IE_ERROR;
3776 goto leave;
3779 /* @dmMimeType is required */
3780 if (!document->dmMimeType) {
3781 isds_log_message(context,
3782 _("Document is missing mandatory MIME type definition"));
3783 err = IE_INVAL;
3784 goto leave;
3786 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
3788 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
3789 if (!string) {
3790 isds_printf_message(context,
3791 _("Document has unknown dmFileMetaType: %ld"),
3792 document->dmFileMetaType);
3793 err = IE_ENUM;
3794 goto leave;
3796 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
3798 if (document->dmFileGuid) {
3799 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
3801 if (document->dmUpFileGuid) {
3802 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
3805 /* @dmFileDescr is required */
3806 if (!document->dmFileDescr) {
3807 isds_log_message(context,
3808 _("Document is missing mandatory description (title)"));
3809 err = IE_INVAL;
3810 goto leave;
3812 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
3814 if (document->dmFormat) {
3815 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
3819 /* Insert content (data) of the document. */
3820 /* XXX; Only base64 is implemented currently. */
3821 base64data = (xmlChar *) _isds_b64encode(document->data,
3822 document->data_length);
3823 if (!base64data) {
3824 isds_printf_message(context,
3825 ngettext("Not enough memory to encode %zd bytes into Base64",
3826 "Not enough memory to encode %zd bytes into Base64",
3827 document->data_length),
3828 document->data_length);
3829 err = IE_NOMEM;
3830 goto leave;
3832 INSERT_STRING(file, "dmEncodedContent", base64data);
3834 leave:
3835 free(base64data);
3836 return err;
3840 /* Append XSD tMStatus XML tree into isds_message_copy structure.
3841 * The copy must be preallocated, the date are just appended into structure.
3842 * @context is ISDS context
3843 * @copy is message copy structure
3844 * @xpath_ctx is XPath context with current node as tMStatus */
3845 static isds_error append_TMStatus(struct isds_ctx *context,
3846 struct isds_message_copy *copy, xmlXPathContextPtr xpath_ctx) {
3847 isds_error err = IE_SUCCESS;
3848 xmlXPathObjectPtr result = NULL;
3849 char *code = NULL, *message = NULL;
3851 if (!context) return IE_INVALID_CONTEXT;
3852 if (!copy || !xpath_ctx) return IE_INVAL;
3854 /* Free old values */
3855 zfree(copy->dmStatus);
3856 zfree(copy->dmID);
3858 /* Get error specific to this copy */
3859 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code);
3860 if (!code) {
3861 isds_log_message(context,
3862 _("Missing isds:dmStatusCode under "
3863 "XSD:tMStatus type element"));
3864 err = IE_ISDS;
3865 goto leave;
3868 if (xmlStrcmp((const xmlChar *)code, BAD_CAST "0000")) {
3869 /* This copy failed */
3870 copy->error = IE_ISDS;
3871 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message);
3872 if (message) {
3873 copy->dmStatus = _isds_astrcat3(code, ": ", message);
3874 if (!copy->dmStatus) {
3875 copy->dmStatus = code;
3876 code = NULL;
3878 } else {
3879 copy->dmStatus = code;
3880 code = NULL;
3882 } else {
3883 /* This copy succeeded. In this case only, message ID is valid */
3884 copy->error = IE_SUCCESS;
3886 EXTRACT_STRING("isds:dmID", copy->dmID);
3887 if (!copy->dmID) {
3888 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
3889 "but did not returned assigned message ID\n"));
3890 err = IE_ISDS;
3894 leave:
3895 free(code);
3896 free(message);
3897 xmlXPathFreeObject(result);
3898 return err;
3902 /* Insert struct isds_approval data (box approval) into XML tree
3903 * @context is session context
3904 * @approval is libisds structure with approval description. NULL is
3905 * acceptable.
3906 * @parent is XML element to append @approval to */
3907 static isds_error insert_GExtApproval(struct isds_ctx *context,
3908 const struct isds_approval *approval, xmlNodePtr parent) {
3910 isds_error err = IE_SUCCESS;
3911 xmlNodePtr node;
3913 if (!context) return IE_INVALID_CONTEXT;
3914 if (!parent) return IE_INVAL;
3916 if (!approval) return IE_SUCCESS;
3918 /* Build XSD:gExtApproval */
3919 INSERT_SCALAR_BOOLEAN(parent, "dbApproved", approval->approved);
3920 INSERT_STRING(parent, "dbExternRefNumber", approval->refference);
3922 leave:
3923 return err;
3927 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
3928 * code
3929 * @context is session context
3930 * @service_name is name of SERVICE_DB_ACCESS
3931 * @response is server SOAP body response as XML document
3932 * @raw_response is automatically reallocated bit stream with response body. Use
3933 * NULL if you don't care
3934 * @raw_response_length is size of @raw_response in bytes
3935 * @code is ISDS status code
3936 * @status_message is ISDS status message
3937 * @return error coded from lower layer, context message will be set up
3938 * appropriately. */
3939 static isds_error build_send_check_dbdummy_request(struct isds_ctx *context,
3940 const xmlChar *service_name,
3941 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
3942 xmlChar **code, xmlChar **status_message) {
3944 isds_error err = IE_SUCCESS;
3945 char *service_name_locale = NULL;
3946 xmlNodePtr request = NULL, node;
3947 xmlNsPtr isds_ns = NULL;
3949 if (!context) return IE_INVALID_CONTEXT;
3950 if (!service_name) return IE_INVAL;
3951 if (!response || !code || !status_message) return IE_INVAL;
3952 if (!raw_response_length && raw_response) return IE_INVAL;
3954 /* Free output argument */
3955 xmlFreeDoc(*response); *response = NULL;
3956 if (raw_response) zfree(*raw_response);
3957 free(*code);
3958 free(*status_message);
3961 /* Check if connection is established
3962 * TODO: This check should be done downstairs. */
3963 if (!context->curl) return IE_CONNECTION_CLOSED;
3965 service_name_locale = _isds_utf82locale((char*)service_name);
3966 if (!service_name_locale) {
3967 err = IE_NOMEM;
3968 goto leave;
3971 /* Build request */
3972 request = xmlNewNode(NULL, service_name);
3973 if (!request) {
3974 isds_printf_message(context,
3975 _("Could not build %s request"), service_name_locale);
3976 err = IE_ERROR;
3977 goto leave;
3979 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
3980 if(!isds_ns) {
3981 isds_log_message(context, _("Could not create ISDS name space"));
3982 err = IE_ERROR;
3983 goto leave;
3985 xmlSetNs(request, isds_ns);
3988 /* Add XSD:tDummyInput child */
3989 INSERT_STRING(request, "dbDummy", NULL);
3992 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
3993 service_name_locale);
3995 /* Send request */
3996 err = isds(context, SERVICE_DB_ACCESS, request, response,
3997 raw_response, raw_response_length);
3998 xmlFreeNode(request); request = NULL;
4000 if (err) {
4001 isds_log(ILF_ISDS, ILL_DEBUG,
4002 _("Processing ISDS response on %s request failed\n"),
4003 service_name_locale);
4004 goto leave;
4007 /* Check for response status */
4008 err = isds_response_status(context, SERVICE_DB_ACCESS, *response,
4009 code, status_message, NULL);
4010 if (err) {
4011 isds_log(ILF_ISDS, ILL_DEBUG,
4012 _("ISDS response on %s request is missing status\n"),
4013 service_name_locale);
4014 goto leave;
4017 /* Request processed, but nothing found */
4018 if (xmlStrcmp(*code, BAD_CAST "0000")) {
4019 char *code_locale = _isds_utf82locale((char*) *code);
4020 char *status_message_locale =
4021 _isds_utf82locale((char*) *status_message);
4022 isds_log(ILF_ISDS, ILL_DEBUG,
4023 _("Server refused %s request (code=%s, message=%s)\n"),
4024 service_name_locale, code_locale, status_message_locale);
4025 isds_log_message(context, status_message_locale);
4026 free(code_locale);
4027 free(status_message_locale);
4028 err = IE_ISDS;
4029 goto leave;
4032 leave:
4033 free(service_name_locale);
4034 xmlFreeNode(request);
4035 return err;
4039 /* Get data about logged in user and his box. */
4040 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
4041 struct isds_DbOwnerInfo **db_owner_info) {
4042 isds_error err = IE_SUCCESS;
4043 xmlDocPtr response = NULL;
4044 xmlChar *code = NULL, *message = NULL;
4045 xmlXPathContextPtr xpath_ctx = NULL;
4046 xmlXPathObjectPtr result = NULL;
4047 char *string = NULL;
4049 if (!context) return IE_INVALID_CONTEXT;
4050 zfree(context->long_message);
4051 if (!db_owner_info) return IE_INVAL;
4053 /* Check if connection is established */
4054 if (!context->curl) return IE_CONNECTION_CLOSED;
4057 /* Do request and check for success */
4058 err = build_send_check_dbdummy_request(context,
4059 BAD_CAST "GetOwnerInfoFromLogin",
4060 &response, NULL, NULL, &code, &message);
4061 if (err) goto leave;
4064 /* Extract data */
4065 /* Prepare structure */
4066 isds_DbOwnerInfo_free(db_owner_info);
4067 *db_owner_info = calloc(1, sizeof(**db_owner_info));
4068 if (!*db_owner_info) {
4069 err = IE_NOMEM;
4070 goto leave;
4072 xpath_ctx = xmlXPathNewContext(response);
4073 if (!xpath_ctx) {
4074 err = IE_ERROR;
4075 goto leave;
4077 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4078 err = IE_ERROR;
4079 goto leave;
4082 /* Set context node */
4083 result = xmlXPathEvalExpression(BAD_CAST
4084 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
4085 if (!result) {
4086 err = IE_ERROR;
4087 goto leave;
4089 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4090 isds_log_message(context, _("Missing dbOwnerInfo element"));
4091 err = IE_ISDS;
4092 goto leave;
4094 if (result->nodesetval->nodeNr > 1) {
4095 isds_log_message(context, _("Multiple dbOwnerInfo element"));
4096 err = IE_ISDS;
4097 goto leave;
4099 xpath_ctx->node = result->nodesetval->nodeTab[0];
4100 xmlXPathFreeObject(result); result = NULL;
4102 /* Extract it */
4103 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
4105 leave:
4106 if (err) {
4107 isds_DbOwnerInfo_free(db_owner_info);
4110 free(string);
4111 xmlXPathFreeObject(result);
4112 xmlXPathFreeContext(xpath_ctx);
4114 free(code);
4115 free(message);
4116 xmlFreeDoc(response);
4118 if (!err)
4119 isds_log(ILF_ISDS, ILL_DEBUG,
4120 _("GetOwnerInfoFromLogin request processed by server "
4121 "successfully.\n"));
4123 return err;
4127 /* Get data about logged in user. */
4128 isds_error isds_GetUserInfoFromLogin(struct isds_ctx *context,
4129 struct isds_DbUserInfo **db_user_info) {
4130 isds_error err = IE_SUCCESS;
4131 xmlDocPtr response = NULL;
4132 xmlChar *code = NULL, *message = NULL;
4133 xmlXPathContextPtr xpath_ctx = NULL;
4134 xmlXPathObjectPtr result = NULL;
4136 if (!context) return IE_INVALID_CONTEXT;
4137 zfree(context->long_message);
4138 if (!db_user_info) return IE_INVAL;
4140 /* Check if connection is established */
4141 if (!context->curl) return IE_CONNECTION_CLOSED;
4144 /* Do request and check for success */
4145 err = build_send_check_dbdummy_request(context,
4146 BAD_CAST "GetUserInfoFromLogin",
4147 &response, NULL, NULL, &code, &message);
4148 if (err) goto leave;
4151 /* Extract data */
4152 /* Prepare structure */
4153 isds_DbUserInfo_free(db_user_info);
4154 *db_user_info = calloc(1, sizeof(**db_user_info));
4155 if (!*db_user_info) {
4156 err = IE_NOMEM;
4157 goto leave;
4159 xpath_ctx = xmlXPathNewContext(response);
4160 if (!xpath_ctx) {
4161 err = IE_ERROR;
4162 goto leave;
4164 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4165 err = IE_ERROR;
4166 goto leave;
4169 /* Set context node */
4170 result = xmlXPathEvalExpression(BAD_CAST
4171 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx);
4172 if (!result) {
4173 err = IE_ERROR;
4174 goto leave;
4176 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4177 isds_log_message(context, _("Missing dbUserInfo element"));
4178 err = IE_ISDS;
4179 goto leave;
4181 if (result->nodesetval->nodeNr > 1) {
4182 isds_log_message(context, _("Multiple dbUserInfo element"));
4183 err = IE_ISDS;
4184 goto leave;
4186 xpath_ctx->node = result->nodesetval->nodeTab[0];
4187 xmlXPathFreeObject(result); result = NULL;
4189 /* Extract it */
4190 err = extract_DbUserInfo(context, db_user_info, xpath_ctx);
4192 leave:
4193 if (err) {
4194 isds_DbUserInfo_free(db_user_info);
4197 xmlXPathFreeObject(result);
4198 xmlXPathFreeContext(xpath_ctx);
4200 free(code);
4201 free(message);
4202 xmlFreeDoc(response);
4204 if (!err)
4205 isds_log(ILF_ISDS, ILL_DEBUG,
4206 _("GetUserInfoFromLogin request processed by server "
4207 "successfully.\n"));
4209 return err;
4213 /* Get expiration time of current password
4214 * @context is session context
4215 * @expiration is automatically reallocated time when password expires, In
4216 * case of error will be nulled. */
4217 isds_error isds_get_password_expiration(struct isds_ctx *context,
4218 struct timeval **expiration) {
4219 isds_error err = IE_SUCCESS;
4220 xmlDocPtr response = NULL;
4221 xmlChar *code = NULL, *message = NULL;
4222 xmlXPathContextPtr xpath_ctx = NULL;
4223 xmlXPathObjectPtr result = NULL;
4224 char *string = NULL;
4226 if (!context) return IE_INVALID_CONTEXT;
4227 zfree(context->long_message);
4228 if (!expiration) return IE_INVAL;
4230 /* Check if connection is established */
4231 if (!context->curl) return IE_CONNECTION_CLOSED;
4234 /* Do request and check for success */
4235 err = build_send_check_dbdummy_request(context,
4236 BAD_CAST "GetPasswordInfo",
4237 &response, NULL, NULL, &code, &message);
4238 if (err) goto leave;
4241 /* Extract data */
4242 xpath_ctx = xmlXPathNewContext(response);
4243 if (!xpath_ctx) {
4244 err = IE_ERROR;
4245 goto leave;
4247 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4248 err = IE_ERROR;
4249 goto leave;
4252 /* Set context node */
4253 result = xmlXPathEvalExpression(BAD_CAST
4254 "/isds:GetPasswordInfoResponse", xpath_ctx);
4255 if (!result) {
4256 err = IE_ERROR;
4257 goto leave;
4259 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4260 isds_log_message(context,
4261 _("Missing GetPasswordInfoResponse element"));
4262 err = IE_ISDS;
4263 goto leave;
4265 if (result->nodesetval->nodeNr > 1) {
4266 isds_log_message(context,
4267 _("Multiple GetPasswordInfoResponse element"));
4268 err = IE_ISDS;
4269 goto leave;
4271 xpath_ctx->node = result->nodesetval->nodeTab[0];
4272 xmlXPathFreeObject(result); result = NULL;
4274 /* Extract expiration date */
4275 EXTRACT_STRING("isds:pswExpDate", string);
4276 if (!string) {
4277 isds_log_message(context, _("Missing pswExpDate element"));
4278 err = IE_ISDS;
4279 goto leave;
4282 err = timestring2timeval((xmlChar *) string, expiration);
4283 if (err) {
4284 char *string_locale = _isds_utf82locale(string);
4285 if (err == IE_DATE) err = IE_ISDS;
4286 isds_printf_message(context,
4287 _("Could not convert pswExpDate as ISO time: %s"),
4288 string_locale);
4289 free(string_locale);
4290 goto leave;
4293 leave:
4294 if (err) {
4295 if (*expiration) {
4296 zfree(*expiration);
4300 free(string);
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 _("GetPasswordInfo request processed by server "
4311 "successfully.\n"));
4313 return err;
4317 /* Change user password in ISDS.
4318 * User must supply old password, new password will takes effect after some
4319 * time, current session can continue. Password must fulfill some constraints.
4320 * @context is session context
4321 * @old_password is current password.
4322 * @new_password is requested new password */
4323 isds_error isds_change_password(struct isds_ctx *context,
4324 const char *old_password, const char *new_password) {
4325 isds_error err = IE_SUCCESS;
4326 xmlNsPtr isds_ns = NULL;
4327 xmlNodePtr request = NULL, node;
4328 xmlDocPtr response = NULL;
4329 xmlChar *code = NULL, *message = NULL;
4331 if (!context) return IE_INVALID_CONTEXT;
4332 zfree(context->long_message);
4333 if (!old_password || !new_password) return IE_INVAL;
4335 /* Check if connection is established
4336 * TODO: This check should be done downstairs. */
4337 if (!context->curl) return IE_CONNECTION_CLOSED;
4340 /* Build ChangeISDSPassword request */
4341 request = xmlNewNode(NULL, BAD_CAST "ChangeISDSPassword");
4342 if (!request) {
4343 isds_log_message(context,
4344 _("Could not build ChangeISDSPassword request"));
4345 return IE_ERROR;
4347 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4348 if(!isds_ns) {
4349 isds_log_message(context, _("Could not create ISDS name space"));
4350 xmlFreeNode(request);
4351 return IE_ERROR;
4353 xmlSetNs(request, isds_ns);
4355 INSERT_STRING(request, "dbOldPassword", old_password);
4356 INSERT_STRING(request, "dbNewPassword", new_password);
4359 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CheckDataBox request to ISDS\n"));
4361 /* Sent request */
4362 err = isds(context, SERVICE_DB_ACCESS, request, &response, NULL, NULL);
4364 /* Destroy request */
4365 xmlFreeNode(request); request = NULL;
4367 if (err) {
4368 isds_log(ILF_ISDS, ILL_DEBUG,
4369 _("Processing ISDS response on ChangeISDSPassword "
4370 "request failed\n"));
4371 goto leave;
4374 /* Check for response status */
4375 err = isds_response_status(context, SERVICE_DB_ACCESS, response,
4376 &code, &message, NULL);
4377 if (err) {
4378 isds_log(ILF_ISDS, ILL_DEBUG,
4379 _("ISDS response on ChangeISDSPassword request is missing "
4380 "status\n"));
4381 goto leave;
4384 /* Request processed, but empty password refused */
4385 if (!xmlStrcmp(code, BAD_CAST "1066")) {
4386 char *code_locale = _isds_utf82locale((char*)code);
4387 char *message_locale = _isds_utf82locale((char*)message);
4388 isds_log(ILF_ISDS, ILL_DEBUG,
4389 _("Server refused empty password on ChangeISDSPassword "
4390 "request (code=%s, message=%s)\n"),
4391 code_locale, message_locale);
4392 isds_log_message(context, _("Password must not be empty"));
4393 free(code_locale);
4394 free(message_locale);
4395 err = IE_INVAL;
4396 goto leave;
4399 /* Request processed, but new password was reused */
4400 else if (!xmlStrcmp(code, BAD_CAST "1067")) {
4401 char *code_locale = _isds_utf82locale((char*)code);
4402 char *message_locale = _isds_utf82locale((char*)message);
4403 isds_log(ILF_ISDS, ILL_DEBUG,
4404 _("Server refused the same new password on ChangeISDSPassword "
4405 "request (code=%s, message=%s)\n"),
4406 code_locale, message_locale);
4407 isds_log_message(context,
4408 _("New password must differ from the current one"));
4409 free(code_locale);
4410 free(message_locale);
4411 err = IE_INVAL;
4412 goto leave;
4415 /* Other error */
4416 else if (xmlStrcmp(code, BAD_CAST "0000")) {
4417 char *code_locale = _isds_utf82locale((char*)code);
4418 char *message_locale = _isds_utf82locale((char*)message);
4419 isds_log(ILF_ISDS, ILL_DEBUG,
4420 _("Server refused to change password on ChangeISDSPassword "
4421 "request (code=%s, message=%s)\n"),
4422 code_locale, message_locale);
4423 isds_log_message(context, message_locale);
4424 free(code_locale);
4425 free(message_locale);
4426 err = IE_ISDS;
4427 goto leave;
4430 /* Otherwise password changed successfully */
4432 leave:
4433 free(code);
4434 free(message);
4435 xmlFreeDoc(response);
4436 xmlFreeNode(request);
4438 if (!err)
4439 isds_log(ILF_ISDS, ILL_DEBUG,
4440 _("Password changed successfully on ChangeISDSPassword "
4441 "request.\n"));
4443 return err;
4447 /* Generic middle part with request sending and response check.
4448 * It sends prepared request and checks for error code.
4449 * @context is ISDS session context.
4450 * @service is ISDS service handler
4451 * @service_name is name in scope of given @service
4452 * @request is XML tree with request. Will be freed to save memory.
4453 * @response is XML document outputting ISDS response.
4454 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4455 * NULL, if you don't care. */
4456 static isds_error send_destroy_request_check_response(
4457 struct isds_ctx *context,
4458 const isds_service service, const xmlChar *service_name,
4459 xmlNodePtr *request, xmlDocPtr *response, xmlChar **refnumber) {
4460 isds_error err = IE_SUCCESS;
4461 char *service_name_locale = NULL;
4462 xmlChar *code = NULL, *message = NULL;
4465 if (!context) return IE_INVALID_CONTEXT;
4466 if (!service_name || *service_name == '\0' || !request || !*request ||
4467 !response)
4468 return IE_INVAL;
4470 /* Check if connection is established
4471 * TODO: This check should be done downstairs. */
4472 if (!context->curl) return IE_CONNECTION_CLOSED;
4474 service_name_locale = _isds_utf82locale((char*) service_name);
4475 if (!service_name_locale) {
4476 err = IE_NOMEM;
4477 goto leave;
4480 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
4481 service_name_locale);
4483 /* Send request */
4484 err = isds(context, service, *request, response, NULL, NULL);
4485 xmlFreeNode(*request); *request = NULL;
4487 if (err) {
4488 isds_log(ILF_ISDS, ILL_DEBUG,
4489 _("Processing ISDS response on %s request failed\n"),
4490 service_name_locale);
4491 goto leave;
4494 /* Check for response status */
4495 err = isds_response_status(context, service, *response,
4496 &code, &message, refnumber);
4497 if (err) {
4498 isds_log(ILF_ISDS, ILL_DEBUG,
4499 _("ISDS response on %s request is missing status\n"),
4500 service_name_locale);
4501 goto leave;
4504 /* Request processed, but server failed */
4505 if (xmlStrcmp(code, BAD_CAST "0000")) {
4506 char *code_locale = _isds_utf82locale((char*) code);
4507 char *message_locale = _isds_utf82locale((char*) message);
4508 isds_log(ILF_ISDS, ILL_DEBUG,
4509 _("Server refused %s request (code=%s, message=%s)\n"),
4510 service_name_locale, code_locale, message_locale);
4511 isds_log_message(context, message_locale);
4512 free(code_locale);
4513 free(message_locale);
4514 err = IE_ISDS;
4515 goto leave;
4519 leave:
4520 free(code);
4521 free(message);
4522 if (err && *response) {
4523 xmlFreeDoc(*response);
4524 *response = NULL;
4526 if (*request) {
4527 xmlFreeNode(*request);
4528 *request = NULL;
4530 free(service_name_locale);
4532 return err;
4536 /* Generic bottom half with request sending.
4537 * It sends prepared request, checks for error code, destroys response and
4538 * request and log success or failure.
4539 * @context is ISDS session context.
4540 * @service is ISDS service handler
4541 * @service_name is name in scope of given @service
4542 * @request is XML tree with request. Will be freed to save memory.
4543 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4544 * NULL, if you don't care. */
4545 static isds_error send_request_check_drop_response(
4546 struct isds_ctx *context,
4547 const isds_service service, const xmlChar *service_name,
4548 xmlNodePtr *request, xmlChar **refnumber) {
4549 isds_error err = IE_SUCCESS;
4550 xmlDocPtr response = NULL;
4553 if (!context) return IE_INVALID_CONTEXT;
4554 if (!service_name || *service_name == '\0' || !request || !*request)
4555 return IE_INVAL;
4557 /* Send request and check response*/
4558 err = send_destroy_request_check_response(context,
4559 service, service_name, request, &response, refnumber);
4561 xmlFreeDoc(response);
4563 if (*request) {
4564 xmlFreeNode(*request);
4565 *request = NULL;
4568 if (!err) {
4569 char *service_name_locale = _isds_utf82locale((char *) service_name);
4570 isds_log(ILF_ISDS, ILL_DEBUG,
4571 _("%s request processed by server successfully.\n"),
4572 service_name_locale);
4573 free(service_name_locale);
4576 return err;
4580 /* Build XSD:tCreateDBInput request type for box creating.
4581 * @context is session context
4582 * @request outputs built XML tree
4583 * @service_name is request name of SERVICE_DB_MANIPULATION service
4584 * @box is box description to create including single primary user (in case of
4585 * FO box type)
4586 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
4587 * box, or contact address of PFO box owner)
4588 * @former_names is optional undocumented string. Pass NULL if you don't care.
4589 * @upper_box_id is optional ID of supper box if currently created box is
4590 * subordinated.
4591 * @ceo_label is optional title of OVM box owner (e.g. mayor) NULL, if you
4592 * don't care.
4593 * @approval is optional external approval of box manipulation */
4594 static isds_error build_CreateDBInput_request(struct isds_ctx *context,
4595 xmlNodePtr *request, const xmlChar *service_name,
4596 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
4597 const xmlChar *former_names, const xmlChar *upper_box_id,
4598 const xmlChar *ceo_label, const struct isds_approval *approval) {
4599 isds_error err = IE_SUCCESS;
4600 xmlNsPtr isds_ns = NULL;
4601 xmlNodePtr node, dbPrimaryUsers;
4602 xmlChar *string = NULL;
4603 const struct isds_list *item;
4606 if (!context) return IE_INVALID_CONTEXT;
4607 if (!request || !service_name || service_name[0] == '\0' || !box)
4608 return IE_INVAL;
4611 /* Build DeleteDataBox request */
4612 *request = xmlNewNode(NULL, service_name);
4613 if (!*request) {
4614 char *service_name_locale = _isds_utf82locale((char*) service_name);
4615 isds_printf_message(context, _("Could build %s request"),
4616 service_name_locale);
4617 free(service_name_locale);
4618 return IE_ERROR;
4620 if (context->type == CTX_TYPE_TESTING_REQUEST_COLLECTOR) {
4621 isds_ns = xmlNewNs(*request, BAD_CAST ISDS1_NS, NULL);
4622 if (!isds_ns) {
4623 isds_log_message(context, _("Could not create ISDS1 name space"));
4624 xmlFreeNode(*request);
4625 return IE_ERROR;
4627 } else {
4628 isds_ns = xmlNewNs(*request, BAD_CAST ISDS_NS, NULL);
4629 if (!isds_ns) {
4630 isds_log_message(context, _("Could not create ISDS name space"));
4631 xmlFreeNode(*request);
4632 return IE_ERROR;
4635 xmlSetNs(*request, isds_ns);
4637 INSERT_ELEMENT(node, *request, "dbOwnerInfo");
4638 err = insert_DbOwnerInfo(context, box, node);
4639 if (err) goto leave;
4641 /* Insert users */
4642 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
4643 * verbose documentation allows none dbUserInfo */
4644 INSERT_ELEMENT(dbPrimaryUsers, *request, "dbPrimaryUsers");
4645 for (item = users; item; item = item->next) {
4646 if (item->data) {
4647 INSERT_ELEMENT(node, dbPrimaryUsers, "dbUserInfo");
4648 err = insert_DbUserInfo(context,
4649 (struct isds_DbUserInfo *) item->data, node);
4650 if (err) goto leave;
4654 INSERT_STRING(*request, "dbFormerNames", former_names);
4655 INSERT_STRING(*request, "dbUpperDBId", upper_box_id);
4656 INSERT_STRING(*request, "dbCEOLabel", ceo_label);
4658 err = insert_GExtApproval(context, approval, *request);
4659 if (err) goto leave;
4661 leave:
4662 if (err) {
4663 xmlFreeNode(*request);
4664 *request = NULL;
4666 free(string);
4667 return err;
4671 /* Create new box.
4672 * @context is session context
4673 * @box is box description to create including single primary user (in case of
4674 * FO box type). It outputs box ID assigned by ISDS in dbID element.
4675 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
4676 * box, or contact address of PFO box owner)
4677 * @former_names is optional undocumented string. Pass NULL if you don't care.
4678 * @upper_box_id is optional ID of supper box if currently created box is
4679 * subordinated.
4680 * @ceo_label is optional title of OVM box owner (e.g. mayor)
4681 * @approval is optional external approval of box manipulation
4682 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4683 * NULL, if you don't care.*/
4684 isds_error isds_add_box(struct isds_ctx *context,
4685 struct isds_DbOwnerInfo *box, const struct isds_list *users,
4686 const char *former_names, const char *upper_box_id,
4687 const char *ceo_label, const struct isds_approval *approval,
4688 char **refnumber) {
4689 isds_error err = IE_SUCCESS;
4690 xmlNodePtr request = NULL;
4691 xmlDocPtr response = NULL;
4692 xmlXPathContextPtr xpath_ctx = NULL;
4693 xmlXPathObjectPtr result = NULL;
4696 if (!context) return IE_INVALID_CONTEXT;
4697 zfree(context->long_message);
4698 if (!box) return IE_INVAL;
4700 /* Scratch box ID */
4701 zfree(box->dbID);
4703 /* Build CreateDataBox request */
4704 err = build_CreateDBInput_request(context,
4705 &request, BAD_CAST "CreateDataBox",
4706 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
4707 (xmlChar *) ceo_label, approval);
4708 if (err) goto leave;
4710 /* Send it to server and process response */
4711 err = send_destroy_request_check_response(context,
4712 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
4713 &response, (xmlChar **) refnumber);
4715 /* Extract box ID */
4716 xpath_ctx = xmlXPathNewContext(response);
4717 if (!xpath_ctx) {
4718 err = IE_ERROR;
4719 goto leave;
4721 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4722 err = IE_ERROR;
4723 goto leave;
4725 EXTRACT_STRING("/isds:CreateDataBoxResponse/dbID", box->dbID);
4727 leave:
4728 xmlXPathFreeObject(result);
4729 xmlXPathFreeContext(xpath_ctx);
4730 xmlFreeDoc(response);
4731 xmlFreeNode(request);
4733 if (!err) {
4734 isds_log(ILF_ISDS, ILL_DEBUG,
4735 _("CreateDataBox request processed by server successfully.\n"));
4738 return err;
4742 /* Notify ISDS about new PFO entity.
4743 * This function has no real effect.
4744 * @context is session context
4745 * @box is PFO description including single primary user.
4746 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
4747 * @former_names is optional undocumented string. Pass NULL if you don't care.
4748 * @upper_box_id is optional ID of supper box if currently created box is
4749 * subordinated.
4750 * @ceo_label is optional title of OVM box owner (e.g. mayor)
4751 * @approval is optional external approval of box manipulation
4752 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4753 * NULL, if you don't care.*/
4754 isds_error isds_add_pfoinfo(struct isds_ctx *context,
4755 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
4756 const char *former_names, const char *upper_box_id,
4757 const char *ceo_label, const struct isds_approval *approval,
4758 char **refnumber) {
4759 isds_error err = IE_SUCCESS;
4760 xmlNodePtr request = NULL;
4762 if (!context) return IE_INVALID_CONTEXT;
4763 zfree(context->long_message);
4764 if (!box) return IE_INVAL;
4766 /* Build CreateDataBoxPFOInfo request */
4767 err = build_CreateDBInput_request(context,
4768 &request, BAD_CAST "CreateDataBoxPFOInfo",
4769 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
4770 (xmlChar *) ceo_label, approval);
4771 if (err) goto leave;
4773 /* Send it to server and process response */
4774 err = send_request_check_drop_response(context,
4775 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
4776 (xmlChar **) refnumber);
4777 leave:
4778 xmlFreeNode(request);
4779 return err;
4783 /* Remove given given box permanently.
4784 * @context is session context
4785 * @box is box description to delete
4786 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
4787 * carry sane value.
4788 * @approval is optional external approval of box manipulation
4789 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4790 * NULL, if you don't care.*/
4791 isds_error isds_delete_box(struct isds_ctx *context,
4792 const struct isds_DbOwnerInfo *box, const struct tm *since,
4793 const struct isds_approval *approval, char **refnumber) {
4794 isds_error err = IE_SUCCESS;
4795 xmlNsPtr isds_ns = NULL;
4796 xmlNodePtr request = NULL;
4797 xmlNodePtr node;
4798 xmlChar *string = NULL;
4801 if (!context) return IE_INVALID_CONTEXT;
4802 zfree(context->long_message);
4803 if (!box || !since) return IE_INVAL;
4806 /* Build DeleteDataBox request */
4807 request = xmlNewNode(NULL, BAD_CAST "DeleteDataBox");
4808 if (!request) {
4809 isds_log_message(context,
4810 _("Could build DeleteDataBox request"));
4811 return IE_ERROR;
4813 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4814 if(!isds_ns) {
4815 isds_log_message(context, _("Could not create ISDS name space"));
4816 xmlFreeNode(request);
4817 return IE_ERROR;
4819 xmlSetNs(request, isds_ns);
4821 INSERT_ELEMENT(node, request, "dbOwnerInfo");
4822 err = insert_DbOwnerInfo(context, box, node);
4823 if (err) goto leave;
4825 err = tm2datestring(since, &string);
4826 if (err) {
4827 isds_log_message(context,
4828 _("Could not convert `since' argument to ISO date string"));
4829 goto leave;
4831 INSERT_STRING(request, "dbOwnerTerminationDate", string);
4832 zfree(string);
4834 err = insert_GExtApproval(context, approval, request);
4835 if (err) goto leave;
4838 /* Send it to server and process response */
4839 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
4840 BAD_CAST "DeleteDataBox", &request, (xmlChar **) refnumber);
4842 leave:
4843 xmlFreeNode(request);
4844 free(string);
4845 return err;
4849 /* Update data about given box.
4850 * @context is session context
4851 * @old_box current box description
4852 * @new_box are updated data about @old_box
4853 * @approval is optional external approval of box manipulation
4854 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4855 * NULL, if you don't care.*/
4856 isds_error isds_UpdateDataBoxDescr(struct isds_ctx *context,
4857 const struct isds_DbOwnerInfo *old_box,
4858 const struct isds_DbOwnerInfo *new_box,
4859 const struct isds_approval *approval, char **refnumber) {
4860 isds_error err = IE_SUCCESS;
4861 xmlNsPtr isds_ns = NULL;
4862 xmlNodePtr request = NULL;
4863 xmlNodePtr node;
4866 if (!context) return IE_INVALID_CONTEXT;
4867 zfree(context->long_message);
4868 if (!old_box || !new_box) return IE_INVAL;
4871 /* Build UpdateDataBoxDescr request */
4872 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxDescr");
4873 if (!request) {
4874 isds_log_message(context,
4875 _("Could build UpdateDataBoxDescr request"));
4876 return IE_ERROR;
4878 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4879 if(!isds_ns) {
4880 isds_log_message(context, _("Could not create ISDS name space"));
4881 xmlFreeNode(request);
4882 return IE_ERROR;
4884 xmlSetNs(request, isds_ns);
4886 INSERT_ELEMENT(node, request, "dbOldOwnerInfo");
4887 err = insert_DbOwnerInfo(context, old_box, node);
4888 if (err) goto leave;
4890 INSERT_ELEMENT(node, request, "dbNewOwnerInfo");
4891 err = insert_DbOwnerInfo(context, new_box, node);
4892 if (err) goto leave;
4894 err = insert_GExtApproval(context, approval, request);
4895 if (err) goto leave;
4898 /* Send it to server and process response */
4899 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
4900 BAD_CAST "UpdateDataBoxDescr", &request, (xmlChar **) refnumber);
4902 leave:
4903 xmlFreeNode(request);
4905 return err;
4909 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
4910 * code
4911 * @context is session context
4912 * @service is SOAP service
4913 * @service_name is name of request in @service
4914 * @box_id is box ID of interest
4915 * @approval is optional external approval of box manipulation
4916 * @response is server SOAP body response as XML document
4917 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4918 * NULL, if you don't care.
4919 * @return error coded from lower layer, context message will be set up
4920 * appropriately. */
4921 static isds_error build_send_dbid_request_check_response(
4922 struct isds_ctx *context, const isds_service service,
4923 const xmlChar *service_name, const xmlChar *box_id,
4924 const struct isds_approval *approval,
4925 xmlDocPtr *response, xmlChar **refnumber) {
4927 isds_error err = IE_SUCCESS;
4928 char *service_name_locale = NULL, *box_id_locale = NULL;
4929 xmlNodePtr request = NULL, node;
4930 xmlNsPtr isds_ns = NULL;
4932 if (!context) return IE_INVALID_CONTEXT;
4933 if (!service_name || !box_id) return IE_INVAL;
4934 if (!response) return IE_INVAL;
4936 /* Free output argument */
4937 xmlFreeDoc(*response); *response = NULL;
4939 /* Prepare strings */
4940 service_name_locale = _isds_utf82locale((char*)service_name);
4941 if (!service_name_locale) {
4942 err = IE_NOMEM;
4943 goto leave;
4945 box_id_locale = _isds_utf82locale((char*)box_id);
4946 if (!box_id_locale) {
4947 err = IE_NOMEM;
4948 goto leave;
4951 /* Build request */
4952 request = xmlNewNode(NULL, service_name);
4953 if (!request) {
4954 isds_printf_message(context,
4955 _("Could not build %s request"), service_name_locale);
4956 err = IE_ERROR;
4957 goto leave;
4959 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4960 if(!isds_ns) {
4961 isds_log_message(context, _("Could not create ISDS name space"));
4962 err = IE_ERROR;
4963 goto leave;
4965 xmlSetNs(request, isds_ns);
4967 /* Add XSD:tIdDbInput children */
4968 INSERT_STRING(request, "dbID", box_id);
4969 err = insert_GExtApproval(context, approval, request);
4970 if (err) goto leave;
4972 /* Send request and check response*/
4973 err = send_destroy_request_check_response(context,
4974 service, service_name, &request, response, refnumber);
4976 leave:
4977 free(service_name_locale);
4978 free(box_id_locale);
4979 xmlFreeNode(request);
4980 return err;
4984 /* Get data about all users assigned to given box.
4985 * @context is session context
4986 * @box_id is box ID
4987 * @users is automatically reallocated list of struct isds_DbUserInfo */
4988 isds_error isds_GetDataBoxUsers(struct isds_ctx *context, const char *box_id,
4989 struct isds_list **users) {
4990 isds_error err = IE_SUCCESS;
4991 xmlDocPtr response = NULL;
4992 xmlXPathContextPtr xpath_ctx = NULL;
4993 xmlXPathObjectPtr result = NULL;
4994 int i;
4995 struct isds_list *item, *prev_item = NULL;
4997 if (!context) return IE_INVALID_CONTEXT;
4998 zfree(context->long_message);
4999 if (!users || !box_id) return IE_INVAL;
5002 /* Do request and check for success */
5003 err = build_send_dbid_request_check_response(context,
5004 SERVICE_DB_MANIPULATION, BAD_CAST "GetDataBoxUsers",
5005 BAD_CAST box_id, NULL, &response, NULL);
5006 if (err) goto leave;
5009 /* Extract data */
5010 /* Prepare structure */
5011 isds_list_free(users);
5012 xpath_ctx = xmlXPathNewContext(response);
5013 if (!xpath_ctx) {
5014 err = IE_ERROR;
5015 goto leave;
5017 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5018 err = IE_ERROR;
5019 goto leave;
5022 /* Set context node */
5023 result = xmlXPathEvalExpression(BAD_CAST
5024 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
5025 xpath_ctx);
5026 if (!result) {
5027 err = IE_ERROR;
5028 goto leave;
5030 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5031 isds_log_message(context, _("Missing dbUserInfo element"));
5032 err = IE_ISDS;
5033 goto leave;
5036 /* Iterate over all users */
5037 for (i = 0; i < result->nodesetval->nodeNr; i++) {
5039 /* Prepare structure */
5040 item = calloc(1, sizeof(*item));
5041 if (!item) {
5042 err = IE_NOMEM;
5043 goto leave;
5045 item->destructor = (void(*)(void**))isds_DbUserInfo_free;
5046 if (i == 0) *users = item;
5047 else prev_item->next = item;
5048 prev_item = item;
5050 /* Extract it */
5051 xpath_ctx->node = result->nodesetval->nodeTab[i];
5052 err = extract_DbUserInfo(context,
5053 (struct isds_DbUserInfo **) (&item->data), xpath_ctx);
5054 if (err) goto leave;
5057 leave:
5058 if (err) {
5059 isds_list_free(users);
5062 xmlXPathFreeObject(result);
5063 xmlXPathFreeContext(xpath_ctx);
5064 xmlFreeDoc(response);
5066 if (!err)
5067 isds_log(ILF_ISDS, ILL_DEBUG,
5068 _("GetDataBoxUsers request processed by server "
5069 "successfully.\n"));
5071 return err;
5075 /* Update data about user assigned to given box.
5076 * @context is session context
5077 * @box is box identification
5078 * @old_user identifies user to update
5079 * @new_user are updated data about @old_user
5080 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5081 * NULL, if you don't care.*/
5082 isds_error isds_UpdateDataBoxUser(struct isds_ctx *context,
5083 const struct isds_DbOwnerInfo *box,
5084 const struct isds_DbUserInfo *old_user,
5085 const struct isds_DbUserInfo *new_user,
5086 char **refnumber) {
5087 isds_error err = IE_SUCCESS;
5088 xmlNsPtr isds_ns = NULL;
5089 xmlNodePtr request = NULL;
5090 xmlNodePtr node;
5093 if (!context) return IE_INVALID_CONTEXT;
5094 zfree(context->long_message);
5095 if (!box || !old_user || !new_user) return IE_INVAL;
5098 /* Build UpdateDataBoxUser request */
5099 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxUser");
5100 if (!request) {
5101 isds_log_message(context,
5102 _("Could build UpdateDataBoxUser request"));
5103 return IE_ERROR;
5105 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5106 if(!isds_ns) {
5107 isds_log_message(context, _("Could not create ISDS name space"));
5108 xmlFreeNode(request);
5109 return IE_ERROR;
5111 xmlSetNs(request, isds_ns);
5113 INSERT_ELEMENT(node, request, "dbOwnerInfo");
5114 err = insert_DbOwnerInfo(context, box, node);
5115 if (err) goto leave;
5117 INSERT_ELEMENT(node, request, "dbOldUserInfo");
5118 err = insert_DbUserInfo(context, old_user, node);
5119 if (err) goto leave;
5121 INSERT_ELEMENT(node, request, "dbNewUserInfo");
5122 err = insert_DbUserInfo(context, new_user, node);
5123 if (err) goto leave;
5125 /* Send it to server and process response */
5126 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
5127 BAD_CAST "UpdateDataBoxUser", &request, (xmlChar **) refnumber);
5129 leave:
5130 xmlFreeNode(request);
5132 return err;
5136 /* Reset credentials of user assigned to given box.
5137 * @context is session context
5138 * @box is box identification
5139 * @user identifies user to reset password
5140 * @fee_paid is true if fee has been paid, false otherwise
5141 * @approval is optional external approval of box manipulation
5142 * @token is NULL if new password should be delivered off-line to the user.
5143 * It is valid pointer if user should obtain new password on-line on dedicated
5144 * web server. Then it output automatically reallocated token user needs to
5145 * use to authorize on the web server to view his new password.
5146 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5147 * NULL, if you don't care.*/
5148 isds_error isds_reset_password(struct isds_ctx *context,
5149 const struct isds_DbOwnerInfo *box,
5150 const struct isds_DbUserInfo *user,
5151 const _Bool fee_paid, const struct isds_approval *approval,
5152 char **token, char **refnumber) {
5153 isds_error err = IE_SUCCESS;
5154 xmlNsPtr isds_ns = NULL;
5155 xmlNodePtr request = NULL, node;
5156 xmlDocPtr response = NULL;
5157 xmlXPathContextPtr xpath_ctx = NULL;
5158 xmlXPathObjectPtr result = NULL;
5161 if (!context) return IE_INVALID_CONTEXT;
5162 zfree(context->long_message);
5163 if (!box || !user) return IE_INVAL;
5165 if (token) zfree(*token);
5168 /* Build NewAccessData request */
5169 request = xmlNewNode(NULL, BAD_CAST "NewAccessData");
5170 if (!request) {
5171 isds_log_message(context,
5172 _("Could build NewAccessData request"));
5173 return IE_ERROR;
5175 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5176 if(!isds_ns) {
5177 isds_log_message(context, _("Could not create ISDS name space"));
5178 xmlFreeNode(request);
5179 return IE_ERROR;
5181 xmlSetNs(request, isds_ns);
5183 INSERT_ELEMENT(node, request, "dbOwnerInfo");
5184 err = insert_DbOwnerInfo(context, box, node);
5185 if (err) goto leave;
5187 INSERT_ELEMENT(node, request, "dbUserInfo");
5188 err = insert_DbUserInfo(context, user, node);
5189 if (err) goto leave;
5191 INSERT_SCALAR_BOOLEAN(request, "dbFeePaid", fee_paid);
5193 if (token) {
5194 INSERT_SCALAR_BOOLEAN(request, "dbVirtual", 1);
5195 } else {
5196 INSERT_SCALAR_BOOLEAN(request, "dbVirtual", 0);
5199 err = insert_GExtApproval(context, approval, request);
5200 if (err) goto leave;
5202 /* Send request and check response*/
5203 err = send_destroy_request_check_response(context,
5204 SERVICE_DB_MANIPULATION, BAD_CAST "NewAccessData", &request,
5205 &response, (xmlChar **) refnumber);
5206 if (err) goto leave;
5209 /* Extract optional token */
5210 if (token) {
5211 xpath_ctx = xmlXPathNewContext(response);
5212 if (!xpath_ctx) {
5213 err = IE_ERROR;
5214 goto leave;
5216 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5217 err = IE_ERROR;
5218 goto leave;
5221 EXTRACT_STRING("/isds:NewAccessDataResponse/dbAccessDataId", *token);
5224 leave:
5225 xmlXPathFreeObject(result);
5226 xmlXPathFreeContext(xpath_ctx);
5227 xmlFreeDoc(response);
5228 xmlFreeNode(request);
5230 if (!err)
5231 isds_log(ILF_ISDS, ILL_DEBUG,
5232 _("NewAccessData request processed by server "
5233 "successfully.\n"));
5235 return err;
5239 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
5240 * code, destroy response and log success.
5241 * @context is ISDS session context.
5242 * @service_name is name of SERVICE_DB_MANIPULATION service
5243 * @box is box identification
5244 * @user identifies user to remove
5245 * @approval is optional external approval of box manipulation
5246 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5247 * NULL, if you don't care. */
5248 static isds_error build_send_manipulationboxuser_request_check_drop_response(
5249 struct isds_ctx *context, const xmlChar *service_name,
5250 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
5251 const struct isds_approval *approval, xmlChar **refnumber) {
5252 isds_error err = IE_SUCCESS;
5253 xmlNsPtr isds_ns = NULL;
5254 xmlNodePtr request = NULL, node;
5257 if (!context) return IE_INVALID_CONTEXT;
5258 zfree(context->long_message);
5259 if (!service_name || service_name[0] == '\0' || !box || !user)
5260 return IE_INVAL;
5263 /* Build NewAccessData request */
5264 request = xmlNewNode(NULL, service_name);
5265 if (!request) {
5266 char *service_name_locale = _isds_utf82locale((char *) service_name);
5267 isds_printf_message(context, _("Could build %s request"),
5268 service_name_locale);
5269 free(service_name_locale);
5270 return IE_ERROR;
5272 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5273 if(!isds_ns) {
5274 isds_log_message(context, _("Could not create ISDS name space"));
5275 xmlFreeNode(request);
5276 return IE_ERROR;
5278 xmlSetNs(request, isds_ns);
5280 INSERT_ELEMENT(node, request, "dbOwnerInfo");
5281 err = insert_DbOwnerInfo(context, box, node);
5282 if (err) goto leave;
5284 INSERT_ELEMENT(node, request, "dbUserInfo");
5285 err = insert_DbUserInfo(context, user, node);
5286 if (err) goto leave;
5288 err = insert_GExtApproval(context, approval, request);
5289 if (err) goto leave;
5291 /* Send request and check response*/
5292 err = send_request_check_drop_response (context,
5293 SERVICE_DB_MANIPULATION, service_name, &request, refnumber);
5295 leave:
5296 xmlFreeNode(request);
5297 return err;
5301 /* Assign new user to given box.
5302 * @context is session context
5303 * @box is box identification
5304 * @user defines new user to add
5305 * @approval is optional external approval of box manipulation
5306 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5307 * NULL, if you don't care.*/
5308 isds_error isds_add_user(struct isds_ctx *context,
5309 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
5310 const struct isds_approval *approval, char **refnumber) {
5311 return build_send_manipulationboxuser_request_check_drop_response(context,
5312 BAD_CAST "AddDataBoxUser", box, user, approval,
5313 (xmlChar **) refnumber);
5317 /* Remove user assigned to given box.
5318 * @context is session context
5319 * @box is box identification
5320 * @user identifies user to remove
5321 * @approval is optional external approval of box manipulation
5322 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5323 * NULL, if you don't care.*/
5324 isds_error isds_delete_user(struct isds_ctx *context,
5325 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
5326 const struct isds_approval *approval, char **refnumber) {
5327 return build_send_manipulationboxuser_request_check_drop_response(context,
5328 BAD_CAST "DeleteDataBoxUser", box, user, approval,
5329 (xmlChar **) refnumber);
5333 /* Find boxes suiting given criteria.
5334 * @criteria is filter. You should fill in at least some members.
5335 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
5336 * possibly empty. Input NULL or valid old structure.
5337 * @return:
5338 * IE_SUCCESS if search succeeded, @boxes contains useful data
5339 * IE_NOEXIST if no such box exists, @boxes will be NULL
5340 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
5341 * contains still valid data
5342 * other code if something bad happens. @boxes will be NULL. */
5343 isds_error isds_FindDataBox(struct isds_ctx *context,
5344 const struct isds_DbOwnerInfo *criteria,
5345 struct isds_list **boxes) {
5346 isds_error err = IE_SUCCESS;
5347 _Bool truncated = 0;
5348 xmlNsPtr isds_ns = NULL;
5349 xmlNodePtr request = NULL;
5350 xmlDocPtr response = NULL;
5351 xmlChar *code = NULL, *message = NULL;
5352 xmlNodePtr db_owner_info;
5353 xmlXPathContextPtr xpath_ctx = NULL;
5354 xmlXPathObjectPtr result = NULL;
5355 xmlChar *string = NULL;
5358 if (!context) return IE_INVALID_CONTEXT;
5359 zfree(context->long_message);
5360 if (!boxes) return IE_INVAL;
5361 isds_list_free(boxes);
5363 if (!criteria) {
5364 return IE_INVAL;
5367 /* Check if connection is established
5368 * TODO: This check should be done downstairs. */
5369 if (!context->curl) return IE_CONNECTION_CLOSED;
5372 /* Build FindDataBox request */
5373 request = xmlNewNode(NULL, BAD_CAST "FindDataBox");
5374 if (!request) {
5375 isds_log_message(context,
5376 _("Could build FindDataBox request"));
5377 return IE_ERROR;
5379 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5380 if(!isds_ns) {
5381 isds_log_message(context, _("Could not create ISDS name space"));
5382 xmlFreeNode(request);
5383 return IE_ERROR;
5385 xmlSetNs(request, isds_ns);
5386 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
5387 if (!db_owner_info) {
5388 isds_log_message(context, _("Could not add dbOwnerInfo child to "
5389 "FindDataBox element"));
5390 xmlFreeNode(request);
5391 return IE_ERROR;
5394 err = insert_DbOwnerInfo(context, criteria, db_owner_info);
5395 if (err) goto leave;
5398 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending FindDataBox request to ISDS\n"));
5400 /* Sent request */
5401 err = isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
5403 /* Destroy request */
5404 xmlFreeNode(request); request = NULL;
5406 if (err) {
5407 isds_log(ILF_ISDS, ILL_DEBUG,
5408 _("Processing ISDS response on FindDataBox "
5409 "request failed\n"));
5410 goto leave;
5413 /* Check for response status */
5414 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
5415 &code, &message, NULL);
5416 if (err) {
5417 isds_log(ILF_ISDS, ILL_DEBUG,
5418 _("ISDS response on FindDataBox request is missing status\n"));
5419 goto leave;
5422 /* Request processed, but nothing found */
5423 if (!xmlStrcmp(code, BAD_CAST "0002") ||
5424 !xmlStrcmp(code, BAD_CAST "5001")) {
5425 char *code_locale = _isds_utf82locale((char*)code);
5426 char *message_locale = _isds_utf82locale((char*)message);
5427 isds_log(ILF_ISDS, ILL_DEBUG,
5428 _("Server did not found any box on FindDataBox request "
5429 "(code=%s, message=%s)\n"), code_locale, message_locale);
5430 isds_log_message(context, message_locale);
5431 free(code_locale);
5432 free(message_locale);
5433 err = IE_NOEXIST;
5434 goto leave;
5437 /* Warning, not a error */
5438 if (!xmlStrcmp(code, BAD_CAST "0003")) {
5439 char *code_locale = _isds_utf82locale((char*)code);
5440 char *message_locale = _isds_utf82locale((char*)message);
5441 isds_log(ILF_ISDS, ILL_DEBUG,
5442 _("Server truncated response on FindDataBox request "
5443 "(code=%s, message=%s)\n"), code_locale, message_locale);
5444 isds_log_message(context, message_locale);
5445 free(code_locale);
5446 free(message_locale);
5447 truncated = 1;
5450 /* Other error */
5451 else if (xmlStrcmp(code, BAD_CAST "0000")) {
5452 char *code_locale = _isds_utf82locale((char*)code);
5453 char *message_locale = _isds_utf82locale((char*)message);
5454 isds_log(ILF_ISDS, ILL_DEBUG,
5455 _("Server refused FindDataBox request "
5456 "(code=%s, message=%s)\n"), code_locale, message_locale);
5457 isds_log_message(context, message_locale);
5458 free(code_locale);
5459 free(message_locale);
5460 err = IE_ISDS;
5461 goto leave;
5464 xpath_ctx = xmlXPathNewContext(response);
5465 if (!xpath_ctx) {
5466 err = IE_ERROR;
5467 goto leave;
5469 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5470 err = IE_ERROR;
5471 goto leave;
5474 /* Extract boxes if they present */
5475 result = xmlXPathEvalExpression(BAD_CAST
5476 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
5477 xpath_ctx);
5478 if (!result) {
5479 err = IE_ERROR;
5480 goto leave;
5482 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5483 struct isds_list *item, *prev_item = NULL;
5484 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
5485 item = calloc(1, sizeof(*item));
5486 if (!item) {
5487 err = IE_NOMEM;
5488 goto leave;
5491 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
5492 if (i == 0) *boxes = item;
5493 else prev_item->next = item;
5494 prev_item = item;
5496 xpath_ctx->node = result->nodesetval->nodeTab[i];
5497 err = extract_DbOwnerInfo(context,
5498 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
5499 if (err) goto leave;
5503 leave:
5504 if (err) {
5505 isds_list_free(boxes);
5506 } else {
5507 if (truncated) err = IE_2BIG;
5510 free(string);
5511 xmlFreeNode(request);
5512 xmlXPathFreeObject(result);
5513 xmlXPathFreeContext(xpath_ctx);
5515 free(code);
5516 free(message);
5517 xmlFreeDoc(response);
5519 if (!err)
5520 isds_log(ILF_ISDS, ILL_DEBUG,
5521 _("FindDataBox request processed by server successfully.\n"));
5523 return err;
5527 /* Get status of a box.
5528 * @context is ISDS session context.
5529 * @box_id is UTF-8 encoded box identifier as zero terminated string
5530 * @box_status is return value of box status.
5531 * @return:
5532 * IE_SUCCESS if box has been found and its status retrieved
5533 * IE_NOEXIST if box is not known to ISDS server
5534 * or other appropriate error.
5535 * You can use isds_DbState to enumerate box status. However out of enum
5536 * range value can be returned too. This is feature because ISDS
5537 * specification leaves the set of values open.
5538 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
5539 * the box has been deleted, but ISDS still lists its former existence. */
5540 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
5541 long int *box_status) {
5542 isds_error err = IE_SUCCESS;
5543 xmlNsPtr isds_ns = NULL;
5544 xmlNodePtr request = NULL, db_id;
5545 xmlDocPtr response = NULL;
5546 xmlChar *code = NULL, *message = NULL;
5547 xmlXPathContextPtr xpath_ctx = NULL;
5548 xmlXPathObjectPtr result = NULL;
5549 xmlChar *string = NULL;
5551 if (!context) return IE_INVALID_CONTEXT;
5552 zfree(context->long_message);
5553 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
5555 /* Check if connection is established
5556 * TODO: This check should be done downstairs. */
5557 if (!context->curl) return IE_CONNECTION_CLOSED;
5560 /* Build CheckDataBox request */
5561 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
5562 if (!request) {
5563 isds_log_message(context,
5564 _("Could build CheckDataBox request"));
5565 return IE_ERROR;
5567 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5568 if(!isds_ns) {
5569 isds_log_message(context, _("Could not create ISDS name space"));
5570 xmlFreeNode(request);
5571 return IE_ERROR;
5573 xmlSetNs(request, isds_ns);
5574 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
5575 if (!db_id) {
5576 isds_log_message(context, _("Could not add dbID child to "
5577 "CheckDataBox element"));
5578 xmlFreeNode(request);
5579 return IE_ERROR;
5583 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CheckDataBox request to ISDS\n"));
5585 /* Sent request */
5586 err = isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
5588 /* Destroy request */
5589 xmlFreeNode(request);
5591 if (err) {
5592 isds_log(ILF_ISDS, ILL_DEBUG,
5593 _("Processing ISDS response on CheckDataBox "
5594 "request failed\n"));
5595 goto leave;
5598 /* Check for response status */
5599 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
5600 &code, &message, NULL);
5601 if (err) {
5602 isds_log(ILF_ISDS, ILL_DEBUG,
5603 _("ISDS response on CheckDataBox request is missing status\n"));
5604 goto leave;
5607 /* Request processed, but nothing found */
5608 if (!xmlStrcmp(code, BAD_CAST "5001")) {
5609 char *box_id_locale = _isds_utf82locale((char*)box_id);
5610 char *code_locale = _isds_utf82locale((char*)code);
5611 char *message_locale = _isds_utf82locale((char*)message);
5612 isds_log(ILF_ISDS, ILL_DEBUG,
5613 _("Server did not found box %s on CheckDataBox request "
5614 "(code=%s, message=%s)\n"),
5615 box_id_locale, code_locale, message_locale);
5616 isds_log_message(context, message_locale);
5617 free(box_id_locale);
5618 free(code_locale);
5619 free(message_locale);
5620 err = IE_NOEXIST;
5621 goto leave;
5624 /* Other error */
5625 else if (xmlStrcmp(code, BAD_CAST "0000")) {
5626 char *code_locale = _isds_utf82locale((char*)code);
5627 char *message_locale = _isds_utf82locale((char*)message);
5628 isds_log(ILF_ISDS, ILL_DEBUG,
5629 _("Server refused CheckDataBox request "
5630 "(code=%s, message=%s)\n"), code_locale, message_locale);
5631 isds_log_message(context, message_locale);
5632 free(code_locale);
5633 free(message_locale);
5634 err = IE_ISDS;
5635 goto leave;
5638 /* Extract data */
5639 xpath_ctx = xmlXPathNewContext(response);
5640 if (!xpath_ctx) {
5641 err = IE_ERROR;
5642 goto leave;
5644 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5645 err = IE_ERROR;
5646 goto leave;
5648 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
5649 xpath_ctx);
5650 if (!result) {
5651 err = IE_ERROR;
5652 goto leave;
5654 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5655 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
5656 err = IE_ISDS;
5657 goto leave;
5659 if (result->nodesetval->nodeNr > 1) {
5660 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
5661 err = IE_ISDS;
5662 goto leave;
5664 xpath_ctx->node = result->nodesetval->nodeTab[0];
5665 xmlXPathFreeObject(result); result = NULL;
5667 EXTRACT_LONGINT("isds:dbState", box_status, 1);
5670 leave:
5671 free(string);
5672 xmlXPathFreeObject(result);
5673 xmlXPathFreeContext(xpath_ctx);
5675 free(code);
5676 free(message);
5677 xmlFreeDoc(response);
5679 if (!err)
5680 isds_log(ILF_ISDS, ILL_DEBUG,
5681 _("CheckDataBox request processed by server successfully.\n"));
5683 return err;
5687 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
5688 * code, destroy response and log success.
5689 * @context is ISDS session context.
5690 * @service_name is name of SERVICE_DB_MANIPULATION service
5691 * @box_id is UTF-8 encoded box identifier as zero terminated string
5692 * @approval is optional external approval of box manipulation
5693 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5694 * NULL, if you don't care. */
5695 static isds_error build_send_manipulationdbid_request_check_drop_response(
5696 struct isds_ctx *context, const xmlChar *service_name,
5697 const xmlChar *box_id, const struct isds_approval *approval,
5698 xmlChar **refnumber) {
5699 isds_error err = IE_SUCCESS;
5700 xmlDocPtr response = NULL;
5702 if (!context) return IE_INVALID_CONTEXT;
5703 zfree(context->long_message);
5704 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
5706 /* Check if connection is established */
5707 if (!context->curl) return IE_CONNECTION_CLOSED;
5709 /* Do request and check for success */
5710 err = build_send_dbid_request_check_response(context,
5711 SERVICE_DB_MANIPULATION, service_name, box_id, approval,
5712 &response, refnumber);
5713 xmlFreeDoc(response);
5715 if (!err) {
5716 char *service_name_locale = _isds_utf82locale((char *) service_name);
5717 isds_log(ILF_ISDS, ILL_DEBUG,
5718 _("%s request processed by server successfully.\n"),
5719 service_name_locale);
5720 free(service_name_locale);
5723 return err;
5727 /* Switch box into state where box can receive commercial messages (off by
5728 * default)
5729 * @context is ISDS session context.
5730 * @box_id is UTF-8 encoded box identifier as zero terminated string
5731 * @allow is true for enable, false for disable commercial messages income
5732 * @approval is optional external approval of box manipulation
5733 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5734 * NULL, if you don't care. */
5735 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
5736 const char *box_id, const _Bool allow,
5737 const struct isds_approval *approval, char **refnumber) {
5738 return build_send_manipulationdbid_request_check_drop_response(context,
5739 (allow) ? BAD_CAST "SetOpenAddressing" :
5740 BAD_CAST "ClearOpenAddressing",
5741 BAD_CAST box_id, approval, (xmlChar **) refnumber);
5745 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
5746 * message acceptance). This is just a box permission. Sender must apply
5747 * such role by sending each message.
5748 * @context is ISDS session context.
5749 * @box_id is UTF-8 encoded box identifier as zero terminated string
5750 * @allow is true for enable, false for disable OVM role permission
5751 * @approval is optional external approval of box manipulation
5752 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5753 * NULL, if you don't care. */
5754 isds_error isds_switch_effective_ovm(struct isds_ctx *context,
5755 const char *box_id, const _Bool allow,
5756 const struct isds_approval *approval, char **refnumber) {
5757 return build_send_manipulationdbid_request_check_drop_response(context,
5758 (allow) ? BAD_CAST "SetEffectiveOVM" :
5759 BAD_CAST "ClearEffectiveOVM",
5760 BAD_CAST box_id, approval, (xmlChar **) refnumber);
5764 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
5765 * code, destroy response and log success.
5766 * @context is ISDS session context.
5767 * @service_name is name of SERVICE_DB_MANIPULATION service
5768 * @owner is structure describing box
5769 * @approval is optional external approval of box manipulation
5770 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5771 * NULL, if you don't care. */
5772 static isds_error build_send_manipulationdbowner_request_check_drop_response(
5773 struct isds_ctx *context, const xmlChar *service_name,
5774 const struct isds_DbOwnerInfo *owner,
5775 const struct isds_approval *approval, xmlChar **refnumber) {
5776 isds_error err = IE_SUCCESS;
5777 char *service_name_locale = NULL;
5778 xmlNodePtr request = NULL, db_owner_info;
5779 xmlNsPtr isds_ns = NULL;
5782 if (!context) return IE_INVALID_CONTEXT;
5783 zfree(context->long_message);
5784 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
5786 service_name_locale = _isds_utf82locale((char*)service_name);
5787 if (!service_name_locale) {
5788 err = IE_NOMEM;
5789 goto leave;
5792 /* Build request */
5793 request = xmlNewNode(NULL, service_name);
5794 if (!request) {
5795 isds_printf_message(context,
5796 _("Could not build %s request"), service_name_locale);
5797 err = IE_ERROR;
5798 goto leave;
5800 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5801 if(!isds_ns) {
5802 isds_log_message(context, _("Could not create ISDS name space"));
5803 err = IE_ERROR;
5804 goto leave;
5806 xmlSetNs(request, isds_ns);
5809 /* Add XSD:tOwnerInfoInput child*/
5810 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
5811 err = insert_DbOwnerInfo(context, owner, db_owner_info);
5812 if (err) goto leave;
5814 /* Add XSD:gExtApproval*/
5815 err = insert_GExtApproval(context, approval, request);
5816 if (err) goto leave;
5818 /* Send it to server and process response */
5819 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
5820 service_name, &request, refnumber);
5822 leave:
5823 xmlFreeNode(request);
5824 free(service_name_locale);
5826 return err;
5830 /* Switch box accessibility state on request of box owner.
5831 * Despite the name, owner must do the request off-line. This function is
5832 * designed for such off-line meeting points (e.g. Czech POINT).
5833 * @context is ISDS session context.
5834 * @box identifies box to switch accessibility state.
5835 * @allow is true for making accessible, false to disallow access.
5836 * @approval is optional external approval of box manipulation
5837 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5838 * NULL, if you don't care. */
5839 isds_error isds_switch_box_accessibility_on_owner_request(
5840 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
5841 const _Bool allow, const struct isds_approval *approval,
5842 char **refnumber) {
5843 return build_send_manipulationdbowner_request_check_drop_response(context,
5844 (allow) ? BAD_CAST "EnableOwnDataBox" :
5845 BAD_CAST "DisableOwnDataBox",
5846 box, approval, (xmlChar **) refnumber);
5850 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
5851 * date.
5852 * @context is ISDS session context.
5853 * @box identifies box to switch accessibility state.
5854 * @since is date since accessibility has been denied. This can be past too.
5855 * Only tm_year, tm_mon and tm_mday carry sane value.
5856 * @approval is optional external approval of box manipulation
5857 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5858 * NULL, if you don't care. */
5859 isds_error isds_disable_box_accessibility_externaly(
5860 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
5861 const struct tm *since, const struct isds_approval *approval,
5862 char **refnumber) {
5863 isds_error err = IE_SUCCESS;
5864 char *service_name_locale = NULL;
5865 xmlNodePtr request = NULL, node;
5866 xmlNsPtr isds_ns = NULL;
5867 xmlChar *string = NULL;
5870 if (!context) return IE_INVALID_CONTEXT;
5871 zfree(context->long_message);
5872 if (!box || !since) return IE_INVAL;
5874 /* Build request */
5875 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
5876 if (!request) {
5877 isds_printf_message(context,
5878 _("Could not build %s request"), "DisableDataBoxExternally");
5879 err = IE_ERROR;
5880 goto leave;
5882 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5883 if(!isds_ns) {
5884 isds_log_message(context, _("Could not create ISDS name space"));
5885 err = IE_ERROR;
5886 goto leave;
5888 xmlSetNs(request, isds_ns);
5891 /* Add @box identification */
5892 INSERT_ELEMENT(node, request, "dbOwnerInfo");
5893 err = insert_DbOwnerInfo(context, box, node);
5894 if (err) goto leave;
5896 /* Add @since date */
5897 err = tm2datestring(since, &string);
5898 if(err) {
5899 isds_log_message(context,
5900 _("Could not convert `since' argument to ISO date string"));
5901 goto leave;
5903 INSERT_STRING(request, "dbOwnerDisableDate", string);
5904 zfree(string);
5906 /* Add @approval */
5907 err = insert_GExtApproval(context, approval, request);
5908 if (err) goto leave;
5910 /* Send it to server and process response */
5911 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
5912 BAD_CAST "DisableDataBoxExternally", &request,
5913 (xmlChar **) refnumber);
5915 leave:
5916 free(string);
5917 xmlFreeNode(request);
5918 free(service_name_locale);
5920 return err;
5924 /* Insert struct isds_message data (envelope (recipient data optional) and
5925 * documents) into XML tree
5926 * @context is session context
5927 * @outgoing_message is libisds structure with message data
5928 * @create_message is XML CreateMessage or CreateMultipleMessage element
5929 * @process_recipient true for recipient data serialization, false for no
5930 * serialization */
5931 static isds_error insert_envelope_files(struct isds_ctx *context,
5932 const struct isds_message *outgoing_message, xmlNodePtr create_message,
5933 const _Bool process_recipient) {
5935 isds_error err = IE_SUCCESS;
5936 xmlNodePtr envelope, dm_files, node;
5937 xmlChar *string = NULL;
5939 if (!context) return IE_INVALID_CONTEXT;
5940 if (!outgoing_message || !create_message) return IE_INVAL;
5943 /* Build envelope */
5944 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
5945 if (!envelope) {
5946 isds_printf_message(context, _("Could not add dmEnvelope child to "
5947 "%s element"), create_message->name);
5948 return IE_ERROR;
5951 if (!outgoing_message->envelope) {
5952 isds_log_message(context, _("Outgoing message is missing envelope"));
5953 err = IE_INVAL;
5954 goto leave;
5957 /* Insert optional message type */
5958 err = insert_message_type(context, outgoing_message->envelope->dmType,
5959 envelope);
5960 if (err) goto leave;
5962 INSERT_STRING(envelope, "dmSenderOrgUnit",
5963 outgoing_message->envelope->dmSenderOrgUnit);
5964 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
5965 outgoing_message->envelope->dmSenderOrgUnitNum, string);
5967 if (process_recipient) {
5968 if (!outgoing_message->envelope->dbIDRecipient) {
5969 isds_log_message(context,
5970 _("Outgoing message is missing recipient box identifier"));
5971 err = IE_INVAL;
5972 goto leave;
5974 INSERT_STRING(envelope, "dbIDRecipient",
5975 outgoing_message->envelope->dbIDRecipient);
5977 INSERT_STRING(envelope, "dmRecipientOrgUnit",
5978 outgoing_message->envelope->dmRecipientOrgUnit);
5979 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
5980 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
5981 INSERT_STRING(envelope, "dmToHands",
5982 outgoing_message->envelope->dmToHands);
5985 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
5986 "dmAnnotation");
5987 INSERT_STRING(envelope, "dmAnnotation",
5988 outgoing_message->envelope->dmAnnotation);
5990 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
5991 0, 50, "dmRecipientRefNumber");
5992 INSERT_STRING(envelope, "dmRecipientRefNumber",
5993 outgoing_message->envelope->dmRecipientRefNumber);
5995 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
5996 0, 50, "dmSenderRefNumber");
5997 INSERT_STRING(envelope, "dmSenderRefNumber",
5998 outgoing_message->envelope->dmSenderRefNumber);
6000 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
6001 0, 50, "dmRecipientIdent");
6002 INSERT_STRING(envelope, "dmRecipientIdent",
6003 outgoing_message->envelope->dmRecipientIdent);
6005 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
6006 0, 50, "dmSenderIdent");
6007 INSERT_STRING(envelope, "dmSenderIdent",
6008 outgoing_message->envelope->dmSenderIdent);
6010 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
6011 outgoing_message->envelope->dmLegalTitleLaw, string);
6012 INSERT_LONGINT(envelope, "dmLegalTitleYear",
6013 outgoing_message->envelope->dmLegalTitleYear, string);
6014 INSERT_STRING(envelope, "dmLegalTitleSect",
6015 outgoing_message->envelope->dmLegalTitleSect);
6016 INSERT_STRING(envelope, "dmLegalTitlePar",
6017 outgoing_message->envelope->dmLegalTitlePar);
6018 INSERT_STRING(envelope, "dmLegalTitlePoint",
6019 outgoing_message->envelope->dmLegalTitlePoint);
6021 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
6022 outgoing_message->envelope->dmPersonalDelivery);
6023 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
6024 outgoing_message->envelope->dmAllowSubstDelivery);
6026 /* ???: Should we require value for dbEffectiveOVM sender?
6027 * ISDS has default as true */
6028 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
6031 /* Append dmFiles */
6032 if (!outgoing_message->documents) {
6033 isds_log_message(context,
6034 _("Outgoing message is missing list of documents"));
6035 err = IE_INVAL;
6036 goto leave;
6038 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
6039 if (!dm_files) {
6040 isds_printf_message(context, _("Could not add dmFiles child to "
6041 "%s element"), create_message->name);
6042 err = IE_ERROR;
6043 goto leave;
6046 /* Check for document hierarchy */
6047 err = _isds_check_documents_hierarchy(context, outgoing_message->documents);
6048 if (err) goto leave;
6050 /* Process each document */
6051 for (struct isds_list *item =
6052 (struct isds_list *) outgoing_message->documents;
6053 item; item = item->next) {
6054 if (!item->data) {
6055 isds_log_message(context,
6056 _("List of documents contains empty item"));
6057 err = IE_INVAL;
6058 goto leave;
6060 /* FIXME: Check for dmFileMetaType and for document references.
6061 * Only first document can be of MAIN type */
6062 err = insert_document(context, (struct isds_document*) item->data,
6063 dm_files);
6065 if (err) goto leave;
6068 leave:
6069 free(string);
6070 return err;
6074 /* Send a message via ISDS to a recipient
6075 * @context is session context
6076 * @outgoing_message is message to send; Some members are mandatory (like
6077 * dbIDRecipient), some are optional and some are irrelevant (especially data
6078 * about sender). Included pointer to isds_list documents must contain at
6079 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
6080 * members will be filled with valid data from ISDS. Exact list of write
6081 * members is subject to change. Currently dmId is changed.
6082 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
6083 isds_error isds_send_message(struct isds_ctx *context,
6084 struct isds_message *outgoing_message) {
6086 isds_error err = IE_SUCCESS;
6087 xmlNsPtr isds_ns = NULL;
6088 xmlNodePtr request = NULL;
6089 xmlDocPtr response = NULL;
6090 xmlChar *code = NULL, *message = NULL;
6091 xmlXPathContextPtr xpath_ctx = NULL;
6092 xmlXPathObjectPtr result = NULL;
6093 _Bool message_is_complete = 0;
6095 if (!context) return IE_INVALID_CONTEXT;
6096 zfree(context->long_message);
6097 if (!outgoing_message) return IE_INVAL;
6099 /* Check if connection is established
6100 * TODO: This check should be done downstairs. */
6101 if (!context->curl) return IE_CONNECTION_CLOSED;
6104 /* Build CreateMessage request */
6105 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
6106 if (!request) {
6107 isds_log_message(context,
6108 _("Could build CreateMessage request"));
6109 return IE_ERROR;
6111 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6112 if(!isds_ns) {
6113 isds_log_message(context, _("Could not create ISDS name space"));
6114 xmlFreeNode(request);
6115 return IE_ERROR;
6117 xmlSetNs(request, isds_ns);
6119 /* Append envelope and files */
6120 err = insert_envelope_files(context, outgoing_message, request, 1);
6121 if (err) goto leave;
6124 /* Signal we can serialize message since now */
6125 message_is_complete = 1;
6128 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
6130 /* Sent request */
6131 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
6133 /* Don't' destroy request, we want to provide it to application later */
6135 if (err) {
6136 isds_log(ILF_ISDS, ILL_DEBUG,
6137 _("Processing ISDS response on CreateMessage "
6138 "request failed\n"));
6139 goto leave;
6142 /* Check for response status */
6143 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
6144 &code, &message, NULL);
6145 if (err) {
6146 isds_log(ILF_ISDS, ILL_DEBUG,
6147 _("ISDS response on CreateMessage request "
6148 "is missing status\n"));
6149 goto leave;
6152 /* Request processed, but refused by server or server failed */
6153 if (xmlStrcmp(code, BAD_CAST "0000")) {
6154 char *box_id_locale =
6155 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
6156 char *code_locale = _isds_utf82locale((char*)code);
6157 char *message_locale = _isds_utf82locale((char*)message);
6158 isds_log(ILF_ISDS, ILL_DEBUG,
6159 _("Server did not accept message for %s on CreateMessage "
6160 "request (code=%s, message=%s)\n"),
6161 box_id_locale, code_locale, message_locale);
6162 isds_log_message(context, message_locale);
6163 free(box_id_locale);
6164 free(code_locale);
6165 free(message_locale);
6166 err = IE_ISDS;
6167 goto leave;
6171 /* Extract data */
6172 xpath_ctx = xmlXPathNewContext(response);
6173 if (!xpath_ctx) {
6174 err = IE_ERROR;
6175 goto leave;
6177 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6178 err = IE_ERROR;
6179 goto leave;
6181 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
6182 xpath_ctx);
6183 if (!result) {
6184 err = IE_ERROR;
6185 goto leave;
6187 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6188 isds_log_message(context, _("Missing CreateMessageResponse element"));
6189 err = IE_ISDS;
6190 goto leave;
6192 if (result->nodesetval->nodeNr > 1) {
6193 isds_log_message(context, _("Multiple CreateMessageResponse element"));
6194 err = IE_ISDS;
6195 goto leave;
6197 xpath_ctx->node = result->nodesetval->nodeTab[0];
6198 xmlXPathFreeObject(result); result = NULL;
6200 if (outgoing_message->envelope->dmID) {
6201 free(outgoing_message->envelope->dmID);
6202 outgoing_message->envelope->dmID = NULL;
6204 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
6205 if (!outgoing_message->envelope->dmID) {
6206 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
6207 "but did not return assigned message ID\n"));
6210 leave:
6211 /* TODO: Serialize message into structure member raw */
6212 /* XXX: Each web service transport message in different format.
6213 * Therefore it's not possible to save them directly.
6214 * To save them, one must figure out common format.
6215 * We can leave it on application, or we can implement the ESS format. */
6216 /*if (message_is_complete) {
6217 if (outgoing_message->envelope->dmID) {
6219 /* Add assigned message ID as first child*/
6220 /*xmlNodePtr dmid_text = xmlNewText(
6221 (xmlChar *) outgoing_message->envelope->dmID);
6222 if (!dmid_text) goto serialization_failed;
6224 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
6225 BAD_CAST "dmID");
6226 if (!dmid_element) {
6227 xmlFreeNode(dmid_text);
6228 goto serialization_failed;
6231 xmlNodePtr dmid_element_with_text =
6232 xmlAddChild(dmid_element, dmid_text);
6233 if (!dmid_element_with_text) {
6234 xmlFreeNode(dmid_element);
6235 xmlFreeNode(dmid_text);
6236 goto serialization_failed;
6239 node = xmlAddPrevSibling(envelope->childern,
6240 dmid_element_with_text);
6241 if (!node) {
6242 xmlFreeNodeList(dmid_element_with_text);
6243 goto serialization_failed;
6247 /* Serialize message with ID into raw */
6248 /*buffer = serialize_element(envelope)*/
6249 /* }
6251 serialization_failed:
6255 /* Clean up */
6256 xmlXPathFreeObject(result);
6257 xmlXPathFreeContext(xpath_ctx);
6259 free(code);
6260 free(message);
6261 xmlFreeDoc(response);
6262 xmlFreeNode(request);
6264 if (!err)
6265 isds_log(ILF_ISDS, ILL_DEBUG,
6266 _("CreateMessage request processed by server "
6267 "successfully.\n"));
6269 return err;
6273 /* Send a message via ISDS to a multiple recipients
6274 * @context is session context
6275 * @outgoing_message is message to send; Some members are mandatory,
6276 * some are optional and some are irrelevant (especially data
6277 * about sender). Data about recipient will be substituted by ISDS from
6278 * @copies. Included pointer to isds_list documents must
6279 * contain at least one document of FILEMETATYPE_MAIN.
6280 * @copies is list of isds_message_copy structures addressing all desired
6281 * recipients. This is read-write structure, some members will be filled with
6282 * valid data from ISDS (message IDs, error codes, error descriptions).
6283 * @return
6284 * ISDS_SUCCESS if all messages have been sent
6285 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
6286 * succeeded messages can be identified by copies->data->error),
6287 * or other error code if something other goes wrong. */
6288 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
6289 const struct isds_message *outgoing_message,
6290 struct isds_list *copies) {
6292 isds_error err = IE_SUCCESS, append_err;
6293 xmlNsPtr isds_ns = NULL;
6294 xmlNodePtr request = NULL, recipients, recipient, node;
6295 struct isds_list *item;
6296 struct isds_message_copy *copy;
6297 xmlDocPtr response = NULL;
6298 xmlChar *code = NULL, *message = NULL;
6299 xmlXPathContextPtr xpath_ctx = NULL;
6300 xmlXPathObjectPtr result = NULL;
6301 xmlChar *string = NULL;
6302 int i;
6304 if (!context) return IE_INVALID_CONTEXT;
6305 zfree(context->long_message);
6306 if (!outgoing_message || !copies) return IE_INVAL;
6308 /* Check if connection is established
6309 * TODO: This check should be done downstairs. */
6310 if (!context->curl) return IE_CONNECTION_CLOSED;
6313 /* Build CreateMultipleMessage request */
6314 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
6315 if (!request) {
6316 isds_log_message(context,
6317 _("Could not build CreateMultipleMessage request"));
6318 return IE_ERROR;
6320 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6321 if(!isds_ns) {
6322 isds_log_message(context, _("Could not create ISDS name space"));
6323 xmlFreeNode(request);
6324 return IE_ERROR;
6326 xmlSetNs(request, isds_ns);
6329 /* Build recipients */
6330 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
6331 if (!recipients) {
6332 isds_log_message(context, _("Could not add dmRecipients child to "
6333 "CreateMultipleMessage element"));
6334 xmlFreeNode(request);
6335 return IE_ERROR;
6338 /* Insert each recipient */
6339 for (item = copies; item; item = item->next) {
6340 copy = (struct isds_message_copy *) item->data;
6341 if (!copy) {
6342 isds_log_message(context,
6343 _("`copies' list item contains empty data"));
6344 err = IE_INVAL;
6345 goto leave;
6348 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
6349 if (!recipient) {
6350 isds_log_message(context, _("Could not add dmRecipient child to "
6351 "dmRecipients element"));
6352 err = IE_ERROR;
6353 goto leave;
6356 if (!copy->dbIDRecipient) {
6357 isds_log_message(context,
6358 _("Message copy is missing recipient box identifier"));
6359 err = IE_INVAL;
6360 goto leave;
6362 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
6363 INSERT_STRING(recipient, "dmRecipientOrgUnit",
6364 copy->dmRecipientOrgUnit);
6365 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
6366 copy->dmRecipientOrgUnitNum, string);
6367 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
6370 /* Append envelope and files */
6371 err = insert_envelope_files(context, outgoing_message, request, 0);
6372 if (err) goto leave;
6375 isds_log(ILF_ISDS, ILL_DEBUG,
6376 _("Sending CreateMultipleMessage request to ISDS\n"));
6378 /* Sent request */
6379 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
6380 if (err) {
6381 isds_log(ILF_ISDS, ILL_DEBUG,
6382 _("Processing ISDS response on CreateMultipleMessage "
6383 "request failed\n"));
6384 goto leave;
6387 /* Check for response status */
6388 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
6389 &code, &message, NULL);
6390 if (err) {
6391 isds_log(ILF_ISDS, ILL_DEBUG,
6392 _("ISDS response on CreateMultipleMessage request "
6393 "is missing status\n"));
6394 goto leave;
6397 /* Request processed, but some copies failed */
6398 if (!xmlStrcmp(code, BAD_CAST "0004")) {
6399 char *box_id_locale =
6400 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
6401 char *code_locale = _isds_utf82locale((char*)code);
6402 char *message_locale = _isds_utf82locale((char*)message);
6403 isds_log(ILF_ISDS, ILL_DEBUG,
6404 _("Server did accept message for multiple recipients "
6405 "on CreateMultipleMessage request but delivery to "
6406 "some of them failed (code=%s, message=%s)\n"),
6407 box_id_locale, code_locale, message_locale);
6408 isds_log_message(context, message_locale);
6409 free(box_id_locale);
6410 free(code_locale);
6411 free(message_locale);
6412 err = IE_PARTIAL_SUCCESS;
6415 /* Request refused by server as whole */
6416 else if (xmlStrcmp(code, BAD_CAST "0000")) {
6417 char *box_id_locale =
6418 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
6419 char *code_locale = _isds_utf82locale((char*)code);
6420 char *message_locale = _isds_utf82locale((char*)message);
6421 isds_log(ILF_ISDS, ILL_DEBUG,
6422 _("Server did not accept message for multiple recipients "
6423 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
6424 box_id_locale, code_locale, message_locale);
6425 isds_log_message(context, message_locale);
6426 free(box_id_locale);
6427 free(code_locale);
6428 free(message_locale);
6429 err = IE_ISDS;
6430 goto leave;
6434 /* Extract data */
6435 xpath_ctx = xmlXPathNewContext(response);
6436 if (!xpath_ctx) {
6437 err = IE_ERROR;
6438 goto leave;
6440 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6441 err = IE_ERROR;
6442 goto leave;
6444 result = xmlXPathEvalExpression(
6445 BAD_CAST "/isds:CreateMultipleMessageResponse"
6446 "/isds:dmMultipleStatus/isds:dmSingleStatus",
6447 xpath_ctx);
6448 if (!result) {
6449 err = IE_ERROR;
6450 goto leave;
6452 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6453 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
6454 err = IE_ISDS;
6455 goto leave;
6458 /* Extract message ID and delivery status for each copy */
6459 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
6460 item = item->next, i++) {
6461 copy = (struct isds_message_copy *) item->data;
6462 xpath_ctx->node = result->nodesetval->nodeTab[i];
6464 append_err = append_TMStatus(context, copy, xpath_ctx);
6465 if (append_err) {
6466 err = append_err;
6467 goto leave;
6470 if (item || i < result->nodesetval->nodeNr) {
6471 isds_printf_message(context, _("ISDS returned unexpected number of "
6472 "message copy delivery states: %d"),
6473 result->nodesetval->nodeNr);
6474 err = IE_ISDS;
6475 goto leave;
6479 leave:
6480 /* Clean up */
6481 free(string);
6482 xmlXPathFreeObject(result);
6483 xmlXPathFreeContext(xpath_ctx);
6485 free(code);
6486 free(message);
6487 xmlFreeDoc(response);
6488 xmlFreeNode(request);
6490 if (!err)
6491 isds_log(ILF_ISDS, ILL_DEBUG,
6492 _("CreateMultipleMessageResponse request processed by server "
6493 "successfully.\n"));
6495 return err;
6499 /* Get list of messages. This is common core for getting sent or received
6500 * messages.
6501 * Any criterion argument can be NULL, if you don't care about it.
6502 * @context is session context. Must not be NULL.
6503 * @outgoing_direction is true if you want list of outgoing messages,
6504 * it's false if you want incoming messages.
6505 * @from_time is minimal time and date of message sending inclusive.
6506 * @to_time is maximal time and date of message sending inclusive
6507 * @organization_unit_number is number of sender/recipient respectively.
6508 * @status_filter is bit field of isds_message_status values. Use special
6509 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
6510 * all values, you can use bit-wise arithmetic if you want.)
6511 * @offset is index of first message we are interested in. First message is 1.
6512 * Set to 0 (or 1) if you don't care.
6513 * @number is maximal length of list you want to get as input value, outputs
6514 * number of messages matching these criteria. Can be NULL if you don't care
6515 * (applies to output value either).
6516 * @messages is automatically reallocated list of isds_message's. Be ware that
6517 * it returns only brief overview (envelope and some other fields) about each
6518 * message, not the complete message. FIXME: Specify exact fields.
6519 * The list is sorted by delivery time in ascending order.
6520 * Use NULL if
6521 * you don't care about don't need the data (useful if you want to know only
6522 * the @number). If you provide &NULL, list will be allocated on heap, if you
6523 * provide pointer to non-NULL, list will be freed automatically at first. Also
6524 * in case of error the list will be NULLed.
6525 * @return IE_SUCCESS or appropriate error code. */
6526 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
6527 _Bool outgoing_direction,
6528 const struct timeval *from_time, const struct timeval *to_time,
6529 const long int *organization_unit_number,
6530 const unsigned int status_filter,
6531 const unsigned long int offset, unsigned long int *number,
6532 struct isds_list **messages) {
6534 isds_error err = IE_SUCCESS;
6535 xmlNsPtr isds_ns = NULL;
6536 xmlNodePtr request = NULL, node;
6537 xmlDocPtr response = NULL;
6538 xmlChar *code = NULL, *message = NULL;
6539 xmlXPathContextPtr xpath_ctx = NULL;
6540 xmlXPathObjectPtr result = NULL;
6541 xmlChar *string = NULL;
6542 long unsigned int count = 0;
6544 if (!context) return IE_INVALID_CONTEXT;
6545 zfree(context->long_message);
6547 /* Free former message list if any */
6548 if (messages) isds_list_free(messages);
6550 /* Check if connection is established
6551 * TODO: This check should be done downstairs. */
6552 if (!context->curl) return IE_CONNECTION_CLOSED;
6554 /* Build GetListOf*Messages request */
6555 request = xmlNewNode(NULL,
6556 (outgoing_direction) ?
6557 BAD_CAST "GetListOfSentMessages" :
6558 BAD_CAST "GetListOfReceivedMessages"
6560 if (!request) {
6561 isds_log_message(context,
6562 (outgoing_direction) ?
6563 _("Could not build GetListOfSentMessages request") :
6564 _("Could not build GetListOfReceivedMessages request")
6566 return IE_ERROR;
6568 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6569 if(!isds_ns) {
6570 isds_log_message(context, _("Could not create ISDS name space"));
6571 xmlFreeNode(request);
6572 return IE_ERROR;
6574 xmlSetNs(request, isds_ns);
6577 if (from_time) {
6578 err = timeval2timestring(from_time, &string);
6579 if (err) goto leave;
6581 INSERT_STRING(request, "dmFromTime", string);
6582 free(string); string = NULL;
6584 if (to_time) {
6585 err = timeval2timestring(to_time, &string);
6586 if (err) goto leave;
6588 INSERT_STRING(request, "dmToTime", string);
6589 free(string); string = NULL;
6591 if (outgoing_direction) {
6592 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
6593 organization_unit_number, string);
6594 } else {
6595 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
6596 organization_unit_number, string);
6599 if (status_filter > MESSAGESTATE_ANY) {
6600 isds_printf_message(context,
6601 _("Invalid message state filter value: %ld"), status_filter);
6602 err = IE_INVAL;
6603 goto leave;
6605 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
6607 if (offset > 0 ) {
6608 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
6609 } else {
6610 INSERT_STRING(request, "dmOffset", "1");
6613 /* number 0 means no limit */
6614 if (number && *number == 0) {
6615 INSERT_STRING(request, "dmLimit", NULL);
6616 } else {
6617 INSERT_ULONGINT(request, "dmLimit", number, string);
6621 isds_log(ILF_ISDS, ILL_DEBUG,
6622 (outgoing_direction) ?
6623 _("Sending GetListOfSentMessages request to ISDS\n") :
6624 _("Sending GetListOfReceivedMessages request to ISDS\n")
6627 /* Sent request */
6628 err = isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
6629 xmlFreeNode(request); request = NULL;
6631 if (err) {
6632 isds_log(ILF_ISDS, ILL_DEBUG,
6633 (outgoing_direction) ?
6634 _("Processing ISDS response on GetListOfSentMessages "
6635 "request failed\n") :
6636 _("Processing ISDS response on GetListOfReceivedMessages "
6637 "request failed\n")
6639 goto leave;
6642 /* Check for response status */
6643 err = isds_response_status(context, SERVICE_DM_INFO, response,
6644 &code, &message, NULL);
6645 if (err) {
6646 isds_log(ILF_ISDS, ILL_DEBUG,
6647 (outgoing_direction) ?
6648 _("ISDS response on GetListOfSentMessages request "
6649 "is missing status\n") :
6650 _("ISDS response on GetListOfReceivedMessages request "
6651 "is missing status\n")
6653 goto leave;
6656 /* Request processed, but nothing found */
6657 if (xmlStrcmp(code, BAD_CAST "0000")) {
6658 char *code_locale = _isds_utf82locale((char*)code);
6659 char *message_locale = _isds_utf82locale((char*)message);
6660 isds_log(ILF_ISDS, ILL_DEBUG,
6661 (outgoing_direction) ?
6662 _("Server refused GetListOfSentMessages request "
6663 "(code=%s, message=%s)\n") :
6664 _("Server refused GetListOfReceivedMessages request "
6665 "(code=%s, message=%s)\n"),
6666 code_locale, message_locale);
6667 isds_log_message(context, message_locale);
6668 free(code_locale);
6669 free(message_locale);
6670 err = IE_ISDS;
6671 goto leave;
6675 /* Extract data */
6676 xpath_ctx = xmlXPathNewContext(response);
6677 if (!xpath_ctx) {
6678 err = IE_ERROR;
6679 goto leave;
6681 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6682 err = IE_ERROR;
6683 goto leave;
6685 result = xmlXPathEvalExpression(
6686 (outgoing_direction) ?
6687 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
6688 "isds:dmRecords/isds:dmRecord" :
6689 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
6690 "isds:dmRecords/isds:dmRecord",
6691 xpath_ctx);
6692 if (!result) {
6693 err = IE_ERROR;
6694 goto leave;
6697 /* Fill output arguments in */
6698 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6699 struct isds_envelope *envelope;
6700 struct isds_list *item = NULL, *last_item = NULL;
6702 for (count = 0; count < result->nodesetval->nodeNr; count++) {
6703 /* Create new message */
6704 item = calloc(1, sizeof(*item));
6705 if (!item) {
6706 err = IE_NOMEM;
6707 goto leave;
6709 item->destructor = (void(*)(void**)) &isds_message_free;
6710 item->data = calloc(1, sizeof(struct isds_message));
6711 if (!item->data) {
6712 isds_list_free(&item);
6713 err = IE_NOMEM;
6714 goto leave;
6717 /* Extract envelope data */
6718 xpath_ctx->node = result->nodesetval->nodeTab[count];
6719 envelope = NULL;
6720 err = extract_DmRecord(context, &envelope, xpath_ctx);
6721 if (err) {
6722 isds_list_free(&item);
6723 goto leave;
6726 /* Attach extracted envelope */
6727 ((struct isds_message *) item->data)->envelope = envelope;
6729 /* Append new message into the list */
6730 if (!*messages) {
6731 *messages = last_item = item;
6732 } else {
6733 last_item->next = item;
6734 last_item = item;
6738 if (number) *number = count;
6740 leave:
6741 if (err) {
6742 isds_list_free(messages);
6745 free(string);
6746 xmlXPathFreeObject(result);
6747 xmlXPathFreeContext(xpath_ctx);
6749 free(code);
6750 free(message);
6751 xmlFreeDoc(response);
6752 xmlFreeNode(request);
6754 if (!err)
6755 isds_log(ILF_ISDS, ILL_DEBUG,
6756 (outgoing_direction) ?
6757 _("GetListOfSentMessages request processed by server "
6758 "successfully.\n") :
6759 _("GetListOfReceivedMessages request processed by server "
6760 "successfully.\n")
6762 return err;
6766 /* Get list of outgoing (already sent) messages.
6767 * Any criterion argument can be NULL, if you don't care about it.
6768 * @context is session context. Must not be NULL.
6769 * @from_time is minimal time and date of message sending inclusive.
6770 * @to_time is maximal time and date of message sending inclusive
6771 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
6772 * @status_filter is bit field of isds_message_status values. Use special
6773 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
6774 * all values, you can use bit-wise arithmetic if you want.)
6775 * @offset is index of first message we are interested in. First message is 1.
6776 * Set to 0 (or 1) if you don't care.
6777 * @number is maximal length of list you want to get as input value, outputs
6778 * number of messages matching these criteria. Can be NULL if you don't care
6779 * (applies to output value either).
6780 * @messages is automatically reallocated list of isds_message's. Be ware that
6781 * it returns only brief overview (envelope and some other fields) about each
6782 * message, not the complete message. FIXME: Specify exact fields.
6783 * The list is sorted by delivery time in ascending order.
6784 * Use NULL if you don't care about the meta data (useful if you want to know
6785 * only the @number). If you provide &NULL, list will be allocated on heap,
6786 * if you provide pointer to non-NULL, list will be freed automatically at first.
6787 * Also in case of error the list will be NULLed.
6788 * @return IE_SUCCESS or appropriate error code. */
6789 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
6790 const struct timeval *from_time, const struct timeval *to_time,
6791 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
6792 const unsigned long int offset, unsigned long int *number,
6793 struct isds_list **messages) {
6795 return isds_get_list_of_messages(
6796 context, 1,
6797 from_time, to_time, dmSenderOrgUnitNum, status_filter,
6798 offset, number,
6799 messages);
6803 /* Get list of incoming (addressed to you) messages.
6804 * Any criterion argument can be NULL, if you don't care about it.
6805 * @context is session context. Must not be NULL.
6806 * @from_time is minimal time and date of message sending inclusive.
6807 * @to_time is maximal time and date of message sending inclusive
6808 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
6809 * @status_filter is bit field of isds_message_status values. Use special
6810 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
6811 * all values, you can use bit-wise arithmetic if you want.)
6812 * @offset is index of first message we are interested in. First message is 1.
6813 * Set to 0 (or 1) if you don't care.
6814 * @number is maximal length of list you want to get as input value, outputs
6815 * number of messages matching these criteria. Can be NULL if you don't care
6816 * (applies to output value either).
6817 * @messages is automatically reallocated list of isds_message's. Be ware that
6818 * it returns only brief overview (envelope and some other fields) about each
6819 * message, not the complete message. FIXME: Specify exact fields.
6820 * Use NULL if you don't care about the meta data (useful if you want to know
6821 * only the @number). If you provide &NULL, list will be allocated on heap,
6822 * if you provide pointer to non-NULL, list will be freed automatically at first.
6823 * Also in case of error the list will be NULLed.
6824 * @return IE_SUCCESS or appropriate error code. */
6825 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
6826 const struct timeval *from_time, const struct timeval *to_time,
6827 const long int *dmRecipientOrgUnitNum,
6828 const unsigned int status_filter,
6829 const unsigned long int offset, unsigned long int *number,
6830 struct isds_list **messages) {
6832 return isds_get_list_of_messages(
6833 context, 0,
6834 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
6835 offset, number,
6836 messages);
6840 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
6841 * code
6842 * @context is session context
6843 * @service is ISDS WS service handler
6844 * @service_name is name of SERVICE_DM_OPERATIONS
6845 * @message_id is message ID to send as service argument to ISDS
6846 * @response is server SOAP body response as XML document
6847 * @raw_response is automatically reallocated bit stream with response body. Use
6848 * NULL if you don't care
6849 * @raw_response_length is size of @raw_response in bytes
6850 * @code is ISDS status code
6851 * @status_message is ISDS status message
6852 * @return error coded from lower layer, context message will be set up
6853 * appropriately. */
6854 static isds_error build_send_check_message_request(struct isds_ctx *context,
6855 const isds_service service, const xmlChar *service_name,
6856 const char *message_id,
6857 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
6858 xmlChar **code, xmlChar **status_message) {
6860 isds_error err = IE_SUCCESS;
6861 char *service_name_locale = NULL, *message_id_locale = NULL;
6862 xmlNodePtr request = NULL, node;
6863 xmlNsPtr isds_ns = NULL;
6865 if (!context) return IE_INVALID_CONTEXT;
6866 if (!service_name || !message_id) return IE_INVAL;
6867 if (!response || !code || !status_message) return IE_INVAL;
6868 if (!raw_response_length && raw_response) return IE_INVAL;
6870 /* Free output argument */
6871 xmlFreeDoc(*response); *response = NULL;
6872 if (raw_response) zfree(*raw_response);
6873 free(*code);
6874 free(*status_message);
6877 /* Check if connection is established
6878 * TODO: This check should be done downstairs. */
6879 if (!context->curl) return IE_CONNECTION_CLOSED;
6881 service_name_locale = _isds_utf82locale((char*)service_name);
6882 message_id_locale = _isds_utf82locale(message_id);
6883 if (!service_name_locale || !message_id_locale) {
6884 err = IE_NOMEM;
6885 goto leave;
6888 /* Build request */
6889 request = xmlNewNode(NULL, service_name);
6890 if (!request) {
6891 isds_printf_message(context,
6892 _("Could not build %s request"), service_name_locale);
6893 err = IE_ERROR;
6894 goto leave;
6896 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6897 if(!isds_ns) {
6898 isds_log_message(context, _("Could not create ISDS name space"));
6899 err = IE_ERROR;
6900 goto leave;
6902 xmlSetNs(request, isds_ns);
6905 /* Add requested ID */
6906 err = validate_message_id_length(context, (xmlChar *) message_id);
6907 if (err) goto leave;
6908 INSERT_STRING(request, "dmID", message_id);
6911 isds_log(ILF_ISDS, ILL_DEBUG,
6912 _("Sending %s request for %s message ID to ISDS\n"),
6913 service_name_locale, message_id_locale);
6915 /* Send request */
6916 err = isds(context, service, request, response,
6917 raw_response, raw_response_length);
6918 xmlFreeNode(request); request = NULL;
6920 if (err) {
6921 isds_log(ILF_ISDS, ILL_DEBUG,
6922 _("Processing ISDS response on %s request failed\n"),
6923 service_name_locale);
6924 goto leave;
6927 /* Check for response status */
6928 err = isds_response_status(context, service, *response,
6929 code, status_message, NULL);
6930 if (err) {
6931 isds_log(ILF_ISDS, ILL_DEBUG,
6932 _("ISDS response on %s request is missing status\n"),
6933 service_name_locale);
6934 goto leave;
6937 /* Request processed, but nothing found */
6938 if (xmlStrcmp(*code, BAD_CAST "0000")) {
6939 char *code_locale = _isds_utf82locale((char*) *code);
6940 char *status_message_locale = _isds_utf82locale((char*) *status_message);
6941 isds_log(ILF_ISDS, ILL_DEBUG,
6942 _("Server refused %s request for %s message ID "
6943 "(code=%s, message=%s)\n"),
6944 service_name_locale, message_id_locale,
6945 code_locale, status_message_locale);
6946 isds_log_message(context, status_message_locale);
6947 free(code_locale);
6948 free(status_message_locale);
6949 err = IE_ISDS;
6950 goto leave;
6953 leave:
6954 free(message_id_locale);
6955 free(service_name_locale);
6956 xmlFreeNode(request);
6957 return err;
6961 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
6962 * signed data and free ISDS response.
6963 * @context is session context
6964 * @message_id is UTF-8 encoded message ID for logging purpose
6965 * @response is parsed XML document. It will be freed and NULLed in the middle
6966 * of function run to save memory. This is not guaranteed in case of error.
6967 * @request_name is name of ISDS request used to construct response root
6968 * element name and for logging purpose.
6969 * @raw is reallocated output buffer with DER encoded CMS data
6970 * @raw_length is size of @raw buffer in bytes
6971 * @returns standard error codes, in case of error, @raw will be freed and
6972 * NULLed, @response sometimes. */
6973 static isds_error find_extract_signed_data_free_response(
6974 struct isds_ctx *context, const xmlChar *message_id,
6975 xmlDocPtr *response, const xmlChar *request_name,
6976 void **raw, size_t *raw_length) {
6978 isds_error err = IE_SUCCESS;
6979 char *xpath_expression = NULL;
6980 xmlXPathContextPtr xpath_ctx = NULL;
6981 xmlXPathObjectPtr result = NULL;
6982 char *encoded_structure = NULL;
6984 if (!context) return IE_INVALID_CONTEXT;
6985 if (!raw) return IE_INVAL;
6986 zfree(*raw);
6987 if (!message_id || !response || !*response || !request_name || !raw_length)
6988 return IE_INVAL;
6990 /* Build XPath expression */
6991 xpath_expression = _isds_astrcat3("/isds:", (char *) request_name,
6992 "Response/isds:dmSignature");
6993 if (!xpath_expression) return IE_NOMEM;
6995 /* Extract data */
6996 xpath_ctx = xmlXPathNewContext(*response);
6997 if (!xpath_ctx) {
6998 err = IE_ERROR;
6999 goto leave;
7001 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7002 err = IE_ERROR;
7003 goto leave;
7005 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
7006 if (!result) {
7007 err = IE_ERROR;
7008 goto leave;
7010 /* Empty response */
7011 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7012 char *message_id_locale = _isds_utf82locale((char*) message_id);
7013 isds_printf_message(context,
7014 _("Server did not return any signed data for message ID `%s' "
7015 "on %s request"),
7016 message_id_locale, request_name);
7017 free(message_id_locale);
7018 err = IE_ISDS;
7019 goto leave;
7021 /* More responses */
7022 if (result->nodesetval->nodeNr > 1) {
7023 char *message_id_locale = _isds_utf82locale((char*) message_id);
7024 isds_printf_message(context,
7025 _("Server did return more signed data for message ID `%s' "
7026 "on %s request"),
7027 message_id_locale, request_name);
7028 free(message_id_locale);
7029 err = IE_ISDS;
7030 goto leave;
7032 /* One response */
7033 xpath_ctx->node = result->nodesetval->nodeTab[0];
7035 /* Extract PKCS#7 structure */
7036 EXTRACT_STRING(".", encoded_structure);
7037 if (!encoded_structure) {
7038 isds_log_message(context, _("dmSignature element is empty"));
7041 /* Here we have delivery info as standalone CMS in encoded_structure.
7042 * We don't need any other data, free them: */
7043 xmlXPathFreeObject(result); result = NULL;
7044 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
7045 xmlFreeDoc(*response); *response = NULL;
7048 /* Decode PKCS#7 to DER format */
7049 *raw_length = _isds_b64decode(encoded_structure, raw);
7050 if (*raw_length == (size_t) -1) {
7051 isds_log_message(context,
7052 _("Error while Base64-decoding PKCS#7 structure"));
7053 err = IE_ERROR;
7054 goto leave;
7057 leave:
7058 if (err) {
7059 zfree(*raw);
7060 raw_length = 0;
7063 free(encoded_structure);
7064 xmlXPathFreeObject(result);
7065 xmlXPathFreeContext(xpath_ctx);
7066 free(xpath_expression);
7068 return err;
7072 /* Download incoming message envelope identified by ID.
7073 * @context is session context
7074 * @message_id is message identifier (you can get them from
7075 * isds_get_list_of_received_messages())
7076 * @message is automatically reallocated message retrieved from ISDS.
7077 * It will miss documents per se. Use isds_get_received_message(), if you are
7078 * interested in documents (content) too.
7079 * Returned hash and timestamp require documents to be verifiable. */
7080 isds_error isds_get_received_envelope(struct isds_ctx *context,
7081 const char *message_id, struct isds_message **message) {
7083 isds_error err = IE_SUCCESS;
7084 xmlDocPtr response = NULL;
7085 xmlChar *code = NULL, *status_message = NULL;
7086 xmlXPathContextPtr xpath_ctx = NULL;
7087 xmlXPathObjectPtr result = NULL;
7089 if (!context) return IE_INVALID_CONTEXT;
7090 zfree(context->long_message);
7092 /* Free former message if any */
7093 if (!message) return IE_INVAL;
7094 isds_message_free(message);
7096 /* Do request and check for success */
7097 err = build_send_check_message_request(context, SERVICE_DM_INFO,
7098 BAD_CAST "MessageEnvelopeDownload", message_id,
7099 &response, NULL, NULL, &code, &status_message);
7100 if (err) goto leave;
7102 /* Extract data */
7103 xpath_ctx = xmlXPathNewContext(response);
7104 if (!xpath_ctx) {
7105 err = IE_ERROR;
7106 goto leave;
7108 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7109 err = IE_ERROR;
7110 goto leave;
7112 result = xmlXPathEvalExpression(
7113 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
7114 "isds:dmReturnedMessageEnvelope",
7115 xpath_ctx);
7116 if (!result) {
7117 err = IE_ERROR;
7118 goto leave;
7120 /* Empty response */
7121 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7122 char *message_id_locale = _isds_utf82locale((char*) message_id);
7123 isds_printf_message(context,
7124 _("Server did not return any envelope for ID `%s' "
7125 "on MessageEnvelopeDownload request"), message_id_locale);
7126 free(message_id_locale);
7127 err = IE_ISDS;
7128 goto leave;
7130 /* More envelops */
7131 if (result->nodesetval->nodeNr > 1) {
7132 char *message_id_locale = _isds_utf82locale((char*) message_id);
7133 isds_printf_message(context,
7134 _("Server did return more envelopes for ID `%s' "
7135 "on MessageEnvelopeDownload request"), message_id_locale);
7136 free(message_id_locale);
7137 err = IE_ISDS;
7138 goto leave;
7140 /* One message */
7141 xpath_ctx->node = result->nodesetval->nodeTab[0];
7143 /* Extract the envelope (= message without documents, hence 0) */
7144 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
7145 if (err) goto leave;
7147 /* Save XML blob */
7148 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
7149 &(*message)->raw_length);
7151 leave:
7152 if (err) {
7153 isds_message_free(message);
7156 xmlXPathFreeObject(result);
7157 xmlXPathFreeContext(xpath_ctx);
7159 free(code);
7160 free(status_message);
7161 xmlFreeDoc(response);
7163 if (!err)
7164 isds_log(ILF_ISDS, ILL_DEBUG,
7165 _("MessageEnvelopeDownload request processed by server "
7166 "successfully.\n")
7168 return err;
7172 /* Load delivery info of any format from buffer.
7173 * @context is session context
7174 * @raw_type advertises format of @buffer content. Only delivery info types
7175 * are accepted.
7176 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
7177 * retrieve such data from message->raw after calling
7178 * isds_get_signed_delivery_info().
7179 * @length is length of buffer in bytes.
7180 * @message is automatically reallocated message parsed from @buffer.
7181 * @strategy selects how buffer will be attached into raw isds_message member.
7182 * */
7183 isds_error isds_load_delivery_info(struct isds_ctx *context,
7184 const isds_raw_type raw_type,
7185 const void *buffer, const size_t length,
7186 struct isds_message **message, const isds_buffer_strategy strategy) {
7188 isds_error err = IE_SUCCESS;
7189 message_ns_type message_ns;
7190 xmlDocPtr message_doc = NULL;
7191 xmlXPathContextPtr xpath_ctx = NULL;
7192 xmlXPathObjectPtr result = NULL;
7193 void *xml_stream = NULL;
7194 size_t xml_stream_length = 0;
7196 if (!context) return IE_INVALID_CONTEXT;
7197 zfree(context->long_message);
7198 if (!message) return IE_INVAL;
7199 isds_message_free(message);
7200 if (!buffer) return IE_INVAL;
7203 /* Select buffer format and extract XML from CMS*/
7204 switch (raw_type) {
7205 case RAWTYPE_DELIVERYINFO:
7206 message_ns = MESSAGE_NS_UNSIGNED;
7207 xml_stream = (void *) buffer;
7208 xml_stream_length = length;
7209 break;
7211 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
7212 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
7213 xml_stream = (void *) buffer;
7214 xml_stream_length = length;
7215 break;
7217 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
7218 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
7219 err = _isds_extract_cms_data(context, buffer, length,
7220 &xml_stream, &xml_stream_length);
7221 if (err) goto leave;
7222 break;
7224 default:
7225 isds_log_message(context, _("Bad raw delivery representation type"));
7226 return IE_INVAL;
7227 break;
7230 isds_log(ILF_ISDS, ILL_DEBUG,
7231 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
7232 xml_stream_length, xml_stream);
7234 /* Convert delivery info XML stream into XPath context */
7235 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
7236 if (!message_doc) {
7237 err = IE_XML;
7238 goto leave;
7240 xpath_ctx = xmlXPathNewContext(message_doc);
7241 if (!xpath_ctx) {
7242 err = IE_ERROR;
7243 goto leave;
7245 /* XXX: Name spaces mangled for signed delivery info:
7246 * http://isds.czechpoint.cz/v20/delivery:
7248 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
7249 * <q:dmDelivery>
7250 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
7251 * <p:dmID>170272</p:dmID>
7252 * ...
7253 * </p:dmDm>
7254 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
7255 * ...
7256 * </q:dmEvents>...</q:dmEvents>
7257 * </q:dmDelivery>
7258 * </q:GetDeliveryInfoResponse>
7259 * */
7260 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
7261 err = IE_ERROR;
7262 goto leave;
7264 result = xmlXPathEvalExpression(
7265 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
7266 xpath_ctx);
7267 if (!result) {
7268 err = IE_ERROR;
7269 goto leave;
7271 /* Empty delivery info */
7272 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7273 isds_printf_message(context,
7274 _("XML document is not sisds:dmDelivery document"));
7275 err = IE_ISDS;
7276 goto leave;
7278 /* More delivery info's */
7279 if (result->nodesetval->nodeNr > 1) {
7280 isds_printf_message(context,
7281 _("XML document has more sisds:dmDelivery elements"));
7282 err = IE_ISDS;
7283 goto leave;
7285 /* One delivery info */
7286 xpath_ctx->node = result->nodesetval->nodeTab[0];
7288 /* Extract the envelope (= message without documents, hence 0).
7289 * XXX: extract_TReturnedMessage() can obtain attachments size,
7290 * but delivery info carries none. It's coded as option elements,
7291 * so it should work. */
7292 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
7293 if (err) goto leave;
7295 /* Extract events */
7296 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
7297 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
7298 if (err) { err = IE_ERROR; goto leave; }
7299 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
7300 if (err) goto leave;
7302 /* Append raw CMS structure into message */
7303 (*message)->raw_type = raw_type;
7304 switch (strategy) {
7305 case BUFFER_DONT_STORE:
7306 break;
7307 case BUFFER_COPY:
7308 (*message)->raw = malloc(length);
7309 if (!(*message)->raw) {
7310 err = IE_NOMEM;
7311 goto leave;
7313 memcpy((*message)->raw, buffer, length);
7314 (*message)->raw_length = length;
7315 break;
7316 case BUFFER_MOVE:
7317 (*message)->raw = (void *) buffer;
7318 (*message)->raw_length = length;
7319 break;
7320 default:
7321 err = IE_ENUM;
7322 goto leave;
7325 leave:
7326 if (err) {
7327 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
7328 isds_message_free(message);
7331 xmlXPathFreeObject(result);
7332 xmlXPathFreeContext(xpath_ctx);
7333 xmlFreeDoc(message_doc);
7334 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
7336 if (!err)
7337 isds_log(ILF_ISDS, ILL_DEBUG,
7338 _("Delivery info loaded successfully.\n"));
7339 return err;
7343 /* Download signed delivery info-sheet of given message identified by ID.
7344 * @context is session context
7345 * @message_id is message identifier (you can get them from
7346 * isds_get_list_of_{sent,received}_messages())
7347 * @message is automatically reallocated message retrieved from ISDS.
7348 * It will miss documents per se. Use isds_get_signed_received_message(),
7349 * if you are interested in documents (content). OTOH, only this function
7350 * can get list events message has gone through. */
7351 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
7352 const char *message_id, struct isds_message **message) {
7354 isds_error err = IE_SUCCESS;
7355 xmlDocPtr response = NULL;
7356 xmlChar *code = NULL, *status_message = NULL;
7357 void *raw = NULL;
7358 size_t raw_length = 0;
7360 if (!context) return IE_INVALID_CONTEXT;
7361 zfree(context->long_message);
7363 /* Free former message if any */
7364 if (!message) return IE_INVAL;
7365 isds_message_free(message);
7367 /* Do request and check for success */
7368 err = build_send_check_message_request(context, SERVICE_DM_INFO,
7369 BAD_CAST "GetSignedDeliveryInfo", message_id,
7370 &response, NULL, NULL, &code, &status_message);
7371 if (err) goto leave;
7373 /* Find signed delivery info, extract it into raw and maybe free
7374 * response */
7375 err = find_extract_signed_data_free_response(context,
7376 (xmlChar *)message_id, &response,
7377 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
7378 if (err) goto leave;
7380 /* Parse delivery info */
7381 err = isds_load_delivery_info(context,
7382 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
7383 message, BUFFER_MOVE);
7384 if (err) goto leave;
7386 raw = NULL;
7388 leave:
7389 if (err) {
7390 isds_message_free(message);
7393 free(raw);
7394 free(code);
7395 free(status_message);
7396 xmlFreeDoc(response);
7398 if (!err)
7399 isds_log(ILF_ISDS, ILL_DEBUG,
7400 _("GetSignedDeliveryInfo request processed by server "
7401 "successfully.\n")
7403 return err;
7407 /* Download delivery info-sheet of given message identified by ID.
7408 * @context is session context
7409 * @message_id is message identifier (you can get them from
7410 * isds_get_list_of_{sent,received}_messages())
7411 * @message is automatically reallocated message retrieved from ISDS.
7412 * It will miss documents per se. Use isds_get_received_message(), if you are
7413 * interested in documents (content). OTOH, only this function can get list
7414 * of events message has gone through. */
7415 isds_error isds_get_delivery_info(struct isds_ctx *context,
7416 const char *message_id, struct isds_message **message) {
7418 isds_error err = IE_SUCCESS;
7419 xmlDocPtr response = NULL;
7420 xmlChar *code = NULL, *status_message = NULL;
7421 xmlNodePtr delivery_node = NULL;
7422 void *raw = NULL;
7423 size_t raw_length = 0;
7425 if (!context) return IE_INVALID_CONTEXT;
7426 zfree(context->long_message);
7428 /* Free former message if any */
7429 if (!message) return IE_INVAL;
7430 isds_message_free(message);
7432 /* Do request and check for success */
7433 err = build_send_check_message_request(context, SERVICE_DM_INFO,
7434 BAD_CAST "GetDeliveryInfo", message_id,
7435 &response, NULL, NULL, &code, &status_message);
7436 if (err) goto leave;
7439 /* Serialize delivery info */
7440 delivery_node = xmlDocGetRootElement(response);
7441 if (!delivery_node) {
7442 char *message_id_locale = _isds_utf82locale((char*) message_id);
7443 isds_printf_message(context,
7444 _("Server did not return any delivery info for ID `%s' "
7445 "on GetDeliveryInfo request"), message_id_locale);
7446 free(message_id_locale);
7447 err = IE_ISDS;
7448 goto leave;
7450 err = serialize_subtree(context, delivery_node, &raw, &raw_length);
7451 if (err) goto leave;
7453 /* Parse delivery info */
7454 /* TODO: Here we parse the response second time. We could single delivery
7455 * parser from isds_load_delivery_info() to make things faster. */
7456 err = isds_load_delivery_info(context,
7457 RAWTYPE_DELIVERYINFO, raw, raw_length,
7458 message, BUFFER_MOVE);
7459 if (err) goto leave;
7461 raw = NULL;
7464 leave:
7465 if (err) {
7466 isds_message_free(message);
7469 free(raw);
7470 free(code);
7471 free(status_message);
7472 xmlFreeDoc(response);
7474 if (!err)
7475 isds_log(ILF_ISDS, ILL_DEBUG,
7476 _("GetDeliveryInfo request processed by server "
7477 "successfully.\n")
7479 return err;
7483 /* Download incoming message identified by ID.
7484 * @context is session context
7485 * @message_id is message identifier (you can get them from
7486 * isds_get_list_of_received_messages())
7487 * @message is automatically reallocated message retrieved from ISDS */
7488 isds_error isds_get_received_message(struct isds_ctx *context,
7489 const char *message_id, struct isds_message **message) {
7491 isds_error err = IE_SUCCESS;
7492 xmlDocPtr response = NULL;
7493 void *xml_stream = NULL;
7494 size_t xml_stream_length;
7495 xmlChar *code = NULL, *status_message = NULL;
7496 xmlXPathContextPtr xpath_ctx = NULL;
7497 xmlXPathObjectPtr result = NULL;
7498 char *phys_path = NULL;
7499 size_t phys_start, phys_end;
7501 if (!context) return IE_INVALID_CONTEXT;
7502 zfree(context->long_message);
7504 /* Free former message if any */
7505 if (message) isds_message_free(message);
7507 /* Do request and check for success */
7508 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
7509 BAD_CAST "MessageDownload", message_id,
7510 &response, &xml_stream, &xml_stream_length,
7511 &code, &status_message);
7512 if (err) goto leave;
7514 /* Extract data */
7515 xpath_ctx = xmlXPathNewContext(response);
7516 if (!xpath_ctx) {
7517 err = IE_ERROR;
7518 goto leave;
7520 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7521 err = IE_ERROR;
7522 goto leave;
7524 result = xmlXPathEvalExpression(
7525 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
7526 xpath_ctx);
7527 if (!result) {
7528 err = IE_ERROR;
7529 goto leave;
7531 /* Empty response */
7532 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7533 char *message_id_locale = _isds_utf82locale((char*) message_id);
7534 isds_printf_message(context,
7535 _("Server did not return any message for ID `%s' "
7536 "on MessageDownload request"), message_id_locale);
7537 free(message_id_locale);
7538 err = IE_ISDS;
7539 goto leave;
7541 /* More messages */
7542 if (result->nodesetval->nodeNr > 1) {
7543 char *message_id_locale = _isds_utf82locale((char*) message_id);
7544 isds_printf_message(context,
7545 _("Server did return more messages for ID `%s' "
7546 "on MessageDownload request"), message_id_locale);
7547 free(message_id_locale);
7548 err = IE_ISDS;
7549 goto leave;
7551 /* One message */
7552 xpath_ctx->node = result->nodesetval->nodeTab[0];
7554 /* Extract the message */
7555 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
7556 if (err) goto leave;
7558 /* Locate raw XML blob */
7559 phys_path = strdup(
7560 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
7561 PHYSXML_ELEMENT_SEPARATOR
7562 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
7563 PHYSXML_ELEMENT_SEPARATOR
7564 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
7566 if (!phys_path) {
7567 err = IE_NOMEM;
7568 goto leave;
7570 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
7571 phys_path, &phys_start, &phys_end);
7572 zfree(phys_path);
7573 if (err) {
7574 isds_log_message(context,
7575 _("Substring with isds:MessageDownloadResponse element "
7576 "could not be located in raw SOAP message"));
7577 goto leave;
7579 /* Save XML blob */
7580 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
7581 &(*message)->raw_length);*/
7582 /* TODO: Store name space declarations from ancestors */
7583 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
7584 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
7585 (*message)->raw_length = phys_end - phys_start + 1;
7586 (*message)->raw = malloc((*message)->raw_length);
7587 if (!(*message)->raw) {
7588 err = IE_NOMEM;
7589 goto leave;
7591 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
7594 leave:
7595 if (err) {
7596 isds_message_free(message);
7599 free(phys_path);
7601 xmlXPathFreeObject(result);
7602 xmlXPathFreeContext(xpath_ctx);
7604 free(code);
7605 free(status_message);
7606 free(xml_stream);
7607 xmlFreeDoc(response);
7609 if (!err)
7610 isds_log(ILF_ISDS, ILL_DEBUG,
7611 _("MessageDownload request processed by server "
7612 "successfully.\n")
7614 return err;
7618 /* Load message of any type from buffer.
7619 * @context is session context
7620 * @raw_type defines content type of @buffer. Only message types are allowed.
7621 * @buffer is message raw representation. Format (CMS, plain signed,
7622 * message direction) is defined in @raw_type. You can retrieve such data
7623 * from message->raw after calling isds_get_[signed]{received,sent}_message().
7624 * @length is length of buffer in bytes.
7625 * @message is automatically reallocated message parsed from @buffer.
7626 * @strategy selects how buffer will be attached into raw isds_message member.
7627 * */
7628 isds_error isds_load_message(struct isds_ctx *context,
7629 const isds_raw_type raw_type, const void *buffer, const size_t length,
7630 struct isds_message **message, const isds_buffer_strategy strategy) {
7632 isds_error err = IE_SUCCESS;
7633 void *xml_stream = NULL;
7634 size_t xml_stream_length = 0;
7635 message_ns_type message_ns;
7636 xmlDocPtr message_doc = NULL;
7637 xmlXPathContextPtr xpath_ctx = NULL;
7638 xmlXPathObjectPtr result = NULL;
7640 if (!context) return IE_INVALID_CONTEXT;
7641 zfree(context->long_message);
7642 if (!message) return IE_INVAL;
7643 isds_message_free(message);
7644 if (!buffer) return IE_INVAL;
7647 /* Select buffer format and extract XML from CMS*/
7648 switch (raw_type) {
7649 case RAWTYPE_INCOMING_MESSAGE:
7650 message_ns = MESSAGE_NS_UNSIGNED;
7651 xml_stream = (void *) buffer;
7652 xml_stream_length = length;
7653 break;
7655 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
7656 message_ns = MESSAGE_NS_SIGNED_INCOMING;
7657 xml_stream = (void *) buffer;
7658 xml_stream_length = length;
7659 break;
7661 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
7662 message_ns = MESSAGE_NS_SIGNED_INCOMING;
7663 err = _isds_extract_cms_data(context, buffer, length,
7664 &xml_stream, &xml_stream_length);
7665 if (err) goto leave;
7666 break;
7668 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
7669 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
7670 xml_stream = (void *) buffer;
7671 xml_stream_length = length;
7672 break;
7674 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
7675 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
7676 err = _isds_extract_cms_data(context, buffer, length,
7677 &xml_stream, &xml_stream_length);
7678 if (err) goto leave;
7679 break;
7681 default:
7682 isds_log_message(context, _("Bad raw message representation type"));
7683 return IE_INVAL;
7684 break;
7687 isds_log(ILF_ISDS, ILL_DEBUG,
7688 _("Loading message:\n%.*s\nEnd of message\n"),
7689 xml_stream_length, xml_stream);
7691 /* Convert messages XML stream into XPath context */
7692 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
7693 if (!message_doc) {
7694 err = IE_XML;
7695 goto leave;
7697 xpath_ctx = xmlXPathNewContext(message_doc);
7698 if (!xpath_ctx) {
7699 err = IE_ERROR;
7700 goto leave;
7702 /* XXX: Standard name space for unsigned incoming direction:
7703 * http://isds.czechpoint.cz/v20/
7705 * XXX: Name spaces mangled for signed outgoing direction:
7706 * http://isds.czechpoint.cz/v20/SentMessage:
7708 * <q:MessageDownloadResponse
7709 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
7710 * <q:dmReturnedMessage>
7711 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
7712 * <p:dmID>151916</p:dmID>
7713 * ...
7714 * </p:dmDm>
7715 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
7716 * ...
7717 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
7718 * </q:dmReturnedMessage>
7719 * </q:MessageDownloadResponse>
7721 * XXX: Name spaces mangled for signed incoming direction:
7722 * http://isds.czechpoint.cz/v20/message:
7724 * <q:MessageDownloadResponse
7725 * xmlns:q="http://isds.czechpoint.cz/v20/message">
7726 * <q:dmReturnedMessage>
7727 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
7728 * <p:dmID>151916</p:dmID>
7729 * ...
7730 * </p:dmDm>
7731 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
7732 * ...
7733 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
7734 * </q:dmReturnedMessage>
7735 * </q:MessageDownloadResponse>
7737 * Stupidity of ISDS developers is unlimited */
7738 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
7739 err = IE_ERROR;
7740 goto leave;
7742 result = xmlXPathEvalExpression(
7743 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
7744 xpath_ctx);
7745 if (!result) {
7746 err = IE_ERROR;
7747 goto leave;
7749 /* Empty message */
7750 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7751 isds_printf_message(context,
7752 _("XML document does not contain "
7753 "sisds:dmReturnedMessage element"));
7754 err = IE_ISDS;
7755 goto leave;
7757 /* More messages */
7758 if (result->nodesetval->nodeNr > 1) {
7759 isds_printf_message(context,
7760 _("XML document has more sisds:dmReturnedMessage elements"));
7761 err = IE_ISDS;
7762 goto leave;
7764 /* One message */
7765 xpath_ctx->node = result->nodesetval->nodeTab[0];
7767 /* Extract the message */
7768 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
7769 if (err) goto leave;
7771 /* Append raw buffer into message */
7772 (*message)->raw_type = raw_type;
7773 switch (strategy) {
7774 case BUFFER_DONT_STORE:
7775 break;
7776 case BUFFER_COPY:
7777 (*message)->raw = malloc(length);
7778 if (!(*message)->raw) {
7779 err = IE_NOMEM;
7780 goto leave;
7782 memcpy((*message)->raw, buffer, length);
7783 (*message)->raw_length = length;
7784 break;
7785 case BUFFER_MOVE:
7786 (*message)->raw = (void *) buffer;
7787 (*message)->raw_length = length;
7788 break;
7789 default:
7790 err = IE_ENUM;
7791 goto leave;
7795 leave:
7796 if (err) {
7797 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
7798 isds_message_free(message);
7801 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
7802 xmlXPathFreeObject(result);
7803 xmlXPathFreeContext(xpath_ctx);
7804 xmlFreeDoc(message_doc);
7806 if (!err)
7807 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
7808 return err;
7812 /* Determine type of raw message or delivery info according some heuristics.
7813 * It does not validate the raw blob.
7814 * @context is session context
7815 * @raw_type returns content type of @buffer. Valid only if exit code of this
7816 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
7817 * reallocated memory.
7818 * @buffer is message raw representation.
7819 * @length is length of buffer in bytes. */
7820 isds_error isds_guess_raw_type(struct isds_ctx *context,
7821 isds_raw_type *raw_type, const void *buffer, const size_t length) {
7822 isds_error err;
7823 void *xml_stream = NULL;
7824 size_t xml_stream_length = 0;
7825 xmlDocPtr document = NULL;
7826 xmlNodePtr root = NULL;
7828 if (!context) return IE_INVALID_CONTEXT;
7829 zfree(context->long_message);
7830 if (length == 0 || !buffer) return IE_INVAL;
7831 if (!raw_type) return IE_INVAL;
7833 /* Try CMS */
7834 err = _isds_extract_cms_data(context, buffer, length,
7835 &xml_stream, &xml_stream_length);
7836 if (err) {
7837 xml_stream = (void *) buffer;
7838 xml_stream_length = (size_t) length;
7839 err = IE_SUCCESS;
7842 /* Try XML */
7843 document = xmlParseMemory(xml_stream, xml_stream_length);
7844 if (!document) {
7845 isds_printf_message(context,
7846 _("Could not parse data as XML document"));
7847 err = IE_NOTSUP;
7848 goto leave;
7851 /* Get root element */
7852 root = xmlDocGetRootElement(document);
7853 if (!root) {
7854 isds_printf_message(context,
7855 _("XML document is missing root element"));
7856 err = IE_XML;
7857 goto leave;
7860 if (!root->ns || !root->ns->href) {
7861 isds_printf_message(context,
7862 _("Root element does not belong to any name space"));
7863 err = IE_NOTSUP;
7864 goto leave;
7867 /* Test name space */
7868 if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_INCOMING_NS)) {
7869 if (xml_stream == buffer)
7870 *raw_type = RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE;
7871 else
7872 *raw_type = RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
7873 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_OUTGOING_NS)) {
7874 if (xml_stream == buffer)
7875 *raw_type = RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE;
7876 else
7877 *raw_type = RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE;
7878 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_DELIVERY_NS)) {
7879 if (xml_stream == buffer)
7880 *raw_type = RAWTYPE_PLAIN_SIGNED_DELIVERYINFO;
7881 else
7882 *raw_type = RAWTYPE_CMS_SIGNED_DELIVERYINFO;
7883 } else if (!xmlStrcmp(root->ns->href, BAD_CAST ISDS_NS)) {
7884 if (xml_stream != buffer) {
7885 isds_printf_message(context,
7886 _("Document in ISDS name space is encapsulated into CMS" ));
7887 err = IE_NOTSUP;
7888 } else if (!xmlStrcmp(root->name, BAD_CAST "MessageDownloadResponse"))
7889 *raw_type = RAWTYPE_INCOMING_MESSAGE;
7890 else if (!xmlStrcmp(root->name, BAD_CAST "GetDeliveryInfoResponse"))
7891 *raw_type = RAWTYPE_DELIVERYINFO;
7892 else {
7893 isds_printf_message(context,
7894 _("Unknown root element in ISDS name space"));
7895 err = IE_NOTSUP;
7897 } else {
7898 isds_printf_message(context,
7899 _("Unknown namespace"));
7900 err = IE_NOTSUP;
7903 leave:
7904 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
7905 xmlFreeDoc(document);
7906 return err;
7910 /* Download signed incoming/outgoing message identified by ID.
7911 * @context is session context
7912 * @output is true for outgoing message, false for incoming message
7913 * @message_id is message identifier (you can get them from
7914 * isds_get_list_of_{sent,received}_messages())
7915 * @message is automatically reallocated message retrieved from ISDS. The raw
7916 * member will be filled with PKCS#7 structure in DER format. */
7917 static isds_error isds_get_signed_message(struct isds_ctx *context,
7918 const _Bool outgoing, const char *message_id,
7919 struct isds_message **message) {
7921 isds_error err = IE_SUCCESS;
7922 xmlDocPtr response = NULL;
7923 xmlChar *code = NULL, *status_message = NULL;
7924 xmlXPathContextPtr xpath_ctx = NULL;
7925 xmlXPathObjectPtr result = NULL;
7926 char *encoded_structure = NULL;
7927 void *raw = NULL;
7928 size_t raw_length = 0;
7930 if (!context) return IE_INVALID_CONTEXT;
7931 zfree(context->long_message);
7932 if (!message) return IE_INVAL;
7933 isds_message_free(message);
7935 /* Do request and check for success */
7936 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
7937 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
7938 BAD_CAST "SignedMessageDownload",
7939 message_id, &response, NULL, NULL, &code, &status_message);
7940 if (err) goto leave;
7942 /* Find signed message, extract it into raw and maybe free
7943 * response */
7944 err = find_extract_signed_data_free_response(context,
7945 (xmlChar *)message_id, &response,
7946 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
7947 BAD_CAST "SignedMessageDownload",
7948 &raw, &raw_length);
7949 if (err) goto leave;
7951 /* Parse message */
7952 err = isds_load_message(context,
7953 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
7954 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
7955 raw, raw_length, message, BUFFER_MOVE);
7956 if (err) goto leave;
7958 raw = NULL;
7960 leave:
7961 if (err) {
7962 isds_message_free(message);
7965 free(encoded_structure);
7966 xmlXPathFreeObject(result);
7967 xmlXPathFreeContext(xpath_ctx);
7968 free(raw);
7970 free(code);
7971 free(status_message);
7972 xmlFreeDoc(response);
7974 if (!err)
7975 isds_log(ILF_ISDS, ILL_DEBUG,
7976 (outgoing) ?
7977 _("SignedSentMessageDownload request processed by server "
7978 "successfully.\n") :
7979 _("SignedMessageDownload request processed by server "
7980 "successfully.\n")
7982 return err;
7986 /* Download signed incoming message identified by ID.
7987 * @context is session context
7988 * @message_id is message identifier (you can get them from
7989 * isds_get_list_of_received_messages())
7990 * @message is automatically reallocated message retrieved from ISDS. The raw
7991 * member will be filled with PKCS#7 structure in DER format. */
7992 isds_error isds_get_signed_received_message(struct isds_ctx *context,
7993 const char *message_id, struct isds_message **message) {
7994 return isds_get_signed_message(context, 0, message_id, message);
7998 /* Download signed outgoing message identified by ID.
7999 * @context is session context
8000 * @message_id is message identifier (you can get them from
8001 * isds_get_list_of_sent_messages())
8002 * @message is automatically reallocated message retrieved from ISDS. The raw
8003 * member will be filled with PKCS#7 structure in DER format. */
8004 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
8005 const char *message_id, struct isds_message **message) {
8006 return isds_get_signed_message(context, 1, message_id, message);
8010 /* Retrieve hash of message identified by ID stored in ISDS.
8011 * @context is session context
8012 * @message_id is message identifier
8013 * @hash is automatically reallocated message hash downloaded from ISDS.
8014 * Message must exist in system and must not be deleted. */
8015 isds_error isds_download_message_hash(struct isds_ctx *context,
8016 const char *message_id, struct isds_hash **hash) {
8018 isds_error err = IE_SUCCESS;
8019 xmlDocPtr response = NULL;
8020 xmlChar *code = NULL, *status_message = NULL;
8021 xmlXPathContextPtr xpath_ctx = NULL;
8022 xmlXPathObjectPtr result = NULL;
8024 if (!context) return IE_INVALID_CONTEXT;
8025 zfree(context->long_message);
8027 isds_hash_free(hash);
8029 err = build_send_check_message_request(context, SERVICE_DM_INFO,
8030 BAD_CAST "VerifyMessage", message_id,
8031 &response, NULL, NULL, &code, &status_message);
8032 if (err) goto leave;
8035 /* Extract data */
8036 xpath_ctx = xmlXPathNewContext(response);
8037 if (!xpath_ctx) {
8038 err = IE_ERROR;
8039 goto leave;
8041 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8042 err = IE_ERROR;
8043 goto leave;
8045 result = xmlXPathEvalExpression(
8046 BAD_CAST "/isds:VerifyMessageResponse",
8047 xpath_ctx);
8048 if (!result) {
8049 err = IE_ERROR;
8050 goto leave;
8052 /* Empty response */
8053 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8054 char *message_id_locale = _isds_utf82locale((char*) message_id);
8055 isds_printf_message(context,
8056 _("Server did not return any response for ID `%s' "
8057 "on VerifyMessage request"), message_id_locale);
8058 free(message_id_locale);
8059 err = IE_ISDS;
8060 goto leave;
8062 /* More responses */
8063 if (result->nodesetval->nodeNr > 1) {
8064 char *message_id_locale = _isds_utf82locale((char*) message_id);
8065 isds_printf_message(context,
8066 _("Server did return more responses for ID `%s' "
8067 "on VerifyMessage request"), message_id_locale);
8068 free(message_id_locale);
8069 err = IE_ISDS;
8070 goto leave;
8072 /* One response */
8073 xpath_ctx->node = result->nodesetval->nodeTab[0];
8075 /* Extract the hash */
8076 err = find_and_extract_DmHash(context, hash, xpath_ctx);
8078 leave:
8079 if (err) {
8080 isds_hash_free(hash);
8083 xmlXPathFreeObject(result);
8084 xmlXPathFreeContext(xpath_ctx);
8086 free(code);
8087 free(status_message);
8088 xmlFreeDoc(response);
8090 if (!err)
8091 isds_log(ILF_ISDS, ILL_DEBUG,
8092 _("VerifyMessage request processed by server "
8093 "successfully.\n")
8095 return err;
8099 /* Mark message as read. This is a transactional commit function to acknowledge
8100 * to ISDS the message has been downloaded and processed by client properly.
8101 * @context is session context
8102 * @message_id is message identifier. */
8103 isds_error isds_mark_message_read(struct isds_ctx *context,
8104 const char *message_id) {
8106 isds_error err = IE_SUCCESS;
8107 xmlDocPtr response = NULL;
8108 xmlChar *code = NULL, *status_message = NULL;
8110 if (!context) return IE_INVALID_CONTEXT;
8111 zfree(context->long_message);
8113 /* Do request and check for success */
8114 err = build_send_check_message_request(context, SERVICE_DM_INFO,
8115 BAD_CAST "MarkMessageAsDownloaded", message_id,
8116 &response, NULL, NULL, &code, &status_message);
8118 free(code);
8119 free(status_message);
8120 xmlFreeDoc(response);
8122 if (!err)
8123 isds_log(ILF_ISDS, ILL_DEBUG,
8124 _("MarkMessageAsDownloaded request processed by server "
8125 "successfully.\n")
8127 return err;
8131 /* Mark message as received by recipient. This is applicable only to
8132 * commercial message. Use envelope->dmType message member to distinguish
8133 * commercial message from government message. Government message is
8134 * received automatically (by law), commercial message on recipient request.
8135 * @context is session context
8136 * @message_id is message identifier. */
8137 isds_error isds_mark_message_received(struct isds_ctx *context,
8138 const char *message_id) {
8140 isds_error err = IE_SUCCESS;
8141 xmlDocPtr response = NULL;
8142 xmlChar *code = NULL, *status_message = NULL;
8144 if (!context) return IE_INVALID_CONTEXT;
8145 zfree(context->long_message);
8147 /* Do request and check for success */
8148 err = build_send_check_message_request(context, SERVICE_DM_INFO,
8149 BAD_CAST "ConfirmDelivery", message_id,
8150 &response, NULL, NULL, &code, &status_message);
8152 free(code);
8153 free(status_message);
8154 xmlFreeDoc(response);
8156 if (!err)
8157 isds_log(ILF_ISDS, ILL_DEBUG,
8158 _("ConfirmDelivery request processed by server "
8159 "successfully.\n")
8161 return err;
8165 /* Send document for authorize conversion into Czech POINT system.
8166 * This is public anonymous service, no log-in necessary. Special context is
8167 * used to reuse keep-a-live HTTPS connection.
8168 * @context is Czech POINT session context. DO NOT use context connected to
8169 * ISDS server. Use new context or context used by this function previously.
8170 * @document is document to convert. Only data, data_length and dmFileDescr
8171 * members are significant. Be ware that not all document formats can be
8172 * converted (signed PDF 1.3 and higher only (2010-02 state)).
8173 * @id is reallocated identifier assigned by Czech POINT system to
8174 * your document on submit. Use is to tell it to Czech POINT officer.
8175 * @date is reallocated document submit date (submitted documents
8176 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
8177 * value. */
8178 isds_error czp_convert_document(struct isds_ctx *context,
8179 const struct isds_document *document,
8180 char **id, struct tm **date) {
8181 isds_error err = IE_SUCCESS;
8182 xmlNsPtr deposit_ns = NULL, empty_ns = NULL;
8183 xmlNodePtr request = NULL, node;
8184 xmlDocPtr response = NULL;
8185 xmlChar *base64data = NULL;
8187 xmlXPathContextPtr xpath_ctx = NULL;
8188 xmlXPathObjectPtr result = NULL;
8189 long int status = -1;
8190 long int *status_ptr = &status;
8191 char *string = NULL;
8194 if (!context) return IE_INVALID_CONTEXT;
8195 zfree(context->long_message);
8196 if (!document || !id || !date) return IE_INVAL;
8198 /* Free output arguments */
8199 zfree(*id);
8200 zfree(*date);
8202 /* Store configuration */
8203 context->type = CTX_TYPE_CZP;
8204 free(context->url);
8205 context->url = strdup("https://www.czechpoint.cz/uschovna/services.php");
8206 if (!(context->url))
8207 return IE_NOMEM;
8209 /* Prepare CURL handle if not yet connected */
8210 if (!context->curl) {
8211 context->curl = curl_easy_init();
8212 if (!(context->curl))
8213 return IE_ERROR;
8216 /* Build conversion request */
8217 request = xmlNewNode(NULL, BAD_CAST "saveDocument");
8218 if (!request) {
8219 isds_log_message(context,
8220 _("Could not build Czech POINT conversion request"));
8221 return IE_ERROR;
8223 deposit_ns = xmlNewNs(request, BAD_CAST DEPOSIT_NS, BAD_CAST "dep");
8224 if(!deposit_ns) {
8225 isds_log_message(context,
8226 _("Could not create Czech POINT deposit name space"));
8227 xmlFreeNode(request);
8228 return IE_ERROR;
8230 xmlSetNs(request, deposit_ns);
8232 /* Insert children. They are in empty namespace! */
8233 empty_ns = xmlNewNs(request, BAD_CAST "", NULL);
8234 if(!empty_ns) {
8235 isds_log_message(context, _("Could not create empty name space"));
8236 err = IE_ERROR;
8237 goto leave;
8239 INSERT_STRING_WITH_NS(request, empty_ns, "conversionID", "0");
8240 INSERT_STRING_WITH_NS(request, empty_ns, "fileName",
8241 document->dmFileDescr);
8243 /* Document encoded in Base64 */
8244 base64data = (xmlChar *) _isds_b64encode(document->data, document->data_length);
8245 if (!base64data) {
8246 isds_printf_message(context,
8247 ngettext("Not enough memory to encode %zd bytes into Base64",
8248 "Not enough memory to encode %zd bytes into Base64",
8249 document->data_length),
8250 document->data_length);
8251 err = IE_NOMEM;
8252 goto leave;
8254 INSERT_STRING_WITH_NS(request, empty_ns, "document", base64data);
8255 zfree(base64data);
8257 isds_log(ILF_ISDS, ILL_DEBUG,
8258 _("Submitting document for conversion into Czech POINT deposit"));
8260 /* Send conversion request */
8261 err = _czp_czpdeposit(context, request, &response);
8262 xmlFreeNode(request); request = NULL;
8264 if (err) {
8265 czp_do_close_connection(context);
8266 goto leave;
8270 /* Extract response */
8271 xpath_ctx = xmlXPathNewContext(response);
8272 if (!xpath_ctx) {
8273 err = IE_ERROR;
8274 goto leave;
8276 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8277 err = IE_ERROR;
8278 goto leave;
8280 result = xmlXPathEvalExpression(
8281 BAD_CAST "/deposit:saveDocumentResponse/return",
8282 xpath_ctx);
8283 if (!result) {
8284 err = IE_ERROR;
8285 goto leave;
8287 /* Empty response */
8288 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8289 isds_printf_message(context,
8290 _("Missing `return' element in Czech POINT deposit response"));
8291 err = IE_ISDS;
8292 goto leave;
8294 /* More responses */
8295 if (result->nodesetval->nodeNr > 1) {
8296 isds_printf_message(context,
8297 _("Multiple `return' element in Czech POINT deposit response"));
8298 err = IE_ISDS;
8299 goto leave;
8301 /* One response */
8302 xpath_ctx->node = result->nodesetval->nodeTab[0];
8304 /* Get status */
8305 EXTRACT_LONGINT("status", status_ptr, 1);
8306 if (status) {
8307 EXTRACT_STRING("statusMsg", string);
8308 char *string_locale = _isds_utf82locale(string);
8309 isds_printf_message(context,
8310 _("Czech POINT deposit refused document for conversion "
8311 "(code=%ld, message=%s)"),
8312 status, string_locale);
8313 free(string_locale);
8314 err = IE_ISDS;
8315 goto leave;
8318 /* Get document ID */
8319 EXTRACT_STRING("documentID", *id);
8321 /* Get submit date */
8322 EXTRACT_STRING("dateInserted", string);
8323 if (string) {
8324 *date = calloc(1, sizeof(**date));
8325 if (!*date) {
8326 err = IE_NOMEM;
8327 goto leave;
8329 err = datestring2tm((xmlChar *)string, *date);
8330 if (err) {
8331 if (err == IE_NOTSUP) {
8332 err = IE_ISDS;
8333 char *string_locale = _isds_utf82locale(string);
8334 isds_printf_message(context,
8335 _("Invalid dateInserted value: %s"), string_locale);
8336 free(string_locale);
8338 goto leave;
8342 leave:
8343 free(string);
8344 xmlXPathFreeObject(result);
8345 xmlXPathFreeContext(xpath_ctx);
8347 xmlFreeDoc(response);
8348 free(base64data);
8349 xmlFreeNode(request);
8351 if (!err) {
8352 char *id_locale = _isds_utf82locale((char *) *id);
8353 isds_log(ILF_ISDS, ILL_DEBUG,
8354 _("Document %s has been submitted for conversion "
8355 "to server successfully\n"), id_locale);
8356 free(id_locale);
8358 return err;
8362 /* Close possibly opened connection to Czech POINT document deposit.
8363 * @context is Czech POINT session context. */
8364 isds_error czp_close_connection(struct isds_ctx *context) {
8365 if (!context) return IE_INVALID_CONTEXT;
8366 zfree(context->long_message);
8367 return czp_do_close_connection(context);
8371 /* Send request for new box creation in testing ISDS instance.
8372 * It's not possible to request for a production box currently, as it
8373 * communicates via e-mail.
8374 * XXX: This function does not work either. Server complains about invalid
8375 * e-mail address.
8376 * XXX: Remove context->type hacks in isds.c and validator.c when removing
8377 * this function
8378 * @context is special session context for box creation request. DO NOT use
8379 * standard context as it could reveal your password. Use fresh new context or
8380 * context previously used by this function.
8381 * @box is box description to create including single primary user (in case of
8382 * FO box type). It outputs box ID assigned by ISDS in dbID element.
8383 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
8384 * box, or contact address of PFO box owner). The email member is mandatory as
8385 * it will be used to deliver credentials.
8386 * @former_names is optional undocumented string. Pass NULL if you don't care.
8387 * @approval is optional external approval of box manipulation
8388 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8389 * NULL, if you don't care.*/
8390 isds_error isds_request_new_testing_box(struct isds_ctx *context,
8391 struct isds_DbOwnerInfo *box, const struct isds_list *users,
8392 const char *former_names, const struct isds_approval *approval,
8393 char **refnumber) {
8394 isds_error err = IE_SUCCESS;
8395 xmlNodePtr request = NULL;
8396 xmlDocPtr response = NULL;
8397 xmlXPathContextPtr xpath_ctx = NULL;
8398 xmlXPathObjectPtr result = NULL;
8401 if (!context) return IE_INVALID_CONTEXT;
8402 zfree(context->long_message);
8403 if (!box) return IE_INVAL;
8405 if (!box->email || box->email[0] == '\0') {
8406 isds_log_message(context, _("E-mail field is mandatory"));
8407 return IE_INVAL;
8410 /* Scratch box ID */
8411 zfree(box->dbID);
8413 /* Store configuration */
8414 context->type = CTX_TYPE_TESTING_REQUEST_COLLECTOR;
8415 free(context->url);
8416 context->url = strdup("http://78.102.19.203/testbox/request_box.php");
8417 if (!(context->url))
8418 return IE_NOMEM;
8420 /* Prepare CURL handle if not yet connected */
8421 if (!context->curl) {
8422 context->curl = curl_easy_init();
8423 if (!(context->curl))
8424 return IE_ERROR;
8427 /* Build CreateDataBox request */
8428 err = build_CreateDBInput_request(context,
8429 &request, BAD_CAST "CreateDataBox",
8430 box, users, (xmlChar *) former_names, NULL, NULL, approval);
8431 if (err) goto leave;
8433 /* Send it to server and process response */
8434 err = send_destroy_request_check_response(context,
8435 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
8436 &response, (xmlChar **) refnumber);
8438 /* Extract box ID */
8439 xpath_ctx = xmlXPathNewContext(response);
8440 if (!xpath_ctx) {
8441 err = IE_ERROR;
8442 goto leave;
8444 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8445 err = IE_ERROR;
8446 goto leave;
8448 EXTRACT_STRING("/isds:CreateDataBoxResponse/dbID", box->dbID);
8450 leave:
8451 xmlXPathFreeObject(result);
8452 xmlXPathFreeContext(xpath_ctx);
8453 xmlFreeDoc(response);
8454 xmlFreeNode(request);
8456 if (!err) {
8457 isds_log(ILF_ISDS, ILL_DEBUG,
8458 _("CreateDataBox request processed by server successfully.\n"));
8461 return err;
8465 /* Submit CMS signed message to ISDS to verify its originality. This is
8466 * stronger form of isds_verify_message_hash() because ISDS does more checks
8467 * than simple one (potentialy old weak) hash comparison.
8468 * @context is session context
8469 * @message is memory with raw CMS signed message bit stream
8470 * @length is @message size in bytes
8471 * @return
8472 * IE_SUCCESS if message originates in ISDS
8473 * IE_NOTEQUAL if message is unknown to ISDS
8474 * other code for other errors */
8475 isds_error isds_authenticate_message(struct isds_ctx *context,
8476 const void *message, size_t length) {
8477 isds_error err = IE_SUCCESS;
8478 xmlNsPtr isds_ns = NULL;
8479 xmlNodePtr request = NULL;
8480 xmlDocPtr response = NULL;
8481 xmlChar *base64data = NULL;
8482 xmlChar *code = NULL, *status_message = NULL;
8483 xmlXPathContextPtr xpath_ctx = NULL;
8484 xmlXPathObjectPtr result = NULL;
8485 xmlNodePtr node;
8486 _Bool *authentic = NULL;
8488 if (!context) return IE_INVALID_CONTEXT;
8489 zfree(context->long_message);
8490 if (!message || length == 0) return IE_INVAL;
8492 /* Check if connection is established
8493 * TODO: This check should be done downstairs. */
8494 if (!context->curl) return IE_CONNECTION_CLOSED;
8497 /* Build AuthenticateMessage request */
8498 request = xmlNewNode(NULL, BAD_CAST "AuthenticateMessage");
8499 if (!request) {
8500 isds_log_message(context,
8501 _("Could not build AuthenticateMessage request"));
8502 return IE_ERROR;
8504 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8505 if(!isds_ns) {
8506 isds_log_message(context, _("Could not create ISDS name space"));
8507 xmlFreeNode(request);
8508 return IE_ERROR;
8510 xmlSetNs(request, isds_ns);
8512 /* Insert Base64 encoded message */
8513 base64data = (xmlChar *) _isds_b64encode(message, length);
8514 if (!base64data) {
8515 isds_printf_message(context,
8516 ngettext("Not enough memory to encode %zd bytes into Base64",
8517 "Not enough memory to encode %zd bytes into Base64",
8518 length),
8519 length);
8520 err = IE_NOMEM;
8521 goto leave;
8523 INSERT_STRING(request, "dmMessage", base64data);
8524 zfree(base64data);
8527 isds_log(ILF_ISDS, ILL_DEBUG,
8528 _("Sending AuthenticateMessage request to ISDS\n"));
8530 /* Sent request */
8531 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
8533 /* Destroy request */
8534 xmlFreeNode(request); request = NULL;
8536 if (err) {
8537 isds_log(ILF_ISDS, ILL_DEBUG,
8538 _("Processing ISDS response on AuthenticateMessage "
8539 "request failed\n"));
8540 goto leave;
8543 /* Check for response status */
8544 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
8545 &code, &status_message, NULL);
8546 if (err) {
8547 isds_log(ILF_ISDS, ILL_DEBUG,
8548 _("ISDS response on AuthenticateMessage request is missing "
8549 "status\n"));
8550 goto leave;
8553 /* Error in ISDS */
8554 if (xmlStrcmp(code, BAD_CAST "0000")) {
8555 char *code_locale = _isds_utf82locale((char*)code);
8556 char *message_locale = _isds_utf82locale((char*)status_message);
8557 isds_log(ILF_ISDS, ILL_DEBUG,
8558 _("Server could not decide authenticity of submitted message "
8559 "(code=%s, message=%s)\n"),
8560 code_locale, message_locale);
8561 isds_log_message(context, message_locale);
8562 free(code_locale);
8563 free(message_locale);
8564 err = IE_ISDS;
8565 goto leave;
8569 /* Otherwise ISDS has decided */
8570 xpath_ctx = xmlXPathNewContext(response);
8571 if (!xpath_ctx) {
8572 err = IE_ERROR;
8573 goto leave;
8575 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8576 err = IE_ERROR;
8577 goto leave;
8579 result = xmlXPathEvalExpression(
8580 BAD_CAST "/isds:AuthenticateMessageResponse",
8581 xpath_ctx);
8582 if (!result) {
8583 err = IE_ERROR;
8584 goto leave;
8586 /* Empty response */
8587 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8588 isds_log_message(context,
8589 _("Server did not return any response on "
8590 "AuthenticateMessage request"));
8591 err = IE_ISDS;
8592 goto leave;
8594 /* More responses */
8595 if (result->nodesetval->nodeNr > 1) {
8596 isds_log_message(context,
8597 _("Server did return more responses on "
8598 "AuthenticateMessage request"));
8599 err = IE_ISDS;
8600 goto leave;
8602 /* One response */
8603 xpath_ctx->node = result->nodesetval->nodeTab[0];
8604 EXTRACT_BOOLEAN("dmAuthResult", authentic);
8606 if (!authentic) {
8607 isds_log_message(context,
8608 _("Server did not return any response on "
8609 "AuthenticateMessage request"));
8610 err = IE_ISDS;
8611 goto leave;
8613 if (*authentic) {
8614 isds_log(ILF_ISDS, ILL_DEBUG,
8615 _("ISDS authenticated the message successfully\n"));
8617 } else {
8618 isds_log_message(context, _("ISDS does not know the message"));
8619 err = IE_NOTEQUAL;
8623 leave:
8624 free(authentic);
8625 xmlXPathFreeObject(result);
8626 xmlXPathFreeContext(xpath_ctx);
8628 free(code);
8629 free(status_message);
8630 free(base64data);
8631 xmlFreeDoc(response);
8632 xmlFreeNode(request);
8634 return err;
8637 #undef INSERT_ELEMENT
8638 #undef CHECK_FOR_STRING_LENGTH
8639 #undef INSERT_STRING_ATTRIBUTE
8640 #undef INSERT_ULONGINTNOPTR
8641 #undef INSERT_ULONGINT
8642 #undef INSERT_LONGINT
8643 #undef INSERT_BOOLEAN
8644 #undef INSERT_SCALAR_BOOLEAN
8645 #undef INSERT_STRING
8646 #undef INSERT_STRING_WITH_NS
8647 #undef EXTRACT_STRING_ATTRIBUTE
8648 #undef EXTRACT_ULONGINT
8649 #undef EXTRACT_LONGINT
8650 #undef EXTRACT_BOOLEAN
8651 #undef EXTRACT_STRING
8654 /* Compute hash of message from raw representation and store it into envelope.
8655 * Original hash structure will be destroyed in envelope.
8656 * @context is session context
8657 * @message is message carrying raw XML message blob
8658 * @algorithm is desired hash algorithm to use */
8659 isds_error isds_compute_message_hash(struct isds_ctx *context,
8660 struct isds_message *message, const isds_hash_algorithm algorithm) {
8661 isds_error err = IE_SUCCESS;
8662 const char *nsuri;
8663 void *xml_stream = NULL;
8664 size_t xml_stream_length;
8665 size_t phys_start, phys_end;
8666 char *phys_path = NULL;
8667 struct isds_hash *new_hash = NULL;
8670 if (!context) return IE_INVALID_CONTEXT;
8671 zfree(context->long_message);
8672 if (!message) return IE_INVAL;
8674 if (!message->raw) {
8675 isds_log_message(context,
8676 _("Message does not carry raw representation"));
8677 return IE_INVAL;
8680 switch (message->raw_type) {
8681 case RAWTYPE_INCOMING_MESSAGE:
8682 nsuri = ISDS_NS;
8683 xml_stream = message->raw;
8684 xml_stream_length = message->raw_length;
8685 break;
8687 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
8688 nsuri = SISDS_INCOMING_NS;
8689 xml_stream = message->raw;
8690 xml_stream_length = message->raw_length;
8691 break;
8693 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
8694 nsuri = SISDS_INCOMING_NS;
8695 err = _isds_extract_cms_data(context,
8696 message->raw, message->raw_length,
8697 &xml_stream, &xml_stream_length);
8698 if (err) goto leave;
8699 break;
8701 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
8702 nsuri = SISDS_OUTGOING_NS;
8703 xml_stream = message->raw;
8704 xml_stream_length = message->raw_length;
8705 break;
8707 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
8708 nsuri = SISDS_OUTGOING_NS;
8709 err = _isds_extract_cms_data(context,
8710 message->raw, message->raw_length,
8711 &xml_stream, &xml_stream_length);
8712 if (err) goto leave;
8713 break;
8715 default:
8716 isds_log_message(context, _("Bad raw representation type"));
8717 return IE_INVAL;
8718 break;
8722 /* XXX: Hash is computed from original string representing isds:dmDm
8723 * subtree. That means no encoding, white space, xmlns attributes changes.
8724 * In other words, input for hash can be invalid XML stream. */
8725 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
8726 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
8727 PHYSXML_ELEMENT_SEPARATOR,
8728 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
8729 PHYSXML_ELEMENT_SEPARATOR
8730 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
8731 err = IE_NOMEM;
8732 goto leave;
8734 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
8735 phys_path, &phys_start, &phys_end);
8736 zfree(phys_path);
8737 if (err) {
8738 isds_log_message(context,
8739 _("Substring with isds:dmDM element could not be located "
8740 "in raw message"));
8741 goto leave;
8745 /* Compute hash */
8746 new_hash = calloc(1, sizeof(*new_hash));
8747 if (!new_hash) {
8748 err = IE_NOMEM;
8749 goto leave;
8751 new_hash->algorithm = algorithm;
8752 err = _isds_compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
8753 new_hash);
8754 if (err) {
8755 isds_log_message(context, _("Could not compute message hash"));
8756 goto leave;
8759 /* Save computed hash */
8760 if (!message->envelope) {
8761 message->envelope = calloc(1, sizeof(*message->envelope));
8762 if (!message->envelope) {
8763 err = IE_NOMEM;
8764 goto leave;
8767 isds_hash_free(&message->envelope->hash);
8768 message->envelope->hash = new_hash;
8770 leave:
8771 if (err) {
8772 isds_hash_free(&new_hash);
8775 free(phys_path);
8776 if (xml_stream != message->raw) free(xml_stream);
8777 return err;
8781 /* Compare two hashes.
8782 * @h1 is first hash
8783 * @h2 is another hash
8784 * @return
8785 * IE_SUCCESS if hashes equal
8786 * IE_NOTUNIQ if hashes are comparable, but they don't equal
8787 * IE_ENUM if not comparable, but both structures defined
8788 * IE_INVAL if some of the structures are undefined (NULL)
8789 * IE_ERROR if internal error occurs */
8790 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
8791 if (h1 == NULL || h2 == NULL) return IE_INVAL;
8792 if (h1->algorithm != h2->algorithm) return IE_ENUM;
8793 if (h1->length != h2->length) return IE_ERROR;
8794 if (h1->length > 0 && !h1->value) return IE_ERROR;
8795 if (h2->length > 0 && !h2->value) return IE_ERROR;
8797 for (int i = 0; i < h1->length; i++) {
8798 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
8799 return IE_NOTEQUAL;
8801 return IE_SUCCESS;
8805 /* Check message has gone through ISDS by comparing message hash stored in
8806 * ISDS and locally computed hash. You must provide message with valid raw
8807 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
8808 * This is convenient wrapper for isds_download_message_hash(),
8809 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
8810 * @context is session context
8811 * @message is message with valid raw and envelope member; envelope->hash
8812 * member will be changed during function run. Use envelope on heap only.
8813 * @return
8814 * IE_SUCCESS if message originates in ISDS
8815 * IE_NOTEQUAL if message is unknown to ISDS
8816 * other code for other errors */
8817 isds_error isds_verify_message_hash(struct isds_ctx *context,
8818 struct isds_message *message) {
8819 isds_error err = IE_SUCCESS;
8820 struct isds_hash *downloaded_hash = NULL;
8822 if (!context) return IE_INVALID_CONTEXT;
8823 zfree(context->long_message);
8824 if (!message) return IE_INVAL;
8826 if (!message->envelope) {
8827 isds_log_message(context,
8828 _("Given message structure is missing envelope"));
8829 return IE_INVAL;
8831 if (!message->raw) {
8832 isds_log_message(context,
8833 _("Given message structure is missing raw representation"));
8834 return IE_INVAL;
8837 err = isds_download_message_hash(context, message->envelope->dmID,
8838 &downloaded_hash);
8839 if (err) goto leave;
8841 err = isds_compute_message_hash(context, message,
8842 downloaded_hash->algorithm);
8843 if (err) goto leave;
8845 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
8847 leave:
8848 isds_hash_free(&downloaded_hash);
8849 return err;
8853 /* Search for document by document ID in list of documents. IDs are compared
8854 * as UTF-8 string.
8855 * @documents is list of isds_documents
8856 * @id is document identifier
8857 * @return first matching document or NULL. */
8858 const struct isds_document *isds_find_document_by_id(
8859 const struct isds_list *documents, const char *id) {
8860 const struct isds_list *item;
8861 const struct isds_document *document;
8863 for (item = documents; item; item = item->next) {
8864 document = (struct isds_document *) item->data;
8865 if (!document) continue;
8867 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
8868 return document;
8871 return NULL;
8875 /* Normalize @mime_type to be proper MIME type.
8876 * ISDS servers passes invalid MIME types (e.g. "pdf"). This function tries to
8877 * guess regular MIME type (e.g. "application/pdf").
8878 * @mime_type is UTF-8 encoded MIME type to fix
8879 * @return original @mime_type if no better interpretation exists, or array to
8880 * constant static UTF-8 encoded string with proper MIME type. */
8881 char *isds_normalize_mime_type(const char* mime_type) {
8882 if (!mime_type) return NULL;
8884 for (int offset = 0;
8885 offset < sizeof(extension_map_mime)/sizeof(extension_map_mime[0]);
8886 offset += 2) {
8887 if (!xmlStrcmp((const xmlChar*) mime_type, extension_map_mime[offset]))
8888 return (char *) extension_map_mime[offset + 1];
8891 return (char *) mime_type;
8895 /* Switch MIME type normalization while message loading. Default state for new
8896 * context is no normalization.
8897 * @normalize use true to switch normalization on, false to switch off */
8898 isds_error isds_set_mime_type_normalization(struct isds_ctx *context,
8899 _Bool normalize) {
8900 if (!context) return IE_INVALID_CONTEXT;
8901 zfree(context->long_message);
8903 context->normalize_mime_type = normalize;
8904 isds_log(ILF_FILE, ILL_INFO, (context->normalize_mime_type) ?
8905 _("MIME type normalization switched on\n") :
8906 _("MIME type normalization switched off\n"));
8907 return IE_SUCCESS;
8911 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
8912 struct isds_message **message);
8913 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
8914 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
8915 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
8916 struct isds_address **address);
8918 int isds_message_free(struct isds_message **message);
8919 int isds_address_free(struct isds_address **address);
8923 /* Makes known all relevant namespaces to given XPath context
8924 * @xpath_ctx is XPath context
8925 * @message_ns selects proper message name space. Unsigned and signed
8926 * messages and delivery info's differ in prefix and URI. */
8927 _hidden isds_error _isds_register_namespaces(xmlXPathContextPtr xpath_ctx,
8928 const message_ns_type message_ns) {
8929 const xmlChar *message_namespace = NULL;
8931 if (!xpath_ctx) return IE_ERROR;
8933 switch(message_ns) {
8934 case MESSAGE_NS_1:
8935 message_namespace = BAD_CAST ISDS1_NS; break;
8936 case MESSAGE_NS_UNSIGNED:
8937 message_namespace = BAD_CAST ISDS_NS; break;
8938 case MESSAGE_NS_SIGNED_INCOMING:
8939 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
8940 case MESSAGE_NS_SIGNED_OUTGOING:
8941 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
8942 case MESSAGE_NS_SIGNED_DELIVERY:
8943 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
8944 default:
8945 return IE_ENUM;
8948 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
8949 return IE_ERROR;
8950 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
8951 return IE_ERROR;
8952 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
8953 return IE_ERROR;
8954 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
8955 return IE_ERROR;
8956 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
8957 return IE_ERROR;
8958 return IE_SUCCESS;