Recognize ChangeISDSPassword errors defined on 2011-10-16
[libisds.git] / src / isds.c
bloba4008a106329bfbb0f14e527f0775ed211654654
1 #include "isds_priv.h"
2 #include <stdlib.h>
3 #include <string.h>
4 #include <stdio.h>
5 #include <stdarg.h>
6 #include <ctype.h>
7 #include <stdint.h>
8 #include <limits.h> /* Because of LONG_{MIN,MAX} constants */
9 #include "utils.h"
10 #if HAVE_LIBCURL
11 #include "soap.h"
12 #endif
13 #include "validator.h"
14 #include "crypto.h"
15 #include <gpg-error.h> /* Because of ksba or gpgme */
16 #include "physxml.h"
18 /* Locators */
19 /* Base URL of production ISDS instance */
20 const char isds_locator[] = "https://ws1.mojedatovaschranka.cz/";
21 const char isds_cert_locator[] = "https://ws1c.mojedatovaschranka.cz/";
22 const char isds_otp_locator[] = "https://www.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/";
27 const char isds_otp_testing_locator[] = "https://www.czebox.cz/";
29 /* Extension to MIME type map */
30 static xmlChar *extension_map_mime[] = {
31 BAD_CAST "cer", BAD_CAST "application/x-x509-ca-cert",
32 BAD_CAST "crt", BAD_CAST "application/x-x509-ca-cert",
33 BAD_CAST "der", BAD_CAST "application/x-x509-ca-cert",
34 BAD_CAST "doc", BAD_CAST "application/msword",
35 BAD_CAST "docx", BAD_CAST "application/vnd.openxmlformats-officedocument."
36 "wordprocessingml.document",
37 BAD_CAST "dbf", BAD_CAST "application/octet-stream",
38 BAD_CAST "prj", BAD_CAST "application/octet-stream",
39 BAD_CAST "qix", BAD_CAST "application/octet-stream",
40 BAD_CAST "sbn", BAD_CAST "application/octet-stream",
41 BAD_CAST "sbx", BAD_CAST "application/octet-stream",
42 BAD_CAST "shp", BAD_CAST "application/octet-stream",
43 BAD_CAST "shx", BAD_CAST "application/octet-stream",
44 BAD_CAST "dgn", BAD_CAST "application/octet-stream",
45 BAD_CAST "dwg", BAD_CAST "image/vnd.dwg",
46 BAD_CAST "edi", BAD_CAST "application/edifact",
47 BAD_CAST "fo", BAD_CAST "application/vnd.software602.filler.form+xml",
48 BAD_CAST "gfs", BAD_CAST "application/xml",
49 BAD_CAST "gml", BAD_CAST "application/xml",
50 BAD_CAST "gif", BAD_CAST "image/gif",
51 BAD_CAST "htm", BAD_CAST "text/html",
52 BAD_CAST "html", BAD_CAST "text/html",
53 BAD_CAST "isdoc", BAD_CAST "text/isdoc",
54 BAD_CAST "isdocx", BAD_CAST "text/isdocx",
55 BAD_CAST "jfif", BAD_CAST "image/jpeg",
56 BAD_CAST "jpg", BAD_CAST "image/jpeg",
57 BAD_CAST "jpeg", BAD_CAST "image/jpeg",
58 BAD_CAST "mpeg", BAD_CAST "video/mpeg",
59 BAD_CAST "mpeg1", BAD_CAST "video/mpeg",
60 BAD_CAST "mpeg2", BAD_CAST "video/mpeg",
61 BAD_CAST "mpg", BAD_CAST "video/mpeg",
62 BAD_CAST "mp2", BAD_CAST "audio/mpeg",
63 BAD_CAST "mp3", BAD_CAST "audio/mpeg",
64 BAD_CAST "odp", BAD_CAST "application/vnd.oasis.opendocument.presentation",
65 BAD_CAST "ods", BAD_CAST "application/vnd.oasis.opendocument.spreadsheet",
66 BAD_CAST "odt", BAD_CAST "application/vnd.oasis.opendocument.text",
67 BAD_CAST "pdf", BAD_CAST "application/pdf",
68 BAD_CAST "p7b", BAD_CAST "application/pkcs7-certificates",
69 BAD_CAST "p7c", BAD_CAST "application/pkcs7-mime",
70 BAD_CAST "p7m", BAD_CAST "application/pkcs7-mime",
71 BAD_CAST "p7f", BAD_CAST "application/pkcs7-signature",
72 BAD_CAST "p7s", BAD_CAST "application/pkcs7-signature",
73 BAD_CAST "pk7", BAD_CAST "application/pkcs7-mime",
74 BAD_CAST "png", BAD_CAST "image/png",
75 BAD_CAST "ppt", BAD_CAST "application/vnd.ms-powerpoint",
76 BAD_CAST "pptx", BAD_CAST "application/vnd.openxmlformats-officedocument."
77 "presentationml.presentation",
78 BAD_CAST "rtf", BAD_CAST "application/rtf",
79 BAD_CAST "tif", BAD_CAST "image/tiff",
80 BAD_CAST "tiff", BAD_CAST "image/tiff",
81 BAD_CAST "tsr", BAD_CAST "application/timestamp-reply",
82 BAD_CAST "tst", BAD_CAST "application/timestamp-reply",
83 BAD_CAST "txt", BAD_CAST "text/plain",
84 BAD_CAST "wav", BAD_CAST "audio/wav",
85 BAD_CAST "xls", BAD_CAST "application/vnd.ms-excel",
86 BAD_CAST "xlsx", BAD_CAST "application/vnd.openxmlformats-officedocument."
87 "spreadsheetml.sheet",
88 BAD_CAST "xml", BAD_CAST "application/xml",
89 BAD_CAST "xsd", BAD_CAST "application/xml",
90 BAD_CAST "zfo", BAD_CAST "application/vnd.software602.filler.form-xml-zip"
93 /* Deallocate structure isds_pki_credentials and NULL it.
94 * Pass-phrase is discarded.
95 * @pki credentials to to free */
96 void isds_pki_credentials_free(struct isds_pki_credentials **pki) {
97 if(!pki || !*pki) return;
99 free((*pki)->engine);
100 free((*pki)->certificate);
101 free((*pki)->key);
103 if ((*pki)->passphrase) {
104 memset((*pki)->passphrase, 0, strlen((*pki)->passphrase));
105 free((*pki)->passphrase);
108 zfree((*pki));
112 /* Free isds_list with all member data.
113 * @list list to free, on return will be NULL */
114 void isds_list_free(struct isds_list **list) {
115 struct isds_list *item, *next_item;
117 if (!list || !*list) return;
119 for(item = *list; item; item = next_item) {
120 if (item->destructor) (item->destructor)(&(item->data));
121 next_item = item->next;
122 free(item);
125 *list = NULL;
129 /* Deallocate structure isds_hash and NULL it.
130 * @hash hash to to free */
131 void isds_hash_free(struct isds_hash **hash) {
132 if(!hash || !*hash) return;
133 free((*hash)->value);
134 zfree((*hash));
138 /* Deallocate structure isds_PersonName recursively and NULL it */
139 static void isds_PersonName_free(struct isds_PersonName **person_name) {
140 if (!person_name || !*person_name) return;
142 free((*person_name)->pnFirstName);
143 free((*person_name)->pnMiddleName);
144 free((*person_name)->pnLastName);
145 free((*person_name)->pnLastNameAtBirth);
147 free(*person_name);
148 *person_name = NULL;
152 /* Deallocate structure isds_BirthInfo recursively and NULL it */
153 static void isds_BirthInfo_free(struct isds_BirthInfo **birth_info) {
154 if (!birth_info || !*birth_info) return;
156 free((*birth_info)->biDate);
157 free((*birth_info)->biCity);
158 free((*birth_info)->biCounty);
159 free((*birth_info)->biState);
161 free(*birth_info);
162 *birth_info = NULL;
166 /* Deallocate structure isds_Address recursively and NULL it */
167 static void isds_Address_free(struct isds_Address **address) {
168 if (!address || !*address) return;
170 free((*address)->adCity);
171 free((*address)->adStreet);
172 free((*address)->adNumberInStreet);
173 free((*address)->adNumberInMunicipality);
174 free((*address)->adZipCode);
175 free((*address)->adState);
177 free(*address);
178 *address = NULL;
182 /* Deallocate structure isds_DbOwnerInfo recursively and NULL it */
183 void isds_DbOwnerInfo_free(struct isds_DbOwnerInfo **db_owner_info) {
184 if (!db_owner_info || !*db_owner_info) return;
186 free((*db_owner_info)->dbID);
187 free((*db_owner_info)->dbType);
188 free((*db_owner_info)->ic);
189 isds_PersonName_free(&((*db_owner_info)->personName));
190 free((*db_owner_info)->firmName);
191 isds_BirthInfo_free(&((*db_owner_info)->birthInfo));
192 isds_Address_free(&((*db_owner_info)->address));
193 free((*db_owner_info)->nationality);
194 free((*db_owner_info)->email);
195 free((*db_owner_info)->telNumber);
196 free((*db_owner_info)->identifier);
197 free((*db_owner_info)->registryCode);
198 free((*db_owner_info)->dbState);
199 free((*db_owner_info)->dbEffectiveOVM);
201 free(*db_owner_info);
202 *db_owner_info = NULL;
205 /* Deallocate structure isds_DbUserInfo recursively and NULL it */
206 void isds_DbUserInfo_free(struct isds_DbUserInfo **db_user_info) {
207 if (!db_user_info || !*db_user_info) return;
209 free((*db_user_info)->userID);
210 free((*db_user_info)->userType);
211 free((*db_user_info)->userPrivils);
212 isds_PersonName_free(&((*db_user_info)->personName));
213 isds_Address_free(&((*db_user_info)->address));
214 free((*db_user_info)->biDate);
215 free((*db_user_info)->ic);
216 free((*db_user_info)->firmName);
217 free((*db_user_info)->caStreet);
218 free((*db_user_info)->caCity);
219 free((*db_user_info)->caZipCode);
220 free((*db_user_info)->caState);
222 zfree(*db_user_info);
226 /* Deallocate struct isds_event recursively and NULL it */
227 void isds_event_free(struct isds_event **event) {
228 if (!event || !*event) return;
230 free((*event)->time);
231 free((*event)->type);
232 free((*event)->description);
233 zfree(*event);
237 /* Deallocate struct isds_envelope recursively and NULL it */
238 void isds_envelope_free(struct isds_envelope **envelope) {
239 if (!envelope || !*envelope) return;
241 free((*envelope)->dmID);
242 free((*envelope)->dbIDSender);
243 free((*envelope)->dmSender);
244 free((*envelope)->dmSenderAddress);
245 free((*envelope)->dmSenderType);
246 free((*envelope)->dmRecipient);
247 free((*envelope)->dmRecipientAddress);
248 free((*envelope)->dmAmbiguousRecipient);
249 free((*envelope)->dmType);
251 free((*envelope)->dmOrdinal);
252 free((*envelope)->dmMessageStatus);
253 free((*envelope)->dmDeliveryTime);
254 free((*envelope)->dmAcceptanceTime);
255 isds_hash_free(&(*envelope)->hash);
256 free((*envelope)->timestamp);
257 isds_list_free(&(*envelope)->events);
259 free((*envelope)->dmSenderOrgUnit);
260 free((*envelope)->dmSenderOrgUnitNum);
261 free((*envelope)->dbIDRecipient);
262 free((*envelope)->dmRecipientOrgUnit);
263 free((*envelope)->dmRecipientOrgUnitNum);
264 free((*envelope)->dmToHands);
265 free((*envelope)->dmAnnotation);
266 free((*envelope)->dmRecipientRefNumber);
267 free((*envelope)->dmSenderRefNumber);
268 free((*envelope)->dmRecipientIdent);
269 free((*envelope)->dmSenderIdent);
271 free((*envelope)->dmLegalTitleLaw);
272 free((*envelope)->dmLegalTitleYear);
273 free((*envelope)->dmLegalTitleSect);
274 free((*envelope)->dmLegalTitlePar);
275 free((*envelope)->dmLegalTitlePoint);
277 free((*envelope)->dmPersonalDelivery);
278 free((*envelope)->dmAllowSubstDelivery);
280 free((*envelope)->dmOVM);
281 free((*envelope)->dmPublishOwnID);
283 free(*envelope);
284 *envelope = NULL;
288 /* Deallocate struct isds_message recursively and NULL it */
289 void isds_message_free(struct isds_message **message) {
290 if (!message || !*message) return;
292 free((*message)->raw);
293 isds_envelope_free(&((*message)->envelope));
294 isds_list_free(&((*message)->documents));
295 xmlFreeDoc((*message)->xml); (*message)->xml = NULL;
297 free(*message);
298 *message = NULL;
302 /* Deallocate struct isds_document recursively and NULL it */
303 void isds_document_free(struct isds_document **document) {
304 if (!document || !*document) return;
306 if (!(*document)->is_xml) {
307 free((*document)->data);
309 free((*document)->dmMimeType);
310 free((*document)->dmFileGuid);
311 free((*document)->dmUpFileGuid);
312 free((*document)->dmFileDescr);
313 free((*document)->dmFormat);
315 free(*document);
316 *document = NULL;
320 /* Deallocate struct isds_message_copy recursively and NULL it */
321 void isds_message_copy_free(struct isds_message_copy **copy) {
322 if (!copy || !*copy) return;
324 free((*copy)->dbIDRecipient);
325 free((*copy)->dmRecipientOrgUnit);
326 free((*copy)->dmRecipientOrgUnitNum);
327 free((*copy)->dmToHands);
329 free((*copy)->dmStatus);
330 free((*copy)->dmID);
332 zfree(*copy);
336 /* Deallocate struct isds_message_status_change recursively and NULL it */
337 void isds_message_status_change_free(
338 struct isds_message_status_change **message_status_change) {
339 if (!message_status_change || !*message_status_change) return;
341 free((*message_status_change)->dmID);
342 free((*message_status_change)->time);
343 free((*message_status_change)->dmMessageStatus);
345 zfree(*message_status_change);
349 /* Deallocate struct isds_approval recursively and NULL it */
350 void isds_approval_free(struct isds_approval **approval) {
351 if (!approval || !*approval) return;
353 free((*approval)->refference);
355 zfree(*approval);
359 /* Deallocate struct isds_credentials_delivery recursively and NULL it.
360 * The email string is deallocated too. */
361 void isds_credentials_delivery_free(
362 struct isds_credentials_delivery **credentials_delivery) {
363 if (!credentials_delivery || !*credentials_delivery) return;
365 free((*credentials_delivery)->email);
366 free((*credentials_delivery)->token);
367 free((*credentials_delivery)->new_user_name);
369 zfree(*credentials_delivery);
373 /* *DUP_OR_ERROR macros needs error label */
374 #define STRDUP_OR_ERROR(new, template) { \
375 if (!template) { \
376 (new) = NULL; \
377 } else { \
378 (new) = strdup(template); \
379 if (!new) goto error; \
383 #define FLATDUP_OR_ERROR(new, template) { \
384 if (!template) { \
385 (new) = NULL; \
386 } else { \
387 (new) = malloc(sizeof(*(new))); \
388 if (!new) goto error; \
389 memcpy((new), (template), sizeof(*(template))); \
393 /* Copy structure isds_pki_credentials recursively. */
394 struct isds_pki_credentials *isds_pki_credentials_duplicate(
395 const struct isds_pki_credentials *template) {
396 struct isds_pki_credentials *new = NULL;
398 if(!template) return NULL;
400 new = calloc(1, sizeof(*new));
401 if (!new) return NULL;
403 STRDUP_OR_ERROR(new->engine, template->engine);
404 new->certificate_format = template->certificate_format;
405 STRDUP_OR_ERROR(new->certificate, template->certificate);
406 new->key_format = template->key_format;
407 STRDUP_OR_ERROR(new->key, template->key);
408 STRDUP_OR_ERROR(new->passphrase, template->passphrase);
410 return new;
412 error:
413 isds_pki_credentials_free(&new);
414 return NULL;
418 /* Copy structure isds_PersonName recursively */
419 struct isds_PersonName *isds_PersonName_duplicate(
420 const struct isds_PersonName *template) {
421 struct isds_PersonName *new = NULL;
423 if (!template) return NULL;
425 new = calloc(1, sizeof(*new));
426 if (!new) return NULL;
428 STRDUP_OR_ERROR(new->pnFirstName, template->pnFirstName);
429 STRDUP_OR_ERROR(new->pnMiddleName, template->pnMiddleName);
430 STRDUP_OR_ERROR(new->pnLastName, template->pnLastName);
431 STRDUP_OR_ERROR(new->pnLastNameAtBirth, template->pnLastNameAtBirth);
433 return new;
435 error:
436 isds_PersonName_free(&new);
437 return NULL;
441 /* Copy structure isds_BirthInfo recursively */
442 static struct isds_BirthInfo *isds_BirthInfo_duplicate(
443 const struct isds_BirthInfo *template) {
444 struct isds_BirthInfo *new = NULL;
446 if (!template) return NULL;
448 new = calloc(1, sizeof(*new));
449 if (!new) return NULL;
451 FLATDUP_OR_ERROR(new->biDate, template->biDate);
452 STRDUP_OR_ERROR(new->biCity, template->biCity);
453 STRDUP_OR_ERROR(new->biCounty, template->biCounty);
454 STRDUP_OR_ERROR(new->biState, template->biState);
456 return new;
458 error:
459 isds_BirthInfo_free(&new);
460 return NULL;
464 /* Copy structure isds_Address recursively */
465 struct isds_Address *isds_Address_duplicate(
466 const struct isds_Address *template) {
467 struct isds_Address *new = NULL;
469 if (!template) return NULL;
471 new = calloc(1, sizeof(*new));
472 if (!new) return NULL;
474 STRDUP_OR_ERROR(new->adCity, template->adCity);
475 STRDUP_OR_ERROR(new->adStreet, template->adStreet);
476 STRDUP_OR_ERROR(new->adNumberInStreet, template->adNumberInStreet);
477 STRDUP_OR_ERROR(new->adNumberInMunicipality,
478 template->adNumberInMunicipality);
479 STRDUP_OR_ERROR(new->adZipCode, template->adZipCode);
480 STRDUP_OR_ERROR(new->adState, template->adState);
482 return new;
484 error:
485 isds_Address_free(&new);
486 return NULL;
490 /* Copy structure isds_DbOwnerInfo recursively */
491 struct isds_DbOwnerInfo *isds_DbOwnerInfo_duplicate(
492 const struct isds_DbOwnerInfo *template) {
493 struct isds_DbOwnerInfo *new = NULL;
494 if (!template) return NULL;
496 new = calloc(1, sizeof(*new));
497 if (!new) return NULL;
499 STRDUP_OR_ERROR(new->dbID, template->dbID);
500 FLATDUP_OR_ERROR(new->dbType, template->dbType);
501 STRDUP_OR_ERROR(new->ic, template->ic);
503 if (template->personName) {
504 if (!(new->personName =
505 isds_PersonName_duplicate(template->personName)))
506 goto error;
509 STRDUP_OR_ERROR(new->firmName, template->firmName);
511 if (template->birthInfo) {
512 if (!(new->birthInfo =
513 isds_BirthInfo_duplicate(template->birthInfo)))
514 goto error;
517 if (template->address) {
518 if (!(new->address = isds_Address_duplicate(template->address)))
519 goto error;
522 STRDUP_OR_ERROR(new->nationality, template->nationality);
523 STRDUP_OR_ERROR(new->email, template->email);
524 STRDUP_OR_ERROR(new->telNumber, template->telNumber);
525 STRDUP_OR_ERROR(new->identifier, template->identifier);
526 STRDUP_OR_ERROR(new->registryCode, template->registryCode);
527 FLATDUP_OR_ERROR(new->dbState, template->dbState);
528 FLATDUP_OR_ERROR(new->dbEffectiveOVM, template->dbEffectiveOVM);
529 FLATDUP_OR_ERROR(new->dbOpenAddressing, template->dbOpenAddressing);
531 return new;
533 error:
534 isds_DbOwnerInfo_free(&new);
535 return NULL;
539 /* Copy structure isds_DbUserInfo recursively */
540 struct isds_DbUserInfo *isds_DbUserInfo_duplicate(
541 const struct isds_DbUserInfo *template) {
542 struct isds_DbUserInfo *new = NULL;
543 if (!template) return NULL;
545 new = calloc(1, sizeof(*new));
546 if (!new) return NULL;
548 STRDUP_OR_ERROR(new->userID, template->userID);
549 FLATDUP_OR_ERROR(new->userType, template->userType);
550 FLATDUP_OR_ERROR(new->userPrivils, template->userPrivils);
552 if (template->personName) {
553 if (!(new->personName =
554 isds_PersonName_duplicate(template->personName)))
555 goto error;
558 if (template->address) {
559 if (!(new->address = isds_Address_duplicate(template->address)))
560 goto error;
563 FLATDUP_OR_ERROR(new->biDate, template->biDate);
564 STRDUP_OR_ERROR(new->ic, template->ic);
565 STRDUP_OR_ERROR(new->firmName, template->firmName);
566 STRDUP_OR_ERROR(new->caStreet, template->caStreet);
567 STRDUP_OR_ERROR(new->caCity, template->caCity);
568 STRDUP_OR_ERROR(new->caZipCode, template->caZipCode);
569 STRDUP_OR_ERROR(new->caState, template->caState);
571 return new;
573 error:
574 isds_DbUserInfo_free(&new);
575 return NULL;
578 #undef FLATDUP_OR_ERROR
579 #undef STRDUP_OR_ERROR
582 /* Logs libxml2 errors. Should be registered to libxml2 library.
583 * @ctx is unused currently
584 * @msg is printf-like formated message from libxml2 (UTF-8?)
585 * @... are variadic arguments for @msg */
586 static void log_xml(void *ctx, const char *msg, ...) {
587 va_list ap;
588 char *text = NULL;
590 if (!msg) return;
592 va_start(ap, msg);
593 isds_vasprintf(&text, msg, ap);
594 va_end(ap);
596 if (text)
597 isds_log(ILF_XML, ILL_ERR, "%s", text);
598 free(text);
602 /* Initialize ISDS library.
603 * Global function, must be called before other functions.
604 * If it fails you can not use ISDS library and must call isds_cleanup() to
605 * free partially initialized global variables. */
606 isds_error isds_init(void) {
607 /* NULL global variables */
608 log_facilities = ILF_ALL;
609 log_level = ILL_WARNING;
610 log_callback = NULL;
611 log_callback_data = NULL;
613 #if ENABLE_NLS
614 /* Initialize gettext */
615 bindtextdomain(PACKAGE, LOCALEDIR);
616 #endif
618 #if HAVE_LIBCURL
619 /* Initialize CURL */
620 if (curl_global_init(CURL_GLOBAL_ALL)) {
621 isds_log(ILF_ISDS, ILL_CRIT, _("CURL library initialization failed\n"));
622 return IE_ERROR;
624 #endif /* HAVE_LIBCURL */
626 /* Initialize gpg-error because of gpgme and ksba */
627 if (gpg_err_init()) {
628 isds_log(ILF_ISDS, ILL_CRIT,
629 _("gpg-error library initialization failed\n"));
630 return IE_ERROR;
633 /* Initialize GPGME */
634 if (_isds_init_gpgme(&version_gpgme)) {
635 isds_log(ILF_ISDS, ILL_CRIT,
636 _("GPGME library initialization failed\n"));
637 return IE_ERROR;
640 /* Initialize gcrypt */
641 if (_isds_init_gcrypt(&version_gcrypt)) {
642 isds_log(ILF_ISDS, ILL_CRIT,
643 _("gcrypt library initialization failed\n"));
644 return IE_ERROR;
647 /* This can _exit() current program. Find not so assertive check. */
648 LIBXML_TEST_VERSION;
649 xmlSetGenericErrorFunc(NULL, log_xml);
651 /* Check expat */
652 if (_isds_init_expat(&version_expat)) {
653 isds_log(ILF_ISDS, ILL_CRIT,
654 _("expat library initialization failed\n"));
655 return IE_ERROR;
658 /* Allocate global variables */
661 return IE_SUCCESS;
665 /* Deinitialize ISDS library.
666 * Global function, must be called as last library function. */
667 isds_error isds_cleanup(void) {
668 /* XML */
669 xmlCleanupParser();
671 #if HAVE_LIBCURL
672 /* Curl */
673 curl_global_cleanup();
674 #endif
676 return IE_SUCCESS;
680 /* Return version string of this library. Version of dependencies can be
681 * embedded. Do no try to parse it. You must free it. */
682 char *isds_version(void) {
683 char *buffer = NULL;
685 isds_asprintf(&buffer,
686 #if HAVE_LIBCURL
687 _("%s (%s, GPGME %s, gcrypt %s, %s, libxml2 %s)"),
688 #else
689 _("%s (GPGME %s, gcrypt %s, %s, libxml2 %s)"),
690 #endif
691 PACKAGE_VERSION,
692 #if HAVE_LIBCURL
693 curl_version(),
694 #endif
695 version_gpgme, version_gcrypt,
696 version_expat, xmlParserVersion);
697 return buffer;
701 /* Return text description of ISDS error */
702 const char *isds_strerror(const isds_error error) {
703 switch (error) {
704 case IE_SUCCESS:
705 return(_("Success")); break;
706 case IE_ERROR:
707 return(_("Unspecified error")); break;
708 case IE_NOTSUP:
709 return(_("Not supported")); break;
710 case IE_INVAL:
711 return(_("Invalid value")); break;
712 case IE_INVALID_CONTEXT:
713 return(_("Invalid context")); break;
714 case IE_NOT_LOGGED_IN:
715 return(_("Not logged in")); break;
716 case IE_CONNECTION_CLOSED:
717 return(_("Connection closed")); break;
718 case IE_TIMED_OUT:
719 return(_("Timed out")); break;
720 case IE_NOEXIST:
721 return(_("Not exist")); break;
722 case IE_NOMEM:
723 return(_("Out of memory")); break;
724 case IE_NETWORK:
725 return(_("Network problem")); break;
726 case IE_HTTP:
727 return(_("HTTP problem")); break;
728 case IE_SOAP:
729 return(_("SOAP problem")); break;
730 case IE_XML:
731 return(_("XML problem")); break;
732 case IE_ISDS:
733 return(_("ISDS server problem")); break;
734 case IE_ENUM:
735 return(_("Invalid enum value")); break;
736 case IE_DATE:
737 return(_("Invalid date value")); break;
738 case IE_2BIG:
739 return(_("Too big")); break;
740 case IE_2SMALL:
741 return(_("Too small")); break;
742 case IE_NOTUNIQ:
743 return(_("Value not unique")); break;
744 case IE_NOTEQUAL:
745 return(_("Values not equal")); break;
746 case IE_PARTIAL_SUCCESS:
747 return(_("Some suboperations failed")); break;
748 case IE_ABORTED:
749 return(_("Operation aborted")); break;
750 default:
751 return(_("Unknown error"));
756 /* Create ISDS context.
757 * Each context can be used for different sessions to (possibly) different
758 * ISDS server with different credentials. */
759 struct isds_ctx *isds_ctx_create(void) {
760 struct isds_ctx *context;
761 context = malloc(sizeof(*context));
762 if (context) memset(context, 0, sizeof(*context));
763 return context;
766 #if HAVE_LIBCURL
767 /* Close possibly opened connection to Czech POINT document deposit without
768 * resetting long_message buffer.
769 * XXX: Do not use czp_close_connection() if you do not want to destroy log
770 * message.
771 * @context is Czech POINT session context. */
772 static isds_error czp_do_close_connection(struct isds_ctx *context) {
773 if (!context) return IE_INVALID_CONTEXT;
774 _isds_close_connection(context);
775 return IE_SUCCESS;
779 /* Discard credentials.
780 * Only that. It does not cause log out, connection close or similar. */
781 _hidden isds_error _isds_discard_credentials(struct isds_ctx *context) {
782 if(!context) return IE_INVALID_CONTEXT;
784 if (context->username) {
785 memset(context->username, 0, strlen(context->username));
786 zfree(context->username);
788 if (context->password) {
789 memset(context->password, 0, strlen(context->password));
790 zfree(context->password);
792 isds_pki_credentials_free(&context->pki_credentials);
794 return IE_SUCCESS;
796 #endif /* HAVE_LIBCURL */
799 /* Destroy ISDS context and free memory.
800 * @context will be NULLed on success. */
801 isds_error isds_ctx_free(struct isds_ctx **context) {
802 if (!context || !*context) {
803 return IE_INVALID_CONTEXT;
806 #if HAVE_LIBCURL
807 /* Discard credentials and close connection */
808 switch ((*context)->type) {
809 case CTX_TYPE_NONE: break;
810 case CTX_TYPE_ISDS: isds_logout(*context); break;
811 case CTX_TYPE_CZP:
812 case CTX_TYPE_TESTING_REQUEST_COLLECTOR:
813 czp_do_close_connection(*context); break;
816 /* For sure */
817 _isds_discard_credentials(*context);
819 /* Free other structures */
820 free((*context)->tls_verify_server);
821 free((*context)->tls_ca_file);
822 free((*context)->tls_ca_dir);
823 free((*context)->tls_crl_file);
824 #endif /* HAVE_LIBCURL */
825 free((*context)->long_message);
827 free(*context);
828 *context = NULL;
829 return IE_SUCCESS;
833 /* Return long message text produced by library function, e.g. detailed error
834 * message. Returned pointer is only valid until new library function is
835 * called for the same context. Could be NULL, especially if NULL context is
836 * supplied. Return string is locale encoded. */
837 char *isds_long_message(const struct isds_ctx *context) {
838 if (!context) return NULL;
839 return context->long_message;
843 /* Stores message into context' long_message buffer.
844 * Application can pick the message up using isds_long_message().
845 * NULL @message truncates the buffer but does not deallocate it.
846 * @message is coded in locale encoding */
847 _hidden isds_error isds_log_message(struct isds_ctx *context,
848 const char *message) {
849 char *buffer;
850 size_t length;
852 if (!context) return IE_INVALID_CONTEXT;
854 /* FIXME: Check for integer overflow */
855 length = 1 + ((message) ? strlen(message) : 0);
856 buffer = realloc(context->long_message, length);
857 if (!buffer) return IE_NOMEM;
859 if (message)
860 strcpy(buffer, message);
861 else
862 *buffer = '\0';
864 context->long_message = buffer;
865 return IE_SUCCESS;
869 /* Appends message into context' long_message buffer.
870 * Application can pick the message up using isds_long_message().
871 * NULL message has void effect. */
872 _hidden isds_error isds_append_message(struct isds_ctx *context,
873 const char *message) {
874 char *buffer;
875 size_t old_length, length;
877 if (!context) return IE_INVALID_CONTEXT;
878 if (!message) return IE_SUCCESS;
879 if (!context->long_message)
880 return isds_log_message(context, message);
882 old_length = strlen(context->long_message);
883 /* FIXME: Check for integer overflow */
884 length = 1 + old_length + strlen(message);
885 buffer = realloc(context->long_message, length);
886 if (!buffer) return IE_NOMEM;
888 strcpy(buffer + old_length, message);
890 context->long_message = buffer;
891 return IE_SUCCESS;
895 /* Stores formatted message into context' long_message buffer.
896 * Application can pick the message up using isds_long_message(). */
897 _hidden isds_error isds_printf_message(struct isds_ctx *context,
898 const char *format, ...) {
899 va_list ap;
900 int length;
902 if (!context) return IE_INVALID_CONTEXT;
903 va_start(ap, format);
904 length = isds_vasprintf(&(context->long_message), format, ap);
905 va_end(ap);
907 return (length < 0) ? IE_ERROR: IE_SUCCESS;
911 /* Set logging up.
912 * @facilities is bit mask of isds_log_facility values,
913 * @level is verbosity level. */
914 void isds_set_logging(const unsigned int facilities,
915 const isds_log_level level) {
916 log_facilities = facilities;
917 log_level = level;
921 /* Register callback function libisds calls when new global log message is
922 * produced by library. Library logs to stderr by default.
923 * @callback is function provided by application libisds will call. See type
924 * definition for @callback argument explanation. Pass NULL to revert logging to
925 * default behaviour.
926 * @data is application specific data @callback gets as last argument */
927 void isds_set_log_callback(isds_log_callback callback, void *data) {
928 log_callback = callback;
929 log_callback_data = data;
933 /* Log @message in class @facility with log @level into global log. @message
934 * is printf(3) formatting string, variadic arguments may be necessary.
935 * For debugging purposes. */
936 _hidden isds_error isds_log(const isds_log_facility facility,
937 const isds_log_level level, const char *message, ...) {
938 va_list ap;
939 char *buffer = NULL;
940 int length;
942 if (level > log_level) return IE_SUCCESS;
943 if (!(log_facilities & facility)) return IE_SUCCESS;
944 if (!message) return IE_INVAL;
946 if (log_callback) {
947 /* Pass message to application supplied callback function */
948 va_start(ap, message);
949 length = isds_vasprintf(&buffer, message, ap);
950 va_end(ap);
952 if (length == -1) {
953 return IE_ERROR;
955 if (length > 0) {
956 log_callback(facility, level, buffer, length, log_callback_data);
958 free(buffer);
959 } else {
960 /* Default: Log it to stderr */
961 va_start(ap, message);
962 vfprintf(stderr, message, ap);
963 va_end(ap);
964 /* Line buffered printf is default.
965 * fflush(stderr);*/
968 return IE_SUCCESS;
972 /* Set timeout in milliseconds for each network job like connecting to server
973 * or sending message. Use 0 to disable timeout limits. */
974 isds_error isds_set_timeout(struct isds_ctx *context,
975 const unsigned int timeout) {
976 if (!context) return IE_INVALID_CONTEXT;
977 zfree(context->long_message);
979 #if HAVE_LIBCURL
980 context->timeout = timeout;
982 if (context->curl) {
983 CURLcode curl_err;
985 curl_err = curl_easy_setopt(context->curl, CURLOPT_NOSIGNAL, 1);
986 if (!curl_err)
987 #if HAVE_DECL_CURLOPT_TIMEOUT_MS /* Since curl-7.16.2 */
988 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT_MS,
989 context->timeout);
990 #else
991 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT,
992 context->timeout / 1000);
993 #endif /* not HAVE_DECL_CURLOPT_TIMEOUT_MS */
994 if (curl_err) return IE_ERROR;
997 return IE_SUCCESS;
998 #else /* not HAVE_LIBCURL */
999 return IE_NOTSUP;
1000 #endif
1004 /* Register callback function libisds calls periodically during HTTP data
1005 * transfer.
1006 * @context is session context
1007 * @callback is function provided by application libisds will call. See type
1008 * definition for @callback argument explanation.
1009 * @data is application specific data @callback gets as last argument */
1010 isds_error isds_set_progress_callback(struct isds_ctx *context,
1011 isds_progress_callback callback, void *data) {
1012 if (!context) return IE_INVALID_CONTEXT;
1013 zfree(context->long_message);
1015 #if HAVE_LIBCURL
1016 context->progress_callback = callback;
1017 context->progress_callback_data = data;
1019 return IE_SUCCESS;
1020 #else /* not HAVE_LIBCURL */
1021 return IE_NOTSUP;
1022 #endif
1026 /* Change context settings.
1027 * @context is context which setting will be applied to
1028 * @option is name of option. It determines the type of last argument. See
1029 * isds_option definition for more info.
1030 * @... is value of new setting. Type is determined by @option
1031 * */
1032 isds_error isds_set_opt(struct isds_ctx *context, const isds_option option,
1033 ...) {
1034 isds_error err = IE_SUCCESS;
1035 va_list ap;
1036 #if HAVE_LIBCURL
1037 char *pointer, *string;
1038 #endif
1040 if (!context) return IE_INVALID_CONTEXT;
1041 zfree(context->long_message);
1043 va_start(ap, option);
1045 #define REPLACE_VA_BOOLEAN(destination) { \
1046 if (!(destination)) { \
1047 (destination) = malloc(sizeof(*(destination))); \
1048 if (!(destination)) { \
1049 err = IE_NOMEM; goto leave; \
1052 *(destination) = (_Bool) !!va_arg(ap, int); \
1055 #define REPLACE_VA_STRING(destination) { \
1056 string = va_arg(ap, char *); \
1057 if (string) { \
1058 pointer = realloc((destination), 1 + strlen(string)); \
1059 if (!pointer) { err = IE_NOMEM; goto leave; } \
1060 strcpy(pointer, string); \
1061 (destination) = pointer; \
1062 } else { \
1063 free(destination); \
1064 (destination) = NULL; \
1068 switch (option) {
1069 case IOPT_TLS_VERIFY_SERVER:
1070 #if HAVE_LIBCURL
1071 REPLACE_VA_BOOLEAN(context->tls_verify_server);
1072 #else
1073 err = IE_NOTSUP; goto leave;
1074 #endif
1075 break;
1076 case IOPT_TLS_CA_FILE:
1077 #if HAVE_LIBCURL
1078 REPLACE_VA_STRING(context->tls_ca_file);
1079 #else
1080 err = IE_NOTSUP; goto leave;
1081 #endif
1082 break;
1083 case IOPT_TLS_CA_DIRECTORY:
1084 #if HAVE_LIBCURL
1085 REPLACE_VA_STRING(context->tls_ca_dir);
1086 #else
1087 err = IE_NOTSUP; goto leave;
1088 #endif
1089 break;
1090 case IOPT_TLS_CRL_FILE:
1091 #if HAVE_LIBCURL
1092 #if HAVE_DECL_CURLOPT_CRLFILE /* Since curl-7.19.0 */
1093 REPLACE_VA_STRING(context->tls_crl_file);
1094 #else
1095 isds_log_message(context,
1096 _("Curl library does not support CRL definition"));
1097 err = IE_NOTSUP;
1098 #endif /* not HAVE_DECL_CURLOPT_CRLFILE */
1099 #else
1100 err = IE_NOTSUP; goto leave;
1101 #endif /* not HAVE_LIBCURL */
1102 break;
1103 case IOPT_NORMALIZE_MIME_TYPE:
1104 context->normalize_mime_type = (_Bool) !!va_arg(ap, int);
1105 break;
1107 default:
1108 err = IE_ENUM; goto leave;
1111 #undef REPLACE_VA_STRING
1112 #undef REPLACE_VA_BOOLEAN
1114 leave:
1115 va_end(ap);
1116 return err;
1120 /* Connect and log into ISDS server.
1121 * All required arguments will be copied, you do not have to keep them after
1122 * that.
1123 * ISDS supports six different authentication methods. Exact method is
1124 * selected on @username, @password, @pki_credentials, and @otp arguments:
1125 * - If @pki_credentials == NULL, @username and @password must be supplied
1126 * and then
1127 * - If @otp == NULL, simple authentication by username and password will
1128 * be proceeded.
1129 * - If @otp != NULL, authentication by username and password and OTP
1130 * will be used.
1131 * - If @pki_credentials != NULL, then
1132 * - If @username == NULL, only certificate will be used
1133 * - If @username != NULL, then
1134 * - If @password == NULL, then certificate will be used and
1135 * @username shifts meaning to box ID. This is used for hosted
1136 * services.
1137 * - Otherwise all three arguments will be used.
1138 * Please note, that different cases require different certificate type
1139 * (system qualified one or commercial non qualified one). This library
1140 * does not check such political issues. Please see ISDS Specification
1141 * for more details.
1142 * @url is base address of ISDS web service. Pass extern isds_locator
1143 * variable to use production ISDS instance without client certificate
1144 * authentication (or extern isds_cert_locator with client certificate
1145 * authentication or extern isds_otp_locators with OTP authentication).
1146 * Passing NULL has the same effect, autoselection between isds_locator,
1147 * isds_cert_locator, and isds_otp_locator is performed in addition. You can
1148 * pass extern isds_testing_locator (or isds_cert_testing_locator or
1149 * isds_otp_testing_locator) variable to select testing instance.
1150 * @username is user name of ISDS user or box ID
1151 * @password is user's secret password
1152 * @pki_credentials defines public key cryptographic material to use in client
1153 * authentication.
1154 * @otp selects one-time password authentication method to use, defines OTP
1155 * code (if known) and returns fine grade resolution of OTP procedure.
1156 * @return:
1157 * IE_SUCCESS if authentication succeeds
1158 * IE_NOT_LOGGED_IN if authentication fails. If OTP authentication has been
1159 * requested, fine grade reason will be set into @otp->resolution. Error
1160 * message from server can be obtained by isds_long_message() call.
1161 * IE_PARTIAL_SUCCESS if time-based OTP authentication has been requested and
1162 * server has sent OTP code through side channel. Application is expected to
1163 * fill the code into @otp->otp_code, keep other arguments unchanged, and retry
1164 * this call to complete second phase of TOTP authentication;
1165 * or other appropriate error. */
1166 isds_error isds_login(struct isds_ctx *context, const char *url,
1167 const char *username, const char *password,
1168 const struct isds_pki_credentials *pki_credentials,
1169 struct isds_otp *otp) {
1170 #if HAVE_LIBCURL
1171 isds_error err = IE_NOT_LOGGED_IN;
1172 isds_error soap_err;
1173 xmlNsPtr isds_ns = NULL;
1174 xmlNodePtr request = NULL;
1175 xmlNodePtr response = NULL;
1176 #endif /* HAVE_LIBCURL */
1178 if (!context) return IE_INVALID_CONTEXT;
1179 zfree(context->long_message);
1181 #if HAVE_LIBCURL
1182 /* Close connection if already logged in */
1183 if (context->curl) {
1184 _isds_close_connection(context);
1187 /* Store configuration */
1188 context->type = CTX_TYPE_ISDS;
1189 zfree(context->url);
1191 /* Mangle base URI according to requested authentication method */
1192 if (NULL == pki_credentials) {
1193 isds_log(ILF_SEC, ILL_INFO,
1194 _("Selected authentication method: no certificate, "
1195 "username and password\n"));
1196 if (!username || !password) {
1197 isds_log_message(context,
1198 _("Both username and password must be supplied"));
1199 return IE_INVAL;
1201 context->otp = otp;
1203 if (NULL == context->otp) {
1204 /* Default locator is official system (without certificate or
1205 * OTP) */
1206 context->url = strdup((NULL != url) ? url : isds_locator);
1207 } else {
1208 const char *authenticator_uri = NULL;
1209 if (!url) url = isds_otp_locator;
1210 otp->resolution = OTP_RESOLUTION_UNKNOWN;
1211 switch (context->otp->method) {
1212 case OTP_HMAC:
1213 isds_log(ILF_SEC, ILL_INFO,
1214 _("Selected authentication method: "
1215 "HMAC-based one-time password\n"));
1216 authenticator_uri =
1217 "%1$sas/processLogin?type=hotp&uri=%1$sapps/";
1218 break;
1219 case OTP_TIME:
1220 isds_log(ILF_SEC, ILL_INFO,
1221 _("Selected authentication method: "
1222 "Time-based one-time password\n"));
1223 if (context->otp->otp_code == NULL) {
1224 isds_log(ILF_SEC, ILL_INFO,
1225 _("OTP code has not been provided by "
1226 "application, requesting server for "
1227 "new one.\n"));
1228 authenticator_uri =
1229 "%1$sas/processLogin?type=totp&sendSms=true&"
1230 "uri=%1$sapps/";
1231 } else {
1232 isds_log(ILF_SEC, ILL_INFO,
1233 _("OTP code has been provided by "
1234 "application, not requesting server "
1235 "for new one.\n"));
1236 authenticator_uri =
1237 "%1$sas/processLogin?type=totp&"
1238 "uri=%1$sapps/";
1240 break;
1241 default:
1242 isds_log_message(context,
1243 _("Unknown one-time password authentication "
1244 "method requested by application"));
1245 return IE_ENUM;
1247 if (-1 == isds_asprintf(&context->url, authenticator_uri, url))
1248 return IE_NOMEM;
1250 } else {
1251 /* Default locator is official system (with client certificate) */
1252 context->otp = NULL;
1253 if (!url) url = isds_cert_locator;
1255 if (!username) {
1256 isds_log(ILF_SEC, ILL_INFO,
1257 _("Selected authentication method: system certificate, "
1258 "no username and no password\n"));
1259 password = NULL;
1260 context->url = _isds_astrcat(url, "cert/");
1261 } else {
1262 if (!password) {
1263 isds_log(ILF_SEC, ILL_INFO,
1264 _("Selected authentication method: system certificate, "
1265 "box ID and no password\n"));
1266 context->url = _isds_astrcat(url, "hspis/");
1267 } else {
1268 isds_log(ILF_SEC, ILL_INFO,
1269 _("Selected authentication method: commercial "
1270 "certificate, username and password\n"));
1271 context->url = _isds_astrcat(url, "certds/");
1275 if (!(context->url))
1276 return IE_NOMEM;
1278 /* Prepare CURL handle */
1279 context->curl = curl_easy_init();
1280 if (!(context->curl))
1281 return IE_ERROR;
1283 /* Build log-in request */
1284 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1285 if (!request) {
1286 isds_log_message(context, _("Could not build ISDS log-in request"));
1287 return IE_ERROR;
1289 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1290 if(!isds_ns) {
1291 isds_log_message(context, _("Could not create ISDS name space"));
1292 xmlFreeNode(request);
1293 return IE_ERROR;
1295 xmlSetNs(request, isds_ns);
1297 /* Store credentials */
1298 /* FIXME: mlock password
1299 * (I have a library) */
1300 _isds_discard_credentials(context);
1301 if (username) context->username = strdup(username);
1302 if (password) {
1303 if (context->otp == NULL)
1304 context->password = strdup(password);
1305 else
1306 context->password = _isds_astrcat(password, context->otp->otp_code);
1308 context->pki_credentials = isds_pki_credentials_duplicate(pki_credentials);
1309 if ((username && !context->username) || (password && !context->password) ||
1310 (pki_credentials && !context->pki_credentials)) {
1311 _isds_discard_credentials(context);
1312 xmlFreeNode(request);
1313 return IE_NOMEM;
1316 isds_log(ILF_ISDS, ILL_DEBUG, _("Logging user %s into server %s\n"),
1317 username, url);
1319 /* Send log-in request */
1320 soap_err = _isds_soap(context, "DS/dz", request, &response, NULL, NULL);
1322 if (context->otp) {
1323 /* Revert context URL from OTP authentication service URL to OTP web
1324 * service base URL for subsequent calls. Potenial isds_login() retry
1325 * will re-set context URL again. */
1326 zfree(context->url);
1327 context->url = _isds_astrcat(url, "apps/");
1328 if (context->url == NULL) {
1329 soap_err = IE_NOMEM;
1333 /* Remove credentials */
1334 _isds_discard_credentials(context);
1336 /* Destroy log-in request */
1337 xmlFreeNode(request);
1339 if (soap_err) {
1340 xmlFreeNodeList(response);
1341 _isds_close_connection(context);
1342 return soap_err;
1345 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1346 * authentication succeeded if soap_err == IE_SUCCESS */
1347 err = IE_SUCCESS;
1349 xmlFreeNodeList(response);
1351 if (!err)
1352 isds_log(ILF_ISDS, ILL_DEBUG,
1353 _("User %s has been logged into server %s successfully\n"),
1354 username, url);
1355 return err;
1356 #else /* not HAVE_LIBCURL */
1357 return IE_NOTSUP;
1358 #endif
1362 /* Log out from ISDS server discards credentials and connection configuration. */
1363 isds_error isds_logout(struct isds_ctx *context) {
1364 if (!context) return IE_INVALID_CONTEXT;
1365 zfree(context->long_message);
1367 #if HAVE_LIBCURL
1368 if (context->curl) {
1369 if (context->otp != NULL) {
1370 isds_error err = _isds_invalidate_otp_cookie(context);
1371 if (err) return err;
1374 /* Close connection */
1375 _isds_close_connection(context);
1377 /* Discard credentials for sure. They should not survive isds_login(),
1378 * even successful .*/
1379 _isds_discard_credentials(context);
1380 zfree(context->url);
1382 isds_log(ILF_ISDS, ILL_DEBUG, _("Logged out from ISDS server\n"));
1383 } else {
1384 _isds_discard_credentials(context);
1386 return IE_SUCCESS;
1387 #else /* not HAVE_LIBCURL */
1388 return IE_NOTSUP;
1389 #endif
1393 /* Verify connection to ISDS is alive and server is responding.
1394 * Sent dummy request to ISDS and expect dummy response. */
1395 isds_error isds_ping(struct isds_ctx *context) {
1396 #if HAVE_LIBCURL
1397 isds_error soap_err;
1398 xmlNsPtr isds_ns = NULL;
1399 xmlNodePtr request = NULL;
1400 xmlNodePtr response = NULL;
1401 #endif /* HAVE_LIBCURL */
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) return IE_CONNECTION_CLOSED;
1411 /* Build dummy request */
1412 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1413 if (!request) {
1414 isds_log_message(context, _("Could build ISDS dummy request"));
1415 return IE_ERROR;
1417 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1418 if(!isds_ns) {
1419 isds_log_message(context, _("Could not create ISDS name space"));
1420 xmlFreeNode(request);
1421 return IE_ERROR;
1423 xmlSetNs(request, isds_ns);
1425 isds_log(ILF_ISDS, ILL_DEBUG, _("Pinging ISDS server\n"));
1427 /* Sent dummy request */
1428 soap_err = _isds_soap(context, "DS/dz", request, &response, NULL, NULL);
1430 /* Destroy log-in request */
1431 xmlFreeNode(request);
1433 if (soap_err) {
1434 isds_log(ILF_ISDS, ILL_DEBUG,
1435 _("ISDS server could not be contacted\n"));
1436 xmlFreeNodeList(response);
1437 return soap_err;
1440 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1441 * authentication succeeded if soap_err == IE_SUCCESS */
1442 /* TODO: ISDS documentation does not specify response body.
1443 * However real server sends back DummyOperationResponse */
1446 xmlFreeNodeList(response);
1448 isds_log(ILF_ISDS, ILL_DEBUG, _("ISDS server alive\n"));
1450 return IE_SUCCESS;
1451 #else /* not HAVE_LIBCURL */
1452 return IE_NOTSUP;
1453 #endif
1457 /* Send bogus request to ISDS.
1458 * Just for test purposes */
1459 isds_error isds_bogus_request(struct isds_ctx *context) {
1460 #if HAVE_LIBCURL
1461 isds_error err;
1462 xmlNsPtr isds_ns = NULL;
1463 xmlNodePtr request = NULL;
1464 xmlDocPtr response = NULL;
1465 xmlChar *code = NULL, *message = NULL;
1466 #endif
1468 if (!context) return IE_INVALID_CONTEXT;
1469 zfree(context->long_message);
1471 #if HAVE_LIBCURL
1472 /* Check if connection is established */
1473 if (!context->curl) {
1474 /* Testing printf message */
1475 isds_printf_message(context, "%s", _("I said connection closed"));
1476 return IE_CONNECTION_CLOSED;
1480 /* Build dummy request */
1481 request = xmlNewNode(NULL, BAD_CAST "X-BogusOperation");
1482 if (!request) {
1483 isds_log_message(context, _("Could build ISDS bogus request"));
1484 return IE_ERROR;
1486 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1487 if(!isds_ns) {
1488 isds_log_message(context, _("Could not create ISDS name space"));
1489 xmlFreeNode(request);
1490 return IE_ERROR;
1492 xmlSetNs(request, isds_ns);
1494 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending bogus request to ISDS\n"));
1496 /* Sent bogus request */
1497 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
1499 /* Destroy request */
1500 xmlFreeNode(request);
1502 if (err) {
1503 isds_log(ILF_ISDS, ILL_DEBUG,
1504 _("Processing ISDS response on bogus request failed\n"));
1505 xmlFreeDoc(response);
1506 return err;
1509 /* Check for response status */
1510 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
1511 &code, &message, NULL);
1512 if (err) {
1513 isds_log(ILF_ISDS, ILL_DEBUG,
1514 _("ISDS response on bogus request is missing status\n"));
1515 free(code);
1516 free(message);
1517 xmlFreeDoc(response);
1518 return err;
1520 if (xmlStrcmp(code, BAD_CAST "0000")) {
1521 char *code_locale = _isds_utf82locale((char*)code);
1522 char *message_locale = _isds_utf82locale((char*)message);
1523 isds_log(ILF_ISDS, ILL_DEBUG,
1524 _("Server refused bogus request (code=%s, message=%s)\n"),
1525 code_locale, message_locale);
1526 /* XXX: Literal error messages from ISDS are Czech messages
1527 * (English sometimes) in UTF-8. It's hard to catch them for
1528 * translation. Successfully gettextized would return in locale
1529 * encoding, unsuccessfully translated would pass in UTF-8. */
1530 isds_log_message(context, message_locale);
1531 free(code_locale);
1532 free(message_locale);
1533 free(code);
1534 free(message);
1535 xmlFreeDoc(response);
1536 return IE_ISDS;
1540 free(code);
1541 free(message);
1542 xmlFreeDoc(response);
1544 isds_log(ILF_ISDS, ILL_DEBUG,
1545 _("Bogus message accepted by server. This should not happen.\n"));
1547 return IE_SUCCESS;
1548 #else /* not HAVE_LIBCURL */
1549 return IE_NOTSUP;
1550 #endif
1554 #if HAVE_LIBCURL
1555 /* Serialize XML subtree to buffer preserving XML indentation.
1556 * @context is session context
1557 * @subtree is XML element to be serialized (with children)
1558 * @buffer is automatically reallocated buffer where serialize to
1559 * @length is size of serialized stream in bytes
1560 * @return standard error code, free @buffer in case of error */
1561 static isds_error serialize_subtree(struct isds_ctx *context,
1562 xmlNodePtr subtree, void **buffer, size_t *length) {
1563 isds_error err = IE_SUCCESS;
1564 xmlBufferPtr xml_buffer = NULL;
1565 xmlSaveCtxtPtr save_ctx = NULL;
1566 xmlDocPtr subtree_doc = NULL;
1567 xmlNodePtr subtree_copy;
1568 xmlNsPtr isds_ns;
1569 void *new_buffer;
1571 if (!context) return IE_INVALID_CONTEXT;
1572 if (!buffer) return IE_INVAL;
1573 zfree(*buffer);
1574 if (!subtree || !length) return IE_INVAL;
1576 /* Make temporary XML document with @subtree root element */
1577 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1578 * It can result in not well-formed on invalid XML tree (e.g. name space
1579 * prefix definition can miss. */
1580 /*FIXME */
1582 subtree_doc = xmlNewDoc(BAD_CAST "1.0");
1583 if (!subtree_doc) {
1584 isds_log_message(context, _("Could not build temporary document"));
1585 err = IE_ERROR;
1586 goto leave;
1589 /* XXX: Copy subtree and attach the copy to document.
1590 * One node can not bee attached into more document at the same time.
1591 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1592 * automatically.
1593 * XXX: Check xmlSaveTree() too. */
1594 subtree_copy = xmlCopyNodeList(subtree);
1595 if (!subtree_copy) {
1596 isds_log_message(context, _("Could not copy subtree"));
1597 err = IE_ERROR;
1598 goto leave;
1600 xmlDocSetRootElement(subtree_doc, subtree_copy);
1602 /* Only this way we get namespace definition as @xmlns:isds,
1603 * otherwise we get namespace prefix without definition */
1604 /* FIXME: Don't overwrite original default namespace */
1605 isds_ns = xmlNewNs(subtree_copy, BAD_CAST ISDS_NS, NULL);
1606 if(!isds_ns) {
1607 isds_log_message(context, _("Could not create ISDS name space"));
1608 err = IE_ERROR;
1609 goto leave;
1611 xmlSetNs(subtree_copy, isds_ns);
1614 /* Serialize the document into buffer */
1615 xml_buffer = xmlBufferCreate();
1616 if (!xml_buffer) {
1617 isds_log_message(context, _("Could not create xmlBuffer"));
1618 err = IE_ERROR;
1619 goto leave;
1621 /* Last argument 0 means to not format the XML tree */
1622 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8", 0);
1623 if (!save_ctx) {
1624 isds_log_message(context, _("Could not create XML serializer"));
1625 err = IE_ERROR;
1626 goto leave;
1628 /* XXX: According LibXML documentation, this function does not return
1629 * meaningful value yet */
1630 xmlSaveDoc(save_ctx, subtree_doc);
1631 if (-1 == xmlSaveFlush(save_ctx)) {
1632 isds_log_message(context,
1633 _("Could not serialize XML subtree"));
1634 err = IE_ERROR;
1635 goto leave;
1637 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1638 * even after xmlSaveFlush(). Thus close it here */
1639 xmlSaveClose(save_ctx); save_ctx = NULL;
1642 /* Store and detach buffer from xml_buffer */
1643 *buffer = xml_buffer->content;
1644 *length = xml_buffer->use;
1645 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1647 /* Shrink buffer */
1648 new_buffer = realloc(*buffer, *length);
1649 if (new_buffer) *buffer = new_buffer;
1651 leave:
1652 if (err) {
1653 zfree(*buffer);
1654 *length = 0;
1657 xmlSaveClose(save_ctx);
1658 xmlBufferFree(xml_buffer);
1659 xmlFreeDoc(subtree_doc); /* Frees subtree_copy, isds_ns etc. */
1660 return err;
1662 #endif /* HAVE_LIBCURL */
1665 #if 0
1666 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1667 * @context is session context
1668 * @document is original document where @nodeset points to
1669 * @nodeset is XPath node set to dump (recursively)
1670 * @buffer is automatically reallocated buffer where serialize to
1671 * @length is size of serialized stream in bytes
1672 * @return standard error code, free @buffer in case of error */
1673 static isds_error dump_nodeset(struct isds_ctx *context,
1674 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1675 void **buffer, size_t *length) {
1676 isds_error err = IE_SUCCESS;
1677 xmlBufferPtr xml_buffer = NULL;
1678 void *new_buffer;
1680 if (!context) return IE_INVALID_CONTEXT;
1681 if (!buffer) return IE_INVAL;
1682 zfree(*buffer);
1683 if (!document || !nodeset || !length) return IE_INVAL;
1684 *length = 0;
1686 /* Empty node set results into NULL buffer */
1687 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1688 goto leave;
1691 /* Resulting the document into buffer */
1692 xml_buffer = xmlBufferCreate();
1693 if (!xml_buffer) {
1694 isds_log_message(context, _("Could not create xmlBuffer"));
1695 err = IE_ERROR;
1696 goto leave;
1699 /* Iterate over all nodes */
1700 for (int i = 0; i < nodeset->nodeNr; i++) {
1701 /* Serialize node.
1702 * XXX: xmlNodeDump() appends to xml_buffer. */
1703 if (-1 ==
1704 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1705 isds_log_message(context, _("Could not dump XML node"));
1706 err = IE_ERROR;
1707 goto leave;
1711 /* Store and detach buffer from xml_buffer */
1712 *buffer = xml_buffer->content;
1713 *length = xml_buffer->use;
1714 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1716 /* Shrink buffer */
1717 new_buffer = realloc(*buffer, *length);
1718 if (new_buffer) *buffer = new_buffer;
1721 leave:
1722 if (err) {
1723 zfree(*buffer);
1724 *length = 0;
1727 xmlBufferFree(xml_buffer);
1728 return err;
1730 #endif
1732 #if 0
1733 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1734 * @context is session context
1735 * @document is original document where @nodeset points to
1736 * @nodeset is XPath node set to dump (recursively)
1737 * @buffer is automatically reallocated buffer where serialize to
1738 * @length is size of serialized stream in bytes
1739 * @return standard error code, free @buffer in case of error */
1740 static isds_error dump_nodeset(struct isds_ctx *context,
1741 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1742 void **buffer, size_t *length) {
1743 isds_error err = IE_SUCCESS;
1744 xmlBufferPtr xml_buffer = NULL;
1745 xmlSaveCtxtPtr save_ctx = NULL;
1746 void *new_buffer;
1748 if (!context) return IE_INVALID_CONTEXT;
1749 if (!buffer) return IE_INVAL;
1750 zfree(*buffer);
1751 if (!document || !nodeset || !length) return IE_INVAL;
1752 *length = 0;
1754 /* Empty node set results into NULL buffer */
1755 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1756 goto leave;
1759 /* Resulting the document into buffer */
1760 xml_buffer = xmlBufferCreate();
1761 if (!xml_buffer) {
1762 isds_log_message(context, _("Could not create xmlBuffer"));
1763 err = IE_ERROR;
1764 goto leave;
1766 if (xmlSubstituteEntitiesDefault(1)) {
1767 isds_log_message(context, _("Could not disable attribute escaping"));
1768 err = IE_ERROR;
1769 goto leave;
1771 /* Last argument means:
1772 * 0 to not format the XML tree
1773 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1774 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8",
1775 XML_SAVE_NO_DECL|XML_SAVE_NO_EMPTY|XML_SAVE_NO_XHTML);
1776 if (!save_ctx) {
1777 isds_log_message(context, _("Could not create XML serializer"));
1778 err = IE_ERROR;
1779 goto leave;
1781 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1782 isds_log_message(context, _("Could not disable attribute escaping"));
1783 err = IE_ERROR;
1784 goto leave;
1788 /* Iterate over all nodes */
1789 for (int i = 0; i < nodeset->nodeNr; i++) {
1790 /* Serialize node.
1791 * XXX: xmlNodeDump() appends to xml_buffer. */
1792 /*if (-1 ==
1793 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1795 /* XXX: According LibXML documentation, this function does not return
1796 * meaningful value yet */
1797 xmlSaveTree(save_ctx, nodeset->nodeTab[i]);
1798 if (-1 == xmlSaveFlush(save_ctx)) {
1799 isds_log_message(context,
1800 _("Could not serialize XML subtree"));
1801 err = IE_ERROR;
1802 goto leave;
1806 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1807 * even after xmlSaveFlush(). Thus close it here */
1808 xmlSaveClose(save_ctx); save_ctx = NULL;
1810 /* Store and detach buffer from xml_buffer */
1811 *buffer = xml_buffer->content;
1812 *length = xml_buffer->use;
1813 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1815 /* Shrink buffer */
1816 new_buffer = realloc(*buffer, *length);
1817 if (new_buffer) *buffer = new_buffer;
1819 leave:
1820 if (err) {
1821 zfree(*buffer);
1822 *length = 0;
1825 xmlSaveClose(save_ctx);
1826 xmlBufferFree(xml_buffer);
1827 return err;
1829 #endif
1832 #if HAVE_LIBCURL
1833 /* Convert UTF-8 @string representation of ISDS dbType to enum @type */
1834 static isds_error string2isds_DbType(xmlChar *string, isds_DbType *type) {
1835 if (!string || !type) return IE_INVAL;
1837 if (!xmlStrcmp(string, BAD_CAST "FO"))
1838 *type = DBTYPE_FO;
1839 else if (!xmlStrcmp(string, BAD_CAST "PFO"))
1840 *type = DBTYPE_PFO;
1841 else if (!xmlStrcmp(string, BAD_CAST "PFO_ADVOK"))
1842 *type = DBTYPE_PFO_ADVOK;
1843 else if (!xmlStrcmp(string, BAD_CAST "PFO_DANPOR"))
1844 *type = DBTYPE_PFO_DANPOR;
1845 else if (!xmlStrcmp(string, BAD_CAST "PFO_INSSPR"))
1846 *type = DBTYPE_PFO_INSSPR;
1847 else if (!xmlStrcmp(string, BAD_CAST "PO"))
1848 *type = DBTYPE_PO;
1849 else if (!xmlStrcmp(string, BAD_CAST "PO_ZAK"))
1850 *type = DBTYPE_PO_ZAK;
1851 else if (!xmlStrcmp(string, BAD_CAST "PO_REQ"))
1852 *type = DBTYPE_PO_REQ;
1853 else if (!xmlStrcmp(string, BAD_CAST "OVM"))
1854 *type = DBTYPE_OVM;
1855 else if (!xmlStrcmp(string, BAD_CAST "OVM_NOTAR"))
1856 *type = DBTYPE_OVM_NOTAR;
1857 else if (!xmlStrcmp(string, BAD_CAST "OVM_EXEKUT"))
1858 *type = DBTYPE_OVM_EXEKUT;
1859 else if (!xmlStrcmp(string, BAD_CAST "OVM_REQ"))
1860 *type = DBTYPE_OVM_REQ;
1861 else
1862 return IE_ENUM;
1863 return IE_SUCCESS;
1867 /* Convert ISDS dbType enum @type to UTF-8 string.
1868 * @Return pointer to static string, or NULL if unknown enum value */
1869 static const xmlChar *isds_DbType2string(const isds_DbType type) {
1870 switch(type) {
1871 /* DBTYPE_SYSTEM is invalid value from point of view of public
1872 * SOAP interface. */
1873 case DBTYPE_FO: return(BAD_CAST "FO"); break;
1874 case DBTYPE_PFO: return(BAD_CAST "PFO"); break;
1875 case DBTYPE_PFO_ADVOK: return(BAD_CAST "PFO_ADVOK"); break;
1876 case DBTYPE_PFO_DANPOR: return(BAD_CAST "PFO_DANPOR"); break;
1877 case DBTYPE_PFO_INSSPR: return(BAD_CAST "PFO_INSSPR"); break;
1878 case DBTYPE_PO: return(BAD_CAST "PO"); break;
1879 case DBTYPE_PO_ZAK: return(BAD_CAST "PO_ZAK"); break;
1880 case DBTYPE_PO_REQ: return(BAD_CAST "PO_REQ"); break;
1881 case DBTYPE_OVM: return(BAD_CAST "OVM"); break;
1882 case DBTYPE_OVM_NOTAR: return(BAD_CAST "OVM_NOTAR"); break;
1883 case DBTYPE_OVM_EXEKUT: return(BAD_CAST "OVM_EXEKUT"); break;
1884 case DBTYPE_OVM_REQ: return(BAD_CAST "OVM_REQ"); break;
1885 default: return NULL; break;
1890 /* Convert UTF-8 @string representation of ISDS userType to enum @type */
1891 static isds_error string2isds_UserType(xmlChar *string, isds_UserType *type) {
1892 if (!string || !type) return IE_INVAL;
1894 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
1895 *type = USERTYPE_PRIMARY;
1896 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
1897 *type = USERTYPE_ENTRUSTED;
1898 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
1899 *type = USERTYPE_ADMINISTRATOR;
1900 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
1901 *type = USERTYPE_OFFICIAL;
1902 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
1903 *type = USERTYPE_OFFICIAL_CERT;
1904 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
1905 *type = USERTYPE_LIQUIDATOR;
1906 else
1907 return IE_ENUM;
1908 return IE_SUCCESS;
1912 /* Convert ISDS userType enum @type to UTF-8 string.
1913 * @Return pointer to static string, or NULL if unknown enum value */
1914 static const xmlChar *isds_UserType2string(const isds_UserType type) {
1915 switch(type) {
1916 case USERTYPE_PRIMARY: return(BAD_CAST "PRIMARY_USER"); break;
1917 case USERTYPE_ENTRUSTED: return(BAD_CAST "ENTRUSTED_USER"); break;
1918 case USERTYPE_ADMINISTRATOR: return(BAD_CAST "ADMINISTRATOR"); break;
1919 case USERTYPE_OFFICIAL: return(BAD_CAST "OFFICIAL"); break;
1920 case USERTYPE_OFFICIAL_CERT: return(BAD_CAST "OFFICIAL_CERT"); break;
1921 case USERTYPE_LIQUIDATOR: return(BAD_CAST "LIQUIDATOR"); break;
1922 default: return NULL; break;
1927 /* Convert UTF-8 @string representation of ISDS sender type to enum @type */
1928 static isds_error string2isds_sender_type(const xmlChar *string,
1929 isds_sender_type *type) {
1930 if (!string || !type) return IE_INVAL;
1932 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
1933 *type = SENDERTYPE_PRIMARY;
1934 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
1935 *type = SENDERTYPE_ENTRUSTED;
1936 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
1937 *type = SENDERTYPE_ADMINISTRATOR;
1938 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
1939 *type = SENDERTYPE_OFFICIAL;
1940 else if (!xmlStrcmp(string, BAD_CAST "VIRTUAL"))
1941 *type = SENDERTYPE_VIRTUAL;
1942 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
1943 *type = SENDERTYPE_OFFICIAL_CERT;
1944 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
1945 *type = SENDERTYPE_LIQUIDATOR;
1946 else
1947 return IE_ENUM;
1948 return IE_SUCCESS;
1952 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
1953 * @Return pointer to static string, or NULL if unknown enum value */
1954 static const xmlChar *isds_FileMetaType2string(const isds_FileMetaType type) {
1955 switch(type) {
1956 case FILEMETATYPE_MAIN: return(BAD_CAST "main"); break;
1957 case FILEMETATYPE_ENCLOSURE: return(BAD_CAST "enclosure"); break;
1958 case FILEMETATYPE_SIGNATURE: return(BAD_CAST "signature"); break;
1959 case FILEMETATYPE_META: return(BAD_CAST "meta"); break;
1960 default: return NULL; break;
1963 #endif /* HAVE_LIBCURL */
1966 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
1967 * @Return IE_ENUM if @string is not valid enum member */
1968 static isds_error string2isds_FileMetaType(const xmlChar *string,
1969 isds_FileMetaType *type) {
1970 if (!string || !type) return IE_INVAL;
1972 if (!xmlStrcmp(string, BAD_CAST "main"))
1973 *type = FILEMETATYPE_MAIN;
1974 else if (!xmlStrcmp(string, BAD_CAST "enclosure"))
1975 *type = FILEMETATYPE_ENCLOSURE;
1976 else if (!xmlStrcmp(string, BAD_CAST "signature"))
1977 *type = FILEMETATYPE_SIGNATURE;
1978 else if (!xmlStrcmp(string, BAD_CAST "meta"))
1979 *type = FILEMETATYPE_META;
1980 else
1981 return IE_ENUM;
1982 return IE_SUCCESS;
1986 /* Convert UTF-8 @string to ISDS hash @algorithm.
1987 * @Return IE_ENUM if @string is not valid enum member */
1988 static isds_error string2isds_hash_algorithm(const xmlChar *string,
1989 isds_hash_algorithm *algorithm) {
1990 if (!string || !algorithm) return IE_INVAL;
1992 if (!xmlStrcmp(string, BAD_CAST "MD5"))
1993 *algorithm = HASH_ALGORITHM_MD5;
1994 else if (!xmlStrcmp(string, BAD_CAST "SHA-1"))
1995 *algorithm = HASH_ALGORITHM_SHA_1;
1996 else if (!xmlStrcmp(string, BAD_CAST "SHA-224"))
1997 *algorithm = HASH_ALGORITHM_SHA_224;
1998 else if (!xmlStrcmp(string, BAD_CAST "SHA-256"))
1999 *algorithm = HASH_ALGORITHM_SHA_256;
2000 else if (!xmlStrcmp(string, BAD_CAST "SHA-384"))
2001 *algorithm = HASH_ALGORITHM_SHA_384;
2002 else if (!xmlStrcmp(string, BAD_CAST "SHA-512"))
2003 *algorithm = HASH_ALGORITHM_SHA_512;
2004 else
2005 return IE_ENUM;
2006 return IE_SUCCESS;
2010 #if HAVE_LIBCURL
2011 /* Convert UTF-8 @string representation of ISO 8601 date to @time.
2012 * XXX: Not all ISO formats are supported */
2013 static isds_error datestring2tm(const xmlChar *string, struct tm *time) {
2014 char *offset;
2015 if (!string || !time) return IE_INVAL;
2017 /* xsd:date is ISO 8601 string, thus ASCII */
2018 offset = strptime((char*)string, "%Y-%m-%d", time);
2019 if (offset && *offset == '\0')
2020 return IE_SUCCESS;
2022 offset = strptime((char*)string, "%Y%m%d", time);
2023 if (offset && *offset == '\0')
2024 return IE_SUCCESS;
2026 offset = strptime((char*)string, "%Y-%j", time);
2027 if (offset && *offset == '\0')
2028 return IE_SUCCESS;
2030 return IE_NOTSUP;
2034 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
2035 static isds_error tm2datestring(const struct tm *time, xmlChar **string) {
2036 if (!time || !string) return IE_INVAL;
2038 if (-1 == isds_asprintf((char **) string, "%d-%02d-%02d",
2039 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
2040 return IE_ERROR;
2042 return IE_SUCCESS;
2046 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
2047 * respects the @time microseconds too. */
2048 static isds_error timeval2timestring(const struct timeval *time,
2049 xmlChar **string) {
2050 struct tm broken;
2052 if (!time || !string) return IE_INVAL;
2054 if (!gmtime_r(&time->tv_sec, &broken)) return IE_DATE;
2055 if (time->tv_usec < 0 || time->tv_usec > 999999) return IE_DATE;
2057 /* TODO: small negative year should be formatted as "-0012". This is not
2058 * true for glibc "%04d". We should implement it.
2059 * TODO: What's type of time->tv_usec exactly? Unsigned? Absolute?
2060 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
2061 if (-1 == isds_asprintf((char **) string,
2062 "%04d-%02d-%02dT%02d:%02d:%02d.%06ld",
2063 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
2064 broken.tm_hour, broken.tm_min, broken.tm_sec,
2065 time->tv_usec))
2066 return IE_ERROR;
2068 return IE_SUCCESS;
2070 #endif /* HAVE_LIBCURL */
2073 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
2074 * It respects microseconds too.
2075 * In case of error, @time will be freed. */
2076 static isds_error timestring2timeval(const xmlChar *string,
2077 struct timeval **time) {
2078 struct tm broken;
2079 char *offset, *delim, *endptr;
2080 char subseconds[7];
2081 int offset_hours, offset_minutes;
2082 int i;
2084 if (!time) return IE_INVAL;
2085 if (!string) {
2086 zfree(*time);
2087 return IE_INVAL;
2090 memset(&broken, 0, sizeof(broken));
2092 if (!*time) {
2093 *time = calloc(1, sizeof(**time));
2094 if (!*time) return IE_NOMEM;
2095 } else {
2096 memset(*time, 0, sizeof(**time));
2100 /* xsd:date is ISO 8601 string, thus ASCII */
2101 /*TODO: negative year */
2103 /* Parse date and time without subseconds and offset */
2104 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
2105 if (!offset) {
2106 zfree(*time);
2107 return IE_DATE;
2110 /* Get subseconds */
2111 if (*offset == '.' ) {
2112 offset++;
2114 /* Copy first 6 digits, pad it with zeros.
2115 * XXX: It truncates longer number, no round.
2116 * Current server implementation uses only millisecond resolution. */
2117 /* TODO: isdigit() is locale sensitive */
2118 for (i = 0;
2119 i < sizeof(subseconds)/sizeof(char) - 1 && isdigit(*offset);
2120 i++, offset++) {
2121 subseconds[i] = *offset;
2123 for (; i < sizeof(subseconds)/sizeof(char) - 1; i++) {
2124 subseconds[i] = '0';
2126 subseconds[6] = '\0';
2128 /* Convert it into integer */
2129 (*time)->tv_usec = strtol(subseconds, &endptr, 10);
2130 if (*endptr != '\0' || (*time)->tv_usec == LONG_MIN ||
2131 (*time)->tv_usec == LONG_MAX) {
2132 zfree(*time);
2133 return IE_DATE;
2136 /* move to the zone offset delimiter or signal NULL*/
2137 delim = strchr(offset, '-');
2138 if (!delim)
2139 delim = strchr(offset, '+');
2140 if (!delim)
2141 delim = strchr(offset, 'Z');
2142 offset = delim;
2145 /* Get zone offset */
2146 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2147 * "" equals to "Z" and it means UTC zone. */
2148 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2149 * colon separator */
2150 if (offset && (*offset == '-' || *offset == '+')) {
2151 if (2 != sscanf(offset + 1, "%2d:%2d", &offset_hours, &offset_minutes)) {
2152 zfree(*time);
2153 return IE_DATE;
2155 if (*offset == '+') {
2156 broken.tm_hour -= offset_hours;
2157 broken.tm_min -= offset_minutes;
2158 } else {
2159 broken.tm_hour += offset_hours;
2160 broken.tm_min += offset_minutes;
2164 /* Convert to time_t */
2165 (*time)->tv_sec = _isds_timegm(&broken);
2166 if ((*time)->tv_sec == (time_t) -1) {
2167 zfree(*time);
2168 return IE_DATE;
2171 return IE_SUCCESS;
2175 /* Convert unsigned int into isds_message_status.
2176 * @context is session context
2177 * @number is pointer to number value. NULL will be treated as invalid value.
2178 * @status is automatically reallocated status
2179 * @return IE_SUCCESS, or error code and free status */
2180 static isds_error uint2isds_message_status(struct isds_ctx *context,
2181 const unsigned long int *number, isds_message_status **status) {
2182 if (!context) return IE_INVALID_CONTEXT;
2183 if (!status) return IE_INVAL;
2185 free(*status); *status = NULL;
2186 if (!number) return IE_INVAL;
2188 if (*number < 1 || *number > 10) {
2189 isds_printf_message(context, _("Invalid message status value: %lu"),
2190 *number);
2191 return IE_ENUM;
2194 *status = malloc(sizeof(**status));
2195 if (!*status) return IE_NOMEM;
2197 **status = 1 << *number;
2198 return IE_SUCCESS;
2202 /* Convert event description string into isds_event members type and
2203 * description
2204 * @string is raw event description starting with event prefix
2205 * @event is structure where to store type and stripped description to
2206 * @return standard error code, unknown prefix is not classified as an error.
2207 * */
2208 static isds_error eventstring2event(const xmlChar *string,
2209 struct isds_event* event) {
2210 const xmlChar *known_prefixes[] = {
2211 BAD_CAST "EV0:",
2212 BAD_CAST "EV1:",
2213 BAD_CAST "EV2:",
2214 BAD_CAST "EV3:",
2215 BAD_CAST "EV4:",
2216 BAD_CAST "EV5:",
2217 BAD_CAST "EV11:",
2218 BAD_CAST "EV12:",
2219 BAD_CAST "EV13:"
2221 const isds_event_type types[] = {
2222 EVENT_ENTERED_SYSTEM,
2223 EVENT_ACCEPTED_BY_RECIPIENT,
2224 EVENT_ACCEPTED_BY_FICTION,
2225 EVENT_UNDELIVERABLE,
2226 EVENT_COMMERCIAL_ACCEPTED,
2227 EVENT_DELIVERED,
2228 EVENT_PRIMARY_LOGIN,
2229 EVENT_ENTRUSTED_LOGIN,
2230 EVENT_SYSCERT_LOGIN
2232 unsigned int index;
2233 size_t length;
2235 if (!string || !event) return IE_INVAL;
2237 if (!event->type) {
2238 event->type = malloc(sizeof(*event->type));
2239 if (!(event->type)) return IE_NOMEM;
2241 zfree(event->description);
2243 for (index = 0; index < sizeof(known_prefixes)/sizeof(known_prefixes[0]);
2244 index++) {
2245 length = xmlUTF8Strlen(known_prefixes[index]);
2247 if (!xmlStrncmp(string, known_prefixes[index], length)) {
2248 /* Prefix is known */
2249 *event->type = types[index];
2251 /* Strip prefix from description and spaces */
2252 /* TODO: Recognize all white spaces from UCS blank class and
2253 * operate on UTF-8 chars. */
2254 for (; string[length] != '\0' && string[length] == ' '; length++);
2255 event->description = strdup((char *) (string + length));
2256 if (!(event->description)) return IE_NOMEM;
2258 return IE_SUCCESS;
2262 /* Unknown event prefix.
2263 * XSD allows any string */
2264 char *string_locale = _isds_utf82locale((char *) string);
2265 isds_log(ILF_ISDS, ILL_WARNING,
2266 _("Unknown delivery info event prefix: %s\n"), string_locale);
2267 free(string_locale);
2269 *event->type = EVENT_UKNOWN;
2270 event->description = strdup((char *) string);
2271 if (!(event->description)) return IE_NOMEM;
2273 return IE_SUCCESS;
2277 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2278 * and leave label */
2279 #define EXTRACT_STRING(element, string) { \
2280 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2281 if (!result) { \
2282 err = IE_ERROR; \
2283 goto leave; \
2285 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2286 if (result->nodesetval->nodeNr > 1) { \
2287 isds_printf_message(context, _("Multiple %s element"), element); \
2288 err = IE_ERROR; \
2289 goto leave; \
2291 (string) = (char *) \
2292 xmlXPathCastNodeSetToString(result->nodesetval); \
2293 if (!(string)) { \
2294 err = IE_ERROR; \
2295 goto leave; \
2300 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2302 char *string = NULL; \
2303 EXTRACT_STRING(element, string); \
2305 if (string) { \
2306 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2307 if (!(booleanPtr)) { \
2308 free(string); \
2309 err = IE_NOMEM; \
2310 goto leave; \
2313 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2314 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2315 *(booleanPtr) = 1; \
2316 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2317 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2318 *(booleanPtr) = 0; \
2319 else { \
2320 char *string_locale = _isds_utf82locale((char*)string); \
2321 isds_printf_message(context, \
2322 _("%s value is not valid boolean: %s"), \
2323 element, string_locale); \
2324 free(string_locale); \
2325 free(string); \
2326 err = IE_ERROR; \
2327 goto leave; \
2330 free(string); \
2334 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2336 char *string = NULL; \
2337 EXTRACT_STRING(element, string); \
2338 if (string) { \
2339 long int number; \
2340 char *endptr; \
2342 number = strtol((char*)string, &endptr, 10); \
2344 if (*endptr != '\0') { \
2345 char *string_locale = _isds_utf82locale((char *)string); \
2346 isds_printf_message(context, \
2347 _("%s is not valid integer: %s"), \
2348 element, string_locale); \
2349 free(string_locale); \
2350 free(string); \
2351 err = IE_ISDS; \
2352 goto leave; \
2355 if (number == LONG_MIN || number == LONG_MAX) { \
2356 char *string_locale = _isds_utf82locale((char *)string); \
2357 isds_printf_message(context, \
2358 _("%s value out of range of long int: %s"), \
2359 element, string_locale); \
2360 free(string_locale); \
2361 free(string); \
2362 err = IE_ERROR; \
2363 goto leave; \
2366 free(string); string = NULL; \
2368 if (!(preallocated)) { \
2369 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2370 if (!(longintPtr)) { \
2371 err = IE_NOMEM; \
2372 goto leave; \
2375 *(longintPtr) = number; \
2379 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2381 char *string = NULL; \
2382 EXTRACT_STRING(element, string); \
2383 if (string) { \
2384 long int number; \
2385 char *endptr; \
2387 number = strtol((char*)string, &endptr, 10); \
2389 if (*endptr != '\0') { \
2390 char *string_locale = _isds_utf82locale((char *)string); \
2391 isds_printf_message(context, \
2392 _("%s is not valid integer: %s"), \
2393 element, string_locale); \
2394 free(string_locale); \
2395 free(string); \
2396 err = IE_ISDS; \
2397 goto leave; \
2400 if (number == LONG_MIN || number == LONG_MAX) { \
2401 char *string_locale = _isds_utf82locale((char *)string); \
2402 isds_printf_message(context, \
2403 _("%s value out of range of long int: %s"), \
2404 element, string_locale); \
2405 free(string_locale); \
2406 free(string); \
2407 err = IE_ERROR; \
2408 goto leave; \
2411 free(string); string = NULL; \
2412 if (number < 0) { \
2413 isds_printf_message(context, \
2414 _("%s value is negative: %ld"), element, number); \
2415 err = IE_ERROR; \
2416 goto leave; \
2419 if (!(preallocated)) { \
2420 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2421 if (!(ulongintPtr)) { \
2422 err = IE_NOMEM; \
2423 goto leave; \
2426 *(ulongintPtr) = number; \
2430 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2431 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2432 NULL); \
2433 if ((required) && (!string)) { \
2434 char *attribute_locale = _isds_utf82locale(attribute); \
2435 char *element_locale = \
2436 _isds_utf82locale((char *)xpath_ctx->node->name); \
2437 isds_printf_message(context, \
2438 _("Could not extract required %s attribute value from " \
2439 "%s element"), attribute_locale, element_locale); \
2440 free(element_locale); \
2441 free(attribute_locale); \
2442 err = IE_ERROR; \
2443 goto leave; \
2448 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2450 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2451 (xmlChar *) (string)); \
2452 if (!node) { \
2453 isds_printf_message(context, \
2454 _("Could not add %s child to %s element"), \
2455 element, (parent)->name); \
2456 err = IE_ERROR; \
2457 goto leave; \
2461 #define INSERT_STRING(parent, element, string) \
2462 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2464 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2466 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2467 else { INSERT_STRING(parent, element, "false"); } \
2470 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2472 if (booleanPtr) { \
2473 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2474 } else { \
2475 INSERT_STRING(parent, element, NULL); \
2479 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2480 if ((longintPtr)) { \
2481 /* FIXME: locale sensitive */ \
2482 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2483 err = IE_NOMEM; \
2484 goto leave; \
2486 INSERT_STRING(parent, element, buffer) \
2487 free(buffer); (buffer) = NULL; \
2488 } else { INSERT_STRING(parent, element, NULL) } \
2491 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2492 if ((ulongintPtr)) { \
2493 /* FIXME: locale sensitive */ \
2494 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2495 err = IE_NOMEM; \
2496 goto leave; \
2498 INSERT_STRING(parent, element, buffer) \
2499 free(buffer); (buffer) = NULL; \
2500 } else { INSERT_STRING(parent, element, NULL) } \
2503 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2505 /* FIXME: locale sensitive */ \
2506 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2507 err = IE_NOMEM; \
2508 goto leave; \
2510 INSERT_STRING(parent, element, buffer) \
2511 free(buffer); (buffer) = NULL; \
2514 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2515 * new attribute. */
2516 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2518 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2519 (xmlChar *) (string)); \
2520 if (!attribute_node) { \
2521 isds_printf_message(context, _("Could not add %s " \
2522 "attribute to %s element"), \
2523 (attribute), (parent)->name); \
2524 err = IE_ERROR; \
2525 goto leave; \
2529 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2530 if (string) { \
2531 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2532 if (length > (maximum)) { \
2533 isds_printf_message(context, \
2534 ngettext("%s has more than %d characters", \
2535 "%s has more than %d characters", (maximum)), \
2536 (name), (maximum)); \
2537 err = IE_2BIG; \
2538 goto leave; \
2540 if (length < (minimum)) { \
2541 isds_printf_message(context, \
2542 ngettext("%s has less than %d characters", \
2543 "%s has less than %d characters", (minimum)), \
2544 (name), (minimum)); \
2545 err = IE_2SMALL; \
2546 goto leave; \
2551 #define INSERT_ELEMENT(child, parent, element) \
2553 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2554 if (!(child)) { \
2555 isds_printf_message(context, \
2556 _("Could not add %s child to %s element"), \
2557 (element), (parent)->name); \
2558 err = IE_ERROR; \
2559 goto leave; \
2564 /* Find child element by name in given XPath context and switch context onto
2565 * it. The child must be uniq and must exist. Otherwise fails.
2566 * @context is ISDS context
2567 * @child is child element name
2568 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2569 * into it child. In error case, the @xpath_ctx keeps original value. */
2570 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
2571 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
2572 isds_error err = IE_SUCCESS;
2573 xmlXPathObjectPtr result = NULL;
2575 if (!context) return IE_INVALID_CONTEXT;
2576 if (!child || !xpath_ctx) return IE_INVAL;
2578 /* Find child */
2579 result = xmlXPathEvalExpression(child, xpath_ctx);
2580 if (!result) {
2581 err = IE_XML;
2582 goto leave;
2585 /* No match */
2586 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2587 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2588 char *child_locale = _isds_utf82locale((char*) child);
2589 isds_printf_message(context,
2590 _("%s element does not contain %s child"),
2591 parent_locale, child_locale);
2592 free(child_locale);
2593 free(parent_locale);
2594 err = IE_NOEXIST;
2595 goto leave;
2598 /* More matches */
2599 if (result->nodesetval->nodeNr > 1) {
2600 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2601 char *child_locale = _isds_utf82locale((char*) child);
2602 isds_printf_message(context,
2603 _("%s element contains multiple %s children"),
2604 parent_locale, child_locale);
2605 free(child_locale);
2606 free(parent_locale);
2607 err = IE_NOTUNIQ;
2608 goto leave;
2611 /* Switch context */
2612 xpath_ctx->node = result->nodesetval->nodeTab[0];
2614 leave:
2615 xmlXPathFreeObject(result);
2616 return err;
2621 #if HAVE_LIBCURL
2622 /* Find and convert XSD:gPersonName group in current node into structure
2623 * @context is ISDS context
2624 * @personName is automatically reallocated person name structure. If no member
2625 * value is found, will be freed.
2626 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2627 * elements
2628 * In case of error @personName will be freed. */
2629 static isds_error extract_gPersonName(struct isds_ctx *context,
2630 struct isds_PersonName **personName, xmlXPathContextPtr xpath_ctx) {
2631 isds_error err = IE_SUCCESS;
2632 xmlXPathObjectPtr result = NULL;
2634 if (!context) return IE_INVALID_CONTEXT;
2635 if (!personName) return IE_INVAL;
2636 isds_PersonName_free(personName);
2637 if (!xpath_ctx) return IE_INVAL;
2640 *personName = calloc(1, sizeof(**personName));
2641 if (!*personName) {
2642 err = IE_NOMEM;
2643 goto leave;
2646 EXTRACT_STRING("isds:pnFirstName", (*personName)->pnFirstName);
2647 EXTRACT_STRING("isds:pnMiddleName", (*personName)->pnMiddleName);
2648 EXTRACT_STRING("isds:pnLastName", (*personName)->pnLastName);
2649 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName)->pnLastNameAtBirth);
2651 if (!(*personName)->pnFirstName && !(*personName)->pnMiddleName &&
2652 !(*personName)->pnLastName && !(*personName)->pnLastNameAtBirth)
2653 isds_PersonName_free(personName);
2655 leave:
2656 if (err) isds_PersonName_free(personName);
2657 xmlXPathFreeObject(result);
2658 return err;
2662 /* Find and convert XSD:gAddress group in current node into structure
2663 * @context is ISDS context
2664 * @address is automatically reallocated address structure. If no member
2665 * value is found, will be freed.
2666 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2667 * elements
2668 * In case of error @address will be freed. */
2669 static isds_error extract_gAddress(struct isds_ctx *context,
2670 struct isds_Address **address, xmlXPathContextPtr xpath_ctx) {
2671 isds_error err = IE_SUCCESS;
2672 xmlXPathObjectPtr result = NULL;
2674 if (!context) return IE_INVALID_CONTEXT;
2675 if (!address) return IE_INVAL;
2676 isds_Address_free(address);
2677 if (!xpath_ctx) return IE_INVAL;
2680 *address = calloc(1, sizeof(**address));
2681 if (!*address) {
2682 err = IE_NOMEM;
2683 goto leave;
2686 EXTRACT_STRING("isds:adCity", (*address)->adCity);
2687 EXTRACT_STRING("isds:adStreet", (*address)->adStreet);
2688 EXTRACT_STRING("isds:adNumberInStreet", (*address)->adNumberInStreet);
2689 EXTRACT_STRING("isds:adNumberInMunicipality",
2690 (*address)->adNumberInMunicipality);
2691 EXTRACT_STRING("isds:adZipCode", (*address)->adZipCode);
2692 EXTRACT_STRING("isds:adState", (*address)->adState);
2694 if (!(*address)->adCity && !(*address)->adStreet &&
2695 !(*address)->adNumberInStreet &&
2696 !(*address)->adNumberInMunicipality &&
2697 !(*address)->adZipCode && !(*address)->adState)
2698 isds_Address_free(address);
2700 leave:
2701 if (err) isds_Address_free(address);
2702 xmlXPathFreeObject(result);
2703 return err;
2707 /* Find and convert isds:biDate element in current node into structure
2708 * @context is ISDS context
2709 * @biDate is automatically reallocated birth date structure. If no member
2710 * value is found, will be freed.
2711 * @xpath_ctx is XPath context with current node as parent for isds:biDate
2712 * element
2713 * In case of error @biDate will be freed. */
2714 static isds_error extract_BiDate(struct isds_ctx *context,
2715 struct tm **biDate, xmlXPathContextPtr xpath_ctx) {
2716 isds_error err = IE_SUCCESS;
2717 xmlXPathObjectPtr result = NULL;
2718 char *string = NULL;
2720 if (!context) return IE_INVALID_CONTEXT;
2721 if (!biDate) return IE_INVAL;
2722 zfree(*biDate);
2723 if (!xpath_ctx) return IE_INVAL;
2725 EXTRACT_STRING("isds:biDate", string);
2726 if (string) {
2727 *biDate = calloc(1, sizeof(**biDate));
2728 if (!*biDate) {
2729 err = IE_NOMEM;
2730 goto leave;
2732 err = datestring2tm((xmlChar *)string, *biDate);
2733 if (err) {
2734 if (err == IE_NOTSUP) {
2735 err = IE_ISDS;
2736 char *string_locale = _isds_utf82locale(string);
2737 isds_printf_message(context,
2738 _("Invalid isds:biDate value: %s"), string_locale);
2739 free(string_locale);
2741 goto leave;
2745 leave:
2746 if (err) zfree(*biDate);
2747 free(string);
2748 xmlXPathFreeObject(result);
2749 return err;
2753 /* Convert isds:dBOwnerInfo XML tree into structure
2754 * @context is ISDS context
2755 * @db_owner_info is automatically reallocated box owner info structure
2756 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
2757 * In case of error @db_owner_info will be freed. */
2758 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
2759 struct isds_DbOwnerInfo **db_owner_info,
2760 xmlXPathContextPtr xpath_ctx) {
2761 isds_error err = IE_SUCCESS;
2762 xmlXPathObjectPtr result = NULL;
2763 char *string = NULL;
2765 if (!context) return IE_INVALID_CONTEXT;
2766 if (!db_owner_info) return IE_INVAL;
2767 isds_DbOwnerInfo_free(db_owner_info);
2768 if (!xpath_ctx) return IE_INVAL;
2771 *db_owner_info = calloc(1, sizeof(**db_owner_info));
2772 if (!*db_owner_info) {
2773 err = IE_NOMEM;
2774 goto leave;
2777 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
2779 EXTRACT_STRING("isds:dbType", string);
2780 if (string) {
2781 (*db_owner_info)->dbType =
2782 calloc(1, sizeof(*((*db_owner_info)->dbType)));
2783 if (!(*db_owner_info)->dbType) {
2784 err = IE_NOMEM;
2785 goto leave;
2787 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
2788 if (err) {
2789 zfree((*db_owner_info)->dbType);
2790 if (err == IE_ENUM) {
2791 err = IE_ISDS;
2792 char *string_locale = _isds_utf82locale(string);
2793 isds_printf_message(context, _("Unknown isds:dbType: %s"),
2794 string_locale);
2795 free(string_locale);
2797 goto leave;
2799 zfree(string);
2802 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
2804 err = extract_gPersonName(context, &(*db_owner_info)->personName,
2805 xpath_ctx);
2806 if (err) goto leave;
2808 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
2810 (*db_owner_info)->birthInfo =
2811 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
2812 if (!(*db_owner_info)->birthInfo) {
2813 err = IE_NOMEM;
2814 goto leave;
2816 err = extract_BiDate(context, &(*db_owner_info)->birthInfo->biDate,
2817 xpath_ctx);
2818 if (err) goto leave;
2819 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
2820 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
2821 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
2822 if (!(*db_owner_info)->birthInfo->biDate &&
2823 !(*db_owner_info)->birthInfo->biCity &&
2824 !(*db_owner_info)->birthInfo->biCounty &&
2825 !(*db_owner_info)->birthInfo->biState)
2826 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
2828 err = extract_gAddress(context, &(*db_owner_info)->address, xpath_ctx);
2829 if (err) goto leave;
2831 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
2832 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
2833 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
2834 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
2835 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
2837 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
2839 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
2840 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
2841 (*db_owner_info)->dbOpenAddressing);
2843 leave:
2844 if (err) isds_DbOwnerInfo_free(db_owner_info);
2845 free(string);
2846 xmlXPathFreeObject(result);
2847 return err;
2851 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
2852 * @context is session context
2853 * @owner is libisds structure with box description
2854 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
2855 static isds_error insert_DbOwnerInfo(struct isds_ctx *context,
2856 const struct isds_DbOwnerInfo *owner, xmlNodePtr db_owner_info) {
2858 isds_error err = IE_SUCCESS;
2859 xmlNodePtr node;
2860 xmlChar *string = NULL;
2862 if (!context) return IE_INVALID_CONTEXT;
2863 if (!owner || !db_owner_info) return IE_INVAL;
2866 /* Build XSD:tDbOwnerInfo */
2867 CHECK_FOR_STRING_LENGTH(owner->dbID, 0, 7, "dbID")
2868 INSERT_STRING(db_owner_info, "dbID", owner->dbID);
2870 /* dbType */
2871 if (owner->dbType) {
2872 const xmlChar *type_string = isds_DbType2string(*(owner->dbType));
2873 if (!type_string) {
2874 isds_printf_message(context, _("Invalid dbType value: %d"),
2875 *(owner->dbType));
2876 err = IE_ENUM;
2877 goto leave;
2879 INSERT_STRING(db_owner_info, "dbType", type_string);
2881 INSERT_STRING(db_owner_info, "ic", owner->ic);
2882 if (owner->personName) {
2883 INSERT_STRING(db_owner_info, "pnFirstName",
2884 owner->personName->pnFirstName);
2885 INSERT_STRING(db_owner_info, "pnMiddleName",
2886 owner->personName->pnMiddleName);
2887 INSERT_STRING(db_owner_info, "pnLastName",
2888 owner->personName->pnLastName);
2889 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
2890 owner->personName->pnLastNameAtBirth);
2892 INSERT_STRING(db_owner_info, "firmName", owner->firmName);
2893 if (owner->birthInfo) {
2894 if (owner->birthInfo->biDate) {
2895 if (!tm2datestring(owner->birthInfo->biDate, &string))
2896 INSERT_STRING(db_owner_info, "biDate", string);
2897 free(string); string = NULL;
2899 INSERT_STRING(db_owner_info, "biCity", owner->birthInfo->biCity);
2900 INSERT_STRING(db_owner_info, "biCounty", owner->birthInfo->biCounty);
2901 INSERT_STRING(db_owner_info, "biState", owner->birthInfo->biState);
2903 if (owner->address) {
2904 INSERT_STRING(db_owner_info, "adCity", owner->address->adCity);
2905 INSERT_STRING(db_owner_info, "adStreet", owner->address->adStreet);
2906 INSERT_STRING(db_owner_info, "adNumberInStreet",
2907 owner->address->adNumberInStreet);
2908 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
2909 owner->address->adNumberInMunicipality);
2910 INSERT_STRING(db_owner_info, "adZipCode", owner->address->adZipCode);
2911 INSERT_STRING(db_owner_info, "adState", owner->address->adState);
2913 INSERT_STRING(db_owner_info, "nationality", owner->nationality);
2914 INSERT_STRING(db_owner_info, "email", owner->email);
2915 INSERT_STRING(db_owner_info, "telNumber", owner->telNumber);
2917 CHECK_FOR_STRING_LENGTH(owner->identifier, 0, 20, "identifier")
2918 INSERT_STRING(db_owner_info, "identifier", owner->identifier);
2920 CHECK_FOR_STRING_LENGTH(owner->registryCode, 0, 5, "registryCode")
2921 INSERT_STRING(db_owner_info, "registryCode", owner->registryCode);
2923 INSERT_LONGINT(db_owner_info, "dbState", owner->dbState, string);
2925 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", owner->dbEffectiveOVM);
2926 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
2927 owner->dbOpenAddressing);
2929 leave:
2930 free(string);
2931 return err;
2935 /* Convert XSD:tDbUserInfo XML tree into structure
2936 * @context is ISDS context
2937 * @db_user_info is automatically reallocated user info structure
2938 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
2939 * In case of error @db_user_info will be freed. */
2940 static isds_error extract_DbUserInfo(struct isds_ctx *context,
2941 struct isds_DbUserInfo **db_user_info, xmlXPathContextPtr xpath_ctx) {
2942 isds_error err = IE_SUCCESS;
2943 xmlXPathObjectPtr result = NULL;
2944 char *string = NULL;
2946 if (!context) return IE_INVALID_CONTEXT;
2947 if (!db_user_info) return IE_INVAL;
2948 isds_DbUserInfo_free(db_user_info);
2949 if (!xpath_ctx) return IE_INVAL;
2952 *db_user_info = calloc(1, sizeof(**db_user_info));
2953 if (!*db_user_info) {
2954 err = IE_NOMEM;
2955 goto leave;
2958 EXTRACT_STRING("isds:userID", (*db_user_info)->userID);
2960 EXTRACT_STRING("isds:userType", string);
2961 if (string) {
2962 (*db_user_info)->userType =
2963 calloc(1, sizeof(*((*db_user_info)->userType)));
2964 if (!(*db_user_info)->userType) {
2965 err = IE_NOMEM;
2966 goto leave;
2968 err = string2isds_UserType((xmlChar *)string,
2969 (*db_user_info)->userType);
2970 if (err) {
2971 zfree((*db_user_info)->userType);
2972 if (err == IE_ENUM) {
2973 err = IE_ISDS;
2974 char *string_locale = _isds_utf82locale(string);
2975 isds_printf_message(context,
2976 _("Unknown isds:userType value: %s"), string_locale);
2977 free(string_locale);
2979 goto leave;
2981 zfree(string);
2984 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info)->userPrivils, 0);
2986 (*db_user_info)->personName =
2987 calloc(1, sizeof(*((*db_user_info)->personName)));
2988 if (!(*db_user_info)->personName) {
2989 err = IE_NOMEM;
2990 goto leave;
2993 err = extract_gPersonName(context, &(*db_user_info)->personName,
2994 xpath_ctx);
2995 if (err) goto leave;
2997 err = extract_gAddress(context, &(*db_user_info)->address, xpath_ctx);
2998 if (err) goto leave;
3000 err = extract_BiDate(context, &(*db_user_info)->biDate, xpath_ctx);
3001 if (err) goto leave;
3003 EXTRACT_STRING("isds:ic", (*db_user_info)->ic);
3004 EXTRACT_STRING("isds:firmName", (*db_user_info)->firmName);
3006 EXTRACT_STRING("isds:caStreet", (*db_user_info)->caStreet);
3007 EXTRACT_STRING("isds:caCity", (*db_user_info)->caCity);
3008 EXTRACT_STRING("isds:caZipCode", (*db_user_info)->caZipCode);
3010 /* ???: Default value is "CZ" according specification. Should we provide
3011 * it? */
3012 EXTRACT_STRING("isds:caState", (*db_user_info)->caState);
3014 leave:
3015 if (err) isds_DbUserInfo_free(db_user_info);
3016 free(string);
3017 xmlXPathFreeObject(result);
3018 return err;
3022 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3023 * @context is session context
3024 * @user is libisds structure with user description
3025 * @db_user_info is XML element of XSD:tDbUserInfo */
3026 static isds_error insert_DbUserInfo(struct isds_ctx *context,
3027 const struct isds_DbUserInfo *user, xmlNodePtr db_user_info) {
3029 isds_error err = IE_SUCCESS;
3030 xmlNodePtr node;
3031 xmlChar *string = NULL;
3033 if (!context) return IE_INVALID_CONTEXT;
3034 if (!user || !db_user_info) return IE_INVAL;
3036 /* Build XSD:tDbUserInfo */
3037 if (user->personName) {
3038 INSERT_STRING(db_user_info, "pnFirstName",
3039 user->personName->pnFirstName);
3040 INSERT_STRING(db_user_info, "pnMiddleName",
3041 user->personName->pnMiddleName);
3042 INSERT_STRING(db_user_info, "pnLastName",
3043 user->personName->pnLastName);
3044 INSERT_STRING(db_user_info, "pnLastNameAtBirth",
3045 user->personName->pnLastNameAtBirth);
3047 if (user->address) {
3048 INSERT_STRING(db_user_info, "adCity", user->address->adCity);
3049 INSERT_STRING(db_user_info, "adStreet", user->address->adStreet);
3050 INSERT_STRING(db_user_info, "adNumberInStreet",
3051 user->address->adNumberInStreet);
3052 INSERT_STRING(db_user_info, "adNumberInMunicipality",
3053 user->address->adNumberInMunicipality);
3054 INSERT_STRING(db_user_info, "adZipCode", user->address->adZipCode);
3055 INSERT_STRING(db_user_info, "adState", user->address->adState);
3057 if (user->biDate) {
3058 if (!tm2datestring(user->biDate, &string))
3059 INSERT_STRING(db_user_info, "biDate", string);
3060 zfree(string);
3062 CHECK_FOR_STRING_LENGTH(user->userID, 6, 12, "userID");
3063 INSERT_STRING(db_user_info, "userID", user->userID);
3065 /* userType */
3066 if (user->userType) {
3067 const xmlChar *type_string = isds_UserType2string(*(user->userType));
3068 if (!type_string) {
3069 isds_printf_message(context, _("Invalid userType value: %d"),
3070 *(user->userType));
3071 err = IE_ENUM;
3072 goto leave;
3074 INSERT_STRING(db_user_info, "userType", type_string);
3077 INSERT_LONGINT(db_user_info, "userPrivils", user->userPrivils, string);
3078 CHECK_FOR_STRING_LENGTH(user->ic, 0, 8, "ic")
3079 INSERT_STRING(db_user_info, "ic", user->ic);
3080 CHECK_FOR_STRING_LENGTH(user->firmName, 0, 100, "firmName")
3081 INSERT_STRING(db_user_info, "firmName", user->firmName);
3082 INSERT_STRING(db_user_info, "caStreet", user->caStreet);
3083 INSERT_STRING(db_user_info, "caCity", user->caCity);
3084 INSERT_STRING(db_user_info, "caZipCode", user->caZipCode);
3085 INSERT_STRING(db_user_info, "caState", user->caState);
3087 leave:
3088 free(string);
3089 return err;
3091 #endif /* HAVE_LIBCURL */
3094 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3095 * isds_envelope structure. The envelope is automatically allocated but not
3096 * reallocated. The date are just appended into envelope structure.
3097 * @context is ISDS context
3098 * @envelope is automatically allocated message envelope structure
3099 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3100 * In case of error @envelope will be freed. */
3101 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
3102 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3103 isds_error err = IE_SUCCESS;
3104 xmlXPathObjectPtr result = NULL;
3106 if (!context) return IE_INVALID_CONTEXT;
3107 if (!envelope) return IE_INVAL;
3108 if (!xpath_ctx) return IE_INVAL;
3111 if (!*envelope) {
3112 /* Allocate envelope */
3113 *envelope = calloc(1, sizeof(**envelope));
3114 if (!*envelope) {
3115 err = IE_NOMEM;
3116 goto leave;
3118 } else {
3119 /* Else free former data */
3120 zfree((*envelope)->dmSenderOrgUnit);
3121 zfree((*envelope)->dmSenderOrgUnitNum);
3122 zfree((*envelope)->dbIDRecipient);
3123 zfree((*envelope)->dmRecipientOrgUnit);
3124 zfree((*envelope)->dmSenderOrgUnitNum);
3125 zfree((*envelope)->dmToHands);
3126 zfree((*envelope)->dmAnnotation);
3127 zfree((*envelope)->dmRecipientRefNumber);
3128 zfree((*envelope)->dmSenderRefNumber);
3129 zfree((*envelope)->dmRecipientIdent);
3130 zfree((*envelope)->dmSenderIdent);
3131 zfree((*envelope)->dmLegalTitleLaw);
3132 zfree((*envelope)->dmLegalTitleYear);
3133 zfree((*envelope)->dmLegalTitleSect);
3134 zfree((*envelope)->dmLegalTitlePar);
3135 zfree((*envelope)->dmLegalTitlePoint);
3136 zfree((*envelope)->dmPersonalDelivery);
3137 zfree((*envelope)->dmAllowSubstDelivery);
3140 /* Extract envelope elements added by sender or ISDS
3141 * (XSD: gMessageEnvelopeSub type) */
3142 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
3143 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3144 (*envelope)->dmSenderOrgUnitNum, 0);
3145 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
3146 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
3147 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3148 (*envelope)->dmSenderOrgUnitNum, 0);
3149 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
3150 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
3151 EXTRACT_STRING("isds:dmRecipientRefNumber",
3152 (*envelope)->dmRecipientRefNumber);
3153 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
3154 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
3155 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
3157 /* Extract envelope elements regarding law reference */
3158 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
3159 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
3160 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
3161 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
3162 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
3164 /* Extract envelope other elements */
3165 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
3166 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3167 (*envelope)->dmAllowSubstDelivery);
3169 leave:
3170 if (err) isds_envelope_free(envelope);
3171 xmlXPathFreeObject(result);
3172 return err;
3177 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3178 * isds_envelope structure. The envelope is automatically allocated but not
3179 * reallocated. The date are just appended into envelope structure.
3180 * @context is ISDS context
3181 * @envelope is automatically allocated message envelope structure
3182 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3183 * In case of error @envelope will be freed. */
3184 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
3185 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3186 isds_error err = IE_SUCCESS;
3187 xmlXPathObjectPtr result = NULL;
3189 if (!context) return IE_INVALID_CONTEXT;
3190 if (!envelope) return IE_INVAL;
3191 if (!xpath_ctx) return IE_INVAL;
3194 if (!*envelope) {
3195 /* Allocate envelope */
3196 *envelope = calloc(1, sizeof(**envelope));
3197 if (!*envelope) {
3198 err = IE_NOMEM;
3199 goto leave;
3201 } else {
3202 /* Else free former data */
3203 zfree((*envelope)->dmID);
3204 zfree((*envelope)->dbIDSender);
3205 zfree((*envelope)->dmSender);
3206 zfree((*envelope)->dmSenderAddress);
3207 zfree((*envelope)->dmSenderType);
3208 zfree((*envelope)->dmRecipient);
3209 zfree((*envelope)->dmRecipientAddress);
3210 zfree((*envelope)->dmAmbiguousRecipient);
3213 /* Extract envelope elements added by ISDS
3214 * (XSD: gMessageEnvelope type) */
3215 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
3216 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
3217 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
3218 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
3219 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3220 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
3221 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
3222 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
3223 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3224 (*envelope)->dmAmbiguousRecipient);
3226 /* Extract envelope elements added by sender and ISDS
3227 * (XSD: gMessageEnvelope type) */
3228 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
3229 if (err) goto leave;
3231 leave:
3232 if (err) isds_envelope_free(envelope);
3233 xmlXPathFreeObject(result);
3234 return err;
3238 /* Convert other envelope elements from XML tree into isds_envelope structure:
3239 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3240 * The envelope is automatically allocated but not reallocated.
3241 * The data are just appended into envelope structure.
3242 * @context is ISDS context
3243 * @envelope is automatically allocated message envelope structure
3244 * @xpath_ctx is XPath context with current node as parent desired elements
3245 * In case of error @envelope will be freed. */
3246 static isds_error append_status_size_times(struct isds_ctx *context,
3247 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3248 isds_error err = IE_SUCCESS;
3249 xmlXPathObjectPtr result = NULL;
3250 char *string = NULL;
3251 unsigned long int *unumber = NULL;
3253 if (!context) return IE_INVALID_CONTEXT;
3254 if (!envelope) return IE_INVAL;
3255 if (!xpath_ctx) return IE_INVAL;
3258 if (!*envelope) {
3259 /* Allocate new */
3260 *envelope = calloc(1, sizeof(**envelope));
3261 if (!*envelope) {
3262 err = IE_NOMEM;
3263 goto leave;
3265 } else {
3266 /* Free old data */
3267 zfree((*envelope)->dmMessageStatus);
3268 zfree((*envelope)->dmAttachmentSize);
3269 zfree((*envelope)->dmDeliveryTime);
3270 zfree((*envelope)->dmAcceptanceTime);
3274 /* dmMessageStatus element is mandatory */
3275 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
3276 if (!unumber) {
3277 isds_log_message(context,
3278 _("Missing mandatory sisds:dmMessageStatus integer"));
3279 err = IE_ISDS;
3280 goto leave;
3282 err = uint2isds_message_status(context, unumber,
3283 &((*envelope)->dmMessageStatus));
3284 if (err) {
3285 if (err == IE_ENUM) err = IE_ISDS;
3286 goto leave;
3288 free(unumber); unumber = NULL;
3290 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
3293 EXTRACT_STRING("sisds:dmDeliveryTime", string);
3294 if (string) {
3295 err = timestring2timeval((xmlChar *) string,
3296 &((*envelope)->dmDeliveryTime));
3297 if (err) {
3298 char *string_locale = _isds_utf82locale(string);
3299 if (err == IE_DATE) err = IE_ISDS;
3300 isds_printf_message(context,
3301 _("Could not convert dmDeliveryTime as ISO time: %s"),
3302 string_locale);
3303 free(string_locale);
3304 goto leave;
3306 zfree(string);
3309 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
3310 if (string) {
3311 err = timestring2timeval((xmlChar *) string,
3312 &((*envelope)->dmAcceptanceTime));
3313 if (err) {
3314 char *string_locale = _isds_utf82locale(string);
3315 if (err == IE_DATE) err = IE_ISDS;
3316 isds_printf_message(context,
3317 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3318 string_locale);
3319 free(string_locale);
3320 goto leave;
3322 zfree(string);
3325 leave:
3326 if (err) isds_envelope_free(envelope);
3327 free(unumber);
3328 free(string);
3329 xmlXPathFreeObject(result);
3330 return err;
3334 /* Convert message type attribute of current element into isds_envelope
3335 * structure.
3336 * TODO: This function can be incorporated into append_status_size_times() as
3337 * they are called always together.
3338 * The envelope is automatically allocated but not reallocated.
3339 * The data are just appended into envelope structure.
3340 * @context is ISDS context
3341 * @envelope is automatically allocated message envelope structure
3342 * @xpath_ctx is XPath context with current node as parent of attribute
3343 * carrying message type
3344 * In case of error @envelope will be freed. */
3345 static isds_error append_message_type(struct isds_ctx *context,
3346 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3347 isds_error err = IE_SUCCESS;
3349 if (!context) return IE_INVALID_CONTEXT;
3350 if (!envelope) return IE_INVAL;
3351 if (!xpath_ctx) return IE_INVAL;
3354 if (!*envelope) {
3355 /* Allocate new */
3356 *envelope = calloc(1, sizeof(**envelope));
3357 if (!*envelope) {
3358 err = IE_NOMEM;
3359 goto leave;
3361 } else {
3362 /* Free old data */
3363 zfree((*envelope)->dmType);
3367 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope)->dmType, 0);
3369 if (!(*envelope)->dmType) {
3370 /* Use default value */
3371 (*envelope)->dmType = strdup("V");
3372 if (!(*envelope)->dmType) {
3373 err = IE_NOMEM;
3374 goto leave;
3376 } else if (1 != xmlUTF8Strlen((xmlChar *) (*envelope)->dmType)) {
3377 char *type_locale = _isds_utf82locale((*envelope)->dmType);
3378 isds_printf_message(context,
3379 _("Message type in dmType attribute is not 1 character long: "
3380 "%s"),
3381 type_locale);
3382 free(type_locale);
3383 err = IE_ISDS;
3384 goto leave;
3387 leave:
3388 if (err) isds_envelope_free(envelope);
3389 return err;
3393 #if HAVE_LIBCURL
3394 /* Convert dmType isds_envelope member into XML attribute and append it to
3395 * current node.
3396 * @context is ISDS context
3397 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3398 * @dm_envelope is XML element the resulting attribute will be appended to.
3399 * @return error code, in case of error context' message is filled. */
3400 static isds_error insert_message_type(struct isds_ctx *context,
3401 const char *type, xmlNodePtr dm_envelope) {
3402 isds_error err = IE_SUCCESS;
3403 xmlAttrPtr attribute_node;
3405 if (!context) return IE_INVALID_CONTEXT;
3406 if (!dm_envelope) return IE_INVAL;
3408 /* Insert optional message type */
3409 if (type) {
3410 if (1 != xmlUTF8Strlen((xmlChar *) type)) {
3411 char *type_locale = _isds_utf82locale(type);
3412 isds_printf_message(context,
3413 _("Message type in envelope is not 1 character long: %s"),
3414 type_locale);
3415 free(type_locale);
3416 err = IE_INVAL;
3417 goto leave;
3419 INSERT_STRING_ATTRIBUTE(dm_envelope, "dmType", type);
3422 leave:
3423 return err;
3425 #endif /* HAVE_LIBCURL */
3428 /* Extract message document into reallocated document structure
3429 * @context is ISDS context
3430 * @document is automatically reallocated message documents structure
3431 * @xpath_ctx is XPath context with current node as isds:dmFile
3432 * In case of error @document will be freed. */
3433 static isds_error extract_document(struct isds_ctx *context,
3434 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
3435 isds_error err = IE_SUCCESS;
3436 xmlXPathObjectPtr result = NULL;
3437 xmlNodePtr file_node = xpath_ctx->node;
3438 char *string = NULL;
3440 if (!context) return IE_INVALID_CONTEXT;
3441 if (!document) return IE_INVAL;
3442 isds_document_free(document);
3443 if (!xpath_ctx) return IE_INVAL;
3445 *document = calloc(1, sizeof(**document));
3446 if (!*document) {
3447 err = IE_NOMEM;
3448 goto leave;
3451 /* Extract document meta data */
3452 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
3453 if (context->normalize_mime_type) {
3454 char *normalized_type =
3455 isds_normalize_mime_type((*document)->dmMimeType);
3456 if (normalized_type && normalized_type != (*document)->dmMimeType) {
3457 char *new_type = strdup(normalized_type);
3458 if (!new_type) {
3459 isds_printf_message(context,
3460 _("Not enough memory to normalize document MIME type"));
3461 err = IE_NOMEM;
3462 goto leave;
3464 free((*document)->dmMimeType);
3465 (*document)->dmMimeType = new_type;
3469 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
3470 err = string2isds_FileMetaType((xmlChar*)string,
3471 &((*document)->dmFileMetaType));
3472 if (err) {
3473 char *meta_type_locale = _isds_utf82locale(string);
3474 isds_printf_message(context,
3475 _("Document has invalid dmFileMetaType attribute value: %s"),
3476 meta_type_locale);
3477 free(meta_type_locale);
3478 err = IE_ISDS;
3479 goto leave;
3481 zfree(string);
3483 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
3484 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
3485 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
3486 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
3489 /* Extract document data.
3490 * Base64 encoded blob or XML subtree must be presented. */
3492 /* Check for dmEncodedContent */
3493 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
3494 xpath_ctx);
3495 if (!result) {
3496 err = IE_XML;
3497 goto leave;
3500 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3501 /* Here we have Base64 blob */
3502 (*document)->is_xml = 0;
3504 if (result->nodesetval->nodeNr > 1) {
3505 isds_printf_message(context,
3506 _("Document has more dmEncodedContent elements"));
3507 err = IE_ISDS;
3508 goto leave;
3511 xmlXPathFreeObject(result); result = NULL;
3512 EXTRACT_STRING("isds:dmEncodedContent", string);
3514 /* Decode non-empty document */
3515 if (string && string[0] != '\0') {
3516 (*document)->data_length =
3517 _isds_b64decode(string, &((*document)->data));
3518 if ((*document)->data_length == (size_t) -1) {
3519 isds_printf_message(context,
3520 _("Error while Base64-decoding document content"));
3521 err = IE_ERROR;
3522 goto leave;
3525 } else {
3526 /* No Base64 blob, try XML document */
3527 xmlXPathFreeObject(result); result = NULL;
3528 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
3529 xpath_ctx);
3530 if (!result) {
3531 err = IE_XML;
3532 goto leave;
3535 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3536 /* Here we have XML document */
3537 (*document)->is_xml = 1;
3539 if (result->nodesetval->nodeNr > 1) {
3540 isds_printf_message(context,
3541 _("Document has more dmXMLContent elements"));
3542 err = IE_ISDS;
3543 goto leave;
3546 /* XXX: We cannot serialize the content simply because:
3547 * - XML document may point out of its scope (e.g. to message
3548 * envelope)
3549 * - isds:dmXMLContent can contain more elements, no element,
3550 * a text node only
3551 * - it's not the XML way
3552 * Thus we provide the only right solution: XML DOM. Let's
3553 * application to cope with this hot potato :) */
3554 (*document)->xml_node_list =
3555 result->nodesetval->nodeTab[0]->children;
3556 } else {
3557 /* No base64 blob, nor XML document */
3558 isds_printf_message(context,
3559 _("Document has no dmEncodedContent, nor dmXMLContent "
3560 "element"));
3561 err = IE_ISDS;
3562 goto leave;
3567 leave:
3568 if (err) isds_document_free(document);
3569 free(string);
3570 xmlXPathFreeObject(result);
3571 xpath_ctx->node = file_node;
3572 return err;
3577 /* Extract message documents into reallocated list of documents
3578 * @context is ISDS context
3579 * @documents is automatically reallocated message documents list structure
3580 * @xpath_ctx is XPath context with current node as XSD tFilesArray
3581 * In case of error @documents will be freed. */
3582 static isds_error extract_documents(struct isds_ctx *context,
3583 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
3584 isds_error err = IE_SUCCESS;
3585 xmlXPathObjectPtr result = NULL;
3586 xmlNodePtr files_node = xpath_ctx->node;
3587 struct isds_list *document, *prev_document = NULL;
3589 if (!context) return IE_INVALID_CONTEXT;
3590 if (!documents) return IE_INVAL;
3591 isds_list_free(documents);
3592 if (!xpath_ctx) return IE_INVAL;
3594 /* Find documents */
3595 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
3596 if (!result) {
3597 err = IE_XML;
3598 goto leave;
3601 /* No match */
3602 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3603 isds_printf_message(context,
3604 _("Message does not contain any document"));
3605 err = IE_ISDS;
3606 goto leave;
3610 /* Iterate over documents */
3611 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
3613 /* Allocate and append list item */
3614 document = calloc(1, sizeof(*document));
3615 if (!document) {
3616 err = IE_NOMEM;
3617 goto leave;
3619 document->destructor = (void (*)(void **))isds_document_free;
3620 if (i == 0) *documents = document;
3621 else prev_document->next = document;
3622 prev_document = document;
3624 /* Extract document */
3625 xpath_ctx->node = result->nodesetval->nodeTab[i];
3626 err = extract_document(context,
3627 (struct isds_document **) &(document->data), xpath_ctx);
3628 if (err) goto leave;
3632 leave:
3633 if (err) isds_list_free(documents);
3634 xmlXPathFreeObject(result);
3635 xpath_ctx->node = files_node;
3636 return err;
3640 #if HAVE_LIBCURL
3641 /* Convert isds:dmRecord XML tree into structure
3642 * @context is ISDS context
3643 * @envelope is automatically reallocated message envelope structure
3644 * @xpath_ctx is XPath context with current node as isds:dmRecord element
3645 * In case of error @envelope will be freed. */
3646 static isds_error extract_DmRecord(struct isds_ctx *context,
3647 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3648 isds_error err = IE_SUCCESS;
3649 xmlXPathObjectPtr result = NULL;
3651 if (!context) return IE_INVALID_CONTEXT;
3652 if (!envelope) return IE_INVAL;
3653 isds_envelope_free(envelope);
3654 if (!xpath_ctx) return IE_INVAL;
3657 *envelope = calloc(1, sizeof(**envelope));
3658 if (!*envelope) {
3659 err = IE_NOMEM;
3660 goto leave;
3664 /* Extract tRecord data */
3665 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
3667 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
3668 * dmAcceptanceTime. */
3669 err = append_status_size_times(context, envelope, xpath_ctx);
3670 if (err) goto leave;
3672 /* Extract envelope elements added by sender and ISDS
3673 * (XSD: gMessageEnvelope type) */
3674 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
3675 if (err) goto leave;
3677 /* Get message type */
3678 err = append_message_type(context, envelope, xpath_ctx);
3679 if (err) goto leave;
3682 leave:
3683 if (err) isds_envelope_free(envelope);
3684 xmlXPathFreeObject(result);
3685 return err;
3689 /* Convert XSD:tStateChangesRecord type XML tree into structure
3690 * @context is ISDS context
3691 * @changed_status is automatically reallocated message state change structure
3692 * @xpath_ctx is XPath context with current node as element of
3693 * XSD:tStateChangesRecord type
3694 * In case of error @changed_status will be freed. */
3695 static isds_error extract_StateChangesRecord(struct isds_ctx *context,
3696 struct isds_message_status_change **changed_status,
3697 xmlXPathContextPtr xpath_ctx) {
3698 isds_error err = IE_SUCCESS;
3699 xmlXPathObjectPtr result = NULL;
3700 unsigned long int *unumber = NULL;
3701 char *string = NULL;
3703 if (!context) return IE_INVALID_CONTEXT;
3704 if (!changed_status) return IE_INVAL;
3705 isds_message_status_change_free(changed_status);
3706 if (!xpath_ctx) return IE_INVAL;
3709 *changed_status = calloc(1, sizeof(**changed_status));
3710 if (!*changed_status) {
3711 err = IE_NOMEM;
3712 goto leave;
3716 /* Extract tGetStateChangesInput data */
3717 EXTRACT_STRING("isds:dmID", (*changed_status)->dmID);
3719 /* dmEventTime is mandatory */
3720 EXTRACT_STRING("isds:dmEventTime", string);
3721 if (string) {
3722 err = timestring2timeval((xmlChar *) string,
3723 &((*changed_status)->time));
3724 if (err) {
3725 char *string_locale = _isds_utf82locale(string);
3726 if (err == IE_DATE) err = IE_ISDS;
3727 isds_printf_message(context,
3728 _("Could not convert dmEventTime as ISO time: %s"),
3729 string_locale);
3730 free(string_locale);
3731 goto leave;
3733 zfree(string);
3736 /* dmMessageStatus element is mandatory */
3737 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber, 0);
3738 if (!unumber) {
3739 isds_log_message(context,
3740 _("Missing mandatory isds:dmMessageStatus integer"));
3741 err = IE_ISDS;
3742 goto leave;
3744 err = uint2isds_message_status(context, unumber,
3745 &((*changed_status)->dmMessageStatus));
3746 if (err) {
3747 if (err == IE_ENUM) err = IE_ISDS;
3748 goto leave;
3750 zfree(unumber);
3753 leave:
3754 free(unumber);
3755 free(string);
3756 if (err) isds_message_status_change_free(changed_status);
3757 xmlXPathFreeObject(result);
3758 return err;
3760 #endif /* HAVE_LIBCURL */
3763 /* Find and convert isds:dmHash XML tree into structure
3764 * @context is ISDS context
3765 * @envelope is automatically reallocated message hash structure
3766 * @xpath_ctx is XPath context with current node containing isds:dmHash child
3767 * In case of error @hash will be freed. */
3768 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
3769 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
3770 isds_error err = IE_SUCCESS;
3771 xmlNodePtr old_ctx_node;
3772 xmlXPathObjectPtr result = NULL;
3773 char *string = NULL;
3775 if (!context) return IE_INVALID_CONTEXT;
3776 if (!hash) return IE_INVAL;
3777 isds_hash_free(hash);
3778 if (!xpath_ctx) return IE_INVAL;
3780 old_ctx_node = xpath_ctx->node;
3782 *hash = calloc(1, sizeof(**hash));
3783 if (!*hash) {
3784 err = IE_NOMEM;
3785 goto leave;
3788 /* Locate dmHash */
3789 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
3790 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
3791 err = IE_ISDS;
3792 goto leave;
3794 if (err) {
3795 err = IE_ERROR;
3796 goto leave;
3799 /* Get hash algorithm */
3800 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
3801 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
3802 if (err) {
3803 if (err == IE_ENUM) {
3804 char *string_locale = _isds_utf82locale(string);
3805 isds_printf_message(context, _("Unsupported hash algorithm: %s"),
3806 string_locale);
3807 free(string_locale);
3809 goto leave;
3811 zfree(string);
3813 /* Get hash value */
3814 EXTRACT_STRING(".", string);
3815 if (!string) {
3816 isds_printf_message(context,
3817 _("sisds:dmHash element is missing hash value"));
3818 err = IE_ISDS;
3819 goto leave;
3821 (*hash)->length = _isds_b64decode(string, &((*hash)->value));
3822 if ((*hash)->length == (size_t) -1) {
3823 isds_printf_message(context,
3824 _("Error while Base64-decoding hash value"));
3825 err = IE_ERROR;
3826 goto leave;
3829 leave:
3830 if (err) isds_hash_free(hash);
3831 free(string);
3832 xmlXPathFreeObject(result);
3833 xpath_ctx->node = old_ctx_node;
3834 return err;
3838 /* Find and append isds:dmQTimestamp XML tree into envelope.
3839 * Because one service is allowed to miss time-stamp content, and we think
3840 * other could too (flaw in specification), this function is deliberated and
3841 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
3842 * @context is ISDS context
3843 * @envelope is automatically allocated envelope structure
3844 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
3845 * child
3846 * In case of error @envelope will be freed. */
3847 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
3848 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3849 isds_error err = IE_SUCCESS;
3850 xmlXPathObjectPtr result = NULL;
3851 char *string = NULL;
3853 if (!context) return IE_INVALID_CONTEXT;
3854 if (!envelope) return IE_INVAL;
3855 if (!xpath_ctx) {
3856 isds_envelope_free(envelope);
3857 return IE_INVAL;
3860 if (!*envelope) {
3861 *envelope = calloc(1, sizeof(**envelope));
3862 if (!*envelope) {
3863 err = IE_NOMEM;
3864 goto leave;
3866 } else {
3867 zfree((*envelope)->timestamp);
3868 (*envelope)->timestamp_length = 0;
3871 /* Get dmQTimestamp */
3872 EXTRACT_STRING("sisds:dmQTimestamp", string);
3873 if (!string) {
3874 isds_log(ILF_ISDS, ILL_INFO, _("Missing dmQTimestamp element content\n"));
3875 goto leave;
3877 (*envelope)->timestamp_length =
3878 _isds_b64decode(string, &((*envelope)->timestamp));
3879 if ((*envelope)->timestamp_length == (size_t) -1) {
3880 isds_printf_message(context,
3881 _("Error while Base64-decoding time stamp value"));
3882 err = IE_ERROR;
3883 goto leave;
3886 leave:
3887 if (err) isds_envelope_free(envelope);
3888 free(string);
3889 xmlXPathFreeObject(result);
3890 return err;
3894 /* Convert XSD tReturnedMessage XML tree into message structure.
3895 * It does not store serialized XML tree into message->raw.
3896 * It does store (pointer to) parsed XML tree into message->xml if needed.
3897 * @context is ISDS context
3898 * @include_documents Use true if documents must be extracted
3899 * (tReturnedMessage XSD type), use false if documents shall be omitted
3900 * (tReturnedMessageEnvelope).
3901 * @message is automatically reallocated message structure
3902 * @xpath_ctx is XPath context with current node as tReturnedMessage element
3903 * type
3904 * In case of error @message will be freed. */
3905 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
3906 const _Bool include_documents, struct isds_message **message,
3907 xmlXPathContextPtr xpath_ctx) {
3908 isds_error err = IE_SUCCESS;
3909 xmlNodePtr message_node;
3911 if (!context) return IE_INVALID_CONTEXT;
3912 if (!message) return IE_INVAL;
3913 isds_message_free(message);
3914 if (!xpath_ctx) return IE_INVAL;
3917 *message = calloc(1, sizeof(**message));
3918 if (!*message) {
3919 err = IE_NOMEM;
3920 goto leave;
3923 /* Save message XPATH context node */
3924 message_node = xpath_ctx->node;
3927 /* Extract dmDM */
3928 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
3929 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
3930 if (err) { err = IE_ERROR; goto leave; }
3931 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
3932 if (err) goto leave;
3934 if (include_documents) {
3935 struct isds_list *item;
3937 /* Extract dmFiles */
3938 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
3939 xpath_ctx);
3940 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
3941 err = IE_ISDS; goto leave;
3943 if (err) { err = IE_ERROR; goto leave; }
3944 err = extract_documents(context, &((*message)->documents), xpath_ctx);
3945 if (err) goto leave;
3947 /* Store xmlDoc of this message if needed */
3948 /* Only if we got a XML document in all the documents. */
3949 for (item = (*message)->documents; item; item = item->next) {
3950 if (item->data && ((struct isds_document *)item->data)->is_xml) {
3951 (*message)->xml = xpath_ctx->doc;
3952 break;
3958 /* Restore context to message */
3959 xpath_ctx->node = message_node;
3961 /* Extract dmHash */
3962 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
3963 xpath_ctx);
3964 if (err) goto leave;
3966 /* Extract dmQTimestamp, */
3967 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
3968 xpath_ctx);
3969 if (err) goto leave;
3971 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
3972 * dmAcceptanceTime. */
3973 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
3974 if (err) goto leave;
3976 /* Get message type */
3977 err = append_message_type(context, &((*message)->envelope), xpath_ctx);
3978 if (err) goto leave;
3980 leave:
3981 if (err) isds_message_free(message);
3982 return err;
3986 /* Extract message event into reallocated isds_event structure
3987 * @context is ISDS context
3988 * @event is automatically reallocated message event structure
3989 * @xpath_ctx is XPath context with current node as isds:dmEvent
3990 * In case of error @event will be freed. */
3991 static isds_error extract_event(struct isds_ctx *context,
3992 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
3993 isds_error err = IE_SUCCESS;
3994 xmlXPathObjectPtr result = NULL;
3995 xmlNodePtr event_node = xpath_ctx->node;
3996 char *string = NULL;
3998 if (!context) return IE_INVALID_CONTEXT;
3999 if (!event) return IE_INVAL;
4000 isds_event_free(event);
4001 if (!xpath_ctx) return IE_INVAL;
4003 *event = calloc(1, sizeof(**event));
4004 if (!*event) {
4005 err = IE_NOMEM;
4006 goto leave;
4009 /* Extract event data.
4010 * All elements are optional according XSD. That's funny. */
4011 EXTRACT_STRING("sisds:dmEventTime", string);
4012 if (string) {
4013 err = timestring2timeval((xmlChar *) string, &((*event)->time));
4014 if (err) {
4015 char *string_locale = _isds_utf82locale(string);
4016 if (err == IE_DATE) err = IE_ISDS;
4017 isds_printf_message(context,
4018 _("Could not convert dmEventTime as ISO time: %s"),
4019 string_locale);
4020 free(string_locale);
4021 goto leave;
4023 zfree(string);
4026 /* dmEventDescr element has prefix and the rest */
4027 EXTRACT_STRING("sisds:dmEventDescr", string);
4028 if (string) {
4029 err = eventstring2event((xmlChar *) string, *event);
4030 if (err) goto leave;
4031 zfree(string);
4034 leave:
4035 if (err) isds_event_free(event);
4036 free(string);
4037 xmlXPathFreeObject(result);
4038 xpath_ctx->node = event_node;
4039 return err;
4043 /* Convert element of XSD tEventsArray type from XML tree into
4044 * isds_list of isds_event's structure. The list is automatically reallocated.
4045 * @context is ISDS context
4046 * @events is automatically reallocated list of event structures
4047 * @xpath_ctx is XPath context with current node as tEventsArray
4048 * In case of error @events will be freed. */
4049 static isds_error extract_events(struct isds_ctx *context,
4050 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
4051 isds_error err = IE_SUCCESS;
4052 xmlXPathObjectPtr result = NULL;
4053 xmlNodePtr events_node = xpath_ctx->node;
4054 struct isds_list *event, *prev_event = NULL;
4056 if (!context) return IE_INVALID_CONTEXT;
4057 if (!events) return IE_INVAL;
4058 if (!xpath_ctx) return IE_INVAL;
4060 /* Free old list */
4061 isds_list_free(events);
4063 /* Find events */
4064 result = xmlXPathEvalExpression(BAD_CAST "sisds:dmEvent", xpath_ctx);
4065 if (!result) {
4066 err = IE_XML;
4067 goto leave;
4070 /* No match */
4071 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4072 isds_printf_message(context,
4073 _("Delivery info does not contain any event"));
4074 err = IE_ISDS;
4075 goto leave;
4079 /* Iterate over events */
4080 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4082 /* Allocate and append list item */
4083 event = calloc(1, sizeof(*event));
4084 if (!event) {
4085 err = IE_NOMEM;
4086 goto leave;
4088 event->destructor = (void (*)(void **))isds_event_free;
4089 if (i == 0) *events = event;
4090 else prev_event->next = event;
4091 prev_event = event;
4093 /* Extract event */
4094 xpath_ctx->node = result->nodesetval->nodeTab[i];
4095 err = extract_event(context,
4096 (struct isds_event **) &(event->data), xpath_ctx);
4097 if (err) goto leave;
4101 leave:
4102 if (err) isds_list_free(events);
4103 xmlXPathFreeObject(result);
4104 xpath_ctx->node = events_node;
4105 return err;
4109 #if HAVE_LIBCURL
4110 /* Insert Base64 encoded data as element with text child.
4111 * @context is session context
4112 * @parent is XML node to append @element with @data as child
4113 * @ns is XML namespace of @element, use NULL to inherit from @parent
4114 * @element is UTF-8 encoded name of new element
4115 * @data is bit stream to encode into @element
4116 * @length is size of @data in bytes
4117 * @return standard error code and fill long error message if needed */
4118 static isds_error insert_base64_encoded_string(struct isds_ctx *context,
4119 xmlNodePtr parent, const xmlNsPtr ns, const char *element,
4120 const void *data, size_t length) {
4121 isds_error err = IE_SUCCESS;
4122 xmlNodePtr node;
4124 if (!context) return IE_INVALID_CONTEXT;
4125 if (!data && length > 0) return IE_INVAL;
4126 if (!parent || !element) return IE_INVAL;
4128 xmlChar *base64data = NULL;
4129 base64data = (xmlChar *) _isds_b64encode(data, length);
4130 if (!base64data) {
4131 isds_printf_message(context,
4132 ngettext("Not enough memory to encode %zd byte into Base64",
4133 "Not enough memory to encode %zd bytes into Base64",
4134 length),
4135 length);
4136 err = IE_NOMEM;
4137 goto leave;
4139 INSERT_STRING_WITH_NS(parent, ns, element, base64data);
4141 leave:
4142 free(base64data);
4143 return err;
4147 /* Convert isds_document structure into XML tree and append to dmFiles node.
4148 * @context is session context
4149 * @document is ISDS document
4150 * @dm_files is XML element the resulting tree will be appended to as a child.
4151 * @return error code, in case of error context' message is filled. */
4152 static isds_error insert_document(struct isds_ctx *context,
4153 struct isds_document *document, xmlNodePtr dm_files) {
4154 isds_error err = IE_SUCCESS;
4155 xmlNodePtr new_file = NULL, file = NULL, node;
4156 xmlAttrPtr attribute_node;
4158 if (!context) return IE_INVALID_CONTEXT;
4159 if (!document || !dm_files) return IE_INVAL;
4161 /* Allocate new dmFile */
4162 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
4163 if (!new_file) {
4164 isds_printf_message(context, _("Could not allocate main dmFile"));
4165 err = IE_ERROR;
4166 goto leave;
4168 /* Append the new dmFile.
4169 * XXX: Main document must go first */
4170 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
4171 file = xmlAddPrevSibling(dm_files->children, new_file);
4172 else
4173 file = xmlAddChild(dm_files, new_file);
4175 if (!file) {
4176 xmlFreeNode(new_file); new_file = NULL;
4177 isds_printf_message(context, _("Could not add dmFile child to "
4178 "%s element"), dm_files->name);
4179 err = IE_ERROR;
4180 goto leave;
4183 /* @dmMimeType is required */
4184 if (!document->dmMimeType) {
4185 isds_log_message(context,
4186 _("Document is missing mandatory MIME type definition"));
4187 err = IE_INVAL;
4188 goto leave;
4190 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
4192 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
4193 if (!string) {
4194 isds_printf_message(context,
4195 _("Document has unknown dmFileMetaType: %ld"),
4196 document->dmFileMetaType);
4197 err = IE_ENUM;
4198 goto leave;
4200 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
4202 if (document->dmFileGuid) {
4203 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
4205 if (document->dmUpFileGuid) {
4206 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
4209 /* @dmFileDescr is required */
4210 if (!document->dmFileDescr) {
4211 isds_log_message(context,
4212 _("Document is missing mandatory description (title)"));
4213 err = IE_INVAL;
4214 goto leave;
4216 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
4218 if (document->dmFormat) {
4219 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
4223 /* Insert content (body) of the document. */
4224 if (document->is_xml) {
4225 /* XML document requested */
4227 /* Allocate new dmXMLContent */
4228 xmlNodePtr xmlcontent = xmlNewNode(file->ns, BAD_CAST "dmXMLContent");
4229 if (!xmlcontent) {
4230 isds_printf_message(context,
4231 _("Could not allocate dmXMLContent element"));
4232 err = IE_ERROR;
4233 goto leave;
4235 /* Append it */
4236 node = xmlAddChild(file, xmlcontent);
4237 if (!node) {
4238 xmlFreeNode(xmlcontent); xmlcontent = NULL;
4239 isds_printf_message(context,
4240 _("Could not add dmXMLContent child to %s element"),
4241 file->name);
4242 err = IE_ERROR;
4243 goto leave;
4246 /* Copy non-empty node list */
4247 if (document->xml_node_list) {
4248 xmlNodePtr content = xmlDocCopyNodeList(node->doc,
4249 document->xml_node_list);
4250 if (!content) {
4251 isds_printf_message(context,
4252 _("Not enough memory to copy XML document"));
4253 err = IE_NOMEM;
4254 goto leave;
4257 if (!xmlAddChildList(node, content)) {
4258 xmlFreeNodeList(content);
4259 isds_printf_message(context,
4260 _("Error while adding XML document into dmXMLContent"));
4261 err = IE_XML;
4262 goto leave;
4264 /* XXX: We cannot free the content here because it's part of node's
4265 * document since now. It will be freed with it automatically. */
4267 } else {
4268 /* Binary document requested */
4269 err = insert_base64_encoded_string(context, file, NULL, "dmEncodedContent",
4270 document->data, document->data_length);
4271 if (err) goto leave;
4274 leave:
4275 return err;
4279 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4280 * The copy must be preallocated, the date are just appended into structure.
4281 * @context is ISDS context
4282 * @copy is message copy structure
4283 * @xpath_ctx is XPath context with current node as tMStatus */
4284 static isds_error append_TMStatus(struct isds_ctx *context,
4285 struct isds_message_copy *copy, xmlXPathContextPtr xpath_ctx) {
4286 isds_error err = IE_SUCCESS;
4287 xmlXPathObjectPtr result = NULL;
4288 char *code = NULL, *message = NULL;
4290 if (!context) return IE_INVALID_CONTEXT;
4291 if (!copy || !xpath_ctx) return IE_INVAL;
4293 /* Free old values */
4294 zfree(copy->dmStatus);
4295 zfree(copy->dmID);
4297 /* Get error specific to this copy */
4298 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code);
4299 if (!code) {
4300 isds_log_message(context,
4301 _("Missing isds:dmStatusCode under "
4302 "XSD:tMStatus type element"));
4303 err = IE_ISDS;
4304 goto leave;
4307 if (xmlStrcmp((const xmlChar *)code, BAD_CAST "0000")) {
4308 /* This copy failed */
4309 copy->error = IE_ISDS;
4310 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message);
4311 if (message) {
4312 copy->dmStatus = _isds_astrcat3(code, ": ", message);
4313 if (!copy->dmStatus) {
4314 copy->dmStatus = code;
4315 code = NULL;
4317 } else {
4318 copy->dmStatus = code;
4319 code = NULL;
4321 } else {
4322 /* This copy succeeded. In this case only, message ID is valid */
4323 copy->error = IE_SUCCESS;
4325 EXTRACT_STRING("isds:dmID", copy->dmID);
4326 if (!copy->dmID) {
4327 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
4328 "but did not returned assigned message ID\n"));
4329 err = IE_ISDS;
4333 leave:
4334 free(code);
4335 free(message);
4336 xmlXPathFreeObject(result);
4337 return err;
4341 /* Insert struct isds_approval data (box approval) into XML tree
4342 * @context is session context
4343 * @approval is libisds structure with approval description. NULL is
4344 * acceptable.
4345 * @parent is XML element to append @approval to */
4346 static isds_error insert_GExtApproval(struct isds_ctx *context,
4347 const struct isds_approval *approval, xmlNodePtr parent) {
4349 isds_error err = IE_SUCCESS;
4350 xmlNodePtr node;
4352 if (!context) return IE_INVALID_CONTEXT;
4353 if (!parent) return IE_INVAL;
4355 if (!approval) return IE_SUCCESS;
4357 /* Build XSD:gExtApproval */
4358 INSERT_SCALAR_BOOLEAN(parent, "dbApproved", approval->approved);
4359 INSERT_STRING(parent, "dbExternRefNumber", approval->refference);
4361 leave:
4362 return err;
4366 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4367 * code
4368 * @context is session context
4369 * @service_name is name of SERVICE_DB_ACCESS
4370 * @response is server SOAP body response as XML document
4371 * @raw_response is automatically reallocated bit stream with response body. Use
4372 * NULL if you don't care
4373 * @raw_response_length is size of @raw_response in bytes
4374 * @code is ISDS status code
4375 * @status_message is ISDS status message
4376 * @return error coded from lower layer, context message will be set up
4377 * appropriately. */
4378 static isds_error build_send_check_dbdummy_request(struct isds_ctx *context,
4379 const xmlChar *service_name,
4380 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
4381 xmlChar **code, xmlChar **status_message) {
4383 isds_error err = IE_SUCCESS;
4384 char *service_name_locale = NULL;
4385 xmlNodePtr request = NULL, node;
4386 xmlNsPtr isds_ns = NULL;
4388 if (!context) return IE_INVALID_CONTEXT;
4389 if (!service_name) return IE_INVAL;
4390 if (!response || !code || !status_message) return IE_INVAL;
4391 if (!raw_response_length && raw_response) return IE_INVAL;
4393 /* Free output argument */
4394 xmlFreeDoc(*response); *response = NULL;
4395 if (raw_response) zfree(*raw_response);
4396 free(*code);
4397 free(*status_message);
4400 /* Check if connection is established
4401 * TODO: This check should be done downstairs. */
4402 if (!context->curl) return IE_CONNECTION_CLOSED;
4404 service_name_locale = _isds_utf82locale((char*)service_name);
4405 if (!service_name_locale) {
4406 err = IE_NOMEM;
4407 goto leave;
4410 /* Build request */
4411 request = xmlNewNode(NULL, service_name);
4412 if (!request) {
4413 isds_printf_message(context,
4414 _("Could not build %s request"), service_name_locale);
4415 err = IE_ERROR;
4416 goto leave;
4418 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4419 if(!isds_ns) {
4420 isds_log_message(context, _("Could not create ISDS name space"));
4421 err = IE_ERROR;
4422 goto leave;
4424 xmlSetNs(request, isds_ns);
4427 /* Add XSD:tDummyInput child */
4428 INSERT_STRING(request, "dbDummy", NULL);
4431 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
4432 service_name_locale);
4434 /* Send request */
4435 err = isds(context, SERVICE_DB_ACCESS, request, response,
4436 raw_response, raw_response_length);
4437 xmlFreeNode(request); request = NULL;
4439 if (err) {
4440 isds_log(ILF_ISDS, ILL_DEBUG,
4441 _("Processing ISDS response on %s request failed\n"),
4442 service_name_locale);
4443 goto leave;
4446 /* Check for response status */
4447 err = isds_response_status(context, SERVICE_DB_ACCESS, *response,
4448 code, status_message, NULL);
4449 if (err) {
4450 isds_log(ILF_ISDS, ILL_DEBUG,
4451 _("ISDS response on %s request is missing status\n"),
4452 service_name_locale);
4453 goto leave;
4456 /* Request processed, but nothing found */
4457 if (xmlStrcmp(*code, BAD_CAST "0000")) {
4458 char *code_locale = _isds_utf82locale((char*) *code);
4459 char *status_message_locale =
4460 _isds_utf82locale((char*) *status_message);
4461 isds_log(ILF_ISDS, ILL_DEBUG,
4462 _("Server refused %s request (code=%s, message=%s)\n"),
4463 service_name_locale, code_locale, status_message_locale);
4464 isds_log_message(context, status_message_locale);
4465 free(code_locale);
4466 free(status_message_locale);
4467 err = IE_ISDS;
4468 goto leave;
4471 leave:
4472 free(service_name_locale);
4473 xmlFreeNode(request);
4474 return err;
4476 #endif
4479 /* Get data about logged in user and his box. */
4480 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
4481 struct isds_DbOwnerInfo **db_owner_info) {
4482 isds_error err = IE_SUCCESS;
4483 #if HAVE_LIBCURL
4484 xmlDocPtr response = NULL;
4485 xmlChar *code = NULL, *message = NULL;
4486 xmlXPathContextPtr xpath_ctx = NULL;
4487 xmlXPathObjectPtr result = NULL;
4488 char *string = NULL;
4489 #endif
4491 if (!context) return IE_INVALID_CONTEXT;
4492 zfree(context->long_message);
4493 if (!db_owner_info) return IE_INVAL;
4494 isds_DbOwnerInfo_free(db_owner_info);
4496 #if HAVE_LIBCURL
4497 /* Check if connection is established */
4498 if (!context->curl) return IE_CONNECTION_CLOSED;
4501 /* Do request and check for success */
4502 err = build_send_check_dbdummy_request(context,
4503 BAD_CAST "GetOwnerInfoFromLogin",
4504 &response, NULL, NULL, &code, &message);
4505 if (err) goto leave;
4508 /* Extract data */
4509 /* Prepare structure */
4510 *db_owner_info = calloc(1, sizeof(**db_owner_info));
4511 if (!*db_owner_info) {
4512 err = IE_NOMEM;
4513 goto leave;
4515 xpath_ctx = xmlXPathNewContext(response);
4516 if (!xpath_ctx) {
4517 err = IE_ERROR;
4518 goto leave;
4520 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4521 err = IE_ERROR;
4522 goto leave;
4525 /* Set context node */
4526 result = xmlXPathEvalExpression(BAD_CAST
4527 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
4528 if (!result) {
4529 err = IE_ERROR;
4530 goto leave;
4532 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4533 isds_log_message(context, _("Missing dbOwnerInfo element"));
4534 err = IE_ISDS;
4535 goto leave;
4537 if (result->nodesetval->nodeNr > 1) {
4538 isds_log_message(context, _("Multiple dbOwnerInfo element"));
4539 err = IE_ISDS;
4540 goto leave;
4542 xpath_ctx->node = result->nodesetval->nodeTab[0];
4543 xmlXPathFreeObject(result); result = NULL;
4545 /* Extract it */
4546 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
4549 leave:
4550 if (err) {
4551 isds_DbOwnerInfo_free(db_owner_info);
4554 free(string);
4555 xmlXPathFreeObject(result);
4556 xmlXPathFreeContext(xpath_ctx);
4558 free(code);
4559 free(message);
4560 xmlFreeDoc(response);
4562 if (!err)
4563 isds_log(ILF_ISDS, ILL_DEBUG,
4564 _("GetOwnerInfoFromLogin request processed by server "
4565 "successfully.\n"));
4566 #else /* not HAVE_LIBCURL */
4567 err = IE_NOTSUP;
4568 #endif
4570 return err;
4574 /* Get data about logged in user. */
4575 isds_error isds_GetUserInfoFromLogin(struct isds_ctx *context,
4576 struct isds_DbUserInfo **db_user_info) {
4577 isds_error err = IE_SUCCESS;
4578 #if HAVE_LIBCURL
4579 xmlDocPtr response = NULL;
4580 xmlChar *code = NULL, *message = NULL;
4581 xmlXPathContextPtr xpath_ctx = NULL;
4582 xmlXPathObjectPtr result = NULL;
4583 #endif
4585 if (!context) return IE_INVALID_CONTEXT;
4586 zfree(context->long_message);
4587 if (!db_user_info) return IE_INVAL;
4588 isds_DbUserInfo_free(db_user_info);
4590 #if HAVE_LIBCURL
4591 /* Check if connection is established */
4592 if (!context->curl) return IE_CONNECTION_CLOSED;
4595 /* Do request and check for success */
4596 err = build_send_check_dbdummy_request(context,
4597 BAD_CAST "GetUserInfoFromLogin",
4598 &response, NULL, NULL, &code, &message);
4599 if (err) goto leave;
4602 /* Extract data */
4603 /* Prepare structure */
4604 *db_user_info = calloc(1, sizeof(**db_user_info));
4605 if (!*db_user_info) {
4606 err = IE_NOMEM;
4607 goto leave;
4609 xpath_ctx = xmlXPathNewContext(response);
4610 if (!xpath_ctx) {
4611 err = IE_ERROR;
4612 goto leave;
4614 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4615 err = IE_ERROR;
4616 goto leave;
4619 /* Set context node */
4620 result = xmlXPathEvalExpression(BAD_CAST
4621 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx);
4622 if (!result) {
4623 err = IE_ERROR;
4624 goto leave;
4626 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4627 isds_log_message(context, _("Missing dbUserInfo element"));
4628 err = IE_ISDS;
4629 goto leave;
4631 if (result->nodesetval->nodeNr > 1) {
4632 isds_log_message(context, _("Multiple dbUserInfo element"));
4633 err = IE_ISDS;
4634 goto leave;
4636 xpath_ctx->node = result->nodesetval->nodeTab[0];
4637 xmlXPathFreeObject(result); result = NULL;
4639 /* Extract it */
4640 err = extract_DbUserInfo(context, db_user_info, xpath_ctx);
4642 leave:
4643 if (err) {
4644 isds_DbUserInfo_free(db_user_info);
4647 xmlXPathFreeObject(result);
4648 xmlXPathFreeContext(xpath_ctx);
4650 free(code);
4651 free(message);
4652 xmlFreeDoc(response);
4654 if (!err)
4655 isds_log(ILF_ISDS, ILL_DEBUG,
4656 _("GetUserInfoFromLogin request processed by server "
4657 "successfully.\n"));
4658 #else /* not HAVE_LIBCURL */
4659 err = IE_NOTSUP;
4660 #endif
4662 return err;
4666 /* Get expiration time of current password
4667 * @context is session context
4668 * @expiration is automatically reallocated time when password expires. If
4669 * password expiration is disables, NULL will be returned. In case of error
4670 * it will be nulled too. */
4671 isds_error isds_get_password_expiration(struct isds_ctx *context,
4672 struct timeval **expiration) {
4673 isds_error err = IE_SUCCESS;
4674 #if HAVE_LIBCURL
4675 xmlDocPtr response = NULL;
4676 xmlChar *code = NULL, *message = NULL;
4677 xmlXPathContextPtr xpath_ctx = NULL;
4678 xmlXPathObjectPtr result = NULL;
4679 char *string = NULL;
4680 #endif
4682 if (!context) return IE_INVALID_CONTEXT;
4683 zfree(context->long_message);
4684 if (!expiration) return IE_INVAL;
4685 zfree(*expiration);
4687 #if HAVE_LIBCURL
4688 /* Check if connection is established */
4689 if (!context->curl) return IE_CONNECTION_CLOSED;
4692 /* Do request and check for success */
4693 err = build_send_check_dbdummy_request(context,
4694 BAD_CAST "GetPasswordInfo",
4695 &response, NULL, NULL, &code, &message);
4696 if (err) goto leave;
4699 /* Extract data */
4700 xpath_ctx = xmlXPathNewContext(response);
4701 if (!xpath_ctx) {
4702 err = IE_ERROR;
4703 goto leave;
4705 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4706 err = IE_ERROR;
4707 goto leave;
4710 /* Set context node */
4711 result = xmlXPathEvalExpression(BAD_CAST
4712 "/isds:GetPasswordInfoResponse", xpath_ctx);
4713 if (!result) {
4714 err = IE_ERROR;
4715 goto leave;
4717 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4718 isds_log_message(context,
4719 _("Missing GetPasswordInfoResponse element"));
4720 err = IE_ISDS;
4721 goto leave;
4723 if (result->nodesetval->nodeNr > 1) {
4724 isds_log_message(context,
4725 _("Multiple GetPasswordInfoResponse element"));
4726 err = IE_ISDS;
4727 goto leave;
4729 xpath_ctx->node = result->nodesetval->nodeTab[0];
4730 xmlXPathFreeObject(result); result = NULL;
4732 /* Extract expiration date */
4733 EXTRACT_STRING("isds:pswExpDate", string);
4734 if (string) {
4735 /* And convert it if any returned. Otherwise expiration is disabled. */
4736 err = timestring2timeval((xmlChar *) string, expiration);
4737 if (err) {
4738 char *string_locale = _isds_utf82locale(string);
4739 if (err == IE_DATE) err = IE_ISDS;
4740 isds_printf_message(context,
4741 _("Could not convert pswExpDate as ISO time: %s"),
4742 string_locale);
4743 free(string_locale);
4744 goto leave;
4748 leave:
4749 if (err) {
4750 if (*expiration) {
4751 zfree(*expiration);
4755 free(string);
4756 xmlXPathFreeObject(result);
4757 xmlXPathFreeContext(xpath_ctx);
4759 free(code);
4760 free(message);
4761 xmlFreeDoc(response);
4763 if (!err)
4764 isds_log(ILF_ISDS, ILL_DEBUG,
4765 _("GetPasswordInfo request processed by server "
4766 "successfully.\n"));
4767 #else /* not HAVE_LIBCURL */
4768 err = IE_NOTSUP;
4769 #endif
4771 return err;
4775 /* Change user password in ISDS.
4776 * User must supply old password, new password will takes effect after some
4777 * time, current session can continue. Password must fulfill some constraints.
4778 * @context is session context
4779 * @old_password is current password.
4780 * @new_password is requested new password
4781 * @otp auxiliary data required if one-time password authentication is in use,
4782 * defines OTP code (if known) and returns fine grade resolution of OTP
4783 * procedure. Pass NULL, if one-time password authentication is not needed.
4784 * Please note the @otp argument must much OTP method used at log-in time. See
4785 * isds_login() function for more details.
4786 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
4787 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
4788 * awaiting OTP code that has been delivered by side channel to the user. */
4789 isds_error isds_change_password(struct isds_ctx *context,
4790 const char *old_password, const char *new_password,
4791 struct isds_otp *otp) {
4792 isds_error err = IE_SUCCESS;
4793 #if HAVE_LIBCURL
4794 xmlNsPtr isds_ns = NULL;
4795 xmlNodePtr request = NULL, node;
4796 xmlDocPtr response = NULL;
4797 xmlChar *code = NULL, *message = NULL;
4798 const xmlChar *codes[] = {
4799 BAD_CAST "1066",
4800 BAD_CAST "1067",
4801 BAD_CAST "1079",
4802 BAD_CAST "1080",
4803 BAD_CAST "1081",
4804 BAD_CAST "1082",
4805 BAD_CAST "1083",
4806 BAD_CAST "1090",
4807 BAD_CAST "1091",
4808 BAD_CAST "9204"
4810 const char *meanings[] = {
4811 N_("Password length must be between 8 and 32 characters"),
4812 N_("New password must differ from the current one"),
4813 N_("Password contains forbidden character"),
4814 N_("Password must contain at least one upper-case letter, "
4815 "one lower-case, and one digit"),
4816 N_("Password cannot contain sequence of three identical characters"),
4817 N_("Password cannot contain user identifier"),
4818 N_("Password is too simmple"),
4819 N_("Old password is not valid"),
4820 N_("Passwords cannot be reused"),
4821 N_("LDAP update error")
4823 #endif
4825 if (!context) return IE_INVALID_CONTEXT;
4826 zfree(context->long_message);
4827 if (!old_password || !new_password) return IE_INVAL;
4829 #if HAVE_LIBCURL
4830 /* Check if connection is established
4831 * TODO: This check should be done downstairs. */
4832 if (!context->curl) return IE_CONNECTION_CLOSED;
4834 if (otp != NULL) {
4835 isds_log_message(context, _("Changing password if one-time password "
4836 "authentication method is in use is not implemented yet"));
4837 return IE_NOTSUP;
4840 /* Build ChangeISDSPassword request */
4841 request = xmlNewNode(NULL, BAD_CAST "ChangeISDSPassword");
4842 if (!request) {
4843 isds_log_message(context,
4844 _("Could not build ChangeISDSPassword request"));
4845 return IE_ERROR;
4847 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4848 if(!isds_ns) {
4849 isds_log_message(context, _("Could not create ISDS name space"));
4850 xmlFreeNode(request);
4851 return IE_ERROR;
4853 xmlSetNs(request, isds_ns);
4855 INSERT_STRING(request, "dbOldPassword", old_password);
4856 INSERT_STRING(request, "dbNewPassword", new_password);
4859 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CheckDataBox request to ISDS\n"));
4861 /* Sent request */
4862 err = isds(context, SERVICE_DB_ACCESS, request, &response, NULL, NULL);
4864 /* Destroy request */
4865 xmlFreeNode(request); request = NULL;
4867 if (err) {
4868 isds_log(ILF_ISDS, ILL_DEBUG,
4869 _("Processing ISDS response on ChangeISDSPassword "
4870 "request failed\n"));
4871 goto leave;
4874 /* Check for response status */
4875 err = isds_response_status(context, SERVICE_DB_ACCESS, response,
4876 &code, &message, NULL);
4877 if (err) {
4878 isds_log(ILF_ISDS, ILL_DEBUG,
4879 _("ISDS response on ChangeISDSPassword request is missing "
4880 "status\n"));
4881 goto leave;
4884 /* Check for known error codes */
4885 for (int i=0; i < sizeof(codes)/sizeof(*codes); i++) {
4886 if (!xmlStrcmp(code, codes[i])) {
4887 char *code_locale = _isds_utf82locale((char*)code);
4888 char *message_locale = _isds_utf82locale((char*)message);
4889 isds_log(ILF_ISDS, ILL_DEBUG,
4890 _("Server refused empty password on ChangeISDSPassword "
4891 "request (code=%s, message=%s)\n"),
4892 code_locale, message_locale);
4893 free(code_locale);
4894 free(message_locale);
4895 isds_log_message(context, _(meanings[i]));
4896 err = IE_INVAL;
4897 goto leave;
4901 /* Other error */
4902 if (xmlStrcmp(code, BAD_CAST "0000")) {
4903 char *code_locale = _isds_utf82locale((char*)code);
4904 char *message_locale = _isds_utf82locale((char*)message);
4905 isds_log(ILF_ISDS, ILL_DEBUG,
4906 _("Server refused to change password on ChangeISDSPassword "
4907 "request (code=%s, message=%s)\n"),
4908 code_locale, message_locale);
4909 isds_log_message(context, message_locale);
4910 free(code_locale);
4911 free(message_locale);
4912 err = IE_ISDS;
4913 goto leave;
4916 /* Otherwise password changed successfully */
4918 leave:
4919 free(code);
4920 free(message);
4921 xmlFreeDoc(response);
4922 xmlFreeNode(request);
4924 if (!err)
4925 isds_log(ILF_ISDS, ILL_DEBUG,
4926 _("Password changed successfully on ChangeISDSPassword "
4927 "request.\n"));
4928 #else /* not HAVE_LIBCURL */
4929 err = IE_NOTSUP;
4930 #endif
4932 return err;
4936 #if HAVE_LIBCURL
4937 /* Generic middle part with request sending and response check.
4938 * It sends prepared request and checks for error code.
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 * @response is XML document outputting ISDS response.
4944 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4945 * NULL, if you don't care. */
4946 static isds_error send_destroy_request_check_response(
4947 struct isds_ctx *context,
4948 const isds_service service, const xmlChar *service_name,
4949 xmlNodePtr *request, xmlDocPtr *response, xmlChar **refnumber) {
4950 isds_error err = IE_SUCCESS;
4951 char *service_name_locale = NULL;
4952 xmlChar *code = NULL, *message = NULL;
4955 if (!context) return IE_INVALID_CONTEXT;
4956 if (!service_name || *service_name == '\0' || !request || !*request ||
4957 !response)
4958 return IE_INVAL;
4960 /* Check if connection is established
4961 * TODO: This check should be done downstairs. */
4962 if (!context->curl) return IE_CONNECTION_CLOSED;
4964 service_name_locale = _isds_utf82locale((char*) service_name);
4965 if (!service_name_locale) {
4966 err = IE_NOMEM;
4967 goto leave;
4970 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
4971 service_name_locale);
4973 /* Send request */
4974 err = isds(context, service, *request, response, NULL, NULL);
4975 xmlFreeNode(*request); *request = NULL;
4977 if (err) {
4978 isds_log(ILF_ISDS, ILL_DEBUG,
4979 _("Processing ISDS response on %s request failed\n"),
4980 service_name_locale);
4981 goto leave;
4984 /* Check for response status */
4985 err = isds_response_status(context, service, *response,
4986 &code, &message, refnumber);
4987 if (err) {
4988 isds_log(ILF_ISDS, ILL_DEBUG,
4989 _("ISDS response on %s request is missing status\n"),
4990 service_name_locale);
4991 goto leave;
4994 /* Request processed, but server failed */
4995 if (xmlStrcmp(code, BAD_CAST "0000")) {
4996 char *code_locale = _isds_utf82locale((char*) code);
4997 char *message_locale = _isds_utf82locale((char*) message);
4998 isds_log(ILF_ISDS, ILL_DEBUG,
4999 _("Server refused %s request (code=%s, message=%s)\n"),
5000 service_name_locale, code_locale, message_locale);
5001 isds_log_message(context, message_locale);
5002 free(code_locale);
5003 free(message_locale);
5004 err = IE_ISDS;
5005 goto leave;
5009 leave:
5010 free(code);
5011 free(message);
5012 if (err && *response) {
5013 xmlFreeDoc(*response);
5014 *response = NULL;
5016 if (*request) {
5017 xmlFreeNode(*request);
5018 *request = NULL;
5020 free(service_name_locale);
5022 return err;
5026 /* Generic bottom half with request sending.
5027 * It sends prepared request, checks for error code, destroys response and
5028 * request and log success or failure.
5029 * @context is ISDS session context.
5030 * @service is ISDS service handler
5031 * @service_name is name in scope of given @service
5032 * @request is XML tree with request. Will be freed to save memory.
5033 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5034 * NULL, if you don't care. */
5035 static isds_error send_request_check_drop_response(
5036 struct isds_ctx *context,
5037 const isds_service service, const xmlChar *service_name,
5038 xmlNodePtr *request, xmlChar **refnumber) {
5039 isds_error err = IE_SUCCESS;
5040 xmlDocPtr response = NULL;
5043 if (!context) return IE_INVALID_CONTEXT;
5044 if (!service_name || *service_name == '\0' || !request || !*request)
5045 return IE_INVAL;
5047 /* Send request and check response*/
5048 err = send_destroy_request_check_response(context,
5049 service, service_name, request, &response, refnumber);
5051 xmlFreeDoc(response);
5053 if (*request) {
5054 xmlFreeNode(*request);
5055 *request = NULL;
5058 if (!err) {
5059 char *service_name_locale = _isds_utf82locale((char *) service_name);
5060 isds_log(ILF_ISDS, ILL_DEBUG,
5061 _("%s request processed by server successfully.\n"),
5062 service_name_locale);
5063 free(service_name_locale);
5066 return err;
5070 /* Insert isds_credentials_delivery structure into XML request if not NULL
5071 * @context is session context
5072 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
5073 * credentials delivery. The email field is passed.
5074 * @parent is XML element where to insert */
5075 static isds_error insert_credentials_delivery(struct isds_ctx *context,
5076 const struct isds_credentials_delivery *credentials_delivery,
5077 xmlNodePtr parent) {
5078 isds_error err = IE_SUCCESS;
5079 xmlNodePtr node;
5081 if (!context) return IE_INVALID_CONTEXT;
5082 if (!parent) return IE_INVAL;
5084 if (credentials_delivery) {
5085 /* Following elements are valid only for services:
5086 * NewAccessData, AddDataBoxUser, CreateDataBox */
5087 INSERT_SCALAR_BOOLEAN(parent, "dbVirtual", 1);
5088 INSERT_STRING(parent, "email", credentials_delivery->email);
5091 leave:
5092 return err;
5096 /* Extract credentials delivery from ISDS response.
5097 * @context is session context
5098 * @credentials_delivery is pointer to valid structure to fill in returned
5099 * user's password (and new log-in name). If NULL, do not extract the data.
5100 * @response is pointer to XML document with ISDS response
5101 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
5102 * @return IE_SUCCESS even if new user name has not been found because it's not
5103 * clear whether it's returned always. */
5104 static isds_error extract_credentials_delivery(struct isds_ctx *context,
5105 struct isds_credentials_delivery *credentials_delivery,
5106 xmlDocPtr response, const char *request_name) {
5107 isds_error err = IE_SUCCESS;
5108 xmlXPathContextPtr xpath_ctx = NULL;
5109 xmlXPathObjectPtr result = NULL;
5110 char *xpath_query = NULL;
5112 if (!context) return IE_INVALID_CONTEXT;
5113 if (credentials_delivery) {
5114 zfree(credentials_delivery->token);
5115 zfree(credentials_delivery->new_user_name);
5117 if (!response || !request_name || !*request_name) return IE_INVAL;
5120 /* Extract optional token */
5121 if (credentials_delivery) {
5122 xpath_ctx = xmlXPathNewContext(response);
5123 if (!xpath_ctx) {
5124 err = IE_ERROR;
5125 goto leave;
5127 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5128 err = IE_ERROR;
5129 goto leave;
5132 /* Verify root element */
5133 if (-1 == isds_asprintf(&xpath_query, "/isds:%sResponse",
5134 request_name)) {
5135 err = IE_NOMEM;
5136 goto leave;
5138 result = xmlXPathEvalExpression(BAD_CAST xpath_query, xpath_ctx);
5139 if (!result) {
5140 err = IE_ERROR;
5141 goto leave;
5143 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5144 char *request_name_locale = _isds_utf82locale(request_name);
5145 isds_log(ILF_ISDS, ILL_WARNING,
5146 _("Wrong element in ISDS response for %s request "
5147 "while extracting credentials delivery details\n"),
5148 request_name_locale);
5149 free(request_name_locale);
5150 err = IE_ERROR;
5151 goto leave;
5153 xpath_ctx->node = result->nodesetval->nodeTab[0];
5156 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
5157 * optional. */
5158 EXTRACT_STRING("isds:dbUserID", credentials_delivery->new_user_name);
5160 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery->token);
5161 if (!credentials_delivery->token) {
5162 char *request_name_locale = _isds_utf82locale(request_name);
5163 isds_log(ILF_ISDS, ILL_ERR,
5164 _("ISDS did not return token on %s request "
5165 "even if requested\n"), request_name_locale);
5166 free(request_name_locale);
5167 err = IE_ERROR;
5171 leave:
5172 free(xpath_query);
5173 xmlXPathFreeObject(result);
5174 xmlXPathFreeContext(xpath_ctx);
5176 return err;
5180 /* Build XSD:tCreateDBInput request type for box creating.
5181 * @context is session context
5182 * @request outputs built XML tree
5183 * @service_name is request name of SERVICE_DB_MANIPULATION service
5184 * @box is box description to create including single primary user (in case of
5185 * FO box type)
5186 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
5187 * box, or contact address of PFO box owner)
5188 * @former_names is optional former name of box owner. Pass NULL if otherwise.
5189 * @upper_box_id is optional ID of supper box if currently created box is
5190 * subordinated.
5191 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
5192 * don't care.
5193 * @credentials_delivery is valid pointer if ISDS should return token that box
5194 * owner can use to obtain his new credentials in on-line way. Then valid email
5195 * member value should be supplied.
5196 * @approval is optional external approval of box manipulation */
5197 static isds_error build_CreateDBInput_request(struct isds_ctx *context,
5198 xmlNodePtr *request, const xmlChar *service_name,
5199 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
5200 const xmlChar *former_names, const xmlChar *upper_box_id,
5201 const xmlChar *ceo_label,
5202 const struct isds_credentials_delivery *credentials_delivery,
5203 const struct isds_approval *approval) {
5204 isds_error err = IE_SUCCESS;
5205 xmlNsPtr isds_ns = NULL;
5206 xmlNodePtr node, dbPrimaryUsers;
5207 xmlChar *string = NULL;
5208 const struct isds_list *item;
5211 if (!context) return IE_INVALID_CONTEXT;
5212 if (!request || !service_name || service_name[0] == '\0' || !box)
5213 return IE_INVAL;
5216 /* Build CreateDataBox-similar request */
5217 *request = xmlNewNode(NULL, service_name);
5218 if (!*request) {
5219 char *service_name_locale = _isds_utf82locale((char*) service_name);
5220 isds_printf_message(context, _("Could build %s request"),
5221 service_name_locale);
5222 free(service_name_locale);
5223 return IE_ERROR;
5225 if (context->type == CTX_TYPE_TESTING_REQUEST_COLLECTOR) {
5226 isds_ns = xmlNewNs(*request, BAD_CAST ISDS1_NS, NULL);
5227 if (!isds_ns) {
5228 isds_log_message(context, _("Could not create ISDS1 name space"));
5229 xmlFreeNode(*request);
5230 return IE_ERROR;
5232 } else {
5233 isds_ns = xmlNewNs(*request, BAD_CAST ISDS_NS, NULL);
5234 if (!isds_ns) {
5235 isds_log_message(context, _("Could not create ISDS name space"));
5236 xmlFreeNode(*request);
5237 return IE_ERROR;
5240 xmlSetNs(*request, isds_ns);
5242 INSERT_ELEMENT(node, *request, "dbOwnerInfo");
5243 err = insert_DbOwnerInfo(context, box, node);
5244 if (err) goto leave;
5246 /* Insert users */
5247 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
5248 * verbose documentation allows none dbUserInfo */
5249 INSERT_ELEMENT(dbPrimaryUsers, *request, "dbPrimaryUsers");
5250 for (item = users; item; item = item->next) {
5251 if (item->data) {
5252 INSERT_ELEMENT(node, dbPrimaryUsers, "dbUserInfo");
5253 err = insert_DbUserInfo(context,
5254 (struct isds_DbUserInfo *) item->data, node);
5255 if (err) goto leave;
5259 INSERT_STRING(*request, "dbFormerNames", former_names);
5260 INSERT_STRING(*request, "dbUpperDBId", upper_box_id);
5261 INSERT_STRING(*request, "dbCEOLabel", ceo_label);
5263 err = insert_credentials_delivery(context, credentials_delivery, *request);
5264 if (err) goto leave;
5266 err = insert_GExtApproval(context, approval, *request);
5267 if (err) goto leave;
5269 leave:
5270 if (err) {
5271 xmlFreeNode(*request);
5272 *request = NULL;
5274 free(string);
5275 return err;
5277 #endif /* HAVE_LIBCURL */
5280 /* Create new box.
5281 * @context is session context
5282 * @box is box description to create including single primary user (in case of
5283 * FO box type). It outputs box ID assigned by ISDS in dbID element.
5284 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
5285 * box, or contact address of PFO box owner)
5286 * @former_names is optional former name of box owner. Pass NULL if you don't care.
5287 * @upper_box_id is optional ID of supper box if currently created box is
5288 * subordinated.
5289 * @ceo_label is optional title of OVM box owner (e.g. mayor)
5290 * @credentials_delivery is NULL if new password should be delivered off-line
5291 * to box owner. It is valid pointer if owner should obtain new password on-line
5292 * on dedicated web server. Then input @credentials_delivery.email value is
5293 * his e-mail address he must provide to dedicated web server together
5294 * with output reallocated @credentials_delivery.token member. Output
5295 * member @credentials_delivery.new_user_name is unused up on this call.
5296 * @approval is optional external approval of box manipulation
5297 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5298 * NULL, if you don't care.*/
5299 isds_error isds_add_box(struct isds_ctx *context,
5300 struct isds_DbOwnerInfo *box, const struct isds_list *users,
5301 const char *former_names, const char *upper_box_id,
5302 const char *ceo_label,
5303 struct isds_credentials_delivery *credentials_delivery,
5304 const struct isds_approval *approval, char **refnumber) {
5305 isds_error err = IE_SUCCESS;
5306 #if HAVE_LIBCURL
5307 xmlNodePtr request = NULL;
5308 xmlDocPtr response = NULL;
5309 xmlXPathContextPtr xpath_ctx = NULL;
5310 xmlXPathObjectPtr result = NULL;
5311 #endif
5314 if (!context) return IE_INVALID_CONTEXT;
5315 zfree(context->long_message);
5316 if (credentials_delivery) {
5317 zfree(credentials_delivery->token);
5318 zfree(credentials_delivery->new_user_name);
5320 if (!box) return IE_INVAL;
5322 #if HAVE_LIBCURL
5323 /* Scratch box ID */
5324 zfree(box->dbID);
5326 /* Build CreateDataBox request */
5327 err = build_CreateDBInput_request(context,
5328 &request, BAD_CAST "CreateDataBox",
5329 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
5330 (xmlChar *) ceo_label, credentials_delivery, approval);
5331 if (err) goto leave;
5333 /* Send it to server and process response */
5334 err = send_destroy_request_check_response(context,
5335 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
5336 &response, (xmlChar **) refnumber);
5338 /* Extract box ID */
5339 xpath_ctx = xmlXPathNewContext(response);
5340 if (!xpath_ctx) {
5341 err = IE_ERROR;
5342 goto leave;
5344 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5345 err = IE_ERROR;
5346 goto leave;
5348 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
5350 /* Extract optional token */
5351 err = extract_credentials_delivery(context, credentials_delivery, response,
5352 "CreateDataBox");
5354 leave:
5355 xmlXPathFreeObject(result);
5356 xmlXPathFreeContext(xpath_ctx);
5357 xmlFreeDoc(response);
5358 xmlFreeNode(request);
5360 if (!err) {
5361 isds_log(ILF_ISDS, ILL_DEBUG,
5362 _("CreateDataBox request processed by server successfully.\n"));
5364 #else /* not HAVE_LIBCURL */
5365 err = IE_NOTSUP;
5366 #endif
5368 return err;
5372 /* Notify ISDS about new PFO entity.
5373 * This function has no real effect.
5374 * @context is session context
5375 * @box is PFO description including single primary user.
5376 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
5377 * @former_names is optional undocumented string. Pass NULL if you don't care.
5378 * @upper_box_id is optional ID of supper box if currently created box is
5379 * subordinated.
5380 * @ceo_label is optional title of OVM box owner (e.g. mayor)
5381 * @approval is optional external approval of box manipulation
5382 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5383 * NULL, if you don't care.*/
5384 isds_error isds_add_pfoinfo(struct isds_ctx *context,
5385 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
5386 const char *former_names, const char *upper_box_id,
5387 const char *ceo_label, const struct isds_approval *approval,
5388 char **refnumber) {
5389 isds_error err = IE_SUCCESS;
5390 #if HAVE_LIBCURL
5391 xmlNodePtr request = NULL;
5392 #endif
5394 if (!context) return IE_INVALID_CONTEXT;
5395 zfree(context->long_message);
5396 if (!box) return IE_INVAL;
5398 #if HAVE_LIBCURL
5399 /* Build CreateDataBoxPFOInfo request */
5400 err = build_CreateDBInput_request(context,
5401 &request, BAD_CAST "CreateDataBoxPFOInfo",
5402 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
5403 (xmlChar *) ceo_label, NULL, approval);
5404 if (err) goto leave;
5406 /* Send it to server and process response */
5407 err = send_request_check_drop_response(context,
5408 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
5409 (xmlChar **) refnumber);
5410 /* XXX: XML Schema names output dbID element but textual documentation
5411 * states no box identifier is returned. */
5412 leave:
5413 xmlFreeNode(request);
5414 #else /* not HAVE_LIBCURL */
5415 err = IE_NOTSUP;
5416 #endif
5417 return err;
5421 /* Common implementation for removing given box.
5422 * @context is session context
5423 * @service_name is UTF-8 encoded name fo ISDS service
5424 * @box is box description to delete
5425 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
5426 * carry sane value. If NULL, do not inject this information into request.
5427 * @approval is optional external approval of box manipulation
5428 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5429 * NULL, if you don't care.*/
5430 isds_error _isds_delete_box_common(struct isds_ctx *context,
5431 const xmlChar *service_name,
5432 const struct isds_DbOwnerInfo *box, const struct tm *since,
5433 const struct isds_approval *approval, char **refnumber) {
5434 isds_error err = IE_SUCCESS;
5435 #if HAVE_LIBCURL
5436 xmlNsPtr isds_ns = NULL;
5437 xmlNodePtr request = NULL;
5438 xmlNodePtr node;
5439 xmlChar *string = NULL;
5440 #endif
5443 if (!context) return IE_INVALID_CONTEXT;
5444 zfree(context->long_message);
5445 if (!service_name || !*service_name || !box) return IE_INVAL;
5448 #if HAVE_LIBCURL
5449 /* Build DeleteDataBox(Promptly) request */
5450 request = xmlNewNode(NULL, service_name);
5451 if (!request) {
5452 char *service_name_locale = _isds_utf82locale((char*)service_name);
5453 isds_printf_message(context,
5454 _("Could build %s request"), service_name_locale);
5455 free(service_name_locale);
5456 return IE_ERROR;
5458 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5459 if(!isds_ns) {
5460 isds_log_message(context, _("Could not create ISDS name space"));
5461 xmlFreeNode(request);
5462 return IE_ERROR;
5464 xmlSetNs(request, isds_ns);
5466 INSERT_ELEMENT(node, request, "dbOwnerInfo");
5467 err = insert_DbOwnerInfo(context, box, node);
5468 if (err) goto leave;
5470 if (since) {
5471 err = tm2datestring(since, &string);
5472 if (err) {
5473 isds_log_message(context,
5474 _("Could not convert `since' argument to ISO date string"));
5475 goto leave;
5477 INSERT_STRING(request, "dbOwnerTerminationDate", string);
5478 zfree(string);
5481 err = insert_GExtApproval(context, approval, request);
5482 if (err) goto leave;
5485 /* Send it to server and process response */
5486 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
5487 service_name, &request, (xmlChar **) refnumber);
5489 leave:
5490 xmlFreeNode(request);
5491 free(string);
5492 #else /* not HAVE_LIBCURL */
5493 err = IE_NOTSUP;
5494 #endif
5495 return err;
5499 /* Remove given box permanently.
5500 * @context is session context
5501 * @box is box description to delete
5502 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
5503 * carry sane value.
5504 * @approval is optional external approval of box manipulation
5505 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5506 * NULL, if you don't care.*/
5507 isds_error isds_delete_box(struct isds_ctx *context,
5508 const struct isds_DbOwnerInfo *box, const struct tm *since,
5509 const struct isds_approval *approval, char **refnumber) {
5510 if (!context) return IE_INVALID_CONTEXT;
5511 zfree(context->long_message);
5512 if (!box || !since) return IE_INVAL;
5514 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBox",
5515 box, since, approval, refnumber);
5519 /* Undocumented function.
5520 * @context is session context
5521 * @box is box description to delete
5522 * @approval is optional external approval of box manipulation
5523 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5524 * NULL, if you don't care.*/
5525 isds_error isds_delete_box_promptly(struct isds_ctx *context,
5526 const struct isds_DbOwnerInfo *box,
5527 const struct isds_approval *approval, char **refnumber) {
5528 if (!context) return IE_INVALID_CONTEXT;
5529 zfree(context->long_message);
5530 if (!box) return IE_INVAL;
5532 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBoxPromptly",
5533 box, NULL, approval, refnumber);
5537 /* Update data about given box.
5538 * @context is session context
5539 * @old_box current box description
5540 * @new_box are updated data about @old_box
5541 * @approval is optional external approval of box manipulation
5542 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5543 * NULL, if you don't care.*/
5544 isds_error isds_UpdateDataBoxDescr(struct isds_ctx *context,
5545 const struct isds_DbOwnerInfo *old_box,
5546 const struct isds_DbOwnerInfo *new_box,
5547 const struct isds_approval *approval, char **refnumber) {
5548 isds_error err = IE_SUCCESS;
5549 #if HAVE_LIBCURL
5550 xmlNsPtr isds_ns = NULL;
5551 xmlNodePtr request = NULL;
5552 xmlNodePtr node;
5553 #endif
5556 if (!context) return IE_INVALID_CONTEXT;
5557 zfree(context->long_message);
5558 if (!old_box || !new_box) return IE_INVAL;
5561 #if HAVE_LIBCURL
5562 /* Build UpdateDataBoxDescr request */
5563 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxDescr");
5564 if (!request) {
5565 isds_log_message(context,
5566 _("Could build UpdateDataBoxDescr request"));
5567 return IE_ERROR;
5569 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5570 if(!isds_ns) {
5571 isds_log_message(context, _("Could not create ISDS name space"));
5572 xmlFreeNode(request);
5573 return IE_ERROR;
5575 xmlSetNs(request, isds_ns);
5577 INSERT_ELEMENT(node, request, "dbOldOwnerInfo");
5578 err = insert_DbOwnerInfo(context, old_box, node);
5579 if (err) goto leave;
5581 INSERT_ELEMENT(node, request, "dbNewOwnerInfo");
5582 err = insert_DbOwnerInfo(context, new_box, node);
5583 if (err) goto leave;
5585 err = insert_GExtApproval(context, approval, request);
5586 if (err) goto leave;
5589 /* Send it to server and process response */
5590 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
5591 BAD_CAST "UpdateDataBoxDescr", &request, (xmlChar **) refnumber);
5593 leave:
5594 xmlFreeNode(request);
5595 #else /* not HAVE_LIBCURL */
5596 err = IE_NOTSUP;
5597 #endif
5599 return err;
5603 #if HAVE_LIBCURL
5604 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
5605 * code
5606 * @context is session context
5607 * @service is SOAP service
5608 * @service_name is name of request in @service
5609 * @box_id is box ID of interest
5610 * @approval is optional external approval of box manipulation
5611 * @response is server SOAP body response as XML document
5612 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5613 * NULL, if you don't care.
5614 * @return error coded from lower layer, context message will be set up
5615 * appropriately. */
5616 static isds_error build_send_dbid_request_check_response(
5617 struct isds_ctx *context, const isds_service service,
5618 const xmlChar *service_name, const xmlChar *box_id,
5619 const struct isds_approval *approval,
5620 xmlDocPtr *response, xmlChar **refnumber) {
5622 isds_error err = IE_SUCCESS;
5623 char *service_name_locale = NULL, *box_id_locale = NULL;
5624 xmlNodePtr request = NULL, node;
5625 xmlNsPtr isds_ns = NULL;
5627 if (!context) return IE_INVALID_CONTEXT;
5628 if (!service_name || !box_id) return IE_INVAL;
5629 if (!response) return IE_INVAL;
5631 /* Free output argument */
5632 xmlFreeDoc(*response); *response = NULL;
5634 /* Prepare strings */
5635 service_name_locale = _isds_utf82locale((char*)service_name);
5636 if (!service_name_locale) {
5637 err = IE_NOMEM;
5638 goto leave;
5640 box_id_locale = _isds_utf82locale((char*)box_id);
5641 if (!box_id_locale) {
5642 err = IE_NOMEM;
5643 goto leave;
5646 /* Build request */
5647 request = xmlNewNode(NULL, service_name);
5648 if (!request) {
5649 isds_printf_message(context,
5650 _("Could not build %s request for %s box"), service_name_locale,
5651 box_id_locale);
5652 err = IE_ERROR;
5653 goto leave;
5655 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5656 if(!isds_ns) {
5657 isds_log_message(context, _("Could not create ISDS name space"));
5658 err = IE_ERROR;
5659 goto leave;
5661 xmlSetNs(request, isds_ns);
5663 /* Add XSD:tIdDbInput children */
5664 INSERT_STRING(request, "dbID", box_id);
5665 err = insert_GExtApproval(context, approval, request);
5666 if (err) goto leave;
5668 /* Send request and check response*/
5669 err = send_destroy_request_check_response(context,
5670 service, service_name, &request, response, refnumber);
5672 leave:
5673 free(service_name_locale);
5674 free(box_id_locale);
5675 xmlFreeNode(request);
5676 return err;
5678 #endif /* HAVE_LIBCURL */
5681 /* Get data about all users assigned to given box.
5682 * @context is session context
5683 * @box_id is box ID
5684 * @users is automatically reallocated list of struct isds_DbUserInfo */
5685 isds_error isds_GetDataBoxUsers(struct isds_ctx *context, const char *box_id,
5686 struct isds_list **users) {
5687 isds_error err = IE_SUCCESS;
5688 #if HAVE_LIBCURL
5689 xmlDocPtr response = NULL;
5690 xmlXPathContextPtr xpath_ctx = NULL;
5691 xmlXPathObjectPtr result = NULL;
5692 int i;
5693 struct isds_list *item, *prev_item = NULL;
5694 #endif
5696 if (!context) return IE_INVALID_CONTEXT;
5697 zfree(context->long_message);
5698 if (!users || !box_id) return IE_INVAL;
5699 isds_list_free(users);
5702 #if HAVE_LIBCURL
5703 /* Do request and check for success */
5704 err = build_send_dbid_request_check_response(context,
5705 SERVICE_DB_MANIPULATION, BAD_CAST "GetDataBoxUsers",
5706 BAD_CAST box_id, NULL, &response, NULL);
5707 if (err) goto leave;
5710 /* Extract data */
5711 /* Prepare structure */
5712 xpath_ctx = xmlXPathNewContext(response);
5713 if (!xpath_ctx) {
5714 err = IE_ERROR;
5715 goto leave;
5717 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5718 err = IE_ERROR;
5719 goto leave;
5722 /* Set context node */
5723 result = xmlXPathEvalExpression(BAD_CAST
5724 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
5725 xpath_ctx);
5726 if (!result) {
5727 err = IE_ERROR;
5728 goto leave;
5730 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5731 /* Iterate over all users */
5732 for (i = 0; i < result->nodesetval->nodeNr; i++) {
5734 /* Prepare structure */
5735 item = calloc(1, sizeof(*item));
5736 if (!item) {
5737 err = IE_NOMEM;
5738 goto leave;
5740 item->destructor = (void(*)(void**))isds_DbUserInfo_free;
5741 if (i == 0) *users = item;
5742 else prev_item->next = item;
5743 prev_item = item;
5745 /* Extract it */
5746 xpath_ctx->node = result->nodesetval->nodeTab[i];
5747 err = extract_DbUserInfo(context,
5748 (struct isds_DbUserInfo **) (&item->data), xpath_ctx);
5749 if (err) goto leave;
5753 leave:
5754 if (err) {
5755 isds_list_free(users);
5758 xmlXPathFreeObject(result);
5759 xmlXPathFreeContext(xpath_ctx);
5760 xmlFreeDoc(response);
5762 if (!err)
5763 isds_log(ILF_ISDS, ILL_DEBUG,
5764 _("GetDataBoxUsers request processed by server "
5765 "successfully.\n"));
5766 #else /* not HAVE_LIBCURL */
5767 err = IE_NOTSUP;
5768 #endif
5770 return err;
5774 /* Update data about user assigned to given box.
5775 * @context is session context
5776 * @box is box identification
5777 * @old_user identifies user to update
5778 * @new_user are updated data about @old_user
5779 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5780 * NULL, if you don't care.*/
5781 isds_error isds_UpdateDataBoxUser(struct isds_ctx *context,
5782 const struct isds_DbOwnerInfo *box,
5783 const struct isds_DbUserInfo *old_user,
5784 const struct isds_DbUserInfo *new_user,
5785 char **refnumber) {
5786 isds_error err = IE_SUCCESS;
5787 #if HAVE_LIBCURL
5788 xmlNsPtr isds_ns = NULL;
5789 xmlNodePtr request = NULL;
5790 xmlNodePtr node;
5791 #endif
5794 if (!context) return IE_INVALID_CONTEXT;
5795 zfree(context->long_message);
5796 if (!box || !old_user || !new_user) return IE_INVAL;
5799 #if HAVE_LIBCURL
5800 /* Build UpdateDataBoxUser request */
5801 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxUser");
5802 if (!request) {
5803 isds_log_message(context,
5804 _("Could build UpdateDataBoxUser request"));
5805 return IE_ERROR;
5807 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5808 if(!isds_ns) {
5809 isds_log_message(context, _("Could not create ISDS name space"));
5810 xmlFreeNode(request);
5811 return IE_ERROR;
5813 xmlSetNs(request, isds_ns);
5815 INSERT_ELEMENT(node, request, "dbOwnerInfo");
5816 err = insert_DbOwnerInfo(context, box, node);
5817 if (err) goto leave;
5819 INSERT_ELEMENT(node, request, "dbOldUserInfo");
5820 err = insert_DbUserInfo(context, old_user, node);
5821 if (err) goto leave;
5823 INSERT_ELEMENT(node, request, "dbNewUserInfo");
5824 err = insert_DbUserInfo(context, new_user, node);
5825 if (err) goto leave;
5827 /* Send it to server and process response */
5828 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
5829 BAD_CAST "UpdateDataBoxUser", &request, (xmlChar **) refnumber);
5831 leave:
5832 xmlFreeNode(request);
5833 #else /* not HAVE_LIBCURL */
5834 err = IE_NOTSUP;
5835 #endif
5837 return err;
5841 /* Undocumented function.
5842 * @context is session context
5843 * @box_id is UTF-8 encoded box identifier
5844 * @token is UTF-8 encoded temporary password
5845 * @user_id outputs UTF-8 encoded reallocated user identifier
5846 * @password outpus UTF-8 encoded reallocated user password
5847 * Output arguments will be nulled in case of error */
5848 isds_error isds_activate(struct isds_ctx *context,
5849 const char *box_id, const char *token,
5850 char **user_id, char **password) {
5851 isds_error err = IE_SUCCESS;
5852 #if HAVE_LIBCURL
5853 xmlNsPtr isds_ns = NULL;
5854 xmlNodePtr request = NULL, node;
5855 xmlDocPtr response = NULL;
5856 xmlXPathContextPtr xpath_ctx = NULL;
5857 xmlXPathObjectPtr result = NULL;
5858 #endif
5861 if (!context) return IE_INVALID_CONTEXT;
5862 zfree(context->long_message);
5864 if (user_id) zfree(*user_id);
5865 if (password) zfree(*password);
5867 if (!box_id || !token || !user_id || !password) return IE_INVAL;
5870 #if HAVE_LIBCURL
5871 /* Build Activate request */
5872 request = xmlNewNode(NULL, BAD_CAST "Activate");
5873 if (!request) {
5874 isds_log_message(context, _("Could build Activate request"));
5875 return IE_ERROR;
5877 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5878 if(!isds_ns) {
5879 isds_log_message(context, _("Could not create ISDS name space"));
5880 xmlFreeNode(request);
5881 return IE_ERROR;
5883 xmlSetNs(request, isds_ns);
5885 INSERT_STRING(request, "dbAccessDataId", token);
5886 CHECK_FOR_STRING_LENGTH(box_id, 7, 7, "dbID");
5887 INSERT_STRING(request, "dbID", box_id);
5890 /* Send request and check response*/
5891 err = send_destroy_request_check_response(context,
5892 SERVICE_DB_MANIPULATION, BAD_CAST "Activate", &request,
5893 &response, NULL);
5894 if (err) goto leave;
5897 /* Extract data */
5898 xpath_ctx = xmlXPathNewContext(response);
5899 if (!xpath_ctx) {
5900 err = IE_ERROR;
5901 goto leave;
5903 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5904 err = IE_ERROR;
5905 goto leave;
5907 result = xmlXPathEvalExpression(BAD_CAST "/isds:ActivateResponse",
5908 xpath_ctx);
5909 if (!result) {
5910 err = IE_ERROR;
5911 goto leave;
5913 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5914 isds_log_message(context, _("Missing ActivateResponse element"));
5915 err = IE_ISDS;
5916 goto leave;
5918 if (result->nodesetval->nodeNr > 1) {
5919 isds_log_message(context, _("Multiple ActivateResponse element"));
5920 err = IE_ISDS;
5921 goto leave;
5923 xpath_ctx->node = result->nodesetval->nodeTab[0];
5924 xmlXPathFreeObject(result); result = NULL;
5926 EXTRACT_STRING("isds:userId", *user_id);
5927 if (!*user_id)
5928 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
5929 "but did not return `userId' element.\n"));
5931 EXTRACT_STRING("isds:password", *password);
5932 if (!*password)
5933 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
5934 "but did not return `password' element.\n"));
5936 leave:
5937 xmlXPathFreeObject(result);
5938 xmlXPathFreeContext(xpath_ctx);
5939 xmlFreeDoc(response);
5940 xmlFreeNode(request);
5942 if (!err)
5943 isds_log(ILF_ISDS, ILL_DEBUG,
5944 _("Activate request processed by server successfully.\n"));
5945 #else /* not HAVE_LIBCURL */
5946 err = IE_NOTSUP;
5947 #endif
5949 return err;
5953 /* Reset credentials of user assigned to given box.
5954 * @context is session context
5955 * @box is box identification
5956 * @user identifies user to reset password
5957 * @fee_paid is true if fee has been paid, false otherwise
5958 * @approval is optional external approval of box manipulation
5959 * @credentials_delivery is NULL if new password should be delivered off-line
5960 * to the user. It is valid pointer if user should obtain new password on-line
5961 * on dedicated web server. Then input @credentials_delivery.email value is
5962 * user's e-mail address user must provide to dedicated web server together
5963 * with @credentials_delivery.token. The output reallocated token user needs
5964 * to use to authorize on the web server to view his new password. Output
5965 * reallocated @credentials_delivery.new_user_name is user's log-in name that
5966 * ISDS changed up on this call. (No reason why server could change the name
5967 * is known now.)
5968 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5969 * NULL, if you don't care.*/
5970 isds_error isds_reset_password(struct isds_ctx *context,
5971 const struct isds_DbOwnerInfo *box,
5972 const struct isds_DbUserInfo *user,
5973 const _Bool fee_paid, const struct isds_approval *approval,
5974 struct isds_credentials_delivery *credentials_delivery,
5975 char **refnumber) {
5976 isds_error err = IE_SUCCESS;
5977 #if HAVE_LIBCURL
5978 xmlNsPtr isds_ns = NULL;
5979 xmlNodePtr request = NULL, node;
5980 xmlDocPtr response = NULL;
5981 #endif
5984 if (!context) return IE_INVALID_CONTEXT;
5985 zfree(context->long_message);
5987 if (credentials_delivery) {
5988 zfree(credentials_delivery->token);
5989 zfree(credentials_delivery->new_user_name);
5991 if (!box || !user) return IE_INVAL;
5994 #if HAVE_LIBCURL
5995 /* Build NewAccessData request */
5996 request = xmlNewNode(NULL, BAD_CAST "NewAccessData");
5997 if (!request) {
5998 isds_log_message(context,
5999 _("Could build NewAccessData request"));
6000 return IE_ERROR;
6002 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6003 if(!isds_ns) {
6004 isds_log_message(context, _("Could not create ISDS name space"));
6005 xmlFreeNode(request);
6006 return IE_ERROR;
6008 xmlSetNs(request, isds_ns);
6010 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6011 err = insert_DbOwnerInfo(context, box, node);
6012 if (err) goto leave;
6014 INSERT_ELEMENT(node, request, "dbUserInfo");
6015 err = insert_DbUserInfo(context, user, node);
6016 if (err) goto leave;
6018 INSERT_SCALAR_BOOLEAN(request, "dbFeePaid", fee_paid);
6020 err = insert_credentials_delivery(context, credentials_delivery, request);
6021 if (err) goto leave;
6023 err = insert_GExtApproval(context, approval, request);
6024 if (err) goto leave;
6026 /* Send request and check response*/
6027 err = send_destroy_request_check_response(context,
6028 SERVICE_DB_MANIPULATION, BAD_CAST "NewAccessData", &request,
6029 &response, (xmlChar **) refnumber);
6030 if (err) goto leave;
6033 /* Extract optional token */
6034 err = extract_credentials_delivery(context, credentials_delivery,
6035 response, "NewAccessData");
6037 leave:
6038 xmlFreeDoc(response);
6039 xmlFreeNode(request);
6041 if (!err)
6042 isds_log(ILF_ISDS, ILL_DEBUG,
6043 _("NewAccessData request processed by server "
6044 "successfully.\n"));
6045 #else /* not HAVE_LIBCURL */
6046 err = IE_NOTSUP;
6047 #endif
6049 return err;
6053 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
6054 * code, destroy response and log success.
6055 * @context is ISDS session context.
6056 * @service_name is name of SERVICE_DB_MANIPULATION service
6057 * @box is box identification
6058 * @user identifies user to remove
6059 * @credentials_delivery is NULL if new user's password should be delivered
6060 * off-line to the user. It is valid pointer if user should obtain new
6061 * password on-line on dedicated web server. Then input
6062 * @credentials_delivery.email value is user's e-mail address user must
6063 * provide to dedicated web server together with @credentials_delivery.token.
6064 * The output reallocated token user needs to use to authorize on the web
6065 * server to view his new password. Output reallocated
6066 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6067 * assingned or changed up on this call.
6068 * @approval is optional external approval of box manipulation
6069 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6070 * NULL, if you don't care. */
6071 static isds_error build_send_manipulationboxuser_request_check_drop_response(
6072 struct isds_ctx *context, const xmlChar *service_name,
6073 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6074 struct isds_credentials_delivery *credentials_delivery,
6075 const struct isds_approval *approval, xmlChar **refnumber) {
6076 isds_error err = IE_SUCCESS;
6077 #if HAVE_LIBCURL
6078 xmlNsPtr isds_ns = NULL;
6079 xmlNodePtr request = NULL, node;
6080 xmlDocPtr response = NULL;
6081 #endif
6084 if (!context) return IE_INVALID_CONTEXT;
6085 zfree(context->long_message);
6086 if (credentials_delivery) {
6087 zfree(credentials_delivery->token);
6088 zfree(credentials_delivery->new_user_name);
6090 if (!service_name || service_name[0] == '\0' || !box || !user)
6091 return IE_INVAL;
6094 #if HAVE_LIBCURL
6095 /* Build NewAccessData or similar request */
6096 request = xmlNewNode(NULL, service_name);
6097 if (!request) {
6098 char *service_name_locale = _isds_utf82locale((char *) service_name);
6099 isds_printf_message(context, _("Could not build %s request"),
6100 service_name_locale);
6101 free(service_name_locale);
6102 return IE_ERROR;
6104 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6105 if(!isds_ns) {
6106 isds_log_message(context, _("Could not create ISDS name space"));
6107 xmlFreeNode(request);
6108 return IE_ERROR;
6110 xmlSetNs(request, isds_ns);
6112 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6113 err = insert_DbOwnerInfo(context, box, node);
6114 if (err) goto leave;
6116 INSERT_ELEMENT(node, request, "dbUserInfo");
6117 err = insert_DbUserInfo(context, user, node);
6118 if (err) goto leave;
6120 err = insert_credentials_delivery(context, credentials_delivery, request);
6121 if (err) goto leave;
6123 err = insert_GExtApproval(context, approval, request);
6124 if (err) goto leave;
6127 /* Send request and check response*/
6128 err = send_destroy_request_check_response(context,
6129 SERVICE_DB_MANIPULATION, service_name, &request, &response, refnumber);
6131 xmlFreeNode(request);
6132 request = NULL;
6134 /* Pick up credentials_delivery if requested */
6135 err = extract_credentials_delivery(context, credentials_delivery, response,
6136 (char *)service_name);
6138 leave:
6139 xmlFreeDoc(response);
6140 if (request) xmlFreeNode(request);
6142 if (!err) {
6143 char *service_name_locale = _isds_utf82locale((char *) service_name);
6144 isds_log(ILF_ISDS, ILL_DEBUG,
6145 _("%s request processed by server successfully.\n"),
6146 service_name_locale);
6147 free(service_name_locale);
6149 #else /* not HAVE_LIBCURL */
6150 err = IE_NOTSUP;
6151 #endif
6153 return err;
6157 /* Assign new user to given box.
6158 * @context is session context
6159 * @box is box identification
6160 * @user defines new user to add
6161 * @credentials_delivery is NULL if new user's password should be delivered
6162 * off-line to the user. It is valid pointer if user should obtain new
6163 * password on-line on dedicated web server. Then input
6164 * @credentials_delivery.email value is user's e-mail address user must
6165 * provide to dedicated web server together with @credentials_delivery.token.
6166 * The output reallocated token user needs to use to authorize on the web
6167 * server to view his new password. Output reallocated
6168 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6169 * assingned up on this call.
6170 * @approval is optional external approval of box manipulation
6171 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6172 * NULL, if you don't care.*/
6173 isds_error isds_add_user(struct isds_ctx *context,
6174 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6175 struct isds_credentials_delivery *credentials_delivery,
6176 const struct isds_approval *approval, char **refnumber) {
6177 return build_send_manipulationboxuser_request_check_drop_response(context,
6178 BAD_CAST "AddDataBoxUser", box, user, credentials_delivery,
6179 approval, (xmlChar **) refnumber);
6183 /* Remove user assigned to given box.
6184 * @context is session context
6185 * @box is box identification
6186 * @user identifies user to remove
6187 * @approval is optional external approval of box manipulation
6188 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6189 * NULL, if you don't care.*/
6190 isds_error isds_delete_user(struct isds_ctx *context,
6191 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6192 const struct isds_approval *approval, char **refnumber) {
6193 return build_send_manipulationboxuser_request_check_drop_response(context,
6194 BAD_CAST "DeleteDataBoxUser", box, user, NULL, approval,
6195 (xmlChar **) refnumber);
6199 /* Get list of boxes in ZIP archive.
6200 * @context is session context
6201 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
6202 * System recognizes following values currently: ALL (all boxes), UPG
6203 * (effectively OVM boxes), OVM (OVM gross type boxes), OPN (boxes allowing
6204 * receiving commercial messages). This argument is a string because
6205 * specification states new values can appear in the future. Not all list
6206 * types are available to all users.
6207 * @buffer is automatically reallocated memory to store the list of boxes. The
6208 * list is zipped CSV file.
6209 * @buffer_length is size of @buffer data in bytes.
6210 * In case of error @buffer will be freed and @buffer_length will be
6211 * undefined.*/
6212 isds_error isds_get_box_list_archive(struct isds_ctx *context,
6213 const char *list_identifier, void **buffer, size_t *buffer_length) {
6214 isds_error err = IE_SUCCESS;
6215 #if HAVE_LIBCURL
6216 xmlNsPtr isds_ns = NULL;
6217 xmlNodePtr request = NULL, node;
6218 xmlDocPtr response = NULL;
6219 xmlXPathContextPtr xpath_ctx = NULL;
6220 xmlXPathObjectPtr result = NULL;
6221 char *string = NULL;
6222 #endif
6225 if (!context) return IE_INVALID_CONTEXT;
6226 zfree(context->long_message);
6227 if (buffer) zfree(*buffer);
6228 if (!buffer || !buffer_length) return IE_INVAL;
6231 #if HAVE_LIBCURL
6232 /* Check if connection is established
6233 * TODO: This check should be done downstairs. */
6234 if (!context->curl) return IE_CONNECTION_CLOSED;
6237 /* Build AuthenticateMessage request */
6238 request = xmlNewNode(NULL, BAD_CAST "GetDataBoxList");
6239 if (!request) {
6240 isds_log_message(context,
6241 _("Could not build GetDataBoxList request"));
6242 return IE_ERROR;
6244 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6245 if(!isds_ns) {
6246 isds_log_message(context, _("Could not create ISDS name space"));
6247 xmlFreeNode(request);
6248 return IE_ERROR;
6250 xmlSetNs(request, isds_ns);
6251 INSERT_STRING(request, "dblType", list_identifier);
6253 /* Send request to server and process response */
6254 err = send_destroy_request_check_response(context,
6255 SERVICE_DB_SEARCH, BAD_CAST "GetDataBoxList", &request,
6256 &response, NULL);
6257 if (err) goto leave;
6260 /* Extract Base-64 encoded ZIP file */
6261 xpath_ctx = xmlXPathNewContext(response);
6262 if (!xpath_ctx) {
6263 err = IE_ERROR;
6264 goto leave;
6266 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6267 err = IE_ERROR;
6268 goto leave;
6270 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string);
6272 /* Decode non-empty archive */
6273 if (string && string[0] != '\0') {
6274 *buffer_length = _isds_b64decode(string, buffer);
6275 if (*buffer_length == (size_t) -1) {
6276 isds_printf_message(context,
6277 _("Error while Base64-decoding box list archive"));
6278 err = IE_ERROR;
6279 goto leave;
6284 leave:
6285 free(string);
6286 xmlXPathFreeObject(result);
6287 xmlXPathFreeContext(xpath_ctx);
6288 xmlFreeDoc(response);
6289 xmlFreeNode(request);
6291 if (!err) {
6292 isds_log(ILF_ISDS, ILL_DEBUG, _("GetDataBoxList request "
6293 "processed by server successfully.\n"));
6295 #else /* not HAVE_LIBCURL */
6296 err = IE_NOTSUP;
6297 #endif
6299 return err;
6303 /* Find boxes suiting given criteria.
6304 * @criteria is filter. You should fill in at least some members.
6305 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
6306 * possibly empty. Input NULL or valid old structure.
6307 * @return:
6308 * IE_SUCCESS if search succeeded, @boxes contains useful data
6309 * IE_NOEXIST if no such box exists, @boxes will be NULL
6310 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
6311 * contains still valid data
6312 * other code if something bad happens. @boxes will be NULL. */
6313 isds_error isds_FindDataBox(struct isds_ctx *context,
6314 const struct isds_DbOwnerInfo *criteria,
6315 struct isds_list **boxes) {
6316 isds_error err = IE_SUCCESS;
6317 #if HAVE_LIBCURL
6318 _Bool truncated = 0;
6319 xmlNsPtr isds_ns = NULL;
6320 xmlNodePtr request = NULL;
6321 xmlDocPtr response = NULL;
6322 xmlChar *code = NULL, *message = NULL;
6323 xmlNodePtr db_owner_info;
6324 xmlXPathContextPtr xpath_ctx = NULL;
6325 xmlXPathObjectPtr result = NULL;
6326 xmlChar *string = NULL;
6327 #endif
6330 if (!context) return IE_INVALID_CONTEXT;
6331 zfree(context->long_message);
6332 if (!boxes) return IE_INVAL;
6333 isds_list_free(boxes);
6335 if (!criteria) {
6336 return IE_INVAL;
6339 #if HAVE_LIBCURL
6340 /* Check if connection is established
6341 * TODO: This check should be done downstairs. */
6342 if (!context->curl) return IE_CONNECTION_CLOSED;
6345 /* Build FindDataBox request */
6346 request = xmlNewNode(NULL, BAD_CAST "FindDataBox");
6347 if (!request) {
6348 isds_log_message(context,
6349 _("Could build FindDataBox request"));
6350 return IE_ERROR;
6352 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6353 if(!isds_ns) {
6354 isds_log_message(context, _("Could not create ISDS name space"));
6355 xmlFreeNode(request);
6356 return IE_ERROR;
6358 xmlSetNs(request, isds_ns);
6359 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
6360 if (!db_owner_info) {
6361 isds_log_message(context, _("Could not add dbOwnerInfo child to "
6362 "FindDataBox element"));
6363 xmlFreeNode(request);
6364 return IE_ERROR;
6367 err = insert_DbOwnerInfo(context, criteria, db_owner_info);
6368 if (err) goto leave;
6371 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending FindDataBox request to ISDS\n"));
6373 /* Sent request */
6374 err = isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
6376 /* Destroy request */
6377 xmlFreeNode(request); request = NULL;
6379 if (err) {
6380 isds_log(ILF_ISDS, ILL_DEBUG,
6381 _("Processing ISDS response on FindDataBox "
6382 "request failed\n"));
6383 goto leave;
6386 /* Check for response status */
6387 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
6388 &code, &message, NULL);
6389 if (err) {
6390 isds_log(ILF_ISDS, ILL_DEBUG,
6391 _("ISDS response on FindDataBox request is missing status\n"));
6392 goto leave;
6395 /* Request processed, but nothing found */
6396 if (!xmlStrcmp(code, BAD_CAST "0002") ||
6397 !xmlStrcmp(code, BAD_CAST "5001")) {
6398 char *code_locale = _isds_utf82locale((char*)code);
6399 char *message_locale = _isds_utf82locale((char*)message);
6400 isds_log(ILF_ISDS, ILL_DEBUG,
6401 _("Server did not found any box on FindDataBox request "
6402 "(code=%s, message=%s)\n"), code_locale, message_locale);
6403 isds_log_message(context, message_locale);
6404 free(code_locale);
6405 free(message_locale);
6406 err = IE_NOEXIST;
6407 goto leave;
6410 /* Warning, not a error */
6411 if (!xmlStrcmp(code, BAD_CAST "0003")) {
6412 char *code_locale = _isds_utf82locale((char*)code);
6413 char *message_locale = _isds_utf82locale((char*)message);
6414 isds_log(ILF_ISDS, ILL_DEBUG,
6415 _("Server truncated response on FindDataBox request "
6416 "(code=%s, message=%s)\n"), code_locale, message_locale);
6417 isds_log_message(context, message_locale);
6418 free(code_locale);
6419 free(message_locale);
6420 truncated = 1;
6423 /* Other error */
6424 else if (xmlStrcmp(code, BAD_CAST "0000")) {
6425 char *code_locale = _isds_utf82locale((char*)code);
6426 char *message_locale = _isds_utf82locale((char*)message);
6427 isds_log(ILF_ISDS, ILL_DEBUG,
6428 _("Server refused FindDataBox request "
6429 "(code=%s, message=%s)\n"), code_locale, message_locale);
6430 isds_log_message(context, message_locale);
6431 free(code_locale);
6432 free(message_locale);
6433 err = IE_ISDS;
6434 goto leave;
6437 xpath_ctx = xmlXPathNewContext(response);
6438 if (!xpath_ctx) {
6439 err = IE_ERROR;
6440 goto leave;
6442 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6443 err = IE_ERROR;
6444 goto leave;
6447 /* Extract boxes if they present */
6448 result = xmlXPathEvalExpression(BAD_CAST
6449 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
6450 xpath_ctx);
6451 if (!result) {
6452 err = IE_ERROR;
6453 goto leave;
6455 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6456 struct isds_list *item, *prev_item = NULL;
6457 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
6458 item = calloc(1, sizeof(*item));
6459 if (!item) {
6460 err = IE_NOMEM;
6461 goto leave;
6464 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
6465 if (i == 0) *boxes = item;
6466 else prev_item->next = item;
6467 prev_item = item;
6469 xpath_ctx->node = result->nodesetval->nodeTab[i];
6470 err = extract_DbOwnerInfo(context,
6471 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
6472 if (err) goto leave;
6476 leave:
6477 if (err) {
6478 isds_list_free(boxes);
6479 } else {
6480 if (truncated) err = IE_2BIG;
6483 free(string);
6484 xmlFreeNode(request);
6485 xmlXPathFreeObject(result);
6486 xmlXPathFreeContext(xpath_ctx);
6488 free(code);
6489 free(message);
6490 xmlFreeDoc(response);
6492 if (!err)
6493 isds_log(ILF_ISDS, ILL_DEBUG,
6494 _("FindDataBox request processed by server successfully.\n"));
6495 #else /* not HAVE_LIBCURL */
6496 err = IE_NOTSUP;
6497 #endif
6499 return err;
6503 /* Get status of a box.
6504 * @context is ISDS session context.
6505 * @box_id is UTF-8 encoded box identifier as zero terminated string
6506 * @box_status is return value of box status.
6507 * @return:
6508 * IE_SUCCESS if box has been found and its status retrieved
6509 * IE_NOEXIST if box is not known to ISDS server
6510 * or other appropriate error.
6511 * You can use isds_DbState to enumerate box status. However out of enum
6512 * range value can be returned too. This is feature because ISDS
6513 * specification leaves the set of values open.
6514 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
6515 * the box has been deleted, but ISDS still lists its former existence. */
6516 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
6517 long int *box_status) {
6518 isds_error err = IE_SUCCESS;
6519 #if HAVE_LIBCURL
6520 xmlNsPtr isds_ns = NULL;
6521 xmlNodePtr request = NULL, db_id;
6522 xmlDocPtr response = NULL;
6523 xmlChar *code = NULL, *message = NULL;
6524 xmlXPathContextPtr xpath_ctx = NULL;
6525 xmlXPathObjectPtr result = NULL;
6526 xmlChar *string = NULL;
6527 #endif
6529 if (!context) return IE_INVALID_CONTEXT;
6530 zfree(context->long_message);
6531 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
6533 #if HAVE_LIBCURL
6534 /* Check if connection is established
6535 * TODO: This check should be done downstairs. */
6536 if (!context->curl) return IE_CONNECTION_CLOSED;
6539 /* Build CheckDataBox request */
6540 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
6541 if (!request) {
6542 isds_log_message(context,
6543 _("Could build CheckDataBox request"));
6544 return IE_ERROR;
6546 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6547 if(!isds_ns) {
6548 isds_log_message(context, _("Could not create ISDS name space"));
6549 xmlFreeNode(request);
6550 return IE_ERROR;
6552 xmlSetNs(request, isds_ns);
6553 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
6554 if (!db_id) {
6555 isds_log_message(context, _("Could not add dbID child to "
6556 "CheckDataBox element"));
6557 xmlFreeNode(request);
6558 return IE_ERROR;
6562 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CheckDataBox request to ISDS\n"));
6564 /* Sent request */
6565 err = isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
6567 /* Destroy request */
6568 xmlFreeNode(request);
6570 if (err) {
6571 isds_log(ILF_ISDS, ILL_DEBUG,
6572 _("Processing ISDS response on CheckDataBox "
6573 "request failed\n"));
6574 goto leave;
6577 /* Check for response status */
6578 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
6579 &code, &message, NULL);
6580 if (err) {
6581 isds_log(ILF_ISDS, ILL_DEBUG,
6582 _("ISDS response on CheckDataBox request is missing status\n"));
6583 goto leave;
6586 /* Request processed, but nothing found */
6587 if (!xmlStrcmp(code, BAD_CAST "5001")) {
6588 char *box_id_locale = _isds_utf82locale((char*)box_id);
6589 char *code_locale = _isds_utf82locale((char*)code);
6590 char *message_locale = _isds_utf82locale((char*)message);
6591 isds_log(ILF_ISDS, ILL_DEBUG,
6592 _("Server did not found box %s on CheckDataBox request "
6593 "(code=%s, message=%s)\n"),
6594 box_id_locale, code_locale, message_locale);
6595 isds_log_message(context, message_locale);
6596 free(box_id_locale);
6597 free(code_locale);
6598 free(message_locale);
6599 err = IE_NOEXIST;
6600 goto leave;
6603 /* Other error */
6604 else if (xmlStrcmp(code, BAD_CAST "0000")) {
6605 char *code_locale = _isds_utf82locale((char*)code);
6606 char *message_locale = _isds_utf82locale((char*)message);
6607 isds_log(ILF_ISDS, ILL_DEBUG,
6608 _("Server refused CheckDataBox request "
6609 "(code=%s, message=%s)\n"), code_locale, message_locale);
6610 isds_log_message(context, message_locale);
6611 free(code_locale);
6612 free(message_locale);
6613 err = IE_ISDS;
6614 goto leave;
6617 /* Extract data */
6618 xpath_ctx = xmlXPathNewContext(response);
6619 if (!xpath_ctx) {
6620 err = IE_ERROR;
6621 goto leave;
6623 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6624 err = IE_ERROR;
6625 goto leave;
6627 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
6628 xpath_ctx);
6629 if (!result) {
6630 err = IE_ERROR;
6631 goto leave;
6633 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6634 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
6635 err = IE_ISDS;
6636 goto leave;
6638 if (result->nodesetval->nodeNr > 1) {
6639 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
6640 err = IE_ISDS;
6641 goto leave;
6643 xpath_ctx->node = result->nodesetval->nodeTab[0];
6644 xmlXPathFreeObject(result); result = NULL;
6646 EXTRACT_LONGINT("isds:dbState", box_status, 1);
6649 leave:
6650 free(string);
6651 xmlXPathFreeObject(result);
6652 xmlXPathFreeContext(xpath_ctx);
6654 free(code);
6655 free(message);
6656 xmlFreeDoc(response);
6658 if (!err)
6659 isds_log(ILF_ISDS, ILL_DEBUG,
6660 _("CheckDataBox request processed by server successfully.\n"));
6661 #else /* not HAVE_LIBCURL */
6662 err = IE_NOTSUP;
6663 #endif
6665 return err;
6669 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
6670 * code, destroy response and log success.
6671 * @context is ISDS session context.
6672 * @service_name is name of SERVICE_DB_MANIPULATION service
6673 * @box_id is UTF-8 encoded box identifier as zero terminated string
6674 * @approval is optional external approval of box manipulation
6675 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6676 * NULL, if you don't care. */
6677 static isds_error build_send_manipulationdbid_request_check_drop_response(
6678 struct isds_ctx *context, const xmlChar *service_name,
6679 const xmlChar *box_id, const struct isds_approval *approval,
6680 xmlChar **refnumber) {
6681 isds_error err = IE_SUCCESS;
6682 #if HAVE_LIBCURL
6683 xmlDocPtr response = NULL;
6684 #endif
6686 if (!context) return IE_INVALID_CONTEXT;
6687 zfree(context->long_message);
6688 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
6690 #if HAVE_LIBCURL
6691 /* Check if connection is established */
6692 if (!context->curl) return IE_CONNECTION_CLOSED;
6694 /* Do request and check for success */
6695 err = build_send_dbid_request_check_response(context,
6696 SERVICE_DB_MANIPULATION, service_name, box_id, approval,
6697 &response, refnumber);
6698 xmlFreeDoc(response);
6700 if (!err) {
6701 char *service_name_locale = _isds_utf82locale((char *) service_name);
6702 isds_log(ILF_ISDS, ILL_DEBUG,
6703 _("%s request processed by server successfully.\n"),
6704 service_name_locale);
6705 free(service_name_locale);
6707 #else /* not HAVE_LIBCURL */
6708 err = IE_NOTSUP;
6709 #endif
6711 return err;
6715 /* Switch box into state where box can receive commercial messages (off by
6716 * default)
6717 * @context is ISDS session context.
6718 * @box_id is UTF-8 encoded box identifier as zero terminated string
6719 * @allow is true for enable, false for disable commercial messages income
6720 * @approval is optional external approval of box manipulation
6721 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6722 * NULL, if you don't care. */
6723 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
6724 const char *box_id, const _Bool allow,
6725 const struct isds_approval *approval, char **refnumber) {
6726 return build_send_manipulationdbid_request_check_drop_response(context,
6727 (allow) ? BAD_CAST "SetOpenAddressing" :
6728 BAD_CAST "ClearOpenAddressing",
6729 BAD_CAST box_id, approval, (xmlChar **) refnumber);
6733 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
6734 * message acceptance). This is just a box permission. Sender must apply
6735 * such role by sending each message.
6736 * @context is ISDS session context.
6737 * @box_id is UTF-8 encoded box identifier as zero terminated string
6738 * @allow is true for enable, false for disable OVM role permission
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_effective_ovm(struct isds_ctx *context,
6743 const char *box_id, const _Bool allow,
6744 const struct isds_approval *approval, char **refnumber) {
6745 return build_send_manipulationdbid_request_check_drop_response(context,
6746 (allow) ? BAD_CAST "SetEffectiveOVM" :
6747 BAD_CAST "ClearEffectiveOVM",
6748 BAD_CAST box_id, approval, (xmlChar **) refnumber);
6752 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
6753 * code, destroy response and log success.
6754 * @context is ISDS session context.
6755 * @service_name is name of SERVICE_DB_MANIPULATION service
6756 * @owner is structure describing box
6757 * @approval is optional external approval of box manipulation
6758 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6759 * NULL, if you don't care. */
6760 static isds_error build_send_manipulationdbowner_request_check_drop_response(
6761 struct isds_ctx *context, const xmlChar *service_name,
6762 const struct isds_DbOwnerInfo *owner,
6763 const struct isds_approval *approval, xmlChar **refnumber) {
6764 isds_error err = IE_SUCCESS;
6765 #if HAVE_LIBCURL
6766 char *service_name_locale = NULL;
6767 xmlNodePtr request = NULL, db_owner_info;
6768 xmlNsPtr isds_ns = NULL;
6769 #endif
6772 if (!context) return IE_INVALID_CONTEXT;
6773 zfree(context->long_message);
6774 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
6776 #if HAVE_LIBCURL
6777 service_name_locale = _isds_utf82locale((char*)service_name);
6778 if (!service_name_locale) {
6779 err = IE_NOMEM;
6780 goto leave;
6783 /* Build request */
6784 request = xmlNewNode(NULL, service_name);
6785 if (!request) {
6786 isds_printf_message(context,
6787 _("Could not build %s request"), service_name_locale);
6788 err = IE_ERROR;
6789 goto leave;
6791 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6792 if(!isds_ns) {
6793 isds_log_message(context, _("Could not create ISDS name space"));
6794 err = IE_ERROR;
6795 goto leave;
6797 xmlSetNs(request, isds_ns);
6800 /* Add XSD:tOwnerInfoInput child*/
6801 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
6802 err = insert_DbOwnerInfo(context, owner, db_owner_info);
6803 if (err) goto leave;
6805 /* Add XSD:gExtApproval*/
6806 err = insert_GExtApproval(context, approval, request);
6807 if (err) goto leave;
6809 /* Send it to server and process response */
6810 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6811 service_name, &request, refnumber);
6813 leave:
6814 xmlFreeNode(request);
6815 free(service_name_locale);
6816 #else /* not HAVE_LIBCURL */
6817 err = IE_NOTSUP;
6818 #endif
6820 return err;
6824 /* Switch box accessibility state on request of box owner.
6825 * Despite the name, owner must do the request off-line. This function is
6826 * designed for such off-line meeting points (e.g. Czech POINT).
6827 * @context is ISDS session context.
6828 * @box identifies box to switch accessibility state.
6829 * @allow is true for making accessible, false to disallow access.
6830 * @approval is optional external approval of box manipulation
6831 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6832 * NULL, if you don't care. */
6833 isds_error isds_switch_box_accessibility_on_owner_request(
6834 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
6835 const _Bool allow, const struct isds_approval *approval,
6836 char **refnumber) {
6837 return build_send_manipulationdbowner_request_check_drop_response(context,
6838 (allow) ? BAD_CAST "EnableOwnDataBox" :
6839 BAD_CAST "DisableOwnDataBox",
6840 box, approval, (xmlChar **) refnumber);
6844 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
6845 * date.
6846 * @context is ISDS session context.
6847 * @box identifies box to switch accessibility state.
6848 * @since is date since accessibility has been denied. This can be past too.
6849 * Only tm_year, tm_mon and tm_mday carry sane value.
6850 * @approval is optional external approval of box manipulation
6851 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6852 * NULL, if you don't care. */
6853 isds_error isds_disable_box_accessibility_externaly(
6854 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
6855 const struct tm *since, const struct isds_approval *approval,
6856 char **refnumber) {
6857 isds_error err = IE_SUCCESS;
6858 #if HAVE_LIBCURL
6859 char *service_name_locale = NULL;
6860 xmlNodePtr request = NULL, node;
6861 xmlNsPtr isds_ns = NULL;
6862 xmlChar *string = NULL;
6863 #endif
6866 if (!context) return IE_INVALID_CONTEXT;
6867 zfree(context->long_message);
6868 if (!box || !since) return IE_INVAL;
6870 #if HAVE_LIBCURL
6871 /* Build request */
6872 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
6873 if (!request) {
6874 isds_printf_message(context,
6875 _("Could not build %s request"), "DisableDataBoxExternally");
6876 err = IE_ERROR;
6877 goto leave;
6879 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6880 if(!isds_ns) {
6881 isds_log_message(context, _("Could not create ISDS name space"));
6882 err = IE_ERROR;
6883 goto leave;
6885 xmlSetNs(request, isds_ns);
6888 /* Add @box identification */
6889 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6890 err = insert_DbOwnerInfo(context, box, node);
6891 if (err) goto leave;
6893 /* Add @since date */
6894 err = tm2datestring(since, &string);
6895 if(err) {
6896 isds_log_message(context,
6897 _("Could not convert `since' argument to ISO date string"));
6898 goto leave;
6900 INSERT_STRING(request, "dbOwnerDisableDate", string);
6901 zfree(string);
6903 /* Add @approval */
6904 err = insert_GExtApproval(context, approval, request);
6905 if (err) goto leave;
6907 /* Send it to server and process response */
6908 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6909 BAD_CAST "DisableDataBoxExternally", &request,
6910 (xmlChar **) refnumber);
6912 leave:
6913 free(string);
6914 xmlFreeNode(request);
6915 free(service_name_locale);
6916 #else /* not HAVE_LIBCURL */
6917 err = IE_NOTSUP;
6918 #endif
6920 return err;
6924 #if HAVE_LIBCURL
6925 /* Insert struct isds_message data (envelope (recipient data optional) and
6926 * documents) into XML tree
6927 * @context is session context
6928 * @outgoing_message is libisds structure with message data
6929 * @create_message is XML CreateMessage or CreateMultipleMessage element
6930 * @process_recipient true for recipient data serialization, false for no
6931 * serialization */
6932 static isds_error insert_envelope_files(struct isds_ctx *context,
6933 const struct isds_message *outgoing_message, xmlNodePtr create_message,
6934 const _Bool process_recipient) {
6936 isds_error err = IE_SUCCESS;
6937 xmlNodePtr envelope, dm_files, node;
6938 xmlChar *string = NULL;
6940 if (!context) return IE_INVALID_CONTEXT;
6941 if (!outgoing_message || !create_message) return IE_INVAL;
6944 /* Build envelope */
6945 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
6946 if (!envelope) {
6947 isds_printf_message(context, _("Could not add dmEnvelope child to "
6948 "%s element"), create_message->name);
6949 return IE_ERROR;
6952 if (!outgoing_message->envelope) {
6953 isds_log_message(context, _("Outgoing message is missing envelope"));
6954 err = IE_INVAL;
6955 goto leave;
6958 /* Insert optional message type */
6959 err = insert_message_type(context, outgoing_message->envelope->dmType,
6960 envelope);
6961 if (err) goto leave;
6963 INSERT_STRING(envelope, "dmSenderOrgUnit",
6964 outgoing_message->envelope->dmSenderOrgUnit);
6965 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
6966 outgoing_message->envelope->dmSenderOrgUnitNum, string);
6968 if (process_recipient) {
6969 if (!outgoing_message->envelope->dbIDRecipient) {
6970 isds_log_message(context,
6971 _("Outgoing message is missing recipient box identifier"));
6972 err = IE_INVAL;
6973 goto leave;
6975 INSERT_STRING(envelope, "dbIDRecipient",
6976 outgoing_message->envelope->dbIDRecipient);
6978 INSERT_STRING(envelope, "dmRecipientOrgUnit",
6979 outgoing_message->envelope->dmRecipientOrgUnit);
6980 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
6981 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
6982 INSERT_STRING(envelope, "dmToHands",
6983 outgoing_message->envelope->dmToHands);
6986 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
6987 "dmAnnotation");
6988 INSERT_STRING(envelope, "dmAnnotation",
6989 outgoing_message->envelope->dmAnnotation);
6991 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
6992 0, 50, "dmRecipientRefNumber");
6993 INSERT_STRING(envelope, "dmRecipientRefNumber",
6994 outgoing_message->envelope->dmRecipientRefNumber);
6996 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
6997 0, 50, "dmSenderRefNumber");
6998 INSERT_STRING(envelope, "dmSenderRefNumber",
6999 outgoing_message->envelope->dmSenderRefNumber);
7001 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
7002 0, 50, "dmRecipientIdent");
7003 INSERT_STRING(envelope, "dmRecipientIdent",
7004 outgoing_message->envelope->dmRecipientIdent);
7006 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
7007 0, 50, "dmSenderIdent");
7008 INSERT_STRING(envelope, "dmSenderIdent",
7009 outgoing_message->envelope->dmSenderIdent);
7011 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
7012 outgoing_message->envelope->dmLegalTitleLaw, string);
7013 INSERT_LONGINT(envelope, "dmLegalTitleYear",
7014 outgoing_message->envelope->dmLegalTitleYear, string);
7015 INSERT_STRING(envelope, "dmLegalTitleSect",
7016 outgoing_message->envelope->dmLegalTitleSect);
7017 INSERT_STRING(envelope, "dmLegalTitlePar",
7018 outgoing_message->envelope->dmLegalTitlePar);
7019 INSERT_STRING(envelope, "dmLegalTitlePoint",
7020 outgoing_message->envelope->dmLegalTitlePoint);
7022 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
7023 outgoing_message->envelope->dmPersonalDelivery);
7024 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
7025 outgoing_message->envelope->dmAllowSubstDelivery);
7027 /* ???: Should we require value for dbEffectiveOVM sender?
7028 * ISDS has default as true */
7029 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
7030 INSERT_BOOLEAN(envelope, "dmOVM",
7031 outgoing_message->envelope->dmPublishOwnID);
7034 /* Append dmFiles */
7035 if (!outgoing_message->documents) {
7036 isds_log_message(context,
7037 _("Outgoing message is missing list of documents"));
7038 err = IE_INVAL;
7039 goto leave;
7041 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
7042 if (!dm_files) {
7043 isds_printf_message(context, _("Could not add dmFiles child to "
7044 "%s element"), create_message->name);
7045 err = IE_ERROR;
7046 goto leave;
7049 /* Check for document hierarchy */
7050 err = _isds_check_documents_hierarchy(context, outgoing_message->documents);
7051 if (err) goto leave;
7053 /* Process each document */
7054 for (struct isds_list *item =
7055 (struct isds_list *) outgoing_message->documents;
7056 item; item = item->next) {
7057 if (!item->data) {
7058 isds_log_message(context,
7059 _("List of documents contains empty item"));
7060 err = IE_INVAL;
7061 goto leave;
7063 /* FIXME: Check for dmFileMetaType and for document references.
7064 * Only first document can be of MAIN type */
7065 err = insert_document(context, (struct isds_document*) item->data,
7066 dm_files);
7068 if (err) goto leave;
7071 leave:
7072 free(string);
7073 return err;
7075 #endif /* HAVE_LIBCURL */
7078 /* Send a message via ISDS to a recipient
7079 * @context is session context
7080 * @outgoing_message is message to send; Some members are mandatory (like
7081 * dbIDRecipient), some are optional and some are irrelevant (especially data
7082 * about sender). Included pointer to isds_list documents must contain at
7083 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
7084 * members will be filled with valid data from ISDS. Exact list of write
7085 * members is subject to change. Currently dmID is changed.
7086 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
7087 isds_error isds_send_message(struct isds_ctx *context,
7088 struct isds_message *outgoing_message) {
7090 isds_error err = IE_SUCCESS;
7091 #if HAVE_LIBCURL
7092 xmlNsPtr isds_ns = NULL;
7093 xmlNodePtr request = NULL;
7094 xmlDocPtr response = NULL;
7095 xmlChar *code = NULL, *message = NULL;
7096 xmlXPathContextPtr xpath_ctx = NULL;
7097 xmlXPathObjectPtr result = NULL;
7098 /*_Bool message_is_complete = 0;*/
7099 #endif
7101 if (!context) return IE_INVALID_CONTEXT;
7102 zfree(context->long_message);
7103 if (!outgoing_message) return IE_INVAL;
7105 #if HAVE_LIBCURL
7106 /* Check if connection is established
7107 * TODO: This check should be done downstairs. */
7108 if (!context->curl) return IE_CONNECTION_CLOSED;
7111 /* Build CreateMessage request */
7112 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
7113 if (!request) {
7114 isds_log_message(context,
7115 _("Could not build CreateMessage request"));
7116 return IE_ERROR;
7118 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7119 if(!isds_ns) {
7120 isds_log_message(context, _("Could not create ISDS name space"));
7121 xmlFreeNode(request);
7122 return IE_ERROR;
7124 xmlSetNs(request, isds_ns);
7126 /* Append envelope and files */
7127 err = insert_envelope_files(context, outgoing_message, request, 1);
7128 if (err) goto leave;
7131 /* Signal we can serialize message since now */
7132 /*message_is_complete = 1;*/
7135 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
7137 /* Sent request */
7138 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
7140 /* Don't' destroy request, we want to provide it to application later */
7142 if (err) {
7143 isds_log(ILF_ISDS, ILL_DEBUG,
7144 _("Processing ISDS response on CreateMessage "
7145 "request failed\n"));
7146 goto leave;
7149 /* Check for response status */
7150 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
7151 &code, &message, NULL);
7152 if (err) {
7153 isds_log(ILF_ISDS, ILL_DEBUG,
7154 _("ISDS response on CreateMessage request "
7155 "is missing status\n"));
7156 goto leave;
7159 /* Request processed, but refused by server or server failed */
7160 if (xmlStrcmp(code, BAD_CAST "0000")) {
7161 char *box_id_locale =
7162 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
7163 char *code_locale = _isds_utf82locale((char*)code);
7164 char *message_locale = _isds_utf82locale((char*)message);
7165 isds_log(ILF_ISDS, ILL_DEBUG,
7166 _("Server did not accept message for %s on CreateMessage "
7167 "request (code=%s, message=%s)\n"),
7168 box_id_locale, code_locale, message_locale);
7169 isds_log_message(context, message_locale);
7170 free(box_id_locale);
7171 free(code_locale);
7172 free(message_locale);
7173 err = IE_ISDS;
7174 goto leave;
7178 /* Extract data */
7179 xpath_ctx = xmlXPathNewContext(response);
7180 if (!xpath_ctx) {
7181 err = IE_ERROR;
7182 goto leave;
7184 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7185 err = IE_ERROR;
7186 goto leave;
7188 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
7189 xpath_ctx);
7190 if (!result) {
7191 err = IE_ERROR;
7192 goto leave;
7194 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7195 isds_log_message(context, _("Missing CreateMessageResponse element"));
7196 err = IE_ISDS;
7197 goto leave;
7199 if (result->nodesetval->nodeNr > 1) {
7200 isds_log_message(context, _("Multiple CreateMessageResponse element"));
7201 err = IE_ISDS;
7202 goto leave;
7204 xpath_ctx->node = result->nodesetval->nodeTab[0];
7205 xmlXPathFreeObject(result); result = NULL;
7207 if (outgoing_message->envelope->dmID) {
7208 free(outgoing_message->envelope->dmID);
7209 outgoing_message->envelope->dmID = NULL;
7211 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
7212 if (!outgoing_message->envelope->dmID) {
7213 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
7214 "but did not return assigned message ID\n"));
7217 leave:
7218 /* TODO: Serialize message into structure member raw */
7219 /* XXX: Each web service transport message in different format.
7220 * Therefore it's not possible to save them directly.
7221 * To save them, one must figure out common format.
7222 * We can leave it on application, or we can implement the ESS format. */
7223 /*if (message_is_complete) {
7224 if (outgoing_message->envelope->dmID) {
7226 /* Add assigned message ID as first child*/
7227 /*xmlNodePtr dmid_text = xmlNewText(
7228 (xmlChar *) outgoing_message->envelope->dmID);
7229 if (!dmid_text) goto serialization_failed;
7231 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
7232 BAD_CAST "dmID");
7233 if (!dmid_element) {
7234 xmlFreeNode(dmid_text);
7235 goto serialization_failed;
7238 xmlNodePtr dmid_element_with_text =
7239 xmlAddChild(dmid_element, dmid_text);
7240 if (!dmid_element_with_text) {
7241 xmlFreeNode(dmid_element);
7242 xmlFreeNode(dmid_text);
7243 goto serialization_failed;
7246 node = xmlAddPrevSibling(envelope->childern,
7247 dmid_element_with_text);
7248 if (!node) {
7249 xmlFreeNodeList(dmid_element_with_text);
7250 goto serialization_failed;
7254 /* Serialize message with ID into raw */
7255 /*buffer = serialize_element(envelope)*/
7256 /* }
7258 serialization_failed:
7262 /* Clean up */
7263 xmlXPathFreeObject(result);
7264 xmlXPathFreeContext(xpath_ctx);
7266 free(code);
7267 free(message);
7268 xmlFreeDoc(response);
7269 xmlFreeNode(request);
7271 if (!err)
7272 isds_log(ILF_ISDS, ILL_DEBUG,
7273 _("CreateMessage request processed by server "
7274 "successfully.\n"));
7275 #else /* not HAVE_LIBCURL */
7276 err = IE_NOTSUP;
7277 #endif
7279 return err;
7283 /* Send a message via ISDS to a multiple recipients
7284 * @context is session context
7285 * @outgoing_message is message to send; Some members are mandatory,
7286 * some are optional and some are irrelevant (especially data
7287 * about sender). Data about recipient will be substituted by ISDS from
7288 * @copies. Included pointer to isds_list documents must
7289 * contain at least one document of FILEMETATYPE_MAIN.
7290 * @copies is list of isds_message_copy structures addressing all desired
7291 * recipients. This is read-write structure, some members will be filled with
7292 * valid data from ISDS (message IDs, error codes, error descriptions).
7293 * @return
7294 * ISDS_SUCCESS if all messages have been sent
7295 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
7296 * succeeded messages can be identified by copies->data->error),
7297 * or other error code if something other goes wrong. */
7298 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
7299 const struct isds_message *outgoing_message,
7300 struct isds_list *copies) {
7302 isds_error err = IE_SUCCESS;
7303 #if HAVE_LIBCURL
7304 isds_error append_err;
7305 xmlNsPtr isds_ns = NULL;
7306 xmlNodePtr request = NULL, recipients, recipient, node;
7307 struct isds_list *item;
7308 struct isds_message_copy *copy;
7309 xmlDocPtr response = NULL;
7310 xmlChar *code = NULL, *message = NULL;
7311 xmlXPathContextPtr xpath_ctx = NULL;
7312 xmlXPathObjectPtr result = NULL;
7313 xmlChar *string = NULL;
7314 int i;
7315 #endif
7317 if (!context) return IE_INVALID_CONTEXT;
7318 zfree(context->long_message);
7319 if (!outgoing_message || !copies) return IE_INVAL;
7321 #if HAVE_LIBCURL
7322 /* Check if connection is established
7323 * TODO: This check should be done downstairs. */
7324 if (!context->curl) return IE_CONNECTION_CLOSED;
7327 /* Build CreateMultipleMessage request */
7328 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
7329 if (!request) {
7330 isds_log_message(context,
7331 _("Could not build CreateMultipleMessage request"));
7332 return IE_ERROR;
7334 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7335 if(!isds_ns) {
7336 isds_log_message(context, _("Could not create ISDS name space"));
7337 xmlFreeNode(request);
7338 return IE_ERROR;
7340 xmlSetNs(request, isds_ns);
7343 /* Build recipients */
7344 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
7345 if (!recipients) {
7346 isds_log_message(context, _("Could not add dmRecipients child to "
7347 "CreateMultipleMessage element"));
7348 xmlFreeNode(request);
7349 return IE_ERROR;
7352 /* Insert each recipient */
7353 for (item = copies; item; item = item->next) {
7354 copy = (struct isds_message_copy *) item->data;
7355 if (!copy) {
7356 isds_log_message(context,
7357 _("`copies' list item contains empty data"));
7358 err = IE_INVAL;
7359 goto leave;
7362 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
7363 if (!recipient) {
7364 isds_log_message(context, _("Could not add dmRecipient child to "
7365 "dmRecipients element"));
7366 err = IE_ERROR;
7367 goto leave;
7370 if (!copy->dbIDRecipient) {
7371 isds_log_message(context,
7372 _("Message copy is missing recipient box identifier"));
7373 err = IE_INVAL;
7374 goto leave;
7376 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
7377 INSERT_STRING(recipient, "dmRecipientOrgUnit",
7378 copy->dmRecipientOrgUnit);
7379 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
7380 copy->dmRecipientOrgUnitNum, string);
7381 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
7384 /* Append envelope and files */
7385 err = insert_envelope_files(context, outgoing_message, request, 0);
7386 if (err) goto leave;
7389 isds_log(ILF_ISDS, ILL_DEBUG,
7390 _("Sending CreateMultipleMessage request to ISDS\n"));
7392 /* Sent request */
7393 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
7394 if (err) {
7395 isds_log(ILF_ISDS, ILL_DEBUG,
7396 _("Processing ISDS response on CreateMultipleMessage "
7397 "request failed\n"));
7398 goto leave;
7401 /* Check for response status */
7402 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
7403 &code, &message, NULL);
7404 if (err) {
7405 isds_log(ILF_ISDS, ILL_DEBUG,
7406 _("ISDS response on CreateMultipleMessage request "
7407 "is missing status\n"));
7408 goto leave;
7411 /* Request processed, but some copies failed */
7412 if (!xmlStrcmp(code, BAD_CAST "0004")) {
7413 char *box_id_locale =
7414 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
7415 char *code_locale = _isds_utf82locale((char*)code);
7416 char *message_locale = _isds_utf82locale((char*)message);
7417 isds_log(ILF_ISDS, ILL_DEBUG,
7418 _("Server did accept message for multiple recipients "
7419 "on CreateMultipleMessage request but delivery to "
7420 "some of them failed (code=%s, message=%s)\n"),
7421 box_id_locale, code_locale, message_locale);
7422 isds_log_message(context, message_locale);
7423 free(box_id_locale);
7424 free(code_locale);
7425 free(message_locale);
7426 err = IE_PARTIAL_SUCCESS;
7429 /* Request refused by server as whole */
7430 else if (xmlStrcmp(code, BAD_CAST "0000")) {
7431 char *box_id_locale =
7432 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
7433 char *code_locale = _isds_utf82locale((char*)code);
7434 char *message_locale = _isds_utf82locale((char*)message);
7435 isds_log(ILF_ISDS, ILL_DEBUG,
7436 _("Server did not accept message for multiple recipients "
7437 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
7438 box_id_locale, code_locale, message_locale);
7439 isds_log_message(context, message_locale);
7440 free(box_id_locale);
7441 free(code_locale);
7442 free(message_locale);
7443 err = IE_ISDS;
7444 goto leave;
7448 /* Extract data */
7449 xpath_ctx = xmlXPathNewContext(response);
7450 if (!xpath_ctx) {
7451 err = IE_ERROR;
7452 goto leave;
7454 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7455 err = IE_ERROR;
7456 goto leave;
7458 result = xmlXPathEvalExpression(
7459 BAD_CAST "/isds:CreateMultipleMessageResponse"
7460 "/isds:dmMultipleStatus/isds:dmSingleStatus",
7461 xpath_ctx);
7462 if (!result) {
7463 err = IE_ERROR;
7464 goto leave;
7466 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7467 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
7468 err = IE_ISDS;
7469 goto leave;
7472 /* Extract message ID and delivery status for each copy */
7473 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
7474 item = item->next, i++) {
7475 copy = (struct isds_message_copy *) item->data;
7476 xpath_ctx->node = result->nodesetval->nodeTab[i];
7478 append_err = append_TMStatus(context, copy, xpath_ctx);
7479 if (append_err) {
7480 err = append_err;
7481 goto leave;
7484 if (item || i < result->nodesetval->nodeNr) {
7485 isds_printf_message(context, _("ISDS returned unexpected number of "
7486 "message copy delivery states: %d"),
7487 result->nodesetval->nodeNr);
7488 err = IE_ISDS;
7489 goto leave;
7493 leave:
7494 /* Clean up */
7495 free(string);
7496 xmlXPathFreeObject(result);
7497 xmlXPathFreeContext(xpath_ctx);
7499 free(code);
7500 free(message);
7501 xmlFreeDoc(response);
7502 xmlFreeNode(request);
7504 if (!err)
7505 isds_log(ILF_ISDS, ILL_DEBUG,
7506 _("CreateMultipleMessageResponse request processed by server "
7507 "successfully.\n"));
7508 #else /* not HAVE_LIBCURL */
7509 err = IE_NOTSUP;
7510 #endif
7512 return err;
7516 /* Get list of messages. This is common core for getting sent or received
7517 * messages.
7518 * Any criterion argument can be NULL, if you don't care about it.
7519 * @context is session context. Must not be NULL.
7520 * @outgoing_direction is true if you want list of outgoing messages,
7521 * it's false if you want incoming messages.
7522 * @from_time is minimal time and date of message sending inclusive.
7523 * @to_time is maximal time and date of message sending inclusive
7524 * @organization_unit_number is number of sender/recipient respectively.
7525 * @status_filter is bit field of isds_message_status values. Use special
7526 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
7527 * all values, you can use bit-wise arithmetic if you want.)
7528 * @offset is index of first message we are interested in. First message is 1.
7529 * Set to 0 (or 1) if you don't care.
7530 * @number is maximal length of list you want to get as input value, outputs
7531 * number of messages matching these criteria. Can be NULL if you don't care
7532 * (applies to output value either).
7533 * @messages is automatically reallocated list of isds_message's. Be ware that
7534 * it returns only brief overview (envelope and some other fields) about each
7535 * message, not the complete message. FIXME: Specify exact fields.
7536 * The list is sorted by delivery time in ascending order.
7537 * Use NULL if you don't care about don't need the data (useful if you want to
7538 * know only the @number). If you provide &NULL, list will be allocated on
7539 * heap, if you provide pointer to non-NULL, list will be freed automatically
7540 * at first. Also in case of error the list will be NULLed.
7541 * @return IE_SUCCESS or appropriate error code. */
7542 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
7543 _Bool outgoing_direction,
7544 const struct timeval *from_time, const struct timeval *to_time,
7545 const long int *organization_unit_number,
7546 const unsigned int status_filter,
7547 const unsigned long int offset, unsigned long int *number,
7548 struct isds_list **messages) {
7550 isds_error err = IE_SUCCESS;
7551 #if HAVE_LIBCURL
7552 xmlNsPtr isds_ns = NULL;
7553 xmlNodePtr request = NULL, node;
7554 xmlDocPtr response = NULL;
7555 xmlChar *code = NULL, *message = NULL;
7556 xmlXPathContextPtr xpath_ctx = NULL;
7557 xmlXPathObjectPtr result = NULL;
7558 xmlChar *string = NULL;
7559 long unsigned int count = 0;
7560 #endif
7562 if (!context) return IE_INVALID_CONTEXT;
7563 zfree(context->long_message);
7565 /* Free former message list if any */
7566 if (messages) isds_list_free(messages);
7568 #if HAVE_LIBCURL
7569 /* Check if connection is established
7570 * TODO: This check should be done downstairs. */
7571 if (!context->curl) return IE_CONNECTION_CLOSED;
7573 /* Build GetListOf*Messages request */
7574 request = xmlNewNode(NULL,
7575 (outgoing_direction) ?
7576 BAD_CAST "GetListOfSentMessages" :
7577 BAD_CAST "GetListOfReceivedMessages"
7579 if (!request) {
7580 isds_log_message(context,
7581 (outgoing_direction) ?
7582 _("Could not build GetListOfSentMessages request") :
7583 _("Could not build GetListOfReceivedMessages request")
7585 return IE_ERROR;
7587 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7588 if(!isds_ns) {
7589 isds_log_message(context, _("Could not create ISDS name space"));
7590 xmlFreeNode(request);
7591 return IE_ERROR;
7593 xmlSetNs(request, isds_ns);
7596 if (from_time) {
7597 err = timeval2timestring(from_time, &string);
7598 if (err) goto leave;
7600 INSERT_STRING(request, "dmFromTime", string);
7601 free(string); string = NULL;
7603 if (to_time) {
7604 err = timeval2timestring(to_time, &string);
7605 if (err) goto leave;
7607 INSERT_STRING(request, "dmToTime", string);
7608 free(string); string = NULL;
7610 if (outgoing_direction) {
7611 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
7612 organization_unit_number, string);
7613 } else {
7614 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
7615 organization_unit_number, string);
7618 if (status_filter > MESSAGESTATE_ANY) {
7619 isds_printf_message(context,
7620 _("Invalid message state filter value: %ld"), status_filter);
7621 err = IE_INVAL;
7622 goto leave;
7624 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
7626 if (offset > 0 ) {
7627 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
7628 } else {
7629 INSERT_STRING(request, "dmOffset", "1");
7632 /* number 0 means no limit */
7633 if (number && *number == 0) {
7634 INSERT_STRING(request, "dmLimit", NULL);
7635 } else {
7636 INSERT_ULONGINT(request, "dmLimit", number, string);
7640 isds_log(ILF_ISDS, ILL_DEBUG,
7641 (outgoing_direction) ?
7642 _("Sending GetListOfSentMessages request to ISDS\n") :
7643 _("Sending GetListOfReceivedMessages request to ISDS\n")
7646 /* Sent request */
7647 err = isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
7648 xmlFreeNode(request); request = NULL;
7650 if (err) {
7651 isds_log(ILF_ISDS, ILL_DEBUG,
7652 (outgoing_direction) ?
7653 _("Processing ISDS response on GetListOfSentMessages "
7654 "request failed\n") :
7655 _("Processing ISDS response on GetListOfReceivedMessages "
7656 "request failed\n")
7658 goto leave;
7661 /* Check for response status */
7662 err = isds_response_status(context, SERVICE_DM_INFO, response,
7663 &code, &message, NULL);
7664 if (err) {
7665 isds_log(ILF_ISDS, ILL_DEBUG,
7666 (outgoing_direction) ?
7667 _("ISDS response on GetListOfSentMessages request "
7668 "is missing status\n") :
7669 _("ISDS response on GetListOfReceivedMessages request "
7670 "is missing status\n")
7672 goto leave;
7675 /* Request processed, but nothing found */
7676 if (xmlStrcmp(code, BAD_CAST "0000")) {
7677 char *code_locale = _isds_utf82locale((char*)code);
7678 char *message_locale = _isds_utf82locale((char*)message);
7679 isds_log(ILF_ISDS, ILL_DEBUG,
7680 (outgoing_direction) ?
7681 _("Server refused GetListOfSentMessages request "
7682 "(code=%s, message=%s)\n") :
7683 _("Server refused GetListOfReceivedMessages request "
7684 "(code=%s, message=%s)\n"),
7685 code_locale, message_locale);
7686 isds_log_message(context, message_locale);
7687 free(code_locale);
7688 free(message_locale);
7689 err = IE_ISDS;
7690 goto leave;
7694 /* Extract data */
7695 xpath_ctx = xmlXPathNewContext(response);
7696 if (!xpath_ctx) {
7697 err = IE_ERROR;
7698 goto leave;
7700 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7701 err = IE_ERROR;
7702 goto leave;
7704 result = xmlXPathEvalExpression(
7705 (outgoing_direction) ?
7706 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
7707 "isds:dmRecords/isds:dmRecord" :
7708 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
7709 "isds:dmRecords/isds:dmRecord",
7710 xpath_ctx);
7711 if (!result) {
7712 err = IE_ERROR;
7713 goto leave;
7716 /* Fill output arguments in */
7717 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7718 struct isds_envelope *envelope;
7719 struct isds_list *item = NULL, *last_item = NULL;
7721 for (count = 0; count < result->nodesetval->nodeNr; count++) {
7722 /* Create new message */
7723 item = calloc(1, sizeof(*item));
7724 if (!item) {
7725 err = IE_NOMEM;
7726 goto leave;
7728 item->destructor = (void(*)(void**)) &isds_message_free;
7729 item->data = calloc(1, sizeof(struct isds_message));
7730 if (!item->data) {
7731 isds_list_free(&item);
7732 err = IE_NOMEM;
7733 goto leave;
7736 /* Extract envelope data */
7737 xpath_ctx->node = result->nodesetval->nodeTab[count];
7738 envelope = NULL;
7739 err = extract_DmRecord(context, &envelope, xpath_ctx);
7740 if (err) {
7741 isds_list_free(&item);
7742 goto leave;
7745 /* Attach extracted envelope */
7746 ((struct isds_message *) item->data)->envelope = envelope;
7748 /* Append new message into the list */
7749 if (!*messages) {
7750 *messages = last_item = item;
7751 } else {
7752 last_item->next = item;
7753 last_item = item;
7757 if (number) *number = count;
7759 leave:
7760 if (err) {
7761 isds_list_free(messages);
7764 free(string);
7765 xmlXPathFreeObject(result);
7766 xmlXPathFreeContext(xpath_ctx);
7768 free(code);
7769 free(message);
7770 xmlFreeDoc(response);
7771 xmlFreeNode(request);
7773 if (!err)
7774 isds_log(ILF_ISDS, ILL_DEBUG,
7775 (outgoing_direction) ?
7776 _("GetListOfSentMessages request processed by server "
7777 "successfully.\n") :
7778 _("GetListOfReceivedMessages request processed by server "
7779 "successfully.\n")
7781 #else /* not HAVE_LIBCURL */
7782 err = IE_NOTSUP;
7783 #endif
7784 return err;
7788 /* Get list of outgoing (already sent) messages.
7789 * Any criterion argument can be NULL, if you don't care about it.
7790 * @context is session context. Must not be NULL.
7791 * @from_time is minimal time and date of message sending inclusive.
7792 * @to_time is maximal time and date of message sending inclusive
7793 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
7794 * @status_filter is bit field of isds_message_status values. Use special
7795 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
7796 * all values, you can use bit-wise arithmetic if you want.)
7797 * @offset is index of first message we are interested in. First message is 1.
7798 * Set to 0 (or 1) if you don't care.
7799 * @number is maximal length of list you want to get as input value, outputs
7800 * number of messages matching these criteria. Can be NULL if you don't care
7801 * (applies to output value either).
7802 * @messages is automatically reallocated list of isds_message's. Be ware that
7803 * it returns only brief overview (envelope and some other fields) about each
7804 * message, not the complete message. FIXME: Specify exact fields.
7805 * The list is sorted by delivery time in ascending order.
7806 * Use NULL if you don't care about the meta data (useful if you want to know
7807 * only the @number). If you provide &NULL, list will be allocated on heap,
7808 * if you provide pointer to non-NULL, list will be freed automatically at
7809 * first. Also in case of error the list will be NULLed.
7810 * @return IE_SUCCESS or appropriate error code. */
7811 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
7812 const struct timeval *from_time, const struct timeval *to_time,
7813 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
7814 const unsigned long int offset, unsigned long int *number,
7815 struct isds_list **messages) {
7817 return isds_get_list_of_messages(
7818 context, 1,
7819 from_time, to_time, dmSenderOrgUnitNum, status_filter,
7820 offset, number,
7821 messages);
7825 /* Get list of incoming (addressed to you) messages.
7826 * Any criterion argument can be NULL, if you don't care about it.
7827 * @context is session context. Must not be NULL.
7828 * @from_time is minimal time and date of message sending inclusive.
7829 * @to_time is maximal time and date of message sending inclusive
7830 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
7831 * @status_filter is bit field of isds_message_status values. Use special
7832 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
7833 * all values, you can use bit-wise arithmetic if you want.)
7834 * @offset is index of first message we are interested in. First message is 1.
7835 * Set to 0 (or 1) if you don't care.
7836 * @number is maximal length of list you want to get as input value, outputs
7837 * number of messages matching these criteria. Can be NULL if you don't care
7838 * (applies to output value either).
7839 * @messages is automatically reallocated list of isds_message's. Be ware that
7840 * it returns only brief overview (envelope and some other fields) about each
7841 * message, not the complete message. FIXME: Specify exact fields.
7842 * Use NULL if you don't care about the meta data (useful if you want to know
7843 * only the @number). If you provide &NULL, list will be allocated on heap,
7844 * if you provide pointer to non-NULL, list will be freed automatically at
7845 * first. Also in case of error the list will be NULLed.
7846 * @return IE_SUCCESS or appropriate error code. */
7847 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
7848 const struct timeval *from_time, const struct timeval *to_time,
7849 const long int *dmRecipientOrgUnitNum,
7850 const unsigned int status_filter,
7851 const unsigned long int offset, unsigned long int *number,
7852 struct isds_list **messages) {
7854 return isds_get_list_of_messages(
7855 context, 0,
7856 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
7857 offset, number,
7858 messages);
7862 /* Get list of sent message state changes.
7863 * Any criterion argument can be NULL, if you don't care about it.
7864 * @context is session context. Must not be NULL.
7865 * @from_time is minimal time and date of status changes inclusive
7866 * @to_time is maximal time and date of status changes inclusive
7867 * @changed_states is automatically reallocated list of
7868 * isds_message_status_change's. If you provide &NULL, list will be allocated
7869 * on heap, if you provide pointer to non-NULL, list will be freed
7870 * automatically at first. Also in case of error the list will be NULLed.
7871 * XXX: The list item ordering is not specified.
7872 * XXX: Server provides only `recent' changes.
7873 * @return IE_SUCCESS or appropriate error code. */
7874 isds_error isds_get_list_of_sent_message_state_changes(
7875 struct isds_ctx *context,
7876 const struct timeval *from_time, const struct timeval *to_time,
7877 struct isds_list **changed_states) {
7879 isds_error err = IE_SUCCESS;
7880 #if HAVE_LIBCURL
7881 xmlNsPtr isds_ns = NULL;
7882 xmlNodePtr request = NULL, node;
7883 xmlDocPtr response = NULL;
7884 xmlXPathContextPtr xpath_ctx = NULL;
7885 xmlXPathObjectPtr result = NULL;
7886 xmlChar *string = NULL;
7887 long unsigned int count = 0;
7888 #endif
7890 if (!context) return IE_INVALID_CONTEXT;
7891 zfree(context->long_message);
7893 /* Free former message list if any */
7894 isds_list_free(changed_states);
7896 #if HAVE_LIBCURL
7897 /* Check if connection is established
7898 * TODO: This check should be done downstairs. */
7899 if (!context->curl) return IE_CONNECTION_CLOSED;
7901 /* Build GetMessageStateChanges request */
7902 request = xmlNewNode(NULL, BAD_CAST "GetMessageStateChanges");
7903 if (!request) {
7904 isds_log_message(context,
7905 _("Could not build GetMessageStateChanges request"));
7906 return IE_ERROR;
7908 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7909 if(!isds_ns) {
7910 isds_log_message(context, _("Could not create ISDS name space"));
7911 xmlFreeNode(request);
7912 return IE_ERROR;
7914 xmlSetNs(request, isds_ns);
7917 if (from_time) {
7918 err = timeval2timestring(from_time, &string);
7919 if (err) goto leave;
7921 INSERT_STRING(request, "dmFromTime", string);
7922 zfree(string);
7924 if (to_time) {
7925 err = timeval2timestring(to_time, &string);
7926 if (err) goto leave;
7928 INSERT_STRING(request, "dmToTime", string);
7929 zfree(string);
7932 /* Sent request */
7933 err = send_destroy_request_check_response(context,
7934 SERVICE_DM_INFO, BAD_CAST "GetMessageStateChanges", &request,
7935 &response, NULL);
7936 if (err) goto leave;
7939 /* Extract data */
7940 xpath_ctx = xmlXPathNewContext(response);
7941 if (!xpath_ctx) {
7942 err = IE_ERROR;
7943 goto leave;
7945 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7946 err = IE_ERROR;
7947 goto leave;
7949 result = xmlXPathEvalExpression(
7950 BAD_CAST "/isds:GetMessageStateChangesResponse/"
7951 "isds:dmRecords/isds:dmRecord", xpath_ctx);
7952 if (!result) {
7953 err = IE_ERROR;
7954 goto leave;
7957 /* Fill output arguments in */
7958 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7959 struct isds_list *item = NULL, *last_item = NULL;
7961 for (count = 0; count < result->nodesetval->nodeNr; count++) {
7962 /* Create new status change */
7963 item = calloc(1, sizeof(*item));
7964 if (!item) {
7965 err = IE_NOMEM;
7966 goto leave;
7968 item->destructor =
7969 (void(*)(void**)) &isds_message_status_change_free;
7971 /* Extract message status change */
7972 xpath_ctx->node = result->nodesetval->nodeTab[count];
7973 err = extract_StateChangesRecord(context,
7974 (struct isds_message_status_change **) &item->data,
7975 xpath_ctx);
7976 if (err) {
7977 isds_list_free(&item);
7978 goto leave;
7981 /* Append new message status change into the list */
7982 if (!*changed_states) {
7983 *changed_states = last_item = item;
7984 } else {
7985 last_item->next = item;
7986 last_item = item;
7991 leave:
7992 if (err) {
7993 isds_list_free(changed_states);
7996 free(string);
7997 xmlXPathFreeObject(result);
7998 xmlXPathFreeContext(xpath_ctx);
7999 xmlFreeDoc(response);
8000 xmlFreeNode(request);
8002 if (!err)
8003 isds_log(ILF_ISDS, ILL_DEBUG,
8004 _("GetMessageStateChanges request processed by server "
8005 "successfully.\n"));
8006 #else /* not HAVE_LIBCURL */
8007 err = IE_NOTSUP;
8008 #endif
8009 return err;
8013 #if HAVE_LIBCURL
8014 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
8015 * code
8016 * @context is session context
8017 * @service is ISDS WS service handler
8018 * @service_name is name of SERVICE_DM_OPERATIONS
8019 * @message_id is message ID to send as service argument to ISDS
8020 * @response is server SOAP body response as XML document
8021 * @raw_response is automatically reallocated bit stream with response body. Use
8022 * NULL if you don't care
8023 * @raw_response_length is size of @raw_response in bytes
8024 * @code is ISDS status code
8025 * @status_message is ISDS status message
8026 * @return error coded from lower layer, context message will be set up
8027 * appropriately. */
8028 static isds_error build_send_check_message_request(struct isds_ctx *context,
8029 const isds_service service, const xmlChar *service_name,
8030 const char *message_id,
8031 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
8032 xmlChar **code, xmlChar **status_message) {
8034 isds_error err = IE_SUCCESS;
8035 char *service_name_locale = NULL, *message_id_locale = NULL;
8036 xmlNodePtr request = NULL, node;
8037 xmlNsPtr isds_ns = NULL;
8039 if (!context) return IE_INVALID_CONTEXT;
8040 if (!service_name || !message_id) return IE_INVAL;
8041 if (!response || !code || !status_message) return IE_INVAL;
8042 if (!raw_response_length && raw_response) return IE_INVAL;
8044 /* Free output argument */
8045 xmlFreeDoc(*response); *response = NULL;
8046 if (raw_response) zfree(*raw_response);
8047 free(*code);
8048 free(*status_message);
8051 /* Check if connection is established
8052 * TODO: This check should be done downstairs. */
8053 if (!context->curl) return IE_CONNECTION_CLOSED;
8055 service_name_locale = _isds_utf82locale((char*)service_name);
8056 message_id_locale = _isds_utf82locale(message_id);
8057 if (!service_name_locale || !message_id_locale) {
8058 err = IE_NOMEM;
8059 goto leave;
8062 /* Build request */
8063 request = xmlNewNode(NULL, service_name);
8064 if (!request) {
8065 isds_printf_message(context,
8066 _("Could not build %s request for %s message ID"),
8067 service_name_locale, message_id_locale);
8068 err = IE_ERROR;
8069 goto leave;
8071 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8072 if(!isds_ns) {
8073 isds_log_message(context, _("Could not create ISDS name space"));
8074 err = IE_ERROR;
8075 goto leave;
8077 xmlSetNs(request, isds_ns);
8080 /* Add requested ID */
8081 err = validate_message_id_length(context, (xmlChar *) message_id);
8082 if (err) goto leave;
8083 INSERT_STRING(request, "dmID", message_id);
8086 isds_log(ILF_ISDS, ILL_DEBUG,
8087 _("Sending %s request for %s message ID to ISDS\n"),
8088 service_name_locale, message_id_locale);
8090 /* Send request */
8091 err = isds(context, service, request, response,
8092 raw_response, raw_response_length);
8093 xmlFreeNode(request); request = NULL;
8095 if (err) {
8096 isds_log(ILF_ISDS, ILL_DEBUG,
8097 _("Processing ISDS response on %s request failed\n"),
8098 service_name_locale);
8099 goto leave;
8102 /* Check for response status */
8103 err = isds_response_status(context, service, *response,
8104 code, status_message, NULL);
8105 if (err) {
8106 isds_log(ILF_ISDS, ILL_DEBUG,
8107 _("ISDS response on %s request is missing status\n"),
8108 service_name_locale);
8109 goto leave;
8112 /* Request processed, but nothing found */
8113 if (xmlStrcmp(*code, BAD_CAST "0000")) {
8114 char *code_locale = _isds_utf82locale((char*) *code);
8115 char *status_message_locale = _isds_utf82locale((char*) *status_message);
8116 isds_log(ILF_ISDS, ILL_DEBUG,
8117 _("Server refused %s request for %s message ID "
8118 "(code=%s, message=%s)\n"),
8119 service_name_locale, message_id_locale,
8120 code_locale, status_message_locale);
8121 isds_log_message(context, status_message_locale);
8122 free(code_locale);
8123 free(status_message_locale);
8124 err = IE_ISDS;
8125 goto leave;
8128 leave:
8129 free(message_id_locale);
8130 free(service_name_locale);
8131 xmlFreeNode(request);
8132 return err;
8136 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
8137 * signed data and free ISDS response.
8138 * @context is session context
8139 * @message_id is UTF-8 encoded message ID for logging purpose
8140 * @response is parsed XML document. It will be freed and NULLed in the middle
8141 * of function run to save memory. This is not guaranteed in case of error.
8142 * @request_name is name of ISDS request used to construct response root
8143 * element name and for logging purpose.
8144 * @raw is reallocated output buffer with DER encoded CMS data
8145 * @raw_length is size of @raw buffer in bytes
8146 * @returns standard error codes, in case of error, @raw will be freed and
8147 * NULLed, @response sometimes. */
8148 static isds_error find_extract_signed_data_free_response(
8149 struct isds_ctx *context, const xmlChar *message_id,
8150 xmlDocPtr *response, const xmlChar *request_name,
8151 void **raw, size_t *raw_length) {
8153 isds_error err = IE_SUCCESS;
8154 char *xpath_expression = NULL;
8155 xmlXPathContextPtr xpath_ctx = NULL;
8156 xmlXPathObjectPtr result = NULL;
8157 char *encoded_structure = NULL;
8159 if (!context) return IE_INVALID_CONTEXT;
8160 if (!raw) return IE_INVAL;
8161 zfree(*raw);
8162 if (!message_id || !response || !*response || !request_name || !raw_length)
8163 return IE_INVAL;
8165 /* Build XPath expression */
8166 xpath_expression = _isds_astrcat3("/isds:", (char *) request_name,
8167 "Response/isds:dmSignature");
8168 if (!xpath_expression) return IE_NOMEM;
8170 /* Extract data */
8171 xpath_ctx = xmlXPathNewContext(*response);
8172 if (!xpath_ctx) {
8173 err = IE_ERROR;
8174 goto leave;
8176 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8177 err = IE_ERROR;
8178 goto leave;
8180 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
8181 if (!result) {
8182 err = IE_ERROR;
8183 goto leave;
8185 /* Empty response */
8186 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8187 char *message_id_locale = _isds_utf82locale((char*) message_id);
8188 isds_printf_message(context,
8189 _("Server did not return any signed data for message ID `%s' "
8190 "on %s request"),
8191 message_id_locale, request_name);
8192 free(message_id_locale);
8193 err = IE_ISDS;
8194 goto leave;
8196 /* More responses */
8197 if (result->nodesetval->nodeNr > 1) {
8198 char *message_id_locale = _isds_utf82locale((char*) message_id);
8199 isds_printf_message(context,
8200 _("Server did return more signed data for message ID `%s' "
8201 "on %s request"),
8202 message_id_locale, request_name);
8203 free(message_id_locale);
8204 err = IE_ISDS;
8205 goto leave;
8207 /* One response */
8208 xpath_ctx->node = result->nodesetval->nodeTab[0];
8210 /* Extract PKCS#7 structure */
8211 EXTRACT_STRING(".", encoded_structure);
8212 if (!encoded_structure) {
8213 isds_log_message(context, _("dmSignature element is empty"));
8216 /* Here we have delivery info as standalone CMS in encoded_structure.
8217 * We don't need any other data, free them: */
8218 xmlXPathFreeObject(result); result = NULL;
8219 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
8220 xmlFreeDoc(*response); *response = NULL;
8223 /* Decode PKCS#7 to DER format */
8224 *raw_length = _isds_b64decode(encoded_structure, raw);
8225 if (*raw_length == (size_t) -1) {
8226 isds_log_message(context,
8227 _("Error while Base64-decoding PKCS#7 structure"));
8228 err = IE_ERROR;
8229 goto leave;
8232 leave:
8233 if (err) {
8234 zfree(*raw);
8235 raw_length = 0;
8238 free(encoded_structure);
8239 xmlXPathFreeObject(result);
8240 xmlXPathFreeContext(xpath_ctx);
8241 free(xpath_expression);
8243 return err;
8245 #endif /* HAVE_LIBCURL */
8248 /* Download incoming message envelope identified by ID.
8249 * @context is session context
8250 * @message_id is message identifier (you can get them from
8251 * isds_get_list_of_received_messages())
8252 * @message is automatically reallocated message retrieved from ISDS.
8253 * It will miss documents per se. Use isds_get_received_message(), if you are
8254 * interested in documents (content) too.
8255 * Returned hash and timestamp require documents to be verifiable. */
8256 isds_error isds_get_received_envelope(struct isds_ctx *context,
8257 const char *message_id, struct isds_message **message) {
8259 isds_error err = IE_SUCCESS;
8260 #if HAVE_LIBCURL
8261 xmlDocPtr response = NULL;
8262 xmlChar *code = NULL, *status_message = NULL;
8263 xmlXPathContextPtr xpath_ctx = NULL;
8264 xmlXPathObjectPtr result = NULL;
8265 #endif
8267 if (!context) return IE_INVALID_CONTEXT;
8268 zfree(context->long_message);
8270 /* Free former message if any */
8271 if (!message) return IE_INVAL;
8272 isds_message_free(message);
8274 #if HAVE_LIBCURL
8275 /* Do request and check for success */
8276 err = build_send_check_message_request(context, SERVICE_DM_INFO,
8277 BAD_CAST "MessageEnvelopeDownload", message_id,
8278 &response, NULL, NULL, &code, &status_message);
8279 if (err) goto leave;
8281 /* Extract data */
8282 xpath_ctx = xmlXPathNewContext(response);
8283 if (!xpath_ctx) {
8284 err = IE_ERROR;
8285 goto leave;
8287 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8288 err = IE_ERROR;
8289 goto leave;
8291 result = xmlXPathEvalExpression(
8292 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
8293 "isds:dmReturnedMessageEnvelope",
8294 xpath_ctx);
8295 if (!result) {
8296 err = IE_ERROR;
8297 goto leave;
8299 /* Empty response */
8300 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8301 char *message_id_locale = _isds_utf82locale((char*) message_id);
8302 isds_printf_message(context,
8303 _("Server did not return any envelope for ID `%s' "
8304 "on MessageEnvelopeDownload request"), message_id_locale);
8305 free(message_id_locale);
8306 err = IE_ISDS;
8307 goto leave;
8309 /* More envelops */
8310 if (result->nodesetval->nodeNr > 1) {
8311 char *message_id_locale = _isds_utf82locale((char*) message_id);
8312 isds_printf_message(context,
8313 _("Server did return more envelopes for ID `%s' "
8314 "on MessageEnvelopeDownload request"), message_id_locale);
8315 free(message_id_locale);
8316 err = IE_ISDS;
8317 goto leave;
8319 /* One message */
8320 xpath_ctx->node = result->nodesetval->nodeTab[0];
8322 /* Extract the envelope (= message without documents, hence 0) */
8323 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
8324 if (err) goto leave;
8326 /* Save XML blob */
8327 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
8328 &(*message)->raw_length);
8330 leave:
8331 if (err) {
8332 isds_message_free(message);
8335 xmlXPathFreeObject(result);
8336 xmlXPathFreeContext(xpath_ctx);
8338 free(code);
8339 free(status_message);
8340 if (!*message || !(*message)->xml) {
8341 xmlFreeDoc(response);
8344 if (!err)
8345 isds_log(ILF_ISDS, ILL_DEBUG,
8346 _("MessageEnvelopeDownload request processed by server "
8347 "successfully.\n")
8349 #else /* not HAVE_LIBCURL */
8350 err = IE_NOTSUP;
8351 #endif
8352 return err;
8356 /* Load delivery info of any format from buffer.
8357 * @context is session context
8358 * @raw_type advertises format of @buffer content. Only delivery info types
8359 * are accepted.
8360 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
8361 * retrieve such data from message->raw after calling
8362 * isds_get_signed_delivery_info().
8363 * @length is length of buffer in bytes.
8364 * @message is automatically reallocated message parsed from @buffer.
8365 * @strategy selects how buffer will be attached into raw isds_message member.
8366 * */
8367 isds_error isds_load_delivery_info(struct isds_ctx *context,
8368 const isds_raw_type raw_type,
8369 const void *buffer, const size_t length,
8370 struct isds_message **message, const isds_buffer_strategy strategy) {
8372 isds_error err = IE_SUCCESS;
8373 message_ns_type message_ns;
8374 xmlDocPtr message_doc = NULL;
8375 xmlXPathContextPtr xpath_ctx = NULL;
8376 xmlXPathObjectPtr result = NULL;
8377 void *xml_stream = NULL;
8378 size_t xml_stream_length = 0;
8380 if (!context) return IE_INVALID_CONTEXT;
8381 zfree(context->long_message);
8382 if (!message) return IE_INVAL;
8383 isds_message_free(message);
8384 if (!buffer) return IE_INVAL;
8387 /* Select buffer format and extract XML from CMS*/
8388 switch (raw_type) {
8389 case RAWTYPE_DELIVERYINFO:
8390 message_ns = MESSAGE_NS_UNSIGNED;
8391 xml_stream = (void *) buffer;
8392 xml_stream_length = length;
8393 break;
8395 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
8396 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
8397 xml_stream = (void *) buffer;
8398 xml_stream_length = length;
8399 break;
8401 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
8402 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
8403 err = _isds_extract_cms_data(context, buffer, length,
8404 &xml_stream, &xml_stream_length);
8405 if (err) goto leave;
8406 break;
8408 default:
8409 isds_log_message(context, _("Bad raw delivery representation type"));
8410 return IE_INVAL;
8411 break;
8414 isds_log(ILF_ISDS, ILL_DEBUG,
8415 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
8416 xml_stream_length, xml_stream);
8418 /* Convert delivery info XML stream into XPath context */
8419 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
8420 if (!message_doc) {
8421 err = IE_XML;
8422 goto leave;
8424 xpath_ctx = xmlXPathNewContext(message_doc);
8425 if (!xpath_ctx) {
8426 err = IE_ERROR;
8427 goto leave;
8429 /* XXX: Name spaces mangled for signed delivery info:
8430 * http://isds.czechpoint.cz/v20/delivery:
8432 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
8433 * <q:dmDelivery>
8434 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
8435 * <p:dmID>170272</p:dmID>
8436 * ...
8437 * </p:dmDm>
8438 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
8439 * ...
8440 * </q:dmEvents>...</q:dmEvents>
8441 * </q:dmDelivery>
8442 * </q:GetDeliveryInfoResponse>
8443 * */
8444 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
8445 err = IE_ERROR;
8446 goto leave;
8448 result = xmlXPathEvalExpression(
8449 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
8450 xpath_ctx);
8451 if (!result) {
8452 err = IE_ERROR;
8453 goto leave;
8455 /* Empty delivery info */
8456 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8457 isds_printf_message(context,
8458 _("XML document is not sisds:dmDelivery document"));
8459 err = IE_ISDS;
8460 goto leave;
8462 /* More delivery info's */
8463 if (result->nodesetval->nodeNr > 1) {
8464 isds_printf_message(context,
8465 _("XML document has more sisds:dmDelivery elements"));
8466 err = IE_ISDS;
8467 goto leave;
8469 /* One delivery info */
8470 xpath_ctx->node = result->nodesetval->nodeTab[0];
8472 /* Extract the envelope (= message without documents, hence 0).
8473 * XXX: extract_TReturnedMessage() can obtain attachments size,
8474 * but delivery info carries none. It's coded as option elements,
8475 * so it should work. */
8476 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
8477 if (err) goto leave;
8479 /* Extract events */
8480 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
8481 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
8482 if (err) { err = IE_ERROR; goto leave; }
8483 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
8484 if (err) goto leave;
8486 /* Append raw CMS structure into message */
8487 (*message)->raw_type = raw_type;
8488 switch (strategy) {
8489 case BUFFER_DONT_STORE:
8490 break;
8491 case BUFFER_COPY:
8492 (*message)->raw = malloc(length);
8493 if (!(*message)->raw) {
8494 err = IE_NOMEM;
8495 goto leave;
8497 memcpy((*message)->raw, buffer, length);
8498 (*message)->raw_length = length;
8499 break;
8500 case BUFFER_MOVE:
8501 (*message)->raw = (void *) buffer;
8502 (*message)->raw_length = length;
8503 break;
8504 default:
8505 err = IE_ENUM;
8506 goto leave;
8509 leave:
8510 if (err) {
8511 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
8512 isds_message_free(message);
8515 xmlXPathFreeObject(result);
8516 xmlXPathFreeContext(xpath_ctx);
8517 if (!*message || !(*message)->xml) {
8518 xmlFreeDoc(message_doc);
8520 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
8522 if (!err)
8523 isds_log(ILF_ISDS, ILL_DEBUG,
8524 _("Delivery info loaded successfully.\n"));
8525 return err;
8529 /* Download signed delivery info-sheet of given message identified by ID.
8530 * @context is session context
8531 * @message_id is message identifier (you can get them from
8532 * isds_get_list_of_{sent,received}_messages())
8533 * @message is automatically reallocated message retrieved from ISDS.
8534 * It will miss documents per se. Use isds_get_signed_received_message(),
8535 * if you are interested in documents (content). OTOH, only this function
8536 * can get list events message has gone through. */
8537 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
8538 const char *message_id, struct isds_message **message) {
8540 isds_error err = IE_SUCCESS;
8541 #if HAVE_LIBCURL
8542 xmlDocPtr response = NULL;
8543 xmlChar *code = NULL, *status_message = NULL;
8544 void *raw = NULL;
8545 size_t raw_length = 0;
8546 #endif
8548 if (!context) return IE_INVALID_CONTEXT;
8549 zfree(context->long_message);
8551 /* Free former message if any */
8552 if (!message) return IE_INVAL;
8553 isds_message_free(message);
8555 #if HAVE_LIBCURL
8556 /* Do request and check for success */
8557 err = build_send_check_message_request(context, SERVICE_DM_INFO,
8558 BAD_CAST "GetSignedDeliveryInfo", message_id,
8559 &response, NULL, NULL, &code, &status_message);
8560 if (err) goto leave;
8562 /* Find signed delivery info, extract it into raw and maybe free
8563 * response */
8564 err = find_extract_signed_data_free_response(context,
8565 (xmlChar *)message_id, &response,
8566 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
8567 if (err) goto leave;
8569 /* Parse delivery info */
8570 err = isds_load_delivery_info(context,
8571 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
8572 message, BUFFER_MOVE);
8573 if (err) goto leave;
8575 raw = NULL;
8577 leave:
8578 if (err) {
8579 isds_message_free(message);
8582 free(raw);
8583 free(code);
8584 free(status_message);
8585 xmlFreeDoc(response);
8587 if (!err)
8588 isds_log(ILF_ISDS, ILL_DEBUG,
8589 _("GetSignedDeliveryInfo request processed by server "
8590 "successfully.\n")
8592 #else /* not HAVE_LIBCURL */
8593 err = IE_NOTSUP;
8594 #endif
8595 return err;
8599 /* Download delivery info-sheet of given message identified by ID.
8600 * @context is session context
8601 * @message_id is message identifier (you can get them from
8602 * isds_get_list_of_{sent,received}_messages())
8603 * @message is automatically reallocated message retrieved from ISDS.
8604 * It will miss documents per se. Use isds_get_received_message(), if you are
8605 * interested in documents (content). OTOH, only this function can get list
8606 * of events message has gone through. */
8607 isds_error isds_get_delivery_info(struct isds_ctx *context,
8608 const char *message_id, struct isds_message **message) {
8610 isds_error err = IE_SUCCESS;
8611 #if HAVE_LIBCURL
8612 xmlDocPtr response = NULL;
8613 xmlChar *code = NULL, *status_message = NULL;
8614 xmlNodePtr delivery_node = NULL;
8615 void *raw = NULL;
8616 size_t raw_length = 0;
8617 #endif
8619 if (!context) return IE_INVALID_CONTEXT;
8620 zfree(context->long_message);
8622 /* Free former message if any */
8623 if (!message) return IE_INVAL;
8624 isds_message_free(message);
8626 #if HAVE_LIBCURL
8627 /* Do request and check for success */
8628 err = build_send_check_message_request(context, SERVICE_DM_INFO,
8629 BAD_CAST "GetDeliveryInfo", message_id,
8630 &response, NULL, NULL, &code, &status_message);
8631 if (err) goto leave;
8634 /* Serialize delivery info */
8635 delivery_node = xmlDocGetRootElement(response);
8636 if (!delivery_node) {
8637 char *message_id_locale = _isds_utf82locale((char*) message_id);
8638 isds_printf_message(context,
8639 _("Server did not return any delivery info for ID `%s' "
8640 "on GetDeliveryInfo request"), message_id_locale);
8641 free(message_id_locale);
8642 err = IE_ISDS;
8643 goto leave;
8645 err = serialize_subtree(context, delivery_node, &raw, &raw_length);
8646 if (err) goto leave;
8648 /* Parse delivery info */
8649 /* TODO: Here we parse the response second time. We could single delivery
8650 * parser from isds_load_delivery_info() to make things faster. */
8651 err = isds_load_delivery_info(context,
8652 RAWTYPE_DELIVERYINFO, raw, raw_length,
8653 message, BUFFER_MOVE);
8654 if (err) goto leave;
8656 raw = NULL;
8659 leave:
8660 if (err) {
8661 isds_message_free(message);
8664 free(raw);
8665 free(code);
8666 free(status_message);
8667 xmlFreeDoc(response);
8669 if (!err)
8670 isds_log(ILF_ISDS, ILL_DEBUG,
8671 _("GetDeliveryInfo request processed by server "
8672 "successfully.\n")
8674 #else /* not HAVE_LIBCURL */
8675 err = IE_NOTSUP;
8676 #endif
8677 return err;
8681 /* Download incoming message identified by ID.
8682 * @context is session context
8683 * @message_id is message identifier (you can get them from
8684 * isds_get_list_of_received_messages())
8685 * @message is automatically reallocated message retrieved from ISDS */
8686 isds_error isds_get_received_message(struct isds_ctx *context,
8687 const char *message_id, struct isds_message **message) {
8689 isds_error err = IE_SUCCESS;
8690 #if HAVE_LIBCURL
8691 xmlDocPtr response = NULL;
8692 void *xml_stream = NULL;
8693 size_t xml_stream_length;
8694 xmlChar *code = NULL, *status_message = NULL;
8695 xmlXPathContextPtr xpath_ctx = NULL;
8696 xmlXPathObjectPtr result = NULL;
8697 char *phys_path = NULL;
8698 size_t phys_start, phys_end;
8699 #endif
8701 if (!context) return IE_INVALID_CONTEXT;
8702 zfree(context->long_message);
8704 /* Free former message if any */
8705 if (message) isds_message_free(message);
8707 #if HAVE_LIBCURL
8708 /* Do request and check for success */
8709 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
8710 BAD_CAST "MessageDownload", message_id,
8711 &response, &xml_stream, &xml_stream_length,
8712 &code, &status_message);
8713 if (err) goto leave;
8715 /* Extract data */
8716 xpath_ctx = xmlXPathNewContext(response);
8717 if (!xpath_ctx) {
8718 err = IE_ERROR;
8719 goto leave;
8721 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8722 err = IE_ERROR;
8723 goto leave;
8725 result = xmlXPathEvalExpression(
8726 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
8727 xpath_ctx);
8728 if (!result) {
8729 err = IE_ERROR;
8730 goto leave;
8732 /* Empty response */
8733 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8734 char *message_id_locale = _isds_utf82locale((char*) message_id);
8735 isds_printf_message(context,
8736 _("Server did not return any message for ID `%s' "
8737 "on MessageDownload request"), message_id_locale);
8738 free(message_id_locale);
8739 err = IE_ISDS;
8740 goto leave;
8742 /* More messages */
8743 if (result->nodesetval->nodeNr > 1) {
8744 char *message_id_locale = _isds_utf82locale((char*) message_id);
8745 isds_printf_message(context,
8746 _("Server did return more messages for ID `%s' "
8747 "on MessageDownload request"), message_id_locale);
8748 free(message_id_locale);
8749 err = IE_ISDS;
8750 goto leave;
8752 /* One message */
8753 xpath_ctx->node = result->nodesetval->nodeTab[0];
8755 /* Extract the message */
8756 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
8757 if (err) goto leave;
8759 /* Locate raw XML blob */
8760 phys_path = strdup(
8761 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
8762 PHYSXML_ELEMENT_SEPARATOR
8763 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
8764 PHYSXML_ELEMENT_SEPARATOR
8765 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
8767 if (!phys_path) {
8768 err = IE_NOMEM;
8769 goto leave;
8771 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
8772 phys_path, &phys_start, &phys_end);
8773 zfree(phys_path);
8774 if (err) {
8775 isds_log_message(context,
8776 _("Substring with isds:MessageDownloadResponse element "
8777 "could not be located in raw SOAP message"));
8778 goto leave;
8780 /* Save XML blob */
8781 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
8782 &(*message)->raw_length);*/
8783 /* TODO: Store name space declarations from ancestors */
8784 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
8785 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
8786 (*message)->raw_length = phys_end - phys_start + 1;
8787 (*message)->raw = malloc((*message)->raw_length);
8788 if (!(*message)->raw) {
8789 err = IE_NOMEM;
8790 goto leave;
8792 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
8795 leave:
8796 if (err) {
8797 isds_message_free(message);
8800 free(phys_path);
8802 xmlXPathFreeObject(result);
8803 xmlXPathFreeContext(xpath_ctx);
8805 free(code);
8806 free(status_message);
8807 free(xml_stream);
8808 if (!*message || !(*message)->xml) {
8809 xmlFreeDoc(response);
8812 if (!err)
8813 isds_log(ILF_ISDS, ILL_DEBUG,
8814 _("MessageDownload request processed by server "
8815 "successfully.\n")
8817 #else /* not HAVE_LIBCURL */
8818 err = IE_NOTSUP;
8819 #endif
8820 return err;
8824 /* Load message of any type from buffer.
8825 * @context is session context
8826 * @raw_type defines content type of @buffer. Only message types are allowed.
8827 * @buffer is message raw representation. Format (CMS, plain signed,
8828 * message direction) is defined in @raw_type. You can retrieve such data
8829 * from message->raw after calling isds_get_[signed]{received,sent}_message().
8830 * @length is length of buffer in bytes.
8831 * @message is automatically reallocated message parsed from @buffer.
8832 * @strategy selects how buffer will be attached into raw isds_message member.
8833 * */
8834 isds_error isds_load_message(struct isds_ctx *context,
8835 const isds_raw_type raw_type, const void *buffer, const size_t length,
8836 struct isds_message **message, const isds_buffer_strategy strategy) {
8838 isds_error err = IE_SUCCESS;
8839 void *xml_stream = NULL;
8840 size_t xml_stream_length = 0;
8841 message_ns_type message_ns;
8842 xmlDocPtr message_doc = NULL;
8843 xmlXPathContextPtr xpath_ctx = NULL;
8844 xmlXPathObjectPtr result = NULL;
8846 if (!context) return IE_INVALID_CONTEXT;
8847 zfree(context->long_message);
8848 if (!message) return IE_INVAL;
8849 isds_message_free(message);
8850 if (!buffer) return IE_INVAL;
8853 /* Select buffer format and extract XML from CMS*/
8854 switch (raw_type) {
8855 case RAWTYPE_INCOMING_MESSAGE:
8856 message_ns = MESSAGE_NS_UNSIGNED;
8857 xml_stream = (void *) buffer;
8858 xml_stream_length = length;
8859 break;
8861 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
8862 message_ns = MESSAGE_NS_SIGNED_INCOMING;
8863 xml_stream = (void *) buffer;
8864 xml_stream_length = length;
8865 break;
8867 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
8868 message_ns = MESSAGE_NS_SIGNED_INCOMING;
8869 err = _isds_extract_cms_data(context, buffer, length,
8870 &xml_stream, &xml_stream_length);
8871 if (err) goto leave;
8872 break;
8874 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
8875 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
8876 xml_stream = (void *) buffer;
8877 xml_stream_length = length;
8878 break;
8880 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
8881 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
8882 err = _isds_extract_cms_data(context, buffer, length,
8883 &xml_stream, &xml_stream_length);
8884 if (err) goto leave;
8885 break;
8887 default:
8888 isds_log_message(context, _("Bad raw message representation type"));
8889 return IE_INVAL;
8890 break;
8893 isds_log(ILF_ISDS, ILL_DEBUG,
8894 _("Loading message:\n%.*s\nEnd of message\n"),
8895 xml_stream_length, xml_stream);
8897 /* Convert messages XML stream into XPath context */
8898 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
8899 if (!message_doc) {
8900 err = IE_XML;
8901 goto leave;
8903 xpath_ctx = xmlXPathNewContext(message_doc);
8904 if (!xpath_ctx) {
8905 err = IE_ERROR;
8906 goto leave;
8908 /* XXX: Standard name space for unsigned incoming direction:
8909 * http://isds.czechpoint.cz/v20/
8911 * XXX: Name spaces mangled for signed outgoing direction:
8912 * http://isds.czechpoint.cz/v20/SentMessage:
8914 * <q:MessageDownloadResponse
8915 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
8916 * <q:dmReturnedMessage>
8917 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
8918 * <p:dmID>151916</p:dmID>
8919 * ...
8920 * </p:dmDm>
8921 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
8922 * ...
8923 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
8924 * </q:dmReturnedMessage>
8925 * </q:MessageDownloadResponse>
8927 * XXX: Name spaces mangled for signed incoming direction:
8928 * http://isds.czechpoint.cz/v20/message:
8930 * <q:MessageDownloadResponse
8931 * xmlns:q="http://isds.czechpoint.cz/v20/message">
8932 * <q:dmReturnedMessage>
8933 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
8934 * <p:dmID>151916</p:dmID>
8935 * ...
8936 * </p:dmDm>
8937 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
8938 * ...
8939 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
8940 * </q:dmReturnedMessage>
8941 * </q:MessageDownloadResponse>
8943 * Stupidity of ISDS developers is unlimited */
8944 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
8945 err = IE_ERROR;
8946 goto leave;
8948 result = xmlXPathEvalExpression(
8949 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
8950 xpath_ctx);
8951 if (!result) {
8952 err = IE_ERROR;
8953 goto leave;
8955 /* Empty message */
8956 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8957 isds_printf_message(context,
8958 _("XML document does not contain "
8959 "sisds:dmReturnedMessage element"));
8960 err = IE_ISDS;
8961 goto leave;
8963 /* More messages */
8964 if (result->nodesetval->nodeNr > 1) {
8965 isds_printf_message(context,
8966 _("XML document has more sisds:dmReturnedMessage elements"));
8967 err = IE_ISDS;
8968 goto leave;
8970 /* One message */
8971 xpath_ctx->node = result->nodesetval->nodeTab[0];
8973 /* Extract the message */
8974 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
8975 if (err) goto leave;
8977 /* Append raw buffer into message */
8978 (*message)->raw_type = raw_type;
8979 switch (strategy) {
8980 case BUFFER_DONT_STORE:
8981 break;
8982 case BUFFER_COPY:
8983 (*message)->raw = malloc(length);
8984 if (!(*message)->raw) {
8985 err = IE_NOMEM;
8986 goto leave;
8988 memcpy((*message)->raw, buffer, length);
8989 (*message)->raw_length = length;
8990 break;
8991 case BUFFER_MOVE:
8992 (*message)->raw = (void *) buffer;
8993 (*message)->raw_length = length;
8994 break;
8995 default:
8996 err = IE_ENUM;
8997 goto leave;
9001 leave:
9002 if (err) {
9003 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
9004 isds_message_free(message);
9007 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
9008 xmlXPathFreeObject(result);
9009 xmlXPathFreeContext(xpath_ctx);
9010 if (!*message || !(*message)->xml) {
9011 xmlFreeDoc(message_doc);
9014 if (!err)
9015 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
9016 return err;
9020 /* Determine type of raw message or delivery info according some heuristics.
9021 * It does not validate the raw blob.
9022 * @context is session context
9023 * @raw_type returns content type of @buffer. Valid only if exit code of this
9024 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
9025 * reallocated memory.
9026 * @buffer is message raw representation.
9027 * @length is length of buffer in bytes. */
9028 isds_error isds_guess_raw_type(struct isds_ctx *context,
9029 isds_raw_type *raw_type, const void *buffer, const size_t length) {
9030 isds_error err;
9031 void *xml_stream = NULL;
9032 size_t xml_stream_length = 0;
9033 xmlDocPtr document = NULL;
9034 xmlNodePtr root = NULL;
9036 if (!context) return IE_INVALID_CONTEXT;
9037 zfree(context->long_message);
9038 if (length == 0 || !buffer) return IE_INVAL;
9039 if (!raw_type) return IE_INVAL;
9041 /* Try CMS */
9042 err = _isds_extract_cms_data(context, buffer, length,
9043 &xml_stream, &xml_stream_length);
9044 if (err) {
9045 xml_stream = (void *) buffer;
9046 xml_stream_length = (size_t) length;
9047 err = IE_SUCCESS;
9050 /* Try XML */
9051 document = xmlParseMemory(xml_stream, xml_stream_length);
9052 if (!document) {
9053 isds_printf_message(context,
9054 _("Could not parse data as XML document"));
9055 err = IE_NOTSUP;
9056 goto leave;
9059 /* Get root element */
9060 root = xmlDocGetRootElement(document);
9061 if (!root) {
9062 isds_printf_message(context,
9063 _("XML document is missing root element"));
9064 err = IE_XML;
9065 goto leave;
9068 if (!root->ns || !root->ns->href) {
9069 isds_printf_message(context,
9070 _("Root element does not belong to any name space"));
9071 err = IE_NOTSUP;
9072 goto leave;
9075 /* Test name space */
9076 if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_INCOMING_NS)) {
9077 if (xml_stream == buffer)
9078 *raw_type = RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE;
9079 else
9080 *raw_type = RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
9081 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_OUTGOING_NS)) {
9082 if (xml_stream == buffer)
9083 *raw_type = RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE;
9084 else
9085 *raw_type = RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE;
9086 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_DELIVERY_NS)) {
9087 if (xml_stream == buffer)
9088 *raw_type = RAWTYPE_PLAIN_SIGNED_DELIVERYINFO;
9089 else
9090 *raw_type = RAWTYPE_CMS_SIGNED_DELIVERYINFO;
9091 } else if (!xmlStrcmp(root->ns->href, BAD_CAST ISDS_NS)) {
9092 if (xml_stream != buffer) {
9093 isds_printf_message(context,
9094 _("Document in ISDS name space is encapsulated into CMS" ));
9095 err = IE_NOTSUP;
9096 } else if (!xmlStrcmp(root->name, BAD_CAST "MessageDownloadResponse"))
9097 *raw_type = RAWTYPE_INCOMING_MESSAGE;
9098 else if (!xmlStrcmp(root->name, BAD_CAST "GetDeliveryInfoResponse"))
9099 *raw_type = RAWTYPE_DELIVERYINFO;
9100 else {
9101 isds_printf_message(context,
9102 _("Unknown root element in ISDS name space"));
9103 err = IE_NOTSUP;
9105 } else {
9106 isds_printf_message(context,
9107 _("Unknown name space"));
9108 err = IE_NOTSUP;
9111 leave:
9112 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
9113 xmlFreeDoc(document);
9114 return err;
9118 /* Download signed incoming/outgoing message identified by ID.
9119 * @context is session context
9120 * @output is true for outgoing message, false for incoming message
9121 * @message_id is message identifier (you can get them from
9122 * isds_get_list_of_{sent,received}_messages())
9123 * @message is automatically reallocated message retrieved from ISDS. The raw
9124 * member will be filled with PKCS#7 structure in DER format. */
9125 static isds_error isds_get_signed_message(struct isds_ctx *context,
9126 const _Bool outgoing, const char *message_id,
9127 struct isds_message **message) {
9129 isds_error err = IE_SUCCESS;
9130 #if HAVE_LIBCURL
9131 xmlDocPtr response = NULL;
9132 xmlChar *code = NULL, *status_message = NULL;
9133 xmlXPathContextPtr xpath_ctx = NULL;
9134 xmlXPathObjectPtr result = NULL;
9135 char *encoded_structure = NULL;
9136 void *raw = NULL;
9137 size_t raw_length = 0;
9138 #endif
9140 if (!context) return IE_INVALID_CONTEXT;
9141 zfree(context->long_message);
9142 if (!message) return IE_INVAL;
9143 isds_message_free(message);
9145 #if HAVE_LIBCURL
9146 /* Do request and check for success */
9147 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
9148 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
9149 BAD_CAST "SignedMessageDownload",
9150 message_id, &response, NULL, NULL, &code, &status_message);
9151 if (err) goto leave;
9153 /* Find signed message, extract it into raw and maybe free
9154 * response */
9155 err = find_extract_signed_data_free_response(context,
9156 (xmlChar *)message_id, &response,
9157 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
9158 BAD_CAST "SignedMessageDownload",
9159 &raw, &raw_length);
9160 if (err) goto leave;
9162 /* Parse message */
9163 err = isds_load_message(context,
9164 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
9165 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
9166 raw, raw_length, message, BUFFER_MOVE);
9167 if (err) goto leave;
9169 raw = NULL;
9171 leave:
9172 if (err) {
9173 isds_message_free(message);
9176 free(encoded_structure);
9177 xmlXPathFreeObject(result);
9178 xmlXPathFreeContext(xpath_ctx);
9179 free(raw);
9181 free(code);
9182 free(status_message);
9183 xmlFreeDoc(response);
9185 if (!err)
9186 isds_log(ILF_ISDS, ILL_DEBUG,
9187 (outgoing) ?
9188 _("SignedSentMessageDownload request processed by server "
9189 "successfully.\n") :
9190 _("SignedMessageDownload request processed by server "
9191 "successfully.\n")
9193 #else /* not HAVE_LIBCURL */
9194 err = IE_NOTSUP;
9195 #endif
9196 return err;
9200 /* Download signed incoming message identified by ID.
9201 * @context is session context
9202 * @message_id is message identifier (you can get them from
9203 * isds_get_list_of_received_messages())
9204 * @message is automatically reallocated message retrieved from ISDS. The raw
9205 * member will be filled with PKCS#7 structure in DER format. */
9206 isds_error isds_get_signed_received_message(struct isds_ctx *context,
9207 const char *message_id, struct isds_message **message) {
9208 return isds_get_signed_message(context, 0, message_id, message);
9212 /* Download signed outgoing message identified by ID.
9213 * @context is session context
9214 * @message_id is message identifier (you can get them from
9215 * isds_get_list_of_sent_messages())
9216 * @message is automatically reallocated message retrieved from ISDS. The raw
9217 * member will be filled with PKCS#7 structure in DER format. */
9218 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
9219 const char *message_id, struct isds_message **message) {
9220 return isds_get_signed_message(context, 1, message_id, message);
9224 /* Get type and name of user who sent a message identified by ID.
9225 * @context is session context
9226 * @message_id is message identifier
9227 * @sender_type is pointer to automatically allocated type of sender detected
9228 * from @raw_sender_type string. If @raw_sender_type is unknown to this
9229 * library or to the server, NULL will be returned. Pass NULL if you don't
9230 * care about it.
9231 * @raw_sender_type is automatically reallocated UTF-8 string describing
9232 * sender type or NULL if not known to server. Pass NULL if you don't care.
9233 * @sender_name is automatically reallocated UTF-8 name of user who sent the
9234 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
9235 isds_error isds_get_message_sender(struct isds_ctx *context,
9236 const char *message_id, isds_sender_type **sender_type,
9237 char **raw_sender_type, char **sender_name) {
9238 isds_error err = IE_SUCCESS;
9239 #if HAVE_LIBCURL
9240 xmlDocPtr response = NULL;
9241 xmlChar *code = NULL, *status_message = NULL;
9242 xmlXPathContextPtr xpath_ctx = NULL;
9243 xmlXPathObjectPtr result = NULL;
9244 char *type_string = NULL;
9245 #endif
9247 if (!context) return IE_INVALID_CONTEXT;
9248 zfree(context->long_message);
9249 if (sender_type) zfree(*sender_type);
9250 if (raw_sender_type) zfree(*raw_sender_type);
9251 if (sender_name) zfree(*sender_name);
9252 if (!message_id) return IE_INVAL;
9254 #if HAVE_LIBCURL
9255 /* Do request and check for success */
9256 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9257 BAD_CAST "GetMessageAuthor",
9258 message_id, &response, NULL, NULL, &code, &status_message);
9259 if (err) goto leave;
9261 /* Extract data */
9262 xpath_ctx = xmlXPathNewContext(response);
9263 if (!xpath_ctx) {
9264 err = IE_ERROR;
9265 goto leave;
9267 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9268 err = IE_ERROR;
9269 goto leave;
9271 result = xmlXPathEvalExpression(
9272 BAD_CAST "/isds:GetMessageAuthorResponse", xpath_ctx);
9273 if (!result) {
9274 err = IE_ERROR;
9275 goto leave;
9277 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9278 isds_log_message(context,
9279 _("Missing GetMessageAuthorResponse element"));
9280 err = IE_ISDS;
9281 goto leave;
9283 if (result->nodesetval->nodeNr > 1) {
9284 isds_log_message(context,
9285 _("Multiple GetMessageAuthorResponse element"));
9286 err = IE_ISDS;
9287 goto leave;
9289 xpath_ctx->node = result->nodesetval->nodeTab[0];
9290 xmlXPathFreeObject(result); result = NULL;
9292 /* Fill output arguments in */
9293 EXTRACT_STRING("isds:userType", type_string);
9294 if (type_string) {
9295 *sender_type = calloc(1, sizeof(**sender_type));
9296 if (!*sender_type) {
9297 err = IE_NOMEM;
9298 goto leave;
9301 if (sender_type) {
9302 err = string2isds_sender_type((xmlChar *)type_string,
9303 *sender_type);
9304 if (err) {
9305 zfree(*sender_type);
9306 if (err == IE_ENUM) {
9307 err = IE_SUCCESS;
9308 char *type_string_locale = _isds_utf82locale(type_string);
9309 isds_log(ILF_ISDS, ILL_WARNING,
9310 _("Unknown isds:userType value: %s"),
9311 type_string_locale);
9312 free(type_string_locale);
9317 if (sender_type)
9318 EXTRACT_STRING("isds:authorName", *sender_name);
9320 leave:
9321 if (err) {
9322 if (sender_type) zfree(*sender_type);
9323 zfree(type_string);
9324 if (sender_name) zfree(*sender_name);
9326 if (raw_sender_type) *raw_sender_type = type_string;
9328 xmlXPathFreeObject(result);
9329 xmlXPathFreeContext(xpath_ctx);
9331 free(code);
9332 free(status_message);
9333 xmlFreeDoc(response);
9335 if (!err)
9336 isds_log(ILF_ISDS, ILL_DEBUG,
9337 _("GetMessageAuthor request processed by server "
9338 "successfully.\n"));
9339 #else /* not HAVE_LIBCURL */
9340 err = IE_NOTSUP;
9341 #endif
9342 return err;
9346 /* Retrieve hash of message identified by ID stored in ISDS.
9347 * @context is session context
9348 * @message_id is message identifier
9349 * @hash is automatically reallocated message hash downloaded from ISDS.
9350 * Message must exist in system and must not be deleted. */
9351 isds_error isds_download_message_hash(struct isds_ctx *context,
9352 const char *message_id, struct isds_hash **hash) {
9354 isds_error err = IE_SUCCESS;
9355 #if HAVE_LIBCURL
9356 xmlDocPtr response = NULL;
9357 xmlChar *code = NULL, *status_message = NULL;
9358 xmlXPathContextPtr xpath_ctx = NULL;
9359 xmlXPathObjectPtr result = NULL;
9360 #endif
9362 if (!context) return IE_INVALID_CONTEXT;
9363 zfree(context->long_message);
9365 isds_hash_free(hash);
9367 #if HAVE_LIBCURL
9368 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9369 BAD_CAST "VerifyMessage", message_id,
9370 &response, NULL, NULL, &code, &status_message);
9371 if (err) goto leave;
9374 /* Extract data */
9375 xpath_ctx = xmlXPathNewContext(response);
9376 if (!xpath_ctx) {
9377 err = IE_ERROR;
9378 goto leave;
9380 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9381 err = IE_ERROR;
9382 goto leave;
9384 result = xmlXPathEvalExpression(
9385 BAD_CAST "/isds:VerifyMessageResponse",
9386 xpath_ctx);
9387 if (!result) {
9388 err = IE_ERROR;
9389 goto leave;
9391 /* Empty response */
9392 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9393 char *message_id_locale = _isds_utf82locale((char*) message_id);
9394 isds_printf_message(context,
9395 _("Server did not return any response for ID `%s' "
9396 "on VerifyMessage request"), message_id_locale);
9397 free(message_id_locale);
9398 err = IE_ISDS;
9399 goto leave;
9401 /* More responses */
9402 if (result->nodesetval->nodeNr > 1) {
9403 char *message_id_locale = _isds_utf82locale((char*) message_id);
9404 isds_printf_message(context,
9405 _("Server did return more responses for ID `%s' "
9406 "on VerifyMessage request"), message_id_locale);
9407 free(message_id_locale);
9408 err = IE_ISDS;
9409 goto leave;
9411 /* One response */
9412 xpath_ctx->node = result->nodesetval->nodeTab[0];
9414 /* Extract the hash */
9415 err = find_and_extract_DmHash(context, hash, xpath_ctx);
9417 leave:
9418 if (err) {
9419 isds_hash_free(hash);
9422 xmlXPathFreeObject(result);
9423 xmlXPathFreeContext(xpath_ctx);
9425 free(code);
9426 free(status_message);
9427 xmlFreeDoc(response);
9429 if (!err)
9430 isds_log(ILF_ISDS, ILL_DEBUG,
9431 _("VerifyMessage request processed by server "
9432 "successfully.\n")
9434 #else /* not HAVE_LIBCURL */
9435 err = IE_NOTSUP;
9436 #endif
9437 return err;
9441 /* Mark message as read. This is a transactional commit function to acknowledge
9442 * to ISDS the message has been downloaded and processed by client properly.
9443 * @context is session context
9444 * @message_id is message identifier. */
9445 isds_error isds_mark_message_read(struct isds_ctx *context,
9446 const char *message_id) {
9448 isds_error err = IE_SUCCESS;
9449 #if HAVE_LIBCURL
9450 xmlDocPtr response = NULL;
9451 xmlChar *code = NULL, *status_message = NULL;
9452 #endif
9454 if (!context) return IE_INVALID_CONTEXT;
9455 zfree(context->long_message);
9457 #if HAVE_LIBCURL
9458 /* Do request and check for success */
9459 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9460 BAD_CAST "MarkMessageAsDownloaded", message_id,
9461 &response, NULL, NULL, &code, &status_message);
9463 free(code);
9464 free(status_message);
9465 xmlFreeDoc(response);
9467 if (!err)
9468 isds_log(ILF_ISDS, ILL_DEBUG,
9469 _("MarkMessageAsDownloaded request processed by server "
9470 "successfully.\n")
9472 #else /* not HAVE_LIBCURL */
9473 err = IE_NOTSUP;
9474 #endif
9475 return err;
9479 /* Mark message as received by recipient. This is applicable only to
9480 * commercial message. Use envelope->dmType message member to distinguish
9481 * commercial message from government message. Government message is
9482 * received automatically (by law), commercial message on recipient request.
9483 * @context is session context
9484 * @message_id is message identifier. */
9485 isds_error isds_mark_message_received(struct isds_ctx *context,
9486 const char *message_id) {
9488 isds_error err = IE_SUCCESS;
9489 #if HAVE_LIBCURL
9490 xmlDocPtr response = NULL;
9491 xmlChar *code = NULL, *status_message = NULL;
9492 #endif
9494 if (!context) return IE_INVALID_CONTEXT;
9495 zfree(context->long_message);
9497 #if HAVE_LIBCURL
9498 /* Do request and check for success */
9499 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9500 BAD_CAST "ConfirmDelivery", message_id,
9501 &response, NULL, NULL, &code, &status_message);
9503 free(code);
9504 free(status_message);
9505 xmlFreeDoc(response);
9507 if (!err)
9508 isds_log(ILF_ISDS, ILL_DEBUG,
9509 _("ConfirmDelivery request processed by server "
9510 "successfully.\n")
9512 #else /* not HAVE_LIBCURL */
9513 err = IE_NOTSUP;
9514 #endif
9515 return err;
9519 /* Send document for authorized conversion into Czech POINT system.
9520 * This is public anonymous service, no log-in necessary. Special context is
9521 * used to reuse keep-a-live HTTPS connection.
9522 * @context is Czech POINT session context. DO NOT use context connected to
9523 * ISDS server. Use new context or context used by this function previously.
9524 * @document is document to convert. Only data, data_length, dmFileDescr and
9525 * is_xml members are significant. Be ware that not all document formats can be
9526 * converted (signed PDF 1.3 and higher only (2010-02 state)).
9527 * @id is reallocated identifier assigned by Czech POINT system to
9528 * your document on submit. Use is to tell it to Czech POINT officer.
9529 * @date is reallocated document submit date (submitted documents
9530 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
9531 * value. */
9532 isds_error czp_convert_document(struct isds_ctx *context,
9533 const struct isds_document *document,
9534 char **id, struct tm **date) {
9535 isds_error err = IE_SUCCESS;
9536 #if HAVE_LIBCURL
9537 xmlNsPtr deposit_ns = NULL, empty_ns = NULL;
9538 xmlNodePtr request = NULL, node;
9539 xmlDocPtr response = NULL;
9541 xmlXPathContextPtr xpath_ctx = NULL;
9542 xmlXPathObjectPtr result = NULL;
9543 long int status = -1;
9544 long int *status_ptr = &status;
9545 char *string = NULL;
9546 #endif
9549 if (!context) return IE_INVALID_CONTEXT;
9550 zfree(context->long_message);
9551 if (!document || !id || !date) return IE_INVAL;
9553 if (document->is_xml) {
9554 isds_log_message(context,
9555 _("XML documents cannot be submitted to conversion"));
9556 return IE_NOTSUP;
9559 /* Free output arguments */
9560 zfree(*id);
9561 zfree(*date);
9563 #if HAVE_LIBCURL
9564 /* Store configuration */
9565 context->type = CTX_TYPE_CZP;
9566 free(context->url);
9567 context->url = strdup("https://www.czechpoint.cz/uschovna/services.php");
9568 if (!(context->url))
9569 return IE_NOMEM;
9571 /* Prepare CURL handle if not yet connected */
9572 if (!context->curl) {
9573 context->curl = curl_easy_init();
9574 if (!(context->curl))
9575 return IE_ERROR;
9578 /* Build conversion request */
9579 request = xmlNewNode(NULL, BAD_CAST "saveDocument");
9580 if (!request) {
9581 isds_log_message(context,
9582 _("Could not build Czech POINT conversion request"));
9583 return IE_ERROR;
9585 deposit_ns = xmlNewNs(request, BAD_CAST DEPOSIT_NS, BAD_CAST "dep");
9586 if(!deposit_ns) {
9587 isds_log_message(context,
9588 _("Could not create Czech POINT deposit name space"));
9589 xmlFreeNode(request);
9590 return IE_ERROR;
9592 xmlSetNs(request, deposit_ns);
9594 /* Insert children. They are in empty namespace! */
9595 empty_ns = xmlNewNs(request, BAD_CAST "", NULL);
9596 if(!empty_ns) {
9597 isds_log_message(context, _("Could not create empty name space"));
9598 err = IE_ERROR;
9599 goto leave;
9601 INSERT_STRING_WITH_NS(request, empty_ns, "conversionID", "0");
9602 INSERT_STRING_WITH_NS(request, empty_ns, "fileName",
9603 document->dmFileDescr);
9605 /* Document encoded in Base64 */
9606 err = insert_base64_encoded_string(context, request, empty_ns, "document",
9607 document->data, document->data_length);
9608 if (err) goto leave;
9610 isds_log(ILF_ISDS, ILL_DEBUG,
9611 _("Submitting document for conversion into Czech POINT deposit"));
9613 /* Send conversion request */
9614 err = _czp_czpdeposit(context, request, &response);
9615 xmlFreeNode(request); request = NULL;
9617 if (err) {
9618 czp_do_close_connection(context);
9619 goto leave;
9623 /* Extract response */
9624 xpath_ctx = xmlXPathNewContext(response);
9625 if (!xpath_ctx) {
9626 err = IE_ERROR;
9627 goto leave;
9629 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9630 err = IE_ERROR;
9631 goto leave;
9633 result = xmlXPathEvalExpression(
9634 BAD_CAST "/deposit:saveDocumentResponse/return",
9635 xpath_ctx);
9636 if (!result) {
9637 err = IE_ERROR;
9638 goto leave;
9640 /* Empty response */
9641 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9642 isds_printf_message(context,
9643 _("Missing `return' element in Czech POINT deposit response"));
9644 err = IE_ISDS;
9645 goto leave;
9647 /* More responses */
9648 if (result->nodesetval->nodeNr > 1) {
9649 isds_printf_message(context,
9650 _("Multiple `return' element in Czech POINT deposit response"));
9651 err = IE_ISDS;
9652 goto leave;
9654 /* One response */
9655 xpath_ctx->node = result->nodesetval->nodeTab[0];
9657 /* Get status */
9658 EXTRACT_LONGINT("status", status_ptr, 1);
9659 if (status) {
9660 EXTRACT_STRING("statusMsg", string);
9661 char *string_locale = _isds_utf82locale(string);
9662 isds_printf_message(context,
9663 _("Czech POINT deposit refused document for conversion "
9664 "(code=%ld, message=%s)"),
9665 status, string_locale);
9666 free(string_locale);
9667 err = IE_ISDS;
9668 goto leave;
9671 /* Get document ID */
9672 EXTRACT_STRING("documentID", *id);
9674 /* Get submit date */
9675 EXTRACT_STRING("dateInserted", string);
9676 if (string) {
9677 *date = calloc(1, sizeof(**date));
9678 if (!*date) {
9679 err = IE_NOMEM;
9680 goto leave;
9682 err = datestring2tm((xmlChar *)string, *date);
9683 if (err) {
9684 if (err == IE_NOTSUP) {
9685 err = IE_ISDS;
9686 char *string_locale = _isds_utf82locale(string);
9687 isds_printf_message(context,
9688 _("Invalid dateInserted value: %s"), string_locale);
9689 free(string_locale);
9691 goto leave;
9695 leave:
9696 free(string);
9697 xmlXPathFreeObject(result);
9698 xmlXPathFreeContext(xpath_ctx);
9700 xmlFreeDoc(response);
9701 xmlFreeNode(request);
9703 if (!err) {
9704 char *id_locale = _isds_utf82locale((char *) *id);
9705 isds_log(ILF_ISDS, ILL_DEBUG,
9706 _("Document %s has been submitted for conversion "
9707 "to server successfully\n"), id_locale);
9708 free(id_locale);
9710 #else /* not HAVE_LIBCURL */
9711 err = IE_NOTSUP;
9712 #endif
9713 return err;
9717 /* Close possibly opened connection to Czech POINT document deposit.
9718 * @context is Czech POINT session context. */
9719 isds_error czp_close_connection(struct isds_ctx *context) {
9720 if (!context) return IE_INVALID_CONTEXT;
9721 zfree(context->long_message);
9722 #if HAVE_LIBCURL
9723 return czp_do_close_connection(context);
9724 #else
9725 return IE_NOTSUP;
9726 #endif
9730 /* Send request for new box creation in testing ISDS instance.
9731 * It's not possible to request for a production box currently, as it
9732 * communicates via e-mail.
9733 * XXX: This function does not work either. Server complains about invalid
9734 * e-mail address.
9735 * XXX: Remove context->type hacks in isds.c and validator.c when removing
9736 * this function
9737 * @context is special session context for box creation request. DO NOT use
9738 * standard context as it could reveal your password. Use fresh new context or
9739 * context previously used by this function.
9740 * @box is box description to create including single primary user (in case of
9741 * FO box type). It outputs box ID assigned by ISDS in dbID element.
9742 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
9743 * box, or contact address of PFO box owner). The email member is mandatory as
9744 * it will be used to deliver credentials.
9745 * @former_names is former name of box owner. Pass NULL if you don't care.
9746 * @approval is optional external approval of box manipulation
9747 * @refnumber is reallocated serial number of request assigned by ISDS. Use
9748 * NULL, if you don't care.*/
9749 isds_error isds_request_new_testing_box(struct isds_ctx *context,
9750 struct isds_DbOwnerInfo *box, const struct isds_list *users,
9751 const char *former_names, const struct isds_approval *approval,
9752 char **refnumber) {
9753 isds_error err = IE_SUCCESS;
9754 #if HAVE_LIBCURL
9755 xmlNodePtr request = NULL;
9756 xmlDocPtr response = NULL;
9757 xmlXPathContextPtr xpath_ctx = NULL;
9758 xmlXPathObjectPtr result = NULL;
9759 #endif
9762 if (!context) return IE_INVALID_CONTEXT;
9763 zfree(context->long_message);
9764 if (!box) return IE_INVAL;
9766 #if HAVE_LIBCURL
9767 if (!box->email || box->email[0] == '\0') {
9768 isds_log_message(context, _("E-mail field is mandatory"));
9769 return IE_INVAL;
9772 /* Scratch box ID */
9773 zfree(box->dbID);
9775 /* Store configuration */
9776 context->type = CTX_TYPE_TESTING_REQUEST_COLLECTOR;
9777 free(context->url);
9778 context->url = strdup("http://78.102.19.203/testbox/request_box.php");
9779 if (!(context->url))
9780 return IE_NOMEM;
9782 /* Prepare CURL handle if not yet connected */
9783 if (!context->curl) {
9784 context->curl = curl_easy_init();
9785 if (!(context->curl))
9786 return IE_ERROR;
9789 /* Build CreateDataBox request */
9790 err = build_CreateDBInput_request(context,
9791 &request, BAD_CAST "CreateDataBox",
9792 box, users, (xmlChar *) former_names, NULL, NULL, NULL, approval);
9793 if (err) goto leave;
9795 /* Send it to server and process response */
9796 err = send_destroy_request_check_response(context,
9797 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
9798 &response, (xmlChar **) refnumber);
9799 if (err) goto leave;
9801 /* Extract box ID */
9802 xpath_ctx = xmlXPathNewContext(response);
9803 if (!xpath_ctx) {
9804 err = IE_ERROR;
9805 goto leave;
9807 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9808 err = IE_ERROR;
9809 goto leave;
9811 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
9813 leave:
9814 xmlXPathFreeObject(result);
9815 xmlXPathFreeContext(xpath_ctx);
9816 xmlFreeDoc(response);
9817 xmlFreeNode(request);
9819 if (!err) {
9820 isds_log(ILF_ISDS, ILL_DEBUG,
9821 _("CreateDataBox request processed by server successfully.\n"));
9823 #else /* not HAVE_LIBCURL */
9824 err = IE_NOTSUP;
9825 #endif
9827 return err;
9831 /* Submit CMS signed message to ISDS to verify its originality. This is
9832 * stronger form of isds_verify_message_hash() because ISDS does more checks
9833 * than simple one (potentialy old weak) hash comparison.
9834 * @context is session context
9835 * @message is memory with raw CMS signed message bit stream
9836 * @length is @message size in bytes
9837 * @return
9838 * IE_SUCCESS if message originates in ISDS
9839 * IE_NOTEQUAL if message is unknown to ISDS
9840 * other code for other errors */
9841 isds_error isds_authenticate_message(struct isds_ctx *context,
9842 const void *message, size_t length) {
9843 isds_error err = IE_SUCCESS;
9844 #if HAVE_LIBCURL
9845 xmlNsPtr isds_ns = NULL;
9846 xmlNodePtr request = NULL;
9847 xmlDocPtr response = NULL;
9848 xmlXPathContextPtr xpath_ctx = NULL;
9849 xmlXPathObjectPtr result = NULL;
9850 _Bool *authentic = NULL;
9851 #endif
9853 if (!context) return IE_INVALID_CONTEXT;
9854 zfree(context->long_message);
9855 if (!message || length == 0) return IE_INVAL;
9857 #if HAVE_LIBCURL
9858 /* Check if connection is established
9859 * TODO: This check should be done downstairs. */
9860 if (!context->curl) return IE_CONNECTION_CLOSED;
9863 /* Build AuthenticateMessage request */
9864 request = xmlNewNode(NULL, BAD_CAST "AuthenticateMessage");
9865 if (!request) {
9866 isds_log_message(context,
9867 _("Could not build AuthenticateMessage request"));
9868 return IE_ERROR;
9870 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9871 if(!isds_ns) {
9872 isds_log_message(context, _("Could not create ISDS name space"));
9873 xmlFreeNode(request);
9874 return IE_ERROR;
9876 xmlSetNs(request, isds_ns);
9878 /* Insert Base64 encoded message */
9879 err = insert_base64_encoded_string(context, request, NULL, "dmMessage",
9880 message, length);
9881 if (err) goto leave;
9883 /* Send request to server and process response */
9884 err = send_destroy_request_check_response(context,
9885 SERVICE_DM_OPERATIONS, BAD_CAST "AuthenticateMessage", &request,
9886 &response, NULL);
9887 if (err) goto leave;
9890 /* ISDS has decided */
9891 xpath_ctx = xmlXPathNewContext(response);
9892 if (!xpath_ctx) {
9893 err = IE_ERROR;
9894 goto leave;
9896 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9897 err = IE_ERROR;
9898 goto leave;
9901 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic);
9903 if (!authentic) {
9904 isds_log_message(context,
9905 _("Server did not return any response on "
9906 "AuthenticateMessage request"));
9907 err = IE_ISDS;
9908 goto leave;
9910 if (*authentic) {
9911 isds_log(ILF_ISDS, ILL_DEBUG,
9912 _("ISDS authenticated the message successfully\n"));
9913 } else {
9914 isds_log_message(context, _("ISDS does not know the message"));
9915 err = IE_NOTEQUAL;
9919 leave:
9920 free(authentic);
9921 xmlXPathFreeObject(result);
9922 xmlXPathFreeContext(xpath_ctx);
9924 xmlFreeDoc(response);
9925 xmlFreeNode(request);
9926 #else /* not HAVE_LIBCURL */
9927 err = IE_NOTSUP;
9928 #endif
9930 return err;
9933 #undef INSERT_ELEMENT
9934 #undef CHECK_FOR_STRING_LENGTH
9935 #undef INSERT_STRING_ATTRIBUTE
9936 #undef INSERT_ULONGINTNOPTR
9937 #undef INSERT_ULONGINT
9938 #undef INSERT_LONGINT
9939 #undef INSERT_BOOLEAN
9940 #undef INSERT_SCALAR_BOOLEAN
9941 #undef INSERT_STRING
9942 #undef INSERT_STRING_WITH_NS
9943 #undef EXTRACT_STRING_ATTRIBUTE
9944 #undef EXTRACT_ULONGINT
9945 #undef EXTRACT_LONGINT
9946 #undef EXTRACT_BOOLEAN
9947 #undef EXTRACT_STRING
9950 /* Compute hash of message from raw representation and store it into envelope.
9951 * Original hash structure will be destroyed in envelope.
9952 * @context is session context
9953 * @message is message carrying raw XML message blob
9954 * @algorithm is desired hash algorithm to use */
9955 isds_error isds_compute_message_hash(struct isds_ctx *context,
9956 struct isds_message *message, const isds_hash_algorithm algorithm) {
9957 isds_error err = IE_SUCCESS;
9958 const char *nsuri;
9959 void *xml_stream = NULL;
9960 size_t xml_stream_length;
9961 size_t phys_start, phys_end;
9962 char *phys_path = NULL;
9963 struct isds_hash *new_hash = NULL;
9966 if (!context) return IE_INVALID_CONTEXT;
9967 zfree(context->long_message);
9968 if (!message) return IE_INVAL;
9970 if (!message->raw) {
9971 isds_log_message(context,
9972 _("Message does not carry raw representation"));
9973 return IE_INVAL;
9976 switch (message->raw_type) {
9977 case RAWTYPE_INCOMING_MESSAGE:
9978 nsuri = ISDS_NS;
9979 xml_stream = message->raw;
9980 xml_stream_length = message->raw_length;
9981 break;
9983 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
9984 nsuri = SISDS_INCOMING_NS;
9985 xml_stream = message->raw;
9986 xml_stream_length = message->raw_length;
9987 break;
9989 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
9990 nsuri = SISDS_INCOMING_NS;
9991 err = _isds_extract_cms_data(context,
9992 message->raw, message->raw_length,
9993 &xml_stream, &xml_stream_length);
9994 if (err) goto leave;
9995 break;
9997 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
9998 nsuri = SISDS_OUTGOING_NS;
9999 xml_stream = message->raw;
10000 xml_stream_length = message->raw_length;
10001 break;
10003 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
10004 nsuri = SISDS_OUTGOING_NS;
10005 err = _isds_extract_cms_data(context,
10006 message->raw, message->raw_length,
10007 &xml_stream, &xml_stream_length);
10008 if (err) goto leave;
10009 break;
10011 default:
10012 isds_log_message(context, _("Bad raw representation type"));
10013 return IE_INVAL;
10014 break;
10018 /* XXX: Hash is computed from original string representing isds:dmDm
10019 * subtree. That means no encoding, white space, xmlns attributes changes.
10020 * In other words, input for hash can be invalid XML stream. */
10021 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
10022 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
10023 PHYSXML_ELEMENT_SEPARATOR,
10024 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
10025 PHYSXML_ELEMENT_SEPARATOR
10026 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
10027 err = IE_NOMEM;
10028 goto leave;
10030 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
10031 phys_path, &phys_start, &phys_end);
10032 zfree(phys_path);
10033 if (err) {
10034 isds_log_message(context,
10035 _("Substring with isds:dmDM element could not be located "
10036 "in raw message"));
10037 goto leave;
10041 /* Compute hash */
10042 new_hash = calloc(1, sizeof(*new_hash));
10043 if (!new_hash) {
10044 err = IE_NOMEM;
10045 goto leave;
10047 new_hash->algorithm = algorithm;
10048 err = _isds_compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
10049 new_hash);
10050 if (err) {
10051 isds_log_message(context, _("Could not compute message hash"));
10052 goto leave;
10055 /* Save computed hash */
10056 if (!message->envelope) {
10057 message->envelope = calloc(1, sizeof(*message->envelope));
10058 if (!message->envelope) {
10059 err = IE_NOMEM;
10060 goto leave;
10063 isds_hash_free(&message->envelope->hash);
10064 message->envelope->hash = new_hash;
10066 leave:
10067 if (err) {
10068 isds_hash_free(&new_hash);
10071 free(phys_path);
10072 if (xml_stream != message->raw) free(xml_stream);
10073 return err;
10077 /* Compare two hashes.
10078 * @h1 is first hash
10079 * @h2 is another hash
10080 * @return
10081 * IE_SUCCESS if hashes equal
10082 * IE_NOTUNIQ if hashes are comparable, but they don't equal
10083 * IE_ENUM if not comparable, but both structures defined
10084 * IE_INVAL if some of the structures are undefined (NULL)
10085 * IE_ERROR if internal error occurs */
10086 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
10087 if (h1 == NULL || h2 == NULL) return IE_INVAL;
10088 if (h1->algorithm != h2->algorithm) return IE_ENUM;
10089 if (h1->length != h2->length) return IE_ERROR;
10090 if (h1->length > 0 && !h1->value) return IE_ERROR;
10091 if (h2->length > 0 && !h2->value) return IE_ERROR;
10093 for (int i = 0; i < h1->length; i++) {
10094 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
10095 return IE_NOTEQUAL;
10097 return IE_SUCCESS;
10101 /* Check message has gone through ISDS by comparing message hash stored in
10102 * ISDS and locally computed hash. You must provide message with valid raw
10103 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
10104 * This is convenient wrapper for isds_download_message_hash(),
10105 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
10106 * @context is session context
10107 * @message is message with valid raw and envelope member; envelope->hash
10108 * member will be changed during function run. Use envelope on heap only.
10109 * @return
10110 * IE_SUCCESS if message originates in ISDS
10111 * IE_NOTEQUAL if message is unknown to ISDS
10112 * other code for other errors */
10113 isds_error isds_verify_message_hash(struct isds_ctx *context,
10114 struct isds_message *message) {
10115 isds_error err = IE_SUCCESS;
10116 struct isds_hash *downloaded_hash = NULL;
10118 if (!context) return IE_INVALID_CONTEXT;
10119 zfree(context->long_message);
10120 if (!message) return IE_INVAL;
10122 if (!message->envelope) {
10123 isds_log_message(context,
10124 _("Given message structure is missing envelope"));
10125 return IE_INVAL;
10127 if (!message->raw) {
10128 isds_log_message(context,
10129 _("Given message structure is missing raw representation"));
10130 return IE_INVAL;
10133 err = isds_download_message_hash(context, message->envelope->dmID,
10134 &downloaded_hash);
10135 if (err) goto leave;
10137 err = isds_compute_message_hash(context, message,
10138 downloaded_hash->algorithm);
10139 if (err) goto leave;
10141 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
10143 leave:
10144 isds_hash_free(&downloaded_hash);
10145 return err;
10149 /* Search for document by document ID in list of documents. IDs are compared
10150 * as UTF-8 string.
10151 * @documents is list of isds_documents
10152 * @id is document identifier
10153 * @return first matching document or NULL. */
10154 const struct isds_document *isds_find_document_by_id(
10155 const struct isds_list *documents, const char *id) {
10156 const struct isds_list *item;
10157 const struct isds_document *document;
10159 for (item = documents; item; item = item->next) {
10160 document = (struct isds_document *) item->data;
10161 if (!document) continue;
10163 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
10164 return document;
10167 return NULL;
10171 /* Normalize @mime_type to be proper MIME type.
10172 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
10173 * guess regular MIME type (e.g. "application/pdf").
10174 * @mime_type is UTF-8 encoded MIME type to fix
10175 * @return original @mime_type if no better interpretation exists, or array to
10176 * constant static UTF-8 encoded string with proper MIME type. */
10177 char *isds_normalize_mime_type(const char* mime_type) {
10178 if (!mime_type) return NULL;
10180 for (int offset = 0;
10181 offset < sizeof(extension_map_mime)/sizeof(extension_map_mime[0]);
10182 offset += 2) {
10183 if (!xmlStrcmp((const xmlChar*) mime_type, extension_map_mime[offset]))
10184 return (char *) extension_map_mime[offset + 1];
10187 return (char *) mime_type;
10191 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
10192 struct isds_message **message);
10193 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
10194 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
10195 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
10196 struct isds_address **address);
10198 int isds_message_free(struct isds_message **message);
10199 int isds_address_free(struct isds_address **address);
10203 /* Makes known all relevant namespaces to given XPath context
10204 * @xpath_ctx is XPath context
10205 * @message_ns selects proper message name space. Unsigned and signed
10206 * messages and delivery info's differ in prefix and URI. */
10207 _hidden isds_error _isds_register_namespaces(xmlXPathContextPtr xpath_ctx,
10208 const message_ns_type message_ns) {
10209 const xmlChar *message_namespace = NULL;
10211 if (!xpath_ctx) return IE_ERROR;
10213 switch(message_ns) {
10214 case MESSAGE_NS_1:
10215 message_namespace = BAD_CAST ISDS1_NS; break;
10216 case MESSAGE_NS_UNSIGNED:
10217 message_namespace = BAD_CAST ISDS_NS; break;
10218 case MESSAGE_NS_SIGNED_INCOMING:
10219 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
10220 case MESSAGE_NS_SIGNED_OUTGOING:
10221 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
10222 case MESSAGE_NS_SIGNED_DELIVERY:
10223 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
10224 default:
10225 return IE_ENUM;
10228 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
10229 return IE_ERROR;
10230 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
10231 return IE_ERROR;
10232 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
10233 return IE_ERROR;
10234 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
10235 return IE_ERROR;
10236 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
10237 return IE_ERROR;
10238 return IE_SUCCESS;