Rename private isds() function to _isds()
[libisds.git] / src / isds.c
blob28ebbc9df43ade4a753ee105dc035d792b18f9a8
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 "physxml.h"
16 #include "system.h"
18 /* Global variables.
19 * Allocated in isds_init() and deallocated in isds_cleanup(). */
20 unsigned int log_facilities;
21 isds_log_level log_level;
22 isds_log_callback log_callback;
23 void *log_callback_data;
24 const char *version_gpgme = N_("n/a");
25 const char *version_gcrypt = N_("n/a");
26 const char *version_openssl = N_("n/a");
27 const char *version_expat = N_("n/a");
29 /* Locators */
30 /* Base URL of production ISDS instance */
31 const char isds_locator[] = "https://ws1.mojedatovaschranka.cz/";
32 const char isds_cert_locator[] = "https://ws1c.mojedatovaschranka.cz/";
33 const char isds_otp_locator[] = "https://www.mojedatovaschranka.cz/";
35 /* Base URL of production ISDS instance */
36 const char isds_testing_locator[] = "https://ws1.czebox.cz/";
37 const char isds_cert_testing_locator[] = "https://ws1c.czebox.cz/";
38 const char isds_otp_testing_locator[] = "https://www.czebox.cz/";
40 /* Extension to MIME type map */
41 static const xmlChar *extension_map_mime[] = {
42 BAD_CAST "cer", BAD_CAST "application/x-x509-ca-cert",
43 BAD_CAST "crt", BAD_CAST "application/x-x509-ca-cert",
44 BAD_CAST "der", BAD_CAST "application/x-x509-ca-cert",
45 BAD_CAST "doc", BAD_CAST "application/msword",
46 BAD_CAST "docx", BAD_CAST "application/vnd.openxmlformats-officedocument."
47 "wordprocessingml.document",
48 BAD_CAST "dbf", BAD_CAST "application/octet-stream",
49 BAD_CAST "prj", BAD_CAST "application/octet-stream",
50 BAD_CAST "qix", BAD_CAST "application/octet-stream",
51 BAD_CAST "sbn", BAD_CAST "application/octet-stream",
52 BAD_CAST "sbx", BAD_CAST "application/octet-stream",
53 BAD_CAST "shp", BAD_CAST "application/octet-stream",
54 BAD_CAST "shx", BAD_CAST "application/octet-stream",
55 BAD_CAST "dgn", BAD_CAST "application/octet-stream",
56 BAD_CAST "dwg", BAD_CAST "image/vnd.dwg",
57 BAD_CAST "edi", BAD_CAST "application/edifact",
58 BAD_CAST "fo", BAD_CAST "application/vnd.software602.filler.form+xml",
59 BAD_CAST "gfs", BAD_CAST "application/xml",
60 BAD_CAST "gml", BAD_CAST "application/xml",
61 BAD_CAST "gif", BAD_CAST "image/gif",
62 BAD_CAST "htm", BAD_CAST "text/html",
63 BAD_CAST "html", BAD_CAST "text/html",
64 BAD_CAST "isdoc", BAD_CAST "text/isdoc",
65 BAD_CAST "isdocx", BAD_CAST "text/isdocx",
66 BAD_CAST "jfif", BAD_CAST "image/jpeg",
67 BAD_CAST "jpg", BAD_CAST "image/jpeg",
68 BAD_CAST "jpeg", BAD_CAST "image/jpeg",
69 BAD_CAST "mpeg", BAD_CAST "video/mpeg",
70 BAD_CAST "mpeg1", BAD_CAST "video/mpeg",
71 BAD_CAST "mpeg2", BAD_CAST "video/mpeg",
72 BAD_CAST "mpg", BAD_CAST "video/mpeg",
73 BAD_CAST "mp2", BAD_CAST "audio/mpeg",
74 BAD_CAST "mp3", BAD_CAST "audio/mpeg",
75 BAD_CAST "odp", BAD_CAST "application/vnd.oasis.opendocument.presentation",
76 BAD_CAST "ods", BAD_CAST "application/vnd.oasis.opendocument.spreadsheet",
77 BAD_CAST "odt", BAD_CAST "application/vnd.oasis.opendocument.text",
78 BAD_CAST "pdf", BAD_CAST "application/pdf",
79 BAD_CAST "p7b", BAD_CAST "application/pkcs7-certificates",
80 BAD_CAST "p7c", BAD_CAST "application/pkcs7-mime",
81 BAD_CAST "p7m", BAD_CAST "application/pkcs7-mime",
82 BAD_CAST "p7f", BAD_CAST "application/pkcs7-signature",
83 BAD_CAST "p7s", BAD_CAST "application/pkcs7-signature",
84 BAD_CAST "pk7", BAD_CAST "application/pkcs7-mime",
85 BAD_CAST "png", BAD_CAST "image/png",
86 BAD_CAST "ppt", BAD_CAST "application/vnd.ms-powerpoint",
87 BAD_CAST "pptx", BAD_CAST "application/vnd.openxmlformats-officedocument."
88 "presentationml.presentation",
89 BAD_CAST "rtf", BAD_CAST "application/rtf",
90 BAD_CAST "tif", BAD_CAST "image/tiff",
91 BAD_CAST "tiff", BAD_CAST "image/tiff",
92 BAD_CAST "tsr", BAD_CAST "application/timestamp-reply",
93 BAD_CAST "tst", BAD_CAST "application/timestamp-reply",
94 BAD_CAST "txt", BAD_CAST "text/plain",
95 BAD_CAST "wav", BAD_CAST "audio/wav",
96 BAD_CAST "xls", BAD_CAST "application/vnd.ms-excel",
97 BAD_CAST "xlsx", BAD_CAST "application/vnd.openxmlformats-officedocument."
98 "spreadsheetml.sheet",
99 BAD_CAST "xml", BAD_CAST "application/xml",
100 BAD_CAST "xsd", BAD_CAST "application/xml",
101 BAD_CAST "zfo", BAD_CAST "application/vnd.software602.filler.form-xml-zip"
104 /* Structure type to hold conversion table from status code to isds_error and
105 * long message */
106 struct code_map_isds_error {
107 const xmlChar **codes; /* NULL terminated array of status codes */
108 const char **meanings; /* Mapping to non-localized long messages */
109 const isds_error *errors; /* Mapping to isds_error code */
112 /* Deallocate structure isds_pki_credentials and NULL it.
113 * Pass-phrase is discarded.
114 * @pki credentials to to free */
115 void isds_pki_credentials_free(struct isds_pki_credentials **pki) {
116 if(!pki || !*pki) return;
118 free((*pki)->engine);
119 free((*pki)->certificate);
120 free((*pki)->key);
122 if ((*pki)->passphrase) {
123 memset((*pki)->passphrase, 0, strlen((*pki)->passphrase));
124 free((*pki)->passphrase);
127 zfree((*pki));
131 /* Free isds_list with all member data.
132 * @list list to free, on return will be NULL */
133 void isds_list_free(struct isds_list **list) {
134 struct isds_list *item, *next_item;
136 if (!list || !*list) return;
138 for(item = *list; item; item = next_item) {
139 if (item->destructor) (item->destructor)(&(item->data));
140 next_item = item->next;
141 free(item);
144 *list = NULL;
148 /* Deallocate structure isds_hash and NULL it.
149 * @hash hash to to free */
150 void isds_hash_free(struct isds_hash **hash) {
151 if(!hash || !*hash) return;
152 free((*hash)->value);
153 zfree((*hash));
157 /* Deallocate structure isds_PersonName recursively and NULL it */
158 void isds_PersonName_free(struct isds_PersonName **person_name) {
159 if (!person_name || !*person_name) return;
161 free((*person_name)->pnFirstName);
162 free((*person_name)->pnMiddleName);
163 free((*person_name)->pnLastName);
164 free((*person_name)->pnLastNameAtBirth);
166 free(*person_name);
167 *person_name = NULL;
171 /* Deallocate structure isds_BirthInfo recursively and NULL it */
172 void isds_BirthInfo_free(struct isds_BirthInfo **birth_info) {
173 if (!birth_info || !*birth_info) return;
175 free((*birth_info)->biDate);
176 free((*birth_info)->biCity);
177 free((*birth_info)->biCounty);
178 free((*birth_info)->biState);
180 free(*birth_info);
181 *birth_info = NULL;
185 /* Deallocate structure isds_Address recursively and NULL it */
186 void isds_Address_free(struct isds_Address **address) {
187 if (!address || !*address) return;
189 free((*address)->adCity);
190 free((*address)->adStreet);
191 free((*address)->adNumberInStreet);
192 free((*address)->adNumberInMunicipality);
193 free((*address)->adZipCode);
194 free((*address)->adState);
196 free(*address);
197 *address = NULL;
201 /* Deallocate structure isds_DbOwnerInfo recursively and NULL it */
202 void isds_DbOwnerInfo_free(struct isds_DbOwnerInfo **db_owner_info) {
203 if (!db_owner_info || !*db_owner_info) return;
205 free((*db_owner_info)->dbID);
206 free((*db_owner_info)->dbType);
207 free((*db_owner_info)->ic);
208 isds_PersonName_free(&((*db_owner_info)->personName));
209 free((*db_owner_info)->firmName);
210 isds_BirthInfo_free(&((*db_owner_info)->birthInfo));
211 isds_Address_free(&((*db_owner_info)->address));
212 free((*db_owner_info)->nationality);
213 free((*db_owner_info)->email);
214 free((*db_owner_info)->telNumber);
215 free((*db_owner_info)->identifier);
216 free((*db_owner_info)->registryCode);
217 free((*db_owner_info)->dbState);
218 free((*db_owner_info)->dbEffectiveOVM);
219 free((*db_owner_info)->dbOpenAddressing);
221 free(*db_owner_info);
222 *db_owner_info = NULL;
225 /* Deallocate structure isds_DbUserInfo recursively and NULL it */
226 void isds_DbUserInfo_free(struct isds_DbUserInfo **db_user_info) {
227 if (!db_user_info || !*db_user_info) return;
229 free((*db_user_info)->userID);
230 free((*db_user_info)->userType);
231 free((*db_user_info)->userPrivils);
232 isds_PersonName_free(&((*db_user_info)->personName));
233 isds_Address_free(&((*db_user_info)->address));
234 free((*db_user_info)->biDate);
235 free((*db_user_info)->ic);
236 free((*db_user_info)->firmName);
237 free((*db_user_info)->caStreet);
238 free((*db_user_info)->caCity);
239 free((*db_user_info)->caZipCode);
240 free((*db_user_info)->caState);
242 zfree(*db_user_info);
246 /* Deallocate struct isds_event recursively and NULL it */
247 void isds_event_free(struct isds_event **event) {
248 if (!event || !*event) return;
250 free((*event)->time);
251 free((*event)->type);
252 free((*event)->description);
253 zfree(*event);
257 /* Deallocate struct isds_envelope recursively and NULL it */
258 void isds_envelope_free(struct isds_envelope **envelope) {
259 if (!envelope || !*envelope) return;
261 free((*envelope)->dmID);
262 free((*envelope)->dbIDSender);
263 free((*envelope)->dmSender);
264 free((*envelope)->dmSenderAddress);
265 free((*envelope)->dmSenderType);
266 free((*envelope)->dmRecipient);
267 free((*envelope)->dmRecipientAddress);
268 free((*envelope)->dmAmbiguousRecipient);
269 free((*envelope)->dmType);
271 free((*envelope)->dmOrdinal);
272 free((*envelope)->dmMessageStatus);
273 free((*envelope)->dmDeliveryTime);
274 free((*envelope)->dmAcceptanceTime);
275 isds_hash_free(&(*envelope)->hash);
276 free((*envelope)->timestamp);
277 isds_list_free(&(*envelope)->events);
279 free((*envelope)->dmSenderOrgUnit);
280 free((*envelope)->dmSenderOrgUnitNum);
281 free((*envelope)->dbIDRecipient);
282 free((*envelope)->dmRecipientOrgUnit);
283 free((*envelope)->dmRecipientOrgUnitNum);
284 free((*envelope)->dmToHands);
285 free((*envelope)->dmAnnotation);
286 free((*envelope)->dmRecipientRefNumber);
287 free((*envelope)->dmSenderRefNumber);
288 free((*envelope)->dmRecipientIdent);
289 free((*envelope)->dmSenderIdent);
291 free((*envelope)->dmLegalTitleLaw);
292 free((*envelope)->dmLegalTitleYear);
293 free((*envelope)->dmLegalTitleSect);
294 free((*envelope)->dmLegalTitlePar);
295 free((*envelope)->dmLegalTitlePoint);
297 free((*envelope)->dmPersonalDelivery);
298 free((*envelope)->dmAllowSubstDelivery);
300 free((*envelope)->dmOVM);
301 free((*envelope)->dmPublishOwnID);
303 free(*envelope);
304 *envelope = NULL;
308 /* Deallocate struct isds_message recursively and NULL it */
309 void isds_message_free(struct isds_message **message) {
310 if (!message || !*message) return;
312 free((*message)->raw);
313 isds_envelope_free(&((*message)->envelope));
314 isds_list_free(&((*message)->documents));
315 xmlFreeDoc((*message)->xml); (*message)->xml = NULL;
317 free(*message);
318 *message = NULL;
322 /* Deallocate struct isds_document recursively and NULL it */
323 void isds_document_free(struct isds_document **document) {
324 if (!document || !*document) return;
326 if (!(*document)->is_xml) {
327 free((*document)->data);
329 free((*document)->dmMimeType);
330 free((*document)->dmFileGuid);
331 free((*document)->dmUpFileGuid);
332 free((*document)->dmFileDescr);
333 free((*document)->dmFormat);
335 free(*document);
336 *document = NULL;
340 /* Deallocate struct isds_message_copy recursively and NULL it */
341 void isds_message_copy_free(struct isds_message_copy **copy) {
342 if (!copy || !*copy) return;
344 free((*copy)->dbIDRecipient);
345 free((*copy)->dmRecipientOrgUnit);
346 free((*copy)->dmRecipientOrgUnitNum);
347 free((*copy)->dmToHands);
349 free((*copy)->dmStatus);
350 free((*copy)->dmID);
352 zfree(*copy);
356 /* Deallocate struct isds_message_status_change recursively and NULL it */
357 void isds_message_status_change_free(
358 struct isds_message_status_change **message_status_change) {
359 if (!message_status_change || !*message_status_change) return;
361 free((*message_status_change)->dmID);
362 free((*message_status_change)->time);
363 free((*message_status_change)->dmMessageStatus);
365 zfree(*message_status_change);
369 /* Deallocate struct isds_approval recursively and NULL it */
370 void isds_approval_free(struct isds_approval **approval) {
371 if (!approval || !*approval) return;
373 free((*approval)->refference);
375 zfree(*approval);
379 /* Deallocate struct isds_credentials_delivery recursively and NULL it.
380 * The email string is deallocated too. */
381 void isds_credentials_delivery_free(
382 struct isds_credentials_delivery **credentials_delivery) {
383 if (!credentials_delivery || !*credentials_delivery) return;
385 free((*credentials_delivery)->email);
386 free((*credentials_delivery)->token);
387 free((*credentials_delivery)->new_user_name);
389 zfree(*credentials_delivery);
393 /* Deallocate struct isds_commercial_permission recursively and NULL it */
394 void isds_commercial_permission_free(
395 struct isds_commercial_permission **permission) {
396 if (NULL == permission || NULL == *permission) return;
398 free((*permission)->recipient);
399 free((*permission)->payer);
400 free((*permission)->expiration);
401 free((*permission)->count);
402 free((*permission)->reply_identifier);
404 zfree(*permission);
408 /* Deallocate struct isds_credit_event recursively and NULL it */
409 void isds_credit_event_free(struct isds_credit_event **event) {
410 if (NULL == event || NULL == *event) return;
412 free((*event)->time);
413 switch ((*event)->type) {
414 case ISDS_CREDIT_CHARGED:
415 free((*event)->details.charged.transaction);
416 break;
417 case ISDS_CREDIT_DISCHARGED:
418 free((*event)->details.discharged.transaction);
419 break;
420 case ISDS_CREDIT_MESSAGE_SENT:
421 free((*event)->details.message_sent.recipient);
422 free((*event)->details.message_sent.message_id);
423 break;
424 case ISDS_CREDIT_STORAGE_SET:
425 free((*event)->details.storage_set.new_valid_from);
426 free((*event)->details.storage_set.new_valid_to);
427 free((*event)->details.storage_set.old_capacity);
428 free((*event)->details.storage_set.old_valid_from);
429 free((*event)->details.storage_set.old_valid_to);
430 free((*event)->details.storage_set.initiator);
431 break;
432 case ISDS_CREDIT_EXPIRED:
433 break;
436 zfree(*event);
440 /* *DUP_OR_ERROR macros needs error label */
441 #define STRDUP_OR_ERROR(new, template) { \
442 if (!template) { \
443 (new) = NULL; \
444 } else { \
445 (new) = strdup(template); \
446 if (!new) goto error; \
450 #define FLATDUP_OR_ERROR(new, template) { \
451 if (!template) { \
452 (new) = NULL; \
453 } else { \
454 (new) = malloc(sizeof(*(new))); \
455 if (!new) goto error; \
456 memcpy((new), (template), sizeof(*(template))); \
460 /* Copy structure isds_pki_credentials recursively. */
461 struct isds_pki_credentials *isds_pki_credentials_duplicate(
462 const struct isds_pki_credentials *template) {
463 struct isds_pki_credentials *new = NULL;
465 if(!template) return NULL;
467 new = calloc(1, sizeof(*new));
468 if (!new) return NULL;
470 STRDUP_OR_ERROR(new->engine, template->engine);
471 new->certificate_format = template->certificate_format;
472 STRDUP_OR_ERROR(new->certificate, template->certificate);
473 new->key_format = template->key_format;
474 STRDUP_OR_ERROR(new->key, template->key);
475 STRDUP_OR_ERROR(new->passphrase, template->passphrase);
477 return new;
479 error:
480 isds_pki_credentials_free(&new);
481 return NULL;
485 /* Copy structure isds_PersonName recursively */
486 struct isds_PersonName *isds_PersonName_duplicate(
487 const struct isds_PersonName *src) {
488 struct isds_PersonName *new = NULL;
490 if (!src) return NULL;
492 new = calloc(1, sizeof(*new));
493 if (!new) return NULL;
495 STRDUP_OR_ERROR(new->pnFirstName, src->pnFirstName);
496 STRDUP_OR_ERROR(new->pnMiddleName, src->pnMiddleName);
497 STRDUP_OR_ERROR(new->pnLastName, src->pnLastName);
498 STRDUP_OR_ERROR(new->pnLastNameAtBirth, src->pnLastNameAtBirth);
500 return new;
502 error:
503 isds_PersonName_free(&new);
504 return NULL;
508 /* Copy structure isds_BirthInfo recursively */
509 static struct isds_BirthInfo *isds_BirthInfo_duplicate(
510 const struct isds_BirthInfo *template) {
511 struct isds_BirthInfo *new = NULL;
513 if (!template) return NULL;
515 new = calloc(1, sizeof(*new));
516 if (!new) return NULL;
518 FLATDUP_OR_ERROR(new->biDate, template->biDate);
519 STRDUP_OR_ERROR(new->biCity, template->biCity);
520 STRDUP_OR_ERROR(new->biCounty, template->biCounty);
521 STRDUP_OR_ERROR(new->biState, template->biState);
523 return new;
525 error:
526 isds_BirthInfo_free(&new);
527 return NULL;
531 /* Copy structure isds_Address recursively */
532 struct isds_Address *isds_Address_duplicate(
533 const struct isds_Address *src) {
534 struct isds_Address *new = NULL;
536 if (!src) return NULL;
538 new = calloc(1, sizeof(*new));
539 if (!new) return NULL;
541 STRDUP_OR_ERROR(new->adCity, src->adCity);
542 STRDUP_OR_ERROR(new->adStreet, src->adStreet);
543 STRDUP_OR_ERROR(new->adNumberInStreet, src->adNumberInStreet);
544 STRDUP_OR_ERROR(new->adNumberInMunicipality,
545 src->adNumberInMunicipality);
546 STRDUP_OR_ERROR(new->adZipCode, src->adZipCode);
547 STRDUP_OR_ERROR(new->adState, src->adState);
549 return new;
551 error:
552 isds_Address_free(&new);
553 return NULL;
557 /* Copy structure isds_DbOwnerInfo recursively */
558 struct isds_DbOwnerInfo *isds_DbOwnerInfo_duplicate(
559 const struct isds_DbOwnerInfo *src) {
560 struct isds_DbOwnerInfo *new = NULL;
561 if (!src) return NULL;
563 new = calloc(1, sizeof(*new));
564 if (!new) return NULL;
566 STRDUP_OR_ERROR(new->dbID, src->dbID);
567 FLATDUP_OR_ERROR(new->dbType, src->dbType);
568 STRDUP_OR_ERROR(new->ic, src->ic);
570 if (src->personName) {
571 if (!(new->personName =
572 isds_PersonName_duplicate(src->personName)))
573 goto error;
576 STRDUP_OR_ERROR(new->firmName, src->firmName);
578 if (src->birthInfo) {
579 if (!(new->birthInfo =
580 isds_BirthInfo_duplicate(src->birthInfo)))
581 goto error;
584 if (src->address) {
585 if (!(new->address = isds_Address_duplicate(src->address)))
586 goto error;
589 STRDUP_OR_ERROR(new->nationality, src->nationality);
590 STRDUP_OR_ERROR(new->email, src->email);
591 STRDUP_OR_ERROR(new->telNumber, src->telNumber);
592 STRDUP_OR_ERROR(new->identifier, src->identifier);
593 STRDUP_OR_ERROR(new->registryCode, src->registryCode);
594 FLATDUP_OR_ERROR(new->dbState, src->dbState);
595 FLATDUP_OR_ERROR(new->dbEffectiveOVM, src->dbEffectiveOVM);
596 FLATDUP_OR_ERROR(new->dbOpenAddressing, src->dbOpenAddressing);
598 return new;
600 error:
601 isds_DbOwnerInfo_free(&new);
602 return NULL;
606 /* Copy structure isds_DbUserInfo recursively */
607 struct isds_DbUserInfo *isds_DbUserInfo_duplicate(
608 const struct isds_DbUserInfo *src) {
609 struct isds_DbUserInfo *new = NULL;
610 if (!src) return NULL;
612 new = calloc(1, sizeof(*new));
613 if (!new) return NULL;
615 STRDUP_OR_ERROR(new->userID, src->userID);
616 FLATDUP_OR_ERROR(new->userType, src->userType);
617 FLATDUP_OR_ERROR(new->userPrivils, src->userPrivils);
619 if (src->personName) {
620 if (!(new->personName =
621 isds_PersonName_duplicate(src->personName)))
622 goto error;
625 if (src->address) {
626 if (!(new->address = isds_Address_duplicate(src->address)))
627 goto error;
630 FLATDUP_OR_ERROR(new->biDate, src->biDate);
631 STRDUP_OR_ERROR(new->ic, src->ic);
632 STRDUP_OR_ERROR(new->firmName, src->firmName);
633 STRDUP_OR_ERROR(new->caStreet, src->caStreet);
634 STRDUP_OR_ERROR(new->caCity, src->caCity);
635 STRDUP_OR_ERROR(new->caZipCode, src->caZipCode);
636 STRDUP_OR_ERROR(new->caState, src->caState);
638 return new;
640 error:
641 isds_DbUserInfo_free(&new);
642 return NULL;
645 #undef FLATDUP_OR_ERROR
646 #undef STRDUP_OR_ERROR
649 /* Logs libxml2 errors. Should be registered to libxml2 library.
650 * @ctx is unused currently
651 * @msg is printf-like formated message from libxml2 (UTF-8?)
652 * @... are variadic arguments for @msg */
653 static void log_xml(void *ctx, const char *msg, ...) {
654 va_list ap;
655 char *text = NULL;
657 if (!msg) return;
659 va_start(ap, msg);
660 isds_vasprintf(&text, msg, ap);
661 va_end(ap);
663 if (text)
664 isds_log(ILF_XML, ILL_ERR, "%s", text);
665 free(text);
669 /* Initialize ISDS library.
670 * Global function, must be called before other functions.
671 * If it fails you can not use ISDS library and must call isds_cleanup() to
672 * free partially initialized global variables. */
673 isds_error isds_init(void) {
674 /* NULL global variables */
675 log_facilities = ILF_ALL;
676 log_level = ILL_WARNING;
677 log_callback = NULL;
678 log_callback_data = NULL;
680 #if ENABLE_NLS
681 /* Initialize gettext */
682 bindtextdomain(PACKAGE, LOCALEDIR);
683 #endif
685 #if HAVE_LIBCURL
686 /* Initialize CURL */
687 if (curl_global_init(CURL_GLOBAL_ALL)) {
688 isds_log(ILF_ISDS, ILL_CRIT, _("CURL library initialization failed\n"));
689 return IE_ERROR;
691 #endif /* HAVE_LIBCURL */
693 /* Initialise cryptographic back-ends. */
694 if (IE_SUCCESS != _isds_init_crypto()) {
695 isds_log(ILF_ISDS, ILL_CRIT,
696 _("initialisation of cryptographic back-end failed\n"));
697 return IE_ERROR;
700 /* This can _exit() current program. Find not so assertive check. */
701 LIBXML_TEST_VERSION;
702 xmlSetGenericErrorFunc(NULL, log_xml);
704 /* Check expat */
705 if (_isds_init_expat(&version_expat)) {
706 isds_log(ILF_ISDS, ILL_CRIT,
707 _("expat library initialization failed\n"));
708 return IE_ERROR;
711 /* Allocate global variables */
714 return IE_SUCCESS;
718 /* Deinitialize ISDS library.
719 * Global function, must be called as last library function. */
720 isds_error isds_cleanup(void) {
721 /* XML */
722 xmlCleanupParser();
724 #if HAVE_LIBCURL
725 /* Curl */
726 curl_global_cleanup();
727 #endif
729 return IE_SUCCESS;
733 /* Return version string of this library. Version of dependencies can be
734 * embedded. Do no try to parse it. You must free it. */
735 char *isds_version(void) {
736 char *buffer = NULL;
738 isds_asprintf(&buffer,
739 #if HAVE_LIBCURL
740 # ifndef USE_OPENSSL_BACKEND
741 _("%s (%s, GPGME %s, gcrypt %s, %s, libxml2 %s)"),
742 # else
743 _("%s (%s, %s, %s, libxml2 %s)"),
744 # endif
745 #else
746 # ifndef USE_OPENSSL_BACKEND
747 _("%s (GPGME %s, gcrypt %s, %s, libxml2 %s)"),
748 # else
749 _("%s (%s, %s, libxml2 %s)"),
750 # endif
751 #endif
752 PACKAGE_VERSION,
753 #if HAVE_LIBCURL
754 curl_version(),
755 #endif
756 #ifndef USE_OPENSSL_BACKEND
757 version_gpgme, version_gcrypt,
758 #else
759 version_openssl,
760 #endif
761 version_expat, xmlParserVersion);
762 return buffer;
766 /* Return text description of ISDS error */
767 const char *isds_strerror(const isds_error error) {
768 switch (error) {
769 case IE_SUCCESS:
770 return(_("Success")); break;
771 case IE_ERROR:
772 return(_("Unspecified error")); break;
773 case IE_NOTSUP:
774 return(_("Not supported")); break;
775 case IE_INVAL:
776 return(_("Invalid value")); break;
777 case IE_INVALID_CONTEXT:
778 return(_("Invalid context")); break;
779 case IE_NOT_LOGGED_IN:
780 return(_("Not logged in")); break;
781 case IE_CONNECTION_CLOSED:
782 return(_("Connection closed")); break;
783 case IE_TIMED_OUT:
784 return(_("Timed out")); break;
785 case IE_NOEXIST:
786 return(_("Not exist")); break;
787 case IE_NOMEM:
788 return(_("Out of memory")); break;
789 case IE_NETWORK:
790 return(_("Network problem")); break;
791 case IE_HTTP:
792 return(_("HTTP problem")); break;
793 case IE_SOAP:
794 return(_("SOAP problem")); break;
795 case IE_XML:
796 return(_("XML problem")); break;
797 case IE_ISDS:
798 return(_("ISDS server problem")); break;
799 case IE_ENUM:
800 return(_("Invalid enum value")); break;
801 case IE_DATE:
802 return(_("Invalid date value")); break;
803 case IE_2BIG:
804 return(_("Too big")); break;
805 case IE_2SMALL:
806 return(_("Too small")); break;
807 case IE_NOTUNIQ:
808 return(_("Value not unique")); break;
809 case IE_NOTEQUAL:
810 return(_("Values not equal")); break;
811 case IE_PARTIAL_SUCCESS:
812 return(_("Some suboperations failed")); break;
813 case IE_ABORTED:
814 return(_("Operation aborted")); break;
815 case IE_SECURITY:
816 return(_("Security problem")); break;
817 default:
818 return(_("Unknown error"));
823 /* Create ISDS context.
824 * Each context can be used for different sessions to (possibly) different
825 * ISDS server with different credentials. */
826 struct isds_ctx *isds_ctx_create(void) {
827 struct isds_ctx *context;
828 context = malloc(sizeof(*context));
829 if (context) memset(context, 0, sizeof(*context));
830 return context;
833 #if HAVE_LIBCURL
834 /* Close possibly opened connection to Czech POINT document deposit without
835 * resetting long_message buffer.
836 * XXX: Do not use czp_close_connection() if you do not want to destroy log
837 * message.
838 * @context is Czech POINT session context. */
839 static isds_error czp_do_close_connection(struct isds_ctx *context) {
840 if (!context) return IE_INVALID_CONTEXT;
841 _isds_close_connection(context);
842 return IE_SUCCESS;
846 /* Discard credentials.
847 * @context is ISDS context
848 * @discard_saved_username is true for removing saved username, false for
849 * keeping it.
850 * Only that. It does not cause log out, connection close or similar. */
851 _hidden isds_error _isds_discard_credentials(struct isds_ctx *context,
852 _Bool discard_saved_username) {
853 if(!context) return IE_INVALID_CONTEXT;
855 if (context->username) {
856 memset(context->username, 0, strlen(context->username));
857 zfree(context->username);
859 if (context->password) {
860 memset(context->password, 0, strlen(context->password));
861 zfree(context->password);
863 isds_pki_credentials_free(&context->pki_credentials);
864 if (discard_saved_username && context->saved_username) {
865 memset(context->saved_username, 0, strlen(context->saved_username));
866 zfree(context->saved_username);
869 return IE_SUCCESS;
871 #endif /* HAVE_LIBCURL */
874 /* Destroy ISDS context and free memory.
875 * @context will be NULLed on success. */
876 isds_error isds_ctx_free(struct isds_ctx **context) {
877 if (!context || !*context) {
878 return IE_INVALID_CONTEXT;
881 #if HAVE_LIBCURL
882 /* Discard credentials and close connection */
883 switch ((*context)->type) {
884 case CTX_TYPE_NONE: break;
885 case CTX_TYPE_ISDS: isds_logout(*context); break;
886 case CTX_TYPE_CZP:
887 case CTX_TYPE_TESTING_REQUEST_COLLECTOR:
888 czp_do_close_connection(*context); break;
891 /* For sure */
892 _isds_discard_credentials(*context, 1);
894 /* Free other structures */
895 free((*context)->tls_verify_server);
896 free((*context)->tls_ca_file);
897 free((*context)->tls_ca_dir);
898 free((*context)->tls_crl_file);
899 #endif /* HAVE_LIBCURL */
900 free((*context)->long_message);
902 free(*context);
903 *context = NULL;
904 return IE_SUCCESS;
908 /* Return long message text produced by library function, e.g. detailed error
909 * message. Returned pointer is only valid until new library function is
910 * called for the same context. Could be NULL, especially if NULL context is
911 * supplied. Return string is locale encoded. */
912 char *isds_long_message(const struct isds_ctx *context) {
913 if (!context) return NULL;
914 return context->long_message;
918 /* Stores message into context' long_message buffer.
919 * Application can pick the message up using isds_long_message().
920 * NULL @message truncates the buffer but does not deallocate it.
921 * @message is coded in locale encoding */
922 _hidden isds_error isds_log_message(struct isds_ctx *context,
923 const char *message) {
924 char *buffer;
925 size_t length;
927 if (!context) return IE_INVALID_CONTEXT;
929 /* FIXME: Check for integer overflow */
930 length = 1 + ((message) ? strlen(message) : 0);
931 buffer = realloc(context->long_message, length);
932 if (!buffer) return IE_NOMEM;
934 if (message)
935 strcpy(buffer, message);
936 else
937 *buffer = '\0';
939 context->long_message = buffer;
940 return IE_SUCCESS;
944 /* Appends message into context' long_message buffer.
945 * Application can pick the message up using isds_long_message().
946 * NULL message has void effect. */
947 _hidden isds_error isds_append_message(struct isds_ctx *context,
948 const char *message) {
949 char *buffer;
950 size_t old_length, length;
952 if (!context) return IE_INVALID_CONTEXT;
953 if (!message) return IE_SUCCESS;
954 if (!context->long_message)
955 return isds_log_message(context, message);
957 old_length = strlen(context->long_message);
958 /* FIXME: Check for integer overflow */
959 length = 1 + old_length + strlen(message);
960 buffer = realloc(context->long_message, length);
961 if (!buffer) return IE_NOMEM;
963 strcpy(buffer + old_length, message);
965 context->long_message = buffer;
966 return IE_SUCCESS;
970 /* Stores formatted message into context' long_message buffer.
971 * Application can pick the message up using isds_long_message(). */
972 _hidden isds_error isds_printf_message(struct isds_ctx *context,
973 const char *format, ...) {
974 va_list ap;
975 int length;
977 if (!context) return IE_INVALID_CONTEXT;
978 va_start(ap, format);
979 length = isds_vasprintf(&(context->long_message), format, ap);
980 va_end(ap);
982 return (length < 0) ? IE_ERROR: IE_SUCCESS;
986 /* Set logging up.
987 * @facilities is bit mask of isds_log_facility values,
988 * @level is verbosity level. */
989 void isds_set_logging(const unsigned int facilities,
990 const isds_log_level level) {
991 log_facilities = facilities;
992 log_level = level;
996 /* Register callback function libisds calls when new global log message is
997 * produced by library. Library logs to stderr by default.
998 * @callback is function provided by application libisds will call. See type
999 * definition for @callback argument explanation. Pass NULL to revert logging to
1000 * default behaviour.
1001 * @data is application specific data @callback gets as last argument */
1002 void isds_set_log_callback(isds_log_callback callback, void *data) {
1003 log_callback = callback;
1004 log_callback_data = data;
1008 /* Log @message in class @facility with log @level into global log. @message
1009 * is printf(3) formatting string, variadic arguments may be necessary.
1010 * For debugging purposes. */
1011 _hidden isds_error isds_log(const isds_log_facility facility,
1012 const isds_log_level level, const char *message, ...) {
1013 va_list ap;
1014 char *buffer = NULL;
1015 int length;
1017 if (level > log_level) return IE_SUCCESS;
1018 if (!(log_facilities & facility)) return IE_SUCCESS;
1019 if (!message) return IE_INVAL;
1021 if (log_callback) {
1022 /* Pass message to application supplied callback function */
1023 va_start(ap, message);
1024 length = isds_vasprintf(&buffer, message, ap);
1025 va_end(ap);
1027 if (length == -1) {
1028 return IE_ERROR;
1030 if (length > 0) {
1031 log_callback(facility, level, buffer, length, log_callback_data);
1033 free(buffer);
1034 } else {
1035 /* Default: Log it to stderr */
1036 va_start(ap, message);
1037 vfprintf(stderr, message, ap);
1038 va_end(ap);
1039 /* Line buffered printf is default.
1040 * fflush(stderr);*/
1043 return IE_SUCCESS;
1047 /* Set timeout in milliseconds for each network job like connecting to server
1048 * or sending message. Use 0 to disable timeout limits. */
1049 isds_error isds_set_timeout(struct isds_ctx *context,
1050 const unsigned int timeout) {
1051 if (!context) return IE_INVALID_CONTEXT;
1052 zfree(context->long_message);
1054 #if HAVE_LIBCURL
1055 context->timeout = timeout;
1057 if (context->curl) {
1058 CURLcode curl_err;
1060 curl_err = curl_easy_setopt(context->curl, CURLOPT_NOSIGNAL, 1);
1061 if (!curl_err)
1062 #if HAVE_DECL_CURLOPT_TIMEOUT_MS /* Since curl-7.16.2 */
1063 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT_MS,
1064 context->timeout);
1065 #else
1066 curl_err = curl_easy_setopt(context->curl, CURLOPT_TIMEOUT,
1067 context->timeout / 1000);
1068 #endif /* not HAVE_DECL_CURLOPT_TIMEOUT_MS */
1069 if (curl_err) return IE_ERROR;
1072 return IE_SUCCESS;
1073 #else /* not HAVE_LIBCURL */
1074 return IE_NOTSUP;
1075 #endif
1079 /* Register callback function libisds calls periodically during HTTP data
1080 * transfer.
1081 * @context is session context
1082 * @callback is function provided by application libisds will call. See type
1083 * definition for @callback argument explanation.
1084 * @data is application specific data @callback gets as last argument */
1085 isds_error isds_set_progress_callback(struct isds_ctx *context,
1086 isds_progress_callback callback, void *data) {
1087 if (!context) return IE_INVALID_CONTEXT;
1088 zfree(context->long_message);
1090 #if HAVE_LIBCURL
1091 context->progress_callback = callback;
1092 context->progress_callback_data = data;
1094 return IE_SUCCESS;
1095 #else /* not HAVE_LIBCURL */
1096 return IE_NOTSUP;
1097 #endif
1101 /* Change context settings.
1102 * @context is context which setting will be applied to
1103 * @option is name of option. It determines the type of last argument. See
1104 * isds_option definition for more info.
1105 * @... is value of new setting. Type is determined by @option
1106 * */
1107 isds_error isds_set_opt(struct isds_ctx *context, const isds_option option,
1108 ...) {
1109 isds_error err = IE_SUCCESS;
1110 va_list ap;
1111 #if HAVE_LIBCURL
1112 char *pointer, *string;
1113 #endif
1115 if (!context) return IE_INVALID_CONTEXT;
1116 zfree(context->long_message);
1118 va_start(ap, option);
1120 #define REPLACE_VA_BOOLEAN(destination) { \
1121 if (!(destination)) { \
1122 (destination) = malloc(sizeof(*(destination))); \
1123 if (!(destination)) { \
1124 err = IE_NOMEM; goto leave; \
1127 *(destination) = (_Bool) !!va_arg(ap, int); \
1130 #define REPLACE_VA_STRING(destination) { \
1131 string = va_arg(ap, char *); \
1132 if (string) { \
1133 pointer = realloc((destination), 1 + strlen(string)); \
1134 if (!pointer) { err = IE_NOMEM; goto leave; } \
1135 strcpy(pointer, string); \
1136 (destination) = pointer; \
1137 } else { \
1138 free(destination); \
1139 (destination) = NULL; \
1143 switch (option) {
1144 case IOPT_TLS_VERIFY_SERVER:
1145 #if HAVE_LIBCURL
1146 REPLACE_VA_BOOLEAN(context->tls_verify_server);
1147 #else
1148 err = IE_NOTSUP; goto leave;
1149 #endif
1150 break;
1151 case IOPT_TLS_CA_FILE:
1152 #if HAVE_LIBCURL
1153 REPLACE_VA_STRING(context->tls_ca_file);
1154 #else
1155 err = IE_NOTSUP; goto leave;
1156 #endif
1157 break;
1158 case IOPT_TLS_CA_DIRECTORY:
1159 #if HAVE_LIBCURL
1160 REPLACE_VA_STRING(context->tls_ca_dir);
1161 #else
1162 err = IE_NOTSUP; goto leave;
1163 #endif
1164 break;
1165 case IOPT_TLS_CRL_FILE:
1166 #if HAVE_LIBCURL
1167 #if HAVE_DECL_CURLOPT_CRLFILE /* Since curl-7.19.0 */
1168 REPLACE_VA_STRING(context->tls_crl_file);
1169 #else
1170 isds_log_message(context,
1171 _("Curl library does not support CRL definition"));
1172 err = IE_NOTSUP;
1173 #endif /* not HAVE_DECL_CURLOPT_CRLFILE */
1174 #else
1175 err = IE_NOTSUP; goto leave;
1176 #endif /* not HAVE_LIBCURL */
1177 break;
1178 case IOPT_NORMALIZE_MIME_TYPE:
1179 context->normalize_mime_type = (_Bool) !!va_arg(ap, int);
1180 break;
1182 default:
1183 err = IE_ENUM; goto leave;
1186 #undef REPLACE_VA_STRING
1187 #undef REPLACE_VA_BOOLEAN
1189 leave:
1190 va_end(ap);
1191 return err;
1195 #if HAVE_LIBCURL
1196 /* Copy credentials into context. Any non-NULL argument will be duplicated.
1197 * Destination for NULL argument will not be touched.
1198 * Destination pointers must be freed before calling this function.
1199 * If @username is @context->saved_username, the saved_username will not be
1200 * replaced. The saved_username is clobbered only if context has set otp
1201 * member.
1202 * Return IE_SUCCESS on success. */
1203 static isds_error _isds_store_credentials(struct isds_ctx *context,
1204 const char *username, const char *password,
1205 const struct isds_pki_credentials *pki_credentials) {
1206 if (NULL == context) return IE_INVALID_CONTEXT;
1208 /* FIXME: mlock password
1209 * (I have a library) */
1211 if (username) {
1212 context->username = strdup(username);
1213 if (context->otp && context->saved_username != username)
1214 context->saved_username = strdup(username);
1216 if (password) {
1217 if (NULL == context->otp_credentials)
1218 context->password = strdup(password);
1219 else
1220 context->password = _isds_astrcat(password,
1221 context->otp_credentials->otp_code);
1223 context->pki_credentials = isds_pki_credentials_duplicate(pki_credentials);
1225 if ((NULL != username && NULL == context->username) ||
1226 (NULL != password && NULL == context->password) ||
1227 (NULL != pki_credentials && NULL == context->pki_credentials) ||
1228 (context->otp && NULL != context->username &&
1229 NULL == context->saved_username)) {
1230 return IE_NOMEM;
1233 return IE_SUCCESS;
1235 #endif
1238 /* Connect and log into ISDS server.
1239 * All required arguments will be copied, you do not have to keep them after
1240 * that.
1241 * ISDS supports six different authentication methods. Exact method is
1242 * selected on @username, @password, @pki_credentials, and @otp arguments:
1243 * - If @pki_credentials == NULL, @username and @password must be supplied
1244 * and then
1245 * - If @otp == NULL, simple authentication by username and password will
1246 * be proceeded.
1247 * - If @otp != NULL, authentication by username and password and OTP
1248 * will be used.
1249 * - If @pki_credentials != NULL, then
1250 * - If @username == NULL, only certificate will be used
1251 * - If @username != NULL, then
1252 * - If @password == NULL, then certificate will be used and
1253 * @username shifts meaning to box ID. This is used for hosted
1254 * services.
1255 * - Otherwise all three arguments will be used.
1256 * Please note, that different cases require different certificate type
1257 * (system qualified one or commercial non qualified one). This library
1258 * does not check such political issues. Please see ISDS Specification
1259 * for more details.
1260 * @url is base address of ISDS web service. Pass extern isds_locator
1261 * variable to use production ISDS instance without client certificate
1262 * authentication (or extern isds_cert_locator with client certificate
1263 * authentication or extern isds_otp_locators with OTP authentication).
1264 * Passing NULL has the same effect, autoselection between isds_locator,
1265 * isds_cert_locator, and isds_otp_locator is performed in addition. You can
1266 * pass extern isds_testing_locator (or isds_cert_testing_locator or
1267 * isds_otp_testing_locator) variable to select testing instance.
1268 * @username is user name of ISDS user or box ID
1269 * @password is user's secret password
1270 * @pki_credentials defines public key cryptographic material to use in client
1271 * authentication.
1272 * @otp selects one-time password authentication method to use, defines OTP
1273 * code (if known) and returns fine grade resolution of OTP procedure.
1274 * @return:
1275 * IE_SUCCESS if authentication succeeds
1276 * IE_NOT_LOGGED_IN if authentication fails. If OTP authentication has been
1277 * requested, fine grade reason will be set into @otp->resolution. Error
1278 * message from server can be obtained by isds_long_message() call.
1279 * IE_PARTIAL_SUCCESS if time-based OTP authentication has been requested and
1280 * server has sent OTP code through side channel. Application is expected to
1281 * fill the code into @otp->otp_code, keep other arguments unchanged, and retry
1282 * this call to complete second phase of TOTP authentication;
1283 * or other appropriate error. */
1284 isds_error isds_login(struct isds_ctx *context, const char *url,
1285 const char *username, const char *password,
1286 const struct isds_pki_credentials *pki_credentials,
1287 struct isds_otp *otp) {
1288 #if HAVE_LIBCURL
1289 isds_error err = IE_NOT_LOGGED_IN;
1290 isds_error soap_err;
1291 xmlNsPtr isds_ns = NULL;
1292 xmlNodePtr request = NULL;
1293 xmlNodePtr response = NULL;
1294 #endif /* HAVE_LIBCURL */
1296 if (!context) return IE_INVALID_CONTEXT;
1297 zfree(context->long_message);
1299 #if HAVE_LIBCURL
1300 /* Close connection if already logged in */
1301 if (context->curl) {
1302 _isds_close_connection(context);
1305 /* Store configuration */
1306 context->type = CTX_TYPE_ISDS;
1307 zfree(context->url);
1309 /* Mangle base URI according to requested authentication method */
1310 if (NULL == pki_credentials) {
1311 isds_log(ILF_SEC, ILL_INFO,
1312 _("Selected authentication method: no certificate, "
1313 "username and password\n"));
1314 if (!username || !password) {
1315 isds_log_message(context,
1316 _("Both username and password must be supplied"));
1317 return IE_INVAL;
1319 context->otp_credentials = otp;
1320 context->otp = (NULL != context->otp_credentials);
1322 if (!context->otp) {
1323 /* Default locator is official system (without certificate or
1324 * OTP) */
1325 context->url = strdup((NULL != url) ? url : isds_locator);
1326 } else {
1327 const char *authenticator_uri = NULL;
1328 if (!url) url = isds_otp_locator;
1329 otp->resolution = OTP_RESOLUTION_UNKNOWN;
1330 switch (context->otp_credentials->method) {
1331 case OTP_HMAC:
1332 isds_log(ILF_SEC, ILL_INFO,
1333 _("Selected authentication method: "
1334 "HMAC-based one-time password\n"));
1335 authenticator_uri =
1336 "%1$sas/processLogin?type=hotp&uri=%1$sapps/";
1337 break;
1338 case OTP_TIME:
1339 isds_log(ILF_SEC, ILL_INFO,
1340 _("Selected authentication method: "
1341 "Time-based one-time password\n"));
1342 if (context->otp_credentials->otp_code == NULL) {
1343 isds_log(ILF_SEC, ILL_INFO,
1344 _("OTP code has not been provided by "
1345 "application, requesting server for "
1346 "new one.\n"));
1347 authenticator_uri =
1348 "%1$sas/processLogin?type=totp&sendSms=true&"
1349 "uri=%1$sapps/";
1350 } else {
1351 isds_log(ILF_SEC, ILL_INFO,
1352 _("OTP code has been provided by "
1353 "application, not requesting server "
1354 "for new one.\n"));
1355 authenticator_uri =
1356 "%1$sas/processLogin?type=totp&"
1357 "uri=%1$sapps/";
1359 break;
1360 default:
1361 isds_log_message(context,
1362 _("Unknown one-time password authentication "
1363 "method requested by application"));
1364 return IE_ENUM;
1366 if (-1 == isds_asprintf(&context->url, authenticator_uri, url))
1367 return IE_NOMEM;
1369 } else {
1370 /* Default locator is official system (with client certificate) */
1371 context->otp = 0;
1372 context->otp_credentials = NULL;
1373 if (!url) url = isds_cert_locator;
1375 if (!username) {
1376 isds_log(ILF_SEC, ILL_INFO,
1377 _("Selected authentication method: system certificate, "
1378 "no username and no password\n"));
1379 password = NULL;
1380 context->url = _isds_astrcat(url, "cert/");
1381 } else {
1382 if (!password) {
1383 isds_log(ILF_SEC, ILL_INFO,
1384 _("Selected authentication method: system certificate, "
1385 "box ID and no password\n"));
1386 context->url = _isds_astrcat(url, "hspis/");
1387 } else {
1388 isds_log(ILF_SEC, ILL_INFO,
1389 _("Selected authentication method: commercial "
1390 "certificate, username and password\n"));
1391 context->url = _isds_astrcat(url, "certds/");
1395 if (!(context->url))
1396 return IE_NOMEM;
1398 /* Prepare CURL handle */
1399 context->curl = curl_easy_init();
1400 if (!(context->curl))
1401 return IE_ERROR;
1403 /* Build log-in request */
1404 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1405 if (!request) {
1406 isds_log_message(context, _("Could not build ISDS log-in request"));
1407 return IE_ERROR;
1409 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1410 if(!isds_ns) {
1411 isds_log_message(context, _("Could not create ISDS name space"));
1412 xmlFreeNode(request);
1413 return IE_ERROR;
1415 xmlSetNs(request, isds_ns);
1417 /* Store credentials */
1418 _isds_discard_credentials(context, 1);
1419 if (_isds_store_credentials(context, username, password, pki_credentials)) {
1420 _isds_discard_credentials(context, 1);
1421 xmlFreeNode(request);
1422 return IE_NOMEM;
1425 isds_log(ILF_ISDS, ILL_DEBUG, _("Logging user %s into server %s\n"),
1426 username, url);
1428 /* XXX: ISDS documentation does not specify response body for
1429 * DummyOperation request. However real server sends back
1430 * DummyOperationResponse. Therefore we cannot check for the SOAP body
1431 * content and we call _isds_soap() instead of _isds(). _isds() checks for
1432 * SOAP body content, e.g. the dmStatus element. */
1434 /* Send log-in request */
1435 soap_err = _isds_soap(context, "DS/dz", request, &response, NULL, NULL);
1437 if (context->otp) {
1438 /* Revert context URL from OTP authentication service URL to OTP web
1439 * service base URL for subsequent calls. Potenial isds_login() retry
1440 * will re-set context URL again. */
1441 zfree(context->url);
1442 context->url = _isds_astrcat(url, "apps/");
1443 if (context->url == NULL) {
1444 soap_err = IE_NOMEM;
1446 /* Detach pointer to OTP credentials from context */
1447 context->otp_credentials = NULL;
1450 /* Remove credentials */
1451 _isds_discard_credentials(context, 0);
1453 /* Destroy log-in request */
1454 xmlFreeNode(request);
1456 if (soap_err) {
1457 xmlFreeNodeList(response);
1458 _isds_close_connection(context);
1459 return soap_err;
1462 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1463 * authentication succeeded if soap_err == IE_SUCCESS */
1464 err = IE_SUCCESS;
1466 xmlFreeNodeList(response);
1468 if (!err)
1469 isds_log(ILF_ISDS, ILL_DEBUG,
1470 _("User %s has been logged into server %s successfully\n"),
1471 username, url);
1472 return err;
1473 #else /* not HAVE_LIBCURL */
1474 return IE_NOTSUP;
1475 #endif
1479 /* Log out from ISDS server discards credentials and connection configuration. */
1480 isds_error isds_logout(struct isds_ctx *context) {
1481 if (!context) return IE_INVALID_CONTEXT;
1482 zfree(context->long_message);
1484 #if HAVE_LIBCURL
1485 if (context->curl) {
1486 if (context->otp) {
1487 isds_error err = _isds_invalidate_otp_cookie(context);
1488 if (err) return err;
1491 /* Close connection */
1492 _isds_close_connection(context);
1494 /* Discard credentials for sure. They should not survive isds_login(),
1495 * even successful .*/
1496 _isds_discard_credentials(context, 1);
1497 zfree(context->url);
1499 isds_log(ILF_ISDS, ILL_DEBUG, _("Logged out from ISDS server\n"));
1500 } else {
1501 _isds_discard_credentials(context, 1);
1503 return IE_SUCCESS;
1504 #else /* not HAVE_LIBCURL */
1505 return IE_NOTSUP;
1506 #endif
1510 /* Verify connection to ISDS is alive and server is responding.
1511 * Send dummy request to ISDS and expect dummy response. */
1512 isds_error isds_ping(struct isds_ctx *context) {
1513 #if HAVE_LIBCURL
1514 isds_error soap_err;
1515 xmlNsPtr isds_ns = NULL;
1516 xmlNodePtr request = NULL;
1517 xmlNodePtr response = NULL;
1518 #endif /* HAVE_LIBCURL */
1520 if (!context) return IE_INVALID_CONTEXT;
1521 zfree(context->long_message);
1523 #if HAVE_LIBCURL
1524 /* Check if connection is established */
1525 if (!context->curl) return IE_CONNECTION_CLOSED;
1528 /* Build dummy request */
1529 request = xmlNewNode(NULL, BAD_CAST "DummyOperation");
1530 if (!request) {
1531 isds_log_message(context, _("Could build ISDS dummy request"));
1532 return IE_ERROR;
1534 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1535 if(!isds_ns) {
1536 isds_log_message(context, _("Could not create ISDS name space"));
1537 xmlFreeNode(request);
1538 return IE_ERROR;
1540 xmlSetNs(request, isds_ns);
1542 isds_log(ILF_ISDS, ILL_DEBUG, _("Pinging ISDS server\n"));
1544 /* XXX: ISDS documentation does not specify response body for
1545 * DummyOperation request. However real server sends back
1546 * DummyOperationResponse. Therefore we cannot check for the SOAP body
1547 * content and we call _isds_soap() instead of _isds(). _isds() checks for
1548 * SOAP body content, e.g. the dmStatus element. */
1550 /* Send dummy request */
1551 soap_err = _isds_soap(context, "DS/dz", request, &response, NULL, NULL);
1553 /* Destroy log-in request */
1554 xmlFreeNode(request);
1556 if (soap_err) {
1557 isds_log(ILF_ISDS, ILL_DEBUG,
1558 _("ISDS server could not be contacted\n"));
1559 xmlFreeNodeList(response);
1560 return soap_err;
1563 /* XXX: Until we don't propagate HTTP code 500 or 4xx, we can be sure
1564 * authentication succeeded if soap_err == IE_SUCCESS */
1567 xmlFreeNodeList(response);
1569 isds_log(ILF_ISDS, ILL_DEBUG, _("ISDS server alive\n"));
1571 return IE_SUCCESS;
1572 #else /* not HAVE_LIBCURL */
1573 return IE_NOTSUP;
1574 #endif
1578 /* Send bogus request to ISDS.
1579 * Just for test purposes */
1580 isds_error isds_bogus_request(struct isds_ctx *context) {
1581 #if HAVE_LIBCURL
1582 isds_error err;
1583 xmlNsPtr isds_ns = NULL;
1584 xmlNodePtr request = NULL;
1585 xmlDocPtr response = NULL;
1586 xmlChar *code = NULL, *message = NULL;
1587 #endif
1589 if (!context) return IE_INVALID_CONTEXT;
1590 zfree(context->long_message);
1592 #if HAVE_LIBCURL
1593 /* Check if connection is established */
1594 if (!context->curl) {
1595 /* Testing printf message */
1596 isds_printf_message(context, "%s", _("I said connection closed"));
1597 return IE_CONNECTION_CLOSED;
1601 /* Build dummy request */
1602 request = xmlNewNode(NULL, BAD_CAST "X-BogusOperation");
1603 if (!request) {
1604 isds_log_message(context, _("Could build ISDS bogus request"));
1605 return IE_ERROR;
1607 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
1608 if(!isds_ns) {
1609 isds_log_message(context, _("Could not create ISDS name space"));
1610 xmlFreeNode(request);
1611 return IE_ERROR;
1613 xmlSetNs(request, isds_ns);
1615 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending bogus request to ISDS\n"));
1617 /* Sent bogus request */
1618 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
1620 /* Destroy request */
1621 xmlFreeNode(request);
1623 if (err) {
1624 isds_log(ILF_ISDS, ILL_DEBUG,
1625 _("Processing ISDS response on bogus request failed\n"));
1626 xmlFreeDoc(response);
1627 return err;
1630 /* Check for response status */
1631 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
1632 &code, &message, NULL);
1633 if (err) {
1634 isds_log(ILF_ISDS, ILL_DEBUG,
1635 _("ISDS response on bogus request is missing status\n"));
1636 free(code);
1637 free(message);
1638 xmlFreeDoc(response);
1639 return err;
1641 if (xmlStrcmp(code, BAD_CAST "0000")) {
1642 char *code_locale = _isds_utf82locale((char*)code);
1643 char *message_locale = _isds_utf82locale((char*)message);
1644 isds_log(ILF_ISDS, ILL_DEBUG,
1645 _("Server refused bogus request (code=%s, message=%s)\n"),
1646 code_locale, message_locale);
1647 /* XXX: Literal error messages from ISDS are Czech messages
1648 * (English sometimes) in UTF-8. It's hard to catch them for
1649 * translation. Successfully gettextized would return in locale
1650 * encoding, unsuccessfully translated would pass in UTF-8. */
1651 isds_log_message(context, message_locale);
1652 free(code_locale);
1653 free(message_locale);
1654 free(code);
1655 free(message);
1656 xmlFreeDoc(response);
1657 return IE_ISDS;
1661 free(code);
1662 free(message);
1663 xmlFreeDoc(response);
1665 isds_log(ILF_ISDS, ILL_DEBUG,
1666 _("Bogus message accepted by server. This should not happen.\n"));
1668 return IE_SUCCESS;
1669 #else /* not HAVE_LIBCURL */
1670 return IE_NOTSUP;
1671 #endif
1675 #if HAVE_LIBCURL
1676 /* Serialize XML subtree to buffer preserving XML indentation.
1677 * @context is session context
1678 * @subtree is XML element to be serialized (with children)
1679 * @buffer is automatically reallocated buffer where serialize to
1680 * @length is size of serialized stream in bytes
1681 * @return standard error code, free @buffer in case of error */
1682 static isds_error serialize_subtree(struct isds_ctx *context,
1683 xmlNodePtr subtree, void **buffer, size_t *length) {
1684 isds_error err = IE_SUCCESS;
1685 xmlBufferPtr xml_buffer = NULL;
1686 xmlSaveCtxtPtr save_ctx = NULL;
1687 xmlDocPtr subtree_doc = NULL;
1688 xmlNodePtr subtree_copy;
1689 xmlNsPtr isds_ns;
1690 void *new_buffer;
1692 if (!context) return IE_INVALID_CONTEXT;
1693 if (!buffer) return IE_INVAL;
1694 zfree(*buffer);
1695 if (!subtree || !length) return IE_INVAL;
1697 /* Make temporary XML document with @subtree root element */
1698 /* XXX: We can not use xmlNodeDump() because it dumps the subtree as is.
1699 * It can result in not well-formed on invalid XML tree (e.g. name space
1700 * prefix definition can miss. */
1701 /*FIXME */
1703 subtree_doc = xmlNewDoc(BAD_CAST "1.0");
1704 if (!subtree_doc) {
1705 isds_log_message(context, _("Could not build temporary document"));
1706 err = IE_ERROR;
1707 goto leave;
1710 /* XXX: Copy subtree and attach the copy to document.
1711 * One node can not bee attached into more document at the same time.
1712 * XXX: Check xmlDOMWrapRemoveNode(). It could solve NS references
1713 * automatically.
1714 * XXX: Check xmlSaveTree() too. */
1715 subtree_copy = xmlCopyNodeList(subtree);
1716 if (!subtree_copy) {
1717 isds_log_message(context, _("Could not copy subtree"));
1718 err = IE_ERROR;
1719 goto leave;
1721 xmlDocSetRootElement(subtree_doc, subtree_copy);
1723 /* Only this way we get namespace definition as @xmlns:isds,
1724 * otherwise we get namespace prefix without definition */
1725 /* FIXME: Don't overwrite original default namespace */
1726 isds_ns = xmlNewNs(subtree_copy, BAD_CAST ISDS_NS, NULL);
1727 if(!isds_ns) {
1728 isds_log_message(context, _("Could not create ISDS name space"));
1729 err = IE_ERROR;
1730 goto leave;
1732 xmlSetNs(subtree_copy, isds_ns);
1735 /* Serialize the document into buffer */
1736 xml_buffer = xmlBufferCreate();
1737 if (!xml_buffer) {
1738 isds_log_message(context, _("Could not create xmlBuffer"));
1739 err = IE_ERROR;
1740 goto leave;
1742 /* Last argument 0 means to not format the XML tree */
1743 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8", 0);
1744 if (!save_ctx) {
1745 isds_log_message(context, _("Could not create XML serializer"));
1746 err = IE_ERROR;
1747 goto leave;
1749 /* XXX: According LibXML documentation, this function does not return
1750 * meaningful value yet */
1751 xmlSaveDoc(save_ctx, subtree_doc);
1752 if (-1 == xmlSaveFlush(save_ctx)) {
1753 isds_log_message(context,
1754 _("Could not serialize XML subtree"));
1755 err = IE_ERROR;
1756 goto leave;
1758 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1759 * even after xmlSaveFlush(). Thus close it here */
1760 xmlSaveClose(save_ctx); save_ctx = NULL;
1763 /* Store and detach buffer from xml_buffer */
1764 *buffer = xml_buffer->content;
1765 *length = xml_buffer->use;
1766 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1768 /* Shrink buffer */
1769 new_buffer = realloc(*buffer, *length);
1770 if (new_buffer) *buffer = new_buffer;
1772 leave:
1773 if (err) {
1774 zfree(*buffer);
1775 *length = 0;
1778 xmlSaveClose(save_ctx);
1779 xmlBufferFree(xml_buffer);
1780 xmlFreeDoc(subtree_doc); /* Frees subtree_copy, isds_ns etc. */
1781 return err;
1783 #endif /* HAVE_LIBCURL */
1786 #if 0
1787 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1788 * @context is session context
1789 * @document is original document where @nodeset points to
1790 * @nodeset is XPath node set to dump (recursively)
1791 * @buffer is automatically reallocated buffer where serialize to
1792 * @length is size of serialized stream in bytes
1793 * @return standard error code, free @buffer in case of error */
1794 static isds_error dump_nodeset(struct isds_ctx *context,
1795 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1796 void **buffer, size_t *length) {
1797 isds_error err = IE_SUCCESS;
1798 xmlBufferPtr xml_buffer = NULL;
1799 void *new_buffer;
1801 if (!context) return IE_INVALID_CONTEXT;
1802 if (!buffer) return IE_INVAL;
1803 zfree(*buffer);
1804 if (!document || !nodeset || !length) return IE_INVAL;
1805 *length = 0;
1807 /* Empty node set results into NULL buffer */
1808 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1809 goto leave;
1812 /* Resulting the document into buffer */
1813 xml_buffer = xmlBufferCreate();
1814 if (!xml_buffer) {
1815 isds_log_message(context, _("Could not create xmlBuffer"));
1816 err = IE_ERROR;
1817 goto leave;
1820 /* Iterate over all nodes */
1821 for (int i = 0; i < nodeset->nodeNr; i++) {
1822 /* Serialize node.
1823 * XXX: xmlNodeDump() appends to xml_buffer. */
1824 if (-1 ==
1825 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1826 isds_log_message(context, _("Could not dump XML node"));
1827 err = IE_ERROR;
1828 goto leave;
1832 /* Store and detach buffer from xml_buffer */
1833 *buffer = xml_buffer->content;
1834 *length = xml_buffer->use;
1835 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1837 /* Shrink buffer */
1838 new_buffer = realloc(*buffer, *length);
1839 if (new_buffer) *buffer = new_buffer;
1842 leave:
1843 if (err) {
1844 zfree(*buffer);
1845 *length = 0;
1848 xmlBufferFree(xml_buffer);
1849 return err;
1851 #endif
1853 #if 0
1854 /* Dump XML subtree to buffer as literal string, not valid XML possibly.
1855 * @context is session context
1856 * @document is original document where @nodeset points to
1857 * @nodeset is XPath node set to dump (recursively)
1858 * @buffer is automatically reallocated buffer where serialize to
1859 * @length is size of serialized stream in bytes
1860 * @return standard error code, free @buffer in case of error */
1861 static isds_error dump_nodeset(struct isds_ctx *context,
1862 const xmlDocPtr document, const xmlNodeSetPtr nodeset,
1863 void **buffer, size_t *length) {
1864 isds_error err = IE_SUCCESS;
1865 xmlBufferPtr xml_buffer = NULL;
1866 xmlSaveCtxtPtr save_ctx = NULL;
1867 void *new_buffer;
1869 if (!context) return IE_INVALID_CONTEXT;
1870 if (!buffer) return IE_INVAL;
1871 zfree(*buffer);
1872 if (!document || !nodeset || !length) return IE_INVAL;
1873 *length = 0;
1875 /* Empty node set results into NULL buffer */
1876 if (xmlXPathNodeSetIsEmpty(nodeset)) {
1877 goto leave;
1880 /* Resulting the document into buffer */
1881 xml_buffer = xmlBufferCreate();
1882 if (!xml_buffer) {
1883 isds_log_message(context, _("Could not create xmlBuffer"));
1884 err = IE_ERROR;
1885 goto leave;
1887 if (xmlSubstituteEntitiesDefault(1)) {
1888 isds_log_message(context, _("Could not disable attribute escaping"));
1889 err = IE_ERROR;
1890 goto leave;
1892 /* Last argument means:
1893 * 0 to not format the XML tree
1894 * XML_SAVE_NO_EMPTY ISDS does not produce shorten tags */
1895 save_ctx = xmlSaveToBuffer(xml_buffer, "UTF-8",
1896 XML_SAVE_NO_DECL|XML_SAVE_NO_EMPTY|XML_SAVE_NO_XHTML);
1897 if (!save_ctx) {
1898 isds_log_message(context, _("Could not create XML serializer"));
1899 err = IE_ERROR;
1900 goto leave;
1902 /*if (xmlSaveSetAttrEscape(save_ctx, NULL)) {
1903 isds_log_message(context, _("Could not disable attribute escaping"));
1904 err = IE_ERROR;
1905 goto leave;
1909 /* Iterate over all nodes */
1910 for (int i = 0; i < nodeset->nodeNr; i++) {
1911 /* Serialize node.
1912 * XXX: xmlNodeDump() appends to xml_buffer. */
1913 /*if (-1 ==
1914 xmlNodeDump(xml_buffer, document, nodeset->nodeTab[i], 0, 0)) {
1916 /* XXX: According LibXML documentation, this function does not return
1917 * meaningful value yet */
1918 xmlSaveTree(save_ctx, nodeset->nodeTab[i]);
1919 if (-1 == xmlSaveFlush(save_ctx)) {
1920 isds_log_message(context,
1921 _("Could not serialize XML subtree"));
1922 err = IE_ERROR;
1923 goto leave;
1927 /* XXX: libxml-2.7.4 complains when xmlSaveClose() on immutable buffer
1928 * even after xmlSaveFlush(). Thus close it here */
1929 xmlSaveClose(save_ctx); save_ctx = NULL;
1931 /* Store and detach buffer from xml_buffer */
1932 *buffer = xml_buffer->content;
1933 *length = xml_buffer->use;
1934 xmlBufferSetAllocationScheme(xml_buffer, XML_BUFFER_ALLOC_IMMUTABLE);
1936 /* Shrink buffer */
1937 new_buffer = realloc(*buffer, *length);
1938 if (new_buffer) *buffer = new_buffer;
1940 leave:
1941 if (err) {
1942 zfree(*buffer);
1943 *length = 0;
1946 xmlSaveClose(save_ctx);
1947 xmlBufferFree(xml_buffer);
1948 return err;
1950 #endif
1953 #if HAVE_LIBCURL
1954 /* Convert UTF-8 @string representation of ISDS dbType to enum @type */
1955 static isds_error string2isds_DbType(xmlChar *string, isds_DbType *type) {
1956 if (!string || !type) return IE_INVAL;
1958 if (!xmlStrcmp(string, BAD_CAST "FO"))
1959 *type = DBTYPE_FO;
1960 else if (!xmlStrcmp(string, BAD_CAST "PFO"))
1961 *type = DBTYPE_PFO;
1962 else if (!xmlStrcmp(string, BAD_CAST "PFO_ADVOK"))
1963 *type = DBTYPE_PFO_ADVOK;
1964 else if (!xmlStrcmp(string, BAD_CAST "PFO_DANPOR"))
1965 *type = DBTYPE_PFO_DANPOR;
1966 else if (!xmlStrcmp(string, BAD_CAST "PFO_INSSPR"))
1967 *type = DBTYPE_PFO_INSSPR;
1968 else if (!xmlStrcmp(string, BAD_CAST "PO"))
1969 *type = DBTYPE_PO;
1970 else if (!xmlStrcmp(string, BAD_CAST "PO_ZAK"))
1971 *type = DBTYPE_PO_ZAK;
1972 else if (!xmlStrcmp(string, BAD_CAST "PO_REQ"))
1973 *type = DBTYPE_PO_REQ;
1974 else if (!xmlStrcmp(string, BAD_CAST "OVM"))
1975 *type = DBTYPE_OVM;
1976 else if (!xmlStrcmp(string, BAD_CAST "OVM_NOTAR"))
1977 *type = DBTYPE_OVM_NOTAR;
1978 else if (!xmlStrcmp(string, BAD_CAST "OVM_EXEKUT"))
1979 *type = DBTYPE_OVM_EXEKUT;
1980 else if (!xmlStrcmp(string, BAD_CAST "OVM_REQ"))
1981 *type = DBTYPE_OVM_REQ;
1982 else
1983 return IE_ENUM;
1984 return IE_SUCCESS;
1988 /* Convert ISDS dbType enum @type to UTF-8 string.
1989 * @Return pointer to static string, or NULL if unknown enum value */
1990 static const xmlChar *isds_DbType2string(const isds_DbType type) {
1991 switch(type) {
1992 /* DBTYPE_SYSTEM is invalid value from point of view of public
1993 * SOAP interface. */
1994 case DBTYPE_FO: return(BAD_CAST "FO"); break;
1995 case DBTYPE_PFO: return(BAD_CAST "PFO"); break;
1996 case DBTYPE_PFO_ADVOK: return(BAD_CAST "PFO_ADVOK"); break;
1997 case DBTYPE_PFO_DANPOR: return(BAD_CAST "PFO_DANPOR"); break;
1998 case DBTYPE_PFO_INSSPR: return(BAD_CAST "PFO_INSSPR"); break;
1999 case DBTYPE_PO: return(BAD_CAST "PO"); break;
2000 case DBTYPE_PO_ZAK: return(BAD_CAST "PO_ZAK"); break;
2001 case DBTYPE_PO_REQ: return(BAD_CAST "PO_REQ"); break;
2002 case DBTYPE_OVM: return(BAD_CAST "OVM"); break;
2003 case DBTYPE_OVM_NOTAR: return(BAD_CAST "OVM_NOTAR"); break;
2004 case DBTYPE_OVM_EXEKUT: return(BAD_CAST "OVM_EXEKUT"); break;
2005 case DBTYPE_OVM_REQ: return(BAD_CAST "OVM_REQ"); break;
2006 default: return NULL; break;
2011 /* Convert UTF-8 @string representation of ISDS userType to enum @type */
2012 static isds_error string2isds_UserType(xmlChar *string, isds_UserType *type) {
2013 if (!string || !type) return IE_INVAL;
2015 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
2016 *type = USERTYPE_PRIMARY;
2017 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
2018 *type = USERTYPE_ENTRUSTED;
2019 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
2020 *type = USERTYPE_ADMINISTRATOR;
2021 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
2022 *type = USERTYPE_OFFICIAL;
2023 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
2024 *type = USERTYPE_OFFICIAL_CERT;
2025 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
2026 *type = USERTYPE_LIQUIDATOR;
2027 else
2028 return IE_ENUM;
2029 return IE_SUCCESS;
2033 /* Convert ISDS userType enum @type to UTF-8 string.
2034 * @Return pointer to static string, or NULL if unknown enum value */
2035 static const xmlChar *isds_UserType2string(const isds_UserType type) {
2036 switch(type) {
2037 case USERTYPE_PRIMARY: return(BAD_CAST "PRIMARY_USER"); break;
2038 case USERTYPE_ENTRUSTED: return(BAD_CAST "ENTRUSTED_USER"); break;
2039 case USERTYPE_ADMINISTRATOR: return(BAD_CAST "ADMINISTRATOR"); break;
2040 case USERTYPE_OFFICIAL: return(BAD_CAST "OFFICIAL"); break;
2041 case USERTYPE_OFFICIAL_CERT: return(BAD_CAST "OFFICIAL_CERT"); break;
2042 case USERTYPE_LIQUIDATOR: return(BAD_CAST "LIQUIDATOR"); break;
2043 default: return NULL; break;
2048 /* Convert UTF-8 @string representation of ISDS sender type to enum @type */
2049 static isds_error string2isds_sender_type(const xmlChar *string,
2050 isds_sender_type *type) {
2051 if (!string || !type) return IE_INVAL;
2053 if (!xmlStrcmp(string, BAD_CAST "PRIMARY_USER"))
2054 *type = SENDERTYPE_PRIMARY;
2055 else if (!xmlStrcmp(string, BAD_CAST "ENTRUSTED_USER"))
2056 *type = SENDERTYPE_ENTRUSTED;
2057 else if (!xmlStrcmp(string, BAD_CAST "ADMINISTRATOR"))
2058 *type = SENDERTYPE_ADMINISTRATOR;
2059 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL"))
2060 *type = SENDERTYPE_OFFICIAL;
2061 else if (!xmlStrcmp(string, BAD_CAST "VIRTUAL"))
2062 *type = SENDERTYPE_VIRTUAL;
2063 else if (!xmlStrcmp(string, BAD_CAST "OFFICIAL_CERT"))
2064 *type = SENDERTYPE_OFFICIAL_CERT;
2065 else if (!xmlStrcmp(string, BAD_CAST "LIQUIDATOR"))
2066 *type = SENDERTYPE_LIQUIDATOR;
2067 else
2068 return IE_ENUM;
2069 return IE_SUCCESS;
2073 /* Convert UTF-8 @string representation of ISDS PDZType to enum @type */
2074 static isds_error string2isds_payment_type(const xmlChar *string,
2075 isds_payment_type *type) {
2076 if (!string || !type) return IE_INVAL;
2078 if (!xmlStrcmp(string, BAD_CAST "K"))
2079 *type = PAYMENT_SENDER;
2080 else if (!xmlStrcmp(string, BAD_CAST "O"))
2081 *type = PAYMENT_RESPONSE;
2082 else if (!xmlStrcmp(string, BAD_CAST "G"))
2083 *type = PAYMENT_SPONSOR;
2084 else if (!xmlStrcmp(string, BAD_CAST "Z"))
2085 *type = PAYMENT_SPONSOR_LIMITED;
2086 else if (!xmlStrcmp(string, BAD_CAST "D"))
2087 *type = PAYMENT_SPONSOR_EXTERNAL;
2088 else if (!xmlStrcmp(string, BAD_CAST "E"))
2089 *type = PAYMENT_STAMP;
2090 else
2091 return IE_ENUM;
2092 return IE_SUCCESS;
2096 /* Convert UTF-8 @string representation of ISDS ciEventType to enum @type.
2097 * ciEventType is integer but we convert it from string representation
2098 * directly. */
2099 static isds_error string2isds_credit_event_type(const xmlChar *string,
2100 isds_credit_event_type *type) {
2101 if (!string || !type) return IE_INVAL;
2103 if (!xmlStrcmp(string, BAD_CAST "1"))
2104 *type = ISDS_CREDIT_CHARGED;
2105 else if (!xmlStrcmp(string, BAD_CAST "2"))
2106 *type = ISDS_CREDIT_DISCHARGED;
2107 else if (!xmlStrcmp(string, BAD_CAST "3"))
2108 *type = ISDS_CREDIT_MESSAGE_SENT;
2109 else if (!xmlStrcmp(string, BAD_CAST "4"))
2110 *type = ISDS_CREDIT_STORAGE_SET;
2111 else if (!xmlStrcmp(string, BAD_CAST "5"))
2112 *type = ISDS_CREDIT_EXPIRED;
2113 else
2114 return IE_ENUM;
2115 return IE_SUCCESS;
2119 /* Convert ISDS dmFileMetaType enum @type to UTF-8 string.
2120 * @Return pointer to static string, or NULL if unknown enum value */
2121 static const xmlChar *isds_FileMetaType2string(const isds_FileMetaType type) {
2122 switch(type) {
2123 case FILEMETATYPE_MAIN: return(BAD_CAST "main"); break;
2124 case FILEMETATYPE_ENCLOSURE: return(BAD_CAST "enclosure"); break;
2125 case FILEMETATYPE_SIGNATURE: return(BAD_CAST "signature"); break;
2126 case FILEMETATYPE_META: return(BAD_CAST "meta"); break;
2127 default: return NULL; break;
2130 #endif /* HAVE_LIBCURL */
2133 /* Convert UTF-8 @string to ISDS dmFileMetaType enum @type.
2134 * @Return IE_ENUM if @string is not valid enum member */
2135 static isds_error string2isds_FileMetaType(const xmlChar *string,
2136 isds_FileMetaType *type) {
2137 if (!string || !type) return IE_INVAL;
2139 if (!xmlStrcmp(string, BAD_CAST "main"))
2140 *type = FILEMETATYPE_MAIN;
2141 else if (!xmlStrcmp(string, BAD_CAST "enclosure"))
2142 *type = FILEMETATYPE_ENCLOSURE;
2143 else if (!xmlStrcmp(string, BAD_CAST "signature"))
2144 *type = FILEMETATYPE_SIGNATURE;
2145 else if (!xmlStrcmp(string, BAD_CAST "meta"))
2146 *type = FILEMETATYPE_META;
2147 else
2148 return IE_ENUM;
2149 return IE_SUCCESS;
2153 /* Convert UTF-8 @string to ISDS hash @algorithm.
2154 * @Return IE_ENUM if @string is not valid enum member */
2155 static isds_error string2isds_hash_algorithm(const xmlChar *string,
2156 isds_hash_algorithm *algorithm) {
2157 if (!string || !algorithm) return IE_INVAL;
2159 if (!xmlStrcmp(string, BAD_CAST "MD5"))
2160 *algorithm = HASH_ALGORITHM_MD5;
2161 else if (!xmlStrcmp(string, BAD_CAST "SHA-1"))
2162 *algorithm = HASH_ALGORITHM_SHA_1;
2163 else if (!xmlStrcmp(string, BAD_CAST "SHA-224"))
2164 *algorithm = HASH_ALGORITHM_SHA_224;
2165 else if (!xmlStrcmp(string, BAD_CAST "SHA-256"))
2166 *algorithm = HASH_ALGORITHM_SHA_256;
2167 else if (!xmlStrcmp(string, BAD_CAST "SHA-384"))
2168 *algorithm = HASH_ALGORITHM_SHA_384;
2169 else if (!xmlStrcmp(string, BAD_CAST "SHA-512"))
2170 *algorithm = HASH_ALGORITHM_SHA_512;
2171 else
2172 return IE_ENUM;
2173 return IE_SUCCESS;
2177 #if HAVE_LIBCURL
2178 /* Convert struct tm *@time to UTF-8 ISO 8601 date @string. */
2179 static isds_error tm2datestring(const struct tm *time, xmlChar **string) {
2180 if (!time || !string) return IE_INVAL;
2182 if (-1 == isds_asprintf((char **) string, "%d-%02d-%02d",
2183 time->tm_year + 1900, time->tm_mon + 1, time->tm_mday))
2184 return IE_ERROR;
2186 return IE_SUCCESS;
2190 /* Convert struct timeval * @time to UTF-8 ISO 8601 date-time @string. It
2191 * respects the @time microseconds too. */
2192 static isds_error timeval2timestring(const struct timeval *time,
2193 xmlChar **string) {
2194 struct tm broken;
2196 if (!time || !string) return IE_INVAL;
2198 if (!gmtime_r(&time->tv_sec, &broken)) return IE_DATE;
2199 if (time->tv_usec < 0 || time->tv_usec > 999999) return IE_DATE;
2201 /* TODO: small negative year should be formatted as "-0012". This is not
2202 * true for glibc "%04d". We should implement it.
2203 * TODO: What's type of time->tv_usec exactly? Unsigned? Absolute?
2204 * See <http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/#dateTime> */
2205 if (-1 == isds_asprintf((char **) string,
2206 "%04d-%02d-%02dT%02d:%02d:%02d.%06ld",
2207 broken.tm_year + 1900, broken.tm_mon + 1, broken.tm_mday,
2208 broken.tm_hour, broken.tm_min, broken.tm_sec,
2209 time->tv_usec))
2210 return IE_ERROR;
2212 return IE_SUCCESS;
2214 #endif /* HAVE_LIBCURL */
2217 /* Convert UTF-8 ISO 8601 date-time @string to struct timeval.
2218 * It respects microseconds too.
2219 * In case of error, @time will be freed. */
2220 static isds_error timestring2timeval(const xmlChar *string,
2221 struct timeval **time) {
2222 struct tm broken;
2223 char *offset, *delim, *endptr;
2224 char subseconds[7];
2225 int offset_hours, offset_minutes;
2226 int i;
2227 #ifdef _WIN32
2228 int tmp;
2229 #endif
2231 if (!time) return IE_INVAL;
2232 if (!string) {
2233 zfree(*time);
2234 return IE_INVAL;
2237 memset(&broken, 0, sizeof(broken));
2239 if (!*time) {
2240 *time = calloc(1, sizeof(**time));
2241 if (!*time) return IE_NOMEM;
2242 } else {
2243 memset(*time, 0, sizeof(**time));
2247 /* xsd:date is ISO 8601 string, thus ASCII */
2248 /*TODO: negative year */
2250 #ifdef _WIN32
2251 i = 0;
2252 if ((tmp = sscanf((const char*)string, "%d-%d-%dT%d:%d:%d%n",
2253 &broken.tm_year, &broken.tm_mon, &broken.tm_mday,
2254 &broken.tm_hour, &broken.tm_min, &broken.tm_sec,
2255 &i)) < 6) {
2256 zfree(*time);
2257 return IE_DATE;
2260 broken.tm_year -= 1900;
2261 broken.tm_mon--;
2262 offset = (char*)string + i;
2263 #else
2264 /* Parse date and time without subseconds and offset */
2265 offset = strptime((char*)string, "%Y-%m-%dT%T", &broken);
2266 if (!offset) {
2267 zfree(*time);
2268 return IE_DATE;
2270 #endif
2272 /* Get subseconds */
2273 if (*offset == '.' ) {
2274 offset++;
2276 /* Copy first 6 digits, pad it with zeros.
2277 * XXX: It truncates longer number, no round.
2278 * Current server implementation uses only millisecond resolution. */
2279 /* TODO: isdigit() is locale sensitive */
2280 for (i = 0;
2281 i < sizeof(subseconds)/sizeof(char) - 1 && isdigit(*offset);
2282 i++, offset++) {
2283 subseconds[i] = *offset;
2285 for (; i < sizeof(subseconds)/sizeof(char) - 1; i++) {
2286 subseconds[i] = '0';
2288 subseconds[6] = '\0';
2290 /* Convert it into integer */
2291 (*time)->tv_usec = strtol(subseconds, &endptr, 10);
2292 if (*endptr != '\0' || (*time)->tv_usec == LONG_MIN ||
2293 (*time)->tv_usec == LONG_MAX) {
2294 zfree(*time);
2295 return IE_DATE;
2298 /* move to the zone offset delimiter or signal NULL*/
2299 delim = strchr(offset, '-');
2300 if (!delim)
2301 delim = strchr(offset, '+');
2302 if (!delim)
2303 delim = strchr(offset, 'Z');
2304 offset = delim;
2307 /* Get zone offset */
2308 /* ISO allows zone offset string only: "" | "Z" | ("+"|"-" "<HH>:<MM>")
2309 * "" equals to "Z" and it means UTC zone. */
2310 /* One can not use strptime(, "%z",) becase it's RFC E-MAIL format without
2311 * colon separator */
2312 if (offset && (*offset == '-' || *offset == '+')) {
2313 if (2 != sscanf(offset + 1, "%2d:%2d", &offset_hours, &offset_minutes)) {
2314 zfree(*time);
2315 return IE_DATE;
2317 if (*offset == '+') {
2318 broken.tm_hour -= offset_hours;
2319 broken.tm_min -= offset_minutes;
2320 } else {
2321 broken.tm_hour += offset_hours;
2322 broken.tm_min += offset_minutes;
2326 /* Convert to time_t */
2327 (*time)->tv_sec = _isds_timegm(&broken);
2328 if ((*time)->tv_sec == (time_t) -1) {
2329 zfree(*time);
2330 return IE_DATE;
2333 return IE_SUCCESS;
2337 /* Convert unsigned int into isds_message_status.
2338 * @context is session context
2339 * @number is pointer to number value. NULL will be treated as invalid value.
2340 * @status is automatically reallocated status
2341 * @return IE_SUCCESS, or error code and free status */
2342 static isds_error uint2isds_message_status(struct isds_ctx *context,
2343 const unsigned long int *number, isds_message_status **status) {
2344 if (!context) return IE_INVALID_CONTEXT;
2345 if (!status) return IE_INVAL;
2347 free(*status); *status = NULL;
2348 if (!number) return IE_INVAL;
2350 if (*number < 1 || *number > 10) {
2351 isds_printf_message(context, _("Invalid message status value: %lu"),
2352 *number);
2353 return IE_ENUM;
2356 *status = malloc(sizeof(**status));
2357 if (!*status) return IE_NOMEM;
2359 **status = 1 << *number;
2360 return IE_SUCCESS;
2364 /* Convert event description string into isds_event members type and
2365 * description
2366 * @string is raw event description starting with event prefix
2367 * @event is structure where to store type and stripped description to
2368 * @return standard error code, unknown prefix is not classified as an error.
2369 * */
2370 static isds_error eventstring2event(const xmlChar *string,
2371 struct isds_event* event) {
2372 const xmlChar *known_prefixes[] = {
2373 BAD_CAST "EV0:",
2374 BAD_CAST "EV1:",
2375 BAD_CAST "EV2:",
2376 BAD_CAST "EV3:",
2377 BAD_CAST "EV4:",
2378 BAD_CAST "EV5:",
2379 BAD_CAST "EV11:",
2380 BAD_CAST "EV12:",
2381 BAD_CAST "EV13:"
2383 const isds_event_type types[] = {
2384 EVENT_ENTERED_SYSTEM,
2385 EVENT_ACCEPTED_BY_RECIPIENT,
2386 EVENT_ACCEPTED_BY_FICTION,
2387 EVENT_UNDELIVERABLE,
2388 EVENT_COMMERCIAL_ACCEPTED,
2389 EVENT_DELIVERED,
2390 EVENT_PRIMARY_LOGIN,
2391 EVENT_ENTRUSTED_LOGIN,
2392 EVENT_SYSCERT_LOGIN
2394 unsigned int index;
2395 size_t length;
2397 if (!string || !event) return IE_INVAL;
2399 if (!event->type) {
2400 event->type = malloc(sizeof(*event->type));
2401 if (!(event->type)) return IE_NOMEM;
2403 zfree(event->description);
2405 for (index = 0; index < sizeof(known_prefixes)/sizeof(known_prefixes[0]);
2406 index++) {
2407 length = xmlUTF8Strlen(known_prefixes[index]);
2409 if (!xmlStrncmp(string, known_prefixes[index], length)) {
2410 /* Prefix is known */
2411 *event->type = types[index];
2413 /* Strip prefix from description and spaces */
2414 /* TODO: Recognize all white spaces from UCS blank class and
2415 * operate on UTF-8 chars. */
2416 for (; string[length] != '\0' && string[length] == ' '; length++);
2417 event->description = strdup((char *) (string + length));
2418 if (!(event->description)) return IE_NOMEM;
2420 return IE_SUCCESS;
2424 /* Unknown event prefix.
2425 * XSD allows any string */
2426 char *string_locale = _isds_utf82locale((char *) string);
2427 isds_log(ILF_ISDS, ILL_WARNING,
2428 _("Unknown delivery info event prefix: %s\n"), string_locale);
2429 free(string_locale);
2431 *event->type = EVENT_UKNOWN;
2432 event->description = strdup((char *) string);
2433 if (!(event->description)) return IE_NOMEM;
2435 return IE_SUCCESS;
2439 /* Following EXTRACT_* macros expect @result, @xpath_ctx, @err, @context
2440 * and leave label */
2441 #define EXTRACT_STRING(element, string) { \
2442 xmlXPathFreeObject(result); \
2443 result = xmlXPathEvalExpression(BAD_CAST element "/text()", xpath_ctx); \
2444 if (NULL == (result)) { \
2445 err = IE_ERROR; \
2446 goto leave; \
2448 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) { \
2449 if (result->nodesetval->nodeNr > 1) { \
2450 isds_printf_message(context, _("Multiple %s element"), element); \
2451 err = IE_ERROR; \
2452 goto leave; \
2454 (string) = (char *) \
2455 xmlXPathCastNodeSetToString(result->nodesetval); \
2456 if (NULL == (string)) { \
2457 err = IE_ERROR; \
2458 goto leave; \
2463 #define EXTRACT_BOOLEAN(element, booleanPtr) \
2465 char *string = NULL; \
2466 EXTRACT_STRING(element, string); \
2468 if (string) { \
2469 (booleanPtr) = calloc(1, sizeof(*(booleanPtr))); \
2470 if (!(booleanPtr)) { \
2471 free(string); \
2472 err = IE_NOMEM; \
2473 goto leave; \
2476 if (!xmlStrcmp((xmlChar *)string, BAD_CAST "true") || \
2477 !xmlStrcmp((xmlChar *)string, BAD_CAST "1")) \
2478 *(booleanPtr) = 1; \
2479 else if (!xmlStrcmp((xmlChar *)string, BAD_CAST "false") || \
2480 !xmlStrcmp((xmlChar *)string, BAD_CAST "0")) \
2481 *(booleanPtr) = 0; \
2482 else { \
2483 char *string_locale = _isds_utf82locale((char*)string); \
2484 isds_printf_message(context, \
2485 _("%s value is not valid boolean: %s"), \
2486 element, string_locale); \
2487 free(string_locale); \
2488 free(string); \
2489 err = IE_ERROR; \
2490 goto leave; \
2493 free(string); \
2497 #define EXTRACT_LONGINT(element, longintPtr, preallocated) \
2499 char *string = NULL; \
2500 EXTRACT_STRING(element, string); \
2501 if (string) { \
2502 long int number; \
2503 char *endptr; \
2505 number = strtol((char*)string, &endptr, 10); \
2507 if (*endptr != '\0') { \
2508 char *string_locale = _isds_utf82locale((char *)string); \
2509 isds_printf_message(context, \
2510 _("%s is not valid integer: %s"), \
2511 element, string_locale); \
2512 free(string_locale); \
2513 free(string); \
2514 err = IE_ISDS; \
2515 goto leave; \
2518 if (number == LONG_MIN || number == LONG_MAX) { \
2519 char *string_locale = _isds_utf82locale((char *)string); \
2520 isds_printf_message(context, \
2521 _("%s value out of range of long int: %s"), \
2522 element, string_locale); \
2523 free(string_locale); \
2524 free(string); \
2525 err = IE_ERROR; \
2526 goto leave; \
2529 free(string); string = NULL; \
2531 if (!(preallocated)) { \
2532 (longintPtr) = calloc(1, sizeof(*(longintPtr))); \
2533 if (!(longintPtr)) { \
2534 err = IE_NOMEM; \
2535 goto leave; \
2538 *(longintPtr) = number; \
2542 #define EXTRACT_ULONGINT(element, ulongintPtr, preallocated) \
2544 char *string = NULL; \
2545 EXTRACT_STRING(element, string); \
2546 if (string) { \
2547 long int number; \
2548 char *endptr; \
2550 number = strtol((char*)string, &endptr, 10); \
2552 if (*endptr != '\0') { \
2553 char *string_locale = _isds_utf82locale((char *)string); \
2554 isds_printf_message(context, \
2555 _("%s is not valid integer: %s"), \
2556 element, string_locale); \
2557 free(string_locale); \
2558 free(string); \
2559 err = IE_ISDS; \
2560 goto leave; \
2563 if (number == LONG_MIN || number == LONG_MAX) { \
2564 char *string_locale = _isds_utf82locale((char *)string); \
2565 isds_printf_message(context, \
2566 _("%s value out of range of long int: %s"), \
2567 element, string_locale); \
2568 free(string_locale); \
2569 free(string); \
2570 err = IE_ERROR; \
2571 goto leave; \
2574 free(string); string = NULL; \
2575 if (number < 0) { \
2576 isds_printf_message(context, \
2577 _("%s value is negative: %ld"), element, number); \
2578 err = IE_ERROR; \
2579 goto leave; \
2582 if (!(preallocated)) { \
2583 (ulongintPtr) = calloc(1, sizeof(*(ulongintPtr))); \
2584 if (!(ulongintPtr)) { \
2585 err = IE_NOMEM; \
2586 goto leave; \
2589 *(ulongintPtr) = number; \
2593 #define EXTRACT_DATE(element, tmPtr) { \
2594 char *string = NULL; \
2595 EXTRACT_STRING(element, string); \
2596 if (NULL != string) { \
2597 (tmPtr) = calloc(1, sizeof(*(tmPtr))); \
2598 if (NULL == (tmPtr)) { \
2599 free(string); \
2600 err = IE_NOMEM; \
2601 goto leave; \
2603 err = _isds_datestring2tm((xmlChar *)string, (tmPtr)); \
2604 if (err) { \
2605 if (err == IE_NOTSUP) { \
2606 err = IE_ISDS; \
2607 char *string_locale = _isds_utf82locale(string); \
2608 char *element_locale = _isds_utf82locale(element); \
2609 isds_printf_message(context, _("Invalid %s value: %s"), \
2610 element_locale, string_locale); \
2611 free(string_locale); \
2612 free(element_locale); \
2614 free(string); \
2615 goto leave; \
2617 free(string); \
2621 #define EXTRACT_STRING_ATTRIBUTE(attribute, string, required) { \
2622 (string) = (char *) xmlGetNsProp(xpath_ctx->node, ( BAD_CAST attribute), \
2623 NULL); \
2624 if ((required) && (!string)) { \
2625 char *attribute_locale = _isds_utf82locale(attribute); \
2626 char *element_locale = \
2627 _isds_utf82locale((char *)xpath_ctx->node->name); \
2628 isds_printf_message(context, \
2629 _("Could not extract required %s attribute value from " \
2630 "%s element"), attribute_locale, element_locale); \
2631 free(element_locale); \
2632 free(attribute_locale); \
2633 err = IE_ERROR; \
2634 goto leave; \
2639 #define INSERT_STRING_WITH_NS(parent, ns, element, string) \
2641 node = xmlNewTextChild(parent, ns, BAD_CAST (element), \
2642 (xmlChar *) (string)); \
2643 if (!node) { \
2644 isds_printf_message(context, \
2645 _("Could not add %s child to %s element"), \
2646 element, (parent)->name); \
2647 err = IE_ERROR; \
2648 goto leave; \
2652 #define INSERT_STRING(parent, element, string) \
2653 { INSERT_STRING_WITH_NS(parent, NULL, element, string) }
2655 #define INSERT_SCALAR_BOOLEAN(parent, element, boolean) \
2657 if (boolean) { INSERT_STRING(parent, element, "true"); } \
2658 else { INSERT_STRING(parent, element, "false"); } \
2661 #define INSERT_BOOLEAN(parent, element, booleanPtr) \
2663 if (booleanPtr) { \
2664 INSERT_SCALAR_BOOLEAN(parent, element, (*(booleanPtr))); \
2665 } else { \
2666 INSERT_STRING(parent, element, NULL); \
2670 #define INSERT_LONGINT(parent, element, longintPtr, buffer) { \
2671 if ((longintPtr)) { \
2672 /* FIXME: locale sensitive */ \
2673 if (-1 == isds_asprintf((char **)&(buffer), "%ld", *(longintPtr))) { \
2674 err = IE_NOMEM; \
2675 goto leave; \
2677 INSERT_STRING(parent, element, buffer) \
2678 free(buffer); (buffer) = NULL; \
2679 } else { INSERT_STRING(parent, element, NULL) } \
2682 #define INSERT_ULONGINT(parent, element, ulongintPtr, buffer) { \
2683 if ((ulongintPtr)) { \
2684 /* FIXME: locale sensitive */ \
2685 if (-1 == isds_asprintf((char **)&(buffer), "%lu", *(ulongintPtr))) { \
2686 err = IE_NOMEM; \
2687 goto leave; \
2689 INSERT_STRING(parent, element, buffer) \
2690 free(buffer); (buffer) = NULL; \
2691 } else { INSERT_STRING(parent, element, NULL) } \
2694 #define INSERT_ULONGINTNOPTR(parent, element, ulongint, buffer) \
2696 /* FIXME: locale sensitive */ \
2697 if (-1 == isds_asprintf((char **)&(buffer), "%lu", ulongint)) { \
2698 err = IE_NOMEM; \
2699 goto leave; \
2701 INSERT_STRING(parent, element, buffer) \
2702 free(buffer); (buffer) = NULL; \
2705 /* Requires attribute_node variable, do not free it. Can be used to reffer to
2706 * new attribute. */
2707 #define INSERT_STRING_ATTRIBUTE(parent, attribute, string) \
2709 attribute_node = xmlNewProp((parent), BAD_CAST (attribute), \
2710 (xmlChar *) (string)); \
2711 if (!attribute_node) { \
2712 isds_printf_message(context, _("Could not add %s " \
2713 "attribute to %s element"), \
2714 (attribute), (parent)->name); \
2715 err = IE_ERROR; \
2716 goto leave; \
2720 #define CHECK_FOR_STRING_LENGTH(string, minimum, maximum, name) { \
2721 if (string) { \
2722 int length = xmlUTF8Strlen((xmlChar *) (string)); \
2723 if (length > (maximum)) { \
2724 isds_printf_message(context, \
2725 ngettext("%s has more than %d characters", \
2726 "%s has more than %d characters", (maximum)), \
2727 (name), (maximum)); \
2728 err = IE_2BIG; \
2729 goto leave; \
2731 if (length < (minimum)) { \
2732 isds_printf_message(context, \
2733 ngettext("%s has less than %d characters", \
2734 "%s has less than %d characters", (minimum)), \
2735 (name), (minimum)); \
2736 err = IE_2SMALL; \
2737 goto leave; \
2742 #define INSERT_ELEMENT(child, parent, element) \
2744 (child) = xmlNewChild((parent), NULL, BAD_CAST (element), NULL); \
2745 if (!(child)) { \
2746 isds_printf_message(context, \
2747 _("Could not add %s child to %s element"), \
2748 (element), (parent)->name); \
2749 err = IE_ERROR; \
2750 goto leave; \
2755 /* Find child element by name in given XPath context and switch context onto
2756 * it. The child must be uniq and must exist. Otherwise fails.
2757 * @context is ISDS context
2758 * @child is child element name
2759 * @xpath_ctx is XPath context. In success, the @xpath_ctx will be changed
2760 * into it child. In error case, the @xpath_ctx keeps original value. */
2761 static isds_error move_xpathctx_to_child(struct isds_ctx *context,
2762 const xmlChar *child, xmlXPathContextPtr xpath_ctx) {
2763 isds_error err = IE_SUCCESS;
2764 xmlXPathObjectPtr result = NULL;
2766 if (!context) return IE_INVALID_CONTEXT;
2767 if (!child || !xpath_ctx) return IE_INVAL;
2769 /* Find child */
2770 result = xmlXPathEvalExpression(child, xpath_ctx);
2771 if (!result) {
2772 err = IE_XML;
2773 goto leave;
2776 /* No match */
2777 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
2778 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2779 char *child_locale = _isds_utf82locale((char*) child);
2780 isds_printf_message(context,
2781 _("%s element does not contain %s child"),
2782 parent_locale, child_locale);
2783 free(child_locale);
2784 free(parent_locale);
2785 err = IE_NOEXIST;
2786 goto leave;
2789 /* More matches */
2790 if (result->nodesetval->nodeNr > 1) {
2791 char *parent_locale = _isds_utf82locale((char*) xpath_ctx->node->name);
2792 char *child_locale = _isds_utf82locale((char*) child);
2793 isds_printf_message(context,
2794 _("%s element contains multiple %s children"),
2795 parent_locale, child_locale);
2796 free(child_locale);
2797 free(parent_locale);
2798 err = IE_NOTUNIQ;
2799 goto leave;
2802 /* Switch context */
2803 xpath_ctx->node = result->nodesetval->nodeTab[0];
2805 leave:
2806 xmlXPathFreeObject(result);
2807 return err;
2812 #if HAVE_LIBCURL
2813 /* Find and convert XSD:gPersonName group in current node into structure
2814 * @context is ISDS context
2815 * @personName is automatically reallocated person name structure. If no member
2816 * value is found, will be freed.
2817 * @xpath_ctx is XPath context with current node as parent for XSD:gPersonName
2818 * elements
2819 * In case of error @personName will be freed. */
2820 static isds_error extract_gPersonName(struct isds_ctx *context,
2821 struct isds_PersonName **personName, xmlXPathContextPtr xpath_ctx) {
2822 isds_error err = IE_SUCCESS;
2823 xmlXPathObjectPtr result = NULL;
2825 if (!context) return IE_INVALID_CONTEXT;
2826 if (!personName) return IE_INVAL;
2827 isds_PersonName_free(personName);
2828 if (!xpath_ctx) return IE_INVAL;
2831 *personName = calloc(1, sizeof(**personName));
2832 if (!*personName) {
2833 err = IE_NOMEM;
2834 goto leave;
2837 EXTRACT_STRING("isds:pnFirstName", (*personName)->pnFirstName);
2838 EXTRACT_STRING("isds:pnMiddleName", (*personName)->pnMiddleName);
2839 EXTRACT_STRING("isds:pnLastName", (*personName)->pnLastName);
2840 EXTRACT_STRING("isds:pnLastNameAtBirth", (*personName)->pnLastNameAtBirth);
2842 if (!(*personName)->pnFirstName && !(*personName)->pnMiddleName &&
2843 !(*personName)->pnLastName && !(*personName)->pnLastNameAtBirth)
2844 isds_PersonName_free(personName);
2846 leave:
2847 if (err) isds_PersonName_free(personName);
2848 xmlXPathFreeObject(result);
2849 return err;
2853 /* Find and convert XSD:gAddress group in current node into structure
2854 * @context is ISDS context
2855 * @address is automatically reallocated address structure. If no member
2856 * value is found, will be freed.
2857 * @xpath_ctx is XPath context with current node as parent for XSD:gAddress
2858 * elements
2859 * In case of error @address will be freed. */
2860 static isds_error extract_gAddress(struct isds_ctx *context,
2861 struct isds_Address **address, xmlXPathContextPtr xpath_ctx) {
2862 isds_error err = IE_SUCCESS;
2863 xmlXPathObjectPtr result = NULL;
2865 if (!context) return IE_INVALID_CONTEXT;
2866 if (!address) return IE_INVAL;
2867 isds_Address_free(address);
2868 if (!xpath_ctx) return IE_INVAL;
2871 *address = calloc(1, sizeof(**address));
2872 if (!*address) {
2873 err = IE_NOMEM;
2874 goto leave;
2877 EXTRACT_STRING("isds:adCity", (*address)->adCity);
2878 EXTRACT_STRING("isds:adStreet", (*address)->adStreet);
2879 EXTRACT_STRING("isds:adNumberInStreet", (*address)->adNumberInStreet);
2880 EXTRACT_STRING("isds:adNumberInMunicipality",
2881 (*address)->adNumberInMunicipality);
2882 EXTRACT_STRING("isds:adZipCode", (*address)->adZipCode);
2883 EXTRACT_STRING("isds:adState", (*address)->adState);
2885 if (!(*address)->adCity && !(*address)->adStreet &&
2886 !(*address)->adNumberInStreet &&
2887 !(*address)->adNumberInMunicipality &&
2888 !(*address)->adZipCode && !(*address)->adState)
2889 isds_Address_free(address);
2891 leave:
2892 if (err) isds_Address_free(address);
2893 xmlXPathFreeObject(result);
2894 return err;
2898 /* Find and convert isds:biDate element in current node into structure
2899 * @context is ISDS context
2900 * @biDate is automatically reallocated birth date structure. If no member
2901 * value is found, will be freed.
2902 * @xpath_ctx is XPath context with current node as parent for isds:biDate
2903 * element
2904 * In case of error @biDate will be freed. */
2905 static isds_error extract_BiDate(struct isds_ctx *context,
2906 struct tm **biDate, xmlXPathContextPtr xpath_ctx) {
2907 isds_error err = IE_SUCCESS;
2908 xmlXPathObjectPtr result = NULL;
2909 char *string = NULL;
2911 if (!context) return IE_INVALID_CONTEXT;
2912 if (!biDate) return IE_INVAL;
2913 zfree(*biDate);
2914 if (!xpath_ctx) return IE_INVAL;
2916 EXTRACT_STRING("isds:biDate", string);
2917 if (string) {
2918 *biDate = calloc(1, sizeof(**biDate));
2919 if (!*biDate) {
2920 err = IE_NOMEM;
2921 goto leave;
2923 err = _isds_datestring2tm((xmlChar *)string, *biDate);
2924 if (err) {
2925 if (err == IE_NOTSUP) {
2926 err = IE_ISDS;
2927 char *string_locale = _isds_utf82locale(string);
2928 isds_printf_message(context,
2929 _("Invalid isds:biDate value: %s"), string_locale);
2930 free(string_locale);
2932 goto leave;
2936 leave:
2937 if (err) zfree(*biDate);
2938 free(string);
2939 xmlXPathFreeObject(result);
2940 return err;
2944 /* Convert isds:dBOwnerInfo XML tree into structure
2945 * @context is ISDS context
2946 * @db_owner_info is automatically reallocated box owner info structure
2947 * @xpath_ctx is XPath context with current node as isds:dBOwnerInfo element
2948 * In case of error @db_owner_info will be freed. */
2949 static isds_error extract_DbOwnerInfo(struct isds_ctx *context,
2950 struct isds_DbOwnerInfo **db_owner_info,
2951 xmlXPathContextPtr xpath_ctx) {
2952 isds_error err = IE_SUCCESS;
2953 xmlXPathObjectPtr result = NULL;
2954 char *string = NULL;
2956 if (!context) return IE_INVALID_CONTEXT;
2957 if (!db_owner_info) return IE_INVAL;
2958 isds_DbOwnerInfo_free(db_owner_info);
2959 if (!xpath_ctx) return IE_INVAL;
2962 *db_owner_info = calloc(1, sizeof(**db_owner_info));
2963 if (!*db_owner_info) {
2964 err = IE_NOMEM;
2965 goto leave;
2968 EXTRACT_STRING("isds:dbID", (*db_owner_info)->dbID);
2970 EXTRACT_STRING("isds:dbType", string);
2971 if (string) {
2972 (*db_owner_info)->dbType =
2973 calloc(1, sizeof(*((*db_owner_info)->dbType)));
2974 if (!(*db_owner_info)->dbType) {
2975 err = IE_NOMEM;
2976 goto leave;
2978 err = string2isds_DbType((xmlChar *)string, (*db_owner_info)->dbType);
2979 if (err) {
2980 zfree((*db_owner_info)->dbType);
2981 if (err == IE_ENUM) {
2982 err = IE_ISDS;
2983 char *string_locale = _isds_utf82locale(string);
2984 isds_printf_message(context, _("Unknown isds:dbType: %s"),
2985 string_locale);
2986 free(string_locale);
2988 goto leave;
2990 zfree(string);
2993 EXTRACT_STRING("isds:ic", (*db_owner_info)->ic);
2995 err = extract_gPersonName(context, &(*db_owner_info)->personName,
2996 xpath_ctx);
2997 if (err) goto leave;
2999 EXTRACT_STRING("isds:firmName", (*db_owner_info)->firmName);
3001 (*db_owner_info)->birthInfo =
3002 calloc(1, sizeof(*((*db_owner_info)->birthInfo)));
3003 if (!(*db_owner_info)->birthInfo) {
3004 err = IE_NOMEM;
3005 goto leave;
3007 err = extract_BiDate(context, &(*db_owner_info)->birthInfo->biDate,
3008 xpath_ctx);
3009 if (err) goto leave;
3010 EXTRACT_STRING("isds:biCity", (*db_owner_info)->birthInfo->biCity);
3011 EXTRACT_STRING("isds:biCounty", (*db_owner_info)->birthInfo->biCounty);
3012 EXTRACT_STRING("isds:biState", (*db_owner_info)->birthInfo->biState);
3013 if (!(*db_owner_info)->birthInfo->biDate &&
3014 !(*db_owner_info)->birthInfo->biCity &&
3015 !(*db_owner_info)->birthInfo->biCounty &&
3016 !(*db_owner_info)->birthInfo->biState)
3017 isds_BirthInfo_free(&(*db_owner_info)->birthInfo);
3019 err = extract_gAddress(context, &(*db_owner_info)->address, xpath_ctx);
3020 if (err) goto leave;
3022 EXTRACT_STRING("isds:nationality", (*db_owner_info)->nationality);
3023 EXTRACT_STRING("isds:email", (*db_owner_info)->email);
3024 EXTRACT_STRING("isds:telNumber", (*db_owner_info)->telNumber);
3025 EXTRACT_STRING("isds:identifier", (*db_owner_info)->identifier);
3026 EXTRACT_STRING("isds:registryCode", (*db_owner_info)->registryCode);
3028 EXTRACT_LONGINT("isds:dbState", (*db_owner_info)->dbState, 0);
3030 EXTRACT_BOOLEAN("isds:dbEffectiveOVM", (*db_owner_info)->dbEffectiveOVM);
3031 EXTRACT_BOOLEAN("isds:dbOpenAddressing",
3032 (*db_owner_info)->dbOpenAddressing);
3034 leave:
3035 if (err) isds_DbOwnerInfo_free(db_owner_info);
3036 free(string);
3037 xmlXPathFreeObject(result);
3038 return err;
3042 /* Insert struct isds_DbOwnerInfo data (box description) into XML tree
3043 * @context is session context
3044 * @owner is libisds structure with box description
3045 * @db_owner_info is XML element of XSD:tDbOwnerInfo */
3046 static isds_error insert_DbOwnerInfo(struct isds_ctx *context,
3047 const struct isds_DbOwnerInfo *owner, xmlNodePtr db_owner_info) {
3049 isds_error err = IE_SUCCESS;
3050 xmlNodePtr node;
3051 xmlChar *string = NULL;
3053 if (!context) return IE_INVALID_CONTEXT;
3054 if (!owner || !db_owner_info) return IE_INVAL;
3057 /* Build XSD:tDbOwnerInfo */
3058 CHECK_FOR_STRING_LENGTH(owner->dbID, 0, 7, "dbID")
3059 INSERT_STRING(db_owner_info, "dbID", owner->dbID);
3061 /* dbType */
3062 if (owner->dbType) {
3063 const xmlChar *type_string = isds_DbType2string(*(owner->dbType));
3064 if (!type_string) {
3065 isds_printf_message(context, _("Invalid dbType value: %d"),
3066 *(owner->dbType));
3067 err = IE_ENUM;
3068 goto leave;
3070 INSERT_STRING(db_owner_info, "dbType", type_string);
3072 INSERT_STRING(db_owner_info, "ic", owner->ic);
3073 if (owner->personName) {
3074 INSERT_STRING(db_owner_info, "pnFirstName",
3075 owner->personName->pnFirstName);
3076 INSERT_STRING(db_owner_info, "pnMiddleName",
3077 owner->personName->pnMiddleName);
3078 INSERT_STRING(db_owner_info, "pnLastName",
3079 owner->personName->pnLastName);
3080 INSERT_STRING(db_owner_info, "pnLastNameAtBirth",
3081 owner->personName->pnLastNameAtBirth);
3083 INSERT_STRING(db_owner_info, "firmName", owner->firmName);
3084 if (owner->birthInfo) {
3085 if (owner->birthInfo->biDate) {
3086 if (!tm2datestring(owner->birthInfo->biDate, &string))
3087 INSERT_STRING(db_owner_info, "biDate", string);
3088 free(string); string = NULL;
3090 INSERT_STRING(db_owner_info, "biCity", owner->birthInfo->biCity);
3091 INSERT_STRING(db_owner_info, "biCounty", owner->birthInfo->biCounty);
3092 INSERT_STRING(db_owner_info, "biState", owner->birthInfo->biState);
3094 if (owner->address) {
3095 INSERT_STRING(db_owner_info, "adCity", owner->address->adCity);
3096 INSERT_STRING(db_owner_info, "adStreet", owner->address->adStreet);
3097 INSERT_STRING(db_owner_info, "adNumberInStreet",
3098 owner->address->adNumberInStreet);
3099 INSERT_STRING(db_owner_info, "adNumberInMunicipality",
3100 owner->address->adNumberInMunicipality);
3101 INSERT_STRING(db_owner_info, "adZipCode", owner->address->adZipCode);
3102 INSERT_STRING(db_owner_info, "adState", owner->address->adState);
3104 INSERT_STRING(db_owner_info, "nationality", owner->nationality);
3105 INSERT_STRING(db_owner_info, "email", owner->email);
3106 INSERT_STRING(db_owner_info, "telNumber", owner->telNumber);
3108 CHECK_FOR_STRING_LENGTH(owner->identifier, 0, 20, "identifier")
3109 INSERT_STRING(db_owner_info, "identifier", owner->identifier);
3111 CHECK_FOR_STRING_LENGTH(owner->registryCode, 0, 5, "registryCode")
3112 INSERT_STRING(db_owner_info, "registryCode", owner->registryCode);
3114 INSERT_LONGINT(db_owner_info, "dbState", owner->dbState, string);
3116 INSERT_BOOLEAN(db_owner_info, "dbEffectiveOVM", owner->dbEffectiveOVM);
3117 INSERT_BOOLEAN(db_owner_info, "dbOpenAddressing",
3118 owner->dbOpenAddressing);
3120 leave:
3121 free(string);
3122 return err;
3126 /* Convert XSD:tDbUserInfo XML tree into structure
3127 * @context is ISDS context
3128 * @db_user_info is automatically reallocated user info structure
3129 * @xpath_ctx is XPath context with current node as XSD:tDbUserInfo element
3130 * In case of error @db_user_info will be freed. */
3131 static isds_error extract_DbUserInfo(struct isds_ctx *context,
3132 struct isds_DbUserInfo **db_user_info, xmlXPathContextPtr xpath_ctx) {
3133 isds_error err = IE_SUCCESS;
3134 xmlXPathObjectPtr result = NULL;
3135 char *string = NULL;
3137 if (!context) return IE_INVALID_CONTEXT;
3138 if (!db_user_info) return IE_INVAL;
3139 isds_DbUserInfo_free(db_user_info);
3140 if (!xpath_ctx) return IE_INVAL;
3143 *db_user_info = calloc(1, sizeof(**db_user_info));
3144 if (!*db_user_info) {
3145 err = IE_NOMEM;
3146 goto leave;
3149 EXTRACT_STRING("isds:userID", (*db_user_info)->userID);
3151 EXTRACT_STRING("isds:userType", string);
3152 if (string) {
3153 (*db_user_info)->userType =
3154 calloc(1, sizeof(*((*db_user_info)->userType)));
3155 if (!(*db_user_info)->userType) {
3156 err = IE_NOMEM;
3157 goto leave;
3159 err = string2isds_UserType((xmlChar *)string,
3160 (*db_user_info)->userType);
3161 if (err) {
3162 zfree((*db_user_info)->userType);
3163 if (err == IE_ENUM) {
3164 err = IE_ISDS;
3165 char *string_locale = _isds_utf82locale(string);
3166 isds_printf_message(context,
3167 _("Unknown isds:userType value: %s"), string_locale);
3168 free(string_locale);
3170 goto leave;
3172 zfree(string);
3175 EXTRACT_LONGINT("isds:userPrivils", (*db_user_info)->userPrivils, 0);
3177 (*db_user_info)->personName =
3178 calloc(1, sizeof(*((*db_user_info)->personName)));
3179 if (!(*db_user_info)->personName) {
3180 err = IE_NOMEM;
3181 goto leave;
3184 err = extract_gPersonName(context, &(*db_user_info)->personName,
3185 xpath_ctx);
3186 if (err) goto leave;
3188 err = extract_gAddress(context, &(*db_user_info)->address, xpath_ctx);
3189 if (err) goto leave;
3191 err = extract_BiDate(context, &(*db_user_info)->biDate, xpath_ctx);
3192 if (err) goto leave;
3194 EXTRACT_STRING("isds:ic", (*db_user_info)->ic);
3195 EXTRACT_STRING("isds:firmName", (*db_user_info)->firmName);
3197 EXTRACT_STRING("isds:caStreet", (*db_user_info)->caStreet);
3198 EXTRACT_STRING("isds:caCity", (*db_user_info)->caCity);
3199 EXTRACT_STRING("isds:caZipCode", (*db_user_info)->caZipCode);
3201 /* ???: Default value is "CZ" according specification. Should we provide
3202 * it? */
3203 EXTRACT_STRING("isds:caState", (*db_user_info)->caState);
3205 leave:
3206 if (err) isds_DbUserInfo_free(db_user_info);
3207 free(string);
3208 xmlXPathFreeObject(result);
3209 return err;
3213 /* Insert struct isds_DbUserInfo data (user description) into XML tree
3214 * @context is session context
3215 * @user is libisds structure with user description
3216 * @db_user_info is XML element of XSD:tDbUserInfo */
3217 static isds_error insert_DbUserInfo(struct isds_ctx *context,
3218 const struct isds_DbUserInfo *user, xmlNodePtr db_user_info) {
3220 isds_error err = IE_SUCCESS;
3221 xmlNodePtr node;
3222 xmlChar *string = NULL;
3224 if (!context) return IE_INVALID_CONTEXT;
3225 if (!user || !db_user_info) return IE_INVAL;
3227 /* Build XSD:tDbUserInfo */
3228 if (user->personName) {
3229 INSERT_STRING(db_user_info, "pnFirstName",
3230 user->personName->pnFirstName);
3231 INSERT_STRING(db_user_info, "pnMiddleName",
3232 user->personName->pnMiddleName);
3233 INSERT_STRING(db_user_info, "pnLastName",
3234 user->personName->pnLastName);
3235 INSERT_STRING(db_user_info, "pnLastNameAtBirth",
3236 user->personName->pnLastNameAtBirth);
3238 if (user->address) {
3239 INSERT_STRING(db_user_info, "adCity", user->address->adCity);
3240 INSERT_STRING(db_user_info, "adStreet", user->address->adStreet);
3241 INSERT_STRING(db_user_info, "adNumberInStreet",
3242 user->address->adNumberInStreet);
3243 INSERT_STRING(db_user_info, "adNumberInMunicipality",
3244 user->address->adNumberInMunicipality);
3245 INSERT_STRING(db_user_info, "adZipCode", user->address->adZipCode);
3246 INSERT_STRING(db_user_info, "adState", user->address->adState);
3248 if (user->biDate) {
3249 if (!tm2datestring(user->biDate, &string))
3250 INSERT_STRING(db_user_info, "biDate", string);
3251 zfree(string);
3253 CHECK_FOR_STRING_LENGTH(user->userID, 6, 12, "userID");
3254 INSERT_STRING(db_user_info, "userID", user->userID);
3256 /* userType */
3257 if (user->userType) {
3258 const xmlChar *type_string = isds_UserType2string(*(user->userType));
3259 if (!type_string) {
3260 isds_printf_message(context, _("Invalid userType value: %d"),
3261 *(user->userType));
3262 err = IE_ENUM;
3263 goto leave;
3265 INSERT_STRING(db_user_info, "userType", type_string);
3268 INSERT_LONGINT(db_user_info, "userPrivils", user->userPrivils, string);
3269 CHECK_FOR_STRING_LENGTH(user->ic, 0, 8, "ic")
3270 INSERT_STRING(db_user_info, "ic", user->ic);
3271 CHECK_FOR_STRING_LENGTH(user->firmName, 0, 100, "firmName")
3272 INSERT_STRING(db_user_info, "firmName", user->firmName);
3273 INSERT_STRING(db_user_info, "caStreet", user->caStreet);
3274 INSERT_STRING(db_user_info, "caCity", user->caCity);
3275 INSERT_STRING(db_user_info, "caZipCode", user->caZipCode);
3276 INSERT_STRING(db_user_info, "caState", user->caState);
3278 leave:
3279 free(string);
3280 return err;
3284 /* Convert XSD:tPDZRec XML tree into structure
3285 * @context is ISDS context
3286 * @permission is automatically reallocated commercial permission structure
3287 * @xpath_ctx is XPath context with current node as XSD:tPDZRec element
3288 * In case of error @permission will be freed. */
3289 static isds_error extract_DbPDZRecord(struct isds_ctx *context,
3290 struct isds_commercial_permission **permission,
3291 xmlXPathContextPtr xpath_ctx) {
3292 isds_error err = IE_SUCCESS;
3293 xmlXPathObjectPtr result = NULL;
3294 char *string = NULL;
3296 if (!context) return IE_INVALID_CONTEXT;
3297 if (!permission) return IE_INVAL;
3298 isds_commercial_permission_free(permission);
3299 if (!xpath_ctx) return IE_INVAL;
3302 *permission = calloc(1, sizeof(**permission));
3303 if (!*permission) {
3304 err = IE_NOMEM;
3305 goto leave;
3308 EXTRACT_STRING("isds:PDZType", string);
3309 if (string) {
3310 err = string2isds_payment_type((xmlChar *)string,
3311 &(*permission)->type);
3312 if (err) {
3313 if (err == IE_ENUM) {
3314 err = IE_ISDS;
3315 char *string_locale = _isds_utf82locale(string);
3316 isds_printf_message(context,
3317 _("Unknown isds:PDZType value: %s"), string_locale);
3318 free(string_locale);
3320 goto leave;
3322 zfree(string);
3325 EXTRACT_STRING("isds:PDZRecip", (*permission)->recipient);
3326 EXTRACT_STRING("isds:PDZPayer", (*permission)->payer);
3328 EXTRACT_STRING("isds:PDZExpire", string);
3329 if (string) {
3330 err = timestring2timeval((xmlChar *) string,
3331 &((*permission)->expiration));
3332 if (err) {
3333 char *string_locale = _isds_utf82locale(string);
3334 if (err == IE_DATE) err = IE_ISDS;
3335 isds_printf_message(context,
3336 _("Could not convert PDZExpire as ISO time: %s"),
3337 string_locale);
3338 free(string_locale);
3339 goto leave;
3341 zfree(string);
3344 EXTRACT_ULONGINT("isds:PDZCnt", (*permission)->count, 0);
3345 EXTRACT_STRING("isds:ODZIdent", (*permission)->reply_identifier);
3347 leave:
3348 if (err) isds_commercial_permission_free(permission);
3349 free(string);
3350 xmlXPathFreeObject(result);
3351 return err;
3355 /* Convert XSD:tCiRecord XML tree into structure
3356 * @context is ISDS context
3357 * @event is automatically reallocated commercial credit event structure
3358 * @xpath_ctx is XPath context with current node as XSD:tCiRecord element
3359 * In case of error @event will be freed. */
3360 static isds_error extract_CiRecord(struct isds_ctx *context,
3361 struct isds_credit_event **event,
3362 xmlXPathContextPtr xpath_ctx) {
3363 isds_error err = IE_SUCCESS;
3364 xmlXPathObjectPtr result = NULL;
3365 char *string = NULL;
3366 long int *number_ptr;
3368 if (!context) return IE_INVALID_CONTEXT;
3369 if (!event) return IE_INVAL;
3370 isds_credit_event_free(event);
3371 if (!xpath_ctx) return IE_INVAL;
3374 *event = calloc(1, sizeof(**event));
3375 if (!*event) {
3376 err = IE_NOMEM;
3377 goto leave;
3380 EXTRACT_STRING("isds:ciEventTime", string);
3381 if (string) {
3382 err = timestring2timeval((xmlChar *) string,
3383 &(*event)->time);
3384 if (err) {
3385 char *string_locale = _isds_utf82locale(string);
3386 if (err == IE_DATE) err = IE_ISDS;
3387 isds_printf_message(context,
3388 _("Could not convert ciEventTime as ISO time: %s"),
3389 string_locale);
3390 free(string_locale);
3391 goto leave;
3393 zfree(string);
3396 EXTRACT_STRING("isds:ciEventType", string);
3397 if (string) {
3398 err = string2isds_credit_event_type((xmlChar *)string,
3399 &(*event)->type);
3400 if (err) {
3401 if (err == IE_ENUM) {
3402 err = IE_ISDS;
3403 char *string_locale = _isds_utf82locale(string);
3404 isds_printf_message(context,
3405 _("Unknown isds:ciEventType value: %s"), string_locale);
3406 free(string_locale);
3408 goto leave;
3410 zfree(string);
3413 number_ptr = &((*event)->credit_change);
3414 EXTRACT_LONGINT("isds:ciCreditChange", number_ptr, 1);
3415 number_ptr = &(*event)->new_credit;
3416 EXTRACT_LONGINT("isds:ciCreditAfter", number_ptr, 1);
3418 switch((*event)->type) {
3419 case ISDS_CREDIT_CHARGED:
3420 EXTRACT_STRING("isds:ciTransID",
3421 (*event)->details.charged.transaction);
3422 break;
3423 case ISDS_CREDIT_DISCHARGED:
3424 EXTRACT_STRING("isds:ciTransID",
3425 (*event)->details.discharged.transaction);
3426 break;
3427 case ISDS_CREDIT_MESSAGE_SENT:
3428 EXTRACT_STRING("isds:ciRecipientID",
3429 (*event)->details.message_sent.recipient);
3430 EXTRACT_STRING("isds:ciPDZID",
3431 (*event)->details.message_sent.message_id);
3432 break;
3433 case ISDS_CREDIT_STORAGE_SET:
3434 number_ptr = &((*event)->details.storage_set.new_capacity);
3435 EXTRACT_LONGINT("isds:ciNewCapacity", number_ptr, 1);
3436 EXTRACT_DATE("isds:ciNewFrom",
3437 (*event)->details.storage_set.new_valid_from);
3438 EXTRACT_DATE("isds:ciNewTo",
3439 (*event)->details.storage_set.new_valid_to);
3440 EXTRACT_LONGINT("isds:ciOldCapacity",
3441 (*event)->details.storage_set.old_capacity, 0);
3442 EXTRACT_DATE("isds:ciOldFrom",
3443 (*event)->details.storage_set.old_valid_from);
3444 EXTRACT_DATE("isds:ciOldTo",
3445 (*event)->details.storage_set.old_valid_to);
3446 EXTRACT_STRING("isds:ciDoneBy",
3447 (*event)->details.storage_set.initiator);
3448 break;
3449 case ISDS_CREDIT_EXPIRED:
3450 break;
3453 leave:
3454 if (err) isds_credit_event_free(event);
3455 free(string);
3456 xmlXPathFreeObject(result);
3457 return err;
3461 #endif /* HAVE_LIBCURL */
3464 /* Convert XSD gMessageEnvelopeSub group of elements from XML tree into
3465 * isds_envelope structure. The envelope is automatically allocated but not
3466 * reallocated. The date are just appended into envelope structure.
3467 * @context is ISDS context
3468 * @envelope is automatically allocated message envelope structure
3469 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3470 * In case of error @envelope will be freed. */
3471 static isds_error append_GMessageEnvelopeSub(struct isds_ctx *context,
3472 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3473 isds_error err = IE_SUCCESS;
3474 xmlXPathObjectPtr result = NULL;
3476 if (!context) return IE_INVALID_CONTEXT;
3477 if (!envelope) return IE_INVAL;
3478 if (!xpath_ctx) return IE_INVAL;
3481 if (!*envelope) {
3482 /* Allocate envelope */
3483 *envelope = calloc(1, sizeof(**envelope));
3484 if (!*envelope) {
3485 err = IE_NOMEM;
3486 goto leave;
3488 } else {
3489 /* Else free former data */
3490 zfree((*envelope)->dmSenderOrgUnit);
3491 zfree((*envelope)->dmSenderOrgUnitNum);
3492 zfree((*envelope)->dbIDRecipient);
3493 zfree((*envelope)->dmRecipientOrgUnit);
3494 zfree((*envelope)->dmRecipientOrgUnitNum);
3495 zfree((*envelope)->dmToHands);
3496 zfree((*envelope)->dmAnnotation);
3497 zfree((*envelope)->dmRecipientRefNumber);
3498 zfree((*envelope)->dmSenderRefNumber);
3499 zfree((*envelope)->dmRecipientIdent);
3500 zfree((*envelope)->dmSenderIdent);
3501 zfree((*envelope)->dmLegalTitleLaw);
3502 zfree((*envelope)->dmLegalTitleYear);
3503 zfree((*envelope)->dmLegalTitleSect);
3504 zfree((*envelope)->dmLegalTitlePar);
3505 zfree((*envelope)->dmLegalTitlePoint);
3506 zfree((*envelope)->dmPersonalDelivery);
3507 zfree((*envelope)->dmAllowSubstDelivery);
3510 /* Extract envelope elements added by sender or ISDS
3511 * (XSD: gMessageEnvelopeSub type) */
3512 EXTRACT_STRING("isds:dmSenderOrgUnit", (*envelope)->dmSenderOrgUnit);
3513 EXTRACT_LONGINT("isds:dmSenderOrgUnitNum",
3514 (*envelope)->dmSenderOrgUnitNum, 0);
3515 EXTRACT_STRING("isds:dbIDRecipient", (*envelope)->dbIDRecipient);
3516 EXTRACT_STRING("isds:dmRecipientOrgUnit", (*envelope)->dmRecipientOrgUnit);
3517 EXTRACT_LONGINT("isds:dmRecipientOrgUnitNum",
3518 (*envelope)->dmRecipientOrgUnitNum, 0);
3519 EXTRACT_STRING("isds:dmToHands", (*envelope)->dmToHands);
3520 EXTRACT_STRING("isds:dmAnnotation", (*envelope)->dmAnnotation);
3521 EXTRACT_STRING("isds:dmRecipientRefNumber",
3522 (*envelope)->dmRecipientRefNumber);
3523 EXTRACT_STRING("isds:dmSenderRefNumber", (*envelope)->dmSenderRefNumber);
3524 EXTRACT_STRING("isds:dmRecipientIdent", (*envelope)->dmRecipientIdent);
3525 EXTRACT_STRING("isds:dmSenderIdent", (*envelope)->dmSenderIdent);
3527 /* Extract envelope elements regarding law reference */
3528 EXTRACT_LONGINT("isds:dmLegalTitleLaw", (*envelope)->dmLegalTitleLaw, 0);
3529 EXTRACT_LONGINT("isds:dmLegalTitleYear", (*envelope)->dmLegalTitleYear, 0);
3530 EXTRACT_STRING("isds:dmLegalTitleSect", (*envelope)->dmLegalTitleSect);
3531 EXTRACT_STRING("isds:dmLegalTitlePar", (*envelope)->dmLegalTitlePar);
3532 EXTRACT_STRING("isds:dmLegalTitlePoint", (*envelope)->dmLegalTitlePoint);
3534 /* Extract envelope other elements */
3535 EXTRACT_BOOLEAN("isds:dmPersonalDelivery", (*envelope)->dmPersonalDelivery);
3536 EXTRACT_BOOLEAN("isds:dmAllowSubstDelivery",
3537 (*envelope)->dmAllowSubstDelivery);
3539 leave:
3540 if (err) isds_envelope_free(envelope);
3541 xmlXPathFreeObject(result);
3542 return err;
3547 /* Convert XSD gMessageEnvelope group of elements from XML tree into
3548 * isds_envelope structure. The envelope is automatically allocated but not
3549 * reallocated. The date are just appended into envelope structure.
3550 * @context is ISDS context
3551 * @envelope is automatically allocated message envelope structure
3552 * @xpath_ctx is XPath context with current node as gMessageEnvelope parent
3553 * In case of error @envelope will be freed. */
3554 static isds_error append_GMessageEnvelope(struct isds_ctx *context,
3555 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3556 isds_error err = IE_SUCCESS;
3557 xmlXPathObjectPtr result = NULL;
3559 if (!context) return IE_INVALID_CONTEXT;
3560 if (!envelope) return IE_INVAL;
3561 if (!xpath_ctx) return IE_INVAL;
3564 if (!*envelope) {
3565 /* Allocate envelope */
3566 *envelope = calloc(1, sizeof(**envelope));
3567 if (!*envelope) {
3568 err = IE_NOMEM;
3569 goto leave;
3571 } else {
3572 /* Else free former data */
3573 zfree((*envelope)->dmID);
3574 zfree((*envelope)->dbIDSender);
3575 zfree((*envelope)->dmSender);
3576 zfree((*envelope)->dmSenderAddress);
3577 zfree((*envelope)->dmSenderType);
3578 zfree((*envelope)->dmRecipient);
3579 zfree((*envelope)->dmRecipientAddress);
3580 zfree((*envelope)->dmAmbiguousRecipient);
3583 /* Extract envelope elements added by ISDS
3584 * (XSD: gMessageEnvelope type) */
3585 EXTRACT_STRING("isds:dmID", (*envelope)->dmID);
3586 EXTRACT_STRING("isds:dbIDSender", (*envelope)->dbIDSender);
3587 EXTRACT_STRING("isds:dmSender", (*envelope)->dmSender);
3588 EXTRACT_STRING("isds:dmSenderAddress", (*envelope)->dmSenderAddress);
3589 /* XML Schema does not guarantee enumeration. It's plain xs:int. */
3590 EXTRACT_LONGINT("isds:dmSenderType", (*envelope)->dmSenderType, 0);
3591 EXTRACT_STRING("isds:dmRecipient", (*envelope)->dmRecipient);
3592 EXTRACT_STRING("isds:dmRecipientAddress", (*envelope)->dmRecipientAddress);
3593 EXTRACT_BOOLEAN("isds:dmAmbiguousRecipient",
3594 (*envelope)->dmAmbiguousRecipient);
3596 /* Extract envelope elements added by sender and ISDS
3597 * (XSD: gMessageEnvelope type) */
3598 err = append_GMessageEnvelopeSub(context, envelope, xpath_ctx);
3599 if (err) goto leave;
3601 leave:
3602 if (err) isds_envelope_free(envelope);
3603 xmlXPathFreeObject(result);
3604 return err;
3608 /* Convert other envelope elements from XML tree into isds_envelope structure:
3609 * dmMessageStatus, dmAttachmentSize, dmDeliveryTime, dmAcceptanceTime.
3610 * The envelope is automatically allocated but not reallocated.
3611 * The data are just appended into envelope structure.
3612 * @context is ISDS context
3613 * @envelope is automatically allocated message envelope structure
3614 * @xpath_ctx is XPath context with current node as parent desired elements
3615 * In case of error @envelope will be freed. */
3616 static isds_error append_status_size_times(struct isds_ctx *context,
3617 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3618 isds_error err = IE_SUCCESS;
3619 xmlXPathObjectPtr result = NULL;
3620 char *string = NULL;
3621 unsigned long int *unumber = NULL;
3623 if (!context) return IE_INVALID_CONTEXT;
3624 if (!envelope) return IE_INVAL;
3625 if (!xpath_ctx) return IE_INVAL;
3628 if (!*envelope) {
3629 /* Allocate new */
3630 *envelope = calloc(1, sizeof(**envelope));
3631 if (!*envelope) {
3632 err = IE_NOMEM;
3633 goto leave;
3635 } else {
3636 /* Free old data */
3637 zfree((*envelope)->dmMessageStatus);
3638 zfree((*envelope)->dmAttachmentSize);
3639 zfree((*envelope)->dmDeliveryTime);
3640 zfree((*envelope)->dmAcceptanceTime);
3644 /* dmMessageStatus element is mandatory */
3645 EXTRACT_ULONGINT("sisds:dmMessageStatus", unumber, 0);
3646 if (!unumber) {
3647 isds_log_message(context,
3648 _("Missing mandatory sisds:dmMessageStatus integer"));
3649 err = IE_ISDS;
3650 goto leave;
3652 err = uint2isds_message_status(context, unumber,
3653 &((*envelope)->dmMessageStatus));
3654 if (err) {
3655 if (err == IE_ENUM) err = IE_ISDS;
3656 goto leave;
3658 free(unumber); unumber = NULL;
3660 EXTRACT_ULONGINT("sisds:dmAttachmentSize", (*envelope)->dmAttachmentSize,
3663 EXTRACT_STRING("sisds:dmDeliveryTime", string);
3664 if (string) {
3665 err = timestring2timeval((xmlChar *) string,
3666 &((*envelope)->dmDeliveryTime));
3667 if (err) {
3668 char *string_locale = _isds_utf82locale(string);
3669 if (err == IE_DATE) err = IE_ISDS;
3670 isds_printf_message(context,
3671 _("Could not convert dmDeliveryTime as ISO time: %s"),
3672 string_locale);
3673 free(string_locale);
3674 goto leave;
3676 zfree(string);
3679 EXTRACT_STRING("sisds:dmAcceptanceTime", string);
3680 if (string) {
3681 err = timestring2timeval((xmlChar *) string,
3682 &((*envelope)->dmAcceptanceTime));
3683 if (err) {
3684 char *string_locale = _isds_utf82locale(string);
3685 if (err == IE_DATE) err = IE_ISDS;
3686 isds_printf_message(context,
3687 _("Could not convert dmAcceptanceTime as ISO time: %s"),
3688 string_locale);
3689 free(string_locale);
3690 goto leave;
3692 zfree(string);
3695 leave:
3696 if (err) isds_envelope_free(envelope);
3697 free(unumber);
3698 free(string);
3699 xmlXPathFreeObject(result);
3700 return err;
3704 /* Convert message type attribute of current element into isds_envelope
3705 * structure.
3706 * TODO: This function can be incorporated into append_status_size_times() as
3707 * they are called always together.
3708 * The envelope is automatically allocated but not reallocated.
3709 * The data are just appended into envelope structure.
3710 * @context is ISDS context
3711 * @envelope is automatically allocated message envelope structure
3712 * @xpath_ctx is XPath context with current node as parent of attribute
3713 * carrying message type
3714 * In case of error @envelope will be freed. */
3715 static isds_error append_message_type(struct isds_ctx *context,
3716 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
3717 isds_error err = IE_SUCCESS;
3719 if (!context) return IE_INVALID_CONTEXT;
3720 if (!envelope) return IE_INVAL;
3721 if (!xpath_ctx) return IE_INVAL;
3724 if (!*envelope) {
3725 /* Allocate new */
3726 *envelope = calloc(1, sizeof(**envelope));
3727 if (!*envelope) {
3728 err = IE_NOMEM;
3729 goto leave;
3731 } else {
3732 /* Free old data */
3733 zfree((*envelope)->dmType);
3737 EXTRACT_STRING_ATTRIBUTE("dmType", (*envelope)->dmType, 0);
3739 if (!(*envelope)->dmType) {
3740 /* Use default value */
3741 (*envelope)->dmType = strdup("V");
3742 if (!(*envelope)->dmType) {
3743 err = IE_NOMEM;
3744 goto leave;
3746 } else if (1 != xmlUTF8Strlen((xmlChar *) (*envelope)->dmType)) {
3747 char *type_locale = _isds_utf82locale((*envelope)->dmType);
3748 isds_printf_message(context,
3749 _("Message type in dmType attribute is not 1 character long: "
3750 "%s"),
3751 type_locale);
3752 free(type_locale);
3753 err = IE_ISDS;
3754 goto leave;
3757 leave:
3758 if (err) isds_envelope_free(envelope);
3759 return err;
3763 #if HAVE_LIBCURL
3764 /* Convert dmType isds_envelope member into XML attribute and append it to
3765 * current node.
3766 * @context is ISDS context
3767 * @type is UTF-8 encoded string one multibyte long exactly or NULL to omit
3768 * @dm_envelope is XML element the resulting attribute will be appended to.
3769 * @return error code, in case of error context' message is filled. */
3770 static isds_error insert_message_type(struct isds_ctx *context,
3771 const char *type, xmlNodePtr dm_envelope) {
3772 isds_error err = IE_SUCCESS;
3773 xmlAttrPtr attribute_node;
3775 if (!context) return IE_INVALID_CONTEXT;
3776 if (!dm_envelope) return IE_INVAL;
3778 /* Insert optional message type */
3779 if (type) {
3780 if (1 != xmlUTF8Strlen((xmlChar *) type)) {
3781 char *type_locale = _isds_utf82locale(type);
3782 isds_printf_message(context,
3783 _("Message type in envelope is not 1 character long: %s"),
3784 type_locale);
3785 free(type_locale);
3786 err = IE_INVAL;
3787 goto leave;
3789 INSERT_STRING_ATTRIBUTE(dm_envelope, "dmType", type);
3792 leave:
3793 return err;
3795 #endif /* HAVE_LIBCURL */
3798 /* Extract message document into reallocated document structure
3799 * @context is ISDS context
3800 * @document is automatically reallocated message documents structure
3801 * @xpath_ctx is XPath context with current node as isds:dmFile
3802 * In case of error @document will be freed. */
3803 static isds_error extract_document(struct isds_ctx *context,
3804 struct isds_document **document, xmlXPathContextPtr xpath_ctx) {
3805 isds_error err = IE_SUCCESS;
3806 xmlXPathObjectPtr result = NULL;
3807 xmlNodePtr file_node;
3808 char *string = NULL;
3810 if (!context) return IE_INVALID_CONTEXT;
3811 if (!document) return IE_INVAL;
3812 isds_document_free(document);
3813 if (!xpath_ctx) return IE_INVAL;
3814 file_node = xpath_ctx->node;
3816 *document = calloc(1, sizeof(**document));
3817 if (!*document) {
3818 err = IE_NOMEM;
3819 goto leave;
3822 /* Extract document meta data */
3823 EXTRACT_STRING_ATTRIBUTE("dmMimeType", (*document)->dmMimeType, 1)
3824 if (context->normalize_mime_type) {
3825 const char *normalized_type =
3826 isds_normalize_mime_type((*document)->dmMimeType);
3827 if (NULL != normalized_type &&
3828 normalized_type != (*document)->dmMimeType) {
3829 char *new_type = strdup(normalized_type);
3830 if (NULL == new_type) {
3831 isds_printf_message(context,
3832 _("Not enough memory to normalize document MIME type"));
3833 err = IE_NOMEM;
3834 goto leave;
3836 free((*document)->dmMimeType);
3837 (*document)->dmMimeType = new_type;
3841 EXTRACT_STRING_ATTRIBUTE("dmFileMetaType", string, 1)
3842 err = string2isds_FileMetaType((xmlChar*)string,
3843 &((*document)->dmFileMetaType));
3844 if (err) {
3845 char *meta_type_locale = _isds_utf82locale(string);
3846 isds_printf_message(context,
3847 _("Document has invalid dmFileMetaType attribute value: %s"),
3848 meta_type_locale);
3849 free(meta_type_locale);
3850 err = IE_ISDS;
3851 goto leave;
3853 zfree(string);
3855 EXTRACT_STRING_ATTRIBUTE("dmFileGuid", (*document)->dmFileGuid, 0)
3856 EXTRACT_STRING_ATTRIBUTE("dmUpFileGuid", (*document)->dmUpFileGuid, 0)
3857 EXTRACT_STRING_ATTRIBUTE("dmFileDescr", (*document)->dmFileDescr, 0)
3858 EXTRACT_STRING_ATTRIBUTE("dmFormat", (*document)->dmFormat, 0)
3861 /* Extract document data.
3862 * Base64 encoded blob or XML subtree must be presented. */
3864 /* Check for dmEncodedContent */
3865 result = xmlXPathEvalExpression(BAD_CAST "isds:dmEncodedContent",
3866 xpath_ctx);
3867 if (!result) {
3868 err = IE_XML;
3869 goto leave;
3872 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3873 /* Here we have Base64 blob */
3874 (*document)->is_xml = 0;
3876 if (result->nodesetval->nodeNr > 1) {
3877 isds_printf_message(context,
3878 _("Document has more dmEncodedContent elements"));
3879 err = IE_ISDS;
3880 goto leave;
3883 xmlXPathFreeObject(result); result = NULL;
3884 EXTRACT_STRING("isds:dmEncodedContent", string);
3886 /* Decode non-empty document */
3887 if (string && string[0] != '\0') {
3888 (*document)->data_length =
3889 _isds_b64decode(string, &((*document)->data));
3890 if ((*document)->data_length == (size_t) -1) {
3891 isds_printf_message(context,
3892 _("Error while Base64-decoding document content"));
3893 err = IE_ERROR;
3894 goto leave;
3897 } else {
3898 /* No Base64 blob, try XML document */
3899 xmlXPathFreeObject(result); result = NULL;
3900 result = xmlXPathEvalExpression(BAD_CAST "isds:dmXMLContent",
3901 xpath_ctx);
3902 if (!result) {
3903 err = IE_XML;
3904 goto leave;
3907 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3908 /* Here we have XML document */
3909 (*document)->is_xml = 1;
3911 if (result->nodesetval->nodeNr > 1) {
3912 isds_printf_message(context,
3913 _("Document has more dmXMLContent elements"));
3914 err = IE_ISDS;
3915 goto leave;
3918 /* XXX: We cannot serialize the content simply because:
3919 * - XML document may point out of its scope (e.g. to message
3920 * envelope)
3921 * - isds:dmXMLContent can contain more elements, no element,
3922 * a text node only
3923 * - it's not the XML way
3924 * Thus we provide the only right solution: XML DOM. Let's
3925 * application to cope with this hot potato :) */
3926 (*document)->xml_node_list =
3927 result->nodesetval->nodeTab[0]->children;
3928 } else {
3929 /* No base64 blob, nor XML document */
3930 isds_printf_message(context,
3931 _("Document has no dmEncodedContent, nor dmXMLContent "
3932 "element"));
3933 err = IE_ISDS;
3934 goto leave;
3939 leave:
3940 if (err) isds_document_free(document);
3941 free(string);
3942 xmlXPathFreeObject(result);
3943 xpath_ctx->node = file_node;
3944 return err;
3949 /* Extract message documents into reallocated list of documents
3950 * @context is ISDS context
3951 * @documents is automatically reallocated message documents list structure
3952 * @xpath_ctx is XPath context with current node as XSD tFilesArray
3953 * In case of error @documents will be freed. */
3954 static isds_error extract_documents(struct isds_ctx *context,
3955 struct isds_list **documents, xmlXPathContextPtr xpath_ctx) {
3956 isds_error err = IE_SUCCESS;
3957 xmlXPathObjectPtr result = NULL;
3958 xmlNodePtr files_node;
3959 struct isds_list *document, *prev_document = NULL;
3961 if (!context) return IE_INVALID_CONTEXT;
3962 if (!documents) return IE_INVAL;
3963 isds_list_free(documents);
3964 if (!xpath_ctx) return IE_INVAL;
3965 files_node = xpath_ctx->node;
3967 /* Find documents */
3968 result = xmlXPathEvalExpression(BAD_CAST "isds:dmFile", xpath_ctx);
3969 if (!result) {
3970 err = IE_XML;
3971 goto leave;
3974 /* No match */
3975 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
3976 isds_printf_message(context,
3977 _("Message does not contain any document"));
3978 err = IE_ISDS;
3979 goto leave;
3983 /* Iterate over documents */
3984 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
3986 /* Allocate and append list item */
3987 document = calloc(1, sizeof(*document));
3988 if (!document) {
3989 err = IE_NOMEM;
3990 goto leave;
3992 document->destructor = (void (*)(void **))isds_document_free;
3993 if (i == 0) *documents = document;
3994 else prev_document->next = document;
3995 prev_document = document;
3997 /* Extract document */
3998 xpath_ctx->node = result->nodesetval->nodeTab[i];
3999 err = extract_document(context,
4000 (struct isds_document **) &(document->data), xpath_ctx);
4001 if (err) goto leave;
4005 leave:
4006 if (err) isds_list_free(documents);
4007 xmlXPathFreeObject(result);
4008 xpath_ctx->node = files_node;
4009 return err;
4013 #if HAVE_LIBCURL
4014 /* Convert isds:dmRecord XML tree into structure
4015 * @context is ISDS context
4016 * @envelope is automatically reallocated message envelope structure
4017 * @xpath_ctx is XPath context with current node as isds:dmRecord element
4018 * In case of error @envelope will be freed. */
4019 static isds_error extract_DmRecord(struct isds_ctx *context,
4020 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4021 isds_error err = IE_SUCCESS;
4022 xmlXPathObjectPtr result = NULL;
4024 if (!context) return IE_INVALID_CONTEXT;
4025 if (!envelope) return IE_INVAL;
4026 isds_envelope_free(envelope);
4027 if (!xpath_ctx) return IE_INVAL;
4030 *envelope = calloc(1, sizeof(**envelope));
4031 if (!*envelope) {
4032 err = IE_NOMEM;
4033 goto leave;
4037 /* Extract tRecord data */
4038 EXTRACT_ULONGINT("isds:dmOrdinal", (*envelope)->dmOrdinal, 0);
4040 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4041 * dmAcceptanceTime. */
4042 err = append_status_size_times(context, envelope, xpath_ctx);
4043 if (err) goto leave;
4045 /* Extract envelope elements added by sender and ISDS
4046 * (XSD: gMessageEnvelope type) */
4047 err = append_GMessageEnvelope(context, envelope, xpath_ctx);
4048 if (err) goto leave;
4050 /* Get message type */
4051 err = append_message_type(context, envelope, xpath_ctx);
4052 if (err) goto leave;
4055 leave:
4056 if (err) isds_envelope_free(envelope);
4057 xmlXPathFreeObject(result);
4058 return err;
4062 /* Convert XSD:tStateChangesRecord type XML tree into structure
4063 * @context is ISDS context
4064 * @changed_status is automatically reallocated message state change structure
4065 * @xpath_ctx is XPath context with current node as element of
4066 * XSD:tStateChangesRecord type
4067 * In case of error @changed_status will be freed. */
4068 static isds_error extract_StateChangesRecord(struct isds_ctx *context,
4069 struct isds_message_status_change **changed_status,
4070 xmlXPathContextPtr xpath_ctx) {
4071 isds_error err = IE_SUCCESS;
4072 xmlXPathObjectPtr result = NULL;
4073 unsigned long int *unumber = NULL;
4074 char *string = NULL;
4076 if (!context) return IE_INVALID_CONTEXT;
4077 if (!changed_status) return IE_INVAL;
4078 isds_message_status_change_free(changed_status);
4079 if (!xpath_ctx) return IE_INVAL;
4082 *changed_status = calloc(1, sizeof(**changed_status));
4083 if (!*changed_status) {
4084 err = IE_NOMEM;
4085 goto leave;
4089 /* Extract tGetStateChangesInput data */
4090 EXTRACT_STRING("isds:dmID", (*changed_status)->dmID);
4092 /* dmEventTime is mandatory */
4093 EXTRACT_STRING("isds:dmEventTime", string);
4094 if (string) {
4095 err = timestring2timeval((xmlChar *) string,
4096 &((*changed_status)->time));
4097 if (err) {
4098 char *string_locale = _isds_utf82locale(string);
4099 if (err == IE_DATE) err = IE_ISDS;
4100 isds_printf_message(context,
4101 _("Could not convert dmEventTime as ISO time: %s"),
4102 string_locale);
4103 free(string_locale);
4104 goto leave;
4106 zfree(string);
4109 /* dmMessageStatus element is mandatory */
4110 EXTRACT_ULONGINT("isds:dmMessageStatus", unumber, 0);
4111 if (!unumber) {
4112 isds_log_message(context,
4113 _("Missing mandatory isds:dmMessageStatus integer"));
4114 err = IE_ISDS;
4115 goto leave;
4117 err = uint2isds_message_status(context, unumber,
4118 &((*changed_status)->dmMessageStatus));
4119 if (err) {
4120 if (err == IE_ENUM) err = IE_ISDS;
4121 goto leave;
4123 zfree(unumber);
4126 leave:
4127 free(unumber);
4128 free(string);
4129 if (err) isds_message_status_change_free(changed_status);
4130 xmlXPathFreeObject(result);
4131 return err;
4133 #endif /* HAVE_LIBCURL */
4136 /* Find and convert isds:dmHash XML tree into structure
4137 * @context is ISDS context
4138 * @envelope is automatically reallocated message hash structure
4139 * @xpath_ctx is XPath context with current node containing isds:dmHash child
4140 * In case of error @hash will be freed. */
4141 static isds_error find_and_extract_DmHash(struct isds_ctx *context,
4142 struct isds_hash **hash, xmlXPathContextPtr xpath_ctx) {
4143 isds_error err = IE_SUCCESS;
4144 xmlNodePtr old_ctx_node;
4145 xmlXPathObjectPtr result = NULL;
4146 char *string = NULL;
4148 if (!context) return IE_INVALID_CONTEXT;
4149 if (!hash) return IE_INVAL;
4150 isds_hash_free(hash);
4151 if (!xpath_ctx) return IE_INVAL;
4153 old_ctx_node = xpath_ctx->node;
4155 *hash = calloc(1, sizeof(**hash));
4156 if (!*hash) {
4157 err = IE_NOMEM;
4158 goto leave;
4161 /* Locate dmHash */
4162 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmHash", xpath_ctx);
4163 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4164 err = IE_ISDS;
4165 goto leave;
4167 if (err) {
4168 err = IE_ERROR;
4169 goto leave;
4172 /* Get hash algorithm */
4173 EXTRACT_STRING_ATTRIBUTE("algorithm", string, 1);
4174 err = string2isds_hash_algorithm((xmlChar*) string, &(*hash)->algorithm);
4175 if (err) {
4176 if (err == IE_ENUM) {
4177 char *string_locale = _isds_utf82locale(string);
4178 isds_printf_message(context, _("Unsupported hash algorithm: %s"),
4179 string_locale);
4180 free(string_locale);
4182 goto leave;
4184 zfree(string);
4186 /* Get hash value */
4187 EXTRACT_STRING(".", string);
4188 if (!string) {
4189 isds_printf_message(context,
4190 _("sisds:dmHash element is missing hash value"));
4191 err = IE_ISDS;
4192 goto leave;
4194 (*hash)->length = _isds_b64decode(string, &((*hash)->value));
4195 if ((*hash)->length == (size_t) -1) {
4196 isds_printf_message(context,
4197 _("Error while Base64-decoding hash value"));
4198 err = IE_ERROR;
4199 goto leave;
4202 leave:
4203 if (err) isds_hash_free(hash);
4204 free(string);
4205 xmlXPathFreeObject(result);
4206 xpath_ctx->node = old_ctx_node;
4207 return err;
4211 /* Find and append isds:dmQTimestamp XML tree into envelope.
4212 * Because one service is allowed to miss time-stamp content, and we think
4213 * other could too (flaw in specification), this function is deliberated and
4214 * will not fail (i.e. will return IE_SUCCESS), if time-stamp is missing.
4215 * @context is ISDS context
4216 * @envelope is automatically allocated envelope structure
4217 * @xpath_ctx is XPath context with current node containing isds:dmQTimestamp
4218 * child
4219 * In case of error @envelope will be freed. */
4220 static isds_error find_and_append_DmQTimestamp(struct isds_ctx *context,
4221 struct isds_envelope **envelope, xmlXPathContextPtr xpath_ctx) {
4222 isds_error err = IE_SUCCESS;
4223 xmlXPathObjectPtr result = NULL;
4224 char *string = NULL;
4226 if (!context) return IE_INVALID_CONTEXT;
4227 if (!envelope) return IE_INVAL;
4228 if (!xpath_ctx) {
4229 isds_envelope_free(envelope);
4230 return IE_INVAL;
4233 if (!*envelope) {
4234 *envelope = calloc(1, sizeof(**envelope));
4235 if (!*envelope) {
4236 err = IE_NOMEM;
4237 goto leave;
4239 } else {
4240 zfree((*envelope)->timestamp);
4241 (*envelope)->timestamp_length = 0;
4244 /* Get dmQTimestamp */
4245 EXTRACT_STRING("sisds:dmQTimestamp", string);
4246 if (!string) {
4247 isds_log(ILF_ISDS, ILL_INFO, _("Missing dmQTimestamp element content\n"));
4248 goto leave;
4250 (*envelope)->timestamp_length =
4251 _isds_b64decode(string, &((*envelope)->timestamp));
4252 if ((*envelope)->timestamp_length == (size_t) -1) {
4253 isds_printf_message(context,
4254 _("Error while Base64-decoding time stamp value"));
4255 err = IE_ERROR;
4256 goto leave;
4259 leave:
4260 if (err) isds_envelope_free(envelope);
4261 free(string);
4262 xmlXPathFreeObject(result);
4263 return err;
4267 /* Convert XSD tReturnedMessage XML tree into message structure.
4268 * It does not store serialized XML tree into message->raw.
4269 * It does store (pointer to) parsed XML tree into message->xml if needed.
4270 * @context is ISDS context
4271 * @include_documents Use true if documents must be extracted
4272 * (tReturnedMessage XSD type), use false if documents shall be omitted
4273 * (tReturnedMessageEnvelope).
4274 * @message is automatically reallocated message structure
4275 * @xpath_ctx is XPath context with current node as tReturnedMessage element
4276 * type
4277 * In case of error @message will be freed. */
4278 static isds_error extract_TReturnedMessage(struct isds_ctx *context,
4279 const _Bool include_documents, struct isds_message **message,
4280 xmlXPathContextPtr xpath_ctx) {
4281 isds_error err = IE_SUCCESS;
4282 xmlNodePtr message_node;
4284 if (!context) return IE_INVALID_CONTEXT;
4285 if (!message) return IE_INVAL;
4286 isds_message_free(message);
4287 if (!xpath_ctx) return IE_INVAL;
4290 *message = calloc(1, sizeof(**message));
4291 if (!*message) {
4292 err = IE_NOMEM;
4293 goto leave;
4296 /* Save message XPATH context node */
4297 message_node = xpath_ctx->node;
4300 /* Extract dmDM */
4301 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmDm", xpath_ctx);
4302 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
4303 if (err) { err = IE_ERROR; goto leave; }
4304 err = append_GMessageEnvelope(context, &((*message)->envelope), xpath_ctx);
4305 if (err) goto leave;
4307 if (include_documents) {
4308 struct isds_list *item;
4310 /* Extract dmFiles */
4311 err = move_xpathctx_to_child(context, BAD_CAST "isds:dmFiles",
4312 xpath_ctx);
4313 if (err == IE_NOEXIST || err == IE_NOTUNIQ) {
4314 err = IE_ISDS; goto leave;
4316 if (err) { err = IE_ERROR; goto leave; }
4317 err = extract_documents(context, &((*message)->documents), xpath_ctx);
4318 if (err) goto leave;
4320 /* Store xmlDoc of this message if needed */
4321 /* Only if we got a XML document in all the documents. */
4322 for (item = (*message)->documents; item; item = item->next) {
4323 if (item->data && ((struct isds_document *)item->data)->is_xml) {
4324 (*message)->xml = xpath_ctx->doc;
4325 break;
4331 /* Restore context to message */
4332 xpath_ctx->node = message_node;
4334 /* Extract dmHash */
4335 err = find_and_extract_DmHash(context, &(*message)->envelope->hash,
4336 xpath_ctx);
4337 if (err) goto leave;
4339 /* Extract dmQTimestamp, */
4340 err = find_and_append_DmQTimestamp(context, &(*message)->envelope,
4341 xpath_ctx);
4342 if (err) goto leave;
4344 /* Get dmMessageStatus, dmAttachmentSize, dmDeliveryTime,
4345 * dmAcceptanceTime. */
4346 err = append_status_size_times(context, &((*message)->envelope), xpath_ctx);
4347 if (err) goto leave;
4349 /* Get message type */
4350 err = append_message_type(context, &((*message)->envelope), xpath_ctx);
4351 if (err) goto leave;
4353 leave:
4354 if (err) isds_message_free(message);
4355 return err;
4359 /* Extract message event into reallocated isds_event structure
4360 * @context is ISDS context
4361 * @event is automatically reallocated message event structure
4362 * @xpath_ctx is XPath context with current node as isds:dmEvent
4363 * In case of error @event will be freed. */
4364 static isds_error extract_event(struct isds_ctx *context,
4365 struct isds_event **event, xmlXPathContextPtr xpath_ctx) {
4366 isds_error err = IE_SUCCESS;
4367 xmlXPathObjectPtr result = NULL;
4368 xmlNodePtr event_node;
4369 char *string = NULL;
4371 if (!context) return IE_INVALID_CONTEXT;
4372 if (!event) return IE_INVAL;
4373 isds_event_free(event);
4374 if (!xpath_ctx) return IE_INVAL;
4375 event_node = xpath_ctx->node;
4377 *event = calloc(1, sizeof(**event));
4378 if (!*event) {
4379 err = IE_NOMEM;
4380 goto leave;
4383 /* Extract event data.
4384 * All elements are optional according XSD. That's funny. */
4385 EXTRACT_STRING("sisds:dmEventTime", string);
4386 if (string) {
4387 err = timestring2timeval((xmlChar *) string, &((*event)->time));
4388 if (err) {
4389 char *string_locale = _isds_utf82locale(string);
4390 if (err == IE_DATE) err = IE_ISDS;
4391 isds_printf_message(context,
4392 _("Could not convert dmEventTime as ISO time: %s"),
4393 string_locale);
4394 free(string_locale);
4395 goto leave;
4397 zfree(string);
4400 /* dmEventDescr element has prefix and the rest */
4401 EXTRACT_STRING("sisds:dmEventDescr", string);
4402 if (string) {
4403 err = eventstring2event((xmlChar *) string, *event);
4404 if (err) goto leave;
4405 zfree(string);
4408 leave:
4409 if (err) isds_event_free(event);
4410 free(string);
4411 xmlXPathFreeObject(result);
4412 xpath_ctx->node = event_node;
4413 return err;
4417 /* Convert element of XSD tEventsArray type from XML tree into
4418 * isds_list of isds_event's structure. The list is automatically reallocated.
4419 * @context is ISDS context
4420 * @events is automatically reallocated list of event structures
4421 * @xpath_ctx is XPath context with current node as tEventsArray
4422 * In case of error @events will be freed. */
4423 static isds_error extract_events(struct isds_ctx *context,
4424 struct isds_list **events, xmlXPathContextPtr xpath_ctx) {
4425 isds_error err = IE_SUCCESS;
4426 xmlXPathObjectPtr result = NULL;
4427 xmlNodePtr events_node;
4428 struct isds_list *event, *prev_event = NULL;
4430 if (!context) return IE_INVALID_CONTEXT;
4431 if (!events) return IE_INVAL;
4432 if (!xpath_ctx) return IE_INVAL;
4433 events_node = xpath_ctx->node;
4435 /* Free old list */
4436 isds_list_free(events);
4438 /* Find events */
4439 result = xmlXPathEvalExpression(BAD_CAST "sisds:dmEvent", xpath_ctx);
4440 if (!result) {
4441 err = IE_XML;
4442 goto leave;
4445 /* No match */
4446 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4447 isds_printf_message(context,
4448 _("Delivery info does not contain any event"));
4449 err = IE_ISDS;
4450 goto leave;
4454 /* Iterate over events */
4455 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
4457 /* Allocate and append list item */
4458 event = calloc(1, sizeof(*event));
4459 if (!event) {
4460 err = IE_NOMEM;
4461 goto leave;
4463 event->destructor = (void (*)(void **))isds_event_free;
4464 if (i == 0) *events = event;
4465 else prev_event->next = event;
4466 prev_event = event;
4468 /* Extract event */
4469 xpath_ctx->node = result->nodesetval->nodeTab[i];
4470 err = extract_event(context,
4471 (struct isds_event **) &(event->data), xpath_ctx);
4472 if (err) goto leave;
4476 leave:
4477 if (err) isds_list_free(events);
4478 xmlXPathFreeObject(result);
4479 xpath_ctx->node = events_node;
4480 return err;
4484 #if HAVE_LIBCURL
4485 /* Insert Base64 encoded data as element with text child.
4486 * @context is session context
4487 * @parent is XML node to append @element with @data as child
4488 * @ns is XML namespace of @element, use NULL to inherit from @parent
4489 * @element is UTF-8 encoded name of new element
4490 * @data is bit stream to encode into @element
4491 * @length is size of @data in bytes
4492 * @return standard error code and fill long error message if needed */
4493 static isds_error insert_base64_encoded_string(struct isds_ctx *context,
4494 xmlNodePtr parent, const xmlNsPtr ns, const char *element,
4495 const void *data, size_t length) {
4496 isds_error err = IE_SUCCESS;
4497 xmlNodePtr node;
4499 if (!context) return IE_INVALID_CONTEXT;
4500 if (!data && length > 0) return IE_INVAL;
4501 if (!parent || !element) return IE_INVAL;
4503 xmlChar *base64data = NULL;
4504 base64data = (xmlChar *) _isds_b64encode(data, length);
4505 if (!base64data) {
4506 isds_printf_message(context,
4507 ngettext("Not enough memory to encode %zd byte into Base64",
4508 "Not enough memory to encode %zd bytes into Base64",
4509 length),
4510 length);
4511 err = IE_NOMEM;
4512 goto leave;
4514 INSERT_STRING_WITH_NS(parent, ns, element, base64data);
4516 leave:
4517 free(base64data);
4518 return err;
4522 /* Convert isds_document structure into XML tree and append to dmFiles node.
4523 * @context is session context
4524 * @document is ISDS document
4525 * @dm_files is XML element the resulting tree will be appended to as a child.
4526 * @return error code, in case of error context' message is filled. */
4527 static isds_error insert_document(struct isds_ctx *context,
4528 struct isds_document *document, xmlNodePtr dm_files) {
4529 isds_error err = IE_SUCCESS;
4530 xmlNodePtr new_file = NULL, file = NULL, node;
4531 xmlAttrPtr attribute_node;
4533 if (!context) return IE_INVALID_CONTEXT;
4534 if (!document || !dm_files) return IE_INVAL;
4536 /* Allocate new dmFile */
4537 new_file = xmlNewNode(dm_files->ns, BAD_CAST "dmFile");
4538 if (!new_file) {
4539 isds_printf_message(context, _("Could not allocate main dmFile"));
4540 err = IE_ERROR;
4541 goto leave;
4543 /* Append the new dmFile.
4544 * XXX: Main document must go first */
4545 if (document->dmFileMetaType == FILEMETATYPE_MAIN && dm_files->children)
4546 file = xmlAddPrevSibling(dm_files->children, new_file);
4547 else
4548 file = xmlAddChild(dm_files, new_file);
4550 if (!file) {
4551 xmlFreeNode(new_file); new_file = NULL;
4552 isds_printf_message(context, _("Could not add dmFile child to "
4553 "%s element"), dm_files->name);
4554 err = IE_ERROR;
4555 goto leave;
4558 /* @dmMimeType is required */
4559 if (!document->dmMimeType) {
4560 isds_log_message(context,
4561 _("Document is missing mandatory MIME type definition"));
4562 err = IE_INVAL;
4563 goto leave;
4565 INSERT_STRING_ATTRIBUTE(file, "dmMimeType", document->dmMimeType);
4567 const xmlChar *string = isds_FileMetaType2string(document->dmFileMetaType);
4568 if (!string) {
4569 isds_printf_message(context,
4570 _("Document has unknown dmFileMetaType: %ld"),
4571 document->dmFileMetaType);
4572 err = IE_ENUM;
4573 goto leave;
4575 INSERT_STRING_ATTRIBUTE(file, "dmFileMetaType", string);
4577 if (document->dmFileGuid) {
4578 INSERT_STRING_ATTRIBUTE(file, "dmFileGuid", document->dmFileGuid);
4580 if (document->dmUpFileGuid) {
4581 INSERT_STRING_ATTRIBUTE(file, "dmUpFileGuid", document->dmUpFileGuid);
4584 /* @dmFileDescr is required */
4585 if (!document->dmFileDescr) {
4586 isds_log_message(context,
4587 _("Document is missing mandatory description (title)"));
4588 err = IE_INVAL;
4589 goto leave;
4591 INSERT_STRING_ATTRIBUTE(file, "dmFileDescr", document->dmFileDescr);
4593 if (document->dmFormat) {
4594 INSERT_STRING_ATTRIBUTE(file, "dmFormat", document->dmFormat);
4598 /* Insert content (body) of the document. */
4599 if (document->is_xml) {
4600 /* XML document requested */
4602 /* Allocate new dmXMLContent */
4603 xmlNodePtr xmlcontent = xmlNewNode(file->ns, BAD_CAST "dmXMLContent");
4604 if (!xmlcontent) {
4605 isds_printf_message(context,
4606 _("Could not allocate dmXMLContent element"));
4607 err = IE_ERROR;
4608 goto leave;
4610 /* Append it */
4611 node = xmlAddChild(file, xmlcontent);
4612 if (!node) {
4613 xmlFreeNode(xmlcontent); xmlcontent = NULL;
4614 isds_printf_message(context,
4615 _("Could not add dmXMLContent child to %s element"),
4616 file->name);
4617 err = IE_ERROR;
4618 goto leave;
4621 /* Copy non-empty node list */
4622 if (document->xml_node_list) {
4623 xmlNodePtr content = xmlDocCopyNodeList(node->doc,
4624 document->xml_node_list);
4625 if (!content) {
4626 isds_printf_message(context,
4627 _("Not enough memory to copy XML document"));
4628 err = IE_NOMEM;
4629 goto leave;
4632 if (!xmlAddChildList(node, content)) {
4633 xmlFreeNodeList(content);
4634 isds_printf_message(context,
4635 _("Error while adding XML document into dmXMLContent"));
4636 err = IE_XML;
4637 goto leave;
4639 /* XXX: We cannot free the content here because it's part of node's
4640 * document since now. It will be freed with it automatically. */
4642 } else {
4643 /* Binary document requested */
4644 err = insert_base64_encoded_string(context, file, NULL, "dmEncodedContent",
4645 document->data, document->data_length);
4646 if (err) goto leave;
4649 leave:
4650 return err;
4654 /* Append XSD tMStatus XML tree into isds_message_copy structure.
4655 * The copy must be preallocated, the date are just appended into structure.
4656 * @context is ISDS context
4657 * @copy is message copy structure
4658 * @xpath_ctx is XPath context with current node as tMStatus */
4659 static isds_error append_TMStatus(struct isds_ctx *context,
4660 struct isds_message_copy *copy, xmlXPathContextPtr xpath_ctx) {
4661 isds_error err = IE_SUCCESS;
4662 xmlXPathObjectPtr result = NULL;
4663 char *code = NULL, *message = NULL;
4665 if (!context) return IE_INVALID_CONTEXT;
4666 if (!copy || !xpath_ctx) return IE_INVAL;
4668 /* Free old values */
4669 zfree(copy->dmStatus);
4670 zfree(copy->dmID);
4672 /* Get error specific to this copy */
4673 EXTRACT_STRING("isds:dmStatus/isds:dmStatusCode", code);
4674 if (!code) {
4675 isds_log_message(context,
4676 _("Missing isds:dmStatusCode under "
4677 "XSD:tMStatus type element"));
4678 err = IE_ISDS;
4679 goto leave;
4682 if (xmlStrcmp((const xmlChar *)code, BAD_CAST "0000")) {
4683 /* This copy failed */
4684 copy->error = IE_ISDS;
4685 EXTRACT_STRING("isds:dmStatus/isds:dmStatusMessage", message);
4686 if (message) {
4687 copy->dmStatus = _isds_astrcat3(code, ": ", message);
4688 if (!copy->dmStatus) {
4689 copy->dmStatus = code;
4690 code = NULL;
4692 } else {
4693 copy->dmStatus = code;
4694 code = NULL;
4696 } else {
4697 /* This copy succeeded. In this case only, message ID is valid */
4698 copy->error = IE_SUCCESS;
4700 EXTRACT_STRING("isds:dmID", copy->dmID);
4701 if (!copy->dmID) {
4702 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
4703 "but did not returned assigned message ID\n"));
4704 err = IE_ISDS;
4708 leave:
4709 free(code);
4710 free(message);
4711 xmlXPathFreeObject(result);
4712 return err;
4716 /* Insert struct isds_approval data (box approval) into XML tree
4717 * @context is session context
4718 * @approval is libisds structure with approval description. NULL is
4719 * acceptable.
4720 * @parent is XML element to append @approval to */
4721 static isds_error insert_GExtApproval(struct isds_ctx *context,
4722 const struct isds_approval *approval, xmlNodePtr parent) {
4724 isds_error err = IE_SUCCESS;
4725 xmlNodePtr node;
4727 if (!context) return IE_INVALID_CONTEXT;
4728 if (!parent) return IE_INVAL;
4730 if (!approval) return IE_SUCCESS;
4732 /* Build XSD:gExtApproval */
4733 INSERT_SCALAR_BOOLEAN(parent, "dbApproved", approval->approved);
4734 INSERT_STRING(parent, "dbExternRefNumber", approval->refference);
4736 leave:
4737 return err;
4741 /* Build ISDS request of XSD tDummyInput type, sent it and check for error
4742 * code
4743 * @context is session context
4744 * @service_name is name of SERVICE_DB_ACCESS
4745 * @response is reallocated server SOAP body response as XML document
4746 * @raw_response is reallocated bit stream with response body. Use
4747 * NULL if you don't care
4748 * @raw_response_length is size of @raw_response in bytes
4749 * @code is reallocated ISDS status code
4750 * @status_message is reallocated ISDS status message
4751 * @return error coded from lower layer, context message will be set up
4752 * appropriately. */
4753 static isds_error build_send_check_dbdummy_request(struct isds_ctx *context,
4754 const xmlChar *service_name,
4755 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
4756 xmlChar **code, xmlChar **status_message) {
4758 isds_error err = IE_SUCCESS;
4759 char *service_name_locale = NULL;
4760 xmlNodePtr request = NULL, node;
4761 xmlNsPtr isds_ns = NULL;
4763 if (!context) return IE_INVALID_CONTEXT;
4764 if (!service_name) return IE_INVAL;
4765 if (!response || !code || !status_message) return IE_INVAL;
4766 if (!raw_response_length && raw_response) return IE_INVAL;
4768 /* Free output argument */
4769 xmlFreeDoc(*response); *response = NULL;
4770 if (raw_response) zfree(*raw_response);
4771 zfree(*code);
4772 zfree(*status_message);
4775 /* Check if connection is established
4776 * TODO: This check should be done downstairs. */
4777 if (!context->curl) return IE_CONNECTION_CLOSED;
4779 service_name_locale = _isds_utf82locale((char*)service_name);
4780 if (!service_name_locale) {
4781 err = IE_NOMEM;
4782 goto leave;
4785 /* Build request */
4786 request = xmlNewNode(NULL, service_name);
4787 if (!request) {
4788 isds_printf_message(context,
4789 _("Could not build %s request"), service_name_locale);
4790 err = IE_ERROR;
4791 goto leave;
4793 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
4794 if(!isds_ns) {
4795 isds_log_message(context, _("Could not create ISDS name space"));
4796 err = IE_ERROR;
4797 goto leave;
4799 xmlSetNs(request, isds_ns);
4802 /* Add XSD:tDummyInput child */
4803 INSERT_STRING(request, "dbDummy", NULL);
4806 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
4807 service_name_locale);
4809 /* Send request */
4810 err = _isds(context, SERVICE_DB_ACCESS, request, response,
4811 raw_response, raw_response_length);
4812 xmlFreeNode(request); request = NULL;
4814 if (err) {
4815 isds_log(ILF_ISDS, ILL_DEBUG,
4816 _("Processing ISDS response on %s request failed\n"),
4817 service_name_locale);
4818 goto leave;
4821 /* Check for response status */
4822 err = isds_response_status(context, SERVICE_DB_ACCESS, *response,
4823 code, status_message, NULL);
4824 if (err) {
4825 isds_log(ILF_ISDS, ILL_DEBUG,
4826 _("ISDS response on %s request is missing status\n"),
4827 service_name_locale);
4828 goto leave;
4831 /* Request processed, but nothing found */
4832 if (xmlStrcmp(*code, BAD_CAST "0000")) {
4833 char *code_locale = _isds_utf82locale((char*) *code);
4834 char *status_message_locale =
4835 _isds_utf82locale((char*) *status_message);
4836 isds_log(ILF_ISDS, ILL_DEBUG,
4837 _("Server refused %s request (code=%s, message=%s)\n"),
4838 service_name_locale, code_locale, status_message_locale);
4839 isds_log_message(context, status_message_locale);
4840 free(code_locale);
4841 free(status_message_locale);
4842 err = IE_ISDS;
4843 goto leave;
4846 leave:
4847 free(service_name_locale);
4848 xmlFreeNode(request);
4849 return err;
4851 #endif
4854 /* Get data about logged in user and his box. */
4855 isds_error isds_GetOwnerInfoFromLogin(struct isds_ctx *context,
4856 struct isds_DbOwnerInfo **db_owner_info) {
4857 isds_error err = IE_SUCCESS;
4858 #if HAVE_LIBCURL
4859 xmlDocPtr response = NULL;
4860 xmlChar *code = NULL, *message = NULL;
4861 xmlXPathContextPtr xpath_ctx = NULL;
4862 xmlXPathObjectPtr result = NULL;
4863 char *string = NULL;
4864 #endif
4866 if (!context) return IE_INVALID_CONTEXT;
4867 zfree(context->long_message);
4868 if (!db_owner_info) return IE_INVAL;
4869 isds_DbOwnerInfo_free(db_owner_info);
4871 #if HAVE_LIBCURL
4872 /* Check if connection is established */
4873 if (!context->curl) return IE_CONNECTION_CLOSED;
4876 /* Do request and check for success */
4877 err = build_send_check_dbdummy_request(context,
4878 BAD_CAST "GetOwnerInfoFromLogin",
4879 &response, NULL, NULL, &code, &message);
4880 if (err) goto leave;
4883 /* Extract data */
4884 /* Prepare structure */
4885 *db_owner_info = calloc(1, sizeof(**db_owner_info));
4886 if (!*db_owner_info) {
4887 err = IE_NOMEM;
4888 goto leave;
4890 xpath_ctx = xmlXPathNewContext(response);
4891 if (!xpath_ctx) {
4892 err = IE_ERROR;
4893 goto leave;
4895 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4896 err = IE_ERROR;
4897 goto leave;
4900 /* Set context node */
4901 result = xmlXPathEvalExpression(BAD_CAST
4902 "/isds:GetOwnerInfoFromLoginResponse/isds:dbOwnerInfo", xpath_ctx);
4903 if (!result) {
4904 err = IE_ERROR;
4905 goto leave;
4907 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
4908 isds_log_message(context, _("Missing dbOwnerInfo element"));
4909 err = IE_ISDS;
4910 goto leave;
4912 if (result->nodesetval->nodeNr > 1) {
4913 isds_log_message(context, _("Multiple dbOwnerInfo element"));
4914 err = IE_ISDS;
4915 goto leave;
4917 xpath_ctx->node = result->nodesetval->nodeTab[0];
4918 xmlXPathFreeObject(result); result = NULL;
4920 /* Extract it */
4921 err = extract_DbOwnerInfo(context, db_owner_info, xpath_ctx);
4924 leave:
4925 if (err) {
4926 isds_DbOwnerInfo_free(db_owner_info);
4929 free(string);
4930 xmlXPathFreeObject(result);
4931 xmlXPathFreeContext(xpath_ctx);
4933 free(code);
4934 free(message);
4935 xmlFreeDoc(response);
4937 if (!err)
4938 isds_log(ILF_ISDS, ILL_DEBUG,
4939 _("GetOwnerInfoFromLogin request processed by server "
4940 "successfully.\n"));
4941 #else /* not HAVE_LIBCURL */
4942 err = IE_NOTSUP;
4943 #endif
4945 return err;
4949 /* Get data about logged in user. */
4950 isds_error isds_GetUserInfoFromLogin(struct isds_ctx *context,
4951 struct isds_DbUserInfo **db_user_info) {
4952 isds_error err = IE_SUCCESS;
4953 #if HAVE_LIBCURL
4954 xmlDocPtr response = NULL;
4955 xmlChar *code = NULL, *message = NULL;
4956 xmlXPathContextPtr xpath_ctx = NULL;
4957 xmlXPathObjectPtr result = NULL;
4958 #endif
4960 if (!context) return IE_INVALID_CONTEXT;
4961 zfree(context->long_message);
4962 if (!db_user_info) return IE_INVAL;
4963 isds_DbUserInfo_free(db_user_info);
4965 #if HAVE_LIBCURL
4966 /* Check if connection is established */
4967 if (!context->curl) return IE_CONNECTION_CLOSED;
4970 /* Do request and check for success */
4971 err = build_send_check_dbdummy_request(context,
4972 BAD_CAST "GetUserInfoFromLogin",
4973 &response, NULL, NULL, &code, &message);
4974 if (err) goto leave;
4977 /* Extract data */
4978 /* Prepare structure */
4979 *db_user_info = calloc(1, sizeof(**db_user_info));
4980 if (!*db_user_info) {
4981 err = IE_NOMEM;
4982 goto leave;
4984 xpath_ctx = xmlXPathNewContext(response);
4985 if (!xpath_ctx) {
4986 err = IE_ERROR;
4987 goto leave;
4989 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
4990 err = IE_ERROR;
4991 goto leave;
4994 /* Set context node */
4995 result = xmlXPathEvalExpression(BAD_CAST
4996 "/isds:GetUserInfoFromLoginResponse/isds:dbUserInfo", xpath_ctx);
4997 if (!result) {
4998 err = IE_ERROR;
4999 goto leave;
5001 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5002 isds_log_message(context, _("Missing dbUserInfo element"));
5003 err = IE_ISDS;
5004 goto leave;
5006 if (result->nodesetval->nodeNr > 1) {
5007 isds_log_message(context, _("Multiple dbUserInfo element"));
5008 err = IE_ISDS;
5009 goto leave;
5011 xpath_ctx->node = result->nodesetval->nodeTab[0];
5012 xmlXPathFreeObject(result); result = NULL;
5014 /* Extract it */
5015 err = extract_DbUserInfo(context, db_user_info, xpath_ctx);
5017 leave:
5018 if (err) {
5019 isds_DbUserInfo_free(db_user_info);
5022 xmlXPathFreeObject(result);
5023 xmlXPathFreeContext(xpath_ctx);
5025 free(code);
5026 free(message);
5027 xmlFreeDoc(response);
5029 if (!err)
5030 isds_log(ILF_ISDS, ILL_DEBUG,
5031 _("GetUserInfoFromLogin request processed by server "
5032 "successfully.\n"));
5033 #else /* not HAVE_LIBCURL */
5034 err = IE_NOTSUP;
5035 #endif
5037 return err;
5041 /* Get expiration time of current password
5042 * @context is session context
5043 * @expiration is automatically reallocated time when password expires. If
5044 * password expiration is disabled, NULL will be returned. In case of error
5045 * it will be nulled too. */
5046 isds_error isds_get_password_expiration(struct isds_ctx *context,
5047 struct timeval **expiration) {
5048 isds_error err = IE_SUCCESS;
5049 #if HAVE_LIBCURL
5050 xmlDocPtr response = NULL;
5051 xmlChar *code = NULL, *message = NULL;
5052 xmlXPathContextPtr xpath_ctx = NULL;
5053 xmlXPathObjectPtr result = NULL;
5054 char *string = NULL;
5055 #endif
5057 if (!context) return IE_INVALID_CONTEXT;
5058 zfree(context->long_message);
5059 if (!expiration) return IE_INVAL;
5060 zfree(*expiration);
5062 #if HAVE_LIBCURL
5063 /* Check if connection is established */
5064 if (!context->curl) return IE_CONNECTION_CLOSED;
5067 /* Do request and check for success */
5068 err = build_send_check_dbdummy_request(context,
5069 BAD_CAST "GetPasswordInfo",
5070 &response, NULL, NULL, &code, &message);
5071 if (err) goto leave;
5074 /* Extract data */
5075 xpath_ctx = xmlXPathNewContext(response);
5076 if (!xpath_ctx) {
5077 err = IE_ERROR;
5078 goto leave;
5080 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5081 err = IE_ERROR;
5082 goto leave;
5085 /* Set context node */
5086 result = xmlXPathEvalExpression(BAD_CAST
5087 "/isds:GetPasswordInfoResponse", xpath_ctx);
5088 if (!result) {
5089 err = IE_ERROR;
5090 goto leave;
5092 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5093 isds_log_message(context,
5094 _("Missing GetPasswordInfoResponse element"));
5095 err = IE_ISDS;
5096 goto leave;
5098 if (result->nodesetval->nodeNr > 1) {
5099 isds_log_message(context,
5100 _("Multiple GetPasswordInfoResponse element"));
5101 err = IE_ISDS;
5102 goto leave;
5104 xpath_ctx->node = result->nodesetval->nodeTab[0];
5105 xmlXPathFreeObject(result); result = NULL;
5107 /* Extract expiration date */
5108 EXTRACT_STRING("isds:pswExpDate", string);
5109 if (string) {
5110 /* And convert it if any returned. Otherwise expiration is disabled. */
5111 err = timestring2timeval((xmlChar *) string, expiration);
5112 if (err) {
5113 char *string_locale = _isds_utf82locale(string);
5114 if (err == IE_DATE) err = IE_ISDS;
5115 isds_printf_message(context,
5116 _("Could not convert pswExpDate as ISO time: %s"),
5117 string_locale);
5118 free(string_locale);
5119 goto leave;
5123 leave:
5124 if (err) {
5125 if (*expiration) {
5126 zfree(*expiration);
5130 free(string);
5131 xmlXPathFreeObject(result);
5132 xmlXPathFreeContext(xpath_ctx);
5134 free(code);
5135 free(message);
5136 xmlFreeDoc(response);
5138 if (!err)
5139 isds_log(ILF_ISDS, ILL_DEBUG,
5140 _("GetPasswordInfo request processed by server "
5141 "successfully.\n"));
5142 #else /* not HAVE_LIBCURL */
5143 err = IE_NOTSUP;
5144 #endif
5146 return err;
5150 #if HAVE_LIBCURL
5151 /* Request delivering new TOTP code from ISDS through side channel before
5152 * changing password.
5153 * @context is session context
5154 * @password is current password.
5155 * @otp auxiliary data required, returns fine grade resolution of OTP procedure.
5156 * Please note the @otp argument must have TOTP OTP method. See isds_login()
5157 * function for more details.
5158 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5159 * NULL, if you don't care.
5160 * @return IE_SUCCESS, if new TOTP code has been sent. Or returns appropriate
5161 * error code. */
5162 static isds_error _isds_request_totp_code(struct isds_ctx *context,
5163 const char *password, struct isds_otp *otp, char **refnumber) {
5164 isds_error err = IE_SUCCESS;
5165 char *saved_url = NULL; /* No copy */
5166 #if HAVE_CURL_REAUTHORIZATION_BUG
5167 CURL *saved_curl = NULL; /* No copy */
5168 #endif
5169 xmlNsPtr isds_ns = NULL;
5170 xmlNodePtr request = NULL;
5171 xmlDocPtr response = NULL;
5172 xmlChar *code = NULL, *message = NULL;
5173 const xmlChar *codes[] = {
5174 BAD_CAST "2300",
5175 BAD_CAST "2301",
5176 BAD_CAST "2302"
5178 const char *meanings[] = {
5179 N_("Unexpected error"),
5180 N_("One-time code cannot be re-send faster than once a 30 seconds"),
5181 N_("One-time code could not been sent. Try later again.")
5183 const isds_otp_resolution resolutions[] = {
5184 OTP_RESOLUTION_UNKNOWN,
5185 OTP_RESOLUTION_TO_FAST,
5186 OTP_RESOLUTION_TOTP_NOT_SENT
5189 if (NULL == context) return IE_INVALID_CONTEXT;
5190 zfree(context->long_message);
5191 if (NULL == password) {
5192 isds_log_message(context,
5193 _("Second argument (password) of isds_change_password() "
5194 "is NULL"));
5195 return IE_INVAL;
5198 /* Check if connection is established
5199 * TODO: This check should be done downstairs. */
5200 if (!context->curl) return IE_CONNECTION_CLOSED;
5202 if (!context->otp) {
5203 isds_log_message(context, _("This function requires OTP-authenticated "
5204 "context"));
5205 return IE_INVALID_CONTEXT;
5207 if (NULL == otp) {
5208 isds_log_message(context, _("If one-time password authentication "
5209 "method is in use, requesting new OTP code requires "
5210 "one-time credentials argument either"));
5211 return IE_INVAL;
5213 if (otp->method != OTP_TIME) {
5214 isds_log_message(context, _("Requesting new time-based OTP code from "
5215 "server requires one-time password authentication "
5216 "method"));
5217 return IE_INVAL;
5219 if (otp->otp_code != NULL) {
5220 isds_log_message(context, _("Requesting new time-based OTP code from "
5221 "server requires undefined OTP code member in "
5222 "one-time credentials argument"));
5223 return IE_INVAL;
5227 /* Build request */
5228 request = xmlNewNode(NULL, BAD_CAST "SendSMSCode");
5229 if (!request) {
5230 isds_log_message(context, _("Could not build SendSMSCode request"));
5231 return IE_ERROR;
5233 isds_ns = xmlNewNs(request, BAD_CAST OISDS_NS, NULL);
5234 if(!isds_ns) {
5235 isds_log_message(context, _("Could not create ISDS name space"));
5236 xmlFreeNode(request);
5237 return IE_ERROR;
5239 xmlSetNs(request, isds_ns);
5241 /* Change URL temporarily for sending this request only */
5243 char *new_url = NULL;
5244 if ((err = _isds_build_url_from_context(context,
5245 "%1$.*2$sasws/changePassword", &new_url))) {
5246 goto leave;
5248 saved_url = context->url;
5249 context->url = new_url;
5252 /* Store credentials for sending this request only */
5253 context->otp_credentials = otp;
5254 _isds_discard_credentials(context, 0);
5255 if ((err = _isds_store_credentials(context, context->saved_username,
5256 password, NULL))) {
5257 _isds_discard_credentials(context, 0);
5258 goto leave;
5260 #if HAVE_CURL_REAUTHORIZATION_BUG
5261 saved_curl = context->curl;
5262 context->curl = curl_easy_init();
5263 if (NULL == context->curl) {
5264 err = IE_ERROR;
5265 goto leave;
5267 if (context->timeout) {
5268 err = isds_set_timeout(context, context->timeout);
5269 if (err) goto leave;
5271 #endif
5273 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending SendSMSCode request to ISDS\n"));
5275 /* Sent request */
5276 err = _isds(context, SERVICE_ASWS, request, &response, NULL, NULL);
5278 /* Remove temporal credentials */
5279 _isds_discard_credentials(context, 0);
5280 /* Detach pointer to OTP credentials from context */
5281 context->otp_credentials = NULL;
5282 /* Keep context->otp true to keep signaling this is OTP session */
5284 /* Destroy request */
5285 xmlFreeNode(request); request = NULL;
5287 if (err) {
5288 isds_log(ILF_ISDS, ILL_DEBUG,
5289 _("Processing ISDS response on SendSMSCode request failed\n"));
5290 goto leave;
5293 /* Check for response status */
5294 err = isds_response_status(context, SERVICE_ASWS, response,
5295 &code, &message, (xmlChar **)refnumber);
5296 if (err) {
5297 isds_log(ILF_ISDS, ILL_DEBUG,
5298 _("ISDS response on SendSMSCode request is missing "
5299 "status\n"));
5300 goto leave;
5303 /* Check for error */
5304 if (xmlStrcmp(code, BAD_CAST "0000")) {
5305 char *code_locale = _isds_utf82locale((char*)code);
5306 char *message_locale = _isds_utf82locale((char*)message);
5307 int i;
5308 isds_log(ILF_ISDS, ILL_DEBUG,
5309 _("Server refused to send new code on SendSMSCode "
5310 "request (code=%s, message=%s)\n"),
5311 code_locale, message_locale);
5313 /* Check for known error codes */
5314 for (i = 0; i < sizeof(codes)/sizeof(*codes); i++) {
5315 if (!xmlStrcmp(code, codes[i])) break;
5317 if (i < sizeof(codes)/sizeof(*codes)) {
5318 isds_log_message(context, _(meanings[i]));
5319 /* Mimic otp->resolution according to the code, specification does
5320 * prescribe OTP header to be available. */
5321 if (OTP_RESOLUTION_SUCCESS == otp->resolution &&
5322 OTP_RESOLUTION_UNKNOWN != resolutions[i])
5323 otp->resolution = resolutions[i];
5324 } else
5325 isds_log_message(context, message_locale);
5327 free(code_locale);
5328 free(message_locale);
5330 err = IE_ISDS;
5331 goto leave;
5334 /* Otherwise new code sent successfully */
5335 /* Mimic otp->resolution according to the code, specification does
5336 * prescribe OTP header to be available. */
5337 if (OTP_RESOLUTION_SUCCESS == otp->resolution)
5338 otp->resolution = OTP_RESOLUTION_TOTP_SENT;
5340 leave:
5341 if (NULL != saved_url) {
5342 /* Revert URL to original one */
5343 zfree(context->url);
5344 context->url = saved_url;
5346 #if HAVE_CURL_REAUTHORIZATION_BUG
5347 if (NULL != saved_curl) {
5348 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5349 context->curl = saved_curl;
5351 #endif
5353 free(code);
5354 free(message);
5355 xmlFreeDoc(response);
5356 xmlFreeNode(request);
5358 if (!err)
5359 isds_log(ILF_ISDS, ILL_DEBUG,
5360 _("New OTP code has been sent successfully on SendSMSCode "
5361 "request.\n"));
5362 return err;
5366 /* Convert response status code to isds_error code and set long message
5367 * @context is context to save long message to
5368 * @map is mapping from codes to errors and messages. Pass NULL for generic
5369 * handling.
5370 * @code is status code to translate
5371 * @message is non-localized status message to put into long message in case
5372 * of uknown error. It can be NULL if server did not provide any.
5373 * @return desired isds_error or IE_ISDS for unknown code or IE_INVAL for
5374 * invalid invocation. */
5375 static isds_error statuscode2isds_error(struct isds_ctx *context,
5376 const struct code_map_isds_error *map,
5377 const xmlChar *code, const xmlChar *message) {
5378 if (NULL == code) {
5379 isds_log_message(context,
5380 _("NULL status code passed to statuscode2isds_error()"));
5381 return IE_INVAL;
5384 if (NULL != map) {
5385 /* Check for known error codes */
5386 for (int i=0; map->codes[i] != NULL; i++) {
5387 if (!xmlStrcmp(code, map->codes[i])) {
5388 isds_log_message(context, _(map->meanings[i]));
5389 return map->errors[i];
5394 /* Other error */
5395 if (xmlStrcmp(code, BAD_CAST "0000")) {
5396 char *message_locale = _isds_utf82locale((char*)message);
5397 if (NULL == message_locale)
5398 isds_log_message(context, _("ISDS server returned unknown error"));
5399 else
5400 isds_log_message(context, message_locale);
5401 free(message_locale);
5402 return IE_ISDS;
5405 return IE_SUCCESS;
5407 #endif
5410 /* Change user password in ISDS.
5411 * User must supply old password, new password will takes effect after some
5412 * time, current session can continue. Password must fulfill some constraints.
5413 * @context is session context
5414 * @old_password is current password.
5415 * @new_password is requested new password
5416 * @otp auxiliary data required if one-time password authentication is in use,
5417 * defines OTP code (if known) and returns fine grade resolution of OTP
5418 * procedure. Pass NULL, if one-time password authentication is not needed.
5419 * Please note the @otp argument must match OTP method used at log-in time. See
5420 * isds_login() function for more details.
5421 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5422 * NULL, if you don't care.
5423 * @return IE_SUCCESS, if password has been changed. Or returns appropriate
5424 * error code. It can return IE_PARTIAL_SUCCESS if OTP is in use and server is
5425 * awaiting OTP code that has been delivered by side channel to the user. */
5426 isds_error isds_change_password(struct isds_ctx *context,
5427 const char *old_password, const char *new_password,
5428 struct isds_otp *otp, char **refnumber) {
5429 isds_error err = IE_SUCCESS;
5430 #if HAVE_LIBCURL
5431 char *saved_url = NULL; /* No copy */
5432 #if HAVE_CURL_REAUTHORIZATION_BUG
5433 CURL *saved_curl = NULL; /* No copy */
5434 #endif
5435 xmlNsPtr isds_ns = NULL;
5436 xmlNodePtr request = NULL, node;
5437 xmlDocPtr response = NULL;
5438 xmlChar *code = NULL, *message = NULL;
5439 const xmlChar *codes[] = {
5440 BAD_CAST "1066",
5441 BAD_CAST "1067",
5442 BAD_CAST "1079",
5443 BAD_CAST "1080",
5444 BAD_CAST "1081",
5445 BAD_CAST "1082",
5446 BAD_CAST "1083",
5447 BAD_CAST "1090",
5448 BAD_CAST "1091",
5449 BAD_CAST "2300",
5450 BAD_CAST "9204"
5452 const char *meanings[] = {
5453 N_("Password length must be between 8 and 32 characters"),
5454 N_("Password cannot be reused"), /* Server does not distinguish 1067
5455 and 1091 on ChangePasswordOTP */
5456 N_("Password contains forbidden character"),
5457 N_("Password must contain at least one upper-case letter, "
5458 "one lower-case, and one digit"),
5459 N_("Password cannot contain sequence of three identical characters"),
5460 N_("Password cannot contain user identifier"),
5461 N_("Password is too simmple"),
5462 N_("Old password is not valid"),
5463 N_("Password cannot be reused"),
5464 N_("Unexpected error"),
5465 N_("LDAP update error")
5467 #endif
5469 if (!context) return IE_INVALID_CONTEXT;
5470 zfree(context->long_message);
5471 if (NULL != refnumber)
5472 zfree(*refnumber);
5473 if (NULL == old_password) {
5474 isds_log_message(context,
5475 _("Second argument (old password) of isds_change_password() "
5476 "is NULL"));
5477 return IE_INVAL;
5479 if (NULL == otp && NULL == new_password) {
5480 isds_log_message(context,
5481 _("Third argument (new password) of isds_change_password() "
5482 "is NULL"));
5483 return IE_INVAL;
5486 #if HAVE_LIBCURL
5487 /* Check if connection is established
5488 * TODO: This check should be done downstairs. */
5489 if (!context->curl) return IE_CONNECTION_CLOSED;
5491 if (context->otp && NULL == otp) {
5492 isds_log_message(context, _("If one-time password authentication "
5493 "method is in use, changing password requires one-time "
5494 "credentials either"));
5495 return IE_INVAL;
5498 /* Build ChangeISDSPassword request */
5499 request = xmlNewNode(NULL, (NULL == otp) ? BAD_CAST "ChangeISDSPassword" :
5500 BAD_CAST "ChangePasswordOTP");
5501 if (!request) {
5502 isds_log_message(context, (NULL == otp) ?
5503 _("Could not build ChangeISDSPassword request") :
5504 _("Could not build ChangePasswordOTP request"));
5505 return IE_ERROR;
5507 isds_ns = xmlNewNs(request,
5508 (NULL == otp) ? BAD_CAST ISDS_NS : BAD_CAST OISDS_NS,
5509 NULL);
5510 if(!isds_ns) {
5511 isds_log_message(context, _("Could not create ISDS name space"));
5512 xmlFreeNode(request);
5513 return IE_ERROR;
5515 xmlSetNs(request, isds_ns);
5517 INSERT_STRING(request, "dbOldPassword", old_password);
5518 INSERT_STRING(request, "dbNewPassword", new_password);
5520 if (NULL != otp) {
5521 otp->resolution = OTP_RESOLUTION_UNKNOWN;
5522 switch (otp->method) {
5523 case OTP_HMAC:
5524 isds_log(ILF_SEC, ILL_INFO,
5525 _("Selected authentication method: "
5526 "HMAC-based one-time password\n"));
5527 INSERT_STRING(request, "dbOTPType", BAD_CAST "HOTP");
5528 break;
5529 case OTP_TIME:
5530 isds_log(ILF_SEC, ILL_INFO,
5531 _("Selected authentication method: "
5532 "Time-based one-time password\n"));
5533 INSERT_STRING(request, "dbOTPType", BAD_CAST "TOTP");
5534 if (otp->otp_code == NULL) {
5535 isds_log(ILF_SEC, ILL_INFO,
5536 _("OTP code has not been provided by "
5537 "application, requesting server for "
5538 "new one.\n"));
5539 err = _isds_request_totp_code(context, old_password, otp,
5540 refnumber);
5541 if (err == IE_SUCCESS) err = IE_PARTIAL_SUCCESS;
5542 goto leave;
5544 } else {
5545 isds_log(ILF_SEC, ILL_INFO,
5546 _("OTP code has been provided by "
5547 "application, not requesting server "
5548 "for new one.\n"));
5550 break;
5551 default:
5552 isds_log_message(context,
5553 _("Unknown one-time password authentication "
5554 "method requested by application"));
5555 err = IE_ENUM;
5556 goto leave;
5559 /* Change URL temporarily for sending this request only */
5561 char *new_url = NULL;
5562 if ((err = _isds_build_url_from_context(context,
5563 "%1$.*2$sasws/changePassword", &new_url))) {
5564 goto leave;
5566 saved_url = context->url;
5567 context->url = new_url;
5570 /* Store credentials for sending this request only */
5571 context->otp_credentials = otp;
5572 _isds_discard_credentials(context, 0);
5573 if ((err = _isds_store_credentials(context, context->saved_username,
5574 old_password, NULL))) {
5575 _isds_discard_credentials(context, 0);
5576 goto leave;
5578 #if HAVE_CURL_REAUTHORIZATION_BUG
5579 saved_curl = context->curl;
5580 context->curl = curl_easy_init();
5581 if (NULL == context->curl) {
5582 err = IE_ERROR;
5583 goto leave;
5585 if (context->timeout) {
5586 err = isds_set_timeout(context, context->timeout);
5587 if (err) goto leave;
5589 #endif
5592 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5593 _("Sending ChangeISDSPassword request to ISDS\n") :
5594 _("Sending ChangePasswordOTP request to ISDS\n"));
5596 /* Sent request */
5597 err = _isds(context, (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS,
5598 request, &response, NULL, NULL);
5600 if (otp) {
5601 /* Remove temporal credentials */
5602 _isds_discard_credentials(context, 0);
5603 /* Detach pointer to OTP credentials from context */
5604 context->otp_credentials = NULL;
5605 /* Keep context->otp true to keep signaling this is OTP session */
5608 /* Destroy request */
5609 xmlFreeNode(request); request = NULL;
5611 if (err) {
5612 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5613 _("Processing ISDS response on ChangeISDSPassword "
5614 "request failed\n") :
5615 _("Processing ISDS response on ChangePasswordOTP "
5616 "request failed\n"));
5617 goto leave;
5620 /* Check for response status */
5621 err = isds_response_status(context,
5622 (NULL == otp) ? SERVICE_DB_ACCESS : SERVICE_ASWS, response,
5623 &code, &message, (xmlChar **)refnumber);
5624 if (err) {
5625 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5626 _("ISDS response on ChangeISDSPassword request is missing "
5627 "status\n") :
5628 _("ISDS response on ChangePasswordOTP request is missing "
5629 "status\n"));
5630 goto leave;
5633 /* Check for known error codes */
5634 for (int i=0; i < sizeof(codes)/sizeof(*codes); i++) {
5635 if (!xmlStrcmp(code, codes[i])) {
5636 char *code_locale = _isds_utf82locale((char*)code);
5637 char *message_locale = _isds_utf82locale((char*)message);
5638 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5639 _("Server refused to change password on ChangeISDSPassword "
5640 "request (code=%s, message=%s)\n") :
5641 _("Server refused to change password on ChangePasswordOTP "
5642 "request (code=%s, message=%s)\n"),
5643 code_locale, message_locale);
5644 free(code_locale);
5645 free(message_locale);
5646 isds_log_message(context, _(meanings[i]));
5647 err = IE_INVAL;
5648 goto leave;
5652 /* Other error */
5653 if (xmlStrcmp(code, BAD_CAST "0000")) {
5654 char *code_locale = _isds_utf82locale((char*)code);
5655 char *message_locale = _isds_utf82locale((char*)message);
5656 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5657 _("Server refused to change password on ChangeISDSPassword "
5658 "request (code=%s, message=%s)\n") :
5659 _("Server refused to change password on ChangePasswordOTP "
5660 "request (code=%s, message=%s)\n"),
5661 code_locale, message_locale);
5662 isds_log_message(context, message_locale);
5663 free(code_locale);
5664 free(message_locale);
5665 err = IE_ISDS;
5666 goto leave;
5669 /* Otherwise password changed successfully */
5671 leave:
5672 if (NULL != saved_url) {
5673 /* Revert URL to original one */
5674 zfree(context->url);
5675 context->url = saved_url;
5677 #if HAVE_CURL_REAUTHORIZATION_BUG
5678 if (NULL != saved_curl) {
5679 if (context->curl != NULL) curl_easy_cleanup(context->curl);
5680 context->curl = saved_curl;
5682 #endif
5684 free(code);
5685 free(message);
5686 xmlFreeDoc(response);
5687 xmlFreeNode(request);
5689 if (!err)
5690 isds_log(ILF_ISDS, ILL_DEBUG, (NULL == otp) ?
5691 _("Password changed successfully on ChangeISDSPassword "
5692 "request.\n") :
5693 _("Password changed successfully on ChangePasswordOTP "
5694 "request.\n"));
5695 #else /* not HAVE_LIBCURL */
5696 err = IE_NOTSUP;
5697 #endif
5699 return err;
5703 #if HAVE_LIBCURL
5704 /* Generic middle part with request sending and response check.
5705 * It sends prepared request and checks for error code.
5706 * @context is ISDS session context.
5707 * @service is ISDS service handler
5708 * @service_name is name in scope of given @service
5709 * @request is XML tree with request. Will be freed to save memory.
5710 * @response is XML document outputting ISDS response.
5711 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5712 * @map is mapping from status code to library error. Pass NULL if no special
5713 * handling is requested.
5714 * NULL, if you don't care. */
5715 static isds_error send_destroy_request_check_response(
5716 struct isds_ctx *context,
5717 const isds_service service, const xmlChar *service_name,
5718 xmlNodePtr *request, xmlDocPtr *response, xmlChar **refnumber,
5719 const struct code_map_isds_error *map) {
5720 isds_error err = IE_SUCCESS;
5721 char *service_name_locale = NULL;
5722 xmlChar *code = NULL, *message = NULL;
5725 if (!context) return IE_INVALID_CONTEXT;
5726 if (!service_name || *service_name == '\0' || !request || !*request ||
5727 !response)
5728 return IE_INVAL;
5730 /* Check if connection is established
5731 * TODO: This check should be done downstairs. */
5732 if (!context->curl) return IE_CONNECTION_CLOSED;
5734 service_name_locale = _isds_utf82locale((char*) service_name);
5735 if (!service_name_locale) {
5736 err = IE_NOMEM;
5737 goto leave;
5740 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending %s request to ISDS\n"),
5741 service_name_locale);
5743 /* Send request */
5744 err = _isds(context, service, *request, response, NULL, NULL);
5745 xmlFreeNode(*request); *request = NULL;
5747 if (err) {
5748 isds_log(ILF_ISDS, ILL_DEBUG,
5749 _("Processing ISDS response on %s request failed\n"),
5750 service_name_locale);
5751 goto leave;
5754 /* Check for response status */
5755 err = isds_response_status(context, service, *response,
5756 &code, &message, refnumber);
5757 if (err) {
5758 isds_log(ILF_ISDS, ILL_DEBUG,
5759 _("ISDS response on %s request is missing status\n"),
5760 service_name_locale);
5761 goto leave;
5764 err = statuscode2isds_error(context, map, code, message);
5766 /* Request processed, but server failed */
5767 if (xmlStrcmp(code, BAD_CAST "0000")) {
5768 char *code_locale = _isds_utf82locale((char*) code);
5769 char *message_locale = _isds_utf82locale((char*) message);
5770 isds_log(ILF_ISDS, ILL_DEBUG,
5771 _("Server refused %s request (code=%s, message=%s)\n"),
5772 service_name_locale, code_locale, message_locale);
5773 free(code_locale);
5774 free(message_locale);
5775 goto leave;
5779 leave:
5780 free(code);
5781 free(message);
5782 if (err && *response) {
5783 xmlFreeDoc(*response);
5784 *response = NULL;
5786 if (*request) {
5787 xmlFreeNode(*request);
5788 *request = NULL;
5790 free(service_name_locale);
5792 return err;
5796 /* Generic bottom half with request sending.
5797 * It sends prepared request, checks for error code, destroys response and
5798 * request and log success or failure.
5799 * @context is ISDS session context.
5800 * @service is ISDS service handler
5801 * @service_name is name in scope of given @service
5802 * @request is XML tree with request. Will be freed to save memory.
5803 * @refnumber is reallocated serial number of request assigned by ISDS. Use
5804 * NULL, if you don't care. */
5805 static isds_error send_request_check_drop_response(
5806 struct isds_ctx *context,
5807 const isds_service service, const xmlChar *service_name,
5808 xmlNodePtr *request, xmlChar **refnumber) {
5809 isds_error err = IE_SUCCESS;
5810 xmlDocPtr response = NULL;
5813 if (!context) return IE_INVALID_CONTEXT;
5814 if (!service_name || *service_name == '\0' || !request || !*request)
5815 return IE_INVAL;
5817 /* Send request and check response*/
5818 err = send_destroy_request_check_response(context,
5819 service, service_name, request, &response, refnumber, NULL);
5821 xmlFreeDoc(response);
5823 if (*request) {
5824 xmlFreeNode(*request);
5825 *request = NULL;
5828 if (!err) {
5829 char *service_name_locale = _isds_utf82locale((char *) service_name);
5830 isds_log(ILF_ISDS, ILL_DEBUG,
5831 _("%s request processed by server successfully.\n"),
5832 service_name_locale);
5833 free(service_name_locale);
5836 return err;
5840 /* Insert isds_credentials_delivery structure into XML request if not NULL
5841 * @context is session context
5842 * @credentials_delivery is NULL if to omit, non-NULL to signal on-line
5843 * credentials delivery. The email field is passed.
5844 * @parent is XML element where to insert */
5845 static isds_error insert_credentials_delivery(struct isds_ctx *context,
5846 const struct isds_credentials_delivery *credentials_delivery,
5847 xmlNodePtr parent) {
5848 isds_error err = IE_SUCCESS;
5849 xmlNodePtr node;
5851 if (!context) return IE_INVALID_CONTEXT;
5852 if (!parent) return IE_INVAL;
5854 if (credentials_delivery) {
5855 /* Following elements are valid only for services:
5856 * NewAccessData, AddDataBoxUser, CreateDataBox */
5857 INSERT_SCALAR_BOOLEAN(parent, "dbVirtual", 1);
5858 INSERT_STRING(parent, "email", credentials_delivery->email);
5861 leave:
5862 return err;
5866 /* Extract credentials delivery from ISDS response.
5867 * @context is session context
5868 * @credentials_delivery is pointer to valid structure to fill in returned
5869 * user's password (and new log-in name). If NULL, do not extract the data.
5870 * @response is pointer to XML document with ISDS response
5871 * @request_name is UTF-8 encoded name of ISDS service the @response it to.
5872 * @return IE_SUCCESS even if new user name has not been found because it's not
5873 * clear whether it's returned always. */
5874 static isds_error extract_credentials_delivery(struct isds_ctx *context,
5875 struct isds_credentials_delivery *credentials_delivery,
5876 xmlDocPtr response, const char *request_name) {
5877 isds_error err = IE_SUCCESS;
5878 xmlXPathContextPtr xpath_ctx = NULL;
5879 xmlXPathObjectPtr result = NULL;
5880 char *xpath_query = NULL;
5882 if (!context) return IE_INVALID_CONTEXT;
5883 if (credentials_delivery) {
5884 zfree(credentials_delivery->token);
5885 zfree(credentials_delivery->new_user_name);
5887 if (!response || !request_name || !*request_name) return IE_INVAL;
5890 /* Extract optional token */
5891 if (credentials_delivery) {
5892 xpath_ctx = xmlXPathNewContext(response);
5893 if (!xpath_ctx) {
5894 err = IE_ERROR;
5895 goto leave;
5897 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
5898 err = IE_ERROR;
5899 goto leave;
5902 /* Verify root element */
5903 if (-1 == isds_asprintf(&xpath_query, "/isds:%sResponse",
5904 request_name)) {
5905 err = IE_NOMEM;
5906 goto leave;
5908 result = xmlXPathEvalExpression(BAD_CAST xpath_query, xpath_ctx);
5909 if (!result) {
5910 err = IE_ERROR;
5911 goto leave;
5913 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
5914 char *request_name_locale = _isds_utf82locale(request_name);
5915 isds_log(ILF_ISDS, ILL_WARNING,
5916 _("Wrong element in ISDS response for %s request "
5917 "while extracting credentials delivery details\n"),
5918 request_name_locale);
5919 free(request_name_locale);
5920 err = IE_ERROR;
5921 goto leave;
5923 xpath_ctx->node = result->nodesetval->nodeTab[0];
5926 /* XXX: isds:dbUserID is provided only on NewAccessData. Leave it
5927 * optional. */
5928 EXTRACT_STRING("isds:dbUserID", credentials_delivery->new_user_name);
5930 EXTRACT_STRING("isds:dbAccessDataId", credentials_delivery->token);
5931 if (!credentials_delivery->token) {
5932 char *request_name_locale = _isds_utf82locale(request_name);
5933 isds_log(ILF_ISDS, ILL_ERR,
5934 _("ISDS did not return token on %s request "
5935 "even if requested\n"), request_name_locale);
5936 free(request_name_locale);
5937 err = IE_ERROR;
5941 leave:
5942 free(xpath_query);
5943 xmlXPathFreeObject(result);
5944 xmlXPathFreeContext(xpath_ctx);
5946 return err;
5950 /* Build XSD:tCreateDBInput request type for box creating.
5951 * @context is session context
5952 * @request outputs built XML tree
5953 * @service_name is request name of SERVICE_DB_MANIPULATION service
5954 * @box is box description to create including single primary user (in case of
5955 * FO box type)
5956 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
5957 * box, or contact address of PFO box owner)
5958 * @former_names is optional former name of box owner. Pass NULL if otherwise.
5959 * @upper_box_id is optional ID of supper box if currently created box is
5960 * subordinated.
5961 * @ceo_label is optional title of OVM box owner (e.g. mayor); NULL, if you
5962 * don't care.
5963 * @credentials_delivery is valid pointer if ISDS should return token that box
5964 * owner can use to obtain his new credentials in on-line way. Then valid email
5965 * member value should be supplied.
5966 * @approval is optional external approval of box manipulation */
5967 static isds_error build_CreateDBInput_request(struct isds_ctx *context,
5968 xmlNodePtr *request, const xmlChar *service_name,
5969 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
5970 const xmlChar *former_names, const xmlChar *upper_box_id,
5971 const xmlChar *ceo_label,
5972 const struct isds_credentials_delivery *credentials_delivery,
5973 const struct isds_approval *approval) {
5974 isds_error err = IE_SUCCESS;
5975 xmlNsPtr isds_ns = NULL;
5976 xmlNodePtr node, dbPrimaryUsers;
5977 xmlChar *string = NULL;
5978 const struct isds_list *item;
5981 if (!context) return IE_INVALID_CONTEXT;
5982 if (!request || !service_name || service_name[0] == '\0' || !box)
5983 return IE_INVAL;
5986 /* Build CreateDataBox-similar request */
5987 *request = xmlNewNode(NULL, service_name);
5988 if (!*request) {
5989 char *service_name_locale = _isds_utf82locale((char*) service_name);
5990 isds_printf_message(context, _("Could build %s request"),
5991 service_name_locale);
5992 free(service_name_locale);
5993 return IE_ERROR;
5995 if (context->type == CTX_TYPE_TESTING_REQUEST_COLLECTOR) {
5996 isds_ns = xmlNewNs(*request, BAD_CAST ISDS1_NS, NULL);
5997 if (!isds_ns) {
5998 isds_log_message(context, _("Could not create ISDS1 name space"));
5999 xmlFreeNode(*request);
6000 return IE_ERROR;
6002 } else {
6003 isds_ns = xmlNewNs(*request, BAD_CAST ISDS_NS, NULL);
6004 if (!isds_ns) {
6005 isds_log_message(context, _("Could not create ISDS name space"));
6006 xmlFreeNode(*request);
6007 return IE_ERROR;
6010 xmlSetNs(*request, isds_ns);
6012 INSERT_ELEMENT(node, *request, "dbOwnerInfo");
6013 err = insert_DbOwnerInfo(context, box, node);
6014 if (err) goto leave;
6016 /* Insert users */
6017 /* XXX: There is bug in XSD: XSD says at least one dbUserInfo must exist,
6018 * verbose documentation allows none dbUserInfo */
6019 INSERT_ELEMENT(dbPrimaryUsers, *request, "dbPrimaryUsers");
6020 for (item = users; item; item = item->next) {
6021 if (item->data) {
6022 INSERT_ELEMENT(node, dbPrimaryUsers, "dbUserInfo");
6023 err = insert_DbUserInfo(context,
6024 (struct isds_DbUserInfo *) item->data, node);
6025 if (err) goto leave;
6029 INSERT_STRING(*request, "dbFormerNames", former_names);
6030 INSERT_STRING(*request, "dbUpperDBId", upper_box_id);
6031 INSERT_STRING(*request, "dbCEOLabel", ceo_label);
6033 err = insert_credentials_delivery(context, credentials_delivery, *request);
6034 if (err) goto leave;
6036 err = insert_GExtApproval(context, approval, *request);
6037 if (err) goto leave;
6039 leave:
6040 if (err) {
6041 xmlFreeNode(*request);
6042 *request = NULL;
6044 free(string);
6045 return err;
6047 #endif /* HAVE_LIBCURL */
6050 /* Create new box.
6051 * @context is session context
6052 * @box is box description to create including single primary user (in case of
6053 * FO box type). It outputs box ID assigned by ISDS in dbID element.
6054 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
6055 * box, or contact address of PFO box owner)
6056 * @former_names is optional former name of box owner. Pass NULL if you don't care.
6057 * @upper_box_id is optional ID of supper box if currently created box is
6058 * subordinated.
6059 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6060 * @credentials_delivery is NULL if new password should be delivered off-line
6061 * to box owner. It is valid pointer if owner should obtain new password on-line
6062 * on dedicated web server. Then input @credentials_delivery.email value is
6063 * his e-mail address he must provide to dedicated web server together
6064 * with output reallocated @credentials_delivery.token member. Output
6065 * member @credentials_delivery.new_user_name is unused up on this call.
6066 * @approval is optional external approval of box manipulation
6067 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6068 * NULL, if you don't care.*/
6069 isds_error isds_add_box(struct isds_ctx *context,
6070 struct isds_DbOwnerInfo *box, const struct isds_list *users,
6071 const char *former_names, const char *upper_box_id,
6072 const char *ceo_label,
6073 struct isds_credentials_delivery *credentials_delivery,
6074 const struct isds_approval *approval, char **refnumber) {
6075 isds_error err = IE_SUCCESS;
6076 #if HAVE_LIBCURL
6077 xmlNodePtr request = NULL;
6078 xmlDocPtr response = NULL;
6079 xmlXPathContextPtr xpath_ctx = NULL;
6080 xmlXPathObjectPtr result = NULL;
6081 #endif
6084 if (!context) return IE_INVALID_CONTEXT;
6085 zfree(context->long_message);
6086 if (credentials_delivery) {
6087 zfree(credentials_delivery->token);
6088 zfree(credentials_delivery->new_user_name);
6090 if (!box) return IE_INVAL;
6092 #if HAVE_LIBCURL
6093 /* Scratch box ID */
6094 zfree(box->dbID);
6096 /* Build CreateDataBox request */
6097 err = build_CreateDBInput_request(context,
6098 &request, BAD_CAST "CreateDataBox",
6099 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6100 (xmlChar *) ceo_label, credentials_delivery, approval);
6101 if (err) goto leave;
6103 /* Send it to server and process response */
6104 err = send_destroy_request_check_response(context,
6105 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6106 &response, (xmlChar **) refnumber, NULL);
6108 /* Extract box ID */
6109 xpath_ctx = xmlXPathNewContext(response);
6110 if (!xpath_ctx) {
6111 err = IE_ERROR;
6112 goto leave;
6114 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6115 err = IE_ERROR;
6116 goto leave;
6118 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
6120 /* Extract optional token */
6121 err = extract_credentials_delivery(context, credentials_delivery, response,
6122 "CreateDataBox");
6124 leave:
6125 xmlXPathFreeObject(result);
6126 xmlXPathFreeContext(xpath_ctx);
6127 xmlFreeDoc(response);
6128 xmlFreeNode(request);
6130 if (!err) {
6131 isds_log(ILF_ISDS, ILL_DEBUG,
6132 _("CreateDataBox request processed by server successfully.\n"));
6134 #else /* not HAVE_LIBCURL */
6135 err = IE_NOTSUP;
6136 #endif
6138 return err;
6142 /* Notify ISDS about new PFO entity.
6143 * This function has no real effect.
6144 * @context is session context
6145 * @box is PFO description including single primary user.
6146 * @users is list of struct isds_DbUserInfo (contact address of PFO box owner)
6147 * @former_names is optional undocumented string. Pass NULL if you don't care.
6148 * @upper_box_id is optional ID of supper box if currently created box is
6149 * subordinated.
6150 * @ceo_label is optional title of OVM box owner (e.g. mayor)
6151 * @approval is optional external approval of box manipulation
6152 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6153 * NULL, if you don't care.*/
6154 isds_error isds_add_pfoinfo(struct isds_ctx *context,
6155 const struct isds_DbOwnerInfo *box, const struct isds_list *users,
6156 const char *former_names, const char *upper_box_id,
6157 const char *ceo_label, const struct isds_approval *approval,
6158 char **refnumber) {
6159 isds_error err = IE_SUCCESS;
6160 #if HAVE_LIBCURL
6161 xmlNodePtr request = NULL;
6162 #endif
6164 if (!context) return IE_INVALID_CONTEXT;
6165 zfree(context->long_message);
6166 if (!box) return IE_INVAL;
6168 #if HAVE_LIBCURL
6169 /* Build CreateDataBoxPFOInfo request */
6170 err = build_CreateDBInput_request(context,
6171 &request, BAD_CAST "CreateDataBoxPFOInfo",
6172 box, users, (xmlChar *) former_names, (xmlChar *) upper_box_id,
6173 (xmlChar *) ceo_label, NULL, approval);
6174 if (err) goto leave;
6176 /* Send it to server and process response */
6177 err = send_request_check_drop_response(context,
6178 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
6179 (xmlChar **) refnumber);
6180 /* XXX: XML Schema names output dbID element but textual documentation
6181 * states no box identifier is returned. */
6182 leave:
6183 xmlFreeNode(request);
6184 #else /* not HAVE_LIBCURL */
6185 err = IE_NOTSUP;
6186 #endif
6187 return err;
6191 /* Common implementation for removing given box.
6192 * @context is session context
6193 * @service_name is UTF-8 encoded name fo ISDS service
6194 * @box is box description to delete
6195 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6196 * carry sane value. If NULL, do not inject this information into request.
6197 * @approval is optional external approval of box manipulation
6198 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6199 * NULL, if you don't care.*/
6200 isds_error _isds_delete_box_common(struct isds_ctx *context,
6201 const xmlChar *service_name,
6202 const struct isds_DbOwnerInfo *box, const struct tm *since,
6203 const struct isds_approval *approval, char **refnumber) {
6204 isds_error err = IE_SUCCESS;
6205 #if HAVE_LIBCURL
6206 xmlNsPtr isds_ns = NULL;
6207 xmlNodePtr request = NULL;
6208 xmlNodePtr node;
6209 xmlChar *string = NULL;
6210 #endif
6213 if (!context) return IE_INVALID_CONTEXT;
6214 zfree(context->long_message);
6215 if (!service_name || !*service_name || !box) return IE_INVAL;
6218 #if HAVE_LIBCURL
6219 /* Build DeleteDataBox(Promptly) request */
6220 request = xmlNewNode(NULL, service_name);
6221 if (!request) {
6222 char *service_name_locale = _isds_utf82locale((char*)service_name);
6223 isds_printf_message(context,
6224 _("Could build %s request"), service_name_locale);
6225 free(service_name_locale);
6226 return IE_ERROR;
6228 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6229 if(!isds_ns) {
6230 isds_log_message(context, _("Could not create ISDS name space"));
6231 xmlFreeNode(request);
6232 return IE_ERROR;
6234 xmlSetNs(request, isds_ns);
6236 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6237 err = insert_DbOwnerInfo(context, box, node);
6238 if (err) goto leave;
6240 if (since) {
6241 err = tm2datestring(since, &string);
6242 if (err) {
6243 isds_log_message(context,
6244 _("Could not convert `since' argument to ISO date string"));
6245 goto leave;
6247 INSERT_STRING(request, "dbOwnerTerminationDate", string);
6248 zfree(string);
6251 err = insert_GExtApproval(context, approval, request);
6252 if (err) goto leave;
6255 /* Send it to server and process response */
6256 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6257 service_name, &request, (xmlChar **) refnumber);
6259 leave:
6260 xmlFreeNode(request);
6261 free(string);
6262 #else /* not HAVE_LIBCURL */
6263 err = IE_NOTSUP;
6264 #endif
6265 return err;
6269 /* Remove given box permanently.
6270 * @context is session context
6271 * @box is box description to delete
6272 * @since is date of box owner cancellation. Only tm_year, tm_mon and tm_mday
6273 * carry sane value.
6274 * @approval is optional external approval of box manipulation
6275 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6276 * NULL, if you don't care.*/
6277 isds_error isds_delete_box(struct isds_ctx *context,
6278 const struct isds_DbOwnerInfo *box, const struct tm *since,
6279 const struct isds_approval *approval, char **refnumber) {
6280 if (!context) return IE_INVALID_CONTEXT;
6281 zfree(context->long_message);
6282 if (!box || !since) return IE_INVAL;
6284 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBox",
6285 box, since, approval, refnumber);
6289 /* Undocumented function.
6290 * @context is session context
6291 * @box is box description to delete
6292 * @approval is optional external approval of box manipulation
6293 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6294 * NULL, if you don't care.*/
6295 isds_error isds_delete_box_promptly(struct isds_ctx *context,
6296 const struct isds_DbOwnerInfo *box,
6297 const struct isds_approval *approval, char **refnumber) {
6298 if (!context) return IE_INVALID_CONTEXT;
6299 zfree(context->long_message);
6300 if (!box) return IE_INVAL;
6302 return _isds_delete_box_common(context, BAD_CAST "DeleteDataBoxPromptly",
6303 box, NULL, approval, refnumber);
6307 /* Update data about given box.
6308 * @context is session context
6309 * @old_box current box description
6310 * @new_box are updated data about @old_box
6311 * @approval is optional external approval of box manipulation
6312 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6313 * NULL, if you don't care.*/
6314 isds_error isds_UpdateDataBoxDescr(struct isds_ctx *context,
6315 const struct isds_DbOwnerInfo *old_box,
6316 const struct isds_DbOwnerInfo *new_box,
6317 const struct isds_approval *approval, char **refnumber) {
6318 isds_error err = IE_SUCCESS;
6319 #if HAVE_LIBCURL
6320 xmlNsPtr isds_ns = NULL;
6321 xmlNodePtr request = NULL;
6322 xmlNodePtr node;
6323 #endif
6326 if (!context) return IE_INVALID_CONTEXT;
6327 zfree(context->long_message);
6328 if (!old_box || !new_box) return IE_INVAL;
6331 #if HAVE_LIBCURL
6332 /* Build UpdateDataBoxDescr request */
6333 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxDescr");
6334 if (!request) {
6335 isds_log_message(context,
6336 _("Could build UpdateDataBoxDescr request"));
6337 return IE_ERROR;
6339 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6340 if(!isds_ns) {
6341 isds_log_message(context, _("Could not create ISDS name space"));
6342 xmlFreeNode(request);
6343 return IE_ERROR;
6345 xmlSetNs(request, isds_ns);
6347 INSERT_ELEMENT(node, request, "dbOldOwnerInfo");
6348 err = insert_DbOwnerInfo(context, old_box, node);
6349 if (err) goto leave;
6351 INSERT_ELEMENT(node, request, "dbNewOwnerInfo");
6352 err = insert_DbOwnerInfo(context, new_box, node);
6353 if (err) goto leave;
6355 err = insert_GExtApproval(context, approval, request);
6356 if (err) goto leave;
6359 /* Send it to server and process response */
6360 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6361 BAD_CAST "UpdateDataBoxDescr", &request, (xmlChar **) refnumber);
6363 leave:
6364 xmlFreeNode(request);
6365 #else /* not HAVE_LIBCURL */
6366 err = IE_NOTSUP;
6367 #endif
6369 return err;
6373 #if HAVE_LIBCURL
6374 /* Build ISDS request of XSD tIdDbInput type, sent it and check for error
6375 * code
6376 * @context is session context
6377 * @service is SOAP service
6378 * @service_name is name of request in @service
6379 * @box_id_element is name of element to wrap the @box_id. NULL means "dbID".
6380 * @box_id is box ID of interest
6381 * @approval is optional external approval of box manipulation
6382 * @response is server SOAP body response as XML document
6383 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6384 * NULL, if you don't care.
6385 * @return error coded from lower layer, context message will be set up
6386 * appropriately. */
6387 static isds_error build_send_dbid_request_check_response(
6388 struct isds_ctx *context, const isds_service service,
6389 const xmlChar *service_name, const xmlChar *box_id_element,
6390 const xmlChar *box_id, const struct isds_approval *approval,
6391 xmlDocPtr *response, xmlChar **refnumber) {
6393 isds_error err = IE_SUCCESS;
6394 char *service_name_locale = NULL, *box_id_locale = NULL;
6395 xmlNodePtr request = NULL, node;
6396 xmlNsPtr isds_ns = NULL;
6398 if (!context) return IE_INVALID_CONTEXT;
6399 if (!service_name || !box_id) return IE_INVAL;
6400 if (!response) return IE_INVAL;
6402 /* Free output argument */
6403 xmlFreeDoc(*response); *response = NULL;
6405 /* Prepare strings */
6406 service_name_locale = _isds_utf82locale((char*)service_name);
6407 if (!service_name_locale) {
6408 err = IE_NOMEM;
6409 goto leave;
6411 box_id_locale = _isds_utf82locale((char*)box_id);
6412 if (!box_id_locale) {
6413 err = IE_NOMEM;
6414 goto leave;
6417 /* Build request */
6418 request = xmlNewNode(NULL, service_name);
6419 if (!request) {
6420 isds_printf_message(context,
6421 _("Could not build %s request for %s box"), service_name_locale,
6422 box_id_locale);
6423 err = IE_ERROR;
6424 goto leave;
6426 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6427 if(!isds_ns) {
6428 isds_log_message(context, _("Could not create ISDS name space"));
6429 err = IE_ERROR;
6430 goto leave;
6432 xmlSetNs(request, isds_ns);
6434 /* Add XSD:tIdDbInput children */
6435 if (NULL == box_id_element) box_id_element = BAD_CAST "dbID";
6436 INSERT_STRING(request, box_id_element, box_id);
6437 err = insert_GExtApproval(context, approval, request);
6438 if (err) goto leave;
6440 /* Send request and check response*/
6441 err = send_destroy_request_check_response(context,
6442 service, service_name, &request, response, refnumber, NULL);
6444 leave:
6445 free(service_name_locale);
6446 free(box_id_locale);
6447 xmlFreeNode(request);
6448 return err;
6450 #endif /* HAVE_LIBCURL */
6453 /* Get data about all users assigned to given box.
6454 * @context is session context
6455 * @box_id is box ID
6456 * @users is automatically reallocated list of struct isds_DbUserInfo */
6457 isds_error isds_GetDataBoxUsers(struct isds_ctx *context, const char *box_id,
6458 struct isds_list **users) {
6459 isds_error err = IE_SUCCESS;
6460 #if HAVE_LIBCURL
6461 xmlDocPtr response = NULL;
6462 xmlXPathContextPtr xpath_ctx = NULL;
6463 xmlXPathObjectPtr result = NULL;
6464 int i;
6465 struct isds_list *item, *prev_item = NULL;
6466 #endif
6468 if (!context) return IE_INVALID_CONTEXT;
6469 zfree(context->long_message);
6470 if (!users || !box_id) return IE_INVAL;
6471 isds_list_free(users);
6474 #if HAVE_LIBCURL
6475 /* Do request and check for success */
6476 err = build_send_dbid_request_check_response(context,
6477 SERVICE_DB_MANIPULATION, BAD_CAST "GetDataBoxUsers", NULL,
6478 BAD_CAST box_id, NULL, &response, NULL);
6479 if (err) goto leave;
6482 /* Extract data */
6483 /* Prepare structure */
6484 xpath_ctx = xmlXPathNewContext(response);
6485 if (!xpath_ctx) {
6486 err = IE_ERROR;
6487 goto leave;
6489 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6490 err = IE_ERROR;
6491 goto leave;
6494 /* Set context node */
6495 result = xmlXPathEvalExpression(BAD_CAST
6496 "/isds:GetDataBoxUsersResponse/isds:dbUsers/isds:dbUserInfo",
6497 xpath_ctx);
6498 if (!result) {
6499 err = IE_ERROR;
6500 goto leave;
6502 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6503 /* Iterate over all users */
6504 for (i = 0; i < result->nodesetval->nodeNr; i++) {
6506 /* Prepare structure */
6507 item = calloc(1, sizeof(*item));
6508 if (!item) {
6509 err = IE_NOMEM;
6510 goto leave;
6512 item->destructor = (void(*)(void**))isds_DbUserInfo_free;
6513 if (i == 0) *users = item;
6514 else prev_item->next = item;
6515 prev_item = item;
6517 /* Extract it */
6518 xpath_ctx->node = result->nodesetval->nodeTab[i];
6519 err = extract_DbUserInfo(context,
6520 (struct isds_DbUserInfo **) (&item->data), xpath_ctx);
6521 if (err) goto leave;
6525 leave:
6526 if (err) {
6527 isds_list_free(users);
6530 xmlXPathFreeObject(result);
6531 xmlXPathFreeContext(xpath_ctx);
6532 xmlFreeDoc(response);
6534 if (!err)
6535 isds_log(ILF_ISDS, ILL_DEBUG,
6536 _("GetDataBoxUsers request processed by server "
6537 "successfully.\n"));
6538 #else /* not HAVE_LIBCURL */
6539 err = IE_NOTSUP;
6540 #endif
6542 return err;
6546 /* Update data about user assigned to given box.
6547 * @context is session context
6548 * @box is box identification
6549 * @old_user identifies user to update
6550 * @new_user are updated data about @old_user
6551 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6552 * NULL, if you don't care.*/
6553 isds_error isds_UpdateDataBoxUser(struct isds_ctx *context,
6554 const struct isds_DbOwnerInfo *box,
6555 const struct isds_DbUserInfo *old_user,
6556 const struct isds_DbUserInfo *new_user,
6557 char **refnumber) {
6558 isds_error err = IE_SUCCESS;
6559 #if HAVE_LIBCURL
6560 xmlNsPtr isds_ns = NULL;
6561 xmlNodePtr request = NULL;
6562 xmlNodePtr node;
6563 #endif
6566 if (!context) return IE_INVALID_CONTEXT;
6567 zfree(context->long_message);
6568 if (!box || !old_user || !new_user) return IE_INVAL;
6571 #if HAVE_LIBCURL
6572 /* Build UpdateDataBoxUser request */
6573 request = xmlNewNode(NULL, BAD_CAST "UpdateDataBoxUser");
6574 if (!request) {
6575 isds_log_message(context,
6576 _("Could build UpdateDataBoxUser request"));
6577 return IE_ERROR;
6579 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6580 if(!isds_ns) {
6581 isds_log_message(context, _("Could not create ISDS name space"));
6582 xmlFreeNode(request);
6583 return IE_ERROR;
6585 xmlSetNs(request, isds_ns);
6587 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6588 err = insert_DbOwnerInfo(context, box, node);
6589 if (err) goto leave;
6591 INSERT_ELEMENT(node, request, "dbOldUserInfo");
6592 err = insert_DbUserInfo(context, old_user, node);
6593 if (err) goto leave;
6595 INSERT_ELEMENT(node, request, "dbNewUserInfo");
6596 err = insert_DbUserInfo(context, new_user, node);
6597 if (err) goto leave;
6599 /* Send it to server and process response */
6600 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
6601 BAD_CAST "UpdateDataBoxUser", &request, (xmlChar **) refnumber);
6603 leave:
6604 xmlFreeNode(request);
6605 #else /* not HAVE_LIBCURL */
6606 err = IE_NOTSUP;
6607 #endif
6609 return err;
6613 /* Undocumented function.
6614 * @context is session context
6615 * @box_id is UTF-8 encoded box identifier
6616 * @token is UTF-8 encoded temporary password
6617 * @user_id outputs UTF-8 encoded reallocated user identifier
6618 * @password outpus UTF-8 encoded reallocated user password
6619 * Output arguments will be nulled in case of error */
6620 isds_error isds_activate(struct isds_ctx *context,
6621 const char *box_id, const char *token,
6622 char **user_id, char **password) {
6623 isds_error err = IE_SUCCESS;
6624 #if HAVE_LIBCURL
6625 xmlNsPtr isds_ns = NULL;
6626 xmlNodePtr request = NULL, node;
6627 xmlDocPtr response = NULL;
6628 xmlXPathContextPtr xpath_ctx = NULL;
6629 xmlXPathObjectPtr result = NULL;
6630 #endif
6633 if (!context) return IE_INVALID_CONTEXT;
6634 zfree(context->long_message);
6636 if (user_id) zfree(*user_id);
6637 if (password) zfree(*password);
6639 if (!box_id || !token || !user_id || !password) return IE_INVAL;
6642 #if HAVE_LIBCURL
6643 /* Build Activate request */
6644 request = xmlNewNode(NULL, BAD_CAST "Activate");
6645 if (!request) {
6646 isds_log_message(context, _("Could build Activate request"));
6647 return IE_ERROR;
6649 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6650 if(!isds_ns) {
6651 isds_log_message(context, _("Could not create ISDS name space"));
6652 xmlFreeNode(request);
6653 return IE_ERROR;
6655 xmlSetNs(request, isds_ns);
6657 INSERT_STRING(request, "dbAccessDataId", token);
6658 CHECK_FOR_STRING_LENGTH(box_id, 7, 7, "dbID");
6659 INSERT_STRING(request, "dbID", box_id);
6662 /* Send request and check response*/
6663 err = send_destroy_request_check_response(context,
6664 SERVICE_DB_MANIPULATION, BAD_CAST "Activate", &request,
6665 &response, NULL, NULL);
6666 if (err) goto leave;
6669 /* Extract data */
6670 xpath_ctx = xmlXPathNewContext(response);
6671 if (!xpath_ctx) {
6672 err = IE_ERROR;
6673 goto leave;
6675 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
6676 err = IE_ERROR;
6677 goto leave;
6679 result = xmlXPathEvalExpression(BAD_CAST "/isds:ActivateResponse",
6680 xpath_ctx);
6681 if (!result) {
6682 err = IE_ERROR;
6683 goto leave;
6685 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
6686 isds_log_message(context, _("Missing ActivateResponse element"));
6687 err = IE_ISDS;
6688 goto leave;
6690 if (result->nodesetval->nodeNr > 1) {
6691 isds_log_message(context, _("Multiple ActivateResponse element"));
6692 err = IE_ISDS;
6693 goto leave;
6695 xpath_ctx->node = result->nodesetval->nodeTab[0];
6696 xmlXPathFreeObject(result); result = NULL;
6698 EXTRACT_STRING("isds:userId", *user_id);
6699 if (!*user_id)
6700 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6701 "but did not return `userId' element.\n"));
6703 EXTRACT_STRING("isds:password", *password);
6704 if (!*password)
6705 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted Activate request, "
6706 "but did not return `password' element.\n"));
6708 leave:
6709 xmlXPathFreeObject(result);
6710 xmlXPathFreeContext(xpath_ctx);
6711 xmlFreeDoc(response);
6712 xmlFreeNode(request);
6714 if (!err)
6715 isds_log(ILF_ISDS, ILL_DEBUG,
6716 _("Activate request processed by server successfully.\n"));
6717 #else /* not HAVE_LIBCURL */
6718 err = IE_NOTSUP;
6719 #endif
6721 return err;
6725 /* Reset credentials of user assigned to given box.
6726 * @context is session context
6727 * @box is box identification
6728 * @user identifies user to reset password
6729 * @fee_paid is true if fee has been paid, false otherwise
6730 * @approval is optional external approval of box manipulation
6731 * @credentials_delivery is NULL if new password should be delivered off-line
6732 * to the user. It is valid pointer if user should obtain new password on-line
6733 * on dedicated web server. Then input @credentials_delivery.email value is
6734 * user's e-mail address user must provide to dedicated web server together
6735 * with @credentials_delivery.token. The output reallocated token user needs
6736 * to use to authorize on the web server to view his new password. Output
6737 * reallocated @credentials_delivery.new_user_name is user's log-in name that
6738 * ISDS changed up on this call. (No reason why server could change the name
6739 * is known now.)
6740 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6741 * NULL, if you don't care.*/
6742 isds_error isds_reset_password(struct isds_ctx *context,
6743 const struct isds_DbOwnerInfo *box,
6744 const struct isds_DbUserInfo *user,
6745 const _Bool fee_paid, const struct isds_approval *approval,
6746 struct isds_credentials_delivery *credentials_delivery,
6747 char **refnumber) {
6748 isds_error err = IE_SUCCESS;
6749 #if HAVE_LIBCURL
6750 xmlNsPtr isds_ns = NULL;
6751 xmlNodePtr request = NULL, node;
6752 xmlDocPtr response = NULL;
6753 #endif
6756 if (!context) return IE_INVALID_CONTEXT;
6757 zfree(context->long_message);
6759 if (credentials_delivery) {
6760 zfree(credentials_delivery->token);
6761 zfree(credentials_delivery->new_user_name);
6763 if (!box || !user) return IE_INVAL;
6766 #if HAVE_LIBCURL
6767 /* Build NewAccessData request */
6768 request = xmlNewNode(NULL, BAD_CAST "NewAccessData");
6769 if (!request) {
6770 isds_log_message(context,
6771 _("Could build NewAccessData request"));
6772 return IE_ERROR;
6774 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6775 if(!isds_ns) {
6776 isds_log_message(context, _("Could not create ISDS name space"));
6777 xmlFreeNode(request);
6778 return IE_ERROR;
6780 xmlSetNs(request, isds_ns);
6782 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6783 err = insert_DbOwnerInfo(context, box, node);
6784 if (err) goto leave;
6786 INSERT_ELEMENT(node, request, "dbUserInfo");
6787 err = insert_DbUserInfo(context, user, node);
6788 if (err) goto leave;
6790 INSERT_SCALAR_BOOLEAN(request, "dbFeePaid", fee_paid);
6792 err = insert_credentials_delivery(context, credentials_delivery, request);
6793 if (err) goto leave;
6795 err = insert_GExtApproval(context, approval, request);
6796 if (err) goto leave;
6798 /* Send request and check response*/
6799 err = send_destroy_request_check_response(context,
6800 SERVICE_DB_MANIPULATION, BAD_CAST "NewAccessData", &request,
6801 &response, (xmlChar **) refnumber, NULL);
6802 if (err) goto leave;
6805 /* Extract optional token */
6806 err = extract_credentials_delivery(context, credentials_delivery,
6807 response, "NewAccessData");
6809 leave:
6810 xmlFreeDoc(response);
6811 xmlFreeNode(request);
6813 if (!err)
6814 isds_log(ILF_ISDS, ILL_DEBUG,
6815 _("NewAccessData request processed by server "
6816 "successfully.\n"));
6817 #else /* not HAVE_LIBCURL */
6818 err = IE_NOTSUP;
6819 #endif
6821 return err;
6825 /* Build ISDS request of XSD tAddDBUserInput type, sent it, check for error
6826 * code, destroy response and log success.
6827 * @context is ISDS session context.
6828 * @service_name is name of SERVICE_DB_MANIPULATION service
6829 * @box is box identification
6830 * @user identifies user to remove
6831 * @credentials_delivery is NULL if new user's password should be delivered
6832 * off-line to the user. It is valid pointer if user should obtain new
6833 * password on-line on dedicated web server. Then input
6834 * @credentials_delivery.email value is user's e-mail address user must
6835 * provide to dedicated web server together with @credentials_delivery.token.
6836 * The output reallocated token user needs to use to authorize on the web
6837 * server to view his new password. Output reallocated
6838 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6839 * assingned or changed up on this call.
6840 * @approval is optional external approval of box manipulation
6841 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6842 * NULL, if you don't care. */
6843 static isds_error build_send_manipulationboxuser_request_check_drop_response(
6844 struct isds_ctx *context, const xmlChar *service_name,
6845 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6846 struct isds_credentials_delivery *credentials_delivery,
6847 const struct isds_approval *approval, xmlChar **refnumber) {
6848 isds_error err = IE_SUCCESS;
6849 #if HAVE_LIBCURL
6850 xmlNsPtr isds_ns = NULL;
6851 xmlNodePtr request = NULL, node;
6852 xmlDocPtr response = NULL;
6853 #endif
6856 if (!context) return IE_INVALID_CONTEXT;
6857 zfree(context->long_message);
6858 if (credentials_delivery) {
6859 zfree(credentials_delivery->token);
6860 zfree(credentials_delivery->new_user_name);
6862 if (!service_name || service_name[0] == '\0' || !box || !user)
6863 return IE_INVAL;
6866 #if HAVE_LIBCURL
6867 /* Build NewAccessData or similar request */
6868 request = xmlNewNode(NULL, service_name);
6869 if (!request) {
6870 char *service_name_locale = _isds_utf82locale((char *) service_name);
6871 isds_printf_message(context, _("Could not build %s request"),
6872 service_name_locale);
6873 free(service_name_locale);
6874 return IE_ERROR;
6876 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
6877 if(!isds_ns) {
6878 isds_log_message(context, _("Could not create ISDS name space"));
6879 xmlFreeNode(request);
6880 return IE_ERROR;
6882 xmlSetNs(request, isds_ns);
6884 INSERT_ELEMENT(node, request, "dbOwnerInfo");
6885 err = insert_DbOwnerInfo(context, box, node);
6886 if (err) goto leave;
6888 INSERT_ELEMENT(node, request, "dbUserInfo");
6889 err = insert_DbUserInfo(context, user, node);
6890 if (err) goto leave;
6892 err = insert_credentials_delivery(context, credentials_delivery, request);
6893 if (err) goto leave;
6895 err = insert_GExtApproval(context, approval, request);
6896 if (err) goto leave;
6899 /* Send request and check response*/
6900 err = send_destroy_request_check_response(context,
6901 SERVICE_DB_MANIPULATION, service_name, &request, &response,
6902 refnumber, NULL);
6904 xmlFreeNode(request);
6905 request = NULL;
6907 /* Pick up credentials_delivery if requested */
6908 err = extract_credentials_delivery(context, credentials_delivery, response,
6909 (char *)service_name);
6911 leave:
6912 xmlFreeDoc(response);
6913 if (request) xmlFreeNode(request);
6915 if (!err) {
6916 char *service_name_locale = _isds_utf82locale((char *) service_name);
6917 isds_log(ILF_ISDS, ILL_DEBUG,
6918 _("%s request processed by server successfully.\n"),
6919 service_name_locale);
6920 free(service_name_locale);
6922 #else /* not HAVE_LIBCURL */
6923 err = IE_NOTSUP;
6924 #endif
6926 return err;
6930 /* Assign new user to given box.
6931 * @context is session context
6932 * @box is box identification
6933 * @user defines new user to add
6934 * @credentials_delivery is NULL if new user's password should be delivered
6935 * off-line to the user. It is valid pointer if user should obtain new
6936 * password on-line on dedicated web server. Then input
6937 * @credentials_delivery.email value is user's e-mail address user must
6938 * provide to dedicated web server together with @credentials_delivery.token.
6939 * The output reallocated token user needs to use to authorize on the web
6940 * server to view his new password. Output reallocated
6941 * @credentials_delivery.new_user_name is user's log-in name that ISDS
6942 * assingned up on this call.
6943 * @approval is optional external approval of box manipulation
6944 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6945 * NULL, if you don't care.*/
6946 isds_error isds_add_user(struct isds_ctx *context,
6947 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6948 struct isds_credentials_delivery *credentials_delivery,
6949 const struct isds_approval *approval, char **refnumber) {
6950 return build_send_manipulationboxuser_request_check_drop_response(context,
6951 BAD_CAST "AddDataBoxUser", box, user, credentials_delivery,
6952 approval, (xmlChar **) refnumber);
6956 /* Remove user assigned to given box.
6957 * @context is session context
6958 * @box is box identification
6959 * @user identifies user to remove
6960 * @approval is optional external approval of box manipulation
6961 * @refnumber is reallocated serial number of request assigned by ISDS. Use
6962 * NULL, if you don't care.*/
6963 isds_error isds_delete_user(struct isds_ctx *context,
6964 const struct isds_DbOwnerInfo *box, const struct isds_DbUserInfo *user,
6965 const struct isds_approval *approval, char **refnumber) {
6966 return build_send_manipulationboxuser_request_check_drop_response(context,
6967 BAD_CAST "DeleteDataBoxUser", box, user, NULL, approval,
6968 (xmlChar **) refnumber);
6972 /* Get list of boxes in ZIP archive.
6973 * @context is session context
6974 * @list_identifier is UTF-8 encoded string identifying boxes of interrest.
6975 * System recognizes following values currently: ALL (all boxes), UPG
6976 * (effectively OVM boxes), OVM (OVM gross type boxes), OPN (boxes allowing
6977 * receiving commercial messages). This argument is a string because
6978 * specification states new values can appear in the future. Not all list
6979 * types are available to all users.
6980 * @buffer is automatically reallocated memory to store the list of boxes. The
6981 * list is zipped CSV file.
6982 * @buffer_length is size of @buffer data in bytes.
6983 * In case of error @buffer will be freed and @buffer_length will be
6984 * undefined.*/
6985 isds_error isds_get_box_list_archive(struct isds_ctx *context,
6986 const char *list_identifier, void **buffer, size_t *buffer_length) {
6987 isds_error err = IE_SUCCESS;
6988 #if HAVE_LIBCURL
6989 xmlNsPtr isds_ns = NULL;
6990 xmlNodePtr request = NULL, node;
6991 xmlDocPtr response = NULL;
6992 xmlXPathContextPtr xpath_ctx = NULL;
6993 xmlXPathObjectPtr result = NULL;
6994 char *string = NULL;
6995 #endif
6998 if (!context) return IE_INVALID_CONTEXT;
6999 zfree(context->long_message);
7000 if (buffer) zfree(*buffer);
7001 if (!buffer || !buffer_length) return IE_INVAL;
7004 #if HAVE_LIBCURL
7005 /* Check if connection is established
7006 * TODO: This check should be done downstairs. */
7007 if (!context->curl) return IE_CONNECTION_CLOSED;
7010 /* Build AuthenticateMessage request */
7011 request = xmlNewNode(NULL, BAD_CAST "GetDataBoxList");
7012 if (!request) {
7013 isds_log_message(context,
7014 _("Could not build GetDataBoxList request"));
7015 return IE_ERROR;
7017 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7018 if(!isds_ns) {
7019 isds_log_message(context, _("Could not create ISDS name space"));
7020 xmlFreeNode(request);
7021 return IE_ERROR;
7023 xmlSetNs(request, isds_ns);
7024 INSERT_STRING(request, "dblType", list_identifier);
7026 /* Send request to server and process response */
7027 err = send_destroy_request_check_response(context,
7028 SERVICE_DB_SEARCH, BAD_CAST "GetDataBoxList", &request,
7029 &response, NULL, NULL);
7030 if (err) goto leave;
7033 /* Extract Base-64 encoded ZIP file */
7034 xpath_ctx = xmlXPathNewContext(response);
7035 if (!xpath_ctx) {
7036 err = IE_ERROR;
7037 goto leave;
7039 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7040 err = IE_ERROR;
7041 goto leave;
7043 EXTRACT_STRING("/isds:GetDataBoxListResponse/isds:dblData", string);
7045 /* Decode non-empty archive */
7046 if (string && string[0] != '\0') {
7047 *buffer_length = _isds_b64decode(string, buffer);
7048 if (*buffer_length == (size_t) -1) {
7049 isds_printf_message(context,
7050 _("Error while Base64-decoding box list archive"));
7051 err = IE_ERROR;
7052 goto leave;
7057 leave:
7058 free(string);
7059 xmlXPathFreeObject(result);
7060 xmlXPathFreeContext(xpath_ctx);
7061 xmlFreeDoc(response);
7062 xmlFreeNode(request);
7064 if (!err) {
7065 isds_log(ILF_ISDS, ILL_DEBUG, _("GetDataBoxList request "
7066 "processed by server successfully.\n"));
7068 #else /* not HAVE_LIBCURL */
7069 err = IE_NOTSUP;
7070 #endif
7072 return err;
7076 /* Find boxes suiting given criteria.
7077 * @criteria is filter. You should fill in at least some members.
7078 * @boxes is automatically reallocated list of isds_DbOwnerInfo structures,
7079 * possibly empty. Input NULL or valid old structure.
7080 * @return:
7081 * IE_SUCCESS if search succeeded, @boxes contains useful data
7082 * IE_NOEXIST if no such box exists, @boxes will be NULL
7083 * IE_2BIG if too much boxes exist and server truncated the results, @boxes
7084 * contains still valid data
7085 * other code if something bad happens. @boxes will be NULL. */
7086 isds_error isds_FindDataBox(struct isds_ctx *context,
7087 const struct isds_DbOwnerInfo *criteria,
7088 struct isds_list **boxes) {
7089 isds_error err = IE_SUCCESS;
7090 #if HAVE_LIBCURL
7091 _Bool truncated = 0;
7092 xmlNsPtr isds_ns = NULL;
7093 xmlNodePtr request = NULL;
7094 xmlDocPtr response = NULL;
7095 xmlChar *code = NULL, *message = NULL;
7096 xmlNodePtr db_owner_info;
7097 xmlXPathContextPtr xpath_ctx = NULL;
7098 xmlXPathObjectPtr result = NULL;
7099 xmlChar *string = NULL;
7100 #endif
7103 if (!context) return IE_INVALID_CONTEXT;
7104 zfree(context->long_message);
7105 if (!boxes) return IE_INVAL;
7106 isds_list_free(boxes);
7108 if (!criteria) {
7109 return IE_INVAL;
7112 #if HAVE_LIBCURL
7113 /* Check if connection is established
7114 * TODO: This check should be done downstairs. */
7115 if (!context->curl) return IE_CONNECTION_CLOSED;
7118 /* Build FindDataBox request */
7119 request = xmlNewNode(NULL, BAD_CAST "FindDataBox");
7120 if (!request) {
7121 isds_log_message(context,
7122 _("Could build FindDataBox request"));
7123 return IE_ERROR;
7125 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7126 if(!isds_ns) {
7127 isds_log_message(context, _("Could not create ISDS name space"));
7128 xmlFreeNode(request);
7129 return IE_ERROR;
7131 xmlSetNs(request, isds_ns);
7132 db_owner_info = xmlNewChild(request, NULL, BAD_CAST "dbOwnerInfo", NULL);
7133 if (!db_owner_info) {
7134 isds_log_message(context, _("Could not add dbOwnerInfo child to "
7135 "FindDataBox element"));
7136 xmlFreeNode(request);
7137 return IE_ERROR;
7140 err = insert_DbOwnerInfo(context, criteria, db_owner_info);
7141 if (err) goto leave;
7144 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending FindDataBox request to ISDS\n"));
7146 /* Sent request */
7147 err = _isds(context, SERVICE_DB_SEARCH, request, &response, NULL, NULL);
7149 /* Destroy request */
7150 xmlFreeNode(request); request = NULL;
7152 if (err) {
7153 isds_log(ILF_ISDS, ILL_DEBUG,
7154 _("Processing ISDS response on FindDataBox "
7155 "request failed\n"));
7156 goto leave;
7159 /* Check for response status */
7160 err = isds_response_status(context, SERVICE_DB_SEARCH, response,
7161 &code, &message, NULL);
7162 if (err) {
7163 isds_log(ILF_ISDS, ILL_DEBUG,
7164 _("ISDS response on FindDataBox request is missing status\n"));
7165 goto leave;
7168 /* Request processed, but nothing found */
7169 if (!xmlStrcmp(code, BAD_CAST "0002") ||
7170 !xmlStrcmp(code, BAD_CAST "5001")) {
7171 char *code_locale = _isds_utf82locale((char*)code);
7172 char *message_locale = _isds_utf82locale((char*)message);
7173 isds_log(ILF_ISDS, ILL_DEBUG,
7174 _("Server did not found any box on FindDataBox request "
7175 "(code=%s, message=%s)\n"), code_locale, message_locale);
7176 isds_log_message(context, message_locale);
7177 free(code_locale);
7178 free(message_locale);
7179 err = IE_NOEXIST;
7180 goto leave;
7183 /* Warning, not a error */
7184 if (!xmlStrcmp(code, BAD_CAST "0003")) {
7185 char *code_locale = _isds_utf82locale((char*)code);
7186 char *message_locale = _isds_utf82locale((char*)message);
7187 isds_log(ILF_ISDS, ILL_DEBUG,
7188 _("Server truncated response on FindDataBox request "
7189 "(code=%s, message=%s)\n"), code_locale, message_locale);
7190 isds_log_message(context, message_locale);
7191 free(code_locale);
7192 free(message_locale);
7193 truncated = 1;
7196 /* Other error */
7197 else if (xmlStrcmp(code, BAD_CAST "0000")) {
7198 char *code_locale = _isds_utf82locale((char*)code);
7199 char *message_locale = _isds_utf82locale((char*)message);
7200 isds_log(ILF_ISDS, ILL_DEBUG,
7201 _("Server refused FindDataBox request "
7202 "(code=%s, message=%s)\n"), code_locale, message_locale);
7203 isds_log_message(context, message_locale);
7204 free(code_locale);
7205 free(message_locale);
7206 err = IE_ISDS;
7207 goto leave;
7210 xpath_ctx = xmlXPathNewContext(response);
7211 if (!xpath_ctx) {
7212 err = IE_ERROR;
7213 goto leave;
7215 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7216 err = IE_ERROR;
7217 goto leave;
7220 /* Extract boxes if they present */
7221 result = xmlXPathEvalExpression(BAD_CAST
7222 "/isds:FindDataBoxResponse/isds:dbResults/isds:dbOwnerInfo",
7223 xpath_ctx);
7224 if (!result) {
7225 err = IE_ERROR;
7226 goto leave;
7228 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7229 struct isds_list *item, *prev_item = NULL;
7230 for (int i = 0; i < result->nodesetval->nodeNr; i++) {
7231 item = calloc(1, sizeof(*item));
7232 if (!item) {
7233 err = IE_NOMEM;
7234 goto leave;
7237 item->destructor = (void (*)(void **))isds_DbOwnerInfo_free;
7238 if (i == 0) *boxes = item;
7239 else prev_item->next = item;
7240 prev_item = item;
7242 xpath_ctx->node = result->nodesetval->nodeTab[i];
7243 err = extract_DbOwnerInfo(context,
7244 (struct isds_DbOwnerInfo **) &(item->data), xpath_ctx);
7245 if (err) goto leave;
7249 leave:
7250 if (err) {
7251 isds_list_free(boxes);
7252 } else {
7253 if (truncated) err = IE_2BIG;
7256 free(string);
7257 xmlFreeNode(request);
7258 xmlXPathFreeObject(result);
7259 xmlXPathFreeContext(xpath_ctx);
7261 free(code);
7262 free(message);
7263 xmlFreeDoc(response);
7265 if (!err)
7266 isds_log(ILF_ISDS, ILL_DEBUG,
7267 _("FindDataBox request processed by server successfully.\n"));
7268 #else /* not HAVE_LIBCURL */
7269 err = IE_NOTSUP;
7270 #endif
7272 return err;
7276 /* Get status of a box.
7277 * @context is ISDS session context.
7278 * @box_id is UTF-8 encoded box identifier as zero terminated string
7279 * @box_status is return value of box status.
7280 * @return:
7281 * IE_SUCCESS if box has been found and its status retrieved
7282 * IE_NOEXIST if box is not known to ISDS server
7283 * or other appropriate error.
7284 * You can use isds_DbState to enumerate box status. However out of enum
7285 * range value can be returned too. This is feature because ISDS
7286 * specification leaves the set of values open.
7287 * Be ware that status DBSTATE_REMOVED is signaled as IE_SUCCESS. That means
7288 * the box has been deleted, but ISDS still lists its former existence. */
7289 isds_error isds_CheckDataBox(struct isds_ctx *context, const char *box_id,
7290 long int *box_status) {
7291 isds_error err = IE_SUCCESS;
7292 #if HAVE_LIBCURL
7293 xmlNsPtr isds_ns = NULL;
7294 xmlNodePtr request = NULL, db_id;
7295 xmlDocPtr response = NULL;
7296 xmlXPathContextPtr xpath_ctx = NULL;
7297 xmlXPathObjectPtr result = NULL;
7298 xmlChar *string = NULL;
7300 const xmlChar *codes[] = {
7301 BAD_CAST "5001",
7302 BAD_CAST "1007",
7303 BAD_CAST "2011",
7304 NULL
7306 const char *meanings[] = {
7307 "The box does not exist",
7308 "Box ID is malformed",
7309 "Box ID malformed",
7311 const isds_error errors[] = {
7312 IE_NOEXIST,
7313 IE_INVAL,
7314 IE_INVAL,
7316 struct code_map_isds_error map = {
7317 .codes = codes,
7318 .meanings = meanings,
7319 .errors = errors
7321 #endif
7323 if (!context) return IE_INVALID_CONTEXT;
7324 zfree(context->long_message);
7325 if (!box_status || !box_id || *box_id == '\0') return IE_INVAL;
7327 #if HAVE_LIBCURL
7328 /* Check if connection is established
7329 * TODO: This check should be done downstairs. */
7330 if (!context->curl) return IE_CONNECTION_CLOSED;
7333 /* Build CheckDataBox request */
7334 request = xmlNewNode(NULL, BAD_CAST "CheckDataBox");
7335 if (!request) {
7336 isds_log_message(context,
7337 _("Could build CheckDataBox request"));
7338 return IE_ERROR;
7340 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7341 if(!isds_ns) {
7342 isds_log_message(context, _("Could not create ISDS name space"));
7343 xmlFreeNode(request);
7344 return IE_ERROR;
7346 xmlSetNs(request, isds_ns);
7347 db_id = xmlNewTextChild(request, NULL, BAD_CAST "dbID", (xmlChar *) box_id);
7348 if (!db_id) {
7349 isds_log_message(context, _("Could not add dbID child to "
7350 "CheckDataBox element"));
7351 xmlFreeNode(request);
7352 return IE_ERROR;
7356 /* Send request and check response*/
7357 err = send_destroy_request_check_response(context,
7358 SERVICE_DB_SEARCH, BAD_CAST "CheckDataBox",
7359 &request, &response, NULL, &map);
7360 if (err) goto leave;
7363 /* Extract data */
7364 xpath_ctx = xmlXPathNewContext(response);
7365 if (!xpath_ctx) {
7366 err = IE_ERROR;
7367 goto leave;
7369 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7370 err = IE_ERROR;
7371 goto leave;
7373 result = xmlXPathEvalExpression(BAD_CAST "/isds:CheckDataBoxResponse",
7374 xpath_ctx);
7375 if (!result) {
7376 err = IE_ERROR;
7377 goto leave;
7379 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7380 isds_log_message(context, _("Missing CheckDataBoxResponse element"));
7381 err = IE_ISDS;
7382 goto leave;
7384 if (result->nodesetval->nodeNr > 1) {
7385 isds_log_message(context, _("Multiple CheckDataBoxResponse element"));
7386 err = IE_ISDS;
7387 goto leave;
7389 xpath_ctx->node = result->nodesetval->nodeTab[0];
7390 xmlXPathFreeObject(result); result = NULL;
7392 EXTRACT_LONGINT("isds:dbState", box_status, 1);
7395 leave:
7396 free(string);
7397 xmlXPathFreeObject(result);
7398 xmlXPathFreeContext(xpath_ctx);
7400 xmlFreeDoc(response);
7402 if (!err)
7403 isds_log(ILF_ISDS, ILL_DEBUG,
7404 _("CheckDataBox request processed by server successfully.\n"));
7405 #else /* not HAVE_LIBCURL */
7406 err = IE_NOTSUP;
7407 #endif
7409 return err;
7413 /* Get list of permissions to send commercial messages.
7414 * @context is ISDS session context.
7415 * @box_id is UTF-8 encoded sender box identifier as zero terminated string
7416 * @permissions is a reallocated list of permissions (struct
7417 * isds_commercial_permission*) to send commercial messages from @box_id. The
7418 * order of permissions is significant as the server applies the permissions
7419 * and associated pre-paid credits in the order. Empty list means no
7420 * permission.
7421 * @return:
7422 * IE_SUCCESS if the list has been obtained correctly,
7423 * or other appropriate error. */
7424 isds_error isds_get_commercial_permissions(struct isds_ctx *context,
7425 const char *box_id, struct isds_list **permissions) {
7426 isds_error err = IE_SUCCESS;
7427 #if HAVE_LIBCURL
7428 xmlDocPtr response = NULL;
7429 xmlXPathContextPtr xpath_ctx = NULL;
7430 xmlXPathObjectPtr result = NULL;
7431 #endif
7433 if (!context) return IE_INVALID_CONTEXT;
7434 zfree(context->long_message);
7435 if (NULL == permissions) return IE_INVAL;
7436 isds_list_free(permissions);
7437 if (NULL == box_id) return IE_INVAL;
7439 #if HAVE_LIBCURL
7440 /* Check if connection is established */
7441 if (!context->curl) return IE_CONNECTION_CLOSED;
7443 /* Do request and check for success */
7444 err = build_send_dbid_request_check_response(context,
7445 SERVICE_DB_SEARCH, BAD_CAST "PDZInfo", BAD_CAST "PDZSender",
7446 BAD_CAST box_id, NULL, &response, NULL);
7447 if (!err) {
7448 isds_log(ILF_ISDS, ILL_DEBUG,
7449 _("PDZInfo request processed by server successfully.\n"));
7452 /* Extract data */
7453 /* Prepare structure */
7454 xpath_ctx = xmlXPathNewContext(response);
7455 if (!xpath_ctx) {
7456 err = IE_ERROR;
7457 goto leave;
7459 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7460 err = IE_ERROR;
7461 goto leave;
7464 /* Set context node */
7465 result = xmlXPathEvalExpression(BAD_CAST
7466 "/isds:PDZInfoResponse/isds:dbPDZRecords/isds:dbPDZRecord",
7467 xpath_ctx);
7468 if (!result) {
7469 err = IE_ERROR;
7470 goto leave;
7472 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7473 struct isds_list *prev_item = NULL;
7475 /* Iterate over all permission records */
7476 for (long unsigned int i = 0; i < result->nodesetval->nodeNr; i++) {
7477 struct isds_list *item;
7479 /* Prepare structure */
7480 item = calloc(1, sizeof(*item));
7481 if (!item) {
7482 err = IE_NOMEM;
7483 goto leave;
7485 item->destructor = (void(*)(void**))isds_commercial_permission_free;
7486 if (i == 0) *permissions = item;
7487 else prev_item->next = item;
7488 prev_item = item;
7490 /* Extract it */
7491 xpath_ctx->node = result->nodesetval->nodeTab[i];
7492 err = extract_DbPDZRecord(context,
7493 (struct isds_commercial_permission **) (&item->data),
7494 xpath_ctx);
7495 if (err) goto leave;
7499 leave:
7500 if (err) {
7501 isds_list_free(permissions);
7504 xmlXPathFreeObject(result);
7505 xmlXPathFreeContext(xpath_ctx);
7506 xmlFreeDoc(response);
7508 #else /* not HAVE_LIBCURL */
7509 err = IE_NOTSUP;
7510 #endif
7512 return err;
7516 /* Get details about credit for sending pre-paid commercial messages.
7517 * @context is ISDS session context.
7518 * @box_id is UTF-8 encoded sender box identifier as zero terminated string.
7519 * @from_date is first day of credit history to return in @history. Only
7520 * tm_year, tm_mon and tm_mday carry sane value.
7521 * @to_date is last day of credit history to return in @history. Only
7522 * tm_year, tm_mon and tm_mday carry sane value.
7523 * @credit outputs current credit value into pre-allocated memory. Pass NULL
7524 * if you don't care. This and all other credit values are integers in
7525 * hundredths of Czech Crowns.
7526 * @email outputs notification e-mail address where notifications about credit
7527 * are sent. This is automatically reallocated string. Pass NULL if you don't
7528 * care. It can return NULL if no address is defined.
7529 * @history outputs auto-reallocated list of pointers to struct
7530 * isds_credit_event. Events in closed interval @from_time to @to_time are
7531 * returned. Pass NULL @to_time and @from_time if you don't care. The events
7532 * are sorted by time.
7533 * @return:
7534 * IE_SUCCESS if the credit details have been obtained correctly,
7535 * or other appropriate error. Please note that server allows to retrieve
7536 * only limited history of events. */
7537 isds_error isds_get_commercial_credit(struct isds_ctx *context,
7538 const char *box_id,
7539 const struct tm *from_date, const struct tm *to_date,
7540 long int *credit, char **email, struct isds_list **history) {
7541 isds_error err = IE_SUCCESS;
7542 #if HAVE_LIBCURL
7543 char *box_id_locale = NULL;
7544 xmlNodePtr request = NULL, node;
7545 xmlNsPtr isds_ns = NULL;
7546 xmlChar *string = NULL;
7548 xmlDocPtr response = NULL;
7549 xmlXPathContextPtr xpath_ctx = NULL;
7550 xmlXPathObjectPtr result = NULL;
7552 const xmlChar *codes[] = {
7553 BAD_CAST "1004",
7554 BAD_CAST "2011",
7555 BAD_CAST "1093",
7556 BAD_CAST "1137",
7557 BAD_CAST "1058",
7558 NULL
7560 const char *meanings[] = {
7561 "Insufficient priviledges for the box",
7562 "The box does not exist",
7563 "Date is too long (history is not available after 15 months)",
7564 "Interval is too long (limit is 3 months)",
7565 "Invalid date"
7567 const isds_error errors[] = {
7568 IE_ISDS,
7569 IE_NOEXIST,
7570 IE_DATE,
7571 IE_DATE,
7572 IE_DATE,
7574 struct code_map_isds_error map = {
7575 .codes = codes,
7576 .meanings = meanings,
7577 .errors = errors
7579 #endif
7581 if (!context) return IE_INVALID_CONTEXT;
7582 zfree(context->long_message);
7584 /* Free output argument */
7585 if (NULL != credit) *credit = 0;
7586 if (NULL != email) zfree(*email);
7587 isds_list_free(history);
7589 if (NULL == box_id) return IE_INVAL;
7591 #if HAVE_LIBCURL
7592 /* Check if connection is established */
7593 if (NULL == context->curl) return IE_CONNECTION_CLOSED;
7595 box_id_locale = _isds_utf82locale((char*)box_id);
7596 if (NULL == box_id_locale) {
7597 err = IE_NOMEM;
7598 goto leave;
7601 /* Build request */
7602 request = xmlNewNode(NULL, BAD_CAST "DataBoxCreditInfo");
7603 if (NULL == request) {
7604 isds_printf_message(context,
7605 _("Could not build DataBoxCreditInfo request for %s box"),
7606 box_id_locale);
7607 err = IE_ERROR;
7608 goto leave;
7610 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7611 if(!isds_ns) {
7612 isds_log_message(context, _("Could not create ISDS name space"));
7613 err = IE_ERROR;
7614 goto leave;
7616 xmlSetNs(request, isds_ns);
7618 /* Add mandatory XSD:tIdDbInput child */
7619 INSERT_STRING(request, BAD_CAST "dbID", box_id);
7620 /* Add mandatory dates elements with optional values */
7621 if (from_date) {
7622 err = tm2datestring(from_date, &string);
7623 if (err) {
7624 isds_log_message(context,
7625 _("Could not convert `from_date' argument to ISO date "
7626 "string"));
7627 goto leave;
7629 INSERT_STRING(request, "ciFromDate", string);
7630 zfree(string);
7631 } else {
7632 INSERT_STRING(request, "ciFromDate", NULL);
7634 if (to_date) {
7635 err = tm2datestring(to_date, &string);
7636 if (err) {
7637 isds_log_message(context,
7638 _("Could not convert `to_date' argument to ISO date "
7639 "string"));
7640 goto leave;
7642 INSERT_STRING(request, "ciTodate", string);
7643 zfree(string);
7644 } else {
7645 INSERT_STRING(request, "ciTodate", NULL);
7648 /* Send request and check response*/
7649 err = send_destroy_request_check_response(context,
7650 SERVICE_DB_SEARCH, BAD_CAST "DataBoxCreditInfo",
7651 &request, &response, NULL, &map);
7652 if (err) goto leave;
7655 /* Extract data */
7656 /* Set context to the root */
7657 xpath_ctx = xmlXPathNewContext(response);
7658 if (!xpath_ctx) {
7659 err = IE_ERROR;
7660 goto leave;
7662 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
7663 err = IE_ERROR;
7664 goto leave;
7666 result = xmlXPathEvalExpression(BAD_CAST "/isds:DataBoxCreditInfoResponse",
7667 xpath_ctx);
7668 if (!result) {
7669 err = IE_ERROR;
7670 goto leave;
7672 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7673 isds_log_message(context, _("Missing DataBoxCreditInfoResponse element"));
7674 err = IE_ISDS;
7675 goto leave;
7677 if (result->nodesetval->nodeNr > 1) {
7678 isds_log_message(context, _("Multiple DataBoxCreditInfoResponse element"));
7679 err = IE_ISDS;
7680 goto leave;
7682 xpath_ctx->node = result->nodesetval->nodeTab[0];
7683 xmlXPathFreeObject(result); result = NULL;
7685 /* Extract common data */
7686 if (NULL != credit) EXTRACT_LONGINT("isds:currentCredit", credit, 1);
7687 if (NULL != email) EXTRACT_STRING("isds:notifEmail", *email);
7689 /* Extract records */
7690 if (NULL == history) goto leave;
7691 result = xmlXPathEvalExpression(BAD_CAST "isds:ciRecords/isds:ciRecord",
7692 xpath_ctx);
7693 if (!result) {
7694 err = IE_ERROR;
7695 goto leave;
7697 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
7698 struct isds_list *prev_item = NULL;
7700 /* Iterate over all records */
7701 for (long unsigned int i = 0; i < result->nodesetval->nodeNr; i++) {
7702 struct isds_list *item;
7704 /* Prepare structure */
7705 item = calloc(1, sizeof(*item));
7706 if (!item) {
7707 err = IE_NOMEM;
7708 goto leave;
7710 item->destructor = (void(*)(void**))isds_credit_event_free;
7711 if (i == 0) *history = item;
7712 else prev_item->next = item;
7713 prev_item = item;
7715 /* Extract it */
7716 xpath_ctx->node = result->nodesetval->nodeTab[i];
7717 err = extract_CiRecord(context,
7718 (struct isds_credit_event **) (&item->data),
7719 xpath_ctx);
7720 if (err) goto leave;
7724 leave:
7725 if (!err) {
7726 isds_log(ILF_ISDS, ILL_DEBUG,
7727 _("DataBoxCreditInfo request processed by server successfully.\n"));
7729 if (err) {
7730 isds_list_free(history);
7731 if (NULL != email) zfree(*email)
7734 free(box_id_locale);
7735 xmlXPathFreeObject(result);
7736 xmlXPathFreeContext(xpath_ctx);
7737 xmlFreeDoc(response);
7739 #else /* not HAVE_LIBCURL */
7740 err = IE_NOTSUP;
7741 #endif
7743 return err;
7747 /* Build ISDS request of XSD tIdDbInput type, sent it, check for error
7748 * code, destroy response and log success.
7749 * @context is ISDS session context.
7750 * @service_name is name of SERVICE_DB_MANIPULATION service
7751 * @box_id is UTF-8 encoded box identifier as zero terminated string
7752 * @approval is optional external approval of box manipulation
7753 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7754 * NULL, if you don't care. */
7755 static isds_error build_send_manipulationdbid_request_check_drop_response(
7756 struct isds_ctx *context, const xmlChar *service_name,
7757 const xmlChar *box_id, const struct isds_approval *approval,
7758 xmlChar **refnumber) {
7759 isds_error err = IE_SUCCESS;
7760 #if HAVE_LIBCURL
7761 xmlDocPtr response = NULL;
7762 #endif
7764 if (!context) return IE_INVALID_CONTEXT;
7765 zfree(context->long_message);
7766 if (!service_name || *service_name == '\0' || !box_id) return IE_INVAL;
7768 #if HAVE_LIBCURL
7769 /* Check if connection is established */
7770 if (!context->curl) return IE_CONNECTION_CLOSED;
7772 /* Do request and check for success */
7773 err = build_send_dbid_request_check_response(context,
7774 SERVICE_DB_MANIPULATION, service_name, NULL, box_id, approval,
7775 &response, refnumber);
7776 xmlFreeDoc(response);
7778 if (!err) {
7779 char *service_name_locale = _isds_utf82locale((char *) service_name);
7780 isds_log(ILF_ISDS, ILL_DEBUG,
7781 _("%s request processed by server successfully.\n"),
7782 service_name_locale);
7783 free(service_name_locale);
7785 #else /* not HAVE_LIBCURL */
7786 err = IE_NOTSUP;
7787 #endif
7789 return err;
7793 /* Switch box into state where box can receive commercial messages (off by
7794 * default)
7795 * @context is ISDS session context.
7796 * @box_id is UTF-8 encoded box identifier as zero terminated string
7797 * @allow is true for enable, false for disable commercial messages income
7798 * @approval is optional external approval of box manipulation
7799 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7800 * NULL, if you don't care. */
7801 isds_error isds_switch_commercial_receiving(struct isds_ctx *context,
7802 const char *box_id, const _Bool allow,
7803 const struct isds_approval *approval, char **refnumber) {
7804 return build_send_manipulationdbid_request_check_drop_response(context,
7805 (allow) ? BAD_CAST "SetOpenAddressing" :
7806 BAD_CAST "ClearOpenAddressing",
7807 BAD_CAST box_id, approval, (xmlChar **) refnumber);
7811 /* Switch box into / out of state where non-OVM box can act as OVM (e.g. force
7812 * message acceptance). This is just a box permission. Sender must apply
7813 * such role by sending each message.
7814 * @context is ISDS session context.
7815 * @box_id is UTF-8 encoded box identifier as zero terminated string
7816 * @allow is true for enable, false for disable OVM role permission
7817 * @approval is optional external approval of box manipulation
7818 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7819 * NULL, if you don't care. */
7820 isds_error isds_switch_effective_ovm(struct isds_ctx *context,
7821 const char *box_id, const _Bool allow,
7822 const struct isds_approval *approval, char **refnumber) {
7823 return build_send_manipulationdbid_request_check_drop_response(context,
7824 (allow) ? BAD_CAST "SetEffectiveOVM" :
7825 BAD_CAST "ClearEffectiveOVM",
7826 BAD_CAST box_id, approval, (xmlChar **) refnumber);
7830 /* Build ISDS request of XSD tOwnerInfoInput type, sent it, check for error
7831 * code, destroy response and log success.
7832 * @context is ISDS session context.
7833 * @service_name is name of SERVICE_DB_MANIPULATION service
7834 * @owner is structure describing box
7835 * @approval is optional external approval of box manipulation
7836 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7837 * NULL, if you don't care. */
7838 static isds_error build_send_manipulationdbowner_request_check_drop_response(
7839 struct isds_ctx *context, const xmlChar *service_name,
7840 const struct isds_DbOwnerInfo *owner,
7841 const struct isds_approval *approval, xmlChar **refnumber) {
7842 isds_error err = IE_SUCCESS;
7843 #if HAVE_LIBCURL
7844 char *service_name_locale = NULL;
7845 xmlNodePtr request = NULL, db_owner_info;
7846 xmlNsPtr isds_ns = NULL;
7847 #endif
7850 if (!context) return IE_INVALID_CONTEXT;
7851 zfree(context->long_message);
7852 if (!service_name || *service_name == '\0' || !owner) return IE_INVAL;
7854 #if HAVE_LIBCURL
7855 service_name_locale = _isds_utf82locale((char*)service_name);
7856 if (!service_name_locale) {
7857 err = IE_NOMEM;
7858 goto leave;
7861 /* Build request */
7862 request = xmlNewNode(NULL, service_name);
7863 if (!request) {
7864 isds_printf_message(context,
7865 _("Could not build %s request"), service_name_locale);
7866 err = IE_ERROR;
7867 goto leave;
7869 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7870 if(!isds_ns) {
7871 isds_log_message(context, _("Could not create ISDS name space"));
7872 err = IE_ERROR;
7873 goto leave;
7875 xmlSetNs(request, isds_ns);
7878 /* Add XSD:tOwnerInfoInput child*/
7879 INSERT_ELEMENT(db_owner_info, request, "dbOwnerInfo");
7880 err = insert_DbOwnerInfo(context, owner, db_owner_info);
7881 if (err) goto leave;
7883 /* Add XSD:gExtApproval*/
7884 err = insert_GExtApproval(context, approval, request);
7885 if (err) goto leave;
7887 /* Send it to server and process response */
7888 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
7889 service_name, &request, refnumber);
7891 leave:
7892 xmlFreeNode(request);
7893 free(service_name_locale);
7894 #else /* not HAVE_LIBCURL */
7895 err = IE_NOTSUP;
7896 #endif
7898 return err;
7902 /* Switch box accessibility state on request of box owner.
7903 * Despite the name, owner must do the request off-line. This function is
7904 * designed for such off-line meeting points (e.g. Czech POINT).
7905 * @context is ISDS session context.
7906 * @box identifies box to switch accessibility state.
7907 * @allow is true for making accessible, false to disallow access.
7908 * @approval is optional external approval of box manipulation
7909 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7910 * NULL, if you don't care. */
7911 isds_error isds_switch_box_accessibility_on_owner_request(
7912 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
7913 const _Bool allow, const struct isds_approval *approval,
7914 char **refnumber) {
7915 return build_send_manipulationdbowner_request_check_drop_response(context,
7916 (allow) ? BAD_CAST "EnableOwnDataBox" :
7917 BAD_CAST "DisableOwnDataBox",
7918 box, approval, (xmlChar **) refnumber);
7922 /* Disable box accessibility on law enforcement (e.g. by prison) since exact
7923 * date.
7924 * @context is ISDS session context.
7925 * @box identifies box to switch accessibility state.
7926 * @since is date since accessibility has been denied. This can be past too.
7927 * Only tm_year, tm_mon and tm_mday carry sane value.
7928 * @approval is optional external approval of box manipulation
7929 * @refnumber is reallocated serial number of request assigned by ISDS. Use
7930 * NULL, if you don't care. */
7931 isds_error isds_disable_box_accessibility_externaly(
7932 struct isds_ctx *context, const struct isds_DbOwnerInfo *box,
7933 const struct tm *since, const struct isds_approval *approval,
7934 char **refnumber) {
7935 isds_error err = IE_SUCCESS;
7936 #if HAVE_LIBCURL
7937 char *service_name_locale = NULL;
7938 xmlNodePtr request = NULL, node;
7939 xmlNsPtr isds_ns = NULL;
7940 xmlChar *string = NULL;
7941 #endif
7944 if (!context) return IE_INVALID_CONTEXT;
7945 zfree(context->long_message);
7946 if (!box || !since) return IE_INVAL;
7948 #if HAVE_LIBCURL
7949 /* Build request */
7950 request = xmlNewNode(NULL, BAD_CAST "DisableDataBoxExternally");
7951 if (!request) {
7952 isds_printf_message(context,
7953 _("Could not build %s request"), "DisableDataBoxExternally");
7954 err = IE_ERROR;
7955 goto leave;
7957 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
7958 if(!isds_ns) {
7959 isds_log_message(context, _("Could not create ISDS name space"));
7960 err = IE_ERROR;
7961 goto leave;
7963 xmlSetNs(request, isds_ns);
7966 /* Add @box identification */
7967 INSERT_ELEMENT(node, request, "dbOwnerInfo");
7968 err = insert_DbOwnerInfo(context, box, node);
7969 if (err) goto leave;
7971 /* Add @since date */
7972 err = tm2datestring(since, &string);
7973 if(err) {
7974 isds_log_message(context,
7975 _("Could not convert `since' argument to ISO date string"));
7976 goto leave;
7978 INSERT_STRING(request, "dbOwnerDisableDate", string);
7979 zfree(string);
7981 /* Add @approval */
7982 err = insert_GExtApproval(context, approval, request);
7983 if (err) goto leave;
7985 /* Send it to server and process response */
7986 err = send_request_check_drop_response(context, SERVICE_DB_MANIPULATION,
7987 BAD_CAST "DisableDataBoxExternally", &request,
7988 (xmlChar **) refnumber);
7990 leave:
7991 free(string);
7992 xmlFreeNode(request);
7993 free(service_name_locale);
7994 #else /* not HAVE_LIBCURL */
7995 err = IE_NOTSUP;
7996 #endif
7998 return err;
8002 #if HAVE_LIBCURL
8003 /* Insert struct isds_message data (envelope (recipient data optional) and
8004 * documents into XML tree
8005 * @context is session context
8006 * @outgoing_message is libisds structure with message data
8007 * @create_message is XML CreateMessage or CreateMultipleMessage element
8008 * @process_recipient true for recipient data serialization, false for no
8009 * serialization */
8010 static isds_error insert_envelope_files(struct isds_ctx *context,
8011 const struct isds_message *outgoing_message, xmlNodePtr create_message,
8012 const _Bool process_recipient) {
8014 isds_error err = IE_SUCCESS;
8015 xmlNodePtr envelope, dm_files, node;
8016 xmlChar *string = NULL;
8018 if (!context) return IE_INVALID_CONTEXT;
8019 if (!outgoing_message || !create_message) return IE_INVAL;
8022 /* Build envelope */
8023 envelope = xmlNewChild(create_message, NULL, BAD_CAST "dmEnvelope", NULL);
8024 if (!envelope) {
8025 isds_printf_message(context, _("Could not add dmEnvelope child to "
8026 "%s element"), create_message->name);
8027 return IE_ERROR;
8030 if (!outgoing_message->envelope) {
8031 isds_log_message(context, _("Outgoing message is missing envelope"));
8032 err = IE_INVAL;
8033 goto leave;
8036 /* Insert optional message type */
8037 err = insert_message_type(context, outgoing_message->envelope->dmType,
8038 envelope);
8039 if (err) goto leave;
8041 INSERT_STRING(envelope, "dmSenderOrgUnit",
8042 outgoing_message->envelope->dmSenderOrgUnit);
8043 INSERT_LONGINT(envelope, "dmSenderOrgUnitNum",
8044 outgoing_message->envelope->dmSenderOrgUnitNum, string);
8046 if (process_recipient) {
8047 if (!outgoing_message->envelope->dbIDRecipient) {
8048 isds_log_message(context,
8049 _("Outgoing message is missing recipient box identifier"));
8050 err = IE_INVAL;
8051 goto leave;
8053 INSERT_STRING(envelope, "dbIDRecipient",
8054 outgoing_message->envelope->dbIDRecipient);
8056 INSERT_STRING(envelope, "dmRecipientOrgUnit",
8057 outgoing_message->envelope->dmRecipientOrgUnit);
8058 INSERT_LONGINT(envelope, "dmRecipientOrgUnitNum",
8059 outgoing_message->envelope->dmRecipientOrgUnitNum, string);
8060 INSERT_STRING(envelope, "dmToHands",
8061 outgoing_message->envelope->dmToHands);
8064 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmAnnotation, 0, 255,
8065 "dmAnnotation");
8066 INSERT_STRING(envelope, "dmAnnotation",
8067 outgoing_message->envelope->dmAnnotation);
8069 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientRefNumber,
8070 0, 50, "dmRecipientRefNumber");
8071 INSERT_STRING(envelope, "dmRecipientRefNumber",
8072 outgoing_message->envelope->dmRecipientRefNumber);
8074 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderRefNumber,
8075 0, 50, "dmSenderRefNumber");
8076 INSERT_STRING(envelope, "dmSenderRefNumber",
8077 outgoing_message->envelope->dmSenderRefNumber);
8079 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmRecipientIdent,
8080 0, 50, "dmRecipientIdent");
8081 INSERT_STRING(envelope, "dmRecipientIdent",
8082 outgoing_message->envelope->dmRecipientIdent);
8084 CHECK_FOR_STRING_LENGTH(outgoing_message->envelope->dmSenderIdent,
8085 0, 50, "dmSenderIdent");
8086 INSERT_STRING(envelope, "dmSenderIdent",
8087 outgoing_message->envelope->dmSenderIdent);
8089 INSERT_LONGINT(envelope, "dmLegalTitleLaw",
8090 outgoing_message->envelope->dmLegalTitleLaw, string);
8091 INSERT_LONGINT(envelope, "dmLegalTitleYear",
8092 outgoing_message->envelope->dmLegalTitleYear, string);
8093 INSERT_STRING(envelope, "dmLegalTitleSect",
8094 outgoing_message->envelope->dmLegalTitleSect);
8095 INSERT_STRING(envelope, "dmLegalTitlePar",
8096 outgoing_message->envelope->dmLegalTitlePar);
8097 INSERT_STRING(envelope, "dmLegalTitlePoint",
8098 outgoing_message->envelope->dmLegalTitlePoint);
8100 INSERT_BOOLEAN(envelope, "dmPersonalDelivery",
8101 outgoing_message->envelope->dmPersonalDelivery);
8102 INSERT_BOOLEAN(envelope, "dmAllowSubstDelivery",
8103 outgoing_message->envelope->dmAllowSubstDelivery);
8105 /* ???: Should we require value for dbEffectiveOVM sender?
8106 * ISDS has default as true */
8107 INSERT_BOOLEAN(envelope, "dmOVM", outgoing_message->envelope->dmOVM);
8108 INSERT_BOOLEAN(envelope, "dmOVM",
8109 outgoing_message->envelope->dmPublishOwnID);
8112 /* Append dmFiles */
8113 if (!outgoing_message->documents) {
8114 isds_log_message(context,
8115 _("Outgoing message is missing list of documents"));
8116 err = IE_INVAL;
8117 goto leave;
8119 dm_files = xmlNewChild(create_message, NULL, BAD_CAST "dmFiles", NULL);
8120 if (!dm_files) {
8121 isds_printf_message(context, _("Could not add dmFiles child to "
8122 "%s element"), create_message->name);
8123 err = IE_ERROR;
8124 goto leave;
8127 /* Check for document hierarchy */
8128 err = _isds_check_documents_hierarchy(context, outgoing_message->documents);
8129 if (err) goto leave;
8131 /* Process each document */
8132 for (struct isds_list *item =
8133 (struct isds_list *) outgoing_message->documents;
8134 item; item = item->next) {
8135 if (!item->data) {
8136 isds_log_message(context,
8137 _("List of documents contains empty item"));
8138 err = IE_INVAL;
8139 goto leave;
8141 /* FIXME: Check for dmFileMetaType and for document references.
8142 * Only first document can be of MAIN type */
8143 err = insert_document(context, (struct isds_document*) item->data,
8144 dm_files);
8146 if (err) goto leave;
8149 leave:
8150 free(string);
8151 return err;
8153 #endif /* HAVE_LIBCURL */
8156 /* Send a message via ISDS to a recipient
8157 * @context is session context
8158 * @outgoing_message is message to send; Some members are mandatory (like
8159 * dbIDRecipient), some are optional and some are irrelevant (especially data
8160 * about sender). Included pointer to isds_list documents must contain at
8161 * least one document of FILEMETATYPE_MAIN. This is read-write structure, some
8162 * members will be filled with valid data from ISDS. Exact list of write
8163 * members is subject to change. Currently dmID is changed.
8164 * @return ISDS_SUCCESS, or other error code if something goes wrong. */
8165 isds_error isds_send_message(struct isds_ctx *context,
8166 struct isds_message *outgoing_message) {
8168 isds_error err = IE_SUCCESS;
8169 #if HAVE_LIBCURL
8170 xmlNsPtr isds_ns = NULL;
8171 xmlNodePtr request = NULL;
8172 xmlDocPtr response = NULL;
8173 xmlChar *code = NULL, *message = NULL;
8174 xmlXPathContextPtr xpath_ctx = NULL;
8175 xmlXPathObjectPtr result = NULL;
8176 /*_Bool message_is_complete = 0;*/
8177 #endif
8179 if (!context) return IE_INVALID_CONTEXT;
8180 zfree(context->long_message);
8181 if (!outgoing_message) return IE_INVAL;
8183 #if HAVE_LIBCURL
8184 /* Check if connection is established
8185 * TODO: This check should be done downstairs. */
8186 if (!context->curl) return IE_CONNECTION_CLOSED;
8189 /* Build CreateMessage request */
8190 request = xmlNewNode(NULL, BAD_CAST "CreateMessage");
8191 if (!request) {
8192 isds_log_message(context,
8193 _("Could not build CreateMessage 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);
8204 /* Append envelope and files */
8205 err = insert_envelope_files(context, outgoing_message, request, 1);
8206 if (err) goto leave;
8209 /* Signal we can serialize message since now */
8210 /*message_is_complete = 1;*/
8213 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending CreateMessage request to ISDS\n"));
8215 /* Sent request */
8216 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
8218 /* Don't' destroy request, we want to provide it to application later */
8220 if (err) {
8221 isds_log(ILF_ISDS, ILL_DEBUG,
8222 _("Processing ISDS response on CreateMessage "
8223 "request failed\n"));
8224 goto leave;
8227 /* Check for response status */
8228 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
8229 &code, &message, NULL);
8230 if (err) {
8231 isds_log(ILF_ISDS, ILL_DEBUG,
8232 _("ISDS response on CreateMessage request "
8233 "is missing status\n"));
8234 goto leave;
8237 /* Request processed, but refused by server or server failed */
8238 if (xmlStrcmp(code, BAD_CAST "0000")) {
8239 char *box_id_locale =
8240 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
8241 char *code_locale = _isds_utf82locale((char*)code);
8242 char *message_locale = _isds_utf82locale((char*)message);
8243 isds_log(ILF_ISDS, ILL_DEBUG,
8244 _("Server did not accept message for %s on CreateMessage "
8245 "request (code=%s, message=%s)\n"),
8246 box_id_locale, code_locale, message_locale);
8247 isds_log_message(context, message_locale);
8248 free(box_id_locale);
8249 free(code_locale);
8250 free(message_locale);
8251 err = IE_ISDS;
8252 goto leave;
8256 /* Extract data */
8257 xpath_ctx = xmlXPathNewContext(response);
8258 if (!xpath_ctx) {
8259 err = IE_ERROR;
8260 goto leave;
8262 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8263 err = IE_ERROR;
8264 goto leave;
8266 result = xmlXPathEvalExpression(BAD_CAST "/isds:CreateMessageResponse",
8267 xpath_ctx);
8268 if (!result) {
8269 err = IE_ERROR;
8270 goto leave;
8272 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8273 isds_log_message(context, _("Missing CreateMessageResponse element"));
8274 err = IE_ISDS;
8275 goto leave;
8277 if (result->nodesetval->nodeNr > 1) {
8278 isds_log_message(context, _("Multiple CreateMessageResponse element"));
8279 err = IE_ISDS;
8280 goto leave;
8282 xpath_ctx->node = result->nodesetval->nodeTab[0];
8283 xmlXPathFreeObject(result); result = NULL;
8285 if (outgoing_message->envelope->dmID) {
8286 free(outgoing_message->envelope->dmID);
8287 outgoing_message->envelope->dmID = NULL;
8289 EXTRACT_STRING("isds:dmID", outgoing_message->envelope->dmID);
8290 if (!outgoing_message->envelope->dmID) {
8291 isds_log(ILF_ISDS, ILL_ERR, _("Server accepted sent message, "
8292 "but did not return assigned message ID\n"));
8295 leave:
8296 /* TODO: Serialize message into structure member raw */
8297 /* XXX: Each web service transport message in different format.
8298 * Therefore it's not possible to save them directly.
8299 * To save them, one must figure out common format.
8300 * We can leave it on application, or we can implement the ESS format. */
8301 /*if (message_is_complete) {
8302 if (outgoing_message->envelope->dmID) {
8304 /* Add assigned message ID as first child*/
8305 /*xmlNodePtr dmid_text = xmlNewText(
8306 (xmlChar *) outgoing_message->envelope->dmID);
8307 if (!dmid_text) goto serialization_failed;
8309 xmlNodePtr dmid_element = xmlNewNode(envelope->ns,
8310 BAD_CAST "dmID");
8311 if (!dmid_element) {
8312 xmlFreeNode(dmid_text);
8313 goto serialization_failed;
8316 xmlNodePtr dmid_element_with_text =
8317 xmlAddChild(dmid_element, dmid_text);
8318 if (!dmid_element_with_text) {
8319 xmlFreeNode(dmid_element);
8320 xmlFreeNode(dmid_text);
8321 goto serialization_failed;
8324 node = xmlAddPrevSibling(envelope->childern,
8325 dmid_element_with_text);
8326 if (!node) {
8327 xmlFreeNodeList(dmid_element_with_text);
8328 goto serialization_failed;
8332 /* Serialize message with ID into raw */
8333 /*buffer = serialize_element(envelope)*/
8334 /* }
8336 serialization_failed:
8340 /* Clean up */
8341 xmlXPathFreeObject(result);
8342 xmlXPathFreeContext(xpath_ctx);
8344 free(code);
8345 free(message);
8346 xmlFreeDoc(response);
8347 xmlFreeNode(request);
8349 if (!err)
8350 isds_log(ILF_ISDS, ILL_DEBUG,
8351 _("CreateMessage request processed by server "
8352 "successfully.\n"));
8353 #else /* not HAVE_LIBCURL */
8354 err = IE_NOTSUP;
8355 #endif
8357 return err;
8361 /* Send a message via ISDS to a multiple recipients
8362 * @context is session context
8363 * @outgoing_message is message to send; Some members are mandatory,
8364 * some are optional and some are irrelevant (especially data
8365 * about sender). Data about recipient will be substituted by ISDS from
8366 * @copies. Included pointer to isds_list documents must
8367 * contain at least one document of FILEMETATYPE_MAIN.
8368 * @copies is list of isds_message_copy structures addressing all desired
8369 * recipients. This is read-write structure, some members will be filled with
8370 * valid data from ISDS (message IDs, error codes, error descriptions).
8371 * @return
8372 * ISDS_SUCCESS if all messages have been sent
8373 * ISDS_PARTIAL_SUCCESS if sending of some messages has failed (failed and
8374 * succeeded messages can be identified by copies->data->error),
8375 * or other error code if something other goes wrong. */
8376 isds_error isds_send_message_to_multiple_recipients(struct isds_ctx *context,
8377 const struct isds_message *outgoing_message,
8378 struct isds_list *copies) {
8380 isds_error err = IE_SUCCESS;
8381 #if HAVE_LIBCURL
8382 isds_error append_err;
8383 xmlNsPtr isds_ns = NULL;
8384 xmlNodePtr request = NULL, recipients, recipient, node;
8385 struct isds_list *item;
8386 struct isds_message_copy *copy;
8387 xmlDocPtr response = NULL;
8388 xmlChar *code = NULL, *message = NULL;
8389 xmlXPathContextPtr xpath_ctx = NULL;
8390 xmlXPathObjectPtr result = NULL;
8391 xmlChar *string = NULL;
8392 int i;
8393 #endif
8395 if (!context) return IE_INVALID_CONTEXT;
8396 zfree(context->long_message);
8397 if (!outgoing_message || !copies) return IE_INVAL;
8399 #if HAVE_LIBCURL
8400 /* Check if connection is established
8401 * TODO: This check should be done downstairs. */
8402 if (!context->curl) return IE_CONNECTION_CLOSED;
8405 /* Build CreateMultipleMessage request */
8406 request = xmlNewNode(NULL, BAD_CAST "CreateMultipleMessage");
8407 if (!request) {
8408 isds_log_message(context,
8409 _("Could not build CreateMultipleMessage request"));
8410 return IE_ERROR;
8412 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8413 if(!isds_ns) {
8414 isds_log_message(context, _("Could not create ISDS name space"));
8415 xmlFreeNode(request);
8416 return IE_ERROR;
8418 xmlSetNs(request, isds_ns);
8421 /* Build recipients */
8422 recipients = xmlNewChild(request, NULL, BAD_CAST "dmRecipients", NULL);
8423 if (!recipients) {
8424 isds_log_message(context, _("Could not add dmRecipients child to "
8425 "CreateMultipleMessage element"));
8426 xmlFreeNode(request);
8427 return IE_ERROR;
8430 /* Insert each recipient */
8431 for (item = copies; item; item = item->next) {
8432 copy = (struct isds_message_copy *) item->data;
8433 if (!copy) {
8434 isds_log_message(context,
8435 _("`copies' list item contains empty data"));
8436 err = IE_INVAL;
8437 goto leave;
8440 recipient = xmlNewChild(recipients, NULL, BAD_CAST "dmRecipient", NULL);
8441 if (!recipient) {
8442 isds_log_message(context, _("Could not add dmRecipient child to "
8443 "dmRecipients element"));
8444 err = IE_ERROR;
8445 goto leave;
8448 if (!copy->dbIDRecipient) {
8449 isds_log_message(context,
8450 _("Message copy is missing recipient box identifier"));
8451 err = IE_INVAL;
8452 goto leave;
8454 INSERT_STRING(recipient, "dbIDRecipient", copy->dbIDRecipient);
8455 INSERT_STRING(recipient, "dmRecipientOrgUnit",
8456 copy->dmRecipientOrgUnit);
8457 INSERT_LONGINT(recipient, "dmRecipientOrgUnitNum",
8458 copy->dmRecipientOrgUnitNum, string);
8459 INSERT_STRING(recipient, "dmToHands", copy->dmToHands);
8462 /* Append envelope and files */
8463 err = insert_envelope_files(context, outgoing_message, request, 0);
8464 if (err) goto leave;
8467 isds_log(ILF_ISDS, ILL_DEBUG,
8468 _("Sending CreateMultipleMessage request to ISDS\n"));
8470 /* Sent request */
8471 err = _isds(context, SERVICE_DM_OPERATIONS, request, &response, NULL, NULL);
8472 if (err) {
8473 isds_log(ILF_ISDS, ILL_DEBUG,
8474 _("Processing ISDS response on CreateMultipleMessage "
8475 "request failed\n"));
8476 goto leave;
8479 /* Check for response status */
8480 err = isds_response_status(context, SERVICE_DM_OPERATIONS, response,
8481 &code, &message, NULL);
8482 if (err) {
8483 isds_log(ILF_ISDS, ILL_DEBUG,
8484 _("ISDS response on CreateMultipleMessage request "
8485 "is missing status\n"));
8486 goto leave;
8489 /* Request processed, but some copies failed */
8490 if (!xmlStrcmp(code, BAD_CAST "0004")) {
8491 char *box_id_locale =
8492 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
8493 char *code_locale = _isds_utf82locale((char*)code);
8494 char *message_locale = _isds_utf82locale((char*)message);
8495 isds_log(ILF_ISDS, ILL_DEBUG,
8496 _("Server did accept message for multiple recipients "
8497 "on CreateMultipleMessage request but delivery to "
8498 "some of them failed (code=%s, message=%s)\n"),
8499 box_id_locale, code_locale, message_locale);
8500 isds_log_message(context, message_locale);
8501 free(box_id_locale);
8502 free(code_locale);
8503 free(message_locale);
8504 err = IE_PARTIAL_SUCCESS;
8507 /* Request refused by server as whole */
8508 else if (xmlStrcmp(code, BAD_CAST "0000")) {
8509 char *box_id_locale =
8510 _isds_utf82locale((char*)outgoing_message->envelope->dbIDRecipient);
8511 char *code_locale = _isds_utf82locale((char*)code);
8512 char *message_locale = _isds_utf82locale((char*)message);
8513 isds_log(ILF_ISDS, ILL_DEBUG,
8514 _("Server did not accept message for multiple recipients "
8515 "on CreateMultipleMessage request (code=%s, message=%s)\n"),
8516 box_id_locale, code_locale, message_locale);
8517 isds_log_message(context, message_locale);
8518 free(box_id_locale);
8519 free(code_locale);
8520 free(message_locale);
8521 err = IE_ISDS;
8522 goto leave;
8526 /* Extract data */
8527 xpath_ctx = xmlXPathNewContext(response);
8528 if (!xpath_ctx) {
8529 err = IE_ERROR;
8530 goto leave;
8532 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8533 err = IE_ERROR;
8534 goto leave;
8536 result = xmlXPathEvalExpression(
8537 BAD_CAST "/isds:CreateMultipleMessageResponse"
8538 "/isds:dmMultipleStatus/isds:dmSingleStatus",
8539 xpath_ctx);
8540 if (!result) {
8541 err = IE_ERROR;
8542 goto leave;
8544 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8545 isds_log_message(context, _("Missing isds:dmSingleStatus element"));
8546 err = IE_ISDS;
8547 goto leave;
8550 /* Extract message ID and delivery status for each copy */
8551 for (item = copies, i = 0; item && i < result->nodesetval->nodeNr;
8552 item = item->next, i++) {
8553 copy = (struct isds_message_copy *) item->data;
8554 xpath_ctx->node = result->nodesetval->nodeTab[i];
8556 append_err = append_TMStatus(context, copy, xpath_ctx);
8557 if (append_err) {
8558 err = append_err;
8559 goto leave;
8562 if (item || i < result->nodesetval->nodeNr) {
8563 isds_printf_message(context, _("ISDS returned unexpected number of "
8564 "message copy delivery states: %d"),
8565 result->nodesetval->nodeNr);
8566 err = IE_ISDS;
8567 goto leave;
8571 leave:
8572 /* Clean up */
8573 free(string);
8574 xmlXPathFreeObject(result);
8575 xmlXPathFreeContext(xpath_ctx);
8577 free(code);
8578 free(message);
8579 xmlFreeDoc(response);
8580 xmlFreeNode(request);
8582 if (!err)
8583 isds_log(ILF_ISDS, ILL_DEBUG,
8584 _("CreateMultipleMessageResponse request processed by server "
8585 "successfully.\n"));
8586 #else /* not HAVE_LIBCURL */
8587 err = IE_NOTSUP;
8588 #endif
8590 return err;
8594 /* Get list of messages. This is common core for getting sent or received
8595 * messages.
8596 * Any criterion argument can be NULL, if you don't care about it.
8597 * @context is session context. Must not be NULL.
8598 * @outgoing_direction is true if you want list of outgoing messages,
8599 * it's false if you want incoming messages.
8600 * @from_time is minimal time and date of message sending inclusive.
8601 * @to_time is maximal time and date of message sending inclusive
8602 * @organization_unit_number is number of sender/recipient respectively.
8603 * @status_filter is bit field of isds_message_status values. Use special
8604 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8605 * all values, you can use bit-wise arithmetic if you want.)
8606 * @offset is index of first message we are interested in. First message is 1.
8607 * Set to 0 (or 1) if you don't care.
8608 * @number is maximal length of list you want to get as input value, outputs
8609 * number of messages matching these criteria. Can be NULL if you don't care
8610 * (applies to output value either).
8611 * @messages is automatically reallocated list of isds_message's. Be ware that
8612 * it returns only brief overview (envelope and some other fields) about each
8613 * message, not the complete message. FIXME: Specify exact fields.
8614 * The list is sorted by delivery time in ascending order.
8615 * Use NULL if you don't care about don't need the data (useful if you want to
8616 * know only the @number). If you provide &NULL, list will be allocated on
8617 * heap, if you provide pointer to non-NULL, list will be freed automatically
8618 * at first. Also in case of error the list will be NULLed.
8619 * @return IE_SUCCESS or appropriate error code. */
8620 static isds_error isds_get_list_of_messages(struct isds_ctx *context,
8621 _Bool outgoing_direction,
8622 const struct timeval *from_time, const struct timeval *to_time,
8623 const long int *organization_unit_number,
8624 const unsigned int status_filter,
8625 const unsigned long int offset, unsigned long int *number,
8626 struct isds_list **messages) {
8628 isds_error err = IE_SUCCESS;
8629 #if HAVE_LIBCURL
8630 xmlNsPtr isds_ns = NULL;
8631 xmlNodePtr request = NULL, node;
8632 xmlDocPtr response = NULL;
8633 xmlChar *code = NULL, *message = NULL;
8634 xmlXPathContextPtr xpath_ctx = NULL;
8635 xmlXPathObjectPtr result = NULL;
8636 xmlChar *string = NULL;
8637 long unsigned int count = 0;
8638 #endif
8640 if (!context) return IE_INVALID_CONTEXT;
8641 zfree(context->long_message);
8643 /* Free former message list if any */
8644 if (messages) isds_list_free(messages);
8646 #if HAVE_LIBCURL
8647 /* Check if connection is established
8648 * TODO: This check should be done downstairs. */
8649 if (!context->curl) return IE_CONNECTION_CLOSED;
8651 /* Build GetListOf*Messages request */
8652 request = xmlNewNode(NULL,
8653 (outgoing_direction) ?
8654 BAD_CAST "GetListOfSentMessages" :
8655 BAD_CAST "GetListOfReceivedMessages"
8657 if (!request) {
8658 isds_log_message(context,
8659 (outgoing_direction) ?
8660 _("Could not build GetListOfSentMessages request") :
8661 _("Could not build GetListOfReceivedMessages request")
8663 return IE_ERROR;
8665 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8666 if(!isds_ns) {
8667 isds_log_message(context, _("Could not create ISDS name space"));
8668 xmlFreeNode(request);
8669 return IE_ERROR;
8671 xmlSetNs(request, isds_ns);
8674 if (from_time) {
8675 err = timeval2timestring(from_time, &string);
8676 if (err) goto leave;
8678 INSERT_STRING(request, "dmFromTime", string);
8679 free(string); string = NULL;
8681 if (to_time) {
8682 err = timeval2timestring(to_time, &string);
8683 if (err) goto leave;
8685 INSERT_STRING(request, "dmToTime", string);
8686 free(string); string = NULL;
8688 if (outgoing_direction) {
8689 INSERT_LONGINT(request, "dmSenderOrgUnitNum",
8690 organization_unit_number, string);
8691 } else {
8692 INSERT_LONGINT(request, "dmRecipientOrgUnitNum",
8693 organization_unit_number, string);
8696 if (status_filter > MESSAGESTATE_ANY) {
8697 isds_printf_message(context,
8698 _("Invalid message state filter value: %ld"), status_filter);
8699 err = IE_INVAL;
8700 goto leave;
8702 INSERT_ULONGINTNOPTR(request, "dmStatusFilter", status_filter, string);
8704 if (offset > 0 ) {
8705 INSERT_ULONGINTNOPTR(request, "dmOffset", offset, string);
8706 } else {
8707 INSERT_STRING(request, "dmOffset", "1");
8710 /* number 0 means no limit */
8711 if (number && *number == 0) {
8712 INSERT_STRING(request, "dmLimit", NULL);
8713 } else {
8714 INSERT_ULONGINT(request, "dmLimit", number, string);
8718 isds_log(ILF_ISDS, ILL_DEBUG,
8719 (outgoing_direction) ?
8720 _("Sending GetListOfSentMessages request to ISDS\n") :
8721 _("Sending GetListOfReceivedMessages request to ISDS\n")
8724 /* Sent request */
8725 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
8726 xmlFreeNode(request); request = NULL;
8728 if (err) {
8729 isds_log(ILF_ISDS, ILL_DEBUG,
8730 (outgoing_direction) ?
8731 _("Processing ISDS response on GetListOfSentMessages "
8732 "request failed\n") :
8733 _("Processing ISDS response on GetListOfReceivedMessages "
8734 "request failed\n")
8736 goto leave;
8739 /* Check for response status */
8740 err = isds_response_status(context, SERVICE_DM_INFO, response,
8741 &code, &message, NULL);
8742 if (err) {
8743 isds_log(ILF_ISDS, ILL_DEBUG,
8744 (outgoing_direction) ?
8745 _("ISDS response on GetListOfSentMessages request "
8746 "is missing status\n") :
8747 _("ISDS response on GetListOfReceivedMessages request "
8748 "is missing status\n")
8750 goto leave;
8753 /* Request processed, but nothing found */
8754 if (xmlStrcmp(code, BAD_CAST "0000")) {
8755 char *code_locale = _isds_utf82locale((char*)code);
8756 char *message_locale = _isds_utf82locale((char*)message);
8757 isds_log(ILF_ISDS, ILL_DEBUG,
8758 (outgoing_direction) ?
8759 _("Server refused GetListOfSentMessages request "
8760 "(code=%s, message=%s)\n") :
8761 _("Server refused GetListOfReceivedMessages request "
8762 "(code=%s, message=%s)\n"),
8763 code_locale, message_locale);
8764 isds_log_message(context, message_locale);
8765 free(code_locale);
8766 free(message_locale);
8767 err = IE_ISDS;
8768 goto leave;
8772 /* Extract data */
8773 xpath_ctx = xmlXPathNewContext(response);
8774 if (!xpath_ctx) {
8775 err = IE_ERROR;
8776 goto leave;
8778 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
8779 err = IE_ERROR;
8780 goto leave;
8782 result = xmlXPathEvalExpression(
8783 (outgoing_direction) ?
8784 BAD_CAST "/isds:GetListOfSentMessagesResponse/"
8785 "isds:dmRecords/isds:dmRecord" :
8786 BAD_CAST "/isds:GetListOfReceivedMessagesResponse/"
8787 "isds:dmRecords/isds:dmRecord",
8788 xpath_ctx);
8789 if (!result) {
8790 err = IE_ERROR;
8791 goto leave;
8794 /* Fill output arguments in */
8795 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
8796 struct isds_envelope *envelope;
8797 struct isds_list *item = NULL, *last_item = NULL;
8799 for (count = 0; count < result->nodesetval->nodeNr; count++) {
8800 /* Create new message */
8801 item = calloc(1, sizeof(*item));
8802 if (!item) {
8803 err = IE_NOMEM;
8804 goto leave;
8806 item->destructor = (void(*)(void**)) &isds_message_free;
8807 item->data = calloc(1, sizeof(struct isds_message));
8808 if (!item->data) {
8809 isds_list_free(&item);
8810 err = IE_NOMEM;
8811 goto leave;
8814 /* Extract envelope data */
8815 xpath_ctx->node = result->nodesetval->nodeTab[count];
8816 envelope = NULL;
8817 err = extract_DmRecord(context, &envelope, xpath_ctx);
8818 if (err) {
8819 isds_list_free(&item);
8820 goto leave;
8823 /* Attach extracted envelope */
8824 ((struct isds_message *) item->data)->envelope = envelope;
8826 /* Append new message into the list */
8827 if (!*messages) {
8828 *messages = last_item = item;
8829 } else {
8830 last_item->next = item;
8831 last_item = item;
8835 if (number) *number = count;
8837 leave:
8838 if (err) {
8839 isds_list_free(messages);
8842 free(string);
8843 xmlXPathFreeObject(result);
8844 xmlXPathFreeContext(xpath_ctx);
8846 free(code);
8847 free(message);
8848 xmlFreeDoc(response);
8849 xmlFreeNode(request);
8851 if (!err)
8852 isds_log(ILF_ISDS, ILL_DEBUG,
8853 (outgoing_direction) ?
8854 _("GetListOfSentMessages request processed by server "
8855 "successfully.\n") :
8856 _("GetListOfReceivedMessages request processed by server "
8857 "successfully.\n")
8859 #else /* not HAVE_LIBCURL */
8860 err = IE_NOTSUP;
8861 #endif
8862 return err;
8866 /* Get list of outgoing (already sent) messages.
8867 * Any criterion argument can be NULL, if you don't care about it.
8868 * @context is session context. Must not be NULL.
8869 * @from_time is minimal time and date of message sending inclusive.
8870 * @to_time is maximal time and date of message sending inclusive
8871 * @dmSenderOrgUnitNum is the same as isds_envelope.dmSenderOrgUnitNum
8872 * @status_filter is bit field of isds_message_status values. Use special
8873 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8874 * all values, you can use bit-wise arithmetic if you want.)
8875 * @offset is index of first message we are interested in. First message is 1.
8876 * Set to 0 (or 1) if you don't care.
8877 * @number is maximal length of list you want to get as input value, outputs
8878 * number of messages matching these criteria. Can be NULL if you don't care
8879 * (applies to output value either).
8880 * @messages is automatically reallocated list of isds_message's. Be ware that
8881 * it returns only brief overview (envelope and some other fields) about each
8882 * message, not the complete message. FIXME: Specify exact fields.
8883 * The list is sorted by delivery time in ascending order.
8884 * Use NULL if you don't care about the meta data (useful if you want to know
8885 * only the @number). If you provide &NULL, list will be allocated on heap,
8886 * if you provide pointer to non-NULL, list will be freed automatically at
8887 * first. Also in case of error the list will be NULLed.
8888 * @return IE_SUCCESS or appropriate error code. */
8889 isds_error isds_get_list_of_sent_messages(struct isds_ctx *context,
8890 const struct timeval *from_time, const struct timeval *to_time,
8891 const long int *dmSenderOrgUnitNum, const unsigned int status_filter,
8892 const unsigned long int offset, unsigned long int *number,
8893 struct isds_list **messages) {
8895 return isds_get_list_of_messages(
8896 context, 1,
8897 from_time, to_time, dmSenderOrgUnitNum, status_filter,
8898 offset, number,
8899 messages);
8903 /* Get list of incoming (addressed to you) messages.
8904 * Any criterion argument can be NULL, if you don't care about it.
8905 * @context is session context. Must not be NULL.
8906 * @from_time is minimal time and date of message sending inclusive.
8907 * @to_time is maximal time and date of message sending inclusive
8908 * @dmRecipientOrgUnitNum is the same as isds_envelope.dmRecipientOrgUnitNum
8909 * @status_filter is bit field of isds_message_status values. Use special
8910 * value MESSAGESTATE_ANY to signal you don't care. (It's defined as union of
8911 * all values, you can use bit-wise arithmetic if you want.)
8912 * @offset is index of first message we are interested in. First message is 1.
8913 * Set to 0 (or 1) if you don't care.
8914 * @number is maximal length of list you want to get as input value, outputs
8915 * number of messages matching these criteria. Can be NULL if you don't care
8916 * (applies to output value either).
8917 * @messages is automatically reallocated list of isds_message's. Be ware that
8918 * it returns only brief overview (envelope and some other fields) about each
8919 * message, not the complete message. FIXME: Specify exact fields.
8920 * Use NULL if you don't care about the meta data (useful if you want to know
8921 * only the @number). If you provide &NULL, list will be allocated on heap,
8922 * if you provide pointer to non-NULL, list will be freed automatically at
8923 * first. Also in case of error the list will be NULLed.
8924 * @return IE_SUCCESS or appropriate error code. */
8925 isds_error isds_get_list_of_received_messages(struct isds_ctx *context,
8926 const struct timeval *from_time, const struct timeval *to_time,
8927 const long int *dmRecipientOrgUnitNum,
8928 const unsigned int status_filter,
8929 const unsigned long int offset, unsigned long int *number,
8930 struct isds_list **messages) {
8932 return isds_get_list_of_messages(
8933 context, 0,
8934 from_time, to_time, dmRecipientOrgUnitNum, status_filter,
8935 offset, number,
8936 messages);
8940 /* Get list of sent message state changes.
8941 * Any criterion argument can be NULL, if you don't care about it.
8942 * @context is session context. Must not be NULL.
8943 * @from_time is minimal time and date of status changes inclusive
8944 * @to_time is maximal time and date of status changes inclusive
8945 * @changed_states is automatically reallocated list of
8946 * isds_message_status_change's. If you provide &NULL, list will be allocated
8947 * on heap, if you provide pointer to non-NULL, list will be freed
8948 * automatically at first. Also in case of error the list will be NULLed.
8949 * XXX: The list item ordering is not specified.
8950 * XXX: Server provides only `recent' changes.
8951 * @return IE_SUCCESS or appropriate error code. */
8952 isds_error isds_get_list_of_sent_message_state_changes(
8953 struct isds_ctx *context,
8954 const struct timeval *from_time, const struct timeval *to_time,
8955 struct isds_list **changed_states) {
8957 isds_error err = IE_SUCCESS;
8958 #if HAVE_LIBCURL
8959 xmlNsPtr isds_ns = NULL;
8960 xmlNodePtr request = NULL, node;
8961 xmlDocPtr response = NULL;
8962 xmlXPathContextPtr xpath_ctx = NULL;
8963 xmlXPathObjectPtr result = NULL;
8964 xmlChar *string = NULL;
8965 long unsigned int count = 0;
8966 #endif
8968 if (!context) return IE_INVALID_CONTEXT;
8969 zfree(context->long_message);
8971 /* Free former message list if any */
8972 isds_list_free(changed_states);
8974 #if HAVE_LIBCURL
8975 /* Check if connection is established
8976 * TODO: This check should be done downstairs. */
8977 if (!context->curl) return IE_CONNECTION_CLOSED;
8979 /* Build GetMessageStateChanges request */
8980 request = xmlNewNode(NULL, BAD_CAST "GetMessageStateChanges");
8981 if (!request) {
8982 isds_log_message(context,
8983 _("Could not build GetMessageStateChanges request"));
8984 return IE_ERROR;
8986 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
8987 if(!isds_ns) {
8988 isds_log_message(context, _("Could not create ISDS name space"));
8989 xmlFreeNode(request);
8990 return IE_ERROR;
8992 xmlSetNs(request, isds_ns);
8995 if (from_time) {
8996 err = timeval2timestring(from_time, &string);
8997 if (err) goto leave;
8999 INSERT_STRING(request, "dmFromTime", string);
9000 zfree(string);
9002 if (to_time) {
9003 err = timeval2timestring(to_time, &string);
9004 if (err) goto leave;
9006 INSERT_STRING(request, "dmToTime", string);
9007 zfree(string);
9010 /* Sent request */
9011 err = send_destroy_request_check_response(context,
9012 SERVICE_DM_INFO, BAD_CAST "GetMessageStateChanges", &request,
9013 &response, NULL, NULL);
9014 if (err) goto leave;
9017 /* Extract data */
9018 xpath_ctx = xmlXPathNewContext(response);
9019 if (!xpath_ctx) {
9020 err = IE_ERROR;
9021 goto leave;
9023 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9024 err = IE_ERROR;
9025 goto leave;
9027 result = xmlXPathEvalExpression(
9028 BAD_CAST "/isds:GetMessageStateChangesResponse/"
9029 "isds:dmRecords/isds:dmRecord", xpath_ctx);
9030 if (!result) {
9031 err = IE_ERROR;
9032 goto leave;
9035 /* Fill output arguments in */
9036 if (!xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9037 struct isds_list *item = NULL, *last_item = NULL;
9039 for (count = 0; count < result->nodesetval->nodeNr; count++) {
9040 /* Create new status change */
9041 item = calloc(1, sizeof(*item));
9042 if (!item) {
9043 err = IE_NOMEM;
9044 goto leave;
9046 item->destructor =
9047 (void(*)(void**)) &isds_message_status_change_free;
9049 /* Extract message status change */
9050 xpath_ctx->node = result->nodesetval->nodeTab[count];
9051 err = extract_StateChangesRecord(context,
9052 (struct isds_message_status_change **) &item->data,
9053 xpath_ctx);
9054 if (err) {
9055 isds_list_free(&item);
9056 goto leave;
9059 /* Append new message status change into the list */
9060 if (!*changed_states) {
9061 *changed_states = last_item = item;
9062 } else {
9063 last_item->next = item;
9064 last_item = item;
9069 leave:
9070 if (err) {
9071 isds_list_free(changed_states);
9074 free(string);
9075 xmlXPathFreeObject(result);
9076 xmlXPathFreeContext(xpath_ctx);
9077 xmlFreeDoc(response);
9078 xmlFreeNode(request);
9080 if (!err)
9081 isds_log(ILF_ISDS, ILL_DEBUG,
9082 _("GetMessageStateChanges request processed by server "
9083 "successfully.\n"));
9084 #else /* not HAVE_LIBCURL */
9085 err = IE_NOTSUP;
9086 #endif
9087 return err;
9091 #if HAVE_LIBCURL
9092 /* Build ISDS request of XSD tIDMessInput type, sent it and check for error
9093 * code
9094 * @context is session context
9095 * @service is ISDS WS service handler
9096 * @service_name is name of SERVICE_DM_OPERATIONS
9097 * @message_id is message ID to send as service argument to ISDS
9098 * @response is reallocated server SOAP body response as XML document
9099 * @raw_response is reallocated bit stream with response body. Use
9100 * NULL if you don't care
9101 * @raw_response_length is size of @raw_response in bytes
9102 * @code is reallocated ISDS status code
9103 * @status_message is reallocated ISDS status message
9104 * @return error coded from lower layer, context message will be set up
9105 * appropriately. */
9106 static isds_error build_send_check_message_request(struct isds_ctx *context,
9107 const isds_service service, const xmlChar *service_name,
9108 const char *message_id,
9109 xmlDocPtr *response, void **raw_response, size_t *raw_response_length,
9110 xmlChar **code, xmlChar **status_message) {
9112 isds_error err = IE_SUCCESS;
9113 char *service_name_locale = NULL, *message_id_locale = NULL;
9114 xmlNodePtr request = NULL, node;
9115 xmlNsPtr isds_ns = NULL;
9117 if (!context) return IE_INVALID_CONTEXT;
9118 if (!service_name || !message_id) return IE_INVAL;
9119 if (!response || !code || !status_message) return IE_INVAL;
9120 if (!raw_response_length && raw_response) return IE_INVAL;
9122 /* Free output argument */
9123 xmlFreeDoc(*response); *response = NULL;
9124 if (raw_response) zfree(*raw_response);
9125 zfree(*code);
9126 zfree(*status_message);
9129 /* Check if connection is established
9130 * TODO: This check should be done downstairs. */
9131 if (!context->curl) return IE_CONNECTION_CLOSED;
9133 service_name_locale = _isds_utf82locale((char*)service_name);
9134 message_id_locale = _isds_utf82locale(message_id);
9135 if (!service_name_locale || !message_id_locale) {
9136 err = IE_NOMEM;
9137 goto leave;
9140 /* Build request */
9141 request = xmlNewNode(NULL, service_name);
9142 if (!request) {
9143 isds_printf_message(context,
9144 _("Could not build %s request for %s message ID"),
9145 service_name_locale, message_id_locale);
9146 err = IE_ERROR;
9147 goto leave;
9149 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
9150 if(!isds_ns) {
9151 isds_log_message(context, _("Could not create ISDS name space"));
9152 err = IE_ERROR;
9153 goto leave;
9155 xmlSetNs(request, isds_ns);
9158 /* Add requested ID */
9159 err = validate_message_id_length(context, (xmlChar *) message_id);
9160 if (err) goto leave;
9161 INSERT_STRING(request, "dmID", message_id);
9164 isds_log(ILF_ISDS, ILL_DEBUG,
9165 _("Sending %s request for %s message ID to ISDS\n"),
9166 service_name_locale, message_id_locale);
9168 /* Send request */
9169 err = _isds(context, service, request, response,
9170 raw_response, raw_response_length);
9171 xmlFreeNode(request); request = NULL;
9173 if (err) {
9174 isds_log(ILF_ISDS, ILL_DEBUG,
9175 _("Processing ISDS response on %s request failed\n"),
9176 service_name_locale);
9177 goto leave;
9180 /* Check for response status */
9181 err = isds_response_status(context, service, *response,
9182 code, status_message, NULL);
9183 if (err) {
9184 isds_log(ILF_ISDS, ILL_DEBUG,
9185 _("ISDS response on %s request is missing status\n"),
9186 service_name_locale);
9187 goto leave;
9190 /* Request processed, but nothing found */
9191 if (xmlStrcmp(*code, BAD_CAST "0000")) {
9192 char *code_locale = _isds_utf82locale((char*) *code);
9193 char *status_message_locale = _isds_utf82locale((char*) *status_message);
9194 isds_log(ILF_ISDS, ILL_DEBUG,
9195 _("Server refused %s request for %s message ID "
9196 "(code=%s, message=%s)\n"),
9197 service_name_locale, message_id_locale,
9198 code_locale, status_message_locale);
9199 isds_log_message(context, status_message_locale);
9200 free(code_locale);
9201 free(status_message_locale);
9202 err = IE_ISDS;
9203 goto leave;
9206 leave:
9207 free(message_id_locale);
9208 free(service_name_locale);
9209 xmlFreeNode(request);
9210 return err;
9214 /* Find dmSignature in ISDS response, extract decoded CMS structure, extract
9215 * signed data and free ISDS response.
9216 * @context is session context
9217 * @message_id is UTF-8 encoded message ID for logging purpose
9218 * @response is parsed XML document. It will be freed and NULLed in the middle
9219 * of function run to save memory. This is not guaranteed in case of error.
9220 * @request_name is name of ISDS request used to construct response root
9221 * element name and for logging purpose.
9222 * @raw is reallocated output buffer with DER encoded CMS data
9223 * @raw_length is size of @raw buffer in bytes
9224 * @returns standard error codes, in case of error, @raw will be freed and
9225 * NULLed, @response sometimes. */
9226 static isds_error find_extract_signed_data_free_response(
9227 struct isds_ctx *context, const xmlChar *message_id,
9228 xmlDocPtr *response, const xmlChar *request_name,
9229 void **raw, size_t *raw_length) {
9231 isds_error err = IE_SUCCESS;
9232 char *xpath_expression = NULL;
9233 xmlXPathContextPtr xpath_ctx = NULL;
9234 xmlXPathObjectPtr result = NULL;
9235 char *encoded_structure = NULL;
9237 if (!context) return IE_INVALID_CONTEXT;
9238 if (!raw) return IE_INVAL;
9239 zfree(*raw);
9240 if (!message_id || !response || !*response || !request_name || !raw_length)
9241 return IE_INVAL;
9243 /* Build XPath expression */
9244 xpath_expression = _isds_astrcat3("/isds:", (char *) request_name,
9245 "Response/isds:dmSignature");
9246 if (!xpath_expression) return IE_NOMEM;
9248 /* Extract data */
9249 xpath_ctx = xmlXPathNewContext(*response);
9250 if (!xpath_ctx) {
9251 err = IE_ERROR;
9252 goto leave;
9254 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9255 err = IE_ERROR;
9256 goto leave;
9258 result = xmlXPathEvalExpression(BAD_CAST xpath_expression, xpath_ctx);
9259 if (!result) {
9260 err = IE_ERROR;
9261 goto leave;
9263 /* Empty response */
9264 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9265 char *message_id_locale = _isds_utf82locale((char*) message_id);
9266 isds_printf_message(context,
9267 _("Server did not return any signed data for message ID `%s' "
9268 "on %s request"),
9269 message_id_locale, request_name);
9270 free(message_id_locale);
9271 err = IE_ISDS;
9272 goto leave;
9274 /* More responses */
9275 if (result->nodesetval->nodeNr > 1) {
9276 char *message_id_locale = _isds_utf82locale((char*) message_id);
9277 isds_printf_message(context,
9278 _("Server did return more signed data for message ID `%s' "
9279 "on %s request"),
9280 message_id_locale, request_name);
9281 free(message_id_locale);
9282 err = IE_ISDS;
9283 goto leave;
9285 /* One response */
9286 xpath_ctx->node = result->nodesetval->nodeTab[0];
9288 /* Extract PKCS#7 structure */
9289 EXTRACT_STRING(".", encoded_structure);
9290 if (!encoded_structure) {
9291 isds_log_message(context, _("dmSignature element is empty"));
9294 /* Here we have delivery info as standalone CMS in encoded_structure.
9295 * We don't need any other data, free them: */
9296 xmlXPathFreeObject(result); result = NULL;
9297 xmlXPathFreeContext(xpath_ctx); xpath_ctx = NULL;
9298 xmlFreeDoc(*response); *response = NULL;
9301 /* Decode PKCS#7 to DER format */
9302 *raw_length = _isds_b64decode(encoded_structure, raw);
9303 if (*raw_length == (size_t) -1) {
9304 isds_log_message(context,
9305 _("Error while Base64-decoding PKCS#7 structure"));
9306 err = IE_ERROR;
9307 goto leave;
9310 leave:
9311 if (err) {
9312 zfree(*raw);
9313 raw_length = 0;
9316 free(encoded_structure);
9317 xmlXPathFreeObject(result);
9318 xmlXPathFreeContext(xpath_ctx);
9319 free(xpath_expression);
9321 return err;
9323 #endif /* HAVE_LIBCURL */
9326 /* Download incoming message envelope identified by ID.
9327 * @context is session context
9328 * @message_id is message identifier (you can get them from
9329 * isds_get_list_of_received_messages())
9330 * @message is automatically reallocated message retrieved from ISDS.
9331 * It will miss documents per se. Use isds_get_received_message(), if you are
9332 * interested in documents (content) too.
9333 * Returned hash and timestamp require documents to be verifiable. */
9334 isds_error isds_get_received_envelope(struct isds_ctx *context,
9335 const char *message_id, struct isds_message **message) {
9337 isds_error err = IE_SUCCESS;
9338 #if HAVE_LIBCURL
9339 xmlDocPtr response = NULL;
9340 xmlChar *code = NULL, *status_message = NULL;
9341 xmlXPathContextPtr xpath_ctx = NULL;
9342 xmlXPathObjectPtr result = NULL;
9343 #endif
9345 if (!context) return IE_INVALID_CONTEXT;
9346 zfree(context->long_message);
9348 /* Free former message if any */
9349 if (!message) return IE_INVAL;
9350 isds_message_free(message);
9352 #if HAVE_LIBCURL
9353 /* Do request and check for success */
9354 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9355 BAD_CAST "MessageEnvelopeDownload", message_id,
9356 &response, NULL, NULL, &code, &status_message);
9357 if (err) goto leave;
9359 /* Extract data */
9360 xpath_ctx = xmlXPathNewContext(response);
9361 if (!xpath_ctx) {
9362 err = IE_ERROR;
9363 goto leave;
9365 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9366 err = IE_ERROR;
9367 goto leave;
9369 result = xmlXPathEvalExpression(
9370 BAD_CAST "/isds:MessageEnvelopeDownloadResponse/"
9371 "isds:dmReturnedMessageEnvelope",
9372 xpath_ctx);
9373 if (!result) {
9374 err = IE_ERROR;
9375 goto leave;
9377 /* Empty response */
9378 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9379 char *message_id_locale = _isds_utf82locale((char*) message_id);
9380 isds_printf_message(context,
9381 _("Server did not return any envelope for ID `%s' "
9382 "on MessageEnvelopeDownload request"), message_id_locale);
9383 free(message_id_locale);
9384 err = IE_ISDS;
9385 goto leave;
9387 /* More envelops */
9388 if (result->nodesetval->nodeNr > 1) {
9389 char *message_id_locale = _isds_utf82locale((char*) message_id);
9390 isds_printf_message(context,
9391 _("Server did return more envelopes for ID `%s' "
9392 "on MessageEnvelopeDownload request"), message_id_locale);
9393 free(message_id_locale);
9394 err = IE_ISDS;
9395 goto leave;
9397 /* One message */
9398 xpath_ctx->node = result->nodesetval->nodeTab[0];
9400 /* Extract the envelope (= message without documents, hence 0) */
9401 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
9402 if (err) goto leave;
9404 /* Save XML blob */
9405 err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
9406 &(*message)->raw_length);
9408 leave:
9409 if (err) {
9410 isds_message_free(message);
9413 xmlXPathFreeObject(result);
9414 xmlXPathFreeContext(xpath_ctx);
9416 free(code);
9417 free(status_message);
9418 if (!*message || !(*message)->xml) {
9419 xmlFreeDoc(response);
9422 if (!err)
9423 isds_log(ILF_ISDS, ILL_DEBUG,
9424 _("MessageEnvelopeDownload request processed by server "
9425 "successfully.\n")
9427 #else /* not HAVE_LIBCURL */
9428 err = IE_NOTSUP;
9429 #endif
9430 return err;
9434 /* Load delivery info of any format from buffer.
9435 * @context is session context
9436 * @raw_type advertises format of @buffer content. Only delivery info types
9437 * are accepted.
9438 * @buffer is DER encoded PKCS#7 structure with signed delivery info. You can
9439 * retrieve such data from message->raw after calling
9440 * isds_get_signed_delivery_info().
9441 * @length is length of buffer in bytes.
9442 * @message is automatically reallocated message parsed from @buffer.
9443 * @strategy selects how buffer will be attached into raw isds_message member.
9444 * */
9445 isds_error isds_load_delivery_info(struct isds_ctx *context,
9446 const isds_raw_type raw_type,
9447 const void *buffer, const size_t length,
9448 struct isds_message **message, const isds_buffer_strategy strategy) {
9450 isds_error err = IE_SUCCESS;
9451 message_ns_type message_ns;
9452 xmlDocPtr message_doc = NULL;
9453 xmlXPathContextPtr xpath_ctx = NULL;
9454 xmlXPathObjectPtr result = NULL;
9455 void *xml_stream = NULL;
9456 size_t xml_stream_length = 0;
9458 if (!context) return IE_INVALID_CONTEXT;
9459 zfree(context->long_message);
9460 if (!message) return IE_INVAL;
9461 isds_message_free(message);
9462 if (!buffer) return IE_INVAL;
9465 /* Select buffer format and extract XML from CMS*/
9466 switch (raw_type) {
9467 case RAWTYPE_DELIVERYINFO:
9468 message_ns = MESSAGE_NS_UNSIGNED;
9469 xml_stream = (void *) buffer;
9470 xml_stream_length = length;
9471 break;
9473 case RAWTYPE_PLAIN_SIGNED_DELIVERYINFO:
9474 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
9475 xml_stream = (void *) buffer;
9476 xml_stream_length = length;
9477 break;
9479 case RAWTYPE_CMS_SIGNED_DELIVERYINFO:
9480 message_ns = MESSAGE_NS_SIGNED_DELIVERY;
9481 err = _isds_extract_cms_data(context, buffer, length,
9482 &xml_stream, &xml_stream_length);
9483 if (err) goto leave;
9484 break;
9486 default:
9487 isds_log_message(context, _("Bad raw delivery representation type"));
9488 return IE_INVAL;
9489 break;
9492 isds_log(ILF_ISDS, ILL_DEBUG,
9493 _("Delivery info content:\n%.*s\nEnd of delivery info\n"),
9494 xml_stream_length, xml_stream);
9496 /* Convert delivery info XML stream into XPath context */
9497 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
9498 if (!message_doc) {
9499 err = IE_XML;
9500 goto leave;
9502 xpath_ctx = xmlXPathNewContext(message_doc);
9503 if (!xpath_ctx) {
9504 err = IE_ERROR;
9505 goto leave;
9507 /* XXX: Name spaces mangled for signed delivery info:
9508 * http://isds.czechpoint.cz/v20/delivery:
9510 * <q:GetDeliveryInfoResponse xmlns:q="http://isds.czechpoint.cz/v20/delivery">
9511 * <q:dmDelivery>
9512 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
9513 * <p:dmID>170272</p:dmID>
9514 * ...
9515 * </p:dmDm>
9516 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
9517 * ...
9518 * </q:dmEvents>...</q:dmEvents>
9519 * </q:dmDelivery>
9520 * </q:GetDeliveryInfoResponse>
9521 * */
9522 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
9523 err = IE_ERROR;
9524 goto leave;
9526 result = xmlXPathEvalExpression(
9527 BAD_CAST "/sisds:GetDeliveryInfoResponse/sisds:dmDelivery",
9528 xpath_ctx);
9529 if (!result) {
9530 err = IE_ERROR;
9531 goto leave;
9533 /* Empty delivery info */
9534 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9535 isds_printf_message(context,
9536 _("XML document is not sisds:dmDelivery document"));
9537 err = IE_ISDS;
9538 goto leave;
9540 /* More delivery info's */
9541 if (result->nodesetval->nodeNr > 1) {
9542 isds_printf_message(context,
9543 _("XML document has more sisds:dmDelivery elements"));
9544 err = IE_ISDS;
9545 goto leave;
9547 /* One delivery info */
9548 xpath_ctx->node = result->nodesetval->nodeTab[0];
9550 /* Extract the envelope (= message without documents, hence 0).
9551 * XXX: extract_TReturnedMessage() can obtain attachments size,
9552 * but delivery info carries none. It's coded as option elements,
9553 * so it should work. */
9554 err = extract_TReturnedMessage(context, 0, message, xpath_ctx);
9555 if (err) goto leave;
9557 /* Extract events */
9558 err = move_xpathctx_to_child(context, BAD_CAST "sisds:dmEvents", xpath_ctx);
9559 if (err == IE_NOEXIST || err == IE_NOTUNIQ) { err = IE_ISDS; goto leave; }
9560 if (err) { err = IE_ERROR; goto leave; }
9561 err = extract_events(context, &(*message)->envelope->events, xpath_ctx);
9562 if (err) goto leave;
9564 /* Append raw CMS structure into message */
9565 (*message)->raw_type = raw_type;
9566 switch (strategy) {
9567 case BUFFER_DONT_STORE:
9568 break;
9569 case BUFFER_COPY:
9570 (*message)->raw = malloc(length);
9571 if (!(*message)->raw) {
9572 err = IE_NOMEM;
9573 goto leave;
9575 memcpy((*message)->raw, buffer, length);
9576 (*message)->raw_length = length;
9577 break;
9578 case BUFFER_MOVE:
9579 (*message)->raw = (void *) buffer;
9580 (*message)->raw_length = length;
9581 break;
9582 default:
9583 err = IE_ENUM;
9584 goto leave;
9587 leave:
9588 if (err) {
9589 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
9590 isds_message_free(message);
9593 xmlXPathFreeObject(result);
9594 xmlXPathFreeContext(xpath_ctx);
9595 if (!*message || !(*message)->xml) {
9596 xmlFreeDoc(message_doc);
9598 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
9600 if (!err)
9601 isds_log(ILF_ISDS, ILL_DEBUG,
9602 _("Delivery info loaded successfully.\n"));
9603 return err;
9607 /* Download signed delivery info-sheet of given message identified by ID.
9608 * @context is session context
9609 * @message_id is message identifier (you can get them from
9610 * isds_get_list_of_{sent,received}_messages())
9611 * @message is automatically reallocated message retrieved from ISDS.
9612 * It will miss documents per se. Use isds_get_signed_received_message(),
9613 * if you are interested in documents (content). OTOH, only this function
9614 * can get list events message has gone through. */
9615 isds_error isds_get_signed_delivery_info(struct isds_ctx *context,
9616 const char *message_id, struct isds_message **message) {
9618 isds_error err = IE_SUCCESS;
9619 #if HAVE_LIBCURL
9620 xmlDocPtr response = NULL;
9621 xmlChar *code = NULL, *status_message = NULL;
9622 void *raw = NULL;
9623 size_t raw_length = 0;
9624 #endif
9626 if (!context) return IE_INVALID_CONTEXT;
9627 zfree(context->long_message);
9629 /* Free former message if any */
9630 if (!message) return IE_INVAL;
9631 isds_message_free(message);
9633 #if HAVE_LIBCURL
9634 /* Do request and check for success */
9635 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9636 BAD_CAST "GetSignedDeliveryInfo", message_id,
9637 &response, NULL, NULL, &code, &status_message);
9638 if (err) goto leave;
9640 /* Find signed delivery info, extract it into raw and maybe free
9641 * response */
9642 err = find_extract_signed_data_free_response(context,
9643 (xmlChar *)message_id, &response,
9644 BAD_CAST "GetSignedDeliveryInfo", &raw, &raw_length);
9645 if (err) goto leave;
9647 /* Parse delivery info */
9648 err = isds_load_delivery_info(context,
9649 RAWTYPE_CMS_SIGNED_DELIVERYINFO, raw, raw_length,
9650 message, BUFFER_MOVE);
9651 if (err) goto leave;
9653 raw = NULL;
9655 leave:
9656 if (err) {
9657 isds_message_free(message);
9660 free(raw);
9661 free(code);
9662 free(status_message);
9663 xmlFreeDoc(response);
9665 if (!err)
9666 isds_log(ILF_ISDS, ILL_DEBUG,
9667 _("GetSignedDeliveryInfo request processed by server "
9668 "successfully.\n")
9670 #else /* not HAVE_LIBCURL */
9671 err = IE_NOTSUP;
9672 #endif
9673 return err;
9677 /* Download delivery info-sheet of given message identified by ID.
9678 * @context is session context
9679 * @message_id is message identifier (you can get them from
9680 * isds_get_list_of_{sent,received}_messages())
9681 * @message is automatically reallocated message retrieved from ISDS.
9682 * It will miss documents per se. Use isds_get_received_message(), if you are
9683 * interested in documents (content). OTOH, only this function can get list
9684 * of events message has gone through. */
9685 isds_error isds_get_delivery_info(struct isds_ctx *context,
9686 const char *message_id, struct isds_message **message) {
9688 isds_error err = IE_SUCCESS;
9689 #if HAVE_LIBCURL
9690 xmlDocPtr response = NULL;
9691 xmlChar *code = NULL, *status_message = NULL;
9692 xmlNodePtr delivery_node = NULL;
9693 void *raw = NULL;
9694 size_t raw_length = 0;
9695 #endif
9697 if (!context) return IE_INVALID_CONTEXT;
9698 zfree(context->long_message);
9700 /* Free former message if any */
9701 if (!message) return IE_INVAL;
9702 isds_message_free(message);
9704 #if HAVE_LIBCURL
9705 /* Do request and check for success */
9706 err = build_send_check_message_request(context, SERVICE_DM_INFO,
9707 BAD_CAST "GetDeliveryInfo", message_id,
9708 &response, NULL, NULL, &code, &status_message);
9709 if (err) goto leave;
9712 /* Serialize delivery info */
9713 delivery_node = xmlDocGetRootElement(response);
9714 if (!delivery_node) {
9715 char *message_id_locale = _isds_utf82locale((char*) message_id);
9716 isds_printf_message(context,
9717 _("Server did not return any delivery info for ID `%s' "
9718 "on GetDeliveryInfo request"), message_id_locale);
9719 free(message_id_locale);
9720 err = IE_ISDS;
9721 goto leave;
9723 err = serialize_subtree(context, delivery_node, &raw, &raw_length);
9724 if (err) goto leave;
9726 /* Parse delivery info */
9727 /* TODO: Here we parse the response second time. We could single delivery
9728 * parser from isds_load_delivery_info() to make things faster. */
9729 err = isds_load_delivery_info(context,
9730 RAWTYPE_DELIVERYINFO, raw, raw_length,
9731 message, BUFFER_MOVE);
9732 if (err) goto leave;
9734 raw = NULL;
9737 leave:
9738 if (err) {
9739 isds_message_free(message);
9742 free(raw);
9743 free(code);
9744 free(status_message);
9745 xmlFreeDoc(response);
9747 if (!err)
9748 isds_log(ILF_ISDS, ILL_DEBUG,
9749 _("GetDeliveryInfo request processed by server "
9750 "successfully.\n")
9752 #else /* not HAVE_LIBCURL */
9753 err = IE_NOTSUP;
9754 #endif
9755 return err;
9759 /* Download incoming message identified by ID.
9760 * @context is session context
9761 * @message_id is message identifier (you can get them from
9762 * isds_get_list_of_received_messages())
9763 * @message is automatically reallocated message retrieved from ISDS */
9764 isds_error isds_get_received_message(struct isds_ctx *context,
9765 const char *message_id, struct isds_message **message) {
9767 isds_error err = IE_SUCCESS;
9768 #if HAVE_LIBCURL
9769 xmlDocPtr response = NULL;
9770 void *xml_stream = NULL;
9771 size_t xml_stream_length;
9772 xmlChar *code = NULL, *status_message = NULL;
9773 xmlXPathContextPtr xpath_ctx = NULL;
9774 xmlXPathObjectPtr result = NULL;
9775 char *phys_path = NULL;
9776 size_t phys_start, phys_end;
9777 #endif
9779 if (!context) return IE_INVALID_CONTEXT;
9780 zfree(context->long_message);
9782 /* Free former message if any */
9783 if (NULL == message) return IE_INVAL;
9784 if (message) isds_message_free(message);
9786 #if HAVE_LIBCURL
9787 /* Do request and check for success */
9788 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
9789 BAD_CAST "MessageDownload", message_id,
9790 &response, &xml_stream, &xml_stream_length,
9791 &code, &status_message);
9792 if (err) goto leave;
9794 /* Extract data */
9795 xpath_ctx = xmlXPathNewContext(response);
9796 if (!xpath_ctx) {
9797 err = IE_ERROR;
9798 goto leave;
9800 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
9801 err = IE_ERROR;
9802 goto leave;
9804 result = xmlXPathEvalExpression(
9805 BAD_CAST "/isds:MessageDownloadResponse/isds:dmReturnedMessage",
9806 xpath_ctx);
9807 if (!result) {
9808 err = IE_ERROR;
9809 goto leave;
9811 /* Empty response */
9812 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
9813 char *message_id_locale = _isds_utf82locale((char*) message_id);
9814 isds_printf_message(context,
9815 _("Server did not return any message for ID `%s' "
9816 "on MessageDownload request"), message_id_locale);
9817 free(message_id_locale);
9818 err = IE_ISDS;
9819 goto leave;
9821 /* More messages */
9822 if (result->nodesetval->nodeNr > 1) {
9823 char *message_id_locale = _isds_utf82locale((char*) message_id);
9824 isds_printf_message(context,
9825 _("Server did return more messages for ID `%s' "
9826 "on MessageDownload request"), message_id_locale);
9827 free(message_id_locale);
9828 err = IE_ISDS;
9829 goto leave;
9831 /* One message */
9832 xpath_ctx->node = result->nodesetval->nodeTab[0];
9834 /* Extract the message */
9835 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
9836 if (err) goto leave;
9838 /* Locate raw XML blob */
9839 phys_path = strdup(
9840 SOAP_NS PHYSXML_NS_SEPARATOR "Envelope"
9841 PHYSXML_ELEMENT_SEPARATOR
9842 SOAP_NS PHYSXML_NS_SEPARATOR "Body"
9843 PHYSXML_ELEMENT_SEPARATOR
9844 ISDS_NS PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
9846 if (!phys_path) {
9847 err = IE_NOMEM;
9848 goto leave;
9850 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
9851 phys_path, &phys_start, &phys_end);
9852 zfree(phys_path);
9853 if (err) {
9854 isds_log_message(context,
9855 _("Substring with isds:MessageDownloadResponse element "
9856 "could not be located in raw SOAP message"));
9857 goto leave;
9859 /* Save XML blob */
9860 /*err = serialize_subtree(context, xpath_ctx->node, &(*message)->raw,
9861 &(*message)->raw_length);*/
9862 /* TODO: Store name space declarations from ancestors */
9863 /* TODO: Handle non-UTF-8 encoding (XML prologue) */
9864 (*message)->raw_type = RAWTYPE_INCOMING_MESSAGE;
9865 (*message)->raw_length = phys_end - phys_start + 1;
9866 (*message)->raw = malloc((*message)->raw_length);
9867 if (!(*message)->raw) {
9868 err = IE_NOMEM;
9869 goto leave;
9871 memcpy((*message)->raw, xml_stream + phys_start, (*message)->raw_length);
9874 leave:
9875 if (err) {
9876 isds_message_free(message);
9879 free(phys_path);
9881 xmlXPathFreeObject(result);
9882 xmlXPathFreeContext(xpath_ctx);
9884 free(code);
9885 free(status_message);
9886 free(xml_stream);
9887 if (!*message || !(*message)->xml) {
9888 xmlFreeDoc(response);
9891 if (!err)
9892 isds_log(ILF_ISDS, ILL_DEBUG,
9893 _("MessageDownload request processed by server "
9894 "successfully.\n")
9896 #else /* not HAVE_LIBCURL */
9897 err = IE_NOTSUP;
9898 #endif
9899 return err;
9903 /* Load message of any type from buffer.
9904 * @context is session context
9905 * @raw_type defines content type of @buffer. Only message types are allowed.
9906 * @buffer is message raw representation. Format (CMS, plain signed,
9907 * message direction) is defined in @raw_type. You can retrieve such data
9908 * from message->raw after calling isds_get_[signed]{received,sent}_message().
9909 * @length is length of buffer in bytes.
9910 * @message is automatically reallocated message parsed from @buffer.
9911 * @strategy selects how buffer will be attached into raw isds_message member.
9912 * */
9913 isds_error isds_load_message(struct isds_ctx *context,
9914 const isds_raw_type raw_type, const void *buffer, const size_t length,
9915 struct isds_message **message, const isds_buffer_strategy strategy) {
9917 isds_error err = IE_SUCCESS;
9918 void *xml_stream = NULL;
9919 size_t xml_stream_length = 0;
9920 message_ns_type message_ns;
9921 xmlDocPtr message_doc = NULL;
9922 xmlXPathContextPtr xpath_ctx = NULL;
9923 xmlXPathObjectPtr result = NULL;
9925 if (!context) return IE_INVALID_CONTEXT;
9926 zfree(context->long_message);
9927 if (!message) return IE_INVAL;
9928 isds_message_free(message);
9929 if (!buffer) return IE_INVAL;
9932 /* Select buffer format and extract XML from CMS*/
9933 switch (raw_type) {
9934 case RAWTYPE_INCOMING_MESSAGE:
9935 message_ns = MESSAGE_NS_UNSIGNED;
9936 xml_stream = (void *) buffer;
9937 xml_stream_length = length;
9938 break;
9940 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
9941 message_ns = MESSAGE_NS_SIGNED_INCOMING;
9942 xml_stream = (void *) buffer;
9943 xml_stream_length = length;
9944 break;
9946 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
9947 message_ns = MESSAGE_NS_SIGNED_INCOMING;
9948 err = _isds_extract_cms_data(context, buffer, length,
9949 &xml_stream, &xml_stream_length);
9950 if (err) goto leave;
9951 break;
9953 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
9954 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
9955 xml_stream = (void *) buffer;
9956 xml_stream_length = length;
9957 break;
9959 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
9960 message_ns = MESSAGE_NS_SIGNED_OUTGOING;
9961 err = _isds_extract_cms_data(context, buffer, length,
9962 &xml_stream, &xml_stream_length);
9963 if (err) goto leave;
9964 break;
9966 default:
9967 isds_log_message(context, _("Bad raw message representation type"));
9968 return IE_INVAL;
9969 break;
9972 isds_log(ILF_ISDS, ILL_DEBUG,
9973 _("Loading message:\n%.*s\nEnd of message\n"),
9974 xml_stream_length, xml_stream);
9976 /* Convert messages XML stream into XPath context */
9977 message_doc = xmlParseMemory(xml_stream, xml_stream_length);
9978 if (!message_doc) {
9979 err = IE_XML;
9980 goto leave;
9982 xpath_ctx = xmlXPathNewContext(message_doc);
9983 if (!xpath_ctx) {
9984 err = IE_ERROR;
9985 goto leave;
9987 /* XXX: Standard name space for unsigned incoming direction:
9988 * http://isds.czechpoint.cz/v20/
9990 * XXX: Name spaces mangled for signed outgoing direction:
9991 * http://isds.czechpoint.cz/v20/SentMessage:
9993 * <q:MessageDownloadResponse
9994 * xmlns:q="http://isds.czechpoint.cz/v20/SentMessage">
9995 * <q:dmReturnedMessage>
9996 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
9997 * <p:dmID>151916</p:dmID>
9998 * ...
9999 * </p:dmDm>
10000 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10001 * ...
10002 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10003 * </q:dmReturnedMessage>
10004 * </q:MessageDownloadResponse>
10006 * XXX: Name spaces mangled for signed incoming direction:
10007 * http://isds.czechpoint.cz/v20/message:
10009 * <q:MessageDownloadResponse
10010 * xmlns:q="http://isds.czechpoint.cz/v20/message">
10011 * <q:dmReturnedMessage>
10012 * <p:dmDm xmlns:p="http://isds.czechpoint.cz/v20">
10013 * <p:dmID>151916</p:dmID>
10014 * ...
10015 * </p:dmDm>
10016 * <q:dmHash algorithm="SHA-1">...</q:dmHash>
10017 * ...
10018 * <q:dmAttachmentSize>260</q:dmAttachmentSize>
10019 * </q:dmReturnedMessage>
10020 * </q:MessageDownloadResponse>
10022 * Stupidity of ISDS developers is unlimited */
10023 if (_isds_register_namespaces(xpath_ctx, message_ns)) {
10024 err = IE_ERROR;
10025 goto leave;
10027 result = xmlXPathEvalExpression(
10028 BAD_CAST "/sisds:MessageDownloadResponse/sisds:dmReturnedMessage",
10029 xpath_ctx);
10030 if (!result) {
10031 err = IE_ERROR;
10032 goto leave;
10034 /* Empty message */
10035 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10036 isds_printf_message(context,
10037 _("XML document does not contain "
10038 "sisds:dmReturnedMessage element"));
10039 err = IE_ISDS;
10040 goto leave;
10042 /* More messages */
10043 if (result->nodesetval->nodeNr > 1) {
10044 isds_printf_message(context,
10045 _("XML document has more sisds:dmReturnedMessage elements"));
10046 err = IE_ISDS;
10047 goto leave;
10049 /* One message */
10050 xpath_ctx->node = result->nodesetval->nodeTab[0];
10052 /* Extract the message */
10053 err = extract_TReturnedMessage(context, 1, message, xpath_ctx);
10054 if (err) goto leave;
10056 /* Append raw buffer into message */
10057 (*message)->raw_type = raw_type;
10058 switch (strategy) {
10059 case BUFFER_DONT_STORE:
10060 break;
10061 case BUFFER_COPY:
10062 (*message)->raw = malloc(length);
10063 if (!(*message)->raw) {
10064 err = IE_NOMEM;
10065 goto leave;
10067 memcpy((*message)->raw, buffer, length);
10068 (*message)->raw_length = length;
10069 break;
10070 case BUFFER_MOVE:
10071 (*message)->raw = (void *) buffer;
10072 (*message)->raw_length = length;
10073 break;
10074 default:
10075 err = IE_ENUM;
10076 goto leave;
10080 leave:
10081 if (err) {
10082 if (*message && strategy == BUFFER_MOVE) (*message)->raw = NULL;
10083 isds_message_free(message);
10086 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10087 xmlXPathFreeObject(result);
10088 xmlXPathFreeContext(xpath_ctx);
10089 if (!*message || !(*message)->xml) {
10090 xmlFreeDoc(message_doc);
10093 if (!err)
10094 isds_log(ILF_ISDS, ILL_DEBUG, _("Message loaded successfully.\n"));
10095 return err;
10099 /* Determine type of raw message or delivery info according some heuristics.
10100 * It does not validate the raw blob.
10101 * @context is session context
10102 * @raw_type returns content type of @buffer. Valid only if exit code of this
10103 * function is IE_SUCCESS. The pointer must be valid. This is no automatically
10104 * reallocated memory.
10105 * @buffer is message raw representation.
10106 * @length is length of buffer in bytes. */
10107 isds_error isds_guess_raw_type(struct isds_ctx *context,
10108 isds_raw_type *raw_type, const void *buffer, const size_t length) {
10109 isds_error err;
10110 void *xml_stream = NULL;
10111 size_t xml_stream_length = 0;
10112 xmlDocPtr document = NULL;
10113 xmlNodePtr root = NULL;
10115 if (!context) return IE_INVALID_CONTEXT;
10116 zfree(context->long_message);
10117 if (length == 0 || !buffer) return IE_INVAL;
10118 if (!raw_type) return IE_INVAL;
10120 /* Try CMS */
10121 err = _isds_extract_cms_data(context, buffer, length,
10122 &xml_stream, &xml_stream_length);
10123 if (err) {
10124 xml_stream = (void *) buffer;
10125 xml_stream_length = (size_t) length;
10126 err = IE_SUCCESS;
10129 /* Try XML */
10130 document = xmlParseMemory(xml_stream, xml_stream_length);
10131 if (!document) {
10132 isds_printf_message(context,
10133 _("Could not parse data as XML document"));
10134 err = IE_NOTSUP;
10135 goto leave;
10138 /* Get root element */
10139 root = xmlDocGetRootElement(document);
10140 if (!root) {
10141 isds_printf_message(context,
10142 _("XML document is missing root element"));
10143 err = IE_XML;
10144 goto leave;
10147 if (!root->ns || !root->ns->href) {
10148 isds_printf_message(context,
10149 _("Root element does not belong to any name space"));
10150 err = IE_NOTSUP;
10151 goto leave;
10154 /* Test name space */
10155 if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_INCOMING_NS)) {
10156 if (xml_stream == buffer)
10157 *raw_type = RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE;
10158 else
10159 *raw_type = RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE;
10160 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_OUTGOING_NS)) {
10161 if (xml_stream == buffer)
10162 *raw_type = RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE;
10163 else
10164 *raw_type = RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE;
10165 } else if (!xmlStrcmp(root->ns->href, BAD_CAST SISDS_DELIVERY_NS)) {
10166 if (xml_stream == buffer)
10167 *raw_type = RAWTYPE_PLAIN_SIGNED_DELIVERYINFO;
10168 else
10169 *raw_type = RAWTYPE_CMS_SIGNED_DELIVERYINFO;
10170 } else if (!xmlStrcmp(root->ns->href, BAD_CAST ISDS_NS)) {
10171 if (xml_stream != buffer) {
10172 isds_printf_message(context,
10173 _("Document in ISDS name space is encapsulated into CMS" ));
10174 err = IE_NOTSUP;
10175 } else if (!xmlStrcmp(root->name, BAD_CAST "MessageDownloadResponse"))
10176 *raw_type = RAWTYPE_INCOMING_MESSAGE;
10177 else if (!xmlStrcmp(root->name, BAD_CAST "GetDeliveryInfoResponse"))
10178 *raw_type = RAWTYPE_DELIVERYINFO;
10179 else {
10180 isds_printf_message(context,
10181 _("Unknown root element in ISDS name space"));
10182 err = IE_NOTSUP;
10184 } else {
10185 isds_printf_message(context,
10186 _("Unknown name space"));
10187 err = IE_NOTSUP;
10190 leave:
10191 if (xml_stream != buffer) _isds_cms_data_free(xml_stream);
10192 xmlFreeDoc(document);
10193 return err;
10197 /* Download signed incoming/outgoing message identified by ID.
10198 * @context is session context
10199 * @output is true for outgoing message, false for incoming message
10200 * @message_id is message identifier (you can get them from
10201 * isds_get_list_of_{sent,received}_messages())
10202 * @message is automatically reallocated message retrieved from ISDS. The raw
10203 * member will be filled with PKCS#7 structure in DER format. */
10204 static isds_error isds_get_signed_message(struct isds_ctx *context,
10205 const _Bool outgoing, const char *message_id,
10206 struct isds_message **message) {
10208 isds_error err = IE_SUCCESS;
10209 #if HAVE_LIBCURL
10210 xmlDocPtr response = NULL;
10211 xmlChar *code = NULL, *status_message = NULL;
10212 xmlXPathContextPtr xpath_ctx = NULL;
10213 xmlXPathObjectPtr result = NULL;
10214 char *encoded_structure = NULL;
10215 void *raw = NULL;
10216 size_t raw_length = 0;
10217 #endif
10219 if (!context) return IE_INVALID_CONTEXT;
10220 zfree(context->long_message);
10221 if (!message) return IE_INVAL;
10222 isds_message_free(message);
10224 #if HAVE_LIBCURL
10225 /* Do request and check for success */
10226 err = build_send_check_message_request(context, SERVICE_DM_OPERATIONS,
10227 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10228 BAD_CAST "SignedMessageDownload",
10229 message_id, &response, NULL, NULL, &code, &status_message);
10230 if (err) goto leave;
10232 /* Find signed message, extract it into raw and maybe free
10233 * response */
10234 err = find_extract_signed_data_free_response(context,
10235 (xmlChar *)message_id, &response,
10236 (outgoing) ? BAD_CAST "SignedSentMessageDownload" :
10237 BAD_CAST "SignedMessageDownload",
10238 &raw, &raw_length);
10239 if (err) goto leave;
10241 /* Parse message */
10242 err = isds_load_message(context,
10243 (outgoing) ? RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE :
10244 RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE,
10245 raw, raw_length, message, BUFFER_MOVE);
10246 if (err) goto leave;
10248 raw = NULL;
10250 leave:
10251 if (err) {
10252 isds_message_free(message);
10255 free(encoded_structure);
10256 xmlXPathFreeObject(result);
10257 xmlXPathFreeContext(xpath_ctx);
10258 free(raw);
10260 free(code);
10261 free(status_message);
10262 xmlFreeDoc(response);
10264 if (!err)
10265 isds_log(ILF_ISDS, ILL_DEBUG,
10266 (outgoing) ?
10267 _("SignedSentMessageDownload request processed by server "
10268 "successfully.\n") :
10269 _("SignedMessageDownload request processed by server "
10270 "successfully.\n")
10272 #else /* not HAVE_LIBCURL */
10273 err = IE_NOTSUP;
10274 #endif
10275 return err;
10279 /* Download signed incoming message identified by ID.
10280 * @context is session context
10281 * @message_id is message identifier (you can get them from
10282 * isds_get_list_of_received_messages())
10283 * @message is automatically reallocated message retrieved from ISDS. The raw
10284 * member will be filled with PKCS#7 structure in DER format. */
10285 isds_error isds_get_signed_received_message(struct isds_ctx *context,
10286 const char *message_id, struct isds_message **message) {
10287 return isds_get_signed_message(context, 0, message_id, message);
10291 /* Download signed outgoing message identified by ID.
10292 * @context is session context
10293 * @message_id is message identifier (you can get them from
10294 * isds_get_list_of_sent_messages())
10295 * @message is automatically reallocated message retrieved from ISDS. The raw
10296 * member will be filled with PKCS#7 structure in DER format. */
10297 isds_error isds_get_signed_sent_message(struct isds_ctx *context,
10298 const char *message_id, struct isds_message **message) {
10299 return isds_get_signed_message(context, 1, message_id, message);
10303 /* Get type and name of user who sent a message identified by ID.
10304 * @context is session context
10305 * @message_id is message identifier
10306 * @sender_type is pointer to automatically allocated type of sender detected
10307 * from @raw_sender_type string. If @raw_sender_type is unknown to this
10308 * library or to the server, NULL will be returned. Pass NULL if you don't
10309 * care about it.
10310 * @raw_sender_type is automatically reallocated UTF-8 string describing
10311 * sender type or NULL if not known to server. Pass NULL if you don't care.
10312 * @sender_name is automatically reallocated UTF-8 name of user who sent the
10313 * message, or NULL if not known to ISDS. Pass NULL if you don't care. */
10314 isds_error isds_get_message_sender(struct isds_ctx *context,
10315 const char *message_id, isds_sender_type **sender_type,
10316 char **raw_sender_type, char **sender_name) {
10317 isds_error err = IE_SUCCESS;
10318 #if HAVE_LIBCURL
10319 xmlDocPtr response = NULL;
10320 xmlChar *code = NULL, *status_message = NULL;
10321 xmlXPathContextPtr xpath_ctx = NULL;
10322 xmlXPathObjectPtr result = NULL;
10323 char *type_string = NULL;
10324 #endif
10326 if (!context) return IE_INVALID_CONTEXT;
10327 zfree(context->long_message);
10328 if (sender_type) zfree(*sender_type);
10329 if (raw_sender_type) zfree(*raw_sender_type);
10330 if (sender_name) zfree(*sender_name);
10331 if (!message_id) return IE_INVAL;
10333 #if HAVE_LIBCURL
10334 /* Do request and check for success */
10335 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10336 BAD_CAST "GetMessageAuthor",
10337 message_id, &response, NULL, NULL, &code, &status_message);
10338 if (err) goto leave;
10340 /* Extract data */
10341 xpath_ctx = xmlXPathNewContext(response);
10342 if (!xpath_ctx) {
10343 err = IE_ERROR;
10344 goto leave;
10346 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10347 err = IE_ERROR;
10348 goto leave;
10350 result = xmlXPathEvalExpression(
10351 BAD_CAST "/isds:GetMessageAuthorResponse", xpath_ctx);
10352 if (!result) {
10353 err = IE_ERROR;
10354 goto leave;
10356 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10357 isds_log_message(context,
10358 _("Missing GetMessageAuthorResponse element"));
10359 err = IE_ISDS;
10360 goto leave;
10362 if (result->nodesetval->nodeNr > 1) {
10363 isds_log_message(context,
10364 _("Multiple GetMessageAuthorResponse element"));
10365 err = IE_ISDS;
10366 goto leave;
10368 xpath_ctx->node = result->nodesetval->nodeTab[0];
10369 xmlXPathFreeObject(result); result = NULL;
10371 /* Fill output arguments in */
10372 EXTRACT_STRING("isds:userType", type_string);
10373 if (NULL != type_string) {
10374 if (NULL != sender_type) {
10375 *sender_type = calloc(1, sizeof(**sender_type));
10376 if (NULL == *sender_type) {
10377 err = IE_NOMEM;
10378 goto leave;
10381 err = string2isds_sender_type((xmlChar *)type_string,
10382 *sender_type);
10383 if (err) {
10384 zfree(*sender_type);
10385 if (err == IE_ENUM) {
10386 err = IE_SUCCESS;
10387 char *type_string_locale = _isds_utf82locale(type_string);
10388 isds_log(ILF_ISDS, ILL_WARNING,
10389 _("Unknown isds:userType value: %s"),
10390 type_string_locale);
10391 free(type_string_locale);
10396 if (NULL != sender_name)
10397 EXTRACT_STRING("isds:authorName", *sender_name);
10399 leave:
10400 if (err) {
10401 if (NULL != sender_type) zfree(*sender_type);
10402 zfree(type_string);
10403 if (NULL != sender_name) zfree(*sender_name);
10405 if (NULL != raw_sender_type) *raw_sender_type = type_string;
10407 xmlXPathFreeObject(result);
10408 xmlXPathFreeContext(xpath_ctx);
10410 free(code);
10411 free(status_message);
10412 xmlFreeDoc(response);
10414 if (!err)
10415 isds_log(ILF_ISDS, ILL_DEBUG,
10416 _("GetMessageAuthor request processed by server "
10417 "successfully.\n"));
10418 #else /* not HAVE_LIBCURL */
10419 err = IE_NOTSUP;
10420 #endif
10421 return err;
10425 /* Retrieve hash of message identified by ID stored in ISDS.
10426 * @context is session context
10427 * @message_id is message identifier
10428 * @hash is automatically reallocated message hash downloaded from ISDS.
10429 * Message must exist in system and must not be deleted. */
10430 isds_error isds_download_message_hash(struct isds_ctx *context,
10431 const char *message_id, struct isds_hash **hash) {
10433 isds_error err = IE_SUCCESS;
10434 #if HAVE_LIBCURL
10435 xmlDocPtr response = NULL;
10436 xmlChar *code = NULL, *status_message = NULL;
10437 xmlXPathContextPtr xpath_ctx = NULL;
10438 xmlXPathObjectPtr result = NULL;
10439 #endif
10441 if (!context) return IE_INVALID_CONTEXT;
10442 zfree(context->long_message);
10444 isds_hash_free(hash);
10446 #if HAVE_LIBCURL
10447 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10448 BAD_CAST "VerifyMessage", message_id,
10449 &response, NULL, NULL, &code, &status_message);
10450 if (err) goto leave;
10453 /* Extract data */
10454 xpath_ctx = xmlXPathNewContext(response);
10455 if (!xpath_ctx) {
10456 err = IE_ERROR;
10457 goto leave;
10459 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10460 err = IE_ERROR;
10461 goto leave;
10463 result = xmlXPathEvalExpression(
10464 BAD_CAST "/isds:VerifyMessageResponse",
10465 xpath_ctx);
10466 if (!result) {
10467 err = IE_ERROR;
10468 goto leave;
10470 /* Empty response */
10471 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10472 char *message_id_locale = _isds_utf82locale((char*) message_id);
10473 isds_printf_message(context,
10474 _("Server did not return any response for ID `%s' "
10475 "on VerifyMessage request"), message_id_locale);
10476 free(message_id_locale);
10477 err = IE_ISDS;
10478 goto leave;
10480 /* More responses */
10481 if (result->nodesetval->nodeNr > 1) {
10482 char *message_id_locale = _isds_utf82locale((char*) message_id);
10483 isds_printf_message(context,
10484 _("Server did return more responses for ID `%s' "
10485 "on VerifyMessage request"), message_id_locale);
10486 free(message_id_locale);
10487 err = IE_ISDS;
10488 goto leave;
10490 /* One response */
10491 xpath_ctx->node = result->nodesetval->nodeTab[0];
10493 /* Extract the hash */
10494 err = find_and_extract_DmHash(context, hash, xpath_ctx);
10496 leave:
10497 if (err) {
10498 isds_hash_free(hash);
10501 xmlXPathFreeObject(result);
10502 xmlXPathFreeContext(xpath_ctx);
10504 free(code);
10505 free(status_message);
10506 xmlFreeDoc(response);
10508 if (!err)
10509 isds_log(ILF_ISDS, ILL_DEBUG,
10510 _("VerifyMessage request processed by server "
10511 "successfully.\n")
10513 #else /* not HAVE_LIBCURL */
10514 err = IE_NOTSUP;
10515 #endif
10516 return err;
10520 /* Erase message specified by @message_id from long term storage. Other
10521 * message cannot be erased on user request.
10522 * @context is session context
10523 * @message_id is message identifier.
10524 * @incoming is true for incoming message, false for outgoing message.
10525 * @return
10526 * IE_SUCCESS if message has ben removed
10527 * IE_INVAL if message does not exist in long term storage or message
10528 * belongs to different box
10529 * TODO: IE_NOEPRM if user has no permission to erase a message */
10530 isds_error isds_delete_message_from_storage(struct isds_ctx *context,
10531 const char *message_id, _Bool incoming) {
10532 isds_error err = IE_SUCCESS;
10533 #if HAVE_LIBCURL
10534 xmlNodePtr request = NULL, node;
10535 xmlNsPtr isds_ns = NULL;
10536 xmlDocPtr response = NULL;
10537 xmlChar *code = NULL, *status_message = NULL;
10538 #endif
10540 if (!context) return IE_INVALID_CONTEXT;
10541 zfree(context->long_message);
10542 if (NULL == message_id) return IE_INVAL;
10544 /* Check if connection is established
10545 * TODO: This check should be done downstairs. */
10546 if (!context->curl) return IE_CONNECTION_CLOSED;
10548 #if HAVE_LIBCURL
10549 /* Build request */
10550 request = xmlNewNode(NULL, BAD_CAST "EraseMessage");
10551 if (!request) {
10552 isds_log_message(context,
10553 _("Could build EraseMessage request"));
10554 return IE_ERROR;
10556 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
10557 if(!isds_ns) {
10558 isds_log_message(context, _("Could not create ISDS name space"));
10559 xmlFreeNode(request);
10560 return IE_ERROR;
10562 xmlSetNs(request, isds_ns);
10564 err = validate_message_id_length(context, (xmlChar *) message_id);
10565 if (err) goto leave;
10566 INSERT_STRING(request, "dmID", message_id);
10568 INSERT_SCALAR_BOOLEAN(request, "dmIncoming", incoming);
10571 /* Send request */
10572 isds_log(ILF_ISDS, ILL_DEBUG, _("Sending EraseMessage request for "
10573 "message ID %s to ISDS\n"), message_id);
10574 err = _isds(context, SERVICE_DM_INFO, request, &response, NULL, NULL);
10575 xmlFreeNode(request); request = NULL;
10577 if (err) {
10578 isds_log(ILF_ISDS, ILL_DEBUG,
10579 _("Processing ISDS response on EraseMessage request "
10580 "failed\n"));
10581 goto leave;
10584 /* Check for response status */
10585 err = isds_response_status(context, SERVICE_DM_INFO, response,
10586 &code, &status_message, NULL);
10587 if (err) {
10588 isds_log(ILF_ISDS, ILL_DEBUG,
10589 _("ISDS response on EraseMessage request is missing "
10590 "status\n"));
10591 goto leave;
10594 /* Check server status code */
10595 if (!xmlStrcmp(code, BAD_CAST "1211")) {
10596 isds_log_message(context, _("Message to erase belongs to other box"));
10597 err = IE_INVAL;
10598 } else if (!xmlStrcmp(code, BAD_CAST "1219")) {
10599 isds_log_message(context, _("Message to erase is not saved in "
10600 "long term storage or the direction does not match"));
10601 err = IE_INVAL;
10602 } else if (xmlStrcmp(code, BAD_CAST "0000")) {
10603 char *code_locale = _isds_utf82locale((char*) code);
10604 char *message_locale = _isds_utf82locale((char*) status_message);
10605 isds_log(ILF_ISDS, ILL_DEBUG,
10606 _("Server refused EraseMessage request "
10607 "(code=%s, message=%s)\n"),
10608 code_locale, message_locale);
10609 isds_log_message(context, message_locale);
10610 free(code_locale);
10611 free(message_locale);
10612 err = IE_ISDS;
10613 goto leave;
10616 leave:
10617 free(code);
10618 free(status_message);
10619 xmlFreeDoc(response);
10620 xmlFreeNode(request);
10622 if (!err)
10623 isds_log(ILF_ISDS, ILL_DEBUG,
10624 _("EraseMessage request processed by server "
10625 "successfully.\n")
10627 #else /* not HAVE_LIBCURL */
10628 err = IE_NOTSUP;
10629 #endif
10630 return err;
10634 /* Mark message as read. This is a transactional commit function to acknowledge
10635 * to ISDS the message has been downloaded and processed by client properly.
10636 * @context is session context
10637 * @message_id is message identifier. */
10638 isds_error isds_mark_message_read(struct isds_ctx *context,
10639 const char *message_id) {
10641 isds_error err = IE_SUCCESS;
10642 #if HAVE_LIBCURL
10643 xmlDocPtr response = NULL;
10644 xmlChar *code = NULL, *status_message = NULL;
10645 #endif
10647 if (!context) return IE_INVALID_CONTEXT;
10648 zfree(context->long_message);
10650 #if HAVE_LIBCURL
10651 /* Do request and check for success */
10652 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10653 BAD_CAST "MarkMessageAsDownloaded", message_id,
10654 &response, NULL, NULL, &code, &status_message);
10656 free(code);
10657 free(status_message);
10658 xmlFreeDoc(response);
10660 if (!err)
10661 isds_log(ILF_ISDS, ILL_DEBUG,
10662 _("MarkMessageAsDownloaded request processed by server "
10663 "successfully.\n")
10665 #else /* not HAVE_LIBCURL */
10666 err = IE_NOTSUP;
10667 #endif
10668 return err;
10672 /* Mark message as received by recipient. This is applicable only to
10673 * commercial message. Use envelope->dmType message member to distinguish
10674 * commercial message from government message. Government message is
10675 * received automatically (by law), commercial message on recipient request.
10676 * @context is session context
10677 * @message_id is message identifier. */
10678 isds_error isds_mark_message_received(struct isds_ctx *context,
10679 const char *message_id) {
10681 isds_error err = IE_SUCCESS;
10682 #if HAVE_LIBCURL
10683 xmlDocPtr response = NULL;
10684 xmlChar *code = NULL, *status_message = NULL;
10685 #endif
10687 if (!context) return IE_INVALID_CONTEXT;
10688 zfree(context->long_message);
10690 #if HAVE_LIBCURL
10691 /* Do request and check for success */
10692 err = build_send_check_message_request(context, SERVICE_DM_INFO,
10693 BAD_CAST "ConfirmDelivery", message_id,
10694 &response, NULL, NULL, &code, &status_message);
10696 free(code);
10697 free(status_message);
10698 xmlFreeDoc(response);
10700 if (!err)
10701 isds_log(ILF_ISDS, ILL_DEBUG,
10702 _("ConfirmDelivery request processed by server "
10703 "successfully.\n")
10705 #else /* not HAVE_LIBCURL */
10706 err = IE_NOTSUP;
10707 #endif
10708 return err;
10712 /* Send document for authorized conversion into Czech POINT system.
10713 * This is public anonymous service, no log-in necessary. Special context is
10714 * used to reuse keep-a-live HTTPS connection.
10715 * @context is Czech POINT session context. DO NOT use context connected to
10716 * ISDS server. Use new context or context used by this function previously.
10717 * @document is document to convert. Only data, data_length, dmFileDescr and
10718 * is_xml members are significant. Be ware that not all document formats can be
10719 * converted (signed PDF 1.3 and higher only (2010-02 state)).
10720 * @id is reallocated identifier assigned by Czech POINT system to
10721 * your document on submit. Use is to tell it to Czech POINT officer.
10722 * @date is reallocated document submit date (submitted documents
10723 * expires after some period). Only tm_year, tm_mon and tm_mday carry sane
10724 * value. */
10725 isds_error czp_convert_document(struct isds_ctx *context,
10726 const struct isds_document *document,
10727 char **id, struct tm **date) {
10728 isds_error err = IE_SUCCESS;
10729 #if HAVE_LIBCURL
10730 xmlNsPtr deposit_ns = NULL, empty_ns = NULL;
10731 xmlNodePtr request = NULL, node;
10732 xmlDocPtr response = NULL;
10734 xmlXPathContextPtr xpath_ctx = NULL;
10735 xmlXPathObjectPtr result = NULL;
10736 long int status = -1;
10737 long int *status_ptr = &status;
10738 char *string = NULL;
10739 #endif
10742 if (!context) return IE_INVALID_CONTEXT;
10743 zfree(context->long_message);
10744 if (!document || !id || !date) return IE_INVAL;
10746 if (document->is_xml) {
10747 isds_log_message(context,
10748 _("XML documents cannot be submitted to conversion"));
10749 return IE_NOTSUP;
10752 /* Free output arguments */
10753 zfree(*id);
10754 zfree(*date);
10756 #if HAVE_LIBCURL
10757 /* Store configuration */
10758 context->type = CTX_TYPE_CZP;
10759 free(context->url);
10760 context->url = strdup("https://www.czechpoint.cz/uschovna/services.php");
10761 if (!(context->url))
10762 return IE_NOMEM;
10764 /* Prepare CURL handle if not yet connected */
10765 if (!context->curl) {
10766 context->curl = curl_easy_init();
10767 if (!(context->curl))
10768 return IE_ERROR;
10771 /* Build conversion request */
10772 request = xmlNewNode(NULL, BAD_CAST "saveDocument");
10773 if (!request) {
10774 isds_log_message(context,
10775 _("Could not build Czech POINT conversion request"));
10776 return IE_ERROR;
10778 deposit_ns = xmlNewNs(request, BAD_CAST DEPOSIT_NS, BAD_CAST "dep");
10779 if(!deposit_ns) {
10780 isds_log_message(context,
10781 _("Could not create Czech POINT deposit name space"));
10782 xmlFreeNode(request);
10783 return IE_ERROR;
10785 xmlSetNs(request, deposit_ns);
10787 /* Insert children. They are in empty namespace! */
10788 empty_ns = xmlNewNs(request, BAD_CAST "", NULL);
10789 if(!empty_ns) {
10790 isds_log_message(context, _("Could not create empty name space"));
10791 err = IE_ERROR;
10792 goto leave;
10794 INSERT_STRING_WITH_NS(request, empty_ns, "conversionID", "0");
10795 INSERT_STRING_WITH_NS(request, empty_ns, "fileName",
10796 document->dmFileDescr);
10798 /* Document encoded in Base64 */
10799 err = insert_base64_encoded_string(context, request, empty_ns, "document",
10800 document->data, document->data_length);
10801 if (err) goto leave;
10803 isds_log(ILF_ISDS, ILL_DEBUG,
10804 _("Submitting document for conversion into Czech POINT deposit"));
10806 /* Send conversion request */
10807 err = _czp_czpdeposit(context, request, &response);
10808 xmlFreeNode(request); request = NULL;
10810 if (err) {
10811 czp_do_close_connection(context);
10812 goto leave;
10816 /* Extract response */
10817 xpath_ctx = xmlXPathNewContext(response);
10818 if (!xpath_ctx) {
10819 err = IE_ERROR;
10820 goto leave;
10822 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
10823 err = IE_ERROR;
10824 goto leave;
10826 result = xmlXPathEvalExpression(
10827 BAD_CAST "/deposit:saveDocumentResponse/return",
10828 xpath_ctx);
10829 if (!result) {
10830 err = IE_ERROR;
10831 goto leave;
10833 /* Empty response */
10834 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
10835 isds_printf_message(context,
10836 _("Missing `return' element in Czech POINT deposit response"));
10837 err = IE_ISDS;
10838 goto leave;
10840 /* More responses */
10841 if (result->nodesetval->nodeNr > 1) {
10842 isds_printf_message(context,
10843 _("Multiple `return' element in Czech POINT deposit response"));
10844 err = IE_ISDS;
10845 goto leave;
10847 /* One response */
10848 xpath_ctx->node = result->nodesetval->nodeTab[0];
10850 /* Get status */
10851 EXTRACT_LONGINT("status", status_ptr, 1);
10852 if (status) {
10853 EXTRACT_STRING("statusMsg", string);
10854 char *string_locale = _isds_utf82locale(string);
10855 isds_printf_message(context,
10856 _("Czech POINT deposit refused document for conversion "
10857 "(code=%ld, message=%s)"),
10858 status, string_locale);
10859 free(string_locale);
10860 err = IE_ISDS;
10861 goto leave;
10864 /* Get document ID */
10865 EXTRACT_STRING("documentID", *id);
10867 /* Get submit date */
10868 EXTRACT_STRING("dateInserted", string);
10869 if (string) {
10870 *date = calloc(1, sizeof(**date));
10871 if (!*date) {
10872 err = IE_NOMEM;
10873 goto leave;
10875 err = _isds_datestring2tm((xmlChar *)string, *date);
10876 if (err) {
10877 if (err == IE_NOTSUP) {
10878 err = IE_ISDS;
10879 char *string_locale = _isds_utf82locale(string);
10880 isds_printf_message(context,
10881 _("Invalid dateInserted value: %s"), string_locale);
10882 free(string_locale);
10884 goto leave;
10888 leave:
10889 free(string);
10890 xmlXPathFreeObject(result);
10891 xmlXPathFreeContext(xpath_ctx);
10893 xmlFreeDoc(response);
10894 xmlFreeNode(request);
10896 if (!err) {
10897 char *id_locale = _isds_utf82locale((char *) *id);
10898 isds_log(ILF_ISDS, ILL_DEBUG,
10899 _("Document %s has been submitted for conversion "
10900 "to server successfully\n"), id_locale);
10901 free(id_locale);
10903 #else /* not HAVE_LIBCURL */
10904 err = IE_NOTSUP;
10905 #endif
10906 return err;
10910 /* Close possibly opened connection to Czech POINT document deposit.
10911 * @context is Czech POINT session context. */
10912 isds_error czp_close_connection(struct isds_ctx *context) {
10913 if (!context) return IE_INVALID_CONTEXT;
10914 zfree(context->long_message);
10915 #if HAVE_LIBCURL
10916 return czp_do_close_connection(context);
10917 #else
10918 return IE_NOTSUP;
10919 #endif
10923 /* Send request for new box creation in testing ISDS instance.
10924 * It's not possible to request for a production box currently, as it
10925 * communicates via e-mail.
10926 * XXX: This function does not work either. Server complains about invalid
10927 * e-mail address.
10928 * XXX: Remove context->type hacks in isds.c and validator.c when removing
10929 * this function
10930 * @context is special session context for box creation request. DO NOT use
10931 * standard context as it could reveal your password. Use fresh new context or
10932 * context previously used by this function.
10933 * @box is box description to create including single primary user (in case of
10934 * FO box type). It outputs box ID assigned by ISDS in dbID element.
10935 * @users is list of struct isds_DbUserInfo (primary users in case of non-FO
10936 * box, or contact address of PFO box owner). The email member is mandatory as
10937 * it will be used to deliver credentials.
10938 * @former_names is former name of box owner. Pass NULL if you don't care.
10939 * @approval is optional external approval of box manipulation
10940 * @refnumber is reallocated serial number of request assigned by ISDS. Use
10941 * NULL, if you don't care.*/
10942 isds_error isds_request_new_testing_box(struct isds_ctx *context,
10943 struct isds_DbOwnerInfo *box, const struct isds_list *users,
10944 const char *former_names, const struct isds_approval *approval,
10945 char **refnumber) {
10946 isds_error err = IE_SUCCESS;
10947 #if HAVE_LIBCURL
10948 xmlNodePtr request = NULL;
10949 xmlDocPtr response = NULL;
10950 xmlXPathContextPtr xpath_ctx = NULL;
10951 xmlXPathObjectPtr result = NULL;
10952 #endif
10955 if (!context) return IE_INVALID_CONTEXT;
10956 zfree(context->long_message);
10957 if (!box) return IE_INVAL;
10959 #if HAVE_LIBCURL
10960 if (!box->email || box->email[0] == '\0') {
10961 isds_log_message(context, _("E-mail field is mandatory"));
10962 return IE_INVAL;
10965 /* Scratch box ID */
10966 zfree(box->dbID);
10968 /* Store configuration */
10969 context->type = CTX_TYPE_TESTING_REQUEST_COLLECTOR;
10970 free(context->url);
10971 context->url = strdup("http://78.102.19.203/testbox/request_box.php");
10972 if (!(context->url))
10973 return IE_NOMEM;
10975 /* Prepare CURL handle if not yet connected */
10976 if (!context->curl) {
10977 context->curl = curl_easy_init();
10978 if (!(context->curl))
10979 return IE_ERROR;
10982 /* Build CreateDataBox request */
10983 err = build_CreateDBInput_request(context,
10984 &request, BAD_CAST "CreateDataBox",
10985 box, users, (xmlChar *) former_names, NULL, NULL, NULL, approval);
10986 if (err) goto leave;
10988 /* Send it to server and process response */
10989 err = send_destroy_request_check_response(context,
10990 SERVICE_DB_MANIPULATION, BAD_CAST "CreateDataBox", &request,
10991 &response, (xmlChar **) refnumber, NULL);
10992 if (err) goto leave;
10994 /* Extract box ID */
10995 xpath_ctx = xmlXPathNewContext(response);
10996 if (!xpath_ctx) {
10997 err = IE_ERROR;
10998 goto leave;
11000 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11001 err = IE_ERROR;
11002 goto leave;
11004 EXTRACT_STRING("/isds:CreateDataBoxResponse/isds:dbID", box->dbID);
11006 leave:
11007 xmlXPathFreeObject(result);
11008 xmlXPathFreeContext(xpath_ctx);
11009 xmlFreeDoc(response);
11010 xmlFreeNode(request);
11012 if (!err) {
11013 isds_log(ILF_ISDS, ILL_DEBUG,
11014 _("CreateDataBox request processed by server successfully.\n"));
11016 #else /* not HAVE_LIBCURL */
11017 err = IE_NOTSUP;
11018 #endif
11020 return err;
11024 /* Submit CMS signed message to ISDS to verify its originality. This is
11025 * stronger form of isds_verify_message_hash() because ISDS does more checks
11026 * than simple one (potentialy old weak) hash comparison.
11027 * @context is session context
11028 * @message is memory with raw CMS signed message bit stream
11029 * @length is @message size in bytes
11030 * @return
11031 * IE_SUCCESS if message originates in ISDS
11032 * IE_NOTEQUAL if message is unknown to ISDS
11033 * other code for other errors */
11034 isds_error isds_authenticate_message(struct isds_ctx *context,
11035 const void *message, size_t length) {
11036 isds_error err = IE_SUCCESS;
11037 #if HAVE_LIBCURL
11038 xmlNsPtr isds_ns = NULL;
11039 xmlNodePtr request = NULL;
11040 xmlDocPtr response = NULL;
11041 xmlXPathContextPtr xpath_ctx = NULL;
11042 xmlXPathObjectPtr result = NULL;
11043 _Bool *authentic = NULL;
11044 #endif
11046 if (!context) return IE_INVALID_CONTEXT;
11047 zfree(context->long_message);
11048 if (!message || length == 0) return IE_INVAL;
11050 #if HAVE_LIBCURL
11051 /* Check if connection is established
11052 * TODO: This check should be done downstairs. */
11053 if (!context->curl) return IE_CONNECTION_CLOSED;
11056 /* Build AuthenticateMessage request */
11057 request = xmlNewNode(NULL, BAD_CAST "AuthenticateMessage");
11058 if (!request) {
11059 isds_log_message(context,
11060 _("Could not build AuthenticateMessage request"));
11061 return IE_ERROR;
11063 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11064 if(!isds_ns) {
11065 isds_log_message(context, _("Could not create ISDS name space"));
11066 xmlFreeNode(request);
11067 return IE_ERROR;
11069 xmlSetNs(request, isds_ns);
11071 /* Insert Base64 encoded message */
11072 err = insert_base64_encoded_string(context, request, NULL, "dmMessage",
11073 message, length);
11074 if (err) goto leave;
11076 /* Send request to server and process response */
11077 err = send_destroy_request_check_response(context,
11078 SERVICE_DM_OPERATIONS, BAD_CAST "AuthenticateMessage", &request,
11079 &response, NULL, NULL);
11080 if (err) goto leave;
11083 /* ISDS has decided */
11084 xpath_ctx = xmlXPathNewContext(response);
11085 if (!xpath_ctx) {
11086 err = IE_ERROR;
11087 goto leave;
11089 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11090 err = IE_ERROR;
11091 goto leave;
11094 EXTRACT_BOOLEAN("/isds:AuthenticateMessageResponse/isds:dmAuthResult", authentic);
11096 if (!authentic) {
11097 isds_log_message(context,
11098 _("Server did not return any response on "
11099 "AuthenticateMessage request"));
11100 err = IE_ISDS;
11101 goto leave;
11103 if (*authentic) {
11104 isds_log(ILF_ISDS, ILL_DEBUG,
11105 _("ISDS authenticated the message successfully\n"));
11106 } else {
11107 isds_log_message(context, _("ISDS does not know the message"));
11108 err = IE_NOTEQUAL;
11112 leave:
11113 free(authentic);
11114 xmlXPathFreeObject(result);
11115 xmlXPathFreeContext(xpath_ctx);
11117 xmlFreeDoc(response);
11118 xmlFreeNode(request);
11119 #else /* not HAVE_LIBCURL */
11120 err = IE_NOTSUP;
11121 #endif
11123 return err;
11127 /* Submit CMS signed message or delivery info to ISDS to re-sign the content
11128 * including adding new CMS time stamp. Only CMS blobs without time stamp can
11129 * be re-signed.
11130 * @context is session context
11131 * @input_data is memory with raw CMS signed message or delivery info bit
11132 * stream to re-sign
11133 * @input_length is @input_data size in bytes
11134 * @output_data is pointer to auto-allocated memory where to store re-signed
11135 * input data blob. Caller must free it.
11136 * @output_data is pointer where to store @output_data size in bytes
11137 * @valid_to is pointer to auto-allocated date of time stamp expiration.
11138 * Only tm_year, tm_mon and tm_mday will be set. Pass NULL, if you don't care.
11139 * @return
11140 * IE_SUCCESS if CMS blob has been re-signed successfully
11141 * other code for other errors */
11142 isds_error isds_resign_message(struct isds_ctx *context,
11143 const void *input_data, size_t input_length,
11144 void **output_data, size_t *output_length, struct tm **valid_to) {
11145 isds_error err = IE_SUCCESS;
11146 #if HAVE_LIBCURL
11147 xmlNsPtr isds_ns = NULL;
11148 xmlNodePtr request = NULL;
11149 xmlDocPtr response = NULL;
11150 xmlXPathContextPtr xpath_ctx = NULL;
11151 xmlXPathObjectPtr result = NULL;
11152 char *string = NULL;
11153 const xmlChar *codes[] = {
11154 BAD_CAST "2200",
11155 BAD_CAST "2201",
11156 BAD_CAST "2204",
11157 BAD_CAST "2207",
11158 NULL
11160 const char *meanings[] = {
11161 "Message is bad",
11162 "Message is not original",
11163 "Message already contains time stamp in CAdES-EPES or CAdES-T CMS structure",
11164 "Time stamp could not been generated in time"
11166 const isds_error errors[] = {
11167 IE_INVAL,
11168 IE_NOTUNIQ,
11169 IE_INVAL,
11170 IE_ISDS,
11172 struct code_map_isds_error map = {
11173 .codes = codes,
11174 .meanings = meanings,
11175 .errors = errors
11177 #endif
11179 if (NULL != output_data) *output_data = NULL;
11180 if (NULL != output_length) *output_length = 0;
11181 if (NULL != valid_to) *valid_to = NULL;
11183 if (NULL == context) return IE_INVALID_CONTEXT;
11184 zfree(context->long_message);
11185 if (NULL == input_data || 0 == input_length) {
11186 isds_log_message(context, _("Empty CMS blob on input"));
11187 return IE_INVAL;
11189 if (NULL == output_data || NULL == output_length) {
11190 isds_log_message(context,
11191 _("NULL pointer provided for output CMS blob"));
11192 return IE_INVAL;
11195 #if HAVE_LIBCURL
11196 /* Check if connection is established
11197 * TODO: This check should be done downstairs. */
11198 if (!context->curl) return IE_CONNECTION_CLOSED;
11201 /* Build Re-signISDSDocument request */
11202 request = xmlNewNode(NULL, BAD_CAST "Re-signISDSDocument");
11203 if (!request) {
11204 isds_log_message(context,
11205 _("Could not build Re-signISDSDocument request"));
11206 return IE_ERROR;
11208 isds_ns = xmlNewNs(request, BAD_CAST ISDS_NS, NULL);
11209 if(!isds_ns) {
11210 isds_log_message(context, _("Could not create ISDS name space"));
11211 xmlFreeNode(request);
11212 return IE_ERROR;
11214 xmlSetNs(request, isds_ns);
11216 /* Insert Base64 encoded CMS blob */
11217 err = insert_base64_encoded_string(context, request, NULL, "dmDoc",
11218 input_data, input_length);
11219 if (err) goto leave;
11221 /* Send request to server and process response */
11222 err = send_destroy_request_check_response(context,
11223 SERVICE_DM_OPERATIONS, BAD_CAST "Re-signISDSDocument", &request,
11224 &response, NULL, &map);
11225 if (err) goto leave;
11228 /* Extract re-signed data */
11229 xpath_ctx = xmlXPathNewContext(response);
11230 if (!xpath_ctx) {
11231 err = IE_ERROR;
11232 goto leave;
11234 if (_isds_register_namespaces(xpath_ctx, MESSAGE_NS_UNSIGNED)) {
11235 err = IE_ERROR;
11236 goto leave;
11238 result = xmlXPathEvalExpression(
11239 BAD_CAST "/isds:Re-signISDSDocumentResponse", xpath_ctx);
11240 if (!result) {
11241 err = IE_ERROR;
11242 goto leave;
11244 if (xmlXPathNodeSetIsEmpty(result->nodesetval)) {
11245 isds_log_message(context,
11246 _("Missing Re-signISDSDocumentResponse element"));
11247 err = IE_ISDS;
11248 goto leave;
11250 if (result->nodesetval->nodeNr > 1) {
11251 isds_log_message(context,
11252 _("Multiple Re-signISDSDocumentResponse element"));
11253 err = IE_ISDS;
11254 goto leave;
11256 xpath_ctx->node = result->nodesetval->nodeTab[0];
11257 xmlXPathFreeObject(result); result = NULL;
11259 EXTRACT_STRING("isds:dmResultDoc", string);
11260 /* Decode non-empty data */
11261 if (NULL != string && string[0] != '\0') {
11262 *output_length = _isds_b64decode(string, output_data);
11263 if (*output_length == (size_t) -1) {
11264 isds_log_message(context,
11265 _("Error while Base64-decoding re-signed data"));
11266 err = IE_ERROR;
11267 goto leave;
11269 } else {
11270 isds_log_message(context, _("Server did not send re-signed data"));
11271 err = IE_ISDS;
11272 goto leave;
11274 zfree(string);
11276 if (NULL != valid_to) {
11277 /* Get time stamp expiration date */
11278 EXTRACT_STRING("isds:dmValidTo", string);
11279 if (NULL != string) {
11280 *valid_to = calloc(1, sizeof(**valid_to));
11281 if (!*valid_to) {
11282 err = IE_NOMEM;
11283 goto leave;
11285 err = _isds_datestring2tm((xmlChar *)string, *valid_to);
11286 if (err) {
11287 if (err == IE_NOTSUP) {
11288 err = IE_ISDS;
11289 char *string_locale = _isds_utf82locale(string);
11290 isds_printf_message(context,
11291 _("Invalid dmValidTo value: %s"), string_locale);
11292 free(string_locale);
11294 goto leave;
11299 leave:
11300 free(string);
11302 xmlXPathFreeObject(result);
11303 xmlXPathFreeContext(xpath_ctx);
11305 xmlFreeDoc(response);
11306 xmlFreeNode(request);
11307 #else /* not HAVE_LIBCURL */
11308 err = IE_NOTSUP;
11309 #endif
11311 return err;
11314 #undef INSERT_ELEMENT
11315 #undef CHECK_FOR_STRING_LENGTH
11316 #undef INSERT_STRING_ATTRIBUTE
11317 #undef INSERT_ULONGINTNOPTR
11318 #undef INSERT_ULONGINT
11319 #undef INSERT_LONGINT
11320 #undef INSERT_BOOLEAN
11321 #undef INSERT_SCALAR_BOOLEAN
11322 #undef INSERT_STRING
11323 #undef INSERT_STRING_WITH_NS
11324 #undef EXTRACT_STRING_ATTRIBUTE
11325 #undef EXTRACT_ULONGINT
11326 #undef EXTRACT_LONGINT
11327 #undef EXTRACT_BOOLEAN
11328 #undef EXTRACT_STRING
11331 /* Compute hash of message from raw representation and store it into envelope.
11332 * Original hash structure will be destroyed in envelope.
11333 * @context is session context
11334 * @message is message carrying raw XML message blob
11335 * @algorithm is desired hash algorithm to use */
11336 isds_error isds_compute_message_hash(struct isds_ctx *context,
11337 struct isds_message *message, const isds_hash_algorithm algorithm) {
11338 isds_error err = IE_SUCCESS;
11339 const char *nsuri;
11340 void *xml_stream = NULL;
11341 size_t xml_stream_length;
11342 size_t phys_start, phys_end;
11343 char *phys_path = NULL;
11344 struct isds_hash *new_hash = NULL;
11347 if (!context) return IE_INVALID_CONTEXT;
11348 zfree(context->long_message);
11349 if (!message) return IE_INVAL;
11351 if (!message->raw) {
11352 isds_log_message(context,
11353 _("Message does not carry raw representation"));
11354 return IE_INVAL;
11357 switch (message->raw_type) {
11358 case RAWTYPE_INCOMING_MESSAGE:
11359 nsuri = ISDS_NS;
11360 xml_stream = message->raw;
11361 xml_stream_length = message->raw_length;
11362 break;
11364 case RAWTYPE_PLAIN_SIGNED_INCOMING_MESSAGE:
11365 nsuri = SISDS_INCOMING_NS;
11366 xml_stream = message->raw;
11367 xml_stream_length = message->raw_length;
11368 break;
11370 case RAWTYPE_CMS_SIGNED_INCOMING_MESSAGE:
11371 nsuri = SISDS_INCOMING_NS;
11372 err = _isds_extract_cms_data(context,
11373 message->raw, message->raw_length,
11374 &xml_stream, &xml_stream_length);
11375 if (err) goto leave;
11376 break;
11378 case RAWTYPE_PLAIN_SIGNED_OUTGOING_MESSAGE:
11379 nsuri = SISDS_OUTGOING_NS;
11380 xml_stream = message->raw;
11381 xml_stream_length = message->raw_length;
11382 break;
11384 case RAWTYPE_CMS_SIGNED_OUTGOING_MESSAGE:
11385 nsuri = SISDS_OUTGOING_NS;
11386 err = _isds_extract_cms_data(context,
11387 message->raw, message->raw_length,
11388 &xml_stream, &xml_stream_length);
11389 if (err) goto leave;
11390 break;
11392 default:
11393 isds_log_message(context, _("Bad raw representation type"));
11394 return IE_INVAL;
11395 break;
11399 /* XXX: Hash is computed from original string representing isds:dmDm
11400 * subtree. That means no encoding, white space, xmlns attributes changes.
11401 * In other words, input for hash can be invalid XML stream. */
11402 if (-1 == isds_asprintf(&phys_path, "%s%s%s%s",
11403 nsuri, PHYSXML_NS_SEPARATOR "MessageDownloadResponse"
11404 PHYSXML_ELEMENT_SEPARATOR,
11405 nsuri, PHYSXML_NS_SEPARATOR "dmReturnedMessage"
11406 PHYSXML_ELEMENT_SEPARATOR
11407 ISDS_NS PHYSXML_NS_SEPARATOR "dmDm")) {
11408 err = IE_NOMEM;
11409 goto leave;
11411 err = _isds_find_element_boundary(xml_stream, xml_stream_length,
11412 phys_path, &phys_start, &phys_end);
11413 zfree(phys_path);
11414 if (err) {
11415 isds_log_message(context,
11416 _("Substring with isds:dmDM element could not be located "
11417 "in raw message"));
11418 goto leave;
11422 /* Compute hash */
11423 new_hash = calloc(1, sizeof(*new_hash));
11424 if (!new_hash) {
11425 err = IE_NOMEM;
11426 goto leave;
11428 new_hash->algorithm = algorithm;
11429 err = _isds_compute_hash(xml_stream + phys_start, phys_end - phys_start + 1,
11430 new_hash);
11431 if (err) {
11432 isds_log_message(context, _("Could not compute message hash"));
11433 goto leave;
11436 /* Save computed hash */
11437 if (!message->envelope) {
11438 message->envelope = calloc(1, sizeof(*message->envelope));
11439 if (!message->envelope) {
11440 err = IE_NOMEM;
11441 goto leave;
11444 isds_hash_free(&message->envelope->hash);
11445 message->envelope->hash = new_hash;
11447 leave:
11448 if (err) {
11449 isds_hash_free(&new_hash);
11452 free(phys_path);
11453 if (xml_stream != message->raw) free(xml_stream);
11454 return err;
11458 /* Compare two hashes.
11459 * @h1 is first hash
11460 * @h2 is another hash
11461 * @return
11462 * IE_SUCCESS if hashes equal
11463 * IE_NOTUNIQ if hashes are comparable, but they don't equal
11464 * IE_ENUM if not comparable, but both structures defined
11465 * IE_INVAL if some of the structures are undefined (NULL)
11466 * IE_ERROR if internal error occurs */
11467 isds_error isds_hash_cmp(const struct isds_hash *h1, const struct isds_hash *h2) {
11468 if (h1 == NULL || h2 == NULL) return IE_INVAL;
11469 if (h1->algorithm != h2->algorithm) return IE_ENUM;
11470 if (h1->length != h2->length) return IE_ERROR;
11471 if (h1->length > 0 && !h1->value) return IE_ERROR;
11472 if (h2->length > 0 && !h2->value) return IE_ERROR;
11474 for (int i = 0; i < h1->length; i++) {
11475 if (((uint8_t *) (h1->value))[i] != ((uint8_t *) (h2->value))[i])
11476 return IE_NOTEQUAL;
11478 return IE_SUCCESS;
11482 /* Check message has gone through ISDS by comparing message hash stored in
11483 * ISDS and locally computed hash. You must provide message with valid raw
11484 * member (do not use isds_load_message(..., BUFFER_DONT_STORE)).
11485 * This is convenient wrapper for isds_download_message_hash(),
11486 * isds_compute_message_hash(), and isds_hash_cmp() sequence.
11487 * @context is session context
11488 * @message is message with valid raw and envelope member; envelope->hash
11489 * member will be changed during function run. Use envelope on heap only.
11490 * @return
11491 * IE_SUCCESS if message originates in ISDS
11492 * IE_NOTEQUAL if message is unknown to ISDS
11493 * other code for other errors */
11494 isds_error isds_verify_message_hash(struct isds_ctx *context,
11495 struct isds_message *message) {
11496 isds_error err = IE_SUCCESS;
11497 struct isds_hash *downloaded_hash = NULL;
11499 if (!context) return IE_INVALID_CONTEXT;
11500 zfree(context->long_message);
11501 if (!message) return IE_INVAL;
11503 if (!message->envelope) {
11504 isds_log_message(context,
11505 _("Given message structure is missing envelope"));
11506 return IE_INVAL;
11508 if (!message->raw) {
11509 isds_log_message(context,
11510 _("Given message structure is missing raw representation"));
11511 return IE_INVAL;
11514 err = isds_download_message_hash(context, message->envelope->dmID,
11515 &downloaded_hash);
11516 if (err) goto leave;
11518 err = isds_compute_message_hash(context, message,
11519 downloaded_hash->algorithm);
11520 if (err) goto leave;
11522 err = isds_hash_cmp(downloaded_hash, message->envelope->hash);
11524 leave:
11525 isds_hash_free(&downloaded_hash);
11526 return err;
11530 /* Search for document by document ID in list of documents. IDs are compared
11531 * as UTF-8 string.
11532 * @documents is list of isds_documents
11533 * @id is document identifier
11534 * @return first matching document or NULL. */
11535 const struct isds_document *isds_find_document_by_id(
11536 const struct isds_list *documents, const char *id) {
11537 const struct isds_list *item;
11538 const struct isds_document *document;
11540 for (item = documents; item; item = item->next) {
11541 document = (struct isds_document *) item->data;
11542 if (!document) continue;
11544 if (!xmlStrcmp((xmlChar *) id, (xmlChar *) document->dmFileGuid))
11545 return document;
11548 return NULL;
11552 /* Normalize @mime_type to be proper MIME type.
11553 * ISDS servers pass invalid MIME types (e.g. "pdf"). This function tries to
11554 * guess regular MIME type (e.g. "application/pdf").
11555 * @mime_type is UTF-8 encoded MIME type to fix
11556 * @return original @mime_type if no better interpretation exists, or
11557 * constant static UTF-8 encoded string with proper MIME type. */
11558 const char *isds_normalize_mime_type(const char *mime_type) {
11559 if (!mime_type) return NULL;
11561 for (int offset = 0;
11562 offset < sizeof(extension_map_mime)/sizeof(extension_map_mime[0]);
11563 offset += 2) {
11564 if (!xmlStrcasecmp((const xmlChar*) mime_type,
11565 extension_map_mime[offset]))
11566 return (const char *) extension_map_mime[offset + 1];
11569 return mime_type;
11573 /*int isds_get_message(struct isds_ctx *context, const unsigned int id,
11574 struct isds_message **message);
11575 int isds_send_message(struct isds_ctx *context, struct isds_message *message);
11576 int isds_list_messages(struct isds_ctx *context, struct isds_message **message);
11577 int isds_find_recipient(struct isds_ctx *context, const struct address *pattern,
11578 struct isds_address **address);
11580 int isds_message_free(struct isds_message **message);
11581 int isds_address_free(struct isds_address **address);
11585 /* Makes known all relevant namespaces to given XPath context
11586 * @xpath_ctx is XPath context
11587 * @message_ns selects proper message name space. Unsigned and signed
11588 * messages and delivery info's differ in prefix and URI. */
11589 _hidden isds_error _isds_register_namespaces(xmlXPathContextPtr xpath_ctx,
11590 const message_ns_type message_ns) {
11591 const xmlChar *message_namespace = NULL;
11593 if (!xpath_ctx) return IE_ERROR;
11595 switch(message_ns) {
11596 case MESSAGE_NS_1:
11597 message_namespace = BAD_CAST ISDS1_NS; break;
11598 case MESSAGE_NS_UNSIGNED:
11599 message_namespace = BAD_CAST ISDS_NS; break;
11600 case MESSAGE_NS_SIGNED_INCOMING:
11601 message_namespace = BAD_CAST SISDS_INCOMING_NS; break;
11602 case MESSAGE_NS_SIGNED_OUTGOING:
11603 message_namespace = BAD_CAST SISDS_OUTGOING_NS; break;
11604 case MESSAGE_NS_SIGNED_DELIVERY:
11605 message_namespace = BAD_CAST SISDS_DELIVERY_NS; break;
11606 default:
11607 return IE_ENUM;
11610 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "soap", BAD_CAST SOAP_NS))
11611 return IE_ERROR;
11612 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "isds", BAD_CAST ISDS_NS))
11613 return IE_ERROR;
11614 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "oisds", BAD_CAST OISDS_NS))
11615 return IE_ERROR;
11616 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "sisds", message_namespace))
11617 return IE_ERROR;
11618 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "xs", BAD_CAST SCHEMA_NS))
11619 return IE_ERROR;
11620 if (xmlXPathRegisterNs(xpath_ctx, BAD_CAST "deposit", BAD_CAST DEPOSIT_NS))
11621 return IE_ERROR;
11622 return IE_SUCCESS;