Set otp->resolution when server cannot send SMS on password change
[libisds.git] / src / isds.c
blob69f3491c2d16988482c5cf8b1ef4468feec66650
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 (context->otp && context->saved_username != username)
1162 context->saved_username = strdup(username);
1164 if (password) {
1165 if (NULL == context->otp_credentials)
1166 context->password = strdup(password);
1167 else
1168 context->password = _isds_astrcat(password,
1169 context->otp_credentials->otp_code);
1171 context->pki_credentials = isds_pki_credentials_duplicate(pki_credentials);
1173 if ((NULL != username && NULL == context->username) ||
1174 (NULL != password && NULL == context->password) ||
1175 (NULL != pki_credentials && NULL == context->pki_credentials) ||
1176 (context->otp && NULL != context->username &&
1177 NULL == context->saved_username)) {
1178 return IE_NOMEM;
1181 return IE_SUCCESS;
1183 #endif
1186 /* Connect and log into ISDS server.
1187 * All required arguments will be copied, you do not have to keep them after
1188 * that.
1189 * ISDS supports six different authentication methods. Exact method is
1190 * selected on @username, @password, @pki_credentials, and @otp arguments:
1191 * - If @pki_credentials == NULL, @username and @password must be supplied
1192 * and then
1193 * - If @otp == NULL, simple authentication by username and password will
1194 * be proceeded.
1195 * - If @otp != NULL, authentication by username and password and OTP
1196 * will be used.
1197 * - If @pki_credentials != NULL, then
1198 * - If @username == NULL, only certificate will be used
1199 * - If @username != NULL, then
1200 * - If @password == NULL, then certificate will be used and
1201 * @username shifts meaning to box ID. This is used for hosted
1202 * services.
1203 * - Otherwise all three arguments will be used.
1204 * Please note, that different cases require different certificate type
1205 * (system qualified one or commercial non qualified one). This library
1206 * does not check such political issues. Please see ISDS Specification
1207 * for more details.
1208 * @url is base address of ISDS web service. Pass extern isds_locator
1209 * variable to use production ISDS instance without client certificate
1210 * authentication (or extern isds_cert_locator with client certificate
1211 * authentication or extern isds_otp_locators with OTP authentication).
1212 * Passing NULL has the same effect, autoselection between isds_locator,
1213 * isds_cert_locator, and isds_otp_locator is performed in addition. You can
1214 * pass extern isds_testing_locator (or isds_cert_testing_locator or
1215 * isds_otp_testing_locator) variable to select testing instance.
1216 * @username is user name of ISDS user or box ID
1217 * @password is user's secret password
1218 * @pki_credentials defines public key cryptographic material to use in client
1219 * authentication.
1220 * @otp selects one-time password authentication method to use, defines OTP
1221 * code (if known) and returns fine grade resolution of OTP procedure.
1222 * @return:
1223 * IE_SUCCESS if authentication succeeds
1224 * IE_NOT_LOGGED_IN if authentication fails. If OTP authentication has been
1225 * requested, fine grade reason will be set into @otp->resolution. Error
1226 * message from server can be obtained by isds_long_message() call.
1227 * IE_PARTIAL_SUCCESS if time-based OTP authentication has been requested and
1228 * server has sent OTP code through side channel. Application is expected to
1229 * fill the code into @otp->otp_code, keep other arguments unchanged, and retry
1230 * this call to complete second phase of TOTP authentication;
1231 * or other appropriate error. */
1232 isds_error isds_login(struct isds_ctx *context, const char *url,
1233 const char *username, const char *password,
1234 const struct isds_pki_credentials *pki_credentials,
1235 struct isds_otp *otp) {
1236 #if HAVE_LIBCURL
1237 isds_error err = IE_NOT_LOGGED_IN;
1238 isds_error soap_err;
1239 xmlNsPtr isds_ns = NULL;
1240 xmlNodePtr request = NULL;
1241 xmlNodePtr response = NULL;
1242 #endif /* HAVE_LIBCURL */
1244 if (!context) return IE_INVALID_CONTEXT;
1245 zfree(context->long_message);
1247 #if HAVE_LIBCURL
1248 /* Close connection if already logged in */
1249 if (context->curl) {
1250 _isds_close_connection(context);
1253 /* Store configuration */
1254 context->type = CTX_TYPE_ISDS;
1255 zfree(context->url);
1257 /* Mangle base URI according to requested authentication method */
1258 if (NULL == pki_credentials) {
1259 isds_log(ILF_SEC, ILL_INFO,
1260 _("Selected authentication method: no certificate, "
1261 "username and password\n"));
1262 if (!username || !password) {
1263 isds_log_message(context,
1264 _("Both username and password must be supplied"));
1265 return IE_INVAL;
1267 context->otp_credentials = otp;
1268 context->otp = (NULL != context->otp_credentials);
1270 if (!context->otp) {
1271 /* Default locator is official system (without certificate or
1272 * OTP) */
1273 context->url = strdup((NULL != url) ? url : isds_locator);
1274 } else {
1275 const char *authenticator_uri = NULL;
1276 if (!url) url = isds_otp_locator;
1277 otp->resolution = OTP_RESOLUTION_UNKNOWN;
1278 switch (context->otp_credentials->method) {
1279 case OTP_HMAC:
1280 isds_log(ILF_SEC, ILL_INFO,
1281 _("Selected authentication method: "
1282 "HMAC-based one-time password\n"));
1283 authenticator_uri =
1284 "%1$sas/processLogin?type=hotp&uri=%1$sapps/";
1285 break;
1286 case OTP_TIME:
1287 isds_log(ILF_SEC, ILL_INFO,
1288 _("Selected authentication method: "
1289 "Time-based one-time password\n"));
1290 if (context->otp_credentials->otp_code == NULL) {
1291 isds_log(ILF_SEC, ILL_INFO,
1292 _("OTP code has not been provided by "
1293 "application, requesting server for "
1294 "new one.\n"));
1295 authenticator_uri =
1296 "%1$sas/processLogin?type=totp&sendSms=true&"
1297 "uri=%1$sapps/";
1298 } else {
1299 isds_log(ILF_SEC, ILL_INFO,
1300 _("OTP code has been provided by "
1301 "application, not requesting server "
1302 "for new one.\n"));
1303 authenticator_uri =
1304 "%1$sas/processLogin?type=totp&"
1305 "uri=%1$sapps/";
1307 break;
1308 default:
1309 isds_log_message(context,
1310 _("Unknown one-time password authentication "
1311 "method requested by application"));
1312 return IE_ENUM;
1314 if (-1 == isds_asprintf(&context->url, authenticator_uri, url))
1315 return IE_NOMEM;
1317 } else {
1318 /* Default locator is official system (with client certificate) */
1319 context->otp = 0;
1320 context->otp_credentials = NULL;
1321 if (!url) url = isds_cert_locator;
1323 if (!username) {
1324 isds_log(ILF_SEC, ILL_INFO,
1325 _("Selected authentication method: system certificate, "
1326 "no username and no password\n"));
1327 password = NULL;
1328 context->url = _isds_astrcat(url, "cert/");
1329 } else {
1330 if (!password) {
1331 isds_log(ILF_SEC, ILL_INFO,
1332 _("Selected authentication method: system certificate, "
1333 "box ID and no password\n"));
1334 context->url = _isds_astrcat(url, "hspis/");
1335 } else {
1336 isds_log(ILF_SEC, ILL_INFO,
1337 _("Selected authentication method: commercial "
1338 "certificate, username and password\n"));
1339 context->url = _isds_astrcat(url, "certds/");
1343 if (!(context->url))
1344 return IE_NOMEM;
1346 /* Prepare CURL handle */
1347 context->curl = curl_easy_init();
1348 if (!(context->curl))
1349 return IE_ERROR;
1351 /* Build log-in request */
1352 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1353 if (!request) {
1354 isds_log_message(context, _("Could not build ISDS log-in request"));
1355 return IE_ERROR;
1357 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1358 if(!isds_ns) {
1359 isds_log_message(context, _("Could not create ISDS name space"));
1360 xmlFreeNode(request);
1361 return IE_ERROR;
1363 xmlSetNs(request, isds_ns);
1365 /* Store credentials */
1366 _isds_discard_credentials(context, 1);
1367 if (_isds_store_credentials(context, username, password, pki_credentials)) {
1368 _isds_discard_credentials(context, 1);
1369 xmlFreeNode(request);
1370 return IE_NOMEM;
1373 isds_log(ILF_ISDS, ILL_DEBUG, _("Logging user %s into server %s\n"),
1374 username, url);
1376 /* Send log-in request */
1377 soap_err = _isds_soap(context, "DS/dz", request, &response, NULL, NULL);
1379 if (context->otp) {
1380 /* Revert context URL from OTP authentication service URL to OTP web
1381 * service base URL for subsequent calls. Potenial isds_login() retry
1382 * will re-set context URL again. */
1383 zfree(context->url);
1384 context->url = _isds_astrcat(url, "apps/");
1385 if (context->url == NULL) {
1386 soap_err = IE_NOMEM;
1388 /* Detach pointer to OTP credentials from context */
1389 context->otp_credentials = NULL;
1392 /* Remove credentials */
1393 _isds_discard_credentials(context, 0);
1395 /* Destroy log-in request */
1396 xmlFreeNode(request);
1398 if (soap_err) {
1399 xmlFreeNodeList(response);
1400 _isds_close_connection(context);
1401 return soap_err;
1404 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1405 * authentication succeeded if soap_err == IE_SUCCESS */
1406 err = IE_SUCCESS;
1408 xmlFreeNodeList(response);
1410 if (!err)
1411 isds_log(ILF_ISDS, ILL_DEBUG,
1412 _("User %s has been logged into server %s successfully\n"),
1413 username, url);
1414 return err;
1415 #else /* not HAVE_LIBCURL */
1416 return IE_NOTSUP;
1417 #endif
1421 /* Log out from ISDS server discards credentials and connection configuration. */
1422 isds_error isds_logout(struct isds_ctx *context) {
1423 if (!context) return IE_INVALID_CONTEXT;
1424 zfree(context->long_message);
1426 #if HAVE_LIBCURL
1427 if (context->curl) {
1428 if (context->otp) {
1429 isds_error err = _isds_invalidate_otp_cookie(context);
1430 if (err) return err;
1433 /* Close connection */
1434 _isds_close_connection(context);
1436 /* Discard credentials for sure. They should not survive isds_login(),
1437 * even successful .*/
1438 _isds_discard_credentials(context, 1);
1439 zfree(context->url);
1441 isds_log(ILF_ISDS, ILL_DEBUG, _("Logged out from ISDS server\n"));
1442 } else {
1443 _isds_discard_credentials(context, 1);
1445 return IE_SUCCESS;
1446 #else /* not HAVE_LIBCURL */
1447 return IE_NOTSUP;
1448 #endif
1452 /* Verify connection to ISDS is alive and server is responding.
1453 * Sent dummy request to ISDS and expect dummy response. */
1454 isds_error isds_ping(struct isds_ctx *context) {
1455 #if HAVE_LIBCURL
1456 isds_error soap_err;
1457 xmlNsPtr isds_ns = NULL;
1458 xmlNodePtr request = NULL;
1459 xmlNodePtr response = NULL;
1460 #endif /* HAVE_LIBCURL */
1462 if (!context) return IE_INVALID_CONTEXT;
1463 zfree(context->long_message);
1465 #if HAVE_LIBCURL
1466 /* Check if connection is established */
1467 if (!context->curl) return IE_CONNECTION_CLOSED;
1470 /* Build dummy request */
1471 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1472 if (!request) {
1473 isds_log_message(context, _("Could build ISDS dummy request"));
1474 return IE_ERROR;
1476 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1477 if(!isds_ns) {
1478 isds_log_message(context, _("Could not create ISDS name space"));
1479 xmlFreeNode(request);
1480 return IE_ERROR;
1482 xmlSetNs(request, isds_ns);
1484 isds_log(ILF_ISDS, ILL_DEBUG, _("Pinging ISDS server\n"));
1486 /* Sent dummy request */
1487 soap_err = _isds_soap(context, "DS/dz", request, &response, NULL, NULL);
1489 /* Destroy log-in request */
1490 xmlFreeNode(request);
1492 if (soap_err) {
1493 isds_log(ILF_ISDS, ILL_DEBUG,
1494 _("ISDS server could not be contacted\n"));
1495 xmlFreeNodeList(response);
1496 return soap_err;
1499 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1500 * authentication succeeded if soap_err == IE_SUCCESS */
1501 /* TODO: ISDS documentation does not specify response body.
1502 * However real server sends back DummyOperationResponse */
1505 xmlFreeNodeList(response);
1507 isds_log(ILF_ISDS, ILL_DEBUG, _("ISDS server alive\n"));
1509 return IE_SUCCESS;
1510 #else /* not HAVE_LIBCURL */
1511 return IE_NOTSUP;
1512 #endif
1516 /* Send bogus request to ISDS.
1517 * Just for test purposes */
1518 isds_error isds_bogus_request(struct isds_ctx *context) {
1519 #if HAVE_LIBCURL
1520 isds_error err;
1521 xmlNsPtr isds_ns = NULL;
1522 xmlNodePtr request = NULL;
1523 xmlDocPtr response = NULL;
1524 xmlChar *code = NULL, *message = NULL;
1525 #endif
1527 if (!context) return IE_INVALID_CONTEXT;
1528 zfree(context->long_message);
1530 #if HAVE_LIBCURL
1531 /* Check if connection is established */
1532 if (!context->curl) {
1533 /* Testing printf message */
1534 isds_printf_message(context, "%s", _("I said connection closed"));
1535 return IE_CONNECTION_CLOSED;
1539 /* Build dummy request */
1540 request = xmlNewNode(NULL, BAD_CAST "X-BogusOperation");
1541 if (!request) {
1542 isds_log_message(context, _("Could build ISDS bogus request"));
1543 return IE_ERROR;
1545 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1546 if(!isds_ns) {
1547 isds_log_message(context, _("Could not create ISDS name space"));
1548 xmlFreeNode(request);
1549 return IE_ERROR;
1551 xmlSetNs(request, isds_ns);
1553 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending bogus request to ISDS\n"));
1555 /* Sent bogus request */
1556 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
1558 /* Destroy request */
1559 xmlFreeNode(request);
1561 if (err) {
1562 isds_log(ILF_ISDS, ILL_DEBUG,
1563 _("Processing ISDS response on bogus request failed\n"));
1564 xmlFreeDoc(response);
1565 return err;
1568 /* Check for response status */
1569 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
1570 &code, &message, NULL);
1571 if (err) {
1572 isds_log(ILF_ISDS, ILL_DEBUG,
1573 _("ISDS response on bogus request is missing status\n"));
1574 free(code);
1575 free(message);
1576 xmlFreeDoc(response);
1577 return err;
1579 if (xmlStrcmp(code, BAD_CAST "0000")) {
1580 char *code_locale = _isds_utf82locale((char*)code);
1581 char *message_locale = _isds_utf82locale((char*)message);
1582 isds_log(ILF_ISDS, ILL_DEBUG,
1583 _("Server refused bogus request (code=%s, message=%s)\n"),
1584 code_locale, message_locale);
1585 /* XXX: Literal error messages from ISDS are Czech messages
1586 * (English sometimes) in UTF-8. It's hard to catch them for
1587 * translation. Successfully gettextized would return in locale
1588 * encoding, unsuccessfully translated would pass in UTF-8. */
1589 isds_log_message(context, message_locale);
1590 free(code_locale);
1591 free(message_locale);
1592 free(code);
1593 free(message);
1594 xmlFreeDoc(response);
1595 return IE_ISDS;
1599 free(code);
1600 free(message);
1601 xmlFreeDoc(response);
1603 isds_log(ILF_ISDS, ILL_DEBUG,
1604 _("Bogus message accepted by server. This should not happen.\n"));
1606 return IE_SUCCESS;
1607 #else /* not HAVE_LIBCURL */
1608 return IE_NOTSUP;
1609 #endif
1613 #if HAVE_LIBCURL
1614 /* Serialize XML subtree to buffer preserving XML indentation.
1615 * @context is session context
1616 * @subtree is XML element to be serialized (with children)
1617 * @buffer is automatically reallocated buffer where serialize to
1618 * @length is size of serialized stream in bytes
1619 * @return standard error code, free @buffer in case of error */
1620 static isds_error serialize_subtree(struct isds_ctx *context,
1621 xmlNodePtr subtree, void **buffer, size_t *length) {
1622 isds_error err = IE_SUCCESS;
1623 xmlBufferPtr xml_buffer = NULL;
1624 xmlSaveCtxtPtr save_ctx = NULL;
1625 xmlDocPtr subtree_doc = NULL;
1626 xmlNodePtr subtree_copy;
1627 xmlNsPtr isds_ns;
1628 void *new_buffer;
1630 if (!context) return IE_INVALID_CONTEXT;
1631 if (!buffer) return IE_INVAL;
1632 zfree(*buffer);
1633 if (!subtree || !length) return IE_INVAL;
1635 /* Make temporary XML document with @subtree root element */
1636 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1637 * It can result in not well-formed on invalid XML tree (e.g. name space
1638 * prefix definition can miss. */
1639 /*FIXME */
1641 subtree_doc = xmlNewDoc(BAD_CAST "1.0");
1642 if (!subtree_doc) {
1643 isds_log_message(context, _("Could not build temporary document"));
1644 err = IE_ERROR;
1645 goto leave;
1648 /* XXX: Copy subtree and attach the copy to document.
1649 * One node can not bee attached into more document at the same time.
1650 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1651 * automatically.
1652 * XXX: Check xmlSaveTree() too. */
1653 subtree_copy = xmlCopyNodeList(subtree);
1654 if (!subtree_copy) {
1655 isds_log_message(context, _("Could not copy subtree"));
1656 err = IE_ERROR;
1657 goto leave;
1659 xmlDocSetRootElement(subtree_doc, subtree_copy);
1661 /* Only this way we get namespace definition as @xmlns:isds,
1662 * otherwise we get namespace prefix without definition */
1663 /* FIXME: Don't overwrite original default namespace */
1664 isds_ns = xmlNewNs(subtree_copy, BAD_CAST ISDS_NS, NULL);
1665 if(!isds_ns) {
1666 isds_log_message(context, _("Could not create ISDS name space"));
1667 err = IE_ERROR;
1668 goto leave;
1670 xmlSetNs(subtree_copy, isds_ns);
1673 /* Serialize the document into buffer */
1674 xml_buffer = xmlBufferCreate();
1675 if (!xml_buffer) {
1676 isds_log_message(context, _("Could not create xmlBuffer"));
1677 err = IE_ERROR;
1678 goto leave;
1680 /* Last argument 0 means to not format the XML tree */
1681 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8", 0);
1682 if (!save_ctx) {
1683 isds_log_message(context, _("Could not create XML serializer"));
1684 err = IE_ERROR;
1685 goto leave;
1687 /* XXX: According LibXML documentation, this function does not return
1688 * meaningful value yet */
1689 xmlSaveDoc(save_ctx, subtree_doc);
1690 if (-1 == xmlSaveFlush(save_ctx)) {
1691 isds_log_message(context,
1692 _("Could not serialize XML subtree"));
1693 err = IE_ERROR;
1694 goto leave;
1696 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1697 * even after xmlSaveFlush(). Thus close it here */
1698 xmlSaveClose(save_ctx); save_ctx = NULL;
1701 /* Store and detach buffer from xml_buffer */
1702 *buffer = xml_buffer->content;
1703 *length = xml_buffer->use;
1704 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1706 /* Shrink buffer */
1707 new_buffer = realloc(*buffer, *length);
1708 if (new_buffer) *buffer = new_buffer;
1710 leave:
1711 if (err) {
1712 zfree(*buffer);
1713 *length = 0;
1716 xmlSaveClose(save_ctx);
1717 xmlBufferFree(xml_buffer);
1718 xmlFreeDoc(subtree_doc); /* Frees subtree_copy, isds_ns etc. */
1719 return err;
1721 #endif /* HAVE_LIBCURL */
1724 #if 0
1725 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1726 * @context is session context
1727 * @document is original document where @nodeset points to
1728 * @nodeset is XPath node set to dump (recursively)
1729 * @buffer is automatically reallocated buffer where serialize to
1730 * @length is size of serialized stream in bytes
1731 * @return standard error code, free @buffer in case of error */
1732 static isds_error dump_nodeset(struct isds_ctx *context,
1733 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1734 void **buffer, size_t *length) {
1735 isds_error err = IE_SUCCESS;
1736 xmlBufferPtr xml_buffer = NULL;
1737 void *new_buffer;
1739 if (!context) return IE_INVALID_CONTEXT;
1740 if (!buffer) return IE_INVAL;
1741 zfree(*buffer);
1742 if (!document || !nodeset || !length) return IE_INVAL;
1743 *length = 0;
1745 /* Empty node set results into NULL buffer */
1746 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1747 goto leave;
1750 /* Resulting the document into buffer */
1751 xml_buffer = xmlBufferCreate();
1752 if (!xml_buffer) {
1753 isds_log_message(context, _("Could not create xmlBuffer"));
1754 err = IE_ERROR;
1755 goto leave;
1758 /* Iterate over all nodes */
1759 for (int i = 0; i < nodeset->nodeNr; i++) {
1760 /* Serialize node.
1761 * XXX: xmlNodeDump() appends to xml_buffer. */
1762 if (-1 ==
1763 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1764 isds_log_message(context, _("Could not dump XML node"));
1765 err = IE_ERROR;
1766 goto leave;
1770 /* Store and detach buffer from xml_buffer */
1771 *buffer = xml_buffer->content;
1772 *length = xml_buffer->use;
1773 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1775 /* Shrink buffer */
1776 new_buffer = realloc(*buffer, *length);
1777 if (new_buffer) *buffer = new_buffer;
1780 leave:
1781 if (err) {
1782 zfree(*buffer);
1783 *length = 0;
1786 xmlBufferFree(xml_buffer);
1787 return err;
1789 #endif
1791 #if 0
1792 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1793 * @context is session context
1794 * @document is original document where @nodeset points to
1795 * @nodeset is XPath node set to dump (recursively)
1796 * @buffer is automatically reallocated buffer where serialize to
1797 * @length is size of serialized stream in bytes
1798 * @return standard error code, free @buffer in case of error */
1799 static isds_error dump_nodeset(struct isds_ctx *context,
1800 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1801 void **buffer, size_t *length) {
1802 isds_error err = IE_SUCCESS;
1803 xmlBufferPtr xml_buffer = NULL;
1804 xmlSaveCtxtPtr save_ctx = NULL;
1805 void *new_buffer;
1807 if (!context) return IE_INVALID_CONTEXT;
1808 if (!buffer) return IE_INVAL;
1809 zfree(*buffer);
1810 if (!document || !nodeset || !length) return IE_INVAL;
1811 *length = 0;
1813 /* Empty node set results into NULL buffer */
1814 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1815 goto leave;
1818 /* Resulting the document into buffer */
1819 xml_buffer = xmlBufferCreate();
1820 if (!xml_buffer) {
1821 isds_log_message(context, _("Could not create xmlBuffer"));
1822 err = IE_ERROR;
1823 goto leave;
1825 if (xmlSubstituteEntitiesDefault(1)) {
1826 isds_log_message(context, _("Could not disable attribute escaping"));
1827 err = IE_ERROR;
1828 goto leave;
1830 /* Last argument means:
1831 * 0 to not format the XML tree
1832 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1833 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8",
1834 XML_SAVE_NO_DECL|XML_SAVE_NO_EMPTY|XML_SAVE_NO_XHTML);
1835 if (!save_ctx) {
1836 isds_log_message(context, _("Could not create XML serializer"));
1837 err = IE_ERROR;
1838 goto leave;
1840 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1841 isds_log_message(context, _("Could not disable attribute escaping"));
1842 err = IE_ERROR;
1843 goto leave;
1847 /* Iterate over all nodes */
1848 for (int i = 0; i < nodeset->nodeNr; i++) {
1849 /* Serialize node.
1850 * XXX: xmlNodeDump() appends to xml_buffer. */
1851 /*if (-1 ==
1852 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1854 /* XXX: According LibXML documentation, this function does not return
1855 * meaningful value yet */
1856 xmlSaveTree(save_ctx, nodeset->nodeTab[i]);
1857 if (-1 == xmlSaveFlush(save_ctx)) {
1858 isds_log_message(context,
1859 _("Could not serialize XML subtree"));
1860 err = IE_ERROR;
1861 goto leave;
1865 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1866 * even after xmlSaveFlush(). Thus close it here */
1867 xmlSaveClose(save_ctx); save_ctx = NULL;
1869 /* Store and detach buffer from xml_buffer */
1870 *buffer = xml_buffer->content;
1871 *length = xml_buffer->use;
1872 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1874 /* Shrink buffer */
1875 new_buffer = realloc(*buffer, *length);
1876 if (new_buffer) *buffer = new_buffer;
1878 leave:
1879 if (err) {
1880 zfree(*buffer);
1881 *length = 0;
1884 xmlSaveClose(save_ctx);
1885 xmlBufferFree(xml_buffer);
1886 return err;
1888 #endif
1891 #if HAVE_LIBCURL
1892 /* Convert UTF-8 @string representation of ISDS dbType to enum @type */
1893 static isds_error string2isds_DbType(xmlChar *string, isds_DbType *type) {
1894 if (!string || !type) return IE_INVAL;
1896 if (!xmlStrcmp(string, BAD_CAST "FO"))
1897 *type = DBTYPE_FO;
1898 else if (!xmlStrcmp(string, BAD_CAST "PFO"))
1899 *type = DBTYPE_PFO;
1900 else if (!xmlStrcmp(string, BAD_CAST "PFO_ADVOK"))
1901 *type = DBTYPE_PFO_ADVOK;
1902 else if (!xmlStrcmp(string, BAD_CAST "PFO_DANPOR"))
1903 *type = DBTYPE_PFO_DANPOR;
1904 else if (!xmlStrcmp(string, BAD_CAST "PFO_INSSPR"))
1905 *type = DBTYPE_PFO_INSSPR;
1906 else if (!xmlStrcmp(string, BAD_CAST "PO"))
1907 *type = DBTYPE_PO;
1908 else if (!xmlStrcmp(string, BAD_CAST "PO_ZAK"))
1909 *type = DBTYPE_PO_ZAK;
1910 else if (!xmlStrcmp(string, BAD_CAST "PO_REQ"))
1911 *type = DBTYPE_PO_REQ;
1912 else if (!xmlStrcmp(string, BAD_CAST "OVM"))
1913 *type = DBTYPE_OVM;
1914 else if (!xmlStrcmp(string, BAD_CAST "OVM_NOTAR"))
1915 *type = DBTYPE_OVM_NOTAR;
1916 else if (!xmlStrcmp(string, BAD_CAST "OVM_EXEKUT"))
1917 *type = DBTYPE_OVM_EXEKUT;
1918 else if (!xmlStrcmp(string, BAD_CAST "OVM_REQ"))
1919 *type = DBTYPE_OVM_REQ;
1920 else
1921 return IE_ENUM;
1922 return IE_SUCCESS;
1926 /* Convert ISDS dbType enum @type to UTF-8 string.
1927 * @Return pointer to static string, or NULL if unknown enum value */
1928 static const xmlChar *isds_DbType2string(const isds_DbType type) {
1929 switch(type) {
1930 /* DBTYPE_SYSTEM is invalid value from point of view of public
1931 * SOAP interface. */
1932 case DBTYPE_FO: return(BAD_CAST "FO"); break;
1933 case DBTYPE_PFO: return(BAD_CAST "PFO"); break;
1934 case DBTYPE_PFO_ADVOK: return(BAD_CAST "PFO_ADVOK"); break;
1935 case DBTYPE_PFO_DANPOR: return(BAD_CAST "PFO_DANPOR"); break;
1936 case DBTYPE_PFO_INSSPR: return(BAD_CAST "PFO_INSSPR"); break;
1937 case DBTYPE_PO: return(BAD_CAST "PO"); break;
1938 case DBTYPE_PO_ZAK: return(BAD_CAST "PO_ZAK"); break;
1939 case DBTYPE_PO_REQ: return(BAD_CAST "PO_REQ"); break;
1940 case DBTYPE_OVM: return(BAD_CAST "OVM"); break;
1941 case DBTYPE_OVM_NOTAR: return(BAD_CAST "OVM_NOTAR"); break;
1942 case DBTYPE_OVM_EXEKUT: return(BAD_CAST "OVM_EXEKUT"); break;
1943 case DBTYPE_OVM_REQ: return(BAD_CAST "OVM_REQ"); break;
1944 default: return NULL; break;
1949 /* Convert UTF-8 @string representation of ISDS userType to enum @type */
1950 static isds_error string2isds_UserType(xmlChar *string, isds_UserType *type) {
1951 if (!string || !type) return IE_INVAL;
1953 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
1954 *type = USERTYPE_PRIMARY;
1955 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
1956 *type = USERTYPE_ENTRUSTED;
1957 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
1958 *type = USERTYPE_ADMINISTRATOR;
1959 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
1960 *type = USERTYPE_OFFICIAL;
1961 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
1962 *type = USERTYPE_OFFICIAL_CERT;
1963 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
1964 *type = USERTYPE_LIQUIDATOR;
1965 else
1966 return IE_ENUM;
1967 return IE_SUCCESS;
1971 /* Convert ISDS userType enum @type to UTF-8 string.
1972 * @Return pointer to static string, or NULL if unknown enum value */
1973 static const xmlChar *isds_UserType2string(const isds_UserType type) {
1974 switch(type) {
1975 case USERTYPE_PRIMARY: return(BAD_CAST "PRIMARY_USER"); break;
1976 case USERTYPE_ENTRUSTED: return(BAD_CAST "ENTRUSTED_USER"); break;
1977 case USERTYPE_ADMINISTRATOR: return(BAD_CAST "ADMINISTRATOR"); break;
1978 case USERTYPE_OFFICIAL: return(BAD_CAST "OFFICIAL"); break;
1979 case USERTYPE_OFFICIAL_CERT: return(BAD_CAST "OFFICIAL_CERT"); break;
1980 case USERTYPE_LIQUIDATOR: return(BAD_CAST "LIQUIDATOR"); break;
1981 default: return NULL; break;
1986 /* Convert UTF-8 @string representation of ISDS sender type to enum @type */
1987 static isds_error string2isds_sender_type(const xmlChar *string,
1988 isds_sender_type *type) {
1989 if (!string || !type) return IE_INVAL;
1991 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
1992 *type = SENDERTYPE_PRIMARY;
1993 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
1994 *type = SENDERTYPE_ENTRUSTED;
1995 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
1996 *type = SENDERTYPE_ADMINISTRATOR;
1997 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
1998 *type = SENDERTYPE_OFFICIAL;
1999 else if (!xmlStrcmp(string, BAD_CAST "VIRTUAL"))
2000 *type = SENDERTYPE_VIRTUAL;
2001 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
2002 *type = SENDERTYPE_OFFICIAL_CERT;
2003 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
2004 *type = SENDERTYPE_LIQUIDATOR;
2005 else
2006 return IE_ENUM;
2007 return IE_SUCCESS;
2011 /* Convert UTF-8 @string representation of ISDS PDZType to enum @type */
2012 static isds_error string2isds_payment_type(const xmlChar *string,
2013 isds_payment_type *type) {
2014 if (!string || !type) return IE_INVAL;
2016 if (!xmlStrcmp(string, BAD_CAST "K"))
2017 *type = PAYMENT_SENDER;
2018 else if (!xmlStrcmp(string, BAD_CAST "O"))
2019 *type = PAYMENT_RESPONSE;
2020 else if (!xmlStrcmp(string, BAD_CAST "G"))
2021 *type = PAYMENT_SPONSOR;
2022 else if (!xmlStrcmp(string, BAD_CAST "Z"))
2023 *type = PAYMENT_SPONSOR_LIMITED;
2024 else if (!xmlStrcmp(string, BAD_CAST "D"))
2025 *type = PAYMENT_SPONSOR_EXTERNAL;
2026 else if (!xmlStrcmp(string, BAD_CAST "E"))
2027 *type = PAYMENT_STAMP;
2028 else
2029 return IE_ENUM;
2030 return IE_SUCCESS;
2034 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
2035 * @Return pointer to static string, or NULL if unknown enum value */
2036 static const xmlChar *isds_FileMetaType2string(const isds_FileMetaType type) {
2037 switch(type) {
2038 case FILEMETATYPE_MAIN: return(BAD_CAST "main"); break;
2039 case FILEMETATYPE_ENCLOSURE: return(BAD_CAST "enclosure"); break;
2040 case FILEMETATYPE_SIGNATURE: return(BAD_CAST "signature"); break;
2041 case FILEMETATYPE_META: return(BAD_CAST "meta"); break;
2042 default: return NULL; break;
2045 #endif /* HAVE_LIBCURL */
2048 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
2049 * @Return IE_ENUM if @string is not valid enum member */
2050 static isds_error string2isds_FileMetaType(const xmlChar *string,
2051 isds_FileMetaType *type) {
2052 if (!string || !type) return IE_INVAL;
2054 if (!xmlStrcmp(string, BAD_CAST "main"))
2055 *type = FILEMETATYPE_MAIN;
2056 else if (!xmlStrcmp(string, BAD_CAST "enclosure"))
2057 *type = FILEMETATYPE_ENCLOSURE;
2058 else if (!xmlStrcmp(string, BAD_CAST "signature"))
2059 *type = FILEMETATYPE_SIGNATURE;
2060 else if (!xmlStrcmp(string, BAD_CAST "meta"))
2061 *type = FILEMETATYPE_META;
2062 else
2063 return IE_ENUM;
2064 return IE_SUCCESS;
2068 /* Convert UTF-8 @string to ISDS hash @algorithm.
2069 * @Return IE_ENUM if @string is not valid enum member */
2070 static isds_error string2isds_hash_algorithm(const xmlChar *string,
2071 isds_hash_algorithm *algorithm) {
2072 if (!string || !algorithm) return IE_INVAL;
2074 if (!xmlStrcmp(string, BAD_CAST "MD5"))
2075 *algorithm = HASH_ALGORITHM_MD5;
2076 else if (!xmlStrcmp(string, BAD_CAST "SHA-1"))
2077 *algorithm = HASH_ALGORITHM_SHA_1;
2078 else if (!xmlStrcmp(string, BAD_CAST "SHA-224"))
2079 *algorithm = HASH_ALGORITHM_SHA_224;
2080 else if (!xmlStrcmp(string, BAD_CAST "SHA-256"))
2081 *algorithm = HASH_ALGORITHM_SHA_256;
2082 else if (!xmlStrcmp(string, BAD_CAST "SHA-384"))
2083 *algorithm = HASH_ALGORITHM_SHA_384;
2084 else if (!xmlStrcmp(string, BAD_CAST "SHA-512"))
2085 *algorithm = HASH_ALGORITHM_SHA_512;
2086 else
2087 return IE_ENUM;
2088 return IE_SUCCESS;
2092 #if HAVE_LIBCURL
2093 /* Convert UTF-8 @string representation of ISO 8601 date to @time.
2094 * XXX: Not all ISO formats are supported */
2095 static isds_error datestring2tm(const xmlChar *string, struct tm *time) {
2096 char *offset;
2097 if (!string || !time) return IE_INVAL;
2099 /* xsd:date is ISO 8601 string, thus ASCII */
2100 offset = strptime((char*)string, "%Y-%m-%d", time);
2101 if (offset && *offset == '\0')
2102 return IE_SUCCESS;
2104 offset = strptime((char*)string, "%Y%m%d", time);
2105 if (offset && *offset == '\0')
2106 return IE_SUCCESS;
2108 offset = strptime((char*)string, "%Y-%j", time);
2109 if (offset && *offset == '\0')
2110 return IE_SUCCESS;
2112 return IE_NOTSUP;
2116 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
2117 static isds_error tm2datestring(const struct tm *time, xmlChar **string) {
2118 if (!time || !string) return IE_INVAL;
2120 if (-1 == isds_asprintf((char **) string, "%d-%02d-%02d",
2121 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
2122 return IE_ERROR;
2124 return IE_SUCCESS;
2128 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
2129 * respects the @time microseconds too. */
2130 static isds_error timeval2timestring(const struct timeval *time,
2131 xmlChar **string) {
2132 struct tm broken;
2134 if (!time || !string) return IE_INVAL;
2136 if (!gmtime_r(&time->tv_sec, &broken)) return IE_DATE;
2137 if (time->tv_usec < 0 || time->tv_usec > 999999) return IE_DATE;
2139 /* TODO: small negative year should be formatted as "-0012". This is not
2140 * true for glibc "%04d". We should implement it.
2141 * TODO: What's type of time->tv_usec exactly? Unsigned? Absolute?
2142 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
2143 if (-1 == isds_asprintf((char **) string,
2144 "%04d-%02d-%02dT%02d:%02d:%02d.%06ld",
2145 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
2146 broken.tm_hour, broken.tm_min, broken.tm_sec,
2147 time->tv_usec))
2148 return IE_ERROR;
2150 return IE_SUCCESS;
2152 #endif /* HAVE_LIBCURL */
2155 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
2156 * It respects microseconds too.
2157 * In case of error, @time will be freed. */
2158 static isds_error timestring2timeval(const xmlChar *string,
2159 struct timeval **time) {
2160 struct tm broken;
2161 char *offset, *delim, *endptr;
2162 char subseconds[7];
2163 int offset_hours, offset_minutes;
2164 int i;
2166 if (!time) return IE_INVAL;
2167 if (!string) {
2168 zfree(*time);
2169 return IE_INVAL;
2172 memset(&broken, 0, sizeof(broken));
2174 if (!*time) {
2175 *time = calloc(1, sizeof(**time));
2176 if (!*time) return IE_NOMEM;
2177 } else {
2178 memset(*time, 0, sizeof(**time));
2182 /* xsd:date is ISO 8601 string, thus ASCII */
2183 /*TODO: negative year */
2185 /* Parse date and time without subseconds and offset */
2186 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
2187 if (!offset) {
2188 zfree(*time);
2189 return IE_DATE;
2192 /* Get subseconds */
2193 if (*offset == '.' ) {
2194 offset++;
2196 /* Copy first 6 digits, pad it with zeros.
2197 * XXX: It truncates longer number, no round.
2198 * Current server implementation uses only millisecond resolution. */
2199 /* TODO: isdigit() is locale sensitive */
2200 for (i = 0;
2201 i < sizeof(subseconds)/sizeof(char) - 1 && isdigit(*offset);
2202 i++, offset++) {
2203 subseconds[i] = *offset;
2205 for (; i < sizeof(subseconds)/sizeof(char) - 1; i++) {
2206 subseconds[i] = '0';
2208 subseconds[6] = '\0';
2210 /* Convert it into integer */
2211 (*time)->tv_usec = strtol(subseconds, &endptr, 10);
2212 if (*endptr != '\0' || (*time)->tv_usec == LONG_MIN ||
2213 (*time)->tv_usec == LONG_MAX) {
2214 zfree(*time);
2215 return IE_DATE;
2218 /* move to the zone offset delimiter or signal NULL*/
2219 delim = strchr(offset, '-');
2220 if (!delim)
2221 delim = strchr(offset, '+');
2222 if (!delim)
2223 delim = strchr(offset, 'Z');
2224 offset = delim;
2227 /* Get zone offset */
2228 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2229 * "" equals to "Z" and it means UTC zone. */
2230 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2231 * colon separator */
2232 if (offset && (*offset == '-' || *offset == '+')) {
2233 if (2 != sscanf(offset + 1, "%2d:%2d", &offset_hours, &offset_minutes)) {
2234 zfree(*time);
2235 return IE_DATE;
2237 if (*offset == '+') {
2238 broken.tm_hour -= offset_hours;
2239 broken.tm_min -= offset_minutes;
2240 } else {
2241 broken.tm_hour += offset_hours;
2242 broken.tm_min += offset_minutes;
2246 /* Convert to time_t */
2247 (*time)->tv_sec = _isds_timegm(&broken);
2248 if ((*time)->tv_sec == (time_t) -1) {
2249 zfree(*time);
2250 return IE_DATE;
2253 return IE_SUCCESS;
2257 /* Convert unsigned int into isds_message_status.
2258 * @context is session context
2259 * @number is pointer to number value. NULL will be treated as invalid value.
2260 * @status is automatically reallocated status
2261 * @return IE_SUCCESS, or error code and free status */
2262 static isds_error uint2isds_message_status(struct isds_ctx *context,
2263 const unsigned long int *number, isds_message_status **status) {
2264 if (!context) return IE_INVALID_CONTEXT;
2265 if (!status) return IE_INVAL;
2267 free(*status); *status = NULL;
2268 if (!number) return IE_INVAL;
2270 if (*number < 1 || *number > 10) {
2271 isds_printf_message(context, _("Invalid message status value: %lu"),
2272 *number);
2273 return IE_ENUM;
2276 *status = malloc(sizeof(**status));
2277 if (!*status) return IE_NOMEM;
2279 **status = 1 << *number;
2280 return IE_SUCCESS;
2284 /* Convert event description string into isds_event members type and
2285 * description
2286 * @string is raw event description starting with event prefix
2287 * @event is structure where to store type and stripped description to
2288 * @return standard error code, unknown prefix is not classified as an error.
2289 * */
2290 static isds_error eventstring2event(const xmlChar *string,
2291 struct isds_event* event) {
2292 const xmlChar *known_prefixes[] = {
2293 BAD_CAST "EV0:",
2294 BAD_CAST "EV1:",
2295 BAD_CAST "EV2:",
2296 BAD_CAST "EV3:",
2297 BAD_CAST "EV4:",
2298 BAD_CAST "EV5:",
2299 BAD_CAST "EV11:",
2300 BAD_CAST "EV12:",
2301 BAD_CAST "EV13:"
2303 const isds_event_type types[] = {
2304 EVENT_ENTERED_SYSTEM,
2305 EVENT_ACCEPTED_BY_RECIPIENT,
2306 EVENT_ACCEPTED_BY_FICTION,
2307 EVENT_UNDELIVERABLE,
2308 EVENT_COMMERCIAL_ACCEPTED,
2309 EVENT_DELIVERED,
2310 EVENT_PRIMARY_LOGIN,
2311 EVENT_ENTRUSTED_LOGIN,
2312 EVENT_SYSCERT_LOGIN
2314 unsigned int index;
2315 size_t length;
2317 if (!string || !event) return IE_INVAL;
2319 if (!event->type) {
2320 event->type = malloc(sizeof(*event->type));
2321 if (!(event->type)) return IE_NOMEM;
2323 zfree(event->description);
2325 for (index = 0; index < sizeof(known_prefixes)/sizeof(known_prefixes[0]);
2326 index++) {
2327 length = xmlUTF8Strlen(known_prefixes[index]);
2329 if (!xmlStrncmp(string, known_prefixes[index], length)) {
2330 /* Prefix is known */
2331 *event->type = types[index];
2333 /* Strip prefix from description and spaces */
2334 /* TODO: Recognize all white spaces from UCS blank class and
2335 * operate on UTF-8 chars. */
2336 for (; string[length] != '\0' && string[length] == ' '; length++);
2337 event->description = strdup((char *) (string + length));
2338 if (!(event->description)) return IE_NOMEM;
2340 return IE_SUCCESS;
2344 /* Unknown event prefix.
2345 * XSD allows any string */
2346 char *string_locale = _isds_utf82locale((char *) string);
2347 isds_log(ILF_ISDS, ILL_WARNING,
2348 _("Unknown delivery info event prefix: %s\n"), string_locale);
2349 free(string_locale);
2351 *event->type = EVENT_UKNOWN;
2352 event->description = strdup((char *) string);
2353 if (!(event->description)) return IE_NOMEM;
2355 return IE_SUCCESS;
2359 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2360 * and leave label */
2361 #define EXTRACT_STRING(element, string) { \
2362 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2363 if (!result) { \
2364 err = IE_ERROR; \
2365 goto leave; \
2367 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2368 if (result->nodesetval->nodeNr > 1) { \
2369 isds_printf_message(context, _("Multiple %s element"), element); \
2370 err = IE_ERROR; \
2371 goto leave; \
2373 (string) = (char *) \
2374 xmlXPathCastNodeSetToString(result->nodesetval); \
2375 if (!(string)) { \
2376 err = IE_ERROR; \
2377 goto leave; \
2382 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2384 char *string = NULL; \
2385 EXTRACT_STRING(element, string); \
2387 if (string) { \
2388 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2389 if (!(booleanPtr)) { \
2390 free(string); \
2391 err = IE_NOMEM; \
2392 goto leave; \
2395 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2396 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2397 *(booleanPtr) = 1; \
2398 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2399 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2400 *(booleanPtr) = 0; \
2401 else { \
2402 char *string_locale = _isds_utf82locale((char*)string); \
2403 isds_printf_message(context, \
2404 _("%s value is not valid boolean: %s"), \
2405 element, string_locale); \
2406 free(string_locale); \
2407 free(string); \
2408 err = IE_ERROR; \
2409 goto leave; \
2412 free(string); \
2416 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2418 char *string = NULL; \
2419 EXTRACT_STRING(element, string); \
2420 if (string) { \
2421 long int number; \
2422 char *endptr; \
2424 number = strtol((char*)string, &endptr, 10); \
2426 if (*endptr != '\0') { \
2427 char *string_locale = _isds_utf82locale((char *)string); \
2428 isds_printf_message(context, \
2429 _("%s is not valid integer: %s"), \
2430 element, string_locale); \
2431 free(string_locale); \
2432 free(string); \
2433 err = IE_ISDS; \
2434 goto leave; \
2437 if (number == LONG_MIN || number == LONG_MAX) { \
2438 char *string_locale = _isds_utf82locale((char *)string); \
2439 isds_printf_message(context, \
2440 _("%s value out of range of long int: %s"), \
2441 element, string_locale); \
2442 free(string_locale); \
2443 free(string); \
2444 err = IE_ERROR; \
2445 goto leave; \
2448 free(string); string = NULL; \
2450 if (!(preallocated)) { \
2451 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2452 if (!(longintPtr)) { \
2453 err = IE_NOMEM; \
2454 goto leave; \
2457 *(longintPtr) = number; \
2461 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2463 char *string = NULL; \
2464 EXTRACT_STRING(element, string); \
2465 if (string) { \
2466 long int number; \
2467 char *endptr; \
2469 number = strtol((char*)string, &endptr, 10); \
2471 if (*endptr != '\0') { \
2472 char *string_locale = _isds_utf82locale((char *)string); \
2473 isds_printf_message(context, \
2474 _("%s is not valid integer: %s"), \
2475 element, string_locale); \
2476 free(string_locale); \
2477 free(string); \
2478 err = IE_ISDS; \
2479 goto leave; \
2482 if (number == LONG_MIN || number == LONG_MAX) { \
2483 char *string_locale = _isds_utf82locale((char *)string); \
2484 isds_printf_message(context, \
2485 _("%s value out of range of long int: %s"), \
2486 element, string_locale); \
2487 free(string_locale); \
2488 free(string); \
2489 err = IE_ERROR; \
2490 goto leave; \
2493 free(string); string = NULL; \
2494 if (number < 0) { \
2495 isds_printf_message(context, \
2496 _("%s value is negative: %ld"), element, number); \
2497 err = IE_ERROR; \
2498 goto leave; \
2501 if (!(preallocated)) { \
2502 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2503 if (!(ulongintPtr)) { \
2504 err = IE_NOMEM; \
2505 goto leave; \
2508 *(ulongintPtr) = number; \
2512 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2513 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2514 NULL); \
2515 if ((required) && (!string)) { \
2516 char *attribute_locale = _isds_utf82locale(attribute); \
2517 char *element_locale = \
2518 _isds_utf82locale((char *)xpath_ctx->node->name); \
2519 isds_printf_message(context, \
2520 _("Could not extract required %s attribute value from " \
2521 "%s element"), attribute_locale, element_locale); \
2522 free(element_locale); \
2523 free(attribute_locale); \
2524 err = IE_ERROR; \
2525 goto leave; \
2530 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2532 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2533 (xmlChar *) (string)); \
2534 if (!node) { \
2535 isds_printf_message(context, \
2536 _("Could not add %s child to %s element"), \
2537 element, (parent)->name); \
2538 err = IE_ERROR; \
2539 goto leave; \
2543 #define INSERT_STRING(parent, element, string) \
2544 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2546 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2548 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2549 else { INSERT_STRING(parent, element, "false"); } \
2552 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2554 if (booleanPtr) { \
2555 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2556 } else { \
2557 INSERT_STRING(parent, element, NULL); \
2561 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2562 if ((longintPtr)) { \
2563 /* FIXME: locale sensitive */ \
2564 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2565 err = IE_NOMEM; \
2566 goto leave; \
2568 INSERT_STRING(parent, element, buffer) \
2569 free(buffer); (buffer) = NULL; \
2570 } else { INSERT_STRING(parent, element, NULL) } \
2573 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2574 if ((ulongintPtr)) { \
2575 /* FIXME: locale sensitive */ \
2576 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2577 err = IE_NOMEM; \
2578 goto leave; \
2580 INSERT_STRING(parent, element, buffer) \
2581 free(buffer); (buffer) = NULL; \
2582 } else { INSERT_STRING(parent, element, NULL) } \
2585 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2587 /* FIXME: locale sensitive */ \
2588 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2589 err = IE_NOMEM; \
2590 goto leave; \
2592 INSERT_STRING(parent, element, buffer) \
2593 free(buffer); (buffer) = NULL; \
2596 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2597 * new attribute. */
2598 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2600 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2601 (xmlChar *) (string)); \
2602 if (!attribute_node) { \
2603 isds_printf_message(context, _("Could not add %s " \
2604 "attribute to %s element"), \
2605 (attribute), (parent)->name); \
2606 err = IE_ERROR; \
2607 goto leave; \
2611 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2612 if (string) { \
2613 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2614 if (length > (maximum)) { \
2615 isds_printf_message(context, \
2616 ngettext("%s has more than %d characters", \
2617 "%s has more than %d characters", (maximum)), \
2618 (name), (maximum)); \
2619 err = IE_2BIG; \
2620 goto leave; \
2622 if (length < (minimum)) { \
2623 isds_printf_message(context, \
2624 ngettext("%s has less than %d characters", \
2625 "%s has less than %d characters", (minimum)), \
2626 (name), (minimum)); \
2627 err = IE_2SMALL; \
2628 goto leave; \
2633 #define INSERT_ELEMENT(child, parent, element) \
2635 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2636 if (!(child)) { \
2637 isds_printf_message(context, \
2638 _("Could not add %s child to %s element"), \
2639 (element), (parent)->name); \
2640 err = IE_ERROR; \
2641 goto leave; \
2646 /* Find child element by name in given XPath context and switch context onto
2647 * it. The child must be uniq and must exist. Otherwise fails.
2648 * @context is ISDS context
2649 * @child is child element name
2650 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2651 * into it child. In error case, the @xpath_ctx keeps original value. */
2652 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
2653 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
2654 isds_error err = IE_SUCCESS;
2655 xmlXPathObjectPtr result = NULL;
2657 if (!context) return IE_INVALID_CONTEXT;
2658 if (!child || !xpath_ctx) return IE_INVAL;
2660 /* Find child */
2661 result = xmlXPathEvalExpression(child, xpath_ctx);
2662 if (!result) {
2663 err = IE_XML;
2664 goto leave;
2667 /* No match */
2668 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2669 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2670 char *child_locale = _isds_utf82locale((char*) child);
2671 isds_printf_message(context,
2672 _("%s element does not contain %s child"),
2673 parent_locale, child_locale);
2674 free(child_locale);
2675 free(parent_locale);
2676 err = IE_NOEXIST;
2677 goto leave;
2680 /* More matches */
2681 if (result->nodesetval->nodeNr > 1) {
2682 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2683 char *child_locale = _isds_utf82locale((char*) child);
2684 isds_printf_message(context,
2685 _("%s element contains multiple %s children"),
2686 parent_locale, child_locale);
2687 free(child_locale);
2688 free(parent_locale);
2689 err = IE_NOTUNIQ;
2690 goto leave;
2693 /* Switch context */
2694 xpath_ctx->node = result->nodesetval->nodeTab[0];
2696 leave:
2697 xmlXPathFreeObject(result);
2698 return err;
2703 #if HAVE_LIBCURL
2704 /* Find and convert XSD:gPersonName group in current node into structure
2705 * @context is ISDS context
2706 * @personName is automatically reallocated person name structure. If no member
2707 * value is found, will be freed.
2708 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2709 * elements
2710 * In case of error @personName will be freed. */
2711 static isds_error extract_gPersonName(struct isds_ctx *context,
2712 struct isds_PersonName **personName, xmlXPathContextPtr xpath_ctx) {
2713 isds_error err = IE_SUCCESS;
2714 xmlXPathObjectPtr result = NULL;
2716 if (!context) return IE_INVALID_CONTEXT;
2717 if (!personName) return IE_INVAL;
2718 isds_PersonName_free(personName);
2719 if (!xpath_ctx) return IE_INVAL;
2722 *personName = calloc(1, sizeof(**personName));
2723 if (!*personName) {
2724 err = IE_NOMEM;
2725 goto leave;
2728 EXTRACT_STRING("isds:pnFirstName", (*personName)->pnFirstName);
2729 EXTRACT_STRING("isds:pnMiddleName", (*personName)->pnMiddleName);
2730 EXTRACT_STRING("isds:pnLastName", (*personName)->pnLastName);
2731 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName)->pnLastNameAtBirth);
2733 if (!(*personName)->pnFirstName && !(*personName)->pnMiddleName &&
2734 !(*personName)->pnLastName && !(*personName)->pnLastNameAtBirth)
2735 isds_PersonName_free(personName);
2737 leave:
2738 if (err) isds_PersonName_free(personName);
2739 xmlXPathFreeObject(result);
2740 return err;
2744 /* Find and convert XSD:gAddress group in current node into structure
2745 * @context is ISDS context
2746 * @address is automatically reallocated address structure. If no member
2747 * value is found, will be freed.
2748 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2749 * elements
2750 * In case of error @address will be freed. */
2751 static isds_error extract_gAddress(struct isds_ctx *context,
2752 struct isds_Address **address, xmlXPathContextPtr xpath_ctx) {
2753 isds_error err = IE_SUCCESS;
2754 xmlXPathObjectPtr result = NULL;
2756 if (!context) return IE_INVALID_CONTEXT;
2757 if (!address) return IE_INVAL;
2758 isds_Address_free(address);
2759 if (!xpath_ctx) return IE_INVAL;
2762 *address = calloc(1, sizeof(**address));
2763 if (!*address) {
2764 err = IE_NOMEM;
2765 goto leave;
2768 EXTRACT_STRING("isds:adCity", (*address)->adCity);
2769 EXTRACT_STRING("isds:adStreet", (*address)->adStreet);
2770 EXTRACT_STRING("isds:adNumberInStreet", (*address)->adNumberInStreet);
2771 EXTRACT_STRING("isds:adNumberInMunicipality",
2772 (*address)->adNumberInMunicipality);
2773 EXTRACT_STRING("isds:adZipCode", (*address)->adZipCode);
2774 EXTRACT_STRING("isds:adState", (*address)->adState);
2776 if (!(*address)->adCity && !(*address)->adStreet &&
2777 !(*address)->adNumberInStreet &&
2778 !(*address)->adNumberInMunicipality &&
2779 !(*address)->adZipCode && !(*address)->adState)
2780 isds_Address_free(address);
2782 leave:
2783 if (err) isds_Address_free(address);
2784 xmlXPathFreeObject(result);
2785 return err;
2789 /* Find and convert isds:biDate element in current node into structure
2790 * @context is ISDS context
2791 * @biDate is automatically reallocated birth date structure. If no member
2792 * value is found, will be freed.
2793 * @xpath_ctx is XPath context with current node as parent for isds:biDate
2794 * element
2795 * In case of error @biDate will be freed. */
2796 static isds_error extract_BiDate(struct isds_ctx *context,
2797 struct tm **biDate, xmlXPathContextPtr xpath_ctx) {
2798 isds_error err = IE_SUCCESS;
2799 xmlXPathObjectPtr result = NULL;
2800 char *string = NULL;
2802 if (!context) return IE_INVALID_CONTEXT;
2803 if (!biDate) return IE_INVAL;
2804 zfree(*biDate);
2805 if (!xpath_ctx) return IE_INVAL;
2807 EXTRACT_STRING("isds:biDate", string);
2808 if (string) {
2809 *biDate = calloc(1, sizeof(**biDate));
2810 if (!*biDate) {
2811 err = IE_NOMEM;
2812 goto leave;
2814 err = datestring2tm((xmlChar *)string, *biDate);
2815 if (err) {
2816 if (err == IE_NOTSUP) {
2817 err = IE_ISDS;
2818 char *string_locale = _isds_utf82locale(string);
2819 isds_printf_message(context,
2820 _("Invalid isds:biDate value: %s"), string_locale);
2821 free(string_locale);
2823 goto leave;
2827 leave:
2828 if (err) zfree(*biDate);
2829 free(string);
2830 xmlXPathFreeObject(result);
2831 return err;
2835 /* Convert isds:dBOwnerInfo XML tree into structure
2836 * @context is ISDS context
2837 * @db_owner_info is automatically reallocated box owner info structure
2838 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
2839 * In case of error @db_owner_info will be freed. */
2840 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
2841 struct isds_DbOwnerInfo **db_owner_info,
2842 xmlXPathContextPtr xpath_ctx) {
2843 isds_error err = IE_SUCCESS;
2844 xmlXPathObjectPtr result = NULL;
2845 char *string = NULL;
2847 if (!context) return IE_INVALID_CONTEXT;
2848 if (!db_owner_info) return IE_INVAL;
2849 isds_DbOwnerInfo_free(db_owner_info);
2850 if (!xpath_ctx) return IE_INVAL;
2853 *db_owner_info = calloc(1, sizeof(**db_owner_info));
2854 if (!*db_owner_info) {
2855 err = IE_NOMEM;
2856 goto leave;
2859 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
2861 EXTRACT_STRING("isds:dbType", string);
2862 if (string) {
2863 (*db_owner_info)->dbType =
2864 calloc(1, sizeof(*((*db_owner_info)->dbType)));
2865 if (!(*db_owner_info)->dbType) {
2866 err = IE_NOMEM;
2867 goto leave;
2869 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
2870 if (err) {
2871 zfree((*db_owner_info)->dbType);
2872 if (err == IE_ENUM) {
2873 err = IE_ISDS;
2874 char *string_locale = _isds_utf82locale(string);
2875 isds_printf_message(context, _("Unknown isds:dbType: %s"),
2876 string_locale);
2877 free(string_locale);
2879 goto leave;
2881 zfree(string);
2884 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
2886 err = extract_gPersonName(context, &(*db_owner_info)->personName,
2887 xpath_ctx);
2888 if (err) goto leave;
2890 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
2892 (*db_owner_info)->birthInfo =
2893 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
2894 if (!(*db_owner_info)->birthInfo) {
2895 err = IE_NOMEM;
2896 goto leave;
2898 err = extract_BiDate(context, &(*db_owner_info)->birthInfo->biDate,
2899 xpath_ctx);
2900 if (err) goto leave;
2901 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
2902 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
2903 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
2904 if (!(*db_owner_info)->birthInfo->biDate &&
2905 !(*db_owner_info)->birthInfo->biCity &&
2906 !(*db_owner_info)->birthInfo->biCounty &&
2907 !(*db_owner_info)->birthInfo->biState)
2908 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
2910 err = extract_gAddress(context, &(*db_owner_info)->address, xpath_ctx);
2911 if (err) goto leave;
2913 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
2914 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
2915 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
2916 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
2917 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
2919 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
2921 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
2922 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
2923 (*db_owner_info)->dbOpenAddressing);
2925 leave:
2926 if (err) isds_DbOwnerInfo_free(db_owner_info);
2927 free(string);
2928 xmlXPathFreeObject(result);
2929 return err;
2933 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
2934 * @context is session context
2935 * @owner is libisds structure with box description
2936 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
2937 static isds_error insert_DbOwnerInfo(struct isds_ctx *context,
2938 const struct isds_DbOwnerInfo *owner, xmlNodePtr db_owner_info) {
2940 isds_error err = IE_SUCCESS;
2941 xmlNodePtr node;
2942 xmlChar *string = NULL;
2944 if (!context) return IE_INVALID_CONTEXT;
2945 if (!owner || !db_owner_info) return IE_INVAL;
2948 /* Build XSD:tDbOwnerInfo */
2949 CHECK_FOR_STRING_LENGTH(owner->dbID, 0, 7, "dbID")
2950 INSERT_STRING(db_owner_info, "dbID", owner->dbID);
2952 /* dbType */
2953 if (owner->dbType) {
2954 const xmlChar *type_string = isds_DbType2string(*(owner->dbType));
2955 if (!type_string) {
2956 isds_printf_message(context, _("Invalid dbType value: %d"),
2957 *(owner->dbType));
2958 err = IE_ENUM;
2959 goto leave;
2961 INSERT_STRING(db_owner_info, "dbType", type_string);
2963 INSERT_STRING(db_owner_info, "ic", owner->ic);
2964 if (owner->personName) {
2965 INSERT_STRING(db_owner_info, "pnFirstName",
2966 owner->personName->pnFirstName);
2967 INSERT_STRING(db_owner_info, "pnMiddleName",
2968 owner->personName->pnMiddleName);
2969 INSERT_STRING(db_owner_info, "pnLastName",
2970 owner->personName->pnLastName);
2971 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
2972 owner->personName->pnLastNameAtBirth);
2974 INSERT_STRING(db_owner_info, "firmName", owner->firmName);
2975 if (owner->birthInfo) {
2976 if (owner->birthInfo->biDate) {
2977 if (!tm2datestring(owner->birthInfo->biDate, &string))
2978 INSERT_STRING(db_owner_info, "biDate", string);
2979 free(string); string = NULL;
2981 INSERT_STRING(db_owner_info, "biCity", owner->birthInfo->biCity);
2982 INSERT_STRING(db_owner_info, "biCounty", owner->birthInfo->biCounty);
2983 INSERT_STRING(db_owner_info, "biState", owner->birthInfo->biState);
2985 if (owner->address) {
2986 INSERT_STRING(db_owner_info, "adCity", owner->address->adCity);
2987 INSERT_STRING(db_owner_info, "adStreet", owner->address->adStreet);
2988 INSERT_STRING(db_owner_info, "adNumberInStreet",
2989 owner->address->adNumberInStreet);
2990 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
2991 owner->address->adNumberInMunicipality);
2992 INSERT_STRING(db_owner_info, "adZipCode", owner->address->adZipCode);
2993 INSERT_STRING(db_owner_info, "adState", owner->address->adState);
2995 INSERT_STRING(db_owner_info, "nationality", owner->nationality);
2996 INSERT_STRING(db_owner_info, "email", owner->email);
2997 INSERT_STRING(db_owner_info, "telNumber", owner->telNumber);
2999 CHECK_FOR_STRING_LENGTH(owner->identifier, 0, 20, "identifier")
3000 INSERT_STRING(db_owner_info, "identifier", owner->identifier);
3002 CHECK_FOR_STRING_LENGTH(owner->registryCode, 0, 5, "registryCode")
3003 INSERT_STRING(db_owner_info, "registryCode", owner->registryCode);
3005 INSERT_LONGINT(db_owner_info, "dbState", owner->dbState, string);
3007 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", owner->dbEffectiveOVM);
3008 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
3009 owner->dbOpenAddressing);
3011 leave:
3012 free(string);
3013 return err;
3017 /* Convert XSD:tDbUserInfo XML tree into structure
3018 * @context is ISDS context
3019 * @db_user_info is automatically reallocated user info structure
3020 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3021 * In case of error @db_user_info will be freed. */
3022 static isds_error extract_DbUserInfo(struct isds_ctx *context,
3023 struct isds_DbUserInfo **db_user_info, xmlXPathContextPtr xpath_ctx) {
3024 isds_error err = IE_SUCCESS;
3025 xmlXPathObjectPtr result = NULL;
3026 char *string = NULL;
3028 if (!context) return IE_INVALID_CONTEXT;
3029 if (!db_user_info) return IE_INVAL;
3030 isds_DbUserInfo_free(db_user_info);
3031 if (!xpath_ctx) return IE_INVAL;
3034 *db_user_info = calloc(1, sizeof(**db_user_info));
3035 if (!*db_user_info) {
3036 err = IE_NOMEM;
3037 goto leave;
3040 EXTRACT_STRING("isds:userID", (*db_user_info)->userID);
3042 EXTRACT_STRING("isds:userType", string);
3043 if (string) {
3044 (*db_user_info)->userType =
3045 calloc(1, sizeof(*((*db_user_info)->userType)));
3046 if (!(*db_user_info)->userType) {
3047 err = IE_NOMEM;
3048 goto leave;
3050 err = string2isds_UserType((xmlChar *)string,
3051 (*db_user_info)->userType);
3052 if (err) {
3053 zfree((*db_user_info)->userType);
3054 if (err == IE_ENUM) {
3055 err = IE_ISDS;
3056 char *string_locale = _isds_utf82locale(string);
3057 isds_printf_message(context,
3058 _("Unknown isds:userType value: %s"), string_locale);
3059 free(string_locale);
3061 goto leave;
3063 zfree(string);
3066 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info)->userPrivils, 0);
3068 (*db_user_info)->personName =
3069 calloc(1, sizeof(*((*db_user_info)->personName)));
3070 if (!(*db_user_info)->personName) {
3071 err = IE_NOMEM;
3072 goto leave;
3075 err = extract_gPersonName(context, &(*db_user_info)->personName,
3076 xpath_ctx);
3077 if (err) goto leave;
3079 err = extract_gAddress(context, &(*db_user_info)->address, xpath_ctx);
3080 if (err) goto leave;
3082 err = extract_BiDate(context, &(*db_user_info)->biDate, xpath_ctx);
3083 if (err) goto leave;
3085 EXTRACT_STRING("isds:ic", (*db_user_info)->ic);
3086 EXTRACT_STRING("isds:firmName", (*db_user_info)->firmName);
3088 EXTRACT_STRING("isds:caStreet", (*db_user_info)->caStreet);
3089 EXTRACT_STRING("isds:caCity", (*db_user_info)->caCity);
3090 EXTRACT_STRING("isds:caZipCode", (*db_user_info)->caZipCode);
3092 /* ???: Default value is "CZ" according specification. Should we provide
3093 * it? */
3094 EXTRACT_STRING("isds:caState", (*db_user_info)->caState);
3096 leave:
3097 if (err) isds_DbUserInfo_free(db_user_info);
3098 free(string);
3099 xmlXPathFreeObject(result);
3100 return err;
3104 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3105 * @context is session context
3106 * @user is libisds structure with user description
3107 * @db_user_info is XML element of XSD:tDbUserInfo */
3108 static isds_error insert_DbUserInfo(struct isds_ctx *context,
3109 const struct isds_DbUserInfo *user, xmlNodePtr db_user_info) {
3111 isds_error err = IE_SUCCESS;
3112 xmlNodePtr node;
3113 xmlChar *string = NULL;
3115 if (!context) return IE_INVALID_CONTEXT;
3116 if (!user || !db_user_info) return IE_INVAL;
3118 /* Build XSD:tDbUserInfo */
3119 if (user->personName) {
3120 INSERT_STRING(db_user_info, "pnFirstName",
3121 user->personName->pnFirstName);
3122 INSERT_STRING(db_user_info, "pnMiddleName",
3123 user->personName->pnMiddleName);
3124 INSERT_STRING(db_user_info, "pnLastName",
3125 user->personName->pnLastName);
3126 INSERT_STRING(db_user_info, "pnLastNameAtBirth",
3127 user->personName->pnLastNameAtBirth);
3129 if (user->address) {
3130 INSERT_STRING(db_user_info, "adCity", user->address->adCity);
3131 INSERT_STRING(db_user_info, "adStreet", user->address->adStreet);
3132 INSERT_STRING(db_user_info, "adNumberInStreet",
3133 user->address->adNumberInStreet);
3134 INSERT_STRING(db_user_info, "adNumberInMunicipality",
3135 user->address->adNumberInMunicipality);
3136 INSERT_STRING(db_user_info, "adZipCode", user->address->adZipCode);
3137 INSERT_STRING(db_user_info, "adState", user->address->adState);
3139 if (user->biDate) {
3140 if (!tm2datestring(user->biDate, &string))
3141 INSERT_STRING(db_user_info, "biDate", string);
3142 zfree(string);
3144 CHECK_FOR_STRING_LENGTH(user->userID, 6, 12, "userID");
3145 INSERT_STRING(db_user_info, "userID", user->userID);
3147 /* userType */
3148 if (user->userType) {
3149 const xmlChar *type_string = isds_UserType2string(*(user->userType));
3150 if (!type_string) {
3151 isds_printf_message(context, _("Invalid userType value: %d"),
3152 *(user->userType));
3153 err = IE_ENUM;
3154 goto leave;
3156 INSERT_STRING(db_user_info, "userType", type_string);
3159 INSERT_LONGINT(db_user_info, "userPrivils", user->userPrivils, string);
3160 CHECK_FOR_STRING_LENGTH(user->ic, 0, 8, "ic")
3161 INSERT_STRING(db_user_info, "ic", user->ic);
3162 CHECK_FOR_STRING_LENGTH(user->firmName, 0, 100, "firmName")
3163 INSERT_STRING(db_user_info, "firmName", user->firmName);
3164 INSERT_STRING(db_user_info, "caStreet", user->caStreet);
3165 INSERT_STRING(db_user_info, "caCity", user->caCity);
3166 INSERT_STRING(db_user_info, "caZipCode", user->caZipCode);
3167 INSERT_STRING(db_user_info, "caState", user->caState);
3169 leave:
3170 free(string);
3171 return err;
3175 /* Convert XSD:tPDZRec XML tree into structure
3176 * @context is ISDS context
3177 * @permission is automatically reallocated commercial permission structure
3178 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3179 * In case of error @permission will be freed. */
3180 static isds_error extract_DbPDZRecord(struct isds_ctx *context,
3181 struct isds_commercial_permission **permission,
3182 xmlXPathContextPtr xpath_ctx) {
3183 isds_error err = IE_SUCCESS;
3184 xmlXPathObjectPtr result = NULL;
3185 char *string = NULL;
3187 if (!context) return IE_INVALID_CONTEXT;
3188 if (!permission) return IE_INVAL;
3189 isds_commercial_permission_free(permission);
3190 if (!xpath_ctx) return IE_INVAL;
3193 *permission = calloc(1, sizeof(**permission));
3194 if (!*permission) {
3195 err = IE_NOMEM;
3196 goto leave;
3199 EXTRACT_STRING("isds:PDZType", string);
3200 if (string) {
3201 err = string2isds_payment_type((xmlChar *)string,
3202 &(*permission)->type);
3203 if (err) {
3204 if (err == IE_ENUM) {
3205 err = IE_ISDS;
3206 char *string_locale = _isds_utf82locale(string);
3207 isds_printf_message(context,
3208 _("Unknown isds:PDZType value: %s"), string_locale);
3209 free(string_locale);
3211 goto leave;
3213 zfree(string);
3216 EXTRACT_STRING("isds:PDZRecip", (*permission)->recipient);
3217 EXTRACT_STRING("isds:PDZPayer", (*permission)->payer);
3219 EXTRACT_STRING("isds:PDZExpire", string);
3220 if (string) {
3221 err = timestring2timeval((xmlChar *) string,
3222 &((*permission)->expiration));
3223 if (err) {
3224 char *string_locale = _isds_utf82locale(string);
3225 if (err == IE_DATE) err = IE_ISDS;
3226 isds_printf_message(context,
3227 _("Could not convert PDZExpire as ISO time: %s"),
3228 string_locale);
3229 free(string_locale);
3230 goto leave;
3232 zfree(string);
3235 EXTRACT_ULONGINT("isds:PDZCnt", (*permission)->count, 0);
3236 EXTRACT_STRING("isds:ODZIdent", (*permission)->reply_identifier);
3238 leave:
3239 if (err) isds_commercial_permission_free(permission);
3240 free(string);
3241 xmlXPathFreeObject(result);
3242 return err;
3246 #endif /* HAVE_LIBCURL */
3249 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3250 * isds_envelope structure. The envelope is automatically allocated but not
3251 * reallocated. The date are just appended into envelope structure.
3252 * @context is ISDS context
3253 * @envelope is automatically allocated message envelope structure
3254 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3255 * In case of error @envelope will be freed. */
3256 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
3257 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3258 isds_error err = IE_SUCCESS;
3259 xmlXPathObjectPtr result = NULL;
3261 if (!context) return IE_INVALID_CONTEXT;
3262 if (!envelope) return IE_INVAL;
3263 if (!xpath_ctx) return IE_INVAL;
3266 if (!*envelope) {
3267 /* Allocate envelope */
3268 *envelope = calloc(1, sizeof(**envelope));
3269 if (!*envelope) {
3270 err = IE_NOMEM;
3271 goto leave;
3273 } else {
3274 /* Else free former data */
3275 zfree((*envelope)->dmSenderOrgUnit);
3276 zfree((*envelope)->dmSenderOrgUnitNum);
3277 zfree((*envelope)->dbIDRecipient);
3278 zfree((*envelope)->dmRecipientOrgUnit);
3279 zfree((*envelope)->dmSenderOrgUnitNum);
3280 zfree((*envelope)->dmToHands);
3281 zfree((*envelope)->dmAnnotation);
3282 zfree((*envelope)->dmRecipientRefNumber);
3283 zfree((*envelope)->dmSenderRefNumber);
3284 zfree((*envelope)->dmRecipientIdent);
3285 zfree((*envelope)->dmSenderIdent);
3286 zfree((*envelope)->dmLegalTitleLaw);
3287 zfree((*envelope)->dmLegalTitleYear);
3288 zfree((*envelope)->dmLegalTitleSect);
3289 zfree((*envelope)->dmLegalTitlePar);
3290 zfree((*envelope)->dmLegalTitlePoint);
3291 zfree((*envelope)->dmPersonalDelivery);
3292 zfree((*envelope)->dmAllowSubstDelivery);
3295 /* Extract envelope elements added by sender or ISDS
3296 * (XSD: gMessageEnvelopeSub type) */
3297 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
3298 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3299 (*envelope)->dmSenderOrgUnitNum, 0);
3300 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
3301 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
3302 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3303 (*envelope)->dmSenderOrgUnitNum, 0);
3304 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
3305 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
3306 EXTRACT_STRING("isds:dmRecipientRefNumber",
3307 (*envelope)->dmRecipientRefNumber);
3308 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
3309 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
3310 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
3312 /* Extract envelope elements regarding law reference */
3313 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
3314 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
3315 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
3316 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
3317 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
3319 /* Extract envelope other elements */
3320 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
3321 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3322 (*envelope)->dmAllowSubstDelivery);
3324 leave:
3325 if (err) isds_envelope_free(envelope);
3326 xmlXPathFreeObject(result);
3327 return err;
3332 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3333 * isds_envelope structure. The envelope is automatically allocated but not
3334 * reallocated. The date are just appended into envelope structure.
3335 * @context is ISDS context
3336 * @envelope is automatically allocated message envelope structure
3337 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3338 * In case of error @envelope will be freed. */
3339 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
3340 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3341 isds_error err = IE_SUCCESS;
3342 xmlXPathObjectPtr result = NULL;
3344 if (!context) return IE_INVALID_CONTEXT;
3345 if (!envelope) return IE_INVAL;
3346 if (!xpath_ctx) return IE_INVAL;
3349 if (!*envelope) {
3350 /* Allocate envelope */
3351 *envelope = calloc(1, sizeof(**envelope));
3352 if (!*envelope) {
3353 err = IE_NOMEM;
3354 goto leave;
3356 } else {
3357 /* Else free former data */
3358 zfree((*envelope)->dmID);
3359 zfree((*envelope)->dbIDSender);
3360 zfree((*envelope)->dmSender);
3361 zfree((*envelope)->dmSenderAddress);
3362 zfree((*envelope)->dmSenderType);
3363 zfree((*envelope)->dmRecipient);
3364 zfree((*envelope)->dmRecipientAddress);
3365 zfree((*envelope)->dmAmbiguousRecipient);
3368 /* Extract envelope elements added by ISDS
3369 * (XSD: gMessageEnvelope type) */
3370 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
3371 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
3372 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
3373 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
3374 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3375 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
3376 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
3377 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
3378 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3379 (*envelope)->dmAmbiguousRecipient);
3381 /* Extract envelope elements added by sender and ISDS
3382 * (XSD: gMessageEnvelope type) */
3383 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
3384 if (err) goto leave;
3386 leave:
3387 if (err) isds_envelope_free(envelope);
3388 xmlXPathFreeObject(result);
3389 return err;
3393 /* Convert other envelope elements from XML tree into isds_envelope structure:
3394 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3395 * The envelope is automatically allocated but not reallocated.
3396 * The data are just appended into envelope structure.
3397 * @context is ISDS context
3398 * @envelope is automatically allocated message envelope structure
3399 * @xpath_ctx is XPath context with current node as parent desired elements
3400 * In case of error @envelope will be freed. */
3401 static isds_error append_status_size_times(struct isds_ctx *context,
3402 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3403 isds_error err = IE_SUCCESS;
3404 xmlXPathObjectPtr result = NULL;
3405 char *string = NULL;
3406 unsigned long int *unumber = NULL;
3408 if (!context) return IE_INVALID_CONTEXT;
3409 if (!envelope) return IE_INVAL;
3410 if (!xpath_ctx) return IE_INVAL;
3413 if (!*envelope) {
3414 /* Allocate new */
3415 *envelope = calloc(1, sizeof(**envelope));
3416 if (!*envelope) {
3417 err = IE_NOMEM;
3418 goto leave;
3420 } else {
3421 /* Free old data */
3422 zfree((*envelope)->dmMessageStatus);
3423 zfree((*envelope)->dmAttachmentSize);
3424 zfree((*envelope)->dmDeliveryTime);
3425 zfree((*envelope)->dmAcceptanceTime);
3429 /* dmMessageStatus element is mandatory */
3430 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
3431 if (!unumber) {
3432 isds_log_message(context,
3433 _("Missing mandatory sisds:dmMessageStatus integer"));
3434 err = IE_ISDS;
3435 goto leave;
3437 err = uint2isds_message_status(context, unumber,
3438 &((*envelope)->dmMessageStatus));
3439 if (err) {
3440 if (err == IE_ENUM) err = IE_ISDS;
3441 goto leave;
3443 free(unumber); unumber = NULL;
3445 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
3448 EXTRACT_STRING("sisds:dmDeliveryTime", string);
3449 if (string) {
3450 err = timestring2timeval((xmlChar *) string,
3451 &((*envelope)->dmDeliveryTime));
3452 if (err) {
3453 char *string_locale = _isds_utf82locale(string);
3454 if (err == IE_DATE) err = IE_ISDS;
3455 isds_printf_message(context,
3456 _("Could not convert dmDeliveryTime as ISO time: %s"),
3457 string_locale);
3458 free(string_locale);
3459 goto leave;
3461 zfree(string);
3464 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
3465 if (string) {
3466 err = timestring2timeval((xmlChar *) string,
3467 &((*envelope)->dmAcceptanceTime));
3468 if (err) {
3469 char *string_locale = _isds_utf82locale(string);
3470 if (err == IE_DATE) err = IE_ISDS;
3471 isds_printf_message(context,
3472 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3473 string_locale);
3474 free(string_locale);
3475 goto leave;
3477 zfree(string);
3480 leave:
3481 if (err) isds_envelope_free(envelope);
3482 free(unumber);
3483 free(string);
3484 xmlXPathFreeObject(result);
3485 return err;
3489 /* Convert message type attribute of current element into isds_envelope
3490 * structure.
3491 * TODO: This function can be incorporated into append_status_size_times() as
3492 * they are called always together.
3493 * The envelope is automatically allocated but not reallocated.
3494 * The data are just appended into envelope structure.
3495 * @context is ISDS context
3496 * @envelope is automatically allocated message envelope structure
3497 * @xpath_ctx is XPath context with current node as parent of attribute
3498 * carrying message type
3499 * In case of error @envelope will be freed. */
3500 static isds_error append_message_type(struct isds_ctx *context,
3501 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3502 isds_error err = IE_SUCCESS;
3504 if (!context) return IE_INVALID_CONTEXT;
3505 if (!envelope) return IE_INVAL;
3506 if (!xpath_ctx) return IE_INVAL;
3509 if (!*envelope) {
3510 /* Allocate new */
3511 *envelope = calloc(1, sizeof(**envelope));
3512 if (!*envelope) {
3513 err = IE_NOMEM;
3514 goto leave;
3516 } else {
3517 /* Free old data */
3518 zfree((*envelope)->dmType);
3522 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope)->dmType, 0);
3524 if (!(*envelope)->dmType) {
3525 /* Use default value */
3526 (*envelope)->dmType = strdup("V");
3527 if (!(*envelope)->dmType) {
3528 err = IE_NOMEM;
3529 goto leave;
3531 } else if (1 != xmlUTF8Strlen((xmlChar *) (*envelope)->dmType)) {
3532 char *type_locale = _isds_utf82locale((*envelope)->dmType);
3533 isds_printf_message(context,
3534 _("Message type in dmType attribute is not 1 character long: "
3535 "%s"),
3536 type_locale);
3537 free(type_locale);
3538 err = IE_ISDS;
3539 goto leave;
3542 leave:
3543 if (err) isds_envelope_free(envelope);
3544 return err;
3548 #if HAVE_LIBCURL
3549 /* Convert dmType isds_envelope member into XML attribute and append it to
3550 * current node.
3551 * @context is ISDS context
3552 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3553 * @dm_envelope is XML element the resulting attribute will be appended to.
3554 * @return error code, in case of error context' message is filled. */
3555 static isds_error insert_message_type(struct isds_ctx *context,
3556 const char *type, xmlNodePtr dm_envelope) {
3557 isds_error err = IE_SUCCESS;
3558 xmlAttrPtr attribute_node;
3560 if (!context) return IE_INVALID_CONTEXT;
3561 if (!dm_envelope) return IE_INVAL;
3563 /* Insert optional message type */
3564 if (type) {
3565 if (1 != xmlUTF8Strlen((xmlChar *) type)) {
3566 char *type_locale = _isds_utf82locale(type);
3567 isds_printf_message(context,
3568 _("Message type in envelope is not 1 character long: %s"),
3569 type_locale);
3570 free(type_locale);
3571 err = IE_INVAL;
3572 goto leave;
3574 INSERT_STRING_ATTRIBUTE(dm_envelope, "dmType", type);
3577 leave:
3578 return err;
3580 #endif /* HAVE_LIBCURL */
3583 /* Extract message document into reallocated document structure
3584 * @context is ISDS context
3585 * @document is automatically reallocated message documents structure
3586 * @xpath_ctx is XPath context with current node as isds:dmFile
3587 * In case of error @document will be freed. */
3588 static isds_error extract_document(struct isds_ctx *context,
3589 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
3590 isds_error err = IE_SUCCESS;
3591 xmlXPathObjectPtr result = NULL;
3592 xmlNodePtr file_node = xpath_ctx->node;
3593 char *string = NULL;
3595 if (!context) return IE_INVALID_CONTEXT;
3596 if (!document) return IE_INVAL;
3597 isds_document_free(document);
3598 if (!xpath_ctx) return IE_INVAL;
3600 *document = calloc(1, sizeof(**document));
3601 if (!*document) {
3602 err = IE_NOMEM;
3603 goto leave;
3606 /* Extract document meta data */
3607 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
3608 if (context->normalize_mime_type) {
3609 char *normalized_type =
3610 isds_normalize_mime_type((*document)->dmMimeType);
3611 if (normalized_type && normalized_type != (*document)->dmMimeType) {
3612 char *new_type = strdup(normalized_type);
3613 if (!new_type) {
3614 isds_printf_message(context,
3615 _("Not enough memory to normalize document MIME type"));
3616 err = IE_NOMEM;
3617 goto leave;
3619 free((*document)->dmMimeType);
3620 (*document)->dmMimeType = new_type;
3624 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
3625 err = string2isds_FileMetaType((xmlChar*)string,
3626 &((*document)->dmFileMetaType));
3627 if (err) {
3628 char *meta_type_locale = _isds_utf82locale(string);
3629 isds_printf_message(context,
3630 _("Document has invalid dmFileMetaType attribute value: %s"),
3631 meta_type_locale);
3632 free(meta_type_locale);
3633 err = IE_ISDS;
3634 goto leave;
3636 zfree(string);
3638 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
3639 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
3640 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
3641 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
3644 /* Extract document data.
3645 * Base64 encoded blob or XML subtree must be presented. */
3647 /* Check for dmEncodedContent */
3648 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
3649 xpath_ctx);
3650 if (!result) {
3651 err = IE_XML;
3652 goto leave;
3655 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3656 /* Here we have Base64 blob */
3657 (*document)->is_xml = 0;
3659 if (result->nodesetval->nodeNr > 1) {
3660 isds_printf_message(context,
3661 _("Document has more dmEncodedContent elements"));
3662 err = IE_ISDS;
3663 goto leave;
3666 xmlXPathFreeObject(result); result = NULL;
3667 EXTRACT_STRING("isds:dmEncodedContent", string);
3669 /* Decode non-empty document */
3670 if (string && string[0] != '\0') {
3671 (*document)->data_length =
3672 _isds_b64decode(string, &((*document)->data));
3673 if ((*document)->data_length == (size_t) -1) {
3674 isds_printf_message(context,
3675 _("Error while Base64-decoding document content"));
3676 err = IE_ERROR;
3677 goto leave;
3680 } else {
3681 /* No Base64 blob, try XML document */
3682 xmlXPathFreeObject(result); result = NULL;
3683 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
3684 xpath_ctx);
3685 if (!result) {
3686 err = IE_XML;
3687 goto leave;
3690 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3691 /* Here we have XML document */
3692 (*document)->is_xml = 1;
3694 if (result->nodesetval->nodeNr > 1) {
3695 isds_printf_message(context,
3696 _("Document has more dmXMLContent elements"));
3697 err = IE_ISDS;
3698 goto leave;
3701 /* XXX: We cannot serialize the content simply because:
3702 * - XML document may point out of its scope (e.g. to message
3703 * envelope)
3704 * - isds:dmXMLContent can contain more elements, no element,
3705 * a text node only
3706 * - it's not the XML way
3707 * Thus we provide the only right solution: XML DOM. Let's
3708 * application to cope with this hot potato :) */
3709 (*document)->xml_node_list =
3710 result->nodesetval->nodeTab[0]->children;
3711 } else {
3712 /* No base64 blob, nor XML document */
3713 isds_printf_message(context,
3714 _("Document has no dmEncodedContent, nor dmXMLContent "
3715 "element"));
3716 err = IE_ISDS;
3717 goto leave;
3722 leave:
3723 if (err) isds_document_free(document);
3724 free(string);
3725 xmlXPathFreeObject(result);
3726 xpath_ctx->node = file_node;
3727 return err;
3732 /* Extract message documents into reallocated list of documents
3733 * @context is ISDS context
3734 * @documents is automatically reallocated message documents list structure
3735 * @xpath_ctx is XPath context with current node as XSD tFilesArray
3736 * In case of error @documents will be freed. */
3737 static isds_error extract_documents(struct isds_ctx *context,
3738 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
3739 isds_error err = IE_SUCCESS;
3740 xmlXPathObjectPtr result = NULL;
3741 xmlNodePtr files_node = xpath_ctx->node;
3742 struct isds_list *document, *prev_document = NULL;
3744 if (!context) return IE_INVALID_CONTEXT;
3745 if (!documents) return IE_INVAL;
3746 isds_list_free(documents);
3747 if (!xpath_ctx) return IE_INVAL;
3749 /* Find documents */
3750 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
3751 if (!result) {
3752 err = IE_XML;
3753 goto leave;
3756 /* No match */
3757 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3758 isds_printf_message(context,
3759 _("Message does not contain any document"));
3760 err = IE_ISDS;
3761 goto leave;
3765 /* Iterate over documents */
3766 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
3768 /* Allocate and append list item */
3769 document = calloc(1, sizeof(*document));
3770 if (!document) {
3771 err = IE_NOMEM;
3772 goto leave;
3774 document->destructor = (void (*)(void **))isds_document_free;
3775 if (i == 0) *documents = document;
3776 else prev_document->next = document;
3777 prev_document = document;
3779 /* Extract document */
3780 xpath_ctx->node = result->nodesetval->nodeTab[i];
3781 err = extract_document(context,
3782 (struct isds_document **) &(document->data), xpath_ctx);
3783 if (err) goto leave;
3787 leave:
3788 if (err) isds_list_free(documents);
3789 xmlXPathFreeObject(result);
3790 xpath_ctx->node = files_node;
3791 return err;
3795 #if HAVE_LIBCURL
3796 /* Convert isds:dmRecord XML tree into structure
3797 * @context is ISDS context
3798 * @envelope is automatically reallocated message envelope structure
3799 * @xpath_ctx is XPath context with current node as isds:dmRecord element
3800 * In case of error @envelope will be freed. */
3801 static isds_error extract_DmRecord(struct isds_ctx *context,
3802 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3803 isds_error err = IE_SUCCESS;
3804 xmlXPathObjectPtr result = NULL;
3806 if (!context) return IE_INVALID_CONTEXT;
3807 if (!envelope) return IE_INVAL;
3808 isds_envelope_free(envelope);
3809 if (!xpath_ctx) return IE_INVAL;
3812 *envelope = calloc(1, sizeof(**envelope));
3813 if (!*envelope) {
3814 err = IE_NOMEM;
3815 goto leave;
3819 /* Extract tRecord data */
3820 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
3822 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
3823 * dmAcceptanceTime. */
3824 err = append_status_size_times(context, envelope, xpath_ctx);
3825 if (err) goto leave;
3827 /* Extract envelope elements added by sender and ISDS
3828 * (XSD: gMessageEnvelope type) */
3829 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
3830 if (err) goto leave;
3832 /* Get message type */
3833 err = append_message_type(context, envelope, xpath_ctx);
3834 if (err) goto leave;
3837 leave:
3838 if (err) isds_envelope_free(envelope);
3839 xmlXPathFreeObject(result);
3840 return err;
3844 /* Convert XSD:tStateChangesRecord type XML tree into structure
3845 * @context is ISDS context
3846 * @changed_status is automatically reallocated message state change structure
3847 * @xpath_ctx is XPath context with current node as element of
3848 * XSD:tStateChangesRecord type
3849 * In case of error @changed_status will be freed. */
3850 static isds_error extract_StateChangesRecord(struct isds_ctx *context,
3851 struct isds_message_status_change **changed_status,
3852 xmlXPathContextPtr xpath_ctx) {
3853 isds_error err = IE_SUCCESS;
3854 xmlXPathObjectPtr result = NULL;
3855 unsigned long int *unumber = NULL;
3856 char *string = NULL;
3858 if (!context) return IE_INVALID_CONTEXT;
3859 if (!changed_status) return IE_INVAL;
3860 isds_message_status_change_free(changed_status);
3861 if (!xpath_ctx) return IE_INVAL;
3864 *changed_status = calloc(1, sizeof(**changed_status));
3865 if (!*changed_status) {
3866 err = IE_NOMEM;
3867 goto leave;
3871 /* Extract tGetStateChangesInput data */
3872 EXTRACT_STRING("isds:dmID", (*changed_status)->dmID);
3874 /* dmEventTime is mandatory */
3875 EXTRACT_STRING("isds:dmEventTime", string);
3876 if (string) {
3877 err = timestring2timeval((xmlChar *) string,
3878 &((*changed_status)->time));
3879 if (err) {
3880 char *string_locale = _isds_utf82locale(string);
3881 if (err == IE_DATE) err = IE_ISDS;
3882 isds_printf_message(context,
3883 _("Could not convert dmEventTime as ISO time: %s"),
3884 string_locale);
3885 free(string_locale);
3886 goto leave;
3888 zfree(string);
3891 /* dmMessageStatus element is mandatory */
3892 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber, 0);
3893 if (!unumber) {
3894 isds_log_message(context,
3895 _("Missing mandatory isds:dmMessageStatus integer"));
3896 err = IE_ISDS;
3897 goto leave;
3899 err = uint2isds_message_status(context, unumber,
3900 &((*changed_status)->dmMessageStatus));
3901 if (err) {
3902 if (err == IE_ENUM) err = IE_ISDS;
3903 goto leave;
3905 zfree(unumber);
3908 leave:
3909 free(unumber);
3910 free(string);
3911 if (err) isds_message_status_change_free(changed_status);
3912 xmlXPathFreeObject(result);
3913 return err;
3915 #endif /* HAVE_LIBCURL */
3918 /* Find and convert isds:dmHash XML tree into structure
3919 * @context is ISDS context
3920 * @envelope is automatically reallocated message hash structure
3921 * @xpath_ctx is XPath context with current node containing isds:dmHash child
3922 * In case of error @hash will be freed. */
3923 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
3924 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
3925 isds_error err = IE_SUCCESS;
3926 xmlNodePtr old_ctx_node;
3927 xmlXPathObjectPtr result = NULL;
3928 char *string = NULL;
3930 if (!context) return IE_INVALID_CONTEXT;
3931 if (!hash) return IE_INVAL;
3932 isds_hash_free(hash);
3933 if (!xpath_ctx) return IE_INVAL;
3935 old_ctx_node = xpath_ctx->node;
3937 *hash = calloc(1, sizeof(**hash));
3938 if (!*hash) {
3939 err = IE_NOMEM;
3940 goto leave;
3943 /* Locate dmHash */
3944 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
3945 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
3946 err = IE_ISDS;
3947 goto leave;
3949 if (err) {
3950 err = IE_ERROR;
3951 goto leave;
3954 /* Get hash algorithm */
3955 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
3956 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
3957 if (err) {
3958 if (err == IE_ENUM) {
3959 char *string_locale = _isds_utf82locale(string);
3960 isds_printf_message(context, _("Unsupported hash algorithm: %s"),
3961 string_locale);
3962 free(string_locale);
3964 goto leave;
3966 zfree(string);
3968 /* Get hash value */
3969 EXTRACT_STRING(".", string);
3970 if (!string) {
3971 isds_printf_message(context,
3972 _("sisds:dmHash element is missing hash value"));
3973 err = IE_ISDS;
3974 goto leave;
3976 (*hash)->length = _isds_b64decode(string, &((*hash)->value));
3977 if ((*hash)->length == (size_t) -1) {
3978 isds_printf_message(context,
3979 _("Error while Base64-decoding hash value"));
3980 err = IE_ERROR;
3981 goto leave;
3984 leave:
3985 if (err) isds_hash_free(hash);
3986 free(string);
3987 xmlXPathFreeObject(result);
3988 xpath_ctx->node = old_ctx_node;
3989 return err;
3993 /* Find and append isds:dmQTimestamp XML tree into envelope.
3994 * Because one service is allowed to miss time-stamp content, and we think
3995 * other could too (flaw in specification), this function is deliberated and
3996 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
3997 * @context is ISDS context
3998 * @envelope is automatically allocated envelope structure
3999 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4000 * child
4001 * In case of error @envelope will be freed. */
4002 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
4003 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4004 isds_error err = IE_SUCCESS;
4005 xmlXPathObjectPtr result = NULL;
4006 char *string = NULL;
4008 if (!context) return IE_INVALID_CONTEXT;
4009 if (!envelope) return IE_INVAL;
4010 if (!xpath_ctx) {
4011 isds_envelope_free(envelope);
4012 return IE_INVAL;
4015 if (!*envelope) {
4016 *envelope = calloc(1, sizeof(**envelope));
4017 if (!*envelope) {
4018 err = IE_NOMEM;
4019 goto leave;
4021 } else {
4022 zfree((*envelope)->timestamp);
4023 (*envelope)->timestamp_length = 0;
4026 /* Get dmQTimestamp */
4027 EXTRACT_STRING("sisds:dmQTimestamp", string);
4028 if (!string) {
4029 isds_log(ILF_ISDS, ILL_INFO, _("Missing dmQTimestamp element content\n"));
4030 goto leave;
4032 (*envelope)->timestamp_length =
4033 _isds_b64decode(string, &((*envelope)->timestamp));
4034 if ((*envelope)->timestamp_length == (size_t) -1) {
4035 isds_printf_message(context,
4036 _("Error while Base64-decoding time stamp value"));
4037 err = IE_ERROR;
4038 goto leave;
4041 leave:
4042 if (err) isds_envelope_free(envelope);
4043 free(string);
4044 xmlXPathFreeObject(result);
4045 return err;
4049 /* Convert XSD tReturnedMessage XML tree into message structure.
4050 * It does not store serialized XML tree into message->raw.
4051 * It does store (pointer to) parsed XML tree into message->xml if needed.
4052 * @context is ISDS context
4053 * @include_documents Use true if documents must be extracted
4054 * (tReturnedMessage XSD type), use false if documents shall be omitted
4055 * (tReturnedMessageEnvelope).
4056 * @message is automatically reallocated message structure
4057 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4058 * type
4059 * In case of error @message will be freed. */
4060 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
4061 const _Bool include_documents, struct isds_message **message,
4062 xmlXPathContextPtr xpath_ctx) {
4063 isds_error err = IE_SUCCESS;
4064 xmlNodePtr message_node;
4066 if (!context) return IE_INVALID_CONTEXT;
4067 if (!message) return IE_INVAL;
4068 isds_message_free(message);
4069 if (!xpath_ctx) return IE_INVAL;
4072 *message = calloc(1, sizeof(**message));
4073 if (!*message) {
4074 err = IE_NOMEM;
4075 goto leave;
4078 /* Save message XPATH context node */
4079 message_node = xpath_ctx->node;
4082 /* Extract dmDM */
4083 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
4084 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
4085 if (err) { err = IE_ERROR; goto leave; }
4086 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
4087 if (err) goto leave;
4089 if (include_documents) {
4090 struct isds_list *item;
4092 /* Extract dmFiles */
4093 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
4094 xpath_ctx);
4095 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4096 err = IE_ISDS; goto leave;
4098 if (err) { err = IE_ERROR; goto leave; }
4099 err = extract_documents(context, &((*message)->documents), xpath_ctx);
4100 if (err) goto leave;
4102 /* Store xmlDoc of this message if needed */
4103 /* Only if we got a XML document in all the documents. */
4104 for (item = (*message)->documents; item; item = item->next) {
4105 if (item->data && ((struct isds_document *)item->data)->is_xml) {
4106 (*message)->xml = xpath_ctx->doc;
4107 break;
4113 /* Restore context to message */
4114 xpath_ctx->node = message_node;
4116 /* Extract dmHash */
4117 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
4118 xpath_ctx);
4119 if (err) goto leave;
4121 /* Extract dmQTimestamp, */
4122 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
4123 xpath_ctx);
4124 if (err) goto leave;
4126 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4127 * dmAcceptanceTime. */
4128 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
4129 if (err) goto leave;
4131 /* Get message type */
4132 err = append_message_type(context, &((*message)->envelope), xpath_ctx);
4133 if (err) goto leave;
4135 leave:
4136 if (err) isds_message_free(message);
4137 return err;
4141 /* Extract message event into reallocated isds_event structure
4142 * @context is ISDS context
4143 * @event is automatically reallocated message event structure
4144 * @xpath_ctx is XPath context with current node as isds:dmEvent
4145 * In case of error @event will be freed. */
4146 static isds_error extract_event(struct isds_ctx *context,
4147 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
4148 isds_error err = IE_SUCCESS;
4149 xmlXPathObjectPtr result = NULL;
4150 xmlNodePtr event_node = xpath_ctx->node;
4151 char *string = NULL;
4153 if (!context) return IE_INVALID_CONTEXT;
4154 if (!event) return IE_INVAL;
4155 isds_event_free(event);
4156 if (!xpath_ctx) return IE_INVAL;
4158 *event = calloc(1, sizeof(**event));
4159 if (!*event) {
4160 err = IE_NOMEM;
4161 goto leave;
4164 /* Extract event data.
4165 * All elements are optional according XSD. That's funny. */
4166 EXTRACT_STRING("sisds:dmEventTime", string);
4167 if (string) {
4168 err = timestring2timeval((xmlChar *) string, &((*event)->time));
4169 if (err) {
4170 char *string_locale = _isds_utf82locale(string);
4171 if (err == IE_DATE) err = IE_ISDS;
4172 isds_printf_message(context,
4173 _("Could not convert dmEventTime as ISO time: %s"),
4174 string_locale);
4175 free(string_locale);
4176 goto leave;
4178 zfree(string);
4181 /* dmEventDescr element has prefix and the rest */
4182 EXTRACT_STRING("sisds:dmEventDescr", string);
4183 if (string) {
4184 err = eventstring2event((xmlChar *) string, *event);
4185 if (err) goto leave;
4186 zfree(string);
4189 leave:
4190 if (err) isds_event_free(event);
4191 free(string);
4192 xmlXPathFreeObject(result);
4193 xpath_ctx->node = event_node;
4194 return err;
4198 /* Convert element of XSD tEventsArray type from XML tree into
4199 * isds_list of isds_event's structure. The list is automatically reallocated.
4200 * @context is ISDS context
4201 * @events is automatically reallocated list of event structures
4202 * @xpath_ctx is XPath context with current node as tEventsArray
4203 * In case of error @events will be freed. */
4204 static isds_error extract_events(struct isds_ctx *context,
4205 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
4206 isds_error err = IE_SUCCESS;
4207 xmlXPathObjectPtr result = NULL;
4208 xmlNodePtr events_node = xpath_ctx->node;
4209 struct isds_list *event, *prev_event = NULL;
4211 if (!context) return IE_INVALID_CONTEXT;
4212 if (!events) return IE_INVAL;
4213 if (!xpath_ctx) return IE_INVAL;
4215 /* Free old list */
4216 isds_list_free(events);
4218 /* Find events */
4219 result = xmlXPathEvalExpression(BAD_CAST "sisds:dmEvent", xpath_ctx);
4220 if (!result) {
4221 err = IE_XML;
4222 goto leave;
4225 /* No match */
4226 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4227 isds_printf_message(context,
4228 _("Delivery info does not contain any event"));
4229 err = IE_ISDS;
4230 goto leave;
4234 /* Iterate over events */
4235 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4237 /* Allocate and append list item */
4238 event = calloc(1, sizeof(*event));
4239 if (!event) {
4240 err = IE_NOMEM;
4241 goto leave;
4243 event->destructor = (void (*)(void **))isds_event_free;
4244 if (i == 0) *events = event;
4245 else prev_event->next = event;
4246 prev_event = event;
4248 /* Extract event */
4249 xpath_ctx->node = result->nodesetval->nodeTab[i];
4250 err = extract_event(context,
4251 (struct isds_event **) &(event->data), xpath_ctx);
4252 if (err) goto leave;
4256 leave:
4257 if (err) isds_list_free(events);
4258 xmlXPathFreeObject(result);
4259 xpath_ctx->node = events_node;
4260 return err;
4264 #if HAVE_LIBCURL
4265 /* Insert Base64 encoded data as element with text child.
4266 * @context is session context
4267 * @parent is XML node to append @element with @data as child
4268 * @ns is XML namespace of @element, use NULL to inherit from @parent
4269 * @element is UTF-8 encoded name of new element
4270 * @data is bit stream to encode into @element
4271 * @length is size of @data in bytes
4272 * @return standard error code and fill long error message if needed */
4273 static isds_error insert_base64_encoded_string(struct isds_ctx *context,
4274 xmlNodePtr parent, const xmlNsPtr ns, const char *element,
4275 const void *data, size_t length) {
4276 isds_error err = IE_SUCCESS;
4277 xmlNodePtr node;
4279 if (!context) return IE_INVALID_CONTEXT;
4280 if (!data && length > 0) return IE_INVAL;
4281 if (!parent || !element) return IE_INVAL;
4283 xmlChar *base64data = NULL;
4284 base64data = (xmlChar *) _isds_b64encode(data, length);
4285 if (!base64data) {
4286 isds_printf_message(context,
4287 ngettext("Not enough memory to encode %zd byte into Base64",
4288 "Not enough memory to encode %zd bytes into Base64",
4289 length),
4290 length);
4291 err = IE_NOMEM;
4292 goto leave;
4294 INSERT_STRING_WITH_NS(parent, ns, element, base64data);
4296 leave:
4297 free(base64data);
4298 return err;
4302 /* Convert isds_document structure into XML tree and append to dmFiles node.
4303 * @context is session context
4304 * @document is ISDS document
4305 * @dm_files is XML element the resulting tree will be appended to as a child.
4306 * @return error code, in case of error context' message is filled. */
4307 static isds_error insert_document(struct isds_ctx *context,
4308 struct isds_document *document, xmlNodePtr dm_files) {
4309 isds_error err = IE_SUCCESS;
4310 xmlNodePtr new_file = NULL, file = NULL, node;
4311 xmlAttrPtr attribute_node;
4313 if (!context) return IE_INVALID_CONTEXT;
4314 if (!document || !dm_files) return IE_INVAL;
4316 /* Allocate new dmFile */
4317 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
4318 if (!new_file) {
4319 isds_printf_message(context, _("Could not allocate main dmFile"));
4320 err = IE_ERROR;
4321 goto leave;
4323 /* Append the new dmFile.
4324 * XXX: Main document must go first */
4325 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
4326 file = xmlAddPrevSibling(dm_files->children, new_file);
4327 else
4328 file = xmlAddChild(dm_files, new_file);
4330 if (!file) {
4331 xmlFreeNode(new_file); new_file = NULL;
4332 isds_printf_message(context, _("Could not add dmFile child to "
4333 "%s element"), dm_files->name);
4334 err = IE_ERROR;
4335 goto leave;
4338 /* @dmMimeType is required */
4339 if (!document->dmMimeType) {
4340 isds_log_message(context,
4341 _("Document is missing mandatory MIME type definition"));
4342 err = IE_INVAL;
4343 goto leave;
4345 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
4347 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
4348 if (!string) {
4349 isds_printf_message(context,
4350 _("Document has unknown dmFileMetaType: %ld"),
4351 document->dmFileMetaType);
4352 err = IE_ENUM;
4353 goto leave;
4355 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
4357 if (document->dmFileGuid) {
4358 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
4360 if (document->dmUpFileGuid) {
4361 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
4364 /* @dmFileDescr is required */
4365 if (!document->dmFileDescr) {
4366 isds_log_message(context,
4367 _("Document is missing mandatory description (title)"));
4368 err = IE_INVAL;
4369 goto leave;
4371 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
4373 if (document->dmFormat) {
4374 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
4378 /* Insert content (body) of the document. */
4379 if (document->is_xml) {
4380 /* XML document requested */
4382 /* Allocate new dmXMLContent */
4383 xmlNodePtr xmlcontent = xmlNewNode(file->ns, BAD_CAST "dmXMLContent");
4384 if (!xmlcontent) {
4385 isds_printf_message(context,
4386 _("Could not allocate dmXMLContent element"));
4387 err = IE_ERROR;
4388 goto leave;
4390 /* Append it */
4391 node = xmlAddChild(file, xmlcontent);
4392 if (!node) {
4393 xmlFreeNode(xmlcontent); xmlcontent = NULL;
4394 isds_printf_message(context,
4395 _("Could not add dmXMLContent child to %s element"),
4396 file->name);
4397 err = IE_ERROR;
4398 goto leave;
4401 /* Copy non-empty node list */
4402 if (document->xml_node_list) {
4403 xmlNodePtr content = xmlDocCopyNodeList(node->doc,
4404 document->xml_node_list);
4405 if (!content) {
4406 isds_printf_message(context,
4407 _("Not enough memory to copy XML document"));
4408 err = IE_NOMEM;
4409 goto leave;
4412 if (!xmlAddChildList(node, content)) {
4413 xmlFreeNodeList(content);
4414 isds_printf_message(context,
4415 _("Error while adding XML document into dmXMLContent"));
4416 err = IE_XML;
4417 goto leave;
4419 /* XXX: We cannot free the content here because it's part of node's
4420 * document since now. It will be freed with it automatically. */
4422 } else {
4423 /* Binary document requested */
4424 err = insert_base64_encoded_string(context, file, NULL, "dmEncodedContent",
4425 document->data, document->data_length);
4426 if (err) goto leave;
4429 leave:
4430 return err;
4434 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4435 * The copy must be preallocated, the date are just appended into structure.
4436 * @context is ISDS context
4437 * @copy is message copy structure
4438 * @xpath_ctx is XPath context with current node as tMStatus */
4439 static isds_error append_TMStatus(struct isds_ctx *context,
4440 struct isds_message_copy *copy, xmlXPathContextPtr xpath_ctx) {
4441 isds_error err = IE_SUCCESS;
4442 xmlXPathObjectPtr result = NULL;
4443 char *code = NULL, *message = NULL;
4445 if (!context) return IE_INVALID_CONTEXT;
4446 if (!copy || !xpath_ctx) return IE_INVAL;
4448 /* Free old values */
4449 zfree(copy->dmStatus);
4450 zfree(copy->dmID);
4452 /* Get error specific to this copy */
4453 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code);
4454 if (!code) {
4455 isds_log_message(context,
4456 _("Missing isds:dmStatusCode under "
4457 "XSD:tMStatus type element"));
4458 err = IE_ISDS;
4459 goto leave;
4462 if (xmlStrcmp((const xmlChar *)code, BAD_CAST "0000")) {
4463 /* This copy failed */
4464 copy->error = IE_ISDS;
4465 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message);
4466 if (message) {
4467 copy->dmStatus = _isds_astrcat3(code, ": ", message);
4468 if (!copy->dmStatus) {
4469 copy->dmStatus = code;
4470 code = NULL;
4472 } else {
4473 copy->dmStatus = code;
4474 code = NULL;
4476 } else {
4477 /* This copy succeeded. In this case only, message ID is valid */
4478 copy->error = IE_SUCCESS;
4480 EXTRACT_STRING("isds:dmID", copy->dmID);
4481 if (!copy->dmID) {
4482 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
4483 "but did not returned assigned message ID\n"));
4484 err = IE_ISDS;
4488 leave:
4489 free(code);
4490 free(message);
4491 xmlXPathFreeObject(result);
4492 return err;
4496 /* Insert struct isds_approval data (box approval) into XML tree
4497 * @context is session context
4498 * @approval is libisds structure with approval description. NULL is
4499 * acceptable.
4500 * @parent is XML element to append @approval to */
4501 static isds_error insert_GExtApproval(struct isds_ctx *context,
4502 const struct isds_approval *approval, xmlNodePtr parent) {
4504 isds_error err = IE_SUCCESS;
4505 xmlNodePtr node;
4507 if (!context) return IE_INVALID_CONTEXT;
4508 if (!parent) return IE_INVAL;
4510 if (!approval) return IE_SUCCESS;
4512 /* Build XSD:gExtApproval */
4513 INSERT_SCALAR_BOOLEAN(parent, "dbApproved", approval->approved);
4514 INSERT_STRING(parent, "dbExternRefNumber", approval->refference);
4516 leave:
4517 return err;
4521 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4522 * code
4523 * @context is session context
4524 * @service_name is name of SERVICE_DB_ACCESS
4525 * @response is server SOAP body response as XML document
4526 * @raw_response is automatically reallocated bit stream with response body. Use
4527 * NULL if you don't care
4528 * @raw_response_length is size of @raw_response in bytes
4529 * @code is ISDS status code
4530 * @status_message is ISDS status message
4531 * @return error coded from lower layer, context message will be set up
4532 * appropriately. */
4533 static isds_error build_send_check_dbdummy_request(struct isds_ctx *context,
4534 const xmlChar *service_name,
4535 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
4536 xmlChar **code, xmlChar **status_message) {
4538 isds_error err = IE_SUCCESS;
4539 char *service_name_locale = NULL;
4540 xmlNodePtr request = NULL, node;
4541 xmlNsPtr isds_ns = NULL;
4543 if (!context) return IE_INVALID_CONTEXT;
4544 if (!service_name) return IE_INVAL;
4545 if (!response || !code || !status_message) return IE_INVAL;
4546 if (!raw_response_length && raw_response) return IE_INVAL;
4548 /* Free output argument */
4549 xmlFreeDoc(*response); *response = NULL;
4550 if (raw_response) zfree(*raw_response);
4551 free(*code);
4552 free(*status_message);
4555 /* Check if connection is established
4556 * TODO: This check should be done downstairs. */
4557 if (!context->curl) return IE_CONNECTION_CLOSED;
4559 service_name_locale = _isds_utf82locale((char*)service_name);
4560 if (!service_name_locale) {
4561 err = IE_NOMEM;
4562 goto leave;
4565 /* Build request */
4566 request = xmlNewNode(NULL, service_name);
4567 if (!request) {
4568 isds_printf_message(context,
4569 _("Could not build %s request"), service_name_locale);
4570 err = IE_ERROR;
4571 goto leave;
4573 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4574 if(!isds_ns) {
4575 isds_log_message(context, _("Could not create ISDS name space"));
4576 err = IE_ERROR;
4577 goto leave;
4579 xmlSetNs(request, isds_ns);
4582 /* Add XSD:tDummyInput child */
4583 INSERT_STRING(request, "dbDummy", NULL);
4586 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
4587 service_name_locale);
4589 /* Send request */
4590 err = isds(context, SERVICE_DB_ACCESS, request, response,
4591 raw_response, raw_response_length);
4592 xmlFreeNode(request); request = NULL;
4594 if (err) {
4595 isds_log(ILF_ISDS, ILL_DEBUG,
4596 _("Processing ISDS response on %s request failed\n"),
4597 service_name_locale);
4598 goto leave;
4601 /* Check for response status */
4602 err = isds_response_status(context, SERVICE_DB_ACCESS, *response,
4603 code, status_message, NULL);
4604 if (err) {
4605 isds_log(ILF_ISDS, ILL_DEBUG,
4606 _("ISDS response on %s request is missing status\n"),
4607 service_name_locale);
4608 goto leave;
4611 /* Request processed, but nothing found */
4612 if (xmlStrcmp(*code, BAD_CAST "0000")) {
4613 char *code_locale = _isds_utf82locale((char*) *code);
4614 char *status_message_locale =
4615 _isds_utf82locale((char*) *status_message);
4616 isds_log(ILF_ISDS, ILL_DEBUG,
4617 _("Server refused %s request (code=%s, message=%s)\n"),
4618 service_name_locale, code_locale, status_message_locale);
4619 isds_log_message(context, status_message_locale);
4620 free(code_locale);
4621 free(status_message_locale);
4622 err = IE_ISDS;
4623 goto leave;
4626 leave:
4627 free(service_name_locale);
4628 xmlFreeNode(request);
4629 return err;
4631 #endif
4634 /* Get data about logged in user and his box. */
4635 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
4636 struct isds_DbOwnerInfo **db_owner_info) {
4637 isds_error err = IE_SUCCESS;
4638 #if HAVE_LIBCURL
4639 xmlDocPtr response = NULL;
4640 xmlChar *code = NULL, *message = NULL;
4641 xmlXPathContextPtr xpath_ctx = NULL;
4642 xmlXPathObjectPtr result = NULL;
4643 char *string = NULL;
4644 #endif
4646 if (!context) return IE_INVALID_CONTEXT;
4647 zfree(context->long_message);
4648 if (!db_owner_info) return IE_INVAL;
4649 isds_DbOwnerInfo_free(db_owner_info);
4651 #if HAVE_LIBCURL
4652 /* Check if connection is established */
4653 if (!context->curl) return IE_CONNECTION_CLOSED;
4656 /* Do request and check for success */
4657 err = build_send_check_dbdummy_request(context,
4658 BAD_CAST "GetOwnerInfoFromLogin",
4659 &response, NULL, NULL, &code, &message);
4660 if (err) goto leave;
4663 /* Extract data */
4664 /* Prepare structure */
4665 *db_owner_info = calloc(1, sizeof(**db_owner_info));
4666 if (!*db_owner_info) {
4667 err = IE_NOMEM;
4668 goto leave;
4670 xpath_ctx = xmlXPathNewContext(response);
4671 if (!xpath_ctx) {
4672 err = IE_ERROR;
4673 goto leave;
4675 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4676 err = IE_ERROR;
4677 goto leave;
4680 /* Set context node */
4681 result = xmlXPathEvalExpression(BAD_CAST
4682 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
4683 if (!result) {
4684 err = IE_ERROR;
4685 goto leave;
4687 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4688 isds_log_message(context, _("Missing dbOwnerInfo element"));
4689 err = IE_ISDS;
4690 goto leave;
4692 if (result->nodesetval->nodeNr > 1) {
4693 isds_log_message(context, _("Multiple dbOwnerInfo element"));
4694 err = IE_ISDS;
4695 goto leave;
4697 xpath_ctx->node = result->nodesetval->nodeTab[0];
4698 xmlXPathFreeObject(result); result = NULL;
4700 /* Extract it */
4701 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
4704 leave:
4705 if (err) {
4706 isds_DbOwnerInfo_free(db_owner_info);
4709 free(string);
4710 xmlXPathFreeObject(result);
4711 xmlXPathFreeContext(xpath_ctx);
4713 free(code);
4714 free(message);
4715 xmlFreeDoc(response);
4717 if (!err)
4718 isds_log(ILF_ISDS, ILL_DEBUG,
4719 _("GetOwnerInfoFromLogin request processed by server "
4720 "successfully.\n"));
4721 #else /* not HAVE_LIBCURL */
4722 err = IE_NOTSUP;
4723 #endif
4725 return err;
4729 /* Get data about logged in user. */
4730 isds_error isds_GetUserInfoFromLogin(struct isds_ctx *context,
4731 struct isds_DbUserInfo **db_user_info) {
4732 isds_error err = IE_SUCCESS;
4733 #if HAVE_LIBCURL
4734 xmlDocPtr response = NULL;
4735 xmlChar *code = NULL, *message = NULL;
4736 xmlXPathContextPtr xpath_ctx = NULL;
4737 xmlXPathObjectPtr result = NULL;
4738 #endif
4740 if (!context) return IE_INVALID_CONTEXT;
4741 zfree(context->long_message);
4742 if (!db_user_info) return IE_INVAL;
4743 isds_DbUserInfo_free(db_user_info);
4745 #if HAVE_LIBCURL
4746 /* Check if connection is established */
4747 if (!context->curl) return IE_CONNECTION_CLOSED;
4750 /* Do request and check for success */
4751 err = build_send_check_dbdummy_request(context,
4752 BAD_CAST "GetUserInfoFromLogin",
4753 &response, NULL, NULL, &code, &message);
4754 if (err) goto leave;
4757 /* Extract data */
4758 /* Prepare structure */
4759 *db_user_info = calloc(1, sizeof(**db_user_info));
4760 if (!*db_user_info) {
4761 err = IE_NOMEM;
4762 goto leave;
4764 xpath_ctx = xmlXPathNewContext(response);
4765 if (!xpath_ctx) {
4766 err = IE_ERROR;
4767 goto leave;
4769 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4770 err = IE_ERROR;
4771 goto leave;
4774 /* Set context node */
4775 result = xmlXPathEvalExpression(BAD_CAST
4776 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx);
4777 if (!result) {
4778 err = IE_ERROR;
4779 goto leave;
4781 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4782 isds_log_message(context, _("Missing dbUserInfo element"));
4783 err = IE_ISDS;
4784 goto leave;
4786 if (result->nodesetval->nodeNr > 1) {
4787 isds_log_message(context, _("Multiple dbUserInfo element"));
4788 err = IE_ISDS;
4789 goto leave;
4791 xpath_ctx->node = result->nodesetval->nodeTab[0];
4792 xmlXPathFreeObject(result); result = NULL;
4794 /* Extract it */
4795 err = extract_DbUserInfo(context, db_user_info, xpath_ctx);
4797 leave:
4798 if (err) {
4799 isds_DbUserInfo_free(db_user_info);
4802 xmlXPathFreeObject(result);
4803 xmlXPathFreeContext(xpath_ctx);
4805 free(code);
4806 free(message);
4807 xmlFreeDoc(response);
4809 if (!err)
4810 isds_log(ILF_ISDS, ILL_DEBUG,
4811 _("GetUserInfoFromLogin request processed by server "
4812 "successfully.\n"));
4813 #else /* not HAVE_LIBCURL */
4814 err = IE_NOTSUP;
4815 #endif
4817 return err;
4821 /* Get expiration time of current password
4822 * @context is session context
4823 * @expiration is automatically reallocated time when password expires. If
4824 * password expiration is disables, NULL will be returned. In case of error
4825 * it will be nulled too. */
4826 isds_error isds_get_password_expiration(struct isds_ctx *context,
4827 struct timeval **expiration) {
4828 isds_error err = IE_SUCCESS;
4829 #if HAVE_LIBCURL
4830 xmlDocPtr response = NULL;
4831 xmlChar *code = NULL, *message = NULL;
4832 xmlXPathContextPtr xpath_ctx = NULL;
4833 xmlXPathObjectPtr result = NULL;
4834 char *string = NULL;
4835 #endif
4837 if (!context) return IE_INVALID_CONTEXT;
4838 zfree(context->long_message);
4839 if (!expiration) return IE_INVAL;
4840 zfree(*expiration);
4842 #if HAVE_LIBCURL
4843 /* Check if connection is established */
4844 if (!context->curl) return IE_CONNECTION_CLOSED;
4847 /* Do request and check for success */
4848 err = build_send_check_dbdummy_request(context,
4849 BAD_CAST "GetPasswordInfo",
4850 &response, NULL, NULL, &code, &message);
4851 if (err) goto leave;
4854 /* Extract data */
4855 xpath_ctx = xmlXPathNewContext(response);
4856 if (!xpath_ctx) {
4857 err = IE_ERROR;
4858 goto leave;
4860 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4861 err = IE_ERROR;
4862 goto leave;
4865 /* Set context node */
4866 result = xmlXPathEvalExpression(BAD_CAST
4867 "/isds:GetPasswordInfoResponse", xpath_ctx);
4868 if (!result) {
4869 err = IE_ERROR;
4870 goto leave;
4872 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4873 isds_log_message(context,
4874 _("Missing GetPasswordInfoResponse element"));
4875 err = IE_ISDS;
4876 goto leave;
4878 if (result->nodesetval->nodeNr > 1) {
4879 isds_log_message(context,
4880 _("Multiple GetPasswordInfoResponse element"));
4881 err = IE_ISDS;
4882 goto leave;
4884 xpath_ctx->node = result->nodesetval->nodeTab[0];
4885 xmlXPathFreeObject(result); result = NULL;
4887 /* Extract expiration date */
4888 EXTRACT_STRING("isds:pswExpDate", string);
4889 if (string) {
4890 /* And convert it if any returned. Otherwise expiration is disabled. */
4891 err = timestring2timeval((xmlChar *) string, expiration);
4892 if (err) {
4893 char *string_locale = _isds_utf82locale(string);
4894 if (err == IE_DATE) err = IE_ISDS;
4895 isds_printf_message(context,
4896 _("Could not convert pswExpDate as ISO time: %s"),
4897 string_locale);
4898 free(string_locale);
4899 goto leave;
4903 leave:
4904 if (err) {
4905 if (*expiration) {
4906 zfree(*expiration);
4910 free(string);
4911 xmlXPathFreeObject(result);
4912 xmlXPathFreeContext(xpath_ctx);
4914 free(code);
4915 free(message);
4916 xmlFreeDoc(response);
4918 if (!err)
4919 isds_log(ILF_ISDS, ILL_DEBUG,
4920 _("GetPasswordInfo request processed by server "
4921 "successfully.\n"));
4922 #else /* not HAVE_LIBCURL */
4923 err = IE_NOTSUP;
4924 #endif
4926 return err;
4930 #if HAVE_LIBCURL
4931 /* Request delivering new TOTP code from ISDS through side channel before
4932 * changing password.
4933 * @context is session context
4934 * @password is current password.
4935 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
4936 * Please note the @otp argument must have TOTP OTP method. See isds_login()
4937 * function for more details.
4938 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
4939 * error code. */
4940 static isds_error _isds_request_totp_code(struct isds_ctx *context,
4941 const char *password, struct isds_otp *otp) {
4942 isds_error err = IE_SUCCESS;
4943 char *saved_url = NULL; /* No copy */
4944 xmlNsPtr isds_ns = NULL;
4945 xmlNodePtr request = NULL;
4946 xmlDocPtr response = NULL;
4947 xmlChar *code = NULL, *message = NULL;
4948 const xmlChar *codes[] = {
4949 BAD_CAST "2300",
4950 BAD_CAST "2301",
4951 BAD_CAST "2302"
4953 const char *meanings[] = {
4954 N_("Unexpected error"),
4955 N_("One-time code cannot be re-send faster than once a 30 seconds"),
4956 N_("One-time code could not been sent. Try later again.")
4958 const isds_otp_resolution resolutions[] = {
4959 OTP_RESOLUTION_UNKNOWN,
4960 OTP_RESOLUTION_TO_FAST,
4961 OTP_RESOLUTION_TOTP_NOT_SENT
4964 if (NULL == context) return IE_INVALID_CONTEXT;
4965 zfree(context->long_message);
4966 if (NULL == password) {
4967 isds_log_message(context,
4968 _("Second argument (password) of isds_change_password() "
4969 "is NULL"));
4970 return IE_INVAL;
4973 /* Check if connection is established
4974 * TODO: This check should be done downstairs. */
4975 if (!context->curl) return IE_CONNECTION_CLOSED;
4977 if (!context->otp) {
4978 isds_log_message(context, _("This function requires OTP-authenticated "
4979 "context"));
4980 return IE_INVALID_CONTEXT;
4982 if (NULL == otp) {
4983 isds_log_message(context, _("If one-time password authentication "
4984 "method is in use, requesting new OTP code requires "
4985 "one-time credentials argument either"));
4986 return IE_INVAL;
4988 if (otp->method != OTP_TIME) {
4989 isds_log_message(context, _("Requesting new time-based OTP code from "
4990 "server requires one-time password authentication "
4991 "method"));
4992 return IE_INVAL;
4994 if (otp->otp_code != NULL) {
4995 isds_log_message(context, _("Requesting new time-based OTP code from "
4996 "server requires undefined OTP code member in "
4997 "one-time credentials argument"));
4998 return IE_INVAL;
5002 /* Build request */
5003 request = xmlNewNode(NULL, BAD_CAST "SendSMSCode");
5004 if (!request) {
5005 isds_log_message(context, _("Could not build SendSMSCode request"));
5006 return IE_ERROR;
5008 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5009 if(!isds_ns) {
5010 isds_log_message(context, _("Could not create ISDS name space"));
5011 xmlFreeNode(request);
5012 return IE_ERROR;
5014 xmlSetNs(request, isds_ns);
5016 /* Change URL temporarily for sending this request only */
5018 char *new_url = NULL;
5019 if ((err = _isds_build_url_from_context(context,
5020 "%1$.*2$sasws/changePassword", &new_url))) {
5021 goto leave;
5023 saved_url = context->url;
5024 context->url = new_url;
5027 /* Store credentials for sending this request only */
5028 context->otp_credentials = otp;
5029 _isds_discard_credentials(context, 0);
5030 if ((err = _isds_store_credentials(context, context->saved_username,
5031 password, NULL))) {
5032 _isds_discard_credentials(context, 0);
5033 goto leave;
5036 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending SendSMSCode request to ISDS\n"));
5038 /* Sent request */
5039 err = isds(context, SERVICE_ASWS, request, &response, NULL, NULL);
5041 /* Remove temporal credentials */
5042 _isds_discard_credentials(context, 0);
5043 /* Detach pointer to OTP credentials from context */
5044 context->otp_credentials = NULL;
5045 /* Keep context->otp true to keep signaling this is OTP session */
5047 /* Destroy request */
5048 xmlFreeNode(request); request = NULL;
5050 if (err) {
5051 isds_log(ILF_ISDS, ILL_DEBUG,
5052 _("Processing ISDS response on SendSMSCode request failed\n"));
5053 goto leave;
5056 /* Check for response status */
5057 err = isds_response_status(context, SERVICE_ASWS, response,
5058 &code, &message, NULL);
5059 if (err) {
5060 isds_log(ILF_ISDS, ILL_DEBUG,
5061 _("ISDS response on SendSMSCode request is missing "
5062 "status\n"));
5063 goto leave;
5066 /* Check for error */
5067 if (xmlStrcmp(code, BAD_CAST "0000")) {
5068 char *code_locale = _isds_utf82locale((char*)code);
5069 char *message_locale = _isds_utf82locale((char*)message);
5070 int i;
5071 isds_log(ILF_ISDS, ILL_DEBUG,
5072 _("Server refused to send new code on SendSMSCode "
5073 "request (code=%s, message=%s)\n"),
5074 code_locale, message_locale);
5076 /* Check for known error codes */
5077 for (i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
5078 if (!xmlStrcmp(code, codes[i])) break;
5080 if (i < sizeof(codes)/sizeof(*codes)) {
5081 isds_log_message(context, _(meanings[i]));
5082 /* Mimic otp->resolution according to the code, specification does
5083 * prescribe OTP header to be available. */
5084 if (OTP_RESOLUTION_SUCCESS == otp->resolution &&
5085 OTP_RESOLUTION_UNKNOWN != resolutions[i])
5086 otp->resolution = resolutions[i];
5087 } else
5088 isds_log_message(context, message_locale);
5090 free(code_locale);
5091 free(message_locale);
5093 err = IE_ISDS;
5094 goto leave;
5097 /* Otherwise new code sent successfully */
5098 /* Mimic otp->resolution according to the code, specification does
5099 * prescribe OTP header to be available. */
5100 if (OTP_RESOLUTION_SUCCESS == otp->resolution)
5101 otp->resolution = OTP_RESOLUTION_TOTP_SENT;
5103 leave:
5104 if (NULL != saved_url) {
5105 /* Revert URL to original one */
5106 zfree(context->url);
5107 context->url = saved_url;
5110 free(code);
5111 free(message);
5112 xmlFreeDoc(response);
5113 xmlFreeNode(request);
5115 if (!err)
5116 isds_log(ILF_ISDS, ILL_DEBUG,
5117 _("New OTP code has been sent successfully on SendSMSCode "
5118 "request.\n"));
5119 return err;
5121 #endif
5124 /* Change user password in ISDS.
5125 * User must supply old password, new password will takes effect after some
5126 * time, current session can continue. Password must fulfill some constraints.
5127 * @context is session context
5128 * @old_password is current password.
5129 * @new_password is requested new password
5130 * @otp auxiliary data required if one-time password authentication is in use,
5131 * defines OTP code (if known) and returns fine grade resolution of OTP
5132 * procedure. Pass NULL, if one-time password authentication is not needed.
5133 * Please note the @otp argument must match OTP method used at log-in time. See
5134 * isds_login() function for more details.
5135 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5136 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5137 * awaiting OTP code that has been delivered by side channel to the user. */
5138 isds_error isds_change_password(struct isds_ctx *context,
5139 const char *old_password, const char *new_password,
5140 struct isds_otp *otp) {
5141 isds_error err = IE_SUCCESS;
5142 #if HAVE_LIBCURL
5143 char *saved_url = NULL; /* No copy */
5144 xmlNsPtr isds_ns = NULL;
5145 xmlNodePtr request = NULL, node;
5146 xmlDocPtr response = NULL;
5147 xmlChar *code = NULL, *message = NULL;
5148 const xmlChar *codes[] = {
5149 BAD_CAST "1066",
5150 BAD_CAST "1067",
5151 BAD_CAST "1079",
5152 BAD_CAST "1080",
5153 BAD_CAST "1081",
5154 BAD_CAST "1082",
5155 BAD_CAST "1083",
5156 BAD_CAST "1090",
5157 BAD_CAST "1091",
5158 BAD_CAST "2300",
5159 BAD_CAST "9204"
5161 const char *meanings[] = {
5162 N_("Password length must be between 8 and 32 characters"),
5163 N_("New password must differ from the current one"),
5164 N_("Password contains forbidden character"),
5165 N_("Password must contain at least one upper-case letter, "
5166 "one lower-case, and one digit"),
5167 N_("Password cannot contain sequence of three identical characters"),
5168 N_("Password cannot contain user identifier"),
5169 N_("Password is too simmple"),
5170 N_("Old password is not valid"),
5171 N_("Passwords cannot be reused"),
5172 N_("Unexpected error"),
5173 N_("LDAP update error")
5175 #endif
5177 if (!context) return IE_INVALID_CONTEXT;
5178 zfree(context->long_message);
5179 if (NULL == old_password) {
5180 isds_log_message(context,
5181 _("Second argument (old password) of isds_change_password() "
5182 "is NULL"));
5183 return IE_INVAL;
5185 if (NULL == new_password) {
5186 isds_log_message(context,
5187 _("Third argument (new password) of isds_change_password() "
5188 "is NULL"));
5189 return IE_INVAL;
5192 #if HAVE_LIBCURL
5193 /* Check if connection is established
5194 * TODO: This check should be done downstairs. */
5195 if (!context->curl) return IE_CONNECTION_CLOSED;
5197 if (context->otp && NULL == otp) {
5198 isds_log_message(context, _("If one-time password authentication "
5199 "method is in use, changing password requires one-time "
5200 "credentials either"));
5201 return IE_INVAL;
5204 /* Build ChangeISDSPassword request */
5205 request = xmlNewNode(NULL, (NULL == otp) ? BAD_CAST "ChangeISDSPassword" :
5206 BAD_CAST "ChangePasswordOTP");
5207 if (!request) {
5208 isds_log_message(context, (NULL == otp) ?
5209 _("Could not build ChangeISDSPassword request") :
5210 _("Could not build ChangePasswordOTP request"));
5211 return IE_ERROR;
5213 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5214 if(!isds_ns) {
5215 isds_log_message(context, _("Could not create ISDS name space"));
5216 xmlFreeNode(request);
5217 return IE_ERROR;
5219 xmlSetNs(request, isds_ns);
5221 INSERT_STRING(request, "dbOldPassword", old_password);
5222 INSERT_STRING(request, "dbNewPassword", new_password);
5224 if (NULL != otp) {
5225 otp->resolution = OTP_RESOLUTION_UNKNOWN;
5226 switch (otp->method) {
5227 case OTP_HMAC:
5228 isds_log(ILF_SEC, ILL_INFO,
5229 _("Selected authentication method: "
5230 "HMAC-based one-time password\n"));
5231 INSERT_STRING(request, "dbOTPType", BAD_CAST "HOTP");
5232 break;
5233 case OTP_TIME:
5234 isds_log(ILF_SEC, ILL_INFO,
5235 _("Selected authentication method: "
5236 "Time-based one-time password\n"));
5237 INSERT_STRING(request, "dbOTPType", BAD_CAST "TOTP");
5238 if (otp->otp_code == NULL) {
5239 isds_log(ILF_SEC, ILL_INFO,
5240 _("OTP code has not been provided by "
5241 "application, requesting server for "
5242 "new one.\n"));
5243 err = _isds_request_totp_code(context, old_password, otp);
5244 if (err == IE_SUCCESS) err = IE_PARTIAL_SUCCESS;
5245 goto leave;
5247 } else {
5248 isds_log(ILF_SEC, ILL_INFO,
5249 _("OTP code has been provided by "
5250 "application, not requesting server "
5251 "for new one.\n"));
5253 break;
5254 default:
5255 isds_log_message(context,
5256 _("Unknown one-time password authentication "
5257 "method requested by application"));
5258 err = IE_ENUM;
5259 goto leave;
5262 /* Change URL temporarily for sending this request only */
5264 char *new_url = NULL;
5265 if ((err = _isds_build_url_from_context(context,
5266 "%1$.*2$sasws/changePassword", &new_url))) {
5267 goto leave;
5269 saved_url = context->url;
5270 context->url = new_url;
5273 /* Store credentials for sending this request only */
5274 context->otp_credentials = otp;
5275 _isds_discard_credentials(context, 0);
5276 if ((err = _isds_store_credentials(context, context->saved_username,
5277 old_password, NULL))) {
5278 _isds_discard_credentials(context, 0);
5279 goto leave;
5284 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5285 _("Sending ChangeISDSPassword request to ISDS\n") :
5286 _("Sending ChangePasswordOTP request to ISDS\n"));
5288 /* Sent request */
5289 err = isds(context, (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS,
5290 request, &response, NULL, NULL);
5292 if (otp) {
5293 /* Remove temporal credentials */
5294 _isds_discard_credentials(context, 0);
5295 /* Detach pointer to OTP credentials from context */
5296 context->otp_credentials = NULL;
5297 /* Keep context->otp true to keep signaling this is OTP session */
5300 /* Destroy request */
5301 xmlFreeNode(request); request = NULL;
5303 if (err) {
5304 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5305 _("Processing ISDS response on ChangeISDSPassword "
5306 "request failed\n") :
5307 _("Processing ISDS response on ChangePasswordOTP "
5308 "request failed\n"));
5309 goto leave;
5312 /* Check for response status */
5313 err = isds_response_status(context,
5314 (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS, response,
5315 &code, &message, NULL);
5316 if (err) {
5317 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5318 _("ISDS response on ChangeISDSPassword request is missing "
5319 "status\n") :
5320 _("ISDS response on ChangePasswordOTP request is missing "
5321 "status\n"));
5322 goto leave;
5325 /* Check for known error codes */
5326 for (int i=0; i < sizeof(codes)/sizeof(*codes); i++) {
5327 if (!xmlStrcmp(code, codes[i])) {
5328 char *code_locale = _isds_utf82locale((char*)code);
5329 char *message_locale = _isds_utf82locale((char*)message);
5330 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5331 _("Server refused to change password on ChangeISDSPassword "
5332 "request (code=%s, message=%s)\n") :
5333 _("Server refused to change password on ChangePasswordOTP "
5334 "request (code=%s, message=%s)\n"),
5335 code_locale, message_locale);
5336 free(code_locale);
5337 free(message_locale);
5338 isds_log_message(context, _(meanings[i]));
5339 err = IE_INVAL;
5340 goto leave;
5344 /* Other error */
5345 if (xmlStrcmp(code, BAD_CAST "0000")) {
5346 char *code_locale = _isds_utf82locale((char*)code);
5347 char *message_locale = _isds_utf82locale((char*)message);
5348 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5349 _("Server refused to change password on ChangeISDSPassword "
5350 "request (code=%s, message=%s)\n") :
5351 _("Server refused to change password on ChangePasswordOTP "
5352 "request (code=%s, message=%s)\n"),
5353 code_locale, message_locale);
5354 isds_log_message(context, message_locale);
5355 free(code_locale);
5356 free(message_locale);
5357 err = IE_ISDS;
5358 goto leave;
5361 /* Otherwise password changed successfully */
5363 leave:
5364 if (NULL != saved_url) {
5365 /* Revert URL to original one */
5366 zfree(context->url);
5367 context->url = saved_url;
5370 free(code);
5371 free(message);
5372 xmlFreeDoc(response);
5373 xmlFreeNode(request);
5375 if (!err)
5376 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5377 _("Password changed successfully on ChangeISDSPassword "
5378 "request.\n") :
5379 _("Password changed successfully on ChangePasswordOTP "
5380 "request.\n"));
5381 #else /* not HAVE_LIBCURL */
5382 err = IE_NOTSUP;
5383 #endif
5385 return err;
5389 #if HAVE_LIBCURL
5390 /* Generic middle part with request sending and response check.
5391 * It sends prepared request and checks for error code.
5392 * @context is ISDS session context.
5393 * @service is ISDS service handler
5394 * @service_name is name in scope of given @service
5395 * @request is XML tree with request. Will be freed to save memory.
5396 * @response is XML document outputting ISDS response.
5397 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5398 * NULL, if you don't care. */
5399 static isds_error send_destroy_request_check_response(
5400 struct isds_ctx *context,
5401 const isds_service service, const xmlChar *service_name,
5402 xmlNodePtr *request, xmlDocPtr *response, xmlChar **refnumber) {
5403 isds_error err = IE_SUCCESS;
5404 char *service_name_locale = NULL;
5405 xmlChar *code = NULL, *message = NULL;
5408 if (!context) return IE_INVALID_CONTEXT;
5409 if (!service_name || *service_name == '\0' || !request || !*request ||
5410 !response)
5411 return IE_INVAL;
5413 /* Check if connection is established
5414 * TODO: This check should be done downstairs. */
5415 if (!context->curl) return IE_CONNECTION_CLOSED;
5417 service_name_locale = _isds_utf82locale((char*) service_name);
5418 if (!service_name_locale) {
5419 err = IE_NOMEM;
5420 goto leave;
5423 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
5424 service_name_locale);
5426 /* Send request */
5427 err = isds(context, service, *request, response, NULL, NULL);
5428 xmlFreeNode(*request); *request = NULL;
5430 if (err) {
5431 isds_log(ILF_ISDS, ILL_DEBUG,
5432 _("Processing ISDS response on %s request failed\n"),
5433 service_name_locale);
5434 goto leave;
5437 /* Check for response status */
5438 err = isds_response_status(context, service, *response,
5439 &code, &message, refnumber);
5440 if (err) {
5441 isds_log(ILF_ISDS, ILL_DEBUG,
5442 _("ISDS response on %s request is missing status\n"),
5443 service_name_locale);
5444 goto leave;
5447 /* Request processed, but server failed */
5448 if (xmlStrcmp(code, BAD_CAST "0000")) {
5449 char *code_locale = _isds_utf82locale((char*) code);
5450 char *message_locale = _isds_utf82locale((char*) message);
5451 isds_log(ILF_ISDS, ILL_DEBUG,
5452 _("Server refused %s request (code=%s, message=%s)\n"),
5453 service_name_locale, code_locale, message_locale);
5454 isds_log_message(context, message_locale);
5455 free(code_locale);
5456 free(message_locale);
5457 err = IE_ISDS;
5458 goto leave;
5462 leave:
5463 free(code);
5464 free(message);
5465 if (err && *response) {
5466 xmlFreeDoc(*response);
5467 *response = NULL;
5469 if (*request) {
5470 xmlFreeNode(*request);
5471 *request = NULL;
5473 free(service_name_locale);
5475 return err;
5479 /* Generic bottom half with request sending.
5480 * It sends prepared request, checks for error code, destroys response and
5481 * request and log success or failure.
5482 * @context is ISDS session context.
5483 * @service is ISDS service handler
5484 * @service_name is name in scope of given @service
5485 * @request is XML tree with request. Will be freed to save memory.
5486 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5487 * NULL, if you don't care. */
5488 static isds_error send_request_check_drop_response(
5489 struct isds_ctx *context,
5490 const isds_service service, const xmlChar *service_name,
5491 xmlNodePtr *request, xmlChar **refnumber) {
5492 isds_error err = IE_SUCCESS;
5493 xmlDocPtr response = NULL;
5496 if (!context) return IE_INVALID_CONTEXT;
5497 if (!service_name || *service_name == '\0' || !request || !*request)
5498 return IE_INVAL;
5500 /* Send request and check response*/
5501 err = send_destroy_request_check_response(context,
5502 service, service_name, request, &response, refnumber);
5504 xmlFreeDoc(response);
5506 if (*request) {
5507 xmlFreeNode(*request);
5508 *request = NULL;
5511 if (!err) {
5512 char *service_name_locale = _isds_utf82locale((char *) service_name);
5513 isds_log(ILF_ISDS, ILL_DEBUG,
5514 _("%s request processed by server successfully.\n"),
5515 service_name_locale);
5516 free(service_name_locale);
5519 return err;
5523 /* Insert isds_credentials_delivery structure into XML request if not NULL
5524 * @context is session context
5525 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
5526 * credentials delivery. The email field is passed.
5527 * @parent is XML element where to insert */
5528 static isds_error insert_credentials_delivery(struct isds_ctx *context,
5529 const struct isds_credentials_delivery *credentials_delivery,
5530 xmlNodePtr parent) {
5531 isds_error err = IE_SUCCESS;
5532 xmlNodePtr node;
5534 if (!context) return IE_INVALID_CONTEXT;
5535 if (!parent) return IE_INVAL;
5537 if (credentials_delivery) {
5538 /* Following elements are valid only for services:
5539 * NewAccessData, AddDataBoxUser, CreateDataBox */
5540 INSERT_SCALAR_BOOLEAN(parent, "dbVirtual", 1);
5541 INSERT_STRING(parent, "email", credentials_delivery->email);
5544 leave:
5545 return err;
5549 /* Extract credentials delivery from ISDS response.
5550 * @context is session context
5551 * @credentials_delivery is pointer to valid structure to fill in returned
5552 * user's password (and new log-in name). If NULL, do not extract the data.
5553 * @response is pointer to XML document with ISDS response
5554 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
5555 * @return IE_SUCCESS even if new user name has not been found because it's not
5556 * clear whether it's returned always. */
5557 static isds_error extract_credentials_delivery(struct isds_ctx *context,
5558 struct isds_credentials_delivery *credentials_delivery,
5559 xmlDocPtr response, const char *request_name) {
5560 isds_error err = IE_SUCCESS;
5561 xmlXPathContextPtr xpath_ctx = NULL;
5562 xmlXPathObjectPtr result = NULL;
5563 char *xpath_query = NULL;
5565 if (!context) return IE_INVALID_CONTEXT;
5566 if (credentials_delivery) {
5567 zfree(credentials_delivery->token);
5568 zfree(credentials_delivery->new_user_name);
5570 if (!response || !request_name || !*request_name) return IE_INVAL;
5573 /* Extract optional token */
5574 if (credentials_delivery) {
5575 xpath_ctx = xmlXPathNewContext(response);
5576 if (!xpath_ctx) {
5577 err = IE_ERROR;
5578 goto leave;
5580 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5581 err = IE_ERROR;
5582 goto leave;
5585 /* Verify root element */
5586 if (-1 == isds_asprintf(&xpath_query, "/isds:%sResponse",
5587 request_name)) {
5588 err = IE_NOMEM;
5589 goto leave;
5591 result = xmlXPathEvalExpression(BAD_CAST xpath_query, xpath_ctx);
5592 if (!result) {
5593 err = IE_ERROR;
5594 goto leave;
5596 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5597 char *request_name_locale = _isds_utf82locale(request_name);
5598 isds_log(ILF_ISDS, ILL_WARNING,
5599 _("Wrong element in ISDS response for %s request "
5600 "while extracting credentials delivery details\n"),
5601 request_name_locale);
5602 free(request_name_locale);
5603 err = IE_ERROR;
5604 goto leave;
5606 xpath_ctx->node = result->nodesetval->nodeTab[0];
5609 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
5610 * optional. */
5611 EXTRACT_STRING("isds:dbUserID", credentials_delivery->new_user_name);
5613 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery->token);
5614 if (!credentials_delivery->token) {
5615 char *request_name_locale = _isds_utf82locale(request_name);
5616 isds_log(ILF_ISDS, ILL_ERR,
5617 _("ISDS did not return token on %s request "
5618 "even if requested\n"), request_name_locale);
5619 free(request_name_locale);
5620 err = IE_ERROR;
5624 leave:
5625 free(xpath_query);
5626 xmlXPathFreeObject(result);
5627 xmlXPathFreeContext(xpath_ctx);
5629 return err;
5633 /* Build XSD:tCreateDBInput request type for box creating.
5634 * @context is session context
5635 * @request outputs built XML tree
5636 * @service_name is request name of SERVICE_DB_MANIPULATION service
5637 * @box is box description to create including single primary user (in case of
5638 * FO box type)
5639 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
5640 * box, or contact address of PFO box owner)
5641 * @former_names is optional former name of box owner. Pass NULL if otherwise.
5642 * @upper_box_id is optional ID of supper box if currently created box is
5643 * subordinated.
5644 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
5645 * don't care.
5646 * @credentials_delivery is valid pointer if ISDS should return token that box
5647 * owner can use to obtain his new credentials in on-line way. Then valid email
5648 * member value should be supplied.
5649 * @approval is optional external approval of box manipulation */
5650 static isds_error build_CreateDBInput_request(struct isds_ctx *context,
5651 xmlNodePtr *request, const xmlChar *service_name,
5652 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
5653 const xmlChar *former_names, const xmlChar *upper_box_id,
5654 const xmlChar *ceo_label,
5655 const struct isds_credentials_delivery *credentials_delivery,
5656 const struct isds_approval *approval) {
5657 isds_error err = IE_SUCCESS;
5658 xmlNsPtr isds_ns = NULL;
5659 xmlNodePtr node, dbPrimaryUsers;
5660 xmlChar *string = NULL;
5661 const struct isds_list *item;
5664 if (!context) return IE_INVALID_CONTEXT;
5665 if (!request || !service_name || service_name[0] == '\0' || !box)
5666 return IE_INVAL;
5669 /* Build CreateDataBox-similar request */
5670 *request = xmlNewNode(NULL, service_name);
5671 if (!*request) {
5672 char *service_name_locale = _isds_utf82locale((char*) service_name);
5673 isds_printf_message(context, _("Could build %s request"),
5674 service_name_locale);
5675 free(service_name_locale);
5676 return IE_ERROR;
5678 if (context->type == CTX_TYPE_TESTING_REQUEST_COLLECTOR) {
5679 isds_ns = xmlNewNs(*request, BAD_CAST ISDS1_NS, NULL);
5680 if (!isds_ns) {
5681 isds_log_message(context, _("Could not create ISDS1 name space"));
5682 xmlFreeNode(*request);
5683 return IE_ERROR;
5685 } else {
5686 isds_ns = xmlNewNs(*request, BAD_CAST ISDS_NS, NULL);
5687 if (!isds_ns) {
5688 isds_log_message(context, _("Could not create ISDS name space"));
5689 xmlFreeNode(*request);
5690 return IE_ERROR;
5693 xmlSetNs(*request, isds_ns);
5695 INSERT_ELEMENT(node, *request, "dbOwnerInfo");
5696 err = insert_DbOwnerInfo(context, box, node);
5697 if (err) goto leave;
5699 /* Insert users */
5700 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
5701 * verbose documentation allows none dbUserInfo */
5702 INSERT_ELEMENT(dbPrimaryUsers, *request, "dbPrimaryUsers");
5703 for (item = users; item; item = item->next) {
5704 if (item->data) {
5705 INSERT_ELEMENT(node, dbPrimaryUsers, "dbUserInfo");
5706 err = insert_DbUserInfo(context,
5707 (struct isds_DbUserInfo *) item->data, node);
5708 if (err) goto leave;
5712 INSERT_STRING(*request, "dbFormerNames", former_names);
5713 INSERT_STRING(*request, "dbUpperDBId", upper_box_id);
5714 INSERT_STRING(*request, "dbCEOLabel", ceo_label);
5716 err = insert_credentials_delivery(context, credentials_delivery, *request);
5717 if (err) goto leave;
5719 err = insert_GExtApproval(context, approval, *request);
5720 if (err) goto leave;
5722 leave:
5723 if (err) {
5724 xmlFreeNode(*request);
5725 *request = NULL;
5727 free(string);
5728 return err;
5730 #endif /* HAVE_LIBCURL */
5733 /* Create new box.
5734 * @context is session context
5735 * @box is box description to create including single primary user (in case of
5736 * FO box type). It outputs box ID assigned by ISDS in dbID element.
5737 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
5738 * box, or contact address of PFO box owner)
5739 * @former_names is optional former name of box owner. Pass NULL if you don't care.
5740 * @upper_box_id is optional ID of supper box if currently created box is
5741 * subordinated.
5742 * @ceo_label is optional title of OVM box owner (e.g. mayor)
5743 * @credentials_delivery is NULL if new password should be delivered off-line
5744 * to box owner. It is valid pointer if owner should obtain new password on-line
5745 * on dedicated web server. Then input @credentials_delivery.email value is
5746 * his e-mail address he must provide to dedicated web server together
5747 * with output reallocated @credentials_delivery.token member. Output
5748 * member @credentials_delivery.new_user_name is unused up on this call.
5749 * @approval is optional external approval of box manipulation
5750 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5751 * NULL, if you don't care.*/
5752 isds_error isds_add_box(struct isds_ctx *context,
5753 struct isds_DbOwnerInfo *box, const struct isds_list *users,
5754 const char *former_names, const char *upper_box_id,
5755 const char *ceo_label,
5756 struct isds_credentials_delivery *credentials_delivery,
5757 const struct isds_approval *approval, char **refnumber) {
5758 isds_error err = IE_SUCCESS;
5759 #if HAVE_LIBCURL
5760 xmlNodePtr request = NULL;
5761 xmlDocPtr response = NULL;
5762 xmlXPathContextPtr xpath_ctx = NULL;
5763 xmlXPathObjectPtr result = NULL;
5764 #endif
5767 if (!context) return IE_INVALID_CONTEXT;
5768 zfree(context->long_message);
5769 if (credentials_delivery) {
5770 zfree(credentials_delivery->token);
5771 zfree(credentials_delivery->new_user_name);
5773 if (!box) return IE_INVAL;
5775 #if HAVE_LIBCURL
5776 /* Scratch box ID */
5777 zfree(box->dbID);
5779 /* Build CreateDataBox request */
5780 err = build_CreateDBInput_request(context,
5781 &request, BAD_CAST "CreateDataBox",
5782 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
5783 (xmlChar *) ceo_label, credentials_delivery, approval);
5784 if (err) goto leave;
5786 /* Send it to server and process response */
5787 err = send_destroy_request_check_response(context,
5788 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
5789 &response, (xmlChar **) refnumber);
5791 /* Extract box ID */
5792 xpath_ctx = xmlXPathNewContext(response);
5793 if (!xpath_ctx) {
5794 err = IE_ERROR;
5795 goto leave;
5797 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5798 err = IE_ERROR;
5799 goto leave;
5801 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
5803 /* Extract optional token */
5804 err = extract_credentials_delivery(context, credentials_delivery, response,
5805 "CreateDataBox");
5807 leave:
5808 xmlXPathFreeObject(result);
5809 xmlXPathFreeContext(xpath_ctx);
5810 xmlFreeDoc(response);
5811 xmlFreeNode(request);
5813 if (!err) {
5814 isds_log(ILF_ISDS, ILL_DEBUG,
5815 _("CreateDataBox request processed by server successfully.\n"));
5817 #else /* not HAVE_LIBCURL */
5818 err = IE_NOTSUP;
5819 #endif
5821 return err;
5825 /* Notify ISDS about new PFO entity.
5826 * This function has no real effect.
5827 * @context is session context
5828 * @box is PFO description including single primary user.
5829 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
5830 * @former_names is optional undocumented string. Pass NULL if you don't care.
5831 * @upper_box_id is optional ID of supper box if currently created box is
5832 * subordinated.
5833 * @ceo_label is optional title of OVM box owner (e.g. mayor)
5834 * @approval is optional external approval of box manipulation
5835 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5836 * NULL, if you don't care.*/
5837 isds_error isds_add_pfoinfo(struct isds_ctx *context,
5838 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
5839 const char *former_names, const char *upper_box_id,
5840 const char *ceo_label, const struct isds_approval *approval,
5841 char **refnumber) {
5842 isds_error err = IE_SUCCESS;
5843 #if HAVE_LIBCURL
5844 xmlNodePtr request = NULL;
5845 #endif
5847 if (!context) return IE_INVALID_CONTEXT;
5848 zfree(context->long_message);
5849 if (!box) return IE_INVAL;
5851 #if HAVE_LIBCURL
5852 /* Build CreateDataBoxPFOInfo request */
5853 err = build_CreateDBInput_request(context,
5854 &request, BAD_CAST "CreateDataBoxPFOInfo",
5855 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
5856 (xmlChar *) ceo_label, NULL, approval);
5857 if (err) goto leave;
5859 /* Send it to server and process response */
5860 err = send_request_check_drop_response(context,
5861 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
5862 (xmlChar **) refnumber);
5863 /* XXX: XML Schema names output dbID element but textual documentation
5864 * states no box identifier is returned. */
5865 leave:
5866 xmlFreeNode(request);
5867 #else /* not HAVE_LIBCURL */
5868 err = IE_NOTSUP;
5869 #endif
5870 return err;
5874 /* Common implementation for removing given box.
5875 * @context is session context
5876 * @service_name is UTF-8 encoded name fo ISDS service
5877 * @box is box description to delete
5878 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
5879 * carry sane value. If NULL, do not inject this information into request.
5880 * @approval is optional external approval of box manipulation
5881 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5882 * NULL, if you don't care.*/
5883 isds_error _isds_delete_box_common(struct isds_ctx *context,
5884 const xmlChar *service_name,
5885 const struct isds_DbOwnerInfo *box, const struct tm *since,
5886 const struct isds_approval *approval, char **refnumber) {
5887 isds_error err = IE_SUCCESS;
5888 #if HAVE_LIBCURL
5889 xmlNsPtr isds_ns = NULL;
5890 xmlNodePtr request = NULL;
5891 xmlNodePtr node;
5892 xmlChar *string = NULL;
5893 #endif
5896 if (!context) return IE_INVALID_CONTEXT;
5897 zfree(context->long_message);
5898 if (!service_name || !*service_name || !box) return IE_INVAL;
5901 #if HAVE_LIBCURL
5902 /* Build DeleteDataBox(Promptly) request */
5903 request = xmlNewNode(NULL, service_name);
5904 if (!request) {
5905 char *service_name_locale = _isds_utf82locale((char*)service_name);
5906 isds_printf_message(context,
5907 _("Could build %s request"), service_name_locale);
5908 free(service_name_locale);
5909 return IE_ERROR;
5911 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5912 if(!isds_ns) {
5913 isds_log_message(context, _("Could not create ISDS name space"));
5914 xmlFreeNode(request);
5915 return IE_ERROR;
5917 xmlSetNs(request, isds_ns);
5919 INSERT_ELEMENT(node, request, "dbOwnerInfo");
5920 err = insert_DbOwnerInfo(context, box, node);
5921 if (err) goto leave;
5923 if (since) {
5924 err = tm2datestring(since, &string);
5925 if (err) {
5926 isds_log_message(context,
5927 _("Could not convert `since' argument to ISO date string"));
5928 goto leave;
5930 INSERT_STRING(request, "dbOwnerTerminationDate", string);
5931 zfree(string);
5934 err = insert_GExtApproval(context, approval, request);
5935 if (err) goto leave;
5938 /* Send it to server and process response */
5939 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
5940 service_name, &request, (xmlChar **) refnumber);
5942 leave:
5943 xmlFreeNode(request);
5944 free(string);
5945 #else /* not HAVE_LIBCURL */
5946 err = IE_NOTSUP;
5947 #endif
5948 return err;
5952 /* Remove given box permanently.
5953 * @context is session context
5954 * @box is box description to delete
5955 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
5956 * carry sane value.
5957 * @approval is optional external approval of box manipulation
5958 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5959 * NULL, if you don't care.*/
5960 isds_error isds_delete_box(struct isds_ctx *context,
5961 const struct isds_DbOwnerInfo *box, const struct tm *since,
5962 const struct isds_approval *approval, char **refnumber) {
5963 if (!context) return IE_INVALID_CONTEXT;
5964 zfree(context->long_message);
5965 if (!box || !since) return IE_INVAL;
5967 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBox",
5968 box, since, approval, refnumber);
5972 /* Undocumented function.
5973 * @context is session context
5974 * @box is box description to delete
5975 * @approval is optional external approval of box manipulation
5976 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5977 * NULL, if you don't care.*/
5978 isds_error isds_delete_box_promptly(struct isds_ctx *context,
5979 const struct isds_DbOwnerInfo *box,
5980 const struct isds_approval *approval, char **refnumber) {
5981 if (!context) return IE_INVALID_CONTEXT;
5982 zfree(context->long_message);
5983 if (!box) return IE_INVAL;
5985 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBoxPromptly",
5986 box, NULL, approval, refnumber);
5990 /* Update data about given box.
5991 * @context is session context
5992 * @old_box current box description
5993 * @new_box are updated data about @old_box
5994 * @approval is optional external approval of box manipulation
5995 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5996 * NULL, if you don't care.*/
5997 isds_error isds_UpdateDataBoxDescr(struct isds_ctx *context,
5998 const struct isds_DbOwnerInfo *old_box,
5999 const struct isds_DbOwnerInfo *new_box,
6000 const struct isds_approval *approval, char **refnumber) {
6001 isds_error err = IE_SUCCESS;
6002 #if HAVE_LIBCURL
6003 xmlNsPtr isds_ns = NULL;
6004 xmlNodePtr request = NULL;
6005 xmlNodePtr node;
6006 #endif
6009 if (!context) return IE_INVALID_CONTEXT;
6010 zfree(context->long_message);
6011 if (!old_box || !new_box) return IE_INVAL;
6014 #if HAVE_LIBCURL
6015 /* Build UpdateDataBoxDescr request */
6016 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxDescr");
6017 if (!request) {
6018 isds_log_message(context,
6019 _("Could build UpdateDataBoxDescr request"));
6020 return IE_ERROR;
6022 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6023 if(!isds_ns) {
6024 isds_log_message(context, _("Could not create ISDS name space"));
6025 xmlFreeNode(request);
6026 return IE_ERROR;
6028 xmlSetNs(request, isds_ns);
6030 INSERT_ELEMENT(node, request, "dbOldOwnerInfo");
6031 err = insert_DbOwnerInfo(context, old_box, node);
6032 if (err) goto leave;
6034 INSERT_ELEMENT(node, request, "dbNewOwnerInfo");
6035 err = insert_DbOwnerInfo(context, new_box, node);
6036 if (err) goto leave;
6038 err = insert_GExtApproval(context, approval, request);
6039 if (err) goto leave;
6042 /* Send it to server and process response */
6043 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6044 BAD_CAST "UpdateDataBoxDescr", &request, (xmlChar **) refnumber);
6046 leave:
6047 xmlFreeNode(request);
6048 #else /* not HAVE_LIBCURL */
6049 err = IE_NOTSUP;
6050 #endif
6052 return err;
6056 #if HAVE_LIBCURL
6057 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6058 * code
6059 * @context is session context
6060 * @service is SOAP service
6061 * @service_name is name of request in @service
6062 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6063 * @box_id is box ID of interest
6064 * @approval is optional external approval of box manipulation
6065 * @response is server SOAP body response as XML document
6066 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6067 * NULL, if you don't care.
6068 * @return error coded from lower layer, context message will be set up
6069 * appropriately. */
6070 static isds_error build_send_dbid_request_check_response(
6071 struct isds_ctx *context, const isds_service service,
6072 const xmlChar *service_name, const xmlChar *box_id_element,
6073 const xmlChar *box_id, const struct isds_approval *approval,
6074 xmlDocPtr *response, xmlChar **refnumber) {
6076 isds_error err = IE_SUCCESS;
6077 char *service_name_locale = NULL, *box_id_locale = NULL;
6078 xmlNodePtr request = NULL, node;
6079 xmlNsPtr isds_ns = NULL;
6081 if (!context) return IE_INVALID_CONTEXT;
6082 if (!service_name || !box_id) return IE_INVAL;
6083 if (!response) return IE_INVAL;
6085 /* Free output argument */
6086 xmlFreeDoc(*response); *response = NULL;
6088 /* Prepare strings */
6089 service_name_locale = _isds_utf82locale((char*)service_name);
6090 if (!service_name_locale) {
6091 err = IE_NOMEM;
6092 goto leave;
6094 box_id_locale = _isds_utf82locale((char*)box_id);
6095 if (!box_id_locale) {
6096 err = IE_NOMEM;
6097 goto leave;
6100 /* Build request */
6101 request = xmlNewNode(NULL, service_name);
6102 if (!request) {
6103 isds_printf_message(context,
6104 _("Could not build %s request for %s box"), service_name_locale,
6105 box_id_locale);
6106 err = IE_ERROR;
6107 goto leave;
6109 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6110 if(!isds_ns) {
6111 isds_log_message(context, _("Could not create ISDS name space"));
6112 err = IE_ERROR;
6113 goto leave;
6115 xmlSetNs(request, isds_ns);
6117 /* Add XSD:tIdDbInput children */
6118 if (NULL == box_id_element) box_id_element = BAD_CAST "dbID";
6119 INSERT_STRING(request, box_id_element, box_id);
6120 err = insert_GExtApproval(context, approval, request);
6121 if (err) goto leave;
6123 /* Send request and check response*/
6124 err = send_destroy_request_check_response(context,
6125 service, service_name, &request, response, refnumber);
6127 leave:
6128 free(service_name_locale);
6129 free(box_id_locale);
6130 xmlFreeNode(request);
6131 return err;
6133 #endif /* HAVE_LIBCURL */
6136 /* Get data about all users assigned to given box.
6137 * @context is session context
6138 * @box_id is box ID
6139 * @users is automatically reallocated list of struct isds_DbUserInfo */
6140 isds_error isds_GetDataBoxUsers(struct isds_ctx *context, const char *box_id,
6141 struct isds_list **users) {
6142 isds_error err = IE_SUCCESS;
6143 #if HAVE_LIBCURL
6144 xmlDocPtr response = NULL;
6145 xmlXPathContextPtr xpath_ctx = NULL;
6146 xmlXPathObjectPtr result = NULL;
6147 int i;
6148 struct isds_list *item, *prev_item = NULL;
6149 #endif
6151 if (!context) return IE_INVALID_CONTEXT;
6152 zfree(context->long_message);
6153 if (!users || !box_id) return IE_INVAL;
6154 isds_list_free(users);
6157 #if HAVE_LIBCURL
6158 /* Do request and check for success */
6159 err = build_send_dbid_request_check_response(context,
6160 SERVICE_DB_MANIPULATION, BAD_CAST "GetDataBoxUsers", NULL,
6161 BAD_CAST box_id, NULL, &response, NULL);
6162 if (err) goto leave;
6165 /* Extract data */
6166 /* Prepare structure */
6167 xpath_ctx = xmlXPathNewContext(response);
6168 if (!xpath_ctx) {
6169 err = IE_ERROR;
6170 goto leave;
6172 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6173 err = IE_ERROR;
6174 goto leave;
6177 /* Set context node */
6178 result = xmlXPathEvalExpression(BAD_CAST
6179 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6180 xpath_ctx);
6181 if (!result) {
6182 err = IE_ERROR;
6183 goto leave;
6185 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6186 /* Iterate over all users */
6187 for (i = 0; i < result->nodesetval->nodeNr; i++) {
6189 /* Prepare structure */
6190 item = calloc(1, sizeof(*item));
6191 if (!item) {
6192 err = IE_NOMEM;
6193 goto leave;
6195 item->destructor = (void(*)(void**))isds_DbUserInfo_free;
6196 if (i == 0) *users = item;
6197 else prev_item->next = item;
6198 prev_item = item;
6200 /* Extract it */
6201 xpath_ctx->node = result->nodesetval->nodeTab[i];
6202 err = extract_DbUserInfo(context,
6203 (struct isds_DbUserInfo **) (&item->data), xpath_ctx);
6204 if (err) goto leave;
6208 leave:
6209 if (err) {
6210 isds_list_free(users);
6213 xmlXPathFreeObject(result);
6214 xmlXPathFreeContext(xpath_ctx);
6215 xmlFreeDoc(response);
6217 if (!err)
6218 isds_log(ILF_ISDS, ILL_DEBUG,
6219 _("GetDataBoxUsers request processed by server "
6220 "successfully.\n"));
6221 #else /* not HAVE_LIBCURL */
6222 err = IE_NOTSUP;
6223 #endif
6225 return err;
6229 /* Update data about user assigned to given box.
6230 * @context is session context
6231 * @box is box identification
6232 * @old_user identifies user to update
6233 * @new_user are updated data about @old_user
6234 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6235 * NULL, if you don't care.*/
6236 isds_error isds_UpdateDataBoxUser(struct isds_ctx *context,
6237 const struct isds_DbOwnerInfo *box,
6238 const struct isds_DbUserInfo *old_user,
6239 const struct isds_DbUserInfo *new_user,
6240 char **refnumber) {
6241 isds_error err = IE_SUCCESS;
6242 #if HAVE_LIBCURL
6243 xmlNsPtr isds_ns = NULL;
6244 xmlNodePtr request = NULL;
6245 xmlNodePtr node;
6246 #endif
6249 if (!context) return IE_INVALID_CONTEXT;
6250 zfree(context->long_message);
6251 if (!box || !old_user || !new_user) return IE_INVAL;
6254 #if HAVE_LIBCURL
6255 /* Build UpdateDataBoxUser request */
6256 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxUser");
6257 if (!request) {
6258 isds_log_message(context,
6259 _("Could build UpdateDataBoxUser request"));
6260 return IE_ERROR;
6262 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6263 if(!isds_ns) {
6264 isds_log_message(context, _("Could not create ISDS name space"));
6265 xmlFreeNode(request);
6266 return IE_ERROR;
6268 xmlSetNs(request, isds_ns);
6270 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6271 err = insert_DbOwnerInfo(context, box, node);
6272 if (err) goto leave;
6274 INSERT_ELEMENT(node, request, "dbOldUserInfo");
6275 err = insert_DbUserInfo(context, old_user, node);
6276 if (err) goto leave;
6278 INSERT_ELEMENT(node, request, "dbNewUserInfo");
6279 err = insert_DbUserInfo(context, new_user, node);
6280 if (err) goto leave;
6282 /* Send it to server and process response */
6283 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6284 BAD_CAST "UpdateDataBoxUser", &request, (xmlChar **) refnumber);
6286 leave:
6287 xmlFreeNode(request);
6288 #else /* not HAVE_LIBCURL */
6289 err = IE_NOTSUP;
6290 #endif
6292 return err;
6296 /* Undocumented function.
6297 * @context is session context
6298 * @box_id is UTF-8 encoded box identifier
6299 * @token is UTF-8 encoded temporary password
6300 * @user_id outputs UTF-8 encoded reallocated user identifier
6301 * @password outpus UTF-8 encoded reallocated user password
6302 * Output arguments will be nulled in case of error */
6303 isds_error isds_activate(struct isds_ctx *context,
6304 const char *box_id, const char *token,
6305 char **user_id, char **password) {
6306 isds_error err = IE_SUCCESS;
6307 #if HAVE_LIBCURL
6308 xmlNsPtr isds_ns = NULL;
6309 xmlNodePtr request = NULL, node;
6310 xmlDocPtr response = NULL;
6311 xmlXPathContextPtr xpath_ctx = NULL;
6312 xmlXPathObjectPtr result = NULL;
6313 #endif
6316 if (!context) return IE_INVALID_CONTEXT;
6317 zfree(context->long_message);
6319 if (user_id) zfree(*user_id);
6320 if (password) zfree(*password);
6322 if (!box_id || !token || !user_id || !password) return IE_INVAL;
6325 #if HAVE_LIBCURL
6326 /* Build Activate request */
6327 request = xmlNewNode(NULL, BAD_CAST "Activate");
6328 if (!request) {
6329 isds_log_message(context, _("Could build Activate request"));
6330 return IE_ERROR;
6332 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6333 if(!isds_ns) {
6334 isds_log_message(context, _("Could not create ISDS name space"));
6335 xmlFreeNode(request);
6336 return IE_ERROR;
6338 xmlSetNs(request, isds_ns);
6340 INSERT_STRING(request, "dbAccessDataId", token);
6341 CHECK_FOR_STRING_LENGTH(box_id, 7, 7, "dbID");
6342 INSERT_STRING(request, "dbID", box_id);
6345 /* Send request and check response*/
6346 err = send_destroy_request_check_response(context,
6347 SERVICE_DB_MANIPULATION, BAD_CAST "Activate", &request,
6348 &response, NULL);
6349 if (err) goto leave;
6352 /* Extract data */
6353 xpath_ctx = xmlXPathNewContext(response);
6354 if (!xpath_ctx) {
6355 err = IE_ERROR;
6356 goto leave;
6358 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6359 err = IE_ERROR;
6360 goto leave;
6362 result = xmlXPathEvalExpression(BAD_CAST "/isds:ActivateResponse",
6363 xpath_ctx);
6364 if (!result) {
6365 err = IE_ERROR;
6366 goto leave;
6368 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6369 isds_log_message(context, _("Missing ActivateResponse element"));
6370 err = IE_ISDS;
6371 goto leave;
6373 if (result->nodesetval->nodeNr > 1) {
6374 isds_log_message(context, _("Multiple ActivateResponse element"));
6375 err = IE_ISDS;
6376 goto leave;
6378 xpath_ctx->node = result->nodesetval->nodeTab[0];
6379 xmlXPathFreeObject(result); result = NULL;
6381 EXTRACT_STRING("isds:userId", *user_id);
6382 if (!*user_id)
6383 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6384 "but did not return `userId' element.\n"));
6386 EXTRACT_STRING("isds:password", *password);
6387 if (!*password)
6388 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6389 "but did not return `password' element.\n"));
6391 leave:
6392 xmlXPathFreeObject(result);
6393 xmlXPathFreeContext(xpath_ctx);
6394 xmlFreeDoc(response);
6395 xmlFreeNode(request);
6397 if (!err)
6398 isds_log(ILF_ISDS, ILL_DEBUG,
6399 _("Activate request processed by server successfully.\n"));
6400 #else /* not HAVE_LIBCURL */
6401 err = IE_NOTSUP;
6402 #endif
6404 return err;
6408 /* Reset credentials of user assigned to given box.
6409 * @context is session context
6410 * @box is box identification
6411 * @user identifies user to reset password
6412 * @fee_paid is true if fee has been paid, false otherwise
6413 * @approval is optional external approval of box manipulation
6414 * @credentials_delivery is NULL if new password should be delivered off-line
6415 * to the user. It is valid pointer if user should obtain new password on-line
6416 * on dedicated web server. Then input @credentials_delivery.email value is
6417 * user's e-mail address user must provide to dedicated web server together
6418 * with @credentials_delivery.token. The output reallocated token user needs
6419 * to use to authorize on the web server to view his new password. Output
6420 * reallocated @credentials_delivery.new_user_name is user's log-in name that
6421 * ISDS changed up on this call. (No reason why server could change the name
6422 * is known now.)
6423 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6424 * NULL, if you don't care.*/
6425 isds_error isds_reset_password(struct isds_ctx *context,
6426 const struct isds_DbOwnerInfo *box,
6427 const struct isds_DbUserInfo *user,
6428 const _Bool fee_paid, const struct isds_approval *approval,
6429 struct isds_credentials_delivery *credentials_delivery,
6430 char **refnumber) {
6431 isds_error err = IE_SUCCESS;
6432 #if HAVE_LIBCURL
6433 xmlNsPtr isds_ns = NULL;
6434 xmlNodePtr request = NULL, node;
6435 xmlDocPtr response = NULL;
6436 #endif
6439 if (!context) return IE_INVALID_CONTEXT;
6440 zfree(context->long_message);
6442 if (credentials_delivery) {
6443 zfree(credentials_delivery->token);
6444 zfree(credentials_delivery->new_user_name);
6446 if (!box || !user) return IE_INVAL;
6449 #if HAVE_LIBCURL
6450 /* Build NewAccessData request */
6451 request = xmlNewNode(NULL, BAD_CAST "NewAccessData");
6452 if (!request) {
6453 isds_log_message(context,
6454 _("Could build NewAccessData request"));
6455 return IE_ERROR;
6457 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6458 if(!isds_ns) {
6459 isds_log_message(context, _("Could not create ISDS name space"));
6460 xmlFreeNode(request);
6461 return IE_ERROR;
6463 xmlSetNs(request, isds_ns);
6465 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6466 err = insert_DbOwnerInfo(context, box, node);
6467 if (err) goto leave;
6469 INSERT_ELEMENT(node, request, "dbUserInfo");
6470 err = insert_DbUserInfo(context, user, node);
6471 if (err) goto leave;
6473 INSERT_SCALAR_BOOLEAN(request, "dbFeePaid", fee_paid);
6475 err = insert_credentials_delivery(context, credentials_delivery, request);
6476 if (err) goto leave;
6478 err = insert_GExtApproval(context, approval, request);
6479 if (err) goto leave;
6481 /* Send request and check response*/
6482 err = send_destroy_request_check_response(context,
6483 SERVICE_DB_MANIPULATION, BAD_CAST "NewAccessData", &request,
6484 &response, (xmlChar **) refnumber);
6485 if (err) goto leave;
6488 /* Extract optional token */
6489 err = extract_credentials_delivery(context, credentials_delivery,
6490 response, "NewAccessData");
6492 leave:
6493 xmlFreeDoc(response);
6494 xmlFreeNode(request);
6496 if (!err)
6497 isds_log(ILF_ISDS, ILL_DEBUG,
6498 _("NewAccessData request processed by server "
6499 "successfully.\n"));
6500 #else /* not HAVE_LIBCURL */
6501 err = IE_NOTSUP;
6502 #endif
6504 return err;
6508 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
6509 * code, destroy response and log success.
6510 * @context is ISDS session context.
6511 * @service_name is name of SERVICE_DB_MANIPULATION service
6512 * @box is box identification
6513 * @user identifies user to remove
6514 * @credentials_delivery is NULL if new user's password should be delivered
6515 * off-line to the user. It is valid pointer if user should obtain new
6516 * password on-line on dedicated web server. Then input
6517 * @credentials_delivery.email value is user's e-mail address user must
6518 * provide to dedicated web server together with @credentials_delivery.token.
6519 * The output reallocated token user needs to use to authorize on the web
6520 * server to view his new password. Output reallocated
6521 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6522 * assingned or changed up on this call.
6523 * @approval is optional external approval of box manipulation
6524 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6525 * NULL, if you don't care. */
6526 static isds_error build_send_manipulationboxuser_request_check_drop_response(
6527 struct isds_ctx *context, const xmlChar *service_name,
6528 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6529 struct isds_credentials_delivery *credentials_delivery,
6530 const struct isds_approval *approval, xmlChar **refnumber) {
6531 isds_error err = IE_SUCCESS;
6532 #if HAVE_LIBCURL
6533 xmlNsPtr isds_ns = NULL;
6534 xmlNodePtr request = NULL, node;
6535 xmlDocPtr response = NULL;
6536 #endif
6539 if (!context) return IE_INVALID_CONTEXT;
6540 zfree(context->long_message);
6541 if (credentials_delivery) {
6542 zfree(credentials_delivery->token);
6543 zfree(credentials_delivery->new_user_name);
6545 if (!service_name || service_name[0] == '\0' || !box || !user)
6546 return IE_INVAL;
6549 #if HAVE_LIBCURL
6550 /* Build NewAccessData or similar request */
6551 request = xmlNewNode(NULL, service_name);
6552 if (!request) {
6553 char *service_name_locale = _isds_utf82locale((char *) service_name);
6554 isds_printf_message(context, _("Could not build %s request"),
6555 service_name_locale);
6556 free(service_name_locale);
6557 return IE_ERROR;
6559 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6560 if(!isds_ns) {
6561 isds_log_message(context, _("Could not create ISDS name space"));
6562 xmlFreeNode(request);
6563 return IE_ERROR;
6565 xmlSetNs(request, isds_ns);
6567 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6568 err = insert_DbOwnerInfo(context, box, node);
6569 if (err) goto leave;
6571 INSERT_ELEMENT(node, request, "dbUserInfo");
6572 err = insert_DbUserInfo(context, user, node);
6573 if (err) goto leave;
6575 err = insert_credentials_delivery(context, credentials_delivery, request);
6576 if (err) goto leave;
6578 err = insert_GExtApproval(context, approval, request);
6579 if (err) goto leave;
6582 /* Send request and check response*/
6583 err = send_destroy_request_check_response(context,
6584 SERVICE_DB_MANIPULATION, service_name, &request, &response, refnumber);
6586 xmlFreeNode(request);
6587 request = NULL;
6589 /* Pick up credentials_delivery if requested */
6590 err = extract_credentials_delivery(context, credentials_delivery, response,
6591 (char *)service_name);
6593 leave:
6594 xmlFreeDoc(response);
6595 if (request) xmlFreeNode(request);
6597 if (!err) {
6598 char *service_name_locale = _isds_utf82locale((char *) service_name);
6599 isds_log(ILF_ISDS, ILL_DEBUG,
6600 _("%s request processed by server successfully.\n"),
6601 service_name_locale);
6602 free(service_name_locale);
6604 #else /* not HAVE_LIBCURL */
6605 err = IE_NOTSUP;
6606 #endif
6608 return err;
6612 /* Assign new user to given box.
6613 * @context is session context
6614 * @box is box identification
6615 * @user defines new user to add
6616 * @credentials_delivery is NULL if new user's password should be delivered
6617 * off-line to the user. It is valid pointer if user should obtain new
6618 * password on-line on dedicated web server. Then input
6619 * @credentials_delivery.email value is user's e-mail address user must
6620 * provide to dedicated web server together with @credentials_delivery.token.
6621 * The output reallocated token user needs to use to authorize on the web
6622 * server to view his new password. Output reallocated
6623 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6624 * assingned up on this call.
6625 * @approval is optional external approval of box manipulation
6626 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6627 * NULL, if you don't care.*/
6628 isds_error isds_add_user(struct isds_ctx *context,
6629 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6630 struct isds_credentials_delivery *credentials_delivery,
6631 const struct isds_approval *approval, char **refnumber) {
6632 return build_send_manipulationboxuser_request_check_drop_response(context,
6633 BAD_CAST "AddDataBoxUser", box, user, credentials_delivery,
6634 approval, (xmlChar **) refnumber);
6638 /* Remove user assigned to given box.
6639 * @context is session context
6640 * @box is box identification
6641 * @user identifies user to remove
6642 * @approval is optional external approval of box manipulation
6643 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6644 * NULL, if you don't care.*/
6645 isds_error isds_delete_user(struct isds_ctx *context,
6646 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6647 const struct isds_approval *approval, char **refnumber) {
6648 return build_send_manipulationboxuser_request_check_drop_response(context,
6649 BAD_CAST "DeleteDataBoxUser", box, user, NULL, approval,
6650 (xmlChar **) refnumber);
6654 /* Get list of boxes in ZIP archive.
6655 * @context is session context
6656 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
6657 * System recognizes following values currently: ALL (all boxes), UPG
6658 * (effectively OVM boxes), OVM (OVM gross type boxes), OPN (boxes allowing
6659 * receiving commercial messages). This argument is a string because
6660 * specification states new values can appear in the future. Not all list
6661 * types are available to all users.
6662 * @buffer is automatically reallocated memory to store the list of boxes. The
6663 * list is zipped CSV file.
6664 * @buffer_length is size of @buffer data in bytes.
6665 * In case of error @buffer will be freed and @buffer_length will be
6666 * undefined.*/
6667 isds_error isds_get_box_list_archive(struct isds_ctx *context,
6668 const char *list_identifier, void **buffer, size_t *buffer_length) {
6669 isds_error err = IE_SUCCESS;
6670 #if HAVE_LIBCURL
6671 xmlNsPtr isds_ns = NULL;
6672 xmlNodePtr request = NULL, node;
6673 xmlDocPtr response = NULL;
6674 xmlXPathContextPtr xpath_ctx = NULL;
6675 xmlXPathObjectPtr result = NULL;
6676 char *string = NULL;
6677 #endif
6680 if (!context) return IE_INVALID_CONTEXT;
6681 zfree(context->long_message);
6682 if (buffer) zfree(*buffer);
6683 if (!buffer || !buffer_length) return IE_INVAL;
6686 #if HAVE_LIBCURL
6687 /* Check if connection is established
6688 * TODO: This check should be done downstairs. */
6689 if (!context->curl) return IE_CONNECTION_CLOSED;
6692 /* Build AuthenticateMessage request */
6693 request = xmlNewNode(NULL, BAD_CAST "GetDataBoxList");
6694 if (!request) {
6695 isds_log_message(context,
6696 _("Could not build GetDataBoxList request"));
6697 return IE_ERROR;
6699 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6700 if(!isds_ns) {
6701 isds_log_message(context, _("Could not create ISDS name space"));
6702 xmlFreeNode(request);
6703 return IE_ERROR;
6705 xmlSetNs(request, isds_ns);
6706 INSERT_STRING(request, "dblType", list_identifier);
6708 /* Send request to server and process response */
6709 err = send_destroy_request_check_response(context,
6710 SERVICE_DB_SEARCH, BAD_CAST "GetDataBoxList", &request,
6711 &response, NULL);
6712 if (err) goto leave;
6715 /* Extract Base-64 encoded ZIP file */
6716 xpath_ctx = xmlXPathNewContext(response);
6717 if (!xpath_ctx) {
6718 err = IE_ERROR;
6719 goto leave;
6721 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6722 err = IE_ERROR;
6723 goto leave;
6725 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string);
6727 /* Decode non-empty archive */
6728 if (string && string[0] != '\0') {
6729 *buffer_length = _isds_b64decode(string, buffer);
6730 if (*buffer_length == (size_t) -1) {
6731 isds_printf_message(context,
6732 _("Error while Base64-decoding box list archive"));
6733 err = IE_ERROR;
6734 goto leave;
6739 leave:
6740 free(string);
6741 xmlXPathFreeObject(result);
6742 xmlXPathFreeContext(xpath_ctx);
6743 xmlFreeDoc(response);
6744 xmlFreeNode(request);
6746 if (!err) {
6747 isds_log(ILF_ISDS, ILL_DEBUG, _("GetDataBoxList request "
6748 "processed by server successfully.\n"));
6750 #else /* not HAVE_LIBCURL */
6751 err = IE_NOTSUP;
6752 #endif
6754 return err;
6758 /* Find boxes suiting given criteria.
6759 * @criteria is filter. You should fill in at least some members.
6760 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
6761 * possibly empty. Input NULL or valid old structure.
6762 * @return:
6763 * IE_SUCCESS if search succeeded, @boxes contains useful data
6764 * IE_NOEXIST if no such box exists, @boxes will be NULL
6765 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
6766 * contains still valid data
6767 * other code if something bad happens. @boxes will be NULL. */
6768 isds_error isds_FindDataBox(struct isds_ctx *context,
6769 const struct isds_DbOwnerInfo *criteria,
6770 struct isds_list **boxes) {
6771 isds_error err = IE_SUCCESS;
6772 #if HAVE_LIBCURL
6773 _Bool truncated = 0;
6774 xmlNsPtr isds_ns = NULL;
6775 xmlNodePtr request = NULL;
6776 xmlDocPtr response = NULL;
6777 xmlChar *code = NULL, *message = NULL;
6778 xmlNodePtr db_owner_info;
6779 xmlXPathContextPtr xpath_ctx = NULL;
6780 xmlXPathObjectPtr result = NULL;
6781 xmlChar *string = NULL;
6782 #endif
6785 if (!context) return IE_INVALID_CONTEXT;
6786 zfree(context->long_message);
6787 if (!boxes) return IE_INVAL;
6788 isds_list_free(boxes);
6790 if (!criteria) {
6791 return IE_INVAL;
6794 #if HAVE_LIBCURL
6795 /* Check if connection is established
6796 * TODO: This check should be done downstairs. */
6797 if (!context->curl) return IE_CONNECTION_CLOSED;
6800 /* Build FindDataBox request */
6801 request = xmlNewNode(NULL, BAD_CAST "FindDataBox");
6802 if (!request) {
6803 isds_log_message(context,
6804 _("Could build FindDataBox request"));
6805 return IE_ERROR;
6807 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6808 if(!isds_ns) {
6809 isds_log_message(context, _("Could not create ISDS name space"));
6810 xmlFreeNode(request);
6811 return IE_ERROR;
6813 xmlSetNs(request, isds_ns);
6814 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
6815 if (!db_owner_info) {
6816 isds_log_message(context, _("Could not add dbOwnerInfo child to "
6817 "FindDataBox element"));
6818 xmlFreeNode(request);
6819 return IE_ERROR;
6822 err = insert_DbOwnerInfo(context, criteria, db_owner_info);
6823 if (err) goto leave;
6826 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending FindDataBox request to ISDS\n"));
6828 /* Sent request */
6829 err = isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
6831 /* Destroy request */
6832 xmlFreeNode(request); request = NULL;
6834 if (err) {
6835 isds_log(ILF_ISDS, ILL_DEBUG,
6836 _("Processing ISDS response on FindDataBox "
6837 "request failed\n"));
6838 goto leave;
6841 /* Check for response status */
6842 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
6843 &code, &message, NULL);
6844 if (err) {
6845 isds_log(ILF_ISDS, ILL_DEBUG,
6846 _("ISDS response on FindDataBox request is missing status\n"));
6847 goto leave;
6850 /* Request processed, but nothing found */
6851 if (!xmlStrcmp(code, BAD_CAST "0002") ||
6852 !xmlStrcmp(code, BAD_CAST "5001")) {
6853 char *code_locale = _isds_utf82locale((char*)code);
6854 char *message_locale = _isds_utf82locale((char*)message);
6855 isds_log(ILF_ISDS, ILL_DEBUG,
6856 _("Server did not found any box on FindDataBox request "
6857 "(code=%s, message=%s)\n"), code_locale, message_locale);
6858 isds_log_message(context, message_locale);
6859 free(code_locale);
6860 free(message_locale);
6861 err = IE_NOEXIST;
6862 goto leave;
6865 /* Warning, not a error */
6866 if (!xmlStrcmp(code, BAD_CAST "0003")) {
6867 char *code_locale = _isds_utf82locale((char*)code);
6868 char *message_locale = _isds_utf82locale((char*)message);
6869 isds_log(ILF_ISDS, ILL_DEBUG,
6870 _("Server truncated response on FindDataBox request "
6871 "(code=%s, message=%s)\n"), code_locale, message_locale);
6872 isds_log_message(context, message_locale);
6873 free(code_locale);
6874 free(message_locale);
6875 truncated = 1;
6878 /* Other error */
6879 else if (xmlStrcmp(code, BAD_CAST "0000")) {
6880 char *code_locale = _isds_utf82locale((char*)code);
6881 char *message_locale = _isds_utf82locale((char*)message);
6882 isds_log(ILF_ISDS, ILL_DEBUG,
6883 _("Server refused FindDataBox request "
6884 "(code=%s, message=%s)\n"), code_locale, message_locale);
6885 isds_log_message(context, message_locale);
6886 free(code_locale);
6887 free(message_locale);
6888 err = IE_ISDS;
6889 goto leave;
6892 xpath_ctx = xmlXPathNewContext(response);
6893 if (!xpath_ctx) {
6894 err = IE_ERROR;
6895 goto leave;
6897 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6898 err = IE_ERROR;
6899 goto leave;
6902 /* Extract boxes if they present */
6903 result = xmlXPathEvalExpression(BAD_CAST
6904 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
6905 xpath_ctx);
6906 if (!result) {
6907 err = IE_ERROR;
6908 goto leave;
6910 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6911 struct isds_list *item, *prev_item = NULL;
6912 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
6913 item = calloc(1, sizeof(*item));
6914 if (!item) {
6915 err = IE_NOMEM;
6916 goto leave;
6919 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
6920 if (i == 0) *boxes = item;
6921 else prev_item->next = item;
6922 prev_item = item;
6924 xpath_ctx->node = result->nodesetval->nodeTab[i];
6925 err = extract_DbOwnerInfo(context,
6926 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
6927 if (err) goto leave;
6931 leave:
6932 if (err) {
6933 isds_list_free(boxes);
6934 } else {
6935 if (truncated) err = IE_2BIG;
6938 free(string);
6939 xmlFreeNode(request);
6940 xmlXPathFreeObject(result);
6941 xmlXPathFreeContext(xpath_ctx);
6943 free(code);
6944 free(message);
6945 xmlFreeDoc(response);
6947 if (!err)
6948 isds_log(ILF_ISDS, ILL_DEBUG,
6949 _("FindDataBox request processed by server successfully.\n"));
6950 #else /* not HAVE_LIBCURL */
6951 err = IE_NOTSUP;
6952 #endif
6954 return err;
6958 /* Get status of a box.
6959 * @context is ISDS session context.
6960 * @box_id is UTF-8 encoded box identifier as zero terminated string
6961 * @box_status is return value of box status.
6962 * @return:
6963 * IE_SUCCESS if box has been found and its status retrieved
6964 * IE_NOEXIST if box is not known to ISDS server
6965 * or other appropriate error.
6966 * You can use isds_DbState to enumerate box status. However out of enum
6967 * range value can be returned too. This is feature because ISDS
6968 * specification leaves the set of values open.
6969 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
6970 * the box has been deleted, but ISDS still lists its former existence. */
6971 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
6972 long int *box_status) {
6973 isds_error err = IE_SUCCESS;
6974 #if HAVE_LIBCURL
6975 xmlNsPtr isds_ns = NULL;
6976 xmlNodePtr request = NULL, db_id;
6977 xmlDocPtr response = NULL;
6978 xmlChar *code = NULL, *message = NULL;
6979 xmlXPathContextPtr xpath_ctx = NULL;
6980 xmlXPathObjectPtr result = NULL;
6981 xmlChar *string = NULL;
6982 #endif
6984 if (!context) return IE_INVALID_CONTEXT;
6985 zfree(context->long_message);
6986 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
6988 #if HAVE_LIBCURL
6989 /* Check if connection is established
6990 * TODO: This check should be done downstairs. */
6991 if (!context->curl) return IE_CONNECTION_CLOSED;
6994 /* Build CheckDataBox request */
6995 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
6996 if (!request) {
6997 isds_log_message(context,
6998 _("Could build CheckDataBox request"));
6999 return IE_ERROR;
7001 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7002 if(!isds_ns) {
7003 isds_log_message(context, _("Could not create ISDS name space"));
7004 xmlFreeNode(request);
7005 return IE_ERROR;
7007 xmlSetNs(request, isds_ns);
7008 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
7009 if (!db_id) {
7010 isds_log_message(context, _("Could not add dbID child to "
7011 "CheckDataBox element"));
7012 xmlFreeNode(request);
7013 return IE_ERROR;
7017 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CheckDataBox request to ISDS\n"));
7019 /* Sent request */
7020 err = isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
7022 /* Destroy request */
7023 xmlFreeNode(request);
7025 if (err) {
7026 isds_log(ILF_ISDS, ILL_DEBUG,
7027 _("Processing ISDS response on CheckDataBox "
7028 "request failed\n"));
7029 goto leave;
7032 /* Check for response status */
7033 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
7034 &code, &message, NULL);
7035 if (err) {
7036 isds_log(ILF_ISDS, ILL_DEBUG,
7037 _("ISDS response on CheckDataBox request is missing status\n"));
7038 goto leave;
7041 /* Request processed, but nothing found */
7042 if (!xmlStrcmp(code, BAD_CAST "5001")) {
7043 char *box_id_locale = _isds_utf82locale((char*)box_id);
7044 char *code_locale = _isds_utf82locale((char*)code);
7045 char *message_locale = _isds_utf82locale((char*)message);
7046 isds_log(ILF_ISDS, ILL_DEBUG,
7047 _("Server did not found box %s on CheckDataBox request "
7048 "(code=%s, message=%s)\n"),
7049 box_id_locale, code_locale, message_locale);
7050 isds_log_message(context, message_locale);
7051 free(box_id_locale);
7052 free(code_locale);
7053 free(message_locale);
7054 err = IE_NOEXIST;
7055 goto leave;
7058 /* Other error */
7059 else if (xmlStrcmp(code, BAD_CAST "0000")) {
7060 char *code_locale = _isds_utf82locale((char*)code);
7061 char *message_locale = _isds_utf82locale((char*)message);
7062 isds_log(ILF_ISDS, ILL_DEBUG,
7063 _("Server refused CheckDataBox request "
7064 "(code=%s, message=%s)\n"), code_locale, message_locale);
7065 isds_log_message(context, message_locale);
7066 free(code_locale);
7067 free(message_locale);
7068 err = IE_ISDS;
7069 goto leave;
7072 /* Extract data */
7073 xpath_ctx = xmlXPathNewContext(response);
7074 if (!xpath_ctx) {
7075 err = IE_ERROR;
7076 goto leave;
7078 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7079 err = IE_ERROR;
7080 goto leave;
7082 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
7083 xpath_ctx);
7084 if (!result) {
7085 err = IE_ERROR;
7086 goto leave;
7088 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7089 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
7090 err = IE_ISDS;
7091 goto leave;
7093 if (result->nodesetval->nodeNr > 1) {
7094 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
7095 err = IE_ISDS;
7096 goto leave;
7098 xpath_ctx->node = result->nodesetval->nodeTab[0];
7099 xmlXPathFreeObject(result); result = NULL;
7101 EXTRACT_LONGINT("isds:dbState", box_status, 1);
7104 leave:
7105 free(string);
7106 xmlXPathFreeObject(result);
7107 xmlXPathFreeContext(xpath_ctx);
7109 free(code);
7110 free(message);
7111 xmlFreeDoc(response);
7113 if (!err)
7114 isds_log(ILF_ISDS, ILL_DEBUG,
7115 _("CheckDataBox request processed by server successfully.\n"));
7116 #else /* not HAVE_LIBCURL */
7117 err = IE_NOTSUP;
7118 #endif
7120 return err;
7124 /* Get list of permissions to send commercial messages.
7125 * @context is ISDS session context.
7126 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
7127 * @permissions is a reallocated list of permissions (struct
7128 * isds_commercial_permission*) to send commercial messages from @box_id. The
7129 * order of permissions is significant as the server applies the permissions
7130 * and associated pre-paid credits in the order. Empty list means no
7131 * permission.
7132 * @return:
7133 * IE_SUCCESS if the list has been obtained correctly,
7134 * or other appropriate error. */
7135 isds_error isds_get_commercial_permissions(struct isds_ctx *context,
7136 const char *box_id, struct isds_list **permissions) {
7137 isds_error err = IE_SUCCESS;
7138 #if HAVE_LIBCURL
7139 xmlDocPtr response = NULL;
7140 xmlXPathContextPtr xpath_ctx = NULL;
7141 xmlXPathObjectPtr result = NULL;
7142 #endif
7144 if (!context) return IE_INVALID_CONTEXT;
7145 zfree(context->long_message);
7146 if (NULL == permissions) return IE_INVAL;
7147 isds_list_free(permissions);
7148 if (NULL == box_id) return IE_INVAL;
7150 #if HAVE_LIBCURL
7151 /* Check if connection is established */
7152 if (!context->curl) return IE_CONNECTION_CLOSED;
7154 /* Do request and check for success */
7155 err = build_send_dbid_request_check_response(context,
7156 SERVICE_DB_SEARCH, BAD_CAST "PDZInfo", BAD_CAST "PDZSender",
7157 BAD_CAST box_id, NULL, &response, NULL);
7158 if (!err) {
7159 isds_log(ILF_ISDS, ILL_DEBUG,
7160 _("PDZInfo request processed by server successfully.\n"));
7163 /* Extract data */
7164 /* Prepare structure */
7165 xpath_ctx = xmlXPathNewContext(response);
7166 if (!xpath_ctx) {
7167 err = IE_ERROR;
7168 goto leave;
7170 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7171 err = IE_ERROR;
7172 goto leave;
7175 /* Set context node */
7176 result = xmlXPathEvalExpression(BAD_CAST
7177 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
7178 xpath_ctx);
7179 if (!result) {
7180 err = IE_ERROR;
7181 goto leave;
7183 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7184 /* Iterate over all permission records */
7185 for (long unsigned int i = 0; i < result->nodesetval->nodeNr; i++) {
7186 struct isds_list *item, *prev_item = NULL;
7188 /* Prepare structure */
7189 item = calloc(1, sizeof(*item));
7190 if (!item) {
7191 err = IE_NOMEM;
7192 goto leave;
7194 item->destructor = (void(*)(void**))isds_commercial_permission_free;
7195 if (i == 0) *permissions = item;
7196 else prev_item->next = item;
7197 prev_item = item;
7199 /* Extract it */
7200 xpath_ctx->node = result->nodesetval->nodeTab[i];
7201 err = extract_DbPDZRecord(context,
7202 (struct isds_commercial_permission **) (&item->data),
7203 xpath_ctx);
7204 if (err) goto leave;
7208 leave:
7209 if (err) {
7210 isds_list_free(permissions);
7213 xmlXPathFreeObject(result);
7214 xmlXPathFreeContext(xpath_ctx);
7215 xmlFreeDoc(response);
7217 #else /* not HAVE_LIBCURL */
7218 err = IE_NOTSUP;
7219 #endif
7221 return err;
7225 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
7226 * code, destroy response and log success.
7227 * @context is ISDS session context.
7228 * @service_name is name of SERVICE_DB_MANIPULATION service
7229 * @box_id is UTF-8 encoded box identifier as zero terminated string
7230 * @approval is optional external approval of box manipulation
7231 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7232 * NULL, if you don't care. */
7233 static isds_error build_send_manipulationdbid_request_check_drop_response(
7234 struct isds_ctx *context, const xmlChar *service_name,
7235 const xmlChar *box_id, const struct isds_approval *approval,
7236 xmlChar **refnumber) {
7237 isds_error err = IE_SUCCESS;
7238 #if HAVE_LIBCURL
7239 xmlDocPtr response = NULL;
7240 #endif
7242 if (!context) return IE_INVALID_CONTEXT;
7243 zfree(context->long_message);
7244 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
7246 #if HAVE_LIBCURL
7247 /* Check if connection is established */
7248 if (!context->curl) return IE_CONNECTION_CLOSED;
7250 /* Do request and check for success */
7251 err = build_send_dbid_request_check_response(context,
7252 SERVICE_DB_MANIPULATION, service_name, NULL, box_id, approval,
7253 &response, refnumber);
7254 xmlFreeDoc(response);
7256 if (!err) {
7257 char *service_name_locale = _isds_utf82locale((char *) service_name);
7258 isds_log(ILF_ISDS, ILL_DEBUG,
7259 _("%s request processed by server successfully.\n"),
7260 service_name_locale);
7261 free(service_name_locale);
7263 #else /* not HAVE_LIBCURL */
7264 err = IE_NOTSUP;
7265 #endif
7267 return err;
7271 /* Switch box into state where box can receive commercial messages (off by
7272 * default)
7273 * @context is ISDS session context.
7274 * @box_id is UTF-8 encoded box identifier as zero terminated string
7275 * @allow is true for enable, false for disable commercial messages income
7276 * @approval is optional external approval of box manipulation
7277 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7278 * NULL, if you don't care. */
7279 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
7280 const char *box_id, const _Bool allow,
7281 const struct isds_approval *approval, char **refnumber) {
7282 return build_send_manipulationdbid_request_check_drop_response(context,
7283 (allow) ? BAD_CAST "SetOpenAddressing" :
7284 BAD_CAST "ClearOpenAddressing",
7285 BAD_CAST box_id, approval, (xmlChar **) refnumber);
7289 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
7290 * message acceptance). This is just a box permission. Sender must apply
7291 * such role by sending each message.
7292 * @context is ISDS session context.
7293 * @box_id is UTF-8 encoded box identifier as zero terminated string
7294 * @allow is true for enable, false for disable OVM role permission
7295 * @approval is optional external approval of box manipulation
7296 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7297 * NULL, if you don't care. */
7298 isds_error isds_switch_effective_ovm(struct isds_ctx *context,
7299 const char *box_id, const _Bool allow,
7300 const struct isds_approval *approval, char **refnumber) {
7301 return build_send_manipulationdbid_request_check_drop_response(context,
7302 (allow) ? BAD_CAST "SetEffectiveOVM" :
7303 BAD_CAST "ClearEffectiveOVM",
7304 BAD_CAST box_id, approval, (xmlChar **) refnumber);
7308 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
7309 * code, destroy response and log success.
7310 * @context is ISDS session context.
7311 * @service_name is name of SERVICE_DB_MANIPULATION service
7312 * @owner is structure describing box
7313 * @approval is optional external approval of box manipulation
7314 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7315 * NULL, if you don't care. */
7316 static isds_error build_send_manipulationdbowner_request_check_drop_response(
7317 struct isds_ctx *context, const xmlChar *service_name,
7318 const struct isds_DbOwnerInfo *owner,
7319 const struct isds_approval *approval, xmlChar **refnumber) {
7320 isds_error err = IE_SUCCESS;
7321 #if HAVE_LIBCURL
7322 char *service_name_locale = NULL;
7323 xmlNodePtr request = NULL, db_owner_info;
7324 xmlNsPtr isds_ns = NULL;
7325 #endif
7328 if (!context) return IE_INVALID_CONTEXT;
7329 zfree(context->long_message);
7330 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
7332 #if HAVE_LIBCURL
7333 service_name_locale = _isds_utf82locale((char*)service_name);
7334 if (!service_name_locale) {
7335 err = IE_NOMEM;
7336 goto leave;
7339 /* Build request */
7340 request = xmlNewNode(NULL, service_name);
7341 if (!request) {
7342 isds_printf_message(context,
7343 _("Could not build %s request"), service_name_locale);
7344 err = IE_ERROR;
7345 goto leave;
7347 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7348 if(!isds_ns) {
7349 isds_log_message(context, _("Could not create ISDS name space"));
7350 err = IE_ERROR;
7351 goto leave;
7353 xmlSetNs(request, isds_ns);
7356 /* Add XSD:tOwnerInfoInput child*/
7357 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
7358 err = insert_DbOwnerInfo(context, owner, db_owner_info);
7359 if (err) goto leave;
7361 /* Add XSD:gExtApproval*/
7362 err = insert_GExtApproval(context, approval, request);
7363 if (err) goto leave;
7365 /* Send it to server and process response */
7366 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
7367 service_name, &request, refnumber);
7369 leave:
7370 xmlFreeNode(request);
7371 free(service_name_locale);
7372 #else /* not HAVE_LIBCURL */
7373 err = IE_NOTSUP;
7374 #endif
7376 return err;
7380 /* Switch box accessibility state on request of box owner.
7381 * Despite the name, owner must do the request off-line. This function is
7382 * designed for such off-line meeting points (e.g. Czech POINT).
7383 * @context is ISDS session context.
7384 * @box identifies box to switch accessibility state.
7385 * @allow is true for making accessible, false to disallow access.
7386 * @approval is optional external approval of box manipulation
7387 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7388 * NULL, if you don't care. */
7389 isds_error isds_switch_box_accessibility_on_owner_request(
7390 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
7391 const _Bool allow, const struct isds_approval *approval,
7392 char **refnumber) {
7393 return build_send_manipulationdbowner_request_check_drop_response(context,
7394 (allow) ? BAD_CAST "EnableOwnDataBox" :
7395 BAD_CAST "DisableOwnDataBox",
7396 box, approval, (xmlChar **) refnumber);
7400 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
7401 * date.
7402 * @context is ISDS session context.
7403 * @box identifies box to switch accessibility state.
7404 * @since is date since accessibility has been denied. This can be past too.
7405 * Only tm_year, tm_mon and tm_mday carry sane value.
7406 * @approval is optional external approval of box manipulation
7407 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7408 * NULL, if you don't care. */
7409 isds_error isds_disable_box_accessibility_externaly(
7410 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
7411 const struct tm *since, const struct isds_approval *approval,
7412 char **refnumber) {
7413 isds_error err = IE_SUCCESS;
7414 #if HAVE_LIBCURL
7415 char *service_name_locale = NULL;
7416 xmlNodePtr request = NULL, node;
7417 xmlNsPtr isds_ns = NULL;
7418 xmlChar *string = NULL;
7419 #endif
7422 if (!context) return IE_INVALID_CONTEXT;
7423 zfree(context->long_message);
7424 if (!box || !since) return IE_INVAL;
7426 #if HAVE_LIBCURL
7427 /* Build request */
7428 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
7429 if (!request) {
7430 isds_printf_message(context,
7431 _("Could not build %s request"), "DisableDataBoxExternally");
7432 err = IE_ERROR;
7433 goto leave;
7435 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7436 if(!isds_ns) {
7437 isds_log_message(context, _("Could not create ISDS name space"));
7438 err = IE_ERROR;
7439 goto leave;
7441 xmlSetNs(request, isds_ns);
7444 /* Add @box identification */
7445 INSERT_ELEMENT(node, request, "dbOwnerInfo");
7446 err = insert_DbOwnerInfo(context, box, node);
7447 if (err) goto leave;
7449 /* Add @since date */
7450 err = tm2datestring(since, &string);
7451 if(err) {
7452 isds_log_message(context,
7453 _("Could not convert `since' argument to ISO date string"));
7454 goto leave;
7456 INSERT_STRING(request, "dbOwnerDisableDate", string);
7457 zfree(string);
7459 /* Add @approval */
7460 err = insert_GExtApproval(context, approval, request);
7461 if (err) goto leave;
7463 /* Send it to server and process response */
7464 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
7465 BAD_CAST "DisableDataBoxExternally", &request,
7466 (xmlChar **) refnumber);
7468 leave:
7469 free(string);
7470 xmlFreeNode(request);
7471 free(service_name_locale);
7472 #else /* not HAVE_LIBCURL */
7473 err = IE_NOTSUP;
7474 #endif
7476 return err;
7480 #if HAVE_LIBCURL
7481 /* Insert struct isds_message data (envelope (recipient data optional) and
7482 * documents into XML tree
7483 * @context is session context
7484 * @outgoing_message is libisds structure with message data
7485 * @create_message is XML CreateMessage or CreateMultipleMessage element
7486 * @process_recipient true for recipient data serialization, false for no
7487 * serialization */
7488 static isds_error insert_envelope_files(struct isds_ctx *context,
7489 const struct isds_message *outgoing_message, xmlNodePtr create_message,
7490 const _Bool process_recipient) {
7492 isds_error err = IE_SUCCESS;
7493 xmlNodePtr envelope, dm_files, node;
7494 xmlChar *string = NULL;
7496 if (!context) return IE_INVALID_CONTEXT;
7497 if (!outgoing_message || !create_message) return IE_INVAL;
7500 /* Build envelope */
7501 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
7502 if (!envelope) {
7503 isds_printf_message(context, _("Could not add dmEnvelope child to "
7504 "%s element"), create_message->name);
7505 return IE_ERROR;
7508 if (!outgoing_message->envelope) {
7509 isds_log_message(context, _("Outgoing message is missing envelope"));
7510 err = IE_INVAL;
7511 goto leave;
7514 /* Insert optional message type */
7515 err = insert_message_type(context, outgoing_message->envelope->dmType,
7516 envelope);
7517 if (err) goto leave;
7519 INSERT_STRING(envelope, "dmSenderOrgUnit",
7520 outgoing_message->envelope->dmSenderOrgUnit);
7521 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
7522 outgoing_message->envelope->dmSenderOrgUnitNum, string);
7524 if (process_recipient) {
7525 if (!outgoing_message->envelope->dbIDRecipient) {
7526 isds_log_message(context,
7527 _("Outgoing message is missing recipient box identifier"));
7528 err = IE_INVAL;
7529 goto leave;
7531 INSERT_STRING(envelope, "dbIDRecipient",
7532 outgoing_message->envelope->dbIDRecipient);
7534 INSERT_STRING(envelope, "dmRecipientOrgUnit",
7535 outgoing_message->envelope->dmRecipientOrgUnit);
7536 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
7537 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
7538 INSERT_STRING(envelope, "dmToHands",
7539 outgoing_message->envelope->dmToHands);
7542 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
7543 "dmAnnotation");
7544 INSERT_STRING(envelope, "dmAnnotation",
7545 outgoing_message->envelope->dmAnnotation);
7547 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
7548 0, 50, "dmRecipientRefNumber");
7549 INSERT_STRING(envelope, "dmRecipientRefNumber",
7550 outgoing_message->envelope->dmRecipientRefNumber);
7552 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
7553 0, 50, "dmSenderRefNumber");
7554 INSERT_STRING(envelope, "dmSenderRefNumber",
7555 outgoing_message->envelope->dmSenderRefNumber);
7557 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
7558 0, 50, "dmRecipientIdent");
7559 INSERT_STRING(envelope, "dmRecipientIdent",
7560 outgoing_message->envelope->dmRecipientIdent);
7562 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
7563 0, 50, "dmSenderIdent");
7564 INSERT_STRING(envelope, "dmSenderIdent",
7565 outgoing_message->envelope->dmSenderIdent);
7567 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
7568 outgoing_message->envelope->dmLegalTitleLaw, string);
7569 INSERT_LONGINT(envelope, "dmLegalTitleYear",
7570 outgoing_message->envelope->dmLegalTitleYear, string);
7571 INSERT_STRING(envelope, "dmLegalTitleSect",
7572 outgoing_message->envelope->dmLegalTitleSect);
7573 INSERT_STRING(envelope, "dmLegalTitlePar",
7574 outgoing_message->envelope->dmLegalTitlePar);
7575 INSERT_STRING(envelope, "dmLegalTitlePoint",
7576 outgoing_message->envelope->dmLegalTitlePoint);
7578 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
7579 outgoing_message->envelope->dmPersonalDelivery);
7580 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
7581 outgoing_message->envelope->dmAllowSubstDelivery);
7583 /* ???: Should we require value for dbEffectiveOVM sender?
7584 * ISDS has default as true */
7585 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
7586 INSERT_BOOLEAN(envelope, "dmOVM",
7587 outgoing_message->envelope->dmPublishOwnID);
7590 /* Append dmFiles */
7591 if (!outgoing_message->documents) {
7592 isds_log_message(context,
7593 _("Outgoing message is missing list of documents"));
7594 err = IE_INVAL;
7595 goto leave;
7597 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
7598 if (!dm_files) {
7599 isds_printf_message(context, _("Could not add dmFiles child to "
7600 "%s element"), create_message->name);
7601 err = IE_ERROR;
7602 goto leave;
7605 /* Check for document hierarchy */
7606 err = _isds_check_documents_hierarchy(context, outgoing_message->documents);
7607 if (err) goto leave;
7609 /* Process each document */
7610 for (struct isds_list *item =
7611 (struct isds_list *) outgoing_message->documents;
7612 item; item = item->next) {
7613 if (!item->data) {
7614 isds_log_message(context,
7615 _("List of documents contains empty item"));
7616 err = IE_INVAL;
7617 goto leave;
7619 /* FIXME: Check for dmFileMetaType and for document references.
7620 * Only first document can be of MAIN type */
7621 err = insert_document(context, (struct isds_document*) item->data,
7622 dm_files);
7624 if (err) goto leave;
7627 leave:
7628 free(string);
7629 return err;
7631 #endif /* HAVE_LIBCURL */
7634 /* Send a message via ISDS to a recipient
7635 * @context is session context
7636 * @outgoing_message is message to send; Some members are mandatory (like
7637 * dbIDRecipient), some are optional and some are irrelevant (especially data
7638 * about sender). Included pointer to isds_list documents must contain at
7639 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
7640 * members will be filled with valid data from ISDS. Exact list of write
7641 * members is subject to change. Currently dmID is changed.
7642 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
7643 isds_error isds_send_message(struct isds_ctx *context,
7644 struct isds_message *outgoing_message) {
7646 isds_error err = IE_SUCCESS;
7647 #if HAVE_LIBCURL
7648 xmlNsPtr isds_ns = NULL;
7649 xmlNodePtr request = NULL;
7650 xmlDocPtr response = NULL;
7651 xmlChar *code = NULL, *message = NULL;
7652 xmlXPathContextPtr xpath_ctx = NULL;
7653 xmlXPathObjectPtr result = NULL;
7654 /*_Bool message_is_complete = 0;*/
7655 #endif
7657 if (!context) return IE_INVALID_CONTEXT;
7658 zfree(context->long_message);
7659 if (!outgoing_message) return IE_INVAL;
7661 #if HAVE_LIBCURL
7662 /* Check if connection is established
7663 * TODO: This check should be done downstairs. */
7664 if (!context->curl) return IE_CONNECTION_CLOSED;
7667 /* Build CreateMessage request */
7668 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
7669 if (!request) {
7670 isds_log_message(context,
7671 _("Could not build CreateMessage request"));
7672 return IE_ERROR;
7674 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7675 if(!isds_ns) {
7676 isds_log_message(context, _("Could not create ISDS name space"));
7677 xmlFreeNode(request);
7678 return IE_ERROR;
7680 xmlSetNs(request, isds_ns);
7682 /* Append envelope and files */
7683 err = insert_envelope_files(context, outgoing_message, request, 1);
7684 if (err) goto leave;
7687 /* Signal we can serialize message since now */
7688 /*message_is_complete = 1;*/
7691 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
7693 /* Sent request */
7694 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
7696 /* Don't' destroy request, we want to provide it to application later */
7698 if (err) {
7699 isds_log(ILF_ISDS, ILL_DEBUG,
7700 _("Processing ISDS response on CreateMessage "
7701 "request failed\n"));
7702 goto leave;
7705 /* Check for response status */
7706 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
7707 &code, &message, NULL);
7708 if (err) {
7709 isds_log(ILF_ISDS, ILL_DEBUG,
7710 _("ISDS response on CreateMessage request "
7711 "is missing status\n"));
7712 goto leave;
7715 /* Request processed, but refused by server or server failed */
7716 if (xmlStrcmp(code, BAD_CAST "0000")) {
7717 char *box_id_locale =
7718 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
7719 char *code_locale = _isds_utf82locale((char*)code);
7720 char *message_locale = _isds_utf82locale((char*)message);
7721 isds_log(ILF_ISDS, ILL_DEBUG,
7722 _("Server did not accept message for %s on CreateMessage "
7723 "request (code=%s, message=%s)\n"),
7724 box_id_locale, code_locale, message_locale);
7725 isds_log_message(context, message_locale);
7726 free(box_id_locale);
7727 free(code_locale);
7728 free(message_locale);
7729 err = IE_ISDS;
7730 goto leave;
7734 /* Extract data */
7735 xpath_ctx = xmlXPathNewContext(response);
7736 if (!xpath_ctx) {
7737 err = IE_ERROR;
7738 goto leave;
7740 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7741 err = IE_ERROR;
7742 goto leave;
7744 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
7745 xpath_ctx);
7746 if (!result) {
7747 err = IE_ERROR;
7748 goto leave;
7750 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7751 isds_log_message(context, _("Missing CreateMessageResponse element"));
7752 err = IE_ISDS;
7753 goto leave;
7755 if (result->nodesetval->nodeNr > 1) {
7756 isds_log_message(context, _("Multiple CreateMessageResponse element"));
7757 err = IE_ISDS;
7758 goto leave;
7760 xpath_ctx->node = result->nodesetval->nodeTab[0];
7761 xmlXPathFreeObject(result); result = NULL;
7763 if (outgoing_message->envelope->dmID) {
7764 free(outgoing_message->envelope->dmID);
7765 outgoing_message->envelope->dmID = NULL;
7767 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
7768 if (!outgoing_message->envelope->dmID) {
7769 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
7770 "but did not return assigned message ID\n"));
7773 leave:
7774 /* TODO: Serialize message into structure member raw */
7775 /* XXX: Each web service transport message in different format.
7776 * Therefore it's not possible to save them directly.
7777 * To save them, one must figure out common format.
7778 * We can leave it on application, or we can implement the ESS format. */
7779 /*if (message_is_complete) {
7780 if (outgoing_message->envelope->dmID) {
7782 /* Add assigned message ID as first child*/
7783 /*xmlNodePtr dmid_text = xmlNewText(
7784 (xmlChar *) outgoing_message->envelope->dmID);
7785 if (!dmid_text) goto serialization_failed;
7787 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
7788 BAD_CAST "dmID");
7789 if (!dmid_element) {
7790 xmlFreeNode(dmid_text);
7791 goto serialization_failed;
7794 xmlNodePtr dmid_element_with_text =
7795 xmlAddChild(dmid_element, dmid_text);
7796 if (!dmid_element_with_text) {
7797 xmlFreeNode(dmid_element);
7798 xmlFreeNode(dmid_text);
7799 goto serialization_failed;
7802 node = xmlAddPrevSibling(envelope->childern,
7803 dmid_element_with_text);
7804 if (!node) {
7805 xmlFreeNodeList(dmid_element_with_text);
7806 goto serialization_failed;
7810 /* Serialize message with ID into raw */
7811 /*buffer = serialize_element(envelope)*/
7812 /* }
7814 serialization_failed:
7818 /* Clean up */
7819 xmlXPathFreeObject(result);
7820 xmlXPathFreeContext(xpath_ctx);
7822 free(code);
7823 free(message);
7824 xmlFreeDoc(response);
7825 xmlFreeNode(request);
7827 if (!err)
7828 isds_log(ILF_ISDS, ILL_DEBUG,
7829 _("CreateMessage request processed by server "
7830 "successfully.\n"));
7831 #else /* not HAVE_LIBCURL */
7832 err = IE_NOTSUP;
7833 #endif
7835 return err;
7839 /* Send a message via ISDS to a multiple recipients
7840 * @context is session context
7841 * @outgoing_message is message to send; Some members are mandatory,
7842 * some are optional and some are irrelevant (especially data
7843 * about sender). Data about recipient will be substituted by ISDS from
7844 * @copies. Included pointer to isds_list documents must
7845 * contain at least one document of FILEMETATYPE_MAIN.
7846 * @copies is list of isds_message_copy structures addressing all desired
7847 * recipients. This is read-write structure, some members will be filled with
7848 * valid data from ISDS (message IDs, error codes, error descriptions).
7849 * @return
7850 * ISDS_SUCCESS if all messages have been sent
7851 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
7852 * succeeded messages can be identified by copies->data->error),
7853 * or other error code if something other goes wrong. */
7854 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
7855 const struct isds_message *outgoing_message,
7856 struct isds_list *copies) {
7858 isds_error err = IE_SUCCESS;
7859 #if HAVE_LIBCURL
7860 isds_error append_err;
7861 xmlNsPtr isds_ns = NULL;
7862 xmlNodePtr request = NULL, recipients, recipient, node;
7863 struct isds_list *item;
7864 struct isds_message_copy *copy;
7865 xmlDocPtr response = NULL;
7866 xmlChar *code = NULL, *message = NULL;
7867 xmlXPathContextPtr xpath_ctx = NULL;
7868 xmlXPathObjectPtr result = NULL;
7869 xmlChar *string = NULL;
7870 int i;
7871 #endif
7873 if (!context) return IE_INVALID_CONTEXT;
7874 zfree(context->long_message);
7875 if (!outgoing_message || !copies) return IE_INVAL;
7877 #if HAVE_LIBCURL
7878 /* Check if connection is established
7879 * TODO: This check should be done downstairs. */
7880 if (!context->curl) return IE_CONNECTION_CLOSED;
7883 /* Build CreateMultipleMessage request */
7884 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
7885 if (!request) {
7886 isds_log_message(context,
7887 _("Could not build CreateMultipleMessage request"));
7888 return IE_ERROR;
7890 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7891 if(!isds_ns) {
7892 isds_log_message(context, _("Could not create ISDS name space"));
7893 xmlFreeNode(request);
7894 return IE_ERROR;
7896 xmlSetNs(request, isds_ns);
7899 /* Build recipients */
7900 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
7901 if (!recipients) {
7902 isds_log_message(context, _("Could not add dmRecipients child to "
7903 "CreateMultipleMessage element"));
7904 xmlFreeNode(request);
7905 return IE_ERROR;
7908 /* Insert each recipient */
7909 for (item = copies; item; item = item->next) {
7910 copy = (struct isds_message_copy *) item->data;
7911 if (!copy) {
7912 isds_log_message(context,
7913 _("`copies' list item contains empty data"));
7914 err = IE_INVAL;
7915 goto leave;
7918 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
7919 if (!recipient) {
7920 isds_log_message(context, _("Could not add dmRecipient child to "
7921 "dmRecipients element"));
7922 err = IE_ERROR;
7923 goto leave;
7926 if (!copy->dbIDRecipient) {
7927 isds_log_message(context,
7928 _("Message copy is missing recipient box identifier"));
7929 err = IE_INVAL;
7930 goto leave;
7932 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
7933 INSERT_STRING(recipient, "dmRecipientOrgUnit",
7934 copy->dmRecipientOrgUnit);
7935 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
7936 copy->dmRecipientOrgUnitNum, string);
7937 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
7940 /* Append envelope and files */
7941 err = insert_envelope_files(context, outgoing_message, request, 0);
7942 if (err) goto leave;
7945 isds_log(ILF_ISDS, ILL_DEBUG,
7946 _("Sending CreateMultipleMessage request to ISDS\n"));
7948 /* Sent request */
7949 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
7950 if (err) {
7951 isds_log(ILF_ISDS, ILL_DEBUG,
7952 _("Processing ISDS response on CreateMultipleMessage "
7953 "request failed\n"));
7954 goto leave;
7957 /* Check for response status */
7958 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
7959 &code, &message, NULL);
7960 if (err) {
7961 isds_log(ILF_ISDS, ILL_DEBUG,
7962 _("ISDS response on CreateMultipleMessage request "
7963 "is missing status\n"));
7964 goto leave;
7967 /* Request processed, but some copies failed */
7968 if (!xmlStrcmp(code, BAD_CAST "0004")) {
7969 char *box_id_locale =
7970 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
7971 char *code_locale = _isds_utf82locale((char*)code);
7972 char *message_locale = _isds_utf82locale((char*)message);
7973 isds_log(ILF_ISDS, ILL_DEBUG,
7974 _("Server did accept message for multiple recipients "
7975 "on CreateMultipleMessage request but delivery to "
7976 "some of them failed (code=%s, message=%s)\n"),
7977 box_id_locale, code_locale, message_locale);
7978 isds_log_message(context, message_locale);
7979 free(box_id_locale);
7980 free(code_locale);
7981 free(message_locale);
7982 err = IE_PARTIAL_SUCCESS;
7985 /* Request refused by server as whole */
7986 else if (xmlStrcmp(code, BAD_CAST "0000")) {
7987 char *box_id_locale =
7988 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
7989 char *code_locale = _isds_utf82locale((char*)code);
7990 char *message_locale = _isds_utf82locale((char*)message);
7991 isds_log(ILF_ISDS, ILL_DEBUG,
7992 _("Server did not accept message for multiple recipients "
7993 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
7994 box_id_locale, code_locale, message_locale);
7995 isds_log_message(context, message_locale);
7996 free(box_id_locale);
7997 free(code_locale);
7998 free(message_locale);
7999 err = IE_ISDS;
8000 goto leave;
8004 /* Extract data */
8005 xpath_ctx = xmlXPathNewContext(response);
8006 if (!xpath_ctx) {
8007 err = IE_ERROR;
8008 goto leave;
8010 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8011 err = IE_ERROR;
8012 goto leave;
8014 result = xmlXPathEvalExpression(
8015 BAD_CAST "/isds:CreateMultipleMessageResponse"
8016 "/isds:dmMultipleStatus/isds:dmSingleStatus",
8017 xpath_ctx);
8018 if (!result) {
8019 err = IE_ERROR;
8020 goto leave;
8022 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8023 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
8024 err = IE_ISDS;
8025 goto leave;
8028 /* Extract message ID and delivery status for each copy */
8029 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
8030 item = item->next, i++) {
8031 copy = (struct isds_message_copy *) item->data;
8032 xpath_ctx->node = result->nodesetval->nodeTab[i];
8034 append_err = append_TMStatus(context, copy, xpath_ctx);
8035 if (append_err) {
8036 err = append_err;
8037 goto leave;
8040 if (item || i < result->nodesetval->nodeNr) {
8041 isds_printf_message(context, _("ISDS returned unexpected number of "
8042 "message copy delivery states: %d"),
8043 result->nodesetval->nodeNr);
8044 err = IE_ISDS;
8045 goto leave;
8049 leave:
8050 /* Clean up */
8051 free(string);
8052 xmlXPathFreeObject(result);
8053 xmlXPathFreeContext(xpath_ctx);
8055 free(code);
8056 free(message);
8057 xmlFreeDoc(response);
8058 xmlFreeNode(request);
8060 if (!err)
8061 isds_log(ILF_ISDS, ILL_DEBUG,
8062 _("CreateMultipleMessageResponse request processed by server "
8063 "successfully.\n"));
8064 #else /* not HAVE_LIBCURL */
8065 err = IE_NOTSUP;
8066 #endif
8068 return err;
8072 /* Get list of messages. This is common core for getting sent or received
8073 * messages.
8074 * Any criterion argument can be NULL, if you don't care about it.
8075 * @context is session context. Must not be NULL.
8076 * @outgoing_direction is true if you want list of outgoing messages,
8077 * it's false if you want incoming messages.
8078 * @from_time is minimal time and date of message sending inclusive.
8079 * @to_time is maximal time and date of message sending inclusive
8080 * @organization_unit_number is number of sender/recipient respectively.
8081 * @status_filter is bit field of isds_message_status values. Use special
8082 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8083 * all values, you can use bit-wise arithmetic if you want.)
8084 * @offset is index of first message we are interested in. First message is 1.
8085 * Set to 0 (or 1) if you don't care.
8086 * @number is maximal length of list you want to get as input value, outputs
8087 * number of messages matching these criteria. Can be NULL if you don't care
8088 * (applies to output value either).
8089 * @messages is automatically reallocated list of isds_message's. Be ware that
8090 * it returns only brief overview (envelope and some other fields) about each
8091 * message, not the complete message. FIXME: Specify exact fields.
8092 * The list is sorted by delivery time in ascending order.
8093 * Use NULL if you don't care about don't need the data (useful if you want to
8094 * know only the @number). If you provide &NULL, list will be allocated on
8095 * heap, if you provide pointer to non-NULL, list will be freed automatically
8096 * at first. Also in case of error the list will be NULLed.
8097 * @return IE_SUCCESS or appropriate error code. */
8098 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
8099 _Bool outgoing_direction,
8100 const struct timeval *from_time, const struct timeval *to_time,
8101 const long int *organization_unit_number,
8102 const unsigned int status_filter,
8103 const unsigned long int offset, unsigned long int *number,
8104 struct isds_list **messages) {
8106 isds_error err = IE_SUCCESS;
8107 #if HAVE_LIBCURL
8108 xmlNsPtr isds_ns = NULL;
8109 xmlNodePtr request = NULL, node;
8110 xmlDocPtr response = NULL;
8111 xmlChar *code = NULL, *message = NULL;
8112 xmlXPathContextPtr xpath_ctx = NULL;
8113 xmlXPathObjectPtr result = NULL;
8114 xmlChar *string = NULL;
8115 long unsigned int count = 0;
8116 #endif
8118 if (!context) return IE_INVALID_CONTEXT;
8119 zfree(context->long_message);
8121 /* Free former message list if any */
8122 if (messages) isds_list_free(messages);
8124 #if HAVE_LIBCURL
8125 /* Check if connection is established
8126 * TODO: This check should be done downstairs. */
8127 if (!context->curl) return IE_CONNECTION_CLOSED;
8129 /* Build GetListOf*Messages request */
8130 request = xmlNewNode(NULL,
8131 (outgoing_direction) ?
8132 BAD_CAST "GetListOfSentMessages" :
8133 BAD_CAST "GetListOfReceivedMessages"
8135 if (!request) {
8136 isds_log_message(context,
8137 (outgoing_direction) ?
8138 _("Could not build GetListOfSentMessages request") :
8139 _("Could not build GetListOfReceivedMessages request")
8141 return IE_ERROR;
8143 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8144 if(!isds_ns) {
8145 isds_log_message(context, _("Could not create ISDS name space"));
8146 xmlFreeNode(request);
8147 return IE_ERROR;
8149 xmlSetNs(request, isds_ns);
8152 if (from_time) {
8153 err = timeval2timestring(from_time, &string);
8154 if (err) goto leave;
8156 INSERT_STRING(request, "dmFromTime", string);
8157 free(string); string = NULL;
8159 if (to_time) {
8160 err = timeval2timestring(to_time, &string);
8161 if (err) goto leave;
8163 INSERT_STRING(request, "dmToTime", string);
8164 free(string); string = NULL;
8166 if (outgoing_direction) {
8167 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
8168 organization_unit_number, string);
8169 } else {
8170 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
8171 organization_unit_number, string);
8174 if (status_filter > MESSAGESTATE_ANY) {
8175 isds_printf_message(context,
8176 _("Invalid message state filter value: %ld"), status_filter);
8177 err = IE_INVAL;
8178 goto leave;
8180 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
8182 if (offset > 0 ) {
8183 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
8184 } else {
8185 INSERT_STRING(request, "dmOffset", "1");
8188 /* number 0 means no limit */
8189 if (number && *number == 0) {
8190 INSERT_STRING(request, "dmLimit", NULL);
8191 } else {
8192 INSERT_ULONGINT(request, "dmLimit", number, string);
8196 isds_log(ILF_ISDS, ILL_DEBUG,
8197 (outgoing_direction) ?
8198 _("Sending GetListOfSentMessages request to ISDS\n") :
8199 _("Sending GetListOfReceivedMessages request to ISDS\n")
8202 /* Sent request */
8203 err = isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
8204 xmlFreeNode(request); request = NULL;
8206 if (err) {
8207 isds_log(ILF_ISDS, ILL_DEBUG,
8208 (outgoing_direction) ?
8209 _("Processing ISDS response on GetListOfSentMessages "
8210 "request failed\n") :
8211 _("Processing ISDS response on GetListOfReceivedMessages "
8212 "request failed\n")
8214 goto leave;
8217 /* Check for response status */
8218 err = isds_response_status(context, SERVICE_DM_INFO, response,
8219 &code, &message, NULL);
8220 if (err) {
8221 isds_log(ILF_ISDS, ILL_DEBUG,
8222 (outgoing_direction) ?
8223 _("ISDS response on GetListOfSentMessages request "
8224 "is missing status\n") :
8225 _("ISDS response on GetListOfReceivedMessages request "
8226 "is missing status\n")
8228 goto leave;
8231 /* Request processed, but nothing found */
8232 if (xmlStrcmp(code, BAD_CAST "0000")) {
8233 char *code_locale = _isds_utf82locale((char*)code);
8234 char *message_locale = _isds_utf82locale((char*)message);
8235 isds_log(ILF_ISDS, ILL_DEBUG,
8236 (outgoing_direction) ?
8237 _("Server refused GetListOfSentMessages request "
8238 "(code=%s, message=%s)\n") :
8239 _("Server refused GetListOfReceivedMessages request "
8240 "(code=%s, message=%s)\n"),
8241 code_locale, message_locale);
8242 isds_log_message(context, message_locale);
8243 free(code_locale);
8244 free(message_locale);
8245 err = IE_ISDS;
8246 goto leave;
8250 /* Extract data */
8251 xpath_ctx = xmlXPathNewContext(response);
8252 if (!xpath_ctx) {
8253 err = IE_ERROR;
8254 goto leave;
8256 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8257 err = IE_ERROR;
8258 goto leave;
8260 result = xmlXPathEvalExpression(
8261 (outgoing_direction) ?
8262 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
8263 "isds:dmRecords/isds:dmRecord" :
8264 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
8265 "isds:dmRecords/isds:dmRecord",
8266 xpath_ctx);
8267 if (!result) {
8268 err = IE_ERROR;
8269 goto leave;
8272 /* Fill output arguments in */
8273 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8274 struct isds_envelope *envelope;
8275 struct isds_list *item = NULL, *last_item = NULL;
8277 for (count = 0; count < result->nodesetval->nodeNr; count++) {
8278 /* Create new message */
8279 item = calloc(1, sizeof(*item));
8280 if (!item) {
8281 err = IE_NOMEM;
8282 goto leave;
8284 item->destructor = (void(*)(void**)) &isds_message_free;
8285 item->data = calloc(1, sizeof(struct isds_message));
8286 if (!item->data) {
8287 isds_list_free(&item);
8288 err = IE_NOMEM;
8289 goto leave;
8292 /* Extract envelope data */
8293 xpath_ctx->node = result->nodesetval->nodeTab[count];
8294 envelope = NULL;
8295 err = extract_DmRecord(context, &envelope, xpath_ctx);
8296 if (err) {
8297 isds_list_free(&item);
8298 goto leave;
8301 /* Attach extracted envelope */
8302 ((struct isds_message *) item->data)->envelope = envelope;
8304 /* Append new message into the list */
8305 if (!*messages) {
8306 *messages = last_item = item;
8307 } else {
8308 last_item->next = item;
8309 last_item = item;
8313 if (number) *number = count;
8315 leave:
8316 if (err) {
8317 isds_list_free(messages);
8320 free(string);
8321 xmlXPathFreeObject(result);
8322 xmlXPathFreeContext(xpath_ctx);
8324 free(code);
8325 free(message);
8326 xmlFreeDoc(response);
8327 xmlFreeNode(request);
8329 if (!err)
8330 isds_log(ILF_ISDS, ILL_DEBUG,
8331 (outgoing_direction) ?
8332 _("GetListOfSentMessages request processed by server "
8333 "successfully.\n") :
8334 _("GetListOfReceivedMessages request processed by server "
8335 "successfully.\n")
8337 #else /* not HAVE_LIBCURL */
8338 err = IE_NOTSUP;
8339 #endif
8340 return err;
8344 /* Get list of outgoing (already sent) messages.
8345 * Any criterion argument can be NULL, if you don't care about it.
8346 * @context is session context. Must not be NULL.
8347 * @from_time is minimal time and date of message sending inclusive.
8348 * @to_time is maximal time and date of message sending inclusive
8349 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
8350 * @status_filter is bit field of isds_message_status values. Use special
8351 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8352 * all values, you can use bit-wise arithmetic if you want.)
8353 * @offset is index of first message we are interested in. First message is 1.
8354 * Set to 0 (or 1) if you don't care.
8355 * @number is maximal length of list you want to get as input value, outputs
8356 * number of messages matching these criteria. Can be NULL if you don't care
8357 * (applies to output value either).
8358 * @messages is automatically reallocated list of isds_message's. Be ware that
8359 * it returns only brief overview (envelope and some other fields) about each
8360 * message, not the complete message. FIXME: Specify exact fields.
8361 * The list is sorted by delivery time in ascending order.
8362 * Use NULL if you don't care about the meta data (useful if you want to know
8363 * only the @number). If you provide &NULL, list will be allocated on heap,
8364 * if you provide pointer to non-NULL, list will be freed automatically at
8365 * first. Also in case of error the list will be NULLed.
8366 * @return IE_SUCCESS or appropriate error code. */
8367 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
8368 const struct timeval *from_time, const struct timeval *to_time,
8369 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
8370 const unsigned long int offset, unsigned long int *number,
8371 struct isds_list **messages) {
8373 return isds_get_list_of_messages(
8374 context, 1,
8375 from_time, to_time, dmSenderOrgUnitNum, status_filter,
8376 offset, number,
8377 messages);
8381 /* Get list of incoming (addressed to you) messages.
8382 * Any criterion argument can be NULL, if you don't care about it.
8383 * @context is session context. Must not be NULL.
8384 * @from_time is minimal time and date of message sending inclusive.
8385 * @to_time is maximal time and date of message sending inclusive
8386 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
8387 * @status_filter is bit field of isds_message_status values. Use special
8388 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8389 * all values, you can use bit-wise arithmetic if you want.)
8390 * @offset is index of first message we are interested in. First message is 1.
8391 * Set to 0 (or 1) if you don't care.
8392 * @number is maximal length of list you want to get as input value, outputs
8393 * number of messages matching these criteria. Can be NULL if you don't care
8394 * (applies to output value either).
8395 * @messages is automatically reallocated list of isds_message's. Be ware that
8396 * it returns only brief overview (envelope and some other fields) about each
8397 * message, not the complete message. FIXME: Specify exact fields.
8398 * Use NULL if you don't care about the meta data (useful if you want to know
8399 * only the @number). If you provide &NULL, list will be allocated on heap,
8400 * if you provide pointer to non-NULL, list will be freed automatically at
8401 * first. Also in case of error the list will be NULLed.
8402 * @return IE_SUCCESS or appropriate error code. */
8403 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
8404 const struct timeval *from_time, const struct timeval *to_time,
8405 const long int *dmRecipientOrgUnitNum,
8406 const unsigned int status_filter,
8407 const unsigned long int offset, unsigned long int *number,
8408 struct isds_list **messages) {
8410 return isds_get_list_of_messages(
8411 context, 0,
8412 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
8413 offset, number,
8414 messages);
8418 /* Get list of sent message state changes.
8419 * Any criterion argument can be NULL, if you don't care about it.
8420 * @context is session context. Must not be NULL.
8421 * @from_time is minimal time and date of status changes inclusive
8422 * @to_time is maximal time and date of status changes inclusive
8423 * @changed_states is automatically reallocated list of
8424 * isds_message_status_change's. If you provide &NULL, list will be allocated
8425 * on heap, if you provide pointer to non-NULL, list will be freed
8426 * automatically at first. Also in case of error the list will be NULLed.
8427 * XXX: The list item ordering is not specified.
8428 * XXX: Server provides only `recent' changes.
8429 * @return IE_SUCCESS or appropriate error code. */
8430 isds_error isds_get_list_of_sent_message_state_changes(
8431 struct isds_ctx *context,
8432 const struct timeval *from_time, const struct timeval *to_time,
8433 struct isds_list **changed_states) {
8435 isds_error err = IE_SUCCESS;
8436 #if HAVE_LIBCURL
8437 xmlNsPtr isds_ns = NULL;
8438 xmlNodePtr request = NULL, node;
8439 xmlDocPtr response = NULL;
8440 xmlXPathContextPtr xpath_ctx = NULL;
8441 xmlXPathObjectPtr result = NULL;
8442 xmlChar *string = NULL;
8443 long unsigned int count = 0;
8444 #endif
8446 if (!context) return IE_INVALID_CONTEXT;
8447 zfree(context->long_message);
8449 /* Free former message list if any */
8450 isds_list_free(changed_states);
8452 #if HAVE_LIBCURL
8453 /* Check if connection is established
8454 * TODO: This check should be done downstairs. */
8455 if (!context->curl) return IE_CONNECTION_CLOSED;
8457 /* Build GetMessageStateChanges request */
8458 request = xmlNewNode(NULL, BAD_CAST "GetMessageStateChanges");
8459 if (!request) {
8460 isds_log_message(context,
8461 _("Could not build GetMessageStateChanges request"));
8462 return IE_ERROR;
8464 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8465 if(!isds_ns) {
8466 isds_log_message(context, _("Could not create ISDS name space"));
8467 xmlFreeNode(request);
8468 return IE_ERROR;
8470 xmlSetNs(request, isds_ns);
8473 if (from_time) {
8474 err = timeval2timestring(from_time, &string);
8475 if (err) goto leave;
8477 INSERT_STRING(request, "dmFromTime", string);
8478 zfree(string);
8480 if (to_time) {
8481 err = timeval2timestring(to_time, &string);
8482 if (err) goto leave;
8484 INSERT_STRING(request, "dmToTime", string);
8485 zfree(string);
8488 /* Sent request */
8489 err = send_destroy_request_check_response(context,
8490 SERVICE_DM_INFO, BAD_CAST "GetMessageStateChanges", &request,
8491 &response, NULL);
8492 if (err) goto leave;
8495 /* Extract data */
8496 xpath_ctx = xmlXPathNewContext(response);
8497 if (!xpath_ctx) {
8498 err = IE_ERROR;
8499 goto leave;
8501 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8502 err = IE_ERROR;
8503 goto leave;
8505 result = xmlXPathEvalExpression(
8506 BAD_CAST "/isds:GetMessageStateChangesResponse/"
8507 "isds:dmRecords/isds:dmRecord", xpath_ctx);
8508 if (!result) {
8509 err = IE_ERROR;
8510 goto leave;
8513 /* Fill output arguments in */
8514 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8515 struct isds_list *item = NULL, *last_item = NULL;
8517 for (count = 0; count < result->nodesetval->nodeNr; count++) {
8518 /* Create new status change */
8519 item = calloc(1, sizeof(*item));
8520 if (!item) {
8521 err = IE_NOMEM;
8522 goto leave;
8524 item->destructor =
8525 (void(*)(void**)) &isds_message_status_change_free;
8527 /* Extract message status change */
8528 xpath_ctx->node = result->nodesetval->nodeTab[count];
8529 err = extract_StateChangesRecord(context,
8530 (struct isds_message_status_change **) &item->data,
8531 xpath_ctx);
8532 if (err) {
8533 isds_list_free(&item);
8534 goto leave;
8537 /* Append new message status change into the list */
8538 if (!*changed_states) {
8539 *changed_states = last_item = item;
8540 } else {
8541 last_item->next = item;
8542 last_item = item;
8547 leave:
8548 if (err) {
8549 isds_list_free(changed_states);
8552 free(string);
8553 xmlXPathFreeObject(result);
8554 xmlXPathFreeContext(xpath_ctx);
8555 xmlFreeDoc(response);
8556 xmlFreeNode(request);
8558 if (!err)
8559 isds_log(ILF_ISDS, ILL_DEBUG,
8560 _("GetMessageStateChanges request processed by server "
8561 "successfully.\n"));
8562 #else /* not HAVE_LIBCURL */
8563 err = IE_NOTSUP;
8564 #endif
8565 return err;
8569 #if HAVE_LIBCURL
8570 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
8571 * code
8572 * @context is session context
8573 * @service is ISDS WS service handler
8574 * @service_name is name of SERVICE_DM_OPERATIONS
8575 * @message_id is message ID to send as service argument to ISDS
8576 * @response is server SOAP body response as XML document
8577 * @raw_response is automatically reallocated bit stream with response body. Use
8578 * NULL if you don't care
8579 * @raw_response_length is size of @raw_response in bytes
8580 * @code is ISDS status code
8581 * @status_message is ISDS status message
8582 * @return error coded from lower layer, context message will be set up
8583 * appropriately. */
8584 static isds_error build_send_check_message_request(struct isds_ctx *context,
8585 const isds_service service, const xmlChar *service_name,
8586 const char *message_id,
8587 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
8588 xmlChar **code, xmlChar **status_message) {
8590 isds_error err = IE_SUCCESS;
8591 char *service_name_locale = NULL, *message_id_locale = NULL;
8592 xmlNodePtr request = NULL, node;
8593 xmlNsPtr isds_ns = NULL;
8595 if (!context) return IE_INVALID_CONTEXT;
8596 if (!service_name || !message_id) return IE_INVAL;
8597 if (!response || !code || !status_message) return IE_INVAL;
8598 if (!raw_response_length && raw_response) return IE_INVAL;
8600 /* Free output argument */
8601 xmlFreeDoc(*response); *response = NULL;
8602 if (raw_response) zfree(*raw_response);
8603 free(*code);
8604 free(*status_message);
8607 /* Check if connection is established
8608 * TODO: This check should be done downstairs. */
8609 if (!context->curl) return IE_CONNECTION_CLOSED;
8611 service_name_locale = _isds_utf82locale((char*)service_name);
8612 message_id_locale = _isds_utf82locale(message_id);
8613 if (!service_name_locale || !message_id_locale) {
8614 err = IE_NOMEM;
8615 goto leave;
8618 /* Build request */
8619 request = xmlNewNode(NULL, service_name);
8620 if (!request) {
8621 isds_printf_message(context,
8622 _("Could not build %s request for %s message ID"),
8623 service_name_locale, message_id_locale);
8624 err = IE_ERROR;
8625 goto leave;
8627 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8628 if(!isds_ns) {
8629 isds_log_message(context, _("Could not create ISDS name space"));
8630 err = IE_ERROR;
8631 goto leave;
8633 xmlSetNs(request, isds_ns);
8636 /* Add requested ID */
8637 err = validate_message_id_length(context, (xmlChar *) message_id);
8638 if (err) goto leave;
8639 INSERT_STRING(request, "dmID", message_id);
8642 isds_log(ILF_ISDS, ILL_DEBUG,
8643 _("Sending %s request for %s message ID to ISDS\n"),
8644 service_name_locale, message_id_locale);
8646 /* Send request */
8647 err = isds(context, service, request, response,
8648 raw_response, raw_response_length);
8649 xmlFreeNode(request); request = NULL;
8651 if (err) {
8652 isds_log(ILF_ISDS, ILL_DEBUG,
8653 _("Processing ISDS response on %s request failed\n"),
8654 service_name_locale);
8655 goto leave;
8658 /* Check for response status */
8659 err = isds_response_status(context, service, *response,
8660 code, status_message, NULL);
8661 if (err) {
8662 isds_log(ILF_ISDS, ILL_DEBUG,
8663 _("ISDS response on %s request is missing status\n"),
8664 service_name_locale);
8665 goto leave;
8668 /* Request processed, but nothing found */
8669 if (xmlStrcmp(*code, BAD_CAST "0000")) {
8670 char *code_locale = _isds_utf82locale((char*) *code);
8671 char *status_message_locale = _isds_utf82locale((char*) *status_message);
8672 isds_log(ILF_ISDS, ILL_DEBUG,
8673 _("Server refused %s request for %s message ID "
8674 "(code=%s, message=%s)\n"),
8675 service_name_locale, message_id_locale,
8676 code_locale, status_message_locale);
8677 isds_log_message(context, status_message_locale);
8678 free(code_locale);
8679 free(status_message_locale);
8680 err = IE_ISDS;
8681 goto leave;
8684 leave:
8685 free(message_id_locale);
8686 free(service_name_locale);
8687 xmlFreeNode(request);
8688 return err;
8692 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
8693 * signed data and free ISDS response.
8694 * @context is session context
8695 * @message_id is UTF-8 encoded message ID for logging purpose
8696 * @response is parsed XML document. It will be freed and NULLed in the middle
8697 * of function run to save memory. This is not guaranteed in case of error.
8698 * @request_name is name of ISDS request used to construct response root
8699 * element name and for logging purpose.
8700 * @raw is reallocated output buffer with DER encoded CMS data
8701 * @raw_length is size of @raw buffer in bytes
8702 * @returns standard error codes, in case of error, @raw will be freed and
8703 * NULLed, @response sometimes. */
8704 static isds_error find_extract_signed_data_free_response(
8705 struct isds_ctx *context, const xmlChar *message_id,
8706 xmlDocPtr *response, const xmlChar *request_name,
8707 void **raw, size_t *raw_length) {
8709 isds_error err = IE_SUCCESS;
8710 char *xpath_expression = NULL;
8711 xmlXPathContextPtr xpath_ctx = NULL;
8712 xmlXPathObjectPtr result = NULL;
8713 char *encoded_structure = NULL;
8715 if (!context) return IE_INVALID_CONTEXT;
8716 if (!raw) return IE_INVAL;
8717 zfree(*raw);
8718 if (!message_id || !response || !*response || !request_name || !raw_length)
8719 return IE_INVAL;
8721 /* Build XPath expression */
8722 xpath_expression = _isds_astrcat3("/isds:", (char *) request_name,
8723 "Response/isds:dmSignature");
8724 if (!xpath_expression) return IE_NOMEM;
8726 /* Extract data */
8727 xpath_ctx = xmlXPathNewContext(*response);
8728 if (!xpath_ctx) {
8729 err = IE_ERROR;
8730 goto leave;
8732 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8733 err = IE_ERROR;
8734 goto leave;
8736 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
8737 if (!result) {
8738 err = IE_ERROR;
8739 goto leave;
8741 /* Empty response */
8742 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8743 char *message_id_locale = _isds_utf82locale((char*) message_id);
8744 isds_printf_message(context,
8745 _("Server did not return any signed data for message ID `%s' "
8746 "on %s request"),
8747 message_id_locale, request_name);
8748 free(message_id_locale);
8749 err = IE_ISDS;
8750 goto leave;
8752 /* More responses */
8753 if (result->nodesetval->nodeNr > 1) {
8754 char *message_id_locale = _isds_utf82locale((char*) message_id);
8755 isds_printf_message(context,
8756 _("Server did return more signed data for message ID `%s' "
8757 "on %s request"),
8758 message_id_locale, request_name);
8759 free(message_id_locale);
8760 err = IE_ISDS;
8761 goto leave;
8763 /* One response */
8764 xpath_ctx->node = result->nodesetval->nodeTab[0];
8766 /* Extract PKCS#7 structure */
8767 EXTRACT_STRING(".", encoded_structure);
8768 if (!encoded_structure) {
8769 isds_log_message(context, _("dmSignature element is empty"));
8772 /* Here we have delivery info as standalone CMS in encoded_structure.
8773 * We don't need any other data, free them: */
8774 xmlXPathFreeObject(result); result = NULL;
8775 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
8776 xmlFreeDoc(*response); *response = NULL;
8779 /* Decode PKCS#7 to DER format */
8780 *raw_length = _isds_b64decode(encoded_structure, raw);
8781 if (*raw_length == (size_t) -1) {
8782 isds_log_message(context,
8783 _("Error while Base64-decoding PKCS#7 structure"));
8784 err = IE_ERROR;
8785 goto leave;
8788 leave:
8789 if (err) {
8790 zfree(*raw);
8791 raw_length = 0;
8794 free(encoded_structure);
8795 xmlXPathFreeObject(result);
8796 xmlXPathFreeContext(xpath_ctx);
8797 free(xpath_expression);
8799 return err;
8801 #endif /* HAVE_LIBCURL */
8804 /* Download incoming message envelope identified by ID.
8805 * @context is session context
8806 * @message_id is message identifier (you can get them from
8807 * isds_get_list_of_received_messages())
8808 * @message is automatically reallocated message retrieved from ISDS.
8809 * It will miss documents per se. Use isds_get_received_message(), if you are
8810 * interested in documents (content) too.
8811 * Returned hash and timestamp require documents to be verifiable. */
8812 isds_error isds_get_received_envelope(struct isds_ctx *context,
8813 const char *message_id, struct isds_message **message) {
8815 isds_error err = IE_SUCCESS;
8816 #if HAVE_LIBCURL
8817 xmlDocPtr response = NULL;
8818 xmlChar *code = NULL, *status_message = NULL;
8819 xmlXPathContextPtr xpath_ctx = NULL;
8820 xmlXPathObjectPtr result = NULL;
8821 #endif
8823 if (!context) return IE_INVALID_CONTEXT;
8824 zfree(context->long_message);
8826 /* Free former message if any */
8827 if (!message) return IE_INVAL;
8828 isds_message_free(message);
8830 #if HAVE_LIBCURL
8831 /* Do request and check for success */
8832 err = build_send_check_message_request(context, SERVICE_DM_INFO,
8833 BAD_CAST "MessageEnvelopeDownload", message_id,
8834 &response, NULL, NULL, &code, &status_message);
8835 if (err) goto leave;
8837 /* Extract data */
8838 xpath_ctx = xmlXPathNewContext(response);
8839 if (!xpath_ctx) {
8840 err = IE_ERROR;
8841 goto leave;
8843 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8844 err = IE_ERROR;
8845 goto leave;
8847 result = xmlXPathEvalExpression(
8848 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
8849 "isds:dmReturnedMessageEnvelope",
8850 xpath_ctx);
8851 if (!result) {
8852 err = IE_ERROR;
8853 goto leave;
8855 /* Empty response */
8856 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8857 char *message_id_locale = _isds_utf82locale((char*) message_id);
8858 isds_printf_message(context,
8859 _("Server did not return any envelope for ID `%s' "
8860 "on MessageEnvelopeDownload request"), message_id_locale);
8861 free(message_id_locale);
8862 err = IE_ISDS;
8863 goto leave;
8865 /* More envelops */
8866 if (result->nodesetval->nodeNr > 1) {
8867 char *message_id_locale = _isds_utf82locale((char*) message_id);
8868 isds_printf_message(context,
8869 _("Server did return more envelopes for ID `%s' "
8870 "on MessageEnvelopeDownload request"), message_id_locale);
8871 free(message_id_locale);
8872 err = IE_ISDS;
8873 goto leave;
8875 /* One message */
8876 xpath_ctx->node = result->nodesetval->nodeTab[0];
8878 /* Extract the envelope (= message without documents, hence 0) */
8879 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
8880 if (err) goto leave;
8882 /* Save XML blob */
8883 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
8884 &(*message)->raw_length);
8886 leave:
8887 if (err) {
8888 isds_message_free(message);
8891 xmlXPathFreeObject(result);
8892 xmlXPathFreeContext(xpath_ctx);
8894 free(code);
8895 free(status_message);
8896 if (!*message || !(*message)->xml) {
8897 xmlFreeDoc(response);
8900 if (!err)
8901 isds_log(ILF_ISDS, ILL_DEBUG,
8902 _("MessageEnvelopeDownload request processed by server "
8903 "successfully.\n")
8905 #else /* not HAVE_LIBCURL */
8906 err = IE_NOTSUP;
8907 #endif
8908 return err;
8912 /* Load delivery info of any format from buffer.
8913 * @context is session context
8914 * @raw_type advertises format of @buffer content. Only delivery info types
8915 * are accepted.
8916 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
8917 * retrieve such data from message->raw after calling
8918 * isds_get_signed_delivery_info().
8919 * @length is length of buffer in bytes.
8920 * @message is automatically reallocated message parsed from @buffer.
8921 * @strategy selects how buffer will be attached into raw isds_message member.
8922 * */
8923 isds_error isds_load_delivery_info(struct isds_ctx *context,
8924 const isds_raw_type raw_type,
8925 const void *buffer, const size_t length,
8926 struct isds_message **message, const isds_buffer_strategy strategy) {
8928 isds_error err = IE_SUCCESS;
8929 message_ns_type message_ns;
8930 xmlDocPtr message_doc = NULL;
8931 xmlXPathContextPtr xpath_ctx = NULL;
8932 xmlXPathObjectPtr result = NULL;
8933 void *xml_stream = NULL;
8934 size_t xml_stream_length = 0;
8936 if (!context) return IE_INVALID_CONTEXT;
8937 zfree(context->long_message);
8938 if (!message) return IE_INVAL;
8939 isds_message_free(message);
8940 if (!buffer) return IE_INVAL;
8943 /* Select buffer format and extract XML from CMS*/
8944 switch (raw_type) {
8945 case RAWTYPE_DELIVERYINFO:
8946 message_ns = MESSAGE_NS_UNSIGNED;
8947 xml_stream = (void *) buffer;
8948 xml_stream_length = length;
8949 break;
8951 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
8952 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
8953 xml_stream = (void *) buffer;
8954 xml_stream_length = length;
8955 break;
8957 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
8958 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
8959 err = _isds_extract_cms_data(context, buffer, length,
8960 &xml_stream, &xml_stream_length);
8961 if (err) goto leave;
8962 break;
8964 default:
8965 isds_log_message(context, _("Bad raw delivery representation type"));
8966 return IE_INVAL;
8967 break;
8970 isds_log(ILF_ISDS, ILL_DEBUG,
8971 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
8972 xml_stream_length, xml_stream);
8974 /* Convert delivery info XML stream into XPath context */
8975 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
8976 if (!message_doc) {
8977 err = IE_XML;
8978 goto leave;
8980 xpath_ctx = xmlXPathNewContext(message_doc);
8981 if (!xpath_ctx) {
8982 err = IE_ERROR;
8983 goto leave;
8985 /* XXX: Name spaces mangled for signed delivery info:
8986 * http://isds.czechpoint.cz/v20/delivery:
8988 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
8989 * <q:dmDelivery>
8990 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
8991 * <p:dmID>170272</p:dmID>
8992 * ...
8993 * </p:dmDm>
8994 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
8995 * ...
8996 * </q:dmEvents>...</q:dmEvents>
8997 * </q:dmDelivery>
8998 * </q:GetDeliveryInfoResponse>
8999 * */
9000 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
9001 err = IE_ERROR;
9002 goto leave;
9004 result = xmlXPathEvalExpression(
9005 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
9006 xpath_ctx);
9007 if (!result) {
9008 err = IE_ERROR;
9009 goto leave;
9011 /* Empty delivery info */
9012 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9013 isds_printf_message(context,
9014 _("XML document is not sisds:dmDelivery document"));
9015 err = IE_ISDS;
9016 goto leave;
9018 /* More delivery info's */
9019 if (result->nodesetval->nodeNr > 1) {
9020 isds_printf_message(context,
9021 _("XML document has more sisds:dmDelivery elements"));
9022 err = IE_ISDS;
9023 goto leave;
9025 /* One delivery info */
9026 xpath_ctx->node = result->nodesetval->nodeTab[0];
9028 /* Extract the envelope (= message without documents, hence 0).
9029 * XXX: extract_TReturnedMessage() can obtain attachments size,
9030 * but delivery info carries none. It's coded as option elements,
9031 * so it should work. */
9032 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
9033 if (err) goto leave;
9035 /* Extract events */
9036 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
9037 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
9038 if (err) { err = IE_ERROR; goto leave; }
9039 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
9040 if (err) goto leave;
9042 /* Append raw CMS structure into message */
9043 (*message)->raw_type = raw_type;
9044 switch (strategy) {
9045 case BUFFER_DONT_STORE:
9046 break;
9047 case BUFFER_COPY:
9048 (*message)->raw = malloc(length);
9049 if (!(*message)->raw) {
9050 err = IE_NOMEM;
9051 goto leave;
9053 memcpy((*message)->raw, buffer, length);
9054 (*message)->raw_length = length;
9055 break;
9056 case BUFFER_MOVE:
9057 (*message)->raw = (void *) buffer;
9058 (*message)->raw_length = length;
9059 break;
9060 default:
9061 err = IE_ENUM;
9062 goto leave;
9065 leave:
9066 if (err) {
9067 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
9068 isds_message_free(message);
9071 xmlXPathFreeObject(result);
9072 xmlXPathFreeContext(xpath_ctx);
9073 if (!*message || !(*message)->xml) {
9074 xmlFreeDoc(message_doc);
9076 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
9078 if (!err)
9079 isds_log(ILF_ISDS, ILL_DEBUG,
9080 _("Delivery info loaded successfully.\n"));
9081 return err;
9085 /* Download signed delivery info-sheet of given message identified by ID.
9086 * @context is session context
9087 * @message_id is message identifier (you can get them from
9088 * isds_get_list_of_{sent,received}_messages())
9089 * @message is automatically reallocated message retrieved from ISDS.
9090 * It will miss documents per se. Use isds_get_signed_received_message(),
9091 * if you are interested in documents (content). OTOH, only this function
9092 * can get list events message has gone through. */
9093 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
9094 const char *message_id, struct isds_message **message) {
9096 isds_error err = IE_SUCCESS;
9097 #if HAVE_LIBCURL
9098 xmlDocPtr response = NULL;
9099 xmlChar *code = NULL, *status_message = NULL;
9100 void *raw = NULL;
9101 size_t raw_length = 0;
9102 #endif
9104 if (!context) return IE_INVALID_CONTEXT;
9105 zfree(context->long_message);
9107 /* Free former message if any */
9108 if (!message) return IE_INVAL;
9109 isds_message_free(message);
9111 #if HAVE_LIBCURL
9112 /* Do request and check for success */
9113 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9114 BAD_CAST "GetSignedDeliveryInfo", message_id,
9115 &response, NULL, NULL, &code, &status_message);
9116 if (err) goto leave;
9118 /* Find signed delivery info, extract it into raw and maybe free
9119 * response */
9120 err = find_extract_signed_data_free_response(context,
9121 (xmlChar *)message_id, &response,
9122 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
9123 if (err) goto leave;
9125 /* Parse delivery info */
9126 err = isds_load_delivery_info(context,
9127 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
9128 message, BUFFER_MOVE);
9129 if (err) goto leave;
9131 raw = NULL;
9133 leave:
9134 if (err) {
9135 isds_message_free(message);
9138 free(raw);
9139 free(code);
9140 free(status_message);
9141 xmlFreeDoc(response);
9143 if (!err)
9144 isds_log(ILF_ISDS, ILL_DEBUG,
9145 _("GetSignedDeliveryInfo request processed by server "
9146 "successfully.\n")
9148 #else /* not HAVE_LIBCURL */
9149 err = IE_NOTSUP;
9150 #endif
9151 return err;
9155 /* Download delivery info-sheet of given message identified by ID.
9156 * @context is session context
9157 * @message_id is message identifier (you can get them from
9158 * isds_get_list_of_{sent,received}_messages())
9159 * @message is automatically reallocated message retrieved from ISDS.
9160 * It will miss documents per se. Use isds_get_received_message(), if you are
9161 * interested in documents (content). OTOH, only this function can get list
9162 * of events message has gone through. */
9163 isds_error isds_get_delivery_info(struct isds_ctx *context,
9164 const char *message_id, struct isds_message **message) {
9166 isds_error err = IE_SUCCESS;
9167 #if HAVE_LIBCURL
9168 xmlDocPtr response = NULL;
9169 xmlChar *code = NULL, *status_message = NULL;
9170 xmlNodePtr delivery_node = NULL;
9171 void *raw = NULL;
9172 size_t raw_length = 0;
9173 #endif
9175 if (!context) return IE_INVALID_CONTEXT;
9176 zfree(context->long_message);
9178 /* Free former message if any */
9179 if (!message) return IE_INVAL;
9180 isds_message_free(message);
9182 #if HAVE_LIBCURL
9183 /* Do request and check for success */
9184 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9185 BAD_CAST "GetDeliveryInfo", message_id,
9186 &response, NULL, NULL, &code, &status_message);
9187 if (err) goto leave;
9190 /* Serialize delivery info */
9191 delivery_node = xmlDocGetRootElement(response);
9192 if (!delivery_node) {
9193 char *message_id_locale = _isds_utf82locale((char*) message_id);
9194 isds_printf_message(context,
9195 _("Server did not return any delivery info for ID `%s' "
9196 "on GetDeliveryInfo request"), message_id_locale);
9197 free(message_id_locale);
9198 err = IE_ISDS;
9199 goto leave;
9201 err = serialize_subtree(context, delivery_node, &raw, &raw_length);
9202 if (err) goto leave;
9204 /* Parse delivery info */
9205 /* TODO: Here we parse the response second time. We could single delivery
9206 * parser from isds_load_delivery_info() to make things faster. */
9207 err = isds_load_delivery_info(context,
9208 RAWTYPE_DELIVERYINFO, raw, raw_length,
9209 message, BUFFER_MOVE);
9210 if (err) goto leave;
9212 raw = NULL;
9215 leave:
9216 if (err) {
9217 isds_message_free(message);
9220 free(raw);
9221 free(code);
9222 free(status_message);
9223 xmlFreeDoc(response);
9225 if (!err)
9226 isds_log(ILF_ISDS, ILL_DEBUG,
9227 _("GetDeliveryInfo request processed by server "
9228 "successfully.\n")
9230 #else /* not HAVE_LIBCURL */
9231 err = IE_NOTSUP;
9232 #endif
9233 return err;
9237 /* Download incoming message identified by ID.
9238 * @context is session context
9239 * @message_id is message identifier (you can get them from
9240 * isds_get_list_of_received_messages())
9241 * @message is automatically reallocated message retrieved from ISDS */
9242 isds_error isds_get_received_message(struct isds_ctx *context,
9243 const char *message_id, struct isds_message **message) {
9245 isds_error err = IE_SUCCESS;
9246 #if HAVE_LIBCURL
9247 xmlDocPtr response = NULL;
9248 void *xml_stream = NULL;
9249 size_t xml_stream_length;
9250 xmlChar *code = NULL, *status_message = NULL;
9251 xmlXPathContextPtr xpath_ctx = NULL;
9252 xmlXPathObjectPtr result = NULL;
9253 char *phys_path = NULL;
9254 size_t phys_start, phys_end;
9255 #endif
9257 if (!context) return IE_INVALID_CONTEXT;
9258 zfree(context->long_message);
9260 /* Free former message if any */
9261 if (message) isds_message_free(message);
9263 #if HAVE_LIBCURL
9264 /* Do request and check for success */
9265 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
9266 BAD_CAST "MessageDownload", message_id,
9267 &response, &xml_stream, &xml_stream_length,
9268 &code, &status_message);
9269 if (err) goto leave;
9271 /* Extract data */
9272 xpath_ctx = xmlXPathNewContext(response);
9273 if (!xpath_ctx) {
9274 err = IE_ERROR;
9275 goto leave;
9277 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9278 err = IE_ERROR;
9279 goto leave;
9281 result = xmlXPathEvalExpression(
9282 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
9283 xpath_ctx);
9284 if (!result) {
9285 err = IE_ERROR;
9286 goto leave;
9288 /* Empty response */
9289 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9290 char *message_id_locale = _isds_utf82locale((char*) message_id);
9291 isds_printf_message(context,
9292 _("Server did not return any message for ID `%s' "
9293 "on MessageDownload request"), message_id_locale);
9294 free(message_id_locale);
9295 err = IE_ISDS;
9296 goto leave;
9298 /* More messages */
9299 if (result->nodesetval->nodeNr > 1) {
9300 char *message_id_locale = _isds_utf82locale((char*) message_id);
9301 isds_printf_message(context,
9302 _("Server did return more messages for ID `%s' "
9303 "on MessageDownload request"), message_id_locale);
9304 free(message_id_locale);
9305 err = IE_ISDS;
9306 goto leave;
9308 /* One message */
9309 xpath_ctx->node = result->nodesetval->nodeTab[0];
9311 /* Extract the message */
9312 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
9313 if (err) goto leave;
9315 /* Locate raw XML blob */
9316 phys_path = strdup(
9317 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
9318 PHYSXML_ELEMENT_SEPARATOR
9319 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
9320 PHYSXML_ELEMENT_SEPARATOR
9321 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
9323 if (!phys_path) {
9324 err = IE_NOMEM;
9325 goto leave;
9327 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
9328 phys_path, &phys_start, &phys_end);
9329 zfree(phys_path);
9330 if (err) {
9331 isds_log_message(context,
9332 _("Substring with isds:MessageDownloadResponse element "
9333 "could not be located in raw SOAP message"));
9334 goto leave;
9336 /* Save XML blob */
9337 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
9338 &(*message)->raw_length);*/
9339 /* TODO: Store name space declarations from ancestors */
9340 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
9341 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
9342 (*message)->raw_length = phys_end - phys_start + 1;
9343 (*message)->raw = malloc((*message)->raw_length);
9344 if (!(*message)->raw) {
9345 err = IE_NOMEM;
9346 goto leave;
9348 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
9351 leave:
9352 if (err) {
9353 isds_message_free(message);
9356 free(phys_path);
9358 xmlXPathFreeObject(result);
9359 xmlXPathFreeContext(xpath_ctx);
9361 free(code);
9362 free(status_message);
9363 free(xml_stream);
9364 if (!*message || !(*message)->xml) {
9365 xmlFreeDoc(response);
9368 if (!err)
9369 isds_log(ILF_ISDS, ILL_DEBUG,
9370 _("MessageDownload request processed by server "
9371 "successfully.\n")
9373 #else /* not HAVE_LIBCURL */
9374 err = IE_NOTSUP;
9375 #endif
9376 return err;
9380 /* Load message of any type from buffer.
9381 * @context is session context
9382 * @raw_type defines content type of @buffer. Only message types are allowed.
9383 * @buffer is message raw representation. Format (CMS, plain signed,
9384 * message direction) is defined in @raw_type. You can retrieve such data
9385 * from message->raw after calling isds_get_[signed]{received,sent}_message().
9386 * @length is length of buffer in bytes.
9387 * @message is automatically reallocated message parsed from @buffer.
9388 * @strategy selects how buffer will be attached into raw isds_message member.
9389 * */
9390 isds_error isds_load_message(struct isds_ctx *context,
9391 const isds_raw_type raw_type, const void *buffer, const size_t length,
9392 struct isds_message **message, const isds_buffer_strategy strategy) {
9394 isds_error err = IE_SUCCESS;
9395 void *xml_stream = NULL;
9396 size_t xml_stream_length = 0;
9397 message_ns_type message_ns;
9398 xmlDocPtr message_doc = NULL;
9399 xmlXPathContextPtr xpath_ctx = NULL;
9400 xmlXPathObjectPtr result = NULL;
9402 if (!context) return IE_INVALID_CONTEXT;
9403 zfree(context->long_message);
9404 if (!message) return IE_INVAL;
9405 isds_message_free(message);
9406 if (!buffer) return IE_INVAL;
9409 /* Select buffer format and extract XML from CMS*/
9410 switch (raw_type) {
9411 case RAWTYPE_INCOMING_MESSAGE:
9412 message_ns = MESSAGE_NS_UNSIGNED;
9413 xml_stream = (void *) buffer;
9414 xml_stream_length = length;
9415 break;
9417 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
9418 message_ns = MESSAGE_NS_SIGNED_INCOMING;
9419 xml_stream = (void *) buffer;
9420 xml_stream_length = length;
9421 break;
9423 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
9424 message_ns = MESSAGE_NS_SIGNED_INCOMING;
9425 err = _isds_extract_cms_data(context, buffer, length,
9426 &xml_stream, &xml_stream_length);
9427 if (err) goto leave;
9428 break;
9430 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
9431 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
9432 xml_stream = (void *) buffer;
9433 xml_stream_length = length;
9434 break;
9436 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
9437 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
9438 err = _isds_extract_cms_data(context, buffer, length,
9439 &xml_stream, &xml_stream_length);
9440 if (err) goto leave;
9441 break;
9443 default:
9444 isds_log_message(context, _("Bad raw message representation type"));
9445 return IE_INVAL;
9446 break;
9449 isds_log(ILF_ISDS, ILL_DEBUG,
9450 _("Loading message:\n%.*s\nEnd of message\n"),
9451 xml_stream_length, xml_stream);
9453 /* Convert messages XML stream into XPath context */
9454 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
9455 if (!message_doc) {
9456 err = IE_XML;
9457 goto leave;
9459 xpath_ctx = xmlXPathNewContext(message_doc);
9460 if (!xpath_ctx) {
9461 err = IE_ERROR;
9462 goto leave;
9464 /* XXX: Standard name space for unsigned incoming direction:
9465 * http://isds.czechpoint.cz/v20/
9467 * XXX: Name spaces mangled for signed outgoing direction:
9468 * http://isds.czechpoint.cz/v20/SentMessage:
9470 * <q:MessageDownloadResponse
9471 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
9472 * <q:dmReturnedMessage>
9473 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
9474 * <p:dmID>151916</p:dmID>
9475 * ...
9476 * </p:dmDm>
9477 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
9478 * ...
9479 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
9480 * </q:dmReturnedMessage>
9481 * </q:MessageDownloadResponse>
9483 * XXX: Name spaces mangled for signed incoming direction:
9484 * http://isds.czechpoint.cz/v20/message:
9486 * <q:MessageDownloadResponse
9487 * xmlns:q="http://isds.czechpoint.cz/v20/message">
9488 * <q:dmReturnedMessage>
9489 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
9490 * <p:dmID>151916</p:dmID>
9491 * ...
9492 * </p:dmDm>
9493 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
9494 * ...
9495 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
9496 * </q:dmReturnedMessage>
9497 * </q:MessageDownloadResponse>
9499 * Stupidity of ISDS developers is unlimited */
9500 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
9501 err = IE_ERROR;
9502 goto leave;
9504 result = xmlXPathEvalExpression(
9505 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
9506 xpath_ctx);
9507 if (!result) {
9508 err = IE_ERROR;
9509 goto leave;
9511 /* Empty message */
9512 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9513 isds_printf_message(context,
9514 _("XML document does not contain "
9515 "sisds:dmReturnedMessage element"));
9516 err = IE_ISDS;
9517 goto leave;
9519 /* More messages */
9520 if (result->nodesetval->nodeNr > 1) {
9521 isds_printf_message(context,
9522 _("XML document has more sisds:dmReturnedMessage elements"));
9523 err = IE_ISDS;
9524 goto leave;
9526 /* One message */
9527 xpath_ctx->node = result->nodesetval->nodeTab[0];
9529 /* Extract the message */
9530 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
9531 if (err) goto leave;
9533 /* Append raw buffer into message */
9534 (*message)->raw_type = raw_type;
9535 switch (strategy) {
9536 case BUFFER_DONT_STORE:
9537 break;
9538 case BUFFER_COPY:
9539 (*message)->raw = malloc(length);
9540 if (!(*message)->raw) {
9541 err = IE_NOMEM;
9542 goto leave;
9544 memcpy((*message)->raw, buffer, length);
9545 (*message)->raw_length = length;
9546 break;
9547 case BUFFER_MOVE:
9548 (*message)->raw = (void *) buffer;
9549 (*message)->raw_length = length;
9550 break;
9551 default:
9552 err = IE_ENUM;
9553 goto leave;
9557 leave:
9558 if (err) {
9559 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
9560 isds_message_free(message);
9563 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
9564 xmlXPathFreeObject(result);
9565 xmlXPathFreeContext(xpath_ctx);
9566 if (!*message || !(*message)->xml) {
9567 xmlFreeDoc(message_doc);
9570 if (!err)
9571 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
9572 return err;
9576 /* Determine type of raw message or delivery info according some heuristics.
9577 * It does not validate the raw blob.
9578 * @context is session context
9579 * @raw_type returns content type of @buffer. Valid only if exit code of this
9580 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
9581 * reallocated memory.
9582 * @buffer is message raw representation.
9583 * @length is length of buffer in bytes. */
9584 isds_error isds_guess_raw_type(struct isds_ctx *context,
9585 isds_raw_type *raw_type, const void *buffer, const size_t length) {
9586 isds_error err;
9587 void *xml_stream = NULL;
9588 size_t xml_stream_length = 0;
9589 xmlDocPtr document = NULL;
9590 xmlNodePtr root = NULL;
9592 if (!context) return IE_INVALID_CONTEXT;
9593 zfree(context->long_message);
9594 if (length == 0 || !buffer) return IE_INVAL;
9595 if (!raw_type) return IE_INVAL;
9597 /* Try CMS */
9598 err = _isds_extract_cms_data(context, buffer, length,
9599 &xml_stream, &xml_stream_length);
9600 if (err) {
9601 xml_stream = (void *) buffer;
9602 xml_stream_length = (size_t) length;
9603 err = IE_SUCCESS;
9606 /* Try XML */
9607 document = xmlParseMemory(xml_stream, xml_stream_length);
9608 if (!document) {
9609 isds_printf_message(context,
9610 _("Could not parse data as XML document"));
9611 err = IE_NOTSUP;
9612 goto leave;
9615 /* Get root element */
9616 root = xmlDocGetRootElement(document);
9617 if (!root) {
9618 isds_printf_message(context,
9619 _("XML document is missing root element"));
9620 err = IE_XML;
9621 goto leave;
9624 if (!root->ns || !root->ns->href) {
9625 isds_printf_message(context,
9626 _("Root element does not belong to any name space"));
9627 err = IE_NOTSUP;
9628 goto leave;
9631 /* Test name space */
9632 if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_INCOMING_NS)) {
9633 if (xml_stream == buffer)
9634 *raw_type = RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE;
9635 else
9636 *raw_type = RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
9637 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_OUTGOING_NS)) {
9638 if (xml_stream == buffer)
9639 *raw_type = RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE;
9640 else
9641 *raw_type = RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE;
9642 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_DELIVERY_NS)) {
9643 if (xml_stream == buffer)
9644 *raw_type = RAWTYPE_PLAIN_SIGNED_DELIVERYINFO;
9645 else
9646 *raw_type = RAWTYPE_CMS_SIGNED_DELIVERYINFO;
9647 } else if (!xmlStrcmp(root->ns->href, BAD_CAST ISDS_NS)) {
9648 if (xml_stream != buffer) {
9649 isds_printf_message(context,
9650 _("Document in ISDS name space is encapsulated into CMS" ));
9651 err = IE_NOTSUP;
9652 } else if (!xmlStrcmp(root->name, BAD_CAST "MessageDownloadResponse"))
9653 *raw_type = RAWTYPE_INCOMING_MESSAGE;
9654 else if (!xmlStrcmp(root->name, BAD_CAST "GetDeliveryInfoResponse"))
9655 *raw_type = RAWTYPE_DELIVERYINFO;
9656 else {
9657 isds_printf_message(context,
9658 _("Unknown root element in ISDS name space"));
9659 err = IE_NOTSUP;
9661 } else {
9662 isds_printf_message(context,
9663 _("Unknown name space"));
9664 err = IE_NOTSUP;
9667 leave:
9668 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
9669 xmlFreeDoc(document);
9670 return err;
9674 /* Download signed incoming/outgoing message identified by ID.
9675 * @context is session context
9676 * @output is true for outgoing message, false for incoming message
9677 * @message_id is message identifier (you can get them from
9678 * isds_get_list_of_{sent,received}_messages())
9679 * @message is automatically reallocated message retrieved from ISDS. The raw
9680 * member will be filled with PKCS#7 structure in DER format. */
9681 static isds_error isds_get_signed_message(struct isds_ctx *context,
9682 const _Bool outgoing, const char *message_id,
9683 struct isds_message **message) {
9685 isds_error err = IE_SUCCESS;
9686 #if HAVE_LIBCURL
9687 xmlDocPtr response = NULL;
9688 xmlChar *code = NULL, *status_message = NULL;
9689 xmlXPathContextPtr xpath_ctx = NULL;
9690 xmlXPathObjectPtr result = NULL;
9691 char *encoded_structure = NULL;
9692 void *raw = NULL;
9693 size_t raw_length = 0;
9694 #endif
9696 if (!context) return IE_INVALID_CONTEXT;
9697 zfree(context->long_message);
9698 if (!message) return IE_INVAL;
9699 isds_message_free(message);
9701 #if HAVE_LIBCURL
9702 /* Do request and check for success */
9703 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
9704 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
9705 BAD_CAST "SignedMessageDownload",
9706 message_id, &response, NULL, NULL, &code, &status_message);
9707 if (err) goto leave;
9709 /* Find signed message, extract it into raw and maybe free
9710 * response */
9711 err = find_extract_signed_data_free_response(context,
9712 (xmlChar *)message_id, &response,
9713 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
9714 BAD_CAST "SignedMessageDownload",
9715 &raw, &raw_length);
9716 if (err) goto leave;
9718 /* Parse message */
9719 err = isds_load_message(context,
9720 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
9721 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
9722 raw, raw_length, message, BUFFER_MOVE);
9723 if (err) goto leave;
9725 raw = NULL;
9727 leave:
9728 if (err) {
9729 isds_message_free(message);
9732 free(encoded_structure);
9733 xmlXPathFreeObject(result);
9734 xmlXPathFreeContext(xpath_ctx);
9735 free(raw);
9737 free(code);
9738 free(status_message);
9739 xmlFreeDoc(response);
9741 if (!err)
9742 isds_log(ILF_ISDS, ILL_DEBUG,
9743 (outgoing) ?
9744 _("SignedSentMessageDownload request processed by server "
9745 "successfully.\n") :
9746 _("SignedMessageDownload request processed by server "
9747 "successfully.\n")
9749 #else /* not HAVE_LIBCURL */
9750 err = IE_NOTSUP;
9751 #endif
9752 return err;
9756 /* Download signed incoming message identified by ID.
9757 * @context is session context
9758 * @message_id is message identifier (you can get them from
9759 * isds_get_list_of_received_messages())
9760 * @message is automatically reallocated message retrieved from ISDS. The raw
9761 * member will be filled with PKCS#7 structure in DER format. */
9762 isds_error isds_get_signed_received_message(struct isds_ctx *context,
9763 const char *message_id, struct isds_message **message) {
9764 return isds_get_signed_message(context, 0, message_id, message);
9768 /* Download signed outgoing message identified by ID.
9769 * @context is session context
9770 * @message_id is message identifier (you can get them from
9771 * isds_get_list_of_sent_messages())
9772 * @message is automatically reallocated message retrieved from ISDS. The raw
9773 * member will be filled with PKCS#7 structure in DER format. */
9774 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
9775 const char *message_id, struct isds_message **message) {
9776 return isds_get_signed_message(context, 1, message_id, message);
9780 /* Get type and name of user who sent a message identified by ID.
9781 * @context is session context
9782 * @message_id is message identifier
9783 * @sender_type is pointer to automatically allocated type of sender detected
9784 * from @raw_sender_type string. If @raw_sender_type is unknown to this
9785 * library or to the server, NULL will be returned. Pass NULL if you don't
9786 * care about it.
9787 * @raw_sender_type is automatically reallocated UTF-8 string describing
9788 * sender type or NULL if not known to server. Pass NULL if you don't care.
9789 * @sender_name is automatically reallocated UTF-8 name of user who sent the
9790 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
9791 isds_error isds_get_message_sender(struct isds_ctx *context,
9792 const char *message_id, isds_sender_type **sender_type,
9793 char **raw_sender_type, char **sender_name) {
9794 isds_error err = IE_SUCCESS;
9795 #if HAVE_LIBCURL
9796 xmlDocPtr response = NULL;
9797 xmlChar *code = NULL, *status_message = NULL;
9798 xmlXPathContextPtr xpath_ctx = NULL;
9799 xmlXPathObjectPtr result = NULL;
9800 char *type_string = NULL;
9801 #endif
9803 if (!context) return IE_INVALID_CONTEXT;
9804 zfree(context->long_message);
9805 if (sender_type) zfree(*sender_type);
9806 if (raw_sender_type) zfree(*raw_sender_type);
9807 if (sender_name) zfree(*sender_name);
9808 if (!message_id) return IE_INVAL;
9810 #if HAVE_LIBCURL
9811 /* Do request and check for success */
9812 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9813 BAD_CAST "GetMessageAuthor",
9814 message_id, &response, NULL, NULL, &code, &status_message);
9815 if (err) goto leave;
9817 /* Extract data */
9818 xpath_ctx = xmlXPathNewContext(response);
9819 if (!xpath_ctx) {
9820 err = IE_ERROR;
9821 goto leave;
9823 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9824 err = IE_ERROR;
9825 goto leave;
9827 result = xmlXPathEvalExpression(
9828 BAD_CAST "/isds:GetMessageAuthorResponse", xpath_ctx);
9829 if (!result) {
9830 err = IE_ERROR;
9831 goto leave;
9833 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9834 isds_log_message(context,
9835 _("Missing GetMessageAuthorResponse element"));
9836 err = IE_ISDS;
9837 goto leave;
9839 if (result->nodesetval->nodeNr > 1) {
9840 isds_log_message(context,
9841 _("Multiple GetMessageAuthorResponse element"));
9842 err = IE_ISDS;
9843 goto leave;
9845 xpath_ctx->node = result->nodesetval->nodeTab[0];
9846 xmlXPathFreeObject(result); result = NULL;
9848 /* Fill output arguments in */
9849 EXTRACT_STRING("isds:userType", type_string);
9850 if (type_string) {
9851 *sender_type = calloc(1, sizeof(**sender_type));
9852 if (!*sender_type) {
9853 err = IE_NOMEM;
9854 goto leave;
9857 if (sender_type) {
9858 err = string2isds_sender_type((xmlChar *)type_string,
9859 *sender_type);
9860 if (err) {
9861 zfree(*sender_type);
9862 if (err == IE_ENUM) {
9863 err = IE_SUCCESS;
9864 char *type_string_locale = _isds_utf82locale(type_string);
9865 isds_log(ILF_ISDS, ILL_WARNING,
9866 _("Unknown isds:userType value: %s"),
9867 type_string_locale);
9868 free(type_string_locale);
9873 if (sender_type)
9874 EXTRACT_STRING("isds:authorName", *sender_name);
9876 leave:
9877 if (err) {
9878 if (sender_type) zfree(*sender_type);
9879 zfree(type_string);
9880 if (sender_name) zfree(*sender_name);
9882 if (raw_sender_type) *raw_sender_type = type_string;
9884 xmlXPathFreeObject(result);
9885 xmlXPathFreeContext(xpath_ctx);
9887 free(code);
9888 free(status_message);
9889 xmlFreeDoc(response);
9891 if (!err)
9892 isds_log(ILF_ISDS, ILL_DEBUG,
9893 _("GetMessageAuthor request processed by server "
9894 "successfully.\n"));
9895 #else /* not HAVE_LIBCURL */
9896 err = IE_NOTSUP;
9897 #endif
9898 return err;
9902 /* Retrieve hash of message identified by ID stored in ISDS.
9903 * @context is session context
9904 * @message_id is message identifier
9905 * @hash is automatically reallocated message hash downloaded from ISDS.
9906 * Message must exist in system and must not be deleted. */
9907 isds_error isds_download_message_hash(struct isds_ctx *context,
9908 const char *message_id, struct isds_hash **hash) {
9910 isds_error err = IE_SUCCESS;
9911 #if HAVE_LIBCURL
9912 xmlDocPtr response = NULL;
9913 xmlChar *code = NULL, *status_message = NULL;
9914 xmlXPathContextPtr xpath_ctx = NULL;
9915 xmlXPathObjectPtr result = NULL;
9916 #endif
9918 if (!context) return IE_INVALID_CONTEXT;
9919 zfree(context->long_message);
9921 isds_hash_free(hash);
9923 #if HAVE_LIBCURL
9924 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9925 BAD_CAST "VerifyMessage", message_id,
9926 &response, NULL, NULL, &code, &status_message);
9927 if (err) goto leave;
9930 /* Extract data */
9931 xpath_ctx = xmlXPathNewContext(response);
9932 if (!xpath_ctx) {
9933 err = IE_ERROR;
9934 goto leave;
9936 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9937 err = IE_ERROR;
9938 goto leave;
9940 result = xmlXPathEvalExpression(
9941 BAD_CAST "/isds:VerifyMessageResponse",
9942 xpath_ctx);
9943 if (!result) {
9944 err = IE_ERROR;
9945 goto leave;
9947 /* Empty response */
9948 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9949 char *message_id_locale = _isds_utf82locale((char*) message_id);
9950 isds_printf_message(context,
9951 _("Server did not return any response for ID `%s' "
9952 "on VerifyMessage request"), message_id_locale);
9953 free(message_id_locale);
9954 err = IE_ISDS;
9955 goto leave;
9957 /* More responses */
9958 if (result->nodesetval->nodeNr > 1) {
9959 char *message_id_locale = _isds_utf82locale((char*) message_id);
9960 isds_printf_message(context,
9961 _("Server did return more responses for ID `%s' "
9962 "on VerifyMessage request"), message_id_locale);
9963 free(message_id_locale);
9964 err = IE_ISDS;
9965 goto leave;
9967 /* One response */
9968 xpath_ctx->node = result->nodesetval->nodeTab[0];
9970 /* Extract the hash */
9971 err = find_and_extract_DmHash(context, hash, xpath_ctx);
9973 leave:
9974 if (err) {
9975 isds_hash_free(hash);
9978 xmlXPathFreeObject(result);
9979 xmlXPathFreeContext(xpath_ctx);
9981 free(code);
9982 free(status_message);
9983 xmlFreeDoc(response);
9985 if (!err)
9986 isds_log(ILF_ISDS, ILL_DEBUG,
9987 _("VerifyMessage 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 read. This is a transactional commit function to acknowledge
9998 * to ISDS the message has been downloaded and processed by client properly.
9999 * @context is session context
10000 * @message_id is message identifier. */
10001 isds_error isds_mark_message_read(struct isds_ctx *context,
10002 const char *message_id) {
10004 isds_error err = IE_SUCCESS;
10005 #if HAVE_LIBCURL
10006 xmlDocPtr response = NULL;
10007 xmlChar *code = NULL, *status_message = NULL;
10008 #endif
10010 if (!context) return IE_INVALID_CONTEXT;
10011 zfree(context->long_message);
10013 #if HAVE_LIBCURL
10014 /* Do request and check for success */
10015 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10016 BAD_CAST "MarkMessageAsDownloaded", message_id,
10017 &response, NULL, NULL, &code, &status_message);
10019 free(code);
10020 free(status_message);
10021 xmlFreeDoc(response);
10023 if (!err)
10024 isds_log(ILF_ISDS, ILL_DEBUG,
10025 _("MarkMessageAsDownloaded request processed by server "
10026 "successfully.\n")
10028 #else /* not HAVE_LIBCURL */
10029 err = IE_NOTSUP;
10030 #endif
10031 return err;
10035 /* Mark message as received by recipient. This is applicable only to
10036 * commercial message. Use envelope->dmType message member to distinguish
10037 * commercial message from government message. Government message is
10038 * received automatically (by law), commercial message on recipient request.
10039 * @context is session context
10040 * @message_id is message identifier. */
10041 isds_error isds_mark_message_received(struct isds_ctx *context,
10042 const char *message_id) {
10044 isds_error err = IE_SUCCESS;
10045 #if HAVE_LIBCURL
10046 xmlDocPtr response = NULL;
10047 xmlChar *code = NULL, *status_message = NULL;
10048 #endif
10050 if (!context) return IE_INVALID_CONTEXT;
10051 zfree(context->long_message);
10053 #if HAVE_LIBCURL
10054 /* Do request and check for success */
10055 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10056 BAD_CAST "ConfirmDelivery", message_id,
10057 &response, NULL, NULL, &code, &status_message);
10059 free(code);
10060 free(status_message);
10061 xmlFreeDoc(response);
10063 if (!err)
10064 isds_log(ILF_ISDS, ILL_DEBUG,
10065 _("ConfirmDelivery request processed by server "
10066 "successfully.\n")
10068 #else /* not HAVE_LIBCURL */
10069 err = IE_NOTSUP;
10070 #endif
10071 return err;
10075 /* Send document for authorized conversion into Czech POINT system.
10076 * This is public anonymous service, no log-in necessary. Special context is
10077 * used to reuse keep-a-live HTTPS connection.
10078 * @context is Czech POINT session context. DO NOT use context connected to
10079 * ISDS server. Use new context or context used by this function previously.
10080 * @document is document to convert. Only data, data_length, dmFileDescr and
10081 * is_xml members are significant. Be ware that not all document formats can be
10082 * converted (signed PDF 1.3 and higher only (2010-02 state)).
10083 * @id is reallocated identifier assigned by Czech POINT system to
10084 * your document on submit. Use is to tell it to Czech POINT officer.
10085 * @date is reallocated document submit date (submitted documents
10086 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
10087 * value. */
10088 isds_error czp_convert_document(struct isds_ctx *context,
10089 const struct isds_document *document,
10090 char **id, struct tm **date) {
10091 isds_error err = IE_SUCCESS;
10092 #if HAVE_LIBCURL
10093 xmlNsPtr deposit_ns = NULL, empty_ns = NULL;
10094 xmlNodePtr request = NULL, node;
10095 xmlDocPtr response = NULL;
10097 xmlXPathContextPtr xpath_ctx = NULL;
10098 xmlXPathObjectPtr result = NULL;
10099 long int status = -1;
10100 long int *status_ptr = &status;
10101 char *string = NULL;
10102 #endif
10105 if (!context) return IE_INVALID_CONTEXT;
10106 zfree(context->long_message);
10107 if (!document || !id || !date) return IE_INVAL;
10109 if (document->is_xml) {
10110 isds_log_message(context,
10111 _("XML documents cannot be submitted to conversion"));
10112 return IE_NOTSUP;
10115 /* Free output arguments */
10116 zfree(*id);
10117 zfree(*date);
10119 #if HAVE_LIBCURL
10120 /* Store configuration */
10121 context->type = CTX_TYPE_CZP;
10122 free(context->url);
10123 context->url = strdup("https://www.czechpoint.cz/uschovna/services.php");
10124 if (!(context->url))
10125 return IE_NOMEM;
10127 /* Prepare CURL handle if not yet connected */
10128 if (!context->curl) {
10129 context->curl = curl_easy_init();
10130 if (!(context->curl))
10131 return IE_ERROR;
10134 /* Build conversion request */
10135 request = xmlNewNode(NULL, BAD_CAST "saveDocument");
10136 if (!request) {
10137 isds_log_message(context,
10138 _("Could not build Czech POINT conversion request"));
10139 return IE_ERROR;
10141 deposit_ns = xmlNewNs(request, BAD_CAST DEPOSIT_NS, BAD_CAST "dep");
10142 if(!deposit_ns) {
10143 isds_log_message(context,
10144 _("Could not create Czech POINT deposit name space"));
10145 xmlFreeNode(request);
10146 return IE_ERROR;
10148 xmlSetNs(request, deposit_ns);
10150 /* Insert children. They are in empty namespace! */
10151 empty_ns = xmlNewNs(request, BAD_CAST "", NULL);
10152 if(!empty_ns) {
10153 isds_log_message(context, _("Could not create empty name space"));
10154 err = IE_ERROR;
10155 goto leave;
10157 INSERT_STRING_WITH_NS(request, empty_ns, "conversionID", "0");
10158 INSERT_STRING_WITH_NS(request, empty_ns, "fileName",
10159 document->dmFileDescr);
10161 /* Document encoded in Base64 */
10162 err = insert_base64_encoded_string(context, request, empty_ns, "document",
10163 document->data, document->data_length);
10164 if (err) goto leave;
10166 isds_log(ILF_ISDS, ILL_DEBUG,
10167 _("Submitting document for conversion into Czech POINT deposit"));
10169 /* Send conversion request */
10170 err = _czp_czpdeposit(context, request, &response);
10171 xmlFreeNode(request); request = NULL;
10173 if (err) {
10174 czp_do_close_connection(context);
10175 goto leave;
10179 /* Extract response */
10180 xpath_ctx = xmlXPathNewContext(response);
10181 if (!xpath_ctx) {
10182 err = IE_ERROR;
10183 goto leave;
10185 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10186 err = IE_ERROR;
10187 goto leave;
10189 result = xmlXPathEvalExpression(
10190 BAD_CAST "/deposit:saveDocumentResponse/return",
10191 xpath_ctx);
10192 if (!result) {
10193 err = IE_ERROR;
10194 goto leave;
10196 /* Empty response */
10197 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10198 isds_printf_message(context,
10199 _("Missing `return' element in Czech POINT deposit response"));
10200 err = IE_ISDS;
10201 goto leave;
10203 /* More responses */
10204 if (result->nodesetval->nodeNr > 1) {
10205 isds_printf_message(context,
10206 _("Multiple `return' element in Czech POINT deposit response"));
10207 err = IE_ISDS;
10208 goto leave;
10210 /* One response */
10211 xpath_ctx->node = result->nodesetval->nodeTab[0];
10213 /* Get status */
10214 EXTRACT_LONGINT("status", status_ptr, 1);
10215 if (status) {
10216 EXTRACT_STRING("statusMsg", string);
10217 char *string_locale = _isds_utf82locale(string);
10218 isds_printf_message(context,
10219 _("Czech POINT deposit refused document for conversion "
10220 "(code=%ld, message=%s)"),
10221 status, string_locale);
10222 free(string_locale);
10223 err = IE_ISDS;
10224 goto leave;
10227 /* Get document ID */
10228 EXTRACT_STRING("documentID", *id);
10230 /* Get submit date */
10231 EXTRACT_STRING("dateInserted", string);
10232 if (string) {
10233 *date = calloc(1, sizeof(**date));
10234 if (!*date) {
10235 err = IE_NOMEM;
10236 goto leave;
10238 err = datestring2tm((xmlChar *)string, *date);
10239 if (err) {
10240 if (err == IE_NOTSUP) {
10241 err = IE_ISDS;
10242 char *string_locale = _isds_utf82locale(string);
10243 isds_printf_message(context,
10244 _("Invalid dateInserted value: %s"), string_locale);
10245 free(string_locale);
10247 goto leave;
10251 leave:
10252 free(string);
10253 xmlXPathFreeObject(result);
10254 xmlXPathFreeContext(xpath_ctx);
10256 xmlFreeDoc(response);
10257 xmlFreeNode(request);
10259 if (!err) {
10260 char *id_locale = _isds_utf82locale((char *) *id);
10261 isds_log(ILF_ISDS, ILL_DEBUG,
10262 _("Document %s has been submitted for conversion "
10263 "to server successfully\n"), id_locale);
10264 free(id_locale);
10266 #else /* not HAVE_LIBCURL */
10267 err = IE_NOTSUP;
10268 #endif
10269 return err;
10273 /* Close possibly opened connection to Czech POINT document deposit.
10274 * @context is Czech POINT session context. */
10275 isds_error czp_close_connection(struct isds_ctx *context) {
10276 if (!context) return IE_INVALID_CONTEXT;
10277 zfree(context->long_message);
10278 #if HAVE_LIBCURL
10279 return czp_do_close_connection(context);
10280 #else
10281 return IE_NOTSUP;
10282 #endif
10286 /* Send request for new box creation in testing ISDS instance.
10287 * It's not possible to request for a production box currently, as it
10288 * communicates via e-mail.
10289 * XXX: This function does not work either. Server complains about invalid
10290 * e-mail address.
10291 * XXX: Remove context->type hacks in isds.c and validator.c when removing
10292 * this function
10293 * @context is special session context for box creation request. DO NOT use
10294 * standard context as it could reveal your password. Use fresh new context or
10295 * context previously used by this function.
10296 * @box is box description to create including single primary user (in case of
10297 * FO box type). It outputs box ID assigned by ISDS in dbID element.
10298 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
10299 * box, or contact address of PFO box owner). The email member is mandatory as
10300 * it will be used to deliver credentials.
10301 * @former_names is former name of box owner. Pass NULL if you don't care.
10302 * @approval is optional external approval of box manipulation
10303 * @refnumber is reallocated serial number of request assigned by ISDS. Use
10304 * NULL, if you don't care.*/
10305 isds_error isds_request_new_testing_box(struct isds_ctx *context,
10306 struct isds_DbOwnerInfo *box, const struct isds_list *users,
10307 const char *former_names, const struct isds_approval *approval,
10308 char **refnumber) {
10309 isds_error err = IE_SUCCESS;
10310 #if HAVE_LIBCURL
10311 xmlNodePtr request = NULL;
10312 xmlDocPtr response = NULL;
10313 xmlXPathContextPtr xpath_ctx = NULL;
10314 xmlXPathObjectPtr result = NULL;
10315 #endif
10318 if (!context) return IE_INVALID_CONTEXT;
10319 zfree(context->long_message);
10320 if (!box) return IE_INVAL;
10322 #if HAVE_LIBCURL
10323 if (!box->email || box->email[0] == '\0') {
10324 isds_log_message(context, _("E-mail field is mandatory"));
10325 return IE_INVAL;
10328 /* Scratch box ID */
10329 zfree(box->dbID);
10331 /* Store configuration */
10332 context->type = CTX_TYPE_TESTING_REQUEST_COLLECTOR;
10333 free(context->url);
10334 context->url = strdup("http://78.102.19.203/testbox/request_box.php");
10335 if (!(context->url))
10336 return IE_NOMEM;
10338 /* Prepare CURL handle if not yet connected */
10339 if (!context->curl) {
10340 context->curl = curl_easy_init();
10341 if (!(context->curl))
10342 return IE_ERROR;
10345 /* Build CreateDataBox request */
10346 err = build_CreateDBInput_request(context,
10347 &request, BAD_CAST "CreateDataBox",
10348 box, users, (xmlChar *) former_names, NULL, NULL, NULL, approval);
10349 if (err) goto leave;
10351 /* Send it to server and process response */
10352 err = send_destroy_request_check_response(context,
10353 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
10354 &response, (xmlChar **) refnumber);
10355 if (err) goto leave;
10357 /* Extract box ID */
10358 xpath_ctx = xmlXPathNewContext(response);
10359 if (!xpath_ctx) {
10360 err = IE_ERROR;
10361 goto leave;
10363 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10364 err = IE_ERROR;
10365 goto leave;
10367 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
10369 leave:
10370 xmlXPathFreeObject(result);
10371 xmlXPathFreeContext(xpath_ctx);
10372 xmlFreeDoc(response);
10373 xmlFreeNode(request);
10375 if (!err) {
10376 isds_log(ILF_ISDS, ILL_DEBUG,
10377 _("CreateDataBox request processed by server successfully.\n"));
10379 #else /* not HAVE_LIBCURL */
10380 err = IE_NOTSUP;
10381 #endif
10383 return err;
10387 /* Submit CMS signed message to ISDS to verify its originality. This is
10388 * stronger form of isds_verify_message_hash() because ISDS does more checks
10389 * than simple one (potentialy old weak) hash comparison.
10390 * @context is session context
10391 * @message is memory with raw CMS signed message bit stream
10392 * @length is @message size in bytes
10393 * @return
10394 * IE_SUCCESS if message originates in ISDS
10395 * IE_NOTEQUAL if message is unknown to ISDS
10396 * other code for other errors */
10397 isds_error isds_authenticate_message(struct isds_ctx *context,
10398 const void *message, size_t length) {
10399 isds_error err = IE_SUCCESS;
10400 #if HAVE_LIBCURL
10401 xmlNsPtr isds_ns = NULL;
10402 xmlNodePtr request = NULL;
10403 xmlDocPtr response = NULL;
10404 xmlXPathContextPtr xpath_ctx = NULL;
10405 xmlXPathObjectPtr result = NULL;
10406 _Bool *authentic = NULL;
10407 #endif
10409 if (!context) return IE_INVALID_CONTEXT;
10410 zfree(context->long_message);
10411 if (!message || length == 0) return IE_INVAL;
10413 #if HAVE_LIBCURL
10414 /* Check if connection is established
10415 * TODO: This check should be done downstairs. */
10416 if (!context->curl) return IE_CONNECTION_CLOSED;
10419 /* Build AuthenticateMessage request */
10420 request = xmlNewNode(NULL, BAD_CAST "AuthenticateMessage");
10421 if (!request) {
10422 isds_log_message(context,
10423 _("Could not build AuthenticateMessage request"));
10424 return IE_ERROR;
10426 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
10427 if(!isds_ns) {
10428 isds_log_message(context, _("Could not create ISDS name space"));
10429 xmlFreeNode(request);
10430 return IE_ERROR;
10432 xmlSetNs(request, isds_ns);
10434 /* Insert Base64 encoded message */
10435 err = insert_base64_encoded_string(context, request, NULL, "dmMessage",
10436 message, length);
10437 if (err) goto leave;
10439 /* Send request to server and process response */
10440 err = send_destroy_request_check_response(context,
10441 SERVICE_DM_OPERATIONS, BAD_CAST "AuthenticateMessage", &request,
10442 &response, NULL);
10443 if (err) goto leave;
10446 /* ISDS has decided */
10447 xpath_ctx = xmlXPathNewContext(response);
10448 if (!xpath_ctx) {
10449 err = IE_ERROR;
10450 goto leave;
10452 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10453 err = IE_ERROR;
10454 goto leave;
10457 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic);
10459 if (!authentic) {
10460 isds_log_message(context,
10461 _("Server did not return any response on "
10462 "AuthenticateMessage request"));
10463 err = IE_ISDS;
10464 goto leave;
10466 if (*authentic) {
10467 isds_log(ILF_ISDS, ILL_DEBUG,
10468 _("ISDS authenticated the message successfully\n"));
10469 } else {
10470 isds_log_message(context, _("ISDS does not know the message"));
10471 err = IE_NOTEQUAL;
10475 leave:
10476 free(authentic);
10477 xmlXPathFreeObject(result);
10478 xmlXPathFreeContext(xpath_ctx);
10480 xmlFreeDoc(response);
10481 xmlFreeNode(request);
10482 #else /* not HAVE_LIBCURL */
10483 err = IE_NOTSUP;
10484 #endif
10486 return err;
10489 #undef INSERT_ELEMENT
10490 #undef CHECK_FOR_STRING_LENGTH
10491 #undef INSERT_STRING_ATTRIBUTE
10492 #undef INSERT_ULONGINTNOPTR
10493 #undef INSERT_ULONGINT
10494 #undef INSERT_LONGINT
10495 #undef INSERT_BOOLEAN
10496 #undef INSERT_SCALAR_BOOLEAN
10497 #undef INSERT_STRING
10498 #undef INSERT_STRING_WITH_NS
10499 #undef EXTRACT_STRING_ATTRIBUTE
10500 #undef EXTRACT_ULONGINT
10501 #undef EXTRACT_LONGINT
10502 #undef EXTRACT_BOOLEAN
10503 #undef EXTRACT_STRING
10506 /* Compute hash of message from raw representation and store it into envelope.
10507 * Original hash structure will be destroyed in envelope.
10508 * @context is session context
10509 * @message is message carrying raw XML message blob
10510 * @algorithm is desired hash algorithm to use */
10511 isds_error isds_compute_message_hash(struct isds_ctx *context,
10512 struct isds_message *message, const isds_hash_algorithm algorithm) {
10513 isds_error err = IE_SUCCESS;
10514 const char *nsuri;
10515 void *xml_stream = NULL;
10516 size_t xml_stream_length;
10517 size_t phys_start, phys_end;
10518 char *phys_path = NULL;
10519 struct isds_hash *new_hash = NULL;
10522 if (!context) return IE_INVALID_CONTEXT;
10523 zfree(context->long_message);
10524 if (!message) return IE_INVAL;
10526 if (!message->raw) {
10527 isds_log_message(context,
10528 _("Message does not carry raw representation"));
10529 return IE_INVAL;
10532 switch (message->raw_type) {
10533 case RAWTYPE_INCOMING_MESSAGE:
10534 nsuri = ISDS_NS;
10535 xml_stream = message->raw;
10536 xml_stream_length = message->raw_length;
10537 break;
10539 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
10540 nsuri = SISDS_INCOMING_NS;
10541 xml_stream = message->raw;
10542 xml_stream_length = message->raw_length;
10543 break;
10545 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
10546 nsuri = SISDS_INCOMING_NS;
10547 err = _isds_extract_cms_data(context,
10548 message->raw, message->raw_length,
10549 &xml_stream, &xml_stream_length);
10550 if (err) goto leave;
10551 break;
10553 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
10554 nsuri = SISDS_OUTGOING_NS;
10555 xml_stream = message->raw;
10556 xml_stream_length = message->raw_length;
10557 break;
10559 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
10560 nsuri = SISDS_OUTGOING_NS;
10561 err = _isds_extract_cms_data(context,
10562 message->raw, message->raw_length,
10563 &xml_stream, &xml_stream_length);
10564 if (err) goto leave;
10565 break;
10567 default:
10568 isds_log_message(context, _("Bad raw representation type"));
10569 return IE_INVAL;
10570 break;
10574 /* XXX: Hash is computed from original string representing isds:dmDm
10575 * subtree. That means no encoding, white space, xmlns attributes changes.
10576 * In other words, input for hash can be invalid XML stream. */
10577 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
10578 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
10579 PHYSXML_ELEMENT_SEPARATOR,
10580 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
10581 PHYSXML_ELEMENT_SEPARATOR
10582 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
10583 err = IE_NOMEM;
10584 goto leave;
10586 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
10587 phys_path, &phys_start, &phys_end);
10588 zfree(phys_path);
10589 if (err) {
10590 isds_log_message(context,
10591 _("Substring with isds:dmDM element could not be located "
10592 "in raw message"));
10593 goto leave;
10597 /* Compute hash */
10598 new_hash = calloc(1, sizeof(*new_hash));
10599 if (!new_hash) {
10600 err = IE_NOMEM;
10601 goto leave;
10603 new_hash->algorithm = algorithm;
10604 err = _isds_compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
10605 new_hash);
10606 if (err) {
10607 isds_log_message(context, _("Could not compute message hash"));
10608 goto leave;
10611 /* Save computed hash */
10612 if (!message->envelope) {
10613 message->envelope = calloc(1, sizeof(*message->envelope));
10614 if (!message->envelope) {
10615 err = IE_NOMEM;
10616 goto leave;
10619 isds_hash_free(&message->envelope->hash);
10620 message->envelope->hash = new_hash;
10622 leave:
10623 if (err) {
10624 isds_hash_free(&new_hash);
10627 free(phys_path);
10628 if (xml_stream != message->raw) free(xml_stream);
10629 return err;
10633 /* Compare two hashes.
10634 * @h1 is first hash
10635 * @h2 is another hash
10636 * @return
10637 * IE_SUCCESS if hashes equal
10638 * IE_NOTUNIQ if hashes are comparable, but they don't equal
10639 * IE_ENUM if not comparable, but both structures defined
10640 * IE_INVAL if some of the structures are undefined (NULL)
10641 * IE_ERROR if internal error occurs */
10642 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
10643 if (h1 == NULL || h2 == NULL) return IE_INVAL;
10644 if (h1->algorithm != h2->algorithm) return IE_ENUM;
10645 if (h1->length != h2->length) return IE_ERROR;
10646 if (h1->length > 0 && !h1->value) return IE_ERROR;
10647 if (h2->length > 0 && !h2->value) return IE_ERROR;
10649 for (int i = 0; i < h1->length; i++) {
10650 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
10651 return IE_NOTEQUAL;
10653 return IE_SUCCESS;
10657 /* Check message has gone through ISDS by comparing message hash stored in
10658 * ISDS and locally computed hash. You must provide message with valid raw
10659 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
10660 * This is convenient wrapper for isds_download_message_hash(),
10661 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
10662 * @context is session context
10663 * @message is message with valid raw and envelope member; envelope->hash
10664 * member will be changed during function run. Use envelope on heap only.
10665 * @return
10666 * IE_SUCCESS if message originates in ISDS
10667 * IE_NOTEQUAL if message is unknown to ISDS
10668 * other code for other errors */
10669 isds_error isds_verify_message_hash(struct isds_ctx *context,
10670 struct isds_message *message) {
10671 isds_error err = IE_SUCCESS;
10672 struct isds_hash *downloaded_hash = NULL;
10674 if (!context) return IE_INVALID_CONTEXT;
10675 zfree(context->long_message);
10676 if (!message) return IE_INVAL;
10678 if (!message->envelope) {
10679 isds_log_message(context,
10680 _("Given message structure is missing envelope"));
10681 return IE_INVAL;
10683 if (!message->raw) {
10684 isds_log_message(context,
10685 _("Given message structure is missing raw representation"));
10686 return IE_INVAL;
10689 err = isds_download_message_hash(context, message->envelope->dmID,
10690 &downloaded_hash);
10691 if (err) goto leave;
10693 err = isds_compute_message_hash(context, message,
10694 downloaded_hash->algorithm);
10695 if (err) goto leave;
10697 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
10699 leave:
10700 isds_hash_free(&downloaded_hash);
10701 return err;
10705 /* Search for document by document ID in list of documents. IDs are compared
10706 * as UTF-8 string.
10707 * @documents is list of isds_documents
10708 * @id is document identifier
10709 * @return first matching document or NULL. */
10710 const struct isds_document *isds_find_document_by_id(
10711 const struct isds_list *documents, const char *id) {
10712 const struct isds_list *item;
10713 const struct isds_document *document;
10715 for (item = documents; item; item = item->next) {
10716 document = (struct isds_document *) item->data;
10717 if (!document) continue;
10719 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
10720 return document;
10723 return NULL;
10727 /* Normalize @mime_type to be proper MIME type.
10728 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
10729 * guess regular MIME type (e.g. "application/pdf").
10730 * @mime_type is UTF-8 encoded MIME type to fix
10731 * @return original @mime_type if no better interpretation exists, or array to
10732 * constant static UTF-8 encoded string with proper MIME type. */
10733 char *isds_normalize_mime_type(const char* mime_type) {
10734 if (!mime_type) return NULL;
10736 for (int offset = 0;
10737 offset < sizeof(extension_map_mime)/sizeof(extension_map_mime[0]);
10738 offset += 2) {
10739 if (!xmlStrcmp((const xmlChar*) mime_type, extension_map_mime[offset]))
10740 return (char *) extension_map_mime[offset + 1];
10743 return (char *) mime_type;
10747 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
10748 struct isds_message **message);
10749 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
10750 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
10751 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
10752 struct isds_address **address);
10754 int isds_message_free(struct isds_message **message);
10755 int isds_address_free(struct isds_address **address);
10759 /* Makes known all relevant namespaces to given XPath context
10760 * @xpath_ctx is XPath context
10761 * @message_ns selects proper message name space. Unsigned and signed
10762 * messages and delivery info's differ in prefix and URI. */
10763 _hidden isds_error _isds_register_namespaces(xmlXPathContextPtr xpath_ctx,
10764 const message_ns_type message_ns) {
10765 const xmlChar *message_namespace = NULL;
10767 if (!xpath_ctx) return IE_ERROR;
10769 switch(message_ns) {
10770 case MESSAGE_NS_1:
10771 message_namespace = BAD_CAST ISDS1_NS; break;
10772 case MESSAGE_NS_UNSIGNED:
10773 message_namespace = BAD_CAST ISDS_NS; break;
10774 case MESSAGE_NS_SIGNED_INCOMING:
10775 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
10776 case MESSAGE_NS_SIGNED_OUTGOING:
10777 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
10778 case MESSAGE_NS_SIGNED_DELIVERY:
10779 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
10780 default:
10781 return IE_ENUM;
10784 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
10785 return IE_ERROR;
10786 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
10787 return IE_ERROR;
10788 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
10789 return IE_ERROR;
10790 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
10791 return IE_ERROR;
10792 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
10793 return IE_ERROR;
10794 return IE_SUCCESS;