Merge branch 'xmldoc'
[libisds.git] / src / isds.c
blobec8663d9fc6271f44d330b81eea37f60fff85c4a
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));
255 xmlFreeDoc((*message)->xml); (*message)->xml = NULL;
257 free(*message);
258 *message = NULL;
262 /* Deallocate struct isds_document recursively and NULL it */
263 void isds_document_free(struct isds_document **document) {
264 if (!document || !*document) return;
266 if (!(*document)->is_xml) {
267 free((*document)->data);
269 free((*document)->dmMimeType);
270 free((*document)->dmFileGuid);
271 free((*document)->dmUpFileGuid);
272 free((*document)->dmFileDescr);
273 free((*document)->dmFormat);
275 free(*document);
276 *document = NULL;
280 /* Deallocate struct isds_message_copy recursively and NULL it */
281 void isds_message_copy_free(struct isds_message_copy **copy) {
282 if (!copy || !*copy) return;
284 free((*copy)->dbIDRecipient);
285 free((*copy)->dmRecipientOrgUnit);
286 free((*copy)->dmRecipientOrgUnitNum);
287 free((*copy)->dmToHands);
289 free((*copy)->dmStatus);
290 free((*copy)->dmID);
292 zfree(*copy);
296 /* Deallocate struct isds_approval recursively and NULL it */
297 void isds_approval_free(struct isds_approval **approval) {
298 if (!approval || !*approval) return;
300 free((*approval)->refference);
302 zfree(*approval);
306 /* *DUP_OR_ERROR macros needs error label */
307 #define STRDUP_OR_ERROR(new, template) { \
308 if (!template) { \
309 (new) = NULL; \
310 } else { \
311 (new) = strdup(template); \
312 if (!new) goto error; \
316 #define FLATDUP_OR_ERROR(new, template) { \
317 if (!template) { \
318 (new) = NULL; \
319 } else { \
320 (new) = malloc(sizeof(*(new))); \
321 if (!new) goto error; \
322 memcpy((new), (template), sizeof(*(template))); \
326 /* Copy structure isds_pki_credentials recursively. */
327 struct isds_pki_credentials *isds_pki_credentials_duplicate(
328 const struct isds_pki_credentials *template) {
329 struct isds_pki_credentials *new = NULL;
331 if(!template) return NULL;
333 new = calloc(1, sizeof(*new));
334 if (!new) return NULL;
336 STRDUP_OR_ERROR(new->engine, template->engine);
337 new->certificate_format = template->certificate_format;
338 STRDUP_OR_ERROR(new->certificate, template->certificate);
339 new->key_format = template->key_format;
340 STRDUP_OR_ERROR(new->key, template->key);
341 STRDUP_OR_ERROR(new->passphrase, template->passphrase);
343 return new;
345 error:
346 isds_pki_credentials_free(&new);
347 return NULL;
351 /* Copy structure isds_PersonName recursively */
352 struct isds_PersonName *isds_PersonName_duplicate(
353 const struct isds_PersonName *template) {
354 struct isds_PersonName *new = NULL;
356 if (!template) return NULL;
358 new = calloc(1, sizeof(*new));
359 if (!new) return NULL;
361 STRDUP_OR_ERROR(new->pnFirstName, template->pnFirstName);
362 STRDUP_OR_ERROR(new->pnMiddleName, template->pnMiddleName);
363 STRDUP_OR_ERROR(new->pnLastName, template->pnLastName);
364 STRDUP_OR_ERROR(new->pnLastNameAtBirth, template->pnLastNameAtBirth);
366 return new;
368 error:
369 isds_PersonName_free(&new);
370 return NULL;
374 /* Copy structure isds_BirthInfo recursively */
375 static struct isds_BirthInfo *isds_BirthInfo_duplicate(
376 const struct isds_BirthInfo *template) {
377 struct isds_BirthInfo *new = NULL;
379 if (!template) return NULL;
381 new = calloc(1, sizeof(*new));
382 if (!new) return NULL;
384 FLATDUP_OR_ERROR(new->biDate, template->biDate);
385 STRDUP_OR_ERROR(new->biCity, template->biCity);
386 STRDUP_OR_ERROR(new->biCounty, template->biCounty);
387 STRDUP_OR_ERROR(new->biState, template->biState);
389 return new;
391 error:
392 isds_BirthInfo_free(&new);
393 return NULL;
397 /* Copy structure isds_Address recursively */
398 struct isds_Address *isds_Address_duplicate(
399 const struct isds_Address *template) {
400 struct isds_Address *new = NULL;
402 if (!template) return NULL;
404 new = calloc(1, sizeof(*new));
405 if (!new) return NULL;
407 STRDUP_OR_ERROR(new->adCity, template->adCity);
408 STRDUP_OR_ERROR(new->adStreet, template->adStreet);
409 STRDUP_OR_ERROR(new->adNumberInStreet, template->adNumberInStreet);
410 STRDUP_OR_ERROR(new->adNumberInMunicipality,
411 template->adNumberInMunicipality);
412 STRDUP_OR_ERROR(new->adZipCode, template->adZipCode);
413 STRDUP_OR_ERROR(new->adState, template->adState);
415 return new;
417 error:
418 isds_Address_free(&new);
419 return NULL;
423 /* Copy structure isds_DbOwnerInfo recursively */
424 struct isds_DbOwnerInfo *isds_DbOwnerInfo_duplicate(
425 const struct isds_DbOwnerInfo *template) {
426 struct isds_DbOwnerInfo *new = NULL;
427 if (!template) return NULL;
429 new = calloc(1, sizeof(*new));
430 if (!new) return NULL;
432 STRDUP_OR_ERROR(new->dbID, template->dbID);
433 FLATDUP_OR_ERROR(new->dbType, template->dbType);
434 STRDUP_OR_ERROR(new->ic, template->ic);
436 if (template->personName) {
437 if (!(new->personName =
438 isds_PersonName_duplicate(template->personName)))
439 goto error;
442 STRDUP_OR_ERROR(new->firmName, template->firmName);
444 if (template->birthInfo) {
445 if (!(new->birthInfo =
446 isds_BirthInfo_duplicate(template->birthInfo)))
447 goto error;
450 if (template->address) {
451 if (!(new->address = isds_Address_duplicate(template->address)))
452 goto error;
455 STRDUP_OR_ERROR(new->nationality, template->nationality);
456 STRDUP_OR_ERROR(new->email, template->email);
457 STRDUP_OR_ERROR(new->telNumber, template->telNumber);
458 STRDUP_OR_ERROR(new->identifier, template->identifier);
459 STRDUP_OR_ERROR(new->registryCode, template->registryCode);
460 FLATDUP_OR_ERROR(new->dbState, template->dbState);
461 FLATDUP_OR_ERROR(new->dbEffectiveOVM, template->dbEffectiveOVM);
462 FLATDUP_OR_ERROR(new->dbOpenAddressing, template->dbOpenAddressing);
464 return new;
466 error:
467 isds_DbOwnerInfo_free(&new);
468 return NULL;
472 /* Copy structure isds_DbUserInfo recursively */
473 struct isds_DbUserInfo *isds_DbUserInfo_duplicate(
474 const struct isds_DbUserInfo *template) {
475 struct isds_DbUserInfo *new = NULL;
476 if (!template) return NULL;
478 new = calloc(1, sizeof(*new));
479 if (!new) return NULL;
481 STRDUP_OR_ERROR(new->userID, template->userID);
482 FLATDUP_OR_ERROR(new->userType, template->userType);
483 FLATDUP_OR_ERROR(new->userPrivils, template->userPrivils);
485 if (template->personName) {
486 if (!(new->personName =
487 isds_PersonName_duplicate(template->personName)))
488 goto error;
491 if (template->address) {
492 if (!(new->address = isds_Address_duplicate(template->address)))
493 goto error;
496 FLATDUP_OR_ERROR(new->biDate, template->biDate);
497 STRDUP_OR_ERROR(new->ic, template->ic);
498 STRDUP_OR_ERROR(new->firmName, template->firmName);
499 STRDUP_OR_ERROR(new->caStreet, template->caStreet);
500 STRDUP_OR_ERROR(new->caCity, template->caCity);
501 STRDUP_OR_ERROR(new->caZipCode, template->caZipCode);
502 STRDUP_OR_ERROR(new->caState, template->caState);
504 return new;
506 error:
507 isds_DbUserInfo_free(&new);
508 return NULL;
511 #undef FLATDUP_OR_ERROR
512 #undef STRDUP_OR_ERROR
515 /* Initialize ISDS library.
516 * Global function, must be called before other functions.
517 * If it fails you can not use ISDS library and must call isds_cleanup() to
518 * free partially initialized global variables. */
519 isds_error isds_init(void) {
520 /* NULL global variables */
521 log_facilities = ILF_ALL;
522 log_level = ILL_WARNING;
523 log_callback = NULL;
524 log_callback_data = NULL;
526 #if ENABLE_NLS
527 /* Initialize gettext */
528 bindtextdomain(PACKAGE, LOCALEDIR);
529 #endif
531 /* Initialize CURL */
532 if (curl_global_init(CURL_GLOBAL_ALL)) {
533 isds_log(ILF_ISDS, ILL_CRIT, _("CURL library initialization failed\n"));
534 return IE_ERROR;
537 /* Initialize gpg-error because of gpgme and ksba */
538 if (gpg_err_init()) {
539 isds_log(ILF_ISDS, ILL_CRIT,
540 _("gpg-error library initialization failed\n"));
541 return IE_ERROR;
544 /* Initialize GPGME */
545 if (_isds_init_gpgme(&version_gpgme)) {
546 isds_log(ILF_ISDS, ILL_CRIT,
547 _("GPGME library initialization failed\n"));
548 return IE_ERROR;
551 /* Initialize gcrypt */
552 if (_isds_init_gcrypt(&version_gcrypt)) {
553 isds_log(ILF_ISDS, ILL_CRIT,
554 _("gcrypt library initialization failed\n"));
555 return IE_ERROR;
558 /* This can _exit() current program. Find not so assertive check. */
559 LIBXML_TEST_VERSION;
561 /* Check expat */
562 if (_isds_init_expat(&version_expat)) {
563 isds_log(ILF_ISDS, ILL_CRIT,
564 _("expat library initialization failed\n"));
565 return IE_ERROR;
568 /* Allocate global variables */
571 return IE_SUCCESS;
575 /* Deinitialize ISDS library.
576 * Global function, must be called as last library function. */
577 isds_error isds_cleanup(void) {
578 /* XML */
579 xmlCleanupParser();
581 /* Curl */
582 curl_global_cleanup();
584 return IE_SUCCESS;
588 /* Return version string of this library. Version of dependencies can be
589 * embedded. Do no try to parse it. You must free it. */
590 char *isds_version(void) {
591 char *buffer = NULL;
593 isds_asprintf(&buffer, _("%s (%s, GPGME %s, gcrypt %s, %s, libxml2 %s)"),
594 PACKAGE_VERSION, curl_version(), version_gpgme, version_gcrypt,
595 version_expat, xmlParserVersion);
596 return buffer;
600 /* Return text description of ISDS error */
601 const char *isds_strerror(const isds_error error) {
602 switch (error) {
603 case IE_SUCCESS:
604 return(_("Success")); break;
605 case IE_ERROR:
606 return(_("Unspecified error")); break;
607 case IE_NOTSUP:
608 return(_("Not supported")); break;
609 case IE_INVAL:
610 return(_("Invalid value")); break;
611 case IE_INVALID_CONTEXT:
612 return(_("Invalid context")); break;
613 case IE_NOT_LOGGED_IN:
614 return(_("Not logged in")); break;
615 case IE_CONNECTION_CLOSED:
616 return(_("Connection closed")); break;
617 case IE_TIMED_OUT:
618 return(_("Timed out")); break;
619 case IE_NOEXIST:
620 return(_("Not exist")); break;
621 case IE_NOMEM:
622 return(_("Out of memory")); break;
623 case IE_NETWORK:
624 return(_("Network problem")); break;
625 case IE_HTTP:
626 return(_("HTTP problem")); break;
627 case IE_SOAP:
628 return(_("SOAP problem")); break;
629 case IE_XML:
630 return(_("XML problem")); break;
631 case IE_ISDS:
632 return(_("ISDS server problem")); break;
633 case IE_ENUM:
634 return(_("Invalid enum value")); break;
635 case IE_DATE:
636 return(_("Invalid date value")); break;
637 case IE_2BIG:
638 return(_("Too big")); break;
639 case IE_2SMALL:
640 return(_("Too small")); break;
641 case IE_NOTUNIQ:
642 return(_("Value not unique")); break;
643 case IE_NOTEQUAL:
644 return(_("Values not equal")); break;
645 case IE_PARTIAL_SUCCESS:
646 return(_("Some suboperations failed")); break;
647 case IE_ABORTED:
648 return(_("Operation aborted")); break;
649 default:
650 return(_("Unknown error"));
655 /* Create ISDS context.
656 * Each context can be used for different sessions to (possibly) different
657 * ISDS server with different credentials. */
658 struct isds_ctx *isds_ctx_create(void) {
659 struct isds_ctx *context;
660 context = malloc(sizeof(*context));
661 if (context) memset(context, 0, sizeof(*context));
662 return context;
666 /* Close possibly opened connection to Czech POINT document deposit without
667 * resetting long_message buffer.
668 * XXX: Do not use czp_close_connection() if you do not want to destroy log
669 * message.
670 * @context is Czech POINT session context. */
671 static isds_error czp_do_close_connection(struct isds_ctx *context) {
672 if (!context) return IE_INVALID_CONTEXT;
673 _isds_close_connection(context);
674 return IE_SUCCESS;
678 /* Discard credentials.
679 * Only that. It does not cause log out, connection close or similar. */
680 static isds_error discard_credentials(struct isds_ctx *context) {
681 if(!context) return IE_INVALID_CONTEXT;
683 if (context->username) {
684 memset(context->username, 0, strlen(context->username));
685 zfree(context->username);
687 if (context->password) {
688 memset(context->password, 0, strlen(context->password));
689 zfree(context->password);
691 isds_pki_credentials_free(&context->pki_credentials);
693 return IE_SUCCESS;
697 /* Destroy ISDS context and free memory.
698 * @context will be NULLed on success. */
699 isds_error isds_ctx_free(struct isds_ctx **context) {
700 if (!context || !*context) {
701 return IE_INVALID_CONTEXT;
704 /* Discard credentials and close connection */
705 switch ((*context)->type) {
706 case CTX_TYPE_NONE: break;
707 case CTX_TYPE_ISDS: isds_logout(*context); break;
708 case CTX_TYPE_CZP:
709 case CTX_TYPE_TESTING_REQUEST_COLLECTOR:
710 czp_do_close_connection(*context); break;
713 /* For sure */
714 discard_credentials(*context);
716 /* Free other structures */
717 free((*context)->tls_verify_server);
718 free((*context)->tls_ca_file);
719 free((*context)->tls_ca_dir);
720 free((*context)->tls_crl_file);
721 free((*context)->long_message);
723 free(*context);
724 *context = NULL;
725 return IE_SUCCESS;
729 /* Return long message text produced by library function, e.g. detailed error
730 * message. Returned pointer is only valid until new library function is
731 * called for the same context. Could be NULL, especially if NULL context is
732 * supplied. Return string is locale encoded. */
733 char *isds_long_message(const struct isds_ctx *context) {
734 if (!context) return NULL;
735 return context->long_message;
739 /* Stores message into context' long_message buffer.
740 * Application can pick the message up using isds_long_message().
741 * NULL @message truncates the buffer but does not deallocate it.
742 * @message is coded in locale encoding */
743 _hidden isds_error isds_log_message(struct isds_ctx *context,
744 const char *message) {
745 char *buffer;
746 size_t length;
748 if (!context) return IE_INVALID_CONTEXT;
750 /* FIXME: Check for integer overflow */
751 length = 1 + ((message) ? strlen(message) : 0);
752 buffer = realloc(context->long_message, length);
753 if (!buffer) return IE_NOMEM;
755 if (message)
756 strcpy(buffer, message);
757 else
758 *buffer = '\0';
760 context->long_message = buffer;
761 return IE_SUCCESS;
765 /* Appends message into context' long_message buffer.
766 * Application can pick the message up using isds_long_message().
767 * NULL message has void effect. */
768 _hidden isds_error isds_append_message(struct isds_ctx *context,
769 const char *message) {
770 char *buffer;
771 size_t old_length, length;
773 if (!context) return IE_INVALID_CONTEXT;
774 if (!message) return IE_SUCCESS;
775 if (!context->long_message)
776 return isds_log_message(context, message);
778 old_length = strlen(context->long_message);
779 /* FIXME: Check for integer overflow */
780 length = 1 + old_length + strlen(message);
781 buffer = realloc(context->long_message, length);
782 if (!buffer) return IE_NOMEM;
784 strcpy(buffer + old_length, message);
786 context->long_message = buffer;
787 return IE_SUCCESS;
791 /* Stores formatted message into context' long_message buffer.
792 * Application can pick the message up using isds_long_message(). */
793 _hidden isds_error isds_printf_message(struct isds_ctx *context,
794 const char *format, ...) {
795 va_list ap;
796 int length;
798 if (!context) return IE_INVALID_CONTEXT;
799 va_start(ap, format);
800 length = isds_vasprintf(&(context->long_message), format, ap);
801 va_end(ap);
803 return (length < 0) ? IE_ERROR: IE_SUCCESS;
807 /* Set logging up.
808 * @facilities is bit mask of isds_log_facility values,
809 * @level is verbosity level. */
810 void isds_set_logging(const unsigned int facilities,
811 const isds_log_level level) {
812 log_facilities = facilities;
813 log_level = level;
817 /* Register callback function libisds calls when new global log message is
818 * produced by library. Library logs to stderr by default.
819 * @callback is function provided by application libisds will call. See type
820 * definition for @callback argument explanation. Pass NULL to revert logging to
821 * default behaviour.
822 * @data is application specific data @callback gets as last argument */
823 void isds_set_log_callback(isds_log_callback callback, void *data) {
824 log_callback = callback;
825 log_callback_data = data;
829 /* Log @message in class @facility with log @level into global log. @message
830 * is printf(3) formatting string, variadic arguments may be necessary.
831 * For debugging purposes. */
832 _hidden isds_error isds_log(const isds_log_facility facility,
833 const isds_log_level level, const char *message, ...) {
834 va_list ap;
835 char *buffer = NULL;
836 int length;
838 if (level > log_level) return IE_SUCCESS;
839 if (!(log_facilities & facility)) return IE_SUCCESS;
840 if (!message) return IE_INVAL;
842 if (log_callback) {
843 /* Pass message to application supplied callback function */
844 va_start(ap, message);
845 length = isds_vasprintf(&buffer, message, ap);
846 va_end(ap);
848 if (length == -1) {
849 return IE_ERROR;
851 if (length > 0) {
852 log_callback(facility, level, buffer, length, log_callback_data);
854 free(buffer);
855 } else {
856 /* Default: Log it to stderr */
857 va_start(ap, message);
858 vfprintf(stderr, message, ap);
859 va_end(ap);
860 /* Line buffered printf is default.
861 * fflush(stderr);*/
864 return IE_SUCCESS;
868 /* Set timeout in milliseconds for each network job like connecting to server
869 * or sending message. Use 0 to disable timeout limits. */
870 isds_error isds_set_timeout(struct isds_ctx *context,
871 const unsigned int timeout) {
872 if (!context) return IE_INVALID_CONTEXT;
873 zfree(context->long_message);
875 context->timeout = timeout;
877 if (context->curl) {
878 CURLcode curl_err;
880 curl_err = curl_easy_setopt(context->curl, CURLOPT_NOSIGNAL, 1);
881 if (!curl_err)
882 #if HAVE_DECL_CURLOPT_TIMEOUT_MS /* Since curl-7.16.2 */
883 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT_MS,
884 context->timeout);
885 #else
886 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT,
887 context->timeout / 1000);
888 #endif /* not HAVE_DECL_CURLOPT_TIMEOUT_MS */
889 if (curl_err) return IE_ERROR;
892 return IE_SUCCESS;
896 /* Register callback function libisds calls periodically during HTTP data
897 * transfer.
898 * @context is session context
899 * @callback is function provided by application libisds will call. See type
900 * definition for @callback argument explanation.
901 * @data is application specific data @callback gets as last argument */
902 isds_error isds_set_progress_callback(struct isds_ctx *context,
903 isds_progress_callback callback, void *data) {
904 if (!context) return IE_INVALID_CONTEXT;
905 zfree(context->long_message);
907 context->progress_callback = callback;
908 context->progress_callback_data = data;
910 return IE_SUCCESS;
914 /* Change context settings.
915 * @context is context which setting will be applied to
916 * @option is name of option. It determines the type of last argument. See
917 * isds_option definition for more info.
918 * @... is value of new setting. Type is determined by @option
919 * */
920 isds_error isds_set_opt(struct isds_ctx *context, const isds_option option,
921 ...) {
922 isds_error err = IE_SUCCESS;
923 va_list ap;
924 char *pointer, *string;
926 if (!context) return IE_INVALID_CONTEXT;
927 zfree(context->long_message);
929 va_start(ap, option);
931 #define REPLACE_VA_BOOLEAN(destination) { \
932 if (!(destination)) { \
933 (destination) = malloc(sizeof(*(destination))); \
934 if (!(destination)) { \
935 err = IE_NOMEM; goto leave; \
938 *(destination) = (_Bool) !!va_arg(ap, int); \
941 #define REPLACE_VA_STRING(destination) { \
942 string = va_arg(ap, char *); \
943 if (string) { \
944 pointer = realloc((destination), 1 + strlen(string)); \
945 if (!pointer) { err = IE_NOMEM; goto leave; } \
946 strcpy(pointer, string); \
947 (destination) = pointer; \
948 } else { \
949 free(destination); \
950 (destination) = NULL; \
954 switch (option) {
955 case IOPT_TLS_VERIFY_SERVER:
956 REPLACE_VA_BOOLEAN(context->tls_verify_server);
957 break;
958 case IOPT_TLS_CA_FILE:
959 REPLACE_VA_STRING(context->tls_ca_file);
960 break;
961 case IOPT_TLS_CA_DIRECTORY:
962 REPLACE_VA_STRING(context->tls_ca_dir);
963 break;
964 case IOPT_TLS_CRL_FILE:
965 #if HAVE_DECL_CURLOPT_CRLFILE /* Since curl-7.19.0 */
966 REPLACE_VA_STRING(context->tls_crl_file);
967 #else
968 isds_log_message(context,
969 _("Curl library does not support CRL definition"));
970 err = IE_NOTSUP;
971 #endif /* not HAVE_DECL_CURLOPT_CRLFILE */
972 break;
973 case IOPT_NORMALIZE_MIME_TYPE:
974 context->normalize_mime_type = (_Bool) !!va_arg(ap, int);
975 break;
977 default:
978 err = IE_ENUM; goto leave;
981 #undef REPLACE_VA_STRING
982 #undef REPLACE_VA_BOOLEAN
984 leave:
985 va_end(ap);
986 return err;
990 /* Deprecated: Use isds_set_opt() instead.
991 * Change SSL/TLS settings.
992 * @context is context which setting will be applied to
993 * @option is name of option. It determines the type of last argument. See
994 * isds_tls_option definition for more info.
995 * @... is value of new setting. Type is determined by @option
996 * */
997 isds_error isds_set_tls(struct isds_ctx *context, const isds_tls_option option,
998 ...) {
999 isds_error err = IE_ENUM;
1000 va_list ap;
1002 va_start(ap, option);
1004 switch (option) {
1005 case ITLS_VERIFY_SERVER:
1006 err = isds_set_opt(context, option, va_arg(ap, int));
1007 break;
1008 case ITLS_CA_FILE:
1009 case ITLS_CA_DIRECTORY:
1010 case ITLS_CRL_FILE:
1011 err = isds_set_opt(context, option, va_arg(ap, char*));
1014 va_end(ap);
1015 return err;
1019 /* Connect and log in into ISDS server.
1020 * All required arguments will be copied, you do not have to keep them after
1021 * that.
1022 * ISDS supports four different authentication methods. Exact method is
1023 * selected on @username, @passwors and @pki_credentials arguments:
1024 * - If @pki_credentials == NULL, @username and @password must be supplied
1025 * - If @pki_credentials != NULL, then
1026 * - If @username == NULL, only certificate will be used
1027 * - If @username != NULL, then
1028 * - If @password == NULL, then certificate will be used and
1029 * @username shifts meaning to box ID. This is used for hosted
1030 * services.
1031 * - Otherwise all three arguments will be used.
1032 * Please note, that different cases requires different certificate type
1033 * (system qualified one or commercial non qualified one). This library does
1034 * not check such political issues. Please see ISDS Specification for more
1035 * details.
1036 * @url is base address of ISDS web service. Pass extern isds_locator
1037 * variable to use production ISDS instance without client certificate
1038 * authentication (or extern isds_cert_locator with client certificate
1039 * authentication). Passing NULL has the same effect, autoselection between
1040 * isds_locator and isds_cert_locator is performed in addition. You can pass
1041 * extern isds_testing_locator (or isds_cert_testing_locator) variable to
1042 * select testing instance.
1043 * @username is user name of ISDS user or box ID
1044 * @password is user's secret password
1045 * @pki_credentials defines public key cryptographic material to use in client
1046 * authentication. */
1047 isds_error isds_login(struct isds_ctx *context, const char *url,
1048 const char *username, const char *password,
1049 const struct isds_pki_credentials *pki_credentials) {
1050 isds_error err = IE_NOT_LOGGED_IN;
1051 isds_error soap_err;
1052 xmlNsPtr isds_ns = NULL;
1053 xmlNodePtr request = NULL;
1054 xmlNodePtr response = NULL;
1056 if (!context) return IE_INVALID_CONTEXT;
1057 zfree(context->long_message);
1059 /* Close connection if already logged in */
1060 if (context->curl) {
1061 _isds_close_connection(context);
1064 /* Store configuration */
1065 context->type = CTX_TYPE_ISDS;
1066 zfree(context->url);
1068 /* Mangle base URI according requested authentication method */
1069 if (!pki_credentials) {
1070 isds_log(ILF_SEC, ILL_INFO,
1071 _("Selected authentication method: no certificate, "
1072 "username and password\n"));
1073 if (!username || !password) {
1074 isds_log_message(context,
1075 _("Both username and password must be supplied"));
1076 return IE_INVAL;
1078 /* Default locator is official system (without client certificate) */
1079 context->url = strdup((url) ? url : isds_locator);
1080 } else {
1081 /* Default locator is official system (with client certificate) */
1082 if (!url) url = isds_cert_locator;
1084 if (!username) {
1085 isds_log(ILF_SEC, ILL_INFO,
1086 _("Selected authentication method: system certificate, "
1087 "no username and no password\n"));
1088 password = NULL;
1089 context->url = _isds_astrcat(url, "cert/");
1090 } else {
1091 if (!password) {
1092 isds_log(ILF_SEC, ILL_INFO,
1093 _("Selected authentication method: system certificate, "
1094 "box ID and no password\n"));
1095 context->url = _isds_astrcat(url, "hspis/");
1096 } else {
1097 isds_log(ILF_SEC, ILL_INFO,
1098 _("Selected authentication method: commercial "
1099 "certificate, username and password\n"));
1100 context->url = _isds_astrcat(url, "certds/");
1104 if (!(context->url))
1105 return IE_NOMEM;
1107 /* Prepare CURL handle */
1108 context->curl = curl_easy_init();
1109 if (!(context->curl))
1110 return IE_ERROR;
1112 /* Build log-in request */
1113 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1114 if (!request) {
1115 isds_log_message(context, _("Could not build ISDS log-in request"));
1116 return IE_ERROR;
1118 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1119 if(!isds_ns) {
1120 isds_log_message(context, _("Could not create ISDS name space"));
1121 xmlFreeNode(request);
1122 return IE_ERROR;
1124 xmlSetNs(request, isds_ns);
1126 /* Store credentials */
1127 /* FIXME: mlock password
1128 * (I have a library) */
1129 discard_credentials(context);
1130 if (username) context->username = strdup(username);
1131 if (password) context->password = strdup(password);
1132 context->pki_credentials = isds_pki_credentials_duplicate(pki_credentials);
1133 if ((username && !context->username) || (password && !context->password) ||
1134 (pki_credentials && !context->pki_credentials)) {
1135 discard_credentials(context);
1136 xmlFreeNode(request);
1137 return IE_NOMEM;
1140 isds_log(ILF_ISDS, ILL_DEBUG, _("Logging user %s into server %s\n"),
1141 username, url);
1143 /* Send log-in request */
1144 soap_err = _isds_soap(context, "DS/dz", request, &response, NULL, NULL);
1146 /* Remove credentials */
1147 discard_credentials(context);
1149 /* Destroy log-in request */
1150 xmlFreeNode(request);
1152 if (soap_err) {
1153 xmlFreeNodeList(response);
1154 _isds_close_connection(context);
1155 return soap_err;
1158 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1159 * authentication succeeded if soap_err == IE_SUCCESS */
1160 err = IE_SUCCESS;
1162 xmlFreeNodeList(response);
1164 if (!err)
1165 isds_log(ILF_ISDS, ILL_DEBUG,
1166 _("User %s has been logged into server %s successfully\n"),
1167 username, url);
1168 return err;
1172 /* Log out from ISDS server discards credentials and connection configuration. */
1173 isds_error isds_logout(struct isds_ctx *context) {
1174 if (!context) return IE_INVALID_CONTEXT;
1175 zfree(context->long_message);
1177 /* Close connection */
1178 if (context->curl) {
1179 _isds_close_connection(context);
1181 /* Discard credentials for sure. They should not survive isds_login(),
1182 * even successful .*/
1183 discard_credentials(context);
1184 zfree(context->url);
1186 isds_log(ILF_ISDS, ILL_DEBUG, _("Logged out from ISDS server\n"));
1187 } else {
1188 discard_credentials(context);
1190 return IE_SUCCESS;
1194 /* Verify connection to ISDS is alive and server is responding.
1195 * Sent dummy request to ISDS and expect dummy response. */
1196 isds_error isds_ping(struct isds_ctx *context) {
1197 isds_error soap_err;
1198 xmlNsPtr isds_ns = NULL;
1199 xmlNodePtr request = NULL;
1200 xmlNodePtr response = NULL;
1202 if (!context) return IE_INVALID_CONTEXT;
1203 zfree(context->long_message);
1205 /* Check if connection is established */
1206 if (!context->curl) return IE_CONNECTION_CLOSED;
1209 /* Build dummy request */
1210 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1211 if (!request) {
1212 isds_log_message(context, _("Could build ISDS dummy request"));
1213 return IE_ERROR;
1215 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1216 if(!isds_ns) {
1217 isds_log_message(context, _("Could not create ISDS name space"));
1218 xmlFreeNode(request);
1219 return IE_ERROR;
1221 xmlSetNs(request, isds_ns);
1223 isds_log(ILF_ISDS, ILL_DEBUG, _("Pinging ISDS server\n"));
1225 /* Sent dummy request */
1226 soap_err = _isds_soap(context, "DS/dz", request, &response, NULL, NULL);
1228 /* Destroy log-in request */
1229 xmlFreeNode(request);
1231 if (soap_err) {
1232 isds_log(ILF_ISDS, ILL_DEBUG,
1233 _("ISDS server could not be contacted\n"));
1234 xmlFreeNodeList(response);
1235 return soap_err;
1238 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1239 * authentication succeeded if soap_err == IE_SUCCESS */
1240 /* TODO: ISDS documentation does not specify response body.
1241 * However real server sends back DummyOperationResponse */
1244 xmlFreeNodeList(response);
1246 isds_log(ILF_ISDS, ILL_DEBUG, _("ISDS server alive\n"));
1248 return IE_SUCCESS;
1252 /* Send bogus request to ISDS.
1253 * Just for test purposes */
1254 isds_error isds_bogus_request(struct isds_ctx *context) {
1255 isds_error err;
1256 xmlNsPtr isds_ns = NULL;
1257 xmlNodePtr request = NULL;
1258 xmlDocPtr response = NULL;
1259 xmlChar *code = NULL, *message = NULL;
1261 if (!context) return IE_INVALID_CONTEXT;
1262 zfree(context->long_message);
1264 /* Check if connection is established */
1265 if (!context->curl) {
1266 /* Testing printf message */
1267 isds_printf_message(context, "%s", _("I said connection closed"));
1268 return IE_CONNECTION_CLOSED;
1272 /* Build dummy request */
1273 request = xmlNewNode(NULL, BAD_CAST "X-BogusOperation");
1274 if (!request) {
1275 isds_log_message(context, _("Could build ISDS bogus request"));
1276 return IE_ERROR;
1278 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1279 if(!isds_ns) {
1280 isds_log_message(context, _("Could not create ISDS name space"));
1281 xmlFreeNode(request);
1282 return IE_ERROR;
1284 xmlSetNs(request, isds_ns);
1286 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending bogus request to ISDS\n"));
1288 /* Sent bogus request */
1289 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
1291 /* Destroy request */
1292 xmlFreeNode(request);
1294 if (err) {
1295 isds_log(ILF_ISDS, ILL_DEBUG,
1296 _("Processing ISDS response on bogus request failed\n"));
1297 xmlFreeDoc(response);
1298 return err;
1301 /* Check for response status */
1302 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
1303 &code, &message, NULL);
1304 if (err) {
1305 isds_log(ILF_ISDS, ILL_DEBUG,
1306 _("ISDS response on bogus request is missing status\n"));
1307 free(code);
1308 free(message);
1309 xmlFreeDoc(response);
1310 return err;
1312 if (xmlStrcmp(code, BAD_CAST "0000")) {
1313 char *code_locale = _isds_utf82locale((char*)code);
1314 char *message_locale = _isds_utf82locale((char*)message);
1315 isds_log(ILF_ISDS, ILL_DEBUG,
1316 _("Server refused bogus request (code=%s, message=%s)\n"),
1317 code_locale, message_locale);
1318 /* XXX: Literal error messages from ISDS are Czech messages
1319 * (English sometimes) in UTF-8. It's hard to catch them for
1320 * translation. Successfully gettextized would return in locale
1321 * encoding, unsuccessfully translated would pass in UTF-8. */
1322 isds_log_message(context, message_locale);
1323 free(code_locale);
1324 free(message_locale);
1325 free(code);
1326 free(message);
1327 xmlFreeDoc(response);
1328 return IE_ISDS;
1332 free(code);
1333 free(message);
1334 xmlFreeDoc(response);
1336 isds_log(ILF_ISDS, ILL_DEBUG,
1337 _("Bogus message accepted by server. This should not happen.\n"));
1339 return IE_SUCCESS;
1343 /* Serialize XML subtree to buffer preserving XML indentation.
1344 * @context is session context
1345 * @subtree is XML element to be serialized (with children)
1346 * @buffer is automatically reallocated buffer where serialize to
1347 * @length is size of serialized stream in bytes
1348 * @return standard error code, free @buffer in case of error */
1349 static isds_error serialize_subtree(struct isds_ctx *context,
1350 xmlNodePtr subtree, void **buffer, size_t *length) {
1351 isds_error err = IE_SUCCESS;
1352 xmlBufferPtr xml_buffer = NULL;
1353 xmlSaveCtxtPtr save_ctx = NULL;
1354 xmlDocPtr subtree_doc = NULL;
1355 xmlNodePtr subtree_copy;
1356 xmlNsPtr isds_ns;
1357 void *new_buffer;
1359 if (!context) return IE_INVALID_CONTEXT;
1360 if (!buffer) return IE_INVAL;
1361 zfree(*buffer);
1362 if (!subtree || !length) return IE_INVAL;
1364 /* Make temporary XML document with @subtree root element */
1365 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1366 * It can result in not well-formed on invalid XML tree (e.g. name space
1367 * prefix definition can miss. */
1368 /*FIXME */
1370 subtree_doc = xmlNewDoc(BAD_CAST "1.0");
1371 if (!subtree_doc) {
1372 isds_log_message(context, _("Could not build temporary document"));
1373 err = IE_ERROR;
1374 goto leave;
1377 /* XXX: Copy subtree and attach the copy to document.
1378 * One node can not bee attached into more document at the same time.
1379 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1380 * automatically.
1381 * XXX: Check xmlSaveTree() too. */
1382 subtree_copy = xmlCopyNodeList(subtree);
1383 if (!subtree_copy) {
1384 isds_log_message(context, _("Could not copy subtree"));
1385 err = IE_ERROR;
1386 goto leave;
1388 xmlDocSetRootElement(subtree_doc, subtree_copy);
1390 /* Only this way we get namespace definition as @xmlns:isds,
1391 * otherwise we get namespace prefix without definition */
1392 /* FIXME: Don't overwrite original default namespace */
1393 isds_ns = xmlNewNs(subtree_copy, BAD_CAST ISDS_NS, NULL);
1394 if(!isds_ns) {
1395 isds_log_message(context, _("Could not create ISDS name space"));
1396 err = IE_ERROR;
1397 goto leave;
1399 xmlSetNs(subtree_copy, isds_ns);
1402 /* Serialize the document into buffer */
1403 xml_buffer = xmlBufferCreate();
1404 if (!xml_buffer) {
1405 isds_log_message(context, _("Could not create xmlBuffer"));
1406 err = IE_ERROR;
1407 goto leave;
1409 /* Last argument 0 means to not format the XML tree */
1410 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8", 0);
1411 if (!save_ctx) {
1412 isds_log_message(context, _("Could not create XML serializer"));
1413 err = IE_ERROR;
1414 goto leave;
1416 /* XXX: According LibXML documentation, this function does not return
1417 * meaningful value yet */
1418 xmlSaveDoc(save_ctx, subtree_doc);
1419 if (-1 == xmlSaveFlush(save_ctx)) {
1420 isds_log_message(context,
1421 _("Could not serialize XML subtree"));
1422 err = IE_ERROR;
1423 goto leave;
1425 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1426 * even after xmlSaveFlush(). Thus close it here */
1427 xmlSaveClose(save_ctx); save_ctx = NULL;
1430 /* Store and detach buffer from xml_buffer */
1431 *buffer = xml_buffer->content;
1432 *length = xml_buffer->use;
1433 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1435 /* Shrink buffer */
1436 new_buffer = realloc(*buffer, *length);
1437 if (new_buffer) *buffer = new_buffer;
1439 leave:
1440 if (err) {
1441 zfree(*buffer);
1442 *length = 0;
1445 xmlSaveClose(save_ctx);
1446 xmlBufferFree(xml_buffer);
1447 xmlFreeDoc(subtree_doc); /* Frees subtree_copy, isds_ns etc. */
1448 return err;
1451 #if 0
1452 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1453 * @context is session context
1454 * @document is original document where @nodeset points to
1455 * @nodeset is XPath node set to dump (recursively)
1456 * @buffer is automatically reallocated buffer where serialize to
1457 * @length is size of serialized stream in bytes
1458 * @return standard error code, free @buffer in case of error */
1459 static isds_error dump_nodeset(struct isds_ctx *context,
1460 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1461 void **buffer, size_t *length) {
1462 isds_error err = IE_SUCCESS;
1463 xmlBufferPtr xml_buffer = NULL;
1464 void *new_buffer;
1466 if (!context) return IE_INVALID_CONTEXT;
1467 if (!buffer) return IE_INVAL;
1468 zfree(*buffer);
1469 if (!document || !nodeset || !length) return IE_INVAL;
1470 *length = 0;
1472 /* Empty node set results into NULL buffer */
1473 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1474 goto leave;
1477 /* Resulting the document into buffer */
1478 xml_buffer = xmlBufferCreate();
1479 if (!xml_buffer) {
1480 isds_log_message(context, _("Could not create xmlBuffer"));
1481 err = IE_ERROR;
1482 goto leave;
1485 /* Iterate over all nodes */
1486 for (int i = 0; i < nodeset->nodeNr; i++) {
1487 /* Serialize node.
1488 * XXX: xmlNodeDump() appends to xml_buffer. */
1489 if (-1 ==
1490 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1491 isds_log_message(context, _("Could not dump XML node"));
1492 err = IE_ERROR;
1493 goto leave;
1497 /* Store and detach buffer from xml_buffer */
1498 *buffer = xml_buffer->content;
1499 *length = xml_buffer->use;
1500 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1502 /* Shrink buffer */
1503 new_buffer = realloc(*buffer, *length);
1504 if (new_buffer) *buffer = new_buffer;
1507 leave:
1508 if (err) {
1509 zfree(*buffer);
1510 *length = 0;
1513 xmlBufferFree(xml_buffer);
1514 return err;
1516 #endif
1518 #if 0
1519 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1520 * @context is session context
1521 * @document is original document where @nodeset points to
1522 * @nodeset is XPath node set to dump (recursively)
1523 * @buffer is automatically reallocated buffer where serialize to
1524 * @length is size of serialized stream in bytes
1525 * @return standard error code, free @buffer in case of error */
1526 static isds_error dump_nodeset(struct isds_ctx *context,
1527 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1528 void **buffer, size_t *length) {
1529 isds_error err = IE_SUCCESS;
1530 xmlBufferPtr xml_buffer = NULL;
1531 xmlSaveCtxtPtr save_ctx = NULL;
1532 void *new_buffer;
1534 if (!context) return IE_INVALID_CONTEXT;
1535 if (!buffer) return IE_INVAL;
1536 zfree(*buffer);
1537 if (!document || !nodeset || !length) return IE_INVAL;
1538 *length = 0;
1540 /* Empty node set results into NULL buffer */
1541 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1542 goto leave;
1545 /* Resulting the document into buffer */
1546 xml_buffer = xmlBufferCreate();
1547 if (!xml_buffer) {
1548 isds_log_message(context, _("Could not create xmlBuffer"));
1549 err = IE_ERROR;
1550 goto leave;
1552 if (xmlSubstituteEntitiesDefault(1)) {
1553 isds_log_message(context, _("Could not disable attribute escaping"));
1554 err = IE_ERROR;
1555 goto leave;
1557 /* Last argument means:
1558 * 0 to not format the XML tree
1559 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1560 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8",
1561 XML_SAVE_NO_DECL|XML_SAVE_NO_EMPTY|XML_SAVE_NO_XHTML);
1562 if (!save_ctx) {
1563 isds_log_message(context, _("Could not create XML serializer"));
1564 err = IE_ERROR;
1565 goto leave;
1567 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1568 isds_log_message(context, _("Could not disable attribute escaping"));
1569 err = IE_ERROR;
1570 goto leave;
1574 /* Iterate over all nodes */
1575 for (int i = 0; i < nodeset->nodeNr; i++) {
1576 /* Serialize node.
1577 * XXX: xmlNodeDump() appends to xml_buffer. */
1578 /*if (-1 ==
1579 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1581 /* XXX: According LibXML documentation, this function does not return
1582 * meaningful value yet */
1583 xmlSaveTree(save_ctx, nodeset->nodeTab[i]);
1584 if (-1 == xmlSaveFlush(save_ctx)) {
1585 isds_log_message(context,
1586 _("Could not serialize XML subtree"));
1587 err = IE_ERROR;
1588 goto leave;
1592 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1593 * even after xmlSaveFlush(). Thus close it here */
1594 xmlSaveClose(save_ctx); save_ctx = NULL;
1596 /* Store and detach buffer from xml_buffer */
1597 *buffer = xml_buffer->content;
1598 *length = xml_buffer->use;
1599 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1601 /* Shrink buffer */
1602 new_buffer = realloc(*buffer, *length);
1603 if (new_buffer) *buffer = new_buffer;
1605 leave:
1606 if (err) {
1607 zfree(*buffer);
1608 *length = 0;
1611 xmlSaveClose(save_ctx);
1612 xmlBufferFree(xml_buffer);
1613 return err;
1615 #endif
1618 /* Convert UTF-8 @string representation of ISDS dbType to enum @type */
1619 static isds_error string2isds_DbType(xmlChar *string, isds_DbType *type) {
1620 if (!string || !type) return IE_INVAL;
1622 if (!xmlStrcmp(string, BAD_CAST "FO"))
1623 *type = DBTYPE_FO;
1624 else if (!xmlStrcmp(string, BAD_CAST "PFO"))
1625 *type = DBTYPE_PFO;
1626 else if (!xmlStrcmp(string, BAD_CAST "PFO_ADVOK"))
1627 *type = DBTYPE_PFO_ADVOK;
1628 else if (!xmlStrcmp(string, BAD_CAST "PFO_DANPOR"))
1629 *type = DBTYPE_PFO_DANPOR;
1630 else if (!xmlStrcmp(string, BAD_CAST "PFO_INSSPR"))
1631 *type = DBTYPE_PFO_INSSPR;
1632 else if (!xmlStrcmp(string, BAD_CAST "PO"))
1633 *type = DBTYPE_PO;
1634 else if (!xmlStrcmp(string, BAD_CAST "PO_ZAK"))
1635 *type = DBTYPE_PO_ZAK;
1636 else if (!xmlStrcmp(string, BAD_CAST "PO_REQ"))
1637 *type = DBTYPE_PO_REQ;
1638 else if (!xmlStrcmp(string, BAD_CAST "OVM"))
1639 *type = DBTYPE_OVM;
1640 else if (!xmlStrcmp(string, BAD_CAST "OVM_NOTAR"))
1641 *type = DBTYPE_OVM_NOTAR;
1642 else if (!xmlStrcmp(string, BAD_CAST "OVM_EXEKUT"))
1643 *type = DBTYPE_OVM_EXEKUT;
1644 else if (!xmlStrcmp(string, BAD_CAST "OVM_REQ"))
1645 *type = DBTYPE_OVM_REQ;
1646 else
1647 return IE_ENUM;
1648 return IE_SUCCESS;
1652 /* Convert ISDS dbType enum @type to UTF-8 string.
1653 * @Return pointer to static string, or NULL if unknown enum value */
1654 static const xmlChar *isds_DbType2string(const isds_DbType type) {
1655 switch(type) {
1656 /* DBTYPE_SYSTEM is invalid value from point of view of public
1657 * SOAP interface. */
1658 case DBTYPE_FO: return(BAD_CAST "FO"); break;
1659 case DBTYPE_PFO: return(BAD_CAST "PFO"); break;
1660 case DBTYPE_PFO_ADVOK: return(BAD_CAST "PFO_ADVOK"); break;
1661 case DBTYPE_PFO_DANPOR: return(BAD_CAST "PFO_DANPOR"); break;
1662 case DBTYPE_PFO_INSSPR: return(BAD_CAST "PFO_INSSPR"); break;
1663 case DBTYPE_PO: return(BAD_CAST "PO"); break;
1664 case DBTYPE_PO_ZAK: return(BAD_CAST "PO_ZAK"); break;
1665 case DBTYPE_PO_REQ: return(BAD_CAST "PO_REQ"); break;
1666 case DBTYPE_OVM: return(BAD_CAST "OVM"); break;
1667 case DBTYPE_OVM_NOTAR: return(BAD_CAST "OVM_NOTAR"); break;
1668 case DBTYPE_OVM_EXEKUT: return(BAD_CAST "OVM_EXEKUT"); break;
1669 case DBTYPE_OVM_REQ: return(BAD_CAST "OVM_REQ"); break;
1670 default: return NULL; break;
1675 /* Convert UTF-8 @string representation of ISDS userType to enum @type */
1676 static isds_error string2isds_UserType(xmlChar *string, isds_UserType *type) {
1677 if (!string || !type) return IE_INVAL;
1679 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
1680 *type = USERTYPE_PRIMARY;
1681 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
1682 *type = USERTYPE_ENTRUSTED;
1683 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
1684 *type = USERTYPE_ADMINISTRATOR;
1685 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
1686 *type = USERTYPE_OFFICIAL;
1687 else
1688 return IE_ENUM;
1689 return IE_SUCCESS;
1693 /* Convert ISDS userType enum @type to UTF-8 string.
1694 * @Return pointer to static string, or NULL if unknown enum value */
1695 static const xmlChar *isds_UserType2string(const isds_UserType type) {
1696 switch(type) {
1697 case USERTYPE_PRIMARY: return(BAD_CAST "PRIMARY_USER"); break;
1698 case USERTYPE_ENTRUSTED: return(BAD_CAST "ENTRUSTED_USER"); break;
1699 case USERTYPE_ADMINISTRATOR: return(BAD_CAST "ADMINISTRATOR"); break;
1700 case USERTYPE_OFFICIAL: return(BAD_CAST "OFFICIAL"); break;
1701 default: return NULL; break;
1706 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
1707 * @Return pointer to static string, or NULL if unknown enum value */
1708 static const xmlChar *isds_FileMetaType2string(const isds_FileMetaType type) {
1709 switch(type) {
1710 case FILEMETATYPE_MAIN: return(BAD_CAST "main"); break;
1711 case FILEMETATYPE_ENCLOSURE: return(BAD_CAST "enclosure"); break;
1712 case FILEMETATYPE_SIGNATURE: return(BAD_CAST "signature"); break;
1713 case FILEMETATYPE_META: return(BAD_CAST "meta"); break;
1714 default: return NULL; break;
1719 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
1720 * @Return IE_ENUM if @string is not valid enum member */
1721 static isds_error string2isds_FileMetaType(const xmlChar *string,
1722 isds_FileMetaType *type) {
1723 if (!string || !type) return IE_INVAL;
1725 if (!xmlStrcmp(string, BAD_CAST "main"))
1726 *type = FILEMETATYPE_MAIN;
1727 else if (!xmlStrcmp(string, BAD_CAST "enclosure"))
1728 *type = FILEMETATYPE_ENCLOSURE;
1729 else if (!xmlStrcmp(string, BAD_CAST "signature"))
1730 *type = FILEMETATYPE_SIGNATURE;
1731 else if (!xmlStrcmp(string, BAD_CAST "meta"))
1732 *type = FILEMETATYPE_META;
1733 else
1734 return IE_ENUM;
1735 return IE_SUCCESS;
1739 /* Convert UTF-8 @string to ISDS hash @algorithm.
1740 * @Return IE_ENUM if @string is not valid enum member */
1741 static isds_error string2isds_hash_algorithm(const xmlChar *string,
1742 isds_hash_algorithm *algorithm) {
1743 if (!string || !algorithm) return IE_INVAL;
1745 if (!xmlStrcmp(string, BAD_CAST "MD5"))
1746 *algorithm = HASH_ALGORITHM_MD5;
1747 else if (!xmlStrcmp(string, BAD_CAST "SHA-1"))
1748 *algorithm = HASH_ALGORITHM_SHA_1;
1749 else if (!xmlStrcmp(string, BAD_CAST "SHA-224"))
1750 *algorithm = HASH_ALGORITHM_SHA_224;
1751 else if (!xmlStrcmp(string, BAD_CAST "SHA-256"))
1752 *algorithm = HASH_ALGORITHM_SHA_256;
1753 else if (!xmlStrcmp(string, BAD_CAST "SHA-384"))
1754 *algorithm = HASH_ALGORITHM_SHA_384;
1755 else if (!xmlStrcmp(string, BAD_CAST "SHA-512"))
1756 *algorithm = HASH_ALGORITHM_SHA_512;
1757 else
1758 return IE_ENUM;
1759 return IE_SUCCESS;
1763 /* Convert UTF-8 @string representation of ISO 8601 date to @time.
1764 * XXX: Not all ISO formats are supported */
1765 static isds_error datestring2tm(const xmlChar *string, struct tm *time) {
1766 char *offset;
1767 if (!string || !time) return IE_INVAL;
1769 /* xsd:date is ISO 8601 string, thus ASCII */
1770 offset = strptime((char*)string, "%Y-%m-%d", time);
1771 if (offset && *offset == '\0')
1772 return IE_SUCCESS;
1774 offset = strptime((char*)string, "%Y%m%d", time);
1775 if (offset && *offset == '\0')
1776 return IE_SUCCESS;
1778 offset = strptime((char*)string, "%Y-%j", time);
1779 if (offset && *offset == '\0')
1780 return IE_SUCCESS;
1782 return IE_NOTSUP;
1786 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
1787 static isds_error tm2datestring(const struct tm *time, xmlChar **string) {
1788 if (!time || !string) return IE_INVAL;
1790 if (-1 == isds_asprintf((char **) string, "%d-%02d-%02d",
1791 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
1792 return IE_ERROR;
1794 return IE_SUCCESS;
1798 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
1799 * respects the @time microseconds too. */
1800 static isds_error timeval2timestring(const struct timeval *time,
1801 xmlChar **string) {
1802 struct tm broken;
1804 if (!time || !string) return IE_INVAL;
1806 if (!gmtime_r(&time->tv_sec, &broken)) return IE_DATE;
1807 if (time->tv_usec < 0 || time->tv_usec > 999999) return IE_DATE;
1809 /* TODO: small negative year should be formatted as "-0012". This is not
1810 * true for glibc "%04d". We should implement it.
1811 * TODO: What's type of time->tv_usec exactly? Unsigned? Absolute?
1812 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
1813 if (-1 == isds_asprintf((char **) string,
1814 "%04d-%02d-%02dT%02d:%02d:%02d.%06ld",
1815 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
1816 broken.tm_hour, broken.tm_min, broken.tm_sec,
1817 time->tv_usec))
1818 return IE_ERROR;
1820 return IE_SUCCESS;
1824 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
1825 * It respects microseconds too.
1826 * In case of error, @time will be freed. */
1827 static isds_error timestring2timeval(const xmlChar *string,
1828 struct timeval **time) {
1829 struct tm broken;
1830 char *offset, *delim, *endptr;
1831 char subseconds[7];
1832 int offset_hours, offset_minutes;
1833 int i;
1835 if (!time) return IE_INVAL;
1837 memset(&broken, 0, sizeof(broken));
1839 if (!*time) {
1840 *time = calloc(1, sizeof(**time));
1841 if (!*time) return IE_NOMEM;
1842 } else {
1843 memset(*time, 0, sizeof(**time));
1847 /* xsd:date is ISO 8601 string, thus ASCII */
1848 /*TODO: negative year */
1850 /* Parse date and time without subseconds and offset */
1851 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
1852 if (!offset) {
1853 free(*time); *time = NULL;
1854 return IE_DATE;
1857 /* Get subseconds */
1858 if (*offset == '.' ) {
1859 offset++;
1861 /* Copy first 6 digits, pad it with zeros.
1862 * XXX: It truncates longer number, no round.
1863 * Current server implementation uses only millisecond resolution. */
1864 /* TODO: isdigit() is locale sensitive */
1865 for (i = 0;
1866 i < sizeof(subseconds)/sizeof(char) - 1 && isdigit(*offset);
1867 i++, offset++) {
1868 subseconds[i] = *offset;
1870 for (; i < sizeof(subseconds)/sizeof(char) - 1; i++) {
1871 subseconds[i] = '0';
1873 subseconds[6] = '\0';
1875 /* Convert it into integer */
1876 (*time)->tv_usec = strtol(subseconds, &endptr, 10);
1877 if (*endptr != '\0' || (*time)->tv_usec == LONG_MIN ||
1878 (*time)->tv_usec == LONG_MAX) {
1879 free(*time); *time = NULL;
1880 return IE_DATE;
1883 /* move to the zone offset delimiter */
1884 delim = strchr(offset, '-');
1885 if (!delim)
1886 delim = strchr(offset, '+');
1887 offset = delim;
1890 /* Get zone offset */
1891 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
1892 * "" equals to "Z" and it means UTC zone. */
1893 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
1894 * colon separator */
1895 if (*offset == '-' || *offset == '+') {
1896 offset++;
1897 if (2 != sscanf(offset, "%2d:%2d", &offset_hours, &offset_minutes)) {
1898 free(*time); *time = NULL;
1899 return IE_DATE;
1901 broken.tm_hour -= offset_hours;
1902 broken.tm_min -= offset_minutes * ((offset_hours<0) ? -1 : 1);
1905 /* Convert to time_t */
1906 _isds_switch_tz_to_utc();
1907 (*time)->tv_sec = mktime(&broken);
1908 _isds_switch_tz_to_native();
1909 if ((*time)->tv_sec == (time_t) -1) {
1910 free(*time); *time = NULL;
1911 return IE_DATE;
1914 return IE_SUCCESS;
1918 /* Convert unsigned int into isds_message_status.
1919 * @context is session context
1920 * @number is pointer to number value. NULL will be treated as invalid value.
1921 * @status is automatically reallocated status
1922 * @return IE_SUCCESS, or error code and free status */
1923 static isds_error uint2isds_message_status(struct isds_ctx *context,
1924 const unsigned long int *number, isds_message_status **status) {
1925 if (!context) return IE_INVALID_CONTEXT;
1926 if (!status) return IE_INVAL;
1928 free(*status); *status = NULL;
1929 if (!number) return IE_INVAL;
1931 if (*number < 1 || *number > 10) {
1932 isds_printf_message(context, _("Invalid message status value: %lu"),
1933 *number);
1934 return IE_ENUM;
1937 *status = malloc(sizeof(**status));
1938 if (!*status) return IE_NOMEM;
1940 **status = 1 << *number;
1941 return IE_SUCCESS;
1945 /* Convert event description string into isds_event members type and
1946 * description
1947 * @string is raw event description starting with event prefix
1948 * @event is structure where to store type and stripped description to
1949 * @return standard error code, unknown prefix is not classified as an error.
1950 * */
1951 static isds_error eventstring2event(const xmlChar *string,
1952 struct isds_event* event) {
1953 const xmlChar *known_prefixes[] = {
1954 BAD_CAST "EV1:",
1955 BAD_CAST "EV2:",
1956 BAD_CAST "EV3:",
1957 BAD_CAST "EV4:"
1959 const isds_event_type types[] = {
1960 EVENT_ACCEPTED_BY_RECIPIENT,
1961 EVENT_ACCEPTED_BY_FICTION,
1962 EVENT_UNDELIVERABLE,
1963 EVENT_COMMERCIAL_ACCEPTED
1965 unsigned int index;
1966 size_t length;
1968 if (!string || !event) return IE_INVAL;
1970 if (!event->type) {
1971 event->type = malloc(sizeof(*event->type));
1972 if (!(event->type)) return IE_NOMEM;
1974 zfree(event->description);
1976 for (index = 0; index < sizeof(known_prefixes)/sizeof(known_prefixes[0]);
1977 index++) {
1978 length = xmlUTF8Strlen(known_prefixes[index]);
1980 if (!xmlStrncmp(string, known_prefixes[index], length)) {
1981 /* Prefix is known */
1982 *event->type = types[index];
1984 /* Strip prefix from description and spaces */
1985 /* TODO: Recognize all white spaces from UCS blank class and
1986 * operate on UTF-8 chars. */
1987 for (; string[length] != '\0' && string[length] == ' '; length++);
1988 event->description = strdup((char *) (string + length));
1989 if (!(event->description)) return IE_NOMEM;
1991 return IE_SUCCESS;
1995 /* Unknown event prefix.
1996 * XSD allows any string */
1997 char *string_locale = _isds_utf82locale((char *) string);
1998 isds_log(ILF_ISDS, ILL_WARNING,
1999 _("Unknown delivery info event prefix: %s\n"), string_locale);
2000 free(string_locale);
2002 *event->type = EVENT_UKNOWN;
2003 event->description = strdup((char *) string);
2004 if (!(event->description)) return IE_NOMEM;
2006 return IE_SUCCESS;
2010 /* Following EXTRACT_* macros expects @result, @xpath_ctx, @err, @context
2011 * and leave label */
2012 #define EXTRACT_STRING(element, string) { \
2013 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2014 if (!result) { \
2015 err = IE_ERROR; \
2016 goto leave; \
2018 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2019 if (result->nodesetval->nodeNr > 1) { \
2020 isds_printf_message(context, _("Multiple %s element"), element); \
2021 err = IE_ERROR; \
2022 goto leave; \
2024 (string) = (char *) \
2025 xmlXPathCastNodeSetToString(result->nodesetval); \
2026 if (!(string)) { \
2027 err = IE_ERROR; \
2028 goto leave; \
2033 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2035 char *string = NULL; \
2036 EXTRACT_STRING(element, string); \
2038 if (string) { \
2039 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2040 if (!(booleanPtr)) { \
2041 free(string); \
2042 err = IE_NOMEM; \
2043 goto leave; \
2046 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2047 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2048 *(booleanPtr) = 1; \
2049 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2050 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2051 *(booleanPtr) = 0; \
2052 else { \
2053 char *string_locale = _isds_utf82locale((char*)string); \
2054 isds_printf_message(context, \
2055 _("%s value is not valid boolean: %s"), \
2056 element, string_locale); \
2057 free(string_locale); \
2058 free(string); \
2059 err = IE_ERROR; \
2060 goto leave; \
2063 free(string); \
2067 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2069 char *string = NULL; \
2070 EXTRACT_STRING(element, string); \
2071 if (string) { \
2072 long int number; \
2073 char *endptr; \
2075 number = strtol((char*)string, &endptr, 10); \
2077 if (*endptr != '\0') { \
2078 char *string_locale = _isds_utf82locale((char *)string); \
2079 isds_printf_message(context, \
2080 _("%s is not valid integer: %s"), \
2081 element, string_locale); \
2082 free(string_locale); \
2083 free(string); \
2084 err = IE_ISDS; \
2085 goto leave; \
2088 if (number == LONG_MIN || number == LONG_MAX) { \
2089 char *string_locale = _isds_utf82locale((char *)string); \
2090 isds_printf_message(context, \
2091 _("%s value out of range of long int: %s"), \
2092 element, string_locale); \
2093 free(string_locale); \
2094 free(string); \
2095 err = IE_ERROR; \
2096 goto leave; \
2099 free(string); string = NULL; \
2101 if (!(preallocated)) { \
2102 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2103 if (!(longintPtr)) { \
2104 err = IE_NOMEM; \
2105 goto leave; \
2108 *(longintPtr) = number; \
2112 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2114 char *string = NULL; \
2115 EXTRACT_STRING(element, string); \
2116 if (string) { \
2117 long int number; \
2118 char *endptr; \
2120 number = strtol((char*)string, &endptr, 10); \
2122 if (*endptr != '\0') { \
2123 char *string_locale = _isds_utf82locale((char *)string); \
2124 isds_printf_message(context, \
2125 _("%s is not valid integer: %s"), \
2126 element, string_locale); \
2127 free(string_locale); \
2128 free(string); \
2129 err = IE_ISDS; \
2130 goto leave; \
2133 if (number == LONG_MIN || number == LONG_MAX) { \
2134 char *string_locale = _isds_utf82locale((char *)string); \
2135 isds_printf_message(context, \
2136 _("%s value out of range of long int: %s"), \
2137 element, string_locale); \
2138 free(string_locale); \
2139 free(string); \
2140 err = IE_ERROR; \
2141 goto leave; \
2144 free(string); string = NULL; \
2145 if (number < 0) { \
2146 isds_printf_message(context, \
2147 _("%s value is negative: %ld"), element, number); \
2148 err = IE_ERROR; \
2149 goto leave; \
2152 if (!(preallocated)) { \
2153 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2154 if (!(ulongintPtr)) { \
2155 err = IE_NOMEM; \
2156 goto leave; \
2159 *(ulongintPtr) = number; \
2163 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2164 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2165 NULL); \
2166 if ((required) && (!string)) { \
2167 char *attribute_locale = _isds_utf82locale(attribute); \
2168 char *element_locale = \
2169 _isds_utf82locale((char *)xpath_ctx->node->name); \
2170 isds_printf_message(context, \
2171 _("Could not extract required %s attribute value from " \
2172 "%s element"), attribute_locale, element_locale); \
2173 free(element_locale); \
2174 free(attribute_locale); \
2175 err = IE_ERROR; \
2176 goto leave; \
2181 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2183 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2184 (xmlChar *) (string)); \
2185 if (!node) { \
2186 isds_printf_message(context, \
2187 _("Could not add %s child to %s element"), \
2188 element, (parent)->name); \
2189 err = IE_ERROR; \
2190 goto leave; \
2194 #define INSERT_STRING(parent, element, string) \
2195 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2197 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2199 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2200 else { INSERT_STRING(parent, element, "false"); } \
2203 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2205 if (booleanPtr) { \
2206 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2207 } else { \
2208 INSERT_STRING(parent, element, NULL); \
2212 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2213 if ((longintPtr)) { \
2214 /* FIXME: locale sensitive */ \
2215 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2216 err = IE_NOMEM; \
2217 goto leave; \
2219 INSERT_STRING(parent, element, buffer) \
2220 free(buffer); (buffer) = NULL; \
2221 } else { INSERT_STRING(parent, element, NULL) } \
2224 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2225 if ((ulongintPtr)) { \
2226 /* FIXME: locale sensitive */ \
2227 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2228 err = IE_NOMEM; \
2229 goto leave; \
2231 INSERT_STRING(parent, element, buffer) \
2232 free(buffer); (buffer) = NULL; \
2233 } else { INSERT_STRING(parent, element, NULL) } \
2236 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2238 /* FIXME: locale sensitive */ \
2239 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2240 err = IE_NOMEM; \
2241 goto leave; \
2243 INSERT_STRING(parent, element, buffer) \
2244 free(buffer); (buffer) = NULL; \
2247 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2248 * new attribute. */
2249 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2251 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2252 (xmlChar *) (string)); \
2253 if (!attribute_node) { \
2254 isds_printf_message(context, _("Could not add %s " \
2255 "attribute to %s element"), \
2256 (attribute), (parent)->name); \
2257 err = IE_ERROR; \
2258 goto leave; \
2262 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2263 if (string) { \
2264 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2265 if (length > (maximum)) { \
2266 isds_printf_message(context, \
2267 ngettext("%s has more than %d characters", \
2268 "%s has more than %d characters", (maximum)), \
2269 (name), (maximum)); \
2270 err = IE_2BIG; \
2271 goto leave; \
2273 if (length < (minimum)) { \
2274 isds_printf_message(context, \
2275 ngettext("%s has less than %d characters", \
2276 "%s has less than %d characters", (minimum)), \
2277 (name), (minimum)); \
2278 err = IE_2SMALL; \
2279 goto leave; \
2284 #define INSERT_ELEMENT(child, parent, element) \
2286 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2287 if (!(child)) { \
2288 isds_printf_message(context, \
2289 _("Could not add %s child to %s element"), \
2290 (element), (parent)->name); \
2291 err = IE_ERROR; \
2292 goto leave; \
2297 /* Find child element by name in given XPath context and switch context onto
2298 * it. The child must be uniq and must exist. Otherwise fails.
2299 * @context is ISDS context
2300 * @child is child element name
2301 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2302 * into it child. In error case, the @xpath_ctx keeps original value. */
2303 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
2304 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
2305 isds_error err = IE_SUCCESS;
2306 xmlXPathObjectPtr result = NULL;
2308 if (!context) return IE_INVALID_CONTEXT;
2309 if (!child || !xpath_ctx) return IE_INVAL;
2311 /* Find child */
2312 result = xmlXPathEvalExpression(child, xpath_ctx);
2313 if (!result) {
2314 err = IE_XML;
2315 goto leave;
2318 /* No match */
2319 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2320 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2321 char *child_locale = _isds_utf82locale((char*) child);
2322 isds_printf_message(context,
2323 _("%s element does not contain %s child"),
2324 parent_locale, child_locale);
2325 free(child_locale);
2326 free(parent_locale);
2327 err = IE_NOEXIST;
2328 goto leave;
2331 /* More matches */
2332 if (result->nodesetval->nodeNr > 1) {
2333 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2334 char *child_locale = _isds_utf82locale((char*) child);
2335 isds_printf_message(context,
2336 _("%s element contains multiple %s children"),
2337 parent_locale, child_locale);
2338 free(child_locale);
2339 free(parent_locale);
2340 err = IE_NOTUNIQ;
2341 goto leave;
2344 /* Switch context */
2345 xpath_ctx->node = result->nodesetval->nodeTab[0];
2347 leave:
2348 xmlXPathFreeObject(result);
2349 return err;
2354 /* Find and convert XSD:gPersonName group in current node into structure
2355 * @context is ISDS context
2356 * @personName is automatically reallocated person name structure. If no member
2357 * value is found, will be freed.
2358 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2359 * elements
2360 * In case of error @personName will be freed. */
2361 static isds_error extract_gPersonName(struct isds_ctx *context,
2362 struct isds_PersonName **personName, xmlXPathContextPtr xpath_ctx) {
2363 isds_error err = IE_SUCCESS;
2364 xmlXPathObjectPtr result = NULL;
2366 if (!context) return IE_INVALID_CONTEXT;
2367 if (!personName) return IE_INVAL;
2368 isds_PersonName_free(personName);
2369 if (!xpath_ctx) return IE_INVAL;
2372 *personName = calloc(1, sizeof(**personName));
2373 if (!*personName) {
2374 err = IE_NOMEM;
2375 goto leave;
2378 EXTRACT_STRING("isds:pnFirstName", (*personName)->pnFirstName);
2379 EXTRACT_STRING("isds:pnMiddleName", (*personName)->pnMiddleName);
2380 EXTRACT_STRING("isds:pnLastName", (*personName)->pnLastName);
2381 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName)->pnLastNameAtBirth);
2383 if (!(*personName)->pnFirstName && !(*personName)->pnMiddleName &&
2384 !(*personName)->pnLastName && !(*personName)->pnLastNameAtBirth)
2385 isds_PersonName_free(personName);
2387 leave:
2388 if (err) isds_PersonName_free(personName);
2389 xmlXPathFreeObject(result);
2390 return err;
2394 /* Find and convert XSD:gAddress group in current node into structure
2395 * @context is ISDS context
2396 * @address is automatically reallocated address structure. If no member
2397 * value is found, will be freed.
2398 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2399 * elements
2400 * In case of error @address will be freed. */
2401 static isds_error extract_gAddress(struct isds_ctx *context,
2402 struct isds_Address **address, xmlXPathContextPtr xpath_ctx) {
2403 isds_error err = IE_SUCCESS;
2404 xmlXPathObjectPtr result = NULL;
2406 if (!context) return IE_INVALID_CONTEXT;
2407 if (!address) return IE_INVAL;
2408 isds_Address_free(address);
2409 if (!xpath_ctx) return IE_INVAL;
2412 *address = calloc(1, sizeof(**address));
2413 if (!*address) {
2414 err = IE_NOMEM;
2415 goto leave;
2418 EXTRACT_STRING("isds:adCity", (*address)->adCity);
2419 EXTRACT_STRING("isds:adStreet", (*address)->adStreet);
2420 EXTRACT_STRING("isds:adNumberInStreet", (*address)->adNumberInStreet);
2421 EXTRACT_STRING("isds:adNumberInMunicipality",
2422 (*address)->adNumberInMunicipality);
2423 EXTRACT_STRING("isds:adZipCode", (*address)->adZipCode);
2424 EXTRACT_STRING("isds:adState", (*address)->adState);
2426 if (!(*address)->adCity && !(*address)->adStreet &&
2427 !(*address)->adNumberInStreet &&
2428 !(*address)->adNumberInMunicipality &&
2429 !(*address)->adZipCode && !(*address)->adState)
2430 isds_Address_free(address);
2432 leave:
2433 if (err) isds_Address_free(address);
2434 xmlXPathFreeObject(result);
2435 return err;
2439 /* Find and convert isds:biDate element in current node into structure
2440 * @context is ISDS context
2441 * @biDate is automatically reallocated birth date structure. If no member
2442 * value is found, will be freed.
2443 * @xpath_ctx is XPath context with current node as parent for isds:biDate
2444 * element
2445 * In case of error @biDate will be freed. */
2446 static isds_error extract_BiDate(struct isds_ctx *context,
2447 struct tm **biDate, xmlXPathContextPtr xpath_ctx) {
2448 isds_error err = IE_SUCCESS;
2449 xmlXPathObjectPtr result = NULL;
2450 char *string = NULL;
2452 if (!context) return IE_INVALID_CONTEXT;
2453 if (!biDate) return IE_INVAL;
2454 zfree(*biDate);
2455 if (!xpath_ctx) return IE_INVAL;
2457 EXTRACT_STRING("isds:biDate", string);
2458 if (string) {
2459 *biDate = calloc(1, sizeof(**biDate));
2460 if (!*biDate) {
2461 err = IE_NOMEM;
2462 goto leave;
2464 err = datestring2tm((xmlChar *)string, *biDate);
2465 if (err) {
2466 if (err == IE_NOTSUP) {
2467 err = IE_ISDS;
2468 char *string_locale = _isds_utf82locale(string);
2469 isds_printf_message(context,
2470 _("Invalid isds:biDate value: %s"), string_locale);
2471 free(string_locale);
2473 goto leave;
2477 leave:
2478 if (err) zfree(*biDate);
2479 free(string);
2480 xmlXPathFreeObject(result);
2481 return err;
2485 /* Convert isds:dBOwnerInfo XML tree into structure
2486 * @context is ISDS context
2487 * @db_owner_info is automatically reallocated box owner info structure
2488 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
2489 * In case of error @db_owner_info will be freed. */
2490 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
2491 struct isds_DbOwnerInfo **db_owner_info,
2492 xmlXPathContextPtr xpath_ctx) {
2493 isds_error err = IE_SUCCESS;
2494 xmlXPathObjectPtr result = NULL;
2495 char *string = NULL;
2497 if (!context) return IE_INVALID_CONTEXT;
2498 if (!db_owner_info) return IE_INVAL;
2499 isds_DbOwnerInfo_free(db_owner_info);
2500 if (!xpath_ctx) return IE_INVAL;
2503 *db_owner_info = calloc(1, sizeof(**db_owner_info));
2504 if (!*db_owner_info) {
2505 err = IE_NOMEM;
2506 goto leave;
2509 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
2511 EXTRACT_STRING("isds:dbType", string);
2512 if (string) {
2513 (*db_owner_info)->dbType =
2514 calloc(1, sizeof(*((*db_owner_info)->dbType)));
2515 if (!(*db_owner_info)->dbType) {
2516 err = IE_NOMEM;
2517 goto leave;
2519 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
2520 if (err) {
2521 zfree((*db_owner_info)->dbType);
2522 if (err == IE_ENUM) {
2523 err = IE_ISDS;
2524 char *string_locale = _isds_utf82locale(string);
2525 isds_printf_message(context, _("Unknown isds:dbType: %s"),
2526 string_locale);
2527 free(string_locale);
2529 goto leave;
2531 zfree(string);
2534 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
2536 err = extract_gPersonName(context, &(*db_owner_info)->personName,
2537 xpath_ctx);
2538 if (err) goto leave;
2540 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
2542 (*db_owner_info)->birthInfo =
2543 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
2544 if (!(*db_owner_info)->birthInfo) {
2545 err = IE_NOMEM;
2546 goto leave;
2548 err = extract_BiDate(context, &(*db_owner_info)->birthInfo->biDate,
2549 xpath_ctx);
2550 if (err) goto leave;
2551 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
2552 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
2553 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
2554 if (!(*db_owner_info)->birthInfo->biDate &&
2555 !(*db_owner_info)->birthInfo->biCity &&
2556 !(*db_owner_info)->birthInfo->biCounty &&
2557 !(*db_owner_info)->birthInfo->biState)
2558 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
2560 err = extract_gAddress(context, &(*db_owner_info)->address, xpath_ctx);
2561 if (err) goto leave;
2563 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
2564 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
2565 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
2566 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
2567 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
2569 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
2571 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
2572 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
2573 (*db_owner_info)->dbOpenAddressing);
2575 leave:
2576 if (err) isds_DbOwnerInfo_free(db_owner_info);
2577 free(string);
2578 xmlXPathFreeObject(result);
2579 return err;
2583 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
2584 * @context is session context
2585 * @owner is libisds structure with box description
2586 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
2587 static isds_error insert_DbOwnerInfo(struct isds_ctx *context,
2588 const struct isds_DbOwnerInfo *owner, xmlNodePtr db_owner_info) {
2590 isds_error err = IE_SUCCESS;
2591 xmlNodePtr node;
2592 xmlChar *string = NULL;
2594 if (!context) return IE_INVALID_CONTEXT;
2595 if (!owner || !db_owner_info) return IE_INVAL;
2598 /* Build XSD:tDbOwnerInfo */
2599 CHECK_FOR_STRING_LENGTH(owner->dbID, 0, 7, "dbID")
2600 INSERT_STRING(db_owner_info, "dbID", owner->dbID);
2602 /* dbType */
2603 if (owner->dbType) {
2604 const xmlChar *type_string = isds_DbType2string(*(owner->dbType));
2605 if (!type_string) {
2606 isds_printf_message(context, _("Invalid dbType value: %d"),
2607 *(owner->dbType));
2608 err = IE_ENUM;
2609 goto leave;
2611 INSERT_STRING(db_owner_info, "dbType", type_string);
2613 INSERT_STRING(db_owner_info, "ic", owner->ic);
2614 if (owner->personName) {
2615 INSERT_STRING(db_owner_info, "pnFirstName",
2616 owner->personName->pnFirstName);
2617 INSERT_STRING(db_owner_info, "pnMiddleName",
2618 owner->personName->pnMiddleName);
2619 INSERT_STRING(db_owner_info, "pnLastName",
2620 owner->personName->pnLastName);
2621 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
2622 owner->personName->pnLastNameAtBirth);
2624 INSERT_STRING(db_owner_info, "firmName", owner->firmName);
2625 if (owner->birthInfo) {
2626 if (owner->birthInfo->biDate) {
2627 if (!tm2datestring(owner->birthInfo->biDate, &string))
2628 INSERT_STRING(db_owner_info, "biDate", string);
2629 free(string); string = NULL;
2631 INSERT_STRING(db_owner_info, "biCity", owner->birthInfo->biCity);
2632 INSERT_STRING(db_owner_info, "biCounty", owner->birthInfo->biCounty);
2633 INSERT_STRING(db_owner_info, "biState", owner->birthInfo->biState);
2635 if (owner->address) {
2636 INSERT_STRING(db_owner_info, "adCity", owner->address->adCity);
2637 INSERT_STRING(db_owner_info, "adStreet", owner->address->adStreet);
2638 INSERT_STRING(db_owner_info, "adNumberInStreet",
2639 owner->address->adNumberInStreet);
2640 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
2641 owner->address->adNumberInMunicipality);
2642 INSERT_STRING(db_owner_info, "adZipCode", owner->address->adZipCode);
2643 INSERT_STRING(db_owner_info, "adState", owner->address->adState);
2645 INSERT_STRING(db_owner_info, "nationality", owner->nationality);
2646 INSERT_STRING(db_owner_info, "email", owner->email);
2647 INSERT_STRING(db_owner_info, "telNumber", owner->telNumber);
2649 CHECK_FOR_STRING_LENGTH(owner->identifier, 0, 20, "identifier")
2650 INSERT_STRING(db_owner_info, "identifier", owner->identifier);
2652 CHECK_FOR_STRING_LENGTH(owner->registryCode, 0, 5, "registryCode")
2653 INSERT_STRING(db_owner_info, "registryCode", owner->registryCode);
2655 INSERT_LONGINT(db_owner_info, "dbState", owner->dbState, string);
2657 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", owner->dbEffectiveOVM);
2658 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
2659 owner->dbOpenAddressing);
2661 leave:
2662 free(string);
2663 return err;
2667 /* Convert XSD:tDbUserInfo XML tree into structure
2668 * @context is ISDS context
2669 * @db_user_info is automatically reallocated user info structure
2670 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
2671 * In case of error @db_user_info will be freed. */
2672 static isds_error extract_DbUserInfo(struct isds_ctx *context,
2673 struct isds_DbUserInfo **db_user_info, xmlXPathContextPtr xpath_ctx) {
2674 isds_error err = IE_SUCCESS;
2675 xmlXPathObjectPtr result = NULL;
2676 char *string = NULL;
2678 if (!context) return IE_INVALID_CONTEXT;
2679 if (!db_user_info) return IE_INVAL;
2680 isds_DbUserInfo_free(db_user_info);
2681 if (!xpath_ctx) return IE_INVAL;
2684 *db_user_info = calloc(1, sizeof(**db_user_info));
2685 if (!*db_user_info) {
2686 err = IE_NOMEM;
2687 goto leave;
2690 EXTRACT_STRING("isds:userID", (*db_user_info)->userID);
2692 EXTRACT_STRING("isds:userType", string);
2693 if (string) {
2694 (*db_user_info)->userType =
2695 calloc(1, sizeof(*((*db_user_info)->userType)));
2696 if (!(*db_user_info)->userType) {
2697 err = IE_NOMEM;
2698 goto leave;
2700 err = string2isds_UserType((xmlChar *)string,
2701 (*db_user_info)->userType);
2702 if (err) {
2703 zfree((*db_user_info)->userType);
2704 if (err == IE_ENUM) {
2705 err = IE_ISDS;
2706 char *string_locale = _isds_utf82locale(string);
2707 isds_printf_message(context,
2708 _("Unknown isds:userType value: %s"), string_locale);
2709 free(string_locale);
2711 goto leave;
2713 zfree(string);
2716 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info)->userPrivils, 0);
2718 (*db_user_info)->personName =
2719 calloc(1, sizeof(*((*db_user_info)->personName)));
2720 if (!(*db_user_info)->personName) {
2721 err = IE_NOMEM;
2722 goto leave;
2725 err = extract_gPersonName(context, &(*db_user_info)->personName,
2726 xpath_ctx);
2727 if (err) goto leave;
2729 err = extract_gAddress(context, &(*db_user_info)->address, xpath_ctx);
2730 if (err) goto leave;
2732 err = extract_BiDate(context, &(*db_user_info)->biDate, xpath_ctx);
2733 if (err) goto leave;
2735 EXTRACT_STRING("isds:ic", (*db_user_info)->ic);
2736 EXTRACT_STRING("isds:firmName", (*db_user_info)->firmName);
2738 EXTRACT_STRING("isds:caStreet", (*db_user_info)->caStreet);
2739 EXTRACT_STRING("isds:caCity", (*db_user_info)->caCity);
2740 EXTRACT_STRING("isds:caZipCode", (*db_user_info)->caZipCode);
2742 /* ???: Default value is "CZ" according specification. Should we provide
2743 * it? */
2744 EXTRACT_STRING("isds:caState", (*db_user_info)->caState);
2746 leave:
2747 if (err) isds_DbUserInfo_free(db_user_info);
2748 free(string);
2749 xmlXPathFreeObject(result);
2750 return err;
2754 /* Insert struct isds_DbUserInfo data (user description) into XML tree
2755 * @context is session context
2756 * @user is libisds structure with user description
2757 * @db_user_info is XML element of XSD:tDbUserInfo */
2758 static isds_error insert_DbUserInfo(struct isds_ctx *context,
2759 const struct isds_DbUserInfo *user, xmlNodePtr db_user_info) {
2761 isds_error err = IE_SUCCESS;
2762 xmlNodePtr node;
2763 xmlChar *string = NULL;
2765 if (!context) return IE_INVALID_CONTEXT;
2766 if (!user || !db_user_info) return IE_INVAL;
2768 /* Build XSD:tDbUserInfo */
2769 if (user->personName) {
2770 INSERT_STRING(db_user_info, "pnFirstName",
2771 user->personName->pnFirstName);
2772 INSERT_STRING(db_user_info, "pnMiddleName",
2773 user->personName->pnMiddleName);
2774 INSERT_STRING(db_user_info, "pnLastName",
2775 user->personName->pnLastName);
2776 INSERT_STRING(db_user_info, "pnLastNameAtBirth",
2777 user->personName->pnLastNameAtBirth);
2779 if (user->address) {
2780 INSERT_STRING(db_user_info, "adCity", user->address->adCity);
2781 INSERT_STRING(db_user_info, "adStreet", user->address->adStreet);
2782 INSERT_STRING(db_user_info, "adNumberInStreet",
2783 user->address->adNumberInStreet);
2784 INSERT_STRING(db_user_info, "adNumberInMunicipality",
2785 user->address->adNumberInMunicipality);
2786 INSERT_STRING(db_user_info, "adZipCode", user->address->adZipCode);
2787 INSERT_STRING(db_user_info, "adState", user->address->adState);
2789 if (user->biDate) {
2790 if (!tm2datestring(user->biDate, &string))
2791 INSERT_STRING(db_user_info, "biDate", string);
2792 zfree(string);
2794 CHECK_FOR_STRING_LENGTH(user->userID, 6, 12, "userID");
2795 INSERT_STRING(db_user_info, "userID", user->userID);
2797 /* userType */
2798 if (user->userType) {
2799 const xmlChar *type_string = isds_UserType2string(*(user->userType));
2800 if (!type_string) {
2801 isds_printf_message(context, _("Invalid userType value: %d"),
2802 *(user->userType));
2803 err = IE_ENUM;
2804 goto leave;
2806 INSERT_STRING(db_user_info, "userType", type_string);
2809 INSERT_LONGINT(db_user_info, "userPrivils", user->userPrivils, string);
2810 CHECK_FOR_STRING_LENGTH(user->ic, 0, 8, "ic")
2811 INSERT_STRING(db_user_info, "ic", user->ic);
2812 CHECK_FOR_STRING_LENGTH(user->firmName, 0, 100, "firmName")
2813 INSERT_STRING(db_user_info, "firmName", user->firmName);
2814 INSERT_STRING(db_user_info, "caStreet", user->caStreet);
2815 INSERT_STRING(db_user_info, "caCity", user->caCity);
2816 INSERT_STRING(db_user_info, "caZipCode", user->caZipCode);
2817 INSERT_STRING(db_user_info, "caState", user->caState);
2819 leave:
2820 free(string);
2821 return err;
2825 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
2826 * isds_envelope structure. The envelope is automatically allocated but not
2827 * reallocated. The date are just appended into envelope structure.
2828 * @context is ISDS context
2829 * @envelope is automatically allocated message envelope structure
2830 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
2831 * In case of error @envelope will be freed. */
2832 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
2833 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2834 isds_error err = IE_SUCCESS;
2835 xmlXPathObjectPtr result = NULL;
2837 if (!context) return IE_INVALID_CONTEXT;
2838 if (!envelope) return IE_INVAL;
2839 if (!xpath_ctx) return IE_INVAL;
2842 if (!*envelope) {
2843 /* Allocate envelope */
2844 *envelope = calloc(1, sizeof(**envelope));
2845 if (!*envelope) {
2846 err = IE_NOMEM;
2847 goto leave;
2849 } else {
2850 /* Else free former data */
2851 zfree((*envelope)->dmSenderOrgUnit);
2852 zfree((*envelope)->dmSenderOrgUnitNum);
2853 zfree((*envelope)->dbIDRecipient);
2854 zfree((*envelope)->dmRecipientOrgUnit);
2855 zfree((*envelope)->dmSenderOrgUnitNum);
2856 zfree((*envelope)->dmToHands);
2857 zfree((*envelope)->dmAnnotation);
2858 zfree((*envelope)->dmRecipientRefNumber);
2859 zfree((*envelope)->dmSenderRefNumber);
2860 zfree((*envelope)->dmRecipientIdent);
2861 zfree((*envelope)->dmSenderIdent);
2862 zfree((*envelope)->dmLegalTitleLaw);
2863 zfree((*envelope)->dmLegalTitleYear);
2864 zfree((*envelope)->dmLegalTitleSect);
2865 zfree((*envelope)->dmLegalTitlePar);
2866 zfree((*envelope)->dmLegalTitlePoint);
2867 zfree((*envelope)->dmPersonalDelivery);
2868 zfree((*envelope)->dmAllowSubstDelivery);
2871 /* Extract envelope elements added by sender or ISDS
2872 * (XSD: gMessageEnvelopeSub type) */
2873 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
2874 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
2875 (*envelope)->dmSenderOrgUnitNum, 0);
2876 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
2877 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
2878 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
2879 (*envelope)->dmSenderOrgUnitNum, 0);
2880 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
2881 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
2882 EXTRACT_STRING("isds:dmRecipientRefNumber",
2883 (*envelope)->dmRecipientRefNumber);
2884 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
2885 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
2886 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
2888 /* Extract envelope elements regarding law reference */
2889 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
2890 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
2891 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
2892 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
2893 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
2895 /* Extract envelope other elements */
2896 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
2897 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
2898 (*envelope)->dmAllowSubstDelivery);
2900 leave:
2901 if (err) isds_envelope_free(envelope);
2902 xmlXPathFreeObject(result);
2903 return err;
2908 /* Convert XSD gMessageEnvelope group of elements from XML tree into
2909 * isds_envelope structure. The envelope is automatically allocated but not
2910 * reallocated. The date are just appended into envelope structure.
2911 * @context is ISDS context
2912 * @envelope is automatically allocated message envelope structure
2913 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
2914 * In case of error @envelope will be freed. */
2915 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
2916 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2917 isds_error err = IE_SUCCESS;
2918 xmlXPathObjectPtr result = NULL;
2920 if (!context) return IE_INVALID_CONTEXT;
2921 if (!envelope) return IE_INVAL;
2922 if (!xpath_ctx) return IE_INVAL;
2925 if (!*envelope) {
2926 /* Allocate envelope */
2927 *envelope = calloc(1, sizeof(**envelope));
2928 if (!*envelope) {
2929 err = IE_NOMEM;
2930 goto leave;
2932 } else {
2933 /* Else free former data */
2934 zfree((*envelope)->dmID);
2935 zfree((*envelope)->dbIDSender);
2936 zfree((*envelope)->dmSender);
2937 zfree((*envelope)->dmSenderAddress);
2938 zfree((*envelope)->dmSenderType);
2939 zfree((*envelope)->dmRecipient);
2940 zfree((*envelope)->dmRecipientAddress);
2941 zfree((*envelope)->dmAmbiguousRecipient);
2944 /* Extract envelope elements added by ISDS
2945 * (XSD: gMessageEnvelope type) */
2946 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
2947 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
2948 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
2949 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
2950 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
2951 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
2952 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
2953 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
2954 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
2955 (*envelope)->dmAmbiguousRecipient);
2957 /* Extract envelope elements added by sender and ISDS
2958 * (XSD: gMessageEnvelope type) */
2959 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
2960 if (err) goto leave;
2962 leave:
2963 if (err) isds_envelope_free(envelope);
2964 xmlXPathFreeObject(result);
2965 return err;
2969 /* Convert other envelope elements from XML tree into isds_envelope structure:
2970 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
2971 * The envelope is automatically allocated but not reallocated.
2972 * The data are just appended into envelope structure.
2973 * @context is ISDS context
2974 * @envelope is automatically allocated message envelope structure
2975 * @xpath_ctx is XPath context with current node as parent desired elements
2976 * In case of error @envelope will be freed. */
2977 static isds_error append_status_size_times(struct isds_ctx *context,
2978 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2979 isds_error err = IE_SUCCESS;
2980 xmlXPathObjectPtr result = NULL;
2981 char *string = NULL;
2982 unsigned long int *unumber = NULL;
2984 if (!context) return IE_INVALID_CONTEXT;
2985 if (!envelope) return IE_INVAL;
2986 if (!xpath_ctx) return IE_INVAL;
2989 if (!*envelope) {
2990 /* Allocate new */
2991 *envelope = calloc(1, sizeof(**envelope));
2992 if (!*envelope) {
2993 err = IE_NOMEM;
2994 goto leave;
2996 } else {
2997 /* Free old data */
2998 zfree((*envelope)->dmMessageStatus);
2999 zfree((*envelope)->dmAttachmentSize);
3000 zfree((*envelope)->dmDeliveryTime);
3001 zfree((*envelope)->dmAcceptanceTime);
3005 /* dmMessageStatus element is mandatory */
3006 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
3007 if (!unumber) {
3008 isds_log_message(context,
3009 _("Missing mandatory sisds:dmMessageStatus integer"));
3010 err = IE_ISDS;
3011 goto leave;
3013 err = uint2isds_message_status(context, unumber,
3014 &((*envelope)->dmMessageStatus));
3015 if (err) {
3016 if (err == IE_ENUM) err = IE_ISDS;
3017 goto leave;
3019 free(unumber); unumber = NULL;
3021 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
3024 EXTRACT_STRING("sisds:dmDeliveryTime", string);
3025 if (string) {
3026 err = timestring2timeval((xmlChar *) string,
3027 &((*envelope)->dmDeliveryTime));
3028 if (err) {
3029 char *string_locale = _isds_utf82locale(string);
3030 if (err == IE_DATE) err = IE_ISDS;
3031 isds_printf_message(context,
3032 _("Could not convert dmDeliveryTime as ISO time: %s"),
3033 string_locale);
3034 free(string_locale);
3035 goto leave;
3037 zfree(string);
3040 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
3041 if (string) {
3042 err = timestring2timeval((xmlChar *) string,
3043 &((*envelope)->dmAcceptanceTime));
3044 if (err) {
3045 char *string_locale = _isds_utf82locale(string);
3046 if (err == IE_DATE) err = IE_ISDS;
3047 isds_printf_message(context,
3048 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3049 string_locale);
3050 free(string_locale);
3051 goto leave;
3053 zfree(string);
3056 leave:
3057 if (err) isds_envelope_free(envelope);
3058 free(unumber);
3059 free(string);
3060 xmlXPathFreeObject(result);
3061 return err;
3065 /* Convert message type attribute of current element into isds_envelope
3066 * structure.
3067 * TODO: This function can be incorporated into append_status_size_times() as
3068 * they are called always together.
3069 * The envelope is automatically allocated but not reallocated.
3070 * The data are just appended into envelope structure.
3071 * @context is ISDS context
3072 * @envelope is automatically allocated message envelope structure
3073 * @xpath_ctx is XPath context with current node as parent of attribute
3074 * carrying message type
3075 * In case of error @envelope will be freed. */
3076 static isds_error append_message_type(struct isds_ctx *context,
3077 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3078 isds_error err = IE_SUCCESS;
3080 if (!context) return IE_INVALID_CONTEXT;
3081 if (!envelope) return IE_INVAL;
3082 if (!xpath_ctx) return IE_INVAL;
3085 if (!*envelope) {
3086 /* Allocate new */
3087 *envelope = calloc(1, sizeof(**envelope));
3088 if (!*envelope) {
3089 err = IE_NOMEM;
3090 goto leave;
3092 } else {
3093 /* Free old data */
3094 zfree((*envelope)->dmType);
3098 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope)->dmType, 0);
3100 if (!(*envelope)->dmType) {
3101 /* Use default value */
3102 (*envelope)->dmType = strdup("V");
3103 if (!(*envelope)->dmType) {
3104 err = IE_NOMEM;
3105 goto leave;
3107 } else if (1 != xmlUTF8Strlen((xmlChar *) (*envelope)->dmType)) {
3108 char *type_locale = _isds_utf82locale((*envelope)->dmType);
3109 isds_printf_message(context,
3110 _("Message type in dmType attribute is not 1 character long: "
3111 "%s"),
3112 type_locale);
3113 free(type_locale);
3114 err = IE_ISDS;
3115 goto leave;
3118 leave:
3119 if (err) isds_envelope_free(envelope);
3120 return err;
3124 /* Convert dmType isds_envelope member into XML attribute and append it to
3125 * current node.
3126 * @context is ISDS context
3127 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3128 * @dm_envelope is XML element the resulting attribute will be appended to.
3129 * @return error code, in case of error context' message is filled. */
3130 static isds_error insert_message_type(struct isds_ctx *context,
3131 const char *type, xmlNodePtr dm_envelope) {
3132 isds_error err = IE_SUCCESS;
3133 xmlAttrPtr attribute_node;
3135 if (!context) return IE_INVALID_CONTEXT;
3136 if (!dm_envelope) return IE_INVAL;
3138 /* Insert optional message type */
3139 if (type) {
3140 if (1 != xmlUTF8Strlen((xmlChar *) type)) {
3141 char *type_locale = _isds_utf82locale(type);
3142 isds_printf_message(context,
3143 _("Message type in envelope is not 1 character long: %s"),
3144 type_locale);
3145 free(type_locale);
3146 err = IE_INVAL;
3147 goto leave;
3149 INSERT_STRING_ATTRIBUTE(dm_envelope, "dmType", type);
3152 leave:
3153 return err;
3157 /* Extract message document into reallocated document structure
3158 * @context is ISDS context
3159 * @document is automatically reallocated message documents structure
3160 * @xpath_ctx is XPath context with current node as isds:dmFile
3161 * In case of error @document will be freed. */
3162 static isds_error extract_document(struct isds_ctx *context,
3163 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
3164 isds_error err = IE_SUCCESS;
3165 xmlXPathObjectPtr result = NULL;
3166 xmlNodePtr file_node = xpath_ctx->node;
3167 char *string = NULL;
3169 if (!context) return IE_INVALID_CONTEXT;
3170 if (!document) return IE_INVAL;
3171 isds_document_free(document);
3172 if (!xpath_ctx) return IE_INVAL;
3174 *document = calloc(1, sizeof(**document));
3175 if (!*document) {
3176 err = IE_NOMEM;
3177 goto leave;
3180 /* Extract document meta data */
3181 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
3182 if (context->normalize_mime_type) {
3183 char *normalized_type =
3184 isds_normalize_mime_type((*document)->dmMimeType);
3185 if (normalized_type && normalized_type != (*document)->dmMimeType) {
3186 char *new_type = strdup(normalized_type);
3187 if (!new_type) {
3188 isds_printf_message(context,
3189 _("Not enough memory to normalize document MIME type"));
3190 err = IE_NOMEM;
3191 goto leave;
3193 free((*document)->dmMimeType);
3194 (*document)->dmMimeType = new_type;
3198 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
3199 err = string2isds_FileMetaType((xmlChar*)string,
3200 &((*document)->dmFileMetaType));
3201 if (err) {
3202 char *meta_type_locale = _isds_utf82locale(string);
3203 isds_printf_message(context,
3204 _("Document has invalid dmFileMetaType attribute value: %s"),
3205 meta_type_locale);
3206 free(meta_type_locale);
3207 err = IE_ISDS;
3208 goto leave;
3210 zfree(string);
3212 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
3213 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
3214 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
3215 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
3218 /* Extract document data.
3219 * Base64 encoded blob or XML subtree must be presented. */
3221 /* Check for dmEncodedContent */
3222 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
3223 xpath_ctx);
3224 if (!result) {
3225 err = IE_XML;
3226 goto leave;
3229 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3230 /* Here we have Base64 blob */
3231 (*document)->is_xml = 0;
3233 if (result->nodesetval->nodeNr > 1) {
3234 isds_printf_message(context,
3235 _("Document has more dmEncodedContent elements"));
3236 err = IE_ISDS;
3237 goto leave;
3240 xmlXPathFreeObject(result); result = NULL;
3241 EXTRACT_STRING("isds:dmEncodedContent", string);
3243 /* Decode non-empty document */
3244 if (string && string[0] != '\0') {
3245 (*document)->data_length =
3246 _isds_b64decode(string, &((*document)->data));
3247 if ((*document)->data_length == (size_t) -1) {
3248 isds_printf_message(context,
3249 _("Error while Base64-decoding document content"));
3250 err = IE_ERROR;
3251 goto leave;
3254 } else {
3255 /* No Base64 blob, try XML document */
3256 xmlXPathFreeObject(result); result = NULL;
3257 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
3258 xpath_ctx);
3259 if (!result) {
3260 err = IE_XML;
3261 goto leave;
3264 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3265 /* Here we have XML document */
3266 (*document)->is_xml = 1;
3268 if (result->nodesetval->nodeNr > 1) {
3269 isds_printf_message(context,
3270 _("Document has more dmXMLContent elements"));
3271 err = IE_ISDS;
3272 goto leave;
3275 /* XXX: We cannot serialize the content simply because:
3276 * - XML document may point out of its scope (e.g. to message
3277 * envelope)
3278 * - isds:dmXMLContent can contain more elements, no element,
3279 * a text node only
3280 * - it's not the XML way
3281 * Thus we provide the only right solution: XML DOM. Let's
3282 * application to cope with this hot potato :) */
3283 (*document)->xml_node_list =
3284 result->nodesetval->nodeTab[0]->children;
3285 } else {
3286 /* No base64 blob, nor XML document */
3287 isds_printf_message(context,
3288 _("Document has no dmEncodedContent, nor dmXMLContent "
3289 "element"));
3290 err = IE_ISDS;
3291 goto leave;
3296 leave:
3297 if (err) isds_document_free(document);
3298 free(string);
3299 xmlXPathFreeObject(result);
3300 xpath_ctx->node = file_node;
3301 return err;
3306 /* Extract message documents into reallocated list of documents
3307 * @context is ISDS context
3308 * @documents is automatically reallocated message documents list structure
3309 * @xpath_ctx is XPath context with current node as XSD tFilesArray
3310 * In case of error @documents will be freed. */
3311 static isds_error extract_documents(struct isds_ctx *context,
3312 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
3313 isds_error err = IE_SUCCESS;
3314 xmlXPathObjectPtr result = NULL;
3315 xmlNodePtr files_node = xpath_ctx->node;
3316 struct isds_list *document, *prev_document;
3318 if (!context) return IE_INVALID_CONTEXT;
3319 if (!documents) return IE_INVAL;
3320 isds_list_free(documents);
3321 if (!xpath_ctx) return IE_INVAL;
3323 /* Find documents */
3324 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
3325 if (!result) {
3326 err = IE_XML;
3327 goto leave;
3330 /* No match */
3331 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3332 isds_printf_message(context,
3333 _("Message does not contain any document"));
3334 err = IE_ISDS;
3335 goto leave;
3339 /* Iterate over documents */
3340 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
3342 /* Allocate and append list item */
3343 document = calloc(1, sizeof(*document));
3344 if (!document) {
3345 err = IE_NOMEM;
3346 goto leave;
3348 document->destructor = (void (*)(void **))isds_document_free;
3349 if (i == 0) *documents = document;
3350 else prev_document->next = document;
3351 prev_document = document;
3353 /* Extract document */
3354 xpath_ctx->node = result->nodesetval->nodeTab[i];
3355 err = extract_document(context,
3356 (struct isds_document **) &(document->data), xpath_ctx);
3357 if (err) goto leave;
3361 leave:
3362 if (err) isds_list_free(documents);
3363 xmlXPathFreeObject(result);
3364 xpath_ctx->node = files_node;
3365 return err;
3369 /* Convert isds:dmRecord XML tree into structure
3370 * @context is ISDS context
3371 * @envelope is automatically reallocated message envelope structure
3372 * @xpath_ctx is XPath context with current node as isds:dmRecord element
3373 * In case of error @envelope will be freed. */
3374 static isds_error extract_DmRecord(struct isds_ctx *context,
3375 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3376 isds_error err = IE_SUCCESS;
3377 xmlXPathObjectPtr result = NULL;
3379 if (!context) return IE_INVALID_CONTEXT;
3380 if (!envelope) return IE_INVAL;
3381 isds_envelope_free(envelope);
3382 if (!xpath_ctx) return IE_INVAL;
3385 *envelope = calloc(1, sizeof(**envelope));
3386 if (!*envelope) {
3387 err = IE_NOMEM;
3388 goto leave;
3392 /* Extract tRecord data */
3393 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
3395 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
3396 * dmAcceptanceTime. */
3397 err = append_status_size_times(context, envelope, xpath_ctx);
3398 if (err) goto leave;
3400 /* Extract envelope elements added by sender and ISDS
3401 * (XSD: gMessageEnvelope type) */
3402 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
3403 if (err) goto leave;
3404 /* dmOVM can not be obtained from ISDS */
3406 /* Get message type */
3407 err = append_message_type(context, envelope, xpath_ctx);
3408 if (err) goto leave;
3411 leave:
3412 if (err) isds_envelope_free(envelope);
3413 xmlXPathFreeObject(result);
3414 return err;
3418 /* Find and convert isds:dmHash XML tree into structure
3419 * @context is ISDS context
3420 * @envelope is automatically reallocated message hash structure
3421 * @xpath_ctx is XPath context with current node containing isds:dmHash child
3422 * In case of error @hash will be freed. */
3423 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
3424 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
3425 isds_error err = IE_SUCCESS;
3426 xmlNodePtr old_ctx_node;
3427 xmlXPathObjectPtr result = NULL;
3428 char *string = NULL;
3430 if (!context) return IE_INVALID_CONTEXT;
3431 if (!hash) return IE_INVAL;
3432 isds_hash_free(hash);
3433 if (!xpath_ctx) return IE_INVAL;
3435 old_ctx_node = xpath_ctx->node;
3437 *hash = calloc(1, sizeof(**hash));
3438 if (!*hash) {
3439 err = IE_NOMEM;
3440 goto leave;
3443 /* Locate dmHash */
3444 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
3445 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
3446 err = IE_ISDS;
3447 goto leave;
3449 if (err) {
3450 err = IE_ERROR;
3451 goto leave;
3454 /* Get hash algorithm */
3455 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
3456 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
3457 if (err) {
3458 if (err == IE_ENUM) {
3459 char *string_locale = _isds_utf82locale(string);
3460 isds_printf_message(context, _("Unsupported hash algorithm: %s"),
3461 string_locale);
3462 free(string_locale);
3464 goto leave;
3466 zfree(string);
3468 /* Get hash value */
3469 EXTRACT_STRING(".", string);
3470 if (!string) {
3471 isds_printf_message(context,
3472 _("sisds:dmHash element is missing hash value"));
3473 err = IE_ISDS;
3474 goto leave;
3476 (*hash)->length = _isds_b64decode(string, &((*hash)->value));
3477 if ((*hash)->length == (size_t) -1) {
3478 isds_printf_message(context,
3479 _("Error while Base64-decoding hash value"));
3480 err = IE_ERROR;
3481 goto leave;
3484 leave:
3485 if (err) isds_hash_free(hash);
3486 free(string);
3487 xmlXPathFreeObject(result);
3488 xpath_ctx->node = old_ctx_node;
3489 return err;
3493 /* Find and append isds:dmQTimestamp XML tree into envelope.
3494 * Because one service is allowed to miss time-stamp content, and we think
3495 * other could too (flaw in specification), this function is deliberated and
3496 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
3497 * @context is ISDS context
3498 * @envelope is automatically allocated envelope structure
3499 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
3500 * child
3501 * In case of error @envelope will be freed. */
3502 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
3503 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3504 isds_error err = IE_SUCCESS;
3505 xmlXPathObjectPtr result = NULL;
3506 char *string = NULL;
3508 if (!context) return IE_INVALID_CONTEXT;
3509 if (!envelope) return IE_INVAL;
3510 if (!xpath_ctx) {
3511 isds_envelope_free(envelope);
3512 return IE_INVAL;
3515 if (!*envelope) {
3516 *envelope = calloc(1, sizeof(**envelope));
3517 if (!*envelope) {
3518 err = IE_NOMEM;
3519 goto leave;
3521 } else {
3522 zfree((*envelope)->timestamp);
3523 (*envelope)->timestamp_length = 0;
3526 /* Get dmQTimestamp */
3527 EXTRACT_STRING("sisds:dmQTimestamp", string);
3528 if (!string) {
3529 isds_log(ILF_ISDS, ILL_INFO, _("Missing dmQTimestamp element content\n"));
3530 goto leave;
3532 (*envelope)->timestamp_length =
3533 _isds_b64decode(string, &((*envelope)->timestamp));
3534 if ((*envelope)->timestamp_length == (size_t) -1) {
3535 isds_printf_message(context,
3536 _("Error while Base64-decoding time stamp value"));
3537 err = IE_ERROR;
3538 goto leave;
3541 leave:
3542 if (err) isds_envelope_free(envelope);
3543 free(string);
3544 xmlXPathFreeObject(result);
3545 return err;
3549 /* Convert XSD tReturnedMessage XML tree into message structure.
3550 * It does not store serialized XML tree into message->raw.
3551 * It does store (pointer to) parsed XML tree into message->xml if needed.
3552 * @context is ISDS context
3553 * @include_documents Use true if documents must be extracted
3554 * (tReturnedMessage XSD type), use false if documents shall be omitted
3555 * (tReturnedMessageEnvelope).
3556 * @message is automatically reallocated message structure
3557 * @xpath_ctx is XPath context with current node as tReturnedMessage element
3558 * type
3559 * In case of error @message will be freed. */
3560 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
3561 const _Bool include_documents, struct isds_message **message,
3562 xmlXPathContextPtr xpath_ctx) {
3563 isds_error err = IE_SUCCESS;
3564 xmlNodePtr message_node;
3566 if (!context) return IE_INVALID_CONTEXT;
3567 if (!message) return IE_INVAL;
3568 isds_message_free(message);
3569 if (!xpath_ctx) return IE_INVAL;
3572 *message = calloc(1, sizeof(**message));
3573 if (!*message) {
3574 err = IE_NOMEM;
3575 goto leave;
3578 /* Save message XPATH context node */
3579 message_node = xpath_ctx->node;
3582 /* Extract dmDM */
3583 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
3584 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
3585 if (err) { err = IE_ERROR; goto leave; }
3586 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
3587 if (err) goto leave;
3589 if (include_documents) {
3590 struct isds_list *item;
3592 /* Extract dmFiles */
3593 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
3594 xpath_ctx);
3595 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
3596 err = IE_ISDS; goto leave;
3598 if (err) { err = IE_ERROR; goto leave; }
3599 err = extract_documents(context, &((*message)->documents), xpath_ctx);
3600 if (err) goto leave;
3602 /* Store xmlDoc of this message if needed */
3603 /* Only if we got a XML document in all the documents. */
3604 for (item = (*message)->documents; item; item = item->next) {
3605 if (item->data && ((struct isds_document *)item->data)->is_xml) {
3606 (*message)->xml = xpath_ctx->doc;
3607 break;
3613 /* Restore context to message */
3614 xpath_ctx->node = message_node;
3616 /* Extract dmHash */
3617 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
3618 xpath_ctx);
3619 if (err) goto leave;
3621 /* Extract dmQTimestamp, */
3622 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
3623 xpath_ctx);
3624 if (err) goto leave;
3626 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
3627 * dmAcceptanceTime. */
3628 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
3629 if (err) goto leave;
3631 /* Get message type */
3632 err = append_message_type(context, &((*message)->envelope), xpath_ctx);
3633 if (err) goto leave;
3635 leave:
3636 if (err) isds_message_free(message);
3637 return err;
3641 /* Extract message event into reallocated isds_event structure
3642 * @context is ISDS context
3643 * @event is automatically reallocated message event structure
3644 * @xpath_ctx is XPath context with current node as isds:dmEvent
3645 * In case of error @event will be freed. */
3646 static isds_error extract_event(struct isds_ctx *context,
3647 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
3648 isds_error err = IE_SUCCESS;
3649 xmlXPathObjectPtr result = NULL;
3650 xmlNodePtr event_node = xpath_ctx->node;
3651 char *string = NULL;
3653 if (!context) return IE_INVALID_CONTEXT;
3654 if (!event) return IE_INVAL;
3655 isds_event_free(event);
3656 if (!xpath_ctx) return IE_INVAL;
3658 *event = calloc(1, sizeof(**event));
3659 if (!*event) {
3660 err = IE_NOMEM;
3661 goto leave;
3664 /* Extract event data.
3665 * All elements are optional according XSD. That's funny. */
3666 EXTRACT_STRING("sisds:dmEventTime", string);
3667 if (string) {
3668 err = timestring2timeval((xmlChar *) string, &((*event)->time));
3669 if (err) {
3670 char *string_locale = _isds_utf82locale(string);
3671 if (err == IE_DATE) err = IE_ISDS;
3672 isds_printf_message(context,
3673 _("Could not convert dmEventTime as ISO time: %s"),
3674 string_locale);
3675 free(string_locale);
3676 goto leave;
3678 zfree(string);
3681 /* dmEventDescr element has prefix and the rest */
3682 EXTRACT_STRING("sisds:dmEventDescr", string);
3683 if (string) {
3684 err = eventstring2event((xmlChar *) string, *event);
3685 if (err) goto leave;
3686 zfree(string);
3689 leave:
3690 if (err) isds_event_free(event);
3691 free(string);
3692 xmlXPathFreeObject(result);
3693 xpath_ctx->node = event_node;
3694 return err;
3698 /* Convert element of XSD tEventsArray type from XML tree into
3699 * isds_list of isds_event's structure. The list is automatically reallocated.
3700 * @context is ISDS context
3701 * @events is automatically reallocated list of event structures
3702 * @xpath_ctx is XPath context with current node as tEventsArray
3703 * In case of error @events will be freed. */
3704 static isds_error extract_events(struct isds_ctx *context,
3705 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
3706 isds_error err = IE_SUCCESS;
3707 xmlXPathObjectPtr result = NULL;
3708 xmlNodePtr events_node = xpath_ctx->node;
3709 struct isds_list *event, *prev_event = NULL;
3711 if (!context) return IE_INVALID_CONTEXT;
3712 if (!events) return IE_INVAL;
3713 if (!xpath_ctx) return IE_INVAL;
3715 /* Free old list */
3716 isds_list_free(events);
3718 /* Find events */
3719 result = xmlXPathEvalExpression(BAD_CAST "sisds:dmEvent", xpath_ctx);
3720 if (!result) {
3721 err = IE_XML;
3722 goto leave;
3725 /* No match */
3726 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3727 isds_printf_message(context,
3728 _("Delivery info does not contain any event"));
3729 err = IE_ISDS;
3730 goto leave;
3734 /* Iterate over events */
3735 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
3737 /* Allocate and append list item */
3738 event = calloc(1, sizeof(*event));
3739 if (!event) {
3740 err = IE_NOMEM;
3741 goto leave;
3743 event->destructor = (void (*)(void **))isds_event_free;
3744 if (i == 0) *events = event;
3745 else prev_event->next = event;
3746 prev_event = event;
3748 /* Extract event */
3749 xpath_ctx->node = result->nodesetval->nodeTab[i];
3750 err = extract_event(context,
3751 (struct isds_event **) &(event->data), xpath_ctx);
3752 if (err) goto leave;
3756 leave:
3757 if (err) isds_list_free(events);
3758 xmlXPathFreeObject(result);
3759 xpath_ctx->node = events_node;
3760 return err;
3764 /* Insert Base64 encoded data as element with text child.
3765 * @context is session context
3766 * @parent is XML node to append @element with @data as child
3767 * @ns is XML namespace of @element, use NULL to inherit from @parent
3768 * @element is UTF-8 encoded name of new element
3769 * @data is bit stream to encode into @element
3770 * @length is size of @data in bytes
3771 * @return standard error code and fill long error message if needed */
3772 static isds_error insert_base64_encoded_string(struct isds_ctx *context,
3773 xmlNodePtr parent, const xmlNsPtr ns, const char *element,
3774 const void *data, size_t length) {
3775 isds_error err = IE_SUCCESS;
3776 xmlNodePtr node;
3778 if (!context) return IE_INVALID_CONTEXT;
3779 if (!data && length > 0) return IE_INVAL;
3780 if (!parent || !element) return IE_INVAL;
3782 xmlChar *base64data = NULL;
3783 base64data = (xmlChar *) _isds_b64encode(data, length);
3784 if (!base64data) {
3785 isds_printf_message(context,
3786 ngettext("Not enough memory to encode %zd bytes into Base64",
3787 "Not enough memory to encode %zd bytes into Base64",
3788 length),
3789 length);
3790 err = IE_NOMEM;
3791 goto leave;
3793 INSERT_STRING_WITH_NS(parent, ns, element, base64data);
3795 leave:
3796 free(base64data);
3797 return err;
3801 /* Convert isds_document structure into XML tree and append to dmFiles node.
3802 * @context is session context
3803 * @document is ISDS document
3804 * @dm_files is XML element the resulting tree will be appended to as a child.
3805 * @return error code, in case of error context' message is filled. */
3806 static isds_error insert_document(struct isds_ctx *context,
3807 struct isds_document *document, xmlNodePtr dm_files) {
3808 isds_error err = IE_SUCCESS;
3809 xmlNodePtr new_file = NULL, file = NULL, node;
3810 xmlAttrPtr attribute_node;
3812 if (!context) return IE_INVALID_CONTEXT;
3813 if (!document || !dm_files) return IE_INVAL;
3815 /* Allocate new dmFile */
3816 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
3817 if (!new_file) {
3818 isds_printf_message(context, _("Could not allocate main dmFile"));
3819 err = IE_ERROR;
3820 goto leave;
3822 /* Append the new dmFile.
3823 * XXX: Main document must go first */
3824 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
3825 file = xmlAddPrevSibling(dm_files->children, new_file);
3826 else
3827 file = xmlAddChild(dm_files, new_file);
3829 if (!file) {
3830 xmlFreeNode(new_file); new_file = NULL;
3831 isds_printf_message(context, _("Could not add dmFile child to "
3832 "%s element"), dm_files->name);
3833 err = IE_ERROR;
3834 goto leave;
3837 /* @dmMimeType is required */
3838 if (!document->dmMimeType) {
3839 isds_log_message(context,
3840 _("Document is missing mandatory MIME type definition"));
3841 err = IE_INVAL;
3842 goto leave;
3844 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
3846 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
3847 if (!string) {
3848 isds_printf_message(context,
3849 _("Document has unknown dmFileMetaType: %ld"),
3850 document->dmFileMetaType);
3851 err = IE_ENUM;
3852 goto leave;
3854 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
3856 if (document->dmFileGuid) {
3857 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
3859 if (document->dmUpFileGuid) {
3860 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
3863 /* @dmFileDescr is required */
3864 if (!document->dmFileDescr) {
3865 isds_log_message(context,
3866 _("Document is missing mandatory description (title)"));
3867 err = IE_INVAL;
3868 goto leave;
3870 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
3872 if (document->dmFormat) {
3873 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
3877 /* Insert content (body) of the document. */
3878 if (document->is_xml) {
3879 /* XML document requested */
3881 /* Allocate new dmXMLContent */
3882 xmlNodePtr xmlcontent = xmlNewNode(file->ns, BAD_CAST "dmXMLContent");
3883 if (!xmlcontent) {
3884 isds_printf_message(context,
3885 _("Could not allocate dmXMLContent elemement"));
3886 err = IE_ERROR;
3887 goto leave;
3889 /* Append it */
3890 node = xmlAddChild(file, xmlcontent);
3891 if (!node) {
3892 xmlFreeNode(xmlcontent); xmlcontent = NULL;
3893 isds_printf_message(context,
3894 _("Could not add dmXMLContent child to %s element"),
3895 file->name);
3896 err = IE_ERROR;
3897 goto leave;
3900 /* Copy non-empty node list */
3901 if (document->xml_node_list) {
3902 xmlNodePtr content = xmlDocCopyNodeList(node->doc,
3903 document->xml_node_list);
3904 if (!content) {
3905 isds_printf_message(context,
3906 _("Not enough memory to copy XML document"));
3907 err = IE_NOMEM;
3908 goto leave;
3911 if (!xmlAddChildList(node, content)) {
3912 xmlFreeNodeList(content);
3913 isds_printf_message(context,
3914 _("Error while adding XML document into dmXMLContent"));
3915 err = IE_XML;
3916 goto leave;
3918 /* XXX: We cannot free the content here because it's part of node's
3919 * document since now. It will be freed with it automatically. */
3921 } else {
3922 /* Binary document requested */
3923 err = insert_base64_encoded_string(context, file, NULL, "dmEncodedContent",
3924 document->data, document->data_length);
3925 if (err) goto leave;
3928 leave:
3929 return err;
3933 /* Append XSD tMStatus XML tree into isds_message_copy structure.
3934 * The copy must be preallocated, the date are just appended into structure.
3935 * @context is ISDS context
3936 * @copy is message copy structure
3937 * @xpath_ctx is XPath context with current node as tMStatus */
3938 static isds_error append_TMStatus(struct isds_ctx *context,
3939 struct isds_message_copy *copy, xmlXPathContextPtr xpath_ctx) {
3940 isds_error err = IE_SUCCESS;
3941 xmlXPathObjectPtr result = NULL;
3942 char *code = NULL, *message = NULL;
3944 if (!context) return IE_INVALID_CONTEXT;
3945 if (!copy || !xpath_ctx) return IE_INVAL;
3947 /* Free old values */
3948 zfree(copy->dmStatus);
3949 zfree(copy->dmID);
3951 /* Get error specific to this copy */
3952 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code);
3953 if (!code) {
3954 isds_log_message(context,
3955 _("Missing isds:dmStatusCode under "
3956 "XSD:tMStatus type element"));
3957 err = IE_ISDS;
3958 goto leave;
3961 if (xmlStrcmp((const xmlChar *)code, BAD_CAST "0000")) {
3962 /* This copy failed */
3963 copy->error = IE_ISDS;
3964 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message);
3965 if (message) {
3966 copy->dmStatus = _isds_astrcat3(code, ": ", message);
3967 if (!copy->dmStatus) {
3968 copy->dmStatus = code;
3969 code = NULL;
3971 } else {
3972 copy->dmStatus = code;
3973 code = NULL;
3975 } else {
3976 /* This copy succeeded. In this case only, message ID is valid */
3977 copy->error = IE_SUCCESS;
3979 EXTRACT_STRING("isds:dmID", copy->dmID);
3980 if (!copy->dmID) {
3981 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
3982 "but did not returned assigned message ID\n"));
3983 err = IE_ISDS;
3987 leave:
3988 free(code);
3989 free(message);
3990 xmlXPathFreeObject(result);
3991 return err;
3995 /* Insert struct isds_approval data (box approval) into XML tree
3996 * @context is session context
3997 * @approval is libisds structure with approval description. NULL is
3998 * acceptable.
3999 * @parent is XML element to append @approval to */
4000 static isds_error insert_GExtApproval(struct isds_ctx *context,
4001 const struct isds_approval *approval, xmlNodePtr parent) {
4003 isds_error err = IE_SUCCESS;
4004 xmlNodePtr node;
4006 if (!context) return IE_INVALID_CONTEXT;
4007 if (!parent) return IE_INVAL;
4009 if (!approval) return IE_SUCCESS;
4011 /* Build XSD:gExtApproval */
4012 INSERT_SCALAR_BOOLEAN(parent, "dbApproved", approval->approved);
4013 INSERT_STRING(parent, "dbExternRefNumber", approval->refference);
4015 leave:
4016 return err;
4020 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4021 * code
4022 * @context is session context
4023 * @service_name is name of SERVICE_DB_ACCESS
4024 * @response is server SOAP body response as XML document
4025 * @raw_response is automatically reallocated bit stream with response body. Use
4026 * NULL if you don't care
4027 * @raw_response_length is size of @raw_response in bytes
4028 * @code is ISDS status code
4029 * @status_message is ISDS status message
4030 * @return error coded from lower layer, context message will be set up
4031 * appropriately. */
4032 static isds_error build_send_check_dbdummy_request(struct isds_ctx *context,
4033 const xmlChar *service_name,
4034 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
4035 xmlChar **code, xmlChar **status_message) {
4037 isds_error err = IE_SUCCESS;
4038 char *service_name_locale = NULL;
4039 xmlNodePtr request = NULL, node;
4040 xmlNsPtr isds_ns = NULL;
4042 if (!context) return IE_INVALID_CONTEXT;
4043 if (!service_name) return IE_INVAL;
4044 if (!response || !code || !status_message) return IE_INVAL;
4045 if (!raw_response_length && raw_response) return IE_INVAL;
4047 /* Free output argument */
4048 xmlFreeDoc(*response); *response = NULL;
4049 if (raw_response) zfree(*raw_response);
4050 free(*code);
4051 free(*status_message);
4054 /* Check if connection is established
4055 * TODO: This check should be done downstairs. */
4056 if (!context->curl) return IE_CONNECTION_CLOSED;
4058 service_name_locale = _isds_utf82locale((char*)service_name);
4059 if (!service_name_locale) {
4060 err = IE_NOMEM;
4061 goto leave;
4064 /* Build request */
4065 request = xmlNewNode(NULL, service_name);
4066 if (!request) {
4067 isds_printf_message(context,
4068 _("Could not build %s request"), service_name_locale);
4069 err = IE_ERROR;
4070 goto leave;
4072 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4073 if(!isds_ns) {
4074 isds_log_message(context, _("Could not create ISDS name space"));
4075 err = IE_ERROR;
4076 goto leave;
4078 xmlSetNs(request, isds_ns);
4081 /* Add XSD:tDummyInput child */
4082 INSERT_STRING(request, "dbDummy", NULL);
4085 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
4086 service_name_locale);
4088 /* Send request */
4089 err = isds(context, SERVICE_DB_ACCESS, request, response,
4090 raw_response, raw_response_length);
4091 xmlFreeNode(request); request = NULL;
4093 if (err) {
4094 isds_log(ILF_ISDS, ILL_DEBUG,
4095 _("Processing ISDS response on %s request failed\n"),
4096 service_name_locale);
4097 goto leave;
4100 /* Check for response status */
4101 err = isds_response_status(context, SERVICE_DB_ACCESS, *response,
4102 code, status_message, NULL);
4103 if (err) {
4104 isds_log(ILF_ISDS, ILL_DEBUG,
4105 _("ISDS response on %s request is missing status\n"),
4106 service_name_locale);
4107 goto leave;
4110 /* Request processed, but nothing found */
4111 if (xmlStrcmp(*code, BAD_CAST "0000")) {
4112 char *code_locale = _isds_utf82locale((char*) *code);
4113 char *status_message_locale =
4114 _isds_utf82locale((char*) *status_message);
4115 isds_log(ILF_ISDS, ILL_DEBUG,
4116 _("Server refused %s request (code=%s, message=%s)\n"),
4117 service_name_locale, code_locale, status_message_locale);
4118 isds_log_message(context, status_message_locale);
4119 free(code_locale);
4120 free(status_message_locale);
4121 err = IE_ISDS;
4122 goto leave;
4125 leave:
4126 free(service_name_locale);
4127 xmlFreeNode(request);
4128 return err;
4132 /* Get data about logged in user and his box. */
4133 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
4134 struct isds_DbOwnerInfo **db_owner_info) {
4135 isds_error err = IE_SUCCESS;
4136 xmlDocPtr response = NULL;
4137 xmlChar *code = NULL, *message = NULL;
4138 xmlXPathContextPtr xpath_ctx = NULL;
4139 xmlXPathObjectPtr result = NULL;
4140 char *string = NULL;
4142 if (!context) return IE_INVALID_CONTEXT;
4143 zfree(context->long_message);
4144 if (!db_owner_info) return IE_INVAL;
4146 /* Check if connection is established */
4147 if (!context->curl) return IE_CONNECTION_CLOSED;
4150 /* Do request and check for success */
4151 err = build_send_check_dbdummy_request(context,
4152 BAD_CAST "GetOwnerInfoFromLogin",
4153 &response, NULL, NULL, &code, &message);
4154 if (err) goto leave;
4157 /* Extract data */
4158 /* Prepare structure */
4159 isds_DbOwnerInfo_free(db_owner_info);
4160 *db_owner_info = calloc(1, sizeof(**db_owner_info));
4161 if (!*db_owner_info) {
4162 err = IE_NOMEM;
4163 goto leave;
4165 xpath_ctx = xmlXPathNewContext(response);
4166 if (!xpath_ctx) {
4167 err = IE_ERROR;
4168 goto leave;
4170 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4171 err = IE_ERROR;
4172 goto leave;
4175 /* Set context node */
4176 result = xmlXPathEvalExpression(BAD_CAST
4177 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
4178 if (!result) {
4179 err = IE_ERROR;
4180 goto leave;
4182 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4183 isds_log_message(context, _("Missing dbOwnerInfo element"));
4184 err = IE_ISDS;
4185 goto leave;
4187 if (result->nodesetval->nodeNr > 1) {
4188 isds_log_message(context, _("Multiple dbOwnerInfo element"));
4189 err = IE_ISDS;
4190 goto leave;
4192 xpath_ctx->node = result->nodesetval->nodeTab[0];
4193 xmlXPathFreeObject(result); result = NULL;
4195 /* Extract it */
4196 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
4198 leave:
4199 if (err) {
4200 isds_DbOwnerInfo_free(db_owner_info);
4203 free(string);
4204 xmlXPathFreeObject(result);
4205 xmlXPathFreeContext(xpath_ctx);
4207 free(code);
4208 free(message);
4209 xmlFreeDoc(response);
4211 if (!err)
4212 isds_log(ILF_ISDS, ILL_DEBUG,
4213 _("GetOwnerInfoFromLogin request processed by server "
4214 "successfully.\n"));
4216 return err;
4220 /* Get data about logged in user. */
4221 isds_error isds_GetUserInfoFromLogin(struct isds_ctx *context,
4222 struct isds_DbUserInfo **db_user_info) {
4223 isds_error err = IE_SUCCESS;
4224 xmlDocPtr response = NULL;
4225 xmlChar *code = NULL, *message = NULL;
4226 xmlXPathContextPtr xpath_ctx = NULL;
4227 xmlXPathObjectPtr result = NULL;
4229 if (!context) return IE_INVALID_CONTEXT;
4230 zfree(context->long_message);
4231 if (!db_user_info) return IE_INVAL;
4233 /* Check if connection is established */
4234 if (!context->curl) return IE_CONNECTION_CLOSED;
4237 /* Do request and check for success */
4238 err = build_send_check_dbdummy_request(context,
4239 BAD_CAST "GetUserInfoFromLogin",
4240 &response, NULL, NULL, &code, &message);
4241 if (err) goto leave;
4244 /* Extract data */
4245 /* Prepare structure */
4246 isds_DbUserInfo_free(db_user_info);
4247 *db_user_info = calloc(1, sizeof(**db_user_info));
4248 if (!*db_user_info) {
4249 err = IE_NOMEM;
4250 goto leave;
4252 xpath_ctx = xmlXPathNewContext(response);
4253 if (!xpath_ctx) {
4254 err = IE_ERROR;
4255 goto leave;
4257 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4258 err = IE_ERROR;
4259 goto leave;
4262 /* Set context node */
4263 result = xmlXPathEvalExpression(BAD_CAST
4264 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx);
4265 if (!result) {
4266 err = IE_ERROR;
4267 goto leave;
4269 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4270 isds_log_message(context, _("Missing dbUserInfo element"));
4271 err = IE_ISDS;
4272 goto leave;
4274 if (result->nodesetval->nodeNr > 1) {
4275 isds_log_message(context, _("Multiple dbUserInfo element"));
4276 err = IE_ISDS;
4277 goto leave;
4279 xpath_ctx->node = result->nodesetval->nodeTab[0];
4280 xmlXPathFreeObject(result); result = NULL;
4282 /* Extract it */
4283 err = extract_DbUserInfo(context, db_user_info, xpath_ctx);
4285 leave:
4286 if (err) {
4287 isds_DbUserInfo_free(db_user_info);
4290 xmlXPathFreeObject(result);
4291 xmlXPathFreeContext(xpath_ctx);
4293 free(code);
4294 free(message);
4295 xmlFreeDoc(response);
4297 if (!err)
4298 isds_log(ILF_ISDS, ILL_DEBUG,
4299 _("GetUserInfoFromLogin request processed by server "
4300 "successfully.\n"));
4302 return err;
4306 /* Get expiration time of current password
4307 * @context is session context
4308 * @expiration is automatically reallocated time when password expires, In
4309 * case of error will be nulled. */
4310 isds_error isds_get_password_expiration(struct isds_ctx *context,
4311 struct timeval **expiration) {
4312 isds_error err = IE_SUCCESS;
4313 xmlDocPtr response = NULL;
4314 xmlChar *code = NULL, *message = NULL;
4315 xmlXPathContextPtr xpath_ctx = NULL;
4316 xmlXPathObjectPtr result = NULL;
4317 char *string = NULL;
4319 if (!context) return IE_INVALID_CONTEXT;
4320 zfree(context->long_message);
4321 if (!expiration) return IE_INVAL;
4323 /* Check if connection is established */
4324 if (!context->curl) return IE_CONNECTION_CLOSED;
4327 /* Do request and check for success */
4328 err = build_send_check_dbdummy_request(context,
4329 BAD_CAST "GetPasswordInfo",
4330 &response, NULL, NULL, &code, &message);
4331 if (err) goto leave;
4334 /* Extract data */
4335 xpath_ctx = xmlXPathNewContext(response);
4336 if (!xpath_ctx) {
4337 err = IE_ERROR;
4338 goto leave;
4340 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4341 err = IE_ERROR;
4342 goto leave;
4345 /* Set context node */
4346 result = xmlXPathEvalExpression(BAD_CAST
4347 "/isds:GetPasswordInfoResponse", xpath_ctx);
4348 if (!result) {
4349 err = IE_ERROR;
4350 goto leave;
4352 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4353 isds_log_message(context,
4354 _("Missing GetPasswordInfoResponse element"));
4355 err = IE_ISDS;
4356 goto leave;
4358 if (result->nodesetval->nodeNr > 1) {
4359 isds_log_message(context,
4360 _("Multiple GetPasswordInfoResponse element"));
4361 err = IE_ISDS;
4362 goto leave;
4364 xpath_ctx->node = result->nodesetval->nodeTab[0];
4365 xmlXPathFreeObject(result); result = NULL;
4367 /* Extract expiration date */
4368 EXTRACT_STRING("isds:pswExpDate", string);
4369 if (!string) {
4370 isds_log_message(context, _("Missing pswExpDate element"));
4371 err = IE_ISDS;
4372 goto leave;
4375 err = timestring2timeval((xmlChar *) string, expiration);
4376 if (err) {
4377 char *string_locale = _isds_utf82locale(string);
4378 if (err == IE_DATE) err = IE_ISDS;
4379 isds_printf_message(context,
4380 _("Could not convert pswExpDate as ISO time: %s"),
4381 string_locale);
4382 free(string_locale);
4383 goto leave;
4386 leave:
4387 if (err) {
4388 if (*expiration) {
4389 zfree(*expiration);
4393 free(string);
4394 xmlXPathFreeObject(result);
4395 xmlXPathFreeContext(xpath_ctx);
4397 free(code);
4398 free(message);
4399 xmlFreeDoc(response);
4401 if (!err)
4402 isds_log(ILF_ISDS, ILL_DEBUG,
4403 _("GetPasswordInfo request processed by server "
4404 "successfully.\n"));
4406 return err;
4410 /* Change user password in ISDS.
4411 * User must supply old password, new password will takes effect after some
4412 * time, current session can continue. Password must fulfill some constraints.
4413 * @context is session context
4414 * @old_password is current password.
4415 * @new_password is requested new password */
4416 isds_error isds_change_password(struct isds_ctx *context,
4417 const char *old_password, const char *new_password) {
4418 isds_error err = IE_SUCCESS;
4419 xmlNsPtr isds_ns = NULL;
4420 xmlNodePtr request = NULL, node;
4421 xmlDocPtr response = NULL;
4422 xmlChar *code = NULL, *message = NULL;
4424 if (!context) return IE_INVALID_CONTEXT;
4425 zfree(context->long_message);
4426 if (!old_password || !new_password) return IE_INVAL;
4428 /* Check if connection is established
4429 * TODO: This check should be done downstairs. */
4430 if (!context->curl) return IE_CONNECTION_CLOSED;
4433 /* Build ChangeISDSPassword request */
4434 request = xmlNewNode(NULL, BAD_CAST "ChangeISDSPassword");
4435 if (!request) {
4436 isds_log_message(context,
4437 _("Could not build ChangeISDSPassword request"));
4438 return IE_ERROR;
4440 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4441 if(!isds_ns) {
4442 isds_log_message(context, _("Could not create ISDS name space"));
4443 xmlFreeNode(request);
4444 return IE_ERROR;
4446 xmlSetNs(request, isds_ns);
4448 INSERT_STRING(request, "dbOldPassword", old_password);
4449 INSERT_STRING(request, "dbNewPassword", new_password);
4452 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CheckDataBox request to ISDS\n"));
4454 /* Sent request */
4455 err = isds(context, SERVICE_DB_ACCESS, request, &response, NULL, NULL);
4457 /* Destroy request */
4458 xmlFreeNode(request); request = NULL;
4460 if (err) {
4461 isds_log(ILF_ISDS, ILL_DEBUG,
4462 _("Processing ISDS response on ChangeISDSPassword "
4463 "request failed\n"));
4464 goto leave;
4467 /* Check for response status */
4468 err = isds_response_status(context, SERVICE_DB_ACCESS, response,
4469 &code, &message, NULL);
4470 if (err) {
4471 isds_log(ILF_ISDS, ILL_DEBUG,
4472 _("ISDS response on ChangeISDSPassword request is missing "
4473 "status\n"));
4474 goto leave;
4477 /* Request processed, but empty password refused */
4478 if (!xmlStrcmp(code, BAD_CAST "1066")) {
4479 char *code_locale = _isds_utf82locale((char*)code);
4480 char *message_locale = _isds_utf82locale((char*)message);
4481 isds_log(ILF_ISDS, ILL_DEBUG,
4482 _("Server refused empty password on ChangeISDSPassword "
4483 "request (code=%s, message=%s)\n"),
4484 code_locale, message_locale);
4485 isds_log_message(context, _("Password must not be empty"));
4486 free(code_locale);
4487 free(message_locale);
4488 err = IE_INVAL;
4489 goto leave;
4492 /* Request processed, but new password was reused */
4493 else if (!xmlStrcmp(code, BAD_CAST "1067")) {
4494 char *code_locale = _isds_utf82locale((char*)code);
4495 char *message_locale = _isds_utf82locale((char*)message);
4496 isds_log(ILF_ISDS, ILL_DEBUG,
4497 _("Server refused the same new password on ChangeISDSPassword "
4498 "request (code=%s, message=%s)\n"),
4499 code_locale, message_locale);
4500 isds_log_message(context,
4501 _("New password must differ from the current one"));
4502 free(code_locale);
4503 free(message_locale);
4504 err = IE_INVAL;
4505 goto leave;
4508 /* Other error */
4509 else if (xmlStrcmp(code, BAD_CAST "0000")) {
4510 char *code_locale = _isds_utf82locale((char*)code);
4511 char *message_locale = _isds_utf82locale((char*)message);
4512 isds_log(ILF_ISDS, ILL_DEBUG,
4513 _("Server refused to change password on ChangeISDSPassword "
4514 "request (code=%s, message=%s)\n"),
4515 code_locale, message_locale);
4516 isds_log_message(context, message_locale);
4517 free(code_locale);
4518 free(message_locale);
4519 err = IE_ISDS;
4520 goto leave;
4523 /* Otherwise password changed successfully */
4525 leave:
4526 free(code);
4527 free(message);
4528 xmlFreeDoc(response);
4529 xmlFreeNode(request);
4531 if (!err)
4532 isds_log(ILF_ISDS, ILL_DEBUG,
4533 _("Password changed successfully on ChangeISDSPassword "
4534 "request.\n"));
4536 return err;
4540 /* Generic middle part with request sending and response check.
4541 * It sends prepared request and checks for error code.
4542 * @context is ISDS session context.
4543 * @service is ISDS service handler
4544 * @service_name is name in scope of given @service
4545 * @request is XML tree with request. Will be freed to save memory.
4546 * @response is XML document outputting ISDS response.
4547 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4548 * NULL, if you don't care. */
4549 static isds_error send_destroy_request_check_response(
4550 struct isds_ctx *context,
4551 const isds_service service, const xmlChar *service_name,
4552 xmlNodePtr *request, xmlDocPtr *response, xmlChar **refnumber) {
4553 isds_error err = IE_SUCCESS;
4554 char *service_name_locale = NULL;
4555 xmlChar *code = NULL, *message = NULL;
4558 if (!context) return IE_INVALID_CONTEXT;
4559 if (!service_name || *service_name == '\0' || !request || !*request ||
4560 !response)
4561 return IE_INVAL;
4563 /* Check if connection is established
4564 * TODO: This check should be done downstairs. */
4565 if (!context->curl) return IE_CONNECTION_CLOSED;
4567 service_name_locale = _isds_utf82locale((char*) service_name);
4568 if (!service_name_locale) {
4569 err = IE_NOMEM;
4570 goto leave;
4573 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
4574 service_name_locale);
4576 /* Send request */
4577 err = isds(context, service, *request, response, NULL, NULL);
4578 xmlFreeNode(*request); *request = NULL;
4580 if (err) {
4581 isds_log(ILF_ISDS, ILL_DEBUG,
4582 _("Processing ISDS response on %s request failed\n"),
4583 service_name_locale);
4584 goto leave;
4587 /* Check for response status */
4588 err = isds_response_status(context, service, *response,
4589 &code, &message, refnumber);
4590 if (err) {
4591 isds_log(ILF_ISDS, ILL_DEBUG,
4592 _("ISDS response on %s request is missing status\n"),
4593 service_name_locale);
4594 goto leave;
4597 /* Request processed, but server failed */
4598 if (xmlStrcmp(code, BAD_CAST "0000")) {
4599 char *code_locale = _isds_utf82locale((char*) code);
4600 char *message_locale = _isds_utf82locale((char*) message);
4601 isds_log(ILF_ISDS, ILL_DEBUG,
4602 _("Server refused %s request (code=%s, message=%s)\n"),
4603 service_name_locale, code_locale, message_locale);
4604 isds_log_message(context, message_locale);
4605 free(code_locale);
4606 free(message_locale);
4607 err = IE_ISDS;
4608 goto leave;
4612 leave:
4613 free(code);
4614 free(message);
4615 if (err && *response) {
4616 xmlFreeDoc(*response);
4617 *response = NULL;
4619 if (*request) {
4620 xmlFreeNode(*request);
4621 *request = NULL;
4623 free(service_name_locale);
4625 return err;
4629 /* Generic bottom half with request sending.
4630 * It sends prepared request, checks for error code, destroys response and
4631 * request and log success or failure.
4632 * @context is ISDS session context.
4633 * @service is ISDS service handler
4634 * @service_name is name in scope of given @service
4635 * @request is XML tree with request. Will be freed to save memory.
4636 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4637 * NULL, if you don't care. */
4638 static isds_error send_request_check_drop_response(
4639 struct isds_ctx *context,
4640 const isds_service service, const xmlChar *service_name,
4641 xmlNodePtr *request, xmlChar **refnumber) {
4642 isds_error err = IE_SUCCESS;
4643 xmlDocPtr response = NULL;
4646 if (!context) return IE_INVALID_CONTEXT;
4647 if (!service_name || *service_name == '\0' || !request || !*request)
4648 return IE_INVAL;
4650 /* Send request and check response*/
4651 err = send_destroy_request_check_response(context,
4652 service, service_name, request, &response, refnumber);
4654 xmlFreeDoc(response);
4656 if (*request) {
4657 xmlFreeNode(*request);
4658 *request = NULL;
4661 if (!err) {
4662 char *service_name_locale = _isds_utf82locale((char *) service_name);
4663 isds_log(ILF_ISDS, ILL_DEBUG,
4664 _("%s request processed by server successfully.\n"),
4665 service_name_locale);
4666 free(service_name_locale);
4669 return err;
4673 /* Build XSD:tCreateDBInput request type for box creating.
4674 * @context is session context
4675 * @request outputs built XML tree
4676 * @service_name is request name of SERVICE_DB_MANIPULATION service
4677 * @box is box description to create including single primary user (in case of
4678 * FO box type)
4679 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
4680 * box, or contact address of PFO box owner)
4681 * @former_names is optional undocumented string. Pass NULL if you don't care.
4682 * @upper_box_id is optional ID of supper box if currently created box is
4683 * subordinated.
4684 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
4685 * don't care.
4686 * @request_token is true if ISDS should return token that box owner can use
4687 * to obtain his new credentials in on-line way
4688 * @approval is optional external approval of box manipulation */
4689 static isds_error build_CreateDBInput_request(struct isds_ctx *context,
4690 xmlNodePtr *request, const xmlChar *service_name,
4691 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
4692 const xmlChar *former_names, const xmlChar *upper_box_id,
4693 const xmlChar *ceo_label, _Bool request_token,
4694 const struct isds_approval *approval) {
4695 isds_error err = IE_SUCCESS;
4696 xmlNsPtr isds_ns = NULL;
4697 xmlNodePtr node, dbPrimaryUsers;
4698 xmlChar *string = NULL;
4699 const struct isds_list *item;
4702 if (!context) return IE_INVALID_CONTEXT;
4703 if (!request || !service_name || service_name[0] == '\0' || !box)
4704 return IE_INVAL;
4707 /* Build CreateDataBox-similar request */
4708 *request = xmlNewNode(NULL, service_name);
4709 if (!*request) {
4710 char *service_name_locale = _isds_utf82locale((char*) service_name);
4711 isds_printf_message(context, _("Could build %s request"),
4712 service_name_locale);
4713 free(service_name_locale);
4714 return IE_ERROR;
4716 if (context->type == CTX_TYPE_TESTING_REQUEST_COLLECTOR) {
4717 isds_ns = xmlNewNs(*request, BAD_CAST ISDS1_NS, NULL);
4718 if (!isds_ns) {
4719 isds_log_message(context, _("Could not create ISDS1 name space"));
4720 xmlFreeNode(*request);
4721 return IE_ERROR;
4723 } else {
4724 isds_ns = xmlNewNs(*request, BAD_CAST ISDS_NS, NULL);
4725 if (!isds_ns) {
4726 isds_log_message(context, _("Could not create ISDS name space"));
4727 xmlFreeNode(*request);
4728 return IE_ERROR;
4731 xmlSetNs(*request, isds_ns);
4733 INSERT_ELEMENT(node, *request, "dbOwnerInfo");
4734 err = insert_DbOwnerInfo(context, box, node);
4735 if (err) goto leave;
4737 /* Insert users */
4738 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
4739 * verbose documentation allows none dbUserInfo */
4740 INSERT_ELEMENT(dbPrimaryUsers, *request, "dbPrimaryUsers");
4741 for (item = users; item; item = item->next) {
4742 if (item->data) {
4743 INSERT_ELEMENT(node, dbPrimaryUsers, "dbUserInfo");
4744 err = insert_DbUserInfo(context,
4745 (struct isds_DbUserInfo *) item->data, node);
4746 if (err) goto leave;
4750 INSERT_STRING(*request, "dbFormerNames", former_names);
4751 INSERT_STRING(*request, "dbUpperDBId", upper_box_id);
4752 INSERT_STRING(*request, "dbCEOLabel", ceo_label);
4754 if (request_token) {
4755 /* This element is allowed only for CreateDataBox and there is optional
4756 * with default to false */
4757 INSERT_SCALAR_BOOLEAN(*request, "dbUseActPortal", 1);
4760 err = insert_GExtApproval(context, approval, *request);
4761 if (err) goto leave;
4763 leave:
4764 if (err) {
4765 xmlFreeNode(*request);
4766 *request = NULL;
4768 free(string);
4769 return err;
4773 /* Create new box.
4774 * @context is session context
4775 * @box is box description to create including single primary user (in case of
4776 * FO box type). It outputs box ID assigned by ISDS in dbID element.
4777 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
4778 * box, or contact address of PFO box owner)
4779 * @former_names is optional undocumented string. Pass NULL if you don't care.
4780 * @upper_box_id is optional ID of supper box if currently created box is
4781 * subordinated.
4782 * @ceo_label is optional title of OVM box owner (e.g. mayor)
4783 * @token is NULL if new password should be delivered off-line to the user.
4784 * It is valid pointer if user should obtain new password on-line on dedicated
4785 * web server. Then it outputs automatically reallocated token user needs to
4786 * use to authorize on the web server to view his new password.
4787 * @approval is optional external approval of box manipulation
4788 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4789 * NULL, if you don't care.*/
4790 isds_error isds_add_box(struct isds_ctx *context,
4791 struct isds_DbOwnerInfo *box, const struct isds_list *users,
4792 const char *former_names, const char *upper_box_id,
4793 const char *ceo_label, char **token,
4794 const struct isds_approval *approval, char **refnumber) {
4795 isds_error err = IE_SUCCESS;
4796 xmlNodePtr request = NULL;
4797 xmlDocPtr response = NULL;
4798 xmlXPathContextPtr xpath_ctx = NULL;
4799 xmlXPathObjectPtr result = NULL;
4802 if (!context) return IE_INVALID_CONTEXT;
4803 zfree(context->long_message);
4804 if (token) zfree(*token);
4805 if (!box) return IE_INVAL;
4807 /* Scratch box ID */
4808 zfree(box->dbID);
4810 /* Build CreateDataBox request */
4811 err = build_CreateDBInput_request(context,
4812 &request, BAD_CAST "CreateDataBox",
4813 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
4814 (xmlChar *) ceo_label, token, approval);
4815 if (err) goto leave;
4817 /* Send it to server and process response */
4818 err = send_destroy_request_check_response(context,
4819 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
4820 &response, (xmlChar **) refnumber);
4822 /* Extract box ID */
4823 xpath_ctx = xmlXPathNewContext(response);
4824 if (!xpath_ctx) {
4825 err = IE_ERROR;
4826 goto leave;
4828 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4829 err = IE_ERROR;
4830 goto leave;
4832 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
4834 /* Extract optional token */
4835 if (token) {
4836 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbAccessDataId", *token);
4837 if (!*token)
4838 isds_log(ILF_ISDS, ILL_WARNING,
4839 _("ISDS did not return token on CreateDataBox request "
4840 "even if requested\n"));
4843 leave:
4844 xmlXPathFreeObject(result);
4845 xmlXPathFreeContext(xpath_ctx);
4846 xmlFreeDoc(response);
4847 xmlFreeNode(request);
4849 if (!err) {
4850 isds_log(ILF_ISDS, ILL_DEBUG,
4851 _("CreateDataBox request processed by server successfully.\n"));
4854 return err;
4858 /* Notify ISDS about new PFO entity.
4859 * This function has no real effect.
4860 * @context is session context
4861 * @box is PFO description including single primary user.
4862 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
4863 * @former_names is optional undocumented string. Pass NULL if you don't care.
4864 * @upper_box_id is optional ID of supper box if currently created box is
4865 * subordinated.
4866 * @ceo_label is optional title of OVM box owner (e.g. mayor)
4867 * @approval is optional external approval of box manipulation
4868 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4869 * NULL, if you don't care.*/
4870 isds_error isds_add_pfoinfo(struct isds_ctx *context,
4871 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
4872 const char *former_names, const char *upper_box_id,
4873 const char *ceo_label, const struct isds_approval *approval,
4874 char **refnumber) {
4875 isds_error err = IE_SUCCESS;
4876 xmlNodePtr request = NULL;
4878 if (!context) return IE_INVALID_CONTEXT;
4879 zfree(context->long_message);
4880 if (!box) return IE_INVAL;
4882 /* Build CreateDataBoxPFOInfo request */
4883 err = build_CreateDBInput_request(context,
4884 &request, BAD_CAST "CreateDataBoxPFOInfo",
4885 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
4886 (xmlChar *) ceo_label, 0, approval);
4887 if (err) goto leave;
4889 /* Send it to server and process response */
4890 err = send_request_check_drop_response(context,
4891 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
4892 (xmlChar **) refnumber);
4893 leave:
4894 xmlFreeNode(request);
4895 return err;
4899 /* Remove given given box permanently.
4900 * @context is session context
4901 * @box is box description to delete
4902 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
4903 * carry sane value.
4904 * @approval is optional external approval of box manipulation
4905 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4906 * NULL, if you don't care.*/
4907 isds_error isds_delete_box(struct isds_ctx *context,
4908 const struct isds_DbOwnerInfo *box, const struct tm *since,
4909 const struct isds_approval *approval, char **refnumber) {
4910 isds_error err = IE_SUCCESS;
4911 xmlNsPtr isds_ns = NULL;
4912 xmlNodePtr request = NULL;
4913 xmlNodePtr node;
4914 xmlChar *string = NULL;
4917 if (!context) return IE_INVALID_CONTEXT;
4918 zfree(context->long_message);
4919 if (!box || !since) return IE_INVAL;
4922 /* Build DeleteDataBox request */
4923 request = xmlNewNode(NULL, BAD_CAST "DeleteDataBox");
4924 if (!request) {
4925 isds_log_message(context,
4926 _("Could build DeleteDataBox request"));
4927 return IE_ERROR;
4929 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4930 if(!isds_ns) {
4931 isds_log_message(context, _("Could not create ISDS name space"));
4932 xmlFreeNode(request);
4933 return IE_ERROR;
4935 xmlSetNs(request, isds_ns);
4937 INSERT_ELEMENT(node, request, "dbOwnerInfo");
4938 err = insert_DbOwnerInfo(context, box, node);
4939 if (err) goto leave;
4941 err = tm2datestring(since, &string);
4942 if (err) {
4943 isds_log_message(context,
4944 _("Could not convert `since' argument to ISO date string"));
4945 goto leave;
4947 INSERT_STRING(request, "dbOwnerTerminationDate", string);
4948 zfree(string);
4950 err = insert_GExtApproval(context, approval, request);
4951 if (err) goto leave;
4954 /* Send it to server and process response */
4955 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
4956 BAD_CAST "DeleteDataBox", &request, (xmlChar **) refnumber);
4958 leave:
4959 xmlFreeNode(request);
4960 free(string);
4961 return err;
4965 /* Update data about given box.
4966 * @context is session context
4967 * @old_box current box description
4968 * @new_box are updated data about @old_box
4969 * @approval is optional external approval of box manipulation
4970 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4971 * NULL, if you don't care.*/
4972 isds_error isds_UpdateDataBoxDescr(struct isds_ctx *context,
4973 const struct isds_DbOwnerInfo *old_box,
4974 const struct isds_DbOwnerInfo *new_box,
4975 const struct isds_approval *approval, char **refnumber) {
4976 isds_error err = IE_SUCCESS;
4977 xmlNsPtr isds_ns = NULL;
4978 xmlNodePtr request = NULL;
4979 xmlNodePtr node;
4982 if (!context) return IE_INVALID_CONTEXT;
4983 zfree(context->long_message);
4984 if (!old_box || !new_box) return IE_INVAL;
4987 /* Build UpdateDataBoxDescr request */
4988 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxDescr");
4989 if (!request) {
4990 isds_log_message(context,
4991 _("Could build UpdateDataBoxDescr request"));
4992 return IE_ERROR;
4994 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4995 if(!isds_ns) {
4996 isds_log_message(context, _("Could not create ISDS name space"));
4997 xmlFreeNode(request);
4998 return IE_ERROR;
5000 xmlSetNs(request, isds_ns);
5002 INSERT_ELEMENT(node, request, "dbOldOwnerInfo");
5003 err = insert_DbOwnerInfo(context, old_box, node);
5004 if (err) goto leave;
5006 INSERT_ELEMENT(node, request, "dbNewOwnerInfo");
5007 err = insert_DbOwnerInfo(context, new_box, node);
5008 if (err) goto leave;
5010 err = insert_GExtApproval(context, approval, request);
5011 if (err) goto leave;
5014 /* Send it to server and process response */
5015 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
5016 BAD_CAST "UpdateDataBoxDescr", &request, (xmlChar **) refnumber);
5018 leave:
5019 xmlFreeNode(request);
5021 return err;
5025 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
5026 * code
5027 * @context is session context
5028 * @service is SOAP service
5029 * @service_name is name of request in @service
5030 * @box_id is box ID of interest
5031 * @approval is optional external approval of box manipulation
5032 * @response is server SOAP body response as XML document
5033 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5034 * NULL, if you don't care.
5035 * @return error coded from lower layer, context message will be set up
5036 * appropriately. */
5037 static isds_error build_send_dbid_request_check_response(
5038 struct isds_ctx *context, const isds_service service,
5039 const xmlChar *service_name, const xmlChar *box_id,
5040 const struct isds_approval *approval,
5041 xmlDocPtr *response, xmlChar **refnumber) {
5043 isds_error err = IE_SUCCESS;
5044 char *service_name_locale = NULL, *box_id_locale = NULL;
5045 xmlNodePtr request = NULL, node;
5046 xmlNsPtr isds_ns = NULL;
5048 if (!context) return IE_INVALID_CONTEXT;
5049 if (!service_name || !box_id) return IE_INVAL;
5050 if (!response) return IE_INVAL;
5052 /* Free output argument */
5053 xmlFreeDoc(*response); *response = NULL;
5055 /* Prepare strings */
5056 service_name_locale = _isds_utf82locale((char*)service_name);
5057 if (!service_name_locale) {
5058 err = IE_NOMEM;
5059 goto leave;
5061 box_id_locale = _isds_utf82locale((char*)box_id);
5062 if (!box_id_locale) {
5063 err = IE_NOMEM;
5064 goto leave;
5067 /* Build request */
5068 request = xmlNewNode(NULL, service_name);
5069 if (!request) {
5070 isds_printf_message(context,
5071 _("Could not build %s request"), service_name_locale);
5072 err = IE_ERROR;
5073 goto leave;
5075 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5076 if(!isds_ns) {
5077 isds_log_message(context, _("Could not create ISDS name space"));
5078 err = IE_ERROR;
5079 goto leave;
5081 xmlSetNs(request, isds_ns);
5083 /* Add XSD:tIdDbInput children */
5084 INSERT_STRING(request, "dbID", box_id);
5085 err = insert_GExtApproval(context, approval, request);
5086 if (err) goto leave;
5088 /* Send request and check response*/
5089 err = send_destroy_request_check_response(context,
5090 service, service_name, &request, response, refnumber);
5092 leave:
5093 free(service_name_locale);
5094 free(box_id_locale);
5095 xmlFreeNode(request);
5096 return err;
5100 /* Get data about all users assigned to given box.
5101 * @context is session context
5102 * @box_id is box ID
5103 * @users is automatically reallocated list of struct isds_DbUserInfo */
5104 isds_error isds_GetDataBoxUsers(struct isds_ctx *context, const char *box_id,
5105 struct isds_list **users) {
5106 isds_error err = IE_SUCCESS;
5107 xmlDocPtr response = NULL;
5108 xmlXPathContextPtr xpath_ctx = NULL;
5109 xmlXPathObjectPtr result = NULL;
5110 int i;
5111 struct isds_list *item, *prev_item = NULL;
5113 if (!context) return IE_INVALID_CONTEXT;
5114 zfree(context->long_message);
5115 if (!users || !box_id) return IE_INVAL;
5118 /* Do request and check for success */
5119 err = build_send_dbid_request_check_response(context,
5120 SERVICE_DB_MANIPULATION, BAD_CAST "GetDataBoxUsers",
5121 BAD_CAST box_id, NULL, &response, NULL);
5122 if (err) goto leave;
5125 /* Extract data */
5126 /* Prepare structure */
5127 isds_list_free(users);
5128 xpath_ctx = xmlXPathNewContext(response);
5129 if (!xpath_ctx) {
5130 err = IE_ERROR;
5131 goto leave;
5133 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5134 err = IE_ERROR;
5135 goto leave;
5138 /* Set context node */
5139 result = xmlXPathEvalExpression(BAD_CAST
5140 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
5141 xpath_ctx);
5142 if (!result) {
5143 err = IE_ERROR;
5144 goto leave;
5146 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5147 isds_log_message(context, _("Missing dbUserInfo element"));
5148 err = IE_ISDS;
5149 goto leave;
5152 /* Iterate over all users */
5153 for (i = 0; i < result->nodesetval->nodeNr; i++) {
5155 /* Prepare structure */
5156 item = calloc(1, sizeof(*item));
5157 if (!item) {
5158 err = IE_NOMEM;
5159 goto leave;
5161 item->destructor = (void(*)(void**))isds_DbUserInfo_free;
5162 if (i == 0) *users = item;
5163 else prev_item->next = item;
5164 prev_item = item;
5166 /* Extract it */
5167 xpath_ctx->node = result->nodesetval->nodeTab[i];
5168 err = extract_DbUserInfo(context,
5169 (struct isds_DbUserInfo **) (&item->data), xpath_ctx);
5170 if (err) goto leave;
5173 leave:
5174 if (err) {
5175 isds_list_free(users);
5178 xmlXPathFreeObject(result);
5179 xmlXPathFreeContext(xpath_ctx);
5180 xmlFreeDoc(response);
5182 if (!err)
5183 isds_log(ILF_ISDS, ILL_DEBUG,
5184 _("GetDataBoxUsers request processed by server "
5185 "successfully.\n"));
5187 return err;
5191 /* Update data about user assigned to given box.
5192 * @context is session context
5193 * @box is box identification
5194 * @old_user identifies user to update
5195 * @new_user are updated data about @old_user
5196 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5197 * NULL, if you don't care.*/
5198 isds_error isds_UpdateDataBoxUser(struct isds_ctx *context,
5199 const struct isds_DbOwnerInfo *box,
5200 const struct isds_DbUserInfo *old_user,
5201 const struct isds_DbUserInfo *new_user,
5202 char **refnumber) {
5203 isds_error err = IE_SUCCESS;
5204 xmlNsPtr isds_ns = NULL;
5205 xmlNodePtr request = NULL;
5206 xmlNodePtr node;
5209 if (!context) return IE_INVALID_CONTEXT;
5210 zfree(context->long_message);
5211 if (!box || !old_user || !new_user) return IE_INVAL;
5214 /* Build UpdateDataBoxUser request */
5215 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxUser");
5216 if (!request) {
5217 isds_log_message(context,
5218 _("Could build UpdateDataBoxUser request"));
5219 return IE_ERROR;
5221 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5222 if(!isds_ns) {
5223 isds_log_message(context, _("Could not create ISDS name space"));
5224 xmlFreeNode(request);
5225 return IE_ERROR;
5227 xmlSetNs(request, isds_ns);
5229 INSERT_ELEMENT(node, request, "dbOwnerInfo");
5230 err = insert_DbOwnerInfo(context, box, node);
5231 if (err) goto leave;
5233 INSERT_ELEMENT(node, request, "dbOldUserInfo");
5234 err = insert_DbUserInfo(context, old_user, node);
5235 if (err) goto leave;
5237 INSERT_ELEMENT(node, request, "dbNewUserInfo");
5238 err = insert_DbUserInfo(context, new_user, node);
5239 if (err) goto leave;
5241 /* Send it to server and process response */
5242 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
5243 BAD_CAST "UpdateDataBoxUser", &request, (xmlChar **) refnumber);
5245 leave:
5246 xmlFreeNode(request);
5248 return err;
5252 /* Reset credentials of user assigned to given box.
5253 * @context is session context
5254 * @box is box identification
5255 * @user identifies user to reset password
5256 * @fee_paid is true if fee has been paid, false otherwise
5257 * @approval is optional external approval of box manipulation
5258 * @token is NULL if new password should be delivered off-line to the user.
5259 * It is valid pointer if user should obtain new password on-line on dedicated
5260 * web server. Then it outputs automatically reallocated token user needs to
5261 * use to authorize on the web server to view his new password.
5262 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5263 * NULL, if you don't care.*/
5264 isds_error isds_reset_password(struct isds_ctx *context,
5265 const struct isds_DbOwnerInfo *box,
5266 const struct isds_DbUserInfo *user,
5267 const _Bool fee_paid, const struct isds_approval *approval,
5268 char **token, char **refnumber) {
5269 isds_error err = IE_SUCCESS;
5270 xmlNsPtr isds_ns = NULL;
5271 xmlNodePtr request = NULL, node;
5272 xmlDocPtr response = NULL;
5273 xmlXPathContextPtr xpath_ctx = NULL;
5274 xmlXPathObjectPtr result = NULL;
5277 if (!context) return IE_INVALID_CONTEXT;
5278 zfree(context->long_message);
5280 if (token) zfree(*token);
5281 if (!box || !user) return IE_INVAL;
5284 /* Build NewAccessData request */
5285 request = xmlNewNode(NULL, BAD_CAST "NewAccessData");
5286 if (!request) {
5287 isds_log_message(context,
5288 _("Could build NewAccessData request"));
5289 return IE_ERROR;
5291 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5292 if(!isds_ns) {
5293 isds_log_message(context, _("Could not create ISDS name space"));
5294 xmlFreeNode(request);
5295 return IE_ERROR;
5297 xmlSetNs(request, isds_ns);
5299 INSERT_ELEMENT(node, request, "dbOwnerInfo");
5300 err = insert_DbOwnerInfo(context, box, node);
5301 if (err) goto leave;
5303 INSERT_ELEMENT(node, request, "dbUserInfo");
5304 err = insert_DbUserInfo(context, user, node);
5305 if (err) goto leave;
5307 INSERT_SCALAR_BOOLEAN(request, "dbFeePaid", fee_paid);
5309 if (token) {
5310 INSERT_SCALAR_BOOLEAN(request, "dbVirtual", 1);
5311 } else {
5312 INSERT_SCALAR_BOOLEAN(request, "dbVirtual", 0);
5315 err = insert_GExtApproval(context, approval, request);
5316 if (err) goto leave;
5318 /* Send request and check response*/
5319 err = send_destroy_request_check_response(context,
5320 SERVICE_DB_MANIPULATION, BAD_CAST "NewAccessData", &request,
5321 &response, (xmlChar **) refnumber);
5322 if (err) goto leave;
5325 /* Extract optional token */
5326 if (token) {
5327 xpath_ctx = xmlXPathNewContext(response);
5328 if (!xpath_ctx) {
5329 err = IE_ERROR;
5330 goto leave;
5332 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5333 err = IE_ERROR;
5334 goto leave;
5337 EXTRACT_STRING("/isds:NewAccessDataResponse/isds:dbAccessDataId", *token);
5338 if (!*token)
5339 isds_log(ILF_ISDS, ILL_WARNING,
5340 _("ISDS did not return token on CreateDataBox request "
5341 "even if requested\n"));
5344 leave:
5345 xmlXPathFreeObject(result);
5346 xmlXPathFreeContext(xpath_ctx);
5347 xmlFreeDoc(response);
5348 xmlFreeNode(request);
5350 if (!err)
5351 isds_log(ILF_ISDS, ILL_DEBUG,
5352 _("NewAccessData request processed by server "
5353 "successfully.\n"));
5355 return err;
5359 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
5360 * code, destroy response and log success.
5361 * @context is ISDS session context.
5362 * @service_name is name of SERVICE_DB_MANIPULATION service
5363 * @box is box identification
5364 * @user identifies user to remove
5365 * @approval is optional external approval of box manipulation
5366 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5367 * NULL, if you don't care. */
5368 static isds_error build_send_manipulationboxuser_request_check_drop_response(
5369 struct isds_ctx *context, const xmlChar *service_name,
5370 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
5371 const struct isds_approval *approval, xmlChar **refnumber) {
5372 isds_error err = IE_SUCCESS;
5373 xmlNsPtr isds_ns = NULL;
5374 xmlNodePtr request = NULL, node;
5377 if (!context) return IE_INVALID_CONTEXT;
5378 zfree(context->long_message);
5379 if (!service_name || service_name[0] == '\0' || !box || !user)
5380 return IE_INVAL;
5383 /* Build NewAccessData request */
5384 request = xmlNewNode(NULL, service_name);
5385 if (!request) {
5386 char *service_name_locale = _isds_utf82locale((char *) service_name);
5387 isds_printf_message(context, _("Could build %s request"),
5388 service_name_locale);
5389 free(service_name_locale);
5390 return IE_ERROR;
5392 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5393 if(!isds_ns) {
5394 isds_log_message(context, _("Could not create ISDS name space"));
5395 xmlFreeNode(request);
5396 return IE_ERROR;
5398 xmlSetNs(request, isds_ns);
5400 INSERT_ELEMENT(node, request, "dbOwnerInfo");
5401 err = insert_DbOwnerInfo(context, box, node);
5402 if (err) goto leave;
5404 INSERT_ELEMENT(node, request, "dbUserInfo");
5405 err = insert_DbUserInfo(context, user, node);
5406 if (err) goto leave;
5408 err = insert_GExtApproval(context, approval, request);
5409 if (err) goto leave;
5411 /* Send request and check response*/
5412 err = send_request_check_drop_response (context,
5413 SERVICE_DB_MANIPULATION, service_name, &request, refnumber);
5415 leave:
5416 xmlFreeNode(request);
5417 return err;
5421 /* Assign new user to given box.
5422 * @context is session context
5423 * @box is box identification
5424 * @user defines new user to add
5425 * @approval is optional external approval of box manipulation
5426 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5427 * NULL, if you don't care.*/
5428 isds_error isds_add_user(struct isds_ctx *context,
5429 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
5430 const struct isds_approval *approval, char **refnumber) {
5431 return build_send_manipulationboxuser_request_check_drop_response(context,
5432 BAD_CAST "AddDataBoxUser", box, user, approval,
5433 (xmlChar **) refnumber);
5437 /* Remove user assigned to given box.
5438 * @context is session context
5439 * @box is box identification
5440 * @user identifies user to remove
5441 * @approval is optional external approval of box manipulation
5442 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5443 * NULL, if you don't care.*/
5444 isds_error isds_delete_user(struct isds_ctx *context,
5445 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
5446 const struct isds_approval *approval, char **refnumber) {
5447 return build_send_manipulationboxuser_request_check_drop_response(context,
5448 BAD_CAST "DeleteDataBoxUser", box, user, approval,
5449 (xmlChar **) refnumber);
5453 /* Find boxes suiting given criteria.
5454 * @criteria is filter. You should fill in at least some members.
5455 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
5456 * possibly empty. Input NULL or valid old structure.
5457 * @return:
5458 * IE_SUCCESS if search succeeded, @boxes contains useful data
5459 * IE_NOEXIST if no such box exists, @boxes will be NULL
5460 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
5461 * contains still valid data
5462 * other code if something bad happens. @boxes will be NULL. */
5463 isds_error isds_FindDataBox(struct isds_ctx *context,
5464 const struct isds_DbOwnerInfo *criteria,
5465 struct isds_list **boxes) {
5466 isds_error err = IE_SUCCESS;
5467 _Bool truncated = 0;
5468 xmlNsPtr isds_ns = NULL;
5469 xmlNodePtr request = NULL;
5470 xmlDocPtr response = NULL;
5471 xmlChar *code = NULL, *message = NULL;
5472 xmlNodePtr db_owner_info;
5473 xmlXPathContextPtr xpath_ctx = NULL;
5474 xmlXPathObjectPtr result = NULL;
5475 xmlChar *string = NULL;
5478 if (!context) return IE_INVALID_CONTEXT;
5479 zfree(context->long_message);
5480 if (!boxes) return IE_INVAL;
5481 isds_list_free(boxes);
5483 if (!criteria) {
5484 return IE_INVAL;
5487 /* Check if connection is established
5488 * TODO: This check should be done downstairs. */
5489 if (!context->curl) return IE_CONNECTION_CLOSED;
5492 /* Build FindDataBox request */
5493 request = xmlNewNode(NULL, BAD_CAST "FindDataBox");
5494 if (!request) {
5495 isds_log_message(context,
5496 _("Could build FindDataBox request"));
5497 return IE_ERROR;
5499 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5500 if(!isds_ns) {
5501 isds_log_message(context, _("Could not create ISDS name space"));
5502 xmlFreeNode(request);
5503 return IE_ERROR;
5505 xmlSetNs(request, isds_ns);
5506 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
5507 if (!db_owner_info) {
5508 isds_log_message(context, _("Could not add dbOwnerInfo child to "
5509 "FindDataBox element"));
5510 xmlFreeNode(request);
5511 return IE_ERROR;
5514 err = insert_DbOwnerInfo(context, criteria, db_owner_info);
5515 if (err) goto leave;
5518 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending FindDataBox request to ISDS\n"));
5520 /* Sent request */
5521 err = isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
5523 /* Destroy request */
5524 xmlFreeNode(request); request = NULL;
5526 if (err) {
5527 isds_log(ILF_ISDS, ILL_DEBUG,
5528 _("Processing ISDS response on FindDataBox "
5529 "request failed\n"));
5530 goto leave;
5533 /* Check for response status */
5534 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
5535 &code, &message, NULL);
5536 if (err) {
5537 isds_log(ILF_ISDS, ILL_DEBUG,
5538 _("ISDS response on FindDataBox request is missing status\n"));
5539 goto leave;
5542 /* Request processed, but nothing found */
5543 if (!xmlStrcmp(code, BAD_CAST "0002") ||
5544 !xmlStrcmp(code, BAD_CAST "5001")) {
5545 char *code_locale = _isds_utf82locale((char*)code);
5546 char *message_locale = _isds_utf82locale((char*)message);
5547 isds_log(ILF_ISDS, ILL_DEBUG,
5548 _("Server did not found any box on FindDataBox request "
5549 "(code=%s, message=%s)\n"), code_locale, message_locale);
5550 isds_log_message(context, message_locale);
5551 free(code_locale);
5552 free(message_locale);
5553 err = IE_NOEXIST;
5554 goto leave;
5557 /* Warning, not a error */
5558 if (!xmlStrcmp(code, BAD_CAST "0003")) {
5559 char *code_locale = _isds_utf82locale((char*)code);
5560 char *message_locale = _isds_utf82locale((char*)message);
5561 isds_log(ILF_ISDS, ILL_DEBUG,
5562 _("Server truncated response on FindDataBox request "
5563 "(code=%s, message=%s)\n"), code_locale, message_locale);
5564 isds_log_message(context, message_locale);
5565 free(code_locale);
5566 free(message_locale);
5567 truncated = 1;
5570 /* Other error */
5571 else if (xmlStrcmp(code, BAD_CAST "0000")) {
5572 char *code_locale = _isds_utf82locale((char*)code);
5573 char *message_locale = _isds_utf82locale((char*)message);
5574 isds_log(ILF_ISDS, ILL_DEBUG,
5575 _("Server refused FindDataBox request "
5576 "(code=%s, message=%s)\n"), code_locale, message_locale);
5577 isds_log_message(context, message_locale);
5578 free(code_locale);
5579 free(message_locale);
5580 err = IE_ISDS;
5581 goto leave;
5584 xpath_ctx = xmlXPathNewContext(response);
5585 if (!xpath_ctx) {
5586 err = IE_ERROR;
5587 goto leave;
5589 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5590 err = IE_ERROR;
5591 goto leave;
5594 /* Extract boxes if they present */
5595 result = xmlXPathEvalExpression(BAD_CAST
5596 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
5597 xpath_ctx);
5598 if (!result) {
5599 err = IE_ERROR;
5600 goto leave;
5602 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5603 struct isds_list *item, *prev_item = NULL;
5604 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
5605 item = calloc(1, sizeof(*item));
5606 if (!item) {
5607 err = IE_NOMEM;
5608 goto leave;
5611 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
5612 if (i == 0) *boxes = item;
5613 else prev_item->next = item;
5614 prev_item = item;
5616 xpath_ctx->node = result->nodesetval->nodeTab[i];
5617 err = extract_DbOwnerInfo(context,
5618 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
5619 if (err) goto leave;
5623 leave:
5624 if (err) {
5625 isds_list_free(boxes);
5626 } else {
5627 if (truncated) err = IE_2BIG;
5630 free(string);
5631 xmlFreeNode(request);
5632 xmlXPathFreeObject(result);
5633 xmlXPathFreeContext(xpath_ctx);
5635 free(code);
5636 free(message);
5637 xmlFreeDoc(response);
5639 if (!err)
5640 isds_log(ILF_ISDS, ILL_DEBUG,
5641 _("FindDataBox request processed by server successfully.\n"));
5643 return err;
5647 /* Get status of a box.
5648 * @context is ISDS session context.
5649 * @box_id is UTF-8 encoded box identifier as zero terminated string
5650 * @box_status is return value of box status.
5651 * @return:
5652 * IE_SUCCESS if box has been found and its status retrieved
5653 * IE_NOEXIST if box is not known to ISDS server
5654 * or other appropriate error.
5655 * You can use isds_DbState to enumerate box status. However out of enum
5656 * range value can be returned too. This is feature because ISDS
5657 * specification leaves the set of values open.
5658 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
5659 * the box has been deleted, but ISDS still lists its former existence. */
5660 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
5661 long int *box_status) {
5662 isds_error err = IE_SUCCESS;
5663 xmlNsPtr isds_ns = NULL;
5664 xmlNodePtr request = NULL, db_id;
5665 xmlDocPtr response = NULL;
5666 xmlChar *code = NULL, *message = NULL;
5667 xmlXPathContextPtr xpath_ctx = NULL;
5668 xmlXPathObjectPtr result = NULL;
5669 xmlChar *string = NULL;
5671 if (!context) return IE_INVALID_CONTEXT;
5672 zfree(context->long_message);
5673 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
5675 /* Check if connection is established
5676 * TODO: This check should be done downstairs. */
5677 if (!context->curl) return IE_CONNECTION_CLOSED;
5680 /* Build CheckDataBox request */
5681 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
5682 if (!request) {
5683 isds_log_message(context,
5684 _("Could build CheckDataBox request"));
5685 return IE_ERROR;
5687 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5688 if(!isds_ns) {
5689 isds_log_message(context, _("Could not create ISDS name space"));
5690 xmlFreeNode(request);
5691 return IE_ERROR;
5693 xmlSetNs(request, isds_ns);
5694 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
5695 if (!db_id) {
5696 isds_log_message(context, _("Could not add dbID child to "
5697 "CheckDataBox element"));
5698 xmlFreeNode(request);
5699 return IE_ERROR;
5703 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CheckDataBox request to ISDS\n"));
5705 /* Sent request */
5706 err = isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
5708 /* Destroy request */
5709 xmlFreeNode(request);
5711 if (err) {
5712 isds_log(ILF_ISDS, ILL_DEBUG,
5713 _("Processing ISDS response on CheckDataBox "
5714 "request failed\n"));
5715 goto leave;
5718 /* Check for response status */
5719 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
5720 &code, &message, NULL);
5721 if (err) {
5722 isds_log(ILF_ISDS, ILL_DEBUG,
5723 _("ISDS response on CheckDataBox request is missing status\n"));
5724 goto leave;
5727 /* Request processed, but nothing found */
5728 if (!xmlStrcmp(code, BAD_CAST "5001")) {
5729 char *box_id_locale = _isds_utf82locale((char*)box_id);
5730 char *code_locale = _isds_utf82locale((char*)code);
5731 char *message_locale = _isds_utf82locale((char*)message);
5732 isds_log(ILF_ISDS, ILL_DEBUG,
5733 _("Server did not found box %s on CheckDataBox request "
5734 "(code=%s, message=%s)\n"),
5735 box_id_locale, code_locale, message_locale);
5736 isds_log_message(context, message_locale);
5737 free(box_id_locale);
5738 free(code_locale);
5739 free(message_locale);
5740 err = IE_NOEXIST;
5741 goto leave;
5744 /* Other error */
5745 else if (xmlStrcmp(code, BAD_CAST "0000")) {
5746 char *code_locale = _isds_utf82locale((char*)code);
5747 char *message_locale = _isds_utf82locale((char*)message);
5748 isds_log(ILF_ISDS, ILL_DEBUG,
5749 _("Server refused CheckDataBox request "
5750 "(code=%s, message=%s)\n"), code_locale, message_locale);
5751 isds_log_message(context, message_locale);
5752 free(code_locale);
5753 free(message_locale);
5754 err = IE_ISDS;
5755 goto leave;
5758 /* Extract data */
5759 xpath_ctx = xmlXPathNewContext(response);
5760 if (!xpath_ctx) {
5761 err = IE_ERROR;
5762 goto leave;
5764 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5765 err = IE_ERROR;
5766 goto leave;
5768 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
5769 xpath_ctx);
5770 if (!result) {
5771 err = IE_ERROR;
5772 goto leave;
5774 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5775 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
5776 err = IE_ISDS;
5777 goto leave;
5779 if (result->nodesetval->nodeNr > 1) {
5780 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
5781 err = IE_ISDS;
5782 goto leave;
5784 xpath_ctx->node = result->nodesetval->nodeTab[0];
5785 xmlXPathFreeObject(result); result = NULL;
5787 EXTRACT_LONGINT("isds:dbState", box_status, 1);
5790 leave:
5791 free(string);
5792 xmlXPathFreeObject(result);
5793 xmlXPathFreeContext(xpath_ctx);
5795 free(code);
5796 free(message);
5797 xmlFreeDoc(response);
5799 if (!err)
5800 isds_log(ILF_ISDS, ILL_DEBUG,
5801 _("CheckDataBox request processed by server successfully.\n"));
5803 return err;
5807 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
5808 * code, destroy response and log success.
5809 * @context is ISDS session context.
5810 * @service_name is name of SERVICE_DB_MANIPULATION service
5811 * @box_id is UTF-8 encoded box identifier as zero terminated string
5812 * @approval is optional external approval of box manipulation
5813 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5814 * NULL, if you don't care. */
5815 static isds_error build_send_manipulationdbid_request_check_drop_response(
5816 struct isds_ctx *context, const xmlChar *service_name,
5817 const xmlChar *box_id, const struct isds_approval *approval,
5818 xmlChar **refnumber) {
5819 isds_error err = IE_SUCCESS;
5820 xmlDocPtr response = NULL;
5822 if (!context) return IE_INVALID_CONTEXT;
5823 zfree(context->long_message);
5824 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
5826 /* Check if connection is established */
5827 if (!context->curl) return IE_CONNECTION_CLOSED;
5829 /* Do request and check for success */
5830 err = build_send_dbid_request_check_response(context,
5831 SERVICE_DB_MANIPULATION, service_name, box_id, approval,
5832 &response, refnumber);
5833 xmlFreeDoc(response);
5835 if (!err) {
5836 char *service_name_locale = _isds_utf82locale((char *) service_name);
5837 isds_log(ILF_ISDS, ILL_DEBUG,
5838 _("%s request processed by server successfully.\n"),
5839 service_name_locale);
5840 free(service_name_locale);
5843 return err;
5847 /* Switch box into state where box can receive commercial messages (off by
5848 * default)
5849 * @context is ISDS session context.
5850 * @box_id is UTF-8 encoded box identifier as zero terminated string
5851 * @allow is true for enable, false for disable commercial messages income
5852 * @approval is optional external approval of box manipulation
5853 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5854 * NULL, if you don't care. */
5855 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
5856 const char *box_id, const _Bool allow,
5857 const struct isds_approval *approval, char **refnumber) {
5858 return build_send_manipulationdbid_request_check_drop_response(context,
5859 (allow) ? BAD_CAST "SetOpenAddressing" :
5860 BAD_CAST "ClearOpenAddressing",
5861 BAD_CAST box_id, approval, (xmlChar **) refnumber);
5865 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
5866 * message acceptance). This is just a box permission. Sender must apply
5867 * such role by sending each message.
5868 * @context is ISDS session context.
5869 * @box_id is UTF-8 encoded box identifier as zero terminated string
5870 * @allow is true for enable, false for disable OVM role permission
5871 * @approval is optional external approval of box manipulation
5872 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5873 * NULL, if you don't care. */
5874 isds_error isds_switch_effective_ovm(struct isds_ctx *context,
5875 const char *box_id, const _Bool allow,
5876 const struct isds_approval *approval, char **refnumber) {
5877 return build_send_manipulationdbid_request_check_drop_response(context,
5878 (allow) ? BAD_CAST "SetEffectiveOVM" :
5879 BAD_CAST "ClearEffectiveOVM",
5880 BAD_CAST box_id, approval, (xmlChar **) refnumber);
5884 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
5885 * code, destroy response and log success.
5886 * @context is ISDS session context.
5887 * @service_name is name of SERVICE_DB_MANIPULATION service
5888 * @owner is structure describing box
5889 * @approval is optional external approval of box manipulation
5890 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5891 * NULL, if you don't care. */
5892 static isds_error build_send_manipulationdbowner_request_check_drop_response(
5893 struct isds_ctx *context, const xmlChar *service_name,
5894 const struct isds_DbOwnerInfo *owner,
5895 const struct isds_approval *approval, xmlChar **refnumber) {
5896 isds_error err = IE_SUCCESS;
5897 char *service_name_locale = NULL;
5898 xmlNodePtr request = NULL, db_owner_info;
5899 xmlNsPtr isds_ns = NULL;
5902 if (!context) return IE_INVALID_CONTEXT;
5903 zfree(context->long_message);
5904 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
5906 service_name_locale = _isds_utf82locale((char*)service_name);
5907 if (!service_name_locale) {
5908 err = IE_NOMEM;
5909 goto leave;
5912 /* Build request */
5913 request = xmlNewNode(NULL, service_name);
5914 if (!request) {
5915 isds_printf_message(context,
5916 _("Could not build %s request"), service_name_locale);
5917 err = IE_ERROR;
5918 goto leave;
5920 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5921 if(!isds_ns) {
5922 isds_log_message(context, _("Could not create ISDS name space"));
5923 err = IE_ERROR;
5924 goto leave;
5926 xmlSetNs(request, isds_ns);
5929 /* Add XSD:tOwnerInfoInput child*/
5930 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
5931 err = insert_DbOwnerInfo(context, owner, db_owner_info);
5932 if (err) goto leave;
5934 /* Add XSD:gExtApproval*/
5935 err = insert_GExtApproval(context, approval, request);
5936 if (err) goto leave;
5938 /* Send it to server and process response */
5939 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
5940 service_name, &request, refnumber);
5942 leave:
5943 xmlFreeNode(request);
5944 free(service_name_locale);
5946 return err;
5950 /* Switch box accessibility state on request of box owner.
5951 * Despite the name, owner must do the request off-line. This function is
5952 * designed for such off-line meeting points (e.g. Czech POINT).
5953 * @context is ISDS session context.
5954 * @box identifies box to switch accessibility state.
5955 * @allow is true for making accessible, false to disallow access.
5956 * @approval is optional external approval of box manipulation
5957 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5958 * NULL, if you don't care. */
5959 isds_error isds_switch_box_accessibility_on_owner_request(
5960 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
5961 const _Bool allow, const struct isds_approval *approval,
5962 char **refnumber) {
5963 return build_send_manipulationdbowner_request_check_drop_response(context,
5964 (allow) ? BAD_CAST "EnableOwnDataBox" :
5965 BAD_CAST "DisableOwnDataBox",
5966 box, approval, (xmlChar **) refnumber);
5970 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
5971 * date.
5972 * @context is ISDS session context.
5973 * @box identifies box to switch accessibility state.
5974 * @since is date since accessibility has been denied. This can be past too.
5975 * Only tm_year, tm_mon and tm_mday carry sane value.
5976 * @approval is optional external approval of box manipulation
5977 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5978 * NULL, if you don't care. */
5979 isds_error isds_disable_box_accessibility_externaly(
5980 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
5981 const struct tm *since, const struct isds_approval *approval,
5982 char **refnumber) {
5983 isds_error err = IE_SUCCESS;
5984 char *service_name_locale = NULL;
5985 xmlNodePtr request = NULL, node;
5986 xmlNsPtr isds_ns = NULL;
5987 xmlChar *string = NULL;
5990 if (!context) return IE_INVALID_CONTEXT;
5991 zfree(context->long_message);
5992 if (!box || !since) return IE_INVAL;
5994 /* Build request */
5995 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
5996 if (!request) {
5997 isds_printf_message(context,
5998 _("Could not build %s request"), "DisableDataBoxExternally");
5999 err = IE_ERROR;
6000 goto leave;
6002 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6003 if(!isds_ns) {
6004 isds_log_message(context, _("Could not create ISDS name space"));
6005 err = IE_ERROR;
6006 goto leave;
6008 xmlSetNs(request, isds_ns);
6011 /* Add @box identification */
6012 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6013 err = insert_DbOwnerInfo(context, box, node);
6014 if (err) goto leave;
6016 /* Add @since date */
6017 err = tm2datestring(since, &string);
6018 if(err) {
6019 isds_log_message(context,
6020 _("Could not convert `since' argument to ISO date string"));
6021 goto leave;
6023 INSERT_STRING(request, "dbOwnerDisableDate", string);
6024 zfree(string);
6026 /* Add @approval */
6027 err = insert_GExtApproval(context, approval, request);
6028 if (err) goto leave;
6030 /* Send it to server and process response */
6031 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6032 BAD_CAST "DisableDataBoxExternally", &request,
6033 (xmlChar **) refnumber);
6035 leave:
6036 free(string);
6037 xmlFreeNode(request);
6038 free(service_name_locale);
6040 return err;
6044 /* Insert struct isds_message data (envelope (recipient data optional) and
6045 * documents) into XML tree
6046 * @context is session context
6047 * @outgoing_message is libisds structure with message data
6048 * @create_message is XML CreateMessage or CreateMultipleMessage element
6049 * @process_recipient true for recipient data serialization, false for no
6050 * serialization */
6051 static isds_error insert_envelope_files(struct isds_ctx *context,
6052 const struct isds_message *outgoing_message, xmlNodePtr create_message,
6053 const _Bool process_recipient) {
6055 isds_error err = IE_SUCCESS;
6056 xmlNodePtr envelope, dm_files, node;
6057 xmlChar *string = NULL;
6059 if (!context) return IE_INVALID_CONTEXT;
6060 if (!outgoing_message || !create_message) return IE_INVAL;
6063 /* Build envelope */
6064 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
6065 if (!envelope) {
6066 isds_printf_message(context, _("Could not add dmEnvelope child to "
6067 "%s element"), create_message->name);
6068 return IE_ERROR;
6071 if (!outgoing_message->envelope) {
6072 isds_log_message(context, _("Outgoing message is missing envelope"));
6073 err = IE_INVAL;
6074 goto leave;
6077 /* Insert optional message type */
6078 err = insert_message_type(context, outgoing_message->envelope->dmType,
6079 envelope);
6080 if (err) goto leave;
6082 INSERT_STRING(envelope, "dmSenderOrgUnit",
6083 outgoing_message->envelope->dmSenderOrgUnit);
6084 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
6085 outgoing_message->envelope->dmSenderOrgUnitNum, string);
6087 if (process_recipient) {
6088 if (!outgoing_message->envelope->dbIDRecipient) {
6089 isds_log_message(context,
6090 _("Outgoing message is missing recipient box identifier"));
6091 err = IE_INVAL;
6092 goto leave;
6094 INSERT_STRING(envelope, "dbIDRecipient",
6095 outgoing_message->envelope->dbIDRecipient);
6097 INSERT_STRING(envelope, "dmRecipientOrgUnit",
6098 outgoing_message->envelope->dmRecipientOrgUnit);
6099 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
6100 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
6101 INSERT_STRING(envelope, "dmToHands",
6102 outgoing_message->envelope->dmToHands);
6105 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
6106 "dmAnnotation");
6107 INSERT_STRING(envelope, "dmAnnotation",
6108 outgoing_message->envelope->dmAnnotation);
6110 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
6111 0, 50, "dmRecipientRefNumber");
6112 INSERT_STRING(envelope, "dmRecipientRefNumber",
6113 outgoing_message->envelope->dmRecipientRefNumber);
6115 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
6116 0, 50, "dmSenderRefNumber");
6117 INSERT_STRING(envelope, "dmSenderRefNumber",
6118 outgoing_message->envelope->dmSenderRefNumber);
6120 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
6121 0, 50, "dmRecipientIdent");
6122 INSERT_STRING(envelope, "dmRecipientIdent",
6123 outgoing_message->envelope->dmRecipientIdent);
6125 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
6126 0, 50, "dmSenderIdent");
6127 INSERT_STRING(envelope, "dmSenderIdent",
6128 outgoing_message->envelope->dmSenderIdent);
6130 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
6131 outgoing_message->envelope->dmLegalTitleLaw, string);
6132 INSERT_LONGINT(envelope, "dmLegalTitleYear",
6133 outgoing_message->envelope->dmLegalTitleYear, string);
6134 INSERT_STRING(envelope, "dmLegalTitleSect",
6135 outgoing_message->envelope->dmLegalTitleSect);
6136 INSERT_STRING(envelope, "dmLegalTitlePar",
6137 outgoing_message->envelope->dmLegalTitlePar);
6138 INSERT_STRING(envelope, "dmLegalTitlePoint",
6139 outgoing_message->envelope->dmLegalTitlePoint);
6141 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
6142 outgoing_message->envelope->dmPersonalDelivery);
6143 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
6144 outgoing_message->envelope->dmAllowSubstDelivery);
6146 /* ???: Should we require value for dbEffectiveOVM sender?
6147 * ISDS has default as true */
6148 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
6151 /* Append dmFiles */
6152 if (!outgoing_message->documents) {
6153 isds_log_message(context,
6154 _("Outgoing message is missing list of documents"));
6155 err = IE_INVAL;
6156 goto leave;
6158 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
6159 if (!dm_files) {
6160 isds_printf_message(context, _("Could not add dmFiles child to "
6161 "%s element"), create_message->name);
6162 err = IE_ERROR;
6163 goto leave;
6166 /* Check for document hierarchy */
6167 err = _isds_check_documents_hierarchy(context, outgoing_message->documents);
6168 if (err) goto leave;
6170 /* Process each document */
6171 for (struct isds_list *item =
6172 (struct isds_list *) outgoing_message->documents;
6173 item; item = item->next) {
6174 if (!item->data) {
6175 isds_log_message(context,
6176 _("List of documents contains empty item"));
6177 err = IE_INVAL;
6178 goto leave;
6180 /* FIXME: Check for dmFileMetaType and for document references.
6181 * Only first document can be of MAIN type */
6182 err = insert_document(context, (struct isds_document*) item->data,
6183 dm_files);
6185 if (err) goto leave;
6188 leave:
6189 free(string);
6190 return err;
6194 /* Send a message via ISDS to a recipient
6195 * @context is session context
6196 * @outgoing_message is message to send; Some members are mandatory (like
6197 * dbIDRecipient), some are optional and some are irrelevant (especially data
6198 * about sender). Included pointer to isds_list documents must contain at
6199 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
6200 * members will be filled with valid data from ISDS. Exact list of write
6201 * members is subject to change. Currently dmId is changed.
6202 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
6203 isds_error isds_send_message(struct isds_ctx *context,
6204 struct isds_message *outgoing_message) {
6206 isds_error err = IE_SUCCESS;
6207 xmlNsPtr isds_ns = NULL;
6208 xmlNodePtr request = NULL;
6209 xmlDocPtr response = NULL;
6210 xmlChar *code = NULL, *message = NULL;
6211 xmlXPathContextPtr xpath_ctx = NULL;
6212 xmlXPathObjectPtr result = NULL;
6213 _Bool message_is_complete = 0;
6215 if (!context) return IE_INVALID_CONTEXT;
6216 zfree(context->long_message);
6217 if (!outgoing_message) return IE_INVAL;
6219 /* Check if connection is established
6220 * TODO: This check should be done downstairs. */
6221 if (!context->curl) return IE_CONNECTION_CLOSED;
6224 /* Build CreateMessage request */
6225 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
6226 if (!request) {
6227 isds_log_message(context,
6228 _("Could not build CreateMessage request"));
6229 return IE_ERROR;
6231 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6232 if(!isds_ns) {
6233 isds_log_message(context, _("Could not create ISDS name space"));
6234 xmlFreeNode(request);
6235 return IE_ERROR;
6237 xmlSetNs(request, isds_ns);
6239 /* Append envelope and files */
6240 err = insert_envelope_files(context, outgoing_message, request, 1);
6241 if (err) goto leave;
6244 /* Signal we can serialize message since now */
6245 message_is_complete = 1;
6248 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
6250 /* Sent request */
6251 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
6253 /* Don't' destroy request, we want to provide it to application later */
6255 if (err) {
6256 isds_log(ILF_ISDS, ILL_DEBUG,
6257 _("Processing ISDS response on CreateMessage "
6258 "request failed\n"));
6259 goto leave;
6262 /* Check for response status */
6263 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
6264 &code, &message, NULL);
6265 if (err) {
6266 isds_log(ILF_ISDS, ILL_DEBUG,
6267 _("ISDS response on CreateMessage request "
6268 "is missing status\n"));
6269 goto leave;
6272 /* Request processed, but refused by server or server failed */
6273 if (xmlStrcmp(code, BAD_CAST "0000")) {
6274 char *box_id_locale =
6275 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
6276 char *code_locale = _isds_utf82locale((char*)code);
6277 char *message_locale = _isds_utf82locale((char*)message);
6278 isds_log(ILF_ISDS, ILL_DEBUG,
6279 _("Server did not accept message for %s on CreateMessage "
6280 "request (code=%s, message=%s)\n"),
6281 box_id_locale, code_locale, message_locale);
6282 isds_log_message(context, message_locale);
6283 free(box_id_locale);
6284 free(code_locale);
6285 free(message_locale);
6286 err = IE_ISDS;
6287 goto leave;
6291 /* Extract data */
6292 xpath_ctx = xmlXPathNewContext(response);
6293 if (!xpath_ctx) {
6294 err = IE_ERROR;
6295 goto leave;
6297 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6298 err = IE_ERROR;
6299 goto leave;
6301 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
6302 xpath_ctx);
6303 if (!result) {
6304 err = IE_ERROR;
6305 goto leave;
6307 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6308 isds_log_message(context, _("Missing CreateMessageResponse element"));
6309 err = IE_ISDS;
6310 goto leave;
6312 if (result->nodesetval->nodeNr > 1) {
6313 isds_log_message(context, _("Multiple CreateMessageResponse element"));
6314 err = IE_ISDS;
6315 goto leave;
6317 xpath_ctx->node = result->nodesetval->nodeTab[0];
6318 xmlXPathFreeObject(result); result = NULL;
6320 if (outgoing_message->envelope->dmID) {
6321 free(outgoing_message->envelope->dmID);
6322 outgoing_message->envelope->dmID = NULL;
6324 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
6325 if (!outgoing_message->envelope->dmID) {
6326 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
6327 "but did not return assigned message ID\n"));
6330 leave:
6331 /* TODO: Serialize message into structure member raw */
6332 /* XXX: Each web service transport message in different format.
6333 * Therefore it's not possible to save them directly.
6334 * To save them, one must figure out common format.
6335 * We can leave it on application, or we can implement the ESS format. */
6336 /*if (message_is_complete) {
6337 if (outgoing_message->envelope->dmID) {
6339 /* Add assigned message ID as first child*/
6340 /*xmlNodePtr dmid_text = xmlNewText(
6341 (xmlChar *) outgoing_message->envelope->dmID);
6342 if (!dmid_text) goto serialization_failed;
6344 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
6345 BAD_CAST "dmID");
6346 if (!dmid_element) {
6347 xmlFreeNode(dmid_text);
6348 goto serialization_failed;
6351 xmlNodePtr dmid_element_with_text =
6352 xmlAddChild(dmid_element, dmid_text);
6353 if (!dmid_element_with_text) {
6354 xmlFreeNode(dmid_element);
6355 xmlFreeNode(dmid_text);
6356 goto serialization_failed;
6359 node = xmlAddPrevSibling(envelope->childern,
6360 dmid_element_with_text);
6361 if (!node) {
6362 xmlFreeNodeList(dmid_element_with_text);
6363 goto serialization_failed;
6367 /* Serialize message with ID into raw */
6368 /*buffer = serialize_element(envelope)*/
6369 /* }
6371 serialization_failed:
6375 /* Clean up */
6376 xmlXPathFreeObject(result);
6377 xmlXPathFreeContext(xpath_ctx);
6379 free(code);
6380 free(message);
6381 xmlFreeDoc(response);
6382 xmlFreeNode(request);
6384 if (!err)
6385 isds_log(ILF_ISDS, ILL_DEBUG,
6386 _("CreateMessage request processed by server "
6387 "successfully.\n"));
6389 return err;
6393 /* Send a message via ISDS to a multiple recipients
6394 * @context is session context
6395 * @outgoing_message is message to send; Some members are mandatory,
6396 * some are optional and some are irrelevant (especially data
6397 * about sender). Data about recipient will be substituted by ISDS from
6398 * @copies. Included pointer to isds_list documents must
6399 * contain at least one document of FILEMETATYPE_MAIN.
6400 * @copies is list of isds_message_copy structures addressing all desired
6401 * recipients. This is read-write structure, some members will be filled with
6402 * valid data from ISDS (message IDs, error codes, error descriptions).
6403 * @return
6404 * ISDS_SUCCESS if all messages have been sent
6405 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
6406 * succeeded messages can be identified by copies->data->error),
6407 * or other error code if something other goes wrong. */
6408 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
6409 const struct isds_message *outgoing_message,
6410 struct isds_list *copies) {
6412 isds_error err = IE_SUCCESS, append_err;
6413 xmlNsPtr isds_ns = NULL;
6414 xmlNodePtr request = NULL, recipients, recipient, node;
6415 struct isds_list *item;
6416 struct isds_message_copy *copy;
6417 xmlDocPtr response = NULL;
6418 xmlChar *code = NULL, *message = NULL;
6419 xmlXPathContextPtr xpath_ctx = NULL;
6420 xmlXPathObjectPtr result = NULL;
6421 xmlChar *string = NULL;
6422 int i;
6424 if (!context) return IE_INVALID_CONTEXT;
6425 zfree(context->long_message);
6426 if (!outgoing_message || !copies) return IE_INVAL;
6428 /* Check if connection is established
6429 * TODO: This check should be done downstairs. */
6430 if (!context->curl) return IE_CONNECTION_CLOSED;
6433 /* Build CreateMultipleMessage request */
6434 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
6435 if (!request) {
6436 isds_log_message(context,
6437 _("Could not build CreateMultipleMessage request"));
6438 return IE_ERROR;
6440 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6441 if(!isds_ns) {
6442 isds_log_message(context, _("Could not create ISDS name space"));
6443 xmlFreeNode(request);
6444 return IE_ERROR;
6446 xmlSetNs(request, isds_ns);
6449 /* Build recipients */
6450 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
6451 if (!recipients) {
6452 isds_log_message(context, _("Could not add dmRecipients child to "
6453 "CreateMultipleMessage element"));
6454 xmlFreeNode(request);
6455 return IE_ERROR;
6458 /* Insert each recipient */
6459 for (item = copies; item; item = item->next) {
6460 copy = (struct isds_message_copy *) item->data;
6461 if (!copy) {
6462 isds_log_message(context,
6463 _("`copies' list item contains empty data"));
6464 err = IE_INVAL;
6465 goto leave;
6468 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
6469 if (!recipient) {
6470 isds_log_message(context, _("Could not add dmRecipient child to "
6471 "dmRecipients element"));
6472 err = IE_ERROR;
6473 goto leave;
6476 if (!copy->dbIDRecipient) {
6477 isds_log_message(context,
6478 _("Message copy is missing recipient box identifier"));
6479 err = IE_INVAL;
6480 goto leave;
6482 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
6483 INSERT_STRING(recipient, "dmRecipientOrgUnit",
6484 copy->dmRecipientOrgUnit);
6485 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
6486 copy->dmRecipientOrgUnitNum, string);
6487 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
6490 /* Append envelope and files */
6491 err = insert_envelope_files(context, outgoing_message, request, 0);
6492 if (err) goto leave;
6495 isds_log(ILF_ISDS, ILL_DEBUG,
6496 _("Sending CreateMultipleMessage request to ISDS\n"));
6498 /* Sent request */
6499 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
6500 if (err) {
6501 isds_log(ILF_ISDS, ILL_DEBUG,
6502 _("Processing ISDS response on CreateMultipleMessage "
6503 "request failed\n"));
6504 goto leave;
6507 /* Check for response status */
6508 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
6509 &code, &message, NULL);
6510 if (err) {
6511 isds_log(ILF_ISDS, ILL_DEBUG,
6512 _("ISDS response on CreateMultipleMessage request "
6513 "is missing status\n"));
6514 goto leave;
6517 /* Request processed, but some copies failed */
6518 if (!xmlStrcmp(code, BAD_CAST "0004")) {
6519 char *box_id_locale =
6520 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
6521 char *code_locale = _isds_utf82locale((char*)code);
6522 char *message_locale = _isds_utf82locale((char*)message);
6523 isds_log(ILF_ISDS, ILL_DEBUG,
6524 _("Server did accept message for multiple recipients "
6525 "on CreateMultipleMessage request but delivery to "
6526 "some of them failed (code=%s, message=%s)\n"),
6527 box_id_locale, code_locale, message_locale);
6528 isds_log_message(context, message_locale);
6529 free(box_id_locale);
6530 free(code_locale);
6531 free(message_locale);
6532 err = IE_PARTIAL_SUCCESS;
6535 /* Request refused by server as whole */
6536 else if (xmlStrcmp(code, BAD_CAST "0000")) {
6537 char *box_id_locale =
6538 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
6539 char *code_locale = _isds_utf82locale((char*)code);
6540 char *message_locale = _isds_utf82locale((char*)message);
6541 isds_log(ILF_ISDS, ILL_DEBUG,
6542 _("Server did not accept message for multiple recipients "
6543 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
6544 box_id_locale, code_locale, message_locale);
6545 isds_log_message(context, message_locale);
6546 free(box_id_locale);
6547 free(code_locale);
6548 free(message_locale);
6549 err = IE_ISDS;
6550 goto leave;
6554 /* Extract data */
6555 xpath_ctx = xmlXPathNewContext(response);
6556 if (!xpath_ctx) {
6557 err = IE_ERROR;
6558 goto leave;
6560 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6561 err = IE_ERROR;
6562 goto leave;
6564 result = xmlXPathEvalExpression(
6565 BAD_CAST "/isds:CreateMultipleMessageResponse"
6566 "/isds:dmMultipleStatus/isds:dmSingleStatus",
6567 xpath_ctx);
6568 if (!result) {
6569 err = IE_ERROR;
6570 goto leave;
6572 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6573 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
6574 err = IE_ISDS;
6575 goto leave;
6578 /* Extract message ID and delivery status for each copy */
6579 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
6580 item = item->next, i++) {
6581 copy = (struct isds_message_copy *) item->data;
6582 xpath_ctx->node = result->nodesetval->nodeTab[i];
6584 append_err = append_TMStatus(context, copy, xpath_ctx);
6585 if (append_err) {
6586 err = append_err;
6587 goto leave;
6590 if (item || i < result->nodesetval->nodeNr) {
6591 isds_printf_message(context, _("ISDS returned unexpected number of "
6592 "message copy delivery states: %d"),
6593 result->nodesetval->nodeNr);
6594 err = IE_ISDS;
6595 goto leave;
6599 leave:
6600 /* Clean up */
6601 free(string);
6602 xmlXPathFreeObject(result);
6603 xmlXPathFreeContext(xpath_ctx);
6605 free(code);
6606 free(message);
6607 xmlFreeDoc(response);
6608 xmlFreeNode(request);
6610 if (!err)
6611 isds_log(ILF_ISDS, ILL_DEBUG,
6612 _("CreateMultipleMessageResponse request processed by server "
6613 "successfully.\n"));
6615 return err;
6619 /* Get list of messages. This is common core for getting sent or received
6620 * messages.
6621 * Any criterion argument can be NULL, if you don't care about it.
6622 * @context is session context. Must not be NULL.
6623 * @outgoing_direction is true if you want list of outgoing messages,
6624 * it's false if you want incoming messages.
6625 * @from_time is minimal time and date of message sending inclusive.
6626 * @to_time is maximal time and date of message sending inclusive
6627 * @organization_unit_number is number of sender/recipient respectively.
6628 * @status_filter is bit field of isds_message_status values. Use special
6629 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
6630 * all values, you can use bit-wise arithmetic if you want.)
6631 * @offset is index of first message we are interested in. First message is 1.
6632 * Set to 0 (or 1) if you don't care.
6633 * @number is maximal length of list you want to get as input value, outputs
6634 * number of messages matching these criteria. Can be NULL if you don't care
6635 * (applies to output value either).
6636 * @messages is automatically reallocated list of isds_message's. Be ware that
6637 * it returns only brief overview (envelope and some other fields) about each
6638 * message, not the complete message. FIXME: Specify exact fields.
6639 * The list is sorted by delivery time in ascending order.
6640 * Use NULL if
6641 * you don't care about don't need the data (useful if you want to know only
6642 * the @number). If you provide &NULL, list will be allocated on heap, if you
6643 * provide pointer to non-NULL, list will be freed automatically at first. Also
6644 * in case of error the list will be NULLed.
6645 * @return IE_SUCCESS or appropriate error code. */
6646 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
6647 _Bool outgoing_direction,
6648 const struct timeval *from_time, const struct timeval *to_time,
6649 const long int *organization_unit_number,
6650 const unsigned int status_filter,
6651 const unsigned long int offset, unsigned long int *number,
6652 struct isds_list **messages) {
6654 isds_error err = IE_SUCCESS;
6655 xmlNsPtr isds_ns = NULL;
6656 xmlNodePtr request = NULL, node;
6657 xmlDocPtr response = NULL;
6658 xmlChar *code = NULL, *message = NULL;
6659 xmlXPathContextPtr xpath_ctx = NULL;
6660 xmlXPathObjectPtr result = NULL;
6661 xmlChar *string = NULL;
6662 long unsigned int count = 0;
6664 if (!context) return IE_INVALID_CONTEXT;
6665 zfree(context->long_message);
6667 /* Free former message list if any */
6668 if (messages) isds_list_free(messages);
6670 /* Check if connection is established
6671 * TODO: This check should be done downstairs. */
6672 if (!context->curl) return IE_CONNECTION_CLOSED;
6674 /* Build GetListOf*Messages request */
6675 request = xmlNewNode(NULL,
6676 (outgoing_direction) ?
6677 BAD_CAST "GetListOfSentMessages" :
6678 BAD_CAST "GetListOfReceivedMessages"
6680 if (!request) {
6681 isds_log_message(context,
6682 (outgoing_direction) ?
6683 _("Could not build GetListOfSentMessages request") :
6684 _("Could not build GetListOfReceivedMessages request")
6686 return IE_ERROR;
6688 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6689 if(!isds_ns) {
6690 isds_log_message(context, _("Could not create ISDS name space"));
6691 xmlFreeNode(request);
6692 return IE_ERROR;
6694 xmlSetNs(request, isds_ns);
6697 if (from_time) {
6698 err = timeval2timestring(from_time, &string);
6699 if (err) goto leave;
6701 INSERT_STRING(request, "dmFromTime", string);
6702 free(string); string = NULL;
6704 if (to_time) {
6705 err = timeval2timestring(to_time, &string);
6706 if (err) goto leave;
6708 INSERT_STRING(request, "dmToTime", string);
6709 free(string); string = NULL;
6711 if (outgoing_direction) {
6712 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
6713 organization_unit_number, string);
6714 } else {
6715 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
6716 organization_unit_number, string);
6719 if (status_filter > MESSAGESTATE_ANY) {
6720 isds_printf_message(context,
6721 _("Invalid message state filter value: %ld"), status_filter);
6722 err = IE_INVAL;
6723 goto leave;
6725 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
6727 if (offset > 0 ) {
6728 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
6729 } else {
6730 INSERT_STRING(request, "dmOffset", "1");
6733 /* number 0 means no limit */
6734 if (number && *number == 0) {
6735 INSERT_STRING(request, "dmLimit", NULL);
6736 } else {
6737 INSERT_ULONGINT(request, "dmLimit", number, string);
6741 isds_log(ILF_ISDS, ILL_DEBUG,
6742 (outgoing_direction) ?
6743 _("Sending GetListOfSentMessages request to ISDS\n") :
6744 _("Sending GetListOfReceivedMessages request to ISDS\n")
6747 /* Sent request */
6748 err = isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
6749 xmlFreeNode(request); request = NULL;
6751 if (err) {
6752 isds_log(ILF_ISDS, ILL_DEBUG,
6753 (outgoing_direction) ?
6754 _("Processing ISDS response on GetListOfSentMessages "
6755 "request failed\n") :
6756 _("Processing ISDS response on GetListOfReceivedMessages "
6757 "request failed\n")
6759 goto leave;
6762 /* Check for response status */
6763 err = isds_response_status(context, SERVICE_DM_INFO, response,
6764 &code, &message, NULL);
6765 if (err) {
6766 isds_log(ILF_ISDS, ILL_DEBUG,
6767 (outgoing_direction) ?
6768 _("ISDS response on GetListOfSentMessages request "
6769 "is missing status\n") :
6770 _("ISDS response on GetListOfReceivedMessages request "
6771 "is missing status\n")
6773 goto leave;
6776 /* Request processed, but nothing found */
6777 if (xmlStrcmp(code, BAD_CAST "0000")) {
6778 char *code_locale = _isds_utf82locale((char*)code);
6779 char *message_locale = _isds_utf82locale((char*)message);
6780 isds_log(ILF_ISDS, ILL_DEBUG,
6781 (outgoing_direction) ?
6782 _("Server refused GetListOfSentMessages request "
6783 "(code=%s, message=%s)\n") :
6784 _("Server refused GetListOfReceivedMessages request "
6785 "(code=%s, message=%s)\n"),
6786 code_locale, message_locale);
6787 isds_log_message(context, message_locale);
6788 free(code_locale);
6789 free(message_locale);
6790 err = IE_ISDS;
6791 goto leave;
6795 /* Extract data */
6796 xpath_ctx = xmlXPathNewContext(response);
6797 if (!xpath_ctx) {
6798 err = IE_ERROR;
6799 goto leave;
6801 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6802 err = IE_ERROR;
6803 goto leave;
6805 result = xmlXPathEvalExpression(
6806 (outgoing_direction) ?
6807 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
6808 "isds:dmRecords/isds:dmRecord" :
6809 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
6810 "isds:dmRecords/isds:dmRecord",
6811 xpath_ctx);
6812 if (!result) {
6813 err = IE_ERROR;
6814 goto leave;
6817 /* Fill output arguments in */
6818 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6819 struct isds_envelope *envelope;
6820 struct isds_list *item = NULL, *last_item = NULL;
6822 for (count = 0; count < result->nodesetval->nodeNr; count++) {
6823 /* Create new message */
6824 item = calloc(1, sizeof(*item));
6825 if (!item) {
6826 err = IE_NOMEM;
6827 goto leave;
6829 item->destructor = (void(*)(void**)) &isds_message_free;
6830 item->data = calloc(1, sizeof(struct isds_message));
6831 if (!item->data) {
6832 isds_list_free(&item);
6833 err = IE_NOMEM;
6834 goto leave;
6837 /* Extract envelope data */
6838 xpath_ctx->node = result->nodesetval->nodeTab[count];
6839 envelope = NULL;
6840 err = extract_DmRecord(context, &envelope, xpath_ctx);
6841 if (err) {
6842 isds_list_free(&item);
6843 goto leave;
6846 /* Attach extracted envelope */
6847 ((struct isds_message *) item->data)->envelope = envelope;
6849 /* Append new message into the list */
6850 if (!*messages) {
6851 *messages = last_item = item;
6852 } else {
6853 last_item->next = item;
6854 last_item = item;
6858 if (number) *number = count;
6860 leave:
6861 if (err) {
6862 isds_list_free(messages);
6865 free(string);
6866 xmlXPathFreeObject(result);
6867 xmlXPathFreeContext(xpath_ctx);
6869 free(code);
6870 free(message);
6871 xmlFreeDoc(response);
6872 xmlFreeNode(request);
6874 if (!err)
6875 isds_log(ILF_ISDS, ILL_DEBUG,
6876 (outgoing_direction) ?
6877 _("GetListOfSentMessages request processed by server "
6878 "successfully.\n") :
6879 _("GetListOfReceivedMessages request processed by server "
6880 "successfully.\n")
6882 return err;
6886 /* Get list of outgoing (already sent) messages.
6887 * Any criterion argument can be NULL, if you don't care about it.
6888 * @context is session context. Must not be NULL.
6889 * @from_time is minimal time and date of message sending inclusive.
6890 * @to_time is maximal time and date of message sending inclusive
6891 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
6892 * @status_filter is bit field of isds_message_status values. Use special
6893 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
6894 * all values, you can use bit-wise arithmetic if you want.)
6895 * @offset is index of first message we are interested in. First message is 1.
6896 * Set to 0 (or 1) if you don't care.
6897 * @number is maximal length of list you want to get as input value, outputs
6898 * number of messages matching these criteria. Can be NULL if you don't care
6899 * (applies to output value either).
6900 * @messages is automatically reallocated list of isds_message's. Be ware that
6901 * it returns only brief overview (envelope and some other fields) about each
6902 * message, not the complete message. FIXME: Specify exact fields.
6903 * The list is sorted by delivery time in ascending order.
6904 * Use NULL if you don't care about the meta data (useful if you want to know
6905 * only the @number). If you provide &NULL, list will be allocated on heap,
6906 * if you provide pointer to non-NULL, list will be freed automatically at first.
6907 * Also in case of error the list will be NULLed.
6908 * @return IE_SUCCESS or appropriate error code. */
6909 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
6910 const struct timeval *from_time, const struct timeval *to_time,
6911 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
6912 const unsigned long int offset, unsigned long int *number,
6913 struct isds_list **messages) {
6915 return isds_get_list_of_messages(
6916 context, 1,
6917 from_time, to_time, dmSenderOrgUnitNum, status_filter,
6918 offset, number,
6919 messages);
6923 /* Get list of incoming (addressed to you) messages.
6924 * Any criterion argument can be NULL, if you don't care about it.
6925 * @context is session context. Must not be NULL.
6926 * @from_time is minimal time and date of message sending inclusive.
6927 * @to_time is maximal time and date of message sending inclusive
6928 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
6929 * @status_filter is bit field of isds_message_status values. Use special
6930 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
6931 * all values, you can use bit-wise arithmetic if you want.)
6932 * @offset is index of first message we are interested in. First message is 1.
6933 * Set to 0 (or 1) if you don't care.
6934 * @number is maximal length of list you want to get as input value, outputs
6935 * number of messages matching these criteria. Can be NULL if you don't care
6936 * (applies to output value either).
6937 * @messages is automatically reallocated list of isds_message's. Be ware that
6938 * it returns only brief overview (envelope and some other fields) about each
6939 * message, not the complete message. FIXME: Specify exact fields.
6940 * Use NULL if you don't care about the meta data (useful if you want to know
6941 * only the @number). If you provide &NULL, list will be allocated on heap,
6942 * if you provide pointer to non-NULL, list will be freed automatically at first.
6943 * Also in case of error the list will be NULLed.
6944 * @return IE_SUCCESS or appropriate error code. */
6945 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
6946 const struct timeval *from_time, const struct timeval *to_time,
6947 const long int *dmRecipientOrgUnitNum,
6948 const unsigned int status_filter,
6949 const unsigned long int offset, unsigned long int *number,
6950 struct isds_list **messages) {
6952 return isds_get_list_of_messages(
6953 context, 0,
6954 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
6955 offset, number,
6956 messages);
6960 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
6961 * code
6962 * @context is session context
6963 * @service is ISDS WS service handler
6964 * @service_name is name of SERVICE_DM_OPERATIONS
6965 * @message_id is message ID to send as service argument to ISDS
6966 * @response is server SOAP body response as XML document
6967 * @raw_response is automatically reallocated bit stream with response body. Use
6968 * NULL if you don't care
6969 * @raw_response_length is size of @raw_response in bytes
6970 * @code is ISDS status code
6971 * @status_message is ISDS status message
6972 * @return error coded from lower layer, context message will be set up
6973 * appropriately. */
6974 static isds_error build_send_check_message_request(struct isds_ctx *context,
6975 const isds_service service, const xmlChar *service_name,
6976 const char *message_id,
6977 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
6978 xmlChar **code, xmlChar **status_message) {
6980 isds_error err = IE_SUCCESS;
6981 char *service_name_locale = NULL, *message_id_locale = NULL;
6982 xmlNodePtr request = NULL, node;
6983 xmlNsPtr isds_ns = NULL;
6985 if (!context) return IE_INVALID_CONTEXT;
6986 if (!service_name || !message_id) return IE_INVAL;
6987 if (!response || !code || !status_message) return IE_INVAL;
6988 if (!raw_response_length && raw_response) return IE_INVAL;
6990 /* Free output argument */
6991 xmlFreeDoc(*response); *response = NULL;
6992 if (raw_response) zfree(*raw_response);
6993 free(*code);
6994 free(*status_message);
6997 /* Check if connection is established
6998 * TODO: This check should be done downstairs. */
6999 if (!context->curl) return IE_CONNECTION_CLOSED;
7001 service_name_locale = _isds_utf82locale((char*)service_name);
7002 message_id_locale = _isds_utf82locale(message_id);
7003 if (!service_name_locale || !message_id_locale) {
7004 err = IE_NOMEM;
7005 goto leave;
7008 /* Build request */
7009 request = xmlNewNode(NULL, service_name);
7010 if (!request) {
7011 isds_printf_message(context,
7012 _("Could not build %s request"), service_name_locale);
7013 err = IE_ERROR;
7014 goto leave;
7016 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7017 if(!isds_ns) {
7018 isds_log_message(context, _("Could not create ISDS name space"));
7019 err = IE_ERROR;
7020 goto leave;
7022 xmlSetNs(request, isds_ns);
7025 /* Add requested ID */
7026 err = validate_message_id_length(context, (xmlChar *) message_id);
7027 if (err) goto leave;
7028 INSERT_STRING(request, "dmID", message_id);
7031 isds_log(ILF_ISDS, ILL_DEBUG,
7032 _("Sending %s request for %s message ID to ISDS\n"),
7033 service_name_locale, message_id_locale);
7035 /* Send request */
7036 err = isds(context, service, request, response,
7037 raw_response, raw_response_length);
7038 xmlFreeNode(request); request = NULL;
7040 if (err) {
7041 isds_log(ILF_ISDS, ILL_DEBUG,
7042 _("Processing ISDS response on %s request failed\n"),
7043 service_name_locale);
7044 goto leave;
7047 /* Check for response status */
7048 err = isds_response_status(context, service, *response,
7049 code, status_message, NULL);
7050 if (err) {
7051 isds_log(ILF_ISDS, ILL_DEBUG,
7052 _("ISDS response on %s request is missing status\n"),
7053 service_name_locale);
7054 goto leave;
7057 /* Request processed, but nothing found */
7058 if (xmlStrcmp(*code, BAD_CAST "0000")) {
7059 char *code_locale = _isds_utf82locale((char*) *code);
7060 char *status_message_locale = _isds_utf82locale((char*) *status_message);
7061 isds_log(ILF_ISDS, ILL_DEBUG,
7062 _("Server refused %s request for %s message ID "
7063 "(code=%s, message=%s)\n"),
7064 service_name_locale, message_id_locale,
7065 code_locale, status_message_locale);
7066 isds_log_message(context, status_message_locale);
7067 free(code_locale);
7068 free(status_message_locale);
7069 err = IE_ISDS;
7070 goto leave;
7073 leave:
7074 free(message_id_locale);
7075 free(service_name_locale);
7076 xmlFreeNode(request);
7077 return err;
7081 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
7082 * signed data and free ISDS response.
7083 * @context is session context
7084 * @message_id is UTF-8 encoded message ID for logging purpose
7085 * @response is parsed XML document. It will be freed and NULLed in the middle
7086 * of function run to save memory. This is not guaranteed in case of error.
7087 * @request_name is name of ISDS request used to construct response root
7088 * element name and for logging purpose.
7089 * @raw is reallocated output buffer with DER encoded CMS data
7090 * @raw_length is size of @raw buffer in bytes
7091 * @returns standard error codes, in case of error, @raw will be freed and
7092 * NULLed, @response sometimes. */
7093 static isds_error find_extract_signed_data_free_response(
7094 struct isds_ctx *context, const xmlChar *message_id,
7095 xmlDocPtr *response, const xmlChar *request_name,
7096 void **raw, size_t *raw_length) {
7098 isds_error err = IE_SUCCESS;
7099 char *xpath_expression = NULL;
7100 xmlXPathContextPtr xpath_ctx = NULL;
7101 xmlXPathObjectPtr result = NULL;
7102 char *encoded_structure = NULL;
7104 if (!context) return IE_INVALID_CONTEXT;
7105 if (!raw) return IE_INVAL;
7106 zfree(*raw);
7107 if (!message_id || !response || !*response || !request_name || !raw_length)
7108 return IE_INVAL;
7110 /* Build XPath expression */
7111 xpath_expression = _isds_astrcat3("/isds:", (char *) request_name,
7112 "Response/isds:dmSignature");
7113 if (!xpath_expression) return IE_NOMEM;
7115 /* Extract data */
7116 xpath_ctx = xmlXPathNewContext(*response);
7117 if (!xpath_ctx) {
7118 err = IE_ERROR;
7119 goto leave;
7121 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7122 err = IE_ERROR;
7123 goto leave;
7125 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
7126 if (!result) {
7127 err = IE_ERROR;
7128 goto leave;
7130 /* Empty response */
7131 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7132 char *message_id_locale = _isds_utf82locale((char*) message_id);
7133 isds_printf_message(context,
7134 _("Server did not return any signed data for message ID `%s' "
7135 "on %s request"),
7136 message_id_locale, request_name);
7137 free(message_id_locale);
7138 err = IE_ISDS;
7139 goto leave;
7141 /* More responses */
7142 if (result->nodesetval->nodeNr > 1) {
7143 char *message_id_locale = _isds_utf82locale((char*) message_id);
7144 isds_printf_message(context,
7145 _("Server did return more signed data for message ID `%s' "
7146 "on %s request"),
7147 message_id_locale, request_name);
7148 free(message_id_locale);
7149 err = IE_ISDS;
7150 goto leave;
7152 /* One response */
7153 xpath_ctx->node = result->nodesetval->nodeTab[0];
7155 /* Extract PKCS#7 structure */
7156 EXTRACT_STRING(".", encoded_structure);
7157 if (!encoded_structure) {
7158 isds_log_message(context, _("dmSignature element is empty"));
7161 /* Here we have delivery info as standalone CMS in encoded_structure.
7162 * We don't need any other data, free them: */
7163 xmlXPathFreeObject(result); result = NULL;
7164 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
7165 xmlFreeDoc(*response); *response = NULL;
7168 /* Decode PKCS#7 to DER format */
7169 *raw_length = _isds_b64decode(encoded_structure, raw);
7170 if (*raw_length == (size_t) -1) {
7171 isds_log_message(context,
7172 _("Error while Base64-decoding PKCS#7 structure"));
7173 err = IE_ERROR;
7174 goto leave;
7177 leave:
7178 if (err) {
7179 zfree(*raw);
7180 raw_length = 0;
7183 free(encoded_structure);
7184 xmlXPathFreeObject(result);
7185 xmlXPathFreeContext(xpath_ctx);
7186 free(xpath_expression);
7188 return err;
7192 /* Download incoming message envelope identified by ID.
7193 * @context is session context
7194 * @message_id is message identifier (you can get them from
7195 * isds_get_list_of_received_messages())
7196 * @message is automatically reallocated message retrieved from ISDS.
7197 * It will miss documents per se. Use isds_get_received_message(), if you are
7198 * interested in documents (content) too.
7199 * Returned hash and timestamp require documents to be verifiable. */
7200 isds_error isds_get_received_envelope(struct isds_ctx *context,
7201 const char *message_id, struct isds_message **message) {
7203 isds_error err = IE_SUCCESS;
7204 xmlDocPtr response = NULL;
7205 xmlChar *code = NULL, *status_message = NULL;
7206 xmlXPathContextPtr xpath_ctx = NULL;
7207 xmlXPathObjectPtr result = NULL;
7209 if (!context) return IE_INVALID_CONTEXT;
7210 zfree(context->long_message);
7212 /* Free former message if any */
7213 if (!message) return IE_INVAL;
7214 isds_message_free(message);
7216 /* Do request and check for success */
7217 err = build_send_check_message_request(context, SERVICE_DM_INFO,
7218 BAD_CAST "MessageEnvelopeDownload", message_id,
7219 &response, NULL, NULL, &code, &status_message);
7220 if (err) goto leave;
7222 /* Extract data */
7223 xpath_ctx = xmlXPathNewContext(response);
7224 if (!xpath_ctx) {
7225 err = IE_ERROR;
7226 goto leave;
7228 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7229 err = IE_ERROR;
7230 goto leave;
7232 result = xmlXPathEvalExpression(
7233 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
7234 "isds:dmReturnedMessageEnvelope",
7235 xpath_ctx);
7236 if (!result) {
7237 err = IE_ERROR;
7238 goto leave;
7240 /* Empty response */
7241 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7242 char *message_id_locale = _isds_utf82locale((char*) message_id);
7243 isds_printf_message(context,
7244 _("Server did not return any envelope for ID `%s' "
7245 "on MessageEnvelopeDownload request"), message_id_locale);
7246 free(message_id_locale);
7247 err = IE_ISDS;
7248 goto leave;
7250 /* More envelops */
7251 if (result->nodesetval->nodeNr > 1) {
7252 char *message_id_locale = _isds_utf82locale((char*) message_id);
7253 isds_printf_message(context,
7254 _("Server did return more envelopes for ID `%s' "
7255 "on MessageEnvelopeDownload request"), message_id_locale);
7256 free(message_id_locale);
7257 err = IE_ISDS;
7258 goto leave;
7260 /* One message */
7261 xpath_ctx->node = result->nodesetval->nodeTab[0];
7263 /* Extract the envelope (= message without documents, hence 0) */
7264 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
7265 if (err) goto leave;
7267 /* Save XML blob */
7268 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
7269 &(*message)->raw_length);
7271 leave:
7272 if (err) {
7273 isds_message_free(message);
7276 xmlXPathFreeObject(result);
7277 xmlXPathFreeContext(xpath_ctx);
7279 free(code);
7280 free(status_message);
7281 if (!*message || !(*message)->xml) {
7282 xmlFreeDoc(response);
7285 if (!err)
7286 isds_log(ILF_ISDS, ILL_DEBUG,
7287 _("MessageEnvelopeDownload request processed by server "
7288 "successfully.\n")
7290 return err;
7294 /* Load delivery info of any format from buffer.
7295 * @context is session context
7296 * @raw_type advertises format of @buffer content. Only delivery info types
7297 * are accepted.
7298 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
7299 * retrieve such data from message->raw after calling
7300 * isds_get_signed_delivery_info().
7301 * @length is length of buffer in bytes.
7302 * @message is automatically reallocated message parsed from @buffer.
7303 * @strategy selects how buffer will be attached into raw isds_message member.
7304 * */
7305 isds_error isds_load_delivery_info(struct isds_ctx *context,
7306 const isds_raw_type raw_type,
7307 const void *buffer, const size_t length,
7308 struct isds_message **message, const isds_buffer_strategy strategy) {
7310 isds_error err = IE_SUCCESS;
7311 message_ns_type message_ns;
7312 xmlDocPtr message_doc = NULL;
7313 xmlXPathContextPtr xpath_ctx = NULL;
7314 xmlXPathObjectPtr result = NULL;
7315 void *xml_stream = NULL;
7316 size_t xml_stream_length = 0;
7318 if (!context) return IE_INVALID_CONTEXT;
7319 zfree(context->long_message);
7320 if (!message) return IE_INVAL;
7321 isds_message_free(message);
7322 if (!buffer) return IE_INVAL;
7325 /* Select buffer format and extract XML from CMS*/
7326 switch (raw_type) {
7327 case RAWTYPE_DELIVERYINFO:
7328 message_ns = MESSAGE_NS_UNSIGNED;
7329 xml_stream = (void *) buffer;
7330 xml_stream_length = length;
7331 break;
7333 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
7334 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
7335 xml_stream = (void *) buffer;
7336 xml_stream_length = length;
7337 break;
7339 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
7340 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
7341 err = _isds_extract_cms_data(context, buffer, length,
7342 &xml_stream, &xml_stream_length);
7343 if (err) goto leave;
7344 break;
7346 default:
7347 isds_log_message(context, _("Bad raw delivery representation type"));
7348 return IE_INVAL;
7349 break;
7352 isds_log(ILF_ISDS, ILL_DEBUG,
7353 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
7354 xml_stream_length, xml_stream);
7356 /* Convert delivery info XML stream into XPath context */
7357 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
7358 if (!message_doc) {
7359 err = IE_XML;
7360 goto leave;
7362 xpath_ctx = xmlXPathNewContext(message_doc);
7363 if (!xpath_ctx) {
7364 err = IE_ERROR;
7365 goto leave;
7367 /* XXX: Name spaces mangled for signed delivery info:
7368 * http://isds.czechpoint.cz/v20/delivery:
7370 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
7371 * <q:dmDelivery>
7372 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
7373 * <p:dmID>170272</p:dmID>
7374 * ...
7375 * </p:dmDm>
7376 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
7377 * ...
7378 * </q:dmEvents>...</q:dmEvents>
7379 * </q:dmDelivery>
7380 * </q:GetDeliveryInfoResponse>
7381 * */
7382 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
7383 err = IE_ERROR;
7384 goto leave;
7386 result = xmlXPathEvalExpression(
7387 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
7388 xpath_ctx);
7389 if (!result) {
7390 err = IE_ERROR;
7391 goto leave;
7393 /* Empty delivery info */
7394 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7395 isds_printf_message(context,
7396 _("XML document is not sisds:dmDelivery document"));
7397 err = IE_ISDS;
7398 goto leave;
7400 /* More delivery info's */
7401 if (result->nodesetval->nodeNr > 1) {
7402 isds_printf_message(context,
7403 _("XML document has more sisds:dmDelivery elements"));
7404 err = IE_ISDS;
7405 goto leave;
7407 /* One delivery info */
7408 xpath_ctx->node = result->nodesetval->nodeTab[0];
7410 /* Extract the envelope (= message without documents, hence 0).
7411 * XXX: extract_TReturnedMessage() can obtain attachments size,
7412 * but delivery info carries none. It's coded as option elements,
7413 * so it should work. */
7414 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
7415 if (err) goto leave;
7417 /* Extract events */
7418 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
7419 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
7420 if (err) { err = IE_ERROR; goto leave; }
7421 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
7422 if (err) goto leave;
7424 /* Append raw CMS structure into message */
7425 (*message)->raw_type = raw_type;
7426 switch (strategy) {
7427 case BUFFER_DONT_STORE:
7428 break;
7429 case BUFFER_COPY:
7430 (*message)->raw = malloc(length);
7431 if (!(*message)->raw) {
7432 err = IE_NOMEM;
7433 goto leave;
7435 memcpy((*message)->raw, buffer, length);
7436 (*message)->raw_length = length;
7437 break;
7438 case BUFFER_MOVE:
7439 (*message)->raw = (void *) buffer;
7440 (*message)->raw_length = length;
7441 break;
7442 default:
7443 err = IE_ENUM;
7444 goto leave;
7447 leave:
7448 if (err) {
7449 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
7450 isds_message_free(message);
7453 xmlXPathFreeObject(result);
7454 xmlXPathFreeContext(xpath_ctx);
7455 if (!*message || !(*message)->xml) {
7456 xmlFreeDoc(message_doc);
7458 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
7460 if (!err)
7461 isds_log(ILF_ISDS, ILL_DEBUG,
7462 _("Delivery info loaded successfully.\n"));
7463 return err;
7467 /* Download signed delivery info-sheet of given message identified by ID.
7468 * @context is session context
7469 * @message_id is message identifier (you can get them from
7470 * isds_get_list_of_{sent,received}_messages())
7471 * @message is automatically reallocated message retrieved from ISDS.
7472 * It will miss documents per se. Use isds_get_signed_received_message(),
7473 * if you are interested in documents (content). OTOH, only this function
7474 * can get list events message has gone through. */
7475 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
7476 const char *message_id, struct isds_message **message) {
7478 isds_error err = IE_SUCCESS;
7479 xmlDocPtr response = NULL;
7480 xmlChar *code = NULL, *status_message = NULL;
7481 void *raw = NULL;
7482 size_t raw_length = 0;
7484 if (!context) return IE_INVALID_CONTEXT;
7485 zfree(context->long_message);
7487 /* Free former message if any */
7488 if (!message) return IE_INVAL;
7489 isds_message_free(message);
7491 /* Do request and check for success */
7492 err = build_send_check_message_request(context, SERVICE_DM_INFO,
7493 BAD_CAST "GetSignedDeliveryInfo", message_id,
7494 &response, NULL, NULL, &code, &status_message);
7495 if (err) goto leave;
7497 /* Find signed delivery info, extract it into raw and maybe free
7498 * response */
7499 err = find_extract_signed_data_free_response(context,
7500 (xmlChar *)message_id, &response,
7501 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
7502 if (err) goto leave;
7504 /* Parse delivery info */
7505 err = isds_load_delivery_info(context,
7506 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
7507 message, BUFFER_MOVE);
7508 if (err) goto leave;
7510 raw = NULL;
7512 leave:
7513 if (err) {
7514 isds_message_free(message);
7517 free(raw);
7518 free(code);
7519 free(status_message);
7520 xmlFreeDoc(response);
7522 if (!err)
7523 isds_log(ILF_ISDS, ILL_DEBUG,
7524 _("GetSignedDeliveryInfo request processed by server "
7525 "successfully.\n")
7527 return err;
7531 /* Download delivery info-sheet of given message identified by ID.
7532 * @context is session context
7533 * @message_id is message identifier (you can get them from
7534 * isds_get_list_of_{sent,received}_messages())
7535 * @message is automatically reallocated message retrieved from ISDS.
7536 * It will miss documents per se. Use isds_get_received_message(), if you are
7537 * interested in documents (content). OTOH, only this function can get list
7538 * of events message has gone through. */
7539 isds_error isds_get_delivery_info(struct isds_ctx *context,
7540 const char *message_id, struct isds_message **message) {
7542 isds_error err = IE_SUCCESS;
7543 xmlDocPtr response = NULL;
7544 xmlChar *code = NULL, *status_message = NULL;
7545 xmlNodePtr delivery_node = NULL;
7546 void *raw = NULL;
7547 size_t raw_length = 0;
7549 if (!context) return IE_INVALID_CONTEXT;
7550 zfree(context->long_message);
7552 /* Free former message if any */
7553 if (!message) return IE_INVAL;
7554 isds_message_free(message);
7556 /* Do request and check for success */
7557 err = build_send_check_message_request(context, SERVICE_DM_INFO,
7558 BAD_CAST "GetDeliveryInfo", message_id,
7559 &response, NULL, NULL, &code, &status_message);
7560 if (err) goto leave;
7563 /* Serialize delivery info */
7564 delivery_node = xmlDocGetRootElement(response);
7565 if (!delivery_node) {
7566 char *message_id_locale = _isds_utf82locale((char*) message_id);
7567 isds_printf_message(context,
7568 _("Server did not return any delivery info for ID `%s' "
7569 "on GetDeliveryInfo request"), message_id_locale);
7570 free(message_id_locale);
7571 err = IE_ISDS;
7572 goto leave;
7574 err = serialize_subtree(context, delivery_node, &raw, &raw_length);
7575 if (err) goto leave;
7577 /* Parse delivery info */
7578 /* TODO: Here we parse the response second time. We could single delivery
7579 * parser from isds_load_delivery_info() to make things faster. */
7580 err = isds_load_delivery_info(context,
7581 RAWTYPE_DELIVERYINFO, raw, raw_length,
7582 message, BUFFER_MOVE);
7583 if (err) goto leave;
7585 raw = NULL;
7588 leave:
7589 if (err) {
7590 isds_message_free(message);
7593 free(raw);
7594 free(code);
7595 free(status_message);
7596 xmlFreeDoc(response);
7598 if (!err)
7599 isds_log(ILF_ISDS, ILL_DEBUG,
7600 _("GetDeliveryInfo request processed by server "
7601 "successfully.\n")
7603 return err;
7607 /* Download incoming message identified by ID.
7608 * @context is session context
7609 * @message_id is message identifier (you can get them from
7610 * isds_get_list_of_received_messages())
7611 * @message is automatically reallocated message retrieved from ISDS */
7612 isds_error isds_get_received_message(struct isds_ctx *context,
7613 const char *message_id, struct isds_message **message) {
7615 isds_error err = IE_SUCCESS;
7616 xmlDocPtr response = NULL;
7617 void *xml_stream = NULL;
7618 size_t xml_stream_length;
7619 xmlChar *code = NULL, *status_message = NULL;
7620 xmlXPathContextPtr xpath_ctx = NULL;
7621 xmlXPathObjectPtr result = NULL;
7622 char *phys_path = NULL;
7623 size_t phys_start, phys_end;
7625 if (!context) return IE_INVALID_CONTEXT;
7626 zfree(context->long_message);
7628 /* Free former message if any */
7629 if (message) isds_message_free(message);
7631 /* Do request and check for success */
7632 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
7633 BAD_CAST "MessageDownload", message_id,
7634 &response, &xml_stream, &xml_stream_length,
7635 &code, &status_message);
7636 if (err) goto leave;
7638 /* Extract data */
7639 xpath_ctx = xmlXPathNewContext(response);
7640 if (!xpath_ctx) {
7641 err = IE_ERROR;
7642 goto leave;
7644 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7645 err = IE_ERROR;
7646 goto leave;
7648 result = xmlXPathEvalExpression(
7649 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
7650 xpath_ctx);
7651 if (!result) {
7652 err = IE_ERROR;
7653 goto leave;
7655 /* Empty response */
7656 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7657 char *message_id_locale = _isds_utf82locale((char*) message_id);
7658 isds_printf_message(context,
7659 _("Server did not return any message for ID `%s' "
7660 "on MessageDownload request"), message_id_locale);
7661 free(message_id_locale);
7662 err = IE_ISDS;
7663 goto leave;
7665 /* More messages */
7666 if (result->nodesetval->nodeNr > 1) {
7667 char *message_id_locale = _isds_utf82locale((char*) message_id);
7668 isds_printf_message(context,
7669 _("Server did return more messages for ID `%s' "
7670 "on MessageDownload request"), message_id_locale);
7671 free(message_id_locale);
7672 err = IE_ISDS;
7673 goto leave;
7675 /* One message */
7676 xpath_ctx->node = result->nodesetval->nodeTab[0];
7678 /* Extract the message */
7679 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
7680 if (err) goto leave;
7682 /* Locate raw XML blob */
7683 phys_path = strdup(
7684 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
7685 PHYSXML_ELEMENT_SEPARATOR
7686 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
7687 PHYSXML_ELEMENT_SEPARATOR
7688 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
7690 if (!phys_path) {
7691 err = IE_NOMEM;
7692 goto leave;
7694 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
7695 phys_path, &phys_start, &phys_end);
7696 zfree(phys_path);
7697 if (err) {
7698 isds_log_message(context,
7699 _("Substring with isds:MessageDownloadResponse element "
7700 "could not be located in raw SOAP message"));
7701 goto leave;
7703 /* Save XML blob */
7704 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
7705 &(*message)->raw_length);*/
7706 /* TODO: Store name space declarations from ancestors */
7707 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
7708 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
7709 (*message)->raw_length = phys_end - phys_start + 1;
7710 (*message)->raw = malloc((*message)->raw_length);
7711 if (!(*message)->raw) {
7712 err = IE_NOMEM;
7713 goto leave;
7715 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
7718 leave:
7719 if (err) {
7720 isds_message_free(message);
7723 free(phys_path);
7725 xmlXPathFreeObject(result);
7726 xmlXPathFreeContext(xpath_ctx);
7728 free(code);
7729 free(status_message);
7730 free(xml_stream);
7731 if (!*message || !(*message)->xml) {
7732 xmlFreeDoc(response);
7735 if (!err)
7736 isds_log(ILF_ISDS, ILL_DEBUG,
7737 _("MessageDownload request processed by server "
7738 "successfully.\n")
7740 return err;
7744 /* Load message of any type from buffer.
7745 * @context is session context
7746 * @raw_type defines content type of @buffer. Only message types are allowed.
7747 * @buffer is message raw representation. Format (CMS, plain signed,
7748 * message direction) is defined in @raw_type. You can retrieve such data
7749 * from message->raw after calling isds_get_[signed]{received,sent}_message().
7750 * @length is length of buffer in bytes.
7751 * @message is automatically reallocated message parsed from @buffer.
7752 * @strategy selects how buffer will be attached into raw isds_message member.
7753 * */
7754 isds_error isds_load_message(struct isds_ctx *context,
7755 const isds_raw_type raw_type, const void *buffer, const size_t length,
7756 struct isds_message **message, const isds_buffer_strategy strategy) {
7758 isds_error err = IE_SUCCESS;
7759 void *xml_stream = NULL;
7760 size_t xml_stream_length = 0;
7761 message_ns_type message_ns;
7762 xmlDocPtr message_doc = NULL;
7763 xmlXPathContextPtr xpath_ctx = NULL;
7764 xmlXPathObjectPtr result = NULL;
7766 if (!context) return IE_INVALID_CONTEXT;
7767 zfree(context->long_message);
7768 if (!message) return IE_INVAL;
7769 isds_message_free(message);
7770 if (!buffer) return IE_INVAL;
7773 /* Select buffer format and extract XML from CMS*/
7774 switch (raw_type) {
7775 case RAWTYPE_INCOMING_MESSAGE:
7776 message_ns = MESSAGE_NS_UNSIGNED;
7777 xml_stream = (void *) buffer;
7778 xml_stream_length = length;
7779 break;
7781 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
7782 message_ns = MESSAGE_NS_SIGNED_INCOMING;
7783 xml_stream = (void *) buffer;
7784 xml_stream_length = length;
7785 break;
7787 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
7788 message_ns = MESSAGE_NS_SIGNED_INCOMING;
7789 err = _isds_extract_cms_data(context, buffer, length,
7790 &xml_stream, &xml_stream_length);
7791 if (err) goto leave;
7792 break;
7794 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
7795 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
7796 xml_stream = (void *) buffer;
7797 xml_stream_length = length;
7798 break;
7800 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
7801 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
7802 err = _isds_extract_cms_data(context, buffer, length,
7803 &xml_stream, &xml_stream_length);
7804 if (err) goto leave;
7805 break;
7807 default:
7808 isds_log_message(context, _("Bad raw message representation type"));
7809 return IE_INVAL;
7810 break;
7813 isds_log(ILF_ISDS, ILL_DEBUG,
7814 _("Loading message:\n%.*s\nEnd of message\n"),
7815 xml_stream_length, xml_stream);
7817 /* Convert messages XML stream into XPath context */
7818 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
7819 if (!message_doc) {
7820 err = IE_XML;
7821 goto leave;
7823 xpath_ctx = xmlXPathNewContext(message_doc);
7824 if (!xpath_ctx) {
7825 err = IE_ERROR;
7826 goto leave;
7828 /* XXX: Standard name space for unsigned incoming direction:
7829 * http://isds.czechpoint.cz/v20/
7831 * XXX: Name spaces mangled for signed outgoing direction:
7832 * http://isds.czechpoint.cz/v20/SentMessage:
7834 * <q:MessageDownloadResponse
7835 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
7836 * <q:dmReturnedMessage>
7837 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
7838 * <p:dmID>151916</p:dmID>
7839 * ...
7840 * </p:dmDm>
7841 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
7842 * ...
7843 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
7844 * </q:dmReturnedMessage>
7845 * </q:MessageDownloadResponse>
7847 * XXX: Name spaces mangled for signed incoming direction:
7848 * http://isds.czechpoint.cz/v20/message:
7850 * <q:MessageDownloadResponse
7851 * xmlns:q="http://isds.czechpoint.cz/v20/message">
7852 * <q:dmReturnedMessage>
7853 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
7854 * <p:dmID>151916</p:dmID>
7855 * ...
7856 * </p:dmDm>
7857 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
7858 * ...
7859 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
7860 * </q:dmReturnedMessage>
7861 * </q:MessageDownloadResponse>
7863 * Stupidity of ISDS developers is unlimited */
7864 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
7865 err = IE_ERROR;
7866 goto leave;
7868 result = xmlXPathEvalExpression(
7869 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
7870 xpath_ctx);
7871 if (!result) {
7872 err = IE_ERROR;
7873 goto leave;
7875 /* Empty message */
7876 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7877 isds_printf_message(context,
7878 _("XML document does not contain "
7879 "sisds:dmReturnedMessage element"));
7880 err = IE_ISDS;
7881 goto leave;
7883 /* More messages */
7884 if (result->nodesetval->nodeNr > 1) {
7885 isds_printf_message(context,
7886 _("XML document has more sisds:dmReturnedMessage elements"));
7887 err = IE_ISDS;
7888 goto leave;
7890 /* One message */
7891 xpath_ctx->node = result->nodesetval->nodeTab[0];
7893 /* Extract the message */
7894 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
7895 if (err) goto leave;
7897 /* Append raw buffer into message */
7898 (*message)->raw_type = raw_type;
7899 switch (strategy) {
7900 case BUFFER_DONT_STORE:
7901 break;
7902 case BUFFER_COPY:
7903 (*message)->raw = malloc(length);
7904 if (!(*message)->raw) {
7905 err = IE_NOMEM;
7906 goto leave;
7908 memcpy((*message)->raw, buffer, length);
7909 (*message)->raw_length = length;
7910 break;
7911 case BUFFER_MOVE:
7912 (*message)->raw = (void *) buffer;
7913 (*message)->raw_length = length;
7914 break;
7915 default:
7916 err = IE_ENUM;
7917 goto leave;
7921 leave:
7922 if (err) {
7923 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
7924 isds_message_free(message);
7927 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
7928 xmlXPathFreeObject(result);
7929 xmlXPathFreeContext(xpath_ctx);
7930 if (!*message || !(*message)->xml) {
7931 xmlFreeDoc(message_doc);
7934 if (!err)
7935 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
7936 return err;
7940 /* Determine type of raw message or delivery info according some heuristics.
7941 * It does not validate the raw blob.
7942 * @context is session context
7943 * @raw_type returns content type of @buffer. Valid only if exit code of this
7944 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
7945 * reallocated memory.
7946 * @buffer is message raw representation.
7947 * @length is length of buffer in bytes. */
7948 isds_error isds_guess_raw_type(struct isds_ctx *context,
7949 isds_raw_type *raw_type, const void *buffer, const size_t length) {
7950 isds_error err;
7951 void *xml_stream = NULL;
7952 size_t xml_stream_length = 0;
7953 xmlDocPtr document = NULL;
7954 xmlNodePtr root = NULL;
7956 if (!context) return IE_INVALID_CONTEXT;
7957 zfree(context->long_message);
7958 if (length == 0 || !buffer) return IE_INVAL;
7959 if (!raw_type) return IE_INVAL;
7961 /* Try CMS */
7962 err = _isds_extract_cms_data(context, buffer, length,
7963 &xml_stream, &xml_stream_length);
7964 if (err) {
7965 xml_stream = (void *) buffer;
7966 xml_stream_length = (size_t) length;
7967 err = IE_SUCCESS;
7970 /* Try XML */
7971 document = xmlParseMemory(xml_stream, xml_stream_length);
7972 if (!document) {
7973 isds_printf_message(context,
7974 _("Could not parse data as XML document"));
7975 err = IE_NOTSUP;
7976 goto leave;
7979 /* Get root element */
7980 root = xmlDocGetRootElement(document);
7981 if (!root) {
7982 isds_printf_message(context,
7983 _("XML document is missing root element"));
7984 err = IE_XML;
7985 goto leave;
7988 if (!root->ns || !root->ns->href) {
7989 isds_printf_message(context,
7990 _("Root element does not belong to any name space"));
7991 err = IE_NOTSUP;
7992 goto leave;
7995 /* Test name space */
7996 if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_INCOMING_NS)) {
7997 if (xml_stream == buffer)
7998 *raw_type = RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE;
7999 else
8000 *raw_type = RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
8001 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_OUTGOING_NS)) {
8002 if (xml_stream == buffer)
8003 *raw_type = RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE;
8004 else
8005 *raw_type = RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE;
8006 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_DELIVERY_NS)) {
8007 if (xml_stream == buffer)
8008 *raw_type = RAWTYPE_PLAIN_SIGNED_DELIVERYINFO;
8009 else
8010 *raw_type = RAWTYPE_CMS_SIGNED_DELIVERYINFO;
8011 } else if (!xmlStrcmp(root->ns->href, BAD_CAST ISDS_NS)) {
8012 if (xml_stream != buffer) {
8013 isds_printf_message(context,
8014 _("Document in ISDS name space is encapsulated into CMS" ));
8015 err = IE_NOTSUP;
8016 } else if (!xmlStrcmp(root->name, BAD_CAST "MessageDownloadResponse"))
8017 *raw_type = RAWTYPE_INCOMING_MESSAGE;
8018 else if (!xmlStrcmp(root->name, BAD_CAST "GetDeliveryInfoResponse"))
8019 *raw_type = RAWTYPE_DELIVERYINFO;
8020 else {
8021 isds_printf_message(context,
8022 _("Unknown root element in ISDS name space"));
8023 err = IE_NOTSUP;
8025 } else {
8026 isds_printf_message(context,
8027 _("Unknown name space"));
8028 err = IE_NOTSUP;
8031 leave:
8032 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
8033 xmlFreeDoc(document);
8034 return err;
8038 /* Download signed incoming/outgoing message identified by ID.
8039 * @context is session context
8040 * @output is true for outgoing message, false for incoming message
8041 * @message_id is message identifier (you can get them from
8042 * isds_get_list_of_{sent,received}_messages())
8043 * @message is automatically reallocated message retrieved from ISDS. The raw
8044 * member will be filled with PKCS#7 structure in DER format. */
8045 static isds_error isds_get_signed_message(struct isds_ctx *context,
8046 const _Bool outgoing, const char *message_id,
8047 struct isds_message **message) {
8049 isds_error err = IE_SUCCESS;
8050 xmlDocPtr response = NULL;
8051 xmlChar *code = NULL, *status_message = NULL;
8052 xmlXPathContextPtr xpath_ctx = NULL;
8053 xmlXPathObjectPtr result = NULL;
8054 char *encoded_structure = NULL;
8055 void *raw = NULL;
8056 size_t raw_length = 0;
8058 if (!context) return IE_INVALID_CONTEXT;
8059 zfree(context->long_message);
8060 if (!message) return IE_INVAL;
8061 isds_message_free(message);
8063 /* Do request and check for success */
8064 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
8065 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
8066 BAD_CAST "SignedMessageDownload",
8067 message_id, &response, NULL, NULL, &code, &status_message);
8068 if (err) goto leave;
8070 /* Find signed message, extract it into raw and maybe free
8071 * response */
8072 err = find_extract_signed_data_free_response(context,
8073 (xmlChar *)message_id, &response,
8074 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
8075 BAD_CAST "SignedMessageDownload",
8076 &raw, &raw_length);
8077 if (err) goto leave;
8079 /* Parse message */
8080 err = isds_load_message(context,
8081 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
8082 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
8083 raw, raw_length, message, BUFFER_MOVE);
8084 if (err) goto leave;
8086 raw = NULL;
8088 leave:
8089 if (err) {
8090 isds_message_free(message);
8093 free(encoded_structure);
8094 xmlXPathFreeObject(result);
8095 xmlXPathFreeContext(xpath_ctx);
8096 free(raw);
8098 free(code);
8099 free(status_message);
8100 xmlFreeDoc(response);
8102 if (!err)
8103 isds_log(ILF_ISDS, ILL_DEBUG,
8104 (outgoing) ?
8105 _("SignedSentMessageDownload request processed by server "
8106 "successfully.\n") :
8107 _("SignedMessageDownload request processed by server "
8108 "successfully.\n")
8110 return err;
8114 /* Download signed incoming message identified by ID.
8115 * @context is session context
8116 * @message_id is message identifier (you can get them from
8117 * isds_get_list_of_received_messages())
8118 * @message is automatically reallocated message retrieved from ISDS. The raw
8119 * member will be filled with PKCS#7 structure in DER format. */
8120 isds_error isds_get_signed_received_message(struct isds_ctx *context,
8121 const char *message_id, struct isds_message **message) {
8122 return isds_get_signed_message(context, 0, message_id, message);
8126 /* Download signed outgoing message identified by ID.
8127 * @context is session context
8128 * @message_id is message identifier (you can get them from
8129 * isds_get_list_of_sent_messages())
8130 * @message is automatically reallocated message retrieved from ISDS. The raw
8131 * member will be filled with PKCS#7 structure in DER format. */
8132 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
8133 const char *message_id, struct isds_message **message) {
8134 return isds_get_signed_message(context, 1, message_id, message);
8138 /* Retrieve hash of message identified by ID stored in ISDS.
8139 * @context is session context
8140 * @message_id is message identifier
8141 * @hash is automatically reallocated message hash downloaded from ISDS.
8142 * Message must exist in system and must not be deleted. */
8143 isds_error isds_download_message_hash(struct isds_ctx *context,
8144 const char *message_id, struct isds_hash **hash) {
8146 isds_error err = IE_SUCCESS;
8147 xmlDocPtr response = NULL;
8148 xmlChar *code = NULL, *status_message = NULL;
8149 xmlXPathContextPtr xpath_ctx = NULL;
8150 xmlXPathObjectPtr result = NULL;
8152 if (!context) return IE_INVALID_CONTEXT;
8153 zfree(context->long_message);
8155 isds_hash_free(hash);
8157 err = build_send_check_message_request(context, SERVICE_DM_INFO,
8158 BAD_CAST "VerifyMessage", message_id,
8159 &response, NULL, NULL, &code, &status_message);
8160 if (err) goto leave;
8163 /* Extract data */
8164 xpath_ctx = xmlXPathNewContext(response);
8165 if (!xpath_ctx) {
8166 err = IE_ERROR;
8167 goto leave;
8169 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8170 err = IE_ERROR;
8171 goto leave;
8173 result = xmlXPathEvalExpression(
8174 BAD_CAST "/isds:VerifyMessageResponse",
8175 xpath_ctx);
8176 if (!result) {
8177 err = IE_ERROR;
8178 goto leave;
8180 /* Empty response */
8181 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8182 char *message_id_locale = _isds_utf82locale((char*) message_id);
8183 isds_printf_message(context,
8184 _("Server did not return any response for ID `%s' "
8185 "on VerifyMessage request"), message_id_locale);
8186 free(message_id_locale);
8187 err = IE_ISDS;
8188 goto leave;
8190 /* More responses */
8191 if (result->nodesetval->nodeNr > 1) {
8192 char *message_id_locale = _isds_utf82locale((char*) message_id);
8193 isds_printf_message(context,
8194 _("Server did return more responses for ID `%s' "
8195 "on VerifyMessage request"), message_id_locale);
8196 free(message_id_locale);
8197 err = IE_ISDS;
8198 goto leave;
8200 /* One response */
8201 xpath_ctx->node = result->nodesetval->nodeTab[0];
8203 /* Extract the hash */
8204 err = find_and_extract_DmHash(context, hash, xpath_ctx);
8206 leave:
8207 if (err) {
8208 isds_hash_free(hash);
8211 xmlXPathFreeObject(result);
8212 xmlXPathFreeContext(xpath_ctx);
8214 free(code);
8215 free(status_message);
8216 xmlFreeDoc(response);
8218 if (!err)
8219 isds_log(ILF_ISDS, ILL_DEBUG,
8220 _("VerifyMessage request processed by server "
8221 "successfully.\n")
8223 return err;
8227 /* Mark message as read. This is a transactional commit function to acknowledge
8228 * to ISDS the message has been downloaded and processed by client properly.
8229 * @context is session context
8230 * @message_id is message identifier. */
8231 isds_error isds_mark_message_read(struct isds_ctx *context,
8232 const char *message_id) {
8234 isds_error err = IE_SUCCESS;
8235 xmlDocPtr response = NULL;
8236 xmlChar *code = NULL, *status_message = NULL;
8238 if (!context) return IE_INVALID_CONTEXT;
8239 zfree(context->long_message);
8241 /* Do request and check for success */
8242 err = build_send_check_message_request(context, SERVICE_DM_INFO,
8243 BAD_CAST "MarkMessageAsDownloaded", message_id,
8244 &response, NULL, NULL, &code, &status_message);
8246 free(code);
8247 free(status_message);
8248 xmlFreeDoc(response);
8250 if (!err)
8251 isds_log(ILF_ISDS, ILL_DEBUG,
8252 _("MarkMessageAsDownloaded request processed by server "
8253 "successfully.\n")
8255 return err;
8259 /* Mark message as received by recipient. This is applicable only to
8260 * commercial message. Use envelope->dmType message member to distinguish
8261 * commercial message from government message. Government message is
8262 * received automatically (by law), commercial message on recipient request.
8263 * @context is session context
8264 * @message_id is message identifier. */
8265 isds_error isds_mark_message_received(struct isds_ctx *context,
8266 const char *message_id) {
8268 isds_error err = IE_SUCCESS;
8269 xmlDocPtr response = NULL;
8270 xmlChar *code = NULL, *status_message = NULL;
8272 if (!context) return IE_INVALID_CONTEXT;
8273 zfree(context->long_message);
8275 /* Do request and check for success */
8276 err = build_send_check_message_request(context, SERVICE_DM_INFO,
8277 BAD_CAST "ConfirmDelivery", message_id,
8278 &response, NULL, NULL, &code, &status_message);
8280 free(code);
8281 free(status_message);
8282 xmlFreeDoc(response);
8284 if (!err)
8285 isds_log(ILF_ISDS, ILL_DEBUG,
8286 _("ConfirmDelivery request processed by server "
8287 "successfully.\n")
8289 return err;
8293 /* Send document for authorize conversion into Czech POINT system.
8294 * This is public anonymous service, no log-in necessary. Special context is
8295 * used to reuse keep-a-live HTTPS connection.
8296 * @context is Czech POINT session context. DO NOT use context connected to
8297 * ISDS server. Use new context or context used by this function previously.
8298 * @document is document to convert. Only data, data_length and dmFileDescr
8299 * members are significant. Be ware that not all document formats can be
8300 * converted (signed PDF 1.3 and higher only (2010-02 state)).
8301 * @id is reallocated identifier assigned by Czech POINT system to
8302 * your document on submit. Use is to tell it to Czech POINT officer.
8303 * @date is reallocated document submit date (submitted documents
8304 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
8305 * value. */
8306 isds_error czp_convert_document(struct isds_ctx *context,
8307 const struct isds_document *document,
8308 char **id, struct tm **date) {
8309 isds_error err = IE_SUCCESS;
8310 xmlNsPtr deposit_ns = NULL, empty_ns = NULL;
8311 xmlNodePtr request = NULL, node;
8312 xmlDocPtr response = NULL;
8314 xmlXPathContextPtr xpath_ctx = NULL;
8315 xmlXPathObjectPtr result = NULL;
8316 long int status = -1;
8317 long int *status_ptr = &status;
8318 char *string = NULL;
8321 if (!context) return IE_INVALID_CONTEXT;
8322 zfree(context->long_message);
8323 if (!document || !id || !date) return IE_INVAL;
8325 /* Free output arguments */
8326 zfree(*id);
8327 zfree(*date);
8329 /* Store configuration */
8330 context->type = CTX_TYPE_CZP;
8331 free(context->url);
8332 context->url = strdup("https://www.czechpoint.cz/uschovna/services.php");
8333 if (!(context->url))
8334 return IE_NOMEM;
8336 /* Prepare CURL handle if not yet connected */
8337 if (!context->curl) {
8338 context->curl = curl_easy_init();
8339 if (!(context->curl))
8340 return IE_ERROR;
8343 /* Build conversion request */
8344 request = xmlNewNode(NULL, BAD_CAST "saveDocument");
8345 if (!request) {
8346 isds_log_message(context,
8347 _("Could not build Czech POINT conversion request"));
8348 return IE_ERROR;
8350 deposit_ns = xmlNewNs(request, BAD_CAST DEPOSIT_NS, BAD_CAST "dep");
8351 if(!deposit_ns) {
8352 isds_log_message(context,
8353 _("Could not create Czech POINT deposit name space"));
8354 xmlFreeNode(request);
8355 return IE_ERROR;
8357 xmlSetNs(request, deposit_ns);
8359 /* Insert children. They are in empty namespace! */
8360 empty_ns = xmlNewNs(request, BAD_CAST "", NULL);
8361 if(!empty_ns) {
8362 isds_log_message(context, _("Could not create empty name space"));
8363 err = IE_ERROR;
8364 goto leave;
8366 INSERT_STRING_WITH_NS(request, empty_ns, "conversionID", "0");
8367 INSERT_STRING_WITH_NS(request, empty_ns, "fileName",
8368 document->dmFileDescr);
8370 /* Document encoded in Base64 */
8371 err = insert_base64_encoded_string(context, request, empty_ns, "document",
8372 document->data, document->data_length);
8373 if (err) goto leave;
8375 isds_log(ILF_ISDS, ILL_DEBUG,
8376 _("Submitting document for conversion into Czech POINT deposit"));
8378 /* Send conversion request */
8379 err = _czp_czpdeposit(context, request, &response);
8380 xmlFreeNode(request); request = NULL;
8382 if (err) {
8383 czp_do_close_connection(context);
8384 goto leave;
8388 /* Extract response */
8389 xpath_ctx = xmlXPathNewContext(response);
8390 if (!xpath_ctx) {
8391 err = IE_ERROR;
8392 goto leave;
8394 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8395 err = IE_ERROR;
8396 goto leave;
8398 result = xmlXPathEvalExpression(
8399 BAD_CAST "/deposit:saveDocumentResponse/return",
8400 xpath_ctx);
8401 if (!result) {
8402 err = IE_ERROR;
8403 goto leave;
8405 /* Empty response */
8406 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8407 isds_printf_message(context,
8408 _("Missing `return' element in Czech POINT deposit response"));
8409 err = IE_ISDS;
8410 goto leave;
8412 /* More responses */
8413 if (result->nodesetval->nodeNr > 1) {
8414 isds_printf_message(context,
8415 _("Multiple `return' element in Czech POINT deposit response"));
8416 err = IE_ISDS;
8417 goto leave;
8419 /* One response */
8420 xpath_ctx->node = result->nodesetval->nodeTab[0];
8422 /* Get status */
8423 EXTRACT_LONGINT("status", status_ptr, 1);
8424 if (status) {
8425 EXTRACT_STRING("statusMsg", string);
8426 char *string_locale = _isds_utf82locale(string);
8427 isds_printf_message(context,
8428 _("Czech POINT deposit refused document for conversion "
8429 "(code=%ld, message=%s)"),
8430 status, string_locale);
8431 free(string_locale);
8432 err = IE_ISDS;
8433 goto leave;
8436 /* Get document ID */
8437 EXTRACT_STRING("documentID", *id);
8439 /* Get submit date */
8440 EXTRACT_STRING("dateInserted", string);
8441 if (string) {
8442 *date = calloc(1, sizeof(**date));
8443 if (!*date) {
8444 err = IE_NOMEM;
8445 goto leave;
8447 err = datestring2tm((xmlChar *)string, *date);
8448 if (err) {
8449 if (err == IE_NOTSUP) {
8450 err = IE_ISDS;
8451 char *string_locale = _isds_utf82locale(string);
8452 isds_printf_message(context,
8453 _("Invalid dateInserted value: %s"), string_locale);
8454 free(string_locale);
8456 goto leave;
8460 leave:
8461 free(string);
8462 xmlXPathFreeObject(result);
8463 xmlXPathFreeContext(xpath_ctx);
8465 xmlFreeDoc(response);
8466 xmlFreeNode(request);
8468 if (!err) {
8469 char *id_locale = _isds_utf82locale((char *) *id);
8470 isds_log(ILF_ISDS, ILL_DEBUG,
8471 _("Document %s has been submitted for conversion "
8472 "to server successfully\n"), id_locale);
8473 free(id_locale);
8475 return err;
8479 /* Close possibly opened connection to Czech POINT document deposit.
8480 * @context is Czech POINT session context. */
8481 isds_error czp_close_connection(struct isds_ctx *context) {
8482 if (!context) return IE_INVALID_CONTEXT;
8483 zfree(context->long_message);
8484 return czp_do_close_connection(context);
8488 /* Send request for new box creation in testing ISDS instance.
8489 * It's not possible to request for a production box currently, as it
8490 * communicates via e-mail.
8491 * XXX: This function does not work either. Server complains about invalid
8492 * e-mail address.
8493 * XXX: Remove context->type hacks in isds.c and validator.c when removing
8494 * this function
8495 * @context is special session context for box creation request. DO NOT use
8496 * standard context as it could reveal your password. Use fresh new context or
8497 * context previously used by this function.
8498 * @box is box description to create including single primary user (in case of
8499 * FO box type). It outputs box ID assigned by ISDS in dbID element.
8500 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
8501 * box, or contact address of PFO box owner). The email member is mandatory as
8502 * it will be used to deliver credentials.
8503 * @former_names is optional undocumented string. Pass NULL if you don't care.
8504 * @approval is optional external approval of box manipulation
8505 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8506 * NULL, if you don't care.*/
8507 isds_error isds_request_new_testing_box(struct isds_ctx *context,
8508 struct isds_DbOwnerInfo *box, const struct isds_list *users,
8509 const char *former_names, const struct isds_approval *approval,
8510 char **refnumber) {
8511 isds_error err = IE_SUCCESS;
8512 xmlNodePtr request = NULL;
8513 xmlDocPtr response = NULL;
8514 xmlXPathContextPtr xpath_ctx = NULL;
8515 xmlXPathObjectPtr result = NULL;
8518 if (!context) return IE_INVALID_CONTEXT;
8519 zfree(context->long_message);
8520 if (!box) return IE_INVAL;
8522 if (!box->email || box->email[0] == '\0') {
8523 isds_log_message(context, _("E-mail field is mandatory"));
8524 return IE_INVAL;
8527 /* Scratch box ID */
8528 zfree(box->dbID);
8530 /* Store configuration */
8531 context->type = CTX_TYPE_TESTING_REQUEST_COLLECTOR;
8532 free(context->url);
8533 context->url = strdup("http://78.102.19.203/testbox/request_box.php");
8534 if (!(context->url))
8535 return IE_NOMEM;
8537 /* Prepare CURL handle if not yet connected */
8538 if (!context->curl) {
8539 context->curl = curl_easy_init();
8540 if (!(context->curl))
8541 return IE_ERROR;
8544 /* Build CreateDataBox request */
8545 err = build_CreateDBInput_request(context,
8546 &request, BAD_CAST "CreateDataBox",
8547 box, users, (xmlChar *) former_names, NULL, NULL, 0, approval);
8548 if (err) goto leave;
8550 /* Send it to server and process response */
8551 err = send_destroy_request_check_response(context,
8552 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
8553 &response, (xmlChar **) refnumber);
8554 if (err) goto leave;
8556 /* Extract box ID */
8557 xpath_ctx = xmlXPathNewContext(response);
8558 if (!xpath_ctx) {
8559 err = IE_ERROR;
8560 goto leave;
8562 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8563 err = IE_ERROR;
8564 goto leave;
8566 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
8568 leave:
8569 xmlXPathFreeObject(result);
8570 xmlXPathFreeContext(xpath_ctx);
8571 xmlFreeDoc(response);
8572 xmlFreeNode(request);
8574 if (!err) {
8575 isds_log(ILF_ISDS, ILL_DEBUG,
8576 _("CreateDataBox request processed by server successfully.\n"));
8579 return err;
8583 /* Submit CMS signed message to ISDS to verify its originality. This is
8584 * stronger form of isds_verify_message_hash() because ISDS does more checks
8585 * than simple one (potentialy old weak) hash comparison.
8586 * @context is session context
8587 * @message is memory with raw CMS signed message bit stream
8588 * @length is @message size in bytes
8589 * @return
8590 * IE_SUCCESS if message originates in ISDS
8591 * IE_NOTEQUAL if message is unknown to ISDS
8592 * other code for other errors */
8593 isds_error isds_authenticate_message(struct isds_ctx *context,
8594 const void *message, size_t length) {
8595 isds_error err = IE_SUCCESS;
8596 xmlNsPtr isds_ns = NULL;
8597 xmlNodePtr request = NULL;
8598 xmlDocPtr response = NULL;
8599 xmlXPathContextPtr xpath_ctx = NULL;
8600 xmlXPathObjectPtr result = NULL;
8601 _Bool *authentic = NULL;
8603 if (!context) return IE_INVALID_CONTEXT;
8604 zfree(context->long_message);
8605 if (!message || length == 0) return IE_INVAL;
8607 /* Check if connection is established
8608 * TODO: This check should be done downstairs. */
8609 if (!context->curl) return IE_CONNECTION_CLOSED;
8612 /* Build AuthenticateMessage request */
8613 request = xmlNewNode(NULL, BAD_CAST "AuthenticateMessage");
8614 if (!request) {
8615 isds_log_message(context,
8616 _("Could not build AuthenticateMessage request"));
8617 return IE_ERROR;
8619 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8620 if(!isds_ns) {
8621 isds_log_message(context, _("Could not create ISDS name space"));
8622 xmlFreeNode(request);
8623 return IE_ERROR;
8625 xmlSetNs(request, isds_ns);
8627 /* Insert Base64 encoded message */
8628 err = insert_base64_encoded_string(context, request, NULL, "dmMessage",
8629 message, length);
8630 if (err) goto leave;
8632 /* Send request to server and process response */
8633 err = send_destroy_request_check_response(context,
8634 SERVICE_DM_OPERATIONS, BAD_CAST "AuthenticateMessage", &request,
8635 &response, NULL);
8636 if (err) goto leave;
8639 /* ISDS has decided */
8640 xpath_ctx = xmlXPathNewContext(response);
8641 if (!xpath_ctx) {
8642 err = IE_ERROR;
8643 goto leave;
8645 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8646 err = IE_ERROR;
8647 goto leave;
8650 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic);
8652 if (!authentic) {
8653 isds_log_message(context,
8654 _("Server did not return any response on "
8655 "AuthenticateMessage request"));
8656 err = IE_ISDS;
8657 goto leave;
8659 if (*authentic) {
8660 isds_log(ILF_ISDS, ILL_DEBUG,
8661 _("ISDS authenticated the message successfully\n"));
8662 } else {
8663 isds_log_message(context, _("ISDS does not know the message"));
8664 err = IE_NOTEQUAL;
8668 leave:
8669 free(authentic);
8670 xmlXPathFreeObject(result);
8671 xmlXPathFreeContext(xpath_ctx);
8673 xmlFreeDoc(response);
8674 xmlFreeNode(request);
8676 return err;
8679 #undef INSERT_ELEMENT
8680 #undef CHECK_FOR_STRING_LENGTH
8681 #undef INSERT_STRING_ATTRIBUTE
8682 #undef INSERT_ULONGINTNOPTR
8683 #undef INSERT_ULONGINT
8684 #undef INSERT_LONGINT
8685 #undef INSERT_BOOLEAN
8686 #undef INSERT_SCALAR_BOOLEAN
8687 #undef INSERT_STRING
8688 #undef INSERT_STRING_WITH_NS
8689 #undef EXTRACT_STRING_ATTRIBUTE
8690 #undef EXTRACT_ULONGINT
8691 #undef EXTRACT_LONGINT
8692 #undef EXTRACT_BOOLEAN
8693 #undef EXTRACT_STRING
8696 /* Compute hash of message from raw representation and store it into envelope.
8697 * Original hash structure will be destroyed in envelope.
8698 * @context is session context
8699 * @message is message carrying raw XML message blob
8700 * @algorithm is desired hash algorithm to use */
8701 isds_error isds_compute_message_hash(struct isds_ctx *context,
8702 struct isds_message *message, const isds_hash_algorithm algorithm) {
8703 isds_error err = IE_SUCCESS;
8704 const char *nsuri;
8705 void *xml_stream = NULL;
8706 size_t xml_stream_length;
8707 size_t phys_start, phys_end;
8708 char *phys_path = NULL;
8709 struct isds_hash *new_hash = NULL;
8712 if (!context) return IE_INVALID_CONTEXT;
8713 zfree(context->long_message);
8714 if (!message) return IE_INVAL;
8716 if (!message->raw) {
8717 isds_log_message(context,
8718 _("Message does not carry raw representation"));
8719 return IE_INVAL;
8722 switch (message->raw_type) {
8723 case RAWTYPE_INCOMING_MESSAGE:
8724 nsuri = ISDS_NS;
8725 xml_stream = message->raw;
8726 xml_stream_length = message->raw_length;
8727 break;
8729 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
8730 nsuri = SISDS_INCOMING_NS;
8731 xml_stream = message->raw;
8732 xml_stream_length = message->raw_length;
8733 break;
8735 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
8736 nsuri = SISDS_INCOMING_NS;
8737 err = _isds_extract_cms_data(context,
8738 message->raw, message->raw_length,
8739 &xml_stream, &xml_stream_length);
8740 if (err) goto leave;
8741 break;
8743 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
8744 nsuri = SISDS_OUTGOING_NS;
8745 xml_stream = message->raw;
8746 xml_stream_length = message->raw_length;
8747 break;
8749 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
8750 nsuri = SISDS_OUTGOING_NS;
8751 err = _isds_extract_cms_data(context,
8752 message->raw, message->raw_length,
8753 &xml_stream, &xml_stream_length);
8754 if (err) goto leave;
8755 break;
8757 default:
8758 isds_log_message(context, _("Bad raw representation type"));
8759 return IE_INVAL;
8760 break;
8764 /* XXX: Hash is computed from original string representing isds:dmDm
8765 * subtree. That means no encoding, white space, xmlns attributes changes.
8766 * In other words, input for hash can be invalid XML stream. */
8767 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
8768 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
8769 PHYSXML_ELEMENT_SEPARATOR,
8770 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
8771 PHYSXML_ELEMENT_SEPARATOR
8772 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
8773 err = IE_NOMEM;
8774 goto leave;
8776 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
8777 phys_path, &phys_start, &phys_end);
8778 zfree(phys_path);
8779 if (err) {
8780 isds_log_message(context,
8781 _("Substring with isds:dmDM element could not be located "
8782 "in raw message"));
8783 goto leave;
8787 /* Compute hash */
8788 new_hash = calloc(1, sizeof(*new_hash));
8789 if (!new_hash) {
8790 err = IE_NOMEM;
8791 goto leave;
8793 new_hash->algorithm = algorithm;
8794 err = _isds_compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
8795 new_hash);
8796 if (err) {
8797 isds_log_message(context, _("Could not compute message hash"));
8798 goto leave;
8801 /* Save computed hash */
8802 if (!message->envelope) {
8803 message->envelope = calloc(1, sizeof(*message->envelope));
8804 if (!message->envelope) {
8805 err = IE_NOMEM;
8806 goto leave;
8809 isds_hash_free(&message->envelope->hash);
8810 message->envelope->hash = new_hash;
8812 leave:
8813 if (err) {
8814 isds_hash_free(&new_hash);
8817 free(phys_path);
8818 if (xml_stream != message->raw) free(xml_stream);
8819 return err;
8823 /* Compare two hashes.
8824 * @h1 is first hash
8825 * @h2 is another hash
8826 * @return
8827 * IE_SUCCESS if hashes equal
8828 * IE_NOTUNIQ if hashes are comparable, but they don't equal
8829 * IE_ENUM if not comparable, but both structures defined
8830 * IE_INVAL if some of the structures are undefined (NULL)
8831 * IE_ERROR if internal error occurs */
8832 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
8833 if (h1 == NULL || h2 == NULL) return IE_INVAL;
8834 if (h1->algorithm != h2->algorithm) return IE_ENUM;
8835 if (h1->length != h2->length) return IE_ERROR;
8836 if (h1->length > 0 && !h1->value) return IE_ERROR;
8837 if (h2->length > 0 && !h2->value) return IE_ERROR;
8839 for (int i = 0; i < h1->length; i++) {
8840 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
8841 return IE_NOTEQUAL;
8843 return IE_SUCCESS;
8847 /* Check message has gone through ISDS by comparing message hash stored in
8848 * ISDS and locally computed hash. You must provide message with valid raw
8849 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
8850 * This is convenient wrapper for isds_download_message_hash(),
8851 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
8852 * @context is session context
8853 * @message is message with valid raw and envelope member; envelope->hash
8854 * member will be changed during function run. Use envelope on heap only.
8855 * @return
8856 * IE_SUCCESS if message originates in ISDS
8857 * IE_NOTEQUAL if message is unknown to ISDS
8858 * other code for other errors */
8859 isds_error isds_verify_message_hash(struct isds_ctx *context,
8860 struct isds_message *message) {
8861 isds_error err = IE_SUCCESS;
8862 struct isds_hash *downloaded_hash = NULL;
8864 if (!context) return IE_INVALID_CONTEXT;
8865 zfree(context->long_message);
8866 if (!message) return IE_INVAL;
8868 if (!message->envelope) {
8869 isds_log_message(context,
8870 _("Given message structure is missing envelope"));
8871 return IE_INVAL;
8873 if (!message->raw) {
8874 isds_log_message(context,
8875 _("Given message structure is missing raw representation"));
8876 return IE_INVAL;
8879 err = isds_download_message_hash(context, message->envelope->dmID,
8880 &downloaded_hash);
8881 if (err) goto leave;
8883 err = isds_compute_message_hash(context, message,
8884 downloaded_hash->algorithm);
8885 if (err) goto leave;
8887 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
8889 leave:
8890 isds_hash_free(&downloaded_hash);
8891 return err;
8895 /* Search for document by document ID in list of documents. IDs are compared
8896 * as UTF-8 string.
8897 * @documents is list of isds_documents
8898 * @id is document identifier
8899 * @return first matching document or NULL. */
8900 const struct isds_document *isds_find_document_by_id(
8901 const struct isds_list *documents, const char *id) {
8902 const struct isds_list *item;
8903 const struct isds_document *document;
8905 for (item = documents; item; item = item->next) {
8906 document = (struct isds_document *) item->data;
8907 if (!document) continue;
8909 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
8910 return document;
8913 return NULL;
8917 /* Normalize @mime_type to be proper MIME type.
8918 * ISDS servers passes invalid MIME types (e.g. "pdf"). This function tries to
8919 * guess regular MIME type (e.g. "application/pdf").
8920 * @mime_type is UTF-8 encoded MIME type to fix
8921 * @return original @mime_type if no better interpretation exists, or array to
8922 * constant static UTF-8 encoded string with proper MIME type. */
8923 char *isds_normalize_mime_type(const char* mime_type) {
8924 if (!mime_type) return NULL;
8926 for (int offset = 0;
8927 offset < sizeof(extension_map_mime)/sizeof(extension_map_mime[0]);
8928 offset += 2) {
8929 if (!xmlStrcmp((const xmlChar*) mime_type, extension_map_mime[offset]))
8930 return (char *) extension_map_mime[offset + 1];
8933 return (char *) mime_type;
8937 /* Switch MIME type normalization while message loading. Default state for new
8938 * context is no normalization.
8939 * @normalize use true to switch normalization on, false to switch off */
8940 isds_error isds_set_mime_type_normalization(struct isds_ctx *context,
8941 _Bool normalize) {
8942 if (!context) return IE_INVALID_CONTEXT;
8943 zfree(context->long_message);
8945 context->normalize_mime_type = normalize;
8946 isds_log(ILF_FILE, ILL_INFO, (context->normalize_mime_type) ?
8947 _("MIME type normalization switched on\n") :
8948 _("MIME type normalization switched off\n"));
8949 return IE_SUCCESS;
8953 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
8954 struct isds_message **message);
8955 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
8956 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
8957 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
8958 struct isds_address **address);
8960 int isds_message_free(struct isds_message **message);
8961 int isds_address_free(struct isds_address **address);
8965 /* Makes known all relevant namespaces to given XPath context
8966 * @xpath_ctx is XPath context
8967 * @message_ns selects proper message name space. Unsigned and signed
8968 * messages and delivery info's differ in prefix and URI. */
8969 _hidden isds_error _isds_register_namespaces(xmlXPathContextPtr xpath_ctx,
8970 const message_ns_type message_ns) {
8971 const xmlChar *message_namespace = NULL;
8973 if (!xpath_ctx) return IE_ERROR;
8975 switch(message_ns) {
8976 case MESSAGE_NS_1:
8977 message_namespace = BAD_CAST ISDS1_NS; break;
8978 case MESSAGE_NS_UNSIGNED:
8979 message_namespace = BAD_CAST ISDS_NS; break;
8980 case MESSAGE_NS_SIGNED_INCOMING:
8981 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
8982 case MESSAGE_NS_SIGNED_OUTGOING:
8983 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
8984 case MESSAGE_NS_SIGNED_DELIVERY:
8985 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
8986 default:
8987 return IE_ENUM;
8990 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
8991 return IE_ERROR;
8992 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
8993 return IE_ERROR;
8994 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
8995 return IE_ERROR;
8996 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
8997 return IE_ERROR;
8998 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
8999 return IE_ERROR;
9000 return IE_SUCCESS;