test: server: push TLS manually
[libisds.git] / src / isds.c
blobbf4b7bb44c0c5b09099195c76ea9893b4b2f094e
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 const 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 * Send 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 xmlXPathFreeObject(result); \
2363 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2364 if (NULL == (result)) { \
2365 err = IE_ERROR; \
2366 goto leave; \
2368 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2369 if (result->nodesetval->nodeNr > 1) { \
2370 isds_printf_message(context, _("Multiple %s element"), element); \
2371 err = IE_ERROR; \
2372 goto leave; \
2374 (string) = (char *) \
2375 xmlXPathCastNodeSetToString(result->nodesetval); \
2376 if (NULL == (string)) { \
2377 err = IE_ERROR; \
2378 goto leave; \
2383 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2385 char *string = NULL; \
2386 EXTRACT_STRING(element, string); \
2388 if (string) { \
2389 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2390 if (!(booleanPtr)) { \
2391 free(string); \
2392 err = IE_NOMEM; \
2393 goto leave; \
2396 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2397 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2398 *(booleanPtr) = 1; \
2399 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2400 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2401 *(booleanPtr) = 0; \
2402 else { \
2403 char *string_locale = _isds_utf82locale((char*)string); \
2404 isds_printf_message(context, \
2405 _("%s value is not valid boolean: %s"), \
2406 element, string_locale); \
2407 free(string_locale); \
2408 free(string); \
2409 err = IE_ERROR; \
2410 goto leave; \
2413 free(string); \
2417 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2419 char *string = NULL; \
2420 EXTRACT_STRING(element, string); \
2421 if (string) { \
2422 long int number; \
2423 char *endptr; \
2425 number = strtol((char*)string, &endptr, 10); \
2427 if (*endptr != '\0') { \
2428 char *string_locale = _isds_utf82locale((char *)string); \
2429 isds_printf_message(context, \
2430 _("%s is not valid integer: %s"), \
2431 element, string_locale); \
2432 free(string_locale); \
2433 free(string); \
2434 err = IE_ISDS; \
2435 goto leave; \
2438 if (number == LONG_MIN || number == LONG_MAX) { \
2439 char *string_locale = _isds_utf82locale((char *)string); \
2440 isds_printf_message(context, \
2441 _("%s value out of range of long int: %s"), \
2442 element, string_locale); \
2443 free(string_locale); \
2444 free(string); \
2445 err = IE_ERROR; \
2446 goto leave; \
2449 free(string); string = NULL; \
2451 if (!(preallocated)) { \
2452 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2453 if (!(longintPtr)) { \
2454 err = IE_NOMEM; \
2455 goto leave; \
2458 *(longintPtr) = number; \
2462 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2464 char *string = NULL; \
2465 EXTRACT_STRING(element, string); \
2466 if (string) { \
2467 long int number; \
2468 char *endptr; \
2470 number = strtol((char*)string, &endptr, 10); \
2472 if (*endptr != '\0') { \
2473 char *string_locale = _isds_utf82locale((char *)string); \
2474 isds_printf_message(context, \
2475 _("%s is not valid integer: %s"), \
2476 element, string_locale); \
2477 free(string_locale); \
2478 free(string); \
2479 err = IE_ISDS; \
2480 goto leave; \
2483 if (number == LONG_MIN || number == LONG_MAX) { \
2484 char *string_locale = _isds_utf82locale((char *)string); \
2485 isds_printf_message(context, \
2486 _("%s value out of range of long int: %s"), \
2487 element, string_locale); \
2488 free(string_locale); \
2489 free(string); \
2490 err = IE_ERROR; \
2491 goto leave; \
2494 free(string); string = NULL; \
2495 if (number < 0) { \
2496 isds_printf_message(context, \
2497 _("%s value is negative: %ld"), element, number); \
2498 err = IE_ERROR; \
2499 goto leave; \
2502 if (!(preallocated)) { \
2503 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2504 if (!(ulongintPtr)) { \
2505 err = IE_NOMEM; \
2506 goto leave; \
2509 *(ulongintPtr) = number; \
2513 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2514 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2515 NULL); \
2516 if ((required) && (!string)) { \
2517 char *attribute_locale = _isds_utf82locale(attribute); \
2518 char *element_locale = \
2519 _isds_utf82locale((char *)xpath_ctx->node->name); \
2520 isds_printf_message(context, \
2521 _("Could not extract required %s attribute value from " \
2522 "%s element"), attribute_locale, element_locale); \
2523 free(element_locale); \
2524 free(attribute_locale); \
2525 err = IE_ERROR; \
2526 goto leave; \
2531 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2533 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2534 (xmlChar *) (string)); \
2535 if (!node) { \
2536 isds_printf_message(context, \
2537 _("Could not add %s child to %s element"), \
2538 element, (parent)->name); \
2539 err = IE_ERROR; \
2540 goto leave; \
2544 #define INSERT_STRING(parent, element, string) \
2545 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2547 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2549 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2550 else { INSERT_STRING(parent, element, "false"); } \
2553 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2555 if (booleanPtr) { \
2556 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2557 } else { \
2558 INSERT_STRING(parent, element, NULL); \
2562 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2563 if ((longintPtr)) { \
2564 /* FIXME: locale sensitive */ \
2565 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2566 err = IE_NOMEM; \
2567 goto leave; \
2569 INSERT_STRING(parent, element, buffer) \
2570 free(buffer); (buffer) = NULL; \
2571 } else { INSERT_STRING(parent, element, NULL) } \
2574 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2575 if ((ulongintPtr)) { \
2576 /* FIXME: locale sensitive */ \
2577 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2578 err = IE_NOMEM; \
2579 goto leave; \
2581 INSERT_STRING(parent, element, buffer) \
2582 free(buffer); (buffer) = NULL; \
2583 } else { INSERT_STRING(parent, element, NULL) } \
2586 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2588 /* FIXME: locale sensitive */ \
2589 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2590 err = IE_NOMEM; \
2591 goto leave; \
2593 INSERT_STRING(parent, element, buffer) \
2594 free(buffer); (buffer) = NULL; \
2597 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2598 * new attribute. */
2599 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2601 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2602 (xmlChar *) (string)); \
2603 if (!attribute_node) { \
2604 isds_printf_message(context, _("Could not add %s " \
2605 "attribute to %s element"), \
2606 (attribute), (parent)->name); \
2607 err = IE_ERROR; \
2608 goto leave; \
2612 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2613 if (string) { \
2614 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2615 if (length > (maximum)) { \
2616 isds_printf_message(context, \
2617 ngettext("%s has more than %d characters", \
2618 "%s has more than %d characters", (maximum)), \
2619 (name), (maximum)); \
2620 err = IE_2BIG; \
2621 goto leave; \
2623 if (length < (minimum)) { \
2624 isds_printf_message(context, \
2625 ngettext("%s has less than %d characters", \
2626 "%s has less than %d characters", (minimum)), \
2627 (name), (minimum)); \
2628 err = IE_2SMALL; \
2629 goto leave; \
2634 #define INSERT_ELEMENT(child, parent, element) \
2636 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2637 if (!(child)) { \
2638 isds_printf_message(context, \
2639 _("Could not add %s child to %s element"), \
2640 (element), (parent)->name); \
2641 err = IE_ERROR; \
2642 goto leave; \
2647 /* Find child element by name in given XPath context and switch context onto
2648 * it. The child must be uniq and must exist. Otherwise fails.
2649 * @context is ISDS context
2650 * @child is child element name
2651 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2652 * into it child. In error case, the @xpath_ctx keeps original value. */
2653 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
2654 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
2655 isds_error err = IE_SUCCESS;
2656 xmlXPathObjectPtr result = NULL;
2658 if (!context) return IE_INVALID_CONTEXT;
2659 if (!child || !xpath_ctx) return IE_INVAL;
2661 /* Find child */
2662 result = xmlXPathEvalExpression(child, xpath_ctx);
2663 if (!result) {
2664 err = IE_XML;
2665 goto leave;
2668 /* No match */
2669 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2670 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2671 char *child_locale = _isds_utf82locale((char*) child);
2672 isds_printf_message(context,
2673 _("%s element does not contain %s child"),
2674 parent_locale, child_locale);
2675 free(child_locale);
2676 free(parent_locale);
2677 err = IE_NOEXIST;
2678 goto leave;
2681 /* More matches */
2682 if (result->nodesetval->nodeNr > 1) {
2683 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2684 char *child_locale = _isds_utf82locale((char*) child);
2685 isds_printf_message(context,
2686 _("%s element contains multiple %s children"),
2687 parent_locale, child_locale);
2688 free(child_locale);
2689 free(parent_locale);
2690 err = IE_NOTUNIQ;
2691 goto leave;
2694 /* Switch context */
2695 xpath_ctx->node = result->nodesetval->nodeTab[0];
2697 leave:
2698 xmlXPathFreeObject(result);
2699 return err;
2704 #if HAVE_LIBCURL
2705 /* Find and convert XSD:gPersonName group in current node into structure
2706 * @context is ISDS context
2707 * @personName is automatically reallocated person name structure. If no member
2708 * value is found, will be freed.
2709 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2710 * elements
2711 * In case of error @personName will be freed. */
2712 static isds_error extract_gPersonName(struct isds_ctx *context,
2713 struct isds_PersonName **personName, xmlXPathContextPtr xpath_ctx) {
2714 isds_error err = IE_SUCCESS;
2715 xmlXPathObjectPtr result = NULL;
2717 if (!context) return IE_INVALID_CONTEXT;
2718 if (!personName) return IE_INVAL;
2719 isds_PersonName_free(personName);
2720 if (!xpath_ctx) return IE_INVAL;
2723 *personName = calloc(1, sizeof(**personName));
2724 if (!*personName) {
2725 err = IE_NOMEM;
2726 goto leave;
2729 EXTRACT_STRING("isds:pnFirstName", (*personName)->pnFirstName);
2730 EXTRACT_STRING("isds:pnMiddleName", (*personName)->pnMiddleName);
2731 EXTRACT_STRING("isds:pnLastName", (*personName)->pnLastName);
2732 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName)->pnLastNameAtBirth);
2734 if (!(*personName)->pnFirstName && !(*personName)->pnMiddleName &&
2735 !(*personName)->pnLastName && !(*personName)->pnLastNameAtBirth)
2736 isds_PersonName_free(personName);
2738 leave:
2739 if (err) isds_PersonName_free(personName);
2740 xmlXPathFreeObject(result);
2741 return err;
2745 /* Find and convert XSD:gAddress group in current node into structure
2746 * @context is ISDS context
2747 * @address is automatically reallocated address structure. If no member
2748 * value is found, will be freed.
2749 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2750 * elements
2751 * In case of error @address will be freed. */
2752 static isds_error extract_gAddress(struct isds_ctx *context,
2753 struct isds_Address **address, xmlXPathContextPtr xpath_ctx) {
2754 isds_error err = IE_SUCCESS;
2755 xmlXPathObjectPtr result = NULL;
2757 if (!context) return IE_INVALID_CONTEXT;
2758 if (!address) return IE_INVAL;
2759 isds_Address_free(address);
2760 if (!xpath_ctx) return IE_INVAL;
2763 *address = calloc(1, sizeof(**address));
2764 if (!*address) {
2765 err = IE_NOMEM;
2766 goto leave;
2769 EXTRACT_STRING("isds:adCity", (*address)->adCity);
2770 EXTRACT_STRING("isds:adStreet", (*address)->adStreet);
2771 EXTRACT_STRING("isds:adNumberInStreet", (*address)->adNumberInStreet);
2772 EXTRACT_STRING("isds:adNumberInMunicipality",
2773 (*address)->adNumberInMunicipality);
2774 EXTRACT_STRING("isds:adZipCode", (*address)->adZipCode);
2775 EXTRACT_STRING("isds:adState", (*address)->adState);
2777 if (!(*address)->adCity && !(*address)->adStreet &&
2778 !(*address)->adNumberInStreet &&
2779 !(*address)->adNumberInMunicipality &&
2780 !(*address)->adZipCode && !(*address)->adState)
2781 isds_Address_free(address);
2783 leave:
2784 if (err) isds_Address_free(address);
2785 xmlXPathFreeObject(result);
2786 return err;
2790 /* Find and convert isds:biDate element in current node into structure
2791 * @context is ISDS context
2792 * @biDate is automatically reallocated birth date structure. If no member
2793 * value is found, will be freed.
2794 * @xpath_ctx is XPath context with current node as parent for isds:biDate
2795 * element
2796 * In case of error @biDate will be freed. */
2797 static isds_error extract_BiDate(struct isds_ctx *context,
2798 struct tm **biDate, xmlXPathContextPtr xpath_ctx) {
2799 isds_error err = IE_SUCCESS;
2800 xmlXPathObjectPtr result = NULL;
2801 char *string = NULL;
2803 if (!context) return IE_INVALID_CONTEXT;
2804 if (!biDate) return IE_INVAL;
2805 zfree(*biDate);
2806 if (!xpath_ctx) return IE_INVAL;
2808 EXTRACT_STRING("isds:biDate", string);
2809 if (string) {
2810 *biDate = calloc(1, sizeof(**biDate));
2811 if (!*biDate) {
2812 err = IE_NOMEM;
2813 goto leave;
2815 err = datestring2tm((xmlChar *)string, *biDate);
2816 if (err) {
2817 if (err == IE_NOTSUP) {
2818 err = IE_ISDS;
2819 char *string_locale = _isds_utf82locale(string);
2820 isds_printf_message(context,
2821 _("Invalid isds:biDate value: %s"), string_locale);
2822 free(string_locale);
2824 goto leave;
2828 leave:
2829 if (err) zfree(*biDate);
2830 free(string);
2831 xmlXPathFreeObject(result);
2832 return err;
2836 /* Convert isds:dBOwnerInfo XML tree into structure
2837 * @context is ISDS context
2838 * @db_owner_info is automatically reallocated box owner info structure
2839 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
2840 * In case of error @db_owner_info will be freed. */
2841 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
2842 struct isds_DbOwnerInfo **db_owner_info,
2843 xmlXPathContextPtr xpath_ctx) {
2844 isds_error err = IE_SUCCESS;
2845 xmlXPathObjectPtr result = NULL;
2846 char *string = NULL;
2848 if (!context) return IE_INVALID_CONTEXT;
2849 if (!db_owner_info) return IE_INVAL;
2850 isds_DbOwnerInfo_free(db_owner_info);
2851 if (!xpath_ctx) return IE_INVAL;
2854 *db_owner_info = calloc(1, sizeof(**db_owner_info));
2855 if (!*db_owner_info) {
2856 err = IE_NOMEM;
2857 goto leave;
2860 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
2862 EXTRACT_STRING("isds:dbType", string);
2863 if (string) {
2864 (*db_owner_info)->dbType =
2865 calloc(1, sizeof(*((*db_owner_info)->dbType)));
2866 if (!(*db_owner_info)->dbType) {
2867 err = IE_NOMEM;
2868 goto leave;
2870 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
2871 if (err) {
2872 zfree((*db_owner_info)->dbType);
2873 if (err == IE_ENUM) {
2874 err = IE_ISDS;
2875 char *string_locale = _isds_utf82locale(string);
2876 isds_printf_message(context, _("Unknown isds:dbType: %s"),
2877 string_locale);
2878 free(string_locale);
2880 goto leave;
2882 zfree(string);
2885 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
2887 err = extract_gPersonName(context, &(*db_owner_info)->personName,
2888 xpath_ctx);
2889 if (err) goto leave;
2891 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
2893 (*db_owner_info)->birthInfo =
2894 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
2895 if (!(*db_owner_info)->birthInfo) {
2896 err = IE_NOMEM;
2897 goto leave;
2899 err = extract_BiDate(context, &(*db_owner_info)->birthInfo->biDate,
2900 xpath_ctx);
2901 if (err) goto leave;
2902 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
2903 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
2904 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
2905 if (!(*db_owner_info)->birthInfo->biDate &&
2906 !(*db_owner_info)->birthInfo->biCity &&
2907 !(*db_owner_info)->birthInfo->biCounty &&
2908 !(*db_owner_info)->birthInfo->biState)
2909 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
2911 err = extract_gAddress(context, &(*db_owner_info)->address, xpath_ctx);
2912 if (err) goto leave;
2914 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
2915 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
2916 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
2917 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
2918 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
2920 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
2922 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
2923 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
2924 (*db_owner_info)->dbOpenAddressing);
2926 leave:
2927 if (err) isds_DbOwnerInfo_free(db_owner_info);
2928 free(string);
2929 xmlXPathFreeObject(result);
2930 return err;
2934 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
2935 * @context is session context
2936 * @owner is libisds structure with box description
2937 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
2938 static isds_error insert_DbOwnerInfo(struct isds_ctx *context,
2939 const struct isds_DbOwnerInfo *owner, xmlNodePtr db_owner_info) {
2941 isds_error err = IE_SUCCESS;
2942 xmlNodePtr node;
2943 xmlChar *string = NULL;
2945 if (!context) return IE_INVALID_CONTEXT;
2946 if (!owner || !db_owner_info) return IE_INVAL;
2949 /* Build XSD:tDbOwnerInfo */
2950 CHECK_FOR_STRING_LENGTH(owner->dbID, 0, 7, "dbID")
2951 INSERT_STRING(db_owner_info, "dbID", owner->dbID);
2953 /* dbType */
2954 if (owner->dbType) {
2955 const xmlChar *type_string = isds_DbType2string(*(owner->dbType));
2956 if (!type_string) {
2957 isds_printf_message(context, _("Invalid dbType value: %d"),
2958 *(owner->dbType));
2959 err = IE_ENUM;
2960 goto leave;
2962 INSERT_STRING(db_owner_info, "dbType", type_string);
2964 INSERT_STRING(db_owner_info, "ic", owner->ic);
2965 if (owner->personName) {
2966 INSERT_STRING(db_owner_info, "pnFirstName",
2967 owner->personName->pnFirstName);
2968 INSERT_STRING(db_owner_info, "pnMiddleName",
2969 owner->personName->pnMiddleName);
2970 INSERT_STRING(db_owner_info, "pnLastName",
2971 owner->personName->pnLastName);
2972 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
2973 owner->personName->pnLastNameAtBirth);
2975 INSERT_STRING(db_owner_info, "firmName", owner->firmName);
2976 if (owner->birthInfo) {
2977 if (owner->birthInfo->biDate) {
2978 if (!tm2datestring(owner->birthInfo->biDate, &string))
2979 INSERT_STRING(db_owner_info, "biDate", string);
2980 free(string); string = NULL;
2982 INSERT_STRING(db_owner_info, "biCity", owner->birthInfo->biCity);
2983 INSERT_STRING(db_owner_info, "biCounty", owner->birthInfo->biCounty);
2984 INSERT_STRING(db_owner_info, "biState", owner->birthInfo->biState);
2986 if (owner->address) {
2987 INSERT_STRING(db_owner_info, "adCity", owner->address->adCity);
2988 INSERT_STRING(db_owner_info, "adStreet", owner->address->adStreet);
2989 INSERT_STRING(db_owner_info, "adNumberInStreet",
2990 owner->address->adNumberInStreet);
2991 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
2992 owner->address->adNumberInMunicipality);
2993 INSERT_STRING(db_owner_info, "adZipCode", owner->address->adZipCode);
2994 INSERT_STRING(db_owner_info, "adState", owner->address->adState);
2996 INSERT_STRING(db_owner_info, "nationality", owner->nationality);
2997 INSERT_STRING(db_owner_info, "email", owner->email);
2998 INSERT_STRING(db_owner_info, "telNumber", owner->telNumber);
3000 CHECK_FOR_STRING_LENGTH(owner->identifier, 0, 20, "identifier")
3001 INSERT_STRING(db_owner_info, "identifier", owner->identifier);
3003 CHECK_FOR_STRING_LENGTH(owner->registryCode, 0, 5, "registryCode")
3004 INSERT_STRING(db_owner_info, "registryCode", owner->registryCode);
3006 INSERT_LONGINT(db_owner_info, "dbState", owner->dbState, string);
3008 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", owner->dbEffectiveOVM);
3009 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
3010 owner->dbOpenAddressing);
3012 leave:
3013 free(string);
3014 return err;
3018 /* Convert XSD:tDbUserInfo XML tree into structure
3019 * @context is ISDS context
3020 * @db_user_info is automatically reallocated user info structure
3021 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3022 * In case of error @db_user_info will be freed. */
3023 static isds_error extract_DbUserInfo(struct isds_ctx *context,
3024 struct isds_DbUserInfo **db_user_info, xmlXPathContextPtr xpath_ctx) {
3025 isds_error err = IE_SUCCESS;
3026 xmlXPathObjectPtr result = NULL;
3027 char *string = NULL;
3029 if (!context) return IE_INVALID_CONTEXT;
3030 if (!db_user_info) return IE_INVAL;
3031 isds_DbUserInfo_free(db_user_info);
3032 if (!xpath_ctx) return IE_INVAL;
3035 *db_user_info = calloc(1, sizeof(**db_user_info));
3036 if (!*db_user_info) {
3037 err = IE_NOMEM;
3038 goto leave;
3041 EXTRACT_STRING("isds:userID", (*db_user_info)->userID);
3043 EXTRACT_STRING("isds:userType", string);
3044 if (string) {
3045 (*db_user_info)->userType =
3046 calloc(1, sizeof(*((*db_user_info)->userType)));
3047 if (!(*db_user_info)->userType) {
3048 err = IE_NOMEM;
3049 goto leave;
3051 err = string2isds_UserType((xmlChar *)string,
3052 (*db_user_info)->userType);
3053 if (err) {
3054 zfree((*db_user_info)->userType);
3055 if (err == IE_ENUM) {
3056 err = IE_ISDS;
3057 char *string_locale = _isds_utf82locale(string);
3058 isds_printf_message(context,
3059 _("Unknown isds:userType value: %s"), string_locale);
3060 free(string_locale);
3062 goto leave;
3064 zfree(string);
3067 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info)->userPrivils, 0);
3069 (*db_user_info)->personName =
3070 calloc(1, sizeof(*((*db_user_info)->personName)));
3071 if (!(*db_user_info)->personName) {
3072 err = IE_NOMEM;
3073 goto leave;
3076 err = extract_gPersonName(context, &(*db_user_info)->personName,
3077 xpath_ctx);
3078 if (err) goto leave;
3080 err = extract_gAddress(context, &(*db_user_info)->address, xpath_ctx);
3081 if (err) goto leave;
3083 err = extract_BiDate(context, &(*db_user_info)->biDate, xpath_ctx);
3084 if (err) goto leave;
3086 EXTRACT_STRING("isds:ic", (*db_user_info)->ic);
3087 EXTRACT_STRING("isds:firmName", (*db_user_info)->firmName);
3089 EXTRACT_STRING("isds:caStreet", (*db_user_info)->caStreet);
3090 EXTRACT_STRING("isds:caCity", (*db_user_info)->caCity);
3091 EXTRACT_STRING("isds:caZipCode", (*db_user_info)->caZipCode);
3093 /* ???: Default value is "CZ" according specification. Should we provide
3094 * it? */
3095 EXTRACT_STRING("isds:caState", (*db_user_info)->caState);
3097 leave:
3098 if (err) isds_DbUserInfo_free(db_user_info);
3099 free(string);
3100 xmlXPathFreeObject(result);
3101 return err;
3105 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3106 * @context is session context
3107 * @user is libisds structure with user description
3108 * @db_user_info is XML element of XSD:tDbUserInfo */
3109 static isds_error insert_DbUserInfo(struct isds_ctx *context,
3110 const struct isds_DbUserInfo *user, xmlNodePtr db_user_info) {
3112 isds_error err = IE_SUCCESS;
3113 xmlNodePtr node;
3114 xmlChar *string = NULL;
3116 if (!context) return IE_INVALID_CONTEXT;
3117 if (!user || !db_user_info) return IE_INVAL;
3119 /* Build XSD:tDbUserInfo */
3120 if (user->personName) {
3121 INSERT_STRING(db_user_info, "pnFirstName",
3122 user->personName->pnFirstName);
3123 INSERT_STRING(db_user_info, "pnMiddleName",
3124 user->personName->pnMiddleName);
3125 INSERT_STRING(db_user_info, "pnLastName",
3126 user->personName->pnLastName);
3127 INSERT_STRING(db_user_info, "pnLastNameAtBirth",
3128 user->personName->pnLastNameAtBirth);
3130 if (user->address) {
3131 INSERT_STRING(db_user_info, "adCity", user->address->adCity);
3132 INSERT_STRING(db_user_info, "adStreet", user->address->adStreet);
3133 INSERT_STRING(db_user_info, "adNumberInStreet",
3134 user->address->adNumberInStreet);
3135 INSERT_STRING(db_user_info, "adNumberInMunicipality",
3136 user->address->adNumberInMunicipality);
3137 INSERT_STRING(db_user_info, "adZipCode", user->address->adZipCode);
3138 INSERT_STRING(db_user_info, "adState", user->address->adState);
3140 if (user->biDate) {
3141 if (!tm2datestring(user->biDate, &string))
3142 INSERT_STRING(db_user_info, "biDate", string);
3143 zfree(string);
3145 CHECK_FOR_STRING_LENGTH(user->userID, 6, 12, "userID");
3146 INSERT_STRING(db_user_info, "userID", user->userID);
3148 /* userType */
3149 if (user->userType) {
3150 const xmlChar *type_string = isds_UserType2string(*(user->userType));
3151 if (!type_string) {
3152 isds_printf_message(context, _("Invalid userType value: %d"),
3153 *(user->userType));
3154 err = IE_ENUM;
3155 goto leave;
3157 INSERT_STRING(db_user_info, "userType", type_string);
3160 INSERT_LONGINT(db_user_info, "userPrivils", user->userPrivils, string);
3161 CHECK_FOR_STRING_LENGTH(user->ic, 0, 8, "ic")
3162 INSERT_STRING(db_user_info, "ic", user->ic);
3163 CHECK_FOR_STRING_LENGTH(user->firmName, 0, 100, "firmName")
3164 INSERT_STRING(db_user_info, "firmName", user->firmName);
3165 INSERT_STRING(db_user_info, "caStreet", user->caStreet);
3166 INSERT_STRING(db_user_info, "caCity", user->caCity);
3167 INSERT_STRING(db_user_info, "caZipCode", user->caZipCode);
3168 INSERT_STRING(db_user_info, "caState", user->caState);
3170 leave:
3171 free(string);
3172 return err;
3176 /* Convert XSD:tPDZRec XML tree into structure
3177 * @context is ISDS context
3178 * @permission is automatically reallocated commercial permission structure
3179 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3180 * In case of error @permission will be freed. */
3181 static isds_error extract_DbPDZRecord(struct isds_ctx *context,
3182 struct isds_commercial_permission **permission,
3183 xmlXPathContextPtr xpath_ctx) {
3184 isds_error err = IE_SUCCESS;
3185 xmlXPathObjectPtr result = NULL;
3186 char *string = NULL;
3188 if (!context) return IE_INVALID_CONTEXT;
3189 if (!permission) return IE_INVAL;
3190 isds_commercial_permission_free(permission);
3191 if (!xpath_ctx) return IE_INVAL;
3194 *permission = calloc(1, sizeof(**permission));
3195 if (!*permission) {
3196 err = IE_NOMEM;
3197 goto leave;
3200 EXTRACT_STRING("isds:PDZType", string);
3201 if (string) {
3202 err = string2isds_payment_type((xmlChar *)string,
3203 &(*permission)->type);
3204 if (err) {
3205 if (err == IE_ENUM) {
3206 err = IE_ISDS;
3207 char *string_locale = _isds_utf82locale(string);
3208 isds_printf_message(context,
3209 _("Unknown isds:PDZType value: %s"), string_locale);
3210 free(string_locale);
3212 goto leave;
3214 zfree(string);
3217 EXTRACT_STRING("isds:PDZRecip", (*permission)->recipient);
3218 EXTRACT_STRING("isds:PDZPayer", (*permission)->payer);
3220 EXTRACT_STRING("isds:PDZExpire", string);
3221 if (string) {
3222 err = timestring2timeval((xmlChar *) string,
3223 &((*permission)->expiration));
3224 if (err) {
3225 char *string_locale = _isds_utf82locale(string);
3226 if (err == IE_DATE) err = IE_ISDS;
3227 isds_printf_message(context,
3228 _("Could not convert PDZExpire as ISO time: %s"),
3229 string_locale);
3230 free(string_locale);
3231 goto leave;
3233 zfree(string);
3236 EXTRACT_ULONGINT("isds:PDZCnt", (*permission)->count, 0);
3237 EXTRACT_STRING("isds:ODZIdent", (*permission)->reply_identifier);
3239 leave:
3240 if (err) isds_commercial_permission_free(permission);
3241 free(string);
3242 xmlXPathFreeObject(result);
3243 return err;
3247 #endif /* HAVE_LIBCURL */
3250 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3251 * isds_envelope structure. The envelope is automatically allocated but not
3252 * reallocated. The date are just appended into envelope structure.
3253 * @context is ISDS context
3254 * @envelope is automatically allocated message envelope structure
3255 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3256 * In case of error @envelope will be freed. */
3257 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
3258 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3259 isds_error err = IE_SUCCESS;
3260 xmlXPathObjectPtr result = NULL;
3262 if (!context) return IE_INVALID_CONTEXT;
3263 if (!envelope) return IE_INVAL;
3264 if (!xpath_ctx) return IE_INVAL;
3267 if (!*envelope) {
3268 /* Allocate envelope */
3269 *envelope = calloc(1, sizeof(**envelope));
3270 if (!*envelope) {
3271 err = IE_NOMEM;
3272 goto leave;
3274 } else {
3275 /* Else free former data */
3276 zfree((*envelope)->dmSenderOrgUnit);
3277 zfree((*envelope)->dmSenderOrgUnitNum);
3278 zfree((*envelope)->dbIDRecipient);
3279 zfree((*envelope)->dmRecipientOrgUnit);
3280 zfree((*envelope)->dmSenderOrgUnitNum);
3281 zfree((*envelope)->dmToHands);
3282 zfree((*envelope)->dmAnnotation);
3283 zfree((*envelope)->dmRecipientRefNumber);
3284 zfree((*envelope)->dmSenderRefNumber);
3285 zfree((*envelope)->dmRecipientIdent);
3286 zfree((*envelope)->dmSenderIdent);
3287 zfree((*envelope)->dmLegalTitleLaw);
3288 zfree((*envelope)->dmLegalTitleYear);
3289 zfree((*envelope)->dmLegalTitleSect);
3290 zfree((*envelope)->dmLegalTitlePar);
3291 zfree((*envelope)->dmLegalTitlePoint);
3292 zfree((*envelope)->dmPersonalDelivery);
3293 zfree((*envelope)->dmAllowSubstDelivery);
3296 /* Extract envelope elements added by sender or ISDS
3297 * (XSD: gMessageEnvelopeSub type) */
3298 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
3299 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3300 (*envelope)->dmSenderOrgUnitNum, 0);
3301 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
3302 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
3303 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3304 (*envelope)->dmSenderOrgUnitNum, 0);
3305 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
3306 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
3307 EXTRACT_STRING("isds:dmRecipientRefNumber",
3308 (*envelope)->dmRecipientRefNumber);
3309 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
3310 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
3311 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
3313 /* Extract envelope elements regarding law reference */
3314 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
3315 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
3316 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
3317 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
3318 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
3320 /* Extract envelope other elements */
3321 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
3322 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3323 (*envelope)->dmAllowSubstDelivery);
3325 leave:
3326 if (err) isds_envelope_free(envelope);
3327 xmlXPathFreeObject(result);
3328 return err;
3333 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3334 * isds_envelope structure. The envelope is automatically allocated but not
3335 * reallocated. The date are just appended into envelope structure.
3336 * @context is ISDS context
3337 * @envelope is automatically allocated message envelope structure
3338 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3339 * In case of error @envelope will be freed. */
3340 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
3341 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3342 isds_error err = IE_SUCCESS;
3343 xmlXPathObjectPtr result = NULL;
3345 if (!context) return IE_INVALID_CONTEXT;
3346 if (!envelope) return IE_INVAL;
3347 if (!xpath_ctx) return IE_INVAL;
3350 if (!*envelope) {
3351 /* Allocate envelope */
3352 *envelope = calloc(1, sizeof(**envelope));
3353 if (!*envelope) {
3354 err = IE_NOMEM;
3355 goto leave;
3357 } else {
3358 /* Else free former data */
3359 zfree((*envelope)->dmID);
3360 zfree((*envelope)->dbIDSender);
3361 zfree((*envelope)->dmSender);
3362 zfree((*envelope)->dmSenderAddress);
3363 zfree((*envelope)->dmSenderType);
3364 zfree((*envelope)->dmRecipient);
3365 zfree((*envelope)->dmRecipientAddress);
3366 zfree((*envelope)->dmAmbiguousRecipient);
3369 /* Extract envelope elements added by ISDS
3370 * (XSD: gMessageEnvelope type) */
3371 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
3372 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
3373 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
3374 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
3375 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3376 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
3377 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
3378 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
3379 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3380 (*envelope)->dmAmbiguousRecipient);
3382 /* Extract envelope elements added by sender and ISDS
3383 * (XSD: gMessageEnvelope type) */
3384 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
3385 if (err) goto leave;
3387 leave:
3388 if (err) isds_envelope_free(envelope);
3389 xmlXPathFreeObject(result);
3390 return err;
3394 /* Convert other envelope elements from XML tree into isds_envelope structure:
3395 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3396 * The envelope is automatically allocated but not reallocated.
3397 * The data are just appended into envelope structure.
3398 * @context is ISDS context
3399 * @envelope is automatically allocated message envelope structure
3400 * @xpath_ctx is XPath context with current node as parent desired elements
3401 * In case of error @envelope will be freed. */
3402 static isds_error append_status_size_times(struct isds_ctx *context,
3403 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3404 isds_error err = IE_SUCCESS;
3405 xmlXPathObjectPtr result = NULL;
3406 char *string = NULL;
3407 unsigned long int *unumber = NULL;
3409 if (!context) return IE_INVALID_CONTEXT;
3410 if (!envelope) return IE_INVAL;
3411 if (!xpath_ctx) return IE_INVAL;
3414 if (!*envelope) {
3415 /* Allocate new */
3416 *envelope = calloc(1, sizeof(**envelope));
3417 if (!*envelope) {
3418 err = IE_NOMEM;
3419 goto leave;
3421 } else {
3422 /* Free old data */
3423 zfree((*envelope)->dmMessageStatus);
3424 zfree((*envelope)->dmAttachmentSize);
3425 zfree((*envelope)->dmDeliveryTime);
3426 zfree((*envelope)->dmAcceptanceTime);
3430 /* dmMessageStatus element is mandatory */
3431 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
3432 if (!unumber) {
3433 isds_log_message(context,
3434 _("Missing mandatory sisds:dmMessageStatus integer"));
3435 err = IE_ISDS;
3436 goto leave;
3438 err = uint2isds_message_status(context, unumber,
3439 &((*envelope)->dmMessageStatus));
3440 if (err) {
3441 if (err == IE_ENUM) err = IE_ISDS;
3442 goto leave;
3444 free(unumber); unumber = NULL;
3446 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
3449 EXTRACT_STRING("sisds:dmDeliveryTime", string);
3450 if (string) {
3451 err = timestring2timeval((xmlChar *) string,
3452 &((*envelope)->dmDeliveryTime));
3453 if (err) {
3454 char *string_locale = _isds_utf82locale(string);
3455 if (err == IE_DATE) err = IE_ISDS;
3456 isds_printf_message(context,
3457 _("Could not convert dmDeliveryTime as ISO time: %s"),
3458 string_locale);
3459 free(string_locale);
3460 goto leave;
3462 zfree(string);
3465 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
3466 if (string) {
3467 err = timestring2timeval((xmlChar *) string,
3468 &((*envelope)->dmAcceptanceTime));
3469 if (err) {
3470 char *string_locale = _isds_utf82locale(string);
3471 if (err == IE_DATE) err = IE_ISDS;
3472 isds_printf_message(context,
3473 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3474 string_locale);
3475 free(string_locale);
3476 goto leave;
3478 zfree(string);
3481 leave:
3482 if (err) isds_envelope_free(envelope);
3483 free(unumber);
3484 free(string);
3485 xmlXPathFreeObject(result);
3486 return err;
3490 /* Convert message type attribute of current element into isds_envelope
3491 * structure.
3492 * TODO: This function can be incorporated into append_status_size_times() as
3493 * they are called always together.
3494 * The envelope is automatically allocated but not reallocated.
3495 * The data are just appended into envelope structure.
3496 * @context is ISDS context
3497 * @envelope is automatically allocated message envelope structure
3498 * @xpath_ctx is XPath context with current node as parent of attribute
3499 * carrying message type
3500 * In case of error @envelope will be freed. */
3501 static isds_error append_message_type(struct isds_ctx *context,
3502 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3503 isds_error err = IE_SUCCESS;
3505 if (!context) return IE_INVALID_CONTEXT;
3506 if (!envelope) return IE_INVAL;
3507 if (!xpath_ctx) return IE_INVAL;
3510 if (!*envelope) {
3511 /* Allocate new */
3512 *envelope = calloc(1, sizeof(**envelope));
3513 if (!*envelope) {
3514 err = IE_NOMEM;
3515 goto leave;
3517 } else {
3518 /* Free old data */
3519 zfree((*envelope)->dmType);
3523 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope)->dmType, 0);
3525 if (!(*envelope)->dmType) {
3526 /* Use default value */
3527 (*envelope)->dmType = strdup("V");
3528 if (!(*envelope)->dmType) {
3529 err = IE_NOMEM;
3530 goto leave;
3532 } else if (1 != xmlUTF8Strlen((xmlChar *) (*envelope)->dmType)) {
3533 char *type_locale = _isds_utf82locale((*envelope)->dmType);
3534 isds_printf_message(context,
3535 _("Message type in dmType attribute is not 1 character long: "
3536 "%s"),
3537 type_locale);
3538 free(type_locale);
3539 err = IE_ISDS;
3540 goto leave;
3543 leave:
3544 if (err) isds_envelope_free(envelope);
3545 return err;
3549 #if HAVE_LIBCURL
3550 /* Convert dmType isds_envelope member into XML attribute and append it to
3551 * current node.
3552 * @context is ISDS context
3553 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3554 * @dm_envelope is XML element the resulting attribute will be appended to.
3555 * @return error code, in case of error context' message is filled. */
3556 static isds_error insert_message_type(struct isds_ctx *context,
3557 const char *type, xmlNodePtr dm_envelope) {
3558 isds_error err = IE_SUCCESS;
3559 xmlAttrPtr attribute_node;
3561 if (!context) return IE_INVALID_CONTEXT;
3562 if (!dm_envelope) return IE_INVAL;
3564 /* Insert optional message type */
3565 if (type) {
3566 if (1 != xmlUTF8Strlen((xmlChar *) type)) {
3567 char *type_locale = _isds_utf82locale(type);
3568 isds_printf_message(context,
3569 _("Message type in envelope is not 1 character long: %s"),
3570 type_locale);
3571 free(type_locale);
3572 err = IE_INVAL;
3573 goto leave;
3575 INSERT_STRING_ATTRIBUTE(dm_envelope, "dmType", type);
3578 leave:
3579 return err;
3581 #endif /* HAVE_LIBCURL */
3584 /* Extract message document into reallocated document structure
3585 * @context is ISDS context
3586 * @document is automatically reallocated message documents structure
3587 * @xpath_ctx is XPath context with current node as isds:dmFile
3588 * In case of error @document will be freed. */
3589 static isds_error extract_document(struct isds_ctx *context,
3590 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
3591 isds_error err = IE_SUCCESS;
3592 xmlXPathObjectPtr result = NULL;
3593 xmlNodePtr file_node = xpath_ctx->node;
3594 char *string = NULL;
3596 if (!context) return IE_INVALID_CONTEXT;
3597 if (!document) return IE_INVAL;
3598 isds_document_free(document);
3599 if (!xpath_ctx) return IE_INVAL;
3601 *document = calloc(1, sizeof(**document));
3602 if (!*document) {
3603 err = IE_NOMEM;
3604 goto leave;
3607 /* Extract document meta data */
3608 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
3609 if (context->normalize_mime_type) {
3610 const char *normalized_type =
3611 isds_normalize_mime_type((*document)->dmMimeType);
3612 if (NULL != normalized_type &&
3613 normalized_type != (*document)->dmMimeType) {
3614 char *new_type = strdup(normalized_type);
3615 if (NULL == new_type) {
3616 isds_printf_message(context,
3617 _("Not enough memory to normalize document MIME type"));
3618 err = IE_NOMEM;
3619 goto leave;
3621 free((*document)->dmMimeType);
3622 (*document)->dmMimeType = new_type;
3626 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
3627 err = string2isds_FileMetaType((xmlChar*)string,
3628 &((*document)->dmFileMetaType));
3629 if (err) {
3630 char *meta_type_locale = _isds_utf82locale(string);
3631 isds_printf_message(context,
3632 _("Document has invalid dmFileMetaType attribute value: %s"),
3633 meta_type_locale);
3634 free(meta_type_locale);
3635 err = IE_ISDS;
3636 goto leave;
3638 zfree(string);
3640 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
3641 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
3642 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
3643 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
3646 /* Extract document data.
3647 * Base64 encoded blob or XML subtree must be presented. */
3649 /* Check for dmEncodedContent */
3650 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
3651 xpath_ctx);
3652 if (!result) {
3653 err = IE_XML;
3654 goto leave;
3657 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3658 /* Here we have Base64 blob */
3659 (*document)->is_xml = 0;
3661 if (result->nodesetval->nodeNr > 1) {
3662 isds_printf_message(context,
3663 _("Document has more dmEncodedContent elements"));
3664 err = IE_ISDS;
3665 goto leave;
3668 xmlXPathFreeObject(result); result = NULL;
3669 EXTRACT_STRING("isds:dmEncodedContent", string);
3671 /* Decode non-empty document */
3672 if (string && string[0] != '\0') {
3673 (*document)->data_length =
3674 _isds_b64decode(string, &((*document)->data));
3675 if ((*document)->data_length == (size_t) -1) {
3676 isds_printf_message(context,
3677 _("Error while Base64-decoding document content"));
3678 err = IE_ERROR;
3679 goto leave;
3682 } else {
3683 /* No Base64 blob, try XML document */
3684 xmlXPathFreeObject(result); result = NULL;
3685 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
3686 xpath_ctx);
3687 if (!result) {
3688 err = IE_XML;
3689 goto leave;
3692 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3693 /* Here we have XML document */
3694 (*document)->is_xml = 1;
3696 if (result->nodesetval->nodeNr > 1) {
3697 isds_printf_message(context,
3698 _("Document has more dmXMLContent elements"));
3699 err = IE_ISDS;
3700 goto leave;
3703 /* XXX: We cannot serialize the content simply because:
3704 * - XML document may point out of its scope (e.g. to message
3705 * envelope)
3706 * - isds:dmXMLContent can contain more elements, no element,
3707 * a text node only
3708 * - it's not the XML way
3709 * Thus we provide the only right solution: XML DOM. Let's
3710 * application to cope with this hot potato :) */
3711 (*document)->xml_node_list =
3712 result->nodesetval->nodeTab[0]->children;
3713 } else {
3714 /* No base64 blob, nor XML document */
3715 isds_printf_message(context,
3716 _("Document has no dmEncodedContent, nor dmXMLContent "
3717 "element"));
3718 err = IE_ISDS;
3719 goto leave;
3724 leave:
3725 if (err) isds_document_free(document);
3726 free(string);
3727 xmlXPathFreeObject(result);
3728 xpath_ctx->node = file_node;
3729 return err;
3734 /* Extract message documents into reallocated list of documents
3735 * @context is ISDS context
3736 * @documents is automatically reallocated message documents list structure
3737 * @xpath_ctx is XPath context with current node as XSD tFilesArray
3738 * In case of error @documents will be freed. */
3739 static isds_error extract_documents(struct isds_ctx *context,
3740 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
3741 isds_error err = IE_SUCCESS;
3742 xmlXPathObjectPtr result = NULL;
3743 xmlNodePtr files_node = xpath_ctx->node;
3744 struct isds_list *document, *prev_document = NULL;
3746 if (!context) return IE_INVALID_CONTEXT;
3747 if (!documents) return IE_INVAL;
3748 isds_list_free(documents);
3749 if (!xpath_ctx) return IE_INVAL;
3751 /* Find documents */
3752 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
3753 if (!result) {
3754 err = IE_XML;
3755 goto leave;
3758 /* No match */
3759 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3760 isds_printf_message(context,
3761 _("Message does not contain any document"));
3762 err = IE_ISDS;
3763 goto leave;
3767 /* Iterate over documents */
3768 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
3770 /* Allocate and append list item */
3771 document = calloc(1, sizeof(*document));
3772 if (!document) {
3773 err = IE_NOMEM;
3774 goto leave;
3776 document->destructor = (void (*)(void **))isds_document_free;
3777 if (i == 0) *documents = document;
3778 else prev_document->next = document;
3779 prev_document = document;
3781 /* Extract document */
3782 xpath_ctx->node = result->nodesetval->nodeTab[i];
3783 err = extract_document(context,
3784 (struct isds_document **) &(document->data), xpath_ctx);
3785 if (err) goto leave;
3789 leave:
3790 if (err) isds_list_free(documents);
3791 xmlXPathFreeObject(result);
3792 xpath_ctx->node = files_node;
3793 return err;
3797 #if HAVE_LIBCURL
3798 /* Convert isds:dmRecord XML tree into structure
3799 * @context is ISDS context
3800 * @envelope is automatically reallocated message envelope structure
3801 * @xpath_ctx is XPath context with current node as isds:dmRecord element
3802 * In case of error @envelope will be freed. */
3803 static isds_error extract_DmRecord(struct isds_ctx *context,
3804 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3805 isds_error err = IE_SUCCESS;
3806 xmlXPathObjectPtr result = NULL;
3808 if (!context) return IE_INVALID_CONTEXT;
3809 if (!envelope) return IE_INVAL;
3810 isds_envelope_free(envelope);
3811 if (!xpath_ctx) return IE_INVAL;
3814 *envelope = calloc(1, sizeof(**envelope));
3815 if (!*envelope) {
3816 err = IE_NOMEM;
3817 goto leave;
3821 /* Extract tRecord data */
3822 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
3824 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
3825 * dmAcceptanceTime. */
3826 err = append_status_size_times(context, envelope, xpath_ctx);
3827 if (err) goto leave;
3829 /* Extract envelope elements added by sender and ISDS
3830 * (XSD: gMessageEnvelope type) */
3831 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
3832 if (err) goto leave;
3834 /* Get message type */
3835 err = append_message_type(context, envelope, xpath_ctx);
3836 if (err) goto leave;
3839 leave:
3840 if (err) isds_envelope_free(envelope);
3841 xmlXPathFreeObject(result);
3842 return err;
3846 /* Convert XSD:tStateChangesRecord type XML tree into structure
3847 * @context is ISDS context
3848 * @changed_status is automatically reallocated message state change structure
3849 * @xpath_ctx is XPath context with current node as element of
3850 * XSD:tStateChangesRecord type
3851 * In case of error @changed_status will be freed. */
3852 static isds_error extract_StateChangesRecord(struct isds_ctx *context,
3853 struct isds_message_status_change **changed_status,
3854 xmlXPathContextPtr xpath_ctx) {
3855 isds_error err = IE_SUCCESS;
3856 xmlXPathObjectPtr result = NULL;
3857 unsigned long int *unumber = NULL;
3858 char *string = NULL;
3860 if (!context) return IE_INVALID_CONTEXT;
3861 if (!changed_status) return IE_INVAL;
3862 isds_message_status_change_free(changed_status);
3863 if (!xpath_ctx) return IE_INVAL;
3866 *changed_status = calloc(1, sizeof(**changed_status));
3867 if (!*changed_status) {
3868 err = IE_NOMEM;
3869 goto leave;
3873 /* Extract tGetStateChangesInput data */
3874 EXTRACT_STRING("isds:dmID", (*changed_status)->dmID);
3876 /* dmEventTime is mandatory */
3877 EXTRACT_STRING("isds:dmEventTime", string);
3878 if (string) {
3879 err = timestring2timeval((xmlChar *) string,
3880 &((*changed_status)->time));
3881 if (err) {
3882 char *string_locale = _isds_utf82locale(string);
3883 if (err == IE_DATE) err = IE_ISDS;
3884 isds_printf_message(context,
3885 _("Could not convert dmEventTime as ISO time: %s"),
3886 string_locale);
3887 free(string_locale);
3888 goto leave;
3890 zfree(string);
3893 /* dmMessageStatus element is mandatory */
3894 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber, 0);
3895 if (!unumber) {
3896 isds_log_message(context,
3897 _("Missing mandatory isds:dmMessageStatus integer"));
3898 err = IE_ISDS;
3899 goto leave;
3901 err = uint2isds_message_status(context, unumber,
3902 &((*changed_status)->dmMessageStatus));
3903 if (err) {
3904 if (err == IE_ENUM) err = IE_ISDS;
3905 goto leave;
3907 zfree(unumber);
3910 leave:
3911 free(unumber);
3912 free(string);
3913 if (err) isds_message_status_change_free(changed_status);
3914 xmlXPathFreeObject(result);
3915 return err;
3917 #endif /* HAVE_LIBCURL */
3920 /* Find and convert isds:dmHash XML tree into structure
3921 * @context is ISDS context
3922 * @envelope is automatically reallocated message hash structure
3923 * @xpath_ctx is XPath context with current node containing isds:dmHash child
3924 * In case of error @hash will be freed. */
3925 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
3926 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
3927 isds_error err = IE_SUCCESS;
3928 xmlNodePtr old_ctx_node;
3929 xmlXPathObjectPtr result = NULL;
3930 char *string = NULL;
3932 if (!context) return IE_INVALID_CONTEXT;
3933 if (!hash) return IE_INVAL;
3934 isds_hash_free(hash);
3935 if (!xpath_ctx) return IE_INVAL;
3937 old_ctx_node = xpath_ctx->node;
3939 *hash = calloc(1, sizeof(**hash));
3940 if (!*hash) {
3941 err = IE_NOMEM;
3942 goto leave;
3945 /* Locate dmHash */
3946 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
3947 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
3948 err = IE_ISDS;
3949 goto leave;
3951 if (err) {
3952 err = IE_ERROR;
3953 goto leave;
3956 /* Get hash algorithm */
3957 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
3958 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
3959 if (err) {
3960 if (err == IE_ENUM) {
3961 char *string_locale = _isds_utf82locale(string);
3962 isds_printf_message(context, _("Unsupported hash algorithm: %s"),
3963 string_locale);
3964 free(string_locale);
3966 goto leave;
3968 zfree(string);
3970 /* Get hash value */
3971 EXTRACT_STRING(".", string);
3972 if (!string) {
3973 isds_printf_message(context,
3974 _("sisds:dmHash element is missing hash value"));
3975 err = IE_ISDS;
3976 goto leave;
3978 (*hash)->length = _isds_b64decode(string, &((*hash)->value));
3979 if ((*hash)->length == (size_t) -1) {
3980 isds_printf_message(context,
3981 _("Error while Base64-decoding hash value"));
3982 err = IE_ERROR;
3983 goto leave;
3986 leave:
3987 if (err) isds_hash_free(hash);
3988 free(string);
3989 xmlXPathFreeObject(result);
3990 xpath_ctx->node = old_ctx_node;
3991 return err;
3995 /* Find and append isds:dmQTimestamp XML tree into envelope.
3996 * Because one service is allowed to miss time-stamp content, and we think
3997 * other could too (flaw in specification), this function is deliberated and
3998 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
3999 * @context is ISDS context
4000 * @envelope is automatically allocated envelope structure
4001 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4002 * child
4003 * In case of error @envelope will be freed. */
4004 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
4005 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4006 isds_error err = IE_SUCCESS;
4007 xmlXPathObjectPtr result = NULL;
4008 char *string = NULL;
4010 if (!context) return IE_INVALID_CONTEXT;
4011 if (!envelope) return IE_INVAL;
4012 if (!xpath_ctx) {
4013 isds_envelope_free(envelope);
4014 return IE_INVAL;
4017 if (!*envelope) {
4018 *envelope = calloc(1, sizeof(**envelope));
4019 if (!*envelope) {
4020 err = IE_NOMEM;
4021 goto leave;
4023 } else {
4024 zfree((*envelope)->timestamp);
4025 (*envelope)->timestamp_length = 0;
4028 /* Get dmQTimestamp */
4029 EXTRACT_STRING("sisds:dmQTimestamp", string);
4030 if (!string) {
4031 isds_log(ILF_ISDS, ILL_INFO, _("Missing dmQTimestamp element content\n"));
4032 goto leave;
4034 (*envelope)->timestamp_length =
4035 _isds_b64decode(string, &((*envelope)->timestamp));
4036 if ((*envelope)->timestamp_length == (size_t) -1) {
4037 isds_printf_message(context,
4038 _("Error while Base64-decoding time stamp value"));
4039 err = IE_ERROR;
4040 goto leave;
4043 leave:
4044 if (err) isds_envelope_free(envelope);
4045 free(string);
4046 xmlXPathFreeObject(result);
4047 return err;
4051 /* Convert XSD tReturnedMessage XML tree into message structure.
4052 * It does not store serialized XML tree into message->raw.
4053 * It does store (pointer to) parsed XML tree into message->xml if needed.
4054 * @context is ISDS context
4055 * @include_documents Use true if documents must be extracted
4056 * (tReturnedMessage XSD type), use false if documents shall be omitted
4057 * (tReturnedMessageEnvelope).
4058 * @message is automatically reallocated message structure
4059 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4060 * type
4061 * In case of error @message will be freed. */
4062 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
4063 const _Bool include_documents, struct isds_message **message,
4064 xmlXPathContextPtr xpath_ctx) {
4065 isds_error err = IE_SUCCESS;
4066 xmlNodePtr message_node;
4068 if (!context) return IE_INVALID_CONTEXT;
4069 if (!message) return IE_INVAL;
4070 isds_message_free(message);
4071 if (!xpath_ctx) return IE_INVAL;
4074 *message = calloc(1, sizeof(**message));
4075 if (!*message) {
4076 err = IE_NOMEM;
4077 goto leave;
4080 /* Save message XPATH context node */
4081 message_node = xpath_ctx->node;
4084 /* Extract dmDM */
4085 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
4086 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
4087 if (err) { err = IE_ERROR; goto leave; }
4088 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
4089 if (err) goto leave;
4091 if (include_documents) {
4092 struct isds_list *item;
4094 /* Extract dmFiles */
4095 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
4096 xpath_ctx);
4097 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4098 err = IE_ISDS; goto leave;
4100 if (err) { err = IE_ERROR; goto leave; }
4101 err = extract_documents(context, &((*message)->documents), xpath_ctx);
4102 if (err) goto leave;
4104 /* Store xmlDoc of this message if needed */
4105 /* Only if we got a XML document in all the documents. */
4106 for (item = (*message)->documents; item; item = item->next) {
4107 if (item->data && ((struct isds_document *)item->data)->is_xml) {
4108 (*message)->xml = xpath_ctx->doc;
4109 break;
4115 /* Restore context to message */
4116 xpath_ctx->node = message_node;
4118 /* Extract dmHash */
4119 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
4120 xpath_ctx);
4121 if (err) goto leave;
4123 /* Extract dmQTimestamp, */
4124 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
4125 xpath_ctx);
4126 if (err) goto leave;
4128 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4129 * dmAcceptanceTime. */
4130 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
4131 if (err) goto leave;
4133 /* Get message type */
4134 err = append_message_type(context, &((*message)->envelope), xpath_ctx);
4135 if (err) goto leave;
4137 leave:
4138 if (err) isds_message_free(message);
4139 return err;
4143 /* Extract message event into reallocated isds_event structure
4144 * @context is ISDS context
4145 * @event is automatically reallocated message event structure
4146 * @xpath_ctx is XPath context with current node as isds:dmEvent
4147 * In case of error @event will be freed. */
4148 static isds_error extract_event(struct isds_ctx *context,
4149 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
4150 isds_error err = IE_SUCCESS;
4151 xmlXPathObjectPtr result = NULL;
4152 xmlNodePtr event_node = xpath_ctx->node;
4153 char *string = NULL;
4155 if (!context) return IE_INVALID_CONTEXT;
4156 if (!event) return IE_INVAL;
4157 isds_event_free(event);
4158 if (!xpath_ctx) return IE_INVAL;
4160 *event = calloc(1, sizeof(**event));
4161 if (!*event) {
4162 err = IE_NOMEM;
4163 goto leave;
4166 /* Extract event data.
4167 * All elements are optional according XSD. That's funny. */
4168 EXTRACT_STRING("sisds:dmEventTime", string);
4169 if (string) {
4170 err = timestring2timeval((xmlChar *) string, &((*event)->time));
4171 if (err) {
4172 char *string_locale = _isds_utf82locale(string);
4173 if (err == IE_DATE) err = IE_ISDS;
4174 isds_printf_message(context,
4175 _("Could not convert dmEventTime as ISO time: %s"),
4176 string_locale);
4177 free(string_locale);
4178 goto leave;
4180 zfree(string);
4183 /* dmEventDescr element has prefix and the rest */
4184 EXTRACT_STRING("sisds:dmEventDescr", string);
4185 if (string) {
4186 err = eventstring2event((xmlChar *) string, *event);
4187 if (err) goto leave;
4188 zfree(string);
4191 leave:
4192 if (err) isds_event_free(event);
4193 free(string);
4194 xmlXPathFreeObject(result);
4195 xpath_ctx->node = event_node;
4196 return err;
4200 /* Convert element of XSD tEventsArray type from XML tree into
4201 * isds_list of isds_event's structure. The list is automatically reallocated.
4202 * @context is ISDS context
4203 * @events is automatically reallocated list of event structures
4204 * @xpath_ctx is XPath context with current node as tEventsArray
4205 * In case of error @events will be freed. */
4206 static isds_error extract_events(struct isds_ctx *context,
4207 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
4208 isds_error err = IE_SUCCESS;
4209 xmlXPathObjectPtr result = NULL;
4210 xmlNodePtr events_node = xpath_ctx->node;
4211 struct isds_list *event, *prev_event = NULL;
4213 if (!context) return IE_INVALID_CONTEXT;
4214 if (!events) return IE_INVAL;
4215 if (!xpath_ctx) return IE_INVAL;
4217 /* Free old list */
4218 isds_list_free(events);
4220 /* Find events */
4221 result = xmlXPathEvalExpression(BAD_CAST "sisds:dmEvent", xpath_ctx);
4222 if (!result) {
4223 err = IE_XML;
4224 goto leave;
4227 /* No match */
4228 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4229 isds_printf_message(context,
4230 _("Delivery info does not contain any event"));
4231 err = IE_ISDS;
4232 goto leave;
4236 /* Iterate over events */
4237 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4239 /* Allocate and append list item */
4240 event = calloc(1, sizeof(*event));
4241 if (!event) {
4242 err = IE_NOMEM;
4243 goto leave;
4245 event->destructor = (void (*)(void **))isds_event_free;
4246 if (i == 0) *events = event;
4247 else prev_event->next = event;
4248 prev_event = event;
4250 /* Extract event */
4251 xpath_ctx->node = result->nodesetval->nodeTab[i];
4252 err = extract_event(context,
4253 (struct isds_event **) &(event->data), xpath_ctx);
4254 if (err) goto leave;
4258 leave:
4259 if (err) isds_list_free(events);
4260 xmlXPathFreeObject(result);
4261 xpath_ctx->node = events_node;
4262 return err;
4266 #if HAVE_LIBCURL
4267 /* Insert Base64 encoded data as element with text child.
4268 * @context is session context
4269 * @parent is XML node to append @element with @data as child
4270 * @ns is XML namespace of @element, use NULL to inherit from @parent
4271 * @element is UTF-8 encoded name of new element
4272 * @data is bit stream to encode into @element
4273 * @length is size of @data in bytes
4274 * @return standard error code and fill long error message if needed */
4275 static isds_error insert_base64_encoded_string(struct isds_ctx *context,
4276 xmlNodePtr parent, const xmlNsPtr ns, const char *element,
4277 const void *data, size_t length) {
4278 isds_error err = IE_SUCCESS;
4279 xmlNodePtr node;
4281 if (!context) return IE_INVALID_CONTEXT;
4282 if (!data && length > 0) return IE_INVAL;
4283 if (!parent || !element) return IE_INVAL;
4285 xmlChar *base64data = NULL;
4286 base64data = (xmlChar *) _isds_b64encode(data, length);
4287 if (!base64data) {
4288 isds_printf_message(context,
4289 ngettext("Not enough memory to encode %zd byte into Base64",
4290 "Not enough memory to encode %zd bytes into Base64",
4291 length),
4292 length);
4293 err = IE_NOMEM;
4294 goto leave;
4296 INSERT_STRING_WITH_NS(parent, ns, element, base64data);
4298 leave:
4299 free(base64data);
4300 return err;
4304 /* Convert isds_document structure into XML tree and append to dmFiles node.
4305 * @context is session context
4306 * @document is ISDS document
4307 * @dm_files is XML element the resulting tree will be appended to as a child.
4308 * @return error code, in case of error context' message is filled. */
4309 static isds_error insert_document(struct isds_ctx *context,
4310 struct isds_document *document, xmlNodePtr dm_files) {
4311 isds_error err = IE_SUCCESS;
4312 xmlNodePtr new_file = NULL, file = NULL, node;
4313 xmlAttrPtr attribute_node;
4315 if (!context) return IE_INVALID_CONTEXT;
4316 if (!document || !dm_files) return IE_INVAL;
4318 /* Allocate new dmFile */
4319 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
4320 if (!new_file) {
4321 isds_printf_message(context, _("Could not allocate main dmFile"));
4322 err = IE_ERROR;
4323 goto leave;
4325 /* Append the new dmFile.
4326 * XXX: Main document must go first */
4327 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
4328 file = xmlAddPrevSibling(dm_files->children, new_file);
4329 else
4330 file = xmlAddChild(dm_files, new_file);
4332 if (!file) {
4333 xmlFreeNode(new_file); new_file = NULL;
4334 isds_printf_message(context, _("Could not add dmFile child to "
4335 "%s element"), dm_files->name);
4336 err = IE_ERROR;
4337 goto leave;
4340 /* @dmMimeType is required */
4341 if (!document->dmMimeType) {
4342 isds_log_message(context,
4343 _("Document is missing mandatory MIME type definition"));
4344 err = IE_INVAL;
4345 goto leave;
4347 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
4349 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
4350 if (!string) {
4351 isds_printf_message(context,
4352 _("Document has unknown dmFileMetaType: %ld"),
4353 document->dmFileMetaType);
4354 err = IE_ENUM;
4355 goto leave;
4357 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
4359 if (document->dmFileGuid) {
4360 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
4362 if (document->dmUpFileGuid) {
4363 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
4366 /* @dmFileDescr is required */
4367 if (!document->dmFileDescr) {
4368 isds_log_message(context,
4369 _("Document is missing mandatory description (title)"));
4370 err = IE_INVAL;
4371 goto leave;
4373 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
4375 if (document->dmFormat) {
4376 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
4380 /* Insert content (body) of the document. */
4381 if (document->is_xml) {
4382 /* XML document requested */
4384 /* Allocate new dmXMLContent */
4385 xmlNodePtr xmlcontent = xmlNewNode(file->ns, BAD_CAST "dmXMLContent");
4386 if (!xmlcontent) {
4387 isds_printf_message(context,
4388 _("Could not allocate dmXMLContent element"));
4389 err = IE_ERROR;
4390 goto leave;
4392 /* Append it */
4393 node = xmlAddChild(file, xmlcontent);
4394 if (!node) {
4395 xmlFreeNode(xmlcontent); xmlcontent = NULL;
4396 isds_printf_message(context,
4397 _("Could not add dmXMLContent child to %s element"),
4398 file->name);
4399 err = IE_ERROR;
4400 goto leave;
4403 /* Copy non-empty node list */
4404 if (document->xml_node_list) {
4405 xmlNodePtr content = xmlDocCopyNodeList(node->doc,
4406 document->xml_node_list);
4407 if (!content) {
4408 isds_printf_message(context,
4409 _("Not enough memory to copy XML document"));
4410 err = IE_NOMEM;
4411 goto leave;
4414 if (!xmlAddChildList(node, content)) {
4415 xmlFreeNodeList(content);
4416 isds_printf_message(context,
4417 _("Error while adding XML document into dmXMLContent"));
4418 err = IE_XML;
4419 goto leave;
4421 /* XXX: We cannot free the content here because it's part of node's
4422 * document since now. It will be freed with it automatically. */
4424 } else {
4425 /* Binary document requested */
4426 err = insert_base64_encoded_string(context, file, NULL, "dmEncodedContent",
4427 document->data, document->data_length);
4428 if (err) goto leave;
4431 leave:
4432 return err;
4436 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4437 * The copy must be preallocated, the date are just appended into structure.
4438 * @context is ISDS context
4439 * @copy is message copy structure
4440 * @xpath_ctx is XPath context with current node as tMStatus */
4441 static isds_error append_TMStatus(struct isds_ctx *context,
4442 struct isds_message_copy *copy, xmlXPathContextPtr xpath_ctx) {
4443 isds_error err = IE_SUCCESS;
4444 xmlXPathObjectPtr result = NULL;
4445 char *code = NULL, *message = NULL;
4447 if (!context) return IE_INVALID_CONTEXT;
4448 if (!copy || !xpath_ctx) return IE_INVAL;
4450 /* Free old values */
4451 zfree(copy->dmStatus);
4452 zfree(copy->dmID);
4454 /* Get error specific to this copy */
4455 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code);
4456 if (!code) {
4457 isds_log_message(context,
4458 _("Missing isds:dmStatusCode under "
4459 "XSD:tMStatus type element"));
4460 err = IE_ISDS;
4461 goto leave;
4464 if (xmlStrcmp((const xmlChar *)code, BAD_CAST "0000")) {
4465 /* This copy failed */
4466 copy->error = IE_ISDS;
4467 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message);
4468 if (message) {
4469 copy->dmStatus = _isds_astrcat3(code, ": ", message);
4470 if (!copy->dmStatus) {
4471 copy->dmStatus = code;
4472 code = NULL;
4474 } else {
4475 copy->dmStatus = code;
4476 code = NULL;
4478 } else {
4479 /* This copy succeeded. In this case only, message ID is valid */
4480 copy->error = IE_SUCCESS;
4482 EXTRACT_STRING("isds:dmID", copy->dmID);
4483 if (!copy->dmID) {
4484 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
4485 "but did not returned assigned message ID\n"));
4486 err = IE_ISDS;
4490 leave:
4491 free(code);
4492 free(message);
4493 xmlXPathFreeObject(result);
4494 return err;
4498 /* Insert struct isds_approval data (box approval) into XML tree
4499 * @context is session context
4500 * @approval is libisds structure with approval description. NULL is
4501 * acceptable.
4502 * @parent is XML element to append @approval to */
4503 static isds_error insert_GExtApproval(struct isds_ctx *context,
4504 const struct isds_approval *approval, xmlNodePtr parent) {
4506 isds_error err = IE_SUCCESS;
4507 xmlNodePtr node;
4509 if (!context) return IE_INVALID_CONTEXT;
4510 if (!parent) return IE_INVAL;
4512 if (!approval) return IE_SUCCESS;
4514 /* Build XSD:gExtApproval */
4515 INSERT_SCALAR_BOOLEAN(parent, "dbApproved", approval->approved);
4516 INSERT_STRING(parent, "dbExternRefNumber", approval->refference);
4518 leave:
4519 return err;
4523 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4524 * code
4525 * @context is session context
4526 * @service_name is name of SERVICE_DB_ACCESS
4527 * @response is server SOAP body response as XML document
4528 * @raw_response is automatically reallocated bit stream with response body. Use
4529 * NULL if you don't care
4530 * @raw_response_length is size of @raw_response in bytes
4531 * @code is ISDS status code
4532 * @status_message is ISDS status message
4533 * @return error coded from lower layer, context message will be set up
4534 * appropriately. */
4535 static isds_error build_send_check_dbdummy_request(struct isds_ctx *context,
4536 const xmlChar *service_name,
4537 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
4538 xmlChar **code, xmlChar **status_message) {
4540 isds_error err = IE_SUCCESS;
4541 char *service_name_locale = NULL;
4542 xmlNodePtr request = NULL, node;
4543 xmlNsPtr isds_ns = NULL;
4545 if (!context) return IE_INVALID_CONTEXT;
4546 if (!service_name) return IE_INVAL;
4547 if (!response || !code || !status_message) return IE_INVAL;
4548 if (!raw_response_length && raw_response) return IE_INVAL;
4550 /* Free output argument */
4551 xmlFreeDoc(*response); *response = NULL;
4552 if (raw_response) zfree(*raw_response);
4553 free(*code);
4554 free(*status_message);
4557 /* Check if connection is established
4558 * TODO: This check should be done downstairs. */
4559 if (!context->curl) return IE_CONNECTION_CLOSED;
4561 service_name_locale = _isds_utf82locale((char*)service_name);
4562 if (!service_name_locale) {
4563 err = IE_NOMEM;
4564 goto leave;
4567 /* Build request */
4568 request = xmlNewNode(NULL, service_name);
4569 if (!request) {
4570 isds_printf_message(context,
4571 _("Could not build %s request"), service_name_locale);
4572 err = IE_ERROR;
4573 goto leave;
4575 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4576 if(!isds_ns) {
4577 isds_log_message(context, _("Could not create ISDS name space"));
4578 err = IE_ERROR;
4579 goto leave;
4581 xmlSetNs(request, isds_ns);
4584 /* Add XSD:tDummyInput child */
4585 INSERT_STRING(request, "dbDummy", NULL);
4588 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
4589 service_name_locale);
4591 /* Send request */
4592 err = isds(context, SERVICE_DB_ACCESS, request, response,
4593 raw_response, raw_response_length);
4594 xmlFreeNode(request); request = NULL;
4596 if (err) {
4597 isds_log(ILF_ISDS, ILL_DEBUG,
4598 _("Processing ISDS response on %s request failed\n"),
4599 service_name_locale);
4600 goto leave;
4603 /* Check for response status */
4604 err = isds_response_status(context, SERVICE_DB_ACCESS, *response,
4605 code, status_message, NULL);
4606 if (err) {
4607 isds_log(ILF_ISDS, ILL_DEBUG,
4608 _("ISDS response on %s request is missing status\n"),
4609 service_name_locale);
4610 goto leave;
4613 /* Request processed, but nothing found */
4614 if (xmlStrcmp(*code, BAD_CAST "0000")) {
4615 char *code_locale = _isds_utf82locale((char*) *code);
4616 char *status_message_locale =
4617 _isds_utf82locale((char*) *status_message);
4618 isds_log(ILF_ISDS, ILL_DEBUG,
4619 _("Server refused %s request (code=%s, message=%s)\n"),
4620 service_name_locale, code_locale, status_message_locale);
4621 isds_log_message(context, status_message_locale);
4622 free(code_locale);
4623 free(status_message_locale);
4624 err = IE_ISDS;
4625 goto leave;
4628 leave:
4629 free(service_name_locale);
4630 xmlFreeNode(request);
4631 return err;
4633 #endif
4636 /* Get data about logged in user and his box. */
4637 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
4638 struct isds_DbOwnerInfo **db_owner_info) {
4639 isds_error err = IE_SUCCESS;
4640 #if HAVE_LIBCURL
4641 xmlDocPtr response = NULL;
4642 xmlChar *code = NULL, *message = NULL;
4643 xmlXPathContextPtr xpath_ctx = NULL;
4644 xmlXPathObjectPtr result = NULL;
4645 char *string = NULL;
4646 #endif
4648 if (!context) return IE_INVALID_CONTEXT;
4649 zfree(context->long_message);
4650 if (!db_owner_info) return IE_INVAL;
4651 isds_DbOwnerInfo_free(db_owner_info);
4653 #if HAVE_LIBCURL
4654 /* Check if connection is established */
4655 if (!context->curl) return IE_CONNECTION_CLOSED;
4658 /* Do request and check for success */
4659 err = build_send_check_dbdummy_request(context,
4660 BAD_CAST "GetOwnerInfoFromLogin",
4661 &response, NULL, NULL, &code, &message);
4662 if (err) goto leave;
4665 /* Extract data */
4666 /* Prepare structure */
4667 *db_owner_info = calloc(1, sizeof(**db_owner_info));
4668 if (!*db_owner_info) {
4669 err = IE_NOMEM;
4670 goto leave;
4672 xpath_ctx = xmlXPathNewContext(response);
4673 if (!xpath_ctx) {
4674 err = IE_ERROR;
4675 goto leave;
4677 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4678 err = IE_ERROR;
4679 goto leave;
4682 /* Set context node */
4683 result = xmlXPathEvalExpression(BAD_CAST
4684 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
4685 if (!result) {
4686 err = IE_ERROR;
4687 goto leave;
4689 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4690 isds_log_message(context, _("Missing dbOwnerInfo element"));
4691 err = IE_ISDS;
4692 goto leave;
4694 if (result->nodesetval->nodeNr > 1) {
4695 isds_log_message(context, _("Multiple dbOwnerInfo element"));
4696 err = IE_ISDS;
4697 goto leave;
4699 xpath_ctx->node = result->nodesetval->nodeTab[0];
4700 xmlXPathFreeObject(result); result = NULL;
4702 /* Extract it */
4703 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
4706 leave:
4707 if (err) {
4708 isds_DbOwnerInfo_free(db_owner_info);
4711 free(string);
4712 xmlXPathFreeObject(result);
4713 xmlXPathFreeContext(xpath_ctx);
4715 free(code);
4716 free(message);
4717 xmlFreeDoc(response);
4719 if (!err)
4720 isds_log(ILF_ISDS, ILL_DEBUG,
4721 _("GetOwnerInfoFromLogin request processed by server "
4722 "successfully.\n"));
4723 #else /* not HAVE_LIBCURL */
4724 err = IE_NOTSUP;
4725 #endif
4727 return err;
4731 /* Get data about logged in user. */
4732 isds_error isds_GetUserInfoFromLogin(struct isds_ctx *context,
4733 struct isds_DbUserInfo **db_user_info) {
4734 isds_error err = IE_SUCCESS;
4735 #if HAVE_LIBCURL
4736 xmlDocPtr response = NULL;
4737 xmlChar *code = NULL, *message = NULL;
4738 xmlXPathContextPtr xpath_ctx = NULL;
4739 xmlXPathObjectPtr result = NULL;
4740 #endif
4742 if (!context) return IE_INVALID_CONTEXT;
4743 zfree(context->long_message);
4744 if (!db_user_info) return IE_INVAL;
4745 isds_DbUserInfo_free(db_user_info);
4747 #if HAVE_LIBCURL
4748 /* Check if connection is established */
4749 if (!context->curl) return IE_CONNECTION_CLOSED;
4752 /* Do request and check for success */
4753 err = build_send_check_dbdummy_request(context,
4754 BAD_CAST "GetUserInfoFromLogin",
4755 &response, NULL, NULL, &code, &message);
4756 if (err) goto leave;
4759 /* Extract data */
4760 /* Prepare structure */
4761 *db_user_info = calloc(1, sizeof(**db_user_info));
4762 if (!*db_user_info) {
4763 err = IE_NOMEM;
4764 goto leave;
4766 xpath_ctx = xmlXPathNewContext(response);
4767 if (!xpath_ctx) {
4768 err = IE_ERROR;
4769 goto leave;
4771 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4772 err = IE_ERROR;
4773 goto leave;
4776 /* Set context node */
4777 result = xmlXPathEvalExpression(BAD_CAST
4778 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx);
4779 if (!result) {
4780 err = IE_ERROR;
4781 goto leave;
4783 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4784 isds_log_message(context, _("Missing dbUserInfo element"));
4785 err = IE_ISDS;
4786 goto leave;
4788 if (result->nodesetval->nodeNr > 1) {
4789 isds_log_message(context, _("Multiple dbUserInfo element"));
4790 err = IE_ISDS;
4791 goto leave;
4793 xpath_ctx->node = result->nodesetval->nodeTab[0];
4794 xmlXPathFreeObject(result); result = NULL;
4796 /* Extract it */
4797 err = extract_DbUserInfo(context, db_user_info, xpath_ctx);
4799 leave:
4800 if (err) {
4801 isds_DbUserInfo_free(db_user_info);
4804 xmlXPathFreeObject(result);
4805 xmlXPathFreeContext(xpath_ctx);
4807 free(code);
4808 free(message);
4809 xmlFreeDoc(response);
4811 if (!err)
4812 isds_log(ILF_ISDS, ILL_DEBUG,
4813 _("GetUserInfoFromLogin request processed by server "
4814 "successfully.\n"));
4815 #else /* not HAVE_LIBCURL */
4816 err = IE_NOTSUP;
4817 #endif
4819 return err;
4823 /* Get expiration time of current password
4824 * @context is session context
4825 * @expiration is automatically reallocated time when password expires. If
4826 * password expiration is disables, NULL will be returned. In case of error
4827 * it will be nulled too. */
4828 isds_error isds_get_password_expiration(struct isds_ctx *context,
4829 struct timeval **expiration) {
4830 isds_error err = IE_SUCCESS;
4831 #if HAVE_LIBCURL
4832 xmlDocPtr response = NULL;
4833 xmlChar *code = NULL, *message = NULL;
4834 xmlXPathContextPtr xpath_ctx = NULL;
4835 xmlXPathObjectPtr result = NULL;
4836 char *string = NULL;
4837 #endif
4839 if (!context) return IE_INVALID_CONTEXT;
4840 zfree(context->long_message);
4841 if (!expiration) return IE_INVAL;
4842 zfree(*expiration);
4844 #if HAVE_LIBCURL
4845 /* Check if connection is established */
4846 if (!context->curl) return IE_CONNECTION_CLOSED;
4849 /* Do request and check for success */
4850 err = build_send_check_dbdummy_request(context,
4851 BAD_CAST "GetPasswordInfo",
4852 &response, NULL, NULL, &code, &message);
4853 if (err) goto leave;
4856 /* Extract data */
4857 xpath_ctx = xmlXPathNewContext(response);
4858 if (!xpath_ctx) {
4859 err = IE_ERROR;
4860 goto leave;
4862 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4863 err = IE_ERROR;
4864 goto leave;
4867 /* Set context node */
4868 result = xmlXPathEvalExpression(BAD_CAST
4869 "/isds:GetPasswordInfoResponse", xpath_ctx);
4870 if (!result) {
4871 err = IE_ERROR;
4872 goto leave;
4874 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4875 isds_log_message(context,
4876 _("Missing GetPasswordInfoResponse element"));
4877 err = IE_ISDS;
4878 goto leave;
4880 if (result->nodesetval->nodeNr > 1) {
4881 isds_log_message(context,
4882 _("Multiple GetPasswordInfoResponse element"));
4883 err = IE_ISDS;
4884 goto leave;
4886 xpath_ctx->node = result->nodesetval->nodeTab[0];
4887 xmlXPathFreeObject(result); result = NULL;
4889 /* Extract expiration date */
4890 EXTRACT_STRING("isds:pswExpDate", string);
4891 if (string) {
4892 /* And convert it if any returned. Otherwise expiration is disabled. */
4893 err = timestring2timeval((xmlChar *) string, expiration);
4894 if (err) {
4895 char *string_locale = _isds_utf82locale(string);
4896 if (err == IE_DATE) err = IE_ISDS;
4897 isds_printf_message(context,
4898 _("Could not convert pswExpDate as ISO time: %s"),
4899 string_locale);
4900 free(string_locale);
4901 goto leave;
4905 leave:
4906 if (err) {
4907 if (*expiration) {
4908 zfree(*expiration);
4912 free(string);
4913 xmlXPathFreeObject(result);
4914 xmlXPathFreeContext(xpath_ctx);
4916 free(code);
4917 free(message);
4918 xmlFreeDoc(response);
4920 if (!err)
4921 isds_log(ILF_ISDS, ILL_DEBUG,
4922 _("GetPasswordInfo request processed by server "
4923 "successfully.\n"));
4924 #else /* not HAVE_LIBCURL */
4925 err = IE_NOTSUP;
4926 #endif
4928 return err;
4932 #if HAVE_LIBCURL
4933 /* Request delivering new TOTP code from ISDS through side channel before
4934 * changing password.
4935 * @context is session context
4936 * @password is current password.
4937 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
4938 * Please note the @otp argument must have TOTP OTP method. See isds_login()
4939 * function for more details.
4940 * @refnumber is reallocated serial number of request assigned by ISDS. Use
4941 * NULL, if you don't care.
4942 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
4943 * error code. */
4944 static isds_error _isds_request_totp_code(struct isds_ctx *context,
4945 const char *password, struct isds_otp *otp, char **refnumber) {
4946 isds_error err = IE_SUCCESS;
4947 char *saved_url = NULL; /* No copy */
4948 #if HAVE_CURL_REAUTHORIZATION_BUG
4949 CURL *saved_curl = NULL; /* No copy */
4950 #endif
4951 xmlNsPtr isds_ns = NULL;
4952 xmlNodePtr request = NULL;
4953 xmlDocPtr response = NULL;
4954 xmlChar *code = NULL, *message = NULL;
4955 const xmlChar *codes[] = {
4956 BAD_CAST "2300",
4957 BAD_CAST "2301",
4958 BAD_CAST "2302"
4960 const char *meanings[] = {
4961 N_("Unexpected error"),
4962 N_("One-time code cannot be re-send faster than once a 30 seconds"),
4963 N_("One-time code could not been sent. Try later again.")
4965 const isds_otp_resolution resolutions[] = {
4966 OTP_RESOLUTION_UNKNOWN,
4967 OTP_RESOLUTION_TO_FAST,
4968 OTP_RESOLUTION_TOTP_NOT_SENT
4971 if (NULL == context) return IE_INVALID_CONTEXT;
4972 zfree(context->long_message);
4973 if (NULL == password) {
4974 isds_log_message(context,
4975 _("Second argument (password) of isds_change_password() "
4976 "is NULL"));
4977 return IE_INVAL;
4980 /* Check if connection is established
4981 * TODO: This check should be done downstairs. */
4982 if (!context->curl) return IE_CONNECTION_CLOSED;
4984 if (!context->otp) {
4985 isds_log_message(context, _("This function requires OTP-authenticated "
4986 "context"));
4987 return IE_INVALID_CONTEXT;
4989 if (NULL == otp) {
4990 isds_log_message(context, _("If one-time password authentication "
4991 "method is in use, requesting new OTP code requires "
4992 "one-time credentials argument either"));
4993 return IE_INVAL;
4995 if (otp->method != OTP_TIME) {
4996 isds_log_message(context, _("Requesting new time-based OTP code from "
4997 "server requires one-time password authentication "
4998 "method"));
4999 return IE_INVAL;
5001 if (otp->otp_code != NULL) {
5002 isds_log_message(context, _("Requesting new time-based OTP code from "
5003 "server requires undefined OTP code member in "
5004 "one-time credentials argument"));
5005 return IE_INVAL;
5009 /* Build request */
5010 request = xmlNewNode(NULL, BAD_CAST "SendSMSCode");
5011 if (!request) {
5012 isds_log_message(context, _("Could not build SendSMSCode request"));
5013 return IE_ERROR;
5015 isds_ns = xmlNewNs(request, BAD_CAST OISDS_NS, NULL);
5016 if(!isds_ns) {
5017 isds_log_message(context, _("Could not create ISDS name space"));
5018 xmlFreeNode(request);
5019 return IE_ERROR;
5021 xmlSetNs(request, isds_ns);
5023 /* Change URL temporarily for sending this request only */
5025 char *new_url = NULL;
5026 if ((err = _isds_build_url_from_context(context,
5027 "%1$.*2$sasws/changePassword", &new_url))) {
5028 goto leave;
5030 saved_url = context->url;
5031 context->url = new_url;
5034 /* Store credentials for sending this request only */
5035 context->otp_credentials = otp;
5036 _isds_discard_credentials(context, 0);
5037 if ((err = _isds_store_credentials(context, context->saved_username,
5038 password, NULL))) {
5039 _isds_discard_credentials(context, 0);
5040 goto leave;
5042 #if HAVE_CURL_REAUTHORIZATION_BUG
5043 saved_curl = context->curl;
5044 context->curl = curl_easy_init();
5045 if (NULL == context->curl) {
5046 err = IE_ERROR;
5047 goto leave;
5049 if (context->timeout) {
5050 err = isds_set_timeout(context, context->timeout);
5051 if (err) goto leave;
5053 #endif
5055 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending SendSMSCode request to ISDS\n"));
5057 /* Sent request */
5058 err = isds(context, SERVICE_ASWS, request, &response, NULL, NULL);
5060 /* Remove temporal credentials */
5061 _isds_discard_credentials(context, 0);
5062 /* Detach pointer to OTP credentials from context */
5063 context->otp_credentials = NULL;
5064 /* Keep context->otp true to keep signaling this is OTP session */
5066 /* Destroy request */
5067 xmlFreeNode(request); request = NULL;
5069 if (err) {
5070 isds_log(ILF_ISDS, ILL_DEBUG,
5071 _("Processing ISDS response on SendSMSCode request failed\n"));
5072 goto leave;
5075 /* Check for response status */
5076 err = isds_response_status(context, SERVICE_ASWS, response,
5077 &code, &message, (xmlChar **)refnumber);
5078 if (err) {
5079 isds_log(ILF_ISDS, ILL_DEBUG,
5080 _("ISDS response on SendSMSCode request is missing "
5081 "status\n"));
5082 goto leave;
5085 /* Check for error */
5086 if (xmlStrcmp(code, BAD_CAST "0000")) {
5087 char *code_locale = _isds_utf82locale((char*)code);
5088 char *message_locale = _isds_utf82locale((char*)message);
5089 int i;
5090 isds_log(ILF_ISDS, ILL_DEBUG,
5091 _("Server refused to send new code on SendSMSCode "
5092 "request (code=%s, message=%s)\n"),
5093 code_locale, message_locale);
5095 /* Check for known error codes */
5096 for (i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
5097 if (!xmlStrcmp(code, codes[i])) break;
5099 if (i < sizeof(codes)/sizeof(*codes)) {
5100 isds_log_message(context, _(meanings[i]));
5101 /* Mimic otp->resolution according to the code, specification does
5102 * prescribe OTP header to be available. */
5103 if (OTP_RESOLUTION_SUCCESS == otp->resolution &&
5104 OTP_RESOLUTION_UNKNOWN != resolutions[i])
5105 otp->resolution = resolutions[i];
5106 } else
5107 isds_log_message(context, message_locale);
5109 free(code_locale);
5110 free(message_locale);
5112 err = IE_ISDS;
5113 goto leave;
5116 /* Otherwise new code sent successfully */
5117 /* Mimic otp->resolution according to the code, specification does
5118 * prescribe OTP header to be available. */
5119 if (OTP_RESOLUTION_SUCCESS == otp->resolution)
5120 otp->resolution = OTP_RESOLUTION_TOTP_SENT;
5122 leave:
5123 if (NULL != saved_url) {
5124 /* Revert URL to original one */
5125 zfree(context->url);
5126 context->url = saved_url;
5128 #if HAVE_CURL_REAUTHORIZATION_BUG
5129 if (NULL != saved_curl) {
5130 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5131 context->curl = saved_curl;
5133 #endif
5135 free(code);
5136 free(message);
5137 xmlFreeDoc(response);
5138 xmlFreeNode(request);
5140 if (!err)
5141 isds_log(ILF_ISDS, ILL_DEBUG,
5142 _("New OTP code has been sent successfully on SendSMSCode "
5143 "request.\n"));
5144 return err;
5146 #endif
5149 /* Change user password in ISDS.
5150 * User must supply old password, new password will takes effect after some
5151 * time, current session can continue. Password must fulfill some constraints.
5152 * @context is session context
5153 * @old_password is current password.
5154 * @new_password is requested new password
5155 * @otp auxiliary data required if one-time password authentication is in use,
5156 * defines OTP code (if known) and returns fine grade resolution of OTP
5157 * procedure. Pass NULL, if one-time password authentication is not needed.
5158 * Please note the @otp argument must match OTP method used at log-in time. See
5159 * isds_login() function for more details.
5160 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5161 * NULL, if you don't care.
5162 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5163 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5164 * awaiting OTP code that has been delivered by side channel to the user. */
5165 isds_error isds_change_password(struct isds_ctx *context,
5166 const char *old_password, const char *new_password,
5167 struct isds_otp *otp, char **refnumber) {
5168 isds_error err = IE_SUCCESS;
5169 #if HAVE_LIBCURL
5170 char *saved_url = NULL; /* No copy */
5171 #if HAVE_CURL_REAUTHORIZATION_BUG
5172 CURL *saved_curl = NULL; /* No copy */
5173 #endif
5174 xmlNsPtr isds_ns = NULL;
5175 xmlNodePtr request = NULL, node;
5176 xmlDocPtr response = NULL;
5177 xmlChar *code = NULL, *message = NULL;
5178 const xmlChar *codes[] = {
5179 BAD_CAST "1066",
5180 BAD_CAST "1067",
5181 BAD_CAST "1079",
5182 BAD_CAST "1080",
5183 BAD_CAST "1081",
5184 BAD_CAST "1082",
5185 BAD_CAST "1083",
5186 BAD_CAST "1090",
5187 BAD_CAST "1091",
5188 BAD_CAST "2300",
5189 BAD_CAST "9204"
5191 const char *meanings[] = {
5192 N_("Password length must be between 8 and 32 characters"),
5193 N_("Password cannot be reused"), /* Server does not distinguish 1067
5194 and 1091 on ChangePasswordOTP */
5195 N_("Password contains forbidden character"),
5196 N_("Password must contain at least one upper-case letter, "
5197 "one lower-case, and one digit"),
5198 N_("Password cannot contain sequence of three identical characters"),
5199 N_("Password cannot contain user identifier"),
5200 N_("Password is too simmple"),
5201 N_("Old password is not valid"),
5202 N_("Password cannot be reused"),
5203 N_("Unexpected error"),
5204 N_("LDAP update error")
5206 #endif
5208 if (!context) return IE_INVALID_CONTEXT;
5209 zfree(context->long_message);
5210 if (NULL != refnumber)
5211 zfree(*refnumber);
5212 if (NULL == old_password) {
5213 isds_log_message(context,
5214 _("Second argument (old password) of isds_change_password() "
5215 "is NULL"));
5216 return IE_INVAL;
5218 if (NULL == otp && NULL == new_password) {
5219 isds_log_message(context,
5220 _("Third argument (new password) of isds_change_password() "
5221 "is NULL"));
5222 return IE_INVAL;
5225 #if HAVE_LIBCURL
5226 /* Check if connection is established
5227 * TODO: This check should be done downstairs. */
5228 if (!context->curl) return IE_CONNECTION_CLOSED;
5230 if (context->otp && NULL == otp) {
5231 isds_log_message(context, _("If one-time password authentication "
5232 "method is in use, changing password requires one-time "
5233 "credentials either"));
5234 return IE_INVAL;
5237 /* Build ChangeISDSPassword request */
5238 request = xmlNewNode(NULL, (NULL == otp) ? BAD_CAST "ChangeISDSPassword" :
5239 BAD_CAST "ChangePasswordOTP");
5240 if (!request) {
5241 isds_log_message(context, (NULL == otp) ?
5242 _("Could not build ChangeISDSPassword request") :
5243 _("Could not build ChangePasswordOTP request"));
5244 return IE_ERROR;
5246 isds_ns = xmlNewNs(request,
5247 (NULL == otp) ? BAD_CAST ISDS_NS : BAD_CAST OISDS_NS,
5248 NULL);
5249 if(!isds_ns) {
5250 isds_log_message(context, _("Could not create ISDS name space"));
5251 xmlFreeNode(request);
5252 return IE_ERROR;
5254 xmlSetNs(request, isds_ns);
5256 INSERT_STRING(request, "dbOldPassword", old_password);
5257 INSERT_STRING(request, "dbNewPassword", new_password);
5259 if (NULL != otp) {
5260 otp->resolution = OTP_RESOLUTION_UNKNOWN;
5261 switch (otp->method) {
5262 case OTP_HMAC:
5263 isds_log(ILF_SEC, ILL_INFO,
5264 _("Selected authentication method: "
5265 "HMAC-based one-time password\n"));
5266 INSERT_STRING(request, "dbOTPType", BAD_CAST "HOTP");
5267 break;
5268 case OTP_TIME:
5269 isds_log(ILF_SEC, ILL_INFO,
5270 _("Selected authentication method: "
5271 "Time-based one-time password\n"));
5272 INSERT_STRING(request, "dbOTPType", BAD_CAST "TOTP");
5273 if (otp->otp_code == NULL) {
5274 isds_log(ILF_SEC, ILL_INFO,
5275 _("OTP code has not been provided by "
5276 "application, requesting server for "
5277 "new one.\n"));
5278 err = _isds_request_totp_code(context, old_password, otp,
5279 refnumber);
5280 if (err == IE_SUCCESS) err = IE_PARTIAL_SUCCESS;
5281 goto leave;
5283 } else {
5284 isds_log(ILF_SEC, ILL_INFO,
5285 _("OTP code has been provided by "
5286 "application, not requesting server "
5287 "for new one.\n"));
5289 break;
5290 default:
5291 isds_log_message(context,
5292 _("Unknown one-time password authentication "
5293 "method requested by application"));
5294 err = IE_ENUM;
5295 goto leave;
5298 /* Change URL temporarily for sending this request only */
5300 char *new_url = NULL;
5301 if ((err = _isds_build_url_from_context(context,
5302 "%1$.*2$sasws/changePassword", &new_url))) {
5303 goto leave;
5305 saved_url = context->url;
5306 context->url = new_url;
5309 /* Store credentials for sending this request only */
5310 context->otp_credentials = otp;
5311 _isds_discard_credentials(context, 0);
5312 if ((err = _isds_store_credentials(context, context->saved_username,
5313 old_password, NULL))) {
5314 _isds_discard_credentials(context, 0);
5315 goto leave;
5317 #if HAVE_CURL_REAUTHORIZATION_BUG
5318 saved_curl = context->curl;
5319 context->curl = curl_easy_init();
5320 if (NULL == context->curl) {
5321 err = IE_ERROR;
5322 goto leave;
5324 if (context->timeout) {
5325 err = isds_set_timeout(context, context->timeout);
5326 if (err) goto leave;
5328 #endif
5331 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5332 _("Sending ChangeISDSPassword request to ISDS\n") :
5333 _("Sending ChangePasswordOTP request to ISDS\n"));
5335 /* Sent request */
5336 err = isds(context, (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS,
5337 request, &response, NULL, NULL);
5339 if (otp) {
5340 /* Remove temporal credentials */
5341 _isds_discard_credentials(context, 0);
5342 /* Detach pointer to OTP credentials from context */
5343 context->otp_credentials = NULL;
5344 /* Keep context->otp true to keep signaling this is OTP session */
5347 /* Destroy request */
5348 xmlFreeNode(request); request = NULL;
5350 if (err) {
5351 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5352 _("Processing ISDS response on ChangeISDSPassword "
5353 "request failed\n") :
5354 _("Processing ISDS response on ChangePasswordOTP "
5355 "request failed\n"));
5356 goto leave;
5359 /* Check for response status */
5360 err = isds_response_status(context,
5361 (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS, response,
5362 &code, &message, (xmlChar **)refnumber);
5363 if (err) {
5364 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5365 _("ISDS response on ChangeISDSPassword request is missing "
5366 "status\n") :
5367 _("ISDS response on ChangePasswordOTP request is missing "
5368 "status\n"));
5369 goto leave;
5372 /* Check for known error codes */
5373 for (int i=0; i < sizeof(codes)/sizeof(*codes); i++) {
5374 if (!xmlStrcmp(code, codes[i])) {
5375 char *code_locale = _isds_utf82locale((char*)code);
5376 char *message_locale = _isds_utf82locale((char*)message);
5377 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5378 _("Server refused to change password on ChangeISDSPassword "
5379 "request (code=%s, message=%s)\n") :
5380 _("Server refused to change password on ChangePasswordOTP "
5381 "request (code=%s, message=%s)\n"),
5382 code_locale, message_locale);
5383 free(code_locale);
5384 free(message_locale);
5385 isds_log_message(context, _(meanings[i]));
5386 err = IE_INVAL;
5387 goto leave;
5391 /* Other error */
5392 if (xmlStrcmp(code, BAD_CAST "0000")) {
5393 char *code_locale = _isds_utf82locale((char*)code);
5394 char *message_locale = _isds_utf82locale((char*)message);
5395 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5396 _("Server refused to change password on ChangeISDSPassword "
5397 "request (code=%s, message=%s)\n") :
5398 _("Server refused to change password on ChangePasswordOTP "
5399 "request (code=%s, message=%s)\n"),
5400 code_locale, message_locale);
5401 isds_log_message(context, message_locale);
5402 free(code_locale);
5403 free(message_locale);
5404 err = IE_ISDS;
5405 goto leave;
5408 /* Otherwise password changed successfully */
5410 leave:
5411 if (NULL != saved_url) {
5412 /* Revert URL to original one */
5413 zfree(context->url);
5414 context->url = saved_url;
5416 #if HAVE_CURL_REAUTHORIZATION_BUG
5417 if (NULL != saved_curl) {
5418 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5419 context->curl = saved_curl;
5421 #endif
5423 free(code);
5424 free(message);
5425 xmlFreeDoc(response);
5426 xmlFreeNode(request);
5428 if (!err)
5429 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5430 _("Password changed successfully on ChangeISDSPassword "
5431 "request.\n") :
5432 _("Password changed successfully on ChangePasswordOTP "
5433 "request.\n"));
5434 #else /* not HAVE_LIBCURL */
5435 err = IE_NOTSUP;
5436 #endif
5438 return err;
5442 #if HAVE_LIBCURL
5443 /* Generic middle part with request sending and response check.
5444 * It sends prepared request and checks for error code.
5445 * @context is ISDS session context.
5446 * @service is ISDS service handler
5447 * @service_name is name in scope of given @service
5448 * @request is XML tree with request. Will be freed to save memory.
5449 * @response is XML document outputting ISDS response.
5450 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5451 * NULL, if you don't care. */
5452 static isds_error send_destroy_request_check_response(
5453 struct isds_ctx *context,
5454 const isds_service service, const xmlChar *service_name,
5455 xmlNodePtr *request, xmlDocPtr *response, xmlChar **refnumber) {
5456 isds_error err = IE_SUCCESS;
5457 char *service_name_locale = NULL;
5458 xmlChar *code = NULL, *message = NULL;
5461 if (!context) return IE_INVALID_CONTEXT;
5462 if (!service_name || *service_name == '\0' || !request || !*request ||
5463 !response)
5464 return IE_INVAL;
5466 /* Check if connection is established
5467 * TODO: This check should be done downstairs. */
5468 if (!context->curl) return IE_CONNECTION_CLOSED;
5470 service_name_locale = _isds_utf82locale((char*) service_name);
5471 if (!service_name_locale) {
5472 err = IE_NOMEM;
5473 goto leave;
5476 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
5477 service_name_locale);
5479 /* Send request */
5480 err = isds(context, service, *request, response, NULL, NULL);
5481 xmlFreeNode(*request); *request = NULL;
5483 if (err) {
5484 isds_log(ILF_ISDS, ILL_DEBUG,
5485 _("Processing ISDS response on %s request failed\n"),
5486 service_name_locale);
5487 goto leave;
5490 /* Check for response status */
5491 err = isds_response_status(context, service, *response,
5492 &code, &message, refnumber);
5493 if (err) {
5494 isds_log(ILF_ISDS, ILL_DEBUG,
5495 _("ISDS response on %s request is missing status\n"),
5496 service_name_locale);
5497 goto leave;
5500 /* Request processed, but server failed */
5501 if (xmlStrcmp(code, BAD_CAST "0000")) {
5502 char *code_locale = _isds_utf82locale((char*) code);
5503 char *message_locale = _isds_utf82locale((char*) message);
5504 isds_log(ILF_ISDS, ILL_DEBUG,
5505 _("Server refused %s request (code=%s, message=%s)\n"),
5506 service_name_locale, code_locale, message_locale);
5507 isds_log_message(context, message_locale);
5508 free(code_locale);
5509 free(message_locale);
5510 err = IE_ISDS;
5511 goto leave;
5515 leave:
5516 free(code);
5517 free(message);
5518 if (err && *response) {
5519 xmlFreeDoc(*response);
5520 *response = NULL;
5522 if (*request) {
5523 xmlFreeNode(*request);
5524 *request = NULL;
5526 free(service_name_locale);
5528 return err;
5532 /* Generic bottom half with request sending.
5533 * It sends prepared request, checks for error code, destroys response and
5534 * request and log success or failure.
5535 * @context is ISDS session context.
5536 * @service is ISDS service handler
5537 * @service_name is name in scope of given @service
5538 * @request is XML tree with request. Will be freed to save memory.
5539 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5540 * NULL, if you don't care. */
5541 static isds_error send_request_check_drop_response(
5542 struct isds_ctx *context,
5543 const isds_service service, const xmlChar *service_name,
5544 xmlNodePtr *request, xmlChar **refnumber) {
5545 isds_error err = IE_SUCCESS;
5546 xmlDocPtr response = NULL;
5549 if (!context) return IE_INVALID_CONTEXT;
5550 if (!service_name || *service_name == '\0' || !request || !*request)
5551 return IE_INVAL;
5553 /* Send request and check response*/
5554 err = send_destroy_request_check_response(context,
5555 service, service_name, request, &response, refnumber);
5557 xmlFreeDoc(response);
5559 if (*request) {
5560 xmlFreeNode(*request);
5561 *request = NULL;
5564 if (!err) {
5565 char *service_name_locale = _isds_utf82locale((char *) service_name);
5566 isds_log(ILF_ISDS, ILL_DEBUG,
5567 _("%s request processed by server successfully.\n"),
5568 service_name_locale);
5569 free(service_name_locale);
5572 return err;
5576 /* Insert isds_credentials_delivery structure into XML request if not NULL
5577 * @context is session context
5578 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
5579 * credentials delivery. The email field is passed.
5580 * @parent is XML element where to insert */
5581 static isds_error insert_credentials_delivery(struct isds_ctx *context,
5582 const struct isds_credentials_delivery *credentials_delivery,
5583 xmlNodePtr parent) {
5584 isds_error err = IE_SUCCESS;
5585 xmlNodePtr node;
5587 if (!context) return IE_INVALID_CONTEXT;
5588 if (!parent) return IE_INVAL;
5590 if (credentials_delivery) {
5591 /* Following elements are valid only for services:
5592 * NewAccessData, AddDataBoxUser, CreateDataBox */
5593 INSERT_SCALAR_BOOLEAN(parent, "dbVirtual", 1);
5594 INSERT_STRING(parent, "email", credentials_delivery->email);
5597 leave:
5598 return err;
5602 /* Extract credentials delivery from ISDS response.
5603 * @context is session context
5604 * @credentials_delivery is pointer to valid structure to fill in returned
5605 * user's password (and new log-in name). If NULL, do not extract the data.
5606 * @response is pointer to XML document with ISDS response
5607 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
5608 * @return IE_SUCCESS even if new user name has not been found because it's not
5609 * clear whether it's returned always. */
5610 static isds_error extract_credentials_delivery(struct isds_ctx *context,
5611 struct isds_credentials_delivery *credentials_delivery,
5612 xmlDocPtr response, const char *request_name) {
5613 isds_error err = IE_SUCCESS;
5614 xmlXPathContextPtr xpath_ctx = NULL;
5615 xmlXPathObjectPtr result = NULL;
5616 char *xpath_query = NULL;
5618 if (!context) return IE_INVALID_CONTEXT;
5619 if (credentials_delivery) {
5620 zfree(credentials_delivery->token);
5621 zfree(credentials_delivery->new_user_name);
5623 if (!response || !request_name || !*request_name) return IE_INVAL;
5626 /* Extract optional token */
5627 if (credentials_delivery) {
5628 xpath_ctx = xmlXPathNewContext(response);
5629 if (!xpath_ctx) {
5630 err = IE_ERROR;
5631 goto leave;
5633 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5634 err = IE_ERROR;
5635 goto leave;
5638 /* Verify root element */
5639 if (-1 == isds_asprintf(&xpath_query, "/isds:%sResponse",
5640 request_name)) {
5641 err = IE_NOMEM;
5642 goto leave;
5644 result = xmlXPathEvalExpression(BAD_CAST xpath_query, xpath_ctx);
5645 if (!result) {
5646 err = IE_ERROR;
5647 goto leave;
5649 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5650 char *request_name_locale = _isds_utf82locale(request_name);
5651 isds_log(ILF_ISDS, ILL_WARNING,
5652 _("Wrong element in ISDS response for %s request "
5653 "while extracting credentials delivery details\n"),
5654 request_name_locale);
5655 free(request_name_locale);
5656 err = IE_ERROR;
5657 goto leave;
5659 xpath_ctx->node = result->nodesetval->nodeTab[0];
5662 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
5663 * optional. */
5664 EXTRACT_STRING("isds:dbUserID", credentials_delivery->new_user_name);
5666 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery->token);
5667 if (!credentials_delivery->token) {
5668 char *request_name_locale = _isds_utf82locale(request_name);
5669 isds_log(ILF_ISDS, ILL_ERR,
5670 _("ISDS did not return token on %s request "
5671 "even if requested\n"), request_name_locale);
5672 free(request_name_locale);
5673 err = IE_ERROR;
5677 leave:
5678 free(xpath_query);
5679 xmlXPathFreeObject(result);
5680 xmlXPathFreeContext(xpath_ctx);
5682 return err;
5686 /* Build XSD:tCreateDBInput request type for box creating.
5687 * @context is session context
5688 * @request outputs built XML tree
5689 * @service_name is request name of SERVICE_DB_MANIPULATION service
5690 * @box is box description to create including single primary user (in case of
5691 * FO box type)
5692 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
5693 * box, or contact address of PFO box owner)
5694 * @former_names is optional former name of box owner. Pass NULL if otherwise.
5695 * @upper_box_id is optional ID of supper box if currently created box is
5696 * subordinated.
5697 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
5698 * don't care.
5699 * @credentials_delivery is valid pointer if ISDS should return token that box
5700 * owner can use to obtain his new credentials in on-line way. Then valid email
5701 * member value should be supplied.
5702 * @approval is optional external approval of box manipulation */
5703 static isds_error build_CreateDBInput_request(struct isds_ctx *context,
5704 xmlNodePtr *request, const xmlChar *service_name,
5705 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
5706 const xmlChar *former_names, const xmlChar *upper_box_id,
5707 const xmlChar *ceo_label,
5708 const struct isds_credentials_delivery *credentials_delivery,
5709 const struct isds_approval *approval) {
5710 isds_error err = IE_SUCCESS;
5711 xmlNsPtr isds_ns = NULL;
5712 xmlNodePtr node, dbPrimaryUsers;
5713 xmlChar *string = NULL;
5714 const struct isds_list *item;
5717 if (!context) return IE_INVALID_CONTEXT;
5718 if (!request || !service_name || service_name[0] == '\0' || !box)
5719 return IE_INVAL;
5722 /* Build CreateDataBox-similar request */
5723 *request = xmlNewNode(NULL, service_name);
5724 if (!*request) {
5725 char *service_name_locale = _isds_utf82locale((char*) service_name);
5726 isds_printf_message(context, _("Could build %s request"),
5727 service_name_locale);
5728 free(service_name_locale);
5729 return IE_ERROR;
5731 if (context->type == CTX_TYPE_TESTING_REQUEST_COLLECTOR) {
5732 isds_ns = xmlNewNs(*request, BAD_CAST ISDS1_NS, NULL);
5733 if (!isds_ns) {
5734 isds_log_message(context, _("Could not create ISDS1 name space"));
5735 xmlFreeNode(*request);
5736 return IE_ERROR;
5738 } else {
5739 isds_ns = xmlNewNs(*request, BAD_CAST ISDS_NS, NULL);
5740 if (!isds_ns) {
5741 isds_log_message(context, _("Could not create ISDS name space"));
5742 xmlFreeNode(*request);
5743 return IE_ERROR;
5746 xmlSetNs(*request, isds_ns);
5748 INSERT_ELEMENT(node, *request, "dbOwnerInfo");
5749 err = insert_DbOwnerInfo(context, box, node);
5750 if (err) goto leave;
5752 /* Insert users */
5753 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
5754 * verbose documentation allows none dbUserInfo */
5755 INSERT_ELEMENT(dbPrimaryUsers, *request, "dbPrimaryUsers");
5756 for (item = users; item; item = item->next) {
5757 if (item->data) {
5758 INSERT_ELEMENT(node, dbPrimaryUsers, "dbUserInfo");
5759 err = insert_DbUserInfo(context,
5760 (struct isds_DbUserInfo *) item->data, node);
5761 if (err) goto leave;
5765 INSERT_STRING(*request, "dbFormerNames", former_names);
5766 INSERT_STRING(*request, "dbUpperDBId", upper_box_id);
5767 INSERT_STRING(*request, "dbCEOLabel", ceo_label);
5769 err = insert_credentials_delivery(context, credentials_delivery, *request);
5770 if (err) goto leave;
5772 err = insert_GExtApproval(context, approval, *request);
5773 if (err) goto leave;
5775 leave:
5776 if (err) {
5777 xmlFreeNode(*request);
5778 *request = NULL;
5780 free(string);
5781 return err;
5783 #endif /* HAVE_LIBCURL */
5786 /* Create new box.
5787 * @context is session context
5788 * @box is box description to create including single primary user (in case of
5789 * FO box type). It outputs box ID assigned by ISDS in dbID element.
5790 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
5791 * box, or contact address of PFO box owner)
5792 * @former_names is optional former name of box owner. Pass NULL if you don't care.
5793 * @upper_box_id is optional ID of supper box if currently created box is
5794 * subordinated.
5795 * @ceo_label is optional title of OVM box owner (e.g. mayor)
5796 * @credentials_delivery is NULL if new password should be delivered off-line
5797 * to box owner. It is valid pointer if owner should obtain new password on-line
5798 * on dedicated web server. Then input @credentials_delivery.email value is
5799 * his e-mail address he must provide to dedicated web server together
5800 * with output reallocated @credentials_delivery.token member. Output
5801 * member @credentials_delivery.new_user_name is unused up on this call.
5802 * @approval is optional external approval of box manipulation
5803 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5804 * NULL, if you don't care.*/
5805 isds_error isds_add_box(struct isds_ctx *context,
5806 struct isds_DbOwnerInfo *box, const struct isds_list *users,
5807 const char *former_names, const char *upper_box_id,
5808 const char *ceo_label,
5809 struct isds_credentials_delivery *credentials_delivery,
5810 const struct isds_approval *approval, char **refnumber) {
5811 isds_error err = IE_SUCCESS;
5812 #if HAVE_LIBCURL
5813 xmlNodePtr request = NULL;
5814 xmlDocPtr response = NULL;
5815 xmlXPathContextPtr xpath_ctx = NULL;
5816 xmlXPathObjectPtr result = NULL;
5817 #endif
5820 if (!context) return IE_INVALID_CONTEXT;
5821 zfree(context->long_message);
5822 if (credentials_delivery) {
5823 zfree(credentials_delivery->token);
5824 zfree(credentials_delivery->new_user_name);
5826 if (!box) return IE_INVAL;
5828 #if HAVE_LIBCURL
5829 /* Scratch box ID */
5830 zfree(box->dbID);
5832 /* Build CreateDataBox request */
5833 err = build_CreateDBInput_request(context,
5834 &request, BAD_CAST "CreateDataBox",
5835 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
5836 (xmlChar *) ceo_label, credentials_delivery, approval);
5837 if (err) goto leave;
5839 /* Send it to server and process response */
5840 err = send_destroy_request_check_response(context,
5841 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
5842 &response, (xmlChar **) refnumber);
5844 /* Extract box ID */
5845 xpath_ctx = xmlXPathNewContext(response);
5846 if (!xpath_ctx) {
5847 err = IE_ERROR;
5848 goto leave;
5850 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5851 err = IE_ERROR;
5852 goto leave;
5854 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
5856 /* Extract optional token */
5857 err = extract_credentials_delivery(context, credentials_delivery, response,
5858 "CreateDataBox");
5860 leave:
5861 xmlXPathFreeObject(result);
5862 xmlXPathFreeContext(xpath_ctx);
5863 xmlFreeDoc(response);
5864 xmlFreeNode(request);
5866 if (!err) {
5867 isds_log(ILF_ISDS, ILL_DEBUG,
5868 _("CreateDataBox request processed by server successfully.\n"));
5870 #else /* not HAVE_LIBCURL */
5871 err = IE_NOTSUP;
5872 #endif
5874 return err;
5878 /* Notify ISDS about new PFO entity.
5879 * This function has no real effect.
5880 * @context is session context
5881 * @box is PFO description including single primary user.
5882 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
5883 * @former_names is optional undocumented string. Pass NULL if you don't care.
5884 * @upper_box_id is optional ID of supper box if currently created box is
5885 * subordinated.
5886 * @ceo_label is optional title of OVM box owner (e.g. mayor)
5887 * @approval is optional external approval of box manipulation
5888 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5889 * NULL, if you don't care.*/
5890 isds_error isds_add_pfoinfo(struct isds_ctx *context,
5891 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
5892 const char *former_names, const char *upper_box_id,
5893 const char *ceo_label, const struct isds_approval *approval,
5894 char **refnumber) {
5895 isds_error err = IE_SUCCESS;
5896 #if HAVE_LIBCURL
5897 xmlNodePtr request = NULL;
5898 #endif
5900 if (!context) return IE_INVALID_CONTEXT;
5901 zfree(context->long_message);
5902 if (!box) return IE_INVAL;
5904 #if HAVE_LIBCURL
5905 /* Build CreateDataBoxPFOInfo request */
5906 err = build_CreateDBInput_request(context,
5907 &request, BAD_CAST "CreateDataBoxPFOInfo",
5908 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
5909 (xmlChar *) ceo_label, NULL, approval);
5910 if (err) goto leave;
5912 /* Send it to server and process response */
5913 err = send_request_check_drop_response(context,
5914 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
5915 (xmlChar **) refnumber);
5916 /* XXX: XML Schema names output dbID element but textual documentation
5917 * states no box identifier is returned. */
5918 leave:
5919 xmlFreeNode(request);
5920 #else /* not HAVE_LIBCURL */
5921 err = IE_NOTSUP;
5922 #endif
5923 return err;
5927 /* Common implementation for removing given box.
5928 * @context is session context
5929 * @service_name is UTF-8 encoded name fo ISDS service
5930 * @box is box description to delete
5931 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
5932 * carry sane value. If NULL, do not inject this information into request.
5933 * @approval is optional external approval of box manipulation
5934 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5935 * NULL, if you don't care.*/
5936 isds_error _isds_delete_box_common(struct isds_ctx *context,
5937 const xmlChar *service_name,
5938 const struct isds_DbOwnerInfo *box, const struct tm *since,
5939 const struct isds_approval *approval, char **refnumber) {
5940 isds_error err = IE_SUCCESS;
5941 #if HAVE_LIBCURL
5942 xmlNsPtr isds_ns = NULL;
5943 xmlNodePtr request = NULL;
5944 xmlNodePtr node;
5945 xmlChar *string = NULL;
5946 #endif
5949 if (!context) return IE_INVALID_CONTEXT;
5950 zfree(context->long_message);
5951 if (!service_name || !*service_name || !box) return IE_INVAL;
5954 #if HAVE_LIBCURL
5955 /* Build DeleteDataBox(Promptly) request */
5956 request = xmlNewNode(NULL, service_name);
5957 if (!request) {
5958 char *service_name_locale = _isds_utf82locale((char*)service_name);
5959 isds_printf_message(context,
5960 _("Could build %s request"), service_name_locale);
5961 free(service_name_locale);
5962 return IE_ERROR;
5964 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
5965 if(!isds_ns) {
5966 isds_log_message(context, _("Could not create ISDS name space"));
5967 xmlFreeNode(request);
5968 return IE_ERROR;
5970 xmlSetNs(request, isds_ns);
5972 INSERT_ELEMENT(node, request, "dbOwnerInfo");
5973 err = insert_DbOwnerInfo(context, box, node);
5974 if (err) goto leave;
5976 if (since) {
5977 err = tm2datestring(since, &string);
5978 if (err) {
5979 isds_log_message(context,
5980 _("Could not convert `since' argument to ISO date string"));
5981 goto leave;
5983 INSERT_STRING(request, "dbOwnerTerminationDate", string);
5984 zfree(string);
5987 err = insert_GExtApproval(context, approval, request);
5988 if (err) goto leave;
5991 /* Send it to server and process response */
5992 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
5993 service_name, &request, (xmlChar **) refnumber);
5995 leave:
5996 xmlFreeNode(request);
5997 free(string);
5998 #else /* not HAVE_LIBCURL */
5999 err = IE_NOTSUP;
6000 #endif
6001 return err;
6005 /* Remove given box permanently.
6006 * @context is session context
6007 * @box is box description to delete
6008 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6009 * carry sane value.
6010 * @approval is optional external approval of box manipulation
6011 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6012 * NULL, if you don't care.*/
6013 isds_error isds_delete_box(struct isds_ctx *context,
6014 const struct isds_DbOwnerInfo *box, const struct tm *since,
6015 const struct isds_approval *approval, char **refnumber) {
6016 if (!context) return IE_INVALID_CONTEXT;
6017 zfree(context->long_message);
6018 if (!box || !since) return IE_INVAL;
6020 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBox",
6021 box, since, approval, refnumber);
6025 /* Undocumented function.
6026 * @context is session context
6027 * @box is box description to delete
6028 * @approval is optional external approval of box manipulation
6029 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6030 * NULL, if you don't care.*/
6031 isds_error isds_delete_box_promptly(struct isds_ctx *context,
6032 const struct isds_DbOwnerInfo *box,
6033 const struct isds_approval *approval, char **refnumber) {
6034 if (!context) return IE_INVALID_CONTEXT;
6035 zfree(context->long_message);
6036 if (!box) return IE_INVAL;
6038 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBoxPromptly",
6039 box, NULL, approval, refnumber);
6043 /* Update data about given box.
6044 * @context is session context
6045 * @old_box current box description
6046 * @new_box are updated data about @old_box
6047 * @approval is optional external approval of box manipulation
6048 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6049 * NULL, if you don't care.*/
6050 isds_error isds_UpdateDataBoxDescr(struct isds_ctx *context,
6051 const struct isds_DbOwnerInfo *old_box,
6052 const struct isds_DbOwnerInfo *new_box,
6053 const struct isds_approval *approval, char **refnumber) {
6054 isds_error err = IE_SUCCESS;
6055 #if HAVE_LIBCURL
6056 xmlNsPtr isds_ns = NULL;
6057 xmlNodePtr request = NULL;
6058 xmlNodePtr node;
6059 #endif
6062 if (!context) return IE_INVALID_CONTEXT;
6063 zfree(context->long_message);
6064 if (!old_box || !new_box) return IE_INVAL;
6067 #if HAVE_LIBCURL
6068 /* Build UpdateDataBoxDescr request */
6069 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxDescr");
6070 if (!request) {
6071 isds_log_message(context,
6072 _("Could build UpdateDataBoxDescr request"));
6073 return IE_ERROR;
6075 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6076 if(!isds_ns) {
6077 isds_log_message(context, _("Could not create ISDS name space"));
6078 xmlFreeNode(request);
6079 return IE_ERROR;
6081 xmlSetNs(request, isds_ns);
6083 INSERT_ELEMENT(node, request, "dbOldOwnerInfo");
6084 err = insert_DbOwnerInfo(context, old_box, node);
6085 if (err) goto leave;
6087 INSERT_ELEMENT(node, request, "dbNewOwnerInfo");
6088 err = insert_DbOwnerInfo(context, new_box, node);
6089 if (err) goto leave;
6091 err = insert_GExtApproval(context, approval, request);
6092 if (err) goto leave;
6095 /* Send it to server and process response */
6096 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6097 BAD_CAST "UpdateDataBoxDescr", &request, (xmlChar **) refnumber);
6099 leave:
6100 xmlFreeNode(request);
6101 #else /* not HAVE_LIBCURL */
6102 err = IE_NOTSUP;
6103 #endif
6105 return err;
6109 #if HAVE_LIBCURL
6110 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6111 * code
6112 * @context is session context
6113 * @service is SOAP service
6114 * @service_name is name of request in @service
6115 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6116 * @box_id is box ID of interest
6117 * @approval is optional external approval of box manipulation
6118 * @response is server SOAP body response as XML document
6119 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6120 * NULL, if you don't care.
6121 * @return error coded from lower layer, context message will be set up
6122 * appropriately. */
6123 static isds_error build_send_dbid_request_check_response(
6124 struct isds_ctx *context, const isds_service service,
6125 const xmlChar *service_name, const xmlChar *box_id_element,
6126 const xmlChar *box_id, const struct isds_approval *approval,
6127 xmlDocPtr *response, xmlChar **refnumber) {
6129 isds_error err = IE_SUCCESS;
6130 char *service_name_locale = NULL, *box_id_locale = NULL;
6131 xmlNodePtr request = NULL, node;
6132 xmlNsPtr isds_ns = NULL;
6134 if (!context) return IE_INVALID_CONTEXT;
6135 if (!service_name || !box_id) return IE_INVAL;
6136 if (!response) return IE_INVAL;
6138 /* Free output argument */
6139 xmlFreeDoc(*response); *response = NULL;
6141 /* Prepare strings */
6142 service_name_locale = _isds_utf82locale((char*)service_name);
6143 if (!service_name_locale) {
6144 err = IE_NOMEM;
6145 goto leave;
6147 box_id_locale = _isds_utf82locale((char*)box_id);
6148 if (!box_id_locale) {
6149 err = IE_NOMEM;
6150 goto leave;
6153 /* Build request */
6154 request = xmlNewNode(NULL, service_name);
6155 if (!request) {
6156 isds_printf_message(context,
6157 _("Could not build %s request for %s box"), service_name_locale,
6158 box_id_locale);
6159 err = IE_ERROR;
6160 goto leave;
6162 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6163 if(!isds_ns) {
6164 isds_log_message(context, _("Could not create ISDS name space"));
6165 err = IE_ERROR;
6166 goto leave;
6168 xmlSetNs(request, isds_ns);
6170 /* Add XSD:tIdDbInput children */
6171 if (NULL == box_id_element) box_id_element = BAD_CAST "dbID";
6172 INSERT_STRING(request, box_id_element, box_id);
6173 err = insert_GExtApproval(context, approval, request);
6174 if (err) goto leave;
6176 /* Send request and check response*/
6177 err = send_destroy_request_check_response(context,
6178 service, service_name, &request, response, refnumber);
6180 leave:
6181 free(service_name_locale);
6182 free(box_id_locale);
6183 xmlFreeNode(request);
6184 return err;
6186 #endif /* HAVE_LIBCURL */
6189 /* Get data about all users assigned to given box.
6190 * @context is session context
6191 * @box_id is box ID
6192 * @users is automatically reallocated list of struct isds_DbUserInfo */
6193 isds_error isds_GetDataBoxUsers(struct isds_ctx *context, const char *box_id,
6194 struct isds_list **users) {
6195 isds_error err = IE_SUCCESS;
6196 #if HAVE_LIBCURL
6197 xmlDocPtr response = NULL;
6198 xmlXPathContextPtr xpath_ctx = NULL;
6199 xmlXPathObjectPtr result = NULL;
6200 int i;
6201 struct isds_list *item, *prev_item = NULL;
6202 #endif
6204 if (!context) return IE_INVALID_CONTEXT;
6205 zfree(context->long_message);
6206 if (!users || !box_id) return IE_INVAL;
6207 isds_list_free(users);
6210 #if HAVE_LIBCURL
6211 /* Do request and check for success */
6212 err = build_send_dbid_request_check_response(context,
6213 SERVICE_DB_MANIPULATION, BAD_CAST "GetDataBoxUsers", NULL,
6214 BAD_CAST box_id, NULL, &response, NULL);
6215 if (err) goto leave;
6218 /* Extract data */
6219 /* Prepare structure */
6220 xpath_ctx = xmlXPathNewContext(response);
6221 if (!xpath_ctx) {
6222 err = IE_ERROR;
6223 goto leave;
6225 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6226 err = IE_ERROR;
6227 goto leave;
6230 /* Set context node */
6231 result = xmlXPathEvalExpression(BAD_CAST
6232 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6233 xpath_ctx);
6234 if (!result) {
6235 err = IE_ERROR;
6236 goto leave;
6238 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6239 /* Iterate over all users */
6240 for (i = 0; i < result->nodesetval->nodeNr; i++) {
6242 /* Prepare structure */
6243 item = calloc(1, sizeof(*item));
6244 if (!item) {
6245 err = IE_NOMEM;
6246 goto leave;
6248 item->destructor = (void(*)(void**))isds_DbUserInfo_free;
6249 if (i == 0) *users = item;
6250 else prev_item->next = item;
6251 prev_item = item;
6253 /* Extract it */
6254 xpath_ctx->node = result->nodesetval->nodeTab[i];
6255 err = extract_DbUserInfo(context,
6256 (struct isds_DbUserInfo **) (&item->data), xpath_ctx);
6257 if (err) goto leave;
6261 leave:
6262 if (err) {
6263 isds_list_free(users);
6266 xmlXPathFreeObject(result);
6267 xmlXPathFreeContext(xpath_ctx);
6268 xmlFreeDoc(response);
6270 if (!err)
6271 isds_log(ILF_ISDS, ILL_DEBUG,
6272 _("GetDataBoxUsers request processed by server "
6273 "successfully.\n"));
6274 #else /* not HAVE_LIBCURL */
6275 err = IE_NOTSUP;
6276 #endif
6278 return err;
6282 /* Update data about user assigned to given box.
6283 * @context is session context
6284 * @box is box identification
6285 * @old_user identifies user to update
6286 * @new_user are updated data about @old_user
6287 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6288 * NULL, if you don't care.*/
6289 isds_error isds_UpdateDataBoxUser(struct isds_ctx *context,
6290 const struct isds_DbOwnerInfo *box,
6291 const struct isds_DbUserInfo *old_user,
6292 const struct isds_DbUserInfo *new_user,
6293 char **refnumber) {
6294 isds_error err = IE_SUCCESS;
6295 #if HAVE_LIBCURL
6296 xmlNsPtr isds_ns = NULL;
6297 xmlNodePtr request = NULL;
6298 xmlNodePtr node;
6299 #endif
6302 if (!context) return IE_INVALID_CONTEXT;
6303 zfree(context->long_message);
6304 if (!box || !old_user || !new_user) return IE_INVAL;
6307 #if HAVE_LIBCURL
6308 /* Build UpdateDataBoxUser request */
6309 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxUser");
6310 if (!request) {
6311 isds_log_message(context,
6312 _("Could build UpdateDataBoxUser request"));
6313 return IE_ERROR;
6315 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6316 if(!isds_ns) {
6317 isds_log_message(context, _("Could not create ISDS name space"));
6318 xmlFreeNode(request);
6319 return IE_ERROR;
6321 xmlSetNs(request, isds_ns);
6323 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6324 err = insert_DbOwnerInfo(context, box, node);
6325 if (err) goto leave;
6327 INSERT_ELEMENT(node, request, "dbOldUserInfo");
6328 err = insert_DbUserInfo(context, old_user, node);
6329 if (err) goto leave;
6331 INSERT_ELEMENT(node, request, "dbNewUserInfo");
6332 err = insert_DbUserInfo(context, new_user, node);
6333 if (err) goto leave;
6335 /* Send it to server and process response */
6336 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6337 BAD_CAST "UpdateDataBoxUser", &request, (xmlChar **) refnumber);
6339 leave:
6340 xmlFreeNode(request);
6341 #else /* not HAVE_LIBCURL */
6342 err = IE_NOTSUP;
6343 #endif
6345 return err;
6349 /* Undocumented function.
6350 * @context is session context
6351 * @box_id is UTF-8 encoded box identifier
6352 * @token is UTF-8 encoded temporary password
6353 * @user_id outputs UTF-8 encoded reallocated user identifier
6354 * @password outpus UTF-8 encoded reallocated user password
6355 * Output arguments will be nulled in case of error */
6356 isds_error isds_activate(struct isds_ctx *context,
6357 const char *box_id, const char *token,
6358 char **user_id, char **password) {
6359 isds_error err = IE_SUCCESS;
6360 #if HAVE_LIBCURL
6361 xmlNsPtr isds_ns = NULL;
6362 xmlNodePtr request = NULL, node;
6363 xmlDocPtr response = NULL;
6364 xmlXPathContextPtr xpath_ctx = NULL;
6365 xmlXPathObjectPtr result = NULL;
6366 #endif
6369 if (!context) return IE_INVALID_CONTEXT;
6370 zfree(context->long_message);
6372 if (user_id) zfree(*user_id);
6373 if (password) zfree(*password);
6375 if (!box_id || !token || !user_id || !password) return IE_INVAL;
6378 #if HAVE_LIBCURL
6379 /* Build Activate request */
6380 request = xmlNewNode(NULL, BAD_CAST "Activate");
6381 if (!request) {
6382 isds_log_message(context, _("Could build Activate request"));
6383 return IE_ERROR;
6385 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6386 if(!isds_ns) {
6387 isds_log_message(context, _("Could not create ISDS name space"));
6388 xmlFreeNode(request);
6389 return IE_ERROR;
6391 xmlSetNs(request, isds_ns);
6393 INSERT_STRING(request, "dbAccessDataId", token);
6394 CHECK_FOR_STRING_LENGTH(box_id, 7, 7, "dbID");
6395 INSERT_STRING(request, "dbID", box_id);
6398 /* Send request and check response*/
6399 err = send_destroy_request_check_response(context,
6400 SERVICE_DB_MANIPULATION, BAD_CAST "Activate", &request,
6401 &response, NULL);
6402 if (err) goto leave;
6405 /* Extract data */
6406 xpath_ctx = xmlXPathNewContext(response);
6407 if (!xpath_ctx) {
6408 err = IE_ERROR;
6409 goto leave;
6411 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6412 err = IE_ERROR;
6413 goto leave;
6415 result = xmlXPathEvalExpression(BAD_CAST "/isds:ActivateResponse",
6416 xpath_ctx);
6417 if (!result) {
6418 err = IE_ERROR;
6419 goto leave;
6421 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6422 isds_log_message(context, _("Missing ActivateResponse element"));
6423 err = IE_ISDS;
6424 goto leave;
6426 if (result->nodesetval->nodeNr > 1) {
6427 isds_log_message(context, _("Multiple ActivateResponse element"));
6428 err = IE_ISDS;
6429 goto leave;
6431 xpath_ctx->node = result->nodesetval->nodeTab[0];
6432 xmlXPathFreeObject(result); result = NULL;
6434 EXTRACT_STRING("isds:userId", *user_id);
6435 if (!*user_id)
6436 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6437 "but did not return `userId' element.\n"));
6439 EXTRACT_STRING("isds:password", *password);
6440 if (!*password)
6441 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6442 "but did not return `password' element.\n"));
6444 leave:
6445 xmlXPathFreeObject(result);
6446 xmlXPathFreeContext(xpath_ctx);
6447 xmlFreeDoc(response);
6448 xmlFreeNode(request);
6450 if (!err)
6451 isds_log(ILF_ISDS, ILL_DEBUG,
6452 _("Activate request processed by server successfully.\n"));
6453 #else /* not HAVE_LIBCURL */
6454 err = IE_NOTSUP;
6455 #endif
6457 return err;
6461 /* Reset credentials of user assigned to given box.
6462 * @context is session context
6463 * @box is box identification
6464 * @user identifies user to reset password
6465 * @fee_paid is true if fee has been paid, false otherwise
6466 * @approval is optional external approval of box manipulation
6467 * @credentials_delivery is NULL if new password should be delivered off-line
6468 * to the user. It is valid pointer if user should obtain new password on-line
6469 * on dedicated web server. Then input @credentials_delivery.email value is
6470 * user's e-mail address user must provide to dedicated web server together
6471 * with @credentials_delivery.token. The output reallocated token user needs
6472 * to use to authorize on the web server to view his new password. Output
6473 * reallocated @credentials_delivery.new_user_name is user's log-in name that
6474 * ISDS changed up on this call. (No reason why server could change the name
6475 * is known now.)
6476 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6477 * NULL, if you don't care.*/
6478 isds_error isds_reset_password(struct isds_ctx *context,
6479 const struct isds_DbOwnerInfo *box,
6480 const struct isds_DbUserInfo *user,
6481 const _Bool fee_paid, const struct isds_approval *approval,
6482 struct isds_credentials_delivery *credentials_delivery,
6483 char **refnumber) {
6484 isds_error err = IE_SUCCESS;
6485 #if HAVE_LIBCURL
6486 xmlNsPtr isds_ns = NULL;
6487 xmlNodePtr request = NULL, node;
6488 xmlDocPtr response = NULL;
6489 #endif
6492 if (!context) return IE_INVALID_CONTEXT;
6493 zfree(context->long_message);
6495 if (credentials_delivery) {
6496 zfree(credentials_delivery->token);
6497 zfree(credentials_delivery->new_user_name);
6499 if (!box || !user) return IE_INVAL;
6502 #if HAVE_LIBCURL
6503 /* Build NewAccessData request */
6504 request = xmlNewNode(NULL, BAD_CAST "NewAccessData");
6505 if (!request) {
6506 isds_log_message(context,
6507 _("Could build NewAccessData request"));
6508 return IE_ERROR;
6510 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6511 if(!isds_ns) {
6512 isds_log_message(context, _("Could not create ISDS name space"));
6513 xmlFreeNode(request);
6514 return IE_ERROR;
6516 xmlSetNs(request, isds_ns);
6518 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6519 err = insert_DbOwnerInfo(context, box, node);
6520 if (err) goto leave;
6522 INSERT_ELEMENT(node, request, "dbUserInfo");
6523 err = insert_DbUserInfo(context, user, node);
6524 if (err) goto leave;
6526 INSERT_SCALAR_BOOLEAN(request, "dbFeePaid", fee_paid);
6528 err = insert_credentials_delivery(context, credentials_delivery, request);
6529 if (err) goto leave;
6531 err = insert_GExtApproval(context, approval, request);
6532 if (err) goto leave;
6534 /* Send request and check response*/
6535 err = send_destroy_request_check_response(context,
6536 SERVICE_DB_MANIPULATION, BAD_CAST "NewAccessData", &request,
6537 &response, (xmlChar **) refnumber);
6538 if (err) goto leave;
6541 /* Extract optional token */
6542 err = extract_credentials_delivery(context, credentials_delivery,
6543 response, "NewAccessData");
6545 leave:
6546 xmlFreeDoc(response);
6547 xmlFreeNode(request);
6549 if (!err)
6550 isds_log(ILF_ISDS, ILL_DEBUG,
6551 _("NewAccessData request processed by server "
6552 "successfully.\n"));
6553 #else /* not HAVE_LIBCURL */
6554 err = IE_NOTSUP;
6555 #endif
6557 return err;
6561 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
6562 * code, destroy response and log success.
6563 * @context is ISDS session context.
6564 * @service_name is name of SERVICE_DB_MANIPULATION service
6565 * @box is box identification
6566 * @user identifies user to remove
6567 * @credentials_delivery is NULL if new user's password should be delivered
6568 * off-line to the user. It is valid pointer if user should obtain new
6569 * password on-line on dedicated web server. Then input
6570 * @credentials_delivery.email value is user's e-mail address user must
6571 * provide to dedicated web server together with @credentials_delivery.token.
6572 * The output reallocated token user needs to use to authorize on the web
6573 * server to view his new password. Output reallocated
6574 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6575 * assingned or changed up on this call.
6576 * @approval is optional external approval of box manipulation
6577 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6578 * NULL, if you don't care. */
6579 static isds_error build_send_manipulationboxuser_request_check_drop_response(
6580 struct isds_ctx *context, const xmlChar *service_name,
6581 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6582 struct isds_credentials_delivery *credentials_delivery,
6583 const struct isds_approval *approval, xmlChar **refnumber) {
6584 isds_error err = IE_SUCCESS;
6585 #if HAVE_LIBCURL
6586 xmlNsPtr isds_ns = NULL;
6587 xmlNodePtr request = NULL, node;
6588 xmlDocPtr response = NULL;
6589 #endif
6592 if (!context) return IE_INVALID_CONTEXT;
6593 zfree(context->long_message);
6594 if (credentials_delivery) {
6595 zfree(credentials_delivery->token);
6596 zfree(credentials_delivery->new_user_name);
6598 if (!service_name || service_name[0] == '\0' || !box || !user)
6599 return IE_INVAL;
6602 #if HAVE_LIBCURL
6603 /* Build NewAccessData or similar request */
6604 request = xmlNewNode(NULL, service_name);
6605 if (!request) {
6606 char *service_name_locale = _isds_utf82locale((char *) service_name);
6607 isds_printf_message(context, _("Could not build %s request"),
6608 service_name_locale);
6609 free(service_name_locale);
6610 return IE_ERROR;
6612 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6613 if(!isds_ns) {
6614 isds_log_message(context, _("Could not create ISDS name space"));
6615 xmlFreeNode(request);
6616 return IE_ERROR;
6618 xmlSetNs(request, isds_ns);
6620 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6621 err = insert_DbOwnerInfo(context, box, node);
6622 if (err) goto leave;
6624 INSERT_ELEMENT(node, request, "dbUserInfo");
6625 err = insert_DbUserInfo(context, user, node);
6626 if (err) goto leave;
6628 err = insert_credentials_delivery(context, credentials_delivery, request);
6629 if (err) goto leave;
6631 err = insert_GExtApproval(context, approval, request);
6632 if (err) goto leave;
6635 /* Send request and check response*/
6636 err = send_destroy_request_check_response(context,
6637 SERVICE_DB_MANIPULATION, service_name, &request, &response, refnumber);
6639 xmlFreeNode(request);
6640 request = NULL;
6642 /* Pick up credentials_delivery if requested */
6643 err = extract_credentials_delivery(context, credentials_delivery, response,
6644 (char *)service_name);
6646 leave:
6647 xmlFreeDoc(response);
6648 if (request) xmlFreeNode(request);
6650 if (!err) {
6651 char *service_name_locale = _isds_utf82locale((char *) service_name);
6652 isds_log(ILF_ISDS, ILL_DEBUG,
6653 _("%s request processed by server successfully.\n"),
6654 service_name_locale);
6655 free(service_name_locale);
6657 #else /* not HAVE_LIBCURL */
6658 err = IE_NOTSUP;
6659 #endif
6661 return err;
6665 /* Assign new user to given box.
6666 * @context is session context
6667 * @box is box identification
6668 * @user defines new user to add
6669 * @credentials_delivery is NULL if new user's password should be delivered
6670 * off-line to the user. It is valid pointer if user should obtain new
6671 * password on-line on dedicated web server. Then input
6672 * @credentials_delivery.email value is user's e-mail address user must
6673 * provide to dedicated web server together with @credentials_delivery.token.
6674 * The output reallocated token user needs to use to authorize on the web
6675 * server to view his new password. Output reallocated
6676 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6677 * assingned up on this call.
6678 * @approval is optional external approval of box manipulation
6679 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6680 * NULL, if you don't care.*/
6681 isds_error isds_add_user(struct isds_ctx *context,
6682 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6683 struct isds_credentials_delivery *credentials_delivery,
6684 const struct isds_approval *approval, char **refnumber) {
6685 return build_send_manipulationboxuser_request_check_drop_response(context,
6686 BAD_CAST "AddDataBoxUser", box, user, credentials_delivery,
6687 approval, (xmlChar **) refnumber);
6691 /* Remove user assigned to given box.
6692 * @context is session context
6693 * @box is box identification
6694 * @user identifies user to remove
6695 * @approval is optional external approval of box manipulation
6696 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6697 * NULL, if you don't care.*/
6698 isds_error isds_delete_user(struct isds_ctx *context,
6699 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6700 const struct isds_approval *approval, char **refnumber) {
6701 return build_send_manipulationboxuser_request_check_drop_response(context,
6702 BAD_CAST "DeleteDataBoxUser", box, user, NULL, approval,
6703 (xmlChar **) refnumber);
6707 /* Get list of boxes in ZIP archive.
6708 * @context is session context
6709 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
6710 * System recognizes following values currently: ALL (all boxes), UPG
6711 * (effectively OVM boxes), OVM (OVM gross type boxes), OPN (boxes allowing
6712 * receiving commercial messages). This argument is a string because
6713 * specification states new values can appear in the future. Not all list
6714 * types are available to all users.
6715 * @buffer is automatically reallocated memory to store the list of boxes. The
6716 * list is zipped CSV file.
6717 * @buffer_length is size of @buffer data in bytes.
6718 * In case of error @buffer will be freed and @buffer_length will be
6719 * undefined.*/
6720 isds_error isds_get_box_list_archive(struct isds_ctx *context,
6721 const char *list_identifier, void **buffer, size_t *buffer_length) {
6722 isds_error err = IE_SUCCESS;
6723 #if HAVE_LIBCURL
6724 xmlNsPtr isds_ns = NULL;
6725 xmlNodePtr request = NULL, node;
6726 xmlDocPtr response = NULL;
6727 xmlXPathContextPtr xpath_ctx = NULL;
6728 xmlXPathObjectPtr result = NULL;
6729 char *string = NULL;
6730 #endif
6733 if (!context) return IE_INVALID_CONTEXT;
6734 zfree(context->long_message);
6735 if (buffer) zfree(*buffer);
6736 if (!buffer || !buffer_length) return IE_INVAL;
6739 #if HAVE_LIBCURL
6740 /* Check if connection is established
6741 * TODO: This check should be done downstairs. */
6742 if (!context->curl) return IE_CONNECTION_CLOSED;
6745 /* Build AuthenticateMessage request */
6746 request = xmlNewNode(NULL, BAD_CAST "GetDataBoxList");
6747 if (!request) {
6748 isds_log_message(context,
6749 _("Could not build GetDataBoxList request"));
6750 return IE_ERROR;
6752 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6753 if(!isds_ns) {
6754 isds_log_message(context, _("Could not create ISDS name space"));
6755 xmlFreeNode(request);
6756 return IE_ERROR;
6758 xmlSetNs(request, isds_ns);
6759 INSERT_STRING(request, "dblType", list_identifier);
6761 /* Send request to server and process response */
6762 err = send_destroy_request_check_response(context,
6763 SERVICE_DB_SEARCH, BAD_CAST "GetDataBoxList", &request,
6764 &response, NULL);
6765 if (err) goto leave;
6768 /* Extract Base-64 encoded ZIP file */
6769 xpath_ctx = xmlXPathNewContext(response);
6770 if (!xpath_ctx) {
6771 err = IE_ERROR;
6772 goto leave;
6774 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6775 err = IE_ERROR;
6776 goto leave;
6778 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string);
6780 /* Decode non-empty archive */
6781 if (string && string[0] != '\0') {
6782 *buffer_length = _isds_b64decode(string, buffer);
6783 if (*buffer_length == (size_t) -1) {
6784 isds_printf_message(context,
6785 _("Error while Base64-decoding box list archive"));
6786 err = IE_ERROR;
6787 goto leave;
6792 leave:
6793 free(string);
6794 xmlXPathFreeObject(result);
6795 xmlXPathFreeContext(xpath_ctx);
6796 xmlFreeDoc(response);
6797 xmlFreeNode(request);
6799 if (!err) {
6800 isds_log(ILF_ISDS, ILL_DEBUG, _("GetDataBoxList request "
6801 "processed by server successfully.\n"));
6803 #else /* not HAVE_LIBCURL */
6804 err = IE_NOTSUP;
6805 #endif
6807 return err;
6811 /* Find boxes suiting given criteria.
6812 * @criteria is filter. You should fill in at least some members.
6813 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
6814 * possibly empty. Input NULL or valid old structure.
6815 * @return:
6816 * IE_SUCCESS if search succeeded, @boxes contains useful data
6817 * IE_NOEXIST if no such box exists, @boxes will be NULL
6818 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
6819 * contains still valid data
6820 * other code if something bad happens. @boxes will be NULL. */
6821 isds_error isds_FindDataBox(struct isds_ctx *context,
6822 const struct isds_DbOwnerInfo *criteria,
6823 struct isds_list **boxes) {
6824 isds_error err = IE_SUCCESS;
6825 #if HAVE_LIBCURL
6826 _Bool truncated = 0;
6827 xmlNsPtr isds_ns = NULL;
6828 xmlNodePtr request = NULL;
6829 xmlDocPtr response = NULL;
6830 xmlChar *code = NULL, *message = NULL;
6831 xmlNodePtr db_owner_info;
6832 xmlXPathContextPtr xpath_ctx = NULL;
6833 xmlXPathObjectPtr result = NULL;
6834 xmlChar *string = NULL;
6835 #endif
6838 if (!context) return IE_INVALID_CONTEXT;
6839 zfree(context->long_message);
6840 if (!boxes) return IE_INVAL;
6841 isds_list_free(boxes);
6843 if (!criteria) {
6844 return IE_INVAL;
6847 #if HAVE_LIBCURL
6848 /* Check if connection is established
6849 * TODO: This check should be done downstairs. */
6850 if (!context->curl) return IE_CONNECTION_CLOSED;
6853 /* Build FindDataBox request */
6854 request = xmlNewNode(NULL, BAD_CAST "FindDataBox");
6855 if (!request) {
6856 isds_log_message(context,
6857 _("Could build FindDataBox request"));
6858 return IE_ERROR;
6860 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6861 if(!isds_ns) {
6862 isds_log_message(context, _("Could not create ISDS name space"));
6863 xmlFreeNode(request);
6864 return IE_ERROR;
6866 xmlSetNs(request, isds_ns);
6867 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
6868 if (!db_owner_info) {
6869 isds_log_message(context, _("Could not add dbOwnerInfo child to "
6870 "FindDataBox element"));
6871 xmlFreeNode(request);
6872 return IE_ERROR;
6875 err = insert_DbOwnerInfo(context, criteria, db_owner_info);
6876 if (err) goto leave;
6879 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending FindDataBox request to ISDS\n"));
6881 /* Sent request */
6882 err = isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
6884 /* Destroy request */
6885 xmlFreeNode(request); request = NULL;
6887 if (err) {
6888 isds_log(ILF_ISDS, ILL_DEBUG,
6889 _("Processing ISDS response on FindDataBox "
6890 "request failed\n"));
6891 goto leave;
6894 /* Check for response status */
6895 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
6896 &code, &message, NULL);
6897 if (err) {
6898 isds_log(ILF_ISDS, ILL_DEBUG,
6899 _("ISDS response on FindDataBox request is missing status\n"));
6900 goto leave;
6903 /* Request processed, but nothing found */
6904 if (!xmlStrcmp(code, BAD_CAST "0002") ||
6905 !xmlStrcmp(code, BAD_CAST "5001")) {
6906 char *code_locale = _isds_utf82locale((char*)code);
6907 char *message_locale = _isds_utf82locale((char*)message);
6908 isds_log(ILF_ISDS, ILL_DEBUG,
6909 _("Server did not found any box on FindDataBox request "
6910 "(code=%s, message=%s)\n"), code_locale, message_locale);
6911 isds_log_message(context, message_locale);
6912 free(code_locale);
6913 free(message_locale);
6914 err = IE_NOEXIST;
6915 goto leave;
6918 /* Warning, not a error */
6919 if (!xmlStrcmp(code, BAD_CAST "0003")) {
6920 char *code_locale = _isds_utf82locale((char*)code);
6921 char *message_locale = _isds_utf82locale((char*)message);
6922 isds_log(ILF_ISDS, ILL_DEBUG,
6923 _("Server truncated response on FindDataBox request "
6924 "(code=%s, message=%s)\n"), code_locale, message_locale);
6925 isds_log_message(context, message_locale);
6926 free(code_locale);
6927 free(message_locale);
6928 truncated = 1;
6931 /* Other error */
6932 else if (xmlStrcmp(code, BAD_CAST "0000")) {
6933 char *code_locale = _isds_utf82locale((char*)code);
6934 char *message_locale = _isds_utf82locale((char*)message);
6935 isds_log(ILF_ISDS, ILL_DEBUG,
6936 _("Server refused FindDataBox request "
6937 "(code=%s, message=%s)\n"), code_locale, message_locale);
6938 isds_log_message(context, message_locale);
6939 free(code_locale);
6940 free(message_locale);
6941 err = IE_ISDS;
6942 goto leave;
6945 xpath_ctx = xmlXPathNewContext(response);
6946 if (!xpath_ctx) {
6947 err = IE_ERROR;
6948 goto leave;
6950 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6951 err = IE_ERROR;
6952 goto leave;
6955 /* Extract boxes if they present */
6956 result = xmlXPathEvalExpression(BAD_CAST
6957 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
6958 xpath_ctx);
6959 if (!result) {
6960 err = IE_ERROR;
6961 goto leave;
6963 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6964 struct isds_list *item, *prev_item = NULL;
6965 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
6966 item = calloc(1, sizeof(*item));
6967 if (!item) {
6968 err = IE_NOMEM;
6969 goto leave;
6972 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
6973 if (i == 0) *boxes = item;
6974 else prev_item->next = item;
6975 prev_item = item;
6977 xpath_ctx->node = result->nodesetval->nodeTab[i];
6978 err = extract_DbOwnerInfo(context,
6979 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
6980 if (err) goto leave;
6984 leave:
6985 if (err) {
6986 isds_list_free(boxes);
6987 } else {
6988 if (truncated) err = IE_2BIG;
6991 free(string);
6992 xmlFreeNode(request);
6993 xmlXPathFreeObject(result);
6994 xmlXPathFreeContext(xpath_ctx);
6996 free(code);
6997 free(message);
6998 xmlFreeDoc(response);
7000 if (!err)
7001 isds_log(ILF_ISDS, ILL_DEBUG,
7002 _("FindDataBox request processed by server successfully.\n"));
7003 #else /* not HAVE_LIBCURL */
7004 err = IE_NOTSUP;
7005 #endif
7007 return err;
7011 /* Get status of a box.
7012 * @context is ISDS session context.
7013 * @box_id is UTF-8 encoded box identifier as zero terminated string
7014 * @box_status is return value of box status.
7015 * @return:
7016 * IE_SUCCESS if box has been found and its status retrieved
7017 * IE_NOEXIST if box is not known to ISDS server
7018 * or other appropriate error.
7019 * You can use isds_DbState to enumerate box status. However out of enum
7020 * range value can be returned too. This is feature because ISDS
7021 * specification leaves the set of values open.
7022 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
7023 * the box has been deleted, but ISDS still lists its former existence. */
7024 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
7025 long int *box_status) {
7026 isds_error err = IE_SUCCESS;
7027 #if HAVE_LIBCURL
7028 xmlNsPtr isds_ns = NULL;
7029 xmlNodePtr request = NULL, db_id;
7030 xmlDocPtr response = NULL;
7031 xmlChar *code = NULL, *message = NULL;
7032 xmlXPathContextPtr xpath_ctx = NULL;
7033 xmlXPathObjectPtr result = NULL;
7034 xmlChar *string = NULL;
7035 #endif
7037 if (!context) return IE_INVALID_CONTEXT;
7038 zfree(context->long_message);
7039 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
7041 #if HAVE_LIBCURL
7042 /* Check if connection is established
7043 * TODO: This check should be done downstairs. */
7044 if (!context->curl) return IE_CONNECTION_CLOSED;
7047 /* Build CheckDataBox request */
7048 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
7049 if (!request) {
7050 isds_log_message(context,
7051 _("Could build CheckDataBox request"));
7052 return IE_ERROR;
7054 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7055 if(!isds_ns) {
7056 isds_log_message(context, _("Could not create ISDS name space"));
7057 xmlFreeNode(request);
7058 return IE_ERROR;
7060 xmlSetNs(request, isds_ns);
7061 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
7062 if (!db_id) {
7063 isds_log_message(context, _("Could not add dbID child to "
7064 "CheckDataBox element"));
7065 xmlFreeNode(request);
7066 return IE_ERROR;
7070 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CheckDataBox request to ISDS\n"));
7072 /* Sent request */
7073 err = isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
7075 /* Destroy request */
7076 xmlFreeNode(request);
7078 if (err) {
7079 isds_log(ILF_ISDS, ILL_DEBUG,
7080 _("Processing ISDS response on CheckDataBox "
7081 "request failed\n"));
7082 goto leave;
7085 /* Check for response status */
7086 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
7087 &code, &message, NULL);
7088 if (err) {
7089 isds_log(ILF_ISDS, ILL_DEBUG,
7090 _("ISDS response on CheckDataBox request is missing status\n"));
7091 goto leave;
7094 /* Request processed, but nothing found */
7095 if (!xmlStrcmp(code, BAD_CAST "5001")) {
7096 char *box_id_locale = _isds_utf82locale((char*)box_id);
7097 char *code_locale = _isds_utf82locale((char*)code);
7098 char *message_locale = _isds_utf82locale((char*)message);
7099 isds_log(ILF_ISDS, ILL_DEBUG,
7100 _("Server did not found box %s on CheckDataBox request "
7101 "(code=%s, message=%s)\n"),
7102 box_id_locale, code_locale, message_locale);
7103 isds_log_message(context, message_locale);
7104 free(box_id_locale);
7105 free(code_locale);
7106 free(message_locale);
7107 err = IE_NOEXIST;
7108 goto leave;
7111 /* Other error */
7112 else if (xmlStrcmp(code, BAD_CAST "0000")) {
7113 char *code_locale = _isds_utf82locale((char*)code);
7114 char *message_locale = _isds_utf82locale((char*)message);
7115 isds_log(ILF_ISDS, ILL_DEBUG,
7116 _("Server refused CheckDataBox request "
7117 "(code=%s, message=%s)\n"), code_locale, message_locale);
7118 isds_log_message(context, message_locale);
7119 free(code_locale);
7120 free(message_locale);
7121 err = IE_ISDS;
7122 goto leave;
7125 /* Extract data */
7126 xpath_ctx = xmlXPathNewContext(response);
7127 if (!xpath_ctx) {
7128 err = IE_ERROR;
7129 goto leave;
7131 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7132 err = IE_ERROR;
7133 goto leave;
7135 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
7136 xpath_ctx);
7137 if (!result) {
7138 err = IE_ERROR;
7139 goto leave;
7141 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7142 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
7143 err = IE_ISDS;
7144 goto leave;
7146 if (result->nodesetval->nodeNr > 1) {
7147 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
7148 err = IE_ISDS;
7149 goto leave;
7151 xpath_ctx->node = result->nodesetval->nodeTab[0];
7152 xmlXPathFreeObject(result); result = NULL;
7154 EXTRACT_LONGINT("isds:dbState", box_status, 1);
7157 leave:
7158 free(string);
7159 xmlXPathFreeObject(result);
7160 xmlXPathFreeContext(xpath_ctx);
7162 free(code);
7163 free(message);
7164 xmlFreeDoc(response);
7166 if (!err)
7167 isds_log(ILF_ISDS, ILL_DEBUG,
7168 _("CheckDataBox request processed by server successfully.\n"));
7169 #else /* not HAVE_LIBCURL */
7170 err = IE_NOTSUP;
7171 #endif
7173 return err;
7177 /* Get list of permissions to send commercial messages.
7178 * @context is ISDS session context.
7179 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
7180 * @permissions is a reallocated list of permissions (struct
7181 * isds_commercial_permission*) to send commercial messages from @box_id. The
7182 * order of permissions is significant as the server applies the permissions
7183 * and associated pre-paid credits in the order. Empty list means no
7184 * permission.
7185 * @return:
7186 * IE_SUCCESS if the list has been obtained correctly,
7187 * or other appropriate error. */
7188 isds_error isds_get_commercial_permissions(struct isds_ctx *context,
7189 const char *box_id, struct isds_list **permissions) {
7190 isds_error err = IE_SUCCESS;
7191 #if HAVE_LIBCURL
7192 xmlDocPtr response = NULL;
7193 xmlXPathContextPtr xpath_ctx = NULL;
7194 xmlXPathObjectPtr result = NULL;
7195 #endif
7197 if (!context) return IE_INVALID_CONTEXT;
7198 zfree(context->long_message);
7199 if (NULL == permissions) return IE_INVAL;
7200 isds_list_free(permissions);
7201 if (NULL == box_id) return IE_INVAL;
7203 #if HAVE_LIBCURL
7204 /* Check if connection is established */
7205 if (!context->curl) return IE_CONNECTION_CLOSED;
7207 /* Do request and check for success */
7208 err = build_send_dbid_request_check_response(context,
7209 SERVICE_DB_SEARCH, BAD_CAST "PDZInfo", BAD_CAST "PDZSender",
7210 BAD_CAST box_id, NULL, &response, NULL);
7211 if (!err) {
7212 isds_log(ILF_ISDS, ILL_DEBUG,
7213 _("PDZInfo request processed by server successfully.\n"));
7216 /* Extract data */
7217 /* Prepare structure */
7218 xpath_ctx = xmlXPathNewContext(response);
7219 if (!xpath_ctx) {
7220 err = IE_ERROR;
7221 goto leave;
7223 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7224 err = IE_ERROR;
7225 goto leave;
7228 /* Set context node */
7229 result = xmlXPathEvalExpression(BAD_CAST
7230 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
7231 xpath_ctx);
7232 if (!result) {
7233 err = IE_ERROR;
7234 goto leave;
7236 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7237 /* Iterate over all permission records */
7238 for (long unsigned int i = 0; i < result->nodesetval->nodeNr; i++) {
7239 struct isds_list *item, *prev_item = NULL;
7241 /* Prepare structure */
7242 item = calloc(1, sizeof(*item));
7243 if (!item) {
7244 err = IE_NOMEM;
7245 goto leave;
7247 item->destructor = (void(*)(void**))isds_commercial_permission_free;
7248 if (i == 0) *permissions = item;
7249 else prev_item->next = item;
7250 prev_item = item;
7252 /* Extract it */
7253 xpath_ctx->node = result->nodesetval->nodeTab[i];
7254 err = extract_DbPDZRecord(context,
7255 (struct isds_commercial_permission **) (&item->data),
7256 xpath_ctx);
7257 if (err) goto leave;
7261 leave:
7262 if (err) {
7263 isds_list_free(permissions);
7266 xmlXPathFreeObject(result);
7267 xmlXPathFreeContext(xpath_ctx);
7268 xmlFreeDoc(response);
7270 #else /* not HAVE_LIBCURL */
7271 err = IE_NOTSUP;
7272 #endif
7274 return err;
7278 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
7279 * code, destroy response and log success.
7280 * @context is ISDS session context.
7281 * @service_name is name of SERVICE_DB_MANIPULATION service
7282 * @box_id is UTF-8 encoded box identifier as zero terminated string
7283 * @approval is optional external approval of box manipulation
7284 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7285 * NULL, if you don't care. */
7286 static isds_error build_send_manipulationdbid_request_check_drop_response(
7287 struct isds_ctx *context, const xmlChar *service_name,
7288 const xmlChar *box_id, const struct isds_approval *approval,
7289 xmlChar **refnumber) {
7290 isds_error err = IE_SUCCESS;
7291 #if HAVE_LIBCURL
7292 xmlDocPtr response = NULL;
7293 #endif
7295 if (!context) return IE_INVALID_CONTEXT;
7296 zfree(context->long_message);
7297 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
7299 #if HAVE_LIBCURL
7300 /* Check if connection is established */
7301 if (!context->curl) return IE_CONNECTION_CLOSED;
7303 /* Do request and check for success */
7304 err = build_send_dbid_request_check_response(context,
7305 SERVICE_DB_MANIPULATION, service_name, NULL, box_id, approval,
7306 &response, refnumber);
7307 xmlFreeDoc(response);
7309 if (!err) {
7310 char *service_name_locale = _isds_utf82locale((char *) service_name);
7311 isds_log(ILF_ISDS, ILL_DEBUG,
7312 _("%s request processed by server successfully.\n"),
7313 service_name_locale);
7314 free(service_name_locale);
7316 #else /* not HAVE_LIBCURL */
7317 err = IE_NOTSUP;
7318 #endif
7320 return err;
7324 /* Switch box into state where box can receive commercial messages (off by
7325 * default)
7326 * @context is ISDS session context.
7327 * @box_id is UTF-8 encoded box identifier as zero terminated string
7328 * @allow is true for enable, false for disable commercial messages income
7329 * @approval is optional external approval of box manipulation
7330 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7331 * NULL, if you don't care. */
7332 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
7333 const char *box_id, const _Bool allow,
7334 const struct isds_approval *approval, char **refnumber) {
7335 return build_send_manipulationdbid_request_check_drop_response(context,
7336 (allow) ? BAD_CAST "SetOpenAddressing" :
7337 BAD_CAST "ClearOpenAddressing",
7338 BAD_CAST box_id, approval, (xmlChar **) refnumber);
7342 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
7343 * message acceptance). This is just a box permission. Sender must apply
7344 * such role by sending each message.
7345 * @context is ISDS session context.
7346 * @box_id is UTF-8 encoded box identifier as zero terminated string
7347 * @allow is true for enable, false for disable OVM role permission
7348 * @approval is optional external approval of box manipulation
7349 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7350 * NULL, if you don't care. */
7351 isds_error isds_switch_effective_ovm(struct isds_ctx *context,
7352 const char *box_id, const _Bool allow,
7353 const struct isds_approval *approval, char **refnumber) {
7354 return build_send_manipulationdbid_request_check_drop_response(context,
7355 (allow) ? BAD_CAST "SetEffectiveOVM" :
7356 BAD_CAST "ClearEffectiveOVM",
7357 BAD_CAST box_id, approval, (xmlChar **) refnumber);
7361 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
7362 * code, destroy response and log success.
7363 * @context is ISDS session context.
7364 * @service_name is name of SERVICE_DB_MANIPULATION service
7365 * @owner is structure describing box
7366 * @approval is optional external approval of box manipulation
7367 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7368 * NULL, if you don't care. */
7369 static isds_error build_send_manipulationdbowner_request_check_drop_response(
7370 struct isds_ctx *context, const xmlChar *service_name,
7371 const struct isds_DbOwnerInfo *owner,
7372 const struct isds_approval *approval, xmlChar **refnumber) {
7373 isds_error err = IE_SUCCESS;
7374 #if HAVE_LIBCURL
7375 char *service_name_locale = NULL;
7376 xmlNodePtr request = NULL, db_owner_info;
7377 xmlNsPtr isds_ns = NULL;
7378 #endif
7381 if (!context) return IE_INVALID_CONTEXT;
7382 zfree(context->long_message);
7383 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
7385 #if HAVE_LIBCURL
7386 service_name_locale = _isds_utf82locale((char*)service_name);
7387 if (!service_name_locale) {
7388 err = IE_NOMEM;
7389 goto leave;
7392 /* Build request */
7393 request = xmlNewNode(NULL, service_name);
7394 if (!request) {
7395 isds_printf_message(context,
7396 _("Could not build %s request"), service_name_locale);
7397 err = IE_ERROR;
7398 goto leave;
7400 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7401 if(!isds_ns) {
7402 isds_log_message(context, _("Could not create ISDS name space"));
7403 err = IE_ERROR;
7404 goto leave;
7406 xmlSetNs(request, isds_ns);
7409 /* Add XSD:tOwnerInfoInput child*/
7410 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
7411 err = insert_DbOwnerInfo(context, owner, db_owner_info);
7412 if (err) goto leave;
7414 /* Add XSD:gExtApproval*/
7415 err = insert_GExtApproval(context, approval, request);
7416 if (err) goto leave;
7418 /* Send it to server and process response */
7419 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
7420 service_name, &request, refnumber);
7422 leave:
7423 xmlFreeNode(request);
7424 free(service_name_locale);
7425 #else /* not HAVE_LIBCURL */
7426 err = IE_NOTSUP;
7427 #endif
7429 return err;
7433 /* Switch box accessibility state on request of box owner.
7434 * Despite the name, owner must do the request off-line. This function is
7435 * designed for such off-line meeting points (e.g. Czech POINT).
7436 * @context is ISDS session context.
7437 * @box identifies box to switch accessibility state.
7438 * @allow is true for making accessible, false to disallow access.
7439 * @approval is optional external approval of box manipulation
7440 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7441 * NULL, if you don't care. */
7442 isds_error isds_switch_box_accessibility_on_owner_request(
7443 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
7444 const _Bool allow, const struct isds_approval *approval,
7445 char **refnumber) {
7446 return build_send_manipulationdbowner_request_check_drop_response(context,
7447 (allow) ? BAD_CAST "EnableOwnDataBox" :
7448 BAD_CAST "DisableOwnDataBox",
7449 box, approval, (xmlChar **) refnumber);
7453 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
7454 * date.
7455 * @context is ISDS session context.
7456 * @box identifies box to switch accessibility state.
7457 * @since is date since accessibility has been denied. This can be past too.
7458 * Only tm_year, tm_mon and tm_mday carry sane value.
7459 * @approval is optional external approval of box manipulation
7460 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7461 * NULL, if you don't care. */
7462 isds_error isds_disable_box_accessibility_externaly(
7463 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
7464 const struct tm *since, const struct isds_approval *approval,
7465 char **refnumber) {
7466 isds_error err = IE_SUCCESS;
7467 #if HAVE_LIBCURL
7468 char *service_name_locale = NULL;
7469 xmlNodePtr request = NULL, node;
7470 xmlNsPtr isds_ns = NULL;
7471 xmlChar *string = NULL;
7472 #endif
7475 if (!context) return IE_INVALID_CONTEXT;
7476 zfree(context->long_message);
7477 if (!box || !since) return IE_INVAL;
7479 #if HAVE_LIBCURL
7480 /* Build request */
7481 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
7482 if (!request) {
7483 isds_printf_message(context,
7484 _("Could not build %s request"), "DisableDataBoxExternally");
7485 err = IE_ERROR;
7486 goto leave;
7488 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7489 if(!isds_ns) {
7490 isds_log_message(context, _("Could not create ISDS name space"));
7491 err = IE_ERROR;
7492 goto leave;
7494 xmlSetNs(request, isds_ns);
7497 /* Add @box identification */
7498 INSERT_ELEMENT(node, request, "dbOwnerInfo");
7499 err = insert_DbOwnerInfo(context, box, node);
7500 if (err) goto leave;
7502 /* Add @since date */
7503 err = tm2datestring(since, &string);
7504 if(err) {
7505 isds_log_message(context,
7506 _("Could not convert `since' argument to ISO date string"));
7507 goto leave;
7509 INSERT_STRING(request, "dbOwnerDisableDate", string);
7510 zfree(string);
7512 /* Add @approval */
7513 err = insert_GExtApproval(context, approval, request);
7514 if (err) goto leave;
7516 /* Send it to server and process response */
7517 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
7518 BAD_CAST "DisableDataBoxExternally", &request,
7519 (xmlChar **) refnumber);
7521 leave:
7522 free(string);
7523 xmlFreeNode(request);
7524 free(service_name_locale);
7525 #else /* not HAVE_LIBCURL */
7526 err = IE_NOTSUP;
7527 #endif
7529 return err;
7533 #if HAVE_LIBCURL
7534 /* Insert struct isds_message data (envelope (recipient data optional) and
7535 * documents into XML tree
7536 * @context is session context
7537 * @outgoing_message is libisds structure with message data
7538 * @create_message is XML CreateMessage or CreateMultipleMessage element
7539 * @process_recipient true for recipient data serialization, false for no
7540 * serialization */
7541 static isds_error insert_envelope_files(struct isds_ctx *context,
7542 const struct isds_message *outgoing_message, xmlNodePtr create_message,
7543 const _Bool process_recipient) {
7545 isds_error err = IE_SUCCESS;
7546 xmlNodePtr envelope, dm_files, node;
7547 xmlChar *string = NULL;
7549 if (!context) return IE_INVALID_CONTEXT;
7550 if (!outgoing_message || !create_message) return IE_INVAL;
7553 /* Build envelope */
7554 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
7555 if (!envelope) {
7556 isds_printf_message(context, _("Could not add dmEnvelope child to "
7557 "%s element"), create_message->name);
7558 return IE_ERROR;
7561 if (!outgoing_message->envelope) {
7562 isds_log_message(context, _("Outgoing message is missing envelope"));
7563 err = IE_INVAL;
7564 goto leave;
7567 /* Insert optional message type */
7568 err = insert_message_type(context, outgoing_message->envelope->dmType,
7569 envelope);
7570 if (err) goto leave;
7572 INSERT_STRING(envelope, "dmSenderOrgUnit",
7573 outgoing_message->envelope->dmSenderOrgUnit);
7574 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
7575 outgoing_message->envelope->dmSenderOrgUnitNum, string);
7577 if (process_recipient) {
7578 if (!outgoing_message->envelope->dbIDRecipient) {
7579 isds_log_message(context,
7580 _("Outgoing message is missing recipient box identifier"));
7581 err = IE_INVAL;
7582 goto leave;
7584 INSERT_STRING(envelope, "dbIDRecipient",
7585 outgoing_message->envelope->dbIDRecipient);
7587 INSERT_STRING(envelope, "dmRecipientOrgUnit",
7588 outgoing_message->envelope->dmRecipientOrgUnit);
7589 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
7590 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
7591 INSERT_STRING(envelope, "dmToHands",
7592 outgoing_message->envelope->dmToHands);
7595 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
7596 "dmAnnotation");
7597 INSERT_STRING(envelope, "dmAnnotation",
7598 outgoing_message->envelope->dmAnnotation);
7600 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
7601 0, 50, "dmRecipientRefNumber");
7602 INSERT_STRING(envelope, "dmRecipientRefNumber",
7603 outgoing_message->envelope->dmRecipientRefNumber);
7605 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
7606 0, 50, "dmSenderRefNumber");
7607 INSERT_STRING(envelope, "dmSenderRefNumber",
7608 outgoing_message->envelope->dmSenderRefNumber);
7610 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
7611 0, 50, "dmRecipientIdent");
7612 INSERT_STRING(envelope, "dmRecipientIdent",
7613 outgoing_message->envelope->dmRecipientIdent);
7615 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
7616 0, 50, "dmSenderIdent");
7617 INSERT_STRING(envelope, "dmSenderIdent",
7618 outgoing_message->envelope->dmSenderIdent);
7620 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
7621 outgoing_message->envelope->dmLegalTitleLaw, string);
7622 INSERT_LONGINT(envelope, "dmLegalTitleYear",
7623 outgoing_message->envelope->dmLegalTitleYear, string);
7624 INSERT_STRING(envelope, "dmLegalTitleSect",
7625 outgoing_message->envelope->dmLegalTitleSect);
7626 INSERT_STRING(envelope, "dmLegalTitlePar",
7627 outgoing_message->envelope->dmLegalTitlePar);
7628 INSERT_STRING(envelope, "dmLegalTitlePoint",
7629 outgoing_message->envelope->dmLegalTitlePoint);
7631 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
7632 outgoing_message->envelope->dmPersonalDelivery);
7633 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
7634 outgoing_message->envelope->dmAllowSubstDelivery);
7636 /* ???: Should we require value for dbEffectiveOVM sender?
7637 * ISDS has default as true */
7638 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
7639 INSERT_BOOLEAN(envelope, "dmOVM",
7640 outgoing_message->envelope->dmPublishOwnID);
7643 /* Append dmFiles */
7644 if (!outgoing_message->documents) {
7645 isds_log_message(context,
7646 _("Outgoing message is missing list of documents"));
7647 err = IE_INVAL;
7648 goto leave;
7650 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
7651 if (!dm_files) {
7652 isds_printf_message(context, _("Could not add dmFiles child to "
7653 "%s element"), create_message->name);
7654 err = IE_ERROR;
7655 goto leave;
7658 /* Check for document hierarchy */
7659 err = _isds_check_documents_hierarchy(context, outgoing_message->documents);
7660 if (err) goto leave;
7662 /* Process each document */
7663 for (struct isds_list *item =
7664 (struct isds_list *) outgoing_message->documents;
7665 item; item = item->next) {
7666 if (!item->data) {
7667 isds_log_message(context,
7668 _("List of documents contains empty item"));
7669 err = IE_INVAL;
7670 goto leave;
7672 /* FIXME: Check for dmFileMetaType and for document references.
7673 * Only first document can be of MAIN type */
7674 err = insert_document(context, (struct isds_document*) item->data,
7675 dm_files);
7677 if (err) goto leave;
7680 leave:
7681 free(string);
7682 return err;
7684 #endif /* HAVE_LIBCURL */
7687 /* Send a message via ISDS to a recipient
7688 * @context is session context
7689 * @outgoing_message is message to send; Some members are mandatory (like
7690 * dbIDRecipient), some are optional and some are irrelevant (especially data
7691 * about sender). Included pointer to isds_list documents must contain at
7692 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
7693 * members will be filled with valid data from ISDS. Exact list of write
7694 * members is subject to change. Currently dmID is changed.
7695 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
7696 isds_error isds_send_message(struct isds_ctx *context,
7697 struct isds_message *outgoing_message) {
7699 isds_error err = IE_SUCCESS;
7700 #if HAVE_LIBCURL
7701 xmlNsPtr isds_ns = NULL;
7702 xmlNodePtr request = NULL;
7703 xmlDocPtr response = NULL;
7704 xmlChar *code = NULL, *message = NULL;
7705 xmlXPathContextPtr xpath_ctx = NULL;
7706 xmlXPathObjectPtr result = NULL;
7707 /*_Bool message_is_complete = 0;*/
7708 #endif
7710 if (!context) return IE_INVALID_CONTEXT;
7711 zfree(context->long_message);
7712 if (!outgoing_message) return IE_INVAL;
7714 #if HAVE_LIBCURL
7715 /* Check if connection is established
7716 * TODO: This check should be done downstairs. */
7717 if (!context->curl) return IE_CONNECTION_CLOSED;
7720 /* Build CreateMessage request */
7721 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
7722 if (!request) {
7723 isds_log_message(context,
7724 _("Could not build CreateMessage request"));
7725 return IE_ERROR;
7727 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7728 if(!isds_ns) {
7729 isds_log_message(context, _("Could not create ISDS name space"));
7730 xmlFreeNode(request);
7731 return IE_ERROR;
7733 xmlSetNs(request, isds_ns);
7735 /* Append envelope and files */
7736 err = insert_envelope_files(context, outgoing_message, request, 1);
7737 if (err) goto leave;
7740 /* Signal we can serialize message since now */
7741 /*message_is_complete = 1;*/
7744 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
7746 /* Sent request */
7747 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
7749 /* Don't' destroy request, we want to provide it to application later */
7751 if (err) {
7752 isds_log(ILF_ISDS, ILL_DEBUG,
7753 _("Processing ISDS response on CreateMessage "
7754 "request failed\n"));
7755 goto leave;
7758 /* Check for response status */
7759 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
7760 &code, &message, NULL);
7761 if (err) {
7762 isds_log(ILF_ISDS, ILL_DEBUG,
7763 _("ISDS response on CreateMessage request "
7764 "is missing status\n"));
7765 goto leave;
7768 /* Request processed, but refused by server or server failed */
7769 if (xmlStrcmp(code, BAD_CAST "0000")) {
7770 char *box_id_locale =
7771 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
7772 char *code_locale = _isds_utf82locale((char*)code);
7773 char *message_locale = _isds_utf82locale((char*)message);
7774 isds_log(ILF_ISDS, ILL_DEBUG,
7775 _("Server did not accept message for %s on CreateMessage "
7776 "request (code=%s, message=%s)\n"),
7777 box_id_locale, code_locale, message_locale);
7778 isds_log_message(context, message_locale);
7779 free(box_id_locale);
7780 free(code_locale);
7781 free(message_locale);
7782 err = IE_ISDS;
7783 goto leave;
7787 /* Extract data */
7788 xpath_ctx = xmlXPathNewContext(response);
7789 if (!xpath_ctx) {
7790 err = IE_ERROR;
7791 goto leave;
7793 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7794 err = IE_ERROR;
7795 goto leave;
7797 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
7798 xpath_ctx);
7799 if (!result) {
7800 err = IE_ERROR;
7801 goto leave;
7803 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7804 isds_log_message(context, _("Missing CreateMessageResponse element"));
7805 err = IE_ISDS;
7806 goto leave;
7808 if (result->nodesetval->nodeNr > 1) {
7809 isds_log_message(context, _("Multiple CreateMessageResponse element"));
7810 err = IE_ISDS;
7811 goto leave;
7813 xpath_ctx->node = result->nodesetval->nodeTab[0];
7814 xmlXPathFreeObject(result); result = NULL;
7816 if (outgoing_message->envelope->dmID) {
7817 free(outgoing_message->envelope->dmID);
7818 outgoing_message->envelope->dmID = NULL;
7820 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
7821 if (!outgoing_message->envelope->dmID) {
7822 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
7823 "but did not return assigned message ID\n"));
7826 leave:
7827 /* TODO: Serialize message into structure member raw */
7828 /* XXX: Each web service transport message in different format.
7829 * Therefore it's not possible to save them directly.
7830 * To save them, one must figure out common format.
7831 * We can leave it on application, or we can implement the ESS format. */
7832 /*if (message_is_complete) {
7833 if (outgoing_message->envelope->dmID) {
7835 /* Add assigned message ID as first child*/
7836 /*xmlNodePtr dmid_text = xmlNewText(
7837 (xmlChar *) outgoing_message->envelope->dmID);
7838 if (!dmid_text) goto serialization_failed;
7840 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
7841 BAD_CAST "dmID");
7842 if (!dmid_element) {
7843 xmlFreeNode(dmid_text);
7844 goto serialization_failed;
7847 xmlNodePtr dmid_element_with_text =
7848 xmlAddChild(dmid_element, dmid_text);
7849 if (!dmid_element_with_text) {
7850 xmlFreeNode(dmid_element);
7851 xmlFreeNode(dmid_text);
7852 goto serialization_failed;
7855 node = xmlAddPrevSibling(envelope->childern,
7856 dmid_element_with_text);
7857 if (!node) {
7858 xmlFreeNodeList(dmid_element_with_text);
7859 goto serialization_failed;
7863 /* Serialize message with ID into raw */
7864 /*buffer = serialize_element(envelope)*/
7865 /* }
7867 serialization_failed:
7871 /* Clean up */
7872 xmlXPathFreeObject(result);
7873 xmlXPathFreeContext(xpath_ctx);
7875 free(code);
7876 free(message);
7877 xmlFreeDoc(response);
7878 xmlFreeNode(request);
7880 if (!err)
7881 isds_log(ILF_ISDS, ILL_DEBUG,
7882 _("CreateMessage request processed by server "
7883 "successfully.\n"));
7884 #else /* not HAVE_LIBCURL */
7885 err = IE_NOTSUP;
7886 #endif
7888 return err;
7892 /* Send a message via ISDS to a multiple recipients
7893 * @context is session context
7894 * @outgoing_message is message to send; Some members are mandatory,
7895 * some are optional and some are irrelevant (especially data
7896 * about sender). Data about recipient will be substituted by ISDS from
7897 * @copies. Included pointer to isds_list documents must
7898 * contain at least one document of FILEMETATYPE_MAIN.
7899 * @copies is list of isds_message_copy structures addressing all desired
7900 * recipients. This is read-write structure, some members will be filled with
7901 * valid data from ISDS (message IDs, error codes, error descriptions).
7902 * @return
7903 * ISDS_SUCCESS if all messages have been sent
7904 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
7905 * succeeded messages can be identified by copies->data->error),
7906 * or other error code if something other goes wrong. */
7907 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
7908 const struct isds_message *outgoing_message,
7909 struct isds_list *copies) {
7911 isds_error err = IE_SUCCESS;
7912 #if HAVE_LIBCURL
7913 isds_error append_err;
7914 xmlNsPtr isds_ns = NULL;
7915 xmlNodePtr request = NULL, recipients, recipient, node;
7916 struct isds_list *item;
7917 struct isds_message_copy *copy;
7918 xmlDocPtr response = NULL;
7919 xmlChar *code = NULL, *message = NULL;
7920 xmlXPathContextPtr xpath_ctx = NULL;
7921 xmlXPathObjectPtr result = NULL;
7922 xmlChar *string = NULL;
7923 int i;
7924 #endif
7926 if (!context) return IE_INVALID_CONTEXT;
7927 zfree(context->long_message);
7928 if (!outgoing_message || !copies) return IE_INVAL;
7930 #if HAVE_LIBCURL
7931 /* Check if connection is established
7932 * TODO: This check should be done downstairs. */
7933 if (!context->curl) return IE_CONNECTION_CLOSED;
7936 /* Build CreateMultipleMessage request */
7937 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
7938 if (!request) {
7939 isds_log_message(context,
7940 _("Could not build CreateMultipleMessage request"));
7941 return IE_ERROR;
7943 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7944 if(!isds_ns) {
7945 isds_log_message(context, _("Could not create ISDS name space"));
7946 xmlFreeNode(request);
7947 return IE_ERROR;
7949 xmlSetNs(request, isds_ns);
7952 /* Build recipients */
7953 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
7954 if (!recipients) {
7955 isds_log_message(context, _("Could not add dmRecipients child to "
7956 "CreateMultipleMessage element"));
7957 xmlFreeNode(request);
7958 return IE_ERROR;
7961 /* Insert each recipient */
7962 for (item = copies; item; item = item->next) {
7963 copy = (struct isds_message_copy *) item->data;
7964 if (!copy) {
7965 isds_log_message(context,
7966 _("`copies' list item contains empty data"));
7967 err = IE_INVAL;
7968 goto leave;
7971 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
7972 if (!recipient) {
7973 isds_log_message(context, _("Could not add dmRecipient child to "
7974 "dmRecipients element"));
7975 err = IE_ERROR;
7976 goto leave;
7979 if (!copy->dbIDRecipient) {
7980 isds_log_message(context,
7981 _("Message copy is missing recipient box identifier"));
7982 err = IE_INVAL;
7983 goto leave;
7985 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
7986 INSERT_STRING(recipient, "dmRecipientOrgUnit",
7987 copy->dmRecipientOrgUnit);
7988 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
7989 copy->dmRecipientOrgUnitNum, string);
7990 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
7993 /* Append envelope and files */
7994 err = insert_envelope_files(context, outgoing_message, request, 0);
7995 if (err) goto leave;
7998 isds_log(ILF_ISDS, ILL_DEBUG,
7999 _("Sending CreateMultipleMessage request to ISDS\n"));
8001 /* Sent request */
8002 err = isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
8003 if (err) {
8004 isds_log(ILF_ISDS, ILL_DEBUG,
8005 _("Processing ISDS response on CreateMultipleMessage "
8006 "request failed\n"));
8007 goto leave;
8010 /* Check for response status */
8011 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
8012 &code, &message, NULL);
8013 if (err) {
8014 isds_log(ILF_ISDS, ILL_DEBUG,
8015 _("ISDS response on CreateMultipleMessage request "
8016 "is missing status\n"));
8017 goto leave;
8020 /* Request processed, but some copies failed */
8021 if (!xmlStrcmp(code, BAD_CAST "0004")) {
8022 char *box_id_locale =
8023 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
8024 char *code_locale = _isds_utf82locale((char*)code);
8025 char *message_locale = _isds_utf82locale((char*)message);
8026 isds_log(ILF_ISDS, ILL_DEBUG,
8027 _("Server did accept message for multiple recipients "
8028 "on CreateMultipleMessage request but delivery to "
8029 "some of them failed (code=%s, message=%s)\n"),
8030 box_id_locale, code_locale, message_locale);
8031 isds_log_message(context, message_locale);
8032 free(box_id_locale);
8033 free(code_locale);
8034 free(message_locale);
8035 err = IE_PARTIAL_SUCCESS;
8038 /* Request refused by server as whole */
8039 else if (xmlStrcmp(code, BAD_CAST "0000")) {
8040 char *box_id_locale =
8041 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
8042 char *code_locale = _isds_utf82locale((char*)code);
8043 char *message_locale = _isds_utf82locale((char*)message);
8044 isds_log(ILF_ISDS, ILL_DEBUG,
8045 _("Server did not accept message for multiple recipients "
8046 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
8047 box_id_locale, code_locale, message_locale);
8048 isds_log_message(context, message_locale);
8049 free(box_id_locale);
8050 free(code_locale);
8051 free(message_locale);
8052 err = IE_ISDS;
8053 goto leave;
8057 /* Extract data */
8058 xpath_ctx = xmlXPathNewContext(response);
8059 if (!xpath_ctx) {
8060 err = IE_ERROR;
8061 goto leave;
8063 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8064 err = IE_ERROR;
8065 goto leave;
8067 result = xmlXPathEvalExpression(
8068 BAD_CAST "/isds:CreateMultipleMessageResponse"
8069 "/isds:dmMultipleStatus/isds:dmSingleStatus",
8070 xpath_ctx);
8071 if (!result) {
8072 err = IE_ERROR;
8073 goto leave;
8075 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8076 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
8077 err = IE_ISDS;
8078 goto leave;
8081 /* Extract message ID and delivery status for each copy */
8082 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
8083 item = item->next, i++) {
8084 copy = (struct isds_message_copy *) item->data;
8085 xpath_ctx->node = result->nodesetval->nodeTab[i];
8087 append_err = append_TMStatus(context, copy, xpath_ctx);
8088 if (append_err) {
8089 err = append_err;
8090 goto leave;
8093 if (item || i < result->nodesetval->nodeNr) {
8094 isds_printf_message(context, _("ISDS returned unexpected number of "
8095 "message copy delivery states: %d"),
8096 result->nodesetval->nodeNr);
8097 err = IE_ISDS;
8098 goto leave;
8102 leave:
8103 /* Clean up */
8104 free(string);
8105 xmlXPathFreeObject(result);
8106 xmlXPathFreeContext(xpath_ctx);
8108 free(code);
8109 free(message);
8110 xmlFreeDoc(response);
8111 xmlFreeNode(request);
8113 if (!err)
8114 isds_log(ILF_ISDS, ILL_DEBUG,
8115 _("CreateMultipleMessageResponse request processed by server "
8116 "successfully.\n"));
8117 #else /* not HAVE_LIBCURL */
8118 err = IE_NOTSUP;
8119 #endif
8121 return err;
8125 /* Get list of messages. This is common core for getting sent or received
8126 * messages.
8127 * Any criterion argument can be NULL, if you don't care about it.
8128 * @context is session context. Must not be NULL.
8129 * @outgoing_direction is true if you want list of outgoing messages,
8130 * it's false if you want incoming messages.
8131 * @from_time is minimal time and date of message sending inclusive.
8132 * @to_time is maximal time and date of message sending inclusive
8133 * @organization_unit_number is number of sender/recipient respectively.
8134 * @status_filter is bit field of isds_message_status values. Use special
8135 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8136 * all values, you can use bit-wise arithmetic if you want.)
8137 * @offset is index of first message we are interested in. First message is 1.
8138 * Set to 0 (or 1) if you don't care.
8139 * @number is maximal length of list you want to get as input value, outputs
8140 * number of messages matching these criteria. Can be NULL if you don't care
8141 * (applies to output value either).
8142 * @messages is automatically reallocated list of isds_message's. Be ware that
8143 * it returns only brief overview (envelope and some other fields) about each
8144 * message, not the complete message. FIXME: Specify exact fields.
8145 * The list is sorted by delivery time in ascending order.
8146 * Use NULL if you don't care about don't need the data (useful if you want to
8147 * know only the @number). If you provide &NULL, list will be allocated on
8148 * heap, if you provide pointer to non-NULL, list will be freed automatically
8149 * at first. Also in case of error the list will be NULLed.
8150 * @return IE_SUCCESS or appropriate error code. */
8151 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
8152 _Bool outgoing_direction,
8153 const struct timeval *from_time, const struct timeval *to_time,
8154 const long int *organization_unit_number,
8155 const unsigned int status_filter,
8156 const unsigned long int offset, unsigned long int *number,
8157 struct isds_list **messages) {
8159 isds_error err = IE_SUCCESS;
8160 #if HAVE_LIBCURL
8161 xmlNsPtr isds_ns = NULL;
8162 xmlNodePtr request = NULL, node;
8163 xmlDocPtr response = NULL;
8164 xmlChar *code = NULL, *message = NULL;
8165 xmlXPathContextPtr xpath_ctx = NULL;
8166 xmlXPathObjectPtr result = NULL;
8167 xmlChar *string = NULL;
8168 long unsigned int count = 0;
8169 #endif
8171 if (!context) return IE_INVALID_CONTEXT;
8172 zfree(context->long_message);
8174 /* Free former message list if any */
8175 if (messages) isds_list_free(messages);
8177 #if HAVE_LIBCURL
8178 /* Check if connection is established
8179 * TODO: This check should be done downstairs. */
8180 if (!context->curl) return IE_CONNECTION_CLOSED;
8182 /* Build GetListOf*Messages request */
8183 request = xmlNewNode(NULL,
8184 (outgoing_direction) ?
8185 BAD_CAST "GetListOfSentMessages" :
8186 BAD_CAST "GetListOfReceivedMessages"
8188 if (!request) {
8189 isds_log_message(context,
8190 (outgoing_direction) ?
8191 _("Could not build GetListOfSentMessages request") :
8192 _("Could not build GetListOfReceivedMessages request")
8194 return IE_ERROR;
8196 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8197 if(!isds_ns) {
8198 isds_log_message(context, _("Could not create ISDS name space"));
8199 xmlFreeNode(request);
8200 return IE_ERROR;
8202 xmlSetNs(request, isds_ns);
8205 if (from_time) {
8206 err = timeval2timestring(from_time, &string);
8207 if (err) goto leave;
8209 INSERT_STRING(request, "dmFromTime", string);
8210 free(string); string = NULL;
8212 if (to_time) {
8213 err = timeval2timestring(to_time, &string);
8214 if (err) goto leave;
8216 INSERT_STRING(request, "dmToTime", string);
8217 free(string); string = NULL;
8219 if (outgoing_direction) {
8220 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
8221 organization_unit_number, string);
8222 } else {
8223 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
8224 organization_unit_number, string);
8227 if (status_filter > MESSAGESTATE_ANY) {
8228 isds_printf_message(context,
8229 _("Invalid message state filter value: %ld"), status_filter);
8230 err = IE_INVAL;
8231 goto leave;
8233 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
8235 if (offset > 0 ) {
8236 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
8237 } else {
8238 INSERT_STRING(request, "dmOffset", "1");
8241 /* number 0 means no limit */
8242 if (number && *number == 0) {
8243 INSERT_STRING(request, "dmLimit", NULL);
8244 } else {
8245 INSERT_ULONGINT(request, "dmLimit", number, string);
8249 isds_log(ILF_ISDS, ILL_DEBUG,
8250 (outgoing_direction) ?
8251 _("Sending GetListOfSentMessages request to ISDS\n") :
8252 _("Sending GetListOfReceivedMessages request to ISDS\n")
8255 /* Sent request */
8256 err = isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
8257 xmlFreeNode(request); request = NULL;
8259 if (err) {
8260 isds_log(ILF_ISDS, ILL_DEBUG,
8261 (outgoing_direction) ?
8262 _("Processing ISDS response on GetListOfSentMessages "
8263 "request failed\n") :
8264 _("Processing ISDS response on GetListOfReceivedMessages "
8265 "request failed\n")
8267 goto leave;
8270 /* Check for response status */
8271 err = isds_response_status(context, SERVICE_DM_INFO, response,
8272 &code, &message, NULL);
8273 if (err) {
8274 isds_log(ILF_ISDS, ILL_DEBUG,
8275 (outgoing_direction) ?
8276 _("ISDS response on GetListOfSentMessages request "
8277 "is missing status\n") :
8278 _("ISDS response on GetListOfReceivedMessages request "
8279 "is missing status\n")
8281 goto leave;
8284 /* Request processed, but nothing found */
8285 if (xmlStrcmp(code, BAD_CAST "0000")) {
8286 char *code_locale = _isds_utf82locale((char*)code);
8287 char *message_locale = _isds_utf82locale((char*)message);
8288 isds_log(ILF_ISDS, ILL_DEBUG,
8289 (outgoing_direction) ?
8290 _("Server refused GetListOfSentMessages request "
8291 "(code=%s, message=%s)\n") :
8292 _("Server refused GetListOfReceivedMessages request "
8293 "(code=%s, message=%s)\n"),
8294 code_locale, message_locale);
8295 isds_log_message(context, message_locale);
8296 free(code_locale);
8297 free(message_locale);
8298 err = IE_ISDS;
8299 goto leave;
8303 /* Extract data */
8304 xpath_ctx = xmlXPathNewContext(response);
8305 if (!xpath_ctx) {
8306 err = IE_ERROR;
8307 goto leave;
8309 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8310 err = IE_ERROR;
8311 goto leave;
8313 result = xmlXPathEvalExpression(
8314 (outgoing_direction) ?
8315 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
8316 "isds:dmRecords/isds:dmRecord" :
8317 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
8318 "isds:dmRecords/isds:dmRecord",
8319 xpath_ctx);
8320 if (!result) {
8321 err = IE_ERROR;
8322 goto leave;
8325 /* Fill output arguments in */
8326 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8327 struct isds_envelope *envelope;
8328 struct isds_list *item = NULL, *last_item = NULL;
8330 for (count = 0; count < result->nodesetval->nodeNr; count++) {
8331 /* Create new message */
8332 item = calloc(1, sizeof(*item));
8333 if (!item) {
8334 err = IE_NOMEM;
8335 goto leave;
8337 item->destructor = (void(*)(void**)) &isds_message_free;
8338 item->data = calloc(1, sizeof(struct isds_message));
8339 if (!item->data) {
8340 isds_list_free(&item);
8341 err = IE_NOMEM;
8342 goto leave;
8345 /* Extract envelope data */
8346 xpath_ctx->node = result->nodesetval->nodeTab[count];
8347 envelope = NULL;
8348 err = extract_DmRecord(context, &envelope, xpath_ctx);
8349 if (err) {
8350 isds_list_free(&item);
8351 goto leave;
8354 /* Attach extracted envelope */
8355 ((struct isds_message *) item->data)->envelope = envelope;
8357 /* Append new message into the list */
8358 if (!*messages) {
8359 *messages = last_item = item;
8360 } else {
8361 last_item->next = item;
8362 last_item = item;
8366 if (number) *number = count;
8368 leave:
8369 if (err) {
8370 isds_list_free(messages);
8373 free(string);
8374 xmlXPathFreeObject(result);
8375 xmlXPathFreeContext(xpath_ctx);
8377 free(code);
8378 free(message);
8379 xmlFreeDoc(response);
8380 xmlFreeNode(request);
8382 if (!err)
8383 isds_log(ILF_ISDS, ILL_DEBUG,
8384 (outgoing_direction) ?
8385 _("GetListOfSentMessages request processed by server "
8386 "successfully.\n") :
8387 _("GetListOfReceivedMessages request processed by server "
8388 "successfully.\n")
8390 #else /* not HAVE_LIBCURL */
8391 err = IE_NOTSUP;
8392 #endif
8393 return err;
8397 /* Get list of outgoing (already sent) messages.
8398 * Any criterion argument can be NULL, if you don't care about it.
8399 * @context is session context. Must not be NULL.
8400 * @from_time is minimal time and date of message sending inclusive.
8401 * @to_time is maximal time and date of message sending inclusive
8402 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
8403 * @status_filter is bit field of isds_message_status values. Use special
8404 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8405 * all values, you can use bit-wise arithmetic if you want.)
8406 * @offset is index of first message we are interested in. First message is 1.
8407 * Set to 0 (or 1) if you don't care.
8408 * @number is maximal length of list you want to get as input value, outputs
8409 * number of messages matching these criteria. Can be NULL if you don't care
8410 * (applies to output value either).
8411 * @messages is automatically reallocated list of isds_message's. Be ware that
8412 * it returns only brief overview (envelope and some other fields) about each
8413 * message, not the complete message. FIXME: Specify exact fields.
8414 * The list is sorted by delivery time in ascending order.
8415 * Use NULL if you don't care about the meta data (useful if you want to know
8416 * only the @number). If you provide &NULL, list will be allocated on heap,
8417 * if you provide pointer to non-NULL, list will be freed automatically at
8418 * first. Also in case of error the list will be NULLed.
8419 * @return IE_SUCCESS or appropriate error code. */
8420 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
8421 const struct timeval *from_time, const struct timeval *to_time,
8422 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
8423 const unsigned long int offset, unsigned long int *number,
8424 struct isds_list **messages) {
8426 return isds_get_list_of_messages(
8427 context, 1,
8428 from_time, to_time, dmSenderOrgUnitNum, status_filter,
8429 offset, number,
8430 messages);
8434 /* Get list of incoming (addressed to you) messages.
8435 * Any criterion argument can be NULL, if you don't care about it.
8436 * @context is session context. Must not be NULL.
8437 * @from_time is minimal time and date of message sending inclusive.
8438 * @to_time is maximal time and date of message sending inclusive
8439 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
8440 * @status_filter is bit field of isds_message_status values. Use special
8441 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8442 * all values, you can use bit-wise arithmetic if you want.)
8443 * @offset is index of first message we are interested in. First message is 1.
8444 * Set to 0 (or 1) if you don't care.
8445 * @number is maximal length of list you want to get as input value, outputs
8446 * number of messages matching these criteria. Can be NULL if you don't care
8447 * (applies to output value either).
8448 * @messages is automatically reallocated list of isds_message's. Be ware that
8449 * it returns only brief overview (envelope and some other fields) about each
8450 * message, not the complete message. FIXME: Specify exact fields.
8451 * Use NULL if you don't care about the meta data (useful if you want to know
8452 * only the @number). If you provide &NULL, list will be allocated on heap,
8453 * if you provide pointer to non-NULL, list will be freed automatically at
8454 * first. Also in case of error the list will be NULLed.
8455 * @return IE_SUCCESS or appropriate error code. */
8456 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
8457 const struct timeval *from_time, const struct timeval *to_time,
8458 const long int *dmRecipientOrgUnitNum,
8459 const unsigned int status_filter,
8460 const unsigned long int offset, unsigned long int *number,
8461 struct isds_list **messages) {
8463 return isds_get_list_of_messages(
8464 context, 0,
8465 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
8466 offset, number,
8467 messages);
8471 /* Get list of sent message state changes.
8472 * Any criterion argument can be NULL, if you don't care about it.
8473 * @context is session context. Must not be NULL.
8474 * @from_time is minimal time and date of status changes inclusive
8475 * @to_time is maximal time and date of status changes inclusive
8476 * @changed_states is automatically reallocated list of
8477 * isds_message_status_change's. If you provide &NULL, list will be allocated
8478 * on heap, if you provide pointer to non-NULL, list will be freed
8479 * automatically at first. Also in case of error the list will be NULLed.
8480 * XXX: The list item ordering is not specified.
8481 * XXX: Server provides only `recent' changes.
8482 * @return IE_SUCCESS or appropriate error code. */
8483 isds_error isds_get_list_of_sent_message_state_changes(
8484 struct isds_ctx *context,
8485 const struct timeval *from_time, const struct timeval *to_time,
8486 struct isds_list **changed_states) {
8488 isds_error err = IE_SUCCESS;
8489 #if HAVE_LIBCURL
8490 xmlNsPtr isds_ns = NULL;
8491 xmlNodePtr request = NULL, node;
8492 xmlDocPtr response = NULL;
8493 xmlXPathContextPtr xpath_ctx = NULL;
8494 xmlXPathObjectPtr result = NULL;
8495 xmlChar *string = NULL;
8496 long unsigned int count = 0;
8497 #endif
8499 if (!context) return IE_INVALID_CONTEXT;
8500 zfree(context->long_message);
8502 /* Free former message list if any */
8503 isds_list_free(changed_states);
8505 #if HAVE_LIBCURL
8506 /* Check if connection is established
8507 * TODO: This check should be done downstairs. */
8508 if (!context->curl) return IE_CONNECTION_CLOSED;
8510 /* Build GetMessageStateChanges request */
8511 request = xmlNewNode(NULL, BAD_CAST "GetMessageStateChanges");
8512 if (!request) {
8513 isds_log_message(context,
8514 _("Could not build GetMessageStateChanges request"));
8515 return IE_ERROR;
8517 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8518 if(!isds_ns) {
8519 isds_log_message(context, _("Could not create ISDS name space"));
8520 xmlFreeNode(request);
8521 return IE_ERROR;
8523 xmlSetNs(request, isds_ns);
8526 if (from_time) {
8527 err = timeval2timestring(from_time, &string);
8528 if (err) goto leave;
8530 INSERT_STRING(request, "dmFromTime", string);
8531 zfree(string);
8533 if (to_time) {
8534 err = timeval2timestring(to_time, &string);
8535 if (err) goto leave;
8537 INSERT_STRING(request, "dmToTime", string);
8538 zfree(string);
8541 /* Sent request */
8542 err = send_destroy_request_check_response(context,
8543 SERVICE_DM_INFO, BAD_CAST "GetMessageStateChanges", &request,
8544 &response, NULL);
8545 if (err) goto leave;
8548 /* Extract data */
8549 xpath_ctx = xmlXPathNewContext(response);
8550 if (!xpath_ctx) {
8551 err = IE_ERROR;
8552 goto leave;
8554 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8555 err = IE_ERROR;
8556 goto leave;
8558 result = xmlXPathEvalExpression(
8559 BAD_CAST "/isds:GetMessageStateChangesResponse/"
8560 "isds:dmRecords/isds:dmRecord", xpath_ctx);
8561 if (!result) {
8562 err = IE_ERROR;
8563 goto leave;
8566 /* Fill output arguments in */
8567 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8568 struct isds_list *item = NULL, *last_item = NULL;
8570 for (count = 0; count < result->nodesetval->nodeNr; count++) {
8571 /* Create new status change */
8572 item = calloc(1, sizeof(*item));
8573 if (!item) {
8574 err = IE_NOMEM;
8575 goto leave;
8577 item->destructor =
8578 (void(*)(void**)) &isds_message_status_change_free;
8580 /* Extract message status change */
8581 xpath_ctx->node = result->nodesetval->nodeTab[count];
8582 err = extract_StateChangesRecord(context,
8583 (struct isds_message_status_change **) &item->data,
8584 xpath_ctx);
8585 if (err) {
8586 isds_list_free(&item);
8587 goto leave;
8590 /* Append new message status change into the list */
8591 if (!*changed_states) {
8592 *changed_states = last_item = item;
8593 } else {
8594 last_item->next = item;
8595 last_item = item;
8600 leave:
8601 if (err) {
8602 isds_list_free(changed_states);
8605 free(string);
8606 xmlXPathFreeObject(result);
8607 xmlXPathFreeContext(xpath_ctx);
8608 xmlFreeDoc(response);
8609 xmlFreeNode(request);
8611 if (!err)
8612 isds_log(ILF_ISDS, ILL_DEBUG,
8613 _("GetMessageStateChanges request processed by server "
8614 "successfully.\n"));
8615 #else /* not HAVE_LIBCURL */
8616 err = IE_NOTSUP;
8617 #endif
8618 return err;
8622 #if HAVE_LIBCURL
8623 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
8624 * code
8625 * @context is session context
8626 * @service is ISDS WS service handler
8627 * @service_name is name of SERVICE_DM_OPERATIONS
8628 * @message_id is message ID to send as service argument to ISDS
8629 * @response is server SOAP body response as XML document
8630 * @raw_response is automatically reallocated bit stream with response body. Use
8631 * NULL if you don't care
8632 * @raw_response_length is size of @raw_response in bytes
8633 * @code is ISDS status code
8634 * @status_message is ISDS status message
8635 * @return error coded from lower layer, context message will be set up
8636 * appropriately. */
8637 static isds_error build_send_check_message_request(struct isds_ctx *context,
8638 const isds_service service, const xmlChar *service_name,
8639 const char *message_id,
8640 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
8641 xmlChar **code, xmlChar **status_message) {
8643 isds_error err = IE_SUCCESS;
8644 char *service_name_locale = NULL, *message_id_locale = NULL;
8645 xmlNodePtr request = NULL, node;
8646 xmlNsPtr isds_ns = NULL;
8648 if (!context) return IE_INVALID_CONTEXT;
8649 if (!service_name || !message_id) return IE_INVAL;
8650 if (!response || !code || !status_message) return IE_INVAL;
8651 if (!raw_response_length && raw_response) return IE_INVAL;
8653 /* Free output argument */
8654 xmlFreeDoc(*response); *response = NULL;
8655 if (raw_response) zfree(*raw_response);
8656 free(*code);
8657 free(*status_message);
8660 /* Check if connection is established
8661 * TODO: This check should be done downstairs. */
8662 if (!context->curl) return IE_CONNECTION_CLOSED;
8664 service_name_locale = _isds_utf82locale((char*)service_name);
8665 message_id_locale = _isds_utf82locale(message_id);
8666 if (!service_name_locale || !message_id_locale) {
8667 err = IE_NOMEM;
8668 goto leave;
8671 /* Build request */
8672 request = xmlNewNode(NULL, service_name);
8673 if (!request) {
8674 isds_printf_message(context,
8675 _("Could not build %s request for %s message ID"),
8676 service_name_locale, message_id_locale);
8677 err = IE_ERROR;
8678 goto leave;
8680 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8681 if(!isds_ns) {
8682 isds_log_message(context, _("Could not create ISDS name space"));
8683 err = IE_ERROR;
8684 goto leave;
8686 xmlSetNs(request, isds_ns);
8689 /* Add requested ID */
8690 err = validate_message_id_length(context, (xmlChar *) message_id);
8691 if (err) goto leave;
8692 INSERT_STRING(request, "dmID", message_id);
8695 isds_log(ILF_ISDS, ILL_DEBUG,
8696 _("Sending %s request for %s message ID to ISDS\n"),
8697 service_name_locale, message_id_locale);
8699 /* Send request */
8700 err = isds(context, service, request, response,
8701 raw_response, raw_response_length);
8702 xmlFreeNode(request); request = NULL;
8704 if (err) {
8705 isds_log(ILF_ISDS, ILL_DEBUG,
8706 _("Processing ISDS response on %s request failed\n"),
8707 service_name_locale);
8708 goto leave;
8711 /* Check for response status */
8712 err = isds_response_status(context, service, *response,
8713 code, status_message, NULL);
8714 if (err) {
8715 isds_log(ILF_ISDS, ILL_DEBUG,
8716 _("ISDS response on %s request is missing status\n"),
8717 service_name_locale);
8718 goto leave;
8721 /* Request processed, but nothing found */
8722 if (xmlStrcmp(*code, BAD_CAST "0000")) {
8723 char *code_locale = _isds_utf82locale((char*) *code);
8724 char *status_message_locale = _isds_utf82locale((char*) *status_message);
8725 isds_log(ILF_ISDS, ILL_DEBUG,
8726 _("Server refused %s request for %s message ID "
8727 "(code=%s, message=%s)\n"),
8728 service_name_locale, message_id_locale,
8729 code_locale, status_message_locale);
8730 isds_log_message(context, status_message_locale);
8731 free(code_locale);
8732 free(status_message_locale);
8733 err = IE_ISDS;
8734 goto leave;
8737 leave:
8738 free(message_id_locale);
8739 free(service_name_locale);
8740 xmlFreeNode(request);
8741 return err;
8745 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
8746 * signed data and free ISDS response.
8747 * @context is session context
8748 * @message_id is UTF-8 encoded message ID for logging purpose
8749 * @response is parsed XML document. It will be freed and NULLed in the middle
8750 * of function run to save memory. This is not guaranteed in case of error.
8751 * @request_name is name of ISDS request used to construct response root
8752 * element name and for logging purpose.
8753 * @raw is reallocated output buffer with DER encoded CMS data
8754 * @raw_length is size of @raw buffer in bytes
8755 * @returns standard error codes, in case of error, @raw will be freed and
8756 * NULLed, @response sometimes. */
8757 static isds_error find_extract_signed_data_free_response(
8758 struct isds_ctx *context, const xmlChar *message_id,
8759 xmlDocPtr *response, const xmlChar *request_name,
8760 void **raw, size_t *raw_length) {
8762 isds_error err = IE_SUCCESS;
8763 char *xpath_expression = NULL;
8764 xmlXPathContextPtr xpath_ctx = NULL;
8765 xmlXPathObjectPtr result = NULL;
8766 char *encoded_structure = NULL;
8768 if (!context) return IE_INVALID_CONTEXT;
8769 if (!raw) return IE_INVAL;
8770 zfree(*raw);
8771 if (!message_id || !response || !*response || !request_name || !raw_length)
8772 return IE_INVAL;
8774 /* Build XPath expression */
8775 xpath_expression = _isds_astrcat3("/isds:", (char *) request_name,
8776 "Response/isds:dmSignature");
8777 if (!xpath_expression) return IE_NOMEM;
8779 /* Extract data */
8780 xpath_ctx = xmlXPathNewContext(*response);
8781 if (!xpath_ctx) {
8782 err = IE_ERROR;
8783 goto leave;
8785 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8786 err = IE_ERROR;
8787 goto leave;
8789 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
8790 if (!result) {
8791 err = IE_ERROR;
8792 goto leave;
8794 /* Empty response */
8795 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8796 char *message_id_locale = _isds_utf82locale((char*) message_id);
8797 isds_printf_message(context,
8798 _("Server did not return any signed data for message ID `%s' "
8799 "on %s request"),
8800 message_id_locale, request_name);
8801 free(message_id_locale);
8802 err = IE_ISDS;
8803 goto leave;
8805 /* More responses */
8806 if (result->nodesetval->nodeNr > 1) {
8807 char *message_id_locale = _isds_utf82locale((char*) message_id);
8808 isds_printf_message(context,
8809 _("Server did return more signed data for message ID `%s' "
8810 "on %s request"),
8811 message_id_locale, request_name);
8812 free(message_id_locale);
8813 err = IE_ISDS;
8814 goto leave;
8816 /* One response */
8817 xpath_ctx->node = result->nodesetval->nodeTab[0];
8819 /* Extract PKCS#7 structure */
8820 EXTRACT_STRING(".", encoded_structure);
8821 if (!encoded_structure) {
8822 isds_log_message(context, _("dmSignature element is empty"));
8825 /* Here we have delivery info as standalone CMS in encoded_structure.
8826 * We don't need any other data, free them: */
8827 xmlXPathFreeObject(result); result = NULL;
8828 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
8829 xmlFreeDoc(*response); *response = NULL;
8832 /* Decode PKCS#7 to DER format */
8833 *raw_length = _isds_b64decode(encoded_structure, raw);
8834 if (*raw_length == (size_t) -1) {
8835 isds_log_message(context,
8836 _("Error while Base64-decoding PKCS#7 structure"));
8837 err = IE_ERROR;
8838 goto leave;
8841 leave:
8842 if (err) {
8843 zfree(*raw);
8844 raw_length = 0;
8847 free(encoded_structure);
8848 xmlXPathFreeObject(result);
8849 xmlXPathFreeContext(xpath_ctx);
8850 free(xpath_expression);
8852 return err;
8854 #endif /* HAVE_LIBCURL */
8857 /* Download incoming message envelope identified by ID.
8858 * @context is session context
8859 * @message_id is message identifier (you can get them from
8860 * isds_get_list_of_received_messages())
8861 * @message is automatically reallocated message retrieved from ISDS.
8862 * It will miss documents per se. Use isds_get_received_message(), if you are
8863 * interested in documents (content) too.
8864 * Returned hash and timestamp require documents to be verifiable. */
8865 isds_error isds_get_received_envelope(struct isds_ctx *context,
8866 const char *message_id, struct isds_message **message) {
8868 isds_error err = IE_SUCCESS;
8869 #if HAVE_LIBCURL
8870 xmlDocPtr response = NULL;
8871 xmlChar *code = NULL, *status_message = NULL;
8872 xmlXPathContextPtr xpath_ctx = NULL;
8873 xmlXPathObjectPtr result = NULL;
8874 #endif
8876 if (!context) return IE_INVALID_CONTEXT;
8877 zfree(context->long_message);
8879 /* Free former message if any */
8880 if (!message) return IE_INVAL;
8881 isds_message_free(message);
8883 #if HAVE_LIBCURL
8884 /* Do request and check for success */
8885 err = build_send_check_message_request(context, SERVICE_DM_INFO,
8886 BAD_CAST "MessageEnvelopeDownload", message_id,
8887 &response, NULL, NULL, &code, &status_message);
8888 if (err) goto leave;
8890 /* Extract data */
8891 xpath_ctx = xmlXPathNewContext(response);
8892 if (!xpath_ctx) {
8893 err = IE_ERROR;
8894 goto leave;
8896 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8897 err = IE_ERROR;
8898 goto leave;
8900 result = xmlXPathEvalExpression(
8901 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
8902 "isds:dmReturnedMessageEnvelope",
8903 xpath_ctx);
8904 if (!result) {
8905 err = IE_ERROR;
8906 goto leave;
8908 /* Empty response */
8909 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8910 char *message_id_locale = _isds_utf82locale((char*) message_id);
8911 isds_printf_message(context,
8912 _("Server did not return any envelope for ID `%s' "
8913 "on MessageEnvelopeDownload request"), message_id_locale);
8914 free(message_id_locale);
8915 err = IE_ISDS;
8916 goto leave;
8918 /* More envelops */
8919 if (result->nodesetval->nodeNr > 1) {
8920 char *message_id_locale = _isds_utf82locale((char*) message_id);
8921 isds_printf_message(context,
8922 _("Server did return more envelopes for ID `%s' "
8923 "on MessageEnvelopeDownload request"), message_id_locale);
8924 free(message_id_locale);
8925 err = IE_ISDS;
8926 goto leave;
8928 /* One message */
8929 xpath_ctx->node = result->nodesetval->nodeTab[0];
8931 /* Extract the envelope (= message without documents, hence 0) */
8932 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
8933 if (err) goto leave;
8935 /* Save XML blob */
8936 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
8937 &(*message)->raw_length);
8939 leave:
8940 if (err) {
8941 isds_message_free(message);
8944 xmlXPathFreeObject(result);
8945 xmlXPathFreeContext(xpath_ctx);
8947 free(code);
8948 free(status_message);
8949 if (!*message || !(*message)->xml) {
8950 xmlFreeDoc(response);
8953 if (!err)
8954 isds_log(ILF_ISDS, ILL_DEBUG,
8955 _("MessageEnvelopeDownload request processed by server "
8956 "successfully.\n")
8958 #else /* not HAVE_LIBCURL */
8959 err = IE_NOTSUP;
8960 #endif
8961 return err;
8965 /* Load delivery info of any format from buffer.
8966 * @context is session context
8967 * @raw_type advertises format of @buffer content. Only delivery info types
8968 * are accepted.
8969 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
8970 * retrieve such data from message->raw after calling
8971 * isds_get_signed_delivery_info().
8972 * @length is length of buffer in bytes.
8973 * @message is automatically reallocated message parsed from @buffer.
8974 * @strategy selects how buffer will be attached into raw isds_message member.
8975 * */
8976 isds_error isds_load_delivery_info(struct isds_ctx *context,
8977 const isds_raw_type raw_type,
8978 const void *buffer, const size_t length,
8979 struct isds_message **message, const isds_buffer_strategy strategy) {
8981 isds_error err = IE_SUCCESS;
8982 message_ns_type message_ns;
8983 xmlDocPtr message_doc = NULL;
8984 xmlXPathContextPtr xpath_ctx = NULL;
8985 xmlXPathObjectPtr result = NULL;
8986 void *xml_stream = NULL;
8987 size_t xml_stream_length = 0;
8989 if (!context) return IE_INVALID_CONTEXT;
8990 zfree(context->long_message);
8991 if (!message) return IE_INVAL;
8992 isds_message_free(message);
8993 if (!buffer) return IE_INVAL;
8996 /* Select buffer format and extract XML from CMS*/
8997 switch (raw_type) {
8998 case RAWTYPE_DELIVERYINFO:
8999 message_ns = MESSAGE_NS_UNSIGNED;
9000 xml_stream = (void *) buffer;
9001 xml_stream_length = length;
9002 break;
9004 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
9005 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
9006 xml_stream = (void *) buffer;
9007 xml_stream_length = length;
9008 break;
9010 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
9011 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
9012 err = _isds_extract_cms_data(context, buffer, length,
9013 &xml_stream, &xml_stream_length);
9014 if (err) goto leave;
9015 break;
9017 default:
9018 isds_log_message(context, _("Bad raw delivery representation type"));
9019 return IE_INVAL;
9020 break;
9023 isds_log(ILF_ISDS, ILL_DEBUG,
9024 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
9025 xml_stream_length, xml_stream);
9027 /* Convert delivery info XML stream into XPath context */
9028 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
9029 if (!message_doc) {
9030 err = IE_XML;
9031 goto leave;
9033 xpath_ctx = xmlXPathNewContext(message_doc);
9034 if (!xpath_ctx) {
9035 err = IE_ERROR;
9036 goto leave;
9038 /* XXX: Name spaces mangled for signed delivery info:
9039 * http://isds.czechpoint.cz/v20/delivery:
9041 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
9042 * <q:dmDelivery>
9043 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
9044 * <p:dmID>170272</p:dmID>
9045 * ...
9046 * </p:dmDm>
9047 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
9048 * ...
9049 * </q:dmEvents>...</q:dmEvents>
9050 * </q:dmDelivery>
9051 * </q:GetDeliveryInfoResponse>
9052 * */
9053 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
9054 err = IE_ERROR;
9055 goto leave;
9057 result = xmlXPathEvalExpression(
9058 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
9059 xpath_ctx);
9060 if (!result) {
9061 err = IE_ERROR;
9062 goto leave;
9064 /* Empty delivery info */
9065 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9066 isds_printf_message(context,
9067 _("XML document is not sisds:dmDelivery document"));
9068 err = IE_ISDS;
9069 goto leave;
9071 /* More delivery info's */
9072 if (result->nodesetval->nodeNr > 1) {
9073 isds_printf_message(context,
9074 _("XML document has more sisds:dmDelivery elements"));
9075 err = IE_ISDS;
9076 goto leave;
9078 /* One delivery info */
9079 xpath_ctx->node = result->nodesetval->nodeTab[0];
9081 /* Extract the envelope (= message without documents, hence 0).
9082 * XXX: extract_TReturnedMessage() can obtain attachments size,
9083 * but delivery info carries none. It's coded as option elements,
9084 * so it should work. */
9085 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
9086 if (err) goto leave;
9088 /* Extract events */
9089 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
9090 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
9091 if (err) { err = IE_ERROR; goto leave; }
9092 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
9093 if (err) goto leave;
9095 /* Append raw CMS structure into message */
9096 (*message)->raw_type = raw_type;
9097 switch (strategy) {
9098 case BUFFER_DONT_STORE:
9099 break;
9100 case BUFFER_COPY:
9101 (*message)->raw = malloc(length);
9102 if (!(*message)->raw) {
9103 err = IE_NOMEM;
9104 goto leave;
9106 memcpy((*message)->raw, buffer, length);
9107 (*message)->raw_length = length;
9108 break;
9109 case BUFFER_MOVE:
9110 (*message)->raw = (void *) buffer;
9111 (*message)->raw_length = length;
9112 break;
9113 default:
9114 err = IE_ENUM;
9115 goto leave;
9118 leave:
9119 if (err) {
9120 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
9121 isds_message_free(message);
9124 xmlXPathFreeObject(result);
9125 xmlXPathFreeContext(xpath_ctx);
9126 if (!*message || !(*message)->xml) {
9127 xmlFreeDoc(message_doc);
9129 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
9131 if (!err)
9132 isds_log(ILF_ISDS, ILL_DEBUG,
9133 _("Delivery info loaded successfully.\n"));
9134 return err;
9138 /* Download signed delivery info-sheet of given message identified by ID.
9139 * @context is session context
9140 * @message_id is message identifier (you can get them from
9141 * isds_get_list_of_{sent,received}_messages())
9142 * @message is automatically reallocated message retrieved from ISDS.
9143 * It will miss documents per se. Use isds_get_signed_received_message(),
9144 * if you are interested in documents (content). OTOH, only this function
9145 * can get list events message has gone through. */
9146 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
9147 const char *message_id, struct isds_message **message) {
9149 isds_error err = IE_SUCCESS;
9150 #if HAVE_LIBCURL
9151 xmlDocPtr response = NULL;
9152 xmlChar *code = NULL, *status_message = NULL;
9153 void *raw = NULL;
9154 size_t raw_length = 0;
9155 #endif
9157 if (!context) return IE_INVALID_CONTEXT;
9158 zfree(context->long_message);
9160 /* Free former message if any */
9161 if (!message) return IE_INVAL;
9162 isds_message_free(message);
9164 #if HAVE_LIBCURL
9165 /* Do request and check for success */
9166 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9167 BAD_CAST "GetSignedDeliveryInfo", message_id,
9168 &response, NULL, NULL, &code, &status_message);
9169 if (err) goto leave;
9171 /* Find signed delivery info, extract it into raw and maybe free
9172 * response */
9173 err = find_extract_signed_data_free_response(context,
9174 (xmlChar *)message_id, &response,
9175 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
9176 if (err) goto leave;
9178 /* Parse delivery info */
9179 err = isds_load_delivery_info(context,
9180 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
9181 message, BUFFER_MOVE);
9182 if (err) goto leave;
9184 raw = NULL;
9186 leave:
9187 if (err) {
9188 isds_message_free(message);
9191 free(raw);
9192 free(code);
9193 free(status_message);
9194 xmlFreeDoc(response);
9196 if (!err)
9197 isds_log(ILF_ISDS, ILL_DEBUG,
9198 _("GetSignedDeliveryInfo request processed by server "
9199 "successfully.\n")
9201 #else /* not HAVE_LIBCURL */
9202 err = IE_NOTSUP;
9203 #endif
9204 return err;
9208 /* Download delivery info-sheet of given message identified by ID.
9209 * @context is session context
9210 * @message_id is message identifier (you can get them from
9211 * isds_get_list_of_{sent,received}_messages())
9212 * @message is automatically reallocated message retrieved from ISDS.
9213 * It will miss documents per se. Use isds_get_received_message(), if you are
9214 * interested in documents (content). OTOH, only this function can get list
9215 * of events message has gone through. */
9216 isds_error isds_get_delivery_info(struct isds_ctx *context,
9217 const char *message_id, struct isds_message **message) {
9219 isds_error err = IE_SUCCESS;
9220 #if HAVE_LIBCURL
9221 xmlDocPtr response = NULL;
9222 xmlChar *code = NULL, *status_message = NULL;
9223 xmlNodePtr delivery_node = NULL;
9224 void *raw = NULL;
9225 size_t raw_length = 0;
9226 #endif
9228 if (!context) return IE_INVALID_CONTEXT;
9229 zfree(context->long_message);
9231 /* Free former message if any */
9232 if (!message) return IE_INVAL;
9233 isds_message_free(message);
9235 #if HAVE_LIBCURL
9236 /* Do request and check for success */
9237 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9238 BAD_CAST "GetDeliveryInfo", message_id,
9239 &response, NULL, NULL, &code, &status_message);
9240 if (err) goto leave;
9243 /* Serialize delivery info */
9244 delivery_node = xmlDocGetRootElement(response);
9245 if (!delivery_node) {
9246 char *message_id_locale = _isds_utf82locale((char*) message_id);
9247 isds_printf_message(context,
9248 _("Server did not return any delivery info for ID `%s' "
9249 "on GetDeliveryInfo request"), message_id_locale);
9250 free(message_id_locale);
9251 err = IE_ISDS;
9252 goto leave;
9254 err = serialize_subtree(context, delivery_node, &raw, &raw_length);
9255 if (err) goto leave;
9257 /* Parse delivery info */
9258 /* TODO: Here we parse the response second time. We could single delivery
9259 * parser from isds_load_delivery_info() to make things faster. */
9260 err = isds_load_delivery_info(context,
9261 RAWTYPE_DELIVERYINFO, raw, raw_length,
9262 message, BUFFER_MOVE);
9263 if (err) goto leave;
9265 raw = NULL;
9268 leave:
9269 if (err) {
9270 isds_message_free(message);
9273 free(raw);
9274 free(code);
9275 free(status_message);
9276 xmlFreeDoc(response);
9278 if (!err)
9279 isds_log(ILF_ISDS, ILL_DEBUG,
9280 _("GetDeliveryInfo request processed by server "
9281 "successfully.\n")
9283 #else /* not HAVE_LIBCURL */
9284 err = IE_NOTSUP;
9285 #endif
9286 return err;
9290 /* Download incoming message identified by ID.
9291 * @context is session context
9292 * @message_id is message identifier (you can get them from
9293 * isds_get_list_of_received_messages())
9294 * @message is automatically reallocated message retrieved from ISDS */
9295 isds_error isds_get_received_message(struct isds_ctx *context,
9296 const char *message_id, struct isds_message **message) {
9298 isds_error err = IE_SUCCESS;
9299 #if HAVE_LIBCURL
9300 xmlDocPtr response = NULL;
9301 void *xml_stream = NULL;
9302 size_t xml_stream_length;
9303 xmlChar *code = NULL, *status_message = NULL;
9304 xmlXPathContextPtr xpath_ctx = NULL;
9305 xmlXPathObjectPtr result = NULL;
9306 char *phys_path = NULL;
9307 size_t phys_start, phys_end;
9308 #endif
9310 if (!context) return IE_INVALID_CONTEXT;
9311 zfree(context->long_message);
9313 /* Free former message if any */
9314 if (message) isds_message_free(message);
9316 #if HAVE_LIBCURL
9317 /* Do request and check for success */
9318 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
9319 BAD_CAST "MessageDownload", message_id,
9320 &response, &xml_stream, &xml_stream_length,
9321 &code, &status_message);
9322 if (err) goto leave;
9324 /* Extract data */
9325 xpath_ctx = xmlXPathNewContext(response);
9326 if (!xpath_ctx) {
9327 err = IE_ERROR;
9328 goto leave;
9330 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9331 err = IE_ERROR;
9332 goto leave;
9334 result = xmlXPathEvalExpression(
9335 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
9336 xpath_ctx);
9337 if (!result) {
9338 err = IE_ERROR;
9339 goto leave;
9341 /* Empty response */
9342 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9343 char *message_id_locale = _isds_utf82locale((char*) message_id);
9344 isds_printf_message(context,
9345 _("Server did not return any message for ID `%s' "
9346 "on MessageDownload request"), message_id_locale);
9347 free(message_id_locale);
9348 err = IE_ISDS;
9349 goto leave;
9351 /* More messages */
9352 if (result->nodesetval->nodeNr > 1) {
9353 char *message_id_locale = _isds_utf82locale((char*) message_id);
9354 isds_printf_message(context,
9355 _("Server did return more messages for ID `%s' "
9356 "on MessageDownload request"), message_id_locale);
9357 free(message_id_locale);
9358 err = IE_ISDS;
9359 goto leave;
9361 /* One message */
9362 xpath_ctx->node = result->nodesetval->nodeTab[0];
9364 /* Extract the message */
9365 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
9366 if (err) goto leave;
9368 /* Locate raw XML blob */
9369 phys_path = strdup(
9370 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
9371 PHYSXML_ELEMENT_SEPARATOR
9372 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
9373 PHYSXML_ELEMENT_SEPARATOR
9374 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
9376 if (!phys_path) {
9377 err = IE_NOMEM;
9378 goto leave;
9380 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
9381 phys_path, &phys_start, &phys_end);
9382 zfree(phys_path);
9383 if (err) {
9384 isds_log_message(context,
9385 _("Substring with isds:MessageDownloadResponse element "
9386 "could not be located in raw SOAP message"));
9387 goto leave;
9389 /* Save XML blob */
9390 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
9391 &(*message)->raw_length);*/
9392 /* TODO: Store name space declarations from ancestors */
9393 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
9394 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
9395 (*message)->raw_length = phys_end - phys_start + 1;
9396 (*message)->raw = malloc((*message)->raw_length);
9397 if (!(*message)->raw) {
9398 err = IE_NOMEM;
9399 goto leave;
9401 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
9404 leave:
9405 if (err) {
9406 isds_message_free(message);
9409 free(phys_path);
9411 xmlXPathFreeObject(result);
9412 xmlXPathFreeContext(xpath_ctx);
9414 free(code);
9415 free(status_message);
9416 free(xml_stream);
9417 if (!*message || !(*message)->xml) {
9418 xmlFreeDoc(response);
9421 if (!err)
9422 isds_log(ILF_ISDS, ILL_DEBUG,
9423 _("MessageDownload request processed by server "
9424 "successfully.\n")
9426 #else /* not HAVE_LIBCURL */
9427 err = IE_NOTSUP;
9428 #endif
9429 return err;
9433 /* Load message of any type from buffer.
9434 * @context is session context
9435 * @raw_type defines content type of @buffer. Only message types are allowed.
9436 * @buffer is message raw representation. Format (CMS, plain signed,
9437 * message direction) is defined in @raw_type. You can retrieve such data
9438 * from message->raw after calling isds_get_[signed]{received,sent}_message().
9439 * @length is length of buffer in bytes.
9440 * @message is automatically reallocated message parsed from @buffer.
9441 * @strategy selects how buffer will be attached into raw isds_message member.
9442 * */
9443 isds_error isds_load_message(struct isds_ctx *context,
9444 const isds_raw_type raw_type, const void *buffer, const size_t length,
9445 struct isds_message **message, const isds_buffer_strategy strategy) {
9447 isds_error err = IE_SUCCESS;
9448 void *xml_stream = NULL;
9449 size_t xml_stream_length = 0;
9450 message_ns_type message_ns;
9451 xmlDocPtr message_doc = NULL;
9452 xmlXPathContextPtr xpath_ctx = NULL;
9453 xmlXPathObjectPtr result = NULL;
9455 if (!context) return IE_INVALID_CONTEXT;
9456 zfree(context->long_message);
9457 if (!message) return IE_INVAL;
9458 isds_message_free(message);
9459 if (!buffer) return IE_INVAL;
9462 /* Select buffer format and extract XML from CMS*/
9463 switch (raw_type) {
9464 case RAWTYPE_INCOMING_MESSAGE:
9465 message_ns = MESSAGE_NS_UNSIGNED;
9466 xml_stream = (void *) buffer;
9467 xml_stream_length = length;
9468 break;
9470 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
9471 message_ns = MESSAGE_NS_SIGNED_INCOMING;
9472 xml_stream = (void *) buffer;
9473 xml_stream_length = length;
9474 break;
9476 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
9477 message_ns = MESSAGE_NS_SIGNED_INCOMING;
9478 err = _isds_extract_cms_data(context, buffer, length,
9479 &xml_stream, &xml_stream_length);
9480 if (err) goto leave;
9481 break;
9483 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
9484 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
9485 xml_stream = (void *) buffer;
9486 xml_stream_length = length;
9487 break;
9489 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
9490 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
9491 err = _isds_extract_cms_data(context, buffer, length,
9492 &xml_stream, &xml_stream_length);
9493 if (err) goto leave;
9494 break;
9496 default:
9497 isds_log_message(context, _("Bad raw message representation type"));
9498 return IE_INVAL;
9499 break;
9502 isds_log(ILF_ISDS, ILL_DEBUG,
9503 _("Loading message:\n%.*s\nEnd of message\n"),
9504 xml_stream_length, xml_stream);
9506 /* Convert messages XML stream into XPath context */
9507 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
9508 if (!message_doc) {
9509 err = IE_XML;
9510 goto leave;
9512 xpath_ctx = xmlXPathNewContext(message_doc);
9513 if (!xpath_ctx) {
9514 err = IE_ERROR;
9515 goto leave;
9517 /* XXX: Standard name space for unsigned incoming direction:
9518 * http://isds.czechpoint.cz/v20/
9520 * XXX: Name spaces mangled for signed outgoing direction:
9521 * http://isds.czechpoint.cz/v20/SentMessage:
9523 * <q:MessageDownloadResponse
9524 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
9525 * <q:dmReturnedMessage>
9526 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
9527 * <p:dmID>151916</p:dmID>
9528 * ...
9529 * </p:dmDm>
9530 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
9531 * ...
9532 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
9533 * </q:dmReturnedMessage>
9534 * </q:MessageDownloadResponse>
9536 * XXX: Name spaces mangled for signed incoming direction:
9537 * http://isds.czechpoint.cz/v20/message:
9539 * <q:MessageDownloadResponse
9540 * xmlns:q="http://isds.czechpoint.cz/v20/message">
9541 * <q:dmReturnedMessage>
9542 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
9543 * <p:dmID>151916</p:dmID>
9544 * ...
9545 * </p:dmDm>
9546 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
9547 * ...
9548 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
9549 * </q:dmReturnedMessage>
9550 * </q:MessageDownloadResponse>
9552 * Stupidity of ISDS developers is unlimited */
9553 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
9554 err = IE_ERROR;
9555 goto leave;
9557 result = xmlXPathEvalExpression(
9558 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
9559 xpath_ctx);
9560 if (!result) {
9561 err = IE_ERROR;
9562 goto leave;
9564 /* Empty message */
9565 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9566 isds_printf_message(context,
9567 _("XML document does not contain "
9568 "sisds:dmReturnedMessage element"));
9569 err = IE_ISDS;
9570 goto leave;
9572 /* More messages */
9573 if (result->nodesetval->nodeNr > 1) {
9574 isds_printf_message(context,
9575 _("XML document has more sisds:dmReturnedMessage elements"));
9576 err = IE_ISDS;
9577 goto leave;
9579 /* One message */
9580 xpath_ctx->node = result->nodesetval->nodeTab[0];
9582 /* Extract the message */
9583 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
9584 if (err) goto leave;
9586 /* Append raw buffer into message */
9587 (*message)->raw_type = raw_type;
9588 switch (strategy) {
9589 case BUFFER_DONT_STORE:
9590 break;
9591 case BUFFER_COPY:
9592 (*message)->raw = malloc(length);
9593 if (!(*message)->raw) {
9594 err = IE_NOMEM;
9595 goto leave;
9597 memcpy((*message)->raw, buffer, length);
9598 (*message)->raw_length = length;
9599 break;
9600 case BUFFER_MOVE:
9601 (*message)->raw = (void *) buffer;
9602 (*message)->raw_length = length;
9603 break;
9604 default:
9605 err = IE_ENUM;
9606 goto leave;
9610 leave:
9611 if (err) {
9612 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
9613 isds_message_free(message);
9616 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
9617 xmlXPathFreeObject(result);
9618 xmlXPathFreeContext(xpath_ctx);
9619 if (!*message || !(*message)->xml) {
9620 xmlFreeDoc(message_doc);
9623 if (!err)
9624 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
9625 return err;
9629 /* Determine type of raw message or delivery info according some heuristics.
9630 * It does not validate the raw blob.
9631 * @context is session context
9632 * @raw_type returns content type of @buffer. Valid only if exit code of this
9633 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
9634 * reallocated memory.
9635 * @buffer is message raw representation.
9636 * @length is length of buffer in bytes. */
9637 isds_error isds_guess_raw_type(struct isds_ctx *context,
9638 isds_raw_type *raw_type, const void *buffer, const size_t length) {
9639 isds_error err;
9640 void *xml_stream = NULL;
9641 size_t xml_stream_length = 0;
9642 xmlDocPtr document = NULL;
9643 xmlNodePtr root = NULL;
9645 if (!context) return IE_INVALID_CONTEXT;
9646 zfree(context->long_message);
9647 if (length == 0 || !buffer) return IE_INVAL;
9648 if (!raw_type) return IE_INVAL;
9650 /* Try CMS */
9651 err = _isds_extract_cms_data(context, buffer, length,
9652 &xml_stream, &xml_stream_length);
9653 if (err) {
9654 xml_stream = (void *) buffer;
9655 xml_stream_length = (size_t) length;
9656 err = IE_SUCCESS;
9659 /* Try XML */
9660 document = xmlParseMemory(xml_stream, xml_stream_length);
9661 if (!document) {
9662 isds_printf_message(context,
9663 _("Could not parse data as XML document"));
9664 err = IE_NOTSUP;
9665 goto leave;
9668 /* Get root element */
9669 root = xmlDocGetRootElement(document);
9670 if (!root) {
9671 isds_printf_message(context,
9672 _("XML document is missing root element"));
9673 err = IE_XML;
9674 goto leave;
9677 if (!root->ns || !root->ns->href) {
9678 isds_printf_message(context,
9679 _("Root element does not belong to any name space"));
9680 err = IE_NOTSUP;
9681 goto leave;
9684 /* Test name space */
9685 if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_INCOMING_NS)) {
9686 if (xml_stream == buffer)
9687 *raw_type = RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE;
9688 else
9689 *raw_type = RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
9690 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_OUTGOING_NS)) {
9691 if (xml_stream == buffer)
9692 *raw_type = RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE;
9693 else
9694 *raw_type = RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE;
9695 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_DELIVERY_NS)) {
9696 if (xml_stream == buffer)
9697 *raw_type = RAWTYPE_PLAIN_SIGNED_DELIVERYINFO;
9698 else
9699 *raw_type = RAWTYPE_CMS_SIGNED_DELIVERYINFO;
9700 } else if (!xmlStrcmp(root->ns->href, BAD_CAST ISDS_NS)) {
9701 if (xml_stream != buffer) {
9702 isds_printf_message(context,
9703 _("Document in ISDS name space is encapsulated into CMS" ));
9704 err = IE_NOTSUP;
9705 } else if (!xmlStrcmp(root->name, BAD_CAST "MessageDownloadResponse"))
9706 *raw_type = RAWTYPE_INCOMING_MESSAGE;
9707 else if (!xmlStrcmp(root->name, BAD_CAST "GetDeliveryInfoResponse"))
9708 *raw_type = RAWTYPE_DELIVERYINFO;
9709 else {
9710 isds_printf_message(context,
9711 _("Unknown root element in ISDS name space"));
9712 err = IE_NOTSUP;
9714 } else {
9715 isds_printf_message(context,
9716 _("Unknown name space"));
9717 err = IE_NOTSUP;
9720 leave:
9721 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
9722 xmlFreeDoc(document);
9723 return err;
9727 /* Download signed incoming/outgoing message identified by ID.
9728 * @context is session context
9729 * @output is true for outgoing message, false for incoming message
9730 * @message_id is message identifier (you can get them from
9731 * isds_get_list_of_{sent,received}_messages())
9732 * @message is automatically reallocated message retrieved from ISDS. The raw
9733 * member will be filled with PKCS#7 structure in DER format. */
9734 static isds_error isds_get_signed_message(struct isds_ctx *context,
9735 const _Bool outgoing, const char *message_id,
9736 struct isds_message **message) {
9738 isds_error err = IE_SUCCESS;
9739 #if HAVE_LIBCURL
9740 xmlDocPtr response = NULL;
9741 xmlChar *code = NULL, *status_message = NULL;
9742 xmlXPathContextPtr xpath_ctx = NULL;
9743 xmlXPathObjectPtr result = NULL;
9744 char *encoded_structure = NULL;
9745 void *raw = NULL;
9746 size_t raw_length = 0;
9747 #endif
9749 if (!context) return IE_INVALID_CONTEXT;
9750 zfree(context->long_message);
9751 if (!message) return IE_INVAL;
9752 isds_message_free(message);
9754 #if HAVE_LIBCURL
9755 /* Do request and check for success */
9756 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
9757 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
9758 BAD_CAST "SignedMessageDownload",
9759 message_id, &response, NULL, NULL, &code, &status_message);
9760 if (err) goto leave;
9762 /* Find signed message, extract it into raw and maybe free
9763 * response */
9764 err = find_extract_signed_data_free_response(context,
9765 (xmlChar *)message_id, &response,
9766 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
9767 BAD_CAST "SignedMessageDownload",
9768 &raw, &raw_length);
9769 if (err) goto leave;
9771 /* Parse message */
9772 err = isds_load_message(context,
9773 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
9774 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
9775 raw, raw_length, message, BUFFER_MOVE);
9776 if (err) goto leave;
9778 raw = NULL;
9780 leave:
9781 if (err) {
9782 isds_message_free(message);
9785 free(encoded_structure);
9786 xmlXPathFreeObject(result);
9787 xmlXPathFreeContext(xpath_ctx);
9788 free(raw);
9790 free(code);
9791 free(status_message);
9792 xmlFreeDoc(response);
9794 if (!err)
9795 isds_log(ILF_ISDS, ILL_DEBUG,
9796 (outgoing) ?
9797 _("SignedSentMessageDownload request processed by server "
9798 "successfully.\n") :
9799 _("SignedMessageDownload request processed by server "
9800 "successfully.\n")
9802 #else /* not HAVE_LIBCURL */
9803 err = IE_NOTSUP;
9804 #endif
9805 return err;
9809 /* Download signed incoming message identified by ID.
9810 * @context is session context
9811 * @message_id is message identifier (you can get them from
9812 * isds_get_list_of_received_messages())
9813 * @message is automatically reallocated message retrieved from ISDS. The raw
9814 * member will be filled with PKCS#7 structure in DER format. */
9815 isds_error isds_get_signed_received_message(struct isds_ctx *context,
9816 const char *message_id, struct isds_message **message) {
9817 return isds_get_signed_message(context, 0, message_id, message);
9821 /* Download signed outgoing message identified by ID.
9822 * @context is session context
9823 * @message_id is message identifier (you can get them from
9824 * isds_get_list_of_sent_messages())
9825 * @message is automatically reallocated message retrieved from ISDS. The raw
9826 * member will be filled with PKCS#7 structure in DER format. */
9827 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
9828 const char *message_id, struct isds_message **message) {
9829 return isds_get_signed_message(context, 1, message_id, message);
9833 /* Get type and name of user who sent a message identified by ID.
9834 * @context is session context
9835 * @message_id is message identifier
9836 * @sender_type is pointer to automatically allocated type of sender detected
9837 * from @raw_sender_type string. If @raw_sender_type is unknown to this
9838 * library or to the server, NULL will be returned. Pass NULL if you don't
9839 * care about it.
9840 * @raw_sender_type is automatically reallocated UTF-8 string describing
9841 * sender type or NULL if not known to server. Pass NULL if you don't care.
9842 * @sender_name is automatically reallocated UTF-8 name of user who sent the
9843 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
9844 isds_error isds_get_message_sender(struct isds_ctx *context,
9845 const char *message_id, isds_sender_type **sender_type,
9846 char **raw_sender_type, char **sender_name) {
9847 isds_error err = IE_SUCCESS;
9848 #if HAVE_LIBCURL
9849 xmlDocPtr response = NULL;
9850 xmlChar *code = NULL, *status_message = NULL;
9851 xmlXPathContextPtr xpath_ctx = NULL;
9852 xmlXPathObjectPtr result = NULL;
9853 char *type_string = NULL;
9854 #endif
9856 if (!context) return IE_INVALID_CONTEXT;
9857 zfree(context->long_message);
9858 if (sender_type) zfree(*sender_type);
9859 if (raw_sender_type) zfree(*raw_sender_type);
9860 if (sender_name) zfree(*sender_name);
9861 if (!message_id) return IE_INVAL;
9863 #if HAVE_LIBCURL
9864 /* Do request and check for success */
9865 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9866 BAD_CAST "GetMessageAuthor",
9867 message_id, &response, NULL, NULL, &code, &status_message);
9868 if (err) goto leave;
9870 /* Extract data */
9871 xpath_ctx = xmlXPathNewContext(response);
9872 if (!xpath_ctx) {
9873 err = IE_ERROR;
9874 goto leave;
9876 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9877 err = IE_ERROR;
9878 goto leave;
9880 result = xmlXPathEvalExpression(
9881 BAD_CAST "/isds:GetMessageAuthorResponse", xpath_ctx);
9882 if (!result) {
9883 err = IE_ERROR;
9884 goto leave;
9886 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9887 isds_log_message(context,
9888 _("Missing GetMessageAuthorResponse element"));
9889 err = IE_ISDS;
9890 goto leave;
9892 if (result->nodesetval->nodeNr > 1) {
9893 isds_log_message(context,
9894 _("Multiple GetMessageAuthorResponse element"));
9895 err = IE_ISDS;
9896 goto leave;
9898 xpath_ctx->node = result->nodesetval->nodeTab[0];
9899 xmlXPathFreeObject(result); result = NULL;
9901 /* Fill output arguments in */
9902 EXTRACT_STRING("isds:userType", type_string);
9903 if (type_string) {
9904 *sender_type = calloc(1, sizeof(**sender_type));
9905 if (!*sender_type) {
9906 err = IE_NOMEM;
9907 goto leave;
9910 if (sender_type) {
9911 err = string2isds_sender_type((xmlChar *)type_string,
9912 *sender_type);
9913 if (err) {
9914 zfree(*sender_type);
9915 if (err == IE_ENUM) {
9916 err = IE_SUCCESS;
9917 char *type_string_locale = _isds_utf82locale(type_string);
9918 isds_log(ILF_ISDS, ILL_WARNING,
9919 _("Unknown isds:userType value: %s"),
9920 type_string_locale);
9921 free(type_string_locale);
9926 if (sender_type)
9927 EXTRACT_STRING("isds:authorName", *sender_name);
9929 leave:
9930 if (err) {
9931 if (sender_type) zfree(*sender_type);
9932 zfree(type_string);
9933 if (sender_name) zfree(*sender_name);
9935 if (raw_sender_type) *raw_sender_type = type_string;
9937 xmlXPathFreeObject(result);
9938 xmlXPathFreeContext(xpath_ctx);
9940 free(code);
9941 free(status_message);
9942 xmlFreeDoc(response);
9944 if (!err)
9945 isds_log(ILF_ISDS, ILL_DEBUG,
9946 _("GetMessageAuthor request processed by server "
9947 "successfully.\n"));
9948 #else /* not HAVE_LIBCURL */
9949 err = IE_NOTSUP;
9950 #endif
9951 return err;
9955 /* Retrieve hash of message identified by ID stored in ISDS.
9956 * @context is session context
9957 * @message_id is message identifier
9958 * @hash is automatically reallocated message hash downloaded from ISDS.
9959 * Message must exist in system and must not be deleted. */
9960 isds_error isds_download_message_hash(struct isds_ctx *context,
9961 const char *message_id, struct isds_hash **hash) {
9963 isds_error err = IE_SUCCESS;
9964 #if HAVE_LIBCURL
9965 xmlDocPtr response = NULL;
9966 xmlChar *code = NULL, *status_message = NULL;
9967 xmlXPathContextPtr xpath_ctx = NULL;
9968 xmlXPathObjectPtr result = NULL;
9969 #endif
9971 if (!context) return IE_INVALID_CONTEXT;
9972 zfree(context->long_message);
9974 isds_hash_free(hash);
9976 #if HAVE_LIBCURL
9977 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9978 BAD_CAST "VerifyMessage", message_id,
9979 &response, NULL, NULL, &code, &status_message);
9980 if (err) goto leave;
9983 /* Extract data */
9984 xpath_ctx = xmlXPathNewContext(response);
9985 if (!xpath_ctx) {
9986 err = IE_ERROR;
9987 goto leave;
9989 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9990 err = IE_ERROR;
9991 goto leave;
9993 result = xmlXPathEvalExpression(
9994 BAD_CAST "/isds:VerifyMessageResponse",
9995 xpath_ctx);
9996 if (!result) {
9997 err = IE_ERROR;
9998 goto leave;
10000 /* Empty response */
10001 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10002 char *message_id_locale = _isds_utf82locale((char*) message_id);
10003 isds_printf_message(context,
10004 _("Server did not return any response for ID `%s' "
10005 "on VerifyMessage request"), message_id_locale);
10006 free(message_id_locale);
10007 err = IE_ISDS;
10008 goto leave;
10010 /* More responses */
10011 if (result->nodesetval->nodeNr > 1) {
10012 char *message_id_locale = _isds_utf82locale((char*) message_id);
10013 isds_printf_message(context,
10014 _("Server did return more responses for ID `%s' "
10015 "on VerifyMessage request"), message_id_locale);
10016 free(message_id_locale);
10017 err = IE_ISDS;
10018 goto leave;
10020 /* One response */
10021 xpath_ctx->node = result->nodesetval->nodeTab[0];
10023 /* Extract the hash */
10024 err = find_and_extract_DmHash(context, hash, xpath_ctx);
10026 leave:
10027 if (err) {
10028 isds_hash_free(hash);
10031 xmlXPathFreeObject(result);
10032 xmlXPathFreeContext(xpath_ctx);
10034 free(code);
10035 free(status_message);
10036 xmlFreeDoc(response);
10038 if (!err)
10039 isds_log(ILF_ISDS, ILL_DEBUG,
10040 _("VerifyMessage request processed by server "
10041 "successfully.\n")
10043 #else /* not HAVE_LIBCURL */
10044 err = IE_NOTSUP;
10045 #endif
10046 return err;
10050 /* Erase message specified by @message_id from long term storage. Other
10051 * message cannot be erased on user request.
10052 * @context is session context
10053 * @message_id is message identifier.
10054 * @incoming is true for incoming message, false for outgoing message.
10055 * @return
10056 * IE_SUCCESS if message has ben removed
10057 * IE_INVAL if message does not exist in long term storage or message
10058 * belongs to different box
10059 * TODO: IE_NOEPRM if user has no permission to erase a message */
10060 isds_error isds_delete_message_from_storage(struct isds_ctx *context,
10061 const char *message_id, _Bool incoming) {
10062 isds_error err = IE_SUCCESS;
10063 #if HAVE_LIBCURL
10064 xmlNodePtr request = NULL, node;
10065 xmlNsPtr isds_ns = NULL;
10066 xmlDocPtr response = NULL;
10067 xmlChar *code = NULL, *status_message = NULL;
10068 #endif
10070 if (!context) return IE_INVALID_CONTEXT;
10071 zfree(context->long_message);
10072 if (NULL == message_id) return IE_INVAL;
10074 /* Check if connection is established
10075 * TODO: This check should be done downstairs. */
10076 if (!context->curl) return IE_CONNECTION_CLOSED;
10078 #if HAVE_LIBCURL
10079 /* Build request */
10080 request = xmlNewNode(NULL, BAD_CAST "EraseMessage");
10081 if (!request) {
10082 isds_log_message(context,
10083 _("Could build EraseMessage request"));
10084 return IE_ERROR;
10086 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
10087 if(!isds_ns) {
10088 isds_log_message(context, _("Could not create ISDS name space"));
10089 xmlFreeNode(request);
10090 return IE_ERROR;
10092 xmlSetNs(request, isds_ns);
10094 err = validate_message_id_length(context, (xmlChar *) message_id);
10095 if (err) goto leave;
10096 INSERT_STRING(request, "dmID", message_id);
10098 INSERT_SCALAR_BOOLEAN(request, "dmIncoming", incoming);
10101 /* Send request */
10102 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending EraseMessage request for "
10103 "message ID %s to ISDS\n"), message_id);
10104 err = isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
10105 xmlFreeNode(request); request = NULL;
10107 if (err) {
10108 isds_log(ILF_ISDS, ILL_DEBUG,
10109 _("Processing ISDS response on EraseMessage request "
10110 "failed\n"));
10111 goto leave;
10114 /* Check for response status */
10115 err = isds_response_status(context, SERVICE_DM_INFO, response,
10116 &code, &status_message, NULL);
10117 if (err) {
10118 isds_log(ILF_ISDS, ILL_DEBUG,
10119 _("ISDS response on EraseMessage request is missing "
10120 "status\n"));
10121 goto leave;
10124 /* Check server status code */
10125 if (!xmlStrcmp(code, BAD_CAST "1211")) {
10126 isds_log_message(context, _("Message to erase belongs to other box"));
10127 err = IE_INVAL;
10128 } else if (!xmlStrcmp(code, BAD_CAST "1219")) {
10129 isds_log_message(context, _("Message to erase is not saved in "
10130 "long term storage or the direction does not match"));
10131 err = IE_INVAL;
10132 } else if (xmlStrcmp(code, BAD_CAST "0000")) {
10133 char *code_locale = _isds_utf82locale((char*) code);
10134 char *message_locale = _isds_utf82locale((char*) status_message);
10135 isds_log(ILF_ISDS, ILL_DEBUG,
10136 _("Server refused EraseMessage request "
10137 "(code=%s, message=%s)\n"),
10138 code_locale, message_locale);
10139 isds_log_message(context, message_locale);
10140 free(code_locale);
10141 free(message_locale);
10142 err = IE_ISDS;
10143 goto leave;
10146 leave:
10147 free(code);
10148 free(status_message);
10149 xmlFreeDoc(response);
10150 xmlFreeNode(request);
10152 if (!err)
10153 isds_log(ILF_ISDS, ILL_DEBUG,
10154 _("EraseMessage request processed by server "
10155 "successfully.\n")
10157 #else /* not HAVE_LIBCURL */
10158 err = IE_NOTSUP;
10159 #endif
10160 return err;
10164 /* Mark message as read. This is a transactional commit function to acknowledge
10165 * to ISDS the message has been downloaded and processed by client properly.
10166 * @context is session context
10167 * @message_id is message identifier. */
10168 isds_error isds_mark_message_read(struct isds_ctx *context,
10169 const char *message_id) {
10171 isds_error err = IE_SUCCESS;
10172 #if HAVE_LIBCURL
10173 xmlDocPtr response = NULL;
10174 xmlChar *code = NULL, *status_message = NULL;
10175 #endif
10177 if (!context) return IE_INVALID_CONTEXT;
10178 zfree(context->long_message);
10180 #if HAVE_LIBCURL
10181 /* Do request and check for success */
10182 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10183 BAD_CAST "MarkMessageAsDownloaded", message_id,
10184 &response, NULL, NULL, &code, &status_message);
10186 free(code);
10187 free(status_message);
10188 xmlFreeDoc(response);
10190 if (!err)
10191 isds_log(ILF_ISDS, ILL_DEBUG,
10192 _("MarkMessageAsDownloaded request processed by server "
10193 "successfully.\n")
10195 #else /* not HAVE_LIBCURL */
10196 err = IE_NOTSUP;
10197 #endif
10198 return err;
10202 /* Mark message as received by recipient. This is applicable only to
10203 * commercial message. Use envelope->dmType message member to distinguish
10204 * commercial message from government message. Government message is
10205 * received automatically (by law), commercial message on recipient request.
10206 * @context is session context
10207 * @message_id is message identifier. */
10208 isds_error isds_mark_message_received(struct isds_ctx *context,
10209 const char *message_id) {
10211 isds_error err = IE_SUCCESS;
10212 #if HAVE_LIBCURL
10213 xmlDocPtr response = NULL;
10214 xmlChar *code = NULL, *status_message = NULL;
10215 #endif
10217 if (!context) return IE_INVALID_CONTEXT;
10218 zfree(context->long_message);
10220 #if HAVE_LIBCURL
10221 /* Do request and check for success */
10222 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10223 BAD_CAST "ConfirmDelivery", message_id,
10224 &response, NULL, NULL, &code, &status_message);
10226 free(code);
10227 free(status_message);
10228 xmlFreeDoc(response);
10230 if (!err)
10231 isds_log(ILF_ISDS, ILL_DEBUG,
10232 _("ConfirmDelivery request processed by server "
10233 "successfully.\n")
10235 #else /* not HAVE_LIBCURL */
10236 err = IE_NOTSUP;
10237 #endif
10238 return err;
10242 /* Send document for authorized conversion into Czech POINT system.
10243 * This is public anonymous service, no log-in necessary. Special context is
10244 * used to reuse keep-a-live HTTPS connection.
10245 * @context is Czech POINT session context. DO NOT use context connected to
10246 * ISDS server. Use new context or context used by this function previously.
10247 * @document is document to convert. Only data, data_length, dmFileDescr and
10248 * is_xml members are significant. Be ware that not all document formats can be
10249 * converted (signed PDF 1.3 and higher only (2010-02 state)).
10250 * @id is reallocated identifier assigned by Czech POINT system to
10251 * your document on submit. Use is to tell it to Czech POINT officer.
10252 * @date is reallocated document submit date (submitted documents
10253 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
10254 * value. */
10255 isds_error czp_convert_document(struct isds_ctx *context,
10256 const struct isds_document *document,
10257 char **id, struct tm **date) {
10258 isds_error err = IE_SUCCESS;
10259 #if HAVE_LIBCURL
10260 xmlNsPtr deposit_ns = NULL, empty_ns = NULL;
10261 xmlNodePtr request = NULL, node;
10262 xmlDocPtr response = NULL;
10264 xmlXPathContextPtr xpath_ctx = NULL;
10265 xmlXPathObjectPtr result = NULL;
10266 long int status = -1;
10267 long int *status_ptr = &status;
10268 char *string = NULL;
10269 #endif
10272 if (!context) return IE_INVALID_CONTEXT;
10273 zfree(context->long_message);
10274 if (!document || !id || !date) return IE_INVAL;
10276 if (document->is_xml) {
10277 isds_log_message(context,
10278 _("XML documents cannot be submitted to conversion"));
10279 return IE_NOTSUP;
10282 /* Free output arguments */
10283 zfree(*id);
10284 zfree(*date);
10286 #if HAVE_LIBCURL
10287 /* Store configuration */
10288 context->type = CTX_TYPE_CZP;
10289 free(context->url);
10290 context->url = strdup("https://www.czechpoint.cz/uschovna/services.php");
10291 if (!(context->url))
10292 return IE_NOMEM;
10294 /* Prepare CURL handle if not yet connected */
10295 if (!context->curl) {
10296 context->curl = curl_easy_init();
10297 if (!(context->curl))
10298 return IE_ERROR;
10301 /* Build conversion request */
10302 request = xmlNewNode(NULL, BAD_CAST "saveDocument");
10303 if (!request) {
10304 isds_log_message(context,
10305 _("Could not build Czech POINT conversion request"));
10306 return IE_ERROR;
10308 deposit_ns = xmlNewNs(request, BAD_CAST DEPOSIT_NS, BAD_CAST "dep");
10309 if(!deposit_ns) {
10310 isds_log_message(context,
10311 _("Could not create Czech POINT deposit name space"));
10312 xmlFreeNode(request);
10313 return IE_ERROR;
10315 xmlSetNs(request, deposit_ns);
10317 /* Insert children. They are in empty namespace! */
10318 empty_ns = xmlNewNs(request, BAD_CAST "", NULL);
10319 if(!empty_ns) {
10320 isds_log_message(context, _("Could not create empty name space"));
10321 err = IE_ERROR;
10322 goto leave;
10324 INSERT_STRING_WITH_NS(request, empty_ns, "conversionID", "0");
10325 INSERT_STRING_WITH_NS(request, empty_ns, "fileName",
10326 document->dmFileDescr);
10328 /* Document encoded in Base64 */
10329 err = insert_base64_encoded_string(context, request, empty_ns, "document",
10330 document->data, document->data_length);
10331 if (err) goto leave;
10333 isds_log(ILF_ISDS, ILL_DEBUG,
10334 _("Submitting document for conversion into Czech POINT deposit"));
10336 /* Send conversion request */
10337 err = _czp_czpdeposit(context, request, &response);
10338 xmlFreeNode(request); request = NULL;
10340 if (err) {
10341 czp_do_close_connection(context);
10342 goto leave;
10346 /* Extract response */
10347 xpath_ctx = xmlXPathNewContext(response);
10348 if (!xpath_ctx) {
10349 err = IE_ERROR;
10350 goto leave;
10352 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10353 err = IE_ERROR;
10354 goto leave;
10356 result = xmlXPathEvalExpression(
10357 BAD_CAST "/deposit:saveDocumentResponse/return",
10358 xpath_ctx);
10359 if (!result) {
10360 err = IE_ERROR;
10361 goto leave;
10363 /* Empty response */
10364 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10365 isds_printf_message(context,
10366 _("Missing `return' element in Czech POINT deposit response"));
10367 err = IE_ISDS;
10368 goto leave;
10370 /* More responses */
10371 if (result->nodesetval->nodeNr > 1) {
10372 isds_printf_message(context,
10373 _("Multiple `return' element in Czech POINT deposit response"));
10374 err = IE_ISDS;
10375 goto leave;
10377 /* One response */
10378 xpath_ctx->node = result->nodesetval->nodeTab[0];
10380 /* Get status */
10381 EXTRACT_LONGINT("status", status_ptr, 1);
10382 if (status) {
10383 EXTRACT_STRING("statusMsg", string);
10384 char *string_locale = _isds_utf82locale(string);
10385 isds_printf_message(context,
10386 _("Czech POINT deposit refused document for conversion "
10387 "(code=%ld, message=%s)"),
10388 status, string_locale);
10389 free(string_locale);
10390 err = IE_ISDS;
10391 goto leave;
10394 /* Get document ID */
10395 EXTRACT_STRING("documentID", *id);
10397 /* Get submit date */
10398 EXTRACT_STRING("dateInserted", string);
10399 if (string) {
10400 *date = calloc(1, sizeof(**date));
10401 if (!*date) {
10402 err = IE_NOMEM;
10403 goto leave;
10405 err = datestring2tm((xmlChar *)string, *date);
10406 if (err) {
10407 if (err == IE_NOTSUP) {
10408 err = IE_ISDS;
10409 char *string_locale = _isds_utf82locale(string);
10410 isds_printf_message(context,
10411 _("Invalid dateInserted value: %s"), string_locale);
10412 free(string_locale);
10414 goto leave;
10418 leave:
10419 free(string);
10420 xmlXPathFreeObject(result);
10421 xmlXPathFreeContext(xpath_ctx);
10423 xmlFreeDoc(response);
10424 xmlFreeNode(request);
10426 if (!err) {
10427 char *id_locale = _isds_utf82locale((char *) *id);
10428 isds_log(ILF_ISDS, ILL_DEBUG,
10429 _("Document %s has been submitted for conversion "
10430 "to server successfully\n"), id_locale);
10431 free(id_locale);
10433 #else /* not HAVE_LIBCURL */
10434 err = IE_NOTSUP;
10435 #endif
10436 return err;
10440 /* Close possibly opened connection to Czech POINT document deposit.
10441 * @context is Czech POINT session context. */
10442 isds_error czp_close_connection(struct isds_ctx *context) {
10443 if (!context) return IE_INVALID_CONTEXT;
10444 zfree(context->long_message);
10445 #if HAVE_LIBCURL
10446 return czp_do_close_connection(context);
10447 #else
10448 return IE_NOTSUP;
10449 #endif
10453 /* Send request for new box creation in testing ISDS instance.
10454 * It's not possible to request for a production box currently, as it
10455 * communicates via e-mail.
10456 * XXX: This function does not work either. Server complains about invalid
10457 * e-mail address.
10458 * XXX: Remove context->type hacks in isds.c and validator.c when removing
10459 * this function
10460 * @context is special session context for box creation request. DO NOT use
10461 * standard context as it could reveal your password. Use fresh new context or
10462 * context previously used by this function.
10463 * @box is box description to create including single primary user (in case of
10464 * FO box type). It outputs box ID assigned by ISDS in dbID element.
10465 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
10466 * box, or contact address of PFO box owner). The email member is mandatory as
10467 * it will be used to deliver credentials.
10468 * @former_names is former name of box owner. Pass NULL if you don't care.
10469 * @approval is optional external approval of box manipulation
10470 * @refnumber is reallocated serial number of request assigned by ISDS. Use
10471 * NULL, if you don't care.*/
10472 isds_error isds_request_new_testing_box(struct isds_ctx *context,
10473 struct isds_DbOwnerInfo *box, const struct isds_list *users,
10474 const char *former_names, const struct isds_approval *approval,
10475 char **refnumber) {
10476 isds_error err = IE_SUCCESS;
10477 #if HAVE_LIBCURL
10478 xmlNodePtr request = NULL;
10479 xmlDocPtr response = NULL;
10480 xmlXPathContextPtr xpath_ctx = NULL;
10481 xmlXPathObjectPtr result = NULL;
10482 #endif
10485 if (!context) return IE_INVALID_CONTEXT;
10486 zfree(context->long_message);
10487 if (!box) return IE_INVAL;
10489 #if HAVE_LIBCURL
10490 if (!box->email || box->email[0] == '\0') {
10491 isds_log_message(context, _("E-mail field is mandatory"));
10492 return IE_INVAL;
10495 /* Scratch box ID */
10496 zfree(box->dbID);
10498 /* Store configuration */
10499 context->type = CTX_TYPE_TESTING_REQUEST_COLLECTOR;
10500 free(context->url);
10501 context->url = strdup("http://78.102.19.203/testbox/request_box.php");
10502 if (!(context->url))
10503 return IE_NOMEM;
10505 /* Prepare CURL handle if not yet connected */
10506 if (!context->curl) {
10507 context->curl = curl_easy_init();
10508 if (!(context->curl))
10509 return IE_ERROR;
10512 /* Build CreateDataBox request */
10513 err = build_CreateDBInput_request(context,
10514 &request, BAD_CAST "CreateDataBox",
10515 box, users, (xmlChar *) former_names, NULL, NULL, NULL, approval);
10516 if (err) goto leave;
10518 /* Send it to server and process response */
10519 err = send_destroy_request_check_response(context,
10520 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
10521 &response, (xmlChar **) refnumber);
10522 if (err) goto leave;
10524 /* Extract box ID */
10525 xpath_ctx = xmlXPathNewContext(response);
10526 if (!xpath_ctx) {
10527 err = IE_ERROR;
10528 goto leave;
10530 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10531 err = IE_ERROR;
10532 goto leave;
10534 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
10536 leave:
10537 xmlXPathFreeObject(result);
10538 xmlXPathFreeContext(xpath_ctx);
10539 xmlFreeDoc(response);
10540 xmlFreeNode(request);
10542 if (!err) {
10543 isds_log(ILF_ISDS, ILL_DEBUG,
10544 _("CreateDataBox request processed by server successfully.\n"));
10546 #else /* not HAVE_LIBCURL */
10547 err = IE_NOTSUP;
10548 #endif
10550 return err;
10554 /* Submit CMS signed message to ISDS to verify its originality. This is
10555 * stronger form of isds_verify_message_hash() because ISDS does more checks
10556 * than simple one (potentialy old weak) hash comparison.
10557 * @context is session context
10558 * @message is memory with raw CMS signed message bit stream
10559 * @length is @message size in bytes
10560 * @return
10561 * IE_SUCCESS if message originates in ISDS
10562 * IE_NOTEQUAL if message is unknown to ISDS
10563 * other code for other errors */
10564 isds_error isds_authenticate_message(struct isds_ctx *context,
10565 const void *message, size_t length) {
10566 isds_error err = IE_SUCCESS;
10567 #if HAVE_LIBCURL
10568 xmlNsPtr isds_ns = NULL;
10569 xmlNodePtr request = NULL;
10570 xmlDocPtr response = NULL;
10571 xmlXPathContextPtr xpath_ctx = NULL;
10572 xmlXPathObjectPtr result = NULL;
10573 _Bool *authentic = NULL;
10574 #endif
10576 if (!context) return IE_INVALID_CONTEXT;
10577 zfree(context->long_message);
10578 if (!message || length == 0) return IE_INVAL;
10580 #if HAVE_LIBCURL
10581 /* Check if connection is established
10582 * TODO: This check should be done downstairs. */
10583 if (!context->curl) return IE_CONNECTION_CLOSED;
10586 /* Build AuthenticateMessage request */
10587 request = xmlNewNode(NULL, BAD_CAST "AuthenticateMessage");
10588 if (!request) {
10589 isds_log_message(context,
10590 _("Could not build AuthenticateMessage request"));
10591 return IE_ERROR;
10593 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
10594 if(!isds_ns) {
10595 isds_log_message(context, _("Could not create ISDS name space"));
10596 xmlFreeNode(request);
10597 return IE_ERROR;
10599 xmlSetNs(request, isds_ns);
10601 /* Insert Base64 encoded message */
10602 err = insert_base64_encoded_string(context, request, NULL, "dmMessage",
10603 message, length);
10604 if (err) goto leave;
10606 /* Send request to server and process response */
10607 err = send_destroy_request_check_response(context,
10608 SERVICE_DM_OPERATIONS, BAD_CAST "AuthenticateMessage", &request,
10609 &response, NULL);
10610 if (err) goto leave;
10613 /* ISDS has decided */
10614 xpath_ctx = xmlXPathNewContext(response);
10615 if (!xpath_ctx) {
10616 err = IE_ERROR;
10617 goto leave;
10619 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10620 err = IE_ERROR;
10621 goto leave;
10624 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic);
10626 if (!authentic) {
10627 isds_log_message(context,
10628 _("Server did not return any response on "
10629 "AuthenticateMessage request"));
10630 err = IE_ISDS;
10631 goto leave;
10633 if (*authentic) {
10634 isds_log(ILF_ISDS, ILL_DEBUG,
10635 _("ISDS authenticated the message successfully\n"));
10636 } else {
10637 isds_log_message(context, _("ISDS does not know the message"));
10638 err = IE_NOTEQUAL;
10642 leave:
10643 free(authentic);
10644 xmlXPathFreeObject(result);
10645 xmlXPathFreeContext(xpath_ctx);
10647 xmlFreeDoc(response);
10648 xmlFreeNode(request);
10649 #else /* not HAVE_LIBCURL */
10650 err = IE_NOTSUP;
10651 #endif
10653 return err;
10656 #undef INSERT_ELEMENT
10657 #undef CHECK_FOR_STRING_LENGTH
10658 #undef INSERT_STRING_ATTRIBUTE
10659 #undef INSERT_ULONGINTNOPTR
10660 #undef INSERT_ULONGINT
10661 #undef INSERT_LONGINT
10662 #undef INSERT_BOOLEAN
10663 #undef INSERT_SCALAR_BOOLEAN
10664 #undef INSERT_STRING
10665 #undef INSERT_STRING_WITH_NS
10666 #undef EXTRACT_STRING_ATTRIBUTE
10667 #undef EXTRACT_ULONGINT
10668 #undef EXTRACT_LONGINT
10669 #undef EXTRACT_BOOLEAN
10670 #undef EXTRACT_STRING
10673 /* Compute hash of message from raw representation and store it into envelope.
10674 * Original hash structure will be destroyed in envelope.
10675 * @context is session context
10676 * @message is message carrying raw XML message blob
10677 * @algorithm is desired hash algorithm to use */
10678 isds_error isds_compute_message_hash(struct isds_ctx *context,
10679 struct isds_message *message, const isds_hash_algorithm algorithm) {
10680 isds_error err = IE_SUCCESS;
10681 const char *nsuri;
10682 void *xml_stream = NULL;
10683 size_t xml_stream_length;
10684 size_t phys_start, phys_end;
10685 char *phys_path = NULL;
10686 struct isds_hash *new_hash = NULL;
10689 if (!context) return IE_INVALID_CONTEXT;
10690 zfree(context->long_message);
10691 if (!message) return IE_INVAL;
10693 if (!message->raw) {
10694 isds_log_message(context,
10695 _("Message does not carry raw representation"));
10696 return IE_INVAL;
10699 switch (message->raw_type) {
10700 case RAWTYPE_INCOMING_MESSAGE:
10701 nsuri = ISDS_NS;
10702 xml_stream = message->raw;
10703 xml_stream_length = message->raw_length;
10704 break;
10706 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
10707 nsuri = SISDS_INCOMING_NS;
10708 xml_stream = message->raw;
10709 xml_stream_length = message->raw_length;
10710 break;
10712 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
10713 nsuri = SISDS_INCOMING_NS;
10714 err = _isds_extract_cms_data(context,
10715 message->raw, message->raw_length,
10716 &xml_stream, &xml_stream_length);
10717 if (err) goto leave;
10718 break;
10720 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
10721 nsuri = SISDS_OUTGOING_NS;
10722 xml_stream = message->raw;
10723 xml_stream_length = message->raw_length;
10724 break;
10726 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
10727 nsuri = SISDS_OUTGOING_NS;
10728 err = _isds_extract_cms_data(context,
10729 message->raw, message->raw_length,
10730 &xml_stream, &xml_stream_length);
10731 if (err) goto leave;
10732 break;
10734 default:
10735 isds_log_message(context, _("Bad raw representation type"));
10736 return IE_INVAL;
10737 break;
10741 /* XXX: Hash is computed from original string representing isds:dmDm
10742 * subtree. That means no encoding, white space, xmlns attributes changes.
10743 * In other words, input for hash can be invalid XML stream. */
10744 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
10745 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
10746 PHYSXML_ELEMENT_SEPARATOR,
10747 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
10748 PHYSXML_ELEMENT_SEPARATOR
10749 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
10750 err = IE_NOMEM;
10751 goto leave;
10753 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
10754 phys_path, &phys_start, &phys_end);
10755 zfree(phys_path);
10756 if (err) {
10757 isds_log_message(context,
10758 _("Substring with isds:dmDM element could not be located "
10759 "in raw message"));
10760 goto leave;
10764 /* Compute hash */
10765 new_hash = calloc(1, sizeof(*new_hash));
10766 if (!new_hash) {
10767 err = IE_NOMEM;
10768 goto leave;
10770 new_hash->algorithm = algorithm;
10771 err = _isds_compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
10772 new_hash);
10773 if (err) {
10774 isds_log_message(context, _("Could not compute message hash"));
10775 goto leave;
10778 /* Save computed hash */
10779 if (!message->envelope) {
10780 message->envelope = calloc(1, sizeof(*message->envelope));
10781 if (!message->envelope) {
10782 err = IE_NOMEM;
10783 goto leave;
10786 isds_hash_free(&message->envelope->hash);
10787 message->envelope->hash = new_hash;
10789 leave:
10790 if (err) {
10791 isds_hash_free(&new_hash);
10794 free(phys_path);
10795 if (xml_stream != message->raw) free(xml_stream);
10796 return err;
10800 /* Compare two hashes.
10801 * @h1 is first hash
10802 * @h2 is another hash
10803 * @return
10804 * IE_SUCCESS if hashes equal
10805 * IE_NOTUNIQ if hashes are comparable, but they don't equal
10806 * IE_ENUM if not comparable, but both structures defined
10807 * IE_INVAL if some of the structures are undefined (NULL)
10808 * IE_ERROR if internal error occurs */
10809 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
10810 if (h1 == NULL || h2 == NULL) return IE_INVAL;
10811 if (h1->algorithm != h2->algorithm) return IE_ENUM;
10812 if (h1->length != h2->length) return IE_ERROR;
10813 if (h1->length > 0 && !h1->value) return IE_ERROR;
10814 if (h2->length > 0 && !h2->value) return IE_ERROR;
10816 for (int i = 0; i < h1->length; i++) {
10817 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
10818 return IE_NOTEQUAL;
10820 return IE_SUCCESS;
10824 /* Check message has gone through ISDS by comparing message hash stored in
10825 * ISDS and locally computed hash. You must provide message with valid raw
10826 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
10827 * This is convenient wrapper for isds_download_message_hash(),
10828 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
10829 * @context is session context
10830 * @message is message with valid raw and envelope member; envelope->hash
10831 * member will be changed during function run. Use envelope on heap only.
10832 * @return
10833 * IE_SUCCESS if message originates in ISDS
10834 * IE_NOTEQUAL if message is unknown to ISDS
10835 * other code for other errors */
10836 isds_error isds_verify_message_hash(struct isds_ctx *context,
10837 struct isds_message *message) {
10838 isds_error err = IE_SUCCESS;
10839 struct isds_hash *downloaded_hash = NULL;
10841 if (!context) return IE_INVALID_CONTEXT;
10842 zfree(context->long_message);
10843 if (!message) return IE_INVAL;
10845 if (!message->envelope) {
10846 isds_log_message(context,
10847 _("Given message structure is missing envelope"));
10848 return IE_INVAL;
10850 if (!message->raw) {
10851 isds_log_message(context,
10852 _("Given message structure is missing raw representation"));
10853 return IE_INVAL;
10856 err = isds_download_message_hash(context, message->envelope->dmID,
10857 &downloaded_hash);
10858 if (err) goto leave;
10860 err = isds_compute_message_hash(context, message,
10861 downloaded_hash->algorithm);
10862 if (err) goto leave;
10864 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
10866 leave:
10867 isds_hash_free(&downloaded_hash);
10868 return err;
10872 /* Search for document by document ID in list of documents. IDs are compared
10873 * as UTF-8 string.
10874 * @documents is list of isds_documents
10875 * @id is document identifier
10876 * @return first matching document or NULL. */
10877 const struct isds_document *isds_find_document_by_id(
10878 const struct isds_list *documents, const char *id) {
10879 const struct isds_list *item;
10880 const struct isds_document *document;
10882 for (item = documents; item; item = item->next) {
10883 document = (struct isds_document *) item->data;
10884 if (!document) continue;
10886 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
10887 return document;
10890 return NULL;
10894 /* Normalize @mime_type to be proper MIME type.
10895 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
10896 * guess regular MIME type (e.g. "application/pdf").
10897 * @mime_type is UTF-8 encoded MIME type to fix
10898 * @return original @mime_type if no better interpretation exists, or
10899 * constant static UTF-8 encoded string with proper MIME type. */
10900 const char *isds_normalize_mime_type(const char *mime_type) {
10901 if (!mime_type) return NULL;
10903 for (int offset = 0;
10904 offset < sizeof(extension_map_mime)/sizeof(extension_map_mime[0]);
10905 offset += 2) {
10906 if (!xmlStrcmp((const xmlChar*) mime_type, extension_map_mime[offset]))
10907 return (const char *) extension_map_mime[offset + 1];
10910 return mime_type;
10914 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
10915 struct isds_message **message);
10916 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
10917 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
10918 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
10919 struct isds_address **address);
10921 int isds_message_free(struct isds_message **message);
10922 int isds_address_free(struct isds_address **address);
10926 /* Makes known all relevant namespaces to given XPath context
10927 * @xpath_ctx is XPath context
10928 * @message_ns selects proper message name space. Unsigned and signed
10929 * messages and delivery info's differ in prefix and URI. */
10930 _hidden isds_error _isds_register_namespaces(xmlXPathContextPtr xpath_ctx,
10931 const message_ns_type message_ns) {
10932 const xmlChar *message_namespace = NULL;
10934 if (!xpath_ctx) return IE_ERROR;
10936 switch(message_ns) {
10937 case MESSAGE_NS_1:
10938 message_namespace = BAD_CAST ISDS1_NS; break;
10939 case MESSAGE_NS_UNSIGNED:
10940 message_namespace = BAD_CAST ISDS_NS; break;
10941 case MESSAGE_NS_SIGNED_INCOMING:
10942 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
10943 case MESSAGE_NS_SIGNED_OUTGOING:
10944 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
10945 case MESSAGE_NS_SIGNED_DELIVERY:
10946 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
10947 default:
10948 return IE_ENUM;
10951 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
10952 return IE_ERROR;
10953 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
10954 return IE_ERROR;
10955 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "oisds", BAD_CAST OISDS_NS))
10956 return IE_ERROR;
10957 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
10958 return IE_ERROR;
10959 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
10960 return IE_ERROR;
10961 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
10962 return IE_ERROR;
10963 return IE_SUCCESS;