Ported to MinGW
[libisds.git] / src / isds.c
blobf05e83b5a243c36bf563e350195b5e1a617105d1
1 #include "isds_priv.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 <limits.h> /* Because of LONG_{MIN,MAX} constants */
9 #include "utils.h"
10 #if HAVE_LIBCURL
11 #include "soap.h"
12 #endif
13 #include "validator.h"
14 #include "crypto.h"
15 #include <gpg-error.h> /* Because of ksba or gpgme */
16 #include "physxml.h"
17 #include "system.h"
19 /* Locators */
20 /* Base URL of production ISDS instance */
21 const char isds_locator[] = "https://ws1.mojedatovaschranka.cz/";
22 const char isds_cert_locator[] = "https://ws1c.mojedatovaschranka.cz/";
23 const char isds_otp_locator[] = "https://www.mojedatovaschranka.cz/";
25 /* Base URL of production ISDS instance */
26 const char isds_testing_locator[] = "https://ws1.czebox.cz/";
27 const char isds_cert_testing_locator[] = "https://ws1c.czebox.cz/";
28 const char isds_otp_testing_locator[] = "https://www.czebox.cz/";
30 /* Extension to MIME type map */
31 static const xmlChar *extension_map_mime[] = {
32 BAD_CAST "cer", BAD_CAST "application/x-x509-ca-cert",
33 BAD_CAST "crt", BAD_CAST "application/x-x509-ca-cert",
34 BAD_CAST "der", BAD_CAST "application/x-x509-ca-cert",
35 BAD_CAST "doc", BAD_CAST "application/msword",
36 BAD_CAST "docx", BAD_CAST "application/vnd.openxmlformats-officedocument."
37 "wordprocessingml.document",
38 BAD_CAST "dbf", BAD_CAST "application/octet-stream",
39 BAD_CAST "prj", BAD_CAST "application/octet-stream",
40 BAD_CAST "qix", BAD_CAST "application/octet-stream",
41 BAD_CAST "sbn", BAD_CAST "application/octet-stream",
42 BAD_CAST "sbx", BAD_CAST "application/octet-stream",
43 BAD_CAST "shp", BAD_CAST "application/octet-stream",
44 BAD_CAST "shx", BAD_CAST "application/octet-stream",
45 BAD_CAST "dgn", BAD_CAST "application/octet-stream",
46 BAD_CAST "dwg", BAD_CAST "image/vnd.dwg",
47 BAD_CAST "edi", BAD_CAST "application/edifact",
48 BAD_CAST "fo", BAD_CAST "application/vnd.software602.filler.form+xml",
49 BAD_CAST "gfs", BAD_CAST "application/xml",
50 BAD_CAST "gml", BAD_CAST "application/xml",
51 BAD_CAST "gif", BAD_CAST "image/gif",
52 BAD_CAST "htm", BAD_CAST "text/html",
53 BAD_CAST "html", BAD_CAST "text/html",
54 BAD_CAST "isdoc", BAD_CAST "text/isdoc",
55 BAD_CAST "isdocx", BAD_CAST "text/isdocx",
56 BAD_CAST "jfif", BAD_CAST "image/jpeg",
57 BAD_CAST "jpg", BAD_CAST "image/jpeg",
58 BAD_CAST "jpeg", BAD_CAST "image/jpeg",
59 BAD_CAST "mpeg", BAD_CAST "video/mpeg",
60 BAD_CAST "mpeg1", BAD_CAST "video/mpeg",
61 BAD_CAST "mpeg2", BAD_CAST "video/mpeg",
62 BAD_CAST "mpg", BAD_CAST "video/mpeg",
63 BAD_CAST "mp2", BAD_CAST "audio/mpeg",
64 BAD_CAST "mp3", BAD_CAST "audio/mpeg",
65 BAD_CAST "odp", BAD_CAST "application/vnd.oasis.opendocument.presentation",
66 BAD_CAST "ods", BAD_CAST "application/vnd.oasis.opendocument.spreadsheet",
67 BAD_CAST "odt", BAD_CAST "application/vnd.oasis.opendocument.text",
68 BAD_CAST "pdf", BAD_CAST "application/pdf",
69 BAD_CAST "p7b", BAD_CAST "application/pkcs7-certificates",
70 BAD_CAST "p7c", BAD_CAST "application/pkcs7-mime",
71 BAD_CAST "p7m", BAD_CAST "application/pkcs7-mime",
72 BAD_CAST "p7f", BAD_CAST "application/pkcs7-signature",
73 BAD_CAST "p7s", BAD_CAST "application/pkcs7-signature",
74 BAD_CAST "pk7", BAD_CAST "application/pkcs7-mime",
75 BAD_CAST "png", BAD_CAST "image/png",
76 BAD_CAST "ppt", BAD_CAST "application/vnd.ms-powerpoint",
77 BAD_CAST "pptx", BAD_CAST "application/vnd.openxmlformats-officedocument."
78 "presentationml.presentation",
79 BAD_CAST "rtf", BAD_CAST "application/rtf",
80 BAD_CAST "tif", BAD_CAST "image/tiff",
81 BAD_CAST "tiff", BAD_CAST "image/tiff",
82 BAD_CAST "tsr", BAD_CAST "application/timestamp-reply",
83 BAD_CAST "tst", BAD_CAST "application/timestamp-reply",
84 BAD_CAST "txt", BAD_CAST "text/plain",
85 BAD_CAST "wav", BAD_CAST "audio/wav",
86 BAD_CAST "xls", BAD_CAST "application/vnd.ms-excel",
87 BAD_CAST "xlsx", BAD_CAST "application/vnd.openxmlformats-officedocument."
88 "spreadsheetml.sheet",
89 BAD_CAST "xml", BAD_CAST "application/xml",
90 BAD_CAST "xsd", BAD_CAST "application/xml",
91 BAD_CAST "zfo", BAD_CAST "application/vnd.software602.filler.form-xml-zip"
94 /* Structure type to hold conversion table from status code to isds_error and
95 * long message */
96 struct code_map_isds_error {
97 const xmlChar **codes; /* NULL terminated array of status codes */
98 const char **meanings; /* Mapping to non-localized long messages */
99 const isds_error *errors; /* Mapping to isds_error code */
102 /* Deallocate structure isds_pki_credentials and NULL it.
103 * Pass-phrase is discarded.
104 * @pki credentials to to free */
105 void isds_pki_credentials_free(struct isds_pki_credentials **pki) {
106 if(!pki || !*pki) return;
108 free((*pki)->engine);
109 free((*pki)->certificate);
110 free((*pki)->key);
112 if ((*pki)->passphrase) {
113 memset((*pki)->passphrase, 0, strlen((*pki)->passphrase));
114 free((*pki)->passphrase);
117 zfree((*pki));
121 /* Free isds_list with all member data.
122 * @list list to free, on return will be NULL */
123 void isds_list_free(struct isds_list **list) {
124 struct isds_list *item, *next_item;
126 if (!list || !*list) return;
128 for(item = *list; item; item = next_item) {
129 if (item->destructor) (item->destructor)(&(item->data));
130 next_item = item->next;
131 free(item);
134 *list = NULL;
138 /* Deallocate structure isds_hash and NULL it.
139 * @hash hash to to free */
140 void isds_hash_free(struct isds_hash **hash) {
141 if(!hash || !*hash) return;
142 free((*hash)->value);
143 zfree((*hash));
147 /* Deallocate structure isds_PersonName recursively and NULL it */
148 static void isds_PersonName_free(struct isds_PersonName **person_name) {
149 if (!person_name || !*person_name) return;
151 free((*person_name)->pnFirstName);
152 free((*person_name)->pnMiddleName);
153 free((*person_name)->pnLastName);
154 free((*person_name)->pnLastNameAtBirth);
156 free(*person_name);
157 *person_name = NULL;
161 /* Deallocate structure isds_BirthInfo recursively and NULL it */
162 static void isds_BirthInfo_free(struct isds_BirthInfo **birth_info) {
163 if (!birth_info || !*birth_info) return;
165 free((*birth_info)->biDate);
166 free((*birth_info)->biCity);
167 free((*birth_info)->biCounty);
168 free((*birth_info)->biState);
170 free(*birth_info);
171 *birth_info = NULL;
175 /* Deallocate structure isds_Address recursively and NULL it */
176 static void isds_Address_free(struct isds_Address **address) {
177 if (!address || !*address) return;
179 free((*address)->adCity);
180 free((*address)->adStreet);
181 free((*address)->adNumberInStreet);
182 free((*address)->adNumberInMunicipality);
183 free((*address)->adZipCode);
184 free((*address)->adState);
186 free(*address);
187 *address = NULL;
191 /* Deallocate structure isds_DbOwnerInfo recursively and NULL it */
192 void isds_DbOwnerInfo_free(struct isds_DbOwnerInfo **db_owner_info) {
193 if (!db_owner_info || !*db_owner_info) return;
195 free((*db_owner_info)->dbID);
196 free((*db_owner_info)->dbType);
197 free((*db_owner_info)->ic);
198 isds_PersonName_free(&((*db_owner_info)->personName));
199 free((*db_owner_info)->firmName);
200 isds_BirthInfo_free(&((*db_owner_info)->birthInfo));
201 isds_Address_free(&((*db_owner_info)->address));
202 free((*db_owner_info)->nationality);
203 free((*db_owner_info)->email);
204 free((*db_owner_info)->telNumber);
205 free((*db_owner_info)->identifier);
206 free((*db_owner_info)->registryCode);
207 free((*db_owner_info)->dbState);
208 free((*db_owner_info)->dbEffectiveOVM);
210 free(*db_owner_info);
211 *db_owner_info = NULL;
214 /* Deallocate structure isds_DbUserInfo recursively and NULL it */
215 void isds_DbUserInfo_free(struct isds_DbUserInfo **db_user_info) {
216 if (!db_user_info || !*db_user_info) return;
218 free((*db_user_info)->userID);
219 free((*db_user_info)->userType);
220 free((*db_user_info)->userPrivils);
221 isds_PersonName_free(&((*db_user_info)->personName));
222 isds_Address_free(&((*db_user_info)->address));
223 free((*db_user_info)->biDate);
224 free((*db_user_info)->ic);
225 free((*db_user_info)->firmName);
226 free((*db_user_info)->caStreet);
227 free((*db_user_info)->caCity);
228 free((*db_user_info)->caZipCode);
229 free((*db_user_info)->caState);
231 zfree(*db_user_info);
235 /* Deallocate struct isds_event recursively and NULL it */
236 void isds_event_free(struct isds_event **event) {
237 if (!event || !*event) return;
239 free((*event)->time);
240 free((*event)->type);
241 free((*event)->description);
242 zfree(*event);
246 /* Deallocate struct isds_envelope recursively and NULL it */
247 void isds_envelope_free(struct isds_envelope **envelope) {
248 if (!envelope || !*envelope) return;
250 free((*envelope)->dmID);
251 free((*envelope)->dbIDSender);
252 free((*envelope)->dmSender);
253 free((*envelope)->dmSenderAddress);
254 free((*envelope)->dmSenderType);
255 free((*envelope)->dmRecipient);
256 free((*envelope)->dmRecipientAddress);
257 free((*envelope)->dmAmbiguousRecipient);
258 free((*envelope)->dmType);
260 free((*envelope)->dmOrdinal);
261 free((*envelope)->dmMessageStatus);
262 free((*envelope)->dmDeliveryTime);
263 free((*envelope)->dmAcceptanceTime);
264 isds_hash_free(&(*envelope)->hash);
265 free((*envelope)->timestamp);
266 isds_list_free(&(*envelope)->events);
268 free((*envelope)->dmSenderOrgUnit);
269 free((*envelope)->dmSenderOrgUnitNum);
270 free((*envelope)->dbIDRecipient);
271 free((*envelope)->dmRecipientOrgUnit);
272 free((*envelope)->dmRecipientOrgUnitNum);
273 free((*envelope)->dmToHands);
274 free((*envelope)->dmAnnotation);
275 free((*envelope)->dmRecipientRefNumber);
276 free((*envelope)->dmSenderRefNumber);
277 free((*envelope)->dmRecipientIdent);
278 free((*envelope)->dmSenderIdent);
280 free((*envelope)->dmLegalTitleLaw);
281 free((*envelope)->dmLegalTitleYear);
282 free((*envelope)->dmLegalTitleSect);
283 free((*envelope)->dmLegalTitlePar);
284 free((*envelope)->dmLegalTitlePoint);
286 free((*envelope)->dmPersonalDelivery);
287 free((*envelope)->dmAllowSubstDelivery);
289 free((*envelope)->dmOVM);
290 free((*envelope)->dmPublishOwnID);
292 free(*envelope);
293 *envelope = NULL;
297 /* Deallocate struct isds_message recursively and NULL it */
298 void isds_message_free(struct isds_message **message) {
299 if (!message || !*message) return;
301 free((*message)->raw);
302 isds_envelope_free(&((*message)->envelope));
303 isds_list_free(&((*message)->documents));
304 xmlFreeDoc((*message)->xml); (*message)->xml = NULL;
306 free(*message);
307 *message = NULL;
311 /* Deallocate struct isds_document recursively and NULL it */
312 void isds_document_free(struct isds_document **document) {
313 if (!document || !*document) return;
315 if (!(*document)->is_xml) {
316 free((*document)->data);
318 free((*document)->dmMimeType);
319 free((*document)->dmFileGuid);
320 free((*document)->dmUpFileGuid);
321 free((*document)->dmFileDescr);
322 free((*document)->dmFormat);
324 free(*document);
325 *document = NULL;
329 /* Deallocate struct isds_message_copy recursively and NULL it */
330 void isds_message_copy_free(struct isds_message_copy **copy) {
331 if (!copy || !*copy) return;
333 free((*copy)->dbIDRecipient);
334 free((*copy)->dmRecipientOrgUnit);
335 free((*copy)->dmRecipientOrgUnitNum);
336 free((*copy)->dmToHands);
338 free((*copy)->dmStatus);
339 free((*copy)->dmID);
341 zfree(*copy);
345 /* Deallocate struct isds_message_status_change recursively and NULL it */
346 void isds_message_status_change_free(
347 struct isds_message_status_change **message_status_change) {
348 if (!message_status_change || !*message_status_change) return;
350 free((*message_status_change)->dmID);
351 free((*message_status_change)->time);
352 free((*message_status_change)->dmMessageStatus);
354 zfree(*message_status_change);
358 /* Deallocate struct isds_approval recursively and NULL it */
359 void isds_approval_free(struct isds_approval **approval) {
360 if (!approval || !*approval) return;
362 free((*approval)->refference);
364 zfree(*approval);
368 /* Deallocate struct isds_credentials_delivery recursively and NULL it.
369 * The email string is deallocated too. */
370 void isds_credentials_delivery_free(
371 struct isds_credentials_delivery **credentials_delivery) {
372 if (!credentials_delivery || !*credentials_delivery) return;
374 free((*credentials_delivery)->email);
375 free((*credentials_delivery)->token);
376 free((*credentials_delivery)->new_user_name);
378 zfree(*credentials_delivery);
382 /* Deallocate struct isds_commercial_permission recursively and NULL it */
383 void isds_commercial_permission_free(
384 struct isds_commercial_permission **permission) {
385 if (NULL == permission || NULL == *permission) return;
387 free((*permission)->recipient);
388 free((*permission)->payer);
389 free((*permission)->expiration);
390 free((*permission)->count);
391 free((*permission)->reply_identifier);
393 zfree(*permission);
397 /* *DUP_OR_ERROR macros needs error label */
398 #define STRDUP_OR_ERROR(new, template) { \
399 if (!template) { \
400 (new) = NULL; \
401 } else { \
402 (new) = strdup(template); \
403 if (!new) goto error; \
407 #define FLATDUP_OR_ERROR(new, template) { \
408 if (!template) { \
409 (new) = NULL; \
410 } else { \
411 (new) = malloc(sizeof(*(new))); \
412 if (!new) goto error; \
413 memcpy((new), (template), sizeof(*(template))); \
417 /* Copy structure isds_pki_credentials recursively. */
418 struct isds_pki_credentials *isds_pki_credentials_duplicate(
419 const struct isds_pki_credentials *template) {
420 struct isds_pki_credentials *new = NULL;
422 if(!template) return NULL;
424 new = calloc(1, sizeof(*new));
425 if (!new) return NULL;
427 STRDUP_OR_ERROR(new->engine, template->engine);
428 new->certificate_format = template->certificate_format;
429 STRDUP_OR_ERROR(new->certificate, template->certificate);
430 new->key_format = template->key_format;
431 STRDUP_OR_ERROR(new->key, template->key);
432 STRDUP_OR_ERROR(new->passphrase, template->passphrase);
434 return new;
436 error:
437 isds_pki_credentials_free(&new);
438 return NULL;
442 /* Copy structure isds_PersonName recursively */
443 struct isds_PersonName *isds_PersonName_duplicate(
444 const struct isds_PersonName *template) {
445 struct isds_PersonName *new = NULL;
447 if (!template) return NULL;
449 new = calloc(1, sizeof(*new));
450 if (!new) return NULL;
452 STRDUP_OR_ERROR(new->pnFirstName, template->pnFirstName);
453 STRDUP_OR_ERROR(new->pnMiddleName, template->pnMiddleName);
454 STRDUP_OR_ERROR(new->pnLastName, template->pnLastName);
455 STRDUP_OR_ERROR(new->pnLastNameAtBirth, template->pnLastNameAtBirth);
457 return new;
459 error:
460 isds_PersonName_free(&new);
461 return NULL;
465 /* Copy structure isds_BirthInfo recursively */
466 static struct isds_BirthInfo *isds_BirthInfo_duplicate(
467 const struct isds_BirthInfo *template) {
468 struct isds_BirthInfo *new = NULL;
470 if (!template) return NULL;
472 new = calloc(1, sizeof(*new));
473 if (!new) return NULL;
475 FLATDUP_OR_ERROR(new->biDate, template->biDate);
476 STRDUP_OR_ERROR(new->biCity, template->biCity);
477 STRDUP_OR_ERROR(new->biCounty, template->biCounty);
478 STRDUP_OR_ERROR(new->biState, template->biState);
480 return new;
482 error:
483 isds_BirthInfo_free(&new);
484 return NULL;
488 /* Copy structure isds_Address recursively */
489 struct isds_Address *isds_Address_duplicate(
490 const struct isds_Address *template) {
491 struct isds_Address *new = NULL;
493 if (!template) return NULL;
495 new = calloc(1, sizeof(*new));
496 if (!new) return NULL;
498 STRDUP_OR_ERROR(new->adCity, template->adCity);
499 STRDUP_OR_ERROR(new->adStreet, template->adStreet);
500 STRDUP_OR_ERROR(new->adNumberInStreet, template->adNumberInStreet);
501 STRDUP_OR_ERROR(new->adNumberInMunicipality,
502 template->adNumberInMunicipality);
503 STRDUP_OR_ERROR(new->adZipCode, template->adZipCode);
504 STRDUP_OR_ERROR(new->adState, template->adState);
506 return new;
508 error:
509 isds_Address_free(&new);
510 return NULL;
514 /* Copy structure isds_DbOwnerInfo recursively */
515 struct isds_DbOwnerInfo *isds_DbOwnerInfo_duplicate(
516 const struct isds_DbOwnerInfo *template) {
517 struct isds_DbOwnerInfo *new = NULL;
518 if (!template) return NULL;
520 new = calloc(1, sizeof(*new));
521 if (!new) return NULL;
523 STRDUP_OR_ERROR(new->dbID, template->dbID);
524 FLATDUP_OR_ERROR(new->dbType, template->dbType);
525 STRDUP_OR_ERROR(new->ic, template->ic);
527 if (template->personName) {
528 if (!(new->personName =
529 isds_PersonName_duplicate(template->personName)))
530 goto error;
533 STRDUP_OR_ERROR(new->firmName, template->firmName);
535 if (template->birthInfo) {
536 if (!(new->birthInfo =
537 isds_BirthInfo_duplicate(template->birthInfo)))
538 goto error;
541 if (template->address) {
542 if (!(new->address = isds_Address_duplicate(template->address)))
543 goto error;
546 STRDUP_OR_ERROR(new->nationality, template->nationality);
547 STRDUP_OR_ERROR(new->email, template->email);
548 STRDUP_OR_ERROR(new->telNumber, template->telNumber);
549 STRDUP_OR_ERROR(new->identifier, template->identifier);
550 STRDUP_OR_ERROR(new->registryCode, template->registryCode);
551 FLATDUP_OR_ERROR(new->dbState, template->dbState);
552 FLATDUP_OR_ERROR(new->dbEffectiveOVM, template->dbEffectiveOVM);
553 FLATDUP_OR_ERROR(new->dbOpenAddressing, template->dbOpenAddressing);
555 return new;
557 error:
558 isds_DbOwnerInfo_free(&new);
559 return NULL;
563 /* Copy structure isds_DbUserInfo recursively */
564 struct isds_DbUserInfo *isds_DbUserInfo_duplicate(
565 const struct isds_DbUserInfo *template) {
566 struct isds_DbUserInfo *new = NULL;
567 if (!template) return NULL;
569 new = calloc(1, sizeof(*new));
570 if (!new) return NULL;
572 STRDUP_OR_ERROR(new->userID, template->userID);
573 FLATDUP_OR_ERROR(new->userType, template->userType);
574 FLATDUP_OR_ERROR(new->userPrivils, template->userPrivils);
576 if (template->personName) {
577 if (!(new->personName =
578 isds_PersonName_duplicate(template->personName)))
579 goto error;
582 if (template->address) {
583 if (!(new->address = isds_Address_duplicate(template->address)))
584 goto error;
587 FLATDUP_OR_ERROR(new->biDate, template->biDate);
588 STRDUP_OR_ERROR(new->ic, template->ic);
589 STRDUP_OR_ERROR(new->firmName, template->firmName);
590 STRDUP_OR_ERROR(new->caStreet, template->caStreet);
591 STRDUP_OR_ERROR(new->caCity, template->caCity);
592 STRDUP_OR_ERROR(new->caZipCode, template->caZipCode);
593 STRDUP_OR_ERROR(new->caState, template->caState);
595 return new;
597 error:
598 isds_DbUserInfo_free(&new);
599 return NULL;
602 #undef FLATDUP_OR_ERROR
603 #undef STRDUP_OR_ERROR
606 /* Logs libxml2 errors. Should be registered to libxml2 library.
607 * @ctx is unused currently
608 * @msg is printf-like formated message from libxml2 (UTF-8?)
609 * @... are variadic arguments for @msg */
610 static void log_xml(void *ctx, const char *msg, ...) {
611 va_list ap;
612 char *text = NULL;
614 if (!msg) return;
616 va_start(ap, msg);
617 isds_vasprintf(&text, msg, ap);
618 va_end(ap);
620 if (text)
621 isds_log(ILF_XML, ILL_ERR, "%s", text);
622 free(text);
626 /* Initialize ISDS library.
627 * Global function, must be called before other functions.
628 * If it fails you can not use ISDS library and must call isds_cleanup() to
629 * free partially initialized global variables. */
630 isds_error isds_init(void) {
631 /* NULL global variables */
632 log_facilities = ILF_ALL;
633 log_level = ILL_WARNING;
634 log_callback = NULL;
635 log_callback_data = NULL;
637 #if ENABLE_NLS
638 /* Initialize gettext */
639 bindtextdomain(PACKAGE, LOCALEDIR);
640 #endif
642 #if HAVE_LIBCURL
643 /* Initialize CURL */
644 if (curl_global_init(CURL_GLOBAL_ALL)) {
645 isds_log(ILF_ISDS, ILL_CRIT, _("CURL library initialization failed\n"));
646 return IE_ERROR;
648 #endif /* HAVE_LIBCURL */
650 /* Initialize gpg-error because of gpgme and ksba */
651 if (gpg_err_init()) {
652 isds_log(ILF_ISDS, ILL_CRIT,
653 _("gpg-error library initialization failed\n"));
654 return IE_ERROR;
657 /* Initialize GPGME */
658 if (_isds_init_gpgme(&version_gpgme)) {
659 isds_log(ILF_ISDS, ILL_CRIT,
660 _("GPGME library initialization failed\n"));
661 return IE_ERROR;
664 /* Initialize gcrypt */
665 if (_isds_init_gcrypt(&version_gcrypt)) {
666 isds_log(ILF_ISDS, ILL_CRIT,
667 _("gcrypt library initialization failed\n"));
668 return IE_ERROR;
671 /* This can _exit() current program. Find not so assertive check. */
672 LIBXML_TEST_VERSION;
673 xmlSetGenericErrorFunc(NULL, log_xml);
675 /* Check expat */
676 if (_isds_init_expat(&version_expat)) {
677 isds_log(ILF_ISDS, ILL_CRIT,
678 _("expat library initialization failed\n"));
679 return IE_ERROR;
682 /* Allocate global variables */
685 return IE_SUCCESS;
689 /* Deinitialize ISDS library.
690 * Global function, must be called as last library function. */
691 isds_error isds_cleanup(void) {
692 /* XML */
693 xmlCleanupParser();
695 #if HAVE_LIBCURL
696 /* Curl */
697 curl_global_cleanup();
698 #endif
700 return IE_SUCCESS;
704 /* Return version string of this library. Version of dependencies can be
705 * embedded. Do no try to parse it. You must free it. */
706 char *isds_version(void) {
707 char *buffer = NULL;
709 isds_asprintf(&buffer,
710 #if HAVE_LIBCURL
711 _("%s (%s, GPGME %s, gcrypt %s, %s, libxml2 %s)"),
712 #else
713 _("%s (GPGME %s, gcrypt %s, %s, libxml2 %s)"),
714 #endif
715 PACKAGE_VERSION,
716 #if HAVE_LIBCURL
717 curl_version(),
718 #endif
719 version_gpgme, version_gcrypt,
720 version_expat, xmlParserVersion);
721 return buffer;
725 /* Return text description of ISDS error */
726 const char *isds_strerror(const isds_error error) {
727 switch (error) {
728 case IE_SUCCESS:
729 return(_("Success")); break;
730 case IE_ERROR:
731 return(_("Unspecified error")); break;
732 case IE_NOTSUP:
733 return(_("Not supported")); break;
734 case IE_INVAL:
735 return(_("Invalid value")); break;
736 case IE_INVALID_CONTEXT:
737 return(_("Invalid context")); break;
738 case IE_NOT_LOGGED_IN:
739 return(_("Not logged in")); break;
740 case IE_CONNECTION_CLOSED:
741 return(_("Connection closed")); break;
742 case IE_TIMED_OUT:
743 return(_("Timed out")); break;
744 case IE_NOEXIST:
745 return(_("Not exist")); break;
746 case IE_NOMEM:
747 return(_("Out of memory")); break;
748 case IE_NETWORK:
749 return(_("Network problem")); break;
750 case IE_HTTP:
751 return(_("HTTP problem")); break;
752 case IE_SOAP:
753 return(_("SOAP problem")); break;
754 case IE_XML:
755 return(_("XML problem")); break;
756 case IE_ISDS:
757 return(_("ISDS server problem")); break;
758 case IE_ENUM:
759 return(_("Invalid enum value")); break;
760 case IE_DATE:
761 return(_("Invalid date value")); break;
762 case IE_2BIG:
763 return(_("Too big")); break;
764 case IE_2SMALL:
765 return(_("Too small")); break;
766 case IE_NOTUNIQ:
767 return(_("Value not unique")); break;
768 case IE_NOTEQUAL:
769 return(_("Values not equal")); break;
770 case IE_PARTIAL_SUCCESS:
771 return(_("Some suboperations failed")); break;
772 case IE_ABORTED:
773 return(_("Operation aborted")); break;
774 case IE_SECURITY:
775 return(_("Security problem")); break;
776 default:
777 return(_("Unknown error"));
782 /* Create ISDS context.
783 * Each context can be used for different sessions to (possibly) different
784 * ISDS server with different credentials. */
785 struct isds_ctx *isds_ctx_create(void) {
786 struct isds_ctx *context;
787 context = malloc(sizeof(*context));
788 if (context) memset(context, 0, sizeof(*context));
789 return context;
792 #if HAVE_LIBCURL
793 /* Close possibly opened connection to Czech POINT document deposit without
794 * resetting long_message buffer.
795 * XXX: Do not use czp_close_connection() if you do not want to destroy log
796 * message.
797 * @context is Czech POINT session context. */
798 static isds_error czp_do_close_connection(struct isds_ctx *context) {
799 if (!context) return IE_INVALID_CONTEXT;
800 _isds_close_connection(context);
801 return IE_SUCCESS;
805 /* Discard credentials.
806 * @context is ISDS context
807 * @discard_saved_username is true for removing saved username, false for
808 * keeping it.
809 * Only that. It does not cause log out, connection close or similar. */
810 _hidden isds_error _isds_discard_credentials(struct isds_ctx *context,
811 _Bool discard_saved_username) {
812 if(!context) return IE_INVALID_CONTEXT;
814 if (context->username) {
815 memset(context->username, 0, strlen(context->username));
816 zfree(context->username);
818 if (context->password) {
819 memset(context->password, 0, strlen(context->password));
820 zfree(context->password);
822 isds_pki_credentials_free(&context->pki_credentials);
823 if (discard_saved_username && context->saved_username) {
824 memset(context->saved_username, 0, strlen(context->saved_username));
825 zfree(context->saved_username);
828 return IE_SUCCESS;
830 #endif /* HAVE_LIBCURL */
833 /* Destroy ISDS context and free memory.
834 * @context will be NULLed on success. */
835 isds_error isds_ctx_free(struct isds_ctx **context) {
836 if (!context || !*context) {
837 return IE_INVALID_CONTEXT;
840 #if HAVE_LIBCURL
841 /* Discard credentials and close connection */
842 switch ((*context)->type) {
843 case CTX_TYPE_NONE: break;
844 case CTX_TYPE_ISDS: isds_logout(*context); break;
845 case CTX_TYPE_CZP:
846 case CTX_TYPE_TESTING_REQUEST_COLLECTOR:
847 czp_do_close_connection(*context); break;
850 /* For sure */
851 _isds_discard_credentials(*context, 1);
853 /* Free other structures */
854 free((*context)->tls_verify_server);
855 free((*context)->tls_ca_file);
856 free((*context)->tls_ca_dir);
857 free((*context)->tls_crl_file);
858 #endif /* HAVE_LIBCURL */
859 free((*context)->long_message);
861 free(*context);
862 *context = NULL;
863 return IE_SUCCESS;
867 /* Return long message text produced by library function, e.g. detailed error
868 * message. Returned pointer is only valid until new library function is
869 * called for the same context. Could be NULL, especially if NULL context is
870 * supplied. Return string is locale encoded. */
871 char *isds_long_message(const struct isds_ctx *context) {
872 if (!context) return NULL;
873 return context->long_message;
877 /* Stores message into context' long_message buffer.
878 * Application can pick the message up using isds_long_message().
879 * NULL @message truncates the buffer but does not deallocate it.
880 * @message is coded in locale encoding */
881 _hidden isds_error isds_log_message(struct isds_ctx *context,
882 const char *message) {
883 char *buffer;
884 size_t length;
886 if (!context) return IE_INVALID_CONTEXT;
888 /* FIXME: Check for integer overflow */
889 length = 1 + ((message) ? strlen(message) : 0);
890 buffer = realloc(context->long_message, length);
891 if (!buffer) return IE_NOMEM;
893 if (message)
894 strcpy(buffer, message);
895 else
896 *buffer = '\0';
898 context->long_message = buffer;
899 return IE_SUCCESS;
903 /* Appends message into context' long_message buffer.
904 * Application can pick the message up using isds_long_message().
905 * NULL message has void effect. */
906 _hidden isds_error isds_append_message(struct isds_ctx *context,
907 const char *message) {
908 char *buffer;
909 size_t old_length, length;
911 if (!context) return IE_INVALID_CONTEXT;
912 if (!message) return IE_SUCCESS;
913 if (!context->long_message)
914 return isds_log_message(context, message);
916 old_length = strlen(context->long_message);
917 /* FIXME: Check for integer overflow */
918 length = 1 + old_length + strlen(message);
919 buffer = realloc(context->long_message, length);
920 if (!buffer) return IE_NOMEM;
922 strcpy(buffer + old_length, message);
924 context->long_message = buffer;
925 return IE_SUCCESS;
929 /* Stores formatted message into context' long_message buffer.
930 * Application can pick the message up using isds_long_message(). */
931 _hidden isds_error isds_printf_message(struct isds_ctx *context,
932 const char *format, ...) {
933 va_list ap;
934 int length;
936 if (!context) return IE_INVALID_CONTEXT;
937 va_start(ap, format);
938 length = isds_vasprintf(&(context->long_message), format, ap);
939 va_end(ap);
941 return (length < 0) ? IE_ERROR: IE_SUCCESS;
945 /* Set logging up.
946 * @facilities is bit mask of isds_log_facility values,
947 * @level is verbosity level. */
948 void isds_set_logging(const unsigned int facilities,
949 const isds_log_level level) {
950 log_facilities = facilities;
951 log_level = level;
955 /* Register callback function libisds calls when new global log message is
956 * produced by library. Library logs to stderr by default.
957 * @callback is function provided by application libisds will call. See type
958 * definition for @callback argument explanation. Pass NULL to revert logging to
959 * default behaviour.
960 * @data is application specific data @callback gets as last argument */
961 void isds_set_log_callback(isds_log_callback callback, void *data) {
962 log_callback = callback;
963 log_callback_data = data;
967 /* Log @message in class @facility with log @level into global log. @message
968 * is printf(3) formatting string, variadic arguments may be necessary.
969 * For debugging purposes. */
970 _hidden isds_error isds_log(const isds_log_facility facility,
971 const isds_log_level level, const char *message, ...) {
972 va_list ap;
973 char *buffer = NULL;
974 int length;
976 if (level > log_level) return IE_SUCCESS;
977 if (!(log_facilities & facility)) return IE_SUCCESS;
978 if (!message) return IE_INVAL;
980 if (log_callback) {
981 /* Pass message to application supplied callback function */
982 va_start(ap, message);
983 length = isds_vasprintf(&buffer, message, ap);
984 va_end(ap);
986 if (length == -1) {
987 return IE_ERROR;
989 if (length > 0) {
990 log_callback(facility, level, buffer, length, log_callback_data);
992 free(buffer);
993 } else {
994 /* Default: Log it to stderr */
995 va_start(ap, message);
996 vfprintf(stderr, message, ap);
997 va_end(ap);
998 /* Line buffered printf is default.
999 * fflush(stderr);*/
1002 return IE_SUCCESS;
1006 /* Set timeout in milliseconds for each network job like connecting to server
1007 * or sending message. Use 0 to disable timeout limits. */
1008 isds_error isds_set_timeout(struct isds_ctx *context,
1009 const unsigned int timeout) {
1010 if (!context) return IE_INVALID_CONTEXT;
1011 zfree(context->long_message);
1013 #if HAVE_LIBCURL
1014 context->timeout = timeout;
1016 if (context->curl) {
1017 CURLcode curl_err;
1019 curl_err = curl_easy_setopt(context->curl, CURLOPT_NOSIGNAL, 1);
1020 if (!curl_err)
1021 #if HAVE_DECL_CURLOPT_TIMEOUT_MS /* Since curl-7.16.2 */
1022 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT_MS,
1023 context->timeout);
1024 #else
1025 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT,
1026 context->timeout / 1000);
1027 #endif /* not HAVE_DECL_CURLOPT_TIMEOUT_MS */
1028 if (curl_err) return IE_ERROR;
1031 return IE_SUCCESS;
1032 #else /* not HAVE_LIBCURL */
1033 return IE_NOTSUP;
1034 #endif
1038 /* Register callback function libisds calls periodically during HTTP data
1039 * transfer.
1040 * @context is session context
1041 * @callback is function provided by application libisds will call. See type
1042 * definition for @callback argument explanation.
1043 * @data is application specific data @callback gets as last argument */
1044 isds_error isds_set_progress_callback(struct isds_ctx *context,
1045 isds_progress_callback callback, void *data) {
1046 if (!context) return IE_INVALID_CONTEXT;
1047 zfree(context->long_message);
1049 #if HAVE_LIBCURL
1050 context->progress_callback = callback;
1051 context->progress_callback_data = data;
1053 return IE_SUCCESS;
1054 #else /* not HAVE_LIBCURL */
1055 return IE_NOTSUP;
1056 #endif
1060 /* Change context settings.
1061 * @context is context which setting will be applied to
1062 * @option is name of option. It determines the type of last argument. See
1063 * isds_option definition for more info.
1064 * @... is value of new setting. Type is determined by @option
1065 * */
1066 isds_error isds_set_opt(struct isds_ctx *context, const isds_option option,
1067 ...) {
1068 isds_error err = IE_SUCCESS;
1069 va_list ap;
1070 #if HAVE_LIBCURL
1071 char *pointer, *string;
1072 #endif
1074 if (!context) return IE_INVALID_CONTEXT;
1075 zfree(context->long_message);
1077 va_start(ap, option);
1079 #define REPLACE_VA_BOOLEAN(destination) { \
1080 if (!(destination)) { \
1081 (destination) = malloc(sizeof(*(destination))); \
1082 if (!(destination)) { \
1083 err = IE_NOMEM; goto leave; \
1086 *(destination) = (_Bool) !!va_arg(ap, int); \
1089 #define REPLACE_VA_STRING(destination) { \
1090 string = va_arg(ap, char *); \
1091 if (string) { \
1092 pointer = realloc((destination), 1 + strlen(string)); \
1093 if (!pointer) { err = IE_NOMEM; goto leave; } \
1094 strcpy(pointer, string); \
1095 (destination) = pointer; \
1096 } else { \
1097 free(destination); \
1098 (destination) = NULL; \
1102 switch (option) {
1103 case IOPT_TLS_VERIFY_SERVER:
1104 #if HAVE_LIBCURL
1105 REPLACE_VA_BOOLEAN(context->tls_verify_server);
1106 #else
1107 err = IE_NOTSUP; goto leave;
1108 #endif
1109 break;
1110 case IOPT_TLS_CA_FILE:
1111 #if HAVE_LIBCURL
1112 REPLACE_VA_STRING(context->tls_ca_file);
1113 #else
1114 err = IE_NOTSUP; goto leave;
1115 #endif
1116 break;
1117 case IOPT_TLS_CA_DIRECTORY:
1118 #if HAVE_LIBCURL
1119 REPLACE_VA_STRING(context->tls_ca_dir);
1120 #else
1121 err = IE_NOTSUP; goto leave;
1122 #endif
1123 break;
1124 case IOPT_TLS_CRL_FILE:
1125 #if HAVE_LIBCURL
1126 #if HAVE_DECL_CURLOPT_CRLFILE /* Since curl-7.19.0 */
1127 REPLACE_VA_STRING(context->tls_crl_file);
1128 #else
1129 isds_log_message(context,
1130 _("Curl library does not support CRL definition"));
1131 err = IE_NOTSUP;
1132 #endif /* not HAVE_DECL_CURLOPT_CRLFILE */
1133 #else
1134 err = IE_NOTSUP; goto leave;
1135 #endif /* not HAVE_LIBCURL */
1136 break;
1137 case IOPT_NORMALIZE_MIME_TYPE:
1138 context->normalize_mime_type = (_Bool) !!va_arg(ap, int);
1139 break;
1141 default:
1142 err = IE_ENUM; goto leave;
1145 #undef REPLACE_VA_STRING
1146 #undef REPLACE_VA_BOOLEAN
1148 leave:
1149 va_end(ap);
1150 return err;
1154 #if HAVE_LIBCURL
1155 /* Copy credentials into context. Any non-NULL argument will be duplicated.
1156 * Destination for NULL argument will not be touched.
1157 * Destination pointers must be freed before calling this function.
1158 * If @username is @context->saved_username, the saved_username will not be
1159 * replaced. The saved_username is clobbered only if context has set otp
1160 * member.
1161 * Return IE_SUCCESS on success. */
1162 static isds_error _isds_store_credentials(struct isds_ctx *context,
1163 const char *username, const char *password,
1164 const struct isds_pki_credentials *pki_credentials) {
1165 if (NULL == context) return IE_INVALID_CONTEXT;
1167 /* FIXME: mlock password
1168 * (I have a library) */
1170 if (username) {
1171 context->username = strdup(username);
1172 if (context->otp && context->saved_username != username)
1173 context->saved_username = strdup(username);
1175 if (password) {
1176 if (NULL == context->otp_credentials)
1177 context->password = strdup(password);
1178 else
1179 context->password = _isds_astrcat(password,
1180 context->otp_credentials->otp_code);
1182 context->pki_credentials = isds_pki_credentials_duplicate(pki_credentials);
1184 if ((NULL != username && NULL == context->username) ||
1185 (NULL != password && NULL == context->password) ||
1186 (NULL != pki_credentials && NULL == context->pki_credentials) ||
1187 (context->otp && NULL != context->username &&
1188 NULL == context->saved_username)) {
1189 return IE_NOMEM;
1192 return IE_SUCCESS;
1194 #endif
1197 /* Connect and log into ISDS server.
1198 * All required arguments will be copied, you do not have to keep them after
1199 * that.
1200 * ISDS supports six different authentication methods. Exact method is
1201 * selected on @username, @password, @pki_credentials, and @otp arguments:
1202 * - If @pki_credentials == NULL, @username and @password must be supplied
1203 * and then
1204 * - If @otp == NULL, simple authentication by username and password will
1205 * be proceeded.
1206 * - If @otp != NULL, authentication by username and password and OTP
1207 * will be used.
1208 * - If @pki_credentials != NULL, then
1209 * - If @username == NULL, only certificate will be used
1210 * - If @username != NULL, then
1211 * - If @password == NULL, then certificate will be used and
1212 * @username shifts meaning to box ID. This is used for hosted
1213 * services.
1214 * - Otherwise all three arguments will be used.
1215 * Please note, that different cases require different certificate type
1216 * (system qualified one or commercial non qualified one). This library
1217 * does not check such political issues. Please see ISDS Specification
1218 * for more details.
1219 * @url is base address of ISDS web service. Pass extern isds_locator
1220 * variable to use production ISDS instance without client certificate
1221 * authentication (or extern isds_cert_locator with client certificate
1222 * authentication or extern isds_otp_locators with OTP authentication).
1223 * Passing NULL has the same effect, autoselection between isds_locator,
1224 * isds_cert_locator, and isds_otp_locator is performed in addition. You can
1225 * pass extern isds_testing_locator (or isds_cert_testing_locator or
1226 * isds_otp_testing_locator) variable to select testing instance.
1227 * @username is user name of ISDS user or box ID
1228 * @password is user's secret password
1229 * @pki_credentials defines public key cryptographic material to use in client
1230 * authentication.
1231 * @otp selects one-time password authentication method to use, defines OTP
1232 * code (if known) and returns fine grade resolution of OTP procedure.
1233 * @return:
1234 * IE_SUCCESS if authentication succeeds
1235 * IE_NOT_LOGGED_IN if authentication fails. If OTP authentication has been
1236 * requested, fine grade reason will be set into @otp->resolution. Error
1237 * message from server can be obtained by isds_long_message() call.
1238 * IE_PARTIAL_SUCCESS if time-based OTP authentication has been requested and
1239 * server has sent OTP code through side channel. Application is expected to
1240 * fill the code into @otp->otp_code, keep other arguments unchanged, and retry
1241 * this call to complete second phase of TOTP authentication;
1242 * or other appropriate error. */
1243 isds_error isds_login(struct isds_ctx *context, const char *url,
1244 const char *username, const char *password,
1245 const struct isds_pki_credentials *pki_credentials,
1246 struct isds_otp *otp) {
1247 #if HAVE_LIBCURL
1248 isds_error err = IE_NOT_LOGGED_IN;
1249 isds_error soap_err;
1250 xmlNsPtr isds_ns = NULL;
1251 xmlNodePtr request = NULL;
1252 xmlNodePtr response = NULL;
1253 #endif /* HAVE_LIBCURL */
1255 if (!context) return IE_INVALID_CONTEXT;
1256 zfree(context->long_message);
1258 #if HAVE_LIBCURL
1259 /* Close connection if already logged in */
1260 if (context->curl) {
1261 _isds_close_connection(context);
1264 /* Store configuration */
1265 context->type = CTX_TYPE_ISDS;
1266 zfree(context->url);
1268 /* Mangle base URI according to requested authentication method */
1269 if (NULL == pki_credentials) {
1270 isds_log(ILF_SEC, ILL_INFO,
1271 _("Selected authentication method: no certificate, "
1272 "username and password\n"));
1273 if (!username || !password) {
1274 isds_log_message(context,
1275 _("Both username and password must be supplied"));
1276 return IE_INVAL;
1278 context->otp_credentials = otp;
1279 context->otp = (NULL != context->otp_credentials);
1281 if (!context->otp) {
1282 /* Default locator is official system (without certificate or
1283 * OTP) */
1284 context->url = strdup((NULL != url) ? url : isds_locator);
1285 } else {
1286 const char *authenticator_uri = NULL;
1287 if (!url) url = isds_otp_locator;
1288 otp->resolution = OTP_RESOLUTION_UNKNOWN;
1289 switch (context->otp_credentials->method) {
1290 case OTP_HMAC:
1291 isds_log(ILF_SEC, ILL_INFO,
1292 _("Selected authentication method: "
1293 "HMAC-based one-time password\n"));
1294 authenticator_uri =
1295 "%1$sas/processLogin?type=hotp&uri=%1$sapps/";
1296 break;
1297 case OTP_TIME:
1298 isds_log(ILF_SEC, ILL_INFO,
1299 _("Selected authentication method: "
1300 "Time-based one-time password\n"));
1301 if (context->otp_credentials->otp_code == NULL) {
1302 isds_log(ILF_SEC, ILL_INFO,
1303 _("OTP code has not been provided by "
1304 "application, requesting server for "
1305 "new one.\n"));
1306 authenticator_uri =
1307 "%1$sas/processLogin?type=totp&sendSms=true&"
1308 "uri=%1$sapps/";
1309 } else {
1310 isds_log(ILF_SEC, ILL_INFO,
1311 _("OTP code has been provided by "
1312 "application, not requesting server "
1313 "for new one.\n"));
1314 authenticator_uri =
1315 "%1$sas/processLogin?type=totp&"
1316 "uri=%1$sapps/";
1318 break;
1319 default:
1320 isds_log_message(context,
1321 _("Unknown one-time password authentication "
1322 "method requested by application"));
1323 return IE_ENUM;
1325 if (-1 == isds_asprintf(&context->url, authenticator_uri, url))
1326 return IE_NOMEM;
1328 } else {
1329 /* Default locator is official system (with client certificate) */
1330 context->otp = 0;
1331 context->otp_credentials = NULL;
1332 if (!url) url = isds_cert_locator;
1334 if (!username) {
1335 isds_log(ILF_SEC, ILL_INFO,
1336 _("Selected authentication method: system certificate, "
1337 "no username and no password\n"));
1338 password = NULL;
1339 context->url = _isds_astrcat(url, "cert/");
1340 } else {
1341 if (!password) {
1342 isds_log(ILF_SEC, ILL_INFO,
1343 _("Selected authentication method: system certificate, "
1344 "box ID and no password\n"));
1345 context->url = _isds_astrcat(url, "hspis/");
1346 } else {
1347 isds_log(ILF_SEC, ILL_INFO,
1348 _("Selected authentication method: commercial "
1349 "certificate, username and password\n"));
1350 context->url = _isds_astrcat(url, "certds/");
1354 if (!(context->url))
1355 return IE_NOMEM;
1357 /* Prepare CURL handle */
1358 context->curl = curl_easy_init();
1359 if (!(context->curl))
1360 return IE_ERROR;
1362 /* Build log-in request */
1363 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1364 if (!request) {
1365 isds_log_message(context, _("Could not build ISDS log-in request"));
1366 return IE_ERROR;
1368 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1369 if(!isds_ns) {
1370 isds_log_message(context, _("Could not create ISDS name space"));
1371 xmlFreeNode(request);
1372 return IE_ERROR;
1374 xmlSetNs(request, isds_ns);
1376 /* Store credentials */
1377 _isds_discard_credentials(context, 1);
1378 if (_isds_store_credentials(context, username, password, pki_credentials)) {
1379 _isds_discard_credentials(context, 1);
1380 xmlFreeNode(request);
1381 return IE_NOMEM;
1384 isds_log(ILF_ISDS, ILL_DEBUG, _("Logging user %s into server %s\n"),
1385 username, url);
1387 /* Send log-in request */
1388 soap_err = _isds_soap(context, "DS/dz", request, &response, NULL, NULL);
1390 if (context->otp) {
1391 /* Revert context URL from OTP authentication service URL to OTP web
1392 * service base URL for subsequent calls. Potenial isds_login() retry
1393 * will re-set context URL again. */
1394 zfree(context->url);
1395 context->url = _isds_astrcat(url, "apps/");
1396 if (context->url == NULL) {
1397 soap_err = IE_NOMEM;
1399 /* Detach pointer to OTP credentials from context */
1400 context->otp_credentials = NULL;
1403 /* Remove credentials */
1404 _isds_discard_credentials(context, 0);
1406 /* Destroy log-in request */
1407 xmlFreeNode(request);
1409 if (soap_err) {
1410 xmlFreeNodeList(response);
1411 _isds_close_connection(context);
1412 return soap_err;
1415 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1416 * authentication succeeded if soap_err == IE_SUCCESS */
1417 err = IE_SUCCESS;
1419 xmlFreeNodeList(response);
1421 if (!err)
1422 isds_log(ILF_ISDS, ILL_DEBUG,
1423 _("User %s has been logged into server %s successfully\n"),
1424 username, url);
1425 return err;
1426 #else /* not HAVE_LIBCURL */
1427 return IE_NOTSUP;
1428 #endif
1432 /* Log out from ISDS server discards credentials and connection configuration. */
1433 isds_error isds_logout(struct isds_ctx *context) {
1434 if (!context) return IE_INVALID_CONTEXT;
1435 zfree(context->long_message);
1437 #if HAVE_LIBCURL
1438 if (context->curl) {
1439 if (context->otp) {
1440 isds_error err = _isds_invalidate_otp_cookie(context);
1441 if (err) return err;
1444 /* Close connection */
1445 _isds_close_connection(context);
1447 /* Discard credentials for sure. They should not survive isds_login(),
1448 * even successful .*/
1449 _isds_discard_credentials(context, 1);
1450 zfree(context->url);
1452 isds_log(ILF_ISDS, ILL_DEBUG, _("Logged out from ISDS server\n"));
1453 } else {
1454 _isds_discard_credentials(context, 1);
1456 return IE_SUCCESS;
1457 #else /* not HAVE_LIBCURL */
1458 return IE_NOTSUP;
1459 #endif
1463 /* Verify connection to ISDS is alive and server is responding.
1464 * Send dummy request to ISDS and expect dummy response. */
1465 isds_error isds_ping(struct isds_ctx *context) {
1466 #if HAVE_LIBCURL
1467 isds_error soap_err;
1468 xmlNsPtr isds_ns = NULL;
1469 xmlNodePtr request = NULL;
1470 xmlNodePtr response = NULL;
1471 #endif /* HAVE_LIBCURL */
1473 if (!context) return IE_INVALID_CONTEXT;
1474 zfree(context->long_message);
1476 #if HAVE_LIBCURL
1477 /* Check if connection is established */
1478 if (!context->curl) return IE_CONNECTION_CLOSED;
1481 /* Build dummy request */
1482 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1483 if (!request) {
1484 isds_log_message(context, _("Could build ISDS dummy request"));
1485 return IE_ERROR;
1487 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1488 if(!isds_ns) {
1489 isds_log_message(context, _("Could not create ISDS name space"));
1490 xmlFreeNode(request);
1491 return IE_ERROR;
1493 xmlSetNs(request, isds_ns);
1495 isds_log(ILF_ISDS, ILL_DEBUG, _("Pinging ISDS server\n"));
1497 /* Sent dummy request */
1498 soap_err = _isds_soap(context, "DS/dz", request, &response, NULL, NULL);
1500 /* Destroy log-in request */
1501 xmlFreeNode(request);
1503 if (soap_err) {
1504 isds_log(ILF_ISDS, ILL_DEBUG,
1505 _("ISDS server could not be contacted\n"));
1506 xmlFreeNodeList(response);
1507 return soap_err;
1510 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1511 * authentication succeeded if soap_err == IE_SUCCESS */
1512 /* TODO: ISDS documentation does not specify response body.
1513 * However real server sends back DummyOperationResponse */
1516 xmlFreeNodeList(response);
1518 isds_log(ILF_ISDS, ILL_DEBUG, _("ISDS server alive\n"));
1520 return IE_SUCCESS;
1521 #else /* not HAVE_LIBCURL */
1522 return IE_NOTSUP;
1523 #endif
1527 /* Send bogus request to ISDS.
1528 * Just for test purposes */
1529 isds_error isds_bogus_request(struct isds_ctx *context) {
1530 #if HAVE_LIBCURL
1531 isds_error err;
1532 xmlNsPtr isds_ns = NULL;
1533 xmlNodePtr request = NULL;
1534 xmlDocPtr response = NULL;
1535 xmlChar *code = NULL, *message = NULL;
1536 #endif
1538 if (!context) return IE_INVALID_CONTEXT;
1539 zfree(context->long_message);
1541 #if HAVE_LIBCURL
1542 /* Check if connection is established */
1543 if (!context->curl) {
1544 /* Testing printf message */
1545 isds_printf_message(context, "%s", _("I said connection closed"));
1546 return IE_CONNECTION_CLOSED;
1550 /* Build dummy request */
1551 request = xmlNewNode(NULL, BAD_CAST "X-BogusOperation");
1552 if (!request) {
1553 isds_log_message(context, _("Could build ISDS bogus request"));
1554 return IE_ERROR;
1556 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1557 if(!isds_ns) {
1558 isds_log_message(context, _("Could not create ISDS name space"));
1559 xmlFreeNode(request);
1560 return IE_ERROR;
1562 xmlSetNs(request, isds_ns);
1564 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending bogus request to ISDS\n"));
1566 /* Sent bogus request */
1567 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
1569 /* Destroy request */
1570 xmlFreeNode(request);
1572 if (err) {
1573 isds_log(ILF_ISDS, ILL_DEBUG,
1574 _("Processing ISDS response on bogus request failed\n"));
1575 xmlFreeDoc(response);
1576 return err;
1579 /* Check for response status */
1580 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
1581 &code, &message, NULL);
1582 if (err) {
1583 isds_log(ILF_ISDS, ILL_DEBUG,
1584 _("ISDS response on bogus request is missing status\n"));
1585 free(code);
1586 free(message);
1587 xmlFreeDoc(response);
1588 return err;
1590 if (xmlStrcmp(code, BAD_CAST "0000")) {
1591 char *code_locale = _isds_utf82locale((char*)code);
1592 char *message_locale = _isds_utf82locale((char*)message);
1593 isds_log(ILF_ISDS, ILL_DEBUG,
1594 _("Server refused bogus request (code=%s, message=%s)\n"),
1595 code_locale, message_locale);
1596 /* XXX: Literal error messages from ISDS are Czech messages
1597 * (English sometimes) in UTF-8. It's hard to catch them for
1598 * translation. Successfully gettextized would return in locale
1599 * encoding, unsuccessfully translated would pass in UTF-8. */
1600 isds_log_message(context, message_locale);
1601 free(code_locale);
1602 free(message_locale);
1603 free(code);
1604 free(message);
1605 xmlFreeDoc(response);
1606 return IE_ISDS;
1610 free(code);
1611 free(message);
1612 xmlFreeDoc(response);
1614 isds_log(ILF_ISDS, ILL_DEBUG,
1615 _("Bogus message accepted by server. This should not happen.\n"));
1617 return IE_SUCCESS;
1618 #else /* not HAVE_LIBCURL */
1619 return IE_NOTSUP;
1620 #endif
1624 #if HAVE_LIBCURL
1625 /* Serialize XML subtree to buffer preserving XML indentation.
1626 * @context is session context
1627 * @subtree is XML element to be serialized (with children)
1628 * @buffer is automatically reallocated buffer where serialize to
1629 * @length is size of serialized stream in bytes
1630 * @return standard error code, free @buffer in case of error */
1631 static isds_error serialize_subtree(struct isds_ctx *context,
1632 xmlNodePtr subtree, void **buffer, size_t *length) {
1633 isds_error err = IE_SUCCESS;
1634 xmlBufferPtr xml_buffer = NULL;
1635 xmlSaveCtxtPtr save_ctx = NULL;
1636 xmlDocPtr subtree_doc = NULL;
1637 xmlNodePtr subtree_copy;
1638 xmlNsPtr isds_ns;
1639 void *new_buffer;
1641 if (!context) return IE_INVALID_CONTEXT;
1642 if (!buffer) return IE_INVAL;
1643 zfree(*buffer);
1644 if (!subtree || !length) return IE_INVAL;
1646 /* Make temporary XML document with @subtree root element */
1647 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1648 * It can result in not well-formed on invalid XML tree (e.g. name space
1649 * prefix definition can miss. */
1650 /*FIXME */
1652 subtree_doc = xmlNewDoc(BAD_CAST "1.0");
1653 if (!subtree_doc) {
1654 isds_log_message(context, _("Could not build temporary document"));
1655 err = IE_ERROR;
1656 goto leave;
1659 /* XXX: Copy subtree and attach the copy to document.
1660 * One node can not bee attached into more document at the same time.
1661 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1662 * automatically.
1663 * XXX: Check xmlSaveTree() too. */
1664 subtree_copy = xmlCopyNodeList(subtree);
1665 if (!subtree_copy) {
1666 isds_log_message(context, _("Could not copy subtree"));
1667 err = IE_ERROR;
1668 goto leave;
1670 xmlDocSetRootElement(subtree_doc, subtree_copy);
1672 /* Only this way we get namespace definition as @xmlns:isds,
1673 * otherwise we get namespace prefix without definition */
1674 /* FIXME: Don't overwrite original default namespace */
1675 isds_ns = xmlNewNs(subtree_copy, BAD_CAST ISDS_NS, NULL);
1676 if(!isds_ns) {
1677 isds_log_message(context, _("Could not create ISDS name space"));
1678 err = IE_ERROR;
1679 goto leave;
1681 xmlSetNs(subtree_copy, isds_ns);
1684 /* Serialize the document into buffer */
1685 xml_buffer = xmlBufferCreate();
1686 if (!xml_buffer) {
1687 isds_log_message(context, _("Could not create xmlBuffer"));
1688 err = IE_ERROR;
1689 goto leave;
1691 /* Last argument 0 means to not format the XML tree */
1692 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8", 0);
1693 if (!save_ctx) {
1694 isds_log_message(context, _("Could not create XML serializer"));
1695 err = IE_ERROR;
1696 goto leave;
1698 /* XXX: According LibXML documentation, this function does not return
1699 * meaningful value yet */
1700 xmlSaveDoc(save_ctx, subtree_doc);
1701 if (-1 == xmlSaveFlush(save_ctx)) {
1702 isds_log_message(context,
1703 _("Could not serialize XML subtree"));
1704 err = IE_ERROR;
1705 goto leave;
1707 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1708 * even after xmlSaveFlush(). Thus close it here */
1709 xmlSaveClose(save_ctx); save_ctx = NULL;
1712 /* Store and detach buffer from xml_buffer */
1713 *buffer = xml_buffer->content;
1714 *length = xml_buffer->use;
1715 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1717 /* Shrink buffer */
1718 new_buffer = realloc(*buffer, *length);
1719 if (new_buffer) *buffer = new_buffer;
1721 leave:
1722 if (err) {
1723 zfree(*buffer);
1724 *length = 0;
1727 xmlSaveClose(save_ctx);
1728 xmlBufferFree(xml_buffer);
1729 xmlFreeDoc(subtree_doc); /* Frees subtree_copy, isds_ns etc. */
1730 return err;
1732 #endif /* HAVE_LIBCURL */
1735 #if 0
1736 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1737 * @context is session context
1738 * @document is original document where @nodeset points to
1739 * @nodeset is XPath node set to dump (recursively)
1740 * @buffer is automatically reallocated buffer where serialize to
1741 * @length is size of serialized stream in bytes
1742 * @return standard error code, free @buffer in case of error */
1743 static isds_error dump_nodeset(struct isds_ctx *context,
1744 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1745 void **buffer, size_t *length) {
1746 isds_error err = IE_SUCCESS;
1747 xmlBufferPtr xml_buffer = NULL;
1748 void *new_buffer;
1750 if (!context) return IE_INVALID_CONTEXT;
1751 if (!buffer) return IE_INVAL;
1752 zfree(*buffer);
1753 if (!document || !nodeset || !length) return IE_INVAL;
1754 *length = 0;
1756 /* Empty node set results into NULL buffer */
1757 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1758 goto leave;
1761 /* Resulting the document into buffer */
1762 xml_buffer = xmlBufferCreate();
1763 if (!xml_buffer) {
1764 isds_log_message(context, _("Could not create xmlBuffer"));
1765 err = IE_ERROR;
1766 goto leave;
1769 /* Iterate over all nodes */
1770 for (int i = 0; i < nodeset->nodeNr; i++) {
1771 /* Serialize node.
1772 * XXX: xmlNodeDump() appends to xml_buffer. */
1773 if (-1 ==
1774 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1775 isds_log_message(context, _("Could not dump XML node"));
1776 err = IE_ERROR;
1777 goto leave;
1781 /* Store and detach buffer from xml_buffer */
1782 *buffer = xml_buffer->content;
1783 *length = xml_buffer->use;
1784 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1786 /* Shrink buffer */
1787 new_buffer = realloc(*buffer, *length);
1788 if (new_buffer) *buffer = new_buffer;
1791 leave:
1792 if (err) {
1793 zfree(*buffer);
1794 *length = 0;
1797 xmlBufferFree(xml_buffer);
1798 return err;
1800 #endif
1802 #if 0
1803 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1804 * @context is session context
1805 * @document is original document where @nodeset points to
1806 * @nodeset is XPath node set to dump (recursively)
1807 * @buffer is automatically reallocated buffer where serialize to
1808 * @length is size of serialized stream in bytes
1809 * @return standard error code, free @buffer in case of error */
1810 static isds_error dump_nodeset(struct isds_ctx *context,
1811 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1812 void **buffer, size_t *length) {
1813 isds_error err = IE_SUCCESS;
1814 xmlBufferPtr xml_buffer = NULL;
1815 xmlSaveCtxtPtr save_ctx = NULL;
1816 void *new_buffer;
1818 if (!context) return IE_INVALID_CONTEXT;
1819 if (!buffer) return IE_INVAL;
1820 zfree(*buffer);
1821 if (!document || !nodeset || !length) return IE_INVAL;
1822 *length = 0;
1824 /* Empty node set results into NULL buffer */
1825 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1826 goto leave;
1829 /* Resulting the document into buffer */
1830 xml_buffer = xmlBufferCreate();
1831 if (!xml_buffer) {
1832 isds_log_message(context, _("Could not create xmlBuffer"));
1833 err = IE_ERROR;
1834 goto leave;
1836 if (xmlSubstituteEntitiesDefault(1)) {
1837 isds_log_message(context, _("Could not disable attribute escaping"));
1838 err = IE_ERROR;
1839 goto leave;
1841 /* Last argument means:
1842 * 0 to not format the XML tree
1843 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1844 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8",
1845 XML_SAVE_NO_DECL|XML_SAVE_NO_EMPTY|XML_SAVE_NO_XHTML);
1846 if (!save_ctx) {
1847 isds_log_message(context, _("Could not create XML serializer"));
1848 err = IE_ERROR;
1849 goto leave;
1851 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1852 isds_log_message(context, _("Could not disable attribute escaping"));
1853 err = IE_ERROR;
1854 goto leave;
1858 /* Iterate over all nodes */
1859 for (int i = 0; i < nodeset->nodeNr; i++) {
1860 /* Serialize node.
1861 * XXX: xmlNodeDump() appends to xml_buffer. */
1862 /*if (-1 ==
1863 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1865 /* XXX: According LibXML documentation, this function does not return
1866 * meaningful value yet */
1867 xmlSaveTree(save_ctx, nodeset->nodeTab[i]);
1868 if (-1 == xmlSaveFlush(save_ctx)) {
1869 isds_log_message(context,
1870 _("Could not serialize XML subtree"));
1871 err = IE_ERROR;
1872 goto leave;
1876 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1877 * even after xmlSaveFlush(). Thus close it here */
1878 xmlSaveClose(save_ctx); save_ctx = NULL;
1880 /* Store and detach buffer from xml_buffer */
1881 *buffer = xml_buffer->content;
1882 *length = xml_buffer->use;
1883 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1885 /* Shrink buffer */
1886 new_buffer = realloc(*buffer, *length);
1887 if (new_buffer) *buffer = new_buffer;
1889 leave:
1890 if (err) {
1891 zfree(*buffer);
1892 *length = 0;
1895 xmlSaveClose(save_ctx);
1896 xmlBufferFree(xml_buffer);
1897 return err;
1899 #endif
1902 #if HAVE_LIBCURL
1903 /* Convert UTF-8 @string representation of ISDS dbType to enum @type */
1904 static isds_error string2isds_DbType(xmlChar *string, isds_DbType *type) {
1905 if (!string || !type) return IE_INVAL;
1907 if (!xmlStrcmp(string, BAD_CAST "FO"))
1908 *type = DBTYPE_FO;
1909 else if (!xmlStrcmp(string, BAD_CAST "PFO"))
1910 *type = DBTYPE_PFO;
1911 else if (!xmlStrcmp(string, BAD_CAST "PFO_ADVOK"))
1912 *type = DBTYPE_PFO_ADVOK;
1913 else if (!xmlStrcmp(string, BAD_CAST "PFO_DANPOR"))
1914 *type = DBTYPE_PFO_DANPOR;
1915 else if (!xmlStrcmp(string, BAD_CAST "PFO_INSSPR"))
1916 *type = DBTYPE_PFO_INSSPR;
1917 else if (!xmlStrcmp(string, BAD_CAST "PO"))
1918 *type = DBTYPE_PO;
1919 else if (!xmlStrcmp(string, BAD_CAST "PO_ZAK"))
1920 *type = DBTYPE_PO_ZAK;
1921 else if (!xmlStrcmp(string, BAD_CAST "PO_REQ"))
1922 *type = DBTYPE_PO_REQ;
1923 else if (!xmlStrcmp(string, BAD_CAST "OVM"))
1924 *type = DBTYPE_OVM;
1925 else if (!xmlStrcmp(string, BAD_CAST "OVM_NOTAR"))
1926 *type = DBTYPE_OVM_NOTAR;
1927 else if (!xmlStrcmp(string, BAD_CAST "OVM_EXEKUT"))
1928 *type = DBTYPE_OVM_EXEKUT;
1929 else if (!xmlStrcmp(string, BAD_CAST "OVM_REQ"))
1930 *type = DBTYPE_OVM_REQ;
1931 else
1932 return IE_ENUM;
1933 return IE_SUCCESS;
1937 /* Convert ISDS dbType enum @type to UTF-8 string.
1938 * @Return pointer to static string, or NULL if unknown enum value */
1939 static const xmlChar *isds_DbType2string(const isds_DbType type) {
1940 switch(type) {
1941 /* DBTYPE_SYSTEM is invalid value from point of view of public
1942 * SOAP interface. */
1943 case DBTYPE_FO: return(BAD_CAST "FO"); break;
1944 case DBTYPE_PFO: return(BAD_CAST "PFO"); break;
1945 case DBTYPE_PFO_ADVOK: return(BAD_CAST "PFO_ADVOK"); break;
1946 case DBTYPE_PFO_DANPOR: return(BAD_CAST "PFO_DANPOR"); break;
1947 case DBTYPE_PFO_INSSPR: return(BAD_CAST "PFO_INSSPR"); break;
1948 case DBTYPE_PO: return(BAD_CAST "PO"); break;
1949 case DBTYPE_PO_ZAK: return(BAD_CAST "PO_ZAK"); break;
1950 case DBTYPE_PO_REQ: return(BAD_CAST "PO_REQ"); break;
1951 case DBTYPE_OVM: return(BAD_CAST "OVM"); break;
1952 case DBTYPE_OVM_NOTAR: return(BAD_CAST "OVM_NOTAR"); break;
1953 case DBTYPE_OVM_EXEKUT: return(BAD_CAST "OVM_EXEKUT"); break;
1954 case DBTYPE_OVM_REQ: return(BAD_CAST "OVM_REQ"); break;
1955 default: return NULL; break;
1960 /* Convert UTF-8 @string representation of ISDS userType to enum @type */
1961 static isds_error string2isds_UserType(xmlChar *string, isds_UserType *type) {
1962 if (!string || !type) return IE_INVAL;
1964 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
1965 *type = USERTYPE_PRIMARY;
1966 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
1967 *type = USERTYPE_ENTRUSTED;
1968 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
1969 *type = USERTYPE_ADMINISTRATOR;
1970 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
1971 *type = USERTYPE_OFFICIAL;
1972 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
1973 *type = USERTYPE_OFFICIAL_CERT;
1974 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
1975 *type = USERTYPE_LIQUIDATOR;
1976 else
1977 return IE_ENUM;
1978 return IE_SUCCESS;
1982 /* Convert ISDS userType enum @type to UTF-8 string.
1983 * @Return pointer to static string, or NULL if unknown enum value */
1984 static const xmlChar *isds_UserType2string(const isds_UserType type) {
1985 switch(type) {
1986 case USERTYPE_PRIMARY: return(BAD_CAST "PRIMARY_USER"); break;
1987 case USERTYPE_ENTRUSTED: return(BAD_CAST "ENTRUSTED_USER"); break;
1988 case USERTYPE_ADMINISTRATOR: return(BAD_CAST "ADMINISTRATOR"); break;
1989 case USERTYPE_OFFICIAL: return(BAD_CAST "OFFICIAL"); break;
1990 case USERTYPE_OFFICIAL_CERT: return(BAD_CAST "OFFICIAL_CERT"); break;
1991 case USERTYPE_LIQUIDATOR: return(BAD_CAST "LIQUIDATOR"); break;
1992 default: return NULL; break;
1997 /* Convert UTF-8 @string representation of ISDS sender type to enum @type */
1998 static isds_error string2isds_sender_type(const xmlChar *string,
1999 isds_sender_type *type) {
2000 if (!string || !type) return IE_INVAL;
2002 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
2003 *type = SENDERTYPE_PRIMARY;
2004 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
2005 *type = SENDERTYPE_ENTRUSTED;
2006 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
2007 *type = SENDERTYPE_ADMINISTRATOR;
2008 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
2009 *type = SENDERTYPE_OFFICIAL;
2010 else if (!xmlStrcmp(string, BAD_CAST "VIRTUAL"))
2011 *type = SENDERTYPE_VIRTUAL;
2012 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
2013 *type = SENDERTYPE_OFFICIAL_CERT;
2014 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
2015 *type = SENDERTYPE_LIQUIDATOR;
2016 else
2017 return IE_ENUM;
2018 return IE_SUCCESS;
2022 /* Convert UTF-8 @string representation of ISDS PDZType to enum @type */
2023 static isds_error string2isds_payment_type(const xmlChar *string,
2024 isds_payment_type *type) {
2025 if (!string || !type) return IE_INVAL;
2027 if (!xmlStrcmp(string, BAD_CAST "K"))
2028 *type = PAYMENT_SENDER;
2029 else if (!xmlStrcmp(string, BAD_CAST "O"))
2030 *type = PAYMENT_RESPONSE;
2031 else if (!xmlStrcmp(string, BAD_CAST "G"))
2032 *type = PAYMENT_SPONSOR;
2033 else if (!xmlStrcmp(string, BAD_CAST "Z"))
2034 *type = PAYMENT_SPONSOR_LIMITED;
2035 else if (!xmlStrcmp(string, BAD_CAST "D"))
2036 *type = PAYMENT_SPONSOR_EXTERNAL;
2037 else if (!xmlStrcmp(string, BAD_CAST "E"))
2038 *type = PAYMENT_STAMP;
2039 else
2040 return IE_ENUM;
2041 return IE_SUCCESS;
2045 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
2046 * @Return pointer to static string, or NULL if unknown enum value */
2047 static const xmlChar *isds_FileMetaType2string(const isds_FileMetaType type) {
2048 switch(type) {
2049 case FILEMETATYPE_MAIN: return(BAD_CAST "main"); break;
2050 case FILEMETATYPE_ENCLOSURE: return(BAD_CAST "enclosure"); break;
2051 case FILEMETATYPE_SIGNATURE: return(BAD_CAST "signature"); break;
2052 case FILEMETATYPE_META: return(BAD_CAST "meta"); break;
2053 default: return NULL; break;
2056 #endif /* HAVE_LIBCURL */
2059 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
2060 * @Return IE_ENUM if @string is not valid enum member */
2061 static isds_error string2isds_FileMetaType(const xmlChar *string,
2062 isds_FileMetaType *type) {
2063 if (!string || !type) return IE_INVAL;
2065 if (!xmlStrcmp(string, BAD_CAST "main"))
2066 *type = FILEMETATYPE_MAIN;
2067 else if (!xmlStrcmp(string, BAD_CAST "enclosure"))
2068 *type = FILEMETATYPE_ENCLOSURE;
2069 else if (!xmlStrcmp(string, BAD_CAST "signature"))
2070 *type = FILEMETATYPE_SIGNATURE;
2071 else if (!xmlStrcmp(string, BAD_CAST "meta"))
2072 *type = FILEMETATYPE_META;
2073 else
2074 return IE_ENUM;
2075 return IE_SUCCESS;
2079 /* Convert UTF-8 @string to ISDS hash @algorithm.
2080 * @Return IE_ENUM if @string is not valid enum member */
2081 static isds_error string2isds_hash_algorithm(const xmlChar *string,
2082 isds_hash_algorithm *algorithm) {
2083 if (!string || !algorithm) return IE_INVAL;
2085 if (!xmlStrcmp(string, BAD_CAST "MD5"))
2086 *algorithm = HASH_ALGORITHM_MD5;
2087 else if (!xmlStrcmp(string, BAD_CAST "SHA-1"))
2088 *algorithm = HASH_ALGORITHM_SHA_1;
2089 else if (!xmlStrcmp(string, BAD_CAST "SHA-224"))
2090 *algorithm = HASH_ALGORITHM_SHA_224;
2091 else if (!xmlStrcmp(string, BAD_CAST "SHA-256"))
2092 *algorithm = HASH_ALGORITHM_SHA_256;
2093 else if (!xmlStrcmp(string, BAD_CAST "SHA-384"))
2094 *algorithm = HASH_ALGORITHM_SHA_384;
2095 else if (!xmlStrcmp(string, BAD_CAST "SHA-512"))
2096 *algorithm = HASH_ALGORITHM_SHA_512;
2097 else
2098 return IE_ENUM;
2099 return IE_SUCCESS;
2103 #if HAVE_LIBCURL
2104 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
2105 static isds_error tm2datestring(const struct tm *time, xmlChar **string) {
2106 if (!time || !string) return IE_INVAL;
2108 if (-1 == isds_asprintf((char **) string, "%d-%02d-%02d",
2109 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
2110 return IE_ERROR;
2112 return IE_SUCCESS;
2116 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
2117 * respects the @time microseconds too. */
2118 static isds_error timeval2timestring(const struct timeval *time,
2119 xmlChar **string) {
2120 struct tm broken;
2122 if (!time || !string) return IE_INVAL;
2124 if (!gmtime_r(&time->tv_sec, &broken)) return IE_DATE;
2125 if (time->tv_usec < 0 || time->tv_usec > 999999) return IE_DATE;
2127 /* TODO: small negative year should be formatted as "-0012". This is not
2128 * true for glibc "%04d". We should implement it.
2129 * TODO: What's type of time->tv_usec exactly? Unsigned? Absolute?
2130 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
2131 if (-1 == isds_asprintf((char **) string,
2132 "%04d-%02d-%02dT%02d:%02d:%02d.%06ld",
2133 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
2134 broken.tm_hour, broken.tm_min, broken.tm_sec,
2135 time->tv_usec))
2136 return IE_ERROR;
2138 return IE_SUCCESS;
2140 #endif /* HAVE_LIBCURL */
2143 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
2144 * It respects microseconds too.
2145 * In case of error, @time will be freed. */
2146 static isds_error timestring2timeval(const xmlChar *string,
2147 struct timeval **time) {
2148 struct tm broken;
2149 char *offset, *delim, *endptr;
2150 char subseconds[7];
2151 int offset_hours, offset_minutes;
2152 int i, tmp;
2154 if (!time) return IE_INVAL;
2155 if (!string) {
2156 zfree(*time);
2157 return IE_INVAL;
2160 memset(&broken, 0, sizeof(broken));
2162 if (!*time) {
2163 *time = calloc(1, sizeof(**time));
2164 if (!*time) return IE_NOMEM;
2165 } else {
2166 memset(*time, 0, sizeof(**time));
2170 /* xsd:date is ISO 8601 string, thus ASCII */
2171 /*TODO: negative year */
2173 #ifdef _WIN32
2174 i = 0;
2175 if ((tmp = sscanf((const char*)string, "%d-%d-%dT%d:%d:%d%n",
2176 &broken.tm_year, &broken.tm_mon, &broken.tm_mday,
2177 &broken.tm_hour, &broken.tm_min, &broken.tm_sec,
2178 &i)) < 6) {
2179 zfree(*time);
2180 return IE_DATE;
2183 broken.tm_year -= 1900;
2184 broken.tm_mon--;
2185 offset = (char*)string + i;
2186 #else
2187 /* Parse date and time without subseconds and offset */
2188 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
2189 if (!offset) {
2190 zfree(*time);
2191 return IE_DATE;
2193 #endif
2195 /* Get subseconds */
2196 if (*offset == '.' ) {
2197 offset++;
2199 /* Copy first 6 digits, pad it with zeros.
2200 * XXX: It truncates longer number, no round.
2201 * Current server implementation uses only millisecond resolution. */
2202 /* TODO: isdigit() is locale sensitive */
2203 for (i = 0;
2204 i < sizeof(subseconds)/sizeof(char) - 1 && isdigit(*offset);
2205 i++, offset++) {
2206 subseconds[i] = *offset;
2208 for (; i < sizeof(subseconds)/sizeof(char) - 1; i++) {
2209 subseconds[i] = '0';
2211 subseconds[6] = '\0';
2213 /* Convert it into integer */
2214 (*time)->tv_usec = strtol(subseconds, &endptr, 10);
2215 if (*endptr != '\0' || (*time)->tv_usec == LONG_MIN ||
2216 (*time)->tv_usec == LONG_MAX) {
2217 zfree(*time);
2218 return IE_DATE;
2221 /* move to the zone offset delimiter or signal NULL*/
2222 delim = strchr(offset, '-');
2223 if (!delim)
2224 delim = strchr(offset, '+');
2225 if (!delim)
2226 delim = strchr(offset, 'Z');
2227 offset = delim;
2230 /* Get zone offset */
2231 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2232 * "" equals to "Z" and it means UTC zone. */
2233 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2234 * colon separator */
2235 if (offset && (*offset == '-' || *offset == '+')) {
2236 if (2 != sscanf(offset + 1, "%2d:%2d", &offset_hours, &offset_minutes)) {
2237 zfree(*time);
2238 return IE_DATE;
2240 if (*offset == '+') {
2241 broken.tm_hour -= offset_hours;
2242 broken.tm_min -= offset_minutes;
2243 } else {
2244 broken.tm_hour += offset_hours;
2245 broken.tm_min += offset_minutes;
2249 /* Convert to time_t */
2250 (*time)->tv_sec = _isds_timegm(&broken);
2251 if ((*time)->tv_sec == (time_t) -1) {
2252 zfree(*time);
2253 return IE_DATE;
2256 return IE_SUCCESS;
2260 /* Convert unsigned int into isds_message_status.
2261 * @context is session context
2262 * @number is pointer to number value. NULL will be treated as invalid value.
2263 * @status is automatically reallocated status
2264 * @return IE_SUCCESS, or error code and free status */
2265 static isds_error uint2isds_message_status(struct isds_ctx *context,
2266 const unsigned long int *number, isds_message_status **status) {
2267 if (!context) return IE_INVALID_CONTEXT;
2268 if (!status) return IE_INVAL;
2270 free(*status); *status = NULL;
2271 if (!number) return IE_INVAL;
2273 if (*number < 1 || *number > 10) {
2274 isds_printf_message(context, _("Invalid message status value: %lu"),
2275 *number);
2276 return IE_ENUM;
2279 *status = malloc(sizeof(**status));
2280 if (!*status) return IE_NOMEM;
2282 **status = 1 << *number;
2283 return IE_SUCCESS;
2287 /* Convert event description string into isds_event members type and
2288 * description
2289 * @string is raw event description starting with event prefix
2290 * @event is structure where to store type and stripped description to
2291 * @return standard error code, unknown prefix is not classified as an error.
2292 * */
2293 static isds_error eventstring2event(const xmlChar *string,
2294 struct isds_event* event) {
2295 const xmlChar *known_prefixes[] = {
2296 BAD_CAST "EV0:",
2297 BAD_CAST "EV1:",
2298 BAD_CAST "EV2:",
2299 BAD_CAST "EV3:",
2300 BAD_CAST "EV4:",
2301 BAD_CAST "EV5:",
2302 BAD_CAST "EV11:",
2303 BAD_CAST "EV12:",
2304 BAD_CAST "EV13:"
2306 const isds_event_type types[] = {
2307 EVENT_ENTERED_SYSTEM,
2308 EVENT_ACCEPTED_BY_RECIPIENT,
2309 EVENT_ACCEPTED_BY_FICTION,
2310 EVENT_UNDELIVERABLE,
2311 EVENT_COMMERCIAL_ACCEPTED,
2312 EVENT_DELIVERED,
2313 EVENT_PRIMARY_LOGIN,
2314 EVENT_ENTRUSTED_LOGIN,
2315 EVENT_SYSCERT_LOGIN
2317 unsigned int index;
2318 size_t length;
2320 if (!string || !event) return IE_INVAL;
2322 if (!event->type) {
2323 event->type = malloc(sizeof(*event->type));
2324 if (!(event->type)) return IE_NOMEM;
2326 zfree(event->description);
2328 for (index = 0; index < sizeof(known_prefixes)/sizeof(known_prefixes[0]);
2329 index++) {
2330 length = xmlUTF8Strlen(known_prefixes[index]);
2332 if (!xmlStrncmp(string, known_prefixes[index], length)) {
2333 /* Prefix is known */
2334 *event->type = types[index];
2336 /* Strip prefix from description and spaces */
2337 /* TODO: Recognize all white spaces from UCS blank class and
2338 * operate on UTF-8 chars. */
2339 for (; string[length] != '\0' && string[length] == ' '; length++);
2340 event->description = strdup((char *) (string + length));
2341 if (!(event->description)) return IE_NOMEM;
2343 return IE_SUCCESS;
2347 /* Unknown event prefix.
2348 * XSD allows any string */
2349 char *string_locale = _isds_utf82locale((char *) string);
2350 isds_log(ILF_ISDS, ILL_WARNING,
2351 _("Unknown delivery info event prefix: %s\n"), string_locale);
2352 free(string_locale);
2354 *event->type = EVENT_UKNOWN;
2355 event->description = strdup((char *) string);
2356 if (!(event->description)) return IE_NOMEM;
2358 return IE_SUCCESS;
2362 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2363 * and leave label */
2364 #define EXTRACT_STRING(element, string) { \
2365 xmlXPathFreeObject(result); \
2366 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2367 if (NULL == (result)) { \
2368 err = IE_ERROR; \
2369 goto leave; \
2371 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2372 if (result->nodesetval->nodeNr > 1) { \
2373 isds_printf_message(context, _("Multiple %s element"), element); \
2374 err = IE_ERROR; \
2375 goto leave; \
2377 (string) = (char *) \
2378 xmlXPathCastNodeSetToString(result->nodesetval); \
2379 if (NULL == (string)) { \
2380 err = IE_ERROR; \
2381 goto leave; \
2386 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2388 char *string = NULL; \
2389 EXTRACT_STRING(element, string); \
2391 if (string) { \
2392 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2393 if (!(booleanPtr)) { \
2394 free(string); \
2395 err = IE_NOMEM; \
2396 goto leave; \
2399 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2400 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2401 *(booleanPtr) = 1; \
2402 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2403 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2404 *(booleanPtr) = 0; \
2405 else { \
2406 char *string_locale = _isds_utf82locale((char*)string); \
2407 isds_printf_message(context, \
2408 _("%s value is not valid boolean: %s"), \
2409 element, string_locale); \
2410 free(string_locale); \
2411 free(string); \
2412 err = IE_ERROR; \
2413 goto leave; \
2416 free(string); \
2420 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2422 char *string = NULL; \
2423 EXTRACT_STRING(element, string); \
2424 if (string) { \
2425 long int number; \
2426 char *endptr; \
2428 number = strtol((char*)string, &endptr, 10); \
2430 if (*endptr != '\0') { \
2431 char *string_locale = _isds_utf82locale((char *)string); \
2432 isds_printf_message(context, \
2433 _("%s is not valid integer: %s"), \
2434 element, string_locale); \
2435 free(string_locale); \
2436 free(string); \
2437 err = IE_ISDS; \
2438 goto leave; \
2441 if (number == LONG_MIN || number == LONG_MAX) { \
2442 char *string_locale = _isds_utf82locale((char *)string); \
2443 isds_printf_message(context, \
2444 _("%s value out of range of long int: %s"), \
2445 element, string_locale); \
2446 free(string_locale); \
2447 free(string); \
2448 err = IE_ERROR; \
2449 goto leave; \
2452 free(string); string = NULL; \
2454 if (!(preallocated)) { \
2455 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2456 if (!(longintPtr)) { \
2457 err = IE_NOMEM; \
2458 goto leave; \
2461 *(longintPtr) = number; \
2465 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2467 char *string = NULL; \
2468 EXTRACT_STRING(element, string); \
2469 if (string) { \
2470 long int number; \
2471 char *endptr; \
2473 number = strtol((char*)string, &endptr, 10); \
2475 if (*endptr != '\0') { \
2476 char *string_locale = _isds_utf82locale((char *)string); \
2477 isds_printf_message(context, \
2478 _("%s is not valid integer: %s"), \
2479 element, string_locale); \
2480 free(string_locale); \
2481 free(string); \
2482 err = IE_ISDS; \
2483 goto leave; \
2486 if (number == LONG_MIN || number == LONG_MAX) { \
2487 char *string_locale = _isds_utf82locale((char *)string); \
2488 isds_printf_message(context, \
2489 _("%s value out of range of long int: %s"), \
2490 element, string_locale); \
2491 free(string_locale); \
2492 free(string); \
2493 err = IE_ERROR; \
2494 goto leave; \
2497 free(string); string = NULL; \
2498 if (number < 0) { \
2499 isds_printf_message(context, \
2500 _("%s value is negative: %ld"), element, number); \
2501 err = IE_ERROR; \
2502 goto leave; \
2505 if (!(preallocated)) { \
2506 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2507 if (!(ulongintPtr)) { \
2508 err = IE_NOMEM; \
2509 goto leave; \
2512 *(ulongintPtr) = number; \
2516 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2517 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2518 NULL); \
2519 if ((required) && (!string)) { \
2520 char *attribute_locale = _isds_utf82locale(attribute); \
2521 char *element_locale = \
2522 _isds_utf82locale((char *)xpath_ctx->node->name); \
2523 isds_printf_message(context, \
2524 _("Could not extract required %s attribute value from " \
2525 "%s element"), attribute_locale, element_locale); \
2526 free(element_locale); \
2527 free(attribute_locale); \
2528 err = IE_ERROR; \
2529 goto leave; \
2534 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2536 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2537 (xmlChar *) (string)); \
2538 if (!node) { \
2539 isds_printf_message(context, \
2540 _("Could not add %s child to %s element"), \
2541 element, (parent)->name); \
2542 err = IE_ERROR; \
2543 goto leave; \
2547 #define INSERT_STRING(parent, element, string) \
2548 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2550 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2552 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2553 else { INSERT_STRING(parent, element, "false"); } \
2556 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2558 if (booleanPtr) { \
2559 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2560 } else { \
2561 INSERT_STRING(parent, element, NULL); \
2565 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2566 if ((longintPtr)) { \
2567 /* FIXME: locale sensitive */ \
2568 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2569 err = IE_NOMEM; \
2570 goto leave; \
2572 INSERT_STRING(parent, element, buffer) \
2573 free(buffer); (buffer) = NULL; \
2574 } else { INSERT_STRING(parent, element, NULL) } \
2577 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2578 if ((ulongintPtr)) { \
2579 /* FIXME: locale sensitive */ \
2580 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2581 err = IE_NOMEM; \
2582 goto leave; \
2584 INSERT_STRING(parent, element, buffer) \
2585 free(buffer); (buffer) = NULL; \
2586 } else { INSERT_STRING(parent, element, NULL) } \
2589 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2591 /* FIXME: locale sensitive */ \
2592 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2593 err = IE_NOMEM; \
2594 goto leave; \
2596 INSERT_STRING(parent, element, buffer) \
2597 free(buffer); (buffer) = NULL; \
2600 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2601 * new attribute. */
2602 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2604 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2605 (xmlChar *) (string)); \
2606 if (!attribute_node) { \
2607 isds_printf_message(context, _("Could not add %s " \
2608 "attribute to %s element"), \
2609 (attribute), (parent)->name); \
2610 err = IE_ERROR; \
2611 goto leave; \
2615 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2616 if (string) { \
2617 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2618 if (length > (maximum)) { \
2619 isds_printf_message(context, \
2620 ngettext("%s has more than %d characters", \
2621 "%s has more than %d characters", (maximum)), \
2622 (name), (maximum)); \
2623 err = IE_2BIG; \
2624 goto leave; \
2626 if (length < (minimum)) { \
2627 isds_printf_message(context, \
2628 ngettext("%s has less than %d characters", \
2629 "%s has less than %d characters", (minimum)), \
2630 (name), (minimum)); \
2631 err = IE_2SMALL; \
2632 goto leave; \
2637 #define INSERT_ELEMENT(child, parent, element) \
2639 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2640 if (!(child)) { \
2641 isds_printf_message(context, \
2642 _("Could not add %s child to %s element"), \
2643 (element), (parent)->name); \
2644 err = IE_ERROR; \
2645 goto leave; \
2650 /* Find child element by name in given XPath context and switch context onto
2651 * it. The child must be uniq and must exist. Otherwise fails.
2652 * @context is ISDS context
2653 * @child is child element name
2654 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2655 * into it child. In error case, the @xpath_ctx keeps original value. */
2656 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
2657 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
2658 isds_error err = IE_SUCCESS;
2659 xmlXPathObjectPtr result = NULL;
2661 if (!context) return IE_INVALID_CONTEXT;
2662 if (!child || !xpath_ctx) return IE_INVAL;
2664 /* Find child */
2665 result = xmlXPathEvalExpression(child, xpath_ctx);
2666 if (!result) {
2667 err = IE_XML;
2668 goto leave;
2671 /* No match */
2672 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2673 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2674 char *child_locale = _isds_utf82locale((char*) child);
2675 isds_printf_message(context,
2676 _("%s element does not contain %s child"),
2677 parent_locale, child_locale);
2678 free(child_locale);
2679 free(parent_locale);
2680 err = IE_NOEXIST;
2681 goto leave;
2684 /* More matches */
2685 if (result->nodesetval->nodeNr > 1) {
2686 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2687 char *child_locale = _isds_utf82locale((char*) child);
2688 isds_printf_message(context,
2689 _("%s element contains multiple %s children"),
2690 parent_locale, child_locale);
2691 free(child_locale);
2692 free(parent_locale);
2693 err = IE_NOTUNIQ;
2694 goto leave;
2697 /* Switch context */
2698 xpath_ctx->node = result->nodesetval->nodeTab[0];
2700 leave:
2701 xmlXPathFreeObject(result);
2702 return err;
2707 #if HAVE_LIBCURL
2708 /* Find and convert XSD:gPersonName group in current node into structure
2709 * @context is ISDS context
2710 * @personName is automatically reallocated person name structure. If no member
2711 * value is found, will be freed.
2712 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2713 * elements
2714 * In case of error @personName will be freed. */
2715 static isds_error extract_gPersonName(struct isds_ctx *context,
2716 struct isds_PersonName **personName, xmlXPathContextPtr xpath_ctx) {
2717 isds_error err = IE_SUCCESS;
2718 xmlXPathObjectPtr result = NULL;
2720 if (!context) return IE_INVALID_CONTEXT;
2721 if (!personName) return IE_INVAL;
2722 isds_PersonName_free(personName);
2723 if (!xpath_ctx) return IE_INVAL;
2726 *personName = calloc(1, sizeof(**personName));
2727 if (!*personName) {
2728 err = IE_NOMEM;
2729 goto leave;
2732 EXTRACT_STRING("isds:pnFirstName", (*personName)->pnFirstName);
2733 EXTRACT_STRING("isds:pnMiddleName", (*personName)->pnMiddleName);
2734 EXTRACT_STRING("isds:pnLastName", (*personName)->pnLastName);
2735 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName)->pnLastNameAtBirth);
2737 if (!(*personName)->pnFirstName && !(*personName)->pnMiddleName &&
2738 !(*personName)->pnLastName && !(*personName)->pnLastNameAtBirth)
2739 isds_PersonName_free(personName);
2741 leave:
2742 if (err) isds_PersonName_free(personName);
2743 xmlXPathFreeObject(result);
2744 return err;
2748 /* Find and convert XSD:gAddress group in current node into structure
2749 * @context is ISDS context
2750 * @address is automatically reallocated address structure. If no member
2751 * value is found, will be freed.
2752 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2753 * elements
2754 * In case of error @address will be freed. */
2755 static isds_error extract_gAddress(struct isds_ctx *context,
2756 struct isds_Address **address, xmlXPathContextPtr xpath_ctx) {
2757 isds_error err = IE_SUCCESS;
2758 xmlXPathObjectPtr result = NULL;
2760 if (!context) return IE_INVALID_CONTEXT;
2761 if (!address) return IE_INVAL;
2762 isds_Address_free(address);
2763 if (!xpath_ctx) return IE_INVAL;
2766 *address = calloc(1, sizeof(**address));
2767 if (!*address) {
2768 err = IE_NOMEM;
2769 goto leave;
2772 EXTRACT_STRING("isds:adCity", (*address)->adCity);
2773 EXTRACT_STRING("isds:adStreet", (*address)->adStreet);
2774 EXTRACT_STRING("isds:adNumberInStreet", (*address)->adNumberInStreet);
2775 EXTRACT_STRING("isds:adNumberInMunicipality",
2776 (*address)->adNumberInMunicipality);
2777 EXTRACT_STRING("isds:adZipCode", (*address)->adZipCode);
2778 EXTRACT_STRING("isds:adState", (*address)->adState);
2780 if (!(*address)->adCity && !(*address)->adStreet &&
2781 !(*address)->adNumberInStreet &&
2782 !(*address)->adNumberInMunicipality &&
2783 !(*address)->adZipCode && !(*address)->adState)
2784 isds_Address_free(address);
2786 leave:
2787 if (err) isds_Address_free(address);
2788 xmlXPathFreeObject(result);
2789 return err;
2793 /* Find and convert isds:biDate element in current node into structure
2794 * @context is ISDS context
2795 * @biDate is automatically reallocated birth date structure. If no member
2796 * value is found, will be freed.
2797 * @xpath_ctx is XPath context with current node as parent for isds:biDate
2798 * element
2799 * In case of error @biDate will be freed. */
2800 static isds_error extract_BiDate(struct isds_ctx *context,
2801 struct tm **biDate, xmlXPathContextPtr xpath_ctx) {
2802 isds_error err = IE_SUCCESS;
2803 xmlXPathObjectPtr result = NULL;
2804 char *string = NULL;
2806 if (!context) return IE_INVALID_CONTEXT;
2807 if (!biDate) return IE_INVAL;
2808 zfree(*biDate);
2809 if (!xpath_ctx) return IE_INVAL;
2811 EXTRACT_STRING("isds:biDate", string);
2812 if (string) {
2813 *biDate = calloc(1, sizeof(**biDate));
2814 if (!*biDate) {
2815 err = IE_NOMEM;
2816 goto leave;
2818 err = datestring2tm((xmlChar *)string, *biDate);
2819 if (err) {
2820 if (err == IE_NOTSUP) {
2821 err = IE_ISDS;
2822 char *string_locale = _isds_utf82locale(string);
2823 isds_printf_message(context,
2824 _("Invalid isds:biDate value: %s"), string_locale);
2825 free(string_locale);
2827 goto leave;
2831 leave:
2832 if (err) zfree(*biDate);
2833 free(string);
2834 xmlXPathFreeObject(result);
2835 return err;
2839 /* Convert isds:dBOwnerInfo XML tree into structure
2840 * @context is ISDS context
2841 * @db_owner_info is automatically reallocated box owner info structure
2842 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
2843 * In case of error @db_owner_info will be freed. */
2844 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
2845 struct isds_DbOwnerInfo **db_owner_info,
2846 xmlXPathContextPtr xpath_ctx) {
2847 isds_error err = IE_SUCCESS;
2848 xmlXPathObjectPtr result = NULL;
2849 char *string = NULL;
2851 if (!context) return IE_INVALID_CONTEXT;
2852 if (!db_owner_info) return IE_INVAL;
2853 isds_DbOwnerInfo_free(db_owner_info);
2854 if (!xpath_ctx) return IE_INVAL;
2857 *db_owner_info = calloc(1, sizeof(**db_owner_info));
2858 if (!*db_owner_info) {
2859 err = IE_NOMEM;
2860 goto leave;
2863 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
2865 EXTRACT_STRING("isds:dbType", string);
2866 if (string) {
2867 (*db_owner_info)->dbType =
2868 calloc(1, sizeof(*((*db_owner_info)->dbType)));
2869 if (!(*db_owner_info)->dbType) {
2870 err = IE_NOMEM;
2871 goto leave;
2873 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
2874 if (err) {
2875 zfree((*db_owner_info)->dbType);
2876 if (err == IE_ENUM) {
2877 err = IE_ISDS;
2878 char *string_locale = _isds_utf82locale(string);
2879 isds_printf_message(context, _("Unknown isds:dbType: %s"),
2880 string_locale);
2881 free(string_locale);
2883 goto leave;
2885 zfree(string);
2888 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
2890 err = extract_gPersonName(context, &(*db_owner_info)->personName,
2891 xpath_ctx);
2892 if (err) goto leave;
2894 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
2896 (*db_owner_info)->birthInfo =
2897 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
2898 if (!(*db_owner_info)->birthInfo) {
2899 err = IE_NOMEM;
2900 goto leave;
2902 err = extract_BiDate(context, &(*db_owner_info)->birthInfo->biDate,
2903 xpath_ctx);
2904 if (err) goto leave;
2905 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
2906 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
2907 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
2908 if (!(*db_owner_info)->birthInfo->biDate &&
2909 !(*db_owner_info)->birthInfo->biCity &&
2910 !(*db_owner_info)->birthInfo->biCounty &&
2911 !(*db_owner_info)->birthInfo->biState)
2912 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
2914 err = extract_gAddress(context, &(*db_owner_info)->address, xpath_ctx);
2915 if (err) goto leave;
2917 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
2918 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
2919 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
2920 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
2921 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
2923 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
2925 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
2926 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
2927 (*db_owner_info)->dbOpenAddressing);
2929 leave:
2930 if (err) isds_DbOwnerInfo_free(db_owner_info);
2931 free(string);
2932 xmlXPathFreeObject(result);
2933 return err;
2937 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
2938 * @context is session context
2939 * @owner is libisds structure with box description
2940 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
2941 static isds_error insert_DbOwnerInfo(struct isds_ctx *context,
2942 const struct isds_DbOwnerInfo *owner, xmlNodePtr db_owner_info) {
2944 isds_error err = IE_SUCCESS;
2945 xmlNodePtr node;
2946 xmlChar *string = NULL;
2948 if (!context) return IE_INVALID_CONTEXT;
2949 if (!owner || !db_owner_info) return IE_INVAL;
2952 /* Build XSD:tDbOwnerInfo */
2953 CHECK_FOR_STRING_LENGTH(owner->dbID, 0, 7, "dbID")
2954 INSERT_STRING(db_owner_info, "dbID", owner->dbID);
2956 /* dbType */
2957 if (owner->dbType) {
2958 const xmlChar *type_string = isds_DbType2string(*(owner->dbType));
2959 if (!type_string) {
2960 isds_printf_message(context, _("Invalid dbType value: %d"),
2961 *(owner->dbType));
2962 err = IE_ENUM;
2963 goto leave;
2965 INSERT_STRING(db_owner_info, "dbType", type_string);
2967 INSERT_STRING(db_owner_info, "ic", owner->ic);
2968 if (owner->personName) {
2969 INSERT_STRING(db_owner_info, "pnFirstName",
2970 owner->personName->pnFirstName);
2971 INSERT_STRING(db_owner_info, "pnMiddleName",
2972 owner->personName->pnMiddleName);
2973 INSERT_STRING(db_owner_info, "pnLastName",
2974 owner->personName->pnLastName);
2975 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
2976 owner->personName->pnLastNameAtBirth);
2978 INSERT_STRING(db_owner_info, "firmName", owner->firmName);
2979 if (owner->birthInfo) {
2980 if (owner->birthInfo->biDate) {
2981 if (!tm2datestring(owner->birthInfo->biDate, &string))
2982 INSERT_STRING(db_owner_info, "biDate", string);
2983 free(string); string = NULL;
2985 INSERT_STRING(db_owner_info, "biCity", owner->birthInfo->biCity);
2986 INSERT_STRING(db_owner_info, "biCounty", owner->birthInfo->biCounty);
2987 INSERT_STRING(db_owner_info, "biState", owner->birthInfo->biState);
2989 if (owner->address) {
2990 INSERT_STRING(db_owner_info, "adCity", owner->address->adCity);
2991 INSERT_STRING(db_owner_info, "adStreet", owner->address->adStreet);
2992 INSERT_STRING(db_owner_info, "adNumberInStreet",
2993 owner->address->adNumberInStreet);
2994 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
2995 owner->address->adNumberInMunicipality);
2996 INSERT_STRING(db_owner_info, "adZipCode", owner->address->adZipCode);
2997 INSERT_STRING(db_owner_info, "adState", owner->address->adState);
2999 INSERT_STRING(db_owner_info, "nationality", owner->nationality);
3000 INSERT_STRING(db_owner_info, "email", owner->email);
3001 INSERT_STRING(db_owner_info, "telNumber", owner->telNumber);
3003 CHECK_FOR_STRING_LENGTH(owner->identifier, 0, 20, "identifier")
3004 INSERT_STRING(db_owner_info, "identifier", owner->identifier);
3006 CHECK_FOR_STRING_LENGTH(owner->registryCode, 0, 5, "registryCode")
3007 INSERT_STRING(db_owner_info, "registryCode", owner->registryCode);
3009 INSERT_LONGINT(db_owner_info, "dbState", owner->dbState, string);
3011 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", owner->dbEffectiveOVM);
3012 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
3013 owner->dbOpenAddressing);
3015 leave:
3016 free(string);
3017 return err;
3021 /* Convert XSD:tDbUserInfo XML tree into structure
3022 * @context is ISDS context
3023 * @db_user_info is automatically reallocated user info structure
3024 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3025 * In case of error @db_user_info will be freed. */
3026 static isds_error extract_DbUserInfo(struct isds_ctx *context,
3027 struct isds_DbUserInfo **db_user_info, xmlXPathContextPtr xpath_ctx) {
3028 isds_error err = IE_SUCCESS;
3029 xmlXPathObjectPtr result = NULL;
3030 char *string = NULL;
3032 if (!context) return IE_INVALID_CONTEXT;
3033 if (!db_user_info) return IE_INVAL;
3034 isds_DbUserInfo_free(db_user_info);
3035 if (!xpath_ctx) return IE_INVAL;
3038 *db_user_info = calloc(1, sizeof(**db_user_info));
3039 if (!*db_user_info) {
3040 err = IE_NOMEM;
3041 goto leave;
3044 EXTRACT_STRING("isds:userID", (*db_user_info)->userID);
3046 EXTRACT_STRING("isds:userType", string);
3047 if (string) {
3048 (*db_user_info)->userType =
3049 calloc(1, sizeof(*((*db_user_info)->userType)));
3050 if (!(*db_user_info)->userType) {
3051 err = IE_NOMEM;
3052 goto leave;
3054 err = string2isds_UserType((xmlChar *)string,
3055 (*db_user_info)->userType);
3056 if (err) {
3057 zfree((*db_user_info)->userType);
3058 if (err == IE_ENUM) {
3059 err = IE_ISDS;
3060 char *string_locale = _isds_utf82locale(string);
3061 isds_printf_message(context,
3062 _("Unknown isds:userType value: %s"), string_locale);
3063 free(string_locale);
3065 goto leave;
3067 zfree(string);
3070 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info)->userPrivils, 0);
3072 (*db_user_info)->personName =
3073 calloc(1, sizeof(*((*db_user_info)->personName)));
3074 if (!(*db_user_info)->personName) {
3075 err = IE_NOMEM;
3076 goto leave;
3079 err = extract_gPersonName(context, &(*db_user_info)->personName,
3080 xpath_ctx);
3081 if (err) goto leave;
3083 err = extract_gAddress(context, &(*db_user_info)->address, xpath_ctx);
3084 if (err) goto leave;
3086 err = extract_BiDate(context, &(*db_user_info)->biDate, xpath_ctx);
3087 if (err) goto leave;
3089 EXTRACT_STRING("isds:ic", (*db_user_info)->ic);
3090 EXTRACT_STRING("isds:firmName", (*db_user_info)->firmName);
3092 EXTRACT_STRING("isds:caStreet", (*db_user_info)->caStreet);
3093 EXTRACT_STRING("isds:caCity", (*db_user_info)->caCity);
3094 EXTRACT_STRING("isds:caZipCode", (*db_user_info)->caZipCode);
3096 /* ???: Default value is "CZ" according specification. Should we provide
3097 * it? */
3098 EXTRACT_STRING("isds:caState", (*db_user_info)->caState);
3100 leave:
3101 if (err) isds_DbUserInfo_free(db_user_info);
3102 free(string);
3103 xmlXPathFreeObject(result);
3104 return err;
3108 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3109 * @context is session context
3110 * @user is libisds structure with user description
3111 * @db_user_info is XML element of XSD:tDbUserInfo */
3112 static isds_error insert_DbUserInfo(struct isds_ctx *context,
3113 const struct isds_DbUserInfo *user, xmlNodePtr db_user_info) {
3115 isds_error err = IE_SUCCESS;
3116 xmlNodePtr node;
3117 xmlChar *string = NULL;
3119 if (!context) return IE_INVALID_CONTEXT;
3120 if (!user || !db_user_info) return IE_INVAL;
3122 /* Build XSD:tDbUserInfo */
3123 if (user->personName) {
3124 INSERT_STRING(db_user_info, "pnFirstName",
3125 user->personName->pnFirstName);
3126 INSERT_STRING(db_user_info, "pnMiddleName",
3127 user->personName->pnMiddleName);
3128 INSERT_STRING(db_user_info, "pnLastName",
3129 user->personName->pnLastName);
3130 INSERT_STRING(db_user_info, "pnLastNameAtBirth",
3131 user->personName->pnLastNameAtBirth);
3133 if (user->address) {
3134 INSERT_STRING(db_user_info, "adCity", user->address->adCity);
3135 INSERT_STRING(db_user_info, "adStreet", user->address->adStreet);
3136 INSERT_STRING(db_user_info, "adNumberInStreet",
3137 user->address->adNumberInStreet);
3138 INSERT_STRING(db_user_info, "adNumberInMunicipality",
3139 user->address->adNumberInMunicipality);
3140 INSERT_STRING(db_user_info, "adZipCode", user->address->adZipCode);
3141 INSERT_STRING(db_user_info, "adState", user->address->adState);
3143 if (user->biDate) {
3144 if (!tm2datestring(user->biDate, &string))
3145 INSERT_STRING(db_user_info, "biDate", string);
3146 zfree(string);
3148 CHECK_FOR_STRING_LENGTH(user->userID, 6, 12, "userID");
3149 INSERT_STRING(db_user_info, "userID", user->userID);
3151 /* userType */
3152 if (user->userType) {
3153 const xmlChar *type_string = isds_UserType2string(*(user->userType));
3154 if (!type_string) {
3155 isds_printf_message(context, _("Invalid userType value: %d"),
3156 *(user->userType));
3157 err = IE_ENUM;
3158 goto leave;
3160 INSERT_STRING(db_user_info, "userType", type_string);
3163 INSERT_LONGINT(db_user_info, "userPrivils", user->userPrivils, string);
3164 CHECK_FOR_STRING_LENGTH(user->ic, 0, 8, "ic")
3165 INSERT_STRING(db_user_info, "ic", user->ic);
3166 CHECK_FOR_STRING_LENGTH(user->firmName, 0, 100, "firmName")
3167 INSERT_STRING(db_user_info, "firmName", user->firmName);
3168 INSERT_STRING(db_user_info, "caStreet", user->caStreet);
3169 INSERT_STRING(db_user_info, "caCity", user->caCity);
3170 INSERT_STRING(db_user_info, "caZipCode", user->caZipCode);
3171 INSERT_STRING(db_user_info, "caState", user->caState);
3173 leave:
3174 free(string);
3175 return err;
3179 /* Convert XSD:tPDZRec XML tree into structure
3180 * @context is ISDS context
3181 * @permission is automatically reallocated commercial permission structure
3182 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3183 * In case of error @permission will be freed. */
3184 static isds_error extract_DbPDZRecord(struct isds_ctx *context,
3185 struct isds_commercial_permission **permission,
3186 xmlXPathContextPtr xpath_ctx) {
3187 isds_error err = IE_SUCCESS;
3188 xmlXPathObjectPtr result = NULL;
3189 char *string = NULL;
3191 if (!context) return IE_INVALID_CONTEXT;
3192 if (!permission) return IE_INVAL;
3193 isds_commercial_permission_free(permission);
3194 if (!xpath_ctx) return IE_INVAL;
3197 *permission = calloc(1, sizeof(**permission));
3198 if (!*permission) {
3199 err = IE_NOMEM;
3200 goto leave;
3203 EXTRACT_STRING("isds:PDZType", string);
3204 if (string) {
3205 err = string2isds_payment_type((xmlChar *)string,
3206 &(*permission)->type);
3207 if (err) {
3208 if (err == IE_ENUM) {
3209 err = IE_ISDS;
3210 char *string_locale = _isds_utf82locale(string);
3211 isds_printf_message(context,
3212 _("Unknown isds:PDZType value: %s"), string_locale);
3213 free(string_locale);
3215 goto leave;
3217 zfree(string);
3220 EXTRACT_STRING("isds:PDZRecip", (*permission)->recipient);
3221 EXTRACT_STRING("isds:PDZPayer", (*permission)->payer);
3223 EXTRACT_STRING("isds:PDZExpire", string);
3224 if (string) {
3225 err = timestring2timeval((xmlChar *) string,
3226 &((*permission)->expiration));
3227 if (err) {
3228 char *string_locale = _isds_utf82locale(string);
3229 if (err == IE_DATE) err = IE_ISDS;
3230 isds_printf_message(context,
3231 _("Could not convert PDZExpire as ISO time: %s"),
3232 string_locale);
3233 free(string_locale);
3234 goto leave;
3236 zfree(string);
3239 EXTRACT_ULONGINT("isds:PDZCnt", (*permission)->count, 0);
3240 EXTRACT_STRING("isds:ODZIdent", (*permission)->reply_identifier);
3242 leave:
3243 if (err) isds_commercial_permission_free(permission);
3244 free(string);
3245 xmlXPathFreeObject(result);
3246 return err;
3250 #endif /* HAVE_LIBCURL */
3253 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3254 * isds_envelope structure. The envelope is automatically allocated but not
3255 * reallocated. The date are just appended into envelope structure.
3256 * @context is ISDS context
3257 * @envelope is automatically allocated message envelope structure
3258 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3259 * In case of error @envelope will be freed. */
3260 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
3261 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3262 isds_error err = IE_SUCCESS;
3263 xmlXPathObjectPtr result = NULL;
3265 if (!context) return IE_INVALID_CONTEXT;
3266 if (!envelope) return IE_INVAL;
3267 if (!xpath_ctx) return IE_INVAL;
3270 if (!*envelope) {
3271 /* Allocate envelope */
3272 *envelope = calloc(1, sizeof(**envelope));
3273 if (!*envelope) {
3274 err = IE_NOMEM;
3275 goto leave;
3277 } else {
3278 /* Else free former data */
3279 zfree((*envelope)->dmSenderOrgUnit);
3280 zfree((*envelope)->dmSenderOrgUnitNum);
3281 zfree((*envelope)->dbIDRecipient);
3282 zfree((*envelope)->dmRecipientOrgUnit);
3283 zfree((*envelope)->dmRecipientOrgUnitNum);
3284 zfree((*envelope)->dmToHands);
3285 zfree((*envelope)->dmAnnotation);
3286 zfree((*envelope)->dmRecipientRefNumber);
3287 zfree((*envelope)->dmSenderRefNumber);
3288 zfree((*envelope)->dmRecipientIdent);
3289 zfree((*envelope)->dmSenderIdent);
3290 zfree((*envelope)->dmLegalTitleLaw);
3291 zfree((*envelope)->dmLegalTitleYear);
3292 zfree((*envelope)->dmLegalTitleSect);
3293 zfree((*envelope)->dmLegalTitlePar);
3294 zfree((*envelope)->dmLegalTitlePoint);
3295 zfree((*envelope)->dmPersonalDelivery);
3296 zfree((*envelope)->dmAllowSubstDelivery);
3299 /* Extract envelope elements added by sender or ISDS
3300 * (XSD: gMessageEnvelopeSub type) */
3301 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
3302 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3303 (*envelope)->dmSenderOrgUnitNum, 0);
3304 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
3305 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
3306 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3307 (*envelope)->dmRecipientOrgUnitNum, 0);
3308 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
3309 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
3310 EXTRACT_STRING("isds:dmRecipientRefNumber",
3311 (*envelope)->dmRecipientRefNumber);
3312 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
3313 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
3314 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
3316 /* Extract envelope elements regarding law reference */
3317 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
3318 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
3319 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
3320 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
3321 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
3323 /* Extract envelope other elements */
3324 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
3325 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3326 (*envelope)->dmAllowSubstDelivery);
3328 leave:
3329 if (err) isds_envelope_free(envelope);
3330 xmlXPathFreeObject(result);
3331 return err;
3336 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3337 * isds_envelope structure. The envelope is automatically allocated but not
3338 * reallocated. The date are just appended into envelope structure.
3339 * @context is ISDS context
3340 * @envelope is automatically allocated message envelope structure
3341 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3342 * In case of error @envelope will be freed. */
3343 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
3344 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3345 isds_error err = IE_SUCCESS;
3346 xmlXPathObjectPtr result = NULL;
3348 if (!context) return IE_INVALID_CONTEXT;
3349 if (!envelope) return IE_INVAL;
3350 if (!xpath_ctx) return IE_INVAL;
3353 if (!*envelope) {
3354 /* Allocate envelope */
3355 *envelope = calloc(1, sizeof(**envelope));
3356 if (!*envelope) {
3357 err = IE_NOMEM;
3358 goto leave;
3360 } else {
3361 /* Else free former data */
3362 zfree((*envelope)->dmID);
3363 zfree((*envelope)->dbIDSender);
3364 zfree((*envelope)->dmSender);
3365 zfree((*envelope)->dmSenderAddress);
3366 zfree((*envelope)->dmSenderType);
3367 zfree((*envelope)->dmRecipient);
3368 zfree((*envelope)->dmRecipientAddress);
3369 zfree((*envelope)->dmAmbiguousRecipient);
3372 /* Extract envelope elements added by ISDS
3373 * (XSD: gMessageEnvelope type) */
3374 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
3375 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
3376 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
3377 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
3378 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3379 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
3380 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
3381 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
3382 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3383 (*envelope)->dmAmbiguousRecipient);
3385 /* Extract envelope elements added by sender and ISDS
3386 * (XSD: gMessageEnvelope type) */
3387 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
3388 if (err) goto leave;
3390 leave:
3391 if (err) isds_envelope_free(envelope);
3392 xmlXPathFreeObject(result);
3393 return err;
3397 /* Convert other envelope elements from XML tree into isds_envelope structure:
3398 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3399 * The envelope is automatically allocated but not reallocated.
3400 * The data are just appended into envelope structure.
3401 * @context is ISDS context
3402 * @envelope is automatically allocated message envelope structure
3403 * @xpath_ctx is XPath context with current node as parent desired elements
3404 * In case of error @envelope will be freed. */
3405 static isds_error append_status_size_times(struct isds_ctx *context,
3406 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3407 isds_error err = IE_SUCCESS;
3408 xmlXPathObjectPtr result = NULL;
3409 char *string = NULL;
3410 unsigned long int *unumber = NULL;
3412 if (!context) return IE_INVALID_CONTEXT;
3413 if (!envelope) return IE_INVAL;
3414 if (!xpath_ctx) return IE_INVAL;
3417 if (!*envelope) {
3418 /* Allocate new */
3419 *envelope = calloc(1, sizeof(**envelope));
3420 if (!*envelope) {
3421 err = IE_NOMEM;
3422 goto leave;
3424 } else {
3425 /* Free old data */
3426 zfree((*envelope)->dmMessageStatus);
3427 zfree((*envelope)->dmAttachmentSize);
3428 zfree((*envelope)->dmDeliveryTime);
3429 zfree((*envelope)->dmAcceptanceTime);
3433 /* dmMessageStatus element is mandatory */
3434 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
3435 if (!unumber) {
3436 isds_log_message(context,
3437 _("Missing mandatory sisds:dmMessageStatus integer"));
3438 err = IE_ISDS;
3439 goto leave;
3441 err = uint2isds_message_status(context, unumber,
3442 &((*envelope)->dmMessageStatus));
3443 if (err) {
3444 if (err == IE_ENUM) err = IE_ISDS;
3445 goto leave;
3447 free(unumber); unumber = NULL;
3449 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
3452 EXTRACT_STRING("sisds:dmDeliveryTime", string);
3453 if (string) {
3454 err = timestring2timeval((xmlChar *) string,
3455 &((*envelope)->dmDeliveryTime));
3456 if (err) {
3457 char *string_locale = _isds_utf82locale(string);
3458 if (err == IE_DATE) err = IE_ISDS;
3459 isds_printf_message(context,
3460 _("Could not convert dmDeliveryTime as ISO time: %s"),
3461 string_locale);
3462 free(string_locale);
3463 goto leave;
3465 zfree(string);
3468 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
3469 if (string) {
3470 err = timestring2timeval((xmlChar *) string,
3471 &((*envelope)->dmAcceptanceTime));
3472 if (err) {
3473 char *string_locale = _isds_utf82locale(string);
3474 if (err == IE_DATE) err = IE_ISDS;
3475 isds_printf_message(context,
3476 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3477 string_locale);
3478 free(string_locale);
3479 goto leave;
3481 zfree(string);
3484 leave:
3485 if (err) isds_envelope_free(envelope);
3486 free(unumber);
3487 free(string);
3488 xmlXPathFreeObject(result);
3489 return err;
3493 /* Convert message type attribute of current element into isds_envelope
3494 * structure.
3495 * TODO: This function can be incorporated into append_status_size_times() as
3496 * they are called always together.
3497 * The envelope is automatically allocated but not reallocated.
3498 * The data are just appended into envelope structure.
3499 * @context is ISDS context
3500 * @envelope is automatically allocated message envelope structure
3501 * @xpath_ctx is XPath context with current node as parent of attribute
3502 * carrying message type
3503 * In case of error @envelope will be freed. */
3504 static isds_error append_message_type(struct isds_ctx *context,
3505 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3506 isds_error err = IE_SUCCESS;
3508 if (!context) return IE_INVALID_CONTEXT;
3509 if (!envelope) return IE_INVAL;
3510 if (!xpath_ctx) return IE_INVAL;
3513 if (!*envelope) {
3514 /* Allocate new */
3515 *envelope = calloc(1, sizeof(**envelope));
3516 if (!*envelope) {
3517 err = IE_NOMEM;
3518 goto leave;
3520 } else {
3521 /* Free old data */
3522 zfree((*envelope)->dmType);
3526 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope)->dmType, 0);
3528 if (!(*envelope)->dmType) {
3529 /* Use default value */
3530 (*envelope)->dmType = strdup("V");
3531 if (!(*envelope)->dmType) {
3532 err = IE_NOMEM;
3533 goto leave;
3535 } else if (1 != xmlUTF8Strlen((xmlChar *) (*envelope)->dmType)) {
3536 char *type_locale = _isds_utf82locale((*envelope)->dmType);
3537 isds_printf_message(context,
3538 _("Message type in dmType attribute is not 1 character long: "
3539 "%s"),
3540 type_locale);
3541 free(type_locale);
3542 err = IE_ISDS;
3543 goto leave;
3546 leave:
3547 if (err) isds_envelope_free(envelope);
3548 return err;
3552 #if HAVE_LIBCURL
3553 /* Convert dmType isds_envelope member into XML attribute and append it to
3554 * current node.
3555 * @context is ISDS context
3556 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3557 * @dm_envelope is XML element the resulting attribute will be appended to.
3558 * @return error code, in case of error context' message is filled. */
3559 static isds_error insert_message_type(struct isds_ctx *context,
3560 const char *type, xmlNodePtr dm_envelope) {
3561 isds_error err = IE_SUCCESS;
3562 xmlAttrPtr attribute_node;
3564 if (!context) return IE_INVALID_CONTEXT;
3565 if (!dm_envelope) return IE_INVAL;
3567 /* Insert optional message type */
3568 if (type) {
3569 if (1 != xmlUTF8Strlen((xmlChar *) type)) {
3570 char *type_locale = _isds_utf82locale(type);
3571 isds_printf_message(context,
3572 _("Message type in envelope is not 1 character long: %s"),
3573 type_locale);
3574 free(type_locale);
3575 err = IE_INVAL;
3576 goto leave;
3578 INSERT_STRING_ATTRIBUTE(dm_envelope, "dmType", type);
3581 leave:
3582 return err;
3584 #endif /* HAVE_LIBCURL */
3587 /* Extract message document into reallocated document structure
3588 * @context is ISDS context
3589 * @document is automatically reallocated message documents structure
3590 * @xpath_ctx is XPath context with current node as isds:dmFile
3591 * In case of error @document will be freed. */
3592 static isds_error extract_document(struct isds_ctx *context,
3593 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
3594 isds_error err = IE_SUCCESS;
3595 xmlXPathObjectPtr result = NULL;
3596 xmlNodePtr file_node = xpath_ctx->node;
3597 char *string = NULL;
3599 if (!context) return IE_INVALID_CONTEXT;
3600 if (!document) return IE_INVAL;
3601 isds_document_free(document);
3602 if (!xpath_ctx) return IE_INVAL;
3604 *document = calloc(1, sizeof(**document));
3605 if (!*document) {
3606 err = IE_NOMEM;
3607 goto leave;
3610 /* Extract document meta data */
3611 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
3612 if (context->normalize_mime_type) {
3613 const char *normalized_type =
3614 isds_normalize_mime_type((*document)->dmMimeType);
3615 if (NULL != normalized_type &&
3616 normalized_type != (*document)->dmMimeType) {
3617 char *new_type = strdup(normalized_type);
3618 if (NULL == new_type) {
3619 isds_printf_message(context,
3620 _("Not enough memory to normalize document MIME type"));
3621 err = IE_NOMEM;
3622 goto leave;
3624 free((*document)->dmMimeType);
3625 (*document)->dmMimeType = new_type;
3629 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
3630 err = string2isds_FileMetaType((xmlChar*)string,
3631 &((*document)->dmFileMetaType));
3632 if (err) {
3633 char *meta_type_locale = _isds_utf82locale(string);
3634 isds_printf_message(context,
3635 _("Document has invalid dmFileMetaType attribute value: %s"),
3636 meta_type_locale);
3637 free(meta_type_locale);
3638 err = IE_ISDS;
3639 goto leave;
3641 zfree(string);
3643 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
3644 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
3645 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
3646 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
3649 /* Extract document data.
3650 * Base64 encoded blob or XML subtree must be presented. */
3652 /* Check for dmEncodedContent */
3653 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
3654 xpath_ctx);
3655 if (!result) {
3656 err = IE_XML;
3657 goto leave;
3660 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3661 /* Here we have Base64 blob */
3662 (*document)->is_xml = 0;
3664 if (result->nodesetval->nodeNr > 1) {
3665 isds_printf_message(context,
3666 _("Document has more dmEncodedContent elements"));
3667 err = IE_ISDS;
3668 goto leave;
3671 xmlXPathFreeObject(result); result = NULL;
3672 EXTRACT_STRING("isds:dmEncodedContent", string);
3674 /* Decode non-empty document */
3675 if (string && string[0] != '\0') {
3676 (*document)->data_length =
3677 _isds_b64decode(string, &((*document)->data));
3678 if ((*document)->data_length == (size_t) -1) {
3679 isds_printf_message(context,
3680 _("Error while Base64-decoding document content"));
3681 err = IE_ERROR;
3682 goto leave;
3685 } else {
3686 /* No Base64 blob, try XML document */
3687 xmlXPathFreeObject(result); result = NULL;
3688 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
3689 xpath_ctx);
3690 if (!result) {
3691 err = IE_XML;
3692 goto leave;
3695 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3696 /* Here we have XML document */
3697 (*document)->is_xml = 1;
3699 if (result->nodesetval->nodeNr > 1) {
3700 isds_printf_message(context,
3701 _("Document has more dmXMLContent elements"));
3702 err = IE_ISDS;
3703 goto leave;
3706 /* XXX: We cannot serialize the content simply because:
3707 * - XML document may point out of its scope (e.g. to message
3708 * envelope)
3709 * - isds:dmXMLContent can contain more elements, no element,
3710 * a text node only
3711 * - it's not the XML way
3712 * Thus we provide the only right solution: XML DOM. Let's
3713 * application to cope with this hot potato :) */
3714 (*document)->xml_node_list =
3715 result->nodesetval->nodeTab[0]->children;
3716 } else {
3717 /* No base64 blob, nor XML document */
3718 isds_printf_message(context,
3719 _("Document has no dmEncodedContent, nor dmXMLContent "
3720 "element"));
3721 err = IE_ISDS;
3722 goto leave;
3727 leave:
3728 if (err) isds_document_free(document);
3729 free(string);
3730 xmlXPathFreeObject(result);
3731 xpath_ctx->node = file_node;
3732 return err;
3737 /* Extract message documents into reallocated list of documents
3738 * @context is ISDS context
3739 * @documents is automatically reallocated message documents list structure
3740 * @xpath_ctx is XPath context with current node as XSD tFilesArray
3741 * In case of error @documents will be freed. */
3742 static isds_error extract_documents(struct isds_ctx *context,
3743 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
3744 isds_error err = IE_SUCCESS;
3745 xmlXPathObjectPtr result = NULL;
3746 xmlNodePtr files_node = xpath_ctx->node;
3747 struct isds_list *document, *prev_document = NULL;
3749 if (!context) return IE_INVALID_CONTEXT;
3750 if (!documents) return IE_INVAL;
3751 isds_list_free(documents);
3752 if (!xpath_ctx) return IE_INVAL;
3754 /* Find documents */
3755 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
3756 if (!result) {
3757 err = IE_XML;
3758 goto leave;
3761 /* No match */
3762 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3763 isds_printf_message(context,
3764 _("Message does not contain any document"));
3765 err = IE_ISDS;
3766 goto leave;
3770 /* Iterate over documents */
3771 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
3773 /* Allocate and append list item */
3774 document = calloc(1, sizeof(*document));
3775 if (!document) {
3776 err = IE_NOMEM;
3777 goto leave;
3779 document->destructor = (void (*)(void **))isds_document_free;
3780 if (i == 0) *documents = document;
3781 else prev_document->next = document;
3782 prev_document = document;
3784 /* Extract document */
3785 xpath_ctx->node = result->nodesetval->nodeTab[i];
3786 err = extract_document(context,
3787 (struct isds_document **) &(document->data), xpath_ctx);
3788 if (err) goto leave;
3792 leave:
3793 if (err) isds_list_free(documents);
3794 xmlXPathFreeObject(result);
3795 xpath_ctx->node = files_node;
3796 return err;
3800 #if HAVE_LIBCURL
3801 /* Convert isds:dmRecord XML tree into structure
3802 * @context is ISDS context
3803 * @envelope is automatically reallocated message envelope structure
3804 * @xpath_ctx is XPath context with current node as isds:dmRecord element
3805 * In case of error @envelope will be freed. */
3806 static isds_error extract_DmRecord(struct isds_ctx *context,
3807 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3808 isds_error err = IE_SUCCESS;
3809 xmlXPathObjectPtr result = NULL;
3811 if (!context) return IE_INVALID_CONTEXT;
3812 if (!envelope) return IE_INVAL;
3813 isds_envelope_free(envelope);
3814 if (!xpath_ctx) return IE_INVAL;
3817 *envelope = calloc(1, sizeof(**envelope));
3818 if (!*envelope) {
3819 err = IE_NOMEM;
3820 goto leave;
3824 /* Extract tRecord data */
3825 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
3827 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
3828 * dmAcceptanceTime. */
3829 err = append_status_size_times(context, envelope, xpath_ctx);
3830 if (err) goto leave;
3832 /* Extract envelope elements added by sender and ISDS
3833 * (XSD: gMessageEnvelope type) */
3834 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
3835 if (err) goto leave;
3837 /* Get message type */
3838 err = append_message_type(context, envelope, xpath_ctx);
3839 if (err) goto leave;
3842 leave:
3843 if (err) isds_envelope_free(envelope);
3844 xmlXPathFreeObject(result);
3845 return err;
3849 /* Convert XSD:tStateChangesRecord type XML tree into structure
3850 * @context is ISDS context
3851 * @changed_status is automatically reallocated message state change structure
3852 * @xpath_ctx is XPath context with current node as element of
3853 * XSD:tStateChangesRecord type
3854 * In case of error @changed_status will be freed. */
3855 static isds_error extract_StateChangesRecord(struct isds_ctx *context,
3856 struct isds_message_status_change **changed_status,
3857 xmlXPathContextPtr xpath_ctx) {
3858 isds_error err = IE_SUCCESS;
3859 xmlXPathObjectPtr result = NULL;
3860 unsigned long int *unumber = NULL;
3861 char *string = NULL;
3863 if (!context) return IE_INVALID_CONTEXT;
3864 if (!changed_status) return IE_INVAL;
3865 isds_message_status_change_free(changed_status);
3866 if (!xpath_ctx) return IE_INVAL;
3869 *changed_status = calloc(1, sizeof(**changed_status));
3870 if (!*changed_status) {
3871 err = IE_NOMEM;
3872 goto leave;
3876 /* Extract tGetStateChangesInput data */
3877 EXTRACT_STRING("isds:dmID", (*changed_status)->dmID);
3879 /* dmEventTime is mandatory */
3880 EXTRACT_STRING("isds:dmEventTime", string);
3881 if (string) {
3882 err = timestring2timeval((xmlChar *) string,
3883 &((*changed_status)->time));
3884 if (err) {
3885 char *string_locale = _isds_utf82locale(string);
3886 if (err == IE_DATE) err = IE_ISDS;
3887 isds_printf_message(context,
3888 _("Could not convert dmEventTime as ISO time: %s"),
3889 string_locale);
3890 free(string_locale);
3891 goto leave;
3893 zfree(string);
3896 /* dmMessageStatus element is mandatory */
3897 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber, 0);
3898 if (!unumber) {
3899 isds_log_message(context,
3900 _("Missing mandatory isds:dmMessageStatus integer"));
3901 err = IE_ISDS;
3902 goto leave;
3904 err = uint2isds_message_status(context, unumber,
3905 &((*changed_status)->dmMessageStatus));
3906 if (err) {
3907 if (err == IE_ENUM) err = IE_ISDS;
3908 goto leave;
3910 zfree(unumber);
3913 leave:
3914 free(unumber);
3915 free(string);
3916 if (err) isds_message_status_change_free(changed_status);
3917 xmlXPathFreeObject(result);
3918 return err;
3920 #endif /* HAVE_LIBCURL */
3923 /* Find and convert isds:dmHash XML tree into structure
3924 * @context is ISDS context
3925 * @envelope is automatically reallocated message hash structure
3926 * @xpath_ctx is XPath context with current node containing isds:dmHash child
3927 * In case of error @hash will be freed. */
3928 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
3929 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
3930 isds_error err = IE_SUCCESS;
3931 xmlNodePtr old_ctx_node;
3932 xmlXPathObjectPtr result = NULL;
3933 char *string = NULL;
3935 if (!context) return IE_INVALID_CONTEXT;
3936 if (!hash) return IE_INVAL;
3937 isds_hash_free(hash);
3938 if (!xpath_ctx) return IE_INVAL;
3940 old_ctx_node = xpath_ctx->node;
3942 *hash = calloc(1, sizeof(**hash));
3943 if (!*hash) {
3944 err = IE_NOMEM;
3945 goto leave;
3948 /* Locate dmHash */
3949 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
3950 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
3951 err = IE_ISDS;
3952 goto leave;
3954 if (err) {
3955 err = IE_ERROR;
3956 goto leave;
3959 /* Get hash algorithm */
3960 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
3961 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
3962 if (err) {
3963 if (err == IE_ENUM) {
3964 char *string_locale = _isds_utf82locale(string);
3965 isds_printf_message(context, _("Unsupported hash algorithm: %s"),
3966 string_locale);
3967 free(string_locale);
3969 goto leave;
3971 zfree(string);
3973 /* Get hash value */
3974 EXTRACT_STRING(".", string);
3975 if (!string) {
3976 isds_printf_message(context,
3977 _("sisds:dmHash element is missing hash value"));
3978 err = IE_ISDS;
3979 goto leave;
3981 (*hash)->length = _isds_b64decode(string, &((*hash)->value));
3982 if ((*hash)->length == (size_t) -1) {
3983 isds_printf_message(context,
3984 _("Error while Base64-decoding hash value"));
3985 err = IE_ERROR;
3986 goto leave;
3989 leave:
3990 if (err) isds_hash_free(hash);
3991 free(string);
3992 xmlXPathFreeObject(result);
3993 xpath_ctx->node = old_ctx_node;
3994 return err;
3998 /* Find and append isds:dmQTimestamp XML tree into envelope.
3999 * Because one service is allowed to miss time-stamp content, and we think
4000 * other could too (flaw in specification), this function is deliberated and
4001 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
4002 * @context is ISDS context
4003 * @envelope is automatically allocated envelope structure
4004 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4005 * child
4006 * In case of error @envelope will be freed. */
4007 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
4008 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4009 isds_error err = IE_SUCCESS;
4010 xmlXPathObjectPtr result = NULL;
4011 char *string = NULL;
4013 if (!context) return IE_INVALID_CONTEXT;
4014 if (!envelope) return IE_INVAL;
4015 if (!xpath_ctx) {
4016 isds_envelope_free(envelope);
4017 return IE_INVAL;
4020 if (!*envelope) {
4021 *envelope = calloc(1, sizeof(**envelope));
4022 if (!*envelope) {
4023 err = IE_NOMEM;
4024 goto leave;
4026 } else {
4027 zfree((*envelope)->timestamp);
4028 (*envelope)->timestamp_length = 0;
4031 /* Get dmQTimestamp */
4032 EXTRACT_STRING("sisds:dmQTimestamp", string);
4033 if (!string) {
4034 isds_log(ILF_ISDS, ILL_INFO, _("Missing dmQTimestamp element content\n"));
4035 goto leave;
4037 (*envelope)->timestamp_length =
4038 _isds_b64decode(string, &((*envelope)->timestamp));
4039 if ((*envelope)->timestamp_length == (size_t) -1) {
4040 isds_printf_message(context,
4041 _("Error while Base64-decoding time stamp value"));
4042 err = IE_ERROR;
4043 goto leave;
4046 leave:
4047 if (err) isds_envelope_free(envelope);
4048 free(string);
4049 xmlXPathFreeObject(result);
4050 return err;
4054 /* Convert XSD tReturnedMessage XML tree into message structure.
4055 * It does not store serialized XML tree into message->raw.
4056 * It does store (pointer to) parsed XML tree into message->xml if needed.
4057 * @context is ISDS context
4058 * @include_documents Use true if documents must be extracted
4059 * (tReturnedMessage XSD type), use false if documents shall be omitted
4060 * (tReturnedMessageEnvelope).
4061 * @message is automatically reallocated message structure
4062 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4063 * type
4064 * In case of error @message will be freed. */
4065 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
4066 const _Bool include_documents, struct isds_message **message,
4067 xmlXPathContextPtr xpath_ctx) {
4068 isds_error err = IE_SUCCESS;
4069 xmlNodePtr message_node;
4071 if (!context) return IE_INVALID_CONTEXT;
4072 if (!message) return IE_INVAL;
4073 isds_message_free(message);
4074 if (!xpath_ctx) return IE_INVAL;
4077 *message = calloc(1, sizeof(**message));
4078 if (!*message) {
4079 err = IE_NOMEM;
4080 goto leave;
4083 /* Save message XPATH context node */
4084 message_node = xpath_ctx->node;
4087 /* Extract dmDM */
4088 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
4089 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
4090 if (err) { err = IE_ERROR; goto leave; }
4091 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
4092 if (err) goto leave;
4094 if (include_documents) {
4095 struct isds_list *item;
4097 /* Extract dmFiles */
4098 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
4099 xpath_ctx);
4100 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4101 err = IE_ISDS; goto leave;
4103 if (err) { err = IE_ERROR; goto leave; }
4104 err = extract_documents(context, &((*message)->documents), xpath_ctx);
4105 if (err) goto leave;
4107 /* Store xmlDoc of this message if needed */
4108 /* Only if we got a XML document in all the documents. */
4109 for (item = (*message)->documents; item; item = item->next) {
4110 if (item->data && ((struct isds_document *)item->data)->is_xml) {
4111 (*message)->xml = xpath_ctx->doc;
4112 break;
4118 /* Restore context to message */
4119 xpath_ctx->node = message_node;
4121 /* Extract dmHash */
4122 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
4123 xpath_ctx);
4124 if (err) goto leave;
4126 /* Extract dmQTimestamp, */
4127 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
4128 xpath_ctx);
4129 if (err) goto leave;
4131 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4132 * dmAcceptanceTime. */
4133 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
4134 if (err) goto leave;
4136 /* Get message type */
4137 err = append_message_type(context, &((*message)->envelope), xpath_ctx);
4138 if (err) goto leave;
4140 leave:
4141 if (err) isds_message_free(message);
4142 return err;
4146 /* Extract message event into reallocated isds_event structure
4147 * @context is ISDS context
4148 * @event is automatically reallocated message event structure
4149 * @xpath_ctx is XPath context with current node as isds:dmEvent
4150 * In case of error @event will be freed. */
4151 static isds_error extract_event(struct isds_ctx *context,
4152 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
4153 isds_error err = IE_SUCCESS;
4154 xmlXPathObjectPtr result = NULL;
4155 xmlNodePtr event_node = xpath_ctx->node;
4156 char *string = NULL;
4158 if (!context) return IE_INVALID_CONTEXT;
4159 if (!event) return IE_INVAL;
4160 isds_event_free(event);
4161 if (!xpath_ctx) return IE_INVAL;
4163 *event = calloc(1, sizeof(**event));
4164 if (!*event) {
4165 err = IE_NOMEM;
4166 goto leave;
4169 /* Extract event data.
4170 * All elements are optional according XSD. That's funny. */
4171 EXTRACT_STRING("sisds:dmEventTime", string);
4172 if (string) {
4173 err = timestring2timeval((xmlChar *) string, &((*event)->time));
4174 if (err) {
4175 char *string_locale = _isds_utf82locale(string);
4176 if (err == IE_DATE) err = IE_ISDS;
4177 isds_printf_message(context,
4178 _("Could not convert dmEventTime as ISO time: %s"),
4179 string_locale);
4180 free(string_locale);
4181 goto leave;
4183 zfree(string);
4186 /* dmEventDescr element has prefix and the rest */
4187 EXTRACT_STRING("sisds:dmEventDescr", string);
4188 if (string) {
4189 err = eventstring2event((xmlChar *) string, *event);
4190 if (err) goto leave;
4191 zfree(string);
4194 leave:
4195 if (err) isds_event_free(event);
4196 free(string);
4197 xmlXPathFreeObject(result);
4198 xpath_ctx->node = event_node;
4199 return err;
4203 /* Convert element of XSD tEventsArray type from XML tree into
4204 * isds_list of isds_event's structure. The list is automatically reallocated.
4205 * @context is ISDS context
4206 * @events is automatically reallocated list of event structures
4207 * @xpath_ctx is XPath context with current node as tEventsArray
4208 * In case of error @events will be freed. */
4209 static isds_error extract_events(struct isds_ctx *context,
4210 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
4211 isds_error err = IE_SUCCESS;
4212 xmlXPathObjectPtr result = NULL;
4213 xmlNodePtr events_node = xpath_ctx->node;
4214 struct isds_list *event, *prev_event = NULL;
4216 if (!context) return IE_INVALID_CONTEXT;
4217 if (!events) return IE_INVAL;
4218 if (!xpath_ctx) return IE_INVAL;
4220 /* Free old list */
4221 isds_list_free(events);
4223 /* Find events */
4224 result = xmlXPathEvalExpression(BAD_CAST "sisds:dmEvent", xpath_ctx);
4225 if (!result) {
4226 err = IE_XML;
4227 goto leave;
4230 /* No match */
4231 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4232 isds_printf_message(context,
4233 _("Delivery info does not contain any event"));
4234 err = IE_ISDS;
4235 goto leave;
4239 /* Iterate over events */
4240 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4242 /* Allocate and append list item */
4243 event = calloc(1, sizeof(*event));
4244 if (!event) {
4245 err = IE_NOMEM;
4246 goto leave;
4248 event->destructor = (void (*)(void **))isds_event_free;
4249 if (i == 0) *events = event;
4250 else prev_event->next = event;
4251 prev_event = event;
4253 /* Extract event */
4254 xpath_ctx->node = result->nodesetval->nodeTab[i];
4255 err = extract_event(context,
4256 (struct isds_event **) &(event->data), xpath_ctx);
4257 if (err) goto leave;
4261 leave:
4262 if (err) isds_list_free(events);
4263 xmlXPathFreeObject(result);
4264 xpath_ctx->node = events_node;
4265 return err;
4269 #if HAVE_LIBCURL
4270 /* Insert Base64 encoded data as element with text child.
4271 * @context is session context
4272 * @parent is XML node to append @element with @data as child
4273 * @ns is XML namespace of @element, use NULL to inherit from @parent
4274 * @element is UTF-8 encoded name of new element
4275 * @data is bit stream to encode into @element
4276 * @length is size of @data in bytes
4277 * @return standard error code and fill long error message if needed */
4278 static isds_error insert_base64_encoded_string(struct isds_ctx *context,
4279 xmlNodePtr parent, const xmlNsPtr ns, const char *element,
4280 const void *data, size_t length) {
4281 isds_error err = IE_SUCCESS;
4282 xmlNodePtr node;
4284 if (!context) return IE_INVALID_CONTEXT;
4285 if (!data && length > 0) return IE_INVAL;
4286 if (!parent || !element) return IE_INVAL;
4288 xmlChar *base64data = NULL;
4289 base64data = (xmlChar *) _isds_b64encode(data, length);
4290 if (!base64data) {
4291 isds_printf_message(context,
4292 ngettext("Not enough memory to encode %zd byte into Base64",
4293 "Not enough memory to encode %zd bytes into Base64",
4294 length),
4295 length);
4296 err = IE_NOMEM;
4297 goto leave;
4299 INSERT_STRING_WITH_NS(parent, ns, element, base64data);
4301 leave:
4302 free(base64data);
4303 return err;
4307 /* Convert isds_document structure into XML tree and append to dmFiles node.
4308 * @context is session context
4309 * @document is ISDS document
4310 * @dm_files is XML element the resulting tree will be appended to as a child.
4311 * @return error code, in case of error context' message is filled. */
4312 static isds_error insert_document(struct isds_ctx *context,
4313 struct isds_document *document, xmlNodePtr dm_files) {
4314 isds_error err = IE_SUCCESS;
4315 xmlNodePtr new_file = NULL, file = NULL, node;
4316 xmlAttrPtr attribute_node;
4318 if (!context) return IE_INVALID_CONTEXT;
4319 if (!document || !dm_files) return IE_INVAL;
4321 /* Allocate new dmFile */
4322 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
4323 if (!new_file) {
4324 isds_printf_message(context, _("Could not allocate main dmFile"));
4325 err = IE_ERROR;
4326 goto leave;
4328 /* Append the new dmFile.
4329 * XXX: Main document must go first */
4330 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
4331 file = xmlAddPrevSibling(dm_files->children, new_file);
4332 else
4333 file = xmlAddChild(dm_files, new_file);
4335 if (!file) {
4336 xmlFreeNode(new_file); new_file = NULL;
4337 isds_printf_message(context, _("Could not add dmFile child to "
4338 "%s element"), dm_files->name);
4339 err = IE_ERROR;
4340 goto leave;
4343 /* @dmMimeType is required */
4344 if (!document->dmMimeType) {
4345 isds_log_message(context,
4346 _("Document is missing mandatory MIME type definition"));
4347 err = IE_INVAL;
4348 goto leave;
4350 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
4352 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
4353 if (!string) {
4354 isds_printf_message(context,
4355 _("Document has unknown dmFileMetaType: %ld"),
4356 document->dmFileMetaType);
4357 err = IE_ENUM;
4358 goto leave;
4360 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
4362 if (document->dmFileGuid) {
4363 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
4365 if (document->dmUpFileGuid) {
4366 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
4369 /* @dmFileDescr is required */
4370 if (!document->dmFileDescr) {
4371 isds_log_message(context,
4372 _("Document is missing mandatory description (title)"));
4373 err = IE_INVAL;
4374 goto leave;
4376 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
4378 if (document->dmFormat) {
4379 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
4383 /* Insert content (body) of the document. */
4384 if (document->is_xml) {
4385 /* XML document requested */
4387 /* Allocate new dmXMLContent */
4388 xmlNodePtr xmlcontent = xmlNewNode(file->ns, BAD_CAST "dmXMLContent");
4389 if (!xmlcontent) {
4390 isds_printf_message(context,
4391 _("Could not allocate dmXMLContent element"));
4392 err = IE_ERROR;
4393 goto leave;
4395 /* Append it */
4396 node = xmlAddChild(file, xmlcontent);
4397 if (!node) {
4398 xmlFreeNode(xmlcontent); xmlcontent = NULL;
4399 isds_printf_message(context,
4400 _("Could not add dmXMLContent child to %s element"),
4401 file->name);
4402 err = IE_ERROR;
4403 goto leave;
4406 /* Copy non-empty node list */
4407 if (document->xml_node_list) {
4408 xmlNodePtr content = xmlDocCopyNodeList(node->doc,
4409 document->xml_node_list);
4410 if (!content) {
4411 isds_printf_message(context,
4412 _("Not enough memory to copy XML document"));
4413 err = IE_NOMEM;
4414 goto leave;
4417 if (!xmlAddChildList(node, content)) {
4418 xmlFreeNodeList(content);
4419 isds_printf_message(context,
4420 _("Error while adding XML document into dmXMLContent"));
4421 err = IE_XML;
4422 goto leave;
4424 /* XXX: We cannot free the content here because it's part of node's
4425 * document since now. It will be freed with it automatically. */
4427 } else {
4428 /* Binary document requested */
4429 err = insert_base64_encoded_string(context, file, NULL, "dmEncodedContent",
4430 document->data, document->data_length);
4431 if (err) goto leave;
4434 leave:
4435 return err;
4439 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4440 * The copy must be preallocated, the date are just appended into structure.
4441 * @context is ISDS context
4442 * @copy is message copy structure
4443 * @xpath_ctx is XPath context with current node as tMStatus */
4444 static isds_error append_TMStatus(struct isds_ctx *context,
4445 struct isds_message_copy *copy, xmlXPathContextPtr xpath_ctx) {
4446 isds_error err = IE_SUCCESS;
4447 xmlXPathObjectPtr result = NULL;
4448 char *code = NULL, *message = NULL;
4450 if (!context) return IE_INVALID_CONTEXT;
4451 if (!copy || !xpath_ctx) return IE_INVAL;
4453 /* Free old values */
4454 zfree(copy->dmStatus);
4455 zfree(copy->dmID);
4457 /* Get error specific to this copy */
4458 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code);
4459 if (!code) {
4460 isds_log_message(context,
4461 _("Missing isds:dmStatusCode under "
4462 "XSD:tMStatus type element"));
4463 err = IE_ISDS;
4464 goto leave;
4467 if (xmlStrcmp((const xmlChar *)code, BAD_CAST "0000")) {
4468 /* This copy failed */
4469 copy->error = IE_ISDS;
4470 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message);
4471 if (message) {
4472 copy->dmStatus = _isds_astrcat3(code, ": ", message);
4473 if (!copy->dmStatus) {
4474 copy->dmStatus = code;
4475 code = NULL;
4477 } else {
4478 copy->dmStatus = code;
4479 code = NULL;
4481 } else {
4482 /* This copy succeeded. In this case only, message ID is valid */
4483 copy->error = IE_SUCCESS;
4485 EXTRACT_STRING("isds:dmID", copy->dmID);
4486 if (!copy->dmID) {
4487 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
4488 "but did not returned assigned message ID\n"));
4489 err = IE_ISDS;
4493 leave:
4494 free(code);
4495 free(message);
4496 xmlXPathFreeObject(result);
4497 return err;
4501 /* Insert struct isds_approval data (box approval) into XML tree
4502 * @context is session context
4503 * @approval is libisds structure with approval description. NULL is
4504 * acceptable.
4505 * @parent is XML element to append @approval to */
4506 static isds_error insert_GExtApproval(struct isds_ctx *context,
4507 const struct isds_approval *approval, xmlNodePtr parent) {
4509 isds_error err = IE_SUCCESS;
4510 xmlNodePtr node;
4512 if (!context) return IE_INVALID_CONTEXT;
4513 if (!parent) return IE_INVAL;
4515 if (!approval) return IE_SUCCESS;
4517 /* Build XSD:gExtApproval */
4518 INSERT_SCALAR_BOOLEAN(parent, "dbApproved", approval->approved);
4519 INSERT_STRING(parent, "dbExternRefNumber", approval->refference);
4521 leave:
4522 return err;
4526 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4527 * code
4528 * @context is session context
4529 * @service_name is name of SERVICE_DB_ACCESS
4530 * @response is server SOAP body response as XML document
4531 * @raw_response is automatically reallocated bit stream with response body. Use
4532 * NULL if you don't care
4533 * @raw_response_length is size of @raw_response in bytes
4534 * @code is ISDS status code
4535 * @status_message is ISDS status message
4536 * @return error coded from lower layer, context message will be set up
4537 * appropriately. */
4538 static isds_error build_send_check_dbdummy_request(struct isds_ctx *context,
4539 const xmlChar *service_name,
4540 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
4541 xmlChar **code, xmlChar **status_message) {
4543 isds_error err = IE_SUCCESS;
4544 char *service_name_locale = NULL;
4545 xmlNodePtr request = NULL, node;
4546 xmlNsPtr isds_ns = NULL;
4548 if (!context) return IE_INVALID_CONTEXT;
4549 if (!service_name) return IE_INVAL;
4550 if (!response || !code || !status_message) return IE_INVAL;
4551 if (!raw_response_length && raw_response) return IE_INVAL;
4553 /* Free output argument */
4554 xmlFreeDoc(*response); *response = NULL;
4555 if (raw_response) zfree(*raw_response);
4556 free(*code);
4557 free(*status_message);
4560 /* Check if connection is established
4561 * TODO: This check should be done downstairs. */
4562 if (!context->curl) return IE_CONNECTION_CLOSED;
4564 service_name_locale = _isds_utf82locale((char*)service_name);
4565 if (!service_name_locale) {
4566 err = IE_NOMEM;
4567 goto leave;
4570 /* Build request */
4571 request = xmlNewNode(NULL, service_name);
4572 if (!request) {
4573 isds_printf_message(context,
4574 _("Could not build %s request"), service_name_locale);
4575 err = IE_ERROR;
4576 goto leave;
4578 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4579 if(!isds_ns) {
4580 isds_log_message(context, _("Could not create ISDS name space"));
4581 err = IE_ERROR;
4582 goto leave;
4584 xmlSetNs(request, isds_ns);
4587 /* Add XSD:tDummyInput child */
4588 INSERT_STRING(request, "dbDummy", NULL);
4591 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
4592 service_name_locale);
4594 /* Send request */
4595 err = isds(context, SERVICE_DB_ACCESS, request, response,
4596 raw_response, raw_response_length);
4597 xmlFreeNode(request); request = NULL;
4599 if (err) {
4600 isds_log(ILF_ISDS, ILL_DEBUG,
4601 _("Processing ISDS response on %s request failed\n"),
4602 service_name_locale);
4603 goto leave;
4606 /* Check for response status */
4607 err = isds_response_status(context, SERVICE_DB_ACCESS, *response,
4608 code, status_message, NULL);
4609 if (err) {
4610 isds_log(ILF_ISDS, ILL_DEBUG,
4611 _("ISDS response on %s request is missing status\n"),
4612 service_name_locale);
4613 goto leave;
4616 /* Request processed, but nothing found */
4617 if (xmlStrcmp(*code, BAD_CAST "0000")) {
4618 char *code_locale = _isds_utf82locale((char*) *code);
4619 char *status_message_locale =
4620 _isds_utf82locale((char*) *status_message);
4621 isds_log(ILF_ISDS, ILL_DEBUG,
4622 _("Server refused %s request (code=%s, message=%s)\n"),
4623 service_name_locale, code_locale, status_message_locale);
4624 isds_log_message(context, status_message_locale);
4625 free(code_locale);
4626 free(status_message_locale);
4627 err = IE_ISDS;
4628 goto leave;
4631 leave:
4632 free(service_name_locale);
4633 xmlFreeNode(request);
4634 return err;
4636 #endif
4639 /* Get data about logged in user and his box. */
4640 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
4641 struct isds_DbOwnerInfo **db_owner_info) {
4642 isds_error err = IE_SUCCESS;
4643 #if HAVE_LIBCURL
4644 xmlDocPtr response = NULL;
4645 xmlChar *code = NULL, *message = NULL;
4646 xmlXPathContextPtr xpath_ctx = NULL;
4647 xmlXPathObjectPtr result = NULL;
4648 char *string = NULL;
4649 #endif
4651 if (!context) return IE_INVALID_CONTEXT;
4652 zfree(context->long_message);
4653 if (!db_owner_info) return IE_INVAL;
4654 isds_DbOwnerInfo_free(db_owner_info);
4656 #if HAVE_LIBCURL
4657 /* Check if connection is established */
4658 if (!context->curl) return IE_CONNECTION_CLOSED;
4661 /* Do request and check for success */
4662 err = build_send_check_dbdummy_request(context,
4663 BAD_CAST "GetOwnerInfoFromLogin",
4664 &response, NULL, NULL, &code, &message);
4665 if (err) goto leave;
4668 /* Extract data */
4669 /* Prepare structure */
4670 *db_owner_info = calloc(1, sizeof(**db_owner_info));
4671 if (!*db_owner_info) {
4672 err = IE_NOMEM;
4673 goto leave;
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;
4685 /* Set context node */
4686 result = xmlXPathEvalExpression(BAD_CAST
4687 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
4688 if (!result) {
4689 err = IE_ERROR;
4690 goto leave;
4692 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4693 isds_log_message(context, _("Missing dbOwnerInfo element"));
4694 err = IE_ISDS;
4695 goto leave;
4697 if (result->nodesetval->nodeNr > 1) {
4698 isds_log_message(context, _("Multiple dbOwnerInfo element"));
4699 err = IE_ISDS;
4700 goto leave;
4702 xpath_ctx->node = result->nodesetval->nodeTab[0];
4703 xmlXPathFreeObject(result); result = NULL;
4705 /* Extract it */
4706 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
4709 leave:
4710 if (err) {
4711 isds_DbOwnerInfo_free(db_owner_info);
4714 free(string);
4715 xmlXPathFreeObject(result);
4716 xmlXPathFreeContext(xpath_ctx);
4718 free(code);
4719 free(message);
4720 xmlFreeDoc(response);
4722 if (!err)
4723 isds_log(ILF_ISDS, ILL_DEBUG,
4724 _("GetOwnerInfoFromLogin request processed by server "
4725 "successfully.\n"));
4726 #else /* not HAVE_LIBCURL */
4727 err = IE_NOTSUP;
4728 #endif
4730 return err;
4734 /* Get data about logged in user. */
4735 isds_error isds_GetUserInfoFromLogin(struct isds_ctx *context,
4736 struct isds_DbUserInfo **db_user_info) {
4737 isds_error err = IE_SUCCESS;
4738 #if HAVE_LIBCURL
4739 xmlDocPtr response = NULL;
4740 xmlChar *code = NULL, *message = NULL;
4741 xmlXPathContextPtr xpath_ctx = NULL;
4742 xmlXPathObjectPtr result = NULL;
4743 #endif
4745 if (!context) return IE_INVALID_CONTEXT;
4746 zfree(context->long_message);
4747 if (!db_user_info) return IE_INVAL;
4748 isds_DbUserInfo_free(db_user_info);
4750 #if HAVE_LIBCURL
4751 /* Check if connection is established */
4752 if (!context->curl) return IE_CONNECTION_CLOSED;
4755 /* Do request and check for success */
4756 err = build_send_check_dbdummy_request(context,
4757 BAD_CAST "GetUserInfoFromLogin",
4758 &response, NULL, NULL, &code, &message);
4759 if (err) goto leave;
4762 /* Extract data */
4763 /* Prepare structure */
4764 *db_user_info = calloc(1, sizeof(**db_user_info));
4765 if (!*db_user_info) {
4766 err = IE_NOMEM;
4767 goto leave;
4769 xpath_ctx = xmlXPathNewContext(response);
4770 if (!xpath_ctx) {
4771 err = IE_ERROR;
4772 goto leave;
4774 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4775 err = IE_ERROR;
4776 goto leave;
4779 /* Set context node */
4780 result = xmlXPathEvalExpression(BAD_CAST
4781 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx);
4782 if (!result) {
4783 err = IE_ERROR;
4784 goto leave;
4786 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4787 isds_log_message(context, _("Missing dbUserInfo element"));
4788 err = IE_ISDS;
4789 goto leave;
4791 if (result->nodesetval->nodeNr > 1) {
4792 isds_log_message(context, _("Multiple dbUserInfo element"));
4793 err = IE_ISDS;
4794 goto leave;
4796 xpath_ctx->node = result->nodesetval->nodeTab[0];
4797 xmlXPathFreeObject(result); result = NULL;
4799 /* Extract it */
4800 err = extract_DbUserInfo(context, db_user_info, xpath_ctx);
4802 leave:
4803 if (err) {
4804 isds_DbUserInfo_free(db_user_info);
4807 xmlXPathFreeObject(result);
4808 xmlXPathFreeContext(xpath_ctx);
4810 free(code);
4811 free(message);
4812 xmlFreeDoc(response);
4814 if (!err)
4815 isds_log(ILF_ISDS, ILL_DEBUG,
4816 _("GetUserInfoFromLogin request processed by server "
4817 "successfully.\n"));
4818 #else /* not HAVE_LIBCURL */
4819 err = IE_NOTSUP;
4820 #endif
4822 return err;
4826 /* Get expiration time of current password
4827 * @context is session context
4828 * @expiration is automatically reallocated time when password expires. If
4829 * password expiration is disables, NULL will be returned. In case of error
4830 * it will be nulled too. */
4831 isds_error isds_get_password_expiration(struct isds_ctx *context,
4832 struct timeval **expiration) {
4833 isds_error err = IE_SUCCESS;
4834 #if HAVE_LIBCURL
4835 xmlDocPtr response = NULL;
4836 xmlChar *code = NULL, *message = NULL;
4837 xmlXPathContextPtr xpath_ctx = NULL;
4838 xmlXPathObjectPtr result = NULL;
4839 char *string = NULL;
4840 #endif
4842 if (!context) return IE_INVALID_CONTEXT;
4843 zfree(context->long_message);
4844 if (!expiration) return IE_INVAL;
4845 zfree(*expiration);
4847 #if HAVE_LIBCURL
4848 /* Check if connection is established */
4849 if (!context->curl) return IE_CONNECTION_CLOSED;
4852 /* Do request and check for success */
4853 err = build_send_check_dbdummy_request(context,
4854 BAD_CAST "GetPasswordInfo",
4855 &response, NULL, NULL, &code, &message);
4856 if (err) goto leave;
4859 /* Extract data */
4860 xpath_ctx = xmlXPathNewContext(response);
4861 if (!xpath_ctx) {
4862 err = IE_ERROR;
4863 goto leave;
4865 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4866 err = IE_ERROR;
4867 goto leave;
4870 /* Set context node */
4871 result = xmlXPathEvalExpression(BAD_CAST
4872 "/isds:GetPasswordInfoResponse", xpath_ctx);
4873 if (!result) {
4874 err = IE_ERROR;
4875 goto leave;
4877 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4878 isds_log_message(context,
4879 _("Missing GetPasswordInfoResponse element"));
4880 err = IE_ISDS;
4881 goto leave;
4883 if (result->nodesetval->nodeNr > 1) {
4884 isds_log_message(context,
4885 _("Multiple GetPasswordInfoResponse element"));
4886 err = IE_ISDS;
4887 goto leave;
4889 xpath_ctx->node = result->nodesetval->nodeTab[0];
4890 xmlXPathFreeObject(result); result = NULL;
4892 /* Extract expiration date */
4893 EXTRACT_STRING("isds:pswExpDate", string);
4894 if (string) {
4895 /* And convert it if any returned. Otherwise expiration is disabled. */
4896 err = timestring2timeval((xmlChar *) string, expiration);
4897 if (err) {
4898 char *string_locale = _isds_utf82locale(string);
4899 if (err == IE_DATE) err = IE_ISDS;
4900 isds_printf_message(context,
4901 _("Could not convert pswExpDate as ISO time: %s"),
4902 string_locale);
4903 free(string_locale);
4904 goto leave;
4908 leave:
4909 if (err) {
4910 if (*expiration) {
4911 zfree(*expiration);
4915 free(string);
4916 xmlXPathFreeObject(result);
4917 xmlXPathFreeContext(xpath_ctx);
4919 free(code);
4920 free(message);
4921 xmlFreeDoc(response);
4923 if (!err)
4924 isds_log(ILF_ISDS, ILL_DEBUG,
4925 _("GetPasswordInfo request processed by server "
4926 "successfully.\n"));
4927 #else /* not HAVE_LIBCURL */
4928 err = IE_NOTSUP;
4929 #endif
4931 return err;
4935 #if HAVE_LIBCURL
4936 /* Request delivering new TOTP code from ISDS through side channel before
4937 * changing password.
4938 * @context is session context
4939 * @password is current password.
4940 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
4941 * Please note the @otp argument must have TOTP OTP method. See isds_login()
4942 * function for more details.
4943 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4944 * NULL, if you don't care.
4945 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
4946 * error code. */
4947 static isds_error _isds_request_totp_code(struct isds_ctx *context,
4948 const char *password, struct isds_otp *otp, char **refnumber) {
4949 isds_error err = IE_SUCCESS;
4950 char *saved_url = NULL; /* No copy */
4951 #if HAVE_CURL_REAUTHORIZATION_BUG
4952 CURL *saved_curl = NULL; /* No copy */
4953 #endif
4954 xmlNsPtr isds_ns = NULL;
4955 xmlNodePtr request = NULL;
4956 xmlDocPtr response = NULL;
4957 xmlChar *code = NULL, *message = NULL;
4958 const xmlChar *codes[] = {
4959 BAD_CAST "2300",
4960 BAD_CAST "2301",
4961 BAD_CAST "2302"
4963 const char *meanings[] = {
4964 N_("Unexpected error"),
4965 N_("One-time code cannot be re-send faster than once a 30 seconds"),
4966 N_("One-time code could not been sent. Try later again.")
4968 const isds_otp_resolution resolutions[] = {
4969 OTP_RESOLUTION_UNKNOWN,
4970 OTP_RESOLUTION_TO_FAST,
4971 OTP_RESOLUTION_TOTP_NOT_SENT
4974 if (NULL == context) return IE_INVALID_CONTEXT;
4975 zfree(context->long_message);
4976 if (NULL == password) {
4977 isds_log_message(context,
4978 _("Second argument (password) of isds_change_password() "
4979 "is NULL"));
4980 return IE_INVAL;
4983 /* Check if connection is established
4984 * TODO: This check should be done downstairs. */
4985 if (!context->curl) return IE_CONNECTION_CLOSED;
4987 if (!context->otp) {
4988 isds_log_message(context, _("This function requires OTP-authenticated "
4989 "context"));
4990 return IE_INVALID_CONTEXT;
4992 if (NULL == otp) {
4993 isds_log_message(context, _("If one-time password authentication "
4994 "method is in use, requesting new OTP code requires "
4995 "one-time credentials argument either"));
4996 return IE_INVAL;
4998 if (otp->method != OTP_TIME) {
4999 isds_log_message(context, _("Requesting new time-based OTP code from "
5000 "server requires one-time password authentication "
5001 "method"));
5002 return IE_INVAL;
5004 if (otp->otp_code != NULL) {
5005 isds_log_message(context, _("Requesting new time-based OTP code from "
5006 "server requires undefined OTP code member in "
5007 "one-time credentials argument"));
5008 return IE_INVAL;
5012 /* Build request */
5013 request = xmlNewNode(NULL, BAD_CAST "SendSMSCode");
5014 if (!request) {
5015 isds_log_message(context, _("Could not build SendSMSCode request"));
5016 return IE_ERROR;
5018 isds_ns = xmlNewNs(request, BAD_CAST OISDS_NS, NULL);
5019 if(!isds_ns) {
5020 isds_log_message(context, _("Could not create ISDS name space"));
5021 xmlFreeNode(request);
5022 return IE_ERROR;
5024 xmlSetNs(request, isds_ns);
5026 /* Change URL temporarily for sending this request only */
5028 char *new_url = NULL;
5029 if ((err = _isds_build_url_from_context(context,
5030 "%1$.*2$sasws/changePassword", &new_url))) {
5031 goto leave;
5033 saved_url = context->url;
5034 context->url = new_url;
5037 /* Store credentials for sending this request only */
5038 context->otp_credentials = otp;
5039 _isds_discard_credentials(context, 0);
5040 if ((err = _isds_store_credentials(context, context->saved_username,
5041 password, NULL))) {
5042 _isds_discard_credentials(context, 0);
5043 goto leave;
5045 #if HAVE_CURL_REAUTHORIZATION_BUG
5046 saved_curl = context->curl;
5047 context->curl = curl_easy_init();
5048 if (NULL == context->curl) {
5049 err = IE_ERROR;
5050 goto leave;
5052 if (context->timeout) {
5053 err = isds_set_timeout(context, context->timeout);
5054 if (err) goto leave;
5056 #endif
5058 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending SendSMSCode request to ISDS\n"));
5060 /* Sent request */
5061 err = isds(context, SERVICE_ASWS, request, &response, NULL, NULL);
5063 /* Remove temporal credentials */
5064 _isds_discard_credentials(context, 0);
5065 /* Detach pointer to OTP credentials from context */
5066 context->otp_credentials = NULL;
5067 /* Keep context->otp true to keep signaling this is OTP session */
5069 /* Destroy request */
5070 xmlFreeNode(request); request = NULL;
5072 if (err) {
5073 isds_log(ILF_ISDS, ILL_DEBUG,
5074 _("Processing ISDS response on SendSMSCode request failed\n"));
5075 goto leave;
5078 /* Check for response status */
5079 err = isds_response_status(context, SERVICE_ASWS, response,
5080 &code, &message, (xmlChar **)refnumber);
5081 if (err) {
5082 isds_log(ILF_ISDS, ILL_DEBUG,
5083 _("ISDS response on SendSMSCode request is missing "
5084 "status\n"));
5085 goto leave;
5088 /* Check for error */
5089 if (xmlStrcmp(code, BAD_CAST "0000")) {
5090 char *code_locale = _isds_utf82locale((char*)code);
5091 char *message_locale = _isds_utf82locale((char*)message);
5092 int i;
5093 isds_log(ILF_ISDS, ILL_DEBUG,
5094 _("Server refused to send new code on SendSMSCode "
5095 "request (code=%s, message=%s)\n"),
5096 code_locale, message_locale);
5098 /* Check for known error codes */
5099 for (i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
5100 if (!xmlStrcmp(code, codes[i])) break;
5102 if (i < sizeof(codes)/sizeof(*codes)) {
5103 isds_log_message(context, _(meanings[i]));
5104 /* Mimic otp->resolution according to the code, specification does
5105 * prescribe OTP header to be available. */
5106 if (OTP_RESOLUTION_SUCCESS == otp->resolution &&
5107 OTP_RESOLUTION_UNKNOWN != resolutions[i])
5108 otp->resolution = resolutions[i];
5109 } else
5110 isds_log_message(context, message_locale);
5112 free(code_locale);
5113 free(message_locale);
5115 err = IE_ISDS;
5116 goto leave;
5119 /* Otherwise new code sent successfully */
5120 /* Mimic otp->resolution according to the code, specification does
5121 * prescribe OTP header to be available. */
5122 if (OTP_RESOLUTION_SUCCESS == otp->resolution)
5123 otp->resolution = OTP_RESOLUTION_TOTP_SENT;
5125 leave:
5126 if (NULL != saved_url) {
5127 /* Revert URL to original one */
5128 zfree(context->url);
5129 context->url = saved_url;
5131 #if HAVE_CURL_REAUTHORIZATION_BUG
5132 if (NULL != saved_curl) {
5133 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5134 context->curl = saved_curl;
5136 #endif
5138 free(code);
5139 free(message);
5140 xmlFreeDoc(response);
5141 xmlFreeNode(request);
5143 if (!err)
5144 isds_log(ILF_ISDS, ILL_DEBUG,
5145 _("New OTP code has been sent successfully on SendSMSCode "
5146 "request.\n"));
5147 return err;
5151 /* Convert response status code to isds_error code and set long message
5152 * @context is context to save long message to
5153 * @map is mapping from codes to errors and messages. Pass NULL for generic
5154 * handling.
5155 * @code is status code to translate
5156 * @message is non-localized status message to put into long message in case
5157 * of uknown error. It can be NULL if server did not provide any.
5158 * @return desired isds_error or IE_ISDS for unknown code or IE_INVAL for
5159 * invalid invocation. */
5160 static isds_error statuscode2isds_error(struct isds_ctx *context,
5161 const struct code_map_isds_error *map,
5162 const xmlChar *code, const xmlChar *message) {
5163 if (NULL == code) {
5164 isds_log_message(context,
5165 _("NULL status code passed to statuscode2isds_error()"));
5166 return IE_INVAL;
5169 if (NULL != map) {
5170 /* Check for known error codes */
5171 for (int i=0; map->codes[i] != NULL; i++) {
5172 if (!xmlStrcmp(code, map->codes[i])) {
5173 isds_log_message(context, _(map->meanings[i]));
5174 return map->errors[i];
5179 /* Other error */
5180 if (xmlStrcmp(code, BAD_CAST "0000")) {
5181 char *message_locale = _isds_utf82locale((char*)message);
5182 if (NULL == message_locale)
5183 isds_log_message(context, _("ISDS server returned unknown error"));
5184 else
5185 isds_log_message(context, message_locale);
5186 free(message_locale);
5187 return IE_ISDS;
5190 return IE_SUCCESS;
5192 #endif
5195 /* Change user password in ISDS.
5196 * User must supply old password, new password will takes effect after some
5197 * time, current session can continue. Password must fulfill some constraints.
5198 * @context is session context
5199 * @old_password is current password.
5200 * @new_password is requested new password
5201 * @otp auxiliary data required if one-time password authentication is in use,
5202 * defines OTP code (if known) and returns fine grade resolution of OTP
5203 * procedure. Pass NULL, if one-time password authentication is not needed.
5204 * Please note the @otp argument must match OTP method used at log-in time. See
5205 * isds_login() function for more details.
5206 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5207 * NULL, if you don't care.
5208 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5209 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5210 * awaiting OTP code that has been delivered by side channel to the user. */
5211 isds_error isds_change_password(struct isds_ctx *context,
5212 const char *old_password, const char *new_password,
5213 struct isds_otp *otp, char **refnumber) {
5214 isds_error err = IE_SUCCESS;
5215 #if HAVE_LIBCURL
5216 char *saved_url = NULL; /* No copy */
5217 #if HAVE_CURL_REAUTHORIZATION_BUG
5218 CURL *saved_curl = NULL; /* No copy */
5219 #endif
5220 xmlNsPtr isds_ns = NULL;
5221 xmlNodePtr request = NULL, node;
5222 xmlDocPtr response = NULL;
5223 xmlChar *code = NULL, *message = NULL;
5224 const xmlChar *codes[] = {
5225 BAD_CAST "1066",
5226 BAD_CAST "1067",
5227 BAD_CAST "1079",
5228 BAD_CAST "1080",
5229 BAD_CAST "1081",
5230 BAD_CAST "1082",
5231 BAD_CAST "1083",
5232 BAD_CAST "1090",
5233 BAD_CAST "1091",
5234 BAD_CAST "2300",
5235 BAD_CAST "9204"
5237 const char *meanings[] = {
5238 N_("Password length must be between 8 and 32 characters"),
5239 N_("Password cannot be reused"), /* Server does not distinguish 1067
5240 and 1091 on ChangePasswordOTP */
5241 N_("Password contains forbidden character"),
5242 N_("Password must contain at least one upper-case letter, "
5243 "one lower-case, and one digit"),
5244 N_("Password cannot contain sequence of three identical characters"),
5245 N_("Password cannot contain user identifier"),
5246 N_("Password is too simmple"),
5247 N_("Old password is not valid"),
5248 N_("Password cannot be reused"),
5249 N_("Unexpected error"),
5250 N_("LDAP update error")
5252 #endif
5254 if (!context) return IE_INVALID_CONTEXT;
5255 zfree(context->long_message);
5256 if (NULL != refnumber)
5257 zfree(*refnumber);
5258 if (NULL == old_password) {
5259 isds_log_message(context,
5260 _("Second argument (old password) of isds_change_password() "
5261 "is NULL"));
5262 return IE_INVAL;
5264 if (NULL == otp && NULL == new_password) {
5265 isds_log_message(context,
5266 _("Third argument (new password) of isds_change_password() "
5267 "is NULL"));
5268 return IE_INVAL;
5271 #if HAVE_LIBCURL
5272 /* Check if connection is established
5273 * TODO: This check should be done downstairs. */
5274 if (!context->curl) return IE_CONNECTION_CLOSED;
5276 if (context->otp && NULL == otp) {
5277 isds_log_message(context, _("If one-time password authentication "
5278 "method is in use, changing password requires one-time "
5279 "credentials either"));
5280 return IE_INVAL;
5283 /* Build ChangeISDSPassword request */
5284 request = xmlNewNode(NULL, (NULL == otp) ? BAD_CAST "ChangeISDSPassword" :
5285 BAD_CAST "ChangePasswordOTP");
5286 if (!request) {
5287 isds_log_message(context, (NULL == otp) ?
5288 _("Could not build ChangeISDSPassword request") :
5289 _("Could not build ChangePasswordOTP request"));
5290 return IE_ERROR;
5292 isds_ns = xmlNewNs(request,
5293 (NULL == otp) ? BAD_CAST ISDS_NS : BAD_CAST OISDS_NS,
5294 NULL);
5295 if(!isds_ns) {
5296 isds_log_message(context, _("Could not create ISDS name space"));
5297 xmlFreeNode(request);
5298 return IE_ERROR;
5300 xmlSetNs(request, isds_ns);
5302 INSERT_STRING(request, "dbOldPassword", old_password);
5303 INSERT_STRING(request, "dbNewPassword", new_password);
5305 if (NULL != otp) {
5306 otp->resolution = OTP_RESOLUTION_UNKNOWN;
5307 switch (otp->method) {
5308 case OTP_HMAC:
5309 isds_log(ILF_SEC, ILL_INFO,
5310 _("Selected authentication method: "
5311 "HMAC-based one-time password\n"));
5312 INSERT_STRING(request, "dbOTPType", BAD_CAST "HOTP");
5313 break;
5314 case OTP_TIME:
5315 isds_log(ILF_SEC, ILL_INFO,
5316 _("Selected authentication method: "
5317 "Time-based one-time password\n"));
5318 INSERT_STRING(request, "dbOTPType", BAD_CAST "TOTP");
5319 if (otp->otp_code == NULL) {
5320 isds_log(ILF_SEC, ILL_INFO,
5321 _("OTP code has not been provided by "
5322 "application, requesting server for "
5323 "new one.\n"));
5324 err = _isds_request_totp_code(context, old_password, otp,
5325 refnumber);
5326 if (err == IE_SUCCESS) err = IE_PARTIAL_SUCCESS;
5327 goto leave;
5329 } else {
5330 isds_log(ILF_SEC, ILL_INFO,
5331 _("OTP code has been provided by "
5332 "application, not requesting server "
5333 "for new one.\n"));
5335 break;
5336 default:
5337 isds_log_message(context,
5338 _("Unknown one-time password authentication "
5339 "method requested by application"));
5340 err = IE_ENUM;
5341 goto leave;
5344 /* Change URL temporarily for sending this request only */
5346 char *new_url = NULL;
5347 if ((err = _isds_build_url_from_context(context,
5348 "%1$.*2$sasws/changePassword", &new_url))) {
5349 goto leave;
5351 saved_url = context->url;
5352 context->url = new_url;
5355 /* Store credentials for sending this request only */
5356 context->otp_credentials = otp;
5357 _isds_discard_credentials(context, 0);
5358 if ((err = _isds_store_credentials(context, context->saved_username,
5359 old_password, NULL))) {
5360 _isds_discard_credentials(context, 0);
5361 goto leave;
5363 #if HAVE_CURL_REAUTHORIZATION_BUG
5364 saved_curl = context->curl;
5365 context->curl = curl_easy_init();
5366 if (NULL == context->curl) {
5367 err = IE_ERROR;
5368 goto leave;
5370 if (context->timeout) {
5371 err = isds_set_timeout(context, context->timeout);
5372 if (err) goto leave;
5374 #endif
5377 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5378 _("Sending ChangeISDSPassword request to ISDS\n") :
5379 _("Sending ChangePasswordOTP request to ISDS\n"));
5381 /* Sent request */
5382 err = isds(context, (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS,
5383 request, &response, NULL, NULL);
5385 if (otp) {
5386 /* Remove temporal credentials */
5387 _isds_discard_credentials(context, 0);
5388 /* Detach pointer to OTP credentials from context */
5389 context->otp_credentials = NULL;
5390 /* Keep context->otp true to keep signaling this is OTP session */
5393 /* Destroy request */
5394 xmlFreeNode(request); request = NULL;
5396 if (err) {
5397 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5398 _("Processing ISDS response on ChangeISDSPassword "
5399 "request failed\n") :
5400 _("Processing ISDS response on ChangePasswordOTP "
5401 "request failed\n"));
5402 goto leave;
5405 /* Check for response status */
5406 err = isds_response_status(context,
5407 (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS, response,
5408 &code, &message, (xmlChar **)refnumber);
5409 if (err) {
5410 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5411 _("ISDS response on ChangeISDSPassword request is missing "
5412 "status\n") :
5413 _("ISDS response on ChangePasswordOTP request is missing "
5414 "status\n"));
5415 goto leave;
5418 /* Check for known error codes */
5419 for (int i=0; i < sizeof(codes)/sizeof(*codes); i++) {
5420 if (!xmlStrcmp(code, codes[i])) {
5421 char *code_locale = _isds_utf82locale((char*)code);
5422 char *message_locale = _isds_utf82locale((char*)message);
5423 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5424 _("Server refused to change password on ChangeISDSPassword "
5425 "request (code=%s, message=%s)\n") :
5426 _("Server refused to change password on ChangePasswordOTP "
5427 "request (code=%s, message=%s)\n"),
5428 code_locale, message_locale);
5429 free(code_locale);
5430 free(message_locale);
5431 isds_log_message(context, _(meanings[i]));
5432 err = IE_INVAL;
5433 goto leave;
5437 /* Other error */
5438 if (xmlStrcmp(code, BAD_CAST "0000")) {
5439 char *code_locale = _isds_utf82locale((char*)code);
5440 char *message_locale = _isds_utf82locale((char*)message);
5441 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5442 _("Server refused to change password on ChangeISDSPassword "
5443 "request (code=%s, message=%s)\n") :
5444 _("Server refused to change password on ChangePasswordOTP "
5445 "request (code=%s, message=%s)\n"),
5446 code_locale, message_locale);
5447 isds_log_message(context, message_locale);
5448 free(code_locale);
5449 free(message_locale);
5450 err = IE_ISDS;
5451 goto leave;
5454 /* Otherwise password changed successfully */
5456 leave:
5457 if (NULL != saved_url) {
5458 /* Revert URL to original one */
5459 zfree(context->url);
5460 context->url = saved_url;
5462 #if HAVE_CURL_REAUTHORIZATION_BUG
5463 if (NULL != saved_curl) {
5464 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5465 context->curl = saved_curl;
5467 #endif
5469 free(code);
5470 free(message);
5471 xmlFreeDoc(response);
5472 xmlFreeNode(request);
5474 if (!err)
5475 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5476 _("Password changed successfully on ChangeISDSPassword "
5477 "request.\n") :
5478 _("Password changed successfully on ChangePasswordOTP "
5479 "request.\n"));
5480 #else /* not HAVE_LIBCURL */
5481 err = IE_NOTSUP;
5482 #endif
5484 return err;
5488 #if HAVE_LIBCURL
5489 /* Generic middle part with request sending and response check.
5490 * It sends prepared request and checks for error code.
5491 * @context is ISDS session context.
5492 * @service is ISDS service handler
5493 * @service_name is name in scope of given @service
5494 * @request is XML tree with request. Will be freed to save memory.
5495 * @response is XML document outputting ISDS response.
5496 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5497 * @map is mapping from status code to library error. Pass NULL if no special
5498 * handling is requested.
5499 * NULL, if you don't care. */
5500 static isds_error send_destroy_request_check_response(
5501 struct isds_ctx *context,
5502 const isds_service service, const xmlChar *service_name,
5503 xmlNodePtr *request, xmlDocPtr *response, xmlChar **refnumber,
5504 const struct code_map_isds_error *map) {
5505 isds_error err = IE_SUCCESS;
5506 char *service_name_locale = NULL;
5507 xmlChar *code = NULL, *message = NULL;
5510 if (!context) return IE_INVALID_CONTEXT;
5511 if (!service_name || *service_name == '\0' || !request || !*request ||
5512 !response)
5513 return IE_INVAL;
5515 /* Check if connection is established
5516 * TODO: This check should be done downstairs. */
5517 if (!context->curl) return IE_CONNECTION_CLOSED;
5519 service_name_locale = _isds_utf82locale((char*) service_name);
5520 if (!service_name_locale) {
5521 err = IE_NOMEM;
5522 goto leave;
5525 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
5526 service_name_locale);
5528 /* Send request */
5529 err = isds(context, service, *request, response, NULL, NULL);
5530 xmlFreeNode(*request); *request = NULL;
5532 if (err) {
5533 isds_log(ILF_ISDS, ILL_DEBUG,
5534 _("Processing ISDS response on %s request failed\n"),
5535 service_name_locale);
5536 goto leave;
5539 /* Check for response status */
5540 err = isds_response_status(context, service, *response,
5541 &code, &message, refnumber);
5542 if (err) {
5543 isds_log(ILF_ISDS, ILL_DEBUG,
5544 _("ISDS response on %s request is missing status\n"),
5545 service_name_locale);
5546 goto leave;
5549 err = statuscode2isds_error(context, map, code, message);
5551 /* Request processed, but server failed */
5552 if (xmlStrcmp(code, BAD_CAST "0000")) {
5553 char *code_locale = _isds_utf82locale((char*) code);
5554 char *message_locale = _isds_utf82locale((char*) message);
5555 isds_log(ILF_ISDS, ILL_DEBUG,
5556 _("Server refused %s request (code=%s, message=%s)\n"),
5557 service_name_locale, code_locale, message_locale);
5558 free(code_locale);
5559 free(message_locale);
5560 goto leave;
5564 leave:
5565 free(code);
5566 free(message);
5567 if (err && *response) {
5568 xmlFreeDoc(*response);
5569 *response = NULL;
5571 if (*request) {
5572 xmlFreeNode(*request);
5573 *request = NULL;
5575 free(service_name_locale);
5577 return err;
5581 /* Generic bottom half with request sending.
5582 * It sends prepared request, checks for error code, destroys response and
5583 * request and log success or failure.
5584 * @context is ISDS session context.
5585 * @service is ISDS service handler
5586 * @service_name is name in scope of given @service
5587 * @request is XML tree with request. Will be freed to save memory.
5588 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5589 * NULL, if you don't care. */
5590 static isds_error send_request_check_drop_response(
5591 struct isds_ctx *context,
5592 const isds_service service, const xmlChar *service_name,
5593 xmlNodePtr *request, xmlChar **refnumber) {
5594 isds_error err = IE_SUCCESS;
5595 xmlDocPtr response = NULL;
5598 if (!context) return IE_INVALID_CONTEXT;
5599 if (!service_name || *service_name == '\0' || !request || !*request)
5600 return IE_INVAL;
5602 /* Send request and check response*/
5603 err = send_destroy_request_check_response(context,
5604 service, service_name, request, &response, refnumber, NULL);
5606 xmlFreeDoc(response);
5608 if (*request) {
5609 xmlFreeNode(*request);
5610 *request = NULL;
5613 if (!err) {
5614 char *service_name_locale = _isds_utf82locale((char *) service_name);
5615 isds_log(ILF_ISDS, ILL_DEBUG,
5616 _("%s request processed by server successfully.\n"),
5617 service_name_locale);
5618 free(service_name_locale);
5621 return err;
5625 /* Insert isds_credentials_delivery structure into XML request if not NULL
5626 * @context is session context
5627 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
5628 * credentials delivery. The email field is passed.
5629 * @parent is XML element where to insert */
5630 static isds_error insert_credentials_delivery(struct isds_ctx *context,
5631 const struct isds_credentials_delivery *credentials_delivery,
5632 xmlNodePtr parent) {
5633 isds_error err = IE_SUCCESS;
5634 xmlNodePtr node;
5636 if (!context) return IE_INVALID_CONTEXT;
5637 if (!parent) return IE_INVAL;
5639 if (credentials_delivery) {
5640 /* Following elements are valid only for services:
5641 * NewAccessData, AddDataBoxUser, CreateDataBox */
5642 INSERT_SCALAR_BOOLEAN(parent, "dbVirtual", 1);
5643 INSERT_STRING(parent, "email", credentials_delivery->email);
5646 leave:
5647 return err;
5651 /* Extract credentials delivery from ISDS response.
5652 * @context is session context
5653 * @credentials_delivery is pointer to valid structure to fill in returned
5654 * user's password (and new log-in name). If NULL, do not extract the data.
5655 * @response is pointer to XML document with ISDS response
5656 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
5657 * @return IE_SUCCESS even if new user name has not been found because it's not
5658 * clear whether it's returned always. */
5659 static isds_error extract_credentials_delivery(struct isds_ctx *context,
5660 struct isds_credentials_delivery *credentials_delivery,
5661 xmlDocPtr response, const char *request_name) {
5662 isds_error err = IE_SUCCESS;
5663 xmlXPathContextPtr xpath_ctx = NULL;
5664 xmlXPathObjectPtr result = NULL;
5665 char *xpath_query = NULL;
5667 if (!context) return IE_INVALID_CONTEXT;
5668 if (credentials_delivery) {
5669 zfree(credentials_delivery->token);
5670 zfree(credentials_delivery->new_user_name);
5672 if (!response || !request_name || !*request_name) return IE_INVAL;
5675 /* Extract optional token */
5676 if (credentials_delivery) {
5677 xpath_ctx = xmlXPathNewContext(response);
5678 if (!xpath_ctx) {
5679 err = IE_ERROR;
5680 goto leave;
5682 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5683 err = IE_ERROR;
5684 goto leave;
5687 /* Verify root element */
5688 if (-1 == isds_asprintf(&xpath_query, "/isds:%sResponse",
5689 request_name)) {
5690 err = IE_NOMEM;
5691 goto leave;
5693 result = xmlXPathEvalExpression(BAD_CAST xpath_query, xpath_ctx);
5694 if (!result) {
5695 err = IE_ERROR;
5696 goto leave;
5698 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5699 char *request_name_locale = _isds_utf82locale(request_name);
5700 isds_log(ILF_ISDS, ILL_WARNING,
5701 _("Wrong element in ISDS response for %s request "
5702 "while extracting credentials delivery details\n"),
5703 request_name_locale);
5704 free(request_name_locale);
5705 err = IE_ERROR;
5706 goto leave;
5708 xpath_ctx->node = result->nodesetval->nodeTab[0];
5711 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
5712 * optional. */
5713 EXTRACT_STRING("isds:dbUserID", credentials_delivery->new_user_name);
5715 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery->token);
5716 if (!credentials_delivery->token) {
5717 char *request_name_locale = _isds_utf82locale(request_name);
5718 isds_log(ILF_ISDS, ILL_ERR,
5719 _("ISDS did not return token on %s request "
5720 "even if requested\n"), request_name_locale);
5721 free(request_name_locale);
5722 err = IE_ERROR;
5726 leave:
5727 free(xpath_query);
5728 xmlXPathFreeObject(result);
5729 xmlXPathFreeContext(xpath_ctx);
5731 return err;
5735 /* Build XSD:tCreateDBInput request type for box creating.
5736 * @context is session context
5737 * @request outputs built XML tree
5738 * @service_name is request name of SERVICE_DB_MANIPULATION service
5739 * @box is box description to create including single primary user (in case of
5740 * FO box type)
5741 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
5742 * box, or contact address of PFO box owner)
5743 * @former_names is optional former name of box owner. Pass NULL if otherwise.
5744 * @upper_box_id is optional ID of supper box if currently created box is
5745 * subordinated.
5746 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
5747 * don't care.
5748 * @credentials_delivery is valid pointer if ISDS should return token that box
5749 * owner can use to obtain his new credentials in on-line way. Then valid email
5750 * member value should be supplied.
5751 * @approval is optional external approval of box manipulation */
5752 static isds_error build_CreateDBInput_request(struct isds_ctx *context,
5753 xmlNodePtr *request, const xmlChar *service_name,
5754 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
5755 const xmlChar *former_names, const xmlChar *upper_box_id,
5756 const xmlChar *ceo_label,
5757 const struct isds_credentials_delivery *credentials_delivery,
5758 const struct isds_approval *approval) {
5759 isds_error err = IE_SUCCESS;
5760 xmlNsPtr isds_ns = NULL;
5761 xmlNodePtr node, dbPrimaryUsers;
5762 xmlChar *string = NULL;
5763 const struct isds_list *item;
5766 if (!context) return IE_INVALID_CONTEXT;
5767 if (!request || !service_name || service_name[0] == '\0' || !box)
5768 return IE_INVAL;
5771 /* Build CreateDataBox-similar request */
5772 *request = xmlNewNode(NULL, service_name);
5773 if (!*request) {
5774 char *service_name_locale = _isds_utf82locale((char*) service_name);
5775 isds_printf_message(context, _("Could build %s request"),
5776 service_name_locale);
5777 free(service_name_locale);
5778 return IE_ERROR;
5780 if (context->type == CTX_TYPE_TESTING_REQUEST_COLLECTOR) {
5781 isds_ns = xmlNewNs(*request, BAD_CAST ISDS1_NS, NULL);
5782 if (!isds_ns) {
5783 isds_log_message(context, _("Could not create ISDS1 name space"));
5784 xmlFreeNode(*request);
5785 return IE_ERROR;
5787 } else {
5788 isds_ns = xmlNewNs(*request, BAD_CAST ISDS_NS, NULL);
5789 if (!isds_ns) {
5790 isds_log_message(context, _("Could not create ISDS name space"));
5791 xmlFreeNode(*request);
5792 return IE_ERROR;
5795 xmlSetNs(*request, isds_ns);
5797 INSERT_ELEMENT(node, *request, "dbOwnerInfo");
5798 err = insert_DbOwnerInfo(context, box, node);
5799 if (err) goto leave;
5801 /* Insert users */
5802 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
5803 * verbose documentation allows none dbUserInfo */
5804 INSERT_ELEMENT(dbPrimaryUsers, *request, "dbPrimaryUsers");
5805 for (item = users; item; item = item->next) {
5806 if (item->data) {
5807 INSERT_ELEMENT(node, dbPrimaryUsers, "dbUserInfo");
5808 err = insert_DbUserInfo(context,
5809 (struct isds_DbUserInfo *) item->data, node);
5810 if (err) goto leave;
5814 INSERT_STRING(*request, "dbFormerNames", former_names);
5815 INSERT_STRING(*request, "dbUpperDBId", upper_box_id);
5816 INSERT_STRING(*request, "dbCEOLabel", ceo_label);
5818 err = insert_credentials_delivery(context, credentials_delivery, *request);
5819 if (err) goto leave;
5821 err = insert_GExtApproval(context, approval, *request);
5822 if (err) goto leave;
5824 leave:
5825 if (err) {
5826 xmlFreeNode(*request);
5827 *request = NULL;
5829 free(string);
5830 return err;
5832 #endif /* HAVE_LIBCURL */
5835 /* Create new box.
5836 * @context is session context
5837 * @box is box description to create including single primary user (in case of
5838 * FO box type). It outputs box ID assigned by ISDS in dbID element.
5839 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
5840 * box, or contact address of PFO box owner)
5841 * @former_names is optional former name of box owner. Pass NULL if you don't care.
5842 * @upper_box_id is optional ID of supper box if currently created box is
5843 * subordinated.
5844 * @ceo_label is optional title of OVM box owner (e.g. mayor)
5845 * @credentials_delivery is NULL if new password should be delivered off-line
5846 * to box owner. It is valid pointer if owner should obtain new password on-line
5847 * on dedicated web server. Then input @credentials_delivery.email value is
5848 * his e-mail address he must provide to dedicated web server together
5849 * with output reallocated @credentials_delivery.token member. Output
5850 * member @credentials_delivery.new_user_name is unused up on this call.
5851 * @approval is optional external approval of box manipulation
5852 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5853 * NULL, if you don't care.*/
5854 isds_error isds_add_box(struct isds_ctx *context,
5855 struct isds_DbOwnerInfo *box, const struct isds_list *users,
5856 const char *former_names, const char *upper_box_id,
5857 const char *ceo_label,
5858 struct isds_credentials_delivery *credentials_delivery,
5859 const struct isds_approval *approval, char **refnumber) {
5860 isds_error err = IE_SUCCESS;
5861 #if HAVE_LIBCURL
5862 xmlNodePtr request = NULL;
5863 xmlDocPtr response = NULL;
5864 xmlXPathContextPtr xpath_ctx = NULL;
5865 xmlXPathObjectPtr result = NULL;
5866 #endif
5869 if (!context) return IE_INVALID_CONTEXT;
5870 zfree(context->long_message);
5871 if (credentials_delivery) {
5872 zfree(credentials_delivery->token);
5873 zfree(credentials_delivery->new_user_name);
5875 if (!box) return IE_INVAL;
5877 #if HAVE_LIBCURL
5878 /* Scratch box ID */
5879 zfree(box->dbID);
5881 /* Build CreateDataBox request */
5882 err = build_CreateDBInput_request(context,
5883 &request, BAD_CAST "CreateDataBox",
5884 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
5885 (xmlChar *) ceo_label, credentials_delivery, approval);
5886 if (err) goto leave;
5888 /* Send it to server and process response */
5889 err = send_destroy_request_check_response(context,
5890 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
5891 &response, (xmlChar **) refnumber, NULL);
5893 /* Extract box ID */
5894 xpath_ctx = xmlXPathNewContext(response);
5895 if (!xpath_ctx) {
5896 err = IE_ERROR;
5897 goto leave;
5899 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5900 err = IE_ERROR;
5901 goto leave;
5903 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
5905 /* Extract optional token */
5906 err = extract_credentials_delivery(context, credentials_delivery, response,
5907 "CreateDataBox");
5909 leave:
5910 xmlXPathFreeObject(result);
5911 xmlXPathFreeContext(xpath_ctx);
5912 xmlFreeDoc(response);
5913 xmlFreeNode(request);
5915 if (!err) {
5916 isds_log(ILF_ISDS, ILL_DEBUG,
5917 _("CreateDataBox request processed by server successfully.\n"));
5919 #else /* not HAVE_LIBCURL */
5920 err = IE_NOTSUP;
5921 #endif
5923 return err;
5927 /* Notify ISDS about new PFO entity.
5928 * This function has no real effect.
5929 * @context is session context
5930 * @box is PFO description including single primary user.
5931 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
5932 * @former_names is optional undocumented string. Pass NULL if you don't care.
5933 * @upper_box_id is optional ID of supper box if currently created box is
5934 * subordinated.
5935 * @ceo_label is optional title of OVM box owner (e.g. mayor)
5936 * @approval is optional external approval of box manipulation
5937 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5938 * NULL, if you don't care.*/
5939 isds_error isds_add_pfoinfo(struct isds_ctx *context,
5940 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
5941 const char *former_names, const char *upper_box_id,
5942 const char *ceo_label, const struct isds_approval *approval,
5943 char **refnumber) {
5944 isds_error err = IE_SUCCESS;
5945 #if HAVE_LIBCURL
5946 xmlNodePtr request = NULL;
5947 #endif
5949 if (!context) return IE_INVALID_CONTEXT;
5950 zfree(context->long_message);
5951 if (!box) return IE_INVAL;
5953 #if HAVE_LIBCURL
5954 /* Build CreateDataBoxPFOInfo request */
5955 err = build_CreateDBInput_request(context,
5956 &request, BAD_CAST "CreateDataBoxPFOInfo",
5957 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
5958 (xmlChar *) ceo_label, NULL, approval);
5959 if (err) goto leave;
5961 /* Send it to server and process response */
5962 err = send_request_check_drop_response(context,
5963 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
5964 (xmlChar **) refnumber);
5965 /* XXX: XML Schema names output dbID element but textual documentation
5966 * states no box identifier is returned. */
5967 leave:
5968 xmlFreeNode(request);
5969 #else /* not HAVE_LIBCURL */
5970 err = IE_NOTSUP;
5971 #endif
5972 return err;
5976 /* Common implementation for removing given box.
5977 * @context is session context
5978 * @service_name is UTF-8 encoded name fo ISDS service
5979 * @box is box description to delete
5980 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
5981 * carry sane value. If NULL, do not inject this information into request.
5982 * @approval is optional external approval of box manipulation
5983 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5984 * NULL, if you don't care.*/
5985 isds_error _isds_delete_box_common(struct isds_ctx *context,
5986 const xmlChar *service_name,
5987 const struct isds_DbOwnerInfo *box, const struct tm *since,
5988 const struct isds_approval *approval, char **refnumber) {
5989 isds_error err = IE_SUCCESS;
5990 #if HAVE_LIBCURL
5991 xmlNsPtr isds_ns = NULL;
5992 xmlNodePtr request = NULL;
5993 xmlNodePtr node;
5994 xmlChar *string = NULL;
5995 #endif
5998 if (!context) return IE_INVALID_CONTEXT;
5999 zfree(context->long_message);
6000 if (!service_name || !*service_name || !box) return IE_INVAL;
6003 #if HAVE_LIBCURL
6004 /* Build DeleteDataBox(Promptly) request */
6005 request = xmlNewNode(NULL, service_name);
6006 if (!request) {
6007 char *service_name_locale = _isds_utf82locale((char*)service_name);
6008 isds_printf_message(context,
6009 _("Could build %s request"), service_name_locale);
6010 free(service_name_locale);
6011 return IE_ERROR;
6013 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6014 if(!isds_ns) {
6015 isds_log_message(context, _("Could not create ISDS name space"));
6016 xmlFreeNode(request);
6017 return IE_ERROR;
6019 xmlSetNs(request, isds_ns);
6021 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6022 err = insert_DbOwnerInfo(context, box, node);
6023 if (err) goto leave;
6025 if (since) {
6026 err = tm2datestring(since, &string);
6027 if (err) {
6028 isds_log_message(context,
6029 _("Could not convert `since' argument to ISO date string"));
6030 goto leave;
6032 INSERT_STRING(request, "dbOwnerTerminationDate", string);
6033 zfree(string);
6036 err = insert_GExtApproval(context, approval, request);
6037 if (err) goto leave;
6040 /* Send it to server and process response */
6041 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6042 service_name, &request, (xmlChar **) refnumber);
6044 leave:
6045 xmlFreeNode(request);
6046 free(string);
6047 #else /* not HAVE_LIBCURL */
6048 err = IE_NOTSUP;
6049 #endif
6050 return err;
6054 /* Remove given box permanently.
6055 * @context is session context
6056 * @box is box description to delete
6057 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6058 * carry sane value.
6059 * @approval is optional external approval of box manipulation
6060 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6061 * NULL, if you don't care.*/
6062 isds_error isds_delete_box(struct isds_ctx *context,
6063 const struct isds_DbOwnerInfo *box, const struct tm *since,
6064 const struct isds_approval *approval, char **refnumber) {
6065 if (!context) return IE_INVALID_CONTEXT;
6066 zfree(context->long_message);
6067 if (!box || !since) return IE_INVAL;
6069 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBox",
6070 box, since, approval, refnumber);
6074 /* Undocumented function.
6075 * @context is session context
6076 * @box is box description to delete
6077 * @approval is optional external approval of box manipulation
6078 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6079 * NULL, if you don't care.*/
6080 isds_error isds_delete_box_promptly(struct isds_ctx *context,
6081 const struct isds_DbOwnerInfo *box,
6082 const struct isds_approval *approval, char **refnumber) {
6083 if (!context) return IE_INVALID_CONTEXT;
6084 zfree(context->long_message);
6085 if (!box) return IE_INVAL;
6087 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBoxPromptly",
6088 box, NULL, approval, refnumber);
6092 /* Update data about given box.
6093 * @context is session context
6094 * @old_box current box description
6095 * @new_box are updated data about @old_box
6096 * @approval is optional external approval of box manipulation
6097 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6098 * NULL, if you don't care.*/
6099 isds_error isds_UpdateDataBoxDescr(struct isds_ctx *context,
6100 const struct isds_DbOwnerInfo *old_box,
6101 const struct isds_DbOwnerInfo *new_box,
6102 const struct isds_approval *approval, char **refnumber) {
6103 isds_error err = IE_SUCCESS;
6104 #if HAVE_LIBCURL
6105 xmlNsPtr isds_ns = NULL;
6106 xmlNodePtr request = NULL;
6107 xmlNodePtr node;
6108 #endif
6111 if (!context) return IE_INVALID_CONTEXT;
6112 zfree(context->long_message);
6113 if (!old_box || !new_box) return IE_INVAL;
6116 #if HAVE_LIBCURL
6117 /* Build UpdateDataBoxDescr request */
6118 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxDescr");
6119 if (!request) {
6120 isds_log_message(context,
6121 _("Could build UpdateDataBoxDescr request"));
6122 return IE_ERROR;
6124 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6125 if(!isds_ns) {
6126 isds_log_message(context, _("Could not create ISDS name space"));
6127 xmlFreeNode(request);
6128 return IE_ERROR;
6130 xmlSetNs(request, isds_ns);
6132 INSERT_ELEMENT(node, request, "dbOldOwnerInfo");
6133 err = insert_DbOwnerInfo(context, old_box, node);
6134 if (err) goto leave;
6136 INSERT_ELEMENT(node, request, "dbNewOwnerInfo");
6137 err = insert_DbOwnerInfo(context, new_box, node);
6138 if (err) goto leave;
6140 err = insert_GExtApproval(context, approval, request);
6141 if (err) goto leave;
6144 /* Send it to server and process response */
6145 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6146 BAD_CAST "UpdateDataBoxDescr", &request, (xmlChar **) refnumber);
6148 leave:
6149 xmlFreeNode(request);
6150 #else /* not HAVE_LIBCURL */
6151 err = IE_NOTSUP;
6152 #endif
6154 return err;
6158 #if HAVE_LIBCURL
6159 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6160 * code
6161 * @context is session context
6162 * @service is SOAP service
6163 * @service_name is name of request in @service
6164 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6165 * @box_id is box ID of interest
6166 * @approval is optional external approval of box manipulation
6167 * @response is server SOAP body response as XML document
6168 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6169 * NULL, if you don't care.
6170 * @return error coded from lower layer, context message will be set up
6171 * appropriately. */
6172 static isds_error build_send_dbid_request_check_response(
6173 struct isds_ctx *context, const isds_service service,
6174 const xmlChar *service_name, const xmlChar *box_id_element,
6175 const xmlChar *box_id, const struct isds_approval *approval,
6176 xmlDocPtr *response, xmlChar **refnumber) {
6178 isds_error err = IE_SUCCESS;
6179 char *service_name_locale = NULL, *box_id_locale = NULL;
6180 xmlNodePtr request = NULL, node;
6181 xmlNsPtr isds_ns = NULL;
6183 if (!context) return IE_INVALID_CONTEXT;
6184 if (!service_name || !box_id) return IE_INVAL;
6185 if (!response) return IE_INVAL;
6187 /* Free output argument */
6188 xmlFreeDoc(*response); *response = NULL;
6190 /* Prepare strings */
6191 service_name_locale = _isds_utf82locale((char*)service_name);
6192 if (!service_name_locale) {
6193 err = IE_NOMEM;
6194 goto leave;
6196 box_id_locale = _isds_utf82locale((char*)box_id);
6197 if (!box_id_locale) {
6198 err = IE_NOMEM;
6199 goto leave;
6202 /* Build request */
6203 request = xmlNewNode(NULL, service_name);
6204 if (!request) {
6205 isds_printf_message(context,
6206 _("Could not build %s request for %s box"), service_name_locale,
6207 box_id_locale);
6208 err = IE_ERROR;
6209 goto leave;
6211 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6212 if(!isds_ns) {
6213 isds_log_message(context, _("Could not create ISDS name space"));
6214 err = IE_ERROR;
6215 goto leave;
6217 xmlSetNs(request, isds_ns);
6219 /* Add XSD:tIdDbInput children */
6220 if (NULL == box_id_element) box_id_element = BAD_CAST "dbID";
6221 INSERT_STRING(request, box_id_element, box_id);
6222 err = insert_GExtApproval(context, approval, request);
6223 if (err) goto leave;
6225 /* Send request and check response*/
6226 err = send_destroy_request_check_response(context,
6227 service, service_name, &request, response, refnumber, NULL);
6229 leave:
6230 free(service_name_locale);
6231 free(box_id_locale);
6232 xmlFreeNode(request);
6233 return err;
6235 #endif /* HAVE_LIBCURL */
6238 /* Get data about all users assigned to given box.
6239 * @context is session context
6240 * @box_id is box ID
6241 * @users is automatically reallocated list of struct isds_DbUserInfo */
6242 isds_error isds_GetDataBoxUsers(struct isds_ctx *context, const char *box_id,
6243 struct isds_list **users) {
6244 isds_error err = IE_SUCCESS;
6245 #if HAVE_LIBCURL
6246 xmlDocPtr response = NULL;
6247 xmlXPathContextPtr xpath_ctx = NULL;
6248 xmlXPathObjectPtr result = NULL;
6249 int i;
6250 struct isds_list *item, *prev_item = NULL;
6251 #endif
6253 if (!context) return IE_INVALID_CONTEXT;
6254 zfree(context->long_message);
6255 if (!users || !box_id) return IE_INVAL;
6256 isds_list_free(users);
6259 #if HAVE_LIBCURL
6260 /* Do request and check for success */
6261 err = build_send_dbid_request_check_response(context,
6262 SERVICE_DB_MANIPULATION, BAD_CAST "GetDataBoxUsers", NULL,
6263 BAD_CAST box_id, NULL, &response, NULL);
6264 if (err) goto leave;
6267 /* Extract data */
6268 /* Prepare structure */
6269 xpath_ctx = xmlXPathNewContext(response);
6270 if (!xpath_ctx) {
6271 err = IE_ERROR;
6272 goto leave;
6274 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6275 err = IE_ERROR;
6276 goto leave;
6279 /* Set context node */
6280 result = xmlXPathEvalExpression(BAD_CAST
6281 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6282 xpath_ctx);
6283 if (!result) {
6284 err = IE_ERROR;
6285 goto leave;
6287 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6288 /* Iterate over all users */
6289 for (i = 0; i < result->nodesetval->nodeNr; i++) {
6291 /* Prepare structure */
6292 item = calloc(1, sizeof(*item));
6293 if (!item) {
6294 err = IE_NOMEM;
6295 goto leave;
6297 item->destructor = (void(*)(void**))isds_DbUserInfo_free;
6298 if (i == 0) *users = item;
6299 else prev_item->next = item;
6300 prev_item = item;
6302 /* Extract it */
6303 xpath_ctx->node = result->nodesetval->nodeTab[i];
6304 err = extract_DbUserInfo(context,
6305 (struct isds_DbUserInfo **) (&item->data), xpath_ctx);
6306 if (err) goto leave;
6310 leave:
6311 if (err) {
6312 isds_list_free(users);
6315 xmlXPathFreeObject(result);
6316 xmlXPathFreeContext(xpath_ctx);
6317 xmlFreeDoc(response);
6319 if (!err)
6320 isds_log(ILF_ISDS, ILL_DEBUG,
6321 _("GetDataBoxUsers request processed by server "
6322 "successfully.\n"));
6323 #else /* not HAVE_LIBCURL */
6324 err = IE_NOTSUP;
6325 #endif
6327 return err;
6331 /* Update data about user assigned to given box.
6332 * @context is session context
6333 * @box is box identification
6334 * @old_user identifies user to update
6335 * @new_user are updated data about @old_user
6336 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6337 * NULL, if you don't care.*/
6338 isds_error isds_UpdateDataBoxUser(struct isds_ctx *context,
6339 const struct isds_DbOwnerInfo *box,
6340 const struct isds_DbUserInfo *old_user,
6341 const struct isds_DbUserInfo *new_user,
6342 char **refnumber) {
6343 isds_error err = IE_SUCCESS;
6344 #if HAVE_LIBCURL
6345 xmlNsPtr isds_ns = NULL;
6346 xmlNodePtr request = NULL;
6347 xmlNodePtr node;
6348 #endif
6351 if (!context) return IE_INVALID_CONTEXT;
6352 zfree(context->long_message);
6353 if (!box || !old_user || !new_user) return IE_INVAL;
6356 #if HAVE_LIBCURL
6357 /* Build UpdateDataBoxUser request */
6358 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxUser");
6359 if (!request) {
6360 isds_log_message(context,
6361 _("Could build UpdateDataBoxUser request"));
6362 return IE_ERROR;
6364 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6365 if(!isds_ns) {
6366 isds_log_message(context, _("Could not create ISDS name space"));
6367 xmlFreeNode(request);
6368 return IE_ERROR;
6370 xmlSetNs(request, isds_ns);
6372 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6373 err = insert_DbOwnerInfo(context, box, node);
6374 if (err) goto leave;
6376 INSERT_ELEMENT(node, request, "dbOldUserInfo");
6377 err = insert_DbUserInfo(context, old_user, node);
6378 if (err) goto leave;
6380 INSERT_ELEMENT(node, request, "dbNewUserInfo");
6381 err = insert_DbUserInfo(context, new_user, node);
6382 if (err) goto leave;
6384 /* Send it to server and process response */
6385 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6386 BAD_CAST "UpdateDataBoxUser", &request, (xmlChar **) refnumber);
6388 leave:
6389 xmlFreeNode(request);
6390 #else /* not HAVE_LIBCURL */
6391 err = IE_NOTSUP;
6392 #endif
6394 return err;
6398 /* Undocumented function.
6399 * @context is session context
6400 * @box_id is UTF-8 encoded box identifier
6401 * @token is UTF-8 encoded temporary password
6402 * @user_id outputs UTF-8 encoded reallocated user identifier
6403 * @password outpus UTF-8 encoded reallocated user password
6404 * Output arguments will be nulled in case of error */
6405 isds_error isds_activate(struct isds_ctx *context,
6406 const char *box_id, const char *token,
6407 char **user_id, char **password) {
6408 isds_error err = IE_SUCCESS;
6409 #if HAVE_LIBCURL
6410 xmlNsPtr isds_ns = NULL;
6411 xmlNodePtr request = NULL, node;
6412 xmlDocPtr response = NULL;
6413 xmlXPathContextPtr xpath_ctx = NULL;
6414 xmlXPathObjectPtr result = NULL;
6415 #endif
6418 if (!context) return IE_INVALID_CONTEXT;
6419 zfree(context->long_message);
6421 if (user_id) zfree(*user_id);
6422 if (password) zfree(*password);
6424 if (!box_id || !token || !user_id || !password) return IE_INVAL;
6427 #if HAVE_LIBCURL
6428 /* Build Activate request */
6429 request = xmlNewNode(NULL, BAD_CAST "Activate");
6430 if (!request) {
6431 isds_log_message(context, _("Could build Activate request"));
6432 return IE_ERROR;
6434 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6435 if(!isds_ns) {
6436 isds_log_message(context, _("Could not create ISDS name space"));
6437 xmlFreeNode(request);
6438 return IE_ERROR;
6440 xmlSetNs(request, isds_ns);
6442 INSERT_STRING(request, "dbAccessDataId", token);
6443 CHECK_FOR_STRING_LENGTH(box_id, 7, 7, "dbID");
6444 INSERT_STRING(request, "dbID", box_id);
6447 /* Send request and check response*/
6448 err = send_destroy_request_check_response(context,
6449 SERVICE_DB_MANIPULATION, BAD_CAST "Activate", &request,
6450 &response, NULL, NULL);
6451 if (err) goto leave;
6454 /* Extract data */
6455 xpath_ctx = xmlXPathNewContext(response);
6456 if (!xpath_ctx) {
6457 err = IE_ERROR;
6458 goto leave;
6460 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6461 err = IE_ERROR;
6462 goto leave;
6464 result = xmlXPathEvalExpression(BAD_CAST "/isds:ActivateResponse",
6465 xpath_ctx);
6466 if (!result) {
6467 err = IE_ERROR;
6468 goto leave;
6470 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6471 isds_log_message(context, _("Missing ActivateResponse element"));
6472 err = IE_ISDS;
6473 goto leave;
6475 if (result->nodesetval->nodeNr > 1) {
6476 isds_log_message(context, _("Multiple ActivateResponse element"));
6477 err = IE_ISDS;
6478 goto leave;
6480 xpath_ctx->node = result->nodesetval->nodeTab[0];
6481 xmlXPathFreeObject(result); result = NULL;
6483 EXTRACT_STRING("isds:userId", *user_id);
6484 if (!*user_id)
6485 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6486 "but did not return `userId' element.\n"));
6488 EXTRACT_STRING("isds:password", *password);
6489 if (!*password)
6490 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6491 "but did not return `password' element.\n"));
6493 leave:
6494 xmlXPathFreeObject(result);
6495 xmlXPathFreeContext(xpath_ctx);
6496 xmlFreeDoc(response);
6497 xmlFreeNode(request);
6499 if (!err)
6500 isds_log(ILF_ISDS, ILL_DEBUG,
6501 _("Activate request processed by server successfully.\n"));
6502 #else /* not HAVE_LIBCURL */
6503 err = IE_NOTSUP;
6504 #endif
6506 return err;
6510 /* Reset credentials of user assigned to given box.
6511 * @context is session context
6512 * @box is box identification
6513 * @user identifies user to reset password
6514 * @fee_paid is true if fee has been paid, false otherwise
6515 * @approval is optional external approval of box manipulation
6516 * @credentials_delivery is NULL if new password should be delivered off-line
6517 * to the user. It is valid pointer if user should obtain new password on-line
6518 * on dedicated web server. Then input @credentials_delivery.email value is
6519 * user's e-mail address user must provide to dedicated web server together
6520 * with @credentials_delivery.token. The output reallocated token user needs
6521 * to use to authorize on the web server to view his new password. Output
6522 * reallocated @credentials_delivery.new_user_name is user's log-in name that
6523 * ISDS changed up on this call. (No reason why server could change the name
6524 * is known now.)
6525 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6526 * NULL, if you don't care.*/
6527 isds_error isds_reset_password(struct isds_ctx *context,
6528 const struct isds_DbOwnerInfo *box,
6529 const struct isds_DbUserInfo *user,
6530 const _Bool fee_paid, const struct isds_approval *approval,
6531 struct isds_credentials_delivery *credentials_delivery,
6532 char **refnumber) {
6533 isds_error err = IE_SUCCESS;
6534 #if HAVE_LIBCURL
6535 xmlNsPtr isds_ns = NULL;
6536 xmlNodePtr request = NULL, node;
6537 xmlDocPtr response = NULL;
6538 #endif
6541 if (!context) return IE_INVALID_CONTEXT;
6542 zfree(context->long_message);
6544 if (credentials_delivery) {
6545 zfree(credentials_delivery->token);
6546 zfree(credentials_delivery->new_user_name);
6548 if (!box || !user) return IE_INVAL;
6551 #if HAVE_LIBCURL
6552 /* Build NewAccessData request */
6553 request = xmlNewNode(NULL, BAD_CAST "NewAccessData");
6554 if (!request) {
6555 isds_log_message(context,
6556 _("Could build NewAccessData request"));
6557 return IE_ERROR;
6559 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6560 if(!isds_ns) {
6561 isds_log_message(context, _("Could not create ISDS name space"));
6562 xmlFreeNode(request);
6563 return IE_ERROR;
6565 xmlSetNs(request, isds_ns);
6567 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6568 err = insert_DbOwnerInfo(context, box, node);
6569 if (err) goto leave;
6571 INSERT_ELEMENT(node, request, "dbUserInfo");
6572 err = insert_DbUserInfo(context, user, node);
6573 if (err) goto leave;
6575 INSERT_SCALAR_BOOLEAN(request, "dbFeePaid", fee_paid);
6577 err = insert_credentials_delivery(context, credentials_delivery, request);
6578 if (err) goto leave;
6580 err = insert_GExtApproval(context, approval, request);
6581 if (err) goto leave;
6583 /* Send request and check response*/
6584 err = send_destroy_request_check_response(context,
6585 SERVICE_DB_MANIPULATION, BAD_CAST "NewAccessData", &request,
6586 &response, (xmlChar **) refnumber, NULL);
6587 if (err) goto leave;
6590 /* Extract optional token */
6591 err = extract_credentials_delivery(context, credentials_delivery,
6592 response, "NewAccessData");
6594 leave:
6595 xmlFreeDoc(response);
6596 xmlFreeNode(request);
6598 if (!err)
6599 isds_log(ILF_ISDS, ILL_DEBUG,
6600 _("NewAccessData request processed by server "
6601 "successfully.\n"));
6602 #else /* not HAVE_LIBCURL */
6603 err = IE_NOTSUP;
6604 #endif
6606 return err;
6610 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
6611 * code, destroy response and log success.
6612 * @context is ISDS session context.
6613 * @service_name is name of SERVICE_DB_MANIPULATION service
6614 * @box is box identification
6615 * @user identifies user to remove
6616 * @credentials_delivery is NULL if new user's password should be delivered
6617 * off-line to the user. It is valid pointer if user should obtain new
6618 * password on-line on dedicated web server. Then input
6619 * @credentials_delivery.email value is user's e-mail address user must
6620 * provide to dedicated web server together with @credentials_delivery.token.
6621 * The output reallocated token user needs to use to authorize on the web
6622 * server to view his new password. Output reallocated
6623 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6624 * assingned or changed up on this call.
6625 * @approval is optional external approval of box manipulation
6626 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6627 * NULL, if you don't care. */
6628 static isds_error build_send_manipulationboxuser_request_check_drop_response(
6629 struct isds_ctx *context, const xmlChar *service_name,
6630 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6631 struct isds_credentials_delivery *credentials_delivery,
6632 const struct isds_approval *approval, xmlChar **refnumber) {
6633 isds_error err = IE_SUCCESS;
6634 #if HAVE_LIBCURL
6635 xmlNsPtr isds_ns = NULL;
6636 xmlNodePtr request = NULL, node;
6637 xmlDocPtr response = NULL;
6638 #endif
6641 if (!context) return IE_INVALID_CONTEXT;
6642 zfree(context->long_message);
6643 if (credentials_delivery) {
6644 zfree(credentials_delivery->token);
6645 zfree(credentials_delivery->new_user_name);
6647 if (!service_name || service_name[0] == '\0' || !box || !user)
6648 return IE_INVAL;
6651 #if HAVE_LIBCURL
6652 /* Build NewAccessData or similar request */
6653 request = xmlNewNode(NULL, service_name);
6654 if (!request) {
6655 char *service_name_locale = _isds_utf82locale((char *) service_name);
6656 isds_printf_message(context, _("Could not build %s request"),
6657 service_name_locale);
6658 free(service_name_locale);
6659 return IE_ERROR;
6661 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6662 if(!isds_ns) {
6663 isds_log_message(context, _("Could not create ISDS name space"));
6664 xmlFreeNode(request);
6665 return IE_ERROR;
6667 xmlSetNs(request, isds_ns);
6669 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6670 err = insert_DbOwnerInfo(context, box, node);
6671 if (err) goto leave;
6673 INSERT_ELEMENT(node, request, "dbUserInfo");
6674 err = insert_DbUserInfo(context, user, node);
6675 if (err) goto leave;
6677 err = insert_credentials_delivery(context, credentials_delivery, request);
6678 if (err) goto leave;
6680 err = insert_GExtApproval(context, approval, request);
6681 if (err) goto leave;
6684 /* Send request and check response*/
6685 err = send_destroy_request_check_response(context,
6686 SERVICE_DB_MANIPULATION, service_name, &request, &response,
6687 refnumber, NULL);
6689 xmlFreeNode(request);
6690 request = NULL;
6692 /* Pick up credentials_delivery if requested */
6693 err = extract_credentials_delivery(context, credentials_delivery, response,
6694 (char *)service_name);
6696 leave:
6697 xmlFreeDoc(response);
6698 if (request) xmlFreeNode(request);
6700 if (!err) {
6701 char *service_name_locale = _isds_utf82locale((char *) service_name);
6702 isds_log(ILF_ISDS, ILL_DEBUG,
6703 _("%s request processed by server successfully.\n"),
6704 service_name_locale);
6705 free(service_name_locale);
6707 #else /* not HAVE_LIBCURL */
6708 err = IE_NOTSUP;
6709 #endif
6711 return err;
6715 /* Assign new user to given box.
6716 * @context is session context
6717 * @box is box identification
6718 * @user defines new user to add
6719 * @credentials_delivery is NULL if new user's password should be delivered
6720 * off-line to the user. It is valid pointer if user should obtain new
6721 * password on-line on dedicated web server. Then input
6722 * @credentials_delivery.email value is user's e-mail address user must
6723 * provide to dedicated web server together with @credentials_delivery.token.
6724 * The output reallocated token user needs to use to authorize on the web
6725 * server to view his new password. Output reallocated
6726 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6727 * assingned up on this call.
6728 * @approval is optional external approval of box manipulation
6729 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6730 * NULL, if you don't care.*/
6731 isds_error isds_add_user(struct isds_ctx *context,
6732 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6733 struct isds_credentials_delivery *credentials_delivery,
6734 const struct isds_approval *approval, char **refnumber) {
6735 return build_send_manipulationboxuser_request_check_drop_response(context,
6736 BAD_CAST "AddDataBoxUser", box, user, credentials_delivery,
6737 approval, (xmlChar **) refnumber);
6741 /* Remove user assigned to given box.
6742 * @context is session context
6743 * @box is box identification
6744 * @user identifies user to remove
6745 * @approval is optional external approval of box manipulation
6746 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6747 * NULL, if you don't care.*/
6748 isds_error isds_delete_user(struct isds_ctx *context,
6749 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6750 const struct isds_approval *approval, char **refnumber) {
6751 return build_send_manipulationboxuser_request_check_drop_response(context,
6752 BAD_CAST "DeleteDataBoxUser", box, user, NULL, approval,
6753 (xmlChar **) refnumber);
6757 /* Get list of boxes in ZIP archive.
6758 * @context is session context
6759 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
6760 * System recognizes following values currently: ALL (all boxes), UPG
6761 * (effectively OVM boxes), OVM (OVM gross type boxes), OPN (boxes allowing
6762 * receiving commercial messages). This argument is a string because
6763 * specification states new values can appear in the future. Not all list
6764 * types are available to all users.
6765 * @buffer is automatically reallocated memory to store the list of boxes. The
6766 * list is zipped CSV file.
6767 * @buffer_length is size of @buffer data in bytes.
6768 * In case of error @buffer will be freed and @buffer_length will be
6769 * undefined.*/
6770 isds_error isds_get_box_list_archive(struct isds_ctx *context,
6771 const char *list_identifier, void **buffer, size_t *buffer_length) {
6772 isds_error err = IE_SUCCESS;
6773 #if HAVE_LIBCURL
6774 xmlNsPtr isds_ns = NULL;
6775 xmlNodePtr request = NULL, node;
6776 xmlDocPtr response = NULL;
6777 xmlXPathContextPtr xpath_ctx = NULL;
6778 xmlXPathObjectPtr result = NULL;
6779 char *string = NULL;
6780 #endif
6783 if (!context) return IE_INVALID_CONTEXT;
6784 zfree(context->long_message);
6785 if (buffer) zfree(*buffer);
6786 if (!buffer || !buffer_length) return IE_INVAL;
6789 #if HAVE_LIBCURL
6790 /* Check if connection is established
6791 * TODO: This check should be done downstairs. */
6792 if (!context->curl) return IE_CONNECTION_CLOSED;
6795 /* Build AuthenticateMessage request */
6796 request = xmlNewNode(NULL, BAD_CAST "GetDataBoxList");
6797 if (!request) {
6798 isds_log_message(context,
6799 _("Could not build GetDataBoxList request"));
6800 return IE_ERROR;
6802 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6803 if(!isds_ns) {
6804 isds_log_message(context, _("Could not create ISDS name space"));
6805 xmlFreeNode(request);
6806 return IE_ERROR;
6808 xmlSetNs(request, isds_ns);
6809 INSERT_STRING(request, "dblType", list_identifier);
6811 /* Send request to server and process response */
6812 err = send_destroy_request_check_response(context,
6813 SERVICE_DB_SEARCH, BAD_CAST "GetDataBoxList", &request,
6814 &response, NULL, NULL);
6815 if (err) goto leave;
6818 /* Extract Base-64 encoded ZIP file */
6819 xpath_ctx = xmlXPathNewContext(response);
6820 if (!xpath_ctx) {
6821 err = IE_ERROR;
6822 goto leave;
6824 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6825 err = IE_ERROR;
6826 goto leave;
6828 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string);
6830 /* Decode non-empty archive */
6831 if (string && string[0] != '\0') {
6832 *buffer_length = _isds_b64decode(string, buffer);
6833 if (*buffer_length == (size_t) -1) {
6834 isds_printf_message(context,
6835 _("Error while Base64-decoding box list archive"));
6836 err = IE_ERROR;
6837 goto leave;
6842 leave:
6843 free(string);
6844 xmlXPathFreeObject(result);
6845 xmlXPathFreeContext(xpath_ctx);
6846 xmlFreeDoc(response);
6847 xmlFreeNode(request);
6849 if (!err) {
6850 isds_log(ILF_ISDS, ILL_DEBUG, _("GetDataBoxList request "
6851 "processed by server successfully.\n"));
6853 #else /* not HAVE_LIBCURL */
6854 err = IE_NOTSUP;
6855 #endif
6857 return err;
6861 /* Find boxes suiting given criteria.
6862 * @criteria is filter. You should fill in at least some members.
6863 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
6864 * possibly empty. Input NULL or valid old structure.
6865 * @return:
6866 * IE_SUCCESS if search succeeded, @boxes contains useful data
6867 * IE_NOEXIST if no such box exists, @boxes will be NULL
6868 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
6869 * contains still valid data
6870 * other code if something bad happens. @boxes will be NULL. */
6871 isds_error isds_FindDataBox(struct isds_ctx *context,
6872 const struct isds_DbOwnerInfo *criteria,
6873 struct isds_list **boxes) {
6874 isds_error err = IE_SUCCESS;
6875 #if HAVE_LIBCURL
6876 _Bool truncated = 0;
6877 xmlNsPtr isds_ns = NULL;
6878 xmlNodePtr request = NULL;
6879 xmlDocPtr response = NULL;
6880 xmlChar *code = NULL, *message = NULL;
6881 xmlNodePtr db_owner_info;
6882 xmlXPathContextPtr xpath_ctx = NULL;
6883 xmlXPathObjectPtr result = NULL;
6884 xmlChar *string = NULL;
6885 #endif
6888 if (!context) return IE_INVALID_CONTEXT;
6889 zfree(context->long_message);
6890 if (!boxes) return IE_INVAL;
6891 isds_list_free(boxes);
6893 if (!criteria) {
6894 return IE_INVAL;
6897 #if HAVE_LIBCURL
6898 /* Check if connection is established
6899 * TODO: This check should be done downstairs. */
6900 if (!context->curl) return IE_CONNECTION_CLOSED;
6903 /* Build FindDataBox request */
6904 request = xmlNewNode(NULL, BAD_CAST "FindDataBox");
6905 if (!request) {
6906 isds_log_message(context,
6907 _("Could build FindDataBox request"));
6908 return IE_ERROR;
6910 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6911 if(!isds_ns) {
6912 isds_log_message(context, _("Could not create ISDS name space"));
6913 xmlFreeNode(request);
6914 return IE_ERROR;
6916 xmlSetNs(request, isds_ns);
6917 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
6918 if (!db_owner_info) {
6919 isds_log_message(context, _("Could not add dbOwnerInfo child to "
6920 "FindDataBox element"));
6921 xmlFreeNode(request);
6922 return IE_ERROR;
6925 err = insert_DbOwnerInfo(context, criteria, db_owner_info);
6926 if (err) goto leave;
6929 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending FindDataBox request to ISDS\n"));
6931 /* Sent request */
6932 err = isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
6934 /* Destroy request */
6935 xmlFreeNode(request); request = NULL;
6937 if (err) {
6938 isds_log(ILF_ISDS, ILL_DEBUG,
6939 _("Processing ISDS response on FindDataBox "
6940 "request failed\n"));
6941 goto leave;
6944 /* Check for response status */
6945 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
6946 &code, &message, NULL);
6947 if (err) {
6948 isds_log(ILF_ISDS, ILL_DEBUG,
6949 _("ISDS response on FindDataBox request is missing status\n"));
6950 goto leave;
6953 /* Request processed, but nothing found */
6954 if (!xmlStrcmp(code, BAD_CAST "0002") ||
6955 !xmlStrcmp(code, BAD_CAST "5001")) {
6956 char *code_locale = _isds_utf82locale((char*)code);
6957 char *message_locale = _isds_utf82locale((char*)message);
6958 isds_log(ILF_ISDS, ILL_DEBUG,
6959 _("Server did not found any box on FindDataBox request "
6960 "(code=%s, message=%s)\n"), code_locale, message_locale);
6961 isds_log_message(context, message_locale);
6962 free(code_locale);
6963 free(message_locale);
6964 err = IE_NOEXIST;
6965 goto leave;
6968 /* Warning, not a error */
6969 if (!xmlStrcmp(code, BAD_CAST "0003")) {
6970 char *code_locale = _isds_utf82locale((char*)code);
6971 char *message_locale = _isds_utf82locale((char*)message);
6972 isds_log(ILF_ISDS, ILL_DEBUG,
6973 _("Server truncated response on FindDataBox request "
6974 "(code=%s, message=%s)\n"), code_locale, message_locale);
6975 isds_log_message(context, message_locale);
6976 free(code_locale);
6977 free(message_locale);
6978 truncated = 1;
6981 /* Other error */
6982 else if (xmlStrcmp(code, BAD_CAST "0000")) {
6983 char *code_locale = _isds_utf82locale((char*)code);
6984 char *message_locale = _isds_utf82locale((char*)message);
6985 isds_log(ILF_ISDS, ILL_DEBUG,
6986 _("Server refused FindDataBox request "
6987 "(code=%s, message=%s)\n"), code_locale, message_locale);
6988 isds_log_message(context, message_locale);
6989 free(code_locale);
6990 free(message_locale);
6991 err = IE_ISDS;
6992 goto leave;
6995 xpath_ctx = xmlXPathNewContext(response);
6996 if (!xpath_ctx) {
6997 err = IE_ERROR;
6998 goto leave;
7000 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7001 err = IE_ERROR;
7002 goto leave;
7005 /* Extract boxes if they present */
7006 result = xmlXPathEvalExpression(BAD_CAST
7007 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
7008 xpath_ctx);
7009 if (!result) {
7010 err = IE_ERROR;
7011 goto leave;
7013 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7014 struct isds_list *item, *prev_item = NULL;
7015 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7016 item = calloc(1, sizeof(*item));
7017 if (!item) {
7018 err = IE_NOMEM;
7019 goto leave;
7022 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
7023 if (i == 0) *boxes = item;
7024 else prev_item->next = item;
7025 prev_item = item;
7027 xpath_ctx->node = result->nodesetval->nodeTab[i];
7028 err = extract_DbOwnerInfo(context,
7029 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
7030 if (err) goto leave;
7034 leave:
7035 if (err) {
7036 isds_list_free(boxes);
7037 } else {
7038 if (truncated) err = IE_2BIG;
7041 free(string);
7042 xmlFreeNode(request);
7043 xmlXPathFreeObject(result);
7044 xmlXPathFreeContext(xpath_ctx);
7046 free(code);
7047 free(message);
7048 xmlFreeDoc(response);
7050 if (!err)
7051 isds_log(ILF_ISDS, ILL_DEBUG,
7052 _("FindDataBox request processed by server successfully.\n"));
7053 #else /* not HAVE_LIBCURL */
7054 err = IE_NOTSUP;
7055 #endif
7057 return err;
7061 /* Get status of a box.
7062 * @context is ISDS session context.
7063 * @box_id is UTF-8 encoded box identifier as zero terminated string
7064 * @box_status is return value of box status.
7065 * @return:
7066 * IE_SUCCESS if box has been found and its status retrieved
7067 * IE_NOEXIST if box is not known to ISDS server
7068 * or other appropriate error.
7069 * You can use isds_DbState to enumerate box status. However out of enum
7070 * range value can be returned too. This is feature because ISDS
7071 * specification leaves the set of values open.
7072 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
7073 * the box has been deleted, but ISDS still lists its former existence. */
7074 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
7075 long int *box_status) {
7076 isds_error err = IE_SUCCESS;
7077 #if HAVE_LIBCURL
7078 xmlNsPtr isds_ns = NULL;
7079 xmlNodePtr request = NULL, db_id;
7080 xmlDocPtr response = NULL;
7081 xmlChar *code = NULL, *message = NULL;
7082 xmlXPathContextPtr xpath_ctx = NULL;
7083 xmlXPathObjectPtr result = NULL;
7084 xmlChar *string = NULL;
7085 #endif
7087 if (!context) return IE_INVALID_CONTEXT;
7088 zfree(context->long_message);
7089 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
7091 #if HAVE_LIBCURL
7092 /* Check if connection is established
7093 * TODO: This check should be done downstairs. */
7094 if (!context->curl) return IE_CONNECTION_CLOSED;
7097 /* Build CheckDataBox request */
7098 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
7099 if (!request) {
7100 isds_log_message(context,
7101 _("Could build CheckDataBox request"));
7102 return IE_ERROR;
7104 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7105 if(!isds_ns) {
7106 isds_log_message(context, _("Could not create ISDS name space"));
7107 xmlFreeNode(request);
7108 return IE_ERROR;
7110 xmlSetNs(request, isds_ns);
7111 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
7112 if (!db_id) {
7113 isds_log_message(context, _("Could not add dbID child to "
7114 "CheckDataBox element"));
7115 xmlFreeNode(request);
7116 return IE_ERROR;
7120 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CheckDataBox request to ISDS\n"));
7122 /* Sent request */
7123 err = isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
7125 /* Destroy request */
7126 xmlFreeNode(request);
7128 if (err) {
7129 isds_log(ILF_ISDS, ILL_DEBUG,
7130 _("Processing ISDS response on CheckDataBox "
7131 "request failed\n"));
7132 goto leave;
7135 /* Check for response status */
7136 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
7137 &code, &message, NULL);
7138 if (err) {
7139 isds_log(ILF_ISDS, ILL_DEBUG,
7140 _("ISDS response on CheckDataBox request is missing status\n"));
7141 goto leave;
7144 /* Request processed, but nothing found */
7145 if (!xmlStrcmp(code, BAD_CAST "5001")) {
7146 char *box_id_locale = _isds_utf82locale((char*)box_id);
7147 char *code_locale = _isds_utf82locale((char*)code);
7148 char *message_locale = _isds_utf82locale((char*)message);
7149 isds_log(ILF_ISDS, ILL_DEBUG,
7150 _("Server did not found box %s on CheckDataBox request "
7151 "(code=%s, message=%s)\n"),
7152 box_id_locale, code_locale, message_locale);
7153 isds_log_message(context, message_locale);
7154 free(box_id_locale);
7155 free(code_locale);
7156 free(message_locale);
7157 err = IE_NOEXIST;
7158 goto leave;
7161 /* Other error */
7162 else if (xmlStrcmp(code, BAD_CAST "0000")) {
7163 char *code_locale = _isds_utf82locale((char*)code);
7164 char *message_locale = _isds_utf82locale((char*)message);
7165 isds_log(ILF_ISDS, ILL_DEBUG,
7166 _("Server refused CheckDataBox request "
7167 "(code=%s, message=%s)\n"), code_locale, message_locale);
7168 isds_log_message(context, message_locale);
7169 free(code_locale);
7170 free(message_locale);
7171 err = IE_ISDS;
7172 goto leave;
7175 /* Extract data */
7176 xpath_ctx = xmlXPathNewContext(response);
7177 if (!xpath_ctx) {
7178 err = IE_ERROR;
7179 goto leave;
7181 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7182 err = IE_ERROR;
7183 goto leave;
7185 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
7186 xpath_ctx);
7187 if (!result) {
7188 err = IE_ERROR;
7189 goto leave;
7191 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7192 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
7193 err = IE_ISDS;
7194 goto leave;
7196 if (result->nodesetval->nodeNr > 1) {
7197 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
7198 err = IE_ISDS;
7199 goto leave;
7201 xpath_ctx->node = result->nodesetval->nodeTab[0];
7202 xmlXPathFreeObject(result); result = NULL;
7204 EXTRACT_LONGINT("isds:dbState", box_status, 1);
7207 leave:
7208 free(string);
7209 xmlXPathFreeObject(result);
7210 xmlXPathFreeContext(xpath_ctx);
7212 free(code);
7213 free(message);
7214 xmlFreeDoc(response);
7216 if (!err)
7217 isds_log(ILF_ISDS, ILL_DEBUG,
7218 _("CheckDataBox request processed by server successfully.\n"));
7219 #else /* not HAVE_LIBCURL */
7220 err = IE_NOTSUP;
7221 #endif
7223 return err;
7227 /* Get list of permissions to send commercial messages.
7228 * @context is ISDS session context.
7229 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
7230 * @permissions is a reallocated list of permissions (struct
7231 * isds_commercial_permission*) to send commercial messages from @box_id. The
7232 * order of permissions is significant as the server applies the permissions
7233 * and associated pre-paid credits in the order. Empty list means no
7234 * permission.
7235 * @return:
7236 * IE_SUCCESS if the list has been obtained correctly,
7237 * or other appropriate error. */
7238 isds_error isds_get_commercial_permissions(struct isds_ctx *context,
7239 const char *box_id, struct isds_list **permissions) {
7240 isds_error err = IE_SUCCESS;
7241 #if HAVE_LIBCURL
7242 xmlDocPtr response = NULL;
7243 xmlXPathContextPtr xpath_ctx = NULL;
7244 xmlXPathObjectPtr result = NULL;
7245 #endif
7247 if (!context) return IE_INVALID_CONTEXT;
7248 zfree(context->long_message);
7249 if (NULL == permissions) return IE_INVAL;
7250 isds_list_free(permissions);
7251 if (NULL == box_id) return IE_INVAL;
7253 #if HAVE_LIBCURL
7254 /* Check if connection is established */
7255 if (!context->curl) return IE_CONNECTION_CLOSED;
7257 /* Do request and check for success */
7258 err = build_send_dbid_request_check_response(context,
7259 SERVICE_DB_SEARCH, BAD_CAST "PDZInfo", BAD_CAST "PDZSender",
7260 BAD_CAST box_id, NULL, &response, NULL);
7261 if (!err) {
7262 isds_log(ILF_ISDS, ILL_DEBUG,
7263 _("PDZInfo request processed by server successfully.\n"));
7266 /* Extract data */
7267 /* Prepare structure */
7268 xpath_ctx = xmlXPathNewContext(response);
7269 if (!xpath_ctx) {
7270 err = IE_ERROR;
7271 goto leave;
7273 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7274 err = IE_ERROR;
7275 goto leave;
7278 /* Set context node */
7279 result = xmlXPathEvalExpression(BAD_CAST
7280 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
7281 xpath_ctx);
7282 if (!result) {
7283 err = IE_ERROR;
7284 goto leave;
7286 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7287 /* Iterate over all permission records */
7288 for (long unsigned int i = 0; i < result->nodesetval->nodeNr; i++) {
7289 struct isds_list *item, *prev_item = NULL;
7291 /* Prepare structure */
7292 item = calloc(1, sizeof(*item));
7293 if (!item) {
7294 err = IE_NOMEM;
7295 goto leave;
7297 item->destructor = (void(*)(void**))isds_commercial_permission_free;
7298 if (i == 0) *permissions = item;
7299 else prev_item->next = item;
7300 prev_item = item;
7302 /* Extract it */
7303 xpath_ctx->node = result->nodesetval->nodeTab[i];
7304 err = extract_DbPDZRecord(context,
7305 (struct isds_commercial_permission **) (&item->data),
7306 xpath_ctx);
7307 if (err) goto leave;
7311 leave:
7312 if (err) {
7313 isds_list_free(permissions);
7316 xmlXPathFreeObject(result);
7317 xmlXPathFreeContext(xpath_ctx);
7318 xmlFreeDoc(response);
7320 #else /* not HAVE_LIBCURL */
7321 err = IE_NOTSUP;
7322 #endif
7324 return err;
7328 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
7329 * code, destroy response and log success.
7330 * @context is ISDS session context.
7331 * @service_name is name of SERVICE_DB_MANIPULATION service
7332 * @box_id is UTF-8 encoded box identifier as zero terminated string
7333 * @approval is optional external approval of box manipulation
7334 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7335 * NULL, if you don't care. */
7336 static isds_error build_send_manipulationdbid_request_check_drop_response(
7337 struct isds_ctx *context, const xmlChar *service_name,
7338 const xmlChar *box_id, const struct isds_approval *approval,
7339 xmlChar **refnumber) {
7340 isds_error err = IE_SUCCESS;
7341 #if HAVE_LIBCURL
7342 xmlDocPtr response = NULL;
7343 #endif
7345 if (!context) return IE_INVALID_CONTEXT;
7346 zfree(context->long_message);
7347 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
7349 #if HAVE_LIBCURL
7350 /* Check if connection is established */
7351 if (!context->curl) return IE_CONNECTION_CLOSED;
7353 /* Do request and check for success */
7354 err = build_send_dbid_request_check_response(context,
7355 SERVICE_DB_MANIPULATION, service_name, NULL, box_id, approval,
7356 &response, refnumber);
7357 xmlFreeDoc(response);
7359 if (!err) {
7360 char *service_name_locale = _isds_utf82locale((char *) service_name);
7361 isds_log(ILF_ISDS, ILL_DEBUG,
7362 _("%s request processed by server successfully.\n"),
7363 service_name_locale);
7364 free(service_name_locale);
7366 #else /* not HAVE_LIBCURL */
7367 err = IE_NOTSUP;
7368 #endif
7370 return err;
7374 /* Switch box into state where box can receive commercial messages (off by
7375 * default)
7376 * @context is ISDS session context.
7377 * @box_id is UTF-8 encoded box identifier as zero terminated string
7378 * @allow is true for enable, false for disable commercial messages income
7379 * @approval is optional external approval of box manipulation
7380 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7381 * NULL, if you don't care. */
7382 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
7383 const char *box_id, const _Bool allow,
7384 const struct isds_approval *approval, char **refnumber) {
7385 return build_send_manipulationdbid_request_check_drop_response(context,
7386 (allow) ? BAD_CAST "SetOpenAddressing" :
7387 BAD_CAST "ClearOpenAddressing",
7388 BAD_CAST box_id, approval, (xmlChar **) refnumber);
7392 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
7393 * message acceptance). This is just a box permission. Sender must apply
7394 * such role by sending each message.
7395 * @context is ISDS session context.
7396 * @box_id is UTF-8 encoded box identifier as zero terminated string
7397 * @allow is true for enable, false for disable OVM role permission
7398 * @approval is optional external approval of box manipulation
7399 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7400 * NULL, if you don't care. */
7401 isds_error isds_switch_effective_ovm(struct isds_ctx *context,
7402 const char *box_id, const _Bool allow,
7403 const struct isds_approval *approval, char **refnumber) {
7404 return build_send_manipulationdbid_request_check_drop_response(context,
7405 (allow) ? BAD_CAST "SetEffectiveOVM" :
7406 BAD_CAST "ClearEffectiveOVM",
7407 BAD_CAST box_id, approval, (xmlChar **) refnumber);
7411 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
7412 * code, destroy response and log success.
7413 * @context is ISDS session context.
7414 * @service_name is name of SERVICE_DB_MANIPULATION service
7415 * @owner is structure describing box
7416 * @approval is optional external approval of box manipulation
7417 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7418 * NULL, if you don't care. */
7419 static isds_error build_send_manipulationdbowner_request_check_drop_response(
7420 struct isds_ctx *context, const xmlChar *service_name,
7421 const struct isds_DbOwnerInfo *owner,
7422 const struct isds_approval *approval, xmlChar **refnumber) {
7423 isds_error err = IE_SUCCESS;
7424 #if HAVE_LIBCURL
7425 char *service_name_locale = NULL;
7426 xmlNodePtr request = NULL, db_owner_info;
7427 xmlNsPtr isds_ns = NULL;
7428 #endif
7431 if (!context) return IE_INVALID_CONTEXT;
7432 zfree(context->long_message);
7433 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
7435 #if HAVE_LIBCURL
7436 service_name_locale = _isds_utf82locale((char*)service_name);
7437 if (!service_name_locale) {
7438 err = IE_NOMEM;
7439 goto leave;
7442 /* Build request */
7443 request = xmlNewNode(NULL, service_name);
7444 if (!request) {
7445 isds_printf_message(context,
7446 _("Could not build %s request"), service_name_locale);
7447 err = IE_ERROR;
7448 goto leave;
7450 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7451 if(!isds_ns) {
7452 isds_log_message(context, _("Could not create ISDS name space"));
7453 err = IE_ERROR;
7454 goto leave;
7456 xmlSetNs(request, isds_ns);
7459 /* Add XSD:tOwnerInfoInput child*/
7460 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
7461 err = insert_DbOwnerInfo(context, owner, db_owner_info);
7462 if (err) goto leave;
7464 /* Add XSD:gExtApproval*/
7465 err = insert_GExtApproval(context, approval, request);
7466 if (err) goto leave;
7468 /* Send it to server and process response */
7469 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
7470 service_name, &request, refnumber);
7472 leave:
7473 xmlFreeNode(request);
7474 free(service_name_locale);
7475 #else /* not HAVE_LIBCURL */
7476 err = IE_NOTSUP;
7477 #endif
7479 return err;
7483 /* Switch box accessibility state on request of box owner.
7484 * Despite the name, owner must do the request off-line. This function is
7485 * designed for such off-line meeting points (e.g. Czech POINT).
7486 * @context is ISDS session context.
7487 * @box identifies box to switch accessibility state.
7488 * @allow is true for making accessible, false to disallow access.
7489 * @approval is optional external approval of box manipulation
7490 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7491 * NULL, if you don't care. */
7492 isds_error isds_switch_box_accessibility_on_owner_request(
7493 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
7494 const _Bool allow, const struct isds_approval *approval,
7495 char **refnumber) {
7496 return build_send_manipulationdbowner_request_check_drop_response(context,
7497 (allow) ? BAD_CAST "EnableOwnDataBox" :
7498 BAD_CAST "DisableOwnDataBox",
7499 box, approval, (xmlChar **) refnumber);
7503 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
7504 * date.
7505 * @context is ISDS session context.
7506 * @box identifies box to switch accessibility state.
7507 * @since is date since accessibility has been denied. This can be past too.
7508 * Only tm_year, tm_mon and tm_mday carry sane value.
7509 * @approval is optional external approval of box manipulation
7510 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7511 * NULL, if you don't care. */
7512 isds_error isds_disable_box_accessibility_externaly(
7513 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
7514 const struct tm *since, const struct isds_approval *approval,
7515 char **refnumber) {
7516 isds_error err = IE_SUCCESS;
7517 #if HAVE_LIBCURL
7518 char *service_name_locale = NULL;
7519 xmlNodePtr request = NULL, node;
7520 xmlNsPtr isds_ns = NULL;
7521 xmlChar *string = NULL;
7522 #endif
7525 if (!context) return IE_INVALID_CONTEXT;
7526 zfree(context->long_message);
7527 if (!box || !since) return IE_INVAL;
7529 #if HAVE_LIBCURL
7530 /* Build request */
7531 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
7532 if (!request) {
7533 isds_printf_message(context,
7534 _("Could not build %s request"), "DisableDataBoxExternally");
7535 err = IE_ERROR;
7536 goto leave;
7538 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7539 if(!isds_ns) {
7540 isds_log_message(context, _("Could not create ISDS name space"));
7541 err = IE_ERROR;
7542 goto leave;
7544 xmlSetNs(request, isds_ns);
7547 /* Add @box identification */
7548 INSERT_ELEMENT(node, request, "dbOwnerInfo");
7549 err = insert_DbOwnerInfo(context, box, node);
7550 if (err) goto leave;
7552 /* Add @since date */
7553 err = tm2datestring(since, &string);
7554 if(err) {
7555 isds_log_message(context,
7556 _("Could not convert `since' argument to ISO date string"));
7557 goto leave;
7559 INSERT_STRING(request, "dbOwnerDisableDate", string);
7560 zfree(string);
7562 /* Add @approval */
7563 err = insert_GExtApproval(context, approval, request);
7564 if (err) goto leave;
7566 /* Send it to server and process response */
7567 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
7568 BAD_CAST "DisableDataBoxExternally", &request,
7569 (xmlChar **) refnumber);
7571 leave:
7572 free(string);
7573 xmlFreeNode(request);
7574 free(service_name_locale);
7575 #else /* not HAVE_LIBCURL */
7576 err = IE_NOTSUP;
7577 #endif
7579 return err;
7583 #if HAVE_LIBCURL
7584 /* Insert struct isds_message data (envelope (recipient data optional) and
7585 * documents into XML tree
7586 * @context is session context
7587 * @outgoing_message is libisds structure with message data
7588 * @create_message is XML CreateMessage or CreateMultipleMessage element
7589 * @process_recipient true for recipient data serialization, false for no
7590 * serialization */
7591 static isds_error insert_envelope_files(struct isds_ctx *context,
7592 const struct isds_message *outgoing_message, xmlNodePtr create_message,
7593 const _Bool process_recipient) {
7595 isds_error err = IE_SUCCESS;
7596 xmlNodePtr envelope, dm_files, node;
7597 xmlChar *string = NULL;
7599 if (!context) return IE_INVALID_CONTEXT;
7600 if (!outgoing_message || !create_message) return IE_INVAL;
7603 /* Build envelope */
7604 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
7605 if (!envelope) {
7606 isds_printf_message(context, _("Could not add dmEnvelope child to "
7607 "%s element"), create_message->name);
7608 return IE_ERROR;
7611 if (!outgoing_message->envelope) {
7612 isds_log_message(context, _("Outgoing message is missing envelope"));
7613 err = IE_INVAL;
7614 goto leave;
7617 /* Insert optional message type */
7618 err = insert_message_type(context, outgoing_message->envelope->dmType,
7619 envelope);
7620 if (err) goto leave;
7622 INSERT_STRING(envelope, "dmSenderOrgUnit",
7623 outgoing_message->envelope->dmSenderOrgUnit);
7624 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
7625 outgoing_message->envelope->dmSenderOrgUnitNum, string);
7627 if (process_recipient) {
7628 if (!outgoing_message->envelope->dbIDRecipient) {
7629 isds_log_message(context,
7630 _("Outgoing message is missing recipient box identifier"));
7631 err = IE_INVAL;
7632 goto leave;
7634 INSERT_STRING(envelope, "dbIDRecipient",
7635 outgoing_message->envelope->dbIDRecipient);
7637 INSERT_STRING(envelope, "dmRecipientOrgUnit",
7638 outgoing_message->envelope->dmRecipientOrgUnit);
7639 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
7640 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
7641 INSERT_STRING(envelope, "dmToHands",
7642 outgoing_message->envelope->dmToHands);
7645 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
7646 "dmAnnotation");
7647 INSERT_STRING(envelope, "dmAnnotation",
7648 outgoing_message->envelope->dmAnnotation);
7650 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
7651 0, 50, "dmRecipientRefNumber");
7652 INSERT_STRING(envelope, "dmRecipientRefNumber",
7653 outgoing_message->envelope->dmRecipientRefNumber);
7655 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
7656 0, 50, "dmSenderRefNumber");
7657 INSERT_STRING(envelope, "dmSenderRefNumber",
7658 outgoing_message->envelope->dmSenderRefNumber);
7660 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
7661 0, 50, "dmRecipientIdent");
7662 INSERT_STRING(envelope, "dmRecipientIdent",
7663 outgoing_message->envelope->dmRecipientIdent);
7665 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
7666 0, 50, "dmSenderIdent");
7667 INSERT_STRING(envelope, "dmSenderIdent",
7668 outgoing_message->envelope->dmSenderIdent);
7670 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
7671 outgoing_message->envelope->dmLegalTitleLaw, string);
7672 INSERT_LONGINT(envelope, "dmLegalTitleYear",
7673 outgoing_message->envelope->dmLegalTitleYear, string);
7674 INSERT_STRING(envelope, "dmLegalTitleSect",
7675 outgoing_message->envelope->dmLegalTitleSect);
7676 INSERT_STRING(envelope, "dmLegalTitlePar",
7677 outgoing_message->envelope->dmLegalTitlePar);
7678 INSERT_STRING(envelope, "dmLegalTitlePoint",
7679 outgoing_message->envelope->dmLegalTitlePoint);
7681 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
7682 outgoing_message->envelope->dmPersonalDelivery);
7683 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
7684 outgoing_message->envelope->dmAllowSubstDelivery);
7686 /* ???: Should we require value for dbEffectiveOVM sender?
7687 * ISDS has default as true */
7688 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
7689 INSERT_BOOLEAN(envelope, "dmOVM",
7690 outgoing_message->envelope->dmPublishOwnID);
7693 /* Append dmFiles */
7694 if (!outgoing_message->documents) {
7695 isds_log_message(context,
7696 _("Outgoing message is missing list of documents"));
7697 err = IE_INVAL;
7698 goto leave;
7700 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
7701 if (!dm_files) {
7702 isds_printf_message(context, _("Could not add dmFiles child to "
7703 "%s element"), create_message->name);
7704 err = IE_ERROR;
7705 goto leave;
7708 /* Check for document hierarchy */
7709 err = _isds_check_documents_hierarchy(context, outgoing_message->documents);
7710 if (err) goto leave;
7712 /* Process each document */
7713 for (struct isds_list *item =
7714 (struct isds_list *) outgoing_message->documents;
7715 item; item = item->next) {
7716 if (!item->data) {
7717 isds_log_message(context,
7718 _("List of documents contains empty item"));
7719 err = IE_INVAL;
7720 goto leave;
7722 /* FIXME: Check for dmFileMetaType and for document references.
7723 * Only first document can be of MAIN type */
7724 err = insert_document(context, (struct isds_document*) item->data,
7725 dm_files);
7727 if (err) goto leave;
7730 leave:
7731 free(string);
7732 return err;
7734 #endif /* HAVE_LIBCURL */
7737 /* Send a message via ISDS to a recipient
7738 * @context is session context
7739 * @outgoing_message is message to send; Some members are mandatory (like
7740 * dbIDRecipient), some are optional and some are irrelevant (especially data
7741 * about sender). Included pointer to isds_list documents must contain at
7742 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
7743 * members will be filled with valid data from ISDS. Exact list of write
7744 * members is subject to change. Currently dmID is changed.
7745 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
7746 isds_error isds_send_message(struct isds_ctx *context,
7747 struct isds_message *outgoing_message) {
7749 isds_error err = IE_SUCCESS;
7750 #if HAVE_LIBCURL
7751 xmlNsPtr isds_ns = NULL;
7752 xmlNodePtr request = NULL;
7753 xmlDocPtr response = NULL;
7754 xmlChar *code = NULL, *message = NULL;
7755 xmlXPathContextPtr xpath_ctx = NULL;
7756 xmlXPathObjectPtr result = NULL;
7757 /*_Bool message_is_complete = 0;*/
7758 #endif
7760 if (!context) return IE_INVALID_CONTEXT;
7761 zfree(context->long_message);
7762 if (!outgoing_message) return IE_INVAL;
7764 #if HAVE_LIBCURL
7765 /* Check if connection is established
7766 * TODO: This check should be done downstairs. */
7767 if (!context->curl) return IE_CONNECTION_CLOSED;
7770 /* Build CreateMessage request */
7771 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
7772 if (!request) {
7773 isds_log_message(context,
7774 _("Could not build CreateMessage request"));
7775 return IE_ERROR;
7777 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7778 if(!isds_ns) {
7779 isds_log_message(context, _("Could not create ISDS name space"));
7780 xmlFreeNode(request);
7781 return IE_ERROR;
7783 xmlSetNs(request, isds_ns);
7785 /* Append envelope and files */
7786 err = insert_envelope_files(context, outgoing_message, request, 1);
7787 if (err) goto leave;
7790 /* Signal we can serialize message since now */
7791 /*message_is_complete = 1;*/
7794 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
7796 /* Sent request */
7797 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
7799 /* Don't' destroy request, we want to provide it to application later */
7801 if (err) {
7802 isds_log(ILF_ISDS, ILL_DEBUG,
7803 _("Processing ISDS response on CreateMessage "
7804 "request failed\n"));
7805 goto leave;
7808 /* Check for response status */
7809 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
7810 &code, &message, NULL);
7811 if (err) {
7812 isds_log(ILF_ISDS, ILL_DEBUG,
7813 _("ISDS response on CreateMessage request "
7814 "is missing status\n"));
7815 goto leave;
7818 /* Request processed, but refused by server or server failed */
7819 if (xmlStrcmp(code, BAD_CAST "0000")) {
7820 char *box_id_locale =
7821 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
7822 char *code_locale = _isds_utf82locale((char*)code);
7823 char *message_locale = _isds_utf82locale((char*)message);
7824 isds_log(ILF_ISDS, ILL_DEBUG,
7825 _("Server did not accept message for %s on CreateMessage "
7826 "request (code=%s, message=%s)\n"),
7827 box_id_locale, code_locale, message_locale);
7828 isds_log_message(context, message_locale);
7829 free(box_id_locale);
7830 free(code_locale);
7831 free(message_locale);
7832 err = IE_ISDS;
7833 goto leave;
7837 /* Extract data */
7838 xpath_ctx = xmlXPathNewContext(response);
7839 if (!xpath_ctx) {
7840 err = IE_ERROR;
7841 goto leave;
7843 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7844 err = IE_ERROR;
7845 goto leave;
7847 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
7848 xpath_ctx);
7849 if (!result) {
7850 err = IE_ERROR;
7851 goto leave;
7853 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7854 isds_log_message(context, _("Missing CreateMessageResponse element"));
7855 err = IE_ISDS;
7856 goto leave;
7858 if (result->nodesetval->nodeNr > 1) {
7859 isds_log_message(context, _("Multiple CreateMessageResponse element"));
7860 err = IE_ISDS;
7861 goto leave;
7863 xpath_ctx->node = result->nodesetval->nodeTab[0];
7864 xmlXPathFreeObject(result); result = NULL;
7866 if (outgoing_message->envelope->dmID) {
7867 free(outgoing_message->envelope->dmID);
7868 outgoing_message->envelope->dmID = NULL;
7870 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
7871 if (!outgoing_message->envelope->dmID) {
7872 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
7873 "but did not return assigned message ID\n"));
7876 leave:
7877 /* TODO: Serialize message into structure member raw */
7878 /* XXX: Each web service transport message in different format.
7879 * Therefore it's not possible to save them directly.
7880 * To save them, one must figure out common format.
7881 * We can leave it on application, or we can implement the ESS format. */
7882 /*if (message_is_complete) {
7883 if (outgoing_message->envelope->dmID) {
7885 /* Add assigned message ID as first child*/
7886 /*xmlNodePtr dmid_text = xmlNewText(
7887 (xmlChar *) outgoing_message->envelope->dmID);
7888 if (!dmid_text) goto serialization_failed;
7890 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
7891 BAD_CAST "dmID");
7892 if (!dmid_element) {
7893 xmlFreeNode(dmid_text);
7894 goto serialization_failed;
7897 xmlNodePtr dmid_element_with_text =
7898 xmlAddChild(dmid_element, dmid_text);
7899 if (!dmid_element_with_text) {
7900 xmlFreeNode(dmid_element);
7901 xmlFreeNode(dmid_text);
7902 goto serialization_failed;
7905 node = xmlAddPrevSibling(envelope->childern,
7906 dmid_element_with_text);
7907 if (!node) {
7908 xmlFreeNodeList(dmid_element_with_text);
7909 goto serialization_failed;
7913 /* Serialize message with ID into raw */
7914 /*buffer = serialize_element(envelope)*/
7915 /* }
7917 serialization_failed:
7921 /* Clean up */
7922 xmlXPathFreeObject(result);
7923 xmlXPathFreeContext(xpath_ctx);
7925 free(code);
7926 free(message);
7927 xmlFreeDoc(response);
7928 xmlFreeNode(request);
7930 if (!err)
7931 isds_log(ILF_ISDS, ILL_DEBUG,
7932 _("CreateMessage request processed by server "
7933 "successfully.\n"));
7934 #else /* not HAVE_LIBCURL */
7935 err = IE_NOTSUP;
7936 #endif
7938 return err;
7942 /* Send a message via ISDS to a multiple recipients
7943 * @context is session context
7944 * @outgoing_message is message to send; Some members are mandatory,
7945 * some are optional and some are irrelevant (especially data
7946 * about sender). Data about recipient will be substituted by ISDS from
7947 * @copies. Included pointer to isds_list documents must
7948 * contain at least one document of FILEMETATYPE_MAIN.
7949 * @copies is list of isds_message_copy structures addressing all desired
7950 * recipients. This is read-write structure, some members will be filled with
7951 * valid data from ISDS (message IDs, error codes, error descriptions).
7952 * @return
7953 * ISDS_SUCCESS if all messages have been sent
7954 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
7955 * succeeded messages can be identified by copies->data->error),
7956 * or other error code if something other goes wrong. */
7957 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
7958 const struct isds_message *outgoing_message,
7959 struct isds_list *copies) {
7961 isds_error err = IE_SUCCESS;
7962 #if HAVE_LIBCURL
7963 isds_error append_err;
7964 xmlNsPtr isds_ns = NULL;
7965 xmlNodePtr request = NULL, recipients, recipient, node;
7966 struct isds_list *item;
7967 struct isds_message_copy *copy;
7968 xmlDocPtr response = NULL;
7969 xmlChar *code = NULL, *message = NULL;
7970 xmlXPathContextPtr xpath_ctx = NULL;
7971 xmlXPathObjectPtr result = NULL;
7972 xmlChar *string = NULL;
7973 int i;
7974 #endif
7976 if (!context) return IE_INVALID_CONTEXT;
7977 zfree(context->long_message);
7978 if (!outgoing_message || !copies) return IE_INVAL;
7980 #if HAVE_LIBCURL
7981 /* Check if connection is established
7982 * TODO: This check should be done downstairs. */
7983 if (!context->curl) return IE_CONNECTION_CLOSED;
7986 /* Build CreateMultipleMessage request */
7987 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
7988 if (!request) {
7989 isds_log_message(context,
7990 _("Could not build CreateMultipleMessage request"));
7991 return IE_ERROR;
7993 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7994 if(!isds_ns) {
7995 isds_log_message(context, _("Could not create ISDS name space"));
7996 xmlFreeNode(request);
7997 return IE_ERROR;
7999 xmlSetNs(request, isds_ns);
8002 /* Build recipients */
8003 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
8004 if (!recipients) {
8005 isds_log_message(context, _("Could not add dmRecipients child to "
8006 "CreateMultipleMessage element"));
8007 xmlFreeNode(request);
8008 return IE_ERROR;
8011 /* Insert each recipient */
8012 for (item = copies; item; item = item->next) {
8013 copy = (struct isds_message_copy *) item->data;
8014 if (!copy) {
8015 isds_log_message(context,
8016 _("`copies' list item contains empty data"));
8017 err = IE_INVAL;
8018 goto leave;
8021 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
8022 if (!recipient) {
8023 isds_log_message(context, _("Could not add dmRecipient child to "
8024 "dmRecipients element"));
8025 err = IE_ERROR;
8026 goto leave;
8029 if (!copy->dbIDRecipient) {
8030 isds_log_message(context,
8031 _("Message copy is missing recipient box identifier"));
8032 err = IE_INVAL;
8033 goto leave;
8035 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
8036 INSERT_STRING(recipient, "dmRecipientOrgUnit",
8037 copy->dmRecipientOrgUnit);
8038 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
8039 copy->dmRecipientOrgUnitNum, string);
8040 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
8043 /* Append envelope and files */
8044 err = insert_envelope_files(context, outgoing_message, request, 0);
8045 if (err) goto leave;
8048 isds_log(ILF_ISDS, ILL_DEBUG,
8049 _("Sending CreateMultipleMessage request to ISDS\n"));
8051 /* Sent request */
8052 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
8053 if (err) {
8054 isds_log(ILF_ISDS, ILL_DEBUG,
8055 _("Processing ISDS response on CreateMultipleMessage "
8056 "request failed\n"));
8057 goto leave;
8060 /* Check for response status */
8061 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
8062 &code, &message, NULL);
8063 if (err) {
8064 isds_log(ILF_ISDS, ILL_DEBUG,
8065 _("ISDS response on CreateMultipleMessage request "
8066 "is missing status\n"));
8067 goto leave;
8070 /* Request processed, but some copies failed */
8071 if (!xmlStrcmp(code, BAD_CAST "0004")) {
8072 char *box_id_locale =
8073 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
8074 char *code_locale = _isds_utf82locale((char*)code);
8075 char *message_locale = _isds_utf82locale((char*)message);
8076 isds_log(ILF_ISDS, ILL_DEBUG,
8077 _("Server did accept message for multiple recipients "
8078 "on CreateMultipleMessage request but delivery to "
8079 "some of them failed (code=%s, message=%s)\n"),
8080 box_id_locale, code_locale, message_locale);
8081 isds_log_message(context, message_locale);
8082 free(box_id_locale);
8083 free(code_locale);
8084 free(message_locale);
8085 err = IE_PARTIAL_SUCCESS;
8088 /* Request refused by server as whole */
8089 else if (xmlStrcmp(code, BAD_CAST "0000")) {
8090 char *box_id_locale =
8091 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
8092 char *code_locale = _isds_utf82locale((char*)code);
8093 char *message_locale = _isds_utf82locale((char*)message);
8094 isds_log(ILF_ISDS, ILL_DEBUG,
8095 _("Server did not accept message for multiple recipients "
8096 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
8097 box_id_locale, code_locale, message_locale);
8098 isds_log_message(context, message_locale);
8099 free(box_id_locale);
8100 free(code_locale);
8101 free(message_locale);
8102 err = IE_ISDS;
8103 goto leave;
8107 /* Extract data */
8108 xpath_ctx = xmlXPathNewContext(response);
8109 if (!xpath_ctx) {
8110 err = IE_ERROR;
8111 goto leave;
8113 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8114 err = IE_ERROR;
8115 goto leave;
8117 result = xmlXPathEvalExpression(
8118 BAD_CAST "/isds:CreateMultipleMessageResponse"
8119 "/isds:dmMultipleStatus/isds:dmSingleStatus",
8120 xpath_ctx);
8121 if (!result) {
8122 err = IE_ERROR;
8123 goto leave;
8125 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8126 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
8127 err = IE_ISDS;
8128 goto leave;
8131 /* Extract message ID and delivery status for each copy */
8132 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
8133 item = item->next, i++) {
8134 copy = (struct isds_message_copy *) item->data;
8135 xpath_ctx->node = result->nodesetval->nodeTab[i];
8137 append_err = append_TMStatus(context, copy, xpath_ctx);
8138 if (append_err) {
8139 err = append_err;
8140 goto leave;
8143 if (item || i < result->nodesetval->nodeNr) {
8144 isds_printf_message(context, _("ISDS returned unexpected number of "
8145 "message copy delivery states: %d"),
8146 result->nodesetval->nodeNr);
8147 err = IE_ISDS;
8148 goto leave;
8152 leave:
8153 /* Clean up */
8154 free(string);
8155 xmlXPathFreeObject(result);
8156 xmlXPathFreeContext(xpath_ctx);
8158 free(code);
8159 free(message);
8160 xmlFreeDoc(response);
8161 xmlFreeNode(request);
8163 if (!err)
8164 isds_log(ILF_ISDS, ILL_DEBUG,
8165 _("CreateMultipleMessageResponse request processed by server "
8166 "successfully.\n"));
8167 #else /* not HAVE_LIBCURL */
8168 err = IE_NOTSUP;
8169 #endif
8171 return err;
8175 /* Get list of messages. This is common core for getting sent or received
8176 * messages.
8177 * Any criterion argument can be NULL, if you don't care about it.
8178 * @context is session context. Must not be NULL.
8179 * @outgoing_direction is true if you want list of outgoing messages,
8180 * it's false if you want incoming messages.
8181 * @from_time is minimal time and date of message sending inclusive.
8182 * @to_time is maximal time and date of message sending inclusive
8183 * @organization_unit_number is number of sender/recipient respectively.
8184 * @status_filter is bit field of isds_message_status values. Use special
8185 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8186 * all values, you can use bit-wise arithmetic if you want.)
8187 * @offset is index of first message we are interested in. First message is 1.
8188 * Set to 0 (or 1) if you don't care.
8189 * @number is maximal length of list you want to get as input value, outputs
8190 * number of messages matching these criteria. Can be NULL if you don't care
8191 * (applies to output value either).
8192 * @messages is automatically reallocated list of isds_message's. Be ware that
8193 * it returns only brief overview (envelope and some other fields) about each
8194 * message, not the complete message. FIXME: Specify exact fields.
8195 * The list is sorted by delivery time in ascending order.
8196 * Use NULL if you don't care about don't need the data (useful if you want to
8197 * know only the @number). If you provide &NULL, list will be allocated on
8198 * heap, if you provide pointer to non-NULL, list will be freed automatically
8199 * at first. Also in case of error the list will be NULLed.
8200 * @return IE_SUCCESS or appropriate error code. */
8201 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
8202 _Bool outgoing_direction,
8203 const struct timeval *from_time, const struct timeval *to_time,
8204 const long int *organization_unit_number,
8205 const unsigned int status_filter,
8206 const unsigned long int offset, unsigned long int *number,
8207 struct isds_list **messages) {
8209 isds_error err = IE_SUCCESS;
8210 #if HAVE_LIBCURL
8211 xmlNsPtr isds_ns = NULL;
8212 xmlNodePtr request = NULL, node;
8213 xmlDocPtr response = NULL;
8214 xmlChar *code = NULL, *message = NULL;
8215 xmlXPathContextPtr xpath_ctx = NULL;
8216 xmlXPathObjectPtr result = NULL;
8217 xmlChar *string = NULL;
8218 long unsigned int count = 0;
8219 #endif
8221 if (!context) return IE_INVALID_CONTEXT;
8222 zfree(context->long_message);
8224 /* Free former message list if any */
8225 if (messages) isds_list_free(messages);
8227 #if HAVE_LIBCURL
8228 /* Check if connection is established
8229 * TODO: This check should be done downstairs. */
8230 if (!context->curl) return IE_CONNECTION_CLOSED;
8232 /* Build GetListOf*Messages request */
8233 request = xmlNewNode(NULL,
8234 (outgoing_direction) ?
8235 BAD_CAST "GetListOfSentMessages" :
8236 BAD_CAST "GetListOfReceivedMessages"
8238 if (!request) {
8239 isds_log_message(context,
8240 (outgoing_direction) ?
8241 _("Could not build GetListOfSentMessages request") :
8242 _("Could not build GetListOfReceivedMessages request")
8244 return IE_ERROR;
8246 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8247 if(!isds_ns) {
8248 isds_log_message(context, _("Could not create ISDS name space"));
8249 xmlFreeNode(request);
8250 return IE_ERROR;
8252 xmlSetNs(request, isds_ns);
8255 if (from_time) {
8256 err = timeval2timestring(from_time, &string);
8257 if (err) goto leave;
8259 INSERT_STRING(request, "dmFromTime", string);
8260 free(string); string = NULL;
8262 if (to_time) {
8263 err = timeval2timestring(to_time, &string);
8264 if (err) goto leave;
8266 INSERT_STRING(request, "dmToTime", string);
8267 free(string); string = NULL;
8269 if (outgoing_direction) {
8270 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
8271 organization_unit_number, string);
8272 } else {
8273 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
8274 organization_unit_number, string);
8277 if (status_filter > MESSAGESTATE_ANY) {
8278 isds_printf_message(context,
8279 _("Invalid message state filter value: %ld"), status_filter);
8280 err = IE_INVAL;
8281 goto leave;
8283 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
8285 if (offset > 0 ) {
8286 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
8287 } else {
8288 INSERT_STRING(request, "dmOffset", "1");
8291 /* number 0 means no limit */
8292 if (number && *number == 0) {
8293 INSERT_STRING(request, "dmLimit", NULL);
8294 } else {
8295 INSERT_ULONGINT(request, "dmLimit", number, string);
8299 isds_log(ILF_ISDS, ILL_DEBUG,
8300 (outgoing_direction) ?
8301 _("Sending GetListOfSentMessages request to ISDS\n") :
8302 _("Sending GetListOfReceivedMessages request to ISDS\n")
8305 /* Sent request */
8306 err = isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
8307 xmlFreeNode(request); request = NULL;
8309 if (err) {
8310 isds_log(ILF_ISDS, ILL_DEBUG,
8311 (outgoing_direction) ?
8312 _("Processing ISDS response on GetListOfSentMessages "
8313 "request failed\n") :
8314 _("Processing ISDS response on GetListOfReceivedMessages "
8315 "request failed\n")
8317 goto leave;
8320 /* Check for response status */
8321 err = isds_response_status(context, SERVICE_DM_INFO, response,
8322 &code, &message, NULL);
8323 if (err) {
8324 isds_log(ILF_ISDS, ILL_DEBUG,
8325 (outgoing_direction) ?
8326 _("ISDS response on GetListOfSentMessages request "
8327 "is missing status\n") :
8328 _("ISDS response on GetListOfReceivedMessages request "
8329 "is missing status\n")
8331 goto leave;
8334 /* Request processed, but nothing found */
8335 if (xmlStrcmp(code, BAD_CAST "0000")) {
8336 char *code_locale = _isds_utf82locale((char*)code);
8337 char *message_locale = _isds_utf82locale((char*)message);
8338 isds_log(ILF_ISDS, ILL_DEBUG,
8339 (outgoing_direction) ?
8340 _("Server refused GetListOfSentMessages request "
8341 "(code=%s, message=%s)\n") :
8342 _("Server refused GetListOfReceivedMessages request "
8343 "(code=%s, message=%s)\n"),
8344 code_locale, message_locale);
8345 isds_log_message(context, message_locale);
8346 free(code_locale);
8347 free(message_locale);
8348 err = IE_ISDS;
8349 goto leave;
8353 /* Extract data */
8354 xpath_ctx = xmlXPathNewContext(response);
8355 if (!xpath_ctx) {
8356 err = IE_ERROR;
8357 goto leave;
8359 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8360 err = IE_ERROR;
8361 goto leave;
8363 result = xmlXPathEvalExpression(
8364 (outgoing_direction) ?
8365 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
8366 "isds:dmRecords/isds:dmRecord" :
8367 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
8368 "isds:dmRecords/isds:dmRecord",
8369 xpath_ctx);
8370 if (!result) {
8371 err = IE_ERROR;
8372 goto leave;
8375 /* Fill output arguments in */
8376 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8377 struct isds_envelope *envelope;
8378 struct isds_list *item = NULL, *last_item = NULL;
8380 for (count = 0; count < result->nodesetval->nodeNr; count++) {
8381 /* Create new message */
8382 item = calloc(1, sizeof(*item));
8383 if (!item) {
8384 err = IE_NOMEM;
8385 goto leave;
8387 item->destructor = (void(*)(void**)) &isds_message_free;
8388 item->data = calloc(1, sizeof(struct isds_message));
8389 if (!item->data) {
8390 isds_list_free(&item);
8391 err = IE_NOMEM;
8392 goto leave;
8395 /* Extract envelope data */
8396 xpath_ctx->node = result->nodesetval->nodeTab[count];
8397 envelope = NULL;
8398 err = extract_DmRecord(context, &envelope, xpath_ctx);
8399 if (err) {
8400 isds_list_free(&item);
8401 goto leave;
8404 /* Attach extracted envelope */
8405 ((struct isds_message *) item->data)->envelope = envelope;
8407 /* Append new message into the list */
8408 if (!*messages) {
8409 *messages = last_item = item;
8410 } else {
8411 last_item->next = item;
8412 last_item = item;
8416 if (number) *number = count;
8418 leave:
8419 if (err) {
8420 isds_list_free(messages);
8423 free(string);
8424 xmlXPathFreeObject(result);
8425 xmlXPathFreeContext(xpath_ctx);
8427 free(code);
8428 free(message);
8429 xmlFreeDoc(response);
8430 xmlFreeNode(request);
8432 if (!err)
8433 isds_log(ILF_ISDS, ILL_DEBUG,
8434 (outgoing_direction) ?
8435 _("GetListOfSentMessages request processed by server "
8436 "successfully.\n") :
8437 _("GetListOfReceivedMessages request processed by server "
8438 "successfully.\n")
8440 #else /* not HAVE_LIBCURL */
8441 err = IE_NOTSUP;
8442 #endif
8443 return err;
8447 /* Get list of outgoing (already sent) messages.
8448 * Any criterion argument can be NULL, if you don't care about it.
8449 * @context is session context. Must not be NULL.
8450 * @from_time is minimal time and date of message sending inclusive.
8451 * @to_time is maximal time and date of message sending inclusive
8452 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
8453 * @status_filter is bit field of isds_message_status values. Use special
8454 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8455 * all values, you can use bit-wise arithmetic if you want.)
8456 * @offset is index of first message we are interested in. First message is 1.
8457 * Set to 0 (or 1) if you don't care.
8458 * @number is maximal length of list you want to get as input value, outputs
8459 * number of messages matching these criteria. Can be NULL if you don't care
8460 * (applies to output value either).
8461 * @messages is automatically reallocated list of isds_message's. Be ware that
8462 * it returns only brief overview (envelope and some other fields) about each
8463 * message, not the complete message. FIXME: Specify exact fields.
8464 * The list is sorted by delivery time in ascending order.
8465 * Use NULL if you don't care about the meta data (useful if you want to know
8466 * only the @number). If you provide &NULL, list will be allocated on heap,
8467 * if you provide pointer to non-NULL, list will be freed automatically at
8468 * first. Also in case of error the list will be NULLed.
8469 * @return IE_SUCCESS or appropriate error code. */
8470 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
8471 const struct timeval *from_time, const struct timeval *to_time,
8472 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
8473 const unsigned long int offset, unsigned long int *number,
8474 struct isds_list **messages) {
8476 return isds_get_list_of_messages(
8477 context, 1,
8478 from_time, to_time, dmSenderOrgUnitNum, status_filter,
8479 offset, number,
8480 messages);
8484 /* Get list of incoming (addressed to you) messages.
8485 * Any criterion argument can be NULL, if you don't care about it.
8486 * @context is session context. Must not be NULL.
8487 * @from_time is minimal time and date of message sending inclusive.
8488 * @to_time is maximal time and date of message sending inclusive
8489 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
8490 * @status_filter is bit field of isds_message_status values. Use special
8491 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8492 * all values, you can use bit-wise arithmetic if you want.)
8493 * @offset is index of first message we are interested in. First message is 1.
8494 * Set to 0 (or 1) if you don't care.
8495 * @number is maximal length of list you want to get as input value, outputs
8496 * number of messages matching these criteria. Can be NULL if you don't care
8497 * (applies to output value either).
8498 * @messages is automatically reallocated list of isds_message's. Be ware that
8499 * it returns only brief overview (envelope and some other fields) about each
8500 * message, not the complete message. FIXME: Specify exact fields.
8501 * Use NULL if you don't care about the meta data (useful if you want to know
8502 * only the @number). If you provide &NULL, list will be allocated on heap,
8503 * if you provide pointer to non-NULL, list will be freed automatically at
8504 * first. Also in case of error the list will be NULLed.
8505 * @return IE_SUCCESS or appropriate error code. */
8506 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
8507 const struct timeval *from_time, const struct timeval *to_time,
8508 const long int *dmRecipientOrgUnitNum,
8509 const unsigned int status_filter,
8510 const unsigned long int offset, unsigned long int *number,
8511 struct isds_list **messages) {
8513 return isds_get_list_of_messages(
8514 context, 0,
8515 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
8516 offset, number,
8517 messages);
8521 /* Get list of sent message state changes.
8522 * Any criterion argument can be NULL, if you don't care about it.
8523 * @context is session context. Must not be NULL.
8524 * @from_time is minimal time and date of status changes inclusive
8525 * @to_time is maximal time and date of status changes inclusive
8526 * @changed_states is automatically reallocated list of
8527 * isds_message_status_change's. If you provide &NULL, list will be allocated
8528 * on heap, if you provide pointer to non-NULL, list will be freed
8529 * automatically at first. Also in case of error the list will be NULLed.
8530 * XXX: The list item ordering is not specified.
8531 * XXX: Server provides only `recent' changes.
8532 * @return IE_SUCCESS or appropriate error code. */
8533 isds_error isds_get_list_of_sent_message_state_changes(
8534 struct isds_ctx *context,
8535 const struct timeval *from_time, const struct timeval *to_time,
8536 struct isds_list **changed_states) {
8538 isds_error err = IE_SUCCESS;
8539 #if HAVE_LIBCURL
8540 xmlNsPtr isds_ns = NULL;
8541 xmlNodePtr request = NULL, node;
8542 xmlDocPtr response = NULL;
8543 xmlXPathContextPtr xpath_ctx = NULL;
8544 xmlXPathObjectPtr result = NULL;
8545 xmlChar *string = NULL;
8546 long unsigned int count = 0;
8547 #endif
8549 if (!context) return IE_INVALID_CONTEXT;
8550 zfree(context->long_message);
8552 /* Free former message list if any */
8553 isds_list_free(changed_states);
8555 #if HAVE_LIBCURL
8556 /* Check if connection is established
8557 * TODO: This check should be done downstairs. */
8558 if (!context->curl) return IE_CONNECTION_CLOSED;
8560 /* Build GetMessageStateChanges request */
8561 request = xmlNewNode(NULL, BAD_CAST "GetMessageStateChanges");
8562 if (!request) {
8563 isds_log_message(context,
8564 _("Could not build GetMessageStateChanges request"));
8565 return IE_ERROR;
8567 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8568 if(!isds_ns) {
8569 isds_log_message(context, _("Could not create ISDS name space"));
8570 xmlFreeNode(request);
8571 return IE_ERROR;
8573 xmlSetNs(request, isds_ns);
8576 if (from_time) {
8577 err = timeval2timestring(from_time, &string);
8578 if (err) goto leave;
8580 INSERT_STRING(request, "dmFromTime", string);
8581 zfree(string);
8583 if (to_time) {
8584 err = timeval2timestring(to_time, &string);
8585 if (err) goto leave;
8587 INSERT_STRING(request, "dmToTime", string);
8588 zfree(string);
8591 /* Sent request */
8592 err = send_destroy_request_check_response(context,
8593 SERVICE_DM_INFO, BAD_CAST "GetMessageStateChanges", &request,
8594 &response, NULL, NULL);
8595 if (err) goto leave;
8598 /* Extract data */
8599 xpath_ctx = xmlXPathNewContext(response);
8600 if (!xpath_ctx) {
8601 err = IE_ERROR;
8602 goto leave;
8604 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8605 err = IE_ERROR;
8606 goto leave;
8608 result = xmlXPathEvalExpression(
8609 BAD_CAST "/isds:GetMessageStateChangesResponse/"
8610 "isds:dmRecords/isds:dmRecord", xpath_ctx);
8611 if (!result) {
8612 err = IE_ERROR;
8613 goto leave;
8616 /* Fill output arguments in */
8617 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8618 struct isds_list *item = NULL, *last_item = NULL;
8620 for (count = 0; count < result->nodesetval->nodeNr; count++) {
8621 /* Create new status change */
8622 item = calloc(1, sizeof(*item));
8623 if (!item) {
8624 err = IE_NOMEM;
8625 goto leave;
8627 item->destructor =
8628 (void(*)(void**)) &isds_message_status_change_free;
8630 /* Extract message status change */
8631 xpath_ctx->node = result->nodesetval->nodeTab[count];
8632 err = extract_StateChangesRecord(context,
8633 (struct isds_message_status_change **) &item->data,
8634 xpath_ctx);
8635 if (err) {
8636 isds_list_free(&item);
8637 goto leave;
8640 /* Append new message status change into the list */
8641 if (!*changed_states) {
8642 *changed_states = last_item = item;
8643 } else {
8644 last_item->next = item;
8645 last_item = item;
8650 leave:
8651 if (err) {
8652 isds_list_free(changed_states);
8655 free(string);
8656 xmlXPathFreeObject(result);
8657 xmlXPathFreeContext(xpath_ctx);
8658 xmlFreeDoc(response);
8659 xmlFreeNode(request);
8661 if (!err)
8662 isds_log(ILF_ISDS, ILL_DEBUG,
8663 _("GetMessageStateChanges request processed by server "
8664 "successfully.\n"));
8665 #else /* not HAVE_LIBCURL */
8666 err = IE_NOTSUP;
8667 #endif
8668 return err;
8672 #if HAVE_LIBCURL
8673 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
8674 * code
8675 * @context is session context
8676 * @service is ISDS WS service handler
8677 * @service_name is name of SERVICE_DM_OPERATIONS
8678 * @message_id is message ID to send as service argument to ISDS
8679 * @response is server SOAP body response as XML document
8680 * @raw_response is automatically reallocated bit stream with response body. Use
8681 * NULL if you don't care
8682 * @raw_response_length is size of @raw_response in bytes
8683 * @code is ISDS status code
8684 * @status_message is ISDS status message
8685 * @return error coded from lower layer, context message will be set up
8686 * appropriately. */
8687 static isds_error build_send_check_message_request(struct isds_ctx *context,
8688 const isds_service service, const xmlChar *service_name,
8689 const char *message_id,
8690 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
8691 xmlChar **code, xmlChar **status_message) {
8693 isds_error err = IE_SUCCESS;
8694 char *service_name_locale = NULL, *message_id_locale = NULL;
8695 xmlNodePtr request = NULL, node;
8696 xmlNsPtr isds_ns = NULL;
8698 if (!context) return IE_INVALID_CONTEXT;
8699 if (!service_name || !message_id) return IE_INVAL;
8700 if (!response || !code || !status_message) return IE_INVAL;
8701 if (!raw_response_length && raw_response) return IE_INVAL;
8703 /* Free output argument */
8704 xmlFreeDoc(*response); *response = NULL;
8705 if (raw_response) zfree(*raw_response);
8706 free(*code);
8707 free(*status_message);
8710 /* Check if connection is established
8711 * TODO: This check should be done downstairs. */
8712 if (!context->curl) return IE_CONNECTION_CLOSED;
8714 service_name_locale = _isds_utf82locale((char*)service_name);
8715 message_id_locale = _isds_utf82locale(message_id);
8716 if (!service_name_locale || !message_id_locale) {
8717 err = IE_NOMEM;
8718 goto leave;
8721 /* Build request */
8722 request = xmlNewNode(NULL, service_name);
8723 if (!request) {
8724 isds_printf_message(context,
8725 _("Could not build %s request for %s message ID"),
8726 service_name_locale, message_id_locale);
8727 err = IE_ERROR;
8728 goto leave;
8730 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8731 if(!isds_ns) {
8732 isds_log_message(context, _("Could not create ISDS name space"));
8733 err = IE_ERROR;
8734 goto leave;
8736 xmlSetNs(request, isds_ns);
8739 /* Add requested ID */
8740 err = validate_message_id_length(context, (xmlChar *) message_id);
8741 if (err) goto leave;
8742 INSERT_STRING(request, "dmID", message_id);
8745 isds_log(ILF_ISDS, ILL_DEBUG,
8746 _("Sending %s request for %s message ID to ISDS\n"),
8747 service_name_locale, message_id_locale);
8749 /* Send request */
8750 err = isds(context, service, request, response,
8751 raw_response, raw_response_length);
8752 xmlFreeNode(request); request = NULL;
8754 if (err) {
8755 isds_log(ILF_ISDS, ILL_DEBUG,
8756 _("Processing ISDS response on %s request failed\n"),
8757 service_name_locale);
8758 goto leave;
8761 /* Check for response status */
8762 err = isds_response_status(context, service, *response,
8763 code, status_message, NULL);
8764 if (err) {
8765 isds_log(ILF_ISDS, ILL_DEBUG,
8766 _("ISDS response on %s request is missing status\n"),
8767 service_name_locale);
8768 goto leave;
8771 /* Request processed, but nothing found */
8772 if (xmlStrcmp(*code, BAD_CAST "0000")) {
8773 char *code_locale = _isds_utf82locale((char*) *code);
8774 char *status_message_locale = _isds_utf82locale((char*) *status_message);
8775 isds_log(ILF_ISDS, ILL_DEBUG,
8776 _("Server refused %s request for %s message ID "
8777 "(code=%s, message=%s)\n"),
8778 service_name_locale, message_id_locale,
8779 code_locale, status_message_locale);
8780 isds_log_message(context, status_message_locale);
8781 free(code_locale);
8782 free(status_message_locale);
8783 err = IE_ISDS;
8784 goto leave;
8787 leave:
8788 free(message_id_locale);
8789 free(service_name_locale);
8790 xmlFreeNode(request);
8791 return err;
8795 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
8796 * signed data and free ISDS response.
8797 * @context is session context
8798 * @message_id is UTF-8 encoded message ID for logging purpose
8799 * @response is parsed XML document. It will be freed and NULLed in the middle
8800 * of function run to save memory. This is not guaranteed in case of error.
8801 * @request_name is name of ISDS request used to construct response root
8802 * element name and for logging purpose.
8803 * @raw is reallocated output buffer with DER encoded CMS data
8804 * @raw_length is size of @raw buffer in bytes
8805 * @returns standard error codes, in case of error, @raw will be freed and
8806 * NULLed, @response sometimes. */
8807 static isds_error find_extract_signed_data_free_response(
8808 struct isds_ctx *context, const xmlChar *message_id,
8809 xmlDocPtr *response, const xmlChar *request_name,
8810 void **raw, size_t *raw_length) {
8812 isds_error err = IE_SUCCESS;
8813 char *xpath_expression = NULL;
8814 xmlXPathContextPtr xpath_ctx = NULL;
8815 xmlXPathObjectPtr result = NULL;
8816 char *encoded_structure = NULL;
8818 if (!context) return IE_INVALID_CONTEXT;
8819 if (!raw) return IE_INVAL;
8820 zfree(*raw);
8821 if (!message_id || !response || !*response || !request_name || !raw_length)
8822 return IE_INVAL;
8824 /* Build XPath expression */
8825 xpath_expression = _isds_astrcat3("/isds:", (char *) request_name,
8826 "Response/isds:dmSignature");
8827 if (!xpath_expression) return IE_NOMEM;
8829 /* Extract data */
8830 xpath_ctx = xmlXPathNewContext(*response);
8831 if (!xpath_ctx) {
8832 err = IE_ERROR;
8833 goto leave;
8835 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8836 err = IE_ERROR;
8837 goto leave;
8839 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
8840 if (!result) {
8841 err = IE_ERROR;
8842 goto leave;
8844 /* Empty response */
8845 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8846 char *message_id_locale = _isds_utf82locale((char*) message_id);
8847 isds_printf_message(context,
8848 _("Server did not return any signed data for message ID `%s' "
8849 "on %s request"),
8850 message_id_locale, request_name);
8851 free(message_id_locale);
8852 err = IE_ISDS;
8853 goto leave;
8855 /* More responses */
8856 if (result->nodesetval->nodeNr > 1) {
8857 char *message_id_locale = _isds_utf82locale((char*) message_id);
8858 isds_printf_message(context,
8859 _("Server did return more signed data for message ID `%s' "
8860 "on %s request"),
8861 message_id_locale, request_name);
8862 free(message_id_locale);
8863 err = IE_ISDS;
8864 goto leave;
8866 /* One response */
8867 xpath_ctx->node = result->nodesetval->nodeTab[0];
8869 /* Extract PKCS#7 structure */
8870 EXTRACT_STRING(".", encoded_structure);
8871 if (!encoded_structure) {
8872 isds_log_message(context, _("dmSignature element is empty"));
8875 /* Here we have delivery info as standalone CMS in encoded_structure.
8876 * We don't need any other data, free them: */
8877 xmlXPathFreeObject(result); result = NULL;
8878 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
8879 xmlFreeDoc(*response); *response = NULL;
8882 /* Decode PKCS#7 to DER format */
8883 *raw_length = _isds_b64decode(encoded_structure, raw);
8884 if (*raw_length == (size_t) -1) {
8885 isds_log_message(context,
8886 _("Error while Base64-decoding PKCS#7 structure"));
8887 err = IE_ERROR;
8888 goto leave;
8891 leave:
8892 if (err) {
8893 zfree(*raw);
8894 raw_length = 0;
8897 free(encoded_structure);
8898 xmlXPathFreeObject(result);
8899 xmlXPathFreeContext(xpath_ctx);
8900 free(xpath_expression);
8902 return err;
8904 #endif /* HAVE_LIBCURL */
8907 /* Download incoming message envelope identified by ID.
8908 * @context is session context
8909 * @message_id is message identifier (you can get them from
8910 * isds_get_list_of_received_messages())
8911 * @message is automatically reallocated message retrieved from ISDS.
8912 * It will miss documents per se. Use isds_get_received_message(), if you are
8913 * interested in documents (content) too.
8914 * Returned hash and timestamp require documents to be verifiable. */
8915 isds_error isds_get_received_envelope(struct isds_ctx *context,
8916 const char *message_id, struct isds_message **message) {
8918 isds_error err = IE_SUCCESS;
8919 #if HAVE_LIBCURL
8920 xmlDocPtr response = NULL;
8921 xmlChar *code = NULL, *status_message = NULL;
8922 xmlXPathContextPtr xpath_ctx = NULL;
8923 xmlXPathObjectPtr result = NULL;
8924 #endif
8926 if (!context) return IE_INVALID_CONTEXT;
8927 zfree(context->long_message);
8929 /* Free former message if any */
8930 if (!message) return IE_INVAL;
8931 isds_message_free(message);
8933 #if HAVE_LIBCURL
8934 /* Do request and check for success */
8935 err = build_send_check_message_request(context, SERVICE_DM_INFO,
8936 BAD_CAST "MessageEnvelopeDownload", message_id,
8937 &response, NULL, NULL, &code, &status_message);
8938 if (err) goto leave;
8940 /* Extract data */
8941 xpath_ctx = xmlXPathNewContext(response);
8942 if (!xpath_ctx) {
8943 err = IE_ERROR;
8944 goto leave;
8946 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8947 err = IE_ERROR;
8948 goto leave;
8950 result = xmlXPathEvalExpression(
8951 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
8952 "isds:dmReturnedMessageEnvelope",
8953 xpath_ctx);
8954 if (!result) {
8955 err = IE_ERROR;
8956 goto leave;
8958 /* Empty response */
8959 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8960 char *message_id_locale = _isds_utf82locale((char*) message_id);
8961 isds_printf_message(context,
8962 _("Server did not return any envelope for ID `%s' "
8963 "on MessageEnvelopeDownload request"), message_id_locale);
8964 free(message_id_locale);
8965 err = IE_ISDS;
8966 goto leave;
8968 /* More envelops */
8969 if (result->nodesetval->nodeNr > 1) {
8970 char *message_id_locale = _isds_utf82locale((char*) message_id);
8971 isds_printf_message(context,
8972 _("Server did return more envelopes for ID `%s' "
8973 "on MessageEnvelopeDownload request"), message_id_locale);
8974 free(message_id_locale);
8975 err = IE_ISDS;
8976 goto leave;
8978 /* One message */
8979 xpath_ctx->node = result->nodesetval->nodeTab[0];
8981 /* Extract the envelope (= message without documents, hence 0) */
8982 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
8983 if (err) goto leave;
8985 /* Save XML blob */
8986 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
8987 &(*message)->raw_length);
8989 leave:
8990 if (err) {
8991 isds_message_free(message);
8994 xmlXPathFreeObject(result);
8995 xmlXPathFreeContext(xpath_ctx);
8997 free(code);
8998 free(status_message);
8999 if (!*message || !(*message)->xml) {
9000 xmlFreeDoc(response);
9003 if (!err)
9004 isds_log(ILF_ISDS, ILL_DEBUG,
9005 _("MessageEnvelopeDownload request processed by server "
9006 "successfully.\n")
9008 #else /* not HAVE_LIBCURL */
9009 err = IE_NOTSUP;
9010 #endif
9011 return err;
9015 /* Load delivery info of any format from buffer.
9016 * @context is session context
9017 * @raw_type advertises format of @buffer content. Only delivery info types
9018 * are accepted.
9019 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
9020 * retrieve such data from message->raw after calling
9021 * isds_get_signed_delivery_info().
9022 * @length is length of buffer in bytes.
9023 * @message is automatically reallocated message parsed from @buffer.
9024 * @strategy selects how buffer will be attached into raw isds_message member.
9025 * */
9026 isds_error isds_load_delivery_info(struct isds_ctx *context,
9027 const isds_raw_type raw_type,
9028 const void *buffer, const size_t length,
9029 struct isds_message **message, const isds_buffer_strategy strategy) {
9031 isds_error err = IE_SUCCESS;
9032 message_ns_type message_ns;
9033 xmlDocPtr message_doc = NULL;
9034 xmlXPathContextPtr xpath_ctx = NULL;
9035 xmlXPathObjectPtr result = NULL;
9036 void *xml_stream = NULL;
9037 size_t xml_stream_length = 0;
9039 if (!context) return IE_INVALID_CONTEXT;
9040 zfree(context->long_message);
9041 if (!message) return IE_INVAL;
9042 isds_message_free(message);
9043 if (!buffer) return IE_INVAL;
9046 /* Select buffer format and extract XML from CMS*/
9047 switch (raw_type) {
9048 case RAWTYPE_DELIVERYINFO:
9049 message_ns = MESSAGE_NS_UNSIGNED;
9050 xml_stream = (void *) buffer;
9051 xml_stream_length = length;
9052 break;
9054 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
9055 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
9056 xml_stream = (void *) buffer;
9057 xml_stream_length = length;
9058 break;
9060 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
9061 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
9062 err = _isds_extract_cms_data(context, buffer, length,
9063 &xml_stream, &xml_stream_length);
9064 if (err) goto leave;
9065 break;
9067 default:
9068 isds_log_message(context, _("Bad raw delivery representation type"));
9069 return IE_INVAL;
9070 break;
9073 isds_log(ILF_ISDS, ILL_DEBUG,
9074 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
9075 xml_stream_length, xml_stream);
9077 /* Convert delivery info XML stream into XPath context */
9078 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
9079 if (!message_doc) {
9080 err = IE_XML;
9081 goto leave;
9083 xpath_ctx = xmlXPathNewContext(message_doc);
9084 if (!xpath_ctx) {
9085 err = IE_ERROR;
9086 goto leave;
9088 /* XXX: Name spaces mangled for signed delivery info:
9089 * http://isds.czechpoint.cz/v20/delivery:
9091 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
9092 * <q:dmDelivery>
9093 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
9094 * <p:dmID>170272</p:dmID>
9095 * ...
9096 * </p:dmDm>
9097 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
9098 * ...
9099 * </q:dmEvents>...</q:dmEvents>
9100 * </q:dmDelivery>
9101 * </q:GetDeliveryInfoResponse>
9102 * */
9103 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
9104 err = IE_ERROR;
9105 goto leave;
9107 result = xmlXPathEvalExpression(
9108 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
9109 xpath_ctx);
9110 if (!result) {
9111 err = IE_ERROR;
9112 goto leave;
9114 /* Empty delivery info */
9115 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9116 isds_printf_message(context,
9117 _("XML document is not sisds:dmDelivery document"));
9118 err = IE_ISDS;
9119 goto leave;
9121 /* More delivery info's */
9122 if (result->nodesetval->nodeNr > 1) {
9123 isds_printf_message(context,
9124 _("XML document has more sisds:dmDelivery elements"));
9125 err = IE_ISDS;
9126 goto leave;
9128 /* One delivery info */
9129 xpath_ctx->node = result->nodesetval->nodeTab[0];
9131 /* Extract the envelope (= message without documents, hence 0).
9132 * XXX: extract_TReturnedMessage() can obtain attachments size,
9133 * but delivery info carries none. It's coded as option elements,
9134 * so it should work. */
9135 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
9136 if (err) goto leave;
9138 /* Extract events */
9139 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
9140 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
9141 if (err) { err = IE_ERROR; goto leave; }
9142 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
9143 if (err) goto leave;
9145 /* Append raw CMS structure into message */
9146 (*message)->raw_type = raw_type;
9147 switch (strategy) {
9148 case BUFFER_DONT_STORE:
9149 break;
9150 case BUFFER_COPY:
9151 (*message)->raw = malloc(length);
9152 if (!(*message)->raw) {
9153 err = IE_NOMEM;
9154 goto leave;
9156 memcpy((*message)->raw, buffer, length);
9157 (*message)->raw_length = length;
9158 break;
9159 case BUFFER_MOVE:
9160 (*message)->raw = (void *) buffer;
9161 (*message)->raw_length = length;
9162 break;
9163 default:
9164 err = IE_ENUM;
9165 goto leave;
9168 leave:
9169 if (err) {
9170 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
9171 isds_message_free(message);
9174 xmlXPathFreeObject(result);
9175 xmlXPathFreeContext(xpath_ctx);
9176 if (!*message || !(*message)->xml) {
9177 xmlFreeDoc(message_doc);
9179 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
9181 if (!err)
9182 isds_log(ILF_ISDS, ILL_DEBUG,
9183 _("Delivery info loaded successfully.\n"));
9184 return err;
9188 /* Download signed delivery info-sheet of given message identified by ID.
9189 * @context is session context
9190 * @message_id is message identifier (you can get them from
9191 * isds_get_list_of_{sent,received}_messages())
9192 * @message is automatically reallocated message retrieved from ISDS.
9193 * It will miss documents per se. Use isds_get_signed_received_message(),
9194 * if you are interested in documents (content). OTOH, only this function
9195 * can get list events message has gone through. */
9196 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
9197 const char *message_id, struct isds_message **message) {
9199 isds_error err = IE_SUCCESS;
9200 #if HAVE_LIBCURL
9201 xmlDocPtr response = NULL;
9202 xmlChar *code = NULL, *status_message = NULL;
9203 void *raw = NULL;
9204 size_t raw_length = 0;
9205 #endif
9207 if (!context) return IE_INVALID_CONTEXT;
9208 zfree(context->long_message);
9210 /* Free former message if any */
9211 if (!message) return IE_INVAL;
9212 isds_message_free(message);
9214 #if HAVE_LIBCURL
9215 /* Do request and check for success */
9216 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9217 BAD_CAST "GetSignedDeliveryInfo", message_id,
9218 &response, NULL, NULL, &code, &status_message);
9219 if (err) goto leave;
9221 /* Find signed delivery info, extract it into raw and maybe free
9222 * response */
9223 err = find_extract_signed_data_free_response(context,
9224 (xmlChar *)message_id, &response,
9225 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
9226 if (err) goto leave;
9228 /* Parse delivery info */
9229 err = isds_load_delivery_info(context,
9230 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
9231 message, BUFFER_MOVE);
9232 if (err) goto leave;
9234 raw = NULL;
9236 leave:
9237 if (err) {
9238 isds_message_free(message);
9241 free(raw);
9242 free(code);
9243 free(status_message);
9244 xmlFreeDoc(response);
9246 if (!err)
9247 isds_log(ILF_ISDS, ILL_DEBUG,
9248 _("GetSignedDeliveryInfo request processed by server "
9249 "successfully.\n")
9251 #else /* not HAVE_LIBCURL */
9252 err = IE_NOTSUP;
9253 #endif
9254 return err;
9258 /* Download delivery info-sheet of given message identified by ID.
9259 * @context is session context
9260 * @message_id is message identifier (you can get them from
9261 * isds_get_list_of_{sent,received}_messages())
9262 * @message is automatically reallocated message retrieved from ISDS.
9263 * It will miss documents per se. Use isds_get_received_message(), if you are
9264 * interested in documents (content). OTOH, only this function can get list
9265 * of events message has gone through. */
9266 isds_error isds_get_delivery_info(struct isds_ctx *context,
9267 const char *message_id, struct isds_message **message) {
9269 isds_error err = IE_SUCCESS;
9270 #if HAVE_LIBCURL
9271 xmlDocPtr response = NULL;
9272 xmlChar *code = NULL, *status_message = NULL;
9273 xmlNodePtr delivery_node = NULL;
9274 void *raw = NULL;
9275 size_t raw_length = 0;
9276 #endif
9278 if (!context) return IE_INVALID_CONTEXT;
9279 zfree(context->long_message);
9281 /* Free former message if any */
9282 if (!message) return IE_INVAL;
9283 isds_message_free(message);
9285 #if HAVE_LIBCURL
9286 /* Do request and check for success */
9287 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9288 BAD_CAST "GetDeliveryInfo", message_id,
9289 &response, NULL, NULL, &code, &status_message);
9290 if (err) goto leave;
9293 /* Serialize delivery info */
9294 delivery_node = xmlDocGetRootElement(response);
9295 if (!delivery_node) {
9296 char *message_id_locale = _isds_utf82locale((char*) message_id);
9297 isds_printf_message(context,
9298 _("Server did not return any delivery info for ID `%s' "
9299 "on GetDeliveryInfo request"), message_id_locale);
9300 free(message_id_locale);
9301 err = IE_ISDS;
9302 goto leave;
9304 err = serialize_subtree(context, delivery_node, &raw, &raw_length);
9305 if (err) goto leave;
9307 /* Parse delivery info */
9308 /* TODO: Here we parse the response second time. We could single delivery
9309 * parser from isds_load_delivery_info() to make things faster. */
9310 err = isds_load_delivery_info(context,
9311 RAWTYPE_DELIVERYINFO, raw, raw_length,
9312 message, BUFFER_MOVE);
9313 if (err) goto leave;
9315 raw = NULL;
9318 leave:
9319 if (err) {
9320 isds_message_free(message);
9323 free(raw);
9324 free(code);
9325 free(status_message);
9326 xmlFreeDoc(response);
9328 if (!err)
9329 isds_log(ILF_ISDS, ILL_DEBUG,
9330 _("GetDeliveryInfo request processed by server "
9331 "successfully.\n")
9333 #else /* not HAVE_LIBCURL */
9334 err = IE_NOTSUP;
9335 #endif
9336 return err;
9340 /* Download incoming message identified by ID.
9341 * @context is session context
9342 * @message_id is message identifier (you can get them from
9343 * isds_get_list_of_received_messages())
9344 * @message is automatically reallocated message retrieved from ISDS */
9345 isds_error isds_get_received_message(struct isds_ctx *context,
9346 const char *message_id, struct isds_message **message) {
9348 isds_error err = IE_SUCCESS;
9349 #if HAVE_LIBCURL
9350 xmlDocPtr response = NULL;
9351 void *xml_stream = NULL;
9352 size_t xml_stream_length;
9353 xmlChar *code = NULL, *status_message = NULL;
9354 xmlXPathContextPtr xpath_ctx = NULL;
9355 xmlXPathObjectPtr result = NULL;
9356 char *phys_path = NULL;
9357 size_t phys_start, phys_end;
9358 #endif
9360 if (!context) return IE_INVALID_CONTEXT;
9361 zfree(context->long_message);
9363 /* Free former message if any */
9364 if (message) isds_message_free(message);
9366 #if HAVE_LIBCURL
9367 /* Do request and check for success */
9368 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
9369 BAD_CAST "MessageDownload", message_id,
9370 &response, &xml_stream, &xml_stream_length,
9371 &code, &status_message);
9372 if (err) goto leave;
9374 /* Extract data */
9375 xpath_ctx = xmlXPathNewContext(response);
9376 if (!xpath_ctx) {
9377 err = IE_ERROR;
9378 goto leave;
9380 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9381 err = IE_ERROR;
9382 goto leave;
9384 result = xmlXPathEvalExpression(
9385 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
9386 xpath_ctx);
9387 if (!result) {
9388 err = IE_ERROR;
9389 goto leave;
9391 /* Empty response */
9392 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9393 char *message_id_locale = _isds_utf82locale((char*) message_id);
9394 isds_printf_message(context,
9395 _("Server did not return any message for ID `%s' "
9396 "on MessageDownload request"), message_id_locale);
9397 free(message_id_locale);
9398 err = IE_ISDS;
9399 goto leave;
9401 /* More messages */
9402 if (result->nodesetval->nodeNr > 1) {
9403 char *message_id_locale = _isds_utf82locale((char*) message_id);
9404 isds_printf_message(context,
9405 _("Server did return more messages for ID `%s' "
9406 "on MessageDownload request"), message_id_locale);
9407 free(message_id_locale);
9408 err = IE_ISDS;
9409 goto leave;
9411 /* One message */
9412 xpath_ctx->node = result->nodesetval->nodeTab[0];
9414 /* Extract the message */
9415 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
9416 if (err) goto leave;
9418 /* Locate raw XML blob */
9419 phys_path = strdup(
9420 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
9421 PHYSXML_ELEMENT_SEPARATOR
9422 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
9423 PHYSXML_ELEMENT_SEPARATOR
9424 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
9426 if (!phys_path) {
9427 err = IE_NOMEM;
9428 goto leave;
9430 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
9431 phys_path, &phys_start, &phys_end);
9432 zfree(phys_path);
9433 if (err) {
9434 isds_log_message(context,
9435 _("Substring with isds:MessageDownloadResponse element "
9436 "could not be located in raw SOAP message"));
9437 goto leave;
9439 /* Save XML blob */
9440 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
9441 &(*message)->raw_length);*/
9442 /* TODO: Store name space declarations from ancestors */
9443 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
9444 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
9445 (*message)->raw_length = phys_end - phys_start + 1;
9446 (*message)->raw = malloc((*message)->raw_length);
9447 if (!(*message)->raw) {
9448 err = IE_NOMEM;
9449 goto leave;
9451 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
9454 leave:
9455 if (err) {
9456 isds_message_free(message);
9459 free(phys_path);
9461 xmlXPathFreeObject(result);
9462 xmlXPathFreeContext(xpath_ctx);
9464 free(code);
9465 free(status_message);
9466 free(xml_stream);
9467 if (!*message || !(*message)->xml) {
9468 xmlFreeDoc(response);
9471 if (!err)
9472 isds_log(ILF_ISDS, ILL_DEBUG,
9473 _("MessageDownload request processed by server "
9474 "successfully.\n")
9476 #else /* not HAVE_LIBCURL */
9477 err = IE_NOTSUP;
9478 #endif
9479 return err;
9483 /* Load message of any type from buffer.
9484 * @context is session context
9485 * @raw_type defines content type of @buffer. Only message types are allowed.
9486 * @buffer is message raw representation. Format (CMS, plain signed,
9487 * message direction) is defined in @raw_type. You can retrieve such data
9488 * from message->raw after calling isds_get_[signed]{received,sent}_message().
9489 * @length is length of buffer in bytes.
9490 * @message is automatically reallocated message parsed from @buffer.
9491 * @strategy selects how buffer will be attached into raw isds_message member.
9492 * */
9493 isds_error isds_load_message(struct isds_ctx *context,
9494 const isds_raw_type raw_type, const void *buffer, const size_t length,
9495 struct isds_message **message, const isds_buffer_strategy strategy) {
9497 isds_error err = IE_SUCCESS;
9498 void *xml_stream = NULL;
9499 size_t xml_stream_length = 0;
9500 message_ns_type message_ns;
9501 xmlDocPtr message_doc = NULL;
9502 xmlXPathContextPtr xpath_ctx = NULL;
9503 xmlXPathObjectPtr result = NULL;
9505 if (!context) return IE_INVALID_CONTEXT;
9506 zfree(context->long_message);
9507 if (!message) return IE_INVAL;
9508 isds_message_free(message);
9509 if (!buffer) return IE_INVAL;
9512 /* Select buffer format and extract XML from CMS*/
9513 switch (raw_type) {
9514 case RAWTYPE_INCOMING_MESSAGE:
9515 message_ns = MESSAGE_NS_UNSIGNED;
9516 xml_stream = (void *) buffer;
9517 xml_stream_length = length;
9518 break;
9520 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
9521 message_ns = MESSAGE_NS_SIGNED_INCOMING;
9522 xml_stream = (void *) buffer;
9523 xml_stream_length = length;
9524 break;
9526 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
9527 message_ns = MESSAGE_NS_SIGNED_INCOMING;
9528 err = _isds_extract_cms_data(context, buffer, length,
9529 &xml_stream, &xml_stream_length);
9530 if (err) goto leave;
9531 break;
9533 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
9534 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
9535 xml_stream = (void *) buffer;
9536 xml_stream_length = length;
9537 break;
9539 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
9540 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
9541 err = _isds_extract_cms_data(context, buffer, length,
9542 &xml_stream, &xml_stream_length);
9543 if (err) goto leave;
9544 break;
9546 default:
9547 isds_log_message(context, _("Bad raw message representation type"));
9548 return IE_INVAL;
9549 break;
9552 isds_log(ILF_ISDS, ILL_DEBUG,
9553 _("Loading message:\n%.*s\nEnd of message\n"),
9554 xml_stream_length, xml_stream);
9556 /* Convert messages XML stream into XPath context */
9557 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
9558 if (!message_doc) {
9559 err = IE_XML;
9560 goto leave;
9562 xpath_ctx = xmlXPathNewContext(message_doc);
9563 if (!xpath_ctx) {
9564 err = IE_ERROR;
9565 goto leave;
9567 /* XXX: Standard name space for unsigned incoming direction:
9568 * http://isds.czechpoint.cz/v20/
9570 * XXX: Name spaces mangled for signed outgoing direction:
9571 * http://isds.czechpoint.cz/v20/SentMessage:
9573 * <q:MessageDownloadResponse
9574 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
9575 * <q:dmReturnedMessage>
9576 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
9577 * <p:dmID>151916</p:dmID>
9578 * ...
9579 * </p:dmDm>
9580 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
9581 * ...
9582 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
9583 * </q:dmReturnedMessage>
9584 * </q:MessageDownloadResponse>
9586 * XXX: Name spaces mangled for signed incoming direction:
9587 * http://isds.czechpoint.cz/v20/message:
9589 * <q:MessageDownloadResponse
9590 * xmlns:q="http://isds.czechpoint.cz/v20/message">
9591 * <q:dmReturnedMessage>
9592 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
9593 * <p:dmID>151916</p:dmID>
9594 * ...
9595 * </p:dmDm>
9596 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
9597 * ...
9598 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
9599 * </q:dmReturnedMessage>
9600 * </q:MessageDownloadResponse>
9602 * Stupidity of ISDS developers is unlimited */
9603 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
9604 err = IE_ERROR;
9605 goto leave;
9607 result = xmlXPathEvalExpression(
9608 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
9609 xpath_ctx);
9610 if (!result) {
9611 err = IE_ERROR;
9612 goto leave;
9614 /* Empty message */
9615 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9616 isds_printf_message(context,
9617 _("XML document does not contain "
9618 "sisds:dmReturnedMessage element"));
9619 err = IE_ISDS;
9620 goto leave;
9622 /* More messages */
9623 if (result->nodesetval->nodeNr > 1) {
9624 isds_printf_message(context,
9625 _("XML document has more sisds:dmReturnedMessage elements"));
9626 err = IE_ISDS;
9627 goto leave;
9629 /* One message */
9630 xpath_ctx->node = result->nodesetval->nodeTab[0];
9632 /* Extract the message */
9633 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
9634 if (err) goto leave;
9636 /* Append raw buffer into message */
9637 (*message)->raw_type = raw_type;
9638 switch (strategy) {
9639 case BUFFER_DONT_STORE:
9640 break;
9641 case BUFFER_COPY:
9642 (*message)->raw = malloc(length);
9643 if (!(*message)->raw) {
9644 err = IE_NOMEM;
9645 goto leave;
9647 memcpy((*message)->raw, buffer, length);
9648 (*message)->raw_length = length;
9649 break;
9650 case BUFFER_MOVE:
9651 (*message)->raw = (void *) buffer;
9652 (*message)->raw_length = length;
9653 break;
9654 default:
9655 err = IE_ENUM;
9656 goto leave;
9660 leave:
9661 if (err) {
9662 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
9663 isds_message_free(message);
9666 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
9667 xmlXPathFreeObject(result);
9668 xmlXPathFreeContext(xpath_ctx);
9669 if (!*message || !(*message)->xml) {
9670 xmlFreeDoc(message_doc);
9673 if (!err)
9674 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
9675 return err;
9679 /* Determine type of raw message or delivery info according some heuristics.
9680 * It does not validate the raw blob.
9681 * @context is session context
9682 * @raw_type returns content type of @buffer. Valid only if exit code of this
9683 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
9684 * reallocated memory.
9685 * @buffer is message raw representation.
9686 * @length is length of buffer in bytes. */
9687 isds_error isds_guess_raw_type(struct isds_ctx *context,
9688 isds_raw_type *raw_type, const void *buffer, const size_t length) {
9689 isds_error err;
9690 void *xml_stream = NULL;
9691 size_t xml_stream_length = 0;
9692 xmlDocPtr document = NULL;
9693 xmlNodePtr root = NULL;
9695 if (!context) return IE_INVALID_CONTEXT;
9696 zfree(context->long_message);
9697 if (length == 0 || !buffer) return IE_INVAL;
9698 if (!raw_type) return IE_INVAL;
9700 /* Try CMS */
9701 err = _isds_extract_cms_data(context, buffer, length,
9702 &xml_stream, &xml_stream_length);
9703 if (err) {
9704 xml_stream = (void *) buffer;
9705 xml_stream_length = (size_t) length;
9706 err = IE_SUCCESS;
9709 /* Try XML */
9710 document = xmlParseMemory(xml_stream, xml_stream_length);
9711 if (!document) {
9712 isds_printf_message(context,
9713 _("Could not parse data as XML document"));
9714 err = IE_NOTSUP;
9715 goto leave;
9718 /* Get root element */
9719 root = xmlDocGetRootElement(document);
9720 if (!root) {
9721 isds_printf_message(context,
9722 _("XML document is missing root element"));
9723 err = IE_XML;
9724 goto leave;
9727 if (!root->ns || !root->ns->href) {
9728 isds_printf_message(context,
9729 _("Root element does not belong to any name space"));
9730 err = IE_NOTSUP;
9731 goto leave;
9734 /* Test name space */
9735 if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_INCOMING_NS)) {
9736 if (xml_stream == buffer)
9737 *raw_type = RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE;
9738 else
9739 *raw_type = RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
9740 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_OUTGOING_NS)) {
9741 if (xml_stream == buffer)
9742 *raw_type = RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE;
9743 else
9744 *raw_type = RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE;
9745 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_DELIVERY_NS)) {
9746 if (xml_stream == buffer)
9747 *raw_type = RAWTYPE_PLAIN_SIGNED_DELIVERYINFO;
9748 else
9749 *raw_type = RAWTYPE_CMS_SIGNED_DELIVERYINFO;
9750 } else if (!xmlStrcmp(root->ns->href, BAD_CAST ISDS_NS)) {
9751 if (xml_stream != buffer) {
9752 isds_printf_message(context,
9753 _("Document in ISDS name space is encapsulated into CMS" ));
9754 err = IE_NOTSUP;
9755 } else if (!xmlStrcmp(root->name, BAD_CAST "MessageDownloadResponse"))
9756 *raw_type = RAWTYPE_INCOMING_MESSAGE;
9757 else if (!xmlStrcmp(root->name, BAD_CAST "GetDeliveryInfoResponse"))
9758 *raw_type = RAWTYPE_DELIVERYINFO;
9759 else {
9760 isds_printf_message(context,
9761 _("Unknown root element in ISDS name space"));
9762 err = IE_NOTSUP;
9764 } else {
9765 isds_printf_message(context,
9766 _("Unknown name space"));
9767 err = IE_NOTSUP;
9770 leave:
9771 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
9772 xmlFreeDoc(document);
9773 return err;
9777 /* Download signed incoming/outgoing message identified by ID.
9778 * @context is session context
9779 * @output is true for outgoing message, false for incoming message
9780 * @message_id is message identifier (you can get them from
9781 * isds_get_list_of_{sent,received}_messages())
9782 * @message is automatically reallocated message retrieved from ISDS. The raw
9783 * member will be filled with PKCS#7 structure in DER format. */
9784 static isds_error isds_get_signed_message(struct isds_ctx *context,
9785 const _Bool outgoing, const char *message_id,
9786 struct isds_message **message) {
9788 isds_error err = IE_SUCCESS;
9789 #if HAVE_LIBCURL
9790 xmlDocPtr response = NULL;
9791 xmlChar *code = NULL, *status_message = NULL;
9792 xmlXPathContextPtr xpath_ctx = NULL;
9793 xmlXPathObjectPtr result = NULL;
9794 char *encoded_structure = NULL;
9795 void *raw = NULL;
9796 size_t raw_length = 0;
9797 #endif
9799 if (!context) return IE_INVALID_CONTEXT;
9800 zfree(context->long_message);
9801 if (!message) return IE_INVAL;
9802 isds_message_free(message);
9804 #if HAVE_LIBCURL
9805 /* Do request and check for success */
9806 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
9807 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
9808 BAD_CAST "SignedMessageDownload",
9809 message_id, &response, NULL, NULL, &code, &status_message);
9810 if (err) goto leave;
9812 /* Find signed message, extract it into raw and maybe free
9813 * response */
9814 err = find_extract_signed_data_free_response(context,
9815 (xmlChar *)message_id, &response,
9816 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
9817 BAD_CAST "SignedMessageDownload",
9818 &raw, &raw_length);
9819 if (err) goto leave;
9821 /* Parse message */
9822 err = isds_load_message(context,
9823 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
9824 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
9825 raw, raw_length, message, BUFFER_MOVE);
9826 if (err) goto leave;
9828 raw = NULL;
9830 leave:
9831 if (err) {
9832 isds_message_free(message);
9835 free(encoded_structure);
9836 xmlXPathFreeObject(result);
9837 xmlXPathFreeContext(xpath_ctx);
9838 free(raw);
9840 free(code);
9841 free(status_message);
9842 xmlFreeDoc(response);
9844 if (!err)
9845 isds_log(ILF_ISDS, ILL_DEBUG,
9846 (outgoing) ?
9847 _("SignedSentMessageDownload request processed by server "
9848 "successfully.\n") :
9849 _("SignedMessageDownload request processed by server "
9850 "successfully.\n")
9852 #else /* not HAVE_LIBCURL */
9853 err = IE_NOTSUP;
9854 #endif
9855 return err;
9859 /* Download signed incoming message identified by ID.
9860 * @context is session context
9861 * @message_id is message identifier (you can get them from
9862 * isds_get_list_of_received_messages())
9863 * @message is automatically reallocated message retrieved from ISDS. The raw
9864 * member will be filled with PKCS#7 structure in DER format. */
9865 isds_error isds_get_signed_received_message(struct isds_ctx *context,
9866 const char *message_id, struct isds_message **message) {
9867 return isds_get_signed_message(context, 0, message_id, message);
9871 /* Download signed outgoing message identified by ID.
9872 * @context is session context
9873 * @message_id is message identifier (you can get them from
9874 * isds_get_list_of_sent_messages())
9875 * @message is automatically reallocated message retrieved from ISDS. The raw
9876 * member will be filled with PKCS#7 structure in DER format. */
9877 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
9878 const char *message_id, struct isds_message **message) {
9879 return isds_get_signed_message(context, 1, message_id, message);
9883 /* Get type and name of user who sent a message identified by ID.
9884 * @context is session context
9885 * @message_id is message identifier
9886 * @sender_type is pointer to automatically allocated type of sender detected
9887 * from @raw_sender_type string. If @raw_sender_type is unknown to this
9888 * library or to the server, NULL will be returned. Pass NULL if you don't
9889 * care about it.
9890 * @raw_sender_type is automatically reallocated UTF-8 string describing
9891 * sender type or NULL if not known to server. Pass NULL if you don't care.
9892 * @sender_name is automatically reallocated UTF-8 name of user who sent the
9893 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
9894 isds_error isds_get_message_sender(struct isds_ctx *context,
9895 const char *message_id, isds_sender_type **sender_type,
9896 char **raw_sender_type, char **sender_name) {
9897 isds_error err = IE_SUCCESS;
9898 #if HAVE_LIBCURL
9899 xmlDocPtr response = NULL;
9900 xmlChar *code = NULL, *status_message = NULL;
9901 xmlXPathContextPtr xpath_ctx = NULL;
9902 xmlXPathObjectPtr result = NULL;
9903 char *type_string = NULL;
9904 #endif
9906 if (!context) return IE_INVALID_CONTEXT;
9907 zfree(context->long_message);
9908 if (sender_type) zfree(*sender_type);
9909 if (raw_sender_type) zfree(*raw_sender_type);
9910 if (sender_name) zfree(*sender_name);
9911 if (!message_id) return IE_INVAL;
9913 #if HAVE_LIBCURL
9914 /* Do request and check for success */
9915 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9916 BAD_CAST "GetMessageAuthor",
9917 message_id, &response, NULL, NULL, &code, &status_message);
9918 if (err) goto leave;
9920 /* Extract data */
9921 xpath_ctx = xmlXPathNewContext(response);
9922 if (!xpath_ctx) {
9923 err = IE_ERROR;
9924 goto leave;
9926 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9927 err = IE_ERROR;
9928 goto leave;
9930 result = xmlXPathEvalExpression(
9931 BAD_CAST "/isds:GetMessageAuthorResponse", xpath_ctx);
9932 if (!result) {
9933 err = IE_ERROR;
9934 goto leave;
9936 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9937 isds_log_message(context,
9938 _("Missing GetMessageAuthorResponse element"));
9939 err = IE_ISDS;
9940 goto leave;
9942 if (result->nodesetval->nodeNr > 1) {
9943 isds_log_message(context,
9944 _("Multiple GetMessageAuthorResponse element"));
9945 err = IE_ISDS;
9946 goto leave;
9948 xpath_ctx->node = result->nodesetval->nodeTab[0];
9949 xmlXPathFreeObject(result); result = NULL;
9951 /* Fill output arguments in */
9952 EXTRACT_STRING("isds:userType", type_string);
9953 if (type_string) {
9954 *sender_type = calloc(1, sizeof(**sender_type));
9955 if (!*sender_type) {
9956 err = IE_NOMEM;
9957 goto leave;
9960 if (sender_type) {
9961 err = string2isds_sender_type((xmlChar *)type_string,
9962 *sender_type);
9963 if (err) {
9964 zfree(*sender_type);
9965 if (err == IE_ENUM) {
9966 err = IE_SUCCESS;
9967 char *type_string_locale = _isds_utf82locale(type_string);
9968 isds_log(ILF_ISDS, ILL_WARNING,
9969 _("Unknown isds:userType value: %s"),
9970 type_string_locale);
9971 free(type_string_locale);
9976 if (sender_type)
9977 EXTRACT_STRING("isds:authorName", *sender_name);
9979 leave:
9980 if (err) {
9981 if (sender_type) zfree(*sender_type);
9982 zfree(type_string);
9983 if (sender_name) zfree(*sender_name);
9985 if (raw_sender_type) *raw_sender_type = type_string;
9987 xmlXPathFreeObject(result);
9988 xmlXPathFreeContext(xpath_ctx);
9990 free(code);
9991 free(status_message);
9992 xmlFreeDoc(response);
9994 if (!err)
9995 isds_log(ILF_ISDS, ILL_DEBUG,
9996 _("GetMessageAuthor request processed by server "
9997 "successfully.\n"));
9998 #else /* not HAVE_LIBCURL */
9999 err = IE_NOTSUP;
10000 #endif
10001 return err;
10005 /* Retrieve hash of message identified by ID stored in ISDS.
10006 * @context is session context
10007 * @message_id is message identifier
10008 * @hash is automatically reallocated message hash downloaded from ISDS.
10009 * Message must exist in system and must not be deleted. */
10010 isds_error isds_download_message_hash(struct isds_ctx *context,
10011 const char *message_id, struct isds_hash **hash) {
10013 isds_error err = IE_SUCCESS;
10014 #if HAVE_LIBCURL
10015 xmlDocPtr response = NULL;
10016 xmlChar *code = NULL, *status_message = NULL;
10017 xmlXPathContextPtr xpath_ctx = NULL;
10018 xmlXPathObjectPtr result = NULL;
10019 #endif
10021 if (!context) return IE_INVALID_CONTEXT;
10022 zfree(context->long_message);
10024 isds_hash_free(hash);
10026 #if HAVE_LIBCURL
10027 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10028 BAD_CAST "VerifyMessage", message_id,
10029 &response, NULL, NULL, &code, &status_message);
10030 if (err) goto leave;
10033 /* Extract data */
10034 xpath_ctx = xmlXPathNewContext(response);
10035 if (!xpath_ctx) {
10036 err = IE_ERROR;
10037 goto leave;
10039 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10040 err = IE_ERROR;
10041 goto leave;
10043 result = xmlXPathEvalExpression(
10044 BAD_CAST "/isds:VerifyMessageResponse",
10045 xpath_ctx);
10046 if (!result) {
10047 err = IE_ERROR;
10048 goto leave;
10050 /* Empty response */
10051 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10052 char *message_id_locale = _isds_utf82locale((char*) message_id);
10053 isds_printf_message(context,
10054 _("Server did not return any response for ID `%s' "
10055 "on VerifyMessage request"), message_id_locale);
10056 free(message_id_locale);
10057 err = IE_ISDS;
10058 goto leave;
10060 /* More responses */
10061 if (result->nodesetval->nodeNr > 1) {
10062 char *message_id_locale = _isds_utf82locale((char*) message_id);
10063 isds_printf_message(context,
10064 _("Server did return more responses for ID `%s' "
10065 "on VerifyMessage request"), message_id_locale);
10066 free(message_id_locale);
10067 err = IE_ISDS;
10068 goto leave;
10070 /* One response */
10071 xpath_ctx->node = result->nodesetval->nodeTab[0];
10073 /* Extract the hash */
10074 err = find_and_extract_DmHash(context, hash, xpath_ctx);
10076 leave:
10077 if (err) {
10078 isds_hash_free(hash);
10081 xmlXPathFreeObject(result);
10082 xmlXPathFreeContext(xpath_ctx);
10084 free(code);
10085 free(status_message);
10086 xmlFreeDoc(response);
10088 if (!err)
10089 isds_log(ILF_ISDS, ILL_DEBUG,
10090 _("VerifyMessage request processed by server "
10091 "successfully.\n")
10093 #else /* not HAVE_LIBCURL */
10094 err = IE_NOTSUP;
10095 #endif
10096 return err;
10100 /* Erase message specified by @message_id from long term storage. Other
10101 * message cannot be erased on user request.
10102 * @context is session context
10103 * @message_id is message identifier.
10104 * @incoming is true for incoming message, false for outgoing message.
10105 * @return
10106 * IE_SUCCESS if message has ben removed
10107 * IE_INVAL if message does not exist in long term storage or message
10108 * belongs to different box
10109 * TODO: IE_NOEPRM if user has no permission to erase a message */
10110 isds_error isds_delete_message_from_storage(struct isds_ctx *context,
10111 const char *message_id, _Bool incoming) {
10112 isds_error err = IE_SUCCESS;
10113 #if HAVE_LIBCURL
10114 xmlNodePtr request = NULL, node;
10115 xmlNsPtr isds_ns = NULL;
10116 xmlDocPtr response = NULL;
10117 xmlChar *code = NULL, *status_message = NULL;
10118 #endif
10120 if (!context) return IE_INVALID_CONTEXT;
10121 zfree(context->long_message);
10122 if (NULL == message_id) return IE_INVAL;
10124 /* Check if connection is established
10125 * TODO: This check should be done downstairs. */
10126 if (!context->curl) return IE_CONNECTION_CLOSED;
10128 #if HAVE_LIBCURL
10129 /* Build request */
10130 request = xmlNewNode(NULL, BAD_CAST "EraseMessage");
10131 if (!request) {
10132 isds_log_message(context,
10133 _("Could build EraseMessage request"));
10134 return IE_ERROR;
10136 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
10137 if(!isds_ns) {
10138 isds_log_message(context, _("Could not create ISDS name space"));
10139 xmlFreeNode(request);
10140 return IE_ERROR;
10142 xmlSetNs(request, isds_ns);
10144 err = validate_message_id_length(context, (xmlChar *) message_id);
10145 if (err) goto leave;
10146 INSERT_STRING(request, "dmID", message_id);
10148 INSERT_SCALAR_BOOLEAN(request, "dmIncoming", incoming);
10151 /* Send request */
10152 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending EraseMessage request for "
10153 "message ID %s to ISDS\n"), message_id);
10154 err = isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
10155 xmlFreeNode(request); request = NULL;
10157 if (err) {
10158 isds_log(ILF_ISDS, ILL_DEBUG,
10159 _("Processing ISDS response on EraseMessage request "
10160 "failed\n"));
10161 goto leave;
10164 /* Check for response status */
10165 err = isds_response_status(context, SERVICE_DM_INFO, response,
10166 &code, &status_message, NULL);
10167 if (err) {
10168 isds_log(ILF_ISDS, ILL_DEBUG,
10169 _("ISDS response on EraseMessage request is missing "
10170 "status\n"));
10171 goto leave;
10174 /* Check server status code */
10175 if (!xmlStrcmp(code, BAD_CAST "1211")) {
10176 isds_log_message(context, _("Message to erase belongs to other box"));
10177 err = IE_INVAL;
10178 } else if (!xmlStrcmp(code, BAD_CAST "1219")) {
10179 isds_log_message(context, _("Message to erase is not saved in "
10180 "long term storage or the direction does not match"));
10181 err = IE_INVAL;
10182 } else if (xmlStrcmp(code, BAD_CAST "0000")) {
10183 char *code_locale = _isds_utf82locale((char*) code);
10184 char *message_locale = _isds_utf82locale((char*) status_message);
10185 isds_log(ILF_ISDS, ILL_DEBUG,
10186 _("Server refused EraseMessage request "
10187 "(code=%s, message=%s)\n"),
10188 code_locale, message_locale);
10189 isds_log_message(context, message_locale);
10190 free(code_locale);
10191 free(message_locale);
10192 err = IE_ISDS;
10193 goto leave;
10196 leave:
10197 free(code);
10198 free(status_message);
10199 xmlFreeDoc(response);
10200 xmlFreeNode(request);
10202 if (!err)
10203 isds_log(ILF_ISDS, ILL_DEBUG,
10204 _("EraseMessage request processed by server "
10205 "successfully.\n")
10207 #else /* not HAVE_LIBCURL */
10208 err = IE_NOTSUP;
10209 #endif
10210 return err;
10214 /* Mark message as read. This is a transactional commit function to acknowledge
10215 * to ISDS the message has been downloaded and processed by client properly.
10216 * @context is session context
10217 * @message_id is message identifier. */
10218 isds_error isds_mark_message_read(struct isds_ctx *context,
10219 const char *message_id) {
10221 isds_error err = IE_SUCCESS;
10222 #if HAVE_LIBCURL
10223 xmlDocPtr response = NULL;
10224 xmlChar *code = NULL, *status_message = NULL;
10225 #endif
10227 if (!context) return IE_INVALID_CONTEXT;
10228 zfree(context->long_message);
10230 #if HAVE_LIBCURL
10231 /* Do request and check for success */
10232 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10233 BAD_CAST "MarkMessageAsDownloaded", message_id,
10234 &response, NULL, NULL, &code, &status_message);
10236 free(code);
10237 free(status_message);
10238 xmlFreeDoc(response);
10240 if (!err)
10241 isds_log(ILF_ISDS, ILL_DEBUG,
10242 _("MarkMessageAsDownloaded request processed by server "
10243 "successfully.\n")
10245 #else /* not HAVE_LIBCURL */
10246 err = IE_NOTSUP;
10247 #endif
10248 return err;
10252 /* Mark message as received by recipient. This is applicable only to
10253 * commercial message. Use envelope->dmType message member to distinguish
10254 * commercial message from government message. Government message is
10255 * received automatically (by law), commercial message on recipient request.
10256 * @context is session context
10257 * @message_id is message identifier. */
10258 isds_error isds_mark_message_received(struct isds_ctx *context,
10259 const char *message_id) {
10261 isds_error err = IE_SUCCESS;
10262 #if HAVE_LIBCURL
10263 xmlDocPtr response = NULL;
10264 xmlChar *code = NULL, *status_message = NULL;
10265 #endif
10267 if (!context) return IE_INVALID_CONTEXT;
10268 zfree(context->long_message);
10270 #if HAVE_LIBCURL
10271 /* Do request and check for success */
10272 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10273 BAD_CAST "ConfirmDelivery", message_id,
10274 &response, NULL, NULL, &code, &status_message);
10276 free(code);
10277 free(status_message);
10278 xmlFreeDoc(response);
10280 if (!err)
10281 isds_log(ILF_ISDS, ILL_DEBUG,
10282 _("ConfirmDelivery request processed by server "
10283 "successfully.\n")
10285 #else /* not HAVE_LIBCURL */
10286 err = IE_NOTSUP;
10287 #endif
10288 return err;
10292 /* Send document for authorized conversion into Czech POINT system.
10293 * This is public anonymous service, no log-in necessary. Special context is
10294 * used to reuse keep-a-live HTTPS connection.
10295 * @context is Czech POINT session context. DO NOT use context connected to
10296 * ISDS server. Use new context or context used by this function previously.
10297 * @document is document to convert. Only data, data_length, dmFileDescr and
10298 * is_xml members are significant. Be ware that not all document formats can be
10299 * converted (signed PDF 1.3 and higher only (2010-02 state)).
10300 * @id is reallocated identifier assigned by Czech POINT system to
10301 * your document on submit. Use is to tell it to Czech POINT officer.
10302 * @date is reallocated document submit date (submitted documents
10303 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
10304 * value. */
10305 isds_error czp_convert_document(struct isds_ctx *context,
10306 const struct isds_document *document,
10307 char **id, struct tm **date) {
10308 isds_error err = IE_SUCCESS;
10309 #if HAVE_LIBCURL
10310 xmlNsPtr deposit_ns = NULL, empty_ns = NULL;
10311 xmlNodePtr request = NULL, node;
10312 xmlDocPtr response = NULL;
10314 xmlXPathContextPtr xpath_ctx = NULL;
10315 xmlXPathObjectPtr result = NULL;
10316 long int status = -1;
10317 long int *status_ptr = &status;
10318 char *string = NULL;
10319 #endif
10322 if (!context) return IE_INVALID_CONTEXT;
10323 zfree(context->long_message);
10324 if (!document || !id || !date) return IE_INVAL;
10326 if (document->is_xml) {
10327 isds_log_message(context,
10328 _("XML documents cannot be submitted to conversion"));
10329 return IE_NOTSUP;
10332 /* Free output arguments */
10333 zfree(*id);
10334 zfree(*date);
10336 #if HAVE_LIBCURL
10337 /* Store configuration */
10338 context->type = CTX_TYPE_CZP;
10339 free(context->url);
10340 context->url = strdup("https://www.czechpoint.cz/uschovna/services.php");
10341 if (!(context->url))
10342 return IE_NOMEM;
10344 /* Prepare CURL handle if not yet connected */
10345 if (!context->curl) {
10346 context->curl = curl_easy_init();
10347 if (!(context->curl))
10348 return IE_ERROR;
10351 /* Build conversion request */
10352 request = xmlNewNode(NULL, BAD_CAST "saveDocument");
10353 if (!request) {
10354 isds_log_message(context,
10355 _("Could not build Czech POINT conversion request"));
10356 return IE_ERROR;
10358 deposit_ns = xmlNewNs(request, BAD_CAST DEPOSIT_NS, BAD_CAST "dep");
10359 if(!deposit_ns) {
10360 isds_log_message(context,
10361 _("Could not create Czech POINT deposit name space"));
10362 xmlFreeNode(request);
10363 return IE_ERROR;
10365 xmlSetNs(request, deposit_ns);
10367 /* Insert children. They are in empty namespace! */
10368 empty_ns = xmlNewNs(request, BAD_CAST "", NULL);
10369 if(!empty_ns) {
10370 isds_log_message(context, _("Could not create empty name space"));
10371 err = IE_ERROR;
10372 goto leave;
10374 INSERT_STRING_WITH_NS(request, empty_ns, "conversionID", "0");
10375 INSERT_STRING_WITH_NS(request, empty_ns, "fileName",
10376 document->dmFileDescr);
10378 /* Document encoded in Base64 */
10379 err = insert_base64_encoded_string(context, request, empty_ns, "document",
10380 document->data, document->data_length);
10381 if (err) goto leave;
10383 isds_log(ILF_ISDS, ILL_DEBUG,
10384 _("Submitting document for conversion into Czech POINT deposit"));
10386 /* Send conversion request */
10387 err = _czp_czpdeposit(context, request, &response);
10388 xmlFreeNode(request); request = NULL;
10390 if (err) {
10391 czp_do_close_connection(context);
10392 goto leave;
10396 /* Extract response */
10397 xpath_ctx = xmlXPathNewContext(response);
10398 if (!xpath_ctx) {
10399 err = IE_ERROR;
10400 goto leave;
10402 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10403 err = IE_ERROR;
10404 goto leave;
10406 result = xmlXPathEvalExpression(
10407 BAD_CAST "/deposit:saveDocumentResponse/return",
10408 xpath_ctx);
10409 if (!result) {
10410 err = IE_ERROR;
10411 goto leave;
10413 /* Empty response */
10414 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10415 isds_printf_message(context,
10416 _("Missing `return' element in Czech POINT deposit response"));
10417 err = IE_ISDS;
10418 goto leave;
10420 /* More responses */
10421 if (result->nodesetval->nodeNr > 1) {
10422 isds_printf_message(context,
10423 _("Multiple `return' element in Czech POINT deposit response"));
10424 err = IE_ISDS;
10425 goto leave;
10427 /* One response */
10428 xpath_ctx->node = result->nodesetval->nodeTab[0];
10430 /* Get status */
10431 EXTRACT_LONGINT("status", status_ptr, 1);
10432 if (status) {
10433 EXTRACT_STRING("statusMsg", string);
10434 char *string_locale = _isds_utf82locale(string);
10435 isds_printf_message(context,
10436 _("Czech POINT deposit refused document for conversion "
10437 "(code=%ld, message=%s)"),
10438 status, string_locale);
10439 free(string_locale);
10440 err = IE_ISDS;
10441 goto leave;
10444 /* Get document ID */
10445 EXTRACT_STRING("documentID", *id);
10447 /* Get submit date */
10448 EXTRACT_STRING("dateInserted", string);
10449 if (string) {
10450 *date = calloc(1, sizeof(**date));
10451 if (!*date) {
10452 err = IE_NOMEM;
10453 goto leave;
10455 err = datestring2tm((xmlChar *)string, *date);
10456 if (err) {
10457 if (err == IE_NOTSUP) {
10458 err = IE_ISDS;
10459 char *string_locale = _isds_utf82locale(string);
10460 isds_printf_message(context,
10461 _("Invalid dateInserted value: %s"), string_locale);
10462 free(string_locale);
10464 goto leave;
10468 leave:
10469 free(string);
10470 xmlXPathFreeObject(result);
10471 xmlXPathFreeContext(xpath_ctx);
10473 xmlFreeDoc(response);
10474 xmlFreeNode(request);
10476 if (!err) {
10477 char *id_locale = _isds_utf82locale((char *) *id);
10478 isds_log(ILF_ISDS, ILL_DEBUG,
10479 _("Document %s has been submitted for conversion "
10480 "to server successfully\n"), id_locale);
10481 free(id_locale);
10483 #else /* not HAVE_LIBCURL */
10484 err = IE_NOTSUP;
10485 #endif
10486 return err;
10490 /* Close possibly opened connection to Czech POINT document deposit.
10491 * @context is Czech POINT session context. */
10492 isds_error czp_close_connection(struct isds_ctx *context) {
10493 if (!context) return IE_INVALID_CONTEXT;
10494 zfree(context->long_message);
10495 #if HAVE_LIBCURL
10496 return czp_do_close_connection(context);
10497 #else
10498 return IE_NOTSUP;
10499 #endif
10503 /* Send request for new box creation in testing ISDS instance.
10504 * It's not possible to request for a production box currently, as it
10505 * communicates via e-mail.
10506 * XXX: This function does not work either. Server complains about invalid
10507 * e-mail address.
10508 * XXX: Remove context->type hacks in isds.c and validator.c when removing
10509 * this function
10510 * @context is special session context for box creation request. DO NOT use
10511 * standard context as it could reveal your password. Use fresh new context or
10512 * context previously used by this function.
10513 * @box is box description to create including single primary user (in case of
10514 * FO box type). It outputs box ID assigned by ISDS in dbID element.
10515 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
10516 * box, or contact address of PFO box owner). The email member is mandatory as
10517 * it will be used to deliver credentials.
10518 * @former_names is former name of box owner. Pass NULL if you don't care.
10519 * @approval is optional external approval of box manipulation
10520 * @refnumber is reallocated serial number of request assigned by ISDS. Use
10521 * NULL, if you don't care.*/
10522 isds_error isds_request_new_testing_box(struct isds_ctx *context,
10523 struct isds_DbOwnerInfo *box, const struct isds_list *users,
10524 const char *former_names, const struct isds_approval *approval,
10525 char **refnumber) {
10526 isds_error err = IE_SUCCESS;
10527 #if HAVE_LIBCURL
10528 xmlNodePtr request = NULL;
10529 xmlDocPtr response = NULL;
10530 xmlXPathContextPtr xpath_ctx = NULL;
10531 xmlXPathObjectPtr result = NULL;
10532 #endif
10535 if (!context) return IE_INVALID_CONTEXT;
10536 zfree(context->long_message);
10537 if (!box) return IE_INVAL;
10539 #if HAVE_LIBCURL
10540 if (!box->email || box->email[0] == '\0') {
10541 isds_log_message(context, _("E-mail field is mandatory"));
10542 return IE_INVAL;
10545 /* Scratch box ID */
10546 zfree(box->dbID);
10548 /* Store configuration */
10549 context->type = CTX_TYPE_TESTING_REQUEST_COLLECTOR;
10550 free(context->url);
10551 context->url = strdup("http://78.102.19.203/testbox/request_box.php");
10552 if (!(context->url))
10553 return IE_NOMEM;
10555 /* Prepare CURL handle if not yet connected */
10556 if (!context->curl) {
10557 context->curl = curl_easy_init();
10558 if (!(context->curl))
10559 return IE_ERROR;
10562 /* Build CreateDataBox request */
10563 err = build_CreateDBInput_request(context,
10564 &request, BAD_CAST "CreateDataBox",
10565 box, users, (xmlChar *) former_names, NULL, NULL, NULL, approval);
10566 if (err) goto leave;
10568 /* Send it to server and process response */
10569 err = send_destroy_request_check_response(context,
10570 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
10571 &response, (xmlChar **) refnumber, NULL);
10572 if (err) goto leave;
10574 /* Extract box ID */
10575 xpath_ctx = xmlXPathNewContext(response);
10576 if (!xpath_ctx) {
10577 err = IE_ERROR;
10578 goto leave;
10580 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10581 err = IE_ERROR;
10582 goto leave;
10584 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
10586 leave:
10587 xmlXPathFreeObject(result);
10588 xmlXPathFreeContext(xpath_ctx);
10589 xmlFreeDoc(response);
10590 xmlFreeNode(request);
10592 if (!err) {
10593 isds_log(ILF_ISDS, ILL_DEBUG,
10594 _("CreateDataBox request processed by server successfully.\n"));
10596 #else /* not HAVE_LIBCURL */
10597 err = IE_NOTSUP;
10598 #endif
10600 return err;
10604 /* Submit CMS signed message to ISDS to verify its originality. This is
10605 * stronger form of isds_verify_message_hash() because ISDS does more checks
10606 * than simple one (potentialy old weak) hash comparison.
10607 * @context is session context
10608 * @message is memory with raw CMS signed message bit stream
10609 * @length is @message size in bytes
10610 * @return
10611 * IE_SUCCESS if message originates in ISDS
10612 * IE_NOTEQUAL if message is unknown to ISDS
10613 * other code for other errors */
10614 isds_error isds_authenticate_message(struct isds_ctx *context,
10615 const void *message, size_t length) {
10616 isds_error err = IE_SUCCESS;
10617 #if HAVE_LIBCURL
10618 xmlNsPtr isds_ns = NULL;
10619 xmlNodePtr request = NULL;
10620 xmlDocPtr response = NULL;
10621 xmlXPathContextPtr xpath_ctx = NULL;
10622 xmlXPathObjectPtr result = NULL;
10623 _Bool *authentic = NULL;
10624 #endif
10626 if (!context) return IE_INVALID_CONTEXT;
10627 zfree(context->long_message);
10628 if (!message || length == 0) return IE_INVAL;
10630 #if HAVE_LIBCURL
10631 /* Check if connection is established
10632 * TODO: This check should be done downstairs. */
10633 if (!context->curl) return IE_CONNECTION_CLOSED;
10636 /* Build AuthenticateMessage request */
10637 request = xmlNewNode(NULL, BAD_CAST "AuthenticateMessage");
10638 if (!request) {
10639 isds_log_message(context,
10640 _("Could not build AuthenticateMessage request"));
10641 return IE_ERROR;
10643 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
10644 if(!isds_ns) {
10645 isds_log_message(context, _("Could not create ISDS name space"));
10646 xmlFreeNode(request);
10647 return IE_ERROR;
10649 xmlSetNs(request, isds_ns);
10651 /* Insert Base64 encoded message */
10652 err = insert_base64_encoded_string(context, request, NULL, "dmMessage",
10653 message, length);
10654 if (err) goto leave;
10656 /* Send request to server and process response */
10657 err = send_destroy_request_check_response(context,
10658 SERVICE_DM_OPERATIONS, BAD_CAST "AuthenticateMessage", &request,
10659 &response, NULL, NULL);
10660 if (err) goto leave;
10663 /* ISDS has decided */
10664 xpath_ctx = xmlXPathNewContext(response);
10665 if (!xpath_ctx) {
10666 err = IE_ERROR;
10667 goto leave;
10669 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10670 err = IE_ERROR;
10671 goto leave;
10674 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic);
10676 if (!authentic) {
10677 isds_log_message(context,
10678 _("Server did not return any response on "
10679 "AuthenticateMessage request"));
10680 err = IE_ISDS;
10681 goto leave;
10683 if (*authentic) {
10684 isds_log(ILF_ISDS, ILL_DEBUG,
10685 _("ISDS authenticated the message successfully\n"));
10686 } else {
10687 isds_log_message(context, _("ISDS does not know the message"));
10688 err = IE_NOTEQUAL;
10692 leave:
10693 free(authentic);
10694 xmlXPathFreeObject(result);
10695 xmlXPathFreeContext(xpath_ctx);
10697 xmlFreeDoc(response);
10698 xmlFreeNode(request);
10699 #else /* not HAVE_LIBCURL */
10700 err = IE_NOTSUP;
10701 #endif
10703 return err;
10707 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
10708 * including adding new CMS time stamp. Only CMS blobs without time stamp can
10709 * be re-signed.
10710 * @context is session context
10711 * @input_data is memory with raw CMS signed message or delivery info bit
10712 * stream to re-sign
10713 * @input_length is @input_data size in bytes
10714 * @output_data is pointer to auto-allocated memory where to store re-signed
10715 * input data blob. Caller must free it.
10716 * @output_data is pointer where to store @output_data size in bytes
10717 * @valid_to is pointer to auto-allocated date of time stamp expiration.
10718 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
10719 * @return
10720 * IE_SUCCESS if CMS blob has been re-signed successfully
10721 * other code for other errors */
10722 isds_error isds_resign_message(struct isds_ctx *context,
10723 const void *input_data, size_t input_length,
10724 void **output_data, size_t *output_length, struct tm **valid_to) {
10725 isds_error err = IE_SUCCESS;
10726 #if HAVE_LIBCURL
10727 xmlNsPtr isds_ns = NULL;
10728 xmlNodePtr request = NULL;
10729 xmlDocPtr response = NULL;
10730 xmlXPathContextPtr xpath_ctx = NULL;
10731 xmlXPathObjectPtr result = NULL;
10732 char *string = NULL;
10733 const xmlChar *codes[] = {
10734 BAD_CAST "2200",
10735 BAD_CAST "2201",
10736 BAD_CAST "2204",
10737 BAD_CAST "2207",
10738 NULL
10740 const char *meanings[] = {
10741 "Message is bad",
10742 "Message is not original",
10743 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
10744 "Time stamp could not been generated in time"
10746 const isds_error errors[] = {
10747 IE_INVAL,
10748 IE_NOTUNIQ,
10749 IE_INVAL,
10750 IE_ISDS,
10752 struct code_map_isds_error map = {
10753 .codes = codes,
10754 .meanings = meanings,
10755 .errors = errors
10757 #endif
10759 if (NULL != output_data) *output_data = NULL;
10760 if (NULL != output_length) *output_length = 0;
10761 if (NULL != valid_to) *valid_to = NULL;
10763 if (NULL == context) return IE_INVALID_CONTEXT;
10764 zfree(context->long_message);
10765 if (NULL == input_data || 0 == input_length) {
10766 isds_log_message(context, _("Empty CMS blob on input"));
10767 return IE_INVAL;
10769 if (NULL == output_data || NULL == output_length) {
10770 isds_log_message(context,
10771 _("NULL pointer provided for output CMS blob"));
10772 return IE_INVAL;
10775 #if HAVE_LIBCURL
10776 /* Check if connection is established
10777 * TODO: This check should be done downstairs. */
10778 if (!context->curl) return IE_CONNECTION_CLOSED;
10781 /* Build Re-signISDSDocument request */
10782 request = xmlNewNode(NULL, BAD_CAST "Re-signISDSDocument");
10783 if (!request) {
10784 isds_log_message(context,
10785 _("Could not build Re-signISDSDocument request"));
10786 return IE_ERROR;
10788 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
10789 if(!isds_ns) {
10790 isds_log_message(context, _("Could not create ISDS name space"));
10791 xmlFreeNode(request);
10792 return IE_ERROR;
10794 xmlSetNs(request, isds_ns);
10796 /* Insert Base64 encoded CMS blob */
10797 err = insert_base64_encoded_string(context, request, NULL, "dmDoc",
10798 input_data, input_length);
10799 if (err) goto leave;
10801 /* Send request to server and process response */
10802 err = send_destroy_request_check_response(context,
10803 SERVICE_DM_OPERATIONS, BAD_CAST "Re-signISDSDocument", &request,
10804 &response, NULL, &map);
10805 if (err) goto leave;
10808 /* Extract re-signed data */
10809 xpath_ctx = xmlXPathNewContext(response);
10810 if (!xpath_ctx) {
10811 err = IE_ERROR;
10812 goto leave;
10814 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10815 err = IE_ERROR;
10816 goto leave;
10818 result = xmlXPathEvalExpression(
10819 BAD_CAST "/isds:Re-signISDSDocumentResponse", xpath_ctx);
10820 if (!result) {
10821 err = IE_ERROR;
10822 goto leave;
10824 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10825 isds_log_message(context,
10826 _("Missing Re-signISDSDocumentResponse element"));
10827 err = IE_ISDS;
10828 goto leave;
10830 if (result->nodesetval->nodeNr > 1) {
10831 isds_log_message(context,
10832 _("Multiple Re-signISDSDocumentResponse element"));
10833 err = IE_ISDS;
10834 goto leave;
10836 xpath_ctx->node = result->nodesetval->nodeTab[0];
10837 xmlXPathFreeObject(result); result = NULL;
10839 EXTRACT_STRING("isds:dmResultDoc", string);
10840 /* Decode non-empty data */
10841 if (NULL != string && string[0] != '\0') {
10842 *output_length = _isds_b64decode(string, output_data);
10843 if (*output_length == (size_t) -1) {
10844 isds_log_message(context,
10845 _("Error while Base64-decoding re-signed data"));
10846 err = IE_ERROR;
10847 goto leave;
10849 } else {
10850 isds_log_message(context, _("Server did not send re-signed data"));
10851 err = IE_ISDS;
10852 goto leave;
10854 zfree(string);
10856 if (NULL != valid_to) {
10857 /* Get time stamp expiration date */
10858 EXTRACT_STRING("isds:dmValidTo", string);
10859 if (NULL != string) {
10860 *valid_to = calloc(1, sizeof(**valid_to));
10861 if (!*valid_to) {
10862 err = IE_NOMEM;
10863 goto leave;
10865 err = datestring2tm((xmlChar *)string, *valid_to);
10866 if (err) {
10867 if (err == IE_NOTSUP) {
10868 err = IE_ISDS;
10869 char *string_locale = _isds_utf82locale(string);
10870 isds_printf_message(context,
10871 _("Invalid dmValidTo value: %s"), string_locale);
10872 free(string_locale);
10874 goto leave;
10879 leave:
10880 free(string);
10882 xmlXPathFreeObject(result);
10883 xmlXPathFreeContext(xpath_ctx);
10885 xmlFreeDoc(response);
10886 xmlFreeNode(request);
10887 #else /* not HAVE_LIBCURL */
10888 err = IE_NOTSUP;
10889 #endif
10891 return err;
10894 #undef INSERT_ELEMENT
10895 #undef CHECK_FOR_STRING_LENGTH
10896 #undef INSERT_STRING_ATTRIBUTE
10897 #undef INSERT_ULONGINTNOPTR
10898 #undef INSERT_ULONGINT
10899 #undef INSERT_LONGINT
10900 #undef INSERT_BOOLEAN
10901 #undef INSERT_SCALAR_BOOLEAN
10902 #undef INSERT_STRING
10903 #undef INSERT_STRING_WITH_NS
10904 #undef EXTRACT_STRING_ATTRIBUTE
10905 #undef EXTRACT_ULONGINT
10906 #undef EXTRACT_LONGINT
10907 #undef EXTRACT_BOOLEAN
10908 #undef EXTRACT_STRING
10911 /* Compute hash of message from raw representation and store it into envelope.
10912 * Original hash structure will be destroyed in envelope.
10913 * @context is session context
10914 * @message is message carrying raw XML message blob
10915 * @algorithm is desired hash algorithm to use */
10916 isds_error isds_compute_message_hash(struct isds_ctx *context,
10917 struct isds_message *message, const isds_hash_algorithm algorithm) {
10918 isds_error err = IE_SUCCESS;
10919 const char *nsuri;
10920 void *xml_stream = NULL;
10921 size_t xml_stream_length;
10922 size_t phys_start, phys_end;
10923 char *phys_path = NULL;
10924 struct isds_hash *new_hash = NULL;
10927 if (!context) return IE_INVALID_CONTEXT;
10928 zfree(context->long_message);
10929 if (!message) return IE_INVAL;
10931 if (!message->raw) {
10932 isds_log_message(context,
10933 _("Message does not carry raw representation"));
10934 return IE_INVAL;
10937 switch (message->raw_type) {
10938 case RAWTYPE_INCOMING_MESSAGE:
10939 nsuri = ISDS_NS;
10940 xml_stream = message->raw;
10941 xml_stream_length = message->raw_length;
10942 break;
10944 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
10945 nsuri = SISDS_INCOMING_NS;
10946 xml_stream = message->raw;
10947 xml_stream_length = message->raw_length;
10948 break;
10950 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
10951 nsuri = SISDS_INCOMING_NS;
10952 err = _isds_extract_cms_data(context,
10953 message->raw, message->raw_length,
10954 &xml_stream, &xml_stream_length);
10955 if (err) goto leave;
10956 break;
10958 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
10959 nsuri = SISDS_OUTGOING_NS;
10960 xml_stream = message->raw;
10961 xml_stream_length = message->raw_length;
10962 break;
10964 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
10965 nsuri = SISDS_OUTGOING_NS;
10966 err = _isds_extract_cms_data(context,
10967 message->raw, message->raw_length,
10968 &xml_stream, &xml_stream_length);
10969 if (err) goto leave;
10970 break;
10972 default:
10973 isds_log_message(context, _("Bad raw representation type"));
10974 return IE_INVAL;
10975 break;
10979 /* XXX: Hash is computed from original string representing isds:dmDm
10980 * subtree. That means no encoding, white space, xmlns attributes changes.
10981 * In other words, input for hash can be invalid XML stream. */
10982 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
10983 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
10984 PHYSXML_ELEMENT_SEPARATOR,
10985 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
10986 PHYSXML_ELEMENT_SEPARATOR
10987 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
10988 err = IE_NOMEM;
10989 goto leave;
10991 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
10992 phys_path, &phys_start, &phys_end);
10993 zfree(phys_path);
10994 if (err) {
10995 isds_log_message(context,
10996 _("Substring with isds:dmDM element could not be located "
10997 "in raw message"));
10998 goto leave;
11002 /* Compute hash */
11003 new_hash = calloc(1, sizeof(*new_hash));
11004 if (!new_hash) {
11005 err = IE_NOMEM;
11006 goto leave;
11008 new_hash->algorithm = algorithm;
11009 err = _isds_compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
11010 new_hash);
11011 if (err) {
11012 isds_log_message(context, _("Could not compute message hash"));
11013 goto leave;
11016 /* Save computed hash */
11017 if (!message->envelope) {
11018 message->envelope = calloc(1, sizeof(*message->envelope));
11019 if (!message->envelope) {
11020 err = IE_NOMEM;
11021 goto leave;
11024 isds_hash_free(&message->envelope->hash);
11025 message->envelope->hash = new_hash;
11027 leave:
11028 if (err) {
11029 isds_hash_free(&new_hash);
11032 free(phys_path);
11033 if (xml_stream != message->raw) free(xml_stream);
11034 return err;
11038 /* Compare two hashes.
11039 * @h1 is first hash
11040 * @h2 is another hash
11041 * @return
11042 * IE_SUCCESS if hashes equal
11043 * IE_NOTUNIQ if hashes are comparable, but they don't equal
11044 * IE_ENUM if not comparable, but both structures defined
11045 * IE_INVAL if some of the structures are undefined (NULL)
11046 * IE_ERROR if internal error occurs */
11047 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
11048 if (h1 == NULL || h2 == NULL) return IE_INVAL;
11049 if (h1->algorithm != h2->algorithm) return IE_ENUM;
11050 if (h1->length != h2->length) return IE_ERROR;
11051 if (h1->length > 0 && !h1->value) return IE_ERROR;
11052 if (h2->length > 0 && !h2->value) return IE_ERROR;
11054 for (int i = 0; i < h1->length; i++) {
11055 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
11056 return IE_NOTEQUAL;
11058 return IE_SUCCESS;
11062 /* Check message has gone through ISDS by comparing message hash stored in
11063 * ISDS and locally computed hash. You must provide message with valid raw
11064 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
11065 * This is convenient wrapper for isds_download_message_hash(),
11066 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
11067 * @context is session context
11068 * @message is message with valid raw and envelope member; envelope->hash
11069 * member will be changed during function run. Use envelope on heap only.
11070 * @return
11071 * IE_SUCCESS if message originates in ISDS
11072 * IE_NOTEQUAL if message is unknown to ISDS
11073 * other code for other errors */
11074 isds_error isds_verify_message_hash(struct isds_ctx *context,
11075 struct isds_message *message) {
11076 isds_error err = IE_SUCCESS;
11077 struct isds_hash *downloaded_hash = NULL;
11079 if (!context) return IE_INVALID_CONTEXT;
11080 zfree(context->long_message);
11081 if (!message) return IE_INVAL;
11083 if (!message->envelope) {
11084 isds_log_message(context,
11085 _("Given message structure is missing envelope"));
11086 return IE_INVAL;
11088 if (!message->raw) {
11089 isds_log_message(context,
11090 _("Given message structure is missing raw representation"));
11091 return IE_INVAL;
11094 err = isds_download_message_hash(context, message->envelope->dmID,
11095 &downloaded_hash);
11096 if (err) goto leave;
11098 err = isds_compute_message_hash(context, message,
11099 downloaded_hash->algorithm);
11100 if (err) goto leave;
11102 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
11104 leave:
11105 isds_hash_free(&downloaded_hash);
11106 return err;
11110 /* Search for document by document ID in list of documents. IDs are compared
11111 * as UTF-8 string.
11112 * @documents is list of isds_documents
11113 * @id is document identifier
11114 * @return first matching document or NULL. */
11115 const struct isds_document *isds_find_document_by_id(
11116 const struct isds_list *documents, const char *id) {
11117 const struct isds_list *item;
11118 const struct isds_document *document;
11120 for (item = documents; item; item = item->next) {
11121 document = (struct isds_document *) item->data;
11122 if (!document) continue;
11124 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
11125 return document;
11128 return NULL;
11132 /* Normalize @mime_type to be proper MIME type.
11133 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
11134 * guess regular MIME type (e.g. "application/pdf").
11135 * @mime_type is UTF-8 encoded MIME type to fix
11136 * @return original @mime_type if no better interpretation exists, or
11137 * constant static UTF-8 encoded string with proper MIME type. */
11138 const char *isds_normalize_mime_type(const char *mime_type) {
11139 if (!mime_type) return NULL;
11141 for (int offset = 0;
11142 offset < sizeof(extension_map_mime)/sizeof(extension_map_mime[0]);
11143 offset += 2) {
11144 if (!xmlStrcmp((const xmlChar*) mime_type, extension_map_mime[offset]))
11145 return (const char *) extension_map_mime[offset + 1];
11148 return mime_type;
11152 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
11153 struct isds_message **message);
11154 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
11155 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
11156 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
11157 struct isds_address **address);
11159 int isds_message_free(struct isds_message **message);
11160 int isds_address_free(struct isds_address **address);
11164 /* Makes known all relevant namespaces to given XPath context
11165 * @xpath_ctx is XPath context
11166 * @message_ns selects proper message name space. Unsigned and signed
11167 * messages and delivery info's differ in prefix and URI. */
11168 _hidden isds_error _isds_register_namespaces(xmlXPathContextPtr xpath_ctx,
11169 const message_ns_type message_ns) {
11170 const xmlChar *message_namespace = NULL;
11172 if (!xpath_ctx) return IE_ERROR;
11174 switch(message_ns) {
11175 case MESSAGE_NS_1:
11176 message_namespace = BAD_CAST ISDS1_NS; break;
11177 case MESSAGE_NS_UNSIGNED:
11178 message_namespace = BAD_CAST ISDS_NS; break;
11179 case MESSAGE_NS_SIGNED_INCOMING:
11180 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
11181 case MESSAGE_NS_SIGNED_OUTGOING:
11182 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
11183 case MESSAGE_NS_SIGNED_DELIVERY:
11184 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
11185 default:
11186 return IE_ENUM;
11189 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
11190 return IE_ERROR;
11191 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
11192 return IE_ERROR;
11193 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "oisds", BAD_CAST OISDS_NS))
11194 return IE_ERROR;
11195 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
11196 return IE_ERROR;
11197 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
11198 return IE_ERROR;
11199 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
11200 return IE_ERROR;
11201 return IE_SUCCESS;