test: Move server to simline subdirectory
[libisds.git] / src / isds.c
blob9ffa552e0afd48d053d9ea89463e70efdb8cc62a
1 #define _XOPEN_SOURCE 500 /* strdup from string.h, strptime from time.h */
2 #include <stdlib.h>
3 #include <string.h>
4 #include <stdio.h>
5 #include <stdarg.h>
6 #include <ctype.h>
7 #include <stdint.h>
8 #include <limits.h> /* Because of LONG_{MIN,MAX} constants */
9 #include "isds_priv.h"
10 #include "utils.h"
11 #if HAVE_LIBCURL
12 #include "soap.h"
13 #endif
14 #include "validator.h"
15 #include "crypto.h"
16 #include <gpg-error.h> /* Because of ksba or gpgme */
17 #include "physxml.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/";
24 /* Base URL of production ISDS instance */
25 const char isds_testing_locator[] = "https://ws1.czebox.cz/";
26 const char isds_cert_testing_locator[] = "https://ws1c.czebox.cz/";
28 /* Extension to MIME type map */
29 static xmlChar *extension_map_mime[] = {
30 BAD_CAST "cer", BAD_CAST "application/x-x509-ca-cert",
31 BAD_CAST "crt", BAD_CAST "application/x-x509-ca-cert",
32 BAD_CAST "der", BAD_CAST "application/x-x509-ca-cert",
33 BAD_CAST "doc", BAD_CAST "application/msword",
34 BAD_CAST "docx", BAD_CAST "application/vnd.openxmlformats-officedocument."
35 "wordprocessingml.document",
36 BAD_CAST "dbf", BAD_CAST "application/octet-stream",
37 BAD_CAST "prj", BAD_CAST "application/octet-stream",
38 BAD_CAST "qix", BAD_CAST "application/octet-stream",
39 BAD_CAST "sbn", BAD_CAST "application/octet-stream",
40 BAD_CAST "sbx", BAD_CAST "application/octet-stream",
41 BAD_CAST "shp", BAD_CAST "application/octet-stream",
42 BAD_CAST "shx", BAD_CAST "application/octet-stream",
43 BAD_CAST "dgn", BAD_CAST "application/octet-stream",
44 BAD_CAST "dwg", BAD_CAST "image/vnd.dwg",
45 BAD_CAST "edi", BAD_CAST "application/edifact",
46 BAD_CAST "fo", BAD_CAST "application/vnd.software602.filler.form+xml",
47 BAD_CAST "gfs", BAD_CAST "application/xml",
48 BAD_CAST "gml", BAD_CAST "application/xml",
49 BAD_CAST "gif", BAD_CAST "image/gif",
50 BAD_CAST "htm", BAD_CAST "text/html",
51 BAD_CAST "html", BAD_CAST "text/html",
52 BAD_CAST "isdoc", BAD_CAST "text/isdoc",
53 BAD_CAST "isdocx", BAD_CAST "text/isdocx",
54 BAD_CAST "jfif", BAD_CAST "image/jpeg",
55 BAD_CAST "jpg", BAD_CAST "image/jpeg",
56 BAD_CAST "jpeg", BAD_CAST "image/jpeg",
57 BAD_CAST "mpeg", BAD_CAST "video/mpeg",
58 BAD_CAST "mpeg1", BAD_CAST "video/mpeg",
59 BAD_CAST "mpeg2", BAD_CAST "video/mpeg",
60 BAD_CAST "mpg", BAD_CAST "video/mpeg",
61 BAD_CAST "mp2", BAD_CAST "audio/mpeg",
62 BAD_CAST "mp3", BAD_CAST "audio/mpeg",
63 BAD_CAST "odp", BAD_CAST "application/vnd.oasis.opendocument.presentation",
64 BAD_CAST "ods", BAD_CAST "application/vnd.oasis.opendocument.spreadsheet",
65 BAD_CAST "odt", BAD_CAST "application/vnd.oasis.opendocument.text",
66 BAD_CAST "pdf", BAD_CAST "application/pdf",
67 BAD_CAST "p7b", BAD_CAST "application/pkcs7-certificates",
68 BAD_CAST "p7c", BAD_CAST "application/pkcs7-mime",
69 BAD_CAST "p7m", BAD_CAST "application/pkcs7-mime",
70 BAD_CAST "p7f", BAD_CAST "application/pkcs7-signature",
71 BAD_CAST "p7s", BAD_CAST "application/pkcs7-signature",
72 BAD_CAST "pk7", BAD_CAST "application/pkcs7-mime",
73 BAD_CAST "png", BAD_CAST "image/png",
74 BAD_CAST "ppt", BAD_CAST "application/vnd.ms-powerpoint",
75 BAD_CAST "pptx", BAD_CAST "application/vnd.openxmlformats-officedocument."
76 "presentationml.presentation",
77 BAD_CAST "rtf", BAD_CAST "application/rtf",
78 BAD_CAST "tif", BAD_CAST "image/tiff",
79 BAD_CAST "tiff", BAD_CAST "image/tiff",
80 BAD_CAST "tsr", BAD_CAST "application/timestamp-reply",
81 BAD_CAST "tst", BAD_CAST "application/timestamp-reply",
82 BAD_CAST "txt", BAD_CAST "text/plain",
83 BAD_CAST "wav", BAD_CAST "audio/wav",
84 BAD_CAST "xls", BAD_CAST "application/vnd.ms-excel",
85 BAD_CAST "xlsx", BAD_CAST "application/vnd.openxmlformats-officedocument."
86 "spreadsheetml.sheet",
87 BAD_CAST "xml", BAD_CAST "application/xml",
88 BAD_CAST "xsd", BAD_CAST "application/xml",
89 BAD_CAST "zfo", BAD_CAST "application/vnd.software602.filler.form-xml-zip"
92 /* Deallocate structure isds_pki_credentials and NULL it.
93 * Pass-phrase is discarded.
94 * @pki credentials to to free */
95 void isds_pki_credentials_free(struct isds_pki_credentials **pki) {
96 if(!pki || !*pki) return;
98 free((*pki)->engine);
99 free((*pki)->certificate);
100 free((*pki)->key);
102 if ((*pki)->passphrase) {
103 memset((*pki)->passphrase, 0, strlen((*pki)->passphrase));
104 free((*pki)->passphrase);
107 zfree((*pki));
111 /* Free isds_list with all member data.
112 * @list list to free, on return will be NULL */
113 void isds_list_free(struct isds_list **list) {
114 struct isds_list *item, *next_item;
116 if (!list || !*list) return;
118 for(item = *list; item; item = next_item) {
119 if (item->destructor) (item->destructor)(&(item->data));
120 next_item = item->next;
121 free(item);
124 *list = NULL;
128 /* Deallocate structure isds_hash and NULL it.
129 * @hash hash to to free */
130 void isds_hash_free(struct isds_hash **hash) {
131 if(!hash || !*hash) return;
132 free((*hash)->value);
133 zfree((*hash));
137 /* Deallocate structure isds_PersonName recursively and NULL it */
138 static void isds_PersonName_free(struct isds_PersonName **person_name) {
139 if (!person_name || !*person_name) return;
141 free((*person_name)->pnFirstName);
142 free((*person_name)->pnMiddleName);
143 free((*person_name)->pnLastName);
144 free((*person_name)->pnLastNameAtBirth);
146 free(*person_name);
147 *person_name = NULL;
151 /* Deallocate structure isds_BirthInfo recursively and NULL it */
152 static void isds_BirthInfo_free(struct isds_BirthInfo **birth_info) {
153 if (!birth_info || !*birth_info) return;
155 free((*birth_info)->biDate);
156 free((*birth_info)->biCity);
157 free((*birth_info)->biCounty);
158 free((*birth_info)->biState);
160 free(*birth_info);
161 *birth_info = NULL;
165 /* Deallocate structure isds_Address recursively and NULL it */
166 static void isds_Address_free(struct isds_Address **address) {
167 if (!address || !*address) return;
169 free((*address)->adCity);
170 free((*address)->adStreet);
171 free((*address)->adNumberInStreet);
172 free((*address)->adNumberInMunicipality);
173 free((*address)->adZipCode);
174 free((*address)->adState);
176 free(*address);
177 *address = NULL;
181 /* Deallocate structure isds_DbOwnerInfo recursively and NULL it */
182 void isds_DbOwnerInfo_free(struct isds_DbOwnerInfo **db_owner_info) {
183 if (!db_owner_info || !*db_owner_info) return;
185 free((*db_owner_info)->dbID);
186 free((*db_owner_info)->dbType);
187 free((*db_owner_info)->ic);
188 isds_PersonName_free(&((*db_owner_info)->personName));
189 free((*db_owner_info)->firmName);
190 isds_BirthInfo_free(&((*db_owner_info)->birthInfo));
191 isds_Address_free(&((*db_owner_info)->address));
192 free((*db_owner_info)->nationality);
193 free((*db_owner_info)->email);
194 free((*db_owner_info)->telNumber);
195 free((*db_owner_info)->identifier);
196 free((*db_owner_info)->registryCode);
197 free((*db_owner_info)->dbState);
198 free((*db_owner_info)->dbEffectiveOVM);
200 free(*db_owner_info);
201 *db_owner_info = NULL;
204 /* Deallocate structure isds_DbUserInfo recursively and NULL it */
205 void isds_DbUserInfo_free(struct isds_DbUserInfo **db_user_info) {
206 if (!db_user_info || !*db_user_info) return;
208 free((*db_user_info)->userID);
209 free((*db_user_info)->userType);
210 free((*db_user_info)->userPrivils);
211 isds_PersonName_free(&((*db_user_info)->personName));
212 isds_Address_free(&((*db_user_info)->address));
213 free((*db_user_info)->biDate);
214 free((*db_user_info)->ic);
215 free((*db_user_info)->firmName);
216 free((*db_user_info)->caStreet);
217 free((*db_user_info)->caCity);
218 free((*db_user_info)->caZipCode);
219 free((*db_user_info)->caState);
221 zfree(*db_user_info);
225 /* Deallocate struct isds_event recursively and NULL it */
226 void isds_event_free(struct isds_event **event) {
227 if (!event || !*event) return;
229 free((*event)->time);
230 free((*event)->type);
231 free((*event)->description);
232 zfree(*event);
236 /* Deallocate struct isds_envelope recursively and NULL it */
237 void isds_envelope_free(struct isds_envelope **envelope) {
238 if (!envelope || !*envelope) return;
240 free((*envelope)->dmID);
241 free((*envelope)->dbIDSender);
242 free((*envelope)->dmSender);
243 free((*envelope)->dmSenderAddress);
244 free((*envelope)->dmSenderType);
245 free((*envelope)->dmRecipient);
246 free((*envelope)->dmRecipientAddress);
247 free((*envelope)->dmAmbiguousRecipient);
248 free((*envelope)->dmType);
250 free((*envelope)->dmOrdinal);
251 free((*envelope)->dmMessageStatus);
252 free((*envelope)->dmDeliveryTime);
253 free((*envelope)->dmAcceptanceTime);
254 isds_hash_free(&(*envelope)->hash);
255 free((*envelope)->timestamp);
256 isds_list_free(&(*envelope)->events);
258 free((*envelope)->dmSenderOrgUnit);
259 free((*envelope)->dmSenderOrgUnitNum);
260 free((*envelope)->dbIDRecipient);
261 free((*envelope)->dmRecipientOrgUnit);
262 free((*envelope)->dmRecipientOrgUnitNum);
263 free((*envelope)->dmToHands);
264 free((*envelope)->dmAnnotation);
265 free((*envelope)->dmRecipientRefNumber);
266 free((*envelope)->dmSenderRefNumber);
267 free((*envelope)->dmRecipientIdent);
268 free((*envelope)->dmSenderIdent);
270 free((*envelope)->dmLegalTitleLaw);
271 free((*envelope)->dmLegalTitleYear);
272 free((*envelope)->dmLegalTitleSect);
273 free((*envelope)->dmLegalTitlePar);
274 free((*envelope)->dmLegalTitlePoint);
276 free((*envelope)->dmPersonalDelivery);
277 free((*envelope)->dmAllowSubstDelivery);
279 free((*envelope)->dmOVM);
280 free((*envelope)->dmPublishOwnID);
282 free(*envelope);
283 *envelope = NULL;
287 /* Deallocate struct isds_message recursively and NULL it */
288 void isds_message_free(struct isds_message **message) {
289 if (!message || !*message) return;
291 free((*message)->raw);
292 isds_envelope_free(&((*message)->envelope));
293 isds_list_free(&((*message)->documents));
294 xmlFreeDoc((*message)->xml); (*message)->xml = NULL;
296 free(*message);
297 *message = NULL;
301 /* Deallocate struct isds_document recursively and NULL it */
302 void isds_document_free(struct isds_document **document) {
303 if (!document || !*document) return;
305 if (!(*document)->is_xml) {
306 free((*document)->data);
308 free((*document)->dmMimeType);
309 free((*document)->dmFileGuid);
310 free((*document)->dmUpFileGuid);
311 free((*document)->dmFileDescr);
312 free((*document)->dmFormat);
314 free(*document);
315 *document = NULL;
319 /* Deallocate struct isds_message_copy recursively and NULL it */
320 void isds_message_copy_free(struct isds_message_copy **copy) {
321 if (!copy || !*copy) return;
323 free((*copy)->dbIDRecipient);
324 free((*copy)->dmRecipientOrgUnit);
325 free((*copy)->dmRecipientOrgUnitNum);
326 free((*copy)->dmToHands);
328 free((*copy)->dmStatus);
329 free((*copy)->dmID);
331 zfree(*copy);
335 /* Deallocate struct isds_message_status_change recursively and NULL it */
336 void isds_message_status_change_free(
337 struct isds_message_status_change **message_status_change) {
338 if (!message_status_change || !*message_status_change) return;
340 free((*message_status_change)->dmID);
341 free((*message_status_change)->time);
342 free((*message_status_change)->dmMessageStatus);
344 zfree(*message_status_change);
348 /* Deallocate struct isds_approval recursively and NULL it */
349 void isds_approval_free(struct isds_approval **approval) {
350 if (!approval || !*approval) return;
352 free((*approval)->refference);
354 zfree(*approval);
358 /* Deallocate struct isds_credentials_delivery recursively and NULL it.
359 * The email string is deallocated too. */
360 void isds_credentials_delivery_free(
361 struct isds_credentials_delivery **credentials_delivery) {
362 if (!credentials_delivery || !*credentials_delivery) return;
364 free((*credentials_delivery)->email);
365 free((*credentials_delivery)->token);
366 free((*credentials_delivery)->new_user_name);
368 zfree(*credentials_delivery);
372 /* *DUP_OR_ERROR macros needs error label */
373 #define STRDUP_OR_ERROR(new, template) { \
374 if (!template) { \
375 (new) = NULL; \
376 } else { \
377 (new) = strdup(template); \
378 if (!new) goto error; \
382 #define FLATDUP_OR_ERROR(new, template) { \
383 if (!template) { \
384 (new) = NULL; \
385 } else { \
386 (new) = malloc(sizeof(*(new))); \
387 if (!new) goto error; \
388 memcpy((new), (template), sizeof(*(template))); \
392 /* Copy structure isds_pki_credentials recursively. */
393 struct isds_pki_credentials *isds_pki_credentials_duplicate(
394 const struct isds_pki_credentials *template) {
395 struct isds_pki_credentials *new = NULL;
397 if(!template) return NULL;
399 new = calloc(1, sizeof(*new));
400 if (!new) return NULL;
402 STRDUP_OR_ERROR(new->engine, template->engine);
403 new->certificate_format = template->certificate_format;
404 STRDUP_OR_ERROR(new->certificate, template->certificate);
405 new->key_format = template->key_format;
406 STRDUP_OR_ERROR(new->key, template->key);
407 STRDUP_OR_ERROR(new->passphrase, template->passphrase);
409 return new;
411 error:
412 isds_pki_credentials_free(&new);
413 return NULL;
417 /* Copy structure isds_PersonName recursively */
418 struct isds_PersonName *isds_PersonName_duplicate(
419 const struct isds_PersonName *template) {
420 struct isds_PersonName *new = NULL;
422 if (!template) return NULL;
424 new = calloc(1, sizeof(*new));
425 if (!new) return NULL;
427 STRDUP_OR_ERROR(new->pnFirstName, template->pnFirstName);
428 STRDUP_OR_ERROR(new->pnMiddleName, template->pnMiddleName);
429 STRDUP_OR_ERROR(new->pnLastName, template->pnLastName);
430 STRDUP_OR_ERROR(new->pnLastNameAtBirth, template->pnLastNameAtBirth);
432 return new;
434 error:
435 isds_PersonName_free(&new);
436 return NULL;
440 /* Copy structure isds_BirthInfo recursively */
441 static struct isds_BirthInfo *isds_BirthInfo_duplicate(
442 const struct isds_BirthInfo *template) {
443 struct isds_BirthInfo *new = NULL;
445 if (!template) return NULL;
447 new = calloc(1, sizeof(*new));
448 if (!new) return NULL;
450 FLATDUP_OR_ERROR(new->biDate, template->biDate);
451 STRDUP_OR_ERROR(new->biCity, template->biCity);
452 STRDUP_OR_ERROR(new->biCounty, template->biCounty);
453 STRDUP_OR_ERROR(new->biState, template->biState);
455 return new;
457 error:
458 isds_BirthInfo_free(&new);
459 return NULL;
463 /* Copy structure isds_Address recursively */
464 struct isds_Address *isds_Address_duplicate(
465 const struct isds_Address *template) {
466 struct isds_Address *new = NULL;
468 if (!template) return NULL;
470 new = calloc(1, sizeof(*new));
471 if (!new) return NULL;
473 STRDUP_OR_ERROR(new->adCity, template->adCity);
474 STRDUP_OR_ERROR(new->adStreet, template->adStreet);
475 STRDUP_OR_ERROR(new->adNumberInStreet, template->adNumberInStreet);
476 STRDUP_OR_ERROR(new->adNumberInMunicipality,
477 template->adNumberInMunicipality);
478 STRDUP_OR_ERROR(new->adZipCode, template->adZipCode);
479 STRDUP_OR_ERROR(new->adState, template->adState);
481 return new;
483 error:
484 isds_Address_free(&new);
485 return NULL;
489 /* Copy structure isds_DbOwnerInfo recursively */
490 struct isds_DbOwnerInfo *isds_DbOwnerInfo_duplicate(
491 const struct isds_DbOwnerInfo *template) {
492 struct isds_DbOwnerInfo *new = NULL;
493 if (!template) return NULL;
495 new = calloc(1, sizeof(*new));
496 if (!new) return NULL;
498 STRDUP_OR_ERROR(new->dbID, template->dbID);
499 FLATDUP_OR_ERROR(new->dbType, template->dbType);
500 STRDUP_OR_ERROR(new->ic, template->ic);
502 if (template->personName) {
503 if (!(new->personName =
504 isds_PersonName_duplicate(template->personName)))
505 goto error;
508 STRDUP_OR_ERROR(new->firmName, template->firmName);
510 if (template->birthInfo) {
511 if (!(new->birthInfo =
512 isds_BirthInfo_duplicate(template->birthInfo)))
513 goto error;
516 if (template->address) {
517 if (!(new->address = isds_Address_duplicate(template->address)))
518 goto error;
521 STRDUP_OR_ERROR(new->nationality, template->nationality);
522 STRDUP_OR_ERROR(new->email, template->email);
523 STRDUP_OR_ERROR(new->telNumber, template->telNumber);
524 STRDUP_OR_ERROR(new->identifier, template->identifier);
525 STRDUP_OR_ERROR(new->registryCode, template->registryCode);
526 FLATDUP_OR_ERROR(new->dbState, template->dbState);
527 FLATDUP_OR_ERROR(new->dbEffectiveOVM, template->dbEffectiveOVM);
528 FLATDUP_OR_ERROR(new->dbOpenAddressing, template->dbOpenAddressing);
530 return new;
532 error:
533 isds_DbOwnerInfo_free(&new);
534 return NULL;
538 /* Copy structure isds_DbUserInfo recursively */
539 struct isds_DbUserInfo *isds_DbUserInfo_duplicate(
540 const struct isds_DbUserInfo *template) {
541 struct isds_DbUserInfo *new = NULL;
542 if (!template) return NULL;
544 new = calloc(1, sizeof(*new));
545 if (!new) return NULL;
547 STRDUP_OR_ERROR(new->userID, template->userID);
548 FLATDUP_OR_ERROR(new->userType, template->userType);
549 FLATDUP_OR_ERROR(new->userPrivils, template->userPrivils);
551 if (template->personName) {
552 if (!(new->personName =
553 isds_PersonName_duplicate(template->personName)))
554 goto error;
557 if (template->address) {
558 if (!(new->address = isds_Address_duplicate(template->address)))
559 goto error;
562 FLATDUP_OR_ERROR(new->biDate, template->biDate);
563 STRDUP_OR_ERROR(new->ic, template->ic);
564 STRDUP_OR_ERROR(new->firmName, template->firmName);
565 STRDUP_OR_ERROR(new->caStreet, template->caStreet);
566 STRDUP_OR_ERROR(new->caCity, template->caCity);
567 STRDUP_OR_ERROR(new->caZipCode, template->caZipCode);
568 STRDUP_OR_ERROR(new->caState, template->caState);
570 return new;
572 error:
573 isds_DbUserInfo_free(&new);
574 return NULL;
577 #undef FLATDUP_OR_ERROR
578 #undef STRDUP_OR_ERROR
581 /* Logs libxml2 errors. Should be registered to libxml2 library.
582 * @ctx is unused currently
583 * @msg is printf-like formated message from libxml2 (UTF-8?)
584 * @... are variadic arguments for @msg */
585 static void log_xml(void *ctx, const char *msg, ...) {
586 va_list ap;
587 char *text = NULL;
589 if (!msg) return;
591 va_start(ap, msg);
592 isds_vasprintf(&text, msg, ap);
593 va_end(ap);
595 if (text)
596 isds_log(ILF_XML, ILL_ERR, "%s", text);
597 free(text);
601 /* Initialize ISDS library.
602 * Global function, must be called before other functions.
603 * If it fails you can not use ISDS library and must call isds_cleanup() to
604 * free partially initialized global variables. */
605 isds_error isds_init(void) {
606 /* NULL global variables */
607 log_facilities = ILF_ALL;
608 log_level = ILL_WARNING;
609 log_callback = NULL;
610 log_callback_data = NULL;
612 #if ENABLE_NLS
613 /* Initialize gettext */
614 bindtextdomain(PACKAGE, LOCALEDIR);
615 #endif
617 #if HAVE_LIBCURL
618 /* Initialize CURL */
619 if (curl_global_init(CURL_GLOBAL_ALL)) {
620 isds_log(ILF_ISDS, ILL_CRIT, _("CURL library initialization failed\n"));
621 return IE_ERROR;
623 #endif /* HAVE_LIBCURL */
625 /* Initialize gpg-error because of gpgme and ksba */
626 if (gpg_err_init()) {
627 isds_log(ILF_ISDS, ILL_CRIT,
628 _("gpg-error library initialization failed\n"));
629 return IE_ERROR;
632 /* Initialize GPGME */
633 if (_isds_init_gpgme(&version_gpgme)) {
634 isds_log(ILF_ISDS, ILL_CRIT,
635 _("GPGME library initialization failed\n"));
636 return IE_ERROR;
639 /* Initialize gcrypt */
640 if (_isds_init_gcrypt(&version_gcrypt)) {
641 isds_log(ILF_ISDS, ILL_CRIT,
642 _("gcrypt library initialization failed\n"));
643 return IE_ERROR;
646 /* This can _exit() current program. Find not so assertive check. */
647 LIBXML_TEST_VERSION;
648 xmlSetGenericErrorFunc(NULL, log_xml);
650 /* Check expat */
651 if (_isds_init_expat(&version_expat)) {
652 isds_log(ILF_ISDS, ILL_CRIT,
653 _("expat library initialization failed\n"));
654 return IE_ERROR;
657 /* Allocate global variables */
660 return IE_SUCCESS;
664 /* Deinitialize ISDS library.
665 * Global function, must be called as last library function. */
666 isds_error isds_cleanup(void) {
667 /* XML */
668 xmlCleanupParser();
670 #if HAVE_LIBCURL
671 /* Curl */
672 curl_global_cleanup();
673 #endif
675 return IE_SUCCESS;
679 /* Return version string of this library. Version of dependencies can be
680 * embedded. Do no try to parse it. You must free it. */
681 char *isds_version(void) {
682 char *buffer = NULL;
684 isds_asprintf(&buffer,
685 #if HAVE_LIBCURL
686 _("%s (%s, GPGME %s, gcrypt %s, %s, libxml2 %s)"),
687 #else
688 _("%s (GPGME %s, gcrypt %s, %s, libxml2 %s)"),
689 #endif
690 PACKAGE_VERSION,
691 #if HAVE_LIBCURL
692 curl_version(),
693 #endif
694 version_gpgme, version_gcrypt,
695 version_expat, xmlParserVersion);
696 return buffer;
700 /* Return text description of ISDS error */
701 const char *isds_strerror(const isds_error error) {
702 switch (error) {
703 case IE_SUCCESS:
704 return(_("Success")); break;
705 case IE_ERROR:
706 return(_("Unspecified error")); break;
707 case IE_NOTSUP:
708 return(_("Not supported")); break;
709 case IE_INVAL:
710 return(_("Invalid value")); break;
711 case IE_INVALID_CONTEXT:
712 return(_("Invalid context")); break;
713 case IE_NOT_LOGGED_IN:
714 return(_("Not logged in")); break;
715 case IE_CONNECTION_CLOSED:
716 return(_("Connection closed")); break;
717 case IE_TIMED_OUT:
718 return(_("Timed out")); break;
719 case IE_NOEXIST:
720 return(_("Not exist")); break;
721 case IE_NOMEM:
722 return(_("Out of memory")); break;
723 case IE_NETWORK:
724 return(_("Network problem")); break;
725 case IE_HTTP:
726 return(_("HTTP problem")); break;
727 case IE_SOAP:
728 return(_("SOAP problem")); break;
729 case IE_XML:
730 return(_("XML problem")); break;
731 case IE_ISDS:
732 return(_("ISDS server problem")); break;
733 case IE_ENUM:
734 return(_("Invalid enum value")); break;
735 case IE_DATE:
736 return(_("Invalid date value")); break;
737 case IE_2BIG:
738 return(_("Too big")); break;
739 case IE_2SMALL:
740 return(_("Too small")); break;
741 case IE_NOTUNIQ:
742 return(_("Value not unique")); break;
743 case IE_NOTEQUAL:
744 return(_("Values not equal")); break;
745 case IE_PARTIAL_SUCCESS:
746 return(_("Some suboperations failed")); break;
747 case IE_ABORTED:
748 return(_("Operation aborted")); break;
749 default:
750 return(_("Unknown error"));
755 /* Create ISDS context.
756 * Each context can be used for different sessions to (possibly) different
757 * ISDS server with different credentials. */
758 struct isds_ctx *isds_ctx_create(void) {
759 struct isds_ctx *context;
760 context = malloc(sizeof(*context));
761 if (context) memset(context, 0, sizeof(*context));
762 return context;
765 #if HAVE_LIBCURL
766 /* Close possibly opened connection to Czech POINT document deposit without
767 * resetting long_message buffer.
768 * XXX: Do not use czp_close_connection() if you do not want to destroy log
769 * message.
770 * @context is Czech POINT session context. */
771 static isds_error czp_do_close_connection(struct isds_ctx *context) {
772 if (!context) return IE_INVALID_CONTEXT;
773 _isds_close_connection(context);
774 return IE_SUCCESS;
778 /* Discard credentials.
779 * Only that. It does not cause log out, connection close or similar. */
780 static isds_error discard_credentials(struct isds_ctx *context) {
781 if(!context) return IE_INVALID_CONTEXT;
783 if (context->username) {
784 memset(context->username, 0, strlen(context->username));
785 zfree(context->username);
787 if (context->password) {
788 memset(context->password, 0, strlen(context->password));
789 zfree(context->password);
791 isds_pki_credentials_free(&context->pki_credentials);
793 return IE_SUCCESS;
795 #endif /* HAVE_LIBCURL */
798 /* Destroy ISDS context and free memory.
799 * @context will be NULLed on success. */
800 isds_error isds_ctx_free(struct isds_ctx **context) {
801 if (!context || !*context) {
802 return IE_INVALID_CONTEXT;
805 #if HAVE_LIBCURL
806 /* Discard credentials and close connection */
807 switch ((*context)->type) {
808 case CTX_TYPE_NONE: break;
809 case CTX_TYPE_ISDS: isds_logout(*context); break;
810 case CTX_TYPE_CZP:
811 case CTX_TYPE_TESTING_REQUEST_COLLECTOR:
812 czp_do_close_connection(*context); break;
815 /* For sure */
816 discard_credentials(*context);
818 /* Free other structures */
819 free((*context)->tls_verify_server);
820 free((*context)->tls_ca_file);
821 free((*context)->tls_ca_dir);
822 free((*context)->tls_crl_file);
823 #endif /* HAVE_LIBCURL */
824 free((*context)->long_message);
826 free(*context);
827 *context = NULL;
828 return IE_SUCCESS;
832 /* Return long message text produced by library function, e.g. detailed error
833 * message. Returned pointer is only valid until new library function is
834 * called for the same context. Could be NULL, especially if NULL context is
835 * supplied. Return string is locale encoded. */
836 char *isds_long_message(const struct isds_ctx *context) {
837 if (!context) return NULL;
838 return context->long_message;
842 /* Stores message into context' long_message buffer.
843 * Application can pick the message up using isds_long_message().
844 * NULL @message truncates the buffer but does not deallocate it.
845 * @message is coded in locale encoding */
846 _hidden isds_error isds_log_message(struct isds_ctx *context,
847 const char *message) {
848 char *buffer;
849 size_t length;
851 if (!context) return IE_INVALID_CONTEXT;
853 /* FIXME: Check for integer overflow */
854 length = 1 + ((message) ? strlen(message) : 0);
855 buffer = realloc(context->long_message, length);
856 if (!buffer) return IE_NOMEM;
858 if (message)
859 strcpy(buffer, message);
860 else
861 *buffer = '\0';
863 context->long_message = buffer;
864 return IE_SUCCESS;
868 /* Appends message into context' long_message buffer.
869 * Application can pick the message up using isds_long_message().
870 * NULL message has void effect. */
871 _hidden isds_error isds_append_message(struct isds_ctx *context,
872 const char *message) {
873 char *buffer;
874 size_t old_length, length;
876 if (!context) return IE_INVALID_CONTEXT;
877 if (!message) return IE_SUCCESS;
878 if (!context->long_message)
879 return isds_log_message(context, message);
881 old_length = strlen(context->long_message);
882 /* FIXME: Check for integer overflow */
883 length = 1 + old_length + strlen(message);
884 buffer = realloc(context->long_message, length);
885 if (!buffer) return IE_NOMEM;
887 strcpy(buffer + old_length, message);
889 context->long_message = buffer;
890 return IE_SUCCESS;
894 /* Stores formatted message into context' long_message buffer.
895 * Application can pick the message up using isds_long_message(). */
896 _hidden isds_error isds_printf_message(struct isds_ctx *context,
897 const char *format, ...) {
898 va_list ap;
899 int length;
901 if (!context) return IE_INVALID_CONTEXT;
902 va_start(ap, format);
903 length = isds_vasprintf(&(context->long_message), format, ap);
904 va_end(ap);
906 return (length < 0) ? IE_ERROR: IE_SUCCESS;
910 /* Set logging up.
911 * @facilities is bit mask of isds_log_facility values,
912 * @level is verbosity level. */
913 void isds_set_logging(const unsigned int facilities,
914 const isds_log_level level) {
915 log_facilities = facilities;
916 log_level = level;
920 /* Register callback function libisds calls when new global log message is
921 * produced by library. Library logs to stderr by default.
922 * @callback is function provided by application libisds will call. See type
923 * definition for @callback argument explanation. Pass NULL to revert logging to
924 * default behaviour.
925 * @data is application specific data @callback gets as last argument */
926 void isds_set_log_callback(isds_log_callback callback, void *data) {
927 log_callback = callback;
928 log_callback_data = data;
932 /* Log @message in class @facility with log @level into global log. @message
933 * is printf(3) formatting string, variadic arguments may be necessary.
934 * For debugging purposes. */
935 _hidden isds_error isds_log(const isds_log_facility facility,
936 const isds_log_level level, const char *message, ...) {
937 va_list ap;
938 char *buffer = NULL;
939 int length;
941 if (level > log_level) return IE_SUCCESS;
942 if (!(log_facilities & facility)) return IE_SUCCESS;
943 if (!message) return IE_INVAL;
945 if (log_callback) {
946 /* Pass message to application supplied callback function */
947 va_start(ap, message);
948 length = isds_vasprintf(&buffer, message, ap);
949 va_end(ap);
951 if (length == -1) {
952 return IE_ERROR;
954 if (length > 0) {
955 log_callback(facility, level, buffer, length, log_callback_data);
957 free(buffer);
958 } else {
959 /* Default: Log it to stderr */
960 va_start(ap, message);
961 vfprintf(stderr, message, ap);
962 va_end(ap);
963 /* Line buffered printf is default.
964 * fflush(stderr);*/
967 return IE_SUCCESS;
971 /* Set timeout in milliseconds for each network job like connecting to server
972 * or sending message. Use 0 to disable timeout limits. */
973 isds_error isds_set_timeout(struct isds_ctx *context,
974 const unsigned int timeout) {
975 if (!context) return IE_INVALID_CONTEXT;
976 zfree(context->long_message);
978 #if HAVE_LIBCURL
979 context->timeout = timeout;
981 if (context->curl) {
982 CURLcode curl_err;
984 curl_err = curl_easy_setopt(context->curl, CURLOPT_NOSIGNAL, 1);
985 if (!curl_err)
986 #if HAVE_DECL_CURLOPT_TIMEOUT_MS /* Since curl-7.16.2 */
987 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT_MS,
988 context->timeout);
989 #else
990 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT,
991 context->timeout / 1000);
992 #endif /* not HAVE_DECL_CURLOPT_TIMEOUT_MS */
993 if (curl_err) return IE_ERROR;
996 return IE_SUCCESS;
997 #else /* not HAVE_LIBCURL */
998 return IE_NOTSUP;
999 #endif
1003 /* Register callback function libisds calls periodically during HTTP data
1004 * transfer.
1005 * @context is session context
1006 * @callback is function provided by application libisds will call. See type
1007 * definition for @callback argument explanation.
1008 * @data is application specific data @callback gets as last argument */
1009 isds_error isds_set_progress_callback(struct isds_ctx *context,
1010 isds_progress_callback callback, void *data) {
1011 if (!context) return IE_INVALID_CONTEXT;
1012 zfree(context->long_message);
1014 #if HAVE_LIBCURL
1015 context->progress_callback = callback;
1016 context->progress_callback_data = data;
1018 return IE_SUCCESS;
1019 #else /* not HAVE_LIBCURL */
1020 return IE_NOTSUP;
1021 #endif
1025 /* Change context settings.
1026 * @context is context which setting will be applied to
1027 * @option is name of option. It determines the type of last argument. See
1028 * isds_option definition for more info.
1029 * @... is value of new setting. Type is determined by @option
1030 * */
1031 isds_error isds_set_opt(struct isds_ctx *context, const isds_option option,
1032 ...) {
1033 isds_error err = IE_SUCCESS;
1034 va_list ap;
1035 #if HAVE_LIBCURL
1036 char *pointer, *string;
1037 #endif
1039 if (!context) return IE_INVALID_CONTEXT;
1040 zfree(context->long_message);
1042 va_start(ap, option);
1044 #define REPLACE_VA_BOOLEAN(destination) { \
1045 if (!(destination)) { \
1046 (destination) = malloc(sizeof(*(destination))); \
1047 if (!(destination)) { \
1048 err = IE_NOMEM; goto leave; \
1051 *(destination) = (_Bool) !!va_arg(ap, int); \
1054 #define REPLACE_VA_STRING(destination) { \
1055 string = va_arg(ap, char *); \
1056 if (string) { \
1057 pointer = realloc((destination), 1 + strlen(string)); \
1058 if (!pointer) { err = IE_NOMEM; goto leave; } \
1059 strcpy(pointer, string); \
1060 (destination) = pointer; \
1061 } else { \
1062 free(destination); \
1063 (destination) = NULL; \
1067 switch (option) {
1068 case IOPT_TLS_VERIFY_SERVER:
1069 #if HAVE_LIBCURL
1070 REPLACE_VA_BOOLEAN(context->tls_verify_server);
1071 #else
1072 err = IE_NOTSUP; goto leave;
1073 #endif
1074 break;
1075 case IOPT_TLS_CA_FILE:
1076 #if HAVE_LIBCURL
1077 REPLACE_VA_STRING(context->tls_ca_file);
1078 #else
1079 err = IE_NOTSUP; goto leave;
1080 #endif
1081 break;
1082 case IOPT_TLS_CA_DIRECTORY:
1083 #if HAVE_LIBCURL
1084 REPLACE_VA_STRING(context->tls_ca_dir);
1085 #else
1086 err = IE_NOTSUP; goto leave;
1087 #endif
1088 break;
1089 case IOPT_TLS_CRL_FILE:
1090 #if HAVE_LIBCURL
1091 #if HAVE_DECL_CURLOPT_CRLFILE /* Since curl-7.19.0 */
1092 REPLACE_VA_STRING(context->tls_crl_file);
1093 #else
1094 isds_log_message(context,
1095 _("Curl library does not support CRL definition"));
1096 err = IE_NOTSUP;
1097 #endif /* not HAVE_DECL_CURLOPT_CRLFILE */
1098 #else
1099 err = IE_NOTSUP; goto leave;
1100 #endif /* not HAVE_LIBCURL */
1101 break;
1102 case IOPT_NORMALIZE_MIME_TYPE:
1103 context->normalize_mime_type = (_Bool) !!va_arg(ap, int);
1104 break;
1106 default:
1107 err = IE_ENUM; goto leave;
1110 #undef REPLACE_VA_STRING
1111 #undef REPLACE_VA_BOOLEAN
1113 leave:
1114 va_end(ap);
1115 return err;
1119 /* Connect and log in into ISDS server.
1120 * All required arguments will be copied, you do not have to keep them after
1121 * that.
1122 * ISDS supports six different authentication methods. Exact method is
1123 * selected on @username, @password, @pki_credentials, and @otp arguments:
1124 * - If @pki_credentials == NULL, @username and @password must be supplied
1125 * and then
1126 * - If @otp == NULL, simple authentication by username and password will
1127 * be proceeded.
1128 * - If @otp != NULL, authentication by username and password and OTP
1129 * will be used.
1130 * - If @pki_credentials != NULL, then
1131 * - If @username == NULL, only certificate will be used
1132 * - If @username != NULL, then
1133 * - If @password == NULL, then certificate will be used and
1134 * @username shifts meaning to box ID. This is used for hosted
1135 * services.
1136 * - Otherwise all three arguments will be used.
1137 * Please note, that different cases requires different certificate type
1138 * (system qualified one or commercial non qualified one). This library
1139 * does not check such political issues. Please see ISDS Specification
1140 * for more details.
1141 * @url is base address of ISDS web service. Pass extern isds_locator
1142 * variable to use production ISDS instance without client certificate
1143 * authentication (or extern isds_cert_locator with client certificate
1144 * authentication). Passing NULL has the same effect, autoselection between
1145 * isds_locator and isds_cert_locator is performed in addition. You can pass
1146 * extern isds_testing_locator (or isds_cert_testing_locator) variable to
1147 * select testing instance.
1148 * @username is user name of ISDS user or box ID
1149 * @password is user's secret password
1150 * @pki_credentials defines public key cryptographic material to use in client
1151 * authentication.
1152 * @otp selects one-time password authentication method to use, defines OTP
1153 * code and returns fine grade resolution of OTP procedure.
1154 * @return:
1155 * IE_SUCCESS if authentication succeeds
1156 * IE_NOT_LOGGED_IN if authentication fails. If OTP authentication has been
1157 * requested, fine grade reason will be set into @otp->resolution. Error
1158 * message from server can be obtained by isds_long_message() call.
1159 * IE_PARTIAL_SUCCESS if time-based OTP authentication has been requested and
1160 * server has sent OTP code through side channel. Application is expected to
1161 * fill the code into @otp->otp_code and retry this call to complete second
1162 * phase of TOTP authentication;
1163 * or other appropriate error. */
1164 isds_error isds_login(struct isds_ctx *context, const char *url,
1165 const char *username, const char *password,
1166 const struct isds_pki_credentials *pki_credentials,
1167 struct isds_otp *otp) {
1168 #if HAVE_LIBCURL
1169 isds_error err = IE_NOT_LOGGED_IN;
1170 isds_error soap_err;
1171 xmlNsPtr isds_ns = NULL;
1172 xmlNodePtr request = NULL;
1173 xmlNodePtr response = NULL;
1174 #endif /* HAVE_LIBCURL */
1176 if (!context) return IE_INVALID_CONTEXT;
1177 zfree(context->long_message);
1179 #if HAVE_LIBCURL
1180 /* Close connection if already logged in */
1181 if (context->curl) {
1182 _isds_close_connection(context);
1185 /* Store configuration */
1186 context->type = CTX_TYPE_ISDS;
1187 zfree(context->url);
1189 /* Mangle base URI according requested authentication method */
1190 if (!pki_credentials) {
1191 isds_log(ILF_SEC, ILL_INFO,
1192 _("Selected authentication method: no certificate, "
1193 "username and password\n"));
1194 if (!username || !password) {
1195 isds_log_message(context,
1196 _("Both username and password must be supplied"));
1197 return IE_INVAL;
1199 if (otp) {
1200 isds_log_message(context,
1201 _("One-time password authentication method has not been "
1202 "implemented yet"));
1203 return IE_NOTSUP;
1205 /* Default locator is official system (without client certificate) */
1206 context->url = strdup((url) ? url : isds_locator);
1207 } else {
1208 /* Default locator is official system (with client certificate) */
1209 if (!url) url = isds_cert_locator;
1211 if (!username) {
1212 isds_log(ILF_SEC, ILL_INFO,
1213 _("Selected authentication method: system certificate, "
1214 "no username and no password\n"));
1215 password = NULL;
1216 context->url = _isds_astrcat(url, "cert/");
1217 } else {
1218 if (!password) {
1219 isds_log(ILF_SEC, ILL_INFO,
1220 _("Selected authentication method: system certificate, "
1221 "box ID and no password\n"));
1222 context->url = _isds_astrcat(url, "hspis/");
1223 } else {
1224 isds_log(ILF_SEC, ILL_INFO,
1225 _("Selected authentication method: commercial "
1226 "certificate, username and password\n"));
1227 context->url = _isds_astrcat(url, "certds/");
1231 if (!(context->url))
1232 return IE_NOMEM;
1234 /* Prepare CURL handle */
1235 context->curl = curl_easy_init();
1236 if (!(context->curl))
1237 return IE_ERROR;
1239 /* Build log-in request */
1240 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1241 if (!request) {
1242 isds_log_message(context, _("Could not build ISDS log-in request"));
1243 return IE_ERROR;
1245 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1246 if(!isds_ns) {
1247 isds_log_message(context, _("Could not create ISDS name space"));
1248 xmlFreeNode(request);
1249 return IE_ERROR;
1251 xmlSetNs(request, isds_ns);
1253 /* Store credentials */
1254 /* FIXME: mlock password
1255 * (I have a library) */
1256 discard_credentials(context);
1257 if (username) context->username = strdup(username);
1258 if (password) context->password = strdup(password);
1259 context->pki_credentials = isds_pki_credentials_duplicate(pki_credentials);
1260 if ((username && !context->username) || (password && !context->password) ||
1261 (pki_credentials && !context->pki_credentials)) {
1262 discard_credentials(context);
1263 xmlFreeNode(request);
1264 return IE_NOMEM;
1267 isds_log(ILF_ISDS, ILL_DEBUG, _("Logging user %s into server %s\n"),
1268 username, url);
1270 /* Send log-in request */
1271 soap_err = _isds_soap(context, "DS/dz", request, &response, NULL, NULL);
1273 /* Remove credentials */
1274 discard_credentials(context);
1276 /* Destroy log-in request */
1277 xmlFreeNode(request);
1279 if (soap_err) {
1280 xmlFreeNodeList(response);
1281 _isds_close_connection(context);
1282 return soap_err;
1285 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1286 * authentication succeeded if soap_err == IE_SUCCESS */
1287 err = IE_SUCCESS;
1289 xmlFreeNodeList(response);
1291 if (!err)
1292 isds_log(ILF_ISDS, ILL_DEBUG,
1293 _("User %s has been logged into server %s successfully\n"),
1294 username, url);
1295 return err;
1296 #else /* not HAVE_LIBCURL */
1297 return IE_NOTSUP;
1298 #endif
1302 /* Log out from ISDS server discards credentials and connection configuration. */
1303 isds_error isds_logout(struct isds_ctx *context) {
1304 if (!context) return IE_INVALID_CONTEXT;
1305 zfree(context->long_message);
1307 #if HAVE_LIBCURL
1308 /* Close connection */
1309 if (context->curl) {
1310 _isds_close_connection(context);
1312 /* Discard credentials for sure. They should not survive isds_login(),
1313 * even successful .*/
1314 discard_credentials(context);
1315 zfree(context->url);
1317 isds_log(ILF_ISDS, ILL_DEBUG, _("Logged out from ISDS server\n"));
1318 } else {
1319 discard_credentials(context);
1321 return IE_SUCCESS;
1322 #else /* not HAVE_LIBCURL */
1323 return IE_NOTSUP;
1324 #endif
1328 /* Verify connection to ISDS is alive and server is responding.
1329 * Sent dummy request to ISDS and expect dummy response. */
1330 isds_error isds_ping(struct isds_ctx *context) {
1331 #if HAVE_LIBCURL
1332 isds_error soap_err;
1333 xmlNsPtr isds_ns = NULL;
1334 xmlNodePtr request = NULL;
1335 xmlNodePtr response = NULL;
1336 #endif /* HAVE_LIBCURL */
1338 if (!context) return IE_INVALID_CONTEXT;
1339 zfree(context->long_message);
1341 #if HAVE_LIBCURL
1342 /* Check if connection is established */
1343 if (!context->curl) return IE_CONNECTION_CLOSED;
1346 /* Build dummy request */
1347 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1348 if (!request) {
1349 isds_log_message(context, _("Could build ISDS dummy request"));
1350 return IE_ERROR;
1352 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1353 if(!isds_ns) {
1354 isds_log_message(context, _("Could not create ISDS name space"));
1355 xmlFreeNode(request);
1356 return IE_ERROR;
1358 xmlSetNs(request, isds_ns);
1360 isds_log(ILF_ISDS, ILL_DEBUG, _("Pinging ISDS server\n"));
1362 /* Sent dummy request */
1363 soap_err = _isds_soap(context, "DS/dz", request, &response, NULL, NULL);
1365 /* Destroy log-in request */
1366 xmlFreeNode(request);
1368 if (soap_err) {
1369 isds_log(ILF_ISDS, ILL_DEBUG,
1370 _("ISDS server could not be contacted\n"));
1371 xmlFreeNodeList(response);
1372 return soap_err;
1375 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1376 * authentication succeeded if soap_err == IE_SUCCESS */
1377 /* TODO: ISDS documentation does not specify response body.
1378 * However real server sends back DummyOperationResponse */
1381 xmlFreeNodeList(response);
1383 isds_log(ILF_ISDS, ILL_DEBUG, _("ISDS server alive\n"));
1385 return IE_SUCCESS;
1386 #else /* not HAVE_LIBCURL */
1387 return IE_NOTSUP;
1388 #endif
1392 /* Send bogus request to ISDS.
1393 * Just for test purposes */
1394 isds_error isds_bogus_request(struct isds_ctx *context) {
1395 #if HAVE_LIBCURL
1396 isds_error err;
1397 xmlNsPtr isds_ns = NULL;
1398 xmlNodePtr request = NULL;
1399 xmlDocPtr response = NULL;
1400 xmlChar *code = NULL, *message = NULL;
1401 #endif
1403 if (!context) return IE_INVALID_CONTEXT;
1404 zfree(context->long_message);
1406 #if HAVE_LIBCURL
1407 /* Check if connection is established */
1408 if (!context->curl) {
1409 /* Testing printf message */
1410 isds_printf_message(context, "%s", _("I said connection closed"));
1411 return IE_CONNECTION_CLOSED;
1415 /* Build dummy request */
1416 request = xmlNewNode(NULL, BAD_CAST "X-BogusOperation");
1417 if (!request) {
1418 isds_log_message(context, _("Could build ISDS bogus request"));
1419 return IE_ERROR;
1421 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1422 if(!isds_ns) {
1423 isds_log_message(context, _("Could not create ISDS name space"));
1424 xmlFreeNode(request);
1425 return IE_ERROR;
1427 xmlSetNs(request, isds_ns);
1429 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending bogus request to ISDS\n"));
1431 /* Sent bogus request */
1432 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
1434 /* Destroy request */
1435 xmlFreeNode(request);
1437 if (err) {
1438 isds_log(ILF_ISDS, ILL_DEBUG,
1439 _("Processing ISDS response on bogus request failed\n"));
1440 xmlFreeDoc(response);
1441 return err;
1444 /* Check for response status */
1445 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
1446 &code, &message, NULL);
1447 if (err) {
1448 isds_log(ILF_ISDS, ILL_DEBUG,
1449 _("ISDS response on bogus request is missing status\n"));
1450 free(code);
1451 free(message);
1452 xmlFreeDoc(response);
1453 return err;
1455 if (xmlStrcmp(code, BAD_CAST "0000")) {
1456 char *code_locale = _isds_utf82locale((char*)code);
1457 char *message_locale = _isds_utf82locale((char*)message);
1458 isds_log(ILF_ISDS, ILL_DEBUG,
1459 _("Server refused bogus request (code=%s, message=%s)\n"),
1460 code_locale, message_locale);
1461 /* XXX: Literal error messages from ISDS are Czech messages
1462 * (English sometimes) in UTF-8. It's hard to catch them for
1463 * translation. Successfully gettextized would return in locale
1464 * encoding, unsuccessfully translated would pass in UTF-8. */
1465 isds_log_message(context, message_locale);
1466 free(code_locale);
1467 free(message_locale);
1468 free(code);
1469 free(message);
1470 xmlFreeDoc(response);
1471 return IE_ISDS;
1475 free(code);
1476 free(message);
1477 xmlFreeDoc(response);
1479 isds_log(ILF_ISDS, ILL_DEBUG,
1480 _("Bogus message accepted by server. This should not happen.\n"));
1482 return IE_SUCCESS;
1483 #else /* not HAVE_LIBCURL */
1484 return IE_NOTSUP;
1485 #endif
1489 #if HAVE_LIBCURL
1490 /* Serialize XML subtree to buffer preserving XML indentation.
1491 * @context is session context
1492 * @subtree is XML element to be serialized (with children)
1493 * @buffer is automatically reallocated buffer where serialize to
1494 * @length is size of serialized stream in bytes
1495 * @return standard error code, free @buffer in case of error */
1496 static isds_error serialize_subtree(struct isds_ctx *context,
1497 xmlNodePtr subtree, void **buffer, size_t *length) {
1498 isds_error err = IE_SUCCESS;
1499 xmlBufferPtr xml_buffer = NULL;
1500 xmlSaveCtxtPtr save_ctx = NULL;
1501 xmlDocPtr subtree_doc = NULL;
1502 xmlNodePtr subtree_copy;
1503 xmlNsPtr isds_ns;
1504 void *new_buffer;
1506 if (!context) return IE_INVALID_CONTEXT;
1507 if (!buffer) return IE_INVAL;
1508 zfree(*buffer);
1509 if (!subtree || !length) return IE_INVAL;
1511 /* Make temporary XML document with @subtree root element */
1512 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1513 * It can result in not well-formed on invalid XML tree (e.g. name space
1514 * prefix definition can miss. */
1515 /*FIXME */
1517 subtree_doc = xmlNewDoc(BAD_CAST "1.0");
1518 if (!subtree_doc) {
1519 isds_log_message(context, _("Could not build temporary document"));
1520 err = IE_ERROR;
1521 goto leave;
1524 /* XXX: Copy subtree and attach the copy to document.
1525 * One node can not bee attached into more document at the same time.
1526 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1527 * automatically.
1528 * XXX: Check xmlSaveTree() too. */
1529 subtree_copy = xmlCopyNodeList(subtree);
1530 if (!subtree_copy) {
1531 isds_log_message(context, _("Could not copy subtree"));
1532 err = IE_ERROR;
1533 goto leave;
1535 xmlDocSetRootElement(subtree_doc, subtree_copy);
1537 /* Only this way we get namespace definition as @xmlns:isds,
1538 * otherwise we get namespace prefix without definition */
1539 /* FIXME: Don't overwrite original default namespace */
1540 isds_ns = xmlNewNs(subtree_copy, BAD_CAST ISDS_NS, NULL);
1541 if(!isds_ns) {
1542 isds_log_message(context, _("Could not create ISDS name space"));
1543 err = IE_ERROR;
1544 goto leave;
1546 xmlSetNs(subtree_copy, isds_ns);
1549 /* Serialize the document into buffer */
1550 xml_buffer = xmlBufferCreate();
1551 if (!xml_buffer) {
1552 isds_log_message(context, _("Could not create xmlBuffer"));
1553 err = IE_ERROR;
1554 goto leave;
1556 /* Last argument 0 means to not format the XML tree */
1557 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8", 0);
1558 if (!save_ctx) {
1559 isds_log_message(context, _("Could not create XML serializer"));
1560 err = IE_ERROR;
1561 goto leave;
1563 /* XXX: According LibXML documentation, this function does not return
1564 * meaningful value yet */
1565 xmlSaveDoc(save_ctx, subtree_doc);
1566 if (-1 == xmlSaveFlush(save_ctx)) {
1567 isds_log_message(context,
1568 _("Could not serialize XML subtree"));
1569 err = IE_ERROR;
1570 goto leave;
1572 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1573 * even after xmlSaveFlush(). Thus close it here */
1574 xmlSaveClose(save_ctx); save_ctx = NULL;
1577 /* Store and detach buffer from xml_buffer */
1578 *buffer = xml_buffer->content;
1579 *length = xml_buffer->use;
1580 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1582 /* Shrink buffer */
1583 new_buffer = realloc(*buffer, *length);
1584 if (new_buffer) *buffer = new_buffer;
1586 leave:
1587 if (err) {
1588 zfree(*buffer);
1589 *length = 0;
1592 xmlSaveClose(save_ctx);
1593 xmlBufferFree(xml_buffer);
1594 xmlFreeDoc(subtree_doc); /* Frees subtree_copy, isds_ns etc. */
1595 return err;
1597 #endif /* HAVE_LIBCURL */
1600 #if 0
1601 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1602 * @context is session context
1603 * @document is original document where @nodeset points to
1604 * @nodeset is XPath node set to dump (recursively)
1605 * @buffer is automatically reallocated buffer where serialize to
1606 * @length is size of serialized stream in bytes
1607 * @return standard error code, free @buffer in case of error */
1608 static isds_error dump_nodeset(struct isds_ctx *context,
1609 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1610 void **buffer, size_t *length) {
1611 isds_error err = IE_SUCCESS;
1612 xmlBufferPtr xml_buffer = NULL;
1613 void *new_buffer;
1615 if (!context) return IE_INVALID_CONTEXT;
1616 if (!buffer) return IE_INVAL;
1617 zfree(*buffer);
1618 if (!document || !nodeset || !length) return IE_INVAL;
1619 *length = 0;
1621 /* Empty node set results into NULL buffer */
1622 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1623 goto leave;
1626 /* Resulting the document into buffer */
1627 xml_buffer = xmlBufferCreate();
1628 if (!xml_buffer) {
1629 isds_log_message(context, _("Could not create xmlBuffer"));
1630 err = IE_ERROR;
1631 goto leave;
1634 /* Iterate over all nodes */
1635 for (int i = 0; i < nodeset->nodeNr; i++) {
1636 /* Serialize node.
1637 * XXX: xmlNodeDump() appends to xml_buffer. */
1638 if (-1 ==
1639 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1640 isds_log_message(context, _("Could not dump XML node"));
1641 err = IE_ERROR;
1642 goto leave;
1646 /* Store and detach buffer from xml_buffer */
1647 *buffer = xml_buffer->content;
1648 *length = xml_buffer->use;
1649 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1651 /* Shrink buffer */
1652 new_buffer = realloc(*buffer, *length);
1653 if (new_buffer) *buffer = new_buffer;
1656 leave:
1657 if (err) {
1658 zfree(*buffer);
1659 *length = 0;
1662 xmlBufferFree(xml_buffer);
1663 return err;
1665 #endif
1667 #if 0
1668 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1669 * @context is session context
1670 * @document is original document where @nodeset points to
1671 * @nodeset is XPath node set to dump (recursively)
1672 * @buffer is automatically reallocated buffer where serialize to
1673 * @length is size of serialized stream in bytes
1674 * @return standard error code, free @buffer in case of error */
1675 static isds_error dump_nodeset(struct isds_ctx *context,
1676 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1677 void **buffer, size_t *length) {
1678 isds_error err = IE_SUCCESS;
1679 xmlBufferPtr xml_buffer = NULL;
1680 xmlSaveCtxtPtr save_ctx = NULL;
1681 void *new_buffer;
1683 if (!context) return IE_INVALID_CONTEXT;
1684 if (!buffer) return IE_INVAL;
1685 zfree(*buffer);
1686 if (!document || !nodeset || !length) return IE_INVAL;
1687 *length = 0;
1689 /* Empty node set results into NULL buffer */
1690 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1691 goto leave;
1694 /* Resulting the document into buffer */
1695 xml_buffer = xmlBufferCreate();
1696 if (!xml_buffer) {
1697 isds_log_message(context, _("Could not create xmlBuffer"));
1698 err = IE_ERROR;
1699 goto leave;
1701 if (xmlSubstituteEntitiesDefault(1)) {
1702 isds_log_message(context, _("Could not disable attribute escaping"));
1703 err = IE_ERROR;
1704 goto leave;
1706 /* Last argument means:
1707 * 0 to not format the XML tree
1708 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1709 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8",
1710 XML_SAVE_NO_DECL|XML_SAVE_NO_EMPTY|XML_SAVE_NO_XHTML);
1711 if (!save_ctx) {
1712 isds_log_message(context, _("Could not create XML serializer"));
1713 err = IE_ERROR;
1714 goto leave;
1716 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1717 isds_log_message(context, _("Could not disable attribute escaping"));
1718 err = IE_ERROR;
1719 goto leave;
1723 /* Iterate over all nodes */
1724 for (int i = 0; i < nodeset->nodeNr; i++) {
1725 /* Serialize node.
1726 * XXX: xmlNodeDump() appends to xml_buffer. */
1727 /*if (-1 ==
1728 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1730 /* XXX: According LibXML documentation, this function does not return
1731 * meaningful value yet */
1732 xmlSaveTree(save_ctx, nodeset->nodeTab[i]);
1733 if (-1 == xmlSaveFlush(save_ctx)) {
1734 isds_log_message(context,
1735 _("Could not serialize XML subtree"));
1736 err = IE_ERROR;
1737 goto leave;
1741 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1742 * even after xmlSaveFlush(). Thus close it here */
1743 xmlSaveClose(save_ctx); save_ctx = NULL;
1745 /* Store and detach buffer from xml_buffer */
1746 *buffer = xml_buffer->content;
1747 *length = xml_buffer->use;
1748 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1750 /* Shrink buffer */
1751 new_buffer = realloc(*buffer, *length);
1752 if (new_buffer) *buffer = new_buffer;
1754 leave:
1755 if (err) {
1756 zfree(*buffer);
1757 *length = 0;
1760 xmlSaveClose(save_ctx);
1761 xmlBufferFree(xml_buffer);
1762 return err;
1764 #endif
1767 #if HAVE_LIBCURL
1768 /* Convert UTF-8 @string representation of ISDS dbType to enum @type */
1769 static isds_error string2isds_DbType(xmlChar *string, isds_DbType *type) {
1770 if (!string || !type) return IE_INVAL;
1772 if (!xmlStrcmp(string, BAD_CAST "FO"))
1773 *type = DBTYPE_FO;
1774 else if (!xmlStrcmp(string, BAD_CAST "PFO"))
1775 *type = DBTYPE_PFO;
1776 else if (!xmlStrcmp(string, BAD_CAST "PFO_ADVOK"))
1777 *type = DBTYPE_PFO_ADVOK;
1778 else if (!xmlStrcmp(string, BAD_CAST "PFO_DANPOR"))
1779 *type = DBTYPE_PFO_DANPOR;
1780 else if (!xmlStrcmp(string, BAD_CAST "PFO_INSSPR"))
1781 *type = DBTYPE_PFO_INSSPR;
1782 else if (!xmlStrcmp(string, BAD_CAST "PO"))
1783 *type = DBTYPE_PO;
1784 else if (!xmlStrcmp(string, BAD_CAST "PO_ZAK"))
1785 *type = DBTYPE_PO_ZAK;
1786 else if (!xmlStrcmp(string, BAD_CAST "PO_REQ"))
1787 *type = DBTYPE_PO_REQ;
1788 else if (!xmlStrcmp(string, BAD_CAST "OVM"))
1789 *type = DBTYPE_OVM;
1790 else if (!xmlStrcmp(string, BAD_CAST "OVM_NOTAR"))
1791 *type = DBTYPE_OVM_NOTAR;
1792 else if (!xmlStrcmp(string, BAD_CAST "OVM_EXEKUT"))
1793 *type = DBTYPE_OVM_EXEKUT;
1794 else if (!xmlStrcmp(string, BAD_CAST "OVM_REQ"))
1795 *type = DBTYPE_OVM_REQ;
1796 else
1797 return IE_ENUM;
1798 return IE_SUCCESS;
1802 /* Convert ISDS dbType enum @type to UTF-8 string.
1803 * @Return pointer to static string, or NULL if unknown enum value */
1804 static const xmlChar *isds_DbType2string(const isds_DbType type) {
1805 switch(type) {
1806 /* DBTYPE_SYSTEM is invalid value from point of view of public
1807 * SOAP interface. */
1808 case DBTYPE_FO: return(BAD_CAST "FO"); break;
1809 case DBTYPE_PFO: return(BAD_CAST "PFO"); break;
1810 case DBTYPE_PFO_ADVOK: return(BAD_CAST "PFO_ADVOK"); break;
1811 case DBTYPE_PFO_DANPOR: return(BAD_CAST "PFO_DANPOR"); break;
1812 case DBTYPE_PFO_INSSPR: return(BAD_CAST "PFO_INSSPR"); break;
1813 case DBTYPE_PO: return(BAD_CAST "PO"); break;
1814 case DBTYPE_PO_ZAK: return(BAD_CAST "PO_ZAK"); break;
1815 case DBTYPE_PO_REQ: return(BAD_CAST "PO_REQ"); break;
1816 case DBTYPE_OVM: return(BAD_CAST "OVM"); break;
1817 case DBTYPE_OVM_NOTAR: return(BAD_CAST "OVM_NOTAR"); break;
1818 case DBTYPE_OVM_EXEKUT: return(BAD_CAST "OVM_EXEKUT"); break;
1819 case DBTYPE_OVM_REQ: return(BAD_CAST "OVM_REQ"); break;
1820 default: return NULL; break;
1825 /* Convert UTF-8 @string representation of ISDS userType to enum @type */
1826 static isds_error string2isds_UserType(xmlChar *string, isds_UserType *type) {
1827 if (!string || !type) return IE_INVAL;
1829 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
1830 *type = USERTYPE_PRIMARY;
1831 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
1832 *type = USERTYPE_ENTRUSTED;
1833 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
1834 *type = USERTYPE_ADMINISTRATOR;
1835 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
1836 *type = USERTYPE_OFFICIAL;
1837 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
1838 *type = USERTYPE_OFFICIAL_CERT;
1839 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
1840 *type = USERTYPE_LIQUIDATOR;
1841 else
1842 return IE_ENUM;
1843 return IE_SUCCESS;
1847 /* Convert ISDS userType enum @type to UTF-8 string.
1848 * @Return pointer to static string, or NULL if unknown enum value */
1849 static const xmlChar *isds_UserType2string(const isds_UserType type) {
1850 switch(type) {
1851 case USERTYPE_PRIMARY: return(BAD_CAST "PRIMARY_USER"); break;
1852 case USERTYPE_ENTRUSTED: return(BAD_CAST "ENTRUSTED_USER"); break;
1853 case USERTYPE_ADMINISTRATOR: return(BAD_CAST "ADMINISTRATOR"); break;
1854 case USERTYPE_OFFICIAL: return(BAD_CAST "OFFICIAL"); break;
1855 case USERTYPE_OFFICIAL_CERT: return(BAD_CAST "OFFICIAL_CERT"); break;
1856 case USERTYPE_LIQUIDATOR: return(BAD_CAST "LIQUIDATOR"); break;
1857 default: return NULL; break;
1862 /* Convert UTF-8 @string representation of ISDS sender type to enum @type */
1863 static isds_error string2isds_sender_type(const xmlChar *string,
1864 isds_sender_type *type) {
1865 if (!string || !type) return IE_INVAL;
1867 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
1868 *type = SENDERTYPE_PRIMARY;
1869 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
1870 *type = SENDERTYPE_ENTRUSTED;
1871 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
1872 *type = SENDERTYPE_ADMINISTRATOR;
1873 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
1874 *type = SENDERTYPE_OFFICIAL;
1875 else if (!xmlStrcmp(string, BAD_CAST "VIRTUAL"))
1876 *type = SENDERTYPE_VIRTUAL;
1877 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
1878 *type = SENDERTYPE_OFFICIAL_CERT;
1879 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
1880 *type = SENDERTYPE_LIQUIDATOR;
1881 else
1882 return IE_ENUM;
1883 return IE_SUCCESS;
1887 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
1888 * @Return pointer to static string, or NULL if unknown enum value */
1889 static const xmlChar *isds_FileMetaType2string(const isds_FileMetaType type) {
1890 switch(type) {
1891 case FILEMETATYPE_MAIN: return(BAD_CAST "main"); break;
1892 case FILEMETATYPE_ENCLOSURE: return(BAD_CAST "enclosure"); break;
1893 case FILEMETATYPE_SIGNATURE: return(BAD_CAST "signature"); break;
1894 case FILEMETATYPE_META: return(BAD_CAST "meta"); break;
1895 default: return NULL; break;
1898 #endif /* HAVE_LIBCURL */
1901 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
1902 * @Return IE_ENUM if @string is not valid enum member */
1903 static isds_error string2isds_FileMetaType(const xmlChar *string,
1904 isds_FileMetaType *type) {
1905 if (!string || !type) return IE_INVAL;
1907 if (!xmlStrcmp(string, BAD_CAST "main"))
1908 *type = FILEMETATYPE_MAIN;
1909 else if (!xmlStrcmp(string, BAD_CAST "enclosure"))
1910 *type = FILEMETATYPE_ENCLOSURE;
1911 else if (!xmlStrcmp(string, BAD_CAST "signature"))
1912 *type = FILEMETATYPE_SIGNATURE;
1913 else if (!xmlStrcmp(string, BAD_CAST "meta"))
1914 *type = FILEMETATYPE_META;
1915 else
1916 return IE_ENUM;
1917 return IE_SUCCESS;
1921 /* Convert UTF-8 @string to ISDS hash @algorithm.
1922 * @Return IE_ENUM if @string is not valid enum member */
1923 static isds_error string2isds_hash_algorithm(const xmlChar *string,
1924 isds_hash_algorithm *algorithm) {
1925 if (!string || !algorithm) return IE_INVAL;
1927 if (!xmlStrcmp(string, BAD_CAST "MD5"))
1928 *algorithm = HASH_ALGORITHM_MD5;
1929 else if (!xmlStrcmp(string, BAD_CAST "SHA-1"))
1930 *algorithm = HASH_ALGORITHM_SHA_1;
1931 else if (!xmlStrcmp(string, BAD_CAST "SHA-224"))
1932 *algorithm = HASH_ALGORITHM_SHA_224;
1933 else if (!xmlStrcmp(string, BAD_CAST "SHA-256"))
1934 *algorithm = HASH_ALGORITHM_SHA_256;
1935 else if (!xmlStrcmp(string, BAD_CAST "SHA-384"))
1936 *algorithm = HASH_ALGORITHM_SHA_384;
1937 else if (!xmlStrcmp(string, BAD_CAST "SHA-512"))
1938 *algorithm = HASH_ALGORITHM_SHA_512;
1939 else
1940 return IE_ENUM;
1941 return IE_SUCCESS;
1945 #if HAVE_LIBCURL
1946 /* Convert UTF-8 @string representation of ISO 8601 date to @time.
1947 * XXX: Not all ISO formats are supported */
1948 static isds_error datestring2tm(const xmlChar *string, struct tm *time) {
1949 char *offset;
1950 if (!string || !time) return IE_INVAL;
1952 /* xsd:date is ISO 8601 string, thus ASCII */
1953 offset = strptime((char*)string, "%Y-%m-%d", time);
1954 if (offset && *offset == '\0')
1955 return IE_SUCCESS;
1957 offset = strptime((char*)string, "%Y%m%d", time);
1958 if (offset && *offset == '\0')
1959 return IE_SUCCESS;
1961 offset = strptime((char*)string, "%Y-%j", time);
1962 if (offset && *offset == '\0')
1963 return IE_SUCCESS;
1965 return IE_NOTSUP;
1969 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
1970 static isds_error tm2datestring(const struct tm *time, xmlChar **string) {
1971 if (!time || !string) return IE_INVAL;
1973 if (-1 == isds_asprintf((char **) string, "%d-%02d-%02d",
1974 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
1975 return IE_ERROR;
1977 return IE_SUCCESS;
1981 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
1982 * respects the @time microseconds too. */
1983 static isds_error timeval2timestring(const struct timeval *time,
1984 xmlChar **string) {
1985 struct tm broken;
1987 if (!time || !string) return IE_INVAL;
1989 if (!gmtime_r(&time->tv_sec, &broken)) return IE_DATE;
1990 if (time->tv_usec < 0 || time->tv_usec > 999999) return IE_DATE;
1992 /* TODO: small negative year should be formatted as "-0012". This is not
1993 * true for glibc "%04d". We should implement it.
1994 * TODO: What's type of time->tv_usec exactly? Unsigned? Absolute?
1995 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
1996 if (-1 == isds_asprintf((char **) string,
1997 "%04d-%02d-%02dT%02d:%02d:%02d.%06ld",
1998 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
1999 broken.tm_hour, broken.tm_min, broken.tm_sec,
2000 time->tv_usec))
2001 return IE_ERROR;
2003 return IE_SUCCESS;
2005 #endif /* HAVE_LIBCURL */
2008 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
2009 * It respects microseconds too.
2010 * In case of error, @time will be freed. */
2011 static isds_error timestring2timeval(const xmlChar *string,
2012 struct timeval **time) {
2013 struct tm broken;
2014 char *offset, *delim, *endptr;
2015 char subseconds[7];
2016 int offset_hours, offset_minutes;
2017 int i;
2019 if (!time) return IE_INVAL;
2020 if (!string) {
2021 zfree(*time);
2022 return IE_INVAL;
2025 memset(&broken, 0, sizeof(broken));
2027 if (!*time) {
2028 *time = calloc(1, sizeof(**time));
2029 if (!*time) return IE_NOMEM;
2030 } else {
2031 memset(*time, 0, sizeof(**time));
2035 /* xsd:date is ISO 8601 string, thus ASCII */
2036 /*TODO: negative year */
2038 /* Parse date and time without subseconds and offset */
2039 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
2040 if (!offset) {
2041 zfree(*time);
2042 return IE_DATE;
2045 /* Get subseconds */
2046 if (*offset == '.' ) {
2047 offset++;
2049 /* Copy first 6 digits, pad it with zeros.
2050 * XXX: It truncates longer number, no round.
2051 * Current server implementation uses only millisecond resolution. */
2052 /* TODO: isdigit() is locale sensitive */
2053 for (i = 0;
2054 i < sizeof(subseconds)/sizeof(char) - 1 && isdigit(*offset);
2055 i++, offset++) {
2056 subseconds[i] = *offset;
2058 for (; i < sizeof(subseconds)/sizeof(char) - 1; i++) {
2059 subseconds[i] = '0';
2061 subseconds[6] = '\0';
2063 /* Convert it into integer */
2064 (*time)->tv_usec = strtol(subseconds, &endptr, 10);
2065 if (*endptr != '\0' || (*time)->tv_usec == LONG_MIN ||
2066 (*time)->tv_usec == LONG_MAX) {
2067 zfree(*time);
2068 return IE_DATE;
2071 /* move to the zone offset delimiter or signal NULL*/
2072 delim = strchr(offset, '-');
2073 if (!delim)
2074 delim = strchr(offset, '+');
2075 if (!delim)
2076 delim = strchr(offset, 'Z');
2077 offset = delim;
2080 /* Get zone offset */
2081 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2082 * "" equals to "Z" and it means UTC zone. */
2083 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2084 * colon separator */
2085 if (offset && (*offset == '-' || *offset == '+')) {
2086 if (2 != sscanf(offset + 1, "%2d:%2d", &offset_hours, &offset_minutes)) {
2087 zfree(*time);
2088 return IE_DATE;
2090 if (*offset == '+') {
2091 broken.tm_hour -= offset_hours;
2092 broken.tm_min -= offset_minutes;
2093 } else {
2094 broken.tm_hour += offset_hours;
2095 broken.tm_min += offset_minutes;
2099 /* Convert to time_t */
2100 (*time)->tv_sec = _isds_timegm(&broken);
2101 if ((*time)->tv_sec == (time_t) -1) {
2102 zfree(*time);
2103 return IE_DATE;
2106 return IE_SUCCESS;
2110 /* Convert unsigned int into isds_message_status.
2111 * @context is session context
2112 * @number is pointer to number value. NULL will be treated as invalid value.
2113 * @status is automatically reallocated status
2114 * @return IE_SUCCESS, or error code and free status */
2115 static isds_error uint2isds_message_status(struct isds_ctx *context,
2116 const unsigned long int *number, isds_message_status **status) {
2117 if (!context) return IE_INVALID_CONTEXT;
2118 if (!status) return IE_INVAL;
2120 free(*status); *status = NULL;
2121 if (!number) return IE_INVAL;
2123 if (*number < 1 || *number > 10) {
2124 isds_printf_message(context, _("Invalid message status value: %lu"),
2125 *number);
2126 return IE_ENUM;
2129 *status = malloc(sizeof(**status));
2130 if (!*status) return IE_NOMEM;
2132 **status = 1 << *number;
2133 return IE_SUCCESS;
2137 /* Convert event description string into isds_event members type and
2138 * description
2139 * @string is raw event description starting with event prefix
2140 * @event is structure where to store type and stripped description to
2141 * @return standard error code, unknown prefix is not classified as an error.
2142 * */
2143 static isds_error eventstring2event(const xmlChar *string,
2144 struct isds_event* event) {
2145 const xmlChar *known_prefixes[] = {
2146 BAD_CAST "EV0:",
2147 BAD_CAST "EV1:",
2148 BAD_CAST "EV2:",
2149 BAD_CAST "EV3:",
2150 BAD_CAST "EV4:",
2151 BAD_CAST "EV5:",
2152 BAD_CAST "EV11:",
2153 BAD_CAST "EV12:",
2154 BAD_CAST "EV13:"
2156 const isds_event_type types[] = {
2157 EVENT_ENTERED_SYSTEM,
2158 EVENT_ACCEPTED_BY_RECIPIENT,
2159 EVENT_ACCEPTED_BY_FICTION,
2160 EVENT_UNDELIVERABLE,
2161 EVENT_COMMERCIAL_ACCEPTED,
2162 EVENT_DELIVERED,
2163 EVENT_PRIMARY_LOGIN,
2164 EVENT_ENTRUSTED_LOGIN,
2165 EVENT_SYSCERT_LOGIN
2167 unsigned int index;
2168 size_t length;
2170 if (!string || !event) return IE_INVAL;
2172 if (!event->type) {
2173 event->type = malloc(sizeof(*event->type));
2174 if (!(event->type)) return IE_NOMEM;
2176 zfree(event->description);
2178 for (index = 0; index < sizeof(known_prefixes)/sizeof(known_prefixes[0]);
2179 index++) {
2180 length = xmlUTF8Strlen(known_prefixes[index]);
2182 if (!xmlStrncmp(string, known_prefixes[index], length)) {
2183 /* Prefix is known */
2184 *event->type = types[index];
2186 /* Strip prefix from description and spaces */
2187 /* TODO: Recognize all white spaces from UCS blank class and
2188 * operate on UTF-8 chars. */
2189 for (; string[length] != '\0' && string[length] == ' '; length++);
2190 event->description = strdup((char *) (string + length));
2191 if (!(event->description)) return IE_NOMEM;
2193 return IE_SUCCESS;
2197 /* Unknown event prefix.
2198 * XSD allows any string */
2199 char *string_locale = _isds_utf82locale((char *) string);
2200 isds_log(ILF_ISDS, ILL_WARNING,
2201 _("Unknown delivery info event prefix: %s\n"), string_locale);
2202 free(string_locale);
2204 *event->type = EVENT_UKNOWN;
2205 event->description = strdup((char *) string);
2206 if (!(event->description)) return IE_NOMEM;
2208 return IE_SUCCESS;
2212 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2213 * and leave label */
2214 #define EXTRACT_STRING(element, string) { \
2215 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2216 if (!result) { \
2217 err = IE_ERROR; \
2218 goto leave; \
2220 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2221 if (result->nodesetval->nodeNr > 1) { \
2222 isds_printf_message(context, _("Multiple %s element"), element); \
2223 err = IE_ERROR; \
2224 goto leave; \
2226 (string) = (char *) \
2227 xmlXPathCastNodeSetToString(result->nodesetval); \
2228 if (!(string)) { \
2229 err = IE_ERROR; \
2230 goto leave; \
2235 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2237 char *string = NULL; \
2238 EXTRACT_STRING(element, string); \
2240 if (string) { \
2241 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2242 if (!(booleanPtr)) { \
2243 free(string); \
2244 err = IE_NOMEM; \
2245 goto leave; \
2248 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2249 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2250 *(booleanPtr) = 1; \
2251 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2252 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2253 *(booleanPtr) = 0; \
2254 else { \
2255 char *string_locale = _isds_utf82locale((char*)string); \
2256 isds_printf_message(context, \
2257 _("%s value is not valid boolean: %s"), \
2258 element, string_locale); \
2259 free(string_locale); \
2260 free(string); \
2261 err = IE_ERROR; \
2262 goto leave; \
2265 free(string); \
2269 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2271 char *string = NULL; \
2272 EXTRACT_STRING(element, string); \
2273 if (string) { \
2274 long int number; \
2275 char *endptr; \
2277 number = strtol((char*)string, &endptr, 10); \
2279 if (*endptr != '\0') { \
2280 char *string_locale = _isds_utf82locale((char *)string); \
2281 isds_printf_message(context, \
2282 _("%s is not valid integer: %s"), \
2283 element, string_locale); \
2284 free(string_locale); \
2285 free(string); \
2286 err = IE_ISDS; \
2287 goto leave; \
2290 if (number == LONG_MIN || number == LONG_MAX) { \
2291 char *string_locale = _isds_utf82locale((char *)string); \
2292 isds_printf_message(context, \
2293 _("%s value out of range of long int: %s"), \
2294 element, string_locale); \
2295 free(string_locale); \
2296 free(string); \
2297 err = IE_ERROR; \
2298 goto leave; \
2301 free(string); string = NULL; \
2303 if (!(preallocated)) { \
2304 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2305 if (!(longintPtr)) { \
2306 err = IE_NOMEM; \
2307 goto leave; \
2310 *(longintPtr) = number; \
2314 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2316 char *string = NULL; \
2317 EXTRACT_STRING(element, string); \
2318 if (string) { \
2319 long int number; \
2320 char *endptr; \
2322 number = strtol((char*)string, &endptr, 10); \
2324 if (*endptr != '\0') { \
2325 char *string_locale = _isds_utf82locale((char *)string); \
2326 isds_printf_message(context, \
2327 _("%s is not valid integer: %s"), \
2328 element, string_locale); \
2329 free(string_locale); \
2330 free(string); \
2331 err = IE_ISDS; \
2332 goto leave; \
2335 if (number == LONG_MIN || number == LONG_MAX) { \
2336 char *string_locale = _isds_utf82locale((char *)string); \
2337 isds_printf_message(context, \
2338 _("%s value out of range of long int: %s"), \
2339 element, string_locale); \
2340 free(string_locale); \
2341 free(string); \
2342 err = IE_ERROR; \
2343 goto leave; \
2346 free(string); string = NULL; \
2347 if (number < 0) { \
2348 isds_printf_message(context, \
2349 _("%s value is negative: %ld"), element, number); \
2350 err = IE_ERROR; \
2351 goto leave; \
2354 if (!(preallocated)) { \
2355 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2356 if (!(ulongintPtr)) { \
2357 err = IE_NOMEM; \
2358 goto leave; \
2361 *(ulongintPtr) = number; \
2365 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2366 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2367 NULL); \
2368 if ((required) && (!string)) { \
2369 char *attribute_locale = _isds_utf82locale(attribute); \
2370 char *element_locale = \
2371 _isds_utf82locale((char *)xpath_ctx->node->name); \
2372 isds_printf_message(context, \
2373 _("Could not extract required %s attribute value from " \
2374 "%s element"), attribute_locale, element_locale); \
2375 free(element_locale); \
2376 free(attribute_locale); \
2377 err = IE_ERROR; \
2378 goto leave; \
2383 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2385 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2386 (xmlChar *) (string)); \
2387 if (!node) { \
2388 isds_printf_message(context, \
2389 _("Could not add %s child to %s element"), \
2390 element, (parent)->name); \
2391 err = IE_ERROR; \
2392 goto leave; \
2396 #define INSERT_STRING(parent, element, string) \
2397 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2399 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2401 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2402 else { INSERT_STRING(parent, element, "false"); } \
2405 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2407 if (booleanPtr) { \
2408 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2409 } else { \
2410 INSERT_STRING(parent, element, NULL); \
2414 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2415 if ((longintPtr)) { \
2416 /* FIXME: locale sensitive */ \
2417 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2418 err = IE_NOMEM; \
2419 goto leave; \
2421 INSERT_STRING(parent, element, buffer) \
2422 free(buffer); (buffer) = NULL; \
2423 } else { INSERT_STRING(parent, element, NULL) } \
2426 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2427 if ((ulongintPtr)) { \
2428 /* FIXME: locale sensitive */ \
2429 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2430 err = IE_NOMEM; \
2431 goto leave; \
2433 INSERT_STRING(parent, element, buffer) \
2434 free(buffer); (buffer) = NULL; \
2435 } else { INSERT_STRING(parent, element, NULL) } \
2438 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2440 /* FIXME: locale sensitive */ \
2441 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2442 err = IE_NOMEM; \
2443 goto leave; \
2445 INSERT_STRING(parent, element, buffer) \
2446 free(buffer); (buffer) = NULL; \
2449 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2450 * new attribute. */
2451 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2453 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2454 (xmlChar *) (string)); \
2455 if (!attribute_node) { \
2456 isds_printf_message(context, _("Could not add %s " \
2457 "attribute to %s element"), \
2458 (attribute), (parent)->name); \
2459 err = IE_ERROR; \
2460 goto leave; \
2464 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2465 if (string) { \
2466 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2467 if (length > (maximum)) { \
2468 isds_printf_message(context, \
2469 ngettext("%s has more than %d characters", \
2470 "%s has more than %d characters", (maximum)), \
2471 (name), (maximum)); \
2472 err = IE_2BIG; \
2473 goto leave; \
2475 if (length < (minimum)) { \
2476 isds_printf_message(context, \
2477 ngettext("%s has less than %d characters", \
2478 "%s has less than %d characters", (minimum)), \
2479 (name), (minimum)); \
2480 err = IE_2SMALL; \
2481 goto leave; \
2486 #define INSERT_ELEMENT(child, parent, element) \
2488 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2489 if (!(child)) { \
2490 isds_printf_message(context, \
2491 _("Could not add %s child to %s element"), \
2492 (element), (parent)->name); \
2493 err = IE_ERROR; \
2494 goto leave; \
2499 /* Find child element by name in given XPath context and switch context onto
2500 * it. The child must be uniq and must exist. Otherwise fails.
2501 * @context is ISDS context
2502 * @child is child element name
2503 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2504 * into it child. In error case, the @xpath_ctx keeps original value. */
2505 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
2506 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
2507 isds_error err = IE_SUCCESS;
2508 xmlXPathObjectPtr result = NULL;
2510 if (!context) return IE_INVALID_CONTEXT;
2511 if (!child || !xpath_ctx) return IE_INVAL;
2513 /* Find child */
2514 result = xmlXPathEvalExpression(child, xpath_ctx);
2515 if (!result) {
2516 err = IE_XML;
2517 goto leave;
2520 /* No match */
2521 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2522 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2523 char *child_locale = _isds_utf82locale((char*) child);
2524 isds_printf_message(context,
2525 _("%s element does not contain %s child"),
2526 parent_locale, child_locale);
2527 free(child_locale);
2528 free(parent_locale);
2529 err = IE_NOEXIST;
2530 goto leave;
2533 /* More matches */
2534 if (result->nodesetval->nodeNr > 1) {
2535 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2536 char *child_locale = _isds_utf82locale((char*) child);
2537 isds_printf_message(context,
2538 _("%s element contains multiple %s children"),
2539 parent_locale, child_locale);
2540 free(child_locale);
2541 free(parent_locale);
2542 err = IE_NOTUNIQ;
2543 goto leave;
2546 /* Switch context */
2547 xpath_ctx->node = result->nodesetval->nodeTab[0];
2549 leave:
2550 xmlXPathFreeObject(result);
2551 return err;
2556 #if HAVE_LIBCURL
2557 /* Find and convert XSD:gPersonName group in current node into structure
2558 * @context is ISDS context
2559 * @personName is automatically reallocated person name structure. If no member
2560 * value is found, will be freed.
2561 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2562 * elements
2563 * In case of error @personName will be freed. */
2564 static isds_error extract_gPersonName(struct isds_ctx *context,
2565 struct isds_PersonName **personName, xmlXPathContextPtr xpath_ctx) {
2566 isds_error err = IE_SUCCESS;
2567 xmlXPathObjectPtr result = NULL;
2569 if (!context) return IE_INVALID_CONTEXT;
2570 if (!personName) return IE_INVAL;
2571 isds_PersonName_free(personName);
2572 if (!xpath_ctx) return IE_INVAL;
2575 *personName = calloc(1, sizeof(**personName));
2576 if (!*personName) {
2577 err = IE_NOMEM;
2578 goto leave;
2581 EXTRACT_STRING("isds:pnFirstName", (*personName)->pnFirstName);
2582 EXTRACT_STRING("isds:pnMiddleName", (*personName)->pnMiddleName);
2583 EXTRACT_STRING("isds:pnLastName", (*personName)->pnLastName);
2584 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName)->pnLastNameAtBirth);
2586 if (!(*personName)->pnFirstName && !(*personName)->pnMiddleName &&
2587 !(*personName)->pnLastName && !(*personName)->pnLastNameAtBirth)
2588 isds_PersonName_free(personName);
2590 leave:
2591 if (err) isds_PersonName_free(personName);
2592 xmlXPathFreeObject(result);
2593 return err;
2597 /* Find and convert XSD:gAddress group in current node into structure
2598 * @context is ISDS context
2599 * @address is automatically reallocated address structure. If no member
2600 * value is found, will be freed.
2601 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2602 * elements
2603 * In case of error @address will be freed. */
2604 static isds_error extract_gAddress(struct isds_ctx *context,
2605 struct isds_Address **address, xmlXPathContextPtr xpath_ctx) {
2606 isds_error err = IE_SUCCESS;
2607 xmlXPathObjectPtr result = NULL;
2609 if (!context) return IE_INVALID_CONTEXT;
2610 if (!address) return IE_INVAL;
2611 isds_Address_free(address);
2612 if (!xpath_ctx) return IE_INVAL;
2615 *address = calloc(1, sizeof(**address));
2616 if (!*address) {
2617 err = IE_NOMEM;
2618 goto leave;
2621 EXTRACT_STRING("isds:adCity", (*address)->adCity);
2622 EXTRACT_STRING("isds:adStreet", (*address)->adStreet);
2623 EXTRACT_STRING("isds:adNumberInStreet", (*address)->adNumberInStreet);
2624 EXTRACT_STRING("isds:adNumberInMunicipality",
2625 (*address)->adNumberInMunicipality);
2626 EXTRACT_STRING("isds:adZipCode", (*address)->adZipCode);
2627 EXTRACT_STRING("isds:adState", (*address)->adState);
2629 if (!(*address)->adCity && !(*address)->adStreet &&
2630 !(*address)->adNumberInStreet &&
2631 !(*address)->adNumberInMunicipality &&
2632 !(*address)->adZipCode && !(*address)->adState)
2633 isds_Address_free(address);
2635 leave:
2636 if (err) isds_Address_free(address);
2637 xmlXPathFreeObject(result);
2638 return err;
2642 /* Find and convert isds:biDate element in current node into structure
2643 * @context is ISDS context
2644 * @biDate is automatically reallocated birth date structure. If no member
2645 * value is found, will be freed.
2646 * @xpath_ctx is XPath context with current node as parent for isds:biDate
2647 * element
2648 * In case of error @biDate will be freed. */
2649 static isds_error extract_BiDate(struct isds_ctx *context,
2650 struct tm **biDate, xmlXPathContextPtr xpath_ctx) {
2651 isds_error err = IE_SUCCESS;
2652 xmlXPathObjectPtr result = NULL;
2653 char *string = NULL;
2655 if (!context) return IE_INVALID_CONTEXT;
2656 if (!biDate) return IE_INVAL;
2657 zfree(*biDate);
2658 if (!xpath_ctx) return IE_INVAL;
2660 EXTRACT_STRING("isds:biDate", string);
2661 if (string) {
2662 *biDate = calloc(1, sizeof(**biDate));
2663 if (!*biDate) {
2664 err = IE_NOMEM;
2665 goto leave;
2667 err = datestring2tm((xmlChar *)string, *biDate);
2668 if (err) {
2669 if (err == IE_NOTSUP) {
2670 err = IE_ISDS;
2671 char *string_locale = _isds_utf82locale(string);
2672 isds_printf_message(context,
2673 _("Invalid isds:biDate value: %s"), string_locale);
2674 free(string_locale);
2676 goto leave;
2680 leave:
2681 if (err) zfree(*biDate);
2682 free(string);
2683 xmlXPathFreeObject(result);
2684 return err;
2688 /* Convert isds:dBOwnerInfo XML tree into structure
2689 * @context is ISDS context
2690 * @db_owner_info is automatically reallocated box owner info structure
2691 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
2692 * In case of error @db_owner_info will be freed. */
2693 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
2694 struct isds_DbOwnerInfo **db_owner_info,
2695 xmlXPathContextPtr xpath_ctx) {
2696 isds_error err = IE_SUCCESS;
2697 xmlXPathObjectPtr result = NULL;
2698 char *string = NULL;
2700 if (!context) return IE_INVALID_CONTEXT;
2701 if (!db_owner_info) return IE_INVAL;
2702 isds_DbOwnerInfo_free(db_owner_info);
2703 if (!xpath_ctx) return IE_INVAL;
2706 *db_owner_info = calloc(1, sizeof(**db_owner_info));
2707 if (!*db_owner_info) {
2708 err = IE_NOMEM;
2709 goto leave;
2712 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
2714 EXTRACT_STRING("isds:dbType", string);
2715 if (string) {
2716 (*db_owner_info)->dbType =
2717 calloc(1, sizeof(*((*db_owner_info)->dbType)));
2718 if (!(*db_owner_info)->dbType) {
2719 err = IE_NOMEM;
2720 goto leave;
2722 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
2723 if (err) {
2724 zfree((*db_owner_info)->dbType);
2725 if (err == IE_ENUM) {
2726 err = IE_ISDS;
2727 char *string_locale = _isds_utf82locale(string);
2728 isds_printf_message(context, _("Unknown isds:dbType: %s"),
2729 string_locale);
2730 free(string_locale);
2732 goto leave;
2734 zfree(string);
2737 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
2739 err = extract_gPersonName(context, &(*db_owner_info)->personName,
2740 xpath_ctx);
2741 if (err) goto leave;
2743 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
2745 (*db_owner_info)->birthInfo =
2746 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
2747 if (!(*db_owner_info)->birthInfo) {
2748 err = IE_NOMEM;
2749 goto leave;
2751 err = extract_BiDate(context, &(*db_owner_info)->birthInfo->biDate,
2752 xpath_ctx);
2753 if (err) goto leave;
2754 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
2755 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
2756 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
2757 if (!(*db_owner_info)->birthInfo->biDate &&
2758 !(*db_owner_info)->birthInfo->biCity &&
2759 !(*db_owner_info)->birthInfo->biCounty &&
2760 !(*db_owner_info)->birthInfo->biState)
2761 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
2763 err = extract_gAddress(context, &(*db_owner_info)->address, xpath_ctx);
2764 if (err) goto leave;
2766 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
2767 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
2768 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
2769 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
2770 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
2772 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
2774 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
2775 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
2776 (*db_owner_info)->dbOpenAddressing);
2778 leave:
2779 if (err) isds_DbOwnerInfo_free(db_owner_info);
2780 free(string);
2781 xmlXPathFreeObject(result);
2782 return err;
2786 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
2787 * @context is session context
2788 * @owner is libisds structure with box description
2789 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
2790 static isds_error insert_DbOwnerInfo(struct isds_ctx *context,
2791 const struct isds_DbOwnerInfo *owner, xmlNodePtr db_owner_info) {
2793 isds_error err = IE_SUCCESS;
2794 xmlNodePtr node;
2795 xmlChar *string = NULL;
2797 if (!context) return IE_INVALID_CONTEXT;
2798 if (!owner || !db_owner_info) return IE_INVAL;
2801 /* Build XSD:tDbOwnerInfo */
2802 CHECK_FOR_STRING_LENGTH(owner->dbID, 0, 7, "dbID")
2803 INSERT_STRING(db_owner_info, "dbID", owner->dbID);
2805 /* dbType */
2806 if (owner->dbType) {
2807 const xmlChar *type_string = isds_DbType2string(*(owner->dbType));
2808 if (!type_string) {
2809 isds_printf_message(context, _("Invalid dbType value: %d"),
2810 *(owner->dbType));
2811 err = IE_ENUM;
2812 goto leave;
2814 INSERT_STRING(db_owner_info, "dbType", type_string);
2816 INSERT_STRING(db_owner_info, "ic", owner->ic);
2817 if (owner->personName) {
2818 INSERT_STRING(db_owner_info, "pnFirstName",
2819 owner->personName->pnFirstName);
2820 INSERT_STRING(db_owner_info, "pnMiddleName",
2821 owner->personName->pnMiddleName);
2822 INSERT_STRING(db_owner_info, "pnLastName",
2823 owner->personName->pnLastName);
2824 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
2825 owner->personName->pnLastNameAtBirth);
2827 INSERT_STRING(db_owner_info, "firmName", owner->firmName);
2828 if (owner->birthInfo) {
2829 if (owner->birthInfo->biDate) {
2830 if (!tm2datestring(owner->birthInfo->biDate, &string))
2831 INSERT_STRING(db_owner_info, "biDate", string);
2832 free(string); string = NULL;
2834 INSERT_STRING(db_owner_info, "biCity", owner->birthInfo->biCity);
2835 INSERT_STRING(db_owner_info, "biCounty", owner->birthInfo->biCounty);
2836 INSERT_STRING(db_owner_info, "biState", owner->birthInfo->biState);
2838 if (owner->address) {
2839 INSERT_STRING(db_owner_info, "adCity", owner->address->adCity);
2840 INSERT_STRING(db_owner_info, "adStreet", owner->address->adStreet);
2841 INSERT_STRING(db_owner_info, "adNumberInStreet",
2842 owner->address->adNumberInStreet);
2843 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
2844 owner->address->adNumberInMunicipality);
2845 INSERT_STRING(db_owner_info, "adZipCode", owner->address->adZipCode);
2846 INSERT_STRING(db_owner_info, "adState", owner->address->adState);
2848 INSERT_STRING(db_owner_info, "nationality", owner->nationality);
2849 INSERT_STRING(db_owner_info, "email", owner->email);
2850 INSERT_STRING(db_owner_info, "telNumber", owner->telNumber);
2852 CHECK_FOR_STRING_LENGTH(owner->identifier, 0, 20, "identifier")
2853 INSERT_STRING(db_owner_info, "identifier", owner->identifier);
2855 CHECK_FOR_STRING_LENGTH(owner->registryCode, 0, 5, "registryCode")
2856 INSERT_STRING(db_owner_info, "registryCode", owner->registryCode);
2858 INSERT_LONGINT(db_owner_info, "dbState", owner->dbState, string);
2860 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", owner->dbEffectiveOVM);
2861 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
2862 owner->dbOpenAddressing);
2864 leave:
2865 free(string);
2866 return err;
2870 /* Convert XSD:tDbUserInfo XML tree into structure
2871 * @context is ISDS context
2872 * @db_user_info is automatically reallocated user info structure
2873 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
2874 * In case of error @db_user_info will be freed. */
2875 static isds_error extract_DbUserInfo(struct isds_ctx *context,
2876 struct isds_DbUserInfo **db_user_info, xmlXPathContextPtr xpath_ctx) {
2877 isds_error err = IE_SUCCESS;
2878 xmlXPathObjectPtr result = NULL;
2879 char *string = NULL;
2881 if (!context) return IE_INVALID_CONTEXT;
2882 if (!db_user_info) return IE_INVAL;
2883 isds_DbUserInfo_free(db_user_info);
2884 if (!xpath_ctx) return IE_INVAL;
2887 *db_user_info = calloc(1, sizeof(**db_user_info));
2888 if (!*db_user_info) {
2889 err = IE_NOMEM;
2890 goto leave;
2893 EXTRACT_STRING("isds:userID", (*db_user_info)->userID);
2895 EXTRACT_STRING("isds:userType", string);
2896 if (string) {
2897 (*db_user_info)->userType =
2898 calloc(1, sizeof(*((*db_user_info)->userType)));
2899 if (!(*db_user_info)->userType) {
2900 err = IE_NOMEM;
2901 goto leave;
2903 err = string2isds_UserType((xmlChar *)string,
2904 (*db_user_info)->userType);
2905 if (err) {
2906 zfree((*db_user_info)->userType);
2907 if (err == IE_ENUM) {
2908 err = IE_ISDS;
2909 char *string_locale = _isds_utf82locale(string);
2910 isds_printf_message(context,
2911 _("Unknown isds:userType value: %s"), string_locale);
2912 free(string_locale);
2914 goto leave;
2916 zfree(string);
2919 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info)->userPrivils, 0);
2921 (*db_user_info)->personName =
2922 calloc(1, sizeof(*((*db_user_info)->personName)));
2923 if (!(*db_user_info)->personName) {
2924 err = IE_NOMEM;
2925 goto leave;
2928 err = extract_gPersonName(context, &(*db_user_info)->personName,
2929 xpath_ctx);
2930 if (err) goto leave;
2932 err = extract_gAddress(context, &(*db_user_info)->address, xpath_ctx);
2933 if (err) goto leave;
2935 err = extract_BiDate(context, &(*db_user_info)->biDate, xpath_ctx);
2936 if (err) goto leave;
2938 EXTRACT_STRING("isds:ic", (*db_user_info)->ic);
2939 EXTRACT_STRING("isds:firmName", (*db_user_info)->firmName);
2941 EXTRACT_STRING("isds:caStreet", (*db_user_info)->caStreet);
2942 EXTRACT_STRING("isds:caCity", (*db_user_info)->caCity);
2943 EXTRACT_STRING("isds:caZipCode", (*db_user_info)->caZipCode);
2945 /* ???: Default value is "CZ" according specification. Should we provide
2946 * it? */
2947 EXTRACT_STRING("isds:caState", (*db_user_info)->caState);
2949 leave:
2950 if (err) isds_DbUserInfo_free(db_user_info);
2951 free(string);
2952 xmlXPathFreeObject(result);
2953 return err;
2957 /* Insert struct isds_DbUserInfo data (user description) into XML tree
2958 * @context is session context
2959 * @user is libisds structure with user description
2960 * @db_user_info is XML element of XSD:tDbUserInfo */
2961 static isds_error insert_DbUserInfo(struct isds_ctx *context,
2962 const struct isds_DbUserInfo *user, xmlNodePtr db_user_info) {
2964 isds_error err = IE_SUCCESS;
2965 xmlNodePtr node;
2966 xmlChar *string = NULL;
2968 if (!context) return IE_INVALID_CONTEXT;
2969 if (!user || !db_user_info) return IE_INVAL;
2971 /* Build XSD:tDbUserInfo */
2972 if (user->personName) {
2973 INSERT_STRING(db_user_info, "pnFirstName",
2974 user->personName->pnFirstName);
2975 INSERT_STRING(db_user_info, "pnMiddleName",
2976 user->personName->pnMiddleName);
2977 INSERT_STRING(db_user_info, "pnLastName",
2978 user->personName->pnLastName);
2979 INSERT_STRING(db_user_info, "pnLastNameAtBirth",
2980 user->personName->pnLastNameAtBirth);
2982 if (user->address) {
2983 INSERT_STRING(db_user_info, "adCity", user->address->adCity);
2984 INSERT_STRING(db_user_info, "adStreet", user->address->adStreet);
2985 INSERT_STRING(db_user_info, "adNumberInStreet",
2986 user->address->adNumberInStreet);
2987 INSERT_STRING(db_user_info, "adNumberInMunicipality",
2988 user->address->adNumberInMunicipality);
2989 INSERT_STRING(db_user_info, "adZipCode", user->address->adZipCode);
2990 INSERT_STRING(db_user_info, "adState", user->address->adState);
2992 if (user->biDate) {
2993 if (!tm2datestring(user->biDate, &string))
2994 INSERT_STRING(db_user_info, "biDate", string);
2995 zfree(string);
2997 CHECK_FOR_STRING_LENGTH(user->userID, 6, 12, "userID");
2998 INSERT_STRING(db_user_info, "userID", user->userID);
3000 /* userType */
3001 if (user->userType) {
3002 const xmlChar *type_string = isds_UserType2string(*(user->userType));
3003 if (!type_string) {
3004 isds_printf_message(context, _("Invalid userType value: %d"),
3005 *(user->userType));
3006 err = IE_ENUM;
3007 goto leave;
3009 INSERT_STRING(db_user_info, "userType", type_string);
3012 INSERT_LONGINT(db_user_info, "userPrivils", user->userPrivils, string);
3013 CHECK_FOR_STRING_LENGTH(user->ic, 0, 8, "ic")
3014 INSERT_STRING(db_user_info, "ic", user->ic);
3015 CHECK_FOR_STRING_LENGTH(user->firmName, 0, 100, "firmName")
3016 INSERT_STRING(db_user_info, "firmName", user->firmName);
3017 INSERT_STRING(db_user_info, "caStreet", user->caStreet);
3018 INSERT_STRING(db_user_info, "caCity", user->caCity);
3019 INSERT_STRING(db_user_info, "caZipCode", user->caZipCode);
3020 INSERT_STRING(db_user_info, "caState", user->caState);
3022 leave:
3023 free(string);
3024 return err;
3026 #endif /* HAVE_LIBCURL */
3029 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3030 * isds_envelope structure. The envelope is automatically allocated but not
3031 * reallocated. The date are just appended into envelope structure.
3032 * @context is ISDS context
3033 * @envelope is automatically allocated message envelope structure
3034 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3035 * In case of error @envelope will be freed. */
3036 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
3037 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3038 isds_error err = IE_SUCCESS;
3039 xmlXPathObjectPtr result = NULL;
3041 if (!context) return IE_INVALID_CONTEXT;
3042 if (!envelope) return IE_INVAL;
3043 if (!xpath_ctx) return IE_INVAL;
3046 if (!*envelope) {
3047 /* Allocate envelope */
3048 *envelope = calloc(1, sizeof(**envelope));
3049 if (!*envelope) {
3050 err = IE_NOMEM;
3051 goto leave;
3053 } else {
3054 /* Else free former data */
3055 zfree((*envelope)->dmSenderOrgUnit);
3056 zfree((*envelope)->dmSenderOrgUnitNum);
3057 zfree((*envelope)->dbIDRecipient);
3058 zfree((*envelope)->dmRecipientOrgUnit);
3059 zfree((*envelope)->dmSenderOrgUnitNum);
3060 zfree((*envelope)->dmToHands);
3061 zfree((*envelope)->dmAnnotation);
3062 zfree((*envelope)->dmRecipientRefNumber);
3063 zfree((*envelope)->dmSenderRefNumber);
3064 zfree((*envelope)->dmRecipientIdent);
3065 zfree((*envelope)->dmSenderIdent);
3066 zfree((*envelope)->dmLegalTitleLaw);
3067 zfree((*envelope)->dmLegalTitleYear);
3068 zfree((*envelope)->dmLegalTitleSect);
3069 zfree((*envelope)->dmLegalTitlePar);
3070 zfree((*envelope)->dmLegalTitlePoint);
3071 zfree((*envelope)->dmPersonalDelivery);
3072 zfree((*envelope)->dmAllowSubstDelivery);
3075 /* Extract envelope elements added by sender or ISDS
3076 * (XSD: gMessageEnvelopeSub type) */
3077 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
3078 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3079 (*envelope)->dmSenderOrgUnitNum, 0);
3080 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
3081 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
3082 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3083 (*envelope)->dmSenderOrgUnitNum, 0);
3084 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
3085 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
3086 EXTRACT_STRING("isds:dmRecipientRefNumber",
3087 (*envelope)->dmRecipientRefNumber);
3088 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
3089 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
3090 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
3092 /* Extract envelope elements regarding law reference */
3093 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
3094 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
3095 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
3096 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
3097 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
3099 /* Extract envelope other elements */
3100 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
3101 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3102 (*envelope)->dmAllowSubstDelivery);
3104 leave:
3105 if (err) isds_envelope_free(envelope);
3106 xmlXPathFreeObject(result);
3107 return err;
3112 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3113 * isds_envelope structure. The envelope is automatically allocated but not
3114 * reallocated. The date are just appended into envelope structure.
3115 * @context is ISDS context
3116 * @envelope is automatically allocated message envelope structure
3117 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3118 * In case of error @envelope will be freed. */
3119 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
3120 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3121 isds_error err = IE_SUCCESS;
3122 xmlXPathObjectPtr result = NULL;
3124 if (!context) return IE_INVALID_CONTEXT;
3125 if (!envelope) return IE_INVAL;
3126 if (!xpath_ctx) return IE_INVAL;
3129 if (!*envelope) {
3130 /* Allocate envelope */
3131 *envelope = calloc(1, sizeof(**envelope));
3132 if (!*envelope) {
3133 err = IE_NOMEM;
3134 goto leave;
3136 } else {
3137 /* Else free former data */
3138 zfree((*envelope)->dmID);
3139 zfree((*envelope)->dbIDSender);
3140 zfree((*envelope)->dmSender);
3141 zfree((*envelope)->dmSenderAddress);
3142 zfree((*envelope)->dmSenderType);
3143 zfree((*envelope)->dmRecipient);
3144 zfree((*envelope)->dmRecipientAddress);
3145 zfree((*envelope)->dmAmbiguousRecipient);
3148 /* Extract envelope elements added by ISDS
3149 * (XSD: gMessageEnvelope type) */
3150 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
3151 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
3152 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
3153 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
3154 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3155 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
3156 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
3157 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
3158 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3159 (*envelope)->dmAmbiguousRecipient);
3161 /* Extract envelope elements added by sender and ISDS
3162 * (XSD: gMessageEnvelope type) */
3163 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
3164 if (err) goto leave;
3166 leave:
3167 if (err) isds_envelope_free(envelope);
3168 xmlXPathFreeObject(result);
3169 return err;
3173 /* Convert other envelope elements from XML tree into isds_envelope structure:
3174 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3175 * The envelope is automatically allocated but not reallocated.
3176 * The data are just appended into envelope structure.
3177 * @context is ISDS context
3178 * @envelope is automatically allocated message envelope structure
3179 * @xpath_ctx is XPath context with current node as parent desired elements
3180 * In case of error @envelope will be freed. */
3181 static isds_error append_status_size_times(struct isds_ctx *context,
3182 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3183 isds_error err = IE_SUCCESS;
3184 xmlXPathObjectPtr result = NULL;
3185 char *string = NULL;
3186 unsigned long int *unumber = NULL;
3188 if (!context) return IE_INVALID_CONTEXT;
3189 if (!envelope) return IE_INVAL;
3190 if (!xpath_ctx) return IE_INVAL;
3193 if (!*envelope) {
3194 /* Allocate new */
3195 *envelope = calloc(1, sizeof(**envelope));
3196 if (!*envelope) {
3197 err = IE_NOMEM;
3198 goto leave;
3200 } else {
3201 /* Free old data */
3202 zfree((*envelope)->dmMessageStatus);
3203 zfree((*envelope)->dmAttachmentSize);
3204 zfree((*envelope)->dmDeliveryTime);
3205 zfree((*envelope)->dmAcceptanceTime);
3209 /* dmMessageStatus element is mandatory */
3210 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
3211 if (!unumber) {
3212 isds_log_message(context,
3213 _("Missing mandatory sisds:dmMessageStatus integer"));
3214 err = IE_ISDS;
3215 goto leave;
3217 err = uint2isds_message_status(context, unumber,
3218 &((*envelope)->dmMessageStatus));
3219 if (err) {
3220 if (err == IE_ENUM) err = IE_ISDS;
3221 goto leave;
3223 free(unumber); unumber = NULL;
3225 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
3228 EXTRACT_STRING("sisds:dmDeliveryTime", string);
3229 if (string) {
3230 err = timestring2timeval((xmlChar *) string,
3231 &((*envelope)->dmDeliveryTime));
3232 if (err) {
3233 char *string_locale = _isds_utf82locale(string);
3234 if (err == IE_DATE) err = IE_ISDS;
3235 isds_printf_message(context,
3236 _("Could not convert dmDeliveryTime as ISO time: %s"),
3237 string_locale);
3238 free(string_locale);
3239 goto leave;
3241 zfree(string);
3244 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
3245 if (string) {
3246 err = timestring2timeval((xmlChar *) string,
3247 &((*envelope)->dmAcceptanceTime));
3248 if (err) {
3249 char *string_locale = _isds_utf82locale(string);
3250 if (err == IE_DATE) err = IE_ISDS;
3251 isds_printf_message(context,
3252 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3253 string_locale);
3254 free(string_locale);
3255 goto leave;
3257 zfree(string);
3260 leave:
3261 if (err) isds_envelope_free(envelope);
3262 free(unumber);
3263 free(string);
3264 xmlXPathFreeObject(result);
3265 return err;
3269 /* Convert message type attribute of current element into isds_envelope
3270 * structure.
3271 * TODO: This function can be incorporated into append_status_size_times() as
3272 * they are called always together.
3273 * The envelope is automatically allocated but not reallocated.
3274 * The data are just appended into envelope structure.
3275 * @context is ISDS context
3276 * @envelope is automatically allocated message envelope structure
3277 * @xpath_ctx is XPath context with current node as parent of attribute
3278 * carrying message type
3279 * In case of error @envelope will be freed. */
3280 static isds_error append_message_type(struct isds_ctx *context,
3281 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3282 isds_error err = IE_SUCCESS;
3284 if (!context) return IE_INVALID_CONTEXT;
3285 if (!envelope) return IE_INVAL;
3286 if (!xpath_ctx) return IE_INVAL;
3289 if (!*envelope) {
3290 /* Allocate new */
3291 *envelope = calloc(1, sizeof(**envelope));
3292 if (!*envelope) {
3293 err = IE_NOMEM;
3294 goto leave;
3296 } else {
3297 /* Free old data */
3298 zfree((*envelope)->dmType);
3302 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope)->dmType, 0);
3304 if (!(*envelope)->dmType) {
3305 /* Use default value */
3306 (*envelope)->dmType = strdup("V");
3307 if (!(*envelope)->dmType) {
3308 err = IE_NOMEM;
3309 goto leave;
3311 } else if (1 != xmlUTF8Strlen((xmlChar *) (*envelope)->dmType)) {
3312 char *type_locale = _isds_utf82locale((*envelope)->dmType);
3313 isds_printf_message(context,
3314 _("Message type in dmType attribute is not 1 character long: "
3315 "%s"),
3316 type_locale);
3317 free(type_locale);
3318 err = IE_ISDS;
3319 goto leave;
3322 leave:
3323 if (err) isds_envelope_free(envelope);
3324 return err;
3328 #if HAVE_LIBCURL
3329 /* Convert dmType isds_envelope member into XML attribute and append it to
3330 * current node.
3331 * @context is ISDS context
3332 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3333 * @dm_envelope is XML element the resulting attribute will be appended to.
3334 * @return error code, in case of error context' message is filled. */
3335 static isds_error insert_message_type(struct isds_ctx *context,
3336 const char *type, xmlNodePtr dm_envelope) {
3337 isds_error err = IE_SUCCESS;
3338 xmlAttrPtr attribute_node;
3340 if (!context) return IE_INVALID_CONTEXT;
3341 if (!dm_envelope) return IE_INVAL;
3343 /* Insert optional message type */
3344 if (type) {
3345 if (1 != xmlUTF8Strlen((xmlChar *) type)) {
3346 char *type_locale = _isds_utf82locale(type);
3347 isds_printf_message(context,
3348 _("Message type in envelope is not 1 character long: %s"),
3349 type_locale);
3350 free(type_locale);
3351 err = IE_INVAL;
3352 goto leave;
3354 INSERT_STRING_ATTRIBUTE(dm_envelope, "dmType", type);
3357 leave:
3358 return err;
3360 #endif /* HAVE_LIBCURL */
3363 /* Extract message document into reallocated document structure
3364 * @context is ISDS context
3365 * @document is automatically reallocated message documents structure
3366 * @xpath_ctx is XPath context with current node as isds:dmFile
3367 * In case of error @document will be freed. */
3368 static isds_error extract_document(struct isds_ctx *context,
3369 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
3370 isds_error err = IE_SUCCESS;
3371 xmlXPathObjectPtr result = NULL;
3372 xmlNodePtr file_node = xpath_ctx->node;
3373 char *string = NULL;
3375 if (!context) return IE_INVALID_CONTEXT;
3376 if (!document) return IE_INVAL;
3377 isds_document_free(document);
3378 if (!xpath_ctx) return IE_INVAL;
3380 *document = calloc(1, sizeof(**document));
3381 if (!*document) {
3382 err = IE_NOMEM;
3383 goto leave;
3386 /* Extract document meta data */
3387 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
3388 if (context->normalize_mime_type) {
3389 char *normalized_type =
3390 isds_normalize_mime_type((*document)->dmMimeType);
3391 if (normalized_type && normalized_type != (*document)->dmMimeType) {
3392 char *new_type = strdup(normalized_type);
3393 if (!new_type) {
3394 isds_printf_message(context,
3395 _("Not enough memory to normalize document MIME type"));
3396 err = IE_NOMEM;
3397 goto leave;
3399 free((*document)->dmMimeType);
3400 (*document)->dmMimeType = new_type;
3404 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
3405 err = string2isds_FileMetaType((xmlChar*)string,
3406 &((*document)->dmFileMetaType));
3407 if (err) {
3408 char *meta_type_locale = _isds_utf82locale(string);
3409 isds_printf_message(context,
3410 _("Document has invalid dmFileMetaType attribute value: %s"),
3411 meta_type_locale);
3412 free(meta_type_locale);
3413 err = IE_ISDS;
3414 goto leave;
3416 zfree(string);
3418 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
3419 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
3420 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
3421 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
3424 /* Extract document data.
3425 * Base64 encoded blob or XML subtree must be presented. */
3427 /* Check for dmEncodedContent */
3428 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
3429 xpath_ctx);
3430 if (!result) {
3431 err = IE_XML;
3432 goto leave;
3435 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3436 /* Here we have Base64 blob */
3437 (*document)->is_xml = 0;
3439 if (result->nodesetval->nodeNr > 1) {
3440 isds_printf_message(context,
3441 _("Document has more dmEncodedContent elements"));
3442 err = IE_ISDS;
3443 goto leave;
3446 xmlXPathFreeObject(result); result = NULL;
3447 EXTRACT_STRING("isds:dmEncodedContent", string);
3449 /* Decode non-empty document */
3450 if (string && string[0] != '\0') {
3451 (*document)->data_length =
3452 _isds_b64decode(string, &((*document)->data));
3453 if ((*document)->data_length == (size_t) -1) {
3454 isds_printf_message(context,
3455 _("Error while Base64-decoding document content"));
3456 err = IE_ERROR;
3457 goto leave;
3460 } else {
3461 /* No Base64 blob, try XML document */
3462 xmlXPathFreeObject(result); result = NULL;
3463 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
3464 xpath_ctx);
3465 if (!result) {
3466 err = IE_XML;
3467 goto leave;
3470 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3471 /* Here we have XML document */
3472 (*document)->is_xml = 1;
3474 if (result->nodesetval->nodeNr > 1) {
3475 isds_printf_message(context,
3476 _("Document has more dmXMLContent elements"));
3477 err = IE_ISDS;
3478 goto leave;
3481 /* XXX: We cannot serialize the content simply because:
3482 * - XML document may point out of its scope (e.g. to message
3483 * envelope)
3484 * - isds:dmXMLContent can contain more elements, no element,
3485 * a text node only
3486 * - it's not the XML way
3487 * Thus we provide the only right solution: XML DOM. Let's
3488 * application to cope with this hot potato :) */
3489 (*document)->xml_node_list =
3490 result->nodesetval->nodeTab[0]->children;
3491 } else {
3492 /* No base64 blob, nor XML document */
3493 isds_printf_message(context,
3494 _("Document has no dmEncodedContent, nor dmXMLContent "
3495 "element"));
3496 err = IE_ISDS;
3497 goto leave;
3502 leave:
3503 if (err) isds_document_free(document);
3504 free(string);
3505 xmlXPathFreeObject(result);
3506 xpath_ctx->node = file_node;
3507 return err;
3512 /* Extract message documents into reallocated list of documents
3513 * @context is ISDS context
3514 * @documents is automatically reallocated message documents list structure
3515 * @xpath_ctx is XPath context with current node as XSD tFilesArray
3516 * In case of error @documents will be freed. */
3517 static isds_error extract_documents(struct isds_ctx *context,
3518 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
3519 isds_error err = IE_SUCCESS;
3520 xmlXPathObjectPtr result = NULL;
3521 xmlNodePtr files_node = xpath_ctx->node;
3522 struct isds_list *document, *prev_document = NULL;
3524 if (!context) return IE_INVALID_CONTEXT;
3525 if (!documents) return IE_INVAL;
3526 isds_list_free(documents);
3527 if (!xpath_ctx) return IE_INVAL;
3529 /* Find documents */
3530 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
3531 if (!result) {
3532 err = IE_XML;
3533 goto leave;
3536 /* No match */
3537 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3538 isds_printf_message(context,
3539 _("Message does not contain any document"));
3540 err = IE_ISDS;
3541 goto leave;
3545 /* Iterate over documents */
3546 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
3548 /* Allocate and append list item */
3549 document = calloc(1, sizeof(*document));
3550 if (!document) {
3551 err = IE_NOMEM;
3552 goto leave;
3554 document->destructor = (void (*)(void **))isds_document_free;
3555 if (i == 0) *documents = document;
3556 else prev_document->next = document;
3557 prev_document = document;
3559 /* Extract document */
3560 xpath_ctx->node = result->nodesetval->nodeTab[i];
3561 err = extract_document(context,
3562 (struct isds_document **) &(document->data), xpath_ctx);
3563 if (err) goto leave;
3567 leave:
3568 if (err) isds_list_free(documents);
3569 xmlXPathFreeObject(result);
3570 xpath_ctx->node = files_node;
3571 return err;
3575 #if HAVE_LIBCURL
3576 /* Convert isds:dmRecord XML tree into structure
3577 * @context is ISDS context
3578 * @envelope is automatically reallocated message envelope structure
3579 * @xpath_ctx is XPath context with current node as isds:dmRecord element
3580 * In case of error @envelope will be freed. */
3581 static isds_error extract_DmRecord(struct isds_ctx *context,
3582 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3583 isds_error err = IE_SUCCESS;
3584 xmlXPathObjectPtr result = NULL;
3586 if (!context) return IE_INVALID_CONTEXT;
3587 if (!envelope) return IE_INVAL;
3588 isds_envelope_free(envelope);
3589 if (!xpath_ctx) return IE_INVAL;
3592 *envelope = calloc(1, sizeof(**envelope));
3593 if (!*envelope) {
3594 err = IE_NOMEM;
3595 goto leave;
3599 /* Extract tRecord data */
3600 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
3602 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
3603 * dmAcceptanceTime. */
3604 err = append_status_size_times(context, envelope, xpath_ctx);
3605 if (err) goto leave;
3607 /* Extract envelope elements added by sender and ISDS
3608 * (XSD: gMessageEnvelope type) */
3609 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
3610 if (err) goto leave;
3612 /* Get message type */
3613 err = append_message_type(context, envelope, xpath_ctx);
3614 if (err) goto leave;
3617 leave:
3618 if (err) isds_envelope_free(envelope);
3619 xmlXPathFreeObject(result);
3620 return err;
3624 /* Convert XSD:tStateChangesRecord type XML tree into structure
3625 * @context is ISDS context
3626 * @changed_status is automatically reallocated message state change structure
3627 * @xpath_ctx is XPath context with current node as element of
3628 * XSD:tStateChangesRecord type
3629 * In case of error @changed_status will be freed. */
3630 static isds_error extract_StateChangesRecord(struct isds_ctx *context,
3631 struct isds_message_status_change **changed_status,
3632 xmlXPathContextPtr xpath_ctx) {
3633 isds_error err = IE_SUCCESS;
3634 xmlXPathObjectPtr result = NULL;
3635 unsigned long int *unumber = NULL;
3636 char *string = NULL;
3638 if (!context) return IE_INVALID_CONTEXT;
3639 if (!changed_status) return IE_INVAL;
3640 isds_message_status_change_free(changed_status);
3641 if (!xpath_ctx) return IE_INVAL;
3644 *changed_status = calloc(1, sizeof(**changed_status));
3645 if (!*changed_status) {
3646 err = IE_NOMEM;
3647 goto leave;
3651 /* Extract tGetStateChangesInput data */
3652 EXTRACT_STRING("isds:dmID", (*changed_status)->dmID);
3654 /* dmEventTime is mandatory */
3655 EXTRACT_STRING("isds:dmEventTime", string);
3656 if (string) {
3657 err = timestring2timeval((xmlChar *) string,
3658 &((*changed_status)->time));
3659 if (err) {
3660 char *string_locale = _isds_utf82locale(string);
3661 if (err == IE_DATE) err = IE_ISDS;
3662 isds_printf_message(context,
3663 _("Could not convert dmEventTime as ISO time: %s"),
3664 string_locale);
3665 free(string_locale);
3666 goto leave;
3668 zfree(string);
3671 /* dmMessageStatus element is mandatory */
3672 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber, 0);
3673 if (!unumber) {
3674 isds_log_message(context,
3675 _("Missing mandatory isds:dmMessageStatus integer"));
3676 err = IE_ISDS;
3677 goto leave;
3679 err = uint2isds_message_status(context, unumber,
3680 &((*changed_status)->dmMessageStatus));
3681 if (err) {
3682 if (err == IE_ENUM) err = IE_ISDS;
3683 goto leave;
3685 zfree(unumber);
3688 leave:
3689 free(unumber);
3690 free(string);
3691 if (err) isds_message_status_change_free(changed_status);
3692 xmlXPathFreeObject(result);
3693 return err;
3695 #endif /* HAVE_LIBCURL */
3698 /* Find and convert isds:dmHash XML tree into structure
3699 * @context is ISDS context
3700 * @envelope is automatically reallocated message hash structure
3701 * @xpath_ctx is XPath context with current node containing isds:dmHash child
3702 * In case of error @hash will be freed. */
3703 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
3704 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
3705 isds_error err = IE_SUCCESS;
3706 xmlNodePtr old_ctx_node;
3707 xmlXPathObjectPtr result = NULL;
3708 char *string = NULL;
3710 if (!context) return IE_INVALID_CONTEXT;
3711 if (!hash) return IE_INVAL;
3712 isds_hash_free(hash);
3713 if (!xpath_ctx) return IE_INVAL;
3715 old_ctx_node = xpath_ctx->node;
3717 *hash = calloc(1, sizeof(**hash));
3718 if (!*hash) {
3719 err = IE_NOMEM;
3720 goto leave;
3723 /* Locate dmHash */
3724 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
3725 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
3726 err = IE_ISDS;
3727 goto leave;
3729 if (err) {
3730 err = IE_ERROR;
3731 goto leave;
3734 /* Get hash algorithm */
3735 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
3736 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
3737 if (err) {
3738 if (err == IE_ENUM) {
3739 char *string_locale = _isds_utf82locale(string);
3740 isds_printf_message(context, _("Unsupported hash algorithm: %s"),
3741 string_locale);
3742 free(string_locale);
3744 goto leave;
3746 zfree(string);
3748 /* Get hash value */
3749 EXTRACT_STRING(".", string);
3750 if (!string) {
3751 isds_printf_message(context,
3752 _("sisds:dmHash element is missing hash value"));
3753 err = IE_ISDS;
3754 goto leave;
3756 (*hash)->length = _isds_b64decode(string, &((*hash)->value));
3757 if ((*hash)->length == (size_t) -1) {
3758 isds_printf_message(context,
3759 _("Error while Base64-decoding hash value"));
3760 err = IE_ERROR;
3761 goto leave;
3764 leave:
3765 if (err) isds_hash_free(hash);
3766 free(string);
3767 xmlXPathFreeObject(result);
3768 xpath_ctx->node = old_ctx_node;
3769 return err;
3773 /* Find and append isds:dmQTimestamp XML tree into envelope.
3774 * Because one service is allowed to miss time-stamp content, and we think
3775 * other could too (flaw in specification), this function is deliberated and
3776 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
3777 * @context is ISDS context
3778 * @envelope is automatically allocated envelope structure
3779 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
3780 * child
3781 * In case of error @envelope will be freed. */
3782 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
3783 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3784 isds_error err = IE_SUCCESS;
3785 xmlXPathObjectPtr result = NULL;
3786 char *string = NULL;
3788 if (!context) return IE_INVALID_CONTEXT;
3789 if (!envelope) return IE_INVAL;
3790 if (!xpath_ctx) {
3791 isds_envelope_free(envelope);
3792 return IE_INVAL;
3795 if (!*envelope) {
3796 *envelope = calloc(1, sizeof(**envelope));
3797 if (!*envelope) {
3798 err = IE_NOMEM;
3799 goto leave;
3801 } else {
3802 zfree((*envelope)->timestamp);
3803 (*envelope)->timestamp_length = 0;
3806 /* Get dmQTimestamp */
3807 EXTRACT_STRING("sisds:dmQTimestamp", string);
3808 if (!string) {
3809 isds_log(ILF_ISDS, ILL_INFO, _("Missing dmQTimestamp element content\n"));
3810 goto leave;
3812 (*envelope)->timestamp_length =
3813 _isds_b64decode(string, &((*envelope)->timestamp));
3814 if ((*envelope)->timestamp_length == (size_t) -1) {
3815 isds_printf_message(context,
3816 _("Error while Base64-decoding time stamp value"));
3817 err = IE_ERROR;
3818 goto leave;
3821 leave:
3822 if (err) isds_envelope_free(envelope);
3823 free(string);
3824 xmlXPathFreeObject(result);
3825 return err;
3829 /* Convert XSD tReturnedMessage XML tree into message structure.
3830 * It does not store serialized XML tree into message->raw.
3831 * It does store (pointer to) parsed XML tree into message->xml if needed.
3832 * @context is ISDS context
3833 * @include_documents Use true if documents must be extracted
3834 * (tReturnedMessage XSD type), use false if documents shall be omitted
3835 * (tReturnedMessageEnvelope).
3836 * @message is automatically reallocated message structure
3837 * @xpath_ctx is XPath context with current node as tReturnedMessage element
3838 * type
3839 * In case of error @message will be freed. */
3840 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
3841 const _Bool include_documents, struct isds_message **message,
3842 xmlXPathContextPtr xpath_ctx) {
3843 isds_error err = IE_SUCCESS;
3844 xmlNodePtr message_node;
3846 if (!context) return IE_INVALID_CONTEXT;
3847 if (!message) return IE_INVAL;
3848 isds_message_free(message);
3849 if (!xpath_ctx) return IE_INVAL;
3852 *message = calloc(1, sizeof(**message));
3853 if (!*message) {
3854 err = IE_NOMEM;
3855 goto leave;
3858 /* Save message XPATH context node */
3859 message_node = xpath_ctx->node;
3862 /* Extract dmDM */
3863 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
3864 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
3865 if (err) { err = IE_ERROR; goto leave; }
3866 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
3867 if (err) goto leave;
3869 if (include_documents) {
3870 struct isds_list *item;
3872 /* Extract dmFiles */
3873 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
3874 xpath_ctx);
3875 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
3876 err = IE_ISDS; goto leave;
3878 if (err) { err = IE_ERROR; goto leave; }
3879 err = extract_documents(context, &((*message)->documents), xpath_ctx);
3880 if (err) goto leave;
3882 /* Store xmlDoc of this message if needed */
3883 /* Only if we got a XML document in all the documents. */
3884 for (item = (*message)->documents; item; item = item->next) {
3885 if (item->data && ((struct isds_document *)item->data)->is_xml) {
3886 (*message)->xml = xpath_ctx->doc;
3887 break;
3893 /* Restore context to message */
3894 xpath_ctx->node = message_node;
3896 /* Extract dmHash */
3897 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
3898 xpath_ctx);
3899 if (err) goto leave;
3901 /* Extract dmQTimestamp, */
3902 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
3903 xpath_ctx);
3904 if (err) goto leave;
3906 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
3907 * dmAcceptanceTime. */
3908 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
3909 if (err) goto leave;
3911 /* Get message type */
3912 err = append_message_type(context, &((*message)->envelope), xpath_ctx);
3913 if (err) goto leave;
3915 leave:
3916 if (err) isds_message_free(message);
3917 return err;
3921 /* Extract message event into reallocated isds_event structure
3922 * @context is ISDS context
3923 * @event is automatically reallocated message event structure
3924 * @xpath_ctx is XPath context with current node as isds:dmEvent
3925 * In case of error @event will be freed. */
3926 static isds_error extract_event(struct isds_ctx *context,
3927 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
3928 isds_error err = IE_SUCCESS;
3929 xmlXPathObjectPtr result = NULL;
3930 xmlNodePtr event_node = xpath_ctx->node;
3931 char *string = NULL;
3933 if (!context) return IE_INVALID_CONTEXT;
3934 if (!event) return IE_INVAL;
3935 isds_event_free(event);
3936 if (!xpath_ctx) return IE_INVAL;
3938 *event = calloc(1, sizeof(**event));
3939 if (!*event) {
3940 err = IE_NOMEM;
3941 goto leave;
3944 /* Extract event data.
3945 * All elements are optional according XSD. That's funny. */
3946 EXTRACT_STRING("sisds:dmEventTime", string);
3947 if (string) {
3948 err = timestring2timeval((xmlChar *) string, &((*event)->time));
3949 if (err) {
3950 char *string_locale = _isds_utf82locale(string);
3951 if (err == IE_DATE) err = IE_ISDS;
3952 isds_printf_message(context,
3953 _("Could not convert dmEventTime as ISO time: %s"),
3954 string_locale);
3955 free(string_locale);
3956 goto leave;
3958 zfree(string);
3961 /* dmEventDescr element has prefix and the rest */
3962 EXTRACT_STRING("sisds:dmEventDescr", string);
3963 if (string) {
3964 err = eventstring2event((xmlChar *) string, *event);
3965 if (err) goto leave;
3966 zfree(string);
3969 leave:
3970 if (err) isds_event_free(event);
3971 free(string);
3972 xmlXPathFreeObject(result);
3973 xpath_ctx->node = event_node;
3974 return err;
3978 /* Convert element of XSD tEventsArray type from XML tree into
3979 * isds_list of isds_event's structure. The list is automatically reallocated.
3980 * @context is ISDS context
3981 * @events is automatically reallocated list of event structures
3982 * @xpath_ctx is XPath context with current node as tEventsArray
3983 * In case of error @events will be freed. */
3984 static isds_error extract_events(struct isds_ctx *context,
3985 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
3986 isds_error err = IE_SUCCESS;
3987 xmlXPathObjectPtr result = NULL;
3988 xmlNodePtr events_node = xpath_ctx->node;
3989 struct isds_list *event, *prev_event = NULL;
3991 if (!context) return IE_INVALID_CONTEXT;
3992 if (!events) return IE_INVAL;
3993 if (!xpath_ctx) return IE_INVAL;
3995 /* Free old list */
3996 isds_list_free(events);
3998 /* Find events */
3999 result = xmlXPathEvalExpression(BAD_CAST "sisds:dmEvent", xpath_ctx);
4000 if (!result) {
4001 err = IE_XML;
4002 goto leave;
4005 /* No match */
4006 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4007 isds_printf_message(context,
4008 _("Delivery info does not contain any event"));
4009 err = IE_ISDS;
4010 goto leave;
4014 /* Iterate over events */
4015 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4017 /* Allocate and append list item */
4018 event = calloc(1, sizeof(*event));
4019 if (!event) {
4020 err = IE_NOMEM;
4021 goto leave;
4023 event->destructor = (void (*)(void **))isds_event_free;
4024 if (i == 0) *events = event;
4025 else prev_event->next = event;
4026 prev_event = event;
4028 /* Extract event */
4029 xpath_ctx->node = result->nodesetval->nodeTab[i];
4030 err = extract_event(context,
4031 (struct isds_event **) &(event->data), xpath_ctx);
4032 if (err) goto leave;
4036 leave:
4037 if (err) isds_list_free(events);
4038 xmlXPathFreeObject(result);
4039 xpath_ctx->node = events_node;
4040 return err;
4044 #if HAVE_LIBCURL
4045 /* Insert Base64 encoded data as element with text child.
4046 * @context is session context
4047 * @parent is XML node to append @element with @data as child
4048 * @ns is XML namespace of @element, use NULL to inherit from @parent
4049 * @element is UTF-8 encoded name of new element
4050 * @data is bit stream to encode into @element
4051 * @length is size of @data in bytes
4052 * @return standard error code and fill long error message if needed */
4053 static isds_error insert_base64_encoded_string(struct isds_ctx *context,
4054 xmlNodePtr parent, const xmlNsPtr ns, const char *element,
4055 const void *data, size_t length) {
4056 isds_error err = IE_SUCCESS;
4057 xmlNodePtr node;
4059 if (!context) return IE_INVALID_CONTEXT;
4060 if (!data && length > 0) return IE_INVAL;
4061 if (!parent || !element) return IE_INVAL;
4063 xmlChar *base64data = NULL;
4064 base64data = (xmlChar *) _isds_b64encode(data, length);
4065 if (!base64data) {
4066 isds_printf_message(context,
4067 ngettext("Not enough memory to encode %zd byte into Base64",
4068 "Not enough memory to encode %zd bytes into Base64",
4069 length),
4070 length);
4071 err = IE_NOMEM;
4072 goto leave;
4074 INSERT_STRING_WITH_NS(parent, ns, element, base64data);
4076 leave:
4077 free(base64data);
4078 return err;
4082 /* Convert isds_document structure into XML tree and append to dmFiles node.
4083 * @context is session context
4084 * @document is ISDS document
4085 * @dm_files is XML element the resulting tree will be appended to as a child.
4086 * @return error code, in case of error context' message is filled. */
4087 static isds_error insert_document(struct isds_ctx *context,
4088 struct isds_document *document, xmlNodePtr dm_files) {
4089 isds_error err = IE_SUCCESS;
4090 xmlNodePtr new_file = NULL, file = NULL, node;
4091 xmlAttrPtr attribute_node;
4093 if (!context) return IE_INVALID_CONTEXT;
4094 if (!document || !dm_files) return IE_INVAL;
4096 /* Allocate new dmFile */
4097 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
4098 if (!new_file) {
4099 isds_printf_message(context, _("Could not allocate main dmFile"));
4100 err = IE_ERROR;
4101 goto leave;
4103 /* Append the new dmFile.
4104 * XXX: Main document must go first */
4105 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
4106 file = xmlAddPrevSibling(dm_files->children, new_file);
4107 else
4108 file = xmlAddChild(dm_files, new_file);
4110 if (!file) {
4111 xmlFreeNode(new_file); new_file = NULL;
4112 isds_printf_message(context, _("Could not add dmFile child to "
4113 "%s element"), dm_files->name);
4114 err = IE_ERROR;
4115 goto leave;
4118 /* @dmMimeType is required */
4119 if (!document->dmMimeType) {
4120 isds_log_message(context,
4121 _("Document is missing mandatory MIME type definition"));
4122 err = IE_INVAL;
4123 goto leave;
4125 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
4127 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
4128 if (!string) {
4129 isds_printf_message(context,
4130 _("Document has unknown dmFileMetaType: %ld"),
4131 document->dmFileMetaType);
4132 err = IE_ENUM;
4133 goto leave;
4135 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
4137 if (document->dmFileGuid) {
4138 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
4140 if (document->dmUpFileGuid) {
4141 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
4144 /* @dmFileDescr is required */
4145 if (!document->dmFileDescr) {
4146 isds_log_message(context,
4147 _("Document is missing mandatory description (title)"));
4148 err = IE_INVAL;
4149 goto leave;
4151 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
4153 if (document->dmFormat) {
4154 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
4158 /* Insert content (body) of the document. */
4159 if (document->is_xml) {
4160 /* XML document requested */
4162 /* Allocate new dmXMLContent */
4163 xmlNodePtr xmlcontent = xmlNewNode(file->ns, BAD_CAST "dmXMLContent");
4164 if (!xmlcontent) {
4165 isds_printf_message(context,
4166 _("Could not allocate dmXMLContent element"));
4167 err = IE_ERROR;
4168 goto leave;
4170 /* Append it */
4171 node = xmlAddChild(file, xmlcontent);
4172 if (!node) {
4173 xmlFreeNode(xmlcontent); xmlcontent = NULL;
4174 isds_printf_message(context,
4175 _("Could not add dmXMLContent child to %s element"),
4176 file->name);
4177 err = IE_ERROR;
4178 goto leave;
4181 /* Copy non-empty node list */
4182 if (document->xml_node_list) {
4183 xmlNodePtr content = xmlDocCopyNodeList(node->doc,
4184 document->xml_node_list);
4185 if (!content) {
4186 isds_printf_message(context,
4187 _("Not enough memory to copy XML document"));
4188 err = IE_NOMEM;
4189 goto leave;
4192 if (!xmlAddChildList(node, content)) {
4193 xmlFreeNodeList(content);
4194 isds_printf_message(context,
4195 _("Error while adding XML document into dmXMLContent"));
4196 err = IE_XML;
4197 goto leave;
4199 /* XXX: We cannot free the content here because it's part of node's
4200 * document since now. It will be freed with it automatically. */
4202 } else {
4203 /* Binary document requested */
4204 err = insert_base64_encoded_string(context, file, NULL, "dmEncodedContent",
4205 document->data, document->data_length);
4206 if (err) goto leave;
4209 leave:
4210 return err;
4214 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4215 * The copy must be preallocated, the date are just appended into structure.
4216 * @context is ISDS context
4217 * @copy is message copy structure
4218 * @xpath_ctx is XPath context with current node as tMStatus */
4219 static isds_error append_TMStatus(struct isds_ctx *context,
4220 struct isds_message_copy *copy, xmlXPathContextPtr xpath_ctx) {
4221 isds_error err = IE_SUCCESS;
4222 xmlXPathObjectPtr result = NULL;
4223 char *code = NULL, *message = NULL;
4225 if (!context) return IE_INVALID_CONTEXT;
4226 if (!copy || !xpath_ctx) return IE_INVAL;
4228 /* Free old values */
4229 zfree(copy->dmStatus);
4230 zfree(copy->dmID);
4232 /* Get error specific to this copy */
4233 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code);
4234 if (!code) {
4235 isds_log_message(context,
4236 _("Missing isds:dmStatusCode under "
4237 "XSD:tMStatus type element"));
4238 err = IE_ISDS;
4239 goto leave;
4242 if (xmlStrcmp((const xmlChar *)code, BAD_CAST "0000")) {
4243 /* This copy failed */
4244 copy->error = IE_ISDS;
4245 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message);
4246 if (message) {
4247 copy->dmStatus = _isds_astrcat3(code, ": ", message);
4248 if (!copy->dmStatus) {
4249 copy->dmStatus = code;
4250 code = NULL;
4252 } else {
4253 copy->dmStatus = code;
4254 code = NULL;
4256 } else {
4257 /* This copy succeeded. In this case only, message ID is valid */
4258 copy->error = IE_SUCCESS;
4260 EXTRACT_STRING("isds:dmID", copy->dmID);
4261 if (!copy->dmID) {
4262 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
4263 "but did not returned assigned message ID\n"));
4264 err = IE_ISDS;
4268 leave:
4269 free(code);
4270 free(message);
4271 xmlXPathFreeObject(result);
4272 return err;
4276 /* Insert struct isds_approval data (box approval) into XML tree
4277 * @context is session context
4278 * @approval is libisds structure with approval description. NULL is
4279 * acceptable.
4280 * @parent is XML element to append @approval to */
4281 static isds_error insert_GExtApproval(struct isds_ctx *context,
4282 const struct isds_approval *approval, xmlNodePtr parent) {
4284 isds_error err = IE_SUCCESS;
4285 xmlNodePtr node;
4287 if (!context) return IE_INVALID_CONTEXT;
4288 if (!parent) return IE_INVAL;
4290 if (!approval) return IE_SUCCESS;
4292 /* Build XSD:gExtApproval */
4293 INSERT_SCALAR_BOOLEAN(parent, "dbApproved", approval->approved);
4294 INSERT_STRING(parent, "dbExternRefNumber", approval->refference);
4296 leave:
4297 return err;
4301 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4302 * code
4303 * @context is session context
4304 * @service_name is name of SERVICE_DB_ACCESS
4305 * @response is server SOAP body response as XML document
4306 * @raw_response is automatically reallocated bit stream with response body. Use
4307 * NULL if you don't care
4308 * @raw_response_length is size of @raw_response in bytes
4309 * @code is ISDS status code
4310 * @status_message is ISDS status message
4311 * @return error coded from lower layer, context message will be set up
4312 * appropriately. */
4313 static isds_error build_send_check_dbdummy_request(struct isds_ctx *context,
4314 const xmlChar *service_name,
4315 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
4316 xmlChar **code, xmlChar **status_message) {
4318 isds_error err = IE_SUCCESS;
4319 char *service_name_locale = NULL;
4320 xmlNodePtr request = NULL, node;
4321 xmlNsPtr isds_ns = NULL;
4323 if (!context) return IE_INVALID_CONTEXT;
4324 if (!service_name) return IE_INVAL;
4325 if (!response || !code || !status_message) return IE_INVAL;
4326 if (!raw_response_length && raw_response) return IE_INVAL;
4328 /* Free output argument */
4329 xmlFreeDoc(*response); *response = NULL;
4330 if (raw_response) zfree(*raw_response);
4331 free(*code);
4332 free(*status_message);
4335 /* Check if connection is established
4336 * TODO: This check should be done downstairs. */
4337 if (!context->curl) return IE_CONNECTION_CLOSED;
4339 service_name_locale = _isds_utf82locale((char*)service_name);
4340 if (!service_name_locale) {
4341 err = IE_NOMEM;
4342 goto leave;
4345 /* Build request */
4346 request = xmlNewNode(NULL, service_name);
4347 if (!request) {
4348 isds_printf_message(context,
4349 _("Could not build %s request"), service_name_locale);
4350 err = IE_ERROR;
4351 goto leave;
4353 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4354 if(!isds_ns) {
4355 isds_log_message(context, _("Could not create ISDS name space"));
4356 err = IE_ERROR;
4357 goto leave;
4359 xmlSetNs(request, isds_ns);
4362 /* Add XSD:tDummyInput child */
4363 INSERT_STRING(request, "dbDummy", NULL);
4366 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
4367 service_name_locale);
4369 /* Send request */
4370 err = isds(context, SERVICE_DB_ACCESS, request, response,
4371 raw_response, raw_response_length);
4372 xmlFreeNode(request); request = NULL;
4374 if (err) {
4375 isds_log(ILF_ISDS, ILL_DEBUG,
4376 _("Processing ISDS response on %s request failed\n"),
4377 service_name_locale);
4378 goto leave;
4381 /* Check for response status */
4382 err = isds_response_status(context, SERVICE_DB_ACCESS, *response,
4383 code, status_message, NULL);
4384 if (err) {
4385 isds_log(ILF_ISDS, ILL_DEBUG,
4386 _("ISDS response on %s request is missing status\n"),
4387 service_name_locale);
4388 goto leave;
4391 /* Request processed, but nothing found */
4392 if (xmlStrcmp(*code, BAD_CAST "0000")) {
4393 char *code_locale = _isds_utf82locale((char*) *code);
4394 char *status_message_locale =
4395 _isds_utf82locale((char*) *status_message);
4396 isds_log(ILF_ISDS, ILL_DEBUG,
4397 _("Server refused %s request (code=%s, message=%s)\n"),
4398 service_name_locale, code_locale, status_message_locale);
4399 isds_log_message(context, status_message_locale);
4400 free(code_locale);
4401 free(status_message_locale);
4402 err = IE_ISDS;
4403 goto leave;
4406 leave:
4407 free(service_name_locale);
4408 xmlFreeNode(request);
4409 return err;
4411 #endif
4414 /* Get data about logged in user and his box. */
4415 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
4416 struct isds_DbOwnerInfo **db_owner_info) {
4417 isds_error err = IE_SUCCESS;
4418 #if HAVE_LIBCURL
4419 xmlDocPtr response = NULL;
4420 xmlChar *code = NULL, *message = NULL;
4421 xmlXPathContextPtr xpath_ctx = NULL;
4422 xmlXPathObjectPtr result = NULL;
4423 char *string = NULL;
4424 #endif
4426 if (!context) return IE_INVALID_CONTEXT;
4427 zfree(context->long_message);
4428 if (!db_owner_info) return IE_INVAL;
4429 isds_DbOwnerInfo_free(db_owner_info);
4431 #if HAVE_LIBCURL
4432 /* Check if connection is established */
4433 if (!context->curl) return IE_CONNECTION_CLOSED;
4436 /* Do request and check for success */
4437 err = build_send_check_dbdummy_request(context,
4438 BAD_CAST "GetOwnerInfoFromLogin",
4439 &response, NULL, NULL, &code, &message);
4440 if (err) goto leave;
4443 /* Extract data */
4444 /* Prepare structure */
4445 *db_owner_info = calloc(1, sizeof(**db_owner_info));
4446 if (!*db_owner_info) {
4447 err = IE_NOMEM;
4448 goto leave;
4450 xpath_ctx = xmlXPathNewContext(response);
4451 if (!xpath_ctx) {
4452 err = IE_ERROR;
4453 goto leave;
4455 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4456 err = IE_ERROR;
4457 goto leave;
4460 /* Set context node */
4461 result = xmlXPathEvalExpression(BAD_CAST
4462 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
4463 if (!result) {
4464 err = IE_ERROR;
4465 goto leave;
4467 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4468 isds_log_message(context, _("Missing dbOwnerInfo element"));
4469 err = IE_ISDS;
4470 goto leave;
4472 if (result->nodesetval->nodeNr > 1) {
4473 isds_log_message(context, _("Multiple dbOwnerInfo element"));
4474 err = IE_ISDS;
4475 goto leave;
4477 xpath_ctx->node = result->nodesetval->nodeTab[0];
4478 xmlXPathFreeObject(result); result = NULL;
4480 /* Extract it */
4481 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
4484 leave:
4485 if (err) {
4486 isds_DbOwnerInfo_free(db_owner_info);
4489 free(string);
4490 xmlXPathFreeObject(result);
4491 xmlXPathFreeContext(xpath_ctx);
4493 free(code);
4494 free(message);
4495 xmlFreeDoc(response);
4497 if (!err)
4498 isds_log(ILF_ISDS, ILL_DEBUG,
4499 _("GetOwnerInfoFromLogin request processed by server "
4500 "successfully.\n"));
4501 #else /* not HAVE_LIBCURL */
4502 err = IE_NOTSUP;
4503 #endif
4505 return err;
4509 /* Get data about logged in user. */
4510 isds_error isds_GetUserInfoFromLogin(struct isds_ctx *context,
4511 struct isds_DbUserInfo **db_user_info) {
4512 isds_error err = IE_SUCCESS;
4513 #if HAVE_LIBCURL
4514 xmlDocPtr response = NULL;
4515 xmlChar *code = NULL, *message = NULL;
4516 xmlXPathContextPtr xpath_ctx = NULL;
4517 xmlXPathObjectPtr result = NULL;
4518 #endif
4520 if (!context) return IE_INVALID_CONTEXT;
4521 zfree(context->long_message);
4522 if (!db_user_info) return IE_INVAL;
4523 isds_DbUserInfo_free(db_user_info);
4525 #if HAVE_LIBCURL
4526 /* Check if connection is established */
4527 if (!context->curl) return IE_CONNECTION_CLOSED;
4530 /* Do request and check for success */
4531 err = build_send_check_dbdummy_request(context,
4532 BAD_CAST "GetUserInfoFromLogin",
4533 &response, NULL, NULL, &code, &message);
4534 if (err) goto leave;
4537 /* Extract data */
4538 /* Prepare structure */
4539 *db_user_info = calloc(1, sizeof(**db_user_info));
4540 if (!*db_user_info) {
4541 err = IE_NOMEM;
4542 goto leave;
4544 xpath_ctx = xmlXPathNewContext(response);
4545 if (!xpath_ctx) {
4546 err = IE_ERROR;
4547 goto leave;
4549 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4550 err = IE_ERROR;
4551 goto leave;
4554 /* Set context node */
4555 result = xmlXPathEvalExpression(BAD_CAST
4556 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx);
4557 if (!result) {
4558 err = IE_ERROR;
4559 goto leave;
4561 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4562 isds_log_message(context, _("Missing dbUserInfo element"));
4563 err = IE_ISDS;
4564 goto leave;
4566 if (result->nodesetval->nodeNr > 1) {
4567 isds_log_message(context, _("Multiple dbUserInfo element"));
4568 err = IE_ISDS;
4569 goto leave;
4571 xpath_ctx->node = result->nodesetval->nodeTab[0];
4572 xmlXPathFreeObject(result); result = NULL;
4574 /* Extract it */
4575 err = extract_DbUserInfo(context, db_user_info, xpath_ctx);
4577 leave:
4578 if (err) {
4579 isds_DbUserInfo_free(db_user_info);
4582 xmlXPathFreeObject(result);
4583 xmlXPathFreeContext(xpath_ctx);
4585 free(code);
4586 free(message);
4587 xmlFreeDoc(response);
4589 if (!err)
4590 isds_log(ILF_ISDS, ILL_DEBUG,
4591 _("GetUserInfoFromLogin request processed by server "
4592 "successfully.\n"));
4593 #else /* not HAVE_LIBCURL */
4594 err = IE_NOTSUP;
4595 #endif
4597 return err;
4601 /* Get expiration time of current password
4602 * @context is session context
4603 * @expiration is automatically reallocated time when password expires. If
4604 * password expiration is disables, NULL will be returned. In case of error
4605 * it will be nulled too. */
4606 isds_error isds_get_password_expiration(struct isds_ctx *context,
4607 struct timeval **expiration) {
4608 isds_error err = IE_SUCCESS;
4609 #if HAVE_LIBCURL
4610 xmlDocPtr response = NULL;
4611 xmlChar *code = NULL, *message = NULL;
4612 xmlXPathContextPtr xpath_ctx = NULL;
4613 xmlXPathObjectPtr result = NULL;
4614 char *string = NULL;
4615 #endif
4617 if (!context) return IE_INVALID_CONTEXT;
4618 zfree(context->long_message);
4619 if (!expiration) return IE_INVAL;
4620 zfree(*expiration);
4622 #if HAVE_LIBCURL
4623 /* Check if connection is established */
4624 if (!context->curl) return IE_CONNECTION_CLOSED;
4627 /* Do request and check for success */
4628 err = build_send_check_dbdummy_request(context,
4629 BAD_CAST "GetPasswordInfo",
4630 &response, NULL, NULL, &code, &message);
4631 if (err) goto leave;
4634 /* Extract data */
4635 xpath_ctx = xmlXPathNewContext(response);
4636 if (!xpath_ctx) {
4637 err = IE_ERROR;
4638 goto leave;
4640 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4641 err = IE_ERROR;
4642 goto leave;
4645 /* Set context node */
4646 result = xmlXPathEvalExpression(BAD_CAST
4647 "/isds:GetPasswordInfoResponse", xpath_ctx);
4648 if (!result) {
4649 err = IE_ERROR;
4650 goto leave;
4652 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4653 isds_log_message(context,
4654 _("Missing GetPasswordInfoResponse element"));
4655 err = IE_ISDS;
4656 goto leave;
4658 if (result->nodesetval->nodeNr > 1) {
4659 isds_log_message(context,
4660 _("Multiple GetPasswordInfoResponse element"));
4661 err = IE_ISDS;
4662 goto leave;
4664 xpath_ctx->node = result->nodesetval->nodeTab[0];
4665 xmlXPathFreeObject(result); result = NULL;
4667 /* Extract expiration date */
4668 EXTRACT_STRING("isds:pswExpDate", string);
4669 if (string) {
4670 /* And convert it if any returned. Otherwise expiration is disabled. */
4671 err = timestring2timeval((xmlChar *) string, expiration);
4672 if (err) {
4673 char *string_locale = _isds_utf82locale(string);
4674 if (err == IE_DATE) err = IE_ISDS;
4675 isds_printf_message(context,
4676 _("Could not convert pswExpDate as ISO time: %s"),
4677 string_locale);
4678 free(string_locale);
4679 goto leave;
4683 leave:
4684 if (err) {
4685 if (*expiration) {
4686 zfree(*expiration);
4690 free(string);
4691 xmlXPathFreeObject(result);
4692 xmlXPathFreeContext(xpath_ctx);
4694 free(code);
4695 free(message);
4696 xmlFreeDoc(response);
4698 if (!err)
4699 isds_log(ILF_ISDS, ILL_DEBUG,
4700 _("GetPasswordInfo request processed by server "
4701 "successfully.\n"));
4702 #else /* not HAVE_LIBCURL */
4703 err = IE_NOTSUP;
4704 #endif
4706 return err;
4710 /* Change user password in ISDS.
4711 * User must supply old password, new password will takes effect after some
4712 * time, current session can continue. Password must fulfill some constraints.
4713 * @context is session context
4714 * @old_password is current password.
4715 * @new_password is requested new password */
4716 isds_error isds_change_password(struct isds_ctx *context,
4717 const char *old_password, const char *new_password) {
4718 isds_error err = IE_SUCCESS;
4719 #if HAVE_LIBCURL
4720 xmlNsPtr isds_ns = NULL;
4721 xmlNodePtr request = NULL, node;
4722 xmlDocPtr response = NULL;
4723 xmlChar *code = NULL, *message = NULL;
4724 #endif
4726 if (!context) return IE_INVALID_CONTEXT;
4727 zfree(context->long_message);
4728 if (!old_password || !new_password) return IE_INVAL;
4730 #if HAVE_LIBCURL
4731 /* Check if connection is established
4732 * TODO: This check should be done downstairs. */
4733 if (!context->curl) return IE_CONNECTION_CLOSED;
4736 /* Build ChangeISDSPassword request */
4737 request = xmlNewNode(NULL, BAD_CAST "ChangeISDSPassword");
4738 if (!request) {
4739 isds_log_message(context,
4740 _("Could not build ChangeISDSPassword request"));
4741 return IE_ERROR;
4743 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4744 if(!isds_ns) {
4745 isds_log_message(context, _("Could not create ISDS name space"));
4746 xmlFreeNode(request);
4747 return IE_ERROR;
4749 xmlSetNs(request, isds_ns);
4751 INSERT_STRING(request, "dbOldPassword", old_password);
4752 INSERT_STRING(request, "dbNewPassword", new_password);
4755 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CheckDataBox request to ISDS\n"));
4757 /* Sent request */
4758 err = isds(context, SERVICE_DB_ACCESS, request, &response, NULL, NULL);
4760 /* Destroy request */
4761 xmlFreeNode(request); request = NULL;
4763 if (err) {
4764 isds_log(ILF_ISDS, ILL_DEBUG,
4765 _("Processing ISDS response on ChangeISDSPassword "
4766 "request failed\n"));
4767 goto leave;
4770 /* Check for response status */
4771 err = isds_response_status(context, SERVICE_DB_ACCESS, response,
4772 &code, &message, NULL);
4773 if (err) {
4774 isds_log(ILF_ISDS, ILL_DEBUG,
4775 _("ISDS response on ChangeISDSPassword request is missing "
4776 "status\n"));
4777 goto leave;
4780 /* Request processed, but empty password refused */
4781 if (!xmlStrcmp(code, BAD_CAST "1066")) {
4782 char *code_locale = _isds_utf82locale((char*)code);
4783 char *message_locale = _isds_utf82locale((char*)message);
4784 isds_log(ILF_ISDS, ILL_DEBUG,
4785 _("Server refused empty password on ChangeISDSPassword "
4786 "request (code=%s, message=%s)\n"),
4787 code_locale, message_locale);
4788 isds_log_message(context, _("Password must not be empty"));
4789 free(code_locale);
4790 free(message_locale);
4791 err = IE_INVAL;
4792 goto leave;
4795 /* Request processed, but new password was reused */
4796 else if (!xmlStrcmp(code, BAD_CAST "1067")) {
4797 char *code_locale = _isds_utf82locale((char*)code);
4798 char *message_locale = _isds_utf82locale((char*)message);
4799 isds_log(ILF_ISDS, ILL_DEBUG,
4800 _("Server refused the same new password on ChangeISDSPassword "
4801 "request (code=%s, message=%s)\n"),
4802 code_locale, message_locale);
4803 isds_log_message(context,
4804 _("New password must differ from the current one"));
4805 free(code_locale);
4806 free(message_locale);
4807 err = IE_INVAL;
4808 goto leave;
4811 /* Other error */
4812 else if (xmlStrcmp(code, BAD_CAST "0000")) {
4813 char *code_locale = _isds_utf82locale((char*)code);
4814 char *message_locale = _isds_utf82locale((char*)message);
4815 isds_log(ILF_ISDS, ILL_DEBUG,
4816 _("Server refused to change password on ChangeISDSPassword "
4817 "request (code=%s, message=%s)\n"),
4818 code_locale, message_locale);
4819 isds_log_message(context, message_locale);
4820 free(code_locale);
4821 free(message_locale);
4822 err = IE_ISDS;
4823 goto leave;
4826 /* Otherwise password changed successfully */
4828 leave:
4829 free(code);
4830 free(message);
4831 xmlFreeDoc(response);
4832 xmlFreeNode(request);
4834 if (!err)
4835 isds_log(ILF_ISDS, ILL_DEBUG,
4836 _("Password changed successfully on ChangeISDSPassword "
4837 "request.\n"));
4838 #else /* not HAVE_LIBCURL */
4839 err = IE_NOTSUP;
4840 #endif
4842 return err;
4846 #if HAVE_LIBCURL
4847 /* Generic middle part with request sending and response check.
4848 * It sends prepared request and checks for error code.
4849 * @context is ISDS session context.
4850 * @service is ISDS service handler
4851 * @service_name is name in scope of given @service
4852 * @request is XML tree with request. Will be freed to save memory.
4853 * @response is XML document outputting ISDS response.
4854 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4855 * NULL, if you don't care. */
4856 static isds_error send_destroy_request_check_response(
4857 struct isds_ctx *context,
4858 const isds_service service, const xmlChar *service_name,
4859 xmlNodePtr *request, xmlDocPtr *response, xmlChar **refnumber) {
4860 isds_error err = IE_SUCCESS;
4861 char *service_name_locale = NULL;
4862 xmlChar *code = NULL, *message = NULL;
4865 if (!context) return IE_INVALID_CONTEXT;
4866 if (!service_name || *service_name == '\0' || !request || !*request ||
4867 !response)
4868 return IE_INVAL;
4870 /* Check if connection is established
4871 * TODO: This check should be done downstairs. */
4872 if (!context->curl) return IE_CONNECTION_CLOSED;
4874 service_name_locale = _isds_utf82locale((char*) service_name);
4875 if (!service_name_locale) {
4876 err = IE_NOMEM;
4877 goto leave;
4880 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
4881 service_name_locale);
4883 /* Send request */
4884 err = isds(context, service, *request, response, NULL, NULL);
4885 xmlFreeNode(*request); *request = NULL;
4887 if (err) {
4888 isds_log(ILF_ISDS, ILL_DEBUG,
4889 _("Processing ISDS response on %s request failed\n"),
4890 service_name_locale);
4891 goto leave;
4894 /* Check for response status */
4895 err = isds_response_status(context, service, *response,
4896 &code, &message, refnumber);
4897 if (err) {
4898 isds_log(ILF_ISDS, ILL_DEBUG,
4899 _("ISDS response on %s request is missing status\n"),
4900 service_name_locale);
4901 goto leave;
4904 /* Request processed, but server failed */
4905 if (xmlStrcmp(code, BAD_CAST "0000")) {
4906 char *code_locale = _isds_utf82locale((char*) code);
4907 char *message_locale = _isds_utf82locale((char*) message);
4908 isds_log(ILF_ISDS, ILL_DEBUG,
4909 _("Server refused %s request (code=%s, message=%s)\n"),
4910 service_name_locale, code_locale, message_locale);
4911 isds_log_message(context, message_locale);
4912 free(code_locale);
4913 free(message_locale);
4914 err = IE_ISDS;
4915 goto leave;
4919 leave:
4920 free(code);
4921 free(message);
4922 if (err && *response) {
4923 xmlFreeDoc(*response);
4924 *response = NULL;
4926 if (*request) {
4927 xmlFreeNode(*request);
4928 *request = NULL;
4930 free(service_name_locale);
4932 return err;
4936 /* Generic bottom half with request sending.
4937 * It sends prepared request, checks for error code, destroys response and
4938 * request and log success or failure.
4939 * @context is ISDS session context.
4940 * @service is ISDS service handler
4941 * @service_name is name in scope of given @service
4942 * @request is XML tree with request. Will be freed to save memory.
4943 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4944 * NULL, if you don't care. */
4945 static isds_error send_request_check_drop_response(
4946 struct isds_ctx *context,
4947 const isds_service service, const xmlChar *service_name,
4948 xmlNodePtr *request, xmlChar **refnumber) {
4949 isds_error err = IE_SUCCESS;
4950 xmlDocPtr response = NULL;
4953 if (!context) return IE_INVALID_CONTEXT;
4954 if (!service_name || *service_name == '\0' || !request || !*request)
4955 return IE_INVAL;
4957 /* Send request and check response*/
4958 err = send_destroy_request_check_response(context,
4959 service, service_name, request, &response, refnumber);
4961 xmlFreeDoc(response);
4963 if (*request) {
4964 xmlFreeNode(*request);
4965 *request = NULL;
4968 if (!err) {
4969 char *service_name_locale = _isds_utf82locale((char *) service_name);
4970 isds_log(ILF_ISDS, ILL_DEBUG,
4971 _("%s request processed by server successfully.\n"),
4972 service_name_locale);
4973 free(service_name_locale);
4976 return err;
4980 /* Insert isds_credentials_delivery structure into XML request if not NULL
4981 * @context is session context
4982 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
4983 * credentials delivery. The email field is passed.
4984 * @parent is XML element where to insert */
4985 static isds_error insert_credentials_delivery(struct isds_ctx *context,
4986 const struct isds_credentials_delivery *credentials_delivery,
4987 xmlNodePtr parent) {
4988 isds_error err = IE_SUCCESS;
4989 xmlNodePtr node;
4991 if (!context) return IE_INVALID_CONTEXT;
4992 if (!parent) return IE_INVAL;
4994 if (credentials_delivery) {
4995 /* Following elements are valid only for services:
4996 * NewAccessData, AddDataBoxUser, CreateDataBox */
4997 INSERT_SCALAR_BOOLEAN(parent, "dbVirtual", 1);
4998 INSERT_STRING(parent, "email", credentials_delivery->email);
5001 leave:
5002 return err;
5006 /* Extract credentials delivery from ISDS response.
5007 * @context is session context
5008 * @credentials_delivery is pointer to valid structure to fill in returned
5009 * user's password (and new log-in name). If NULL, do not extract the data.
5010 * @response is pointer to XML document with ISDS response
5011 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
5012 * @return IE_SUCCESS even if new user name has not been found because it's not
5013 * clear whether it's returned always. */
5014 static isds_error extract_credentials_delivery(struct isds_ctx *context,
5015 struct isds_credentials_delivery *credentials_delivery,
5016 xmlDocPtr response, const char *request_name) {
5017 isds_error err = IE_SUCCESS;
5018 xmlXPathContextPtr xpath_ctx = NULL;
5019 xmlXPathObjectPtr result = NULL;
5020 char *xpath_query = NULL;
5022 if (!context) return IE_INVALID_CONTEXT;
5023 if (credentials_delivery) {
5024 zfree(credentials_delivery->token);
5025 zfree(credentials_delivery->new_user_name);
5027 if (!response || !request_name || !*request_name) return IE_INVAL;
5030 /* Extract optional token */
5031 if (credentials_delivery) {
5032 xpath_ctx = xmlXPathNewContext(response);
5033 if (!xpath_ctx) {
5034 err = IE_ERROR;
5035 goto leave;
5037 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5038 err = IE_ERROR;
5039 goto leave;
5042 /* Verify root element */
5043 if (-1 == isds_asprintf(&xpath_query, "/isds:%sResponse",
5044 request_name)) {
5045 err = IE_NOMEM;
5046 goto leave;
5048 result = xmlXPathEvalExpression(BAD_CAST xpath_query, xpath_ctx);
5049 if (!result) {
5050 err = IE_ERROR;
5051 goto leave;
5053 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5054 char *request_name_locale = _isds_utf82locale(request_name);
5055 isds_log(ILF_ISDS, ILL_WARNING,
5056 _("Wrong element in ISDS response for %s request "
5057 "while extracting credentials delivery details\n"),
5058 request_name_locale);
5059 free(request_name_locale);
5060 err = IE_ERROR;
5061 goto leave;
5063 xpath_ctx->node = result->nodesetval->nodeTab[0];
5066 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
5067 * optional. */
5068 EXTRACT_STRING("isds:dbUserID", credentials_delivery->new_user_name);
5070 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery->token);
5071 if (!credentials_delivery->token) {
5072 char *request_name_locale = _isds_utf82locale(request_name);
5073 isds_log(ILF_ISDS, ILL_ERR,
5074 _("ISDS did not return token on %s request "
5075 "even if requested\n"), request_name_locale);
5076 free(request_name_locale);
5077 err = IE_ERROR;
5081 leave:
5082 free(xpath_query);
5083 xmlXPathFreeObject(result);
5084 xmlXPathFreeContext(xpath_ctx);
5086 return err;
5090 /* Build XSD:tCreateDBInput request type for box creating.
5091 * @context is session context
5092 * @request outputs built XML tree
5093 * @service_name is request name of SERVICE_DB_MANIPULATION service
5094 * @box is box description to create including single primary user (in case of
5095 * FO box type)
5096 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
5097 * box, or contact address of PFO box owner)
5098 * @former_names is optional former name of box owner. Pass NULL if otherwise.
5099 * @upper_box_id is optional ID of supper box if currently created box is
5100 * subordinated.
5101 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
5102 * don't care.
5103 * @credentials_delivery is valid pointer if ISDS should return token that box
5104 * owner can use to obtain his new credentials in on-line way. Then valid email
5105 * member value should be supplied.
5106 * @approval is optional external approval of box manipulation */
5107 static isds_error build_CreateDBInput_request(struct isds_ctx *context,
5108 xmlNodePtr *request, const xmlChar *service_name,
5109 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
5110 const xmlChar *former_names, const xmlChar *upper_box_id,
5111 const xmlChar *ceo_label,
5112 const struct isds_credentials_delivery *credentials_delivery,
5113 const struct isds_approval *approval) {
5114 isds_error err = IE_SUCCESS;
5115 xmlNsPtr isds_ns = NULL;
5116 xmlNodePtr node, dbPrimaryUsers;
5117 xmlChar *string = NULL;
5118 const struct isds_list *item;
5121 if (!context) return IE_INVALID_CONTEXT;
5122 if (!request || !service_name || service_name[0] == '\0' || !box)
5123 return IE_INVAL;
5126 /* Build CreateDataBox-similar request */
5127 *request = xmlNewNode(NULL, service_name);
5128 if (!*request) {
5129 char *service_name_locale = _isds_utf82locale((char*) service_name);
5130 isds_printf_message(context, _("Could build %s request"),
5131 service_name_locale);
5132 free(service_name_locale);
5133 return IE_ERROR;
5135 if (context->type == CTX_TYPE_TESTING_REQUEST_COLLECTOR) {
5136 isds_ns = xmlNewNs(*request, BAD_CAST ISDS1_NS, NULL);
5137 if (!isds_ns) {
5138 isds_log_message(context, _("Could not create ISDS1 name space"));
5139 xmlFreeNode(*request);
5140 return IE_ERROR;
5142 } else {
5143 isds_ns = xmlNewNs(*request, BAD_CAST ISDS_NS, NULL);
5144 if (!isds_ns) {
5145 isds_log_message(context, _("Could not create ISDS name space"));
5146 xmlFreeNode(*request);
5147 return IE_ERROR;
5150 xmlSetNs(*request, isds_ns);
5152 INSERT_ELEMENT(node, *request, "dbOwnerInfo");
5153 err = insert_DbOwnerInfo(context, box, node);
5154 if (err) goto leave;
5156 /* Insert users */
5157 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
5158 * verbose documentation allows none dbUserInfo */
5159 INSERT_ELEMENT(dbPrimaryUsers, *request, "dbPrimaryUsers");
5160 for (item = users; item; item = item->next) {
5161 if (item->data) {
5162 INSERT_ELEMENT(node, dbPrimaryUsers, "dbUserInfo");
5163 err = insert_DbUserInfo(context,
5164 (struct isds_DbUserInfo *) item->data, node);
5165 if (err) goto leave;
5169 INSERT_STRING(*request, "dbFormerNames", former_names);
5170 INSERT_STRING(*request, "dbUpperDBId", upper_box_id);
5171 INSERT_STRING(*request, "dbCEOLabel", ceo_label);
5173 err = insert_credentials_delivery(context, credentials_delivery, *request);
5174 if (err) goto leave;
5176 err = insert_GExtApproval(context, approval, *request);
5177 if (err) goto leave;
5179 leave:
5180 if (err) {
5181 xmlFreeNode(*request);
5182 *request = NULL;
5184 free(string);
5185 return err;
5187 #endif /* HAVE_LIBCURL */
5190 /* Create new box.
5191 * @context is session context
5192 * @box is box description to create including single primary user (in case of
5193 * FO box type). It outputs box ID assigned by ISDS in dbID element.
5194 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
5195 * box, or contact address of PFO box owner)
5196 * @former_names is optional former name of box owner. Pass NULL if you don't care.
5197 * @upper_box_id is optional ID of supper box if currently created box is
5198 * subordinated.
5199 * @ceo_label is optional title of OVM box owner (e.g. mayor)
5200 * @credentials_delivery is NULL if new password should be delivered off-line
5201 * to box owner. It is valid pointer if owner should obtain new password on-line
5202 * on dedicated web server. Then input @credentials_delivery.email value is
5203 * his e-mail address he must provide to dedicated web server together
5204 * with output reallocated @credentials_delivery.token member. Output
5205 * member @credentials_delivery.new_user_name is unused up on this call.
5206 * @approval is optional external approval of box manipulation
5207 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5208 * NULL, if you don't care.*/
5209 isds_error isds_add_box(struct isds_ctx *context,
5210 struct isds_DbOwnerInfo *box, const struct isds_list *users,
5211 const char *former_names, const char *upper_box_id,
5212 const char *ceo_label,
5213 struct isds_credentials_delivery *credentials_delivery,
5214 const struct isds_approval *approval, char **refnumber) {
5215 isds_error err = IE_SUCCESS;
5216 #if HAVE_LIBCURL
5217 xmlNodePtr request = NULL;
5218 xmlDocPtr response = NULL;
5219 xmlXPathContextPtr xpath_ctx = NULL;
5220 xmlXPathObjectPtr result = NULL;
5221 #endif
5224 if (!context) return IE_INVALID_CONTEXT;
5225 zfree(context->long_message);
5226 if (credentials_delivery) {
5227 zfree(credentials_delivery->token);
5228 zfree(credentials_delivery->new_user_name);
5230 if (!box) return IE_INVAL;
5232 #if HAVE_LIBCURL
5233 /* Scratch box ID */
5234 zfree(box->dbID);
5236 /* Build CreateDataBox request */
5237 err = build_CreateDBInput_request(context,
5238 &request, BAD_CAST "CreateDataBox",
5239 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
5240 (xmlChar *) ceo_label, credentials_delivery, approval);
5241 if (err) goto leave;
5243 /* Send it to server and process response */
5244 err = send_destroy_request_check_response(context,
5245 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
5246 &response, (xmlChar **) refnumber);
5248 /* Extract box ID */
5249 xpath_ctx = xmlXPathNewContext(response);
5250 if (!xpath_ctx) {
5251 err = IE_ERROR;
5252 goto leave;
5254 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5255 err = IE_ERROR;
5256 goto leave;
5258 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
5260 /* Extract optional token */
5261 err = extract_credentials_delivery(context, credentials_delivery, response,
5262 "CreateDataBox");
5264 leave:
5265 xmlXPathFreeObject(result);
5266 xmlXPathFreeContext(xpath_ctx);
5267 xmlFreeDoc(response);
5268 xmlFreeNode(request);
5270 if (!err) {
5271 isds_log(ILF_ISDS, ILL_DEBUG,
5272 _("CreateDataBox request processed by server successfully.\n"));
5274 #else /* not HAVE_LIBCURL */
5275 err = IE_NOTSUP;
5276 #endif
5278 return err;
5282 /* Notify ISDS about new PFO entity.
5283 * This function has no real effect.
5284 * @context is session context
5285 * @box is PFO description including single primary user.
5286 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
5287 * @former_names is optional undocumented string. Pass NULL if you don't care.
5288 * @upper_box_id is optional ID of supper box if currently created box is
5289 * subordinated.
5290 * @ceo_label is optional title of OVM box owner (e.g. mayor)
5291 * @approval is optional external approval of box manipulation
5292 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5293 * NULL, if you don't care.*/
5294 isds_error isds_add_pfoinfo(struct isds_ctx *context,
5295 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
5296 const char *former_names, const char *upper_box_id,
5297 const char *ceo_label, const struct isds_approval *approval,
5298 char **refnumber) {
5299 isds_error err = IE_SUCCESS;
5300 #if HAVE_LIBCURL
5301 xmlNodePtr request = NULL;
5302 #endif
5304 if (!context) return IE_INVALID_CONTEXT;
5305 zfree(context->long_message);
5306 if (!box) return IE_INVAL;
5308 #if HAVE_LIBCURL
5309 /* Build CreateDataBoxPFOInfo request */
5310 err = build_CreateDBInput_request(context,
5311 &request, BAD_CAST "CreateDataBoxPFOInfo",
5312 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
5313 (xmlChar *) ceo_label, NULL, approval);
5314 if (err) goto leave;
5316 /* Send it to server and process response */
5317 err = send_request_check_drop_response(context,
5318 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
5319 (xmlChar **) refnumber);
5320 /* XXX: XML Schema names output dbID element but textual documentation
5321 * states no box identifier is returned. */
5322 leave:
5323 xmlFreeNode(request);
5324 #else /* not HAVE_LIBCURL */
5325 err = IE_NOTSUP;
5326 #endif
5327 return err;
5331 /* Common implementation for removing given box.
5332 * @context is session context
5333 * @service_name is UTF-8 encoded name fo ISDS service
5334 * @box is box description to delete
5335 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
5336 * carry sane value. If NULL, do not inject this information into request.
5337 * @approval is optional external approval of box manipulation
5338 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5339 * NULL, if you don't care.*/
5340 isds_error _isds_delete_box_common(struct isds_ctx *context,
5341 const xmlChar *service_name,
5342 const struct isds_DbOwnerInfo *box, const struct tm *since,
5343 const struct isds_approval *approval, char **refnumber) {
5344 isds_error err = IE_SUCCESS;
5345 #if HAVE_LIBCURL
5346 xmlNsPtr isds_ns = NULL;
5347 xmlNodePtr request = NULL;
5348 xmlNodePtr node;
5349 xmlChar *string = NULL;
5350 #endif
5353 if (!context) return IE_INVALID_CONTEXT;
5354 zfree(context->long_message);
5355 if (!service_name || !*service_name || !box) return IE_INVAL;
5358 #if HAVE_LIBCURL
5359 /* Build DeleteDataBox(Promptly) request */
5360 request = xmlNewNode(NULL, service_name);
5361 if (!request) {
5362 char *service_name_locale = _isds_utf82locale((char*)service_name);
5363 isds_printf_message(context,
5364 _("Could build %s request"), service_name_locale);
5365 free(service_name_locale);
5366 return IE_ERROR;
5368 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5369 if(!isds_ns) {
5370 isds_log_message(context, _("Could not create ISDS name space"));
5371 xmlFreeNode(request);
5372 return IE_ERROR;
5374 xmlSetNs(request, isds_ns);
5376 INSERT_ELEMENT(node, request, "dbOwnerInfo");
5377 err = insert_DbOwnerInfo(context, box, node);
5378 if (err) goto leave;
5380 if (since) {
5381 err = tm2datestring(since, &string);
5382 if (err) {
5383 isds_log_message(context,
5384 _("Could not convert `since' argument to ISO date string"));
5385 goto leave;
5387 INSERT_STRING(request, "dbOwnerTerminationDate", string);
5388 zfree(string);
5391 err = insert_GExtApproval(context, approval, request);
5392 if (err) goto leave;
5395 /* Send it to server and process response */
5396 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
5397 service_name, &request, (xmlChar **) refnumber);
5399 leave:
5400 xmlFreeNode(request);
5401 free(string);
5402 #else /* not HAVE_LIBCURL */
5403 err = IE_NOTSUP;
5404 #endif
5405 return err;
5409 /* Remove given box permanently.
5410 * @context is session context
5411 * @box is box description to delete
5412 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
5413 * carry sane value.
5414 * @approval is optional external approval of box manipulation
5415 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5416 * NULL, if you don't care.*/
5417 isds_error isds_delete_box(struct isds_ctx *context,
5418 const struct isds_DbOwnerInfo *box, const struct tm *since,
5419 const struct isds_approval *approval, char **refnumber) {
5420 if (!context) return IE_INVALID_CONTEXT;
5421 zfree(context->long_message);
5422 if (!box || !since) return IE_INVAL;
5424 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBox",
5425 box, since, approval, refnumber);
5429 /* Undocumented function.
5430 * @context is session context
5431 * @box is box description to delete
5432 * @approval is optional external approval of box manipulation
5433 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5434 * NULL, if you don't care.*/
5435 isds_error isds_delete_box_promptly(struct isds_ctx *context,
5436 const struct isds_DbOwnerInfo *box,
5437 const struct isds_approval *approval, char **refnumber) {
5438 if (!context) return IE_INVALID_CONTEXT;
5439 zfree(context->long_message);
5440 if (!box) return IE_INVAL;
5442 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBoxPromptly",
5443 box, NULL, approval, refnumber);
5447 /* Update data about given box.
5448 * @context is session context
5449 * @old_box current box description
5450 * @new_box are updated data about @old_box
5451 * @approval is optional external approval of box manipulation
5452 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5453 * NULL, if you don't care.*/
5454 isds_error isds_UpdateDataBoxDescr(struct isds_ctx *context,
5455 const struct isds_DbOwnerInfo *old_box,
5456 const struct isds_DbOwnerInfo *new_box,
5457 const struct isds_approval *approval, char **refnumber) {
5458 isds_error err = IE_SUCCESS;
5459 #if HAVE_LIBCURL
5460 xmlNsPtr isds_ns = NULL;
5461 xmlNodePtr request = NULL;
5462 xmlNodePtr node;
5463 #endif
5466 if (!context) return IE_INVALID_CONTEXT;
5467 zfree(context->long_message);
5468 if (!old_box || !new_box) return IE_INVAL;
5471 #if HAVE_LIBCURL
5472 /* Build UpdateDataBoxDescr request */
5473 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxDescr");
5474 if (!request) {
5475 isds_log_message(context,
5476 _("Could build UpdateDataBoxDescr request"));
5477 return IE_ERROR;
5479 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5480 if(!isds_ns) {
5481 isds_log_message(context, _("Could not create ISDS name space"));
5482 xmlFreeNode(request);
5483 return IE_ERROR;
5485 xmlSetNs(request, isds_ns);
5487 INSERT_ELEMENT(node, request, "dbOldOwnerInfo");
5488 err = insert_DbOwnerInfo(context, old_box, node);
5489 if (err) goto leave;
5491 INSERT_ELEMENT(node, request, "dbNewOwnerInfo");
5492 err = insert_DbOwnerInfo(context, new_box, node);
5493 if (err) goto leave;
5495 err = insert_GExtApproval(context, approval, request);
5496 if (err) goto leave;
5499 /* Send it to server and process response */
5500 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
5501 BAD_CAST "UpdateDataBoxDescr", &request, (xmlChar **) refnumber);
5503 leave:
5504 xmlFreeNode(request);
5505 #else /* not HAVE_LIBCURL */
5506 err = IE_NOTSUP;
5507 #endif
5509 return err;
5513 #if HAVE_LIBCURL
5514 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
5515 * code
5516 * @context is session context
5517 * @service is SOAP service
5518 * @service_name is name of request in @service
5519 * @box_id is box ID of interest
5520 * @approval is optional external approval of box manipulation
5521 * @response is server SOAP body response as XML document
5522 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5523 * NULL, if you don't care.
5524 * @return error coded from lower layer, context message will be set up
5525 * appropriately. */
5526 static isds_error build_send_dbid_request_check_response(
5527 struct isds_ctx *context, const isds_service service,
5528 const xmlChar *service_name, const xmlChar *box_id,
5529 const struct isds_approval *approval,
5530 xmlDocPtr *response, xmlChar **refnumber) {
5532 isds_error err = IE_SUCCESS;
5533 char *service_name_locale = NULL, *box_id_locale = NULL;
5534 xmlNodePtr request = NULL, node;
5535 xmlNsPtr isds_ns = NULL;
5537 if (!context) return IE_INVALID_CONTEXT;
5538 if (!service_name || !box_id) return IE_INVAL;
5539 if (!response) return IE_INVAL;
5541 /* Free output argument */
5542 xmlFreeDoc(*response); *response = NULL;
5544 /* Prepare strings */
5545 service_name_locale = _isds_utf82locale((char*)service_name);
5546 if (!service_name_locale) {
5547 err = IE_NOMEM;
5548 goto leave;
5550 box_id_locale = _isds_utf82locale((char*)box_id);
5551 if (!box_id_locale) {
5552 err = IE_NOMEM;
5553 goto leave;
5556 /* Build request */
5557 request = xmlNewNode(NULL, service_name);
5558 if (!request) {
5559 isds_printf_message(context,
5560 _("Could not build %s request"), service_name_locale);
5561 err = IE_ERROR;
5562 goto leave;
5564 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5565 if(!isds_ns) {
5566 isds_log_message(context, _("Could not create ISDS name space"));
5567 err = IE_ERROR;
5568 goto leave;
5570 xmlSetNs(request, isds_ns);
5572 /* Add XSD:tIdDbInput children */
5573 INSERT_STRING(request, "dbID", box_id);
5574 err = insert_GExtApproval(context, approval, request);
5575 if (err) goto leave;
5577 /* Send request and check response*/
5578 err = send_destroy_request_check_response(context,
5579 service, service_name, &request, response, refnumber);
5581 leave:
5582 free(service_name_locale);
5583 free(box_id_locale);
5584 xmlFreeNode(request);
5585 return err;
5587 #endif /* HAVE_LIBCURL */
5590 /* Get data about all users assigned to given box.
5591 * @context is session context
5592 * @box_id is box ID
5593 * @users is automatically reallocated list of struct isds_DbUserInfo */
5594 isds_error isds_GetDataBoxUsers(struct isds_ctx *context, const char *box_id,
5595 struct isds_list **users) {
5596 isds_error err = IE_SUCCESS;
5597 #if HAVE_LIBCURL
5598 xmlDocPtr response = NULL;
5599 xmlXPathContextPtr xpath_ctx = NULL;
5600 xmlXPathObjectPtr result = NULL;
5601 int i;
5602 struct isds_list *item, *prev_item = NULL;
5603 #endif
5605 if (!context) return IE_INVALID_CONTEXT;
5606 zfree(context->long_message);
5607 if (!users || !box_id) return IE_INVAL;
5608 isds_list_free(users);
5611 #if HAVE_LIBCURL
5612 /* Do request and check for success */
5613 err = build_send_dbid_request_check_response(context,
5614 SERVICE_DB_MANIPULATION, BAD_CAST "GetDataBoxUsers",
5615 BAD_CAST box_id, NULL, &response, NULL);
5616 if (err) goto leave;
5619 /* Extract data */
5620 /* Prepare structure */
5621 xpath_ctx = xmlXPathNewContext(response);
5622 if (!xpath_ctx) {
5623 err = IE_ERROR;
5624 goto leave;
5626 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5627 err = IE_ERROR;
5628 goto leave;
5631 /* Set context node */
5632 result = xmlXPathEvalExpression(BAD_CAST
5633 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
5634 xpath_ctx);
5635 if (!result) {
5636 err = IE_ERROR;
5637 goto leave;
5639 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5640 /* Iterate over all users */
5641 for (i = 0; i < result->nodesetval->nodeNr; i++) {
5643 /* Prepare structure */
5644 item = calloc(1, sizeof(*item));
5645 if (!item) {
5646 err = IE_NOMEM;
5647 goto leave;
5649 item->destructor = (void(*)(void**))isds_DbUserInfo_free;
5650 if (i == 0) *users = item;
5651 else prev_item->next = item;
5652 prev_item = item;
5654 /* Extract it */
5655 xpath_ctx->node = result->nodesetval->nodeTab[i];
5656 err = extract_DbUserInfo(context,
5657 (struct isds_DbUserInfo **) (&item->data), xpath_ctx);
5658 if (err) goto leave;
5662 leave:
5663 if (err) {
5664 isds_list_free(users);
5667 xmlXPathFreeObject(result);
5668 xmlXPathFreeContext(xpath_ctx);
5669 xmlFreeDoc(response);
5671 if (!err)
5672 isds_log(ILF_ISDS, ILL_DEBUG,
5673 _("GetDataBoxUsers request processed by server "
5674 "successfully.\n"));
5675 #else /* not HAVE_LIBCURL */
5676 err = IE_NOTSUP;
5677 #endif
5679 return err;
5683 /* Update data about user assigned to given box.
5684 * @context is session context
5685 * @box is box identification
5686 * @old_user identifies user to update
5687 * @new_user are updated data about @old_user
5688 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5689 * NULL, if you don't care.*/
5690 isds_error isds_UpdateDataBoxUser(struct isds_ctx *context,
5691 const struct isds_DbOwnerInfo *box,
5692 const struct isds_DbUserInfo *old_user,
5693 const struct isds_DbUserInfo *new_user,
5694 char **refnumber) {
5695 isds_error err = IE_SUCCESS;
5696 #if HAVE_LIBCURL
5697 xmlNsPtr isds_ns = NULL;
5698 xmlNodePtr request = NULL;
5699 xmlNodePtr node;
5700 #endif
5703 if (!context) return IE_INVALID_CONTEXT;
5704 zfree(context->long_message);
5705 if (!box || !old_user || !new_user) return IE_INVAL;
5708 #if HAVE_LIBCURL
5709 /* Build UpdateDataBoxUser request */
5710 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxUser");
5711 if (!request) {
5712 isds_log_message(context,
5713 _("Could build UpdateDataBoxUser request"));
5714 return IE_ERROR;
5716 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5717 if(!isds_ns) {
5718 isds_log_message(context, _("Could not create ISDS name space"));
5719 xmlFreeNode(request);
5720 return IE_ERROR;
5722 xmlSetNs(request, isds_ns);
5724 INSERT_ELEMENT(node, request, "dbOwnerInfo");
5725 err = insert_DbOwnerInfo(context, box, node);
5726 if (err) goto leave;
5728 INSERT_ELEMENT(node, request, "dbOldUserInfo");
5729 err = insert_DbUserInfo(context, old_user, node);
5730 if (err) goto leave;
5732 INSERT_ELEMENT(node, request, "dbNewUserInfo");
5733 err = insert_DbUserInfo(context, new_user, node);
5734 if (err) goto leave;
5736 /* Send it to server and process response */
5737 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
5738 BAD_CAST "UpdateDataBoxUser", &request, (xmlChar **) refnumber);
5740 leave:
5741 xmlFreeNode(request);
5742 #else /* not HAVE_LIBCURL */
5743 err = IE_NOTSUP;
5744 #endif
5746 return err;
5750 /* Undocumented function.
5751 * @context is session context
5752 * @box_id is UTF-8 encoded box identifier
5753 * @token is UTF-8 encoded temporary password
5754 * @user_id outputs UTF-8 encoded reallocated user identifier
5755 * @password outpus UTF-8 encoded reallocated user password
5756 * Output arguments will be nulled in case of error */
5757 isds_error isds_activate(struct isds_ctx *context,
5758 const char *box_id, const char *token,
5759 char **user_id, char **password) {
5760 isds_error err = IE_SUCCESS;
5761 #if HAVE_LIBCURL
5762 xmlNsPtr isds_ns = NULL;
5763 xmlNodePtr request = NULL, node;
5764 xmlDocPtr response = NULL;
5765 xmlXPathContextPtr xpath_ctx = NULL;
5766 xmlXPathObjectPtr result = NULL;
5767 #endif
5770 if (!context) return IE_INVALID_CONTEXT;
5771 zfree(context->long_message);
5773 if (user_id) zfree(*user_id);
5774 if (password) zfree(*password);
5776 if (!box_id || !token || !user_id || !password) return IE_INVAL;
5779 #if HAVE_LIBCURL
5780 /* Build Activate request */
5781 request = xmlNewNode(NULL, BAD_CAST "Activate");
5782 if (!request) {
5783 isds_log_message(context, _("Could build Activate request"));
5784 return IE_ERROR;
5786 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5787 if(!isds_ns) {
5788 isds_log_message(context, _("Could not create ISDS name space"));
5789 xmlFreeNode(request);
5790 return IE_ERROR;
5792 xmlSetNs(request, isds_ns);
5794 INSERT_STRING(request, "dbAccessDataId", token);
5795 CHECK_FOR_STRING_LENGTH(box_id, 7, 7, "dbID");
5796 INSERT_STRING(request, "dbID", box_id);
5799 /* Send request and check response*/
5800 err = send_destroy_request_check_response(context,
5801 SERVICE_DB_MANIPULATION, BAD_CAST "Activate", &request,
5802 &response, NULL);
5803 if (err) goto leave;
5806 /* Extract data */
5807 xpath_ctx = xmlXPathNewContext(response);
5808 if (!xpath_ctx) {
5809 err = IE_ERROR;
5810 goto leave;
5812 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5813 err = IE_ERROR;
5814 goto leave;
5816 result = xmlXPathEvalExpression(BAD_CAST "/isds:ActivateResponse",
5817 xpath_ctx);
5818 if (!result) {
5819 err = IE_ERROR;
5820 goto leave;
5822 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5823 isds_log_message(context, _("Missing ActivateResponse element"));
5824 err = IE_ISDS;
5825 goto leave;
5827 if (result->nodesetval->nodeNr > 1) {
5828 isds_log_message(context, _("Multiple ActivateResponse element"));
5829 err = IE_ISDS;
5830 goto leave;
5832 xpath_ctx->node = result->nodesetval->nodeTab[0];
5833 xmlXPathFreeObject(result); result = NULL;
5835 EXTRACT_STRING("isds:userId", *user_id);
5836 if (!*user_id)
5837 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
5838 "but did not return `userId' element.\n"));
5840 EXTRACT_STRING("isds:password", *password);
5841 if (!*password)
5842 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
5843 "but did not return `password' element.\n"));
5845 leave:
5846 xmlXPathFreeObject(result);
5847 xmlXPathFreeContext(xpath_ctx);
5848 xmlFreeDoc(response);
5849 xmlFreeNode(request);
5851 if (!err)
5852 isds_log(ILF_ISDS, ILL_DEBUG,
5853 _("Activate request processed by server successfully.\n"));
5854 #else /* not HAVE_LIBCURL */
5855 err = IE_NOTSUP;
5856 #endif
5858 return err;
5862 /* Reset credentials of user assigned to given box.
5863 * @context is session context
5864 * @box is box identification
5865 * @user identifies user to reset password
5866 * @fee_paid is true if fee has been paid, false otherwise
5867 * @approval is optional external approval of box manipulation
5868 * @credentials_delivery is NULL if new password should be delivered off-line
5869 * to the user. It is valid pointer if user should obtain new password on-line
5870 * on dedicated web server. Then input @credentials_delivery.email value is
5871 * user's e-mail address user must provide to dedicated web server together
5872 * with @credentials_delivery.token. The output reallocated token user needs
5873 * to use to authorize on the web server to view his new password. Output
5874 * reallocated @credentials_delivery.new_user_name is user's log-in name that
5875 * ISDS changed up on this call. (No reason why server could change the name
5876 * is known now.)
5877 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5878 * NULL, if you don't care.*/
5879 isds_error isds_reset_password(struct isds_ctx *context,
5880 const struct isds_DbOwnerInfo *box,
5881 const struct isds_DbUserInfo *user,
5882 const _Bool fee_paid, const struct isds_approval *approval,
5883 struct isds_credentials_delivery *credentials_delivery,
5884 char **refnumber) {
5885 isds_error err = IE_SUCCESS;
5886 #if HAVE_LIBCURL
5887 xmlNsPtr isds_ns = NULL;
5888 xmlNodePtr request = NULL, node;
5889 xmlDocPtr response = NULL;
5890 #endif
5893 if (!context) return IE_INVALID_CONTEXT;
5894 zfree(context->long_message);
5896 if (credentials_delivery) {
5897 zfree(credentials_delivery->token);
5898 zfree(credentials_delivery->new_user_name);
5900 if (!box || !user) return IE_INVAL;
5903 #if HAVE_LIBCURL
5904 /* Build NewAccessData request */
5905 request = xmlNewNode(NULL, BAD_CAST "NewAccessData");
5906 if (!request) {
5907 isds_log_message(context,
5908 _("Could build NewAccessData request"));
5909 return IE_ERROR;
5911 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5912 if(!isds_ns) {
5913 isds_log_message(context, _("Could not create ISDS name space"));
5914 xmlFreeNode(request);
5915 return IE_ERROR;
5917 xmlSetNs(request, isds_ns);
5919 INSERT_ELEMENT(node, request, "dbOwnerInfo");
5920 err = insert_DbOwnerInfo(context, box, node);
5921 if (err) goto leave;
5923 INSERT_ELEMENT(node, request, "dbUserInfo");
5924 err = insert_DbUserInfo(context, user, node);
5925 if (err) goto leave;
5927 INSERT_SCALAR_BOOLEAN(request, "dbFeePaid", fee_paid);
5929 err = insert_credentials_delivery(context, credentials_delivery, request);
5930 if (err) goto leave;
5932 err = insert_GExtApproval(context, approval, request);
5933 if (err) goto leave;
5935 /* Send request and check response*/
5936 err = send_destroy_request_check_response(context,
5937 SERVICE_DB_MANIPULATION, BAD_CAST "NewAccessData", &request,
5938 &response, (xmlChar **) refnumber);
5939 if (err) goto leave;
5942 /* Extract optional token */
5943 err = extract_credentials_delivery(context, credentials_delivery,
5944 response, "NewAccessData");
5946 leave:
5947 xmlFreeDoc(response);
5948 xmlFreeNode(request);
5950 if (!err)
5951 isds_log(ILF_ISDS, ILL_DEBUG,
5952 _("NewAccessData request processed by server "
5953 "successfully.\n"));
5954 #else /* not HAVE_LIBCURL */
5955 err = IE_NOTSUP;
5956 #endif
5958 return err;
5962 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
5963 * code, destroy response and log success.
5964 * @context is ISDS session context.
5965 * @service_name is name of SERVICE_DB_MANIPULATION service
5966 * @box is box identification
5967 * @user identifies user to remove
5968 * @credentials_delivery is NULL if new user's password should be delivered
5969 * off-line to the user. It is valid pointer if user should obtain new
5970 * password on-line on dedicated web server. Then input
5971 * @credentials_delivery.email value is user's e-mail address user must
5972 * provide to dedicated web server together with @credentials_delivery.token.
5973 * The output reallocated token user needs to use to authorize on the web
5974 * server to view his new password. Output reallocated
5975 * @credentials_delivery.new_user_name is user's log-in name that ISDS
5976 * assingned or changed up on this call.
5977 * @approval is optional external approval of box manipulation
5978 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5979 * NULL, if you don't care. */
5980 static isds_error build_send_manipulationboxuser_request_check_drop_response(
5981 struct isds_ctx *context, const xmlChar *service_name,
5982 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
5983 struct isds_credentials_delivery *credentials_delivery,
5984 const struct isds_approval *approval, xmlChar **refnumber) {
5985 isds_error err = IE_SUCCESS;
5986 #if HAVE_LIBCURL
5987 xmlNsPtr isds_ns = NULL;
5988 xmlNodePtr request = NULL, node;
5989 xmlDocPtr response = NULL;
5990 #endif
5993 if (!context) return IE_INVALID_CONTEXT;
5994 zfree(context->long_message);
5995 if (credentials_delivery) {
5996 zfree(credentials_delivery->token);
5997 zfree(credentials_delivery->new_user_name);
5999 if (!service_name || service_name[0] == '\0' || !box || !user)
6000 return IE_INVAL;
6003 #if HAVE_LIBCURL
6004 /* Build NewAccessData or similar 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, _("Could not build %s request"),
6009 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 INSERT_ELEMENT(node, request, "dbUserInfo");
6026 err = insert_DbUserInfo(context, user, node);
6027 if (err) goto leave;
6029 err = insert_credentials_delivery(context, credentials_delivery, request);
6030 if (err) goto leave;
6032 err = insert_GExtApproval(context, approval, request);
6033 if (err) goto leave;
6036 /* Send request and check response*/
6037 err = send_destroy_request_check_response(context,
6038 SERVICE_DB_MANIPULATION, service_name, &request, &response, refnumber);
6040 xmlFreeNode(request);
6041 request = NULL;
6043 /* Pick up credentials_delivery if requested */
6044 err = extract_credentials_delivery(context, credentials_delivery, response,
6045 (char *)service_name);
6047 leave:
6048 xmlFreeDoc(response);
6049 if (request) xmlFreeNode(request);
6051 if (!err) {
6052 char *service_name_locale = _isds_utf82locale((char *) service_name);
6053 isds_log(ILF_ISDS, ILL_DEBUG,
6054 _("%s request processed by server successfully.\n"),
6055 service_name_locale);
6056 free(service_name_locale);
6058 #else /* not HAVE_LIBCURL */
6059 err = IE_NOTSUP;
6060 #endif
6062 return err;
6066 /* Assign new user to given box.
6067 * @context is session context
6068 * @box is box identification
6069 * @user defines new user to add
6070 * @credentials_delivery is NULL if new user's password should be delivered
6071 * off-line to the user. It is valid pointer if user should obtain new
6072 * password on-line on dedicated web server. Then input
6073 * @credentials_delivery.email value is user's e-mail address user must
6074 * provide to dedicated web server together with @credentials_delivery.token.
6075 * The output reallocated token user needs to use to authorize on the web
6076 * server to view his new password. Output reallocated
6077 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6078 * assingned up on this call.
6079 * @approval is optional external approval of box manipulation
6080 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6081 * NULL, if you don't care.*/
6082 isds_error isds_add_user(struct isds_ctx *context,
6083 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6084 struct isds_credentials_delivery *credentials_delivery,
6085 const struct isds_approval *approval, char **refnumber) {
6086 return build_send_manipulationboxuser_request_check_drop_response(context,
6087 BAD_CAST "AddDataBoxUser", box, user, credentials_delivery,
6088 approval, (xmlChar **) refnumber);
6092 /* Remove user assigned to given box.
6093 * @context is session context
6094 * @box is box identification
6095 * @user identifies user to remove
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_delete_user(struct isds_ctx *context,
6100 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6101 const struct isds_approval *approval, char **refnumber) {
6102 return build_send_manipulationboxuser_request_check_drop_response(context,
6103 BAD_CAST "DeleteDataBoxUser", box, user, NULL, approval,
6104 (xmlChar **) refnumber);
6108 /* Get list of boxes in ZIP archive.
6109 * @context is session context
6110 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
6111 * System recognizes following values currently: ALL (all boxes), UPG
6112 * (effectively OVM boxes), OVM (OVM gross type boxes), OPN (boxes allowing
6113 * receiving commercial messages). This argument is a string because
6114 * specification states new values can appear in the future. Not all list
6115 * types are available to all users.
6116 * @buffer is automatically reallocated memory to store the list of boxes. The
6117 * list is zipped CSV file.
6118 * @buffer_length is size of @buffer data in bytes.
6119 * In case of error @buffer will be freed and @buffer_length will be
6120 * undefined.*/
6121 isds_error isds_get_box_list_archive(struct isds_ctx *context,
6122 const char *list_identifier, void **buffer, size_t *buffer_length) {
6123 isds_error err = IE_SUCCESS;
6124 #if HAVE_LIBCURL
6125 xmlNsPtr isds_ns = NULL;
6126 xmlNodePtr request = NULL, node;
6127 xmlDocPtr response = NULL;
6128 xmlXPathContextPtr xpath_ctx = NULL;
6129 xmlXPathObjectPtr result = NULL;
6130 char *string = NULL;
6131 #endif
6134 if (!context) return IE_INVALID_CONTEXT;
6135 zfree(context->long_message);
6136 if (buffer) zfree(*buffer);
6137 if (!buffer || !buffer_length) return IE_INVAL;
6140 #if HAVE_LIBCURL
6141 /* Check if connection is established
6142 * TODO: This check should be done downstairs. */
6143 if (!context->curl) return IE_CONNECTION_CLOSED;
6146 /* Build AuthenticateMessage request */
6147 request = xmlNewNode(NULL, BAD_CAST "GetDataBoxList");
6148 if (!request) {
6149 isds_log_message(context,
6150 _("Could not build GetDataBoxList request"));
6151 return IE_ERROR;
6153 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6154 if(!isds_ns) {
6155 isds_log_message(context, _("Could not create ISDS name space"));
6156 xmlFreeNode(request);
6157 return IE_ERROR;
6159 xmlSetNs(request, isds_ns);
6160 INSERT_STRING(request, "dblType", list_identifier);
6162 /* Send request to server and process response */
6163 err = send_destroy_request_check_response(context,
6164 SERVICE_DB_SEARCH, BAD_CAST "GetDataBoxList", &request,
6165 &response, NULL);
6166 if (err) goto leave;
6169 /* Extract Base-64 encoded ZIP file */
6170 xpath_ctx = xmlXPathNewContext(response);
6171 if (!xpath_ctx) {
6172 err = IE_ERROR;
6173 goto leave;
6175 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6176 err = IE_ERROR;
6177 goto leave;
6179 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string);
6181 /* Decode non-empty archive */
6182 if (string && string[0] != '\0') {
6183 *buffer_length = _isds_b64decode(string, buffer);
6184 if (*buffer_length == (size_t) -1) {
6185 isds_printf_message(context,
6186 _("Error while Base64-decoding box list archive"));
6187 err = IE_ERROR;
6188 goto leave;
6193 leave:
6194 free(string);
6195 xmlXPathFreeObject(result);
6196 xmlXPathFreeContext(xpath_ctx);
6197 xmlFreeDoc(response);
6198 xmlFreeNode(request);
6200 if (!err) {
6201 isds_log(ILF_ISDS, ILL_DEBUG, _("GetDataBoxList request "
6202 "processed by server successfully.\n"));
6204 #else /* not HAVE_LIBCURL */
6205 err = IE_NOTSUP;
6206 #endif
6208 return err;
6212 /* Find boxes suiting given criteria.
6213 * @criteria is filter. You should fill in at least some members.
6214 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
6215 * possibly empty. Input NULL or valid old structure.
6216 * @return:
6217 * IE_SUCCESS if search succeeded, @boxes contains useful data
6218 * IE_NOEXIST if no such box exists, @boxes will be NULL
6219 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
6220 * contains still valid data
6221 * other code if something bad happens. @boxes will be NULL. */
6222 isds_error isds_FindDataBox(struct isds_ctx *context,
6223 const struct isds_DbOwnerInfo *criteria,
6224 struct isds_list **boxes) {
6225 isds_error err = IE_SUCCESS;
6226 #if HAVE_LIBCURL
6227 _Bool truncated = 0;
6228 xmlNsPtr isds_ns = NULL;
6229 xmlNodePtr request = NULL;
6230 xmlDocPtr response = NULL;
6231 xmlChar *code = NULL, *message = NULL;
6232 xmlNodePtr db_owner_info;
6233 xmlXPathContextPtr xpath_ctx = NULL;
6234 xmlXPathObjectPtr result = NULL;
6235 xmlChar *string = NULL;
6236 #endif
6239 if (!context) return IE_INVALID_CONTEXT;
6240 zfree(context->long_message);
6241 if (!boxes) return IE_INVAL;
6242 isds_list_free(boxes);
6244 if (!criteria) {
6245 return IE_INVAL;
6248 #if HAVE_LIBCURL
6249 /* Check if connection is established
6250 * TODO: This check should be done downstairs. */
6251 if (!context->curl) return IE_CONNECTION_CLOSED;
6254 /* Build FindDataBox request */
6255 request = xmlNewNode(NULL, BAD_CAST "FindDataBox");
6256 if (!request) {
6257 isds_log_message(context,
6258 _("Could build FindDataBox request"));
6259 return IE_ERROR;
6261 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6262 if(!isds_ns) {
6263 isds_log_message(context, _("Could not create ISDS name space"));
6264 xmlFreeNode(request);
6265 return IE_ERROR;
6267 xmlSetNs(request, isds_ns);
6268 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
6269 if (!db_owner_info) {
6270 isds_log_message(context, _("Could not add dbOwnerInfo child to "
6271 "FindDataBox element"));
6272 xmlFreeNode(request);
6273 return IE_ERROR;
6276 err = insert_DbOwnerInfo(context, criteria, db_owner_info);
6277 if (err) goto leave;
6280 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending FindDataBox request to ISDS\n"));
6282 /* Sent request */
6283 err = isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
6285 /* Destroy request */
6286 xmlFreeNode(request); request = NULL;
6288 if (err) {
6289 isds_log(ILF_ISDS, ILL_DEBUG,
6290 _("Processing ISDS response on FindDataBox "
6291 "request failed\n"));
6292 goto leave;
6295 /* Check for response status */
6296 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
6297 &code, &message, NULL);
6298 if (err) {
6299 isds_log(ILF_ISDS, ILL_DEBUG,
6300 _("ISDS response on FindDataBox request is missing status\n"));
6301 goto leave;
6304 /* Request processed, but nothing found */
6305 if (!xmlStrcmp(code, BAD_CAST "0002") ||
6306 !xmlStrcmp(code, BAD_CAST "5001")) {
6307 char *code_locale = _isds_utf82locale((char*)code);
6308 char *message_locale = _isds_utf82locale((char*)message);
6309 isds_log(ILF_ISDS, ILL_DEBUG,
6310 _("Server did not found any box on FindDataBox request "
6311 "(code=%s, message=%s)\n"), code_locale, message_locale);
6312 isds_log_message(context, message_locale);
6313 free(code_locale);
6314 free(message_locale);
6315 err = IE_NOEXIST;
6316 goto leave;
6319 /* Warning, not a error */
6320 if (!xmlStrcmp(code, BAD_CAST "0003")) {
6321 char *code_locale = _isds_utf82locale((char*)code);
6322 char *message_locale = _isds_utf82locale((char*)message);
6323 isds_log(ILF_ISDS, ILL_DEBUG,
6324 _("Server truncated response on FindDataBox request "
6325 "(code=%s, message=%s)\n"), code_locale, message_locale);
6326 isds_log_message(context, message_locale);
6327 free(code_locale);
6328 free(message_locale);
6329 truncated = 1;
6332 /* Other error */
6333 else if (xmlStrcmp(code, BAD_CAST "0000")) {
6334 char *code_locale = _isds_utf82locale((char*)code);
6335 char *message_locale = _isds_utf82locale((char*)message);
6336 isds_log(ILF_ISDS, ILL_DEBUG,
6337 _("Server refused FindDataBox request "
6338 "(code=%s, message=%s)\n"), code_locale, message_locale);
6339 isds_log_message(context, message_locale);
6340 free(code_locale);
6341 free(message_locale);
6342 err = IE_ISDS;
6343 goto leave;
6346 xpath_ctx = xmlXPathNewContext(response);
6347 if (!xpath_ctx) {
6348 err = IE_ERROR;
6349 goto leave;
6351 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6352 err = IE_ERROR;
6353 goto leave;
6356 /* Extract boxes if they present */
6357 result = xmlXPathEvalExpression(BAD_CAST
6358 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
6359 xpath_ctx);
6360 if (!result) {
6361 err = IE_ERROR;
6362 goto leave;
6364 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6365 struct isds_list *item, *prev_item = NULL;
6366 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
6367 item = calloc(1, sizeof(*item));
6368 if (!item) {
6369 err = IE_NOMEM;
6370 goto leave;
6373 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
6374 if (i == 0) *boxes = item;
6375 else prev_item->next = item;
6376 prev_item = item;
6378 xpath_ctx->node = result->nodesetval->nodeTab[i];
6379 err = extract_DbOwnerInfo(context,
6380 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
6381 if (err) goto leave;
6385 leave:
6386 if (err) {
6387 isds_list_free(boxes);
6388 } else {
6389 if (truncated) err = IE_2BIG;
6392 free(string);
6393 xmlFreeNode(request);
6394 xmlXPathFreeObject(result);
6395 xmlXPathFreeContext(xpath_ctx);
6397 free(code);
6398 free(message);
6399 xmlFreeDoc(response);
6401 if (!err)
6402 isds_log(ILF_ISDS, ILL_DEBUG,
6403 _("FindDataBox request processed by server successfully.\n"));
6404 #else /* not HAVE_LIBCURL */
6405 err = IE_NOTSUP;
6406 #endif
6408 return err;
6412 /* Get status of a box.
6413 * @context is ISDS session context.
6414 * @box_id is UTF-8 encoded box identifier as zero terminated string
6415 * @box_status is return value of box status.
6416 * @return:
6417 * IE_SUCCESS if box has been found and its status retrieved
6418 * IE_NOEXIST if box is not known to ISDS server
6419 * or other appropriate error.
6420 * You can use isds_DbState to enumerate box status. However out of enum
6421 * range value can be returned too. This is feature because ISDS
6422 * specification leaves the set of values open.
6423 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
6424 * the box has been deleted, but ISDS still lists its former existence. */
6425 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
6426 long int *box_status) {
6427 isds_error err = IE_SUCCESS;
6428 #if HAVE_LIBCURL
6429 xmlNsPtr isds_ns = NULL;
6430 xmlNodePtr request = NULL, db_id;
6431 xmlDocPtr response = NULL;
6432 xmlChar *code = NULL, *message = NULL;
6433 xmlXPathContextPtr xpath_ctx = NULL;
6434 xmlXPathObjectPtr result = NULL;
6435 xmlChar *string = NULL;
6436 #endif
6438 if (!context) return IE_INVALID_CONTEXT;
6439 zfree(context->long_message);
6440 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
6442 #if HAVE_LIBCURL
6443 /* Check if connection is established
6444 * TODO: This check should be done downstairs. */
6445 if (!context->curl) return IE_CONNECTION_CLOSED;
6448 /* Build CheckDataBox request */
6449 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
6450 if (!request) {
6451 isds_log_message(context,
6452 _("Could build CheckDataBox request"));
6453 return IE_ERROR;
6455 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6456 if(!isds_ns) {
6457 isds_log_message(context, _("Could not create ISDS name space"));
6458 xmlFreeNode(request);
6459 return IE_ERROR;
6461 xmlSetNs(request, isds_ns);
6462 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
6463 if (!db_id) {
6464 isds_log_message(context, _("Could not add dbID child to "
6465 "CheckDataBox element"));
6466 xmlFreeNode(request);
6467 return IE_ERROR;
6471 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CheckDataBox request to ISDS\n"));
6473 /* Sent request */
6474 err = isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
6476 /* Destroy request */
6477 xmlFreeNode(request);
6479 if (err) {
6480 isds_log(ILF_ISDS, ILL_DEBUG,
6481 _("Processing ISDS response on CheckDataBox "
6482 "request failed\n"));
6483 goto leave;
6486 /* Check for response status */
6487 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
6488 &code, &message, NULL);
6489 if (err) {
6490 isds_log(ILF_ISDS, ILL_DEBUG,
6491 _("ISDS response on CheckDataBox request is missing status\n"));
6492 goto leave;
6495 /* Request processed, but nothing found */
6496 if (!xmlStrcmp(code, BAD_CAST "5001")) {
6497 char *box_id_locale = _isds_utf82locale((char*)box_id);
6498 char *code_locale = _isds_utf82locale((char*)code);
6499 char *message_locale = _isds_utf82locale((char*)message);
6500 isds_log(ILF_ISDS, ILL_DEBUG,
6501 _("Server did not found box %s on CheckDataBox request "
6502 "(code=%s, message=%s)\n"),
6503 box_id_locale, code_locale, message_locale);
6504 isds_log_message(context, message_locale);
6505 free(box_id_locale);
6506 free(code_locale);
6507 free(message_locale);
6508 err = IE_NOEXIST;
6509 goto leave;
6512 /* Other error */
6513 else if (xmlStrcmp(code, BAD_CAST "0000")) {
6514 char *code_locale = _isds_utf82locale((char*)code);
6515 char *message_locale = _isds_utf82locale((char*)message);
6516 isds_log(ILF_ISDS, ILL_DEBUG,
6517 _("Server refused CheckDataBox request "
6518 "(code=%s, message=%s)\n"), code_locale, message_locale);
6519 isds_log_message(context, message_locale);
6520 free(code_locale);
6521 free(message_locale);
6522 err = IE_ISDS;
6523 goto leave;
6526 /* Extract data */
6527 xpath_ctx = xmlXPathNewContext(response);
6528 if (!xpath_ctx) {
6529 err = IE_ERROR;
6530 goto leave;
6532 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6533 err = IE_ERROR;
6534 goto leave;
6536 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
6537 xpath_ctx);
6538 if (!result) {
6539 err = IE_ERROR;
6540 goto leave;
6542 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6543 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
6544 err = IE_ISDS;
6545 goto leave;
6547 if (result->nodesetval->nodeNr > 1) {
6548 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
6549 err = IE_ISDS;
6550 goto leave;
6552 xpath_ctx->node = result->nodesetval->nodeTab[0];
6553 xmlXPathFreeObject(result); result = NULL;
6555 EXTRACT_LONGINT("isds:dbState", box_status, 1);
6558 leave:
6559 free(string);
6560 xmlXPathFreeObject(result);
6561 xmlXPathFreeContext(xpath_ctx);
6563 free(code);
6564 free(message);
6565 xmlFreeDoc(response);
6567 if (!err)
6568 isds_log(ILF_ISDS, ILL_DEBUG,
6569 _("CheckDataBox request processed by server successfully.\n"));
6570 #else /* not HAVE_LIBCURL */
6571 err = IE_NOTSUP;
6572 #endif
6574 return err;
6578 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
6579 * code, destroy response and log success.
6580 * @context is ISDS session context.
6581 * @service_name is name of SERVICE_DB_MANIPULATION service
6582 * @box_id is UTF-8 encoded box identifier as zero terminated string
6583 * @approval is optional external approval of box manipulation
6584 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6585 * NULL, if you don't care. */
6586 static isds_error build_send_manipulationdbid_request_check_drop_response(
6587 struct isds_ctx *context, const xmlChar *service_name,
6588 const xmlChar *box_id, const struct isds_approval *approval,
6589 xmlChar **refnumber) {
6590 isds_error err = IE_SUCCESS;
6591 #if HAVE_LIBCURL
6592 xmlDocPtr response = NULL;
6593 #endif
6595 if (!context) return IE_INVALID_CONTEXT;
6596 zfree(context->long_message);
6597 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
6599 #if HAVE_LIBCURL
6600 /* Check if connection is established */
6601 if (!context->curl) return IE_CONNECTION_CLOSED;
6603 /* Do request and check for success */
6604 err = build_send_dbid_request_check_response(context,
6605 SERVICE_DB_MANIPULATION, service_name, box_id, approval,
6606 &response, refnumber);
6607 xmlFreeDoc(response);
6609 if (!err) {
6610 char *service_name_locale = _isds_utf82locale((char *) service_name);
6611 isds_log(ILF_ISDS, ILL_DEBUG,
6612 _("%s request processed by server successfully.\n"),
6613 service_name_locale);
6614 free(service_name_locale);
6616 #else /* not HAVE_LIBCURL */
6617 err = IE_NOTSUP;
6618 #endif
6620 return err;
6624 /* Switch box into state where box can receive commercial messages (off by
6625 * default)
6626 * @context is ISDS session context.
6627 * @box_id is UTF-8 encoded box identifier as zero terminated string
6628 * @allow is true for enable, false for disable commercial messages income
6629 * @approval is optional external approval of box manipulation
6630 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6631 * NULL, if you don't care. */
6632 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
6633 const char *box_id, const _Bool allow,
6634 const struct isds_approval *approval, char **refnumber) {
6635 return build_send_manipulationdbid_request_check_drop_response(context,
6636 (allow) ? BAD_CAST "SetOpenAddressing" :
6637 BAD_CAST "ClearOpenAddressing",
6638 BAD_CAST box_id, approval, (xmlChar **) refnumber);
6642 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
6643 * message acceptance). This is just a box permission. Sender must apply
6644 * such role by sending each message.
6645 * @context is ISDS session context.
6646 * @box_id is UTF-8 encoded box identifier as zero terminated string
6647 * @allow is true for enable, false for disable OVM role permission
6648 * @approval is optional external approval of box manipulation
6649 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6650 * NULL, if you don't care. */
6651 isds_error isds_switch_effective_ovm(struct isds_ctx *context,
6652 const char *box_id, const _Bool allow,
6653 const struct isds_approval *approval, char **refnumber) {
6654 return build_send_manipulationdbid_request_check_drop_response(context,
6655 (allow) ? BAD_CAST "SetEffectiveOVM" :
6656 BAD_CAST "ClearEffectiveOVM",
6657 BAD_CAST box_id, approval, (xmlChar **) refnumber);
6661 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
6662 * code, destroy response and log success.
6663 * @context is ISDS session context.
6664 * @service_name is name of SERVICE_DB_MANIPULATION service
6665 * @owner is structure describing box
6666 * @approval is optional external approval of box manipulation
6667 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6668 * NULL, if you don't care. */
6669 static isds_error build_send_manipulationdbowner_request_check_drop_response(
6670 struct isds_ctx *context, const xmlChar *service_name,
6671 const struct isds_DbOwnerInfo *owner,
6672 const struct isds_approval *approval, xmlChar **refnumber) {
6673 isds_error err = IE_SUCCESS;
6674 #if HAVE_LIBCURL
6675 char *service_name_locale = NULL;
6676 xmlNodePtr request = NULL, db_owner_info;
6677 xmlNsPtr isds_ns = NULL;
6678 #endif
6681 if (!context) return IE_INVALID_CONTEXT;
6682 zfree(context->long_message);
6683 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
6685 #if HAVE_LIBCURL
6686 service_name_locale = _isds_utf82locale((char*)service_name);
6687 if (!service_name_locale) {
6688 err = IE_NOMEM;
6689 goto leave;
6692 /* Build request */
6693 request = xmlNewNode(NULL, service_name);
6694 if (!request) {
6695 isds_printf_message(context,
6696 _("Could not build %s request"), service_name_locale);
6697 err = IE_ERROR;
6698 goto leave;
6700 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6701 if(!isds_ns) {
6702 isds_log_message(context, _("Could not create ISDS name space"));
6703 err = IE_ERROR;
6704 goto leave;
6706 xmlSetNs(request, isds_ns);
6709 /* Add XSD:tOwnerInfoInput child*/
6710 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
6711 err = insert_DbOwnerInfo(context, owner, db_owner_info);
6712 if (err) goto leave;
6714 /* Add XSD:gExtApproval*/
6715 err = insert_GExtApproval(context, approval, request);
6716 if (err) goto leave;
6718 /* Send it to server and process response */
6719 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6720 service_name, &request, refnumber);
6722 leave:
6723 xmlFreeNode(request);
6724 free(service_name_locale);
6725 #else /* not HAVE_LIBCURL */
6726 err = IE_NOTSUP;
6727 #endif
6729 return err;
6733 /* Switch box accessibility state on request of box owner.
6734 * Despite the name, owner must do the request off-line. This function is
6735 * designed for such off-line meeting points (e.g. Czech POINT).
6736 * @context is ISDS session context.
6737 * @box identifies box to switch accessibility state.
6738 * @allow is true for making accessible, false to disallow access.
6739 * @approval is optional external approval of box manipulation
6740 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6741 * NULL, if you don't care. */
6742 isds_error isds_switch_box_accessibility_on_owner_request(
6743 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
6744 const _Bool allow, const struct isds_approval *approval,
6745 char **refnumber) {
6746 return build_send_manipulationdbowner_request_check_drop_response(context,
6747 (allow) ? BAD_CAST "EnableOwnDataBox" :
6748 BAD_CAST "DisableOwnDataBox",
6749 box, approval, (xmlChar **) refnumber);
6753 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
6754 * date.
6755 * @context is ISDS session context.
6756 * @box identifies box to switch accessibility state.
6757 * @since is date since accessibility has been denied. This can be past too.
6758 * Only tm_year, tm_mon and tm_mday carry sane value.
6759 * @approval is optional external approval of box manipulation
6760 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6761 * NULL, if you don't care. */
6762 isds_error isds_disable_box_accessibility_externaly(
6763 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
6764 const struct tm *since, const struct isds_approval *approval,
6765 char **refnumber) {
6766 isds_error err = IE_SUCCESS;
6767 #if HAVE_LIBCURL
6768 char *service_name_locale = NULL;
6769 xmlNodePtr request = NULL, node;
6770 xmlNsPtr isds_ns = NULL;
6771 xmlChar *string = NULL;
6772 #endif
6775 if (!context) return IE_INVALID_CONTEXT;
6776 zfree(context->long_message);
6777 if (!box || !since) return IE_INVAL;
6779 #if HAVE_LIBCURL
6780 /* Build request */
6781 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
6782 if (!request) {
6783 isds_printf_message(context,
6784 _("Could not build %s request"), "DisableDataBoxExternally");
6785 err = IE_ERROR;
6786 goto leave;
6788 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6789 if(!isds_ns) {
6790 isds_log_message(context, _("Could not create ISDS name space"));
6791 err = IE_ERROR;
6792 goto leave;
6794 xmlSetNs(request, isds_ns);
6797 /* Add @box identification */
6798 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6799 err = insert_DbOwnerInfo(context, box, node);
6800 if (err) goto leave;
6802 /* Add @since date */
6803 err = tm2datestring(since, &string);
6804 if(err) {
6805 isds_log_message(context,
6806 _("Could not convert `since' argument to ISO date string"));
6807 goto leave;
6809 INSERT_STRING(request, "dbOwnerDisableDate", string);
6810 zfree(string);
6812 /* Add @approval */
6813 err = insert_GExtApproval(context, approval, request);
6814 if (err) goto leave;
6816 /* Send it to server and process response */
6817 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6818 BAD_CAST "DisableDataBoxExternally", &request,
6819 (xmlChar **) refnumber);
6821 leave:
6822 free(string);
6823 xmlFreeNode(request);
6824 free(service_name_locale);
6825 #else /* not HAVE_LIBCURL */
6826 err = IE_NOTSUP;
6827 #endif
6829 return err;
6833 #if HAVE_LIBCURL
6834 /* Insert struct isds_message data (envelope (recipient data optional) and
6835 * documents) into XML tree
6836 * @context is session context
6837 * @outgoing_message is libisds structure with message data
6838 * @create_message is XML CreateMessage or CreateMultipleMessage element
6839 * @process_recipient true for recipient data serialization, false for no
6840 * serialization */
6841 static isds_error insert_envelope_files(struct isds_ctx *context,
6842 const struct isds_message *outgoing_message, xmlNodePtr create_message,
6843 const _Bool process_recipient) {
6845 isds_error err = IE_SUCCESS;
6846 xmlNodePtr envelope, dm_files, node;
6847 xmlChar *string = NULL;
6849 if (!context) return IE_INVALID_CONTEXT;
6850 if (!outgoing_message || !create_message) return IE_INVAL;
6853 /* Build envelope */
6854 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
6855 if (!envelope) {
6856 isds_printf_message(context, _("Could not add dmEnvelope child to "
6857 "%s element"), create_message->name);
6858 return IE_ERROR;
6861 if (!outgoing_message->envelope) {
6862 isds_log_message(context, _("Outgoing message is missing envelope"));
6863 err = IE_INVAL;
6864 goto leave;
6867 /* Insert optional message type */
6868 err = insert_message_type(context, outgoing_message->envelope->dmType,
6869 envelope);
6870 if (err) goto leave;
6872 INSERT_STRING(envelope, "dmSenderOrgUnit",
6873 outgoing_message->envelope->dmSenderOrgUnit);
6874 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
6875 outgoing_message->envelope->dmSenderOrgUnitNum, string);
6877 if (process_recipient) {
6878 if (!outgoing_message->envelope->dbIDRecipient) {
6879 isds_log_message(context,
6880 _("Outgoing message is missing recipient box identifier"));
6881 err = IE_INVAL;
6882 goto leave;
6884 INSERT_STRING(envelope, "dbIDRecipient",
6885 outgoing_message->envelope->dbIDRecipient);
6887 INSERT_STRING(envelope, "dmRecipientOrgUnit",
6888 outgoing_message->envelope->dmRecipientOrgUnit);
6889 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
6890 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
6891 INSERT_STRING(envelope, "dmToHands",
6892 outgoing_message->envelope->dmToHands);
6895 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
6896 "dmAnnotation");
6897 INSERT_STRING(envelope, "dmAnnotation",
6898 outgoing_message->envelope->dmAnnotation);
6900 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
6901 0, 50, "dmRecipientRefNumber");
6902 INSERT_STRING(envelope, "dmRecipientRefNumber",
6903 outgoing_message->envelope->dmRecipientRefNumber);
6905 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
6906 0, 50, "dmSenderRefNumber");
6907 INSERT_STRING(envelope, "dmSenderRefNumber",
6908 outgoing_message->envelope->dmSenderRefNumber);
6910 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
6911 0, 50, "dmRecipientIdent");
6912 INSERT_STRING(envelope, "dmRecipientIdent",
6913 outgoing_message->envelope->dmRecipientIdent);
6915 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
6916 0, 50, "dmSenderIdent");
6917 INSERT_STRING(envelope, "dmSenderIdent",
6918 outgoing_message->envelope->dmSenderIdent);
6920 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
6921 outgoing_message->envelope->dmLegalTitleLaw, string);
6922 INSERT_LONGINT(envelope, "dmLegalTitleYear",
6923 outgoing_message->envelope->dmLegalTitleYear, string);
6924 INSERT_STRING(envelope, "dmLegalTitleSect",
6925 outgoing_message->envelope->dmLegalTitleSect);
6926 INSERT_STRING(envelope, "dmLegalTitlePar",
6927 outgoing_message->envelope->dmLegalTitlePar);
6928 INSERT_STRING(envelope, "dmLegalTitlePoint",
6929 outgoing_message->envelope->dmLegalTitlePoint);
6931 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
6932 outgoing_message->envelope->dmPersonalDelivery);
6933 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
6934 outgoing_message->envelope->dmAllowSubstDelivery);
6936 /* ???: Should we require value for dbEffectiveOVM sender?
6937 * ISDS has default as true */
6938 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
6939 INSERT_BOOLEAN(envelope, "dmOVM",
6940 outgoing_message->envelope->dmPublishOwnID);
6943 /* Append dmFiles */
6944 if (!outgoing_message->documents) {
6945 isds_log_message(context,
6946 _("Outgoing message is missing list of documents"));
6947 err = IE_INVAL;
6948 goto leave;
6950 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
6951 if (!dm_files) {
6952 isds_printf_message(context, _("Could not add dmFiles child to "
6953 "%s element"), create_message->name);
6954 err = IE_ERROR;
6955 goto leave;
6958 /* Check for document hierarchy */
6959 err = _isds_check_documents_hierarchy(context, outgoing_message->documents);
6960 if (err) goto leave;
6962 /* Process each document */
6963 for (struct isds_list *item =
6964 (struct isds_list *) outgoing_message->documents;
6965 item; item = item->next) {
6966 if (!item->data) {
6967 isds_log_message(context,
6968 _("List of documents contains empty item"));
6969 err = IE_INVAL;
6970 goto leave;
6972 /* FIXME: Check for dmFileMetaType and for document references.
6973 * Only first document can be of MAIN type */
6974 err = insert_document(context, (struct isds_document*) item->data,
6975 dm_files);
6977 if (err) goto leave;
6980 leave:
6981 free(string);
6982 return err;
6984 #endif /* HAVE_LIBCURL */
6987 /* Send a message via ISDS to a recipient
6988 * @context is session context
6989 * @outgoing_message is message to send; Some members are mandatory (like
6990 * dbIDRecipient), some are optional and some are irrelevant (especially data
6991 * about sender). Included pointer to isds_list documents must contain at
6992 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
6993 * members will be filled with valid data from ISDS. Exact list of write
6994 * members is subject to change. Currently dmID is changed.
6995 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
6996 isds_error isds_send_message(struct isds_ctx *context,
6997 struct isds_message *outgoing_message) {
6999 isds_error err = IE_SUCCESS;
7000 #if HAVE_LIBCURL
7001 xmlNsPtr isds_ns = NULL;
7002 xmlNodePtr request = NULL;
7003 xmlDocPtr response = NULL;
7004 xmlChar *code = NULL, *message = NULL;
7005 xmlXPathContextPtr xpath_ctx = NULL;
7006 xmlXPathObjectPtr result = NULL;
7007 /*_Bool message_is_complete = 0;*/
7008 #endif
7010 if (!context) return IE_INVALID_CONTEXT;
7011 zfree(context->long_message);
7012 if (!outgoing_message) return IE_INVAL;
7014 #if HAVE_LIBCURL
7015 /* Check if connection is established
7016 * TODO: This check should be done downstairs. */
7017 if (!context->curl) return IE_CONNECTION_CLOSED;
7020 /* Build CreateMessage request */
7021 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
7022 if (!request) {
7023 isds_log_message(context,
7024 _("Could not build CreateMessage request"));
7025 return IE_ERROR;
7027 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7028 if(!isds_ns) {
7029 isds_log_message(context, _("Could not create ISDS name space"));
7030 xmlFreeNode(request);
7031 return IE_ERROR;
7033 xmlSetNs(request, isds_ns);
7035 /* Append envelope and files */
7036 err = insert_envelope_files(context, outgoing_message, request, 1);
7037 if (err) goto leave;
7040 /* Signal we can serialize message since now */
7041 /*message_is_complete = 1;*/
7044 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
7046 /* Sent request */
7047 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
7049 /* Don't' destroy request, we want to provide it to application later */
7051 if (err) {
7052 isds_log(ILF_ISDS, ILL_DEBUG,
7053 _("Processing ISDS response on CreateMessage "
7054 "request failed\n"));
7055 goto leave;
7058 /* Check for response status */
7059 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
7060 &code, &message, NULL);
7061 if (err) {
7062 isds_log(ILF_ISDS, ILL_DEBUG,
7063 _("ISDS response on CreateMessage request "
7064 "is missing status\n"));
7065 goto leave;
7068 /* Request processed, but refused by server or server failed */
7069 if (xmlStrcmp(code, BAD_CAST "0000")) {
7070 char *box_id_locale =
7071 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
7072 char *code_locale = _isds_utf82locale((char*)code);
7073 char *message_locale = _isds_utf82locale((char*)message);
7074 isds_log(ILF_ISDS, ILL_DEBUG,
7075 _("Server did not accept message for %s on CreateMessage "
7076 "request (code=%s, message=%s)\n"),
7077 box_id_locale, code_locale, message_locale);
7078 isds_log_message(context, message_locale);
7079 free(box_id_locale);
7080 free(code_locale);
7081 free(message_locale);
7082 err = IE_ISDS;
7083 goto leave;
7087 /* Extract data */
7088 xpath_ctx = xmlXPathNewContext(response);
7089 if (!xpath_ctx) {
7090 err = IE_ERROR;
7091 goto leave;
7093 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7094 err = IE_ERROR;
7095 goto leave;
7097 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
7098 xpath_ctx);
7099 if (!result) {
7100 err = IE_ERROR;
7101 goto leave;
7103 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7104 isds_log_message(context, _("Missing CreateMessageResponse element"));
7105 err = IE_ISDS;
7106 goto leave;
7108 if (result->nodesetval->nodeNr > 1) {
7109 isds_log_message(context, _("Multiple CreateMessageResponse element"));
7110 err = IE_ISDS;
7111 goto leave;
7113 xpath_ctx->node = result->nodesetval->nodeTab[0];
7114 xmlXPathFreeObject(result); result = NULL;
7116 if (outgoing_message->envelope->dmID) {
7117 free(outgoing_message->envelope->dmID);
7118 outgoing_message->envelope->dmID = NULL;
7120 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
7121 if (!outgoing_message->envelope->dmID) {
7122 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
7123 "but did not return assigned message ID\n"));
7126 leave:
7127 /* TODO: Serialize message into structure member raw */
7128 /* XXX: Each web service transport message in different format.
7129 * Therefore it's not possible to save them directly.
7130 * To save them, one must figure out common format.
7131 * We can leave it on application, or we can implement the ESS format. */
7132 /*if (message_is_complete) {
7133 if (outgoing_message->envelope->dmID) {
7135 /* Add assigned message ID as first child*/
7136 /*xmlNodePtr dmid_text = xmlNewText(
7137 (xmlChar *) outgoing_message->envelope->dmID);
7138 if (!dmid_text) goto serialization_failed;
7140 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
7141 BAD_CAST "dmID");
7142 if (!dmid_element) {
7143 xmlFreeNode(dmid_text);
7144 goto serialization_failed;
7147 xmlNodePtr dmid_element_with_text =
7148 xmlAddChild(dmid_element, dmid_text);
7149 if (!dmid_element_with_text) {
7150 xmlFreeNode(dmid_element);
7151 xmlFreeNode(dmid_text);
7152 goto serialization_failed;
7155 node = xmlAddPrevSibling(envelope->childern,
7156 dmid_element_with_text);
7157 if (!node) {
7158 xmlFreeNodeList(dmid_element_with_text);
7159 goto serialization_failed;
7163 /* Serialize message with ID into raw */
7164 /*buffer = serialize_element(envelope)*/
7165 /* }
7167 serialization_failed:
7171 /* Clean up */
7172 xmlXPathFreeObject(result);
7173 xmlXPathFreeContext(xpath_ctx);
7175 free(code);
7176 free(message);
7177 xmlFreeDoc(response);
7178 xmlFreeNode(request);
7180 if (!err)
7181 isds_log(ILF_ISDS, ILL_DEBUG,
7182 _("CreateMessage request processed by server "
7183 "successfully.\n"));
7184 #else /* not HAVE_LIBCURL */
7185 err = IE_NOTSUP;
7186 #endif
7188 return err;
7192 /* Send a message via ISDS to a multiple recipients
7193 * @context is session context
7194 * @outgoing_message is message to send; Some members are mandatory,
7195 * some are optional and some are irrelevant (especially data
7196 * about sender). Data about recipient will be substituted by ISDS from
7197 * @copies. Included pointer to isds_list documents must
7198 * contain at least one document of FILEMETATYPE_MAIN.
7199 * @copies is list of isds_message_copy structures addressing all desired
7200 * recipients. This is read-write structure, some members will be filled with
7201 * valid data from ISDS (message IDs, error codes, error descriptions).
7202 * @return
7203 * ISDS_SUCCESS if all messages have been sent
7204 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
7205 * succeeded messages can be identified by copies->data->error),
7206 * or other error code if something other goes wrong. */
7207 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
7208 const struct isds_message *outgoing_message,
7209 struct isds_list *copies) {
7211 isds_error err = IE_SUCCESS;
7212 #if HAVE_LIBCURL
7213 isds_error append_err;
7214 xmlNsPtr isds_ns = NULL;
7215 xmlNodePtr request = NULL, recipients, recipient, node;
7216 struct isds_list *item;
7217 struct isds_message_copy *copy;
7218 xmlDocPtr response = NULL;
7219 xmlChar *code = NULL, *message = NULL;
7220 xmlXPathContextPtr xpath_ctx = NULL;
7221 xmlXPathObjectPtr result = NULL;
7222 xmlChar *string = NULL;
7223 int i;
7224 #endif
7226 if (!context) return IE_INVALID_CONTEXT;
7227 zfree(context->long_message);
7228 if (!outgoing_message || !copies) return IE_INVAL;
7230 #if HAVE_LIBCURL
7231 /* Check if connection is established
7232 * TODO: This check should be done downstairs. */
7233 if (!context->curl) return IE_CONNECTION_CLOSED;
7236 /* Build CreateMultipleMessage request */
7237 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
7238 if (!request) {
7239 isds_log_message(context,
7240 _("Could not build CreateMultipleMessage request"));
7241 return IE_ERROR;
7243 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7244 if(!isds_ns) {
7245 isds_log_message(context, _("Could not create ISDS name space"));
7246 xmlFreeNode(request);
7247 return IE_ERROR;
7249 xmlSetNs(request, isds_ns);
7252 /* Build recipients */
7253 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
7254 if (!recipients) {
7255 isds_log_message(context, _("Could not add dmRecipients child to "
7256 "CreateMultipleMessage element"));
7257 xmlFreeNode(request);
7258 return IE_ERROR;
7261 /* Insert each recipient */
7262 for (item = copies; item; item = item->next) {
7263 copy = (struct isds_message_copy *) item->data;
7264 if (!copy) {
7265 isds_log_message(context,
7266 _("`copies' list item contains empty data"));
7267 err = IE_INVAL;
7268 goto leave;
7271 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
7272 if (!recipient) {
7273 isds_log_message(context, _("Could not add dmRecipient child to "
7274 "dmRecipients element"));
7275 err = IE_ERROR;
7276 goto leave;
7279 if (!copy->dbIDRecipient) {
7280 isds_log_message(context,
7281 _("Message copy is missing recipient box identifier"));
7282 err = IE_INVAL;
7283 goto leave;
7285 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
7286 INSERT_STRING(recipient, "dmRecipientOrgUnit",
7287 copy->dmRecipientOrgUnit);
7288 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
7289 copy->dmRecipientOrgUnitNum, string);
7290 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
7293 /* Append envelope and files */
7294 err = insert_envelope_files(context, outgoing_message, request, 0);
7295 if (err) goto leave;
7298 isds_log(ILF_ISDS, ILL_DEBUG,
7299 _("Sending CreateMultipleMessage request to ISDS\n"));
7301 /* Sent request */
7302 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
7303 if (err) {
7304 isds_log(ILF_ISDS, ILL_DEBUG,
7305 _("Processing ISDS response on CreateMultipleMessage "
7306 "request failed\n"));
7307 goto leave;
7310 /* Check for response status */
7311 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
7312 &code, &message, NULL);
7313 if (err) {
7314 isds_log(ILF_ISDS, ILL_DEBUG,
7315 _("ISDS response on CreateMultipleMessage request "
7316 "is missing status\n"));
7317 goto leave;
7320 /* Request processed, but some copies failed */
7321 if (!xmlStrcmp(code, BAD_CAST "0004")) {
7322 char *box_id_locale =
7323 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
7324 char *code_locale = _isds_utf82locale((char*)code);
7325 char *message_locale = _isds_utf82locale((char*)message);
7326 isds_log(ILF_ISDS, ILL_DEBUG,
7327 _("Server did accept message for multiple recipients "
7328 "on CreateMultipleMessage request but delivery to "
7329 "some of them failed (code=%s, message=%s)\n"),
7330 box_id_locale, code_locale, message_locale);
7331 isds_log_message(context, message_locale);
7332 free(box_id_locale);
7333 free(code_locale);
7334 free(message_locale);
7335 err = IE_PARTIAL_SUCCESS;
7338 /* Request refused by server as whole */
7339 else if (xmlStrcmp(code, BAD_CAST "0000")) {
7340 char *box_id_locale =
7341 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
7342 char *code_locale = _isds_utf82locale((char*)code);
7343 char *message_locale = _isds_utf82locale((char*)message);
7344 isds_log(ILF_ISDS, ILL_DEBUG,
7345 _("Server did not accept message for multiple recipients "
7346 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
7347 box_id_locale, code_locale, message_locale);
7348 isds_log_message(context, message_locale);
7349 free(box_id_locale);
7350 free(code_locale);
7351 free(message_locale);
7352 err = IE_ISDS;
7353 goto leave;
7357 /* Extract data */
7358 xpath_ctx = xmlXPathNewContext(response);
7359 if (!xpath_ctx) {
7360 err = IE_ERROR;
7361 goto leave;
7363 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7364 err = IE_ERROR;
7365 goto leave;
7367 result = xmlXPathEvalExpression(
7368 BAD_CAST "/isds:CreateMultipleMessageResponse"
7369 "/isds:dmMultipleStatus/isds:dmSingleStatus",
7370 xpath_ctx);
7371 if (!result) {
7372 err = IE_ERROR;
7373 goto leave;
7375 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7376 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
7377 err = IE_ISDS;
7378 goto leave;
7381 /* Extract message ID and delivery status for each copy */
7382 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
7383 item = item->next, i++) {
7384 copy = (struct isds_message_copy *) item->data;
7385 xpath_ctx->node = result->nodesetval->nodeTab[i];
7387 append_err = append_TMStatus(context, copy, xpath_ctx);
7388 if (append_err) {
7389 err = append_err;
7390 goto leave;
7393 if (item || i < result->nodesetval->nodeNr) {
7394 isds_printf_message(context, _("ISDS returned unexpected number of "
7395 "message copy delivery states: %d"),
7396 result->nodesetval->nodeNr);
7397 err = IE_ISDS;
7398 goto leave;
7402 leave:
7403 /* Clean up */
7404 free(string);
7405 xmlXPathFreeObject(result);
7406 xmlXPathFreeContext(xpath_ctx);
7408 free(code);
7409 free(message);
7410 xmlFreeDoc(response);
7411 xmlFreeNode(request);
7413 if (!err)
7414 isds_log(ILF_ISDS, ILL_DEBUG,
7415 _("CreateMultipleMessageResponse request processed by server "
7416 "successfully.\n"));
7417 #else /* not HAVE_LIBCURL */
7418 err = IE_NOTSUP;
7419 #endif
7421 return err;
7425 /* Get list of messages. This is common core for getting sent or received
7426 * messages.
7427 * Any criterion argument can be NULL, if you don't care about it.
7428 * @context is session context. Must not be NULL.
7429 * @outgoing_direction is true if you want list of outgoing messages,
7430 * it's false if you want incoming messages.
7431 * @from_time is minimal time and date of message sending inclusive.
7432 * @to_time is maximal time and date of message sending inclusive
7433 * @organization_unit_number is number of sender/recipient respectively.
7434 * @status_filter is bit field of isds_message_status values. Use special
7435 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
7436 * all values, you can use bit-wise arithmetic if you want.)
7437 * @offset is index of first message we are interested in. First message is 1.
7438 * Set to 0 (or 1) if you don't care.
7439 * @number is maximal length of list you want to get as input value, outputs
7440 * number of messages matching these criteria. Can be NULL if you don't care
7441 * (applies to output value either).
7442 * @messages is automatically reallocated list of isds_message's. Be ware that
7443 * it returns only brief overview (envelope and some other fields) about each
7444 * message, not the complete message. FIXME: Specify exact fields.
7445 * The list is sorted by delivery time in ascending order.
7446 * Use NULL if you don't care about don't need the data (useful if you want to
7447 * know only the @number). If you provide &NULL, list will be allocated on
7448 * heap, if you provide pointer to non-NULL, list will be freed automatically
7449 * at first. Also in case of error the list will be NULLed.
7450 * @return IE_SUCCESS or appropriate error code. */
7451 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
7452 _Bool outgoing_direction,
7453 const struct timeval *from_time, const struct timeval *to_time,
7454 const long int *organization_unit_number,
7455 const unsigned int status_filter,
7456 const unsigned long int offset, unsigned long int *number,
7457 struct isds_list **messages) {
7459 isds_error err = IE_SUCCESS;
7460 #if HAVE_LIBCURL
7461 xmlNsPtr isds_ns = NULL;
7462 xmlNodePtr request = NULL, node;
7463 xmlDocPtr response = NULL;
7464 xmlChar *code = NULL, *message = NULL;
7465 xmlXPathContextPtr xpath_ctx = NULL;
7466 xmlXPathObjectPtr result = NULL;
7467 xmlChar *string = NULL;
7468 long unsigned int count = 0;
7469 #endif
7471 if (!context) return IE_INVALID_CONTEXT;
7472 zfree(context->long_message);
7474 /* Free former message list if any */
7475 if (messages) isds_list_free(messages);
7477 #if HAVE_LIBCURL
7478 /* Check if connection is established
7479 * TODO: This check should be done downstairs. */
7480 if (!context->curl) return IE_CONNECTION_CLOSED;
7482 /* Build GetListOf*Messages request */
7483 request = xmlNewNode(NULL,
7484 (outgoing_direction) ?
7485 BAD_CAST "GetListOfSentMessages" :
7486 BAD_CAST "GetListOfReceivedMessages"
7488 if (!request) {
7489 isds_log_message(context,
7490 (outgoing_direction) ?
7491 _("Could not build GetListOfSentMessages request") :
7492 _("Could not build GetListOfReceivedMessages request")
7494 return IE_ERROR;
7496 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7497 if(!isds_ns) {
7498 isds_log_message(context, _("Could not create ISDS name space"));
7499 xmlFreeNode(request);
7500 return IE_ERROR;
7502 xmlSetNs(request, isds_ns);
7505 if (from_time) {
7506 err = timeval2timestring(from_time, &string);
7507 if (err) goto leave;
7509 INSERT_STRING(request, "dmFromTime", string);
7510 free(string); string = NULL;
7512 if (to_time) {
7513 err = timeval2timestring(to_time, &string);
7514 if (err) goto leave;
7516 INSERT_STRING(request, "dmToTime", string);
7517 free(string); string = NULL;
7519 if (outgoing_direction) {
7520 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
7521 organization_unit_number, string);
7522 } else {
7523 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
7524 organization_unit_number, string);
7527 if (status_filter > MESSAGESTATE_ANY) {
7528 isds_printf_message(context,
7529 _("Invalid message state filter value: %ld"), status_filter);
7530 err = IE_INVAL;
7531 goto leave;
7533 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
7535 if (offset > 0 ) {
7536 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
7537 } else {
7538 INSERT_STRING(request, "dmOffset", "1");
7541 /* number 0 means no limit */
7542 if (number && *number == 0) {
7543 INSERT_STRING(request, "dmLimit", NULL);
7544 } else {
7545 INSERT_ULONGINT(request, "dmLimit", number, string);
7549 isds_log(ILF_ISDS, ILL_DEBUG,
7550 (outgoing_direction) ?
7551 _("Sending GetListOfSentMessages request to ISDS\n") :
7552 _("Sending GetListOfReceivedMessages request to ISDS\n")
7555 /* Sent request */
7556 err = isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
7557 xmlFreeNode(request); request = NULL;
7559 if (err) {
7560 isds_log(ILF_ISDS, ILL_DEBUG,
7561 (outgoing_direction) ?
7562 _("Processing ISDS response on GetListOfSentMessages "
7563 "request failed\n") :
7564 _("Processing ISDS response on GetListOfReceivedMessages "
7565 "request failed\n")
7567 goto leave;
7570 /* Check for response status */
7571 err = isds_response_status(context, SERVICE_DM_INFO, response,
7572 &code, &message, NULL);
7573 if (err) {
7574 isds_log(ILF_ISDS, ILL_DEBUG,
7575 (outgoing_direction) ?
7576 _("ISDS response on GetListOfSentMessages request "
7577 "is missing status\n") :
7578 _("ISDS response on GetListOfReceivedMessages request "
7579 "is missing status\n")
7581 goto leave;
7584 /* Request processed, but nothing found */
7585 if (xmlStrcmp(code, BAD_CAST "0000")) {
7586 char *code_locale = _isds_utf82locale((char*)code);
7587 char *message_locale = _isds_utf82locale((char*)message);
7588 isds_log(ILF_ISDS, ILL_DEBUG,
7589 (outgoing_direction) ?
7590 _("Server refused GetListOfSentMessages request "
7591 "(code=%s, message=%s)\n") :
7592 _("Server refused GetListOfReceivedMessages request "
7593 "(code=%s, message=%s)\n"),
7594 code_locale, message_locale);
7595 isds_log_message(context, message_locale);
7596 free(code_locale);
7597 free(message_locale);
7598 err = IE_ISDS;
7599 goto leave;
7603 /* Extract data */
7604 xpath_ctx = xmlXPathNewContext(response);
7605 if (!xpath_ctx) {
7606 err = IE_ERROR;
7607 goto leave;
7609 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7610 err = IE_ERROR;
7611 goto leave;
7613 result = xmlXPathEvalExpression(
7614 (outgoing_direction) ?
7615 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
7616 "isds:dmRecords/isds:dmRecord" :
7617 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
7618 "isds:dmRecords/isds:dmRecord",
7619 xpath_ctx);
7620 if (!result) {
7621 err = IE_ERROR;
7622 goto leave;
7625 /* Fill output arguments in */
7626 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7627 struct isds_envelope *envelope;
7628 struct isds_list *item = NULL, *last_item = NULL;
7630 for (count = 0; count < result->nodesetval->nodeNr; count++) {
7631 /* Create new message */
7632 item = calloc(1, sizeof(*item));
7633 if (!item) {
7634 err = IE_NOMEM;
7635 goto leave;
7637 item->destructor = (void(*)(void**)) &isds_message_free;
7638 item->data = calloc(1, sizeof(struct isds_message));
7639 if (!item->data) {
7640 isds_list_free(&item);
7641 err = IE_NOMEM;
7642 goto leave;
7645 /* Extract envelope data */
7646 xpath_ctx->node = result->nodesetval->nodeTab[count];
7647 envelope = NULL;
7648 err = extract_DmRecord(context, &envelope, xpath_ctx);
7649 if (err) {
7650 isds_list_free(&item);
7651 goto leave;
7654 /* Attach extracted envelope */
7655 ((struct isds_message *) item->data)->envelope = envelope;
7657 /* Append new message into the list */
7658 if (!*messages) {
7659 *messages = last_item = item;
7660 } else {
7661 last_item->next = item;
7662 last_item = item;
7666 if (number) *number = count;
7668 leave:
7669 if (err) {
7670 isds_list_free(messages);
7673 free(string);
7674 xmlXPathFreeObject(result);
7675 xmlXPathFreeContext(xpath_ctx);
7677 free(code);
7678 free(message);
7679 xmlFreeDoc(response);
7680 xmlFreeNode(request);
7682 if (!err)
7683 isds_log(ILF_ISDS, ILL_DEBUG,
7684 (outgoing_direction) ?
7685 _("GetListOfSentMessages request processed by server "
7686 "successfully.\n") :
7687 _("GetListOfReceivedMessages request processed by server "
7688 "successfully.\n")
7690 #else /* not HAVE_LIBCURL */
7691 err = IE_NOTSUP;
7692 #endif
7693 return err;
7697 /* Get list of outgoing (already sent) messages.
7698 * Any criterion argument can be NULL, if you don't care about it.
7699 * @context is session context. Must not be NULL.
7700 * @from_time is minimal time and date of message sending inclusive.
7701 * @to_time is maximal time and date of message sending inclusive
7702 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
7703 * @status_filter is bit field of isds_message_status values. Use special
7704 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
7705 * all values, you can use bit-wise arithmetic if you want.)
7706 * @offset is index of first message we are interested in. First message is 1.
7707 * Set to 0 (or 1) if you don't care.
7708 * @number is maximal length of list you want to get as input value, outputs
7709 * number of messages matching these criteria. Can be NULL if you don't care
7710 * (applies to output value either).
7711 * @messages is automatically reallocated list of isds_message's. Be ware that
7712 * it returns only brief overview (envelope and some other fields) about each
7713 * message, not the complete message. FIXME: Specify exact fields.
7714 * The list is sorted by delivery time in ascending order.
7715 * Use NULL if you don't care about the meta data (useful if you want to know
7716 * only the @number). If you provide &NULL, list will be allocated on heap,
7717 * if you provide pointer to non-NULL, list will be freed automatically at
7718 * first. Also in case of error the list will be NULLed.
7719 * @return IE_SUCCESS or appropriate error code. */
7720 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
7721 const struct timeval *from_time, const struct timeval *to_time,
7722 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
7723 const unsigned long int offset, unsigned long int *number,
7724 struct isds_list **messages) {
7726 return isds_get_list_of_messages(
7727 context, 1,
7728 from_time, to_time, dmSenderOrgUnitNum, status_filter,
7729 offset, number,
7730 messages);
7734 /* Get list of incoming (addressed to you) messages.
7735 * Any criterion argument can be NULL, if you don't care about it.
7736 * @context is session context. Must not be NULL.
7737 * @from_time is minimal time and date of message sending inclusive.
7738 * @to_time is maximal time and date of message sending inclusive
7739 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
7740 * @status_filter is bit field of isds_message_status values. Use special
7741 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
7742 * all values, you can use bit-wise arithmetic if you want.)
7743 * @offset is index of first message we are interested in. First message is 1.
7744 * Set to 0 (or 1) if you don't care.
7745 * @number is maximal length of list you want to get as input value, outputs
7746 * number of messages matching these criteria. Can be NULL if you don't care
7747 * (applies to output value either).
7748 * @messages is automatically reallocated list of isds_message's. Be ware that
7749 * it returns only brief overview (envelope and some other fields) about each
7750 * message, not the complete message. FIXME: Specify exact fields.
7751 * Use NULL if you don't care about the meta data (useful if you want to know
7752 * only the @number). If you provide &NULL, list will be allocated on heap,
7753 * if you provide pointer to non-NULL, list will be freed automatically at
7754 * first. Also in case of error the list will be NULLed.
7755 * @return IE_SUCCESS or appropriate error code. */
7756 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
7757 const struct timeval *from_time, const struct timeval *to_time,
7758 const long int *dmRecipientOrgUnitNum,
7759 const unsigned int status_filter,
7760 const unsigned long int offset, unsigned long int *number,
7761 struct isds_list **messages) {
7763 return isds_get_list_of_messages(
7764 context, 0,
7765 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
7766 offset, number,
7767 messages);
7771 /* Get list of sent message state changes.
7772 * Any criterion argument can be NULL, if you don't care about it.
7773 * @context is session context. Must not be NULL.
7774 * @from_time is minimal time and date of status changes inclusive
7775 * @to_time is maximal time and date of status changes inclusive
7776 * @changed_states is automatically reallocated list of
7777 * isds_message_status_change's. If you provide &NULL, list will be allocated
7778 * on heap, if you provide pointer to non-NULL, list will be freed
7779 * automatically at first. Also in case of error the list will be NULLed.
7780 * XXX: The list item ordering is not specified.
7781 * XXX: Server provides only `recent' changes.
7782 * @return IE_SUCCESS or appropriate error code. */
7783 isds_error isds_get_list_of_sent_message_state_changes(
7784 struct isds_ctx *context,
7785 const struct timeval *from_time, const struct timeval *to_time,
7786 struct isds_list **changed_states) {
7788 isds_error err = IE_SUCCESS;
7789 #if HAVE_LIBCURL
7790 xmlNsPtr isds_ns = NULL;
7791 xmlNodePtr request = NULL, node;
7792 xmlDocPtr response = NULL;
7793 xmlXPathContextPtr xpath_ctx = NULL;
7794 xmlXPathObjectPtr result = NULL;
7795 xmlChar *string = NULL;
7796 long unsigned int count = 0;
7797 #endif
7799 if (!context) return IE_INVALID_CONTEXT;
7800 zfree(context->long_message);
7802 /* Free former message list if any */
7803 isds_list_free(changed_states);
7805 #if HAVE_LIBCURL
7806 /* Check if connection is established
7807 * TODO: This check should be done downstairs. */
7808 if (!context->curl) return IE_CONNECTION_CLOSED;
7810 /* Build GetMessageStateChanges request */
7811 request = xmlNewNode(NULL, BAD_CAST "GetMessageStateChanges");
7812 if (!request) {
7813 isds_log_message(context,
7814 _("Could not build GetMessageStateChanges request"));
7815 return IE_ERROR;
7817 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7818 if(!isds_ns) {
7819 isds_log_message(context, _("Could not create ISDS name space"));
7820 xmlFreeNode(request);
7821 return IE_ERROR;
7823 xmlSetNs(request, isds_ns);
7826 if (from_time) {
7827 err = timeval2timestring(from_time, &string);
7828 if (err) goto leave;
7830 INSERT_STRING(request, "dmFromTime", string);
7831 zfree(string);
7833 if (to_time) {
7834 err = timeval2timestring(to_time, &string);
7835 if (err) goto leave;
7837 INSERT_STRING(request, "dmToTime", string);
7838 zfree(string);
7841 /* Sent request */
7842 err = send_destroy_request_check_response(context,
7843 SERVICE_DM_INFO, BAD_CAST "GetMessageStateChanges", &request,
7844 &response, NULL);
7845 if (err) goto leave;
7848 /* Extract data */
7849 xpath_ctx = xmlXPathNewContext(response);
7850 if (!xpath_ctx) {
7851 err = IE_ERROR;
7852 goto leave;
7854 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7855 err = IE_ERROR;
7856 goto leave;
7858 result = xmlXPathEvalExpression(
7859 BAD_CAST "/isds:GetMessageStateChangesResponse/"
7860 "isds:dmRecords/isds:dmRecord", xpath_ctx);
7861 if (!result) {
7862 err = IE_ERROR;
7863 goto leave;
7866 /* Fill output arguments in */
7867 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7868 struct isds_list *item = NULL, *last_item = NULL;
7870 for (count = 0; count < result->nodesetval->nodeNr; count++) {
7871 /* Create new status change */
7872 item = calloc(1, sizeof(*item));
7873 if (!item) {
7874 err = IE_NOMEM;
7875 goto leave;
7877 item->destructor =
7878 (void(*)(void**)) &isds_message_status_change_free;
7880 /* Extract message status change */
7881 xpath_ctx->node = result->nodesetval->nodeTab[count];
7882 err = extract_StateChangesRecord(context,
7883 (struct isds_message_status_change **) &item->data,
7884 xpath_ctx);
7885 if (err) {
7886 isds_list_free(&item);
7887 goto leave;
7890 /* Append new message status change into the list */
7891 if (!*changed_states) {
7892 *changed_states = last_item = item;
7893 } else {
7894 last_item->next = item;
7895 last_item = item;
7900 leave:
7901 if (err) {
7902 isds_list_free(changed_states);
7905 free(string);
7906 xmlXPathFreeObject(result);
7907 xmlXPathFreeContext(xpath_ctx);
7908 xmlFreeDoc(response);
7909 xmlFreeNode(request);
7911 if (!err)
7912 isds_log(ILF_ISDS, ILL_DEBUG,
7913 _("GetMessageStateChanges request processed by server "
7914 "successfully.\n"));
7915 #else /* not HAVE_LIBCURL */
7916 err = IE_NOTSUP;
7917 #endif
7918 return err;
7922 #if HAVE_LIBCURL
7923 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
7924 * code
7925 * @context is session context
7926 * @service is ISDS WS service handler
7927 * @service_name is name of SERVICE_DM_OPERATIONS
7928 * @message_id is message ID to send as service argument to ISDS
7929 * @response is server SOAP body response as XML document
7930 * @raw_response is automatically reallocated bit stream with response body. Use
7931 * NULL if you don't care
7932 * @raw_response_length is size of @raw_response in bytes
7933 * @code is ISDS status code
7934 * @status_message is ISDS status message
7935 * @return error coded from lower layer, context message will be set up
7936 * appropriately. */
7937 static isds_error build_send_check_message_request(struct isds_ctx *context,
7938 const isds_service service, const xmlChar *service_name,
7939 const char *message_id,
7940 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
7941 xmlChar **code, xmlChar **status_message) {
7943 isds_error err = IE_SUCCESS;
7944 char *service_name_locale = NULL, *message_id_locale = NULL;
7945 xmlNodePtr request = NULL, node;
7946 xmlNsPtr isds_ns = NULL;
7948 if (!context) return IE_INVALID_CONTEXT;
7949 if (!service_name || !message_id) return IE_INVAL;
7950 if (!response || !code || !status_message) return IE_INVAL;
7951 if (!raw_response_length && raw_response) return IE_INVAL;
7953 /* Free output argument */
7954 xmlFreeDoc(*response); *response = NULL;
7955 if (raw_response) zfree(*raw_response);
7956 free(*code);
7957 free(*status_message);
7960 /* Check if connection is established
7961 * TODO: This check should be done downstairs. */
7962 if (!context->curl) return IE_CONNECTION_CLOSED;
7964 service_name_locale = _isds_utf82locale((char*)service_name);
7965 message_id_locale = _isds_utf82locale(message_id);
7966 if (!service_name_locale || !message_id_locale) {
7967 err = IE_NOMEM;
7968 goto leave;
7971 /* Build request */
7972 request = xmlNewNode(NULL, service_name);
7973 if (!request) {
7974 isds_printf_message(context,
7975 _("Could not build %s request"), service_name_locale);
7976 err = IE_ERROR;
7977 goto leave;
7979 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7980 if(!isds_ns) {
7981 isds_log_message(context, _("Could not create ISDS name space"));
7982 err = IE_ERROR;
7983 goto leave;
7985 xmlSetNs(request, isds_ns);
7988 /* Add requested ID */
7989 err = validate_message_id_length(context, (xmlChar *) message_id);
7990 if (err) goto leave;
7991 INSERT_STRING(request, "dmID", message_id);
7994 isds_log(ILF_ISDS, ILL_DEBUG,
7995 _("Sending %s request for %s message ID to ISDS\n"),
7996 service_name_locale, message_id_locale);
7998 /* Send request */
7999 err = isds(context, service, request, response,
8000 raw_response, raw_response_length);
8001 xmlFreeNode(request); request = NULL;
8003 if (err) {
8004 isds_log(ILF_ISDS, ILL_DEBUG,
8005 _("Processing ISDS response on %s request failed\n"),
8006 service_name_locale);
8007 goto leave;
8010 /* Check for response status */
8011 err = isds_response_status(context, service, *response,
8012 code, status_message, NULL);
8013 if (err) {
8014 isds_log(ILF_ISDS, ILL_DEBUG,
8015 _("ISDS response on %s request is missing status\n"),
8016 service_name_locale);
8017 goto leave;
8020 /* Request processed, but nothing found */
8021 if (xmlStrcmp(*code, BAD_CAST "0000")) {
8022 char *code_locale = _isds_utf82locale((char*) *code);
8023 char *status_message_locale = _isds_utf82locale((char*) *status_message);
8024 isds_log(ILF_ISDS, ILL_DEBUG,
8025 _("Server refused %s request for %s message ID "
8026 "(code=%s, message=%s)\n"),
8027 service_name_locale, message_id_locale,
8028 code_locale, status_message_locale);
8029 isds_log_message(context, status_message_locale);
8030 free(code_locale);
8031 free(status_message_locale);
8032 err = IE_ISDS;
8033 goto leave;
8036 leave:
8037 free(message_id_locale);
8038 free(service_name_locale);
8039 xmlFreeNode(request);
8040 return err;
8044 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
8045 * signed data and free ISDS response.
8046 * @context is session context
8047 * @message_id is UTF-8 encoded message ID for logging purpose
8048 * @response is parsed XML document. It will be freed and NULLed in the middle
8049 * of function run to save memory. This is not guaranteed in case of error.
8050 * @request_name is name of ISDS request used to construct response root
8051 * element name and for logging purpose.
8052 * @raw is reallocated output buffer with DER encoded CMS data
8053 * @raw_length is size of @raw buffer in bytes
8054 * @returns standard error codes, in case of error, @raw will be freed and
8055 * NULLed, @response sometimes. */
8056 static isds_error find_extract_signed_data_free_response(
8057 struct isds_ctx *context, const xmlChar *message_id,
8058 xmlDocPtr *response, const xmlChar *request_name,
8059 void **raw, size_t *raw_length) {
8061 isds_error err = IE_SUCCESS;
8062 char *xpath_expression = NULL;
8063 xmlXPathContextPtr xpath_ctx = NULL;
8064 xmlXPathObjectPtr result = NULL;
8065 char *encoded_structure = NULL;
8067 if (!context) return IE_INVALID_CONTEXT;
8068 if (!raw) return IE_INVAL;
8069 zfree(*raw);
8070 if (!message_id || !response || !*response || !request_name || !raw_length)
8071 return IE_INVAL;
8073 /* Build XPath expression */
8074 xpath_expression = _isds_astrcat3("/isds:", (char *) request_name,
8075 "Response/isds:dmSignature");
8076 if (!xpath_expression) return IE_NOMEM;
8078 /* Extract data */
8079 xpath_ctx = xmlXPathNewContext(*response);
8080 if (!xpath_ctx) {
8081 err = IE_ERROR;
8082 goto leave;
8084 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8085 err = IE_ERROR;
8086 goto leave;
8088 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
8089 if (!result) {
8090 err = IE_ERROR;
8091 goto leave;
8093 /* Empty response */
8094 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8095 char *message_id_locale = _isds_utf82locale((char*) message_id);
8096 isds_printf_message(context,
8097 _("Server did not return any signed data for message ID `%s' "
8098 "on %s request"),
8099 message_id_locale, request_name);
8100 free(message_id_locale);
8101 err = IE_ISDS;
8102 goto leave;
8104 /* More responses */
8105 if (result->nodesetval->nodeNr > 1) {
8106 char *message_id_locale = _isds_utf82locale((char*) message_id);
8107 isds_printf_message(context,
8108 _("Server did return more signed data for message ID `%s' "
8109 "on %s request"),
8110 message_id_locale, request_name);
8111 free(message_id_locale);
8112 err = IE_ISDS;
8113 goto leave;
8115 /* One response */
8116 xpath_ctx->node = result->nodesetval->nodeTab[0];
8118 /* Extract PKCS#7 structure */
8119 EXTRACT_STRING(".", encoded_structure);
8120 if (!encoded_structure) {
8121 isds_log_message(context, _("dmSignature element is empty"));
8124 /* Here we have delivery info as standalone CMS in encoded_structure.
8125 * We don't need any other data, free them: */
8126 xmlXPathFreeObject(result); result = NULL;
8127 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
8128 xmlFreeDoc(*response); *response = NULL;
8131 /* Decode PKCS#7 to DER format */
8132 *raw_length = _isds_b64decode(encoded_structure, raw);
8133 if (*raw_length == (size_t) -1) {
8134 isds_log_message(context,
8135 _("Error while Base64-decoding PKCS#7 structure"));
8136 err = IE_ERROR;
8137 goto leave;
8140 leave:
8141 if (err) {
8142 zfree(*raw);
8143 raw_length = 0;
8146 free(encoded_structure);
8147 xmlXPathFreeObject(result);
8148 xmlXPathFreeContext(xpath_ctx);
8149 free(xpath_expression);
8151 return err;
8153 #endif /* HAVE_LIBCURL */
8156 /* Download incoming message envelope identified by ID.
8157 * @context is session context
8158 * @message_id is message identifier (you can get them from
8159 * isds_get_list_of_received_messages())
8160 * @message is automatically reallocated message retrieved from ISDS.
8161 * It will miss documents per se. Use isds_get_received_message(), if you are
8162 * interested in documents (content) too.
8163 * Returned hash and timestamp require documents to be verifiable. */
8164 isds_error isds_get_received_envelope(struct isds_ctx *context,
8165 const char *message_id, struct isds_message **message) {
8167 isds_error err = IE_SUCCESS;
8168 #if HAVE_LIBCURL
8169 xmlDocPtr response = NULL;
8170 xmlChar *code = NULL, *status_message = NULL;
8171 xmlXPathContextPtr xpath_ctx = NULL;
8172 xmlXPathObjectPtr result = NULL;
8173 #endif
8175 if (!context) return IE_INVALID_CONTEXT;
8176 zfree(context->long_message);
8178 /* Free former message if any */
8179 if (!message) return IE_INVAL;
8180 isds_message_free(message);
8182 #if HAVE_LIBCURL
8183 /* Do request and check for success */
8184 err = build_send_check_message_request(context, SERVICE_DM_INFO,
8185 BAD_CAST "MessageEnvelopeDownload", message_id,
8186 &response, NULL, NULL, &code, &status_message);
8187 if (err) goto leave;
8189 /* Extract data */
8190 xpath_ctx = xmlXPathNewContext(response);
8191 if (!xpath_ctx) {
8192 err = IE_ERROR;
8193 goto leave;
8195 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8196 err = IE_ERROR;
8197 goto leave;
8199 result = xmlXPathEvalExpression(
8200 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
8201 "isds:dmReturnedMessageEnvelope",
8202 xpath_ctx);
8203 if (!result) {
8204 err = IE_ERROR;
8205 goto leave;
8207 /* Empty response */
8208 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8209 char *message_id_locale = _isds_utf82locale((char*) message_id);
8210 isds_printf_message(context,
8211 _("Server did not return any envelope for ID `%s' "
8212 "on MessageEnvelopeDownload request"), message_id_locale);
8213 free(message_id_locale);
8214 err = IE_ISDS;
8215 goto leave;
8217 /* More envelops */
8218 if (result->nodesetval->nodeNr > 1) {
8219 char *message_id_locale = _isds_utf82locale((char*) message_id);
8220 isds_printf_message(context,
8221 _("Server did return more envelopes for ID `%s' "
8222 "on MessageEnvelopeDownload request"), message_id_locale);
8223 free(message_id_locale);
8224 err = IE_ISDS;
8225 goto leave;
8227 /* One message */
8228 xpath_ctx->node = result->nodesetval->nodeTab[0];
8230 /* Extract the envelope (= message without documents, hence 0) */
8231 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
8232 if (err) goto leave;
8234 /* Save XML blob */
8235 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
8236 &(*message)->raw_length);
8238 leave:
8239 if (err) {
8240 isds_message_free(message);
8243 xmlXPathFreeObject(result);
8244 xmlXPathFreeContext(xpath_ctx);
8246 free(code);
8247 free(status_message);
8248 if (!*message || !(*message)->xml) {
8249 xmlFreeDoc(response);
8252 if (!err)
8253 isds_log(ILF_ISDS, ILL_DEBUG,
8254 _("MessageEnvelopeDownload request processed by server "
8255 "successfully.\n")
8257 #else /* not HAVE_LIBCURL */
8258 err = IE_NOTSUP;
8259 #endif
8260 return err;
8264 /* Load delivery info of any format from buffer.
8265 * @context is session context
8266 * @raw_type advertises format of @buffer content. Only delivery info types
8267 * are accepted.
8268 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
8269 * retrieve such data from message->raw after calling
8270 * isds_get_signed_delivery_info().
8271 * @length is length of buffer in bytes.
8272 * @message is automatically reallocated message parsed from @buffer.
8273 * @strategy selects how buffer will be attached into raw isds_message member.
8274 * */
8275 isds_error isds_load_delivery_info(struct isds_ctx *context,
8276 const isds_raw_type raw_type,
8277 const void *buffer, const size_t length,
8278 struct isds_message **message, const isds_buffer_strategy strategy) {
8280 isds_error err = IE_SUCCESS;
8281 message_ns_type message_ns;
8282 xmlDocPtr message_doc = NULL;
8283 xmlXPathContextPtr xpath_ctx = NULL;
8284 xmlXPathObjectPtr result = NULL;
8285 void *xml_stream = NULL;
8286 size_t xml_stream_length = 0;
8288 if (!context) return IE_INVALID_CONTEXT;
8289 zfree(context->long_message);
8290 if (!message) return IE_INVAL;
8291 isds_message_free(message);
8292 if (!buffer) return IE_INVAL;
8295 /* Select buffer format and extract XML from CMS*/
8296 switch (raw_type) {
8297 case RAWTYPE_DELIVERYINFO:
8298 message_ns = MESSAGE_NS_UNSIGNED;
8299 xml_stream = (void *) buffer;
8300 xml_stream_length = length;
8301 break;
8303 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
8304 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
8305 xml_stream = (void *) buffer;
8306 xml_stream_length = length;
8307 break;
8309 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
8310 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
8311 err = _isds_extract_cms_data(context, buffer, length,
8312 &xml_stream, &xml_stream_length);
8313 if (err) goto leave;
8314 break;
8316 default:
8317 isds_log_message(context, _("Bad raw delivery representation type"));
8318 return IE_INVAL;
8319 break;
8322 isds_log(ILF_ISDS, ILL_DEBUG,
8323 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
8324 xml_stream_length, xml_stream);
8326 /* Convert delivery info XML stream into XPath context */
8327 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
8328 if (!message_doc) {
8329 err = IE_XML;
8330 goto leave;
8332 xpath_ctx = xmlXPathNewContext(message_doc);
8333 if (!xpath_ctx) {
8334 err = IE_ERROR;
8335 goto leave;
8337 /* XXX: Name spaces mangled for signed delivery info:
8338 * http://isds.czechpoint.cz/v20/delivery:
8340 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
8341 * <q:dmDelivery>
8342 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
8343 * <p:dmID>170272</p:dmID>
8344 * ...
8345 * </p:dmDm>
8346 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
8347 * ...
8348 * </q:dmEvents>...</q:dmEvents>
8349 * </q:dmDelivery>
8350 * </q:GetDeliveryInfoResponse>
8351 * */
8352 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
8353 err = IE_ERROR;
8354 goto leave;
8356 result = xmlXPathEvalExpression(
8357 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
8358 xpath_ctx);
8359 if (!result) {
8360 err = IE_ERROR;
8361 goto leave;
8363 /* Empty delivery info */
8364 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8365 isds_printf_message(context,
8366 _("XML document is not sisds:dmDelivery document"));
8367 err = IE_ISDS;
8368 goto leave;
8370 /* More delivery info's */
8371 if (result->nodesetval->nodeNr > 1) {
8372 isds_printf_message(context,
8373 _("XML document has more sisds:dmDelivery elements"));
8374 err = IE_ISDS;
8375 goto leave;
8377 /* One delivery info */
8378 xpath_ctx->node = result->nodesetval->nodeTab[0];
8380 /* Extract the envelope (= message without documents, hence 0).
8381 * XXX: extract_TReturnedMessage() can obtain attachments size,
8382 * but delivery info carries none. It's coded as option elements,
8383 * so it should work. */
8384 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
8385 if (err) goto leave;
8387 /* Extract events */
8388 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
8389 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
8390 if (err) { err = IE_ERROR; goto leave; }
8391 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
8392 if (err) goto leave;
8394 /* Append raw CMS structure into message */
8395 (*message)->raw_type = raw_type;
8396 switch (strategy) {
8397 case BUFFER_DONT_STORE:
8398 break;
8399 case BUFFER_COPY:
8400 (*message)->raw = malloc(length);
8401 if (!(*message)->raw) {
8402 err = IE_NOMEM;
8403 goto leave;
8405 memcpy((*message)->raw, buffer, length);
8406 (*message)->raw_length = length;
8407 break;
8408 case BUFFER_MOVE:
8409 (*message)->raw = (void *) buffer;
8410 (*message)->raw_length = length;
8411 break;
8412 default:
8413 err = IE_ENUM;
8414 goto leave;
8417 leave:
8418 if (err) {
8419 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
8420 isds_message_free(message);
8423 xmlXPathFreeObject(result);
8424 xmlXPathFreeContext(xpath_ctx);
8425 if (!*message || !(*message)->xml) {
8426 xmlFreeDoc(message_doc);
8428 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
8430 if (!err)
8431 isds_log(ILF_ISDS, ILL_DEBUG,
8432 _("Delivery info loaded successfully.\n"));
8433 return err;
8437 /* Download signed delivery info-sheet of given message identified by ID.
8438 * @context is session context
8439 * @message_id is message identifier (you can get them from
8440 * isds_get_list_of_{sent,received}_messages())
8441 * @message is automatically reallocated message retrieved from ISDS.
8442 * It will miss documents per se. Use isds_get_signed_received_message(),
8443 * if you are interested in documents (content). OTOH, only this function
8444 * can get list events message has gone through. */
8445 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
8446 const char *message_id, struct isds_message **message) {
8448 isds_error err = IE_SUCCESS;
8449 #if HAVE_LIBCURL
8450 xmlDocPtr response = NULL;
8451 xmlChar *code = NULL, *status_message = NULL;
8452 void *raw = NULL;
8453 size_t raw_length = 0;
8454 #endif
8456 if (!context) return IE_INVALID_CONTEXT;
8457 zfree(context->long_message);
8459 /* Free former message if any */
8460 if (!message) return IE_INVAL;
8461 isds_message_free(message);
8463 #if HAVE_LIBCURL
8464 /* Do request and check for success */
8465 err = build_send_check_message_request(context, SERVICE_DM_INFO,
8466 BAD_CAST "GetSignedDeliveryInfo", message_id,
8467 &response, NULL, NULL, &code, &status_message);
8468 if (err) goto leave;
8470 /* Find signed delivery info, extract it into raw and maybe free
8471 * response */
8472 err = find_extract_signed_data_free_response(context,
8473 (xmlChar *)message_id, &response,
8474 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
8475 if (err) goto leave;
8477 /* Parse delivery info */
8478 err = isds_load_delivery_info(context,
8479 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
8480 message, BUFFER_MOVE);
8481 if (err) goto leave;
8483 raw = NULL;
8485 leave:
8486 if (err) {
8487 isds_message_free(message);
8490 free(raw);
8491 free(code);
8492 free(status_message);
8493 xmlFreeDoc(response);
8495 if (!err)
8496 isds_log(ILF_ISDS, ILL_DEBUG,
8497 _("GetSignedDeliveryInfo request processed by server "
8498 "successfully.\n")
8500 #else /* not HAVE_LIBCURL */
8501 err = IE_NOTSUP;
8502 #endif
8503 return err;
8507 /* Download delivery info-sheet of given message identified by ID.
8508 * @context is session context
8509 * @message_id is message identifier (you can get them from
8510 * isds_get_list_of_{sent,received}_messages())
8511 * @message is automatically reallocated message retrieved from ISDS.
8512 * It will miss documents per se. Use isds_get_received_message(), if you are
8513 * interested in documents (content). OTOH, only this function can get list
8514 * of events message has gone through. */
8515 isds_error isds_get_delivery_info(struct isds_ctx *context,
8516 const char *message_id, struct isds_message **message) {
8518 isds_error err = IE_SUCCESS;
8519 #if HAVE_LIBCURL
8520 xmlDocPtr response = NULL;
8521 xmlChar *code = NULL, *status_message = NULL;
8522 xmlNodePtr delivery_node = NULL;
8523 void *raw = NULL;
8524 size_t raw_length = 0;
8525 #endif
8527 if (!context) return IE_INVALID_CONTEXT;
8528 zfree(context->long_message);
8530 /* Free former message if any */
8531 if (!message) return IE_INVAL;
8532 isds_message_free(message);
8534 #if HAVE_LIBCURL
8535 /* Do request and check for success */
8536 err = build_send_check_message_request(context, SERVICE_DM_INFO,
8537 BAD_CAST "GetDeliveryInfo", message_id,
8538 &response, NULL, NULL, &code, &status_message);
8539 if (err) goto leave;
8542 /* Serialize delivery info */
8543 delivery_node = xmlDocGetRootElement(response);
8544 if (!delivery_node) {
8545 char *message_id_locale = _isds_utf82locale((char*) message_id);
8546 isds_printf_message(context,
8547 _("Server did not return any delivery info for ID `%s' "
8548 "on GetDeliveryInfo request"), message_id_locale);
8549 free(message_id_locale);
8550 err = IE_ISDS;
8551 goto leave;
8553 err = serialize_subtree(context, delivery_node, &raw, &raw_length);
8554 if (err) goto leave;
8556 /* Parse delivery info */
8557 /* TODO: Here we parse the response second time. We could single delivery
8558 * parser from isds_load_delivery_info() to make things faster. */
8559 err = isds_load_delivery_info(context,
8560 RAWTYPE_DELIVERYINFO, raw, raw_length,
8561 message, BUFFER_MOVE);
8562 if (err) goto leave;
8564 raw = NULL;
8567 leave:
8568 if (err) {
8569 isds_message_free(message);
8572 free(raw);
8573 free(code);
8574 free(status_message);
8575 xmlFreeDoc(response);
8577 if (!err)
8578 isds_log(ILF_ISDS, ILL_DEBUG,
8579 _("GetDeliveryInfo request processed by server "
8580 "successfully.\n")
8582 #else /* not HAVE_LIBCURL */
8583 err = IE_NOTSUP;
8584 #endif
8585 return err;
8589 /* Download incoming message identified by ID.
8590 * @context is session context
8591 * @message_id is message identifier (you can get them from
8592 * isds_get_list_of_received_messages())
8593 * @message is automatically reallocated message retrieved from ISDS */
8594 isds_error isds_get_received_message(struct isds_ctx *context,
8595 const char *message_id, struct isds_message **message) {
8597 isds_error err = IE_SUCCESS;
8598 #if HAVE_LIBCURL
8599 xmlDocPtr response = NULL;
8600 void *xml_stream = NULL;
8601 size_t xml_stream_length;
8602 xmlChar *code = NULL, *status_message = NULL;
8603 xmlXPathContextPtr xpath_ctx = NULL;
8604 xmlXPathObjectPtr result = NULL;
8605 char *phys_path = NULL;
8606 size_t phys_start, phys_end;
8607 #endif
8609 if (!context) return IE_INVALID_CONTEXT;
8610 zfree(context->long_message);
8612 /* Free former message if any */
8613 if (message) isds_message_free(message);
8615 #if HAVE_LIBCURL
8616 /* Do request and check for success */
8617 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
8618 BAD_CAST "MessageDownload", message_id,
8619 &response, &xml_stream, &xml_stream_length,
8620 &code, &status_message);
8621 if (err) goto leave;
8623 /* Extract data */
8624 xpath_ctx = xmlXPathNewContext(response);
8625 if (!xpath_ctx) {
8626 err = IE_ERROR;
8627 goto leave;
8629 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8630 err = IE_ERROR;
8631 goto leave;
8633 result = xmlXPathEvalExpression(
8634 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
8635 xpath_ctx);
8636 if (!result) {
8637 err = IE_ERROR;
8638 goto leave;
8640 /* Empty response */
8641 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8642 char *message_id_locale = _isds_utf82locale((char*) message_id);
8643 isds_printf_message(context,
8644 _("Server did not return any message for ID `%s' "
8645 "on MessageDownload request"), message_id_locale);
8646 free(message_id_locale);
8647 err = IE_ISDS;
8648 goto leave;
8650 /* More messages */
8651 if (result->nodesetval->nodeNr > 1) {
8652 char *message_id_locale = _isds_utf82locale((char*) message_id);
8653 isds_printf_message(context,
8654 _("Server did return more messages for ID `%s' "
8655 "on MessageDownload request"), message_id_locale);
8656 free(message_id_locale);
8657 err = IE_ISDS;
8658 goto leave;
8660 /* One message */
8661 xpath_ctx->node = result->nodesetval->nodeTab[0];
8663 /* Extract the message */
8664 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
8665 if (err) goto leave;
8667 /* Locate raw XML blob */
8668 phys_path = strdup(
8669 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
8670 PHYSXML_ELEMENT_SEPARATOR
8671 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
8672 PHYSXML_ELEMENT_SEPARATOR
8673 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
8675 if (!phys_path) {
8676 err = IE_NOMEM;
8677 goto leave;
8679 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
8680 phys_path, &phys_start, &phys_end);
8681 zfree(phys_path);
8682 if (err) {
8683 isds_log_message(context,
8684 _("Substring with isds:MessageDownloadResponse element "
8685 "could not be located in raw SOAP message"));
8686 goto leave;
8688 /* Save XML blob */
8689 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
8690 &(*message)->raw_length);*/
8691 /* TODO: Store name space declarations from ancestors */
8692 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
8693 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
8694 (*message)->raw_length = phys_end - phys_start + 1;
8695 (*message)->raw = malloc((*message)->raw_length);
8696 if (!(*message)->raw) {
8697 err = IE_NOMEM;
8698 goto leave;
8700 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
8703 leave:
8704 if (err) {
8705 isds_message_free(message);
8708 free(phys_path);
8710 xmlXPathFreeObject(result);
8711 xmlXPathFreeContext(xpath_ctx);
8713 free(code);
8714 free(status_message);
8715 free(xml_stream);
8716 if (!*message || !(*message)->xml) {
8717 xmlFreeDoc(response);
8720 if (!err)
8721 isds_log(ILF_ISDS, ILL_DEBUG,
8722 _("MessageDownload request processed by server "
8723 "successfully.\n")
8725 #else /* not HAVE_LIBCURL */
8726 err = IE_NOTSUP;
8727 #endif
8728 return err;
8732 /* Load message of any type from buffer.
8733 * @context is session context
8734 * @raw_type defines content type of @buffer. Only message types are allowed.
8735 * @buffer is message raw representation. Format (CMS, plain signed,
8736 * message direction) is defined in @raw_type. You can retrieve such data
8737 * from message->raw after calling isds_get_[signed]{received,sent}_message().
8738 * @length is length of buffer in bytes.
8739 * @message is automatically reallocated message parsed from @buffer.
8740 * @strategy selects how buffer will be attached into raw isds_message member.
8741 * */
8742 isds_error isds_load_message(struct isds_ctx *context,
8743 const isds_raw_type raw_type, const void *buffer, const size_t length,
8744 struct isds_message **message, const isds_buffer_strategy strategy) {
8746 isds_error err = IE_SUCCESS;
8747 void *xml_stream = NULL;
8748 size_t xml_stream_length = 0;
8749 message_ns_type message_ns;
8750 xmlDocPtr message_doc = NULL;
8751 xmlXPathContextPtr xpath_ctx = NULL;
8752 xmlXPathObjectPtr result = NULL;
8754 if (!context) return IE_INVALID_CONTEXT;
8755 zfree(context->long_message);
8756 if (!message) return IE_INVAL;
8757 isds_message_free(message);
8758 if (!buffer) return IE_INVAL;
8761 /* Select buffer format and extract XML from CMS*/
8762 switch (raw_type) {
8763 case RAWTYPE_INCOMING_MESSAGE:
8764 message_ns = MESSAGE_NS_UNSIGNED;
8765 xml_stream = (void *) buffer;
8766 xml_stream_length = length;
8767 break;
8769 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
8770 message_ns = MESSAGE_NS_SIGNED_INCOMING;
8771 xml_stream = (void *) buffer;
8772 xml_stream_length = length;
8773 break;
8775 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
8776 message_ns = MESSAGE_NS_SIGNED_INCOMING;
8777 err = _isds_extract_cms_data(context, buffer, length,
8778 &xml_stream, &xml_stream_length);
8779 if (err) goto leave;
8780 break;
8782 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
8783 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
8784 xml_stream = (void *) buffer;
8785 xml_stream_length = length;
8786 break;
8788 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
8789 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
8790 err = _isds_extract_cms_data(context, buffer, length,
8791 &xml_stream, &xml_stream_length);
8792 if (err) goto leave;
8793 break;
8795 default:
8796 isds_log_message(context, _("Bad raw message representation type"));
8797 return IE_INVAL;
8798 break;
8801 isds_log(ILF_ISDS, ILL_DEBUG,
8802 _("Loading message:\n%.*s\nEnd of message\n"),
8803 xml_stream_length, xml_stream);
8805 /* Convert messages XML stream into XPath context */
8806 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
8807 if (!message_doc) {
8808 err = IE_XML;
8809 goto leave;
8811 xpath_ctx = xmlXPathNewContext(message_doc);
8812 if (!xpath_ctx) {
8813 err = IE_ERROR;
8814 goto leave;
8816 /* XXX: Standard name space for unsigned incoming direction:
8817 * http://isds.czechpoint.cz/v20/
8819 * XXX: Name spaces mangled for signed outgoing direction:
8820 * http://isds.czechpoint.cz/v20/SentMessage:
8822 * <q:MessageDownloadResponse
8823 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
8824 * <q:dmReturnedMessage>
8825 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
8826 * <p:dmID>151916</p:dmID>
8827 * ...
8828 * </p:dmDm>
8829 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
8830 * ...
8831 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
8832 * </q:dmReturnedMessage>
8833 * </q:MessageDownloadResponse>
8835 * XXX: Name spaces mangled for signed incoming direction:
8836 * http://isds.czechpoint.cz/v20/message:
8838 * <q:MessageDownloadResponse
8839 * xmlns:q="http://isds.czechpoint.cz/v20/message">
8840 * <q:dmReturnedMessage>
8841 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
8842 * <p:dmID>151916</p:dmID>
8843 * ...
8844 * </p:dmDm>
8845 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
8846 * ...
8847 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
8848 * </q:dmReturnedMessage>
8849 * </q:MessageDownloadResponse>
8851 * Stupidity of ISDS developers is unlimited */
8852 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
8853 err = IE_ERROR;
8854 goto leave;
8856 result = xmlXPathEvalExpression(
8857 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
8858 xpath_ctx);
8859 if (!result) {
8860 err = IE_ERROR;
8861 goto leave;
8863 /* Empty message */
8864 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8865 isds_printf_message(context,
8866 _("XML document does not contain "
8867 "sisds:dmReturnedMessage element"));
8868 err = IE_ISDS;
8869 goto leave;
8871 /* More messages */
8872 if (result->nodesetval->nodeNr > 1) {
8873 isds_printf_message(context,
8874 _("XML document has more sisds:dmReturnedMessage elements"));
8875 err = IE_ISDS;
8876 goto leave;
8878 /* One message */
8879 xpath_ctx->node = result->nodesetval->nodeTab[0];
8881 /* Extract the message */
8882 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
8883 if (err) goto leave;
8885 /* Append raw buffer into message */
8886 (*message)->raw_type = raw_type;
8887 switch (strategy) {
8888 case BUFFER_DONT_STORE:
8889 break;
8890 case BUFFER_COPY:
8891 (*message)->raw = malloc(length);
8892 if (!(*message)->raw) {
8893 err = IE_NOMEM;
8894 goto leave;
8896 memcpy((*message)->raw, buffer, length);
8897 (*message)->raw_length = length;
8898 break;
8899 case BUFFER_MOVE:
8900 (*message)->raw = (void *) buffer;
8901 (*message)->raw_length = length;
8902 break;
8903 default:
8904 err = IE_ENUM;
8905 goto leave;
8909 leave:
8910 if (err) {
8911 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
8912 isds_message_free(message);
8915 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
8916 xmlXPathFreeObject(result);
8917 xmlXPathFreeContext(xpath_ctx);
8918 if (!*message || !(*message)->xml) {
8919 xmlFreeDoc(message_doc);
8922 if (!err)
8923 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
8924 return err;
8928 /* Determine type of raw message or delivery info according some heuristics.
8929 * It does not validate the raw blob.
8930 * @context is session context
8931 * @raw_type returns content type of @buffer. Valid only if exit code of this
8932 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
8933 * reallocated memory.
8934 * @buffer is message raw representation.
8935 * @length is length of buffer in bytes. */
8936 isds_error isds_guess_raw_type(struct isds_ctx *context,
8937 isds_raw_type *raw_type, const void *buffer, const size_t length) {
8938 isds_error err;
8939 void *xml_stream = NULL;
8940 size_t xml_stream_length = 0;
8941 xmlDocPtr document = NULL;
8942 xmlNodePtr root = NULL;
8944 if (!context) return IE_INVALID_CONTEXT;
8945 zfree(context->long_message);
8946 if (length == 0 || !buffer) return IE_INVAL;
8947 if (!raw_type) return IE_INVAL;
8949 /* Try CMS */
8950 err = _isds_extract_cms_data(context, buffer, length,
8951 &xml_stream, &xml_stream_length);
8952 if (err) {
8953 xml_stream = (void *) buffer;
8954 xml_stream_length = (size_t) length;
8955 err = IE_SUCCESS;
8958 /* Try XML */
8959 document = xmlParseMemory(xml_stream, xml_stream_length);
8960 if (!document) {
8961 isds_printf_message(context,
8962 _("Could not parse data as XML document"));
8963 err = IE_NOTSUP;
8964 goto leave;
8967 /* Get root element */
8968 root = xmlDocGetRootElement(document);
8969 if (!root) {
8970 isds_printf_message(context,
8971 _("XML document is missing root element"));
8972 err = IE_XML;
8973 goto leave;
8976 if (!root->ns || !root->ns->href) {
8977 isds_printf_message(context,
8978 _("Root element does not belong to any name space"));
8979 err = IE_NOTSUP;
8980 goto leave;
8983 /* Test name space */
8984 if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_INCOMING_NS)) {
8985 if (xml_stream == buffer)
8986 *raw_type = RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE;
8987 else
8988 *raw_type = RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
8989 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_OUTGOING_NS)) {
8990 if (xml_stream == buffer)
8991 *raw_type = RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE;
8992 else
8993 *raw_type = RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE;
8994 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_DELIVERY_NS)) {
8995 if (xml_stream == buffer)
8996 *raw_type = RAWTYPE_PLAIN_SIGNED_DELIVERYINFO;
8997 else
8998 *raw_type = RAWTYPE_CMS_SIGNED_DELIVERYINFO;
8999 } else if (!xmlStrcmp(root->ns->href, BAD_CAST ISDS_NS)) {
9000 if (xml_stream != buffer) {
9001 isds_printf_message(context,
9002 _("Document in ISDS name space is encapsulated into CMS" ));
9003 err = IE_NOTSUP;
9004 } else if (!xmlStrcmp(root->name, BAD_CAST "MessageDownloadResponse"))
9005 *raw_type = RAWTYPE_INCOMING_MESSAGE;
9006 else if (!xmlStrcmp(root->name, BAD_CAST "GetDeliveryInfoResponse"))
9007 *raw_type = RAWTYPE_DELIVERYINFO;
9008 else {
9009 isds_printf_message(context,
9010 _("Unknown root element in ISDS name space"));
9011 err = IE_NOTSUP;
9013 } else {
9014 isds_printf_message(context,
9015 _("Unknown name space"));
9016 err = IE_NOTSUP;
9019 leave:
9020 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
9021 xmlFreeDoc(document);
9022 return err;
9026 /* Download signed incoming/outgoing message identified by ID.
9027 * @context is session context
9028 * @output is true for outgoing message, false for incoming message
9029 * @message_id is message identifier (you can get them from
9030 * isds_get_list_of_{sent,received}_messages())
9031 * @message is automatically reallocated message retrieved from ISDS. The raw
9032 * member will be filled with PKCS#7 structure in DER format. */
9033 static isds_error isds_get_signed_message(struct isds_ctx *context,
9034 const _Bool outgoing, const char *message_id,
9035 struct isds_message **message) {
9037 isds_error err = IE_SUCCESS;
9038 #if HAVE_LIBCURL
9039 xmlDocPtr response = NULL;
9040 xmlChar *code = NULL, *status_message = NULL;
9041 xmlXPathContextPtr xpath_ctx = NULL;
9042 xmlXPathObjectPtr result = NULL;
9043 char *encoded_structure = NULL;
9044 void *raw = NULL;
9045 size_t raw_length = 0;
9046 #endif
9048 if (!context) return IE_INVALID_CONTEXT;
9049 zfree(context->long_message);
9050 if (!message) return IE_INVAL;
9051 isds_message_free(message);
9053 #if HAVE_LIBCURL
9054 /* Do request and check for success */
9055 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
9056 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
9057 BAD_CAST "SignedMessageDownload",
9058 message_id, &response, NULL, NULL, &code, &status_message);
9059 if (err) goto leave;
9061 /* Find signed message, extract it into raw and maybe free
9062 * response */
9063 err = find_extract_signed_data_free_response(context,
9064 (xmlChar *)message_id, &response,
9065 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
9066 BAD_CAST "SignedMessageDownload",
9067 &raw, &raw_length);
9068 if (err) goto leave;
9070 /* Parse message */
9071 err = isds_load_message(context,
9072 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
9073 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
9074 raw, raw_length, message, BUFFER_MOVE);
9075 if (err) goto leave;
9077 raw = NULL;
9079 leave:
9080 if (err) {
9081 isds_message_free(message);
9084 free(encoded_structure);
9085 xmlXPathFreeObject(result);
9086 xmlXPathFreeContext(xpath_ctx);
9087 free(raw);
9089 free(code);
9090 free(status_message);
9091 xmlFreeDoc(response);
9093 if (!err)
9094 isds_log(ILF_ISDS, ILL_DEBUG,
9095 (outgoing) ?
9096 _("SignedSentMessageDownload request processed by server "
9097 "successfully.\n") :
9098 _("SignedMessageDownload request processed by server "
9099 "successfully.\n")
9101 #else /* not HAVE_LIBCURL */
9102 err = IE_NOTSUP;
9103 #endif
9104 return err;
9108 /* Download signed incoming message identified by ID.
9109 * @context is session context
9110 * @message_id is message identifier (you can get them from
9111 * isds_get_list_of_received_messages())
9112 * @message is automatically reallocated message retrieved from ISDS. The raw
9113 * member will be filled with PKCS#7 structure in DER format. */
9114 isds_error isds_get_signed_received_message(struct isds_ctx *context,
9115 const char *message_id, struct isds_message **message) {
9116 return isds_get_signed_message(context, 0, message_id, message);
9120 /* Download signed outgoing message identified by ID.
9121 * @context is session context
9122 * @message_id is message identifier (you can get them from
9123 * isds_get_list_of_sent_messages())
9124 * @message is automatically reallocated message retrieved from ISDS. The raw
9125 * member will be filled with PKCS#7 structure in DER format. */
9126 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
9127 const char *message_id, struct isds_message **message) {
9128 return isds_get_signed_message(context, 1, message_id, message);
9132 /* Get type and name of user who sent a message identified by ID.
9133 * @context is session context
9134 * @message_id is message identifier
9135 * @sender_type is pointer to automatically allocated type of sender detected
9136 * from @raw_sender_type string. If @raw_sender_type is unknown to this
9137 * library or to the server, NULL will be returned. Pass NULL if you don't
9138 * care about it.
9139 * @raw_sender_type is automatically reallocated UTF-8 string describing
9140 * sender type or NULL if not known to server. Pass NULL if you don't care.
9141 * @sender_name is automatically reallocated UTF-8 name of user who sent the
9142 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
9143 isds_error isds_get_message_sender(struct isds_ctx *context,
9144 const char *message_id, isds_sender_type **sender_type,
9145 char **raw_sender_type, char **sender_name) {
9146 isds_error err = IE_SUCCESS;
9147 #if HAVE_LIBCURL
9148 xmlDocPtr response = NULL;
9149 xmlChar *code = NULL, *status_message = NULL;
9150 xmlXPathContextPtr xpath_ctx = NULL;
9151 xmlXPathObjectPtr result = NULL;
9152 char *type_string = NULL;
9153 #endif
9155 if (!context) return IE_INVALID_CONTEXT;
9156 zfree(context->long_message);
9157 if (sender_type) zfree(*sender_type);
9158 if (raw_sender_type) zfree(*raw_sender_type);
9159 if (sender_name) zfree(*sender_name);
9160 if (!message_id) return IE_INVAL;
9162 #if HAVE_LIBCURL
9163 /* Do request and check for success */
9164 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9165 BAD_CAST "GetMessageAuthor",
9166 message_id, &response, NULL, NULL, &code, &status_message);
9167 if (err) goto leave;
9169 /* Extract data */
9170 xpath_ctx = xmlXPathNewContext(response);
9171 if (!xpath_ctx) {
9172 err = IE_ERROR;
9173 goto leave;
9175 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9176 err = IE_ERROR;
9177 goto leave;
9179 result = xmlXPathEvalExpression(
9180 BAD_CAST "/isds:GetMessageAuthorResponse", xpath_ctx);
9181 if (!result) {
9182 err = IE_ERROR;
9183 goto leave;
9185 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9186 isds_log_message(context,
9187 _("Missing GetMessageAuthorResponse element"));
9188 err = IE_ISDS;
9189 goto leave;
9191 if (result->nodesetval->nodeNr > 1) {
9192 isds_log_message(context,
9193 _("Multiple GetMessageAuthorResponse element"));
9194 err = IE_ISDS;
9195 goto leave;
9197 xpath_ctx->node = result->nodesetval->nodeTab[0];
9198 xmlXPathFreeObject(result); result = NULL;
9200 /* Fill output arguments in */
9201 EXTRACT_STRING("isds:userType", type_string);
9202 if (type_string) {
9203 *sender_type = calloc(1, sizeof(**sender_type));
9204 if (!*sender_type) {
9205 err = IE_NOMEM;
9206 goto leave;
9209 if (sender_type) {
9210 err = string2isds_sender_type((xmlChar *)type_string,
9211 *sender_type);
9212 if (err) {
9213 zfree(*sender_type);
9214 if (err == IE_ENUM) {
9215 err = IE_SUCCESS;
9216 char *type_string_locale = _isds_utf82locale(type_string);
9217 isds_log(ILF_ISDS, ILL_WARNING,
9218 _("Unknown isds:userType value: %s"),
9219 type_string_locale);
9220 free(type_string_locale);
9225 if (sender_type)
9226 EXTRACT_STRING("isds:authorName", *sender_name);
9228 leave:
9229 if (err) {
9230 if (sender_type) zfree(*sender_type);
9231 zfree(type_string);
9232 if (sender_name) zfree(*sender_name);
9234 if (raw_sender_type) *raw_sender_type = type_string;
9236 xmlXPathFreeObject(result);
9237 xmlXPathFreeContext(xpath_ctx);
9239 free(code);
9240 free(status_message);
9241 xmlFreeDoc(response);
9243 if (!err)
9244 isds_log(ILF_ISDS, ILL_DEBUG,
9245 _("GetMessageAuthor request processed by server "
9246 "successfully.\n"));
9247 #else /* not HAVE_LIBCURL */
9248 err = IE_NOTSUP;
9249 #endif
9250 return err;
9254 /* Retrieve hash of message identified by ID stored in ISDS.
9255 * @context is session context
9256 * @message_id is message identifier
9257 * @hash is automatically reallocated message hash downloaded from ISDS.
9258 * Message must exist in system and must not be deleted. */
9259 isds_error isds_download_message_hash(struct isds_ctx *context,
9260 const char *message_id, struct isds_hash **hash) {
9262 isds_error err = IE_SUCCESS;
9263 #if HAVE_LIBCURL
9264 xmlDocPtr response = NULL;
9265 xmlChar *code = NULL, *status_message = NULL;
9266 xmlXPathContextPtr xpath_ctx = NULL;
9267 xmlXPathObjectPtr result = NULL;
9268 #endif
9270 if (!context) return IE_INVALID_CONTEXT;
9271 zfree(context->long_message);
9273 isds_hash_free(hash);
9275 #if HAVE_LIBCURL
9276 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9277 BAD_CAST "VerifyMessage", message_id,
9278 &response, NULL, NULL, &code, &status_message);
9279 if (err) goto leave;
9282 /* Extract data */
9283 xpath_ctx = xmlXPathNewContext(response);
9284 if (!xpath_ctx) {
9285 err = IE_ERROR;
9286 goto leave;
9288 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9289 err = IE_ERROR;
9290 goto leave;
9292 result = xmlXPathEvalExpression(
9293 BAD_CAST "/isds:VerifyMessageResponse",
9294 xpath_ctx);
9295 if (!result) {
9296 err = IE_ERROR;
9297 goto leave;
9299 /* Empty response */
9300 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9301 char *message_id_locale = _isds_utf82locale((char*) message_id);
9302 isds_printf_message(context,
9303 _("Server did not return any response for ID `%s' "
9304 "on VerifyMessage request"), message_id_locale);
9305 free(message_id_locale);
9306 err = IE_ISDS;
9307 goto leave;
9309 /* More responses */
9310 if (result->nodesetval->nodeNr > 1) {
9311 char *message_id_locale = _isds_utf82locale((char*) message_id);
9312 isds_printf_message(context,
9313 _("Server did return more responses for ID `%s' "
9314 "on VerifyMessage request"), message_id_locale);
9315 free(message_id_locale);
9316 err = IE_ISDS;
9317 goto leave;
9319 /* One response */
9320 xpath_ctx->node = result->nodesetval->nodeTab[0];
9322 /* Extract the hash */
9323 err = find_and_extract_DmHash(context, hash, xpath_ctx);
9325 leave:
9326 if (err) {
9327 isds_hash_free(hash);
9330 xmlXPathFreeObject(result);
9331 xmlXPathFreeContext(xpath_ctx);
9333 free(code);
9334 free(status_message);
9335 xmlFreeDoc(response);
9337 if (!err)
9338 isds_log(ILF_ISDS, ILL_DEBUG,
9339 _("VerifyMessage request processed by server "
9340 "successfully.\n")
9342 #else /* not HAVE_LIBCURL */
9343 err = IE_NOTSUP;
9344 #endif
9345 return err;
9349 /* Mark message as read. This is a transactional commit function to acknowledge
9350 * to ISDS the message has been downloaded and processed by client properly.
9351 * @context is session context
9352 * @message_id is message identifier. */
9353 isds_error isds_mark_message_read(struct isds_ctx *context,
9354 const char *message_id) {
9356 isds_error err = IE_SUCCESS;
9357 #if HAVE_LIBCURL
9358 xmlDocPtr response = NULL;
9359 xmlChar *code = NULL, *status_message = NULL;
9360 #endif
9362 if (!context) return IE_INVALID_CONTEXT;
9363 zfree(context->long_message);
9365 #if HAVE_LIBCURL
9366 /* Do request and check for success */
9367 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9368 BAD_CAST "MarkMessageAsDownloaded", message_id,
9369 &response, NULL, NULL, &code, &status_message);
9371 free(code);
9372 free(status_message);
9373 xmlFreeDoc(response);
9375 if (!err)
9376 isds_log(ILF_ISDS, ILL_DEBUG,
9377 _("MarkMessageAsDownloaded request processed by server "
9378 "successfully.\n")
9380 #else /* not HAVE_LIBCURL */
9381 err = IE_NOTSUP;
9382 #endif
9383 return err;
9387 /* Mark message as received by recipient. This is applicable only to
9388 * commercial message. Use envelope->dmType message member to distinguish
9389 * commercial message from government message. Government message is
9390 * received automatically (by law), commercial message on recipient request.
9391 * @context is session context
9392 * @message_id is message identifier. */
9393 isds_error isds_mark_message_received(struct isds_ctx *context,
9394 const char *message_id) {
9396 isds_error err = IE_SUCCESS;
9397 #if HAVE_LIBCURL
9398 xmlDocPtr response = NULL;
9399 xmlChar *code = NULL, *status_message = NULL;
9400 #endif
9402 if (!context) return IE_INVALID_CONTEXT;
9403 zfree(context->long_message);
9405 #if HAVE_LIBCURL
9406 /* Do request and check for success */
9407 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9408 BAD_CAST "ConfirmDelivery", message_id,
9409 &response, NULL, NULL, &code, &status_message);
9411 free(code);
9412 free(status_message);
9413 xmlFreeDoc(response);
9415 if (!err)
9416 isds_log(ILF_ISDS, ILL_DEBUG,
9417 _("ConfirmDelivery request processed by server "
9418 "successfully.\n")
9420 #else /* not HAVE_LIBCURL */
9421 err = IE_NOTSUP;
9422 #endif
9423 return err;
9427 /* Send document for authorized conversion into Czech POINT system.
9428 * This is public anonymous service, no log-in necessary. Special context is
9429 * used to reuse keep-a-live HTTPS connection.
9430 * @context is Czech POINT session context. DO NOT use context connected to
9431 * ISDS server. Use new context or context used by this function previously.
9432 * @document is document to convert. Only data, data_length, dmFileDescr and
9433 * is_xml members are significant. Be ware that not all document formats can be
9434 * converted (signed PDF 1.3 and higher only (2010-02 state)).
9435 * @id is reallocated identifier assigned by Czech POINT system to
9436 * your document on submit. Use is to tell it to Czech POINT officer.
9437 * @date is reallocated document submit date (submitted documents
9438 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
9439 * value. */
9440 isds_error czp_convert_document(struct isds_ctx *context,
9441 const struct isds_document *document,
9442 char **id, struct tm **date) {
9443 isds_error err = IE_SUCCESS;
9444 #if HAVE_LIBCURL
9445 xmlNsPtr deposit_ns = NULL, empty_ns = NULL;
9446 xmlNodePtr request = NULL, node;
9447 xmlDocPtr response = NULL;
9449 xmlXPathContextPtr xpath_ctx = NULL;
9450 xmlXPathObjectPtr result = NULL;
9451 long int status = -1;
9452 long int *status_ptr = &status;
9453 char *string = NULL;
9454 #endif
9457 if (!context) return IE_INVALID_CONTEXT;
9458 zfree(context->long_message);
9459 if (!document || !id || !date) return IE_INVAL;
9461 if (document->is_xml) {
9462 isds_log_message(context,
9463 _("XML documents cannot be submitted to conversion"));
9464 return IE_NOTSUP;
9467 /* Free output arguments */
9468 zfree(*id);
9469 zfree(*date);
9471 #if HAVE_LIBCURL
9472 /* Store configuration */
9473 context->type = CTX_TYPE_CZP;
9474 free(context->url);
9475 context->url = strdup("https://www.czechpoint.cz/uschovna/services.php");
9476 if (!(context->url))
9477 return IE_NOMEM;
9479 /* Prepare CURL handle if not yet connected */
9480 if (!context->curl) {
9481 context->curl = curl_easy_init();
9482 if (!(context->curl))
9483 return IE_ERROR;
9486 /* Build conversion request */
9487 request = xmlNewNode(NULL, BAD_CAST "saveDocument");
9488 if (!request) {
9489 isds_log_message(context,
9490 _("Could not build Czech POINT conversion request"));
9491 return IE_ERROR;
9493 deposit_ns = xmlNewNs(request, BAD_CAST DEPOSIT_NS, BAD_CAST "dep");
9494 if(!deposit_ns) {
9495 isds_log_message(context,
9496 _("Could not create Czech POINT deposit name space"));
9497 xmlFreeNode(request);
9498 return IE_ERROR;
9500 xmlSetNs(request, deposit_ns);
9502 /* Insert children. They are in empty namespace! */
9503 empty_ns = xmlNewNs(request, BAD_CAST "", NULL);
9504 if(!empty_ns) {
9505 isds_log_message(context, _("Could not create empty name space"));
9506 err = IE_ERROR;
9507 goto leave;
9509 INSERT_STRING_WITH_NS(request, empty_ns, "conversionID", "0");
9510 INSERT_STRING_WITH_NS(request, empty_ns, "fileName",
9511 document->dmFileDescr);
9513 /* Document encoded in Base64 */
9514 err = insert_base64_encoded_string(context, request, empty_ns, "document",
9515 document->data, document->data_length);
9516 if (err) goto leave;
9518 isds_log(ILF_ISDS, ILL_DEBUG,
9519 _("Submitting document for conversion into Czech POINT deposit"));
9521 /* Send conversion request */
9522 err = _czp_czpdeposit(context, request, &response);
9523 xmlFreeNode(request); request = NULL;
9525 if (err) {
9526 czp_do_close_connection(context);
9527 goto leave;
9531 /* Extract response */
9532 xpath_ctx = xmlXPathNewContext(response);
9533 if (!xpath_ctx) {
9534 err = IE_ERROR;
9535 goto leave;
9537 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9538 err = IE_ERROR;
9539 goto leave;
9541 result = xmlXPathEvalExpression(
9542 BAD_CAST "/deposit:saveDocumentResponse/return",
9543 xpath_ctx);
9544 if (!result) {
9545 err = IE_ERROR;
9546 goto leave;
9548 /* Empty response */
9549 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9550 isds_printf_message(context,
9551 _("Missing `return' element in Czech POINT deposit response"));
9552 err = IE_ISDS;
9553 goto leave;
9555 /* More responses */
9556 if (result->nodesetval->nodeNr > 1) {
9557 isds_printf_message(context,
9558 _("Multiple `return' element in Czech POINT deposit response"));
9559 err = IE_ISDS;
9560 goto leave;
9562 /* One response */
9563 xpath_ctx->node = result->nodesetval->nodeTab[0];
9565 /* Get status */
9566 EXTRACT_LONGINT("status", status_ptr, 1);
9567 if (status) {
9568 EXTRACT_STRING("statusMsg", string);
9569 char *string_locale = _isds_utf82locale(string);
9570 isds_printf_message(context,
9571 _("Czech POINT deposit refused document for conversion "
9572 "(code=%ld, message=%s)"),
9573 status, string_locale);
9574 free(string_locale);
9575 err = IE_ISDS;
9576 goto leave;
9579 /* Get document ID */
9580 EXTRACT_STRING("documentID", *id);
9582 /* Get submit date */
9583 EXTRACT_STRING("dateInserted", string);
9584 if (string) {
9585 *date = calloc(1, sizeof(**date));
9586 if (!*date) {
9587 err = IE_NOMEM;
9588 goto leave;
9590 err = datestring2tm((xmlChar *)string, *date);
9591 if (err) {
9592 if (err == IE_NOTSUP) {
9593 err = IE_ISDS;
9594 char *string_locale = _isds_utf82locale(string);
9595 isds_printf_message(context,
9596 _("Invalid dateInserted value: %s"), string_locale);
9597 free(string_locale);
9599 goto leave;
9603 leave:
9604 free(string);
9605 xmlXPathFreeObject(result);
9606 xmlXPathFreeContext(xpath_ctx);
9608 xmlFreeDoc(response);
9609 xmlFreeNode(request);
9611 if (!err) {
9612 char *id_locale = _isds_utf82locale((char *) *id);
9613 isds_log(ILF_ISDS, ILL_DEBUG,
9614 _("Document %s has been submitted for conversion "
9615 "to server successfully\n"), id_locale);
9616 free(id_locale);
9618 #else /* not HAVE_LIBCURL */
9619 err = IE_NOTSUP;
9620 #endif
9621 return err;
9625 /* Close possibly opened connection to Czech POINT document deposit.
9626 * @context is Czech POINT session context. */
9627 isds_error czp_close_connection(struct isds_ctx *context) {
9628 if (!context) return IE_INVALID_CONTEXT;
9629 zfree(context->long_message);
9630 #if HAVE_LIBCURL
9631 return czp_do_close_connection(context);
9632 #else
9633 return IE_NOTSUP;
9634 #endif
9638 /* Send request for new box creation in testing ISDS instance.
9639 * It's not possible to request for a production box currently, as it
9640 * communicates via e-mail.
9641 * XXX: This function does not work either. Server complains about invalid
9642 * e-mail address.
9643 * XXX: Remove context->type hacks in isds.c and validator.c when removing
9644 * this function
9645 * @context is special session context for box creation request. DO NOT use
9646 * standard context as it could reveal your password. Use fresh new context or
9647 * context previously used by this function.
9648 * @box is box description to create including single primary user (in case of
9649 * FO box type). It outputs box ID assigned by ISDS in dbID element.
9650 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
9651 * box, or contact address of PFO box owner). The email member is mandatory as
9652 * it will be used to deliver credentials.
9653 * @former_names is former name of box owner. Pass NULL if you don't care.
9654 * @approval is optional external approval of box manipulation
9655 * @refnumber is reallocated serial number of request assigned by ISDS. Use
9656 * NULL, if you don't care.*/
9657 isds_error isds_request_new_testing_box(struct isds_ctx *context,
9658 struct isds_DbOwnerInfo *box, const struct isds_list *users,
9659 const char *former_names, const struct isds_approval *approval,
9660 char **refnumber) {
9661 isds_error err = IE_SUCCESS;
9662 #if HAVE_LIBCURL
9663 xmlNodePtr request = NULL;
9664 xmlDocPtr response = NULL;
9665 xmlXPathContextPtr xpath_ctx = NULL;
9666 xmlXPathObjectPtr result = NULL;
9667 #endif
9670 if (!context) return IE_INVALID_CONTEXT;
9671 zfree(context->long_message);
9672 if (!box) return IE_INVAL;
9674 #if HAVE_LIBCURL
9675 if (!box->email || box->email[0] == '\0') {
9676 isds_log_message(context, _("E-mail field is mandatory"));
9677 return IE_INVAL;
9680 /* Scratch box ID */
9681 zfree(box->dbID);
9683 /* Store configuration */
9684 context->type = CTX_TYPE_TESTING_REQUEST_COLLECTOR;
9685 free(context->url);
9686 context->url = strdup("http://78.102.19.203/testbox/request_box.php");
9687 if (!(context->url))
9688 return IE_NOMEM;
9690 /* Prepare CURL handle if not yet connected */
9691 if (!context->curl) {
9692 context->curl = curl_easy_init();
9693 if (!(context->curl))
9694 return IE_ERROR;
9697 /* Build CreateDataBox request */
9698 err = build_CreateDBInput_request(context,
9699 &request, BAD_CAST "CreateDataBox",
9700 box, users, (xmlChar *) former_names, NULL, NULL, NULL, approval);
9701 if (err) goto leave;
9703 /* Send it to server and process response */
9704 err = send_destroy_request_check_response(context,
9705 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
9706 &response, (xmlChar **) refnumber);
9707 if (err) goto leave;
9709 /* Extract box ID */
9710 xpath_ctx = xmlXPathNewContext(response);
9711 if (!xpath_ctx) {
9712 err = IE_ERROR;
9713 goto leave;
9715 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9716 err = IE_ERROR;
9717 goto leave;
9719 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
9721 leave:
9722 xmlXPathFreeObject(result);
9723 xmlXPathFreeContext(xpath_ctx);
9724 xmlFreeDoc(response);
9725 xmlFreeNode(request);
9727 if (!err) {
9728 isds_log(ILF_ISDS, ILL_DEBUG,
9729 _("CreateDataBox request processed by server successfully.\n"));
9731 #else /* not HAVE_LIBCURL */
9732 err = IE_NOTSUP;
9733 #endif
9735 return err;
9739 /* Submit CMS signed message to ISDS to verify its originality. This is
9740 * stronger form of isds_verify_message_hash() because ISDS does more checks
9741 * than simple one (potentialy old weak) hash comparison.
9742 * @context is session context
9743 * @message is memory with raw CMS signed message bit stream
9744 * @length is @message size in bytes
9745 * @return
9746 * IE_SUCCESS if message originates in ISDS
9747 * IE_NOTEQUAL if message is unknown to ISDS
9748 * other code for other errors */
9749 isds_error isds_authenticate_message(struct isds_ctx *context,
9750 const void *message, size_t length) {
9751 isds_error err = IE_SUCCESS;
9752 #if HAVE_LIBCURL
9753 xmlNsPtr isds_ns = NULL;
9754 xmlNodePtr request = NULL;
9755 xmlDocPtr response = NULL;
9756 xmlXPathContextPtr xpath_ctx = NULL;
9757 xmlXPathObjectPtr result = NULL;
9758 _Bool *authentic = NULL;
9759 #endif
9761 if (!context) return IE_INVALID_CONTEXT;
9762 zfree(context->long_message);
9763 if (!message || length == 0) return IE_INVAL;
9765 #if HAVE_LIBCURL
9766 /* Check if connection is established
9767 * TODO: This check should be done downstairs. */
9768 if (!context->curl) return IE_CONNECTION_CLOSED;
9771 /* Build AuthenticateMessage request */
9772 request = xmlNewNode(NULL, BAD_CAST "AuthenticateMessage");
9773 if (!request) {
9774 isds_log_message(context,
9775 _("Could not build AuthenticateMessage request"));
9776 return IE_ERROR;
9778 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9779 if(!isds_ns) {
9780 isds_log_message(context, _("Could not create ISDS name space"));
9781 xmlFreeNode(request);
9782 return IE_ERROR;
9784 xmlSetNs(request, isds_ns);
9786 /* Insert Base64 encoded message */
9787 err = insert_base64_encoded_string(context, request, NULL, "dmMessage",
9788 message, length);
9789 if (err) goto leave;
9791 /* Send request to server and process response */
9792 err = send_destroy_request_check_response(context,
9793 SERVICE_DM_OPERATIONS, BAD_CAST "AuthenticateMessage", &request,
9794 &response, NULL);
9795 if (err) goto leave;
9798 /* ISDS has decided */
9799 xpath_ctx = xmlXPathNewContext(response);
9800 if (!xpath_ctx) {
9801 err = IE_ERROR;
9802 goto leave;
9804 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9805 err = IE_ERROR;
9806 goto leave;
9809 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic);
9811 if (!authentic) {
9812 isds_log_message(context,
9813 _("Server did not return any response on "
9814 "AuthenticateMessage request"));
9815 err = IE_ISDS;
9816 goto leave;
9818 if (*authentic) {
9819 isds_log(ILF_ISDS, ILL_DEBUG,
9820 _("ISDS authenticated the message successfully\n"));
9821 } else {
9822 isds_log_message(context, _("ISDS does not know the message"));
9823 err = IE_NOTEQUAL;
9827 leave:
9828 free(authentic);
9829 xmlXPathFreeObject(result);
9830 xmlXPathFreeContext(xpath_ctx);
9832 xmlFreeDoc(response);
9833 xmlFreeNode(request);
9834 #else /* not HAVE_LIBCURL */
9835 err = IE_NOTSUP;
9836 #endif
9838 return err;
9841 #undef INSERT_ELEMENT
9842 #undef CHECK_FOR_STRING_LENGTH
9843 #undef INSERT_STRING_ATTRIBUTE
9844 #undef INSERT_ULONGINTNOPTR
9845 #undef INSERT_ULONGINT
9846 #undef INSERT_LONGINT
9847 #undef INSERT_BOOLEAN
9848 #undef INSERT_SCALAR_BOOLEAN
9849 #undef INSERT_STRING
9850 #undef INSERT_STRING_WITH_NS
9851 #undef EXTRACT_STRING_ATTRIBUTE
9852 #undef EXTRACT_ULONGINT
9853 #undef EXTRACT_LONGINT
9854 #undef EXTRACT_BOOLEAN
9855 #undef EXTRACT_STRING
9858 /* Compute hash of message from raw representation and store it into envelope.
9859 * Original hash structure will be destroyed in envelope.
9860 * @context is session context
9861 * @message is message carrying raw XML message blob
9862 * @algorithm is desired hash algorithm to use */
9863 isds_error isds_compute_message_hash(struct isds_ctx *context,
9864 struct isds_message *message, const isds_hash_algorithm algorithm) {
9865 isds_error err = IE_SUCCESS;
9866 const char *nsuri;
9867 void *xml_stream = NULL;
9868 size_t xml_stream_length;
9869 size_t phys_start, phys_end;
9870 char *phys_path = NULL;
9871 struct isds_hash *new_hash = NULL;
9874 if (!context) return IE_INVALID_CONTEXT;
9875 zfree(context->long_message);
9876 if (!message) return IE_INVAL;
9878 if (!message->raw) {
9879 isds_log_message(context,
9880 _("Message does not carry raw representation"));
9881 return IE_INVAL;
9884 switch (message->raw_type) {
9885 case RAWTYPE_INCOMING_MESSAGE:
9886 nsuri = ISDS_NS;
9887 xml_stream = message->raw;
9888 xml_stream_length = message->raw_length;
9889 break;
9891 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
9892 nsuri = SISDS_INCOMING_NS;
9893 xml_stream = message->raw;
9894 xml_stream_length = message->raw_length;
9895 break;
9897 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
9898 nsuri = SISDS_INCOMING_NS;
9899 err = _isds_extract_cms_data(context,
9900 message->raw, message->raw_length,
9901 &xml_stream, &xml_stream_length);
9902 if (err) goto leave;
9903 break;
9905 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
9906 nsuri = SISDS_OUTGOING_NS;
9907 xml_stream = message->raw;
9908 xml_stream_length = message->raw_length;
9909 break;
9911 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
9912 nsuri = SISDS_OUTGOING_NS;
9913 err = _isds_extract_cms_data(context,
9914 message->raw, message->raw_length,
9915 &xml_stream, &xml_stream_length);
9916 if (err) goto leave;
9917 break;
9919 default:
9920 isds_log_message(context, _("Bad raw representation type"));
9921 return IE_INVAL;
9922 break;
9926 /* XXX: Hash is computed from original string representing isds:dmDm
9927 * subtree. That means no encoding, white space, xmlns attributes changes.
9928 * In other words, input for hash can be invalid XML stream. */
9929 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
9930 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
9931 PHYSXML_ELEMENT_SEPARATOR,
9932 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
9933 PHYSXML_ELEMENT_SEPARATOR
9934 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
9935 err = IE_NOMEM;
9936 goto leave;
9938 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
9939 phys_path, &phys_start, &phys_end);
9940 zfree(phys_path);
9941 if (err) {
9942 isds_log_message(context,
9943 _("Substring with isds:dmDM element could not be located "
9944 "in raw message"));
9945 goto leave;
9949 /* Compute hash */
9950 new_hash = calloc(1, sizeof(*new_hash));
9951 if (!new_hash) {
9952 err = IE_NOMEM;
9953 goto leave;
9955 new_hash->algorithm = algorithm;
9956 err = _isds_compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
9957 new_hash);
9958 if (err) {
9959 isds_log_message(context, _("Could not compute message hash"));
9960 goto leave;
9963 /* Save computed hash */
9964 if (!message->envelope) {
9965 message->envelope = calloc(1, sizeof(*message->envelope));
9966 if (!message->envelope) {
9967 err = IE_NOMEM;
9968 goto leave;
9971 isds_hash_free(&message->envelope->hash);
9972 message->envelope->hash = new_hash;
9974 leave:
9975 if (err) {
9976 isds_hash_free(&new_hash);
9979 free(phys_path);
9980 if (xml_stream != message->raw) free(xml_stream);
9981 return err;
9985 /* Compare two hashes.
9986 * @h1 is first hash
9987 * @h2 is another hash
9988 * @return
9989 * IE_SUCCESS if hashes equal
9990 * IE_NOTUNIQ if hashes are comparable, but they don't equal
9991 * IE_ENUM if not comparable, but both structures defined
9992 * IE_INVAL if some of the structures are undefined (NULL)
9993 * IE_ERROR if internal error occurs */
9994 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
9995 if (h1 == NULL || h2 == NULL) return IE_INVAL;
9996 if (h1->algorithm != h2->algorithm) return IE_ENUM;
9997 if (h1->length != h2->length) return IE_ERROR;
9998 if (h1->length > 0 && !h1->value) return IE_ERROR;
9999 if (h2->length > 0 && !h2->value) return IE_ERROR;
10001 for (int i = 0; i < h1->length; i++) {
10002 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
10003 return IE_NOTEQUAL;
10005 return IE_SUCCESS;
10009 /* Check message has gone through ISDS by comparing message hash stored in
10010 * ISDS and locally computed hash. You must provide message with valid raw
10011 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
10012 * This is convenient wrapper for isds_download_message_hash(),
10013 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
10014 * @context is session context
10015 * @message is message with valid raw and envelope member; envelope->hash
10016 * member will be changed during function run. Use envelope on heap only.
10017 * @return
10018 * IE_SUCCESS if message originates in ISDS
10019 * IE_NOTEQUAL if message is unknown to ISDS
10020 * other code for other errors */
10021 isds_error isds_verify_message_hash(struct isds_ctx *context,
10022 struct isds_message *message) {
10023 isds_error err = IE_SUCCESS;
10024 struct isds_hash *downloaded_hash = NULL;
10026 if (!context) return IE_INVALID_CONTEXT;
10027 zfree(context->long_message);
10028 if (!message) return IE_INVAL;
10030 if (!message->envelope) {
10031 isds_log_message(context,
10032 _("Given message structure is missing envelope"));
10033 return IE_INVAL;
10035 if (!message->raw) {
10036 isds_log_message(context,
10037 _("Given message structure is missing raw representation"));
10038 return IE_INVAL;
10041 err = isds_download_message_hash(context, message->envelope->dmID,
10042 &downloaded_hash);
10043 if (err) goto leave;
10045 err = isds_compute_message_hash(context, message,
10046 downloaded_hash->algorithm);
10047 if (err) goto leave;
10049 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
10051 leave:
10052 isds_hash_free(&downloaded_hash);
10053 return err;
10057 /* Search for document by document ID in list of documents. IDs are compared
10058 * as UTF-8 string.
10059 * @documents is list of isds_documents
10060 * @id is document identifier
10061 * @return first matching document or NULL. */
10062 const struct isds_document *isds_find_document_by_id(
10063 const struct isds_list *documents, const char *id) {
10064 const struct isds_list *item;
10065 const struct isds_document *document;
10067 for (item = documents; item; item = item->next) {
10068 document = (struct isds_document *) item->data;
10069 if (!document) continue;
10071 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
10072 return document;
10075 return NULL;
10079 /* Normalize @mime_type to be proper MIME type.
10080 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
10081 * guess regular MIME type (e.g. "application/pdf").
10082 * @mime_type is UTF-8 encoded MIME type to fix
10083 * @return original @mime_type if no better interpretation exists, or array to
10084 * constant static UTF-8 encoded string with proper MIME type. */
10085 char *isds_normalize_mime_type(const char* mime_type) {
10086 if (!mime_type) return NULL;
10088 for (int offset = 0;
10089 offset < sizeof(extension_map_mime)/sizeof(extension_map_mime[0]);
10090 offset += 2) {
10091 if (!xmlStrcmp((const xmlChar*) mime_type, extension_map_mime[offset]))
10092 return (char *) extension_map_mime[offset + 1];
10095 return (char *) mime_type;
10099 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
10100 struct isds_message **message);
10101 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
10102 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
10103 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
10104 struct isds_address **address);
10106 int isds_message_free(struct isds_message **message);
10107 int isds_address_free(struct isds_address **address);
10111 /* Makes known all relevant namespaces to given XPath context
10112 * @xpath_ctx is XPath context
10113 * @message_ns selects proper message name space. Unsigned and signed
10114 * messages and delivery info's differ in prefix and URI. */
10115 _hidden isds_error _isds_register_namespaces(xmlXPathContextPtr xpath_ctx,
10116 const message_ns_type message_ns) {
10117 const xmlChar *message_namespace = NULL;
10119 if (!xpath_ctx) return IE_ERROR;
10121 switch(message_ns) {
10122 case MESSAGE_NS_1:
10123 message_namespace = BAD_CAST ISDS1_NS; break;
10124 case MESSAGE_NS_UNSIGNED:
10125 message_namespace = BAD_CAST ISDS_NS; break;
10126 case MESSAGE_NS_SIGNED_INCOMING:
10127 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
10128 case MESSAGE_NS_SIGNED_OUTGOING:
10129 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
10130 case MESSAGE_NS_SIGNED_DELIVERY:
10131 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
10132 default:
10133 return IE_ENUM;
10136 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
10137 return IE_ERROR;
10138 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
10139 return IE_ERROR;
10140 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
10141 return IE_ERROR;
10142 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
10143 return IE_ERROR;
10144 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
10145 return IE_ERROR;
10146 return IE_SUCCESS;