test: ChangePasswordOTP implemented on server side
[libisds.git] / src / isds.c
blob7d8cf0bd1c6d7d98c206ecd21713b199147facaa
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 /* Deallocate struct isds_commercial_permission recursively and NULL it */
374 void isds_commercial_permission_free(
375 struct isds_commercial_permission **permission) {
376 if (NULL == permission || NULL == *permission) return;
378 free((*permission)->recipient);
379 free((*permission)->payer);
380 free((*permission)->expiration);
381 free((*permission)->count);
382 free((*permission)->reply_identifier);
384 zfree(*permission);
388 /* *DUP_OR_ERROR macros needs error label */
389 #define STRDUP_OR_ERROR(new, template) { \
390 if (!template) { \
391 (new) = NULL; \
392 } else { \
393 (new) = strdup(template); \
394 if (!new) goto error; \
398 #define FLATDUP_OR_ERROR(new, template) { \
399 if (!template) { \
400 (new) = NULL; \
401 } else { \
402 (new) = malloc(sizeof(*(new))); \
403 if (!new) goto error; \
404 memcpy((new), (template), sizeof(*(template))); \
408 /* Copy structure isds_pki_credentials recursively. */
409 struct isds_pki_credentials *isds_pki_credentials_duplicate(
410 const struct isds_pki_credentials *template) {
411 struct isds_pki_credentials *new = NULL;
413 if(!template) return NULL;
415 new = calloc(1, sizeof(*new));
416 if (!new) return NULL;
418 STRDUP_OR_ERROR(new->engine, template->engine);
419 new->certificate_format = template->certificate_format;
420 STRDUP_OR_ERROR(new->certificate, template->certificate);
421 new->key_format = template->key_format;
422 STRDUP_OR_ERROR(new->key, template->key);
423 STRDUP_OR_ERROR(new->passphrase, template->passphrase);
425 return new;
427 error:
428 isds_pki_credentials_free(&new);
429 return NULL;
433 /* Copy structure isds_PersonName recursively */
434 struct isds_PersonName *isds_PersonName_duplicate(
435 const struct isds_PersonName *template) {
436 struct isds_PersonName *new = NULL;
438 if (!template) return NULL;
440 new = calloc(1, sizeof(*new));
441 if (!new) return NULL;
443 STRDUP_OR_ERROR(new->pnFirstName, template->pnFirstName);
444 STRDUP_OR_ERROR(new->pnMiddleName, template->pnMiddleName);
445 STRDUP_OR_ERROR(new->pnLastName, template->pnLastName);
446 STRDUP_OR_ERROR(new->pnLastNameAtBirth, template->pnLastNameAtBirth);
448 return new;
450 error:
451 isds_PersonName_free(&new);
452 return NULL;
456 /* Copy structure isds_BirthInfo recursively */
457 static struct isds_BirthInfo *isds_BirthInfo_duplicate(
458 const struct isds_BirthInfo *template) {
459 struct isds_BirthInfo *new = NULL;
461 if (!template) return NULL;
463 new = calloc(1, sizeof(*new));
464 if (!new) return NULL;
466 FLATDUP_OR_ERROR(new->biDate, template->biDate);
467 STRDUP_OR_ERROR(new->biCity, template->biCity);
468 STRDUP_OR_ERROR(new->biCounty, template->biCounty);
469 STRDUP_OR_ERROR(new->biState, template->biState);
471 return new;
473 error:
474 isds_BirthInfo_free(&new);
475 return NULL;
479 /* Copy structure isds_Address recursively */
480 struct isds_Address *isds_Address_duplicate(
481 const struct isds_Address *template) {
482 struct isds_Address *new = NULL;
484 if (!template) return NULL;
486 new = calloc(1, sizeof(*new));
487 if (!new) return NULL;
489 STRDUP_OR_ERROR(new->adCity, template->adCity);
490 STRDUP_OR_ERROR(new->adStreet, template->adStreet);
491 STRDUP_OR_ERROR(new->adNumberInStreet, template->adNumberInStreet);
492 STRDUP_OR_ERROR(new->adNumberInMunicipality,
493 template->adNumberInMunicipality);
494 STRDUP_OR_ERROR(new->adZipCode, template->adZipCode);
495 STRDUP_OR_ERROR(new->adState, template->adState);
497 return new;
499 error:
500 isds_Address_free(&new);
501 return NULL;
505 /* Copy structure isds_DbOwnerInfo recursively */
506 struct isds_DbOwnerInfo *isds_DbOwnerInfo_duplicate(
507 const struct isds_DbOwnerInfo *template) {
508 struct isds_DbOwnerInfo *new = NULL;
509 if (!template) return NULL;
511 new = calloc(1, sizeof(*new));
512 if (!new) return NULL;
514 STRDUP_OR_ERROR(new->dbID, template->dbID);
515 FLATDUP_OR_ERROR(new->dbType, template->dbType);
516 STRDUP_OR_ERROR(new->ic, template->ic);
518 if (template->personName) {
519 if (!(new->personName =
520 isds_PersonName_duplicate(template->personName)))
521 goto error;
524 STRDUP_OR_ERROR(new->firmName, template->firmName);
526 if (template->birthInfo) {
527 if (!(new->birthInfo =
528 isds_BirthInfo_duplicate(template->birthInfo)))
529 goto error;
532 if (template->address) {
533 if (!(new->address = isds_Address_duplicate(template->address)))
534 goto error;
537 STRDUP_OR_ERROR(new->nationality, template->nationality);
538 STRDUP_OR_ERROR(new->email, template->email);
539 STRDUP_OR_ERROR(new->telNumber, template->telNumber);
540 STRDUP_OR_ERROR(new->identifier, template->identifier);
541 STRDUP_OR_ERROR(new->registryCode, template->registryCode);
542 FLATDUP_OR_ERROR(new->dbState, template->dbState);
543 FLATDUP_OR_ERROR(new->dbEffectiveOVM, template->dbEffectiveOVM);
544 FLATDUP_OR_ERROR(new->dbOpenAddressing, template->dbOpenAddressing);
546 return new;
548 error:
549 isds_DbOwnerInfo_free(&new);
550 return NULL;
554 /* Copy structure isds_DbUserInfo recursively */
555 struct isds_DbUserInfo *isds_DbUserInfo_duplicate(
556 const struct isds_DbUserInfo *template) {
557 struct isds_DbUserInfo *new = NULL;
558 if (!template) return NULL;
560 new = calloc(1, sizeof(*new));
561 if (!new) return NULL;
563 STRDUP_OR_ERROR(new->userID, template->userID);
564 FLATDUP_OR_ERROR(new->userType, template->userType);
565 FLATDUP_OR_ERROR(new->userPrivils, template->userPrivils);
567 if (template->personName) {
568 if (!(new->personName =
569 isds_PersonName_duplicate(template->personName)))
570 goto error;
573 if (template->address) {
574 if (!(new->address = isds_Address_duplicate(template->address)))
575 goto error;
578 FLATDUP_OR_ERROR(new->biDate, template->biDate);
579 STRDUP_OR_ERROR(new->ic, template->ic);
580 STRDUP_OR_ERROR(new->firmName, template->firmName);
581 STRDUP_OR_ERROR(new->caStreet, template->caStreet);
582 STRDUP_OR_ERROR(new->caCity, template->caCity);
583 STRDUP_OR_ERROR(new->caZipCode, template->caZipCode);
584 STRDUP_OR_ERROR(new->caState, template->caState);
586 return new;
588 error:
589 isds_DbUserInfo_free(&new);
590 return NULL;
593 #undef FLATDUP_OR_ERROR
594 #undef STRDUP_OR_ERROR
597 /* Logs libxml2 errors. Should be registered to libxml2 library.
598 * @ctx is unused currently
599 * @msg is printf-like formated message from libxml2 (UTF-8?)
600 * @... are variadic arguments for @msg */
601 static void log_xml(void *ctx, const char *msg, ...) {
602 va_list ap;
603 char *text = NULL;
605 if (!msg) return;
607 va_start(ap, msg);
608 isds_vasprintf(&text, msg, ap);
609 va_end(ap);
611 if (text)
612 isds_log(ILF_XML, ILL_ERR, "%s", text);
613 free(text);
617 /* Initialize ISDS library.
618 * Global function, must be called before other functions.
619 * If it fails you can not use ISDS library and must call isds_cleanup() to
620 * free partially initialized global variables. */
621 isds_error isds_init(void) {
622 /* NULL global variables */
623 log_facilities = ILF_ALL;
624 log_level = ILL_WARNING;
625 log_callback = NULL;
626 log_callback_data = NULL;
628 #if ENABLE_NLS
629 /* Initialize gettext */
630 bindtextdomain(PACKAGE, LOCALEDIR);
631 #endif
633 #if HAVE_LIBCURL
634 /* Initialize CURL */
635 if (curl_global_init(CURL_GLOBAL_ALL)) {
636 isds_log(ILF_ISDS, ILL_CRIT, _("CURL library initialization failed\n"));
637 return IE_ERROR;
639 #endif /* HAVE_LIBCURL */
641 /* Initialize gpg-error because of gpgme and ksba */
642 if (gpg_err_init()) {
643 isds_log(ILF_ISDS, ILL_CRIT,
644 _("gpg-error library initialization failed\n"));
645 return IE_ERROR;
648 /* Initialize GPGME */
649 if (_isds_init_gpgme(&version_gpgme)) {
650 isds_log(ILF_ISDS, ILL_CRIT,
651 _("GPGME library initialization failed\n"));
652 return IE_ERROR;
655 /* Initialize gcrypt */
656 if (_isds_init_gcrypt(&version_gcrypt)) {
657 isds_log(ILF_ISDS, ILL_CRIT,
658 _("gcrypt library initialization failed\n"));
659 return IE_ERROR;
662 /* This can _exit() current program. Find not so assertive check. */
663 LIBXML_TEST_VERSION;
664 xmlSetGenericErrorFunc(NULL, log_xml);
666 /* Check expat */
667 if (_isds_init_expat(&version_expat)) {
668 isds_log(ILF_ISDS, ILL_CRIT,
669 _("expat library initialization failed\n"));
670 return IE_ERROR;
673 /* Allocate global variables */
676 return IE_SUCCESS;
680 /* Deinitialize ISDS library.
681 * Global function, must be called as last library function. */
682 isds_error isds_cleanup(void) {
683 /* XML */
684 xmlCleanupParser();
686 #if HAVE_LIBCURL
687 /* Curl */
688 curl_global_cleanup();
689 #endif
691 return IE_SUCCESS;
695 /* Return version string of this library. Version of dependencies can be
696 * embedded. Do no try to parse it. You must free it. */
697 char *isds_version(void) {
698 char *buffer = NULL;
700 isds_asprintf(&buffer,
701 #if HAVE_LIBCURL
702 _("%s (%s, GPGME %s, gcrypt %s, %s, libxml2 %s)"),
703 #else
704 _("%s (GPGME %s, gcrypt %s, %s, libxml2 %s)"),
705 #endif
706 PACKAGE_VERSION,
707 #if HAVE_LIBCURL
708 curl_version(),
709 #endif
710 version_gpgme, version_gcrypt,
711 version_expat, xmlParserVersion);
712 return buffer;
716 /* Return text description of ISDS error */
717 const char *isds_strerror(const isds_error error) {
718 switch (error) {
719 case IE_SUCCESS:
720 return(_("Success")); break;
721 case IE_ERROR:
722 return(_("Unspecified error")); break;
723 case IE_NOTSUP:
724 return(_("Not supported")); break;
725 case IE_INVAL:
726 return(_("Invalid value")); break;
727 case IE_INVALID_CONTEXT:
728 return(_("Invalid context")); break;
729 case IE_NOT_LOGGED_IN:
730 return(_("Not logged in")); break;
731 case IE_CONNECTION_CLOSED:
732 return(_("Connection closed")); break;
733 case IE_TIMED_OUT:
734 return(_("Timed out")); break;
735 case IE_NOEXIST:
736 return(_("Not exist")); break;
737 case IE_NOMEM:
738 return(_("Out of memory")); break;
739 case IE_NETWORK:
740 return(_("Network problem")); break;
741 case IE_HTTP:
742 return(_("HTTP problem")); break;
743 case IE_SOAP:
744 return(_("SOAP problem")); break;
745 case IE_XML:
746 return(_("XML problem")); break;
747 case IE_ISDS:
748 return(_("ISDS server problem")); break;
749 case IE_ENUM:
750 return(_("Invalid enum value")); break;
751 case IE_DATE:
752 return(_("Invalid date value")); break;
753 case IE_2BIG:
754 return(_("Too big")); break;
755 case IE_2SMALL:
756 return(_("Too small")); break;
757 case IE_NOTUNIQ:
758 return(_("Value not unique")); break;
759 case IE_NOTEQUAL:
760 return(_("Values not equal")); break;
761 case IE_PARTIAL_SUCCESS:
762 return(_("Some suboperations failed")); break;
763 case IE_ABORTED:
764 return(_("Operation aborted")); break;
765 default:
766 return(_("Unknown error"));
771 /* Create ISDS context.
772 * Each context can be used for different sessions to (possibly) different
773 * ISDS server with different credentials. */
774 struct isds_ctx *isds_ctx_create(void) {
775 struct isds_ctx *context;
776 context = malloc(sizeof(*context));
777 if (context) memset(context, 0, sizeof(*context));
778 return context;
781 #if HAVE_LIBCURL
782 /* Close possibly opened connection to Czech POINT document deposit without
783 * resetting long_message buffer.
784 * XXX: Do not use czp_close_connection() if you do not want to destroy log
785 * message.
786 * @context is Czech POINT session context. */
787 static isds_error czp_do_close_connection(struct isds_ctx *context) {
788 if (!context) return IE_INVALID_CONTEXT;
789 _isds_close_connection(context);
790 return IE_SUCCESS;
794 /* Discard credentials.
795 * @context is ISDS context
796 * @discard_saved_username is true for removing saved username, false for
797 * keeping it.
798 * Only that. It does not cause log out, connection close or similar. */
799 _hidden isds_error _isds_discard_credentials(struct isds_ctx *context,
800 _Bool discard_saved_username) {
801 if(!context) return IE_INVALID_CONTEXT;
803 if (context->username) {
804 memset(context->username, 0, strlen(context->username));
805 zfree(context->username);
807 if (context->password) {
808 memset(context->password, 0, strlen(context->password));
809 zfree(context->password);
811 isds_pki_credentials_free(&context->pki_credentials);
812 if (discard_saved_username && context->saved_username) {
813 memset(context->saved_username, 0, strlen(context->saved_username));
814 zfree(context->saved_username);
817 return IE_SUCCESS;
819 #endif /* HAVE_LIBCURL */
822 /* Destroy ISDS context and free memory.
823 * @context will be NULLed on success. */
824 isds_error isds_ctx_free(struct isds_ctx **context) {
825 if (!context || !*context) {
826 return IE_INVALID_CONTEXT;
829 #if HAVE_LIBCURL
830 /* Discard credentials and close connection */
831 switch ((*context)->type) {
832 case CTX_TYPE_NONE: break;
833 case CTX_TYPE_ISDS: isds_logout(*context); break;
834 case CTX_TYPE_CZP:
835 case CTX_TYPE_TESTING_REQUEST_COLLECTOR:
836 czp_do_close_connection(*context); break;
839 /* For sure */
840 _isds_discard_credentials(*context, 1);
842 /* Free other structures */
843 free((*context)->tls_verify_server);
844 free((*context)->tls_ca_file);
845 free((*context)->tls_ca_dir);
846 free((*context)->tls_crl_file);
847 #endif /* HAVE_LIBCURL */
848 free((*context)->long_message);
850 free(*context);
851 *context = NULL;
852 return IE_SUCCESS;
856 /* Return long message text produced by library function, e.g. detailed error
857 * message. Returned pointer is only valid until new library function is
858 * called for the same context. Could be NULL, especially if NULL context is
859 * supplied. Return string is locale encoded. */
860 char *isds_long_message(const struct isds_ctx *context) {
861 if (!context) return NULL;
862 return context->long_message;
866 /* Stores message into context' long_message buffer.
867 * Application can pick the message up using isds_long_message().
868 * NULL @message truncates the buffer but does not deallocate it.
869 * @message is coded in locale encoding */
870 _hidden isds_error isds_log_message(struct isds_ctx *context,
871 const char *message) {
872 char *buffer;
873 size_t length;
875 if (!context) return IE_INVALID_CONTEXT;
877 /* FIXME: Check for integer overflow */
878 length = 1 + ((message) ? strlen(message) : 0);
879 buffer = realloc(context->long_message, length);
880 if (!buffer) return IE_NOMEM;
882 if (message)
883 strcpy(buffer, message);
884 else
885 *buffer = '\0';
887 context->long_message = buffer;
888 return IE_SUCCESS;
892 /* Appends message into context' long_message buffer.
893 * Application can pick the message up using isds_long_message().
894 * NULL message has void effect. */
895 _hidden isds_error isds_append_message(struct isds_ctx *context,
896 const char *message) {
897 char *buffer;
898 size_t old_length, length;
900 if (!context) return IE_INVALID_CONTEXT;
901 if (!message) return IE_SUCCESS;
902 if (!context->long_message)
903 return isds_log_message(context, message);
905 old_length = strlen(context->long_message);
906 /* FIXME: Check for integer overflow */
907 length = 1 + old_length + strlen(message);
908 buffer = realloc(context->long_message, length);
909 if (!buffer) return IE_NOMEM;
911 strcpy(buffer + old_length, message);
913 context->long_message = buffer;
914 return IE_SUCCESS;
918 /* Stores formatted message into context' long_message buffer.
919 * Application can pick the message up using isds_long_message(). */
920 _hidden isds_error isds_printf_message(struct isds_ctx *context,
921 const char *format, ...) {
922 va_list ap;
923 int length;
925 if (!context) return IE_INVALID_CONTEXT;
926 va_start(ap, format);
927 length = isds_vasprintf(&(context->long_message), format, ap);
928 va_end(ap);
930 return (length < 0) ? IE_ERROR: IE_SUCCESS;
934 /* Set logging up.
935 * @facilities is bit mask of isds_log_facility values,
936 * @level is verbosity level. */
937 void isds_set_logging(const unsigned int facilities,
938 const isds_log_level level) {
939 log_facilities = facilities;
940 log_level = level;
944 /* Register callback function libisds calls when new global log message is
945 * produced by library. Library logs to stderr by default.
946 * @callback is function provided by application libisds will call. See type
947 * definition for @callback argument explanation. Pass NULL to revert logging to
948 * default behaviour.
949 * @data is application specific data @callback gets as last argument */
950 void isds_set_log_callback(isds_log_callback callback, void *data) {
951 log_callback = callback;
952 log_callback_data = data;
956 /* Log @message in class @facility with log @level into global log. @message
957 * is printf(3) formatting string, variadic arguments may be necessary.
958 * For debugging purposes. */
959 _hidden isds_error isds_log(const isds_log_facility facility,
960 const isds_log_level level, const char *message, ...) {
961 va_list ap;
962 char *buffer = NULL;
963 int length;
965 if (level > log_level) return IE_SUCCESS;
966 if (!(log_facilities & facility)) return IE_SUCCESS;
967 if (!message) return IE_INVAL;
969 if (log_callback) {
970 /* Pass message to application supplied callback function */
971 va_start(ap, message);
972 length = isds_vasprintf(&buffer, message, ap);
973 va_end(ap);
975 if (length == -1) {
976 return IE_ERROR;
978 if (length > 0) {
979 log_callback(facility, level, buffer, length, log_callback_data);
981 free(buffer);
982 } else {
983 /* Default: Log it to stderr */
984 va_start(ap, message);
985 vfprintf(stderr, message, ap);
986 va_end(ap);
987 /* Line buffered printf is default.
988 * fflush(stderr);*/
991 return IE_SUCCESS;
995 /* Set timeout in milliseconds for each network job like connecting to server
996 * or sending message. Use 0 to disable timeout limits. */
997 isds_error isds_set_timeout(struct isds_ctx *context,
998 const unsigned int timeout) {
999 if (!context) return IE_INVALID_CONTEXT;
1000 zfree(context->long_message);
1002 #if HAVE_LIBCURL
1003 context->timeout = timeout;
1005 if (context->curl) {
1006 CURLcode curl_err;
1008 curl_err = curl_easy_setopt(context->curl, CURLOPT_NOSIGNAL, 1);
1009 if (!curl_err)
1010 #if HAVE_DECL_CURLOPT_TIMEOUT_MS /* Since curl-7.16.2 */
1011 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT_MS,
1012 context->timeout);
1013 #else
1014 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT,
1015 context->timeout / 1000);
1016 #endif /* not HAVE_DECL_CURLOPT_TIMEOUT_MS */
1017 if (curl_err) return IE_ERROR;
1020 return IE_SUCCESS;
1021 #else /* not HAVE_LIBCURL */
1022 return IE_NOTSUP;
1023 #endif
1027 /* Register callback function libisds calls periodically during HTTP data
1028 * transfer.
1029 * @context is session context
1030 * @callback is function provided by application libisds will call. See type
1031 * definition for @callback argument explanation.
1032 * @data is application specific data @callback gets as last argument */
1033 isds_error isds_set_progress_callback(struct isds_ctx *context,
1034 isds_progress_callback callback, void *data) {
1035 if (!context) return IE_INVALID_CONTEXT;
1036 zfree(context->long_message);
1038 #if HAVE_LIBCURL
1039 context->progress_callback = callback;
1040 context->progress_callback_data = data;
1042 return IE_SUCCESS;
1043 #else /* not HAVE_LIBCURL */
1044 return IE_NOTSUP;
1045 #endif
1049 /* Change context settings.
1050 * @context is context which setting will be applied to
1051 * @option is name of option. It determines the type of last argument. See
1052 * isds_option definition for more info.
1053 * @... is value of new setting. Type is determined by @option
1054 * */
1055 isds_error isds_set_opt(struct isds_ctx *context, const isds_option option,
1056 ...) {
1057 isds_error err = IE_SUCCESS;
1058 va_list ap;
1059 #if HAVE_LIBCURL
1060 char *pointer, *string;
1061 #endif
1063 if (!context) return IE_INVALID_CONTEXT;
1064 zfree(context->long_message);
1066 va_start(ap, option);
1068 #define REPLACE_VA_BOOLEAN(destination) { \
1069 if (!(destination)) { \
1070 (destination) = malloc(sizeof(*(destination))); \
1071 if (!(destination)) { \
1072 err = IE_NOMEM; goto leave; \
1075 *(destination) = (_Bool) !!va_arg(ap, int); \
1078 #define REPLACE_VA_STRING(destination) { \
1079 string = va_arg(ap, char *); \
1080 if (string) { \
1081 pointer = realloc((destination), 1 + strlen(string)); \
1082 if (!pointer) { err = IE_NOMEM; goto leave; } \
1083 strcpy(pointer, string); \
1084 (destination) = pointer; \
1085 } else { \
1086 free(destination); \
1087 (destination) = NULL; \
1091 switch (option) {
1092 case IOPT_TLS_VERIFY_SERVER:
1093 #if HAVE_LIBCURL
1094 REPLACE_VA_BOOLEAN(context->tls_verify_server);
1095 #else
1096 err = IE_NOTSUP; goto leave;
1097 #endif
1098 break;
1099 case IOPT_TLS_CA_FILE:
1100 #if HAVE_LIBCURL
1101 REPLACE_VA_STRING(context->tls_ca_file);
1102 #else
1103 err = IE_NOTSUP; goto leave;
1104 #endif
1105 break;
1106 case IOPT_TLS_CA_DIRECTORY:
1107 #if HAVE_LIBCURL
1108 REPLACE_VA_STRING(context->tls_ca_dir);
1109 #else
1110 err = IE_NOTSUP; goto leave;
1111 #endif
1112 break;
1113 case IOPT_TLS_CRL_FILE:
1114 #if HAVE_LIBCURL
1115 #if HAVE_DECL_CURLOPT_CRLFILE /* Since curl-7.19.0 */
1116 REPLACE_VA_STRING(context->tls_crl_file);
1117 #else
1118 isds_log_message(context,
1119 _("Curl library does not support CRL definition"));
1120 err = IE_NOTSUP;
1121 #endif /* not HAVE_DECL_CURLOPT_CRLFILE */
1122 #else
1123 err = IE_NOTSUP; goto leave;
1124 #endif /* not HAVE_LIBCURL */
1125 break;
1126 case IOPT_NORMALIZE_MIME_TYPE:
1127 context->normalize_mime_type = (_Bool) !!va_arg(ap, int);
1128 break;
1130 default:
1131 err = IE_ENUM; goto leave;
1134 #undef REPLACE_VA_STRING
1135 #undef REPLACE_VA_BOOLEAN
1137 leave:
1138 va_end(ap);
1139 return err;
1143 #if HAVE_LIBCURL
1144 /* Copy credentials into context. Any non-NULL argument will be duplicated.
1145 * Destination for NULL argument will not be touched.
1146 * Destination pointers must be freed before calling this function.
1147 * If @username is @context->saved_username, the saved_username will not be
1148 * replaced. The saved_username is clobbered only if context has set otp
1149 * member.
1150 * Return IE_SUCCESS on success. */
1151 static isds_error _isds_store_credentials(struct isds_ctx *context,
1152 const char *username, const char *password,
1153 const struct isds_pki_credentials *pki_credentials) {
1154 if (NULL == context) return IE_INVALID_CONTEXT;
1156 /* FIXME: mlock password
1157 * (I have a library) */
1159 if (username) {
1160 context->username = strdup(username);
1161 if (NULL != context->otp && context->saved_username != username)
1162 context->saved_username = strdup(username);
1164 if (password) {
1165 if (NULL == context->otp)
1166 context->password = strdup(password);
1167 else
1168 context->password = _isds_astrcat(password, context->otp->otp_code);
1170 context->pki_credentials = isds_pki_credentials_duplicate(pki_credentials);
1172 if ((NULL != username && NULL == context->username) ||
1173 (NULL != password && NULL == context->password) ||
1174 (NULL != pki_credentials && NULL == context->pki_credentials) ||
1175 (NULL != context->otp && NULL != context->username &&
1176 NULL == context->saved_username)) {
1177 return IE_NOMEM;
1180 return IE_SUCCESS;
1182 #endif
1185 /* Connect and log into ISDS server.
1186 * All required arguments will be copied, you do not have to keep them after
1187 * that.
1188 * ISDS supports six different authentication methods. Exact method is
1189 * selected on @username, @password, @pki_credentials, and @otp arguments:
1190 * - If @pki_credentials == NULL, @username and @password must be supplied
1191 * and then
1192 * - If @otp == NULL, simple authentication by username and password will
1193 * be proceeded.
1194 * - If @otp != NULL, authentication by username and password and OTP
1195 * will be used.
1196 * - If @pki_credentials != NULL, then
1197 * - If @username == NULL, only certificate will be used
1198 * - If @username != NULL, then
1199 * - If @password == NULL, then certificate will be used and
1200 * @username shifts meaning to box ID. This is used for hosted
1201 * services.
1202 * - Otherwise all three arguments will be used.
1203 * Please note, that different cases require different certificate type
1204 * (system qualified one or commercial non qualified one). This library
1205 * does not check such political issues. Please see ISDS Specification
1206 * for more details.
1207 * @url is base address of ISDS web service. Pass extern isds_locator
1208 * variable to use production ISDS instance without client certificate
1209 * authentication (or extern isds_cert_locator with client certificate
1210 * authentication or extern isds_otp_locators with OTP authentication).
1211 * Passing NULL has the same effect, autoselection between isds_locator,
1212 * isds_cert_locator, and isds_otp_locator is performed in addition. You can
1213 * pass extern isds_testing_locator (or isds_cert_testing_locator or
1214 * isds_otp_testing_locator) variable to select testing instance.
1215 * @username is user name of ISDS user or box ID
1216 * @password is user's secret password
1217 * @pki_credentials defines public key cryptographic material to use in client
1218 * authentication.
1219 * @otp selects one-time password authentication method to use, defines OTP
1220 * code (if known) and returns fine grade resolution of OTP procedure.
1221 * @return:
1222 * IE_SUCCESS if authentication succeeds
1223 * IE_NOT_LOGGED_IN if authentication fails. If OTP authentication has been
1224 * requested, fine grade reason will be set into @otp->resolution. Error
1225 * message from server can be obtained by isds_long_message() call.
1226 * IE_PARTIAL_SUCCESS if time-based OTP authentication has been requested and
1227 * server has sent OTP code through side channel. Application is expected to
1228 * fill the code into @otp->otp_code, keep other arguments unchanged, and retry
1229 * this call to complete second phase of TOTP authentication;
1230 * or other appropriate error. */
1231 isds_error isds_login(struct isds_ctx *context, const char *url,
1232 const char *username, const char *password,
1233 const struct isds_pki_credentials *pki_credentials,
1234 struct isds_otp *otp) {
1235 #if HAVE_LIBCURL
1236 isds_error err = IE_NOT_LOGGED_IN;
1237 isds_error soap_err;
1238 xmlNsPtr isds_ns = NULL;
1239 xmlNodePtr request = NULL;
1240 xmlNodePtr response = NULL;
1241 #endif /* HAVE_LIBCURL */
1243 if (!context) return IE_INVALID_CONTEXT;
1244 zfree(context->long_message);
1246 #if HAVE_LIBCURL
1247 /* Close connection if already logged in */
1248 if (context->curl) {
1249 _isds_close_connection(context);
1252 /* Store configuration */
1253 context->type = CTX_TYPE_ISDS;
1254 zfree(context->url);
1256 /* Mangle base URI according to requested authentication method */
1257 if (NULL == pki_credentials) {
1258 isds_log(ILF_SEC, ILL_INFO,
1259 _("Selected authentication method: no certificate, "
1260 "username and password\n"));
1261 if (!username || !password) {
1262 isds_log_message(context,
1263 _("Both username and password must be supplied"));
1264 return IE_INVAL;
1266 context->otp = otp;
1268 if (NULL == context->otp) {
1269 /* Default locator is official system (without certificate or
1270 * OTP) */
1271 context->url = strdup((NULL != url) ? url : isds_locator);
1272 } else {
1273 const char *authenticator_uri = NULL;
1274 if (!url) url = isds_otp_locator;
1275 otp->resolution = OTP_RESOLUTION_UNKNOWN;
1276 switch (context->otp->method) {
1277 case OTP_HMAC:
1278 isds_log(ILF_SEC, ILL_INFO,
1279 _("Selected authentication method: "
1280 "HMAC-based one-time password\n"));
1281 authenticator_uri =
1282 "%1$sas/processLogin?type=hotp&uri=%1$sapps/";
1283 break;
1284 case OTP_TIME:
1285 isds_log(ILF_SEC, ILL_INFO,
1286 _("Selected authentication method: "
1287 "Time-based one-time password\n"));
1288 if (context->otp->otp_code == NULL) {
1289 isds_log(ILF_SEC, ILL_INFO,
1290 _("OTP code has not been provided by "
1291 "application, requesting server for "
1292 "new one.\n"));
1293 authenticator_uri =
1294 "%1$sas/processLogin?type=totp&sendSms=true&"
1295 "uri=%1$sapps/";
1296 } else {
1297 isds_log(ILF_SEC, ILL_INFO,
1298 _("OTP code has been provided by "
1299 "application, not requesting server "
1300 "for new one.\n"));
1301 authenticator_uri =
1302 "%1$sas/processLogin?type=totp&"
1303 "uri=%1$sapps/";
1305 break;
1306 default:
1307 isds_log_message(context,
1308 _("Unknown one-time password authentication "
1309 "method requested by application"));
1310 return IE_ENUM;
1312 if (-1 == isds_asprintf(&context->url, authenticator_uri, url))
1313 return IE_NOMEM;
1315 } else {
1316 /* Default locator is official system (with client certificate) */
1317 context->otp = NULL;
1318 if (!url) url = isds_cert_locator;
1320 if (!username) {
1321 isds_log(ILF_SEC, ILL_INFO,
1322 _("Selected authentication method: system certificate, "
1323 "no username and no password\n"));
1324 password = NULL;
1325 context->url = _isds_astrcat(url, "cert/");
1326 } else {
1327 if (!password) {
1328 isds_log(ILF_SEC, ILL_INFO,
1329 _("Selected authentication method: system certificate, "
1330 "box ID and no password\n"));
1331 context->url = _isds_astrcat(url, "hspis/");
1332 } else {
1333 isds_log(ILF_SEC, ILL_INFO,
1334 _("Selected authentication method: commercial "
1335 "certificate, username and password\n"));
1336 context->url = _isds_astrcat(url, "certds/");
1340 if (!(context->url))
1341 return IE_NOMEM;
1343 /* Prepare CURL handle */
1344 context->curl = curl_easy_init();
1345 if (!(context->curl))
1346 return IE_ERROR;
1348 /* Build log-in request */
1349 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1350 if (!request) {
1351 isds_log_message(context, _("Could not build ISDS log-in request"));
1352 return IE_ERROR;
1354 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1355 if(!isds_ns) {
1356 isds_log_message(context, _("Could not create ISDS name space"));
1357 xmlFreeNode(request);
1358 return IE_ERROR;
1360 xmlSetNs(request, isds_ns);
1362 /* Store credentials */
1363 _isds_discard_credentials(context, 1);
1364 if (_isds_store_credentials(context, username, password, pki_credentials)) {
1365 _isds_discard_credentials(context, 1);
1366 xmlFreeNode(request);
1367 return IE_NOMEM;
1370 isds_log(ILF_ISDS, ILL_DEBUG, _("Logging user %s into server %s\n"),
1371 username, url);
1373 /* Send log-in request */
1374 soap_err = _isds_soap(context, "DS/dz", request, &response, NULL, NULL);
1376 if (context->otp) {
1377 /* Revert context URL from OTP authentication service URL to OTP web
1378 * service base URL for subsequent calls. Potenial isds_login() retry
1379 * will re-set context URL again. */
1380 zfree(context->url);
1381 context->url = _isds_astrcat(url, "apps/");
1382 if (context->url == NULL) {
1383 soap_err = IE_NOMEM;
1387 /* Remove credentials */
1388 _isds_discard_credentials(context, 0);
1390 /* Destroy log-in request */
1391 xmlFreeNode(request);
1393 if (soap_err) {
1394 xmlFreeNodeList(response);
1395 _isds_close_connection(context);
1396 return soap_err;
1399 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1400 * authentication succeeded if soap_err == IE_SUCCESS */
1401 err = IE_SUCCESS;
1403 xmlFreeNodeList(response);
1405 if (!err)
1406 isds_log(ILF_ISDS, ILL_DEBUG,
1407 _("User %s has been logged into server %s successfully\n"),
1408 username, url);
1409 return err;
1410 #else /* not HAVE_LIBCURL */
1411 return IE_NOTSUP;
1412 #endif
1416 /* Log out from ISDS server discards credentials and connection configuration. */
1417 isds_error isds_logout(struct isds_ctx *context) {
1418 if (!context) return IE_INVALID_CONTEXT;
1419 zfree(context->long_message);
1421 #if HAVE_LIBCURL
1422 if (context->curl) {
1423 if (context->otp != NULL) {
1424 isds_error err = _isds_invalidate_otp_cookie(context);
1425 if (err) return err;
1428 /* Close connection */
1429 _isds_close_connection(context);
1431 /* Discard credentials for sure. They should not survive isds_login(),
1432 * even successful .*/
1433 _isds_discard_credentials(context, 1);
1434 zfree(context->url);
1436 isds_log(ILF_ISDS, ILL_DEBUG, _("Logged out from ISDS server\n"));
1437 } else {
1438 _isds_discard_credentials(context, 1);
1440 return IE_SUCCESS;
1441 #else /* not HAVE_LIBCURL */
1442 return IE_NOTSUP;
1443 #endif
1447 /* Verify connection to ISDS is alive and server is responding.
1448 * Sent dummy request to ISDS and expect dummy response. */
1449 isds_error isds_ping(struct isds_ctx *context) {
1450 #if HAVE_LIBCURL
1451 isds_error soap_err;
1452 xmlNsPtr isds_ns = NULL;
1453 xmlNodePtr request = NULL;
1454 xmlNodePtr response = NULL;
1455 #endif /* HAVE_LIBCURL */
1457 if (!context) return IE_INVALID_CONTEXT;
1458 zfree(context->long_message);
1460 #if HAVE_LIBCURL
1461 /* Check if connection is established */
1462 if (!context->curl) return IE_CONNECTION_CLOSED;
1465 /* Build dummy request */
1466 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1467 if (!request) {
1468 isds_log_message(context, _("Could build ISDS dummy request"));
1469 return IE_ERROR;
1471 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1472 if(!isds_ns) {
1473 isds_log_message(context, _("Could not create ISDS name space"));
1474 xmlFreeNode(request);
1475 return IE_ERROR;
1477 xmlSetNs(request, isds_ns);
1479 isds_log(ILF_ISDS, ILL_DEBUG, _("Pinging ISDS server\n"));
1481 /* Sent dummy request */
1482 soap_err = _isds_soap(context, "DS/dz", request, &response, NULL, NULL);
1484 /* Destroy log-in request */
1485 xmlFreeNode(request);
1487 if (soap_err) {
1488 isds_log(ILF_ISDS, ILL_DEBUG,
1489 _("ISDS server could not be contacted\n"));
1490 xmlFreeNodeList(response);
1491 return soap_err;
1494 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1495 * authentication succeeded if soap_err == IE_SUCCESS */
1496 /* TODO: ISDS documentation does not specify response body.
1497 * However real server sends back DummyOperationResponse */
1500 xmlFreeNodeList(response);
1502 isds_log(ILF_ISDS, ILL_DEBUG, _("ISDS server alive\n"));
1504 return IE_SUCCESS;
1505 #else /* not HAVE_LIBCURL */
1506 return IE_NOTSUP;
1507 #endif
1511 /* Send bogus request to ISDS.
1512 * Just for test purposes */
1513 isds_error isds_bogus_request(struct isds_ctx *context) {
1514 #if HAVE_LIBCURL
1515 isds_error err;
1516 xmlNsPtr isds_ns = NULL;
1517 xmlNodePtr request = NULL;
1518 xmlDocPtr response = NULL;
1519 xmlChar *code = NULL, *message = NULL;
1520 #endif
1522 if (!context) return IE_INVALID_CONTEXT;
1523 zfree(context->long_message);
1525 #if HAVE_LIBCURL
1526 /* Check if connection is established */
1527 if (!context->curl) {
1528 /* Testing printf message */
1529 isds_printf_message(context, "%s", _("I said connection closed"));
1530 return IE_CONNECTION_CLOSED;
1534 /* Build dummy request */
1535 request = xmlNewNode(NULL, BAD_CAST "X-BogusOperation");
1536 if (!request) {
1537 isds_log_message(context, _("Could build ISDS bogus request"));
1538 return IE_ERROR;
1540 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1541 if(!isds_ns) {
1542 isds_log_message(context, _("Could not create ISDS name space"));
1543 xmlFreeNode(request);
1544 return IE_ERROR;
1546 xmlSetNs(request, isds_ns);
1548 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending bogus request to ISDS\n"));
1550 /* Sent bogus request */
1551 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
1553 /* Destroy request */
1554 xmlFreeNode(request);
1556 if (err) {
1557 isds_log(ILF_ISDS, ILL_DEBUG,
1558 _("Processing ISDS response on bogus request failed\n"));
1559 xmlFreeDoc(response);
1560 return err;
1563 /* Check for response status */
1564 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
1565 &code, &message, NULL);
1566 if (err) {
1567 isds_log(ILF_ISDS, ILL_DEBUG,
1568 _("ISDS response on bogus request is missing status\n"));
1569 free(code);
1570 free(message);
1571 xmlFreeDoc(response);
1572 return err;
1574 if (xmlStrcmp(code, BAD_CAST "0000")) {
1575 char *code_locale = _isds_utf82locale((char*)code);
1576 char *message_locale = _isds_utf82locale((char*)message);
1577 isds_log(ILF_ISDS, ILL_DEBUG,
1578 _("Server refused bogus request (code=%s, message=%s)\n"),
1579 code_locale, message_locale);
1580 /* XXX: Literal error messages from ISDS are Czech messages
1581 * (English sometimes) in UTF-8. It's hard to catch them for
1582 * translation. Successfully gettextized would return in locale
1583 * encoding, unsuccessfully translated would pass in UTF-8. */
1584 isds_log_message(context, message_locale);
1585 free(code_locale);
1586 free(message_locale);
1587 free(code);
1588 free(message);
1589 xmlFreeDoc(response);
1590 return IE_ISDS;
1594 free(code);
1595 free(message);
1596 xmlFreeDoc(response);
1598 isds_log(ILF_ISDS, ILL_DEBUG,
1599 _("Bogus message accepted by server. This should not happen.\n"));
1601 return IE_SUCCESS;
1602 #else /* not HAVE_LIBCURL */
1603 return IE_NOTSUP;
1604 #endif
1608 #if HAVE_LIBCURL
1609 /* Serialize XML subtree to buffer preserving XML indentation.
1610 * @context is session context
1611 * @subtree is XML element to be serialized (with children)
1612 * @buffer is automatically reallocated buffer where serialize to
1613 * @length is size of serialized stream in bytes
1614 * @return standard error code, free @buffer in case of error */
1615 static isds_error serialize_subtree(struct isds_ctx *context,
1616 xmlNodePtr subtree, void **buffer, size_t *length) {
1617 isds_error err = IE_SUCCESS;
1618 xmlBufferPtr xml_buffer = NULL;
1619 xmlSaveCtxtPtr save_ctx = NULL;
1620 xmlDocPtr subtree_doc = NULL;
1621 xmlNodePtr subtree_copy;
1622 xmlNsPtr isds_ns;
1623 void *new_buffer;
1625 if (!context) return IE_INVALID_CONTEXT;
1626 if (!buffer) return IE_INVAL;
1627 zfree(*buffer);
1628 if (!subtree || !length) return IE_INVAL;
1630 /* Make temporary XML document with @subtree root element */
1631 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1632 * It can result in not well-formed on invalid XML tree (e.g. name space
1633 * prefix definition can miss. */
1634 /*FIXME */
1636 subtree_doc = xmlNewDoc(BAD_CAST "1.0");
1637 if (!subtree_doc) {
1638 isds_log_message(context, _("Could not build temporary document"));
1639 err = IE_ERROR;
1640 goto leave;
1643 /* XXX: Copy subtree and attach the copy to document.
1644 * One node can not bee attached into more document at the same time.
1645 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1646 * automatically.
1647 * XXX: Check xmlSaveTree() too. */
1648 subtree_copy = xmlCopyNodeList(subtree);
1649 if (!subtree_copy) {
1650 isds_log_message(context, _("Could not copy subtree"));
1651 err = IE_ERROR;
1652 goto leave;
1654 xmlDocSetRootElement(subtree_doc, subtree_copy);
1656 /* Only this way we get namespace definition as @xmlns:isds,
1657 * otherwise we get namespace prefix without definition */
1658 /* FIXME: Don't overwrite original default namespace */
1659 isds_ns = xmlNewNs(subtree_copy, BAD_CAST ISDS_NS, NULL);
1660 if(!isds_ns) {
1661 isds_log_message(context, _("Could not create ISDS name space"));
1662 err = IE_ERROR;
1663 goto leave;
1665 xmlSetNs(subtree_copy, isds_ns);
1668 /* Serialize the document into buffer */
1669 xml_buffer = xmlBufferCreate();
1670 if (!xml_buffer) {
1671 isds_log_message(context, _("Could not create xmlBuffer"));
1672 err = IE_ERROR;
1673 goto leave;
1675 /* Last argument 0 means to not format the XML tree */
1676 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8", 0);
1677 if (!save_ctx) {
1678 isds_log_message(context, _("Could not create XML serializer"));
1679 err = IE_ERROR;
1680 goto leave;
1682 /* XXX: According LibXML documentation, this function does not return
1683 * meaningful value yet */
1684 xmlSaveDoc(save_ctx, subtree_doc);
1685 if (-1 == xmlSaveFlush(save_ctx)) {
1686 isds_log_message(context,
1687 _("Could not serialize XML subtree"));
1688 err = IE_ERROR;
1689 goto leave;
1691 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1692 * even after xmlSaveFlush(). Thus close it here */
1693 xmlSaveClose(save_ctx); save_ctx = NULL;
1696 /* Store and detach buffer from xml_buffer */
1697 *buffer = xml_buffer->content;
1698 *length = xml_buffer->use;
1699 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1701 /* Shrink buffer */
1702 new_buffer = realloc(*buffer, *length);
1703 if (new_buffer) *buffer = new_buffer;
1705 leave:
1706 if (err) {
1707 zfree(*buffer);
1708 *length = 0;
1711 xmlSaveClose(save_ctx);
1712 xmlBufferFree(xml_buffer);
1713 xmlFreeDoc(subtree_doc); /* Frees subtree_copy, isds_ns etc. */
1714 return err;
1716 #endif /* HAVE_LIBCURL */
1719 #if 0
1720 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1721 * @context is session context
1722 * @document is original document where @nodeset points to
1723 * @nodeset is XPath node set to dump (recursively)
1724 * @buffer is automatically reallocated buffer where serialize to
1725 * @length is size of serialized stream in bytes
1726 * @return standard error code, free @buffer in case of error */
1727 static isds_error dump_nodeset(struct isds_ctx *context,
1728 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1729 void **buffer, size_t *length) {
1730 isds_error err = IE_SUCCESS;
1731 xmlBufferPtr xml_buffer = NULL;
1732 void *new_buffer;
1734 if (!context) return IE_INVALID_CONTEXT;
1735 if (!buffer) return IE_INVAL;
1736 zfree(*buffer);
1737 if (!document || !nodeset || !length) return IE_INVAL;
1738 *length = 0;
1740 /* Empty node set results into NULL buffer */
1741 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1742 goto leave;
1745 /* Resulting the document into buffer */
1746 xml_buffer = xmlBufferCreate();
1747 if (!xml_buffer) {
1748 isds_log_message(context, _("Could not create xmlBuffer"));
1749 err = IE_ERROR;
1750 goto leave;
1753 /* Iterate over all nodes */
1754 for (int i = 0; i < nodeset->nodeNr; i++) {
1755 /* Serialize node.
1756 * XXX: xmlNodeDump() appends to xml_buffer. */
1757 if (-1 ==
1758 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1759 isds_log_message(context, _("Could not dump XML node"));
1760 err = IE_ERROR;
1761 goto leave;
1765 /* Store and detach buffer from xml_buffer */
1766 *buffer = xml_buffer->content;
1767 *length = xml_buffer->use;
1768 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1770 /* Shrink buffer */
1771 new_buffer = realloc(*buffer, *length);
1772 if (new_buffer) *buffer = new_buffer;
1775 leave:
1776 if (err) {
1777 zfree(*buffer);
1778 *length = 0;
1781 xmlBufferFree(xml_buffer);
1782 return err;
1784 #endif
1786 #if 0
1787 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1788 * @context is session context
1789 * @document is original document where @nodeset points to
1790 * @nodeset is XPath node set to dump (recursively)
1791 * @buffer is automatically reallocated buffer where serialize to
1792 * @length is size of serialized stream in bytes
1793 * @return standard error code, free @buffer in case of error */
1794 static isds_error dump_nodeset(struct isds_ctx *context,
1795 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1796 void **buffer, size_t *length) {
1797 isds_error err = IE_SUCCESS;
1798 xmlBufferPtr xml_buffer = NULL;
1799 xmlSaveCtxtPtr save_ctx = NULL;
1800 void *new_buffer;
1802 if (!context) return IE_INVALID_CONTEXT;
1803 if (!buffer) return IE_INVAL;
1804 zfree(*buffer);
1805 if (!document || !nodeset || !length) return IE_INVAL;
1806 *length = 0;
1808 /* Empty node set results into NULL buffer */
1809 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1810 goto leave;
1813 /* Resulting the document into buffer */
1814 xml_buffer = xmlBufferCreate();
1815 if (!xml_buffer) {
1816 isds_log_message(context, _("Could not create xmlBuffer"));
1817 err = IE_ERROR;
1818 goto leave;
1820 if (xmlSubstituteEntitiesDefault(1)) {
1821 isds_log_message(context, _("Could not disable attribute escaping"));
1822 err = IE_ERROR;
1823 goto leave;
1825 /* Last argument means:
1826 * 0 to not format the XML tree
1827 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1828 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8",
1829 XML_SAVE_NO_DECL|XML_SAVE_NO_EMPTY|XML_SAVE_NO_XHTML);
1830 if (!save_ctx) {
1831 isds_log_message(context, _("Could not create XML serializer"));
1832 err = IE_ERROR;
1833 goto leave;
1835 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1836 isds_log_message(context, _("Could not disable attribute escaping"));
1837 err = IE_ERROR;
1838 goto leave;
1842 /* Iterate over all nodes */
1843 for (int i = 0; i < nodeset->nodeNr; i++) {
1844 /* Serialize node.
1845 * XXX: xmlNodeDump() appends to xml_buffer. */
1846 /*if (-1 ==
1847 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1849 /* XXX: According LibXML documentation, this function does not return
1850 * meaningful value yet */
1851 xmlSaveTree(save_ctx, nodeset->nodeTab[i]);
1852 if (-1 == xmlSaveFlush(save_ctx)) {
1853 isds_log_message(context,
1854 _("Could not serialize XML subtree"));
1855 err = IE_ERROR;
1856 goto leave;
1860 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1861 * even after xmlSaveFlush(). Thus close it here */
1862 xmlSaveClose(save_ctx); save_ctx = NULL;
1864 /* Store and detach buffer from xml_buffer */
1865 *buffer = xml_buffer->content;
1866 *length = xml_buffer->use;
1867 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1869 /* Shrink buffer */
1870 new_buffer = realloc(*buffer, *length);
1871 if (new_buffer) *buffer = new_buffer;
1873 leave:
1874 if (err) {
1875 zfree(*buffer);
1876 *length = 0;
1879 xmlSaveClose(save_ctx);
1880 xmlBufferFree(xml_buffer);
1881 return err;
1883 #endif
1886 #if HAVE_LIBCURL
1887 /* Convert UTF-8 @string representation of ISDS dbType to enum @type */
1888 static isds_error string2isds_DbType(xmlChar *string, isds_DbType *type) {
1889 if (!string || !type) return IE_INVAL;
1891 if (!xmlStrcmp(string, BAD_CAST "FO"))
1892 *type = DBTYPE_FO;
1893 else if (!xmlStrcmp(string, BAD_CAST "PFO"))
1894 *type = DBTYPE_PFO;
1895 else if (!xmlStrcmp(string, BAD_CAST "PFO_ADVOK"))
1896 *type = DBTYPE_PFO_ADVOK;
1897 else if (!xmlStrcmp(string, BAD_CAST "PFO_DANPOR"))
1898 *type = DBTYPE_PFO_DANPOR;
1899 else if (!xmlStrcmp(string, BAD_CAST "PFO_INSSPR"))
1900 *type = DBTYPE_PFO_INSSPR;
1901 else if (!xmlStrcmp(string, BAD_CAST "PO"))
1902 *type = DBTYPE_PO;
1903 else if (!xmlStrcmp(string, BAD_CAST "PO_ZAK"))
1904 *type = DBTYPE_PO_ZAK;
1905 else if (!xmlStrcmp(string, BAD_CAST "PO_REQ"))
1906 *type = DBTYPE_PO_REQ;
1907 else if (!xmlStrcmp(string, BAD_CAST "OVM"))
1908 *type = DBTYPE_OVM;
1909 else if (!xmlStrcmp(string, BAD_CAST "OVM_NOTAR"))
1910 *type = DBTYPE_OVM_NOTAR;
1911 else if (!xmlStrcmp(string, BAD_CAST "OVM_EXEKUT"))
1912 *type = DBTYPE_OVM_EXEKUT;
1913 else if (!xmlStrcmp(string, BAD_CAST "OVM_REQ"))
1914 *type = DBTYPE_OVM_REQ;
1915 else
1916 return IE_ENUM;
1917 return IE_SUCCESS;
1921 /* Convert ISDS dbType enum @type to UTF-8 string.
1922 * @Return pointer to static string, or NULL if unknown enum value */
1923 static const xmlChar *isds_DbType2string(const isds_DbType type) {
1924 switch(type) {
1925 /* DBTYPE_SYSTEM is invalid value from point of view of public
1926 * SOAP interface. */
1927 case DBTYPE_FO: return(BAD_CAST "FO"); break;
1928 case DBTYPE_PFO: return(BAD_CAST "PFO"); break;
1929 case DBTYPE_PFO_ADVOK: return(BAD_CAST "PFO_ADVOK"); break;
1930 case DBTYPE_PFO_DANPOR: return(BAD_CAST "PFO_DANPOR"); break;
1931 case DBTYPE_PFO_INSSPR: return(BAD_CAST "PFO_INSSPR"); break;
1932 case DBTYPE_PO: return(BAD_CAST "PO"); break;
1933 case DBTYPE_PO_ZAK: return(BAD_CAST "PO_ZAK"); break;
1934 case DBTYPE_PO_REQ: return(BAD_CAST "PO_REQ"); break;
1935 case DBTYPE_OVM: return(BAD_CAST "OVM"); break;
1936 case DBTYPE_OVM_NOTAR: return(BAD_CAST "OVM_NOTAR"); break;
1937 case DBTYPE_OVM_EXEKUT: return(BAD_CAST "OVM_EXEKUT"); break;
1938 case DBTYPE_OVM_REQ: return(BAD_CAST "OVM_REQ"); break;
1939 default: return NULL; break;
1944 /* Convert UTF-8 @string representation of ISDS userType to enum @type */
1945 static isds_error string2isds_UserType(xmlChar *string, isds_UserType *type) {
1946 if (!string || !type) return IE_INVAL;
1948 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
1949 *type = USERTYPE_PRIMARY;
1950 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
1951 *type = USERTYPE_ENTRUSTED;
1952 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
1953 *type = USERTYPE_ADMINISTRATOR;
1954 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
1955 *type = USERTYPE_OFFICIAL;
1956 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
1957 *type = USERTYPE_OFFICIAL_CERT;
1958 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
1959 *type = USERTYPE_LIQUIDATOR;
1960 else
1961 return IE_ENUM;
1962 return IE_SUCCESS;
1966 /* Convert ISDS userType enum @type to UTF-8 string.
1967 * @Return pointer to static string, or NULL if unknown enum value */
1968 static const xmlChar *isds_UserType2string(const isds_UserType type) {
1969 switch(type) {
1970 case USERTYPE_PRIMARY: return(BAD_CAST "PRIMARY_USER"); break;
1971 case USERTYPE_ENTRUSTED: return(BAD_CAST "ENTRUSTED_USER"); break;
1972 case USERTYPE_ADMINISTRATOR: return(BAD_CAST "ADMINISTRATOR"); break;
1973 case USERTYPE_OFFICIAL: return(BAD_CAST "OFFICIAL"); break;
1974 case USERTYPE_OFFICIAL_CERT: return(BAD_CAST "OFFICIAL_CERT"); break;
1975 case USERTYPE_LIQUIDATOR: return(BAD_CAST "LIQUIDATOR"); break;
1976 default: return NULL; break;
1981 /* Convert UTF-8 @string representation of ISDS sender type to enum @type */
1982 static isds_error string2isds_sender_type(const xmlChar *string,
1983 isds_sender_type *type) {
1984 if (!string || !type) return IE_INVAL;
1986 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
1987 *type = SENDERTYPE_PRIMARY;
1988 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
1989 *type = SENDERTYPE_ENTRUSTED;
1990 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
1991 *type = SENDERTYPE_ADMINISTRATOR;
1992 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
1993 *type = SENDERTYPE_OFFICIAL;
1994 else if (!xmlStrcmp(string, BAD_CAST "VIRTUAL"))
1995 *type = SENDERTYPE_VIRTUAL;
1996 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
1997 *type = SENDERTYPE_OFFICIAL_CERT;
1998 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
1999 *type = SENDERTYPE_LIQUIDATOR;
2000 else
2001 return IE_ENUM;
2002 return IE_SUCCESS;
2006 /* Convert UTF-8 @string representation of ISDS PDZType to enum @type */
2007 static isds_error string2isds_payment_type(const xmlChar *string,
2008 isds_payment_type *type) {
2009 if (!string || !type) return IE_INVAL;
2011 if (!xmlStrcmp(string, BAD_CAST "K"))
2012 *type = PAYMENT_SENDER;
2013 else if (!xmlStrcmp(string, BAD_CAST "O"))
2014 *type = PAYMENT_RESPONSE;
2015 else if (!xmlStrcmp(string, BAD_CAST "G"))
2016 *type = PAYMENT_SPONSOR;
2017 else if (!xmlStrcmp(string, BAD_CAST "Z"))
2018 *type = PAYMENT_SPONSOR_LIMITED;
2019 else if (!xmlStrcmp(string, BAD_CAST "D"))
2020 *type = PAYMENT_SPONSOR_EXTERNAL;
2021 else if (!xmlStrcmp(string, BAD_CAST "E"))
2022 *type = PAYMENT_STAMP;
2023 else
2024 return IE_ENUM;
2025 return IE_SUCCESS;
2029 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
2030 * @Return pointer to static string, or NULL if unknown enum value */
2031 static const xmlChar *isds_FileMetaType2string(const isds_FileMetaType type) {
2032 switch(type) {
2033 case FILEMETATYPE_MAIN: return(BAD_CAST "main"); break;
2034 case FILEMETATYPE_ENCLOSURE: return(BAD_CAST "enclosure"); break;
2035 case FILEMETATYPE_SIGNATURE: return(BAD_CAST "signature"); break;
2036 case FILEMETATYPE_META: return(BAD_CAST "meta"); break;
2037 default: return NULL; break;
2040 #endif /* HAVE_LIBCURL */
2043 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
2044 * @Return IE_ENUM if @string is not valid enum member */
2045 static isds_error string2isds_FileMetaType(const xmlChar *string,
2046 isds_FileMetaType *type) {
2047 if (!string || !type) return IE_INVAL;
2049 if (!xmlStrcmp(string, BAD_CAST "main"))
2050 *type = FILEMETATYPE_MAIN;
2051 else if (!xmlStrcmp(string, BAD_CAST "enclosure"))
2052 *type = FILEMETATYPE_ENCLOSURE;
2053 else if (!xmlStrcmp(string, BAD_CAST "signature"))
2054 *type = FILEMETATYPE_SIGNATURE;
2055 else if (!xmlStrcmp(string, BAD_CAST "meta"))
2056 *type = FILEMETATYPE_META;
2057 else
2058 return IE_ENUM;
2059 return IE_SUCCESS;
2063 /* Convert UTF-8 @string to ISDS hash @algorithm.
2064 * @Return IE_ENUM if @string is not valid enum member */
2065 static isds_error string2isds_hash_algorithm(const xmlChar *string,
2066 isds_hash_algorithm *algorithm) {
2067 if (!string || !algorithm) return IE_INVAL;
2069 if (!xmlStrcmp(string, BAD_CAST "MD5"))
2070 *algorithm = HASH_ALGORITHM_MD5;
2071 else if (!xmlStrcmp(string, BAD_CAST "SHA-1"))
2072 *algorithm = HASH_ALGORITHM_SHA_1;
2073 else if (!xmlStrcmp(string, BAD_CAST "SHA-224"))
2074 *algorithm = HASH_ALGORITHM_SHA_224;
2075 else if (!xmlStrcmp(string, BAD_CAST "SHA-256"))
2076 *algorithm = HASH_ALGORITHM_SHA_256;
2077 else if (!xmlStrcmp(string, BAD_CAST "SHA-384"))
2078 *algorithm = HASH_ALGORITHM_SHA_384;
2079 else if (!xmlStrcmp(string, BAD_CAST "SHA-512"))
2080 *algorithm = HASH_ALGORITHM_SHA_512;
2081 else
2082 return IE_ENUM;
2083 return IE_SUCCESS;
2087 #if HAVE_LIBCURL
2088 /* Convert UTF-8 @string representation of ISO 8601 date to @time.
2089 * XXX: Not all ISO formats are supported */
2090 static isds_error datestring2tm(const xmlChar *string, struct tm *time) {
2091 char *offset;
2092 if (!string || !time) return IE_INVAL;
2094 /* xsd:date is ISO 8601 string, thus ASCII */
2095 offset = strptime((char*)string, "%Y-%m-%d", time);
2096 if (offset && *offset == '\0')
2097 return IE_SUCCESS;
2099 offset = strptime((char*)string, "%Y%m%d", time);
2100 if (offset && *offset == '\0')
2101 return IE_SUCCESS;
2103 offset = strptime((char*)string, "%Y-%j", time);
2104 if (offset && *offset == '\0')
2105 return IE_SUCCESS;
2107 return IE_NOTSUP;
2111 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
2112 static isds_error tm2datestring(const struct tm *time, xmlChar **string) {
2113 if (!time || !string) return IE_INVAL;
2115 if (-1 == isds_asprintf((char **) string, "%d-%02d-%02d",
2116 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
2117 return IE_ERROR;
2119 return IE_SUCCESS;
2123 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
2124 * respects the @time microseconds too. */
2125 static isds_error timeval2timestring(const struct timeval *time,
2126 xmlChar **string) {
2127 struct tm broken;
2129 if (!time || !string) return IE_INVAL;
2131 if (!gmtime_r(&time->tv_sec, &broken)) return IE_DATE;
2132 if (time->tv_usec < 0 || time->tv_usec > 999999) return IE_DATE;
2134 /* TODO: small negative year should be formatted as "-0012". This is not
2135 * true for glibc "%04d". We should implement it.
2136 * TODO: What's type of time->tv_usec exactly? Unsigned? Absolute?
2137 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
2138 if (-1 == isds_asprintf((char **) string,
2139 "%04d-%02d-%02dT%02d:%02d:%02d.%06ld",
2140 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
2141 broken.tm_hour, broken.tm_min, broken.tm_sec,
2142 time->tv_usec))
2143 return IE_ERROR;
2145 return IE_SUCCESS;
2147 #endif /* HAVE_LIBCURL */
2150 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
2151 * It respects microseconds too.
2152 * In case of error, @time will be freed. */
2153 static isds_error timestring2timeval(const xmlChar *string,
2154 struct timeval **time) {
2155 struct tm broken;
2156 char *offset, *delim, *endptr;
2157 char subseconds[7];
2158 int offset_hours, offset_minutes;
2159 int i;
2161 if (!time) return IE_INVAL;
2162 if (!string) {
2163 zfree(*time);
2164 return IE_INVAL;
2167 memset(&broken, 0, sizeof(broken));
2169 if (!*time) {
2170 *time = calloc(1, sizeof(**time));
2171 if (!*time) return IE_NOMEM;
2172 } else {
2173 memset(*time, 0, sizeof(**time));
2177 /* xsd:date is ISO 8601 string, thus ASCII */
2178 /*TODO: negative year */
2180 /* Parse date and time without subseconds and offset */
2181 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
2182 if (!offset) {
2183 zfree(*time);
2184 return IE_DATE;
2187 /* Get subseconds */
2188 if (*offset == '.' ) {
2189 offset++;
2191 /* Copy first 6 digits, pad it with zeros.
2192 * XXX: It truncates longer number, no round.
2193 * Current server implementation uses only millisecond resolution. */
2194 /* TODO: isdigit() is locale sensitive */
2195 for (i = 0;
2196 i < sizeof(subseconds)/sizeof(char) - 1 && isdigit(*offset);
2197 i++, offset++) {
2198 subseconds[i] = *offset;
2200 for (; i < sizeof(subseconds)/sizeof(char) - 1; i++) {
2201 subseconds[i] = '0';
2203 subseconds[6] = '\0';
2205 /* Convert it into integer */
2206 (*time)->tv_usec = strtol(subseconds, &endptr, 10);
2207 if (*endptr != '\0' || (*time)->tv_usec == LONG_MIN ||
2208 (*time)->tv_usec == LONG_MAX) {
2209 zfree(*time);
2210 return IE_DATE;
2213 /* move to the zone offset delimiter or signal NULL*/
2214 delim = strchr(offset, '-');
2215 if (!delim)
2216 delim = strchr(offset, '+');
2217 if (!delim)
2218 delim = strchr(offset, 'Z');
2219 offset = delim;
2222 /* Get zone offset */
2223 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2224 * "" equals to "Z" and it means UTC zone. */
2225 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2226 * colon separator */
2227 if (offset && (*offset == '-' || *offset == '+')) {
2228 if (2 != sscanf(offset + 1, "%2d:%2d", &offset_hours, &offset_minutes)) {
2229 zfree(*time);
2230 return IE_DATE;
2232 if (*offset == '+') {
2233 broken.tm_hour -= offset_hours;
2234 broken.tm_min -= offset_minutes;
2235 } else {
2236 broken.tm_hour += offset_hours;
2237 broken.tm_min += offset_minutes;
2241 /* Convert to time_t */
2242 (*time)->tv_sec = _isds_timegm(&broken);
2243 if ((*time)->tv_sec == (time_t) -1) {
2244 zfree(*time);
2245 return IE_DATE;
2248 return IE_SUCCESS;
2252 /* Convert unsigned int into isds_message_status.
2253 * @context is session context
2254 * @number is pointer to number value. NULL will be treated as invalid value.
2255 * @status is automatically reallocated status
2256 * @return IE_SUCCESS, or error code and free status */
2257 static isds_error uint2isds_message_status(struct isds_ctx *context,
2258 const unsigned long int *number, isds_message_status **status) {
2259 if (!context) return IE_INVALID_CONTEXT;
2260 if (!status) return IE_INVAL;
2262 free(*status); *status = NULL;
2263 if (!number) return IE_INVAL;
2265 if (*number < 1 || *number > 10) {
2266 isds_printf_message(context, _("Invalid message status value: %lu"),
2267 *number);
2268 return IE_ENUM;
2271 *status = malloc(sizeof(**status));
2272 if (!*status) return IE_NOMEM;
2274 **status = 1 << *number;
2275 return IE_SUCCESS;
2279 /* Convert event description string into isds_event members type and
2280 * description
2281 * @string is raw event description starting with event prefix
2282 * @event is structure where to store type and stripped description to
2283 * @return standard error code, unknown prefix is not classified as an error.
2284 * */
2285 static isds_error eventstring2event(const xmlChar *string,
2286 struct isds_event* event) {
2287 const xmlChar *known_prefixes[] = {
2288 BAD_CAST "EV0:",
2289 BAD_CAST "EV1:",
2290 BAD_CAST "EV2:",
2291 BAD_CAST "EV3:",
2292 BAD_CAST "EV4:",
2293 BAD_CAST "EV5:",
2294 BAD_CAST "EV11:",
2295 BAD_CAST "EV12:",
2296 BAD_CAST "EV13:"
2298 const isds_event_type types[] = {
2299 EVENT_ENTERED_SYSTEM,
2300 EVENT_ACCEPTED_BY_RECIPIENT,
2301 EVENT_ACCEPTED_BY_FICTION,
2302 EVENT_UNDELIVERABLE,
2303 EVENT_COMMERCIAL_ACCEPTED,
2304 EVENT_DELIVERED,
2305 EVENT_PRIMARY_LOGIN,
2306 EVENT_ENTRUSTED_LOGIN,
2307 EVENT_SYSCERT_LOGIN
2309 unsigned int index;
2310 size_t length;
2312 if (!string || !event) return IE_INVAL;
2314 if (!event->type) {
2315 event->type = malloc(sizeof(*event->type));
2316 if (!(event->type)) return IE_NOMEM;
2318 zfree(event->description);
2320 for (index = 0; index < sizeof(known_prefixes)/sizeof(known_prefixes[0]);
2321 index++) {
2322 length = xmlUTF8Strlen(known_prefixes[index]);
2324 if (!xmlStrncmp(string, known_prefixes[index], length)) {
2325 /* Prefix is known */
2326 *event->type = types[index];
2328 /* Strip prefix from description and spaces */
2329 /* TODO: Recognize all white spaces from UCS blank class and
2330 * operate on UTF-8 chars. */
2331 for (; string[length] != '\0' && string[length] == ' '; length++);
2332 event->description = strdup((char *) (string + length));
2333 if (!(event->description)) return IE_NOMEM;
2335 return IE_SUCCESS;
2339 /* Unknown event prefix.
2340 * XSD allows any string */
2341 char *string_locale = _isds_utf82locale((char *) string);
2342 isds_log(ILF_ISDS, ILL_WARNING,
2343 _("Unknown delivery info event prefix: %s\n"), string_locale);
2344 free(string_locale);
2346 *event->type = EVENT_UKNOWN;
2347 event->description = strdup((char *) string);
2348 if (!(event->description)) return IE_NOMEM;
2350 return IE_SUCCESS;
2354 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2355 * and leave label */
2356 #define EXTRACT_STRING(element, string) { \
2357 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2358 if (!result) { \
2359 err = IE_ERROR; \
2360 goto leave; \
2362 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2363 if (result->nodesetval->nodeNr > 1) { \
2364 isds_printf_message(context, _("Multiple %s element"), element); \
2365 err = IE_ERROR; \
2366 goto leave; \
2368 (string) = (char *) \
2369 xmlXPathCastNodeSetToString(result->nodesetval); \
2370 if (!(string)) { \
2371 err = IE_ERROR; \
2372 goto leave; \
2377 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2379 char *string = NULL; \
2380 EXTRACT_STRING(element, string); \
2382 if (string) { \
2383 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2384 if (!(booleanPtr)) { \
2385 free(string); \
2386 err = IE_NOMEM; \
2387 goto leave; \
2390 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2391 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2392 *(booleanPtr) = 1; \
2393 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2394 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2395 *(booleanPtr) = 0; \
2396 else { \
2397 char *string_locale = _isds_utf82locale((char*)string); \
2398 isds_printf_message(context, \
2399 _("%s value is not valid boolean: %s"), \
2400 element, string_locale); \
2401 free(string_locale); \
2402 free(string); \
2403 err = IE_ERROR; \
2404 goto leave; \
2407 free(string); \
2411 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2413 char *string = NULL; \
2414 EXTRACT_STRING(element, string); \
2415 if (string) { \
2416 long int number; \
2417 char *endptr; \
2419 number = strtol((char*)string, &endptr, 10); \
2421 if (*endptr != '\0') { \
2422 char *string_locale = _isds_utf82locale((char *)string); \
2423 isds_printf_message(context, \
2424 _("%s is not valid integer: %s"), \
2425 element, string_locale); \
2426 free(string_locale); \
2427 free(string); \
2428 err = IE_ISDS; \
2429 goto leave; \
2432 if (number == LONG_MIN || number == LONG_MAX) { \
2433 char *string_locale = _isds_utf82locale((char *)string); \
2434 isds_printf_message(context, \
2435 _("%s value out of range of long int: %s"), \
2436 element, string_locale); \
2437 free(string_locale); \
2438 free(string); \
2439 err = IE_ERROR; \
2440 goto leave; \
2443 free(string); string = NULL; \
2445 if (!(preallocated)) { \
2446 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2447 if (!(longintPtr)) { \
2448 err = IE_NOMEM; \
2449 goto leave; \
2452 *(longintPtr) = number; \
2456 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2458 char *string = NULL; \
2459 EXTRACT_STRING(element, string); \
2460 if (string) { \
2461 long int number; \
2462 char *endptr; \
2464 number = strtol((char*)string, &endptr, 10); \
2466 if (*endptr != '\0') { \
2467 char *string_locale = _isds_utf82locale((char *)string); \
2468 isds_printf_message(context, \
2469 _("%s is not valid integer: %s"), \
2470 element, string_locale); \
2471 free(string_locale); \
2472 free(string); \
2473 err = IE_ISDS; \
2474 goto leave; \
2477 if (number == LONG_MIN || number == LONG_MAX) { \
2478 char *string_locale = _isds_utf82locale((char *)string); \
2479 isds_printf_message(context, \
2480 _("%s value out of range of long int: %s"), \
2481 element, string_locale); \
2482 free(string_locale); \
2483 free(string); \
2484 err = IE_ERROR; \
2485 goto leave; \
2488 free(string); string = NULL; \
2489 if (number < 0) { \
2490 isds_printf_message(context, \
2491 _("%s value is negative: %ld"), element, number); \
2492 err = IE_ERROR; \
2493 goto leave; \
2496 if (!(preallocated)) { \
2497 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2498 if (!(ulongintPtr)) { \
2499 err = IE_NOMEM; \
2500 goto leave; \
2503 *(ulongintPtr) = number; \
2507 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2508 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2509 NULL); \
2510 if ((required) && (!string)) { \
2511 char *attribute_locale = _isds_utf82locale(attribute); \
2512 char *element_locale = \
2513 _isds_utf82locale((char *)xpath_ctx->node->name); \
2514 isds_printf_message(context, \
2515 _("Could not extract required %s attribute value from " \
2516 "%s element"), attribute_locale, element_locale); \
2517 free(element_locale); \
2518 free(attribute_locale); \
2519 err = IE_ERROR; \
2520 goto leave; \
2525 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2527 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2528 (xmlChar *) (string)); \
2529 if (!node) { \
2530 isds_printf_message(context, \
2531 _("Could not add %s child to %s element"), \
2532 element, (parent)->name); \
2533 err = IE_ERROR; \
2534 goto leave; \
2538 #define INSERT_STRING(parent, element, string) \
2539 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2541 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2543 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2544 else { INSERT_STRING(parent, element, "false"); } \
2547 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2549 if (booleanPtr) { \
2550 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2551 } else { \
2552 INSERT_STRING(parent, element, NULL); \
2556 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2557 if ((longintPtr)) { \
2558 /* FIXME: locale sensitive */ \
2559 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2560 err = IE_NOMEM; \
2561 goto leave; \
2563 INSERT_STRING(parent, element, buffer) \
2564 free(buffer); (buffer) = NULL; \
2565 } else { INSERT_STRING(parent, element, NULL) } \
2568 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2569 if ((ulongintPtr)) { \
2570 /* FIXME: locale sensitive */ \
2571 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2572 err = IE_NOMEM; \
2573 goto leave; \
2575 INSERT_STRING(parent, element, buffer) \
2576 free(buffer); (buffer) = NULL; \
2577 } else { INSERT_STRING(parent, element, NULL) } \
2580 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2582 /* FIXME: locale sensitive */ \
2583 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2584 err = IE_NOMEM; \
2585 goto leave; \
2587 INSERT_STRING(parent, element, buffer) \
2588 free(buffer); (buffer) = NULL; \
2591 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2592 * new attribute. */
2593 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2595 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2596 (xmlChar *) (string)); \
2597 if (!attribute_node) { \
2598 isds_printf_message(context, _("Could not add %s " \
2599 "attribute to %s element"), \
2600 (attribute), (parent)->name); \
2601 err = IE_ERROR; \
2602 goto leave; \
2606 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2607 if (string) { \
2608 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2609 if (length > (maximum)) { \
2610 isds_printf_message(context, \
2611 ngettext("%s has more than %d characters", \
2612 "%s has more than %d characters", (maximum)), \
2613 (name), (maximum)); \
2614 err = IE_2BIG; \
2615 goto leave; \
2617 if (length < (minimum)) { \
2618 isds_printf_message(context, \
2619 ngettext("%s has less than %d characters", \
2620 "%s has less than %d characters", (minimum)), \
2621 (name), (minimum)); \
2622 err = IE_2SMALL; \
2623 goto leave; \
2628 #define INSERT_ELEMENT(child, parent, element) \
2630 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2631 if (!(child)) { \
2632 isds_printf_message(context, \
2633 _("Could not add %s child to %s element"), \
2634 (element), (parent)->name); \
2635 err = IE_ERROR; \
2636 goto leave; \
2641 /* Find child element by name in given XPath context and switch context onto
2642 * it. The child must be uniq and must exist. Otherwise fails.
2643 * @context is ISDS context
2644 * @child is child element name
2645 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2646 * into it child. In error case, the @xpath_ctx keeps original value. */
2647 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
2648 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
2649 isds_error err = IE_SUCCESS;
2650 xmlXPathObjectPtr result = NULL;
2652 if (!context) return IE_INVALID_CONTEXT;
2653 if (!child || !xpath_ctx) return IE_INVAL;
2655 /* Find child */
2656 result = xmlXPathEvalExpression(child, xpath_ctx);
2657 if (!result) {
2658 err = IE_XML;
2659 goto leave;
2662 /* No match */
2663 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2664 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2665 char *child_locale = _isds_utf82locale((char*) child);
2666 isds_printf_message(context,
2667 _("%s element does not contain %s child"),
2668 parent_locale, child_locale);
2669 free(child_locale);
2670 free(parent_locale);
2671 err = IE_NOEXIST;
2672 goto leave;
2675 /* More matches */
2676 if (result->nodesetval->nodeNr > 1) {
2677 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2678 char *child_locale = _isds_utf82locale((char*) child);
2679 isds_printf_message(context,
2680 _("%s element contains multiple %s children"),
2681 parent_locale, child_locale);
2682 free(child_locale);
2683 free(parent_locale);
2684 err = IE_NOTUNIQ;
2685 goto leave;
2688 /* Switch context */
2689 xpath_ctx->node = result->nodesetval->nodeTab[0];
2691 leave:
2692 xmlXPathFreeObject(result);
2693 return err;
2698 #if HAVE_LIBCURL
2699 /* Find and convert XSD:gPersonName group in current node into structure
2700 * @context is ISDS context
2701 * @personName is automatically reallocated person name structure. If no member
2702 * value is found, will be freed.
2703 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2704 * elements
2705 * In case of error @personName will be freed. */
2706 static isds_error extract_gPersonName(struct isds_ctx *context,
2707 struct isds_PersonName **personName, xmlXPathContextPtr xpath_ctx) {
2708 isds_error err = IE_SUCCESS;
2709 xmlXPathObjectPtr result = NULL;
2711 if (!context) return IE_INVALID_CONTEXT;
2712 if (!personName) return IE_INVAL;
2713 isds_PersonName_free(personName);
2714 if (!xpath_ctx) return IE_INVAL;
2717 *personName = calloc(1, sizeof(**personName));
2718 if (!*personName) {
2719 err = IE_NOMEM;
2720 goto leave;
2723 EXTRACT_STRING("isds:pnFirstName", (*personName)->pnFirstName);
2724 EXTRACT_STRING("isds:pnMiddleName", (*personName)->pnMiddleName);
2725 EXTRACT_STRING("isds:pnLastName", (*personName)->pnLastName);
2726 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName)->pnLastNameAtBirth);
2728 if (!(*personName)->pnFirstName && !(*personName)->pnMiddleName &&
2729 !(*personName)->pnLastName && !(*personName)->pnLastNameAtBirth)
2730 isds_PersonName_free(personName);
2732 leave:
2733 if (err) isds_PersonName_free(personName);
2734 xmlXPathFreeObject(result);
2735 return err;
2739 /* Find and convert XSD:gAddress group in current node into structure
2740 * @context is ISDS context
2741 * @address is automatically reallocated address structure. If no member
2742 * value is found, will be freed.
2743 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2744 * elements
2745 * In case of error @address will be freed. */
2746 static isds_error extract_gAddress(struct isds_ctx *context,
2747 struct isds_Address **address, xmlXPathContextPtr xpath_ctx) {
2748 isds_error err = IE_SUCCESS;
2749 xmlXPathObjectPtr result = NULL;
2751 if (!context) return IE_INVALID_CONTEXT;
2752 if (!address) return IE_INVAL;
2753 isds_Address_free(address);
2754 if (!xpath_ctx) return IE_INVAL;
2757 *address = calloc(1, sizeof(**address));
2758 if (!*address) {
2759 err = IE_NOMEM;
2760 goto leave;
2763 EXTRACT_STRING("isds:adCity", (*address)->adCity);
2764 EXTRACT_STRING("isds:adStreet", (*address)->adStreet);
2765 EXTRACT_STRING("isds:adNumberInStreet", (*address)->adNumberInStreet);
2766 EXTRACT_STRING("isds:adNumberInMunicipality",
2767 (*address)->adNumberInMunicipality);
2768 EXTRACT_STRING("isds:adZipCode", (*address)->adZipCode);
2769 EXTRACT_STRING("isds:adState", (*address)->adState);
2771 if (!(*address)->adCity && !(*address)->adStreet &&
2772 !(*address)->adNumberInStreet &&
2773 !(*address)->adNumberInMunicipality &&
2774 !(*address)->adZipCode && !(*address)->adState)
2775 isds_Address_free(address);
2777 leave:
2778 if (err) isds_Address_free(address);
2779 xmlXPathFreeObject(result);
2780 return err;
2784 /* Find and convert isds:biDate element in current node into structure
2785 * @context is ISDS context
2786 * @biDate is automatically reallocated birth date structure. If no member
2787 * value is found, will be freed.
2788 * @xpath_ctx is XPath context with current node as parent for isds:biDate
2789 * element
2790 * In case of error @biDate will be freed. */
2791 static isds_error extract_BiDate(struct isds_ctx *context,
2792 struct tm **biDate, xmlXPathContextPtr xpath_ctx) {
2793 isds_error err = IE_SUCCESS;
2794 xmlXPathObjectPtr result = NULL;
2795 char *string = NULL;
2797 if (!context) return IE_INVALID_CONTEXT;
2798 if (!biDate) return IE_INVAL;
2799 zfree(*biDate);
2800 if (!xpath_ctx) return IE_INVAL;
2802 EXTRACT_STRING("isds:biDate", string);
2803 if (string) {
2804 *biDate = calloc(1, sizeof(**biDate));
2805 if (!*biDate) {
2806 err = IE_NOMEM;
2807 goto leave;
2809 err = datestring2tm((xmlChar *)string, *biDate);
2810 if (err) {
2811 if (err == IE_NOTSUP) {
2812 err = IE_ISDS;
2813 char *string_locale = _isds_utf82locale(string);
2814 isds_printf_message(context,
2815 _("Invalid isds:biDate value: %s"), string_locale);
2816 free(string_locale);
2818 goto leave;
2822 leave:
2823 if (err) zfree(*biDate);
2824 free(string);
2825 xmlXPathFreeObject(result);
2826 return err;
2830 /* Convert isds:dBOwnerInfo XML tree into structure
2831 * @context is ISDS context
2832 * @db_owner_info is automatically reallocated box owner info structure
2833 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
2834 * In case of error @db_owner_info will be freed. */
2835 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
2836 struct isds_DbOwnerInfo **db_owner_info,
2837 xmlXPathContextPtr xpath_ctx) {
2838 isds_error err = IE_SUCCESS;
2839 xmlXPathObjectPtr result = NULL;
2840 char *string = NULL;
2842 if (!context) return IE_INVALID_CONTEXT;
2843 if (!db_owner_info) return IE_INVAL;
2844 isds_DbOwnerInfo_free(db_owner_info);
2845 if (!xpath_ctx) return IE_INVAL;
2848 *db_owner_info = calloc(1, sizeof(**db_owner_info));
2849 if (!*db_owner_info) {
2850 err = IE_NOMEM;
2851 goto leave;
2854 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
2856 EXTRACT_STRING("isds:dbType", string);
2857 if (string) {
2858 (*db_owner_info)->dbType =
2859 calloc(1, sizeof(*((*db_owner_info)->dbType)));
2860 if (!(*db_owner_info)->dbType) {
2861 err = IE_NOMEM;
2862 goto leave;
2864 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
2865 if (err) {
2866 zfree((*db_owner_info)->dbType);
2867 if (err == IE_ENUM) {
2868 err = IE_ISDS;
2869 char *string_locale = _isds_utf82locale(string);
2870 isds_printf_message(context, _("Unknown isds:dbType: %s"),
2871 string_locale);
2872 free(string_locale);
2874 goto leave;
2876 zfree(string);
2879 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
2881 err = extract_gPersonName(context, &(*db_owner_info)->personName,
2882 xpath_ctx);
2883 if (err) goto leave;
2885 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
2887 (*db_owner_info)->birthInfo =
2888 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
2889 if (!(*db_owner_info)->birthInfo) {
2890 err = IE_NOMEM;
2891 goto leave;
2893 err = extract_BiDate(context, &(*db_owner_info)->birthInfo->biDate,
2894 xpath_ctx);
2895 if (err) goto leave;
2896 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
2897 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
2898 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
2899 if (!(*db_owner_info)->birthInfo->biDate &&
2900 !(*db_owner_info)->birthInfo->biCity &&
2901 !(*db_owner_info)->birthInfo->biCounty &&
2902 !(*db_owner_info)->birthInfo->biState)
2903 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
2905 err = extract_gAddress(context, &(*db_owner_info)->address, xpath_ctx);
2906 if (err) goto leave;
2908 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
2909 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
2910 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
2911 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
2912 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
2914 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
2916 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
2917 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
2918 (*db_owner_info)->dbOpenAddressing);
2920 leave:
2921 if (err) isds_DbOwnerInfo_free(db_owner_info);
2922 free(string);
2923 xmlXPathFreeObject(result);
2924 return err;
2928 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
2929 * @context is session context
2930 * @owner is libisds structure with box description
2931 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
2932 static isds_error insert_DbOwnerInfo(struct isds_ctx *context,
2933 const struct isds_DbOwnerInfo *owner, xmlNodePtr db_owner_info) {
2935 isds_error err = IE_SUCCESS;
2936 xmlNodePtr node;
2937 xmlChar *string = NULL;
2939 if (!context) return IE_INVALID_CONTEXT;
2940 if (!owner || !db_owner_info) return IE_INVAL;
2943 /* Build XSD:tDbOwnerInfo */
2944 CHECK_FOR_STRING_LENGTH(owner->dbID, 0, 7, "dbID")
2945 INSERT_STRING(db_owner_info, "dbID", owner->dbID);
2947 /* dbType */
2948 if (owner->dbType) {
2949 const xmlChar *type_string = isds_DbType2string(*(owner->dbType));
2950 if (!type_string) {
2951 isds_printf_message(context, _("Invalid dbType value: %d"),
2952 *(owner->dbType));
2953 err = IE_ENUM;
2954 goto leave;
2956 INSERT_STRING(db_owner_info, "dbType", type_string);
2958 INSERT_STRING(db_owner_info, "ic", owner->ic);
2959 if (owner->personName) {
2960 INSERT_STRING(db_owner_info, "pnFirstName",
2961 owner->personName->pnFirstName);
2962 INSERT_STRING(db_owner_info, "pnMiddleName",
2963 owner->personName->pnMiddleName);
2964 INSERT_STRING(db_owner_info, "pnLastName",
2965 owner->personName->pnLastName);
2966 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
2967 owner->personName->pnLastNameAtBirth);
2969 INSERT_STRING(db_owner_info, "firmName", owner->firmName);
2970 if (owner->birthInfo) {
2971 if (owner->birthInfo->biDate) {
2972 if (!tm2datestring(owner->birthInfo->biDate, &string))
2973 INSERT_STRING(db_owner_info, "biDate", string);
2974 free(string); string = NULL;
2976 INSERT_STRING(db_owner_info, "biCity", owner->birthInfo->biCity);
2977 INSERT_STRING(db_owner_info, "biCounty", owner->birthInfo->biCounty);
2978 INSERT_STRING(db_owner_info, "biState", owner->birthInfo->biState);
2980 if (owner->address) {
2981 INSERT_STRING(db_owner_info, "adCity", owner->address->adCity);
2982 INSERT_STRING(db_owner_info, "adStreet", owner->address->adStreet);
2983 INSERT_STRING(db_owner_info, "adNumberInStreet",
2984 owner->address->adNumberInStreet);
2985 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
2986 owner->address->adNumberInMunicipality);
2987 INSERT_STRING(db_owner_info, "adZipCode", owner->address->adZipCode);
2988 INSERT_STRING(db_owner_info, "adState", owner->address->adState);
2990 INSERT_STRING(db_owner_info, "nationality", owner->nationality);
2991 INSERT_STRING(db_owner_info, "email", owner->email);
2992 INSERT_STRING(db_owner_info, "telNumber", owner->telNumber);
2994 CHECK_FOR_STRING_LENGTH(owner->identifier, 0, 20, "identifier")
2995 INSERT_STRING(db_owner_info, "identifier", owner->identifier);
2997 CHECK_FOR_STRING_LENGTH(owner->registryCode, 0, 5, "registryCode")
2998 INSERT_STRING(db_owner_info, "registryCode", owner->registryCode);
3000 INSERT_LONGINT(db_owner_info, "dbState", owner->dbState, string);
3002 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", owner->dbEffectiveOVM);
3003 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
3004 owner->dbOpenAddressing);
3006 leave:
3007 free(string);
3008 return err;
3012 /* Convert XSD:tDbUserInfo XML tree into structure
3013 * @context is ISDS context
3014 * @db_user_info is automatically reallocated user info structure
3015 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3016 * In case of error @db_user_info will be freed. */
3017 static isds_error extract_DbUserInfo(struct isds_ctx *context,
3018 struct isds_DbUserInfo **db_user_info, xmlXPathContextPtr xpath_ctx) {
3019 isds_error err = IE_SUCCESS;
3020 xmlXPathObjectPtr result = NULL;
3021 char *string = NULL;
3023 if (!context) return IE_INVALID_CONTEXT;
3024 if (!db_user_info) return IE_INVAL;
3025 isds_DbUserInfo_free(db_user_info);
3026 if (!xpath_ctx) return IE_INVAL;
3029 *db_user_info = calloc(1, sizeof(**db_user_info));
3030 if (!*db_user_info) {
3031 err = IE_NOMEM;
3032 goto leave;
3035 EXTRACT_STRING("isds:userID", (*db_user_info)->userID);
3037 EXTRACT_STRING("isds:userType", string);
3038 if (string) {
3039 (*db_user_info)->userType =
3040 calloc(1, sizeof(*((*db_user_info)->userType)));
3041 if (!(*db_user_info)->userType) {
3042 err = IE_NOMEM;
3043 goto leave;
3045 err = string2isds_UserType((xmlChar *)string,
3046 (*db_user_info)->userType);
3047 if (err) {
3048 zfree((*db_user_info)->userType);
3049 if (err == IE_ENUM) {
3050 err = IE_ISDS;
3051 char *string_locale = _isds_utf82locale(string);
3052 isds_printf_message(context,
3053 _("Unknown isds:userType value: %s"), string_locale);
3054 free(string_locale);
3056 goto leave;
3058 zfree(string);
3061 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info)->userPrivils, 0);
3063 (*db_user_info)->personName =
3064 calloc(1, sizeof(*((*db_user_info)->personName)));
3065 if (!(*db_user_info)->personName) {
3066 err = IE_NOMEM;
3067 goto leave;
3070 err = extract_gPersonName(context, &(*db_user_info)->personName,
3071 xpath_ctx);
3072 if (err) goto leave;
3074 err = extract_gAddress(context, &(*db_user_info)->address, xpath_ctx);
3075 if (err) goto leave;
3077 err = extract_BiDate(context, &(*db_user_info)->biDate, xpath_ctx);
3078 if (err) goto leave;
3080 EXTRACT_STRING("isds:ic", (*db_user_info)->ic);
3081 EXTRACT_STRING("isds:firmName", (*db_user_info)->firmName);
3083 EXTRACT_STRING("isds:caStreet", (*db_user_info)->caStreet);
3084 EXTRACT_STRING("isds:caCity", (*db_user_info)->caCity);
3085 EXTRACT_STRING("isds:caZipCode", (*db_user_info)->caZipCode);
3087 /* ???: Default value is "CZ" according specification. Should we provide
3088 * it? */
3089 EXTRACT_STRING("isds:caState", (*db_user_info)->caState);
3091 leave:
3092 if (err) isds_DbUserInfo_free(db_user_info);
3093 free(string);
3094 xmlXPathFreeObject(result);
3095 return err;
3099 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3100 * @context is session context
3101 * @user is libisds structure with user description
3102 * @db_user_info is XML element of XSD:tDbUserInfo */
3103 static isds_error insert_DbUserInfo(struct isds_ctx *context,
3104 const struct isds_DbUserInfo *user, xmlNodePtr db_user_info) {
3106 isds_error err = IE_SUCCESS;
3107 xmlNodePtr node;
3108 xmlChar *string = NULL;
3110 if (!context) return IE_INVALID_CONTEXT;
3111 if (!user || !db_user_info) return IE_INVAL;
3113 /* Build XSD:tDbUserInfo */
3114 if (user->personName) {
3115 INSERT_STRING(db_user_info, "pnFirstName",
3116 user->personName->pnFirstName);
3117 INSERT_STRING(db_user_info, "pnMiddleName",
3118 user->personName->pnMiddleName);
3119 INSERT_STRING(db_user_info, "pnLastName",
3120 user->personName->pnLastName);
3121 INSERT_STRING(db_user_info, "pnLastNameAtBirth",
3122 user->personName->pnLastNameAtBirth);
3124 if (user->address) {
3125 INSERT_STRING(db_user_info, "adCity", user->address->adCity);
3126 INSERT_STRING(db_user_info, "adStreet", user->address->adStreet);
3127 INSERT_STRING(db_user_info, "adNumberInStreet",
3128 user->address->adNumberInStreet);
3129 INSERT_STRING(db_user_info, "adNumberInMunicipality",
3130 user->address->adNumberInMunicipality);
3131 INSERT_STRING(db_user_info, "adZipCode", user->address->adZipCode);
3132 INSERT_STRING(db_user_info, "adState", user->address->adState);
3134 if (user->biDate) {
3135 if (!tm2datestring(user->biDate, &string))
3136 INSERT_STRING(db_user_info, "biDate", string);
3137 zfree(string);
3139 CHECK_FOR_STRING_LENGTH(user->userID, 6, 12, "userID");
3140 INSERT_STRING(db_user_info, "userID", user->userID);
3142 /* userType */
3143 if (user->userType) {
3144 const xmlChar *type_string = isds_UserType2string(*(user->userType));
3145 if (!type_string) {
3146 isds_printf_message(context, _("Invalid userType value: %d"),
3147 *(user->userType));
3148 err = IE_ENUM;
3149 goto leave;
3151 INSERT_STRING(db_user_info, "userType", type_string);
3154 INSERT_LONGINT(db_user_info, "userPrivils", user->userPrivils, string);
3155 CHECK_FOR_STRING_LENGTH(user->ic, 0, 8, "ic")
3156 INSERT_STRING(db_user_info, "ic", user->ic);
3157 CHECK_FOR_STRING_LENGTH(user->firmName, 0, 100, "firmName")
3158 INSERT_STRING(db_user_info, "firmName", user->firmName);
3159 INSERT_STRING(db_user_info, "caStreet", user->caStreet);
3160 INSERT_STRING(db_user_info, "caCity", user->caCity);
3161 INSERT_STRING(db_user_info, "caZipCode", user->caZipCode);
3162 INSERT_STRING(db_user_info, "caState", user->caState);
3164 leave:
3165 free(string);
3166 return err;
3170 /* Convert XSD:tPDZRec XML tree into structure
3171 * @context is ISDS context
3172 * @permission is automatically reallocated commercial permission structure
3173 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3174 * In case of error @permission will be freed. */
3175 static isds_error extract_DbPDZRecord(struct isds_ctx *context,
3176 struct isds_commercial_permission **permission,
3177 xmlXPathContextPtr xpath_ctx) {
3178 isds_error err = IE_SUCCESS;
3179 xmlXPathObjectPtr result = NULL;
3180 char *string = NULL;
3182 if (!context) return IE_INVALID_CONTEXT;
3183 if (!permission) return IE_INVAL;
3184 isds_commercial_permission_free(permission);
3185 if (!xpath_ctx) return IE_INVAL;
3188 *permission = calloc(1, sizeof(**permission));
3189 if (!*permission) {
3190 err = IE_NOMEM;
3191 goto leave;
3194 EXTRACT_STRING("isds:PDZType", string);
3195 if (string) {
3196 err = string2isds_payment_type((xmlChar *)string,
3197 &(*permission)->type);
3198 if (err) {
3199 if (err == IE_ENUM) {
3200 err = IE_ISDS;
3201 char *string_locale = _isds_utf82locale(string);
3202 isds_printf_message(context,
3203 _("Unknown isds:PDZType value: %s"), string_locale);
3204 free(string_locale);
3206 goto leave;
3208 zfree(string);
3211 EXTRACT_STRING("isds:PDZRecip", (*permission)->recipient);
3212 EXTRACT_STRING("isds:PDZPayer", (*permission)->payer);
3214 EXTRACT_STRING("isds:PDZExpire", string);
3215 if (string) {
3216 err = timestring2timeval((xmlChar *) string,
3217 &((*permission)->expiration));
3218 if (err) {
3219 char *string_locale = _isds_utf82locale(string);
3220 if (err == IE_DATE) err = IE_ISDS;
3221 isds_printf_message(context,
3222 _("Could not convert PDZExpire as ISO time: %s"),
3223 string_locale);
3224 free(string_locale);
3225 goto leave;
3227 zfree(string);
3230 EXTRACT_ULONGINT("isds:PDZCnt", (*permission)->count, 0);
3231 EXTRACT_STRING("isds:ODZIdent", (*permission)->reply_identifier);
3233 leave:
3234 if (err) isds_commercial_permission_free(permission);
3235 free(string);
3236 xmlXPathFreeObject(result);
3237 return err;
3241 #endif /* HAVE_LIBCURL */
3244 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3245 * isds_envelope structure. The envelope is automatically allocated but not
3246 * reallocated. The date are just appended into envelope structure.
3247 * @context is ISDS context
3248 * @envelope is automatically allocated message envelope structure
3249 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3250 * In case of error @envelope will be freed. */
3251 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
3252 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3253 isds_error err = IE_SUCCESS;
3254 xmlXPathObjectPtr result = NULL;
3256 if (!context) return IE_INVALID_CONTEXT;
3257 if (!envelope) return IE_INVAL;
3258 if (!xpath_ctx) return IE_INVAL;
3261 if (!*envelope) {
3262 /* Allocate envelope */
3263 *envelope = calloc(1, sizeof(**envelope));
3264 if (!*envelope) {
3265 err = IE_NOMEM;
3266 goto leave;
3268 } else {
3269 /* Else free former data */
3270 zfree((*envelope)->dmSenderOrgUnit);
3271 zfree((*envelope)->dmSenderOrgUnitNum);
3272 zfree((*envelope)->dbIDRecipient);
3273 zfree((*envelope)->dmRecipientOrgUnit);
3274 zfree((*envelope)->dmSenderOrgUnitNum);
3275 zfree((*envelope)->dmToHands);
3276 zfree((*envelope)->dmAnnotation);
3277 zfree((*envelope)->dmRecipientRefNumber);
3278 zfree((*envelope)->dmSenderRefNumber);
3279 zfree((*envelope)->dmRecipientIdent);
3280 zfree((*envelope)->dmSenderIdent);
3281 zfree((*envelope)->dmLegalTitleLaw);
3282 zfree((*envelope)->dmLegalTitleYear);
3283 zfree((*envelope)->dmLegalTitleSect);
3284 zfree((*envelope)->dmLegalTitlePar);
3285 zfree((*envelope)->dmLegalTitlePoint);
3286 zfree((*envelope)->dmPersonalDelivery);
3287 zfree((*envelope)->dmAllowSubstDelivery);
3290 /* Extract envelope elements added by sender or ISDS
3291 * (XSD: gMessageEnvelopeSub type) */
3292 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
3293 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3294 (*envelope)->dmSenderOrgUnitNum, 0);
3295 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
3296 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
3297 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3298 (*envelope)->dmSenderOrgUnitNum, 0);
3299 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
3300 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
3301 EXTRACT_STRING("isds:dmRecipientRefNumber",
3302 (*envelope)->dmRecipientRefNumber);
3303 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
3304 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
3305 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
3307 /* Extract envelope elements regarding law reference */
3308 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
3309 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
3310 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
3311 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
3312 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
3314 /* Extract envelope other elements */
3315 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
3316 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3317 (*envelope)->dmAllowSubstDelivery);
3319 leave:
3320 if (err) isds_envelope_free(envelope);
3321 xmlXPathFreeObject(result);
3322 return err;
3327 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3328 * isds_envelope structure. The envelope is automatically allocated but not
3329 * reallocated. The date are just appended into envelope structure.
3330 * @context is ISDS context
3331 * @envelope is automatically allocated message envelope structure
3332 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3333 * In case of error @envelope will be freed. */
3334 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
3335 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3336 isds_error err = IE_SUCCESS;
3337 xmlXPathObjectPtr result = NULL;
3339 if (!context) return IE_INVALID_CONTEXT;
3340 if (!envelope) return IE_INVAL;
3341 if (!xpath_ctx) return IE_INVAL;
3344 if (!*envelope) {
3345 /* Allocate envelope */
3346 *envelope = calloc(1, sizeof(**envelope));
3347 if (!*envelope) {
3348 err = IE_NOMEM;
3349 goto leave;
3351 } else {
3352 /* Else free former data */
3353 zfree((*envelope)->dmID);
3354 zfree((*envelope)->dbIDSender);
3355 zfree((*envelope)->dmSender);
3356 zfree((*envelope)->dmSenderAddress);
3357 zfree((*envelope)->dmSenderType);
3358 zfree((*envelope)->dmRecipient);
3359 zfree((*envelope)->dmRecipientAddress);
3360 zfree((*envelope)->dmAmbiguousRecipient);
3363 /* Extract envelope elements added by ISDS
3364 * (XSD: gMessageEnvelope type) */
3365 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
3366 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
3367 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
3368 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
3369 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3370 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
3371 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
3372 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
3373 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3374 (*envelope)->dmAmbiguousRecipient);
3376 /* Extract envelope elements added by sender and ISDS
3377 * (XSD: gMessageEnvelope type) */
3378 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
3379 if (err) goto leave;
3381 leave:
3382 if (err) isds_envelope_free(envelope);
3383 xmlXPathFreeObject(result);
3384 return err;
3388 /* Convert other envelope elements from XML tree into isds_envelope structure:
3389 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3390 * The envelope is automatically allocated but not reallocated.
3391 * The data are just appended into envelope structure.
3392 * @context is ISDS context
3393 * @envelope is automatically allocated message envelope structure
3394 * @xpath_ctx is XPath context with current node as parent desired elements
3395 * In case of error @envelope will be freed. */
3396 static isds_error append_status_size_times(struct isds_ctx *context,
3397 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3398 isds_error err = IE_SUCCESS;
3399 xmlXPathObjectPtr result = NULL;
3400 char *string = NULL;
3401 unsigned long int *unumber = NULL;
3403 if (!context) return IE_INVALID_CONTEXT;
3404 if (!envelope) return IE_INVAL;
3405 if (!xpath_ctx) return IE_INVAL;
3408 if (!*envelope) {
3409 /* Allocate new */
3410 *envelope = calloc(1, sizeof(**envelope));
3411 if (!*envelope) {
3412 err = IE_NOMEM;
3413 goto leave;
3415 } else {
3416 /* Free old data */
3417 zfree((*envelope)->dmMessageStatus);
3418 zfree((*envelope)->dmAttachmentSize);
3419 zfree((*envelope)->dmDeliveryTime);
3420 zfree((*envelope)->dmAcceptanceTime);
3424 /* dmMessageStatus element is mandatory */
3425 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
3426 if (!unumber) {
3427 isds_log_message(context,
3428 _("Missing mandatory sisds:dmMessageStatus integer"));
3429 err = IE_ISDS;
3430 goto leave;
3432 err = uint2isds_message_status(context, unumber,
3433 &((*envelope)->dmMessageStatus));
3434 if (err) {
3435 if (err == IE_ENUM) err = IE_ISDS;
3436 goto leave;
3438 free(unumber); unumber = NULL;
3440 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
3443 EXTRACT_STRING("sisds:dmDeliveryTime", string);
3444 if (string) {
3445 err = timestring2timeval((xmlChar *) string,
3446 &((*envelope)->dmDeliveryTime));
3447 if (err) {
3448 char *string_locale = _isds_utf82locale(string);
3449 if (err == IE_DATE) err = IE_ISDS;
3450 isds_printf_message(context,
3451 _("Could not convert dmDeliveryTime as ISO time: %s"),
3452 string_locale);
3453 free(string_locale);
3454 goto leave;
3456 zfree(string);
3459 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
3460 if (string) {
3461 err = timestring2timeval((xmlChar *) string,
3462 &((*envelope)->dmAcceptanceTime));
3463 if (err) {
3464 char *string_locale = _isds_utf82locale(string);
3465 if (err == IE_DATE) err = IE_ISDS;
3466 isds_printf_message(context,
3467 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3468 string_locale);
3469 free(string_locale);
3470 goto leave;
3472 zfree(string);
3475 leave:
3476 if (err) isds_envelope_free(envelope);
3477 free(unumber);
3478 free(string);
3479 xmlXPathFreeObject(result);
3480 return err;
3484 /* Convert message type attribute of current element into isds_envelope
3485 * structure.
3486 * TODO: This function can be incorporated into append_status_size_times() as
3487 * they are called always together.
3488 * The envelope is automatically allocated but not reallocated.
3489 * The data are just appended into envelope structure.
3490 * @context is ISDS context
3491 * @envelope is automatically allocated message envelope structure
3492 * @xpath_ctx is XPath context with current node as parent of attribute
3493 * carrying message type
3494 * In case of error @envelope will be freed. */
3495 static isds_error append_message_type(struct isds_ctx *context,
3496 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3497 isds_error err = IE_SUCCESS;
3499 if (!context) return IE_INVALID_CONTEXT;
3500 if (!envelope) return IE_INVAL;
3501 if (!xpath_ctx) return IE_INVAL;
3504 if (!*envelope) {
3505 /* Allocate new */
3506 *envelope = calloc(1, sizeof(**envelope));
3507 if (!*envelope) {
3508 err = IE_NOMEM;
3509 goto leave;
3511 } else {
3512 /* Free old data */
3513 zfree((*envelope)->dmType);
3517 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope)->dmType, 0);
3519 if (!(*envelope)->dmType) {
3520 /* Use default value */
3521 (*envelope)->dmType = strdup("V");
3522 if (!(*envelope)->dmType) {
3523 err = IE_NOMEM;
3524 goto leave;
3526 } else if (1 != xmlUTF8Strlen((xmlChar *) (*envelope)->dmType)) {
3527 char *type_locale = _isds_utf82locale((*envelope)->dmType);
3528 isds_printf_message(context,
3529 _("Message type in dmType attribute is not 1 character long: "
3530 "%s"),
3531 type_locale);
3532 free(type_locale);
3533 err = IE_ISDS;
3534 goto leave;
3537 leave:
3538 if (err) isds_envelope_free(envelope);
3539 return err;
3543 #if HAVE_LIBCURL
3544 /* Convert dmType isds_envelope member into XML attribute and append it to
3545 * current node.
3546 * @context is ISDS context
3547 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3548 * @dm_envelope is XML element the resulting attribute will be appended to.
3549 * @return error code, in case of error context' message is filled. */
3550 static isds_error insert_message_type(struct isds_ctx *context,
3551 const char *type, xmlNodePtr dm_envelope) {
3552 isds_error err = IE_SUCCESS;
3553 xmlAttrPtr attribute_node;
3555 if (!context) return IE_INVALID_CONTEXT;
3556 if (!dm_envelope) return IE_INVAL;
3558 /* Insert optional message type */
3559 if (type) {
3560 if (1 != xmlUTF8Strlen((xmlChar *) type)) {
3561 char *type_locale = _isds_utf82locale(type);
3562 isds_printf_message(context,
3563 _("Message type in envelope is not 1 character long: %s"),
3564 type_locale);
3565 free(type_locale);
3566 err = IE_INVAL;
3567 goto leave;
3569 INSERT_STRING_ATTRIBUTE(dm_envelope, "dmType", type);
3572 leave:
3573 return err;
3575 #endif /* HAVE_LIBCURL */
3578 /* Extract message document into reallocated document structure
3579 * @context is ISDS context
3580 * @document is automatically reallocated message documents structure
3581 * @xpath_ctx is XPath context with current node as isds:dmFile
3582 * In case of error @document will be freed. */
3583 static isds_error extract_document(struct isds_ctx *context,
3584 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
3585 isds_error err = IE_SUCCESS;
3586 xmlXPathObjectPtr result = NULL;
3587 xmlNodePtr file_node = xpath_ctx->node;
3588 char *string = NULL;
3590 if (!context) return IE_INVALID_CONTEXT;
3591 if (!document) return IE_INVAL;
3592 isds_document_free(document);
3593 if (!xpath_ctx) return IE_INVAL;
3595 *document = calloc(1, sizeof(**document));
3596 if (!*document) {
3597 err = IE_NOMEM;
3598 goto leave;
3601 /* Extract document meta data */
3602 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
3603 if (context->normalize_mime_type) {
3604 char *normalized_type =
3605 isds_normalize_mime_type((*document)->dmMimeType);
3606 if (normalized_type && normalized_type != (*document)->dmMimeType) {
3607 char *new_type = strdup(normalized_type);
3608 if (!new_type) {
3609 isds_printf_message(context,
3610 _("Not enough memory to normalize document MIME type"));
3611 err = IE_NOMEM;
3612 goto leave;
3614 free((*document)->dmMimeType);
3615 (*document)->dmMimeType = new_type;
3619 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
3620 err = string2isds_FileMetaType((xmlChar*)string,
3621 &((*document)->dmFileMetaType));
3622 if (err) {
3623 char *meta_type_locale = _isds_utf82locale(string);
3624 isds_printf_message(context,
3625 _("Document has invalid dmFileMetaType attribute value: %s"),
3626 meta_type_locale);
3627 free(meta_type_locale);
3628 err = IE_ISDS;
3629 goto leave;
3631 zfree(string);
3633 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
3634 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
3635 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
3636 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
3639 /* Extract document data.
3640 * Base64 encoded blob or XML subtree must be presented. */
3642 /* Check for dmEncodedContent */
3643 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
3644 xpath_ctx);
3645 if (!result) {
3646 err = IE_XML;
3647 goto leave;
3650 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3651 /* Here we have Base64 blob */
3652 (*document)->is_xml = 0;
3654 if (result->nodesetval->nodeNr > 1) {
3655 isds_printf_message(context,
3656 _("Document has more dmEncodedContent elements"));
3657 err = IE_ISDS;
3658 goto leave;
3661 xmlXPathFreeObject(result); result = NULL;
3662 EXTRACT_STRING("isds:dmEncodedContent", string);
3664 /* Decode non-empty document */
3665 if (string && string[0] != '\0') {
3666 (*document)->data_length =
3667 _isds_b64decode(string, &((*document)->data));
3668 if ((*document)->data_length == (size_t) -1) {
3669 isds_printf_message(context,
3670 _("Error while Base64-decoding document content"));
3671 err = IE_ERROR;
3672 goto leave;
3675 } else {
3676 /* No Base64 blob, try XML document */
3677 xmlXPathFreeObject(result); result = NULL;
3678 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
3679 xpath_ctx);
3680 if (!result) {
3681 err = IE_XML;
3682 goto leave;
3685 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3686 /* Here we have XML document */
3687 (*document)->is_xml = 1;
3689 if (result->nodesetval->nodeNr > 1) {
3690 isds_printf_message(context,
3691 _("Document has more dmXMLContent elements"));
3692 err = IE_ISDS;
3693 goto leave;
3696 /* XXX: We cannot serialize the content simply because:
3697 * - XML document may point out of its scope (e.g. to message
3698 * envelope)
3699 * - isds:dmXMLContent can contain more elements, no element,
3700 * a text node only
3701 * - it's not the XML way
3702 * Thus we provide the only right solution: XML DOM. Let's
3703 * application to cope with this hot potato :) */
3704 (*document)->xml_node_list =
3705 result->nodesetval->nodeTab[0]->children;
3706 } else {
3707 /* No base64 blob, nor XML document */
3708 isds_printf_message(context,
3709 _("Document has no dmEncodedContent, nor dmXMLContent "
3710 "element"));
3711 err = IE_ISDS;
3712 goto leave;
3717 leave:
3718 if (err) isds_document_free(document);
3719 free(string);
3720 xmlXPathFreeObject(result);
3721 xpath_ctx->node = file_node;
3722 return err;
3727 /* Extract message documents into reallocated list of documents
3728 * @context is ISDS context
3729 * @documents is automatically reallocated message documents list structure
3730 * @xpath_ctx is XPath context with current node as XSD tFilesArray
3731 * In case of error @documents will be freed. */
3732 static isds_error extract_documents(struct isds_ctx *context,
3733 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
3734 isds_error err = IE_SUCCESS;
3735 xmlXPathObjectPtr result = NULL;
3736 xmlNodePtr files_node = xpath_ctx->node;
3737 struct isds_list *document, *prev_document = NULL;
3739 if (!context) return IE_INVALID_CONTEXT;
3740 if (!documents) return IE_INVAL;
3741 isds_list_free(documents);
3742 if (!xpath_ctx) return IE_INVAL;
3744 /* Find documents */
3745 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
3746 if (!result) {
3747 err = IE_XML;
3748 goto leave;
3751 /* No match */
3752 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3753 isds_printf_message(context,
3754 _("Message does not contain any document"));
3755 err = IE_ISDS;
3756 goto leave;
3760 /* Iterate over documents */
3761 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
3763 /* Allocate and append list item */
3764 document = calloc(1, sizeof(*document));
3765 if (!document) {
3766 err = IE_NOMEM;
3767 goto leave;
3769 document->destructor = (void (*)(void **))isds_document_free;
3770 if (i == 0) *documents = document;
3771 else prev_document->next = document;
3772 prev_document = document;
3774 /* Extract document */
3775 xpath_ctx->node = result->nodesetval->nodeTab[i];
3776 err = extract_document(context,
3777 (struct isds_document **) &(document->data), xpath_ctx);
3778 if (err) goto leave;
3782 leave:
3783 if (err) isds_list_free(documents);
3784 xmlXPathFreeObject(result);
3785 xpath_ctx->node = files_node;
3786 return err;
3790 #if HAVE_LIBCURL
3791 /* Convert isds:dmRecord XML tree into structure
3792 * @context is ISDS context
3793 * @envelope is automatically reallocated message envelope structure
3794 * @xpath_ctx is XPath context with current node as isds:dmRecord element
3795 * In case of error @envelope will be freed. */
3796 static isds_error extract_DmRecord(struct isds_ctx *context,
3797 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3798 isds_error err = IE_SUCCESS;
3799 xmlXPathObjectPtr result = NULL;
3801 if (!context) return IE_INVALID_CONTEXT;
3802 if (!envelope) return IE_INVAL;
3803 isds_envelope_free(envelope);
3804 if (!xpath_ctx) return IE_INVAL;
3807 *envelope = calloc(1, sizeof(**envelope));
3808 if (!*envelope) {
3809 err = IE_NOMEM;
3810 goto leave;
3814 /* Extract tRecord data */
3815 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
3817 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
3818 * dmAcceptanceTime. */
3819 err = append_status_size_times(context, envelope, xpath_ctx);
3820 if (err) goto leave;
3822 /* Extract envelope elements added by sender and ISDS
3823 * (XSD: gMessageEnvelope type) */
3824 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
3825 if (err) goto leave;
3827 /* Get message type */
3828 err = append_message_type(context, envelope, xpath_ctx);
3829 if (err) goto leave;
3832 leave:
3833 if (err) isds_envelope_free(envelope);
3834 xmlXPathFreeObject(result);
3835 return err;
3839 /* Convert XSD:tStateChangesRecord type XML tree into structure
3840 * @context is ISDS context
3841 * @changed_status is automatically reallocated message state change structure
3842 * @xpath_ctx is XPath context with current node as element of
3843 * XSD:tStateChangesRecord type
3844 * In case of error @changed_status will be freed. */
3845 static isds_error extract_StateChangesRecord(struct isds_ctx *context,
3846 struct isds_message_status_change **changed_status,
3847 xmlXPathContextPtr xpath_ctx) {
3848 isds_error err = IE_SUCCESS;
3849 xmlXPathObjectPtr result = NULL;
3850 unsigned long int *unumber = NULL;
3851 char *string = NULL;
3853 if (!context) return IE_INVALID_CONTEXT;
3854 if (!changed_status) return IE_INVAL;
3855 isds_message_status_change_free(changed_status);
3856 if (!xpath_ctx) return IE_INVAL;
3859 *changed_status = calloc(1, sizeof(**changed_status));
3860 if (!*changed_status) {
3861 err = IE_NOMEM;
3862 goto leave;
3866 /* Extract tGetStateChangesInput data */
3867 EXTRACT_STRING("isds:dmID", (*changed_status)->dmID);
3869 /* dmEventTime is mandatory */
3870 EXTRACT_STRING("isds:dmEventTime", string);
3871 if (string) {
3872 err = timestring2timeval((xmlChar *) string,
3873 &((*changed_status)->time));
3874 if (err) {
3875 char *string_locale = _isds_utf82locale(string);
3876 if (err == IE_DATE) err = IE_ISDS;
3877 isds_printf_message(context,
3878 _("Could not convert dmEventTime as ISO time: %s"),
3879 string_locale);
3880 free(string_locale);
3881 goto leave;
3883 zfree(string);
3886 /* dmMessageStatus element is mandatory */
3887 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber, 0);
3888 if (!unumber) {
3889 isds_log_message(context,
3890 _("Missing mandatory isds:dmMessageStatus integer"));
3891 err = IE_ISDS;
3892 goto leave;
3894 err = uint2isds_message_status(context, unumber,
3895 &((*changed_status)->dmMessageStatus));
3896 if (err) {
3897 if (err == IE_ENUM) err = IE_ISDS;
3898 goto leave;
3900 zfree(unumber);
3903 leave:
3904 free(unumber);
3905 free(string);
3906 if (err) isds_message_status_change_free(changed_status);
3907 xmlXPathFreeObject(result);
3908 return err;
3910 #endif /* HAVE_LIBCURL */
3913 /* Find and convert isds:dmHash XML tree into structure
3914 * @context is ISDS context
3915 * @envelope is automatically reallocated message hash structure
3916 * @xpath_ctx is XPath context with current node containing isds:dmHash child
3917 * In case of error @hash will be freed. */
3918 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
3919 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
3920 isds_error err = IE_SUCCESS;
3921 xmlNodePtr old_ctx_node;
3922 xmlXPathObjectPtr result = NULL;
3923 char *string = NULL;
3925 if (!context) return IE_INVALID_CONTEXT;
3926 if (!hash) return IE_INVAL;
3927 isds_hash_free(hash);
3928 if (!xpath_ctx) return IE_INVAL;
3930 old_ctx_node = xpath_ctx->node;
3932 *hash = calloc(1, sizeof(**hash));
3933 if (!*hash) {
3934 err = IE_NOMEM;
3935 goto leave;
3938 /* Locate dmHash */
3939 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
3940 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
3941 err = IE_ISDS;
3942 goto leave;
3944 if (err) {
3945 err = IE_ERROR;
3946 goto leave;
3949 /* Get hash algorithm */
3950 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
3951 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
3952 if (err) {
3953 if (err == IE_ENUM) {
3954 char *string_locale = _isds_utf82locale(string);
3955 isds_printf_message(context, _("Unsupported hash algorithm: %s"),
3956 string_locale);
3957 free(string_locale);
3959 goto leave;
3961 zfree(string);
3963 /* Get hash value */
3964 EXTRACT_STRING(".", string);
3965 if (!string) {
3966 isds_printf_message(context,
3967 _("sisds:dmHash element is missing hash value"));
3968 err = IE_ISDS;
3969 goto leave;
3971 (*hash)->length = _isds_b64decode(string, &((*hash)->value));
3972 if ((*hash)->length == (size_t) -1) {
3973 isds_printf_message(context,
3974 _("Error while Base64-decoding hash value"));
3975 err = IE_ERROR;
3976 goto leave;
3979 leave:
3980 if (err) isds_hash_free(hash);
3981 free(string);
3982 xmlXPathFreeObject(result);
3983 xpath_ctx->node = old_ctx_node;
3984 return err;
3988 /* Find and append isds:dmQTimestamp XML tree into envelope.
3989 * Because one service is allowed to miss time-stamp content, and we think
3990 * other could too (flaw in specification), this function is deliberated and
3991 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
3992 * @context is ISDS context
3993 * @envelope is automatically allocated envelope structure
3994 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
3995 * child
3996 * In case of error @envelope will be freed. */
3997 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
3998 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3999 isds_error err = IE_SUCCESS;
4000 xmlXPathObjectPtr result = NULL;
4001 char *string = NULL;
4003 if (!context) return IE_INVALID_CONTEXT;
4004 if (!envelope) return IE_INVAL;
4005 if (!xpath_ctx) {
4006 isds_envelope_free(envelope);
4007 return IE_INVAL;
4010 if (!*envelope) {
4011 *envelope = calloc(1, sizeof(**envelope));
4012 if (!*envelope) {
4013 err = IE_NOMEM;
4014 goto leave;
4016 } else {
4017 zfree((*envelope)->timestamp);
4018 (*envelope)->timestamp_length = 0;
4021 /* Get dmQTimestamp */
4022 EXTRACT_STRING("sisds:dmQTimestamp", string);
4023 if (!string) {
4024 isds_log(ILF_ISDS, ILL_INFO, _("Missing dmQTimestamp element content\n"));
4025 goto leave;
4027 (*envelope)->timestamp_length =
4028 _isds_b64decode(string, &((*envelope)->timestamp));
4029 if ((*envelope)->timestamp_length == (size_t) -1) {
4030 isds_printf_message(context,
4031 _("Error while Base64-decoding time stamp value"));
4032 err = IE_ERROR;
4033 goto leave;
4036 leave:
4037 if (err) isds_envelope_free(envelope);
4038 free(string);
4039 xmlXPathFreeObject(result);
4040 return err;
4044 /* Convert XSD tReturnedMessage XML tree into message structure.
4045 * It does not store serialized XML tree into message->raw.
4046 * It does store (pointer to) parsed XML tree into message->xml if needed.
4047 * @context is ISDS context
4048 * @include_documents Use true if documents must be extracted
4049 * (tReturnedMessage XSD type), use false if documents shall be omitted
4050 * (tReturnedMessageEnvelope).
4051 * @message is automatically reallocated message structure
4052 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4053 * type
4054 * In case of error @message will be freed. */
4055 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
4056 const _Bool include_documents, struct isds_message **message,
4057 xmlXPathContextPtr xpath_ctx) {
4058 isds_error err = IE_SUCCESS;
4059 xmlNodePtr message_node;
4061 if (!context) return IE_INVALID_CONTEXT;
4062 if (!message) return IE_INVAL;
4063 isds_message_free(message);
4064 if (!xpath_ctx) return IE_INVAL;
4067 *message = calloc(1, sizeof(**message));
4068 if (!*message) {
4069 err = IE_NOMEM;
4070 goto leave;
4073 /* Save message XPATH context node */
4074 message_node = xpath_ctx->node;
4077 /* Extract dmDM */
4078 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
4079 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
4080 if (err) { err = IE_ERROR; goto leave; }
4081 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
4082 if (err) goto leave;
4084 if (include_documents) {
4085 struct isds_list *item;
4087 /* Extract dmFiles */
4088 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
4089 xpath_ctx);
4090 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4091 err = IE_ISDS; goto leave;
4093 if (err) { err = IE_ERROR; goto leave; }
4094 err = extract_documents(context, &((*message)->documents), xpath_ctx);
4095 if (err) goto leave;
4097 /* Store xmlDoc of this message if needed */
4098 /* Only if we got a XML document in all the documents. */
4099 for (item = (*message)->documents; item; item = item->next) {
4100 if (item->data && ((struct isds_document *)item->data)->is_xml) {
4101 (*message)->xml = xpath_ctx->doc;
4102 break;
4108 /* Restore context to message */
4109 xpath_ctx->node = message_node;
4111 /* Extract dmHash */
4112 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
4113 xpath_ctx);
4114 if (err) goto leave;
4116 /* Extract dmQTimestamp, */
4117 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
4118 xpath_ctx);
4119 if (err) goto leave;
4121 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4122 * dmAcceptanceTime. */
4123 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
4124 if (err) goto leave;
4126 /* Get message type */
4127 err = append_message_type(context, &((*message)->envelope), xpath_ctx);
4128 if (err) goto leave;
4130 leave:
4131 if (err) isds_message_free(message);
4132 return err;
4136 /* Extract message event into reallocated isds_event structure
4137 * @context is ISDS context
4138 * @event is automatically reallocated message event structure
4139 * @xpath_ctx is XPath context with current node as isds:dmEvent
4140 * In case of error @event will be freed. */
4141 static isds_error extract_event(struct isds_ctx *context,
4142 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
4143 isds_error err = IE_SUCCESS;
4144 xmlXPathObjectPtr result = NULL;
4145 xmlNodePtr event_node = xpath_ctx->node;
4146 char *string = NULL;
4148 if (!context) return IE_INVALID_CONTEXT;
4149 if (!event) return IE_INVAL;
4150 isds_event_free(event);
4151 if (!xpath_ctx) return IE_INVAL;
4153 *event = calloc(1, sizeof(**event));
4154 if (!*event) {
4155 err = IE_NOMEM;
4156 goto leave;
4159 /* Extract event data.
4160 * All elements are optional according XSD. That's funny. */
4161 EXTRACT_STRING("sisds:dmEventTime", string);
4162 if (string) {
4163 err = timestring2timeval((xmlChar *) string, &((*event)->time));
4164 if (err) {
4165 char *string_locale = _isds_utf82locale(string);
4166 if (err == IE_DATE) err = IE_ISDS;
4167 isds_printf_message(context,
4168 _("Could not convert dmEventTime as ISO time: %s"),
4169 string_locale);
4170 free(string_locale);
4171 goto leave;
4173 zfree(string);
4176 /* dmEventDescr element has prefix and the rest */
4177 EXTRACT_STRING("sisds:dmEventDescr", string);
4178 if (string) {
4179 err = eventstring2event((xmlChar *) string, *event);
4180 if (err) goto leave;
4181 zfree(string);
4184 leave:
4185 if (err) isds_event_free(event);
4186 free(string);
4187 xmlXPathFreeObject(result);
4188 xpath_ctx->node = event_node;
4189 return err;
4193 /* Convert element of XSD tEventsArray type from XML tree into
4194 * isds_list of isds_event's structure. The list is automatically reallocated.
4195 * @context is ISDS context
4196 * @events is automatically reallocated list of event structures
4197 * @xpath_ctx is XPath context with current node as tEventsArray
4198 * In case of error @events will be freed. */
4199 static isds_error extract_events(struct isds_ctx *context,
4200 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
4201 isds_error err = IE_SUCCESS;
4202 xmlXPathObjectPtr result = NULL;
4203 xmlNodePtr events_node = xpath_ctx->node;
4204 struct isds_list *event, *prev_event = NULL;
4206 if (!context) return IE_INVALID_CONTEXT;
4207 if (!events) return IE_INVAL;
4208 if (!xpath_ctx) return IE_INVAL;
4210 /* Free old list */
4211 isds_list_free(events);
4213 /* Find events */
4214 result = xmlXPathEvalExpression(BAD_CAST "sisds:dmEvent", xpath_ctx);
4215 if (!result) {
4216 err = IE_XML;
4217 goto leave;
4220 /* No match */
4221 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4222 isds_printf_message(context,
4223 _("Delivery info does not contain any event"));
4224 err = IE_ISDS;
4225 goto leave;
4229 /* Iterate over events */
4230 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4232 /* Allocate and append list item */
4233 event = calloc(1, sizeof(*event));
4234 if (!event) {
4235 err = IE_NOMEM;
4236 goto leave;
4238 event->destructor = (void (*)(void **))isds_event_free;
4239 if (i == 0) *events = event;
4240 else prev_event->next = event;
4241 prev_event = event;
4243 /* Extract event */
4244 xpath_ctx->node = result->nodesetval->nodeTab[i];
4245 err = extract_event(context,
4246 (struct isds_event **) &(event->data), xpath_ctx);
4247 if (err) goto leave;
4251 leave:
4252 if (err) isds_list_free(events);
4253 xmlXPathFreeObject(result);
4254 xpath_ctx->node = events_node;
4255 return err;
4259 #if HAVE_LIBCURL
4260 /* Insert Base64 encoded data as element with text child.
4261 * @context is session context
4262 * @parent is XML node to append @element with @data as child
4263 * @ns is XML namespace of @element, use NULL to inherit from @parent
4264 * @element is UTF-8 encoded name of new element
4265 * @data is bit stream to encode into @element
4266 * @length is size of @data in bytes
4267 * @return standard error code and fill long error message if needed */
4268 static isds_error insert_base64_encoded_string(struct isds_ctx *context,
4269 xmlNodePtr parent, const xmlNsPtr ns, const char *element,
4270 const void *data, size_t length) {
4271 isds_error err = IE_SUCCESS;
4272 xmlNodePtr node;
4274 if (!context) return IE_INVALID_CONTEXT;
4275 if (!data && length > 0) return IE_INVAL;
4276 if (!parent || !element) return IE_INVAL;
4278 xmlChar *base64data = NULL;
4279 base64data = (xmlChar *) _isds_b64encode(data, length);
4280 if (!base64data) {
4281 isds_printf_message(context,
4282 ngettext("Not enough memory to encode %zd byte into Base64",
4283 "Not enough memory to encode %zd bytes into Base64",
4284 length),
4285 length);
4286 err = IE_NOMEM;
4287 goto leave;
4289 INSERT_STRING_WITH_NS(parent, ns, element, base64data);
4291 leave:
4292 free(base64data);
4293 return err;
4297 /* Convert isds_document structure into XML tree and append to dmFiles node.
4298 * @context is session context
4299 * @document is ISDS document
4300 * @dm_files is XML element the resulting tree will be appended to as a child.
4301 * @return error code, in case of error context' message is filled. */
4302 static isds_error insert_document(struct isds_ctx *context,
4303 struct isds_document *document, xmlNodePtr dm_files) {
4304 isds_error err = IE_SUCCESS;
4305 xmlNodePtr new_file = NULL, file = NULL, node;
4306 xmlAttrPtr attribute_node;
4308 if (!context) return IE_INVALID_CONTEXT;
4309 if (!document || !dm_files) return IE_INVAL;
4311 /* Allocate new dmFile */
4312 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
4313 if (!new_file) {
4314 isds_printf_message(context, _("Could not allocate main dmFile"));
4315 err = IE_ERROR;
4316 goto leave;
4318 /* Append the new dmFile.
4319 * XXX: Main document must go first */
4320 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
4321 file = xmlAddPrevSibling(dm_files->children, new_file);
4322 else
4323 file = xmlAddChild(dm_files, new_file);
4325 if (!file) {
4326 xmlFreeNode(new_file); new_file = NULL;
4327 isds_printf_message(context, _("Could not add dmFile child to "
4328 "%s element"), dm_files->name);
4329 err = IE_ERROR;
4330 goto leave;
4333 /* @dmMimeType is required */
4334 if (!document->dmMimeType) {
4335 isds_log_message(context,
4336 _("Document is missing mandatory MIME type definition"));
4337 err = IE_INVAL;
4338 goto leave;
4340 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
4342 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
4343 if (!string) {
4344 isds_printf_message(context,
4345 _("Document has unknown dmFileMetaType: %ld"),
4346 document->dmFileMetaType);
4347 err = IE_ENUM;
4348 goto leave;
4350 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
4352 if (document->dmFileGuid) {
4353 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
4355 if (document->dmUpFileGuid) {
4356 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
4359 /* @dmFileDescr is required */
4360 if (!document->dmFileDescr) {
4361 isds_log_message(context,
4362 _("Document is missing mandatory description (title)"));
4363 err = IE_INVAL;
4364 goto leave;
4366 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
4368 if (document->dmFormat) {
4369 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
4373 /* Insert content (body) of the document. */
4374 if (document->is_xml) {
4375 /* XML document requested */
4377 /* Allocate new dmXMLContent */
4378 xmlNodePtr xmlcontent = xmlNewNode(file->ns, BAD_CAST "dmXMLContent");
4379 if (!xmlcontent) {
4380 isds_printf_message(context,
4381 _("Could not allocate dmXMLContent element"));
4382 err = IE_ERROR;
4383 goto leave;
4385 /* Append it */
4386 node = xmlAddChild(file, xmlcontent);
4387 if (!node) {
4388 xmlFreeNode(xmlcontent); xmlcontent = NULL;
4389 isds_printf_message(context,
4390 _("Could not add dmXMLContent child to %s element"),
4391 file->name);
4392 err = IE_ERROR;
4393 goto leave;
4396 /* Copy non-empty node list */
4397 if (document->xml_node_list) {
4398 xmlNodePtr content = xmlDocCopyNodeList(node->doc,
4399 document->xml_node_list);
4400 if (!content) {
4401 isds_printf_message(context,
4402 _("Not enough memory to copy XML document"));
4403 err = IE_NOMEM;
4404 goto leave;
4407 if (!xmlAddChildList(node, content)) {
4408 xmlFreeNodeList(content);
4409 isds_printf_message(context,
4410 _("Error while adding XML document into dmXMLContent"));
4411 err = IE_XML;
4412 goto leave;
4414 /* XXX: We cannot free the content here because it's part of node's
4415 * document since now. It will be freed with it automatically. */
4417 } else {
4418 /* Binary document requested */
4419 err = insert_base64_encoded_string(context, file, NULL, "dmEncodedContent",
4420 document->data, document->data_length);
4421 if (err) goto leave;
4424 leave:
4425 return err;
4429 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4430 * The copy must be preallocated, the date are just appended into structure.
4431 * @context is ISDS context
4432 * @copy is message copy structure
4433 * @xpath_ctx is XPath context with current node as tMStatus */
4434 static isds_error append_TMStatus(struct isds_ctx *context,
4435 struct isds_message_copy *copy, xmlXPathContextPtr xpath_ctx) {
4436 isds_error err = IE_SUCCESS;
4437 xmlXPathObjectPtr result = NULL;
4438 char *code = NULL, *message = NULL;
4440 if (!context) return IE_INVALID_CONTEXT;
4441 if (!copy || !xpath_ctx) return IE_INVAL;
4443 /* Free old values */
4444 zfree(copy->dmStatus);
4445 zfree(copy->dmID);
4447 /* Get error specific to this copy */
4448 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code);
4449 if (!code) {
4450 isds_log_message(context,
4451 _("Missing isds:dmStatusCode under "
4452 "XSD:tMStatus type element"));
4453 err = IE_ISDS;
4454 goto leave;
4457 if (xmlStrcmp((const xmlChar *)code, BAD_CAST "0000")) {
4458 /* This copy failed */
4459 copy->error = IE_ISDS;
4460 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message);
4461 if (message) {
4462 copy->dmStatus = _isds_astrcat3(code, ": ", message);
4463 if (!copy->dmStatus) {
4464 copy->dmStatus = code;
4465 code = NULL;
4467 } else {
4468 copy->dmStatus = code;
4469 code = NULL;
4471 } else {
4472 /* This copy succeeded. In this case only, message ID is valid */
4473 copy->error = IE_SUCCESS;
4475 EXTRACT_STRING("isds:dmID", copy->dmID);
4476 if (!copy->dmID) {
4477 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
4478 "but did not returned assigned message ID\n"));
4479 err = IE_ISDS;
4483 leave:
4484 free(code);
4485 free(message);
4486 xmlXPathFreeObject(result);
4487 return err;
4491 /* Insert struct isds_approval data (box approval) into XML tree
4492 * @context is session context
4493 * @approval is libisds structure with approval description. NULL is
4494 * acceptable.
4495 * @parent is XML element to append @approval to */
4496 static isds_error insert_GExtApproval(struct isds_ctx *context,
4497 const struct isds_approval *approval, xmlNodePtr parent) {
4499 isds_error err = IE_SUCCESS;
4500 xmlNodePtr node;
4502 if (!context) return IE_INVALID_CONTEXT;
4503 if (!parent) return IE_INVAL;
4505 if (!approval) return IE_SUCCESS;
4507 /* Build XSD:gExtApproval */
4508 INSERT_SCALAR_BOOLEAN(parent, "dbApproved", approval->approved);
4509 INSERT_STRING(parent, "dbExternRefNumber", approval->refference);
4511 leave:
4512 return err;
4516 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4517 * code
4518 * @context is session context
4519 * @service_name is name of SERVICE_DB_ACCESS
4520 * @response is server SOAP body response as XML document
4521 * @raw_response is automatically reallocated bit stream with response body. Use
4522 * NULL if you don't care
4523 * @raw_response_length is size of @raw_response in bytes
4524 * @code is ISDS status code
4525 * @status_message is ISDS status message
4526 * @return error coded from lower layer, context message will be set up
4527 * appropriately. */
4528 static isds_error build_send_check_dbdummy_request(struct isds_ctx *context,
4529 const xmlChar *service_name,
4530 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
4531 xmlChar **code, xmlChar **status_message) {
4533 isds_error err = IE_SUCCESS;
4534 char *service_name_locale = NULL;
4535 xmlNodePtr request = NULL, node;
4536 xmlNsPtr isds_ns = NULL;
4538 if (!context) return IE_INVALID_CONTEXT;
4539 if (!service_name) return IE_INVAL;
4540 if (!response || !code || !status_message) return IE_INVAL;
4541 if (!raw_response_length && raw_response) return IE_INVAL;
4543 /* Free output argument */
4544 xmlFreeDoc(*response); *response = NULL;
4545 if (raw_response) zfree(*raw_response);
4546 free(*code);
4547 free(*status_message);
4550 /* Check if connection is established
4551 * TODO: This check should be done downstairs. */
4552 if (!context->curl) return IE_CONNECTION_CLOSED;
4554 service_name_locale = _isds_utf82locale((char*)service_name);
4555 if (!service_name_locale) {
4556 err = IE_NOMEM;
4557 goto leave;
4560 /* Build request */
4561 request = xmlNewNode(NULL, service_name);
4562 if (!request) {
4563 isds_printf_message(context,
4564 _("Could not build %s request"), service_name_locale);
4565 err = IE_ERROR;
4566 goto leave;
4568 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4569 if(!isds_ns) {
4570 isds_log_message(context, _("Could not create ISDS name space"));
4571 err = IE_ERROR;
4572 goto leave;
4574 xmlSetNs(request, isds_ns);
4577 /* Add XSD:tDummyInput child */
4578 INSERT_STRING(request, "dbDummy", NULL);
4581 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
4582 service_name_locale);
4584 /* Send request */
4585 err = isds(context, SERVICE_DB_ACCESS, request, response,
4586 raw_response, raw_response_length);
4587 xmlFreeNode(request); request = NULL;
4589 if (err) {
4590 isds_log(ILF_ISDS, ILL_DEBUG,
4591 _("Processing ISDS response on %s request failed\n"),
4592 service_name_locale);
4593 goto leave;
4596 /* Check for response status */
4597 err = isds_response_status(context, SERVICE_DB_ACCESS, *response,
4598 code, status_message, NULL);
4599 if (err) {
4600 isds_log(ILF_ISDS, ILL_DEBUG,
4601 _("ISDS response on %s request is missing status\n"),
4602 service_name_locale);
4603 goto leave;
4606 /* Request processed, but nothing found */
4607 if (xmlStrcmp(*code, BAD_CAST "0000")) {
4608 char *code_locale = _isds_utf82locale((char*) *code);
4609 char *status_message_locale =
4610 _isds_utf82locale((char*) *status_message);
4611 isds_log(ILF_ISDS, ILL_DEBUG,
4612 _("Server refused %s request (code=%s, message=%s)\n"),
4613 service_name_locale, code_locale, status_message_locale);
4614 isds_log_message(context, status_message_locale);
4615 free(code_locale);
4616 free(status_message_locale);
4617 err = IE_ISDS;
4618 goto leave;
4621 leave:
4622 free(service_name_locale);
4623 xmlFreeNode(request);
4624 return err;
4626 #endif
4629 /* Get data about logged in user and his box. */
4630 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
4631 struct isds_DbOwnerInfo **db_owner_info) {
4632 isds_error err = IE_SUCCESS;
4633 #if HAVE_LIBCURL
4634 xmlDocPtr response = NULL;
4635 xmlChar *code = NULL, *message = NULL;
4636 xmlXPathContextPtr xpath_ctx = NULL;
4637 xmlXPathObjectPtr result = NULL;
4638 char *string = NULL;
4639 #endif
4641 if (!context) return IE_INVALID_CONTEXT;
4642 zfree(context->long_message);
4643 if (!db_owner_info) return IE_INVAL;
4644 isds_DbOwnerInfo_free(db_owner_info);
4646 #if HAVE_LIBCURL
4647 /* Check if connection is established */
4648 if (!context->curl) return IE_CONNECTION_CLOSED;
4651 /* Do request and check for success */
4652 err = build_send_check_dbdummy_request(context,
4653 BAD_CAST "GetOwnerInfoFromLogin",
4654 &response, NULL, NULL, &code, &message);
4655 if (err) goto leave;
4658 /* Extract data */
4659 /* Prepare structure */
4660 *db_owner_info = calloc(1, sizeof(**db_owner_info));
4661 if (!*db_owner_info) {
4662 err = IE_NOMEM;
4663 goto leave;
4665 xpath_ctx = xmlXPathNewContext(response);
4666 if (!xpath_ctx) {
4667 err = IE_ERROR;
4668 goto leave;
4670 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4671 err = IE_ERROR;
4672 goto leave;
4675 /* Set context node */
4676 result = xmlXPathEvalExpression(BAD_CAST
4677 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
4678 if (!result) {
4679 err = IE_ERROR;
4680 goto leave;
4682 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4683 isds_log_message(context, _("Missing dbOwnerInfo element"));
4684 err = IE_ISDS;
4685 goto leave;
4687 if (result->nodesetval->nodeNr > 1) {
4688 isds_log_message(context, _("Multiple dbOwnerInfo element"));
4689 err = IE_ISDS;
4690 goto leave;
4692 xpath_ctx->node = result->nodesetval->nodeTab[0];
4693 xmlXPathFreeObject(result); result = NULL;
4695 /* Extract it */
4696 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
4699 leave:
4700 if (err) {
4701 isds_DbOwnerInfo_free(db_owner_info);
4704 free(string);
4705 xmlXPathFreeObject(result);
4706 xmlXPathFreeContext(xpath_ctx);
4708 free(code);
4709 free(message);
4710 xmlFreeDoc(response);
4712 if (!err)
4713 isds_log(ILF_ISDS, ILL_DEBUG,
4714 _("GetOwnerInfoFromLogin request processed by server "
4715 "successfully.\n"));
4716 #else /* not HAVE_LIBCURL */
4717 err = IE_NOTSUP;
4718 #endif
4720 return err;
4724 /* Get data about logged in user. */
4725 isds_error isds_GetUserInfoFromLogin(struct isds_ctx *context,
4726 struct isds_DbUserInfo **db_user_info) {
4727 isds_error err = IE_SUCCESS;
4728 #if HAVE_LIBCURL
4729 xmlDocPtr response = NULL;
4730 xmlChar *code = NULL, *message = NULL;
4731 xmlXPathContextPtr xpath_ctx = NULL;
4732 xmlXPathObjectPtr result = NULL;
4733 #endif
4735 if (!context) return IE_INVALID_CONTEXT;
4736 zfree(context->long_message);
4737 if (!db_user_info) return IE_INVAL;
4738 isds_DbUserInfo_free(db_user_info);
4740 #if HAVE_LIBCURL
4741 /* Check if connection is established */
4742 if (!context->curl) return IE_CONNECTION_CLOSED;
4745 /* Do request and check for success */
4746 err = build_send_check_dbdummy_request(context,
4747 BAD_CAST "GetUserInfoFromLogin",
4748 &response, NULL, NULL, &code, &message);
4749 if (err) goto leave;
4752 /* Extract data */
4753 /* Prepare structure */
4754 *db_user_info = calloc(1, sizeof(**db_user_info));
4755 if (!*db_user_info) {
4756 err = IE_NOMEM;
4757 goto leave;
4759 xpath_ctx = xmlXPathNewContext(response);
4760 if (!xpath_ctx) {
4761 err = IE_ERROR;
4762 goto leave;
4764 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4765 err = IE_ERROR;
4766 goto leave;
4769 /* Set context node */
4770 result = xmlXPathEvalExpression(BAD_CAST
4771 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx);
4772 if (!result) {
4773 err = IE_ERROR;
4774 goto leave;
4776 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4777 isds_log_message(context, _("Missing dbUserInfo element"));
4778 err = IE_ISDS;
4779 goto leave;
4781 if (result->nodesetval->nodeNr > 1) {
4782 isds_log_message(context, _("Multiple dbUserInfo element"));
4783 err = IE_ISDS;
4784 goto leave;
4786 xpath_ctx->node = result->nodesetval->nodeTab[0];
4787 xmlXPathFreeObject(result); result = NULL;
4789 /* Extract it */
4790 err = extract_DbUserInfo(context, db_user_info, xpath_ctx);
4792 leave:
4793 if (err) {
4794 isds_DbUserInfo_free(db_user_info);
4797 xmlXPathFreeObject(result);
4798 xmlXPathFreeContext(xpath_ctx);
4800 free(code);
4801 free(message);
4802 xmlFreeDoc(response);
4804 if (!err)
4805 isds_log(ILF_ISDS, ILL_DEBUG,
4806 _("GetUserInfoFromLogin request processed by server "
4807 "successfully.\n"));
4808 #else /* not HAVE_LIBCURL */
4809 err = IE_NOTSUP;
4810 #endif
4812 return err;
4816 /* Get expiration time of current password
4817 * @context is session context
4818 * @expiration is automatically reallocated time when password expires. If
4819 * password expiration is disables, NULL will be returned. In case of error
4820 * it will be nulled too. */
4821 isds_error isds_get_password_expiration(struct isds_ctx *context,
4822 struct timeval **expiration) {
4823 isds_error err = IE_SUCCESS;
4824 #if HAVE_LIBCURL
4825 xmlDocPtr response = NULL;
4826 xmlChar *code = NULL, *message = NULL;
4827 xmlXPathContextPtr xpath_ctx = NULL;
4828 xmlXPathObjectPtr result = NULL;
4829 char *string = NULL;
4830 #endif
4832 if (!context) return IE_INVALID_CONTEXT;
4833 zfree(context->long_message);
4834 if (!expiration) return IE_INVAL;
4835 zfree(*expiration);
4837 #if HAVE_LIBCURL
4838 /* Check if connection is established */
4839 if (!context->curl) return IE_CONNECTION_CLOSED;
4842 /* Do request and check for success */
4843 err = build_send_check_dbdummy_request(context,
4844 BAD_CAST "GetPasswordInfo",
4845 &response, NULL, NULL, &code, &message);
4846 if (err) goto leave;
4849 /* Extract data */
4850 xpath_ctx = xmlXPathNewContext(response);
4851 if (!xpath_ctx) {
4852 err = IE_ERROR;
4853 goto leave;
4855 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4856 err = IE_ERROR;
4857 goto leave;
4860 /* Set context node */
4861 result = xmlXPathEvalExpression(BAD_CAST
4862 "/isds:GetPasswordInfoResponse", xpath_ctx);
4863 if (!result) {
4864 err = IE_ERROR;
4865 goto leave;
4867 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4868 isds_log_message(context,
4869 _("Missing GetPasswordInfoResponse element"));
4870 err = IE_ISDS;
4871 goto leave;
4873 if (result->nodesetval->nodeNr > 1) {
4874 isds_log_message(context,
4875 _("Multiple GetPasswordInfoResponse element"));
4876 err = IE_ISDS;
4877 goto leave;
4879 xpath_ctx->node = result->nodesetval->nodeTab[0];
4880 xmlXPathFreeObject(result); result = NULL;
4882 /* Extract expiration date */
4883 EXTRACT_STRING("isds:pswExpDate", string);
4884 if (string) {
4885 /* And convert it if any returned. Otherwise expiration is disabled. */
4886 err = timestring2timeval((xmlChar *) string, expiration);
4887 if (err) {
4888 char *string_locale = _isds_utf82locale(string);
4889 if (err == IE_DATE) err = IE_ISDS;
4890 isds_printf_message(context,
4891 _("Could not convert pswExpDate as ISO time: %s"),
4892 string_locale);
4893 free(string_locale);
4894 goto leave;
4898 leave:
4899 if (err) {
4900 if (*expiration) {
4901 zfree(*expiration);
4905 free(string);
4906 xmlXPathFreeObject(result);
4907 xmlXPathFreeContext(xpath_ctx);
4909 free(code);
4910 free(message);
4911 xmlFreeDoc(response);
4913 if (!err)
4914 isds_log(ILF_ISDS, ILL_DEBUG,
4915 _("GetPasswordInfo request processed by server "
4916 "successfully.\n"));
4917 #else /* not HAVE_LIBCURL */
4918 err = IE_NOTSUP;
4919 #endif
4921 return err;
4925 #if HAVE_LIBCURL
4926 /* Request delivering new TOTP code from ISDS through side channel before
4927 * changing password.
4928 * @context is session context
4929 * @password is current password.
4930 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
4931 * Please note the @otp argument must have TOTP OTP method. See isds_login()
4932 * function for more details.
4933 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
4934 * error code. */
4935 static isds_error _isds_request_totp_code(struct isds_ctx *context,
4936 const char *password, struct isds_otp *otp) {
4937 isds_error err = IE_SUCCESS;
4938 char *saved_url = NULL; /* No copy */
4939 xmlNsPtr isds_ns = NULL;
4940 xmlNodePtr request = NULL;
4941 xmlDocPtr response = NULL;
4942 xmlChar *code = NULL, *message = NULL;
4943 const xmlChar *codes[] = {
4944 BAD_CAST "2300",
4945 BAD_CAST "2301",
4946 BAD_CAST "2302"
4948 const char *meanings[] = {
4949 N_("Unexpected error"),
4950 N_("One-time code cannot be re-send faster than once a 30 seconds"),
4951 N_("One-time code could not been sent. Try later again.")
4954 if (NULL == context) return IE_INVALID_CONTEXT;
4955 zfree(context->long_message);
4956 if (NULL == password) return IE_INVAL;
4958 /* Check if connection is established
4959 * TODO: This check should be done downstairs. */
4960 if (!context->curl) return IE_CONNECTION_CLOSED;
4962 if (NULL == context->otp) {
4963 isds_log_message(context, _("This function requires OTP-authenticated "
4964 "context"));
4965 return IE_INVALID_CONTEXT;
4967 if (NULL == otp) {
4968 isds_log_message(context, _("If one-time password authentication "
4969 "method is in use, requesting new OTP code requires "
4970 "one-time credentials argument either"));
4971 return IE_INVAL;
4973 if (otp->method != OTP_TIME) {
4974 isds_log_message(context, _("Requesting new time-based OTP code from "
4975 "server requires one-time password authentication "
4976 "method"));
4977 return IE_INVAL;
4979 if (context->otp->otp_code != NULL) {
4980 isds_log_message(context, _("Requesting new time-based OTP code from "
4981 "server requires undefined OTP code member in "
4982 "one-time credentials argument"));
4983 return IE_INVAL;
4987 /* Build request */
4988 request = xmlNewNode(NULL, BAD_CAST "SendSMSCode");
4989 if (!request) {
4990 isds_log_message(context, _("Could not build SendSMSCode request"));
4991 return IE_ERROR;
4993 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4994 if(!isds_ns) {
4995 isds_log_message(context, _("Could not create ISDS name space"));
4996 xmlFreeNode(request);
4997 return IE_ERROR;
4999 xmlSetNs(request, isds_ns);
5001 /* Change URL temporarily for sending this request only */
5003 char *new_url = NULL;
5004 if ((err = _isds_build_url_from_context(context,
5005 "%1$.*2$sasws/changePassword", &new_url))) {
5006 goto leave;
5008 saved_url = context->url;
5009 context->url = new_url;
5012 /* Store credentials for sending this request only */
5013 context->otp = otp;
5014 _isds_discard_credentials(context, 0);
5015 if ((err = _isds_store_credentials(context, context->saved_username,
5016 password, NULL))) {
5017 _isds_discard_credentials(context, 0);
5018 goto leave;
5021 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending SendSMSCode request to ISDS\n"));
5023 /* Sent request */
5024 err = isds(context, SERVICE_ASWS, request, &response, NULL, NULL);
5026 if (otp) {
5027 /* Remove temporal credentials */
5028 _isds_discard_credentials(context, 0);
5029 /* Keep context->otp to keep signaling this is OTP session */
5032 /* Destroy request */
5033 xmlFreeNode(request); request = NULL;
5035 if (err) {
5036 isds_log(ILF_ISDS, ILL_DEBUG,
5037 _("Processing ISDS response on SendSMSCode request failed\n"));
5038 goto leave;
5041 /* Check for response status */
5042 err = isds_response_status(context, SERVICE_ASWS, response,
5043 &code, &message, NULL);
5044 if (err) {
5045 isds_log(ILF_ISDS, ILL_DEBUG,
5046 _("ISDS response on SendSMSCode request is missing "
5047 "status\n"));
5048 goto leave;
5051 /* Check for error */
5052 if (xmlStrcmp(code, BAD_CAST "0000")) {
5053 char *code_locale = _isds_utf82locale((char*)code);
5054 char *message_locale = _isds_utf82locale((char*)message);
5055 int i;
5056 isds_log(ILF_ISDS, ILL_DEBUG,
5057 _("Server refused to send new code on SendSMSCode "
5058 "request (code=%s, message=%s)\n"),
5059 code_locale, message_locale);
5061 /* Check for known error codes */
5062 for (i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
5063 if (!xmlStrcmp(code, codes[i])) break;
5065 if (i < sizeof(codes)/sizeof(*codes))
5066 isds_log_message(context, _(meanings[i]));
5067 else
5068 isds_log_message(context, message_locale);
5070 free(code_locale);
5071 free(message_locale);
5073 err = IE_ISDS;
5074 goto leave;
5077 /* Otherwise new code sent successfully */
5079 leave:
5080 if (NULL != saved_url) {
5081 /* Revert URL to original one */
5082 zfree(context->url);
5083 context->url = saved_url;
5086 free(code);
5087 free(message);
5088 xmlFreeDoc(response);
5089 xmlFreeNode(request);
5091 if (!err)
5092 isds_log(ILF_ISDS, ILL_DEBUG,
5093 _("New OTP code has been sent successfully on SendSMSCode "
5094 "request.\n"));
5095 return err;
5097 #endif
5100 /* Change user password in ISDS.
5101 * User must supply old password, new password will takes effect after some
5102 * time, current session can continue. Password must fulfill some constraints.
5103 * @context is session context
5104 * @old_password is current password.
5105 * @new_password is requested new password
5106 * @otp auxiliary data required if one-time password authentication is in use,
5107 * defines OTP code (if known) and returns fine grade resolution of OTP
5108 * procedure. Pass NULL, if one-time password authentication is not needed.
5109 * Please note the @otp argument must match OTP method used at log-in time. See
5110 * isds_login() function for more details.
5111 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5112 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5113 * awaiting OTP code that has been delivered by side channel to the user. */
5114 isds_error isds_change_password(struct isds_ctx *context,
5115 const char *old_password, const char *new_password,
5116 struct isds_otp *otp) {
5117 isds_error err = IE_SUCCESS;
5118 #if HAVE_LIBCURL
5119 char *saved_url = NULL; /* No copy */
5120 xmlNsPtr isds_ns = NULL;
5121 xmlNodePtr request = NULL, node;
5122 xmlDocPtr response = NULL;
5123 xmlChar *code = NULL, *message = NULL;
5124 const xmlChar *codes[] = {
5125 BAD_CAST "1066",
5126 BAD_CAST "1067",
5127 BAD_CAST "1079",
5128 BAD_CAST "1080",
5129 BAD_CAST "1081",
5130 BAD_CAST "1082",
5131 BAD_CAST "1083",
5132 BAD_CAST "1090",
5133 BAD_CAST "1091",
5134 BAD_CAST "2300",
5135 BAD_CAST "9204"
5137 const char *meanings[] = {
5138 N_("Password length must be between 8 and 32 characters"),
5139 N_("New password must differ from the current one"),
5140 N_("Password contains forbidden character"),
5141 N_("Password must contain at least one upper-case letter, "
5142 "one lower-case, and one digit"),
5143 N_("Password cannot contain sequence of three identical characters"),
5144 N_("Password cannot contain user identifier"),
5145 N_("Password is too simmple"),
5146 N_("Old password is not valid"),
5147 N_("Passwords cannot be reused"),
5148 N_("Unexpected error"),
5149 N_("LDAP update error")
5151 #endif
5153 if (!context) return IE_INVALID_CONTEXT;
5154 zfree(context->long_message);
5155 if (!old_password || !new_password) return IE_INVAL;
5157 #if HAVE_LIBCURL
5158 /* Check if connection is established
5159 * TODO: This check should be done downstairs. */
5160 if (!context->curl) return IE_CONNECTION_CLOSED;
5162 if (NULL != context->otp && NULL == otp) {
5163 isds_log_message(context, _("If one-time password authentication "
5164 "method is in use, changing password requires one-time "
5165 "credentials either"));
5166 return IE_INVAL;
5169 /* Build ChangeISDSPassword request */
5170 request = xmlNewNode(NULL, (NULL == otp) ? BAD_CAST "ChangeISDSPassword" :
5171 BAD_CAST "ChangePasswordOTP");
5172 if (!request) {
5173 isds_log_message(context, (NULL == otp) ?
5174 _("Could not build ChangeISDSPassword request") :
5175 _("Could not build ChangePasswordOTP request"));
5176 return IE_ERROR;
5178 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5179 if(!isds_ns) {
5180 isds_log_message(context, _("Could not create ISDS name space"));
5181 xmlFreeNode(request);
5182 return IE_ERROR;
5184 xmlSetNs(request, isds_ns);
5186 INSERT_STRING(request, "dbOldPassword", old_password);
5187 INSERT_STRING(request, "dbNewPassword", new_password);
5189 if (NULL != otp) {
5190 switch (otp->method) {
5191 case OTP_HMAC:
5192 isds_log(ILF_SEC, ILL_INFO,
5193 _("Selected authentication method: "
5194 "HMAC-based one-time password\n"));
5195 INSERT_STRING(request, "dbOTPType", BAD_CAST "HOTP");
5196 break;
5197 case OTP_TIME:
5198 isds_log(ILF_SEC, ILL_INFO,
5199 _("Selected authentication method: "
5200 "Time-based one-time password\n"));
5201 INSERT_STRING(request, "dbOTPType", BAD_CAST "TOTP");
5202 if (context->otp->otp_code == NULL) {
5203 isds_log(ILF_SEC, ILL_INFO,
5204 _("OTP code has not been provided by "
5205 "application, requesting server for "
5206 "new one.\n"));
5207 err = _isds_request_totp_code(context, old_password, otp);
5208 if (err == IE_SUCCESS) err = IE_PARTIAL_SUCCESS;
5209 goto leave;
5211 } else {
5212 isds_log(ILF_SEC, ILL_INFO,
5213 _("OTP code has been provided by "
5214 "application, not requesting server "
5215 "for new one.\n"));
5217 break;
5218 default:
5219 isds_log_message(context,
5220 _("Unknown one-time password authentication "
5221 "method requested by application"));
5222 err = IE_ENUM;
5223 goto leave;
5226 /* Change URL temporarily for sending this request only */
5228 char *new_url = NULL;
5229 if ((err = _isds_build_url_from_context(context,
5230 "%1$.*2$sasws/changePassword", &new_url))) {
5231 goto leave;
5233 saved_url = context->url;
5234 context->url = new_url;
5237 /* Store credentials for sending this request only */
5238 context->otp = otp;
5239 _isds_discard_credentials(context, 0);
5240 if ((err = _isds_store_credentials(context, context->saved_username,
5241 old_password, NULL))) {
5242 _isds_discard_credentials(context, 0);
5243 goto leave;
5248 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5249 _("Sending ChangeISDSPassword request to ISDS\n") :
5250 _("Sending ChangePasswordOTP request to ISDS\n"));
5252 /* Sent request */
5253 err = isds(context, (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS,
5254 request, &response, NULL, NULL);
5256 if (otp) {
5257 /* Remove temporal credentials */
5258 _isds_discard_credentials(context, 0);
5259 /* Keep context->otp to keep signaling this is OTP session */
5262 /* Destroy request */
5263 xmlFreeNode(request); request = NULL;
5265 if (err) {
5266 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5267 _("Processing ISDS response on ChangeISDSPassword "
5268 "request failed\n") :
5269 _("Processing ISDS response on ChangePasswordOTP "
5270 "request failed\n"));
5271 goto leave;
5274 /* Check for response status */
5275 err = isds_response_status(context,
5276 (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS, response,
5277 &code, &message, NULL);
5278 if (err) {
5279 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5280 _("ISDS response on ChangeISDSPassword request is missing "
5281 "status\n") :
5282 _("ISDS response on ChangePasswordOTP request is missing "
5283 "status\n"));
5284 goto leave;
5287 /* Check for known error codes */
5288 for (int i=0; i < sizeof(codes)/sizeof(*codes); i++) {
5289 if (!xmlStrcmp(code, codes[i])) {
5290 char *code_locale = _isds_utf82locale((char*)code);
5291 char *message_locale = _isds_utf82locale((char*)message);
5292 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5293 _("Server refused to change password on ChangeISDSPassword "
5294 "request (code=%s, message=%s)\n") :
5295 _("Server refused to change password on ChangePasswordOTP "
5296 "request (code=%s, message=%s)\n"),
5297 code_locale, message_locale);
5298 free(code_locale);
5299 free(message_locale);
5300 isds_log_message(context, _(meanings[i]));
5301 err = IE_INVAL;
5302 goto leave;
5306 /* Other error */
5307 if (xmlStrcmp(code, BAD_CAST "0000")) {
5308 char *code_locale = _isds_utf82locale((char*)code);
5309 char *message_locale = _isds_utf82locale((char*)message);
5310 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5311 _("Server refused to change password on ChangeISDSPassword "
5312 "request (code=%s, message=%s)\n") :
5313 _("Server refused to change password on ChangePasswordOTP "
5314 "request (code=%s, message=%s)\n"),
5315 code_locale, message_locale);
5316 isds_log_message(context, message_locale);
5317 free(code_locale);
5318 free(message_locale);
5319 err = IE_ISDS;
5320 goto leave;
5323 /* Otherwise password changed successfully */
5325 leave:
5326 if (NULL != saved_url) {
5327 /* Revert URL to original one */
5328 zfree(context->url);
5329 context->url = saved_url;
5332 free(code);
5333 free(message);
5334 xmlFreeDoc(response);
5335 xmlFreeNode(request);
5337 if (!err)
5338 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5339 _("Password changed successfully on ChangeISDSPassword "
5340 "request.\n") :
5341 _("Password changed successfully on ChangePasswordOTP "
5342 "request.\n"));
5343 #else /* not HAVE_LIBCURL */
5344 err = IE_NOTSUP;
5345 #endif
5347 return err;
5351 #if HAVE_LIBCURL
5352 /* Generic middle part with request sending and response check.
5353 * It sends prepared request and checks for error code.
5354 * @context is ISDS session context.
5355 * @service is ISDS service handler
5356 * @service_name is name in scope of given @service
5357 * @request is XML tree with request. Will be freed to save memory.
5358 * @response is XML document outputting ISDS response.
5359 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5360 * NULL, if you don't care. */
5361 static isds_error send_destroy_request_check_response(
5362 struct isds_ctx *context,
5363 const isds_service service, const xmlChar *service_name,
5364 xmlNodePtr *request, xmlDocPtr *response, xmlChar **refnumber) {
5365 isds_error err = IE_SUCCESS;
5366 char *service_name_locale = NULL;
5367 xmlChar *code = NULL, *message = NULL;
5370 if (!context) return IE_INVALID_CONTEXT;
5371 if (!service_name || *service_name == '\0' || !request || !*request ||
5372 !response)
5373 return IE_INVAL;
5375 /* Check if connection is established
5376 * TODO: This check should be done downstairs. */
5377 if (!context->curl) return IE_CONNECTION_CLOSED;
5379 service_name_locale = _isds_utf82locale((char*) service_name);
5380 if (!service_name_locale) {
5381 err = IE_NOMEM;
5382 goto leave;
5385 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
5386 service_name_locale);
5388 /* Send request */
5389 err = isds(context, service, *request, response, NULL, NULL);
5390 xmlFreeNode(*request); *request = NULL;
5392 if (err) {
5393 isds_log(ILF_ISDS, ILL_DEBUG,
5394 _("Processing ISDS response on %s request failed\n"),
5395 service_name_locale);
5396 goto leave;
5399 /* Check for response status */
5400 err = isds_response_status(context, service, *response,
5401 &code, &message, refnumber);
5402 if (err) {
5403 isds_log(ILF_ISDS, ILL_DEBUG,
5404 _("ISDS response on %s request is missing status\n"),
5405 service_name_locale);
5406 goto leave;
5409 /* Request processed, but server failed */
5410 if (xmlStrcmp(code, BAD_CAST "0000")) {
5411 char *code_locale = _isds_utf82locale((char*) code);
5412 char *message_locale = _isds_utf82locale((char*) message);
5413 isds_log(ILF_ISDS, ILL_DEBUG,
5414 _("Server refused %s request (code=%s, message=%s)\n"),
5415 service_name_locale, code_locale, message_locale);
5416 isds_log_message(context, message_locale);
5417 free(code_locale);
5418 free(message_locale);
5419 err = IE_ISDS;
5420 goto leave;
5424 leave:
5425 free(code);
5426 free(message);
5427 if (err && *response) {
5428 xmlFreeDoc(*response);
5429 *response = NULL;
5431 if (*request) {
5432 xmlFreeNode(*request);
5433 *request = NULL;
5435 free(service_name_locale);
5437 return err;
5441 /* Generic bottom half with request sending.
5442 * It sends prepared request, checks for error code, destroys response and
5443 * request and log success or failure.
5444 * @context is ISDS session context.
5445 * @service is ISDS service handler
5446 * @service_name is name in scope of given @service
5447 * @request is XML tree with request. Will be freed to save memory.
5448 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5449 * NULL, if you don't care. */
5450 static isds_error send_request_check_drop_response(
5451 struct isds_ctx *context,
5452 const isds_service service, const xmlChar *service_name,
5453 xmlNodePtr *request, xmlChar **refnumber) {
5454 isds_error err = IE_SUCCESS;
5455 xmlDocPtr response = NULL;
5458 if (!context) return IE_INVALID_CONTEXT;
5459 if (!service_name || *service_name == '\0' || !request || !*request)
5460 return IE_INVAL;
5462 /* Send request and check response*/
5463 err = send_destroy_request_check_response(context,
5464 service, service_name, request, &response, refnumber);
5466 xmlFreeDoc(response);
5468 if (*request) {
5469 xmlFreeNode(*request);
5470 *request = NULL;
5473 if (!err) {
5474 char *service_name_locale = _isds_utf82locale((char *) service_name);
5475 isds_log(ILF_ISDS, ILL_DEBUG,
5476 _("%s request processed by server successfully.\n"),
5477 service_name_locale);
5478 free(service_name_locale);
5481 return err;
5485 /* Insert isds_credentials_delivery structure into XML request if not NULL
5486 * @context is session context
5487 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
5488 * credentials delivery. The email field is passed.
5489 * @parent is XML element where to insert */
5490 static isds_error insert_credentials_delivery(struct isds_ctx *context,
5491 const struct isds_credentials_delivery *credentials_delivery,
5492 xmlNodePtr parent) {
5493 isds_error err = IE_SUCCESS;
5494 xmlNodePtr node;
5496 if (!context) return IE_INVALID_CONTEXT;
5497 if (!parent) return IE_INVAL;
5499 if (credentials_delivery) {
5500 /* Following elements are valid only for services:
5501 * NewAccessData, AddDataBoxUser, CreateDataBox */
5502 INSERT_SCALAR_BOOLEAN(parent, "dbVirtual", 1);
5503 INSERT_STRING(parent, "email", credentials_delivery->email);
5506 leave:
5507 return err;
5511 /* Extract credentials delivery from ISDS response.
5512 * @context is session context
5513 * @credentials_delivery is pointer to valid structure to fill in returned
5514 * user's password (and new log-in name). If NULL, do not extract the data.
5515 * @response is pointer to XML document with ISDS response
5516 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
5517 * @return IE_SUCCESS even if new user name has not been found because it's not
5518 * clear whether it's returned always. */
5519 static isds_error extract_credentials_delivery(struct isds_ctx *context,
5520 struct isds_credentials_delivery *credentials_delivery,
5521 xmlDocPtr response, const char *request_name) {
5522 isds_error err = IE_SUCCESS;
5523 xmlXPathContextPtr xpath_ctx = NULL;
5524 xmlXPathObjectPtr result = NULL;
5525 char *xpath_query = NULL;
5527 if (!context) return IE_INVALID_CONTEXT;
5528 if (credentials_delivery) {
5529 zfree(credentials_delivery->token);
5530 zfree(credentials_delivery->new_user_name);
5532 if (!response || !request_name || !*request_name) return IE_INVAL;
5535 /* Extract optional token */
5536 if (credentials_delivery) {
5537 xpath_ctx = xmlXPathNewContext(response);
5538 if (!xpath_ctx) {
5539 err = IE_ERROR;
5540 goto leave;
5542 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5543 err = IE_ERROR;
5544 goto leave;
5547 /* Verify root element */
5548 if (-1 == isds_asprintf(&xpath_query, "/isds:%sResponse",
5549 request_name)) {
5550 err = IE_NOMEM;
5551 goto leave;
5553 result = xmlXPathEvalExpression(BAD_CAST xpath_query, xpath_ctx);
5554 if (!result) {
5555 err = IE_ERROR;
5556 goto leave;
5558 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5559 char *request_name_locale = _isds_utf82locale(request_name);
5560 isds_log(ILF_ISDS, ILL_WARNING,
5561 _("Wrong element in ISDS response for %s request "
5562 "while extracting credentials delivery details\n"),
5563 request_name_locale);
5564 free(request_name_locale);
5565 err = IE_ERROR;
5566 goto leave;
5568 xpath_ctx->node = result->nodesetval->nodeTab[0];
5571 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
5572 * optional. */
5573 EXTRACT_STRING("isds:dbUserID", credentials_delivery->new_user_name);
5575 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery->token);
5576 if (!credentials_delivery->token) {
5577 char *request_name_locale = _isds_utf82locale(request_name);
5578 isds_log(ILF_ISDS, ILL_ERR,
5579 _("ISDS did not return token on %s request "
5580 "even if requested\n"), request_name_locale);
5581 free(request_name_locale);
5582 err = IE_ERROR;
5586 leave:
5587 free(xpath_query);
5588 xmlXPathFreeObject(result);
5589 xmlXPathFreeContext(xpath_ctx);
5591 return err;
5595 /* Build XSD:tCreateDBInput request type for box creating.
5596 * @context is session context
5597 * @request outputs built XML tree
5598 * @service_name is request name of SERVICE_DB_MANIPULATION service
5599 * @box is box description to create including single primary user (in case of
5600 * FO box type)
5601 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
5602 * box, or contact address of PFO box owner)
5603 * @former_names is optional former name of box owner. Pass NULL if otherwise.
5604 * @upper_box_id is optional ID of supper box if currently created box is
5605 * subordinated.
5606 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
5607 * don't care.
5608 * @credentials_delivery is valid pointer if ISDS should return token that box
5609 * owner can use to obtain his new credentials in on-line way. Then valid email
5610 * member value should be supplied.
5611 * @approval is optional external approval of box manipulation */
5612 static isds_error build_CreateDBInput_request(struct isds_ctx *context,
5613 xmlNodePtr *request, const xmlChar *service_name,
5614 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
5615 const xmlChar *former_names, const xmlChar *upper_box_id,
5616 const xmlChar *ceo_label,
5617 const struct isds_credentials_delivery *credentials_delivery,
5618 const struct isds_approval *approval) {
5619 isds_error err = IE_SUCCESS;
5620 xmlNsPtr isds_ns = NULL;
5621 xmlNodePtr node, dbPrimaryUsers;
5622 xmlChar *string = NULL;
5623 const struct isds_list *item;
5626 if (!context) return IE_INVALID_CONTEXT;
5627 if (!request || !service_name || service_name[0] == '\0' || !box)
5628 return IE_INVAL;
5631 /* Build CreateDataBox-similar request */
5632 *request = xmlNewNode(NULL, service_name);
5633 if (!*request) {
5634 char *service_name_locale = _isds_utf82locale((char*) service_name);
5635 isds_printf_message(context, _("Could build %s request"),
5636 service_name_locale);
5637 free(service_name_locale);
5638 return IE_ERROR;
5640 if (context->type == CTX_TYPE_TESTING_REQUEST_COLLECTOR) {
5641 isds_ns = xmlNewNs(*request, BAD_CAST ISDS1_NS, NULL);
5642 if (!isds_ns) {
5643 isds_log_message(context, _("Could not create ISDS1 name space"));
5644 xmlFreeNode(*request);
5645 return IE_ERROR;
5647 } else {
5648 isds_ns = xmlNewNs(*request, BAD_CAST ISDS_NS, NULL);
5649 if (!isds_ns) {
5650 isds_log_message(context, _("Could not create ISDS name space"));
5651 xmlFreeNode(*request);
5652 return IE_ERROR;
5655 xmlSetNs(*request, isds_ns);
5657 INSERT_ELEMENT(node, *request, "dbOwnerInfo");
5658 err = insert_DbOwnerInfo(context, box, node);
5659 if (err) goto leave;
5661 /* Insert users */
5662 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
5663 * verbose documentation allows none dbUserInfo */
5664 INSERT_ELEMENT(dbPrimaryUsers, *request, "dbPrimaryUsers");
5665 for (item = users; item; item = item->next) {
5666 if (item->data) {
5667 INSERT_ELEMENT(node, dbPrimaryUsers, "dbUserInfo");
5668 err = insert_DbUserInfo(context,
5669 (struct isds_DbUserInfo *) item->data, node);
5670 if (err) goto leave;
5674 INSERT_STRING(*request, "dbFormerNames", former_names);
5675 INSERT_STRING(*request, "dbUpperDBId", upper_box_id);
5676 INSERT_STRING(*request, "dbCEOLabel", ceo_label);
5678 err = insert_credentials_delivery(context, credentials_delivery, *request);
5679 if (err) goto leave;
5681 err = insert_GExtApproval(context, approval, *request);
5682 if (err) goto leave;
5684 leave:
5685 if (err) {
5686 xmlFreeNode(*request);
5687 *request = NULL;
5689 free(string);
5690 return err;
5692 #endif /* HAVE_LIBCURL */
5695 /* Create new box.
5696 * @context is session context
5697 * @box is box description to create including single primary user (in case of
5698 * FO box type). It outputs box ID assigned by ISDS in dbID element.
5699 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
5700 * box, or contact address of PFO box owner)
5701 * @former_names is optional former name of box owner. Pass NULL if you don't care.
5702 * @upper_box_id is optional ID of supper box if currently created box is
5703 * subordinated.
5704 * @ceo_label is optional title of OVM box owner (e.g. mayor)
5705 * @credentials_delivery is NULL if new password should be delivered off-line
5706 * to box owner. It is valid pointer if owner should obtain new password on-line
5707 * on dedicated web server. Then input @credentials_delivery.email value is
5708 * his e-mail address he must provide to dedicated web server together
5709 * with output reallocated @credentials_delivery.token member. Output
5710 * member @credentials_delivery.new_user_name is unused up on this call.
5711 * @approval is optional external approval of box manipulation
5712 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5713 * NULL, if you don't care.*/
5714 isds_error isds_add_box(struct isds_ctx *context,
5715 struct isds_DbOwnerInfo *box, const struct isds_list *users,
5716 const char *former_names, const char *upper_box_id,
5717 const char *ceo_label,
5718 struct isds_credentials_delivery *credentials_delivery,
5719 const struct isds_approval *approval, char **refnumber) {
5720 isds_error err = IE_SUCCESS;
5721 #if HAVE_LIBCURL
5722 xmlNodePtr request = NULL;
5723 xmlDocPtr response = NULL;
5724 xmlXPathContextPtr xpath_ctx = NULL;
5725 xmlXPathObjectPtr result = NULL;
5726 #endif
5729 if (!context) return IE_INVALID_CONTEXT;
5730 zfree(context->long_message);
5731 if (credentials_delivery) {
5732 zfree(credentials_delivery->token);
5733 zfree(credentials_delivery->new_user_name);
5735 if (!box) return IE_INVAL;
5737 #if HAVE_LIBCURL
5738 /* Scratch box ID */
5739 zfree(box->dbID);
5741 /* Build CreateDataBox request */
5742 err = build_CreateDBInput_request(context,
5743 &request, BAD_CAST "CreateDataBox",
5744 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
5745 (xmlChar *) ceo_label, credentials_delivery, approval);
5746 if (err) goto leave;
5748 /* Send it to server and process response */
5749 err = send_destroy_request_check_response(context,
5750 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
5751 &response, (xmlChar **) refnumber);
5753 /* Extract box ID */
5754 xpath_ctx = xmlXPathNewContext(response);
5755 if (!xpath_ctx) {
5756 err = IE_ERROR;
5757 goto leave;
5759 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5760 err = IE_ERROR;
5761 goto leave;
5763 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
5765 /* Extract optional token */
5766 err = extract_credentials_delivery(context, credentials_delivery, response,
5767 "CreateDataBox");
5769 leave:
5770 xmlXPathFreeObject(result);
5771 xmlXPathFreeContext(xpath_ctx);
5772 xmlFreeDoc(response);
5773 xmlFreeNode(request);
5775 if (!err) {
5776 isds_log(ILF_ISDS, ILL_DEBUG,
5777 _("CreateDataBox request processed by server successfully.\n"));
5779 #else /* not HAVE_LIBCURL */
5780 err = IE_NOTSUP;
5781 #endif
5783 return err;
5787 /* Notify ISDS about new PFO entity.
5788 * This function has no real effect.
5789 * @context is session context
5790 * @box is PFO description including single primary user.
5791 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
5792 * @former_names is optional undocumented string. Pass NULL if you don't care.
5793 * @upper_box_id is optional ID of supper box if currently created box is
5794 * subordinated.
5795 * @ceo_label is optional title of OVM box owner (e.g. mayor)
5796 * @approval is optional external approval of box manipulation
5797 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5798 * NULL, if you don't care.*/
5799 isds_error isds_add_pfoinfo(struct isds_ctx *context,
5800 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
5801 const char *former_names, const char *upper_box_id,
5802 const char *ceo_label, const struct isds_approval *approval,
5803 char **refnumber) {
5804 isds_error err = IE_SUCCESS;
5805 #if HAVE_LIBCURL
5806 xmlNodePtr request = NULL;
5807 #endif
5809 if (!context) return IE_INVALID_CONTEXT;
5810 zfree(context->long_message);
5811 if (!box) return IE_INVAL;
5813 #if HAVE_LIBCURL
5814 /* Build CreateDataBoxPFOInfo request */
5815 err = build_CreateDBInput_request(context,
5816 &request, BAD_CAST "CreateDataBoxPFOInfo",
5817 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
5818 (xmlChar *) ceo_label, NULL, approval);
5819 if (err) goto leave;
5821 /* Send it to server and process response */
5822 err = send_request_check_drop_response(context,
5823 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
5824 (xmlChar **) refnumber);
5825 /* XXX: XML Schema names output dbID element but textual documentation
5826 * states no box identifier is returned. */
5827 leave:
5828 xmlFreeNode(request);
5829 #else /* not HAVE_LIBCURL */
5830 err = IE_NOTSUP;
5831 #endif
5832 return err;
5836 /* Common implementation for removing given box.
5837 * @context is session context
5838 * @service_name is UTF-8 encoded name fo ISDS service
5839 * @box is box description to delete
5840 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
5841 * carry sane value. If NULL, do not inject this information into request.
5842 * @approval is optional external approval of box manipulation
5843 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5844 * NULL, if you don't care.*/
5845 isds_error _isds_delete_box_common(struct isds_ctx *context,
5846 const xmlChar *service_name,
5847 const struct isds_DbOwnerInfo *box, const struct tm *since,
5848 const struct isds_approval *approval, char **refnumber) {
5849 isds_error err = IE_SUCCESS;
5850 #if HAVE_LIBCURL
5851 xmlNsPtr isds_ns = NULL;
5852 xmlNodePtr request = NULL;
5853 xmlNodePtr node;
5854 xmlChar *string = NULL;
5855 #endif
5858 if (!context) return IE_INVALID_CONTEXT;
5859 zfree(context->long_message);
5860 if (!service_name || !*service_name || !box) return IE_INVAL;
5863 #if HAVE_LIBCURL
5864 /* Build DeleteDataBox(Promptly) request */
5865 request = xmlNewNode(NULL, service_name);
5866 if (!request) {
5867 char *service_name_locale = _isds_utf82locale((char*)service_name);
5868 isds_printf_message(context,
5869 _("Could build %s request"), service_name_locale);
5870 free(service_name_locale);
5871 return IE_ERROR;
5873 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5874 if(!isds_ns) {
5875 isds_log_message(context, _("Could not create ISDS name space"));
5876 xmlFreeNode(request);
5877 return IE_ERROR;
5879 xmlSetNs(request, isds_ns);
5881 INSERT_ELEMENT(node, request, "dbOwnerInfo");
5882 err = insert_DbOwnerInfo(context, box, node);
5883 if (err) goto leave;
5885 if (since) {
5886 err = tm2datestring(since, &string);
5887 if (err) {
5888 isds_log_message(context,
5889 _("Could not convert `since' argument to ISO date string"));
5890 goto leave;
5892 INSERT_STRING(request, "dbOwnerTerminationDate", string);
5893 zfree(string);
5896 err = insert_GExtApproval(context, approval, request);
5897 if (err) goto leave;
5900 /* Send it to server and process response */
5901 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
5902 service_name, &request, (xmlChar **) refnumber);
5904 leave:
5905 xmlFreeNode(request);
5906 free(string);
5907 #else /* not HAVE_LIBCURL */
5908 err = IE_NOTSUP;
5909 #endif
5910 return err;
5914 /* Remove given box permanently.
5915 * @context is session context
5916 * @box is box description to delete
5917 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
5918 * carry sane value.
5919 * @approval is optional external approval of box manipulation
5920 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5921 * NULL, if you don't care.*/
5922 isds_error isds_delete_box(struct isds_ctx *context,
5923 const struct isds_DbOwnerInfo *box, const struct tm *since,
5924 const struct isds_approval *approval, char **refnumber) {
5925 if (!context) return IE_INVALID_CONTEXT;
5926 zfree(context->long_message);
5927 if (!box || !since) return IE_INVAL;
5929 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBox",
5930 box, since, approval, refnumber);
5934 /* Undocumented function.
5935 * @context is session context
5936 * @box is box description to delete
5937 * @approval is optional external approval of box manipulation
5938 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5939 * NULL, if you don't care.*/
5940 isds_error isds_delete_box_promptly(struct isds_ctx *context,
5941 const struct isds_DbOwnerInfo *box,
5942 const struct isds_approval *approval, char **refnumber) {
5943 if (!context) return IE_INVALID_CONTEXT;
5944 zfree(context->long_message);
5945 if (!box) return IE_INVAL;
5947 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBoxPromptly",
5948 box, NULL, approval, refnumber);
5952 /* Update data about given box.
5953 * @context is session context
5954 * @old_box current box description
5955 * @new_box are updated data about @old_box
5956 * @approval is optional external approval of box manipulation
5957 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5958 * NULL, if you don't care.*/
5959 isds_error isds_UpdateDataBoxDescr(struct isds_ctx *context,
5960 const struct isds_DbOwnerInfo *old_box,
5961 const struct isds_DbOwnerInfo *new_box,
5962 const struct isds_approval *approval, char **refnumber) {
5963 isds_error err = IE_SUCCESS;
5964 #if HAVE_LIBCURL
5965 xmlNsPtr isds_ns = NULL;
5966 xmlNodePtr request = NULL;
5967 xmlNodePtr node;
5968 #endif
5971 if (!context) return IE_INVALID_CONTEXT;
5972 zfree(context->long_message);
5973 if (!old_box || !new_box) return IE_INVAL;
5976 #if HAVE_LIBCURL
5977 /* Build UpdateDataBoxDescr request */
5978 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxDescr");
5979 if (!request) {
5980 isds_log_message(context,
5981 _("Could build UpdateDataBoxDescr request"));
5982 return IE_ERROR;
5984 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5985 if(!isds_ns) {
5986 isds_log_message(context, _("Could not create ISDS name space"));
5987 xmlFreeNode(request);
5988 return IE_ERROR;
5990 xmlSetNs(request, isds_ns);
5992 INSERT_ELEMENT(node, request, "dbOldOwnerInfo");
5993 err = insert_DbOwnerInfo(context, old_box, node);
5994 if (err) goto leave;
5996 INSERT_ELEMENT(node, request, "dbNewOwnerInfo");
5997 err = insert_DbOwnerInfo(context, new_box, node);
5998 if (err) goto leave;
6000 err = insert_GExtApproval(context, approval, request);
6001 if (err) goto leave;
6004 /* Send it to server and process response */
6005 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6006 BAD_CAST "UpdateDataBoxDescr", &request, (xmlChar **) refnumber);
6008 leave:
6009 xmlFreeNode(request);
6010 #else /* not HAVE_LIBCURL */
6011 err = IE_NOTSUP;
6012 #endif
6014 return err;
6018 #if HAVE_LIBCURL
6019 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6020 * code
6021 * @context is session context
6022 * @service is SOAP service
6023 * @service_name is name of request in @service
6024 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6025 * @box_id is box ID of interest
6026 * @approval is optional external approval of box manipulation
6027 * @response is server SOAP body response as XML document
6028 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6029 * NULL, if you don't care.
6030 * @return error coded from lower layer, context message will be set up
6031 * appropriately. */
6032 static isds_error build_send_dbid_request_check_response(
6033 struct isds_ctx *context, const isds_service service,
6034 const xmlChar *service_name, const xmlChar *box_id_element,
6035 const xmlChar *box_id, const struct isds_approval *approval,
6036 xmlDocPtr *response, xmlChar **refnumber) {
6038 isds_error err = IE_SUCCESS;
6039 char *service_name_locale = NULL, *box_id_locale = NULL;
6040 xmlNodePtr request = NULL, node;
6041 xmlNsPtr isds_ns = NULL;
6043 if (!context) return IE_INVALID_CONTEXT;
6044 if (!service_name || !box_id) return IE_INVAL;
6045 if (!response) return IE_INVAL;
6047 /* Free output argument */
6048 xmlFreeDoc(*response); *response = NULL;
6050 /* Prepare strings */
6051 service_name_locale = _isds_utf82locale((char*)service_name);
6052 if (!service_name_locale) {
6053 err = IE_NOMEM;
6054 goto leave;
6056 box_id_locale = _isds_utf82locale((char*)box_id);
6057 if (!box_id_locale) {
6058 err = IE_NOMEM;
6059 goto leave;
6062 /* Build request */
6063 request = xmlNewNode(NULL, service_name);
6064 if (!request) {
6065 isds_printf_message(context,
6066 _("Could not build %s request for %s box"), service_name_locale,
6067 box_id_locale);
6068 err = IE_ERROR;
6069 goto leave;
6071 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6072 if(!isds_ns) {
6073 isds_log_message(context, _("Could not create ISDS name space"));
6074 err = IE_ERROR;
6075 goto leave;
6077 xmlSetNs(request, isds_ns);
6079 /* Add XSD:tIdDbInput children */
6080 if (NULL == box_id_element) box_id_element = BAD_CAST "dbID";
6081 INSERT_STRING(request, box_id_element, box_id);
6082 err = insert_GExtApproval(context, approval, request);
6083 if (err) goto leave;
6085 /* Send request and check response*/
6086 err = send_destroy_request_check_response(context,
6087 service, service_name, &request, response, refnumber);
6089 leave:
6090 free(service_name_locale);
6091 free(box_id_locale);
6092 xmlFreeNode(request);
6093 return err;
6095 #endif /* HAVE_LIBCURL */
6098 /* Get data about all users assigned to given box.
6099 * @context is session context
6100 * @box_id is box ID
6101 * @users is automatically reallocated list of struct isds_DbUserInfo */
6102 isds_error isds_GetDataBoxUsers(struct isds_ctx *context, const char *box_id,
6103 struct isds_list **users) {
6104 isds_error err = IE_SUCCESS;
6105 #if HAVE_LIBCURL
6106 xmlDocPtr response = NULL;
6107 xmlXPathContextPtr xpath_ctx = NULL;
6108 xmlXPathObjectPtr result = NULL;
6109 int i;
6110 struct isds_list *item, *prev_item = NULL;
6111 #endif
6113 if (!context) return IE_INVALID_CONTEXT;
6114 zfree(context->long_message);
6115 if (!users || !box_id) return IE_INVAL;
6116 isds_list_free(users);
6119 #if HAVE_LIBCURL
6120 /* Do request and check for success */
6121 err = build_send_dbid_request_check_response(context,
6122 SERVICE_DB_MANIPULATION, BAD_CAST "GetDataBoxUsers", NULL,
6123 BAD_CAST box_id, NULL, &response, NULL);
6124 if (err) goto leave;
6127 /* Extract data */
6128 /* Prepare structure */
6129 xpath_ctx = xmlXPathNewContext(response);
6130 if (!xpath_ctx) {
6131 err = IE_ERROR;
6132 goto leave;
6134 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6135 err = IE_ERROR;
6136 goto leave;
6139 /* Set context node */
6140 result = xmlXPathEvalExpression(BAD_CAST
6141 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6142 xpath_ctx);
6143 if (!result) {
6144 err = IE_ERROR;
6145 goto leave;
6147 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6148 /* Iterate over all users */
6149 for (i = 0; i < result->nodesetval->nodeNr; i++) {
6151 /* Prepare structure */
6152 item = calloc(1, sizeof(*item));
6153 if (!item) {
6154 err = IE_NOMEM;
6155 goto leave;
6157 item->destructor = (void(*)(void**))isds_DbUserInfo_free;
6158 if (i == 0) *users = item;
6159 else prev_item->next = item;
6160 prev_item = item;
6162 /* Extract it */
6163 xpath_ctx->node = result->nodesetval->nodeTab[i];
6164 err = extract_DbUserInfo(context,
6165 (struct isds_DbUserInfo **) (&item->data), xpath_ctx);
6166 if (err) goto leave;
6170 leave:
6171 if (err) {
6172 isds_list_free(users);
6175 xmlXPathFreeObject(result);
6176 xmlXPathFreeContext(xpath_ctx);
6177 xmlFreeDoc(response);
6179 if (!err)
6180 isds_log(ILF_ISDS, ILL_DEBUG,
6181 _("GetDataBoxUsers request processed by server "
6182 "successfully.\n"));
6183 #else /* not HAVE_LIBCURL */
6184 err = IE_NOTSUP;
6185 #endif
6187 return err;
6191 /* Update data about user assigned to given box.
6192 * @context is session context
6193 * @box is box identification
6194 * @old_user identifies user to update
6195 * @new_user are updated data about @old_user
6196 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6197 * NULL, if you don't care.*/
6198 isds_error isds_UpdateDataBoxUser(struct isds_ctx *context,
6199 const struct isds_DbOwnerInfo *box,
6200 const struct isds_DbUserInfo *old_user,
6201 const struct isds_DbUserInfo *new_user,
6202 char **refnumber) {
6203 isds_error err = IE_SUCCESS;
6204 #if HAVE_LIBCURL
6205 xmlNsPtr isds_ns = NULL;
6206 xmlNodePtr request = NULL;
6207 xmlNodePtr node;
6208 #endif
6211 if (!context) return IE_INVALID_CONTEXT;
6212 zfree(context->long_message);
6213 if (!box || !old_user || !new_user) return IE_INVAL;
6216 #if HAVE_LIBCURL
6217 /* Build UpdateDataBoxUser request */
6218 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxUser");
6219 if (!request) {
6220 isds_log_message(context,
6221 _("Could build UpdateDataBoxUser request"));
6222 return IE_ERROR;
6224 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6225 if(!isds_ns) {
6226 isds_log_message(context, _("Could not create ISDS name space"));
6227 xmlFreeNode(request);
6228 return IE_ERROR;
6230 xmlSetNs(request, isds_ns);
6232 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6233 err = insert_DbOwnerInfo(context, box, node);
6234 if (err) goto leave;
6236 INSERT_ELEMENT(node, request, "dbOldUserInfo");
6237 err = insert_DbUserInfo(context, old_user, node);
6238 if (err) goto leave;
6240 INSERT_ELEMENT(node, request, "dbNewUserInfo");
6241 err = insert_DbUserInfo(context, new_user, node);
6242 if (err) goto leave;
6244 /* Send it to server and process response */
6245 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6246 BAD_CAST "UpdateDataBoxUser", &request, (xmlChar **) refnumber);
6248 leave:
6249 xmlFreeNode(request);
6250 #else /* not HAVE_LIBCURL */
6251 err = IE_NOTSUP;
6252 #endif
6254 return err;
6258 /* Undocumented function.
6259 * @context is session context
6260 * @box_id is UTF-8 encoded box identifier
6261 * @token is UTF-8 encoded temporary password
6262 * @user_id outputs UTF-8 encoded reallocated user identifier
6263 * @password outpus UTF-8 encoded reallocated user password
6264 * Output arguments will be nulled in case of error */
6265 isds_error isds_activate(struct isds_ctx *context,
6266 const char *box_id, const char *token,
6267 char **user_id, char **password) {
6268 isds_error err = IE_SUCCESS;
6269 #if HAVE_LIBCURL
6270 xmlNsPtr isds_ns = NULL;
6271 xmlNodePtr request = NULL, node;
6272 xmlDocPtr response = NULL;
6273 xmlXPathContextPtr xpath_ctx = NULL;
6274 xmlXPathObjectPtr result = NULL;
6275 #endif
6278 if (!context) return IE_INVALID_CONTEXT;
6279 zfree(context->long_message);
6281 if (user_id) zfree(*user_id);
6282 if (password) zfree(*password);
6284 if (!box_id || !token || !user_id || !password) return IE_INVAL;
6287 #if HAVE_LIBCURL
6288 /* Build Activate request */
6289 request = xmlNewNode(NULL, BAD_CAST "Activate");
6290 if (!request) {
6291 isds_log_message(context, _("Could build Activate request"));
6292 return IE_ERROR;
6294 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6295 if(!isds_ns) {
6296 isds_log_message(context, _("Could not create ISDS name space"));
6297 xmlFreeNode(request);
6298 return IE_ERROR;
6300 xmlSetNs(request, isds_ns);
6302 INSERT_STRING(request, "dbAccessDataId", token);
6303 CHECK_FOR_STRING_LENGTH(box_id, 7, 7, "dbID");
6304 INSERT_STRING(request, "dbID", box_id);
6307 /* Send request and check response*/
6308 err = send_destroy_request_check_response(context,
6309 SERVICE_DB_MANIPULATION, BAD_CAST "Activate", &request,
6310 &response, NULL);
6311 if (err) goto leave;
6314 /* Extract data */
6315 xpath_ctx = xmlXPathNewContext(response);
6316 if (!xpath_ctx) {
6317 err = IE_ERROR;
6318 goto leave;
6320 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6321 err = IE_ERROR;
6322 goto leave;
6324 result = xmlXPathEvalExpression(BAD_CAST "/isds:ActivateResponse",
6325 xpath_ctx);
6326 if (!result) {
6327 err = IE_ERROR;
6328 goto leave;
6330 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6331 isds_log_message(context, _("Missing ActivateResponse element"));
6332 err = IE_ISDS;
6333 goto leave;
6335 if (result->nodesetval->nodeNr > 1) {
6336 isds_log_message(context, _("Multiple ActivateResponse element"));
6337 err = IE_ISDS;
6338 goto leave;
6340 xpath_ctx->node = result->nodesetval->nodeTab[0];
6341 xmlXPathFreeObject(result); result = NULL;
6343 EXTRACT_STRING("isds:userId", *user_id);
6344 if (!*user_id)
6345 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6346 "but did not return `userId' element.\n"));
6348 EXTRACT_STRING("isds:password", *password);
6349 if (!*password)
6350 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6351 "but did not return `password' element.\n"));
6353 leave:
6354 xmlXPathFreeObject(result);
6355 xmlXPathFreeContext(xpath_ctx);
6356 xmlFreeDoc(response);
6357 xmlFreeNode(request);
6359 if (!err)
6360 isds_log(ILF_ISDS, ILL_DEBUG,
6361 _("Activate request processed by server successfully.\n"));
6362 #else /* not HAVE_LIBCURL */
6363 err = IE_NOTSUP;
6364 #endif
6366 return err;
6370 /* Reset credentials of user assigned to given box.
6371 * @context is session context
6372 * @box is box identification
6373 * @user identifies user to reset password
6374 * @fee_paid is true if fee has been paid, false otherwise
6375 * @approval is optional external approval of box manipulation
6376 * @credentials_delivery is NULL if new password should be delivered off-line
6377 * to the user. It is valid pointer if user should obtain new password on-line
6378 * on dedicated web server. Then input @credentials_delivery.email value is
6379 * user's e-mail address user must provide to dedicated web server together
6380 * with @credentials_delivery.token. The output reallocated token user needs
6381 * to use to authorize on the web server to view his new password. Output
6382 * reallocated @credentials_delivery.new_user_name is user's log-in name that
6383 * ISDS changed up on this call. (No reason why server could change the name
6384 * is known now.)
6385 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6386 * NULL, if you don't care.*/
6387 isds_error isds_reset_password(struct isds_ctx *context,
6388 const struct isds_DbOwnerInfo *box,
6389 const struct isds_DbUserInfo *user,
6390 const _Bool fee_paid, const struct isds_approval *approval,
6391 struct isds_credentials_delivery *credentials_delivery,
6392 char **refnumber) {
6393 isds_error err = IE_SUCCESS;
6394 #if HAVE_LIBCURL
6395 xmlNsPtr isds_ns = NULL;
6396 xmlNodePtr request = NULL, node;
6397 xmlDocPtr response = NULL;
6398 #endif
6401 if (!context) return IE_INVALID_CONTEXT;
6402 zfree(context->long_message);
6404 if (credentials_delivery) {
6405 zfree(credentials_delivery->token);
6406 zfree(credentials_delivery->new_user_name);
6408 if (!box || !user) return IE_INVAL;
6411 #if HAVE_LIBCURL
6412 /* Build NewAccessData request */
6413 request = xmlNewNode(NULL, BAD_CAST "NewAccessData");
6414 if (!request) {
6415 isds_log_message(context,
6416 _("Could build NewAccessData request"));
6417 return IE_ERROR;
6419 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6420 if(!isds_ns) {
6421 isds_log_message(context, _("Could not create ISDS name space"));
6422 xmlFreeNode(request);
6423 return IE_ERROR;
6425 xmlSetNs(request, isds_ns);
6427 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6428 err = insert_DbOwnerInfo(context, box, node);
6429 if (err) goto leave;
6431 INSERT_ELEMENT(node, request, "dbUserInfo");
6432 err = insert_DbUserInfo(context, user, node);
6433 if (err) goto leave;
6435 INSERT_SCALAR_BOOLEAN(request, "dbFeePaid", fee_paid);
6437 err = insert_credentials_delivery(context, credentials_delivery, request);
6438 if (err) goto leave;
6440 err = insert_GExtApproval(context, approval, request);
6441 if (err) goto leave;
6443 /* Send request and check response*/
6444 err = send_destroy_request_check_response(context,
6445 SERVICE_DB_MANIPULATION, BAD_CAST "NewAccessData", &request,
6446 &response, (xmlChar **) refnumber);
6447 if (err) goto leave;
6450 /* Extract optional token */
6451 err = extract_credentials_delivery(context, credentials_delivery,
6452 response, "NewAccessData");
6454 leave:
6455 xmlFreeDoc(response);
6456 xmlFreeNode(request);
6458 if (!err)
6459 isds_log(ILF_ISDS, ILL_DEBUG,
6460 _("NewAccessData request processed by server "
6461 "successfully.\n"));
6462 #else /* not HAVE_LIBCURL */
6463 err = IE_NOTSUP;
6464 #endif
6466 return err;
6470 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
6471 * code, destroy response and log success.
6472 * @context is ISDS session context.
6473 * @service_name is name of SERVICE_DB_MANIPULATION service
6474 * @box is box identification
6475 * @user identifies user to remove
6476 * @credentials_delivery is NULL if new user's password should be delivered
6477 * off-line to the user. It is valid pointer if user should obtain new
6478 * password on-line on dedicated web server. Then input
6479 * @credentials_delivery.email value is user's e-mail address user must
6480 * provide to dedicated web server together with @credentials_delivery.token.
6481 * The output reallocated token user needs to use to authorize on the web
6482 * server to view his new password. Output reallocated
6483 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6484 * assingned or changed up on this call.
6485 * @approval is optional external approval of box manipulation
6486 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6487 * NULL, if you don't care. */
6488 static isds_error build_send_manipulationboxuser_request_check_drop_response(
6489 struct isds_ctx *context, const xmlChar *service_name,
6490 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6491 struct isds_credentials_delivery *credentials_delivery,
6492 const struct isds_approval *approval, xmlChar **refnumber) {
6493 isds_error err = IE_SUCCESS;
6494 #if HAVE_LIBCURL
6495 xmlNsPtr isds_ns = NULL;
6496 xmlNodePtr request = NULL, node;
6497 xmlDocPtr response = NULL;
6498 #endif
6501 if (!context) return IE_INVALID_CONTEXT;
6502 zfree(context->long_message);
6503 if (credentials_delivery) {
6504 zfree(credentials_delivery->token);
6505 zfree(credentials_delivery->new_user_name);
6507 if (!service_name || service_name[0] == '\0' || !box || !user)
6508 return IE_INVAL;
6511 #if HAVE_LIBCURL
6512 /* Build NewAccessData or similar request */
6513 request = xmlNewNode(NULL, service_name);
6514 if (!request) {
6515 char *service_name_locale = _isds_utf82locale((char *) service_name);
6516 isds_printf_message(context, _("Could not build %s request"),
6517 service_name_locale);
6518 free(service_name_locale);
6519 return IE_ERROR;
6521 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6522 if(!isds_ns) {
6523 isds_log_message(context, _("Could not create ISDS name space"));
6524 xmlFreeNode(request);
6525 return IE_ERROR;
6527 xmlSetNs(request, isds_ns);
6529 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6530 err = insert_DbOwnerInfo(context, box, node);
6531 if (err) goto leave;
6533 INSERT_ELEMENT(node, request, "dbUserInfo");
6534 err = insert_DbUserInfo(context, user, node);
6535 if (err) goto leave;
6537 err = insert_credentials_delivery(context, credentials_delivery, request);
6538 if (err) goto leave;
6540 err = insert_GExtApproval(context, approval, request);
6541 if (err) goto leave;
6544 /* Send request and check response*/
6545 err = send_destroy_request_check_response(context,
6546 SERVICE_DB_MANIPULATION, service_name, &request, &response, refnumber);
6548 xmlFreeNode(request);
6549 request = NULL;
6551 /* Pick up credentials_delivery if requested */
6552 err = extract_credentials_delivery(context, credentials_delivery, response,
6553 (char *)service_name);
6555 leave:
6556 xmlFreeDoc(response);
6557 if (request) xmlFreeNode(request);
6559 if (!err) {
6560 char *service_name_locale = _isds_utf82locale((char *) service_name);
6561 isds_log(ILF_ISDS, ILL_DEBUG,
6562 _("%s request processed by server successfully.\n"),
6563 service_name_locale);
6564 free(service_name_locale);
6566 #else /* not HAVE_LIBCURL */
6567 err = IE_NOTSUP;
6568 #endif
6570 return err;
6574 /* Assign new user to given box.
6575 * @context is session context
6576 * @box is box identification
6577 * @user defines new user to add
6578 * @credentials_delivery is NULL if new user's password should be delivered
6579 * off-line to the user. It is valid pointer if user should obtain new
6580 * password on-line on dedicated web server. Then input
6581 * @credentials_delivery.email value is user's e-mail address user must
6582 * provide to dedicated web server together with @credentials_delivery.token.
6583 * The output reallocated token user needs to use to authorize on the web
6584 * server to view his new password. Output reallocated
6585 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6586 * assingned up on this call.
6587 * @approval is optional external approval of box manipulation
6588 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6589 * NULL, if you don't care.*/
6590 isds_error isds_add_user(struct isds_ctx *context,
6591 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6592 struct isds_credentials_delivery *credentials_delivery,
6593 const struct isds_approval *approval, char **refnumber) {
6594 return build_send_manipulationboxuser_request_check_drop_response(context,
6595 BAD_CAST "AddDataBoxUser", box, user, credentials_delivery,
6596 approval, (xmlChar **) refnumber);
6600 /* Remove user assigned to given box.
6601 * @context is session context
6602 * @box is box identification
6603 * @user identifies user to remove
6604 * @approval is optional external approval of box manipulation
6605 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6606 * NULL, if you don't care.*/
6607 isds_error isds_delete_user(struct isds_ctx *context,
6608 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6609 const struct isds_approval *approval, char **refnumber) {
6610 return build_send_manipulationboxuser_request_check_drop_response(context,
6611 BAD_CAST "DeleteDataBoxUser", box, user, NULL, approval,
6612 (xmlChar **) refnumber);
6616 /* Get list of boxes in ZIP archive.
6617 * @context is session context
6618 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
6619 * System recognizes following values currently: ALL (all boxes), UPG
6620 * (effectively OVM boxes), OVM (OVM gross type boxes), OPN (boxes allowing
6621 * receiving commercial messages). This argument is a string because
6622 * specification states new values can appear in the future. Not all list
6623 * types are available to all users.
6624 * @buffer is automatically reallocated memory to store the list of boxes. The
6625 * list is zipped CSV file.
6626 * @buffer_length is size of @buffer data in bytes.
6627 * In case of error @buffer will be freed and @buffer_length will be
6628 * undefined.*/
6629 isds_error isds_get_box_list_archive(struct isds_ctx *context,
6630 const char *list_identifier, void **buffer, size_t *buffer_length) {
6631 isds_error err = IE_SUCCESS;
6632 #if HAVE_LIBCURL
6633 xmlNsPtr isds_ns = NULL;
6634 xmlNodePtr request = NULL, node;
6635 xmlDocPtr response = NULL;
6636 xmlXPathContextPtr xpath_ctx = NULL;
6637 xmlXPathObjectPtr result = NULL;
6638 char *string = NULL;
6639 #endif
6642 if (!context) return IE_INVALID_CONTEXT;
6643 zfree(context->long_message);
6644 if (buffer) zfree(*buffer);
6645 if (!buffer || !buffer_length) return IE_INVAL;
6648 #if HAVE_LIBCURL
6649 /* Check if connection is established
6650 * TODO: This check should be done downstairs. */
6651 if (!context->curl) return IE_CONNECTION_CLOSED;
6654 /* Build AuthenticateMessage request */
6655 request = xmlNewNode(NULL, BAD_CAST "GetDataBoxList");
6656 if (!request) {
6657 isds_log_message(context,
6658 _("Could not build GetDataBoxList request"));
6659 return IE_ERROR;
6661 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6662 if(!isds_ns) {
6663 isds_log_message(context, _("Could not create ISDS name space"));
6664 xmlFreeNode(request);
6665 return IE_ERROR;
6667 xmlSetNs(request, isds_ns);
6668 INSERT_STRING(request, "dblType", list_identifier);
6670 /* Send request to server and process response */
6671 err = send_destroy_request_check_response(context,
6672 SERVICE_DB_SEARCH, BAD_CAST "GetDataBoxList", &request,
6673 &response, NULL);
6674 if (err) goto leave;
6677 /* Extract Base-64 encoded ZIP file */
6678 xpath_ctx = xmlXPathNewContext(response);
6679 if (!xpath_ctx) {
6680 err = IE_ERROR;
6681 goto leave;
6683 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6684 err = IE_ERROR;
6685 goto leave;
6687 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string);
6689 /* Decode non-empty archive */
6690 if (string && string[0] != '\0') {
6691 *buffer_length = _isds_b64decode(string, buffer);
6692 if (*buffer_length == (size_t) -1) {
6693 isds_printf_message(context,
6694 _("Error while Base64-decoding box list archive"));
6695 err = IE_ERROR;
6696 goto leave;
6701 leave:
6702 free(string);
6703 xmlXPathFreeObject(result);
6704 xmlXPathFreeContext(xpath_ctx);
6705 xmlFreeDoc(response);
6706 xmlFreeNode(request);
6708 if (!err) {
6709 isds_log(ILF_ISDS, ILL_DEBUG, _("GetDataBoxList request "
6710 "processed by server successfully.\n"));
6712 #else /* not HAVE_LIBCURL */
6713 err = IE_NOTSUP;
6714 #endif
6716 return err;
6720 /* Find boxes suiting given criteria.
6721 * @criteria is filter. You should fill in at least some members.
6722 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
6723 * possibly empty. Input NULL or valid old structure.
6724 * @return:
6725 * IE_SUCCESS if search succeeded, @boxes contains useful data
6726 * IE_NOEXIST if no such box exists, @boxes will be NULL
6727 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
6728 * contains still valid data
6729 * other code if something bad happens. @boxes will be NULL. */
6730 isds_error isds_FindDataBox(struct isds_ctx *context,
6731 const struct isds_DbOwnerInfo *criteria,
6732 struct isds_list **boxes) {
6733 isds_error err = IE_SUCCESS;
6734 #if HAVE_LIBCURL
6735 _Bool truncated = 0;
6736 xmlNsPtr isds_ns = NULL;
6737 xmlNodePtr request = NULL;
6738 xmlDocPtr response = NULL;
6739 xmlChar *code = NULL, *message = NULL;
6740 xmlNodePtr db_owner_info;
6741 xmlXPathContextPtr xpath_ctx = NULL;
6742 xmlXPathObjectPtr result = NULL;
6743 xmlChar *string = NULL;
6744 #endif
6747 if (!context) return IE_INVALID_CONTEXT;
6748 zfree(context->long_message);
6749 if (!boxes) return IE_INVAL;
6750 isds_list_free(boxes);
6752 if (!criteria) {
6753 return IE_INVAL;
6756 #if HAVE_LIBCURL
6757 /* Check if connection is established
6758 * TODO: This check should be done downstairs. */
6759 if (!context->curl) return IE_CONNECTION_CLOSED;
6762 /* Build FindDataBox request */
6763 request = xmlNewNode(NULL, BAD_CAST "FindDataBox");
6764 if (!request) {
6765 isds_log_message(context,
6766 _("Could build FindDataBox request"));
6767 return IE_ERROR;
6769 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6770 if(!isds_ns) {
6771 isds_log_message(context, _("Could not create ISDS name space"));
6772 xmlFreeNode(request);
6773 return IE_ERROR;
6775 xmlSetNs(request, isds_ns);
6776 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
6777 if (!db_owner_info) {
6778 isds_log_message(context, _("Could not add dbOwnerInfo child to "
6779 "FindDataBox element"));
6780 xmlFreeNode(request);
6781 return IE_ERROR;
6784 err = insert_DbOwnerInfo(context, criteria, db_owner_info);
6785 if (err) goto leave;
6788 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending FindDataBox request to ISDS\n"));
6790 /* Sent request */
6791 err = isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
6793 /* Destroy request */
6794 xmlFreeNode(request); request = NULL;
6796 if (err) {
6797 isds_log(ILF_ISDS, ILL_DEBUG,
6798 _("Processing ISDS response on FindDataBox "
6799 "request failed\n"));
6800 goto leave;
6803 /* Check for response status */
6804 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
6805 &code, &message, NULL);
6806 if (err) {
6807 isds_log(ILF_ISDS, ILL_DEBUG,
6808 _("ISDS response on FindDataBox request is missing status\n"));
6809 goto leave;
6812 /* Request processed, but nothing found */
6813 if (!xmlStrcmp(code, BAD_CAST "0002") ||
6814 !xmlStrcmp(code, BAD_CAST "5001")) {
6815 char *code_locale = _isds_utf82locale((char*)code);
6816 char *message_locale = _isds_utf82locale((char*)message);
6817 isds_log(ILF_ISDS, ILL_DEBUG,
6818 _("Server did not found any box on FindDataBox request "
6819 "(code=%s, message=%s)\n"), code_locale, message_locale);
6820 isds_log_message(context, message_locale);
6821 free(code_locale);
6822 free(message_locale);
6823 err = IE_NOEXIST;
6824 goto leave;
6827 /* Warning, not a error */
6828 if (!xmlStrcmp(code, BAD_CAST "0003")) {
6829 char *code_locale = _isds_utf82locale((char*)code);
6830 char *message_locale = _isds_utf82locale((char*)message);
6831 isds_log(ILF_ISDS, ILL_DEBUG,
6832 _("Server truncated response on FindDataBox request "
6833 "(code=%s, message=%s)\n"), code_locale, message_locale);
6834 isds_log_message(context, message_locale);
6835 free(code_locale);
6836 free(message_locale);
6837 truncated = 1;
6840 /* Other error */
6841 else if (xmlStrcmp(code, BAD_CAST "0000")) {
6842 char *code_locale = _isds_utf82locale((char*)code);
6843 char *message_locale = _isds_utf82locale((char*)message);
6844 isds_log(ILF_ISDS, ILL_DEBUG,
6845 _("Server refused FindDataBox request "
6846 "(code=%s, message=%s)\n"), code_locale, message_locale);
6847 isds_log_message(context, message_locale);
6848 free(code_locale);
6849 free(message_locale);
6850 err = IE_ISDS;
6851 goto leave;
6854 xpath_ctx = xmlXPathNewContext(response);
6855 if (!xpath_ctx) {
6856 err = IE_ERROR;
6857 goto leave;
6859 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6860 err = IE_ERROR;
6861 goto leave;
6864 /* Extract boxes if they present */
6865 result = xmlXPathEvalExpression(BAD_CAST
6866 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
6867 xpath_ctx);
6868 if (!result) {
6869 err = IE_ERROR;
6870 goto leave;
6872 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6873 struct isds_list *item, *prev_item = NULL;
6874 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
6875 item = calloc(1, sizeof(*item));
6876 if (!item) {
6877 err = IE_NOMEM;
6878 goto leave;
6881 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
6882 if (i == 0) *boxes = item;
6883 else prev_item->next = item;
6884 prev_item = item;
6886 xpath_ctx->node = result->nodesetval->nodeTab[i];
6887 err = extract_DbOwnerInfo(context,
6888 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
6889 if (err) goto leave;
6893 leave:
6894 if (err) {
6895 isds_list_free(boxes);
6896 } else {
6897 if (truncated) err = IE_2BIG;
6900 free(string);
6901 xmlFreeNode(request);
6902 xmlXPathFreeObject(result);
6903 xmlXPathFreeContext(xpath_ctx);
6905 free(code);
6906 free(message);
6907 xmlFreeDoc(response);
6909 if (!err)
6910 isds_log(ILF_ISDS, ILL_DEBUG,
6911 _("FindDataBox request processed by server successfully.\n"));
6912 #else /* not HAVE_LIBCURL */
6913 err = IE_NOTSUP;
6914 #endif
6916 return err;
6920 /* Get status of a box.
6921 * @context is ISDS session context.
6922 * @box_id is UTF-8 encoded box identifier as zero terminated string
6923 * @box_status is return value of box status.
6924 * @return:
6925 * IE_SUCCESS if box has been found and its status retrieved
6926 * IE_NOEXIST if box is not known to ISDS server
6927 * or other appropriate error.
6928 * You can use isds_DbState to enumerate box status. However out of enum
6929 * range value can be returned too. This is feature because ISDS
6930 * specification leaves the set of values open.
6931 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
6932 * the box has been deleted, but ISDS still lists its former existence. */
6933 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
6934 long int *box_status) {
6935 isds_error err = IE_SUCCESS;
6936 #if HAVE_LIBCURL
6937 xmlNsPtr isds_ns = NULL;
6938 xmlNodePtr request = NULL, db_id;
6939 xmlDocPtr response = NULL;
6940 xmlChar *code = NULL, *message = NULL;
6941 xmlXPathContextPtr xpath_ctx = NULL;
6942 xmlXPathObjectPtr result = NULL;
6943 xmlChar *string = NULL;
6944 #endif
6946 if (!context) return IE_INVALID_CONTEXT;
6947 zfree(context->long_message);
6948 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
6950 #if HAVE_LIBCURL
6951 /* Check if connection is established
6952 * TODO: This check should be done downstairs. */
6953 if (!context->curl) return IE_CONNECTION_CLOSED;
6956 /* Build CheckDataBox request */
6957 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
6958 if (!request) {
6959 isds_log_message(context,
6960 _("Could build CheckDataBox request"));
6961 return IE_ERROR;
6963 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6964 if(!isds_ns) {
6965 isds_log_message(context, _("Could not create ISDS name space"));
6966 xmlFreeNode(request);
6967 return IE_ERROR;
6969 xmlSetNs(request, isds_ns);
6970 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
6971 if (!db_id) {
6972 isds_log_message(context, _("Could not add dbID child to "
6973 "CheckDataBox element"));
6974 xmlFreeNode(request);
6975 return IE_ERROR;
6979 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CheckDataBox request to ISDS\n"));
6981 /* Sent request */
6982 err = isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
6984 /* Destroy request */
6985 xmlFreeNode(request);
6987 if (err) {
6988 isds_log(ILF_ISDS, ILL_DEBUG,
6989 _("Processing ISDS response on CheckDataBox "
6990 "request failed\n"));
6991 goto leave;
6994 /* Check for response status */
6995 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
6996 &code, &message, NULL);
6997 if (err) {
6998 isds_log(ILF_ISDS, ILL_DEBUG,
6999 _("ISDS response on CheckDataBox request is missing status\n"));
7000 goto leave;
7003 /* Request processed, but nothing found */
7004 if (!xmlStrcmp(code, BAD_CAST "5001")) {
7005 char *box_id_locale = _isds_utf82locale((char*)box_id);
7006 char *code_locale = _isds_utf82locale((char*)code);
7007 char *message_locale = _isds_utf82locale((char*)message);
7008 isds_log(ILF_ISDS, ILL_DEBUG,
7009 _("Server did not found box %s on CheckDataBox request "
7010 "(code=%s, message=%s)\n"),
7011 box_id_locale, code_locale, message_locale);
7012 isds_log_message(context, message_locale);
7013 free(box_id_locale);
7014 free(code_locale);
7015 free(message_locale);
7016 err = IE_NOEXIST;
7017 goto leave;
7020 /* Other error */
7021 else if (xmlStrcmp(code, BAD_CAST "0000")) {
7022 char *code_locale = _isds_utf82locale((char*)code);
7023 char *message_locale = _isds_utf82locale((char*)message);
7024 isds_log(ILF_ISDS, ILL_DEBUG,
7025 _("Server refused CheckDataBox request "
7026 "(code=%s, message=%s)\n"), code_locale, message_locale);
7027 isds_log_message(context, message_locale);
7028 free(code_locale);
7029 free(message_locale);
7030 err = IE_ISDS;
7031 goto leave;
7034 /* Extract data */
7035 xpath_ctx = xmlXPathNewContext(response);
7036 if (!xpath_ctx) {
7037 err = IE_ERROR;
7038 goto leave;
7040 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7041 err = IE_ERROR;
7042 goto leave;
7044 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
7045 xpath_ctx);
7046 if (!result) {
7047 err = IE_ERROR;
7048 goto leave;
7050 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7051 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
7052 err = IE_ISDS;
7053 goto leave;
7055 if (result->nodesetval->nodeNr > 1) {
7056 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
7057 err = IE_ISDS;
7058 goto leave;
7060 xpath_ctx->node = result->nodesetval->nodeTab[0];
7061 xmlXPathFreeObject(result); result = NULL;
7063 EXTRACT_LONGINT("isds:dbState", box_status, 1);
7066 leave:
7067 free(string);
7068 xmlXPathFreeObject(result);
7069 xmlXPathFreeContext(xpath_ctx);
7071 free(code);
7072 free(message);
7073 xmlFreeDoc(response);
7075 if (!err)
7076 isds_log(ILF_ISDS, ILL_DEBUG,
7077 _("CheckDataBox request processed by server successfully.\n"));
7078 #else /* not HAVE_LIBCURL */
7079 err = IE_NOTSUP;
7080 #endif
7082 return err;
7086 /* Get list of permissions to send commercial messages.
7087 * @context is ISDS session context.
7088 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
7089 * @permissions is a reallocated list of permissions (struct
7090 * isds_commercial_permission*) to send commercial messages from @box_id. The
7091 * order of permissions is significant as the server applies the permissions
7092 * and associated pre-paid credits in the order. Empty list means no
7093 * permission.
7094 * @return:
7095 * IE_SUCCESS if the list has been obtained correctly,
7096 * or other appropriate error. */
7097 isds_error isds_get_commercial_permissions(struct isds_ctx *context,
7098 const char *box_id, struct isds_list **permissions) {
7099 isds_error err = IE_SUCCESS;
7100 #if HAVE_LIBCURL
7101 xmlDocPtr response = NULL;
7102 xmlXPathContextPtr xpath_ctx = NULL;
7103 xmlXPathObjectPtr result = NULL;
7104 #endif
7106 if (!context) return IE_INVALID_CONTEXT;
7107 zfree(context->long_message);
7108 if (NULL == permissions) return IE_INVAL;
7109 isds_list_free(permissions);
7110 if (NULL == box_id) return IE_INVAL;
7112 #if HAVE_LIBCURL
7113 /* Check if connection is established */
7114 if (!context->curl) return IE_CONNECTION_CLOSED;
7116 /* Do request and check for success */
7117 err = build_send_dbid_request_check_response(context,
7118 SERVICE_DB_SEARCH, BAD_CAST "PDZInfo", BAD_CAST "PDZSender",
7119 BAD_CAST box_id, NULL, &response, NULL);
7120 if (!err) {
7121 isds_log(ILF_ISDS, ILL_DEBUG,
7122 _("PDZInfo request processed by server successfully.\n"));
7125 /* Extract data */
7126 /* Prepare structure */
7127 xpath_ctx = xmlXPathNewContext(response);
7128 if (!xpath_ctx) {
7129 err = IE_ERROR;
7130 goto leave;
7132 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7133 err = IE_ERROR;
7134 goto leave;
7137 /* Set context node */
7138 result = xmlXPathEvalExpression(BAD_CAST
7139 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
7140 xpath_ctx);
7141 if (!result) {
7142 err = IE_ERROR;
7143 goto leave;
7145 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7146 /* Iterate over all permission records */
7147 for (long unsigned int i = 0; i < result->nodesetval->nodeNr; i++) {
7148 struct isds_list *item, *prev_item = NULL;
7150 /* Prepare structure */
7151 item = calloc(1, sizeof(*item));
7152 if (!item) {
7153 err = IE_NOMEM;
7154 goto leave;
7156 item->destructor = (void(*)(void**))isds_commercial_permission_free;
7157 if (i == 0) *permissions = item;
7158 else prev_item->next = item;
7159 prev_item = item;
7161 /* Extract it */
7162 xpath_ctx->node = result->nodesetval->nodeTab[i];
7163 err = extract_DbPDZRecord(context,
7164 (struct isds_commercial_permission **) (&item->data),
7165 xpath_ctx);
7166 if (err) goto leave;
7170 leave:
7171 if (err) {
7172 isds_list_free(permissions);
7175 xmlXPathFreeObject(result);
7176 xmlXPathFreeContext(xpath_ctx);
7177 xmlFreeDoc(response);
7179 #else /* not HAVE_LIBCURL */
7180 err = IE_NOTSUP;
7181 #endif
7183 return err;
7187 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
7188 * code, destroy response and log success.
7189 * @context is ISDS session context.
7190 * @service_name is name of SERVICE_DB_MANIPULATION service
7191 * @box_id is UTF-8 encoded box identifier as zero terminated string
7192 * @approval is optional external approval of box manipulation
7193 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7194 * NULL, if you don't care. */
7195 static isds_error build_send_manipulationdbid_request_check_drop_response(
7196 struct isds_ctx *context, const xmlChar *service_name,
7197 const xmlChar *box_id, const struct isds_approval *approval,
7198 xmlChar **refnumber) {
7199 isds_error err = IE_SUCCESS;
7200 #if HAVE_LIBCURL
7201 xmlDocPtr response = NULL;
7202 #endif
7204 if (!context) return IE_INVALID_CONTEXT;
7205 zfree(context->long_message);
7206 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
7208 #if HAVE_LIBCURL
7209 /* Check if connection is established */
7210 if (!context->curl) return IE_CONNECTION_CLOSED;
7212 /* Do request and check for success */
7213 err = build_send_dbid_request_check_response(context,
7214 SERVICE_DB_MANIPULATION, service_name, NULL, box_id, approval,
7215 &response, refnumber);
7216 xmlFreeDoc(response);
7218 if (!err) {
7219 char *service_name_locale = _isds_utf82locale((char *) service_name);
7220 isds_log(ILF_ISDS, ILL_DEBUG,
7221 _("%s request processed by server successfully.\n"),
7222 service_name_locale);
7223 free(service_name_locale);
7225 #else /* not HAVE_LIBCURL */
7226 err = IE_NOTSUP;
7227 #endif
7229 return err;
7233 /* Switch box into state where box can receive commercial messages (off by
7234 * default)
7235 * @context is ISDS session context.
7236 * @box_id is UTF-8 encoded box identifier as zero terminated string
7237 * @allow is true for enable, false for disable commercial messages income
7238 * @approval is optional external approval of box manipulation
7239 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7240 * NULL, if you don't care. */
7241 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
7242 const char *box_id, const _Bool allow,
7243 const struct isds_approval *approval, char **refnumber) {
7244 return build_send_manipulationdbid_request_check_drop_response(context,
7245 (allow) ? BAD_CAST "SetOpenAddressing" :
7246 BAD_CAST "ClearOpenAddressing",
7247 BAD_CAST box_id, approval, (xmlChar **) refnumber);
7251 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
7252 * message acceptance). This is just a box permission. Sender must apply
7253 * such role by sending each message.
7254 * @context is ISDS session context.
7255 * @box_id is UTF-8 encoded box identifier as zero terminated string
7256 * @allow is true for enable, false for disable OVM role permission
7257 * @approval is optional external approval of box manipulation
7258 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7259 * NULL, if you don't care. */
7260 isds_error isds_switch_effective_ovm(struct isds_ctx *context,
7261 const char *box_id, const _Bool allow,
7262 const struct isds_approval *approval, char **refnumber) {
7263 return build_send_manipulationdbid_request_check_drop_response(context,
7264 (allow) ? BAD_CAST "SetEffectiveOVM" :
7265 BAD_CAST "ClearEffectiveOVM",
7266 BAD_CAST box_id, approval, (xmlChar **) refnumber);
7270 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
7271 * code, destroy response and log success.
7272 * @context is ISDS session context.
7273 * @service_name is name of SERVICE_DB_MANIPULATION service
7274 * @owner is structure describing box
7275 * @approval is optional external approval of box manipulation
7276 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7277 * NULL, if you don't care. */
7278 static isds_error build_send_manipulationdbowner_request_check_drop_response(
7279 struct isds_ctx *context, const xmlChar *service_name,
7280 const struct isds_DbOwnerInfo *owner,
7281 const struct isds_approval *approval, xmlChar **refnumber) {
7282 isds_error err = IE_SUCCESS;
7283 #if HAVE_LIBCURL
7284 char *service_name_locale = NULL;
7285 xmlNodePtr request = NULL, db_owner_info;
7286 xmlNsPtr isds_ns = NULL;
7287 #endif
7290 if (!context) return IE_INVALID_CONTEXT;
7291 zfree(context->long_message);
7292 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
7294 #if HAVE_LIBCURL
7295 service_name_locale = _isds_utf82locale((char*)service_name);
7296 if (!service_name_locale) {
7297 err = IE_NOMEM;
7298 goto leave;
7301 /* Build request */
7302 request = xmlNewNode(NULL, service_name);
7303 if (!request) {
7304 isds_printf_message(context,
7305 _("Could not build %s request"), service_name_locale);
7306 err = IE_ERROR;
7307 goto leave;
7309 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7310 if(!isds_ns) {
7311 isds_log_message(context, _("Could not create ISDS name space"));
7312 err = IE_ERROR;
7313 goto leave;
7315 xmlSetNs(request, isds_ns);
7318 /* Add XSD:tOwnerInfoInput child*/
7319 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
7320 err = insert_DbOwnerInfo(context, owner, db_owner_info);
7321 if (err) goto leave;
7323 /* Add XSD:gExtApproval*/
7324 err = insert_GExtApproval(context, approval, request);
7325 if (err) goto leave;
7327 /* Send it to server and process response */
7328 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
7329 service_name, &request, refnumber);
7331 leave:
7332 xmlFreeNode(request);
7333 free(service_name_locale);
7334 #else /* not HAVE_LIBCURL */
7335 err = IE_NOTSUP;
7336 #endif
7338 return err;
7342 /* Switch box accessibility state on request of box owner.
7343 * Despite the name, owner must do the request off-line. This function is
7344 * designed for such off-line meeting points (e.g. Czech POINT).
7345 * @context is ISDS session context.
7346 * @box identifies box to switch accessibility state.
7347 * @allow is true for making accessible, false to disallow access.
7348 * @approval is optional external approval of box manipulation
7349 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7350 * NULL, if you don't care. */
7351 isds_error isds_switch_box_accessibility_on_owner_request(
7352 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
7353 const _Bool allow, const struct isds_approval *approval,
7354 char **refnumber) {
7355 return build_send_manipulationdbowner_request_check_drop_response(context,
7356 (allow) ? BAD_CAST "EnableOwnDataBox" :
7357 BAD_CAST "DisableOwnDataBox",
7358 box, approval, (xmlChar **) refnumber);
7362 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
7363 * date.
7364 * @context is ISDS session context.
7365 * @box identifies box to switch accessibility state.
7366 * @since is date since accessibility has been denied. This can be past too.
7367 * Only tm_year, tm_mon and tm_mday carry sane value.
7368 * @approval is optional external approval of box manipulation
7369 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7370 * NULL, if you don't care. */
7371 isds_error isds_disable_box_accessibility_externaly(
7372 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
7373 const struct tm *since, const struct isds_approval *approval,
7374 char **refnumber) {
7375 isds_error err = IE_SUCCESS;
7376 #if HAVE_LIBCURL
7377 char *service_name_locale = NULL;
7378 xmlNodePtr request = NULL, node;
7379 xmlNsPtr isds_ns = NULL;
7380 xmlChar *string = NULL;
7381 #endif
7384 if (!context) return IE_INVALID_CONTEXT;
7385 zfree(context->long_message);
7386 if (!box || !since) return IE_INVAL;
7388 #if HAVE_LIBCURL
7389 /* Build request */
7390 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
7391 if (!request) {
7392 isds_printf_message(context,
7393 _("Could not build %s request"), "DisableDataBoxExternally");
7394 err = IE_ERROR;
7395 goto leave;
7397 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7398 if(!isds_ns) {
7399 isds_log_message(context, _("Could not create ISDS name space"));
7400 err = IE_ERROR;
7401 goto leave;
7403 xmlSetNs(request, isds_ns);
7406 /* Add @box identification */
7407 INSERT_ELEMENT(node, request, "dbOwnerInfo");
7408 err = insert_DbOwnerInfo(context, box, node);
7409 if (err) goto leave;
7411 /* Add @since date */
7412 err = tm2datestring(since, &string);
7413 if(err) {
7414 isds_log_message(context,
7415 _("Could not convert `since' argument to ISO date string"));
7416 goto leave;
7418 INSERT_STRING(request, "dbOwnerDisableDate", string);
7419 zfree(string);
7421 /* Add @approval */
7422 err = insert_GExtApproval(context, approval, request);
7423 if (err) goto leave;
7425 /* Send it to server and process response */
7426 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
7427 BAD_CAST "DisableDataBoxExternally", &request,
7428 (xmlChar **) refnumber);
7430 leave:
7431 free(string);
7432 xmlFreeNode(request);
7433 free(service_name_locale);
7434 #else /* not HAVE_LIBCURL */
7435 err = IE_NOTSUP;
7436 #endif
7438 return err;
7442 #if HAVE_LIBCURL
7443 /* Insert struct isds_message data (envelope (recipient data optional) and
7444 * documents into XML tree
7445 * @context is session context
7446 * @outgoing_message is libisds structure with message data
7447 * @create_message is XML CreateMessage or CreateMultipleMessage element
7448 * @process_recipient true for recipient data serialization, false for no
7449 * serialization */
7450 static isds_error insert_envelope_files(struct isds_ctx *context,
7451 const struct isds_message *outgoing_message, xmlNodePtr create_message,
7452 const _Bool process_recipient) {
7454 isds_error err = IE_SUCCESS;
7455 xmlNodePtr envelope, dm_files, node;
7456 xmlChar *string = NULL;
7458 if (!context) return IE_INVALID_CONTEXT;
7459 if (!outgoing_message || !create_message) return IE_INVAL;
7462 /* Build envelope */
7463 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
7464 if (!envelope) {
7465 isds_printf_message(context, _("Could not add dmEnvelope child to "
7466 "%s element"), create_message->name);
7467 return IE_ERROR;
7470 if (!outgoing_message->envelope) {
7471 isds_log_message(context, _("Outgoing message is missing envelope"));
7472 err = IE_INVAL;
7473 goto leave;
7476 /* Insert optional message type */
7477 err = insert_message_type(context, outgoing_message->envelope->dmType,
7478 envelope);
7479 if (err) goto leave;
7481 INSERT_STRING(envelope, "dmSenderOrgUnit",
7482 outgoing_message->envelope->dmSenderOrgUnit);
7483 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
7484 outgoing_message->envelope->dmSenderOrgUnitNum, string);
7486 if (process_recipient) {
7487 if (!outgoing_message->envelope->dbIDRecipient) {
7488 isds_log_message(context,
7489 _("Outgoing message is missing recipient box identifier"));
7490 err = IE_INVAL;
7491 goto leave;
7493 INSERT_STRING(envelope, "dbIDRecipient",
7494 outgoing_message->envelope->dbIDRecipient);
7496 INSERT_STRING(envelope, "dmRecipientOrgUnit",
7497 outgoing_message->envelope->dmRecipientOrgUnit);
7498 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
7499 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
7500 INSERT_STRING(envelope, "dmToHands",
7501 outgoing_message->envelope->dmToHands);
7504 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
7505 "dmAnnotation");
7506 INSERT_STRING(envelope, "dmAnnotation",
7507 outgoing_message->envelope->dmAnnotation);
7509 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
7510 0, 50, "dmRecipientRefNumber");
7511 INSERT_STRING(envelope, "dmRecipientRefNumber",
7512 outgoing_message->envelope->dmRecipientRefNumber);
7514 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
7515 0, 50, "dmSenderRefNumber");
7516 INSERT_STRING(envelope, "dmSenderRefNumber",
7517 outgoing_message->envelope->dmSenderRefNumber);
7519 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
7520 0, 50, "dmRecipientIdent");
7521 INSERT_STRING(envelope, "dmRecipientIdent",
7522 outgoing_message->envelope->dmRecipientIdent);
7524 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
7525 0, 50, "dmSenderIdent");
7526 INSERT_STRING(envelope, "dmSenderIdent",
7527 outgoing_message->envelope->dmSenderIdent);
7529 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
7530 outgoing_message->envelope->dmLegalTitleLaw, string);
7531 INSERT_LONGINT(envelope, "dmLegalTitleYear",
7532 outgoing_message->envelope->dmLegalTitleYear, string);
7533 INSERT_STRING(envelope, "dmLegalTitleSect",
7534 outgoing_message->envelope->dmLegalTitleSect);
7535 INSERT_STRING(envelope, "dmLegalTitlePar",
7536 outgoing_message->envelope->dmLegalTitlePar);
7537 INSERT_STRING(envelope, "dmLegalTitlePoint",
7538 outgoing_message->envelope->dmLegalTitlePoint);
7540 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
7541 outgoing_message->envelope->dmPersonalDelivery);
7542 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
7543 outgoing_message->envelope->dmAllowSubstDelivery);
7545 /* ???: Should we require value for dbEffectiveOVM sender?
7546 * ISDS has default as true */
7547 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
7548 INSERT_BOOLEAN(envelope, "dmOVM",
7549 outgoing_message->envelope->dmPublishOwnID);
7552 /* Append dmFiles */
7553 if (!outgoing_message->documents) {
7554 isds_log_message(context,
7555 _("Outgoing message is missing list of documents"));
7556 err = IE_INVAL;
7557 goto leave;
7559 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
7560 if (!dm_files) {
7561 isds_printf_message(context, _("Could not add dmFiles child to "
7562 "%s element"), create_message->name);
7563 err = IE_ERROR;
7564 goto leave;
7567 /* Check for document hierarchy */
7568 err = _isds_check_documents_hierarchy(context, outgoing_message->documents);
7569 if (err) goto leave;
7571 /* Process each document */
7572 for (struct isds_list *item =
7573 (struct isds_list *) outgoing_message->documents;
7574 item; item = item->next) {
7575 if (!item->data) {
7576 isds_log_message(context,
7577 _("List of documents contains empty item"));
7578 err = IE_INVAL;
7579 goto leave;
7581 /* FIXME: Check for dmFileMetaType and for document references.
7582 * Only first document can be of MAIN type */
7583 err = insert_document(context, (struct isds_document*) item->data,
7584 dm_files);
7586 if (err) goto leave;
7589 leave:
7590 free(string);
7591 return err;
7593 #endif /* HAVE_LIBCURL */
7596 /* Send a message via ISDS to a recipient
7597 * @context is session context
7598 * @outgoing_message is message to send; Some members are mandatory (like
7599 * dbIDRecipient), some are optional and some are irrelevant (especially data
7600 * about sender). Included pointer to isds_list documents must contain at
7601 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
7602 * members will be filled with valid data from ISDS. Exact list of write
7603 * members is subject to change. Currently dmID is changed.
7604 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
7605 isds_error isds_send_message(struct isds_ctx *context,
7606 struct isds_message *outgoing_message) {
7608 isds_error err = IE_SUCCESS;
7609 #if HAVE_LIBCURL
7610 xmlNsPtr isds_ns = NULL;
7611 xmlNodePtr request = NULL;
7612 xmlDocPtr response = NULL;
7613 xmlChar *code = NULL, *message = NULL;
7614 xmlXPathContextPtr xpath_ctx = NULL;
7615 xmlXPathObjectPtr result = NULL;
7616 /*_Bool message_is_complete = 0;*/
7617 #endif
7619 if (!context) return IE_INVALID_CONTEXT;
7620 zfree(context->long_message);
7621 if (!outgoing_message) return IE_INVAL;
7623 #if HAVE_LIBCURL
7624 /* Check if connection is established
7625 * TODO: This check should be done downstairs. */
7626 if (!context->curl) return IE_CONNECTION_CLOSED;
7629 /* Build CreateMessage request */
7630 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
7631 if (!request) {
7632 isds_log_message(context,
7633 _("Could not build CreateMessage request"));
7634 return IE_ERROR;
7636 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7637 if(!isds_ns) {
7638 isds_log_message(context, _("Could not create ISDS name space"));
7639 xmlFreeNode(request);
7640 return IE_ERROR;
7642 xmlSetNs(request, isds_ns);
7644 /* Append envelope and files */
7645 err = insert_envelope_files(context, outgoing_message, request, 1);
7646 if (err) goto leave;
7649 /* Signal we can serialize message since now */
7650 /*message_is_complete = 1;*/
7653 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
7655 /* Sent request */
7656 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
7658 /* Don't' destroy request, we want to provide it to application later */
7660 if (err) {
7661 isds_log(ILF_ISDS, ILL_DEBUG,
7662 _("Processing ISDS response on CreateMessage "
7663 "request failed\n"));
7664 goto leave;
7667 /* Check for response status */
7668 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
7669 &code, &message, NULL);
7670 if (err) {
7671 isds_log(ILF_ISDS, ILL_DEBUG,
7672 _("ISDS response on CreateMessage request "
7673 "is missing status\n"));
7674 goto leave;
7677 /* Request processed, but refused by server or server failed */
7678 if (xmlStrcmp(code, BAD_CAST "0000")) {
7679 char *box_id_locale =
7680 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
7681 char *code_locale = _isds_utf82locale((char*)code);
7682 char *message_locale = _isds_utf82locale((char*)message);
7683 isds_log(ILF_ISDS, ILL_DEBUG,
7684 _("Server did not accept message for %s on CreateMessage "
7685 "request (code=%s, message=%s)\n"),
7686 box_id_locale, code_locale, message_locale);
7687 isds_log_message(context, message_locale);
7688 free(box_id_locale);
7689 free(code_locale);
7690 free(message_locale);
7691 err = IE_ISDS;
7692 goto leave;
7696 /* Extract data */
7697 xpath_ctx = xmlXPathNewContext(response);
7698 if (!xpath_ctx) {
7699 err = IE_ERROR;
7700 goto leave;
7702 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7703 err = IE_ERROR;
7704 goto leave;
7706 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
7707 xpath_ctx);
7708 if (!result) {
7709 err = IE_ERROR;
7710 goto leave;
7712 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7713 isds_log_message(context, _("Missing CreateMessageResponse element"));
7714 err = IE_ISDS;
7715 goto leave;
7717 if (result->nodesetval->nodeNr > 1) {
7718 isds_log_message(context, _("Multiple CreateMessageResponse element"));
7719 err = IE_ISDS;
7720 goto leave;
7722 xpath_ctx->node = result->nodesetval->nodeTab[0];
7723 xmlXPathFreeObject(result); result = NULL;
7725 if (outgoing_message->envelope->dmID) {
7726 free(outgoing_message->envelope->dmID);
7727 outgoing_message->envelope->dmID = NULL;
7729 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
7730 if (!outgoing_message->envelope->dmID) {
7731 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
7732 "but did not return assigned message ID\n"));
7735 leave:
7736 /* TODO: Serialize message into structure member raw */
7737 /* XXX: Each web service transport message in different format.
7738 * Therefore it's not possible to save them directly.
7739 * To save them, one must figure out common format.
7740 * We can leave it on application, or we can implement the ESS format. */
7741 /*if (message_is_complete) {
7742 if (outgoing_message->envelope->dmID) {
7744 /* Add assigned message ID as first child*/
7745 /*xmlNodePtr dmid_text = xmlNewText(
7746 (xmlChar *) outgoing_message->envelope->dmID);
7747 if (!dmid_text) goto serialization_failed;
7749 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
7750 BAD_CAST "dmID");
7751 if (!dmid_element) {
7752 xmlFreeNode(dmid_text);
7753 goto serialization_failed;
7756 xmlNodePtr dmid_element_with_text =
7757 xmlAddChild(dmid_element, dmid_text);
7758 if (!dmid_element_with_text) {
7759 xmlFreeNode(dmid_element);
7760 xmlFreeNode(dmid_text);
7761 goto serialization_failed;
7764 node = xmlAddPrevSibling(envelope->childern,
7765 dmid_element_with_text);
7766 if (!node) {
7767 xmlFreeNodeList(dmid_element_with_text);
7768 goto serialization_failed;
7772 /* Serialize message with ID into raw */
7773 /*buffer = serialize_element(envelope)*/
7774 /* }
7776 serialization_failed:
7780 /* Clean up */
7781 xmlXPathFreeObject(result);
7782 xmlXPathFreeContext(xpath_ctx);
7784 free(code);
7785 free(message);
7786 xmlFreeDoc(response);
7787 xmlFreeNode(request);
7789 if (!err)
7790 isds_log(ILF_ISDS, ILL_DEBUG,
7791 _("CreateMessage request processed by server "
7792 "successfully.\n"));
7793 #else /* not HAVE_LIBCURL */
7794 err = IE_NOTSUP;
7795 #endif
7797 return err;
7801 /* Send a message via ISDS to a multiple recipients
7802 * @context is session context
7803 * @outgoing_message is message to send; Some members are mandatory,
7804 * some are optional and some are irrelevant (especially data
7805 * about sender). Data about recipient will be substituted by ISDS from
7806 * @copies. Included pointer to isds_list documents must
7807 * contain at least one document of FILEMETATYPE_MAIN.
7808 * @copies is list of isds_message_copy structures addressing all desired
7809 * recipients. This is read-write structure, some members will be filled with
7810 * valid data from ISDS (message IDs, error codes, error descriptions).
7811 * @return
7812 * ISDS_SUCCESS if all messages have been sent
7813 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
7814 * succeeded messages can be identified by copies->data->error),
7815 * or other error code if something other goes wrong. */
7816 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
7817 const struct isds_message *outgoing_message,
7818 struct isds_list *copies) {
7820 isds_error err = IE_SUCCESS;
7821 #if HAVE_LIBCURL
7822 isds_error append_err;
7823 xmlNsPtr isds_ns = NULL;
7824 xmlNodePtr request = NULL, recipients, recipient, node;
7825 struct isds_list *item;
7826 struct isds_message_copy *copy;
7827 xmlDocPtr response = NULL;
7828 xmlChar *code = NULL, *message = NULL;
7829 xmlXPathContextPtr xpath_ctx = NULL;
7830 xmlXPathObjectPtr result = NULL;
7831 xmlChar *string = NULL;
7832 int i;
7833 #endif
7835 if (!context) return IE_INVALID_CONTEXT;
7836 zfree(context->long_message);
7837 if (!outgoing_message || !copies) return IE_INVAL;
7839 #if HAVE_LIBCURL
7840 /* Check if connection is established
7841 * TODO: This check should be done downstairs. */
7842 if (!context->curl) return IE_CONNECTION_CLOSED;
7845 /* Build CreateMultipleMessage request */
7846 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
7847 if (!request) {
7848 isds_log_message(context,
7849 _("Could not build CreateMultipleMessage request"));
7850 return IE_ERROR;
7852 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7853 if(!isds_ns) {
7854 isds_log_message(context, _("Could not create ISDS name space"));
7855 xmlFreeNode(request);
7856 return IE_ERROR;
7858 xmlSetNs(request, isds_ns);
7861 /* Build recipients */
7862 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
7863 if (!recipients) {
7864 isds_log_message(context, _("Could not add dmRecipients child to "
7865 "CreateMultipleMessage element"));
7866 xmlFreeNode(request);
7867 return IE_ERROR;
7870 /* Insert each recipient */
7871 for (item = copies; item; item = item->next) {
7872 copy = (struct isds_message_copy *) item->data;
7873 if (!copy) {
7874 isds_log_message(context,
7875 _("`copies' list item contains empty data"));
7876 err = IE_INVAL;
7877 goto leave;
7880 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
7881 if (!recipient) {
7882 isds_log_message(context, _("Could not add dmRecipient child to "
7883 "dmRecipients element"));
7884 err = IE_ERROR;
7885 goto leave;
7888 if (!copy->dbIDRecipient) {
7889 isds_log_message(context,
7890 _("Message copy is missing recipient box identifier"));
7891 err = IE_INVAL;
7892 goto leave;
7894 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
7895 INSERT_STRING(recipient, "dmRecipientOrgUnit",
7896 copy->dmRecipientOrgUnit);
7897 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
7898 copy->dmRecipientOrgUnitNum, string);
7899 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
7902 /* Append envelope and files */
7903 err = insert_envelope_files(context, outgoing_message, request, 0);
7904 if (err) goto leave;
7907 isds_log(ILF_ISDS, ILL_DEBUG,
7908 _("Sending CreateMultipleMessage request to ISDS\n"));
7910 /* Sent request */
7911 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
7912 if (err) {
7913 isds_log(ILF_ISDS, ILL_DEBUG,
7914 _("Processing ISDS response on CreateMultipleMessage "
7915 "request failed\n"));
7916 goto leave;
7919 /* Check for response status */
7920 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
7921 &code, &message, NULL);
7922 if (err) {
7923 isds_log(ILF_ISDS, ILL_DEBUG,
7924 _("ISDS response on CreateMultipleMessage request "
7925 "is missing status\n"));
7926 goto leave;
7929 /* Request processed, but some copies failed */
7930 if (!xmlStrcmp(code, BAD_CAST "0004")) {
7931 char *box_id_locale =
7932 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
7933 char *code_locale = _isds_utf82locale((char*)code);
7934 char *message_locale = _isds_utf82locale((char*)message);
7935 isds_log(ILF_ISDS, ILL_DEBUG,
7936 _("Server did accept message for multiple recipients "
7937 "on CreateMultipleMessage request but delivery to "
7938 "some of them failed (code=%s, message=%s)\n"),
7939 box_id_locale, code_locale, message_locale);
7940 isds_log_message(context, message_locale);
7941 free(box_id_locale);
7942 free(code_locale);
7943 free(message_locale);
7944 err = IE_PARTIAL_SUCCESS;
7947 /* Request refused by server as whole */
7948 else if (xmlStrcmp(code, BAD_CAST "0000")) {
7949 char *box_id_locale =
7950 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
7951 char *code_locale = _isds_utf82locale((char*)code);
7952 char *message_locale = _isds_utf82locale((char*)message);
7953 isds_log(ILF_ISDS, ILL_DEBUG,
7954 _("Server did not accept message for multiple recipients "
7955 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
7956 box_id_locale, code_locale, message_locale);
7957 isds_log_message(context, message_locale);
7958 free(box_id_locale);
7959 free(code_locale);
7960 free(message_locale);
7961 err = IE_ISDS;
7962 goto leave;
7966 /* Extract data */
7967 xpath_ctx = xmlXPathNewContext(response);
7968 if (!xpath_ctx) {
7969 err = IE_ERROR;
7970 goto leave;
7972 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7973 err = IE_ERROR;
7974 goto leave;
7976 result = xmlXPathEvalExpression(
7977 BAD_CAST "/isds:CreateMultipleMessageResponse"
7978 "/isds:dmMultipleStatus/isds:dmSingleStatus",
7979 xpath_ctx);
7980 if (!result) {
7981 err = IE_ERROR;
7982 goto leave;
7984 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7985 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
7986 err = IE_ISDS;
7987 goto leave;
7990 /* Extract message ID and delivery status for each copy */
7991 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
7992 item = item->next, i++) {
7993 copy = (struct isds_message_copy *) item->data;
7994 xpath_ctx->node = result->nodesetval->nodeTab[i];
7996 append_err = append_TMStatus(context, copy, xpath_ctx);
7997 if (append_err) {
7998 err = append_err;
7999 goto leave;
8002 if (item || i < result->nodesetval->nodeNr) {
8003 isds_printf_message(context, _("ISDS returned unexpected number of "
8004 "message copy delivery states: %d"),
8005 result->nodesetval->nodeNr);
8006 err = IE_ISDS;
8007 goto leave;
8011 leave:
8012 /* Clean up */
8013 free(string);
8014 xmlXPathFreeObject(result);
8015 xmlXPathFreeContext(xpath_ctx);
8017 free(code);
8018 free(message);
8019 xmlFreeDoc(response);
8020 xmlFreeNode(request);
8022 if (!err)
8023 isds_log(ILF_ISDS, ILL_DEBUG,
8024 _("CreateMultipleMessageResponse request processed by server "
8025 "successfully.\n"));
8026 #else /* not HAVE_LIBCURL */
8027 err = IE_NOTSUP;
8028 #endif
8030 return err;
8034 /* Get list of messages. This is common core for getting sent or received
8035 * messages.
8036 * Any criterion argument can be NULL, if you don't care about it.
8037 * @context is session context. Must not be NULL.
8038 * @outgoing_direction is true if you want list of outgoing messages,
8039 * it's false if you want incoming messages.
8040 * @from_time is minimal time and date of message sending inclusive.
8041 * @to_time is maximal time and date of message sending inclusive
8042 * @organization_unit_number is number of sender/recipient respectively.
8043 * @status_filter is bit field of isds_message_status values. Use special
8044 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8045 * all values, you can use bit-wise arithmetic if you want.)
8046 * @offset is index of first message we are interested in. First message is 1.
8047 * Set to 0 (or 1) if you don't care.
8048 * @number is maximal length of list you want to get as input value, outputs
8049 * number of messages matching these criteria. Can be NULL if you don't care
8050 * (applies to output value either).
8051 * @messages is automatically reallocated list of isds_message's. Be ware that
8052 * it returns only brief overview (envelope and some other fields) about each
8053 * message, not the complete message. FIXME: Specify exact fields.
8054 * The list is sorted by delivery time in ascending order.
8055 * Use NULL if you don't care about don't need the data (useful if you want to
8056 * know only the @number). If you provide &NULL, list will be allocated on
8057 * heap, if you provide pointer to non-NULL, list will be freed automatically
8058 * at first. Also in case of error the list will be NULLed.
8059 * @return IE_SUCCESS or appropriate error code. */
8060 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
8061 _Bool outgoing_direction,
8062 const struct timeval *from_time, const struct timeval *to_time,
8063 const long int *organization_unit_number,
8064 const unsigned int status_filter,
8065 const unsigned long int offset, unsigned long int *number,
8066 struct isds_list **messages) {
8068 isds_error err = IE_SUCCESS;
8069 #if HAVE_LIBCURL
8070 xmlNsPtr isds_ns = NULL;
8071 xmlNodePtr request = NULL, node;
8072 xmlDocPtr response = NULL;
8073 xmlChar *code = NULL, *message = NULL;
8074 xmlXPathContextPtr xpath_ctx = NULL;
8075 xmlXPathObjectPtr result = NULL;
8076 xmlChar *string = NULL;
8077 long unsigned int count = 0;
8078 #endif
8080 if (!context) return IE_INVALID_CONTEXT;
8081 zfree(context->long_message);
8083 /* Free former message list if any */
8084 if (messages) isds_list_free(messages);
8086 #if HAVE_LIBCURL
8087 /* Check if connection is established
8088 * TODO: This check should be done downstairs. */
8089 if (!context->curl) return IE_CONNECTION_CLOSED;
8091 /* Build GetListOf*Messages request */
8092 request = xmlNewNode(NULL,
8093 (outgoing_direction) ?
8094 BAD_CAST "GetListOfSentMessages" :
8095 BAD_CAST "GetListOfReceivedMessages"
8097 if (!request) {
8098 isds_log_message(context,
8099 (outgoing_direction) ?
8100 _("Could not build GetListOfSentMessages request") :
8101 _("Could not build GetListOfReceivedMessages request")
8103 return IE_ERROR;
8105 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8106 if(!isds_ns) {
8107 isds_log_message(context, _("Could not create ISDS name space"));
8108 xmlFreeNode(request);
8109 return IE_ERROR;
8111 xmlSetNs(request, isds_ns);
8114 if (from_time) {
8115 err = timeval2timestring(from_time, &string);
8116 if (err) goto leave;
8118 INSERT_STRING(request, "dmFromTime", string);
8119 free(string); string = NULL;
8121 if (to_time) {
8122 err = timeval2timestring(to_time, &string);
8123 if (err) goto leave;
8125 INSERT_STRING(request, "dmToTime", string);
8126 free(string); string = NULL;
8128 if (outgoing_direction) {
8129 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
8130 organization_unit_number, string);
8131 } else {
8132 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
8133 organization_unit_number, string);
8136 if (status_filter > MESSAGESTATE_ANY) {
8137 isds_printf_message(context,
8138 _("Invalid message state filter value: %ld"), status_filter);
8139 err = IE_INVAL;
8140 goto leave;
8142 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
8144 if (offset > 0 ) {
8145 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
8146 } else {
8147 INSERT_STRING(request, "dmOffset", "1");
8150 /* number 0 means no limit */
8151 if (number && *number == 0) {
8152 INSERT_STRING(request, "dmLimit", NULL);
8153 } else {
8154 INSERT_ULONGINT(request, "dmLimit", number, string);
8158 isds_log(ILF_ISDS, ILL_DEBUG,
8159 (outgoing_direction) ?
8160 _("Sending GetListOfSentMessages request to ISDS\n") :
8161 _("Sending GetListOfReceivedMessages request to ISDS\n")
8164 /* Sent request */
8165 err = isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
8166 xmlFreeNode(request); request = NULL;
8168 if (err) {
8169 isds_log(ILF_ISDS, ILL_DEBUG,
8170 (outgoing_direction) ?
8171 _("Processing ISDS response on GetListOfSentMessages "
8172 "request failed\n") :
8173 _("Processing ISDS response on GetListOfReceivedMessages "
8174 "request failed\n")
8176 goto leave;
8179 /* Check for response status */
8180 err = isds_response_status(context, SERVICE_DM_INFO, response,
8181 &code, &message, NULL);
8182 if (err) {
8183 isds_log(ILF_ISDS, ILL_DEBUG,
8184 (outgoing_direction) ?
8185 _("ISDS response on GetListOfSentMessages request "
8186 "is missing status\n") :
8187 _("ISDS response on GetListOfReceivedMessages request "
8188 "is missing status\n")
8190 goto leave;
8193 /* Request processed, but nothing found */
8194 if (xmlStrcmp(code, BAD_CAST "0000")) {
8195 char *code_locale = _isds_utf82locale((char*)code);
8196 char *message_locale = _isds_utf82locale((char*)message);
8197 isds_log(ILF_ISDS, ILL_DEBUG,
8198 (outgoing_direction) ?
8199 _("Server refused GetListOfSentMessages request "
8200 "(code=%s, message=%s)\n") :
8201 _("Server refused GetListOfReceivedMessages request "
8202 "(code=%s, message=%s)\n"),
8203 code_locale, message_locale);
8204 isds_log_message(context, message_locale);
8205 free(code_locale);
8206 free(message_locale);
8207 err = IE_ISDS;
8208 goto leave;
8212 /* Extract data */
8213 xpath_ctx = xmlXPathNewContext(response);
8214 if (!xpath_ctx) {
8215 err = IE_ERROR;
8216 goto leave;
8218 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8219 err = IE_ERROR;
8220 goto leave;
8222 result = xmlXPathEvalExpression(
8223 (outgoing_direction) ?
8224 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
8225 "isds:dmRecords/isds:dmRecord" :
8226 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
8227 "isds:dmRecords/isds:dmRecord",
8228 xpath_ctx);
8229 if (!result) {
8230 err = IE_ERROR;
8231 goto leave;
8234 /* Fill output arguments in */
8235 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8236 struct isds_envelope *envelope;
8237 struct isds_list *item = NULL, *last_item = NULL;
8239 for (count = 0; count < result->nodesetval->nodeNr; count++) {
8240 /* Create new message */
8241 item = calloc(1, sizeof(*item));
8242 if (!item) {
8243 err = IE_NOMEM;
8244 goto leave;
8246 item->destructor = (void(*)(void**)) &isds_message_free;
8247 item->data = calloc(1, sizeof(struct isds_message));
8248 if (!item->data) {
8249 isds_list_free(&item);
8250 err = IE_NOMEM;
8251 goto leave;
8254 /* Extract envelope data */
8255 xpath_ctx->node = result->nodesetval->nodeTab[count];
8256 envelope = NULL;
8257 err = extract_DmRecord(context, &envelope, xpath_ctx);
8258 if (err) {
8259 isds_list_free(&item);
8260 goto leave;
8263 /* Attach extracted envelope */
8264 ((struct isds_message *) item->data)->envelope = envelope;
8266 /* Append new message into the list */
8267 if (!*messages) {
8268 *messages = last_item = item;
8269 } else {
8270 last_item->next = item;
8271 last_item = item;
8275 if (number) *number = count;
8277 leave:
8278 if (err) {
8279 isds_list_free(messages);
8282 free(string);
8283 xmlXPathFreeObject(result);
8284 xmlXPathFreeContext(xpath_ctx);
8286 free(code);
8287 free(message);
8288 xmlFreeDoc(response);
8289 xmlFreeNode(request);
8291 if (!err)
8292 isds_log(ILF_ISDS, ILL_DEBUG,
8293 (outgoing_direction) ?
8294 _("GetListOfSentMessages request processed by server "
8295 "successfully.\n") :
8296 _("GetListOfReceivedMessages request processed by server "
8297 "successfully.\n")
8299 #else /* not HAVE_LIBCURL */
8300 err = IE_NOTSUP;
8301 #endif
8302 return err;
8306 /* Get list of outgoing (already sent) messages.
8307 * Any criterion argument can be NULL, if you don't care about it.
8308 * @context is session context. Must not be NULL.
8309 * @from_time is minimal time and date of message sending inclusive.
8310 * @to_time is maximal time and date of message sending inclusive
8311 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
8312 * @status_filter is bit field of isds_message_status values. Use special
8313 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8314 * all values, you can use bit-wise arithmetic if you want.)
8315 * @offset is index of first message we are interested in. First message is 1.
8316 * Set to 0 (or 1) if you don't care.
8317 * @number is maximal length of list you want to get as input value, outputs
8318 * number of messages matching these criteria. Can be NULL if you don't care
8319 * (applies to output value either).
8320 * @messages is automatically reallocated list of isds_message's. Be ware that
8321 * it returns only brief overview (envelope and some other fields) about each
8322 * message, not the complete message. FIXME: Specify exact fields.
8323 * The list is sorted by delivery time in ascending order.
8324 * Use NULL if you don't care about the meta data (useful if you want to know
8325 * only the @number). If you provide &NULL, list will be allocated on heap,
8326 * if you provide pointer to non-NULL, list will be freed automatically at
8327 * first. Also in case of error the list will be NULLed.
8328 * @return IE_SUCCESS or appropriate error code. */
8329 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
8330 const struct timeval *from_time, const struct timeval *to_time,
8331 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
8332 const unsigned long int offset, unsigned long int *number,
8333 struct isds_list **messages) {
8335 return isds_get_list_of_messages(
8336 context, 1,
8337 from_time, to_time, dmSenderOrgUnitNum, status_filter,
8338 offset, number,
8339 messages);
8343 /* Get list of incoming (addressed to you) messages.
8344 * Any criterion argument can be NULL, if you don't care about it.
8345 * @context is session context. Must not be NULL.
8346 * @from_time is minimal time and date of message sending inclusive.
8347 * @to_time is maximal time and date of message sending inclusive
8348 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
8349 * @status_filter is bit field of isds_message_status values. Use special
8350 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8351 * all values, you can use bit-wise arithmetic if you want.)
8352 * @offset is index of first message we are interested in. First message is 1.
8353 * Set to 0 (or 1) if you don't care.
8354 * @number is maximal length of list you want to get as input value, outputs
8355 * number of messages matching these criteria. Can be NULL if you don't care
8356 * (applies to output value either).
8357 * @messages is automatically reallocated list of isds_message's. Be ware that
8358 * it returns only brief overview (envelope and some other fields) about each
8359 * message, not the complete message. FIXME: Specify exact fields.
8360 * Use NULL if you don't care about the meta data (useful if you want to know
8361 * only the @number). If you provide &NULL, list will be allocated on heap,
8362 * if you provide pointer to non-NULL, list will be freed automatically at
8363 * first. Also in case of error the list will be NULLed.
8364 * @return IE_SUCCESS or appropriate error code. */
8365 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
8366 const struct timeval *from_time, const struct timeval *to_time,
8367 const long int *dmRecipientOrgUnitNum,
8368 const unsigned int status_filter,
8369 const unsigned long int offset, unsigned long int *number,
8370 struct isds_list **messages) {
8372 return isds_get_list_of_messages(
8373 context, 0,
8374 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
8375 offset, number,
8376 messages);
8380 /* Get list of sent message state changes.
8381 * Any criterion argument can be NULL, if you don't care about it.
8382 * @context is session context. Must not be NULL.
8383 * @from_time is minimal time and date of status changes inclusive
8384 * @to_time is maximal time and date of status changes inclusive
8385 * @changed_states is automatically reallocated list of
8386 * isds_message_status_change's. If you provide &NULL, list will be allocated
8387 * on heap, if you provide pointer to non-NULL, list will be freed
8388 * automatically at first. Also in case of error the list will be NULLed.
8389 * XXX: The list item ordering is not specified.
8390 * XXX: Server provides only `recent' changes.
8391 * @return IE_SUCCESS or appropriate error code. */
8392 isds_error isds_get_list_of_sent_message_state_changes(
8393 struct isds_ctx *context,
8394 const struct timeval *from_time, const struct timeval *to_time,
8395 struct isds_list **changed_states) {
8397 isds_error err = IE_SUCCESS;
8398 #if HAVE_LIBCURL
8399 xmlNsPtr isds_ns = NULL;
8400 xmlNodePtr request = NULL, node;
8401 xmlDocPtr response = NULL;
8402 xmlXPathContextPtr xpath_ctx = NULL;
8403 xmlXPathObjectPtr result = NULL;
8404 xmlChar *string = NULL;
8405 long unsigned int count = 0;
8406 #endif
8408 if (!context) return IE_INVALID_CONTEXT;
8409 zfree(context->long_message);
8411 /* Free former message list if any */
8412 isds_list_free(changed_states);
8414 #if HAVE_LIBCURL
8415 /* Check if connection is established
8416 * TODO: This check should be done downstairs. */
8417 if (!context->curl) return IE_CONNECTION_CLOSED;
8419 /* Build GetMessageStateChanges request */
8420 request = xmlNewNode(NULL, BAD_CAST "GetMessageStateChanges");
8421 if (!request) {
8422 isds_log_message(context,
8423 _("Could not build GetMessageStateChanges request"));
8424 return IE_ERROR;
8426 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8427 if(!isds_ns) {
8428 isds_log_message(context, _("Could not create ISDS name space"));
8429 xmlFreeNode(request);
8430 return IE_ERROR;
8432 xmlSetNs(request, isds_ns);
8435 if (from_time) {
8436 err = timeval2timestring(from_time, &string);
8437 if (err) goto leave;
8439 INSERT_STRING(request, "dmFromTime", string);
8440 zfree(string);
8442 if (to_time) {
8443 err = timeval2timestring(to_time, &string);
8444 if (err) goto leave;
8446 INSERT_STRING(request, "dmToTime", string);
8447 zfree(string);
8450 /* Sent request */
8451 err = send_destroy_request_check_response(context,
8452 SERVICE_DM_INFO, BAD_CAST "GetMessageStateChanges", &request,
8453 &response, NULL);
8454 if (err) goto leave;
8457 /* Extract data */
8458 xpath_ctx = xmlXPathNewContext(response);
8459 if (!xpath_ctx) {
8460 err = IE_ERROR;
8461 goto leave;
8463 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8464 err = IE_ERROR;
8465 goto leave;
8467 result = xmlXPathEvalExpression(
8468 BAD_CAST "/isds:GetMessageStateChangesResponse/"
8469 "isds:dmRecords/isds:dmRecord", xpath_ctx);
8470 if (!result) {
8471 err = IE_ERROR;
8472 goto leave;
8475 /* Fill output arguments in */
8476 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8477 struct isds_list *item = NULL, *last_item = NULL;
8479 for (count = 0; count < result->nodesetval->nodeNr; count++) {
8480 /* Create new status change */
8481 item = calloc(1, sizeof(*item));
8482 if (!item) {
8483 err = IE_NOMEM;
8484 goto leave;
8486 item->destructor =
8487 (void(*)(void**)) &isds_message_status_change_free;
8489 /* Extract message status change */
8490 xpath_ctx->node = result->nodesetval->nodeTab[count];
8491 err = extract_StateChangesRecord(context,
8492 (struct isds_message_status_change **) &item->data,
8493 xpath_ctx);
8494 if (err) {
8495 isds_list_free(&item);
8496 goto leave;
8499 /* Append new message status change into the list */
8500 if (!*changed_states) {
8501 *changed_states = last_item = item;
8502 } else {
8503 last_item->next = item;
8504 last_item = item;
8509 leave:
8510 if (err) {
8511 isds_list_free(changed_states);
8514 free(string);
8515 xmlXPathFreeObject(result);
8516 xmlXPathFreeContext(xpath_ctx);
8517 xmlFreeDoc(response);
8518 xmlFreeNode(request);
8520 if (!err)
8521 isds_log(ILF_ISDS, ILL_DEBUG,
8522 _("GetMessageStateChanges request processed by server "
8523 "successfully.\n"));
8524 #else /* not HAVE_LIBCURL */
8525 err = IE_NOTSUP;
8526 #endif
8527 return err;
8531 #if HAVE_LIBCURL
8532 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
8533 * code
8534 * @context is session context
8535 * @service is ISDS WS service handler
8536 * @service_name is name of SERVICE_DM_OPERATIONS
8537 * @message_id is message ID to send as service argument to ISDS
8538 * @response is server SOAP body response as XML document
8539 * @raw_response is automatically reallocated bit stream with response body. Use
8540 * NULL if you don't care
8541 * @raw_response_length is size of @raw_response in bytes
8542 * @code is ISDS status code
8543 * @status_message is ISDS status message
8544 * @return error coded from lower layer, context message will be set up
8545 * appropriately. */
8546 static isds_error build_send_check_message_request(struct isds_ctx *context,
8547 const isds_service service, const xmlChar *service_name,
8548 const char *message_id,
8549 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
8550 xmlChar **code, xmlChar **status_message) {
8552 isds_error err = IE_SUCCESS;
8553 char *service_name_locale = NULL, *message_id_locale = NULL;
8554 xmlNodePtr request = NULL, node;
8555 xmlNsPtr isds_ns = NULL;
8557 if (!context) return IE_INVALID_CONTEXT;
8558 if (!service_name || !message_id) return IE_INVAL;
8559 if (!response || !code || !status_message) return IE_INVAL;
8560 if (!raw_response_length && raw_response) return IE_INVAL;
8562 /* Free output argument */
8563 xmlFreeDoc(*response); *response = NULL;
8564 if (raw_response) zfree(*raw_response);
8565 free(*code);
8566 free(*status_message);
8569 /* Check if connection is established
8570 * TODO: This check should be done downstairs. */
8571 if (!context->curl) return IE_CONNECTION_CLOSED;
8573 service_name_locale = _isds_utf82locale((char*)service_name);
8574 message_id_locale = _isds_utf82locale(message_id);
8575 if (!service_name_locale || !message_id_locale) {
8576 err = IE_NOMEM;
8577 goto leave;
8580 /* Build request */
8581 request = xmlNewNode(NULL, service_name);
8582 if (!request) {
8583 isds_printf_message(context,
8584 _("Could not build %s request for %s message ID"),
8585 service_name_locale, message_id_locale);
8586 err = IE_ERROR;
8587 goto leave;
8589 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8590 if(!isds_ns) {
8591 isds_log_message(context, _("Could not create ISDS name space"));
8592 err = IE_ERROR;
8593 goto leave;
8595 xmlSetNs(request, isds_ns);
8598 /* Add requested ID */
8599 err = validate_message_id_length(context, (xmlChar *) message_id);
8600 if (err) goto leave;
8601 INSERT_STRING(request, "dmID", message_id);
8604 isds_log(ILF_ISDS, ILL_DEBUG,
8605 _("Sending %s request for %s message ID to ISDS\n"),
8606 service_name_locale, message_id_locale);
8608 /* Send request */
8609 err = isds(context, service, request, response,
8610 raw_response, raw_response_length);
8611 xmlFreeNode(request); request = NULL;
8613 if (err) {
8614 isds_log(ILF_ISDS, ILL_DEBUG,
8615 _("Processing ISDS response on %s request failed\n"),
8616 service_name_locale);
8617 goto leave;
8620 /* Check for response status */
8621 err = isds_response_status(context, service, *response,
8622 code, status_message, NULL);
8623 if (err) {
8624 isds_log(ILF_ISDS, ILL_DEBUG,
8625 _("ISDS response on %s request is missing status\n"),
8626 service_name_locale);
8627 goto leave;
8630 /* Request processed, but nothing found */
8631 if (xmlStrcmp(*code, BAD_CAST "0000")) {
8632 char *code_locale = _isds_utf82locale((char*) *code);
8633 char *status_message_locale = _isds_utf82locale((char*) *status_message);
8634 isds_log(ILF_ISDS, ILL_DEBUG,
8635 _("Server refused %s request for %s message ID "
8636 "(code=%s, message=%s)\n"),
8637 service_name_locale, message_id_locale,
8638 code_locale, status_message_locale);
8639 isds_log_message(context, status_message_locale);
8640 free(code_locale);
8641 free(status_message_locale);
8642 err = IE_ISDS;
8643 goto leave;
8646 leave:
8647 free(message_id_locale);
8648 free(service_name_locale);
8649 xmlFreeNode(request);
8650 return err;
8654 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
8655 * signed data and free ISDS response.
8656 * @context is session context
8657 * @message_id is UTF-8 encoded message ID for logging purpose
8658 * @response is parsed XML document. It will be freed and NULLed in the middle
8659 * of function run to save memory. This is not guaranteed in case of error.
8660 * @request_name is name of ISDS request used to construct response root
8661 * element name and for logging purpose.
8662 * @raw is reallocated output buffer with DER encoded CMS data
8663 * @raw_length is size of @raw buffer in bytes
8664 * @returns standard error codes, in case of error, @raw will be freed and
8665 * NULLed, @response sometimes. */
8666 static isds_error find_extract_signed_data_free_response(
8667 struct isds_ctx *context, const xmlChar *message_id,
8668 xmlDocPtr *response, const xmlChar *request_name,
8669 void **raw, size_t *raw_length) {
8671 isds_error err = IE_SUCCESS;
8672 char *xpath_expression = NULL;
8673 xmlXPathContextPtr xpath_ctx = NULL;
8674 xmlXPathObjectPtr result = NULL;
8675 char *encoded_structure = NULL;
8677 if (!context) return IE_INVALID_CONTEXT;
8678 if (!raw) return IE_INVAL;
8679 zfree(*raw);
8680 if (!message_id || !response || !*response || !request_name || !raw_length)
8681 return IE_INVAL;
8683 /* Build XPath expression */
8684 xpath_expression = _isds_astrcat3("/isds:", (char *) request_name,
8685 "Response/isds:dmSignature");
8686 if (!xpath_expression) return IE_NOMEM;
8688 /* Extract data */
8689 xpath_ctx = xmlXPathNewContext(*response);
8690 if (!xpath_ctx) {
8691 err = IE_ERROR;
8692 goto leave;
8694 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8695 err = IE_ERROR;
8696 goto leave;
8698 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
8699 if (!result) {
8700 err = IE_ERROR;
8701 goto leave;
8703 /* Empty response */
8704 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8705 char *message_id_locale = _isds_utf82locale((char*) message_id);
8706 isds_printf_message(context,
8707 _("Server did not return any signed data for message ID `%s' "
8708 "on %s request"),
8709 message_id_locale, request_name);
8710 free(message_id_locale);
8711 err = IE_ISDS;
8712 goto leave;
8714 /* More responses */
8715 if (result->nodesetval->nodeNr > 1) {
8716 char *message_id_locale = _isds_utf82locale((char*) message_id);
8717 isds_printf_message(context,
8718 _("Server did return more signed data for message ID `%s' "
8719 "on %s request"),
8720 message_id_locale, request_name);
8721 free(message_id_locale);
8722 err = IE_ISDS;
8723 goto leave;
8725 /* One response */
8726 xpath_ctx->node = result->nodesetval->nodeTab[0];
8728 /* Extract PKCS#7 structure */
8729 EXTRACT_STRING(".", encoded_structure);
8730 if (!encoded_structure) {
8731 isds_log_message(context, _("dmSignature element is empty"));
8734 /* Here we have delivery info as standalone CMS in encoded_structure.
8735 * We don't need any other data, free them: */
8736 xmlXPathFreeObject(result); result = NULL;
8737 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
8738 xmlFreeDoc(*response); *response = NULL;
8741 /* Decode PKCS#7 to DER format */
8742 *raw_length = _isds_b64decode(encoded_structure, raw);
8743 if (*raw_length == (size_t) -1) {
8744 isds_log_message(context,
8745 _("Error while Base64-decoding PKCS#7 structure"));
8746 err = IE_ERROR;
8747 goto leave;
8750 leave:
8751 if (err) {
8752 zfree(*raw);
8753 raw_length = 0;
8756 free(encoded_structure);
8757 xmlXPathFreeObject(result);
8758 xmlXPathFreeContext(xpath_ctx);
8759 free(xpath_expression);
8761 return err;
8763 #endif /* HAVE_LIBCURL */
8766 /* Download incoming message envelope identified by ID.
8767 * @context is session context
8768 * @message_id is message identifier (you can get them from
8769 * isds_get_list_of_received_messages())
8770 * @message is automatically reallocated message retrieved from ISDS.
8771 * It will miss documents per se. Use isds_get_received_message(), if you are
8772 * interested in documents (content) too.
8773 * Returned hash and timestamp require documents to be verifiable. */
8774 isds_error isds_get_received_envelope(struct isds_ctx *context,
8775 const char *message_id, struct isds_message **message) {
8777 isds_error err = IE_SUCCESS;
8778 #if HAVE_LIBCURL
8779 xmlDocPtr response = NULL;
8780 xmlChar *code = NULL, *status_message = NULL;
8781 xmlXPathContextPtr xpath_ctx = NULL;
8782 xmlXPathObjectPtr result = NULL;
8783 #endif
8785 if (!context) return IE_INVALID_CONTEXT;
8786 zfree(context->long_message);
8788 /* Free former message if any */
8789 if (!message) return IE_INVAL;
8790 isds_message_free(message);
8792 #if HAVE_LIBCURL
8793 /* Do request and check for success */
8794 err = build_send_check_message_request(context, SERVICE_DM_INFO,
8795 BAD_CAST "MessageEnvelopeDownload", message_id,
8796 &response, NULL, NULL, &code, &status_message);
8797 if (err) goto leave;
8799 /* Extract data */
8800 xpath_ctx = xmlXPathNewContext(response);
8801 if (!xpath_ctx) {
8802 err = IE_ERROR;
8803 goto leave;
8805 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8806 err = IE_ERROR;
8807 goto leave;
8809 result = xmlXPathEvalExpression(
8810 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
8811 "isds:dmReturnedMessageEnvelope",
8812 xpath_ctx);
8813 if (!result) {
8814 err = IE_ERROR;
8815 goto leave;
8817 /* Empty response */
8818 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8819 char *message_id_locale = _isds_utf82locale((char*) message_id);
8820 isds_printf_message(context,
8821 _("Server did not return any envelope for ID `%s' "
8822 "on MessageEnvelopeDownload request"), message_id_locale);
8823 free(message_id_locale);
8824 err = IE_ISDS;
8825 goto leave;
8827 /* More envelops */
8828 if (result->nodesetval->nodeNr > 1) {
8829 char *message_id_locale = _isds_utf82locale((char*) message_id);
8830 isds_printf_message(context,
8831 _("Server did return more envelopes for ID `%s' "
8832 "on MessageEnvelopeDownload request"), message_id_locale);
8833 free(message_id_locale);
8834 err = IE_ISDS;
8835 goto leave;
8837 /* One message */
8838 xpath_ctx->node = result->nodesetval->nodeTab[0];
8840 /* Extract the envelope (= message without documents, hence 0) */
8841 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
8842 if (err) goto leave;
8844 /* Save XML blob */
8845 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
8846 &(*message)->raw_length);
8848 leave:
8849 if (err) {
8850 isds_message_free(message);
8853 xmlXPathFreeObject(result);
8854 xmlXPathFreeContext(xpath_ctx);
8856 free(code);
8857 free(status_message);
8858 if (!*message || !(*message)->xml) {
8859 xmlFreeDoc(response);
8862 if (!err)
8863 isds_log(ILF_ISDS, ILL_DEBUG,
8864 _("MessageEnvelopeDownload request processed by server "
8865 "successfully.\n")
8867 #else /* not HAVE_LIBCURL */
8868 err = IE_NOTSUP;
8869 #endif
8870 return err;
8874 /* Load delivery info of any format from buffer.
8875 * @context is session context
8876 * @raw_type advertises format of @buffer content. Only delivery info types
8877 * are accepted.
8878 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
8879 * retrieve such data from message->raw after calling
8880 * isds_get_signed_delivery_info().
8881 * @length is length of buffer in bytes.
8882 * @message is automatically reallocated message parsed from @buffer.
8883 * @strategy selects how buffer will be attached into raw isds_message member.
8884 * */
8885 isds_error isds_load_delivery_info(struct isds_ctx *context,
8886 const isds_raw_type raw_type,
8887 const void *buffer, const size_t length,
8888 struct isds_message **message, const isds_buffer_strategy strategy) {
8890 isds_error err = IE_SUCCESS;
8891 message_ns_type message_ns;
8892 xmlDocPtr message_doc = NULL;
8893 xmlXPathContextPtr xpath_ctx = NULL;
8894 xmlXPathObjectPtr result = NULL;
8895 void *xml_stream = NULL;
8896 size_t xml_stream_length = 0;
8898 if (!context) return IE_INVALID_CONTEXT;
8899 zfree(context->long_message);
8900 if (!message) return IE_INVAL;
8901 isds_message_free(message);
8902 if (!buffer) return IE_INVAL;
8905 /* Select buffer format and extract XML from CMS*/
8906 switch (raw_type) {
8907 case RAWTYPE_DELIVERYINFO:
8908 message_ns = MESSAGE_NS_UNSIGNED;
8909 xml_stream = (void *) buffer;
8910 xml_stream_length = length;
8911 break;
8913 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
8914 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
8915 xml_stream = (void *) buffer;
8916 xml_stream_length = length;
8917 break;
8919 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
8920 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
8921 err = _isds_extract_cms_data(context, buffer, length,
8922 &xml_stream, &xml_stream_length);
8923 if (err) goto leave;
8924 break;
8926 default:
8927 isds_log_message(context, _("Bad raw delivery representation type"));
8928 return IE_INVAL;
8929 break;
8932 isds_log(ILF_ISDS, ILL_DEBUG,
8933 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
8934 xml_stream_length, xml_stream);
8936 /* Convert delivery info XML stream into XPath context */
8937 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
8938 if (!message_doc) {
8939 err = IE_XML;
8940 goto leave;
8942 xpath_ctx = xmlXPathNewContext(message_doc);
8943 if (!xpath_ctx) {
8944 err = IE_ERROR;
8945 goto leave;
8947 /* XXX: Name spaces mangled for signed delivery info:
8948 * http://isds.czechpoint.cz/v20/delivery:
8950 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
8951 * <q:dmDelivery>
8952 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
8953 * <p:dmID>170272</p:dmID>
8954 * ...
8955 * </p:dmDm>
8956 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
8957 * ...
8958 * </q:dmEvents>...</q:dmEvents>
8959 * </q:dmDelivery>
8960 * </q:GetDeliveryInfoResponse>
8961 * */
8962 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
8963 err = IE_ERROR;
8964 goto leave;
8966 result = xmlXPathEvalExpression(
8967 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
8968 xpath_ctx);
8969 if (!result) {
8970 err = IE_ERROR;
8971 goto leave;
8973 /* Empty delivery info */
8974 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8975 isds_printf_message(context,
8976 _("XML document is not sisds:dmDelivery document"));
8977 err = IE_ISDS;
8978 goto leave;
8980 /* More delivery info's */
8981 if (result->nodesetval->nodeNr > 1) {
8982 isds_printf_message(context,
8983 _("XML document has more sisds:dmDelivery elements"));
8984 err = IE_ISDS;
8985 goto leave;
8987 /* One delivery info */
8988 xpath_ctx->node = result->nodesetval->nodeTab[0];
8990 /* Extract the envelope (= message without documents, hence 0).
8991 * XXX: extract_TReturnedMessage() can obtain attachments size,
8992 * but delivery info carries none. It's coded as option elements,
8993 * so it should work. */
8994 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
8995 if (err) goto leave;
8997 /* Extract events */
8998 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
8999 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
9000 if (err) { err = IE_ERROR; goto leave; }
9001 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
9002 if (err) goto leave;
9004 /* Append raw CMS structure into message */
9005 (*message)->raw_type = raw_type;
9006 switch (strategy) {
9007 case BUFFER_DONT_STORE:
9008 break;
9009 case BUFFER_COPY:
9010 (*message)->raw = malloc(length);
9011 if (!(*message)->raw) {
9012 err = IE_NOMEM;
9013 goto leave;
9015 memcpy((*message)->raw, buffer, length);
9016 (*message)->raw_length = length;
9017 break;
9018 case BUFFER_MOVE:
9019 (*message)->raw = (void *) buffer;
9020 (*message)->raw_length = length;
9021 break;
9022 default:
9023 err = IE_ENUM;
9024 goto leave;
9027 leave:
9028 if (err) {
9029 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
9030 isds_message_free(message);
9033 xmlXPathFreeObject(result);
9034 xmlXPathFreeContext(xpath_ctx);
9035 if (!*message || !(*message)->xml) {
9036 xmlFreeDoc(message_doc);
9038 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
9040 if (!err)
9041 isds_log(ILF_ISDS, ILL_DEBUG,
9042 _("Delivery info loaded successfully.\n"));
9043 return err;
9047 /* Download signed delivery info-sheet of given message identified by ID.
9048 * @context is session context
9049 * @message_id is message identifier (you can get them from
9050 * isds_get_list_of_{sent,received}_messages())
9051 * @message is automatically reallocated message retrieved from ISDS.
9052 * It will miss documents per se. Use isds_get_signed_received_message(),
9053 * if you are interested in documents (content). OTOH, only this function
9054 * can get list events message has gone through. */
9055 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
9056 const char *message_id, struct isds_message **message) {
9058 isds_error err = IE_SUCCESS;
9059 #if HAVE_LIBCURL
9060 xmlDocPtr response = NULL;
9061 xmlChar *code = NULL, *status_message = NULL;
9062 void *raw = NULL;
9063 size_t raw_length = 0;
9064 #endif
9066 if (!context) return IE_INVALID_CONTEXT;
9067 zfree(context->long_message);
9069 /* Free former message if any */
9070 if (!message) return IE_INVAL;
9071 isds_message_free(message);
9073 #if HAVE_LIBCURL
9074 /* Do request and check for success */
9075 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9076 BAD_CAST "GetSignedDeliveryInfo", message_id,
9077 &response, NULL, NULL, &code, &status_message);
9078 if (err) goto leave;
9080 /* Find signed delivery info, extract it into raw and maybe free
9081 * response */
9082 err = find_extract_signed_data_free_response(context,
9083 (xmlChar *)message_id, &response,
9084 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
9085 if (err) goto leave;
9087 /* Parse delivery info */
9088 err = isds_load_delivery_info(context,
9089 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
9090 message, BUFFER_MOVE);
9091 if (err) goto leave;
9093 raw = NULL;
9095 leave:
9096 if (err) {
9097 isds_message_free(message);
9100 free(raw);
9101 free(code);
9102 free(status_message);
9103 xmlFreeDoc(response);
9105 if (!err)
9106 isds_log(ILF_ISDS, ILL_DEBUG,
9107 _("GetSignedDeliveryInfo request processed by server "
9108 "successfully.\n")
9110 #else /* not HAVE_LIBCURL */
9111 err = IE_NOTSUP;
9112 #endif
9113 return err;
9117 /* Download delivery info-sheet of given message identified by ID.
9118 * @context is session context
9119 * @message_id is message identifier (you can get them from
9120 * isds_get_list_of_{sent,received}_messages())
9121 * @message is automatically reallocated message retrieved from ISDS.
9122 * It will miss documents per se. Use isds_get_received_message(), if you are
9123 * interested in documents (content). OTOH, only this function can get list
9124 * of events message has gone through. */
9125 isds_error isds_get_delivery_info(struct isds_ctx *context,
9126 const char *message_id, struct isds_message **message) {
9128 isds_error err = IE_SUCCESS;
9129 #if HAVE_LIBCURL
9130 xmlDocPtr response = NULL;
9131 xmlChar *code = NULL, *status_message = NULL;
9132 xmlNodePtr delivery_node = NULL;
9133 void *raw = NULL;
9134 size_t raw_length = 0;
9135 #endif
9137 if (!context) return IE_INVALID_CONTEXT;
9138 zfree(context->long_message);
9140 /* Free former message if any */
9141 if (!message) return IE_INVAL;
9142 isds_message_free(message);
9144 #if HAVE_LIBCURL
9145 /* Do request and check for success */
9146 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9147 BAD_CAST "GetDeliveryInfo", message_id,
9148 &response, NULL, NULL, &code, &status_message);
9149 if (err) goto leave;
9152 /* Serialize delivery info */
9153 delivery_node = xmlDocGetRootElement(response);
9154 if (!delivery_node) {
9155 char *message_id_locale = _isds_utf82locale((char*) message_id);
9156 isds_printf_message(context,
9157 _("Server did not return any delivery info for ID `%s' "
9158 "on GetDeliveryInfo request"), message_id_locale);
9159 free(message_id_locale);
9160 err = IE_ISDS;
9161 goto leave;
9163 err = serialize_subtree(context, delivery_node, &raw, &raw_length);
9164 if (err) goto leave;
9166 /* Parse delivery info */
9167 /* TODO: Here we parse the response second time. We could single delivery
9168 * parser from isds_load_delivery_info() to make things faster. */
9169 err = isds_load_delivery_info(context,
9170 RAWTYPE_DELIVERYINFO, raw, raw_length,
9171 message, BUFFER_MOVE);
9172 if (err) goto leave;
9174 raw = NULL;
9177 leave:
9178 if (err) {
9179 isds_message_free(message);
9182 free(raw);
9183 free(code);
9184 free(status_message);
9185 xmlFreeDoc(response);
9187 if (!err)
9188 isds_log(ILF_ISDS, ILL_DEBUG,
9189 _("GetDeliveryInfo request processed by server "
9190 "successfully.\n")
9192 #else /* not HAVE_LIBCURL */
9193 err = IE_NOTSUP;
9194 #endif
9195 return err;
9199 /* Download incoming message identified by ID.
9200 * @context is session context
9201 * @message_id is message identifier (you can get them from
9202 * isds_get_list_of_received_messages())
9203 * @message is automatically reallocated message retrieved from ISDS */
9204 isds_error isds_get_received_message(struct isds_ctx *context,
9205 const char *message_id, struct isds_message **message) {
9207 isds_error err = IE_SUCCESS;
9208 #if HAVE_LIBCURL
9209 xmlDocPtr response = NULL;
9210 void *xml_stream = NULL;
9211 size_t xml_stream_length;
9212 xmlChar *code = NULL, *status_message = NULL;
9213 xmlXPathContextPtr xpath_ctx = NULL;
9214 xmlXPathObjectPtr result = NULL;
9215 char *phys_path = NULL;
9216 size_t phys_start, phys_end;
9217 #endif
9219 if (!context) return IE_INVALID_CONTEXT;
9220 zfree(context->long_message);
9222 /* Free former message if any */
9223 if (message) isds_message_free(message);
9225 #if HAVE_LIBCURL
9226 /* Do request and check for success */
9227 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
9228 BAD_CAST "MessageDownload", message_id,
9229 &response, &xml_stream, &xml_stream_length,
9230 &code, &status_message);
9231 if (err) goto leave;
9233 /* Extract data */
9234 xpath_ctx = xmlXPathNewContext(response);
9235 if (!xpath_ctx) {
9236 err = IE_ERROR;
9237 goto leave;
9239 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9240 err = IE_ERROR;
9241 goto leave;
9243 result = xmlXPathEvalExpression(
9244 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
9245 xpath_ctx);
9246 if (!result) {
9247 err = IE_ERROR;
9248 goto leave;
9250 /* Empty response */
9251 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9252 char *message_id_locale = _isds_utf82locale((char*) message_id);
9253 isds_printf_message(context,
9254 _("Server did not return any message for ID `%s' "
9255 "on MessageDownload request"), message_id_locale);
9256 free(message_id_locale);
9257 err = IE_ISDS;
9258 goto leave;
9260 /* More messages */
9261 if (result->nodesetval->nodeNr > 1) {
9262 char *message_id_locale = _isds_utf82locale((char*) message_id);
9263 isds_printf_message(context,
9264 _("Server did return more messages for ID `%s' "
9265 "on MessageDownload request"), message_id_locale);
9266 free(message_id_locale);
9267 err = IE_ISDS;
9268 goto leave;
9270 /* One message */
9271 xpath_ctx->node = result->nodesetval->nodeTab[0];
9273 /* Extract the message */
9274 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
9275 if (err) goto leave;
9277 /* Locate raw XML blob */
9278 phys_path = strdup(
9279 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
9280 PHYSXML_ELEMENT_SEPARATOR
9281 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
9282 PHYSXML_ELEMENT_SEPARATOR
9283 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
9285 if (!phys_path) {
9286 err = IE_NOMEM;
9287 goto leave;
9289 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
9290 phys_path, &phys_start, &phys_end);
9291 zfree(phys_path);
9292 if (err) {
9293 isds_log_message(context,
9294 _("Substring with isds:MessageDownloadResponse element "
9295 "could not be located in raw SOAP message"));
9296 goto leave;
9298 /* Save XML blob */
9299 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
9300 &(*message)->raw_length);*/
9301 /* TODO: Store name space declarations from ancestors */
9302 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
9303 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
9304 (*message)->raw_length = phys_end - phys_start + 1;
9305 (*message)->raw = malloc((*message)->raw_length);
9306 if (!(*message)->raw) {
9307 err = IE_NOMEM;
9308 goto leave;
9310 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
9313 leave:
9314 if (err) {
9315 isds_message_free(message);
9318 free(phys_path);
9320 xmlXPathFreeObject(result);
9321 xmlXPathFreeContext(xpath_ctx);
9323 free(code);
9324 free(status_message);
9325 free(xml_stream);
9326 if (!*message || !(*message)->xml) {
9327 xmlFreeDoc(response);
9330 if (!err)
9331 isds_log(ILF_ISDS, ILL_DEBUG,
9332 _("MessageDownload request processed by server "
9333 "successfully.\n")
9335 #else /* not HAVE_LIBCURL */
9336 err = IE_NOTSUP;
9337 #endif
9338 return err;
9342 /* Load message of any type from buffer.
9343 * @context is session context
9344 * @raw_type defines content type of @buffer. Only message types are allowed.
9345 * @buffer is message raw representation. Format (CMS, plain signed,
9346 * message direction) is defined in @raw_type. You can retrieve such data
9347 * from message->raw after calling isds_get_[signed]{received,sent}_message().
9348 * @length is length of buffer in bytes.
9349 * @message is automatically reallocated message parsed from @buffer.
9350 * @strategy selects how buffer will be attached into raw isds_message member.
9351 * */
9352 isds_error isds_load_message(struct isds_ctx *context,
9353 const isds_raw_type raw_type, const void *buffer, const size_t length,
9354 struct isds_message **message, const isds_buffer_strategy strategy) {
9356 isds_error err = IE_SUCCESS;
9357 void *xml_stream = NULL;
9358 size_t xml_stream_length = 0;
9359 message_ns_type message_ns;
9360 xmlDocPtr message_doc = NULL;
9361 xmlXPathContextPtr xpath_ctx = NULL;
9362 xmlXPathObjectPtr result = NULL;
9364 if (!context) return IE_INVALID_CONTEXT;
9365 zfree(context->long_message);
9366 if (!message) return IE_INVAL;
9367 isds_message_free(message);
9368 if (!buffer) return IE_INVAL;
9371 /* Select buffer format and extract XML from CMS*/
9372 switch (raw_type) {
9373 case RAWTYPE_INCOMING_MESSAGE:
9374 message_ns = MESSAGE_NS_UNSIGNED;
9375 xml_stream = (void *) buffer;
9376 xml_stream_length = length;
9377 break;
9379 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
9380 message_ns = MESSAGE_NS_SIGNED_INCOMING;
9381 xml_stream = (void *) buffer;
9382 xml_stream_length = length;
9383 break;
9385 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
9386 message_ns = MESSAGE_NS_SIGNED_INCOMING;
9387 err = _isds_extract_cms_data(context, buffer, length,
9388 &xml_stream, &xml_stream_length);
9389 if (err) goto leave;
9390 break;
9392 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
9393 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
9394 xml_stream = (void *) buffer;
9395 xml_stream_length = length;
9396 break;
9398 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
9399 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
9400 err = _isds_extract_cms_data(context, buffer, length,
9401 &xml_stream, &xml_stream_length);
9402 if (err) goto leave;
9403 break;
9405 default:
9406 isds_log_message(context, _("Bad raw message representation type"));
9407 return IE_INVAL;
9408 break;
9411 isds_log(ILF_ISDS, ILL_DEBUG,
9412 _("Loading message:\n%.*s\nEnd of message\n"),
9413 xml_stream_length, xml_stream);
9415 /* Convert messages XML stream into XPath context */
9416 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
9417 if (!message_doc) {
9418 err = IE_XML;
9419 goto leave;
9421 xpath_ctx = xmlXPathNewContext(message_doc);
9422 if (!xpath_ctx) {
9423 err = IE_ERROR;
9424 goto leave;
9426 /* XXX: Standard name space for unsigned incoming direction:
9427 * http://isds.czechpoint.cz/v20/
9429 * XXX: Name spaces mangled for signed outgoing direction:
9430 * http://isds.czechpoint.cz/v20/SentMessage:
9432 * <q:MessageDownloadResponse
9433 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
9434 * <q:dmReturnedMessage>
9435 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
9436 * <p:dmID>151916</p:dmID>
9437 * ...
9438 * </p:dmDm>
9439 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
9440 * ...
9441 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
9442 * </q:dmReturnedMessage>
9443 * </q:MessageDownloadResponse>
9445 * XXX: Name spaces mangled for signed incoming direction:
9446 * http://isds.czechpoint.cz/v20/message:
9448 * <q:MessageDownloadResponse
9449 * xmlns:q="http://isds.czechpoint.cz/v20/message">
9450 * <q:dmReturnedMessage>
9451 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
9452 * <p:dmID>151916</p:dmID>
9453 * ...
9454 * </p:dmDm>
9455 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
9456 * ...
9457 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
9458 * </q:dmReturnedMessage>
9459 * </q:MessageDownloadResponse>
9461 * Stupidity of ISDS developers is unlimited */
9462 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
9463 err = IE_ERROR;
9464 goto leave;
9466 result = xmlXPathEvalExpression(
9467 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
9468 xpath_ctx);
9469 if (!result) {
9470 err = IE_ERROR;
9471 goto leave;
9473 /* Empty message */
9474 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9475 isds_printf_message(context,
9476 _("XML document does not contain "
9477 "sisds:dmReturnedMessage element"));
9478 err = IE_ISDS;
9479 goto leave;
9481 /* More messages */
9482 if (result->nodesetval->nodeNr > 1) {
9483 isds_printf_message(context,
9484 _("XML document has more sisds:dmReturnedMessage elements"));
9485 err = IE_ISDS;
9486 goto leave;
9488 /* One message */
9489 xpath_ctx->node = result->nodesetval->nodeTab[0];
9491 /* Extract the message */
9492 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
9493 if (err) goto leave;
9495 /* Append raw buffer into message */
9496 (*message)->raw_type = raw_type;
9497 switch (strategy) {
9498 case BUFFER_DONT_STORE:
9499 break;
9500 case BUFFER_COPY:
9501 (*message)->raw = malloc(length);
9502 if (!(*message)->raw) {
9503 err = IE_NOMEM;
9504 goto leave;
9506 memcpy((*message)->raw, buffer, length);
9507 (*message)->raw_length = length;
9508 break;
9509 case BUFFER_MOVE:
9510 (*message)->raw = (void *) buffer;
9511 (*message)->raw_length = length;
9512 break;
9513 default:
9514 err = IE_ENUM;
9515 goto leave;
9519 leave:
9520 if (err) {
9521 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
9522 isds_message_free(message);
9525 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
9526 xmlXPathFreeObject(result);
9527 xmlXPathFreeContext(xpath_ctx);
9528 if (!*message || !(*message)->xml) {
9529 xmlFreeDoc(message_doc);
9532 if (!err)
9533 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
9534 return err;
9538 /* Determine type of raw message or delivery info according some heuristics.
9539 * It does not validate the raw blob.
9540 * @context is session context
9541 * @raw_type returns content type of @buffer. Valid only if exit code of this
9542 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
9543 * reallocated memory.
9544 * @buffer is message raw representation.
9545 * @length is length of buffer in bytes. */
9546 isds_error isds_guess_raw_type(struct isds_ctx *context,
9547 isds_raw_type *raw_type, const void *buffer, const size_t length) {
9548 isds_error err;
9549 void *xml_stream = NULL;
9550 size_t xml_stream_length = 0;
9551 xmlDocPtr document = NULL;
9552 xmlNodePtr root = NULL;
9554 if (!context) return IE_INVALID_CONTEXT;
9555 zfree(context->long_message);
9556 if (length == 0 || !buffer) return IE_INVAL;
9557 if (!raw_type) return IE_INVAL;
9559 /* Try CMS */
9560 err = _isds_extract_cms_data(context, buffer, length,
9561 &xml_stream, &xml_stream_length);
9562 if (err) {
9563 xml_stream = (void *) buffer;
9564 xml_stream_length = (size_t) length;
9565 err = IE_SUCCESS;
9568 /* Try XML */
9569 document = xmlParseMemory(xml_stream, xml_stream_length);
9570 if (!document) {
9571 isds_printf_message(context,
9572 _("Could not parse data as XML document"));
9573 err = IE_NOTSUP;
9574 goto leave;
9577 /* Get root element */
9578 root = xmlDocGetRootElement(document);
9579 if (!root) {
9580 isds_printf_message(context,
9581 _("XML document is missing root element"));
9582 err = IE_XML;
9583 goto leave;
9586 if (!root->ns || !root->ns->href) {
9587 isds_printf_message(context,
9588 _("Root element does not belong to any name space"));
9589 err = IE_NOTSUP;
9590 goto leave;
9593 /* Test name space */
9594 if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_INCOMING_NS)) {
9595 if (xml_stream == buffer)
9596 *raw_type = RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE;
9597 else
9598 *raw_type = RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
9599 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_OUTGOING_NS)) {
9600 if (xml_stream == buffer)
9601 *raw_type = RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE;
9602 else
9603 *raw_type = RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE;
9604 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_DELIVERY_NS)) {
9605 if (xml_stream == buffer)
9606 *raw_type = RAWTYPE_PLAIN_SIGNED_DELIVERYINFO;
9607 else
9608 *raw_type = RAWTYPE_CMS_SIGNED_DELIVERYINFO;
9609 } else if (!xmlStrcmp(root->ns->href, BAD_CAST ISDS_NS)) {
9610 if (xml_stream != buffer) {
9611 isds_printf_message(context,
9612 _("Document in ISDS name space is encapsulated into CMS" ));
9613 err = IE_NOTSUP;
9614 } else if (!xmlStrcmp(root->name, BAD_CAST "MessageDownloadResponse"))
9615 *raw_type = RAWTYPE_INCOMING_MESSAGE;
9616 else if (!xmlStrcmp(root->name, BAD_CAST "GetDeliveryInfoResponse"))
9617 *raw_type = RAWTYPE_DELIVERYINFO;
9618 else {
9619 isds_printf_message(context,
9620 _("Unknown root element in ISDS name space"));
9621 err = IE_NOTSUP;
9623 } else {
9624 isds_printf_message(context,
9625 _("Unknown name space"));
9626 err = IE_NOTSUP;
9629 leave:
9630 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
9631 xmlFreeDoc(document);
9632 return err;
9636 /* Download signed incoming/outgoing message identified by ID.
9637 * @context is session context
9638 * @output is true for outgoing message, false for incoming message
9639 * @message_id is message identifier (you can get them from
9640 * isds_get_list_of_{sent,received}_messages())
9641 * @message is automatically reallocated message retrieved from ISDS. The raw
9642 * member will be filled with PKCS#7 structure in DER format. */
9643 static isds_error isds_get_signed_message(struct isds_ctx *context,
9644 const _Bool outgoing, const char *message_id,
9645 struct isds_message **message) {
9647 isds_error err = IE_SUCCESS;
9648 #if HAVE_LIBCURL
9649 xmlDocPtr response = NULL;
9650 xmlChar *code = NULL, *status_message = NULL;
9651 xmlXPathContextPtr xpath_ctx = NULL;
9652 xmlXPathObjectPtr result = NULL;
9653 char *encoded_structure = NULL;
9654 void *raw = NULL;
9655 size_t raw_length = 0;
9656 #endif
9658 if (!context) return IE_INVALID_CONTEXT;
9659 zfree(context->long_message);
9660 if (!message) return IE_INVAL;
9661 isds_message_free(message);
9663 #if HAVE_LIBCURL
9664 /* Do request and check for success */
9665 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
9666 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
9667 BAD_CAST "SignedMessageDownload",
9668 message_id, &response, NULL, NULL, &code, &status_message);
9669 if (err) goto leave;
9671 /* Find signed message, extract it into raw and maybe free
9672 * response */
9673 err = find_extract_signed_data_free_response(context,
9674 (xmlChar *)message_id, &response,
9675 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
9676 BAD_CAST "SignedMessageDownload",
9677 &raw, &raw_length);
9678 if (err) goto leave;
9680 /* Parse message */
9681 err = isds_load_message(context,
9682 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
9683 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
9684 raw, raw_length, message, BUFFER_MOVE);
9685 if (err) goto leave;
9687 raw = NULL;
9689 leave:
9690 if (err) {
9691 isds_message_free(message);
9694 free(encoded_structure);
9695 xmlXPathFreeObject(result);
9696 xmlXPathFreeContext(xpath_ctx);
9697 free(raw);
9699 free(code);
9700 free(status_message);
9701 xmlFreeDoc(response);
9703 if (!err)
9704 isds_log(ILF_ISDS, ILL_DEBUG,
9705 (outgoing) ?
9706 _("SignedSentMessageDownload request processed by server "
9707 "successfully.\n") :
9708 _("SignedMessageDownload request processed by server "
9709 "successfully.\n")
9711 #else /* not HAVE_LIBCURL */
9712 err = IE_NOTSUP;
9713 #endif
9714 return err;
9718 /* Download signed incoming message identified by ID.
9719 * @context is session context
9720 * @message_id is message identifier (you can get them from
9721 * isds_get_list_of_received_messages())
9722 * @message is automatically reallocated message retrieved from ISDS. The raw
9723 * member will be filled with PKCS#7 structure in DER format. */
9724 isds_error isds_get_signed_received_message(struct isds_ctx *context,
9725 const char *message_id, struct isds_message **message) {
9726 return isds_get_signed_message(context, 0, message_id, message);
9730 /* Download signed outgoing message identified by ID.
9731 * @context is session context
9732 * @message_id is message identifier (you can get them from
9733 * isds_get_list_of_sent_messages())
9734 * @message is automatically reallocated message retrieved from ISDS. The raw
9735 * member will be filled with PKCS#7 structure in DER format. */
9736 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
9737 const char *message_id, struct isds_message **message) {
9738 return isds_get_signed_message(context, 1, message_id, message);
9742 /* Get type and name of user who sent a message identified by ID.
9743 * @context is session context
9744 * @message_id is message identifier
9745 * @sender_type is pointer to automatically allocated type of sender detected
9746 * from @raw_sender_type string. If @raw_sender_type is unknown to this
9747 * library or to the server, NULL will be returned. Pass NULL if you don't
9748 * care about it.
9749 * @raw_sender_type is automatically reallocated UTF-8 string describing
9750 * sender type or NULL if not known to server. Pass NULL if you don't care.
9751 * @sender_name is automatically reallocated UTF-8 name of user who sent the
9752 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
9753 isds_error isds_get_message_sender(struct isds_ctx *context,
9754 const char *message_id, isds_sender_type **sender_type,
9755 char **raw_sender_type, char **sender_name) {
9756 isds_error err = IE_SUCCESS;
9757 #if HAVE_LIBCURL
9758 xmlDocPtr response = NULL;
9759 xmlChar *code = NULL, *status_message = NULL;
9760 xmlXPathContextPtr xpath_ctx = NULL;
9761 xmlXPathObjectPtr result = NULL;
9762 char *type_string = NULL;
9763 #endif
9765 if (!context) return IE_INVALID_CONTEXT;
9766 zfree(context->long_message);
9767 if (sender_type) zfree(*sender_type);
9768 if (raw_sender_type) zfree(*raw_sender_type);
9769 if (sender_name) zfree(*sender_name);
9770 if (!message_id) return IE_INVAL;
9772 #if HAVE_LIBCURL
9773 /* Do request and check for success */
9774 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9775 BAD_CAST "GetMessageAuthor",
9776 message_id, &response, NULL, NULL, &code, &status_message);
9777 if (err) goto leave;
9779 /* Extract data */
9780 xpath_ctx = xmlXPathNewContext(response);
9781 if (!xpath_ctx) {
9782 err = IE_ERROR;
9783 goto leave;
9785 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9786 err = IE_ERROR;
9787 goto leave;
9789 result = xmlXPathEvalExpression(
9790 BAD_CAST "/isds:GetMessageAuthorResponse", xpath_ctx);
9791 if (!result) {
9792 err = IE_ERROR;
9793 goto leave;
9795 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9796 isds_log_message(context,
9797 _("Missing GetMessageAuthorResponse element"));
9798 err = IE_ISDS;
9799 goto leave;
9801 if (result->nodesetval->nodeNr > 1) {
9802 isds_log_message(context,
9803 _("Multiple GetMessageAuthorResponse element"));
9804 err = IE_ISDS;
9805 goto leave;
9807 xpath_ctx->node = result->nodesetval->nodeTab[0];
9808 xmlXPathFreeObject(result); result = NULL;
9810 /* Fill output arguments in */
9811 EXTRACT_STRING("isds:userType", type_string);
9812 if (type_string) {
9813 *sender_type = calloc(1, sizeof(**sender_type));
9814 if (!*sender_type) {
9815 err = IE_NOMEM;
9816 goto leave;
9819 if (sender_type) {
9820 err = string2isds_sender_type((xmlChar *)type_string,
9821 *sender_type);
9822 if (err) {
9823 zfree(*sender_type);
9824 if (err == IE_ENUM) {
9825 err = IE_SUCCESS;
9826 char *type_string_locale = _isds_utf82locale(type_string);
9827 isds_log(ILF_ISDS, ILL_WARNING,
9828 _("Unknown isds:userType value: %s"),
9829 type_string_locale);
9830 free(type_string_locale);
9835 if (sender_type)
9836 EXTRACT_STRING("isds:authorName", *sender_name);
9838 leave:
9839 if (err) {
9840 if (sender_type) zfree(*sender_type);
9841 zfree(type_string);
9842 if (sender_name) zfree(*sender_name);
9844 if (raw_sender_type) *raw_sender_type = type_string;
9846 xmlXPathFreeObject(result);
9847 xmlXPathFreeContext(xpath_ctx);
9849 free(code);
9850 free(status_message);
9851 xmlFreeDoc(response);
9853 if (!err)
9854 isds_log(ILF_ISDS, ILL_DEBUG,
9855 _("GetMessageAuthor request processed by server "
9856 "successfully.\n"));
9857 #else /* not HAVE_LIBCURL */
9858 err = IE_NOTSUP;
9859 #endif
9860 return err;
9864 /* Retrieve hash of message identified by ID stored in ISDS.
9865 * @context is session context
9866 * @message_id is message identifier
9867 * @hash is automatically reallocated message hash downloaded from ISDS.
9868 * Message must exist in system and must not be deleted. */
9869 isds_error isds_download_message_hash(struct isds_ctx *context,
9870 const char *message_id, struct isds_hash **hash) {
9872 isds_error err = IE_SUCCESS;
9873 #if HAVE_LIBCURL
9874 xmlDocPtr response = NULL;
9875 xmlChar *code = NULL, *status_message = NULL;
9876 xmlXPathContextPtr xpath_ctx = NULL;
9877 xmlXPathObjectPtr result = NULL;
9878 #endif
9880 if (!context) return IE_INVALID_CONTEXT;
9881 zfree(context->long_message);
9883 isds_hash_free(hash);
9885 #if HAVE_LIBCURL
9886 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9887 BAD_CAST "VerifyMessage", message_id,
9888 &response, NULL, NULL, &code, &status_message);
9889 if (err) goto leave;
9892 /* Extract data */
9893 xpath_ctx = xmlXPathNewContext(response);
9894 if (!xpath_ctx) {
9895 err = IE_ERROR;
9896 goto leave;
9898 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9899 err = IE_ERROR;
9900 goto leave;
9902 result = xmlXPathEvalExpression(
9903 BAD_CAST "/isds:VerifyMessageResponse",
9904 xpath_ctx);
9905 if (!result) {
9906 err = IE_ERROR;
9907 goto leave;
9909 /* Empty response */
9910 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9911 char *message_id_locale = _isds_utf82locale((char*) message_id);
9912 isds_printf_message(context,
9913 _("Server did not return any response for ID `%s' "
9914 "on VerifyMessage request"), message_id_locale);
9915 free(message_id_locale);
9916 err = IE_ISDS;
9917 goto leave;
9919 /* More responses */
9920 if (result->nodesetval->nodeNr > 1) {
9921 char *message_id_locale = _isds_utf82locale((char*) message_id);
9922 isds_printf_message(context,
9923 _("Server did return more responses for ID `%s' "
9924 "on VerifyMessage request"), message_id_locale);
9925 free(message_id_locale);
9926 err = IE_ISDS;
9927 goto leave;
9929 /* One response */
9930 xpath_ctx->node = result->nodesetval->nodeTab[0];
9932 /* Extract the hash */
9933 err = find_and_extract_DmHash(context, hash, xpath_ctx);
9935 leave:
9936 if (err) {
9937 isds_hash_free(hash);
9940 xmlXPathFreeObject(result);
9941 xmlXPathFreeContext(xpath_ctx);
9943 free(code);
9944 free(status_message);
9945 xmlFreeDoc(response);
9947 if (!err)
9948 isds_log(ILF_ISDS, ILL_DEBUG,
9949 _("VerifyMessage request processed by server "
9950 "successfully.\n")
9952 #else /* not HAVE_LIBCURL */
9953 err = IE_NOTSUP;
9954 #endif
9955 return err;
9959 /* Mark message as read. This is a transactional commit function to acknowledge
9960 * to ISDS the message has been downloaded and processed by client properly.
9961 * @context is session context
9962 * @message_id is message identifier. */
9963 isds_error isds_mark_message_read(struct isds_ctx *context,
9964 const char *message_id) {
9966 isds_error err = IE_SUCCESS;
9967 #if HAVE_LIBCURL
9968 xmlDocPtr response = NULL;
9969 xmlChar *code = NULL, *status_message = NULL;
9970 #endif
9972 if (!context) return IE_INVALID_CONTEXT;
9973 zfree(context->long_message);
9975 #if HAVE_LIBCURL
9976 /* Do request and check for success */
9977 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9978 BAD_CAST "MarkMessageAsDownloaded", message_id,
9979 &response, NULL, NULL, &code, &status_message);
9981 free(code);
9982 free(status_message);
9983 xmlFreeDoc(response);
9985 if (!err)
9986 isds_log(ILF_ISDS, ILL_DEBUG,
9987 _("MarkMessageAsDownloaded request processed by server "
9988 "successfully.\n")
9990 #else /* not HAVE_LIBCURL */
9991 err = IE_NOTSUP;
9992 #endif
9993 return err;
9997 /* Mark message as received by recipient. This is applicable only to
9998 * commercial message. Use envelope->dmType message member to distinguish
9999 * commercial message from government message. Government message is
10000 * received automatically (by law), commercial message on recipient request.
10001 * @context is session context
10002 * @message_id is message identifier. */
10003 isds_error isds_mark_message_received(struct isds_ctx *context,
10004 const char *message_id) {
10006 isds_error err = IE_SUCCESS;
10007 #if HAVE_LIBCURL
10008 xmlDocPtr response = NULL;
10009 xmlChar *code = NULL, *status_message = NULL;
10010 #endif
10012 if (!context) return IE_INVALID_CONTEXT;
10013 zfree(context->long_message);
10015 #if HAVE_LIBCURL
10016 /* Do request and check for success */
10017 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10018 BAD_CAST "ConfirmDelivery", message_id,
10019 &response, NULL, NULL, &code, &status_message);
10021 free(code);
10022 free(status_message);
10023 xmlFreeDoc(response);
10025 if (!err)
10026 isds_log(ILF_ISDS, ILL_DEBUG,
10027 _("ConfirmDelivery request processed by server "
10028 "successfully.\n")
10030 #else /* not HAVE_LIBCURL */
10031 err = IE_NOTSUP;
10032 #endif
10033 return err;
10037 /* Send document for authorized conversion into Czech POINT system.
10038 * This is public anonymous service, no log-in necessary. Special context is
10039 * used to reuse keep-a-live HTTPS connection.
10040 * @context is Czech POINT session context. DO NOT use context connected to
10041 * ISDS server. Use new context or context used by this function previously.
10042 * @document is document to convert. Only data, data_length, dmFileDescr and
10043 * is_xml members are significant. Be ware that not all document formats can be
10044 * converted (signed PDF 1.3 and higher only (2010-02 state)).
10045 * @id is reallocated identifier assigned by Czech POINT system to
10046 * your document on submit. Use is to tell it to Czech POINT officer.
10047 * @date is reallocated document submit date (submitted documents
10048 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
10049 * value. */
10050 isds_error czp_convert_document(struct isds_ctx *context,
10051 const struct isds_document *document,
10052 char **id, struct tm **date) {
10053 isds_error err = IE_SUCCESS;
10054 #if HAVE_LIBCURL
10055 xmlNsPtr deposit_ns = NULL, empty_ns = NULL;
10056 xmlNodePtr request = NULL, node;
10057 xmlDocPtr response = NULL;
10059 xmlXPathContextPtr xpath_ctx = NULL;
10060 xmlXPathObjectPtr result = NULL;
10061 long int status = -1;
10062 long int *status_ptr = &status;
10063 char *string = NULL;
10064 #endif
10067 if (!context) return IE_INVALID_CONTEXT;
10068 zfree(context->long_message);
10069 if (!document || !id || !date) return IE_INVAL;
10071 if (document->is_xml) {
10072 isds_log_message(context,
10073 _("XML documents cannot be submitted to conversion"));
10074 return IE_NOTSUP;
10077 /* Free output arguments */
10078 zfree(*id);
10079 zfree(*date);
10081 #if HAVE_LIBCURL
10082 /* Store configuration */
10083 context->type = CTX_TYPE_CZP;
10084 free(context->url);
10085 context->url = strdup("https://www.czechpoint.cz/uschovna/services.php");
10086 if (!(context->url))
10087 return IE_NOMEM;
10089 /* Prepare CURL handle if not yet connected */
10090 if (!context->curl) {
10091 context->curl = curl_easy_init();
10092 if (!(context->curl))
10093 return IE_ERROR;
10096 /* Build conversion request */
10097 request = xmlNewNode(NULL, BAD_CAST "saveDocument");
10098 if (!request) {
10099 isds_log_message(context,
10100 _("Could not build Czech POINT conversion request"));
10101 return IE_ERROR;
10103 deposit_ns = xmlNewNs(request, BAD_CAST DEPOSIT_NS, BAD_CAST "dep");
10104 if(!deposit_ns) {
10105 isds_log_message(context,
10106 _("Could not create Czech POINT deposit name space"));
10107 xmlFreeNode(request);
10108 return IE_ERROR;
10110 xmlSetNs(request, deposit_ns);
10112 /* Insert children. They are in empty namespace! */
10113 empty_ns = xmlNewNs(request, BAD_CAST "", NULL);
10114 if(!empty_ns) {
10115 isds_log_message(context, _("Could not create empty name space"));
10116 err = IE_ERROR;
10117 goto leave;
10119 INSERT_STRING_WITH_NS(request, empty_ns, "conversionID", "0");
10120 INSERT_STRING_WITH_NS(request, empty_ns, "fileName",
10121 document->dmFileDescr);
10123 /* Document encoded in Base64 */
10124 err = insert_base64_encoded_string(context, request, empty_ns, "document",
10125 document->data, document->data_length);
10126 if (err) goto leave;
10128 isds_log(ILF_ISDS, ILL_DEBUG,
10129 _("Submitting document for conversion into Czech POINT deposit"));
10131 /* Send conversion request */
10132 err = _czp_czpdeposit(context, request, &response);
10133 xmlFreeNode(request); request = NULL;
10135 if (err) {
10136 czp_do_close_connection(context);
10137 goto leave;
10141 /* Extract response */
10142 xpath_ctx = xmlXPathNewContext(response);
10143 if (!xpath_ctx) {
10144 err = IE_ERROR;
10145 goto leave;
10147 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10148 err = IE_ERROR;
10149 goto leave;
10151 result = xmlXPathEvalExpression(
10152 BAD_CAST "/deposit:saveDocumentResponse/return",
10153 xpath_ctx);
10154 if (!result) {
10155 err = IE_ERROR;
10156 goto leave;
10158 /* Empty response */
10159 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10160 isds_printf_message(context,
10161 _("Missing `return' element in Czech POINT deposit response"));
10162 err = IE_ISDS;
10163 goto leave;
10165 /* More responses */
10166 if (result->nodesetval->nodeNr > 1) {
10167 isds_printf_message(context,
10168 _("Multiple `return' element in Czech POINT deposit response"));
10169 err = IE_ISDS;
10170 goto leave;
10172 /* One response */
10173 xpath_ctx->node = result->nodesetval->nodeTab[0];
10175 /* Get status */
10176 EXTRACT_LONGINT("status", status_ptr, 1);
10177 if (status) {
10178 EXTRACT_STRING("statusMsg", string);
10179 char *string_locale = _isds_utf82locale(string);
10180 isds_printf_message(context,
10181 _("Czech POINT deposit refused document for conversion "
10182 "(code=%ld, message=%s)"),
10183 status, string_locale);
10184 free(string_locale);
10185 err = IE_ISDS;
10186 goto leave;
10189 /* Get document ID */
10190 EXTRACT_STRING("documentID", *id);
10192 /* Get submit date */
10193 EXTRACT_STRING("dateInserted", string);
10194 if (string) {
10195 *date = calloc(1, sizeof(**date));
10196 if (!*date) {
10197 err = IE_NOMEM;
10198 goto leave;
10200 err = datestring2tm((xmlChar *)string, *date);
10201 if (err) {
10202 if (err == IE_NOTSUP) {
10203 err = IE_ISDS;
10204 char *string_locale = _isds_utf82locale(string);
10205 isds_printf_message(context,
10206 _("Invalid dateInserted value: %s"), string_locale);
10207 free(string_locale);
10209 goto leave;
10213 leave:
10214 free(string);
10215 xmlXPathFreeObject(result);
10216 xmlXPathFreeContext(xpath_ctx);
10218 xmlFreeDoc(response);
10219 xmlFreeNode(request);
10221 if (!err) {
10222 char *id_locale = _isds_utf82locale((char *) *id);
10223 isds_log(ILF_ISDS, ILL_DEBUG,
10224 _("Document %s has been submitted for conversion "
10225 "to server successfully\n"), id_locale);
10226 free(id_locale);
10228 #else /* not HAVE_LIBCURL */
10229 err = IE_NOTSUP;
10230 #endif
10231 return err;
10235 /* Close possibly opened connection to Czech POINT document deposit.
10236 * @context is Czech POINT session context. */
10237 isds_error czp_close_connection(struct isds_ctx *context) {
10238 if (!context) return IE_INVALID_CONTEXT;
10239 zfree(context->long_message);
10240 #if HAVE_LIBCURL
10241 return czp_do_close_connection(context);
10242 #else
10243 return IE_NOTSUP;
10244 #endif
10248 /* Send request for new box creation in testing ISDS instance.
10249 * It's not possible to request for a production box currently, as it
10250 * communicates via e-mail.
10251 * XXX: This function does not work either. Server complains about invalid
10252 * e-mail address.
10253 * XXX: Remove context->type hacks in isds.c and validator.c when removing
10254 * this function
10255 * @context is special session context for box creation request. DO NOT use
10256 * standard context as it could reveal your password. Use fresh new context or
10257 * context previously used by this function.
10258 * @box is box description to create including single primary user (in case of
10259 * FO box type). It outputs box ID assigned by ISDS in dbID element.
10260 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
10261 * box, or contact address of PFO box owner). The email member is mandatory as
10262 * it will be used to deliver credentials.
10263 * @former_names is former name of box owner. Pass NULL if you don't care.
10264 * @approval is optional external approval of box manipulation
10265 * @refnumber is reallocated serial number of request assigned by ISDS. Use
10266 * NULL, if you don't care.*/
10267 isds_error isds_request_new_testing_box(struct isds_ctx *context,
10268 struct isds_DbOwnerInfo *box, const struct isds_list *users,
10269 const char *former_names, const struct isds_approval *approval,
10270 char **refnumber) {
10271 isds_error err = IE_SUCCESS;
10272 #if HAVE_LIBCURL
10273 xmlNodePtr request = NULL;
10274 xmlDocPtr response = NULL;
10275 xmlXPathContextPtr xpath_ctx = NULL;
10276 xmlXPathObjectPtr result = NULL;
10277 #endif
10280 if (!context) return IE_INVALID_CONTEXT;
10281 zfree(context->long_message);
10282 if (!box) return IE_INVAL;
10284 #if HAVE_LIBCURL
10285 if (!box->email || box->email[0] == '\0') {
10286 isds_log_message(context, _("E-mail field is mandatory"));
10287 return IE_INVAL;
10290 /* Scratch box ID */
10291 zfree(box->dbID);
10293 /* Store configuration */
10294 context->type = CTX_TYPE_TESTING_REQUEST_COLLECTOR;
10295 free(context->url);
10296 context->url = strdup("http://78.102.19.203/testbox/request_box.php");
10297 if (!(context->url))
10298 return IE_NOMEM;
10300 /* Prepare CURL handle if not yet connected */
10301 if (!context->curl) {
10302 context->curl = curl_easy_init();
10303 if (!(context->curl))
10304 return IE_ERROR;
10307 /* Build CreateDataBox request */
10308 err = build_CreateDBInput_request(context,
10309 &request, BAD_CAST "CreateDataBox",
10310 box, users, (xmlChar *) former_names, NULL, NULL, NULL, approval);
10311 if (err) goto leave;
10313 /* Send it to server and process response */
10314 err = send_destroy_request_check_response(context,
10315 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
10316 &response, (xmlChar **) refnumber);
10317 if (err) goto leave;
10319 /* Extract box ID */
10320 xpath_ctx = xmlXPathNewContext(response);
10321 if (!xpath_ctx) {
10322 err = IE_ERROR;
10323 goto leave;
10325 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10326 err = IE_ERROR;
10327 goto leave;
10329 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
10331 leave:
10332 xmlXPathFreeObject(result);
10333 xmlXPathFreeContext(xpath_ctx);
10334 xmlFreeDoc(response);
10335 xmlFreeNode(request);
10337 if (!err) {
10338 isds_log(ILF_ISDS, ILL_DEBUG,
10339 _("CreateDataBox request processed by server successfully.\n"));
10341 #else /* not HAVE_LIBCURL */
10342 err = IE_NOTSUP;
10343 #endif
10345 return err;
10349 /* Submit CMS signed message to ISDS to verify its originality. This is
10350 * stronger form of isds_verify_message_hash() because ISDS does more checks
10351 * than simple one (potentialy old weak) hash comparison.
10352 * @context is session context
10353 * @message is memory with raw CMS signed message bit stream
10354 * @length is @message size in bytes
10355 * @return
10356 * IE_SUCCESS if message originates in ISDS
10357 * IE_NOTEQUAL if message is unknown to ISDS
10358 * other code for other errors */
10359 isds_error isds_authenticate_message(struct isds_ctx *context,
10360 const void *message, size_t length) {
10361 isds_error err = IE_SUCCESS;
10362 #if HAVE_LIBCURL
10363 xmlNsPtr isds_ns = NULL;
10364 xmlNodePtr request = NULL;
10365 xmlDocPtr response = NULL;
10366 xmlXPathContextPtr xpath_ctx = NULL;
10367 xmlXPathObjectPtr result = NULL;
10368 _Bool *authentic = NULL;
10369 #endif
10371 if (!context) return IE_INVALID_CONTEXT;
10372 zfree(context->long_message);
10373 if (!message || length == 0) return IE_INVAL;
10375 #if HAVE_LIBCURL
10376 /* Check if connection is established
10377 * TODO: This check should be done downstairs. */
10378 if (!context->curl) return IE_CONNECTION_CLOSED;
10381 /* Build AuthenticateMessage request */
10382 request = xmlNewNode(NULL, BAD_CAST "AuthenticateMessage");
10383 if (!request) {
10384 isds_log_message(context,
10385 _("Could not build AuthenticateMessage request"));
10386 return IE_ERROR;
10388 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
10389 if(!isds_ns) {
10390 isds_log_message(context, _("Could not create ISDS name space"));
10391 xmlFreeNode(request);
10392 return IE_ERROR;
10394 xmlSetNs(request, isds_ns);
10396 /* Insert Base64 encoded message */
10397 err = insert_base64_encoded_string(context, request, NULL, "dmMessage",
10398 message, length);
10399 if (err) goto leave;
10401 /* Send request to server and process response */
10402 err = send_destroy_request_check_response(context,
10403 SERVICE_DM_OPERATIONS, BAD_CAST "AuthenticateMessage", &request,
10404 &response, NULL);
10405 if (err) goto leave;
10408 /* ISDS has decided */
10409 xpath_ctx = xmlXPathNewContext(response);
10410 if (!xpath_ctx) {
10411 err = IE_ERROR;
10412 goto leave;
10414 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10415 err = IE_ERROR;
10416 goto leave;
10419 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic);
10421 if (!authentic) {
10422 isds_log_message(context,
10423 _("Server did not return any response on "
10424 "AuthenticateMessage request"));
10425 err = IE_ISDS;
10426 goto leave;
10428 if (*authentic) {
10429 isds_log(ILF_ISDS, ILL_DEBUG,
10430 _("ISDS authenticated the message successfully\n"));
10431 } else {
10432 isds_log_message(context, _("ISDS does not know the message"));
10433 err = IE_NOTEQUAL;
10437 leave:
10438 free(authentic);
10439 xmlXPathFreeObject(result);
10440 xmlXPathFreeContext(xpath_ctx);
10442 xmlFreeDoc(response);
10443 xmlFreeNode(request);
10444 #else /* not HAVE_LIBCURL */
10445 err = IE_NOTSUP;
10446 #endif
10448 return err;
10451 #undef INSERT_ELEMENT
10452 #undef CHECK_FOR_STRING_LENGTH
10453 #undef INSERT_STRING_ATTRIBUTE
10454 #undef INSERT_ULONGINTNOPTR
10455 #undef INSERT_ULONGINT
10456 #undef INSERT_LONGINT
10457 #undef INSERT_BOOLEAN
10458 #undef INSERT_SCALAR_BOOLEAN
10459 #undef INSERT_STRING
10460 #undef INSERT_STRING_WITH_NS
10461 #undef EXTRACT_STRING_ATTRIBUTE
10462 #undef EXTRACT_ULONGINT
10463 #undef EXTRACT_LONGINT
10464 #undef EXTRACT_BOOLEAN
10465 #undef EXTRACT_STRING
10468 /* Compute hash of message from raw representation and store it into envelope.
10469 * Original hash structure will be destroyed in envelope.
10470 * @context is session context
10471 * @message is message carrying raw XML message blob
10472 * @algorithm is desired hash algorithm to use */
10473 isds_error isds_compute_message_hash(struct isds_ctx *context,
10474 struct isds_message *message, const isds_hash_algorithm algorithm) {
10475 isds_error err = IE_SUCCESS;
10476 const char *nsuri;
10477 void *xml_stream = NULL;
10478 size_t xml_stream_length;
10479 size_t phys_start, phys_end;
10480 char *phys_path = NULL;
10481 struct isds_hash *new_hash = NULL;
10484 if (!context) return IE_INVALID_CONTEXT;
10485 zfree(context->long_message);
10486 if (!message) return IE_INVAL;
10488 if (!message->raw) {
10489 isds_log_message(context,
10490 _("Message does not carry raw representation"));
10491 return IE_INVAL;
10494 switch (message->raw_type) {
10495 case RAWTYPE_INCOMING_MESSAGE:
10496 nsuri = ISDS_NS;
10497 xml_stream = message->raw;
10498 xml_stream_length = message->raw_length;
10499 break;
10501 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
10502 nsuri = SISDS_INCOMING_NS;
10503 xml_stream = message->raw;
10504 xml_stream_length = message->raw_length;
10505 break;
10507 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
10508 nsuri = SISDS_INCOMING_NS;
10509 err = _isds_extract_cms_data(context,
10510 message->raw, message->raw_length,
10511 &xml_stream, &xml_stream_length);
10512 if (err) goto leave;
10513 break;
10515 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
10516 nsuri = SISDS_OUTGOING_NS;
10517 xml_stream = message->raw;
10518 xml_stream_length = message->raw_length;
10519 break;
10521 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
10522 nsuri = SISDS_OUTGOING_NS;
10523 err = _isds_extract_cms_data(context,
10524 message->raw, message->raw_length,
10525 &xml_stream, &xml_stream_length);
10526 if (err) goto leave;
10527 break;
10529 default:
10530 isds_log_message(context, _("Bad raw representation type"));
10531 return IE_INVAL;
10532 break;
10536 /* XXX: Hash is computed from original string representing isds:dmDm
10537 * subtree. That means no encoding, white space, xmlns attributes changes.
10538 * In other words, input for hash can be invalid XML stream. */
10539 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
10540 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
10541 PHYSXML_ELEMENT_SEPARATOR,
10542 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
10543 PHYSXML_ELEMENT_SEPARATOR
10544 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
10545 err = IE_NOMEM;
10546 goto leave;
10548 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
10549 phys_path, &phys_start, &phys_end);
10550 zfree(phys_path);
10551 if (err) {
10552 isds_log_message(context,
10553 _("Substring with isds:dmDM element could not be located "
10554 "in raw message"));
10555 goto leave;
10559 /* Compute hash */
10560 new_hash = calloc(1, sizeof(*new_hash));
10561 if (!new_hash) {
10562 err = IE_NOMEM;
10563 goto leave;
10565 new_hash->algorithm = algorithm;
10566 err = _isds_compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
10567 new_hash);
10568 if (err) {
10569 isds_log_message(context, _("Could not compute message hash"));
10570 goto leave;
10573 /* Save computed hash */
10574 if (!message->envelope) {
10575 message->envelope = calloc(1, sizeof(*message->envelope));
10576 if (!message->envelope) {
10577 err = IE_NOMEM;
10578 goto leave;
10581 isds_hash_free(&message->envelope->hash);
10582 message->envelope->hash = new_hash;
10584 leave:
10585 if (err) {
10586 isds_hash_free(&new_hash);
10589 free(phys_path);
10590 if (xml_stream != message->raw) free(xml_stream);
10591 return err;
10595 /* Compare two hashes.
10596 * @h1 is first hash
10597 * @h2 is another hash
10598 * @return
10599 * IE_SUCCESS if hashes equal
10600 * IE_NOTUNIQ if hashes are comparable, but they don't equal
10601 * IE_ENUM if not comparable, but both structures defined
10602 * IE_INVAL if some of the structures are undefined (NULL)
10603 * IE_ERROR if internal error occurs */
10604 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
10605 if (h1 == NULL || h2 == NULL) return IE_INVAL;
10606 if (h1->algorithm != h2->algorithm) return IE_ENUM;
10607 if (h1->length != h2->length) return IE_ERROR;
10608 if (h1->length > 0 && !h1->value) return IE_ERROR;
10609 if (h2->length > 0 && !h2->value) return IE_ERROR;
10611 for (int i = 0; i < h1->length; i++) {
10612 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
10613 return IE_NOTEQUAL;
10615 return IE_SUCCESS;
10619 /* Check message has gone through ISDS by comparing message hash stored in
10620 * ISDS and locally computed hash. You must provide message with valid raw
10621 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
10622 * This is convenient wrapper for isds_download_message_hash(),
10623 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
10624 * @context is session context
10625 * @message is message with valid raw and envelope member; envelope->hash
10626 * member will be changed during function run. Use envelope on heap only.
10627 * @return
10628 * IE_SUCCESS if message originates in ISDS
10629 * IE_NOTEQUAL if message is unknown to ISDS
10630 * other code for other errors */
10631 isds_error isds_verify_message_hash(struct isds_ctx *context,
10632 struct isds_message *message) {
10633 isds_error err = IE_SUCCESS;
10634 struct isds_hash *downloaded_hash = NULL;
10636 if (!context) return IE_INVALID_CONTEXT;
10637 zfree(context->long_message);
10638 if (!message) return IE_INVAL;
10640 if (!message->envelope) {
10641 isds_log_message(context,
10642 _("Given message structure is missing envelope"));
10643 return IE_INVAL;
10645 if (!message->raw) {
10646 isds_log_message(context,
10647 _("Given message structure is missing raw representation"));
10648 return IE_INVAL;
10651 err = isds_download_message_hash(context, message->envelope->dmID,
10652 &downloaded_hash);
10653 if (err) goto leave;
10655 err = isds_compute_message_hash(context, message,
10656 downloaded_hash->algorithm);
10657 if (err) goto leave;
10659 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
10661 leave:
10662 isds_hash_free(&downloaded_hash);
10663 return err;
10667 /* Search for document by document ID in list of documents. IDs are compared
10668 * as UTF-8 string.
10669 * @documents is list of isds_documents
10670 * @id is document identifier
10671 * @return first matching document or NULL. */
10672 const struct isds_document *isds_find_document_by_id(
10673 const struct isds_list *documents, const char *id) {
10674 const struct isds_list *item;
10675 const struct isds_document *document;
10677 for (item = documents; item; item = item->next) {
10678 document = (struct isds_document *) item->data;
10679 if (!document) continue;
10681 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
10682 return document;
10685 return NULL;
10689 /* Normalize @mime_type to be proper MIME type.
10690 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
10691 * guess regular MIME type (e.g. "application/pdf").
10692 * @mime_type is UTF-8 encoded MIME type to fix
10693 * @return original @mime_type if no better interpretation exists, or array to
10694 * constant static UTF-8 encoded string with proper MIME type. */
10695 char *isds_normalize_mime_type(const char* mime_type) {
10696 if (!mime_type) return NULL;
10698 for (int offset = 0;
10699 offset < sizeof(extension_map_mime)/sizeof(extension_map_mime[0]);
10700 offset += 2) {
10701 if (!xmlStrcmp((const xmlChar*) mime_type, extension_map_mime[offset]))
10702 return (char *) extension_map_mime[offset + 1];
10705 return (char *) mime_type;
10709 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
10710 struct isds_message **message);
10711 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
10712 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
10713 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
10714 struct isds_address **address);
10716 int isds_message_free(struct isds_message **message);
10717 int isds_address_free(struct isds_address **address);
10721 /* Makes known all relevant namespaces to given XPath context
10722 * @xpath_ctx is XPath context
10723 * @message_ns selects proper message name space. Unsigned and signed
10724 * messages and delivery info's differ in prefix and URI. */
10725 _hidden isds_error _isds_register_namespaces(xmlXPathContextPtr xpath_ctx,
10726 const message_ns_type message_ns) {
10727 const xmlChar *message_namespace = NULL;
10729 if (!xpath_ctx) return IE_ERROR;
10731 switch(message_ns) {
10732 case MESSAGE_NS_1:
10733 message_namespace = BAD_CAST ISDS1_NS; break;
10734 case MESSAGE_NS_UNSIGNED:
10735 message_namespace = BAD_CAST ISDS_NS; break;
10736 case MESSAGE_NS_SIGNED_INCOMING:
10737 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
10738 case MESSAGE_NS_SIGNED_OUTGOING:
10739 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
10740 case MESSAGE_NS_SIGNED_DELIVERY:
10741 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
10742 default:
10743 return IE_ENUM;
10746 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
10747 return IE_ERROR;
10748 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
10749 return IE_ERROR;
10750 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
10751 return IE_ERROR;
10752 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
10753 return IE_ERROR;
10754 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
10755 return IE_ERROR;
10756 return IE_SUCCESS;