Save XML into message by extract_TReturnedMessage()
[libisds.git] / src / isds.c
blobfec63b5d56fcb130307f26a1b748d75e365296a9
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://www.mojedatovaschranka.cz/";
20 /* Base URL of production ISDS instance */
21 const char isds_testing_locator[] = "https://www.czebox.cz/";
23 /* Extension to MIME type map */
24 static xmlChar *extension_map_mime[] = {
25 BAD_CAST "pdf", BAD_CAST "application/pdf",
26 BAD_CAST "xml", BAD_CAST "application/xml",
27 BAD_CAST "fo", BAD_CAST "application/vnd.software602.filler.xml+form",
28 BAD_CAST "zfo", BAD_CAST "application/vnd.software602.filler.xml+zip+form",
29 BAD_CAST "html", BAD_CAST "text/html",
30 BAD_CAST "htm", BAD_CAST "text/html",
31 BAD_CAST "odt", BAD_CAST "application/vnd.oasis.opendocument.text",
32 BAD_CAST "ods", BAD_CAST "application/vnd.oasis.opendocument.spreadsheet",
33 BAD_CAST "odp", BAD_CAST "application/vnd.oasis.opendocument.presentation",
34 BAD_CAST "txt", BAD_CAST "text/plain",
35 BAD_CAST "rtf", BAD_CAST "application/rtf",
36 BAD_CAST "doc", BAD_CAST "application/msword",
37 BAD_CAST "xls", BAD_CAST "application/vnd.ms-excel",
38 BAD_CAST "ppt", BAD_CAST "application/vnd.ms-powerpoint",
39 BAD_CAST "jpg", BAD_CAST "image/jpeg",
40 BAD_CAST "jpeg", BAD_CAST "image/jpeg",
41 BAD_CAST "jfif", BAD_CAST "image/jpeg",
42 BAD_CAST "png", BAD_CAST "image/png",
43 BAD_CAST "tiff", BAD_CAST "image/tiff",
44 BAD_CAST "gif", BAD_CAST "image/gif",
45 BAD_CAST "mpeg1", BAD_CAST "video/mpeg",
46 BAD_CAST "mpeg2", BAD_CAST "video/mpeg2",
47 BAD_CAST "wav", BAD_CAST "audio/x-wav",
48 BAD_CAST "mp2", BAD_CAST "audio/mpeg",
49 BAD_CAST "mp3", BAD_CAST "audio/mpeg",
50 /* TODO: Add MIME types for ISDOC, X.509 certificates, CMS and TST */
53 /* Deallocate structure isds_pki_credentials and NULL it.
54 * Passphrase is discarded.
55 * @pki credentials to to free */
56 void isds_pki_credentials_free(struct isds_pki_credentials **pki) {
57 if(!pki || !*pki) return;
59 free((*pki)->engine);
60 free((*pki)->certificate);
61 free((*pki)->key);
63 if ((*pki)->passphrase) {
64 memset((*pki)->passphrase, 0, strlen((*pki)->passphrase));
65 free((*pki)->passphrase);
68 zfree((*pki));
72 /* Free isds_list with all member data.
73 * @list list to free, on return will be NULL */
74 void isds_list_free(struct isds_list **list) {
75 struct isds_list *item, *next_item;
77 if (!list || !*list) return;
79 for(item = *list; item; item = next_item) {
80 (item->destructor)(&(item->data));
81 next_item = item->next;
82 free(item);
85 *list = NULL;
89 /* Deallocate structure isds_hash and NULL it.
90 * @hash hash to to free */
91 void isds_hash_free(struct isds_hash **hash) {
92 if(!hash || !*hash) return;
93 free((*hash)->value);
94 zfree((*hash));
98 /* Deallocate structure isds_PersonName recursively and NULL it */
99 static void isds_PersonName_free(struct isds_PersonName **person_name) {
100 if (!person_name || !*person_name) return;
102 free((*person_name)->pnFirstName);
103 free((*person_name)->pnMiddleName);
104 free((*person_name)->pnLastName);
105 free((*person_name)->pnLastNameAtBirth);
107 free(*person_name);
108 *person_name = NULL;
112 /* Deallocate structure isds_BirthInfo recursively and NULL it */
113 static void isds_BirthInfo_free(struct isds_BirthInfo **birth_info) {
114 if (!birth_info || !*birth_info) return;
116 free((*birth_info)->biDate);
117 free((*birth_info)->biCity);
118 free((*birth_info)->biCounty);
119 free((*birth_info)->biState);
121 free(*birth_info);
122 *birth_info = NULL;
126 /* Deallocate structure isds_Address recursively and NULL it */
127 static void isds_Address_free(struct isds_Address **address) {
128 if (!address || !*address) return;
130 free((*address)->adCity);
131 free((*address)->adStreet);
132 free((*address)->adNumberInStreet);
133 free((*address)->adNumberInMunicipality);
134 free((*address)->adZipCode);
135 free((*address)->adState);
137 free(*address);
138 *address = NULL;
142 /* Deallocate structure isds_DbOwnerInfo recursively and NULL it */
143 void isds_DbOwnerInfo_free(struct isds_DbOwnerInfo **db_owner_info) {
144 if (!db_owner_info || !*db_owner_info) return;
146 free((*db_owner_info)->dbID);
147 free((*db_owner_info)->dbType);
148 free((*db_owner_info)->ic);
149 isds_PersonName_free(&((*db_owner_info)->personName));
150 free((*db_owner_info)->firmName);
151 isds_BirthInfo_free(&((*db_owner_info)->birthInfo));
152 isds_Address_free(&((*db_owner_info)->address));
153 free((*db_owner_info)->nationality);
154 free((*db_owner_info)->email);
155 free((*db_owner_info)->telNumber);
156 free((*db_owner_info)->identifier);
157 free((*db_owner_info)->registryCode);
158 free((*db_owner_info)->dbState);
159 free((*db_owner_info)->dbEffectiveOVM);
161 free(*db_owner_info);
162 *db_owner_info = NULL;
165 /* Deallocate structure isds_DbUserInfo recursively and NULL it */
166 void isds_DbUserInfo_free(struct isds_DbUserInfo **db_user_info) {
167 if (!db_user_info || !*db_user_info) return;
169 free((*db_user_info)->userID);
170 free((*db_user_info)->userType);
171 free((*db_user_info)->userPrivils);
172 isds_PersonName_free(&((*db_user_info)->personName));
173 isds_Address_free(&((*db_user_info)->address));
174 free((*db_user_info)->biDate);
175 free((*db_user_info)->ic);
176 free((*db_user_info)->firmName);
177 free((*db_user_info)->caStreet);
178 free((*db_user_info)->caCity);
179 free((*db_user_info)->caZipCode);
180 free((*db_user_info)->caState);
182 zfree(*db_user_info);
186 /* Deallocate struct isds_event recursively and NULL it */
187 void isds_event_free(struct isds_event **event) {
188 if (!event || !*event) return;
190 free((*event)->time);
191 free((*event)->type);
192 free((*event)->description);
193 zfree(*event);
197 /* Deallocate struct isds_envelope recursively and NULL it */
198 void isds_envelope_free(struct isds_envelope **envelope) {
199 if (!envelope || !*envelope) return;
201 free((*envelope)->dmID);
202 free((*envelope)->dbIDSender);
203 free((*envelope)->dmSender);
204 free((*envelope)->dmSenderAddress);
205 free((*envelope)->dmSenderType);
206 free((*envelope)->dmRecipient);
207 free((*envelope)->dmRecipientAddress);
208 free((*envelope)->dmAmbiguousRecipient);
209 free((*envelope)->dmType);
211 free((*envelope)->dmOrdinal);
212 free((*envelope)->dmMessageStatus);
213 free((*envelope)->dmDeliveryTime);
214 free((*envelope)->dmAcceptanceTime);
215 isds_hash_free(&(*envelope)->hash);
216 free((*envelope)->timestamp);
217 isds_list_free(&(*envelope)->events);
219 free((*envelope)->dmSenderOrgUnit);
220 free((*envelope)->dmSenderOrgUnitNum);
221 free((*envelope)->dbIDRecipient);
222 free((*envelope)->dmRecipientOrgUnit);
223 free((*envelope)->dmRecipientOrgUnitNum);
224 free((*envelope)->dmToHands);
225 free((*envelope)->dmAnnotation);
226 free((*envelope)->dmRecipientRefNumber);
227 free((*envelope)->dmSenderRefNumber);
228 free((*envelope)->dmRecipientIdent);
229 free((*envelope)->dmSenderIdent);
231 free((*envelope)->dmLegalTitleLaw);
232 free((*envelope)->dmLegalTitleYear);
233 free((*envelope)->dmLegalTitleSect);
234 free((*envelope)->dmLegalTitlePar);
235 free((*envelope)->dmLegalTitlePoint);
237 free((*envelope)->dmPersonalDelivery);
238 free((*envelope)->dmAllowSubstDelivery);
239 free((*envelope)->dmOVM);
241 free(*envelope);
242 *envelope = NULL;
246 /* Deallocate struct isds_message recursively and NULL it */
247 void isds_message_free(struct isds_message **message) {
248 if (!message || !*message) return;
250 free((*message)->raw);
251 isds_envelope_free(&((*message)->envelope));
252 isds_list_free(&((*message)->documents));
253 xmlFreeDoc((*message)->xml); (*message)->xml = NULL;
255 free(*message);
256 *message = NULL;
260 /* Deallocate struct isds_document recursively and NULL it */
261 void isds_document_free(struct isds_document **document) {
262 if (!document || !*document) return;
264 free((*document)->data);
265 free((*document)->dmMimeType);
266 free((*document)->dmFileGuid);
267 free((*document)->dmUpFileGuid);
268 free((*document)->dmFileDescr);
269 free((*document)->dmFormat);
271 free(*document);
272 *document = NULL;
276 /* Deallocate struct isds_message_copy recursively and NULL it */
277 void isds_message_copy_free(struct isds_message_copy **copy) {
278 if (!copy || !*copy) return;
280 free((*copy)->dbIDRecipient);
281 free((*copy)->dmRecipientOrgUnit);
282 free((*copy)->dmRecipientOrgUnitNum);
283 free((*copy)->dmToHands);
285 free((*copy)->dmStatus);
286 free((*copy)->dmID);
288 zfree(*copy);
292 /* Deallocate struct isds_approval recursively and NULL it */
293 void isds_approval_free(struct isds_approval **approval) {
294 if (!approval || !*approval) return;
296 free((*approval)->refference);
298 zfree(*approval);
302 /* *DUP_OR_ERROR macros needs error label */
303 #define STRDUP_OR_ERROR(new, template) { \
304 if (!template) { \
305 (new) = NULL; \
306 } else { \
307 (new) = strdup(template); \
308 if (!new) goto error; \
312 #define FLATDUP_OR_ERROR(new, template) { \
313 if (!template) { \
314 (new) = NULL; \
315 } else { \
316 (new) = malloc(sizeof(*(new))); \
317 if (!new) goto error; \
318 memcpy((new), (template), sizeof(*(template))); \
322 /* Copy structure isds_pki_credentials recursively. */
323 struct isds_pki_credentials *isds_pki_credentials_duplicate(
324 const struct isds_pki_credentials *template) {
325 struct isds_pki_credentials *new = NULL;
327 if(!template) return NULL;
329 new = calloc(1, sizeof(*new));
330 if (!new) return NULL;
332 STRDUP_OR_ERROR(new->engine, template->engine);
333 new->certificate_format = template->certificate_format;
334 STRDUP_OR_ERROR(new->certificate, template->certificate);
335 new->key_format = template->key_format;
336 STRDUP_OR_ERROR(new->key, template->key);
337 STRDUP_OR_ERROR(new->passphrase, template->passphrase);
339 return new;
341 error:
342 isds_pki_credentials_free(&new);
343 return NULL;
347 /* Copy structure isds_PersonName recursively */
348 struct isds_PersonName *isds_PersonName_duplicate(
349 const struct isds_PersonName *template) {
350 struct isds_PersonName *new = NULL;
352 if (!template) return NULL;
354 new = calloc(1, sizeof(*new));
355 if (!new) return NULL;
357 STRDUP_OR_ERROR(new->pnFirstName, template->pnFirstName);
358 STRDUP_OR_ERROR(new->pnMiddleName, template->pnMiddleName);
359 STRDUP_OR_ERROR(new->pnLastName, template->pnLastName);
360 STRDUP_OR_ERROR(new->pnLastNameAtBirth, template->pnLastNameAtBirth);
362 return new;
364 error:
365 isds_PersonName_free(&new);
366 return NULL;
370 /* Copy structure isds_BirthInfo recursively */
371 static struct isds_BirthInfo *isds_BirthInfo_duplicate(
372 const struct isds_BirthInfo *template) {
373 struct isds_BirthInfo *new = NULL;
375 if (!template) return NULL;
377 new = calloc(1, sizeof(*new));
378 if (!new) return NULL;
380 FLATDUP_OR_ERROR(new->biDate, template->biDate);
381 STRDUP_OR_ERROR(new->biCity, template->biCity);
382 STRDUP_OR_ERROR(new->biCounty, template->biCounty);
383 STRDUP_OR_ERROR(new->biState, template->biState);
385 return new;
387 error:
388 isds_BirthInfo_free(&new);
389 return NULL;
393 /* Copy structure isds_Address recursively */
394 struct isds_Address *isds_Address_duplicate(
395 const struct isds_Address *template) {
396 struct isds_Address *new = NULL;
398 if (!template) return NULL;
400 new = calloc(1, sizeof(*new));
401 if (!new) return NULL;
403 STRDUP_OR_ERROR(new->adCity, template->adCity);
404 STRDUP_OR_ERROR(new->adStreet, template->adStreet);
405 STRDUP_OR_ERROR(new->adNumberInStreet, template->adNumberInStreet);
406 STRDUP_OR_ERROR(new->adNumberInMunicipality,
407 template->adNumberInMunicipality);
408 STRDUP_OR_ERROR(new->adZipCode, template->adZipCode);
409 STRDUP_OR_ERROR(new->adState, template->adState);
411 return new;
413 error:
414 isds_Address_free(&new);
415 return NULL;
419 /* Copy structure isds_DbOwnerInfo recursively */
420 struct isds_DbOwnerInfo *isds_DbOwnerInfo_duplicate(
421 const struct isds_DbOwnerInfo *template) {
422 struct isds_DbOwnerInfo *new = NULL;
423 if (!template) return NULL;
425 new = calloc(1, sizeof(*new));
426 if (!new) return NULL;
428 STRDUP_OR_ERROR(new->dbID, template->dbID);
429 FLATDUP_OR_ERROR(new->dbType, template->dbType);
430 STRDUP_OR_ERROR(new->ic, template->ic);
432 if (template->personName) {
433 if (!(new->personName =
434 isds_PersonName_duplicate(template->personName)))
435 goto error;
438 STRDUP_OR_ERROR(new->firmName, template->firmName);
440 if (template->birthInfo) {
441 if (!(new->birthInfo =
442 isds_BirthInfo_duplicate(template->birthInfo)))
443 goto error;
446 if (template->address) {
447 if (!(new->address = isds_Address_duplicate(template->address)))
448 goto error;
451 STRDUP_OR_ERROR(new->nationality, template->nationality);
452 STRDUP_OR_ERROR(new->email, template->email);
453 STRDUP_OR_ERROR(new->telNumber, template->telNumber);
454 STRDUP_OR_ERROR(new->identifier, template->identifier);
455 STRDUP_OR_ERROR(new->registryCode, template->registryCode);
456 FLATDUP_OR_ERROR(new->dbState, template->dbState);
457 FLATDUP_OR_ERROR(new->dbEffectiveOVM, template->dbEffectiveOVM);
458 FLATDUP_OR_ERROR(new->dbOpenAddressing, template->dbOpenAddressing);
460 return new;
462 error:
463 isds_DbOwnerInfo_free(&new);
464 return NULL;
468 /* Copy structure isds_DbUserInfo recursively */
469 struct isds_DbUserInfo *isds_DbUserInfo_duplicate(
470 const struct isds_DbUserInfo *template) {
471 struct isds_DbUserInfo *new = NULL;
472 if (!template) return NULL;
474 new = calloc(1, sizeof(*new));
475 if (!new) return NULL;
477 STRDUP_OR_ERROR(new->userID, template->userID);
478 FLATDUP_OR_ERROR(new->userType, template->userType);
479 FLATDUP_OR_ERROR(new->userPrivils, template->userPrivils);
481 if (template->personName) {
482 if (!(new->personName =
483 isds_PersonName_duplicate(template->personName)))
484 goto error;
487 if (template->address) {
488 if (!(new->address = isds_Address_duplicate(template->address)))
489 goto error;
492 FLATDUP_OR_ERROR(new->biDate, template->biDate);
493 STRDUP_OR_ERROR(new->ic, template->ic);
494 STRDUP_OR_ERROR(new->firmName, template->firmName);
495 STRDUP_OR_ERROR(new->caStreet, template->caStreet);
496 STRDUP_OR_ERROR(new->caCity, template->caCity);
497 STRDUP_OR_ERROR(new->caZipCode, template->caZipCode);
498 STRDUP_OR_ERROR(new->caState, template->caState);
500 return new;
502 error:
503 isds_DbUserInfo_free(&new);
504 return NULL;
507 #undef FLATDUP_OR_ERROR
508 #undef STRDUP_OR_ERROR
511 /* Initialize ISDS library.
512 * Global function, must be called before other functions.
513 * If it failes you can not use ISDS library and must call isds_cleanup() to
514 * free partially inititialized global variables. */
515 isds_error isds_init(void) {
516 /* NULL global variables */
517 log_facilities = ILF_ALL;
518 log_level = ILL_WARNING;
519 log_callback = NULL;
520 log_callback_data = NULL;
522 #if ENABLE_NLS
523 /* Initialize gettext */
524 bindtextdomain(PACKAGE, LOCALEDIR);
525 #endif
527 /* Initialize CURL */
528 if (curl_global_init(CURL_GLOBAL_ALL)) {
529 isds_log(ILF_ISDS, ILL_CRIT, _("CURL library initialization failed\n"));
530 return IE_ERROR;
533 /* Inicialize gpg-error because of gpgme and ksba */
534 if (gpg_err_init()) {
535 isds_log(ILF_ISDS, ILL_CRIT,
536 _("gpg-error library initialization failed\n"));
537 return IE_ERROR;
540 /* Initialize GPGME */
541 if (_isds_init_gpgme(&version_gpgme)) {
542 isds_log(ILF_ISDS, ILL_CRIT,
543 _("GPGME library initialization failed\n"));
544 return IE_ERROR;
547 /* Initialize gcrypt */
548 if (_isds_init_gcrypt(&version_gcrypt)) {
549 isds_log(ILF_ISDS, ILL_CRIT,
550 _("gcrypt library initialization failed\n"));
551 return IE_ERROR;
554 /* This can _exit() current program. Find not so assertive check. */
555 LIBXML_TEST_VERSION;
557 /* Check expat */
558 if (_isds_init_expat(&version_expat)) {
559 isds_log(ILF_ISDS, ILL_CRIT,
560 _("expat library initialization failed\n"));
561 return IE_ERROR;
564 /* Allocate global variables */
567 return IE_SUCCESS;
571 /* Deinicialize ISDS library.
572 * Global function, must be called as last library function. */
573 isds_error isds_cleanup(void) {
574 /* XML */
575 xmlCleanupParser();
577 /* Curl */
578 curl_global_cleanup();
580 return IE_SUCCESS;
584 /* Return version string of this library. Version of dependecies can be
585 * embedded. Do no try to parse it. You must free it. */
586 char *isds_version(void) {
587 char *buffer = NULL;
589 isds_asprintf(&buffer, _("%s (%s, GPGME %s, gcrypt %s, %s, libxml2 %s)"),
590 PACKAGE_VERSION, curl_version(), version_gpgme, version_gcrypt,
591 version_expat, xmlParserVersion);
592 return buffer;
596 /* Return text description of ISDS error */
597 const char *isds_strerror(const isds_error error) {
598 switch (error) {
599 case IE_SUCCESS:
600 return(_("Success")); break;
601 case IE_ERROR:
602 return(_("Unspecified error")); break;
603 case IE_NOTSUP:
604 return(_("Not supported")); break;
605 case IE_INVAL:
606 return(_("Invalid value")); break;
607 case IE_INVALID_CONTEXT:
608 return(_("Invalid context")); break;
609 case IE_NOT_LOGGED_IN:
610 return(_("Not logged in")); break;
611 case IE_CONNECTION_CLOSED:
612 return(_("Connection closed")); break;
613 case IE_TIMED_OUT:
614 return(_("Timed out")); break;
615 case IE_NOEXIST:
616 return(_("Not exist")); break;
617 case IE_NOMEM:
618 return(_("Out of memory")); break;
619 case IE_NETWORK:
620 return(_("Network problem")); break;
621 case IE_HTTP:
622 return(_("HTTP problem")); break;
623 case IE_SOAP:
624 return(_("SOAP problem")); break;
625 case IE_XML:
626 return(_("XML problem")); break;
627 case IE_ISDS:
628 return(_("ISDS server problem")); break;
629 case IE_ENUM:
630 return(_("Invalid enum value")); break;
631 case IE_DATE:
632 return(_("Invalid date value")); break;
633 case IE_2BIG:
634 return(_("Too big")); break;
635 case IE_2SMALL:
636 return(_("Too small")); break;
637 case IE_NOTUNIQ:
638 return(_("Value not unique")); break;
639 case IE_NOTEQUAL:
640 return(_("Values not uqual")); break;
641 case IE_PARTIAL_SUCCESS:
642 return(_("Some suboperations failed")); break;
643 case IE_ABORTED:
644 return(_("Operation aborted")); break;
645 default:
646 return(_("Unknown error"));
651 /* Create ISDS context.
652 * Each context can be used for different sessions to (possibly) differnet
653 * ISDS server with different credentials. */
654 struct isds_ctx *isds_ctx_create(void) {
655 struct isds_ctx *context;
656 context = malloc(sizeof(*context));
657 if (context) memset(context, 0, sizeof(*context));
658 return context;
662 /* Close possibly opened connection to Czech POINT document deposit without
663 * reseting long_message buffer.
664 * XXX: Do not use czp_close_connection() if you do not want to destroy log
665 * message.
666 * @context is Czech POINT session context. */
667 static isds_error czp_do_close_connection(struct isds_ctx *context) {
668 if (!context) return IE_INVALID_CONTEXT;
669 _isds_close_connection(context);
670 return IE_SUCCESS;
674 /* Discard credentials.
675 * Only that. It does not cause log out, connection close or similar. */
676 static isds_error discard_credentials(struct isds_ctx *context) {
677 if(!context) return IE_INVALID_CONTEXT;
679 if (context->username) {
680 memset(context->username, 0, strlen(context->username));
681 zfree(context->username);
683 if (context->password) {
684 memset(context->password, 0, strlen(context->password));
685 zfree(context->password);
687 isds_pki_credentials_free(&context->pki_credentials);
689 return IE_SUCCESS;
693 /* Destroy ISDS context and free memmory.
694 * @context will be NULLed on success. */
695 isds_error isds_ctx_free(struct isds_ctx **context) {
696 if (!context || !*context) {
697 return IE_INVALID_CONTEXT;
700 /* Discard credentials and close connection */
701 switch ((*context)->type) {
702 case CTX_TYPE_NONE: break;
703 case CTX_TYPE_ISDS: isds_logout(*context); break;
704 case CTX_TYPE_CZP:
705 case CTX_TYPE_TESTING_REQUEST_COLLECTOR:
706 czp_do_close_connection(*context); break;
709 /* For sure */
710 discard_credentials(*context);
712 /* Free other structures */
713 free((*context)->tls_verify_server);
714 free((*context)->tls_ca_file);
715 free((*context)->tls_ca_dir);
716 free((*context)->tls_crl_file);
717 free((*context)->long_message);
719 free(*context);
720 *context = NULL;
721 return IE_SUCCESS;
725 /* Return long message text produced by library fucntion, e.g. detailed error
726 * mesage. Returned pointer is only valid until new library function is
727 * called for the same context. Could be NULL, especially if NULL context is
728 * supplied. Return string is locale encoded. */
729 char *isds_long_message(const struct isds_ctx *context) {
730 if (!context) return NULL;
731 return context->long_message;
735 /* Stores message into context' long_message buffer.
736 * Application can pick the message up using isds_long_message().
737 * NULL @message truncates the buffer but does not deallocate it.
738 * @message is coded in locale encoding */
739 _hidden isds_error isds_log_message(struct isds_ctx *context,
740 const char *message) {
741 char *buffer;
742 size_t length;
744 if (!context) return IE_INVALID_CONTEXT;
746 /* FIXME: Check for integer overflow */
747 length = 1 + ((message) ? strlen(message) : 0);
748 buffer = realloc(context->long_message, length);
749 if (!buffer) return IE_NOMEM;
751 if (message)
752 strcpy(buffer, message);
753 else
754 *buffer = '\0';
756 context->long_message = buffer;
757 return IE_SUCCESS;
761 /* Appends message into context' long_message buffer.
762 * Application can pick the message up using isds_long_message().
763 * NULL message has void effect. */
764 _hidden isds_error isds_append_message(struct isds_ctx *context,
765 const char *message) {
766 char *buffer;
767 size_t old_length, length;
769 if (!context) return IE_INVALID_CONTEXT;
770 if (!message) return IE_SUCCESS;
771 if (!context->long_message)
772 return isds_log_message(context, message);
774 old_length = strlen(context->long_message);
775 /* FIXME: Check for integer overflow */
776 length = 1 + old_length + strlen(message);
777 buffer = realloc(context->long_message, length);
778 if (!buffer) return IE_NOMEM;
780 strcpy(buffer + old_length, message);
782 context->long_message = buffer;
783 return IE_SUCCESS;
787 /* Stores formated message into context' long_message buffer.
788 * Application can pick the message up using isds_long_message(). */
789 _hidden isds_error isds_printf_message(struct isds_ctx *context,
790 const char *format, ...) {
791 va_list ap;
792 int length;
794 if (!context) return IE_INVALID_CONTEXT;
795 va_start(ap, format);
796 length = isds_vasprintf(&(context->long_message), format, ap);
797 va_end(ap);
799 return (length < 0) ? IE_ERROR: IE_SUCCESS;
803 /* Set logging up.
804 * @facilities is bitmask of isds_log_facility values,
805 * @level is verbosity level. */
806 void isds_set_logging(const unsigned int facilities,
807 const isds_log_level level) {
808 log_facilities = facilities;
809 log_level = level;
813 /* Register callback function libisds calls when new global log message is
814 * produced by library. Library logs to stderr by default.
815 * @callback is function provided by application libsds will call. See type
816 * defition for @callback argument explanation. Pass NULL to revert logging to
817 * default behaviour.
818 * @data is application specific data @callback gets as last argument */
819 void isds_set_log_callback(isds_log_callback callback, void *data) {
820 log_callback = callback;
821 log_callback_data = data;
825 /* Log @message in class @facility with log @level into global log. @message
826 * is printf(3) formating string, variadic arguments may be neccessary.
827 * For debugging purposes. */
828 _hidden isds_error isds_log(const isds_log_facility facility,
829 const isds_log_level level, const char *message, ...) {
830 va_list ap;
831 char *buffer = NULL;
832 int length;
834 if (level > log_level) return IE_SUCCESS;
835 if (!(log_facilities & facility)) return IE_SUCCESS;
836 if (!message) return IE_INVAL;
838 if (log_callback) {
839 /* Pass message to application supplied callback function */
840 va_start(ap, message);
841 length = isds_vasprintf(&buffer, message, ap);
842 va_end(ap);
844 if (length == -1) {
845 return IE_ERROR;
847 if (length > 0) {
848 log_callback(facility, level, buffer, length, log_callback_data);
850 free(buffer);
851 } else {
852 /* Default: Log it to stderr */
853 va_start(ap, message);
854 vfprintf(stderr, message, ap);
855 va_end(ap);
856 /* Line buffered printf is default.
857 * fflush(stderr);*/
860 return IE_SUCCESS;
864 /* Set timeout in miliseconds for each network job like connecting to server
865 * or sending message. Use 0 to disable timeout limits. */
866 isds_error isds_set_timeout(struct isds_ctx *context,
867 const unsigned int timeout) {
868 if (!context) return IE_INVALID_CONTEXT;
869 zfree(context->long_message);
871 context->timeout = timeout;
873 if (context->curl) {
874 CURLcode curl_err;
876 curl_err = curl_easy_setopt(context->curl, CURLOPT_NOSIGNAL, 1);
877 if (!curl_err)
878 #if HAVE_DECL_CURLOPT_TIMEOUT_MS /* Since curl-7.16.2 */
879 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT_MS,
880 context->timeout);
881 #else
882 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT,
883 context->timeout / 1000);
884 #endif /* not HAVE_DECL_CURLOPT_TIMEOUT_MS */
885 if (curl_err) return IE_ERROR;
888 return IE_SUCCESS;
892 /* Register callback function libisds calls periodocally during HTTP data
893 * transfer.
894 * @context is session context
895 * @callback is function provided by application libsds will call. See type
896 * defition for @callback argument explanation.
897 * @data is application specific data @callback gets as last argument */
898 isds_error isds_set_progress_callback(struct isds_ctx *context,
899 isds_progress_callback callback, void *data) {
900 if (!context) return IE_INVALID_CONTEXT;
901 zfree(context->long_message);
903 context->progress_callback = callback;
904 context->progress_callback_data = data;
906 return IE_SUCCESS;
910 /* Change context settings.
911 * @context is context which setting vill be applied to
912 * @option is name of option. It determines the type of last argument. See
913 * isds_option definition for more info.
914 * @... is value of new setting. Type is determined by @option
915 * */
916 isds_error isds_set_opt(struct isds_ctx *context, const isds_option option,
917 ...) {
918 isds_error err = IE_SUCCESS;
919 va_list ap;
920 char *pointer, *string;
922 if (!context) return IE_INVALID_CONTEXT;
923 zfree(context->long_message);
925 va_start(ap, option);
927 #define REPLACE_VA_BOOLEAN(destination) { \
928 if (!(destination)) { \
929 (destination) = malloc(sizeof(*(destination))); \
930 if (!(destination)) { \
931 err = IE_NOMEM; goto leave; \
934 *(destination) = (_Bool) !!va_arg(ap, int); \
937 #define REPLACE_VA_STRING(destination) { \
938 string = va_arg(ap, char *); \
939 if (string) { \
940 pointer = realloc((destination), 1 + strlen(string)); \
941 if (!pointer) { err = IE_NOMEM; goto leave; } \
942 strcpy(pointer, string); \
943 (destination) = pointer; \
944 } else { \
945 free(destination); \
946 (destination) = NULL; \
950 switch (option) {
951 case IOPT_TLS_VERIFY_SERVER:
952 REPLACE_VA_BOOLEAN(context->tls_verify_server);
953 break;
954 case IOPT_TLS_CA_FILE:
955 REPLACE_VA_STRING(context->tls_ca_file);
956 break;
957 case IOPT_TLS_CA_DIRECTORY:
958 REPLACE_VA_STRING(context->tls_ca_dir);
959 break;
960 case IOPT_TLS_CRL_FILE:
961 #if HAVE_DECL_CURLOPT_CRLFILE /* Since curl-7.19.0 */
962 REPLACE_VA_STRING(context->tls_crl_file);
963 #else
964 isds_log_message(context,
965 _("Curl library does not support CRL definition"));
966 err = IE_NOTSUP;
967 #endif /* not HAVE_DECL_CURLOPT_CRLFILE */
968 break;
969 case IOPT_NORMALIZE_MIME_TYPE:
970 context->normalize_mime_type = (_Bool) !!va_arg(ap, int);
972 default:
973 err = IE_ENUM; goto leave;
976 #undef REPLACE_VA_STRING
977 #undef REPLACE_VA_BOOLEAN
979 leave:
980 va_end(ap);
981 return err;
985 /* Deprecated: Use isds_set_opt() instead.
986 * Change SSL/TLS settings.
987 * @context is context which setting will be applied to
988 * @option is name of option. It determines the type of last argument. See
989 * isds_tls_option definition for more info.
990 * @... is value of new setting. Type is determined by @option
991 * */
992 isds_error isds_set_tls(struct isds_ctx *context, const isds_tls_option option,
993 ...) {
994 isds_error err = IE_ENUM;
995 va_list ap;
997 va_start(ap, option);
999 switch (option) {
1000 case ITLS_VERIFY_SERVER:
1001 err = isds_set_opt(context, option, va_arg(ap, int));
1002 break;
1003 case ITLS_CA_FILE:
1004 case ITLS_CA_DIRECTORY:
1005 case ITLS_CRL_FILE:
1006 err = isds_set_opt(context, option, va_arg(ap, char*));
1009 va_end(ap);
1010 return err;
1014 /* Connect and log in into ISDS server.
1015 * All required arguments will be copied, you do not have to keep them after
1016 * that.
1017 * ISDS supports four different authentication methods. Exact method is
1018 * selected on @username, @passwors and @pki_credentials arguments:
1019 * - If @pki_credentials == NULL, @username and @password must be supplied
1020 * - If @pki_credentials != NULL, then
1021 * - If @username == NULL, only certificate will be used
1022 * - If @username != NULL, then
1023 * - If @password == NULL, then certificate will be used and
1024 * @username shifts meaning to box ID. This is used for hosted
1025 * services.
1026 * - Otherwise all three arguments will be used.
1027 * Please note, that differen cases requires different certificate type
1028 * (system qualified one or commercial non qualified one). This library does
1029 * not check such political issues. Please see ISDS Specification for more
1030 * details.
1031 * @url is base address of ISDS web service. Pass NULL or extern isds_locator
1032 * variable to use production ISDS instance. You can pass extern
1033 * isds_testing_locator variable to select testing instance.
1034 * @username is user name of ISDS user or box ID
1035 * @password is user's secret password
1036 * @pki_credentials defines public key cryptographic material to use in client
1037 * authentication. */
1038 isds_error isds_login(struct isds_ctx *context, const char *url,
1039 const char *username, const char *password,
1040 const struct isds_pki_credentials *pki_credentials) {
1041 isds_error err = IE_NOT_LOGGED_IN;
1042 isds_error soap_err;
1043 xmlNsPtr isds_ns = NULL;
1044 xmlNodePtr request = NULL;
1045 xmlNodePtr response = NULL;
1047 if (!context) return IE_INVALID_CONTEXT;
1048 zfree(context->long_message);
1050 /* Close connection if already logged in */
1051 if (context->curl) {
1052 _isds_close_connection(context);
1055 /* Default locator is offical system */
1056 if (!url) url = isds_locator;
1058 /* Store configuration */
1059 context->type = CTX_TYPE_ISDS;
1060 zfree(context->url);
1062 /* Mangle base URI according requested authentication method */
1063 if (!pki_credentials) {
1064 isds_log(ILF_SEC, ILL_INFO,
1065 _("Selected authentication method: no certificate, "
1066 "username and password\n"));
1067 if (!username || !password) {
1068 isds_log_message(context,
1069 _("Both username and password must be supplied"));
1070 return IE_INVAL;
1072 context->url = strdup(url);
1073 } else {
1074 if (!username) {
1075 isds_log(ILF_SEC, ILL_INFO,
1076 _("Selected authentication method: system certificate, "
1077 "no username and no password\n"));
1078 password = NULL;
1079 context->url = _isds_astrcat(url, "cert/");
1080 } else {
1081 if (!password) {
1082 isds_log(ILF_SEC, ILL_INFO,
1083 _("Selected authentication method: system certificate, "
1084 "box ID and no password\n"));
1085 context->url = _isds_astrcat(url, "hspis/");
1086 } else {
1087 isds_log(ILF_SEC, ILL_INFO,
1088 _("Selected authentication method: commercial "
1089 "certificate, username and password\n"));
1090 context->url = _isds_astrcat(url, "cert/");
1094 if (!(context->url))
1095 return IE_NOMEM;
1097 /* Prepare CURL handle */
1098 context->curl = curl_easy_init();
1099 if (!(context->curl))
1100 return IE_ERROR;
1102 /* Build login request */
1103 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1104 if (!request) {
1105 isds_log_message(context, _("Could build ISDS login request"));
1106 return IE_ERROR;
1108 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1109 if(!isds_ns) {
1110 isds_log_message(context, _("Could not create ISDS name space"));
1111 xmlFreeNode(request);
1112 return IE_ERROR;
1114 xmlSetNs(request, isds_ns);
1116 /* Store credentials */
1117 /* FIXME: mlock password
1118 * (I have a library) */
1119 discard_credentials(context);
1120 if (username) context->username = strdup(username);
1121 if (password) context->password = strdup(password);
1122 context->pki_credentials = isds_pki_credentials_duplicate(pki_credentials);
1123 if ((username && !context->username) || (password && !context->password) ||
1124 (pki_credentials && !context->pki_credentials)) {
1125 discard_credentials(context);
1126 xmlFreeNode(request);
1127 return IE_NOMEM;
1130 isds_log(ILF_ISDS, ILL_DEBUG, _("Logging user %s into server %s\n"),
1131 username, url);
1133 /* Send login request */
1134 soap_err = _isds_soap(context, "DS/dz", request, &response, NULL, NULL);
1136 /* Remove credentials */
1137 discard_credentials(context);
1139 /* Destroy login request */
1140 xmlFreeNode(request);
1142 if (soap_err) {
1143 xmlFreeNodeList(response);
1144 _isds_close_connection(context);
1145 return soap_err;
1148 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1149 * authentication succeeded if soap_err == IE_SUCCESS */
1150 err = IE_SUCCESS;
1152 xmlFreeNodeList(response);
1154 if (!err)
1155 isds_log(ILF_ISDS, ILL_DEBUG,
1156 _("User %s has been logged into server %s successfully\n"),
1157 username, url);
1158 return err;
1162 /* Log out from ISDS server discards credentials and connection configuration. */
1163 isds_error isds_logout(struct isds_ctx *context) {
1164 if (!context) return IE_INVALID_CONTEXT;
1165 zfree(context->long_message);
1167 /* Close connection */
1168 if (context->curl) {
1169 _isds_close_connection(context);
1171 /* Discard credentials for sure. They should not survive isds_login(),
1172 * even successful .*/
1173 discard_credentials(context);
1174 zfree(context->url);
1176 isds_log(ILF_ISDS, ILL_DEBUG, _("Logged out from ISDS server\n"));
1177 } else {
1178 discard_credentials(context);
1180 return IE_SUCCESS;
1184 /* Verify connection to ISDS is alive and server is responding.
1185 * Sent dumy request to ISDS and expect dummy response. */
1186 isds_error isds_ping(struct isds_ctx *context) {
1187 isds_error soap_err;
1188 xmlNsPtr isds_ns = NULL;
1189 xmlNodePtr request = NULL;
1190 xmlNodePtr response = NULL;
1192 if (!context) return IE_INVALID_CONTEXT;
1193 zfree(context->long_message);
1195 /* Check if connection is established */
1196 if (!context->curl) return IE_CONNECTION_CLOSED;
1199 /* Build dummy request */
1200 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1201 if (!request) {
1202 isds_log_message(context, _("Could build ISDS dummy request"));
1203 return IE_ERROR;
1205 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1206 if(!isds_ns) {
1207 isds_log_message(context, _("Could not create ISDS name space"));
1208 xmlFreeNode(request);
1209 return IE_ERROR;
1211 xmlSetNs(request, isds_ns);
1213 isds_log(ILF_ISDS, ILL_DEBUG, _("Pinging ISDS server\n"));
1215 /* Sent dummy request */
1216 soap_err = _isds_soap(context, "DS/dz", request, &response, NULL, NULL);
1218 /* Destroy login request */
1219 xmlFreeNode(request);
1221 if (soap_err) {
1222 isds_log(ILF_ISDS, ILL_DEBUG,
1223 _("ISDS server could not be contacted\n"));
1224 xmlFreeNodeList(response);
1225 return soap_err;
1228 /* XXX: Untill we don't propagate HTTP code 500 or 4xx, we can be sure
1229 * authentication succeeded if soap_err == IE_SUCCESS */
1230 /* TODO: ISDS documentation does not specify response body.
1231 * However real server sends back DummyOperationResponse */
1234 xmlFreeNodeList(response);
1236 isds_log(ILF_ISDS, ILL_DEBUG, _("ISDS server alive\n"));
1238 return IE_SUCCESS;
1242 /* Send bogus request to ISDS.
1243 * Just for test purposes */
1244 isds_error isds_bogus_request(struct isds_ctx *context) {
1245 isds_error err;
1246 xmlNsPtr isds_ns = NULL;
1247 xmlNodePtr request = NULL;
1248 xmlDocPtr response = NULL;
1249 xmlChar *code = NULL, *message = NULL;
1251 if (!context) return IE_INVALID_CONTEXT;
1252 zfree(context->long_message);
1254 /* Check if connection is established */
1255 if (!context->curl) {
1256 /* Testing printf message */
1257 isds_printf_message(context, "%s", _("I said connection closed"));
1258 return IE_CONNECTION_CLOSED;
1262 /* Build dummy request */
1263 request = xmlNewNode(NULL, BAD_CAST "X-BogusOperation");
1264 if (!request) {
1265 isds_log_message(context, _("Could build ISDS bogus request"));
1266 return IE_ERROR;
1268 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1269 if(!isds_ns) {
1270 isds_log_message(context, _("Could not create ISDS name space"));
1271 xmlFreeNode(request);
1272 return IE_ERROR;
1274 xmlSetNs(request, isds_ns);
1276 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending bogus request to ISDS\n"));
1278 /* Sent bogus request */
1279 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
1281 /* Destroy request */
1282 xmlFreeNode(request);
1284 if (err) {
1285 isds_log(ILF_ISDS, ILL_DEBUG,
1286 _("Processing ISDS response on bogus request failed\n"));
1287 xmlFreeDoc(response);
1288 return err;
1291 /* Check for response status */
1292 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
1293 &code, &message, NULL);
1294 if (err) {
1295 isds_log(ILF_ISDS, ILL_DEBUG,
1296 _("ISDS response on bogus request is missing status\n"));
1297 free(code);
1298 free(message);
1299 xmlFreeDoc(response);
1300 return err;
1302 if (xmlStrcmp(code, BAD_CAST "0000")) {
1303 char *code_locale = _isds_utf82locale((char*)code);
1304 char *message_locale = _isds_utf82locale((char*)message);
1305 isds_log(ILF_ISDS, ILL_DEBUG,
1306 _("Server refused bogus request (code=%s, message=%s)\n"),
1307 code_locale, message_locale);
1308 /* XXX: Literal error messages from ISDS are Czech mesages
1309 * (English sometimes) in UTF-8. It's hard to catch them for
1310 * translation. Successfully gettextized would return in locale
1311 * encoding, unsuccessfully translated would pass in UTF-8. */
1312 isds_log_message(context, message_locale);
1313 free(code_locale);
1314 free(message_locale);
1315 free(code);
1316 free(message);
1317 xmlFreeDoc(response);
1318 return IE_ISDS;
1322 free(code);
1323 free(message);
1324 xmlFreeDoc(response);
1326 isds_log(ILF_ISDS, ILL_DEBUG,
1327 _("Bogus message accepted by server. This should not happen.\n"));
1329 return IE_SUCCESS;
1333 /* Serialize XML subtree to buffer preserving XML indentatition.
1334 * @context is session context
1335 * @subtree is XML element to be serialized (with childern)
1336 * @buffer is automatically reallocated buffer where serialize to
1337 * @length is size of serialized stream in bytes
1338 * @return standard error code, free @buffer in case of error */
1339 static isds_error serialize_subtree(struct isds_ctx *context,
1340 xmlNodePtr subtree, void **buffer, size_t *length) {
1341 isds_error err = IE_SUCCESS;
1342 xmlBufferPtr xml_buffer = NULL;
1343 xmlSaveCtxtPtr save_ctx = NULL;
1344 xmlDocPtr subtree_doc = NULL;
1345 xmlNodePtr subtree_copy;
1346 xmlNsPtr isds_ns;
1347 void *new_buffer;
1349 if (!context) return IE_INVALID_CONTEXT;
1350 if (!buffer) return IE_INVAL;
1351 zfree(*buffer);
1352 if (!subtree || !length) return IE_INVAL;
1354 /* Make temporary XML document with @subtree root element */
1355 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1356 * It can result in not well-formed on invalid XML tree (e.g. name space
1357 * prefix definition can miss. */
1358 /*FIXME */
1360 subtree_doc = xmlNewDoc(BAD_CAST "1.0");
1361 if (!subtree_doc) {
1362 isds_log_message(context, _("Could not build temporary document"));
1363 err = IE_ERROR;
1364 goto leave;
1367 /* XXX: Copy subtree and attach the copy to document.
1368 * One node can not bee attached into more document at the same time.
1369 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1370 * automatically.
1371 * XXX: Check xmlSaveTree() too. */
1372 subtree_copy = xmlCopyNodeList(subtree);
1373 if (!subtree_copy) {
1374 isds_log_message(context, _("Could not copy subtree"));
1375 err = IE_ERROR;
1376 goto leave;
1378 xmlDocSetRootElement(subtree_doc, subtree_copy);
1380 /* Only this way we get namespace definition as @xmlns:isds,
1381 * otherwise we get namespace prefix without definition */
1382 /* FIXME: Don't overwrite original default namespace */
1383 isds_ns = xmlNewNs(subtree_copy, BAD_CAST ISDS_NS, NULL);
1384 if(!isds_ns) {
1385 isds_log_message(context, _("Could not create ISDS name space"));
1386 err = IE_ERROR;
1387 goto leave;
1389 xmlSetNs(subtree_copy, isds_ns);
1392 /* Serialize the document into buffer */
1393 xml_buffer = xmlBufferCreate();
1394 if (!xml_buffer) {
1395 isds_log_message(context, _("Could not create xmlBuffer"));
1396 err = IE_ERROR;
1397 goto leave;
1399 /* Last argument 0 means to not format the XML tree */
1400 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8", 0);
1401 if (!save_ctx) {
1402 isds_log_message(context, _("Could not create XML serializer"));
1403 err = IE_ERROR;
1404 goto leave;
1406 /* XXX: According LibXML documentation, this function does not return
1407 * meaningfull value yet */
1408 xmlSaveDoc(save_ctx, subtree_doc);
1409 if (-1 == xmlSaveFlush(save_ctx)) {
1410 isds_log_message(context,
1411 _("Could not serialize XML subtree"));
1412 err = IE_ERROR;
1413 goto leave;
1415 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1416 * even after xmlSaveFlush(). Thus close it here */
1417 xmlSaveClose(save_ctx); save_ctx = NULL;
1420 /* Store and detach buffer from xml_buffer */
1421 *buffer = xml_buffer->content;
1422 *length = xml_buffer->use;
1423 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1425 /* Shrink buffer */
1426 new_buffer = realloc(*buffer, *length);
1427 if (new_buffer) *buffer = new_buffer;
1429 leave:
1430 if (err) {
1431 zfree(*buffer);
1432 *length = 0;
1435 xmlSaveClose(save_ctx);
1436 xmlBufferFree(xml_buffer);
1437 xmlFreeDoc(subtree_doc); /* Frees subtree_copy, isds_ns etc. */
1438 return err;
1441 #if 0
1442 /* Dump XML subtree to buffer as literral string, not valid XML possibly.
1443 * @context is session context
1444 * @document is original document where @nodeset points to
1445 * @nodeset is XPath node set to dump (recursively)
1446 * @buffer is automarically reallocated buffer where serialize to
1447 * @length is size of serialized stream in bytes
1448 * @return standard error code, free @buffer in case of error */
1449 static isds_error dump_nodeset(struct isds_ctx *context,
1450 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1451 void **buffer, size_t *length) {
1452 isds_error err = IE_SUCCESS;
1453 xmlBufferPtr xml_buffer = NULL;
1454 void *new_buffer;
1456 if (!context) return IE_INVALID_CONTEXT;
1457 if (!buffer) return IE_INVAL;
1458 zfree(*buffer);
1459 if (!document || !nodeset || !length) return IE_INVAL;
1460 *length = 0;
1462 /* Empty node set results into NULL buffer */
1463 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1464 goto leave;
1467 /* Resuling the document into buffer */
1468 xml_buffer = xmlBufferCreate();
1469 if (!xml_buffer) {
1470 isds_log_message(context, _("Could not create xmlBuffer"));
1471 err = IE_ERROR;
1472 goto leave;
1475 /* Itearate over all nodes */
1476 for (int i = 0; i < nodeset->nodeNr; i++) {
1477 /* Serialize node.
1478 * XXX: xmlNodeDump() appends to xml_buffer. */
1479 if (-1 ==
1480 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1481 isds_log_message(context, _("Could not dump XML node"));
1482 err = IE_ERROR;
1483 goto leave;
1487 /* Store and detach buffer from xml_buffer */
1488 *buffer = xml_buffer->content;
1489 *length = xml_buffer->use;
1490 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1492 /* Shrink buffer */
1493 new_buffer = realloc(*buffer, *length);
1494 if (new_buffer) *buffer = new_buffer;
1497 leave:
1498 if (err) {
1499 zfree(*buffer);
1500 *length = 0;
1503 xmlBufferFree(xml_buffer);
1504 return err;
1506 #endif
1508 #if 0
1509 /* Dump XML subtree to buffer as literral string, not valid XML possibly.
1510 * @context is session context
1511 * @document is original document where @nodeset points to
1512 * @nodeset is XPath node set to dump (recursively)
1513 * @buffer is automarically reallocated buffer where serialize to
1514 * @length is size of serialized stream in bytes
1515 * @return standard error code, free @buffer in case of error */
1516 static isds_error dump_nodeset(struct isds_ctx *context,
1517 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1518 void **buffer, size_t *length) {
1519 isds_error err = IE_SUCCESS;
1520 xmlBufferPtr xml_buffer = NULL;
1521 xmlSaveCtxtPtr save_ctx = NULL;
1522 void *new_buffer;
1524 if (!context) return IE_INVALID_CONTEXT;
1525 if (!buffer) return IE_INVAL;
1526 zfree(*buffer);
1527 if (!document || !nodeset || !length) return IE_INVAL;
1528 *length = 0;
1530 /* Empty node set results into NULL buffer */
1531 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1532 goto leave;
1535 /* Resuling the document into buffer */
1536 xml_buffer = xmlBufferCreate();
1537 if (!xml_buffer) {
1538 isds_log_message(context, _("Could not create xmlBuffer"));
1539 err = IE_ERROR;
1540 goto leave;
1542 if (xmlSubstituteEntitiesDefault(1)) {
1543 isds_log_message(context, _("Could not disable attribute escaping"));
1544 err = IE_ERROR;
1545 goto leave;
1547 /* Last argument means:
1548 * 0 to not format the XML tree
1549 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1550 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8",
1551 XML_SAVE_NO_DECL|XML_SAVE_NO_EMPTY|XML_SAVE_NO_XHTML);
1552 if (!save_ctx) {
1553 isds_log_message(context, _("Could not create XML serializer"));
1554 err = IE_ERROR;
1555 goto leave;
1557 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1558 isds_log_message(context, _("Could not disable attribute escaping"));
1559 err = IE_ERROR;
1560 goto leave;
1564 /* Itearate over all nodes */
1565 for (int i = 0; i < nodeset->nodeNr; i++) {
1566 /* Serialize node.
1567 * XXX: xmlNodeDump() appends to xml_buffer. */
1568 /*if (-1 ==
1569 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1571 /* XXX: According LibXML documentation, this function does not return
1572 * meaningfull value yet */
1573 xmlSaveTree(save_ctx, nodeset->nodeTab[i]);
1574 if (-1 == xmlSaveFlush(save_ctx)) {
1575 isds_log_message(context,
1576 _("Could not serialize XML subtree"));
1577 err = IE_ERROR;
1578 goto leave;
1582 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1583 * even after xmlSaveFlush(). Thus close it here */
1584 xmlSaveClose(save_ctx); save_ctx = NULL;
1586 /* Store and detach buffer from xml_buffer */
1587 *buffer = xml_buffer->content;
1588 *length = xml_buffer->use;
1589 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1591 /* Shrink buffer */
1592 new_buffer = realloc(*buffer, *length);
1593 if (new_buffer) *buffer = new_buffer;
1595 leave:
1596 if (err) {
1597 zfree(*buffer);
1598 *length = 0;
1601 xmlSaveClose(save_ctx);
1602 xmlBufferFree(xml_buffer);
1603 return err;
1605 #endif
1608 /* Convert UTF-8 @string represantion of ISDS dbType to enum @type */
1609 static isds_error string2isds_DbType(xmlChar *string, isds_DbType *type) {
1610 if (!string || !type) return IE_INVAL;
1612 if (!xmlStrcmp(string, BAD_CAST "FO"))
1613 *type = DBTYPE_FO;
1614 else if (!xmlStrcmp(string, BAD_CAST "PFO"))
1615 *type = DBTYPE_PFO;
1616 else if (!xmlStrcmp(string, BAD_CAST "PFO_ADVOK"))
1617 *type = DBTYPE_PFO_ADVOK;
1618 else if (!xmlStrcmp(string, BAD_CAST "PFO_DANPOR"))
1619 *type = DBTYPE_PFO_DANPOR;
1620 else if (!xmlStrcmp(string, BAD_CAST "PFO_INSSPR"))
1621 *type = DBTYPE_PFO_INSSPR;
1622 else if (!xmlStrcmp(string, BAD_CAST "PO"))
1623 *type = DBTYPE_PO;
1624 else if (!xmlStrcmp(string, BAD_CAST "PO_ZAK"))
1625 *type = DBTYPE_PO_ZAK;
1626 else if (!xmlStrcmp(string, BAD_CAST "PO_REQ"))
1627 *type = DBTYPE_PO_REQ;
1628 else if (!xmlStrcmp(string, BAD_CAST "OVM"))
1629 *type = DBTYPE_OVM;
1630 else if (!xmlStrcmp(string, BAD_CAST "OVM_NOTAR"))
1631 *type = DBTYPE_OVM_NOTAR;
1632 else if (!xmlStrcmp(string, BAD_CAST "OVM_EXEKUT"))
1633 *type = DBTYPE_OVM_EXEKUT;
1634 else if (!xmlStrcmp(string, BAD_CAST "OVM_REQ"))
1635 *type = DBTYPE_OVM_REQ;
1636 else
1637 return IE_ENUM;
1638 return IE_SUCCESS;
1642 /* Convert ISDS dbType enum @type to UTF-8 string.
1643 * @Return pointer to static string, or NULL if unkwnow enum value */
1644 static const xmlChar *isds_DbType2string(const isds_DbType type) {
1645 switch(type) {
1646 case DBTYPE_FO: return(BAD_CAST "FO"); break;
1647 case DBTYPE_PFO: return(BAD_CAST "PFO"); break;
1648 case DBTYPE_PFO_ADVOK: return(BAD_CAST "PFO_ADVOK"); break;
1649 case DBTYPE_PFO_DANPOR: return(BAD_CAST "PFO_DANPOR"); break;
1650 case DBTYPE_PFO_INSSPR: return(BAD_CAST "PFO_INSSPR"); break;
1651 case DBTYPE_PO: return(BAD_CAST "PO"); break;
1652 case DBTYPE_PO_ZAK: return(BAD_CAST "PO_ZAK"); break;
1653 case DBTYPE_PO_REQ: return(BAD_CAST "PO_REQ"); break;
1654 case DBTYPE_OVM: return(BAD_CAST "OVM"); break;
1655 case DBTYPE_OVM_NOTAR: return(BAD_CAST "OVM_NOTAR"); break;
1656 case DBTYPE_OVM_EXEKUT: return(BAD_CAST "OVM_EXEKUT"); break;
1657 case DBTYPE_OVM_REQ: return(BAD_CAST "OVM_REQ"); break;
1658 default: return NULL; break;
1663 /* Convert UTF-8 @string represantion of ISDS userType to enum @type */
1664 static isds_error string2isds_UserType(xmlChar *string, isds_UserType *type) {
1665 if (!string || !type) return IE_INVAL;
1667 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
1668 *type = USERTYPE_PRIMARY;
1669 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
1670 *type = USERTYPE_ENTRUSTED;
1671 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
1672 *type = USERTYPE_ADMINISTRATOR;
1673 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
1674 *type = USERTYPE_OFFICIAL;
1675 else
1676 return IE_ENUM;
1677 return IE_SUCCESS;
1681 /* Convert ISDS userType enum @type to UTF-8 string.
1682 * @Return pointer to static string, or NULL if unkwnow enum value */
1683 static const xmlChar *isds_UserType2string(const isds_UserType type) {
1684 switch(type) {
1685 case USERTYPE_PRIMARY: return(BAD_CAST "PRIMARY_USER"); break;
1686 case USERTYPE_ENTRUSTED: return(BAD_CAST "ENTRUSTED_USER"); break;
1687 case USERTYPE_ADMINISTRATOR: return(BAD_CAST "ADMINISTRATOR"); break;
1688 case USERTYPE_OFFICIAL: return(BAD_CAST "OFFICIAL"); break;
1689 default: return NULL; break;
1694 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
1695 * @Return pointer to static string, or NULL if unkwnow enum value */
1696 static const xmlChar *isds_FileMetaType2string(const isds_FileMetaType type) {
1697 switch(type) {
1698 case FILEMETATYPE_MAIN: return(BAD_CAST "main"); break;
1699 case FILEMETATYPE_ENCLOSURE: return(BAD_CAST "enclosure"); break;
1700 case FILEMETATYPE_SIGNATURE: return(BAD_CAST "signature"); break;
1701 case FILEMETATYPE_META: return(BAD_CAST "meta"); break;
1702 default: return NULL; break;
1707 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
1708 * @Return IE_ENUM if @string is not valid enum member */
1709 static isds_error string2isds_FileMetaType(const xmlChar *string,
1710 isds_FileMetaType *type) {
1711 if (!string || !type) return IE_INVAL;
1713 if (!xmlStrcmp(string, BAD_CAST "main"))
1714 *type = FILEMETATYPE_MAIN;
1715 else if (!xmlStrcmp(string, BAD_CAST "enclosure"))
1716 *type = FILEMETATYPE_ENCLOSURE;
1717 else if (!xmlStrcmp(string, BAD_CAST "signature"))
1718 *type = FILEMETATYPE_SIGNATURE;
1719 else if (!xmlStrcmp(string, BAD_CAST "meta"))
1720 *type = FILEMETATYPE_META;
1721 else
1722 return IE_ENUM;
1723 return IE_SUCCESS;
1727 /* Convert UTF-8 @string to ISDS hash @algorithm.
1728 * @Return IE_ENUM if @string is not valid enum member */
1729 static isds_error string2isds_hash_algorithm(const xmlChar *string,
1730 isds_hash_algorithm *algorithm) {
1731 if (!string || !algorithm) return IE_INVAL;
1733 if (!xmlStrcmp(string, BAD_CAST "MD5"))
1734 *algorithm = HASH_ALGORITHM_MD5;
1735 else if (!xmlStrcmp(string, BAD_CAST "SHA-1"))
1736 *algorithm = HASH_ALGORITHM_SHA_1;
1737 else if (!xmlStrcmp(string, BAD_CAST "SHA-224"))
1738 *algorithm = HASH_ALGORITHM_SHA_224;
1739 else if (!xmlStrcmp(string, BAD_CAST "SHA-256"))
1740 *algorithm = HASH_ALGORITHM_SHA_256;
1741 else if (!xmlStrcmp(string, BAD_CAST "SHA-384"))
1742 *algorithm = HASH_ALGORITHM_SHA_384;
1743 else if (!xmlStrcmp(string, BAD_CAST "SHA-512"))
1744 *algorithm = HASH_ALGORITHM_SHA_512;
1745 else
1746 return IE_ENUM;
1747 return IE_SUCCESS;
1751 /* Convert UTF-8 @string represantion of ISO 8601 date to @time.
1752 * XXX: Not all ISO formats are supported */
1753 static isds_error datestring2tm(const xmlChar *string, struct tm *time) {
1754 char *offset;
1755 if (!string || !time) return IE_INVAL;
1757 /* xsd:date is ISO 8601 string, thus ASCII */
1758 offset = strptime((char*)string, "%Y-%m-%d", time);
1759 if (offset && *offset == '\0')
1760 return IE_SUCCESS;
1762 offset = strptime((char*)string, "%Y%m%d", time);
1763 if (offset && *offset == '\0')
1764 return IE_SUCCESS;
1766 offset = strptime((char*)string, "%Y-%j", time);
1767 if (offset && *offset == '\0')
1768 return IE_SUCCESS;
1770 return IE_NOTSUP;
1774 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
1775 static isds_error tm2datestring(const struct tm *time, xmlChar **string) {
1776 if (!time || !string) return IE_INVAL;
1778 if (-1 == isds_asprintf((char **) string, "%d-%02d-%02d",
1779 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
1780 return IE_ERROR;
1782 return IE_SUCCESS;
1786 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
1787 * respects the @time microseconds too. */
1788 static isds_error timeval2timestring(const struct timeval *time,
1789 xmlChar **string) {
1790 struct tm broken;
1792 if (!time || !string) return IE_INVAL;
1794 if (!gmtime_r(&time->tv_sec, &broken)) return IE_DATE;
1795 if (time->tv_usec < 0 || time->tv_usec > 999999) return IE_DATE;
1797 /* TODO: small negative year should be formated as "-0012". This is not
1798 * true for glibc "%04d". We should implement it.
1799 * TODO: What's type of time->tv_usec exactly? Unsigned? Absolute?
1800 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
1801 if (-1 == isds_asprintf((char **) string,
1802 "%04d-%02d-%02dT%02d:%02d:%02d.%06ld",
1803 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
1804 broken.tm_hour, broken.tm_min, broken.tm_sec,
1805 time->tv_usec))
1806 return IE_ERROR;
1808 return IE_SUCCESS;
1812 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
1813 * It respects microseconds too.
1814 * In case of error, @time will be freed. */
1815 static isds_error timestring2timeval(const xmlChar *string,
1816 struct timeval **time) {
1817 struct tm broken;
1818 char *offset, *delim, *endptr;
1819 char subseconds[7];
1820 int offset_hours, offset_minutes;
1821 int i;
1823 if (!time) return IE_INVAL;
1825 memset(&broken, 0, sizeof(broken));
1827 if (!*time) {
1828 *time = calloc(1, sizeof(**time));
1829 if (!*time) return IE_NOMEM;
1830 } else {
1831 memset(*time, 0, sizeof(**time));
1835 /* xsd:date is ISO 8601 string, thus ASCII */
1836 /*TODO: negative year */
1838 /* Parse date and time without subseconds and offset */
1839 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
1840 if (!offset) {
1841 free(*time); *time = NULL;
1842 return IE_DATE;
1845 /* Get subseconds */
1846 if (*offset == '.' ) {
1847 offset++;
1849 /* Copy first 6 digits, padd it with zeros.
1850 * XXX: It truncates longer number, no round.
1851 * Current server implementation uses only milisecond resolution. */
1852 /* TODO: isdigit() is locale sensitive */
1853 for (i = 0;
1854 i < sizeof(subseconds)/sizeof(char) - 1 && isdigit(*offset);
1855 i++, offset++) {
1856 subseconds[i] = *offset;
1858 for (; i < sizeof(subseconds)/sizeof(char) - 1; i++) {
1859 subseconds[i] = '0';
1861 subseconds[6] = '\0';
1863 /* Convert it into integer */
1864 (*time)->tv_usec = strtol(subseconds, &endptr, 10);
1865 if (*endptr != '\0' || (*time)->tv_usec == LONG_MIN ||
1866 (*time)->tv_usec == LONG_MAX) {
1867 free(*time); *time = NULL;
1868 return IE_DATE;
1871 /* move to the zone offset delimiter */
1872 delim = strchr(offset, '-');
1873 if (!delim)
1874 delim = strchr(offset, '+');
1875 offset = delim;
1878 /* Get zone offset */
1879 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
1880 * "" equals to "Z" and it means UTC zone. */
1881 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
1882 * colon separator */
1883 if (*offset == '-' || *offset == '+') {
1884 offset++;
1885 if (2 != sscanf(offset, "%2d:%2d", &offset_hours, &offset_minutes)) {
1886 free(*time); *time = NULL;
1887 return IE_DATE;
1889 broken.tm_hour -= offset_hours;
1890 broken.tm_min -= offset_minutes * ((offset_hours<0) ? -1 : 1);
1893 /* Convert to time_t */
1894 _isds_switch_tz_to_utc();
1895 (*time)->tv_sec = mktime(&broken);
1896 _isds_switch_tz_to_native();
1897 if ((*time)->tv_sec == (time_t) -1) {
1898 free(*time); *time = NULL;
1899 return IE_DATE;
1902 return IE_SUCCESS;
1906 /* Convert unsigned int into isds_message_status.
1907 * @context is session context
1908 * @number is pointer to number value. NULL will be treated as invalid value.
1909 * @status is automatically reallocated status
1910 * @return IE_SUCCESS, or error code and free status */
1911 static isds_error uint2isds_message_status(struct isds_ctx *context,
1912 const unsigned long int *number, isds_message_status **status) {
1913 if (!context) return IE_INVALID_CONTEXT;
1914 if (!status) return IE_INVAL;
1916 free(*status); *status = NULL;
1917 if (!number) return IE_INVAL;
1919 if (*number < 1 || *number > 10) {
1920 isds_printf_message(context, _("Invalid messsage status value: %lu"),
1921 *number);
1922 return IE_ENUM;
1925 *status = malloc(sizeof(**status));
1926 if (!*status) return IE_NOMEM;
1928 **status = 1 << *number;
1929 return IE_SUCCESS;
1933 /* Convert event description string into isds_event memebers type and
1934 * description
1935 * @string is raw event decsription starting with event prefix
1936 * @event is structure where to store type and stripped description to
1937 * @return standard error code, unknown prefix is not classified as an error.
1938 * */
1939 static isds_error eventstring2event(const xmlChar *string,
1940 struct isds_event* event) {
1941 const xmlChar *known_prefixes[] = {
1942 BAD_CAST "EV1:",
1943 BAD_CAST "EV2:",
1944 BAD_CAST "EV3:",
1945 BAD_CAST "EV4:"
1947 const isds_event_type types[] = {
1948 EVENT_ACCEPTED_BY_RECIPIENT,
1949 EVENT_ACCEPTED_BY_FICTION,
1950 EVENT_UNDELIVERABLE,
1951 EVENT_COMMERCIAL_ACCEPTED
1953 unsigned int index;
1954 size_t length;
1956 if (!string || !event) return IE_INVAL;
1958 if (!event->type) {
1959 event->type = malloc(sizeof(*event->type));
1960 if (!(event->type)) return IE_NOMEM;
1962 zfree(event->description);
1964 for (index = 0; index < sizeof(known_prefixes)/sizeof(known_prefixes[0]);
1965 index++) {
1966 length = xmlUTF8Strlen(known_prefixes[index]);
1968 if (!xmlStrncmp(string, known_prefixes[index], length)) {
1969 /* Prefix is known */
1970 *event->type = types[index];
1972 /* Strip prefix from description and spaces */
1973 /* TODO: Recognize all wite spaces from UCS blank class and
1974 * operate on UTF-8 chars. */
1975 for (; string[length] != '\0' && string[length] == ' '; length++);
1976 event->description = strdup((char *) (string + length));
1977 if (!(event->description)) return IE_NOMEM;
1979 return IE_SUCCESS;
1983 /* Unknown event prefix.
1984 * XSD allows any string */
1985 char *string_locale = _isds_utf82locale((char *) string);
1986 isds_log(ILF_ISDS, ILL_WARNING,
1987 _("Uknown delivery info event prefix: %s\n"), string_locale);
1988 free(string_locale);
1990 *event->type = EVENT_UKNOWN;
1991 event->description = strdup((char *) string);
1992 if (!(event->description)) return IE_NOMEM;
1994 return IE_SUCCESS;
1998 /* Following EXTRACT_* macros expects @result, @xpath_ctx, @err, @context
1999 * and leave lable */
2000 #define EXTRACT_STRING(element, string) { \
2001 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2002 if (!result) { \
2003 err = IE_ERROR; \
2004 goto leave; \
2006 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2007 if (result->nodesetval->nodeNr > 1) { \
2008 isds_printf_message(context, _("Multiple %s element"), element); \
2009 err = IE_ERROR; \
2010 goto leave; \
2012 (string) = (char *) \
2013 xmlXPathCastNodeSetToString(result->nodesetval); \
2014 if (!(string)) { \
2015 err = IE_ERROR; \
2016 goto leave; \
2021 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2023 char *string = NULL; \
2024 EXTRACT_STRING(element, string); \
2026 if (string) { \
2027 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2028 if (!(booleanPtr)) { \
2029 free(string); \
2030 err = IE_NOMEM; \
2031 goto leave; \
2034 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2035 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2036 *(booleanPtr) = 1; \
2037 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2038 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2039 *(booleanPtr) = 0; \
2040 else { \
2041 char *string_locale = _isds_utf82locale((char*)string); \
2042 isds_printf_message(context, \
2043 _("%s value is not valid boolean: %s"), \
2044 element, string_locale); \
2045 free(string_locale); \
2046 free(string); \
2047 err = IE_ERROR; \
2048 goto leave; \
2051 free(string); \
2055 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2057 char *string = NULL; \
2058 EXTRACT_STRING(element, string); \
2059 if (string) { \
2060 long int number; \
2061 char *endptr; \
2063 number = strtol((char*)string, &endptr, 10); \
2065 if (*endptr != '\0') { \
2066 char *string_locale = _isds_utf82locale((char *)string); \
2067 isds_printf_message(context, \
2068 _("%s is not valid integer: %s"), \
2069 element, string_locale); \
2070 free(string_locale); \
2071 free(string); \
2072 err = IE_ISDS; \
2073 goto leave; \
2076 if (number == LONG_MIN || number == LONG_MAX) { \
2077 char *string_locale = _isds_utf82locale((char *)string); \
2078 isds_printf_message(context, \
2079 _("%s value out of range of long int: %s"), \
2080 element, string_locale); \
2081 free(string_locale); \
2082 free(string); \
2083 err = IE_ERROR; \
2084 goto leave; \
2087 free(string); string = NULL; \
2089 if (!(preallocated)) { \
2090 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2091 if (!(longintPtr)) { \
2092 err = IE_NOMEM; \
2093 goto leave; \
2096 *(longintPtr) = number; \
2100 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2102 char *string = NULL; \
2103 EXTRACT_STRING(element, string); \
2104 if (string) { \
2105 long int number; \
2106 char *endptr; \
2108 number = strtol((char*)string, &endptr, 10); \
2110 if (*endptr != '\0') { \
2111 char *string_locale = _isds_utf82locale((char *)string); \
2112 isds_printf_message(context, \
2113 _("%s is not valid integer: %s"), \
2114 element, string_locale); \
2115 free(string_locale); \
2116 free(string); \
2117 err = IE_ISDS; \
2118 goto leave; \
2121 if (number == LONG_MIN || number == LONG_MAX) { \
2122 char *string_locale = _isds_utf82locale((char *)string); \
2123 isds_printf_message(context, \
2124 _("%s value out of range of long int: %s"), \
2125 element, string_locale); \
2126 free(string_locale); \
2127 free(string); \
2128 err = IE_ERROR; \
2129 goto leave; \
2132 free(string); string = NULL; \
2133 if (number < 0) { \
2134 isds_printf_message(context, \
2135 _("%s value is negative: %ld"), element, number); \
2136 err = IE_ERROR; \
2137 goto leave; \
2140 if (!(preallocated)) { \
2141 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2142 if (!(ulongintPtr)) { \
2143 err = IE_NOMEM; \
2144 goto leave; \
2147 *(ulongintPtr) = number; \
2151 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2152 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2153 NULL); \
2154 if ((required) && (!string)) { \
2155 char *attribute_locale = _isds_utf82locale(attribute); \
2156 char *element_locale = \
2157 _isds_utf82locale((char *)xpath_ctx->node->name); \
2158 isds_printf_message(context, \
2159 _("Could not extract required %s attribute value from " \
2160 "%s element"), attribute_locale, element_locale); \
2161 free(element_locale); \
2162 free(attribute_locale); \
2163 err = IE_ERROR; \
2164 goto leave; \
2169 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2171 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2172 (xmlChar *) (string)); \
2173 if (!node) { \
2174 isds_printf_message(context, \
2175 _("Could not add %s child to %s element"), \
2176 element, (parent)->name); \
2177 err = IE_ERROR; \
2178 goto leave; \
2182 #define INSERT_STRING(parent, element, string) \
2183 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2185 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2187 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2188 else { INSERT_STRING(parent, element, "false"); } \
2191 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2193 if (booleanPtr) { \
2194 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2195 } else { \
2196 INSERT_STRING(parent, element, NULL); \
2200 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2201 if ((longintPtr)) { \
2202 /* FIXME: locale sensitive */ \
2203 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2204 err = IE_NOMEM; \
2205 goto leave; \
2207 INSERT_STRING(parent, element, buffer) \
2208 free(buffer); (buffer) = NULL; \
2209 } else { INSERT_STRING(parent, element, NULL) } \
2212 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2213 if ((ulongintPtr)) { \
2214 /* FIXME: locale sensitive */ \
2215 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
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_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2226 /* FIXME: locale sensitive */ \
2227 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2228 err = IE_NOMEM; \
2229 goto leave; \
2231 INSERT_STRING(parent, element, buffer) \
2232 free(buffer); (buffer) = NULL; \
2235 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2237 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2238 (xmlChar *) (string)); \
2239 if (!attribute_node) { \
2240 isds_printf_message(context, _("Could not add %s " \
2241 "attribute to %s element"), \
2242 (attribute), (parent)->name); \
2243 err = IE_ERROR; \
2244 goto leave; \
2248 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2249 if (string) { \
2250 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2251 if (length > (maximum)) { \
2252 isds_printf_message(context, \
2253 ngettext("%s has more than %d characters", \
2254 "%s has more than %d characters", (maximum)), \
2255 (name), (maximum)); \
2256 err = IE_2BIG; \
2257 goto leave; \
2259 if (length < (minimum)) { \
2260 isds_printf_message(context, \
2261 ngettext("%s has less than %d characters", \
2262 "%s has less than %d characters", (minimum)), \
2263 (name), (minimum)); \
2264 err = IE_2SMALL; \
2265 goto leave; \
2270 #define INSERT_ELEMENT(child, parent, element) \
2272 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2273 if (!(child)) { \
2274 isds_printf_message(context, \
2275 _("Could not add %s child to %s element"), \
2276 (element), (parent)->name); \
2277 err = IE_ERROR; \
2278 goto leave; \
2283 /* Find child element by name in given XPath context and switch context onto
2284 * it. The child must be uniq and must exist. Otherwise failes.
2285 * @context is ISDS context
2286 * @child is child element name
2287 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2288 * into it child. In error case, the @xpath_ctx keeps original value. */
2289 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
2290 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
2291 isds_error err = IE_SUCCESS;
2292 xmlXPathObjectPtr result = NULL;
2294 if (!context) return IE_INVALID_CONTEXT;
2295 if (!child || !xpath_ctx) return IE_INVAL;
2297 /* Find child */
2298 result = xmlXPathEvalExpression(child, xpath_ctx);
2299 if (!result) {
2300 err = IE_XML;
2301 goto leave;
2304 /* No match */
2305 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2306 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2307 char *child_locale = _isds_utf82locale((char*) child);
2308 isds_printf_message(context,
2309 _("%s element does not contain %s child"),
2310 parent_locale, child_locale);
2311 free(child_locale);
2312 free(parent_locale);
2313 err = IE_NOEXIST;
2314 goto leave;
2317 /* More matches */
2318 if (result->nodesetval->nodeNr > 1) {
2319 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2320 char *child_locale = _isds_utf82locale((char*) child);
2321 isds_printf_message(context,
2322 _("%s element contains multiple %s children"),
2323 parent_locale, child_locale);
2324 free(child_locale);
2325 free(parent_locale);
2326 err = IE_NOTUNIQ;
2327 goto leave;
2330 /* Switch context */
2331 xpath_ctx->node = result->nodesetval->nodeTab[0];
2333 leave:
2334 xmlXPathFreeObject(result);
2335 return err;
2340 /* Find and convert XSD:gPersonName group in current node into structure
2341 * @context is ISDS context
2342 * @personName is automically reallocated person name structure. If no member
2343 * value is found, will be freed.
2344 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2345 * elements
2346 * In case of error @personName will be freed. */
2347 static isds_error extract_gPersonName(struct isds_ctx *context,
2348 struct isds_PersonName **personName, xmlXPathContextPtr xpath_ctx) {
2349 isds_error err = IE_SUCCESS;
2350 xmlXPathObjectPtr result = NULL;
2352 if (!context) return IE_INVALID_CONTEXT;
2353 if (!personName) return IE_INVAL;
2354 isds_PersonName_free(personName);
2355 if (!xpath_ctx) return IE_INVAL;
2358 *personName = calloc(1, sizeof(**personName));
2359 if (!*personName) {
2360 err = IE_NOMEM;
2361 goto leave;
2364 EXTRACT_STRING("isds:pnFirstName", (*personName)->pnFirstName);
2365 EXTRACT_STRING("isds:pnMiddleName", (*personName)->pnMiddleName);
2366 EXTRACT_STRING("isds:pnLastName", (*personName)->pnLastName);
2367 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName)->pnLastNameAtBirth);
2369 if (!(*personName)->pnFirstName && !(*personName)->pnMiddleName &&
2370 !(*personName)->pnLastName && !(*personName)->pnLastNameAtBirth)
2371 isds_PersonName_free(personName);
2373 leave:
2374 if (err) isds_PersonName_free(personName);
2375 xmlXPathFreeObject(result);
2376 return err;
2380 /* Find and convert XSD:gAddress group in current node into structure
2381 * @context is ISDS context
2382 * @address is automically reallocated address structure. If no member
2383 * value is found, will be freed.
2384 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2385 * elements
2386 * In case of error @address will be freed. */
2387 static isds_error extract_gAddress(struct isds_ctx *context,
2388 struct isds_Address **address, xmlXPathContextPtr xpath_ctx) {
2389 isds_error err = IE_SUCCESS;
2390 xmlXPathObjectPtr result = NULL;
2392 if (!context) return IE_INVALID_CONTEXT;
2393 if (!address) return IE_INVAL;
2394 isds_Address_free(address);
2395 if (!xpath_ctx) return IE_INVAL;
2398 *address = calloc(1, sizeof(**address));
2399 if (!*address) {
2400 err = IE_NOMEM;
2401 goto leave;
2404 EXTRACT_STRING("isds:adCity", (*address)->adCity);
2405 EXTRACT_STRING("isds:adStreet", (*address)->adStreet);
2406 EXTRACT_STRING("isds:adNumberInStreet", (*address)->adNumberInStreet);
2407 EXTRACT_STRING("isds:adNumberInMunicipality",
2408 (*address)->adNumberInMunicipality);
2409 EXTRACT_STRING("isds:adZipCode", (*address)->adZipCode);
2410 EXTRACT_STRING("isds:adState", (*address)->adState);
2412 if (!(*address)->adCity && !(*address)->adStreet &&
2413 !(*address)->adNumberInStreet &&
2414 !(*address)->adNumberInMunicipality &&
2415 !(*address)->adZipCode && !(*address)->adState)
2416 isds_Address_free(address);
2418 leave:
2419 if (err) isds_Address_free(address);
2420 xmlXPathFreeObject(result);
2421 return err;
2425 /* Find and convert isds:biDate element in current node into structure
2426 * @context is ISDS context
2427 * @biDate is automically reallocated birth date structure. If no member
2428 * value is found, will be freed.
2429 * @xpath_ctx is XPath context with current node as parent for isds:biDate
2430 * element
2431 * In case of error @biDate will be freed. */
2432 static isds_error extract_BiDate(struct isds_ctx *context,
2433 struct tm **biDate, xmlXPathContextPtr xpath_ctx) {
2434 isds_error err = IE_SUCCESS;
2435 xmlXPathObjectPtr result = NULL;
2436 char *string = NULL;
2438 if (!context) return IE_INVALID_CONTEXT;
2439 if (!biDate) return IE_INVAL;
2440 zfree(*biDate);
2441 if (!xpath_ctx) return IE_INVAL;
2443 EXTRACT_STRING("isds:biDate", string);
2444 if (string) {
2445 *biDate = calloc(1, sizeof(**biDate));
2446 if (!*biDate) {
2447 err = IE_NOMEM;
2448 goto leave;
2450 err = datestring2tm((xmlChar *)string, *biDate);
2451 if (err) {
2452 if (err == IE_NOTSUP) {
2453 err = IE_ISDS;
2454 char *string_locale = _isds_utf82locale(string);
2455 isds_printf_message(context,
2456 _("Invalid isds:biDate value: %s"), string_locale);
2457 free(string_locale);
2459 goto leave;
2463 leave:
2464 if (err) zfree(*biDate);
2465 free(string);
2466 xmlXPathFreeObject(result);
2467 return err;
2471 /* Convert isds:dBOwnerInfo XML tree into structure
2472 * @context is ISDS context
2473 * @db_owner_info is automically reallocated box owner info structure
2474 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
2475 * In case of error @db_owner_info will be freed. */
2476 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
2477 struct isds_DbOwnerInfo **db_owner_info,
2478 xmlXPathContextPtr xpath_ctx) {
2479 isds_error err = IE_SUCCESS;
2480 xmlXPathObjectPtr result = NULL;
2481 char *string = NULL;
2483 if (!context) return IE_INVALID_CONTEXT;
2484 if (!db_owner_info) return IE_INVAL;
2485 isds_DbOwnerInfo_free(db_owner_info);
2486 if (!xpath_ctx) return IE_INVAL;
2489 *db_owner_info = calloc(1, sizeof(**db_owner_info));
2490 if (!*db_owner_info) {
2491 err = IE_NOMEM;
2492 goto leave;
2495 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
2497 EXTRACT_STRING("isds:dbType", string);
2498 if (string) {
2499 (*db_owner_info)->dbType =
2500 calloc(1, sizeof(*((*db_owner_info)->dbType)));
2501 if (!(*db_owner_info)->dbType) {
2502 err = IE_NOMEM;
2503 goto leave;
2505 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
2506 if (err) {
2507 zfree((*db_owner_info)->dbType);
2508 if (err == IE_ENUM) {
2509 err = IE_ISDS;
2510 char *string_locale = _isds_utf82locale(string);
2511 isds_printf_message(context, _("Unknown isds:dbType: %s"),
2512 string_locale);
2513 free(string_locale);
2515 goto leave;
2517 zfree(string);
2520 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
2522 err = extract_gPersonName(context, &(*db_owner_info)->personName,
2523 xpath_ctx);
2524 if (err) goto leave;
2526 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
2528 (*db_owner_info)->birthInfo =
2529 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
2530 if (!(*db_owner_info)->birthInfo) {
2531 err = IE_NOMEM;
2532 goto leave;
2534 err = extract_BiDate(context, &(*db_owner_info)->birthInfo->biDate,
2535 xpath_ctx);
2536 if (err) goto leave;
2537 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
2538 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
2539 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
2540 if (!(*db_owner_info)->birthInfo->biDate &&
2541 !(*db_owner_info)->birthInfo->biCity &&
2542 !(*db_owner_info)->birthInfo->biCounty &&
2543 !(*db_owner_info)->birthInfo->biState)
2544 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
2546 err = extract_gAddress(context, &(*db_owner_info)->address, xpath_ctx);
2547 if (err) goto leave;
2549 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
2550 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
2551 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
2552 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
2553 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
2555 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
2557 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
2558 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
2559 (*db_owner_info)->dbOpenAddressing);
2561 leave:
2562 if (err) isds_DbOwnerInfo_free(db_owner_info);
2563 free(string);
2564 xmlXPathFreeObject(result);
2565 return err;
2569 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
2570 * @context is sesstion context
2571 * @owner is libsids structure with box description
2572 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
2573 static isds_error insert_DbOwnerInfo(struct isds_ctx *context,
2574 const struct isds_DbOwnerInfo *owner, xmlNodePtr db_owner_info) {
2576 isds_error err = IE_SUCCESS;
2577 xmlNodePtr node;
2578 xmlChar *string = NULL;
2580 if (!context) return IE_INVALID_CONTEXT;
2581 if (!owner || !db_owner_info) return IE_INVAL;
2584 /* Build XSD:tDbOwnerInfo */
2585 CHECK_FOR_STRING_LENGTH(owner->dbID, 0, 7, "dbID")
2586 INSERT_STRING(db_owner_info, "dbID", owner->dbID);
2588 /* dbType */
2589 if (owner->dbType) {
2590 const xmlChar *type_string = isds_DbType2string(*(owner->dbType));
2591 if (!type_string) {
2592 isds_printf_message(context, _("Invalid dbType value: %d"),
2593 *(owner->dbType));
2594 err = IE_ENUM;
2595 goto leave;
2597 INSERT_STRING(db_owner_info, "dbType", type_string);
2599 INSERT_STRING(db_owner_info, "ic", owner->ic);
2600 if (owner->personName) {
2601 INSERT_STRING(db_owner_info, "pnFirstName",
2602 owner->personName->pnFirstName);
2603 INSERT_STRING(db_owner_info, "pnMiddleName",
2604 owner->personName->pnMiddleName);
2605 INSERT_STRING(db_owner_info, "pnLastName",
2606 owner->personName->pnLastName);
2607 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
2608 owner->personName->pnLastNameAtBirth);
2610 INSERT_STRING(db_owner_info, "firmName", owner->firmName);
2611 if (owner->birthInfo) {
2612 if (owner->birthInfo->biDate) {
2613 if (!tm2datestring(owner->birthInfo->biDate, &string))
2614 INSERT_STRING(db_owner_info, "biDate", string);
2615 free(string); string = NULL;
2617 INSERT_STRING(db_owner_info, "biCity", owner->birthInfo->biCity);
2618 INSERT_STRING(db_owner_info, "biCounty", owner->birthInfo->biCounty);
2619 INSERT_STRING(db_owner_info, "biState", owner->birthInfo->biState);
2621 if (owner->address) {
2622 INSERT_STRING(db_owner_info, "adCity", owner->address->adCity);
2623 INSERT_STRING(db_owner_info, "adStreet", owner->address->adStreet);
2624 INSERT_STRING(db_owner_info, "adNumberInStreet",
2625 owner->address->adNumberInStreet);
2626 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
2627 owner->address->adNumberInMunicipality);
2628 INSERT_STRING(db_owner_info, "adZipCode", owner->address->adZipCode);
2629 INSERT_STRING(db_owner_info, "adState", owner->address->adState);
2631 INSERT_STRING(db_owner_info, "nationality", owner->nationality);
2632 INSERT_STRING(db_owner_info, "email", owner->email);
2633 INSERT_STRING(db_owner_info, "telNumber", owner->telNumber);
2635 CHECK_FOR_STRING_LENGTH(owner->identifier, 0, 20, "identifier")
2636 INSERT_STRING(db_owner_info, "identifier", owner->identifier);
2638 CHECK_FOR_STRING_LENGTH(owner->registryCode, 0, 5, "registryCode")
2639 INSERT_STRING(db_owner_info, "registryCode", owner->registryCode);
2641 INSERT_LONGINT(db_owner_info, "dbState", owner->dbState, string);
2643 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", owner->dbEffectiveOVM);
2644 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
2645 owner->dbOpenAddressing);
2647 leave:
2648 free(string);
2649 return err;
2653 /* Convert XSD:tDbUserInfo XML tree into structure
2654 * @context is ISDS context
2655 * @db_user_info is automically reallocated user info structure
2656 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
2657 * In case of error @db_user_info will be freed. */
2658 static isds_error extract_DbUserInfo(struct isds_ctx *context,
2659 struct isds_DbUserInfo **db_user_info, xmlXPathContextPtr xpath_ctx) {
2660 isds_error err = IE_SUCCESS;
2661 xmlXPathObjectPtr result = NULL;
2662 char *string = NULL;
2664 if (!context) return IE_INVALID_CONTEXT;
2665 if (!db_user_info) return IE_INVAL;
2666 isds_DbUserInfo_free(db_user_info);
2667 if (!xpath_ctx) return IE_INVAL;
2670 *db_user_info = calloc(1, sizeof(**db_user_info));
2671 if (!*db_user_info) {
2672 err = IE_NOMEM;
2673 goto leave;
2676 EXTRACT_STRING("isds:userID", (*db_user_info)->userID);
2678 EXTRACT_STRING("isds:userType", string);
2679 if (string) {
2680 (*db_user_info)->userType =
2681 calloc(1, sizeof(*((*db_user_info)->userType)));
2682 if (!(*db_user_info)->userType) {
2683 err = IE_NOMEM;
2684 goto leave;
2686 err = string2isds_UserType((xmlChar *)string,
2687 (*db_user_info)->userType);
2688 if (err) {
2689 zfree((*db_user_info)->userType);
2690 if (err == IE_ENUM) {
2691 err = IE_ISDS;
2692 char *string_locale = _isds_utf82locale(string);
2693 isds_printf_message(context,
2694 _("Unknown isds:userType value: %s"), string_locale);
2695 free(string_locale);
2697 goto leave;
2699 zfree(string);
2702 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info)->userPrivils, 0);
2704 (*db_user_info)->personName =
2705 calloc(1, sizeof(*((*db_user_info)->personName)));
2706 if (!(*db_user_info)->personName) {
2707 err = IE_NOMEM;
2708 goto leave;
2711 err = extract_gPersonName(context, &(*db_user_info)->personName,
2712 xpath_ctx);
2713 if (err) goto leave;
2715 err = extract_gAddress(context, &(*db_user_info)->address, xpath_ctx);
2716 if (err) goto leave;
2718 err = extract_BiDate(context, &(*db_user_info)->biDate, xpath_ctx);
2719 if (err) goto leave;
2721 EXTRACT_STRING("isds:ic", (*db_user_info)->ic);
2722 EXTRACT_STRING("isds:firmName", (*db_user_info)->firmName);
2724 EXTRACT_STRING("isds:caStreet", (*db_user_info)->caStreet);
2725 EXTRACT_STRING("isds:caCity", (*db_user_info)->caCity);
2726 EXTRACT_STRING("isds:caZipCode", (*db_user_info)->caZipCode);
2728 /* ???: Default value is "CZ" according specification. Should we provide
2729 * it? */
2730 EXTRACT_STRING("isds:caState", (*db_user_info)->caState);
2732 leave:
2733 if (err) isds_DbUserInfo_free(db_user_info);
2734 free(string);
2735 xmlXPathFreeObject(result);
2736 return err;
2740 /* Insert struct isds_DbUserInfo data (user description) into XML tree
2741 * @context is sesstion context
2742 * @user is libsids structure with user description
2743 * @db_user_info is XML element of XSD:tDbUserInfo */
2744 static isds_error insert_DbUserInfo(struct isds_ctx *context,
2745 const struct isds_DbUserInfo *user, xmlNodePtr db_user_info) {
2747 isds_error err = IE_SUCCESS;
2748 xmlNodePtr node;
2749 xmlChar *string = NULL;
2751 if (!context) return IE_INVALID_CONTEXT;
2752 if (!user || !db_user_info) return IE_INVAL;
2754 /* Build XSD:tDbUserInfo */
2755 if (user->personName) {
2756 INSERT_STRING(db_user_info, "pnFirstName",
2757 user->personName->pnFirstName);
2758 INSERT_STRING(db_user_info, "pnMiddleName",
2759 user->personName->pnMiddleName);
2760 INSERT_STRING(db_user_info, "pnLastName",
2761 user->personName->pnLastName);
2762 INSERT_STRING(db_user_info, "pnLastNameAtBirth",
2763 user->personName->pnLastNameAtBirth);
2765 if (user->address) {
2766 INSERT_STRING(db_user_info, "adCity", user->address->adCity);
2767 INSERT_STRING(db_user_info, "adStreet", user->address->adStreet);
2768 INSERT_STRING(db_user_info, "adNumberInStreet",
2769 user->address->adNumberInStreet);
2770 INSERT_STRING(db_user_info, "adNumberInMunicipality",
2771 user->address->adNumberInMunicipality);
2772 INSERT_STRING(db_user_info, "adZipCode", user->address->adZipCode);
2773 INSERT_STRING(db_user_info, "adState", user->address->adState);
2775 if (user->biDate) {
2776 if (!tm2datestring(user->biDate, &string))
2777 INSERT_STRING(db_user_info, "biDate", string);
2778 zfree(string);
2780 CHECK_FOR_STRING_LENGTH(user->userID, 6, 12, "userID");
2781 INSERT_STRING(db_user_info, "userID", user->userID);
2783 /* userType */
2784 if (user->userType) {
2785 const xmlChar *type_string = isds_UserType2string(*(user->userType));
2786 if (!type_string) {
2787 isds_printf_message(context, _("Invalid userType value: %d"),
2788 *(user->userType));
2789 err = IE_ENUM;
2790 goto leave;
2792 INSERT_STRING(db_user_info, "userType", type_string);
2795 INSERT_LONGINT(db_user_info, "userPrivils", user->userPrivils, string);
2796 CHECK_FOR_STRING_LENGTH(user->ic, 0, 8, "ic")
2797 INSERT_STRING(db_user_info, "ic", user->ic);
2798 CHECK_FOR_STRING_LENGTH(user->firmName, 0, 100, "firmName")
2799 INSERT_STRING(db_user_info, "firmName", user->firmName);
2800 INSERT_STRING(db_user_info, "caStreet", user->caStreet);
2801 INSERT_STRING(db_user_info, "caCity", user->caCity);
2802 INSERT_STRING(db_user_info, "caZipCode", user->caZipCode);
2803 INSERT_STRING(db_user_info, "caState", user->caState);
2805 leave:
2806 free(string);
2807 return err;
2811 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
2812 * isds_envelope structure. The envelope is automatically allocated but not
2813 * reallocated. The date are just appended into envelope structure.
2814 * @context is ISDS context
2815 * @envelope is automically allocated message envelope structure
2816 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
2817 * In case of error @envelope will be freed. */
2818 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
2819 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2820 isds_error err = IE_SUCCESS;
2821 xmlXPathObjectPtr result = NULL;
2823 if (!context) return IE_INVALID_CONTEXT;
2824 if (!envelope) return IE_INVAL;
2825 if (!xpath_ctx) return IE_INVAL;
2828 if (!*envelope) {
2829 /* Allocate envelope */
2830 *envelope = calloc(1, sizeof(**envelope));
2831 if (!*envelope) {
2832 err = IE_NOMEM;
2833 goto leave;
2835 } else {
2836 /* Else free former data */
2837 zfree((*envelope)->dmSenderOrgUnit);
2838 zfree((*envelope)->dmSenderOrgUnitNum);
2839 zfree((*envelope)->dbIDRecipient);
2840 zfree((*envelope)->dmRecipientOrgUnit);
2841 zfree((*envelope)->dmSenderOrgUnitNum);
2842 zfree((*envelope)->dmToHands);
2843 zfree((*envelope)->dmAnnotation);
2844 zfree((*envelope)->dmRecipientRefNumber);
2845 zfree((*envelope)->dmSenderRefNumber);
2846 zfree((*envelope)->dmRecipientIdent);
2847 zfree((*envelope)->dmSenderIdent);
2848 zfree((*envelope)->dmLegalTitleLaw);
2849 zfree((*envelope)->dmLegalTitleYear);
2850 zfree((*envelope)->dmLegalTitleSect);
2851 zfree((*envelope)->dmLegalTitlePar);
2852 zfree((*envelope)->dmLegalTitlePoint);
2853 zfree((*envelope)->dmPersonalDelivery);
2854 zfree((*envelope)->dmAllowSubstDelivery);
2857 /* Extract envelope elements added by sender or ISDS
2858 * (XSD: gMessageEnvelopeSub type) */
2859 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
2860 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
2861 (*envelope)->dmSenderOrgUnitNum, 0);
2862 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
2863 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
2864 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
2865 (*envelope)->dmSenderOrgUnitNum, 0);
2866 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
2867 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
2868 EXTRACT_STRING("isds:dmRecipientRefNumber",
2869 (*envelope)->dmRecipientRefNumber);
2870 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
2871 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
2872 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
2874 /* Extract envelope elements regarding law refference */
2875 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
2876 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
2877 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
2878 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
2879 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
2881 /* Extract envelope other elements */
2882 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
2883 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
2884 (*envelope)->dmAllowSubstDelivery);
2886 leave:
2887 if (err) isds_envelope_free(envelope);
2888 xmlXPathFreeObject(result);
2889 return err;
2894 /* Convert XSD gMessageEnvelope group of elements from XML tree into
2895 * isds_envelope structure. The envelope is automatically allocated but not
2896 * reallocated. The date are just appended into envelope structure.
2897 * @context is ISDS context
2898 * @envelope is automically allocated message envelope structure
2899 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
2900 * In case of error @envelope will be freed. */
2901 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
2902 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2903 isds_error err = IE_SUCCESS;
2904 xmlXPathObjectPtr result = NULL;
2906 if (!context) return IE_INVALID_CONTEXT;
2907 if (!envelope) return IE_INVAL;
2908 if (!xpath_ctx) return IE_INVAL;
2911 if (!*envelope) {
2912 /* Allocate envelope */
2913 *envelope = calloc(1, sizeof(**envelope));
2914 if (!*envelope) {
2915 err = IE_NOMEM;
2916 goto leave;
2918 } else {
2919 /* Else free former data */
2920 zfree((*envelope)->dmID);
2921 zfree((*envelope)->dbIDSender);
2922 zfree((*envelope)->dmSender);
2923 zfree((*envelope)->dmSenderAddress);
2924 zfree((*envelope)->dmSenderType);
2925 zfree((*envelope)->dmRecipient);
2926 zfree((*envelope)->dmRecipientAddress);
2927 zfree((*envelope)->dmAmbiguousRecipient);
2930 /* Extract envelope elements added by ISDS
2931 * (XSD: gMessageEnvelope type) */
2932 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
2933 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
2934 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
2935 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
2936 /* XML Schema does not guaratee enumratation. It's plain xs:int. */
2937 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
2938 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
2939 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
2940 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
2941 (*envelope)->dmAmbiguousRecipient);
2943 /* Extract envelope elements added by sender and ISDS
2944 * (XSD: gMessageEnvelope type) */
2945 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
2946 if (err) goto leave;
2948 leave:
2949 if (err) isds_envelope_free(envelope);
2950 xmlXPathFreeObject(result);
2951 return err;
2955 /* Convert other envelope elements from XML tree into isds_envelope structure:
2956 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
2957 * The envelope is automatically allocated but not reallocated.
2958 * The data are just appended into envelope structure.
2959 * @context is ISDS context
2960 * @envelope is automically allocated message envelope structure
2961 * @xpath_ctx is XPath context with current node as parent desired elements
2962 * In case of error @envelope will be freed. */
2963 static isds_error append_status_size_times(struct isds_ctx *context,
2964 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
2965 isds_error err = IE_SUCCESS;
2966 xmlXPathObjectPtr result = NULL;
2967 char *string = NULL;
2968 unsigned long int *unumber = NULL;
2970 if (!context) return IE_INVALID_CONTEXT;
2971 if (!envelope) return IE_INVAL;
2972 if (!xpath_ctx) return IE_INVAL;
2975 if (!*envelope) {
2976 /* Allocate new */
2977 *envelope = calloc(1, sizeof(**envelope));
2978 if (!*envelope) {
2979 err = IE_NOMEM;
2980 goto leave;
2982 } else {
2983 /* Free old data */
2984 zfree((*envelope)->dmMessageStatus);
2985 zfree((*envelope)->dmAttachmentSize);
2986 zfree((*envelope)->dmDeliveryTime);
2987 zfree((*envelope)->dmAcceptanceTime);
2991 /* dmMessageStatus element is mandatory */
2992 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
2993 if (!unumber) {
2994 isds_log_message(context,
2995 _("Missing mandatory sisds:dmMessageStatus integer"));
2996 err = IE_ISDS;
2997 goto leave;
2999 err = uint2isds_message_status(context, unumber,
3000 &((*envelope)->dmMessageStatus));
3001 if (err) {
3002 if (err == IE_ENUM) err = IE_ISDS;
3003 goto leave;
3005 free(unumber); unumber = NULL;
3007 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
3010 EXTRACT_STRING("sisds:dmDeliveryTime", string);
3011 if (string) {
3012 err = timestring2timeval((xmlChar *) string,
3013 &((*envelope)->dmDeliveryTime));
3014 if (err) {
3015 char *string_locale = _isds_utf82locale(string);
3016 if (err == IE_DATE) err = IE_ISDS;
3017 isds_printf_message(context,
3018 _("Could not convert dmDeliveryTime as ISO time: %s"),
3019 string_locale);
3020 free(string_locale);
3021 goto leave;
3023 zfree(string);
3026 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
3027 if (string) {
3028 err = timestring2timeval((xmlChar *) string,
3029 &((*envelope)->dmAcceptanceTime));
3030 if (err) {
3031 char *string_locale = _isds_utf82locale(string);
3032 if (err == IE_DATE) err = IE_ISDS;
3033 isds_printf_message(context,
3034 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3035 string_locale);
3036 free(string_locale);
3037 goto leave;
3039 zfree(string);
3042 leave:
3043 if (err) isds_envelope_free(envelope);
3044 free(unumber);
3045 free(string);
3046 xmlXPathFreeObject(result);
3047 return err;
3051 /* Convert message type attribute of current element into isds_envelope
3052 * structure.
3053 * TODO: This function can be incorporated into append_status_size_times() as
3054 * they are called always together.
3055 * The envelope is automatically allocated but not reallocated.
3056 * The data are just appended into envelope structure.
3057 * @context is ISDS context
3058 * @envelope is automically allocated message envelope structure
3059 * @xpath_ctx is XPath context with current node as parent of attribute
3060 * carrying message type
3061 * In case of error @envelope will be freed. */
3062 static isds_error append_message_type(struct isds_ctx *context,
3063 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3064 isds_error err = IE_SUCCESS;
3066 if (!context) return IE_INVALID_CONTEXT;
3067 if (!envelope) return IE_INVAL;
3068 if (!xpath_ctx) return IE_INVAL;
3071 if (!*envelope) {
3072 /* Allocate new */
3073 *envelope = calloc(1, sizeof(**envelope));
3074 if (!*envelope) {
3075 err = IE_NOMEM;
3076 goto leave;
3078 } else {
3079 /* Free old data */
3080 zfree((*envelope)->dmType);
3084 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope)->dmType, 0);
3086 if (!(*envelope)->dmType) {
3087 /* Use default value */
3088 (*envelope)->dmType = strdup("V");
3089 if (!(*envelope)->dmType) {
3090 err = IE_NOMEM;
3091 goto leave;
3093 } else if (1 != xmlUTF8Strlen((xmlChar *) (*envelope)->dmType)) {
3094 char *type_locale = _isds_utf82locale((*envelope)->dmType);
3095 isds_printf_message(context,
3096 _("Message type in dmType attribute is not 1 character long: "
3097 "%s"),
3098 type_locale);
3099 free(type_locale);
3100 err = IE_ISDS;
3101 goto leave;
3104 leave:
3105 if (err) isds_envelope_free(envelope);
3106 return err;
3111 /* Extract message document into reallocated document structure
3112 * @context is ISDS context
3113 * @document is automically reallocated message documents structure
3114 * @xpath_ctx is XPath context with current node as isds:dmFile
3115 * In case of error @document will be freed. */
3116 static isds_error extract_document(struct isds_ctx *context,
3117 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
3118 isds_error err = IE_SUCCESS;
3119 xmlXPathObjectPtr result = NULL;
3120 xmlNodePtr file_node = xpath_ctx->node;
3121 char *string = NULL;
3123 if (!context) return IE_INVALID_CONTEXT;
3124 if (!document) return IE_INVAL;
3125 isds_document_free(document);
3126 if (!xpath_ctx) return IE_INVAL;
3128 *document = calloc(1, sizeof(**document));
3129 if (!*document) {
3130 err = IE_NOMEM;
3131 goto leave;
3134 /* Extract document metadata */
3135 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
3136 if (context->normalize_mime_type) {
3137 char *normalized_type =
3138 isds_normalize_mime_type((*document)->dmMimeType);
3139 if (normalized_type && normalized_type != (*document)->dmMimeType) {
3140 char *new_type = strdup(normalized_type);
3141 if (!new_type) {
3142 isds_printf_message(context,
3143 _("No enough memory to normalize document MIME type"));
3144 err = IE_NOMEM;
3145 goto leave;
3147 free((*document)->dmMimeType);
3148 (*document)->dmMimeType = new_type;
3152 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
3153 err = string2isds_FileMetaType((xmlChar*)string,
3154 &((*document)->dmFileMetaType));
3155 if (err) {
3156 char *meta_type_locale = _isds_utf82locale(string);
3157 isds_printf_message(context,
3158 _("Document has invalid dmFileMetaType attribute value: %s"),
3159 meta_type_locale);
3160 free(meta_type_locale);
3161 err = IE_ISDS;
3162 goto leave;
3164 zfree(string);
3166 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
3167 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
3168 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
3169 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
3172 /* Extract document data.
3173 * Base64 encoded blob or XML subtree must be presented. */
3175 /* Check from dmEncodedContent */
3176 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
3177 xpath_ctx);
3178 if (!result) {
3179 err = IE_XML;
3180 goto leave;
3183 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3184 /* Here we have Base64 blob */
3186 if (result->nodesetval->nodeNr > 1) {
3187 isds_printf_message(context,
3188 _("Document has more dmEncodedContent elements"));
3189 err = IE_ISDS;
3190 goto leave;
3193 xmlXPathFreeObject(result); result = NULL;
3194 EXTRACT_STRING("isds:dmEncodedContent", string);
3196 /* Decode non-emptys document */
3197 if (string && string[0] != '\0') {
3198 (*document)->data_length =
3199 _isds_b64decode(string, &((*document)->data));
3200 if ((*document)->data_length == (size_t) -1) {
3201 isds_printf_message(context,
3202 _("Error while Base64-decoding document content"));
3203 err = IE_ERROR;
3204 goto leave;
3207 } else {
3208 /* No Base64 blob, try XML document */
3209 xmlXPathFreeObject(result); result = NULL;
3210 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
3211 xpath_ctx);
3212 if (!result) {
3213 err = IE_XML;
3214 goto leave;
3217 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3218 /* Here we have XML document */
3220 if (result->nodesetval->nodeNr > 1) {
3221 isds_printf_message(context,
3222 _("Document has more dmXMLContent elements"));
3223 err = IE_ISDS;
3224 goto leave;
3227 /* FIXME: Serialize the tree rooted at result's node */
3228 isds_printf_message(context,
3229 _("XML documents not yet supported"));
3230 err = IE_NOTSUP;
3231 goto leave;
3232 } else {
3233 /* No bas64 blob, nor XML document */
3234 isds_printf_message(context,
3235 _("Document has no dmEncodedContent, nor dmXMLContent "
3236 "element"));
3237 err = IE_ISDS;
3238 goto leave;
3243 leave:
3244 if (err) isds_document_free(document);
3245 free(string);
3246 xmlXPathFreeObject(result);
3247 xpath_ctx->node = file_node;
3248 return err;
3253 /* Extract message documents into reallocated list of documents
3254 * @context is ISDS context
3255 * @documents is automically reallocated message documents list structure
3256 * @xpath_ctx is XPath context with current node as XSD tFilesArray
3257 * In case of error @documents will be freed. */
3258 static isds_error extract_documents(struct isds_ctx *context,
3259 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
3260 isds_error err = IE_SUCCESS;
3261 xmlXPathObjectPtr result = NULL;
3262 xmlNodePtr files_node = xpath_ctx->node;
3263 struct isds_list *document, *prev_document;
3265 if (!context) return IE_INVALID_CONTEXT;
3266 if (!documents) return IE_INVAL;
3267 isds_list_free(documents);
3268 if (!xpath_ctx) return IE_INVAL;
3270 /* Find documents */
3271 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
3272 if (!result) {
3273 err = IE_XML;
3274 goto leave;
3277 /* No match */
3278 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3279 isds_printf_message(context,
3280 _("Message does not contain any document"));
3281 err = IE_ISDS;
3282 goto leave;
3286 /* Iterate over documents */
3287 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
3289 /* Allocate and append list item */
3290 document = calloc(1, sizeof(*document));
3291 if (!document) {
3292 err = IE_NOMEM;
3293 goto leave;
3295 document->destructor = (void (*)(void **))isds_document_free;
3296 if (i == 0) *documents = document;
3297 else prev_document->next = document;
3298 prev_document = document;
3300 /* Extract document */
3301 xpath_ctx->node = result->nodesetval->nodeTab[i];
3302 err = extract_document(context,
3303 (struct isds_document **) &(document->data), xpath_ctx);
3304 if (err) goto leave;
3308 leave:
3309 if (err) isds_list_free(documents);
3310 xmlXPathFreeObject(result);
3311 xpath_ctx->node = files_node;
3312 return err;
3316 /* Convert isds:dmRecord XML tree into structure
3317 * @context is ISDS context
3318 * @envelope is automically reallocated message envelope structure
3319 * @xpath_ctx is XPath context with current node as isds:dmRecord element
3320 * In case of error @envelope will be freed. */
3321 static isds_error extract_DmRecord(struct isds_ctx *context,
3322 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3323 isds_error err = IE_SUCCESS;
3324 xmlXPathObjectPtr result = NULL;
3326 if (!context) return IE_INVALID_CONTEXT;
3327 if (!envelope) return IE_INVAL;
3328 isds_envelope_free(envelope);
3329 if (!xpath_ctx) return IE_INVAL;
3332 *envelope = calloc(1, sizeof(**envelope));
3333 if (!*envelope) {
3334 err = IE_NOMEM;
3335 goto leave;
3339 /* Extract tRecord data */
3340 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
3342 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
3343 * dmAcceptanceTime. */
3344 err = append_status_size_times(context, envelope, xpath_ctx);
3345 if (err) goto leave;
3347 /* Extract envelope elements added by sender and ISDS
3348 * (XSD: gMessageEnvelope type) */
3349 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
3350 if (err) goto leave;
3351 /* dmOVM can not be obtained from ISDS */
3353 /* Get message type */
3354 err = append_message_type(context, envelope, xpath_ctx);
3355 if (err) goto leave;
3358 leave:
3359 if (err) isds_envelope_free(envelope);
3360 xmlXPathFreeObject(result);
3361 return err;
3365 /* Find and convert isds:dmHash XML tree into structure
3366 * @context is ISDS context
3367 * @envelope is automically reallocated message hash structure
3368 * @xpath_ctx is XPath context with current node containing isds:dmHash child
3369 * In case of error @hash will be freed. */
3370 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
3371 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
3372 isds_error err = IE_SUCCESS;
3373 xmlNodePtr old_ctx_node;
3374 xmlXPathObjectPtr result = NULL;
3375 char *string = NULL;
3377 if (!context) return IE_INVALID_CONTEXT;
3378 if (!hash) return IE_INVAL;
3379 isds_hash_free(hash);
3380 if (!xpath_ctx) return IE_INVAL;
3382 old_ctx_node = xpath_ctx->node;
3384 *hash = calloc(1, sizeof(**hash));
3385 if (!*hash) {
3386 err = IE_NOMEM;
3387 goto leave;
3390 /* Locate dmHash */
3391 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
3392 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
3393 err = IE_ISDS;
3394 goto leave;
3396 if (err) {
3397 err = IE_ERROR;
3398 goto leave;
3401 /* Get hash algorithm */
3402 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
3403 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
3404 if (err) {
3405 if (err == IE_ENUM) {
3406 char *string_locale = _isds_utf82locale(string);
3407 isds_printf_message(context, _("Unsported hash algorithm: %s"),
3408 string_locale);
3409 free(string_locale);
3411 goto leave;
3413 zfree(string);
3415 /* Get hash value */
3416 EXTRACT_STRING(".", string);
3417 if (!string) {
3418 isds_printf_message(context,
3419 _("sisds:dmHash element is missing hash value"));
3420 err = IE_ISDS;
3421 goto leave;
3423 (*hash)->length = _isds_b64decode(string, &((*hash)->value));
3424 if ((*hash)->length == (size_t) -1) {
3425 isds_printf_message(context,
3426 _("Error while Base64-decoding hash value"));
3427 err = IE_ERROR;
3428 goto leave;
3431 leave:
3432 if (err) isds_hash_free(hash);
3433 free(string);
3434 xmlXPathFreeObject(result);
3435 xpath_ctx->node = old_ctx_node;
3436 return err;
3440 /* Find and append isds:dmQTimestamp XML tree into envelope
3441 * @context is ISDS context
3442 * @envelope is automically allocated evnelope structure
3443 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
3444 * child
3445 * In case of error @envelope will be freed. */
3446 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
3447 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3448 isds_error err = IE_SUCCESS;
3449 xmlXPathObjectPtr result = NULL;
3450 char *string = NULL;
3452 if (!context) return IE_INVALID_CONTEXT;
3453 if (!envelope) return IE_INVAL;
3454 if (!xpath_ctx) {
3455 isds_envelope_free(envelope);
3456 return IE_INVAL;
3459 if (!*envelope) {
3460 *envelope = calloc(1, sizeof(**envelope));
3461 if (!*envelope) {
3462 err = IE_NOMEM;
3463 goto leave;
3465 } else {
3466 zfree((*envelope)->timestamp);
3467 (*envelope)->timestamp_length = 0;
3470 /* Get dmQTimestamp */
3471 EXTRACT_STRING("sisds:dmQTimestamp", string);
3472 if (!string) {
3473 isds_printf_message(context, _("Missing dmQTimestamp element content"));
3474 err = IE_ISDS;
3475 goto leave;
3477 (*envelope)->timestamp_length =
3478 _isds_b64decode(string, &((*envelope)->timestamp));
3479 if ((*envelope)->timestamp_length == (size_t) -1) {
3480 isds_printf_message(context,
3481 _("Error while Base64-decoding timestamp value"));
3482 err = IE_ERROR;
3483 goto leave;
3486 leave:
3487 if (err) isds_envelope_free(envelope);
3488 free(string);
3489 xmlXPathFreeObject(result);
3490 return err;
3494 /* Convert XSD tReturnedMessage XML tree into message structure.
3495 * It does not store serialized XML tree into message->raw.
3496 * It does store (pointer to) parsed XML tree into message->xml if needed.
3497 * @context is ISDS context
3498 * @include_documents Use true if documents must be extracted
3499 * (tReturnedMessage XSD type), use false if documents shall be ommited
3500 * (tReturnedMessageEnvelope).
3501 * @message is automically reallocated message structure
3502 * @xpath_ctx is XPath context with current node as tReturnedMessage element
3503 * type
3504 * In case of error @message will be freed. */
3505 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
3506 const _Bool include_documents, struct isds_message **message,
3507 xmlXPathContextPtr xpath_ctx) {
3508 isds_error err = IE_SUCCESS;
3509 xmlNodePtr message_node;
3511 if (!context) return IE_INVALID_CONTEXT;
3512 if (!message) return IE_INVAL;
3513 isds_message_free(message);
3514 if (!xpath_ctx) return IE_INVAL;
3517 *message = calloc(1, sizeof(**message));
3518 if (!*message) {
3519 err = IE_NOMEM;
3520 goto leave;
3523 /* Save message XPATH context node */
3524 message_node = xpath_ctx->node;
3527 /* Extract dmDM */
3528 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
3529 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
3530 if (err) { err = IE_ERROR; goto leave; }
3531 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
3532 if (err) goto leave;
3534 if (include_documents) {
3535 /* Extract dmFiles */
3536 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
3537 xpath_ctx);
3538 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
3539 err = IE_ISDS; goto leave;
3541 if (err) { err = IE_ERROR; goto leave; }
3542 err = extract_documents(context, &((*message)->documents), xpath_ctx);
3543 if (err) goto leave;
3544 /* Store xmlDoc of this message if needed */
3545 /* FIXME: Only if needed */
3546 (*message)->xml = xpath_ctx->doc;
3550 /* Restore context to message */
3551 xpath_ctx->node = message_node;
3553 /* Extract dmHash */
3554 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
3555 xpath_ctx);
3556 if (err) goto leave;
3558 /* Extract dmQTimestamp, */
3559 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
3560 xpath_ctx);
3561 if (err) goto leave;
3563 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
3564 * dmAcceptanceTime. */
3565 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
3566 if (err) goto leave;
3568 /* Get message type */
3569 err = append_message_type(context, &((*message)->envelope), xpath_ctx);
3570 if (err) goto leave;
3572 leave:
3573 if (err) isds_message_free(message);
3574 return err;
3578 /* Extract message event into reallocated isds_event structure
3579 * @context is ISDS context
3580 * @event is automically reallocated message event structure
3581 * @xpath_ctx is XPath context with current node as isds:dmEvent
3582 * In case of error @event will be freed. */
3583 static isds_error extract_event(struct isds_ctx *context,
3584 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
3585 isds_error err = IE_SUCCESS;
3586 xmlXPathObjectPtr result = NULL;
3587 xmlNodePtr event_node = xpath_ctx->node;
3588 char *string = NULL;
3590 if (!context) return IE_INVALID_CONTEXT;
3591 if (!event) return IE_INVAL;
3592 isds_event_free(event);
3593 if (!xpath_ctx) return IE_INVAL;
3595 *event = calloc(1, sizeof(**event));
3596 if (!*event) {
3597 err = IE_NOMEM;
3598 goto leave;
3601 /* Extract event data.
3602 * All elements are optional according XSD. That's funny. */
3603 EXTRACT_STRING("sisds:dmEventTime", string);
3604 if (string) {
3605 err = timestring2timeval((xmlChar *) string, &((*event)->time));
3606 if (err) {
3607 char *string_locale = _isds_utf82locale(string);
3608 if (err == IE_DATE) err = IE_ISDS;
3609 isds_printf_message(context,
3610 _("Could not convert dmEventTime as ISO time: %s"),
3611 string_locale);
3612 free(string_locale);
3613 goto leave;
3615 zfree(string);
3618 /* dmEventDescr element has prefix and the rest */
3619 EXTRACT_STRING("sisds:dmEventDescr", string);
3620 if (string) {
3621 err = eventstring2event((xmlChar *) string, *event);
3622 if (err) goto leave;
3623 zfree(string);
3626 leave:
3627 if (err) isds_event_free(event);
3628 free(string);
3629 xmlXPathFreeObject(result);
3630 xpath_ctx->node = event_node;
3631 return err;
3635 /* Convert element of XSD tEventsArray type from XML tree into
3636 * isds_list of isds_event's structure. The list is automatically reallocated.
3637 * @context is ISDS context
3638 * @events is automically reallocated list of event structures
3639 * @xpath_ctx is XPath context with current node as tEventsArray
3640 * In case of error @evnets will be freed. */
3641 static isds_error extract_events(struct isds_ctx *context,
3642 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
3643 isds_error err = IE_SUCCESS;
3644 xmlXPathObjectPtr result = NULL;
3645 xmlNodePtr events_node = xpath_ctx->node;
3646 struct isds_list *event, *prev_event = NULL;
3648 if (!context) return IE_INVALID_CONTEXT;
3649 if (!events) return IE_INVAL;
3650 if (!xpath_ctx) return IE_INVAL;
3652 /* Free old list */
3653 isds_list_free(events);
3655 /* Find events */
3656 result = xmlXPathEvalExpression(BAD_CAST "sisds:dmEvent", xpath_ctx);
3657 if (!result) {
3658 err = IE_XML;
3659 goto leave;
3662 /* No match */
3663 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3664 isds_printf_message(context,
3665 _("Delivery info does not contain any event"));
3666 err = IE_ISDS;
3667 goto leave;
3671 /* Iterate over events */
3672 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
3674 /* Allocate and append list item */
3675 event = calloc(1, sizeof(*event));
3676 if (!event) {
3677 err = IE_NOMEM;
3678 goto leave;
3680 event->destructor = (void (*)(void **))isds_event_free;
3681 if (i == 0) *events = event;
3682 else prev_event->next = event;
3683 prev_event = event;
3685 /* Extract event */
3686 xpath_ctx->node = result->nodesetval->nodeTab[i];
3687 err = extract_event(context,
3688 (struct isds_event **) &(event->data), xpath_ctx);
3689 if (err) goto leave;
3693 leave:
3694 if (err) isds_list_free(events);
3695 xmlXPathFreeObject(result);
3696 xpath_ctx->node = events_node;
3697 return err;
3701 /* Convert isds_document structure into XML tree and append to dmFiles node.
3702 * @context is session context
3703 * @document is ISDS document
3704 * @dm_files is XML element the resulting tree will be appended to as a child.
3705 * @return error code, in case of error context' message is filled. */
3706 static isds_error insert_document(struct isds_ctx *context,
3707 struct isds_document *document, xmlNodePtr dm_files) {
3708 isds_error err = IE_SUCCESS;
3709 xmlNodePtr new_file = NULL, file = NULL, node;
3710 xmlAttrPtr attribute_node;
3711 xmlChar *base64data = NULL;
3713 if (!context) return IE_INVALID_CONTEXT;
3714 if (!document || !dm_files) return IE_INVAL;
3716 /* Allocate new dmFile */
3717 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
3718 if (!new_file) {
3719 isds_printf_message(context, _("Could not allocate main dmFile"));
3720 err = IE_ERROR;
3721 goto leave;
3723 /* Append the new dmFile.
3724 * XXX: Main document must go first */
3725 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
3726 file = xmlAddPrevSibling(dm_files->children, new_file);
3727 else
3728 file = xmlAddChild(dm_files, new_file);
3730 if (!file) {
3731 xmlFreeNode(new_file); new_file = NULL;
3732 isds_printf_message(context, _("Could not add dmFile child to "
3733 "%s element"), dm_files->name);
3734 err = IE_ERROR;
3735 goto leave;
3738 /* @dmMimeType is required */
3739 if (!document->dmMimeType) {
3740 isds_log_message(context,
3741 _("Document is missing mandatory MIME type definition"));
3742 err = IE_INVAL;
3743 goto leave;
3745 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
3747 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
3748 if (!string) {
3749 isds_printf_message(context,
3750 _("Document has unknown dmFileMetaType: %ld"),
3751 document->dmFileMetaType);
3752 err = IE_ENUM;
3753 goto leave;
3755 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
3757 if (document->dmFileGuid) {
3758 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
3760 if (document->dmUpFileGuid) {
3761 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
3764 /* @dmFileDescr is required */
3765 if (!document->dmFileDescr) {
3766 isds_log_message(context,
3767 _("Document is missing mandatory description (title)"));
3768 err = IE_INVAL;
3769 goto leave;
3771 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
3773 if (document->dmFormat) {
3774 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
3778 /* Insert content (data) of the document. */
3779 /* XXX; Only base64 is implemented currently. */
3780 base64data = (xmlChar *) _isds_b64encode(document->data,
3781 document->data_length);
3782 if (!base64data) {
3783 isds_printf_message(context,
3784 ngettext("Not enought memory to encode %zd bytes into Base64",
3785 "Not enought memory to encode %zd bytes into Base64",
3786 document->data_length),
3787 document->data_length);
3788 err = IE_NOMEM;
3789 goto leave;
3791 INSERT_STRING(file, "dmEncodedContent", base64data);
3792 free(base64data);
3794 leave:
3795 return err;
3799 /* Append XSD tMStatus XML tree into isds_message_copy structure.
3800 * The copy must pre prealocated, the date are just appended into structure.
3801 * @context is ISDS context
3802 * @copy is message copy struture
3803 * @xpath_ctx is XPath context with current node as tMStatus */
3804 static isds_error append_TMStatus(struct isds_ctx *context,
3805 struct isds_message_copy *copy, xmlXPathContextPtr xpath_ctx) {
3806 isds_error err = IE_SUCCESS;
3807 xmlXPathObjectPtr result = NULL;
3808 char *code = NULL, *message = NULL;
3810 if (!context) return IE_INVALID_CONTEXT;
3811 if (!copy || !xpath_ctx) return IE_INVAL;
3813 /* Free old values */
3814 zfree(copy->dmStatus);
3815 zfree(copy->dmID);
3817 /* Get error specific to this copy */
3818 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code);
3819 if (!code) {
3820 isds_log_message(context,
3821 _("Missing isds:dmStatusCode under "
3822 "XSD:tMStatus type element"));
3823 err = IE_ISDS;
3824 goto leave;
3827 if (xmlStrcmp((const xmlChar *)code, BAD_CAST "0000")) {
3828 /* This copy failed */
3829 copy->error = IE_ISDS;
3830 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message);
3831 if (message) {
3832 copy->dmStatus = _isds_astrcat3(code, ": ", message);
3833 if (!copy->dmStatus) {
3834 copy->dmStatus = code;
3835 code = NULL;
3837 } else {
3838 copy->dmStatus = code;
3839 code = NULL;
3841 } else {
3842 /* This copy succeeded. In this case only, message ID is valid */
3843 copy->error = IE_SUCCESS;
3845 EXTRACT_STRING("isds:dmID", copy->dmID);
3846 if (!copy->dmID) {
3847 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
3848 "but did not returned assigned message ID\n"));
3849 err = IE_ISDS;
3853 leave:
3854 free(code);
3855 free(message);
3856 xmlXPathFreeObject(result);
3857 return err;
3861 /* Insert struct isds_approval data (box approval) into XML tree
3862 * @context is sesstion context
3863 * @approval is libsids structure with approval description. NULL is
3864 * acceptible.
3865 * @parent is XML element to append @approval to */
3866 static isds_error insert_GExtApproval(struct isds_ctx *context,
3867 const struct isds_approval *approval, xmlNodePtr parent) {
3869 isds_error err = IE_SUCCESS;
3870 xmlNodePtr node;
3872 if (!context) return IE_INVALID_CONTEXT;
3873 if (!parent) return IE_INVAL;
3875 if (!approval) return IE_SUCCESS;
3877 /* Build XSD:gExtApproval */
3878 INSERT_SCALAR_BOOLEAN(parent, "dbApproved", approval->approved);
3879 INSERT_STRING(parent, "dbExternRefNumber", approval->refference);
3881 leave:
3882 return err;
3886 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
3887 * code
3888 * @context is session context
3889 * @service_name is name of SERVICE_DB_ACCESS
3890 * @response is server SOAP body response as XML document
3891 * @raw_response is automatically reallocated bitstream with response body. Use
3892 * NULL if you don't care
3893 * @raw_response_length is size of @raw_response in bytes
3894 * @code is ISDS status code
3895 * @status_message is ISDS status message
3896 * @return error coded from lower layer, context message will be set up
3897 * appropriately. */
3898 static isds_error build_send_check_dbdummy_request(struct isds_ctx *context,
3899 const xmlChar *service_name,
3900 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
3901 xmlChar **code, xmlChar **status_message) {
3903 isds_error err = IE_SUCCESS;
3904 char *service_name_locale = NULL;
3905 xmlNodePtr request = NULL, node;
3906 xmlNsPtr isds_ns = NULL;
3908 if (!context) return IE_INVALID_CONTEXT;
3909 if (!service_name) return IE_INVAL;
3910 if (!response || !code || !status_message) return IE_INVAL;
3911 if (!raw_response_length && raw_response) return IE_INVAL;
3913 /* Free output argument */
3914 xmlFreeDoc(*response); *response = NULL;
3915 if (raw_response) zfree(*raw_response);
3916 free(*code);
3917 free(*status_message);
3920 /* Check if connection is established
3921 * TODO: This check should be done donwstairs. */
3922 if (!context->curl) return IE_CONNECTION_CLOSED;
3924 service_name_locale = _isds_utf82locale((char*)service_name);
3925 if (!service_name_locale) {
3926 err = IE_NOMEM;
3927 goto leave;
3930 /* Build request */
3931 request = xmlNewNode(NULL, service_name);
3932 if (!request) {
3933 isds_printf_message(context,
3934 _("Could not build %s request"), service_name_locale);
3935 err = IE_ERROR;
3936 goto leave;
3938 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
3939 if(!isds_ns) {
3940 isds_log_message(context, _("Could not create ISDS name space"));
3941 err = IE_ERROR;
3942 goto leave;
3944 xmlSetNs(request, isds_ns);
3947 /* Add XSD:tDummyInput child */
3948 INSERT_STRING(request, "dbDummy", NULL);
3951 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
3952 service_name_locale);
3954 /* Send request */
3955 err = isds(context, SERVICE_DB_ACCESS, request, response,
3956 raw_response, raw_response_length);
3957 xmlFreeNode(request); request = NULL;
3959 if (err) {
3960 isds_log(ILF_ISDS, ILL_DEBUG,
3961 _("Processing ISDS response on %s request failed\n"),
3962 service_name_locale);
3963 goto leave;
3966 /* Check for response status */
3967 err = isds_response_status(context, SERVICE_DB_ACCESS, *response,
3968 code, status_message, NULL);
3969 if (err) {
3970 isds_log(ILF_ISDS, ILL_DEBUG,
3971 _("ISDS response on %s request is missing status\n"),
3972 service_name_locale);
3973 goto leave;
3976 /* Request processed, but nothing found */
3977 if (xmlStrcmp(*code, BAD_CAST "0000")) {
3978 char *code_locale = _isds_utf82locale((char*) *code);
3979 char *status_message_locale =
3980 _isds_utf82locale((char*) *status_message);
3981 isds_log(ILF_ISDS, ILL_DEBUG,
3982 _("Server refused %s request (code=%s, message=%s)\n"),
3983 service_name_locale, code_locale, status_message_locale);
3984 isds_log_message(context, status_message_locale);
3985 free(code_locale);
3986 free(status_message_locale);
3987 err = IE_ISDS;
3988 goto leave;
3991 leave:
3992 free(service_name_locale);
3993 xmlFreeNode(request);
3994 return err;
3998 /* Get data about logged in user and his box. */
3999 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
4000 struct isds_DbOwnerInfo **db_owner_info) {
4001 isds_error err = IE_SUCCESS;
4002 xmlDocPtr response = NULL;
4003 xmlChar *code = NULL, *message = NULL;
4004 xmlXPathContextPtr xpath_ctx = NULL;
4005 xmlXPathObjectPtr result = NULL;
4006 char *string = NULL;
4008 if (!context) return IE_INVALID_CONTEXT;
4009 zfree(context->long_message);
4010 if (!db_owner_info) return IE_INVAL;
4012 /* Check if connection is established */
4013 if (!context->curl) return IE_CONNECTION_CLOSED;
4016 /* Do request and check for success */
4017 err = build_send_check_dbdummy_request(context,
4018 BAD_CAST "GetOwnerInfoFromLogin",
4019 &response, NULL, NULL, &code, &message);
4020 if (err) goto leave;
4023 /* Extract data */
4024 /* Prepare stucture */
4025 isds_DbOwnerInfo_free(db_owner_info);
4026 *db_owner_info = calloc(1, sizeof(**db_owner_info));
4027 if (!*db_owner_info) {
4028 err = IE_NOMEM;
4029 goto leave;
4031 xpath_ctx = xmlXPathNewContext(response);
4032 if (!xpath_ctx) {
4033 err = IE_ERROR;
4034 goto leave;
4036 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4037 err = IE_ERROR;
4038 goto leave;
4041 /* Set context node */
4042 result = xmlXPathEvalExpression(BAD_CAST
4043 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
4044 if (!result) {
4045 err = IE_ERROR;
4046 goto leave;
4048 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4049 isds_log_message(context, _("Missing dbOwnerInfo element"));
4050 err = IE_ISDS;
4051 goto leave;
4053 if (result->nodesetval->nodeNr > 1) {
4054 isds_log_message(context, _("Multiple dbOwnerInfo element"));
4055 err = IE_ISDS;
4056 goto leave;
4058 xpath_ctx->node = result->nodesetval->nodeTab[0];
4059 xmlXPathFreeObject(result); result = NULL;
4061 /* Extract it */
4062 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
4064 leave:
4065 if (err) {
4066 isds_DbOwnerInfo_free(db_owner_info);
4069 free(string);
4070 xmlXPathFreeObject(result);
4071 xmlXPathFreeContext(xpath_ctx);
4073 free(code);
4074 free(message);
4075 xmlFreeDoc(response);
4077 if (!err)
4078 isds_log(ILF_ISDS, ILL_DEBUG,
4079 _("GetOwnerInfoFromLogin request processed by server "
4080 "successfully.\n"));
4082 return err;
4086 /* Get data about logged in user. */
4087 isds_error isds_GetUserInfoFromLogin(struct isds_ctx *context,
4088 struct isds_DbUserInfo **db_user_info) {
4089 isds_error err = IE_SUCCESS;
4090 xmlDocPtr response = NULL;
4091 xmlChar *code = NULL, *message = NULL;
4092 xmlXPathContextPtr xpath_ctx = NULL;
4093 xmlXPathObjectPtr result = NULL;
4095 if (!context) return IE_INVALID_CONTEXT;
4096 zfree(context->long_message);
4097 if (!db_user_info) return IE_INVAL;
4099 /* Check if connection is established */
4100 if (!context->curl) return IE_CONNECTION_CLOSED;
4103 /* Do request and check for success */
4104 err = build_send_check_dbdummy_request(context,
4105 BAD_CAST "GetUserInfoFromLogin",
4106 &response, NULL, NULL, &code, &message);
4107 if (err) goto leave;
4110 /* Extract data */
4111 /* Prepare stucture */
4112 isds_DbUserInfo_free(db_user_info);
4113 *db_user_info = calloc(1, sizeof(**db_user_info));
4114 if (!*db_user_info) {
4115 err = IE_NOMEM;
4116 goto leave;
4118 xpath_ctx = xmlXPathNewContext(response);
4119 if (!xpath_ctx) {
4120 err = IE_ERROR;
4121 goto leave;
4123 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4124 err = IE_ERROR;
4125 goto leave;
4128 /* Set context node */
4129 result = xmlXPathEvalExpression(BAD_CAST
4130 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx);
4131 if (!result) {
4132 err = IE_ERROR;
4133 goto leave;
4135 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4136 isds_log_message(context, _("Missing dbUserInfo element"));
4137 err = IE_ISDS;
4138 goto leave;
4140 if (result->nodesetval->nodeNr > 1) {
4141 isds_log_message(context, _("Multiple dbUserInfo element"));
4142 err = IE_ISDS;
4143 goto leave;
4145 xpath_ctx->node = result->nodesetval->nodeTab[0];
4146 xmlXPathFreeObject(result); result = NULL;
4148 /* Extract it */
4149 err = extract_DbUserInfo(context, db_user_info, xpath_ctx);
4151 leave:
4152 if (err) {
4153 isds_DbUserInfo_free(db_user_info);
4156 xmlXPathFreeObject(result);
4157 xmlXPathFreeContext(xpath_ctx);
4159 free(code);
4160 free(message);
4161 xmlFreeDoc(response);
4163 if (!err)
4164 isds_log(ILF_ISDS, ILL_DEBUG,
4165 _("GetUserInfoFromLogin request processed by server "
4166 "successfully.\n"));
4168 return err;
4172 /* Get expiration time of current password
4173 * @context is session context
4174 * @expiration is automatically reallocated time when password expires, In
4175 * case of error will be nulled. */
4176 isds_error isds_get_password_expiration(struct isds_ctx *context,
4177 struct timeval **expiration) {
4178 isds_error err = IE_SUCCESS;
4179 xmlDocPtr response = NULL;
4180 xmlChar *code = NULL, *message = NULL;
4181 xmlXPathContextPtr xpath_ctx = NULL;
4182 xmlXPathObjectPtr result = NULL;
4183 char *string = NULL;
4185 if (!context) return IE_INVALID_CONTEXT;
4186 zfree(context->long_message);
4187 if (!expiration) return IE_INVAL;
4189 /* Check if connection is established */
4190 if (!context->curl) return IE_CONNECTION_CLOSED;
4193 /* Do request and check for success */
4194 err = build_send_check_dbdummy_request(context,
4195 BAD_CAST "GetPasswordInfo",
4196 &response, NULL, NULL, &code, &message);
4197 if (err) goto leave;
4200 /* Extract data */
4201 xpath_ctx = xmlXPathNewContext(response);
4202 if (!xpath_ctx) {
4203 err = IE_ERROR;
4204 goto leave;
4206 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4207 err = IE_ERROR;
4208 goto leave;
4211 /* Set context node */
4212 result = xmlXPathEvalExpression(BAD_CAST
4213 "/isds:GetPasswordInfoResponse", xpath_ctx);
4214 if (!result) {
4215 err = IE_ERROR;
4216 goto leave;
4218 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4219 isds_log_message(context,
4220 _("Missing GetPasswordInfoResponse element"));
4221 err = IE_ISDS;
4222 goto leave;
4224 if (result->nodesetval->nodeNr > 1) {
4225 isds_log_message(context,
4226 _("Multiple GetPasswordInfoResponse element"));
4227 err = IE_ISDS;
4228 goto leave;
4230 xpath_ctx->node = result->nodesetval->nodeTab[0];
4231 xmlXPathFreeObject(result); result = NULL;
4233 /* Extract expiration date */
4234 EXTRACT_STRING("isds:pswExpDate", string);
4235 if (!string) {
4236 isds_log_message(context, _("Missing pswExpDate element"));
4237 err = IE_ISDS;
4238 goto leave;
4241 err = timestring2timeval((xmlChar *) string, expiration);
4242 if (err) {
4243 char *string_locale = _isds_utf82locale(string);
4244 if (err == IE_DATE) err = IE_ISDS;
4245 isds_printf_message(context,
4246 _("Could not convert pswExpDate as ISO time: %s"),
4247 string_locale);
4248 free(string_locale);
4249 goto leave;
4252 leave:
4253 if (err) {
4254 if (*expiration) {
4255 zfree(*expiration);
4259 free(string);
4260 xmlXPathFreeObject(result);
4261 xmlXPathFreeContext(xpath_ctx);
4263 free(code);
4264 free(message);
4265 xmlFreeDoc(response);
4267 if (!err)
4268 isds_log(ILF_ISDS, ILL_DEBUG,
4269 _("GetPasswordInfo request processed by server "
4270 "successfully.\n"));
4272 return err;
4276 /* Change user password in ISDS.
4277 * User must supply old password, new password will takes effect after some
4278 * time, current session can continue. Password must fulfill some constraints.
4279 * @context is session context
4280 * @old_password is current password.
4281 * @new_password is requested new password */
4282 isds_error isds_change_password(struct isds_ctx *context,
4283 const char *old_password, const char *new_password) {
4284 isds_error err = IE_SUCCESS;
4285 xmlNsPtr isds_ns = NULL;
4286 xmlNodePtr request = NULL, node;
4287 xmlDocPtr response = NULL;
4288 xmlChar *code = NULL, *message = NULL;
4290 if (!context) return IE_INVALID_CONTEXT;
4291 zfree(context->long_message);
4292 if (!old_password || !new_password) return IE_INVAL;
4294 /* Check if connection is established
4295 * TODO: This check should be done donwstairs. */
4296 if (!context->curl) return IE_CONNECTION_CLOSED;
4299 /* Build ChangeISDSPassword request */
4300 request = xmlNewNode(NULL, BAD_CAST "ChangeISDSPassword");
4301 if (!request) {
4302 isds_log_message(context,
4303 _("Could not build ChangeISDSPassword request"));
4304 return IE_ERROR;
4306 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4307 if(!isds_ns) {
4308 isds_log_message(context, _("Could not create ISDS name space"));
4309 xmlFreeNode(request);
4310 return IE_ERROR;
4312 xmlSetNs(request, isds_ns);
4314 INSERT_STRING(request, "dbOldPassword", old_password);
4315 INSERT_STRING(request, "dbNewPassword", new_password);
4318 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CheckDataBox request to ISDS\n"));
4320 /* Sent request */
4321 err = isds(context, SERVICE_DB_ACCESS, request, &response, NULL, NULL);
4323 /* Destroy request */
4324 xmlFreeNode(request); request = NULL;
4326 if (err) {
4327 isds_log(ILF_ISDS, ILL_DEBUG,
4328 _("Processing ISDS response on ChangeISDSPassword "
4329 "request failed\n"));
4330 goto leave;
4333 /* Check for response status */
4334 err = isds_response_status(context, SERVICE_DB_ACCESS, response,
4335 &code, &message, NULL);
4336 if (err) {
4337 isds_log(ILF_ISDS, ILL_DEBUG,
4338 _("ISDS response on ChangeISDSPassword request is missing "
4339 "status\n"));
4340 goto leave;
4343 /* Request processed, but empty password refused */
4344 if (!xmlStrcmp(code, BAD_CAST "1066")) {
4345 char *code_locale = _isds_utf82locale((char*)code);
4346 char *message_locale = _isds_utf82locale((char*)message);
4347 isds_log(ILF_ISDS, ILL_DEBUG,
4348 _("Server refused empty password on ChangeISDSPassword "
4349 "request (code=%s, message=%s)\n"),
4350 code_locale, message_locale);
4351 isds_log_message(context, _("Password must not be empty"));
4352 free(code_locale);
4353 free(message_locale);
4354 err = IE_INVAL;
4355 goto leave;
4358 /* Request processed, but new password was reused */
4359 else if (!xmlStrcmp(code, BAD_CAST "1067")) {
4360 char *code_locale = _isds_utf82locale((char*)code);
4361 char *message_locale = _isds_utf82locale((char*)message);
4362 isds_log(ILF_ISDS, ILL_DEBUG,
4363 _("Server refused the same new password on ChangeISDSPassword "
4364 "request (code=%s, message=%s)\n"),
4365 code_locale, message_locale);
4366 isds_log_message(context,
4367 _("New password must differ from the current one"));
4368 free(code_locale);
4369 free(message_locale);
4370 err = IE_INVAL;
4371 goto leave;
4374 /* Other error */
4375 else if (xmlStrcmp(code, BAD_CAST "0000")) {
4376 char *code_locale = _isds_utf82locale((char*)code);
4377 char *message_locale = _isds_utf82locale((char*)message);
4378 isds_log(ILF_ISDS, ILL_DEBUG,
4379 _("Server refused to change password on ChangeISDSPassword "
4380 "request (code=%s, message=%s)\n"),
4381 code_locale, message_locale);
4382 isds_log_message(context, message_locale);
4383 free(code_locale);
4384 free(message_locale);
4385 err = IE_ISDS;
4386 goto leave;
4389 /* Otherwise password changed successfully */
4391 leave:
4392 free(code);
4393 free(message);
4394 xmlFreeDoc(response);
4395 xmlFreeNode(request);
4397 if (!err)
4398 isds_log(ILF_ISDS, ILL_DEBUG,
4399 _("Password changed successfully on ChangeISDSPassword "
4400 "request.\n"));
4402 return err;
4406 /* Generic middle part with request sending and response check.
4407 * It sends prepared request and checks for error code.
4408 * @context is ISDS session context.
4409 * @service is ISDS service handler
4410 * @service_name is name in scope of given @service
4411 * @request is XML tree with request. Will be freed to save memory.
4412 * @response is XML document ouputing ISDS response.
4413 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4414 * NULL, if you don't care. */
4415 static isds_error send_destroy_request_check_response(
4416 struct isds_ctx *context,
4417 const isds_service service, const xmlChar *service_name,
4418 xmlNodePtr *request, xmlDocPtr *response, xmlChar **refnumber) {
4419 isds_error err = IE_SUCCESS;
4420 char *service_name_locale = NULL;
4421 xmlChar *code = NULL, *message = NULL;
4424 if (!context) return IE_INVALID_CONTEXT;
4425 if (!service_name || *service_name == '\0' || !request || !*request ||
4426 !response)
4427 return IE_INVAL;
4429 /* Check if connection is established
4430 * TODO: This check should be done donwstairs. */
4431 if (!context->curl) return IE_CONNECTION_CLOSED;
4433 service_name_locale = _isds_utf82locale((char*) service_name);
4434 if (!service_name_locale) {
4435 err = IE_NOMEM;
4436 goto leave;
4439 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
4440 service_name_locale);
4442 /* Send request */
4443 err = isds(context, service, *request, response, NULL, NULL);
4444 xmlFreeNode(*request); *request = NULL;
4446 if (err) {
4447 isds_log(ILF_ISDS, ILL_DEBUG,
4448 _("Processing ISDS response on %s request failed\n"),
4449 service_name_locale);
4450 goto leave;
4453 /* Check for response status */
4454 err = isds_response_status(context, service, *response,
4455 &code, &message, refnumber);
4456 if (err) {
4457 isds_log(ILF_ISDS, ILL_DEBUG,
4458 _("ISDS response on %s request is missing status\n"),
4459 service_name_locale);
4460 goto leave;
4463 /* Request processed, but server failed */
4464 if (xmlStrcmp(code, BAD_CAST "0000")) {
4465 char *code_locale = _isds_utf82locale((char*) code);
4466 char *message_locale = _isds_utf82locale((char*) message);
4467 isds_log(ILF_ISDS, ILL_DEBUG,
4468 _("Server refused %s request (code=%s, message=%s)\n"),
4469 service_name_locale, code_locale, message_locale);
4470 isds_log_message(context, message_locale);
4471 free(code_locale);
4472 free(message_locale);
4473 err = IE_ISDS;
4474 goto leave;
4478 leave:
4479 free(code);
4480 free(message);
4481 if (err && *response) {
4482 xmlFreeDoc(*response);
4483 *response = NULL;
4485 if (*request) {
4486 xmlFreeNode(*request);
4487 *request = NULL;
4489 free(service_name_locale);
4491 return err;
4495 /* Generic bottom half with request sending.
4496 * It sends prepared request, checks for error code, destroys response and
4497 * request and log success or failure.
4498 * @context is ISDS session context.
4499 * @service is ISDS service handler
4500 * @service_name is name in scope of given @service
4501 * @request is XML tree with request. Will be freed to save memory.
4502 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4503 * NULL, if you don't care. */
4504 static isds_error send_request_check_drop_response(
4505 struct isds_ctx *context,
4506 const isds_service service, const xmlChar *service_name,
4507 xmlNodePtr *request, xmlChar **refnumber) {
4508 isds_error err = IE_SUCCESS;
4509 xmlDocPtr response = NULL;
4512 if (!context) return IE_INVALID_CONTEXT;
4513 if (!service_name || *service_name == '\0' || !request || !*request)
4514 return IE_INVAL;
4516 /* Send request and check response*/
4517 err = send_destroy_request_check_response(context,
4518 service, service_name, request, &response, refnumber);
4520 xmlFreeDoc(response);
4522 if (*request) {
4523 xmlFreeNode(*request);
4524 *request = NULL;
4527 if (!err) {
4528 char *service_name_locale = _isds_utf82locale((char *) service_name);
4529 isds_log(ILF_ISDS, ILL_DEBUG,
4530 _("%s request processed by server successfully.\n"),
4531 service_name_locale);
4532 free(service_name_locale);
4535 return err;
4539 /* Build XSD:tCreateDBInput request type for box createing.
4540 * @context is session context
4541 * @request outputs built XML tree
4542 * @service_name is request name of SERVICE_DB_MANIPULATION service
4543 * @box is box description to create including single primary user (in case of
4544 * FO box type)
4545 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
4546 * box, or contact address of PFO box owner)
4547 * @former_names is optional undocumented string. Pass NULL if you don't care.
4548 * @upper_box_id is optional ID of supper box if currently created box is
4549 * subordinated.
4550 * @ceo_label is optional title of OVM box owner (e.g. mayor) NULL, if you
4551 * don't care.
4552 * @approval is optional external approval of box manipulation */
4553 static isds_error build_CreateDBInput_request(struct isds_ctx *context,
4554 xmlNodePtr *request, const xmlChar *service_name,
4555 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
4556 const xmlChar *former_names, const xmlChar *upper_box_id,
4557 const xmlChar *ceo_label, const struct isds_approval *approval) {
4558 isds_error err = IE_SUCCESS;
4559 xmlNsPtr isds_ns = NULL;
4560 xmlNodePtr node, dbPrimaryUsers;
4561 xmlChar *string = NULL;
4562 const struct isds_list *item;
4565 if (!context) return IE_INVALID_CONTEXT;
4566 if (!request || !service_name || service_name[0] == '\0' || !box)
4567 return IE_INVAL;
4570 /* Build DeleteDataBox request */
4571 *request = xmlNewNode(NULL, service_name);
4572 if (!*request) {
4573 char *service_name_locale = _isds_utf82locale((char*) service_name);
4574 isds_printf_message(context, _("Could build %s request"),
4575 service_name_locale);
4576 free(service_name_locale);
4577 return IE_ERROR;
4579 if (context->type == CTX_TYPE_TESTING_REQUEST_COLLECTOR) {
4580 isds_ns = xmlNewNs(*request, BAD_CAST ISDS1_NS, NULL);
4581 if (!isds_ns) {
4582 isds_log_message(context, _("Could not create ISDS1 name space"));
4583 xmlFreeNode(*request);
4584 return IE_ERROR;
4586 } else {
4587 isds_ns = xmlNewNs(*request, BAD_CAST ISDS_NS, NULL);
4588 if (!isds_ns) {
4589 isds_log_message(context, _("Could not create ISDS name space"));
4590 xmlFreeNode(*request);
4591 return IE_ERROR;
4594 xmlSetNs(*request, isds_ns);
4596 INSERT_ELEMENT(node, *request, "dbOwnerInfo");
4597 err = insert_DbOwnerInfo(context, box, node);
4598 if (err) goto leave;
4600 /* Insert users */
4601 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
4602 * verbose documentatiot allows none dbUserInfo */
4603 INSERT_ELEMENT(dbPrimaryUsers, *request, "dbPrimaryUsers");
4604 for (item = users; item; item = item->next) {
4605 if (item->data) {
4606 INSERT_ELEMENT(node, dbPrimaryUsers, "dbUserInfo");
4607 err = insert_DbUserInfo(context,
4608 (struct isds_DbUserInfo *) item->data, node);
4609 if (err) goto leave;
4613 INSERT_STRING(*request, "dbFormerNames", former_names);
4614 INSERT_STRING(*request, "dbUpperDBId", upper_box_id);
4615 INSERT_STRING(*request, "dbCEOLabel", ceo_label);
4617 err = insert_GExtApproval(context, approval, *request);
4618 if (err) goto leave;
4620 leave:
4621 if (err) {
4622 xmlFreeNode(*request);
4623 *request = NULL;
4625 free(string);
4626 return err;
4630 /* Create new box.
4631 * @context is session context
4632 * @box is box description to create including single primary user (in case of
4633 * FO box type). It outputs box ID assigned by ISDS in dbID element.
4634 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
4635 * box, or contact address of PFO box owner)
4636 * @former_names is optional undocumented string. Pass NULL if you don't care.
4637 * @upper_box_id is optional ID of supper box if currently created box is
4638 * subordinated.
4639 * @ceo_label is optional title of OVM box owner (e.g. mayor)
4640 * @approval is optional external approval of box manipulation
4641 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4642 * NULL, if you don't care.*/
4643 isds_error isds_add_box(struct isds_ctx *context,
4644 struct isds_DbOwnerInfo *box, const struct isds_list *users,
4645 const char *former_names, const char *upper_box_id,
4646 const char *ceo_label, const struct isds_approval *approval,
4647 char **refnumber) {
4648 isds_error err = IE_SUCCESS;
4649 xmlNodePtr request = NULL;
4650 xmlDocPtr response = NULL;
4651 xmlXPathContextPtr xpath_ctx = NULL;
4652 xmlXPathObjectPtr result = NULL;
4655 if (!context) return IE_INVALID_CONTEXT;
4656 zfree(context->long_message);
4657 if (!box) return IE_INVAL;
4659 /* Scratch box ID */
4660 zfree(box->dbID);
4662 /* Build CreateDataBox request */
4663 err = build_CreateDBInput_request(context,
4664 &request, BAD_CAST "CreateDataBox",
4665 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
4666 (xmlChar *) ceo_label, approval);
4667 if (err) goto leave;
4669 /* Send it to server and process response */
4670 err = send_destroy_request_check_response(context,
4671 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
4672 &response, (xmlChar **) refnumber);
4674 /* Extract box ID */
4675 xpath_ctx = xmlXPathNewContext(response);
4676 if (!xpath_ctx) {
4677 err = IE_ERROR;
4678 goto leave;
4680 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4681 err = IE_ERROR;
4682 goto leave;
4684 EXTRACT_STRING("/isds:CreateDataBoxResponse/dbID", box->dbID);
4686 leave:
4687 xmlXPathFreeObject(result);
4688 xmlXPathFreeContext(xpath_ctx);
4689 xmlFreeDoc(response);
4690 xmlFreeNode(request);
4692 if (!err) {
4693 isds_log(ILF_ISDS, ILL_DEBUG,
4694 _("CreateDataBox request processed by server successfully.\n"));
4697 return err;
4701 /* Notify ISDS about new PFO entity.
4702 * This function has no real effect.
4703 * @context is session context
4704 * @box is PFO description including single primary user.
4705 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
4706 * @former_names is optional undocumented string. Pass NULL if you don't care.
4707 * @upper_box_id is optional ID of supper box if currently created box is
4708 * subordinated.
4709 * @ceo_label is optional title of OVM box owner (e.g. mayor)
4710 * @approval is optional external approval of box manipulation
4711 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4712 * NULL, if you don't care.*/
4713 isds_error isds_add_pfoinfo(struct isds_ctx *context,
4714 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
4715 const char *former_names, const char *upper_box_id,
4716 const char *ceo_label, const struct isds_approval *approval,
4717 char **refnumber) {
4718 isds_error err = IE_SUCCESS;
4719 xmlNodePtr request = NULL;
4721 if (!context) return IE_INVALID_CONTEXT;
4722 zfree(context->long_message);
4723 if (!box) return IE_INVAL;
4725 /* Build CreateDataBoxPFOInfo request */
4726 err = build_CreateDBInput_request(context,
4727 &request, BAD_CAST "CreateDataBoxPFOInfo",
4728 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
4729 (xmlChar *) ceo_label, approval);
4730 if (err) goto leave;
4732 /* Send it to server and process response */
4733 err = send_request_check_drop_response(context,
4734 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
4735 (xmlChar **) refnumber);
4736 leave:
4737 xmlFreeNode(request);
4738 return err;
4742 /* Remove given given box permanetly.
4743 * @context is session context
4744 * @box is box description to delete
4745 * @since is date of box owner cancalation. Only tm_year, tm_mon and tm_mday
4746 * carry sane value.
4747 * @approval is optional external approval of box manipulation
4748 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4749 * NULL, if you don't care.*/
4750 isds_error isds_delete_box(struct isds_ctx *context,
4751 const struct isds_DbOwnerInfo *box, const struct tm *since,
4752 const struct isds_approval *approval, char **refnumber) {
4753 isds_error err = IE_SUCCESS;
4754 xmlNsPtr isds_ns = NULL;
4755 xmlNodePtr request = NULL;
4756 xmlNodePtr node;
4757 xmlChar *string = NULL;
4760 if (!context) return IE_INVALID_CONTEXT;
4761 zfree(context->long_message);
4762 if (!box || !since) return IE_INVAL;
4765 /* Build DeleteDataBox request */
4766 request = xmlNewNode(NULL, BAD_CAST "DeleteDataBox");
4767 if (!request) {
4768 isds_log_message(context,
4769 _("Could build DeleteDataBox request"));
4770 return IE_ERROR;
4772 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4773 if(!isds_ns) {
4774 isds_log_message(context, _("Could not create ISDS name space"));
4775 xmlFreeNode(request);
4776 return IE_ERROR;
4778 xmlSetNs(request, isds_ns);
4780 INSERT_ELEMENT(node, request, "dbOwnerInfo");
4781 err = insert_DbOwnerInfo(context, box, node);
4782 if (err) goto leave;
4784 err = tm2datestring(since, &string);
4785 if (err) {
4786 isds_log_message(context,
4787 _("Could not convert `since' argument to ISO date string"));
4788 goto leave;
4790 INSERT_STRING(request, "dbOwnerTerminationDate", string);
4791 zfree(string);
4793 err = insert_GExtApproval(context, approval, request);
4794 if (err) goto leave;
4797 /* Send it to server and process response */
4798 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
4799 BAD_CAST "DeleteDataBox", &request, (xmlChar **) refnumber);
4801 leave:
4802 xmlFreeNode(request);
4803 free(string);
4804 return err;
4808 /* Update data about given box.
4809 * @context is session context
4810 * @old_box current box description
4811 * @new_box are updated data about @old_box
4812 * @approval is optional external approval of box manipulation
4813 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4814 * NULL, if you don't care.*/
4815 isds_error isds_UpdateDataBoxDescr(struct isds_ctx *context,
4816 const struct isds_DbOwnerInfo *old_box,
4817 const struct isds_DbOwnerInfo *new_box,
4818 const struct isds_approval *approval, char **refnumber) {
4819 isds_error err = IE_SUCCESS;
4820 xmlNsPtr isds_ns = NULL;
4821 xmlNodePtr request = NULL;
4822 xmlNodePtr node;
4825 if (!context) return IE_INVALID_CONTEXT;
4826 zfree(context->long_message);
4827 if (!old_box || !new_box) return IE_INVAL;
4830 /* Build UpdateDataBoxDescr request */
4831 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxDescr");
4832 if (!request) {
4833 isds_log_message(context,
4834 _("Could build UpdateDataBoxDescr request"));
4835 return IE_ERROR;
4837 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4838 if(!isds_ns) {
4839 isds_log_message(context, _("Could not create ISDS name space"));
4840 xmlFreeNode(request);
4841 return IE_ERROR;
4843 xmlSetNs(request, isds_ns);
4845 INSERT_ELEMENT(node, request, "dbOldOwnerInfo");
4846 err = insert_DbOwnerInfo(context, old_box, node);
4847 if (err) goto leave;
4849 INSERT_ELEMENT(node, request, "dbNewOwnerInfo");
4850 err = insert_DbOwnerInfo(context, new_box, node);
4851 if (err) goto leave;
4853 err = insert_GExtApproval(context, approval, request);
4854 if (err) goto leave;
4857 /* Send it to server and process response */
4858 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
4859 BAD_CAST "UpdateDataBoxDescr", &request, (xmlChar **) refnumber);
4861 leave:
4862 xmlFreeNode(request);
4864 return err;
4868 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
4869 * code
4870 * @context is session context
4871 * @service is SOAP service
4872 * @service_name is name of request in @service
4873 * @box_id is box ID of interrest
4874 * @approval is optional external approval of box manipulation
4875 * @response is server SOAP body response as XML document
4876 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4877 * NULL, if you don't care.
4878 * @return error coded from lower layer, context message will be set up
4879 * appropriately. */
4880 static isds_error build_send_dbid_request_check_response(
4881 struct isds_ctx *context, const isds_service service,
4882 const xmlChar *service_name, const xmlChar *box_id,
4883 const struct isds_approval *approval,
4884 xmlDocPtr *response, xmlChar **refnumber) {
4886 isds_error err = IE_SUCCESS;
4887 char *service_name_locale = NULL, *box_id_locale = NULL;
4888 xmlNodePtr request = NULL, node;
4889 xmlNsPtr isds_ns = NULL;
4891 if (!context) return IE_INVALID_CONTEXT;
4892 if (!service_name || !box_id) return IE_INVAL;
4893 if (!response) return IE_INVAL;
4895 /* Free output argument */
4896 xmlFreeDoc(*response); *response = NULL;
4898 /* Prepare strings */
4899 service_name_locale = _isds_utf82locale((char*)service_name);
4900 if (!service_name_locale) {
4901 err = IE_NOMEM;
4902 goto leave;
4904 box_id_locale = _isds_utf82locale((char*)box_id);
4905 if (!box_id_locale) {
4906 err = IE_NOMEM;
4907 goto leave;
4910 /* Build request */
4911 request = xmlNewNode(NULL, service_name);
4912 if (!request) {
4913 isds_printf_message(context,
4914 _("Could not build %s request"), service_name_locale);
4915 err = IE_ERROR;
4916 goto leave;
4918 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4919 if(!isds_ns) {
4920 isds_log_message(context, _("Could not create ISDS name space"));
4921 err = IE_ERROR;
4922 goto leave;
4924 xmlSetNs(request, isds_ns);
4926 /* Add XSD:tIdDbInput children */
4927 INSERT_STRING(request, "dbID", box_id);
4928 err = insert_GExtApproval(context, approval, request);
4929 if (err) goto leave;
4931 /* Send request and check response*/
4932 err = send_destroy_request_check_response(context,
4933 service, service_name, &request, response, refnumber);
4935 leave:
4936 free(service_name_locale);
4937 free(box_id_locale);
4938 xmlFreeNode(request);
4939 return err;
4943 /* Get data about all users assigned to given box.
4944 * @context is session context
4945 * @box_id is box ID
4946 * @users is automatically reallocated list of struct isds_DbUserInfo */
4947 isds_error isds_GetDataBoxUsers(struct isds_ctx *context, const char *box_id,
4948 struct isds_list **users) {
4949 isds_error err = IE_SUCCESS;
4950 xmlDocPtr response = NULL;
4951 xmlXPathContextPtr xpath_ctx = NULL;
4952 xmlXPathObjectPtr result = NULL;
4953 int i;
4954 struct isds_list *item, *prev_item = NULL;
4956 if (!context) return IE_INVALID_CONTEXT;
4957 zfree(context->long_message);
4958 if (!users || !box_id) return IE_INVAL;
4961 /* Do request and check for success */
4962 err = build_send_dbid_request_check_response(context,
4963 SERVICE_DB_MANIPULATION, BAD_CAST "GetDataBoxUsers",
4964 BAD_CAST box_id, NULL, &response, NULL);
4965 if (err) goto leave;
4968 /* Extract data */
4969 /* Prepare stucture */
4970 isds_list_free(users);
4971 xpath_ctx = xmlXPathNewContext(response);
4972 if (!xpath_ctx) {
4973 err = IE_ERROR;
4974 goto leave;
4976 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4977 err = IE_ERROR;
4978 goto leave;
4981 /* Set context node */
4982 result = xmlXPathEvalExpression(BAD_CAST
4983 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
4984 xpath_ctx);
4985 if (!result) {
4986 err = IE_ERROR;
4987 goto leave;
4989 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4990 isds_log_message(context, _("Missing dbUserInfo element"));
4991 err = IE_ISDS;
4992 goto leave;
4995 /* Iterate over all users */
4996 for (i = 0; i < result->nodesetval->nodeNr; i++) {
4998 /* Prepare structure */
4999 item = calloc(1, sizeof(*item));
5000 if (!item) {
5001 err = IE_NOMEM;
5002 goto leave;
5004 item->destructor = (void(*)(void**))isds_DbUserInfo_free;
5005 if (i == 0) *users = item;
5006 else prev_item->next = item;
5007 prev_item = item;
5009 /* Extract it */
5010 xpath_ctx->node = result->nodesetval->nodeTab[i];
5011 err = extract_DbUserInfo(context,
5012 (struct isds_DbUserInfo **) (&item->data), xpath_ctx);
5013 if (err) goto leave;
5016 leave:
5017 if (err) {
5018 isds_list_free(users);
5021 xmlXPathFreeObject(result);
5022 xmlXPathFreeContext(xpath_ctx);
5023 xmlFreeDoc(response);
5025 if (!err)
5026 isds_log(ILF_ISDS, ILL_DEBUG,
5027 _("GetDataBoxUsers request processed by server "
5028 "successfully.\n"));
5030 return err;
5034 /* Update data about user assigned to given box.
5035 * @context is session context
5036 * @box is box identification
5037 * @old_user identifies user to update
5038 * @new_user are updated data about @old_user
5039 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5040 * NULL, if you don't care.*/
5041 isds_error isds_UpdateDataBoxUser(struct isds_ctx *context,
5042 const struct isds_DbOwnerInfo *box,
5043 const struct isds_DbUserInfo *old_user,
5044 const struct isds_DbUserInfo *new_user,
5045 char **refnumber) {
5046 isds_error err = IE_SUCCESS;
5047 xmlNsPtr isds_ns = NULL;
5048 xmlNodePtr request = NULL;
5049 xmlNodePtr node;
5052 if (!context) return IE_INVALID_CONTEXT;
5053 zfree(context->long_message);
5054 if (!box || !old_user || !new_user) return IE_INVAL;
5057 /* Build UpdateDataBoxUser request */
5058 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxUser");
5059 if (!request) {
5060 isds_log_message(context,
5061 _("Could build UpdateDataBoxUser request"));
5062 return IE_ERROR;
5064 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5065 if(!isds_ns) {
5066 isds_log_message(context, _("Could not create ISDS name space"));
5067 xmlFreeNode(request);
5068 return IE_ERROR;
5070 xmlSetNs(request, isds_ns);
5072 INSERT_ELEMENT(node, request, "dbOwnerInfo");
5073 err = insert_DbOwnerInfo(context, box, node);
5074 if (err) goto leave;
5076 INSERT_ELEMENT(node, request, "dbOldUserInfo");
5077 err = insert_DbUserInfo(context, old_user, node);
5078 if (err) goto leave;
5080 INSERT_ELEMENT(node, request, "dbNewUserInfo");
5081 err = insert_DbUserInfo(context, new_user, node);
5082 if (err) goto leave;
5084 /* Send it to server and process response */
5085 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
5086 BAD_CAST "UpdateDataBoxUser", &request, (xmlChar **) refnumber);
5088 leave:
5089 xmlFreeNode(request);
5091 return err;
5095 /* Reset credentials of user assigned to given box.
5096 * @context is session context
5097 * @box is box identification
5098 * @user identifies user to reset password
5099 * @fee_paid is true if fee has been paid, false otherwise
5100 * @approval is optional external approval of box manipulation
5101 * @token is NULL if new password should be delivered off-line to the user.
5102 * It is valid pointer if user should obtain new password on-line on dedicated
5103 * web server. Then it output automatically reallocated token user needs to
5104 * use to athtorize on the web server to view his new password.
5105 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5106 * NULL, if you don't care.*/
5107 isds_error isds_reset_password(struct isds_ctx *context,
5108 const struct isds_DbOwnerInfo *box,
5109 const struct isds_DbUserInfo *user,
5110 const _Bool fee_paid, const struct isds_approval *approval,
5111 char **token, char **refnumber) {
5112 isds_error err = IE_SUCCESS;
5113 xmlNsPtr isds_ns = NULL;
5114 xmlNodePtr request = NULL, node;
5115 xmlDocPtr response = NULL;
5116 xmlXPathContextPtr xpath_ctx = NULL;
5117 xmlXPathObjectPtr result = NULL;
5120 if (!context) return IE_INVALID_CONTEXT;
5121 zfree(context->long_message);
5122 if (!box || !user) return IE_INVAL;
5124 if (token) zfree(*token);
5127 /* Build NewAccessData request */
5128 request = xmlNewNode(NULL, BAD_CAST "NewAccessData");
5129 if (!request) {
5130 isds_log_message(context,
5131 _("Could build NewAccessData request"));
5132 return IE_ERROR;
5134 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5135 if(!isds_ns) {
5136 isds_log_message(context, _("Could not create ISDS name space"));
5137 xmlFreeNode(request);
5138 return IE_ERROR;
5140 xmlSetNs(request, isds_ns);
5142 INSERT_ELEMENT(node, request, "dbOwnerInfo");
5143 err = insert_DbOwnerInfo(context, box, node);
5144 if (err) goto leave;
5146 INSERT_ELEMENT(node, request, "dbUserInfo");
5147 err = insert_DbUserInfo(context, user, node);
5148 if (err) goto leave;
5150 INSERT_SCALAR_BOOLEAN(request, "dbFeePaid", fee_paid);
5152 if (token) {
5153 INSERT_SCALAR_BOOLEAN(request, "dbVirtual", 1);
5154 } else {
5155 INSERT_SCALAR_BOOLEAN(request, "dbVirtual", 0);
5158 err = insert_GExtApproval(context, approval, request);
5159 if (err) goto leave;
5161 /* Send request and check reposne*/
5162 err = send_destroy_request_check_response(context,
5163 SERVICE_DB_MANIPULATION, BAD_CAST "NewAccessData", &request,
5164 &response, (xmlChar **) refnumber);
5165 if (err) goto leave;
5168 /* Extract optional token */
5169 if (token) {
5170 xpath_ctx = xmlXPathNewContext(response);
5171 if (!xpath_ctx) {
5172 err = IE_ERROR;
5173 goto leave;
5175 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5176 err = IE_ERROR;
5177 goto leave;
5180 EXTRACT_STRING("/isds:NewAccessDataResponse/dbAccessDataId", *token);
5183 leave:
5184 xmlXPathFreeObject(result);
5185 xmlXPathFreeContext(xpath_ctx);
5186 xmlFreeDoc(response);
5187 xmlFreeNode(request);
5189 if (!err)
5190 isds_log(ILF_ISDS, ILL_DEBUG,
5191 _("NewAccessData request processed by server "
5192 "successfully.\n"));
5194 return err;
5198 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
5199 * code, destroy response and log success.
5200 * @context is ISDS session context.
5201 * @service_name is name of SERVICE_DB_MANIPULATION service
5202 * @box is box identification
5203 * @user identifies user to removve
5204 * @approval is optional external approval of box manipulation
5205 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5206 * NULL, if you don't care. */
5207 static isds_error build_send_manipulationboxuser_request_check_drop_response(
5208 struct isds_ctx *context, const xmlChar *service_name,
5209 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
5210 const struct isds_approval *approval, xmlChar **refnumber) {
5211 isds_error err = IE_SUCCESS;
5212 xmlNsPtr isds_ns = NULL;
5213 xmlNodePtr request = NULL, node;
5216 if (!context) return IE_INVALID_CONTEXT;
5217 zfree(context->long_message);
5218 if (!service_name || service_name[0] == '\0' || !box || !user)
5219 return IE_INVAL;
5222 /* Build NewAccessData request */
5223 request = xmlNewNode(NULL, service_name);
5224 if (!request) {
5225 char *service_name_locale = _isds_utf82locale((char *) service_name);
5226 isds_printf_message(context, _("Could build %s request"),
5227 service_name_locale);
5228 free(service_name_locale);
5229 return IE_ERROR;
5231 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5232 if(!isds_ns) {
5233 isds_log_message(context, _("Could not create ISDS name space"));
5234 xmlFreeNode(request);
5235 return IE_ERROR;
5237 xmlSetNs(request, isds_ns);
5239 INSERT_ELEMENT(node, request, "dbOwnerInfo");
5240 err = insert_DbOwnerInfo(context, box, node);
5241 if (err) goto leave;
5243 INSERT_ELEMENT(node, request, "dbUserInfo");
5244 err = insert_DbUserInfo(context, user, node);
5245 if (err) goto leave;
5247 err = insert_GExtApproval(context, approval, request);
5248 if (err) goto leave;
5250 /* Send request and check reposne*/
5251 err = send_request_check_drop_response (context,
5252 SERVICE_DB_MANIPULATION, service_name, &request, refnumber);
5254 leave:
5255 xmlFreeNode(request);
5256 return err;
5260 /* Assign new user to given box.
5261 * @context is session context
5262 * @box is box identification
5263 * @user defines new user to add
5264 * @approval is optional external approval of box manipulation
5265 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5266 * NULL, if you don't care.*/
5267 isds_error isds_add_user(struct isds_ctx *context,
5268 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
5269 const struct isds_approval *approval, char **refnumber) {
5270 return build_send_manipulationboxuser_request_check_drop_response(context,
5271 BAD_CAST "AddDataBoxUser", box, user, approval,
5272 (xmlChar **) refnumber);
5276 /* Remove user assigned to given box.
5277 * @context is session context
5278 * @box is box identification
5279 * @user identifies user to removve
5280 * @approval is optional external approval of box manipulation
5281 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5282 * NULL, if you don't care.*/
5283 isds_error isds_delete_user(struct isds_ctx *context,
5284 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
5285 const struct isds_approval *approval, char **refnumber) {
5286 return build_send_manipulationboxuser_request_check_drop_response(context,
5287 BAD_CAST "DeleteDataBoxUser", box, user, approval,
5288 (xmlChar **) refnumber);
5292 /* Find boxes suiting given criteria.
5293 * @criteria is filter. You should fill in at least some memebers.
5294 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
5295 * possibly empty. Input NULL or valid old structure.
5296 * @return:
5297 * IE_SUCCESS if search sucseeded, @boxes contains usefull data
5298 * IE_NOEXIST if no such box exists, @boxes will be NULL
5299 * IE_2BIG if too much boxes exist and server truncated the resuluts, @boxes
5300 * contains still valid data
5301 * other code if something bad happens. @boxes will be NULL. */
5302 isds_error isds_FindDataBox(struct isds_ctx *context,
5303 const struct isds_DbOwnerInfo *criteria,
5304 struct isds_list **boxes) {
5305 isds_error err = IE_SUCCESS;
5306 _Bool truncated = 0;
5307 xmlNsPtr isds_ns = NULL;
5308 xmlNodePtr request = NULL;
5309 xmlDocPtr response = NULL;
5310 xmlChar *code = NULL, *message = NULL;
5311 xmlNodePtr db_owner_info;
5312 xmlXPathContextPtr xpath_ctx = NULL;
5313 xmlXPathObjectPtr result = NULL;
5314 xmlChar *string = NULL;
5317 if (!context) return IE_INVALID_CONTEXT;
5318 zfree(context->long_message);
5319 if (!boxes) return IE_INVAL;
5320 isds_list_free(boxes);
5322 if (!criteria) {
5323 return IE_INVAL;
5326 /* Check if connection is established
5327 * TODO: This check should be done donwstairs. */
5328 if (!context->curl) return IE_CONNECTION_CLOSED;
5331 /* Build FindDataBox request */
5332 request = xmlNewNode(NULL, BAD_CAST "FindDataBox");
5333 if (!request) {
5334 isds_log_message(context,
5335 _("Could build FindDataBox request"));
5336 return IE_ERROR;
5338 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5339 if(!isds_ns) {
5340 isds_log_message(context, _("Could not create ISDS name space"));
5341 xmlFreeNode(request);
5342 return IE_ERROR;
5344 xmlSetNs(request, isds_ns);
5345 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
5346 if (!db_owner_info) {
5347 isds_log_message(context, _("Could not add dbOwnerInfo child to "
5348 "FindDataBox element"));
5349 xmlFreeNode(request);
5350 return IE_ERROR;
5353 err = insert_DbOwnerInfo(context, criteria, db_owner_info);
5354 if (err) goto leave;
5357 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending FindDataBox request to ISDS\n"));
5359 /* Sent request */
5360 err = isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
5362 /* Destroy request */
5363 xmlFreeNode(request); request = NULL;
5365 if (err) {
5366 isds_log(ILF_ISDS, ILL_DEBUG,
5367 _("Processing ISDS response on FindDataBox "
5368 "request failed\n"));
5369 goto leave;
5372 /* Check for response status */
5373 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
5374 &code, &message, NULL);
5375 if (err) {
5376 isds_log(ILF_ISDS, ILL_DEBUG,
5377 _("ISDS response on FindDataBox request is missing status\n"));
5378 goto leave;
5381 /* Request processed, but nothing found */
5382 if (!xmlStrcmp(code, BAD_CAST "0002") ||
5383 !xmlStrcmp(code, BAD_CAST "5001")) {
5384 char *code_locale = _isds_utf82locale((char*)code);
5385 char *message_locale = _isds_utf82locale((char*)message);
5386 isds_log(ILF_ISDS, ILL_DEBUG,
5387 _("Server did not found any box on FindDataBox request "
5388 "(code=%s, message=%s)\n"), code_locale, message_locale);
5389 isds_log_message(context, message_locale);
5390 free(code_locale);
5391 free(message_locale);
5392 err = IE_NOEXIST;
5393 goto leave;
5396 /* Warning, not a error */
5397 if (!xmlStrcmp(code, BAD_CAST "0003")) {
5398 char *code_locale = _isds_utf82locale((char*)code);
5399 char *message_locale = _isds_utf82locale((char*)message);
5400 isds_log(ILF_ISDS, ILL_DEBUG,
5401 _("Server truncated response on FindDataBox request "
5402 "(code=%s, message=%s)\n"), code_locale, message_locale);
5403 isds_log_message(context, message_locale);
5404 free(code_locale);
5405 free(message_locale);
5406 truncated = 1;
5409 /* Other error */
5410 else if (xmlStrcmp(code, BAD_CAST "0000")) {
5411 char *code_locale = _isds_utf82locale((char*)code);
5412 char *message_locale = _isds_utf82locale((char*)message);
5413 isds_log(ILF_ISDS, ILL_DEBUG,
5414 _("Server refused FindDataBox request "
5415 "(code=%s, message=%s)\n"), code_locale, message_locale);
5416 isds_log_message(context, message_locale);
5417 free(code_locale);
5418 free(message_locale);
5419 err = IE_ISDS;
5420 goto leave;
5423 xpath_ctx = xmlXPathNewContext(response);
5424 if (!xpath_ctx) {
5425 err = IE_ERROR;
5426 goto leave;
5428 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5429 err = IE_ERROR;
5430 goto leave;
5433 /* Extract boxes if they present */
5434 result = xmlXPathEvalExpression(BAD_CAST
5435 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
5436 xpath_ctx);
5437 if (!result) {
5438 err = IE_ERROR;
5439 goto leave;
5441 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5442 struct isds_list *item, *prev_item = NULL;
5443 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
5444 item = calloc(1, sizeof(*item));
5445 if (!item) {
5446 err = IE_NOMEM;
5447 goto leave;
5450 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
5451 if (i == 0) *boxes = item;
5452 else prev_item->next = item;
5453 prev_item = item;
5455 xpath_ctx->node = result->nodesetval->nodeTab[i];
5456 err = extract_DbOwnerInfo(context,
5457 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
5458 if (err) goto leave;
5462 leave:
5463 if (err) {
5464 isds_list_free(boxes);
5465 } else {
5466 if (truncated) err = IE_2BIG;
5469 free(string);
5470 xmlFreeNode(request);
5471 xmlXPathFreeObject(result);
5472 xmlXPathFreeContext(xpath_ctx);
5474 free(code);
5475 free(message);
5476 xmlFreeDoc(response);
5478 if (!err)
5479 isds_log(ILF_ISDS, ILL_DEBUG,
5480 _("FindDataBox request processed by server successfully.\n"));
5482 return err;
5486 /* Get status of a box.
5487 * @context is ISDS session context.
5488 * @box_id is UTF-8 encoded box identifier as zero terminated string
5489 * @box_status is return value of box status.
5490 * @return:
5491 * IE_SUCCESS if box has been found and its status retrieved
5492 * IE_NOEXIST if box is not known to ISDS server
5493 * or other appropriate error.
5494 * You can use isds_DbState to enumerate box status. However out of enum
5495 * range value can be returned too. This is feature because ISDS
5496 * specification leaves the set of values open.
5497 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
5498 * the box has been deleted, but ISDS still lists its former existence. */
5499 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
5500 long int *box_status) {
5501 isds_error err = IE_SUCCESS;
5502 xmlNsPtr isds_ns = NULL;
5503 xmlNodePtr request = NULL, db_id;
5504 xmlDocPtr response = NULL;
5505 xmlChar *code = NULL, *message = NULL;
5506 xmlXPathContextPtr xpath_ctx = NULL;
5507 xmlXPathObjectPtr result = NULL;
5508 xmlChar *string = NULL;
5510 if (!context) return IE_INVALID_CONTEXT;
5511 zfree(context->long_message);
5512 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
5514 /* Check if connection is established
5515 * TODO: This check should be done donwstairs. */
5516 if (!context->curl) return IE_CONNECTION_CLOSED;
5519 /* Build CheckDataBox request */
5520 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
5521 if (!request) {
5522 isds_log_message(context,
5523 _("Could build CheckDataBox request"));
5524 return IE_ERROR;
5526 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5527 if(!isds_ns) {
5528 isds_log_message(context, _("Could not create ISDS name space"));
5529 xmlFreeNode(request);
5530 return IE_ERROR;
5532 xmlSetNs(request, isds_ns);
5533 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
5534 if (!db_id) {
5535 isds_log_message(context, _("Could not add dbID child to "
5536 "CheckDataBox element"));
5537 xmlFreeNode(request);
5538 return IE_ERROR;
5542 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CheckDataBox request to ISDS\n"));
5544 /* Sent request */
5545 err = isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
5547 /* Destroy request */
5548 xmlFreeNode(request);
5550 if (err) {
5551 isds_log(ILF_ISDS, ILL_DEBUG,
5552 _("Processing ISDS response on CheckDataBox "
5553 "request failed\n"));
5554 goto leave;
5557 /* Check for response status */
5558 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
5559 &code, &message, NULL);
5560 if (err) {
5561 isds_log(ILF_ISDS, ILL_DEBUG,
5562 _("ISDS response on CheckDataBox request is missing status\n"));
5563 goto leave;
5566 /* Request processed, but nothing found */
5567 if (!xmlStrcmp(code, BAD_CAST "5001")) {
5568 char *box_id_locale = _isds_utf82locale((char*)box_id);
5569 char *code_locale = _isds_utf82locale((char*)code);
5570 char *message_locale = _isds_utf82locale((char*)message);
5571 isds_log(ILF_ISDS, ILL_DEBUG,
5572 _("Server did not found box %s on CheckDataBox request "
5573 "(code=%s, message=%s)\n"),
5574 box_id_locale, code_locale, message_locale);
5575 isds_log_message(context, message_locale);
5576 free(box_id_locale);
5577 free(code_locale);
5578 free(message_locale);
5579 err = IE_NOEXIST;
5580 goto leave;
5583 /* Other error */
5584 else if (xmlStrcmp(code, BAD_CAST "0000")) {
5585 char *code_locale = _isds_utf82locale((char*)code);
5586 char *message_locale = _isds_utf82locale((char*)message);
5587 isds_log(ILF_ISDS, ILL_DEBUG,
5588 _("Server refused CheckDataBox request "
5589 "(code=%s, message=%s)\n"), code_locale, message_locale);
5590 isds_log_message(context, message_locale);
5591 free(code_locale);
5592 free(message_locale);
5593 err = IE_ISDS;
5594 goto leave;
5597 /* Extract data */
5598 xpath_ctx = xmlXPathNewContext(response);
5599 if (!xpath_ctx) {
5600 err = IE_ERROR;
5601 goto leave;
5603 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5604 err = IE_ERROR;
5605 goto leave;
5607 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
5608 xpath_ctx);
5609 if (!result) {
5610 err = IE_ERROR;
5611 goto leave;
5613 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5614 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
5615 err = IE_ISDS;
5616 goto leave;
5618 if (result->nodesetval->nodeNr > 1) {
5619 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
5620 err = IE_ISDS;
5621 goto leave;
5623 xpath_ctx->node = result->nodesetval->nodeTab[0];
5624 xmlXPathFreeObject(result); result = NULL;
5626 EXTRACT_LONGINT("isds:dbState", box_status, 1);
5629 leave:
5630 free(string);
5631 xmlXPathFreeObject(result);
5632 xmlXPathFreeContext(xpath_ctx);
5634 free(code);
5635 free(message);
5636 xmlFreeDoc(response);
5638 if (!err)
5639 isds_log(ILF_ISDS, ILL_DEBUG,
5640 _("CheckDataBox request processed by server successfully.\n"));
5642 return err;
5646 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
5647 * code, destroy response and log success.
5648 * @context is ISDS session context.
5649 * @service_name is name of SERVICE_DB_MANIPULATION service
5650 * @box_id is UTF-8 encoded box identifier as zero terminated string
5651 * @approval is optional external approval of box manipulation
5652 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5653 * NULL, if you don't care. */
5654 static isds_error build_send_manipulationdbid_request_check_drop_response(
5655 struct isds_ctx *context, const xmlChar *service_name,
5656 const xmlChar *box_id, const struct isds_approval *approval,
5657 xmlChar **refnumber) {
5658 isds_error err = IE_SUCCESS;
5659 xmlDocPtr response = NULL;
5661 if (!context) return IE_INVALID_CONTEXT;
5662 zfree(context->long_message);
5663 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
5665 /* Check if connection is established */
5666 if (!context->curl) return IE_CONNECTION_CLOSED;
5668 /* Do request and check for success */
5669 err = build_send_dbid_request_check_response(context,
5670 SERVICE_DB_MANIPULATION, service_name, box_id, approval,
5671 &response, refnumber);
5672 xmlFreeDoc(response);
5674 if (!err) {
5675 char *service_name_locale = _isds_utf82locale((char *) service_name);
5676 isds_log(ILF_ISDS, ILL_DEBUG,
5677 _("%s request processed by server successfully.\n"),
5678 service_name_locale);
5679 free(service_name_locale);
5682 return err;
5686 /* Switch box into state where box can receive commercial messages (off by
5687 * default)
5688 * @context is ISDS session context.
5689 * @box_id is UTF-8 encoded box identifier as zero terminated string
5690 * @allow is true for enable, false for disable commercial messages income
5691 * @approval is optional external approval of box manipulation
5692 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5693 * NULL, if you don't care. */
5694 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
5695 const char *box_id, const _Bool allow,
5696 const struct isds_approval *approval, char **refnumber) {
5697 return build_send_manipulationdbid_request_check_drop_response(context,
5698 (allow) ? BAD_CAST "SetOpenAddressing" :
5699 BAD_CAST "ClearOpenAddressing",
5700 BAD_CAST box_id, approval, (xmlChar **) refnumber);
5704 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
5705 * message acceptance). This is just a box permission. Sender must apply
5706 * such role by sending each message.
5707 * @context is ISDS session context.
5708 * @box_id is UTF-8 encoded box identifier as zero terminated string
5709 * @allow is true for enable, false for disable OVM role permission
5710 * @approval is optional external approval of box manipulation
5711 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5712 * NULL, if you don't care. */
5713 isds_error isds_switch_effective_ovm(struct isds_ctx *context,
5714 const char *box_id, const _Bool allow,
5715 const struct isds_approval *approval, char **refnumber) {
5716 return build_send_manipulationdbid_request_check_drop_response(context,
5717 (allow) ? BAD_CAST "SetEffectiveOVM" :
5718 BAD_CAST "ClearEffectiveOVM",
5719 BAD_CAST box_id, approval, (xmlChar **) refnumber);
5723 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
5724 * code, destroy response and log success.
5725 * @context is ISDS session context.
5726 * @service_name is name of SERVICE_DB_MANIPULATION service
5727 * @owner is structure describing box
5728 * @approval is optional external approval of box manipulation
5729 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5730 * NULL, if you don't care. */
5731 static isds_error build_send_manipulationdbowner_request_check_drop_response(
5732 struct isds_ctx *context, const xmlChar *service_name,
5733 const struct isds_DbOwnerInfo *owner,
5734 const struct isds_approval *approval, xmlChar **refnumber) {
5735 isds_error err = IE_SUCCESS;
5736 char *service_name_locale = NULL;
5737 xmlNodePtr request = NULL, db_owner_info;
5738 xmlNsPtr isds_ns = NULL;
5741 if (!context) return IE_INVALID_CONTEXT;
5742 zfree(context->long_message);
5743 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
5745 service_name_locale = _isds_utf82locale((char*)service_name);
5746 if (!service_name_locale) {
5747 err = IE_NOMEM;
5748 goto leave;
5751 /* Build request */
5752 request = xmlNewNode(NULL, service_name);
5753 if (!request) {
5754 isds_printf_message(context,
5755 _("Could not build %s request"), service_name_locale);
5756 err = IE_ERROR;
5757 goto leave;
5759 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5760 if(!isds_ns) {
5761 isds_log_message(context, _("Could not create ISDS name space"));
5762 err = IE_ERROR;
5763 goto leave;
5765 xmlSetNs(request, isds_ns);
5768 /* Add XSD:tOwnerInfoInput child*/
5769 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
5770 err = insert_DbOwnerInfo(context, owner, db_owner_info);
5771 if (err) goto leave;
5773 /* Add XSD:gExtApproval*/
5774 err = insert_GExtApproval(context, approval, request);
5775 if (err) goto leave;
5777 /* Send it to server and process response */
5778 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
5779 service_name, &request, refnumber);
5781 leave:
5782 xmlFreeNode(request);
5783 free(service_name_locale);
5785 return err;
5789 /* Switch box accessibility state on request of box owner.
5790 * Despite the name, owner must do the request off-line. This function is
5791 * designed for such off-line meeting points (e.g. Czech POINT).
5792 * @context is ISDS session context.
5793 * @box identifies box to swith accesibilty state.
5794 * @allow is true for making accesibale, false to disallow access.
5795 * @approval is optional external approval of box manipulation
5796 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5797 * NULL, if you don't care. */
5798 isds_error isds_switch_box_accessibility_on_owner_request(
5799 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
5800 const _Bool allow, const struct isds_approval *approval,
5801 char **refnumber) {
5802 return build_send_manipulationdbowner_request_check_drop_response(context,
5803 (allow) ? BAD_CAST "EnableOwnDataBox" :
5804 BAD_CAST "DisableOwnDataBox",
5805 box, approval, (xmlChar **) refnumber);
5809 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
5810 * date.
5811 * @context is ISDS session context.
5812 * @box identifies box to swith accesibilty state.
5813 * @since is date since accesseibility has been denied. This can be past too.
5814 * Only tm_year, tm_mon and tm_mday carry sane value.
5815 * @approval is optional external approval of box manipulation
5816 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5817 * NULL, if you don't care. */
5818 isds_error isds_disable_box_accessibility_externaly(
5819 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
5820 const struct tm *since, const struct isds_approval *approval,
5821 char **refnumber) {
5822 isds_error err = IE_SUCCESS;
5823 char *service_name_locale = NULL;
5824 xmlNodePtr request = NULL, node;
5825 xmlNsPtr isds_ns = NULL;
5826 xmlChar *string = NULL;
5829 if (!context) return IE_INVALID_CONTEXT;
5830 zfree(context->long_message);
5831 if (!box || !since) return IE_INVAL;
5833 /* Build request */
5834 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
5835 if (!request) {
5836 isds_printf_message(context,
5837 _("Could not build %s request"), "DisableDataBoxExternally");
5838 err = IE_ERROR;
5839 goto leave;
5841 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5842 if(!isds_ns) {
5843 isds_log_message(context, _("Could not create ISDS name space"));
5844 err = IE_ERROR;
5845 goto leave;
5847 xmlSetNs(request, isds_ns);
5850 /* Add @box identification */
5851 INSERT_ELEMENT(node, request, "dbOwnerInfo");
5852 err = insert_DbOwnerInfo(context, box, node);
5853 if (err) goto leave;
5855 /* Add @since date */
5856 err = tm2datestring(since, &string);
5857 if(err) {
5858 isds_log_message(context,
5859 _("Could not convert `since' argument to ISO date string"));
5860 goto leave;
5862 INSERT_STRING(request, "dbOwnerDisableDate", string);
5863 zfree(string);
5865 /* Add @approval */
5866 err = insert_GExtApproval(context, approval, request);
5867 if (err) goto leave;
5869 /* Send it to server and process response */
5870 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
5871 BAD_CAST "DisableDataBoxExternally", &request,
5872 (xmlChar **) refnumber);
5874 leave:
5875 free(string);
5876 xmlFreeNode(request);
5877 free(service_name_locale);
5879 return err;
5883 /* Insert struct isds_message data (envelope (recipient data optional) and
5884 * documents) into XML tree
5885 * @context is sesstion context
5886 * @outgoing_message is libsids structure with message data
5887 * @create_message is XML CreateMessage or CreateMultipleMessage element
5888 * @process_recipient true for recipient data serialization, false for no
5889 * serialization */
5890 static isds_error insert_envelope_files(struct isds_ctx *context,
5891 const struct isds_message *outgoing_message, xmlNodePtr create_message,
5892 const _Bool process_recipient) {
5894 isds_error err = IE_SUCCESS;
5895 xmlNodePtr envelope, dm_files, node;
5896 xmlChar *string = NULL;
5898 if (!context) return IE_INVALID_CONTEXT;
5899 if (!outgoing_message || !create_message) return IE_INVAL;
5902 /* Build envelope */
5903 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
5904 if (!envelope) {
5905 isds_printf_message(context, _("Could not add dmEnvelope child to "
5906 "%s element"), create_message->name);
5907 return IE_ERROR;
5910 if (!outgoing_message->envelope) {
5911 isds_log_message(context, _("Outgoing message is missing envelope"));
5912 err = IE_INVAL;
5913 goto leave;
5916 INSERT_STRING(envelope, "dmSenderOrgUnit",
5917 outgoing_message->envelope->dmSenderOrgUnit);
5918 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
5919 outgoing_message->envelope->dmSenderOrgUnitNum, string);
5921 if (process_recipient) {
5922 if (!outgoing_message->envelope->dbIDRecipient) {
5923 isds_log_message(context,
5924 _("Outgoing message is missing recipient box identifier"));
5925 err = IE_INVAL;
5926 goto leave;
5928 INSERT_STRING(envelope, "dbIDRecipient",
5929 outgoing_message->envelope->dbIDRecipient);
5931 INSERT_STRING(envelope, "dmRecipientOrgUnit",
5932 outgoing_message->envelope->dmRecipientOrgUnit);
5933 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
5934 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
5935 INSERT_STRING(envelope, "dmToHands",
5936 outgoing_message->envelope->dmToHands);
5939 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
5940 "dmAnnotation");
5941 INSERT_STRING(envelope, "dmAnnotation",
5942 outgoing_message->envelope->dmAnnotation);
5944 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
5945 0, 50, "dmRecipientRefNumber");
5946 INSERT_STRING(envelope, "dmRecipientRefNumber",
5947 outgoing_message->envelope->dmRecipientRefNumber);
5949 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
5950 0, 50, "dmSenderRefNumber");
5951 INSERT_STRING(envelope, "dmSenderRefNumber",
5952 outgoing_message->envelope->dmSenderRefNumber);
5954 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
5955 0, 50, "dmRecipientIdent");
5956 INSERT_STRING(envelope, "dmRecipientIdent",
5957 outgoing_message->envelope->dmRecipientIdent);
5959 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
5960 0, 50, "dmSenderIdent");
5961 INSERT_STRING(envelope, "dmSenderIdent",
5962 outgoing_message->envelope->dmSenderIdent);
5964 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
5965 outgoing_message->envelope->dmLegalTitleLaw, string);
5966 INSERT_LONGINT(envelope, "dmLegalTitleYear",
5967 outgoing_message->envelope->dmLegalTitleYear, string);
5968 INSERT_STRING(envelope, "dmLegalTitleSect",
5969 outgoing_message->envelope->dmLegalTitleSect);
5970 INSERT_STRING(envelope, "dmLegalTitlePar",
5971 outgoing_message->envelope->dmLegalTitlePar);
5972 INSERT_STRING(envelope, "dmLegalTitlePoint",
5973 outgoing_message->envelope->dmLegalTitlePoint);
5975 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
5976 outgoing_message->envelope->dmPersonalDelivery);
5977 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
5978 outgoing_message->envelope->dmAllowSubstDelivery);
5980 /* ???: Should we require value for dbEffectiveOVM sender?
5981 * ISDS has default as true */
5982 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
5985 /* Append dmFiles */
5986 if (!outgoing_message->documents) {
5987 isds_log_message(context,
5988 _("Outgoing message is missing list of documents"));
5989 err = IE_INVAL;
5990 goto leave;
5992 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
5993 if (!dm_files) {
5994 isds_printf_message(context, _("Could not add dmFiles child to "
5995 "%s element"), create_message->name);
5996 err = IE_ERROR;
5997 goto leave;
6000 /* Check for document hieararchy */
6001 err = _isds_check_documents_hierarchy(context, outgoing_message->documents);
6002 if (err) goto leave;
6004 /* Process each document */
6005 for (struct isds_list *item =
6006 (struct isds_list *) outgoing_message->documents;
6007 item; item = item->next) {
6008 if (!item->data) {
6009 isds_log_message(context,
6010 _("List of documents contains empty item"));
6011 err = IE_INVAL;
6012 goto leave;
6014 /* FIXME: Check for dmFileMetaType and for document references.
6015 * Only first document can be of MAIN type */
6016 err = insert_document(context, (struct isds_document*) item->data,
6017 dm_files);
6019 if (err) goto leave;
6022 leave:
6023 free(string);
6024 return err;
6028 /* Send a message via ISDS to a recipent
6029 * @context is session context
6030 * @outgoing_message is message to send; Some memebers are mandatory (like
6031 * dbIDRecipient), some are optional and some are irrelevant (especialy data
6032 * about sender). Included pointer to isds_list documents must contain at
6033 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
6034 * members will be filled with valid data from ISDS. Exact list of write
6035 * members is subject to change. Currently dmId is changed.
6036 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
6037 isds_error isds_send_message(struct isds_ctx *context,
6038 struct isds_message *outgoing_message) {
6040 isds_error err = IE_SUCCESS;
6041 xmlNsPtr isds_ns = NULL;
6042 xmlNodePtr request = NULL;
6043 xmlDocPtr response = NULL;
6044 xmlChar *code = NULL, *message = NULL;
6045 xmlXPathContextPtr xpath_ctx = NULL;
6046 xmlXPathObjectPtr result = NULL;
6047 _Bool message_is_complete = 0;
6049 if (!context) return IE_INVALID_CONTEXT;
6050 zfree(context->long_message);
6051 if (!outgoing_message) return IE_INVAL;
6053 /* Check if connection is established
6054 * TODO: This check should be done donwstairs. */
6055 if (!context->curl) return IE_CONNECTION_CLOSED;
6058 /* Build CreateMessage request */
6059 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
6060 if (!request) {
6061 isds_log_message(context,
6062 _("Could build CreateMessage request"));
6063 return IE_ERROR;
6065 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6066 if(!isds_ns) {
6067 isds_log_message(context, _("Could not create ISDS name space"));
6068 xmlFreeNode(request);
6069 return IE_ERROR;
6071 xmlSetNs(request, isds_ns);
6073 /* Append envelope and files */
6074 err = insert_envelope_files(context, outgoing_message, request, 1);
6075 if (err) goto leave;
6078 /* Signal we can serilize message since now */
6079 message_is_complete = 1;
6082 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
6084 /* Sent request */
6085 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
6087 /* Dont' destroy request, we want to provide it to application later */
6089 if (err) {
6090 isds_log(ILF_ISDS, ILL_DEBUG,
6091 _("Processing ISDS response on CreateMessage "
6092 "request failed\n"));
6093 goto leave;
6096 /* Check for response status */
6097 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
6098 &code, &message, NULL);
6099 if (err) {
6100 isds_log(ILF_ISDS, ILL_DEBUG,
6101 _("ISDS response on CreateMessage request "
6102 "is missing status\n"));
6103 goto leave;
6106 /* Request processed, but refused by server or server failed */
6107 if (xmlStrcmp(code, BAD_CAST "0000")) {
6108 char *box_id_locale =
6109 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
6110 char *code_locale = _isds_utf82locale((char*)code);
6111 char *message_locale = _isds_utf82locale((char*)message);
6112 isds_log(ILF_ISDS, ILL_DEBUG,
6113 _("Server did not accept message for %s on CreateMessage "
6114 "request (code=%s, message=%s)\n"),
6115 box_id_locale, code_locale, message_locale);
6116 isds_log_message(context, message_locale);
6117 free(box_id_locale);
6118 free(code_locale);
6119 free(message_locale);
6120 err = IE_ISDS;
6121 goto leave;
6125 /* Extract data */
6126 xpath_ctx = xmlXPathNewContext(response);
6127 if (!xpath_ctx) {
6128 err = IE_ERROR;
6129 goto leave;
6131 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6132 err = IE_ERROR;
6133 goto leave;
6135 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
6136 xpath_ctx);
6137 if (!result) {
6138 err = IE_ERROR;
6139 goto leave;
6141 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6142 isds_log_message(context, _("Missing CreateMessageResponse element"));
6143 err = IE_ISDS;
6144 goto leave;
6146 if (result->nodesetval->nodeNr > 1) {
6147 isds_log_message(context, _("Multiple CreateMessageResponse element"));
6148 err = IE_ISDS;
6149 goto leave;
6151 xpath_ctx->node = result->nodesetval->nodeTab[0];
6152 xmlXPathFreeObject(result); result = NULL;
6154 if (outgoing_message->envelope->dmID) {
6155 free(outgoing_message->envelope->dmID);
6156 outgoing_message->envelope->dmID = NULL;
6158 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
6159 if (!outgoing_message->envelope->dmID) {
6160 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
6161 "but did not return assigned message ID\n"));
6164 leave:
6165 /* TODO: Serialize message into structure member raw */
6166 /* XXX: Each web service transport message in different format.
6167 * Therefore it's not possible to save them directly.
6168 * To save them, one must figure out common format.
6169 * We can leave it on application, or we can implement the ESS format. */
6170 /*if (message_is_complete) {
6171 if (outgoing_message->envelope->dmID) {
6173 /* Add assigned message ID as first child*/
6174 /*xmlNodePtr dmid_text = xmlNewText(
6175 (xmlChar *) outgoing_message->envelope->dmID);
6176 if (!dmid_text) goto serialization_failed;
6178 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
6179 BAD_CAST "dmID");
6180 if (!dmid_element) {
6181 xmlFreeNode(dmid_text);
6182 goto serialization_failed;
6185 xmlNodePtr dmid_element_with_text =
6186 xmlAddChild(dmid_element, dmid_text);
6187 if (!dmid_element_with_text) {
6188 xmlFreeNode(dmid_element);
6189 xmlFreeNode(dmid_text);
6190 goto serialization_failed;
6193 node = xmlAddPrevSibling(envelope->childern,
6194 dmid_element_with_text);
6195 if (!node) {
6196 xmlFreeNodeList(dmid_element_with_text);
6197 goto serialization_failed;
6201 /* Serialize message with ID into raw */
6202 /*buffer = serialize_element(envelope)*/
6203 /* }
6205 serialization_failed:
6209 /* Clean up */
6210 xmlXPathFreeObject(result);
6211 xmlXPathFreeContext(xpath_ctx);
6213 free(code);
6214 free(message);
6215 xmlFreeDoc(response);
6216 xmlFreeNode(request);
6218 if (!err)
6219 isds_log(ILF_ISDS, ILL_DEBUG,
6220 _("CreateMessage request processed by server "
6221 "successfully.\n"));
6223 return err;
6227 /* Send a message via ISDS to a multiple recipents
6228 * @context is session context
6229 * @outgoing_message is message to send; Some memebers are mandatory,
6230 * some are optional and some are irrelevant (especialy data
6231 * about sender). Data about recipient will be substituted by ISDS from
6232 * @copies. Included pointer to isds_list documents must
6233 * contain at least one document of FILEMETATYPE_MAIN.
6234 * @copies is list of isds_message_copy structures addressing all desired
6235 * recipients. This is read-write structure, some members will be filled with
6236 * valid data from ISDS (message IDs, error codes, error descriptions).
6237 * @return
6238 * ISDS_SUCCESS if all messages have been sent
6239 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
6240 * succesed messages can be identified by copies->data->error),
6241 * or other error code if something other goes wrong. */
6242 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
6243 const struct isds_message *outgoing_message,
6244 struct isds_list *copies) {
6246 isds_error err = IE_SUCCESS, append_err;
6247 xmlNsPtr isds_ns = NULL;
6248 xmlNodePtr request = NULL, recipients, recipient, node;
6249 struct isds_list *item;
6250 struct isds_message_copy *copy;
6251 xmlDocPtr response = NULL;
6252 xmlChar *code = NULL, *message = NULL;
6253 xmlXPathContextPtr xpath_ctx = NULL;
6254 xmlXPathObjectPtr result = NULL;
6255 xmlChar *string = NULL;
6256 int i;
6258 if (!context) return IE_INVALID_CONTEXT;
6259 zfree(context->long_message);
6260 if (!outgoing_message || !copies) return IE_INVAL;
6262 /* Check if connection is established
6263 * TODO: This check should be done donwstairs. */
6264 if (!context->curl) return IE_CONNECTION_CLOSED;
6267 /* Build CreateMultipleMessage request */
6268 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
6269 if (!request) {
6270 isds_log_message(context,
6271 _("Could not build CreateMultipleMessage request"));
6272 return IE_ERROR;
6274 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6275 if(!isds_ns) {
6276 isds_log_message(context, _("Could not create ISDS name space"));
6277 xmlFreeNode(request);
6278 return IE_ERROR;
6280 xmlSetNs(request, isds_ns);
6283 /* Build recipients */
6284 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
6285 if (!recipients) {
6286 isds_log_message(context, _("Could not add dmRecipients child to "
6287 "CreateMultipleMessage element"));
6288 xmlFreeNode(request);
6289 return IE_ERROR;
6292 /* Insert each recipient */
6293 for (item = copies; item; item = item->next) {
6294 copy = (struct isds_message_copy *) item->data;
6295 if (!copy) {
6296 isds_log_message(context,
6297 _("`copies' list item contains empty data"));
6298 err = IE_INVAL;
6299 goto leave;
6302 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
6303 if (!recipient) {
6304 isds_log_message(context, _("Could not add dmRecipient child to "
6305 "dmRecipients element"));
6306 err = IE_ERROR;
6307 goto leave;
6310 if (!copy->dbIDRecipient) {
6311 isds_log_message(context,
6312 _("Message copy is missing recipient box identifier"));
6313 err = IE_INVAL;
6314 goto leave;
6316 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
6317 INSERT_STRING(recipient, "dmRecipientOrgUnit",
6318 copy->dmRecipientOrgUnit);
6319 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
6320 copy->dmRecipientOrgUnitNum, string);
6321 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
6324 /* Append envelope and files */
6325 err = insert_envelope_files(context, outgoing_message, request, 0);
6326 if (err) goto leave;
6329 isds_log(ILF_ISDS, ILL_DEBUG,
6330 _("Sending CreateMultipleMessage request to ISDS\n"));
6332 /* Sent request */
6333 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
6334 if (err) {
6335 isds_log(ILF_ISDS, ILL_DEBUG,
6336 _("Processing ISDS response on CreateMultipleMessage "
6337 "request failed\n"));
6338 goto leave;
6341 /* Check for response status */
6342 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
6343 &code, &message, NULL);
6344 if (err) {
6345 isds_log(ILF_ISDS, ILL_DEBUG,
6346 _("ISDS response on CreateMultipleMessage request "
6347 "is missing status\n"));
6348 goto leave;
6351 /* Request processed, but some copies failed */
6352 if (!xmlStrcmp(code, BAD_CAST "0004")) {
6353 char *box_id_locale =
6354 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
6355 char *code_locale = _isds_utf82locale((char*)code);
6356 char *message_locale = _isds_utf82locale((char*)message);
6357 isds_log(ILF_ISDS, ILL_DEBUG,
6358 _("Server did accept message for multiple recipients "
6359 "on CreateMultipleMessage request but delivery to "
6360 "some of them failed (code=%s, message=%s)\n"),
6361 box_id_locale, code_locale, message_locale);
6362 isds_log_message(context, message_locale);
6363 free(box_id_locale);
6364 free(code_locale);
6365 free(message_locale);
6366 err = IE_PARTIAL_SUCCESS;
6369 /* Request refused by server as whole */
6370 else if (xmlStrcmp(code, BAD_CAST "0000")) {
6371 char *box_id_locale =
6372 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
6373 char *code_locale = _isds_utf82locale((char*)code);
6374 char *message_locale = _isds_utf82locale((char*)message);
6375 isds_log(ILF_ISDS, ILL_DEBUG,
6376 _("Server did not accept message for multiple recipients "
6377 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
6378 box_id_locale, code_locale, message_locale);
6379 isds_log_message(context, message_locale);
6380 free(box_id_locale);
6381 free(code_locale);
6382 free(message_locale);
6383 err = IE_ISDS;
6384 goto leave;
6388 /* Extract data */
6389 xpath_ctx = xmlXPathNewContext(response);
6390 if (!xpath_ctx) {
6391 err = IE_ERROR;
6392 goto leave;
6394 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6395 err = IE_ERROR;
6396 goto leave;
6398 result = xmlXPathEvalExpression(
6399 BAD_CAST "/isds:CreateMultipleMessageResponse"
6400 "/isds:dmMultipleStatus/isds:dmSingleStatus",
6401 xpath_ctx);
6402 if (!result) {
6403 err = IE_ERROR;
6404 goto leave;
6406 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6407 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
6408 err = IE_ISDS;
6409 goto leave;
6412 /* Extract message ID and delivery status for each copy */
6413 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
6414 item = item->next, i++) {
6415 copy = (struct isds_message_copy *) item->data;
6416 xpath_ctx->node = result->nodesetval->nodeTab[i];
6418 append_err = append_TMStatus(context, copy, xpath_ctx);
6419 if (append_err) {
6420 err = append_err;
6421 goto leave;
6424 if (item || i < result->nodesetval->nodeNr) {
6425 isds_printf_message(context, _("ISDS returned unexpected number of "
6426 "message copy delivery states: %d"),
6427 result->nodesetval->nodeNr);
6428 err = IE_ISDS;
6429 goto leave;
6433 leave:
6434 /* Clean up */
6435 free(string);
6436 xmlXPathFreeObject(result);
6437 xmlXPathFreeContext(xpath_ctx);
6439 free(code);
6440 free(message);
6441 xmlFreeDoc(response);
6442 xmlFreeNode(request);
6444 if (!err)
6445 isds_log(ILF_ISDS, ILL_DEBUG,
6446 _("CreateMultipleMessageResponse request processed by server "
6447 "successfully.\n"));
6449 return err;
6453 /* Get list of messages. This is common core for getting sent or received
6454 * messaeges.
6455 * Any criterion argument can be NULL, if you don't care about it.
6456 * @context is session context. Must not be NULL.
6457 * @outgoing_direction is true if you want list of outgoing messages,
6458 * it's false if you want incoming messages.
6459 * @from_time is minimal time and date of message sending inclusive.
6460 * @to_time is maximal time and date of message sending inclusive
6461 * @organization_unit_number is number of sender/recipient respectively.
6462 * @status_filter is bit field of isds_message_status values. Use special
6463 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
6464 * all values, you can use bitwise arithmetic if you want.)
6465 * @offset is index of first message we are interested in. First message is 1.
6466 * Set to 0 (or 1) if you don't care.
6467 * @number is maximal length of list you want to get as input value, outputs
6468 * number of messages matching these criteria. Can be NULL if you don't care
6469 * (applies to output value either).
6470 * @messages is automatically reallocated list of isds_message's. Be ware that
6471 * it returns only brief overview (envelope and some other fields) about each
6472 * message, not the complete message. FIXME: Specify exact fields.
6473 * The list is sorted by delivery time in ascending order.
6474 * Use NULL if
6475 * you don't care about don't need the data (useful if you want to know only
6476 * the @number). If you provide &NULL, list will be allocated on heap, if you
6477 * provide pointer to non-NULL, list will be freed automacally at first. Also
6478 * in case of error the list will be NULLed.
6479 * @return IE_SUCCESS or appropriate error code. */
6480 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
6481 _Bool outgoing_direction,
6482 const struct timeval *from_time, const struct timeval *to_time,
6483 const long int *organization_unit_number,
6484 const unsigned int status_filter,
6485 const unsigned long int offset, unsigned long int *number,
6486 struct isds_list **messages) {
6488 isds_error err = IE_SUCCESS;
6489 xmlNsPtr isds_ns = NULL;
6490 xmlNodePtr request = NULL, node;
6491 xmlDocPtr response = NULL;
6492 xmlChar *code = NULL, *message = NULL;
6493 xmlXPathContextPtr xpath_ctx = NULL;
6494 xmlXPathObjectPtr result = NULL;
6495 xmlChar *string = NULL;
6496 long unsigned int count = 0;
6498 if (!context) return IE_INVALID_CONTEXT;
6499 zfree(context->long_message);
6501 /* Free former message list if any */
6502 if (messages) isds_list_free(messages);
6504 /* Check if connection is established
6505 * TODO: This check should be done donwstairs. */
6506 if (!context->curl) return IE_CONNECTION_CLOSED;
6508 /* Build GetListOf*Messages request */
6509 request = xmlNewNode(NULL,
6510 (outgoing_direction) ?
6511 BAD_CAST "GetListOfSentMessages" :
6512 BAD_CAST "GetListOfReceivedMessages"
6514 if (!request) {
6515 isds_log_message(context,
6516 (outgoing_direction) ?
6517 _("Could not build GetListOfSentMessages request") :
6518 _("Could not build GetListOfReceivedMessages request")
6520 return IE_ERROR;
6522 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6523 if(!isds_ns) {
6524 isds_log_message(context, _("Could not create ISDS name space"));
6525 xmlFreeNode(request);
6526 return IE_ERROR;
6528 xmlSetNs(request, isds_ns);
6531 if (from_time) {
6532 err = timeval2timestring(from_time, &string);
6533 if (err) goto leave;
6535 INSERT_STRING(request, "dmFromTime", string);
6536 free(string); string = NULL;
6538 if (to_time) {
6539 err = timeval2timestring(to_time, &string);
6540 if (err) goto leave;
6542 INSERT_STRING(request, "dmToTime", string);
6543 free(string); string = NULL;
6545 if (outgoing_direction) {
6546 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
6547 organization_unit_number, string);
6548 } else {
6549 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
6550 organization_unit_number, string);
6553 if (status_filter > MESSAGESTATE_ANY) {
6554 isds_printf_message(context,
6555 _("Invalid message state filter value: %ld"), status_filter);
6556 err = IE_INVAL;
6557 goto leave;
6559 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
6561 if (offset > 0 ) {
6562 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
6563 } else {
6564 INSERT_STRING(request, "dmOffset", "1");
6567 /* number 0 means no limit */
6568 if (number && *number == 0) {
6569 INSERT_STRING(request, "dmLimit", NULL);
6570 } else {
6571 INSERT_ULONGINT(request, "dmLimit", number, string);
6575 isds_log(ILF_ISDS, ILL_DEBUG,
6576 (outgoing_direction) ?
6577 _("Sending GetListOfSentMessages request to ISDS\n") :
6578 _("Sending GetListOfReceivedMessages request to ISDS\n")
6581 /* Sent request */
6582 err = isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
6583 xmlFreeNode(request); request = NULL;
6585 if (err) {
6586 isds_log(ILF_ISDS, ILL_DEBUG,
6587 (outgoing_direction) ?
6588 _("Processing ISDS response on GetListOfSentMessages "
6589 "request failed\n") :
6590 _("Processing ISDS response on GetListOfReceivedMessages "
6591 "request failed\n")
6593 goto leave;
6596 /* Check for response status */
6597 err = isds_response_status(context, SERVICE_DM_INFO, response,
6598 &code, &message, NULL);
6599 if (err) {
6600 isds_log(ILF_ISDS, ILL_DEBUG,
6601 (outgoing_direction) ?
6602 _("ISDS response on GetListOfSentMessages request "
6603 "is missing status\n") :
6604 _("ISDS response on GetListOfReceivedMessages request "
6605 "is missing status\n")
6607 goto leave;
6610 /* Request processed, but nothing found */
6611 if (xmlStrcmp(code, BAD_CAST "0000")) {
6612 char *code_locale = _isds_utf82locale((char*)code);
6613 char *message_locale = _isds_utf82locale((char*)message);
6614 isds_log(ILF_ISDS, ILL_DEBUG,
6615 (outgoing_direction) ?
6616 _("Server refused GetListOfSentMessages request "
6617 "(code=%s, message=%s)\n") :
6618 _("Server refused GetListOfReceivedMessages request "
6619 "(code=%s, message=%s)\n"),
6620 code_locale, message_locale);
6621 isds_log_message(context, message_locale);
6622 free(code_locale);
6623 free(message_locale);
6624 err = IE_ISDS;
6625 goto leave;
6629 /* Extract data */
6630 xpath_ctx = xmlXPathNewContext(response);
6631 if (!xpath_ctx) {
6632 err = IE_ERROR;
6633 goto leave;
6635 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6636 err = IE_ERROR;
6637 goto leave;
6639 result = xmlXPathEvalExpression(
6640 (outgoing_direction) ?
6641 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
6642 "isds:dmRecords/isds:dmRecord" :
6643 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
6644 "isds:dmRecords/isds:dmRecord",
6645 xpath_ctx);
6646 if (!result) {
6647 err = IE_ERROR;
6648 goto leave;
6651 /* Fill output arguments in */
6652 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6653 struct isds_envelope *envelope;
6654 struct isds_list *item = NULL, *last_item = NULL;
6656 for (count = 0; count < result->nodesetval->nodeNr; count++) {
6657 /* Create new message */
6658 item = calloc(1, sizeof(*item));
6659 if (!item) {
6660 err = IE_NOMEM;
6661 goto leave;
6663 item->destructor = (void(*)(void**)) &isds_message_free;
6664 item->data = calloc(1, sizeof(struct isds_message));
6665 if (!item->data) {
6666 isds_list_free(&item);
6667 err = IE_NOMEM;
6668 goto leave;
6671 /* Extract envelope data */
6672 xpath_ctx->node = result->nodesetval->nodeTab[count];
6673 envelope = NULL;
6674 err = extract_DmRecord(context, &envelope, xpath_ctx);
6675 if (err) {
6676 isds_list_free(&item);
6677 goto leave;
6680 /* Attach extracted envelope */
6681 ((struct isds_message *) item->data)->envelope = envelope;
6683 /* Append new message into the list */
6684 if (!*messages) {
6685 *messages = last_item = item;
6686 } else {
6687 last_item->next = item;
6688 last_item = item;
6692 if (number) *number = count;
6694 leave:
6695 if (err) {
6696 isds_list_free(messages);
6699 free(string);
6700 xmlXPathFreeObject(result);
6701 xmlXPathFreeContext(xpath_ctx);
6703 free(code);
6704 free(message);
6705 xmlFreeDoc(response);
6706 xmlFreeNode(request);
6708 if (!err)
6709 isds_log(ILF_ISDS, ILL_DEBUG,
6710 (outgoing_direction) ?
6711 _("GetListOfSentMessages request processed by server "
6712 "successfully.\n") :
6713 _("GetListOfReceivedMessages request processed by server "
6714 "successfully.\n")
6716 return err;
6720 /* Get list of outgoing (already sent) messages.
6721 * Any criterion argument can be NULL, if you don't care about it.
6722 * @context is session context. Must not be NULL.
6723 * @from_time is minimal time and date of message sending inclusive.
6724 * @to_time is maximal time and date of message sending inclusive
6725 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
6726 * @status_filter is bit field of isds_message_status values. Use special
6727 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
6728 * all values, you can use bitwise arithmetic if you want.)
6729 * @offset is index of first message we are interested in. First message is 1.
6730 * Set to 0 (or 1) if you don't care.
6731 * @number is maximal length of list you want to get as input value, outputs
6732 * number of messages matching these criteria. Can be NULL if you don't care
6733 * (applies to output value either).
6734 * @messages is automatically reallocated list of isds_message's. Be ware that
6735 * it returns only brief overview (envelope and some other fields) about each
6736 * message, not the complete message. FIXME: Specify exact fields.
6737 * The list is sorted by delivery time in ascending order.
6738 * Use NULL if you don't care about the metadata (useful if you want to know
6739 * only the @number). If you provide &NULL, list will be allocated on heap,
6740 * if you provide pointer to non-NULL, list will be freed automacally at first.
6741 * Also in case of error the list will be NULLed.
6742 * @return IE_SUCCESS or appropriate error code. */
6743 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
6744 const struct timeval *from_time, const struct timeval *to_time,
6745 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
6746 const unsigned long int offset, unsigned long int *number,
6747 struct isds_list **messages) {
6749 return isds_get_list_of_messages(
6750 context, 1,
6751 from_time, to_time, dmSenderOrgUnitNum, status_filter,
6752 offset, number,
6753 messages);
6757 /* Get list of incoming (addressed to you) messages.
6758 * Any criterion argument can be NULL, if you don't care about it.
6759 * @context is session context. Must not be NULL.
6760 * @from_time is minimal time and date of message sending inclusive.
6761 * @to_time is maximal time and date of message sending inclusive
6762 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
6763 * @status_filter is bit field of isds_message_status values. Use special
6764 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
6765 * all values, you can use bitwise arithmetic if you want.)
6766 * @offset is index of first message we are interested in. First message is 1.
6767 * Set to 0 (or 1) if you don't care.
6768 * @number is maximal length of list you want to get as input value, outputs
6769 * number of messages matching these criteria. Can be NULL if you don't care
6770 * (applies to output value either).
6771 * @messages is automatically reallocated list of isds_message's. Be ware that
6772 * it returns only brief overview (envelope and some other fields) about each
6773 * message, not the complete message. FIXME: Specify exact fields.
6774 * Use NULL if you don't care about the metadata (useful if you want to know
6775 * only the @number). If you provide &NULL, list will be allocated on heap,
6776 * if you provide pointer to non-NULL, list will be freed automacally at first.
6777 * Also in case of error the list will be NULLed.
6778 * @return IE_SUCCESS or appropriate error code. */
6779 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
6780 const struct timeval *from_time, const struct timeval *to_time,
6781 const long int *dmRecipientOrgUnitNum,
6782 const unsigned int status_filter,
6783 const unsigned long int offset, unsigned long int *number,
6784 struct isds_list **messages) {
6786 return isds_get_list_of_messages(
6787 context, 0,
6788 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
6789 offset, number,
6790 messages);
6794 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
6795 * code
6796 * @context is session context
6797 * @service is ISDS WS service handler
6798 * @service_name is name of SERVICE_DM_OPERATIONS
6799 * @message_id is message ID to send as service argument to ISDS
6800 * @response is server SOAP body response as XML document
6801 * @raw_response is automatically reallocated bitstream with response body. Use
6802 * NULL if you don't care
6803 * @raw_response_length is size of @raw_response in bytes
6804 * @code is ISDS status code
6805 * @status_message is ISDS status message
6806 * @return error coded from lower layer, context message will be set up
6807 * appropriately. */
6808 static isds_error build_send_check_message_request(struct isds_ctx *context,
6809 const isds_service service, const xmlChar *service_name,
6810 const char *message_id,
6811 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
6812 xmlChar **code, xmlChar **status_message) {
6814 isds_error err = IE_SUCCESS;
6815 char *service_name_locale = NULL, *message_id_locale = NULL;
6816 xmlNodePtr request = NULL, node;
6817 xmlNsPtr isds_ns = NULL;
6819 if (!context) return IE_INVALID_CONTEXT;
6820 if (!service_name || !message_id) return IE_INVAL;
6821 if (!response || !code || !status_message) return IE_INVAL;
6822 if (!raw_response_length && raw_response) return IE_INVAL;
6824 /* Free output argument */
6825 xmlFreeDoc(*response); *response = NULL;
6826 if (raw_response) zfree(*raw_response);
6827 free(*code);
6828 free(*status_message);
6831 /* Check if connection is established
6832 * TODO: This check should be done donwstairs. */
6833 if (!context->curl) return IE_CONNECTION_CLOSED;
6835 service_name_locale = _isds_utf82locale((char*)service_name);
6836 message_id_locale = _isds_utf82locale(message_id);
6837 if (!service_name_locale || !message_id_locale) {
6838 err = IE_NOMEM;
6839 goto leave;
6842 /* Build request */
6843 request = xmlNewNode(NULL, service_name);
6844 if (!request) {
6845 isds_printf_message(context,
6846 _("Could not build %s request"), service_name_locale);
6847 err = IE_ERROR;
6848 goto leave;
6850 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6851 if(!isds_ns) {
6852 isds_log_message(context, _("Could not create ISDS name space"));
6853 err = IE_ERROR;
6854 goto leave;
6856 xmlSetNs(request, isds_ns);
6859 /* Add requested ID */
6860 err = validate_message_id_length(context, (xmlChar *) message_id);
6861 if (err) goto leave;
6862 INSERT_STRING(request, "dmID", message_id);
6865 isds_log(ILF_ISDS, ILL_DEBUG,
6866 _("Sending %s request for %s message ID to ISDS\n"),
6867 service_name_locale, message_id_locale);
6869 /* Send request */
6870 err = isds(context, service, request, response,
6871 raw_response, raw_response_length);
6872 xmlFreeNode(request); request = NULL;
6874 if (err) {
6875 isds_log(ILF_ISDS, ILL_DEBUG,
6876 _("Processing ISDS response on %s request failed\n"),
6877 service_name_locale);
6878 goto leave;
6881 /* Check for response status */
6882 err = isds_response_status(context, service, *response,
6883 code, status_message, NULL);
6884 if (err) {
6885 isds_log(ILF_ISDS, ILL_DEBUG,
6886 _("ISDS response on %s request is missing status\n"),
6887 service_name_locale);
6888 goto leave;
6891 /* Request processed, but nothing found */
6892 if (xmlStrcmp(*code, BAD_CAST "0000")) {
6893 char *code_locale = _isds_utf82locale((char*) *code);
6894 char *status_message_locale = _isds_utf82locale((char*) *status_message);
6895 isds_log(ILF_ISDS, ILL_DEBUG,
6896 _("Server refused %s request for %s message ID "
6897 "(code=%s, message=%s)\n"),
6898 service_name_locale, message_id_locale,
6899 code_locale, status_message_locale);
6900 isds_log_message(context, status_message_locale);
6901 free(code_locale);
6902 free(status_message_locale);
6903 err = IE_ISDS;
6904 goto leave;
6907 leave:
6908 free(message_id_locale);
6909 free(service_name_locale);
6910 xmlFreeNode(request);
6911 return err;
6915 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
6916 * signed data and free ISDS response.
6917 * @context is session context
6918 * @message_id is UTF-8 encoded message ID for loging purpose
6919 * @response is parsed XML document. It will be freed and NULLed in the middle
6920 * of function run to save memmory. This is not guaranted in case of error.
6921 * @request_name is name of ISDS request used to construct response root
6922 * element name and for logging purpose.
6923 * @raw is reallocated output buffer with DER encoded CMS data
6924 * @raw_length is size of @raw buffer in bytes
6925 * @returns standard error codes, in case of error, @raw will be freed and
6926 * NULLed, @response sometimes. */
6927 static isds_error find_extract_signed_data_free_response(
6928 struct isds_ctx *context, const xmlChar *message_id,
6929 xmlDocPtr *response, const xmlChar *request_name,
6930 void **raw, size_t *raw_length) {
6932 isds_error err = IE_SUCCESS;
6933 char *xpath_expression = NULL;
6934 xmlXPathContextPtr xpath_ctx = NULL;
6935 xmlXPathObjectPtr result = NULL;
6936 char *encoded_structure = NULL;
6938 if (!context) return IE_INVALID_CONTEXT;
6939 if (!raw) return IE_INVAL;
6940 zfree(*raw);
6941 if (!message_id || !response || !*response || !request_name || !raw_length)
6942 return IE_INVAL;
6944 /* Build XPath expression */
6945 xpath_expression = _isds_astrcat3("/isds:", (char *) request_name,
6946 "Response/isds:dmSignature");
6947 if (!xpath_expression) return IE_NOMEM;
6949 /* Extract data */
6950 xpath_ctx = xmlXPathNewContext(*response);
6951 if (!xpath_ctx) {
6952 err = IE_ERROR;
6953 goto leave;
6955 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6956 err = IE_ERROR;
6957 goto leave;
6959 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
6960 if (!result) {
6961 err = IE_ERROR;
6962 goto leave;
6964 /* Empty response */
6965 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6966 char *message_id_locale = _isds_utf82locale((char*) message_id);
6967 isds_printf_message(context,
6968 _("Server did not return any signed data for mesage ID `%s' "
6969 "on %s request"),
6970 message_id_locale, request_name);
6971 free(message_id_locale);
6972 err = IE_ISDS;
6973 goto leave;
6975 /* More reponses */
6976 if (result->nodesetval->nodeNr > 1) {
6977 char *message_id_locale = _isds_utf82locale((char*) message_id);
6978 isds_printf_message(context,
6979 _("Server did return more signed data for message ID `%s' "
6980 "on %s request"),
6981 message_id_locale, request_name);
6982 free(message_id_locale);
6983 err = IE_ISDS;
6984 goto leave;
6986 /* One response */
6987 xpath_ctx->node = result->nodesetval->nodeTab[0];
6989 /* Extract PKCS#7 structure */
6990 EXTRACT_STRING(".", encoded_structure);
6991 if (!encoded_structure) {
6992 isds_log_message(context, _("dmSignature element is empty"));
6995 /* Here we have delivery info as standalone CMS in encoded_structure.
6996 * We don't need any other data, free them: */
6997 xmlXPathFreeObject(result); result = NULL;
6998 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
6999 xmlFreeDoc(*response); *response = NULL;
7002 /* Decode PKCS#7 to DER format */
7003 *raw_length = _isds_b64decode(encoded_structure, raw);
7004 if (*raw_length == (size_t) -1) {
7005 isds_log_message(context,
7006 _("Error while Base64-decoding PKCS#7 structure"));
7007 err = IE_ERROR;
7008 goto leave;
7011 leave:
7012 if (err) {
7013 zfree(*raw);
7014 raw_length = 0;
7017 free(encoded_structure);
7018 xmlXPathFreeObject(result);
7019 xmlXPathFreeContext(xpath_ctx);
7020 free(xpath_expression);
7022 return err;
7026 /* Download incoming message envelope identified by ID.
7027 * @context is session context
7028 * @message_id is message identifier (you can get them from
7029 * isds_get_list_of_received_messages())
7030 * @message is automatically reallocated message retrieved from ISDS.
7031 * It will miss documents per se. Use isds_get_received_message(), if you are
7032 * interrested in documents (content) too.
7033 * Returned hash and timestamp require documents to be verifiable. */
7034 isds_error isds_get_received_envelope(struct isds_ctx *context,
7035 const char *message_id, struct isds_message **message) {
7037 isds_error err = IE_SUCCESS;
7038 xmlDocPtr response = NULL;
7039 xmlChar *code = NULL, *status_message = NULL;
7040 xmlXPathContextPtr xpath_ctx = NULL;
7041 xmlXPathObjectPtr result = NULL;
7043 if (!context) return IE_INVALID_CONTEXT;
7044 zfree(context->long_message);
7046 /* Free former message if any */
7047 if (!message) return IE_INVAL;
7048 isds_message_free(message);
7050 /* Do request and check for success */
7051 err = build_send_check_message_request(context, SERVICE_DM_INFO,
7052 BAD_CAST "MessageEnvelopeDownload", message_id,
7053 &response, NULL, NULL, &code, &status_message);
7054 if (err) goto leave;
7056 /* Extract data */
7057 xpath_ctx = xmlXPathNewContext(response);
7058 if (!xpath_ctx) {
7059 err = IE_ERROR;
7060 goto leave;
7062 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7063 err = IE_ERROR;
7064 goto leave;
7066 result = xmlXPathEvalExpression(
7067 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
7068 "isds:dmReturnedMessageEnvelope",
7069 xpath_ctx);
7070 if (!result) {
7071 err = IE_ERROR;
7072 goto leave;
7074 /* Empty response */
7075 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7076 char *message_id_locale = _isds_utf82locale((char*) message_id);
7077 isds_printf_message(context,
7078 _("Server did not return any envelope for ID `%s' "
7079 "on MessageEnvelopeDownload request"), message_id_locale);
7080 free(message_id_locale);
7081 err = IE_ISDS;
7082 goto leave;
7084 /* More envelops */
7085 if (result->nodesetval->nodeNr > 1) {
7086 char *message_id_locale = _isds_utf82locale((char*) message_id);
7087 isds_printf_message(context,
7088 _("Server did return more envelopes for ID `%s' "
7089 "on MessageEnvelopeDownload request"), message_id_locale);
7090 free(message_id_locale);
7091 err = IE_ISDS;
7092 goto leave;
7094 /* One message */
7095 xpath_ctx->node = result->nodesetval->nodeTab[0];
7097 /* Extract the envelope (= message without documents, hence 0) */
7098 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
7099 if (err) goto leave;
7101 /* Save XML blob */
7102 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
7103 &(*message)->raw_length);
7105 leave:
7106 if (err) {
7107 isds_message_free(message);
7110 xmlXPathFreeObject(result);
7111 xmlXPathFreeContext(xpath_ctx);
7113 free(code);
7114 free(status_message);
7115 if (!*message || !(*message)->xml) {
7116 xmlFreeDoc(response);
7119 if (!err)
7120 isds_log(ILF_ISDS, ILL_DEBUG,
7121 _("MessageEnvelopeDownload request processed by server "
7122 "successfully.\n")
7124 return err;
7128 /* Load delivery info of any format from buffer.
7129 * @context is session context
7130 * @raw_type advertises format of @buffer content. Only delivery info types
7131 * are accepted.
7132 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
7133 * retrieve such data from message->raw after calling
7134 * isds_get_signed_delivery_info().
7135 * @length is length of buffer in bytes.
7136 * @message is automatically reallocated message parsed from @buffer.
7137 * @strategy selects how buffer will be attached into raw isds_message member.
7138 * */
7139 isds_error isds_load_delivery_info(struct isds_ctx *context,
7140 const isds_raw_type raw_type,
7141 const void *buffer, const size_t length,
7142 struct isds_message **message, const isds_buffer_strategy strategy) {
7144 isds_error err = IE_SUCCESS;
7145 message_ns_type message_ns;
7146 xmlDocPtr message_doc = NULL;
7147 xmlXPathContextPtr xpath_ctx = NULL;
7148 xmlXPathObjectPtr result = NULL;
7149 void *xml_stream = NULL;
7150 size_t xml_stream_length = 0;
7152 if (!context) return IE_INVALID_CONTEXT;
7153 zfree(context->long_message);
7154 if (!message) return IE_INVAL;
7155 isds_message_free(message);
7156 if (!buffer) return IE_INVAL;
7159 /* Select buffer format and extract XML from CMS*/
7160 switch (raw_type) {
7161 case RAWTYPE_DELIVERYINFO:
7162 message_ns = MESSAGE_NS_UNSIGNED;
7163 xml_stream = (void *) buffer;
7164 xml_stream_length = length;
7165 break;
7167 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
7168 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
7169 xml_stream = (void *) buffer;
7170 xml_stream_length = length;
7171 break;
7173 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
7174 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
7175 err = _isds_extract_cms_data(context, buffer, length,
7176 &xml_stream, &xml_stream_length);
7177 if (err) goto leave;
7178 break;
7180 default:
7181 isds_log_message(context, _("Bad raw delivery representation type"));
7182 return IE_INVAL;
7183 break;
7186 isds_log(ILF_ISDS, ILL_DEBUG,
7187 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
7188 xml_stream_length, xml_stream);
7190 /* Convert delivery info XML stream into XPath context */
7191 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
7192 if (!message_doc) {
7193 err = IE_XML;
7194 goto leave;
7196 xpath_ctx = xmlXPathNewContext(message_doc);
7197 if (!xpath_ctx) {
7198 err = IE_ERROR;
7199 goto leave;
7201 /* XXX: Name spaces mangled for signed delivery info:
7202 * http://isds.czechpoint.cz/v20/delivery:
7204 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
7205 * <q:dmDelivery>
7206 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
7207 * <p:dmID>170272</p:dmID>
7208 * ...
7209 * </p:dmDm>
7210 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
7211 * ...
7212 * </q:dmEvents>...</q:dmEvents>
7213 * </q:dmDelivery>
7214 * </q:GetDeliveryInfoResponse>
7215 * */
7216 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
7217 err = IE_ERROR;
7218 goto leave;
7220 result = xmlXPathEvalExpression(
7221 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
7222 xpath_ctx);
7223 if (!result) {
7224 err = IE_ERROR;
7225 goto leave;
7227 /* Empty delivery info */
7228 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7229 isds_printf_message(context,
7230 _("XML document is not sisds:dmDelivery document"));
7231 err = IE_ISDS;
7232 goto leave;
7234 /* More delivery infos */
7235 if (result->nodesetval->nodeNr > 1) {
7236 isds_printf_message(context,
7237 _("XML document has more sisds:dmDelivery elements"));
7238 err = IE_ISDS;
7239 goto leave;
7241 /* One delivery info */
7242 xpath_ctx->node = result->nodesetval->nodeTab[0];
7244 /* Extract the envelope (= message without documents, hence 0).
7245 * XXX: extract_TReturnedMessage() can obtain attachments size,
7246 * but delivery info carries none. It's coded as option elements,
7247 * so it should work. */
7248 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
7249 if (err) goto leave;
7251 /* Extract events */
7252 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
7253 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
7254 if (err) { err = IE_ERROR; goto leave; }
7255 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
7256 if (err) goto leave;
7258 /* Append raw CMS structure into message */
7259 (*message)->raw_type = raw_type;
7260 switch (strategy) {
7261 case BUFFER_DONT_STORE:
7262 break;
7263 case BUFFER_COPY:
7264 (*message)->raw = malloc(length);
7265 if (!(*message)->raw) {
7266 err = IE_NOMEM;
7267 goto leave;
7269 memcpy((*message)->raw, buffer, length);
7270 (*message)->raw_length = length;
7271 break;
7272 case BUFFER_MOVE:
7273 (*message)->raw = (void *) buffer;
7274 (*message)->raw_length = length;
7275 break;
7276 default:
7277 err = IE_ENUM;
7278 goto leave;
7281 leave:
7282 if (err) {
7283 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
7284 isds_message_free(message);
7287 xmlXPathFreeObject(result);
7288 xmlXPathFreeContext(xpath_ctx);
7289 if (!*message || !(*message)->xml) {
7290 xmlFreeDoc(message_doc);
7292 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
7294 if (!err)
7295 isds_log(ILF_ISDS, ILL_DEBUG,
7296 _("Delivery info loaded successfully.\n"));
7297 return err;
7301 /* Download signed delivery infosheet of given message identified by ID.
7302 * @context is session context
7303 * @message_id is message identifier (you can get them from
7304 * isds_get_list_of_{sent,received}_messages())
7305 * @message is automatically reallocated message retrieved from ISDS.
7306 * It will miss documents per se. Use isds_get_signed_received_message(),
7307 * if you are interrested in documents (content). OTOH, only this function
7308 * can get list events message has gone through. */
7309 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
7310 const char *message_id, struct isds_message **message) {
7312 isds_error err = IE_SUCCESS;
7313 xmlDocPtr response = NULL;
7314 xmlChar *code = NULL, *status_message = NULL;
7315 void *raw = NULL;
7316 size_t raw_length = 0;
7318 if (!context) return IE_INVALID_CONTEXT;
7319 zfree(context->long_message);
7321 /* Free former message if any */
7322 if (!message) return IE_INVAL;
7323 isds_message_free(message);
7325 /* Do request and check for success */
7326 err = build_send_check_message_request(context, SERVICE_DM_INFO,
7327 BAD_CAST "GetSignedDeliveryInfo", message_id,
7328 &response, NULL, NULL, &code, &status_message);
7329 if (err) goto leave;
7331 /* Find signed delivery info, extract it into raw and maybe free
7332 * response */
7333 err = find_extract_signed_data_free_response(context,
7334 (xmlChar *)message_id, &response,
7335 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
7336 if (err) goto leave;
7338 /* Parse delivery info */
7339 err = isds_load_delivery_info(context,
7340 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
7341 message, BUFFER_MOVE);
7342 if (err) goto leave;
7344 raw = NULL;
7346 leave:
7347 if (err) {
7348 isds_message_free(message);
7351 free(raw);
7352 free(code);
7353 free(status_message);
7354 xmlFreeDoc(response);
7356 if (!err)
7357 isds_log(ILF_ISDS, ILL_DEBUG,
7358 _("GetSignedDeliveryInfo request processed by server "
7359 "successfully.\n")
7361 return err;
7365 /* Download delivery infosheet of given message identified by ID.
7366 * @context is session context
7367 * @message_id is message identifier (you can get them from
7368 * isds_get_list_of_{sent,received}_messages())
7369 * @message is automatically reallocated message retrieved from ISDS.
7370 * It will miss documents per se. Use isds_get_received_message(), if you are
7371 * interrested in documents (content). OTOH, only this function can get list
7372 * of events message has gone through. */
7373 isds_error isds_get_delivery_info(struct isds_ctx *context,
7374 const char *message_id, struct isds_message **message) {
7376 isds_error err = IE_SUCCESS;
7377 xmlDocPtr response = NULL;
7378 xmlChar *code = NULL, *status_message = NULL;
7379 xmlNodePtr delivery_node = NULL;
7380 void *raw = NULL;
7381 size_t raw_length = 0;
7383 if (!context) return IE_INVALID_CONTEXT;
7384 zfree(context->long_message);
7386 /* Free former message if any */
7387 if (!message) return IE_INVAL;
7388 isds_message_free(message);
7390 /* Do request and check for success */
7391 err = build_send_check_message_request(context, SERVICE_DM_INFO,
7392 BAD_CAST "GetDeliveryInfo", message_id,
7393 &response, NULL, NULL, &code, &status_message);
7394 if (err) goto leave;
7397 /* Serialize delivery info */
7398 delivery_node = xmlDocGetRootElement(response);
7399 if (!delivery_node) {
7400 char *message_id_locale = _isds_utf82locale((char*) message_id);
7401 isds_printf_message(context,
7402 _("Server did not return any delivery info for ID `%s' "
7403 "on GetDeliveryInfo request"), message_id_locale);
7404 free(message_id_locale);
7405 err = IE_ISDS;
7406 goto leave;
7408 err = serialize_subtree(context, delivery_node, &raw, &raw_length);
7409 if (err) goto leave;
7411 /* Parse delivery info */
7412 /* TODO: Here we parse the reponse second time. We could single delivery
7413 * parser from isds_load_delivery_info() to make things faster. */
7414 err = isds_load_delivery_info(context,
7415 RAWTYPE_DELIVERYINFO, raw, raw_length,
7416 message, BUFFER_MOVE);
7417 if (err) goto leave;
7419 raw = NULL;
7422 leave:
7423 if (err) {
7424 isds_message_free(message);
7427 free(raw);
7428 free(code);
7429 free(status_message);
7430 xmlFreeDoc(response);
7432 if (!err)
7433 isds_log(ILF_ISDS, ILL_DEBUG,
7434 _("GetDeliveryInfo request processed by server "
7435 "successfully.\n")
7437 return err;
7441 /* Download incoming message identified by ID.
7442 * @context is session context
7443 * @message_id is message identifier (you can get them from
7444 * isds_get_list_of_received_messages())
7445 * @message is automatically reallocated message retrieved from ISDS */
7446 isds_error isds_get_received_message(struct isds_ctx *context,
7447 const char *message_id, struct isds_message **message) {
7449 isds_error err = IE_SUCCESS;
7450 xmlDocPtr response = NULL;
7451 void *xml_stream = NULL;
7452 size_t xml_stream_length;
7453 xmlChar *code = NULL, *status_message = NULL;
7454 xmlXPathContextPtr xpath_ctx = NULL;
7455 xmlXPathObjectPtr result = NULL;
7456 char *phys_path = NULL;
7457 size_t phys_start, phys_end;
7459 if (!context) return IE_INVALID_CONTEXT;
7460 zfree(context->long_message);
7462 /* Free former message if any */
7463 if (message) isds_message_free(message);
7465 /* Do request and check for success */
7466 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
7467 BAD_CAST "MessageDownload", message_id,
7468 &response, &xml_stream, &xml_stream_length,
7469 &code, &status_message);
7470 if (err) goto leave;
7472 /* Extract data */
7473 xpath_ctx = xmlXPathNewContext(response);
7474 if (!xpath_ctx) {
7475 err = IE_ERROR;
7476 goto leave;
7478 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7479 err = IE_ERROR;
7480 goto leave;
7482 result = xmlXPathEvalExpression(
7483 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
7484 xpath_ctx);
7485 if (!result) {
7486 err = IE_ERROR;
7487 goto leave;
7489 /* Empty response */
7490 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7491 char *message_id_locale = _isds_utf82locale((char*) message_id);
7492 isds_printf_message(context,
7493 _("Server did not return any message for ID `%s' "
7494 "on MessageDownload request"), message_id_locale);
7495 free(message_id_locale);
7496 err = IE_ISDS;
7497 goto leave;
7499 /* More messages */
7500 if (result->nodesetval->nodeNr > 1) {
7501 char *message_id_locale = _isds_utf82locale((char*) message_id);
7502 isds_printf_message(context,
7503 _("Server did return more messages for ID `%s' "
7504 "on MessageDownload request"), message_id_locale);
7505 free(message_id_locale);
7506 err = IE_ISDS;
7507 goto leave;
7509 /* One message */
7510 xpath_ctx->node = result->nodesetval->nodeTab[0];
7512 /* Extract the message */
7513 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
7514 if (err) goto leave;
7516 /* Locate raw XML blob */
7517 phys_path = strdup(
7518 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
7519 PHYSXML_ELEMENT_SEPARATOR
7520 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
7521 PHYSXML_ELEMENT_SEPARATOR
7522 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
7524 if (!phys_path) {
7525 err = IE_NOMEM;
7526 goto leave;
7528 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
7529 phys_path, &phys_start, &phys_end);
7530 zfree(phys_path);
7531 if (err) {
7532 isds_log_message(context,
7533 _("Substring with isds:MessageDownloadResponse element "
7534 "could not be located in raw SOAP message"));
7535 goto leave;
7537 /* Save XML blob */
7538 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
7539 &(*message)->raw_length);*/
7540 /* TODO: Store name space declarations from ancestors */
7541 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
7542 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
7543 (*message)->raw_length = phys_end - phys_start + 1;
7544 (*message)->raw = malloc((*message)->raw_length);
7545 if (!(*message)->raw) {
7546 err = IE_NOMEM;
7547 goto leave;
7549 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
7552 leave:
7553 if (err) {
7554 isds_message_free(message);
7557 free(phys_path);
7559 xmlXPathFreeObject(result);
7560 xmlXPathFreeContext(xpath_ctx);
7562 free(code);
7563 free(status_message);
7564 free(xml_stream);
7565 if (!*message || !(*message)->xml) {
7566 xmlFreeDoc(response);
7569 if (!err)
7570 isds_log(ILF_ISDS, ILL_DEBUG,
7571 _("MessageDownload request processed by server "
7572 "successfully.\n")
7574 return err;
7578 /* Load message of any type from buffer.
7579 * @context is session context
7580 * @raw_type defines content type of @buffer. Only message types are allowed.
7581 * @buffer is message raw representation. Format (CMS, plain signed,
7582 * message direction) is defined in @raw_type. You can retrieve such data
7583 * from message->raw after calling isds_get_[signed]{received,sent}_message().
7584 * @length is length of buffer in bytes.
7585 * @message is automatically reallocated message parsed from @buffer.
7586 * @strategy selects how buffer will be attached into raw isds_message member.
7587 * */
7588 isds_error isds_load_message(struct isds_ctx *context,
7589 const isds_raw_type raw_type, const void *buffer, const size_t length,
7590 struct isds_message **message, const isds_buffer_strategy strategy) {
7592 isds_error err = IE_SUCCESS;
7593 void *xml_stream = NULL;
7594 size_t xml_stream_length = 0;
7595 message_ns_type message_ns;
7596 xmlDocPtr message_doc = NULL;
7597 xmlXPathContextPtr xpath_ctx = NULL;
7598 xmlXPathObjectPtr result = NULL;
7600 if (!context) return IE_INVALID_CONTEXT;
7601 zfree(context->long_message);
7602 if (!message) return IE_INVAL;
7603 isds_message_free(message);
7604 if (!buffer) return IE_INVAL;
7607 /* Select buffer format and extract XML from CMS*/
7608 switch (raw_type) {
7609 case RAWTYPE_INCOMING_MESSAGE:
7610 message_ns = MESSAGE_NS_UNSIGNED;
7611 xml_stream = (void *) buffer;
7612 xml_stream_length = length;
7613 break;
7615 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
7616 message_ns = MESSAGE_NS_SIGNED_INCOMING;
7617 xml_stream = (void *) buffer;
7618 xml_stream_length = length;
7619 break;
7621 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
7622 message_ns = MESSAGE_NS_SIGNED_INCOMING;
7623 err = _isds_extract_cms_data(context, buffer, length,
7624 &xml_stream, &xml_stream_length);
7625 if (err) goto leave;
7626 break;
7628 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
7629 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
7630 xml_stream = (void *) buffer;
7631 xml_stream_length = length;
7632 break;
7634 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
7635 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
7636 err = _isds_extract_cms_data(context, buffer, length,
7637 &xml_stream, &xml_stream_length);
7638 if (err) goto leave;
7639 break;
7641 default:
7642 isds_log_message(context, _("Bad raw message representation type"));
7643 return IE_INVAL;
7644 break;
7647 isds_log(ILF_ISDS, ILL_DEBUG,
7648 _("Loading message:\n%.*s\nEnd of message\n"),
7649 xml_stream_length, xml_stream);
7651 /* Convert messages XML stream into XPath context */
7652 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
7653 if (!message_doc) {
7654 err = IE_XML;
7655 goto leave;
7657 xpath_ctx = xmlXPathNewContext(message_doc);
7658 if (!xpath_ctx) {
7659 err = IE_ERROR;
7660 goto leave;
7662 /* XXX: Standard name space for unsigned incoming direction:
7663 * http://isds.czechpoint.cz/v20/
7665 * XXX: Name spaces mangled for signed outgoing direction:
7666 * http://isds.czechpoint.cz/v20/SentMessage:
7668 * <q:MessageDownloadResponse
7669 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
7670 * <q:dmReturnedMessage>
7671 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
7672 * <p:dmID>151916</p:dmID>
7673 * ...
7674 * </p:dmDm>
7675 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
7676 * ...
7677 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
7678 * </q:dmReturnedMessage>
7679 * </q:MessageDownloadResponse>
7681 * XXX: Name spaces mangled for signed incoming direction:
7682 * http://isds.czechpoint.cz/v20/message:
7684 * <q:MessageDownloadResponse
7685 * xmlns:q="http://isds.czechpoint.cz/v20/message">
7686 * <q:dmReturnedMessage>
7687 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
7688 * <p:dmID>151916</p:dmID>
7689 * ...
7690 * </p:dmDm>
7691 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
7692 * ...
7693 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
7694 * </q:dmReturnedMessage>
7695 * </q:MessageDownloadResponse>
7697 * Stupidity of ISDS developers is unlimited */
7698 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
7699 err = IE_ERROR;
7700 goto leave;
7702 result = xmlXPathEvalExpression(
7703 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
7704 xpath_ctx);
7705 if (!result) {
7706 err = IE_ERROR;
7707 goto leave;
7709 /* Empty message */
7710 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7711 isds_printf_message(context,
7712 _("XML document does not contain "
7713 "sisds:dmReturnedMessage element"));
7714 err = IE_ISDS;
7715 goto leave;
7717 /* More messages */
7718 if (result->nodesetval->nodeNr > 1) {
7719 isds_printf_message(context,
7720 _("XML document has more sisds:dmReturnedMessage elements"));
7721 err = IE_ISDS;
7722 goto leave;
7724 /* One message */
7725 xpath_ctx->node = result->nodesetval->nodeTab[0];
7727 /* Extract the message */
7728 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
7729 if (err) goto leave;
7731 /* Append raw buffer into message */
7732 (*message)->raw_type = raw_type;
7733 switch (strategy) {
7734 case BUFFER_DONT_STORE:
7735 break;
7736 case BUFFER_COPY:
7737 (*message)->raw = malloc(length);
7738 if (!(*message)->raw) {
7739 err = IE_NOMEM;
7740 goto leave;
7742 memcpy((*message)->raw, buffer, length);
7743 (*message)->raw_length = length;
7744 break;
7745 case BUFFER_MOVE:
7746 (*message)->raw = (void *) buffer;
7747 (*message)->raw_length = length;
7748 break;
7749 default:
7750 err = IE_ENUM;
7751 goto leave;
7755 leave:
7756 if (err) {
7757 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
7758 isds_message_free(message);
7761 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
7762 xmlXPathFreeObject(result);
7763 xmlXPathFreeContext(xpath_ctx);
7764 if (!*message || !(*message)->xml) {
7765 xmlFreeDoc(message_doc);
7768 if (!err)
7769 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
7770 return err;
7774 /* Determine type of raw message or delivery info according some heuristics.
7775 * It does not validate the raw blob.
7776 * @context is session context
7777 * @raw_type returns content type of @buffer. Valid only if exit code of this
7778 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
7779 * reallocted memory.
7780 * @buffer is message raw representation.
7781 * @length is length of buffer in bytes. */
7782 isds_error isds_guess_raw_type(struct isds_ctx *context,
7783 isds_raw_type *raw_type, const void *buffer, const size_t length) {
7784 isds_error err;
7785 void *xml_stream = NULL;
7786 size_t xml_stream_length = 0;
7787 xmlDocPtr document = NULL;
7788 xmlNodePtr root = NULL;
7790 if (!context) return IE_INVALID_CONTEXT;
7791 zfree(context->long_message);
7792 if (length == 0 || !buffer) return IE_INVAL;
7793 if (!raw_type) return IE_INVAL;
7795 /* Try CMS */
7796 err = _isds_extract_cms_data(context, buffer, length,
7797 &xml_stream, &xml_stream_length);
7798 if (err) {
7799 xml_stream = (void *) buffer;
7800 xml_stream_length = (size_t) length;
7801 err = IE_SUCCESS;
7804 /* Try XML */
7805 document = xmlParseMemory(xml_stream, xml_stream_length);
7806 if (!document) {
7807 isds_printf_message(context,
7808 _("Could not parse data as XML document"));
7809 err = IE_NOTSUP;
7810 goto leave;
7813 /* Get root element */
7814 root = xmlDocGetRootElement(document);
7815 if (!root) {
7816 isds_printf_message(context,
7817 _("XML document is missing root element"));
7818 err = IE_XML;
7819 goto leave;
7822 if (!root->ns || !root->ns->href) {
7823 isds_printf_message(context,
7824 _("Root element does not belong to any name space"));
7825 err = IE_NOTSUP;
7826 goto leave;
7829 /* Test name space */
7830 if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_INCOMING_NS)) {
7831 if (xml_stream == buffer)
7832 *raw_type = RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE;
7833 else
7834 *raw_type = RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
7835 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_OUTGOING_NS)) {
7836 if (xml_stream == buffer)
7837 *raw_type = RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE;
7838 else
7839 *raw_type = RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE;
7840 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_DELIVERY_NS)) {
7841 if (xml_stream == buffer)
7842 *raw_type = RAWTYPE_PLAIN_SIGNED_DELIVERYINFO;
7843 else
7844 *raw_type = RAWTYPE_CMS_SIGNED_DELIVERYINFO;
7845 } else if (!xmlStrcmp(root->ns->href, BAD_CAST ISDS_NS)) {
7846 if (xml_stream != buffer) {
7847 isds_printf_message(context,
7848 _("Document in ISDS name space is encapsulated into CMS" ));
7849 err = IE_NOTSUP;
7850 } else if (!xmlStrcmp(root->name, BAD_CAST "MessageDownloadResponse"))
7851 *raw_type = RAWTYPE_INCOMING_MESSAGE;
7852 else if (!xmlStrcmp(root->name, BAD_CAST "GetDeliveryInfoResponse"))
7853 *raw_type = RAWTYPE_DELIVERYINFO;
7854 else {
7855 isds_printf_message(context,
7856 _("Unknown root element in ISDS name space"));
7857 err = IE_NOTSUP;
7859 } else {
7860 isds_printf_message(context,
7861 _("Uknown namespace"));
7862 err = IE_NOTSUP;
7865 leave:
7866 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
7867 xmlFreeDoc(document);
7868 return err;
7872 /* Download signed incoming/outgoing message identified by ID.
7873 * @context is session context
7874 * @output is true for outging message, false for incoming message
7875 * @message_id is message identifier (you can get them from
7876 * isds_get_list_of_{sent,received}_messages())
7877 * @message is automatically reallocated message retrieved from ISDS. The raw
7878 * memeber will be filled with PKCS#7 structure in DER format. */
7879 static isds_error isds_get_signed_message(struct isds_ctx *context,
7880 const _Bool outgoing, const char *message_id,
7881 struct isds_message **message) {
7883 isds_error err = IE_SUCCESS;
7884 xmlDocPtr response = NULL;
7885 xmlChar *code = NULL, *status_message = NULL;
7886 xmlXPathContextPtr xpath_ctx = NULL;
7887 xmlXPathObjectPtr result = NULL;
7888 char *encoded_structure = NULL;
7889 void *raw = NULL;
7890 size_t raw_length = 0;
7892 if (!context) return IE_INVALID_CONTEXT;
7893 zfree(context->long_message);
7894 if (!message) return IE_INVAL;
7895 isds_message_free(message);
7897 /* Do request and check for success */
7898 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
7899 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
7900 BAD_CAST "SignedMessageDownload",
7901 message_id, &response, NULL, NULL, &code, &status_message);
7902 if (err) goto leave;
7904 /* Find signed message, extract it into raw and maybe free
7905 * response */
7906 err = find_extract_signed_data_free_response(context,
7907 (xmlChar *)message_id, &response,
7908 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
7909 BAD_CAST "SignedMessageDownload",
7910 &raw, &raw_length);
7911 if (err) goto leave;
7913 /* Parse message */
7914 err = isds_load_message(context,
7915 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
7916 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
7917 raw, raw_length, message, BUFFER_MOVE);
7918 if (err) goto leave;
7920 raw = NULL;
7922 leave:
7923 if (err) {
7924 isds_message_free(message);
7927 free(encoded_structure);
7928 xmlXPathFreeObject(result);
7929 xmlXPathFreeContext(xpath_ctx);
7930 free(raw);
7932 free(code);
7933 free(status_message);
7934 xmlFreeDoc(response);
7936 if (!err)
7937 isds_log(ILF_ISDS, ILL_DEBUG,
7938 (outgoing) ?
7939 _("SignedSentMessageDownload request processed by server "
7940 "successfully.\n") :
7941 _("SignedMessageDownload request processed by server "
7942 "successfully.\n")
7944 return err;
7948 /* Download signed incoming message identified by ID.
7949 * @context is session context
7950 * @message_id is message identifier (you can get them from
7951 * isds_get_list_of_received_messages())
7952 * @message is automatically reallocated message retrieved from ISDS. The raw
7953 * memeber will be filled with PKCS#7 structure in DER format. */
7954 isds_error isds_get_signed_received_message(struct isds_ctx *context,
7955 const char *message_id, struct isds_message **message) {
7956 return isds_get_signed_message(context, 0, message_id, message);
7960 /* Download signed outgoing message identified by ID.
7961 * @context is session context
7962 * @message_id is message identifier (you can get them from
7963 * isds_get_list_of_sent_messages())
7964 * @message is automatically reallocated message retrieved from ISDS. The raw
7965 * memeber will be filled with PKCS#7 structure in DER format. */
7966 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
7967 const char *message_id, struct isds_message **message) {
7968 return isds_get_signed_message(context, 1, message_id, message);
7972 /* Retrieve hash of message identified by ID stored in ISDS.
7973 * @context is session context
7974 * @message_id is message identifier
7975 * @hash is automatically reallocated message hash downloaded from ISDS.
7976 * Message must exist in system and must not be deleted. */
7977 isds_error isds_download_message_hash(struct isds_ctx *context,
7978 const char *message_id, struct isds_hash **hash) {
7980 isds_error err = IE_SUCCESS;
7981 xmlDocPtr response = NULL;
7982 xmlChar *code = NULL, *status_message = NULL;
7983 xmlXPathContextPtr xpath_ctx = NULL;
7984 xmlXPathObjectPtr result = NULL;
7986 if (!context) return IE_INVALID_CONTEXT;
7987 zfree(context->long_message);
7989 isds_hash_free(hash);
7991 err = build_send_check_message_request(context, SERVICE_DM_INFO,
7992 BAD_CAST "VerifyMessage", message_id,
7993 &response, NULL, NULL, &code, &status_message);
7994 if (err) goto leave;
7997 /* Extract data */
7998 xpath_ctx = xmlXPathNewContext(response);
7999 if (!xpath_ctx) {
8000 err = IE_ERROR;
8001 goto leave;
8003 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8004 err = IE_ERROR;
8005 goto leave;
8007 result = xmlXPathEvalExpression(
8008 BAD_CAST "/isds:VerifyMessageResponse",
8009 xpath_ctx);
8010 if (!result) {
8011 err = IE_ERROR;
8012 goto leave;
8014 /* Empty response */
8015 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8016 char *message_id_locale = _isds_utf82locale((char*) message_id);
8017 isds_printf_message(context,
8018 _("Server did not return any response for ID `%s' "
8019 "on VerifyMessage request"), message_id_locale);
8020 free(message_id_locale);
8021 err = IE_ISDS;
8022 goto leave;
8024 /* More responses */
8025 if (result->nodesetval->nodeNr > 1) {
8026 char *message_id_locale = _isds_utf82locale((char*) message_id);
8027 isds_printf_message(context,
8028 _("Server did return more responses for ID `%s' "
8029 "on VerifyMessage request"), message_id_locale);
8030 free(message_id_locale);
8031 err = IE_ISDS;
8032 goto leave;
8034 /* One response */
8035 xpath_ctx->node = result->nodesetval->nodeTab[0];
8037 /* Extract the hash */
8038 err = find_and_extract_DmHash(context, hash, xpath_ctx);
8040 leave:
8041 if (err) {
8042 isds_hash_free(hash);
8045 xmlXPathFreeObject(result);
8046 xmlXPathFreeContext(xpath_ctx);
8048 free(code);
8049 free(status_message);
8050 xmlFreeDoc(response);
8052 if (!err)
8053 isds_log(ILF_ISDS, ILL_DEBUG,
8054 _("VerifyMessage request processed by server "
8055 "successfully.\n")
8057 return err;
8061 /* Mark message as read. This is a transactional commit function to acknoledge
8062 * to ISDS the message has been downloaded and processed by client properly.
8063 * @context is session context
8064 * @message_id is message identifier. */
8065 isds_error isds_mark_message_read(struct isds_ctx *context,
8066 const char *message_id) {
8068 isds_error err = IE_SUCCESS;
8069 xmlDocPtr response = NULL;
8070 xmlChar *code = NULL, *status_message = NULL;
8072 if (!context) return IE_INVALID_CONTEXT;
8073 zfree(context->long_message);
8075 /* Do request and check for success */
8076 err = build_send_check_message_request(context, SERVICE_DM_INFO,
8077 BAD_CAST "MarkMessageAsDownloaded", message_id,
8078 &response, NULL, NULL, &code, &status_message);
8080 free(code);
8081 free(status_message);
8082 xmlFreeDoc(response);
8084 if (!err)
8085 isds_log(ILF_ISDS, ILL_DEBUG,
8086 _("MarkMessageAsDownloaded request processed by server "
8087 "successfully.\n")
8089 return err;
8092 /* Mark message as received by recipient. This is applicable only to
8093 * commercial message. There is no specified way how to distinguishe
8094 * commercial message from government message yet. Government message is
8095 * received automatically (by law), commenrcial message on recipient request.
8096 * @context is session context
8097 * @message_id is message identifier. */
8098 isds_error isds_mark_message_received(struct isds_ctx *context,
8099 const char *message_id) {
8101 isds_error err = IE_SUCCESS;
8102 xmlDocPtr response = NULL;
8103 xmlChar *code = NULL, *status_message = NULL;
8105 if (!context) return IE_INVALID_CONTEXT;
8106 zfree(context->long_message);
8108 /* Do request and check for success */
8109 err = build_send_check_message_request(context, SERVICE_DM_INFO,
8110 BAD_CAST "ConfirmDelivery", message_id,
8111 &response, NULL, NULL, &code, &status_message);
8113 free(code);
8114 free(status_message);
8115 xmlFreeDoc(response);
8117 if (!err)
8118 isds_log(ILF_ISDS, ILL_DEBUG,
8119 _("ConfirmDelivery request processed by server "
8120 "successfully.\n")
8122 return err;
8126 /* Send document for authorize conversion into Czech POINT system.
8127 * This is public anonymous service, no login necessary. Special context is
8128 * used to reuse keep-a-live HTTPS connection.
8129 * @context is Czech POINT session context. DO NOT use context connected to
8130 * ISDS server. Use new context or context used by this function previously.
8131 * @document is document to convert. Only data, data_length and dmFileDescr
8132 * memebers are signifact. Be ware that not all document formats can be
8133 * converted (signed PDF 1.3 and higher only (2010-02 state)).
8134 * @id is reallocated identifier assigned by Czech POINT system to
8135 * your document on submit. Use is to tell it to Czech POINT officer.
8136 * @date is reallocated document submit date (submitted documents
8137 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
8138 * value. */
8139 isds_error czp_convert_document(struct isds_ctx *context,
8140 const struct isds_document *document,
8141 char **id, struct tm **date) {
8142 isds_error err = IE_SUCCESS;
8143 xmlNsPtr deposit_ns = NULL, empty_ns = NULL;
8144 xmlNodePtr request = NULL, node;
8145 xmlDocPtr response = NULL;
8146 xmlChar *base64data = NULL;
8148 xmlXPathContextPtr xpath_ctx = NULL;
8149 xmlXPathObjectPtr result = NULL;
8150 long int status = -1;
8151 long int *status_ptr = &status;
8152 char *string = NULL;
8155 if (!context) return IE_INVALID_CONTEXT;
8156 zfree(context->long_message);
8157 if (!document || !id || !date) return IE_INVAL;
8159 /* Free output arguments */
8160 zfree(*id);
8161 zfree(*date);
8163 /* Store configuration */
8164 context->type = CTX_TYPE_CZP;
8165 free(context->url);
8166 context->url = strdup("https://www.czechpoint.cz/uschovna/services.php");
8167 if (!(context->url))
8168 return IE_NOMEM;
8170 /* Prepare CURL handle if not yet connected */
8171 if (!context->curl) {
8172 context->curl = curl_easy_init();
8173 if (!(context->curl))
8174 return IE_ERROR;
8177 /* Build conversion request */
8178 request = xmlNewNode(NULL, BAD_CAST "saveDocument");
8179 if (!request) {
8180 isds_log_message(context,
8181 _("Could not build Czech POINT conversion request"));
8182 return IE_ERROR;
8184 deposit_ns = xmlNewNs(request, BAD_CAST DEPOSIT_NS, BAD_CAST "dep");
8185 if(!deposit_ns) {
8186 isds_log_message(context,
8187 _("Could not create Czech POINT deposit name space"));
8188 xmlFreeNode(request);
8189 return IE_ERROR;
8191 xmlSetNs(request, deposit_ns);
8193 /* Insert childern. They are in empty namespace! */
8194 empty_ns = xmlNewNs(request, BAD_CAST "", NULL);
8195 if(!empty_ns) {
8196 isds_log_message(context, _("Could not create empty name space"));
8197 err = IE_ERROR;
8198 goto leave;
8200 INSERT_STRING_WITH_NS(request, empty_ns, "conversionID", "0");
8201 INSERT_STRING_WITH_NS(request, empty_ns, "fileName",
8202 document->dmFileDescr);
8204 /* Document encoded in Base64 */
8205 base64data = (xmlChar *) _isds_b64encode(document->data, document->data_length);
8206 if (!base64data) {
8207 isds_printf_message(context,
8208 ngettext("Not enought memory to encode %zd bytes into Base64",
8209 "Not enought memory to encode %zd bytes into Base64",
8210 document->data_length),
8211 document->data_length);
8212 err = IE_NOMEM;
8213 goto leave;
8215 INSERT_STRING_WITH_NS(request, empty_ns, "document", base64data);
8216 zfree(base64data);
8218 isds_log(ILF_ISDS, ILL_DEBUG,
8219 _("Submitting document for conversion into Czech POINT deposit"));
8221 /* Send conversion request */
8222 err = _czp_czpdeposit(context, request, &response);
8223 xmlFreeNode(request); request = NULL;
8225 if (err) {
8226 czp_do_close_connection(context);
8227 goto leave;
8231 /* Extract response */
8232 xpath_ctx = xmlXPathNewContext(response);
8233 if (!xpath_ctx) {
8234 err = IE_ERROR;
8235 goto leave;
8237 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8238 err = IE_ERROR;
8239 goto leave;
8241 result = xmlXPathEvalExpression(
8242 BAD_CAST "/deposit:saveDocumentResponse/return",
8243 xpath_ctx);
8244 if (!result) {
8245 err = IE_ERROR;
8246 goto leave;
8248 /* Empty response */
8249 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8250 isds_printf_message(context,
8251 _("Missing `return' element in Czech POINT deposit response"));
8252 err = IE_ISDS;
8253 goto leave;
8255 /* More responses */
8256 if (result->nodesetval->nodeNr > 1) {
8257 isds_printf_message(context,
8258 _("Multiple `return' element in Czech POINT deposit response"));
8259 err = IE_ISDS;
8260 goto leave;
8262 /* One response */
8263 xpath_ctx->node = result->nodesetval->nodeTab[0];
8265 /* Get status */
8266 EXTRACT_LONGINT("status", status_ptr, 1);
8267 if (status) {
8268 EXTRACT_STRING("statusMsg", string);
8269 char *string_locale = _isds_utf82locale(string);
8270 isds_printf_message(context,
8271 _("Czech POINT deposit refused document for conversion "
8272 "(code=%ld, message=%s)"),
8273 status, string_locale);
8274 free(string_locale);
8275 err = IE_ISDS;
8276 goto leave;
8279 /* Get docuement ID */
8280 EXTRACT_STRING("documentID", *id);
8282 /* Get submit date */
8283 EXTRACT_STRING("dateInserted", string);
8284 if (string) {
8285 *date = calloc(1, sizeof(**date));
8286 if (!*date) {
8287 err = IE_NOMEM;
8288 goto leave;
8290 err = datestring2tm((xmlChar *)string, *date);
8291 if (err) {
8292 if (err == IE_NOTSUP) {
8293 err = IE_ISDS;
8294 char *string_locale = _isds_utf82locale(string);
8295 isds_printf_message(context,
8296 _("Invalid dateInserted value: %s"), string_locale);
8297 free(string_locale);
8299 goto leave;
8303 leave:
8304 free(string);
8305 xmlXPathFreeObject(result);
8306 xmlXPathFreeContext(xpath_ctx);
8308 xmlFreeDoc(response);
8309 free(base64data);
8310 xmlFreeNode(request);
8312 if (!err) {
8313 char *id_locale = _isds_utf82locale((char *) *id);
8314 isds_log(ILF_ISDS, ILL_DEBUG,
8315 _("Document %s has been submitted for conversion "
8316 "to server successfully\n"), id_locale);
8317 free(id_locale);
8319 return err;
8323 /* Close possibly opened connection to Czech POINT document deposit.
8324 * @context is Czech POINT session context. */
8325 isds_error czp_close_connection(struct isds_ctx *context) {
8326 if (!context) return IE_INVALID_CONTEXT;
8327 zfree(context->long_message);
8328 return czp_do_close_connection(context);
8332 /* Send request for new box creation in testing ISDS instance.
8333 * It's not possible to requst for a production box currently, as it
8334 * communicates via e-mail.
8335 * XXX: This function does not work either. Server complains about invalid
8336 * e-mail address.
8337 * XXX: Remove context->type hacks in isds.c and validator.c when removing
8338 * this function
8339 * @context is special session context for box creation request. DO NOT use
8340 * standard context as it could reveal your password. Use fresh new context or
8341 * context previously used by this function.
8342 * @box is box description to create including single primary user (in case of
8343 * FO box type). It outputs box ID assigned by ISDS in dbID element.
8344 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
8345 * box, or contact address of PFO box owner). The email member is mandatory as
8346 * it will be used to deliver credentials.
8347 * @former_names is optional undocumented string. Pass NULL if you don't care.
8348 * @approval is optional external approval of box manipulation
8349 * @refnumber is reallocated serial number of request assigned by ISDS. Use
8350 * NULL, if you don't care.*/
8351 isds_error isds_request_new_testing_box(struct isds_ctx *context,
8352 struct isds_DbOwnerInfo *box, const struct isds_list *users,
8353 const char *former_names, const struct isds_approval *approval,
8354 char **refnumber) {
8355 isds_error err = IE_SUCCESS;
8356 xmlNodePtr request = NULL;
8357 xmlDocPtr response = NULL;
8358 xmlXPathContextPtr xpath_ctx = NULL;
8359 xmlXPathObjectPtr result = NULL;
8362 if (!context) return IE_INVALID_CONTEXT;
8363 zfree(context->long_message);
8364 if (!box) return IE_INVAL;
8366 if (!box->email || box->email[0] == '\0') {
8367 isds_log_message(context, _("E-mail field is mandatory"));
8368 return IE_INVAL;
8371 /* Scratch box ID */
8372 zfree(box->dbID);
8374 /* Store configuration */
8375 context->type = CTX_TYPE_TESTING_REQUEST_COLLECTOR;
8376 free(context->url);
8377 context->url = strdup("http://78.102.19.203/testbox/request_box.php");
8378 if (!(context->url))
8379 return IE_NOMEM;
8381 /* Prepare CURL handle if not yet connected */
8382 if (!context->curl) {
8383 context->curl = curl_easy_init();
8384 if (!(context->curl))
8385 return IE_ERROR;
8388 /* Build CreateDataBox request */
8389 err = build_CreateDBInput_request(context,
8390 &request, BAD_CAST "CreateDataBox",
8391 box, users, (xmlChar *) former_names, NULL, NULL, approval);
8392 if (err) goto leave;
8394 /* Send it to server and process response */
8395 err = send_destroy_request_check_response(context,
8396 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
8397 &response, (xmlChar **) refnumber);
8399 /* Extract box ID */
8400 xpath_ctx = xmlXPathNewContext(response);
8401 if (!xpath_ctx) {
8402 err = IE_ERROR;
8403 goto leave;
8405 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8406 err = IE_ERROR;
8407 goto leave;
8409 EXTRACT_STRING("/isds:CreateDataBoxResponse/dbID", box->dbID);
8411 leave:
8412 xmlXPathFreeObject(result);
8413 xmlXPathFreeContext(xpath_ctx);
8414 xmlFreeDoc(response);
8415 xmlFreeNode(request);
8417 if (!err) {
8418 isds_log(ILF_ISDS, ILL_DEBUG,
8419 _("CreateDataBox request processed by server successfully.\n"));
8422 return err;
8425 #undef INSERT_ELEMENT
8426 #undef CHECK_FOR_STRING_LENGTH
8427 #undef INSERT_STRING_ATTRIBUTE
8428 #undef INSERT_ULONGINTNOPTR
8429 #undef INSERT_ULONGINT
8430 #undef INSERT_LONGINT
8431 #undef INSERT_BOOLEAN
8432 #undef INSERT_SCALAR_BOOLEAN
8433 #undef INSERT_STRING
8434 #undef INSERT_STRING_WITH_NS
8435 #undef EXTRACT_STRING_ATTRIBUTE
8436 #undef EXTRACT_ULONGINT
8437 #undef EXTRACT_LONGINT
8438 #undef EXTRACT_BOOLEAN
8439 #undef EXTRACT_STRING
8442 /* Compute hash of message from raw representation and store it into envelope.
8443 * Original hash structure will be destroyed in envelope.
8444 * @context is session context
8445 * @message is message carrying raw XML message blob
8446 * @algorithm is desired hash algorithm to use */
8447 isds_error isds_compute_message_hash(struct isds_ctx *context,
8448 struct isds_message *message, const isds_hash_algorithm algorithm) {
8449 isds_error err = IE_SUCCESS;
8450 const char *nsuri;
8451 void *xml_stream = NULL;
8452 size_t xml_stream_length;
8453 size_t phys_start, phys_end;
8454 char *phys_path = NULL;
8455 struct isds_hash *new_hash = NULL;
8458 if (!context) return IE_INVALID_CONTEXT;
8459 zfree(context->long_message);
8460 if (!message) return IE_INVAL;
8462 if (!message->raw) {
8463 isds_log_message(context,
8464 _("Message does not carry raw representation"));
8465 return IE_INVAL;
8468 switch (message->raw_type) {
8469 case RAWTYPE_INCOMING_MESSAGE:
8470 nsuri = ISDS_NS;
8471 xml_stream = message->raw;
8472 xml_stream_length = message->raw_length;
8473 break;
8475 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
8476 nsuri = SISDS_INCOMING_NS;
8477 xml_stream = message->raw;
8478 xml_stream_length = message->raw_length;
8479 break;
8481 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
8482 nsuri = SISDS_INCOMING_NS;
8483 err = _isds_extract_cms_data(context,
8484 message->raw, message->raw_length,
8485 &xml_stream, &xml_stream_length);
8486 if (err) goto leave;
8487 break;
8489 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
8490 nsuri = SISDS_OUTGOING_NS;
8491 xml_stream = message->raw;
8492 xml_stream_length = message->raw_length;
8493 break;
8495 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
8496 nsuri = SISDS_OUTGOING_NS;
8497 err = _isds_extract_cms_data(context,
8498 message->raw, message->raw_length,
8499 &xml_stream, &xml_stream_length);
8500 if (err) goto leave;
8501 break;
8503 default:
8504 isds_log_message(context, _("Bad raw representation type"));
8505 return IE_INVAL;
8506 break;
8510 /* XXX: Hash is computed from original string represinting isds:dmDm
8511 * subtree. That means no encoding, white space, xmlns attributes changes.
8512 * In other words, input for hash can be invalid XML stream. */
8513 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
8514 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
8515 PHYSXML_ELEMENT_SEPARATOR,
8516 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
8517 PHYSXML_ELEMENT_SEPARATOR
8518 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
8519 err = IE_NOMEM;
8520 goto leave;
8522 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
8523 phys_path, &phys_start, &phys_end);
8524 zfree(phys_path);
8525 if (err) {
8526 isds_log_message(context,
8527 _("Substring with isds:dmDM element could not be located "
8528 "in raw message"));
8529 goto leave;
8533 /* Compute hash */
8534 new_hash = calloc(1, sizeof(*new_hash));
8535 if (!new_hash) {
8536 err = IE_NOMEM;
8537 goto leave;
8539 new_hash->algorithm = algorithm;
8540 err = _isds_compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
8541 new_hash);
8542 if (err) {
8543 isds_log_message(context, _("Could not compute message hash"));
8544 goto leave;
8547 /* Save computed hash */
8548 if (!message->envelope) {
8549 message->envelope = calloc(1, sizeof(*message->envelope));
8550 if (!message->envelope) {
8551 err = IE_NOMEM;
8552 goto leave;
8555 isds_hash_free(&message->envelope->hash);
8556 message->envelope->hash = new_hash;
8558 leave:
8559 if (err) {
8560 isds_hash_free(&new_hash);
8563 free(phys_path);
8564 if (xml_stream != message->raw) free(xml_stream);
8565 return err;
8569 /* Compare two hashes.
8570 * @h1 is first hash
8571 * @h2 is another hash
8572 * @return
8573 * IE_SUCCESS if hashes equal
8574 * IE_NOTUNIQ if hashes are comparable, but they don't equal
8575 * IE_ENUM if not comparable, but both structures defined
8576 * IE_INVAL if some of the structures are undefined (NULL)
8577 * IE_ERROR if internal error occurs */
8578 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
8579 if (h1 == NULL || h2 == NULL) return IE_INVAL;
8580 if (h1->algorithm != h2->algorithm) return IE_ENUM;
8581 if (h1->length != h2->length) return IE_ERROR;
8582 if (h1->length > 0 && !h1->value) return IE_ERROR;
8583 if (h2->length > 0 && !h2->value) return IE_ERROR;
8585 for (int i = 0; i < h1->length; i++) {
8586 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
8587 return IE_NOTEQUAL;
8589 return IE_SUCCESS;
8593 /* Check message has gone through ISDS by comparing message hash stored in
8594 * ISDS and locally computed hash. You must provide message with valid raw
8595 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
8596 * This is convenient wrapper for isds_download_message_hash(),
8597 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
8598 * @context is session context
8599 * @message is message with valid raw and envelope member; envelope->hash
8600 * member will be changed during funcion run. Use envelope on heap only.
8601 * @return
8602 * IE_SUCCESS if message originates in ISDS
8603 * IE_NOTEQUAL if message is unknown to ISDS
8604 * other code for other errors */
8605 isds_error isds_verify_message_hash(struct isds_ctx *context,
8606 struct isds_message *message) {
8607 isds_error err = IE_SUCCESS;
8608 struct isds_hash *downloaded_hash = NULL;
8610 if (!context) return IE_INVALID_CONTEXT;
8611 zfree(context->long_message);
8612 if (!message) return IE_INVAL;
8614 if (!message->envelope) {
8615 isds_log_message(context,
8616 _("Given message structure is missing envelope"));
8617 return IE_INVAL;
8619 if (!message->raw) {
8620 isds_log_message(context,
8621 _("Given message structure is missing raw representation"));
8622 return IE_INVAL;
8625 err = isds_download_message_hash(context, message->envelope->dmID,
8626 &downloaded_hash);
8627 if (err) goto leave;
8629 err = isds_compute_message_hash(context, message,
8630 downloaded_hash->algorithm);
8631 if (err) goto leave;
8633 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
8635 leave:
8636 isds_hash_free(&downloaded_hash);
8637 return err;
8641 /* Search for document by document ID in list of documents. IDs are compared
8642 * as UTF-8 string.
8643 * @documents is list of isds_documents
8644 * @id is document identifier
8645 * @return first matching document or NULL. */
8646 const struct isds_document *isds_find_document_by_id(
8647 const struct isds_list *documents, const char *id) {
8648 const struct isds_list *item;
8649 const struct isds_document *document;
8651 for (item = documents; item; item = item->next) {
8652 document = (struct isds_document *) item->data;
8653 if (!document) continue;
8655 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
8656 return document;
8659 return NULL;
8663 /* Normalize @mime_type to be proper MIME type.
8664 * ISDS servers passes invalid MIME types (e.g. "pdf"). This function tries to
8665 * guess regular MIME type (e.g. "application/pdf").
8666 * @mime_type is UTF-8 encoded MIME type to fix
8667 * @return original @mime_type if no better interpretation exists, or array to
8668 * constant static UTF-8 encoded string with proper MIME type. */
8669 char *isds_normalize_mime_type(const char* mime_type) {
8670 if (!mime_type) return NULL;
8672 for (int offset = 0;
8673 offset < sizeof(extension_map_mime)/sizeof(extension_map_mime[0]);
8674 offset += 2) {
8675 if (!xmlStrcmp((const xmlChar*) mime_type, extension_map_mime[offset]))
8676 return (char *) extension_map_mime[offset + 1];
8679 return (char *) mime_type;
8683 /* Switch MIME type normalization while message loading. Default state for new
8684 * context is no normalization.
8685 * @normalize use true to switch normalization on, false to switch off */
8686 isds_error isds_set_mime_type_normalization(struct isds_ctx *context,
8687 _Bool normalize) {
8688 if (!context) return IE_INVALID_CONTEXT;
8689 zfree(context->long_message);
8691 context->normalize_mime_type = normalize;
8692 isds_log(ILF_FILE, ILL_INFO, (context->normalize_mime_type) ?
8693 _("MIME type normalization switched on\n") :
8694 _("MIME type normalization switched off\n"));
8695 return IE_SUCCESS;
8699 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
8700 struct isds_message **message);
8701 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
8702 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
8703 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
8704 struct isds_address **address);
8706 int isds_message_free(struct isds_message **message);
8707 int isds_address_free(struct isds_address **address);
8711 /* Makes known all relevant namespaces to given XPath context
8712 * @xpath_ctx is XPath context
8713 * @message_ns selects propper message name space. Unsisnged and signed
8714 * messages and delivery infos differ in prefix and URI. */
8715 _hidden isds_error _isds_register_namespaces(xmlXPathContextPtr xpath_ctx,
8716 const message_ns_type message_ns) {
8717 const xmlChar *message_namespace = NULL;
8719 if (!xpath_ctx) return IE_ERROR;
8721 switch(message_ns) {
8722 case MESSAGE_NS_1:
8723 message_namespace = BAD_CAST ISDS1_NS; break;
8724 case MESSAGE_NS_UNSIGNED:
8725 message_namespace = BAD_CAST ISDS_NS; break;
8726 case MESSAGE_NS_SIGNED_INCOMING:
8727 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
8728 case MESSAGE_NS_SIGNED_OUTGOING:
8729 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
8730 case MESSAGE_NS_SIGNED_DELIVERY:
8731 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
8732 default:
8733 return IE_ENUM;
8736 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
8737 return IE_ERROR;
8738 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
8739 return IE_ERROR;
8740 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
8741 return IE_ERROR;
8742 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
8743 return IE_ERROR;
8744 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
8745 return IE_ERROR;
8746 return IE_SUCCESS;